AdgEntity: added "destroy" signal
[adg.git] / src / adg / adg-entity.c
blob7be13d030af7b552aed2cd7117a779f263dff825
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 * @destroy: when a destroy request has been explicitely requested
47 * @parent_set: called whenever the parent of an entity has changed
48 * @global_changed: the global matrix has been invalidated
49 * @local_changed: the local matrix has been invalidated
50 * @invalidate: invalidating callback, used to clear the internal cache
51 * @arrange: prepare the layout and fill the extents struct
52 * @render: rendering callback, it must be implemented by every entity
54 * Any entity (if not abstract) must implement at least the @render method.
55 * The other signal handlers can be overriden to provide custom behaviors
56 * and usually must chain up the original handler.
58 * Since: 1.0
59 **/
61 /**
62 * AdgEntityCallback:
63 * @entity: an #AdgEntity
64 * @user_data: a general purpose pointer
66 * Callback used when inspecting or browsing entities. For example,
67 * it is passed to adg_model_foreach_dependency() to perform an
68 * operation on all the entities depending on an #AdgModel.
70 * Since: 1.0
71 **/
74 #include "adg-internal.h"
75 #if GTK3_ENABLED || GTK2_ENABLED
76 #include <gtk/gtk.h>
77 #endif
79 #include "adg-container.h"
80 #include "adg-table.h"
81 #include "adg-title-block.h"
82 #include "adg-canvas.h"
83 #include "adg-dress.h"
84 #include "adg-style.h"
85 #include "adg-model.h"
86 #include "adg-point.h"
88 #include "adg-entity-private.h"
91 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
94 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED)
96 enum {
97 PROP_0,
98 PROP_PARENT,
99 PROP_GLOBAL_MAP,
100 PROP_LOCAL_MAP,
101 PROP_LOCAL_METHOD
104 enum {
105 DESTROY,
106 PARENT_SET,
107 GLOBAL_CHANGED,
108 LOCAL_CHANGED,
109 INVALIDATE,
110 ARRANGE,
111 RENDER,
112 LAST_SIGNAL
116 static void _adg_dispose (GObject *object);
117 static void _adg_get_property (GObject *object,
118 guint prop_id,
119 GValue *value,
120 GParamSpec *pspec);
121 static void _adg_set_property (GObject *object,
122 guint prop_id,
123 const GValue *value,
124 GParamSpec *pspec);
125 static void _adg_set_parent (AdgEntity *entity,
126 AdgEntity *parent);
127 static void _adg_global_changed (AdgEntity *entity);
128 static void _adg_local_changed (AdgEntity *entity);
129 static void _adg_real_invalidate (AdgEntity *entity);
130 static void _adg_real_arrange (AdgEntity *entity);
131 static void _adg_real_render (AdgEntity *entity,
132 cairo_t *cr);
133 static guint _adg_signals[LAST_SIGNAL] = { 0 };
134 static gboolean _adg_show_extents = FALSE;
137 static void
138 adg_entity_class_init(AdgEntityClass *klass)
140 GObjectClass *gobject_class;
141 GParamSpec *param;
142 GClosure *closure;
143 GType param_types[1];
145 gobject_class = (GObjectClass *) klass;
147 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
149 gobject_class->dispose = _adg_dispose;
150 gobject_class->get_property = _adg_get_property;
151 gobject_class->set_property = _adg_set_property;
153 klass->destroy = (void (*)(AdgEntity *)) g_object_unref;
154 klass->parent_set = NULL;
155 klass->global_changed = _adg_global_changed;
156 klass->local_changed = _adg_local_changed;
157 klass->invalidate = NULL;
158 klass->arrange= NULL;
159 klass->render = NULL;
161 param = g_param_spec_object("parent",
162 P_("Parent Entity"),
163 P_("The parent entity of this entity or NULL if this is a top-level entity"),
164 ADG_TYPE_ENTITY,
165 G_PARAM_READWRITE);
166 g_object_class_install_property(gobject_class, PROP_PARENT, param);
168 param = g_param_spec_boxed("global-map",
169 P_("Global Map"),
170 P_("The transformation to be combined with the parent ones to get the global matrix"),
171 ADG_TYPE_MATRIX,
172 G_PARAM_READWRITE);
173 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
175 param = g_param_spec_boxed("local-map",
176 P_("Local Map"),
177 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
178 ADG_TYPE_MATRIX,
179 G_PARAM_READWRITE);
180 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
182 param = g_param_spec_enum("local-method",
183 P_("Local Mix Method"),
184 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
185 ADG_TYPE_MIX_METHOD, ADG_MIX_ANCESTORS,
186 G_PARAM_READWRITE);
187 g_object_class_install_property(gobject_class, PROP_LOCAL_METHOD, param);
190 * AdgEntity::destroy:
191 * @entity: an #AdgEntity
193 * Emitted to explicitely destroy @entity. It unreferences
194 * @entity so that will be destroyed, unless the caller owns
195 * an additional references added with g_object_ref().
197 * In the usual case, this is equivalent of calling
198 * g_object_unref() on @entity but, for composite entities or
199 * containers, the destroy signal is propagated to the children.
201 * Since: 1.0
203 _adg_signals[DESTROY] =
204 g_signal_new("destroy",
205 G_OBJECT_CLASS_TYPE(gobject_class),
206 G_SIGNAL_RUN_FIRST,
207 G_STRUCT_OFFSET(AdgEntityClass, destroy),
208 NULL, NULL,
209 adg_marshal_VOID__VOID,
210 G_TYPE_NONE, 0);
213 * AdgEntity::parent-set:
214 * @entity: an #AdgEntity
215 * @old_parent: the old parent
217 * Emitted after the parent entity has changed. The new parent
218 * can be inspected using adg_entity_get_parent().
220 * It is allowed for both old and new parent to be %NULL.
222 * Since: 1.0
224 _adg_signals[PARENT_SET] =
225 g_signal_new("parent-set",
226 G_OBJECT_CLASS_TYPE(gobject_class),
227 G_SIGNAL_RUN_FIRST,
228 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
229 NULL, NULL,
230 adg_marshal_VOID__OBJECT,
231 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
234 * AdgEntity::global-changed
235 * @entity: an #AdgEntity
237 * Emitted when the global map of @entity or any of its parent
238 * has changed. The default handler will compute the new global
239 * matrix, updating the internal cache.
241 * Since: 1.0
243 _adg_signals[GLOBAL_CHANGED] =
244 g_signal_new("global-changed",
245 G_OBJECT_CLASS_TYPE(gobject_class),
246 G_SIGNAL_RUN_FIRST,
247 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
248 NULL, NULL,
249 adg_marshal_VOID__VOID,
250 G_TYPE_NONE, 0);
253 * AdgEntity::local-changed
254 * @entity: an #AdgEntity
256 * Emitted when the local map of @entity or any of its parent
257 * has changed. The default handler will compute the new local
258 * matrix, updating the internal cache.
260 * Since: 1.0
262 _adg_signals[LOCAL_CHANGED] =
263 g_signal_new("local-changed",
264 G_OBJECT_CLASS_TYPE(gobject_class),
265 G_SIGNAL_RUN_FIRST,
266 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
267 NULL, NULL,
268 adg_marshal_VOID__VOID,
269 G_TYPE_NONE, 0);
272 * AdgEntity::invalidate:
273 * @entity: an #AdgEntity
275 * Invalidates the whole @entity, that is resets all the cache
276 * (if present) built during the #AdgEntity::arrange signal.
277 * The resulting state is a clean entity, similar to what you
278 * have just before the first rendering.
280 * Since: 1.0
282 closure = g_cclosure_new(G_CALLBACK(_adg_real_invalidate), NULL, NULL);
283 _adg_signals[INVALIDATE] =
284 g_signal_newv("invalidate", ADG_TYPE_ENTITY,
285 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
286 adg_marshal_VOID__VOID,
287 G_TYPE_NONE, 0, param_types);
290 * AdgEntity::arrange:
291 * @entity: an #AdgEntity
293 * Arranges the layout of @entity, updating the cache if necessary,
294 * and computes the extents of @entity.
296 * Since: 1.0
298 closure = g_cclosure_new(G_CALLBACK(_adg_real_arrange), NULL, NULL);
299 _adg_signals[ARRANGE] =
300 g_signal_newv("arrange", ADG_TYPE_ENTITY,
301 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
302 adg_marshal_VOID__VOID,
303 G_TYPE_NONE, 0, param_types);
306 * AdgEntity::render:
307 * @entity: an #AdgEntity
308 * @cr: a #cairo_t drawing context
310 * Causes the rendering of @entity on @cr. A render signal will
311 * automatically emit #AdgEntity::arrange just before the real
312 * rendering on the cairo context.
314 * Since: 1.0
316 closure = g_cclosure_new(G_CALLBACK(_adg_real_render), NULL, NULL);
317 param_types[0] = G_TYPE_POINTER;
318 _adg_signals[RENDER] =
319 g_signal_newv("render", ADG_TYPE_ENTITY,
320 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
321 adg_marshal_VOID__POINTER,
322 G_TYPE_NONE, 1, param_types);
325 static void
326 adg_entity_init(AdgEntity *entity)
328 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
329 ADG_TYPE_ENTITY,
330 AdgEntityPrivate);
331 data->parent = NULL;
332 cairo_matrix_init_identity(&data->global_map);
333 cairo_matrix_init_identity(&data->local_map);
334 data->local_method = ADG_MIX_ANCESTORS;
335 data->hash_styles = NULL;
336 data->global.is_defined = FALSE;
337 adg_matrix_copy(&data->global.matrix, adg_matrix_null());
338 data->local.is_defined = FALSE;
339 adg_matrix_copy(&data->local.matrix, adg_matrix_null());
340 data->extents.is_defined = FALSE;
342 entity->data = data;
345 static void
346 _adg_dispose(GObject *object)
348 AdgEntity *entity;
349 AdgEntityPrivate *data;
351 entity = (AdgEntity *) object;
352 data = entity->data;
354 /* This call will emit a "notify" signal for parent.
355 * Consequentially, the references to the old parent is dropped. */
356 adg_entity_set_parent(entity, NULL);
358 if (data->hash_styles != NULL) {
359 g_hash_table_destroy(data->hash_styles);
360 data->hash_styles = NULL;
363 if (_ADG_OLD_OBJECT_CLASS->dispose)
364 _ADG_OLD_OBJECT_CLASS->dispose(object);
367 static void
368 _adg_get_property(GObject *object, guint prop_id,
369 GValue *value, GParamSpec *pspec)
371 AdgEntity *entity;
372 AdgEntityPrivate *data;
374 entity = (AdgEntity *) object;
375 data = entity->data;
377 switch (prop_id) {
378 case PROP_PARENT:
379 g_value_set_object(value, data->parent);
380 break;
381 case PROP_GLOBAL_MAP:
382 g_value_set_boxed(value, &data->global_map);
383 break;
384 case PROP_LOCAL_MAP:
385 g_value_set_boxed(value, &data->local_map);
386 break;
387 case PROP_LOCAL_METHOD:
388 g_value_set_enum(value, data->local_method);
389 break;
390 default:
391 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
392 break;
396 static void
397 _adg_set_property(GObject *object, guint prop_id,
398 const GValue *value, GParamSpec *pspec)
400 AdgEntityPrivate *data = ((AdgEntity *) object)->data;
402 switch (prop_id) {
403 case PROP_PARENT:
404 _adg_set_parent((AdgEntity *) object,
405 (AdgEntity *) g_value_get_object(value));
406 break;
407 case PROP_GLOBAL_MAP:
408 adg_matrix_copy(&data->global_map, g_value_get_boxed(value));
409 data->global.is_defined = FALSE;
410 break;
411 case PROP_LOCAL_MAP:
412 adg_matrix_copy(&data->local_map, g_value_get_boxed(value));
413 data->local.is_defined = FALSE;
414 break;
415 case PROP_LOCAL_METHOD:
416 data->local_method = g_value_get_enum(value);
417 g_signal_emit(object, _adg_signals[LOCAL_CHANGED], 0);
418 break;
419 default:
420 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
421 break;
427 * adg_switch_extents:
428 * @state: new extents state
430 * Strokes (if @state is %TRUE) a rectangle around every entity to
431 * show their extents. Useful for debugging purposes.
433 * Since: 1.0
435 void
436 adg_switch_extents(gboolean state)
438 _adg_show_extents = state;
442 * adg_entity_destroy:
443 * @entity: an #AdgEntity
445 * Emits the #AdgEntity::destroy signal on @entity and on all of
446 * its children, if any.
448 * Since: 1.0
450 void
451 adg_entity_destroy(AdgEntity *entity)
453 g_return_if_fail(ADG_IS_ENTITY(entity));
455 g_signal_emit(entity, _adg_signals[DESTROY], 0);
459 * adg_entity_get_canvas:
460 * @entity: an #AdgEntity
462 * Walks on the @entity hierarchy and gets the first parent of @entity,
463 * that is the first #AdgCanvas instance. The returned object is
464 * owned by @entity and should not be freed or modified.
466 * Returns: (transfer none): the requested canvas or %NULL on errors
467 * or if there is no #AdgCanvas in the
468 * @entity hierarchy.
470 * Since: 1.0
472 AdgCanvas *
473 adg_entity_get_canvas(AdgEntity *entity)
475 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
477 while (entity) {
478 if (ADG_IS_CANVAS(entity))
479 return (AdgCanvas *) entity;
481 entity = adg_entity_get_parent(entity);
484 return NULL;
488 * adg_entity_set_parent:
489 * @entity: an #AdgEntity
490 * @parent: the parent entity
492 * <note><para>
493 * This function is only useful in entity implementations.
494 * </para></note>
496 * Sets a new parent on @entity.
498 * Since: 1.0
500 void
501 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
503 g_return_if_fail(ADG_IS_ENTITY(entity));
504 g_object_set(entity, "parent", parent, NULL);
508 * adg_entity_get_parent:
509 * @entity: an #AdgEntity
511 * Gets the parent of @entity. The returned object is owned
512 * by @entity and should not be freed or modified.
514 * Returns: (transfer none): the parent entity or %NULL on errors
515 * or if @entity is a toplevel.
517 * Since: 1.0
519 AdgEntity *
520 adg_entity_get_parent(AdgEntity *entity)
522 AdgEntityPrivate *data;
524 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
526 data = entity->data;
528 return data->parent;
532 * adg_entity_set_global_map:
533 * @entity: an #AdgEntity object
534 * @map: the new map
536 * Sets the new global transformation of @entity to @map:
537 * the old map is discarded. If @map is %NULL, the global
538 * map is left unchanged.
540 * Since: 1.0
542 void
543 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
545 g_return_if_fail(ADG_IS_ENTITY(entity));
546 g_object_set(entity, "global-map", map, NULL);
550 * adg_entity_transform_global_map:
551 * @entity: an #AdgEntity object
552 * @transformation: the transformation to apply
553 * @mode: how @transformation should be applied
555 * Convenient function to change the global map of @entity by
556 * applying @tranformation using the @mode operator. This is
557 * logically equivalent to the following:
559 * |[
560 * AdgMatrix map;
561 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
562 * adg_matrix_transform(&map, transformation, mode);
563 * adg_entity_set_global_map(entity, &map);
564 * ]|
566 * Since: 1.0
568 void
569 adg_entity_transform_global_map(AdgEntity *entity,
570 const AdgMatrix *transformation,
571 AdgTransformMode mode)
573 AdgEntityPrivate *data;
574 AdgMatrix map;
576 g_return_if_fail(ADG_IS_ENTITY(entity));
577 g_return_if_fail(transformation != NULL);
579 data = entity->data;
581 adg_matrix_copy(&map, &data->global_map);
582 adg_matrix_transform(&map, transformation, mode);
584 g_object_set(entity, "global-map", &map, NULL);
588 * adg_entity_get_global_map:
589 * @entity: an #AdgEntity object
591 * Gets the transformation to be used to compute the global matrix
592 * of @entity.
594 * Returns: the requested map or %NULL on errors
596 * Since: 1.0
598 const AdgMatrix *
599 adg_entity_get_global_map(AdgEntity *entity)
601 AdgEntityPrivate *data;
603 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
605 data = entity->data;
607 return &data->global_map;
611 * adg_entity_get_global_matrix:
612 * @entity: an #AdgEntity object
614 * Gets the current global matrix of @entity. The returned value
615 * is owned by @entity and should not be changed or freed.
617 * The global matrix is computed in the arrange() phase by
618 * combining all the global maps of the @entity hierarchy using
619 * the %ADG_MIX_ANCESTORS method.
621 * Returns: the global matrix or %NULL on errors
623 * Since: 1.0
625 const AdgMatrix *
626 adg_entity_get_global_matrix(AdgEntity *entity)
628 AdgEntityPrivate *data;
630 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
632 data = entity->data;
634 return &data->global.matrix;
638 * adg_entity_set_local_map:
639 * @entity: an #AdgEntity object
640 * @map: the new map
642 * Sets the new local transformation of @entity to @map:
643 * the old map is discarded. If @map is %NULL, the local
644 * map is left unchanged.
646 * Since: 1.0
648 void
649 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
651 g_return_if_fail(ADG_IS_ENTITY(entity));
652 g_object_set(entity, "local-map", map, NULL);
656 * adg_entity_transform_local_map:
657 * @entity: an #AdgEntity object
658 * @transformation: the transformation to apply
659 * @mode: how @transformation should be applied
661 * Convenient function to change the local map of @entity by
662 * applying @tranformation using the @mode operator. This is
663 * logically equivalent to the following:
665 * |[
666 * AdgMatrix map;
667 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
668 * adg_matrix_transform(&map, transformation, mode);
669 * adg_entity_set_local_map(entity, &map);
670 * ]|
672 * Since: 1.0
674 void
675 adg_entity_transform_local_map(AdgEntity *entity,
676 const AdgMatrix *transformation,
677 AdgTransformMode mode)
679 AdgEntityPrivate *data;
680 AdgMatrix map;
682 g_return_if_fail(ADG_IS_ENTITY(entity));
683 g_return_if_fail(transformation != NULL);
685 data = entity->data;
687 adg_matrix_copy(&map, &data->local_map);
688 adg_matrix_transform(&map, transformation, mode);
689 g_object_set(entity, "local-map", &map, NULL);
693 * adg_entity_get_local_map:
694 * @entity: an #AdgEntity object
696 * Gets the transformation to be used to compute the local matrix
697 * of @entity and store it in @map.
699 * Returns: the requested map or %NULL on errors
701 * Since: 1.0
703 const AdgMatrix *
704 adg_entity_get_local_map(AdgEntity *entity)
706 AdgEntityPrivate *data;
708 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
710 data = entity->data;
712 return &data->local_map;
716 * adg_entity_get_local_matrix:
717 * @entity: an #AdgEntity object
719 * Gets the current local matrix of @entity. The returned value
720 * is owned by @entity and should not be changed or freed.
722 * The local matrix is computed in the arrange() phase by
723 * combining all the local maps of the @entity hierarchy using
724 * the method specified by the #AdgEntity:local-method property.
726 * Returns: the local matrix or %NULL on errors
728 * Since: 1.0
730 const AdgMatrix *
731 adg_entity_get_local_matrix(AdgEntity *entity)
733 AdgEntityPrivate *data;
735 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
737 data = entity->data;
739 return &data->local.matrix;
743 * adg_entity_set_local_method:
744 * @entity: an #AdgEntity object
745 * @local_method: new method
747 * Sets a new local mix method on @entity. The
748 * #AdgEntity:local-method property defines how the local
749 * matrix must be computed: check out the #AdgMixMethod
750 * documentation to know what are the availables methods
751 * and how they affect the local matrix computation.
753 * Setting a different local method emits an #Adgentity::local-changed
754 * signal on @entity.
756 * Since: 1.0
758 void
759 adg_entity_set_local_method(AdgEntity *entity, AdgMixMethod local_method)
761 g_return_if_fail(ADG_IS_ENTITY(entity));
762 g_object_set(entity, "local-method", local_method, NULL);
766 * adg_entity_get_local_method:
767 * @entity: an #AdgEntity object
769 * Gets the local mix method of @entity. Check out the
770 * adg_entity_set_local_method() documentation to know what the
771 * local method is used for.
773 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
775 * Since: 1.0
777 AdgMixMethod
778 adg_entity_get_local_method(AdgEntity *entity)
780 AdgEntityPrivate *data;
782 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
784 data = entity->data;
786 return data->local_method;
790 * adg_entity_set_extents:
791 * @entity: an #AdgEntity
792 * @extents: the new extents
794 * <note><para>
795 * This function is only useful in entity implementations.
796 * </para></note>
798 * Sets a new bounding box for @entity. @extents can be %NULL,
799 * in which case the extents are unset.
801 * Since: 1.0
803 void
804 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
806 AdgEntityPrivate *data;
808 g_return_if_fail(ADG_IS_ENTITY(entity));
810 data = entity->data;
812 if (extents == NULL)
813 data->extents.is_defined = FALSE;
814 else
815 cpml_extents_copy(&data->extents, extents);
819 * adg_entity_get_extents:
820 * @entity: an #AdgEntity
822 * Gets the bounding box of @entity. The returned struct is
823 * owned by @entity and should not modified or freed.
825 * This struct specifies the surface portion (in global space
826 * of @entity) occupied by the entity without taking into
827 * account rendering properties such as line thickness or caps.
829 * The #AdgEntity::arrange signal should be emitted before
830 * this call (either explicitely trought adg_entity_arrange()
831 * or implicitely with adg_entity_render()) in order to get
832 * an up to date boundary box.
834 * Returns: the bounding box of @entity or %NULL on errors
836 * Since: 1.0
838 const CpmlExtents *
839 adg_entity_get_extents(AdgEntity *entity)
841 AdgEntityPrivate *data;
843 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
845 data = entity->data;
847 return &data->extents;
851 * adg_entity_set_style:
852 * @entity: an #AdgEntity
853 * @dress: a dress style
854 * @style: the new style to use
856 * Overrides the style of @dress for @entity and its children.
857 * If @style is %NULL, any previous override is removed.
859 * The new style must still be compatible with @dress: check out
860 * the adg_dress_style_is_compatible() documentation to know
861 * what a compatible style means.
863 * Since: 1.0
865 void
866 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
868 AdgEntityPrivate *data;
869 gpointer p_dress;
870 AdgStyle *old_style;
872 g_return_if_fail(ADG_IS_ENTITY(entity));
874 data = entity->data;
876 if (data->hash_styles == NULL && style == NULL)
877 return;
879 if (data->hash_styles == NULL)
880 data->hash_styles = g_hash_table_new_full(NULL, NULL,
881 NULL, g_object_unref);
883 p_dress = GINT_TO_POINTER(dress);
884 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
886 if (style == old_style)
887 return;
889 if (style == NULL) {
890 g_hash_table_remove(data->hash_styles, p_dress);
891 return;
894 if (!adg_dress_style_is_compatible(dress, style)) {
895 GType ancestor_type = adg_dress_get_ancestor_type(dress);
897 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
898 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
899 g_type_name(ancestor_type), adg_dress_get_name(dress));
901 return;
904 g_object_ref(style);
905 g_hash_table_replace(data->hash_styles, p_dress, style);
909 * adg_entity_get_style:
910 * @entity: an #AdgEntity
911 * @dress: the dress of the style to get
913 * Gets the overriden @dress style from @entity. This is a kind
914 * of accessor function: for rendering purpose use adg_entity_style()
915 * instead. The returned object is owned by @entity and should not be
916 * freed or modified.
918 * Returns: (transfer none): the requested style or %NULL
919 * if the @dress style is not overriden
921 * Since: 1.0
923 AdgStyle *
924 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
926 AdgEntityPrivate *data;
928 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
930 data = entity->data;
932 if (data->hash_styles == NULL)
933 return NULL;
935 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
939 * adg_entity_style:
940 * @entity: an #AdgEntity
941 * @dress: the dress of the style to get
943 * Gets the style to be used for @entity. @dress specifies which
944 * "family" of style to get.
946 * The following sequence of checks is performed to get the proper
947 * style, stopping at the first succesfull result:
949 * <orderedlist>
950 * <listitem>check if the style is directly overriden by this entity,
951 * as returned by adg_entity_get_style();</listitem>
952 * <listitem>check if @entity has a parent, in which case returns the
953 * adg_entity_style() of the parent;</listitem>
954 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
955 * </orderedlist>
957 * The returned object is owned by @entity and should not be
958 * freed or modified.
960 * Returns: (transfer none): the requested style or %NULL for
961 * transparent dresses or errors.
963 * Since: 1.0
965 AdgStyle *
966 adg_entity_style(AdgEntity *entity, AdgDress dress)
968 AdgStyle *style;
970 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
972 style = adg_entity_get_style(entity, dress);
974 if (style == NULL) {
975 AdgEntityPrivate *data = entity->data;
977 if (data->parent != NULL)
978 style = adg_entity_style(data->parent, dress);
979 else
980 style = adg_dress_get_fallback(dress);
983 return style;
987 * adg_entity_apply_dress:
988 * @entity: an #AdgEntity
989 * @dress: the dress style to apply
990 * @cr: a #cairo_t drawing context
992 * Convenient function to apply a @dress style (as returned by
993 * adg_entity_style()) to the @cr cairo context.
995 * Since: 1.0
997 void
998 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
1000 AdgStyle *style;
1002 g_return_if_fail(ADG_IS_ENTITY(entity));
1003 g_return_if_fail(cr != NULL);
1005 style = adg_entity_style(entity, dress);
1007 if (style != NULL)
1008 adg_style_apply(style, entity, cr);
1012 * adg_entity_global_changed:
1013 * @entity: an #AdgEntity
1015 * Emits the #AdgEntity::global-changed signal on @entity and on all of
1016 * its children, if any.
1018 * Since: 1.0
1020 void
1021 adg_entity_global_changed(AdgEntity *entity)
1023 g_return_if_fail(ADG_IS_ENTITY(entity));
1025 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1029 * adg_entity_local_changed:
1030 * @entity: an #AdgEntity
1032 * Emits the #AdgEntity::local-changed signal on @entity and on all of
1033 * its children, if any.
1035 * Since: 1.0
1037 void
1038 adg_entity_local_changed(AdgEntity *entity)
1040 g_return_if_fail(ADG_IS_ENTITY(entity));
1042 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1046 * adg_entity_invalidate:
1047 * @entity: an #AdgEntity
1049 * Emits the #AdgEntity::invalidate signal on @entity and on all of
1050 * its children, if any, clearing the eventual cache stored by the
1051 * #AdgEntity::arrange signal and setting the entity state similary
1052 * to the just initialized entity.
1054 * Since: 1.0
1056 void
1057 adg_entity_invalidate(AdgEntity *entity)
1059 g_return_if_fail(ADG_IS_ENTITY(entity));
1061 g_signal_emit(entity, _adg_signals[INVALIDATE], 0);
1065 * adg_entity_arrange:
1066 * @entity: an #AdgEntity
1068 * Emits the #AdgEntity::arrange signal on @entity and all its children,
1069 * if any. The arrange call is implicitely called by the
1070 * #AdgEntity::render signal but not by adg_entity_get_extents().
1072 * Since: 1.0
1074 void
1075 adg_entity_arrange(AdgEntity *entity)
1077 g_return_if_fail(ADG_IS_ENTITY(entity));
1079 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1083 * adg_entity_render:
1084 * @entity: an #AdgEntity
1085 * @cr: a #cairo_t drawing context
1087 * Emits the #AdgEntity::render signal on @entity and on all of its
1088 * children, if any, causing the rendering to the @cr cairo context.
1090 * Since: 1.0
1092 void
1093 adg_entity_render(AdgEntity *entity, cairo_t *cr)
1095 g_return_if_fail(ADG_IS_ENTITY(entity));
1097 g_signal_emit(entity, _adg_signals[RENDER], 0, cr);
1101 * adg_entity_point:
1102 * @entity: an #AdgEntity
1103 * @point: the #AdgPoint to define
1104 * @new_point: the new #AdgPoint value
1106 * <note><para>
1107 * This function is only useful in entity implementations.
1108 * </para></note>
1110 * A convenient method to set an #AdgPoint owned by @entity.
1111 * @point is the old value while @new_point is the new value. It
1112 * can be used for setting #AdgPoint in the private data, such as:
1114 * |[
1115 * data->point = adg_entity_point(entity, data->point, new_point);
1116 * ]|
1118 * This function takes care of the dependencies between @entity and
1119 * the eventual models bound to the old and new points.
1121 * @point can be %NULL, in which case a clone of @new_point will be
1122 * returned. Also @new_point can be %NULL, in which case @point is
1123 * destroyed and %NULL will be returned.
1125 * Returns: the new properly defined point
1127 * Since: 1.0
1129 AdgPoint *
1130 adg_entity_point(AdgEntity *entity, AdgPoint *point, AdgPoint *new_point)
1132 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
1134 if (!adg_point_equal(point, new_point)) {
1135 AdgModel *old_model, *new_model;
1137 old_model = point ? adg_point_get_model(point) : NULL;
1138 new_model = new_point ? adg_point_get_model(new_point) : NULL;
1140 if (new_model != old_model) {
1141 if (new_model)
1142 adg_model_add_dependency(new_model, entity);
1143 if (old_model)
1144 adg_model_remove_dependency(old_model, entity);
1147 if (new_point && point) {
1148 adg_point_copy(point, new_point);
1149 } else if (new_point) {
1150 point = adg_point_dup(new_point);
1151 } else {
1152 adg_point_destroy(point);
1153 point = NULL;
1157 return point;
1161 static void
1162 _adg_set_parent(AdgEntity *entity, AdgEntity *parent)
1164 AdgEntityPrivate *data;
1165 AdgEntity *old_parent;
1167 data = entity->data;
1168 old_parent = data->parent;
1170 if (parent)
1171 g_object_ref(parent);
1173 data->parent = parent;
1174 data->global.is_defined = FALSE;
1175 data->local.is_defined = FALSE;
1177 g_signal_emit(entity, _adg_signals[PARENT_SET], 0, old_parent);
1179 if (old_parent)
1180 g_object_unref(old_parent);
1183 static void
1184 _adg_global_changed(AdgEntity *entity)
1186 AdgEntityPrivate *data;
1187 const AdgMatrix *map;
1188 AdgMatrix *matrix;
1190 data = entity->data;
1191 map = &data->global_map;
1192 matrix = &data->global.matrix;
1194 if (data->parent) {
1195 adg_matrix_copy(matrix, adg_entity_get_global_matrix(data->parent));
1196 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1197 } else {
1198 adg_matrix_copy(matrix, map);
1202 static void
1203 _adg_local_changed(AdgEntity *entity)
1205 AdgEntityPrivate *data;
1206 const AdgMatrix *map;
1207 AdgMatrix *matrix;
1209 data = entity->data;
1210 map = &data->local_map;
1211 matrix = &data->local.matrix;
1213 switch (data->local_method) {
1214 case ADG_MIX_DISABLED:
1215 adg_matrix_copy(matrix, adg_matrix_identity());
1216 break;
1217 case ADG_MIX_NONE:
1218 adg_matrix_copy(matrix, map);
1219 break;
1220 case ADG_MIX_ANCESTORS:
1221 if (data->parent) {
1222 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1223 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1224 } else {
1225 adg_matrix_copy(matrix, map);
1227 break;
1228 case ADG_MIX_ANCESTORS_NORMALIZED:
1229 if (data->parent) {
1230 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1231 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1232 } else {
1233 adg_matrix_copy(matrix, map);
1235 adg_matrix_normalize(matrix);
1236 break;
1237 case ADG_MIX_PARENT:
1238 if (data->parent) {
1239 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1240 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1241 } else {
1242 adg_matrix_copy(matrix, map);
1244 break;
1245 case ADG_MIX_PARENT_NORMALIZED:
1246 if (data->parent) {
1247 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1248 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1249 } else {
1250 adg_matrix_copy(matrix, map);
1252 adg_matrix_normalize(matrix);
1253 break;
1254 case ADG_MIX_UNDEFINED:
1255 g_warning(_("%s: requested to mix the maps using an undefined method"),
1256 G_STRLOC);
1257 break;
1258 default:
1259 g_return_if_reached();
1260 break;
1264 static void
1265 _adg_real_invalidate(AdgEntity *entity)
1267 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1268 AdgEntityPrivate *data = entity->data;
1270 /* Do not raise any warning if invalidate() is not defined,
1271 * assuming entity does not have additional cache to be cleared */
1272 if (klass->invalidate)
1273 klass->invalidate(entity);
1275 data->extents.is_defined = FALSE;
1278 static void
1279 _adg_real_arrange(AdgEntity *entity)
1281 AdgEntityClass *klass;
1282 AdgEntityPrivate *data;
1284 klass = ADG_ENTITY_GET_CLASS(entity);
1285 data = entity->data;
1287 /* Update the global matrix, if required */
1288 if (!data->global.is_defined) {
1289 data->global.is_defined = TRUE;
1290 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1293 /* Update the local matrix, if required */
1294 if (!data->local.is_defined) {
1295 data->local.is_defined = TRUE;
1296 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1299 /* The arrange() method must be defined */
1300 if (klass->arrange == NULL) {
1301 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1302 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1303 data->extents.is_defined = FALSE;
1304 return;
1307 klass->arrange(entity);
1310 static void
1311 _adg_real_render(AdgEntity *entity, cairo_t *cr)
1313 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1315 /* The render method must be defined */
1316 if (klass->render == NULL) {
1317 g_warning(_("%s: `render' method not implemented for type `%s'"),
1318 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1319 return;
1322 /* Before the rendering, the entity should be arranged */
1323 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1325 cairo_save(cr);
1326 klass->render(entity, cr);
1327 cairo_restore(cr);
1329 if (_adg_show_extents) {
1330 AdgEntityPrivate *data = entity->data;
1331 CpmlExtents *extents = &data->extents;
1333 if (extents->is_defined) {
1334 cairo_save(cr);
1335 cairo_set_source_rgba(cr, 0.15, 0.15, 0.15, 0.15);
1336 cairo_rectangle(cr, extents->org.x, extents->org.y,
1337 extents->size.x, extents->size.y);
1338 cairo_fill(cr);
1339 cairo_restore(cr);