[AdgWidget] Code cleanup
[adg.git] / adg / adg-entity.c
blob3af4e44402e026d7007dbdc5c4dcadf3770ce342
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_LOCAL_METHOD
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 gboolean set_local_method (AdgEntity *entity,
109 AdgMixMethod local_method);
110 static void real_invalidate (AdgEntity *entity);
111 static void real_arrange (AdgEntity *entity);
112 static void real_render (AdgEntity *entity,
113 cairo_t *cr);
115 static guint signals[LAST_SIGNAL] = { 0 };
118 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
121 static void
122 adg_entity_class_init(AdgEntityClass *klass)
124 GObjectClass *gobject_class;
125 GParamSpec *param;
126 GClosure *closure;
127 GType param_types[1];
129 gobject_class = (GObjectClass *) klass;
131 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
133 gobject_class->dispose = dispose;
134 gobject_class->get_property = get_property;
135 gobject_class->set_property = set_property;
137 klass->parent_set = NULL;
138 klass->global_changed = global_changed;
139 klass->local_changed = local_changed;
140 klass->invalidate = NULL;
141 klass->arrange= NULL;
142 klass->render = NULL;
144 param = g_param_spec_object("parent",
145 P_("Parent Entity"),
146 P_("The parent entity of this entity or NULL if this is a top-level entity"),
147 ADG_TYPE_ENTITY,
148 G_PARAM_READWRITE);
149 g_object_class_install_property(gobject_class, PROP_PARENT, param);
151 param = g_param_spec_boxed("global-map",
152 P_("Global Map"),
153 P_("The transformation to be combined with the parent ones to get the global matrix"),
154 ADG_TYPE_MATRIX,
155 G_PARAM_READWRITE);
156 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
158 param = g_param_spec_boxed("local-map",
159 P_("Local Map"),
160 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
161 ADG_TYPE_MATRIX,
162 G_PARAM_READWRITE);
163 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
165 param = g_param_spec_enum("local-method",
166 P_("Local Mix Method"),
167 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
168 ADG_TYPE_MIX_METHOD, ADG_MIX_ANCESTORS,
169 G_PARAM_READWRITE);
170 g_object_class_install_property(gobject_class, PROP_LOCAL_METHOD, param);
173 * AdgEntity::parent-set:
174 * @entity: an #AdgEntity
175 * @old_parent: the old parent
177 * Emitted after the parent entity has changed. The new parent
178 * can be inspected using adg_entity_get_parent().
180 * It is allowed for both old and new parent to be %NULL.
182 signals[PARENT_SET] = g_signal_new("parent-set",
183 G_OBJECT_CLASS_TYPE(gobject_class),
184 G_SIGNAL_RUN_FIRST,
185 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
186 NULL, NULL,
187 adg_marshal_VOID__OBJECT,
188 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
191 * AdgEntity::global-changed
192 * @entity: an #AdgEntity
194 * Emitted when the global map of @entity or any of its parent
195 * has changed. The default handler will compute the new global
196 * matrix, updating the internal cache.
198 signals[GLOBAL_CHANGED] = g_signal_new("global-changed",
199 G_OBJECT_CLASS_TYPE(gobject_class),
200 G_SIGNAL_RUN_FIRST,
201 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
202 NULL, NULL,
203 adg_marshal_VOID__VOID,
204 G_TYPE_NONE, 0);
207 * AdgEntity::local-changed
208 * @entity: an #AdgEntity
210 * Emitted when the local map of @entity or any of its parent
211 * has changed. The default handler will compute the new local
212 * matrix, updating the internal cache.
214 signals[LOCAL_CHANGED] = g_signal_new("local-changed",
215 G_OBJECT_CLASS_TYPE(gobject_class),
216 G_SIGNAL_RUN_FIRST,
217 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
218 NULL, NULL,
219 adg_marshal_VOID__VOID,
220 G_TYPE_NONE, 0);
223 * AdgEntity::invalidate:
224 * @entity: an #AdgEntity
226 * Invalidates the whole @entity, that is resets all the cache
227 * (if present) built during the #AdgEntity::arrange signal.
228 * The resulting state is a clean entity, similar to what you
229 * have just before the first rendering.
231 closure = g_cclosure_new(G_CALLBACK(real_invalidate), NULL, NULL);
232 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
233 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
234 adg_marshal_VOID__VOID,
235 G_TYPE_NONE, 0, param_types);
238 * AdgEntity::arrange:
239 * @entity: an #AdgEntity
241 * Arranges the layout of @entity, updating the cache if necessary,
242 * and computes the extents of @entity.
244 closure = g_cclosure_new(G_CALLBACK(real_arrange), NULL, NULL);
245 signals[ARRANGE] = g_signal_newv("arrange", ADG_TYPE_ENTITY,
246 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
247 adg_marshal_VOID__VOID,
248 G_TYPE_NONE, 0, param_types);
250 * AdgEntity::render:
251 * @entity: an #AdgEntity
252 * @cr: a #cairo_t drawing context
254 * Causes the rendering of @entity on @cr. A render signal will
255 * automatically emit #AdgEntity::arrange just before the real
256 * rendering on the cairo context.
258 closure = g_cclosure_new(G_CALLBACK(real_render), NULL, NULL);
259 param_types[0] = G_TYPE_POINTER;
260 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
261 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
262 adg_marshal_VOID__POINTER,
263 G_TYPE_NONE, 1, param_types);
266 static void
267 adg_entity_init(AdgEntity *entity)
269 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
270 ADG_TYPE_ENTITY,
271 AdgEntityPrivate);
272 data->parent = NULL;
273 cairo_matrix_init_identity(&data->global_map);
274 cairo_matrix_init_identity(&data->local_map);
275 data->local_method = ADG_MIX_ANCESTORS;
276 data->hash_styles = NULL;
277 cairo_matrix_init_identity(&data->global_matrix);
278 cairo_matrix_init_identity(&data->local_matrix);
279 data->extents.is_defined = FALSE;
281 entity->data = data;
284 static void
285 dispose(GObject *object)
287 AdgEntity *entity;
288 AdgEntityPrivate *data;
290 entity = (AdgEntity *) object;
291 data = entity->data;
293 /* This call will emit a "notify" signal for parent.
294 * Consequentially, the references to the old parent is dropped. */
295 adg_entity_set_parent(entity, NULL);
297 if (data->hash_styles != NULL) {
298 g_hash_table_destroy(data->hash_styles);
299 data->hash_styles = NULL;
302 if (PARENT_OBJECT_CLASS->dispose != NULL)
303 PARENT_OBJECT_CLASS->dispose(object);
307 static void
308 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
310 AdgEntity *entity;
311 AdgEntityPrivate *data;
313 entity = (AdgEntity *) object;
314 data = entity->data;
316 switch (prop_id) {
317 case PROP_PARENT:
318 g_value_set_object(value, data->parent);
319 break;
320 case PROP_GLOBAL_MAP:
321 g_value_set_boxed(value, &data->global_map);
322 break;
323 case PROP_LOCAL_MAP:
324 g_value_set_boxed(value, &data->local_map);
325 break;
326 case PROP_LOCAL_METHOD:
327 g_value_set_enum(value, data->local_method);
328 break;
329 default:
330 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
331 break;
335 static void
336 set_property(GObject *object,
337 guint prop_id, const GValue *value, GParamSpec *pspec)
339 AdgEntity *entity;
340 AdgEntityPrivate *data;
342 entity = (AdgEntity *) object;
343 data = entity->data;
345 switch (prop_id) {
346 case PROP_PARENT:
347 set_parent(entity, g_value_get_object(value));
348 break;
349 case PROP_GLOBAL_MAP:
350 set_global_map(entity, g_value_get_boxed(value));
351 break;
352 case PROP_LOCAL_MAP:
353 set_local_map(entity, g_value_get_boxed(value));
354 break;
355 case PROP_LOCAL_METHOD:
356 set_local_method(entity, g_value_get_enum(value));
357 break;
358 default:
359 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
360 break;
366 * adg_entity_get_parent:
367 * @entity: an #AdgEntity
369 * Gets the parent of @entity.
371 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
373 AdgEntity *
374 adg_entity_get_parent(AdgEntity *entity)
376 AdgEntityPrivate *data;
378 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
380 data = entity->data;
382 return data->parent;
386 * adg_entity_set_parent:
387 * @entity: an #AdgEntity
388 * @parent: the parent entity
390 * <note><para>
391 * This function is only useful in entity implementations.
392 * </para></note>
394 * Sets a new parent on @entity.
396 void
397 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
399 g_return_if_fail(ADG_IS_ENTITY(entity));
401 if (set_parent(entity, parent))
402 g_object_notify((GObject *) entity, "parent");
406 * adg_entity_get_canvas:
407 * @entity: an #AdgEntity
409 * Walks on the @entity hierarchy and gets the first parent of @entity that is
410 * of #AdgCanvas derived type.
412 * Returns: the requested canvas or %NULL on errors or if there is
413 * no #AdgCanvas in the @entity hierarchy
415 AdgCanvas *
416 adg_entity_get_canvas(AdgEntity *entity)
418 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
420 while (entity) {
421 if (ADG_IS_CANVAS(entity))
422 return (AdgCanvas *) entity;
424 entity = (AdgEntity *) adg_entity_get_parent(entity);
427 return NULL;
431 * adg_entity_get_global_map:
432 * @entity: an #AdgEntity object
433 * @map: where to store the global map
435 * Gets the transformation to be used to compute the global matrix
436 * of @entity and store it in @map.
438 void
439 adg_entity_get_global_map(AdgEntity *entity, AdgMatrix *map)
441 AdgEntityPrivate *data;
443 g_return_if_fail(ADG_IS_ENTITY(entity));
444 g_return_if_fail(map != NULL);
446 data = entity->data;
447 adg_matrix_copy(map, &data->global_map);
451 * adg_entity_set_global_map:
452 * @entity: an #AdgEntity object
453 * @map: the new map
455 * Sets the new global transformation of @entity to @map:
456 * the old map is discarded. If @map is %NULL an identity
457 * matrix is implied.
459 void
460 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
462 g_return_if_fail(ADG_IS_ENTITY(entity));
464 if (set_global_map(entity, map))
465 g_object_notify((GObject *) entity, "global-map");
469 * adg_entity_transform_global_map:
470 * @entity: an #AdgEntity object
471 * @transformation: the transformation to apply
472 * @mode: how @transformation should be applied
474 * Convenient function to change the global map of @entity by
475 * applying @tranformation using the @mode operator. This is
476 * logically equivalent to the following:
478 * |[
479 * AdgMatrix tmp_map;
480 * adg_entity_get_global_map(entity, &tmp_map);
481 * adg_matrix_transform(&tmp_map, transformation, mode);
482 * adg_entity_set_global_map(entity, &tmp_map);
483 * ]|
485 void
486 adg_entity_transform_global_map(AdgEntity *entity,
487 const AdgMatrix *transformation,
488 AdgTransformMode mode)
490 AdgEntityPrivate *data;
491 AdgMatrix map;
493 g_return_if_fail(ADG_IS_ENTITY(entity));
494 g_return_if_fail(transformation != NULL);
496 data = entity->data;
498 adg_matrix_copy(&map, &data->global_map);
499 adg_matrix_transform(&map, transformation, mode);
501 if (set_global_map(entity, &map))
502 g_object_notify((GObject *) entity, "global-map");
506 * adg_entity_get_local_map:
507 * @entity: an #AdgEntity object
508 * @map: where to store the local map
510 * Gets the transformation to be used to compute the local matrix
511 * of @entity and store it in @map.
513 void
514 adg_entity_get_local_map(AdgEntity *entity, AdgMatrix *map)
516 AdgEntityPrivate *data;
518 g_return_if_fail(ADG_IS_ENTITY(entity));
519 g_return_if_fail(map != NULL);
521 data = entity->data;
523 adg_matrix_copy(map, &data->local_map);
527 * adg_entity_set_local_map:
528 * @entity: an #AdgEntity object
529 * @map: the new map
531 * Sets the new local transformation of @entity to @map:
532 * the old map is discarded. If @map is %NULL an identity
533 * matrix is implied.
535 void
536 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
538 g_return_if_fail(ADG_IS_ENTITY(entity));
540 if (set_local_map(entity, map))
541 g_object_notify((GObject *) entity, "local-map");
545 * adg_entity_transform_local_map:
546 * @entity: an #AdgEntity object
547 * @transformation: the transformation to apply
548 * @mode: how @transformation should be applied
550 * Convenient function to change the local map of @entity by
551 * applying @tranformation using the @mode operator. This is
552 * logically equivalent to the following:
554 * |[
555 * AdgMatrix tmp_map;
556 * adg_entity_get_local_map(entity, &tmp_map);
557 * adg_matrix_transform(&tmp_map, transformation, mode);
558 * adg_entity_set_local_map(entity, &tmp_map);
559 * ]|
561 void
562 adg_entity_transform_local_map(AdgEntity *entity,
563 const AdgMatrix *transformation,
564 AdgTransformMode mode)
566 AdgEntityPrivate *data;
567 AdgMatrix map;
569 g_return_if_fail(ADG_IS_ENTITY(entity));
570 g_return_if_fail(transformation != NULL);
572 data = entity->data;
574 adg_matrix_copy(&map, &data->local_map);
575 adg_matrix_transform(&map, transformation, mode);
577 if (set_local_map(entity, &map))
578 g_object_notify((GObject *) entity, "local-map");
582 * adg_entity_get_local_method:
583 * @entity: an #AdgEntity object
585 * Gets the local mix method of @entity. Check out the
586 * adg_entity_set_local_method() documentation to know what the
587 * local method is used for.
589 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
591 AdgMixMethod
592 adg_entity_get_local_method(AdgEntity *entity)
594 AdgEntityPrivate *data;
596 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
598 data = entity->data;
600 return data->local_method;
604 * adg_entity_set_local_method:
605 * @entity: an #AdgEntity object
606 * @local_method: new method
608 * Sets a new local mix method on @entity. The
609 * #AdgEntity:local-method property defines how the local
610 * matrix must be computed: check out the #AdgMixMethod
611 * documentation to know what are the availables methods
612 * and how they affect the local matrix computation.
614 * Setting a different local method emits an #Adgentity::local-changed
615 * signal on @entity.
617 void
618 adg_entity_set_local_method(AdgEntity *entity, AdgMixMethod local_method)
620 g_return_if_fail(ADG_IS_ENTITY(entity));
622 if (set_local_method(entity, local_method))
623 g_object_notify((GObject *) entity, "local-method");
627 * adg_entity_extents:
628 * @entity: an #AdgEntity
630 * Gets the bounding box of @entity. The returned struct is
631 * owned by @entity and should not modified or freed.
633 * This struct specifies the surface portion (in global space
634 * of @entity) occupied by the entity without taking into
635 * account rendering properties such as line thickness or caps.
637 * The #AdgEntity::arrange signal should be emitted before
638 * this call (either explicitely trought adg_entity_arrange()
639 * or implicitely with adg_entity_render()) in order to get
640 * an up to date boundary box.
642 * Returns: the bounding box of @entity or %NULL on errors
644 const CpmlExtents *
645 adg_entity_extents(AdgEntity *entity)
647 AdgEntityPrivate *data;
649 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
651 data = entity->data;
653 return &data->extents;
657 * adg_entity_set_extents:
658 * @entity: an #AdgEntity
659 * @extents: the new extents
661 * <note><para>
662 * This function is only useful in entity implementations.
663 * </para></note>
665 * Sets a new bounding box for @entity. @extents can be %NULL,
666 * in which case the extents are unset.
668 void
669 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
671 AdgEntityPrivate *data;
673 g_return_if_fail(ADG_IS_ENTITY(entity));
675 data = entity->data;
677 if (extents == NULL)
678 data->extents.is_defined = FALSE;
679 else
680 cpml_extents_copy(&data->extents, extents);
684 * adg_entity_style:
685 * @entity: an #AdgEntity
686 * @dress: the dress of the style to get
688 * Gets the style to be used for @entity. @dress specifies which
689 * "family" of style to get.
691 * The following sequence of checks is performed to get the proper
692 * style, stopping at the first succesfull result:
694 * <orderedlist>
695 * <listitem>check if the style is directly overriden by this entity,
696 * as returned by adg_entity_get_style();</listitem>
697 * <listitem>check if @entity has a parent, in which case returns the
698 * adg_entity_style() of the parent;</listitem>
699 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
700 * </orderedlist>
702 * Returns: the requested style or %NULL for transparent dresses or errors
704 AdgStyle *
705 adg_entity_style(AdgEntity *entity, AdgDress dress)
707 AdgStyle *style;
709 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
711 style = adg_entity_get_style(entity, dress);
713 if (style == NULL) {
714 AdgEntityPrivate *data = entity->data;
716 if (data->parent != NULL)
717 style = adg_entity_style(data->parent, dress);
718 else
719 style = adg_dress_get_fallback(dress);
722 return style;
726 * adg_entity_get_style:
727 * @entity: an #AdgEntity
728 * @dress: the dress of the style to get
730 * Gets the overriden @dress style from @entity. This is a kind
731 * of accessor function: to get the style to be used for rendering
732 * purpose, use adg_entity_style() instead.
734 * Returns: the requested style or %NULL if the @dress style
735 * is not overriden
737 AdgStyle *
738 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
740 AdgEntityPrivate *data;
742 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
744 data = entity->data;
746 if (data->hash_styles == NULL)
747 return NULL;
749 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
753 * adg_entity_set_style:
754 * @entity: an #AdgEntity
755 * @dress: a dress style
756 * @style: the new style to use
758 * Overrides the style of @dress for @entity and its children.
759 * If @style is %NULL, any previous override is removed.
761 * The new style must still be compatible with @dress: check out
762 * the adg_dress_style_is_compatible() documentation to know
763 * what a compatible style means.
765 void
766 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
768 AdgEntityPrivate *data;
769 gpointer p_dress;
770 AdgStyle *old_style;
772 g_return_if_fail(ADG_IS_ENTITY(entity));
774 data = entity->data;
776 if (data->hash_styles == NULL && style == NULL)
777 return;
779 if (data->hash_styles == NULL)
780 data->hash_styles = g_hash_table_new_full(NULL, NULL,
781 NULL, g_object_unref);
783 p_dress = GINT_TO_POINTER(dress);
784 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
786 if (style == old_style)
787 return;
789 if (style == NULL) {
790 g_hash_table_remove(data->hash_styles, p_dress);
791 return;
794 if (!adg_dress_style_is_compatible(dress, style)) {
795 GType ancestor_type = adg_dress_get_ancestor_type(dress);
797 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
798 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
799 g_type_name(ancestor_type), adg_dress_name(dress));
801 return;
804 g_object_ref(style);
805 g_hash_table_replace(data->hash_styles, p_dress, style);
809 * adg_entity_apply_dress:
810 * @entity: an #AdgEntity
811 * @dress: the dress style to apply
812 * @cr: a #cairo_t drawing context
814 * Convenient function to apply a @dress style (as returned by
815 * adg_entity_style()) to the @cr cairo context.
817 void
818 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
820 AdgStyle *style;
822 g_return_if_fail(ADG_IS_ENTITY(entity));
823 g_return_if_fail(cr != NULL);
825 style = adg_entity_style(entity, dress);
827 if (style != NULL)
828 adg_style_apply(style, entity, cr);
832 * adg_entity_global_changed:
833 * @entity: an #AdgEntity
835 * Emits the #AdgEntity::global-changed signal on @entity and on all of
836 * its children, if any.
838 void
839 adg_entity_global_changed(AdgEntity *entity)
841 g_return_if_fail(ADG_IS_ENTITY(entity));
843 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
847 * adg_entity_local_changed:
848 * @entity: an #AdgEntity
850 * Emits the #AdgEntity::local-changed signal on @entity and on all of
851 * its children, if any.
853 void
854 adg_entity_local_changed(AdgEntity *entity)
856 g_return_if_fail(ADG_IS_ENTITY(entity));
858 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
862 * adg_entity_global_matrix:
863 * @entity: an #AdgEntity object
865 * Gets the global matrix by combining all the global maps of the
866 * @entity hierarchy using the %ADG_MIX_ANCESTORS method. The
867 * returned value is owned by @entity and should not be changed
868 * or freed.
870 * Returns: the global matrix or %NULL on errors
872 const AdgMatrix *
873 adg_entity_global_matrix(AdgEntity *entity)
875 AdgEntityPrivate *data;
877 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
879 data = entity->data;
881 return &data->global_matrix;
885 * adg_entity_local_matrix:
886 * @entity: an #AdgEntity object
887 * @matrix: where to store the local matrix
889 * Gets the local matrix of @entity in the way specified by the
890 * #AdgEntity:local-method property. The returned value is owned
891 * by @entity and should not be changed or freed.
893 * Returns: the local matrix or %NULL on errors
895 const AdgMatrix *
896 adg_entity_local_matrix(AdgEntity *entity)
898 AdgEntityPrivate *data;
900 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
902 data = entity->data;
904 return &data->local_matrix;
908 * adg_entity_ctm:
909 * @entity: an #AdgEntity object
911 * Returns the current transformation matrix (ctm) of @entity
912 * by applying the global matrix above the local matrix. This
913 * is roughly equivalent to the following pseudo code:
915 * |[
916 * cairo_matrix_multiply(ctm, local_matrix, global_matrix);
917 * ]|
919 * This method is only useful inside or after the #AdgEntity::arrange
920 * default signal handler has been called, that is at the arranging
921 * or rendering stage. In the other cases, the returned matrix will
922 * luckely be not up to date.
924 * Returns: the current transformation matrix or %NULL on errors
926 const AdgMatrix *
927 adg_entity_ctm(AdgEntity *entity)
929 AdgEntityPrivate *data;
931 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
933 data = entity->data;
935 return &data->ctm;
939 * adg_entity_invalidate:
940 * @entity: an #AdgEntity
942 * Emits the #AdgEntity::invalidate signal on @entity and on all of
943 * its children, if any, clearing the eventual cache stored by the
944 * #AdgEntity::arrange signal and setting the entity state similary
945 * to the just initialized entity.
947 void
948 adg_entity_invalidate(AdgEntity *entity)
950 g_return_if_fail(ADG_IS_ENTITY(entity));
952 g_signal_emit(entity, signals[INVALIDATE], 0);
956 * adg_entity_arrange:
957 * @entity: an #AdgEntity
959 * Emits the #AdgEntity::arrange signal on @entity and all its children,
960 * if any. This function is rarely needed as the arrange call is usually
961 * implicitely called by the #AdgEntity::render signal or iby a call to
962 * adg_entity_get_extents().
964 void
965 adg_entity_arrange(AdgEntity *entity)
967 g_return_if_fail(ADG_IS_ENTITY(entity));
969 g_signal_emit(entity, signals[ARRANGE], 0);
973 * adg_entity_render:
974 * @entity: an #AdgEntity
975 * @cr: a #cairo_t drawing context
977 * Emits the #AdgEntity::render signal on @entity and on all of its
978 * children, if any, causing the rendering to the @cr cairo context.
980 void
981 adg_entity_render(AdgEntity *entity, cairo_t *cr)
983 g_return_if_fail(ADG_IS_ENTITY(entity));
985 g_signal_emit(entity, signals[RENDER], 0, cr);
989 static gboolean
990 set_parent(AdgEntity *entity, AdgEntity *parent)
992 AdgEntityPrivate *data;
993 AdgEntity *old_parent;
995 data = entity->data;
996 old_parent = data->parent;
998 /* Check if parent has changed */
999 if (parent == old_parent)
1000 return FALSE;
1002 if (parent != NULL)
1003 g_object_ref(parent);
1005 data->parent = parent;
1007 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
1008 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
1009 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
1011 if (old_parent != NULL)
1012 g_object_unref(old_parent);
1014 return TRUE;
1017 static void
1018 global_changed(AdgEntity *entity)
1020 AdgEntityPrivate *data;
1021 AdgMatrix *map, *matrix;
1023 data = entity->data;
1024 map = &data->global_map;
1025 matrix = &data->global_matrix;
1027 if (data->parent == NULL) {
1028 adg_matrix_copy(matrix, map);
1029 } else {
1030 adg_matrix_copy(matrix, adg_entity_global_matrix(data->parent));
1031 adg_matrix_transform(matrix, map, ADG_TRANSFORM_AFTER);
1035 static void
1036 local_changed(AdgEntity *entity)
1038 AdgEntityPrivate *data;
1039 AdgMatrix *matrix;
1041 data = entity->data;
1042 matrix = &data->local_matrix;
1044 switch (data->local_method) {
1045 case ADG_MIX_DISABLED:
1046 adg_matrix_copy(matrix, adg_matrix_identity());
1047 break;
1048 case ADG_MIX_NONE:
1049 adg_matrix_copy(matrix, &data->local_map);
1050 break;
1051 case ADG_MIX_ANCESTORS:
1052 if (data->parent != NULL) {
1053 adg_matrix_copy(matrix, adg_entity_local_matrix(data->parent));
1054 adg_matrix_transform(matrix, &data->local_map, ADG_TRANSFORM_AFTER);
1055 } else {
1056 adg_matrix_copy(matrix, &data->local_map);
1058 break;
1059 case ADG_MIX_ANCESTORS_NORMALIZED:
1060 if (data->parent != NULL) {
1061 adg_matrix_copy(matrix, adg_entity_local_matrix(data->parent));
1062 adg_matrix_transform(matrix, &data->local_map, ADG_TRANSFORM_AFTER);
1063 } else {
1064 adg_matrix_copy(matrix, &data->local_map);
1066 adg_matrix_normalize(matrix);
1067 break;
1068 case ADG_MIX_PARENT:
1069 if (data->parent != NULL) {
1070 adg_entity_get_local_map(data->parent, matrix);
1071 adg_matrix_transform(matrix, &data->local_map, ADG_TRANSFORM_AFTER);
1072 } else {
1073 adg_matrix_copy(matrix, &data->local_map);
1075 break;
1076 case ADG_MIX_PARENT_NORMALIZED:
1077 if (data->parent != NULL) {
1078 adg_entity_get_local_map(data->parent, matrix);
1079 adg_matrix_transform(matrix, &data->local_map, ADG_TRANSFORM_AFTER);
1080 } else {
1081 adg_matrix_copy(matrix, &data->local_map);
1083 adg_matrix_normalize(matrix);
1084 break;
1085 case ADG_MIX_UNDEFINED:
1086 g_warning(_("%s: requested to mix the maps using an undefined method"),
1087 G_STRLOC);
1088 break;
1089 default:
1090 g_assert_not_reached();
1091 break;
1095 static gboolean
1096 set_global_map(AdgEntity *entity, const AdgMatrix *map)
1098 AdgEntityPrivate *data = entity->data;
1100 if (map != NULL && adg_matrix_equal(&data->global_map, map))
1101 return FALSE;
1103 if (map == NULL)
1104 cairo_matrix_init_identity(&data->global_map);
1105 else
1106 adg_matrix_copy(&data->global_map, map);
1108 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
1109 return TRUE;
1112 static gboolean
1113 set_local_map(AdgEntity *entity, const AdgMatrix *map)
1115 AdgEntityPrivate *data = entity->data;
1117 if (map != NULL && adg_matrix_equal(&data->local_map, map))
1118 return FALSE;
1120 if (map == NULL)
1121 cairo_matrix_init_identity(&data->local_map);
1122 else
1123 adg_matrix_copy(&data->local_map, map);
1125 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
1126 return TRUE;
1129 static gboolean
1130 set_local_method(AdgEntity *entity, AdgMixMethod local_method)
1132 AdgEntityPrivate *data = entity->data;
1134 if (data->local_method == local_method)
1135 return FALSE;
1137 data->local_method = local_method;
1138 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
1140 return TRUE;
1143 static void
1144 real_invalidate(AdgEntity *entity)
1146 AdgEntityPrivate *data = entity->data;
1147 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1149 if (klass->invalidate == NULL) {
1150 /* Do not raise any warning if invalidate() is not defined,
1151 * assuming entity does not have cache to be cleared */
1152 } else {
1153 klass->invalidate(entity);
1156 data->extents.is_defined = FALSE;
1159 static void
1160 real_arrange(AdgEntity *entity)
1162 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1164 if (klass->arrange == NULL) {
1165 /* The arrange() method must be defined */
1166 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1167 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1168 } else {
1169 AdgEntityPrivate *data;
1170 AdgMatrix *ctm;
1172 data = entity->data;
1173 ctm = &data->ctm;
1175 /* Update the ctm (current transformation matrix) */
1176 adg_matrix_copy(ctm, &data->global_matrix);
1177 adg_matrix_transform(ctm, &data->local_matrix, ADG_TRANSFORM_BEFORE);
1179 klass->arrange(entity);
1183 static void
1184 real_render(AdgEntity *entity, cairo_t *cr)
1186 AdgEntityPrivate *data = entity->data;
1187 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1189 if (klass->render == NULL) {
1190 /* The render method must be defined */
1191 g_warning(_("%s: `render' method not implemented for type `%s'"),
1192 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1193 } else {
1194 /* Before the rendering, the entity should be arranged */
1195 g_signal_emit(entity, signals[ARRANGE], 0);
1197 cairo_save(cr);
1198 cairo_set_matrix(cr, &data->global_matrix);
1199 klass->render(entity, cr);
1200 cairo_restore(cr);