doc: update copyright line for 2021
[adg.git] / src / adg / adg-entity.c
blobabc8d792129168e45ec31039dace26d2fa4c5540
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2021 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 create a drawing you usually create entities by calling their
28 * specific constructors and add them to a single #AdgCanvas
29 * instance. When cleaning up, you therefore need to destroy only
30 * that canvas instance with adg_entity_destroy(): this in turn
31 * will destroy every contained entity. This is pretty similar to
32 * how the GTK+ world works: you add #GtkWidget instances to a
33 * single #GtkWindow and destroy only that window when finished.
35 * To provide a proper #AdgEntity derived type, you must at least
36 * implement its arrange() and render() virtual methods. Also, if
37 * you are using some sort of caching, ensure to clear it in the
38 * invalidate() method.
40 * Since: 1.0
41 **/
43 /**
44 * AdgEntity:
46 * All fields are private and should not be used directly.
47 * Use its public methods instead.
49 * Since: 1.0
50 **/
52 /**
53 * AdgEntityClass:
54 * @destroy: when a destroy request has been explicitely requested
55 * @parent_set: called whenever the parent of an entity has changed
56 * @global_changed: the global matrix has been invalidated
57 * @local_changed: the local matrix has been invalidated
58 * @invalidate: invalidating callback, used to clear the internal cache
59 * @arrange: prepare the layout and fill the extents struct
60 * @render: rendering callback, it must be implemented by every entity
62 * Any entity (if not abstract) must implement at least the @render method.
63 * The other signal handlers can be overriden to provide custom behaviors
64 * and usually must chain up the original handler.
66 * Since: 1.0
67 **/
69 /**
70 * AdgEntityCallback:
71 * @entity: an #AdgEntity
72 * @user_data: a general purpose pointer
74 * Callback used when inspecting or browsing entities. For example,
75 * it is passed to adg_model_foreach_dependency() to perform an
76 * operation on all the entities depending on an #AdgModel.
78 * Since: 1.0
79 **/
82 #include "adg-internal.h"
83 #if GTK3_ENABLED || GTK2_ENABLED
84 #include <gtk/gtk.h>
85 #endif
87 #include "adg-container.h"
88 #include "adg-table.h"
89 #include "adg-title-block.h"
90 #include <adg-canvas.h>
91 #include "adg-dress.h"
92 #include "adg-style.h"
93 #include "adg-model.h"
94 #include "adg-point.h"
95 #include "adg-cairo-fallback.h"
97 #include "adg-entity-private.h"
100 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_entity_parent_class)
103 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(AdgEntity, adg_entity, G_TYPE_INITIALLY_UNOWNED)
105 enum {
106 PROP_0,
107 PROP_FLOATING,
108 PROP_PARENT,
109 PROP_GLOBAL_MAP,
110 PROP_LOCAL_MAP,
111 PROP_LOCAL_MIX
114 enum {
115 DESTROY,
116 PARENT_SET,
117 GLOBAL_CHANGED,
118 LOCAL_CHANGED,
119 INVALIDATE,
120 ARRANGE,
121 RENDER,
122 LAST_SIGNAL
126 static void _adg_dispose (GObject *object);
127 static void _adg_get_property (GObject *object,
128 guint prop_id,
129 GValue *value,
130 GParamSpec *pspec);
131 static void _adg_set_property (GObject *object,
132 guint prop_id,
133 const GValue *value,
134 GParamSpec *pspec);
135 static void _adg_destroy (AdgEntity *entity);
136 static void _adg_set_parent (AdgEntity *entity,
137 AdgEntity *parent);
138 static void _adg_global_changed (AdgEntity *entity);
139 static void _adg_local_changed (AdgEntity *entity);
140 static void _adg_real_invalidate (AdgEntity *entity);
141 static void _adg_real_arrange (AdgEntity *entity);
142 static void _adg_real_render (AdgEntity *entity,
143 cairo_t *cr);
144 static guint _adg_signals[LAST_SIGNAL] = { 0 };
145 static gboolean _adg_show_extents = FALSE;
148 static void
149 adg_entity_class_init(AdgEntityClass *klass)
151 GObjectClass *gobject_class;
152 GParamSpec *param;
153 GClosure *closure;
154 GType param_types[1];
156 gobject_class = (GObjectClass *) klass;
158 gobject_class->dispose = _adg_dispose;
159 gobject_class->get_property = _adg_get_property;
160 gobject_class->set_property = _adg_set_property;
162 klass->destroy = _adg_destroy;
163 klass->parent_set = NULL;
164 klass->global_changed = _adg_global_changed;
165 klass->local_changed = _adg_local_changed;
166 klass->invalidate = NULL;
167 klass->arrange= NULL;
168 klass->render = NULL;
170 param = g_param_spec_boolean("floating",
171 P_("Floating Entity"),
172 P_("Flag that includes (FALSE) or excludes (TRUE) this entity from the computation of the parent entity extents"),
173 FALSE, G_PARAM_READWRITE);
174 g_object_class_install_property(gobject_class, PROP_FLOATING, param);
176 param = g_param_spec_object("parent",
177 P_("Parent Entity"),
178 P_("The parent entity of this entity or NULL if this is a top-level entity"),
179 ADG_TYPE_ENTITY,
180 G_PARAM_READWRITE);
181 g_object_class_install_property(gobject_class, PROP_PARENT, param);
183 param = g_param_spec_boxed("global-map",
184 P_("Global Map"),
185 P_("The transformation to be combined with the parent ones to get the global matrix"),
186 CAIRO_GOBJECT_TYPE_MATRIX,
187 G_PARAM_READWRITE);
188 g_object_class_install_property(gobject_class, PROP_GLOBAL_MAP, param);
190 param = g_param_spec_boxed("local-map",
191 P_("Local Map"),
192 P_("The local transformation that could be used to compute the local matrix in the way specified by the #AdgEntity:local-mix property"),
193 CAIRO_GOBJECT_TYPE_MATRIX,
194 G_PARAM_READWRITE);
195 g_object_class_install_property(gobject_class, PROP_LOCAL_MAP, param);
197 param = g_param_spec_enum("local-mix",
198 P_("Local Mix Method"),
199 P_("Define how the local maps of the entity and its ancestors should be combined to get the local matrix"),
200 ADG_TYPE_MIX, ADG_MIX_ANCESTORS,
201 G_PARAM_READWRITE);
202 g_object_class_install_property(gobject_class, PROP_LOCAL_MIX, param);
205 * AdgEntity::destroy:
206 * @entity: an #AdgEntity
208 * Emitted to explicitely destroy @entity. It unreferences
209 * @entity so that will be destroyed, unless the caller owns
210 * an additional references added with g_object_ref().
212 * In the usual case, this is equivalent of calling
213 * g_object_unref() on @entity but, for composite entities or
214 * containers, the destroy signal is propagated to the children.
216 * Since: 1.0
218 _adg_signals[DESTROY] =
219 g_signal_new("destroy",
220 G_OBJECT_CLASS_TYPE(gobject_class),
221 G_SIGNAL_RUN_FIRST,
222 G_STRUCT_OFFSET(AdgEntityClass, destroy),
223 NULL, NULL,
224 g_cclosure_marshal_VOID__VOID,
225 G_TYPE_NONE, 0);
228 * AdgEntity::parent-set:
229 * @entity: an #AdgEntity
230 * @old_parent: the old parent
232 * Emitted after the parent entity has changed. The new parent
233 * can be inspected using adg_entity_get_parent().
235 * It is allowed for both old and new parent to
236 * be <constant>NULL</constant>.
238 * Since: 1.0
240 _adg_signals[PARENT_SET] =
241 g_signal_new("parent-set",
242 G_OBJECT_CLASS_TYPE(gobject_class),
243 G_SIGNAL_RUN_FIRST,
244 G_STRUCT_OFFSET(AdgEntityClass, parent_set),
245 NULL, NULL,
246 g_cclosure_marshal_VOID__OBJECT,
247 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
250 * AdgEntity::global-changed:
251 * @entity: an #AdgEntity
253 * Emitted when the global map of @entity or any of its parent
254 * has changed. The default handler will compute the new global
255 * matrix, updating the internal cache.
257 * This signal has lazy emission, i.e. it is emitted only when
258 * the global matrix is requested, typically in the arrange phase.
260 * Since: 1.0
262 _adg_signals[GLOBAL_CHANGED] =
263 g_signal_new("global-changed",
264 G_OBJECT_CLASS_TYPE(gobject_class),
265 G_SIGNAL_RUN_FIRST,
266 G_STRUCT_OFFSET(AdgEntityClass, global_changed),
267 NULL, NULL,
268 g_cclosure_marshal_VOID__VOID,
269 G_TYPE_NONE, 0);
272 * AdgEntity::local-changed:
273 * @entity: an #AdgEntity
275 * Emitted when the local map of @entity or any of its parent
276 * has changed. The default handler will compute the new local
277 * matrix, updating the internal cache.
279 * This signal has lazy emission, i.e. it is emitted only when
280 * the local matrix is requested, typically in the arrange phase.
282 * Since: 1.0
284 _adg_signals[LOCAL_CHANGED] =
285 g_signal_new("local-changed",
286 G_OBJECT_CLASS_TYPE(gobject_class),
287 G_SIGNAL_RUN_FIRST,
288 G_STRUCT_OFFSET(AdgEntityClass, local_changed),
289 NULL, NULL,
290 g_cclosure_marshal_VOID__VOID,
291 G_TYPE_NONE, 0);
294 * AdgEntity::invalidate:
295 * @entity: an #AdgEntity
297 * Invalidates the whole @entity, that is resets all the cache
298 * (if present) built during the #AdgEntity::arrange signal.
299 * The resulting state is a clean entity, similar to what you
300 * have just before the first rendering.
302 * Since: 1.0
304 closure = g_cclosure_new(G_CALLBACK(_adg_real_invalidate), NULL, NULL);
305 _adg_signals[INVALIDATE] =
306 g_signal_newv("invalidate", ADG_TYPE_ENTITY,
307 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
308 g_cclosure_marshal_VOID__VOID,
309 G_TYPE_NONE, 0, param_types);
312 * AdgEntity::arrange:
313 * @entity: an #AdgEntity
315 * Arranges the layout of @entity, updating the cache if necessary,
316 * and computes the extents of @entity.
318 * Since: 1.0
320 closure = g_cclosure_new(G_CALLBACK(_adg_real_arrange), NULL, NULL);
321 _adg_signals[ARRANGE] =
322 g_signal_newv("arrange", ADG_TYPE_ENTITY,
323 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
324 g_cclosure_marshal_VOID__VOID,
325 G_TYPE_NONE, 0, param_types);
328 * AdgEntity::render:
329 * @entity: an #AdgEntity
330 * @cr: a #cairo_t drawing context
332 * Causes the rendering of @entity on @cr. A render signal will
333 * automatically emit #AdgEntity::arrange just before the real
334 * rendering on the cairo context.
336 * Since: 1.0
338 closure = g_cclosure_new(G_CALLBACK(_adg_real_render), NULL, NULL);
339 param_types[0] = G_TYPE_POINTER;
340 _adg_signals[RENDER] =
341 g_signal_newv("render", ADG_TYPE_ENTITY,
342 G_SIGNAL_RUN_LAST, closure, NULL, NULL,
343 g_cclosure_marshal_VOID__POINTER,
344 G_TYPE_NONE, 1, param_types);
347 static void
348 adg_entity_init(AdgEntity *entity)
350 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
351 data->floating = FALSE;
352 data->parent = NULL;
353 cairo_matrix_init_identity(&data->global_map);
354 cairo_matrix_init_identity(&data->local_map);
355 data->local_mix = ADG_MIX_ANCESTORS;
356 data->hash_styles = NULL;
357 data->global.is_defined = FALSE;
358 adg_matrix_copy(&data->global.matrix, adg_matrix_null());
359 data->local.is_defined = FALSE;
360 adg_matrix_copy(&data->local.matrix, adg_matrix_null());
361 data->extents.is_defined = FALSE;
364 static void
365 _adg_dispose(GObject *object)
367 AdgEntity *entity = (AdgEntity *) object;
368 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
370 /* This call will emit a "notify" signal for parent.
371 * Consequentially, the references to the old parent is dropped. */
372 adg_entity_set_parent(entity, NULL);
374 if (data->hash_styles != NULL) {
375 g_hash_table_destroy(data->hash_styles);
376 data->hash_styles = NULL;
379 if (_ADG_OLD_OBJECT_CLASS->dispose)
380 _ADG_OLD_OBJECT_CLASS->dispose(object);
383 static void
384 _adg_get_property(GObject *object, guint prop_id,
385 GValue *value, GParamSpec *pspec)
387 AdgEntity *entity = (AdgEntity *) object;
388 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
390 switch (prop_id) {
391 case PROP_FLOATING:
392 g_value_set_boolean(value, data->floating);
393 break;
394 case PROP_PARENT:
395 g_value_set_object(value, data->parent);
396 break;
397 case PROP_GLOBAL_MAP:
398 g_value_set_boxed(value, &data->global_map);
399 break;
400 case PROP_LOCAL_MAP:
401 g_value_set_boxed(value, &data->local_map);
402 break;
403 case PROP_LOCAL_MIX:
404 g_value_set_enum(value, data->local_mix);
405 break;
406 default:
407 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
408 break;
412 static void
413 _adg_set_property(GObject *object, guint prop_id,
414 const GValue *value, GParamSpec *pspec)
416 AdgEntityPrivate *data = adg_entity_get_instance_private((AdgEntity *) object);
418 switch (prop_id) {
419 case PROP_FLOATING:
420 data->floating = g_value_get_boolean(value);
421 break;
422 case PROP_PARENT:
423 _adg_set_parent((AdgEntity *) object,
424 (AdgEntity *) g_value_get_object(value));
425 break;
426 case PROP_GLOBAL_MAP:
427 adg_matrix_copy(&data->global_map, g_value_get_boxed(value));
428 data->global.is_defined = FALSE;
429 break;
430 case PROP_LOCAL_MAP:
431 adg_matrix_copy(&data->local_map, g_value_get_boxed(value));
432 data->local.is_defined = FALSE;
433 break;
434 case PROP_LOCAL_MIX:
435 data->local_mix = g_value_get_enum(value);
436 data->local.is_defined = FALSE;
437 break;
438 default:
439 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
440 break;
446 * adg_switch_extents:
447 * @state: new extents state
449 * Strokes (if @state is <constant>TRUE</constant>) a rectangle
450 * around every entity to show their extents. Useful for
451 * debugging purposes.
453 * Since: 1.0
455 void
456 adg_switch_extents(gboolean state)
458 _adg_show_extents = state;
462 * adg_entity_destroy:
463 * @entity: an #AdgEntity
465 * Emits the #AdgEntity::destroy signal on @entity and on all of
466 * its children, if any.
468 * Since: 1.0
470 void
471 adg_entity_destroy(AdgEntity *entity)
473 g_return_if_fail(ADG_IS_ENTITY(entity));
475 g_signal_emit(entity, _adg_signals[DESTROY], 0);
479 * adg_entity_switch_floating:
480 * @entity: an #AdgEntity
481 * @new_state: the new floating state
483 * Sets or resets the floating state of @entity.
485 * By default all entities are not floating. When an entity is "floating", its
486 * extents does not concur on increasing the extents of its own container. In
487 * other words, during the arrange phase #AdgContainer only considers the
488 * non-floating children to compute its extents. In particular, this affects
489 * how adg_canvas_autoscale() works: all floating entities are not taken into
490 * consideration.
492 * A typical example is the title block or any other annotation not dependent
493 * from the model for positioning.
495 * Since: 1.0
497 void
498 adg_entity_switch_floating(AdgEntity *entity, gboolean new_state)
500 AdgEntityPrivate *data;
502 g_return_if_fail(ADG_IS_ENTITY(entity));
503 g_return_if_fail(adg_is_boolean_value(new_state));
505 data = adg_entity_get_instance_private(entity);
506 data->floating = new_state;
510 * adg_entity_has_floating:
511 * @entity: an #AdgEntity
513 * Checks if @entity has the floating state enabled.
515 * See adg_entity_switch_floating() for a description of what the floating
516 * state is.
518 * Returns: the current state of the floating flag.
520 * Since: 1.0
522 gboolean
523 adg_entity_has_floating(AdgEntity *entity)
525 AdgEntityPrivate *data;
527 g_return_val_if_fail(ADG_IS_ENTITY(entity), FALSE);
529 data = adg_entity_get_instance_private(entity);
530 return data->floating;
534 * adg_entity_get_canvas:
535 * @entity: an #AdgEntity
537 * Walks on the @entity hierarchy and gets the first parent of @entity,
538 * that is the first #AdgCanvas instance. The returned object is
539 * owned by @entity and should not be freed or modified.
541 * Returns: (transfer none): the requested canvas or <constant>NULL</constant> on errors or if there is no #AdgCanvas in the @entity hierarchy.
543 * Since: 1.0
545 AdgCanvas *
546 adg_entity_get_canvas(AdgEntity *entity)
548 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
550 while (entity) {
551 if (ADG_IS_CANVAS(entity))
552 return (AdgCanvas *) entity;
554 entity = adg_entity_get_parent(entity);
557 return NULL;
561 * adg_entity_set_parent:
562 * @entity: an #AdgEntity
563 * @parent: the parent entity
565 * <note><para>
566 * This function is only useful in entity implementations.
567 * </para></note>
569 * Sets a new parent on @entity. Changing the @parent of an entity
570 * emits the #AdgEntity::parent-set signal on it.
572 * There is no reference management at this level: they should be
573 * handled at a higher level, e.g. by #AdgContainer.
575 * Since: 1.0
577 void
578 adg_entity_set_parent(AdgEntity *entity, AdgEntity *parent)
580 g_return_if_fail(ADG_IS_ENTITY(entity));
581 g_object_set(entity, "parent", parent, NULL);
585 * adg_entity_get_parent:
586 * @entity: an #AdgEntity
588 * Gets the parent of @entity. The returned object is owned
589 * by @entity and should not be freed or modified.
591 * Returns: (transfer none): the parent entity or <constant>NULL</constant> on errors or if @entity is a toplevel.
593 * Since: 1.0
595 AdgEntity *
596 adg_entity_get_parent(AdgEntity *entity)
598 AdgEntityPrivate *data;
600 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
602 data = adg_entity_get_instance_private(entity);
603 return data->parent;
607 * adg_entity_set_global_map:
608 * @entity: an #AdgEntity object
609 * @map: the new map
611 * Sets the new global transformation of @entity to @map:
612 * the old map is discarded. If @map is <constant>NULL</constant>,
613 * the global map is left unchanged.
615 * Since: 1.0
617 void
618 adg_entity_set_global_map(AdgEntity *entity, const cairo_matrix_t *map)
620 g_return_if_fail(ADG_IS_ENTITY(entity));
621 g_object_set(entity, "global-map", map, NULL);
625 * adg_entity_transform_global_map:
626 * @entity: an #AdgEntity object
627 * @transformation: the transformation to apply
628 * @mode: how @transformation should be applied
630 * Convenient function to change the global map of @entity by
631 * applying @tranformation using the @mode operator. This is
632 * logically equivalent to the following:
634 * <informalexample><programlisting language="C">
635 * cairo_matrix_t map;
636 * adg_matrix_copy(&map, adg_entity_get_global_map(entity));
637 * adg_matrix_transform(&map, transformation, mode);
638 * adg_entity_set_global_map(entity, &map);
639 * </programlisting></informalexample>
641 * Since: 1.0
643 void
644 adg_entity_transform_global_map(AdgEntity *entity,
645 const cairo_matrix_t *transformation,
646 AdgTransformMode mode)
648 AdgEntityPrivate *data;
649 cairo_matrix_t map;
651 g_return_if_fail(ADG_IS_ENTITY(entity));
652 g_return_if_fail(transformation != NULL);
654 data = adg_entity_get_instance_private(entity);
656 adg_matrix_copy(&map, &data->global_map);
657 adg_matrix_transform(&map, transformation, mode);
659 g_object_set(entity, "global-map", &map, NULL);
663 * adg_entity_get_global_map:
664 * @entity: an #AdgEntity object
666 * Gets the transformation to be used to compute the global matrix
667 * of @entity.
669 * Returns: the requested map or <constant>NULL</constant> on errors.
671 * Since: 1.0
673 const cairo_matrix_t *
674 adg_entity_get_global_map(AdgEntity *entity)
676 AdgEntityPrivate *data;
678 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
680 data = adg_entity_get_instance_private(entity);
681 return &data->global_map;
685 * adg_entity_get_global_matrix:
686 * @entity: an #AdgEntity object
688 * Gets the current global matrix of @entity. The returned value
689 * is owned by @entity and should not be changed or freed.
691 * The global matrix is computed in the arrange() phase by
692 * combining all the global maps of the @entity hierarchy using
693 * the %ADG_MIX_ANCESTORS method.
695 * Returns: the global matrix or <constant>NULL</constant> on errors.
697 * Since: 1.0
699 const cairo_matrix_t *
700 adg_entity_get_global_matrix(AdgEntity *entity)
702 AdgEntityPrivate *data;
704 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
706 data = adg_entity_get_instance_private(entity);
707 return &data->global.matrix;
711 * adg_entity_set_local_map:
712 * @entity: an #AdgEntity object
713 * @map: the new map
715 * Sets the new local transformation of @entity to @map:
716 * the old map is discarded. If @map is <constant>NULL</constant>,
717 * the local map is left unchanged.
719 * Since: 1.0
721 void
722 adg_entity_set_local_map(AdgEntity *entity, const cairo_matrix_t *map)
724 g_return_if_fail(ADG_IS_ENTITY(entity));
725 g_object_set(entity, "local-map", map, NULL);
729 * adg_entity_transform_local_map:
730 * @entity: an #AdgEntity object
731 * @transformation: the transformation to apply
732 * @mode: how @transformation should be applied
734 * Convenient function to change the local map of @entity by
735 * applying @tranformation using the @mode operator. This is
736 * logically equivalent to the following:
738 * <informalexample><programlisting language="C">
739 * cairo_matrix_t map;
740 * adg_matrix_copy(&map, adg_entity_get_local_map(entity));
741 * adg_matrix_transform(&map, transformation, mode);
742 * adg_entity_set_local_map(entity, &map);
743 * </programlisting></informalexample>
745 * Since: 1.0
747 void
748 adg_entity_transform_local_map(AdgEntity *entity,
749 const cairo_matrix_t *transformation,
750 AdgTransformMode mode)
752 AdgEntityPrivate *data;
753 cairo_matrix_t map;
755 g_return_if_fail(ADG_IS_ENTITY(entity));
756 g_return_if_fail(transformation != NULL);
758 data = adg_entity_get_instance_private(entity);
760 adg_matrix_copy(&map, &data->local_map);
761 adg_matrix_transform(&map, transformation, mode);
762 g_object_set(entity, "local-map", &map, NULL);
766 * adg_entity_get_local_map:
767 * @entity: an #AdgEntity object
769 * Gets the transformation to be used to compute the local matrix
770 * of @entity and store it in @map.
772 * Returns: the requested map or <constant>NULL</constant> on errors.
774 * Since: 1.0
776 const cairo_matrix_t *
777 adg_entity_get_local_map(AdgEntity *entity)
779 AdgEntityPrivate *data;
781 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
783 data = adg_entity_get_instance_private(entity);
784 return &data->local_map;
788 * adg_entity_get_local_matrix:
789 * @entity: an #AdgEntity object
791 * Gets the current local matrix of @entity. The returned value
792 * is owned by @entity and should not be changed or freed.
794 * The local matrix is computed in the arrange() phase by
795 * combining all the local maps of the @entity hierarchy using
796 * the method specified by the #AdgEntity:local-mix property.
798 * Returns: the local matrix or <constant>NULL</constant> on errors.
800 * Since: 1.0
802 const cairo_matrix_t *
803 adg_entity_get_local_matrix(AdgEntity *entity)
805 AdgEntityPrivate *data;
807 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
809 data = adg_entity_get_instance_private(entity);
810 return &data->local.matrix;
814 * adg_entity_set_local_mix:
815 * @entity: an #AdgEntity object
816 * @local_mix: new mix method
818 * Sets a new local mix method on @entity. The
819 * #AdgEntity:local-mix property defines how the local
820 * matrix must be computed: check out the #AdgMix
821 * documentation to know what are the availables methods
822 * and how they affect the local matrix computation.
824 * Setting a different local mix method emits an
825 * #AdgEntity::local-changed signal on @entity.
827 * Since: 1.0
829 void
830 adg_entity_set_local_mix(AdgEntity *entity, AdgMix local_mix)
832 g_return_if_fail(ADG_IS_ENTITY(entity));
833 g_object_set(entity, "local-mix", local_mix, NULL);
837 * adg_entity_get_local_mix:
838 * @entity: an #AdgEntity object
840 * Gets the local mix method of @entity. Check out the
841 * adg_entity_set_local_mix() documentation to know what the
842 * local mix method is used for.
844 * Returns: the local mix method of @entity or %ADG_MIX_UNDEFINED on errors
846 * Since: 1.0
848 AdgMix
849 adg_entity_get_local_mix(AdgEntity *entity)
851 AdgEntityPrivate *data;
853 g_return_val_if_fail(ADG_IS_ENTITY(entity), ADG_MIX_UNDEFINED);
855 data = adg_entity_get_instance_private(entity);
856 return data->local_mix;
860 * adg_entity_set_extents:
861 * @entity: an #AdgEntity
862 * @extents: the new extents
864 * <note><para>
865 * This function is only useful in entity implementations.
866 * </para></note>
868 * Sets a new bounding box for @entity. @extents can
869 * be <constant>NULL</constant>, in which case the extents are unset.
871 * Since: 1.0
873 void
874 adg_entity_set_extents(AdgEntity *entity, const CpmlExtents *extents)
876 AdgEntityPrivate *data;
878 g_return_if_fail(ADG_IS_ENTITY(entity));
880 data = adg_entity_get_instance_private(entity);
882 if (extents == NULL)
883 data->extents.is_defined = FALSE;
884 else
885 cpml_extents_copy(&data->extents, extents);
889 * adg_entity_get_extents:
890 * @entity: an #AdgEntity
892 * Gets the bounding box of @entity. The returned struct is
893 * owned by @entity and should not modified or freed.
895 * This struct specifies the surface portion (in global space
896 * of @entity) occupied by the entity without taking into
897 * account rendering properties such as line thickness or caps.
899 * The #AdgEntity::arrange signal should be emitted before
900 * this call (either explicitely trought adg_entity_arrange()
901 * or implicitely with adg_entity_render()) in order to get
902 * an up to date boundary box.
904 * Returns: the bounding box of @entity or <constant>NULL</constant> on errors.
906 * Since: 1.0
908 const CpmlExtents *
909 adg_entity_get_extents(AdgEntity *entity)
911 AdgEntityPrivate *data;
913 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
915 data = adg_entity_get_instance_private(entity);
916 return &data->extents;
920 * adg_entity_set_style:
921 * @entity: an #AdgEntity
922 * @dress: a dress style
923 * @style: the new style to use
925 * Overrides the style of @dress for @entity and its children.
926 * If @style is <constant>NULL</constant>, any previous
927 * override is removed.
929 * The new style must still be compatible with @dress: check out
930 * the adg_dress_style_is_compatible() documentation to know
931 * what a compatible style means.
933 * Since: 1.0
935 void
936 adg_entity_set_style(AdgEntity *entity, AdgDress dress, AdgStyle *style)
938 AdgEntityPrivate *data;
939 gpointer p_dress;
940 AdgStyle *old_style;
942 g_return_if_fail(ADG_IS_ENTITY(entity));
944 data = adg_entity_get_instance_private(entity);
946 if (data->hash_styles == NULL && style == NULL)
947 return;
949 if (data->hash_styles == NULL)
950 data->hash_styles = g_hash_table_new_full(NULL, NULL,
951 NULL, g_object_unref);
953 p_dress = GINT_TO_POINTER(dress);
954 old_style = g_hash_table_lookup(data->hash_styles, p_dress);
956 if (style == old_style)
957 return;
959 if (style == NULL) {
960 g_hash_table_remove(data->hash_styles, p_dress);
961 return;
964 if (!adg_dress_style_is_compatible(dress, style)) {
965 GType ancestor_type = adg_dress_get_ancestor_type(dress);
967 g_warning(_("%s: '%s' is not compatible with '%s' for '%s' dress (%d)"),
968 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
969 g_type_name(ancestor_type), adg_dress_get_name(dress), dress);
971 return;
974 g_object_ref(style);
975 g_hash_table_replace(data->hash_styles, p_dress, style);
979 * adg_entity_get_style:
980 * @entity: an #AdgEntity
981 * @dress: the dress of the style to get
983 * Gets the overriden @dress style from @entity. This is a kind
984 * of accessor function: for rendering purpose use adg_entity_style()
985 * instead. The returned object is owned by @entity and should not be
986 * freed or modified.
988 * Returns: (transfer none): the requested style or <constant>NULL</constant> if the @dress style is not overriden.
990 * Since: 1.0
992 AdgStyle *
993 adg_entity_get_style(AdgEntity *entity, AdgDress dress)
995 AdgEntityPrivate *data;
997 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
999 data = adg_entity_get_instance_private(entity);
1001 if (data->hash_styles == NULL)
1002 return NULL;
1004 return g_hash_table_lookup(data->hash_styles, GINT_TO_POINTER(dress));
1008 * adg_entity_style:
1009 * @entity: an #AdgEntity
1010 * @dress: the dress of the style to get
1012 * Gets the style to be used for @entity. @dress specifies which
1013 * "family" of style to get.
1015 * The following sequence of checks is performed to get the proper
1016 * style, stopping at the first succesfull result:
1018 * <orderedlist>
1019 * <listitem>check if the style is directly overriden by this entity,
1020 * as returned by adg_entity_get_style();</listitem>
1021 * <listitem>check if @entity has a parent, in which case returns the
1022 * adg_entity_style() of the parent;</listitem>
1023 * <listitem>returns the main style with adg_dress_get_fallback().</listitem>
1024 * </orderedlist>
1026 * The returned object is owned by @entity and should not be
1027 * freed or modified.
1029 * Returns: (transfer none): the requested style or <constant>NULL</constant> for transparent dresses or errors.
1031 * Since: 1.0
1033 AdgStyle *
1034 adg_entity_style(AdgEntity *entity, AdgDress dress)
1036 AdgStyle *style;
1038 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
1040 style = adg_entity_get_style(entity, dress);
1042 if (style == NULL) {
1043 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
1045 if (data->parent != NULL)
1046 style = adg_entity_style(data->parent, dress);
1047 else
1048 style = adg_dress_get_fallback(dress);
1051 return style;
1055 * adg_entity_apply_dress:
1056 * @entity: an #AdgEntity
1057 * @dress: the dress style to apply
1058 * @cr: a #cairo_t drawing context
1060 * Convenient function to apply a @dress style (as returned by
1061 * adg_entity_style()) to the @cr cairo context.
1063 * Since: 1.0
1065 void
1066 adg_entity_apply_dress(AdgEntity *entity, AdgDress dress, cairo_t *cr)
1068 AdgStyle *style;
1070 g_return_if_fail(ADG_IS_ENTITY(entity));
1071 g_return_if_fail(cr != NULL);
1073 style = adg_entity_style(entity, dress);
1075 if (style != NULL)
1076 adg_style_apply(style, entity, cr);
1080 * adg_entity_global_changed:
1081 * @entity: an #AdgEntity
1083 * Emits the #AdgEntity::global-changed signal on @entity and on all of
1084 * its children, if any.
1086 * Since: 1.0
1088 void
1089 adg_entity_global_changed(AdgEntity *entity)
1091 g_return_if_fail(ADG_IS_ENTITY(entity));
1093 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1097 * adg_entity_local_changed:
1098 * @entity: an #AdgEntity
1100 * Emits the #AdgEntity::local-changed signal on @entity and on all of
1101 * its children, if any.
1103 * Since: 1.0
1105 void
1106 adg_entity_local_changed(AdgEntity *entity)
1108 g_return_if_fail(ADG_IS_ENTITY(entity));
1110 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1114 * adg_entity_invalidate:
1115 * @entity: an #AdgEntity
1117 * Emits the #AdgEntity::invalidate signal on @entity and on all of
1118 * its children, if any, clearing the eventual cache stored by the
1119 * #AdgEntity::arrange signal and setting the entity state similary
1120 * to the just initialized entity.
1122 * Since: 1.0
1124 void
1125 adg_entity_invalidate(AdgEntity *entity)
1127 g_return_if_fail(ADG_IS_ENTITY(entity));
1129 g_signal_emit(entity, _adg_signals[INVALIDATE], 0);
1133 * adg_entity_arrange:
1134 * @entity: an #AdgEntity
1136 * Emits the #AdgEntity::arrange signal on @entity and all its children,
1137 * if any. The arrange call is implicitely called by the
1138 * #AdgEntity::render signal but not by adg_entity_get_extents().
1140 * Since: 1.0
1142 void
1143 adg_entity_arrange(AdgEntity *entity)
1145 g_return_if_fail(ADG_IS_ENTITY(entity));
1147 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1151 * adg_entity_render:
1152 * @entity: an #AdgEntity
1153 * @cr: a #cairo_t drawing context
1155 * Emits the #AdgEntity::render signal on @entity and on all of its
1156 * children, if any, causing the rendering to the @cr cairo context.
1158 * Since: 1.0
1160 void
1161 adg_entity_render(AdgEntity *entity, cairo_t *cr)
1163 g_return_if_fail(ADG_IS_ENTITY(entity));
1165 g_signal_emit(entity, _adg_signals[RENDER], 0, cr);
1169 * adg_entity_point:
1170 * @entity: an #AdgEntity
1171 * @point: the #AdgPoint to define
1172 * @new_point: the new #AdgPoint value
1174 * <note><para>
1175 * This function is only useful in entity implementations.
1176 * </para></note>
1178 * A convenient method to set an #AdgPoint owned by @entity.
1179 * @old_point is the old value while @new_point is the new value.
1180 * It can be used for changing a private #AdgPoint struct, such as:
1182 * <informalexample><programlisting language="C">
1183 * data->point = adg_entity_point(entity, data->point, new_point);
1184 * </programlisting></informalexample>
1186 * This function takes care of the dependencies between @entity and
1187 * the eventual models bound to the old and new points.
1189 * @old_point can be <constant>NULL</constant>, in which case a
1190 * clone of @new_point will be returned. Also @new_point can
1191 * be <constant>NULL</constant>, in which case @old_point
1192 * is destroyed and <constant>NULL</constant> will be returned.
1194 * Returns: (transfer full): the new properly defined point
1196 * Since: 1.0
1198 AdgPoint *
1199 adg_entity_point(AdgEntity *entity, AdgPoint *old_point, const AdgPoint *new_point)
1201 AdgPoint *point;
1203 g_return_val_if_fail(ADG_IS_ENTITY(entity), NULL);
1205 point = NULL;
1207 if (! adg_point_equal(old_point, new_point)) {
1208 AdgModel *old_model, *new_model;
1210 old_model = old_point != NULL ? adg_point_get_model(old_point) : NULL;
1211 new_model = new_point != NULL ? adg_point_get_model(new_point) : NULL;
1213 if (new_model != old_model) {
1214 /* Handle model-entity dependencies */
1215 if (new_model != NULL)
1216 adg_model_add_dependency(new_model, entity);
1217 if (old_model != NULL)
1218 adg_model_remove_dependency(old_model, entity);
1221 if (new_point != NULL)
1222 point = adg_point_dup(new_point);
1223 if (old_point != NULL)
1224 adg_point_destroy(old_point);
1227 return point;
1231 static void
1232 _adg_destroy(AdgEntity *entity)
1234 GObject *object = (GObject *) entity;
1236 g_object_run_dispose(object);
1237 g_object_unref(object);
1240 static void
1241 _adg_set_parent(AdgEntity *entity, AdgEntity *parent)
1243 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
1244 AdgEntity *old_parent = data->parent;
1246 data->parent = parent;
1247 data->global.is_defined = FALSE;
1248 data->local.is_defined = FALSE;
1250 g_signal_emit(entity, _adg_signals[PARENT_SET], 0, old_parent);
1253 static void
1254 _adg_global_changed(AdgEntity *entity)
1256 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
1257 const cairo_matrix_t *map = &data->global_map;
1258 cairo_matrix_t *matrix = &data->global.matrix;
1260 if (data->parent) {
1261 adg_matrix_copy(matrix, adg_entity_get_global_matrix(data->parent));
1262 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1263 } else {
1264 adg_matrix_copy(matrix, map);
1268 static void
1269 _adg_local_changed(AdgEntity *entity)
1271 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
1272 const cairo_matrix_t *map = &data->local_map;
1273 cairo_matrix_t *matrix = &data->local.matrix;
1275 switch (data->local_mix) {
1276 case ADG_MIX_DISABLED:
1277 adg_matrix_copy(matrix, adg_matrix_identity());
1278 break;
1279 case ADG_MIX_NONE:
1280 adg_matrix_copy(matrix, map);
1281 break;
1282 case ADG_MIX_ANCESTORS:
1283 if (data->parent) {
1284 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1285 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1286 } else {
1287 adg_matrix_copy(matrix, map);
1289 break;
1290 case ADG_MIX_ANCESTORS_NORMALIZED:
1291 if (data->parent) {
1292 adg_matrix_copy(matrix, adg_entity_get_local_matrix(data->parent));
1293 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1294 } else {
1295 adg_matrix_copy(matrix, map);
1297 adg_matrix_normalize(matrix);
1298 break;
1299 case ADG_MIX_PARENT:
1300 if (data->parent) {
1301 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1302 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1303 } else {
1304 adg_matrix_copy(matrix, map);
1306 break;
1307 case ADG_MIX_PARENT_NORMALIZED:
1308 if (data->parent) {
1309 adg_matrix_copy(matrix, adg_entity_get_local_map(data->parent));
1310 adg_matrix_transform(matrix, map, ADG_TRANSFORM_BEFORE);
1311 } else {
1312 adg_matrix_copy(matrix, map);
1314 adg_matrix_normalize(matrix);
1315 break;
1316 case ADG_MIX_UNDEFINED:
1317 g_warning(_("%s: requested to mix the maps using an undefined mix method"),
1318 G_STRLOC);
1319 break;
1320 default:
1321 g_return_if_reached();
1322 break;
1326 static void
1327 _adg_real_invalidate(AdgEntity *entity)
1329 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1330 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
1332 /* Do not raise any warning if invalidate() is not defined,
1333 * assuming entity does not have additional cache to be cleared */
1334 if (klass->invalidate)
1335 klass->invalidate(entity);
1337 data->extents.is_defined = FALSE;
1340 static void
1341 _adg_real_arrange(AdgEntity *entity)
1343 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1344 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
1346 /* Update the global matrix, if required */
1347 if (!data->global.is_defined) {
1348 data->global.is_defined = TRUE;
1349 g_signal_emit(entity, _adg_signals[GLOBAL_CHANGED], 0);
1352 /* Update the local matrix, if required */
1353 if (!data->local.is_defined) {
1354 data->local.is_defined = TRUE;
1355 g_signal_emit(entity, _adg_signals[LOCAL_CHANGED], 0);
1358 /* The arrange() method must be defined */
1359 if (klass->arrange == NULL) {
1360 g_warning(_("%s: 'arrange' method not implemented for type '%s'"),
1361 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1362 data->extents.is_defined = FALSE;
1363 return;
1366 klass->arrange(entity);
1369 static void
1370 _adg_real_render(AdgEntity *entity, cairo_t *cr)
1372 AdgEntityClass *klass = ADG_ENTITY_GET_CLASS(entity);
1374 /* The render method must be defined */
1375 if (klass->render == NULL) {
1376 g_warning(_("%s: 'render' method not implemented for type '%s'"),
1377 G_STRLOC, g_type_name(G_OBJECT_TYPE(entity)));
1378 return;
1381 /* Before the rendering, the entity should be arranged */
1382 g_signal_emit(entity, _adg_signals[ARRANGE], 0);
1384 cairo_save(cr);
1385 klass->render(entity, cr);
1386 cairo_restore(cr);
1388 if (_adg_show_extents) {
1389 AdgEntityPrivate *data = adg_entity_get_instance_private(entity);
1390 CpmlExtents *extents = &data->extents;
1392 if (extents->is_defined) {
1393 cairo_save(cr);
1394 cairo_set_source_rgba(cr, 0.15, 0.15, 0.15, 0.15);
1395 cairo_rectangle(cr, extents->org.x, extents->org.y,
1396 extents->size.x, extents->size.y);
1397 cairo_fill(cr);
1398 cairo_restore(cr);