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.
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.
32 * By default, the #AdgText:local-mix property is set to
33 * #ADG_MIX_ANCESTORS_NORMALIZED on #AdgText entities.
42 * All fields are privates and should not be used directly.
43 * Use its public methods instead.
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"
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
))
81 static void _adg_dispose (GObject
*object
);
82 static void _adg_finalize (GObject
*object
);
83 static void _adg_get_property (GObject
*object
,
87 static void _adg_set_property (GObject
*object
,
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
,
97 static void _adg_set_font_dress (AdgTextual
*textual
,
99 static AdgDress
_adg_get_font_dress (AdgTextual
*textual
);
100 static void _adg_set_text (AdgTextual
*textual
,
102 static gchar
* _adg_dup_text (AdgTextual
*textual
);
103 static void _adg_refresh_extents (AdgText
*text
);
104 static void _adg_clear_layout (AdgText
*text
);
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");
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
;
144 adg_text_init(AdgText
*text
)
146 AdgTextPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(text
, ADG_TYPE_TEXT
,
148 AdgEntityPrivate
*entity_data
= ((AdgEntity
*) text
)->data
;
150 data
->font_dress
= ADG_DRESS_FONT_TEXT
;
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
;
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
);
174 _adg_finalize(GObject
*object
)
177 AdgTextPrivate
*data
;
179 text
= (AdgText
*) object
;
184 if (_ADG_OLD_OBJECT_CLASS
->finalize
)
185 _ADG_OLD_OBJECT_CLASS
->finalize(object
);
189 _adg_get_property(GObject
*object
, guint prop_id
,
190 GValue
*value
, GParamSpec
*pspec
)
192 AdgTextPrivate
*data
= ((AdgText
*) object
)->data
;
195 case PROP_FONT_DRESS
:
196 g_value_set_int(value
, data
->font_dress
);
199 g_value_set_string(value
, data
->text
);
202 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
208 _adg_set_property(GObject
*object
, guint prop_id
,
209 const GValue
*value
, GParamSpec
*pspec
)
212 AdgTextPrivate
*data
;
214 text
= (AdgText
*) object
;
218 case PROP_FONT_DRESS
:
219 data
->font_dress
= g_value_get_int(value
);
220 _adg_clear_layout(text
);
224 data
->text
= g_value_dup_string(value
);
225 _adg_clear_layout(text
);
228 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
238 * Creates a new text entity using @text as its content.
240 * Returns: the newly created text entity
245 adg_text_new(const gchar
*text
)
247 return g_object_new(ADG_TYPE_TEXT
,
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
);
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
);
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
);
280 _adg_arrange(AdgEntity
*entity
)
283 AdgTextPrivate
*data
;
286 text
= (AdgText
*) entity
;
289 if (adg_is_string_empty(data
->text
)) {
291 CpmlExtents new_extents
;
292 new_extents
.is_defined
= FALSE
;
293 adg_entity_set_extents(entity
, &new_extents
);
294 _adg_clear_layout(text
);
296 } else if (data
->layout
!= NULL
) {
301 if (data
->layout
== NULL
) {
302 static PangoFontMap
*font_map
= NULL
;
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
);
353 _adg_render(AdgEntity
*entity
, cairo_t
*cr
)
356 AdgTextPrivate
*data
;
358 text
= (AdgText
*) entity
;
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
);
376 _adg_set_font_dress(AdgTextual
*textual
, AdgDress dress
)
378 g_object_set(textual
, "font-dress", dress
, NULL
);
382 _adg_get_font_dress(AdgTextual
*textual
)
384 AdgTextPrivate
*data
= ((AdgText
*) textual
)->data
;
385 return data
->font_dress
;
389 _adg_set_text(AdgTextual
*textual
, const gchar
*text
)
391 g_object_set(textual
, "text", text
, NULL
);
395 _adg_dup_text(AdgTextual
*textual
)
397 AdgTextPrivate
*data
= ((AdgText
*) textual
)->data
;
398 return g_strdup(data
->text
);
402 _adg_refresh_extents(AdgText
*text
)
404 AdgTextPrivate
*data
;
407 CpmlExtents new_extents
;
411 if (! data
->raw_extents
.is_defined
)
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
);
430 _adg_clear_layout(AdgText
*text
)
432 AdgTextPrivate
*data
= text
->data
;
434 if (data
->layout
!= NULL
) {
435 g_object_unref(data
->layout
);