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 a base interface for all renderable objects
26 * (all the objects that can be printed or viewed).
32 * All fields are private and should not be used directly.
33 * Use its public methods instead.
37 #include "adg-entity.h"
38 #include "adg-entity-private.h"
39 #include "adg-canvas.h"
40 #include "adg-context.h"
61 static void get_property (GObject
*object
,
65 static void set_property (GObject
*object
,
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
,
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
,
84 static void real_render (AdgEntity
*entity
,
88 static guint signals
[LAST_SIGNAL
] = { 0 };
91 G_DEFINE_ABSTRACT_TYPE(AdgEntity
, adg_entity
, G_TYPE_INITIALLY_UNOWNED
);
95 adg_entity_class_init(AdgEntityClass
*klass
)
97 GObjectClass
*gobject_class
;
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",
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",
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",
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
),
151 G_STRUCT_OFFSET(AdgEntityClass
, parent_set
),
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
);
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
);
191 adg_entity_init(AdgEntity
*entity
)
193 AdgEntityPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(entity
,
198 data
->context
= NULL
;
199 cairo_matrix_init_identity(&data
->local_map
);
200 cairo_matrix_init_identity(&data
->global_map
);
206 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
209 AdgEntityPrivate
*data
;
211 entity
= (AdgEntity
*) object
;
216 g_value_set_object(value
, get_parent(entity
));
219 g_value_set_object(value
, get_context(entity
));
221 case PROP_GLOBAL_MAP
:
222 g_value_set_boxed(value
, &data
->global_map
);
225 g_value_set_boxed(value
, &data
->local_map
);
228 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
234 set_property(GObject
*object
,
235 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
237 AdgEntity
*entity
= (AdgEntity
*) object
;
241 set_parent(entity
, g_value_get_object(value
));
244 set_context(entity
, g_value_get_object(value
));
246 case PROP_GLOBAL_MAP
:
247 set_global_map(entity
, g_value_get_boxed(value
));
250 set_local_map(entity
, g_value_get_boxed(value
));
253 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
259 dispose(GObject
*object
)
262 AdgEntityPrivate
*data
;
263 GObjectClass
*object_class
;
265 entity
= (AdgEntity
*) object
;
267 object_class
= (GObjectClass
*) adg_entity_parent_class
;
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
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.
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.
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
)
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.
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
)
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
379 adg_entity_get_context(AdgEntity
*entity
)
381 AdgEntityPrivate
*data
;
383 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
388 return data
->context
;
391 return adg_entity_get_context((AdgEntity
*) data
->parent
);
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.
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.
425 adg_entity_get_canvas(AdgEntity
*entity
)
427 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
430 if (ADG_IS_CANVAS(entity
))
431 return (AdgCanvas
*) entity
;
433 entity
= (AdgEntity
*) adg_entity_get_parent(entity
);
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
449 adg_entity_get_rendered(AdgEntity
*entity
)
451 AdgEntityPrivate
*data
;
453 g_return_val_if_fail(ADG_IS_ENTITY(entity
), FALSE
);
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.
469 adg_entity_set_rendered(AdgEntity
*entity
, gboolean rendered
)
471 AdgEntityPrivate
*data
;
473 g_return_if_fail(ADG_IS_ENTITY(entity
));
478 ADG_SET(data
->flags
, RENDERED
);
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.
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
);
500 adg_matrix_copy(map
, &data
->global_map
);
504 * adg_entity_set_global_map:
505 * @entity: an #AdgEntity object
508 * Sets the new global transformation of @entity to @map:
509 * the old map is discarded. If @map is %NULL an identity
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.
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
);
538 adg_matrix_copy(map
, &data
->local_map
);
542 * adg_entity_set_local_map:
543 * @entity: an #AdgEntity object
546 * Sets the new global transformation of @entity to @map:
547 * the old map is discarded. If @map is %NULL an identity
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.
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
);
577 if (data
->parent
== NULL
) {
578 adg_matrix_copy(matrix
, &data
->global_map
);
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.
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
);
603 if (data
->parent
== NULL
) {
604 adg_matrix_copy(matrix
, &data
->local_map
);
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
620 * Return value: the requested style or %NULL on errors
623 adg_entity_get_style(AdgEntity
*entity
, AdgStyleSlot style_slot
)
625 AdgEntityPrivate
*data
;
627 g_return_val_if_fail(ADG_IS_ENTITY(entity
), NULL
);
632 AdgStyle
*style
= adg_context_get_style(data
->context
, style_slot
);
638 return adg_entity_get_style((AdgEntity
*) data
->parent
, style_slot
);
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.
652 adg_entity_apply(AdgEntity
*entity
, AdgStyleSlot style_slot
, cairo_t
*cr
)
654 AdgStyle
*style
= adg_entity_get_style(entity
, style_slot
);
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.
668 adg_entity_invalidate(AdgEntity
*entity
)
670 g_return_if_fail(ADG_IS_ENTITY(entity
));
672 g_signal_emit(entity
, signals
[INVALIDATE
], 0, NULL
);
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.
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
;
701 set_parent(AdgEntity
*entity
, AdgContainer
*parent
)
703 AdgEntityPrivate
*data
= entity
->data
;
705 data
->parent
= parent
;
709 parent_set(AdgEntity
*entity
, AdgContainer
*old_parent
)
714 get_context(AdgEntity
*entity
)
716 AdgEntityPrivate
*data
= entity
->data
;
720 return data
->context
;
722 parent
= (AdgEntity
*) data
->parent
;
724 return parent
? ADG_ENTITY_GET_CLASS(parent
)->get_context(parent
) : NULL
;
728 set_context(AdgEntity
*entity
, AdgContext
*context
)
730 AdgEntityPrivate
*data
= entity
->data
;
733 g_object_unref((GObject
*) data
->context
);
735 g_object_ref((GObject
*) context
);
736 data
->context
= context
;
740 set_global_map(AdgEntity
*entity
, const AdgMatrix
*map
)
742 AdgEntityPrivate
*data
= entity
->data
;
745 cairo_matrix_init_identity(&data
->global_map
);
747 adg_matrix_copy(&data
->global_map
, map
);
751 set_local_map(AdgEntity
*entity
, const AdgMatrix
*map
)
753 AdgEntityPrivate
*data
= entity
->data
;
756 cairo_matrix_init_identity(&data
->local_map
);
758 adg_matrix_copy(&data
->local_map
, map
);
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
)) {
777 ADG_UNSET(data
->flags
, RENDERED
);
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
)));
799 cairo_transform(cr
, &data
->global_map
);
801 if (entity_class
->render(entity
, cr
))
802 ADG_SET(data
->flags
, RENDERED
);