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.
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.
61 #include "adg-entity.h"
62 #include "adg-entity-private.h"
63 #include "adg-canvas.h"
64 #include "adg-font-style.h"
65 #include "adg-dim-style.h"
66 #include "adg-marshal.h"
69 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
91 static void dispose (GObject
*object
);
92 static void get_property (GObject
*object
,
96 static void set_property (GObject
*object
,
100 static gboolean
set_parent (AdgEntity
*entity
,
102 static void global_changed (AdgEntity
*entity
);
103 static void local_changed (AdgEntity
*entity
);
104 static gboolean
set_global_map (AdgEntity
*entity
,
105 const AdgMatrix
*map
);
106 static gboolean
set_local_map (AdgEntity
*entity
,
107 const AdgMatrix
*map
);
108 static void real_invalidate (AdgEntity
*entity
);
109 static void real_arrange (AdgEntity
*entity
);
110 static void real_render (AdgEntity
*entity
,
113 static guint signals
[LAST_SIGNAL
] = { 0 };
116 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
);
120 adg_entity_class_init(AdgEntityClass
*klass
)
122 GObjectClass
*gobject_class
;
125 GType param_types
[1];
127 gobject_class
= (GObjectClass
*) klass
;
129 g_type_class_add_private(klass
, sizeof(AdgEntityPrivate
));
131 gobject_class
->dispose
= dispose
;
132 gobject_class
->get_property
= get_property
;
133 gobject_class
->set_property
= set_property
;
135 klass
->parent_set
= NULL
;
136 klass
->global_changed
= global_changed
;
137 klass
->local_changed
= local_changed
;
138 klass
->invalidate
= NULL
;
139 klass
->arrange
= NULL
;
140 klass
->render
= NULL
;
142 param
= g_param_spec_object("parent",
144 P_("The parent entity of this entity or NULL if this is a top-level entity"),
147 g_object_class_install_property(gobject_class
, PROP_PARENT
, param
);
149 param
= g_param_spec_boxed("global-map",
151 P_("The transformation to be combined with the parent ones to get the global matrix"),
154 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
156 param
= g_param_spec_boxed("local-map",
158 P_("The transformation to be combined with the parent ones to get the local matrix"),
161 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
163 param
= g_param_spec_boolean("normalized",
164 P_("Normalized Entity"),
165 P_("A normalized entity is insensitive to the scaling of the local matrix, that is its local matrix is normalized before being applied"),
168 g_object_class_install_property(gobject_class
, PROP_NORMALIZED
, param
);
171 * AdgEntity::parent-set:
172 * @entity: an #AdgEntity
173 * @old_parent: the old parent
175 * Emitted after the parent entity has changed. The new parent
176 * can be inspected using adg_entity_get_parent().
178 * It is allowed for both old and new parent to be %NULL.
180 signals
[PARENT_SET
] = g_signal_new("parent-set",
181 G_OBJECT_CLASS_TYPE(gobject_class
),
183 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
185 adg_marshal_VOID__OBJECT
,
186 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
189 * AdgEntity::global-changed
190 * @entity: an #AdgEntity
192 * Emitted when the global map of @entity or any of its parent
193 * has changed. The default handler will compute the new global
194 * matrix, updating the internal cache.
196 signals
[GLOBAL_CHANGED
] = g_signal_new("global-changed",
197 G_OBJECT_CLASS_TYPE(gobject_class
),
199 G_STRUCT_OFFSET(AdgEntityClass
, global_changed
),
201 adg_marshal_VOID__VOID
,
205 * AdgEntity::local-changed
206 * @entity: an #AdgEntity
208 * Emitted when the local map of @entity or any of its parent
209 * has changed. The default handler will compute the new local
210 * matrix, updating the internal cache.
212 signals
[LOCAL_CHANGED
] = g_signal_new("local-changed",
213 G_OBJECT_CLASS_TYPE(gobject_class
),
215 G_STRUCT_OFFSET(AdgEntityClass
, local_changed
),
217 adg_marshal_VOID__VOID
,
221 * AdgEntity::invalidate:
222 * @entity: an #AdgEntity
224 * Invalidates the whole @entity, that is resets all the cache
225 * (if present) built during the #AdgEntity::arrange signal.
226 * The resulting state is a clean entity, similar to what you
227 * have just before the first rendering.
229 closure
= g_cclosure_new(G_CALLBACK(real_invalidate
), NULL
, NULL
);
230 signals
[INVALIDATE
] = g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
231 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
232 adg_marshal_VOID__VOID
,
233 G_TYPE_NONE
, 0, param_types
);
236 * AdgEntity::arrange:
237 * @entity: an #AdgEntity
239 * Arranges the layout of @entity, updating the cache if necessary,
240 * and computes the extents of @entity.
242 closure
= g_cclosure_new(G_CALLBACK(real_arrange
), NULL
, NULL
);
243 signals
[ARRANGE
] = g_signal_newv("arrange", ADG_TYPE_ENTITY
,
244 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
245 adg_marshal_VOID__VOID
,
246 G_TYPE_NONE
, 0, param_types
);
249 * @entity: an #AdgEntity
250 * @cr: a #cairo_t drawing context
252 * Causes the rendering of @entity on @cr. A render signal will
253 * automatically emit #AdgEntity::arrange just before the real
254 * rendering on the cairo context.
256 closure
= g_cclosure_new(G_CALLBACK(real_render
), NULL
, NULL
);
257 param_types
[0] = G_TYPE_POINTER
;
258 signals
[RENDER
] = g_signal_newv("render", ADG_TYPE_ENTITY
,
259 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
260 adg_marshal_VOID__POINTER
,
261 G_TYPE_NONE
, 1, param_types
);
265 adg_entity_init(AdgEntity
*entity
)
267 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
271 cairo_matrix_init_identity(&data
->global_map
);
272 cairo_matrix_init_identity(&data
->local_map
);
273 data
->normalized
= FALSE
;
274 data
->hash_styles
= NULL
;
275 cairo_matrix_init_identity(&data
->global_matrix
);
276 cairo_matrix_init_identity(&data
->local_matrix
);
277 data
->extents
.is_defined
= FALSE
;
283 dispose(GObject
*object
)
286 AdgEntityPrivate
*data
;
288 entity
= (AdgEntity
*) object
;
291 /* This call will emit a "notify" signal for parent.
292 * Consequentially, the references to the old parent is dropped. */
293 adg_entity_set_parent(entity
, NULL
);
295 if (data
->hash_styles
!= NULL
) {
296 g_hash_table_destroy(data
->hash_styles
);
297 data
->hash_styles
= NULL
;
300 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
301 PARENT_OBJECT_CLASS
->dispose(object
);
306 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
309 AdgEntityPrivate
*data
;
311 entity
= (AdgEntity
*) object
;
316 g_value_set_object(value
, data
->parent
);
318 case PROP_GLOBAL_MAP
:
319 g_value_set_boxed(value
, &data
->global_map
);
322 g_value_set_boxed(value
, &data
->local_map
);
324 case PROP_NORMALIZED
:
325 g_value_set_boolean(value
, data
->normalized
);
328 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
334 set_property(GObject
*object
,
335 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
338 AdgEntityPrivate
*data
;
340 entity
= (AdgEntity
*) object
;
345 set_parent(entity
, g_value_get_object(value
));
347 case PROP_GLOBAL_MAP
:
348 set_global_map(entity
, g_value_get_boxed(value
));
351 set_local_map(entity
, g_value_get_boxed(value
));
353 case PROP_NORMALIZED
:
354 data
->normalized
= g_value_get_boolean(value
);
357 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
364 * adg_entity_get_parent:
365 * @entity: an #AdgEntity
367 * Gets the parent of @entity.
369 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
372 adg_entity_get_parent(AdgEntity
*entity
)
374 AdgEntityPrivate
*data
;
376 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
384 * adg_entity_set_parent:
385 * @entity: an #AdgEntity
386 * @parent: the parent entity
389 * This function is only useful in entity implementations.
392 * Sets a new parent on @entity.
395 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
397 g_return_if_fail(ADG_IS_ENTITY(entity
));
399 if (set_parent(entity
, parent
))
400 g_object_notify((GObject
*) entity
, "parent");
404 * adg_entity_get_canvas:
405 * @entity: an #AdgEntity
407 * Walks on the @entity hierarchy and gets the first parent of @entity that is
408 * of #AdgCanvas derived type.
410 * Returns: the requested canvas or %NULL on errors or if there is
411 * no #AdgCanvas in the @entity hierarchy
414 adg_entity_get_canvas(AdgEntity
*entity
)
416 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
419 if (ADG_IS_CANVAS(entity
))
420 return (AdgCanvas
*) entity
;
422 entity
= (AdgEntity
*) adg_entity_get_parent(entity
);
429 * adg_entity_get_global_map:
430 * @entity: an #AdgEntity object
431 * @map: where to store the global map
433 * Gets the transformation to be used to compute the global matrix
434 * of @entity and store it in @map.
437 adg_entity_get_global_map(AdgEntity
*entity
, AdgMatrix
*map
)
439 AdgEntityPrivate
*data
;
441 g_return_if_fail(ADG_IS_ENTITY(entity
));
442 g_return_if_fail(map
!= NULL
);
445 adg_matrix_copy(map
, &data
->global_map
);
449 * adg_entity_set_global_map:
450 * @entity: an #AdgEntity object
453 * Sets the new global transformation of @entity to @map:
454 * the old map is discarded. If @map is %NULL an identity
458 adg_entity_set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
460 g_return_if_fail(ADG_IS_ENTITY(entity
));
462 if (set_global_map(entity
, map
))
463 g_object_notify((GObject
*) entity
, "global-map");
467 * adg_entity_transform_global_map:
468 * @entity: an #AdgEntity object
469 * @transformation: the transformation to apply
470 * @mode: how @transformation should be applied
472 * Convenient function to change the global map of @entity by
473 * applying @tranformation using the @mode operator. This is
474 * logically equivalent to the following:
478 * adg_entity_get_global_map(entity, &tmp_map);
479 * adg_matrix_transform(&tmp_map, transformation, mode);
480 * adg_entity_set_global_map(entity, &tmp_map);
484 adg_entity_transform_global_map(AdgEntity
*entity
,
485 const AdgMatrix
*transformation
,
486 AdgTransformationMode mode
)
488 AdgEntityPrivate
*data
;
491 g_return_if_fail(ADG_IS_ENTITY(entity
));
492 g_return_if_fail(transformation
!= NULL
);
496 adg_matrix_copy(&map
, &data
->global_map
);
497 adg_matrix_transform(&map
, transformation
, mode
);
499 if (set_global_map(entity
, &map
))
500 g_object_notify((GObject
*) entity
, "global-map");
504 * adg_entity_get_local_map:
505 * @entity: an #AdgEntity object
506 * @map: where to store the local map
508 * Gets the transformation to be used to compute the local matrix
509 * of @entity and store it in @map.
512 adg_entity_get_local_map(AdgEntity
*entity
, AdgMatrix
*map
)
514 AdgEntityPrivate
*data
;
516 g_return_if_fail(ADG_IS_ENTITY(entity
));
517 g_return_if_fail(map
!= NULL
);
521 adg_matrix_copy(map
, &data
->local_map
);
525 * adg_entity_set_local_map:
526 * @entity: an #AdgEntity object
529 * Sets the new local transformation of @entity to @map:
530 * the old map is discarded. If @map is %NULL an identity
534 adg_entity_set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
536 g_return_if_fail(ADG_IS_ENTITY(entity
));
538 if (set_local_map(entity
, map
))
539 g_object_notify((GObject
*) entity
, "local-map");
543 * adg_entity_transform_local_map:
544 * @entity: an #AdgEntity object
545 * @transformation: the transformation to apply
546 * @mode: how @transformation should be applied
548 * Convenient function to change the local map of @entity by
549 * applying @tranformation using the @mode operator. This is
550 * logically equivalent to the following:
554 * adg_entity_get_local_map(entity, &tmp_map);
555 * adg_matrix_transform(&tmp_map, transformation, mode);
556 * adg_entity_set_local_map(entity, &tmp_map);
560 adg_entity_transform_local_map(AdgEntity
*entity
,
561 const AdgMatrix
*transformation
,
562 AdgTransformationMode mode
)
564 AdgEntityPrivate
*data
;
567 g_return_if_fail(ADG_IS_ENTITY(entity
));
568 g_return_if_fail(transformation
!= NULL
);
572 adg_matrix_copy(&map
, &data
->local_map
);
573 adg_matrix_transform(&map
, transformation
, mode
);
575 if (set_local_map(entity
, &map
))
576 g_object_notify((GObject
*) entity
, "local-map");
580 * adg_entity_get_normalized:
581 * @entity: an #AdgEntity object
583 * Gets the normalized state of @entity. Check out the
584 * adg_entity_set_normalized() documentation to know what a
585 * normalized entity is.
587 * Returns: the normalized state of @entity
590 adg_entity_get_normalized(AdgEntity
*entity
)
592 AdgEntityPrivate
*data
;
594 g_return_val_if_fail(ADG_IS_ENTITY(entity
), FALSE
);
598 return data
->normalized
;
602 * adg_entity_set_normalized:
603 * @entity: an #AdgEntity object
604 * @normalized: new normalized state
606 * Sets a new normalized state on @entity. A normalized entity
607 * is immune to the scaling of the local matrix: this means
608 * the local matrix is normalized with adg_matrix_normalize()
612 adg_entity_set_normalized(AdgEntity
*entity
, gboolean normalized
)
614 AdgEntityPrivate
*data
;
616 g_return_if_fail(ADG_IS_ENTITY(entity
));
620 data
->normalized
= normalized
;
624 * adg_entity_get_extents:
625 * @entity: an #AdgEntity
626 * @extents: where to store the extents
628 * Stores a copy of the bounding box of @entity in @extents.
629 * This struct specifies the surface portion (in global space)
630 * occupied by the entity without taking into account line
631 * thickness and caps.
633 * The #AdgEntity::arrange should be emitted before this call
634 * (either explicitely or throught adg_entity_arrange()) in
635 * order to get an up to date boundary box.
638 adg_entity_get_extents(AdgEntity
*entity
, CpmlExtents
*extents
)
640 AdgEntityPrivate
*data
;
642 g_return_if_fail(ADG_IS_ENTITY(entity
));
643 g_return_if_fail(extents
!= NULL
);
647 cpml_extents_copy(extents
, &data
->extents
);
651 * adg_entity_set_extents:
652 * @entity: an #AdgEntity
653 * @extents: the new extents
656 * This function is only useful in entity implementations.
659 * Sets a new bounding box for @entity. @extents can be %NULL,
660 * in which case the extents are unset.
663 adg_entity_set_extents(AdgEntity
*entity
, const CpmlExtents
*extents
)
665 AdgEntityPrivate
*data
;
667 g_return_if_fail(ADG_IS_ENTITY(entity
));
672 data
->extents
.is_defined
= FALSE
;
674 cpml_extents_copy(&data
->extents
, extents
);
679 * @entity: an #AdgEntity
680 * @dress: the dress of the style to get
682 * Gets the style to be used for @entity. @dress specifies which
683 * "family" of style to get.
685 * The following sequence of checks is performed to get the proper
686 * style, stopping at the first succesfull result:
689 * <listitem>check if the style is directly overriden by this entity,
690 * as returned by adg_entity_get_style();</listitem>
691 * <listitem>check if @entity has a parent, in which case returns the
692 * adg_entity_style() of the parent;</listitem>
693 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
696 * Returns: the requested style or %NULL for transparent dresses or errors
699 adg_entity_style(AdgEntity
*entity
, AdgDress dress
)
703 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
705 style
= adg_entity_get_style(entity
, dress
);
708 AdgEntityPrivate
*data
= entity
->data
;
710 if (data
->parent
!= NULL
)
711 style
= adg_entity_style(data
->parent
, dress
);
713 style
= adg_dress_get_fallback(dress
);
720 * adg_entity_get_style:
721 * @entity: an #AdgEntity
722 * @dress: the dress of the style to get
724 * Gets the overriden @dress style from @entity. This is a kind
725 * of accessor function: to get the style to be used for rendering
726 * purpose, use adg_entity_style() instead.
728 * Returns: the requested style or %NULL if the @dress style
732 adg_entity_get_style(AdgEntity
*entity
, AdgDress dress
)
734 AdgEntityPrivate
*data
;
736 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
740 if (data
->hash_styles
== NULL
)
743 return g_hash_table_lookup(data
->hash_styles
, GINT_TO_POINTER(dress
));
747 * adg_entity_set_style:
748 * @entity: an #AdgEntity
749 * @dress: a dress style
750 * @style: the new style to use
752 * Overrides the style of @dress for @entity and its children.
753 * If @style is %NULL, any previous override is removed.
755 * The new style must still be compatible with @dress: check out
756 * the adg_dress_style_is_compatible() documentation to know
757 * what a compatible style means.
760 adg_entity_set_style(AdgEntity
*entity
, AdgDress dress
, AdgStyle
*style
)
762 AdgEntityPrivate
*data
;
766 g_return_if_fail(ADG_IS_ENTITY(entity
));
770 if (data
->hash_styles
== NULL
&& style
== NULL
)
773 if (data
->hash_styles
== NULL
)
774 data
->hash_styles
= g_hash_table_new_full(NULL
, NULL
,
775 NULL
, g_object_unref
);
777 p_dress
= GINT_TO_POINTER(dress
);
778 old_style
= g_hash_table_lookup(data
->hash_styles
, p_dress
);
780 if (style
== old_style
)
784 g_hash_table_remove(data
->hash_styles
, p_dress
);
788 if (!adg_dress_style_is_compatible(dress
, style
)) {
789 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
791 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
792 G_STRLOC
, g_type_name(G_TYPE_FROM_INSTANCE(style
)),
793 g_type_name(ancestor_type
), adg_dress_name(dress
));
799 g_hash_table_replace(data
->hash_styles
, p_dress
, style
);
803 * adg_entity_apply_dress:
804 * @entity: an #AdgEntity
805 * @dress: the dress style to apply
806 * @cr: a #cairo_t drawing context
808 * Convenient function to apply a @dress style (as returned by
809 * adg_entity_style()) to the @cr cairo context.
812 adg_entity_apply_dress(AdgEntity
*entity
, AdgDress dress
, cairo_t
*cr
)
816 g_return_if_fail(ADG_IS_ENTITY(entity
));
817 g_return_if_fail(cr
!= NULL
);
819 style
= adg_entity_style(entity
, dress
);
822 adg_style_apply(style
, entity
, cr
);
826 * adg_entity_global_changed:
827 * @entity: an #AdgEntity
829 * Emits the #AdgEntity::global-changed signal on @entity and on all of
830 * its children, if any.
833 adg_entity_global_changed(AdgEntity
*entity
)
835 g_return_if_fail(ADG_IS_ENTITY(entity
));
837 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0);
841 * adg_entity_local_changed:
842 * @entity: an #AdgEntity
844 * Emits the #AdgEntity::local-changed signal on @entity and on all of
845 * its children, if any.
848 adg_entity_local_changed(AdgEntity
*entity
)
850 g_return_if_fail(ADG_IS_ENTITY(entity
));
852 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0);
856 * adg_entity_global_matrix:
857 * @entity: an #AdgEntity object
859 * Gets the global matrix by combining all the global maps of the
860 * @entity hierarchy. The returned value is owned by @entity and
861 * should not be changed or freed.
863 * Returns: the global matrix or %NULL on errors
866 adg_entity_global_matrix(AdgEntity
*entity
)
868 AdgEntityPrivate
*data
;
870 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
874 return &data
->global_matrix
;
878 * adg_entity_local_matrix:
879 * @entity: an #AdgEntity object
880 * @matrix: where to store the local matrix
882 * Gets the local matrix by combining all the local maps of the
883 * @entity hierarchy. The returned value is owned by @entity and
884 * should not be changed or freed.
886 * Returns: the local matrix or %NULL on errors
889 adg_entity_local_matrix(AdgEntity
*entity
)
891 AdgEntityPrivate
*data
;
893 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
897 return &data
->local_matrix
;
902 * @entity: an #AdgEntity object
904 * Gets the current transformation matrix (ctm) of @entity
905 * by transforming the local matrix with the global matrix.
907 * This method is only useful after the #AdgEntity::arrange default
908 * handler has been called, that is at the rendering stage or while
909 * arranging the entity after the parent method has been chained up.
911 * Returns: the current transformation matrix or %NULL on errors
914 adg_entity_ctm(AdgEntity
*entity
)
916 AdgEntityPrivate
*data
;
918 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
926 * adg_entity_invalidate:
927 * @entity: an #AdgEntity
929 * Emits the #AdgEntity::invalidate signal on @entity and on all of
930 * its children, if any, clearing the eventual cache stored by the
931 * #AdgEntity::arrange signal and setting the entity state similary
932 * to the just initialized entity.
935 adg_entity_invalidate(AdgEntity
*entity
)
937 g_return_if_fail(ADG_IS_ENTITY(entity
));
939 g_signal_emit(entity
, signals
[INVALIDATE
], 0);
943 * adg_entity_arrange:
944 * @entity: an #AdgEntity
947 * This function is only useful in entity implementations.
950 * Emits the #AdgEntity::arrange signal on @entity and all its children,
951 * if any. This function is rarely needed as the arrange call is usually
952 * managed by the #AdgEntity::render signal or indirectly by a call to
953 * adg_entity_get_extents().
956 adg_entity_arrange(AdgEntity
*entity
)
958 g_return_if_fail(ADG_IS_ENTITY(entity
));
960 g_signal_emit(entity
, signals
[ARRANGE
], 0);
965 * @entity: an #AdgEntity
966 * @cr: a #cairo_t drawing context
968 * Emits the #AdgEntity::render signal on @entity and on all of its
969 * children, if any, causing the rendering to the @cr cairo context.
972 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
974 g_return_if_fail(ADG_IS_ENTITY(entity
));
976 g_signal_emit(entity
, signals
[RENDER
], 0, cr
);
981 set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
983 AdgEntityPrivate
*data
;
984 AdgEntity
*old_parent
;
987 old_parent
= data
->parent
;
989 /* Check if parent has changed */
990 if (parent
== old_parent
)
994 g_object_ref(parent
);
996 data
->parent
= parent
;
997 g_signal_emit(entity
, signals
[PARENT_SET
], 0, old_parent
);
999 if (old_parent
!= NULL
)
1000 g_object_unref(old_parent
);
1006 global_changed(AdgEntity
*entity
)
1008 AdgEntityPrivate
*data
;
1009 AdgMatrix
*map
, *matrix
;
1011 data
= entity
->data
;
1012 map
= &data
->global_map
;
1013 matrix
= &data
->global_matrix
;
1015 if (data
->parent
== NULL
) {
1016 adg_matrix_copy(matrix
, map
);
1018 adg_matrix_copy(matrix
, adg_entity_global_matrix(data
->parent
));
1019 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_AFTER
);
1024 local_changed(AdgEntity
*entity
)
1026 AdgEntityPrivate
*data
;
1027 AdgMatrix
*map
, *matrix
;
1029 data
= entity
->data
;
1030 map
= &data
->local_map
;
1031 matrix
= &data
->local_matrix
;
1033 if (data
->parent
== NULL
) {
1034 adg_matrix_copy(matrix
, map
);
1036 adg_matrix_copy(matrix
, adg_entity_local_matrix(data
->parent
));
1037 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_AFTER
);
1040 if (data
->normalized
)
1041 adg_matrix_normalize(matrix
);
1045 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1047 AdgEntityPrivate
*data
= entity
->data
;
1049 if (map
!= NULL
&& adg_matrix_equal(&data
->global_map
, map
))
1053 cairo_matrix_init_identity(&data
->global_map
);
1055 adg_matrix_copy(&data
->global_map
, map
);
1057 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0, &data
->global_matrix
);
1062 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1064 AdgEntityPrivate
*data
= entity
->data
;
1066 if (map
!= NULL
&& adg_matrix_equal(&data
->local_map
, map
))
1070 cairo_matrix_init_identity(&data
->local_map
);
1072 adg_matrix_copy(&data
->local_map
, map
);
1074 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0, &data
->local_matrix
);
1079 real_invalidate(AdgEntity
*entity
)
1081 AdgEntityPrivate
*data
= entity
->data
;
1082 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1084 if (klass
->invalidate
== NULL
) {
1085 /* Do not raise any warning if invalidate() is not defined,
1086 * assuming entity does not have cache to be cleared */
1088 klass
->invalidate(entity
);
1091 data
->extents
.is_defined
= FALSE
;
1095 real_arrange(AdgEntity
*entity
)
1097 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1099 if (klass
->arrange
== NULL
) {
1100 /* The arrange() method must be defined */
1101 g_warning("%s: `arrange' method not implemented for type `%s'",
1102 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1104 AdgEntityPrivate
*data
;
1107 data
= entity
->data
;
1110 /* Update the ctm (current transformation matrix) */
1111 adg_matrix_copy(ctm
, &data
->global_matrix
);
1112 adg_matrix_transform(ctm
, &data
->local_matrix
, ADG_TRANSFORM_BEFORE
);
1114 klass
->arrange(entity
);
1119 real_render(AdgEntity
*entity
, cairo_t
*cr
)
1121 AdgEntityPrivate
*data
= entity
->data
;
1122 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1124 if (klass
->render
== NULL
) {
1125 /* The render method must be defined */
1126 g_warning("%s: `render' method not implemented for type `%s'",
1127 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1129 /* Before the rendering, the entity should be arranged */
1130 g_signal_emit(entity
, signals
[ARRANGE
], 0);
1133 cairo_set_matrix(cr
, &data
->global_matrix
);
1134 klass
->render(entity
, cr
);