[ADG] Moved include dependencies from .h to .c
[adg.git] / src / adg / adg-entity.c
blobcf8ff719a1cf4dc4feec56d3856c47bbe26b8a96
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011 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-container.h"
63 #include "adg-table.h"
64 #include "adg-title-block.h"
65 #include "adg-canvas.h"
66 #include "adg-dress.h"
67 #include "adg-style.h"
68 #include "adg-model.h"
69 #include "adg-point.h"
71 #include "adg-entity-private.h"
74 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
77 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
79 enum {
80 PROP_0,
81 PROP_PARENT,
82 PROP_GLOBAL_MAP,
83 PROP_LOCAL_MAP,
84 PROP_LOCAL_METHOD
87 enum {
88 PARENT_SET,
89 GLOBAL_CHANGED,
90 LOCAL_CHANGED,
91 INVALIDATE,
92 ARRANGE,
93 RENDER,
94 LAST_SIGNAL
98 static void _adg_dispose (GObject *object);
99 static void _adg_get_property (GObject *object,
100 guint prop_id,
101 GValue *value,
102 GParamSpec *pspec);
103 static void _adg_set_property (GObject *object,
104 guint prop_id,
105 const GValue *value,
106 GParamSpec *pspec);
107 static void _adg_set_parent (AdgEntity *entity,
108 AdgEntity *parent);
109 static void _adg_global_changed (AdgEntity *entity);
110 static void _adg_local_changed (AdgEntity *entity);
111 static void _adg_real_invalidate (AdgEntity *entity);
112 static void _adg_real_arrange (AdgEntity *entity);
113 static void _adg_real_render (AdgEntity *entity,
114 cairo_t *cr);
115 static guint _adg_signals[LAST_SIGNAL] = { 0 };
116 static gboolean _adg_show_extents = FALSE;
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 = _adg_dispose;
132 gobject_class->get_property = _adg_get_property;
133 gobject_class->set_property = _adg_set_property;
135 klass->parent_set = NULL;
136 klass->global_changed = _adg_global_changed;
137 klass->local_changed = _adg_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 local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
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_enum("local-method",
164 P_("Local Mix Method"),
165 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
166 ADG_TYPE_MIX_METHOD, ADG_MIX_ANCESTORS,
167 G_PARAM_READWRITE);
168 g_object_class_install_property(gobject_class, PROP_LOCAL_METHOD, 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 _adg_signals[PARENT_SET] =
181 g_signal_new("parent-set",
182 G_OBJECT_CLASS_TYPE(gobject_class),
183 G_SIGNAL_RUN_FIRST,
184 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
185 NULL, NULL,
186 adg_marshal_VOID__OBJECT,
187 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
190 * AdgEntity::global-changed
191 * @entity: an #AdgEntity
193 * Emitted when the global map of @entity or any of its parent
194 * has changed. The default handler will compute the new global
195 * matrix, updating the internal cache.
197 _adg_signals[GLOBAL_CHANGED] =
198 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 _adg_signals[LOCAL_CHANGED] =
215 g_signal_new("local-changed",
216 G_OBJECT_CLASS_TYPE(gobject_class),
217 G_SIGNAL_RUN_FIRST,
218 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
219 NULL, NULL,
220 adg_marshal_VOID__VOID,
221 G_TYPE_NONE, 0);
224 * AdgEntity::invalidate:
225 * @entity: an #AdgEntity
227 * Invalidates the whole @entity, that is resets all the cache
228 * (if present) built during the #AdgEntity::arrange signal.
229 * The resulting state is a clean entity, similar to what you
230 * have just before the first rendering.
232 closure = g_cclosure_new(G_CALLBACK(_adg_real_invalidate), NULL, NULL);
233 _adg_signals[INVALIDATE] =
234 g_signal_newv("invalidate", ADG_TYPE_ENTITY,
235 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
236 adg_marshal_VOID__VOID,
237 G_TYPE_NONE, 0, param_types);
240 * AdgEntity::arrange:
241 * @entity: an #AdgEntity
243 * Arranges the layout of @entity, updating the cache if necessary,
244 * and computes the extents of @entity.
246 closure = g_cclosure_new(G_CALLBACK(_adg_real_arrange), NULL, NULL);
247 _adg_signals[ARRANGE] =
248 g_signal_newv("arrange", ADG_TYPE_ENTITY,
249 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
250 adg_marshal_VOID__VOID,
251 G_TYPE_NONE, 0, param_types);
254 * AdgEntity::render:
255 * @entity: an #AdgEntity
256 * @cr: a #cairo_t drawing context
258 * Causes the rendering of @entity on @cr. A render signal will
259 * automatically emit #AdgEntity::arrange just before the real
260 * rendering on the cairo context.
262 closure = g_cclosure_new(G_CALLBACK(_adg_real_render), NULL, NULL);
263 param_types[0] = G_TYPE_POINTER;
264 _adg_signals[RENDER] =
265 g_signal_newv("render", ADG_TYPE_ENTITY,
266 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
267 adg_marshal_VOID__POINTER,
268 G_TYPE_NONE, 1, param_types);
271 static void
272 adg_entity_init(AdgEntity *entity)
274 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
275 ADG_TYPE_ENTITY,
276 AdgEntityPrivate);
277 data->parent = NULL;
278 cairo_matrix_init_identity(&data->global_map);
279 cairo_matrix_init_identity(&data->local_map);
280 data->local_method = ADG_MIX_ANCESTORS;
281 data->hash_styles = NULL;
282 data->global.is_defined = FALSE;
283 adg_matrix_copy(&data->global.matrix, adg_matrix_null());
284 data->local.is_defined = FALSE;
285 adg_matrix_copy(&data->local.matrix, adg_matrix_null());
286 data->extents.is_defined = FALSE;
288 entity->data = data;
291 static void
292 _adg_dispose(GObject *object)
294 AdgEntity *entity;
295 AdgEntityPrivate *data;
297 entity = (AdgEntity *) object;
298 data = entity->data;
300 /* This call will emit a "notify" signal for parent.
301 * Consequentially, the references to the old parent is dropped. */
302 adg_entity_set_parent(entity, NULL);
304 if (data->hash_styles != NULL) {
305 g_hash_table_destroy(data->hash_styles);
306 data->hash_styles = NULL;
309 if (_ADG_OLD_OBJECT_CLASS->dispose)
310 _ADG_OLD_OBJECT_CLASS->dispose(object);
313 static void
314 _adg_get_property(GObject *object, guint prop_id,
315 GValue *value, GParamSpec *pspec)
317 AdgEntity *entity;
318 AdgEntityPrivate *data;
320 entity = (AdgEntity *) object;
321 data = entity->data;
323 switch (prop_id) {
324 case PROP_PARENT:
325 g_value_set_object(value, data->parent);
326 break;
327 case PROP_GLOBAL_MAP:
328 g_value_set_boxed(value, &data->global_map);
329 break;
330 case PROP_LOCAL_MAP:
331 g_value_set_boxed(value, &data->local_map);
332 break;
333 case PROP_LOCAL_METHOD:
334 g_value_set_enum(value, data->local_method);
335 break;
336 default:
337 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
338 break;
342 static void
343 _adg_set_property(GObject *object, guint prop_id,
344 const GValue *value, GParamSpec *pspec)
346 AdgEntityPrivate *data = ((AdgEntity *) object)->data;
348 switch (prop_id) {
349 case PROP_PARENT:
350 _adg_set_parent((AdgEntity *) object,
351 (AdgEntity *) g_value_get_object(value));
352 break;
353 case PROP_GLOBAL_MAP:
354 adg_matrix_copy(&data->global_map, g_value_get_boxed(value));
355 data->global.is_defined = FALSE;
356 break;
357 case PROP_LOCAL_MAP:
358 adg_matrix_copy(&data->local_map, g_value_get_boxed(value));
359 data->local.is_defined = FALSE;
360 break;
361 case PROP_LOCAL_METHOD:
362 data->local_method = g_value_get_enum(value);
363 g_signal_emit(object, _adg_signals[LOCAL_CHANGED], 0);
364 break;
365 default:
366 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
367 break;
373 * adg_switch_extents:
374 * @state: new extents state
376 * Strokes (if @state is %TRUE) a rectangle around every entity to
377 * show their extents. Useful for debugging purposes.
379 void
380 adg_switch_extents(gboolean state)
382 _adg_show_extents = state;
386 * adg_entity_get_canvas:
387 * @entity: an #AdgEntity
389 * Walks on the @entity hierarchy and gets the first parent of @entity that is
390 * of #AdgCanvas derived type.
392 * Returns: the requested canvas or %NULL on errors or if there is
393 * no #AdgCanvas in the @entity hierarchy
395 AdgCanvas *
396 adg_entity_get_canvas(AdgEntity *entity)
398 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
400 while (entity) {
401 if (ADG_IS_CANVAS(entity))
402 return (AdgCanvas *) entity;
404 entity = adg_entity_get_parent(entity);
407 return NULL;
411 * adg_entity_set_parent:
412 * @entity: an #AdgEntity
413 * @parent: the parent entity
415 * <note><para>
416 * This function is only useful in entity implementations.
417 * </para></note>
419 * Sets a new parent on @entity.
421 void
422 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
424 g_return_if_fail(ADG_IS_ENTITY(entity));
425 g_object_set(entity, "parent", parent, NULL);
429 * adg_entity_get_parent:
430 * @entity: an #AdgEntity
432 * Gets the parent of @entity.
434 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
436 AdgEntity *
437 adg_entity_get_parent(AdgEntity *entity)
439 AdgEntityPrivate *data;
441 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
443 data = entity->data;
445 return data->parent;
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, the global
455 * map is left unchanged.
457 void
458 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
460 g_return_if_fail(ADG_IS_ENTITY(entity));
461 g_object_set(entity, "global-map", map, NULL);
465 * adg_entity_transform_global_map:
466 * @entity: an #AdgEntity object
467 * @transformation: the transformation to apply
468 * @mode: how @transformation should be applied
470 * Convenient function to change the global map of @entity by
471 * applying @tranformation using the @mode operator. This is
472 * logically equivalent to the following:
474 * |[
475 * AdgMatrix map;
476 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
477 * adg_matrix_transform(&map, transformation, mode);
478 * adg_entity_set_global_map(entity, &map);
479 * ]|
481 void
482 adg_entity_transform_global_map(AdgEntity *entity,
483 const AdgMatrix *transformation,
484 AdgTransformMode mode)
486 AdgEntityPrivate *data;
487 AdgMatrix map;
489 g_return_if_fail(ADG_IS_ENTITY(entity));
490 g_return_if_fail(transformation != NULL);
492 data = entity->data;
494 adg_matrix_copy(&map, &data->global_map);
495 adg_matrix_transform(&map, transformation, mode);
497 g_object_set(entity, "global-map", &map, NULL);
501 * adg_entity_get_global_map:
502 * @entity: an #AdgEntity object
504 * Gets the transformation to be used to compute the global matrix
505 * of @entity.
507 * Returns: the requested map or %NULL on errors
509 const AdgMatrix *
510 adg_entity_get_global_map(AdgEntity *entity)
512 AdgEntityPrivate *data;
514 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
516 data = entity->data;
518 return &data->global_map;
522 * adg_entity_get_global_matrix:
523 * @entity: an #AdgEntity object
525 * Gets the current global matrix of @entity. The returned value
526 * is owned by @entity and should not be changed or freed.
528 * The global matrix is computed in the arrange() phase by
529 * combining all the global maps of the @entity hierarchy using
530 * the %ADG_MIX_ANCESTORS method.
532 * Returns: the global matrix or %NULL on errors
534 const AdgMatrix *
535 adg_entity_get_global_matrix(AdgEntity *entity)
537 AdgEntityPrivate *data;
539 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
541 data = entity->data;
543 return &data->global.matrix;
547 * adg_entity_set_local_map:
548 * @entity: an #AdgEntity object
549 * @map: the new map
551 * Sets the new local transformation of @entity to @map:
552 * the old map is discarded. If @map is %NULL, the local
553 * map is left unchanged.
555 void
556 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
558 g_return_if_fail(ADG_IS_ENTITY(entity));
559 g_object_set(entity, "local-map", map, NULL);
563 * adg_entity_transform_local_map:
564 * @entity: an #AdgEntity object
565 * @transformation: the transformation to apply
566 * @mode: how @transformation should be applied
568 * Convenient function to change the local map of @entity by
569 * applying @tranformation using the @mode operator. This is
570 * logically equivalent to the following:
572 * |[
573 * AdgMatrix map;
574 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
575 * adg_matrix_transform(&map, transformation, mode);
576 * adg_entity_set_local_map(entity, &map);
577 * ]|
579 void
580 adg_entity_transform_local_map(AdgEntity *entity,
581 const AdgMatrix *transformation,
582 AdgTransformMode mode)
584 AdgEntityPrivate *data;
585 AdgMatrix map;
587 g_return_if_fail(ADG_IS_ENTITY(entity));
588 g_return_if_fail(transformation != NULL);
590 data = entity->data;
592 adg_matrix_copy(&map, &data->local_map);
593 adg_matrix_transform(&map, transformation, mode);
594 g_object_set(entity, "local-map", &map, NULL);
598 * adg_entity_get_local_map:
599 * @entity: an #AdgEntity object
601 * Gets the transformation to be used to compute the local matrix
602 * of @entity and store it in @map.
604 * Returns: the requested map or %NULL on errors
606 const AdgMatrix *
607 adg_entity_get_local_map(AdgEntity *entity)
609 AdgEntityPrivate *data;
611 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
613 data = entity->data;
615 return &data->local_map;
619 * adg_entity_get_local_matrix:
620 * @entity: an #AdgEntity object
622 * Gets the current local matrix of @entity. The returned value
623 * is owned by @entity and should not be changed or freed.
625 * The local matrix is computed in the arrange() phase by
626 * combining all the local maps of the @entity hierarchy using
627 * the method specified by the #AdgEntity:local-method property.
629 * Returns: the local matrix or %NULL on errors
631 const AdgMatrix *
632 adg_entity_get_local_matrix(AdgEntity *entity)
634 AdgEntityPrivate *data;
636 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
638 data = entity->data;
640 return &data->local.matrix;
644 * adg_entity_set_local_method:
645 * @entity: an #AdgEntity object
646 * @local_method: new method
648 * Sets a new local mix method on @entity. The
649 * #AdgEntity:local-method property defines how the local
650 * matrix must be computed: check out the #AdgMixMethod
651 * documentation to know what are the availables methods
652 * and how they affect the local matrix computation.
654 * Setting a different local method emits an #Adgentity::local-changed
655 * signal on @entity.
657 void
658 adg_entity_set_local_method(AdgEntity *entity, AdgMixMethod local_method)
660 g_return_if_fail(ADG_IS_ENTITY(entity));
661 g_object_set(entity, "local-method", local_method, NULL);
665 * adg_entity_get_local_method:
666 * @entity: an #AdgEntity object
668 * Gets the local mix method of @entity. Check out the
669 * adg_entity_set_local_method() documentation to know what the
670 * local method is used for.
672 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
674 AdgMixMethod
675 adg_entity_get_local_method(AdgEntity *entity)
677 AdgEntityPrivate *data;
679 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
681 data = entity->data;
683 return data->local_method;
687 * adg_entity_set_extents:
688 * @entity: an #AdgEntity
689 * @extents: the new extents
691 * <note><para>
692 * This function is only useful in entity implementations.
693 * </para></note>
695 * Sets a new bounding box for @entity. @extents can be %NULL,
696 * in which case the extents are unset.
698 void
699 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
701 AdgEntityPrivate *data;
703 g_return_if_fail(ADG_IS_ENTITY(entity));
705 data = entity->data;
707 if (extents == NULL)
708 data->extents.is_defined = FALSE;
709 else
710 cpml_extents_copy(&data->extents, extents);
714 * adg_entity_get_extents:
715 * @entity: an #AdgEntity
717 * Gets the bounding box of @entity. The returned struct is
718 * owned by @entity and should not modified or freed.
720 * This struct specifies the surface portion (in global space
721 * of @entity) occupied by the entity without taking into
722 * account rendering properties such as line thickness or caps.
724 * The #AdgEntity::arrange signal should be emitted before
725 * this call (either explicitely trought adg_entity_arrange()
726 * or implicitely with adg_entity_render()) in order to get
727 * an up to date boundary box.
729 * Returns: the bounding box of @entity or %NULL on errors
731 const CpmlExtents *
732 adg_entity_get_extents(AdgEntity *entity)
734 AdgEntityPrivate *data;
736 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
738 data = entity->data;
740 return &data->extents;
744 * adg_entity_set_style:
745 * @entity: an #AdgEntity
746 * @dress: a dress style
747 * @style: the new style to use
749 * Overrides the style of @dress for @entity and its children.
750 * If @style is %NULL, any previous override is removed.
752 * The new style must still be compatible with @dress: check out
753 * the adg_dress_style_is_compatible() documentation to know
754 * what a compatible style means.
756 void
757 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
759 AdgEntityPrivate *data;
760 gpointer p_dress;
761 AdgStyle *old_style;
763 g_return_if_fail(ADG_IS_ENTITY(entity));
765 data = entity->data;
767 if (data->hash_styles == NULL && style == NULL)
768 return;
770 if (data->hash_styles == NULL)
771 data->hash_styles = g_hash_table_new_full(NULL, NULL,
772 NULL, g_object_unref);
774 p_dress = GINT_TO_POINTER(dress);
775 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
777 if (style == old_style)
778 return;
780 if (style == NULL) {
781 g_hash_table_remove(data->hash_styles, p_dress);
782 return;
785 if (!adg_dress_style_is_compatible(dress, style)) {
786 GType ancestor_type = adg_dress_get_ancestor_type(dress);
788 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
789 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
790 g_type_name(ancestor_type), adg_dress_get_name(dress));
792 return;
795 g_object_ref(style);
796 g_hash_table_replace(data->hash_styles, p_dress, style);
800 * adg_entity_get_style:
801 * @entity: an #AdgEntity
802 * @dress: the dress of the style to get
804 * Gets the overriden @dress style from @entity. This is a kind
805 * of accessor function: to get the style to be used for rendering
806 * purpose, use adg_entity_style() instead.
808 * Returns: the requested style or %NULL if the @dress style
809 * is not overriden
811 AdgStyle *
812 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
814 AdgEntityPrivate *data;
816 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
818 data = entity->data;
820 if (data->hash_styles == NULL)
821 return NULL;
823 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
827 * adg_entity_style:
828 * @entity: an #AdgEntity
829 * @dress: the dress of the style to get
831 * Gets the style to be used for @entity. @dress specifies which
832 * "family" of style to get.
834 * The following sequence of checks is performed to get the proper
835 * style, stopping at the first succesfull result:
837 * <orderedlist>
838 * <listitem>check if the style is directly overriden by this entity,
839 * as returned by adg_entity_get_style();</listitem>
840 * <listitem>check if @entity has a parent, in which case returns the
841 * adg_entity_style() of the parent;</listitem>
842 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
843 * </orderedlist>
845 * Returns: the requested style or %NULL for transparent dresses or errors
847 AdgStyle *
848 adg_entity_style(AdgEntity *entity, AdgDress dress)
850 AdgStyle *style;
852 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
854 style = adg_entity_get_style(entity, dress);
856 if (style == NULL) {
857 AdgEntityPrivate *data = entity->data;
859 if (data->parent != NULL)
860 style = adg_entity_style(data->parent, dress);
861 else
862 style = adg_dress_get_fallback(dress);
865 return style;
869 * adg_entity_apply_dress:
870 * @entity: an #AdgEntity
871 * @dress: the dress style to apply
872 * @cr: a #cairo_t drawing context
874 * Convenient function to apply a @dress style (as returned by
875 * adg_entity_style()) to the @cr cairo context.
877 void
878 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
880 AdgStyle *style;
882 g_return_if_fail(ADG_IS_ENTITY(entity));
883 g_return_if_fail(cr != NULL);
885 style = adg_entity_style(entity, dress);
887 if (style != NULL)
888 adg_style_apply(style, entity, cr);
892 * adg_entity_global_changed:
893 * @entity: an #AdgEntity
895 * Emits the #AdgEntity::global-changed signal on @entity and on all of
896 * its children, if any.
898 void
899 adg_entity_global_changed(AdgEntity *entity)
901 g_return_if_fail(ADG_IS_ENTITY(entity));
903 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
907 * adg_entity_local_changed:
908 * @entity: an #AdgEntity
910 * Emits the #AdgEntity::local-changed signal on @entity and on all of
911 * its children, if any.
913 void
914 adg_entity_local_changed(AdgEntity *entity)
916 g_return_if_fail(ADG_IS_ENTITY(entity));
918 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
922 * adg_entity_invalidate:
923 * @entity: an #AdgEntity
925 * Emits the #AdgEntity::invalidate signal on @entity and on all of
926 * its children, if any, clearing the eventual cache stored by the
927 * #AdgEntity::arrange signal and setting the entity state similary
928 * to the just initialized entity.
930 void
931 adg_entity_invalidate(AdgEntity *entity)
933 g_return_if_fail(ADG_IS_ENTITY(entity));
935 g_signal_emit(entity, _adg_signals[INVALIDATE], 0);
939 * adg_entity_arrange:
940 * @entity: an #AdgEntity
942 * Emits the #AdgEntity::arrange signal on @entity and all its children,
943 * if any. The arrange call is implicitely called by the
944 * #AdgEntity::render signal but not by adg_entity_get_extents().
946 void
947 adg_entity_arrange(AdgEntity *entity)
949 g_return_if_fail(ADG_IS_ENTITY(entity));
951 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
955 * adg_entity_render:
956 * @entity: an #AdgEntity
957 * @cr: a #cairo_t drawing context
959 * Emits the #AdgEntity::render signal on @entity and on all of its
960 * children, if any, causing the rendering to the @cr cairo context.
962 void
963 adg_entity_render(AdgEntity *entity, cairo_t *cr)
965 g_return_if_fail(ADG_IS_ENTITY(entity));
967 g_signal_emit(entity, _adg_signals[RENDER], 0, cr);
971 * adg_entity_point:
972 * @entity: an #AdgEntity
973 * @point: the #AdgPoint to define
974 * @new_point: the new #AdgPoint value
976 * <note><para>
977 * This function is only useful in entity implementations.
978 * </para></note>
980 * A convenient method to set an #AdgPoint owned by @entity.
981 * @point is the old value while @new_point is the new value. It
982 * can be used for setting #AdgPoint in the private data, such as:
984 * |[
985 * data->point = adg_entity_point(entity, data->point, new_point);
986 * ]|
988 * This function takes care of the dependencies between @entity and
989 * the eventual models bound to the old and new points.
991 * @point can be %NULL, in which case a clone of @new_point will be
992 * returned. Also @new_point can be %NULL, in which case @point is
993 * destroyed and %NULL will be returned.
995 * Returns: the new properly defined point
997 AdgPoint *
998 adg_entity_point(AdgEntity *entity, AdgPoint *point, AdgPoint *new_point)
1000 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
1002 if (!adg_point_equal(point, new_point)) {
1003 AdgModel *old_model, *new_model;
1005 old_model = point ? adg_point_get_model(point) : NULL;
1006 new_model = new_point ? adg_point_get_model(new_point) : NULL;
1008 if (new_model != old_model) {
1009 if (new_model)
1010 adg_model_add_dependency(new_model, entity);
1011 if (old_model)
1012 adg_model_remove_dependency(old_model, entity);
1015 if (new_point && point) {
1016 adg_point_copy(point, new_point);
1017 } else if (new_point) {
1018 point = adg_point_dup(new_point);
1019 } else {
1020 adg_point_destroy(point);
1021 point = NULL;
1025 return point;
1029 static void
1030 _adg_set_parent(AdgEntity *entity, AdgEntity *parent)
1032 AdgEntityPrivate *data;
1033 AdgEntity *old_parent;
1035 data = entity->data;
1036 old_parent = data->parent;
1038 if (parent)
1039 g_object_ref(parent);
1041 data->parent = parent;
1042 data->global.is_defined = FALSE;
1043 data->local.is_defined = FALSE;
1045 g_signal_emit(entity, _adg_signals[PARENT_SET], 0, old_parent);
1047 if (old_parent)
1048 g_object_unref(old_parent);
1051 static void
1052 _adg_global_changed(AdgEntity *entity)
1054 AdgEntityPrivate *data;
1055 const AdgMatrix *map;
1056 AdgMatrix *matrix;
1058 data = entity->data;
1059 map = &data->global_map;
1060 matrix = &data->global.matrix;
1062 if (data->parent) {
1063 adg_matrix_copy(matrix, adg_entity_get_global_matrix(data->parent));
1064 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1065 } else {
1066 adg_matrix_copy(matrix, map);
1070 static void
1071 _adg_local_changed(AdgEntity *entity)
1073 AdgEntityPrivate *data;
1074 const AdgMatrix *map;
1075 AdgMatrix *matrix;
1077 data = entity->data;
1078 map = &data->local_map;
1079 matrix = &data->local.matrix;
1081 switch (data->local_method) {
1082 case ADG_MIX_DISABLED:
1083 adg_matrix_copy(matrix, adg_matrix_identity());
1084 break;
1085 case ADG_MIX_NONE:
1086 adg_matrix_copy(matrix, map);
1087 break;
1088 case ADG_MIX_ANCESTORS:
1089 if (data->parent) {
1090 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1091 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1092 } else {
1093 adg_matrix_copy(matrix, map);
1095 break;
1096 case ADG_MIX_ANCESTORS_NORMALIZED:
1097 if (data->parent) {
1098 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1099 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1100 } else {
1101 adg_matrix_copy(matrix, map);
1103 adg_matrix_normalize(matrix);
1104 break;
1105 case ADG_MIX_PARENT:
1106 if (data->parent) {
1107 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1108 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1109 } else {
1110 adg_matrix_copy(matrix, map);
1112 break;
1113 case ADG_MIX_PARENT_NORMALIZED:
1114 if (data->parent) {
1115 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1116 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1117 } else {
1118 adg_matrix_copy(matrix, map);
1120 adg_matrix_normalize(matrix);
1121 break;
1122 case ADG_MIX_UNDEFINED:
1123 g_warning(_("%s: requested to mix the maps using an undefined method"),
1124 G_STRLOC);
1125 break;
1126 default:
1127 g_return_if_reached();
1128 break;
1132 static void
1133 _adg_real_invalidate(AdgEntity *entity)
1135 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1136 AdgEntityPrivate *data = entity->data;
1138 /* Do not raise any warning if invalidate() is not defined,
1139 * assuming entity does not have additional cache to be cleared */
1140 if (klass->invalidate)
1141 klass->invalidate(entity);
1143 data->extents.is_defined = FALSE;
1146 static void
1147 _adg_real_arrange(AdgEntity *entity)
1149 AdgEntityClass *klass;
1150 AdgEntityPrivate *data;
1152 klass = ADG_ENTITY_GET_CLASS(entity);
1153 data = entity->data;
1155 /* Update the global matrix, if required */
1156 if (!data->global.is_defined) {
1157 data->global.is_defined = TRUE;
1158 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1161 /* Update the local matrix, if required */
1162 if (!data->local.is_defined) {
1163 data->local.is_defined = TRUE;
1164 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1167 /* The arrange() method must be defined */
1168 if (klass->arrange == NULL) {
1169 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1170 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1171 data->extents.is_defined = FALSE;
1172 return;
1175 klass->arrange(entity);
1178 static void
1179 _adg_real_render(AdgEntity *entity, cairo_t *cr)
1181 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1183 /* The render method must be defined */
1184 if (klass->render == NULL) {
1185 g_warning(_("%s: `render' method not implemented for type `%s'"),
1186 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1187 return;
1190 /* Before the rendering, the entity should be arranged */
1191 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1193 cairo_save(cr);
1194 klass->render(entity, cr);
1195 cairo_restore(cr);
1197 if (_adg_show_extents) {
1198 AdgEntityPrivate *data = entity->data;
1199 CpmlExtents *extents = &data->extents;
1201 if (extents->is_defined) {
1202 cairo_save(cr);
1203 cairo_set_source_rgba(cr, 0.15, 0.15, 0.15, 0.15);
1204 cairo_rectangle(cr, extents->org.x, extents->org.y,
1205 extents->size.x, extents->size.y);
1206 cairo_fill(cr);
1207 cairo_restore(cr);