s/2008,2009/2008,2009,2010/
[adg.git] / adg / adg-entity.c
blob2fd9211a2d241d40097e04669491b0d42cf66c16
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-entity
23 * @short_description: The base class for renderable objects
25 * This abstract class provides the base for all renderable objects.
27 * To provide a proper #AdgEntity derived type, you must at least
28 * implement its arrange() and render() virtual methods. Also, if
29 * you are using some sort of caching, ensure to clear it in the
30 * invalidate() method.
31 **/
33 /**
34 * AdgEntity:
36 * All fields are private and should not be used directly.
37 * Use its public methods instead.
38 **/
40 /**
41 * AdgEntityClass:
42 * @parent_set: called after the parent has changed
43 * @invalidate: invalidating callback, used to clear the cache
44 * @arrange: prepare the layout and fill the extents struct
45 * @render: rendering callback, it must be implemented
47 * Any entity (if not abstract) must implement at least the @render method.
48 **/
50 /**
51 * AdgEntityCallback:
52 * @entity: an #AdgEntity
53 * @user_data: a general purpose pointer
55 * Callback used when inspecting or browsing entities. For example,
56 * it is passed to adg_model_foreach_dependency() to perform an
57 * operation on all the entities depending on an #AdgModel.
58 **/
61 #include "adg-internal.h"
62 #include "adg-entity.h"
63 #include "adg-entity-private.h"
64 #include "adg-canvas.h"
65 #include "adg-font-style.h"
66 #include "adg-dim-style.h"
67 #include "adg-marshal.h"
69 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
72 enum {
73 PROP_0,
74 PROP_PARENT,
75 PROP_GLOBAL_MAP,
76 PROP_LOCAL_MAP,
77 PROP_LOCAL_METHOD
80 enum {
81 PARENT_SET,
82 GLOBAL_CHANGED,
83 LOCAL_CHANGED,
84 INVALIDATE,
85 ARRANGE,
86 RENDER,
87 LAST_SIGNAL
91 static void dispose (GObject *object);
92 static void get_property (GObject *object,
93 guint prop_id,
94 GValue *value,
95 GParamSpec *pspec);
96 static void set_property (GObject *object,
97 guint prop_id,
98 const GValue *value,
99 GParamSpec *pspec);
100 static gboolean set_parent (AdgEntity *entity,
101 AdgEntity *parent);
102 static gboolean set_global_map (AdgEntity *entity,
103 const AdgMatrix *map);
104 static gboolean set_local_map (AdgEntity *entity,
105 const AdgMatrix *map);
106 static gboolean set_local_method (AdgEntity *entity,
107 AdgMixMethod local_method);
108 static void global_changed (AdgEntity *entity);
109 static void local_changed (AdgEntity *entity);
110 static void real_invalidate (AdgEntity *entity);
111 static void real_arrange (AdgEntity *entity);
112 static void real_render (AdgEntity *entity,
113 cairo_t *cr);
115 static guint signals[LAST_SIGNAL] = { 0 };
116 static gboolean show_extents = FALSE;
119 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
122 static void
123 adg_entity_class_init(AdgEntityClass *klass)
125 GObjectClass *gobject_class;
126 GParamSpec *param;
127 GClosure *closure;
128 GType param_types[1];
130 gobject_class = (GObjectClass *) klass;
132 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
134 gobject_class->dispose = dispose;
135 gobject_class->get_property = get_property;
136 gobject_class->set_property = set_property;
138 klass->parent_set = NULL;
139 klass->global_changed = global_changed;
140 klass->local_changed = local_changed;
141 klass->invalidate = NULL;
142 klass->arrange= NULL;
143 klass->render = NULL;
145 param = g_param_spec_object("parent",
146 P_("Parent Entity"),
147 P_("The parent entity of this entity or NULL if this is a top-level entity"),
148 ADG_TYPE_ENTITY,
149 G_PARAM_READWRITE);
150 g_object_class_install_property(gobject_class, PROP_PARENT, param);
152 param = g_param_spec_boxed("global-map",
153 P_("Global Map"),
154 P_("The transformation to be combined with the parent ones to get the global matrix"),
155 ADG_TYPE_MATRIX,
156 G_PARAM_READWRITE);
157 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
159 param = g_param_spec_boxed("local-map",
160 P_("Local Map"),
161 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
162 ADG_TYPE_MATRIX,
163 G_PARAM_READWRITE);
164 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
166 param = g_param_spec_enum("local-method",
167 P_("Local Mix Method"),
168 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
169 ADG_TYPE_MIX_METHOD, ADG_MIX_ANCESTORS,
170 G_PARAM_READWRITE);
171 g_object_class_install_property(gobject_class, PROP_LOCAL_METHOD, param);
174 * AdgEntity::parent-set:
175 * @entity: an #AdgEntity
176 * @old_parent: the old parent
178 * Emitted after the parent entity has changed. The new parent
179 * can be inspected using adg_entity_get_parent().
181 * It is allowed for both old and new parent to be %NULL.
183 signals[PARENT_SET] = g_signal_new("parent-set",
184 G_OBJECT_CLASS_TYPE(gobject_class),
185 G_SIGNAL_RUN_FIRST,
186 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
187 NULL, NULL,
188 adg_marshal_VOID__OBJECT,
189 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
192 * AdgEntity::global-changed
193 * @entity: an #AdgEntity
195 * Emitted when the global map of @entity or any of its parent
196 * has changed. The default handler will compute the new global
197 * matrix, updating the internal cache.
199 signals[GLOBAL_CHANGED] = g_signal_new("global-changed",
200 G_OBJECT_CLASS_TYPE(gobject_class),
201 G_SIGNAL_RUN_FIRST,
202 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
203 NULL, NULL,
204 adg_marshal_VOID__VOID,
205 G_TYPE_NONE, 0);
208 * AdgEntity::local-changed
209 * @entity: an #AdgEntity
211 * Emitted when the local map of @entity or any of its parent
212 * has changed. The default handler will compute the new local
213 * matrix, updating the internal cache.
215 signals[LOCAL_CHANGED] = g_signal_new("local-changed",
216 G_OBJECT_CLASS_TYPE(gobject_class),
217 G_SIGNAL_RUN_FIRST,
218 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
219 NULL, NULL,
220 adg_marshal_VOID__VOID,
221 G_TYPE_NONE, 0);
224 * AdgEntity::invalidate:
225 * @entity: an #AdgEntity
227 * Invalidates the whole @entity, that is resets all the cache
228 * (if present) built during the #AdgEntity::arrange signal.
229 * The resulting state is a clean entity, similar to what you
230 * have just before the first rendering.
232 closure = g_cclosure_new(G_CALLBACK(real_invalidate), NULL, NULL);
233 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
234 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
235 adg_marshal_VOID__VOID,
236 G_TYPE_NONE, 0, param_types);
239 * AdgEntity::arrange:
240 * @entity: an #AdgEntity
242 * Arranges the layout of @entity, updating the cache if necessary,
243 * and computes the extents of @entity.
245 closure = g_cclosure_new(G_CALLBACK(real_arrange), NULL, NULL);
246 signals[ARRANGE] = g_signal_newv("arrange", ADG_TYPE_ENTITY,
247 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
248 adg_marshal_VOID__VOID,
249 G_TYPE_NONE, 0, param_types);
251 * AdgEntity::render:
252 * @entity: an #AdgEntity
253 * @cr: a #cairo_t drawing context
255 * Causes the rendering of @entity on @cr. A render signal will
256 * automatically emit #AdgEntity::arrange just before the real
257 * rendering on the cairo context.
259 closure = g_cclosure_new(G_CALLBACK(real_render), NULL, NULL);
260 param_types[0] = G_TYPE_POINTER;
261 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
262 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
263 adg_marshal_VOID__POINTER,
264 G_TYPE_NONE, 1, param_types);
267 static void
268 adg_entity_init(AdgEntity *entity)
270 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
271 ADG_TYPE_ENTITY,
272 AdgEntityPrivate);
273 data->parent = NULL;
274 cairo_matrix_init_identity(&data->global_map);
275 cairo_matrix_init_identity(&data->local_map);
276 data->local_method = ADG_MIX_ANCESTORS;
277 data->hash_styles = NULL;
278 data->global.is_defined = FALSE;
279 adg_matrix_copy(&data->global.matrix, adg_matrix_null());
280 data->local.is_defined = FALSE;
281 adg_matrix_copy(&data->local.matrix, adg_matrix_null());
282 data->extents.is_defined = FALSE;
284 entity->data = data;
287 static void
288 dispose(GObject *object)
290 AdgEntity *entity;
291 AdgEntityPrivate *data;
293 entity = (AdgEntity *) object;
294 data = entity->data;
296 /* This call will emit a "notify" signal for parent.
297 * Consequentially, the references to the old parent is dropped. */
298 adg_entity_set_parent(entity, NULL);
300 if (data->hash_styles != NULL) {
301 g_hash_table_destroy(data->hash_styles);
302 data->hash_styles = NULL;
305 if (PARENT_OBJECT_CLASS->dispose)
306 PARENT_OBJECT_CLASS->dispose(object);
309 static void
310 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
312 AdgEntity *entity;
313 AdgEntityPrivate *data;
315 entity = (AdgEntity *) object;
316 data = entity->data;
318 switch (prop_id) {
319 case PROP_PARENT:
320 g_value_set_object(value, data->parent);
321 break;
322 case PROP_GLOBAL_MAP:
323 g_value_set_boxed(value, &data->global_map);
324 break;
325 case PROP_LOCAL_MAP:
326 g_value_set_boxed(value, &data->local_map);
327 break;
328 case PROP_LOCAL_METHOD:
329 g_value_set_enum(value, data->local_method);
330 break;
331 default:
332 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
333 break;
337 static void
338 set_property(GObject *object,
339 guint prop_id, const GValue *value, GParamSpec *pspec)
341 AdgEntity *entity;
342 AdgEntityPrivate *data;
344 entity = (AdgEntity *) object;
345 data = entity->data;
347 switch (prop_id) {
348 case PROP_PARENT:
349 set_parent(entity, g_value_get_object(value));
350 break;
351 case PROP_GLOBAL_MAP:
352 set_global_map(entity, g_value_get_boxed(value));
353 break;
354 case PROP_LOCAL_MAP:
355 set_local_map(entity, g_value_get_boxed(value));
356 break;
357 case PROP_LOCAL_METHOD:
358 set_local_method(entity, g_value_get_enum(value));
359 break;
360 default:
361 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
362 break;
368 * adg_switch_extents:
369 * @state: new extents state
371 * Strokes (if @state is %TRUE) a rectangle around every entity to
372 * show their extents. Useful for debugging purposes.
374 void
375 adg_switch_extents(gboolean state)
377 show_extents = state;
381 * adg_entity_get_canvas:
382 * @entity: an #AdgEntity
384 * Walks on the @entity hierarchy and gets the first parent of @entity that is
385 * of #AdgCanvas derived type.
387 * Returns: the requested canvas or %NULL on errors or if there is
388 * no #AdgCanvas in the @entity hierarchy
390 AdgCanvas *
391 adg_entity_get_canvas(AdgEntity *entity)
393 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
395 while (entity) {
396 if (ADG_IS_CANVAS(entity))
397 return (AdgCanvas *) entity;
399 entity = adg_entity_get_parent(entity);
402 return NULL;
406 * adg_entity_set_parent:
407 * @entity: an #AdgEntity
408 * @parent: the parent entity
410 * <note><para>
411 * This function is only useful in entity implementations.
412 * </para></note>
414 * Sets a new parent on @entity.
416 void
417 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
419 g_return_if_fail(ADG_IS_ENTITY(entity));
421 if (set_parent(entity, parent))
422 g_object_notify((GObject *) entity, "parent");
426 * adg_entity_get_parent:
427 * @entity: an #AdgEntity
429 * Gets the parent of @entity.
431 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
433 AdgEntity *
434 adg_entity_get_parent(AdgEntity *entity)
436 AdgEntityPrivate *data;
438 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
440 data = entity->data;
442 return data->parent;
446 * adg_entity_set_global_map:
447 * @entity: an #AdgEntity object
448 * @map: the new map
450 * Sets the new global transformation of @entity to @map:
451 * the old map is discarded. If @map is %NULL an identity
452 * matrix is implied.
454 void
455 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
457 g_return_if_fail(ADG_IS_ENTITY(entity));
459 if (set_global_map(entity, map))
460 g_object_notify((GObject *) entity, "global-map");
464 * adg_entity_transform_global_map:
465 * @entity: an #AdgEntity object
466 * @transformation: the transformation to apply
467 * @mode: how @transformation should be applied
469 * Convenient function to change the global map of @entity by
470 * applying @tranformation using the @mode operator. This is
471 * logically equivalent to the following:
473 * |[
474 * AdgMatrix map;
475 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
476 * adg_matrix_transform(&map, transformation, mode);
477 * adg_entity_set_global_map(entity, &map);
478 * ]|
480 void
481 adg_entity_transform_global_map(AdgEntity *entity,
482 const AdgMatrix *transformation,
483 AdgTransformMode mode)
485 AdgEntityPrivate *data;
486 AdgMatrix map;
488 g_return_if_fail(ADG_IS_ENTITY(entity));
489 g_return_if_fail(transformation != NULL);
491 data = entity->data;
493 adg_matrix_copy(&map, &data->global_map);
494 adg_matrix_transform(&map, transformation, mode);
496 if (set_global_map(entity, &map))
497 g_object_notify((GObject *) entity, "global-map");
501 * adg_entity_get_global_map:
502 * @entity: an #AdgEntity object
504 * Gets the transformation to be used to compute the global matrix
505 * of @entity.
507 * Returns: the requested map or %NULL on errors
509 const AdgMatrix *
510 adg_entity_get_global_map(AdgEntity *entity)
512 AdgEntityPrivate *data;
514 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
516 data = entity->data;
518 return &data->global_map;
522 * adg_entity_get_global_matrix:
523 * @entity: an #AdgEntity object
525 * Gets the current global matrix of @entity. The returned value
526 * is owned by @entity and should not be changed or freed.
528 * The global matrix is computed in the arrange() phase by
529 * combining all the global maps of the @entity hierarchy using
530 * the %ADG_MIX_ANCESTORS method.
532 * Returns: the global matrix or %NULL on errors
534 const AdgMatrix *
535 adg_entity_get_global_matrix(AdgEntity *entity)
537 AdgEntityPrivate *data;
539 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
541 data = entity->data;
543 return &data->global.matrix;
547 * adg_entity_set_local_map:
548 * @entity: an #AdgEntity object
549 * @map: the new map
551 * Sets the new local transformation of @entity to @map:
552 * the old map is discarded. If @map is %NULL an identity
553 * matrix is implied.
555 void
556 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
558 g_return_if_fail(ADG_IS_ENTITY(entity));
560 if (set_local_map(entity, map))
561 g_object_notify((GObject *) entity, "local-map");
565 * adg_entity_transform_local_map:
566 * @entity: an #AdgEntity object
567 * @transformation: the transformation to apply
568 * @mode: how @transformation should be applied
570 * Convenient function to change the local map of @entity by
571 * applying @tranformation using the @mode operator. This is
572 * logically equivalent to the following:
574 * |[
575 * AdgMatrix map;
576 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
577 * adg_matrix_transform(&map, transformation, mode);
578 * adg_entity_set_local_map(entity, &map);
579 * ]|
581 void
582 adg_entity_transform_local_map(AdgEntity *entity,
583 const AdgMatrix *transformation,
584 AdgTransformMode mode)
586 AdgEntityPrivate *data;
587 AdgMatrix map;
589 g_return_if_fail(ADG_IS_ENTITY(entity));
590 g_return_if_fail(transformation != NULL);
592 data = entity->data;
594 adg_matrix_copy(&map, &data->local_map);
595 adg_matrix_transform(&map, transformation, mode);
597 if (set_local_map(entity, &map))
598 g_object_notify((GObject *) entity, "local-map");
602 * adg_entity_get_local_map:
603 * @entity: an #AdgEntity object
605 * Gets the transformation to be used to compute the local matrix
606 * of @entity and store it in @map.
608 * Returns: the requested map or %NULL on errors
610 const AdgMatrix *
611 adg_entity_get_local_map(AdgEntity *entity)
613 AdgEntityPrivate *data;
615 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
617 data = entity->data;
619 return &data->local_map;
623 * adg_entity_get_local_matrix:
624 * @entity: an #AdgEntity object
626 * Gets the current local matrix of @entity. The returned value
627 * is owned by @entity and should not be changed or freed.
629 * The local matrix is computed in the arrange() phase by
630 * combining all the local maps of the @entity hierarchy using
631 * the method specified by the #AdgEntity:local-method property.
633 * Returns: the local matrix or %NULL on errors
635 const AdgMatrix *
636 adg_entity_get_local_matrix(AdgEntity *entity)
638 AdgEntityPrivate *data;
640 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
642 data = entity->data;
644 return &data->local.matrix;
648 * adg_entity_set_local_method:
649 * @entity: an #AdgEntity object
650 * @local_method: new method
652 * Sets a new local mix method on @entity. The
653 * #AdgEntity:local-method property defines how the local
654 * matrix must be computed: check out the #AdgMixMethod
655 * documentation to know what are the availables methods
656 * and how they affect the local matrix computation.
658 * Setting a different local method emits an #Adgentity::local-changed
659 * signal on @entity.
661 void
662 adg_entity_set_local_method(AdgEntity *entity, AdgMixMethod local_method)
664 g_return_if_fail(ADG_IS_ENTITY(entity));
666 if (set_local_method(entity, local_method))
667 g_object_notify((GObject *) entity, "local-method");
671 * adg_entity_get_local_method:
672 * @entity: an #AdgEntity object
674 * Gets the local mix method of @entity. Check out the
675 * adg_entity_set_local_method() documentation to know what the
676 * local method is used for.
678 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
680 AdgMixMethod
681 adg_entity_get_local_method(AdgEntity *entity)
683 AdgEntityPrivate *data;
685 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
687 data = entity->data;
689 return data->local_method;
693 * adg_entity_set_extents:
694 * @entity: an #AdgEntity
695 * @extents: the new extents
697 * <note><para>
698 * This function is only useful in entity implementations.
699 * </para></note>
701 * Sets a new bounding box for @entity. @extents can be %NULL,
702 * in which case the extents are unset.
704 void
705 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
707 AdgEntityPrivate *data;
709 g_return_if_fail(ADG_IS_ENTITY(entity));
711 data = entity->data;
713 if (extents == NULL)
714 data->extents.is_defined = FALSE;
715 else
716 cpml_extents_copy(&data->extents, extents);
720 * adg_entity_get_extents:
721 * @entity: an #AdgEntity
723 * Gets the bounding box of @entity. The returned struct is
724 * owned by @entity and should not modified or freed.
726 * This struct specifies the surface portion (in global space
727 * of @entity) occupied by the entity without taking into
728 * account rendering properties such as line thickness or caps.
730 * The #AdgEntity::arrange signal should be emitted before
731 * this call (either explicitely trought adg_entity_arrange()
732 * or implicitely with adg_entity_render()) in order to get
733 * an up to date boundary box.
735 * Returns: the bounding box of @entity or %NULL on errors
737 const CpmlExtents *
738 adg_entity_get_extents(AdgEntity *entity)
740 AdgEntityPrivate *data;
742 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
744 data = entity->data;
746 return &data->extents;
750 * adg_entity_set_style:
751 * @entity: an #AdgEntity
752 * @dress: a dress style
753 * @style: the new style to use
755 * Overrides the style of @dress for @entity and its children.
756 * If @style is %NULL, any previous override is removed.
758 * The new style must still be compatible with @dress: check out
759 * the adg_dress_style_is_compatible() documentation to know
760 * what a compatible style means.
762 void
763 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
765 AdgEntityPrivate *data;
766 gpointer p_dress;
767 AdgStyle *old_style;
769 g_return_if_fail(ADG_IS_ENTITY(entity));
771 data = entity->data;
773 if (data->hash_styles == NULL && style == NULL)
774 return;
776 if (data->hash_styles == NULL)
777 data->hash_styles = g_hash_table_new_full(NULL, NULL,
778 NULL, g_object_unref);
780 p_dress = GINT_TO_POINTER(dress);
781 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
783 if (style == old_style)
784 return;
786 if (style == NULL) {
787 g_hash_table_remove(data->hash_styles, p_dress);
788 return;
791 if (!adg_dress_style_is_compatible(dress, style)) {
792 GType ancestor_type = adg_dress_get_ancestor_type(dress);
794 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
795 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
796 g_type_name(ancestor_type), adg_dress_get_name(dress));
798 return;
801 g_object_ref(style);
802 g_hash_table_replace(data->hash_styles, p_dress, style);
806 * adg_entity_get_style:
807 * @entity: an #AdgEntity
808 * @dress: the dress of the style to get
810 * Gets the overriden @dress style from @entity. This is a kind
811 * of accessor function: to get the style to be used for rendering
812 * purpose, use adg_entity_style() instead.
814 * Returns: the requested style or %NULL if the @dress style
815 * is not overriden
817 AdgStyle *
818 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
820 AdgEntityPrivate *data;
822 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
824 data = entity->data;
826 if (data->hash_styles == NULL)
827 return NULL;
829 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
833 * adg_entity_style:
834 * @entity: an #AdgEntity
835 * @dress: the dress of the style to get
837 * Gets the style to be used for @entity. @dress specifies which
838 * "family" of style to get.
840 * The following sequence of checks is performed to get the proper
841 * style, stopping at the first succesfull result:
843 * <orderedlist>
844 * <listitem>check if the style is directly overriden by this entity,
845 * as returned by adg_entity_get_style();</listitem>
846 * <listitem>check if @entity has a parent, in which case returns the
847 * adg_entity_style() of the parent;</listitem>
848 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
849 * </orderedlist>
851 * Returns: the requested style or %NULL for transparent dresses or errors
853 AdgStyle *
854 adg_entity_style(AdgEntity *entity, AdgDress dress)
856 AdgStyle *style;
858 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
860 style = adg_entity_get_style(entity, dress);
862 if (style == NULL) {
863 AdgEntityPrivate *data = entity->data;
865 if (data->parent != NULL)
866 style = adg_entity_style(data->parent, dress);
867 else
868 style = adg_dress_get_fallback(dress);
871 return style;
875 * adg_entity_apply_dress:
876 * @entity: an #AdgEntity
877 * @dress: the dress style to apply
878 * @cr: a #cairo_t drawing context
880 * Convenient function to apply a @dress style (as returned by
881 * adg_entity_style()) to the @cr cairo context.
883 void
884 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
886 AdgStyle *style;
888 g_return_if_fail(ADG_IS_ENTITY(entity));
889 g_return_if_fail(cr != NULL);
891 style = adg_entity_style(entity, dress);
893 if (style != NULL)
894 adg_style_apply(style, entity, cr);
898 * adg_entity_global_changed:
899 * @entity: an #AdgEntity
901 * Emits the #AdgEntity::global-changed signal on @entity and on all of
902 * its children, if any.
904 void
905 adg_entity_global_changed(AdgEntity *entity)
907 g_return_if_fail(ADG_IS_ENTITY(entity));
909 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
913 * adg_entity_local_changed:
914 * @entity: an #AdgEntity
916 * Emits the #AdgEntity::local-changed signal on @entity and on all of
917 * its children, if any.
919 void
920 adg_entity_local_changed(AdgEntity *entity)
922 g_return_if_fail(ADG_IS_ENTITY(entity));
924 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
928 * adg_entity_invalidate:
929 * @entity: an #AdgEntity
931 * Emits the #AdgEntity::invalidate signal on @entity and on all of
932 * its children, if any, clearing the eventual cache stored by the
933 * #AdgEntity::arrange signal and setting the entity state similary
934 * to the just initialized entity.
936 void
937 adg_entity_invalidate(AdgEntity *entity)
939 g_return_if_fail(ADG_IS_ENTITY(entity));
941 g_signal_emit(entity, signals[INVALIDATE], 0);
945 * adg_entity_arrange:
946 * @entity: an #AdgEntity
948 * Emits the #AdgEntity::arrange signal on @entity and all its children,
949 * if any. This function is rarely needed as the arrange call is usually
950 * implicitely called by the #AdgEntity::render signal or iby a call to
951 * adg_entity_get_extents().
953 void
954 adg_entity_arrange(AdgEntity *entity)
956 g_return_if_fail(ADG_IS_ENTITY(entity));
958 g_signal_emit(entity, signals[ARRANGE], 0);
962 * adg_entity_render:
963 * @entity: an #AdgEntity
964 * @cr: a #cairo_t drawing context
966 * Emits the #AdgEntity::render signal on @entity and on all of its
967 * children, if any, causing the rendering to the @cr cairo context.
969 void
970 adg_entity_render(AdgEntity *entity, cairo_t *cr)
972 g_return_if_fail(ADG_IS_ENTITY(entity));
974 g_signal_emit(entity, signals[RENDER], 0, cr);
978 static gboolean
979 set_parent(AdgEntity *entity, AdgEntity *parent)
981 AdgEntityPrivate *data;
982 AdgEntity *old_parent;
984 data = entity->data;
985 old_parent = data->parent;
987 /* Check if parent has changed */
988 if (parent == old_parent)
989 return FALSE;
991 if (parent != NULL)
992 g_object_ref(parent);
994 data->parent = parent;
995 data->global.is_defined = FALSE;
996 data->local.is_defined = FALSE;
998 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
1000 if (old_parent != NULL)
1001 g_object_unref(old_parent);
1003 return TRUE;
1006 static gboolean
1007 set_global_map(AdgEntity *entity, const AdgMatrix *map)
1009 AdgEntityPrivate *data = entity->data;
1011 if (map == NULL)
1012 map = adg_matrix_identity();
1014 if (adg_matrix_equal(&data->global_map, map))
1015 return FALSE;
1017 adg_matrix_copy(&data->global_map, map);
1019 data->global.is_defined = FALSE;
1020 return TRUE;
1023 static gboolean
1024 set_local_map(AdgEntity *entity, const AdgMatrix *map)
1026 AdgEntityPrivate *data = entity->data;
1028 if (map == NULL)
1029 map = adg_matrix_identity();
1031 if (adg_matrix_equal(&data->local_map, map))
1032 return FALSE;
1034 adg_matrix_copy(&data->local_map, map);
1036 data->local.is_defined = FALSE;
1037 return TRUE;
1040 static gboolean
1041 set_local_method(AdgEntity *entity, AdgMixMethod local_method)
1043 AdgEntityPrivate *data = entity->data;
1045 if (data->local_method == local_method)
1046 return FALSE;
1048 data->local_method = local_method;
1049 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
1051 return TRUE;
1054 static void
1055 global_changed(AdgEntity *entity)
1057 AdgEntityPrivate *data;
1058 const AdgMatrix *map;
1059 AdgMatrix *matrix;
1061 data = entity->data;
1062 map = &data->global_map;
1063 matrix = &data->global.matrix;
1065 if (data->parent) {
1066 adg_matrix_copy(matrix, adg_entity_get_global_matrix(data->parent));
1067 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1068 } else {
1069 adg_matrix_copy(matrix, map);
1073 static void
1074 local_changed(AdgEntity *entity)
1076 AdgEntityPrivate *data;
1077 const AdgMatrix *map;
1078 AdgMatrix *matrix;
1080 data = entity->data;
1081 map = &data->local_map;
1082 matrix = &data->local.matrix;
1084 switch (data->local_method) {
1085 case ADG_MIX_DISABLED:
1086 adg_matrix_copy(matrix, adg_matrix_identity());
1087 break;
1088 case ADG_MIX_NONE:
1089 adg_matrix_copy(matrix, map);
1090 break;
1091 case ADG_MIX_ANCESTORS:
1092 if (data->parent) {
1093 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1094 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1095 } else {
1096 adg_matrix_copy(matrix, map);
1098 break;
1099 case ADG_MIX_ANCESTORS_NORMALIZED:
1100 if (data->parent) {
1101 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1102 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1103 } else {
1104 adg_matrix_copy(matrix, map);
1106 adg_matrix_normalize(matrix);
1107 break;
1108 case ADG_MIX_PARENT:
1109 if (data->parent) {
1110 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1111 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1112 } else {
1113 adg_matrix_copy(matrix, map);
1115 break;
1116 case ADG_MIX_PARENT_NORMALIZED:
1117 if (data->parent) {
1118 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1119 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1120 } else {
1121 adg_matrix_copy(matrix, map);
1123 adg_matrix_normalize(matrix);
1124 break;
1125 case ADG_MIX_UNDEFINED:
1126 g_warning(_("%s: requested to mix the maps using an undefined method"),
1127 G_STRLOC);
1128 break;
1129 default:
1130 g_return_if_reached();
1131 break;
1135 static void
1136 real_invalidate(AdgEntity *entity)
1138 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1139 AdgEntityPrivate *data = entity->data;
1141 /* Do not raise any warning if invalidate() is not defined,
1142 * assuming entity does not have additional cache to be cleared */
1143 if (klass->invalidate)
1144 klass->invalidate(entity);
1146 data->extents.is_defined = FALSE;
1149 static void
1150 real_arrange(AdgEntity *entity)
1152 AdgEntityClass *klass;
1153 AdgEntityPrivate *data;
1155 klass = ADG_ENTITY_GET_CLASS(entity);
1156 data = entity->data;
1158 /* Update the global matrix, if required */
1159 if (!data->global.is_defined) {
1160 data->global.is_defined = TRUE;
1161 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
1164 /* Update the local matrix, if required */
1165 if (!data->local.is_defined) {
1166 data->local.is_defined = TRUE;
1167 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
1170 /* The arrange() method must be defined */
1171 if (klass->arrange == NULL) {
1172 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1173 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1174 data->extents.is_defined = FALSE;
1175 return;
1178 klass->arrange(entity);
1181 static void
1182 real_render(AdgEntity *entity, cairo_t *cr)
1184 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1186 /* The render method must be defined */
1187 if (klass->render == NULL) {
1188 g_warning(_("%s: `render' method not implemented for type `%s'"),
1189 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1190 return;
1193 /* Before the rendering, the entity should be arranged */
1194 g_signal_emit(entity, signals[ARRANGE], 0);
1197 if (show_extents) {
1198 AdgEntityPrivate *data = entity->data;
1200 if (data->extents.is_defined) {
1201 cairo_save(cr);
1202 cairo_identity_matrix(cr);
1203 cairo_set_line_width(cr, 1);
1204 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
1205 cairo_rectangle(cr, data->extents.org.x, data->extents.org.y,
1206 data->extents.size.x, data->extents.size.y);
1207 cairo_stroke(cr);
1208 cairo_restore(cr);
1212 cairo_save(cr);
1213 cairo_set_matrix(cr, adg_entity_get_global_matrix(entity));
1214 klass->render(entity, cr);
1215 cairo_restore(cr);