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"
54 #include "adg-font-style.h"
55 #include "adg-dim-style.h"
58 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
78 static void dispose (GObject
*object
);
79 static void get_property (GObject
*object
,
83 static void set_property (GObject
*object
,
87 static gboolean
set_parent (AdgEntity
*entity
,
89 static gboolean
set_context (AdgEntity
*entity
,
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 real_invalidate (AdgEntity
*entity
,
97 static void real_render (AdgEntity
*entity
,
101 static guint signals
[LAST_SIGNAL
] = { 0 };
104 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
);
108 adg_entity_class_init(AdgEntityClass
*klass
)
110 GObjectClass
*gobject_class
;
113 GType param_types
[1];
115 gobject_class
= (GObjectClass
*) klass
;
117 g_type_class_add_private(klass
, sizeof(AdgEntityPrivate
));
119 gobject_class
->get_property
= get_property
;
120 gobject_class
->set_property
= set_property
;
121 gobject_class
->dispose
= dispose
;
123 klass
->parent_set
= NULL
;
124 klass
->context_set
= NULL
;
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_object("context",
136 P_("The context associated to this entity or NULL to inherit the parent context"),
137 ADG_TYPE_CONTEXT
, G_PARAM_READWRITE
);
138 g_object_class_install_property(gobject_class
, PROP_CONTEXT
, param
);
140 param
= g_param_spec_boxed("global-map",
142 P_("The transformation to be combined with the parent ones to get the global matrix"),
143 ADG_TYPE_MATRIX
, G_PARAM_READWRITE
);
144 g_object_class_install_property(gobject_class
, PROP_GLOBAL_MAP
, param
);
146 param
= g_param_spec_boxed("local-map",
148 P_("The transformation to be combined with the parent ones to get the local matrix"),
149 ADG_TYPE_MATRIX
, G_PARAM_READWRITE
);
150 g_object_class_install_property(gobject_class
, PROP_LOCAL_MAP
, param
);
153 * AdgEntity::parent-set:
154 * @entity: an #AdgEntity
155 * @old_parent: the old parent
157 * Emitted after the parent entity has changed. The new parent
158 * can be inspected using adg_entity_get_parent().
160 * It is allowed for both old and new parent to be %NULL.
162 signals
[PARENT_SET
] = g_signal_new("parent-set",
163 G_OBJECT_CLASS_TYPE(gobject_class
),
165 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
167 g_cclosure_marshal_VOID__OBJECT
,
168 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
171 * AdgEntity::context-set:
172 * @entity: an #AdgEntity
173 * @old_context: the old context
175 * Emitted after the context has changed.
176 * Emitted after the context has changed. The new context can be
177 * inspected using adg_entity_get_context().
179 * It is allowed for both old and new context to be %NULL.
181 signals
[CONTEXT_SET
] = g_signal_new("context-set",
182 G_OBJECT_CLASS_TYPE(gobject_class
),
184 G_STRUCT_OFFSET(AdgEntityClass
, context_set
),
186 g_cclosure_marshal_VOID__OBJECT
,
187 G_TYPE_NONE
, 1, ADG_TYPE_CONTEXT
);
190 * AdgEntity::invalidate:
191 * @entity: an #AdgEntity
193 * The inverse of the rendering. Usually, invalidation causes the
194 * cache of @entity to be cleared. After a succesful invalidation
195 * the rendered flag is reset: you can access its state using
196 * adg_entity_get_rendered().
198 closure
= g_cclosure_new(G_CALLBACK(real_invalidate
),
199 (gpointer
)0xdeadbeaf, NULL
);
200 signals
[INVALIDATE
] = g_signal_newv("invalidate", ADG_TYPE_ENTITY
,
201 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
202 g_cclosure_marshal_VOID__VOID
,
203 G_TYPE_NONE
, 0, param_types
);
207 * @entity: an #AdgEntity
208 * @cr: a #cairo_t drawing context
210 * Causes the rendering of @entity on @cr. After a succesful rendering
211 * the rendered flag is set: you can access its state using
212 * adg_entity_get_rendered().
214 closure
= g_cclosure_new(G_CALLBACK(real_render
),
215 (gpointer
)0xdeadbeaf, NULL
);
216 param_types
[0] = G_TYPE_POINTER
;
217 signals
[RENDER
] = g_signal_newv("render", ADG_TYPE_ENTITY
,
218 G_SIGNAL_RUN_LAST
, closure
, NULL
, NULL
,
219 g_cclosure_marshal_VOID__POINTER
,
220 G_TYPE_NONE
, 1, param_types
);
224 adg_entity_init(AdgEntity
*entity
)
226 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
231 data
->context
= NULL
;
232 cairo_matrix_init_identity(&data
->local_map
);
233 cairo_matrix_init_identity(&data
->global_map
);
239 dispose(GObject
*object
)
244 entity
= (AdgEntity
*) object
;
245 parent
= adg_entity_get_parent(entity
);
247 /* These calls will emit a "notify" signal both for parent and
248 * context if they are not NULL. Consequentially, the references
249 * to the old parent and context are dropped. */
250 adg_entity_set_parent(entity
, NULL
);
251 adg_entity_set_context(entity
, NULL
);
253 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
254 PARENT_OBJECT_CLASS
->dispose(object
);
259 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
262 AdgEntityPrivate
*data
;
264 entity
= (AdgEntity
*) object
;
269 g_value_set_object(value
, data
->parent
);
272 g_value_set_object(value
, adg_entity_get_context(entity
));
274 case PROP_GLOBAL_MAP
:
275 g_value_set_boxed(value
, &data
->global_map
);
278 g_value_set_boxed(value
, &data
->local_map
);
281 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
287 set_property(GObject
*object
,
288 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
290 AdgEntity
*entity
= (AdgEntity
*) object
;
294 set_parent(entity
, g_value_get_object(value
));
297 set_context(entity
, g_value_get_object(value
));
299 case PROP_GLOBAL_MAP
:
300 set_global_map(entity
, g_value_get_boxed(value
));
303 set_local_map(entity
, g_value_get_boxed(value
));
306 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
313 * adg_entity_get_parent:
314 * @entity: an #AdgEntity
316 * Gets the parent of @entity.
318 * Returns: the parent entity or %NULL on errors or if @entity is a toplevel
321 adg_entity_get_parent(AdgEntity
*entity
)
323 AdgEntityPrivate
*data
;
325 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
333 * adg_entity_set_parent:
334 * @entity: an #AdgEntity
335 * @parent: the parent entity
337 * Sets a new parent on @entity.
339 * This function is only useful in entity implementations.
342 adg_entity_set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
344 g_return_if_fail(ADG_IS_ENTITY(entity
));
346 if (set_parent(entity
, parent
))
347 g_object_notify((GObject
*) entity
, "parent");
351 * adg_entity_context:
352 * @entity: an #AdgEntity instance
354 * Gets the context to be used for @entity. If no context was
355 * explicitely set, it returns the parent context.
357 * Returns: the requested context or %NULL on errors or if @entity
358 * does not have any parent with a context defined
361 adg_entity_context(AdgEntity
*entity
)
365 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
367 context
= adg_entity_get_context(entity
);
369 if (context
== NULL
) {
370 AdgEntity
*parent
= adg_entity_get_parent(entity
);
373 context
= adg_entity_get_context(parent
);
380 * adg_entity_get_context:
381 * @entity: an #AdgEntity instance
383 * Gets the context associated to @entity. This is an accessor method:
384 * if you need to get the context for rendering, use adg_entity_context()
387 * Returns: the context binded to @entity
390 adg_entity_get_context(AdgEntity
*entity
)
392 AdgEntityPrivate
*data
;
394 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
398 return data
->context
;
402 * adg_entity_set_context:
403 * @entity: an #AdgEntity instance
404 * @context: the new context
406 * Sets a new context. The old context (if any) will be unreferenced
407 * while a new reference will be added to @context.
410 adg_entity_set_context(AdgEntity
*entity
, AdgContext
*context
)
412 g_return_if_fail(ADG_IS_ENTITY(entity
));
413 g_return_if_fail(ADG_IS_CONTEXT(context
));
415 if (set_context(entity
, context
))
416 g_object_notify((GObject
*) entity
, "context");
420 * adg_entity_get_canvas:
421 * @entity: an #AdgEntity
423 * Walks on the @entity hierarchy and gets the first parent of @entity that is
424 * of #AdgCanvas derived type.
426 * Returns: the requested canvas or %NULL on errors or if there is
427 * no #AdgCanvas in the @entity hierarchy
430 adg_entity_get_canvas(AdgEntity
*entity
)
432 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
435 if (ADG_IS_CANVAS(entity
))
436 return (AdgCanvas
*) entity
;
438 entity
= (AdgEntity
*) adg_entity_get_parent(entity
);
445 * adg_entity_get_rendered:
446 * @entity: an #AdgEntity object
448 * This function is only useful in entity implementations.
449 * Gets the rendered flag of @entity.
451 * Returns: the current rendered state
454 adg_entity_get_rendered(AdgEntity
*entity
)
456 AdgEntityPrivate
*data
;
458 g_return_val_if_fail(ADG_IS_ENTITY(entity
), FALSE
);
462 return ADG_ISSET(data
->flags
, RENDERED
);
466 * adg_entity_set_rendered:
467 * @entity: an #AdgEntity object
468 * @rendered: new state for the rendered flag
470 * This function is only useful in entity implementations.
471 * Sets the rendered flag of @entity to @rendered.
474 adg_entity_set_rendered(AdgEntity
*entity
, gboolean rendered
)
476 AdgEntityPrivate
*data
;
478 g_return_if_fail(ADG_IS_ENTITY(entity
));
483 ADG_SET(data
->flags
, RENDERED
);
485 ADG_UNSET(data
->flags
, RENDERED
);
489 * adg_entity_get_global_map:
490 * @entity: an #AdgEntity object
491 * @map: where to store the global map
493 * Gets the transformation to be used to compute the global matrix
494 * of @entity and store it in @map.
497 adg_entity_get_global_map(AdgEntity
*entity
, AdgMatrix
*map
)
499 AdgEntityPrivate
*data
;
501 g_return_if_fail(ADG_IS_ENTITY(entity
));
502 g_return_if_fail(map
!= NULL
);
505 adg_matrix_copy(map
, &data
->global_map
);
509 * adg_entity_set_global_map:
510 * @entity: an #AdgEntity object
513 * Sets the new global transformation of @entity to @map:
514 * the old map is discarded. If @map is %NULL an identity
518 adg_entity_set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
520 g_return_if_fail(ADG_IS_ENTITY(entity
));
522 set_global_map(entity
, map
);
523 g_object_notify((GObject
*) entity
, "global-map");
527 * adg_entity_get_local_map:
528 * @entity: an #AdgEntity object
529 * @map: where to store the local map
531 * Gets the transformation to be used to compute the local matrix
532 * of @entity and store it in @map.
535 adg_entity_get_local_map(AdgEntity
*entity
, AdgMatrix
*map
)
537 AdgEntityPrivate
*data
;
539 g_return_if_fail(ADG_IS_ENTITY(entity
));
540 g_return_if_fail(map
!= NULL
);
543 adg_matrix_copy(map
, &data
->local_map
);
547 * adg_entity_set_local_map:
548 * @entity: an #AdgEntity object
551 * Sets the new global transformation of @entity to @map:
552 * the old map is discarded. If @map is %NULL an identity
556 adg_entity_set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
558 g_return_if_fail(ADG_IS_ENTITY(entity
));
560 set_local_map(entity
, map
);
561 g_object_notify((GObject
*) entity
, "local-map");
565 * adg_entity_get_global_matrix:
566 * @entity: an #AdgEntity object
567 * @matrix: where to store the global matrix
569 * Computes the global matrix by combining all the global maps of the
570 * @entity hierarchy and stores the result in @matrix.
573 adg_entity_get_global_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
575 AdgEntityPrivate
*data
;
577 g_return_if_fail(ADG_IS_ENTITY(entity
));
578 g_return_if_fail(matrix
!= NULL
);
582 if (data
->parent
== NULL
) {
583 adg_matrix_copy(matrix
, &data
->global_map
);
585 adg_entity_get_global_matrix((AdgEntity
*) data
->parent
, matrix
);
586 cairo_matrix_multiply(matrix
, &data
->global_map
, matrix
);
591 * adg_entity_get_local_matrix:
592 * @entity: an #AdgEntity object
593 * @matrix: where to store the local matrix
595 * Computes the local matrix by combining all the local maps of the
596 * @entity hierarchy and stores the result in @matrix.
599 adg_entity_get_local_matrix(AdgEntity
*entity
, AdgMatrix
*matrix
)
601 AdgEntityPrivate
*data
;
603 g_return_if_fail(ADG_IS_ENTITY(entity
));
604 g_return_if_fail(matrix
!= NULL
);
608 if (data
->parent
== NULL
) {
609 adg_matrix_copy(matrix
, &data
->local_map
);
611 adg_entity_get_local_matrix((AdgEntity
*) data
->parent
, matrix
);
612 cairo_matrix_multiply(matrix
, &data
->local_map
, matrix
);
617 * adg_entity_get_style:
618 * @entity: an #AdgEntity
619 * @style_slot: the slot of the style to get
621 * Gets a style from this entity. If the entity has no context associated
622 * or the style is undefined within this context, gets the style from its
625 * Returns: the requested style or %NULL on errors or if there is no
626 * context with this style defined in the @entity hierarchy
629 adg_entity_get_style(AdgEntity
*entity
, AdgStyleSlot style_slot
)
631 AdgEntityPrivate
*data
;
633 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
638 AdgStyle
*style
= adg_context_get_style(data
->context
, style_slot
);
644 return adg_entity_get_style((AdgEntity
*) data
->parent
, style_slot
);
651 * @entity: an #AdgEntity
652 * @style_slot: the slot of the style to apply
653 * @cr: a #cairo_t drawing context
655 * Applies the specified style to the @cr cairo context.
658 adg_entity_apply(AdgEntity
*entity
, AdgStyleSlot style_slot
, cairo_t
*cr
)
660 AdgStyle
*style
= adg_entity_get_style(entity
, style_slot
);
663 adg_style_apply(style
, cr
);
667 * adg_entity_apply_font:
668 * @entity: an #AdgEntity
669 * @font_id: a font id
670 * @cr: a #cairo_t drawing context
672 * Applies the specified font to the @cr cairo context. It is similar
673 * to adg_entity_apply() but instead of looking for a font slot, it
674 * searches for a specific font style by inspecting composite styles too.
676 * A typical example is when the tolerance font should be applied: this
677 * is a font style embedded in the dim style, so it is not enough to
678 * use adg_entity_apply().
681 adg_entity_apply_font(AdgEntity
*entity
, AdgFontStyleId font_id
, cairo_t
*cr
)
687 case ADG_FONT_STYLE_TEXT
:
688 style
= adg_entity_get_style(entity
, ADG_SLOT_FONT_STYLE
);
691 case ADG_FONT_STYLE_VALUE
:
692 style
= adg_entity_get_style(entity
, ADG_SLOT_DIM_STYLE
);
693 style
= adg_dim_style_get_value_style((AdgDimStyle
*) style
);
696 case ADG_FONT_STYLE_TOLERANCE
:
697 style
= adg_entity_get_style(entity
, ADG_SLOT_DIM_STYLE
);
698 style
= adg_dim_style_get_tolerance_style((AdgDimStyle
*) style
);
701 case ADG_FONT_STYLE_NOTE
:
702 style
= adg_entity_get_style(entity
, ADG_SLOT_DIM_STYLE
);
703 style
= adg_dim_style_get_note_style((AdgDimStyle
*) style
);
707 g_warning ("%s: invalid font id (%d)", G_STRLOC
, font_id
);
712 adg_style_apply(style
, cr
);
716 * adg_entity_invalidate:
717 * @entity: an #AdgEntity
719 * Emits the "invalidate" signal on @entity and all its children, if any,
720 * so subsequent rendering will need a global recomputation.
723 adg_entity_invalidate(AdgEntity
*entity
)
725 g_return_if_fail(ADG_IS_ENTITY(entity
));
727 g_signal_emit(entity
, signals
[INVALIDATE
], 0, NULL
);
732 * @entity: an #AdgEntity
733 * @cr: a #cairo_t drawing context
735 * Emits the "render" signal on @entity and all its children, if any,
736 * causing the rendering operation the @cr cairo context.
739 adg_entity_render(AdgEntity
*entity
, cairo_t
*cr
)
741 g_return_if_fail(ADG_IS_ENTITY(entity
));
743 g_signal_emit(entity
, signals
[RENDER
], 0, cr
);
748 set_parent(AdgEntity
*entity
, AdgEntity
*parent
)
750 AdgEntityPrivate
*data
;
751 AdgEntity
*old_parent
;
754 old_parent
= data
->parent
;
756 /* Check if parent has changed */
757 if (parent
== old_parent
)
761 g_object_ref(parent
);
763 data
->parent
= parent
;
764 g_signal_emit(entity
, signals
[PARENT_SET
], 0, old_parent
);
766 if (old_parent
!= NULL
)
767 g_object_unref(old_parent
);
773 set_context(AdgEntity
*entity
, AdgContext
*context
)
775 AdgEntityPrivate
*data
;
776 AdgContext
*old_context
;
779 old_context
= data
->context
;
781 /* Check if context has changed */
782 if (context
== old_context
)
786 g_object_ref(context
);
788 data
->context
= context
;
789 g_signal_emit(entity
, signals
[CONTEXT_SET
], 0, old_context
);
791 if (old_context
!= NULL
)
792 g_object_unref(old_context
);
798 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
800 AdgEntityPrivate
*data
= entity
->data
;
803 cairo_matrix_init_identity(&data
->global_map
);
805 adg_matrix_copy(&data
->global_map
, map
);
809 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
811 AdgEntityPrivate
*data
= entity
->data
;
814 cairo_matrix_init_identity(&data
->local_map
);
816 adg_matrix_copy(&data
->local_map
, map
);
820 real_invalidate(AdgEntity
*entity
, gpointer user_data
)
822 AdgEntityClass
*entity_class
;
823 AdgEntityPrivate
*data
;
825 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
827 entity_class
= ADG_ENTITY_GET_CLASS(entity
);
828 if (entity_class
->invalidate
== NULL
) {
829 /* Assume the entity does not have cache to be cleared */
830 } else if (!entity_class
->invalidate(entity
)) {
835 ADG_UNSET(data
->flags
, RENDERED
);
839 real_render(AdgEntity
*entity
, cairo_t
*cr
, gpointer user_data
)
841 AdgEntityClass
*entity_class
;
842 AdgEntityPrivate
*data
;
844 g_assert(user_data
== (gpointer
) 0xdeadbeaf);
846 entity_class
= ADG_ENTITY_GET_CLASS(entity
);
847 if (entity_class
->render
== NULL
) {
848 /* The render method must be defined */
849 g_warning("%s: `render' method not implemented for type `%s'",
850 G_STRLOC
, g_type_name(G_OBJECT_TYPE(entity
)));
857 cairo_transform(cr
, &data
->global_map
);
859 if (entity_class
->render(entity
, cr
))
860 ADG_SET(data
->flags
, RENDERED
);