[docs] Covered all the documentation
[adg.git] / adg / adg-entity.c
blob596d4b0781fbcd32c1210104f14987e11ca3661f
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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 **/
51 #include "adg-entity.h"
52 #include "adg-entity-private.h"
53 #include "adg-canvas.h"
54 #include "adg-font-style.h"
55 #include "adg-dim-style.h"
56 #include "adg-marshal.h"
57 #include "adg-intl.h"
59 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
62 enum {
63 PROP_0,
64 PROP_PARENT,
65 PROP_GLOBAL_MAP,
66 PROP_LOCAL_MAP
69 enum {
70 PARENT_SET,
71 GLOBAL_CHANGED,
72 LOCAL_CHANGED,
73 INVALIDATE,
74 ARRANGE,
75 RENDER,
76 LAST_SIGNAL
80 static void dispose (GObject *object);
81 static void get_property (GObject *object,
82 guint prop_id,
83 GValue *value,
84 GParamSpec *pspec);
85 static void set_property (GObject *object,
86 guint prop_id,
87 const GValue *value,
88 GParamSpec *pspec);
89 static gboolean set_parent (AdgEntity *entity,
90 AdgEntity *parent);
91 static void global_changed (AdgEntity *entity);
92 static void local_changed (AdgEntity *entity);
93 static gboolean set_global_map (AdgEntity *entity,
94 const AdgMatrix *map);
95 static gboolean set_local_map (AdgEntity *entity,
96 const AdgMatrix *map);
97 static void real_invalidate (AdgEntity *entity,
98 gpointer user_data);
99 static void real_arrange (AdgEntity *entity,
100 gpointer user_data);
101 static void real_render (AdgEntity *entity,
102 cairo_t *cr,
103 gpointer user_data);
105 static guint signals[LAST_SIGNAL] = { 0 };
108 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
111 static void
112 adg_entity_class_init(AdgEntityClass *klass)
114 GObjectClass *gobject_class;
115 GParamSpec *param;
116 GClosure *closure;
117 GType param_types[1];
119 gobject_class = (GObjectClass *) klass;
121 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
123 gobject_class->dispose = dispose;
124 gobject_class->get_property = get_property;
125 gobject_class->set_property = set_property;
127 klass->parent_set = NULL;
128 klass->global_changed = global_changed;
129 klass->local_changed = local_changed;
130 klass->invalidate = NULL;
131 klass->arrange= NULL;
132 klass->render = NULL;
134 param = g_param_spec_object("parent",
135 P_("Parent Entity"),
136 P_("The parent entity of this entity or NULL if this is a top-level entity"),
137 ADG_TYPE_ENTITY, G_PARAM_READWRITE);
138 g_object_class_install_property(gobject_class, PROP_PARENT, param);
140 param = g_param_spec_boxed("global-map",
141 P_("Global Map"),
142 P_("The transformation to be combined with the parent ones to get the global matrix"),
143 ADG_TYPE_MATRIX, G_PARAM_READWRITE);
144 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
146 param = g_param_spec_boxed("local-map",
147 P_("Local Map"),
148 P_("The transformation to be combined with the parent ones to get the local matrix"),
149 ADG_TYPE_MATRIX, G_PARAM_READWRITE);
150 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
153 * AdgEntity::parent-set:
154 * @entity: an #AdgEntity
155 * @old_parent: the old parent
157 * Emitted after the parent entity has changed. The new parent
158 * can be inspected using adg_entity_get_parent().
160 * It is allowed for both old and new parent to be %NULL.
162 signals[PARENT_SET] = g_signal_new("parent-set",
163 G_OBJECT_CLASS_TYPE(gobject_class),
164 G_SIGNAL_RUN_FIRST,
165 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
166 NULL, NULL,
167 adg_marshal_VOID__OBJECT,
168 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
171 * AdgEntity::global-changed
172 * @entity: an #AdgEntity
174 * Emitted when the global map of @entity or any of its parent
175 * has changed. The default handler will compute the new global
176 * matrix, storing the value with adg_entity_set_global_matrix().
178 signals[GLOBAL_CHANGED] = g_signal_new("global-changed",
179 G_OBJECT_CLASS_TYPE(gobject_class),
180 G_SIGNAL_RUN_FIRST,
181 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
182 NULL, NULL,
183 adg_marshal_VOID__VOID,
184 G_TYPE_NONE, 0);
187 * AdgEntity::local-changed
188 * @entity: an #AdgEntity
190 * Emitted when the local map of @entity or any of its parent
191 * has changed. The default handler will compute the new local
192 * matrix, storing the value with adg_entity_set_local_matrix().
194 signals[LOCAL_CHANGED] = g_signal_new("local-changed",
195 G_OBJECT_CLASS_TYPE(gobject_class),
196 G_SIGNAL_RUN_FIRST,
197 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
198 NULL, NULL,
199 adg_marshal_VOID__VOID,
200 G_TYPE_NONE, 0);
203 * AdgEntity::invalidate:
204 * @entity: an #AdgEntity
206 * Invalidates the whole @entity, that is resets all the cache
207 * (if present) built during the #AdgEntity::arrange signal.
208 * The resulting state is a clean entity, similar to what you
209 * have just before the first rendering.
211 closure = g_cclosure_new(G_CALLBACK(real_invalidate),
212 (gpointer)0xdeadbeaf, NULL);
213 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
214 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
215 adg_marshal_VOID__VOID,
216 G_TYPE_NONE, 0, param_types);
219 * AdgEntity::arrange:
220 * @entity: an #AdgEntity
222 * Arranges the layout of @entity, updating the cache if necessary,
223 * and computes the extents of @entity.
225 closure = g_cclosure_new(G_CALLBACK(real_arrange),
226 (gpointer)0xdeadbeaf, NULL);
227 signals[ARRANGE] = g_signal_newv("arrange", ADG_TYPE_ENTITY,
228 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
229 adg_marshal_VOID__VOID,
230 G_TYPE_NONE, 0, param_types);
232 * AdgEntity::render:
233 * @entity: an #AdgEntity
234 * @cr: a #cairo_t drawing context
236 * Causes the rendering of @entity on @cr. A render signal will
237 * automatically emit #AdgEntity::arrange just before the real
238 * rendering on the cairo context.
240 closure = g_cclosure_new(G_CALLBACK(real_render),
241 (gpointer)0xdeadbeaf, NULL);
242 param_types[0] = G_TYPE_POINTER;
243 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
244 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
245 adg_marshal_VOID__POINTER,
246 G_TYPE_NONE, 1, param_types);
249 static void
250 adg_entity_init(AdgEntity *entity)
252 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
253 ADG_TYPE_ENTITY,
254 AdgEntityPrivate);
255 data->parent = NULL;
256 cairo_matrix_init_identity(&data->global_map);
257 cairo_matrix_init_identity(&data->local_map);
258 data->extents.is_defined = FALSE;
259 data->hash_styles = NULL;
261 cairo_matrix_init_identity(&data->global_matrix);
262 cairo_matrix_init_identity(&data->local_matrix);
264 entity->data = data;
267 static void
268 dispose(GObject *object)
270 AdgEntity *entity;
271 AdgEntityPrivate *data;
273 entity = (AdgEntity *) object;
274 data = entity->data;
276 /* This call will emit a "notify" signal for parent.
277 * Consequentially, the references to the old parent is dropped. */
278 adg_entity_set_parent(entity, NULL);
280 if (data->hash_styles != NULL) {
281 g_hash_table_destroy(data->hash_styles);
282 data->hash_styles = NULL;
285 if (PARENT_OBJECT_CLASS->dispose != NULL)
286 PARENT_OBJECT_CLASS->dispose(object);
290 static void
291 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
293 AdgEntity *entity;
294 AdgEntityPrivate *data;
296 entity = (AdgEntity *) object;
297 data = entity->data;
299 switch (prop_id) {
300 case PROP_PARENT:
301 g_value_set_object(value, data->parent);
302 break;
303 case PROP_GLOBAL_MAP:
304 g_value_set_boxed(value, &data->global_map);
305 break;
306 case PROP_LOCAL_MAP:
307 g_value_set_boxed(value, &data->local_map);
308 break;
309 default:
310 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
311 break;
315 static void
316 set_property(GObject *object,
317 guint prop_id, const GValue *value, GParamSpec *pspec)
319 AdgEntity *entity = (AdgEntity *) object;
321 switch (prop_id) {
322 case PROP_PARENT:
323 set_parent(entity, g_value_get_object(value));
324 break;
325 case PROP_GLOBAL_MAP:
326 set_global_map(entity, g_value_get_boxed(value));
327 break;
328 case PROP_LOCAL_MAP:
329 set_local_map(entity, g_value_get_boxed(value));
330 break;
331 default:
332 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
333 break;
339 * adg_entity_get_parent:
340 * @entity: an #AdgEntity
342 * Gets the parent of @entity.
344 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
346 AdgEntity *
347 adg_entity_get_parent(AdgEntity *entity)
349 AdgEntityPrivate *data;
351 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
353 data = entity->data;
355 return data->parent;
359 * adg_entity_set_parent:
360 * @entity: an #AdgEntity
361 * @parent: the parent entity
363 * <note><para>
364 * This function is only useful in entity implementations.
365 * </para></note>
367 * Sets a new parent on @entity.
369 void
370 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
372 g_return_if_fail(ADG_IS_ENTITY(entity));
374 if (set_parent(entity, parent))
375 g_object_notify((GObject *) entity, "parent");
379 * adg_entity_get_canvas:
380 * @entity: an #AdgEntity
382 * Walks on the @entity hierarchy and gets the first parent of @entity that is
383 * of #AdgCanvas derived type.
385 * Returns: the requested canvas or %NULL on errors or if there is
386 * no #AdgCanvas in the @entity hierarchy
388 AdgCanvas *
389 adg_entity_get_canvas(AdgEntity *entity)
391 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
393 while (entity) {
394 if (ADG_IS_CANVAS(entity))
395 return (AdgCanvas *) entity;
397 entity = (AdgEntity *) adg_entity_get_parent(entity);
400 return NULL;
404 * adg_entity_get_global_map:
405 * @entity: an #AdgEntity object
406 * @map: where to store the global map
408 * Gets the transformation to be used to compute the global matrix
409 * of @entity and store it in @map.
411 void
412 adg_entity_get_global_map(AdgEntity *entity, AdgMatrix *map)
414 AdgEntityPrivate *data;
416 g_return_if_fail(ADG_IS_ENTITY(entity));
417 g_return_if_fail(map != NULL);
419 data = entity->data;
420 adg_matrix_copy(map, &data->global_map);
424 * adg_entity_set_global_map:
425 * @entity: an #AdgEntity object
426 * @map: the new map
428 * Sets the new global transformation of @entity to @map:
429 * the old map is discarded. If @map is %NULL an identity
430 * matrix is implied.
432 void
433 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
435 g_return_if_fail(ADG_IS_ENTITY(entity));
437 if (set_global_map(entity, map))
438 g_object_notify((GObject *) entity, "global-map");
442 * adg_entity_before_global_map:
443 * @entity: an #AdgEntity object
444 * @transformation: the transformation to apply
446 * Convenient function to change the global map of @entity by
447 * applying @tranformation before the current matrix. This is
448 * logically equivalent to the following code:
450 * |[
451 * AdgMatrix tmp_map;
452 * adg_entity_get_global_map(entity, &tmp_map);
453 * cairo_matrix_multiply(&tmp_map, &tmp_map, transformation);
454 * adg_entity_set_global_map(entity, &tmp_map);
455 * ]|
457 void
458 adg_entity_before_global_map(AdgEntity *entity, const AdgMatrix *transformation)
460 AdgEntityPrivate *data;
461 AdgMatrix map;
463 g_return_if_fail(ADG_IS_ENTITY(entity));
464 g_return_if_fail(transformation != NULL);
466 data = entity->data;
467 cairo_matrix_multiply(&map, &data->global_map, transformation);
469 if (set_global_map(entity, &map))
470 g_object_notify((GObject *) entity, "global-map");
474 * adg_entity_after_global_map:
475 * @entity: an #AdgEntity object
476 * @transformation: the transformation to apply
478 * Convenient function to change the global map of @entity by
479 * applying @tranformation after the current matrix. This is
480 * logically equivalent to the following code:
482 * |[
483 * AdgMatrix tmp_map;
484 * adg_entity_get_global_map(entity, &tmp_map);
485 * cairo_matrix_multiply(&tmp_map, transformation, &tmp_map);
486 * adg_entity_set_global_map(entity, &tmp_map);
487 * ]|
489 void
490 adg_entity_after_global_map(AdgEntity *entity, const AdgMatrix *transformation)
492 AdgEntityPrivate *data;
493 AdgMatrix map;
495 g_return_if_fail(ADG_IS_ENTITY(entity));
496 g_return_if_fail(transformation != NULL);
498 data = entity->data;
499 cairo_matrix_multiply(&map, transformation, &data->global_map);
501 if (set_global_map(entity, &map))
502 g_object_notify((GObject *) entity, "global-map");
506 * adg_entity_get_global_matrix:
507 * @entity: an #AdgEntity object
508 * @matrix: where to store the global matrix
510 * Gets the global matrix by combining all the global maps of the
511 * @entity hierarchy and stores the result in @matrix.
513 void
514 adg_entity_get_global_matrix(AdgEntity *entity, AdgMatrix *matrix)
516 AdgEntityPrivate *data;
518 g_return_if_fail(ADG_IS_ENTITY(entity));
519 g_return_if_fail(matrix != NULL);
521 data = entity->data;
523 adg_matrix_copy(matrix, &data->global_matrix);
527 * adg_entity_set_global_matrix:
528 * @entity: an #AdgEntity object
529 * @matrix: the new global matrix
531 * <note><para>
532 * This function is only useful in entity implementations.
533 * </para></note>
535 * Sets a new global matrix on @entity. This matrix is usually computed
536 * from the global maps of @entity and its parents, so this method is
537 * provided only for entity implementation.
539 void
540 adg_entity_set_global_matrix(AdgEntity *entity, const AdgMatrix *matrix)
542 AdgEntityPrivate *data;
544 g_return_if_fail(ADG_IS_ENTITY(entity));
545 g_return_if_fail(matrix != NULL);
547 data = entity->data;
549 adg_matrix_copy(&data->global_matrix, matrix);
553 * adg_entity_get_local_map:
554 * @entity: an #AdgEntity object
555 * @map: where to store the local map
557 * Gets the transformation to be used to compute the local matrix
558 * of @entity and store it in @map.
560 void
561 adg_entity_get_local_map(AdgEntity *entity, AdgMatrix *map)
563 AdgEntityPrivate *data;
565 g_return_if_fail(ADG_IS_ENTITY(entity));
566 g_return_if_fail(map != NULL);
568 data = entity->data;
570 adg_matrix_copy(map, &data->local_map);
574 * adg_entity_set_local_map:
575 * @entity: an #AdgEntity object
576 * @map: the new map
578 * Sets the new local transformation of @entity to @map:
579 * the old map is discarded. If @map is %NULL an identity
580 * matrix is implied.
582 void
583 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
585 g_return_if_fail(ADG_IS_ENTITY(entity));
587 if (set_local_map(entity, map))
588 g_object_notify((GObject *) entity, "local-map");
592 * adg_entity_before_local_map:
593 * @entity: an #AdgEntity object
594 * @transformation: the transformation to apply
596 * Convenient function to change the local map of @entity by
597 * applying @tranformation before the current matrix. This is
598 * logically equivalent to the following code:
600 * |[
601 * AdgMatrix tmp_map;
602 * adg_entity_get_local_map(entity, &tmp_map);
603 * cairo_matrix_multiply(&tmp_map, &tmp_map, transformation);
604 * adg_entity_set_local_map(entity, &tmp_map);
605 * ]|
607 void
608 adg_entity_before_local_map(AdgEntity *entity, const AdgMatrix *transformation)
610 AdgEntityPrivate *data;
611 AdgMatrix map;
613 g_return_if_fail(ADG_IS_ENTITY(entity));
614 g_return_if_fail(transformation != NULL);
616 data = entity->data;
617 cairo_matrix_multiply(&map, &data->local_map, transformation);
619 if (set_local_map(entity, &map))
620 g_object_notify((GObject *) entity, "local-map");
624 * adg_entity_after_local_map:
625 * @entity: an #AdgEntity object
626 * @transformation: the transformation to apply
628 * Convenient function to change the local map of @entity by
629 * applying @tranformation after the current matrix. This is
630 * logically equivalent to the following code:
632 * |[
633 * AdgMatrix tmp_map;
634 * adg_entity_get_local_map(entity, &tmp_map);
635 * cairo_matrix_multiply(&tmp_map, transformation, &tmp_map);
636 * adg_entity_set_local_map(entity, &tmp_map);
637 * ]|
639 void
640 adg_entity_after_local_map(AdgEntity *entity, const AdgMatrix *transformation)
642 AdgEntityPrivate *data;
643 AdgMatrix map;
645 g_return_if_fail(ADG_IS_ENTITY(entity));
646 g_return_if_fail(transformation != NULL);
648 data = entity->data;
649 cairo_matrix_multiply(&map, transformation, &data->local_map);
651 if (set_local_map(entity, &map))
652 g_object_notify((GObject *) entity, "local-map");
656 * adg_entity_get_local_matrix:
657 * @entity: an #AdgEntity object
658 * @matrix: where to store the local matrix
660 * Computes the local matrix by combining all the local maps of the
661 * @entity hierarchy and stores the result in @matrix.
663 void
664 adg_entity_get_local_matrix(AdgEntity *entity, AdgMatrix *matrix)
666 AdgEntityPrivate *data;
668 g_return_if_fail(ADG_IS_ENTITY(entity));
669 g_return_if_fail(matrix != NULL);
671 data = entity->data;
673 adg_matrix_copy(matrix, &data->local_matrix);
677 * adg_entity_set_local_matrix:
678 * @entity: an #AdgEntity object
679 * @matrix: the new local matrix
681 * <note><para>
682 * This function is only useful in entity implementations.
683 * </para></note>
685 * Sets a new local matrix on @entity. This matrix is usually computed
686 * from the local maps of @entity and its parents, so this method is
687 * provided only for entity implementation.
689 void
690 adg_entity_set_local_matrix(AdgEntity *entity, const AdgMatrix *matrix)
692 AdgEntityPrivate *data;
694 g_return_if_fail(ADG_IS_ENTITY(entity));
695 g_return_if_fail(matrix != NULL);
697 data = entity->data;
699 adg_matrix_copy(&data->local_matrix, matrix);
703 * adg_entity_apply_local_matrix:
704 * @entity: an #AdgEntity object
705 * @cr: a #cairo_t drawing context
707 * Applies the local matrix of @entity BEFORE the current transformation
708 * matrix of @cr. This is a convenience function equivalent to:
710 * |[
711 * AdgMatrix local, ctm;
713 * adg_entity_get_local_matrix(entity, &local);
714 * cairo_get_matrix(cr, &ctm);
716 * cairo_matrix_multiply(&ctm, &ctm, &local);
717 * cairo_set_matrix(cr, &ctm);
718 * ]|
720 void
721 adg_entity_apply_local_matrix(AdgEntity *entity, cairo_t *cr)
723 AdgMatrix local, ctm;
725 g_return_if_fail(ADG_IS_ENTITY(entity));
726 g_return_if_fail(cr != NULL);
728 adg_entity_get_local_matrix(entity, &local);
729 cairo_get_matrix(cr, &ctm);
731 cairo_matrix_multiply(&ctm, &ctm, &local);
732 cairo_set_matrix(cr, &ctm);
736 * adg_entity_get_extents:
737 * @entity: an #AdgEntity
738 * @extents: where to store the extents
740 * Stores a copy of the bounding box of @entity in @extents.
741 * This struct specifies the surface portion (in global space)
742 * occupied by the entity without taking into account line
743 * thickness and caps.
745 * The #AdgEntity::arrange signal will be emitted in order to
746 * update the entity layout before computing the extents.
748 void
749 adg_entity_get_extents(AdgEntity *entity, CpmlExtents *extents)
751 AdgEntityPrivate *data;
753 g_return_if_fail(ADG_IS_ENTITY(entity));
755 data = entity->data;
757 g_signal_emit(entity, signals[ARRANGE], 0);
759 if (extents != NULL)
760 cpml_extents_copy(extents, &data->extents);
764 * adg_entity_set_extents:
765 * @entity: an #AdgEntity
766 * @extents: the new extents
768 * <note><para>
769 * This function is only useful in entity implementations.
770 * </para></note>
772 * Sets a new bounding box for @entity.
774 void
775 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
777 AdgEntityPrivate *data;
779 g_return_if_fail(ADG_IS_ENTITY(entity));
781 data = entity->data;
783 cpml_extents_copy(&data->extents, extents);
787 * adg_entity_style:
788 * @entity: an #AdgEntity
789 * @dress: the dress of the style to get
791 * Gets the style to be used for @entity. @dress specifies the "type"
792 * of style to get.
794 * This method is supposed to always return a valid style instance,
795 * specifically the most appropriate one for this entity. The following
796 * sequence of checks is performed to get the proper @dress style,
797 * stopping at the first succesfull result:
799 * <orderedlist>
800 * <listitem>check if the style is defined directly by this entity type,
801 * as returned by adg_entity_get_style();</listitem>
802 * <listitem>check if @entity has a parent, in which case returns the
803 * adg_entity_style() of the parent;</listitem>
804 * <listitem>returns the main style with adg_dress_get_style().</listitem>
805 * </orderedlist>
807 * Returns: the requested style or %NULL on errors
809 AdgStyle *
810 adg_entity_style(AdgEntity *entity, AdgDress dress)
812 AdgStyle *style;
814 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
815 g_return_val_if_fail(dress != ADG_DRESS_UNDEFINED, NULL);
817 style = adg_entity_get_style(entity, dress);
819 if (style == NULL) {
820 AdgEntityPrivate *data = entity->data;
822 if (data->parent != NULL)
823 style = adg_entity_style(data->parent, dress);
824 else
825 style = adg_dress_get_style(dress);
828 return style;
832 * adg_entity_get_style:
833 * @entity: an #AdgEntity
834 * @dress: the dress of the style to get
836 * Gets the overriden @dress style from @entity. This is a kind
837 * of accessor function: to get the style to be used for rendering
838 * purpose, consider adg_entity_style().
840 * Returns: the requested style or %NULL if the @dress style
841 * is not overriden
843 AdgStyle *
844 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
846 AdgEntityPrivate *data;
848 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
849 g_return_val_if_fail(dress != ADG_DRESS_UNDEFINED, NULL);
851 data = entity->data;
853 if (data->hash_styles == NULL)
854 return NULL;
856 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
860 * adg_entity_set_style:
861 * @entity: an #AdgEntity
862 * @dress: a dress style
863 * @style: the new style to use
865 * Overrides the style of @dress for @entity and its children.
866 * If @style is %NULL, any previous override is removed.
868 void
869 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
871 AdgEntityPrivate *data;
872 gpointer p_dress;
873 AdgStyle *old_style;
875 g_return_if_fail(ADG_IS_ENTITY(entity));
877 data = entity->data;
879 if (data->hash_styles == NULL && style == NULL)
880 return;
882 if (data->hash_styles == NULL)
883 data->hash_styles = g_hash_table_new_full(NULL, NULL,
884 NULL, g_object_unref);
886 p_dress = GINT_TO_POINTER(dress);
887 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
889 if (style == old_style)
890 return;
892 if (style == NULL) {
893 g_hash_table_remove(data->hash_styles, p_dress);
894 } else {
895 g_object_ref(style);
896 g_hash_table_replace(data->hash_styles, p_dress, style);
901 * adg_entity_apply_dress:
902 * @entity: an #AdgEntity
903 * @dress: the dress style to apply
904 * @cr: a #cairo_t drawing context
906 * Convenient function to apply a @dress style (as returned by
907 * adg_entity_style()) to the @cr cairo context.
909 void
910 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
912 g_return_if_fail(ADG_IS_ENTITY(entity));
913 g_return_if_fail(cr != NULL);
915 adg_style_apply(adg_entity_style(entity, dress), cr);
919 * adg_entity_global_changed:
920 * @entity: an #AdgEntity
922 * Emits the #AdgEntity::global-changed signal on @entity and on all of
923 * its children, if any.
925 void
926 adg_entity_global_changed(AdgEntity *entity)
928 g_return_if_fail(ADG_IS_ENTITY(entity));
930 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0);
934 * adg_entity_local_changed:
935 * @entity: an #AdgEntity
937 * Emits the #AdgEntity::local-changed signal on @entity and on all of
938 * its children, if any.
940 void
941 adg_entity_local_changed(AdgEntity *entity)
943 g_return_if_fail(ADG_IS_ENTITY(entity));
945 g_signal_emit(entity, signals[LOCAL_CHANGED], 0);
949 * adg_entity_invalidate:
950 * @entity: an #AdgEntity
952 * Emits the #AdgEntity::invalidate signal on @entity and on all of
953 * its children, if any, clearing the eventual cache stored by the
954 * #AdgEntity::arrange signal and setting the entity state similary
955 * to the just initialized entity.
957 void
958 adg_entity_invalidate(AdgEntity *entity)
960 g_return_if_fail(ADG_IS_ENTITY(entity));
962 g_signal_emit(entity, signals[INVALIDATE], 0);
966 * adg_entity_arrange:
967 * @entity: an #AdgEntity
969 * <note><para>
970 * This function is only useful in entity implementations.
971 * </para></note>
973 * Emits the #AdgEntity::arrange signal on @entity and all its children,
974 * if any. This function is rarely needed as the arrange call is usually
975 * managed by the #AdgEntity::render signal or indirectly by a call to
976 * adg_entity_get_extents().
978 void
979 adg_entity_arrange(AdgEntity *entity)
981 g_return_if_fail(ADG_IS_ENTITY(entity));
983 g_signal_emit(entity, signals[ARRANGE], 0);
987 * adg_entity_render:
988 * @entity: an #AdgEntity
989 * @cr: a #cairo_t drawing context
991 * Emits the #AdgEntity::render signal on @entity and on all of its
992 * children, if any, causing the rendering to the @cr cairo context.
994 void
995 adg_entity_render(AdgEntity *entity, cairo_t *cr)
997 g_return_if_fail(ADG_IS_ENTITY(entity));
999 g_signal_emit(entity, signals[RENDER], 0, cr);
1003 static gboolean
1004 set_parent(AdgEntity *entity, AdgEntity *parent)
1006 AdgEntityPrivate *data;
1007 AdgEntity *old_parent;
1009 data = entity->data;
1010 old_parent = data->parent;
1012 /* Check if parent has changed */
1013 if (parent == old_parent)
1014 return FALSE;
1016 if (parent != NULL)
1017 g_object_ref(parent);
1019 data->parent = parent;
1020 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
1022 if (old_parent != NULL)
1023 g_object_unref(old_parent);
1025 return TRUE;
1028 static void
1029 global_changed(AdgEntity *entity)
1031 AdgEntityPrivate *data;
1032 AdgMatrix *matrix;
1034 data = entity->data;
1035 matrix = &data->global_matrix;
1037 if (data->parent == NULL) {
1038 adg_matrix_copy(matrix, &data->global_map);
1039 } else {
1040 adg_entity_get_global_matrix(data->parent, matrix);
1041 cairo_matrix_multiply(matrix, &data->global_map, matrix);
1045 static void
1046 local_changed(AdgEntity *entity)
1048 AdgEntityPrivate *data;
1049 AdgMatrix *matrix;
1051 data = entity->data;
1052 matrix = &data->local_matrix;
1054 if (data->parent == NULL) {
1055 adg_matrix_copy(matrix, &data->local_map);
1056 } else {
1057 adg_entity_get_local_matrix(data->parent, matrix);
1058 cairo_matrix_multiply(matrix, &data->local_map, matrix);
1062 static gboolean
1063 set_global_map(AdgEntity *entity, const AdgMatrix *map)
1065 AdgEntityPrivate *data = entity->data;
1067 if (map != NULL && adg_matrix_equal(&data->global_map, map))
1068 return FALSE;
1070 if (map == NULL)
1071 cairo_matrix_init_identity(&data->global_map);
1072 else
1073 adg_matrix_copy(&data->global_map, map);
1075 g_signal_emit(entity, signals[GLOBAL_CHANGED], 0, &data->global_matrix);
1076 return TRUE;
1079 static gboolean
1080 set_local_map(AdgEntity *entity, const AdgMatrix *map)
1082 AdgEntityPrivate *data = entity->data;
1084 if (map != NULL && adg_matrix_equal(&data->local_map, map))
1085 return FALSE;
1087 if (map == NULL)
1088 cairo_matrix_init_identity(&data->local_map);
1089 else
1090 adg_matrix_copy(&data->local_map, map);
1092 g_signal_emit(entity, signals[LOCAL_CHANGED], 0, &data->local_matrix);
1093 return TRUE;
1096 static void
1097 real_invalidate(AdgEntity *entity, gpointer user_data)
1099 AdgEntityClass *klass;
1100 AdgEntityPrivate *data;
1102 g_assert(user_data == (gpointer) 0xdeadbeaf);
1104 klass = ADG_ENTITY_GET_CLASS(entity);
1106 /* Do not raise any warning if invalidate() is not defined,
1107 * assuming entity does not have cache to be cleared */
1108 if (klass->invalidate != NULL)
1109 klass->invalidate(entity);
1111 data = entity->data;
1113 data->extents.is_defined = FALSE;
1116 static void
1117 real_arrange(AdgEntity *entity, gpointer user_data)
1119 AdgEntityClass *klass;
1120 AdgEntityPrivate *data;
1122 g_assert(user_data == (gpointer) 0xdeadbeaf);
1124 klass = ADG_ENTITY_GET_CLASS(entity);
1125 if (klass->arrange == NULL) {
1126 /* The arrange() method must be defined */
1127 g_warning("%s: `arrange' method not implemented for type `%s'",
1128 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1129 return;
1132 data = entity->data;
1134 klass->arrange(entity);
1137 static void
1138 real_render(AdgEntity *entity, cairo_t *cr, gpointer user_data)
1140 AdgEntityClass *klass;
1141 AdgEntityPrivate *data;
1143 g_assert(user_data == (gpointer) 0xdeadbeaf);
1145 klass = ADG_ENTITY_GET_CLASS(entity);
1146 if (klass->render == NULL) {
1147 /* The render method must be defined */
1148 g_warning("%s: `render' method not implemented for type `%s'",
1149 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1150 return;
1153 data = entity->data;
1155 cairo_save(cr);
1156 cairo_transform(cr, &data->global_map);
1158 /* Before the rendering, the entity should be arranged */
1159 g_signal_emit(entity, signals[ARRANGE], 0);
1160 klass->render(entity, cr);
1162 cairo_restore(cr);