[AdgModel] Added "clear" signal
[adg.git] / adg / adg-model.c
blobe191d309424c398b3719d7225ccc69b4aa9f0b56
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-model
23 * @short_description: The base class of the ADG model infrastructure
25 * A model is a conceptual representation of something. From an ADG
26 * user point of view, it is a collection of data and rules that
27 * defines how an object should be represented on a drawing.
29 * Because #AdgModel instances are only a conceptual idea, they are
30 * not renderable (that is, #AdgModel is not derived from #AdgEntity).
31 * Instead, it must be passed as subject to entities such as #AdgStroke
32 * or #AdgHatch.
33 **/
35 /**
36 * AdgModel:
38 * All fields are private and should not be used directly.
39 * Use its public methods instead.
40 **/
43 #include "adg-model.h"
44 #include "adg-model-private.h"
45 #include "adg-marshal.h"
46 #include "adg-intl.h"
48 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_model_parent_class)
51 enum {
52 PROP_0,
53 PROP_DEPENDENCY
56 enum {
57 ADD_DEPENDENCY,
58 REMOVE_DEPENDENCY,
59 CLEAR,
60 CHANGED,
61 LAST_SIGNAL
65 static void dispose (GObject *object);
66 static void set_property (GObject *object,
67 guint prop_id,
68 const GValue *value,
69 GParamSpec *pspec);
71 static GSList * get_dependencies (AdgModel *model);
72 static void add_dependency (AdgModel *model,
73 AdgEntity *entity);
74 static void remove_dependency (AdgModel *model,
75 AdgEntity *entity);
76 static void changed (AdgModel *model);
78 static guint signals[LAST_SIGNAL] = { 0 };
81 G_DEFINE_ABSTRACT_TYPE(AdgModel, adg_model, G_TYPE_OBJECT);
84 static void
85 adg_model_class_init(AdgModelClass *klass)
87 GObjectClass *gobject_class;
88 GParamSpec *param;
90 gobject_class = (GObjectClass *) klass;
92 g_type_class_add_private(klass, sizeof(AdgModelPrivate));
94 gobject_class->dispose = dispose;
95 gobject_class->set_property = set_property;
97 klass->get_dependencies = get_dependencies;
98 klass->add_dependency = add_dependency;
99 klass->remove_dependency = remove_dependency;
100 klass->clear = NULL;
101 klass->changed = changed;
103 param = g_param_spec_object("dependency",
104 P_("Dependency"),
105 P_("Can be used to add a new dependency from this model (this entity will be invalidated on model changed)"),
106 ADG_TYPE_ENTITY, G_PARAM_WRITABLE);
107 g_object_class_install_property(gobject_class, PROP_DEPENDENCY, param);
110 * AdgModel::add-dependency:
111 * @model: an #AdgModel
112 * @entity: an #AdgEntity that depends on @model
114 * Adds @entity to @model. After that @entity will depend on @model,
115 * that is #AdgModel::changed on @model will invalidate @entity.
117 signals[ADD_DEPENDENCY] = g_signal_new("add-dependency",
118 G_OBJECT_CLASS_TYPE(gobject_class),
119 G_SIGNAL_RUN_FIRST,
120 G_STRUCT_OFFSET(AdgModelClass, add_dependency),
121 NULL, NULL,
122 adg_marshal_VOID__OBJECT,
123 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
126 * AdgModel::remove-dependency:
127 * @model: an #AdgModel
128 * @entity: the #AdgEntity that does not depend on @model anymore
130 * Removes the @entity from @model, that is @entity will not depend
131 * on @model anymore.
133 signals[REMOVE_DEPENDENCY] = g_signal_new("remove-dependency",
134 G_OBJECT_CLASS_TYPE(gobject_class),
135 G_SIGNAL_RUN_FIRST,
136 G_STRUCT_OFFSET(AdgModelClass, remove_dependency),
137 NULL, NULL,
138 adg_marshal_VOID__OBJECT,
139 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
142 * AdgModel::clear:
143 * @model: an #AdgModel
145 * Removes any cached information from @model.
147 signals[CLEAR] = g_signal_new("clear", ADG_TYPE_MODEL,
148 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
149 G_STRUCT_OFFSET(AdgModelClass, clear),
150 NULL, NULL,
151 adg_marshal_VOID__VOID,
152 G_TYPE_NONE, 0);
155 * AdgModel::changed:
156 * @model: an #AdgModel
158 * Notificates that the model has changed. By default, all the
159 * dependent entities are invalidated.
161 signals[CHANGED] = g_signal_new("changed", ADG_TYPE_MODEL,
162 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
163 G_STRUCT_OFFSET(AdgModelClass, changed),
164 NULL, NULL,
165 adg_marshal_VOID__VOID,
166 G_TYPE_NONE, 0);
169 static void
170 adg_model_init(AdgModel *model)
172 AdgModelPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(model, ADG_TYPE_MODEL,
173 AdgModelPrivate);
175 data->dependencies = NULL;
177 model->data = data;
180 static void
181 dispose(GObject *object)
183 AdgModel *model;
184 AdgModelPrivate *data;
186 model = (AdgModel *) object;
187 data = model->data;
189 /* Remove all the dependencies from the model: this will emit
190 * a "remove" signal for every dependency and will drop all the
191 * references from those entities to this model */
192 while (data->dependencies != NULL)
193 adg_model_remove_dependency(model,
194 (AdgEntity *) data->dependencies->data);
196 if (PARENT_OBJECT_CLASS->dispose != NULL)
197 PARENT_OBJECT_CLASS->dispose(object);
200 static void
201 set_property(GObject *object,
202 guint prop_id, const GValue *value, GParamSpec *pspec)
204 AdgModel *model = (AdgModel *) object;
206 switch (prop_id) {
207 case PROP_DEPENDENCY:
208 adg_model_add_dependency(model, g_value_get_object(value));
209 break;
210 default:
211 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
217 * adg_model_add_dependency:
218 * @model: an #AdgModel
219 * @entity: an #AdgEntity
221 * <note><para>
222 * This function is only useful in entity implementations.
223 * </para></note>
225 * Emits a #AdgModel::add-dependency signal on @model passing @entity
226 * as argument. This will add a reference to @entity owned by @model.
228 void
229 adg_model_add_dependency(AdgModel *model, AdgEntity *entity)
231 g_return_if_fail(ADG_IS_MODEL(model));
232 g_return_if_fail(ADG_IS_ENTITY(entity));
234 g_signal_emit(model, signals[ADD_DEPENDENCY], 0, entity);
238 * adg_model_remove_dependency:
239 * @model: an #AdgModel
240 * @entity: an #AdgEntity
242 * <note><para>
243 * This function is only useful in entity implementations.
244 * </para></note>
246 * Emits a #AdgModel::remove-dependency signal on @model passing
247 * @entity as argument. @entity must be inside @model.
249 * Note that @model will own a reference to @entity and it
250 * may be the last reference held: this means removing an entity
251 * from the model can destroy it.
253 void
254 adg_model_remove_dependency(AdgModel *model, AdgEntity *entity)
256 g_return_if_fail(ADG_IS_MODEL(model));
257 g_return_if_fail(ADG_IS_ENTITY(entity));
259 g_signal_emit(model, signals[REMOVE_DEPENDENCY], 0, entity);
263 * adg_model_get_dependencies:
264 * @model: an #AdgModel
266 * Gets a list of entities dependent on @model.
267 * This list must be freed with g_slist_free() when no longer user.
269 * Returns: a newly allocated #GSList or %NULL on error
271 GSList *
272 adg_model_get_dependencies(AdgModel *model)
274 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
276 return ADG_MODEL_GET_CLASS(model)->get_dependencies(model);
280 * adg_model_foreach_dependency:
281 * @model: an #AdgModel
282 * @callback: a callback
283 * @user_data: callback user data
285 * Invokes @callback on each entity linked to @model.
286 * The callback should be declared as:
288 * |[
289 * void callback(AdgEntity *entity, gpointer user_data);
290 * ]|
292 void
293 adg_model_foreach_dependency(AdgModel *model, GCallback callback,
294 gpointer user_data)
296 GSList *dependencies;
298 g_return_if_fail(ADG_IS_MODEL(model));
299 g_return_if_fail(callback != NULL);
301 dependencies = adg_model_get_dependencies(model);
303 while (dependencies) {
304 if (dependencies->data)
305 ((void (*) (gpointer, gpointer)) callback) (dependencies->data,
306 user_data);
308 dependencies = g_slist_delete_link(dependencies, dependencies);
313 * adg_model_clear:
314 * @model: an #AdgModel
316 * <note><para>
317 * This function is only useful in entity implementations.
318 * </para></note>
320 * Emits the #AdgModel::clear signal on @model.
322 void
323 adg_model_clear(AdgModel *model)
325 g_return_if_fail(ADG_IS_MODEL(model));
327 g_signal_emit(model, signals[CLEAR], 0);
331 * adg_model_changed:
332 * @model: an #AdgModel
334 * <note><para>
335 * This function is only useful in entity implementations.
336 * </para></note>
338 * Emits the #AdgModel::changed signal on @model.
340 void
341 adg_model_changed(AdgModel *model)
343 g_return_if_fail(ADG_IS_MODEL(model));
345 g_signal_emit(model, signals[CHANGED], 0);
349 static void
350 add_dependency(AdgModel *model, AdgEntity *entity)
352 AdgModelPrivate *data = model->data;
354 /* The prepend operation is more efficient */
355 data->dependencies = g_slist_prepend(data->dependencies, entity);
357 g_object_ref(entity);
360 static void
361 remove_dependency(AdgModel *model, AdgEntity *entity)
363 AdgModelPrivate *data;
364 GSList *node;
366 data = model->data;
367 node = g_slist_find(data->dependencies, entity);
369 if (node == NULL) {
370 g_warning("Attempting to remove an entity with type %s from a "
371 "model of type %s, but the entity is not present.",
372 g_type_name(G_OBJECT_TYPE(entity)),
373 g_type_name(G_OBJECT_TYPE(model)));
374 return;
377 data->dependencies = g_slist_delete_link(data->dependencies, node);
378 g_object_unref(entity);
381 static GSList *
382 get_dependencies(AdgModel *model)
384 AdgModelPrivate *data = model->data;
386 /* The NULL case is yet managed by GLib */
387 return g_slist_copy(data->dependencies);
390 static void
391 changed(AdgModel *model)
393 /* Invalidate all the entities dependent on this model */
394 adg_model_foreach_dependency(model,
395 G_CALLBACK(adg_entity_invalidate), NULL);