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.
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.
36 * All fields are private and should not be used directly.
37 * Use its public methods instead.
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.
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"
59 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
80 static void dispose (GObject
*object
);
81 static void get_property (GObject
*object
,
85 static void set_property (GObject
*object
,
89 static gboolean
set_parent (AdgEntity
*entity
,
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
,
99 static void real_arrange (AdgEntity
*entity
,
101 static void real_render (AdgEntity
*entity
,
105 static guint signals
[LAST_SIGNAL
] = { 0 };
108 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
);
112 adg_entity_class_init(AdgEntityClass
*klass
)
114 GObjectClass
*gobject_class
;
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",
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",
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",
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
),
165 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
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
),
181 G_STRUCT_OFFSET(AdgEntityClass
, global_changed
),
183 adg_marshal_VOID__VOID
,
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
),
197 G_STRUCT_OFFSET(AdgEntityClass
, local_changed
),
199 adg_marshal_VOID__VOID
,
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
);
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
);
250 adg_entity_init(AdgEntity
*entity
)
252 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
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
);
268 dispose(GObject
*object
)
271 AdgEntityPrivate
*data
;
273 entity
= (AdgEntity
*) object
;
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
);
291 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
294 AdgEntityPrivate
*data
;
296 entity
= (AdgEntity
*) object
;
301 g_value_set_object(value
, data
->parent
);
303 case PROP_GLOBAL_MAP
:
304 g_value_set_boxed(value
, &data
->global_map
);
307 g_value_set_boxed(value
, &data
->local_map
);
310 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
316 set_property(GObject
*object
,
317 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
319 AdgEntity
*entity
= (AdgEntity
*) object
;
323 set_parent(entity
, g_value_get_object(value
));
325 case PROP_GLOBAL_MAP
:
326 set_global_map(entity
, g_value_get_boxed(value
));
329 set_local_map(entity
, g_value_get_boxed(value
));
332 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
347 adg_entity_get_parent(AdgEntity
*entity
)
349 AdgEntityPrivate
*data
;
351 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
359 * adg_entity_set_parent:
360 * @entity: an #AdgEntity
361 * @parent: the parent entity
364 * This function is only useful in entity implementations.
367 * Sets a new parent on @entity.
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
389 adg_entity_get_canvas(AdgEntity
*entity
)
391 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
394 if (ADG_IS_CANVAS(entity
))
395 return (AdgCanvas
*) entity
;
397 entity
= (AdgEntity
*) adg_entity_get_parent(entity
);
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.
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
);
420 adg_matrix_copy(map
, &data
->global_map
);
424 * adg_entity_set_global_map:
425 * @entity: an #AdgEntity object
428 * Sets the new global transformation of @entity to @map:
429 * the old map is discarded. If @map is %NULL an identity
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_transform_global_map:
443 * @entity: an #AdgEntity object
444 * @transformation: the transformation to apply
445 * @mode: how @transformation should be applied
447 * Convenient function to change the global map of @entity by
448 * applying @tranformation using the @mode operator. This is
449 * logically equivalent to the following:
453 * adg_entity_get_global_map(entity, &tmp_map);
454 * adg_matrix_transform(&tmp_map, transformation, mode);
455 * adg_entity_set_global_map(entity, &tmp_map);
459 adg_entity_transform_global_map(AdgEntity
*entity
,
460 const AdgMatrix
*transformation
,
461 AdgTransformationMode mode
)
463 AdgEntityPrivate
*data
;
466 g_return_if_fail(ADG_IS_ENTITY(entity
));
467 g_return_if_fail(transformation
!= NULL
);
471 adg_matrix_copy(&map
, &data
->global_map
);
472 adg_matrix_transform(&map
, transformation
, mode
);
474 if (set_global_map(entity
, &map
))
475 g_object_notify((GObject
*) entity
, "global-map");
479 * adg_entity_get_global_matrix:
480 * @entity: an #AdgEntity object
481 * @matrix: where to store the global matrix
483 * Gets the global matrix by combining all the global maps of the
484 * @entity hierarchy and stores the result in @matrix.
487 adg_entity_get_global_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
489 AdgEntityPrivate
*data
;
491 g_return_if_fail(ADG_IS_ENTITY(entity
));
492 g_return_if_fail(matrix
!= NULL
);
496 adg_matrix_copy(matrix
, &data
->global_matrix
);
500 * adg_entity_set_global_matrix:
501 * @entity: an #AdgEntity object
502 * @matrix: the new global matrix
505 * This function is only useful in entity implementations.
508 * Sets a new global matrix on @entity. This matrix is usually computed
509 * from the global maps of @entity and its parents, so this method is
510 * provided only for entity implementation.
513 adg_entity_set_global_matrix(AdgEntity
*entity
, const AdgMatrix
*matrix
)
515 AdgEntityPrivate
*data
;
517 g_return_if_fail(ADG_IS_ENTITY(entity
));
518 g_return_if_fail(matrix
!= NULL
);
522 adg_matrix_copy(&data
->global_matrix
, matrix
);
526 * adg_entity_get_local_map:
527 * @entity: an #AdgEntity object
528 * @map: where to store the local map
530 * Gets the transformation to be used to compute the local matrix
531 * of @entity and store it in @map.
534 adg_entity_get_local_map(AdgEntity
*entity
, AdgMatrix
*map
)
536 AdgEntityPrivate
*data
;
538 g_return_if_fail(ADG_IS_ENTITY(entity
));
539 g_return_if_fail(map
!= NULL
);
543 adg_matrix_copy(map
, &data
->local_map
);
547 * adg_entity_set_local_map:
548 * @entity: an #AdgEntity object
551 * Sets the new local transformation of @entity to @map:
552 * the old map is discarded. If @map is %NULL an identity
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:
576 * adg_entity_get_local_map(entity, &tmp_map);
577 * adg_matrix_transform(&tmp_map, transformation, mode);
578 * adg_entity_set_local_map(entity, &tmp_map);
582 adg_entity_transform_local_map(AdgEntity
*entity
,
583 const AdgMatrix
*transformation
,
584 AdgTransformationMode mode
)
586 AdgEntityPrivate
*data
;
589 g_return_if_fail(ADG_IS_ENTITY(entity
));
590 g_return_if_fail(transformation
!= NULL
);
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_matrix:
603 * @entity: an #AdgEntity object
604 * @matrix: where to store the local matrix
606 * Computes the local matrix by combining all the local maps of the
607 * @entity hierarchy and stores the result in @matrix.
610 adg_entity_get_local_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
612 AdgEntityPrivate
*data
;
614 g_return_if_fail(ADG_IS_ENTITY(entity
));
615 g_return_if_fail(matrix
!= NULL
);
619 adg_matrix_copy(matrix
, &data
->local_matrix
);
623 * adg_entity_set_local_matrix:
624 * @entity: an #AdgEntity object
625 * @matrix: the new local matrix
628 * This function is only useful in entity implementations.
631 * Sets a new local matrix on @entity. This matrix is usually computed
632 * from the local maps of @entity and its parents, so this method is
633 * provided only for entity implementation.
636 adg_entity_set_local_matrix(AdgEntity
*entity
, const AdgMatrix
*matrix
)
638 AdgEntityPrivate
*data
;
640 g_return_if_fail(ADG_IS_ENTITY(entity
));
641 g_return_if_fail(matrix
!= NULL
);
645 adg_matrix_copy(&data
->local_matrix
, matrix
);
649 * adg_entity_apply_local_matrix:
650 * @entity: an #AdgEntity object
651 * @cr: a #cairo_t drawing context
653 * Applies the local matrix of @entity BEFORE the current transformation
654 * matrix of @cr. This is a convenience function equivalent to:
657 * AdgMatrix local, ctm;
659 * adg_entity_get_local_matrix(entity, &local);
660 * cairo_get_matrix(cr, &ctm);
662 * cairo_matrix_multiply(&ctm, &ctm, &local);
663 * cairo_set_matrix(cr, &ctm);
667 adg_entity_apply_local_matrix(AdgEntity
*entity
, cairo_t
*cr
)
669 AdgMatrix local
, ctm
;
671 g_return_if_fail(ADG_IS_ENTITY(entity
));
672 g_return_if_fail(cr
!= NULL
);
674 adg_entity_get_local_matrix(entity
, &local
);
675 cairo_get_matrix(cr
, &ctm
);
677 cairo_matrix_multiply(&ctm
, &ctm
, &local
);
678 cairo_set_matrix(cr
, &ctm
);
682 * adg_entity_get_extents:
683 * @entity: an #AdgEntity
684 * @extents: where to store the extents
686 * Stores a copy of the bounding box of @entity in @extents.
687 * This struct specifies the surface portion (in global space)
688 * occupied by the entity without taking into account line
689 * thickness and caps.
691 * The #AdgEntity::arrange signal will be emitted in order to
692 * update the entity layout before computing the extents.
695 adg_entity_get_extents(AdgEntity
*entity
, CpmlExtents
*extents
)
697 AdgEntityPrivate
*data
;
699 g_return_if_fail(ADG_IS_ENTITY(entity
));
703 g_signal_emit(entity
, signals
[ARRANGE
], 0);
706 cpml_extents_copy(extents
, &data
->extents
);
710 * adg_entity_set_extents:
711 * @entity: an #AdgEntity
712 * @extents: the new extents
715 * This function is only useful in entity implementations.
718 * Sets a new bounding box for @entity.
721 adg_entity_set_extents(AdgEntity
*entity
, const CpmlExtents
*extents
)
723 AdgEntityPrivate
*data
;
725 g_return_if_fail(ADG_IS_ENTITY(entity
));
729 cpml_extents_copy(&data
->extents
, extents
);
734 * @entity: an #AdgEntity
735 * @dress: the dress of the style to get
737 * Gets the style to be used for @entity. @dress specifies the "type"
740 * This method is supposed to always return a valid style instance,
741 * specifically the most appropriate one for this entity. The following
742 * sequence of checks is performed to get the proper @dress style,
743 * stopping at the first succesfull result:
746 * <listitem>check if the style is defined directly by this entity type,
747 * as returned by adg_entity_get_style();</listitem>
748 * <listitem>check if @entity has a parent, in which case returns the
749 * adg_entity_style() of the parent;</listitem>
750 * <listitem>returns the main style with adg_dress_get_style().</listitem>
753 * Returns: the requested style or %NULL on errors
756 adg_entity_style(AdgEntity
*entity
, AdgDress dress
)
760 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
761 g_return_val_if_fail(dress
!= ADG_DRESS_UNDEFINED
, NULL
);
763 style
= adg_entity_get_style(entity
, dress
);
766 AdgEntityPrivate
*data
= entity
->data
;
768 if (data
->parent
!= NULL
)
769 style
= adg_entity_style(data
->parent
, dress
);
771 style
= adg_dress_get_style(dress
);
778 * adg_entity_get_style:
779 * @entity: an #AdgEntity
780 * @dress: the dress of the style to get
782 * Gets the overriden @dress style from @entity. This is a kind
783 * of accessor function: to get the style to be used for rendering
784 * purpose, consider adg_entity_style().
786 * Returns: the requested style or %NULL if the @dress style
790 adg_entity_get_style(AdgEntity
*entity
, AdgDress dress
)
792 AdgEntityPrivate
*data
;
794 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
795 g_return_val_if_fail(dress
!= ADG_DRESS_UNDEFINED
, NULL
);
799 if (data
->hash_styles
== NULL
)
802 return g_hash_table_lookup(data
->hash_styles
, GINT_TO_POINTER(dress
));
806 * adg_entity_set_style:
807 * @entity: an #AdgEntity
808 * @dress: a dress style
809 * @style: the new style to use
811 * Overrides the style of @dress for @entity and its children.
812 * If @style is %NULL, any previous override is removed.
815 adg_entity_set_style(AdgEntity
*entity
, AdgDress dress
, AdgStyle
*style
)
817 AdgEntityPrivate
*data
;
821 g_return_if_fail(ADG_IS_ENTITY(entity
));
825 if (data
->hash_styles
== NULL
&& style
== NULL
)
828 if (data
->hash_styles
== NULL
)
829 data
->hash_styles
= g_hash_table_new_full(NULL
, NULL
,
830 NULL
, g_object_unref
);
832 p_dress
= GINT_TO_POINTER(dress
);
833 old_style
= g_hash_table_lookup(data
->hash_styles
, p_dress
);
835 if (style
== old_style
)
839 g_hash_table_remove(data
->hash_styles
, p_dress
);
842 g_hash_table_replace(data
->hash_styles
, p_dress
, style
);
847 * adg_entity_apply_dress:
848 * @entity: an #AdgEntity
849 * @dress: the dress style to apply
850 * @cr: a #cairo_t drawing context
852 * Convenient function to apply a @dress style (as returned by
853 * adg_entity_style()) to the @cr cairo context.
856 adg_entity_apply_dress(AdgEntity
*entity
, AdgDress dress
, cairo_t
*cr
)
858 g_return_if_fail(ADG_IS_ENTITY(entity
));
859 g_return_if_fail(cr
!= NULL
);
861 adg_style_apply(adg_entity_style(entity
, dress
), cr
);
865 * adg_entity_global_changed:
866 * @entity: an #AdgEntity
868 * Emits the #AdgEntity::global-changed signal on @entity and on all of
869 * its children, if any.
872 adg_entity_global_changed(AdgEntity
*entity
)
874 g_return_if_fail(ADG_IS_ENTITY(entity
));
876 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0);
880 * adg_entity_local_changed:
881 * @entity: an #AdgEntity
883 * Emits the #AdgEntity::local-changed signal on @entity and on all of
884 * its children, if any.
887 adg_entity_local_changed(AdgEntity
*entity
)
889 g_return_if_fail(ADG_IS_ENTITY(entity
));
891 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0);
895 * adg_entity_invalidate:
896 * @entity: an #AdgEntity
898 * Emits the #AdgEntity::invalidate signal on @entity and on all of
899 * its children, if any, clearing the eventual cache stored by the
900 * #AdgEntity::arrange signal and setting the entity state similary
901 * to the just initialized entity.
904 adg_entity_invalidate(AdgEntity
*entity
)
906 g_return_if_fail(ADG_IS_ENTITY(entity
));
908 g_signal_emit(entity
, signals
[INVALIDATE
], 0);
912 * adg_entity_arrange:
913 * @entity: an #AdgEntity
916 * This function is only useful in entity implementations.
919 * Emits the #AdgEntity::arrange signal on @entity and all its children,
920 * if any. This function is rarely needed as the arrange call is usually
921 * managed by the #AdgEntity::render signal or indirectly by a call to
922 * adg_entity_get_extents().
925 adg_entity_arrange(AdgEntity
*entity
)
927 g_return_if_fail(ADG_IS_ENTITY(entity
));
929 g_signal_emit(entity
, signals
[ARRANGE
], 0);
934 * @entity: an #AdgEntity
935 * @cr: a #cairo_t drawing context
937 * Emits the #AdgEntity::render signal on @entity and on all of its
938 * children, if any, causing the rendering to the @cr cairo context.
941 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
943 g_return_if_fail(ADG_IS_ENTITY(entity
));
945 g_signal_emit(entity
, signals
[RENDER
], 0, cr
);
950 set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
952 AdgEntityPrivate
*data
;
953 AdgEntity
*old_parent
;
956 old_parent
= data
->parent
;
958 /* Check if parent has changed */
959 if (parent
== old_parent
)
963 g_object_ref(parent
);
965 data
->parent
= parent
;
966 g_signal_emit(entity
, signals
[PARENT_SET
], 0, old_parent
);
968 if (old_parent
!= NULL
)
969 g_object_unref(old_parent
);
975 global_changed(AdgEntity
*entity
)
977 AdgEntityPrivate
*data
;
981 matrix
= &data
->global_matrix
;
983 if (data
->parent
== NULL
) {
984 adg_matrix_copy(matrix
, &data
->global_map
);
986 adg_entity_get_global_matrix(data
->parent
, matrix
);
987 cairo_matrix_multiply(matrix
, &data
->global_map
, matrix
);
992 local_changed(AdgEntity
*entity
)
994 AdgEntityPrivate
*data
;
998 matrix
= &data
->local_matrix
;
1000 if (data
->parent
== NULL
) {
1001 adg_matrix_copy(matrix
, &data
->local_map
);
1003 adg_entity_get_local_matrix(data
->parent
, matrix
);
1004 cairo_matrix_multiply(matrix
, &data
->local_map
, matrix
);
1009 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1011 AdgEntityPrivate
*data
= entity
->data
;
1013 if (map
!= NULL
&& adg_matrix_equal(&data
->global_map
, map
))
1017 cairo_matrix_init_identity(&data
->global_map
);
1019 adg_matrix_copy(&data
->global_map
, map
);
1021 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0, &data
->global_matrix
);
1026 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1028 AdgEntityPrivate
*data
= entity
->data
;
1030 if (map
!= NULL
&& adg_matrix_equal(&data
->local_map
, map
))
1034 cairo_matrix_init_identity(&data
->local_map
);
1036 adg_matrix_copy(&data
->local_map
, map
);
1038 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0, &data
->local_matrix
);
1043 real_invalidate(AdgEntity
*entity
, gpointer user_data
)
1045 AdgEntityClass
*klass
;
1046 AdgEntityPrivate
*data
;
1048 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
1050 klass
= ADG_ENTITY_GET_CLASS(entity
);
1052 /* Do not raise any warning if invalidate() is not defined,
1053 * assuming entity does not have cache to be cleared */
1054 if (klass
->invalidate
!= NULL
)
1055 klass
->invalidate(entity
);
1057 data
= entity
->data
;
1059 data
->extents
.is_defined
= FALSE
;
1063 real_arrange(AdgEntity
*entity
, gpointer user_data
)
1065 AdgEntityClass
*klass
;
1066 AdgEntityPrivate
*data
;
1068 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
1070 klass
= ADG_ENTITY_GET_CLASS(entity
);
1071 if (klass
->arrange
== NULL
) {
1072 /* The arrange() method must be defined */
1073 g_warning("%s: `arrange' method not implemented for type `%s'",
1074 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1078 data
= entity
->data
;
1080 klass
->arrange(entity
);
1084 real_render(AdgEntity
*entity
, cairo_t
*cr
, gpointer user_data
)
1086 AdgEntityClass
*klass
;
1087 AdgEntityPrivate
*data
;
1089 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
1091 klass
= ADG_ENTITY_GET_CLASS(entity
);
1092 if (klass
->render
== NULL
) {
1093 /* The render method must be defined */
1094 g_warning("%s: `render' method not implemented for type `%s'",
1095 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1099 data
= entity
->data
;
1102 cairo_transform(cr
, &data
->global_map
);
1104 /* Before the rendering, the entity should be arranged */
1105 g_signal_emit(entity
, signals
[ARRANGE
], 0);
1106 klass
->render(entity
, cr
);