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.
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.
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
37 #include "adg-container.h"
38 #include "adg-container-private.h"
41 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_container_parent_class)
56 static void set_property (GObject
*object
,
60 static void dispose (GObject
*object
);
61 static GSList
* get_children (AdgContainer
*container
);
62 static void add (AdgContainer
*container
,
64 static void remove (AdgContainer
*container
,
66 static void invalidate (AdgEntity
*entity
);
67 static void render (AdgEntity
*entity
,
70 static guint signals
[LAST_SIGNAL
] = { 0 };
73 G_DEFINE_TYPE(AdgContainer
, adg_container
, ADG_TYPE_ENTITY
)
77 adg_container_class_init(AdgContainerClass
*klass
)
79 GObjectClass
*gobject_class
;
80 AdgEntityClass
*entity_class
;
83 gobject_class
= (GObjectClass
*) klass
;
84 entity_class
= (AdgEntityClass
*) klass
;
86 g_type_class_add_private(klass
, sizeof(AdgContainerPrivate
));
88 gobject_class
->set_property
= set_property
;
89 gobject_class
->dispose
= dispose
;
91 entity_class
->invalidate
= invalidate
;
92 entity_class
->render
= render
;
94 klass
->get_children
= get_children
;
96 klass
->remove
= remove
;
98 param
= g_param_spec_object("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
);
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
),
115 G_STRUCT_OFFSET(AdgContainerClass
, add
),
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
),
130 G_STRUCT_OFFSET(AdgContainerClass
, remove
),
132 g_cclosure_marshal_VOID__OBJECT
,
133 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
137 adg_container_init(AdgContainer
*container
)
139 AdgContainerPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(container
,
141 AdgContainerPrivate
);
143 data
->children
= NULL
;
145 container
->data
= data
;
149 set_property(GObject
*object
,
150 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
152 AdgContainer
*container
;
153 AdgContainerPrivate
*data
;
155 container
= (AdgContainer
*) object
;
156 data
= container
->data
;
160 adg_container_add(container
, g_value_get_object(value
));
163 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
168 dispose(GObject
*object
)
170 GObjectClass
*object_class
= (GObjectClass
*) adg_container_parent_class
;
172 adg_container_foreach((AdgContainer
*) object
,
173 G_CALLBACK(adg_entity_unparent
), NULL
);
175 if (object_class
->dispose
!= NULL
)
176 object_class
->dispose(object
);
181 get_children(AdgContainer
*container
)
183 AdgContainerPrivate
*data
= container
->data
;
185 return g_slist_copy(data
->children
);
189 add(AdgContainer
*container
, AdgEntity
*entity
)
191 AdgContainer
*old_parent
;
192 AdgContainerPrivate
*data
;
194 old_parent
= adg_entity_get_parent(entity
);
195 if (old_parent
!= NULL
) {
196 g_warning("Attempting to add an entity with type %s to a container "
197 "of type %s, but the entity is already inside a container "
199 g_type_name(G_OBJECT_TYPE(entity
)),
200 g_type_name(G_OBJECT_TYPE(container
)),
201 g_type_name(G_OBJECT_TYPE(old_parent
)));
205 data
= container
->data
;
206 data
->children
= g_slist_append(data
->children
, entity
);
207 adg_entity_set_parent(entity
, container
);
211 remove(AdgContainer
*container
, AdgEntity
*entity
)
213 AdgContainerPrivate
*data
;
216 data
= container
->data
;
217 node
= g_slist_find(data
->children
, entity
);
220 g_warning("Attempting to remove an entity with type %s from a "
221 "container of type %s, but the entity is not present.",
222 g_type_name(G_OBJECT_TYPE(entity
)),
223 g_type_name(G_OBJECT_TYPE(container
)));
227 data
->children
= g_slist_delete_link(data
->children
, node
);
228 adg_entity_unparent(entity
);
233 invalidate(AdgEntity
*entity
)
235 adg_container_propagate_by_name((AdgContainer
*) entity
, "invalidate");
237 if (PARENT_ENTITY_CLASS
->invalidate
)
238 PARENT_ENTITY_CLASS
->invalidate(entity
);
242 render(AdgEntity
*entity
, cairo_t
*cr
)
244 adg_container_propagate_by_name((AdgContainer
*) entity
, "render", cr
);
246 if (PARENT_ENTITY_CLASS
->render
)
247 PARENT_ENTITY_CLASS
->render(entity
, cr
);
254 * Creates a new container entity.
256 * Return value: the newly created entity
259 adg_container_new(void)
261 return (AdgEntity
*) g_object_new(ADG_TYPE_CONTAINER
, NULL
);
267 * @container: an #AdgContainer
268 * @entity: an #AdgEntity
270 * Emits a #AdgContainer::add signal on @container passing
271 * @entity as argument.
273 * @entity may be added to only one container at a time; you can't
274 * place the same entity inside two different containers.
277 adg_container_add(AdgContainer
*container
, AdgEntity
*entity
)
279 g_return_if_fail(ADG_IS_CONTAINER(container
));
280 g_return_if_fail(ADG_IS_ENTITY(entity
));
282 g_signal_emit(container
, signals
[ADD
], 0, entity
);
286 * adg_container_remove:
287 * @container: an #AdgContainer
288 * @entity: an #AdgEntity
290 * Emits a #AdgContainer::remove signal on @container passing
291 * @entity as argument. @entity must be inside @container.
293 * Note that @container will own a reference to @entity
294 * and that this may be the last reference held; so removing an
295 * entity from its container can destroy it.
297 * If you want to use @entity again, you need to add a reference
298 * to it, using g_object_ref(), before removing it from @container.
300 * If you don't want to use @entity again, it's usually more
301 * efficient to simply destroy it directly using g_object_unref()
302 * since this will remove it from the container.
305 adg_container_remove(AdgContainer
*container
, AdgEntity
*entity
)
307 g_return_if_fail(ADG_IS_CONTAINER(container
));
308 g_return_if_fail(ADG_IS_ENTITY(entity
));
310 g_signal_emit(container
, signals
[REMOVE
], 0, entity
);
314 * adg_container_get_children:
315 * @container: an #AdgContainer
317 * Gets the children list of @container.
318 * This list must be manually freed when no longer user.
320 * Returns: a newly allocated #GSList or %NULL on error
323 adg_container_get_children(AdgContainer
*container
)
325 g_return_val_if_fail(ADG_IS_CONTAINER(container
), NULL
);
327 return ADG_CONTAINER_GET_CLASS(container
)->get_children(container
);
331 * adg_container_foreach:
332 * @container: an #AdgContainer
333 * @callback: a callback
334 * @user_data: callback user data
336 * Invokes @callback on each child of @container.
337 * The callback should be declared as:
340 * void callback(AdgEntity *entity, gpointer user_data);
344 adg_container_foreach(AdgContainer
*container
,
345 GCallback callback
, gpointer user_data
)
349 g_return_if_fail(ADG_IS_CONTAINER(container
));
350 g_return_if_fail(callback
!= NULL
);
352 children
= adg_container_get_children (container
);
356 ((void (*) (gpointer
, gpointer
)) callback
) (children
->data
, user_data
);
358 children
= g_slist_delete_link(children
, children
);
363 * adg_container_propagate:
364 * @container: an #AdgContainer
365 * @signal_id: the signal id
366 * @detail: the detail
367 * @...: parameters to be passed to the signal, followed by a location for
368 * the return value. If the return type of the signal is G_TYPE_NONE,
369 * the return value location can be omitted.
371 * Emits the specified signal to all the children of @container
372 * using g_signal_emit_valist() calls.
375 adg_container_propagate(AdgContainer
*container
,
376 guint signal_id
, GQuark detail
, ...)
380 va_start(var_args
, detail
);
381 adg_container_propagate_valist(container
, signal_id
, detail
, var_args
);
386 * adg_container_propagate_by_name:
387 * @container: an #AdgContainer
388 * @detailed_signal: a string of the form "signal-name::detail".
389 * @...: a list of parameters to be passed to the signal, followed by
390 * a location for the return value. If the return type of the signal
391 * is G_TYPE_NONE, the return value location can be omitted.
393 * Emits the specified signal to all the children of @container
394 * using g_signal_emit_valist() calls.
397 adg_container_propagate_by_name(AdgContainer
*container
,
398 const gchar
*detailed_signal
, ...)
404 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(container
),
405 &signal_id
, &detail
, FALSE
)) {
406 g_warning("%s: signal `%s' is invalid for instance `%p'",
407 G_STRLOC
, detailed_signal
, container
);
411 va_start(var_args
, detailed_signal
);
412 adg_container_propagate_valist(container
, signal_id
, detail
, var_args
);
417 * adg_container_propagate_valist:
418 * @container: an #AdgContainer
419 * @signal_id: the signal id
420 * @detail: the detail
421 * @var_args: a list of parameters to be passed to the signal, followed by a
422 * location for the return value. If the return type of the signal
423 * is G_TYPE_NONE, the return value location can be omitted.
425 * Emits the specified signal to all the children of @container
426 * using g_signal_emit_valist() calls.
429 adg_container_propagate_valist(AdgContainer
*container
,
430 guint signal_id
, GQuark detail
, va_list var_args
)
435 g_return_if_fail(ADG_IS_CONTAINER(container
));
437 children
= adg_container_get_children(container
);
440 if (children
->data
) {
441 G_VA_COPY(var_copy
, var_args
);
442 g_signal_emit_valist(children
->data
, signal_id
, detail
, var_copy
);
445 children
= g_slist_delete_link(children
, children
);