[AdgEntity] Improved "invalidate" signal
[adg.git] / adg / adg-entity.c
blobec0889a4c6fb43078d6321c12f2f60dc423f5024
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 a base interface for all renderable objects
26 * (all the objects that can be printed or viewed).
27 **/
29 /**
30 * AdgEntity:
32 * All fields are private and should not be used directly.
33 * Use its public methods instead.
34 **/
37 #include "adg-entity.h"
38 #include "adg-entity-private.h"
39 #include "adg-canvas.h"
40 #include "adg-context.h"
41 #include "adg-util.h"
42 #include "adg-intl.h"
45 enum {
46 PROP_0,
47 PROP_PARENT,
48 PROP_CONTEXT,
49 PROP_GLOBAL_MAP,
50 PROP_LOCAL_MAP
53 enum {
54 PARENT_SET,
55 INVALIDATE,
56 RENDER,
57 LAST_SIGNAL
61 static void get_property (GObject *object,
62 guint prop_id,
63 GValue *value,
64 GParamSpec *pspec);
65 static void set_property (GObject *object,
66 guint prop_id,
67 const GValue *value,
68 GParamSpec *pspec);
69 static void dispose (GObject *object);
70 static AdgContainer * get_parent (AdgEntity *entity);
71 static void set_parent (AdgEntity *entity,
72 AdgContainer *parent);
73 static void parent_set (AdgEntity *entity,
74 AdgContainer *old_parent);
75 static AdgContext * get_context (AdgEntity *entity);
76 static void set_context (AdgEntity *entity,
77 AdgContext *context);
78 static void set_global_map (AdgEntity *entity,
79 const AdgMatrix *map);
80 static void set_local_map (AdgEntity *entity,
81 const AdgMatrix *map);
82 static void real_invalidate (AdgEntity *entity,
83 gpointer user_data);
84 static void real_render (AdgEntity *entity,
85 cairo_t *cr,
86 gpointer user_data);
88 static guint signals[LAST_SIGNAL] = { 0 };
91 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
94 static void
95 adg_entity_class_init(AdgEntityClass *klass)
97 GObjectClass *gobject_class;
98 GParamSpec *param;
99 GClosure *closure;
100 GType param_types[1];
102 gobject_class = (GObjectClass *) klass;
104 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
106 gobject_class->get_property = get_property;
107 gobject_class->set_property = set_property;
108 gobject_class->dispose = dispose;
110 klass->get_parent = get_parent;
111 klass->set_parent = set_parent;
112 klass->parent_set = parent_set;
113 klass->get_context = get_context;
114 klass->invalidate = NULL;
115 klass->render = NULL;
117 param = g_param_spec_object("parent",
118 P_("Parent Container"),
119 P_("The parent AdgContainer of this entity or NULL if this is a top-level entity"),
120 ADG_TYPE_CONTAINER, G_PARAM_READWRITE);
121 g_object_class_install_property(gobject_class, PROP_PARENT, param);
123 param = g_param_spec_object("context",
124 P_("Context"),
125 P_("The context associated to this entity or NULL to inherit the parent context"),
126 ADG_TYPE_CONTEXT, G_PARAM_READWRITE);
127 g_object_class_install_property(gobject_class, PROP_CONTEXT, param);
129 param = g_param_spec_boxed("global-map",
130 P_("Global Map"),
131 P_("The transformation to be combined with the parent ones to get the global matrix"),
132 ADG_TYPE_MATRIX, G_PARAM_READWRITE);
133 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
135 param = g_param_spec_boxed("local-map",
136 P_("Local Map"),
137 P_("The transformation to be combined with the parent ones to get the local matrix"),
138 ADG_TYPE_MATRIX, G_PARAM_READWRITE);
139 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
142 * AdgEntity::parent-set:
143 * @entity: an #AdgEntity
144 * @parent: the #AdgContainer parent of @entity
146 * Emitted after the parent container has changed.
148 signals[PARENT_SET] = g_signal_new("parent-set",
149 G_OBJECT_CLASS_TYPE(gobject_class),
150 G_SIGNAL_RUN_FIRST,
151 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
152 NULL, NULL,
153 g_cclosure_marshal_VOID__OBJECT,
154 G_TYPE_NONE, 1, ADG_TYPE_CONTAINER);
157 * AdgEntity::invalidate:
158 * @entity: an #AdgEntity
160 * The inverse of the rendering. Usually, invalidation causes the
161 * cache of @entity to be cleared. After a succesful invalidation
162 * the rendered flag is reset: you can access its state using
163 * adg_entity_get_rendered().
165 closure = g_cclosure_new(G_CALLBACK(real_invalidate),
166 (gpointer)0xdeadbeaf, NULL);
167 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
168 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
169 g_cclosure_marshal_VOID__VOID,
170 G_TYPE_NONE, 0, param_types);
173 * AdgEntity::render:
174 * @entity: an #AdgEntity
175 * @cr: a #cairo_t drawing context
177 * Causes the rendering of @entity on @cr. After a succesful rendering
178 * the rendered flag is set: you can access its state using
179 * adg_entity_get_rendered().
181 closure = g_cclosure_new(G_CALLBACK(real_render),
182 (gpointer)0xdeadbeaf, NULL);
183 param_types[0] = G_TYPE_POINTER;
184 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
185 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
186 g_cclosure_marshal_VOID__POINTER,
187 G_TYPE_NONE, 1, param_types);
190 static void
191 adg_entity_init(AdgEntity *entity)
193 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
194 ADG_TYPE_ENTITY,
195 AdgEntityPrivate);
196 data->parent = NULL;
197 data->flags = 0;
198 data->context = NULL;
199 cairo_matrix_init_identity(&data->local_map);
200 cairo_matrix_init_identity(&data->global_map);
202 entity->data = data;
205 static void
206 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
208 AdgEntity *entity;
209 AdgEntityPrivate *data;
211 entity = (AdgEntity *) object;
212 data = entity->data;
214 switch (prop_id) {
215 case PROP_PARENT:
216 g_value_set_object(value, get_parent(entity));
217 break;
218 case PROP_CONTEXT:
219 g_value_set_object(value, get_context(entity));
220 break;
221 case PROP_GLOBAL_MAP:
222 g_value_set_boxed(value, &data->global_map);
223 break;
224 case PROP_LOCAL_MAP:
225 g_value_set_boxed(value, &data->local_map);
226 break;
227 default:
228 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
229 break;
233 static void
234 set_property(GObject *object,
235 guint prop_id, const GValue *value, GParamSpec *pspec)
237 AdgEntity *entity = (AdgEntity *) object;
239 switch (prop_id) {
240 case PROP_PARENT:
241 set_parent(entity, g_value_get_object(value));
242 break;
243 case PROP_CONTEXT:
244 set_context(entity, g_value_get_object(value));
245 break;
246 case PROP_GLOBAL_MAP:
247 set_global_map(entity, g_value_get_boxed(value));
248 break;
249 case PROP_LOCAL_MAP:
250 set_local_map(entity, g_value_get_boxed(value));
251 break;
252 default:
253 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
254 break;
258 static void
259 dispose(GObject *object)
261 AdgEntity *entity;
262 AdgEntityPrivate *data;
263 GObjectClass *object_class;
265 entity = (AdgEntity *) object;
266 data = entity->data;
267 object_class = (GObjectClass *) adg_entity_parent_class;
269 if (data->parent)
270 adg_container_remove(data->parent, entity);
272 if (object_class->dispose != NULL)
273 object_class->dispose(object);
278 * adg_entity_get_parent:
279 * @entity: an #AdgEntity
281 * Gets the container parent of @entity.
283 * This function is only useful in entity implementations.
285 * Return value: the container object or %NULL if @entity is not contained
287 AdgContainer *
288 adg_entity_get_parent(AdgEntity *entity)
290 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
292 return ADG_ENTITY_GET_CLASS(entity)->get_parent(entity);
296 * adg_entity_set_parent:
297 * @entity: an #AdgEntity
298 * @parent: an #AdgContainer
300 * Sets a new container of @entity.
302 * This function is only useful in entity implementations.
304 void
305 adg_entity_set_parent(AdgEntity *entity, AdgContainer *parent)
307 g_return_if_fail(ADG_IS_ENTITY(entity));
309 ADG_ENTITY_GET_CLASS(entity)->set_parent(entity, parent);
310 g_object_notify((GObject *) entity, "parent");
314 * adg_entity_unparent:
315 * @entity: an #AdgEntity
317 * Removes the current parent of @entity, properly handling
318 * the references between them.
320 * If @entity has no parent, this function simply returns.
322 void
323 adg_entity_unparent(AdgEntity *entity)
325 AdgContainer *old_parent;
327 g_return_if_fail(ADG_IS_ENTITY(entity));
329 old_parent = ADG_ENTITY_GET_CLASS(entity)->get_parent(entity);
331 if (old_parent == NULL)
332 return;
334 ADG_ENTITY_GET_CLASS(entity)->set_parent(entity, NULL);
335 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
337 g_object_unref(entity);
341 * adg_entity_reparent:
342 * @entity: an #AdgEntity
343 * @parent: the new container
345 * Moves @entity from the old parent to @parent, handling reference
346 * count issues to avoid destroying the object.
348 void
349 adg_entity_reparent(AdgEntity *entity, AdgContainer *parent)
351 AdgContainer *old_parent;
353 g_return_if_fail(ADG_IS_CONTAINER(parent));
355 old_parent = adg_entity_get_parent(entity);
357 /* Reparenting on the same container: do nothing */
358 if (old_parent == parent)
359 return;
361 g_return_if_fail(ADG_IS_CONTAINER(old_parent));
363 g_object_ref(entity);
364 adg_container_remove(old_parent, entity);
365 adg_container_add(parent, entity);
366 g_object_unref(entity);
370 * adg_entity_get_context:
371 * @entity: an #AdgEntity instance
373 * Gets the context associated to @entity.
374 * If no context was explicitely set, get the parent context.
376 * Return value: the requested context or %NULL on errors
378 AdgContext *
379 adg_entity_get_context(AdgEntity *entity)
381 AdgEntityPrivate *data;
383 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
385 data = entity->data;
387 if (data->context)
388 return data->context;
390 if (data->parent)
391 return adg_entity_get_context((AdgEntity *) data->parent);
393 return NULL;
397 * adg_entity_set_context:
398 * @entity: an #AdgEntity instance
399 * @context: the new context
401 * Sets a new context. The old context (if any) will be unreferenced
402 * while a new reference will be added to @context.
404 void
405 adg_entity_set_context(AdgEntity *entity, AdgContext *context)
407 g_return_if_fail(ADG_IS_ENTITY(entity));
408 g_return_if_fail(ADG_IS_CONTEXT(context));
410 set_context(entity, context);
411 g_object_notify((GObject *) entity, "context");
415 * adg_entity_get_canvas:
416 * @entity: an #AdgEntity
418 * Walks on the @entity hierarchy and gets the first parent of @entity that is
419 * of #AdgCanvas derived type.
421 * Return value: the requested object or %NULL if there is no #AdgCanvas in
422 * the parent hierarchy.
424 AdgCanvas *
425 adg_entity_get_canvas(AdgEntity *entity)
427 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
429 while (entity) {
430 if (ADG_IS_CANVAS(entity))
431 return (AdgCanvas *) entity;
433 entity = (AdgEntity *) adg_entity_get_parent(entity);
436 return NULL;
440 * adg_entity_get_rendered:
441 * @entity: an #AdgEntity object
443 * This function is only for use in entity implementations.
444 * Gets the rendered flag of @entity.
446 * Returns: the current rendered state
448 gboolean
449 adg_entity_get_rendered(AdgEntity *entity)
451 AdgEntityPrivate *data;
453 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
455 data = entity->data;
457 return ADG_ISSET(data->flags, RENDERED);
461 * adg_entity_set_rendered:
462 * @entity: an #AdgEntity object
463 * @rendered: new state for the rendered flag
465 * This function is only for use in entity implementations.
466 * Sets the rendered flag of @entity to @rendered.
468 void
469 adg_entity_set_rendered(AdgEntity *entity, gboolean rendered)
471 AdgEntityPrivate *data;
473 g_return_if_fail(ADG_IS_ENTITY(entity));
475 data = entity->data;
477 if (rendered)
478 ADG_SET(data->flags, RENDERED);
479 else
480 ADG_UNSET(data->flags, RENDERED);
484 * adg_entity_get_global_map:
485 * @entity: an #AdgEntity object
486 * @map: where to store the global map
488 * Gets the transformation to be used to compute the global matrix
489 * of @entity and store it in @map.
491 void
492 adg_entity_get_global_map(AdgEntity *entity, AdgMatrix *map)
494 AdgEntityPrivate *data;
496 g_return_if_fail(ADG_IS_ENTITY(entity));
497 g_return_if_fail(map != NULL);
499 data = entity->data;
500 adg_matrix_copy(map, &data->global_map);
504 * adg_entity_set_global_map:
505 * @entity: an #AdgEntity object
506 * @map: the new map
508 * Sets the new global transformation of @entity to @map:
509 * the old map is discarded. If @map is %NULL an identity
510 * matrix is implied.
512 void
513 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
515 g_return_if_fail(ADG_IS_ENTITY(entity));
517 set_global_map(entity, map);
518 g_object_notify((GObject *) entity, "global-map");
522 * adg_entity_get_local_map:
523 * @entity: an #AdgEntity object
524 * @map: where to store the local map
526 * Gets the transformation to be used to compute the local matrix
527 * of @entity and store it in @map.
529 void
530 adg_entity_get_local_map(AdgEntity *entity, AdgMatrix *map)
532 AdgEntityPrivate *data;
534 g_return_if_fail(ADG_IS_ENTITY(entity));
535 g_return_if_fail(map != NULL);
537 data = entity->data;
538 adg_matrix_copy(map, &data->local_map);
542 * adg_entity_set_local_map:
543 * @entity: an #AdgEntity object
544 * @map: the new map
546 * Sets the new global transformation of @entity to @map:
547 * the old map is discarded. If @map is %NULL an identity
548 * matrix is implied.
550 void
551 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
553 g_return_if_fail(ADG_IS_ENTITY(entity));
555 set_local_map(entity, map);
556 g_object_notify((GObject *) entity, "local-map");
560 * adg_entity_get_global_matrix:
561 * @entity: an #AdgEntity object
562 * @matrix: where to store the global matrix
564 * Computes the global matrix by combining all the global maps of the
565 * @entity hierarchy and stores the result in @matrix.
567 void
568 adg_entity_get_global_matrix(AdgEntity *entity, AdgMatrix *matrix)
570 AdgEntityPrivate *data;
572 g_return_if_fail(ADG_IS_ENTITY(entity));
573 g_return_if_fail(matrix != NULL);
575 data = entity->data;
577 if (data->parent == NULL) {
578 adg_matrix_copy(matrix, &data->global_map);
579 } else {
580 adg_entity_get_global_matrix((AdgEntity *) data->parent, matrix);
581 cairo_matrix_multiply(matrix, &data->global_map, matrix);
586 * adg_entity_get_local_matrix:
587 * @entity: an #AdgEntity object
588 * @matrix: where to store the local matrix
590 * Computes the local matrix by combining all the local maps of the
591 * @entity hierarchy and stores the result in @matrix.
593 void
594 adg_entity_get_local_matrix(AdgEntity *entity, AdgMatrix *matrix)
596 AdgEntityPrivate *data;
598 g_return_if_fail(ADG_IS_ENTITY(entity));
599 g_return_if_fail(matrix != NULL);
601 data = entity->data;
603 if (data->parent == NULL) {
604 adg_matrix_copy(matrix, &data->local_map);
605 } else {
606 adg_entity_get_local_matrix((AdgEntity *) data->parent, matrix);
607 cairo_matrix_multiply(matrix, &data->local_map, matrix);
612 * adg_entity_get_style:
613 * @entity: an #AdgEntity
614 * @style_slot: the slot of the style to get
616 * Gets a style from this entity. If the entity has no context associated
617 * or the style in undefined within this context, gets the style from its
618 * parent container.
620 * Return value: the requested style or %NULL on errors
622 AdgStyle *
623 adg_entity_get_style(AdgEntity *entity, AdgStyleSlot style_slot)
625 AdgEntityPrivate *data;
627 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
629 data = entity->data;
631 if (data->context) {
632 AdgStyle *style = adg_context_get_style(data->context, style_slot);
633 if (style)
634 return style;
637 if (data->parent)
638 return adg_entity_get_style((AdgEntity *) data->parent, style_slot);
640 return NULL;
644 * adg_entity_apply:
645 * @entity: an #AdgEntity
646 * @style_slot: the slot of the style to apply
647 * @cr: a #cairo_t drawing context
649 * Applies the specified style to the @cr cairo context.
651 void
652 adg_entity_apply(AdgEntity *entity, AdgStyleSlot style_slot, cairo_t *cr)
654 AdgStyle *style = adg_entity_get_style(entity, style_slot);
656 if (style)
657 adg_style_apply(style, cr);
661 * adg_entity_invalidate:
662 * @entity: an #AdgEntity
664 * Emits the "invalidate" signal on @entity and all its children, if any,
665 * so subsequent rendering will need a global recomputation.
667 void
668 adg_entity_invalidate(AdgEntity *entity)
670 g_return_if_fail(ADG_IS_ENTITY(entity));
672 g_signal_emit(entity, signals[INVALIDATE], 0, NULL);
676 * adg_entity_render:
677 * @entity: an #AdgEntity
678 * @cr: a #cairo_t drawing context
680 * Emits the "render" signal on @entity and all its children, if any,
681 * causing the rendering operation the @cr cairo context.
683 void
684 adg_entity_render(AdgEntity *entity, cairo_t *cr)
686 g_return_if_fail(ADG_IS_ENTITY(entity));
688 g_signal_emit(entity, signals[RENDER], 0, cr);
692 static AdgContainer *
693 get_parent(AdgEntity *entity)
695 AdgEntityPrivate *data = entity->data;
697 return data->parent;
700 static void
701 set_parent(AdgEntity *entity, AdgContainer *parent)
703 AdgEntityPrivate *data = entity->data;
705 data->parent = parent;
708 static void
709 parent_set(AdgEntity *entity, AdgContainer *old_parent)
713 static AdgContext *
714 get_context(AdgEntity *entity)
716 AdgEntityPrivate *data = entity->data;
717 AdgEntity *parent;
719 if (data->context)
720 return data->context;
722 parent = (AdgEntity *) data->parent;
724 return parent ? ADG_ENTITY_GET_CLASS(parent)->get_context(parent) : NULL;
727 static void
728 set_context(AdgEntity *entity, AdgContext *context)
730 AdgEntityPrivate *data = entity->data;
732 if (data->context)
733 g_object_unref((GObject *) data->context);
735 g_object_ref((GObject *) context);
736 data->context = context;
739 static void
740 set_global_map(AdgEntity *entity, const AdgMatrix *map)
742 AdgEntityPrivate *data = entity->data;
744 if (map == NULL)
745 cairo_matrix_init_identity(&data->global_map);
746 else
747 adg_matrix_copy(&data->global_map, map);
750 static void
751 set_local_map(AdgEntity *entity, const AdgMatrix *map)
753 AdgEntityPrivate *data = entity->data;
755 if (map == NULL)
756 cairo_matrix_init_identity(&data->local_map);
757 else
758 adg_matrix_copy(&data->local_map, map);
761 static void
762 real_invalidate(AdgEntity *entity, gpointer user_data)
764 AdgEntityClass *entity_class;
765 AdgEntityPrivate *data;
767 g_assert(user_data == (gpointer) 0xdeadbeaf);
769 entity_class = ADG_ENTITY_GET_CLASS(entity);
770 if (entity_class->invalidate == NULL) {
771 /* Assume the entity does not have cache to be cleared */
772 } else if (!entity_class->invalidate(entity)) {
773 return;
776 data = entity->data;
777 ADG_UNSET(data->flags, RENDERED);
780 static void
781 real_render(AdgEntity *entity, cairo_t *cr, gpointer user_data)
783 AdgEntityClass *entity_class;
784 AdgEntityPrivate *data;
786 g_assert(user_data == (gpointer) 0xdeadbeaf);
788 entity_class = ADG_ENTITY_GET_CLASS(entity);
789 if (entity_class->render == NULL) {
790 /* The render method must be defined */
791 g_warning("%s: `render' method not implemented for type `%s'",
792 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
793 return;
796 data = entity->data;
798 cairo_save(cr);
799 cairo_transform(cr, &data->global_map);
801 if (entity_class->render(entity, cr))
802 ADG_SET(data->flags, RENDERED);
804 cairo_restore(cr);