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"
39 #include "adg-marshal.h"
42 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_container_parent_class)
43 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_container_parent_class)
58 static void dispose (GObject
*object
);
59 static void set_property (GObject
*object
,
63 static void global_changed (AdgEntity
*entity
);
64 static void local_changed (AdgEntity
*entity
);
65 static void invalidate (AdgEntity
*entity
);
66 static void arrange (AdgEntity
*entity
);
67 static void add_extents (AdgEntity
*entity
,
68 CpmlExtents
*container_extents
);
69 static void render (AdgEntity
*entity
,
71 static GSList
* get_children (AdgContainer
*container
);
72 static void add (AdgContainer
*container
,
74 static void remove (AdgContainer
*container
,
77 static guint signals
[LAST_SIGNAL
] = { 0 };
80 G_DEFINE_TYPE(AdgContainer
, adg_container
, ADG_TYPE_ENTITY
);
84 adg_container_class_init(AdgContainerClass
*klass
)
86 GObjectClass
*gobject_class
;
87 AdgEntityClass
*entity_class
;
90 gobject_class
= (GObjectClass
*) klass
;
91 entity_class
= (AdgEntityClass
*) klass
;
93 g_type_class_add_private(klass
, sizeof(AdgContainerPrivate
));
95 gobject_class
->dispose
= dispose
;
96 gobject_class
->set_property
= set_property
;
98 entity_class
->global_changed
= global_changed
;
99 entity_class
->local_changed
= local_changed
;
100 entity_class
->invalidate
= invalidate
;
101 entity_class
->arrange
= arrange
;
102 entity_class
->render
= render
;
104 klass
->get_children
= get_children
;
106 klass
->remove
= remove
;
108 param
= g_param_spec_object("child",
110 P_("Can be used to add a new child to the container"),
111 ADG_TYPE_ENTITY
, G_PARAM_WRITABLE
);
112 g_object_class_install_property(gobject_class
, PROP_CHILD
, param
);
116 * @container: an #AdgContainer
117 * @entity: the #AdgEntity to add
119 * Adds @entity to @container. @entity must not be inside another
120 * container or the operation will fail.
122 signals
[ADD
] = g_signal_new("add",
123 G_OBJECT_CLASS_TYPE(gobject_class
),
125 G_STRUCT_OFFSET(AdgContainerClass
, add
),
127 adg_marshal_VOID__OBJECT
,
128 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
131 * AdgContainer::remove:
132 * @container: an #AdgContainer
133 * @entity: the #AdgEntity to remove
135 * Removes @entity from @container.
137 signals
[REMOVE
] = g_signal_new("remove",
138 G_OBJECT_CLASS_TYPE(gobject_class
),
140 G_STRUCT_OFFSET(AdgContainerClass
, remove
),
142 adg_marshal_VOID__OBJECT
,
143 G_TYPE_NONE
, 1, ADG_TYPE_ENTITY
);
147 adg_container_init(AdgContainer
*container
)
149 AdgContainerPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(container
,
151 AdgContainerPrivate
);
153 data
->children
= NULL
;
155 container
->data
= data
;
159 dispose(GObject
*object
)
161 AdgContainer
*container
;
162 AdgContainerPrivate
*data
;
164 container
= (AdgContainer
*) object
;
165 data
= container
->data
;
167 /* Remove all the children from the container: these will emit
168 * a "remove" signal for every child and will drop all the
169 * references from the children to this container (and, obviously,
170 * from the container to the children). */
171 while (data
->children
!= NULL
)
172 adg_container_remove(container
, (AdgEntity
*) data
->children
->data
);
174 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
175 PARENT_OBJECT_CLASS
->dispose(object
);
179 set_property(GObject
*object
,
180 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
182 AdgContainer
*container
= (AdgContainer
*) object
;
186 adg_container_add(container
, g_value_get_object(value
));
189 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
197 * Creates a new container entity.
199 * Returns: the newly created container entity
202 adg_container_new(void)
204 return g_object_new(ADG_TYPE_CONTAINER
, NULL
);
210 * @container: an #AdgContainer
211 * @entity: an #AdgEntity
213 * Emits a #AdgContainer::add signal on @container passing @entity
214 * as argument. @entity must be added to only one container at a time,
215 * you can't place the same entity inside two different containers.
217 * Once @entity has been added, the floating reference will be removed
218 * and @container will own a reference to @entity. This means the only
219 * proper way to destroy @entity is to call adg_container_remove().
222 adg_container_add(AdgContainer
*container
, AdgEntity
*entity
)
224 g_return_if_fail(ADG_IS_CONTAINER(container
));
225 g_return_if_fail(ADG_IS_ENTITY(entity
));
227 g_signal_emit(container
, signals
[ADD
], 0, entity
);
231 * adg_container_remove:
232 * @container: an #AdgContainer
233 * @entity: an #AdgEntity
235 * Emits a #AdgContainer::remove signal on @container passing
236 * @entity as argument. @entity must be inside @container.
238 * Note that @container will own a reference to @entity and it
239 * may be the last reference held: this means removing an entity
240 * from its container can destroy it.
242 * If you want to use @entity again, you need to add a reference
243 * to it, using g_object_ref(), before removing it from @container.
244 * The following typical example shows you how to properly move
245 * <varname>entity</varname> from <varname>container1</varname>
246 * to <varname>container2</varname>:
249 * g_object_ref(entity);
250 * adg_container_remove(container1, entity);
251 * adg_container_add(container2, entity)
252 * g_object_unref(entity);
256 adg_container_remove(AdgContainer
*container
, AdgEntity
*entity
)
258 g_return_if_fail(ADG_IS_CONTAINER(container
));
259 g_return_if_fail(ADG_IS_ENTITY(entity
));
261 g_signal_emit(container
, signals
[REMOVE
], 0, entity
);
265 * adg_container_get_children:
266 * @container: an #AdgContainer
268 * Gets the children list of @container. This list must be manually
269 * freed with g_slist_free() when no longer user.
271 * The returned list is ordered from the most recently added child
274 * Returns: a newly allocated #GSList or %NULL empty list or on errors
277 adg_container_get_children(AdgContainer
*container
)
279 g_return_val_if_fail(ADG_IS_CONTAINER(container
), NULL
);
281 return ADG_CONTAINER_GET_CLASS(container
)->get_children(container
);
285 * adg_container_foreach:
286 * @container: an #AdgContainer
287 * @callback: a callback
288 * @user_data: callback user data
290 * Invokes @callback on each child of @container.
291 * The callback should be declared as:
294 * void callback(AdgEntity *entity, gpointer user_data);
298 adg_container_foreach(AdgContainer
*container
,
299 GCallback callback
, gpointer user_data
)
303 g_return_if_fail(ADG_IS_CONTAINER(container
));
304 g_return_if_fail(callback
!= NULL
);
306 children
= adg_container_get_children(container
);
308 while (children
!= NULL
) {
309 if (children
->data
!= NULL
)
310 ((void (*) (gpointer
, gpointer
)) callback
) (children
->data
, user_data
);
312 children
= g_slist_delete_link(children
, children
);
317 * adg_container_propagate:
318 * @container: an #AdgContainer
319 * @signal_id: the signal id
320 * @detail: the detail
321 * @...: parameters to be passed to the signal, followed by a pointer
322 * to the allocated memory where to store the return type: if
323 * the signal is %G_TYPE_NONE (void return type), this trailing
324 * pointer should be omitted
326 * Emits the specified signal to all the children of @container
327 * using g_signal_emit_valist() calls.
330 adg_container_propagate(AdgContainer
*container
,
331 guint signal_id
, GQuark detail
, ...)
335 va_start(var_args
, detail
);
336 adg_container_propagate_valist(container
, signal_id
, detail
, var_args
);
341 * adg_container_propagate_by_name:
342 * @container: an #AdgContainer
343 * @detailed_signal: a string of the form "signal-name::detail".
344 * @...: parameters to be passed to the signal, followed by a pointer
345 * to the allocated memory where to store the return type: if
346 * the signal is %G_TYPE_NONE (void return type), this trailing
347 * pointer should be omitted
349 * Emits the specified signal to all the children of @container
350 * using g_signal_emit_valist() calls.
353 adg_container_propagate_by_name(AdgContainer
*container
,
354 const gchar
*detailed_signal
, ...)
360 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(container
),
361 &signal_id
, &detail
, FALSE
)) {
362 g_warning("%s: signal `%s' is invalid for instance `%p'",
363 G_STRLOC
, detailed_signal
, container
);
367 va_start(var_args
, detailed_signal
);
368 adg_container_propagate_valist(container
, signal_id
, detail
, var_args
);
373 * adg_container_propagate_valist:
374 * @container: an #AdgContainer
375 * @signal_id: the signal id
376 * @detail: the detail
377 * @var_args: parameters to be passed to the signal, followed by a
378 * pointer to the allocated memory where to store the
379 * return type: if the signal is %G_TYPE_NONE (void return
380 * type), this trailing pointer should be omitted
382 * Emits the specified signal to all the children of @container
383 * using g_signal_emit_valist() calls.
386 adg_container_propagate_valist(AdgContainer
*container
,
387 guint signal_id
, GQuark detail
, va_list var_args
)
392 g_return_if_fail(ADG_IS_CONTAINER(container
));
394 children
= adg_container_get_children(container
);
396 while (children
!= NULL
) {
397 if (children
->data
!= NULL
) {
398 G_VA_COPY(var_copy
, var_args
);
399 g_signal_emit_valist(children
->data
, signal_id
, detail
, var_copy
);
402 children
= g_slist_delete_link(children
, children
);
408 global_changed(AdgEntity
*entity
)
410 PARENT_ENTITY_CLASS
->global_changed(entity
);
411 adg_container_propagate_by_name((AdgContainer
*) entity
, "global-changed");
415 local_changed(AdgEntity
*entity
)
417 PARENT_ENTITY_CLASS
->local_changed(entity
);
418 adg_container_propagate_by_name((AdgContainer
*) entity
, "local-changed");
422 invalidate(AdgEntity
*entity
)
424 adg_container_propagate_by_name((AdgContainer
*) entity
, "invalidate");
428 arrange(AdgEntity
*entity
)
430 AdgContainer
*container
= (AdgContainer
*) entity
;
431 CpmlExtents extents
= { 0 };
433 adg_container_propagate_by_name(container
, "arrange", NULL
);
434 adg_container_foreach(container
, G_CALLBACK(add_extents
), &extents
);
435 adg_entity_set_extents(entity
, &extents
);
439 add_extents(AdgEntity
*entity
, CpmlExtents
*container_extents
)
444 adg_entity_get_extents(entity
, &extents
);
445 adg_entity_get_global_map(entity
, &map
);
447 cairo_matrix_transform_point(&map
, &extents
.org
.x
, &extents
.org
.y
);
448 cairo_matrix_transform_distance(&map
, &extents
.size
.x
, &extents
.size
.y
);
449 cpml_extents_add(container_extents
, &extents
);
453 render(AdgEntity
*entity
, cairo_t
*cr
)
455 adg_container_propagate_by_name((AdgContainer
*) entity
, "render", cr
);
460 get_children(AdgContainer
*container
)
462 AdgContainerPrivate
*data
= container
->data
;
464 /* The NULL case is yet managed by GLib */
465 return g_slist_copy(data
->children
);
469 add(AdgContainer
*container
, AdgEntity
*entity
)
471 AdgEntity
*old_parent
;
472 AdgContainerPrivate
*data
;
474 old_parent
= adg_entity_get_parent(entity
);
475 if (old_parent
!= NULL
) {
476 g_warning("Attempting to add an entity with type %s to a container "
477 "of type %s, but the entity is already inside a container "
479 g_type_name(G_OBJECT_TYPE(entity
)),
480 g_type_name(G_OBJECT_TYPE(container
)),
481 g_type_name(G_OBJECT_TYPE(old_parent
)));
485 data
= container
->data
;
486 data
->children
= g_slist_prepend(data
->children
, entity
);
488 g_object_ref_sink(entity
);
489 adg_entity_set_parent(entity
, (AdgEntity
*) container
);
493 remove(AdgContainer
*container
, AdgEntity
*entity
)
495 AdgContainerPrivate
*data
;
498 data
= container
->data
;
499 node
= g_slist_find(data
->children
, entity
);
502 g_warning("Attempting to remove an entity with type %s from a "
503 "container of type %s, but the entity is not present.",
504 g_type_name(G_OBJECT_TYPE(entity
)),
505 g_type_name(G_OBJECT_TYPE(container
)));
509 data
->children
= g_slist_delete_link(data
->children
, node
);
510 adg_entity_set_parent(entity
, NULL
);
511 g_object_unref(entity
);