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.
23 * @Section_Id: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.
37 * An index representing a virtual #AdgStyle.
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).
48 * ADG_VALUE_HOLDS_DRESS:
51 * Checks whether a #GValue is actually holding an #AdgDress value or not.
53 * Returns: %TRUE is @value is holding an #AdgDress, %FALSE otherwise
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
,
65 static void string_to_dress (const GValue
*src
,
67 static void param_class_init (GParamSpecClass
*klass
);
68 static gboolean
value_validate (GParamSpec
*spec
,
71 static guint
array_append (AdgDressPrivate
*data
);
72 static AdgDressPrivate
*array_lookup (guint n
);
73 static guint
array_len (void);
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
);
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
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
132 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
135 adg_dress_new_full(const gchar
*name
, AdgStyle
*fallback
, GType ancestor_type
)
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
);
152 g_warning("%s: `%s' name yet used by the `%d' dress",
153 G_STRLOC
, name
, dress
);
154 return ADG_DRESS_UNDEFINED
;
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
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
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)
202 ancestor_type2
= adg_dress_get_ancestor_type(dress2
);
203 if (ancestor_type2
<= 0)
206 return ancestor_type1
== ancestor_type2
;
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
221 adg_dress_set(AdgDress
*dress
, AdgDress src
)
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
);
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
260 adg_dress_get_name(AdgDress dress
)
262 if (dress
<= 0 || dress
>= array_len())
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
278 adg_dress_get_ancestor_type(AdgDress dress
)
280 AdgDressPrivate
*data
;
282 if (dress
<= 0 || dress
>= array_len())
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
300 adg_dress_get_fallback(AdgDress dress
)
302 AdgDressPrivate
*data
;
304 if (dress
<= 0 || dress
>= array_len())
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.
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
);
338 data
= array_lookup(dress
);
340 if (data
->fallback
== fallback
)
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_get_name(dress
));
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
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
);
384 quark_to_dress(GQuark quark
)
387 AdgDressPrivate
*data
;
389 for (dress
= 0; dress
< array_len(); ++dress
) {
390 data
= array_lookup(dress
);
392 if (data
->quark
== quark
)
396 return ADG_DRESS_UNDEFINED
;
400 dress_to_string(const GValue
*src
, GValue
*dst
)
402 g_value_set_string(dst
, adg_dress_get_name(g_value_get_int(src
)));
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
;
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
),
430 (GClassInitFunc
) param_class_init
,
433 sizeof(AdgParamSpecDress
),
437 type
= g_type_register_static(G_TYPE_PARAM_INT
,
438 "AdgParamSpecDress", &info
, 0);
445 param_class_init(GParamSpecClass
*klass
)
447 klass
->value_type
= ADG_TYPE_DRESS
;
448 klass
->value_validate
= value_validate
;
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
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
);
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
;
502 array_singleton(void)
504 static GArray
*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
);
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
);
533 return array_singleton()->len
;