[AdgLDim] Removed PARENT_CLASS define
[adg.git] / adg / adg-toy-text.c
blobb4c30cb8b436c354f4c05acc5c3656125de62e1d
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"
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 translatable_init (AdgTranslatableIface *iface);
47 static void get_origin (AdgTranslatable*translatable,
48 AdgPoint *dest);
49 static void set_origin (AdgTranslatable*translatable,
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);
79 G_DEFINE_TYPE_WITH_CODE(AdgToyText, adg_toy_text, ADG_TYPE_ENTITY,
80 G_IMPLEMENT_INTERFACE(ADG_TYPE_TRANSLATABLE,
81 translatable_init)
82 G_IMPLEMENT_INTERFACE(ADG_TYPE_ROTABLE, rotable_init))
85 static void
86 translatable_init(AdgTranslatableIface *iface)
88 iface->get_origin = get_origin;
89 iface->set_origin = set_origin;
92 static void
93 get_origin(AdgTranslatable *translatable, AdgPoint *dest)
95 AdgToyText *toy_text = (AdgToyText *) translatable;
96 adg_point_copy(dest, &toy_text->priv->origin);
99 static void
100 set_origin(AdgTranslatable *translatable, const AdgPoint *origin)
102 AdgToyText *toy_text = (AdgToyText *) translatable;
103 adg_point_copy(&toy_text->priv->origin, origin);
107 static void
108 rotable_init(AdgRotableIface *iface)
110 iface->get_angle = get_angle;
111 iface->set_angle = set_angle;
114 static gdouble
115 get_angle(AdgRotable *rotable)
117 AdgToyText *toy_text = (AdgToyText *) rotable;
118 return toy_text->priv->angle;
121 static void
122 set_angle(AdgRotable *rotable, gdouble angle)
124 AdgToyText *toy_text = (AdgToyText *) rotable;
125 toy_text->priv->angle = angle;
129 static void
130 adg_toy_text_class_init(AdgToyTextClass *klass)
132 GObjectClass *gobject_class;
133 AdgEntityClass *entity_class;
134 GParamSpec *param;
136 gobject_class = (GObjectClass *) klass;
137 entity_class = (AdgEntityClass *) klass;
139 g_type_class_add_private(klass, sizeof(AdgToyTextPrivate));
141 gobject_class->finalize = finalize;
142 gobject_class->get_property = get_property;
143 gobject_class->set_property = set_property;
145 entity_class->model_matrix_changed = model_matrix_changed;
146 entity_class->invalidate = invalidate;
147 entity_class->render = render;
149 g_object_class_override_property(gobject_class, PROP_ORIGIN, "origin");
150 g_object_class_override_property(gobject_class, PROP_ANGLE, "angle");
152 param = g_param_spec_string("label",
153 P_("Label"),
154 P_("The label to display"),
155 NULL, G_PARAM_READWRITE);
156 g_object_class_install_property(gobject_class, PROP_LABEL, param);
159 static void
160 adg_toy_text_init(AdgToyText *toy_text)
162 AdgToyTextPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(toy_text,
163 ADG_TYPE_TOY_TEXT,
164 AdgToyTextPrivate);
166 priv->label = NULL;
167 adg_point_unset(&priv->origin);
169 priv->origin_cached = FALSE;
170 priv->glyphs = NULL;
172 toy_text->priv = priv;
175 static void
176 finalize(GObject *object)
178 AdgToyText *toy_text = (AdgToyText *) object;
180 g_free(toy_text->priv->label);
181 clear_label_cache(toy_text);
182 clear_origin_cache(toy_text);
184 ((GObjectClass *) PARENT_CLASS)->finalize(object);
187 static void
188 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
190 AdgToyText *toy_text = (AdgToyText *) object;
192 switch (prop_id) {
193 case PROP_ORIGIN:
194 g_value_set_boxed(value, &toy_text->priv->origin);
195 break;
196 case PROP_ANGLE:
197 g_value_set_double(value, toy_text->priv->angle);
198 break;
199 case PROP_LABEL:
200 g_value_set_string(value, toy_text->priv->label);
201 break;
202 default:
203 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
204 break;
208 static void
209 set_property(GObject *object, guint prop_id,
210 const GValue *value, GParamSpec *pspec)
212 AdgToyText *toy_text = (AdgToyText *) object;
214 switch (prop_id) {
215 case PROP_ORIGIN:
216 adg_point_copy(&toy_text->priv->origin,
217 (AdgPoint *) g_value_get_boxed(value));
218 clear_origin_cache(toy_text);
219 break;
220 case PROP_ANGLE:
221 toy_text->priv->angle = g_value_get_double(value);
222 clear_origin_cache(toy_text);
223 break;
224 case PROP_LABEL:
225 g_free(toy_text->priv->label);
226 toy_text->priv->label = g_value_dup_string(value);
227 clear_label_cache(toy_text);
228 break;
229 default:
230 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
231 break;
237 * adg_toy_text_new:
238 * @label: the label text
240 * Creates a new toy text entity using @label as its text
242 * Return value: the new entity
244 AdgEntity *
245 adg_toy_text_new(const gchar *label)
247 return (AdgEntity *) g_object_new(ADG_TYPE_TOY_TEXT, "label", label, NULL);
251 * adg_toy_text_get_label:
252 * @toy_text: an #AdgToyText
254 * Gets the label text. The string is internally owned and
255 * must not be freed or modified.
257 * Return value: the label text
259 const gchar *
260 adg_toy_text_get_label(AdgToyText *toy_text)
262 g_return_val_if_fail(ADG_IS_TOY_TEXT(toy_text), NULL);
264 return toy_text->priv->label;
268 * adg_toy_text_set_label:
269 * @toy_text: an #AdgToyText
270 * @label: the label text
272 * Explicitely sets the text to use as label.
274 void
275 adg_toy_text_set_label(AdgToyText *toy_text, const gchar *label)
277 g_return_if_fail(ADG_IS_TOY_TEXT(label));
279 g_free(toy_text->priv->label);
280 toy_text->priv->label = g_strdup(label);
281 g_object_notify((GObject *) toy_text, "label");
283 clear_label_cache(toy_text);
287 static void
288 render(AdgEntity *entity, cairo_t *cr)
290 AdgToyText *toy_text = (AdgToyText *) entity;
291 AdgToyTextPrivate *priv = toy_text->priv;
293 if (priv->label) {
294 AdgStyle *font_style;
296 font_style = adg_entity_get_style(entity, ADG_SLOT_FONT_STYLE);
298 cairo_save(cr);
299 cairo_set_matrix(cr, adg_entity_get_paper_matrix(entity));
300 adg_style_apply(font_style, cr);
301 cairo_rotate(cr, priv->angle);
303 if (!priv->glyphs)
304 update_label_cache(toy_text, cr);
305 if (!priv->origin_cached)
306 update_origin_cache(toy_text, cr);
308 cairo_show_glyphs(cr, priv->glyphs, priv->num_glyphs);
309 cairo_restore(cr);
312 PARENT_CLASS->render(entity, cr);
315 static void
316 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
318 clear_origin_cache((AdgToyText *) entity);
319 PARENT_CLASS->model_matrix_changed(entity, parent_matrix);
322 static void
323 invalidate(AdgEntity *entity)
325 AdgToyText *toy_text = (AdgToyText *) entity;
327 clear_label_cache(toy_text);
328 clear_origin_cache(toy_text);
330 PARENT_CLASS->invalidate(entity);
333 static gboolean
334 update_origin_cache(AdgToyText *toy_text, cairo_t *cr)
336 AdgToyTextPrivate *priv;
337 AdgPoint point;
338 AdgPair *pair;
339 cairo_glyph_t *glyph;
340 double x, y;
341 int cnt;
343 priv = toy_text->priv;
344 adg_point_copy(&point, &priv->origin);
345 pair = &priv->origin_pair;
346 glyph = priv->glyphs;
347 cnt = priv->num_glyphs;
349 /* On undefined label return error */
350 if (glyph == NULL || cnt <= 0)
351 return FALSE;
353 if (priv->angle != 0.) {
354 /* Following the less surprise rule, also the paper component
355 * of the origin point should rotate with the provided angle */
356 cairo_matrix_t rotation;
357 cairo_matrix_init_rotate(&rotation, priv->angle);
358 cpml_pair_transform(&point.paper, &rotation);
361 adg_entity_point_to_pair((AdgEntity *) toy_text, &point, pair, cr);
362 priv->origin_cached = TRUE;
364 /* Check if the origin is still the same */
365 if (pair->x == glyph->x && pair->y == glyph->y)
366 return TRUE;
368 x = pair->x - glyph->x;
369 y = pair->y - glyph->y;
371 while (cnt --) {
372 glyph->x += x;
373 glyph->y += y;
374 ++ glyph;
377 return TRUE;
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));