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 * @context_set: called after the context has changed
44 * @invalidate: invalidating callback, used to clear the cache
45 * @render: rendering callback, it must be implemented
47 * Any entity (if not abstract) must implement at least the @render method.
51 #include "adg-entity.h"
52 #include "adg-entity-private.h"
53 #include "adg-canvas.h"
56 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
76 static void dispose (GObject
*object
);
77 static void get_property (GObject
*object
,
81 static void set_property (GObject
*object
,
85 static gboolean
set_parent (AdgEntity
*entity
,
87 static gboolean
set_context (AdgEntity
*entity
,
89 static void set_global_map (AdgEntity
*entity
,
90 const AdgMatrix
*map
);
91 static void set_local_map (AdgEntity
*entity
,
92 const AdgMatrix
*map
);
93 static void real_invalidate (AdgEntity
*entity
,
95 static void real_render (AdgEntity
*entity
,
99 static guint signals
[LAST_SIGNAL
] = { 0 };
102 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
);
106 adg_entity_class_init(AdgEntityClass
*klass
)
108 GObjectClass
*gobject_class
;
111 GType param_types
[1];
113 gobject_class
= (GObjectClass
*) klass
;
115 g_type_class_add_private(klass
, sizeof(AdgEntityPrivate
));
117 gobject_class
->get_property
= get_property
;
118 gobject_class
->set_property
= set_property
;
119 gobject_class
->dispose
= dispose
;
121 klass
->parent_set
= NULL
;
122 klass
->context_set
= NULL
;
123 klass
->invalidate
= NULL
;
124 klass
->render
= NULL
;
126 param
= g_param_spec_object("parent",
128 P_("The parent entity of this entity or NULL if this is a top-level entity"),
129 ADG_TYPE_ENTITY
, G_PARAM_READWRITE
);
130 g_object_class_install_property(gobject_class
, PROP_PARENT
, param
);
132 param
= g_param_spec_object("context",
134 P_("The context associated to this entity or NULL to inherit the parent context"),
135 ADG_TYPE_CONTEXT
, G_PARAM_READWRITE
);
136 g_object_class_install_property(gobject_class
, PROP_CONTEXT
, param
);
138 param
= g_param_spec_boxed("global-map",
140 P_("The transformation to be combined with the parent ones to get the global matrix"),
141 ADG_TYPE_MATRIX
, G_PARAM_READWRITE
);
142 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
144 param
= g_param_spec_boxed("local-map",
146 P_("The transformation to be combined with the parent ones to get the local matrix"),
147 ADG_TYPE_MATRIX
, G_PARAM_READWRITE
);
148 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
151 * AdgEntity::parent-set:
152 * @entity: an #AdgEntity
153 * @old_parent: the old parent
155 * Emitted after the parent entity has changed. The new parent
156 * can be inspected using adg_entity_get_parent().
158 * It is allowed for both old and new parent to be %NULL.
160 signals
[PARENT_SET
] = g_signal_new("parent-set",
161 G_OBJECT_CLASS_TYPE(gobject_class
),
163 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
165 g_cclosure_marshal_VOID__OBJECT
,
166 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
169 * AdgEntity::context-set:
170 * @entity: an #AdgEntity
171 * @old_context: the old context
173 * Emitted after the context has changed.
174 * Emitted after the context has changed. The new context can be
175 * inspected using adg_entity_get_context().
177 * It is allowed for both old and new context to be %NULL.
179 signals
[CONTEXT_SET
] = g_signal_new("context-set",
180 G_OBJECT_CLASS_TYPE(gobject_class
),
182 G_STRUCT_OFFSET(AdgEntityClass
, context_set
),
184 g_cclosure_marshal_VOID__OBJECT
,
185 G_TYPE_NONE
, 1, ADG_TYPE_CONTEXT
);
188 * AdgEntity::invalidate:
189 * @entity: an #AdgEntity
191 * The inverse of the rendering. Usually, invalidation causes the
192 * cache of @entity to be cleared. After a succesful invalidation
193 * the rendered flag is reset: you can access its state using
194 * adg_entity_get_rendered().
196 closure
= g_cclosure_new(G_CALLBACK(real_invalidate
),
197 (gpointer
)0xdeadbeaf, NULL
);
198 signals
[INVALIDATE
] = g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
199 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
200 g_cclosure_marshal_VOID__VOID
,
201 G_TYPE_NONE
, 0, param_types
);
205 * @entity: an #AdgEntity
206 * @cr: a #cairo_t drawing context
208 * Causes the rendering of @entity on @cr. After a succesful rendering
209 * the rendered flag is set: you can access its state using
210 * adg_entity_get_rendered().
212 closure
= g_cclosure_new(G_CALLBACK(real_render
),
213 (gpointer
)0xdeadbeaf, NULL
);
214 param_types
[0] = G_TYPE_POINTER
;
215 signals
[RENDER
] = g_signal_newv("render", ADG_TYPE_ENTITY
,
216 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
217 g_cclosure_marshal_VOID__POINTER
,
218 G_TYPE_NONE
, 1, param_types
);
222 adg_entity_init(AdgEntity
*entity
)
224 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
229 data
->context
= NULL
;
230 cairo_matrix_init_identity(&data
->local_map
);
231 cairo_matrix_init_identity(&data
->global_map
);
237 dispose(GObject
*object
)
242 entity
= (AdgEntity
*) object
;
243 parent
= adg_entity_get_parent(entity
);
245 /* These calls will emit a "notify" signal both for parent and
246 * context if they are not NULL. Consequentially, the references
247 * to the old parent and context are dropped. */
248 adg_entity_set_parent(entity
, NULL
);
249 adg_entity_set_context(entity
, NULL
);
251 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
252 PARENT_OBJECT_CLASS
->dispose(object
);
257 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
260 AdgEntityPrivate
*data
;
262 entity
= (AdgEntity
*) object
;
267 g_value_set_object(value
, data
->parent
);
270 g_value_set_object(value
, adg_entity_get_context(entity
));
272 case PROP_GLOBAL_MAP
:
273 g_value_set_boxed(value
, &data
->global_map
);
276 g_value_set_boxed(value
, &data
->local_map
);
279 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
285 set_property(GObject
*object
,
286 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
288 AdgEntity
*entity
= (AdgEntity
*) object
;
292 set_parent(entity
, g_value_get_object(value
));
295 set_context(entity
, g_value_get_object(value
));
297 case PROP_GLOBAL_MAP
:
298 set_global_map(entity
, g_value_get_boxed(value
));
301 set_local_map(entity
, g_value_get_boxed(value
));
304 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
311 * adg_entity_get_parent:
312 * @entity: an #AdgEntity
314 * Gets the parent of @entity.
316 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
319 adg_entity_get_parent(AdgEntity
*entity
)
321 AdgEntityPrivate
*data
;
323 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
331 * adg_entity_set_parent:
332 * @entity: an #AdgEntity
333 * @parent: the parent entity
335 * Sets a new parent on @entity.
337 * This function is only useful in entity implementations.
340 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
342 g_return_if_fail(ADG_IS_ENTITY(entity
));
344 if (set_parent(entity
, parent
))
345 g_object_notify((GObject
*) entity
, "parent");
349 * adg_entity_context:
350 * @entity: an #AdgEntity instance
352 * Gets the context to be used for @entity. If no context was
353 * explicitely set, it returns the parent context.
355 * Returns: the requested context or %NULL on errors or if @entity
356 * does not have any parent with a context defined
359 adg_entity_context(AdgEntity
*entity
)
363 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
365 context
= adg_entity_get_context(entity
);
367 if (context
== NULL
) {
368 AdgEntity
*parent
= adg_entity_get_parent(entity
);
371 context
= adg_entity_get_context(parent
);
378 * adg_entity_get_context:
379 * @entity: an #AdgEntity instance
381 * Gets the context associated to @entity. This is an accessor method:
382 * if you need to get the context for rendering, use adg_entity_context()
385 * Returns: the context binded to @entity
388 adg_entity_get_context(AdgEntity
*entity
)
390 AdgEntityPrivate
*data
;
392 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
396 return data
->context
;
400 * adg_entity_set_context:
401 * @entity: an #AdgEntity instance
402 * @context: the new context
404 * Sets a new context. The old context (if any) will be unreferenced
405 * while a new reference will be added to @context.
408 adg_entity_set_context(AdgEntity
*entity
, AdgContext
*context
)
410 g_return_if_fail(ADG_IS_ENTITY(entity
));
411 g_return_if_fail(ADG_IS_CONTEXT(context
));
413 if (set_context(entity
, context
))
414 g_object_notify((GObject
*) entity
, "context");
418 * adg_entity_get_canvas:
419 * @entity: an #AdgEntity
421 * Walks on the @entity hierarchy and gets the first parent of @entity that is
422 * of #AdgCanvas derived type.
424 * Returns: the requested canvas or %NULL on errors or if there is
425 * 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
= (AdgEntity
*) adg_entity_get_parent(entity
);
443 * adg_entity_get_rendered:
444 * @entity: an #AdgEntity object
446 * This function is only useful in entity implementations.
447 * Gets the rendered flag of @entity.
449 * Returns: the current rendered state
452 adg_entity_get_rendered(AdgEntity
*entity
)
454 AdgEntityPrivate
*data
;
456 g_return_val_if_fail(ADG_IS_ENTITY(entity
), FALSE
);
460 return ADG_ISSET(data
->flags
, RENDERED
);
464 * adg_entity_set_rendered:
465 * @entity: an #AdgEntity object
466 * @rendered: new state for the rendered flag
468 * This function is only useful in entity implementations.
469 * Sets the rendered flag of @entity to @rendered.
472 adg_entity_set_rendered(AdgEntity
*entity
, gboolean rendered
)
474 AdgEntityPrivate
*data
;
476 g_return_if_fail(ADG_IS_ENTITY(entity
));
481 ADG_SET(data
->flags
, RENDERED
);
483 ADG_UNSET(data
->flags
, RENDERED
);
487 * adg_entity_get_global_map:
488 * @entity: an #AdgEntity object
489 * @map: where to store the global map
491 * Gets the transformation to be used to compute the global matrix
492 * of @entity and store it in @map.
495 adg_entity_get_global_map(AdgEntity
*entity
, AdgMatrix
*map
)
497 AdgEntityPrivate
*data
;
499 g_return_if_fail(ADG_IS_ENTITY(entity
));
500 g_return_if_fail(map
!= NULL
);
503 adg_matrix_copy(map
, &data
->global_map
);
507 * adg_entity_set_global_map:
508 * @entity: an #AdgEntity object
511 * Sets the new global transformation of @entity to @map:
512 * the old map is discarded. If @map is %NULL an identity
516 adg_entity_set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
518 g_return_if_fail(ADG_IS_ENTITY(entity
));
520 set_global_map(entity
, map
);
521 g_object_notify((GObject
*) entity
, "global-map");
525 * adg_entity_get_local_map:
526 * @entity: an #AdgEntity object
527 * @map: where to store the local map
529 * Gets the transformation to be used to compute the local matrix
530 * of @entity and store it in @map.
533 adg_entity_get_local_map(AdgEntity
*entity
, AdgMatrix
*map
)
535 AdgEntityPrivate
*data
;
537 g_return_if_fail(ADG_IS_ENTITY(entity
));
538 g_return_if_fail(map
!= NULL
);
541 adg_matrix_copy(map
, &data
->local_map
);
545 * adg_entity_set_local_map:
546 * @entity: an #AdgEntity object
549 * Sets the new global transformation of @entity to @map:
550 * the old map is discarded. If @map is %NULL an identity
554 adg_entity_set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
556 g_return_if_fail(ADG_IS_ENTITY(entity
));
558 set_local_map(entity
, map
);
559 g_object_notify((GObject
*) entity
, "local-map");
563 * adg_entity_get_global_matrix:
564 * @entity: an #AdgEntity object
565 * @matrix: where to store the global matrix
567 * Computes the global matrix by combining all the global maps of the
568 * @entity hierarchy and stores the result in @matrix.
571 adg_entity_get_global_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
573 AdgEntityPrivate
*data
;
575 g_return_if_fail(ADG_IS_ENTITY(entity
));
576 g_return_if_fail(matrix
!= NULL
);
580 if (data
->parent
== NULL
) {
581 adg_matrix_copy(matrix
, &data
->global_map
);
583 adg_entity_get_global_matrix((AdgEntity
*) data
->parent
, matrix
);
584 cairo_matrix_multiply(matrix
, &data
->global_map
, matrix
);
589 * adg_entity_get_local_matrix:
590 * @entity: an #AdgEntity object
591 * @matrix: where to store the local matrix
593 * Computes the local matrix by combining all the local maps of the
594 * @entity hierarchy and stores the result in @matrix.
597 adg_entity_get_local_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
599 AdgEntityPrivate
*data
;
601 g_return_if_fail(ADG_IS_ENTITY(entity
));
602 g_return_if_fail(matrix
!= NULL
);
606 if (data
->parent
== NULL
) {
607 adg_matrix_copy(matrix
, &data
->local_map
);
609 adg_entity_get_local_matrix((AdgEntity
*) data
->parent
, matrix
);
610 cairo_matrix_multiply(matrix
, &data
->local_map
, matrix
);
615 * adg_entity_get_style:
616 * @entity: an #AdgEntity
617 * @style_slot: the slot of the style to get
619 * Gets a style from this entity. If the entity has no context associated
620 * or the style is undefined within this context, gets the style from its
623 * Returns: the requested style or %NULL on errors or if there is no
624 * context with this style defined in the @entity hierarchy
627 adg_entity_get_style(AdgEntity
*entity
, AdgStyleSlot style_slot
)
629 AdgEntityPrivate
*data
;
631 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
636 AdgStyle
*style
= adg_context_get_style(data
->context
, style_slot
);
642 return adg_entity_get_style((AdgEntity
*) data
->parent
, style_slot
);
649 * @entity: an #AdgEntity
650 * @style_slot: the slot of the style to apply
651 * @cr: a #cairo_t drawing context
653 * Applies the specified style to the @cr cairo context.
656 adg_entity_apply(AdgEntity
*entity
, AdgStyleSlot style_slot
, cairo_t
*cr
)
658 AdgStyle
*style
= adg_entity_get_style(entity
, style_slot
);
661 adg_style_apply(style
, cr
);
665 * adg_entity_invalidate:
666 * @entity: an #AdgEntity
668 * Emits the "invalidate" signal on @entity and all its children, if any,
669 * so subsequent rendering will need a global recomputation.
672 adg_entity_invalidate(AdgEntity
*entity
)
674 g_return_if_fail(ADG_IS_ENTITY(entity
));
676 g_signal_emit(entity
, signals
[INVALIDATE
], 0, NULL
);
681 * @entity: an #AdgEntity
682 * @cr: a #cairo_t drawing context
684 * Emits the "render" signal on @entity and all its children, if any,
685 * causing the rendering operation the @cr cairo context.
688 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
690 g_return_if_fail(ADG_IS_ENTITY(entity
));
692 g_signal_emit(entity
, signals
[RENDER
], 0, cr
);
697 set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
699 AdgEntityPrivate
*data
;
700 AdgEntity
*old_parent
;
703 old_parent
= data
->parent
;
705 /* Check if parent has changed */
706 if (parent
== old_parent
)
710 g_object_ref(parent
);
712 data
->parent
= parent
;
713 g_signal_emit(entity
, signals
[PARENT_SET
], 0, old_parent
);
715 if (old_parent
!= NULL
)
716 g_object_unref(old_parent
);
722 set_context(AdgEntity
*entity
, AdgContext
*context
)
724 AdgEntityPrivate
*data
;
725 AdgContext
*old_context
;
728 old_context
= data
->context
;
730 /* Check if context has changed */
731 if (context
== old_context
)
735 g_object_ref(context
);
737 data
->context
= context
;
738 g_signal_emit(entity
, signals
[CONTEXT_SET
], 0, old_context
);
740 if (old_context
!= NULL
)
741 g_object_unref(old_context
);
747 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
749 AdgEntityPrivate
*data
= entity
->data
;
752 cairo_matrix_init_identity(&data
->global_map
);
754 adg_matrix_copy(&data
->global_map
, map
);
758 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
760 AdgEntityPrivate
*data
= entity
->data
;
763 cairo_matrix_init_identity(&data
->local_map
);
765 adg_matrix_copy(&data
->local_map
, map
);
769 real_invalidate(AdgEntity
*entity
, gpointer user_data
)
771 AdgEntityClass
*entity_class
;
772 AdgEntityPrivate
*data
;
774 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
776 entity_class
= ADG_ENTITY_GET_CLASS(entity
);
777 if (entity_class
->invalidate
== NULL
) {
778 /* Assume the entity does not have cache to be cleared */
779 } else if (!entity_class
->invalidate(entity
)) {
784 ADG_UNSET(data
->flags
, RENDERED
);
788 real_render(AdgEntity
*entity
, cairo_t
*cr
, gpointer user_data
)
790 AdgEntityClass
*entity_class
;
791 AdgEntityPrivate
*data
;
793 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
795 entity_class
= ADG_ENTITY_GET_CLASS(entity
);
796 if (entity_class
->render
== NULL
) {
797 /* The render method must be defined */
798 g_warning("%s: `render' method not implemented for type `%s'",
799 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
806 cairo_transform(cr
, &data
->global_map
);
808 if (entity_class
->render(entity
, cr
))
809 ADG_SET(data
->flags
, RENDERED
);