[AdgContainer] Updated to new invalidate signal
[adg.git] / adg / adg-container.c
blob88d46a4139768a1729c43d0b6094a806f81a08fd
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"
42 enum {
43 PROP_0,
44 PROP_CHILD
47 enum {
48 ADD,
49 REMOVE,
50 LAST_SIGNAL
54 static void set_property (GObject *object,
55 guint prop_id,
56 const GValue *value,
57 GParamSpec *pspec);
58 static void dispose (GObject *object);
59 static GSList * get_children (AdgContainer *container);
60 static void add (AdgContainer *container,
61 AdgEntity *entity);
62 static void remove (AdgContainer *container,
63 AdgEntity *entity);
64 static gboolean invalidate (AdgEntity *entity);
65 static gboolean render (AdgEntity *entity,
66 cairo_t *cr);
68 static guint signals[LAST_SIGNAL] = { 0 };
71 G_DEFINE_TYPE(AdgContainer, adg_container, ADG_TYPE_ENTITY)
74 static void
75 adg_container_class_init(AdgContainerClass *klass)
77 GObjectClass *gobject_class;
78 AdgEntityClass *entity_class;
79 GParamSpec *param;
81 gobject_class = (GObjectClass *) klass;
82 entity_class = (AdgEntityClass *) klass;
84 g_type_class_add_private(klass, sizeof(AdgContainerPrivate));
86 gobject_class->set_property = set_property;
87 gobject_class->dispose = dispose;
89 entity_class->invalidate = invalidate;
90 entity_class->render = render;
92 klass->get_children = get_children;
93 klass->add = add;
94 klass->remove = remove;
96 param = g_param_spec_object("child",
97 P_("Child"),
98 P_("Can be used to add a new child to the container"),
99 ADG_TYPE_ENTITY, G_PARAM_WRITABLE);
100 g_object_class_install_property(gobject_class, PROP_CHILD, param);
103 * AdgContainer::add:
104 * @container: an #AdgContainer
105 * @entity: the #AdgEntity to add
107 * Adds @entity to @container. @entity must not be inside another
108 * container or the operation will fail.
110 signals[ADD] = g_signal_new("add",
111 G_OBJECT_CLASS_TYPE(gobject_class),
112 G_SIGNAL_RUN_FIRST,
113 G_STRUCT_OFFSET(AdgContainerClass, add),
114 NULL, NULL,
115 g_cclosure_marshal_VOID__OBJECT,
116 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
119 * AdgContainer::remove:
120 * @container: an #AdgContainer
121 * @entity: the #AdgEntity to remove
123 * Removes @entity from @container.
125 signals[REMOVE] = g_signal_new("remove",
126 G_OBJECT_CLASS_TYPE(gobject_class),
127 G_SIGNAL_RUN_FIRST,
128 G_STRUCT_OFFSET(AdgContainerClass, remove),
129 NULL, NULL,
130 g_cclosure_marshal_VOID__OBJECT,
131 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
134 static void
135 adg_container_init(AdgContainer *container)
137 AdgContainerPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(container,
138 ADG_TYPE_CONTAINER,
139 AdgContainerPrivate);
141 data->children = NULL;
143 container->data = data;
146 static void
147 set_property(GObject *object,
148 guint prop_id, const GValue *value, GParamSpec *pspec)
150 AdgContainer *container;
151 AdgContainerPrivate *data;
153 container = (AdgContainer *) object;
154 data = container->data;
156 switch (prop_id) {
157 case PROP_CHILD:
158 adg_container_add(container, g_value_get_object(value));
159 break;
160 default:
161 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
165 static void
166 dispose(GObject *object)
168 GObjectClass *object_class = (GObjectClass *) adg_container_parent_class;
170 adg_container_foreach((AdgContainer *) object,
171 G_CALLBACK(adg_entity_unparent), NULL);
173 if (object_class->dispose != NULL)
174 object_class->dispose(object);
179 * adg_container_new:
181 * Creates a new container entity.
183 * Return value: the newly created entity
185 AdgEntity *
186 adg_container_new(void)
188 return (AdgEntity *) g_object_new(ADG_TYPE_CONTAINER, NULL);
193 * adg_container_add:
194 * @container: an #AdgContainer
195 * @entity: an #AdgEntity
197 * Emits a #AdgContainer::add signal on @container passing
198 * @entity as argument.
200 * @entity may be added to only one container at a time; you can't
201 * place the same entity inside two different containers.
203 void
204 adg_container_add(AdgContainer *container, AdgEntity *entity)
206 g_return_if_fail(ADG_IS_CONTAINER(container));
207 g_return_if_fail(ADG_IS_ENTITY(entity));
209 g_signal_emit(container, signals[ADD], 0, entity);
213 * adg_container_remove:
214 * @container: an #AdgContainer
215 * @entity: an #AdgEntity
217 * Emits a #AdgContainer::remove signal on @container passing
218 * @entity as argument. @entity must be inside @container.
220 * Note that @container will own a reference to @entity
221 * and that this may be the last reference held; so removing an
222 * entity from its container can destroy it.
224 * If you want to use @entity again, you need to add a reference
225 * to it, using g_object_ref(), before removing it from @container.
227 * If you don't want to use @entity again, it's usually more
228 * efficient to simply destroy it directly using g_object_unref()
229 * since this will remove it from the container.
231 void
232 adg_container_remove(AdgContainer *container, AdgEntity *entity)
234 g_return_if_fail(ADG_IS_CONTAINER(container));
235 g_return_if_fail(ADG_IS_ENTITY(entity));
237 g_signal_emit(container, signals[REMOVE], 0, entity);
241 * adg_container_get_children:
242 * @container: an #AdgContainer
244 * Gets the children list of @container.
245 * This list must be manually freed when no longer user.
247 * Returns: a newly allocated #GSList or %NULL on error
249 GSList *
250 adg_container_get_children(AdgContainer *container)
252 g_return_val_if_fail(ADG_IS_CONTAINER(container), NULL);
254 return ADG_CONTAINER_GET_CLASS(container)->get_children(container);
258 * adg_container_foreach:
259 * @container: an #AdgContainer
260 * @callback: a callback
261 * @user_data: callback user data
263 * Invokes @callback on each child of @container.
264 * The callback should be declared as:
266 * <code>
267 * void callback(AdgEntity *entity, gpointer user_data);
268 * </code>
270 void
271 adg_container_foreach(AdgContainer *container,
272 GCallback callback, gpointer user_data)
274 GSList *children;
276 g_return_if_fail(ADG_IS_CONTAINER(container));
277 g_return_if_fail(callback != NULL);
279 children = adg_container_get_children (container);
281 while (children) {
282 if (children->data)
283 ((void (*) (gpointer, gpointer)) callback) (children->data, user_data);
285 children = g_slist_delete_link(children, children);
290 * adg_container_propagate:
291 * @container: an #AdgContainer
292 * @signal_id: the signal id
293 * @detail: the detail
294 * @...: parameters to be passed to the signal, followed by a location for
295 * the return value. If the return type of the signal is G_TYPE_NONE,
296 * the return value location can be omitted.
298 * Emits the specified signal to all the children of @container
299 * using g_signal_emit_valist() calls.
301 void
302 adg_container_propagate(AdgContainer *container,
303 guint signal_id, GQuark detail, ...)
305 va_list var_args;
307 va_start(var_args, detail);
308 adg_container_propagate_valist(container, signal_id, detail, var_args);
309 va_end(var_args);
313 * adg_container_propagate_by_name:
314 * @container: an #AdgContainer
315 * @detailed_signal: a string of the form "signal-name::detail".
316 * @...: a list of parameters to be passed to the signal, followed by
317 * a location for the return value. If the return type of the signal
318 * is G_TYPE_NONE, the return value location can be omitted.
320 * Emits the specified signal to all the children of @container
321 * using g_signal_emit_valist() calls.
323 void
324 adg_container_propagate_by_name(AdgContainer *container,
325 const gchar *detailed_signal, ...)
327 guint signal_id;
328 GQuark detail = 0;
329 va_list var_args;
331 if (!g_signal_parse_name(detailed_signal, G_TYPE_FROM_INSTANCE(container),
332 &signal_id, &detail, FALSE)) {
333 g_warning("%s: signal `%s' is invalid for instance `%p'",
334 G_STRLOC, detailed_signal, container);
335 return;
338 va_start(var_args, detailed_signal);
339 adg_container_propagate_valist(container, signal_id, detail, var_args);
340 va_end(var_args);
344 * adg_container_propagate_valist:
345 * @container: an #AdgContainer
346 * @signal_id: the signal id
347 * @detail: the detail
348 * @var_args: a list of parameters to be passed to the signal, followed by a
349 * location for the return value. If the return type of the signal
350 * is G_TYPE_NONE, the return value location can be omitted.
352 * Emits the specified signal to all the children of @container
353 * using g_signal_emit_valist() calls.
355 void
356 adg_container_propagate_valist(AdgContainer *container,
357 guint signal_id, GQuark detail, va_list var_args)
359 GSList *children;
360 va_list var_copy;
362 g_return_if_fail(ADG_IS_CONTAINER(container));
364 children = adg_container_get_children(container);
366 while (children) {
367 if (children->data) {
368 G_VA_COPY(var_copy, var_args);
369 g_signal_emit_valist(children->data, signal_id, detail, var_copy);
372 children = g_slist_delete_link(children, children);
377 static GSList *
378 get_children(AdgContainer *container)
380 AdgContainerPrivate *data = container->data;
382 return g_slist_copy(data->children);
385 static void
386 add(AdgContainer *container, AdgEntity *entity)
388 AdgContainer *old_parent;
389 AdgContainerPrivate *data;
391 old_parent = adg_entity_get_parent(entity);
392 if (old_parent != NULL) {
393 g_warning("Attempting to add an entity with type %s to a container "
394 "of type %s, but the entity is already inside a container "
395 "of type %s.",
396 g_type_name(G_OBJECT_TYPE(entity)),
397 g_type_name(G_OBJECT_TYPE(container)),
398 g_type_name(G_OBJECT_TYPE(old_parent)));
399 return;
402 data = container->data;
403 data->children = g_slist_append(data->children, entity);
404 adg_entity_set_parent(entity, container);
407 static void
408 remove(AdgContainer *container, AdgEntity *entity)
410 AdgContainerPrivate *data;
411 GSList *node;
413 data = container->data;
414 node = g_slist_find(data->children, entity);
416 if (node == NULL) {
417 g_warning("Attempting to remove an entity with type %s from a "
418 "container of type %s, but the entity is not present.",
419 g_type_name(G_OBJECT_TYPE(entity)),
420 g_type_name(G_OBJECT_TYPE(container)));
421 return;
424 data->children = g_slist_delete_link(data->children, node);
425 adg_entity_unparent(entity);
429 static gboolean
430 invalidate(AdgEntity *entity)
432 adg_container_propagate_by_name((AdgContainer *) entity, "invalidate");
433 return TRUE;
436 static gboolean
437 render(AdgEntity *entity, cairo_t *cr)
439 adg_container_propagate_by_name((AdgContainer *) entity, "render", cr);
440 return TRUE;