1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012 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.
39 * An index representing a virtual #AdgStyle.
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).
54 * ADG_VALUE_HOLDS_DRESS:
57 * Checks whether a #GValue is actually holding an #AdgDress value or not.
59 * Returns: %TRUE is @value is holding an #AdgDress, %FALSE otherwise
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
{
77 AdgDress source_dress
;
81 static AdgDress
_adg_quark_to_dress (GQuark quark
);
82 static void _adg_dress_to_string (const GValue
*src
,
84 static void _adg_string_to_dress (const GValue
*src
,
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
91 static gboolean
_adg_value_validate (GParamSpec
*spec
,
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);
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
);
120 * @name: the dress name
121 * @fallback: 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: (type gint) (transfer full): the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors.
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: 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
159 * Returns: (type gint) (transfer full): the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
164 adg_dress_new_full(const gchar
*name
, AdgStyle
*fallback
, GType ancestor_type
)
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
);
181 g_warning(_("%s: the `%s' name is yet used by the `%d' dress"),
182 G_STRLOC
, name
, dress
);
183 return ADG_DRESS_UNDEFINED
;
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: (transfer none): 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
204 * #ADG_DRESS_UNDEFINED if not found.
209 adg_dress_from_name(const gchar
*name
)
211 return _adg_quark_to_dress(g_quark_try_string(name
));
215 * adg_dress_are_related:
216 * @dress1: an #AdgDress
217 * @dress2: another #AdgDress
219 * Checks whether @dress1 and @dress2 are related, that is
220 * if they have the same ancestor type as returned by
221 * adg_dress_get_ancestor_type().
223 * Returns: %TRUE if the dresses are related, %FALSE otherwise
228 adg_dress_are_related(AdgDress dress1
, AdgDress dress2
)
230 GType ancestor_type1
, ancestor_type2
;
232 ancestor_type1
= adg_dress_get_ancestor_type(dress1
);
233 if (ancestor_type1
<= 0)
236 ancestor_type2
= adg_dress_get_ancestor_type(dress2
);
237 if (ancestor_type2
<= 0)
240 return ancestor_type1
== ancestor_type2
;
245 * @dress: a pointer to an #AdgDress
246 * @src: the source dress
248 * Copies @src in @dress. This operation can be succesful only if
249 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
250 * to @src, that is adg_dress_are_related() returns %TRUE.
252 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
257 adg_dress_set(AdgDress
*dress
, AdgDress src
)
262 if (*dress
!= ADG_DRESS_UNDEFINED
&& !adg_dress_are_related(*dress
, src
))
270 * adg_dress_get_name:
271 * @dress: an #AdgDress
273 * Gets the name associated to @dress. No warnings are raised if
274 * @dress is not found.
276 * Returns: the requested name or %NULL if not found
281 adg_dress_get_name(AdgDress dress
)
283 if (!_adg_dress_is_valid(dress
))
286 return g_quark_to_string(_adg_array_lookup(dress
)->quark
);
290 * adg_dress_get_ancestor_type:
291 * @dress: an #AdgDress
293 * Gets the base type that should be present in every #AdgStyle
294 * acceptable by @dress. No warnings are raised if @dress
297 * Returns: the ancestor type or %0 on errors
302 adg_dress_get_ancestor_type(AdgDress dress
)
304 AdgDressPrivate
*data
;
306 if (!_adg_dress_is_valid(dress
))
309 data
= _adg_array_lookup(dress
);
311 return data
->ancestor_type
;
315 * adg_dress_set_fallback:
316 * @dress: an #AdgDress
317 * @fallback: the new fallback style
319 * Associates a new @fallback style to @dress. If the dress does
320 * not exist (it was not previously created by adg_dress_new()),
321 * a warning message is raised and the function fails.
323 * @fallback is checked for compatibily with @dress. Any dress holds
324 * an ancestor type: if this type is not found in the @fallback
325 * hierarchy, a warning message is raised and the function fails.
327 * After a succesfull call, the reference to the previous fallback
328 * (if any) is dropped while a new reference to @fallback is added.
333 adg_dress_set_fallback(AdgDress dress
, AdgStyle
*fallback
)
335 AdgDressPrivate
*data
;
337 if (!_adg_dress_is_valid_with_log(dress
))
340 data
= _adg_array_lookup(dress
);
342 if (data
->fallback
== fallback
)
345 /* Check if the new fallback style is compatible with this dress */
346 if (fallback
!= NULL
&& !adg_dress_style_is_compatible(dress
, fallback
)) {
347 g_warning(_("%s: the fallback style of `%d' (%s) must be a `%s' derived type, but a `%s' has been provided"),
348 G_STRLOC
, dress
, _adg_dress_name(dress
),
349 g_type_name(data
->ancestor_type
),
350 g_type_name(G_TYPE_FROM_INSTANCE(fallback
)));
354 if (data
->fallback
!= NULL
)
355 g_object_unref(data
->fallback
);
357 data
->fallback
= fallback
;
359 if (data
->fallback
!= NULL
)
360 g_object_ref(data
->fallback
);
364 * adg_dress_get_fallback:
365 * @dress: an #AdgDress
367 * Gets the fallback style associated to @dress. No warnings
368 * are raised if the dress is not found. The returned style
369 * is owned by dress and should not be freed or modified.
371 * Returns: (transfer none): the requested #AdgStyle derived instance
372 * or %NULL if not set.
377 adg_dress_get_fallback(AdgDress dress
)
379 AdgDressPrivate
*data
;
381 if (!_adg_dress_is_valid(dress
))
384 data
= _adg_array_lookup(dress
);
386 return data
->fallback
;
390 * adg_dress_style_is_compatible:
391 * @dress: an #AdgDress
392 * @style: the #AdgStyle to check
394 * Checks whether @style is compatible with @dress, that is if
395 * @style has the ancestor style type (as returned by
396 * adg_dress_get_ancestor_type()) in its hierarchy.
398 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise
403 adg_dress_style_is_compatible(AdgDress dress
, AdgStyle
*style
)
405 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
407 g_return_val_if_fail(ancestor_type
> 0, FALSE
);
408 g_return_val_if_fail(ADG_IS_STYLE(style
), FALSE
);
410 return G_TYPE_CHECK_INSTANCE_TYPE(style
, ancestor_type
);
415 _adg_quark_to_dress(GQuark quark
)
418 AdgDressPrivate
*data
;
420 for (dress
= 0; dress
< _adg_array_len(); ++dress
) {
421 data
= _adg_array_lookup(dress
);
423 if (data
->quark
== quark
)
427 return ADG_DRESS_UNDEFINED
;
431 _adg_dress_to_string(const GValue
*src
, GValue
*dst
)
433 g_value_set_string(dst
, adg_dress_get_name(g_value_get_int(src
)));
437 _adg_string_to_dress(const GValue
*src
, GValue
*dst
)
439 g_value_set_int(dst
, adg_dress_from_name(g_value_get_string(src
)));
443 _adg_param_spec_dress_get_type(void)
445 static GType type
= 0;
447 if (G_UNLIKELY(type
== 0)) {
448 const GTypeInfo info
= {
449 sizeof(GParamSpecClass
),
452 (GClassInitFunc
) _adg_param_class_init
,
455 sizeof(AdgParamSpecDress
),
459 type
= g_type_register_static(G_TYPE_PARAM_INT
, "AdgParamSpecDress",
467 _adg_param_class_init(GParamSpecClass
*klass
)
469 klass
->value_type
= ADG_TYPE_DRESS
;
470 klass
->value_validate
= _adg_value_validate
;
474 _adg_dress_name(AdgDress dress
)
476 const gchar
*name
= adg_dress_get_name(dress
);
479 name
= _("(UNDEFINED)");
486 _adg_dress_is_valid(AdgDress dress
)
488 return dress
> 0 && dress
< _adg_array_len();
492 _adg_dress_is_valid_with_log(AdgDress dress
)
494 if (!_adg_dress_is_valid(dress
)) {
495 g_warning(_("%s: the dress `%d' is undefined"), G_STRLOC
, dress
);
503 _adg_value_validate(GParamSpec
*spec
, GValue
*value
)
505 AdgParamSpecDress
*fspec
;
507 AdgDress wanted_dress
;
509 fspec
= (AdgParamSpecDress
*) spec
;
510 dress
= &value
->data
[0].v_int
;
511 wanted_dress
= *dress
;
513 /* Fallback to the source dress, returned in case of errors */
514 *dress
= fspec
->source_dress
;
516 /* This method will fail (that is, it leaves *dress untouched)
517 * if the current *dress value (source_dress) and wanted_dress
519 adg_dress_set(dress
, wanted_dress
);
521 return *dress
!= wanted_dress
;
526 * adg_param_spec_dress:
527 * @name: canonical name
528 * @nick: nickname of the param
529 * @blurb: brief desciption
530 * @dress: the #AdgDress dress
531 * @flags: a combination of #GParamFlags
533 * Creates a param spec to hold a dress value.
535 * Returns: (transfer none): the newly allocated #GParamSpec.
540 adg_param_spec_dress(const gchar
*name
, const gchar
*nick
, const gchar
*blurb
,
541 AdgDress dress
, GParamFlags flags
)
543 AdgParamSpecDress
*fspec
;
545 if (!_adg_dress_is_valid_with_log(dress
))
548 fspec
= g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS
,
549 name
, nick
, blurb
, flags
);
550 fspec
->source_dress
= dress
;
552 return (GParamSpec
*) fspec
;
557 _adg_array_singleton(void)
559 static GArray
*array
= NULL
;
562 const AdgDressPrivate data
= { 0, };
564 array
= g_array_new(FALSE
, FALSE
, sizeof(AdgDressPrivate
));
566 /* Reserve the first item for the undefined dress */
567 g_array_append_val(array
, data
);
574 _adg_array_append(AdgDressPrivate
*data
)
576 return g_array_append_val(_adg_array_singleton(), *data
)->len
;
579 static AdgDressPrivate
*
580 _adg_array_lookup(guint n
)
582 return &g_array_index(_adg_array_singleton(), AdgDressPrivate
, n
);
588 return _adg_array_singleton()->len
;