doc: updated copyright
[adg.git] / src / adg / adg-entity.c
blob8337c278e3b95cfb2f491362080a7472c5def5b7
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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"
87 #include "adg-matrix-fallback.h"
89 #include "adg-entity-private.h"
92 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
95 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED)
97 enum {
98 PROP_0,
99 PROP_PARENT,
100 PROP_GLOBAL_MAP,
101 PROP_LOCAL_MAP,
102 PROP_LOCAL_METHOD
105 enum {
106 DESTROY,
107 PARENT_SET,
108 GLOBAL_CHANGED,
109 LOCAL_CHANGED,
110 INVALIDATE,
111 ARRANGE,
112 RENDER,
113 LAST_SIGNAL
117 static void _adg_dispose (GObject *object);
118 static void _adg_get_property (GObject *object,
119 guint prop_id,
120 GValue *value,
121 GParamSpec *pspec);
122 static void _adg_set_property (GObject *object,
123 guint prop_id,
124 const GValue *value,
125 GParamSpec *pspec);
126 static void _adg_set_parent (AdgEntity *entity,
127 AdgEntity *parent);
128 static void _adg_global_changed (AdgEntity *entity);
129 static void _adg_local_changed (AdgEntity *entity);
130 static void _adg_real_invalidate (AdgEntity *entity);
131 static void _adg_real_arrange (AdgEntity *entity);
132 static void _adg_real_render (AdgEntity *entity,
133 cairo_t *cr);
134 static guint _adg_signals[LAST_SIGNAL] = { 0 };
135 static gboolean _adg_show_extents = FALSE;
138 static void
139 adg_entity_class_init(AdgEntityClass *klass)
141 GObjectClass *gobject_class;
142 GParamSpec *param;
143 GClosure *closure;
144 GType param_types[1];
146 gobject_class = (GObjectClass *) klass;
148 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
150 gobject_class->dispose = _adg_dispose;
151 gobject_class->get_property = _adg_get_property;
152 gobject_class->set_property = _adg_set_property;
154 klass->destroy = (void (*)(AdgEntity *)) g_object_unref;
155 klass->parent_set = NULL;
156 klass->global_changed = _adg_global_changed;
157 klass->local_changed = _adg_local_changed;
158 klass->invalidate = NULL;
159 klass->arrange= NULL;
160 klass->render = NULL;
162 param = g_param_spec_object("parent",
163 P_("Parent Entity"),
164 P_("The parent entity of this entity or NULL if this is a top-level entity"),
165 ADG_TYPE_ENTITY,
166 G_PARAM_READWRITE);
167 g_object_class_install_property(gobject_class, PROP_PARENT, param);
169 param = g_param_spec_boxed("global-map",
170 P_("Global Map"),
171 P_("The transformation to be combined with the parent ones to get the global matrix"),
172 CAIRO_GOBJECT_TYPE_MATRIX,
173 G_PARAM_READWRITE);
174 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
176 param = g_param_spec_boxed("local-map",
177 P_("Local Map"),
178 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
179 CAIRO_GOBJECT_TYPE_MATRIX,
180 G_PARAM_READWRITE);
181 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
183 param = g_param_spec_enum("local-method",
184 P_("Local Mix Method"),
185 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
186 ADG_TYPE_MIX_METHOD, ADG_MIX_ANCESTORS,
187 G_PARAM_READWRITE);
188 g_object_class_install_property(gobject_class, PROP_LOCAL_METHOD, param);
191 * AdgEntity::destroy:
192 * @entity: an #AdgEntity
194 * Emitted to explicitely destroy @entity. It unreferences
195 * @entity so that will be destroyed, unless the caller owns
196 * an additional references added with g_object_ref().
198 * In the usual case, this is equivalent of calling
199 * g_object_unref() on @entity but, for composite entities or
200 * containers, the destroy signal is propagated to the children.
202 * Since: 1.0
204 _adg_signals[DESTROY] =
205 g_signal_new("destroy",
206 G_OBJECT_CLASS_TYPE(gobject_class),
207 G_SIGNAL_RUN_FIRST,
208 G_STRUCT_OFFSET(AdgEntityClass, destroy),
209 NULL, NULL,
210 adg_marshal_VOID__VOID,
211 G_TYPE_NONE, 0);
214 * AdgEntity::parent-set:
215 * @entity: an #AdgEntity
216 * @old_parent: the old parent
218 * Emitted after the parent entity has changed. The new parent
219 * can be inspected using adg_entity_get_parent().
221 * It is allowed for both old and new parent to be %NULL.
223 * Since: 1.0
225 _adg_signals[PARENT_SET] =
226 g_signal_new("parent-set",
227 G_OBJECT_CLASS_TYPE(gobject_class),
228 G_SIGNAL_RUN_FIRST,
229 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
230 NULL, NULL,
231 adg_marshal_VOID__OBJECT,
232 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
235 * AdgEntity::global-changed:
236 * @entity: an #AdgEntity
238 * Emitted when the global map of @entity or any of its parent
239 * has changed. The default handler will compute the new global
240 * matrix, updating the internal cache.
242 * Since: 1.0
244 _adg_signals[GLOBAL_CHANGED] =
245 g_signal_new("global-changed",
246 G_OBJECT_CLASS_TYPE(gobject_class),
247 G_SIGNAL_RUN_FIRST,
248 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
249 NULL, NULL,
250 adg_marshal_VOID__VOID,
251 G_TYPE_NONE, 0);
254 * AdgEntity::local-changed:
255 * @entity: an #AdgEntity
257 * Emitted when the local map of @entity or any of its parent
258 * has changed. The default handler will compute the new local
259 * matrix, updating the internal cache.
261 * Since: 1.0
263 _adg_signals[LOCAL_CHANGED] =
264 g_signal_new("local-changed",
265 G_OBJECT_CLASS_TYPE(gobject_class),
266 G_SIGNAL_RUN_FIRST,
267 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
268 NULL, NULL,
269 adg_marshal_VOID__VOID,
270 G_TYPE_NONE, 0);
273 * AdgEntity::invalidate:
274 * @entity: an #AdgEntity
276 * Invalidates the whole @entity, that is resets all the cache
277 * (if present) built during the #AdgEntity::arrange signal.
278 * The resulting state is a clean entity, similar to what you
279 * have just before the first rendering.
281 * Since: 1.0
283 closure = g_cclosure_new(G_CALLBACK(_adg_real_invalidate), NULL, NULL);
284 _adg_signals[INVALIDATE] =
285 g_signal_newv("invalidate", ADG_TYPE_ENTITY,
286 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
287 adg_marshal_VOID__VOID,
288 G_TYPE_NONE, 0, param_types);
291 * AdgEntity::arrange:
292 * @entity: an #AdgEntity
294 * Arranges the layout of @entity, updating the cache if necessary,
295 * and computes the extents of @entity.
297 * Since: 1.0
299 closure = g_cclosure_new(G_CALLBACK(_adg_real_arrange), NULL, NULL);
300 _adg_signals[ARRANGE] =
301 g_signal_newv("arrange", ADG_TYPE_ENTITY,
302 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
303 adg_marshal_VOID__VOID,
304 G_TYPE_NONE, 0, param_types);
307 * AdgEntity::render:
308 * @entity: an #AdgEntity
309 * @cr: a #cairo_t drawing context
311 * Causes the rendering of @entity on @cr. A render signal will
312 * automatically emit #AdgEntity::arrange just before the real
313 * rendering on the cairo context.
315 * Since: 1.0
317 closure = g_cclosure_new(G_CALLBACK(_adg_real_render), NULL, NULL);
318 param_types[0] = G_TYPE_POINTER;
319 _adg_signals[RENDER] =
320 g_signal_newv("render", ADG_TYPE_ENTITY,
321 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
322 adg_marshal_VOID__POINTER,
323 G_TYPE_NONE, 1, param_types);
326 static void
327 adg_entity_init(AdgEntity *entity)
329 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
330 ADG_TYPE_ENTITY,
331 AdgEntityPrivate);
332 data->parent = NULL;
333 cairo_matrix_init_identity(&data->global_map);
334 cairo_matrix_init_identity(&data->local_map);
335 data->local_method = ADG_MIX_ANCESTORS;
336 data->hash_styles = NULL;
337 data->global.is_defined = FALSE;
338 adg_matrix_copy(&data->global.matrix, adg_matrix_null());
339 data->local.is_defined = FALSE;
340 adg_matrix_copy(&data->local.matrix, adg_matrix_null());
341 data->extents.is_defined = FALSE;
343 entity->data = data;
346 static void
347 _adg_dispose(GObject *object)
349 AdgEntity *entity;
350 AdgEntityPrivate *data;
352 entity = (AdgEntity *) object;
353 data = entity->data;
355 /* This call will emit a "notify" signal for parent.
356 * Consequentially, the references to the old parent is dropped. */
357 adg_entity_set_parent(entity, NULL);
359 if (data->hash_styles != NULL) {
360 g_hash_table_destroy(data->hash_styles);
361 data->hash_styles = NULL;
364 if (_ADG_OLD_OBJECT_CLASS->dispose)
365 _ADG_OLD_OBJECT_CLASS->dispose(object);
368 static void
369 _adg_get_property(GObject *object, guint prop_id,
370 GValue *value, GParamSpec *pspec)
372 AdgEntity *entity;
373 AdgEntityPrivate *data;
375 entity = (AdgEntity *) object;
376 data = entity->data;
378 switch (prop_id) {
379 case PROP_PARENT:
380 g_value_set_object(value, data->parent);
381 break;
382 case PROP_GLOBAL_MAP:
383 g_value_set_boxed(value, &data->global_map);
384 break;
385 case PROP_LOCAL_MAP:
386 g_value_set_boxed(value, &data->local_map);
387 break;
388 case PROP_LOCAL_METHOD:
389 g_value_set_enum(value, data->local_method);
390 break;
391 default:
392 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
393 break;
397 static void
398 _adg_set_property(GObject *object, guint prop_id,
399 const GValue *value, GParamSpec *pspec)
401 AdgEntityPrivate *data = ((AdgEntity *) object)->data;
403 switch (prop_id) {
404 case PROP_PARENT:
405 _adg_set_parent((AdgEntity *) object,
406 (AdgEntity *) g_value_get_object(value));
407 break;
408 case PROP_GLOBAL_MAP:
409 adg_matrix_copy(&data->global_map, g_value_get_boxed(value));
410 data->global.is_defined = FALSE;
411 break;
412 case PROP_LOCAL_MAP:
413 adg_matrix_copy(&data->local_map, g_value_get_boxed(value));
414 data->local.is_defined = FALSE;
415 break;
416 case PROP_LOCAL_METHOD:
417 data->local_method = g_value_get_enum(value);
418 g_signal_emit(object, _adg_signals[LOCAL_CHANGED], 0);
419 break;
420 default:
421 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
422 break;
428 * adg_switch_extents:
429 * @state: new extents state
431 * Strokes (if @state is %TRUE) a rectangle around every entity to
432 * show their extents. Useful for debugging purposes.
434 * Since: 1.0
436 void
437 adg_switch_extents(gboolean state)
439 _adg_show_extents = state;
443 * adg_entity_destroy:
444 * @entity: an #AdgEntity
446 * Emits the #AdgEntity::destroy signal on @entity and on all of
447 * its children, if any.
449 * Since: 1.0
451 void
452 adg_entity_destroy(AdgEntity *entity)
454 g_return_if_fail(ADG_IS_ENTITY(entity));
456 g_signal_emit(entity, _adg_signals[DESTROY], 0);
460 * adg_entity_get_canvas:
461 * @entity: an #AdgEntity
463 * Walks on the @entity hierarchy and gets the first parent of @entity,
464 * that is the first #AdgCanvas instance. The returned object is
465 * owned by @entity and should not be freed or modified.
467 * Returns: (transfer none): the requested canvas or %NULL on errors
468 * or if there is no #AdgCanvas in the
469 * @entity hierarchy.
471 * Since: 1.0
473 AdgCanvas *
474 adg_entity_get_canvas(AdgEntity *entity)
476 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
478 while (entity) {
479 if (ADG_IS_CANVAS(entity))
480 return (AdgCanvas *) entity;
482 entity = adg_entity_get_parent(entity);
485 return NULL;
489 * adg_entity_set_parent:
490 * @entity: an #AdgEntity
491 * @parent: the parent entity
493 * <note><para>
494 * This function is only useful in entity implementations.
495 * </para></note>
497 * Sets a new parent on @entity.
499 * Since: 1.0
501 void
502 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
504 g_return_if_fail(ADG_IS_ENTITY(entity));
505 g_object_set(entity, "parent", parent, NULL);
509 * adg_entity_get_parent:
510 * @entity: an #AdgEntity
512 * Gets the parent of @entity. The returned object is owned
513 * by @entity and should not be freed or modified.
515 * Returns: (transfer none): the parent entity or %NULL on errors
516 * or if @entity is a toplevel.
518 * Since: 1.0
520 AdgEntity *
521 adg_entity_get_parent(AdgEntity *entity)
523 AdgEntityPrivate *data;
525 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
527 data = entity->data;
529 return data->parent;
533 * adg_entity_set_global_map:
534 * @entity: an #AdgEntity object
535 * @map: the new map
537 * Sets the new global transformation of @entity to @map:
538 * the old map is discarded. If @map is %NULL, the global
539 * map is left unchanged.
541 * Since: 1.0
543 void
544 adg_entity_set_global_map(AdgEntity *entity, const cairo_matrix_t *map)
546 g_return_if_fail(ADG_IS_ENTITY(entity));
547 g_object_set(entity, "global-map", map, NULL);
551 * adg_entity_transform_global_map:
552 * @entity: an #AdgEntity object
553 * @transformation: the transformation to apply
554 * @mode: how @transformation should be applied
556 * Convenient function to change the global map of @entity by
557 * applying @tranformation using the @mode operator. This is
558 * logically equivalent to the following:
560 * |[
561 * cairo_matrix_t map;
562 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
563 * adg_matrix_transform(&map, transformation, mode);
564 * adg_entity_set_global_map(entity, &map);
565 * ]|
567 * Since: 1.0
569 void
570 adg_entity_transform_global_map(AdgEntity *entity,
571 const cairo_matrix_t *transformation,
572 AdgTransformMode mode)
574 AdgEntityPrivate *data;
575 cairo_matrix_t map;
577 g_return_if_fail(ADG_IS_ENTITY(entity));
578 g_return_if_fail(transformation != NULL);
580 data = entity->data;
582 adg_matrix_copy(&map, &data->global_map);
583 adg_matrix_transform(&map, transformation, mode);
585 g_object_set(entity, "global-map", &map, NULL);
589 * adg_entity_get_global_map:
590 * @entity: an #AdgEntity object
592 * Gets the transformation to be used to compute the global matrix
593 * of @entity.
595 * Returns: the requested map or %NULL on errors
597 * Since: 1.0
599 const cairo_matrix_t *
600 adg_entity_get_global_map(AdgEntity *entity)
602 AdgEntityPrivate *data;
604 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
606 data = entity->data;
608 return &data->global_map;
612 * adg_entity_get_global_matrix:
613 * @entity: an #AdgEntity object
615 * Gets the current global matrix of @entity. The returned value
616 * is owned by @entity and should not be changed or freed.
618 * The global matrix is computed in the arrange() phase by
619 * combining all the global maps of the @entity hierarchy using
620 * the %ADG_MIX_ANCESTORS method.
622 * Returns: the global matrix or %NULL on errors
624 * Since: 1.0
626 const cairo_matrix_t *
627 adg_entity_get_global_matrix(AdgEntity *entity)
629 AdgEntityPrivate *data;
631 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
633 data = entity->data;
635 return &data->global.matrix;
639 * adg_entity_set_local_map:
640 * @entity: an #AdgEntity object
641 * @map: the new map
643 * Sets the new local transformation of @entity to @map:
644 * the old map is discarded. If @map is %NULL, the local
645 * map is left unchanged.
647 * Since: 1.0
649 void
650 adg_entity_set_local_map(AdgEntity *entity, const cairo_matrix_t *map)
652 g_return_if_fail(ADG_IS_ENTITY(entity));
653 g_object_set(entity, "local-map", map, NULL);
657 * adg_entity_transform_local_map:
658 * @entity: an #AdgEntity object
659 * @transformation: the transformation to apply
660 * @mode: how @transformation should be applied
662 * Convenient function to change the local map of @entity by
663 * applying @tranformation using the @mode operator. This is
664 * logically equivalent to the following:
666 * |[
667 * cairo_matrix_t map;
668 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
669 * adg_matrix_transform(&map, transformation, mode);
670 * adg_entity_set_local_map(entity, &map);
671 * ]|
673 * Since: 1.0
675 void
676 adg_entity_transform_local_map(AdgEntity *entity,
677 const cairo_matrix_t *transformation,
678 AdgTransformMode mode)
680 AdgEntityPrivate *data;
681 cairo_matrix_t map;
683 g_return_if_fail(ADG_IS_ENTITY(entity));
684 g_return_if_fail(transformation != NULL);
686 data = entity->data;
688 adg_matrix_copy(&map, &data->local_map);
689 adg_matrix_transform(&map, transformation, mode);
690 g_object_set(entity, "local-map", &map, NULL);
694 * adg_entity_get_local_map:
695 * @entity: an #AdgEntity object
697 * Gets the transformation to be used to compute the local matrix
698 * of @entity and store it in @map.
700 * Returns: the requested map or %NULL on errors
702 * Since: 1.0
704 const cairo_matrix_t *
705 adg_entity_get_local_map(AdgEntity *entity)
707 AdgEntityPrivate *data;
709 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
711 data = entity->data;
713 return &data->local_map;
717 * adg_entity_get_local_matrix:
718 * @entity: an #AdgEntity object
720 * Gets the current local matrix of @entity. The returned value
721 * is owned by @entity and should not be changed or freed.
723 * The local matrix is computed in the arrange() phase by
724 * combining all the local maps of the @entity hierarchy using
725 * the method specified by the #AdgEntity:local-method property.
727 * Returns: the local matrix or %NULL on errors
729 * Since: 1.0
731 const cairo_matrix_t *
732 adg_entity_get_local_matrix(AdgEntity *entity)
734 AdgEntityPrivate *data;
736 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
738 data = entity->data;
740 return &data->local.matrix;
744 * adg_entity_set_local_method:
745 * @entity: an #AdgEntity object
746 * @local_method: new method
748 * Sets a new local mix method on @entity. The
749 * #AdgEntity:local-method property defines how the local
750 * matrix must be computed: check out the #AdgMixMethod
751 * documentation to know what are the availables methods
752 * and how they affect the local matrix computation.
754 * Setting a different local method emits an #Adgentity::local-changed
755 * signal on @entity.
757 * Since: 1.0
759 void
760 adg_entity_set_local_method(AdgEntity *entity, AdgMixMethod local_method)
762 g_return_if_fail(ADG_IS_ENTITY(entity));
763 g_object_set(entity, "local-method", local_method, NULL);
767 * adg_entity_get_local_method:
768 * @entity: an #AdgEntity object
770 * Gets the local mix method of @entity. Check out the
771 * adg_entity_set_local_method() documentation to know what the
772 * local method is used for.
774 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
776 * Since: 1.0
778 AdgMixMethod
779 adg_entity_get_local_method(AdgEntity *entity)
781 AdgEntityPrivate *data;
783 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
785 data = entity->data;
787 return data->local_method;
791 * adg_entity_set_extents:
792 * @entity: an #AdgEntity
793 * @extents: the new extents
795 * <note><para>
796 * This function is only useful in entity implementations.
797 * </para></note>
799 * Sets a new bounding box for @entity. @extents can be %NULL,
800 * in which case the extents are unset.
802 * Since: 1.0
804 void
805 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
807 AdgEntityPrivate *data;
809 g_return_if_fail(ADG_IS_ENTITY(entity));
811 data = entity->data;
813 if (extents == NULL)
814 data->extents.is_defined = FALSE;
815 else
816 cpml_extents_copy(&data->extents, extents);
820 * adg_entity_get_extents:
821 * @entity: an #AdgEntity
823 * Gets the bounding box of @entity. The returned struct is
824 * owned by @entity and should not modified or freed.
826 * This struct specifies the surface portion (in global space
827 * of @entity) occupied by the entity without taking into
828 * account rendering properties such as line thickness or caps.
830 * The #AdgEntity::arrange signal should be emitted before
831 * this call (either explicitely trought adg_entity_arrange()
832 * or implicitely with adg_entity_render()) in order to get
833 * an up to date boundary box.
835 * Returns: the bounding box of @entity or %NULL on errors
837 * Since: 1.0
839 const CpmlExtents *
840 adg_entity_get_extents(AdgEntity *entity)
842 AdgEntityPrivate *data;
844 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
846 data = entity->data;
848 return &data->extents;
852 * adg_entity_set_style:
853 * @entity: an #AdgEntity
854 * @dress: a dress style
855 * @style: the new style to use
857 * Overrides the style of @dress for @entity and its children.
858 * If @style is %NULL, any previous override is removed.
860 * The new style must still be compatible with @dress: check out
861 * the adg_dress_style_is_compatible() documentation to know
862 * what a compatible style means.
864 * Since: 1.0
866 void
867 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
869 AdgEntityPrivate *data;
870 gpointer p_dress;
871 AdgStyle *old_style;
873 g_return_if_fail(ADG_IS_ENTITY(entity));
875 data = entity->data;
877 if (data->hash_styles == NULL && style == NULL)
878 return;
880 if (data->hash_styles == NULL)
881 data->hash_styles = g_hash_table_new_full(NULL, NULL,
882 NULL, g_object_unref);
884 p_dress = GINT_TO_POINTER(dress);
885 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
887 if (style == old_style)
888 return;
890 if (style == NULL) {
891 g_hash_table_remove(data->hash_styles, p_dress);
892 return;
895 if (!adg_dress_style_is_compatible(dress, style)) {
896 GType ancestor_type = adg_dress_get_ancestor_type(dress);
898 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
899 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
900 g_type_name(ancestor_type), adg_dress_get_name(dress));
902 return;
905 g_object_ref(style);
906 g_hash_table_replace(data->hash_styles, p_dress, style);
910 * adg_entity_get_style:
911 * @entity: an #AdgEntity
912 * @dress: the dress of the style to get
914 * Gets the overriden @dress style from @entity. This is a kind
915 * of accessor function: for rendering purpose use adg_entity_style()
916 * instead. The returned object is owned by @entity and should not be
917 * freed or modified.
919 * Returns: (transfer none): the requested style or %NULL
920 * if the @dress style is not overriden
922 * Since: 1.0
924 AdgStyle *
925 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
927 AdgEntityPrivate *data;
929 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
931 data = entity->data;
933 if (data->hash_styles == NULL)
934 return NULL;
936 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
940 * adg_entity_style:
941 * @entity: an #AdgEntity
942 * @dress: the dress of the style to get
944 * Gets the style to be used for @entity. @dress specifies which
945 * "family" of style to get.
947 * The following sequence of checks is performed to get the proper
948 * style, stopping at the first succesfull result:
950 * <orderedlist>
951 * <listitem>check if the style is directly overriden by this entity,
952 * as returned by adg_entity_get_style();</listitem>
953 * <listitem>check if @entity has a parent, in which case returns the
954 * adg_entity_style() of the parent;</listitem>
955 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
956 * </orderedlist>
958 * The returned object is owned by @entity and should not be
959 * freed or modified.
961 * Returns: (transfer none): the requested style or %NULL for
962 * transparent dresses or errors.
964 * Since: 1.0
966 AdgStyle *
967 adg_entity_style(AdgEntity *entity, AdgDress dress)
969 AdgStyle *style;
971 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
973 style = adg_entity_get_style(entity, dress);
975 if (style == NULL) {
976 AdgEntityPrivate *data = entity->data;
978 if (data->parent != NULL)
979 style = adg_entity_style(data->parent, dress);
980 else
981 style = adg_dress_get_fallback(dress);
984 return style;
988 * adg_entity_apply_dress:
989 * @entity: an #AdgEntity
990 * @dress: the dress style to apply
991 * @cr: a #cairo_t drawing context
993 * Convenient function to apply a @dress style (as returned by
994 * adg_entity_style()) to the @cr cairo context.
996 * Since: 1.0
998 void
999 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
1001 AdgStyle *style;
1003 g_return_if_fail(ADG_IS_ENTITY(entity));
1004 g_return_if_fail(cr != NULL);
1006 style = adg_entity_style(entity, dress);
1008 if (style != NULL)
1009 adg_style_apply(style, entity, cr);
1013 * adg_entity_global_changed:
1014 * @entity: an #AdgEntity
1016 * Emits the #AdgEntity::global-changed signal on @entity and on all of
1017 * its children, if any.
1019 * Since: 1.0
1021 void
1022 adg_entity_global_changed(AdgEntity *entity)
1024 g_return_if_fail(ADG_IS_ENTITY(entity));
1026 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1030 * adg_entity_local_changed:
1031 * @entity: an #AdgEntity
1033 * Emits the #AdgEntity::local-changed signal on @entity and on all of
1034 * its children, if any.
1036 * Since: 1.0
1038 void
1039 adg_entity_local_changed(AdgEntity *entity)
1041 g_return_if_fail(ADG_IS_ENTITY(entity));
1043 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1047 * adg_entity_invalidate:
1048 * @entity: an #AdgEntity
1050 * Emits the #AdgEntity::invalidate signal on @entity and on all of
1051 * its children, if any, clearing the eventual cache stored by the
1052 * #AdgEntity::arrange signal and setting the entity state similary
1053 * to the just initialized entity.
1055 * Since: 1.0
1057 void
1058 adg_entity_invalidate(AdgEntity *entity)
1060 g_return_if_fail(ADG_IS_ENTITY(entity));
1062 g_signal_emit(entity, _adg_signals[INVALIDATE], 0);
1066 * adg_entity_arrange:
1067 * @entity: an #AdgEntity
1069 * Emits the #AdgEntity::arrange signal on @entity and all its children,
1070 * if any. The arrange call is implicitely called by the
1071 * #AdgEntity::render signal but not by adg_entity_get_extents().
1073 * Since: 1.0
1075 void
1076 adg_entity_arrange(AdgEntity *entity)
1078 g_return_if_fail(ADG_IS_ENTITY(entity));
1080 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1084 * adg_entity_render:
1085 * @entity: an #AdgEntity
1086 * @cr: a #cairo_t drawing context
1088 * Emits the #AdgEntity::render signal on @entity and on all of its
1089 * children, if any, causing the rendering to the @cr cairo context.
1091 * Since: 1.0
1093 void
1094 adg_entity_render(AdgEntity *entity, cairo_t *cr)
1096 g_return_if_fail(ADG_IS_ENTITY(entity));
1098 g_signal_emit(entity, _adg_signals[RENDER], 0, cr);
1102 * adg_entity_point:
1103 * @entity: an #AdgEntity
1104 * @point: the #AdgPoint to define
1105 * @new_point: the new #AdgPoint value
1107 * <note><para>
1108 * This function is only useful in entity implementations.
1109 * </para></note>
1111 * A convenient method to set an #AdgPoint owned by @entity.
1112 * @point is the old value while @new_point is the new value. It
1113 * can be used for setting #AdgPoint in the private data, such as:
1115 * |[
1116 * data->point = adg_entity_point(entity, data->point, new_point);
1117 * ]|
1119 * This function takes care of the dependencies between @entity and
1120 * the eventual models bound to the old and new points.
1122 * @point can be %NULL, in which case a clone of @new_point will be
1123 * returned. Also @new_point can be %NULL, in which case @point is
1124 * destroyed and %NULL will be returned.
1126 * Returns: the new properly defined point
1128 * Since: 1.0
1130 AdgPoint *
1131 adg_entity_point(AdgEntity *entity, AdgPoint *point, AdgPoint *new_point)
1133 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
1135 if (!adg_point_equal(point, new_point)) {
1136 AdgModel *old_model, *new_model;
1138 old_model = point ? adg_point_get_model(point) : NULL;
1139 new_model = new_point ? adg_point_get_model(new_point) : NULL;
1141 if (new_model != old_model) {
1142 if (new_model)
1143 adg_model_add_dependency(new_model, entity);
1144 if (old_model)
1145 adg_model_remove_dependency(old_model, entity);
1148 if (new_point && point) {
1149 adg_point_copy(point, new_point);
1150 } else if (new_point) {
1151 point = adg_point_dup(new_point);
1152 } else {
1153 adg_point_destroy(point);
1154 point = NULL;
1158 return point;
1162 static void
1163 _adg_set_parent(AdgEntity *entity, AdgEntity *parent)
1165 AdgEntityPrivate *data;
1166 AdgEntity *old_parent;
1168 data = entity->data;
1169 old_parent = data->parent;
1171 if (parent)
1172 g_object_ref(parent);
1174 data->parent = parent;
1175 data->global.is_defined = FALSE;
1176 data->local.is_defined = FALSE;
1178 g_signal_emit(entity, _adg_signals[PARENT_SET], 0, old_parent);
1180 if (old_parent)
1181 g_object_unref(old_parent);
1184 static void
1185 _adg_global_changed(AdgEntity *entity)
1187 AdgEntityPrivate *data;
1188 const cairo_matrix_t *map;
1189 cairo_matrix_t *matrix;
1191 data = entity->data;
1192 map = &data->global_map;
1193 matrix = &data->global.matrix;
1195 if (data->parent) {
1196 adg_matrix_copy(matrix, adg_entity_get_global_matrix(data->parent));
1197 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1198 } else {
1199 adg_matrix_copy(matrix, map);
1203 static void
1204 _adg_local_changed(AdgEntity *entity)
1206 AdgEntityPrivate *data;
1207 const cairo_matrix_t *map;
1208 cairo_matrix_t *matrix;
1210 data = entity->data;
1211 map = &data->local_map;
1212 matrix = &data->local.matrix;
1214 switch (data->local_method) {
1215 case ADG_MIX_DISABLED:
1216 adg_matrix_copy(matrix, adg_matrix_identity());
1217 break;
1218 case ADG_MIX_NONE:
1219 adg_matrix_copy(matrix, map);
1220 break;
1221 case ADG_MIX_ANCESTORS:
1222 if (data->parent) {
1223 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1224 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1225 } else {
1226 adg_matrix_copy(matrix, map);
1228 break;
1229 case ADG_MIX_ANCESTORS_NORMALIZED:
1230 if (data->parent) {
1231 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1232 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1233 } else {
1234 adg_matrix_copy(matrix, map);
1236 adg_matrix_normalize(matrix);
1237 break;
1238 case ADG_MIX_PARENT:
1239 if (data->parent) {
1240 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1241 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1242 } else {
1243 adg_matrix_copy(matrix, map);
1245 break;
1246 case ADG_MIX_PARENT_NORMALIZED:
1247 if (data->parent) {
1248 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1249 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1250 } else {
1251 adg_matrix_copy(matrix, map);
1253 adg_matrix_normalize(matrix);
1254 break;
1255 case ADG_MIX_UNDEFINED:
1256 g_warning(_("%s: requested to mix the maps using an undefined method"),
1257 G_STRLOC);
1258 break;
1259 default:
1260 g_return_if_reached();
1261 break;
1265 static void
1266 _adg_real_invalidate(AdgEntity *entity)
1268 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1269 AdgEntityPrivate *data = entity->data;
1271 /* Do not raise any warning if invalidate() is not defined,
1272 * assuming entity does not have additional cache to be cleared */
1273 if (klass->invalidate)
1274 klass->invalidate(entity);
1276 data->extents.is_defined = FALSE;
1279 static void
1280 _adg_real_arrange(AdgEntity *entity)
1282 AdgEntityClass *klass;
1283 AdgEntityPrivate *data;
1285 klass = ADG_ENTITY_GET_CLASS(entity);
1286 data = entity->data;
1288 /* Update the global matrix, if required */
1289 if (!data->global.is_defined) {
1290 data->global.is_defined = TRUE;
1291 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1294 /* Update the local matrix, if required */
1295 if (!data->local.is_defined) {
1296 data->local.is_defined = TRUE;
1297 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1300 /* The arrange() method must be defined */
1301 if (klass->arrange == NULL) {
1302 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1303 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1304 data->extents.is_defined = FALSE;
1305 return;
1308 klass->arrange(entity);
1311 static void
1312 _adg_real_render(AdgEntity *entity, cairo_t *cr)
1314 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1316 /* The render method must be defined */
1317 if (klass->render == NULL) {
1318 g_warning(_("%s: `render' method not implemented for type `%s'"),
1319 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1320 return;
1323 /* Before the rendering, the entity should be arranged */
1324 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1326 cairo_save(cr);
1327 klass->render(entity, cr);
1328 cairo_restore(cr);
1330 if (_adg_show_extents) {
1331 AdgEntityPrivate *data = entity->data;
1332 CpmlExtents *extents = &data->extents;
1334 if (extents->is_defined) {
1335 cairo_save(cr);
1336 cairo_set_source_rgba(cr, 0.15, 0.15, 0.15, 0.15);
1337 cairo_rectangle(cr, extents->org.x, extents->org.y,
1338 extents->size.x, extents->size.y);
1339 cairo_fill(cr);
1340 cairo_restore(cr);