1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
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-internal.h"
62 #include "adg-entity.h"
63 #include "adg-entity-private.h"
64 #include "adg-canvas.h"
65 #include "adg-font-style.h"
66 #include "adg-dim-style.h"
67 #include "adg-marshal.h"
69 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
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 gboolean
set_global_map (AdgEntity
*entity
,
103 const AdgMatrix
*map
);
104 static gboolean
set_local_map (AdgEntity
*entity
,
105 const AdgMatrix
*map
);
106 static gboolean
set_local_method (AdgEntity
*entity
,
107 AdgMixMethod local_method
);
108 static void global_changed (AdgEntity
*entity
);
109 static void local_changed (AdgEntity
*entity
);
110 static void real_invalidate (AdgEntity
*entity
);
111 static void real_arrange (AdgEntity
*entity
);
112 static void real_render (AdgEntity
*entity
,
115 static guint signals
[LAST_SIGNAL
] = { 0 };
116 static gboolean show_extents
= FALSE
;
119 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
);
123 adg_entity_class_init(AdgEntityClass
*klass
)
125 GObjectClass
*gobject_class
;
128 GType param_types
[1];
130 gobject_class
= (GObjectClass
*) klass
;
132 g_type_class_add_private(klass
, sizeof(AdgEntityPrivate
));
134 gobject_class
->dispose
= dispose
;
135 gobject_class
->get_property
= get_property
;
136 gobject_class
->set_property
= set_property
;
138 klass
->parent_set
= NULL
;
139 klass
->global_changed
= global_changed
;
140 klass
->local_changed
= local_changed
;
141 klass
->invalidate
= NULL
;
142 klass
->arrange
= NULL
;
143 klass
->render
= NULL
;
145 param
= g_param_spec_object("parent",
147 P_("The parent entity of this entity or NULL if this is a top-level entity"),
150 g_object_class_install_property(gobject_class
, PROP_PARENT
, param
);
152 param
= g_param_spec_boxed("global-map",
154 P_("The transformation to be combined with the parent ones to get the global matrix"),
157 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
159 param
= g_param_spec_boxed("local-map",
161 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
164 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
166 param
= g_param_spec_enum("local-method",
167 P_("Local Mix Method"),
168 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
169 ADG_TYPE_MIX_METHOD
, ADG_MIX_ANCESTORS
,
171 g_object_class_install_property(gobject_class
, PROP_LOCAL_METHOD
, param
);
174 * AdgEntity::parent-set:
175 * @entity: an #AdgEntity
176 * @old_parent: the old parent
178 * Emitted after the parent entity has changed. The new parent
179 * can be inspected using adg_entity_get_parent().
181 * It is allowed for both old and new parent to be %NULL.
183 signals
[PARENT_SET
] = g_signal_new("parent-set",
184 G_OBJECT_CLASS_TYPE(gobject_class
),
186 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
188 adg_marshal_VOID__OBJECT
,
189 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
192 * AdgEntity::global-changed
193 * @entity: an #AdgEntity
195 * Emitted when the global map of @entity or any of its parent
196 * has changed. The default handler will compute the new global
197 * matrix, updating the internal cache.
199 signals
[GLOBAL_CHANGED
] = g_signal_new("global-changed",
200 G_OBJECT_CLASS_TYPE(gobject_class
),
202 G_STRUCT_OFFSET(AdgEntityClass
, global_changed
),
204 adg_marshal_VOID__VOID
,
208 * AdgEntity::local-changed
209 * @entity: an #AdgEntity
211 * Emitted when the local map of @entity or any of its parent
212 * has changed. The default handler will compute the new local
213 * matrix, updating the internal cache.
215 signals
[LOCAL_CHANGED
] = g_signal_new("local-changed",
216 G_OBJECT_CLASS_TYPE(gobject_class
),
218 G_STRUCT_OFFSET(AdgEntityClass
, local_changed
),
220 adg_marshal_VOID__VOID
,
224 * AdgEntity::invalidate:
225 * @entity: an #AdgEntity
227 * Invalidates the whole @entity, that is resets all the cache
228 * (if present) built during the #AdgEntity::arrange signal.
229 * The resulting state is a clean entity, similar to what you
230 * have just before the first rendering.
232 closure
= g_cclosure_new(G_CALLBACK(real_invalidate
), NULL
, NULL
);
233 signals
[INVALIDATE
] = g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
234 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
235 adg_marshal_VOID__VOID
,
236 G_TYPE_NONE
, 0, param_types
);
239 * AdgEntity::arrange:
240 * @entity: an #AdgEntity
242 * Arranges the layout of @entity, updating the cache if necessary,
243 * and computes the extents of @entity.
245 closure
= g_cclosure_new(G_CALLBACK(real_arrange
), NULL
, NULL
);
246 signals
[ARRANGE
] = g_signal_newv("arrange", ADG_TYPE_ENTITY
,
247 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
248 adg_marshal_VOID__VOID
,
249 G_TYPE_NONE
, 0, param_types
);
252 * @entity: an #AdgEntity
253 * @cr: a #cairo_t drawing context
255 * Causes the rendering of @entity on @cr. A render signal will
256 * automatically emit #AdgEntity::arrange just before the real
257 * rendering on the cairo context.
259 closure
= g_cclosure_new(G_CALLBACK(real_render
), NULL
, NULL
);
260 param_types
[0] = G_TYPE_POINTER
;
261 signals
[RENDER
] = g_signal_newv("render", ADG_TYPE_ENTITY
,
262 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
263 adg_marshal_VOID__POINTER
,
264 G_TYPE_NONE
, 1, param_types
);
268 adg_entity_init(AdgEntity
*entity
)
270 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
274 cairo_matrix_init_identity(&data
->global_map
);
275 cairo_matrix_init_identity(&data
->local_map
);
276 data
->local_method
= ADG_MIX_ANCESTORS
;
277 data
->hash_styles
= NULL
;
278 data
->global
.is_defined
= FALSE
;
279 adg_matrix_copy(&data
->global
.matrix
, adg_matrix_null());
280 data
->local
.is_defined
= FALSE
;
281 adg_matrix_copy(&data
->local
.matrix
, adg_matrix_null());
282 data
->extents
.is_defined
= FALSE
;
288 dispose(GObject
*object
)
291 AdgEntityPrivate
*data
;
293 entity
= (AdgEntity
*) object
;
296 /* This call will emit a "notify" signal for parent.
297 * Consequentially, the references to the old parent is dropped. */
298 adg_entity_set_parent(entity
, NULL
);
300 if (data
->hash_styles
!= NULL
) {
301 g_hash_table_destroy(data
->hash_styles
);
302 data
->hash_styles
= NULL
;
305 if (PARENT_OBJECT_CLASS
->dispose
)
306 PARENT_OBJECT_CLASS
->dispose(object
);
310 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
313 AdgEntityPrivate
*data
;
315 entity
= (AdgEntity
*) object
;
320 g_value_set_object(value
, data
->parent
);
322 case PROP_GLOBAL_MAP
:
323 g_value_set_boxed(value
, &data
->global_map
);
326 g_value_set_boxed(value
, &data
->local_map
);
328 case PROP_LOCAL_METHOD
:
329 g_value_set_enum(value
, data
->local_method
);
332 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
338 set_property(GObject
*object
,
339 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
342 AdgEntityPrivate
*data
;
344 entity
= (AdgEntity
*) object
;
349 set_parent(entity
, g_value_get_object(value
));
351 case PROP_GLOBAL_MAP
:
352 set_global_map(entity
, g_value_get_boxed(value
));
355 set_local_map(entity
, g_value_get_boxed(value
));
357 case PROP_LOCAL_METHOD
:
358 set_local_method(entity
, g_value_get_enum(value
));
361 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
368 * adg_switch_extents:
369 * @state: new extents state
371 * Strokes (if @state is %TRUE) a rectangle around every entity to
372 * show their extents. Useful for debugging purposes.
375 adg_switch_extents(gboolean state
)
377 show_extents
= state
;
381 * adg_entity_get_canvas:
382 * @entity: an #AdgEntity
384 * Walks on the @entity hierarchy and gets the first parent of @entity that is
385 * of #AdgCanvas derived type.
387 * Returns: the requested canvas or %NULL on errors or if there is
388 * no #AdgCanvas in the @entity hierarchy
391 adg_entity_get_canvas(AdgEntity
*entity
)
393 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
396 if (ADG_IS_CANVAS(entity
))
397 return (AdgCanvas
*) entity
;
399 entity
= adg_entity_get_parent(entity
);
406 * adg_entity_set_parent:
407 * @entity: an #AdgEntity
408 * @parent: the parent entity
411 * This function is only useful in entity implementations.
414 * Sets a new parent on @entity.
417 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
419 g_return_if_fail(ADG_IS_ENTITY(entity
));
421 if (set_parent(entity
, parent
))
422 g_object_notify((GObject
*) entity
, "parent");
426 * adg_entity_get_parent:
427 * @entity: an #AdgEntity
429 * Gets the parent of @entity.
431 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
434 adg_entity_get_parent(AdgEntity
*entity
)
436 AdgEntityPrivate
*data
;
438 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
446 * adg_entity_set_global_map:
447 * @entity: an #AdgEntity object
450 * Sets the new global transformation of @entity to @map:
451 * the old map is discarded. If @map is %NULL an identity
455 adg_entity_set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
457 g_return_if_fail(ADG_IS_ENTITY(entity
));
459 if (set_global_map(entity
, map
))
460 g_object_notify((GObject
*) entity
, "global-map");
464 * adg_entity_transform_global_map:
465 * @entity: an #AdgEntity object
466 * @transformation: the transformation to apply
467 * @mode: how @transformation should be applied
469 * Convenient function to change the global map of @entity by
470 * applying @tranformation using the @mode operator. This is
471 * logically equivalent to the following:
475 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
476 * adg_matrix_transform(&map, transformation, mode);
477 * adg_entity_set_global_map(entity, &map);
481 adg_entity_transform_global_map(AdgEntity
*entity
,
482 const AdgMatrix
*transformation
,
483 AdgTransformMode mode
)
485 AdgEntityPrivate
*data
;
488 g_return_if_fail(ADG_IS_ENTITY(entity
));
489 g_return_if_fail(transformation
!= NULL
);
493 adg_matrix_copy(&map
, &data
->global_map
);
494 adg_matrix_transform(&map
, transformation
, mode
);
496 if (set_global_map(entity
, &map
))
497 g_object_notify((GObject
*) entity
, "global-map");
501 * adg_entity_get_global_map:
502 * @entity: an #AdgEntity object
504 * Gets the transformation to be used to compute the global matrix
507 * Returns: the requested map or %NULL on errors
510 adg_entity_get_global_map(AdgEntity
*entity
)
512 AdgEntityPrivate
*data
;
514 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
518 return &data
->global_map
;
522 * adg_entity_get_global_matrix:
523 * @entity: an #AdgEntity object
525 * Gets the current global matrix of @entity. The returned value
526 * is owned by @entity and should not be changed or freed.
528 * The global matrix is computed in the arrange() phase by
529 * combining all the global maps of the @entity hierarchy using
530 * the %ADG_MIX_ANCESTORS method.
532 * Returns: the global matrix or %NULL on errors
535 adg_entity_get_global_matrix(AdgEntity
*entity
)
537 AdgEntityPrivate
*data
;
539 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
543 return &data
->global
.matrix
;
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_matrix_copy(&map, adg_entity_get_local_map(entity));
577 * adg_matrix_transform(&map, transformation, mode);
578 * adg_entity_set_local_map(entity, &map);
582 adg_entity_transform_local_map(AdgEntity
*entity
,
583 const AdgMatrix
*transformation
,
584 AdgTransformMode 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_map:
603 * @entity: an #AdgEntity object
605 * Gets the transformation to be used to compute the local matrix
606 * of @entity and store it in @map.
608 * Returns: the requested map or %NULL on errors
611 adg_entity_get_local_map(AdgEntity
*entity
)
613 AdgEntityPrivate
*data
;
615 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
619 return &data
->local_map
;
623 * adg_entity_get_local_matrix:
624 * @entity: an #AdgEntity object
626 * Gets the current local matrix of @entity. The returned value
627 * is owned by @entity and should not be changed or freed.
629 * The local matrix is computed in the arrange() phase by
630 * combining all the local maps of the @entity hierarchy using
631 * the method specified by the #AdgEntity:local-method property.
633 * Returns: the local matrix or %NULL on errors
636 adg_entity_get_local_matrix(AdgEntity
*entity
)
638 AdgEntityPrivate
*data
;
640 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
644 return &data
->local
.matrix
;
648 * adg_entity_set_local_method:
649 * @entity: an #AdgEntity object
650 * @local_method: new method
652 * Sets a new local mix method on @entity. The
653 * #AdgEntity:local-method property defines how the local
654 * matrix must be computed: check out the #AdgMixMethod
655 * documentation to know what are the availables methods
656 * and how they affect the local matrix computation.
658 * Setting a different local method emits an #Adgentity::local-changed
662 adg_entity_set_local_method(AdgEntity
*entity
, AdgMixMethod local_method
)
664 g_return_if_fail(ADG_IS_ENTITY(entity
));
666 if (set_local_method(entity
, local_method
))
667 g_object_notify((GObject
*) entity
, "local-method");
671 * adg_entity_get_local_method:
672 * @entity: an #AdgEntity object
674 * Gets the local mix method of @entity. Check out the
675 * adg_entity_set_local_method() documentation to know what the
676 * local method is used for.
678 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
681 adg_entity_get_local_method(AdgEntity
*entity
)
683 AdgEntityPrivate
*data
;
685 g_return_val_if_fail(ADG_IS_ENTITY(entity
), ADG_MIX_UNDEFINED
);
689 return data
->local_method
;
693 * adg_entity_set_extents:
694 * @entity: an #AdgEntity
695 * @extents: the new extents
698 * This function is only useful in entity implementations.
701 * Sets a new bounding box for @entity. @extents can be %NULL,
702 * in which case the extents are unset.
705 adg_entity_set_extents(AdgEntity
*entity
, const CpmlExtents
*extents
)
707 AdgEntityPrivate
*data
;
709 g_return_if_fail(ADG_IS_ENTITY(entity
));
714 data
->extents
.is_defined
= FALSE
;
716 cpml_extents_copy(&data
->extents
, extents
);
720 * adg_entity_get_extents:
721 * @entity: an #AdgEntity
723 * Gets the bounding box of @entity. The returned struct is
724 * owned by @entity and should not modified or freed.
726 * This struct specifies the surface portion (in global space
727 * of @entity) occupied by the entity without taking into
728 * account rendering properties such as line thickness or caps.
730 * The #AdgEntity::arrange signal should be emitted before
731 * this call (either explicitely trought adg_entity_arrange()
732 * or implicitely with adg_entity_render()) in order to get
733 * an up to date boundary box.
735 * Returns: the bounding box of @entity or %NULL on errors
738 adg_entity_get_extents(AdgEntity
*entity
)
740 AdgEntityPrivate
*data
;
742 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
746 return &data
->extents
;
750 * adg_entity_set_style:
751 * @entity: an #AdgEntity
752 * @dress: a dress style
753 * @style: the new style to use
755 * Overrides the style of @dress for @entity and its children.
756 * If @style is %NULL, any previous override is removed.
758 * The new style must still be compatible with @dress: check out
759 * the adg_dress_style_is_compatible() documentation to know
760 * what a compatible style means.
763 adg_entity_set_style(AdgEntity
*entity
, AdgDress dress
, AdgStyle
*style
)
765 AdgEntityPrivate
*data
;
769 g_return_if_fail(ADG_IS_ENTITY(entity
));
773 if (data
->hash_styles
== NULL
&& style
== NULL
)
776 if (data
->hash_styles
== NULL
)
777 data
->hash_styles
= g_hash_table_new_full(NULL
, NULL
,
778 NULL
, g_object_unref
);
780 p_dress
= GINT_TO_POINTER(dress
);
781 old_style
= g_hash_table_lookup(data
->hash_styles
, p_dress
);
783 if (style
== old_style
)
787 g_hash_table_remove(data
->hash_styles
, p_dress
);
791 if (!adg_dress_style_is_compatible(dress
, style
)) {
792 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
794 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
795 G_STRLOC
, g_type_name(G_TYPE_FROM_INSTANCE(style
)),
796 g_type_name(ancestor_type
), adg_dress_get_name(dress
));
802 g_hash_table_replace(data
->hash_styles
, p_dress
, style
);
806 * adg_entity_get_style:
807 * @entity: an #AdgEntity
808 * @dress: the dress of the style to get
810 * Gets the overriden @dress style from @entity. This is a kind
811 * of accessor function: to get the style to be used for rendering
812 * purpose, use adg_entity_style() instead.
814 * Returns: the requested style or %NULL if the @dress style
818 adg_entity_get_style(AdgEntity
*entity
, AdgDress dress
)
820 AdgEntityPrivate
*data
;
822 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
826 if (data
->hash_styles
== NULL
)
829 return g_hash_table_lookup(data
->hash_styles
, GINT_TO_POINTER(dress
));
834 * @entity: an #AdgEntity
835 * @dress: the dress of the style to get
837 * Gets the style to be used for @entity. @dress specifies which
838 * "family" of style to get.
840 * The following sequence of checks is performed to get the proper
841 * style, stopping at the first succesfull result:
844 * <listitem>check if the style is directly overriden by this entity,
845 * as returned by adg_entity_get_style();</listitem>
846 * <listitem>check if @entity has a parent, in which case returns the
847 * adg_entity_style() of the parent;</listitem>
848 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
851 * Returns: the requested style or %NULL for transparent dresses or errors
854 adg_entity_style(AdgEntity
*entity
, AdgDress dress
)
858 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
860 style
= adg_entity_get_style(entity
, dress
);
863 AdgEntityPrivate
*data
= entity
->data
;
865 if (data
->parent
!= NULL
)
866 style
= adg_entity_style(data
->parent
, dress
);
868 style
= adg_dress_get_fallback(dress
);
875 * adg_entity_apply_dress:
876 * @entity: an #AdgEntity
877 * @dress: the dress style to apply
878 * @cr: a #cairo_t drawing context
880 * Convenient function to apply a @dress style (as returned by
881 * adg_entity_style()) to the @cr cairo context.
884 adg_entity_apply_dress(AdgEntity
*entity
, AdgDress dress
, cairo_t
*cr
)
888 g_return_if_fail(ADG_IS_ENTITY(entity
));
889 g_return_if_fail(cr
!= NULL
);
891 style
= adg_entity_style(entity
, dress
);
894 adg_style_apply(style
, entity
, cr
);
898 * adg_entity_global_changed:
899 * @entity: an #AdgEntity
901 * Emits the #AdgEntity::global-changed signal on @entity and on all of
902 * its children, if any.
905 adg_entity_global_changed(AdgEntity
*entity
)
907 g_return_if_fail(ADG_IS_ENTITY(entity
));
909 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0);
913 * adg_entity_local_changed:
914 * @entity: an #AdgEntity
916 * Emits the #AdgEntity::local-changed signal on @entity and on all of
917 * its children, if any.
920 adg_entity_local_changed(AdgEntity
*entity
)
922 g_return_if_fail(ADG_IS_ENTITY(entity
));
924 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0);
928 * adg_entity_invalidate:
929 * @entity: an #AdgEntity
931 * Emits the #AdgEntity::invalidate signal on @entity and on all of
932 * its children, if any, clearing the eventual cache stored by the
933 * #AdgEntity::arrange signal and setting the entity state similary
934 * to the just initialized entity.
937 adg_entity_invalidate(AdgEntity
*entity
)
939 g_return_if_fail(ADG_IS_ENTITY(entity
));
941 g_signal_emit(entity
, signals
[INVALIDATE
], 0);
945 * adg_entity_arrange:
946 * @entity: an #AdgEntity
948 * Emits the #AdgEntity::arrange signal on @entity and all its children,
949 * if any. This function is rarely needed as the arrange call is usually
950 * implicitely called by the #AdgEntity::render signal or iby a call to
951 * adg_entity_get_extents().
954 adg_entity_arrange(AdgEntity
*entity
)
956 g_return_if_fail(ADG_IS_ENTITY(entity
));
958 g_signal_emit(entity
, signals
[ARRANGE
], 0);
963 * @entity: an #AdgEntity
964 * @cr: a #cairo_t drawing context
966 * Emits the #AdgEntity::render signal on @entity and on all of its
967 * children, if any, causing the rendering to the @cr cairo context.
970 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
972 g_return_if_fail(ADG_IS_ENTITY(entity
));
974 g_signal_emit(entity
, signals
[RENDER
], 0, cr
);
979 set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
981 AdgEntityPrivate
*data
;
982 AdgEntity
*old_parent
;
984 g_return_val_if_fail(parent
== NULL
|| ADG_IS_ENTITY(parent
), FALSE
);
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 data
->global
.is_defined
= FALSE
;
998 data
->local
.is_defined
= FALSE
;
1000 g_signal_emit(entity
, signals
[PARENT_SET
], 0, old_parent
);
1002 if (old_parent
!= NULL
)
1003 g_object_unref(old_parent
);
1009 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1011 AdgEntityPrivate
*data
= entity
->data
;
1014 map
= adg_matrix_identity();
1016 if (adg_matrix_equal(&data
->global_map
, map
))
1019 adg_matrix_copy(&data
->global_map
, map
);
1021 data
->global
.is_defined
= FALSE
;
1026 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
1028 AdgEntityPrivate
*data
= entity
->data
;
1031 map
= adg_matrix_identity();
1033 if (adg_matrix_equal(&data
->local_map
, map
))
1036 adg_matrix_copy(&data
->local_map
, map
);
1038 data
->local
.is_defined
= FALSE
;
1043 set_local_method(AdgEntity
*entity
, AdgMixMethod local_method
)
1045 AdgEntityPrivate
*data
= entity
->data
;
1047 if (data
->local_method
== local_method
)
1050 data
->local_method
= local_method
;
1051 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0);
1057 global_changed(AdgEntity
*entity
)
1059 AdgEntityPrivate
*data
;
1060 const AdgMatrix
*map
;
1063 data
= entity
->data
;
1064 map
= &data
->global_map
;
1065 matrix
= &data
->global
.matrix
;
1068 adg_matrix_copy(matrix
, adg_entity_get_global_matrix(data
->parent
));
1069 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1071 adg_matrix_copy(matrix
, map
);
1076 local_changed(AdgEntity
*entity
)
1078 AdgEntityPrivate
*data
;
1079 const AdgMatrix
*map
;
1082 data
= entity
->data
;
1083 map
= &data
->local_map
;
1084 matrix
= &data
->local
.matrix
;
1086 switch (data
->local_method
) {
1087 case ADG_MIX_DISABLED
:
1088 adg_matrix_copy(matrix
, adg_matrix_identity());
1091 adg_matrix_copy(matrix
, map
);
1093 case ADG_MIX_ANCESTORS
:
1095 adg_matrix_copy(matrix
, adg_entity_get_local_matrix(data
->parent
));
1096 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1098 adg_matrix_copy(matrix
, map
);
1101 case ADG_MIX_ANCESTORS_NORMALIZED
:
1103 adg_matrix_copy(matrix
, adg_entity_get_local_matrix(data
->parent
));
1104 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1106 adg_matrix_copy(matrix
, map
);
1108 adg_matrix_normalize(matrix
);
1110 case ADG_MIX_PARENT
:
1112 adg_matrix_copy(matrix
, adg_entity_get_local_map(data
->parent
));
1113 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1115 adg_matrix_copy(matrix
, map
);
1118 case ADG_MIX_PARENT_NORMALIZED
:
1120 adg_matrix_copy(matrix
, adg_entity_get_local_map(data
->parent
));
1121 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1123 adg_matrix_copy(matrix
, map
);
1125 adg_matrix_normalize(matrix
);
1127 case ADG_MIX_UNDEFINED
:
1128 g_warning(_("%s: requested to mix the maps using an undefined method"),
1132 g_return_if_reached();
1138 real_invalidate(AdgEntity
*entity
)
1140 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1141 AdgEntityPrivate
*data
= entity
->data
;
1143 /* Do not raise any warning if invalidate() is not defined,
1144 * assuming entity does not have additional cache to be cleared */
1145 if (klass
->invalidate
)
1146 klass
->invalidate(entity
);
1148 data
->extents
.is_defined
= FALSE
;
1152 real_arrange(AdgEntity
*entity
)
1154 AdgEntityClass
*klass
;
1155 AdgEntityPrivate
*data
;
1157 klass
= ADG_ENTITY_GET_CLASS(entity
);
1158 data
= entity
->data
;
1160 /* Update the global matrix, if required */
1161 if (!data
->global
.is_defined
) {
1162 data
->global
.is_defined
= TRUE
;
1163 g_signal_emit(entity
, signals
[GLOBAL_CHANGED
], 0);
1166 /* Update the local matrix, if required */
1167 if (!data
->local
.is_defined
) {
1168 data
->local
.is_defined
= TRUE
;
1169 g_signal_emit(entity
, signals
[LOCAL_CHANGED
], 0);
1172 /* The arrange() method must be defined */
1173 if (klass
->arrange
== NULL
) {
1174 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1175 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1176 data
->extents
.is_defined
= FALSE
;
1180 klass
->arrange(entity
);
1184 real_render(AdgEntity
*entity
, cairo_t
*cr
)
1186 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1188 /* The render method must be defined */
1189 if (klass
->render
== NULL
) {
1190 g_warning(_("%s: `render' method not implemented for type `%s'"),
1191 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1195 /* Before the rendering, the entity should be arranged */
1196 g_signal_emit(entity
, signals
[ARRANGE
], 0);
1200 AdgEntityPrivate
*data
= entity
->data
;
1202 if (data
->extents
.is_defined
) {
1204 cairo_identity_matrix(cr
);
1205 cairo_set_line_width(cr
, 1);
1206 cairo_set_antialias(cr
, CAIRO_ANTIALIAS_NONE
);
1207 cairo_rectangle(cr
, data
->extents
.org
.x
, data
->extents
.org
.y
,
1208 data
->extents
.size
.x
, data
->extents
.size
.y
);
1215 cairo_set_matrix(cr
, adg_entity_get_global_matrix(entity
));
1216 klass
->render(entity
, cr
);