[build] Moved project dirs under src/
[adg.git] / src / adg / adg-model.c
blob6d75104e692622f2a9d524ddfcf29ad3acbbad6a
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_set_named_pair_explicit:
385 * @model: an #AdgModel
386 * @name: the name to associate to the pair
387 * @x: the x coordinate of the point
388 * @y: the y coordinate of the point
390 * <note><para>
391 * This function is only useful in model definitions, such as
392 * inside an #AdgTrailCallback function or while constructing
393 * an #AdgPath instance.
394 * </para></note>
396 * Convenient wrapper on adg_model_set_named_pair() that accepts
397 * explicit coordinates.
399 void
400 adg_model_set_named_pair_explicit(AdgModel *model, const gchar *name,
401 gdouble x, gdouble y)
403 AdgPair pair;
405 pair.x = x;
406 pair.y = y;
408 adg_model_set_named_pair(model, name, &pair);
412 * adg_model_get_named_pair:
413 * @model: an #AdgModel
414 * @name: the name of the pair to get
416 * Gets the @name named pair associated to @model. The returned
417 * pair is owned by @model and must not be modified or freed.
419 * Returns: the requested #AdgPair or %NULL if not found
421 const AdgPair *
422 adg_model_get_named_pair(AdgModel *model, const gchar *name)
424 AdgModelClass *klass;
426 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
427 g_return_val_if_fail(name != NULL, NULL);
429 klass = ADG_MODEL_GET_CLASS(model);
431 if (klass->named_pair == NULL)
432 return NULL;
434 return klass->named_pair(model, name);
438 * adg_model_foreach_named_pair:
439 * @model: an #AdgModel
440 * @callback: the named pair callback
441 * @user_data: general purpose user data passed "as is" to @callback
443 * Invokes @callback for each named pair set on @model. This can
444 * be used, for example, to retrieve all the named pairs of a @model
445 * or to duplicate a transformed version of every named pair.
447 void
448 adg_model_foreach_named_pair(AdgModel *model, AdgNamedPairCallback callback,
449 gpointer user_data)
451 AdgModelPrivate *data;
453 g_return_if_fail(ADG_IS_MODEL(model));
454 g_return_if_fail(callback != NULL);
456 data = model->data;
458 if (data->named_pairs == NULL)
459 return;
461 g_hash_table_foreach(data->named_pairs, (GHFunc) callback, user_data);
465 * adg_model_clear:
466 * @model: an #AdgModel
468 * <note><para>
469 * This function is only useful in entity implementations.
470 * </para></note>
472 * Emits the #AdgModel::clear signal on @model.
474 void
475 adg_model_clear(AdgModel *model)
477 g_return_if_fail(ADG_IS_MODEL(model));
479 g_signal_emit(model, signals[CLEAR], 0);
483 * adg_model_changed:
484 * @model: an #AdgModel
486 * <note><para>
487 * This function is only useful in entity implementations.
488 * </para></note>
490 * Emits the #AdgModel::changed signal on @model.
492 void
493 adg_model_changed(AdgModel *model)
495 g_return_if_fail(ADG_IS_MODEL(model));
497 g_signal_emit(model, signals[CHANGED], 0);
501 static void
502 add_dependency(AdgModel *model, AdgEntity *entity)
504 AdgModelPrivate *data = model->data;
506 /* The prepend operation is more efficient */
507 data->dependencies = g_slist_prepend(data->dependencies, entity);
509 g_object_ref(entity);
512 static void
513 remove_dependency(AdgModel *model, AdgEntity *entity)
515 AdgModelPrivate *data;
516 GSList *node;
518 data = model->data;
519 node = g_slist_find(data->dependencies, entity);
521 if (node == NULL) {
522 g_warning("%s: attempting to remove an entity with type %s "
523 "from a model of type %s, but the entity is not present.",
524 G_STRLOC,
525 g_type_name(G_OBJECT_TYPE(entity)),
526 g_type_name(G_OBJECT_TYPE(model)));
527 return;
530 data->dependencies = g_slist_delete_link(data->dependencies, node);
531 g_object_unref(entity);
534 static void
535 set_named_pair(AdgModel *model, const gchar *name, const AdgPair *pair)
537 AdgModelPrivate *data;
538 GHashTable **hash;
539 gchar *key;
540 AdgPair *value;
542 data = model->data;
543 hash = &data->named_pairs;
545 if (pair == NULL) {
546 /* Delete mode: raise a warning if @name is not found */
547 if (*hash == NULL || !g_hash_table_remove(*hash, name))
548 g_warning("%s: attempting to remove inexistent `%s' named pair",
549 G_STRLOC, name);
551 return;
554 /* Insert/update mode */
555 key = g_strdup(name);
556 value = adg_pair_dup(pair);
558 if (*hash == NULL)
559 *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
561 g_hash_table_insert(*hash, key, value);
564 static const AdgPair *
565 named_pair(AdgModel *model, const gchar *name)
567 AdgModelPrivate *data = model->data;
569 if (data->named_pairs == NULL)
570 return NULL;
572 return g_hash_table_lookup(data->named_pairs, name);
575 static void
576 changed(AdgModel *model)
578 /* Invalidate all the entities dependent on this model */
579 adg_model_foreach_dependency(model,
580 (AdgEntityCallback) adg_entity_invalidate,
581 NULL);