[AdgLDim] Avoid arrange() on the same data
[adg.git] / adg / adg-dress.c
blob346f79e35c9af1cbd3b130bde660b157186617be
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 * @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.
32 **/
34 /**
35 * AdgDress:
37 * An index representing a virtual #AdgStyle.
38 **/
40 /**
41 * ADG_TYPE_DRESS:
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).
45 **/
47 /**
48 * ADG_VALUE_HOLDS_DRESS:
49 * @value: a #GValue
51 * Checks whether a #GValue is actually holding an #AdgDress value or not.
53 * Returns: %TRUE is @value is holding an #AdgDress, %FALSE otherwise
54 **/
57 #include "adg-dress.h"
58 #include "adg-dress-private.h"
59 #include "adg-color-style.h"
60 #include "adg-line-style.h"
61 #include "adg-font-style.h"
62 #include "adg-dim-style.h"
63 #include "adg-arrow.h"
66 static AdgDress quark_to_dress (GQuark quark);
67 static void dress_to_string (const GValue *src,
68 GValue *dst);
69 static void string_to_dress (const GValue *src,
70 GValue *dst);
71 static void param_class_init (GParamSpecClass *klass);
72 static gboolean value_validate (GParamSpec *spec,
73 GValue *value);
75 static guint array_append (AdgDressPrivate *data);
76 static AdgDressPrivate *
77 array_lookup (guint n);
78 static guint array_len (void);
81 GType
82 adg_dress_get_type(void)
84 static GType type = 0;
86 if (G_UNLIKELY(type == 0)) {
87 const GTypeInfo info = { 0, };
89 type = g_type_register_static(G_TYPE_INT, "AdgDress", &info, 0);
91 g_value_register_transform_func(type, G_TYPE_STRING, dress_to_string);
92 g_value_register_transform_func(G_TYPE_STRING, type, string_to_dress);
95 return type;
98 /**
99 * adg_dress_new:
100 * @name: the dress name
101 * @style: the style to associate
103 * Creates a new dress. It is a convenient wrapper of adg_dress_new_full()
104 * that uses as ancestor the G_TYPE_FROM_INSTANCE() of @style.
106 * After a succesfull call, a new reference is added to @style.
108 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
110 AdgDress
111 adg_dress_new(const gchar *name, AdgStyle *style)
113 return adg_dress_new_full(name, style, G_TYPE_FROM_INSTANCE(style));
117 * adg_dress_new_full:
118 * @name: the dress name
119 * @style: the style to associate
120 * @ancestor_type: the common ancestor type
122 * Creates a new dress, explicitely setting the ancestor type.
123 * @ancestor_type must be present in the @style hierarchy: check
124 * out the adg_dress_set_style() documentation to know what the
125 * ancestor type is used for.
127 * If a dress with the same name exists, a warning is raised and
128 * #ADG_DRESS_UNDEFINED is returned without further actions.
130 * After a succesfull call, a new reference is added to @style.
132 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
134 AdgDress
135 adg_dress_new_full(const gchar *name, AdgStyle *style, GType ancestor_type)
137 GQuark quark;
138 AdgDress dress;
139 AdgDressPrivate data;
141 g_return_val_if_fail(name != NULL, 0);
142 g_return_val_if_fail(style != NULL, 0);
143 g_return_val_if_fail(G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type), 0);
145 quark = g_quark_from_string(name);
146 dress = quark_to_dress(quark);
148 if (dress > 0) {
149 g_warning("%s: `%s' name yet used by the `%d' dress",
150 G_STRLOC, name, dress);
151 return ADG_DRESS_UNDEFINED;
154 data.quark = quark;
155 data.style = style;
156 data.ancestor_type = ancestor_type;
158 g_object_ref(style);
160 return array_append(&data) - 1;
164 * adg_dress_are_related:
165 * @dress1: an #AdgDress
166 * @dress2: another #AdgDress
168 * Checks whether @dress1 and @dress2 are related, that is
169 * if they have the same ancestor type as returned by
170 * adg_dress_get_ancestor_type().
172 * Returns: %TRUE if the dresses are related, %FALSE otherwise
174 gboolean
175 adg_dress_are_related(AdgDress dress1, AdgDress dress2)
177 GType ancestor_type1, ancestor_type2;
179 ancestor_type1 = adg_dress_get_ancestor_type(dress1);
180 if (ancestor_type1 <= 0)
181 return FALSE;
183 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
184 if (ancestor_type2 <= 0)
185 return FALSE;
187 return ancestor_type1 == ancestor_type2;
191 * adg_dress_set:
192 * @dress: a pointer to an #AdgDress
193 * @src: the source dress
195 * Copies @src in @dress. This operation can be succesful only if
196 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
197 * to @src, that is adg_dress_are_related() returns %TRUE.
199 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
201 gboolean
202 adg_dress_set(AdgDress *dress, AdgDress src)
204 if (*dress == src)
205 return FALSE;
207 if (*dress != ADG_DRESS_UNDEFINED &&
208 !adg_dress_are_related(*dress, src)) {
209 const gchar *dress_name;
210 const gchar *src_name;
212 dress_name = adg_dress_name(*dress);
213 if (dress_name == NULL)
214 dress_name = "UNDEFINED";
216 src_name = adg_dress_name(src);
217 if (src_name == NULL)
218 src_name = "UNDEFINED";
220 g_warning("%s: `%d' (%s) and `%d' (%s) dresses are not related",
221 G_STRLOC, *dress, dress_name, src, src_name);
223 return FALSE;
226 *dress = src;
228 return TRUE;
232 * adg_dress_name:
233 * @dress: an #AdgDress
235 * Gets the name associated to @dress. No warnings are raised if
236 * @dress is not found.
238 * Returns: the requested name or %NULL if not found
240 const gchar *
241 adg_dress_name(AdgDress dress)
243 if (dress <= 0 || dress >= array_len())
244 return NULL;
246 return g_quark_to_string(array_lookup(dress)->quark);
250 * adg_dress_from_name:
251 * @name: the name of a dress
253 * Gets the dress bound to a @name string. No warnings are raised
254 * if the dress is not found.
256 * Returns: the #AdgDress code or #ADG_DRESS_UNDEFINED if not found
258 AdgDress
259 adg_dress_from_name(const gchar *name)
261 return quark_to_dress(g_quark_try_string(name));
265 * adg_dress_get_ancestor_type:
266 * @dress: an #AdgDress
268 * Gets the base type that should be present in every #AdgStyle
269 * acceptable by @dress.
271 * Returns: the ancestor type
273 GType
274 adg_dress_get_ancestor_type(AdgDress dress)
276 AdgDressPrivate *data;
278 if (dress <= 0 || dress >= array_len())
279 return 0;
281 data = array_lookup(dress);
283 return data->ancestor_type;
287 * adg_dress_get_style:
288 * @dress: an #AdgDress
290 * Gets the style associated to @dress. No warnings are raised
291 * if the dress is not found.
293 * Returns: the requested #AdgStyle derived instance or %NULL if not found
295 AdgStyle *
296 adg_dress_get_style(AdgDress dress)
298 AdgDressPrivate *data;
300 if (dress <= 0 || dress >= array_len())
301 return NULL;
303 data = array_lookup(dress);
305 return data->style;
309 * adg_dress_set_style:
310 * @dress: an #AdgDress
311 * @style: the new style
313 * Associates a new @style to @dress. If the dress does not exist
314 * (it was not previously created by adg_dress_new()), a warning
315 * message is raised and the function fails.
317 * @style is checked for compatibily with @dress. Any dress holds
318 * an ancestor type: if this type is not found in the @style
319 * hierarchy, a warning message is raised the function fails.
321 * After a succesfull call, the reference to the old style (if any)
322 * is dropped while a new reference to @style is added.
324 void
325 adg_dress_set_style(AdgDress dress, AdgStyle *style)
327 AdgDressPrivate *data;
329 if (dress <= 0 || dress >= array_len()) {
330 g_warning("%s: `%d' dress undefined", G_STRLOC, dress);
331 return;
334 data = array_lookup(dress);
336 if (data->style == style)
337 return;
339 /* Check if the new style is compatible with this dress */
340 if (!G_TYPE_CHECK_INSTANCE_TYPE(style, data->ancestor_type)) {
341 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
342 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
343 g_type_name(data->ancestor_type), adg_dress_name(dress));
344 return;
347 if (data->style != NULL)
348 g_object_unref(data->style);
350 data->style = style;
352 if (data->style != NULL)
353 g_object_ref(data->style);
357 * adg_dress_accept_style:
358 * @dress: an #AdgDress
359 * @style: the #AdgStyle to check
361 * Checks whether @style could be associated to @dress using
362 * adg_dress_set_style().
364 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise
366 gboolean
367 adg_dress_accept_style(AdgDress dress, AdgStyle *style)
369 GType ancestor_type = adg_dress_get_ancestor_type(dress);
371 g_return_val_if_fail(ancestor_type > 0, FALSE);
372 g_return_val_if_fail(ADG_IS_STYLE(style), FALSE);
374 return G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type);
378 static AdgDress
379 quark_to_dress(GQuark quark)
381 AdgDress dress;
382 AdgDressPrivate *data;
384 for (dress = 0; dress < array_len(); ++dress) {
385 data = array_lookup(dress);
387 if (data->quark == quark)
388 return dress;
391 return ADG_DRESS_UNDEFINED;
394 static void
395 dress_to_string(const GValue *src, GValue *dst)
397 g_value_set_string(dst, adg_dress_name(g_value_get_int(src)));
400 static void
401 string_to_dress(const GValue *src, GValue *dst)
403 g_value_set_int(dst, adg_dress_from_name(g_value_get_string(src)));
407 typedef struct _AdgParamSpecDress AdgParamSpecDress;
409 struct _AdgParamSpecDress {
410 GParamSpecInt parent;
411 AdgDress source_dress;
415 GType
416 _adg_param_spec_dress_get_type(void)
418 static GType type = 0;
420 if (G_UNLIKELY(type == 0)) {
421 const GTypeInfo info = {
422 sizeof(GParamSpecClass),
423 NULL,
424 NULL,
425 (GClassInitFunc) param_class_init,
426 NULL,
427 NULL,
428 sizeof(AdgParamSpecDress),
432 type = g_type_register_static(G_TYPE_PARAM_INT,
433 "AdgParamSpecDress", &info, 0);
436 return type;
439 static void
440 param_class_init(GParamSpecClass *klass)
442 klass->value_type = ADG_TYPE_DRESS;
443 klass->value_validate = value_validate;
446 static gboolean
447 value_validate(GParamSpec *spec, GValue *value)
449 AdgParamSpecDress *fspec;
450 AdgDress dress, new_dress;
452 fspec = (AdgParamSpecDress *) spec;
453 dress = value->data[0].v_int;
454 new_dress = ADG_DRESS_UNDEFINED;
456 adg_dress_set(&new_dress, dress);
457 value->data[0].v_int = new_dress;
459 return dress != new_dress;
464 * adg_param_spec_dress:
465 * @name: canonical name
466 * @nick: nickname of the param
467 * @blurb: brief desciption
468 * @dress: the #AdgDress dress
469 * @flags: a combination of #GParamFlags
471 * Creates a param spec to hold a dress value.
473 * Returns: the newly allocated #GParamSpec
475 GParamSpec *
476 adg_param_spec_dress(const gchar *name, const gchar *nick, const gchar *blurb,
477 AdgDress dress, GParamFlags flags)
479 AdgParamSpecDress *fspec;
481 if (dress <= 0 || dress >= array_len()) {
482 g_warning("%s: `%d' dress undefined", G_STRLOC, dress);
483 return NULL;
486 fspec = g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS,
487 name, nick, blurb, flags);
488 fspec->source_dress = dress;
490 return (GParamSpec *) fspec;
495 * ADG_DRESS_UNDEFINED:
497 * A value reperesenting an undefined #AdgDress.
501 * ADG_DRESS_COLOR_REGULAR:
503 * The default builtin #AdgDress color. This dress will be resolved
504 * to an #AdgColorStyle instance.
506 AdgDress
507 _adg_dress_color_regular(void)
509 static AdgDress dress = 0;
511 if (G_UNLIKELY(dress == 0)) {
512 AdgStyle *style = g_object_new(ADG_TYPE_COLOR_STYLE, NULL);
514 dress = adg_dress_new("color-regular", style);
515 g_object_unref(style);
518 return dress;
522 * ADG_DRESS_COLOR_DIMENSION:
524 * The builtin #AdgDress color used by dimensioning items. This dress
525 * will be resolved to an #AdgColorStyle instance.
527 AdgDress
528 _adg_dress_color_dimension(void)
530 static AdgDress dress = 0;
532 if (G_UNLIKELY(dress == 0)) {
533 AdgStyle *style = g_object_new(ADG_TYPE_COLOR_STYLE,
534 "red", 0.75, NULL);
536 dress = adg_dress_new("color-dimension", style);
537 g_object_unref(style);
540 return dress;
544 * ADG_DRESS_LINE_REGULAR:
546 * The default builtin #AdgDress line. This dress will be resolved
547 * to an #AdgLineStyle instance.
549 AdgDress
550 _adg_dress_line_regular(void)
552 static AdgDress dress = 0;
554 if (G_UNLIKELY(dress == 0)) {
555 AdgStyle *style = g_object_new(ADG_TYPE_LINE_STYLE,
556 "width", 2., NULL);
558 dress = adg_dress_new("line-regular", style);
559 g_object_unref(style);
562 return dress;
566 * ADG_DRESS_LINE_DIMENSION:
568 * The builtin #AdgDress line type used by base and extension lines in
569 * dimensioning. This dress will be resolved to an #AdgLineStyle instance.
571 AdgDress
572 _adg_dress_line_dimension(void)
574 static AdgDress dress = 0;
576 if (G_UNLIKELY(dress == 0)) {
577 AdgStyle *style = g_object_new(ADG_TYPE_LINE_STYLE,
578 "antialias", CAIRO_ANTIALIAS_NONE,
579 "width", 1., NULL);
581 dress = adg_dress_new("line-dimension", style);
582 g_object_unref(style);
585 return dress;
589 * ADG_DRESS_TEXT_REGULAR:
591 * The default builtin #AdgDress font. This dress will be resolved
592 * to an #AdgFontStyle instance.
594 AdgDress
595 _adg_dress_text_regular(void)
597 static AdgDress dress = 0;
599 if (G_UNLIKELY(dress == 0)) {
600 AdgStyle *style = g_object_new(ADG_TYPE_FONT_STYLE,
601 "family", "Serif",
602 "size", 14., NULL);
604 dress = adg_dress_new("text-regular", style);
605 g_object_unref(style);
608 return dress;
612 * ADG_DRESS_TEXT_VALUE:
614 * The builtin #AdgDress font used to render the nominal value of a
615 * dimension. This dress will be resolved to an #AdgFontStyle instance.
617 AdgDress
618 _adg_dress_text_value(void)
620 static AdgDress dress = 0;
622 if (G_UNLIKELY(dress == 0)) {
623 AdgStyle *style = g_object_new(ADG_TYPE_FONT_STYLE,
624 "family", "Sans",
625 "weight", CAIRO_FONT_WEIGHT_BOLD,
626 "size", 12., NULL);
628 dress = adg_dress_new("text-value", style);
629 g_object_unref(style);
632 return dress;
636 * ADG_DRESS_TEXT_LIMIT:
638 * The builtin #AdgDress font used to render the limits of either
639 * the min and max values of a dimension. This dress will be
640 * resolved to an #AdgFontStyle instance.
642 AdgDress
643 _adg_dress_text_limit(void)
645 static AdgDress dress = 0;
647 if (G_UNLIKELY(dress == 0)) {
648 AdgStyle *style = g_object_new(ADG_TYPE_FONT_STYLE,
649 "family", "Sans",
650 "size", 8., NULL);
652 dress = adg_dress_new("text-limit", style);
653 g_object_unref(style);
656 return dress;
660 * ADG_DRESS_DIMENSION_REGULAR:
662 * The default builtin #AdgDress dimensioning style. This dress
663 * will be resolved to an #AdgDimStyle instance.
665 AdgDress
666 _adg_dress_dimension_regular(void)
668 static AdgDress dress = 0;
670 if (G_UNLIKELY(dress == 0)) {
671 AdgMarker *arrow = g_object_new(ADG_TYPE_ARROW, NULL);
672 AdgStyle *style = g_object_new(ADG_TYPE_DIM_STYLE, NULL);
674 adg_dim_style_use_marker1((AdgDimStyle *) style, arrow);
675 adg_marker_set_pos(arrow, 1);
676 adg_dim_style_use_marker2((AdgDimStyle *) style, arrow);
678 dress = adg_dress_new("dimension-regular", style);
679 g_object_unref(style);
680 g_object_unref(arrow);
683 return dress;
687 static GArray * array_singleton (void) G_GNUC_CONST;
689 static GArray *
690 array_singleton(void)
692 static GArray *array = NULL;
694 if (array == NULL) {
695 const AdgDressPrivate data = { 0, };
697 array = g_array_new(FALSE, FALSE, sizeof(AdgDressPrivate));
699 /* Reserve the first item for the undefined dress */
700 g_array_append_val(array, data);
703 return array;
706 static guint
707 array_append(AdgDressPrivate *data)
709 return g_array_append_val(array_singleton(), *data)->len;
712 static AdgDressPrivate *
713 array_lookup(guint n)
715 return &g_array_index(array_singleton(), AdgDressPrivate, n);
718 static guint
719 array_len(void)
721 return array_singleton()->len;