ADG: corrected g-ir-scanner warnings where possible
[adg.git] / src / adg / adg-entity.c
blob2ee187bb709981a033b69aa6e63cfbafe75952ce
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.
32 * Since: 1.0
33 **/
35 /**
36 * AdgEntity:
38 * All fields are private and should not be used directly.
39 * Use its public methods instead.
41 * Since: 1.0
42 **/
44 /**
45 * AdgEntityClass:
46 * @parent_set: called whenever the parent of an entity has changed
47 * @global_changed: the global matrix has been invalidated
48 * @local_changed: the local matrix has been invalidated
49 * @invalidate: invalidating callback, used to clear the internal cache
50 * @arrange: prepare the layout and fill the extents struct
51 * @render: rendering callback, it must be implemented by every entity
53 * Any entity (if not abstract) must implement at least the @render method.
54 * The other signal handlers can be overriden to provide custom behaviors
55 * and usually must chain up the original handler.
57 * Since: 1.0
58 **/
60 /**
61 * AdgEntityCallback:
62 * @entity: an #AdgEntity
63 * @user_data: a general purpose pointer
65 * Callback used when inspecting or browsing entities. For example,
66 * it is passed to adg_model_foreach_dependency() to perform an
67 * operation on all the entities depending on an #AdgModel.
69 * Since: 1.0
70 **/
73 #include "adg-internal.h"
74 #if GTK2_ENABLED
75 #include <gtk/gtk.h>
76 #endif
78 #include "adg-container.h"
79 #include "adg-table.h"
80 #include "adg-title-block.h"
81 #include "adg-canvas.h"
82 #include "adg-dress.h"
83 #include "adg-style.h"
84 #include "adg-model.h"
85 #include "adg-point.h"
87 #include "adg-entity-private.h"
90 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
93 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED)
95 enum {
96 PROP_0,
97 PROP_PARENT,
98 PROP_GLOBAL_MAP,
99 PROP_LOCAL_MAP,
100 PROP_LOCAL_METHOD
103 enum {
104 PARENT_SET,
105 GLOBAL_CHANGED,
106 LOCAL_CHANGED,
107 INVALIDATE,
108 ARRANGE,
109 RENDER,
110 LAST_SIGNAL
114 static void _adg_dispose (GObject *object);
115 static void _adg_get_property (GObject *object,
116 guint prop_id,
117 GValue *value,
118 GParamSpec *pspec);
119 static void _adg_set_property (GObject *object,
120 guint prop_id,
121 const GValue *value,
122 GParamSpec *pspec);
123 static void _adg_set_parent (AdgEntity *entity,
124 AdgEntity *parent);
125 static void _adg_global_changed (AdgEntity *entity);
126 static void _adg_local_changed (AdgEntity *entity);
127 static void _adg_real_invalidate (AdgEntity *entity);
128 static void _adg_real_arrange (AdgEntity *entity);
129 static void _adg_real_render (AdgEntity *entity,
130 cairo_t *cr);
131 static guint _adg_signals[LAST_SIGNAL] = { 0 };
132 static gboolean _adg_show_extents = FALSE;
135 static void
136 adg_entity_class_init(AdgEntityClass *klass)
138 GObjectClass *gobject_class;
139 GParamSpec *param;
140 GClosure *closure;
141 GType param_types[1];
143 gobject_class = (GObjectClass *) klass;
145 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
147 gobject_class->dispose = _adg_dispose;
148 gobject_class->get_property = _adg_get_property;
149 gobject_class->set_property = _adg_set_property;
151 klass->parent_set = NULL;
152 klass->global_changed = _adg_global_changed;
153 klass->local_changed = _adg_local_changed;
154 klass->invalidate = NULL;
155 klass->arrange= NULL;
156 klass->render = NULL;
158 param = g_param_spec_object("parent",
159 P_("Parent Entity"),
160 P_("The parent entity of this entity or NULL if this is a top-level entity"),
161 ADG_TYPE_ENTITY,
162 G_PARAM_READWRITE);
163 g_object_class_install_property(gobject_class, PROP_PARENT, param);
165 param = g_param_spec_boxed("global-map",
166 P_("Global Map"),
167 P_("The transformation to be combined with the parent ones to get the global matrix"),
168 ADG_TYPE_MATRIX,
169 G_PARAM_READWRITE);
170 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
172 param = g_param_spec_boxed("local-map",
173 P_("Local Map"),
174 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
175 ADG_TYPE_MATRIX,
176 G_PARAM_READWRITE);
177 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
179 param = g_param_spec_enum("local-method",
180 P_("Local Mix Method"),
181 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
182 ADG_TYPE_MIX_METHOD, ADG_MIX_ANCESTORS,
183 G_PARAM_READWRITE);
184 g_object_class_install_property(gobject_class, PROP_LOCAL_METHOD, param);
187 * AdgEntity::parent-set:
188 * @entity: an #AdgEntity
189 * @old_parent: the old parent
191 * Emitted after the parent entity has changed. The new parent
192 * can be inspected using adg_entity_get_parent().
194 * It is allowed for both old and new parent to be %NULL.
196 * Since: 1.0
198 _adg_signals[PARENT_SET] =
199 g_signal_new("parent-set",
200 G_OBJECT_CLASS_TYPE(gobject_class),
201 G_SIGNAL_RUN_FIRST,
202 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
203 NULL, NULL,
204 adg_marshal_VOID__OBJECT,
205 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
208 * AdgEntity::global-changed
209 * @entity: an #AdgEntity
211 * Emitted when the global map of @entity or any of its parent
212 * has changed. The default handler will compute the new global
213 * matrix, updating the internal cache.
215 * Since: 1.0
217 _adg_signals[GLOBAL_CHANGED] =
218 g_signal_new("global-changed",
219 G_OBJECT_CLASS_TYPE(gobject_class),
220 G_SIGNAL_RUN_FIRST,
221 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
222 NULL, NULL,
223 adg_marshal_VOID__VOID,
224 G_TYPE_NONE, 0);
227 * AdgEntity::local-changed
228 * @entity: an #AdgEntity
230 * Emitted when the local map of @entity or any of its parent
231 * has changed. The default handler will compute the new local
232 * matrix, updating the internal cache.
234 * Since: 1.0
236 _adg_signals[LOCAL_CHANGED] =
237 g_signal_new("local-changed",
238 G_OBJECT_CLASS_TYPE(gobject_class),
239 G_SIGNAL_RUN_FIRST,
240 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
241 NULL, NULL,
242 adg_marshal_VOID__VOID,
243 G_TYPE_NONE, 0);
246 * AdgEntity::invalidate:
247 * @entity: an #AdgEntity
249 * Invalidates the whole @entity, that is resets all the cache
250 * (if present) built during the #AdgEntity::arrange signal.
251 * The resulting state is a clean entity, similar to what you
252 * have just before the first rendering.
254 * Since: 1.0
256 closure = g_cclosure_new(G_CALLBACK(_adg_real_invalidate), NULL, NULL);
257 _adg_signals[INVALIDATE] =
258 g_signal_newv("invalidate", ADG_TYPE_ENTITY,
259 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
260 adg_marshal_VOID__VOID,
261 G_TYPE_NONE, 0, param_types);
264 * AdgEntity::arrange:
265 * @entity: an #AdgEntity
267 * Arranges the layout of @entity, updating the cache if necessary,
268 * and computes the extents of @entity.
270 * Since: 1.0
272 closure = g_cclosure_new(G_CALLBACK(_adg_real_arrange), NULL, NULL);
273 _adg_signals[ARRANGE] =
274 g_signal_newv("arrange", ADG_TYPE_ENTITY,
275 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
276 adg_marshal_VOID__VOID,
277 G_TYPE_NONE, 0, param_types);
280 * AdgEntity::render:
281 * @entity: an #AdgEntity
282 * @cr: a #cairo_t drawing context
284 * Causes the rendering of @entity on @cr. A render signal will
285 * automatically emit #AdgEntity::arrange just before the real
286 * rendering on the cairo context.
288 * Since: 1.0
290 closure = g_cclosure_new(G_CALLBACK(_adg_real_render), NULL, NULL);
291 param_types[0] = G_TYPE_POINTER;
292 _adg_signals[RENDER] =
293 g_signal_newv("render", ADG_TYPE_ENTITY,
294 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
295 adg_marshal_VOID__POINTER,
296 G_TYPE_NONE, 1, param_types);
299 static void
300 adg_entity_init(AdgEntity *entity)
302 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
303 ADG_TYPE_ENTITY,
304 AdgEntityPrivate);
305 data->parent = NULL;
306 cairo_matrix_init_identity(&data->global_map);
307 cairo_matrix_init_identity(&data->local_map);
308 data->local_method = ADG_MIX_ANCESTORS;
309 data->hash_styles = NULL;
310 data->global.is_defined = FALSE;
311 adg_matrix_copy(&data->global.matrix, adg_matrix_null());
312 data->local.is_defined = FALSE;
313 adg_matrix_copy(&data->local.matrix, adg_matrix_null());
314 data->extents.is_defined = FALSE;
316 entity->data = data;
319 static void
320 _adg_dispose(GObject *object)
322 AdgEntity *entity;
323 AdgEntityPrivate *data;
325 entity = (AdgEntity *) object;
326 data = entity->data;
328 /* This call will emit a "notify" signal for parent.
329 * Consequentially, the references to the old parent is dropped. */
330 adg_entity_set_parent(entity, NULL);
332 if (data->hash_styles != NULL) {
333 g_hash_table_destroy(data->hash_styles);
334 data->hash_styles = NULL;
337 if (_ADG_OLD_OBJECT_CLASS->dispose)
338 _ADG_OLD_OBJECT_CLASS->dispose(object);
341 static void
342 _adg_get_property(GObject *object, guint prop_id,
343 GValue *value, GParamSpec *pspec)
345 AdgEntity *entity;
346 AdgEntityPrivate *data;
348 entity = (AdgEntity *) object;
349 data = entity->data;
351 switch (prop_id) {
352 case PROP_PARENT:
353 g_value_set_object(value, data->parent);
354 break;
355 case PROP_GLOBAL_MAP:
356 g_value_set_boxed(value, &data->global_map);
357 break;
358 case PROP_LOCAL_MAP:
359 g_value_set_boxed(value, &data->local_map);
360 break;
361 case PROP_LOCAL_METHOD:
362 g_value_set_enum(value, data->local_method);
363 break;
364 default:
365 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
366 break;
370 static void
371 _adg_set_property(GObject *object, guint prop_id,
372 const GValue *value, GParamSpec *pspec)
374 AdgEntityPrivate *data = ((AdgEntity *) object)->data;
376 switch (prop_id) {
377 case PROP_PARENT:
378 _adg_set_parent((AdgEntity *) object,
379 (AdgEntity *) g_value_get_object(value));
380 break;
381 case PROP_GLOBAL_MAP:
382 adg_matrix_copy(&data->global_map, g_value_get_boxed(value));
383 data->global.is_defined = FALSE;
384 break;
385 case PROP_LOCAL_MAP:
386 adg_matrix_copy(&data->local_map, g_value_get_boxed(value));
387 data->local.is_defined = FALSE;
388 break;
389 case PROP_LOCAL_METHOD:
390 data->local_method = g_value_get_enum(value);
391 g_signal_emit(object, _adg_signals[LOCAL_CHANGED], 0);
392 break;
393 default:
394 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
395 break;
401 * adg_switch_extents:
402 * @state: new extents state
404 * Strokes (if @state is %TRUE) a rectangle around every entity to
405 * show their extents. Useful for debugging purposes.
407 * Since: 1.0
409 void
410 adg_switch_extents(gboolean state)
412 _adg_show_extents = state;
416 * adg_entity_get_canvas:
417 * @entity: an #AdgEntity
419 * Walks on the @entity hierarchy and gets the first parent of @entity,
420 * that is the first #AdgCanvas instance. The returned object is
421 * owned by @entity and should not be freed or modified.
423 * Returns: (transfer none): the requested canvas or %NULL on errors
424 * or if there is no #AdgCanvas in the
425 * @entity hierarchy.
427 * Since: 1.0
429 AdgCanvas *
430 adg_entity_get_canvas(AdgEntity *entity)
432 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
434 while (entity) {
435 if (ADG_IS_CANVAS(entity))
436 return (AdgCanvas *) entity;
438 entity = adg_entity_get_parent(entity);
441 return NULL;
445 * adg_entity_set_parent:
446 * @entity: an #AdgEntity
447 * @parent: the parent entity
449 * <note><para>
450 * This function is only useful in entity implementations.
451 * </para></note>
453 * Sets a new parent on @entity.
455 * Since: 1.0
457 void
458 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
460 g_return_if_fail(ADG_IS_ENTITY(entity));
461 g_object_set(entity, "parent", parent, NULL);
465 * adg_entity_get_parent:
466 * @entity: an #AdgEntity
468 * Gets the parent of @entity. The returned object is owned
469 * by @entity and should not be freed or modified.
471 * Returns: (transfer none): the parent entity or %NULL on errors
472 * or if @entity is a toplevel.
474 * Since: 1.0
476 AdgEntity *
477 adg_entity_get_parent(AdgEntity *entity)
479 AdgEntityPrivate *data;
481 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
483 data = entity->data;
485 return data->parent;
489 * adg_entity_set_global_map:
490 * @entity: an #AdgEntity object
491 * @map: the new map
493 * Sets the new global transformation of @entity to @map:
494 * the old map is discarded. If @map is %NULL, the global
495 * map is left unchanged.
497 * Since: 1.0
499 void
500 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
502 g_return_if_fail(ADG_IS_ENTITY(entity));
503 g_object_set(entity, "global-map", map, NULL);
507 * adg_entity_transform_global_map:
508 * @entity: an #AdgEntity object
509 * @transformation: the transformation to apply
510 * @mode: how @transformation should be applied
512 * Convenient function to change the global map of @entity by
513 * applying @tranformation using the @mode operator. This is
514 * logically equivalent to the following:
516 * |[
517 * AdgMatrix map;
518 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
519 * adg_matrix_transform(&map, transformation, mode);
520 * adg_entity_set_global_map(entity, &map);
521 * ]|
523 * Since: 1.0
525 void
526 adg_entity_transform_global_map(AdgEntity *entity,
527 const AdgMatrix *transformation,
528 AdgTransformMode mode)
530 AdgEntityPrivate *data;
531 AdgMatrix map;
533 g_return_if_fail(ADG_IS_ENTITY(entity));
534 g_return_if_fail(transformation != NULL);
536 data = entity->data;
538 adg_matrix_copy(&map, &data->global_map);
539 adg_matrix_transform(&map, transformation, mode);
541 g_object_set(entity, "global-map", &map, NULL);
545 * adg_entity_get_global_map:
546 * @entity: an #AdgEntity object
548 * Gets the transformation to be used to compute the global matrix
549 * of @entity.
551 * Returns: the requested map or %NULL on errors
553 * Since: 1.0
555 const AdgMatrix *
556 adg_entity_get_global_map(AdgEntity *entity)
558 AdgEntityPrivate *data;
560 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
562 data = entity->data;
564 return &data->global_map;
568 * adg_entity_get_global_matrix:
569 * @entity: an #AdgEntity object
571 * Gets the current global matrix of @entity. The returned value
572 * is owned by @entity and should not be changed or freed.
574 * The global matrix is computed in the arrange() phase by
575 * combining all the global maps of the @entity hierarchy using
576 * the %ADG_MIX_ANCESTORS method.
578 * Returns: the global matrix or %NULL on errors
580 * Since: 1.0
582 const AdgMatrix *
583 adg_entity_get_global_matrix(AdgEntity *entity)
585 AdgEntityPrivate *data;
587 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
589 data = entity->data;
591 return &data->global.matrix;
595 * adg_entity_set_local_map:
596 * @entity: an #AdgEntity object
597 * @map: the new map
599 * Sets the new local transformation of @entity to @map:
600 * the old map is discarded. If @map is %NULL, the local
601 * map is left unchanged.
603 * Since: 1.0
605 void
606 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
608 g_return_if_fail(ADG_IS_ENTITY(entity));
609 g_object_set(entity, "local-map", map, NULL);
613 * adg_entity_transform_local_map:
614 * @entity: an #AdgEntity object
615 * @transformation: the transformation to apply
616 * @mode: how @transformation should be applied
618 * Convenient function to change the local map of @entity by
619 * applying @tranformation using the @mode operator. This is
620 * logically equivalent to the following:
622 * |[
623 * AdgMatrix map;
624 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
625 * adg_matrix_transform(&map, transformation, mode);
626 * adg_entity_set_local_map(entity, &map);
627 * ]|
629 * Since: 1.0
631 void
632 adg_entity_transform_local_map(AdgEntity *entity,
633 const AdgMatrix *transformation,
634 AdgTransformMode mode)
636 AdgEntityPrivate *data;
637 AdgMatrix map;
639 g_return_if_fail(ADG_IS_ENTITY(entity));
640 g_return_if_fail(transformation != NULL);
642 data = entity->data;
644 adg_matrix_copy(&map, &data->local_map);
645 adg_matrix_transform(&map, transformation, mode);
646 g_object_set(entity, "local-map", &map, NULL);
650 * adg_entity_get_local_map:
651 * @entity: an #AdgEntity object
653 * Gets the transformation to be used to compute the local matrix
654 * of @entity and store it in @map.
656 * Returns: the requested map or %NULL on errors
658 * Since: 1.0
660 const AdgMatrix *
661 adg_entity_get_local_map(AdgEntity *entity)
663 AdgEntityPrivate *data;
665 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
667 data = entity->data;
669 return &data->local_map;
673 * adg_entity_get_local_matrix:
674 * @entity: an #AdgEntity object
676 * Gets the current local matrix of @entity. The returned value
677 * is owned by @entity and should not be changed or freed.
679 * The local matrix is computed in the arrange() phase by
680 * combining all the local maps of the @entity hierarchy using
681 * the method specified by the #AdgEntity:local-method property.
683 * Returns: the local matrix or %NULL on errors
685 * Since: 1.0
687 const AdgMatrix *
688 adg_entity_get_local_matrix(AdgEntity *entity)
690 AdgEntityPrivate *data;
692 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
694 data = entity->data;
696 return &data->local.matrix;
700 * adg_entity_set_local_method:
701 * @entity: an #AdgEntity object
702 * @local_method: new method
704 * Sets a new local mix method on @entity. The
705 * #AdgEntity:local-method property defines how the local
706 * matrix must be computed: check out the #AdgMixMethod
707 * documentation to know what are the availables methods
708 * and how they affect the local matrix computation.
710 * Setting a different local method emits an #Adgentity::local-changed
711 * signal on @entity.
713 * Since: 1.0
715 void
716 adg_entity_set_local_method(AdgEntity *entity, AdgMixMethod local_method)
718 g_return_if_fail(ADG_IS_ENTITY(entity));
719 g_object_set(entity, "local-method", local_method, NULL);
723 * adg_entity_get_local_method:
724 * @entity: an #AdgEntity object
726 * Gets the local mix method of @entity. Check out the
727 * adg_entity_set_local_method() documentation to know what the
728 * local method is used for.
730 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
732 * Since: 1.0
734 AdgMixMethod
735 adg_entity_get_local_method(AdgEntity *entity)
737 AdgEntityPrivate *data;
739 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
741 data = entity->data;
743 return data->local_method;
747 * adg_entity_set_extents:
748 * @entity: an #AdgEntity
749 * @extents: the new extents
751 * <note><para>
752 * This function is only useful in entity implementations.
753 * </para></note>
755 * Sets a new bounding box for @entity. @extents can be %NULL,
756 * in which case the extents are unset.
758 * Since: 1.0
760 void
761 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
763 AdgEntityPrivate *data;
765 g_return_if_fail(ADG_IS_ENTITY(entity));
767 data = entity->data;
769 if (extents == NULL)
770 data->extents.is_defined = FALSE;
771 else
772 cpml_extents_copy(&data->extents, extents);
776 * adg_entity_get_extents:
777 * @entity: an #AdgEntity
779 * Gets the bounding box of @entity. The returned struct is
780 * owned by @entity and should not modified or freed.
782 * This struct specifies the surface portion (in global space
783 * of @entity) occupied by the entity without taking into
784 * account rendering properties such as line thickness or caps.
786 * The #AdgEntity::arrange signal should be emitted before
787 * this call (either explicitely trought adg_entity_arrange()
788 * or implicitely with adg_entity_render()) in order to get
789 * an up to date boundary box.
791 * Returns: the bounding box of @entity or %NULL on errors
793 * Since: 1.0
795 const CpmlExtents *
796 adg_entity_get_extents(AdgEntity *entity)
798 AdgEntityPrivate *data;
800 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
802 data = entity->data;
804 return &data->extents;
808 * adg_entity_set_style:
809 * @entity: an #AdgEntity
810 * @dress: a dress style
811 * @style: the new style to use
813 * Overrides the style of @dress for @entity and its children.
814 * If @style is %NULL, any previous override is removed.
816 * The new style must still be compatible with @dress: check out
817 * the adg_dress_style_is_compatible() documentation to know
818 * what a compatible style means.
820 * Since: 1.0
822 void
823 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
825 AdgEntityPrivate *data;
826 gpointer p_dress;
827 AdgStyle *old_style;
829 g_return_if_fail(ADG_IS_ENTITY(entity));
831 data = entity->data;
833 if (data->hash_styles == NULL && style == NULL)
834 return;
836 if (data->hash_styles == NULL)
837 data->hash_styles = g_hash_table_new_full(NULL, NULL,
838 NULL, g_object_unref);
840 p_dress = GINT_TO_POINTER(dress);
841 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
843 if (style == old_style)
844 return;
846 if (style == NULL) {
847 g_hash_table_remove(data->hash_styles, p_dress);
848 return;
851 if (!adg_dress_style_is_compatible(dress, style)) {
852 GType ancestor_type = adg_dress_get_ancestor_type(dress);
854 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
855 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
856 g_type_name(ancestor_type), adg_dress_get_name(dress));
858 return;
861 g_object_ref(style);
862 g_hash_table_replace(data->hash_styles, p_dress, style);
866 * adg_entity_get_style:
867 * @entity: an #AdgEntity
868 * @dress: the dress of the style to get
870 * Gets the overriden @dress style from @entity. This is a kind
871 * of accessor function: for rendering purpose use adg_entity_style()
872 * instead. The returned object is owned by @entity and should not be
873 * freed or modified.
875 * Returns: (transfer none): the requested style or %NULL
876 * if the @dress style is not overriden
878 * Since: 1.0
880 AdgStyle *
881 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
883 AdgEntityPrivate *data;
885 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
887 data = entity->data;
889 if (data->hash_styles == NULL)
890 return NULL;
892 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
896 * adg_entity_style:
897 * @entity: an #AdgEntity
898 * @dress: the dress of the style to get
900 * Gets the style to be used for @entity. @dress specifies which
901 * "family" of style to get.
903 * The following sequence of checks is performed to get the proper
904 * style, stopping at the first succesfull result:
906 * <orderedlist>
907 * <listitem>check if the style is directly overriden by this entity,
908 * as returned by adg_entity_get_style();</listitem>
909 * <listitem>check if @entity has a parent, in which case returns the
910 * adg_entity_style() of the parent;</listitem>
911 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
912 * </orderedlist>
914 * The returned object is owned by @entity and should not be
915 * freed or modified.
917 * Returns: (transfer none): the requested style or %NULL for
918 * transparent dresses or errors.
920 * Since: 1.0
922 AdgStyle *
923 adg_entity_style(AdgEntity *entity, AdgDress dress)
925 AdgStyle *style;
927 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
929 style = adg_entity_get_style(entity, dress);
931 if (style == NULL) {
932 AdgEntityPrivate *data = entity->data;
934 if (data->parent != NULL)
935 style = adg_entity_style(data->parent, dress);
936 else
937 style = adg_dress_get_fallback(dress);
940 return style;
944 * adg_entity_apply_dress:
945 * @entity: an #AdgEntity
946 * @dress: the dress style to apply
947 * @cr: a #cairo_t drawing context
949 * Convenient function to apply a @dress style (as returned by
950 * adg_entity_style()) to the @cr cairo context.
952 * Since: 1.0
954 void
955 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
957 AdgStyle *style;
959 g_return_if_fail(ADG_IS_ENTITY(entity));
960 g_return_if_fail(cr != NULL);
962 style = adg_entity_style(entity, dress);
964 if (style != NULL)
965 adg_style_apply(style, entity, cr);
969 * adg_entity_global_changed:
970 * @entity: an #AdgEntity
972 * Emits the #AdgEntity::global-changed signal on @entity and on all of
973 * its children, if any.
975 * Since: 1.0
977 void
978 adg_entity_global_changed(AdgEntity *entity)
980 g_return_if_fail(ADG_IS_ENTITY(entity));
982 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
986 * adg_entity_local_changed:
987 * @entity: an #AdgEntity
989 * Emits the #AdgEntity::local-changed signal on @entity and on all of
990 * its children, if any.
992 * Since: 1.0
994 void
995 adg_entity_local_changed(AdgEntity *entity)
997 g_return_if_fail(ADG_IS_ENTITY(entity));
999 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1003 * adg_entity_invalidate:
1004 * @entity: an #AdgEntity
1006 * Emits the #AdgEntity::invalidate signal on @entity and on all of
1007 * its children, if any, clearing the eventual cache stored by the
1008 * #AdgEntity::arrange signal and setting the entity state similary
1009 * to the just initialized entity.
1011 * Since: 1.0
1013 void
1014 adg_entity_invalidate(AdgEntity *entity)
1016 g_return_if_fail(ADG_IS_ENTITY(entity));
1018 g_signal_emit(entity, _adg_signals[INVALIDATE], 0);
1022 * adg_entity_arrange:
1023 * @entity: an #AdgEntity
1025 * Emits the #AdgEntity::arrange signal on @entity and all its children,
1026 * if any. The arrange call is implicitely called by the
1027 * #AdgEntity::render signal but not by adg_entity_get_extents().
1029 * Since: 1.0
1031 void
1032 adg_entity_arrange(AdgEntity *entity)
1034 g_return_if_fail(ADG_IS_ENTITY(entity));
1036 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1040 * adg_entity_render:
1041 * @entity: an #AdgEntity
1042 * @cr: a #cairo_t drawing context
1044 * Emits the #AdgEntity::render signal on @entity and on all of its
1045 * children, if any, causing the rendering to the @cr cairo context.
1047 * Since: 1.0
1049 void
1050 adg_entity_render(AdgEntity *entity, cairo_t *cr)
1052 g_return_if_fail(ADG_IS_ENTITY(entity));
1054 g_signal_emit(entity, _adg_signals[RENDER], 0, cr);
1058 * adg_entity_point:
1059 * @entity: an #AdgEntity
1060 * @point: the #AdgPoint to define
1061 * @new_point: the new #AdgPoint value
1063 * <note><para>
1064 * This function is only useful in entity implementations.
1065 * </para></note>
1067 * A convenient method to set an #AdgPoint owned by @entity.
1068 * @point is the old value while @new_point is the new value. It
1069 * can be used for setting #AdgPoint in the private data, such as:
1071 * |[
1072 * data->point = adg_entity_point(entity, data->point, new_point);
1073 * ]|
1075 * This function takes care of the dependencies between @entity and
1076 * the eventual models bound to the old and new points.
1078 * @point can be %NULL, in which case a clone of @new_point will be
1079 * returned. Also @new_point can be %NULL, in which case @point is
1080 * destroyed and %NULL will be returned.
1082 * Returns: the new properly defined point
1084 * Since: 1.0
1086 AdgPoint *
1087 adg_entity_point(AdgEntity *entity, AdgPoint *point, AdgPoint *new_point)
1089 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
1091 if (!adg_point_equal(point, new_point)) {
1092 AdgModel *old_model, *new_model;
1094 old_model = point ? adg_point_get_model(point) : NULL;
1095 new_model = new_point ? adg_point_get_model(new_point) : NULL;
1097 if (new_model != old_model) {
1098 if (new_model)
1099 adg_model_add_dependency(new_model, entity);
1100 if (old_model)
1101 adg_model_remove_dependency(old_model, entity);
1104 if (new_point && point) {
1105 adg_point_copy(point, new_point);
1106 } else if (new_point) {
1107 point = adg_point_dup(new_point);
1108 } else {
1109 adg_point_destroy(point);
1110 point = NULL;
1114 return point;
1118 static void
1119 _adg_set_parent(AdgEntity *entity, AdgEntity *parent)
1121 AdgEntityPrivate *data;
1122 AdgEntity *old_parent;
1124 data = entity->data;
1125 old_parent = data->parent;
1127 if (parent)
1128 g_object_ref(parent);
1130 data->parent = parent;
1131 data->global.is_defined = FALSE;
1132 data->local.is_defined = FALSE;
1134 g_signal_emit(entity, _adg_signals[PARENT_SET], 0, old_parent);
1136 if (old_parent)
1137 g_object_unref(old_parent);
1140 static void
1141 _adg_global_changed(AdgEntity *entity)
1143 AdgEntityPrivate *data;
1144 const AdgMatrix *map;
1145 AdgMatrix *matrix;
1147 data = entity->data;
1148 map = &data->global_map;
1149 matrix = &data->global.matrix;
1151 if (data->parent) {
1152 adg_matrix_copy(matrix, adg_entity_get_global_matrix(data->parent));
1153 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1154 } else {
1155 adg_matrix_copy(matrix, map);
1159 static void
1160 _adg_local_changed(AdgEntity *entity)
1162 AdgEntityPrivate *data;
1163 const AdgMatrix *map;
1164 AdgMatrix *matrix;
1166 data = entity->data;
1167 map = &data->local_map;
1168 matrix = &data->local.matrix;
1170 switch (data->local_method) {
1171 case ADG_MIX_DISABLED:
1172 adg_matrix_copy(matrix, adg_matrix_identity());
1173 break;
1174 case ADG_MIX_NONE:
1175 adg_matrix_copy(matrix, map);
1176 break;
1177 case ADG_MIX_ANCESTORS:
1178 if (data->parent) {
1179 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1180 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1181 } else {
1182 adg_matrix_copy(matrix, map);
1184 break;
1185 case ADG_MIX_ANCESTORS_NORMALIZED:
1186 if (data->parent) {
1187 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1188 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1189 } else {
1190 adg_matrix_copy(matrix, map);
1192 adg_matrix_normalize(matrix);
1193 break;
1194 case ADG_MIX_PARENT:
1195 if (data->parent) {
1196 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1197 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1198 } else {
1199 adg_matrix_copy(matrix, map);
1201 break;
1202 case ADG_MIX_PARENT_NORMALIZED:
1203 if (data->parent) {
1204 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1205 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1206 } else {
1207 adg_matrix_copy(matrix, map);
1209 adg_matrix_normalize(matrix);
1210 break;
1211 case ADG_MIX_UNDEFINED:
1212 g_warning(_("%s: requested to mix the maps using an undefined method"),
1213 G_STRLOC);
1214 break;
1215 default:
1216 g_return_if_reached();
1217 break;
1221 static void
1222 _adg_real_invalidate(AdgEntity *entity)
1224 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1225 AdgEntityPrivate *data = entity->data;
1227 /* Do not raise any warning if invalidate() is not defined,
1228 * assuming entity does not have additional cache to be cleared */
1229 if (klass->invalidate)
1230 klass->invalidate(entity);
1232 data->extents.is_defined = FALSE;
1235 static void
1236 _adg_real_arrange(AdgEntity *entity)
1238 AdgEntityClass *klass;
1239 AdgEntityPrivate *data;
1241 klass = ADG_ENTITY_GET_CLASS(entity);
1242 data = entity->data;
1244 /* Update the global matrix, if required */
1245 if (!data->global.is_defined) {
1246 data->global.is_defined = TRUE;
1247 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1250 /* Update the local matrix, if required */
1251 if (!data->local.is_defined) {
1252 data->local.is_defined = TRUE;
1253 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1256 /* The arrange() method must be defined */
1257 if (klass->arrange == NULL) {
1258 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1259 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1260 data->extents.is_defined = FALSE;
1261 return;
1264 klass->arrange(entity);
1267 static void
1268 _adg_real_render(AdgEntity *entity, cairo_t *cr)
1270 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1272 /* The render method must be defined */
1273 if (klass->render == NULL) {
1274 g_warning(_("%s: `render' method not implemented for type `%s'"),
1275 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1276 return;
1279 /* Before the rendering, the entity should be arranged */
1280 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1282 cairo_save(cr);
1283 klass->render(entity, cr);
1284 cairo_restore(cr);
1286 if (_adg_show_extents) {
1287 AdgEntityPrivate *data = entity->data;
1288 CpmlExtents *extents = &data->extents;
1290 if (extents->is_defined) {
1291 cairo_save(cr);
1292 cairo_set_source_rgba(cr, 0.15, 0.15, 0.15, 0.15);
1293 cairo_rectangle(cr, extents->org.x, extents->org.y,
1294 extents->size.x, extents->size.y);
1295 cairo_fill(cr);
1296 cairo_restore(cr);