[build] Moved project dirs under src/
[adg.git] / src / adg / adg-dress.c
blob805ed8c183327d7f968c0b4ef44f105fe3ecd6ce
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-dress
23 * @Section_Id:AdgDress
24 * @title: AdgDress
25 * @short_description: The ADG way to associate styles to entities
27 * The dress is a virtualization of an #AdgStyle instance. #AdgEntity
28 * objects do not directly refer to #AdgStyle but use #AdgDress values
29 * instead. This allows some advanced operations, such as overriding
30 * a dress only in a specific entity branch of the hierarchy or
31 * customize multiple entities at once.
32 **/
34 /**
35 * AdgDress:
37 * An index representing a virtual #AdgStyle.
38 **/
40 /**
41 * ADG_TYPE_DRESS:
43 * The type used to express a dress index. It is defined only for GObject
44 * internal and should not be used directly (at least, as far as I know).
45 **/
47 /**
48 * ADG_VALUE_HOLDS_DRESS:
49 * @value: a #GValue
51 * Checks whether a #GValue is actually holding an #AdgDress value or not.
53 * Returns: %TRUE is @value is holding an #AdgDress, %FALSE otherwise
54 **/
57 #include "adg-internal.h"
58 #include "adg-dress.h"
59 #include "adg-dress-private.h"
60 #include "adg-dress-builtins.h"
63 static AdgDress quark_to_dress (GQuark quark);
64 static void dress_to_string (const GValue *src,
65 GValue *dst);
66 static void string_to_dress (const GValue *src,
67 GValue *dst);
68 static void param_class_init (GParamSpecClass*klass);
69 static const gchar * _adg_dress_name (AdgDress dress);
70 static gboolean dress_is_valid (AdgDress dress);
71 static gboolean dress_is_valid_with_log (AdgDress dress);
72 static gboolean value_validate (GParamSpec *spec,
73 GValue *value);
75 static guint array_append (AdgDressPrivate*data);
76 static AdgDressPrivate *array_lookup (guint n);
77 static guint array_len (void);
80 GType
81 adg_dress_get_type(void)
83 static GType type = 0;
85 if (G_UNLIKELY(type == 0)) {
86 const GTypeInfo info = { 0, };
88 type = g_type_register_static(G_TYPE_INT, "AdgDress", &info, 0);
90 g_value_register_transform_func(type, G_TYPE_STRING, dress_to_string);
91 g_value_register_transform_func(G_TYPE_STRING, type, string_to_dress);
94 return type;
97 /**
98 * adg_dress_new:
99 * @name: the dress name
100 * @fallback: the fallback style
102 * Creates a new dress. It is a convenient wrapper of adg_dress_new_full()
103 * that uses as ancestor the G_TYPE_FROM_INSTANCE() of @fallback.
105 * After a succesfull call, a new reference is added to @fallback.
107 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
109 AdgDress
110 adg_dress_new(const gchar *name, AdgStyle *fallback)
112 g_return_val_if_fail(ADG_IS_STYLE(fallback), ADG_DRESS_UNDEFINED);
114 return adg_dress_new_full(name, fallback, G_TYPE_FROM_INSTANCE(fallback));
118 * adg_dress_new_full:
119 * @name: the dress name
120 * @fallback: the fallback style
121 * @ancestor_type: the common ancestor type
123 * Creates a new dress, explicitely setting the ancestor type.
124 * If @fallback is not %NULL, @ancestor_type must be present in
125 * its hierarchy: check out the adg_dress_style_is_compatible()
126 * documentation to know what the ancestor type is used for.
128 * @fallback can be %NULL, in which case a "transparent" dress
129 * is created. This kind of dress does not change the cairo
130 * context because there is no style to apply. Any entity could
131 * override it to change this behavior though.
133 * After a succesfull call, a new reference is added to @fallback
134 * if needed.
136 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
138 AdgDress
139 adg_dress_new_full(const gchar *name, AdgStyle *fallback, GType ancestor_type)
141 GQuark quark;
142 AdgDress dress;
143 AdgDressPrivate data;
145 g_return_val_if_fail(name != NULL, ADG_DRESS_UNDEFINED);
146 g_return_val_if_fail(g_type_is_a(ancestor_type, ADG_TYPE_STYLE),
147 ADG_DRESS_UNDEFINED);
148 g_return_val_if_fail(fallback == NULL ||
149 G_TYPE_CHECK_INSTANCE_TYPE(fallback, ancestor_type),
150 ADG_DRESS_UNDEFINED);
152 quark = g_quark_from_string(name);
153 dress = quark_to_dress(quark);
155 if (dress > 0) {
156 g_warning(_("%s: the `%s' name is yet used by the `%d' dress"),
157 G_STRLOC, name, dress);
158 return ADG_DRESS_UNDEFINED;
161 data.quark = quark;
162 data.fallback = fallback;
163 data.ancestor_type = ancestor_type;
165 if (fallback != NULL)
166 g_object_ref(fallback);
168 return array_append(&data) - 1;
172 * adg_dress_from_name:
173 * @name: the name of a dress
175 * Gets the dress bound to a @name string. No warnings are raised
176 * if the dress is not found.
178 * Returns: the #AdgDress code or #ADG_DRESS_UNDEFINED if not found
180 AdgDress
181 adg_dress_from_name(const gchar *name)
183 return quark_to_dress(g_quark_try_string(name));
187 * adg_dress_are_related:
188 * @dress1: an #AdgDress
189 * @dress2: another #AdgDress
191 * Checks whether @dress1 and @dress2 are related, that is
192 * if they have the same ancestor type as returned by
193 * adg_dress_get_ancestor_type().
195 * Returns: %TRUE if the dresses are related, %FALSE otherwise
197 gboolean
198 adg_dress_are_related(AdgDress dress1, AdgDress dress2)
200 GType ancestor_type1, ancestor_type2;
202 ancestor_type1 = adg_dress_get_ancestor_type(dress1);
203 if (ancestor_type1 <= 0)
204 return FALSE;
206 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
207 if (ancestor_type2 <= 0)
208 return FALSE;
210 return ancestor_type1 == ancestor_type2;
214 * adg_dress_set:
215 * @dress: a pointer to an #AdgDress
216 * @src: the source dress
218 * Copies @src in @dress. This operation can be succesful only if
219 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
220 * to @src, that is adg_dress_are_related() returns %TRUE.
222 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
224 gboolean
225 adg_dress_set(AdgDress *dress, AdgDress src)
227 if (*dress == src)
228 return FALSE;
230 if (*dress != ADG_DRESS_UNDEFINED && !adg_dress_are_related(*dress, src)) {
231 g_warning(_("%s: `%d' (%s) cannot be replaced with `%d' (%s) because these dresses are not related"),
232 G_STRLOC, src, _adg_dress_name(src),
233 *dress, _adg_dress_name(*dress));
234 return FALSE;
237 *dress = src;
239 return TRUE;
243 * adg_dress_get_name:
244 * @dress: an #AdgDress
246 * Gets the name associated to @dress. No warnings are raised if
247 * @dress is not found.
249 * Returns: the requested name or %NULL if not found
251 const gchar *
252 adg_dress_get_name(AdgDress dress)
254 if (!dress_is_valid(dress))
255 return NULL;
257 return g_quark_to_string(array_lookup(dress)->quark);
261 * adg_dress_get_ancestor_type:
262 * @dress: an #AdgDress
264 * Gets the base type that should be present in every #AdgStyle
265 * acceptable by @dress. No warnings are raised if @dress
266 * is not found.
268 * Returns: the ancestor type or %0 on errors
270 GType
271 adg_dress_get_ancestor_type(AdgDress dress)
273 AdgDressPrivate *data;
275 if (!dress_is_valid(dress))
276 return 0;
278 data = array_lookup(dress);
280 return data->ancestor_type;
284 * adg_dress_set_fallback:
285 * @dress: an #AdgDress
286 * @fallback: the new fallback style
288 * Associates a new @fallback style to @dress. If the dress does
289 * not exist (it was not previously created by adg_dress_new()),
290 * a warning message is raised and the function fails.
292 * @fallback is checked for compatibily with @dress. Any dress holds
293 * an ancestor type: if this type is not found in the @fallback
294 * hierarchy, a warning message is raised and the function fails.
296 * After a succesfull call, the reference to the previous fallback
297 * (if any) is dropped while a new reference to @fallback is added.
299 void
300 adg_dress_set_fallback(AdgDress dress, AdgStyle *fallback)
302 AdgDressPrivate *data;
304 if (!dress_is_valid_with_log(dress))
305 return;
307 data = array_lookup(dress);
309 if (data->fallback == fallback)
310 return;
312 /* Check if the new fallback style is compatible with this dress */
313 if (fallback != NULL && !adg_dress_style_is_compatible(dress, fallback)) {
314 g_warning(_("%s: the fallback style of `%d' (%s) must be a `%s' derived type, but a `%s' has been provided"),
315 G_STRLOC, dress, _adg_dress_name(dress),
316 g_type_name(data->ancestor_type),
317 g_type_name(G_TYPE_FROM_INSTANCE(fallback)));
318 return;
321 if (data->fallback != NULL)
322 g_object_unref(data->fallback);
324 data->fallback = fallback;
326 if (data->fallback != NULL)
327 g_object_ref(data->fallback);
331 * adg_dress_get_fallback:
332 * @dress: an #AdgDress
334 * Gets the fallback style associated to @dress. No warnings
335 * are raised if the dress is not found.
337 * Returns: the requested #AdgStyle derived instance or %NULL if not set
339 AdgStyle *
340 adg_dress_get_fallback(AdgDress dress)
342 AdgDressPrivate *data;
344 if (!dress_is_valid(dress))
345 return NULL;
347 data = array_lookup(dress);
349 return data->fallback;
353 * adg_dress_style_is_compatible:
354 * @dress: an #AdgDress
355 * @style: the #AdgStyle to check
357 * Checks whether @style is compatible with @dress, that is if
358 * @style has the ancestor style type (as returned by
359 * adg_dress_get_ancestor_type()) in its hierarchy.
361 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise
363 gboolean
364 adg_dress_style_is_compatible(AdgDress dress, AdgStyle *style)
366 GType ancestor_type = adg_dress_get_ancestor_type(dress);
368 g_return_val_if_fail(ancestor_type > 0, FALSE);
369 g_return_val_if_fail(ADG_IS_STYLE(style), FALSE);
371 return G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type);
375 static AdgDress
376 quark_to_dress(GQuark quark)
378 AdgDress dress;
379 AdgDressPrivate *data;
381 for (dress = 0; dress < array_len(); ++dress) {
382 data = array_lookup(dress);
384 if (data->quark == quark)
385 return dress;
388 return ADG_DRESS_UNDEFINED;
391 static void
392 dress_to_string(const GValue *src, GValue *dst)
394 g_value_set_string(dst, adg_dress_get_name(g_value_get_int(src)));
397 static void
398 string_to_dress(const GValue *src, GValue *dst)
400 g_value_set_int(dst, adg_dress_from_name(g_value_get_string(src)));
404 typedef struct _AdgParamSpecDress AdgParamSpecDress;
406 struct _AdgParamSpecDress {
407 GParamSpecInt parent;
408 AdgDress source_dress;
412 GType
413 _adg_param_spec_dress_get_type(void)
415 static GType type = 0;
417 if (G_UNLIKELY(type == 0)) {
418 const GTypeInfo info = {
419 sizeof(GParamSpecClass),
420 NULL,
421 NULL,
422 (GClassInitFunc) param_class_init,
423 NULL,
424 NULL,
425 sizeof(AdgParamSpecDress),
429 type = g_type_register_static(G_TYPE_PARAM_INT,
430 "AdgParamSpecDress", &info, 0);
433 return type;
436 static void
437 param_class_init(GParamSpecClass *klass)
439 klass->value_type = ADG_TYPE_DRESS;
440 klass->value_validate = value_validate;
443 static const gchar *
444 _adg_dress_name(AdgDress dress)
446 const gchar *name = adg_dress_get_name(dress);
448 if (name == NULL) {
449 name = _("(UNDEFINED)");
452 return name;
455 static gboolean
456 dress_is_valid(AdgDress dress)
458 return dress > 0 && dress < array_len();
461 static gboolean
462 dress_is_valid_with_log(AdgDress dress)
464 if (!dress_is_valid(dress)) {
465 g_warning(_("%s: the dress `%d' is undefined"), G_STRLOC, dress);
466 return FALSE;
469 return TRUE;
472 static gboolean
473 value_validate(GParamSpec *spec, GValue *value)
475 AdgParamSpecDress *fspec;
476 AdgDress dress, new_dress;
478 fspec = (AdgParamSpecDress *) spec;
479 dress = value->data[0].v_int;
480 new_dress = ADG_DRESS_UNDEFINED;
482 adg_dress_set(&new_dress, dress);
483 value->data[0].v_int = new_dress;
485 return dress != new_dress;
490 * adg_param_spec_dress:
491 * @name: canonical name
492 * @nick: nickname of the param
493 * @blurb: brief desciption
494 * @dress: the #AdgDress dress
495 * @flags: a combination of #GParamFlags
497 * Creates a param spec to hold a dress value.
499 * Returns: the newly allocated #GParamSpec
501 GParamSpec *
502 adg_param_spec_dress(const gchar *name, const gchar *nick, const gchar *blurb,
503 AdgDress dress, GParamFlags flags)
505 AdgParamSpecDress *fspec;
507 if (!dress_is_valid_with_log(dress))
508 return NULL;
510 fspec = g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS,
511 name, nick, blurb, flags);
512 fspec->source_dress = dress;
514 return (GParamSpec *) fspec;
518 static GArray * array_singleton (void) G_GNUC_CONST;
520 static GArray *
521 array_singleton(void)
523 static GArray *array = NULL;
525 if (array == NULL) {
526 const AdgDressPrivate data = { 0, };
528 array = g_array_new(FALSE, FALSE, sizeof(AdgDressPrivate));
530 /* Reserve the first item for the undefined dress */
531 g_array_append_val(array, data);
534 return array;
537 static guint
538 array_append(AdgDressPrivate *data)
540 return g_array_append_val(array_singleton(), *data)->len;
543 static AdgDressPrivate *
544 array_lookup(guint n)
546 return &g_array_index(array_singleton(), AdgDressPrivate, n);
549 static guint
550 array_len(void)
552 return array_singleton()->len;