1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011 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.
38 * All fields are private and should not be used directly.
39 * Use its public methods instead.
46 * @parent_set: called whenever the parent of an entity has changed
47 * @global_changed: the global matrix has been invalidated
48 * @local_changed: the local matrix has been invalidated
49 * @invalidate: invalidating callback, used to clear the internal cache
50 * @arrange: prepare the layout and fill the extents struct
51 * @render: rendering callback, it must be implemented by every entity
53 * Any entity (if not abstract) must implement at least the @render method.
54 * The other signal handlers can be overriden to provide custom behaviors
55 * and usually must chain up the original handler.
62 * @entity: an #AdgEntity
63 * @user_data: a general purpose pointer
65 * Callback used when inspecting or browsing entities. For example,
66 * it is passed to adg_model_foreach_dependency() to perform an
67 * operation on all the entities depending on an #AdgModel.
73 #include "adg-internal.h"
78 #include "adg-container.h"
79 #include "adg-table.h"
80 #include "adg-title-block.h"
81 #include "adg-canvas.h"
82 #include "adg-dress.h"
83 #include "adg-style.h"
84 #include "adg-model.h"
85 #include "adg-point.h"
87 #include "adg-entity-private.h"
90 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
93 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
)
114 static void _adg_dispose (GObject
*object
);
115 static void _adg_get_property (GObject
*object
,
119 static void _adg_set_property (GObject
*object
,
123 static void _adg_set_parent (AdgEntity
*entity
,
125 static void _adg_global_changed (AdgEntity
*entity
);
126 static void _adg_local_changed (AdgEntity
*entity
);
127 static void _adg_real_invalidate (AdgEntity
*entity
);
128 static void _adg_real_arrange (AdgEntity
*entity
);
129 static void _adg_real_render (AdgEntity
*entity
,
131 static guint _adg_signals
[LAST_SIGNAL
] = { 0 };
132 static gboolean _adg_show_extents
= FALSE
;
136 adg_entity_class_init(AdgEntityClass
*klass
)
138 GObjectClass
*gobject_class
;
141 GType param_types
[1];
143 gobject_class
= (GObjectClass
*) klass
;
145 g_type_class_add_private(klass
, sizeof(AdgEntityPrivate
));
147 gobject_class
->dispose
= _adg_dispose
;
148 gobject_class
->get_property
= _adg_get_property
;
149 gobject_class
->set_property
= _adg_set_property
;
151 klass
->parent_set
= NULL
;
152 klass
->global_changed
= _adg_global_changed
;
153 klass
->local_changed
= _adg_local_changed
;
154 klass
->invalidate
= NULL
;
155 klass
->arrange
= NULL
;
156 klass
->render
= NULL
;
158 param
= g_param_spec_object("parent",
160 P_("The parent entity of this entity or NULL if this is a top-level entity"),
163 g_object_class_install_property(gobject_class
, PROP_PARENT
, param
);
165 param
= g_param_spec_boxed("global-map",
167 P_("The transformation to be combined with the parent ones to get the global matrix"),
170 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
172 param
= g_param_spec_boxed("local-map",
174 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-method property"),
177 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
179 param
= g_param_spec_enum("local-method",
180 P_("Local Mix Method"),
181 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
182 ADG_TYPE_MIX_METHOD
, ADG_MIX_ANCESTORS
,
184 g_object_class_install_property(gobject_class
, PROP_LOCAL_METHOD
, param
);
187 * AdgEntity::parent-set:
188 * @entity: an #AdgEntity
189 * @old_parent: the old parent
191 * Emitted after the parent entity has changed. The new parent
192 * can be inspected using adg_entity_get_parent().
194 * It is allowed for both old and new parent to be %NULL.
198 _adg_signals
[PARENT_SET
] =
199 g_signal_new("parent-set",
200 G_OBJECT_CLASS_TYPE(gobject_class
),
202 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
204 adg_marshal_VOID__OBJECT
,
205 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
208 * AdgEntity::global-changed
209 * @entity: an #AdgEntity
211 * Emitted when the global map of @entity or any of its parent
212 * has changed. The default handler will compute the new global
213 * matrix, updating the internal cache.
217 _adg_signals
[GLOBAL_CHANGED
] =
218 g_signal_new("global-changed",
219 G_OBJECT_CLASS_TYPE(gobject_class
),
221 G_STRUCT_OFFSET(AdgEntityClass
, global_changed
),
223 adg_marshal_VOID__VOID
,
227 * AdgEntity::local-changed
228 * @entity: an #AdgEntity
230 * Emitted when the local map of @entity or any of its parent
231 * has changed. The default handler will compute the new local
232 * matrix, updating the internal cache.
236 _adg_signals
[LOCAL_CHANGED
] =
237 g_signal_new("local-changed",
238 G_OBJECT_CLASS_TYPE(gobject_class
),
240 G_STRUCT_OFFSET(AdgEntityClass
, local_changed
),
242 adg_marshal_VOID__VOID
,
246 * AdgEntity::invalidate:
247 * @entity: an #AdgEntity
249 * Invalidates the whole @entity, that is resets all the cache
250 * (if present) built during the #AdgEntity::arrange signal.
251 * The resulting state is a clean entity, similar to what you
252 * have just before the first rendering.
256 closure
= g_cclosure_new(G_CALLBACK(_adg_real_invalidate
), NULL
, NULL
);
257 _adg_signals
[INVALIDATE
] =
258 g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
259 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
260 adg_marshal_VOID__VOID
,
261 G_TYPE_NONE
, 0, param_types
);
264 * AdgEntity::arrange:
265 * @entity: an #AdgEntity
267 * Arranges the layout of @entity, updating the cache if necessary,
268 * and computes the extents of @entity.
272 closure
= g_cclosure_new(G_CALLBACK(_adg_real_arrange
), NULL
, NULL
);
273 _adg_signals
[ARRANGE
] =
274 g_signal_newv("arrange", ADG_TYPE_ENTITY
,
275 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
276 adg_marshal_VOID__VOID
,
277 G_TYPE_NONE
, 0, param_types
);
281 * @entity: an #AdgEntity
282 * @cr: a #cairo_t drawing context
284 * Causes the rendering of @entity on @cr. A render signal will
285 * automatically emit #AdgEntity::arrange just before the real
286 * rendering on the cairo context.
290 closure
= g_cclosure_new(G_CALLBACK(_adg_real_render
), NULL
, NULL
);
291 param_types
[0] = G_TYPE_POINTER
;
292 _adg_signals
[RENDER
] =
293 g_signal_newv("render", ADG_TYPE_ENTITY
,
294 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
295 adg_marshal_VOID__POINTER
,
296 G_TYPE_NONE
, 1, param_types
);
300 adg_entity_init(AdgEntity
*entity
)
302 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
306 cairo_matrix_init_identity(&data
->global_map
);
307 cairo_matrix_init_identity(&data
->local_map
);
308 data
->local_method
= ADG_MIX_ANCESTORS
;
309 data
->hash_styles
= NULL
;
310 data
->global
.is_defined
= FALSE
;
311 adg_matrix_copy(&data
->global
.matrix
, adg_matrix_null());
312 data
->local
.is_defined
= FALSE
;
313 adg_matrix_copy(&data
->local
.matrix
, adg_matrix_null());
314 data
->extents
.is_defined
= FALSE
;
320 _adg_dispose(GObject
*object
)
323 AdgEntityPrivate
*data
;
325 entity
= (AdgEntity
*) object
;
328 /* This call will emit a "notify" signal for parent.
329 * Consequentially, the references to the old parent is dropped. */
330 adg_entity_set_parent(entity
, NULL
);
332 if (data
->hash_styles
!= NULL
) {
333 g_hash_table_destroy(data
->hash_styles
);
334 data
->hash_styles
= NULL
;
337 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
338 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
342 _adg_get_property(GObject
*object
, guint prop_id
,
343 GValue
*value
, GParamSpec
*pspec
)
346 AdgEntityPrivate
*data
;
348 entity
= (AdgEntity
*) object
;
353 g_value_set_object(value
, data
->parent
);
355 case PROP_GLOBAL_MAP
:
356 g_value_set_boxed(value
, &data
->global_map
);
359 g_value_set_boxed(value
, &data
->local_map
);
361 case PROP_LOCAL_METHOD
:
362 g_value_set_enum(value
, data
->local_method
);
365 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
371 _adg_set_property(GObject
*object
, guint prop_id
,
372 const GValue
*value
, GParamSpec
*pspec
)
374 AdgEntityPrivate
*data
= ((AdgEntity
*) object
)->data
;
378 _adg_set_parent((AdgEntity
*) object
,
379 (AdgEntity
*) g_value_get_object(value
));
381 case PROP_GLOBAL_MAP
:
382 adg_matrix_copy(&data
->global_map
, g_value_get_boxed(value
));
383 data
->global
.is_defined
= FALSE
;
386 adg_matrix_copy(&data
->local_map
, g_value_get_boxed(value
));
387 data
->local
.is_defined
= FALSE
;
389 case PROP_LOCAL_METHOD
:
390 data
->local_method
= g_value_get_enum(value
);
391 g_signal_emit(object
, _adg_signals
[LOCAL_CHANGED
], 0);
394 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
401 * adg_switch_extents:
402 * @state: new extents state
404 * Strokes (if @state is %TRUE) a rectangle around every entity to
405 * show their extents. Useful for debugging purposes.
410 adg_switch_extents(gboolean state
)
412 _adg_show_extents
= state
;
416 * adg_entity_get_canvas:
417 * @entity: an #AdgEntity
419 * Walks on the @entity hierarchy and gets the first parent of @entity that is
420 * of #AdgCanvas derived type.
422 * Returns: the requested canvas or %NULL on errors or if there is
423 * no #AdgCanvas in the @entity hierarchy
428 adg_entity_get_canvas(AdgEntity
*entity
)
430 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
433 if (ADG_IS_CANVAS(entity
))
434 return (AdgCanvas
*) entity
;
436 entity
= adg_entity_get_parent(entity
);
443 * adg_entity_set_parent:
444 * @entity: an #AdgEntity
445 * @parent: the parent entity
448 * This function is only useful in entity implementations.
451 * Sets a new parent on @entity.
456 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
458 g_return_if_fail(ADG_IS_ENTITY(entity
));
459 g_object_set(entity
, "parent", parent
, NULL
);
463 * adg_entity_get_parent:
464 * @entity: an #AdgEntity
466 * Gets the parent of @entity.
468 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
473 adg_entity_get_parent(AdgEntity
*entity
)
475 AdgEntityPrivate
*data
;
477 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
485 * adg_entity_set_global_map:
486 * @entity: an #AdgEntity object
489 * Sets the new global transformation of @entity to @map:
490 * the old map is discarded. If @map is %NULL, the global
491 * map is left unchanged.
496 adg_entity_set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
498 g_return_if_fail(ADG_IS_ENTITY(entity
));
499 g_object_set(entity
, "global-map", map
, NULL
);
503 * adg_entity_transform_global_map:
504 * @entity: an #AdgEntity object
505 * @transformation: the transformation to apply
506 * @mode: how @transformation should be applied
508 * Convenient function to change the global map of @entity by
509 * applying @tranformation using the @mode operator. This is
510 * logically equivalent to the following:
514 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
515 * adg_matrix_transform(&map, transformation, mode);
516 * adg_entity_set_global_map(entity, &map);
522 adg_entity_transform_global_map(AdgEntity
*entity
,
523 const AdgMatrix
*transformation
,
524 AdgTransformMode mode
)
526 AdgEntityPrivate
*data
;
529 g_return_if_fail(ADG_IS_ENTITY(entity
));
530 g_return_if_fail(transformation
!= NULL
);
534 adg_matrix_copy(&map
, &data
->global_map
);
535 adg_matrix_transform(&map
, transformation
, mode
);
537 g_object_set(entity
, "global-map", &map
, NULL
);
541 * adg_entity_get_global_map:
542 * @entity: an #AdgEntity object
544 * Gets the transformation to be used to compute the global matrix
547 * Returns: the requested map or %NULL on errors
552 adg_entity_get_global_map(AdgEntity
*entity
)
554 AdgEntityPrivate
*data
;
556 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
560 return &data
->global_map
;
564 * adg_entity_get_global_matrix:
565 * @entity: an #AdgEntity object
567 * Gets the current global matrix of @entity. The returned value
568 * is owned by @entity and should not be changed or freed.
570 * The global matrix is computed in the arrange() phase by
571 * combining all the global maps of the @entity hierarchy using
572 * the %ADG_MIX_ANCESTORS method.
574 * Returns: the global matrix or %NULL on errors
579 adg_entity_get_global_matrix(AdgEntity
*entity
)
581 AdgEntityPrivate
*data
;
583 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
587 return &data
->global
.matrix
;
591 * adg_entity_set_local_map:
592 * @entity: an #AdgEntity object
595 * Sets the new local transformation of @entity to @map:
596 * the old map is discarded. If @map is %NULL, the local
597 * map is left unchanged.
602 adg_entity_set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
604 g_return_if_fail(ADG_IS_ENTITY(entity
));
605 g_object_set(entity
, "local-map", map
, NULL
);
609 * adg_entity_transform_local_map:
610 * @entity: an #AdgEntity object
611 * @transformation: the transformation to apply
612 * @mode: how @transformation should be applied
614 * Convenient function to change the local map of @entity by
615 * applying @tranformation using the @mode operator. This is
616 * logically equivalent to the following:
620 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
621 * adg_matrix_transform(&map, transformation, mode);
622 * adg_entity_set_local_map(entity, &map);
628 adg_entity_transform_local_map(AdgEntity
*entity
,
629 const AdgMatrix
*transformation
,
630 AdgTransformMode mode
)
632 AdgEntityPrivate
*data
;
635 g_return_if_fail(ADG_IS_ENTITY(entity
));
636 g_return_if_fail(transformation
!= NULL
);
640 adg_matrix_copy(&map
, &data
->local_map
);
641 adg_matrix_transform(&map
, transformation
, mode
);
642 g_object_set(entity
, "local-map", &map
, NULL
);
646 * adg_entity_get_local_map:
647 * @entity: an #AdgEntity object
649 * Gets the transformation to be used to compute the local matrix
650 * of @entity and store it in @map.
652 * Returns: the requested map or %NULL on errors
657 adg_entity_get_local_map(AdgEntity
*entity
)
659 AdgEntityPrivate
*data
;
661 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
665 return &data
->local_map
;
669 * adg_entity_get_local_matrix:
670 * @entity: an #AdgEntity object
672 * Gets the current local matrix of @entity. The returned value
673 * is owned by @entity and should not be changed or freed.
675 * The local matrix is computed in the arrange() phase by
676 * combining all the local maps of the @entity hierarchy using
677 * the method specified by the #AdgEntity:local-method property.
679 * Returns: the local matrix or %NULL on errors
684 adg_entity_get_local_matrix(AdgEntity
*entity
)
686 AdgEntityPrivate
*data
;
688 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
692 return &data
->local
.matrix
;
696 * adg_entity_set_local_method:
697 * @entity: an #AdgEntity object
698 * @local_method: new method
700 * Sets a new local mix method on @entity. The
701 * #AdgEntity:local-method property defines how the local
702 * matrix must be computed: check out the #AdgMixMethod
703 * documentation to know what are the availables methods
704 * and how they affect the local matrix computation.
706 * Setting a different local method emits an #Adgentity::local-changed
712 adg_entity_set_local_method(AdgEntity
*entity
, AdgMixMethod local_method
)
714 g_return_if_fail(ADG_IS_ENTITY(entity
));
715 g_object_set(entity
, "local-method", local_method
, NULL
);
719 * adg_entity_get_local_method:
720 * @entity: an #AdgEntity object
722 * Gets the local mix method of @entity. Check out the
723 * adg_entity_set_local_method() documentation to know what the
724 * local method is used for.
726 * Returns: the local method of @entity or %ADG_MIX_UNDEFINED on errors
731 adg_entity_get_local_method(AdgEntity
*entity
)
733 AdgEntityPrivate
*data
;
735 g_return_val_if_fail(ADG_IS_ENTITY(entity
), ADG_MIX_UNDEFINED
);
739 return data
->local_method
;
743 * adg_entity_set_extents:
744 * @entity: an #AdgEntity
745 * @extents: the new extents
748 * This function is only useful in entity implementations.
751 * Sets a new bounding box for @entity. @extents can be %NULL,
752 * in which case the extents are unset.
757 adg_entity_set_extents(AdgEntity
*entity
, const CpmlExtents
*extents
)
759 AdgEntityPrivate
*data
;
761 g_return_if_fail(ADG_IS_ENTITY(entity
));
766 data
->extents
.is_defined
= FALSE
;
768 cpml_extents_copy(&data
->extents
, extents
);
772 * adg_entity_get_extents:
773 * @entity: an #AdgEntity
775 * Gets the bounding box of @entity. The returned struct is
776 * owned by @entity and should not modified or freed.
778 * This struct specifies the surface portion (in global space
779 * of @entity) occupied by the entity without taking into
780 * account rendering properties such as line thickness or caps.
782 * The #AdgEntity::arrange signal should be emitted before
783 * this call (either explicitely trought adg_entity_arrange()
784 * or implicitely with adg_entity_render()) in order to get
785 * an up to date boundary box.
787 * Returns: the bounding box of @entity or %NULL on errors
792 adg_entity_get_extents(AdgEntity
*entity
)
794 AdgEntityPrivate
*data
;
796 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
800 return &data
->extents
;
804 * adg_entity_set_style:
805 * @entity: an #AdgEntity
806 * @dress: a dress style
807 * @style: the new style to use
809 * Overrides the style of @dress for @entity and its children.
810 * If @style is %NULL, any previous override is removed.
812 * The new style must still be compatible with @dress: check out
813 * the adg_dress_style_is_compatible() documentation to know
814 * what a compatible style means.
819 adg_entity_set_style(AdgEntity
*entity
, AdgDress dress
, AdgStyle
*style
)
821 AdgEntityPrivate
*data
;
825 g_return_if_fail(ADG_IS_ENTITY(entity
));
829 if (data
->hash_styles
== NULL
&& style
== NULL
)
832 if (data
->hash_styles
== NULL
)
833 data
->hash_styles
= g_hash_table_new_full(NULL
, NULL
,
834 NULL
, g_object_unref
);
836 p_dress
= GINT_TO_POINTER(dress
);
837 old_style
= g_hash_table_lookup(data
->hash_styles
, p_dress
);
839 if (style
== old_style
)
843 g_hash_table_remove(data
->hash_styles
, p_dress
);
847 if (!adg_dress_style_is_compatible(dress
, style
)) {
848 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
850 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
851 G_STRLOC
, g_type_name(G_TYPE_FROM_INSTANCE(style
)),
852 g_type_name(ancestor_type
), adg_dress_get_name(dress
));
858 g_hash_table_replace(data
->hash_styles
, p_dress
, style
);
862 * adg_entity_get_style:
863 * @entity: an #AdgEntity
864 * @dress: the dress of the style to get
866 * Gets the overriden @dress style from @entity. This is a kind
867 * of accessor function: to get the style to be used for rendering
868 * purpose, use adg_entity_style() instead.
870 * Returns: the requested style or %NULL if the @dress style
876 adg_entity_get_style(AdgEntity
*entity
, AdgDress dress
)
878 AdgEntityPrivate
*data
;
880 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
884 if (data
->hash_styles
== NULL
)
887 return g_hash_table_lookup(data
->hash_styles
, GINT_TO_POINTER(dress
));
892 * @entity: an #AdgEntity
893 * @dress: the dress of the style to get
895 * Gets the style to be used for @entity. @dress specifies which
896 * "family" of style to get.
898 * The following sequence of checks is performed to get the proper
899 * style, stopping at the first succesfull result:
902 * <listitem>check if the style is directly overriden by this entity,
903 * as returned by adg_entity_get_style();</listitem>
904 * <listitem>check if @entity has a parent, in which case returns the
905 * adg_entity_style() of the parent;</listitem>
906 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
909 * Returns: the requested style or %NULL for transparent dresses or errors
914 adg_entity_style(AdgEntity
*entity
, AdgDress dress
)
918 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
920 style
= adg_entity_get_style(entity
, dress
);
923 AdgEntityPrivate
*data
= entity
->data
;
925 if (data
->parent
!= NULL
)
926 style
= adg_entity_style(data
->parent
, dress
);
928 style
= adg_dress_get_fallback(dress
);
935 * adg_entity_apply_dress:
936 * @entity: an #AdgEntity
937 * @dress: the dress style to apply
938 * @cr: a #cairo_t drawing context
940 * Convenient function to apply a @dress style (as returned by
941 * adg_entity_style()) to the @cr cairo context.
946 adg_entity_apply_dress(AdgEntity
*entity
, AdgDress dress
, cairo_t
*cr
)
950 g_return_if_fail(ADG_IS_ENTITY(entity
));
951 g_return_if_fail(cr
!= NULL
);
953 style
= adg_entity_style(entity
, dress
);
956 adg_style_apply(style
, entity
, cr
);
960 * adg_entity_global_changed:
961 * @entity: an #AdgEntity
963 * Emits the #AdgEntity::global-changed signal on @entity and on all of
964 * its children, if any.
969 adg_entity_global_changed(AdgEntity
*entity
)
971 g_return_if_fail(ADG_IS_ENTITY(entity
));
973 g_signal_emit(entity
, _adg_signals
[GLOBAL_CHANGED
], 0);
977 * adg_entity_local_changed:
978 * @entity: an #AdgEntity
980 * Emits the #AdgEntity::local-changed signal on @entity and on all of
981 * its children, if any.
986 adg_entity_local_changed(AdgEntity
*entity
)
988 g_return_if_fail(ADG_IS_ENTITY(entity
));
990 g_signal_emit(entity
, _adg_signals
[LOCAL_CHANGED
], 0);
994 * adg_entity_invalidate:
995 * @entity: an #AdgEntity
997 * Emits the #AdgEntity::invalidate signal on @entity and on all of
998 * its children, if any, clearing the eventual cache stored by the
999 * #AdgEntity::arrange signal and setting the entity state similary
1000 * to the just initialized entity.
1005 adg_entity_invalidate(AdgEntity
*entity
)
1007 g_return_if_fail(ADG_IS_ENTITY(entity
));
1009 g_signal_emit(entity
, _adg_signals
[INVALIDATE
], 0);
1013 * adg_entity_arrange:
1014 * @entity: an #AdgEntity
1016 * Emits the #AdgEntity::arrange signal on @entity and all its children,
1017 * if any. The arrange call is implicitely called by the
1018 * #AdgEntity::render signal but not by adg_entity_get_extents().
1023 adg_entity_arrange(AdgEntity
*entity
)
1025 g_return_if_fail(ADG_IS_ENTITY(entity
));
1027 g_signal_emit(entity
, _adg_signals
[ARRANGE
], 0);
1031 * adg_entity_render:
1032 * @entity: an #AdgEntity
1033 * @cr: a #cairo_t drawing context
1035 * Emits the #AdgEntity::render signal on @entity and on all of its
1036 * children, if any, causing the rendering to the @cr cairo context.
1041 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
1043 g_return_if_fail(ADG_IS_ENTITY(entity
));
1045 g_signal_emit(entity
, _adg_signals
[RENDER
], 0, cr
);
1050 * @entity: an #AdgEntity
1051 * @point: the #AdgPoint to define
1052 * @new_point: the new #AdgPoint value
1055 * This function is only useful in entity implementations.
1058 * A convenient method to set an #AdgPoint owned by @entity.
1059 * @point is the old value while @new_point is the new value. It
1060 * can be used for setting #AdgPoint in the private data, such as:
1063 * data->point = adg_entity_point(entity, data->point, new_point);
1066 * This function takes care of the dependencies between @entity and
1067 * the eventual models bound to the old and new points.
1069 * @point can be %NULL, in which case a clone of @new_point will be
1070 * returned. Also @new_point can be %NULL, in which case @point is
1071 * destroyed and %NULL will be returned.
1073 * Returns: the new properly defined point
1078 adg_entity_point(AdgEntity
*entity
, AdgPoint
*point
, AdgPoint
*new_point
)
1080 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
1082 if (!adg_point_equal(point
, new_point
)) {
1083 AdgModel
*old_model
, *new_model
;
1085 old_model
= point
? adg_point_get_model(point
) : NULL
;
1086 new_model
= new_point
? adg_point_get_model(new_point
) : NULL
;
1088 if (new_model
!= old_model
) {
1090 adg_model_add_dependency(new_model
, entity
);
1092 adg_model_remove_dependency(old_model
, entity
);
1095 if (new_point
&& point
) {
1096 adg_point_copy(point
, new_point
);
1097 } else if (new_point
) {
1098 point
= adg_point_dup(new_point
);
1100 adg_point_destroy(point
);
1110 _adg_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
1112 AdgEntityPrivate
*data
;
1113 AdgEntity
*old_parent
;
1115 data
= entity
->data
;
1116 old_parent
= data
->parent
;
1119 g_object_ref(parent
);
1121 data
->parent
= parent
;
1122 data
->global
.is_defined
= FALSE
;
1123 data
->local
.is_defined
= FALSE
;
1125 g_signal_emit(entity
, _adg_signals
[PARENT_SET
], 0, old_parent
);
1128 g_object_unref(old_parent
);
1132 _adg_global_changed(AdgEntity
*entity
)
1134 AdgEntityPrivate
*data
;
1135 const AdgMatrix
*map
;
1138 data
= entity
->data
;
1139 map
= &data
->global_map
;
1140 matrix
= &data
->global
.matrix
;
1143 adg_matrix_copy(matrix
, adg_entity_get_global_matrix(data
->parent
));
1144 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1146 adg_matrix_copy(matrix
, map
);
1151 _adg_local_changed(AdgEntity
*entity
)
1153 AdgEntityPrivate
*data
;
1154 const AdgMatrix
*map
;
1157 data
= entity
->data
;
1158 map
= &data
->local_map
;
1159 matrix
= &data
->local
.matrix
;
1161 switch (data
->local_method
) {
1162 case ADG_MIX_DISABLED
:
1163 adg_matrix_copy(matrix
, adg_matrix_identity());
1166 adg_matrix_copy(matrix
, map
);
1168 case ADG_MIX_ANCESTORS
:
1170 adg_matrix_copy(matrix
, adg_entity_get_local_matrix(data
->parent
));
1171 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1173 adg_matrix_copy(matrix
, map
);
1176 case ADG_MIX_ANCESTORS_NORMALIZED
:
1178 adg_matrix_copy(matrix
, adg_entity_get_local_matrix(data
->parent
));
1179 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1181 adg_matrix_copy(matrix
, map
);
1183 adg_matrix_normalize(matrix
);
1185 case ADG_MIX_PARENT
:
1187 adg_matrix_copy(matrix
, adg_entity_get_local_map(data
->parent
));
1188 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1190 adg_matrix_copy(matrix
, map
);
1193 case ADG_MIX_PARENT_NORMALIZED
:
1195 adg_matrix_copy(matrix
, adg_entity_get_local_map(data
->parent
));
1196 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1198 adg_matrix_copy(matrix
, map
);
1200 adg_matrix_normalize(matrix
);
1202 case ADG_MIX_UNDEFINED
:
1203 g_warning(_("%s: requested to mix the maps using an undefined method"),
1207 g_return_if_reached();
1213 _adg_real_invalidate(AdgEntity
*entity
)
1215 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1216 AdgEntityPrivate
*data
= entity
->data
;
1218 /* Do not raise any warning if invalidate() is not defined,
1219 * assuming entity does not have additional cache to be cleared */
1220 if (klass
->invalidate
)
1221 klass
->invalidate(entity
);
1223 data
->extents
.is_defined
= FALSE
;
1227 _adg_real_arrange(AdgEntity
*entity
)
1229 AdgEntityClass
*klass
;
1230 AdgEntityPrivate
*data
;
1232 klass
= ADG_ENTITY_GET_CLASS(entity
);
1233 data
= entity
->data
;
1235 /* Update the global matrix, if required */
1236 if (!data
->global
.is_defined
) {
1237 data
->global
.is_defined
= TRUE
;
1238 g_signal_emit(entity
, _adg_signals
[GLOBAL_CHANGED
], 0);
1241 /* Update the local matrix, if required */
1242 if (!data
->local
.is_defined
) {
1243 data
->local
.is_defined
= TRUE
;
1244 g_signal_emit(entity
, _adg_signals
[LOCAL_CHANGED
], 0);
1247 /* The arrange() method must be defined */
1248 if (klass
->arrange
== NULL
) {
1249 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1250 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1251 data
->extents
.is_defined
= FALSE
;
1255 klass
->arrange(entity
);
1259 _adg_real_render(AdgEntity
*entity
, cairo_t
*cr
)
1261 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1263 /* The render method must be defined */
1264 if (klass
->render
== NULL
) {
1265 g_warning(_("%s: `render' method not implemented for type `%s'"),
1266 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1270 /* Before the rendering, the entity should be arranged */
1271 g_signal_emit(entity
, _adg_signals
[ARRANGE
], 0);
1274 klass
->render(entity
, cr
);
1277 if (_adg_show_extents
) {
1278 AdgEntityPrivate
*data
= entity
->data
;
1279 CpmlExtents
*extents
= &data
->extents
;
1281 if (extents
->is_defined
) {
1283 cairo_set_source_rgba(cr
, 0.15, 0.15, 0.15, 0.15);
1284 cairo_rectangle(cr
, extents
->org
.x
, extents
->org
.y
,
1285 extents
->size
.x
, extents
->size
.y
);