[AdgModel] Changed API to adg_model_named_pair()
[adg.git] / adg / adg-model.c
blobc69ca9ee789dce364b9929500a41019cb6d4482e
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 SET_NAMED_PAIR,
60 CLEAR,
61 CHANGED,
62 LAST_SIGNAL
66 static void dispose (GObject *object);
67 static void set_property (GObject *object,
68 guint prop_id,
69 const GValue *value,
70 GParamSpec *pspec);
71 static void add_dependency (AdgModel *model,
72 AdgEntity *entity);
73 static void remove_dependency (AdgModel *model,
74 AdgEntity *entity);
75 static const AdgPair *
76 named_pair (AdgModel *model,
77 const gchar *name);
78 static void set_named_pair (AdgModel *model,
79 const gchar *name,
80 const AdgPair *pair);
81 static void changed (AdgModel *model);
83 static guint signals[LAST_SIGNAL] = { 0 };
86 G_DEFINE_ABSTRACT_TYPE(AdgModel, adg_model, G_TYPE_OBJECT);
89 static void
90 adg_model_class_init(AdgModelClass *klass)
92 GObjectClass *gobject_class;
93 GParamSpec *param;
95 gobject_class = (GObjectClass *) klass;
97 g_type_class_add_private(klass, sizeof(AdgModelPrivate));
99 gobject_class->dispose = dispose;
100 gobject_class->set_property = set_property;
102 klass->add_dependency = add_dependency;
103 klass->remove_dependency = remove_dependency;
104 klass->named_pair = named_pair;
105 klass->set_named_pair = set_named_pair;
106 klass->clear = NULL;
107 klass->changed = changed;
109 param = g_param_spec_object("dependency",
110 P_("Dependency"),
111 P_("Can be used to add a new dependency from this model (this entity will be invalidated on model changed)"),
112 ADG_TYPE_ENTITY,
113 G_PARAM_WRITABLE);
114 g_object_class_install_property(gobject_class, PROP_DEPENDENCY, param);
117 * AdgModel::add-dependency:
118 * @model: an #AdgModel
119 * @entity: an #AdgEntity that depends on @model
121 * Adds @entity to @model. After that @entity will depend on @model,
122 * that is #AdgModel::changed on @model will invalidate @entity.
124 signals[ADD_DEPENDENCY] = g_signal_new("add-dependency",
125 G_OBJECT_CLASS_TYPE(gobject_class),
126 G_SIGNAL_RUN_FIRST,
127 G_STRUCT_OFFSET(AdgModelClass, add_dependency),
128 NULL, NULL,
129 adg_marshal_VOID__OBJECT,
130 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
133 * AdgModel::remove-dependency:
134 * @model: an #AdgModel
135 * @entity: the #AdgEntity that does not depend on @model anymore
137 * Removes the @entity from @model, that is @entity will not depend
138 * on @model anymore.
140 signals[REMOVE_DEPENDENCY] = g_signal_new("remove-dependency",
141 G_OBJECT_CLASS_TYPE(gobject_class),
142 G_SIGNAL_RUN_FIRST,
143 G_STRUCT_OFFSET(AdgModelClass, remove_dependency),
144 NULL, NULL,
145 adg_marshal_VOID__OBJECT,
146 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
149 * AdgModel::set-named-pair:
150 * @model: an #AdgModel
151 * @name: an arbitrary name
152 * @pair: an #AdgPair
154 * Adds, updates or deletes a named pair, accordling to the given
155 * parameters.
157 * If @pair is %NULL, the @name named pair is searched and deleted.
158 * If it is not found, a warning is raised.
160 * Otherwise, the @name named pair is searched: if it is found,
161 * its data are updated with @pair. If it is not found, a new
162 * named pair is created using @name and @pair.
164 signals[SET_NAMED_PAIR] = g_signal_new("set-named-pair",
165 G_OBJECT_CLASS_TYPE(gobject_class),
166 G_SIGNAL_RUN_FIRST,
167 G_STRUCT_OFFSET(AdgModelClass, set_named_pair),
168 NULL, NULL,
169 adg_marshal_VOID__STRING_POINTER,
170 G_TYPE_NONE, 2,
171 G_TYPE_STRING, G_TYPE_POINTER);
174 * AdgModel::clear:
175 * @model: an #AdgModel
177 * Removes any cached information from @model.
179 signals[CLEAR] = g_signal_new("clear", ADG_TYPE_MODEL,
180 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
181 G_STRUCT_OFFSET(AdgModelClass, clear),
182 NULL, NULL,
183 adg_marshal_VOID__VOID,
184 G_TYPE_NONE, 0);
187 * AdgModel::changed:
188 * @model: an #AdgModel
190 * Notificates that the model has changed. By default, all the
191 * dependent entities are invalidated.
193 signals[CHANGED] = g_signal_new("changed", ADG_TYPE_MODEL,
194 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
195 G_STRUCT_OFFSET(AdgModelClass, changed),
196 NULL, NULL,
197 adg_marshal_VOID__VOID,
198 G_TYPE_NONE, 0);
201 static void
202 adg_model_init(AdgModel *model)
204 AdgModelPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(model, ADG_TYPE_MODEL,
205 AdgModelPrivate);
207 data->dependencies = NULL;
209 model->data = data;
212 static void
213 dispose(GObject *object)
215 AdgModel *model;
216 AdgModelPrivate *data;
217 AdgEntity *entity;
219 model = (AdgModel *) object;
220 data = model->data;
222 /* Remove all the dependencies from the model: this will emit
223 * a "remove" signal for every dependency and will drop all the
224 * references from those entities to this model */
225 while (data->dependencies != NULL) {
226 entity = (AdgEntity *) data->dependencies->data;
227 adg_model_remove_dependency(model, entity);
230 if (PARENT_OBJECT_CLASS->dispose != NULL)
231 PARENT_OBJECT_CLASS->dispose(object);
234 static void
235 set_property(GObject *object,
236 guint prop_id, const GValue *value, GParamSpec *pspec)
238 AdgModel *model = (AdgModel *) object;
240 switch (prop_id) {
241 case PROP_DEPENDENCY:
242 adg_model_add_dependency(model, g_value_get_object(value));
243 break;
244 default:
245 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
251 * adg_model_add_dependency:
252 * @model: an #AdgModel
253 * @entity: an #AdgEntity
255 * <note><para>
256 * This function is only useful in entity implementations.
257 * </para></note>
259 * Emits a #AdgModel::add-dependency signal on @model passing @entity
260 * as argument. This will add a reference to @entity owned by @model.
262 void
263 adg_model_add_dependency(AdgModel *model, AdgEntity *entity)
265 g_return_if_fail(ADG_IS_MODEL(model));
266 g_return_if_fail(ADG_IS_ENTITY(entity));
268 g_signal_emit(model, signals[ADD_DEPENDENCY], 0, entity);
272 * adg_model_remove_dependency:
273 * @model: an #AdgModel
274 * @entity: an #AdgEntity
276 * <note><para>
277 * This function is only useful in entity implementations.
278 * </para></note>
280 * Emits a #AdgModel::remove-dependency signal on @model passing
281 * @entity as argument. @entity must be inside @model.
283 * Note that @model will own a reference to @entity and it
284 * may be the last reference held: this means removing an entity
285 * from the model can destroy it.
287 void
288 adg_model_remove_dependency(AdgModel *model, AdgEntity *entity)
290 g_return_if_fail(ADG_IS_MODEL(model));
291 g_return_if_fail(ADG_IS_ENTITY(entity));
293 g_signal_emit(model, signals[REMOVE_DEPENDENCY], 0, entity);
297 * adg_model_dependencies:
298 * @model: an #AdgModel
300 * Gets the list of entities dependending on @model. This list
301 * is owned by @model and must not be modified or freed.
303 * Returns: a #GSList of dependencies or %NULL on error
305 const GSList *
306 adg_model_dependencies(AdgModel *model)
308 AdgModelPrivate *data;
310 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
312 data = model->data;
314 return data->dependencies;
318 * adg_model_foreach_dependency:
319 * @model: an #AdgModel
320 * @callback: the entity callback
321 * @user_data: callback general purpose user data
323 * Invokes @callback on each entity linked to @model.
325 void
326 adg_model_foreach_dependency(AdgModel *model, AdgEntityCallback callback,
327 gpointer user_data)
329 AdgModelPrivate *data;
330 GSList *dependency;
331 AdgEntity *entity;
333 g_return_if_fail(ADG_IS_MODEL(model));
334 g_return_if_fail(callback != NULL);
336 data = model->data;
337 dependency = data->dependencies;
339 while (dependency != NULL) {
340 entity = dependency->data;
342 if (entity != NULL && ADG_IS_ENTITY(entity))
343 callback(entity, user_data);
345 dependency = dependency->next;
350 * adg_model_set_named_pair:
351 * @model: an #AdgModel
352 * @name: the name to associate to the pair
353 * @pair: the #AdgPair
355 * <note><para>
356 * This function is only useful in model definitions, such as
357 * inside an #AdgTrailCallback function or while constructing
358 * an #AdgPath instance.
359 * </para></note>
361 * Emits a #AdgModel::set-named-pair signal on @model passing
362 * @name and @pair as arguments.
364 void
365 adg_model_set_named_pair(AdgModel *model, const gchar *name,
366 const AdgPair *pair)
368 g_return_if_fail(ADG_IS_MODEL(model));
369 g_return_if_fail(name != NULL);
371 g_signal_emit(model, signals[SET_NAMED_PAIR], 0, name, pair);
375 * adg_model_named_pair:
376 * @model: an #AdgModel
377 * @name: the name of the pair to get
379 * Gets the @name named pair associated to @model. The returned
380 * pair is owned by @model and must not be modified or freed.
382 * Returns: the requested #AdgPair or %NULL if not found
384 const AdgPair *
385 adg_model_named_pair(AdgModel *model, const gchar *name)
387 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
388 g_return_val_if_fail(name != NULL, NULL);
390 return ADG_MODEL_GET_CLASS(model)->named_pair(model, name);
394 * adg_model_clear:
395 * @model: an #AdgModel
397 * <note><para>
398 * This function is only useful in entity implementations.
399 * </para></note>
401 * Emits the #AdgModel::clear signal on @model.
403 void
404 adg_model_clear(AdgModel *model)
406 g_return_if_fail(ADG_IS_MODEL(model));
408 g_signal_emit(model, signals[CLEAR], 0);
412 * adg_model_changed:
413 * @model: an #AdgModel
415 * <note><para>
416 * This function is only useful in entity implementations.
417 * </para></note>
419 * Emits the #AdgModel::changed signal on @model.
421 void
422 adg_model_changed(AdgModel *model)
424 g_return_if_fail(ADG_IS_MODEL(model));
426 g_signal_emit(model, signals[CHANGED], 0);
430 static void
431 add_dependency(AdgModel *model, AdgEntity *entity)
433 AdgModelPrivate *data = model->data;
435 /* The prepend operation is more efficient */
436 data->dependencies = g_slist_prepend(data->dependencies, entity);
438 g_object_ref(entity);
441 static void
442 remove_dependency(AdgModel *model, AdgEntity *entity)
444 AdgModelPrivate *data;
445 GSList *node;
447 data = model->data;
448 node = g_slist_find(data->dependencies, entity);
450 if (node == NULL) {
451 g_warning("%s: attempting to remove an entity with type %s "
452 "from a model of type %s, but the entity is not present.",
453 G_STRLOC,
454 g_type_name(G_OBJECT_TYPE(entity)),
455 g_type_name(G_OBJECT_TYPE(model)));
456 return;
459 data->dependencies = g_slist_delete_link(data->dependencies, node);
460 g_object_unref(entity);
463 static void
464 set_named_pair(AdgModel *model, const gchar *name, const AdgPair *pair)
466 AdgModelPrivate *data;
467 GHashTable **hash;
468 gchar *key;
469 AdgPair *value;
471 data = model->data;
472 hash = &data->named_pairs;
474 if (pair == NULL) {
475 /* Delete mode: raise a warning if @name is not found */
476 if (*hash == NULL || !g_hash_table_remove(*hash, name))
477 g_warning("%s: attempting to remove inexistent `%s' named pair",
478 G_STRLOC, name);
480 return;
483 /* Insert/update mode */
484 key = g_strdup(name);
485 value = adg_pair_dup(pair);
487 if (*hash == NULL)
488 *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
490 g_hash_table_insert(*hash, key, value);
493 static const AdgPair *
494 named_pair(AdgModel *model, const gchar *name)
496 AdgModelPrivate *data = model->data;
498 if (data->named_pairs == NULL)
499 return NULL;
501 return g_hash_table_lookup(data->named_pairs, name);
504 static void
505 changed(AdgModel *model)
507 /* Invalidate all the entities dependent on this model */
508 adg_model_foreach_dependency(model,
509 (AdgEntityCallback) adg_entity_invalidate,
510 NULL);