[ADG] Avoid to forcibly set the CTM on the cr
[adg.git] / src / adg / adg-entity.c
blob9ccd43fc2d95f73d01eff4d2ddea5c8d9321a73a
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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-internal.h"
62 #include "adg-entity.h"
63 #include "adg-entity-private.h"
64 #include "adg-canvas.h"
65 #include "adg-font-style.h"
66 #include "adg-dim-style.h"
67 #include "adg-marshal.h"
69 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
72 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
74 enum {
75 PROP_0,
76 PROP_PARENT,
77 PROP_GLOBAL_MAP,
78 PROP_LOCAL_MAP,
79 PROP_LOCAL_METHOD
82 enum {
83 PARENT_SET,
84 GLOBAL_CHANGED,
85 LOCAL_CHANGED,
86 INVALIDATE,
87 ARRANGE,
88 RENDER,
89 LAST_SIGNAL
93 static void _adg_dispose (GObject *object);
94 static void _adg_get_property (GObject *object,
95 guint prop_id,
96 GValue *value,
97 GParamSpec *pspec);
98 static void _adg_set_property (GObject *object,
99 guint prop_id,
100 const GValue *value,
101 GParamSpec *pspec);
102 static void _adg_set_parent (AdgEntity *entity,
103 AdgEntity *parent);
104 static void _adg_global_changed (AdgEntity *entity);
105 static void _adg_local_changed (AdgEntity *entity);
106 static void _adg_real_invalidate (AdgEntity *entity);
107 static void _adg_real_arrange (AdgEntity *entity);
108 static void _adg_real_render (AdgEntity *entity,
109 cairo_t *cr);
110 static guint _adg_signals[LAST_SIGNAL] = { 0 };
111 static gboolean _adg_show_extents = FALSE;
114 static void
115 adg_entity_class_init(AdgEntityClass *klass)
117 GObjectClass *gobject_class;
118 GParamSpec *param;
119 GClosure *closure;
120 GType param_types[1];
122 gobject_class = (GObjectClass *) klass;
124 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
126 gobject_class->dispose = _adg_dispose;
127 gobject_class->get_property = _adg_get_property;
128 gobject_class->set_property = _adg_set_property;
130 klass->parent_set = NULL;
131 klass->global_changed = _adg_global_changed;
132 klass->local_changed = _adg_local_changed;
133 klass->invalidate = NULL;
134 klass->arrange= NULL;
135 klass->render = NULL;
137 param = g_param_spec_object("parent",
138 P_("Parent Entity"),
139 P_("The parent entity of this entity or NULL if this is a top-level entity"),
140 ADG_TYPE_ENTITY,
141 G_PARAM_READWRITE);
142 g_object_class_install_property(gobject_class, PROP_PARENT, param);
144 param = g_param_spec_boxed("global-map",
145 P_("Global Map"),
146 P_("The transformation to be combined with the parent ones to get the global matrix"),
147 ADG_TYPE_MATRIX,
148 G_PARAM_READWRITE);
149 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
151 param = g_param_spec_boxed("local-map",
152 P_("Local Map"),
153 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
154 ADG_TYPE_MATRIX,
155 G_PARAM_READWRITE);
156 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
158 param = g_param_spec_enum("local-method",
159 P_("Local Mix Method"),
160 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
161 ADG_TYPE_MIX_METHOD, ADG_MIX_ANCESTORS,
162 G_PARAM_READWRITE);
163 g_object_class_install_property(gobject_class, PROP_LOCAL_METHOD, param);
166 * AdgEntity::parent-set:
167 * @entity: an #AdgEntity
168 * @old_parent: the old parent
170 * Emitted after the parent entity has changed. The new parent
171 * can be inspected using adg_entity_get_parent().
173 * It is allowed for both old and new parent to be %NULL.
175 _adg_signals[PARENT_SET] =
176 g_signal_new("parent-set",
177 G_OBJECT_CLASS_TYPE(gobject_class),
178 G_SIGNAL_RUN_FIRST,
179 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
180 NULL, NULL,
181 adg_marshal_VOID__OBJECT,
182 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
185 * AdgEntity::global-changed
186 * @entity: an #AdgEntity
188 * Emitted when the global map of @entity or any of its parent
189 * has changed. The default handler will compute the new global
190 * matrix, updating the internal cache.
192 _adg_signals[GLOBAL_CHANGED] =
193 g_signal_new("global-changed",
194 G_OBJECT_CLASS_TYPE(gobject_class),
195 G_SIGNAL_RUN_FIRST,
196 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
197 NULL, NULL,
198 adg_marshal_VOID__VOID,
199 G_TYPE_NONE, 0);
202 * AdgEntity::local-changed
203 * @entity: an #AdgEntity
205 * Emitted when the local map of @entity or any of its parent
206 * has changed. The default handler will compute the new local
207 * matrix, updating the internal cache.
209 _adg_signals[LOCAL_CHANGED] =
210 g_signal_new("local-changed",
211 G_OBJECT_CLASS_TYPE(gobject_class),
212 G_SIGNAL_RUN_FIRST,
213 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
214 NULL, NULL,
215 adg_marshal_VOID__VOID,
216 G_TYPE_NONE, 0);
219 * AdgEntity::invalidate:
220 * @entity: an #AdgEntity
222 * Invalidates the whole @entity, that is resets all the cache
223 * (if present) built during the #AdgEntity::arrange signal.
224 * The resulting state is a clean entity, similar to what you
225 * have just before the first rendering.
227 closure = g_cclosure_new(G_CALLBACK(_adg_real_invalidate), NULL, NULL);
228 _adg_signals[INVALIDATE] =
229 g_signal_newv("invalidate", ADG_TYPE_ENTITY,
230 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
231 adg_marshal_VOID__VOID,
232 G_TYPE_NONE, 0, param_types);
235 * AdgEntity::arrange:
236 * @entity: an #AdgEntity
238 * Arranges the layout of @entity, updating the cache if necessary,
239 * and computes the extents of @entity.
241 closure = g_cclosure_new(G_CALLBACK(_adg_real_arrange), NULL, NULL);
242 _adg_signals[ARRANGE] =
243 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);
249 * AdgEntity::render:
250 * @entity: an #AdgEntity
251 * @cr: a #cairo_t drawing context
253 * Causes the rendering of @entity on @cr. A render signal will
254 * automatically emit #AdgEntity::arrange just before the real
255 * rendering on the cairo context.
257 closure = g_cclosure_new(G_CALLBACK(_adg_real_render), NULL, NULL);
258 param_types[0] = G_TYPE_POINTER;
259 _adg_signals[RENDER] =
260 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 data->global.is_defined = FALSE;
278 adg_matrix_copy(&data->global.matrix, adg_matrix_null());
279 data->local.is_defined = FALSE;
280 adg_matrix_copy(&data->local.matrix, adg_matrix_null());
281 data->extents.is_defined = FALSE;
283 entity->data = data;
286 static void
287 _adg_dispose(GObject *object)
289 AdgEntity *entity;
290 AdgEntityPrivate *data;
292 entity = (AdgEntity *) object;
293 data = entity->data;
295 /* This call will emit a "notify" signal for parent.
296 * Consequentially, the references to the old parent is dropped. */
297 adg_entity_set_parent(entity, NULL);
299 if (data->hash_styles != NULL) {
300 g_hash_table_destroy(data->hash_styles);
301 data->hash_styles = NULL;
304 if (_ADG_OLD_OBJECT_CLASS->dispose)
305 _ADG_OLD_OBJECT_CLASS->dispose(object);
308 static void
309 _adg_get_property(GObject *object, guint prop_id,
310 GValue *value, GParamSpec *pspec)
312 AdgEntity *entity;
313 AdgEntityPrivate *data;
315 entity = (AdgEntity *) object;
316 data = entity->data;
318 switch (prop_id) {
319 case PROP_PARENT:
320 g_value_set_object(value, data->parent);
321 break;
322 case PROP_GLOBAL_MAP:
323 g_value_set_boxed(value, &data->global_map);
324 break;
325 case PROP_LOCAL_MAP:
326 g_value_set_boxed(value, &data->local_map);
327 break;
328 case PROP_LOCAL_METHOD:
329 g_value_set_enum(value, data->local_method);
330 break;
331 default:
332 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
333 break;
337 static void
338 _adg_set_property(GObject *object, guint prop_id,
339 const GValue *value, GParamSpec *pspec)
341 AdgEntityPrivate *data = ((AdgEntity *) object)->data;
343 switch (prop_id) {
344 case PROP_PARENT:
345 _adg_set_parent((AdgEntity *) object,
346 (AdgEntity *) g_value_get_object(value));
347 break;
348 case PROP_GLOBAL_MAP:
349 adg_matrix_copy(&data->global_map, g_value_get_boxed(value));
350 data->global.is_defined = FALSE;
351 break;
352 case PROP_LOCAL_MAP:
353 adg_matrix_copy(&data->local_map, g_value_get_boxed(value));
354 data->local.is_defined = FALSE;
355 break;
356 case PROP_LOCAL_METHOD:
357 data->local_method = g_value_get_enum(value);
358 g_signal_emit(object, _adg_signals[LOCAL_CHANGED], 0);
359 break;
360 default:
361 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
362 break;
368 * adg_switch_extents:
369 * @state: new extents state
371 * Strokes (if @state is %TRUE) a rectangle around every entity to
372 * show their extents. Useful for debugging purposes.
374 void
375 adg_switch_extents(gboolean state)
377 _adg_show_extents = state;
381 * adg_entity_get_canvas:
382 * @entity: an #AdgEntity
384 * Walks on the @entity hierarchy and gets the first parent of @entity that is
385 * of #AdgCanvas derived type.
387 * Returns: the requested canvas or %NULL on errors or if there is
388 * no #AdgCanvas in the @entity hierarchy
390 AdgCanvas *
391 adg_entity_get_canvas(AdgEntity *entity)
393 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
395 while (entity) {
396 if (ADG_IS_CANVAS(entity))
397 return (AdgCanvas *) entity;
399 entity = adg_entity_get_parent(entity);
402 return NULL;
406 * adg_entity_set_parent:
407 * @entity: an #AdgEntity
408 * @parent: the parent entity
410 * <note><para>
411 * This function is only useful in entity implementations.
412 * </para></note>
414 * Sets a new parent on @entity.
416 void
417 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
419 g_return_if_fail(ADG_IS_ENTITY(entity));
420 g_object_set(entity, "parent", parent, NULL);
424 * adg_entity_get_parent:
425 * @entity: an #AdgEntity
427 * Gets the parent of @entity.
429 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
431 AdgEntity *
432 adg_entity_get_parent(AdgEntity *entity)
434 AdgEntityPrivate *data;
436 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
438 data = entity->data;
440 return data->parent;
444 * adg_entity_set_global_map:
445 * @entity: an #AdgEntity object
446 * @map: the new map
448 * Sets the new global transformation of @entity to @map:
449 * the old map is discarded. If @map is %NULL, the global
450 * map is left unchanged.
452 void
453 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
455 g_return_if_fail(ADG_IS_ENTITY(entity));
456 g_object_set(entity, "global-map", map, NULL);
460 * adg_entity_transform_global_map:
461 * @entity: an #AdgEntity object
462 * @transformation: the transformation to apply
463 * @mode: how @transformation should be applied
465 * Convenient function to change the global map of @entity by
466 * applying @tranformation using the @mode operator. This is
467 * logically equivalent to the following:
469 * |[
470 * AdgMatrix map;
471 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
472 * adg_matrix_transform(&map, transformation, mode);
473 * adg_entity_set_global_map(entity, &map);
474 * ]|
476 void
477 adg_entity_transform_global_map(AdgEntity *entity,
478 const AdgMatrix *transformation,
479 AdgTransformMode mode)
481 AdgEntityPrivate *data;
482 AdgMatrix map;
484 g_return_if_fail(ADG_IS_ENTITY(entity));
485 g_return_if_fail(transformation != NULL);
487 data = entity->data;
489 adg_matrix_copy(&map, &data->global_map);
490 adg_matrix_transform(&map, transformation, mode);
492 g_object_set(entity, "global-map", &map, NULL);
496 * adg_entity_get_global_map:
497 * @entity: an #AdgEntity object
499 * Gets the transformation to be used to compute the global matrix
500 * of @entity.
502 * Returns: the requested map or %NULL on errors
504 const AdgMatrix *
505 adg_entity_get_global_map(AdgEntity *entity)
507 AdgEntityPrivate *data;
509 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
511 data = entity->data;
513 return &data->global_map;
517 * adg_entity_get_global_matrix:
518 * @entity: an #AdgEntity object
520 * Gets the current global matrix of @entity. The returned value
521 * is owned by @entity and should not be changed or freed.
523 * The global matrix is computed in the arrange() phase by
524 * combining all the global maps of the @entity hierarchy using
525 * the %ADG_MIX_ANCESTORS method.
527 * Returns: the global matrix or %NULL on errors
529 const AdgMatrix *
530 adg_entity_get_global_matrix(AdgEntity *entity)
532 AdgEntityPrivate *data;
534 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
536 data = entity->data;
538 return &data->global.matrix;
542 * adg_entity_set_local_map:
543 * @entity: an #AdgEntity object
544 * @map: the new map
546 * Sets the new local transformation of @entity to @map:
547 * the old map is discarded. If @map is %NULL, the local
548 * map is left unchanged.
550 void
551 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
553 g_return_if_fail(ADG_IS_ENTITY(entity));
554 g_object_set(entity, "local-map", map, NULL);
558 * adg_entity_transform_local_map:
559 * @entity: an #AdgEntity object
560 * @transformation: the transformation to apply
561 * @mode: how @transformation should be applied
563 * Convenient function to change the local map of @entity by
564 * applying @tranformation using the @mode operator. This is
565 * logically equivalent to the following:
567 * |[
568 * AdgMatrix map;
569 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
570 * adg_matrix_transform(&map, transformation, mode);
571 * adg_entity_set_local_map(entity, &map);
572 * ]|
574 void
575 adg_entity_transform_local_map(AdgEntity *entity,
576 const AdgMatrix *transformation,
577 AdgTransformMode mode)
579 AdgEntityPrivate *data;
580 AdgMatrix map;
582 g_return_if_fail(ADG_IS_ENTITY(entity));
583 g_return_if_fail(transformation != NULL);
585 data = entity->data;
587 adg_matrix_copy(&map, &data->local_map);
588 adg_matrix_transform(&map, transformation, mode);
589 g_object_set(entity, "local-map", &map, NULL);
593 * adg_entity_get_local_map:
594 * @entity: an #AdgEntity object
596 * Gets the transformation to be used to compute the local matrix
597 * of @entity and store it in @map.
599 * Returns: the requested map or %NULL on errors
601 const AdgMatrix *
602 adg_entity_get_local_map(AdgEntity *entity)
604 AdgEntityPrivate *data;
606 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
608 data = entity->data;
610 return &data->local_map;
614 * adg_entity_get_local_matrix:
615 * @entity: an #AdgEntity object
617 * Gets the current local matrix of @entity. The returned value
618 * is owned by @entity and should not be changed or freed.
620 * The local matrix is computed in the arrange() phase by
621 * combining all the local maps of the @entity hierarchy using
622 * the method specified by the #AdgEntity:local-method property.
624 * Returns: the local matrix or %NULL on errors
626 const AdgMatrix *
627 adg_entity_get_local_matrix(AdgEntity *entity)
629 AdgEntityPrivate *data;
631 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
633 data = entity->data;
635 return &data->local.matrix;
639 * adg_entity_set_local_method:
640 * @entity: an #AdgEntity object
641 * @local_method: new method
643 * Sets a new local mix method on @entity. The
644 * #AdgEntity:local-method property defines how the local
645 * matrix must be computed: check out the #AdgMixMethod
646 * documentation to know what are the availables methods
647 * and how they affect the local matrix computation.
649 * Setting a different local method emits an #Adgentity::local-changed
650 * signal on @entity.
652 void
653 adg_entity_set_local_method(AdgEntity *entity, AdgMixMethod local_method)
655 g_return_if_fail(ADG_IS_ENTITY(entity));
656 g_object_set(entity, "local-method", local_method, NULL);
660 * adg_entity_get_local_method:
661 * @entity: an #AdgEntity object
663 * Gets the local mix method of @entity. Check out the
664 * adg_entity_set_local_method() documentation to know what the
665 * local method is used for.
667 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
669 AdgMixMethod
670 adg_entity_get_local_method(AdgEntity *entity)
672 AdgEntityPrivate *data;
674 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
676 data = entity->data;
678 return data->local_method;
682 * adg_entity_set_extents:
683 * @entity: an #AdgEntity
684 * @extents: the new extents
686 * <note><para>
687 * This function is only useful in entity implementations.
688 * </para></note>
690 * Sets a new bounding box for @entity. @extents can be %NULL,
691 * in which case the extents are unset.
693 void
694 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
696 AdgEntityPrivate *data;
698 g_return_if_fail(ADG_IS_ENTITY(entity));
700 data = entity->data;
702 if (extents == NULL)
703 data->extents.is_defined = FALSE;
704 else
705 cpml_extents_copy(&data->extents, extents);
709 * adg_entity_get_extents:
710 * @entity: an #AdgEntity
712 * Gets the bounding box of @entity. The returned struct is
713 * owned by @entity and should not modified or freed.
715 * This struct specifies the surface portion (in global space
716 * of @entity) occupied by the entity without taking into
717 * account rendering properties such as line thickness or caps.
719 * The #AdgEntity::arrange signal should be emitted before
720 * this call (either explicitely trought adg_entity_arrange()
721 * or implicitely with adg_entity_render()) in order to get
722 * an up to date boundary box.
724 * Returns: the bounding box of @entity or %NULL on errors
726 const CpmlExtents *
727 adg_entity_get_extents(AdgEntity *entity)
729 AdgEntityPrivate *data;
731 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
733 data = entity->data;
735 return &data->extents;
739 * adg_entity_set_style:
740 * @entity: an #AdgEntity
741 * @dress: a dress style
742 * @style: the new style to use
744 * Overrides the style of @dress for @entity and its children.
745 * If @style is %NULL, any previous override is removed.
747 * The new style must still be compatible with @dress: check out
748 * the adg_dress_style_is_compatible() documentation to know
749 * what a compatible style means.
751 void
752 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
754 AdgEntityPrivate *data;
755 gpointer p_dress;
756 AdgStyle *old_style;
758 g_return_if_fail(ADG_IS_ENTITY(entity));
760 data = entity->data;
762 if (data->hash_styles == NULL && style == NULL)
763 return;
765 if (data->hash_styles == NULL)
766 data->hash_styles = g_hash_table_new_full(NULL, NULL,
767 NULL, g_object_unref);
769 p_dress = GINT_TO_POINTER(dress);
770 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
772 if (style == old_style)
773 return;
775 if (style == NULL) {
776 g_hash_table_remove(data->hash_styles, p_dress);
777 return;
780 if (!adg_dress_style_is_compatible(dress, style)) {
781 GType ancestor_type = adg_dress_get_ancestor_type(dress);
783 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
784 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
785 g_type_name(ancestor_type), adg_dress_get_name(dress));
787 return;
790 g_object_ref(style);
791 g_hash_table_replace(data->hash_styles, p_dress, style);
795 * adg_entity_get_style:
796 * @entity: an #AdgEntity
797 * @dress: the dress of the style to get
799 * Gets the overriden @dress style from @entity. This is a kind
800 * of accessor function: to get the style to be used for rendering
801 * purpose, use adg_entity_style() instead.
803 * Returns: the requested style or %NULL if the @dress style
804 * is not overriden
806 AdgStyle *
807 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
809 AdgEntityPrivate *data;
811 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
813 data = entity->data;
815 if (data->hash_styles == NULL)
816 return NULL;
818 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
822 * adg_entity_style:
823 * @entity: an #AdgEntity
824 * @dress: the dress of the style to get
826 * Gets the style to be used for @entity. @dress specifies which
827 * "family" of style to get.
829 * The following sequence of checks is performed to get the proper
830 * style, stopping at the first succesfull result:
832 * <orderedlist>
833 * <listitem>check if the style is directly overriden by this entity,
834 * as returned by adg_entity_get_style();</listitem>
835 * <listitem>check if @entity has a parent, in which case returns the
836 * adg_entity_style() of the parent;</listitem>
837 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
838 * </orderedlist>
840 * Returns: the requested style or %NULL for transparent dresses or errors
842 AdgStyle *
843 adg_entity_style(AdgEntity *entity, AdgDress dress)
845 AdgStyle *style;
847 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
849 style = adg_entity_get_style(entity, dress);
851 if (style == NULL) {
852 AdgEntityPrivate *data = entity->data;
854 if (data->parent != NULL)
855 style = adg_entity_style(data->parent, dress);
856 else
857 style = adg_dress_get_fallback(dress);
860 return style;
864 * adg_entity_apply_dress:
865 * @entity: an #AdgEntity
866 * @dress: the dress style to apply
867 * @cr: a #cairo_t drawing context
869 * Convenient function to apply a @dress style (as returned by
870 * adg_entity_style()) to the @cr cairo context.
872 void
873 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
875 AdgStyle *style;
877 g_return_if_fail(ADG_IS_ENTITY(entity));
878 g_return_if_fail(cr != NULL);
880 style = adg_entity_style(entity, dress);
882 if (style != NULL)
883 adg_style_apply(style, entity, cr);
887 * adg_entity_global_changed:
888 * @entity: an #AdgEntity
890 * Emits the #AdgEntity::global-changed signal on @entity and on all of
891 * its children, if any.
893 void
894 adg_entity_global_changed(AdgEntity *entity)
896 g_return_if_fail(ADG_IS_ENTITY(entity));
898 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
902 * adg_entity_local_changed:
903 * @entity: an #AdgEntity
905 * Emits the #AdgEntity::local-changed signal on @entity and on all of
906 * its children, if any.
908 void
909 adg_entity_local_changed(AdgEntity *entity)
911 g_return_if_fail(ADG_IS_ENTITY(entity));
913 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
917 * adg_entity_invalidate:
918 * @entity: an #AdgEntity
920 * Emits the #AdgEntity::invalidate signal on @entity and on all of
921 * its children, if any, clearing the eventual cache stored by the
922 * #AdgEntity::arrange signal and setting the entity state similary
923 * to the just initialized entity.
925 void
926 adg_entity_invalidate(AdgEntity *entity)
928 g_return_if_fail(ADG_IS_ENTITY(entity));
930 g_signal_emit(entity, _adg_signals[INVALIDATE], 0);
934 * adg_entity_arrange:
935 * @entity: an #AdgEntity
937 * Emits the #AdgEntity::arrange signal on @entity and all its children,
938 * if any. This function is rarely needed as the arrange call is usually
939 * implicitely called by the #AdgEntity::render signal or iby a call to
940 * adg_entity_get_extents().
942 void
943 adg_entity_arrange(AdgEntity *entity)
945 g_return_if_fail(ADG_IS_ENTITY(entity));
947 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
951 * adg_entity_render:
952 * @entity: an #AdgEntity
953 * @cr: a #cairo_t drawing context
955 * Emits the #AdgEntity::render signal on @entity and on all of its
956 * children, if any, causing the rendering to the @cr cairo context.
958 void
959 adg_entity_render(AdgEntity *entity, cairo_t *cr)
961 g_return_if_fail(ADG_IS_ENTITY(entity));
963 g_signal_emit(entity, _adg_signals[RENDER], 0, cr);
967 * adg_entity_point:
968 * @entity: an #AdgEntity
969 * @point: the #AdgPoint to define
970 * @new_point: the new #AdgPoint value
972 * A convenient method to set an #AdgPoint with old value of @point
973 * to the new value @new_point. It assumes the points are owned by
974 * @entity, so it takes also care of the dependencies between
975 * @entity and the model bound to the #AdgPoint.
977 * @point can be %NULL, in which case a new #AdgPoint is created.
978 * Also, @new_point can be %NULL in which case @point is destroyed.
980 * Returns: the new properly defined point
982 AdgPoint *
983 adg_entity_point(AdgEntity *entity, AdgPoint *point, AdgPoint *new_point)
985 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
987 if (!adg_point_equal(point, new_point)) {
988 AdgModel *old_model, *new_model;
990 old_model = point ? adg_point_get_model(point) : NULL;
991 new_model = new_point ? adg_point_get_model(new_point) : NULL;
993 if (new_model != old_model) {
994 if (new_model)
995 adg_model_add_dependency(new_model, entity);
996 if (old_model)
997 adg_model_remove_dependency(old_model, entity);
1000 if (new_point && point) {
1001 adg_point_copy(point, new_point);
1002 } else if (new_point) {
1003 point = adg_point_dup(new_point);
1004 } else {
1005 adg_point_destroy(point);
1006 point = NULL;
1010 return point;
1014 static void
1015 _adg_set_parent(AdgEntity *entity, AdgEntity *parent)
1017 AdgEntityPrivate *data;
1018 AdgEntity *old_parent;
1020 data = entity->data;
1021 old_parent = data->parent;
1023 if (parent)
1024 g_object_ref(parent);
1026 data->parent = parent;
1027 data->global.is_defined = FALSE;
1028 data->local.is_defined = FALSE;
1030 g_signal_emit(entity, _adg_signals[PARENT_SET], 0, old_parent);
1032 if (old_parent)
1033 g_object_unref(old_parent);
1036 static void
1037 _adg_global_changed(AdgEntity *entity)
1039 AdgEntityPrivate *data;
1040 const AdgMatrix *map;
1041 AdgMatrix *matrix;
1043 data = entity->data;
1044 map = &data->global_map;
1045 matrix = &data->global.matrix;
1047 if (data->parent) {
1048 adg_matrix_copy(matrix, adg_entity_get_global_matrix(data->parent));
1049 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1050 } else {
1051 adg_matrix_copy(matrix, map);
1055 static void
1056 _adg_local_changed(AdgEntity *entity)
1058 AdgEntityPrivate *data;
1059 const AdgMatrix *map;
1060 AdgMatrix *matrix;
1062 data = entity->data;
1063 map = &data->local_map;
1064 matrix = &data->local.matrix;
1066 switch (data->local_method) {
1067 case ADG_MIX_DISABLED:
1068 adg_matrix_copy(matrix, adg_matrix_identity());
1069 break;
1070 case ADG_MIX_NONE:
1071 adg_matrix_copy(matrix, map);
1072 break;
1073 case ADG_MIX_ANCESTORS:
1074 if (data->parent) {
1075 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1076 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1077 } else {
1078 adg_matrix_copy(matrix, map);
1080 break;
1081 case ADG_MIX_ANCESTORS_NORMALIZED:
1082 if (data->parent) {
1083 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1084 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1085 } else {
1086 adg_matrix_copy(matrix, map);
1088 adg_matrix_normalize(matrix);
1089 break;
1090 case ADG_MIX_PARENT:
1091 if (data->parent) {
1092 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1093 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1094 } else {
1095 adg_matrix_copy(matrix, map);
1097 break;
1098 case ADG_MIX_PARENT_NORMALIZED:
1099 if (data->parent) {
1100 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1101 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1102 } else {
1103 adg_matrix_copy(matrix, map);
1105 adg_matrix_normalize(matrix);
1106 break;
1107 case ADG_MIX_UNDEFINED:
1108 g_warning(_("%s: requested to mix the maps using an undefined method"),
1109 G_STRLOC);
1110 break;
1111 default:
1112 g_return_if_reached();
1113 break;
1117 static void
1118 _adg_real_invalidate(AdgEntity *entity)
1120 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1121 AdgEntityPrivate *data = entity->data;
1123 /* Do not raise any warning if invalidate() is not defined,
1124 * assuming entity does not have additional cache to be cleared */
1125 if (klass->invalidate)
1126 klass->invalidate(entity);
1128 data->extents.is_defined = FALSE;
1131 static void
1132 _adg_real_arrange(AdgEntity *entity)
1134 AdgEntityClass *klass;
1135 AdgEntityPrivate *data;
1137 klass = ADG_ENTITY_GET_CLASS(entity);
1138 data = entity->data;
1140 /* Update the global matrix, if required */
1141 if (!data->global.is_defined) {
1142 data->global.is_defined = TRUE;
1143 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1146 /* Update the local matrix, if required */
1147 if (!data->local.is_defined) {
1148 data->local.is_defined = TRUE;
1149 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1152 /* The arrange() method must be defined */
1153 if (klass->arrange == NULL) {
1154 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1155 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1156 data->extents.is_defined = FALSE;
1157 return;
1160 klass->arrange(entity);
1163 static void
1164 _adg_real_render(AdgEntity *entity, cairo_t *cr)
1166 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1168 /* The render method must be defined */
1169 if (klass->render == NULL) {
1170 g_warning(_("%s: `render' method not implemented for type `%s'"),
1171 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1172 return;
1175 /* Before the rendering, the entity should be arranged */
1176 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1178 cairo_save(cr);
1179 klass->render(entity, cr);
1180 cairo_restore(cr);
1182 if (_adg_show_extents) {
1183 AdgEntityPrivate *data = entity->data;
1184 CpmlExtents *extents = &data->extents;
1186 if (extents->is_defined) {
1187 cairo_save(cr);
1188 cairo_set_source_rgba(cr, 0.15, 0.15, 0.15, 0.15);
1189 cairo_rectangle(cr, extents->org.x, extents->org.y,
1190 extents->size.x, extents->size.y);
1191 cairo_fill(cr);
1192 cairo_restore(cr);