build: depends on cairo-gobject if introspection is enabled
[adg.git] / src / adg / adg-text.c
blob22532a4d1e58e83947ff87a9021c789067e3ecd5
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-style.h"
54 #include "adg-font-style.h"
55 #include "adg-pango-style.h"
56 #include "adg-textual.h"
57 #include "adg-entity-private.h"
59 #include "adg-text.h"
60 #include "adg-text-private.h"
63 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_text_parent_class)
64 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_text_parent_class)
67 static void _adg_iface_init (AdgTextualIface *iface);
70 G_DEFINE_TYPE_WITH_CODE(AdgText, adg_text, ADG_TYPE_ENTITY,
71 G_IMPLEMENT_INTERFACE(ADG_TYPE_TEXTUAL, _adg_iface_init))
73 enum {
74 PROP_0,
75 PROP_FONT_DRESS,
76 PROP_TEXT
80 static void _adg_dispose (GObject *object);
81 static void _adg_finalize (GObject *object);
82 static void _adg_get_property (GObject *object,
83 guint param_id,
84 GValue *value,
85 GParamSpec *pspec);
86 static void _adg_set_property (GObject *object,
87 guint param_id,
88 const GValue *value,
89 GParamSpec *pspec);
90 static void _adg_global_changed (AdgEntity *entity);
91 static void _adg_local_changed (AdgEntity *entity);
92 static void _adg_invalidate (AdgEntity *entity);
93 static void _adg_arrange (AdgEntity *entity);
94 static void _adg_render (AdgEntity *entity,
95 cairo_t *cr);
96 static void _adg_set_font_dress (AdgTextual *textual,
97 AdgDress dress);
98 static AdgDress _adg_get_font_dress (AdgTextual *textual);
99 static void _adg_set_text (AdgTextual *textual,
100 const gchar *text);
101 static gchar * _adg_dup_text (AdgTextual *textual);
102 static void _adg_refresh_extents (AdgText *text);
103 static void _adg_clear_layout (AdgText *text);
106 static void
107 adg_text_class_init(AdgTextClass *klass)
109 GObjectClass *gobject_class;
110 AdgEntityClass *entity_class;
112 gobject_class = (GObjectClass *) klass;
113 entity_class = (AdgEntityClass *) klass;
115 g_type_class_add_private(klass, sizeof(AdgTextPrivate));
117 gobject_class->dispose = _adg_dispose;
118 gobject_class->finalize = _adg_finalize;
119 gobject_class->get_property = _adg_get_property;
120 gobject_class->set_property = _adg_set_property;
122 entity_class->global_changed = _adg_global_changed;
123 entity_class->local_changed = _adg_local_changed;
124 entity_class->invalidate = _adg_invalidate;
125 entity_class->arrange = _adg_arrange;
126 entity_class->render = _adg_render;
128 g_object_class_override_property(gobject_class, PROP_FONT_DRESS, "font-dress");
129 g_object_class_override_property(gobject_class, PROP_TEXT, "text");
132 static void
133 _adg_iface_init(AdgTextualIface *iface)
135 iface->set_font_dress = _adg_set_font_dress;
136 iface->get_font_dress = _adg_get_font_dress;
137 iface->set_text = _adg_set_text;
138 iface->dup_text = _adg_dup_text;
139 iface->text_changed = NULL;
142 static void
143 adg_text_init(AdgText *text)
145 AdgTextPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(text, ADG_TYPE_TEXT,
146 AdgTextPrivate);
147 AdgEntityPrivate *entity_data = ((AdgEntity *) text)->data;
149 data->font_dress = ADG_DRESS_FONT_TEXT;
150 data->text = NULL;
151 data->layout = NULL;
153 text->data = data;
155 /* Initialize to custom default some AdgEntity field by directly
156 * accessing the private struct to avoid notify signal emissions
158 entity_data->local_mix = ADG_MIX_ANCESTORS_NORMALIZED;
161 static void
162 _adg_dispose(GObject *object)
164 AdgText *text = (AdgText *) object;
166 _adg_clear_layout(text);
168 if (_ADG_OLD_OBJECT_CLASS->dispose)
169 _ADG_OLD_OBJECT_CLASS->dispose(object);
172 static void
173 _adg_finalize(GObject *object)
175 AdgText *text;
176 AdgTextPrivate *data;
178 text = (AdgText *) object;
179 data = text->data;
181 g_free(data->text);
183 if (_ADG_OLD_OBJECT_CLASS->finalize)
184 _ADG_OLD_OBJECT_CLASS->finalize(object);
187 static void
188 _adg_get_property(GObject *object, guint prop_id,
189 GValue *value, GParamSpec *pspec)
191 AdgTextPrivate *data = ((AdgText *) object)->data;
193 switch (prop_id) {
194 case PROP_FONT_DRESS:
195 g_value_set_enum(value, data->font_dress);
196 break;
197 case PROP_TEXT:
198 g_value_set_string(value, data->text);
199 break;
200 default:
201 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
202 break;
206 static void
207 _adg_set_property(GObject *object, guint prop_id,
208 const GValue *value, GParamSpec *pspec)
210 AdgText *text;
211 AdgTextPrivate *data;
213 text = (AdgText *) object;
214 data = text->data;
216 switch (prop_id) {
217 case PROP_FONT_DRESS:
218 data->font_dress = g_value_get_enum(value);
219 _adg_clear_layout(text);
220 break;
221 case PROP_TEXT:
222 g_free(data->text);
223 data->text = g_value_dup_string(value);
224 _adg_clear_layout(text);
225 break;
226 default:
227 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
228 break;
234 * adg_text_new:
235 * @text: the text
237 * Creates a new text entity using @text as its content.
239 * Returns: the newly created text entity
241 * Since: 1.0
243 AdgText *
244 adg_text_new(const gchar *text)
246 return g_object_new(ADG_TYPE_TEXT,
247 "text", text, NULL);
251 static void
252 _adg_global_changed(AdgEntity *entity)
254 if (_ADG_OLD_ENTITY_CLASS->global_changed != NULL)
255 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
257 _adg_refresh_extents((AdgText *) entity);
260 static void
261 _adg_local_changed(AdgEntity *entity)
263 if (_ADG_OLD_ENTITY_CLASS->local_changed != NULL)
264 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
266 _adg_refresh_extents((AdgText *) entity);
269 static void
270 _adg_invalidate(AdgEntity *entity)
272 _adg_clear_layout((AdgText *) entity);
274 if (_ADG_OLD_ENTITY_CLASS->invalidate)
275 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
278 static void
279 _adg_arrange(AdgEntity *entity)
281 AdgText *text;
282 AdgTextPrivate *data;
283 PangoRectangle size;
285 text = (AdgText *) entity;
286 data = text->data;
288 if (adg_is_string_empty(data->text)) {
289 /* Undefined text */
290 CpmlExtents new_extents;
291 new_extents.is_defined = FALSE;
292 adg_entity_set_extents(entity, &new_extents);
293 _adg_clear_layout(text);
294 return;
295 } else if (data->layout != NULL) {
296 /* Cached result */
297 return;
300 if (data->layout == NULL) {
301 static PangoFontMap *font_map = NULL;
302 AdgDress dress;
303 AdgPangoStyle *pango_style;
304 PangoFontDescription *font_description;
305 cairo_font_options_t *options;
306 PangoContext *context;
308 /* Keep around a font_map object. The rationale is here:
309 * https://bugzilla.gnome.org/show_bug.cgi?id=143542
311 * Basically, PangoFontMap is an heavy object and
312 * creating/destroying it is not the right way.
314 * In reality, the blocking issue for me was the following
315 * line make the adg-demo program crash on MinGW32:
316 * g_object_unref(font_map);
318 if (font_map == NULL)
319 font_map = pango_cairo_font_map_new();
321 dress = data->font_dress;
322 pango_style = (AdgPangoStyle *) adg_entity_style(entity, dress);
323 font_description = adg_pango_style_get_description(pango_style);
325 context = pango_context_new();
326 pango_context_set_font_map(context, font_map);
327 pango_cairo_context_set_resolution(context, 72);
329 options = adg_font_style_new_options((AdgFontStyle *) pango_style);
330 pango_cairo_context_set_font_options(context, options);
331 cairo_font_options_destroy(options);
333 data->layout = pango_layout_new(context);
334 g_object_unref(context);
336 pango_layout_set_text(data->layout, data->text, -1);
337 pango_layout_set_font_description(data->layout, font_description);
340 pango_layout_get_extents(data->layout, NULL, &size);
342 data->raw_extents.org.x = pango_units_to_double(size.x);
343 data->raw_extents.org.y = pango_units_to_double(size.y);
344 data->raw_extents.size.x = pango_units_to_double(size.width);
345 data->raw_extents.size.y = pango_units_to_double(size.height);
346 data->raw_extents.is_defined = TRUE;
348 _adg_refresh_extents((AdgText *) entity);
351 static void
352 _adg_render(AdgEntity *entity, cairo_t *cr)
354 AdgText *text;
355 AdgTextPrivate *data;
357 text = (AdgText *) entity;
358 data = text->data;
360 if (data->layout != NULL) {
361 adg_entity_apply_dress(entity, data->font_dress, cr);
362 cairo_transform(cr, adg_entity_get_global_matrix(entity));
363 cairo_transform(cr, adg_entity_get_local_matrix(entity));
365 /* Realign the text to follow the cairo toy text convention:
366 * use bottom/left corner as reference (pango uses top/left). */
367 cairo_translate(cr, 0, -data->raw_extents.size.y);
369 pango_cairo_update_layout(cr, data->layout);
370 pango_cairo_show_layout(cr, data->layout);
374 static void
375 _adg_set_font_dress(AdgTextual *textual, AdgDress dress)
377 g_object_set(textual, "font-dress", dress, NULL);
380 static AdgDress
381 _adg_get_font_dress(AdgTextual *textual)
383 AdgTextPrivate *data = ((AdgText *) textual)->data;
384 return data->font_dress;
387 static void
388 _adg_set_text(AdgTextual *textual, const gchar *text)
390 g_object_set(textual, "text", text, NULL);
393 static gchar *
394 _adg_dup_text(AdgTextual *textual)
396 AdgTextPrivate *data = ((AdgText *) textual)->data;
397 return g_strdup(data->text);
400 static void
401 _adg_refresh_extents(AdgText *text)
403 AdgTextPrivate *data;
404 AdgEntity *entity;
405 cairo_matrix_t ctm;
406 CpmlExtents new_extents;
408 data = text->data;
410 if (! data->raw_extents.is_defined)
411 return;
413 entity = (AdgEntity *) text;
415 adg_matrix_copy(&ctm, adg_entity_get_global_matrix(entity));
416 adg_matrix_transform(&ctm, adg_entity_get_local_matrix(entity),
417 ADG_TRANSFORM_AFTER);
418 cpml_extents_copy(&new_extents, &data->raw_extents);
420 /* Realign the text to follow the cairo toy text convention:
421 * use bottom/left corner as reference (pango uses top/left). */
422 new_extents.org.y -= new_extents.size.y;
424 cpml_extents_transform(&new_extents, &ctm);
425 adg_entity_set_extents(entity, &new_extents);
428 static void
429 _adg_clear_layout(AdgText *text)
431 AdgTextPrivate *data = text->data;
433 if (data->layout != NULL) {
434 g_object_unref(data->layout);
435 data->layout = NULL;