1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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 * @destroy: when a destroy request has been explicitely requested
47 * @parent_set: called whenever the parent of an entity has changed
48 * @global_changed: the global matrix has been invalidated
49 * @local_changed: the local matrix has been invalidated
50 * @invalidate: invalidating callback, used to clear the internal cache
51 * @arrange: prepare the layout and fill the extents struct
52 * @render: rendering callback, it must be implemented by every entity
54 * Any entity (if not abstract) must implement at least the @render method.
55 * The other signal handlers can be overriden to provide custom behaviors
56 * and usually must chain up the original handler.
63 * @entity: an #AdgEntity
64 * @user_data: a general purpose pointer
66 * Callback used when inspecting or browsing entities. For example,
67 * it is passed to adg_model_foreach_dependency() to perform an
68 * operation on all the entities depending on an #AdgModel.
74 #include "adg-internal.h"
75 #if GTK3_ENABLED || GTK2_ENABLED
79 #include "adg-container.h"
80 #include "adg-table.h"
81 #include "adg-title-block.h"
82 #include <adg-canvas.h>
83 #include "adg-dress.h"
84 #include "adg-style.h"
85 #include "adg-model.h"
86 #include "adg-point.h"
87 #include "adg-cairo-fallback.h"
89 #include "adg-entity-private.h"
92 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
95 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
)
117 static void _adg_dispose (GObject
*object
);
118 static void _adg_get_property (GObject
*object
,
122 static void _adg_set_property (GObject
*object
,
126 static void _adg_set_parent (AdgEntity
*entity
,
128 static void _adg_global_changed (AdgEntity
*entity
);
129 static void _adg_local_changed (AdgEntity
*entity
);
130 static void _adg_real_invalidate (AdgEntity
*entity
);
131 static void _adg_real_arrange (AdgEntity
*entity
);
132 static void _adg_real_render (AdgEntity
*entity
,
134 static guint _adg_signals
[LAST_SIGNAL
] = { 0 };
135 static gboolean _adg_show_extents
= FALSE
;
139 adg_entity_class_init(AdgEntityClass
*klass
)
141 GObjectClass
*gobject_class
;
144 GType param_types
[1];
146 gobject_class
= (GObjectClass
*) klass
;
148 g_type_class_add_private(klass
, sizeof(AdgEntityPrivate
));
150 gobject_class
->dispose
= _adg_dispose
;
151 gobject_class
->get_property
= _adg_get_property
;
152 gobject_class
->set_property
= _adg_set_property
;
154 klass
->destroy
= (void (*)(AdgEntity
*)) g_object_unref
;
155 klass
->parent_set
= NULL
;
156 klass
->global_changed
= _adg_global_changed
;
157 klass
->local_changed
= _adg_local_changed
;
158 klass
->invalidate
= NULL
;
159 klass
->arrange
= NULL
;
160 klass
->render
= NULL
;
162 param
= g_param_spec_object("parent",
164 P_("The parent entity of this entity or NULL if this is a top-level entity"),
167 g_object_class_install_property(gobject_class
, PROP_PARENT
, param
);
169 param
= g_param_spec_boxed("global-map",
171 P_("The transformation to be combined with the parent ones to get the global matrix"),
172 CAIRO_GOBJECT_TYPE_MATRIX
,
174 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
176 param
= g_param_spec_boxed("local-map",
178 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-mix property"),
179 CAIRO_GOBJECT_TYPE_MATRIX
,
181 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
183 param
= g_param_spec_enum("local-mix",
184 P_("Local Mix Method"),
185 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
186 ADG_TYPE_MIX
, ADG_MIX_ANCESTORS
,
188 g_object_class_install_property(gobject_class
, PROP_LOCAL_MIX
, param
);
191 * AdgEntity::destroy:
192 * @entity: an #AdgEntity
194 * Emitted to explicitely destroy @entity. It unreferences
195 * @entity so that will be destroyed, unless the caller owns
196 * an additional references added with g_object_ref().
198 * In the usual case, this is equivalent of calling
199 * g_object_unref() on @entity but, for composite entities or
200 * containers, the destroy signal is propagated to the children.
204 _adg_signals
[DESTROY
] =
205 g_signal_new("destroy",
206 G_OBJECT_CLASS_TYPE(gobject_class
),
208 G_STRUCT_OFFSET(AdgEntityClass
, destroy
),
210 adg_marshal_VOID__VOID
,
214 * AdgEntity::parent-set:
215 * @entity: an #AdgEntity
216 * @old_parent: the old parent
218 * Emitted after the parent entity has changed. The new parent
219 * can be inspected using adg_entity_get_parent().
221 * It is allowed for both old and new parent to
222 * be <constant>NULL</constant>.
226 _adg_signals
[PARENT_SET
] =
227 g_signal_new("parent-set",
228 G_OBJECT_CLASS_TYPE(gobject_class
),
230 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
232 adg_marshal_VOID__OBJECT
,
233 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
236 * AdgEntity::global-changed:
237 * @entity: an #AdgEntity
239 * Emitted when the global map of @entity or any of its parent
240 * has changed. The default handler will compute the new global
241 * matrix, updating the internal cache.
245 _adg_signals
[GLOBAL_CHANGED
] =
246 g_signal_new("global-changed",
247 G_OBJECT_CLASS_TYPE(gobject_class
),
249 G_STRUCT_OFFSET(AdgEntityClass
, global_changed
),
251 adg_marshal_VOID__VOID
,
255 * AdgEntity::local-changed:
256 * @entity: an #AdgEntity
258 * Emitted when the local map of @entity or any of its parent
259 * has changed. The default handler will compute the new local
260 * matrix, updating the internal cache.
264 _adg_signals
[LOCAL_CHANGED
] =
265 g_signal_new("local-changed",
266 G_OBJECT_CLASS_TYPE(gobject_class
),
268 G_STRUCT_OFFSET(AdgEntityClass
, local_changed
),
270 adg_marshal_VOID__VOID
,
274 * AdgEntity::invalidate:
275 * @entity: an #AdgEntity
277 * Invalidates the whole @entity, that is resets all the cache
278 * (if present) built during the #AdgEntity::arrange signal.
279 * The resulting state is a clean entity, similar to what you
280 * have just before the first rendering.
284 closure
= g_cclosure_new(G_CALLBACK(_adg_real_invalidate
), NULL
, NULL
);
285 _adg_signals
[INVALIDATE
] =
286 g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
287 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
288 adg_marshal_VOID__VOID
,
289 G_TYPE_NONE
, 0, param_types
);
292 * AdgEntity::arrange:
293 * @entity: an #AdgEntity
295 * Arranges the layout of @entity, updating the cache if necessary,
296 * and computes the extents of @entity.
300 closure
= g_cclosure_new(G_CALLBACK(_adg_real_arrange
), NULL
, NULL
);
301 _adg_signals
[ARRANGE
] =
302 g_signal_newv("arrange", ADG_TYPE_ENTITY
,
303 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
304 adg_marshal_VOID__VOID
,
305 G_TYPE_NONE
, 0, param_types
);
309 * @entity: an #AdgEntity
310 * @cr: a #cairo_t drawing context
312 * Causes the rendering of @entity on @cr. A render signal will
313 * automatically emit #AdgEntity::arrange just before the real
314 * rendering on the cairo context.
318 closure
= g_cclosure_new(G_CALLBACK(_adg_real_render
), NULL
, NULL
);
319 param_types
[0] = G_TYPE_POINTER
;
320 _adg_signals
[RENDER
] =
321 g_signal_newv("render", ADG_TYPE_ENTITY
,
322 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
323 adg_marshal_VOID__POINTER
,
324 G_TYPE_NONE
, 1, param_types
);
328 adg_entity_init(AdgEntity
*entity
)
330 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
334 cairo_matrix_init_identity(&data
->global_map
);
335 cairo_matrix_init_identity(&data
->local_map
);
336 data
->local_mix
= ADG_MIX_ANCESTORS
;
337 data
->hash_styles
= NULL
;
338 data
->global
.is_defined
= FALSE
;
339 adg_matrix_copy(&data
->global
.matrix
, adg_matrix_null());
340 data
->local
.is_defined
= FALSE
;
341 adg_matrix_copy(&data
->local
.matrix
, adg_matrix_null());
342 data
->extents
.is_defined
= FALSE
;
348 _adg_dispose(GObject
*object
)
351 AdgEntityPrivate
*data
;
353 entity
= (AdgEntity
*) object
;
356 /* This call will emit a "notify" signal for parent.
357 * Consequentially, the references to the old parent is dropped. */
358 adg_entity_set_parent(entity
, NULL
);
360 if (data
->hash_styles
!= NULL
) {
361 g_hash_table_destroy(data
->hash_styles
);
362 data
->hash_styles
= NULL
;
365 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
366 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
370 _adg_get_property(GObject
*object
, guint prop_id
,
371 GValue
*value
, GParamSpec
*pspec
)
374 AdgEntityPrivate
*data
;
376 entity
= (AdgEntity
*) object
;
381 g_value_set_object(value
, data
->parent
);
383 case PROP_GLOBAL_MAP
:
384 g_value_set_boxed(value
, &data
->global_map
);
387 g_value_set_boxed(value
, &data
->local_map
);
390 g_value_set_enum(value
, data
->local_mix
);
393 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
399 _adg_set_property(GObject
*object
, guint prop_id
,
400 const GValue
*value
, GParamSpec
*pspec
)
402 AdgEntityPrivate
*data
= ((AdgEntity
*) object
)->data
;
406 _adg_set_parent((AdgEntity
*) object
,
407 (AdgEntity
*) g_value_get_object(value
));
409 case PROP_GLOBAL_MAP
:
410 adg_matrix_copy(&data
->global_map
, g_value_get_boxed(value
));
411 data
->global
.is_defined
= FALSE
;
414 adg_matrix_copy(&data
->local_map
, g_value_get_boxed(value
));
415 data
->local
.is_defined
= FALSE
;
418 data
->local_mix
= g_value_get_enum(value
);
419 g_signal_emit(object
, _adg_signals
[LOCAL_CHANGED
], 0);
422 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
429 * adg_switch_extents:
430 * @state: new extents state
432 * Strokes (if @state is <constant>TRUE</constant>) a rectangle
433 * around every entity to show their extents. Useful for
434 * debugging purposes.
439 adg_switch_extents(gboolean state
)
441 _adg_show_extents
= state
;
445 * adg_entity_destroy:
446 * @entity: an #AdgEntity
448 * Emits the #AdgEntity::destroy signal on @entity and on all of
449 * its children, if any.
454 adg_entity_destroy(AdgEntity
*entity
)
456 g_return_if_fail(ADG_IS_ENTITY(entity
));
458 g_signal_emit(entity
, _adg_signals
[DESTROY
], 0);
462 * adg_entity_get_canvas:
463 * @entity: an #AdgEntity
465 * Walks on the @entity hierarchy and gets the first parent of @entity,
466 * that is the first #AdgCanvas instance. The returned object is
467 * owned by @entity and should not be freed or modified.
469 * Returns: (transfer none): the requested canvas or <constant>NULL</constant> on errors or if there is no #AdgCanvas in the @entity hierarchy.
474 adg_entity_get_canvas(AdgEntity
*entity
)
476 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
479 if (ADG_IS_CANVAS(entity
))
480 return (AdgCanvas
*) entity
;
482 entity
= adg_entity_get_parent(entity
);
489 * adg_entity_set_parent:
490 * @entity: an #AdgEntity
491 * @parent: the parent entity
494 * This function is only useful in entity implementations.
497 * Sets a new parent on @entity.
502 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
504 g_return_if_fail(ADG_IS_ENTITY(entity
));
505 g_object_set(entity
, "parent", parent
, NULL
);
509 * adg_entity_get_parent:
510 * @entity: an #AdgEntity
512 * Gets the parent of @entity. The returned object is owned
513 * by @entity and should not be freed or modified.
515 * Returns: (transfer none): the parent entity or <constant>NULL</constant> on errors or if @entity is a toplevel.
520 adg_entity_get_parent(AdgEntity
*entity
)
522 AdgEntityPrivate
*data
;
524 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
532 * adg_entity_set_global_map:
533 * @entity: an #AdgEntity object
536 * Sets the new global transformation of @entity to @map:
537 * the old map is discarded. If @map is <constant>NULL</constant>,
538 * the global map is left unchanged.
543 adg_entity_set_global_map(AdgEntity
*entity
, const cairo_matrix_t
*map
)
545 g_return_if_fail(ADG_IS_ENTITY(entity
));
546 g_object_set(entity
, "global-map", map
, NULL
);
550 * adg_entity_transform_global_map:
551 * @entity: an #AdgEntity object
552 * @transformation: the transformation to apply
553 * @mode: how @transformation should be applied
555 * Convenient function to change the global map of @entity by
556 * applying @tranformation using the @mode operator. This is
557 * logically equivalent to the following:
559 * <informalexample><programlisting language="C">
560 * cairo_matrix_t map;
561 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
562 * adg_matrix_transform(&map, transformation, mode);
563 * adg_entity_set_global_map(entity, &map);
564 * </programlisting></informalexample>
569 adg_entity_transform_global_map(AdgEntity
*entity
,
570 const cairo_matrix_t
*transformation
,
571 AdgTransformMode mode
)
573 AdgEntityPrivate
*data
;
576 g_return_if_fail(ADG_IS_ENTITY(entity
));
577 g_return_if_fail(transformation
!= NULL
);
581 adg_matrix_copy(&map
, &data
->global_map
);
582 adg_matrix_transform(&map
, transformation
, mode
);
584 g_object_set(entity
, "global-map", &map
, NULL
);
588 * adg_entity_get_global_map:
589 * @entity: an #AdgEntity object
591 * Gets the transformation to be used to compute the global matrix
594 * Returns: the requested map or <constant>NULL</constant> on errors.
598 const cairo_matrix_t
*
599 adg_entity_get_global_map(AdgEntity
*entity
)
601 AdgEntityPrivate
*data
;
603 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
607 return &data
->global_map
;
611 * adg_entity_get_global_matrix:
612 * @entity: an #AdgEntity object
614 * Gets the current global matrix of @entity. The returned value
615 * is owned by @entity and should not be changed or freed.
617 * The global matrix is computed in the arrange() phase by
618 * combining all the global maps of the @entity hierarchy using
619 * the %ADG_MIX_ANCESTORS method.
621 * Returns: the global matrix or <constant>NULL</constant> on errors.
625 const cairo_matrix_t
*
626 adg_entity_get_global_matrix(AdgEntity
*entity
)
628 AdgEntityPrivate
*data
;
630 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
634 return &data
->global
.matrix
;
638 * adg_entity_set_local_map:
639 * @entity: an #AdgEntity object
642 * Sets the new local transformation of @entity to @map:
643 * the old map is discarded. If @map is <constant>NULL</constant>,
644 * the local map is left unchanged.
649 adg_entity_set_local_map(AdgEntity
*entity
, const cairo_matrix_t
*map
)
651 g_return_if_fail(ADG_IS_ENTITY(entity
));
652 g_object_set(entity
, "local-map", map
, NULL
);
656 * adg_entity_transform_local_map:
657 * @entity: an #AdgEntity object
658 * @transformation: the transformation to apply
659 * @mode: how @transformation should be applied
661 * Convenient function to change the local map of @entity by
662 * applying @tranformation using the @mode operator. This is
663 * logically equivalent to the following:
665 * <informalexample><programlisting language="C">
666 * cairo_matrix_t map;
667 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
668 * adg_matrix_transform(&map, transformation, mode);
669 * adg_entity_set_local_map(entity, &map);
670 * </programlisting></informalexample>
675 adg_entity_transform_local_map(AdgEntity
*entity
,
676 const cairo_matrix_t
*transformation
,
677 AdgTransformMode mode
)
679 AdgEntityPrivate
*data
;
682 g_return_if_fail(ADG_IS_ENTITY(entity
));
683 g_return_if_fail(transformation
!= NULL
);
687 adg_matrix_copy(&map
, &data
->local_map
);
688 adg_matrix_transform(&map
, transformation
, mode
);
689 g_object_set(entity
, "local-map", &map
, NULL
);
693 * adg_entity_get_local_map:
694 * @entity: an #AdgEntity object
696 * Gets the transformation to be used to compute the local matrix
697 * of @entity and store it in @map.
699 * Returns: the requested map or <constant>NULL</constant> on errors.
703 const cairo_matrix_t
*
704 adg_entity_get_local_map(AdgEntity
*entity
)
706 AdgEntityPrivate
*data
;
708 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
712 return &data
->local_map
;
716 * adg_entity_get_local_matrix:
717 * @entity: an #AdgEntity object
719 * Gets the current local matrix of @entity. The returned value
720 * is owned by @entity and should not be changed or freed.
722 * The local matrix is computed in the arrange() phase by
723 * combining all the local maps of the @entity hierarchy using
724 * the method specified by the #AdgEntity:local-mix property.
726 * Returns: the local matrix or <constant>NULL</constant> on errors.
730 const cairo_matrix_t
*
731 adg_entity_get_local_matrix(AdgEntity
*entity
)
733 AdgEntityPrivate
*data
;
735 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
739 return &data
->local
.matrix
;
743 * adg_entity_set_local_mix:
744 * @entity: an #AdgEntity object
745 * @local_mix: new mix method
747 * Sets a new local mix method on @entity. The
748 * #AdgEntity:local-mix property defines how the local
749 * matrix must be computed: check out the #AdgMix
750 * documentation to know what are the availables methods
751 * and how they affect the local matrix computation.
753 * Setting a different local mix method emits an
754 * #AdgEntity::local-changed signal on @entity.
759 adg_entity_set_local_mix(AdgEntity
*entity
, AdgMix local_mix
)
761 g_return_if_fail(ADG_IS_ENTITY(entity
));
762 g_object_set(entity
, "local-mix", local_mix
, NULL
);
766 * adg_entity_get_local_mix:
767 * @entity: an #AdgEntity object
769 * Gets the local mix method of @entity. Check out the
770 * adg_entity_set_local_mix() documentation to know what the
771 * local mix method is used for.
773 * Returns: the local mix method of @entity or %ADG_MIX_UNDEFINED on errors
778 adg_entity_get_local_mix(AdgEntity
*entity
)
780 AdgEntityPrivate
*data
;
782 g_return_val_if_fail(ADG_IS_ENTITY(entity
), ADG_MIX_UNDEFINED
);
786 return data
->local_mix
;
790 * adg_entity_set_extents:
791 * @entity: an #AdgEntity
792 * @extents: the new extents
795 * This function is only useful in entity implementations.
798 * Sets a new bounding box for @entity. @extents can
799 * be <constant>NULL</constant>, in which case the extents are unset.
804 adg_entity_set_extents(AdgEntity
*entity
, const CpmlExtents
*extents
)
806 AdgEntityPrivate
*data
;
808 g_return_if_fail(ADG_IS_ENTITY(entity
));
813 data
->extents
.is_defined
= FALSE
;
815 cpml_extents_copy(&data
->extents
, extents
);
819 * adg_entity_get_extents:
820 * @entity: an #AdgEntity
822 * Gets the bounding box of @entity. The returned struct is
823 * owned by @entity and should not modified or freed.
825 * This struct specifies the surface portion (in global space
826 * of @entity) occupied by the entity without taking into
827 * account rendering properties such as line thickness or caps.
829 * The #AdgEntity::arrange signal should be emitted before
830 * this call (either explicitely trought adg_entity_arrange()
831 * or implicitely with adg_entity_render()) in order to get
832 * an up to date boundary box.
834 * Returns: the bounding box of @entity or <constant>NULL</constant> on errors.
839 adg_entity_get_extents(AdgEntity
*entity
)
841 AdgEntityPrivate
*data
;
843 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
847 return &data
->extents
;
851 * adg_entity_set_style:
852 * @entity: an #AdgEntity
853 * @dress: a dress style
854 * @style: the new style to use
856 * Overrides the style of @dress for @entity and its children.
857 * If @style is <constant>NULL</constant>, any previous
858 * override is removed.
860 * The new style must still be compatible with @dress: check out
861 * the adg_dress_style_is_compatible() documentation to know
862 * what a compatible style means.
867 adg_entity_set_style(AdgEntity
*entity
, AdgDress dress
, AdgStyle
*style
)
869 AdgEntityPrivate
*data
;
873 g_return_if_fail(ADG_IS_ENTITY(entity
));
877 if (data
->hash_styles
== NULL
&& style
== NULL
)
880 if (data
->hash_styles
== NULL
)
881 data
->hash_styles
= g_hash_table_new_full(NULL
, NULL
,
882 NULL
, g_object_unref
);
884 p_dress
= GINT_TO_POINTER(dress
);
885 old_style
= g_hash_table_lookup(data
->hash_styles
, p_dress
);
887 if (style
== old_style
)
891 g_hash_table_remove(data
->hash_styles
, p_dress
);
895 if (!adg_dress_style_is_compatible(dress
, style
)) {
896 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
898 g_warning(_("%s: `%s' is not compatible with `%s' for `%s' dress"),
899 G_STRLOC
, g_type_name(G_TYPE_FROM_INSTANCE(style
)),
900 g_type_name(ancestor_type
), adg_dress_get_name(dress
));
906 g_hash_table_replace(data
->hash_styles
, p_dress
, style
);
910 * adg_entity_get_style:
911 * @entity: an #AdgEntity
912 * @dress: the dress of the style to get
914 * Gets the overriden @dress style from @entity. This is a kind
915 * of accessor function: for rendering purpose use adg_entity_style()
916 * instead. The returned object is owned by @entity and should not be
919 * Returns: (transfer none): the requested style or <constant>NULL</constant> if the @dress style is not overriden.
924 adg_entity_get_style(AdgEntity
*entity
, AdgDress dress
)
926 AdgEntityPrivate
*data
;
928 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
932 if (data
->hash_styles
== NULL
)
935 return g_hash_table_lookup(data
->hash_styles
, GINT_TO_POINTER(dress
));
940 * @entity: an #AdgEntity
941 * @dress: the dress of the style to get
943 * Gets the style to be used for @entity. @dress specifies which
944 * "family" of style to get.
946 * The following sequence of checks is performed to get the proper
947 * style, stopping at the first succesfull result:
950 * <listitem>check if the style is directly overriden by this entity,
951 * as returned by adg_entity_get_style();</listitem>
952 * <listitem>check if @entity has a parent, in which case returns the
953 * adg_entity_style() of the parent;</listitem>
954 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
957 * The returned object is owned by @entity and should not be
960 * Returns: (transfer none): the requested style or <constant>NULL</constant> for transparent dresses or errors.
965 adg_entity_style(AdgEntity
*entity
, AdgDress dress
)
969 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
971 style
= adg_entity_get_style(entity
, dress
);
974 AdgEntityPrivate
*data
= entity
->data
;
976 if (data
->parent
!= NULL
)
977 style
= adg_entity_style(data
->parent
, dress
);
979 style
= adg_dress_get_fallback(dress
);
986 * adg_entity_apply_dress:
987 * @entity: an #AdgEntity
988 * @dress: the dress style to apply
989 * @cr: a #cairo_t drawing context
991 * Convenient function to apply a @dress style (as returned by
992 * adg_entity_style()) to the @cr cairo context.
997 adg_entity_apply_dress(AdgEntity
*entity
, AdgDress dress
, cairo_t
*cr
)
1001 g_return_if_fail(ADG_IS_ENTITY(entity
));
1002 g_return_if_fail(cr
!= NULL
);
1004 style
= adg_entity_style(entity
, dress
);
1007 adg_style_apply(style
, entity
, cr
);
1011 * adg_entity_global_changed:
1012 * @entity: an #AdgEntity
1014 * Emits the #AdgEntity::global-changed signal on @entity and on all of
1015 * its children, if any.
1020 adg_entity_global_changed(AdgEntity
*entity
)
1022 g_return_if_fail(ADG_IS_ENTITY(entity
));
1024 g_signal_emit(entity
, _adg_signals
[GLOBAL_CHANGED
], 0);
1028 * adg_entity_local_changed:
1029 * @entity: an #AdgEntity
1031 * Emits the #AdgEntity::local-changed signal on @entity and on all of
1032 * its children, if any.
1037 adg_entity_local_changed(AdgEntity
*entity
)
1039 g_return_if_fail(ADG_IS_ENTITY(entity
));
1041 g_signal_emit(entity
, _adg_signals
[LOCAL_CHANGED
], 0);
1045 * adg_entity_invalidate:
1046 * @entity: an #AdgEntity
1048 * Emits the #AdgEntity::invalidate signal on @entity and on all of
1049 * its children, if any, clearing the eventual cache stored by the
1050 * #AdgEntity::arrange signal and setting the entity state similary
1051 * to the just initialized entity.
1056 adg_entity_invalidate(AdgEntity
*entity
)
1058 g_return_if_fail(ADG_IS_ENTITY(entity
));
1060 g_signal_emit(entity
, _adg_signals
[INVALIDATE
], 0);
1064 * adg_entity_arrange:
1065 * @entity: an #AdgEntity
1067 * Emits the #AdgEntity::arrange signal on @entity and all its children,
1068 * if any. The arrange call is implicitely called by the
1069 * #AdgEntity::render signal but not by adg_entity_get_extents().
1074 adg_entity_arrange(AdgEntity
*entity
)
1076 g_return_if_fail(ADG_IS_ENTITY(entity
));
1078 g_signal_emit(entity
, _adg_signals
[ARRANGE
], 0);
1082 * adg_entity_render:
1083 * @entity: an #AdgEntity
1084 * @cr: a #cairo_t drawing context
1086 * Emits the #AdgEntity::render signal on @entity and on all of its
1087 * children, if any, causing the rendering to the @cr cairo context.
1092 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
1094 g_return_if_fail(ADG_IS_ENTITY(entity
));
1096 g_signal_emit(entity
, _adg_signals
[RENDER
], 0, cr
);
1101 * @entity: an #AdgEntity
1102 * @point: the #AdgPoint to define
1103 * @new_point: the new #AdgPoint value
1106 * This function is only useful in entity implementations.
1109 * A convenient method to set an #AdgPoint owned by @entity.
1110 * @old_point is the old value while @new_point is the new value.
1111 * It can be used for changing a private #AdgPoint struct, such as:
1113 * <informalexample><programlisting language="C">
1114 * data->point = adg_entity_point(entity, data->point, new_point);
1115 * </programlisting></informalexample>
1117 * This function takes care of the dependencies between @entity and
1118 * the eventual models bound to the old and new points.
1120 * @old_point can be <constant>NULL</constant>, in which case a
1121 * clone of @new_point will be returned. Also @new_point can
1122 * be <constant>NULL</constant>, in which case @old_point
1123 * is destroyed and <constant>NULL</constant> will be returned.
1125 * Returns: (transfer full): the new properly defined point
1130 adg_entity_point(AdgEntity
*entity
, AdgPoint
*old_point
, const AdgPoint
*new_point
)
1134 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
1138 if (! adg_point_equal(old_point
, new_point
)) {
1139 AdgModel
*old_model
, *new_model
;
1141 old_model
= old_point
!= NULL
? adg_point_get_model(old_point
) : NULL
;
1142 new_model
= new_point
!= NULL
? adg_point_get_model(new_point
) : NULL
;
1144 if (new_model
!= old_model
) {
1145 /* Handle model-entity dependencies */
1146 if (new_model
!= NULL
)
1147 adg_model_add_dependency(new_model
, entity
);
1148 if (old_model
!= NULL
)
1149 adg_model_remove_dependency(old_model
, entity
);
1152 if (new_point
!= NULL
)
1153 point
= adg_point_dup(new_point
);
1154 if (old_point
!= NULL
)
1155 adg_point_destroy(old_point
);
1163 _adg_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
1165 AdgEntityPrivate
*data
;
1166 AdgEntity
*old_parent
;
1168 data
= entity
->data
;
1169 old_parent
= data
->parent
;
1172 g_object_ref(parent
);
1174 data
->parent
= parent
;
1175 data
->global
.is_defined
= FALSE
;
1176 data
->local
.is_defined
= FALSE
;
1178 g_signal_emit(entity
, _adg_signals
[PARENT_SET
], 0, old_parent
);
1181 g_object_unref(old_parent
);
1185 _adg_global_changed(AdgEntity
*entity
)
1187 AdgEntityPrivate
*data
;
1188 const cairo_matrix_t
*map
;
1189 cairo_matrix_t
*matrix
;
1191 data
= entity
->data
;
1192 map
= &data
->global_map
;
1193 matrix
= &data
->global
.matrix
;
1196 adg_matrix_copy(matrix
, adg_entity_get_global_matrix(data
->parent
));
1197 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1199 adg_matrix_copy(matrix
, map
);
1204 _adg_local_changed(AdgEntity
*entity
)
1206 AdgEntityPrivate
*data
;
1207 const cairo_matrix_t
*map
;
1208 cairo_matrix_t
*matrix
;
1210 data
= entity
->data
;
1211 map
= &data
->local_map
;
1212 matrix
= &data
->local
.matrix
;
1214 switch (data
->local_mix
) {
1215 case ADG_MIX_DISABLED
:
1216 adg_matrix_copy(matrix
, adg_matrix_identity());
1219 adg_matrix_copy(matrix
, map
);
1221 case ADG_MIX_ANCESTORS
:
1223 adg_matrix_copy(matrix
, adg_entity_get_local_matrix(data
->parent
));
1224 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1226 adg_matrix_copy(matrix
, map
);
1229 case ADG_MIX_ANCESTORS_NORMALIZED
:
1231 adg_matrix_copy(matrix
, adg_entity_get_local_matrix(data
->parent
));
1232 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1234 adg_matrix_copy(matrix
, map
);
1236 adg_matrix_normalize(matrix
);
1238 case ADG_MIX_PARENT
:
1240 adg_matrix_copy(matrix
, adg_entity_get_local_map(data
->parent
));
1241 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1243 adg_matrix_copy(matrix
, map
);
1246 case ADG_MIX_PARENT_NORMALIZED
:
1248 adg_matrix_copy(matrix
, adg_entity_get_local_map(data
->parent
));
1249 adg_matrix_transform(matrix
, map
, ADG_TRANSFORM_BEFORE
);
1251 adg_matrix_copy(matrix
, map
);
1253 adg_matrix_normalize(matrix
);
1255 case ADG_MIX_UNDEFINED
:
1256 g_warning(_("%s: requested to mix the maps using an undefined mix method"),
1260 g_return_if_reached();
1266 _adg_real_invalidate(AdgEntity
*entity
)
1268 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1269 AdgEntityPrivate
*data
= entity
->data
;
1271 /* Do not raise any warning if invalidate() is not defined,
1272 * assuming entity does not have additional cache to be cleared */
1273 if (klass
->invalidate
)
1274 klass
->invalidate(entity
);
1276 data
->extents
.is_defined
= FALSE
;
1280 _adg_real_arrange(AdgEntity
*entity
)
1282 AdgEntityClass
*klass
;
1283 AdgEntityPrivate
*data
;
1285 klass
= ADG_ENTITY_GET_CLASS(entity
);
1286 data
= entity
->data
;
1288 /* Update the global matrix, if required */
1289 if (!data
->global
.is_defined
) {
1290 data
->global
.is_defined
= TRUE
;
1291 g_signal_emit(entity
, _adg_signals
[GLOBAL_CHANGED
], 0);
1294 /* Update the local matrix, if required */
1295 if (!data
->local
.is_defined
) {
1296 data
->local
.is_defined
= TRUE
;
1297 g_signal_emit(entity
, _adg_signals
[LOCAL_CHANGED
], 0);
1300 /* The arrange() method must be defined */
1301 if (klass
->arrange
== NULL
) {
1302 g_warning(_("%s: `arrange' method not implemented for type `%s'"),
1303 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1304 data
->extents
.is_defined
= FALSE
;
1308 klass
->arrange(entity
);
1312 _adg_real_render(AdgEntity
*entity
, cairo_t
*cr
)
1314 AdgEntityClass
*klass
= ADG_ENTITY_GET_CLASS(entity
);
1316 /* The render method must be defined */
1317 if (klass
->render
== NULL
) {
1318 g_warning(_("%s: `render' method not implemented for type `%s'"),
1319 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
1323 /* Before the rendering, the entity should be arranged */
1324 g_signal_emit(entity
, _adg_signals
[ARRANGE
], 0);
1327 klass
->render(entity
, cr
);
1330 if (_adg_show_extents
) {
1331 AdgEntityPrivate
*data
= entity
->data
;
1332 CpmlExtents
*extents
= &data
->extents
;
1334 if (extents
->is_defined
) {
1336 cairo_set_source_rgba(cr
, 0.15, 0.15, 0.15, 0.15);
1337 cairo_rectangle(cr
, extents
->org
.x
, extents
->org
.y
,
1338 extents
->size
.x
, extents
->size
.y
);