adg: properly initialize AdgText:local-mix
[adg.git] / src / adg / adg-text.c
blobde07bf33280fac1ccfdfbffe188604164581f604
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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.
21 /**
22 * SECTION:adg-text
23 * @short_description: A pango based text entity
25 * The #AdgText class is the main class for showing text. It works
26 * the same way as #AdgToyText but uses pango instead of the so
27 * called cairo "toy" API.
29 * The text entity is not subject to the local matrix, only its origin is.
31 * <note><para>
32 * By default, the #AdgText:local-mix property is set to
33 * #ADG_MIX_ANCESTORS_NORMALIZED on #AdgText entities.
34 * </para></note>
36 * Since: 1.0
37 **/
39 /**
40 * AdgText:
42 * All fields are privates and should not be used directly.
43 * Use its public methods instead.
45 * Since: 1.0
46 **/
49 #include "adg-internal.h"
50 #include <pango/pangocairo.h>
52 #include "adg-dress.h"
53 #include "adg-dress-builtins.h"
54 #include "adg-style.h"
55 #include "adg-font-style.h"
56 #include "adg-pango-style.h"
57 #include "adg-textual.h"
58 #include "adg-entity-private.h"
60 #include "adg-text.h"
61 #include "adg-text-private.h"
64 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_text_parent_class)
65 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_text_parent_class)
68 static void _adg_iface_init (AdgTextualIface *iface);
71 G_DEFINE_TYPE_WITH_CODE(AdgText, adg_text, ADG_TYPE_ENTITY,
72 G_IMPLEMENT_INTERFACE(ADG_TYPE_TEXTUAL, _adg_iface_init))
74 enum {
75 PROP_0,
76 PROP_FONT_DRESS,
77 PROP_TEXT
81 static void _adg_dispose (GObject *object);
82 static void _adg_finalize (GObject *object);
83 static void _adg_get_property (GObject *object,
84 guint param_id,
85 GValue *value,
86 GParamSpec *pspec);
87 static void _adg_set_property (GObject *object,
88 guint param_id,
89 const GValue *value,
90 GParamSpec *pspec);
91 static void _adg_global_changed (AdgEntity *entity);
92 static void _adg_local_changed (AdgEntity *entity);
93 static void _adg_invalidate (AdgEntity *entity);
94 static void _adg_arrange (AdgEntity *entity);
95 static void _adg_render (AdgEntity *entity,
96 cairo_t *cr);
97 static void _adg_set_font_dress (AdgTextual *textual,
98 AdgDress dress);
99 static AdgDress _adg_get_font_dress (AdgTextual *textual);
100 static void _adg_set_text (AdgTextual *textual,
101 const gchar *text);
102 static gchar * _adg_dup_text (AdgTextual *textual);
103 static void _adg_refresh_extents (AdgText *text);
104 static void _adg_clear_layout (AdgText *text);
107 static void
108 adg_text_class_init(AdgTextClass *klass)
110 GObjectClass *gobject_class;
111 AdgEntityClass *entity_class;
113 gobject_class = (GObjectClass *) klass;
114 entity_class = (AdgEntityClass *) klass;
116 g_type_class_add_private(klass, sizeof(AdgTextPrivate));
118 gobject_class->dispose = _adg_dispose;
119 gobject_class->finalize = _adg_finalize;
120 gobject_class->get_property = _adg_get_property;
121 gobject_class->set_property = _adg_set_property;
123 entity_class->global_changed = _adg_global_changed;
124 entity_class->local_changed = _adg_local_changed;
125 entity_class->invalidate = _adg_invalidate;
126 entity_class->arrange = _adg_arrange;
127 entity_class->render = _adg_render;
129 g_object_class_override_property(gobject_class, PROP_FONT_DRESS, "font-dress");
130 g_object_class_override_property(gobject_class, PROP_TEXT, "text");
133 static void
134 _adg_iface_init(AdgTextualIface *iface)
136 iface->set_font_dress = _adg_set_font_dress;
137 iface->get_font_dress = _adg_get_font_dress;
138 iface->set_text = _adg_set_text;
139 iface->dup_text = _adg_dup_text;
140 iface->text_changed = NULL;
143 static void
144 adg_text_init(AdgText *text)
146 AdgTextPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(text, ADG_TYPE_TEXT,
147 AdgTextPrivate);
148 AdgEntityPrivate *entity_data = ((AdgEntity *) text)->data;
150 data->font_dress = ADG_DRESS_FONT_TEXT;
151 data->text = NULL;
152 data->layout = NULL;
154 text->data = data;
156 /* Initialize to custom default some AdgEntity field by directly
157 * accessing the private struct to avoid notify signal emissions
159 entity_data->local_mix = ADG_MIX_ANCESTORS_NORMALIZED;
162 static void
163 _adg_dispose(GObject *object)
165 AdgText *text = (AdgText *) object;
167 _adg_clear_layout(text);
169 if (_ADG_OLD_OBJECT_CLASS->dispose)
170 _ADG_OLD_OBJECT_CLASS->dispose(object);
173 static void
174 _adg_finalize(GObject *object)
176 AdgText *text;
177 AdgTextPrivate *data;
179 text = (AdgText *) object;
180 data = text->data;
182 g_free(data->text);
184 if (_ADG_OLD_OBJECT_CLASS->finalize)
185 _ADG_OLD_OBJECT_CLASS->finalize(object);
188 static void
189 _adg_get_property(GObject *object, guint prop_id,
190 GValue *value, GParamSpec *pspec)
192 AdgTextPrivate *data = ((AdgText *) object)->data;
194 switch (prop_id) {
195 case PROP_FONT_DRESS:
196 g_value_set_int(value, data->font_dress);
197 break;
198 case PROP_TEXT:
199 g_value_set_string(value, data->text);
200 break;
201 default:
202 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
203 break;
207 static void
208 _adg_set_property(GObject *object, guint prop_id,
209 const GValue *value, GParamSpec *pspec)
211 AdgText *text;
212 AdgTextPrivate *data;
214 text = (AdgText *) object;
215 data = text->data;
217 switch (prop_id) {
218 case PROP_FONT_DRESS:
219 data->font_dress = g_value_get_int(value);
220 _adg_clear_layout(text);
221 break;
222 case PROP_TEXT:
223 g_free(data->text);
224 data->text = g_value_dup_string(value);
225 _adg_clear_layout(text);
226 break;
227 default:
228 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
229 break;
235 * adg_text_new:
236 * @text: the text
238 * Creates a new text entity using @text as its content.
240 * Returns: the newly created text entity
242 * Since: 1.0
244 AdgText *
245 adg_text_new(const gchar *text)
247 return g_object_new(ADG_TYPE_TEXT,
248 "text", text, NULL);
252 static void
253 _adg_global_changed(AdgEntity *entity)
255 if (_ADG_OLD_ENTITY_CLASS->global_changed != NULL)
256 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
258 _adg_refresh_extents((AdgText *) entity);
261 static void
262 _adg_local_changed(AdgEntity *entity)
264 if (_ADG_OLD_ENTITY_CLASS->local_changed != NULL)
265 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
267 _adg_refresh_extents((AdgText *) entity);
270 static void
271 _adg_invalidate(AdgEntity *entity)
273 _adg_clear_layout((AdgText *) entity);
275 if (_ADG_OLD_ENTITY_CLASS->invalidate)
276 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
279 static void
280 _adg_arrange(AdgEntity *entity)
282 AdgText *text;
283 AdgTextPrivate *data;
284 PangoRectangle size;
286 text = (AdgText *) entity;
287 data = text->data;
289 if (adg_is_string_empty(data->text)) {
290 /* Undefined text */
291 CpmlExtents new_extents;
292 new_extents.is_defined = FALSE;
293 adg_entity_set_extents(entity, &new_extents);
294 _adg_clear_layout(text);
295 return;
296 } else if (data->layout != NULL) {
297 /* Cached result */
298 return;
301 if (data->layout == NULL) {
302 static PangoFontMap *font_map = NULL;
303 AdgDress dress;
304 AdgPangoStyle *pango_style;
305 PangoFontDescription *font_description;
306 cairo_font_options_t *options;
307 PangoContext *context;
309 /* Keep around a font_map object. The rationale is here:
310 * https://bugzilla.gnome.org/show_bug.cgi?id=143542
312 * Basically, PangoFontMap is an heavy object and
313 * creating/destroying it is not the right way.
315 * In reality, the blocking issue for me was the following
316 * line make the adg-demo program crash on MinGW32:
317 * g_object_unref(font_map);
319 if (font_map == NULL)
320 font_map = pango_cairo_font_map_new();
322 dress = data->font_dress;
323 pango_style = (AdgPangoStyle *) adg_entity_style(entity, dress);
324 font_description = adg_pango_style_get_description(pango_style);
326 context = pango_context_new();
327 pango_context_set_font_map(context, font_map);
328 pango_cairo_context_set_resolution(context, 72);
330 options = adg_font_style_new_options((AdgFontStyle *) pango_style);
331 pango_cairo_context_set_font_options(context, options);
332 cairo_font_options_destroy(options);
334 data->layout = pango_layout_new(context);
335 g_object_unref(context);
337 pango_layout_set_text(data->layout, data->text, -1);
338 pango_layout_set_font_description(data->layout, font_description);
341 pango_layout_get_extents(data->layout, NULL, &size);
343 data->raw_extents.org.x = pango_units_to_double(size.x);
344 data->raw_extents.org.y = pango_units_to_double(size.y);
345 data->raw_extents.size.x = pango_units_to_double(size.width);
346 data->raw_extents.size.y = pango_units_to_double(size.height);
347 data->raw_extents.is_defined = TRUE;
349 _adg_refresh_extents((AdgText *) entity);
352 static void
353 _adg_render(AdgEntity *entity, cairo_t *cr)
355 AdgText *text;
356 AdgTextPrivate *data;
358 text = (AdgText *) entity;
359 data = text->data;
361 if (data->layout != NULL) {
362 adg_entity_apply_dress(entity, data->font_dress, cr);
363 cairo_transform(cr, adg_entity_get_global_matrix(entity));
364 cairo_transform(cr, adg_entity_get_local_matrix(entity));
366 /* Realign the text to follow the cairo toy text convention:
367 * use bottom/left corner as reference (pango uses top/left). */
368 cairo_translate(cr, 0, -data->raw_extents.size.y);
370 pango_cairo_update_layout(cr, data->layout);
371 pango_cairo_show_layout(cr, data->layout);
375 static void
376 _adg_set_font_dress(AdgTextual *textual, AdgDress dress)
378 g_object_set(textual, "font-dress", dress, NULL);
381 static AdgDress
382 _adg_get_font_dress(AdgTextual *textual)
384 AdgTextPrivate *data = ((AdgText *) textual)->data;
385 return data->font_dress;
388 static void
389 _adg_set_text(AdgTextual *textual, const gchar *text)
391 g_object_set(textual, "text", text, NULL);
394 static gchar *
395 _adg_dup_text(AdgTextual *textual)
397 AdgTextPrivate *data = ((AdgText *) textual)->data;
398 return g_strdup(data->text);
401 static void
402 _adg_refresh_extents(AdgText *text)
404 AdgTextPrivate *data;
405 AdgEntity *entity;
406 cairo_matrix_t ctm;
407 CpmlExtents new_extents;
409 data = text->data;
411 if (! data->raw_extents.is_defined)
412 return;
414 entity = (AdgEntity *) text;
416 adg_matrix_copy(&ctm, adg_entity_get_global_matrix(entity));
417 adg_matrix_transform(&ctm, adg_entity_get_local_matrix(entity),
418 ADG_TRANSFORM_AFTER);
419 cpml_extents_copy(&new_extents, &data->raw_extents);
421 /* Realign the text to follow the cairo toy text convention:
422 * use bottom/left corner as reference (pango uses top/left). */
423 new_extents.org.y -= new_extents.size.y;
425 cpml_extents_transform(&new_extents, &ctm);
426 adg_entity_set_extents(entity, &new_extents);
429 static void
430 _adg_clear_layout(AdgText *text)
432 AdgTextPrivate *data = text->data;
434 if (data->layout != NULL) {
435 g_object_unref(data->layout);
436 data->layout = NULL;