s/2008,2009/2008,2009,2010/
[adg.git] / adg / adg-model.c
blobb1f5a8b4200d21b7b4ebf8bf915c1caf8d9359b1
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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 **/
42 /**
43 * AdgNamedPairCallback:
44 * @name: an arbitrary name
45 * @pair: an #AdgPair
46 * @user_data: a general purpose pointer
48 * Callback used by adg_model_foreach_named_pair().
49 **/
52 #include "adg-internal.h"
53 #include "adg-model.h"
54 #include "adg-model-private.h"
55 #include "adg-marshal.h"
57 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_model_parent_class)
60 enum {
61 PROP_0,
62 PROP_DEPENDENCY
65 enum {
66 ADD_DEPENDENCY,
67 REMOVE_DEPENDENCY,
68 SET_NAMED_PAIR,
69 CLEAR,
70 CHANGED,
71 LAST_SIGNAL
75 static void dispose (GObject *object);
76 static void set_property (GObject *object,
77 guint prop_id,
78 const GValue *value,
79 GParamSpec *pspec);
80 static void add_dependency (AdgModel *model,
81 AdgEntity *entity);
82 static void remove_dependency (AdgModel *model,
83 AdgEntity *entity);
84 static const AdgPair *
85 named_pair (AdgModel *model,
86 const gchar *name);
87 static void set_named_pair (AdgModel *model,
88 const gchar *name,
89 const AdgPair *pair);
90 static void changed (AdgModel *model);
92 static guint signals[LAST_SIGNAL] = { 0 };
95 G_DEFINE_ABSTRACT_TYPE(AdgModel, adg_model, G_TYPE_OBJECT);
98 static void
99 adg_model_class_init(AdgModelClass *klass)
101 GObjectClass *gobject_class;
102 GParamSpec *param;
104 gobject_class = (GObjectClass *) klass;
106 g_type_class_add_private(klass, sizeof(AdgModelPrivate));
108 gobject_class->dispose = dispose;
109 gobject_class->set_property = set_property;
111 klass->add_dependency = add_dependency;
112 klass->remove_dependency = remove_dependency;
113 klass->named_pair = named_pair;
114 klass->set_named_pair = set_named_pair;
115 klass->clear = NULL;
116 klass->changed = changed;
118 param = g_param_spec_object("dependency",
119 P_("Dependency"),
120 P_("Can be used to add a new dependency from this model (this entity will be invalidated on model changed)"),
121 ADG_TYPE_ENTITY,
122 G_PARAM_WRITABLE);
123 g_object_class_install_property(gobject_class, PROP_DEPENDENCY, param);
126 * AdgModel::add-dependency:
127 * @model: an #AdgModel
128 * @entity: an #AdgEntity that depends on @model
130 * Adds @entity to @model. After that @entity will depend on @model,
131 * that is #AdgModel::changed on @model will invalidate @entity.
133 signals[ADD_DEPENDENCY] = g_signal_new("add-dependency",
134 G_OBJECT_CLASS_TYPE(gobject_class),
135 G_SIGNAL_RUN_FIRST,
136 G_STRUCT_OFFSET(AdgModelClass, add_dependency),
137 NULL, NULL,
138 adg_marshal_VOID__OBJECT,
139 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
142 * AdgModel::remove-dependency:
143 * @model: an #AdgModel
144 * @entity: the #AdgEntity that does not depend on @model anymore
146 * Removes the @entity from @model, that is @entity will not depend
147 * on @model anymore.
149 signals[REMOVE_DEPENDENCY] = g_signal_new("remove-dependency",
150 G_OBJECT_CLASS_TYPE(gobject_class),
151 G_SIGNAL_RUN_FIRST,
152 G_STRUCT_OFFSET(AdgModelClass, remove_dependency),
153 NULL, NULL,
154 adg_marshal_VOID__OBJECT,
155 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
158 * AdgModel::set-named-pair:
159 * @model: an #AdgModel
160 * @name: an arbitrary name
161 * @pair: an #AdgPair
163 * Adds, updates or deletes a named pair, accordling to the given
164 * parameters.
166 * If @pair is %NULL, the @name named pair is searched and deleted.
167 * If it is not found, a warning is raised.
169 * Otherwise, the @name named pair is searched: if it is found,
170 * its data are updated with @pair. If it is not found, a new
171 * named pair is created using @name and @pair.
173 signals[SET_NAMED_PAIR] = g_signal_new("set-named-pair",
174 G_OBJECT_CLASS_TYPE(gobject_class),
175 G_SIGNAL_RUN_FIRST,
176 G_STRUCT_OFFSET(AdgModelClass, set_named_pair),
177 NULL, NULL,
178 adg_marshal_VOID__STRING_POINTER,
179 G_TYPE_NONE, 2,
180 G_TYPE_STRING, G_TYPE_POINTER);
183 * AdgModel::clear:
184 * @model: an #AdgModel
186 * Removes any cached information from @model.
188 signals[CLEAR] = g_signal_new("clear", ADG_TYPE_MODEL,
189 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
190 G_STRUCT_OFFSET(AdgModelClass, clear),
191 NULL, NULL,
192 adg_marshal_VOID__VOID,
193 G_TYPE_NONE, 0);
196 * AdgModel::changed:
197 * @model: an #AdgModel
199 * Notificates that the model has changed. By default, all the
200 * dependent entities are invalidated.
202 signals[CHANGED] = g_signal_new("changed", ADG_TYPE_MODEL,
203 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
204 G_STRUCT_OFFSET(AdgModelClass, changed),
205 NULL, NULL,
206 adg_marshal_VOID__VOID,
207 G_TYPE_NONE, 0);
210 static void
211 adg_model_init(AdgModel *model)
213 AdgModelPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(model, ADG_TYPE_MODEL,
214 AdgModelPrivate);
216 data->dependencies = NULL;
218 model->data = data;
221 static void
222 dispose(GObject *object)
224 AdgModel *model;
225 AdgModelPrivate *data;
226 AdgEntity *entity;
228 model = (AdgModel *) object;
229 data = model->data;
231 /* Remove all the dependencies from the model: this will emit
232 * a "remove" signal for every dependency and will drop all the
233 * references from those entities to this model */
234 while (data->dependencies != NULL) {
235 entity = (AdgEntity *) data->dependencies->data;
236 adg_model_remove_dependency(model, entity);
239 if (PARENT_OBJECT_CLASS->dispose)
240 PARENT_OBJECT_CLASS->dispose(object);
243 static void
244 set_property(GObject *object,
245 guint prop_id, const GValue *value, GParamSpec *pspec)
247 AdgModel *model = (AdgModel *) object;
249 switch (prop_id) {
250 case PROP_DEPENDENCY:
251 adg_model_add_dependency(model, g_value_get_object(value));
252 break;
253 default:
254 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
260 * adg_model_add_dependency:
261 * @model: an #AdgModel
262 * @entity: an #AdgEntity
264 * <note><para>
265 * This function is only useful in entity implementations.
266 * </para></note>
268 * Emits a #AdgModel::add-dependency signal on @model passing @entity
269 * as argument. This will add a reference to @entity owned by @model.
271 void
272 adg_model_add_dependency(AdgModel *model, AdgEntity *entity)
274 g_return_if_fail(ADG_IS_MODEL(model));
275 g_return_if_fail(ADG_IS_ENTITY(entity));
277 g_signal_emit(model, signals[ADD_DEPENDENCY], 0, entity);
281 * adg_model_remove_dependency:
282 * @model: an #AdgModel
283 * @entity: an #AdgEntity
285 * <note><para>
286 * This function is only useful in entity implementations.
287 * </para></note>
289 * Emits a #AdgModel::remove-dependency signal on @model passing
290 * @entity as argument. @entity must be inside @model.
292 * Note that @model will own a reference to @entity and it
293 * may be the last reference held: this means removing an entity
294 * from the model can destroy it.
296 void
297 adg_model_remove_dependency(AdgModel *model, AdgEntity *entity)
299 g_return_if_fail(ADG_IS_MODEL(model));
300 g_return_if_fail(ADG_IS_ENTITY(entity));
302 g_signal_emit(model, signals[REMOVE_DEPENDENCY], 0, entity);
306 * adg_model_get_dependencies:
307 * @model: an #AdgModel
309 * Gets the list of entities dependending on @model. This list
310 * is owned by @model and must not be modified or freed.
312 * Returns: a #GSList of dependencies or %NULL on error
314 const GSList *
315 adg_model_get_dependencies(AdgModel *model)
317 AdgModelPrivate *data;
319 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
321 data = model->data;
323 return data->dependencies;
327 * adg_model_foreach_dependency:
328 * @model: an #AdgModel
329 * @callback: the entity callback
330 * @user_data: general purpose user data passed "as is" to @callback
332 * Invokes @callback on each entity linked to @model.
334 void
335 adg_model_foreach_dependency(AdgModel *model, AdgEntityCallback callback,
336 gpointer user_data)
338 AdgModelPrivate *data;
339 GSList *dependency;
340 AdgEntity *entity;
342 g_return_if_fail(ADG_IS_MODEL(model));
343 g_return_if_fail(callback != NULL);
345 data = model->data;
346 dependency = data->dependencies;
348 while (dependency != NULL) {
349 entity = dependency->data;
351 if (entity != NULL && ADG_IS_ENTITY(entity))
352 callback(entity, user_data);
354 dependency = dependency->next;
359 * adg_model_set_named_pair:
360 * @model: an #AdgModel
361 * @name: the name to associate to the pair
362 * @pair: the #AdgPair
364 * <note><para>
365 * This function is only useful in model definitions, such as
366 * inside an #AdgTrailCallback function or while constructing
367 * an #AdgPath instance.
368 * </para></note>
370 * Emits a #AdgModel::set-named-pair signal on @model passing
371 * @name and @pair as arguments.
373 void
374 adg_model_set_named_pair(AdgModel *model, const gchar *name,
375 const AdgPair *pair)
377 g_return_if_fail(ADG_IS_MODEL(model));
378 g_return_if_fail(name != NULL);
380 g_signal_emit(model, signals[SET_NAMED_PAIR], 0, name, pair);
384 * adg_model_get_named_pair:
385 * @model: an #AdgModel
386 * @name: the name of the pair to get
388 * Gets the @name named pair associated to @model. The returned
389 * pair is owned by @model and must not be modified or freed.
391 * Returns: the requested #AdgPair or %NULL if not found
393 const AdgPair *
394 adg_model_get_named_pair(AdgModel *model, const gchar *name)
396 AdgModelClass *klass;
398 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
399 g_return_val_if_fail(name != NULL, NULL);
401 klass = ADG_MODEL_GET_CLASS(model);
403 if (klass->named_pair == NULL)
404 return NULL;
406 return klass->named_pair(model, name);
410 * adg_model_foreach_named_pair:
411 * @model: an #AdgModel
412 * @callback: the named pair callback
413 * @user_data: general purpose user data passed "as is" to @callback
415 * Invokes @callback for each named pair set on @model. This can
416 * be used, for example, to retrieve all the named pairs of a @model
417 * or to duplicate a transformed version of every named pair.
419 void
420 adg_model_foreach_named_pair(AdgModel *model, AdgNamedPairCallback callback,
421 gpointer user_data)
423 AdgModelPrivate *data;
425 g_return_if_fail(ADG_IS_MODEL(model));
426 g_return_if_fail(callback != NULL);
428 data = model->data;
430 if (data->named_pairs == NULL)
431 return;
433 g_hash_table_foreach(data->named_pairs, (GHFunc) callback, user_data);
437 * adg_model_clear:
438 * @model: an #AdgModel
440 * <note><para>
441 * This function is only useful in entity implementations.
442 * </para></note>
444 * Emits the #AdgModel::clear signal on @model.
446 void
447 adg_model_clear(AdgModel *model)
449 g_return_if_fail(ADG_IS_MODEL(model));
451 g_signal_emit(model, signals[CLEAR], 0);
455 * adg_model_changed:
456 * @model: an #AdgModel
458 * <note><para>
459 * This function is only useful in entity implementations.
460 * </para></note>
462 * Emits the #AdgModel::changed signal on @model.
464 void
465 adg_model_changed(AdgModel *model)
467 g_return_if_fail(ADG_IS_MODEL(model));
469 g_signal_emit(model, signals[CHANGED], 0);
473 static void
474 add_dependency(AdgModel *model, AdgEntity *entity)
476 AdgModelPrivate *data = model->data;
478 /* The prepend operation is more efficient */
479 data->dependencies = g_slist_prepend(data->dependencies, entity);
481 g_object_ref(entity);
484 static void
485 remove_dependency(AdgModel *model, AdgEntity *entity)
487 AdgModelPrivate *data;
488 GSList *node;
490 data = model->data;
491 node = g_slist_find(data->dependencies, entity);
493 if (node == NULL) {
494 g_warning("%s: attempting to remove an entity with type %s "
495 "from a model of type %s, but the entity is not present.",
496 G_STRLOC,
497 g_type_name(G_OBJECT_TYPE(entity)),
498 g_type_name(G_OBJECT_TYPE(model)));
499 return;
502 data->dependencies = g_slist_delete_link(data->dependencies, node);
503 g_object_unref(entity);
506 static void
507 set_named_pair(AdgModel *model, const gchar *name, const AdgPair *pair)
509 AdgModelPrivate *data;
510 GHashTable **hash;
511 gchar *key;
512 AdgPair *value;
514 data = model->data;
515 hash = &data->named_pairs;
517 if (pair == NULL) {
518 /* Delete mode: raise a warning if @name is not found */
519 if (*hash == NULL || !g_hash_table_remove(*hash, name))
520 g_warning("%s: attempting to remove inexistent `%s' named pair",
521 G_STRLOC, name);
523 return;
526 /* Insert/update mode */
527 key = g_strdup(name);
528 value = adg_pair_dup(pair);
530 if (*hash == NULL)
531 *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
533 g_hash_table_insert(*hash, key, value);
536 static const AdgPair *
537 named_pair(AdgModel *model, const gchar *name)
539 AdgModelPrivate *data = model->data;
541 if (data->named_pairs == NULL)
542 return NULL;
544 return g_hash_table_lookup(data->named_pairs, name);
547 static void
548 changed(AdgModel *model)
550 /* Invalidate all the entities dependent on this model */
551 adg_model_foreach_dependency(model,
552 (AdgEntityCallback) adg_entity_invalidate,
553 NULL);