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.
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
38 * All fields are private and should not be used directly.
39 * Use its public methods instead.
43 * AdgNamedPairCallback:
44 * @name: an arbitrary name
46 * @user_data: a general purpose pointer
48 * Callback used by adg_model_foreach_named_pair().
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)
75 static void dispose (GObject
*object
);
76 static void set_property (GObject
*object
,
80 static void add_dependency (AdgModel
*model
,
82 static void remove_dependency (AdgModel
*model
,
84 static const AdgPair
*
85 named_pair (AdgModel
*model
,
87 static void set_named_pair (AdgModel
*model
,
90 static void changed (AdgModel
*model
);
92 static guint signals
[LAST_SIGNAL
] = { 0 };
95 G_DEFINE_ABSTRACT_TYPE(AdgModel
, adg_model
, G_TYPE_OBJECT
);
99 adg_model_class_init(AdgModelClass
*klass
)
101 GObjectClass
*gobject_class
;
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
;
116 klass
->changed
= changed
;
118 param
= g_param_spec_object("dependency",
120 P_("Can be used to add a new dependency from this model (this entity will be invalidated on model changed)"),
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
),
136 G_STRUCT_OFFSET(AdgModelClass
, add_dependency
),
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
149 signals
[REMOVE_DEPENDENCY
] = g_signal_new("remove-dependency",
150 G_OBJECT_CLASS_TYPE(gobject_class
),
152 G_STRUCT_OFFSET(AdgModelClass
, remove_dependency
),
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
163 * Adds, updates or deletes a named pair, accordling to the given
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
),
176 G_STRUCT_OFFSET(AdgModelClass
, set_named_pair
),
178 adg_marshal_VOID__STRING_POINTER
,
180 G_TYPE_STRING
, G_TYPE_POINTER
);
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
),
192 adg_marshal_VOID__VOID
,
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
),
206 adg_marshal_VOID__VOID
,
211 adg_model_init(AdgModel
*model
)
213 AdgModelPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(model
, ADG_TYPE_MODEL
,
216 data
->dependencies
= NULL
;
222 dispose(GObject
*object
)
225 AdgModelPrivate
*data
;
228 model
= (AdgModel
*) object
;
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
);
244 set_property(GObject
*object
,
245 guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
247 AdgModel
*model
= (AdgModel
*) object
;
250 case PROP_DEPENDENCY
:
251 adg_model_add_dependency(model
, g_value_get_object(value
));
254 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
260 * adg_model_add_dependency:
261 * @model: an #AdgModel
262 * @entity: an #AdgEntity
265 * This function is only useful in entity implementations.
268 * Emits a #AdgModel::add-dependency signal on @model passing @entity
269 * as argument. This will add a reference to @entity owned by @model.
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
286 * This function is only useful in entity implementations.
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.
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
315 adg_model_get_dependencies(AdgModel
*model
)
317 AdgModelPrivate
*data
;
319 g_return_val_if_fail(ADG_IS_MODEL(model
), NULL
);
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.
335 adg_model_foreach_dependency(AdgModel
*model
, AdgEntityCallback callback
,
338 AdgModelPrivate
*data
;
342 g_return_if_fail(ADG_IS_MODEL(model
));
343 g_return_if_fail(callback
!= NULL
);
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
365 * This function is only useful in model definitions, such as
366 * inside an #AdgTrailCallback function or while constructing
367 * an #AdgPath instance.
370 * Emits a #AdgModel::set-named-pair signal on @model passing
371 * @name and @pair as arguments.
374 adg_model_set_named_pair(AdgModel
*model
, const gchar
*name
,
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
391 * This function is only useful in model definitions, such as
392 * inside an #AdgTrailCallback function or while constructing
393 * an #AdgPath instance.
396 * Convenient wrapper on adg_model_set_named_pair() that accepts
397 * explicit coordinates.
400 adg_model_set_named_pair_explicit(AdgModel
*model
, const gchar
*name
,
401 gdouble x
, gdouble 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
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
)
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.
448 adg_model_foreach_named_pair(AdgModel
*model
, AdgNamedPairCallback callback
,
451 AdgModelPrivate
*data
;
453 g_return_if_fail(ADG_IS_MODEL(model
));
454 g_return_if_fail(callback
!= NULL
);
458 if (data
->named_pairs
== NULL
)
461 g_hash_table_foreach(data
->named_pairs
, (GHFunc
) callback
, user_data
);
466 * @model: an #AdgModel
469 * This function is only useful in entity implementations.
472 * Emits the #AdgModel::clear signal on @model.
475 adg_model_clear(AdgModel
*model
)
477 g_return_if_fail(ADG_IS_MODEL(model
));
479 g_signal_emit(model
, signals
[CLEAR
], 0);
484 * @model: an #AdgModel
487 * This function is only useful in entity implementations.
490 * Emits the #AdgModel::changed signal on @model.
493 adg_model_changed(AdgModel
*model
)
495 g_return_if_fail(ADG_IS_MODEL(model
));
497 g_signal_emit(model
, signals
[CHANGED
], 0);
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
);
513 remove_dependency(AdgModel
*model
, AdgEntity
*entity
)
515 AdgModelPrivate
*data
;
519 node
= g_slist_find(data
->dependencies
, entity
);
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.",
525 g_type_name(G_OBJECT_TYPE(entity
)),
526 g_type_name(G_OBJECT_TYPE(model
)));
530 data
->dependencies
= g_slist_delete_link(data
->dependencies
, node
);
531 g_object_unref(entity
);
535 set_named_pair(AdgModel
*model
, const gchar
*name
, const AdgPair
*pair
)
537 AdgModelPrivate
*data
;
543 hash
= &data
->named_pairs
;
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",
554 /* Insert/update mode */
555 key
= g_strdup(name
);
556 value
= adg_pair_dup(pair
);
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
)
572 return g_hash_table_lookup(data
->named_pairs
, name
);
576 changed(AdgModel
*model
)
578 /* Invalidate all the entities dependent on this model */
579 adg_model_foreach_dependency(model
,
580 (AdgEntityCallback
) adg_entity_invalidate
,