[build] Added ChangeLog target to make
[adg.git] / adg / adg-toy-text.c
blob698a5aec682ec9dcd7873c09e9a98e2d1b325a8c
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2008, Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 /**
21 * SECTION:toytext
22 * @title: AdgToyText
23 * @short_description: Simple text entity that use the cairo "toy" text API
25 * The #AdgToyText class is a basic class to show simple text. It internally
26 * uses the so called cairo "toy" API and it shares the same limitations.
27 **/
29 #include "adg-toy-text.h"
30 #include "adg-toy-text-private.h"
31 #include "adg-positionable.h"
32 #include "adg-rotable.h"
33 #include "adg-font-style.h"
34 #include "adg-intl.h"
36 #define PARENT_CLASS ((AdgEntityClass *) adg_toy_text_parent_class)
39 enum {
40 PROP_0,
41 PROP_ORIGIN,
42 PROP_ANGLE,
43 PROP_LABEL
46 static void positionable_init (AdgPositionableIface *iface);
47 static void get_origin (AdgPositionable*positionable,
48 AdgPoint *dest);
49 static void set_origin (AdgPositionable*positionable,
50 const AdgPoint *origin);
52 static void rotable_init (AdgRotableIface*iface);
53 static gdouble get_angle (AdgRotable *rotable);
54 static void set_angle (AdgRotable *rotable,
55 gdouble angle);
57 static void finalize (GObject *object);
58 static void get_property (GObject *object,
59 guint param_id,
60 GValue *value,
61 GParamSpec *pspec);
62 static void set_property (GObject *object,
63 guint param_id,
64 const GValue *value,
65 GParamSpec *pspec);
66 static void model_matrix_changed (AdgEntity *entity,
67 AdgMatrix *parent_matrix);
68 static void invalidate (AdgEntity *entity);
69 static void render (AdgEntity *entity,
70 cairo_t *cr);
71 static gboolean update_origin_cache (AdgToyText *toy_text,
72 cairo_t *cr);
73 static gboolean update_label_cache (AdgToyText *toy_text,
74 cairo_t *cr);
75 static void clear_label_cache (AdgToyText *toy_text);
76 static void clear_origin_cache (AdgToyText *toy_text);
77 static void move_label_cache (AdgToyText *toy_text,
78 const AdgPair *to);
81 G_DEFINE_TYPE_WITH_CODE(AdgToyText, adg_toy_text, ADG_TYPE_ENTITY,
82 G_IMPLEMENT_INTERFACE(ADG_TYPE_POSITIONABLE,
83 positionable_init)
84 G_IMPLEMENT_INTERFACE(ADG_TYPE_ROTABLE, rotable_init))
87 static void
88 positionable_init(AdgPositionableIface *iface)
90 iface->get_origin = get_origin;
91 iface->set_origin = set_origin;
94 static void
95 get_origin(AdgPositionable *positionable, AdgPoint *dest)
97 AdgToyText *toy_text = (AdgToyText *) positionable;
98 adg_point_copy(dest, &toy_text->priv->origin);
101 static void
102 set_origin(AdgPositionable *positionable, const AdgPoint *origin)
104 AdgToyText *toy_text = (AdgToyText *) positionable;
105 adg_point_copy(&toy_text->priv->origin, origin);
109 static void
110 rotable_init(AdgRotableIface *iface)
112 iface->get_angle = get_angle;
113 iface->set_angle = set_angle;
116 static gdouble
117 get_angle(AdgRotable *rotable)
119 AdgToyText *toy_text = (AdgToyText *) rotable;
120 return toy_text->priv->angle;
123 static void
124 set_angle(AdgRotable *rotable, gdouble angle)
126 AdgToyText *toy_text = (AdgToyText *) rotable;
127 toy_text->priv->angle = angle;
131 static void
132 adg_toy_text_class_init(AdgToyTextClass *klass)
134 GObjectClass *gobject_class;
135 AdgEntityClass *entity_class;
136 GParamSpec *param;
138 gobject_class = (GObjectClass *) klass;
139 entity_class = (AdgEntityClass *) klass;
141 g_type_class_add_private(klass, sizeof(AdgToyTextPrivate));
143 gobject_class->finalize = finalize;
144 gobject_class->get_property = get_property;
145 gobject_class->set_property = set_property;
147 entity_class->model_matrix_changed = model_matrix_changed;
148 entity_class->invalidate = invalidate;
149 entity_class->render = render;
151 g_object_class_override_property(gobject_class, PROP_ORIGIN, "origin");
152 g_object_class_override_property(gobject_class, PROP_ANGLE, "angle");
154 param = g_param_spec_string("label",
155 P_("Label"),
156 P_("The label to display"),
157 NULL, G_PARAM_READWRITE);
158 g_object_class_install_property(gobject_class, PROP_LABEL, param);
161 static void
162 adg_toy_text_init(AdgToyText *toy_text)
164 AdgToyTextPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(toy_text,
165 ADG_TYPE_TOY_TEXT,
166 AdgToyTextPrivate);
168 priv->label = NULL;
169 adg_point_unset(&priv->origin);
171 priv->origin_cached = FALSE;
172 priv->glyphs = NULL;
174 toy_text->priv = priv;
177 static void
178 finalize(GObject *object)
180 AdgToyText *toy_text = (AdgToyText *) object;
182 g_free(toy_text->priv->label);
183 clear_label_cache(toy_text);
184 clear_origin_cache(toy_text);
186 ((GObjectClass *) PARENT_CLASS)->finalize(object);
189 static void
190 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
192 AdgToyText *toy_text = (AdgToyText *) toy_text;
194 switch (prop_id) {
195 case PROP_ORIGIN:
196 g_value_set_boxed(value, &toy_text->priv->origin);
197 break;
198 case PROP_ANGLE:
199 g_value_set_double(value, toy_text->priv->angle);
200 break;
201 case PROP_LABEL:
202 g_value_set_string(value, toy_text->priv->label);
203 break;
204 default:
205 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
206 break;
210 static void
211 set_property(GObject *object, guint prop_id,
212 const GValue *value, GParamSpec *pspec)
214 AdgToyText *toy_text = (AdgToyText *) object;
216 switch (prop_id) {
217 case PROP_ORIGIN:
218 adg_point_copy(&toy_text->priv->origin,
219 (AdgPoint *) g_value_get_boxed(value));
220 clear_origin_cache(toy_text);
221 break;
222 case PROP_ANGLE:
223 toy_text->priv->angle = g_value_get_double(value);
224 clear_origin_cache(toy_text);
225 break;
226 case PROP_LABEL:
227 g_free(toy_text->priv->label);
228 toy_text->priv->label = g_value_dup_string(value);
229 clear_label_cache(toy_text);
230 break;
231 default:
232 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
233 break;
239 * adg_toy_text_new:
240 * @label: the label text
242 * Creates a new toy text entity using @label as its text
244 * Return value: the new entity
246 AdgEntity *
247 adg_toy_text_new(const gchar *label)
249 return (AdgEntity *) g_object_new(ADG_TYPE_TOY_TEXT, "label", label, NULL);
253 * adg_toy_text_get_label:
254 * @toy_text: an #AdgToyText
256 * Gets the label text. The string is internally owned and
257 * must not be freed or modified.
259 * Return value: the label text
261 const gchar *
262 adg_toy_text_get_label(AdgToyText *toy_text)
264 g_return_val_if_fail(ADG_IS_TOY_TEXT(toy_text), NULL);
266 return toy_text->priv->label;
270 * adg_toy_text_set_label:
271 * @toy_text: an #AdgToyText
272 * @label: the label text
274 * Explicitely sets the text to use as label.
276 void
277 adg_toy_text_set_label(AdgToyText *toy_text, const gchar *label)
279 g_return_if_fail(ADG_IS_TOY_TEXT(label));
281 g_free(toy_text->priv->label);
282 toy_text->priv->label = g_strdup(label);
283 g_object_notify((GObject *) toy_text, "label");
285 clear_label_cache(toy_text);
289 static void
290 render(AdgEntity *entity, cairo_t *cr)
292 AdgToyText *toy_text = (AdgToyText *) entity;
293 AdgToyTextPrivate *priv = toy_text->priv;
295 if (priv->label) {
296 AdgStyle *font_style;
298 font_style = adg_entity_get_style(entity, ADG_SLOT_FONT_STYLE);
300 cairo_save(cr);
301 cairo_set_matrix(cr, adg_entity_get_paper_matrix(entity));
302 adg_style_apply(font_style, cr);
303 cairo_rotate(cr, priv->angle);
305 if (!priv->glyphs)
306 update_label_cache(toy_text, cr);
307 if (!priv->origin_cached)
308 update_origin_cache(toy_text, cr);
310 cairo_show_glyphs(cr, priv->glyphs, priv->num_glyphs);
311 cairo_restore(cr);
314 PARENT_CLASS->render(entity, cr);
317 static void
318 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
320 clear_origin_cache((AdgToyText *) entity);
321 PARENT_CLASS->model_matrix_changed(entity, parent_matrix);
324 static void
325 invalidate(AdgEntity *entity)
327 AdgToyText *toy_text = (AdgToyText *) entity;
329 clear_label_cache(toy_text);
330 clear_origin_cache(toy_text);
332 PARENT_CLASS->invalidate(entity);
335 static gboolean
336 update_origin_cache(AdgToyText *toy_text, cairo_t *cr)
338 AdgToyTextPrivate *priv;
339 AdgPoint point;
340 AdgPair *pair;
341 cairo_glyph_t *glyph;
342 double x, y;
343 int cnt;
345 priv = toy_text->priv;
346 adg_point_copy(&point, &priv->origin);
347 pair = &priv->origin_pair;
348 glyph = priv->glyphs;
349 cnt = priv->num_glyphs;
351 /* On undefined label return error */
352 if (glyph == NULL || cnt <= 0)
353 return FALSE;
355 if (priv->angle != 0.) {
356 /* Following the less surprise rule, also the paper component
357 * of the origin point should rotate with the provided angle */
358 cairo_matrix_t rotation;
359 cairo_matrix_init_rotate(&rotation, priv->angle);
360 cpml_pair_transform(&point.paper, &rotation);
363 adg_entity_point_to_pair((AdgEntity *) toy_text, &point, pair, cr);
364 priv->origin_cached = TRUE;
366 /* Check if the origin is still the same */
367 if (pair->x == glyph->x && pair->y == glyph->y)
368 return TRUE;
370 x = pair->x - glyph->x;
371 y = pair->y - glyph->y;
373 while (cnt --) {
374 glyph->x += x;
375 glyph->y += y;
376 ++ glyph;
380 static gboolean
381 update_label_cache(AdgToyText *toy_text, cairo_t *cr)
383 AdgToyTextPrivate *priv = toy_text->priv;
384 cairo_status_t status;
386 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
387 0., 0., priv->label, -1,
388 &priv->glyphs,
389 &priv->num_glyphs,
390 NULL, NULL, NULL);
392 if (status != CAIRO_STATUS_SUCCESS) {
393 g_error("Unable to build glyphs (cairo message: %s)",
394 cairo_status_to_string(status));
395 return FALSE;
398 cairo_glyph_extents(cr, priv->glyphs, priv->num_glyphs, &priv->extents);
400 clear_origin_cache(toy_text);
401 return TRUE;
404 static void
405 clear_origin_cache(AdgToyText *toy_text)
407 toy_text->priv->origin_cached = FALSE;
410 static void
411 clear_label_cache(AdgToyText *toy_text)
413 AdgToyTextPrivate *priv = toy_text->priv;
415 if (priv->glyphs) {
416 cairo_glyph_free(priv->glyphs);
417 priv->glyphs = NULL;
420 priv->num_glyphs = 0;
421 memset(&priv->extents, 0, sizeof(priv->extents));