[build] Moved project dirs under src/
[adg.git] / src / adg / adg-container.c
blobf6703042035958135032f0dfa3800844410c0762
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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-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/tutorial/view/3 for further details.
28 **/
30 /**
31 * AdgContainer:
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
35 **/
37 #include "adg-internal.h"
38 #include "adg-container.h"
39 #include "adg-container-private.h"
40 #include "adg-marshal.h"
42 #define _ADG_PARENT_OBJECT_CLASS ((GObjectClass *) adg_container_parent_class)
43 #define _ADG_PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_container_parent_class)
46 enum {
47 PROP_0,
48 PROP_CHILD
51 enum {
52 ADD,
53 REMOVE,
54 LAST_SIGNAL
58 static void _adg_dispose (GObject *object);
59 static void _adg_set_property (GObject *object,
60 guint prop_id,
61 const GValue *value,
62 GParamSpec *pspec);
63 static void _adg_global_changed (AdgEntity *entity);
64 static void _adg_local_changed (AdgEntity *entity);
65 static void _adg_invalidate (AdgEntity *entity);
66 static void _adg_arrange (AdgEntity *entity);
67 static void _adg_add_extents (AdgEntity *entity,
68 CpmlExtents *container_extents);
69 static void _adg_render (AdgEntity *entity,
70 cairo_t *cr);
71 static GSList * _adg_children (AdgContainer *container);
72 static void _adg_add (AdgContainer *container,
73 AdgEntity *entity);
74 static void _adg_remove (AdgContainer *container,
75 AdgEntity *entity);
77 static guint _adg_signals[LAST_SIGNAL] = { 0 };
80 G_DEFINE_TYPE(AdgContainer, adg_container, ADG_TYPE_ENTITY);
83 static void
84 adg_container_class_init(AdgContainerClass *klass)
86 GObjectClass *gobject_class;
87 AdgEntityClass *entity_class;
88 GParamSpec *param;
90 gobject_class = (GObjectClass *) klass;
91 entity_class = (AdgEntityClass *) klass;
93 g_type_class_add_private(klass, sizeof(AdgContainerPrivate));
95 gobject_class->dispose = _adg_dispose;
96 gobject_class->set_property = _adg_set_property;
98 entity_class->global_changed = _adg_global_changed;
99 entity_class->local_changed = _adg_local_changed;
100 entity_class->invalidate = _adg_invalidate;
101 entity_class->arrange = _adg_arrange;
102 entity_class->render = _adg_render;
104 klass->children = _adg_children;
105 klass->add = _adg_add;
106 klass->remove = _adg_remove;
108 param = g_param_spec_object("child",
109 P_("Child"),
110 P_("Can be used to add a new child to the container"),
111 ADG_TYPE_ENTITY,
112 G_PARAM_WRITABLE);
113 g_object_class_install_property(gobject_class, PROP_CHILD, param);
116 * AdgContainer::add:
117 * @container: an #AdgContainer
118 * @entity: the #AdgEntity to add
120 * Adds @entity to @container. @entity must not be inside another
121 * container or the operation will fail.
123 _adg_signals[ADD] = g_signal_new("add",
124 G_OBJECT_CLASS_TYPE(gobject_class),
125 G_SIGNAL_RUN_FIRST,
126 G_STRUCT_OFFSET(AdgContainerClass, add),
127 NULL, NULL,
128 adg_marshal_VOID__OBJECT,
129 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
132 * AdgContainer::remove:
133 * @container: an #AdgContainer
134 * @entity: the #AdgEntity to remove
136 * Removes @entity from @container.
138 _adg_signals[REMOVE] = g_signal_new("remove",
139 G_OBJECT_CLASS_TYPE(gobject_class),
140 G_SIGNAL_RUN_FIRST,
141 G_STRUCT_OFFSET(AdgContainerClass, remove),
142 NULL, NULL,
143 adg_marshal_VOID__OBJECT,
144 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
147 static void
148 adg_container_init(AdgContainer *container)
150 AdgContainerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(container,
151 ADG_TYPE_CONTAINER,
152 AdgContainerPrivate);
154 data->children = NULL;
156 container->data = data;
159 static void
160 _adg_dispose(GObject *object)
162 AdgContainer *container;
163 AdgContainerPrivate *data;
165 container = (AdgContainer *) object;
166 data = container->data;
168 /* Remove all the children from the container: these will emit
169 * a "remove" signal for every child and will drop all the
170 * references from the children to this container (and, obviously,
171 * from the container to the children). */
172 while (data->children != NULL)
173 adg_container_remove(container, (AdgEntity *) data->children->data);
175 if (_ADG_PARENT_OBJECT_CLASS->dispose)
176 _ADG_PARENT_OBJECT_CLASS->dispose(object);
179 static void
180 _adg_set_property(GObject *object,
181 guint prop_id, const GValue *value, GParamSpec *pspec)
183 AdgContainer *container = (AdgContainer *) object;
185 switch (prop_id) {
186 case PROP_CHILD:
187 adg_container_add(container, g_value_get_object(value));
188 break;
189 default:
190 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
196 * adg_container_new:
198 * Creates a new container entity.
200 * Returns: the newly created container entity
202 AdgContainer *
203 adg_container_new(void)
205 return g_object_new(ADG_TYPE_CONTAINER, NULL);
210 * adg_container_add:
211 * @container: an #AdgContainer
212 * @entity: an #AdgEntity
214 * Emits a #AdgContainer::add signal on @container passing @entity
215 * as argument. @entity must be added to only one container at a time,
216 * you can't place the same entity inside two different containers.
218 * Once @entity has been added, the floating reference will be removed
219 * and @container will own a reference to @entity. This means the only
220 * proper way to destroy @entity is to call adg_container_remove().
222 void
223 adg_container_add(AdgContainer *container, AdgEntity *entity)
225 g_return_if_fail(ADG_IS_CONTAINER(container));
226 g_return_if_fail(ADG_IS_ENTITY(entity));
228 g_signal_emit(container, _adg_signals[ADD], 0, entity);
232 * adg_container_remove:
233 * @container: an #AdgContainer
234 * @entity: an #AdgEntity
236 * Emits a #AdgContainer::remove signal on @container passing
237 * @entity as argument. @entity must be inside @container.
239 * Note that @container will own a reference to @entity and it
240 * may be the last reference held: this means removing an entity
241 * from its container can destroy it.
243 * If you want to use @entity again, you need to add a reference
244 * to it, using g_object_ref(), before removing it from @container.
245 * The following typical example shows you how to properly move
246 * <varname>entity</varname> from <varname>container1</varname>
247 * to <varname>container2</varname>:
249 * |[
250 * g_object_ref(entity);
251 * adg_container_remove(container1, entity);
252 * adg_container_add(container2, entity)
253 * g_object_unref(entity);
254 * ]|
256 void
257 adg_container_remove(AdgContainer *container, AdgEntity *entity)
259 g_return_if_fail(ADG_IS_CONTAINER(container));
260 g_return_if_fail(ADG_IS_ENTITY(entity));
262 g_signal_emit(container, _adg_signals[REMOVE], 0, entity);
266 * adg_container_children:
267 * @container: an #AdgContainer
269 * Gets the children list of @container. This list must be manually
270 * freed with g_slist_free() when no longer user.
272 * The returned list is ordered from the most recently added child
273 * to the oldest one.
275 * Returns: a newly allocated #GSList or %NULL empty list or on errors
277 GSList *
278 adg_container_children(AdgContainer *container)
280 AdgContainerClass *klass;
282 g_return_val_if_fail(ADG_IS_CONTAINER(container), NULL);
284 klass = ADG_CONTAINER_GET_CLASS(container);
286 if (klass->children == NULL)
287 return NULL;
289 return klass->children(container);
293 * adg_container_foreach:
294 * @container: an #AdgContainer
295 * @callback: a callback
296 * @user_data: callback user data
298 * Invokes @callback on each child of @container.
299 * The callback should be declared as:
301 * |[
302 * void callback(AdgEntity *entity, gpointer user_data);
303 * ]|
305 void
306 adg_container_foreach(AdgContainer *container,
307 GCallback callback, gpointer user_data)
309 GSList *children;
311 g_return_if_fail(ADG_IS_CONTAINER(container));
312 g_return_if_fail(callback != NULL);
314 children = adg_container_children(container);
316 while (children != NULL) {
317 if (children->data != NULL)
318 ((void (*) (gpointer, gpointer)) callback) (children->data, user_data);
320 children = g_slist_delete_link(children, children);
325 * adg_container_propagate:
326 * @container: an #AdgContainer
327 * @signal_id: the signal id
328 * @detail: the detail
329 * @...: parameters to be passed to the signal, followed by a pointer
330 * to the allocated memory where to store the return type: if
331 * the signal is %G_TYPE_NONE (void return type), this trailing
332 * pointer should be omitted
334 * Emits the specified signal to all the children of @container
335 * using g_signal_emit_valist() calls.
337 void
338 adg_container_propagate(AdgContainer *container,
339 guint signal_id, GQuark detail, ...)
341 va_list var_args;
343 va_start(var_args, detail);
344 adg_container_propagate_valist(container, signal_id, detail, var_args);
345 va_end(var_args);
349 * adg_container_propagate_by_name:
350 * @container: an #AdgContainer
351 * @detailed_signal: a string of the form "signal-name::detail".
352 * @...: parameters to be passed to the signal, followed by a pointer
353 * to the allocated memory where to store the return type: if
354 * the signal is %G_TYPE_NONE (void return type), this trailing
355 * pointer should be omitted
357 * Emits the specified signal to all the children of @container
358 * using g_signal_emit_valist() calls.
360 void
361 adg_container_propagate_by_name(AdgContainer *container,
362 const gchar *detailed_signal, ...)
364 guint signal_id;
365 GQuark detail = 0;
366 va_list var_args;
368 if (!g_signal_parse_name(detailed_signal, G_TYPE_FROM_INSTANCE(container),
369 &signal_id, &detail, FALSE)) {
370 g_warning("%s: signal `%s' is invalid for instance `%p'",
371 G_STRLOC, detailed_signal, container);
372 return;
375 va_start(var_args, detailed_signal);
376 adg_container_propagate_valist(container, signal_id, detail, var_args);
377 va_end(var_args);
381 * adg_container_propagate_valist:
382 * @container: an #AdgContainer
383 * @signal_id: the signal id
384 * @detail: the detail
385 * @var_args: parameters to be passed to the signal, followed by a
386 * pointer to the allocated memory where to store the
387 * return type: if the signal is %G_TYPE_NONE (void return
388 * type), this trailing pointer should be omitted
390 * Emits the specified signal to all the children of @container
391 * using g_signal_emit_valist() calls.
393 void
394 adg_container_propagate_valist(AdgContainer *container,
395 guint signal_id, GQuark detail, va_list var_args)
397 GSList *children;
398 va_list var_copy;
400 g_return_if_fail(ADG_IS_CONTAINER(container));
402 children = adg_container_children(container);
404 while (children != NULL) {
405 if (children->data != NULL) {
406 G_VA_COPY(var_copy, var_args);
407 g_signal_emit_valist(children->data, signal_id, detail, var_copy);
410 children = g_slist_delete_link(children, children);
415 static void
416 _adg_global_changed(AdgEntity *entity)
418 if (_ADG_PARENT_ENTITY_CLASS->global_changed)
419 _ADG_PARENT_ENTITY_CLASS->global_changed(entity);
421 adg_container_propagate_by_name((AdgContainer *) entity, "global-changed");
424 static void
425 _adg_local_changed(AdgEntity *entity)
427 if (_ADG_PARENT_ENTITY_CLASS->local_changed)
428 _ADG_PARENT_ENTITY_CLASS->local_changed(entity);
430 adg_container_propagate_by_name((AdgContainer *) entity, "local-changed");
433 static void
434 _adg_invalidate(AdgEntity *entity)
436 adg_container_propagate_by_name((AdgContainer *) entity, "invalidate");
439 static void
440 _adg_arrange(AdgEntity *entity)
442 AdgContainer *container = (AdgContainer *) entity;
443 CpmlExtents extents = { 0 };
445 adg_container_propagate_by_name(container, "arrange", NULL);
446 adg_container_foreach(container, G_CALLBACK(_adg_add_extents), &extents);
447 adg_entity_set_extents(entity, &extents);
450 static void
451 _adg_add_extents(AdgEntity *entity, CpmlExtents *container_extents)
453 cpml_extents_add(container_extents, adg_entity_get_extents(entity));
456 static void
457 _adg_render(AdgEntity *entity, cairo_t *cr)
459 adg_container_propagate_by_name((AdgContainer *) entity, "render", cr);
463 static GSList *
464 _adg_children(AdgContainer *container)
466 AdgContainerPrivate *data = container->data;
468 /* The NULL case is yet managed by GLib */
469 return g_slist_copy(data->children);
472 static void
473 _adg_add(AdgContainer *container, AdgEntity *entity)
475 const AdgEntity *old_parent;
476 AdgContainerPrivate *data;
478 old_parent = adg_entity_get_parent(entity);
479 if (old_parent != NULL) {
480 g_warning("Attempting to add an entity with type %s to a container "
481 "of type %s, but the entity is already inside a container "
482 "of type %s.",
483 g_type_name(G_OBJECT_TYPE(entity)),
484 g_type_name(G_OBJECT_TYPE(container)),
485 g_type_name(G_OBJECT_TYPE(old_parent)));
486 return;
489 data = container->data;
490 data->children = g_slist_prepend(data->children, entity);
492 g_object_ref_sink(entity);
493 adg_entity_set_parent(entity, (AdgEntity *) container);
496 static void
497 _adg_remove(AdgContainer *container, AdgEntity *entity)
499 AdgContainerPrivate *data;
500 GSList *node;
502 data = container->data;
503 node = g_slist_find(data->children, entity);
505 if (node == NULL) {
506 g_warning("Attempting to remove an entity with type %s from a "
507 "container of type %s, but the entity is not present.",
508 g_type_name(G_OBJECT_TYPE(entity)),
509 g_type_name(G_OBJECT_TYPE(container)));
510 return;
513 data->children = g_slist_delete_link(data->children, node);
514 adg_entity_set_parent(entity, NULL);
515 g_object_unref(entity);