[AdgEntity] Avoid "arrange" emission in get_extents
[adg.git] / adg / adg-entity.c
blob8e637e2b1648a255b875a960e0ccb8df465aa1b6
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_LOCAL_MODE
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 gboolean set_local_mode (AdgEntity *entity,
99 AdgTransformationMode mode);
100 static void real_invalidate (AdgEntity *entity);
101 static void real_arrange (AdgEntity *entity);
102 static void real_render (AdgEntity *entity,
103 cairo_t *cr);
105 static guint signals[LAST_SIGNAL] = { 0 };
108 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
111 static void
112 adg_entity_class_init(AdgEntityClass *klass)
114 GObjectClass *gobject_class;
115 GParamSpec *param;
116 GClosure *closure;
117 GType param_types[1];
119 gobject_class = (GObjectClass *) klass;
121 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
123 gobject_class->dispose = dispose;
124 gobject_class->get_property = get_property;
125 gobject_class->set_property = set_property;
127 klass->parent_set = NULL;
128 klass->global_changed = global_changed;
129 klass->local_changed = local_changed;
130 klass->invalidate = NULL;
131 klass->arrange= NULL;
132 klass->render = NULL;
134 param = g_param_spec_object("parent",
135 P_("Parent Entity"),
136 P_("The parent entity of this entity or NULL if this is a top-level entity"),
137 ADG_TYPE_ENTITY,
138 G_PARAM_READWRITE);
139 g_object_class_install_property(gobject_class, PROP_PARENT, param);
141 param = g_param_spec_boxed("global-map",
142 P_("Global Map"),
143 P_("The transformation to be combined with the parent ones to get the global matrix"),
144 ADG_TYPE_MATRIX,
145 G_PARAM_READWRITE);
146 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
148 param = g_param_spec_boxed("local-map",
149 P_("Local Map"),
150 P_("The transformation to be combined with the parent ones to get the local matrix"),
151 ADG_TYPE_MATRIX,
152 G_PARAM_READWRITE);
153 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
155 param = g_param_spec_enum("local-mode",
156 P_("Local Mode"),
157 P_("How the local matrix should be combined with the global matrix to get the current transformation matrix (ctm)"),
158 ADG_TYPE_TRANSFORMATION_MODE,
159 ADG_TRANSFORM_NONE,
160 G_PARAM_READWRITE);
161 g_object_class_install_property(gobject_class, PROP_LOCAL_MODE, param);
164 * AdgEntity::parent-set:
165 * @entity: an #AdgEntity
166 * @old_parent: the old parent
168 * Emitted after the parent entity has changed. The new parent
169 * can be inspected using adg_entity_get_parent().
171 * It is allowed for both old and new parent to be %NULL.
173 signals[PARENT_SET] = g_signal_new("parent-set",
174 G_OBJECT_CLASS_TYPE(gobject_class),
175 G_SIGNAL_RUN_FIRST,
176 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
177 NULL, NULL,
178 adg_marshal_VOID__OBJECT,
179 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
182 * AdgEntity::global-changed
183 * @entity: an #AdgEntity
185 * Emitted when the global map of @entity or any of its parent
186 * has changed. The default handler will compute the new global
187 * matrix, updating the internal cache.
189 signals[GLOBAL_CHANGED] = g_signal_new("global-changed",
190 G_OBJECT_CLASS_TYPE(gobject_class),
191 G_SIGNAL_RUN_FIRST,
192 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
193 NULL, NULL,
194 adg_marshal_VOID__VOID,
195 G_TYPE_NONE, 0);
198 * AdgEntity::local-changed
199 * @entity: an #AdgEntity
201 * Emitted when the local map of @entity or any of its parent
202 * has changed. The default handler will compute the new local
203 * matrix, updating the internal cache.
205 signals[LOCAL_CHANGED] = g_signal_new("local-changed",
206 G_OBJECT_CLASS_TYPE(gobject_class),
207 G_SIGNAL_RUN_FIRST,
208 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
209 NULL, NULL,
210 adg_marshal_VOID__VOID,
211 G_TYPE_NONE, 0);
214 * AdgEntity::invalidate:
215 * @entity: an #AdgEntity
217 * Invalidates the whole @entity, that is resets all the cache
218 * (if present) built during the #AdgEntity::arrange signal.
219 * The resulting state is a clean entity, similar to what you
220 * have just before the first rendering.
222 closure = g_cclosure_new(G_CALLBACK(real_invalidate), NULL, NULL);
223 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
224 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
225 adg_marshal_VOID__VOID,
226 G_TYPE_NONE, 0, param_types);
229 * AdgEntity::arrange:
230 * @entity: an #AdgEntity
232 * Arranges the layout of @entity, updating the cache if necessary,
233 * and computes the extents of @entity.
235 closure = g_cclosure_new(G_CALLBACK(real_arrange), NULL, NULL);
236 signals[ARRANGE] = g_signal_newv("arrange", ADG_TYPE_ENTITY,
237 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
238 adg_marshal_VOID__VOID,
239 G_TYPE_NONE, 0, param_types);
241 * AdgEntity::render:
242 * @entity: an #AdgEntity
243 * @cr: a #cairo_t drawing context
245 * Causes the rendering of @entity on @cr. A render signal will
246 * automatically emit #AdgEntity::arrange just before the real
247 * rendering on the cairo context.
249 closure = g_cclosure_new(G_CALLBACK(real_render), NULL, NULL);
250 param_types[0] = G_TYPE_POINTER;
251 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
252 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
253 adg_marshal_VOID__POINTER,
254 G_TYPE_NONE, 1, param_types);
257 static void
258 adg_entity_init(AdgEntity *entity)
260 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
261 ADG_TYPE_ENTITY,
262 AdgEntityPrivate);
263 data->parent = NULL;
264 cairo_matrix_init_identity(&data->global_map);
265 cairo_matrix_init_identity(&data->local_map);
266 data->local_mode = ADG_TRANSFORM_NONE;
267 data->hash_styles = NULL;
268 cairo_matrix_init_identity(&data->global_matrix);
269 cairo_matrix_init_identity(&data->local_matrix);
270 data->extents.is_defined = FALSE;
272 entity->data = data;
275 static void
276 dispose(GObject *object)
278 AdgEntity *entity;
279 AdgEntityPrivate *data;
281 entity = (AdgEntity *) object;
282 data = entity->data;
284 /* This call will emit a "notify" signal for parent.
285 * Consequentially, the references to the old parent is dropped. */
286 adg_entity_set_parent(entity, NULL);
288 if (data->hash_styles != NULL) {
289 g_hash_table_destroy(data->hash_styles);
290 data->hash_styles = NULL;
293 if (PARENT_OBJECT_CLASS->dispose != NULL)
294 PARENT_OBJECT_CLASS->dispose(object);
298 static void
299 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
301 AdgEntity *entity;
302 AdgEntityPrivate *data;
304 entity = (AdgEntity *) object;
305 data = entity->data;
307 switch (prop_id) {
308 case PROP_PARENT:
309 g_value_set_object(value, data->parent);
310 break;
311 case PROP_GLOBAL_MAP:
312 g_value_set_boxed(value, &data->global_map);
313 break;
314 case PROP_LOCAL_MAP:
315 g_value_set_boxed(value, &data->local_map);
316 break;
317 case PROP_LOCAL_MODE:
318 g_value_set_enum(value, data->local_mode);
319 break;
320 default:
321 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
322 break;
326 static void
327 set_property(GObject *object,
328 guint prop_id, const GValue *value, GParamSpec *pspec)
330 AdgEntity *entity = (AdgEntity *) object;
332 switch (prop_id) {
333 case PROP_PARENT:
334 set_parent(entity, g_value_get_object(value));
335 break;
336 case PROP_GLOBAL_MAP:
337 set_global_map(entity, g_value_get_boxed(value));
338 break;
339 case PROP_LOCAL_MAP:
340 set_local_map(entity, g_value_get_boxed(value));
341 break;
342 case PROP_LOCAL_MODE:
343 set_local_mode(entity, g_value_get_enum(value));
344 break;
345 default:
346 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
347 break;
353 * adg_entity_get_parent:
354 * @entity: an #AdgEntity
356 * Gets the parent of @entity.
358 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
360 AdgEntity *
361 adg_entity_get_parent(AdgEntity *entity)
363 AdgEntityPrivate *data;
365 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
367 data = entity->data;
369 return data->parent;
373 * adg_entity_set_parent:
374 * @entity: an #AdgEntity
375 * @parent: the parent entity
377 * <note><para>
378 * This function is only useful in entity implementations.
379 * </para></note>
381 * Sets a new parent on @entity.
383 void
384 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
386 g_return_if_fail(ADG_IS_ENTITY(entity));
388 if (set_parent(entity, parent))
389 g_object_notify((GObject *) entity, "parent");
393 * adg_entity_get_canvas:
394 * @entity: an #AdgEntity
396 * Walks on the @entity hierarchy and gets the first parent of @entity that is
397 * of #AdgCanvas derived type.
399 * Returns: the requested canvas or %NULL on errors or if there is
400 * no #AdgCanvas in the @entity hierarchy
402 AdgCanvas *
403 adg_entity_get_canvas(AdgEntity *entity)
405 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
407 while (entity) {
408 if (ADG_IS_CANVAS(entity))
409 return (AdgCanvas *) entity;
411 entity = (AdgEntity *) adg_entity_get_parent(entity);
414 return NULL;
418 * adg_entity_get_global_map:
419 * @entity: an #AdgEntity object
420 * @map: where to store the global map
422 * Gets the transformation to be used to compute the global matrix
423 * of @entity and store it in @map.
425 void
426 adg_entity_get_global_map(AdgEntity *entity, AdgMatrix *map)
428 AdgEntityPrivate *data;
430 g_return_if_fail(ADG_IS_ENTITY(entity));
431 g_return_if_fail(map != NULL);
433 data = entity->data;
434 adg_matrix_copy(map, &data->global_map);
438 * adg_entity_set_global_map:
439 * @entity: an #AdgEntity object
440 * @map: the new map
442 * Sets the new global transformation of @entity to @map:
443 * the old map is discarded. If @map is %NULL an identity
444 * matrix is implied.
446 void
447 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
449 g_return_if_fail(ADG_IS_ENTITY(entity));
451 if (set_global_map(entity, map))
452 g_object_notify((GObject *) entity, "global-map");
456 * adg_entity_transform_global_map:
457 * @entity: an #AdgEntity object
458 * @transformation: the transformation to apply
459 * @mode: how @transformation should be applied
461 * Convenient function to change the global map of @entity by
462 * applying @tranformation using the @mode operator. This is
463 * logically equivalent to the following:
465 * |[
466 * AdgMatrix tmp_map;
467 * adg_entity_get_global_map(entity, &tmp_map);
468 * adg_matrix_transform(&tmp_map, transformation, mode);
469 * adg_entity_set_global_map(entity, &tmp_map);
470 * ]|
472 void
473 adg_entity_transform_global_map(AdgEntity *entity,
474 const AdgMatrix *transformation,
475 AdgTransformationMode mode)
477 AdgEntityPrivate *data;
478 AdgMatrix map;
480 g_return_if_fail(ADG_IS_ENTITY(entity));
481 g_return_if_fail(transformation != NULL);
483 data = entity->data;
485 adg_matrix_copy(&map, &data->global_map);
486 adg_matrix_transform(&map, transformation, mode);
488 if (set_global_map(entity, &map))
489 g_object_notify((GObject *) entity, "global-map");
493 * adg_entity_get_local_map:
494 * @entity: an #AdgEntity object
495 * @map: where to store the local map
497 * Gets the transformation to be used to compute the local matrix
498 * of @entity and store it in @map.
500 void
501 adg_entity_get_local_map(AdgEntity *entity, AdgMatrix *map)
503 AdgEntityPrivate *data;
505 g_return_if_fail(ADG_IS_ENTITY(entity));
506 g_return_if_fail(map != NULL);
508 data = entity->data;
510 adg_matrix_copy(map, &data->local_map);
514 * adg_entity_set_local_map:
515 * @entity: an #AdgEntity object
516 * @map: the new map
518 * Sets the new local transformation of @entity to @map:
519 * the old map is discarded. If @map is %NULL an identity
520 * matrix is implied.
522 void
523 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
525 g_return_if_fail(ADG_IS_ENTITY(entity));
527 if (set_local_map(entity, map))
528 g_object_notify((GObject *) entity, "local-map");
532 * adg_entity_transform_local_map:
533 * @entity: an #AdgEntity object
534 * @transformation: the transformation to apply
535 * @mode: how @transformation should be applied
537 * Convenient function to change the local map of @entity by
538 * applying @tranformation using the @mode operator. This is
539 * logically equivalent to the following:
541 * |[
542 * AdgMatrix tmp_map;
543 * adg_entity_get_local_map(entity, &tmp_map);
544 * adg_matrix_transform(&tmp_map, transformation, mode);
545 * adg_entity_set_local_map(entity, &tmp_map);
546 * ]|
548 void
549 adg_entity_transform_local_map(AdgEntity *entity,
550 const AdgMatrix *transformation,
551 AdgTransformationMode mode)
553 AdgEntityPrivate *data;
554 AdgMatrix map;
556 g_return_if_fail(ADG_IS_ENTITY(entity));
557 g_return_if_fail(transformation != NULL);
559 data = entity->data;
561 adg_matrix_copy(&map, &data->local_map);
562 adg_matrix_transform(&map, transformation, mode);
564 if (set_local_map(entity, &map))
565 g_object_notify((GObject *) entity, "local-map");
569 * adg_entity_get_local_mode:
570 * @entity: an #AdgEntity object
572 * Gets the transformation mode to be used while applying the
573 * local matrix over the global matrix. It is internally used
574 * by adg_entity_get_ctm().
576 * Returns: the current transformation mode
578 AdgTransformationMode
579 adg_entity_get_local_mode(AdgEntity *entity)
581 AdgEntityPrivate *data;
583 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_TRANSFORM_NONE);
585 data = entity->data;
587 return data->local_mode;
591 * adg_entity_set_local_mode:
592 * @entity: an #AdgEntity object
593 * @mode: the new mode
595 * Sets a new transformation mode to be used while combining
596 * global and local matrices.
597 * Sets a new transformation mode to be used while applying
598 * the local matrix over the global matrix. It is internally
599 * used by adg_entity_get_ctm().
601 void
602 adg_entity_set_local_mode(AdgEntity *entity, AdgTransformationMode mode)
604 g_return_if_fail(ADG_IS_ENTITY(entity));
606 if (set_local_mode(entity, mode))
607 g_object_notify((GObject *) entity, "local-mode");
611 * adg_entity_get_extents:
612 * @entity: an #AdgEntity
613 * @extents: where to store the extents
615 * Stores a copy of the bounding box of @entity in @extents.
616 * This struct specifies the surface portion (in global space)
617 * occupied by the entity without taking into account line
618 * thickness and caps.
620 * The #AdgEntity::arrange should be emitted before this call
621 * (either explicitely or throught adg_entity_arrange()) in
622 * order to get an up to date boundary box.
624 void
625 adg_entity_get_extents(AdgEntity *entity, CpmlExtents *extents)
627 AdgEntityPrivate *data;
629 g_return_if_fail(ADG_IS_ENTITY(entity));
630 g_return_if_fail(extents != NULL);
632 data = entity->data;
634 cpml_extents_copy(extents, &data->extents);
638 * adg_entity_set_extents:
639 * @entity: an #AdgEntity
640 * @extents: the new extents
642 * <note><para>
643 * This function is only useful in entity implementations.
644 * </para></note>
646 * Sets a new bounding box for @entity. @extents can be %NULL,
647 * in which case the extents are unset.
649 void
650 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
652 AdgEntityPrivate *data;
654 g_return_if_fail(ADG_IS_ENTITY(entity));
656 data = entity->data;
658 if (extents == NULL)
659 data->extents.is_defined = FALSE;
660 else
661 cpml_extents_copy(&data->extents, extents);
665 * adg_entity_style:
666 * @entity: an #AdgEntity
667 * @dress: the dress of the style to get
669 * Gets the style to be used for @entity. @dress specifies the "type"
670 * of style to get.
672 * This method is supposed to always return a valid style instance,
673 * specifically the most appropriate one for this entity. The following
674 * sequence of checks is performed to get the proper @dress style,
675 * stopping at the first succesfull result:
677 * <orderedlist>
678 * <listitem>check if the style is defined directly by this entity type,
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_style().</listitem>
683 * </orderedlist>
685 * Returns: the requested style or %NULL on 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);
693 g_return_val_if_fail(dress != ADG_DRESS_UNDEFINED, NULL);
695 style = adg_entity_get_style(entity, dress);
697 if (style == NULL) {
698 AdgEntityPrivate *data = entity->data;
700 if (data->parent != NULL)
701 style = adg_entity_style(data->parent, dress);
702 else
703 style = adg_dress_get_style(dress);
706 return style;
710 * adg_entity_get_style:
711 * @entity: an #AdgEntity
712 * @dress: the dress of the style to get
714 * Gets the overriden @dress style from @entity. This is a kind
715 * of accessor function: to get the style to be used for rendering
716 * purpose, consider adg_entity_style().
718 * Returns: the requested style or %NULL if the @dress style
719 * is not overriden
721 AdgStyle *
722 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
724 AdgEntityPrivate *data;
726 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
727 g_return_val_if_fail(dress != ADG_DRESS_UNDEFINED, NULL);
729 data = entity->data;
731 if (data->hash_styles == NULL)
732 return NULL;
734 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
738 * adg_entity_set_style:
739 * @entity: an #AdgEntity
740 * @dress: a dress style
741 * @style: the new style to use
743 * Overrides the style of @dress for @entity and its children.
744 * If @style is %NULL, any previous override is removed.
746 void
747 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
749 AdgEntityPrivate *data;
750 gpointer p_dress;
751 AdgStyle *old_style;
753 g_return_if_fail(ADG_IS_ENTITY(entity));
755 data = entity->data;
757 if (data->hash_styles == NULL && style == NULL)
758 return;
760 if (data->hash_styles == NULL)
761 data->hash_styles = g_hash_table_new_full(NULL, NULL,
762 NULL, g_object_unref);
764 p_dress = GINT_TO_POINTER(dress);
765 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
767 if (style == old_style)
768 return;
770 if (style == NULL) {
771 g_hash_table_remove(data->hash_styles, p_dress);
772 } else {
773 g_object_ref(style);
774 g_hash_table_replace(data->hash_styles, p_dress, style);
779 * adg_entity_apply_dress:
780 * @entity: an #AdgEntity
781 * @dress: the dress style to apply
782 * @cr: a #cairo_t drawing context
784 * Convenient function to apply a @dress style (as returned by
785 * adg_entity_style()) to the @cr cairo context.
787 void
788 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
790 g_return_if_fail(ADG_IS_ENTITY(entity));
791 g_return_if_fail(cr != NULL);
793 adg_style_apply(adg_entity_style(entity, dress), cr);
797 * adg_entity_global_changed:
798 * @entity: an #AdgEntity
800 * Emits the #AdgEntity::global-changed signal on @entity and on all of
801 * its children, if any.
803 void
804 adg_entity_global_changed(AdgEntity *entity)
806 g_return_if_fail(ADG_IS_ENTITY(entity));
808 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
812 * adg_entity_local_changed:
813 * @entity: an #AdgEntity
815 * Emits the #AdgEntity::local-changed signal on @entity and on all of
816 * its children, if any.
818 void
819 adg_entity_local_changed(AdgEntity *entity)
821 g_return_if_fail(ADG_IS_ENTITY(entity));
823 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
827 * adg_entity_global_matrix:
828 * @entity: an #AdgEntity object
830 * Gets the global matrix by combining all the global maps of the
831 * @entity hierarchy. The returned value is owned by @entity and
832 * should not be changed or freed.
834 * Returns: the global matrix or %NULL on errors
836 const AdgMatrix *
837 adg_entity_global_matrix(AdgEntity *entity)
839 AdgEntityPrivate *data;
841 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
843 data = entity->data;
845 return &data->global_matrix;
849 * adg_entity_local_matrix:
850 * @entity: an #AdgEntity object
851 * @matrix: where to store the local matrix
853 * Gets the local matrix by combining all the local maps of the
854 * @entity hierarchy. The returned value is owned by @entity and
855 * should not be changed or freed.
857 * Returns: the local matrix or %NULL on errors
859 const AdgMatrix *
860 adg_entity_local_matrix(AdgEntity *entity)
862 AdgEntityPrivate *data;
864 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
866 data = entity->data;
868 return &data->local_matrix;
872 * adg_entity_get_ctm:
873 * @entity: an #AdgEntity object
874 * @ctm: where to store the current transformation matrix
876 * Gets the current transformation matrix (ctm) of @entity
877 * by combining global and local matrices in the way specified
878 * by the #AdgEntity:local-mode property. The result is stored
879 * in @ctm.
881 * This method is only useful after the #AdgEntity::arrange default
882 * handler has been called, that is at the rendering stage or while
883 * arranging the entity after the parent method has been chained up.
885 void
886 adg_entity_get_ctm(AdgEntity *entity, AdgMatrix *ctm)
888 AdgEntityPrivate *data;
890 g_return_if_fail(ADG_IS_ENTITY(entity));
891 g_return_if_fail(ctm != NULL);
893 data = entity->data;
895 adg_matrix_copy(ctm, &data->global_matrix);
896 adg_matrix_transform(ctm, &data->local_matrix, data->local_mode);
900 * adg_entity_invalidate:
901 * @entity: an #AdgEntity
903 * Emits the #AdgEntity::invalidate signal on @entity and on all of
904 * its children, if any, clearing the eventual cache stored by the
905 * #AdgEntity::arrange signal and setting the entity state similary
906 * to the just initialized entity.
908 void
909 adg_entity_invalidate(AdgEntity *entity)
911 g_return_if_fail(ADG_IS_ENTITY(entity));
913 g_signal_emit(entity, signals[INVALIDATE], 0);
917 * adg_entity_arrange:
918 * @entity: an #AdgEntity
920 * <note><para>
921 * This function is only useful in entity implementations.
922 * </para></note>
924 * Emits the #AdgEntity::arrange signal on @entity and all its children,
925 * if any. This function is rarely needed as the arrange call is usually
926 * managed by the #AdgEntity::render signal or indirectly by a call to
927 * adg_entity_get_extents().
929 void
930 adg_entity_arrange(AdgEntity *entity)
932 g_return_if_fail(ADG_IS_ENTITY(entity));
934 g_signal_emit(entity, signals[ARRANGE], 0);
938 * adg_entity_render:
939 * @entity: an #AdgEntity
940 * @cr: a #cairo_t drawing context
942 * Emits the #AdgEntity::render signal on @entity and on all of its
943 * children, if any, causing the rendering to the @cr cairo context.
945 void
946 adg_entity_render(AdgEntity *entity, cairo_t *cr)
948 g_return_if_fail(ADG_IS_ENTITY(entity));
950 g_signal_emit(entity, signals[RENDER], 0, cr);
954 static gboolean
955 set_parent(AdgEntity *entity, AdgEntity *parent)
957 AdgEntityPrivate *data;
958 AdgEntity *old_parent;
960 data = entity->data;
961 old_parent = data->parent;
963 /* Check if parent has changed */
964 if (parent == old_parent)
965 return FALSE;
967 if (parent != NULL)
968 g_object_ref(parent);
970 data->parent = parent;
971 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
973 if (old_parent != NULL)
974 g_object_unref(old_parent);
976 return TRUE;
979 static void
980 global_changed(AdgEntity *entity)
982 AdgEntityPrivate *data;
983 AdgMatrix *matrix;
985 data = entity->data;
986 matrix = &data->global_matrix;
988 if (data->parent == NULL) {
989 adg_matrix_copy(matrix, &data->global_map);
990 } else {
991 adg_matrix_copy(matrix, adg_entity_global_matrix(data->parent));
992 cairo_matrix_multiply(matrix, &data->global_map, matrix);
996 static void
997 local_changed(AdgEntity *entity)
999 AdgEntityPrivate *data;
1000 AdgMatrix *matrix;
1002 data = entity->data;
1003 matrix = &data->local_matrix;
1005 if (data->parent == NULL) {
1006 adg_matrix_copy(matrix, &data->local_map);
1007 } else {
1008 adg_matrix_copy(matrix, adg_entity_local_matrix(data->parent));
1009 cairo_matrix_multiply(matrix, &data->local_map, matrix);
1013 static gboolean
1014 set_global_map(AdgEntity *entity, const AdgMatrix *map)
1016 AdgEntityPrivate *data = entity->data;
1018 if (map != NULL && adg_matrix_equal(&data->global_map, map))
1019 return FALSE;
1021 if (map == NULL)
1022 cairo_matrix_init_identity(&data->global_map);
1023 else
1024 adg_matrix_copy(&data->global_map, map);
1026 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0, &data->global_matrix);
1027 return TRUE;
1030 static gboolean
1031 set_local_map(AdgEntity *entity, const AdgMatrix *map)
1033 AdgEntityPrivate *data = entity->data;
1035 if (map != NULL && adg_matrix_equal(&data->local_map, map))
1036 return FALSE;
1038 if (map == NULL)
1039 cairo_matrix_init_identity(&data->local_map);
1040 else
1041 adg_matrix_copy(&data->local_map, map);
1043 g_signal_emit(entity, signals[LOCAL_CHANGED], 0, &data->local_matrix);
1044 return TRUE;
1047 static gboolean
1048 set_local_mode(AdgEntity *entity, AdgTransformationMode mode)
1050 AdgEntityPrivate *data = entity->data;
1052 if (mode == data->local_mode)
1053 return FALSE;
1055 data->local_mode = mode;
1057 return TRUE;
1060 static void
1061 real_invalidate(AdgEntity *entity)
1063 AdgEntityPrivate *data = entity->data;
1064 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1066 if (klass->invalidate == NULL) {
1067 /* Do not raise any warning if invalidate() is not defined,
1068 * assuming entity does not have cache to be cleared */
1069 } else {
1070 klass->invalidate(entity);
1073 data->extents.is_defined = FALSE;
1076 static void
1077 real_arrange(AdgEntity *entity)
1079 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1081 if (klass->arrange == NULL) {
1082 /* The arrange() method must be defined */
1083 g_warning("%s: `arrange' method not implemented for type `%s'",
1084 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1085 } else {
1086 klass->arrange(entity);
1090 static void
1091 real_render(AdgEntity *entity, cairo_t *cr)
1093 AdgEntityPrivate *data = entity->data;
1094 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1096 if (klass->render == NULL) {
1097 /* The render method must be defined */
1098 g_warning("%s: `render' method not implemented for type `%s'",
1099 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1100 } else {
1101 /* Before the rendering, the entity should be arranged */
1102 g_signal_emit(entity, signals[ARRANGE], 0);
1104 cairo_save(cr);
1105 cairo_set_matrix(cr, &data->global_matrix);
1106 klass->render(entity, cr);
1107 cairo_restore(cr);