[AdgTransformationMode] Renamed to AdgTransformMode
[adg.git] / adg / adg-entity.c
blob1a90081ba3b7abcdfbfbaf85c4929e4125a6ce90
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 **/
50 /**
51 * AdgEntityCallback:
52 * @entity: an #AdgEntity
53 * @user_data: a general purpose pointer
55 * Callback used when inspecting or browsing entities. For example,
56 * it is passed to adg_model_foreach_dependency() to perform an
57 * operation on all the entities depending on an #AdgModel.
58 **/
61 #include "adg-entity.h"
62 #include "adg-entity-private.h"
63 #include "adg-canvas.h"
64 #include "adg-font-style.h"
65 #include "adg-dim-style.h"
66 #include "adg-marshal.h"
67 #include "adg-intl.h"
69 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
72 enum {
73 PROP_0,
74 PROP_PARENT,
75 PROP_GLOBAL_MAP,
76 PROP_LOCAL_MAP,
77 PROP_NORMALIZED
80 enum {
81 PARENT_SET,
82 GLOBAL_CHANGED,
83 LOCAL_CHANGED,
84 INVALIDATE,
85 ARRANGE,
86 RENDER,
87 LAST_SIGNAL
91 static void dispose (GObject *object);
92 static void get_property (GObject *object,
93 guint prop_id,
94 GValue *value,
95 GParamSpec *pspec);
96 static void set_property (GObject *object,
97 guint prop_id,
98 const GValue *value,
99 GParamSpec *pspec);
100 static gboolean set_parent (AdgEntity *entity,
101 AdgEntity *parent);
102 static void global_changed (AdgEntity *entity);
103 static void local_changed (AdgEntity *entity);
104 static gboolean set_global_map (AdgEntity *entity,
105 const AdgMatrix *map);
106 static gboolean set_local_map (AdgEntity *entity,
107 const AdgMatrix *map);
108 static void real_invalidate (AdgEntity *entity);
109 static void real_arrange (AdgEntity *entity);
110 static void real_render (AdgEntity *entity,
111 cairo_t *cr);
113 static guint signals[LAST_SIGNAL] = { 0 };
116 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
119 static void
120 adg_entity_class_init(AdgEntityClass *klass)
122 GObjectClass *gobject_class;
123 GParamSpec *param;
124 GClosure *closure;
125 GType param_types[1];
127 gobject_class = (GObjectClass *) klass;
129 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
131 gobject_class->dispose = dispose;
132 gobject_class->get_property = get_property;
133 gobject_class->set_property = set_property;
135 klass->parent_set = NULL;
136 klass->global_changed = global_changed;
137 klass->local_changed = local_changed;
138 klass->invalidate = NULL;
139 klass->arrange= NULL;
140 klass->render = NULL;
142 param = g_param_spec_object("parent",
143 P_("Parent Entity"),
144 P_("The parent entity of this entity or NULL if this is a top-level entity"),
145 ADG_TYPE_ENTITY,
146 G_PARAM_READWRITE);
147 g_object_class_install_property(gobject_class, PROP_PARENT, param);
149 param = g_param_spec_boxed("global-map",
150 P_("Global Map"),
151 P_("The transformation to be combined with the parent ones to get the global matrix"),
152 ADG_TYPE_MATRIX,
153 G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
156 param = g_param_spec_boxed("local-map",
157 P_("Local Map"),
158 P_("The transformation to be combined with the parent ones to get the local matrix"),
159 ADG_TYPE_MATRIX,
160 G_PARAM_READWRITE);
161 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
163 param = g_param_spec_boolean("normalized",
164 P_("Normalized Entity"),
165 P_("A normalized entity is insensitive to the scaling of the local matrix, that is its local matrix is normalized before being applied"),
166 FALSE,
167 G_PARAM_READWRITE);
168 g_object_class_install_property(gobject_class, PROP_NORMALIZED, param);
171 * AdgEntity::parent-set:
172 * @entity: an #AdgEntity
173 * @old_parent: the old parent
175 * Emitted after the parent entity has changed. The new parent
176 * can be inspected using adg_entity_get_parent().
178 * It is allowed for both old and new parent to be %NULL.
180 signals[PARENT_SET] = g_signal_new("parent-set",
181 G_OBJECT_CLASS_TYPE(gobject_class),
182 G_SIGNAL_RUN_FIRST,
183 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
184 NULL, NULL,
185 adg_marshal_VOID__OBJECT,
186 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
189 * AdgEntity::global-changed
190 * @entity: an #AdgEntity
192 * Emitted when the global map of @entity or any of its parent
193 * has changed. The default handler will compute the new global
194 * matrix, updating the internal cache.
196 signals[GLOBAL_CHANGED] = g_signal_new("global-changed",
197 G_OBJECT_CLASS_TYPE(gobject_class),
198 G_SIGNAL_RUN_FIRST,
199 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
200 NULL, NULL,
201 adg_marshal_VOID__VOID,
202 G_TYPE_NONE, 0);
205 * AdgEntity::local-changed
206 * @entity: an #AdgEntity
208 * Emitted when the local map of @entity or any of its parent
209 * has changed. The default handler will compute the new local
210 * matrix, updating the internal cache.
212 signals[LOCAL_CHANGED] = g_signal_new("local-changed",
213 G_OBJECT_CLASS_TYPE(gobject_class),
214 G_SIGNAL_RUN_FIRST,
215 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
216 NULL, NULL,
217 adg_marshal_VOID__VOID,
218 G_TYPE_NONE, 0);
221 * AdgEntity::invalidate:
222 * @entity: an #AdgEntity
224 * Invalidates the whole @entity, that is resets all the cache
225 * (if present) built during the #AdgEntity::arrange signal.
226 * The resulting state is a clean entity, similar to what you
227 * have just before the first rendering.
229 closure = g_cclosure_new(G_CALLBACK(real_invalidate), NULL, NULL);
230 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
231 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
232 adg_marshal_VOID__VOID,
233 G_TYPE_NONE, 0, param_types);
236 * AdgEntity::arrange:
237 * @entity: an #AdgEntity
239 * Arranges the layout of @entity, updating the cache if necessary,
240 * and computes the extents of @entity.
242 closure = g_cclosure_new(G_CALLBACK(real_arrange), NULL, NULL);
243 signals[ARRANGE] = g_signal_newv("arrange", ADG_TYPE_ENTITY,
244 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
245 adg_marshal_VOID__VOID,
246 G_TYPE_NONE, 0, param_types);
248 * AdgEntity::render:
249 * @entity: an #AdgEntity
250 * @cr: a #cairo_t drawing context
252 * Causes the rendering of @entity on @cr. A render signal will
253 * automatically emit #AdgEntity::arrange just before the real
254 * rendering on the cairo context.
256 closure = g_cclosure_new(G_CALLBACK(real_render), NULL, NULL);
257 param_types[0] = G_TYPE_POINTER;
258 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
259 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
260 adg_marshal_VOID__POINTER,
261 G_TYPE_NONE, 1, param_types);
264 static void
265 adg_entity_init(AdgEntity *entity)
267 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
268 ADG_TYPE_ENTITY,
269 AdgEntityPrivate);
270 data->parent = NULL;
271 cairo_matrix_init_identity(&data->global_map);
272 cairo_matrix_init_identity(&data->local_map);
273 data->normalized = FALSE;
274 data->hash_styles = NULL;
275 cairo_matrix_init_identity(&data->global_matrix);
276 cairo_matrix_init_identity(&data->local_matrix);
277 data->extents.is_defined = FALSE;
279 entity->data = data;
282 static void
283 dispose(GObject *object)
285 AdgEntity *entity;
286 AdgEntityPrivate *data;
288 entity = (AdgEntity *) object;
289 data = entity->data;
291 /* This call will emit a "notify" signal for parent.
292 * Consequentially, the references to the old parent is dropped. */
293 adg_entity_set_parent(entity, NULL);
295 if (data->hash_styles != NULL) {
296 g_hash_table_destroy(data->hash_styles);
297 data->hash_styles = NULL;
300 if (PARENT_OBJECT_CLASS->dispose != NULL)
301 PARENT_OBJECT_CLASS->dispose(object);
305 static void
306 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
308 AdgEntity *entity;
309 AdgEntityPrivate *data;
311 entity = (AdgEntity *) object;
312 data = entity->data;
314 switch (prop_id) {
315 case PROP_PARENT:
316 g_value_set_object(value, data->parent);
317 break;
318 case PROP_GLOBAL_MAP:
319 g_value_set_boxed(value, &data->global_map);
320 break;
321 case PROP_LOCAL_MAP:
322 g_value_set_boxed(value, &data->local_map);
323 break;
324 case PROP_NORMALIZED:
325 g_value_set_boolean(value, data->normalized);
326 break;
327 default:
328 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
329 break;
333 static void
334 set_property(GObject *object,
335 guint prop_id, const GValue *value, GParamSpec *pspec)
337 AdgEntity *entity;
338 AdgEntityPrivate *data;
340 entity = (AdgEntity *) object;
341 data = entity->data;
343 switch (prop_id) {
344 case PROP_PARENT:
345 set_parent(entity, g_value_get_object(value));
346 break;
347 case PROP_GLOBAL_MAP:
348 set_global_map(entity, g_value_get_boxed(value));
349 break;
350 case PROP_LOCAL_MAP:
351 set_local_map(entity, g_value_get_boxed(value));
352 break;
353 case PROP_NORMALIZED:
354 data->normalized = g_value_get_boolean(value);
355 break;
356 default:
357 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
358 break;
364 * adg_entity_get_parent:
365 * @entity: an #AdgEntity
367 * Gets the parent of @entity.
369 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
371 AdgEntity *
372 adg_entity_get_parent(AdgEntity *entity)
374 AdgEntityPrivate *data;
376 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
378 data = entity->data;
380 return data->parent;
384 * adg_entity_set_parent:
385 * @entity: an #AdgEntity
386 * @parent: the parent entity
388 * <note><para>
389 * This function is only useful in entity implementations.
390 * </para></note>
392 * Sets a new parent on @entity.
394 void
395 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
397 g_return_if_fail(ADG_IS_ENTITY(entity));
399 if (set_parent(entity, parent))
400 g_object_notify((GObject *) entity, "parent");
404 * adg_entity_get_canvas:
405 * @entity: an #AdgEntity
407 * Walks on the @entity hierarchy and gets the first parent of @entity that is
408 * of #AdgCanvas derived type.
410 * Returns: the requested canvas or %NULL on errors or if there is
411 * no #AdgCanvas in the @entity hierarchy
413 AdgCanvas *
414 adg_entity_get_canvas(AdgEntity *entity)
416 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
418 while (entity) {
419 if (ADG_IS_CANVAS(entity))
420 return (AdgCanvas *) entity;
422 entity = (AdgEntity *) adg_entity_get_parent(entity);
425 return NULL;
429 * adg_entity_get_global_map:
430 * @entity: an #AdgEntity object
431 * @map: where to store the global map
433 * Gets the transformation to be used to compute the global matrix
434 * of @entity and store it in @map.
436 void
437 adg_entity_get_global_map(AdgEntity *entity, AdgMatrix *map)
439 AdgEntityPrivate *data;
441 g_return_if_fail(ADG_IS_ENTITY(entity));
442 g_return_if_fail(map != NULL);
444 data = entity->data;
445 adg_matrix_copy(map, &data->global_map);
449 * adg_entity_set_global_map:
450 * @entity: an #AdgEntity object
451 * @map: the new map
453 * Sets the new global transformation of @entity to @map:
454 * the old map is discarded. If @map is %NULL an identity
455 * matrix is implied.
457 void
458 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
460 g_return_if_fail(ADG_IS_ENTITY(entity));
462 if (set_global_map(entity, map))
463 g_object_notify((GObject *) entity, "global-map");
467 * adg_entity_transform_global_map:
468 * @entity: an #AdgEntity object
469 * @transformation: the transformation to apply
470 * @mode: how @transformation should be applied
472 * Convenient function to change the global map of @entity by
473 * applying @tranformation using the @mode operator. This is
474 * logically equivalent to the following:
476 * |[
477 * AdgMatrix tmp_map;
478 * adg_entity_get_global_map(entity, &tmp_map);
479 * adg_matrix_transform(&tmp_map, transformation, mode);
480 * adg_entity_set_global_map(entity, &tmp_map);
481 * ]|
483 void
484 adg_entity_transform_global_map(AdgEntity *entity,
485 const AdgMatrix *transformation,
486 AdgTransformMode mode)
488 AdgEntityPrivate *data;
489 AdgMatrix map;
491 g_return_if_fail(ADG_IS_ENTITY(entity));
492 g_return_if_fail(transformation != NULL);
494 data = entity->data;
496 adg_matrix_copy(&map, &data->global_map);
497 adg_matrix_transform(&map, transformation, mode);
499 if (set_global_map(entity, &map))
500 g_object_notify((GObject *) entity, "global-map");
504 * adg_entity_get_local_map:
505 * @entity: an #AdgEntity object
506 * @map: where to store the local map
508 * Gets the transformation to be used to compute the local matrix
509 * of @entity and store it in @map.
511 void
512 adg_entity_get_local_map(AdgEntity *entity, AdgMatrix *map)
514 AdgEntityPrivate *data;
516 g_return_if_fail(ADG_IS_ENTITY(entity));
517 g_return_if_fail(map != NULL);
519 data = entity->data;
521 adg_matrix_copy(map, &data->local_map);
525 * adg_entity_set_local_map:
526 * @entity: an #AdgEntity object
527 * @map: the new map
529 * Sets the new local transformation of @entity to @map:
530 * the old map is discarded. If @map is %NULL an identity
531 * matrix is implied.
533 void
534 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
536 g_return_if_fail(ADG_IS_ENTITY(entity));
538 if (set_local_map(entity, map))
539 g_object_notify((GObject *) entity, "local-map");
543 * adg_entity_transform_local_map:
544 * @entity: an #AdgEntity object
545 * @transformation: the transformation to apply
546 * @mode: how @transformation should be applied
548 * Convenient function to change the local map of @entity by
549 * applying @tranformation using the @mode operator. This is
550 * logically equivalent to the following:
552 * |[
553 * AdgMatrix tmp_map;
554 * adg_entity_get_local_map(entity, &tmp_map);
555 * adg_matrix_transform(&tmp_map, transformation, mode);
556 * adg_entity_set_local_map(entity, &tmp_map);
557 * ]|
559 void
560 adg_entity_transform_local_map(AdgEntity *entity,
561 const AdgMatrix *transformation,
562 AdgTransformMode mode)
564 AdgEntityPrivate *data;
565 AdgMatrix map;
567 g_return_if_fail(ADG_IS_ENTITY(entity));
568 g_return_if_fail(transformation != NULL);
570 data = entity->data;
572 adg_matrix_copy(&map, &data->local_map);
573 adg_matrix_transform(&map, transformation, mode);
575 if (set_local_map(entity, &map))
576 g_object_notify((GObject *) entity, "local-map");
580 * adg_entity_get_normalized:
581 * @entity: an #AdgEntity object
583 * Gets the normalized state of @entity. Check out the
584 * adg_entity_set_normalized() documentation to know what a
585 * normalized entity is.
587 * Returns: the normalized state of @entity
589 gboolean
590 adg_entity_get_normalized(AdgEntity *entity)
592 AdgEntityPrivate *data;
594 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
596 data = entity->data;
598 return data->normalized;
602 * adg_entity_set_normalized:
603 * @entity: an #AdgEntity object
604 * @normalized: new normalized state
606 * Sets a new normalized state on @entity. A normalized entity
607 * is immune to the scaling of the local matrix: this means
608 * the local matrix is normalized with adg_matrix_normalize()
609 * before being used.
611 void
612 adg_entity_set_normalized(AdgEntity *entity, gboolean normalized)
614 AdgEntityPrivate *data;
616 g_return_if_fail(ADG_IS_ENTITY(entity));
618 data = entity->data;
620 data->normalized = normalized;
624 * adg_entity_extents:
625 * @entity: an #AdgEntity
627 * Gets the bounding box of @entity. The returned struct is
628 * owned by @entity and should not modified or freed.
630 * This struct specifies the surface portion (in global space
631 * of @entity) occupied by the entity without taking into
632 * account rendering properties such as line thickness or caps.
634 * The #AdgEntity::arrange signal should be emitted before
635 * this call (either explicitely trought adg_entity_arrange()
636 * or implicitely with adg_entity_render()) in order to get
637 * an up to date boundary box.
639 * Returns: the bounding box of @entity or %NULL on errors
641 const CpmlExtents *
642 adg_entity_extents(AdgEntity *entity)
644 AdgEntityPrivate *data;
646 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
648 data = entity->data;
650 return &data->extents;
654 * adg_entity_set_extents:
655 * @entity: an #AdgEntity
656 * @extents: the new extents
658 * <note><para>
659 * This function is only useful in entity implementations.
660 * </para></note>
662 * Sets a new bounding box for @entity. @extents can be %NULL,
663 * in which case the extents are unset.
665 void
666 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
668 AdgEntityPrivate *data;
670 g_return_if_fail(ADG_IS_ENTITY(entity));
672 data = entity->data;
674 if (extents == NULL)
675 data->extents.is_defined = FALSE;
676 else
677 cpml_extents_copy(&data->extents, extents);
681 * adg_entity_style:
682 * @entity: an #AdgEntity
683 * @dress: the dress of the style to get
685 * Gets the style to be used for @entity. @dress specifies which
686 * "family" of style to get.
688 * The following sequence of checks is performed to get the proper
689 * style, stopping at the first succesfull result:
691 * <orderedlist>
692 * <listitem>check if the style is directly overriden by this entity,
693 * as returned by adg_entity_get_style();</listitem>
694 * <listitem>check if @entity has a parent, in which case returns the
695 * adg_entity_style() of the parent;</listitem>
696 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
697 * </orderedlist>
699 * Returns: the requested style or %NULL for transparent dresses or errors
701 AdgStyle *
702 adg_entity_style(AdgEntity *entity, AdgDress dress)
704 AdgStyle *style;
706 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
708 style = adg_entity_get_style(entity, dress);
710 if (style == NULL) {
711 AdgEntityPrivate *data = entity->data;
713 if (data->parent != NULL)
714 style = adg_entity_style(data->parent, dress);
715 else
716 style = adg_dress_get_fallback(dress);
719 return style;
723 * adg_entity_get_style:
724 * @entity: an #AdgEntity
725 * @dress: the dress of the style to get
727 * Gets the overriden @dress style from @entity. This is a kind
728 * of accessor function: to get the style to be used for rendering
729 * purpose, use adg_entity_style() instead.
731 * Returns: the requested style or %NULL if the @dress style
732 * is not overriden
734 AdgStyle *
735 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
737 AdgEntityPrivate *data;
739 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
741 data = entity->data;
743 if (data->hash_styles == NULL)
744 return NULL;
746 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
750 * adg_entity_set_style:
751 * @entity: an #AdgEntity
752 * @dress: a dress style
753 * @style: the new style to use
755 * Overrides the style of @dress for @entity and its children.
756 * If @style is %NULL, any previous override is removed.
758 * The new style must still be compatible with @dress: check out
759 * the adg_dress_style_is_compatible() documentation to know
760 * what a compatible style means.
762 void
763 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
765 AdgEntityPrivate *data;
766 gpointer p_dress;
767 AdgStyle *old_style;
769 g_return_if_fail(ADG_IS_ENTITY(entity));
771 data = entity->data;
773 if (data->hash_styles == NULL && style == NULL)
774 return;
776 if (data->hash_styles == NULL)
777 data->hash_styles = g_hash_table_new_full(NULL, NULL,
778 NULL, g_object_unref);
780 p_dress = GINT_TO_POINTER(dress);
781 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
783 if (style == old_style)
784 return;
786 if (style == NULL) {
787 g_hash_table_remove(data->hash_styles, p_dress);
788 return;
791 if (!adg_dress_style_is_compatible(dress, style)) {
792 GType ancestor_type = adg_dress_get_ancestor_type(dress);
794 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
795 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
796 g_type_name(ancestor_type), adg_dress_name(dress));
798 return;
801 g_object_ref(style);
802 g_hash_table_replace(data->hash_styles, p_dress, style);
806 * adg_entity_apply_dress:
807 * @entity: an #AdgEntity
808 * @dress: the dress style to apply
809 * @cr: a #cairo_t drawing context
811 * Convenient function to apply a @dress style (as returned by
812 * adg_entity_style()) to the @cr cairo context.
814 void
815 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
817 AdgStyle *style;
819 g_return_if_fail(ADG_IS_ENTITY(entity));
820 g_return_if_fail(cr != NULL);
822 style = adg_entity_style(entity, dress);
824 if (style != NULL)
825 adg_style_apply(style, entity, cr);
829 * adg_entity_global_changed:
830 * @entity: an #AdgEntity
832 * Emits the #AdgEntity::global-changed signal on @entity and on all of
833 * its children, if any.
835 void
836 adg_entity_global_changed(AdgEntity *entity)
838 g_return_if_fail(ADG_IS_ENTITY(entity));
840 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
844 * adg_entity_local_changed:
845 * @entity: an #AdgEntity
847 * Emits the #AdgEntity::local-changed signal on @entity and on all of
848 * its children, if any.
850 void
851 adg_entity_local_changed(AdgEntity *entity)
853 g_return_if_fail(ADG_IS_ENTITY(entity));
855 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
859 * adg_entity_global_matrix:
860 * @entity: an #AdgEntity object
862 * Gets the global matrix by combining all the global maps of the
863 * @entity hierarchy. The returned value is owned by @entity and
864 * should not be changed or freed.
866 * Returns: the global matrix or %NULL on errors
868 const AdgMatrix *
869 adg_entity_global_matrix(AdgEntity *entity)
871 AdgEntityPrivate *data;
873 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
875 data = entity->data;
877 return &data->global_matrix;
881 * adg_entity_local_matrix:
882 * @entity: an #AdgEntity object
883 * @matrix: where to store the local matrix
885 * Gets the local matrix by combining all the local maps of the
886 * @entity hierarchy. The returned value is owned by @entity and
887 * should not be changed or freed.
889 * Returns: the local matrix or %NULL on errors
891 const AdgMatrix *
892 adg_entity_local_matrix(AdgEntity *entity)
894 AdgEntityPrivate *data;
896 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
898 data = entity->data;
900 return &data->local_matrix;
904 * adg_entity_ctm:
905 * @entity: an #AdgEntity object
907 * Gets the current transformation matrix (ctm) of @entity
908 * by transforming the local matrix with the global matrix.
910 * This method is only useful after the #AdgEntity::arrange default
911 * handler has been called, that is at the rendering stage or while
912 * arranging the entity after the parent method has been chained up.
914 * Returns: the current transformation matrix or %NULL on errors
916 const AdgMatrix *
917 adg_entity_ctm(AdgEntity *entity)
919 AdgEntityPrivate *data;
921 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
923 data = entity->data;
925 return &data->ctm;
929 * adg_entity_invalidate:
930 * @entity: an #AdgEntity
932 * Emits the #AdgEntity::invalidate signal on @entity and on all of
933 * its children, if any, clearing the eventual cache stored by the
934 * #AdgEntity::arrange signal and setting the entity state similary
935 * to the just initialized entity.
937 void
938 adg_entity_invalidate(AdgEntity *entity)
940 g_return_if_fail(ADG_IS_ENTITY(entity));
942 g_signal_emit(entity, signals[INVALIDATE], 0);
946 * adg_entity_arrange:
947 * @entity: an #AdgEntity
949 * <note><para>
950 * This function is only useful in entity implementations.
951 * </para></note>
953 * Emits the #AdgEntity::arrange signal on @entity and all its children,
954 * if any. This function is rarely needed as the arrange call is usually
955 * managed by the #AdgEntity::render signal or indirectly by a call to
956 * adg_entity_get_extents().
958 void
959 adg_entity_arrange(AdgEntity *entity)
961 g_return_if_fail(ADG_IS_ENTITY(entity));
963 g_signal_emit(entity, signals[ARRANGE], 0);
967 * adg_entity_render:
968 * @entity: an #AdgEntity
969 * @cr: a #cairo_t drawing context
971 * Emits the #AdgEntity::render signal on @entity and on all of its
972 * children, if any, causing the rendering to the @cr cairo context.
974 void
975 adg_entity_render(AdgEntity *entity, cairo_t *cr)
977 g_return_if_fail(ADG_IS_ENTITY(entity));
979 g_signal_emit(entity, signals[RENDER], 0, cr);
983 static gboolean
984 set_parent(AdgEntity *entity, AdgEntity *parent)
986 AdgEntityPrivate *data;
987 AdgEntity *old_parent;
989 data = entity->data;
990 old_parent = data->parent;
992 /* Check if parent has changed */
993 if (parent == old_parent)
994 return FALSE;
996 if (parent != NULL)
997 g_object_ref(parent);
999 data->parent = parent;
1001 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
1002 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
1003 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
1005 if (old_parent != NULL)
1006 g_object_unref(old_parent);
1008 return TRUE;
1011 static void
1012 global_changed(AdgEntity *entity)
1014 AdgEntityPrivate *data;
1015 AdgMatrix *map, *matrix;
1017 data = entity->data;
1018 map = &data->global_map;
1019 matrix = &data->global_matrix;
1021 if (data->parent == NULL) {
1022 adg_matrix_copy(matrix, map);
1023 } else {
1024 adg_matrix_copy(matrix, adg_entity_global_matrix(data->parent));
1025 adg_matrix_transform(matrix, map, ADG_TRANSFORM_AFTER);
1029 static void
1030 local_changed(AdgEntity *entity)
1032 AdgEntityPrivate *data;
1033 AdgMatrix *map, *matrix;
1035 data = entity->data;
1036 map = &data->local_map;
1037 matrix = &data->local_matrix;
1039 if (data->parent == NULL) {
1040 adg_matrix_copy(matrix, map);
1041 } else {
1042 adg_matrix_copy(matrix, adg_entity_local_matrix(data->parent));
1043 adg_matrix_transform(matrix, map, ADG_TRANSFORM_AFTER);
1046 if (data->normalized)
1047 adg_matrix_normalize(matrix);
1050 static gboolean
1051 set_global_map(AdgEntity *entity, const AdgMatrix *map)
1053 AdgEntityPrivate *data = entity->data;
1055 if (map != NULL && adg_matrix_equal(&data->global_map, map))
1056 return FALSE;
1058 if (map == NULL)
1059 cairo_matrix_init_identity(&data->global_map);
1060 else
1061 adg_matrix_copy(&data->global_map, map);
1063 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
1064 return TRUE;
1067 static gboolean
1068 set_local_map(AdgEntity *entity, const AdgMatrix *map)
1070 AdgEntityPrivate *data = entity->data;
1072 if (map != NULL && adg_matrix_equal(&data->local_map, map))
1073 return FALSE;
1075 if (map == NULL)
1076 cairo_matrix_init_identity(&data->local_map);
1077 else
1078 adg_matrix_copy(&data->local_map, map);
1080 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
1081 return TRUE;
1084 static void
1085 real_invalidate(AdgEntity *entity)
1087 AdgEntityPrivate *data = entity->data;
1088 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1090 if (klass->invalidate == NULL) {
1091 /* Do not raise any warning if invalidate() is not defined,
1092 * assuming entity does not have cache to be cleared */
1093 } else {
1094 klass->invalidate(entity);
1097 data->extents.is_defined = FALSE;
1100 static void
1101 real_arrange(AdgEntity *entity)
1103 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1105 if (klass->arrange == NULL) {
1106 /* The arrange() method must be defined */
1107 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1108 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1109 } else {
1110 AdgEntityPrivate *data;
1111 AdgMatrix *ctm;
1113 data = entity->data;
1114 ctm = &data->ctm;
1116 /* Update the ctm (current transformation matrix) */
1117 adg_matrix_copy(ctm, &data->global_matrix);
1118 adg_matrix_transform(ctm, &data->local_matrix, ADG_TRANSFORM_BEFORE);
1120 klass->arrange(entity);
1124 static void
1125 real_render(AdgEntity *entity, cairo_t *cr)
1127 AdgEntityPrivate *data = entity->data;
1128 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1130 if (klass->render == NULL) {
1131 /* The render method must be defined */
1132 g_warning(_("%s: `render' method not implemented for type `%s'"),
1133 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1134 } else {
1135 /* Before the rendering, the entity should be arranged */
1136 g_signal_emit(entity, signals[ARRANGE], 0);
1138 cairo_save(cr);
1139 cairo_set_matrix(cr, &data->global_matrix);
1140 klass->render(entity, cr);
1141 cairo_restore(cr);