[ADG] Moved include dependencies from .h to .c
[adg.git] / src / adg / adg-model.c
blobbf7e08faf89d7bee072e573a1043e0ea3297c164
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011 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 * AdgDependencyFunc:
44 * @model: the #AdgModel
45 * @entity: the #AdgEntity dependent on @model
46 * @user_data: a general purpose pointer
48 * Callback used by adg_model_foreach_dependency().
49 **/
51 /**
52 * AdgNamedPairFunc:
53 * @model: the #AdgModel
54 * @name: the name of the named pair
55 * @pair: an #AdgPair
56 * @user_data: a general purpose pointer
58 * Callback used by adg_model_foreach_named_pair().
59 **/
62 #include "adg-internal.h"
64 #include "adg-model.h"
65 #include "adg-model-private.h"
68 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_model_parent_class)
71 G_DEFINE_ABSTRACT_TYPE(AdgModel, adg_model, G_TYPE_OBJECT);
73 enum {
74 PROP_0,
75 PROP_DEPENDENCY
78 enum {
79 ADD_DEPENDENCY,
80 REMOVE_DEPENDENCY,
81 SET_NAMED_PAIR,
82 CLEAR,
83 RESET,
84 CHANGED,
85 LAST_SIGNAL
89 static void _adg_dispose (GObject *object);
90 static void _adg_set_property (GObject *object,
91 guint prop_id,
92 const GValue *value,
93 GParamSpec *pspec);
94 static void _adg_add_dependency (AdgModel *model,
95 AdgEntity *entity);
96 static void _adg_remove_dependency (AdgModel *model,
97 AdgEntity *entity);
98 static const AdgPair * _adg_named_pair (AdgModel *model,
99 const gchar *name);
100 static void _adg_reset (AdgModel *model);
101 static void _adg_set_named_pair (AdgModel *model,
102 const gchar *name,
103 const AdgPair *pair);
104 static void _adg_changed (AdgModel *model);
105 static void _adg_named_pair_wrapper (gpointer key,
106 gpointer value,
107 gpointer user_data);
108 static void _adg_invalidate_wrapper (AdgModel *model,
109 AdgEntity *entity,
110 gpointer user_data);
111 static guint _adg_signals[LAST_SIGNAL] = { 0 };
114 static void
115 adg_model_class_init(AdgModelClass *klass)
117 GObjectClass *gobject_class;
118 GParamSpec *param;
120 gobject_class = (GObjectClass *) klass;
122 g_type_class_add_private(klass, sizeof(AdgModelPrivate));
124 gobject_class->dispose = _adg_dispose;
125 gobject_class->set_property = _adg_set_property;
127 klass->add_dependency = _adg_add_dependency;
128 klass->remove_dependency = _adg_remove_dependency;
129 klass->named_pair = _adg_named_pair;
130 klass->set_named_pair = _adg_set_named_pair;
131 klass->clear = NULL;
132 klass->reset = _adg_reset;
133 klass->changed = _adg_changed;
135 param = g_param_spec_object("dependency",
136 P_("Dependency"),
137 P_("Can be used to add a new dependency from this model (this entity will be invalidated on model changed)"),
138 ADG_TYPE_ENTITY,
139 G_PARAM_WRITABLE);
140 g_object_class_install_property(gobject_class, PROP_DEPENDENCY, param);
143 * AdgModel::add-dependency:
144 * @model: an #AdgModel
145 * @entity: an #AdgEntity that depends on @model
147 * Adds @entity to @model. After that @entity will depend on @model,
148 * that is #AdgModel::changed on @model will invalidate @entity.
150 _adg_signals[ADD_DEPENDENCY] =
151 g_signal_new("add-dependency",
152 G_OBJECT_CLASS_TYPE(gobject_class),
153 G_SIGNAL_RUN_FIRST,
154 G_STRUCT_OFFSET(AdgModelClass, add_dependency),
155 NULL, NULL,
156 adg_marshal_VOID__OBJECT,
157 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
160 * AdgModel::remove-dependency:
161 * @model: an #AdgModel
162 * @entity: the #AdgEntity that does not depend on @model anymore
164 * Removes the @entity from @model, that is @entity will not depend
165 * on @model anymore.
167 _adg_signals[REMOVE_DEPENDENCY] =
168 g_signal_new("remove-dependency",
169 G_OBJECT_CLASS_TYPE(gobject_class),
170 G_SIGNAL_RUN_FIRST,
171 G_STRUCT_OFFSET(AdgModelClass, remove_dependency),
172 NULL, NULL,
173 adg_marshal_VOID__OBJECT,
174 G_TYPE_NONE, 1, ADG_TYPE_ENTITY);
177 * AdgModel::set-named-pair:
178 * @model: an #AdgModel
179 * @name: an arbitrary name
180 * @pair: an #AdgPair
182 * Adds, updates or deletes a named pair, accordling to the given
183 * parameters.
185 * If @pair is %NULL, the @name named pair is searched and deleted.
186 * If it is not found, a warning is raised.
188 * Otherwise, the @name named pair is searched: if it is found,
189 * its data are updated with @pair. If it is not found, a new
190 * named pair is created using @name and @pair.
192 _adg_signals[SET_NAMED_PAIR] =
193 g_signal_new("set-named-pair",
194 G_OBJECT_CLASS_TYPE(gobject_class),
195 G_SIGNAL_RUN_FIRST,
196 G_STRUCT_OFFSET(AdgModelClass, set_named_pair),
197 NULL, NULL,
198 adg_marshal_VOID__STRING_POINTER,
199 G_TYPE_NONE, 2,
200 G_TYPE_STRING, G_TYPE_POINTER);
203 * AdgModel::clear:
204 * @model: an #AdgModel
206 * <note><para>
207 * This signal is only useful in model implementations.
208 * </para></note>
210 * Removes any information from @model cached by the implementation
211 * code. Useful to force a recomputation of the cache when something
212 * in the model has changed.
214 _adg_signals[CLEAR] =
215 g_signal_new("clear", ADG_TYPE_MODEL,
216 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
217 G_STRUCT_OFFSET(AdgModelClass, clear),
218 NULL, NULL,
219 adg_marshal_VOID__VOID,
220 G_TYPE_NONE, 0);
223 * AdgModel::reset:
224 * @model: an #AdgModel
226 * Resets the state of @model by destroying any named pair
227 * associated to it. This step also involves the emission of the
228 * AdgModel:clear signal.
230 * This signal is intended to be used while redefining the model.
231 * A typical usage would be on these terms:
233 * |[
234 * adg_model_reset(model);
235 * // Definition of model. This also requires the redefinition of
236 * // the named pairs because the old ones have been destroyed.
237 * ...
238 * adg_model_changed(model);
239 * ]|
241 _adg_signals[RESET] =
242 g_signal_new("reset", ADG_TYPE_MODEL,
243 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
244 G_STRUCT_OFFSET(AdgModelClass, reset),
245 NULL, NULL,
246 adg_marshal_VOID__VOID,
247 G_TYPE_NONE, 0);
250 * AdgModel::changed:
251 * @model: an #AdgModel
253 * Notificates that the model has changed. By default, all the
254 * dependent entities are invalidated.
256 _adg_signals[CHANGED] =
257 g_signal_new("changed", ADG_TYPE_MODEL,
258 G_SIGNAL_RUN_LAST|G_SIGNAL_NO_RECURSE,
259 G_STRUCT_OFFSET(AdgModelClass, changed),
260 NULL, NULL,
261 adg_marshal_VOID__VOID,
262 G_TYPE_NONE, 0);
265 static void
266 adg_model_init(AdgModel *model)
268 AdgModelPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(model, ADG_TYPE_MODEL,
269 AdgModelPrivate);
271 data->dependencies = NULL;
273 model->data = data;
276 static void
277 _adg_dispose(GObject *object)
279 static gboolean is_disposed = FALSE;
281 if (G_UNLIKELY(!is_disposed)) {
282 AdgModel *model;
283 AdgModelPrivate *data;
284 AdgEntity *entity;
286 model = (AdgModel *) object;
287 data = model->data;
289 /* Remove all the dependencies: this will emit a
290 * "remove-dependency" signal for every dependency, dropping
291 * all references from entities to this model */
292 while (data->dependencies != NULL) {
293 entity = (AdgEntity *) data->dependencies->data;
294 adg_model_remove_dependency(model, entity);
297 g_signal_emit(model, _adg_signals[RESET], 0);
300 if (_ADG_OLD_OBJECT_CLASS->dispose)
301 _ADG_OLD_OBJECT_CLASS->dispose(object);
304 static void
305 _adg_set_property(GObject *object, guint prop_id,
306 const GValue *value, GParamSpec *pspec)
308 switch (prop_id) {
309 case PROP_DEPENDENCY:
310 g_signal_emit(object, _adg_signals[ADD_DEPENDENCY], 0,
311 g_value_get_object(value));
312 break;
313 default:
314 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
320 * adg_model_add_dependency:
321 * @model: an #AdgModel
322 * @entity: an #AdgEntity
324 * <note><para>
325 * This function is only useful in entity implementations.
326 * </para></note>
328 * Emits a #AdgModel::add-dependency signal on @model passing @entity
329 * as argument. This will add a reference to @entity owned by @model.
331 void
332 adg_model_add_dependency(AdgModel *model, AdgEntity *entity)
334 g_return_if_fail(ADG_IS_MODEL(model));
335 g_return_if_fail(ADG_IS_ENTITY(entity));
336 g_object_set(model, "dependency", entity, NULL);
340 * adg_model_remove_dependency:
341 * @model: an #AdgModel
342 * @entity: an #AdgEntity
344 * <note><para>
345 * This function is only useful in entity implementations.
346 * </para></note>
348 * Emits a #AdgModel::remove-dependency signal on @model passing
349 * @entity as argument. @entity must be inside @model.
351 * Note that @model will own a reference to @entity and it
352 * may be the last reference held: this means removing an entity
353 * from the model can destroy it.
355 void
356 adg_model_remove_dependency(AdgModel *model, AdgEntity *entity)
358 g_return_if_fail(ADG_IS_MODEL(model));
359 g_return_if_fail(ADG_IS_ENTITY(entity));
361 g_signal_emit(model, _adg_signals[REMOVE_DEPENDENCY], 0, entity);
365 * adg_model_get_dependencies:
366 * @model: an #AdgModel
368 * Gets the list of entities dependending on @model. This list
369 * is owned by @model and must not be modified or freed.
371 * Returns: a #GSList of dependencies or %NULL on error
373 const GSList *
374 adg_model_get_dependencies(AdgModel *model)
376 AdgModelPrivate *data;
378 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
380 data = model->data;
382 return data->dependencies;
386 * adg_model_foreach_dependency:
387 * @model: an #AdgModel
388 * @callback: the entity callback
389 * @user_data: general purpose user data passed "as is" to @callback
391 * Invokes @callback on each entity linked to @model.
393 void
394 adg_model_foreach_dependency(AdgModel *model, AdgDependencyFunc callback,
395 gpointer user_data)
397 AdgModelPrivate *data;
398 GSList *dependency;
399 AdgEntity *entity;
401 g_return_if_fail(ADG_IS_MODEL(model));
402 g_return_if_fail(callback != NULL);
404 data = model->data;
405 dependency = data->dependencies;
407 while (dependency) {
408 entity = dependency->data;
410 if (entity != NULL && ADG_IS_ENTITY(entity))
411 callback(model, entity, user_data);
413 dependency = dependency->next;
418 * adg_model_set_named_pair:
419 * @model: an #AdgModel
420 * @name: the name to associate to the pair
421 * @pair: the #AdgPair
423 * <note><para>
424 * This function is only useful in model definitions, such as
425 * inside an #AdgTrailCallback function or while constructing
426 * an #AdgPath instance.
427 * </para></note>
429 * Emits a #AdgModel::set-named-pair signal on @model passing
430 * @name and @pair as arguments.
432 void
433 adg_model_set_named_pair(AdgModel *model, const gchar *name,
434 const AdgPair *pair)
436 g_return_if_fail(ADG_IS_MODEL(model));
437 g_return_if_fail(name != NULL);
439 g_signal_emit(model, _adg_signals[SET_NAMED_PAIR], 0, name, pair);
443 * adg_model_set_named_pair_explicit:
444 * @model: an #AdgModel
445 * @name: the name to associate to the pair
446 * @x: the x coordinate of the point
447 * @y: the y coordinate of the point
449 * <note><para>
450 * This function is only useful in model definitions, such as
451 * inside an #AdgTrailCallback function or while constructing
452 * an #AdgPath instance.
453 * </para></note>
455 * Convenient wrapper on adg_model_set_named_pair() that accepts
456 * explicit coordinates.
458 void
459 adg_model_set_named_pair_explicit(AdgModel *model, const gchar *name,
460 gdouble x, gdouble y)
462 AdgPair pair;
464 pair.x = x;
465 pair.y = y;
467 adg_model_set_named_pair(model, name, &pair);
471 * adg_model_get_named_pair:
472 * @model: an #AdgModel
473 * @name: the name of the pair to get
475 * Gets the @name named pair associated to @model. The returned
476 * pair is owned by @model and must not be modified or freed.
478 * Returns: the requested #AdgPair or %NULL if not found
480 const AdgPair *
481 adg_model_get_named_pair(AdgModel *model, const gchar *name)
483 AdgModelClass *klass;
485 g_return_val_if_fail(ADG_IS_MODEL(model), NULL);
486 g_return_val_if_fail(name != NULL, NULL);
488 klass = ADG_MODEL_GET_CLASS(model);
490 if (klass->named_pair == NULL)
491 return NULL;
493 return klass->named_pair(model, name);
497 * adg_model_foreach_named_pair:
498 * @model: an #AdgModel
499 * @callback: the named pair callback
500 * @user_data: general purpose user data passed "as is" to @callback
502 * Invokes @callback for each named pair set on @model. This can
503 * be used, for example, to retrieve all the named pairs of a @model
504 * or to duplicate a transformed version of every named pair.
506 void
507 adg_model_foreach_named_pair(AdgModel *model, AdgNamedPairFunc callback,
508 gpointer user_data)
510 AdgModelPrivate *data;
511 AdgWrapperHelper helper;
513 g_return_if_fail(ADG_IS_MODEL(model));
514 g_return_if_fail(callback != NULL);
516 data = model->data;
518 if (data->named_pairs == NULL)
519 return;
521 helper.callback = callback;
522 helper.model = model;
523 helper.user_data = user_data;
525 g_hash_table_foreach(data->named_pairs, _adg_named_pair_wrapper, &helper);
529 * adg_model_clear:
530 * @model: an #AdgModel
532 * <note><para>
533 * This function is only useful new model implementations.
534 * </para></note>
536 * Emits the #AdgModel::clear signal on @model.
538 void
539 adg_model_clear(AdgModel *model)
541 g_return_if_fail(ADG_IS_MODEL(model));
543 g_signal_emit(model, _adg_signals[CLEAR], 0);
547 * adg_model_reset:
548 * @model: an #AdgModel
550 * Emits the #AdgModel::reset signal on @model.
552 void
553 adg_model_reset(AdgModel *model)
555 g_return_if_fail(ADG_IS_MODEL(model));
557 g_signal_emit(model, _adg_signals[RESET], 0);
561 * adg_model_changed:
562 * @model: an #AdgModel
564 * <note><para>
565 * This function is only useful in entity implementations.
566 * </para></note>
568 * Emits the #AdgModel::changed signal on @model.
570 void
571 adg_model_changed(AdgModel *model)
573 g_return_if_fail(ADG_IS_MODEL(model));
575 g_signal_emit(model, _adg_signals[CHANGED], 0);
579 static void
580 _adg_add_dependency(AdgModel *model, AdgEntity *entity)
582 AdgModelPrivate *data;
584 /* Do not add NULL values */
585 if (entity == NULL)
586 return;
588 data = model->data;
590 /* The prepend operation is more efficient */
591 data->dependencies = g_slist_prepend(data->dependencies, entity);
593 g_object_ref(entity);
596 static void
597 _adg_remove_dependency(AdgModel *model, AdgEntity *entity)
599 AdgModelPrivate *data;
600 GSList *node;
602 data = model->data;
603 node = g_slist_find(data->dependencies, entity);
605 if (node == NULL) {
606 g_warning(_("%s: attempting to remove the nonexistent dependency "
607 "on the entity with type %s from a model of type %s"),
608 G_STRLOC,
609 g_type_name(G_OBJECT_TYPE(entity)),
610 g_type_name(G_OBJECT_TYPE(model)));
611 return;
614 data->dependencies = g_slist_delete_link(data->dependencies, node);
615 g_object_unref(entity);
618 static void
619 _adg_reset(AdgModel *model)
621 AdgModelPrivate *data = model->data;
623 adg_model_clear(model);
625 if (data->named_pairs) {
626 g_hash_table_destroy(data->named_pairs);
627 data->named_pairs = NULL;
631 static void
632 _adg_set_named_pair(AdgModel *model, const gchar *name, const AdgPair *pair)
634 AdgModelPrivate *data;
635 GHashTable **hash;
636 gchar *key;
637 AdgPair *value;
639 data = model->data;
640 hash = &data->named_pairs;
642 if (pair == NULL) {
643 /* Delete mode: raise a warning if @name is not found */
644 if (*hash == NULL || !g_hash_table_remove(*hash, name))
645 g_warning(_("%s: attempting to remove nonexistent `%s' named pair"),
646 G_STRLOC, name);
648 return;
651 /* Insert or update mode */
652 key = g_strdup(name);
653 value = adg_pair_dup(pair);
655 if (*hash == NULL)
656 *hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
658 g_hash_table_insert(*hash, key, value);
661 static const AdgPair *
662 _adg_named_pair(AdgModel *model, const gchar *name)
664 AdgModelPrivate *data = model->data;
666 if (data->named_pairs == NULL)
667 return NULL;
669 return g_hash_table_lookup(data->named_pairs, name);
672 static void
673 _adg_changed(AdgModel *model)
675 /* Invalidate all the entities dependent on this model */
676 adg_model_foreach_dependency(model, _adg_invalidate_wrapper, NULL);
679 static void
680 _adg_named_pair_wrapper(gpointer key, gpointer value, gpointer user_data)
682 const gchar *name;
683 AdgPair *pair;
684 AdgWrapperHelper *helper;
686 name = key;
687 pair = value;
688 helper = user_data;
690 helper->callback(helper->model, name, pair, helper->user_data);
693 static void
694 _adg_invalidate_wrapper(AdgModel *model, AdgEntity *entity, gpointer user_data)
696 adg_entity_invalidate(entity);