[AdgStroke] Using adg_entity_apply_local_matrix()
[adg.git] / adg / adg-container.c
blobb76b8ca933187985a82094d6ef5cd1fca0441d45
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.
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-container.h"
38 #include "adg-container-private.h"
39 #include "adg-intl.h"
41 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_container_parent_class)
44 enum {
45 PROP_0,
46 PROP_CHILD
49 enum {
50 ADD,
51 REMOVE,
52 LAST_SIGNAL
56 static void dispose (GObject *object);
57 static void set_property (GObject *object,
58 guint prop_id,
59 const GValue *value,
60 GParamSpec *pspec);
61 static GSList * get_children (AdgContainer *container);
62 static void add (AdgContainer *container,
63 AdgEntity *entity);
64 static void remove (AdgContainer *container,
65 AdgEntity *entity);
66 static gboolean invalidate (AdgEntity *entity);
67 static gboolean render (AdgEntity *entity,
68 cairo_t *cr);
70 static guint signals[LAST_SIGNAL] = { 0 };
73 G_DEFINE_TYPE(AdgContainer, adg_container, ADG_TYPE_ENTITY);
76 static void
77 adg_container_class_init(AdgContainerClass *klass)
79 GObjectClass *gobject_class;
80 AdgEntityClass *entity_class;
81 GParamSpec *param;
83 gobject_class = (GObjectClass *) klass;
84 entity_class = (AdgEntityClass *) klass;
86 g_type_class_add_private(klass, sizeof(AdgContainerPrivate));
88 gobject_class->dispose = dispose;
89 gobject_class->set_property = set_property;
91 entity_class->invalidate = invalidate;
92 entity_class->render = render;
94 klass->get_children = get_children;
95 klass->add = add;
96 klass->remove = remove;
98 param = g_param_spec_object("child",
99 P_("Child"),
100 P_("Can be used to add a new child to the container"),
101 ADG_TYPE_ENTITY, G_PARAM_WRITABLE);
102 g_object_class_install_property(gobject_class, PROP_CHILD, param);
105 * AdgContainer::add:
106 * @container: an #AdgContainer
107 * @entity: the #AdgEntity to add
109 * Adds @entity to @container. @entity must not be inside another
110 * container or the operation will fail.
112 signals[ADD] = g_signal_new("add",
113 G_OBJECT_CLASS_TYPE(gobject_class),
114 G_SIGNAL_RUN_FIRST,
115 G_STRUCT_OFFSET(AdgContainerClass, add),
116 NULL, NULL,
117 g_cclosure_marshal_VOID__OBJECT,
118 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
121 * AdgContainer::remove:
122 * @container: an #AdgContainer
123 * @entity: the #AdgEntity to remove
125 * Removes @entity from @container.
127 signals[REMOVE] = g_signal_new("remove",
128 G_OBJECT_CLASS_TYPE(gobject_class),
129 G_SIGNAL_RUN_FIRST,
130 G_STRUCT_OFFSET(AdgContainerClass, remove),
131 NULL, NULL,
132 g_cclosure_marshal_VOID__OBJECT,
133 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
136 static void
137 adg_container_init(AdgContainer *container)
139 AdgContainerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(container,
140 ADG_TYPE_CONTAINER,
141 AdgContainerPrivate);
143 data->children = NULL;
145 container->data = data;
148 static void
149 dispose(GObject *object)
151 AdgContainer *container;
152 AdgContainerPrivate *data;
154 container = (AdgContainer *) object;
155 data = container->data;
157 /* Remove all the children from the container: these will emit
158 * a "remove" signal for every child and will drop all the
159 * references from the children to this container (and, obviously,
160 * from the container to the children). */
161 while (data->children != NULL)
162 adg_container_remove(container, (AdgEntity *) data->children->data);
164 if (PARENT_OBJECT_CLASS->dispose != NULL)
165 PARENT_OBJECT_CLASS->dispose(object);
168 static void
169 set_property(GObject *object,
170 guint prop_id, const GValue *value, GParamSpec *pspec)
172 AdgContainer *container = (AdgContainer *) object;
174 switch (prop_id) {
175 case PROP_CHILD:
176 adg_container_add(container, g_value_get_object(value));
177 break;
178 default:
179 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
185 * adg_container_new:
187 * Creates a new container entity.
189 * Returns: the newly created entity
191 AdgEntity *
192 adg_container_new(void)
194 return (AdgEntity *) g_object_new(ADG_TYPE_CONTAINER, NULL);
199 * adg_container_add:
200 * @container: an #AdgContainer
201 * @entity: an #AdgEntity
203 * Emits a #AdgContainer::add signal on @container passing @entity
204 * as argument. @entity must be added to only one container at a time,
205 * you can't place the same entity inside two different containers.
207 * Once @entity has been added, the floating reference will be removed
208 * and @container will own a reference to @entity. This means the only
209 * proper way to destroy @entity is to call adg_container_remove().
211 void
212 adg_container_add(AdgContainer *container, AdgEntity *entity)
214 g_return_if_fail(ADG_IS_CONTAINER(container));
215 g_return_if_fail(ADG_IS_ENTITY(entity));
217 g_signal_emit(container, signals[ADD], 0, entity);
221 * adg_container_remove:
222 * @container: an #AdgContainer
223 * @entity: an #AdgEntity
225 * Emits a #AdgContainer::remove signal on @container passing
226 * @entity as argument. @entity must be inside @container.
228 * Note that @container will own a reference to @entity and it
229 * may be the last reference held: this means removing an entity
230 * from its container can destroy it.
232 * If you want to use @entity again, you need to add a reference
233 * to it, using g_object_ref(), before removing it from @container.
234 * The following typical example shows you how to properly move
235 * <varname>entity</varname> from <varname>container1</varname>
236 * to <varname>container2</varname>:
238 * |[
239 * g_object_ref(entity);
240 * adg_container_remove(container1, entity);
241 * adg_container_add(container2, entity)
242 * g_object_unref(entity);
243 * ]|
245 void
246 adg_container_remove(AdgContainer *container, AdgEntity *entity)
248 g_return_if_fail(ADG_IS_CONTAINER(container));
249 g_return_if_fail(ADG_IS_ENTITY(entity));
251 g_signal_emit(container, signals[REMOVE], 0, entity);
255 * adg_container_get_children:
256 * @container: an #AdgContainer
258 * Gets the children list of @container. This list must be manually
259 * freed with g_slist_free() when no longer user.
261 * The returned list is ordered from the most recently added child
262 * to the oldest one.
264 * Returns: a newly allocated #GSList or %NULL empty list or on errors
266 GSList *
267 adg_container_get_children(AdgContainer *container)
269 g_return_val_if_fail(ADG_IS_CONTAINER(container), NULL);
271 return ADG_CONTAINER_GET_CLASS(container)->get_children(container);
275 * adg_container_foreach:
276 * @container: an #AdgContainer
277 * @callback: a callback
278 * @user_data: callback user data
280 * Invokes @callback on each child of @container.
281 * The callback should be declared as:
283 * |[
284 * void callback(AdgEntity *entity, gpointer user_data);
285 * ]|
287 void
288 adg_container_foreach(AdgContainer *container,
289 GCallback callback, gpointer user_data)
291 GSList *children;
293 g_return_if_fail(ADG_IS_CONTAINER(container));
294 g_return_if_fail(callback != NULL);
296 children = adg_container_get_children (container);
298 while (children) {
299 if (children->data)
300 ((void (*) (gpointer, gpointer)) callback) (children->data, user_data);
302 children = g_slist_delete_link(children, children);
307 * adg_container_propagate:
308 * @container: an #AdgContainer
309 * @signal_id: the signal id
310 * @detail: the detail
311 * @...: parameters to be passed to the signal, followed by a pointer
312 * to the allocated memory where to store the return type: if
313 * the signal is %G_TYPE_NONE (void return type), this trailing
314 * pointer should be omitted
316 * Emits the specified signal to all the children of @container
317 * using g_signal_emit_valist() calls.
319 void
320 adg_container_propagate(AdgContainer *container,
321 guint signal_id, GQuark detail, ...)
323 va_list var_args;
325 va_start(var_args, detail);
326 adg_container_propagate_valist(container, signal_id, detail, var_args);
327 va_end(var_args);
331 * adg_container_propagate_by_name:
332 * @container: an #AdgContainer
333 * @detailed_signal: a string of the form "signal-name::detail".
334 * @...: parameters to be passed to the signal, followed by a pointer
335 * to the allocated memory where to store the return type: if
336 * the signal is %G_TYPE_NONE (void return type), this trailing
337 * pointer should be omitted
339 * Emits the specified signal to all the children of @container
340 * using g_signal_emit_valist() calls.
342 void
343 adg_container_propagate_by_name(AdgContainer *container,
344 const gchar *detailed_signal, ...)
346 guint signal_id;
347 GQuark detail = 0;
348 va_list var_args;
350 if (!g_signal_parse_name(detailed_signal, G_TYPE_FROM_INSTANCE(container),
351 &signal_id, &detail, FALSE)) {
352 g_warning("%s: signal `%s' is invalid for instance `%p'",
353 G_STRLOC, detailed_signal, container);
354 return;
357 va_start(var_args, detailed_signal);
358 adg_container_propagate_valist(container, signal_id, detail, var_args);
359 va_end(var_args);
363 * adg_container_propagate_valist:
364 * @container: an #AdgContainer
365 * @signal_id: the signal id
366 * @detail: the detail
367 * @var_args: parameters to be passed to the signal, followed by a
368 * pointer to the allocated memory where to store the
369 * return type: if the signal is %G_TYPE_NONE (void return
370 * type), this trailing pointer should be omitted
372 * Emits the specified signal to all the children of @container
373 * using g_signal_emit_valist() calls.
375 void
376 adg_container_propagate_valist(AdgContainer *container,
377 guint signal_id, GQuark detail, va_list var_args)
379 GSList *children;
380 va_list var_copy;
382 g_return_if_fail(ADG_IS_CONTAINER(container));
384 children = adg_container_get_children(container);
386 while (children) {
387 if (children->data) {
388 G_VA_COPY(var_copy, var_args);
389 g_signal_emit_valist(children->data, signal_id, detail, var_copy);
392 children = g_slist_delete_link(children, children);
397 static GSList *
398 get_children(AdgContainer *container)
400 AdgContainerPrivate *data = container->data;
402 /* The NULL case is yet managed by GLib */
403 return g_slist_copy(data->children);
406 static void
407 add(AdgContainer *container, AdgEntity *entity)
409 AdgEntity *old_parent;
410 AdgContainerPrivate *data;
412 old_parent = adg_entity_get_parent(entity);
413 if (old_parent != NULL) {
414 g_warning("Attempting to add an entity with type %s to a container "
415 "of type %s, but the entity is already inside a container "
416 "of type %s.",
417 g_type_name(G_OBJECT_TYPE(entity)),
418 g_type_name(G_OBJECT_TYPE(container)),
419 g_type_name(G_OBJECT_TYPE(old_parent)));
420 return;
423 data = container->data;
424 data->children = g_slist_prepend(data->children, entity);
426 g_object_ref_sink(entity);
427 adg_entity_set_parent(entity, (AdgEntity *) container);
430 static void
431 remove(AdgContainer *container, AdgEntity *entity)
433 AdgContainerPrivate *data;
434 GSList *node;
436 data = container->data;
437 node = g_slist_find(data->children, entity);
439 if (node == NULL) {
440 g_warning("Attempting to remove an entity with type %s from a "
441 "container of type %s, but the entity is not present.",
442 g_type_name(G_OBJECT_TYPE(entity)),
443 g_type_name(G_OBJECT_TYPE(container)));
444 return;
447 data->children = g_slist_delete_link(data->children, node);
448 adg_entity_set_parent(entity, NULL);
449 g_object_unref(entity);
453 static gboolean
454 invalidate(AdgEntity *entity)
456 adg_container_propagate_by_name((AdgContainer *) entity, "invalidate");
457 return TRUE;
460 static gboolean
461 render(AdgEntity *entity, cairo_t *cr)
463 adg_container_propagate_by_name((AdgContainer *) entity, "render", cr);
464 return TRUE;