adg: cosmetic improvements in adg-dress-builtins.h
[adg.git] / src / adg / adg-dress.c
blob53855a6ea9e22abf388802558390a28e566799b1
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 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.
33 * Since: 1.0
34 **/
36 /**
37 * AdgDress:
39 * An index representing a virtual #AdgStyle.
41 * Since: 1.0
42 **/
44 /**
45 * ADG_TYPE_DRESS:
47 * The type used to express a dress index. It is defined only for GObject
48 * internal and should not be used directly (at least, as far as I know).
50 * Since: 1.0
51 **/
53 /**
54 * ADG_VALUE_HOLDS_DRESS:
55 * @value: a #GValue
57 * Checks whether a #GValue is actually holding an #AdgDress value or not.
59 * Returns: %TRUE is @value is holding an #AdgDress, %FALSE otherwise
61 * Since: 1.0
62 **/
65 #include "adg-internal.h"
66 #include "adg-style.h"
67 #include "adg-dress-builtins.h"
69 #include "adg-dress.h"
70 #include "adg-dress-private.h"
73 typedef struct _AdgParamSpecDress AdgParamSpecDress;
75 struct _AdgParamSpecDress {
76 GParamSpecInt parent;
77 AdgDress source_dress;
81 static AdgDress _adg_quark_to_dress (GQuark quark);
82 static void _adg_dress_to_string (const GValue *src,
83 GValue *dst);
84 static void _adg_string_to_dress (const GValue *src,
85 GValue *dst);
86 static void _adg_param_class_init (GParamSpecClass*klass);
87 static const gchar * _adg_dress_name (AdgDress dress);
88 static gboolean _adg_dress_is_valid (AdgDress dress);
89 static gboolean _adg_dress_is_valid_with_log
90 (AdgDress dress);
91 static gboolean _adg_value_validate (GParamSpec *spec,
92 GValue *value);
93 static GArray * _adg_array_singleton (void) G_GNUC_CONST;
94 static guint _adg_array_append (AdgDressPrivate*data);
95 static AdgDressPrivate *_adg_array_lookup (guint n);
96 static guint _adg_array_len (void);
99 GType
100 adg_dress_get_type(void)
102 static GType type = 0;
104 if (G_UNLIKELY(type == 0)) {
105 const GTypeInfo info = { 0, };
107 type = g_type_register_static(G_TYPE_INT, "AdgDress", &info, 0);
109 g_value_register_transform_func(type, G_TYPE_STRING,
110 _adg_dress_to_string);
111 g_value_register_transform_func(G_TYPE_STRING, type,
112 _adg_string_to_dress);
115 return type;
119 * adg_dress_new:
120 * @name: the dress name
121 * @fallback: (transfer full): the fallback style
123 * Creates a new dress. It is a convenient wrapper of adg_dress_new_full()
124 * that uses as ancestor the G_TYPE_FROM_INSTANCE() of @fallback.
126 * After a succesfull call, a new reference is added to @fallback.
128 * Returns: (transfer none): the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors.
130 * Since: 1.0
132 AdgDress
133 adg_dress_new(const gchar *name, AdgStyle *fallback)
135 g_return_val_if_fail(ADG_IS_STYLE(fallback), ADG_DRESS_UNDEFINED);
137 return adg_dress_new_full(name, fallback, G_TYPE_FROM_INSTANCE(fallback));
141 * adg_dress_new_full:
142 * @name: the dress name
143 * @fallback: (transfer full): the fallback style
144 * @ancestor_type: the common ancestor type
146 * Creates a new dress, explicitely setting the ancestor type.
147 * If @fallback is not %NULL, @ancestor_type must be present in
148 * its hierarchy: check out the adg_dress_style_is_compatible()
149 * documentation to know what the ancestor type is used for.
151 * @fallback can be %NULL, in which case a "transparent" dress
152 * is created. This kind of dress does not change the cairo
153 * context because there is no style to apply. Any entity could
154 * override it to change this behavior though.
156 * After a succesfull call, a new reference is added to @fallback
157 * if needed.
159 * Returns: (transfer none): the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors.
161 * Since: 1.0
163 AdgDress
164 adg_dress_new_full(const gchar *name, AdgStyle *fallback, GType ancestor_type)
166 GQuark quark;
167 AdgDress dress;
168 AdgDressPrivate data;
170 g_return_val_if_fail(name != NULL, ADG_DRESS_UNDEFINED);
171 g_return_val_if_fail(g_type_is_a(ancestor_type, ADG_TYPE_STYLE),
172 ADG_DRESS_UNDEFINED);
173 g_return_val_if_fail(fallback == NULL ||
174 G_TYPE_CHECK_INSTANCE_TYPE(fallback, ancestor_type),
175 ADG_DRESS_UNDEFINED);
177 quark = g_quark_from_string(name);
178 dress = _adg_quark_to_dress(quark);
180 if (dress > 0) {
181 g_warning(_("%s: the `%s' name is yet used by the `%d' dress"),
182 G_STRLOC, name, dress);
183 return ADG_DRESS_UNDEFINED;
186 data.quark = quark;
187 data.fallback = fallback;
188 data.ancestor_type = ancestor_type;
190 if (fallback != NULL)
191 g_object_ref(fallback);
193 return _adg_array_append(&data) - 1;
197 * adg_dress_from_name:
198 * @name: the name of a dress
200 * Gets the dress bound to a @name string. No warnings are raised
201 * if the dress is not found.
203 * Returns: (transfer none): the #AdgDress code or #ADG_DRESS_UNDEFINED if not found.
205 * Since: 1.0
207 AdgDress
208 adg_dress_from_name(const gchar *name)
210 return _adg_quark_to_dress(g_quark_try_string(name));
214 * adg_dress_are_related:
215 * @dress1: an #AdgDress
216 * @dress2: another #AdgDress
218 * Checks whether @dress1 and @dress2 are related, that is
219 * if they have the same ancestor type as returned by
220 * adg_dress_get_ancestor_type().
222 * Returns: %TRUE if the dresses are related, %FALSE otherwise.
224 * Since: 1.0
226 gboolean
227 adg_dress_are_related(AdgDress dress1, AdgDress dress2)
229 GType ancestor_type1, ancestor_type2;
231 ancestor_type1 = adg_dress_get_ancestor_type(dress1);
232 if (ancestor_type1 <= 0)
233 return FALSE;
235 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
236 if (ancestor_type2 <= 0)
237 return FALSE;
239 return ancestor_type1 == ancestor_type2;
243 * adg_dress_set:
244 * @dress: (transfer none): a pointer to an #AdgDress
245 * @src: the source dress
247 * Copies @src in @dress. This operation can be succesful only if
248 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
249 * to @src, that is adg_dress_are_related() returns %TRUE.
251 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed.
253 * Since: 1.0
255 gboolean
256 adg_dress_set(AdgDress *dress, AdgDress src)
258 if (*dress == src)
259 return FALSE;
261 if (*dress != ADG_DRESS_UNDEFINED && !adg_dress_are_related(*dress, src))
262 return FALSE;
264 *dress = src;
265 return TRUE;
269 * adg_dress_get_name:
270 * @dress: an #AdgDress
272 * Gets the name associated to @dress. No warnings are raised if
273 * @dress is not found.
275 * Returns: the requested name or %NULL if not found.
277 * Since: 1.0
279 const gchar *
280 adg_dress_get_name(AdgDress dress)
282 if (!_adg_dress_is_valid(dress))
283 return NULL;
285 return g_quark_to_string(_adg_array_lookup(dress)->quark);
289 * adg_dress_get_ancestor_type:
290 * @dress: an #AdgDress
292 * Gets the base type that should be present in every #AdgStyle
293 * acceptable by @dress. No warnings are raised if @dress
294 * is not found.
296 * Returns: the ancestor type or %0 on errors.
298 * Since: 1.0
300 GType
301 adg_dress_get_ancestor_type(AdgDress dress)
303 AdgDressPrivate *data;
305 if (!_adg_dress_is_valid(dress))
306 return 0;
308 data = _adg_array_lookup(dress);
310 return data->ancestor_type;
314 * adg_dress_set_fallback:
315 * @dress: an #AdgDress
316 * @fallback: (transfer full): the new fallback style
318 * Associates a new @fallback style to @dress. If the dress does
319 * not exist (it was not previously created by adg_dress_new()),
320 * a warning message is raised and the function fails.
322 * @fallback is checked for compatibily with @dress. Any dress holds
323 * an ancestor type: if this type is not found in the @fallback
324 * hierarchy, a warning message is raised and the function fails.
326 * After a succesfull call, the reference to the previous fallback
327 * (if any) is dropped while a new reference to @fallback is added.
329 * Since: 1.0
331 void
332 adg_dress_set_fallback(AdgDress dress, AdgStyle *fallback)
334 AdgDressPrivate *data;
336 if (!_adg_dress_is_valid_with_log(dress))
337 return;
339 data = _adg_array_lookup(dress);
341 if (data->fallback == fallback)
342 return;
344 /* Check if the new fallback style is compatible with this dress */
345 if (fallback != NULL && !adg_dress_style_is_compatible(dress, fallback)) {
346 g_warning(_("%s: the fallback style of `%d' (%s) must be a `%s' derived type, but a `%s' has been provided"),
347 G_STRLOC, dress, _adg_dress_name(dress),
348 g_type_name(data->ancestor_type),
349 g_type_name(G_TYPE_FROM_INSTANCE(fallback)));
350 return;
353 if (data->fallback != NULL)
354 g_object_unref(data->fallback);
356 data->fallback = fallback;
358 if (data->fallback != NULL)
359 g_object_ref(data->fallback);
363 * adg_dress_get_fallback:
364 * @dress: an #AdgDress
366 * Gets the fallback style associated to @dress. No warnings
367 * are raised if the dress is not found. The returned style
368 * is owned by dress and should not be freed or modified.
370 * Returns: (transfer none): the requested #AdgStyle derived instance or %NULL if not set.
372 * Since: 1.0
374 AdgStyle *
375 adg_dress_get_fallback(AdgDress dress)
377 AdgDressPrivate *data;
379 if (!_adg_dress_is_valid(dress))
380 return NULL;
382 data = _adg_array_lookup(dress);
384 return data->fallback;
388 * adg_dress_style_is_compatible:
389 * @dress: an #AdgDress
390 * @style: (transfer none): the #AdgStyle to check
392 * Checks whether @style is compatible with @dress, that is if
393 * @style has the ancestor style type (as returned by
394 * adg_dress_get_ancestor_type()) in its hierarchy.
396 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise.
398 * Since: 1.0
400 gboolean
401 adg_dress_style_is_compatible(AdgDress dress, AdgStyle *style)
403 GType ancestor_type = adg_dress_get_ancestor_type(dress);
405 g_return_val_if_fail(ancestor_type > 0, FALSE);
406 g_return_val_if_fail(ADG_IS_STYLE(style), FALSE);
408 return G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type);
412 static AdgDress
413 _adg_quark_to_dress(GQuark quark)
415 AdgDress dress;
416 AdgDressPrivate *data;
418 for (dress = 0; dress < _adg_array_len(); ++dress) {
419 data = _adg_array_lookup(dress);
421 if (data->quark == quark)
422 return dress;
425 return ADG_DRESS_UNDEFINED;
428 static void
429 _adg_dress_to_string(const GValue *src, GValue *dst)
431 g_value_set_string(dst, adg_dress_get_name(g_value_get_int(src)));
434 static void
435 _adg_string_to_dress(const GValue *src, GValue *dst)
437 g_value_set_int(dst, adg_dress_from_name(g_value_get_string(src)));
440 GType
441 _adg_param_spec_dress_get_type(void)
443 static GType type = 0;
445 if (G_UNLIKELY(type == 0)) {
446 const GTypeInfo info = {
447 sizeof(GParamSpecClass),
448 NULL,
449 NULL,
450 (GClassInitFunc) _adg_param_class_init,
451 NULL,
452 NULL,
453 sizeof(AdgParamSpecDress),
457 type = g_type_register_static(G_TYPE_PARAM_INT, "AdgParamSpecDress",
458 &info, 0);
461 return type;
464 static void
465 _adg_param_class_init(GParamSpecClass *klass)
467 klass->value_type = ADG_TYPE_DRESS;
468 klass->value_validate = _adg_value_validate;
471 static const gchar *
472 _adg_dress_name(AdgDress dress)
474 const gchar *name = adg_dress_get_name(dress);
476 if (name == NULL) {
477 name = _("(UNDEFINED)");
480 return name;
483 static gboolean
484 _adg_dress_is_valid(AdgDress dress)
486 return dress > 0 && dress < _adg_array_len();
489 static gboolean
490 _adg_dress_is_valid_with_log(AdgDress dress)
492 if (!_adg_dress_is_valid(dress)) {
493 g_warning(_("%s: the dress `%d' is undefined"), G_STRLOC, dress);
494 return FALSE;
497 return TRUE;
500 static gboolean
501 _adg_value_validate(GParamSpec *spec, GValue *value)
503 AdgParamSpecDress *fspec;
504 AdgDress *dress;
505 AdgDress wanted_dress;
507 fspec = (AdgParamSpecDress *) spec;
508 dress = &value->data[0].v_int;
509 wanted_dress = *dress;
511 /* Fallback to the source dress, returned in case of errors */
512 *dress = fspec->source_dress;
514 /* This method will fail (that is, it leaves *dress untouched)
515 * if the current *dress value (source_dress) and wanted_dress
516 * are not related */
517 adg_dress_set(dress, wanted_dress);
519 return *dress != wanted_dress;
524 * adg_param_spec_dress:
525 * @name: canonical name
526 * @nick: nickname of the param
527 * @blurb: brief desciption
528 * @dress: the #AdgDress dress
529 * @flags: a combination of #GParamFlags
531 * Creates a param spec to hold a dress value.
533 * Returns: (transfer full): the newly allocated #GParamSpec.
535 * Since: 1.0
537 GParamSpec *
538 adg_param_spec_dress(const gchar *name, const gchar *nick, const gchar *blurb,
539 AdgDress dress, GParamFlags flags)
541 AdgParamSpecDress *fspec;
543 if (!_adg_dress_is_valid_with_log(dress))
544 return NULL;
546 fspec = g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS,
547 name, nick, blurb, flags);
548 fspec->source_dress = dress;
550 return (GParamSpec *) fspec;
554 static GArray *
555 _adg_array_singleton(void)
557 static GArray *array = NULL;
559 if (array == NULL) {
560 const AdgDressPrivate data = { 0, };
562 array = g_array_new(FALSE, FALSE, sizeof(AdgDressPrivate));
564 /* Reserve the first item for the undefined dress */
565 g_array_append_val(array, data);
568 return array;
571 static guint
572 _adg_array_append(AdgDressPrivate *data)
574 return g_array_append_val(_adg_array_singleton(), *data)->len;
577 static AdgDressPrivate *
578 _adg_array_lookup(guint n)
580 return &g_array_index(_adg_array_singleton(), AdgDressPrivate, n);
583 static guint
584 _adg_array_len(void)
586 return _adg_array_singleton()->len;