[AdgToyText] Initial implementation
[adg.git] / adg / adg-toy-text.c
blob49c78ffc889bf394187aa65f21b8d57eac42010e
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-font-style.h"
33 #include "adg-intl.h"
35 #define PARENT_CLASS ((AdgEntityClass *) adg_toy_text_parent_class)
38 enum {
39 PROP_0,
40 PROP_ORG,
41 PROP_LABEL
44 static void positionable_init (AdgPositionableIface *iface);
45 static AdgPoint*org (AdgPositionable*positionable);
47 static void finalize (GObject *object);
48 static void get_property (GObject *object,
49 guint param_id,
50 GValue *value,
51 GParamSpec *pspec);
52 static void set_property (GObject *object,
53 guint param_id,
54 const GValue *value,
55 GParamSpec *pspec);
56 static void model_matrix_changed (AdgEntity *entity,
57 AdgMatrix *parent_matrix);
58 static void invalidate (AdgEntity *entity);
59 static void render (AdgEntity *entity,
60 cairo_t *cr);
61 static gboolean update_org_cache (AdgToyText *toy_text);
62 static gboolean update_label_cache (AdgToyText *toy_text,
63 cairo_t *cr);
64 static void clear_label_cache (AdgToyText *toy_text);
65 static void move_label_cache (AdgToyText *toy_text,
66 const AdgPair *to);
69 G_DEFINE_TYPE_WITH_CODE(AdgToyText, adg_toy_text, ADG_TYPE_ENTITY,
70 G_IMPLEMENT_INTERFACE(ADG_TYPE_POSITIONABLE,
71 positionable_init))
74 static void
75 adg_toy_text_class_init(AdgToyTextClass *klass)
77 GObjectClass *gobject_class;
78 AdgEntityClass *entity_class;
79 GParamSpec *param;
81 gobject_class = (GObjectClass *) klass;
82 entity_class = (AdgEntityClass *) klass;
84 g_type_class_add_private(klass, sizeof(AdgToyTextPrivate));
86 gobject_class->finalize = finalize;
87 gobject_class->get_property = get_property;
88 gobject_class->set_property = set_property;
90 entity_class->model_matrix_changed = model_matrix_changed;
91 entity_class->invalidate = invalidate;
92 entity_class->render = render;
94 g_object_class_override_property(gobject_class, PROP_ORG, "org");
96 param = g_param_spec_string("label",
97 P_("Label"),
98 P_("The label to display"),
99 NULL, G_PARAM_READWRITE);
100 g_object_class_install_property(gobject_class, PROP_LABEL, param);
103 static void
104 adg_toy_text_init(AdgToyText *toy_text)
106 AdgToyTextPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(toy_text,
107 ADG_TYPE_TOY_TEXT,
108 AdgToyTextPrivate);
110 priv->label = NULL;
111 adg_point_unset(&priv->org);
112 priv->org_pair.x = priv->org_pair.y = 0.;
113 priv->num_glyphs = 0;
114 priv->glyphs = NULL;
116 toy_text->priv = priv;
119 static void
120 positionable_init(AdgPositionableIface *iface)
122 iface->org = org;
125 static AdgPoint *
126 org(AdgPositionable *positionable)
128 return & ((AdgToyText *) positionable)->priv->org;
131 static void
132 finalize(GObject *object)
134 AdgToyText *toy_text = (AdgToyText *) object;
136 g_free(toy_text->priv->label);
137 clear_label_cache(toy_text);
139 ((GObjectClass *) PARENT_CLASS)->finalize(object);
142 static void
143 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
145 AdgToyText *toy_text = (AdgToyText *) toy_text;
147 switch (prop_id) {
148 case PROP_ORG:
149 g_value_set_boxed(value, &toy_text->priv->org);
150 break;
151 case PROP_LABEL:
152 g_value_set_string(value, toy_text->priv->label);
153 break;
154 default:
155 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
156 break;
160 static void
161 set_property(GObject *object, guint prop_id,
162 const GValue *value, GParamSpec *pspec)
164 AdgToyText *toy_text = (AdgToyText *) object;
165 AdgToyTextPrivate *priv = toy_text->priv;
167 switch (prop_id) {
168 case PROP_ORG:
169 adg_point_copy(&priv->org, (AdgPoint *) g_value_get_boxed(value));
170 update_org_cache(toy_text);
171 break;
172 case PROP_LABEL:
173 g_free(priv->label);
174 priv->label = g_value_dup_string(value);
175 clear_label_cache(toy_text);
176 break;
177 default:
178 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
179 break;
185 * adg_toy_text_new:
186 * @label: the label text
188 * Creates a new toy text entity using @label as its text
190 * Return value: the new entity
192 AdgEntity *
193 adg_toy_text_new(const gchar *label)
195 return (AdgEntity *) g_object_new(ADG_TYPE_TOY_TEXT, "label", label, NULL);
199 * adg_toy_text_get_label:
200 * @toy_text: an #AdgToyText
202 * Gets the label text. The string is internally owned and
203 * must not be freed or modified.
205 * Return value: the label text
207 const gchar *
208 adg_toy_text_get_label(AdgToyText *toy_text)
210 g_return_val_if_fail(ADG_IS_TOY_TEXT(toy_text), NULL);
212 return toy_text->priv->label;
216 * adg_toy_text_set_label:
217 * @toy_text: an #AdgToyText
218 * @label: the label text
220 * Explicitely sets the text to use as label.
222 void
223 adg_toy_text_set_label(AdgToyText *toy_text, const gchar *label)
225 g_return_if_fail(ADG_IS_TOY_TEXT(label));
227 g_free(toy_text->priv->label);
228 toy_text->priv->label = g_strdup(label);
229 g_object_notify((GObject *) toy_text, "label");
231 clear_label_cache(toy_text);
235 static void
236 render(AdgEntity *entity, cairo_t *cr)
238 AdgToyText *toy_text = (AdgToyText *) entity;
239 AdgToyTextPrivate *priv = toy_text->priv;
241 if (priv->label) {
242 AdgStyle *font_style = adg_entity_get_style(entity, ADG_SLOT_FONT_STYLE);
243 cairo_set_matrix(cr, adg_entity_get_paper_matrix(entity));
244 adg_style_apply(font_style, cr);
246 if (!priv->glyphs) {
247 update_label_cache(toy_text, cr);
248 update_org_cache(toy_text);
251 cairo_show_glyphs(cr, priv->glyphs, priv->num_glyphs);
254 PARENT_CLASS->render(entity, cr);
257 static void
258 model_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
260 update_org_cache((AdgToyText *) entity);
261 PARENT_CLASS->model_matrix_changed(entity, parent_matrix);
264 static void
265 invalidate(AdgEntity *entity)
267 clear_label_cache((AdgToyText *) entity);
268 PARENT_CLASS->invalidate(entity);
271 static gboolean
272 update_org_cache(AdgToyText *toy_text)
274 AdgToyTextPrivate *priv = toy_text->priv;
275 AdgPoint *org = &toy_text->priv->org;
276 AdgPair *org_pair = &toy_text->priv->org_pair;
277 cairo_glyph_t *glyph;
278 double x, y;
279 int cnt;
281 org = &priv->org;
282 org_pair = &priv->org_pair;
283 glyph = priv->glyphs;
284 cnt = priv->num_glyphs;
286 if (glyph == NULL || cnt <= 0) {
287 /* No label cache to update: return */
288 return TRUE;
291 adg_entity_point_to_paper_pair((AdgEntity *) toy_text, org, org_pair);
292 if (org_pair->x == glyph->x && org_pair->y == glyph->y) {
293 /* The label is yet properly positioned */
294 return TRUE;
297 x = org_pair->x - glyph->x;
298 y = org_pair->y - glyph->y;
300 while (cnt --) {
301 glyph->x += x;
302 glyph->y += y;
303 ++ glyph;
307 static gboolean
308 update_label_cache(AdgToyText *toy_text, cairo_t *cr)
310 AdgToyTextPrivate *priv = toy_text->priv;
311 cairo_status_t status;
313 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
314 0., 0., priv->label, -1,
315 &priv->glyphs,
316 &priv->num_glyphs,
317 NULL, NULL, NULL);
319 if (status != CAIRO_STATUS_SUCCESS) {
320 g_error("Unable to build glyphs (cairo message: %s)",
321 cairo_status_to_string(status));
322 return FALSE;
325 cairo_glyph_extents(cr, priv->glyphs, priv->num_glyphs, &priv->extents);
327 return TRUE;
330 static void
331 clear_label_cache(AdgToyText *toy_text)
333 AdgToyTextPrivate *priv = toy_text->priv;
335 if (priv->glyphs) {
336 cairo_glyph_free(priv->glyphs);
337 priv->glyphs = NULL;
339 priv->num_glyphs = 0;
340 memset(&priv->extents, 0, sizeof(priv->extents));