[AdgEntity] get_{global,local}_matrix now virtuals
[adg.git] / adg / adg-entity.c
blob5af549683b8a588bab0329417d74e5d3ab82d9f0
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-font-style.h"
55 #include "adg-dim-style.h"
56 #include "adg-intl.h"
58 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
61 enum {
62 PROP_0,
63 PROP_PARENT,
64 PROP_CONTEXT,
65 PROP_GLOBAL_MAP,
66 PROP_LOCAL_MAP
69 enum {
70 PARENT_SET,
71 CONTEXT_SET,
72 INVALIDATE,
73 RENDER,
74 LAST_SIGNAL
78 static void dispose (GObject *object);
79 static void get_property (GObject *object,
80 guint prop_id,
81 GValue *value,
82 GParamSpec *pspec);
83 static void set_property (GObject *object,
84 guint prop_id,
85 const GValue *value,
86 GParamSpec *pspec);
87 static gboolean set_parent (AdgEntity *entity,
88 AdgEntity *parent);
89 static gboolean set_context (AdgEntity *entity,
90 AdgContext *context);
91 static void set_global_map (AdgEntity *entity,
92 const AdgMatrix *map);
93 static void set_local_map (AdgEntity *entity,
94 const AdgMatrix *map);
95 static void get_global_matrix (AdgEntity *entity,
96 AdgMatrix *matrix);
97 static void get_local_matrix (AdgEntity *entity,
98 AdgMatrix *matrix);
99 static void real_invalidate (AdgEntity *entity,
100 gpointer user_data);
101 static void real_render (AdgEntity *entity,
102 cairo_t *cr,
103 gpointer user_data);
105 static guint signals[LAST_SIGNAL] = { 0 };
108 G_DEFINE_ABSTRACT_TYPE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED);
111 static void
112 adg_entity_class_init(AdgEntityClass *klass)
114 GObjectClass *gobject_class;
115 GParamSpec *param;
116 GClosure *closure;
117 GType param_types[1];
119 gobject_class = (GObjectClass *) klass;
121 g_type_class_add_private(klass, sizeof(AdgEntityPrivate));
123 gobject_class->dispose = dispose;
124 gobject_class->get_property = get_property;
125 gobject_class->set_property = set_property;
127 klass->parent_set = NULL;
128 klass->context_set = NULL;
129 klass->get_global_matrix = get_global_matrix;
130 klass->get_local_matrix = get_local_matrix;
131 klass->invalidate = NULL;
132 klass->render = NULL;
134 param = g_param_spec_object("parent",
135 P_("Parent Entity"),
136 P_("The parent entity of this entity or NULL if this is a top-level entity"),
137 ADG_TYPE_ENTITY, G_PARAM_READWRITE);
138 g_object_class_install_property(gobject_class, PROP_PARENT, param);
140 param = g_param_spec_object("context",
141 P_("Context"),
142 P_("The context associated to this entity or NULL to inherit the parent context"),
143 ADG_TYPE_CONTEXT, G_PARAM_READWRITE);
144 g_object_class_install_property(gobject_class, PROP_CONTEXT, param);
146 param = g_param_spec_boxed("global-map",
147 P_("Global Map"),
148 P_("The transformation to be combined with the parent ones to get the global matrix"),
149 ADG_TYPE_MATRIX, G_PARAM_READWRITE);
150 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
152 param = g_param_spec_boxed("local-map",
153 P_("Local Map"),
154 P_("The transformation to be combined with the parent ones to get the local matrix"),
155 ADG_TYPE_MATRIX, G_PARAM_READWRITE);
156 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
159 * AdgEntity::parent-set:
160 * @entity: an #AdgEntity
161 * @old_parent: the old parent
163 * Emitted after the parent entity has changed. The new parent
164 * can be inspected using adg_entity_get_parent().
166 * It is allowed for both old and new parent to be %NULL.
168 signals[PARENT_SET] = g_signal_new("parent-set",
169 G_OBJECT_CLASS_TYPE(gobject_class),
170 G_SIGNAL_RUN_FIRST,
171 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
172 NULL, NULL,
173 g_cclosure_marshal_VOID__OBJECT,
174 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
177 * AdgEntity::context-set:
178 * @entity: an #AdgEntity
179 * @old_context: the old context
181 * Emitted after the context has changed.
182 * Emitted after the context has changed. The new context can be
183 * inspected using adg_entity_get_context().
185 * It is allowed for both old and new context to be %NULL.
187 signals[CONTEXT_SET] = g_signal_new("context-set",
188 G_OBJECT_CLASS_TYPE(gobject_class),
189 G_SIGNAL_RUN_FIRST,
190 G_STRUCT_OFFSET(AdgEntityClass, context_set),
191 NULL, NULL,
192 g_cclosure_marshal_VOID__OBJECT,
193 G_TYPE_NONE, 1, ADG_TYPE_CONTEXT);
196 * AdgEntity::invalidate:
197 * @entity: an #AdgEntity
199 * The inverse of the rendering. Usually, invalidation causes the
200 * cache of @entity to be cleared. After a succesful invalidation
201 * the rendered flag is reset: you can access its state using
202 * adg_entity_get_rendered().
204 closure = g_cclosure_new(G_CALLBACK(real_invalidate),
205 (gpointer)0xdeadbeaf, NULL);
206 signals[INVALIDATE] = g_signal_newv("invalidate", ADG_TYPE_ENTITY,
207 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
208 g_cclosure_marshal_VOID__VOID,
209 G_TYPE_NONE, 0, param_types);
212 * AdgEntity::render:
213 * @entity: an #AdgEntity
214 * @cr: a #cairo_t drawing context
216 * Causes the rendering of @entity on @cr. After a succesful rendering
217 * the rendered flag is set: you can access its state using
218 * adg_entity_get_rendered().
220 closure = g_cclosure_new(G_CALLBACK(real_render),
221 (gpointer)0xdeadbeaf, NULL);
222 param_types[0] = G_TYPE_POINTER;
223 signals[RENDER] = g_signal_newv("render", ADG_TYPE_ENTITY,
224 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
225 g_cclosure_marshal_VOID__POINTER,
226 G_TYPE_NONE, 1, param_types);
229 static void
230 adg_entity_init(AdgEntity *entity)
232 AdgEntityPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(entity,
233 ADG_TYPE_ENTITY,
234 AdgEntityPrivate);
235 data->parent = NULL;
236 data->flags = 0;
237 data->context = NULL;
238 cairo_matrix_init_identity(&data->local_map);
239 cairo_matrix_init_identity(&data->global_map);
241 entity->data = data;
244 static void
245 dispose(GObject *object)
247 AdgEntity *entity;
248 AdgEntity *parent;
250 entity = (AdgEntity *) object;
251 parent = adg_entity_get_parent(entity);
253 /* These calls will emit a "notify" signal both for parent and
254 * context if they are not NULL. Consequentially, the references
255 * to the old parent and context are dropped. */
256 adg_entity_set_parent(entity, NULL);
257 adg_entity_set_context(entity, NULL);
259 if (PARENT_OBJECT_CLASS->dispose != NULL)
260 PARENT_OBJECT_CLASS->dispose(object);
264 static void
265 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
267 AdgEntity *entity;
268 AdgEntityPrivate *data;
270 entity = (AdgEntity *) object;
271 data = entity->data;
273 switch (prop_id) {
274 case PROP_PARENT:
275 g_value_set_object(value, data->parent);
276 break;
277 case PROP_CONTEXT:
278 g_value_set_object(value, adg_entity_get_context(entity));
279 break;
280 case PROP_GLOBAL_MAP:
281 g_value_set_boxed(value, &data->global_map);
282 break;
283 case PROP_LOCAL_MAP:
284 g_value_set_boxed(value, &data->local_map);
285 break;
286 default:
287 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
288 break;
292 static void
293 set_property(GObject *object,
294 guint prop_id, const GValue *value, GParamSpec *pspec)
296 AdgEntity *entity = (AdgEntity *) object;
298 switch (prop_id) {
299 case PROP_PARENT:
300 set_parent(entity, g_value_get_object(value));
301 break;
302 case PROP_CONTEXT:
303 set_context(entity, g_value_get_object(value));
304 break;
305 case PROP_GLOBAL_MAP:
306 set_global_map(entity, g_value_get_boxed(value));
307 break;
308 case PROP_LOCAL_MAP:
309 set_local_map(entity, g_value_get_boxed(value));
310 break;
311 default:
312 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
313 break;
319 * adg_entity_get_parent:
320 * @entity: an #AdgEntity
322 * Gets the parent of @entity.
324 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
326 AdgEntity *
327 adg_entity_get_parent(AdgEntity *entity)
329 AdgEntityPrivate *data;
331 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
333 data = entity->data;
335 return data->parent;
339 * adg_entity_set_parent:
340 * @entity: an #AdgEntity
341 * @parent: the parent entity
343 * <note><para>
344 * This function is only useful in entity implementations.
345 * </para></note>
347 * Sets a new parent on @entity.
349 void
350 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
352 g_return_if_fail(ADG_IS_ENTITY(entity));
354 if (set_parent(entity, parent))
355 g_object_notify((GObject *) entity, "parent");
359 * adg_entity_context:
360 * @entity: an #AdgEntity instance
362 * Gets the context to be used for @entity. If no context was
363 * explicitely set, it returns the parent context.
365 * Returns: the requested context or %NULL on errors or if @entity
366 * does not have any parent with a context defined
368 AdgContext *
369 adg_entity_context(AdgEntity *entity)
371 AdgContext *context;
373 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
375 context = adg_entity_get_context(entity);
377 if (context == NULL) {
378 AdgEntity *parent = adg_entity_get_parent(entity);
380 if (parent != NULL)
381 context = adg_entity_get_context(parent);
384 return context;
388 * adg_entity_get_context:
389 * @entity: an #AdgEntity instance
391 * Gets the context associated to @entity. This is an accessor method:
392 * if you need to get the context for rendering, use adg_entity_context()
393 * instead.
395 * Returns: the context binded to @entity
397 AdgContext *
398 adg_entity_get_context(AdgEntity *entity)
400 AdgEntityPrivate *data;
402 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
404 data = entity->data;
406 return data->context;
410 * adg_entity_set_context:
411 * @entity: an #AdgEntity instance
412 * @context: the new context
414 * Sets a new context. The old context (if any) will be unreferenced
415 * while a new reference will be added to @context.
417 void
418 adg_entity_set_context(AdgEntity *entity, AdgContext *context)
420 g_return_if_fail(ADG_IS_ENTITY(entity));
421 g_return_if_fail(ADG_IS_CONTEXT(context));
423 if (set_context(entity, context))
424 g_object_notify((GObject *) entity, "context");
428 * adg_entity_get_canvas:
429 * @entity: an #AdgEntity
431 * Walks on the @entity hierarchy and gets the first parent of @entity that is
432 * of #AdgCanvas derived type.
434 * Returns: the requested canvas or %NULL on errors or if there is
435 * no #AdgCanvas in the @entity hierarchy
437 AdgCanvas *
438 adg_entity_get_canvas(AdgEntity *entity)
440 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
442 while (entity) {
443 if (ADG_IS_CANVAS(entity))
444 return (AdgCanvas *) entity;
446 entity = (AdgEntity *) adg_entity_get_parent(entity);
449 return NULL;
453 * adg_entity_get_rendered:
454 * @entity: an #AdgEntity object
456 * <note><para>
457 * This function is only useful in entity implementations.
458 * </para></note>
460 * Gets the rendered flag of @entity.
462 * Returns: the current rendered state
464 gboolean
465 adg_entity_get_rendered(AdgEntity *entity)
467 AdgEntityPrivate *data;
469 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
471 data = entity->data;
473 return ADG_ISSET(data->flags, RENDERED);
477 * adg_entity_set_rendered:
478 * @entity: an #AdgEntity object
479 * @rendered: new state for the rendered flag
481 * <note><para>
482 * This function is only useful in entity implementations.
483 * </para></note>
485 * Sets the rendered flag of @entity to @rendered.
487 void
488 adg_entity_set_rendered(AdgEntity *entity, gboolean rendered)
490 AdgEntityPrivate *data;
492 g_return_if_fail(ADG_IS_ENTITY(entity));
494 data = entity->data;
496 if (rendered)
497 ADG_SET(data->flags, RENDERED);
498 else
499 ADG_UNSET(data->flags, RENDERED);
503 * adg_entity_get_global_map:
504 * @entity: an #AdgEntity object
505 * @map: where to store the global map
507 * Gets the transformation to be used to compute the global matrix
508 * of @entity and store it in @map.
510 void
511 adg_entity_get_global_map(AdgEntity *entity, AdgMatrix *map)
513 AdgEntityPrivate *data;
515 g_return_if_fail(ADG_IS_ENTITY(entity));
516 g_return_if_fail(map != NULL);
518 data = entity->data;
519 adg_matrix_copy(map, &data->global_map);
523 * adg_entity_set_global_map:
524 * @entity: an #AdgEntity object
525 * @map: the new map
527 * Sets the new global transformation of @entity to @map:
528 * the old map is discarded. If @map is %NULL an identity
529 * matrix is implied.
531 void
532 adg_entity_set_global_map(AdgEntity *entity, const AdgMatrix *map)
534 g_return_if_fail(ADG_IS_ENTITY(entity));
536 set_global_map(entity, map);
537 g_object_notify((GObject *) entity, "global-map");
541 * adg_entity_transform_global_map:
542 * @entity: an #AdgEntity object
543 * @transformation: the transformation to apply
545 * Applies a transformation to the global map of @entity. This is
546 * equivalent to the following code:
548 * |[
549 * AdgMatrix tmp_map;
550 * adg_entity_get_global_map(entity, &tmp_map);
551 * cairo_matrix_multiply(&tmp_map, transformation, &tmp_map);
552 * adg_entity_set_global_map(entity, &tmp_map);
553 * ]|
555 void
556 adg_entity_transform_global_map(AdgEntity *entity,
557 const AdgMatrix *transformation)
559 AdgEntityPrivate *data;
561 g_return_if_fail(ADG_IS_ENTITY(entity));
562 g_return_if_fail(transformation != NULL);
564 data = entity->data;
566 cairo_matrix_multiply(&data->global_map, transformation, &data->global_map);
567 g_object_notify((GObject *) entity, "global-map");
571 * adg_entity_get_global_matrix:
572 * @entity: an #AdgEntity object
573 * @matrix: where to store the global matrix
575 * Computes the global matrix by combining all the global maps of the
576 * @entity hierarchy and stores the result in @matrix.
578 void
579 adg_entity_get_global_matrix(AdgEntity *entity, AdgMatrix *matrix)
581 g_return_if_fail(ADG_IS_ENTITY(entity));
582 g_return_if_fail(matrix != NULL);
584 ADG_ENTITY_GET_CLASS(entity)->get_global_matrix(entity, matrix);
588 * adg_entity_get_local_map:
589 * @entity: an #AdgEntity object
590 * @map: where to store the local map
592 * Gets the transformation to be used to compute the local matrix
593 * of @entity and store it in @map.
595 void
596 adg_entity_get_local_map(AdgEntity *entity, AdgMatrix *map)
598 AdgEntityPrivate *data;
600 g_return_if_fail(ADG_IS_ENTITY(entity));
601 g_return_if_fail(map != NULL);
603 data = entity->data;
604 adg_matrix_copy(map, &data->local_map);
608 * adg_entity_set_local_map:
609 * @entity: an #AdgEntity object
610 * @map: the new map
612 * Sets the new global transformation of @entity to @map:
613 * the old map is discarded. If @map is %NULL an identity
614 * matrix is implied.
616 void
617 adg_entity_set_local_map(AdgEntity *entity, const AdgMatrix *map)
619 g_return_if_fail(ADG_IS_ENTITY(entity));
621 set_local_map(entity, map);
622 g_object_notify((GObject *) entity, "local-map");
626 * adg_entity_transform_local_map:
627 * @entity: an #AdgEntity object
628 * @transformation: the transformation to apply
630 * Applies a transformation to the local map of @entity. This is
631 * equivalent to the following code:
633 * |[
634 * AdgMatrix tmp_map;
635 * adg_entity_get_local_map(entity, &tmp_map);
636 * cairo_matrix_multiply(&tmp_map, transformation, &tmp_map);
637 * adg_entity_set_local_map(entity, &tmp_map);
638 * ]|
640 void
641 adg_entity_transform_local_map(AdgEntity *entity,
642 const AdgMatrix *transformation)
644 AdgEntityPrivate *data;
646 g_return_if_fail(ADG_IS_ENTITY(entity));
647 g_return_if_fail(transformation != NULL);
649 data = entity->data;
651 cairo_matrix_multiply(&data->local_map, transformation, &data->local_map);
652 g_object_notify((GObject *) entity, "local-map");
656 * adg_entity_get_local_matrix:
657 * @entity: an #AdgEntity object
658 * @matrix: where to store the local matrix
660 * Computes the local matrix by combining all the local maps of the
661 * @entity hierarchy and stores the result in @matrix.
663 void
664 adg_entity_get_local_matrix(AdgEntity *entity, AdgMatrix *matrix)
666 g_return_if_fail(ADG_IS_ENTITY(entity));
667 g_return_if_fail(matrix != NULL);
669 ADG_ENTITY_GET_CLASS(entity)->get_local_matrix(entity, matrix);
673 * adg_entity_apply_local_matrix:
674 * @entity: an #AdgEntity object
675 * @cr: a #cairo_t drawing context
677 * Applies the local matrix of @entity BEFORE the current transformation
678 * matrix of @cr. This is a convenience function equivalent to:
680 * |[
681 * AdgMatrix local, ctm;
683 * adg_entity_get_local_matrix(entity, &local);
684 * cairo_get_matrix(cr, &ctm);
686 * cairo_matrix_multiply(&ctm, &ctm, &local);
687 * cairo_set_matrix(cr, &ctm);
688 * ]|
690 void
691 adg_entity_apply_local_matrix(AdgEntity *entity, cairo_t *cr)
693 AdgMatrix local, ctm;
695 g_return_if_fail(ADG_IS_ENTITY(entity));
696 g_return_if_fail(cr != NULL);
698 adg_entity_get_local_matrix(entity, &local);
699 cairo_get_matrix(cr, &ctm);
701 cairo_matrix_multiply(&ctm, &ctm, &local);
702 cairo_set_matrix(cr, &ctm);
706 * adg_entity_get_style:
707 * @entity: an #AdgEntity
708 * @style_slot: the slot of the style to get
710 * Gets a style from this entity. If the entity has no context associated
711 * or the style is undefined within this context, gets the style from its
712 * parent entity.
714 * Returns: the requested style or %NULL on errors or if there is no
715 * context with this style defined in the @entity hierarchy
717 AdgStyle *
718 adg_entity_get_style(AdgEntity *entity, AdgStyleSlot style_slot)
720 AdgEntityPrivate *data;
722 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
724 data = entity->data;
726 if (data->context) {
727 AdgStyle *style = adg_context_get_style(data->context, style_slot);
728 if (style)
729 return style;
732 if (data->parent)
733 return adg_entity_get_style(data->parent, style_slot);
735 return NULL;
739 * adg_entity_apply:
740 * @entity: an #AdgEntity
741 * @style_slot: the slot of the style to apply
742 * @cr: a #cairo_t drawing context
744 * Applies the specified style to the @cr cairo context.
746 void
747 adg_entity_apply(AdgEntity *entity, AdgStyleSlot style_slot, cairo_t *cr)
749 AdgStyle *style = adg_entity_get_style(entity, style_slot);
751 if (style)
752 adg_style_apply(style, cr);
756 * adg_entity_apply_font:
757 * @entity: an #AdgEntity
758 * @font_id: a font id
759 * @cr: a #cairo_t drawing context
761 * Applies the specified font to the @cr cairo context. It is similar
762 * to adg_entity_apply() but instead of looking for a font slot, it
763 * searches for a specific font style by inspecting composite styles too.
765 * A typical example is when the tolerance font should be applied: this
766 * is a font style embedded in the dim style, so it is not enough to
767 * use adg_entity_apply().
769 void
770 adg_entity_apply_font(AdgEntity *entity, AdgFontStyleId font_id, cairo_t *cr)
772 AdgStyle *style;
774 switch (font_id) {
776 case ADG_FONT_STYLE_TEXT:
777 style = adg_entity_get_style(entity, ADG_SLOT_FONT_STYLE);
778 break;
780 case ADG_FONT_STYLE_VALUE:
781 style = adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
782 style = adg_dim_style_get_value_style((AdgDimStyle *) style);
783 break;
785 case ADG_FONT_STYLE_TOLERANCE:
786 style = adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
787 style = adg_dim_style_get_tolerance_style((AdgDimStyle *) style);
788 break;
790 case ADG_FONT_STYLE_NOTE:
791 style = adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
792 style = adg_dim_style_get_note_style((AdgDimStyle *) style);
793 break;
795 default:
796 g_warning ("%s: invalid font id (%d)", G_STRLOC, font_id);
797 return;
800 if (style)
801 adg_style_apply(style, cr);
805 * adg_entity_invalidate:
806 * @entity: an #AdgEntity
808 * Emits the "invalidate" signal on @entity and all its children, if any,
809 * so subsequent rendering will need a global recomputation.
811 void
812 adg_entity_invalidate(AdgEntity *entity)
814 g_return_if_fail(ADG_IS_ENTITY(entity));
816 g_signal_emit(entity, signals[INVALIDATE], 0, NULL);
820 * adg_entity_render:
821 * @entity: an #AdgEntity
822 * @cr: a #cairo_t drawing context
824 * Emits the "render" signal on @entity and all its children, if any,
825 * causing the rendering operation the @cr cairo context.
827 void
828 adg_entity_render(AdgEntity *entity, cairo_t *cr)
830 g_return_if_fail(ADG_IS_ENTITY(entity));
832 g_signal_emit(entity, signals[RENDER], 0, cr);
836 static gboolean
837 set_parent(AdgEntity *entity, AdgEntity *parent)
839 AdgEntityPrivate *data;
840 AdgEntity *old_parent;
842 data = entity->data;
843 old_parent = data->parent;
845 /* Check if parent has changed */
846 if (parent == old_parent)
847 return FALSE;
849 if (parent != NULL)
850 g_object_ref(parent);
852 data->parent = parent;
853 g_signal_emit(entity, signals[PARENT_SET], 0, old_parent);
855 if (old_parent != NULL)
856 g_object_unref(old_parent);
858 return TRUE;
861 static gboolean
862 set_context(AdgEntity *entity, AdgContext *context)
864 AdgEntityPrivate *data;
865 AdgContext *old_context;
867 data = entity->data;
868 old_context = data->context;
870 /* Check if context has changed */
871 if (context == old_context)
872 return FALSE;
874 if (context != NULL)
875 g_object_ref(context);
877 data->context = context;
878 g_signal_emit(entity, signals[CONTEXT_SET], 0, old_context);
880 if (old_context != NULL)
881 g_object_unref(old_context);
883 return TRUE;
886 static void
887 set_global_map(AdgEntity *entity, const AdgMatrix *map)
889 AdgEntityPrivate *data = entity->data;
891 if (map == NULL)
892 cairo_matrix_init_identity(&data->global_map);
893 else
894 adg_matrix_copy(&data->global_map, map);
897 static void
898 set_local_map(AdgEntity *entity, const AdgMatrix *map)
900 AdgEntityPrivate *data = entity->data;
902 if (map == NULL)
903 cairo_matrix_init_identity(&data->local_map);
904 else
905 adg_matrix_copy(&data->local_map, map);
908 static void
909 get_global_matrix(AdgEntity *entity, AdgMatrix *matrix)
911 AdgEntityPrivate *data = entity->data;
913 if (data->parent == NULL) {
914 adg_matrix_copy(matrix, &data->global_map);
915 } else {
916 adg_entity_get_global_matrix(data->parent, matrix);
917 cairo_matrix_multiply(matrix, &data->global_map, matrix);
921 static void
922 get_local_matrix(AdgEntity *entity, AdgMatrix *matrix)
924 AdgEntityPrivate *data = entity->data;
926 if (data->parent == NULL) {
927 adg_matrix_copy(matrix, &data->local_map);
928 } else {
929 adg_entity_get_local_matrix(data->parent, matrix);
930 cairo_matrix_multiply(matrix, &data->local_map, matrix);
934 static void
935 real_invalidate(AdgEntity *entity, gpointer user_data)
937 AdgEntityClass *entity_class;
938 AdgEntityPrivate *data;
940 g_assert(user_data == (gpointer) 0xdeadbeaf);
942 entity_class = ADG_ENTITY_GET_CLASS(entity);
943 if (entity_class->invalidate == NULL) {
944 /* Assume the entity does not have cache to be cleared */
945 } else if (!entity_class->invalidate(entity)) {
946 return;
949 data = entity->data;
950 ADG_UNSET(data->flags, RENDERED);
953 static void
954 real_render(AdgEntity *entity, cairo_t *cr, gpointer user_data)
956 AdgEntityClass *entity_class;
957 AdgEntityPrivate *data;
959 g_assert(user_data == (gpointer) 0xdeadbeaf);
961 entity_class = ADG_ENTITY_GET_CLASS(entity);
962 if (entity_class->render == NULL) {
963 /* The render method must be defined */
964 g_warning("%s: `render' method not implemented for type `%s'",
965 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
966 return;
969 data = entity->data;
971 cairo_save(cr);
972 cairo_transform(cr, &data->global_map);
974 if (entity_class->render(entity, cr))
975 ADG_SET(data->flags, RENDERED);
977 cairo_restore(cr);