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 the render() virtual method. Also, if you are using
29 * some sort of caching, you must implement invalidate() to clear
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 * @render: rendering callback, it must be implemented
46 * Any entity (if not abstract) must implement at least the @render method.
50 #include "adg-entity.h"
51 #include "adg-entity-private.h"
52 #include "adg-canvas.h"
53 #include "adg-font-style.h"
54 #include "adg-dim-style.h"
57 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
75 static void dispose (GObject
*object
);
76 static void get_property (GObject
*object
,
80 static void set_property (GObject
*object
,
84 static gboolean
set_parent (AdgEntity
*entity
,
86 static void set_global_map (AdgEntity
*entity
,
87 const AdgMatrix
*map
);
88 static void set_local_map (AdgEntity
*entity
,
89 const AdgMatrix
*map
);
90 static void get_global_matrix (AdgEntity
*entity
,
92 static void get_local_matrix (AdgEntity
*entity
,
94 static void real_invalidate (AdgEntity
*entity
,
96 static void real_render (AdgEntity
*entity
,
100 static guint signals
[LAST_SIGNAL
] = { 0 };
103 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
);
107 adg_entity_class_init(AdgEntityClass
*klass
)
109 GObjectClass
*gobject_class
;
112 GType param_types
[1];
114 gobject_class
= (GObjectClass
*) klass
;
116 g_type_class_add_private(klass
, sizeof(AdgEntityPrivate
));
118 gobject_class
->dispose
= dispose
;
119 gobject_class
->get_property
= get_property
;
120 gobject_class
->set_property
= set_property
;
122 klass
->parent_set
= NULL
;
123 klass
->get_global_matrix
= get_global_matrix
;
124 klass
->get_local_matrix
= get_local_matrix
;
125 klass
->invalidate
= NULL
;
126 klass
->render
= NULL
;
128 param
= g_param_spec_object("parent",
130 P_("The parent entity of this entity or NULL if this is a top-level entity"),
131 ADG_TYPE_ENTITY
, G_PARAM_READWRITE
);
132 g_object_class_install_property(gobject_class
, PROP_PARENT
, param
);
134 param
= g_param_spec_boxed("global-map",
136 P_("The transformation to be combined with the parent ones to get the global matrix"),
137 ADG_TYPE_MATRIX
, G_PARAM_READWRITE
);
138 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
140 param
= g_param_spec_boxed("local-map",
142 P_("The transformation to be combined with the parent ones to get the local matrix"),
143 ADG_TYPE_MATRIX
, G_PARAM_READWRITE
);
144 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
147 * AdgEntity::parent-set:
148 * @entity: an #AdgEntity
149 * @old_parent: the old parent
151 * Emitted after the parent entity has changed. The new parent
152 * can be inspected using adg_entity_get_parent().
154 * It is allowed for both old and new parent to be %NULL.
156 signals
[PARENT_SET
] = g_signal_new("parent-set",
157 G_OBJECT_CLASS_TYPE(gobject_class
),
159 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
161 g_cclosure_marshal_VOID__OBJECT
,
162 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
165 * AdgEntity::invalidate:
166 * @entity: an #AdgEntity
168 * The inverse of the rendering. Usually, invalidation causes the
169 * cache of @entity to be cleared. After a succesful invalidation
170 * the rendered flag is reset: you can access its state using
171 * adg_entity_get_rendered().
173 closure
= g_cclosure_new(G_CALLBACK(real_invalidate
),
174 (gpointer
)0xdeadbeaf, NULL
);
175 signals
[INVALIDATE
] = g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
176 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
177 g_cclosure_marshal_VOID__VOID
,
178 G_TYPE_NONE
, 0, param_types
);
182 * @entity: an #AdgEntity
183 * @cr: a #cairo_t drawing context
185 * Causes the rendering of @entity on @cr. After a succesful rendering
186 * the rendered flag is set: you can access its state using
187 * adg_entity_get_rendered().
189 closure
= g_cclosure_new(G_CALLBACK(real_render
),
190 (gpointer
)0xdeadbeaf, NULL
);
191 param_types
[0] = G_TYPE_POINTER
;
192 signals
[RENDER
] = g_signal_newv("render", ADG_TYPE_ENTITY
,
193 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
194 g_cclosure_marshal_VOID__POINTER
,
195 G_TYPE_NONE
, 1, param_types
);
199 adg_entity_init(AdgEntity
*entity
)
201 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
206 cairo_matrix_init_identity(&data
->local_map
);
207 cairo_matrix_init_identity(&data
->global_map
);
208 data
->hash_styles
= NULL
;
214 dispose(GObject
*object
)
217 AdgEntityPrivate
*data
;
219 entity
= (AdgEntity
*) object
;
222 /* This call will emit a "notify" signal for parent.
223 * Consequentially, the references to the old parent is dropped. */
224 adg_entity_set_parent(entity
, NULL
);
226 if (data
->hash_styles
!= NULL
) {
227 g_hash_table_destroy(data
->hash_styles
);
228 data
->hash_styles
= NULL
;
231 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
232 PARENT_OBJECT_CLASS
->dispose(object
);
237 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
240 AdgEntityPrivate
*data
;
242 entity
= (AdgEntity
*) object
;
247 g_value_set_object(value
, data
->parent
);
249 case PROP_GLOBAL_MAP
:
250 g_value_set_boxed(value
, &data
->global_map
);
253 g_value_set_boxed(value
, &data
->local_map
);
256 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
262 set_property(GObject
*object
,
263 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
265 AdgEntity
*entity
= (AdgEntity
*) object
;
269 set_parent(entity
, g_value_get_object(value
));
271 case PROP_GLOBAL_MAP
:
272 set_global_map(entity
, g_value_get_boxed(value
));
275 set_local_map(entity
, g_value_get_boxed(value
));
278 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
285 * adg_entity_get_parent:
286 * @entity: an #AdgEntity
288 * Gets the parent of @entity.
290 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
293 adg_entity_get_parent(AdgEntity
*entity
)
295 AdgEntityPrivate
*data
;
297 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
305 * adg_entity_set_parent:
306 * @entity: an #AdgEntity
307 * @parent: the parent entity
310 * This function is only useful in entity implementations.
313 * Sets a new parent on @entity.
316 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
318 g_return_if_fail(ADG_IS_ENTITY(entity
));
320 if (set_parent(entity
, parent
))
321 g_object_notify((GObject
*) entity
, "parent");
325 * adg_entity_get_canvas:
326 * @entity: an #AdgEntity
328 * Walks on the @entity hierarchy and gets the first parent of @entity that is
329 * of #AdgCanvas derived type.
331 * Returns: the requested canvas or %NULL on errors or if there is
332 * no #AdgCanvas in the @entity hierarchy
335 adg_entity_get_canvas(AdgEntity
*entity
)
337 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
340 if (ADG_IS_CANVAS(entity
))
341 return (AdgCanvas
*) entity
;
343 entity
= (AdgEntity
*) adg_entity_get_parent(entity
);
350 * adg_entity_get_rendered:
351 * @entity: an #AdgEntity object
354 * This function is only useful in entity implementations.
357 * Gets the rendered flag of @entity.
359 * Returns: the current rendered state
362 adg_entity_get_rendered(AdgEntity
*entity
)
364 AdgEntityPrivate
*data
;
366 g_return_val_if_fail(ADG_IS_ENTITY(entity
), FALSE
);
370 return ADG_ISSET(data
->flags
, RENDERED
);
374 * adg_entity_set_rendered:
375 * @entity: an #AdgEntity object
376 * @rendered: new state for the rendered flag
379 * This function is only useful in entity implementations.
382 * Sets the rendered flag of @entity to @rendered.
385 adg_entity_set_rendered(AdgEntity
*entity
, gboolean rendered
)
387 AdgEntityPrivate
*data
;
389 g_return_if_fail(ADG_IS_ENTITY(entity
));
394 ADG_SET(data
->flags
, RENDERED
);
396 ADG_UNSET(data
->flags
, RENDERED
);
400 * adg_entity_get_global_map:
401 * @entity: an #AdgEntity object
402 * @map: where to store the global map
404 * Gets the transformation to be used to compute the global matrix
405 * of @entity and store it in @map.
408 adg_entity_get_global_map(AdgEntity
*entity
, AdgMatrix
*map
)
410 AdgEntityPrivate
*data
;
412 g_return_if_fail(ADG_IS_ENTITY(entity
));
413 g_return_if_fail(map
!= NULL
);
416 adg_matrix_copy(map
, &data
->global_map
);
420 * adg_entity_set_global_map:
421 * @entity: an #AdgEntity object
424 * Sets the new global transformation of @entity to @map:
425 * the old map is discarded. If @map is %NULL an identity
429 adg_entity_set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
431 g_return_if_fail(ADG_IS_ENTITY(entity
));
433 set_global_map(entity
, map
);
434 g_object_notify((GObject
*) entity
, "global-map");
438 * adg_entity_transform_global_map:
439 * @entity: an #AdgEntity object
440 * @transformation: the transformation to apply
442 * Applies a transformation to the global map of @entity. This is
443 * equivalent to the following code:
447 * adg_entity_get_global_map(entity, &tmp_map);
448 * cairo_matrix_multiply(&tmp_map, transformation, &tmp_map);
449 * adg_entity_set_global_map(entity, &tmp_map);
453 adg_entity_transform_global_map(AdgEntity
*entity
,
454 const AdgMatrix
*transformation
)
456 AdgEntityPrivate
*data
;
458 g_return_if_fail(ADG_IS_ENTITY(entity
));
459 g_return_if_fail(transformation
!= NULL
);
463 cairo_matrix_multiply(&data
->global_map
, transformation
, &data
->global_map
);
464 g_object_notify((GObject
*) entity
, "global-map");
468 * adg_entity_get_global_matrix:
469 * @entity: an #AdgEntity object
470 * @matrix: where to store the global matrix
472 * Computes the global matrix by combining all the global maps of the
473 * @entity hierarchy and stores the result in @matrix.
476 adg_entity_get_global_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
478 g_return_if_fail(ADG_IS_ENTITY(entity
));
479 g_return_if_fail(matrix
!= NULL
);
481 ADG_ENTITY_GET_CLASS(entity
)->get_global_matrix(entity
, matrix
);
485 * adg_entity_get_local_map:
486 * @entity: an #AdgEntity object
487 * @map: where to store the local map
489 * Gets the transformation to be used to compute the local matrix
490 * of @entity and store it in @map.
493 adg_entity_get_local_map(AdgEntity
*entity
, AdgMatrix
*map
)
495 AdgEntityPrivate
*data
;
497 g_return_if_fail(ADG_IS_ENTITY(entity
));
498 g_return_if_fail(map
!= NULL
);
501 adg_matrix_copy(map
, &data
->local_map
);
505 * adg_entity_set_local_map:
506 * @entity: an #AdgEntity object
509 * Sets the new global transformation of @entity to @map:
510 * the old map is discarded. If @map is %NULL an identity
514 adg_entity_set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
516 g_return_if_fail(ADG_IS_ENTITY(entity
));
518 set_local_map(entity
, map
);
519 g_object_notify((GObject
*) entity
, "local-map");
523 * adg_entity_transform_local_map:
524 * @entity: an #AdgEntity object
525 * @transformation: the transformation to apply
527 * Applies a transformation to the local map of @entity. This is
528 * equivalent to the following code:
532 * adg_entity_get_local_map(entity, &tmp_map);
533 * cairo_matrix_multiply(&tmp_map, transformation, &tmp_map);
534 * adg_entity_set_local_map(entity, &tmp_map);
538 adg_entity_transform_local_map(AdgEntity
*entity
,
539 const AdgMatrix
*transformation
)
541 AdgEntityPrivate
*data
;
543 g_return_if_fail(ADG_IS_ENTITY(entity
));
544 g_return_if_fail(transformation
!= NULL
);
548 cairo_matrix_multiply(&data
->local_map
, transformation
, &data
->local_map
);
549 g_object_notify((GObject
*) entity
, "local-map");
553 * adg_entity_get_local_matrix:
554 * @entity: an #AdgEntity object
555 * @matrix: where to store the local matrix
557 * Computes the local matrix by combining all the local maps of the
558 * @entity hierarchy and stores the result in @matrix.
561 adg_entity_get_local_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
563 g_return_if_fail(ADG_IS_ENTITY(entity
));
564 g_return_if_fail(matrix
!= NULL
);
566 ADG_ENTITY_GET_CLASS(entity
)->get_local_matrix(entity
, matrix
);
570 * adg_entity_apply_local_matrix:
571 * @entity: an #AdgEntity object
572 * @cr: a #cairo_t drawing context
574 * Applies the local matrix of @entity BEFORE the current transformation
575 * matrix of @cr. This is a convenience function equivalent to:
578 * AdgMatrix local, ctm;
580 * adg_entity_get_local_matrix(entity, &local);
581 * cairo_get_matrix(cr, &ctm);
583 * cairo_matrix_multiply(&ctm, &ctm, &local);
584 * cairo_set_matrix(cr, &ctm);
588 adg_entity_apply_local_matrix(AdgEntity
*entity
, cairo_t
*cr
)
590 AdgMatrix local
, ctm
;
592 g_return_if_fail(ADG_IS_ENTITY(entity
));
593 g_return_if_fail(cr
!= NULL
);
595 adg_entity_get_local_matrix(entity
, &local
);
596 cairo_get_matrix(cr
, &ctm
);
598 cairo_matrix_multiply(&ctm
, &ctm
, &local
);
599 cairo_set_matrix(cr
, &ctm
);
604 * @entity: an #AdgEntity
605 * @dress: the dress of the style to get
607 * Gets the style to be used for @entity. @dress specifies the "type"
610 * This method is supposed to always return a valid style instance,
611 * specifically the most appropriate one for this entity. The following
612 * sequence of checks is performed to get the proper @dress style,
613 * stopping at the first succesfull result:
616 * <listitem>check if the style is defined directly by this entity type,
617 * as returned by adg_entity_get_style();</listitem>
618 * <listitem>check if @entity has a parent, in which case returns the
619 * adg_entity_style() of the parent;</listitem>
620 * <listitem>returns the main style with adg_dress_get_style().</listitem>
623 * Returns: the requested style or %NULL on errors
626 adg_entity_style(AdgEntity
*entity
, AdgDress dress
)
630 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
631 g_return_val_if_fail(dress
!= ADG_DRESS_UNDEFINED
, NULL
);
633 style
= adg_entity_get_style(entity
, dress
);
636 AdgEntityPrivate
*data
= entity
->data
;
638 if (data
->parent
!= NULL
)
639 style
= adg_entity_style(data
->parent
, dress
);
641 style
= adg_dress_get_style(dress
);
648 * adg_entity_get_style:
649 * @entity: an #AdgEntity
650 * @dress: the dress of the style to get
652 * Gets the overriden @dress style from @entity. This is a kind
653 * of accessor function: to get the style to be used for rendering
654 * purpose, consider adg_entity_style().
656 * Returns: the requested style or %NULL if the @dress style
660 adg_entity_get_style(AdgEntity
*entity
, AdgDress dress
)
662 AdgEntityPrivate
*data
;
664 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
665 g_return_val_if_fail(dress
!= ADG_DRESS_UNDEFINED
, NULL
);
669 if (data
->hash_styles
== NULL
)
672 return g_hash_table_lookup(data
->hash_styles
, GINT_TO_POINTER(dress
));
676 * adg_entity_set_style:
677 * @entity: an #AdgEntity
678 * @dress: a dress style
679 * @style: the new style to use
681 * Overrides the style of @dress for @entity and its children.
682 * If @style is %NULL, any previous override is removed.
685 adg_entity_set_style(AdgEntity
*entity
, AdgDress dress
, AdgStyle
*style
)
687 AdgEntityPrivate
*data
;
691 g_return_if_fail(ADG_IS_ENTITY(entity
));
695 if (data
->hash_styles
== NULL
&& style
== NULL
)
698 if (data
->hash_styles
== NULL
)
699 data
->hash_styles
= g_hash_table_new_full(NULL
, NULL
,
700 NULL
, g_object_unref
);
702 p_dress
= GINT_TO_POINTER(dress
);
703 old_style
= g_hash_table_lookup(data
->hash_styles
, p_dress
);
705 if (style
== old_style
)
709 g_hash_table_remove(data
->hash_styles
, p_dress
);
712 g_hash_table_replace(data
->hash_styles
, p_dress
, style
);
717 * adg_entity_apply_dress:
718 * @entity: an #AdgEntity
719 * @dress: the dress style to apply
720 * @cr: a #cairo_t drawing context
722 * Convenient function to apply a @dress style (as returned by
723 * adg_entity_style()) to the @cr cairo context.
726 adg_entity_apply_dress(AdgEntity
*entity
, AdgDress dress
, cairo_t
*cr
)
728 g_return_if_fail(ADG_IS_ENTITY(entity
));
729 g_return_if_fail(cr
!= NULL
);
731 adg_style_apply(adg_entity_style(entity
, dress
), cr
);
735 * adg_entity_invalidate:
736 * @entity: an #AdgEntity
738 * Emits the "invalidate" signal on @entity and all its children, if any,
739 * so subsequent rendering will need a global recomputation.
742 adg_entity_invalidate(AdgEntity
*entity
)
744 g_return_if_fail(ADG_IS_ENTITY(entity
));
746 g_signal_emit(entity
, signals
[INVALIDATE
], 0, NULL
);
751 * @entity: an #AdgEntity
752 * @cr: a #cairo_t drawing context
754 * Emits the "render" signal on @entity and all its children, if any,
755 * causing the rendering operation the @cr cairo context.
758 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
760 g_return_if_fail(ADG_IS_ENTITY(entity
));
762 g_signal_emit(entity
, signals
[RENDER
], 0, cr
);
767 set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
769 AdgEntityPrivate
*data
;
770 AdgEntity
*old_parent
;
773 old_parent
= data
->parent
;
775 /* Check if parent has changed */
776 if (parent
== old_parent
)
780 g_object_ref(parent
);
782 data
->parent
= parent
;
783 g_signal_emit(entity
, signals
[PARENT_SET
], 0, old_parent
);
785 if (old_parent
!= NULL
)
786 g_object_unref(old_parent
);
792 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
794 AdgEntityPrivate
*data
= entity
->data
;
797 cairo_matrix_init_identity(&data
->global_map
);
799 adg_matrix_copy(&data
->global_map
, map
);
803 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
805 AdgEntityPrivate
*data
= entity
->data
;
808 cairo_matrix_init_identity(&data
->local_map
);
810 adg_matrix_copy(&data
->local_map
, map
);
814 get_global_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
816 AdgEntityPrivate
*data
= entity
->data
;
818 if (data
->parent
== NULL
) {
819 adg_matrix_copy(matrix
, &data
->global_map
);
821 adg_entity_get_global_matrix(data
->parent
, matrix
);
822 cairo_matrix_multiply(matrix
, &data
->global_map
, matrix
);
827 get_local_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
829 AdgEntityPrivate
*data
= entity
->data
;
831 if (data
->parent
== NULL
) {
832 adg_matrix_copy(matrix
, &data
->local_map
);
834 adg_entity_get_local_matrix(data
->parent
, matrix
);
835 cairo_matrix_multiply(matrix
, &data
->local_map
, matrix
);
840 real_invalidate(AdgEntity
*entity
, gpointer user_data
)
842 AdgEntityClass
*klass
;
843 AdgEntityPrivate
*data
;
845 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
847 klass
= ADG_ENTITY_GET_CLASS(entity
);
848 if (klass
->invalidate
== NULL
) {
849 /* Assume the entity does not have cache to be cleared */
850 } else if (!klass
->invalidate(entity
)) {
855 ADG_UNSET(data
->flags
, RENDERED
);
859 real_render(AdgEntity
*entity
, cairo_t
*cr
, gpointer user_data
)
861 AdgEntityClass
*klass
;
862 AdgEntityPrivate
*data
;
864 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
866 klass
= ADG_ENTITY_GET_CLASS(entity
);
867 if (klass
->render
== NULL
) {
868 /* The render method must be defined */
869 g_warning("%s: `render' method not implemented for type `%s'",
870 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
877 cairo_transform(cr
, &data
->global_map
);
879 if (klass
->render(entity
, cr
))
880 ADG_SET(data
->flags
, RENDERED
);