[AdgDress] Prefixed internal symbols
[adg.git] / src / adg / adg-dress.c
blob77bdd45f3a01cfa6080bdad3f6c6c99da3f02793
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 typedef struct _AdgParamSpecDress AdgParamSpecDress;
65 struct _AdgParamSpecDress {
66 GParamSpecInt parent;
67 AdgDress source_dress;
71 static AdgDress _adg_quark_to_dress (GQuark quark);
72 static void _adg_dress_to_string (const GValue *src,
73 GValue *dst);
74 static void _adg_string_to_dress (const GValue *src,
75 GValue *dst);
76 static void _adg_param_class_init (GParamSpecClass*klass);
77 static const gchar * _adg_dress_name (AdgDress dress);
78 static gboolean _adg_dress_is_valid (AdgDress dress);
79 static gboolean _adg_dress_is_valid_with_log
80 (AdgDress dress);
81 static gboolean _adg_value_validate (GParamSpec *spec,
82 GValue *value);
83 static GArray * _adg_array_singleton (void) G_GNUC_CONST;
84 static guint _adg_array_append (AdgDressPrivate*data);
85 static AdgDressPrivate *_adg_array_lookup (guint n);
86 static guint _adg_array_len (void);
89 GType
90 adg_dress_get_type(void)
92 static GType type = 0;
94 if (G_UNLIKELY(type == 0)) {
95 const GTypeInfo info = { 0, };
97 type = g_type_register_static(G_TYPE_INT, "AdgDress", &info, 0);
99 g_value_register_transform_func(type, G_TYPE_STRING,
100 _adg_dress_to_string);
101 g_value_register_transform_func(G_TYPE_STRING, type,
102 _adg_string_to_dress);
105 return type;
109 * adg_dress_new:
110 * @name: the dress name
111 * @fallback: the fallback style
113 * Creates a new dress. It is a convenient wrapper of adg_dress_new_full()
114 * that uses as ancestor the G_TYPE_FROM_INSTANCE() of @fallback.
116 * After a succesfull call, a new reference is added to @fallback.
118 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
120 AdgDress
121 adg_dress_new(const gchar *name, AdgStyle *fallback)
123 g_return_val_if_fail(ADG_IS_STYLE(fallback), ADG_DRESS_UNDEFINED);
125 return adg_dress_new_full(name, fallback, G_TYPE_FROM_INSTANCE(fallback));
129 * adg_dress_new_full:
130 * @name: the dress name
131 * @fallback: the fallback style
132 * @ancestor_type: the common ancestor type
134 * Creates a new dress, explicitely setting the ancestor type.
135 * If @fallback is not %NULL, @ancestor_type must be present in
136 * its hierarchy: check out the adg_dress_style_is_compatible()
137 * documentation to know what the ancestor type is used for.
139 * @fallback can be %NULL, in which case a "transparent" dress
140 * is created. This kind of dress does not change the cairo
141 * context because there is no style to apply. Any entity could
142 * override it to change this behavior though.
144 * After a succesfull call, a new reference is added to @fallback
145 * if needed.
147 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
149 AdgDress
150 adg_dress_new_full(const gchar *name, AdgStyle *fallback, GType ancestor_type)
152 GQuark quark;
153 AdgDress dress;
154 AdgDressPrivate data;
156 g_return_val_if_fail(name != NULL, ADG_DRESS_UNDEFINED);
157 g_return_val_if_fail(g_type_is_a(ancestor_type, ADG_TYPE_STYLE),
158 ADG_DRESS_UNDEFINED);
159 g_return_val_if_fail(fallback == NULL ||
160 G_TYPE_CHECK_INSTANCE_TYPE(fallback, ancestor_type),
161 ADG_DRESS_UNDEFINED);
163 quark = g_quark_from_string(name);
164 dress = _adg_quark_to_dress(quark);
166 if (dress > 0) {
167 g_warning(_("%s: the `%s' name is yet used by the `%d' dress"),
168 G_STRLOC, name, dress);
169 return ADG_DRESS_UNDEFINED;
172 data.quark = quark;
173 data.fallback = fallback;
174 data.ancestor_type = ancestor_type;
176 if (fallback != NULL)
177 g_object_ref(fallback);
179 return _adg_array_append(&data) - 1;
183 * adg_dress_from_name:
184 * @name: the name of a dress
186 * Gets the dress bound to a @name string. No warnings are raised
187 * if the dress is not found.
189 * Returns: the #AdgDress code or #ADG_DRESS_UNDEFINED if not found
191 AdgDress
192 adg_dress_from_name(const gchar *name)
194 return _adg_quark_to_dress(g_quark_try_string(name));
198 * adg_dress_are_related:
199 * @dress1: an #AdgDress
200 * @dress2: another #AdgDress
202 * Checks whether @dress1 and @dress2 are related, that is
203 * if they have the same ancestor type as returned by
204 * adg_dress_get_ancestor_type().
206 * Returns: %TRUE if the dresses are related, %FALSE otherwise
208 gboolean
209 adg_dress_are_related(AdgDress dress1, AdgDress dress2)
211 GType ancestor_type1, ancestor_type2;
213 ancestor_type1 = adg_dress_get_ancestor_type(dress1);
214 if (ancestor_type1 <= 0)
215 return FALSE;
217 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
218 if (ancestor_type2 <= 0)
219 return FALSE;
221 return ancestor_type1 == ancestor_type2;
225 * adg_dress_set:
226 * @dress: a pointer to an #AdgDress
227 * @src: the source dress
229 * Copies @src in @dress. This operation can be succesful only if
230 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
231 * to @src, that is adg_dress_are_related() returns %TRUE.
233 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
235 gboolean
236 adg_dress_set(AdgDress *dress, AdgDress src)
238 if (*dress == src)
239 return FALSE;
241 if (*dress != ADG_DRESS_UNDEFINED && !adg_dress_are_related(*dress, src))
242 return FALSE;
244 *dress = src;
245 return TRUE;
249 * adg_dress_get_name:
250 * @dress: an #AdgDress
252 * Gets the name associated to @dress. No warnings are raised if
253 * @dress is not found.
255 * Returns: the requested name or %NULL if not found
257 const gchar *
258 adg_dress_get_name(AdgDress dress)
260 if (!_adg_dress_is_valid(dress))
261 return NULL;
263 return g_quark_to_string(_adg_array_lookup(dress)->quark);
267 * adg_dress_get_ancestor_type:
268 * @dress: an #AdgDress
270 * Gets the base type that should be present in every #AdgStyle
271 * acceptable by @dress. No warnings are raised if @dress
272 * is not found.
274 * Returns: the ancestor type or %0 on errors
276 GType
277 adg_dress_get_ancestor_type(AdgDress dress)
279 AdgDressPrivate *data;
281 if (!_adg_dress_is_valid(dress))
282 return 0;
284 data = _adg_array_lookup(dress);
286 return data->ancestor_type;
290 * adg_dress_set_fallback:
291 * @dress: an #AdgDress
292 * @fallback: the new fallback style
294 * Associates a new @fallback style to @dress. If the dress does
295 * not exist (it was not previously created by adg_dress_new()),
296 * a warning message is raised and the function fails.
298 * @fallback is checked for compatibily with @dress. Any dress holds
299 * an ancestor type: if this type is not found in the @fallback
300 * hierarchy, a warning message is raised and the function fails.
302 * After a succesfull call, the reference to the previous fallback
303 * (if any) is dropped while a new reference to @fallback is added.
305 void
306 adg_dress_set_fallback(AdgDress dress, AdgStyle *fallback)
308 AdgDressPrivate *data;
310 if (!_adg_dress_is_valid_with_log(dress))
311 return;
313 data = _adg_array_lookup(dress);
315 if (data->fallback == fallback)
316 return;
318 /* Check if the new fallback style is compatible with this dress */
319 if (fallback != NULL && !adg_dress_style_is_compatible(dress, fallback)) {
320 g_warning(_("%s: the fallback style of `%d' (%s) must be a `%s' derived type, but a `%s' has been provided"),
321 G_STRLOC, dress, _adg_dress_name(dress),
322 g_type_name(data->ancestor_type),
323 g_type_name(G_TYPE_FROM_INSTANCE(fallback)));
324 return;
327 if (data->fallback != NULL)
328 g_object_unref(data->fallback);
330 data->fallback = fallback;
332 if (data->fallback != NULL)
333 g_object_ref(data->fallback);
337 * adg_dress_get_fallback:
338 * @dress: an #AdgDress
340 * Gets the fallback style associated to @dress. No warnings
341 * are raised if the dress is not found.
343 * Returns: the requested #AdgStyle derived instance or %NULL if not set
345 AdgStyle *
346 adg_dress_get_fallback(AdgDress dress)
348 AdgDressPrivate *data;
350 if (!_adg_dress_is_valid(dress))
351 return NULL;
353 data = _adg_array_lookup(dress);
355 return data->fallback;
359 * adg_dress_style_is_compatible:
360 * @dress: an #AdgDress
361 * @style: the #AdgStyle to check
363 * Checks whether @style is compatible with @dress, that is if
364 * @style has the ancestor style type (as returned by
365 * adg_dress_get_ancestor_type()) in its hierarchy.
367 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise
369 gboolean
370 adg_dress_style_is_compatible(AdgDress dress, AdgStyle *style)
372 GType ancestor_type = adg_dress_get_ancestor_type(dress);
374 g_return_val_if_fail(ancestor_type > 0, FALSE);
375 g_return_val_if_fail(ADG_IS_STYLE(style), FALSE);
377 return G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type);
381 static AdgDress
382 _adg_quark_to_dress(GQuark quark)
384 AdgDress dress;
385 AdgDressPrivate *data;
387 for (dress = 0; dress < _adg_array_len(); ++dress) {
388 data = _adg_array_lookup(dress);
390 if (data->quark == quark)
391 return dress;
394 return ADG_DRESS_UNDEFINED;
397 static void
398 _adg_dress_to_string(const GValue *src, GValue *dst)
400 g_value_set_string(dst, adg_dress_get_name(g_value_get_int(src)));
403 static void
404 _adg_string_to_dress(const GValue *src, GValue *dst)
406 g_value_set_int(dst, adg_dress_from_name(g_value_get_string(src)));
409 GType
410 _adg_param_spec_dress_get_type(void)
412 static GType type = 0;
414 if (G_UNLIKELY(type == 0)) {
415 const GTypeInfo info = {
416 sizeof(GParamSpecClass),
417 NULL,
418 NULL,
419 (GClassInitFunc) _adg_param_class_init,
420 NULL,
421 NULL,
422 sizeof(AdgParamSpecDress),
426 type = g_type_register_static(G_TYPE_PARAM_INT, "AdgParamSpecDress",
427 &info, 0);
430 return type;
433 static void
434 _adg_param_class_init(GParamSpecClass *klass)
436 klass->value_type = ADG_TYPE_DRESS;
437 klass->value_validate = _adg_value_validate;
440 static const gchar *
441 _adg_dress_name(AdgDress dress)
443 const gchar *name = adg_dress_get_name(dress);
445 if (name == NULL) {
446 name = _("(UNDEFINED)");
449 return name;
452 static gboolean
453 _adg_dress_is_valid(AdgDress dress)
455 return dress > 0 && dress < _adg_array_len();
458 static gboolean
459 _adg_dress_is_valid_with_log(AdgDress dress)
461 if (!_adg_dress_is_valid(dress)) {
462 g_warning(_("%s: the dress `%d' is undefined"), G_STRLOC, dress);
463 return FALSE;
466 return TRUE;
469 static gboolean
470 _adg_value_validate(GParamSpec *spec, GValue *value)
472 AdgParamSpecDress *fspec;
473 AdgDress *dress;
474 AdgDress wanted_dress;
476 fspec = (AdgParamSpecDress *) spec;
477 dress = &value->data[0].v_int;
478 wanted_dress = *dress;
480 /* Fallback to the source dress, returned in case of errors */
481 *dress = fspec->source_dress;
483 /* This method will fail (that is, it leaves *dress untouched)
484 * if the current *dress value (source_dress) and wanted_dress
485 * are not related */
486 adg_dress_set(dress, wanted_dress);
488 return *dress != wanted_dress;
493 * adg_param_spec_dress:
494 * @name: canonical name
495 * @nick: nickname of the param
496 * @blurb: brief desciption
497 * @dress: the #AdgDress dress
498 * @flags: a combination of #GParamFlags
500 * Creates a param spec to hold a dress value.
502 * Returns: the newly allocated #GParamSpec
504 GParamSpec *
505 adg_param_spec_dress(const gchar *name, const gchar *nick, const gchar *blurb,
506 AdgDress dress, GParamFlags flags)
508 AdgParamSpecDress *fspec;
510 if (!_adg_dress_is_valid_with_log(dress))
511 return NULL;
513 fspec = g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS,
514 name, nick, blurb, flags);
515 fspec->source_dress = dress;
517 return (GParamSpec *) fspec;
521 static GArray *
522 _adg_array_singleton(void)
524 static GArray *array = NULL;
526 if (array == NULL) {
527 const AdgDressPrivate data = { 0, };
529 array = g_array_new(FALSE, FALSE, sizeof(AdgDressPrivate));
531 /* Reserve the first item for the undefined dress */
532 g_array_append_val(array, data);
535 return array;
538 static guint
539 _adg_array_append(AdgDressPrivate *data)
541 return g_array_append_val(_adg_array_singleton(), *data)->len;
544 static AdgDressPrivate *
545 _adg_array_lookup(guint n)
547 return &g_array_index(_adg_array_singleton(), AdgDressPrivate, n);
550 static guint
551 _adg_array_len(void)
553 return _adg_array_singleton()->len;