[AdgStroke] Using the line style dress color
[adg.git] / adg / adg-dress.c
blob957285466d33e0dfbe2e9d678c1824b797b4bbb0
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 * @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.
30 **/
32 /**
33 * AdgDress:
35 * An index representing a virtual #AdgStyle.
36 **/
38 /**
39 * AdgParamSpecDress:
41 * A #GParamSpec for #AdgDress values.
42 **/
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,
55 GValue *dst);
56 static void string_to_dress (const GValue *src,
57 GValue *dst);
58 static void param_class_init (GParamSpecClass *klass);
59 static gboolean value_validate (GParamSpec *spec,
60 GValue *value);
62 static guint array_append (AdgDressPrivate *data);
63 static AdgDressPrivate *
64 array_lookup (guint n);
65 static guint array_len (void);
68 GType
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);
82 return type;
85 /**
86 * adg_dress_new:
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
96 **/
97 AdgDress
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
121 AdgDress
122 adg_dress_new_full(const gchar *name, AdgStyle *style, GType ancestor_type)
124 GQuark quark;
125 AdgDress dress;
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);
135 if (dress > 0) {
136 g_warning("%s: `%s' name yet used by the `%d' dress",
137 G_STRLOC, name, dress);
138 return ADG_DRESS_UNDEFINED;
141 data.quark = quark;
142 data.style = style;
143 data.ancestor_type = ancestor_type;
145 g_object_ref(style);
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
161 gboolean
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)
168 return FALSE;
170 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
171 if (ancestor_type2 <= 0)
172 return FALSE;
174 return ancestor_type1 == ancestor_type2;
178 * adg_dress_set:
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
188 gboolean
189 adg_dress_set(AdgDress *dress, AdgDress src)
191 if (*dress == src)
192 return FALSE;
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);
210 return FALSE;
213 *dress = src;
215 return TRUE;
219 * adg_dress_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
227 const gchar *
228 adg_dress_name(AdgDress dress)
230 if (dress <= 0 || dress >= array_len())
231 return NULL;
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
245 AdgDress
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
260 GType
261 adg_dress_get_ancestor_type(AdgDress dress)
263 AdgDressPrivate *data;
265 if (dress <= 0 || dress >= array_len())
266 return 0;
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
282 AdgStyle *
283 adg_dress_get_style(AdgDress dress)
285 AdgDressPrivate *data;
287 if (dress <= 0 || dress >= array_len())
288 return NULL;
290 data = array_lookup(dress);
292 return data->style;
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.
311 void
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);
318 return;
321 data = array_lookup(dress);
323 if (data->style == style)
324 return;
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));
331 return;
334 if (data->style != NULL)
335 g_object_unref(data->style);
337 data->style = 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
353 gboolean
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);
365 static AdgDress
366 quark_to_dress(GQuark quark)
368 AdgDress dress;
369 AdgDressPrivate *data;
371 for (dress = 0; dress < array_len(); ++dress) {
372 data = array_lookup(dress);
374 if (data->quark == quark)
375 return dress;
378 return ADG_DRESS_UNDEFINED;
381 static void
382 dress_to_string(const GValue *src, GValue *dst)
384 g_value_set_string(dst, adg_dress_name(g_value_get_int(src)));
387 static void
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;
402 GType
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),
410 NULL,
411 NULL,
412 (GClassInitFunc) param_class_init,
413 NULL,
414 NULL,
415 sizeof(AdgParamSpecDress),
419 type = g_type_register_static(G_TYPE_PARAM_INT,
420 "AdgParamSpecDress", &info, 0);
423 return type;
426 static void
427 param_class_init(GParamSpecClass *klass)
429 klass->value_type = ADG_TYPE_DRESS;
430 klass->value_validate = value_validate;
433 static gboolean
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
462 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);
470 return NULL;
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;
481 AdgDress
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);
493 return dress;
496 AdgDress
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,
503 "red", 0.75, NULL);
505 dress = adg_dress_new("color-dimension", style);
506 g_object_unref(style);
509 return dress;
512 AdgDress
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,
519 "width", 2., NULL);
521 dress = adg_dress_new("line-regular", style);
522 g_object_unref(style);
525 return dress;
528 AdgDress
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,
536 "width", 1., NULL);
538 dress = adg_dress_new("line-dimension", style);
539 g_object_unref(style);
542 return dress;
545 AdgDress
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,
552 "family", "Serif",
553 "size", 14., NULL);
555 dress = adg_dress_new("text-regular", style);
556 g_object_unref(style);
559 return dress;
562 AdgDress
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,
569 "family", "Sans",
570 "weight", CAIRO_FONT_WEIGHT_BOLD,
571 "size", 12., NULL);
573 dress = adg_dress_new("text-value", style);
574 g_object_unref(style);
577 return dress;
580 AdgDress
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,
587 "family", "Sans",
588 "size", 8., NULL);
590 dress = adg_dress_new("text-limit", style);
591 g_object_unref(style);
594 return dress;
597 AdgDress
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);
609 return dress;
613 static GArray * array_singleton (void) G_GNUC_CONST;
615 static GArray *
616 array_singleton(void)
618 static GArray *array = NULL;
620 if (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);
629 return array;
632 static guint
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);
644 static guint
645 array_len(void)
647 return array_singleton()->len;