[AdgEntity] Changed "local-mode" to "normalized"
[adg.git] / adg / adg-entity.c
blob415986f5ebee45f873b5952f7616607ab00ed7a7
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.
21 /**
22 * SECTION:adg-entity
23 * @short_description: The base class for renderable objects
25 * This abstract class provides the base for all renderable objects.
27 * To provide a proper #AdgEntity derived type, you must at least
28 * implement its arrange() and render() virtual methods. Also, if
29 * you are using some sort of caching, ensure to clear it in the
30 * invalidate() method.
31 **/
33 /**
34 * AdgEntity:
36 * All fields are private and should not be used directly.
37 * Use its public methods instead.
38 **/
40 /**
41 * AdgEntityClass:
42 * @parent_set: called after the parent has changed
43 * @invalidate: invalidating callback, used to clear the cache
44 * @arrange: prepare the layout and fill the extents struct
45 * @render: rendering callback, it must be implemented
47 * Any entity (if not abstract) must implement at least the @render method.
48 **/
51 #include "adg-entity.h"
52 #include "adg-entity-private.h"
53 #include "adg-canvas.h"
54 #include "adg-font-style.h"
55 #include "adg-dim-style.h"
56 #include "adg-marshal.h"
57 #include "adg-intl.h"
59 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
62 enum {
63 PROP_0,
64 PROP_PARENT,
65 PROP_GLOBAL_MAP,
66 PROP_LOCAL_MAP,
67 PROP_NORMALIZED
70 enum {
71 PARENT_SET,
72 GLOBAL_CHANGED,
73 LOCAL_CHANGED,
74 INVALIDATE,
75 ARRANGE,
76 RENDER,
77 LAST_SIGNAL
81 static void dispose (GObject *object);
82 static void get_property (GObject *object,
83 guint prop_id,
84 GValue *value,
85 GParamSpec *pspec);
86 static void set_property (GObject *object,
87 guint prop_id,
88 const GValue *value,
89 GParamSpec *pspec);
90 static gboolean set_parent (AdgEntity *entity,
91 AdgEntity *parent);
92 static void global_changed (AdgEntity *entity);
93 static void local_changed (AdgEntity *entity);
94 static gboolean set_global_map (AdgEntity *entity,
95 const AdgMatrix *map);
96 static gboolean set_local_map (AdgEntity *entity,
97 const AdgMatrix *map);
98 static void real_invalidate (AdgEntity *entity);
99 static void real_arrange (AdgEntity *entity);
100 static void real_render (AdgEntity *entity,
101 cairo_t *cr);
103 static guint signals[LAST_SIGNAL] = { 0 };
106 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
109 static void
110 adg_entity_class_init(AdgEntityClass *klass)
112 GObjectClass *gobject_class;
113 GParamSpec *param;
114 GClosure *closure;
115 GType param_types[1];
117 gobject_class = (GObjectClass *) klass;
119 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
121 gobject_class->dispose = dispose;
122 gobject_class->get_property = get_property;
123 gobject_class->set_property = set_property;
125 klass->parent_set = NULL;
126 klass->global_changed = global_changed;
127 klass->local_changed = local_changed;
128 klass->invalidate = NULL;
129 klass->arrange= NULL;
130 klass->render = NULL;
132 param = g_param_spec_object("parent",
133 P_("Parent Entity"),
134 P_("The parent entity of this entity or NULL if this is a top-level entity"),
135 ADG_TYPE_ENTITY,
136 G_PARAM_READWRITE);
137 g_object_class_install_property(gobject_class, PROP_PARENT, param);
139 param = g_param_spec_boxed("global-map",
140 P_("Global Map"),
141 P_("The transformation to be combined with the parent ones to get the global matrix"),
142 ADG_TYPE_MATRIX,
143 G_PARAM_READWRITE);
144 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
146 param = g_param_spec_boxed("local-map",
147 P_("Local Map"),
148 P_("The transformation to be combined with the parent ones to get the local matrix"),
149 ADG_TYPE_MATRIX,
150 G_PARAM_READWRITE);
151 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
153 param = g_param_spec_boolean("normalized",
154 P_("Normalized Entity"),
155 P_("A normalized entity is insensitive to the scaling of the local matrix, that is its local matrix is normalized before being applied"),
156 FALSE,
157 G_PARAM_READWRITE);
158 g_object_class_install_property(gobject_class, PROP_NORMALIZED, param);
161 * AdgEntity::parent-set:
162 * @entity: an #AdgEntity
163 * @old_parent: the old parent
165 * Emitted after the parent entity has changed. The new parent
166 * can be inspected using adg_entity_get_parent().
168 * It is allowed for both old and new parent to be %NULL.
170 signals[PARENT_SET] = g_signal_new("parent-set",
171 G_OBJECT_CLASS_TYPE(gobject_class),
172 G_SIGNAL_RUN_FIRST,
173 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
174 NULL, NULL,
175 adg_marshal_VOID__OBJECT,
176 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
179 * AdgEntity::global-changed
180 * @entity: an #AdgEntity
182 * Emitted when the global map of @entity or any of its parent
183 * has changed. The default handler will compute the new global
184 * matrix, updating the internal cache.
186 signals[GLOBAL_CHANGED] = g_signal_new("global-changed",
187 G_OBJECT_CLASS_TYPE(gobject_class),
188 G_SIGNAL_RUN_FIRST,
189 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
190 NULL, NULL,
191 adg_marshal_VOID__VOID,
192 G_TYPE_NONE, 0);
195 * AdgEntity::local-changed
196 * @entity: an #AdgEntity
198 * Emitted when the local map of @entity or any of its parent
199 * has changed. The default handler will compute the new local
200 * matrix, updating the internal cache.
202 signals[LOCAL_CHANGED] = g_signal_new("local-changed",
203 G_OBJECT_CLASS_TYPE(gobject_class),
204 G_SIGNAL_RUN_FIRST,
205 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
206 NULL, NULL,
207 adg_marshal_VOID__VOID,
208 G_TYPE_NONE, 0);
211 * AdgEntity::invalidate:
212 * @entity: an #AdgEntity
214 * Invalidates the whole @entity, that is resets all the cache
215 * (if present) built during the #AdgEntity::arrange signal.
216 * The resulting state is a clean entity, similar to what you
217 * have just before the first rendering.
219 closure = g_cclosure_new(G_CALLBACK(real_invalidate), NULL, NULL);
220 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
221 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
222 adg_marshal_VOID__VOID,
223 G_TYPE_NONE, 0, param_types);
226 * AdgEntity::arrange:
227 * @entity: an #AdgEntity
229 * Arranges the layout of @entity, updating the cache if necessary,
230 * and computes the extents of @entity.
232 closure = g_cclosure_new(G_CALLBACK(real_arrange), NULL, NULL);
233 signals[ARRANGE] = g_signal_newv("arrange", ADG_TYPE_ENTITY,
234 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
235 adg_marshal_VOID__VOID,
236 G_TYPE_NONE, 0, param_types);
238 * AdgEntity::render:
239 * @entity: an #AdgEntity
240 * @cr: a #cairo_t drawing context
242 * Causes the rendering of @entity on @cr. A render signal will
243 * automatically emit #AdgEntity::arrange just before the real
244 * rendering on the cairo context.
246 closure = g_cclosure_new(G_CALLBACK(real_render), NULL, NULL);
247 param_types[0] = G_TYPE_POINTER;
248 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
249 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
250 adg_marshal_VOID__POINTER,
251 G_TYPE_NONE, 1, param_types);
254 static void
255 adg_entity_init(AdgEntity *entity)
257 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
258 ADG_TYPE_ENTITY,
259 AdgEntityPrivate);
260 data->parent = NULL;
261 cairo_matrix_init_identity(&data->global_map);
262 cairo_matrix_init_identity(&data->local_map);
263 data->normalized = FALSE;
264 data->hash_styles = NULL;
265 cairo_matrix_init_identity(&data->global_matrix);
266 cairo_matrix_init_identity(&data->local_matrix);
267 data->extents.is_defined = FALSE;
269 entity->data = data;
272 static void
273 dispose(GObject *object)
275 AdgEntity *entity;
276 AdgEntityPrivate *data;
278 entity = (AdgEntity *) object;
279 data = entity->data;
281 /* This call will emit a "notify" signal for parent.
282 * Consequentially, the references to the old parent is dropped. */
283 adg_entity_set_parent(entity, NULL);
285 if (data->hash_styles != NULL) {
286 g_hash_table_destroy(data->hash_styles);
287 data->hash_styles = NULL;
290 if (PARENT_OBJECT_CLASS->dispose != NULL)
291 PARENT_OBJECT_CLASS->dispose(object);
295 static void
296 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
298 AdgEntity *entity;
299 AdgEntityPrivate *data;
301 entity = (AdgEntity *) object;
302 data = entity->data;
304 switch (prop_id) {
305 case PROP_PARENT:
306 g_value_set_object(value, data->parent);
307 break;
308 case PROP_GLOBAL_MAP:
309 g_value_set_boxed(value, &data->global_map);
310 break;
311 case PROP_LOCAL_MAP:
312 g_value_set_boxed(value, &data->local_map);
313 break;
314 case PROP_NORMALIZED:
315 g_value_set_boolean(value, data->normalized);
316 break;
317 default:
318 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
319 break;
323 static void
324 set_property(GObject *object,
325 guint prop_id, const GValue *value, GParamSpec *pspec)
327 AdgEntity *entity;
328 AdgEntityPrivate *data;
330 entity = (AdgEntity *) object;
331 data = entity->data;
333 switch (prop_id) {
334 case PROP_PARENT:
335 set_parent(entity, g_value_get_object(value));
336 break;
337 case PROP_GLOBAL_MAP:
338 set_global_map(entity, g_value_get_boxed(value));
339 break;
340 case PROP_LOCAL_MAP:
341 set_local_map(entity, g_value_get_boxed(value));
342 break;
343 case PROP_NORMALIZED:
344 data->normalized = g_value_get_boolean(value);
345 break;
346 default:
347 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
348 break;
354 * adg_entity_get_parent:
355 * @entity: an #AdgEntity
357 * Gets the parent of @entity.
359 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
361 AdgEntity *
362 adg_entity_get_parent(AdgEntity *entity)
364 AdgEntityPrivate *data;
366 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
368 data = entity->data;
370 return data->parent;
374 * adg_entity_set_parent:
375 * @entity: an #AdgEntity
376 * @parent: the parent entity
378 * <note><para>
379 * This function is only useful in entity implementations.
380 * </para></note>
382 * Sets a new parent on @entity.
384 void
385 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
387 g_return_if_fail(ADG_IS_ENTITY(entity));
389 if (set_parent(entity, parent))
390 g_object_notify((GObject *) entity, "parent");
394 * adg_entity_get_canvas:
395 * @entity: an #AdgEntity
397 * Walks on the @entity hierarchy and gets the first parent of @entity that is
398 * of #AdgCanvas derived type.
400 * Returns: the requested canvas or %NULL on errors or if there is
401 * no #AdgCanvas in the @entity hierarchy
403 AdgCanvas *
404 adg_entity_get_canvas(AdgEntity *entity)
406 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
408 while (entity) {
409 if (ADG_IS_CANVAS(entity))
410 return (AdgCanvas *) entity;
412 entity = (AdgEntity *) adg_entity_get_parent(entity);
415 return NULL;
419 * adg_entity_get_global_map:
420 * @entity: an #AdgEntity object
421 * @map: where to store the global map
423 * Gets the transformation to be used to compute the global matrix
424 * of @entity and store it in @map.
426 void
427 adg_entity_get_global_map(AdgEntity *entity, AdgMatrix *map)
429 AdgEntityPrivate *data;
431 g_return_if_fail(ADG_IS_ENTITY(entity));
432 g_return_if_fail(map != NULL);
434 data = entity->data;
435 adg_matrix_copy(map, &data->global_map);
439 * adg_entity_set_global_map:
440 * @entity: an #AdgEntity object
441 * @map: the new map
443 * Sets the new global transformation of @entity to @map:
444 * the old map is discarded. If @map is %NULL an identity
445 * matrix is implied.
447 void
448 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
450 g_return_if_fail(ADG_IS_ENTITY(entity));
452 if (set_global_map(entity, map))
453 g_object_notify((GObject *) entity, "global-map");
457 * adg_entity_transform_global_map:
458 * @entity: an #AdgEntity object
459 * @transformation: the transformation to apply
460 * @mode: how @transformation should be applied
462 * Convenient function to change the global map of @entity by
463 * applying @tranformation using the @mode operator. This is
464 * logically equivalent to the following:
466 * |[
467 * AdgMatrix tmp_map;
468 * adg_entity_get_global_map(entity, &tmp_map);
469 * adg_matrix_transform(&tmp_map, transformation, mode);
470 * adg_entity_set_global_map(entity, &tmp_map);
471 * ]|
473 void
474 adg_entity_transform_global_map(AdgEntity *entity,
475 const AdgMatrix *transformation,
476 AdgTransformationMode mode)
478 AdgEntityPrivate *data;
479 AdgMatrix map;
481 g_return_if_fail(ADG_IS_ENTITY(entity));
482 g_return_if_fail(transformation != NULL);
484 data = entity->data;
486 adg_matrix_copy(&map, &data->global_map);
487 adg_matrix_transform(&map, transformation, mode);
489 if (set_global_map(entity, &map))
490 g_object_notify((GObject *) entity, "global-map");
494 * adg_entity_get_local_map:
495 * @entity: an #AdgEntity object
496 * @map: where to store the local map
498 * Gets the transformation to be used to compute the local matrix
499 * of @entity and store it in @map.
501 void
502 adg_entity_get_local_map(AdgEntity *entity, AdgMatrix *map)
504 AdgEntityPrivate *data;
506 g_return_if_fail(ADG_IS_ENTITY(entity));
507 g_return_if_fail(map != NULL);
509 data = entity->data;
511 adg_matrix_copy(map, &data->local_map);
515 * adg_entity_set_local_map:
516 * @entity: an #AdgEntity object
517 * @map: the new map
519 * Sets the new local transformation of @entity to @map:
520 * the old map is discarded. If @map is %NULL an identity
521 * matrix is implied.
523 void
524 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
526 g_return_if_fail(ADG_IS_ENTITY(entity));
528 if (set_local_map(entity, map))
529 g_object_notify((GObject *) entity, "local-map");
533 * adg_entity_transform_local_map:
534 * @entity: an #AdgEntity object
535 * @transformation: the transformation to apply
536 * @mode: how @transformation should be applied
538 * Convenient function to change the local map of @entity by
539 * applying @tranformation using the @mode operator. This is
540 * logically equivalent to the following:
542 * |[
543 * AdgMatrix tmp_map;
544 * adg_entity_get_local_map(entity, &tmp_map);
545 * adg_matrix_transform(&tmp_map, transformation, mode);
546 * adg_entity_set_local_map(entity, &tmp_map);
547 * ]|
549 void
550 adg_entity_transform_local_map(AdgEntity *entity,
551 const AdgMatrix *transformation,
552 AdgTransformationMode mode)
554 AdgEntityPrivate *data;
555 AdgMatrix map;
557 g_return_if_fail(ADG_IS_ENTITY(entity));
558 g_return_if_fail(transformation != NULL);
560 data = entity->data;
562 adg_matrix_copy(&map, &data->local_map);
563 adg_matrix_transform(&map, transformation, mode);
565 if (set_local_map(entity, &map))
566 g_object_notify((GObject *) entity, "local-map");
570 * adg_entity_set_normalized:
571 * @entity: an #AdgEntity object
573 * Gets the normalized state of @entity. Check out the
574 * adg_entity_set_normalized() to know what a normalized entity is.
576 * Returns: the normalized state of @entity
578 gboolean
579 adg_entity_get_normalized(AdgEntity *entity)
581 AdgEntityPrivate *data;
583 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
585 data = entity->data;
587 return data->normalized;
591 * adg_entity_set_normalized:
592 * @entity: an #AdgEntity object
593 * @normalized: new normalized state
595 * Sets a new normalized state on @entity. A normalized entity
596 * is immune to the scaling of the local matrix: this means
597 * the local matrix is normalized with adg_matrix_normalize()
598 * before being used.
600 void
601 adg_entity_set_normalized(AdgEntity *entity, gboolean normalized)
603 AdgEntityPrivate *data;
605 g_return_if_fail(ADG_IS_ENTITY(entity));
607 data = entity->data;
609 data->normalized = normalized;
613 * adg_entity_get_extents:
614 * @entity: an #AdgEntity
615 * @extents: where to store the extents
617 * Stores a copy of the bounding box of @entity in @extents.
618 * This struct specifies the surface portion (in global space)
619 * occupied by the entity without taking into account line
620 * thickness and caps.
622 * The #AdgEntity::arrange should be emitted before this call
623 * (either explicitely or throught adg_entity_arrange()) in
624 * order to get an up to date boundary box.
626 void
627 adg_entity_get_extents(AdgEntity *entity, CpmlExtents *extents)
629 AdgEntityPrivate *data;
631 g_return_if_fail(ADG_IS_ENTITY(entity));
632 g_return_if_fail(extents != NULL);
634 data = entity->data;
636 cpml_extents_copy(extents, &data->extents);
640 * adg_entity_set_extents:
641 * @entity: an #AdgEntity
642 * @extents: the new extents
644 * <note><para>
645 * This function is only useful in entity implementations.
646 * </para></note>
648 * Sets a new bounding box for @entity. @extents can be %NULL,
649 * in which case the extents are unset.
651 void
652 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
654 AdgEntityPrivate *data;
656 g_return_if_fail(ADG_IS_ENTITY(entity));
658 data = entity->data;
660 if (extents == NULL)
661 data->extents.is_defined = FALSE;
662 else
663 cpml_extents_copy(&data->extents, extents);
667 * adg_entity_style:
668 * @entity: an #AdgEntity
669 * @dress: the dress of the style to get
671 * Gets the style to be used for @entity. @dress specifies which
672 * "family" of style to get.
674 * The following sequence of checks is performed to get the proper
675 * style, stopping at the first succesfull result:
677 * <orderedlist>
678 * <listitem>check if the style is directly overriden by this entity,
679 * as returned by adg_entity_get_style();</listitem>
680 * <listitem>check if @entity has a parent, in which case returns the
681 * adg_entity_style() of the parent;</listitem>
682 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
683 * </orderedlist>
685 * Returns: the requested style or %NULL for transparent dresses or errors
687 AdgStyle *
688 adg_entity_style(AdgEntity *entity, AdgDress dress)
690 AdgStyle *style;
692 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
694 style = adg_entity_get_style(entity, dress);
696 if (style == NULL) {
697 AdgEntityPrivate *data = entity->data;
699 if (data->parent != NULL)
700 style = adg_entity_style(data->parent, dress);
701 else
702 style = adg_dress_get_fallback(dress);
705 return style;
709 * adg_entity_get_style:
710 * @entity: an #AdgEntity
711 * @dress: the dress of the style to get
713 * Gets the overriden @dress style from @entity. This is a kind
714 * of accessor function: to get the style to be used for rendering
715 * purpose, use adg_entity_style() instead.
717 * Returns: the requested style or %NULL if the @dress style
718 * is not overriden
720 AdgStyle *
721 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
723 AdgEntityPrivate *data;
725 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
727 data = entity->data;
729 if (data->hash_styles == NULL)
730 return NULL;
732 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
736 * adg_entity_set_style:
737 * @entity: an #AdgEntity
738 * @dress: a dress style
739 * @style: the new style to use
741 * Overrides the style of @dress for @entity and its children.
742 * If @style is %NULL, any previous override is removed.
744 * The new style must still be compatible with @dress: check out
745 * the adg_dress_style_is_compatible() documentation to know
746 * what a compatible style means.
748 void
749 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
751 AdgEntityPrivate *data;
752 gpointer p_dress;
753 AdgStyle *old_style;
755 g_return_if_fail(ADG_IS_ENTITY(entity));
757 data = entity->data;
759 if (data->hash_styles == NULL && style == NULL)
760 return;
762 if (data->hash_styles == NULL)
763 data->hash_styles = g_hash_table_new_full(NULL, NULL,
764 NULL, g_object_unref);
766 p_dress = GINT_TO_POINTER(dress);
767 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
769 if (style == old_style)
770 return;
772 if (style == NULL) {
773 g_hash_table_remove(data->hash_styles, p_dress);
774 return;
777 if (!adg_dress_style_is_compatible(dress, style)) {
778 GType ancestor_type = adg_dress_get_ancestor_type(dress);
780 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
781 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
782 g_type_name(ancestor_type), adg_dress_name(dress));
784 return;
787 g_object_ref(style);
788 g_hash_table_replace(data->hash_styles, p_dress, style);
792 * adg_entity_apply_dress:
793 * @entity: an #AdgEntity
794 * @dress: the dress style to apply
795 * @cr: a #cairo_t drawing context
797 * Convenient function to apply a @dress style (as returned by
798 * adg_entity_style()) to the @cr cairo context.
800 void
801 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
803 AdgStyle *style;
805 g_return_if_fail(ADG_IS_ENTITY(entity));
806 g_return_if_fail(cr != NULL);
808 style = adg_entity_style(entity, dress);
810 if (style != NULL)
811 adg_style_apply(style, entity, cr);
815 * adg_entity_global_changed:
816 * @entity: an #AdgEntity
818 * Emits the #AdgEntity::global-changed signal on @entity and on all of
819 * its children, if any.
821 void
822 adg_entity_global_changed(AdgEntity *entity)
824 g_return_if_fail(ADG_IS_ENTITY(entity));
826 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
830 * adg_entity_local_changed:
831 * @entity: an #AdgEntity
833 * Emits the #AdgEntity::local-changed signal on @entity and on all of
834 * its children, if any.
836 void
837 adg_entity_local_changed(AdgEntity *entity)
839 g_return_if_fail(ADG_IS_ENTITY(entity));
841 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
845 * adg_entity_global_matrix:
846 * @entity: an #AdgEntity object
848 * Gets the global matrix by combining all the global maps of the
849 * @entity hierarchy. The returned value is owned by @entity and
850 * should not be changed or freed.
852 * Returns: the global matrix or %NULL on errors
854 const AdgMatrix *
855 adg_entity_global_matrix(AdgEntity *entity)
857 AdgEntityPrivate *data;
859 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
861 data = entity->data;
863 return &data->global_matrix;
867 * adg_entity_local_matrix:
868 * @entity: an #AdgEntity object
869 * @matrix: where to store the local matrix
871 * Gets the local matrix by combining all the local maps of the
872 * @entity hierarchy. The returned value is owned by @entity and
873 * should not be changed or freed.
875 * Returns: the local matrix or %NULL on errors
877 const AdgMatrix *
878 adg_entity_local_matrix(AdgEntity *entity)
880 AdgEntityPrivate *data;
882 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
884 data = entity->data;
886 return &data->local_matrix;
890 * adg_entity_ctm:
891 * @entity: an #AdgEntity object
893 * Gets the current transformation matrix (ctm) of @entity
894 * by transforming the local matrix with the global matrix.
896 * This method is only useful after the #AdgEntity::arrange default
897 * handler has been called, that is at the rendering stage or while
898 * arranging the entity after the parent method has been chained up.
900 * Returns: the current transformation matrix or %NULL on errors
902 const AdgMatrix *
903 adg_entity_ctm(AdgEntity *entity)
905 AdgEntityPrivate *data;
907 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
909 data = entity->data;
911 return &data->ctm;
915 * adg_entity_invalidate:
916 * @entity: an #AdgEntity
918 * Emits the #AdgEntity::invalidate signal on @entity and on all of
919 * its children, if any, clearing the eventual cache stored by the
920 * #AdgEntity::arrange signal and setting the entity state similary
921 * to the just initialized entity.
923 void
924 adg_entity_invalidate(AdgEntity *entity)
926 g_return_if_fail(ADG_IS_ENTITY(entity));
928 g_signal_emit(entity, signals[INVALIDATE], 0);
932 * adg_entity_arrange:
933 * @entity: an #AdgEntity
935 * <note><para>
936 * This function is only useful in entity implementations.
937 * </para></note>
939 * Emits the #AdgEntity::arrange signal on @entity and all its children,
940 * if any. This function is rarely needed as the arrange call is usually
941 * managed by the #AdgEntity::render signal or indirectly by a call to
942 * adg_entity_get_extents().
944 void
945 adg_entity_arrange(AdgEntity *entity)
947 g_return_if_fail(ADG_IS_ENTITY(entity));
949 g_signal_emit(entity, signals[ARRANGE], 0);
953 * adg_entity_render:
954 * @entity: an #AdgEntity
955 * @cr: a #cairo_t drawing context
957 * Emits the #AdgEntity::render signal on @entity and on all of its
958 * children, if any, causing the rendering to the @cr cairo context.
960 void
961 adg_entity_render(AdgEntity *entity, cairo_t *cr)
963 g_return_if_fail(ADG_IS_ENTITY(entity));
965 g_signal_emit(entity, signals[RENDER], 0, cr);
969 static gboolean
970 set_parent(AdgEntity *entity, AdgEntity *parent)
972 AdgEntityPrivate *data;
973 AdgEntity *old_parent;
975 data = entity->data;
976 old_parent = data->parent;
978 /* Check if parent has changed */
979 if (parent == old_parent)
980 return FALSE;
982 if (parent != NULL)
983 g_object_ref(parent);
985 data->parent = parent;
986 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
988 if (old_parent != NULL)
989 g_object_unref(old_parent);
991 return TRUE;
994 static void
995 global_changed(AdgEntity *entity)
997 AdgEntityPrivate *data;
998 AdgMatrix *map, *matrix;
1000 data = entity->data;
1001 map = &data->global_map;
1002 matrix = &data->global_matrix;
1004 if (data->parent == NULL) {
1005 adg_matrix_copy(matrix, map);
1006 } else {
1007 adg_matrix_copy(matrix, adg_entity_global_matrix(data->parent));
1008 adg_matrix_transform(matrix, map, ADG_TRANSFORM_AFTER);
1012 static void
1013 local_changed(AdgEntity *entity)
1015 AdgEntityPrivate *data;
1016 AdgMatrix *map, *matrix;
1018 data = entity->data;
1019 map = &data->local_map;
1020 matrix = &data->local_matrix;
1022 if (data->parent == NULL) {
1023 adg_matrix_copy(matrix, map);
1024 } else {
1025 adg_matrix_copy(matrix, adg_entity_local_matrix(data->parent));
1026 adg_matrix_transform(matrix, map, ADG_TRANSFORM_AFTER);
1029 if (data->normalized)
1030 adg_matrix_normalize(matrix);
1033 static gboolean
1034 set_global_map(AdgEntity *entity, const AdgMatrix *map)
1036 AdgEntityPrivate *data = entity->data;
1038 if (map != NULL && adg_matrix_equal(&data->global_map, map))
1039 return FALSE;
1041 if (map == NULL)
1042 cairo_matrix_init_identity(&data->global_map);
1043 else
1044 adg_matrix_copy(&data->global_map, map);
1046 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0, &data->global_matrix);
1047 return TRUE;
1050 static gboolean
1051 set_local_map(AdgEntity *entity, const AdgMatrix *map)
1053 AdgEntityPrivate *data = entity->data;
1055 if (map != NULL && adg_matrix_equal(&data->local_map, map))
1056 return FALSE;
1058 if (map == NULL)
1059 cairo_matrix_init_identity(&data->local_map);
1060 else
1061 adg_matrix_copy(&data->local_map, map);
1063 g_signal_emit(entity, signals[LOCAL_CHANGED], 0, &data->local_matrix);
1064 return TRUE;
1067 static void
1068 real_invalidate(AdgEntity *entity)
1070 AdgEntityPrivate *data = entity->data;
1071 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1073 if (klass->invalidate == NULL) {
1074 /* Do not raise any warning if invalidate() is not defined,
1075 * assuming entity does not have cache to be cleared */
1076 } else {
1077 klass->invalidate(entity);
1080 data->extents.is_defined = FALSE;
1083 static void
1084 real_arrange(AdgEntity *entity)
1086 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1088 if (klass->arrange == NULL) {
1089 /* The arrange() method must be defined */
1090 g_warning("%s: `arrange' method not implemented for type `%s'",
1091 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1092 } else {
1093 AdgEntityPrivate *data;
1094 AdgMatrix *ctm;
1096 data = entity->data;
1097 ctm = &data->ctm;
1099 /* Update the ctm (current transformation matrix) */
1100 adg_matrix_copy(ctm, &data->global_matrix);
1101 adg_matrix_transform(ctm, &data->local_matrix, ADG_TRANSFORM_BEFORE);
1103 klass->arrange(entity);
1107 static void
1108 real_render(AdgEntity *entity, cairo_t *cr)
1110 AdgEntityPrivate *data = entity->data;
1111 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1113 if (klass->render == NULL) {
1114 /* The render method must be defined */
1115 g_warning("%s: `render' method not implemented for type `%s'",
1116 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1117 } else {
1118 /* Before the rendering, the entity should be arranged */
1119 g_signal_emit(entity, signals[ARRANGE], 0);
1121 cairo_save(cr);
1122 cairo_set_matrix(cr, &data->global_matrix);
1123 klass->render(entity, cr);
1124 cairo_restore(cr);