[AdgEntity] Changed "parent" type to AdgEntity
[adg.git] / adg / adg-entity.c
blobfbf0f8bedc1a61bc085a74fd0653e149cb665796
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.
21 /**
22 * SECTION:adg-entity
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
30 * this cache.
31 **/
33 /**
34 * AdgEntity:
36 * All fields are private and should not be used directly.
37 * Use its public methods instead.
38 **/
40 /**
41 * AdgEntityClass:
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.
48 **/
51 #include "adg-entity.h"
52 #include "adg-entity-private.h"
53 #include "adg-canvas.h"
54 #include "adg-intl.h"
56 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
59 enum {
60 PROP_0,
61 PROP_PARENT,
62 PROP_CONTEXT,
63 PROP_GLOBAL_MAP,
64 PROP_LOCAL_MAP
67 enum {
68 PARENT_SET,
69 CONTEXT_SET,
70 INVALIDATE,
71 RENDER,
72 LAST_SIGNAL
76 static void dispose (GObject *object);
77 static void get_property (GObject *object,
78 guint prop_id,
79 GValue *value,
80 GParamSpec *pspec);
81 static void set_property (GObject *object,
82 guint prop_id,
83 const GValue *value,
84 GParamSpec *pspec);
85 static gboolean set_parent (AdgEntity *entity,
86 AdgEntity *parent);
87 static gboolean set_context (AdgEntity *entity,
88 AdgContext *context);
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,
94 gpointer user_data);
95 static void real_render (AdgEntity *entity,
96 cairo_t *cr,
97 gpointer user_data);
99 static guint signals[LAST_SIGNAL] = { 0 };
102 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
105 static void
106 adg_entity_class_init(AdgEntityClass *klass)
108 GObjectClass *gobject_class;
109 GParamSpec *param;
110 GClosure *closure;
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",
127 P_("Parent Entity"),
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",
133 P_("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",
139 P_("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",
145 P_("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),
162 G_SIGNAL_RUN_FIRST,
163 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
164 NULL, NULL,
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),
181 G_SIGNAL_RUN_FIRST,
182 G_STRUCT_OFFSET(AdgEntityClass, context_set),
183 NULL, NULL,
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);
204 * AdgEntity::render:
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);
221 static void
222 adg_entity_init(AdgEntity *entity)
224 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
225 ADG_TYPE_ENTITY,
226 AdgEntityPrivate);
227 data->parent = NULL;
228 data->flags = 0;
229 data->context = NULL;
230 cairo_matrix_init_identity(&data->local_map);
231 cairo_matrix_init_identity(&data->global_map);
233 entity->data = data;
236 static void
237 dispose(GObject *object)
239 AdgEntity *entity;
240 AdgEntity *parent;
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);
256 static void
257 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
259 AdgEntity *entity;
260 AdgEntityPrivate *data;
262 entity = (AdgEntity *) object;
263 data = entity->data;
265 switch (prop_id) {
266 case PROP_PARENT:
267 g_value_set_object(value, data->parent);
268 break;
269 case PROP_CONTEXT:
270 g_value_set_object(value, adg_entity_get_context(entity));
271 break;
272 case PROP_GLOBAL_MAP:
273 g_value_set_boxed(value, &data->global_map);
274 break;
275 case PROP_LOCAL_MAP:
276 g_value_set_boxed(value, &data->local_map);
277 break;
278 default:
279 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
280 break;
284 static void
285 set_property(GObject *object,
286 guint prop_id, const GValue *value, GParamSpec *pspec)
288 AdgEntity *entity = (AdgEntity *) object;
290 switch (prop_id) {
291 case PROP_PARENT:
292 set_parent(entity, g_value_get_object(value));
293 break;
294 case PROP_CONTEXT:
295 set_context(entity, g_value_get_object(value));
296 break;
297 case PROP_GLOBAL_MAP:
298 set_global_map(entity, g_value_get_boxed(value));
299 break;
300 case PROP_LOCAL_MAP:
301 set_local_map(entity, g_value_get_boxed(value));
302 break;
303 default:
304 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
305 break;
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
318 AdgEntity *
319 adg_entity_get_parent(AdgEntity *entity)
321 AdgEntityPrivate *data;
323 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
325 data = entity->data;
327 return data->parent;
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.
339 void
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
358 AdgContext *
359 adg_entity_context(AdgEntity *entity)
361 AdgContext *context;
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);
370 if (parent != NULL)
371 context = adg_entity_get_context(parent);
374 return context;
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()
383 * instead.
385 * Returns: the context binded to @entity
387 AdgContext *
388 adg_entity_get_context(AdgEntity *entity)
390 AdgEntityPrivate *data;
392 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
394 data = entity->data;
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.
407 void
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
427 AdgCanvas *
428 adg_entity_get_canvas(AdgEntity *entity)
430 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
432 while (entity) {
433 if (ADG_IS_CANVAS(entity))
434 return (AdgCanvas *) entity;
436 entity = (AdgEntity *) adg_entity_get_parent(entity);
439 return NULL;
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
451 gboolean
452 adg_entity_get_rendered(AdgEntity *entity)
454 AdgEntityPrivate *data;
456 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
458 data = entity->data;
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.
471 void
472 adg_entity_set_rendered(AdgEntity *entity, gboolean rendered)
474 AdgEntityPrivate *data;
476 g_return_if_fail(ADG_IS_ENTITY(entity));
478 data = entity->data;
480 if (rendered)
481 ADG_SET(data->flags, RENDERED);
482 else
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.
494 void
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);
502 data = entity->data;
503 adg_matrix_copy(map, &data->global_map);
507 * adg_entity_set_global_map:
508 * @entity: an #AdgEntity object
509 * @map: the new map
511 * Sets the new global transformation of @entity to @map:
512 * the old map is discarded. If @map is %NULL an identity
513 * matrix is implied.
515 void
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.
532 void
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);
540 data = entity->data;
541 adg_matrix_copy(map, &data->local_map);
545 * adg_entity_set_local_map:
546 * @entity: an #AdgEntity object
547 * @map: the new map
549 * Sets the new global transformation of @entity to @map:
550 * the old map is discarded. If @map is %NULL an identity
551 * matrix is implied.
553 void
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.
570 void
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);
578 data = entity->data;
580 if (data->parent == NULL) {
581 adg_matrix_copy(matrix, &data->global_map);
582 } else {
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.
596 void
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);
604 data = entity->data;
606 if (data->parent == NULL) {
607 adg_matrix_copy(matrix, &data->local_map);
608 } else {
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
621 * parent entity.
623 * Returns: the requested style or %NULL on errors or if there is no
624 * context with this style defined in the @entity hierarchy
626 AdgStyle *
627 adg_entity_get_style(AdgEntity *entity, AdgStyleSlot style_slot)
629 AdgEntityPrivate *data;
631 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
633 data = entity->data;
635 if (data->context) {
636 AdgStyle *style = adg_context_get_style(data->context, style_slot);
637 if (style)
638 return style;
641 if (data->parent)
642 return adg_entity_get_style((AdgEntity *) data->parent, style_slot);
644 return NULL;
648 * adg_entity_apply:
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.
655 void
656 adg_entity_apply(AdgEntity *entity, AdgStyleSlot style_slot, cairo_t *cr)
658 AdgStyle *style = adg_entity_get_style(entity, style_slot);
660 if (style)
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.
671 void
672 adg_entity_invalidate(AdgEntity *entity)
674 g_return_if_fail(ADG_IS_ENTITY(entity));
676 g_signal_emit(entity, signals[INVALIDATE], 0, NULL);
680 * adg_entity_render:
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.
687 void
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);
696 static gboolean
697 set_parent(AdgEntity *entity, AdgEntity *parent)
699 AdgEntityPrivate *data;
700 AdgEntity *old_parent;
702 data = entity->data;
703 old_parent = data->parent;
705 /* Check if parent has changed */
706 if (parent == old_parent)
707 return FALSE;
709 if (parent != NULL)
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);
718 return TRUE;
721 static gboolean
722 set_context(AdgEntity *entity, AdgContext *context)
724 AdgEntityPrivate *data;
725 AdgContext *old_context;
727 data = entity->data;
728 old_context = data->context;
730 /* Check if context has changed */
731 if (context == old_context)
732 return FALSE;
734 if (context != NULL)
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);
743 return TRUE;
746 static void
747 set_global_map(AdgEntity *entity, const AdgMatrix *map)
749 AdgEntityPrivate *data = entity->data;
751 if (map == NULL)
752 cairo_matrix_init_identity(&data->global_map);
753 else
754 adg_matrix_copy(&data->global_map, map);
757 static void
758 set_local_map(AdgEntity *entity, const AdgMatrix *map)
760 AdgEntityPrivate *data = entity->data;
762 if (map == NULL)
763 cairo_matrix_init_identity(&data->local_map);
764 else
765 adg_matrix_copy(&data->local_map, map);
768 static void
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)) {
780 return;
783 data = entity->data;
784 ADG_UNSET(data->flags, RENDERED);
787 static void
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)));
800 return;
803 data = entity->data;
805 cairo_save(cr);
806 cairo_transform(cr, &data->global_map);
808 if (entity_class->render(entity, cr))
809 ADG_SET(data->flags, RENDERED);
811 cairo_restore(cr);