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)
81 static void dispose (GObject
*object
);
82 static void get_property (GObject
*object
,
86 static void set_property (GObject
*object
,
90 static gboolean
set_parent (AdgEntity
*entity
,
92 static void global_changed (AdgEntity
*entity
);
93 static void local_changed (AdgEntity
*entity
);
94 static gboolean
set_global_map (AdgEntity
*entity
,
95 const AdgMatrix
*map
);
96 static gboolean
set_local_map (AdgEntity
*entity
,
97 const AdgMatrix
*map
);
98 static gboolean
set_local_mode (AdgEntity
*entity
,
99 AdgTransformationMode mode
);
100 static void real_invalidate (AdgEntity
*entity
);
101 static void real_arrange (AdgEntity
*entity
);
102 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"),
139 g_object_class_install_property(gobject_class
, PROP_PARENT
, param
);
141 param
= g_param_spec_boxed("global-map",
143 P_("The transformation to be combined with the parent ones to get the global matrix"),
146 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
148 param
= g_param_spec_boxed("local-map",
150 P_("The transformation to be combined with the parent ones to get the local matrix"),
153 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
155 param
= g_param_spec_enum("local-mode",
157 P_("How the local matrix should be combined with the global matrix to get the current transformation matrix (ctm)"),
158 ADG_TYPE_TRANSFORMATION_MODE
,
161 g_object_class_install_property(gobject_class
, PROP_LOCAL_MODE
, param
);
164 * AdgEntity::parent-set:
165 * @entity: an #AdgEntity
166 * @old_parent: the old parent
168 * Emitted after the parent entity has changed. The new parent
169 * can be inspected using adg_entity_get_parent().
171 * It is allowed for both old and new parent to be %NULL.
173 signals
[PARENT_SET
] = g_signal_new("parent-set",
174 G_OBJECT_CLASS_TYPE(gobject_class
),
176 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
178 adg_marshal_VOID__OBJECT
,
179 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
182 * AdgEntity::global-changed
183 * @entity: an #AdgEntity
185 * Emitted when the global map of @entity or any of its parent
186 * has changed. The default handler will compute the new global
187 * matrix, updating the internal cache.
189 signals
[GLOBAL_CHANGED
] = g_signal_new("global-changed",
190 G_OBJECT_CLASS_TYPE(gobject_class
),
192 G_STRUCT_OFFSET(AdgEntityClass
, global_changed
),
194 adg_marshal_VOID__VOID
,
198 * AdgEntity::local-changed
199 * @entity: an #AdgEntity
201 * Emitted when the local map of @entity or any of its parent
202 * has changed. The default handler will compute the new local
203 * matrix, updating the internal cache.
205 signals
[LOCAL_CHANGED
] = g_signal_new("local-changed",
206 G_OBJECT_CLASS_TYPE(gobject_class
),
208 G_STRUCT_OFFSET(AdgEntityClass
, local_changed
),
210 adg_marshal_VOID__VOID
,
214 * AdgEntity::invalidate:
215 * @entity: an #AdgEntity
217 * Invalidates the whole @entity, that is resets all the cache
218 * (if present) built during the #AdgEntity::arrange signal.
219 * The resulting state is a clean entity, similar to what you
220 * have just before the first rendering.
222 closure
= g_cclosure_new(G_CALLBACK(real_invalidate
), NULL
, NULL
);
223 signals
[INVALIDATE
] = g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
224 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
225 adg_marshal_VOID__VOID
,
226 G_TYPE_NONE
, 0, param_types
);
229 * AdgEntity::arrange:
230 * @entity: an #AdgEntity
232 * Arranges the layout of @entity, updating the cache if necessary,
233 * and computes the extents of @entity.
235 closure
= g_cclosure_new(G_CALLBACK(real_arrange
), NULL
, NULL
);
236 signals
[ARRANGE
] = g_signal_newv("arrange", ADG_TYPE_ENTITY
,
237 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
238 adg_marshal_VOID__VOID
,
239 G_TYPE_NONE
, 0, param_types
);
242 * @entity: an #AdgEntity
243 * @cr: a #cairo_t drawing context
245 * Causes the rendering of @entity on @cr. A render signal will
246 * automatically emit #AdgEntity::arrange just before the real
247 * rendering on the cairo context.
249 closure
= g_cclosure_new(G_CALLBACK(real_render
), NULL
, NULL
);
250 param_types
[0] = G_TYPE_POINTER
;
251 signals
[RENDER
] = g_signal_newv("render", ADG_TYPE_ENTITY
,
252 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
253 adg_marshal_VOID__POINTER
,
254 G_TYPE_NONE
, 1, param_types
);
258 adg_entity_init(AdgEntity
*entity
)
260 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
264 cairo_matrix_init_identity(&data
->global_map
);
265 cairo_matrix_init_identity(&data
->local_map
);
266 data
->local_mode
= ADG_TRANSFORM_NONE
;
267 data
->hash_styles
= NULL
;
268 cairo_matrix_init_identity(&data
->global_matrix
);
269 cairo_matrix_init_identity(&data
->local_matrix
);
270 data
->extents
.is_defined
= FALSE
;
276 dispose(GObject
*object
)
279 AdgEntityPrivate
*data
;
281 entity
= (AdgEntity
*) object
;
284 /* This call will emit a "notify" signal for parent.
285 * Consequentially, the references to the old parent is dropped. */
286 adg_entity_set_parent(entity
, NULL
);
288 if (data
->hash_styles
!= NULL
) {
289 g_hash_table_destroy(data
->hash_styles
);
290 data
->hash_styles
= NULL
;
293 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
294 PARENT_OBJECT_CLASS
->dispose(object
);
299 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
302 AdgEntityPrivate
*data
;
304 entity
= (AdgEntity
*) object
;
309 g_value_set_object(value
, data
->parent
);
311 case PROP_GLOBAL_MAP
:
312 g_value_set_boxed(value
, &data
->global_map
);
315 g_value_set_boxed(value
, &data
->local_map
);
317 case PROP_LOCAL_MODE
:
318 g_value_set_enum(value
, data
->local_mode
);
321 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
327 set_property(GObject
*object
,
328 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
330 AdgEntity
*entity
= (AdgEntity
*) object
;
334 set_parent(entity
, g_value_get_object(value
));
336 case PROP_GLOBAL_MAP
:
337 set_global_map(entity
, g_value_get_boxed(value
));
340 set_local_map(entity
, g_value_get_boxed(value
));
342 case PROP_LOCAL_MODE
:
343 set_local_mode(entity
, g_value_get_enum(value
));
346 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
353 * adg_entity_get_parent:
354 * @entity: an #AdgEntity
356 * Gets the parent of @entity.
358 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
361 adg_entity_get_parent(AdgEntity
*entity
)
363 AdgEntityPrivate
*data
;
365 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
373 * adg_entity_set_parent:
374 * @entity: an #AdgEntity
375 * @parent: the parent entity
378 * This function is only useful in entity implementations.
381 * Sets a new parent on @entity.
384 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
386 g_return_if_fail(ADG_IS_ENTITY(entity
));
388 if (set_parent(entity
, parent
))
389 g_object_notify((GObject
*) entity
, "parent");
393 * adg_entity_get_canvas:
394 * @entity: an #AdgEntity
396 * Walks on the @entity hierarchy and gets the first parent of @entity that is
397 * of #AdgCanvas derived type.
399 * Returns: the requested canvas or %NULL on errors or if there is
400 * no #AdgCanvas in the @entity hierarchy
403 adg_entity_get_canvas(AdgEntity
*entity
)
405 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
408 if (ADG_IS_CANVAS(entity
))
409 return (AdgCanvas
*) entity
;
411 entity
= (AdgEntity
*) adg_entity_get_parent(entity
);
418 * adg_entity_get_global_map:
419 * @entity: an #AdgEntity object
420 * @map: where to store the global map
422 * Gets the transformation to be used to compute the global matrix
423 * of @entity and store it in @map.
426 adg_entity_get_global_map(AdgEntity
*entity
, AdgMatrix
*map
)
428 AdgEntityPrivate
*data
;
430 g_return_if_fail(ADG_IS_ENTITY(entity
));
431 g_return_if_fail(map
!= NULL
);
434 adg_matrix_copy(map
, &data
->global_map
);
438 * adg_entity_set_global_map:
439 * @entity: an #AdgEntity object
442 * Sets the new global transformation of @entity to @map:
443 * the old map is discarded. If @map is %NULL an identity
447 adg_entity_set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
449 g_return_if_fail(ADG_IS_ENTITY(entity
));
451 if (set_global_map(entity
, map
))
452 g_object_notify((GObject
*) entity
, "global-map");
456 * adg_entity_transform_global_map:
457 * @entity: an #AdgEntity object
458 * @transformation: the transformation to apply
459 * @mode: how @transformation should be applied
461 * Convenient function to change the global map of @entity by
462 * applying @tranformation using the @mode operator. This is
463 * logically equivalent to the following:
467 * adg_entity_get_global_map(entity, &tmp_map);
468 * adg_matrix_transform(&tmp_map, transformation, mode);
469 * adg_entity_set_global_map(entity, &tmp_map);
473 adg_entity_transform_global_map(AdgEntity
*entity
,
474 const AdgMatrix
*transformation
,
475 AdgTransformationMode mode
)
477 AdgEntityPrivate
*data
;
480 g_return_if_fail(ADG_IS_ENTITY(entity
));
481 g_return_if_fail(transformation
!= NULL
);
485 adg_matrix_copy(&map
, &data
->global_map
);
486 adg_matrix_transform(&map
, transformation
, mode
);
488 if (set_global_map(entity
, &map
))
489 g_object_notify((GObject
*) entity
, "global-map");
493 * adg_entity_get_local_map:
494 * @entity: an #AdgEntity object
495 * @map: where to store the local map
497 * Gets the transformation to be used to compute the local matrix
498 * of @entity and store it in @map.
501 adg_entity_get_local_map(AdgEntity
*entity
, AdgMatrix
*map
)
503 AdgEntityPrivate
*data
;
505 g_return_if_fail(ADG_IS_ENTITY(entity
));
506 g_return_if_fail(map
!= NULL
);
510 adg_matrix_copy(map
, &data
->local_map
);
514 * adg_entity_set_local_map:
515 * @entity: an #AdgEntity object
518 * Sets the new local transformation of @entity to @map:
519 * the old map is discarded. If @map is %NULL an identity
523 adg_entity_set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
525 g_return_if_fail(ADG_IS_ENTITY(entity
));
527 if (set_local_map(entity
, map
))
528 g_object_notify((GObject
*) entity
, "local-map");
532 * adg_entity_transform_local_map:
533 * @entity: an #AdgEntity object
534 * @transformation: the transformation to apply
535 * @mode: how @transformation should be applied
537 * Convenient function to change the local map of @entity by
538 * applying @tranformation using the @mode operator. This is
539 * logically equivalent to the following:
543 * adg_entity_get_local_map(entity, &tmp_map);
544 * adg_matrix_transform(&tmp_map, transformation, mode);
545 * adg_entity_set_local_map(entity, &tmp_map);
549 adg_entity_transform_local_map(AdgEntity
*entity
,
550 const AdgMatrix
*transformation
,
551 AdgTransformationMode mode
)
553 AdgEntityPrivate
*data
;
556 g_return_if_fail(ADG_IS_ENTITY(entity
));
557 g_return_if_fail(transformation
!= NULL
);
561 adg_matrix_copy(&map
, &data
->local_map
);
562 adg_matrix_transform(&map
, transformation
, mode
);
564 if (set_local_map(entity
, &map
))
565 g_object_notify((GObject
*) entity
, "local-map");
569 * adg_entity_get_local_mode:
570 * @entity: an #AdgEntity object
572 * Gets the transformation mode to be used while applying the
573 * local matrix over the global matrix. It is internally used
574 * by adg_entity_get_ctm().
576 * Returns: the current transformation mode
578 AdgTransformationMode
579 adg_entity_get_local_mode(AdgEntity
*entity
)
581 AdgEntityPrivate
*data
;
583 g_return_val_if_fail(ADG_IS_ENTITY(entity
), ADG_TRANSFORM_NONE
);
587 return data
->local_mode
;
591 * adg_entity_set_local_mode:
592 * @entity: an #AdgEntity object
593 * @mode: the new mode
595 * Sets a new transformation mode to be used while combining
596 * global and local matrices.
597 * Sets a new transformation mode to be used while applying
598 * the local matrix over the global matrix. It is internally
599 * used by adg_entity_get_ctm().
602 adg_entity_set_local_mode(AdgEntity
*entity
, AdgTransformationMode mode
)
604 g_return_if_fail(ADG_IS_ENTITY(entity
));
606 if (set_local_mode(entity
, mode
))
607 g_object_notify((GObject
*) entity
, "local-mode");
611 * adg_entity_get_extents:
612 * @entity: an #AdgEntity
613 * @extents: where to store the extents
615 * Stores a copy of the bounding box of @entity in @extents.
616 * This struct specifies the surface portion (in global space)
617 * occupied by the entity without taking into account line
618 * thickness and caps.
620 * The #AdgEntity::arrange should be emitted before this call
621 * (either explicitely or throught adg_entity_arrange()) in
622 * order to get an up to date boundary box.
625 adg_entity_get_extents(AdgEntity
*entity
, CpmlExtents
*extents
)
627 AdgEntityPrivate
*data
;
629 g_return_if_fail(ADG_IS_ENTITY(entity
));
630 g_return_if_fail(extents
!= NULL
);
634 cpml_extents_copy(extents
, &data
->extents
);
638 * adg_entity_set_extents:
639 * @entity: an #AdgEntity
640 * @extents: the new extents
643 * This function is only useful in entity implementations.
646 * Sets a new bounding box for @entity. @extents can be %NULL,
647 * in which case the extents are unset.
650 adg_entity_set_extents(AdgEntity
*entity
, const CpmlExtents
*extents
)
652 AdgEntityPrivate
*data
;
654 g_return_if_fail(ADG_IS_ENTITY(entity
));
659 data
->extents
.is_defined
= FALSE
;
661 cpml_extents_copy(&data
->extents
, extents
);
666 * @entity: an #AdgEntity
667 * @dress: the dress of the style to get
669 * Gets the style to be used for @entity. @dress specifies which
670 * "family" of style to get.
672 * The following sequence of checks is performed to get the proper
673 * style, stopping at the first succesfull result:
676 * <listitem>check if the style is directly overriden by this entity,
677 * as returned by adg_entity_get_style();</listitem>
678 * <listitem>check if @entity has a parent, in which case returns the
679 * adg_entity_style() of the parent;</listitem>
680 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
683 * Returns: the requested style or %NULL for transparent dresses or errors
686 adg_entity_style(AdgEntity
*entity
, AdgDress dress
)
690 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
692 style
= adg_entity_get_style(entity
, dress
);
695 AdgEntityPrivate
*data
= entity
->data
;
697 if (data
->parent
!= NULL
)
698 style
= adg_entity_style(data
->parent
, dress
);
700 style
= adg_dress_get_fallback(dress
);
707 * adg_entity_get_style:
708 * @entity: an #AdgEntity
709 * @dress: the dress of the style to get
711 * Gets the overriden @dress style from @entity. This is a kind
712 * of accessor function: to get the style to be used for rendering
713 * purpose, use adg_entity_style() instead.
715 * Returns: the requested style or %NULL if the @dress style
719 adg_entity_get_style(AdgEntity
*entity
, AdgDress dress
)
721 AdgEntityPrivate
*data
;
723 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
727 if (data
->hash_styles
== NULL
)
730 return g_hash_table_lookup(data
->hash_styles
, GINT_TO_POINTER(dress
));
734 * adg_entity_set_style:
735 * @entity: an #AdgEntity
736 * @dress: a dress style
737 * @style: the new style to use
739 * Overrides the style of @dress for @entity and its children.
740 * If @style is %NULL, any previous override is removed.
742 * The new style must still be compatible with @dress: check out
743 * the adg_dress_style_is_compatible() documentation to know
744 * what a compatible style means.
747 adg_entity_set_style(AdgEntity
*entity
, AdgDress dress
, AdgStyle
*style
)
749 AdgEntityPrivate
*data
;
753 g_return_if_fail(ADG_IS_ENTITY(entity
));
757 if (data
->hash_styles
== NULL
&& style
== NULL
)
760 if (data
->hash_styles
== NULL
)
761 data
->hash_styles
= g_hash_table_new_full(NULL
, NULL
,
762 NULL
, g_object_unref
);
764 p_dress
= GINT_TO_POINTER(dress
);
765 old_style
= g_hash_table_lookup(data
->hash_styles
, p_dress
);
767 if (style
== old_style
)
771 g_hash_table_remove(data
->hash_styles
, p_dress
);
775 if (!adg_dress_style_is_compatible(dress
, style
)) {
776 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
778 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
779 G_STRLOC
, g_type_name(G_TYPE_FROM_INSTANCE(style
)),
780 g_type_name(ancestor_type
), adg_dress_name(dress
));
786 g_hash_table_replace(data
->hash_styles
, p_dress
, style
);
790 * adg_entity_apply_dress:
791 * @entity: an #AdgEntity
792 * @dress: the dress style to apply
793 * @cr: a #cairo_t drawing context
795 * Convenient function to apply a @dress style (as returned by
796 * adg_entity_style()) to the @cr cairo context.
799 adg_entity_apply_dress(AdgEntity
*entity
, AdgDress dress
, cairo_t
*cr
)
803 g_return_if_fail(ADG_IS_ENTITY(entity
));
804 g_return_if_fail(cr
!= NULL
);
806 style
= adg_entity_style(entity
, dress
);
809 adg_style_apply(style
, cr
);
813 * adg_entity_global_changed:
814 * @entity: an #AdgEntity
816 * Emits the #AdgEntity::global-changed signal on @entity and on all of
817 * its children, if any.
820 adg_entity_global_changed(AdgEntity
*entity
)
822 g_return_if_fail(ADG_IS_ENTITY(entity
));
824 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0);
828 * adg_entity_local_changed:
829 * @entity: an #AdgEntity
831 * Emits the #AdgEntity::local-changed signal on @entity and on all of
832 * its children, if any.
835 adg_entity_local_changed(AdgEntity
*entity
)
837 g_return_if_fail(ADG_IS_ENTITY(entity
));
839 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0);
843 * adg_entity_global_matrix:
844 * @entity: an #AdgEntity object
846 * Gets the global matrix by combining all the global maps of the
847 * @entity hierarchy. The returned value is owned by @entity and
848 * should not be changed or freed.
850 * Returns: the global matrix or %NULL on errors
853 adg_entity_global_matrix(AdgEntity
*entity
)
855 AdgEntityPrivate
*data
;
857 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
861 return &data
->global_matrix
;
865 * adg_entity_local_matrix:
866 * @entity: an #AdgEntity object
867 * @matrix: where to store the local matrix
869 * Gets the local matrix by combining all the local maps of the
870 * @entity hierarchy. The returned value is owned by @entity and
871 * should not be changed or freed.
873 * Returns: the local matrix or %NULL on errors
876 adg_entity_local_matrix(AdgEntity
*entity
)
878 AdgEntityPrivate
*data
;
880 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
884 return &data
->local_matrix
;
888 * adg_entity_get_ctm:
889 * @entity: an #AdgEntity object
890 * @ctm: where to store the current transformation matrix
892 * Gets the current transformation matrix (ctm) of @entity
893 * by combining global and local matrices in the way specified
894 * by the #AdgEntity:local-mode property. The result is stored
897 * This method is only useful after the #AdgEntity::arrange default
898 * handler has been called, that is at the rendering stage or while
899 * arranging the entity after the parent method has been chained up.
902 adg_entity_get_ctm(AdgEntity
*entity
, AdgMatrix
*ctm
)
904 AdgEntityPrivate
*data
;
906 g_return_if_fail(ADG_IS_ENTITY(entity
));
907 g_return_if_fail(ctm
!= NULL
);
911 adg_matrix_copy(ctm
, &data
->global_matrix
);
912 adg_matrix_transform(ctm
, &data
->local_matrix
, data
->local_mode
);
916 * adg_entity_invalidate:
917 * @entity: an #AdgEntity
919 * Emits the #AdgEntity::invalidate signal on @entity and on all of
920 * its children, if any, clearing the eventual cache stored by the
921 * #AdgEntity::arrange signal and setting the entity state similary
922 * to the just initialized entity.
925 adg_entity_invalidate(AdgEntity
*entity
)
927 g_return_if_fail(ADG_IS_ENTITY(entity
));
929 g_signal_emit(entity
, signals
[INVALIDATE
], 0);
933 * adg_entity_arrange:
934 * @entity: an #AdgEntity
937 * This function is only useful in entity implementations.
940 * Emits the #AdgEntity::arrange signal on @entity and all its children,
941 * if any. This function is rarely needed as the arrange call is usually
942 * managed by the #AdgEntity::render signal or indirectly by a call to
943 * adg_entity_get_extents().
946 adg_entity_arrange(AdgEntity
*entity
)
948 g_return_if_fail(ADG_IS_ENTITY(entity
));
950 g_signal_emit(entity
, signals
[ARRANGE
], 0);
955 * @entity: an #AdgEntity
956 * @cr: a #cairo_t drawing context
958 * Emits the #AdgEntity::render signal on @entity and on all of its
959 * children, if any, causing the rendering to the @cr cairo context.
962 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
964 g_return_if_fail(ADG_IS_ENTITY(entity
));
966 g_signal_emit(entity
, signals
[RENDER
], 0, cr
);
971 set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
973 AdgEntityPrivate
*data
;
974 AdgEntity
*old_parent
;
977 old_parent
= data
->parent
;
979 /* Check if parent has changed */
980 if (parent
== old_parent
)
984 g_object_ref(parent
);
986 data
->parent
= parent
;
987 g_signal_emit(entity
, signals
[PARENT_SET
], 0, old_parent
);
989 if (old_parent
!= NULL
)
990 g_object_unref(old_parent
);
996 global_changed(AdgEntity
*entity
)
998 AdgEntityPrivate
*data
;
1001 data
= entity
->data
;
1002 matrix
= &data
->global_matrix
;
1004 if (data
->parent
== NULL
) {
1005 adg_matrix_copy(matrix
, &data
->global_map
);
1007 adg_matrix_copy(matrix
, adg_entity_global_matrix(data
->parent
));
1008 cairo_matrix_multiply(matrix
, &data
->global_map
, matrix
);
1013 local_changed(AdgEntity
*entity
)
1015 AdgEntityPrivate
*data
;
1018 data
= entity
->data
;
1019 matrix
= &data
->local_matrix
;
1021 if (data
->parent
== NULL
) {
1022 adg_matrix_copy(matrix
, &data
->local_map
);
1024 adg_matrix_copy(matrix
, adg_entity_local_matrix(data
->parent
));
1025 cairo_matrix_multiply(matrix
, &data
->local_map
, matrix
);
1030 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1032 AdgEntityPrivate
*data
= entity
->data
;
1034 if (map
!= NULL
&& adg_matrix_equal(&data
->global_map
, map
))
1038 cairo_matrix_init_identity(&data
->global_map
);
1040 adg_matrix_copy(&data
->global_map
, map
);
1042 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0, &data
->global_matrix
);
1047 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1049 AdgEntityPrivate
*data
= entity
->data
;
1051 if (map
!= NULL
&& adg_matrix_equal(&data
->local_map
, map
))
1055 cairo_matrix_init_identity(&data
->local_map
);
1057 adg_matrix_copy(&data
->local_map
, map
);
1059 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0, &data
->local_matrix
);
1064 set_local_mode(AdgEntity
*entity
, AdgTransformationMode mode
)
1066 AdgEntityPrivate
*data
= entity
->data
;
1068 if (mode
== data
->local_mode
)
1071 data
->local_mode
= mode
;
1077 real_invalidate(AdgEntity
*entity
)
1079 AdgEntityPrivate
*data
= entity
->data
;
1080 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1082 if (klass
->invalidate
== NULL
) {
1083 /* Do not raise any warning if invalidate() is not defined,
1084 * assuming entity does not have cache to be cleared */
1086 klass
->invalidate(entity
);
1089 data
->extents
.is_defined
= FALSE
;
1093 real_arrange(AdgEntity
*entity
)
1095 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1097 if (klass
->arrange
== NULL
) {
1098 /* The arrange() method must be defined */
1099 g_warning("%s: `arrange' method not implemented for type `%s'",
1100 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1102 klass
->arrange(entity
);
1107 real_render(AdgEntity
*entity
, cairo_t
*cr
)
1109 AdgEntityPrivate
*data
= entity
->data
;
1110 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1112 if (klass
->render
== NULL
) {
1113 /* The render method must be defined */
1114 g_warning("%s: `render' method not implemented for type `%s'",
1115 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1117 /* Before the rendering, the entity should be arranged */
1118 g_signal_emit(entity
, signals
[ARRANGE
], 0);
1121 cairo_set_matrix(cr
, &data
->global_matrix
);
1122 klass
->render(entity
, cr
);