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 * @short_description: The ADG way to associate styles to entities
25 * The dress is a virtualization of an #AdgStyle instance. #AdgEntity
26 * instances do not directly embed #AdgStyle but use instead #AdgDress.
27 * This allows some advanced operations, such as overriding a dress
28 * only in a specific entity branch of the hierarchy or customize
29 * multiple entities at once.
35 * An index representing a virtual #AdgStyle.
41 * A #GParamSpec for #AdgDress values.
45 #include "adg-dress.h"
46 #include "adg-dress-private.h"
47 #include "adg-color-style.h"
48 #include "adg-line-style.h"
49 #include "adg-font-style.h"
50 #include "adg-dim-style.h"
53 static AdgDress
quark_to_dress (GQuark quark
);
54 static void dress_to_string (const GValue
*src
,
56 static void string_to_dress (const GValue
*src
,
58 static void param_class_init (GParamSpecClass
*klass
);
59 static gboolean
value_validate (GParamSpec
*spec
,
62 static guint
array_append (AdgDressPrivate
*data
);
63 static AdgDressPrivate
*
64 array_lookup (guint n
);
65 static guint
array_len (void);
69 adg_dress_get_type(void)
71 static GType type
= 0;
73 if (G_UNLIKELY(type
== 0)) {
74 const GTypeInfo info
= { 0, };
76 type
= g_type_register_static(G_TYPE_INT
, "AdgDress", &info
, 0);
78 g_value_register_transform_func(type
, G_TYPE_STRING
, dress_to_string
);
79 g_value_register_transform_func(G_TYPE_STRING
, type
, string_to_dress
);
87 * @name: the dress name
88 * @style: the style to associate
90 * Creates a new dress. It is a convenient wrapper for adg_dress_new_full()
91 * that uses as ancestor the #G_TYPE_FROM_INSTANCE of @style.
93 * After a succesfull call, a new reference is added to @style.
95 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
98 adg_dress_new(const gchar
*name
, AdgStyle
*style
)
100 return adg_dress_new_full(name
, style
, G_TYPE_FROM_INSTANCE(style
));
104 * adg_dress_new_full:
105 * @name: the dress name
106 * @style: the style to associate
107 * @ancestor_type: the common ancestor type
109 * Creates a new dress, explicitely setting the ancestor type.
110 * @ancestor_type must be present in the @style hierarchy: check
111 * out the adg_dress_set_style() documentation to know what the
112 * ancestor type is used for.
114 * If a dress with the same name exists, a warning is raised and
115 * #ADG_DRESS_UNDEFINED is returned without further actions.
117 * After a succesfull call, a new reference is added to @style.
119 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
122 adg_dress_new_full(const gchar
*name
, AdgStyle
*style
, GType ancestor_type
)
126 AdgDressPrivate data
;
128 g_return_val_if_fail(name
!= NULL
, 0);
129 g_return_val_if_fail(style
!= NULL
, 0);
130 g_return_val_if_fail(G_TYPE_CHECK_INSTANCE_TYPE(style
, ancestor_type
), 0);
132 quark
= g_quark_from_string(name
);
133 dress
= quark_to_dress(quark
);
136 g_warning("%s: `%s' name yet used by the `%d' dress",
137 G_STRLOC
, name
, dress
);
138 return ADG_DRESS_UNDEFINED
;
143 data
.ancestor_type
= ancestor_type
;
147 return array_append(&data
) - 1;
151 * adg_dress_are_related:
152 * @dress1: an #AdgDress
153 * @dress2: another #AdgDress
155 * Checks whether @dress1 and @dress2 are related, that is
156 * if they have the same ancestor type as returned by
157 * adg_dress_get_ancestor_type().
159 * Returns: %TRUE if the dresses are related, %FALSE otherwise
162 adg_dress_are_related(AdgDress dress1
, AdgDress dress2
)
164 GType ancestor_type1
, ancestor_type2
;
166 ancestor_type1
= adg_dress_get_ancestor_type(dress1
);
167 if (ancestor_type1
<= 0)
170 ancestor_type2
= adg_dress_get_ancestor_type(dress2
);
171 if (ancestor_type2
<= 0)
174 return ancestor_type1
== ancestor_type2
;
179 * @dress: a pointer to an #AdgDress
180 * @src: the source dress
182 * Copies @src in @dress. This operation can be succesful only if
183 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
184 * to @src, that is adg_dress_are_related() returns %TRUE.
186 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
189 adg_dress_set(AdgDress
*dress
, AdgDress src
)
194 if (*dress
!= ADG_DRESS_UNDEFINED
&&
195 !adg_dress_are_related(*dress
, src
)) {
196 const gchar
*dress_name
;
197 const gchar
*src_name
;
199 dress_name
= adg_dress_name(*dress
);
200 if (dress_name
== NULL
)
201 dress_name
= "UNDEFINED";
203 src_name
= adg_dress_name(src
);
204 if (src_name
== NULL
)
205 src_name
= "UNDEFINED";
207 g_warning("%s: `%d' (%s) and `%d' (%s) dresses are not related",
208 G_STRLOC
, *dress
, dress_name
, src
, src_name
);
220 * @dress: an #AdgDress
222 * Gets the name associated to @dress. No warnings are raised if
223 * @dress is not found.
225 * Returns: the requested name or %NULL if not found
228 adg_dress_name(AdgDress dress
)
230 if (dress
<= 0 || dress
>= array_len())
233 return g_quark_to_string(array_lookup(dress
)->quark
);
237 * adg_dress_from_name:
238 * @name: the name of a dress
240 * Gets the dress binded to a @name string. No warnings are raised
241 * if the dress is not found.
243 * Returns: the #AdgDress code or #ADG_DRESS_UNDEFINED if not found
246 adg_dress_from_name(const gchar
*name
)
248 return quark_to_dress(g_quark_try_string(name
));
252 * adg_dress_get_ancestor_type:
253 * @dress: an #AdgDress
255 * Gets the base type that should be present in every #AdgStyle
256 * acceptable by @dress.
258 * Returns: the ancestor type
261 adg_dress_get_ancestor_type(AdgDress dress
)
263 AdgDressPrivate
*data
;
265 if (dress
<= 0 || dress
>= array_len())
268 data
= array_lookup(dress
);
270 return data
->ancestor_type
;
274 * adg_dress_get_style:
275 * @dress: an #AdgDress
277 * Gets the style associated to @dress. No warnings are raised
278 * if the dress is not found.
280 * Returns: the requested #AdgStyle derived instance or %NULL if not found
283 adg_dress_get_style(AdgDress dress
)
285 AdgDressPrivate
*data
;
287 if (dress
<= 0 || dress
>= array_len())
290 data
= array_lookup(dress
);
296 * adg_dress_set_style:
297 * @dress: an #AdgDress
298 * @style: the new style
300 * Associates a new @style to @dress. If the dress does not exist
301 * (it was not previously created by adg_dress_new()), a warning
302 * message is raised and the function fails.
304 * @style is checked for compatibily with @dress. Any dress holds
305 * an ancestor type: if this type is not found in the @style
306 * hierarchy, a warning message is raised the function fails.
308 * After a succesfull call, the reference to the old style (if any)
309 * is dropped while a new reference to @style is added.
312 adg_dress_set_style(AdgDress dress
, AdgStyle
*style
)
314 AdgDressPrivate
*data
;
316 if (dress
<= 0 || dress
>= array_len()) {
317 g_warning("%s: `%d' dress undefined", G_STRLOC
, dress
);
321 data
= array_lookup(dress
);
323 if (data
->style
== style
)
326 /* Check if the new style is compatible with this dress */
327 if (!G_TYPE_CHECK_INSTANCE_TYPE(style
, data
->ancestor_type
)) {
328 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
329 G_STRLOC
, g_type_name(G_TYPE_FROM_INSTANCE(style
)),
330 g_type_name(data
->ancestor_type
), adg_dress_name(dress
));
334 if (data
->style
!= NULL
)
335 g_object_unref(data
->style
);
339 if (data
->style
!= NULL
)
340 g_object_ref(data
->style
);
344 * adg_dress_accept_style:
345 * @dress: an #AdgDress
346 * @style: the #AdgStyle to check
348 * Checks whether @style could be associated to @dress using
349 * adg_dress_set_style().
351 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise
354 adg_dress_accept_style(AdgDress dress
, AdgStyle
*style
)
356 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
358 g_return_val_if_fail(ancestor_type
> 0, FALSE
);
359 g_return_val_if_fail(ADG_IS_STYLE(style
), FALSE
);
361 return G_TYPE_CHECK_INSTANCE_TYPE(style
, ancestor_type
);
366 quark_to_dress(GQuark quark
)
369 AdgDressPrivate
*data
;
371 for (dress
= 0; dress
< array_len(); ++dress
) {
372 data
= array_lookup(dress
);
374 if (data
->quark
== quark
)
378 return ADG_DRESS_UNDEFINED
;
382 dress_to_string(const GValue
*src
, GValue
*dst
)
384 g_value_set_string(dst
, adg_dress_name(g_value_get_int(src
)));
388 string_to_dress(const GValue
*src
, GValue
*dst
)
390 g_value_set_int(dst
, adg_dress_from_name(g_value_get_string(src
)));
394 typedef struct _AdgParamSpecDress AdgParamSpecDress
;
396 struct _AdgParamSpecDress
{
397 GParamSpecInt parent
;
398 AdgDress source_dress
;
403 adg_param_spec_dress_get_type(void)
405 static GType type
= 0;
407 if (G_UNLIKELY(type
== 0)) {
408 const GTypeInfo info
= {
409 sizeof(GParamSpecClass
),
412 (GClassInitFunc
) param_class_init
,
415 sizeof(AdgParamSpecDress
),
419 type
= g_type_register_static(G_TYPE_PARAM_INT
,
420 "AdgParamSpecDress", &info
, 0);
427 param_class_init(GParamSpecClass
*klass
)
429 klass
->value_type
= ADG_TYPE_DRESS
;
430 klass
->value_validate
= value_validate
;
434 value_validate(GParamSpec
*spec
, GValue
*value
)
436 AdgParamSpecDress
*fspec
;
437 AdgDress dress
, new_dress
;
439 fspec
= (AdgParamSpecDress
*) spec
;
440 dress
= value
->data
[0].v_int
;
441 new_dress
= ADG_DRESS_UNDEFINED
;
443 adg_dress_set(&new_dress
, dress
);
444 value
->data
[0].v_int
= new_dress
;
446 return dress
!= new_dress
;
451 * adg_param_spec_dress:
452 * @name: canonical name
453 * @nick: nickname of the param
454 * @blurb: brief desciption
455 * @dress: the #AdgDress dress
456 * @flags: a combination of #GParamFlags
458 * Creates a param spec to hold a dress param.
460 * Returns: the newly allocated #GParamSpec
463 adg_param_spec_dress(const gchar
*name
, const gchar
*nick
, const gchar
*blurb
,
464 AdgDress dress
, GParamFlags flags
)
466 AdgParamSpecDress
*fspec
;
468 if (dress
<= 0 || dress
>= array_len()) {
469 g_warning("%s: `%d' dress undefined", G_STRLOC
, dress
);
473 fspec
= g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS
,
474 name
, nick
, blurb
, flags
);
475 fspec
->source_dress
= dress
;
477 return (GParamSpec
*) fspec
;
482 _adg_dress_color_regular(void)
484 static AdgDress dress
= 0;
486 if (G_UNLIKELY(dress
== 0)) {
487 AdgStyle
*style
= g_object_new(ADG_TYPE_COLOR_STYLE
, NULL
);
489 dress
= adg_dress_new("color-regular", style
);
490 g_object_unref(style
);
497 _adg_dress_color_dimension(void)
499 static AdgDress dress
= 0;
501 if (G_UNLIKELY(dress
== 0)) {
502 AdgStyle
*style
= g_object_new(ADG_TYPE_COLOR_STYLE
,
505 dress
= adg_dress_new("color-dimension", style
);
506 g_object_unref(style
);
513 _adg_dress_line_regular(void)
515 static AdgDress dress
= 0;
517 if (G_UNLIKELY(dress
== 0)) {
518 AdgStyle
*style
= g_object_new(ADG_TYPE_LINE_STYLE
,
521 dress
= adg_dress_new("line-regular", style
);
522 g_object_unref(style
);
529 _adg_dress_line_dimension(void)
531 static AdgDress dress
= 0;
533 if (G_UNLIKELY(dress
== 0)) {
534 AdgStyle
*style
= g_object_new(ADG_TYPE_LINE_STYLE
,
535 "color-dress", ADG_DRESS_COLOR_DIMENSION
,
538 dress
= adg_dress_new("line-dimension", style
);
539 g_object_unref(style
);
546 _adg_dress_text_regular(void)
548 static AdgDress dress
= 0;
550 if (G_UNLIKELY(dress
== 0)) {
551 AdgStyle
*style
= g_object_new(ADG_TYPE_FONT_STYLE
,
555 dress
= adg_dress_new("text-regular", style
);
556 g_object_unref(style
);
563 _adg_dress_text_value(void)
565 static AdgDress dress
= 0;
567 if (G_UNLIKELY(dress
== 0)) {
568 AdgStyle
*style
= g_object_new(ADG_TYPE_FONT_STYLE
,
570 "weight", CAIRO_FONT_WEIGHT_BOLD
,
573 dress
= adg_dress_new("text-value", style
);
574 g_object_unref(style
);
581 _adg_dress_text_limit(void)
583 static AdgDress dress
= 0;
585 if (G_UNLIKELY(dress
== 0)) {
586 AdgStyle
*style
= g_object_new(ADG_TYPE_FONT_STYLE
,
590 dress
= adg_dress_new("text-limit", style
);
591 g_object_unref(style
);
598 _adg_dress_dimension_regular(void)
600 static AdgDress dress
= 0;
602 if (G_UNLIKELY(dress
== 0)) {
603 AdgStyle
*style
= g_object_new(ADG_TYPE_DIM_STYLE
, NULL
);
605 dress
= adg_dress_new("dimension-regular", style
);
606 g_object_unref(style
);
613 static GArray
* array_singleton (void) G_GNUC_CONST
;
616 array_singleton(void)
618 static GArray
*array
= NULL
;
621 const AdgDressPrivate data
= { 0, };
623 array
= g_array_new(FALSE
, FALSE
, sizeof(AdgDressPrivate
));
625 /* Reserve the first item for the undefined dress */
626 g_array_append_val(array
, data
);
633 array_append(AdgDressPrivate
*data
)
635 return g_array_append_val(array_singleton(), *data
)->len
;
638 static AdgDressPrivate
*
639 array_lookup(guint n
)
641 return &g_array_index(array_singleton(), AdgDressPrivate
, n
);
647 return array_singleton()->len
;