[AdgToyText] Removed PARENT_CLASS define
[adg.git] / adg / adg-toy-text.c
bloba135df286bcd2e283ac33410cf9972c820b67bd8
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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:toy-text
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-translatable.h"
32 #include "adg-rotable.h"
33 #include "adg-font-style.h"
34 #include "adg-intl.h"
37 enum {
38 PROP_0,
39 PROP_ORIGIN,
40 PROP_ANGLE,
41 PROP_LABEL
44 static void translatable_init (AdgTranslatableIface *iface);
45 static void get_origin (AdgTranslatable*translatable,
46 AdgPoint *dest);
47 static void set_origin (AdgTranslatable*translatable,
48 const AdgPoint *origin);
50 static void rotable_init (AdgRotableIface*iface);
51 static gdouble get_angle (AdgRotable *rotable);
52 static void set_angle (AdgRotable *rotable,
53 gdouble angle);
55 static void finalize (GObject *object);
56 static void get_property (GObject *object,
57 guint param_id,
58 GValue *value,
59 GParamSpec *pspec);
60 static void set_property (GObject *object,
61 guint param_id,
62 const GValue *value,
63 GParamSpec *pspec);
64 static void model_matrix_changed (AdgEntity *entity,
65 AdgMatrix *parent_matrix);
66 static void invalidate (AdgEntity *entity);
67 static void render (AdgEntity *entity,
68 cairo_t *cr);
69 static gboolean update_origin_cache (AdgToyText *toy_text,
70 cairo_t *cr);
71 static gboolean update_label_cache (AdgToyText *toy_text,
72 cairo_t *cr);
73 static void clear_label_cache (AdgToyText *toy_text);
74 static void clear_origin_cache (AdgToyText *toy_text);
77 G_DEFINE_TYPE_WITH_CODE(AdgToyText, adg_toy_text, ADG_TYPE_ENTITY,
78 G_IMPLEMENT_INTERFACE(ADG_TYPE_TRANSLATABLE,
79 translatable_init)
80 G_IMPLEMENT_INTERFACE(ADG_TYPE_ROTABLE, rotable_init))
83 static void
84 translatable_init(AdgTranslatableIface *iface)
86 iface->get_origin = get_origin;
87 iface->set_origin = set_origin;
90 static void
91 get_origin(AdgTranslatable *translatable, AdgPoint *dest)
93 AdgToyText *toy_text = (AdgToyText *) translatable;
94 adg_point_copy(dest, &toy_text->priv->origin);
97 static void
98 set_origin(AdgTranslatable *translatable, const AdgPoint *origin)
100 AdgToyText *toy_text = (AdgToyText *) translatable;
101 adg_point_copy(&toy_text->priv->origin, origin);
105 static void
106 rotable_init(AdgRotableIface *iface)
108 iface->get_angle = get_angle;
109 iface->set_angle = set_angle;
112 static gdouble
113 get_angle(AdgRotable *rotable)
115 AdgToyText *toy_text = (AdgToyText *) rotable;
116 return toy_text->priv->angle;
119 static void
120 set_angle(AdgRotable *rotable, gdouble angle)
122 AdgToyText *toy_text = (AdgToyText *) rotable;
123 toy_text->priv->angle = angle;
127 static void
128 adg_toy_text_class_init(AdgToyTextClass *klass)
130 GObjectClass *gobject_class;
131 AdgEntityClass *entity_class;
132 GParamSpec *param;
134 gobject_class = (GObjectClass *) klass;
135 entity_class = (AdgEntityClass *) klass;
137 g_type_class_add_private(klass, sizeof(AdgToyTextPrivate));
139 gobject_class->finalize = finalize;
140 gobject_class->get_property = get_property;
141 gobject_class->set_property = set_property;
143 entity_class->model_matrix_changed = model_matrix_changed;
144 entity_class->invalidate = invalidate;
145 entity_class->render = render;
147 g_object_class_override_property(gobject_class, PROP_ORIGIN, "origin");
148 g_object_class_override_property(gobject_class, PROP_ANGLE, "angle");
150 param = g_param_spec_string("label",
151 P_("Label"),
152 P_("The label to display"),
153 NULL, G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_LABEL, param);
157 static void
158 adg_toy_text_init(AdgToyText *toy_text)
160 AdgToyTextPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(toy_text,
161 ADG_TYPE_TOY_TEXT,
162 AdgToyTextPrivate);
164 priv->label = NULL;
165 adg_point_unset(&priv->origin);
167 priv->origin_cached = FALSE;
168 priv->glyphs = NULL;
170 toy_text->priv = priv;
173 static void
174 finalize(GObject *object)
176 AdgToyText *toy_text;
177 GObjectClass *object_class;
179 toy_text = (AdgToyText *) object;
180 object_class = (GObjectClass *) adg_toy_text_parent_class;
182 g_free(toy_text->priv->label);
183 clear_label_cache(toy_text);
184 clear_origin_cache(toy_text);
186 if (object_class->finalize != NULL)
187 object_class->finalize(object);
190 static void
191 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
193 AdgToyText *toy_text = (AdgToyText *) object;
195 switch (prop_id) {
196 case PROP_ORIGIN:
197 g_value_set_boxed(value, &toy_text->priv->origin);
198 break;
199 case PROP_ANGLE:
200 g_value_set_double(value, toy_text->priv->angle);
201 break;
202 case PROP_LABEL:
203 g_value_set_string(value, toy_text->priv->label);
204 break;
205 default:
206 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
207 break;
211 static void
212 set_property(GObject *object, guint prop_id,
213 const GValue *value, GParamSpec *pspec)
215 AdgToyText *toy_text = (AdgToyText *) object;
217 switch (prop_id) {
218 case PROP_ORIGIN:
219 adg_point_copy(&toy_text->priv->origin,
220 (AdgPoint *) g_value_get_boxed(value));
221 clear_origin_cache(toy_text);
222 break;
223 case PROP_ANGLE:
224 toy_text->priv->angle = g_value_get_double(value);
225 clear_origin_cache(toy_text);
226 break;
227 case PROP_LABEL:
228 g_free(toy_text->priv->label);
229 toy_text->priv->label = g_value_dup_string(value);
230 clear_label_cache(toy_text);
231 break;
232 default:
233 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
234 break;
240 * adg_toy_text_new:
241 * @label: the label text
243 * Creates a new toy text entity using @label as its text
245 * Return value: the new entity
247 AdgEntity *
248 adg_toy_text_new(const gchar *label)
250 return (AdgEntity *) g_object_new(ADG_TYPE_TOY_TEXT, "label", label, NULL);
254 * adg_toy_text_get_label:
255 * @toy_text: an #AdgToyText
257 * Gets the label text. The string is internally owned and
258 * must not be freed or modified.
260 * Return value: the label text
262 const gchar *
263 adg_toy_text_get_label(AdgToyText *toy_text)
265 g_return_val_if_fail(ADG_IS_TOY_TEXT(toy_text), NULL);
267 return toy_text->priv->label;
271 * adg_toy_text_set_label:
272 * @toy_text: an #AdgToyText
273 * @label: the label text
275 * Explicitely sets the text to use as label.
277 void
278 adg_toy_text_set_label(AdgToyText *toy_text, const gchar *label)
280 g_return_if_fail(ADG_IS_TOY_TEXT(label));
282 g_free(toy_text->priv->label);
283 toy_text->priv->label = g_strdup(label);
284 g_object_notify((GObject *) toy_text, "label");
286 clear_label_cache(toy_text);
290 static void
291 render(AdgEntity *entity, cairo_t *cr)
293 AdgToyText *toy_text;
294 AdgToyTextPrivate *priv;
295 AdgEntityClass *entity_class;
297 toy_text = (AdgToyText *) entity;
298 priv = toy_text->priv;
299 entity_class = (AdgEntityClass *) adg_toy_text_parent_class;
301 if (priv->label) {
302 AdgStyle *font_style;
304 font_style = adg_entity_get_style(entity, ADG_SLOT_FONT_STYLE);
306 cairo_save(cr);
307 cairo_set_matrix(cr, adg_entity_get_paper_matrix(entity));
308 adg_style_apply(font_style, cr);
309 cairo_rotate(cr, priv->angle);
311 if (!priv->glyphs)
312 update_label_cache(toy_text, cr);
313 if (!priv->origin_cached)
314 update_origin_cache(toy_text, cr);
316 cairo_show_glyphs(cr, priv->glyphs, priv->num_glyphs);
317 cairo_restore(cr);
320 if (entity_class->render != NULL)
321 entity_class->render(entity, cr);
324 static void
325 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
327 AdgEntityClass *entity_class = (AdgEntityClass *) adg_toy_text_parent_class;
329 clear_origin_cache((AdgToyText *) entity);
331 if (entity_class->model_matrix_changed != NULL)
332 entity_class->model_matrix_changed(entity, parent_matrix);
335 static void
336 invalidate(AdgEntity *entity)
338 AdgToyText *toy_text;
339 AdgEntityClass *entity_class;
341 toy_text = (AdgToyText *) entity;
342 entity_class = (AdgEntityClass *) adg_toy_text_parent_class;
344 clear_label_cache(toy_text);
345 clear_origin_cache(toy_text);
347 if (entity_class->invalidate != NULL)
348 entity_class->invalidate(entity);
351 static gboolean
352 update_origin_cache(AdgToyText *toy_text, cairo_t *cr)
354 AdgToyTextPrivate *priv;
355 AdgPoint point;
356 AdgPair *pair;
357 cairo_glyph_t *glyph;
358 double x, y;
359 int cnt;
361 priv = toy_text->priv;
362 adg_point_copy(&point, &priv->origin);
363 pair = &priv->origin_pair;
364 glyph = priv->glyphs;
365 cnt = priv->num_glyphs;
367 /* On undefined label return error */
368 if (glyph == NULL || cnt <= 0)
369 return FALSE;
371 if (priv->angle != 0.) {
372 /* Following the less surprise rule, also the paper component
373 * of the origin point should rotate with the provided angle */
374 cairo_matrix_t rotation;
375 cairo_matrix_init_rotate(&rotation, priv->angle);
376 cpml_pair_transform(&point.paper, &rotation);
379 adg_entity_point_to_pair((AdgEntity *) toy_text, &point, pair, cr);
380 priv->origin_cached = TRUE;
382 /* Check if the origin is still the same */
383 if (pair->x == glyph->x && pair->y == glyph->y)
384 return TRUE;
386 x = pair->x - glyph->x;
387 y = pair->y - glyph->y;
389 while (cnt --) {
390 glyph->x += x;
391 glyph->y += y;
392 ++ glyph;
395 return TRUE;
398 static gboolean
399 update_label_cache(AdgToyText *toy_text, cairo_t *cr)
401 AdgToyTextPrivate *priv = toy_text->priv;
402 cairo_status_t status;
404 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
405 0., 0., priv->label, -1,
406 &priv->glyphs,
407 &priv->num_glyphs,
408 NULL, NULL, NULL);
410 if (status != CAIRO_STATUS_SUCCESS) {
411 g_error("Unable to build glyphs (cairo message: %s)",
412 cairo_status_to_string(status));
413 return FALSE;
416 cairo_glyph_extents(cr, priv->glyphs, priv->num_glyphs, &priv->extents);
418 clear_origin_cache(toy_text);
419 return TRUE;
422 static void
423 clear_origin_cache(AdgToyText *toy_text)
425 toy_text->priv->origin_cached = FALSE;
428 static void
429 clear_label_cache(AdgToyText *toy_text)
431 AdgToyTextPrivate *priv = toy_text->priv;
433 if (priv->glyphs) {
434 cairo_glyph_free(priv->glyphs);
435 priv->glyphs = NULL;
438 priv->num_glyphs = 0;
439 memset(&priv->extents, 0, sizeof(priv->extents));