[docs] Updated TODO.xml
[adg.git] / adg / adg-dress.c
blobcab49ab4897c6ccb26c05cdbd4b7a7fe6c63f765
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_from_name:
169 * @name: the name of a dress
171 * Gets the dress bound to a @name string. No warnings are raised
172 * if the dress is not found.
174 * Returns: the #AdgDress code or #ADG_DRESS_UNDEFINED if not found
176 AdgDress
177 adg_dress_from_name(const gchar *name)
179 return quark_to_dress(g_quark_try_string(name));
183 * adg_dress_are_related:
184 * @dress1: an #AdgDress
185 * @dress2: another #AdgDress
187 * Checks whether @dress1 and @dress2 are related, that is
188 * if they have the same ancestor type as returned by
189 * adg_dress_get_ancestor_type().
191 * Returns: %TRUE if the dresses are related, %FALSE otherwise
193 gboolean
194 adg_dress_are_related(AdgDress dress1, AdgDress dress2)
196 GType ancestor_type1, ancestor_type2;
198 ancestor_type1 = adg_dress_get_ancestor_type(dress1);
199 if (ancestor_type1 <= 0)
200 return FALSE;
202 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
203 if (ancestor_type2 <= 0)
204 return FALSE;
206 return ancestor_type1 == ancestor_type2;
210 * adg_dress_set:
211 * @dress: a pointer to an #AdgDress
212 * @src: the source dress
214 * Copies @src in @dress. This operation can be succesful only if
215 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
216 * to @src, that is adg_dress_are_related() returns %TRUE.
218 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
220 gboolean
221 adg_dress_set(AdgDress *dress, AdgDress src)
223 if (*dress == src)
224 return FALSE;
226 if (*dress != ADG_DRESS_UNDEFINED &&
227 !adg_dress_are_related(*dress, src)) {
228 const gchar *dress_name;
229 const gchar *src_name;
231 dress_name = adg_dress_get_name(*dress);
232 if (dress_name == NULL)
233 dress_name = "UNDEFINED";
235 src_name = adg_dress_get_name(src);
236 if (src_name == NULL)
237 src_name = "UNDEFINED";
239 g_warning("%s: `%d' (%s) and `%d' (%s) dresses are not related",
240 G_STRLOC, *dress, dress_name, src, src_name);
242 return FALSE;
245 *dress = src;
247 return TRUE;
251 * adg_dress_get_name:
252 * @dress: an #AdgDress
254 * Gets the name associated to @dress. No warnings are raised if
255 * @dress is not found.
257 * Returns: the requested name or %NULL if not found
259 const gchar *
260 adg_dress_get_name(AdgDress dress)
262 if (dress <= 0 || dress >= array_len())
263 return NULL;
265 return g_quark_to_string(array_lookup(dress)->quark);
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_set_fallback:
292 * @dress: an #AdgDress
293 * @fallback: the new fallback style
295 * Associates a new @fallback style to @dress. If the dress does
296 * not exist (it was not previously created by adg_dress_new()),
297 * a warning message is raised and the function fails.
299 * @fallback is checked for compatibily with @dress. Any dress holds
300 * an ancestor type: if this type is not found in the @fallback
301 * hierarchy, a warning message is raised and the function fails.
303 * After a succesfull call, the reference to the previous fallback
304 * (if any) is dropped while a new reference to @fallback is added.
306 void
307 adg_dress_set_fallback(AdgDress dress, AdgStyle *fallback)
309 AdgDressPrivate *data;
311 if (dress <= 0 || dress >= array_len()) {
312 g_warning("%s: `%d' dress undefined", G_STRLOC, dress);
313 return;
316 data = array_lookup(dress);
318 if (data->fallback == fallback)
319 return;
321 /* Check if the new fallback style is compatible with this dress */
322 if (fallback != NULL && !adg_dress_style_is_compatible(dress, fallback)) {
323 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
324 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(fallback)),
325 g_type_name(data->ancestor_type), adg_dress_get_name(dress));
326 return;
329 if (data->fallback != NULL)
330 g_object_unref(data->fallback);
332 data->fallback = fallback;
334 if (data->fallback != NULL)
335 g_object_ref(data->fallback);
339 * adg_dress_get_fallback:
340 * @dress: an #AdgDress
342 * Gets the fallback style associated to @dress. No warnings
343 * are raised if the dress is not found.
345 * Returns: the requested #AdgStyle derived instance or %NULL if not set
347 AdgStyle *
348 adg_dress_get_fallback(AdgDress dress)
350 AdgDressPrivate *data;
352 if (dress <= 0 || dress >= array_len())
353 return NULL;
355 data = array_lookup(dress);
357 return 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_get_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;