1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 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.
22 * SECTION:adg-container
23 * @short_description: Base class for entity that can contain other entities
25 * The #AdgContainer is an entity that can contains more sub-entities.
26 * Moreover, it can apply a common transformation to local and/or global
27 * maps: see http://adg.entidi.com/home/details/ for further details.
29 * Adding an entity to a container will create a strong reference to the
30 * container in the entity and a weak reference to the entity on the
31 * container. This way the container will be able to destroy its children
32 * when destroyed and it will be able to update its children when an entity
41 * All fields are private and should not be used directly.
42 * Use its public methods instead.
49 * @children: virtual method that gets the list of entities (that is
50 * #AdgEntity and derived instances) owned by the container.
51 * @add: signal that adds a new entity to the container.
52 * @remove: signal that removes a specific entity from the container.
54 * #AdgContainer effectively stores a #GSList of children into its
55 * private data and keeps a reference to every child it owns.
61 #include "adg-internal.h"
63 #include "adg-container.h"
64 #include "adg-container-private.h"
67 #define _ADG_PARENT_OBJECT_CLASS ((GObjectClass *) adg_container_parent_class)
68 #define _ADG_PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_container_parent_class)
71 G_DEFINE_TYPE(AdgContainer
, adg_container
, ADG_TYPE_ENTITY
)
85 static void _adg_dispose (GObject
*object
);
86 static void _adg_set_property (GObject
*object
,
90 static void _adg_destroy (AdgEntity
*entity
);
91 static void _adg_global_changed (AdgEntity
*entity
);
92 static void _adg_local_changed (AdgEntity
*entity
);
93 static void _adg_invalidate (AdgEntity
*entity
);
94 static void _adg_arrange (AdgEntity
*entity
);
95 static void _adg_add_extents (AdgEntity
*entity
,
96 CpmlExtents
*extents
);
97 static void _adg_render (AdgEntity
*entity
,
99 static GSList
* _adg_children (AdgContainer
*container
);
100 static void _adg_add (AdgContainer
*container
,
102 static void _adg_remove (AdgContainer
*container
,
104 static void _adg_remove_from_list (gpointer container
,
107 static guint _adg_signals
[LAST_SIGNAL
] = { 0 };
111 adg_container_class_init(AdgContainerClass
*klass
)
113 GObjectClass
*gobject_class
;
114 AdgEntityClass
*entity_class
;
117 gobject_class
= (GObjectClass
*) klass
;
118 entity_class
= (AdgEntityClass
*) klass
;
120 g_type_class_add_private(klass
, sizeof(AdgContainerPrivate
));
122 gobject_class
->dispose
= _adg_dispose
;
123 gobject_class
->set_property
= _adg_set_property
;
125 entity_class
->destroy
= _adg_destroy
;
126 entity_class
->global_changed
= _adg_global_changed
;
127 entity_class
->local_changed
= _adg_local_changed
;
128 entity_class
->invalidate
= _adg_invalidate
;
129 entity_class
->arrange
= _adg_arrange
;
130 entity_class
->render
= _adg_render
;
132 klass
->children
= _adg_children
;
133 klass
->add
= _adg_add
;
134 klass
->remove
= _adg_remove
;
136 param
= g_param_spec_object("child",
138 P_("Can be used to add a new child to the container"),
141 g_object_class_install_property(gobject_class
, PROP_CHILD
, param
);
145 * @container: an #AdgContainer
146 * @entity: the #AdgEntity to add
148 * Adds @entity to @container. @entity must not be inside another
149 * container or the operation will fail.
153 _adg_signals
[ADD
] = g_signal_new("add",
154 G_OBJECT_CLASS_TYPE(gobject_class
),
156 G_STRUCT_OFFSET(AdgContainerClass
, add
),
158 adg_marshal_VOID__OBJECT
,
159 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
162 * AdgContainer::remove:
163 * @container: an #AdgContainer
164 * @entity: the #AdgEntity to remove
166 * Removes @entity from @container.
170 _adg_signals
[REMOVE
] = g_signal_new("remove",
171 G_OBJECT_CLASS_TYPE(gobject_class
),
173 G_STRUCT_OFFSET(AdgContainerClass
, remove
),
175 adg_marshal_VOID__OBJECT
,
176 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
180 adg_container_init(AdgContainer
*container
)
182 AdgContainerPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(container
,
184 AdgContainerPrivate
);
186 data
->children
= NULL
;
188 container
->data
= data
;
192 _adg_dispose(GObject
*object
)
194 AdgContainer
*container
;
195 AdgContainerPrivate
*data
;
197 container
= (AdgContainer
*) object
;
198 data
= container
->data
;
200 /* Remove all the children from the container: these will emit
201 * a "remove" signal for every child and will drop all the
202 * references from the children to this container (and, obviously,
203 * from the container to the children). */
204 while (data
->children
!= NULL
)
205 adg_container_remove(container
, (AdgEntity
*) data
->children
->data
);
207 if (_ADG_PARENT_OBJECT_CLASS
->dispose
)
208 _ADG_PARENT_OBJECT_CLASS
->dispose(object
);
212 _adg_set_property(GObject
*object
,
213 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
215 AdgContainer
*container
= (AdgContainer
*) object
;
219 adg_container_add(container
, g_value_get_object(value
));
222 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
230 * Creates a new container entity.
232 * Returns: the newly created container entity
237 adg_container_new(void)
239 return g_object_new(ADG_TYPE_CONTAINER
, NULL
);
245 * @container: an #AdgContainer
246 * @entity: an #AdgEntity
248 * Emits a #AdgContainer::add signal on @container passing @entity
249 * as argument. @entity must be added to only one container at a time,
250 * you can't place the same entity inside two different containers.
252 * Once @entity has been added, the floating reference will be removed
253 * and @container will own a reference to @entity. This means the only
254 * proper way to destroy @entity is to call adg_container_remove().
259 adg_container_add(AdgContainer
*container
, AdgEntity
*entity
)
261 g_return_if_fail(ADG_IS_CONTAINER(container
));
262 g_return_if_fail(ADG_IS_ENTITY(entity
));
264 g_signal_emit(container
, _adg_signals
[ADD
], 0, entity
);
268 * adg_container_remove:
269 * @container: an #AdgContainer
270 * @entity: an #AdgEntity
272 * Emits a #AdgContainer::remove signal on @container passing
273 * @entity as argument. @entity must be inside @container.
275 * Note that @container will own a reference to @entity and it
276 * may be the last reference held: this means removing an entity
277 * from its container can destroy it.
279 * If you want to use @entity again, you need to add a reference
280 * to it, using g_object_ref(), before removing it from @container.
281 * The following typical example shows you how to properly move
282 * <varname>entity</varname> from <varname>container1</varname>
283 * to <varname>container2</varname>:
285 * <informalexample><programlisting language="C">
286 * g_object_ref(entity);
287 * adg_container_remove(container1, entity);
288 * adg_container_add(container2, entity)
289 * g_object_unref(entity);
290 * </programlisting></informalexample>
295 adg_container_remove(AdgContainer
*container
, AdgEntity
*entity
)
297 g_return_if_fail(ADG_IS_CONTAINER(container
));
298 g_return_if_fail(ADG_IS_ENTITY(entity
));
300 g_signal_emit(container
, _adg_signals
[REMOVE
], 0, entity
);
304 * adg_container_children:
305 * @container: an #AdgContainer
307 * Gets the children list of @container. This list must be manually
308 * freed with g_slist_free() when no longer user.
310 * The returned list is ordered from the most recently added child
313 * Returns: (element-type AdgEntity) (transfer container): a newly allocated #GSList of #AdgEntity or <constant>NULL</constant> on no children or errors
318 adg_container_children(AdgContainer
*container
)
320 AdgContainerClass
*klass
;
322 g_return_val_if_fail(ADG_IS_CONTAINER(container
), NULL
);
324 klass
= ADG_CONTAINER_GET_CLASS(container
);
326 if (klass
->children
== NULL
)
329 return klass
->children(container
);
333 * adg_container_foreach:
334 * @container: an #AdgContainer
335 * @callback: (scope call): a callback
336 * @user_data: callback user data
338 * Invokes @callback on each child of @container.
339 * The callback should be declared as:
341 * <informalexample><programlisting language="C">
342 * void callback(AdgEntity *entity, gpointer user_data);
343 * </programlisting></informalexample>
348 adg_container_foreach(AdgContainer
*container
,
349 GCallback callback
, gpointer user_data
)
353 g_return_if_fail(ADG_IS_CONTAINER(container
));
354 g_return_if_fail(callback
!= NULL
);
356 children
= adg_container_children(container
);
358 while (children
!= NULL
) {
359 if (children
->data
!= NULL
)
360 ((void (*) (gpointer
, gpointer
)) callback
) (children
->data
, user_data
);
362 children
= g_slist_delete_link(children
, children
);
367 * adg_container_propagate:
368 * @container: an #AdgContainer
369 * @signal_id: the signal id
370 * @detail: the detail
371 * @...: parameters to be passed to the signal, followed by a pointer
372 * to the allocated memory where to store the return type: if
373 * the signal is %G_TYPE_NONE (void return type), this trailing
374 * pointer should be omitted
376 * Emits the specified signal to all the children of @container
377 * using g_signal_emit_valist() calls.
382 adg_container_propagate(AdgContainer
*container
,
383 guint signal_id
, GQuark detail
, ...)
387 va_start(var_args
, detail
);
388 adg_container_propagate_valist(container
, signal_id
, detail
, var_args
);
393 * adg_container_propagate_by_name:
394 * @container: an #AdgContainer
395 * @detailed_signal: a string of the form "signal-name::detail".
396 * @...: parameters to be passed to the signal, followed by a pointer
397 * to the allocated memory where to store the return type: if
398 * the signal is %G_TYPE_NONE (void return type), this trailing
399 * pointer should be omitted
401 * Emits the specified signal to all the children of @container
402 * using g_signal_emit_valist() calls.
407 adg_container_propagate_by_name(AdgContainer
*container
,
408 const gchar
*detailed_signal
, ...)
414 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(container
),
415 &signal_id
, &detail
, FALSE
)) {
416 g_warning(_("%s: signal '%s' is invalid for instance %p"),
417 G_STRLOC
, detailed_signal
, container
);
421 va_start(var_args
, detailed_signal
);
422 adg_container_propagate_valist(container
, signal_id
, detail
, var_args
);
427 * adg_container_propagate_valist:
428 * @container: an #AdgContainer
429 * @signal_id: the signal id
430 * @detail: the detail
431 * @var_args: parameters to be passed to the signal, followed by a
432 * pointer to the allocated memory where to store the
433 * return type: if the signal is %G_TYPE_NONE (void return
434 * type), this trailing pointer should be omitted
436 * Emits the specified signal to all the children of @container
437 * using g_signal_emit_valist() calls.
442 adg_container_propagate_valist(AdgContainer
*container
,
443 guint signal_id
, GQuark detail
, va_list var_args
)
448 g_return_if_fail(ADG_IS_CONTAINER(container
));
450 children
= adg_container_children(container
);
452 while (children
!= NULL
) {
453 if (children
->data
!= NULL
) {
454 G_VA_COPY(var_copy
, var_args
);
455 g_signal_emit_valist(children
->data
, signal_id
, detail
, var_copy
);
458 children
= g_slist_delete_link(children
, children
);
464 _adg_destroy(AdgEntity
*entity
)
466 adg_container_propagate_by_name((AdgContainer
*) entity
, "destroy");
468 if (_ADG_PARENT_ENTITY_CLASS
->destroy
)
469 _ADG_PARENT_ENTITY_CLASS
->destroy(entity
);
473 _adg_global_changed(AdgEntity
*entity
)
475 if (_ADG_PARENT_ENTITY_CLASS
->global_changed
)
476 _ADG_PARENT_ENTITY_CLASS
->global_changed(entity
);
478 adg_container_propagate_by_name((AdgContainer
*) entity
, "global-changed");
482 _adg_local_changed(AdgEntity
*entity
)
484 if (_ADG_PARENT_ENTITY_CLASS
->local_changed
)
485 _ADG_PARENT_ENTITY_CLASS
->local_changed(entity
);
487 adg_container_propagate_by_name((AdgContainer
*) entity
, "local-changed");
491 _adg_invalidate(AdgEntity
*entity
)
493 adg_container_propagate_by_name((AdgContainer
*) entity
, "invalidate");
497 _adg_arrange(AdgEntity
*entity
)
499 AdgContainer
*container
= (AdgContainer
*) entity
;
500 CpmlExtents extents
= { 0 };
502 adg_container_propagate_by_name(container
, "arrange", NULL
);
503 adg_container_foreach(container
, G_CALLBACK(_adg_add_extents
), &extents
);
504 adg_entity_set_extents(entity
, &extents
);
508 _adg_add_extents(AdgEntity
*entity
, CpmlExtents
*extents
)
510 cpml_extents_add(extents
, adg_entity_get_extents(entity
));
514 _adg_render(AdgEntity
*entity
, cairo_t
*cr
)
516 adg_container_propagate_by_name((AdgContainer
*) entity
, "render", cr
);
521 _adg_children(AdgContainer
*container
)
523 AdgContainerPrivate
*data
= container
->data
;
525 /* The NULL case is already managed by GLib */
526 return g_slist_copy(data
->children
);
530 _adg_add(AdgContainer
*container
, AdgEntity
*entity
)
532 const AdgEntity
*old_parent
;
533 AdgContainerPrivate
*data
;
535 old_parent
= adg_entity_get_parent(entity
);
536 if (old_parent
!= NULL
) {
537 g_warning(_("Attempting to add an entity with type %s to a container "
538 "of type %s, but the entity is already inside a container "
540 g_type_name(G_OBJECT_TYPE(entity
)),
541 g_type_name(G_OBJECT_TYPE(container
)),
542 g_type_name(G_OBJECT_TYPE(old_parent
)));
546 data
= container
->data
;
547 data
->children
= g_slist_prepend(data
->children
, entity
);
549 g_object_ref_sink(entity
);
550 adg_entity_set_parent(entity
, (AdgEntity
*) container
);
551 g_object_weak_ref((GObject
*) entity
, _adg_remove_from_list
, container
);
555 _adg_remove_from_list(gpointer container
, GObject
*entity
)
557 AdgContainerPrivate
*data
= ((AdgContainer
*) container
)->data
;
558 data
->children
= g_slist_remove(data
->children
, entity
);
562 _adg_remove(AdgContainer
*container
, AdgEntity
*entity
)
564 AdgContainerPrivate
*data
;
567 data
= container
->data
;
568 node
= g_slist_find(data
->children
, entity
);
571 g_warning(_("Attempting to remove an entity with type %s from a "
572 "container of type %s, but the entity is not present"),
573 g_type_name(G_OBJECT_TYPE(entity
)),
574 g_type_name(G_OBJECT_TYPE(container
)));
578 g_object_weak_unref((GObject
*) entity
, _adg_remove_from_list
, container
);
579 data
->children
= g_slist_delete_link(data
->children
, node
);
580 adg_entity_set_parent(entity
, NULL
);
581 g_object_unref(entity
);