[AdgDress] Avoid ref on transparent dresses
[adg.git] / adg / adg-dress.c
blobf2b29a0c39a6b8dec8ef241f512a677c4ef3630a
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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-dress.h"
58 #include "adg-dress-private.h"
59 #include "adg-dress-builtins.h"
62 static AdgDress quark_to_dress (GQuark quark);
63 static void dress_to_string (const GValue *src,
64 GValue *dst);
65 static void string_to_dress (const GValue *src,
66 GValue *dst);
67 static void param_class_init (GParamSpecClass*klass);
68 static gboolean value_validate (GParamSpec *spec,
69 GValue *value);
71 static guint array_append (AdgDressPrivate*data);
72 static AdgDressPrivate *array_lookup (guint n);
73 static guint array_len (void);
76 GType
77 adg_dress_get_type(void)
79 static GType type = 0;
81 if (G_UNLIKELY(type == 0)) {
82 const GTypeInfo info = { 0, };
84 type = g_type_register_static(G_TYPE_INT, "AdgDress", &info, 0);
86 g_value_register_transform_func(type, G_TYPE_STRING, dress_to_string);
87 g_value_register_transform_func(G_TYPE_STRING, type, string_to_dress);
90 return type;
93 /**
94 * adg_dress_new:
95 * @name: the dress name
96 * @fallback: the fallback style
98 * Creates a new dress. It is a convenient wrapper of adg_dress_new_full()
99 * that uses as ancestor the G_TYPE_FROM_INSTANCE() of @fallback.
101 * After a succesfull call, a new reference is added to @fallback.
103 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
105 AdgDress
106 adg_dress_new(const gchar *name, AdgStyle *fallback)
108 g_return_val_if_fail(ADG_IS_STYLE(fallback), ADG_DRESS_UNDEFINED);
110 return adg_dress_new_full(name, fallback, G_TYPE_FROM_INSTANCE(fallback));
114 * adg_dress_new_full:
115 * @name: the dress name
116 * @fallback: the fallback style
117 * @ancestor_type: the common ancestor type
119 * Creates a new dress, explicitely setting the ancestor type.
120 * If @fallback is not %NULL, @ancestor_type must be present in
121 * its hierarchy: check out the adg_dress_style_is_compatible()
122 * documentation to know what the ancestor type is used for.
124 * @fallback can be %NULL, in which case a "transparent" dress
125 * is created. This kind of dress does not change the cairo
126 * context because there is no style to apply. Any entity could
127 * override it to change this behavior though.
129 * After a succesfull call, a new reference is added to @fallback
130 * if needed.
132 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
134 AdgDress
135 adg_dress_new_full(const gchar *name, AdgStyle *fallback, GType ancestor_type)
137 GQuark quark;
138 AdgDress dress;
139 AdgDressPrivate data;
141 g_return_val_if_fail(name != NULL, ADG_DRESS_UNDEFINED);
142 g_return_val_if_fail(g_type_is_a(ancestor_type, ADG_TYPE_STYLE),
143 ADG_DRESS_UNDEFINED);
144 g_return_val_if_fail(fallback == NULL ||
145 G_TYPE_CHECK_INSTANCE_TYPE(fallback, ancestor_type),
146 ADG_DRESS_UNDEFINED);
148 quark = g_quark_from_string(name);
149 dress = quark_to_dress(quark);
151 if (dress > 0) {
152 g_warning("%s: `%s' name yet used by the `%d' dress",
153 G_STRLOC, name, dress);
154 return ADG_DRESS_UNDEFINED;
157 data.quark = quark;
158 data.fallback = fallback;
159 data.ancestor_type = ancestor_type;
161 if (fallback != NULL)
162 g_object_ref(fallback);
164 return array_append(&data) - 1;
168 * adg_dress_are_related:
169 * @dress1: an #AdgDress
170 * @dress2: another #AdgDress
172 * Checks whether @dress1 and @dress2 are related, that is
173 * if they have the same ancestor type as returned by
174 * adg_dress_get_ancestor_type().
176 * Returns: %TRUE if the dresses are related, %FALSE otherwise
178 gboolean
179 adg_dress_are_related(AdgDress dress1, AdgDress dress2)
181 GType ancestor_type1, ancestor_type2;
183 ancestor_type1 = adg_dress_get_ancestor_type(dress1);
184 if (ancestor_type1 <= 0)
185 return FALSE;
187 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
188 if (ancestor_type2 <= 0)
189 return FALSE;
191 return ancestor_type1 == ancestor_type2;
195 * adg_dress_set:
196 * @dress: a pointer to an #AdgDress
197 * @src: the source dress
199 * Copies @src in @dress. This operation can be succesful only if
200 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
201 * to @src, that is adg_dress_are_related() returns %TRUE.
203 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
205 gboolean
206 adg_dress_set(AdgDress *dress, AdgDress src)
208 if (*dress == src)
209 return FALSE;
211 if (*dress != ADG_DRESS_UNDEFINED &&
212 !adg_dress_are_related(*dress, src)) {
213 const gchar *dress_name;
214 const gchar *src_name;
216 dress_name = adg_dress_name(*dress);
217 if (dress_name == NULL)
218 dress_name = "UNDEFINED";
220 src_name = adg_dress_name(src);
221 if (src_name == NULL)
222 src_name = "UNDEFINED";
224 g_warning("%s: `%d' (%s) and `%d' (%s) dresses are not related",
225 G_STRLOC, *dress, dress_name, src, src_name);
227 return FALSE;
230 *dress = src;
232 return TRUE;
236 * adg_dress_name:
237 * @dress: an #AdgDress
239 * Gets the name associated to @dress. No warnings are raised if
240 * @dress is not found.
242 * Returns: the requested name or %NULL if not found
244 const gchar *
245 adg_dress_name(AdgDress dress)
247 if (dress <= 0 || dress >= array_len())
248 return NULL;
250 return g_quark_to_string(array_lookup(dress)->quark);
254 * adg_dress_from_name:
255 * @name: the name of a dress
257 * Gets the dress bound to a @name string. No warnings are raised
258 * if the dress is not found.
260 * Returns: the #AdgDress code or #ADG_DRESS_UNDEFINED if not found
262 AdgDress
263 adg_dress_from_name(const gchar *name)
265 return quark_to_dress(g_quark_try_string(name));
269 * adg_dress_get_ancestor_type:
270 * @dress: an #AdgDress
272 * Gets the base type that should be present in every #AdgStyle
273 * acceptable by @dress.
275 * Returns: the ancestor type
277 GType
278 adg_dress_get_ancestor_type(AdgDress dress)
280 AdgDressPrivate *data;
282 if (dress <= 0 || dress >= array_len())
283 return 0;
285 data = array_lookup(dress);
287 return data->ancestor_type;
291 * adg_dress_get_fallback:
292 * @dress: an #AdgDress
294 * Gets the fallback style associated to @dress. No warnings
295 * are raised if the dress is not found.
297 * Returns: the requested #AdgStyle derived instance or %NULL if not set
299 AdgStyle *
300 adg_dress_get_fallback(AdgDress dress)
302 AdgDressPrivate *data;
304 if (dress <= 0 || dress >= array_len())
305 return NULL;
307 data = array_lookup(dress);
309 return data->fallback;
313 * adg_dress_set_fallback:
314 * @dress: an #AdgDress
315 * @fallback: the new fallback style
317 * Associates a new @fallback style to @dress. If the dress does
318 * not exist (it was not previously created by adg_dress_new()),
319 * a warning message is raised and the function fails.
321 * @fallback is checked for compatibily with @dress. Any dress holds
322 * an ancestor type: if this type is not found in the @fallback
323 * hierarchy, a warning message is raised and the function fails.
325 * After a succesfull call, the reference to the previous fallback
326 * (if any) is dropped while a new reference to @fallback is added.
328 void
329 adg_dress_set_fallback(AdgDress dress, AdgStyle *fallback)
331 AdgDressPrivate *data;
333 if (dress <= 0 || dress >= array_len()) {
334 g_warning("%s: `%d' dress undefined", G_STRLOC, dress);
335 return;
338 data = array_lookup(dress);
340 if (data->fallback == fallback)
341 return;
343 /* Check if the new fallback style is compatible with this dress */
344 if (fallback != NULL && !adg_dress_style_is_compatible(dress, fallback)) {
345 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
346 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(fallback)),
347 g_type_name(data->ancestor_type), adg_dress_name(dress));
348 return;
351 if (data->fallback != NULL)
352 g_object_unref(data->fallback);
354 data->fallback = fallback;
356 if (data->fallback != NULL)
357 g_object_ref(data->fallback);
361 * adg_dress_style_is_compatible:
362 * @dress: an #AdgDress
363 * @style: the #AdgStyle to check
365 * Checks whether @style is compatible with @dress, that is if
366 * @style has the ancestor style type (as returned by
367 * adg_dress_get_ancestor_type()) in its hierarchy.
369 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise
371 gboolean
372 adg_dress_style_is_compatible(AdgDress dress, AdgStyle *style)
374 GType ancestor_type = adg_dress_get_ancestor_type(dress);
376 g_return_val_if_fail(ancestor_type > 0, FALSE);
377 g_return_val_if_fail(ADG_IS_STYLE(style), FALSE);
379 return G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type);
383 static AdgDress
384 quark_to_dress(GQuark quark)
386 AdgDress dress;
387 AdgDressPrivate *data;
389 for (dress = 0; dress < array_len(); ++dress) {
390 data = array_lookup(dress);
392 if (data->quark == quark)
393 return dress;
396 return ADG_DRESS_UNDEFINED;
399 static void
400 dress_to_string(const GValue *src, GValue *dst)
402 g_value_set_string(dst, adg_dress_name(g_value_get_int(src)));
405 static void
406 string_to_dress(const GValue *src, GValue *dst)
408 g_value_set_int(dst, adg_dress_from_name(g_value_get_string(src)));
412 typedef struct _AdgParamSpecDress AdgParamSpecDress;
414 struct _AdgParamSpecDress {
415 GParamSpecInt parent;
416 AdgDress source_dress;
420 GType
421 _adg_param_spec_dress_get_type(void)
423 static GType type = 0;
425 if (G_UNLIKELY(type == 0)) {
426 const GTypeInfo info = {
427 sizeof(GParamSpecClass),
428 NULL,
429 NULL,
430 (GClassInitFunc) param_class_init,
431 NULL,
432 NULL,
433 sizeof(AdgParamSpecDress),
437 type = g_type_register_static(G_TYPE_PARAM_INT,
438 "AdgParamSpecDress", &info, 0);
441 return type;
444 static void
445 param_class_init(GParamSpecClass *klass)
447 klass->value_type = ADG_TYPE_DRESS;
448 klass->value_validate = value_validate;
451 static gboolean
452 value_validate(GParamSpec *spec, GValue *value)
454 AdgParamSpecDress *fspec;
455 AdgDress dress, new_dress;
457 fspec = (AdgParamSpecDress *) spec;
458 dress = value->data[0].v_int;
459 new_dress = ADG_DRESS_UNDEFINED;
461 adg_dress_set(&new_dress, dress);
462 value->data[0].v_int = new_dress;
464 return dress != new_dress;
469 * adg_param_spec_dress:
470 * @name: canonical name
471 * @nick: nickname of the param
472 * @blurb: brief desciption
473 * @dress: the #AdgDress dress
474 * @flags: a combination of #GParamFlags
476 * Creates a param spec to hold a dress value.
478 * Returns: the newly allocated #GParamSpec
480 GParamSpec *
481 adg_param_spec_dress(const gchar *name, const gchar *nick, const gchar *blurb,
482 AdgDress dress, GParamFlags flags)
484 AdgParamSpecDress *fspec;
486 if (dress <= 0 || dress >= array_len()) {
487 g_warning("%s: `%d' dress undefined", G_STRLOC, dress);
488 return NULL;
491 fspec = g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS,
492 name, nick, blurb, flags);
493 fspec->source_dress = dress;
495 return (GParamSpec *) fspec;
499 static GArray * array_singleton (void) G_GNUC_CONST;
501 static GArray *
502 array_singleton(void)
504 static GArray *array = NULL;
506 if (array == NULL) {
507 const AdgDressPrivate data = { 0, };
509 array = g_array_new(FALSE, FALSE, sizeof(AdgDressPrivate));
511 /* Reserve the first item for the undefined dress */
512 g_array_append_val(array, data);
515 return array;
518 static guint
519 array_append(AdgDressPrivate *data)
521 return g_array_append_val(array_singleton(), *data)->len;
524 static AdgDressPrivate *
525 array_lookup(guint n)
527 return &g_array_index(array_singleton(), AdgDressPrivate, n);
530 static guint
531 array_len(void)
533 return array_singleton()->len;