[AdgDress] Added markers to dim styles
[adg.git] / adg / adg-dress.c
blob9883b15d4ba85c510583cddcbe88f480c80482b1
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_VALUE_HOLDS_DRESS:
42 * @value: a #GValue
44 * Checks whether a #GValue is actually holding an #AdgDress value or not.
46 * Returns: %TRUE is @value is holding an #AdgDress, %FALSE otherwise
47 **/
50 #include "adg-dress.h"
51 #include "adg-dress-private.h"
52 #include "adg-color-style.h"
53 #include "adg-line-style.h"
54 #include "adg-font-style.h"
55 #include "adg-dim-style.h"
56 #include "adg-arrow.h"
59 static AdgDress quark_to_dress (GQuark quark);
60 static void dress_to_string (const GValue *src,
61 GValue *dst);
62 static void string_to_dress (const GValue *src,
63 GValue *dst);
64 static void param_class_init (GParamSpecClass *klass);
65 static gboolean value_validate (GParamSpec *spec,
66 GValue *value);
68 static guint array_append (AdgDressPrivate *data);
69 static AdgDressPrivate *
70 array_lookup (guint n);
71 static guint array_len (void);
74 GType
75 adg_dress_get_type(void)
77 static GType type = 0;
79 if (G_UNLIKELY(type == 0)) {
80 const GTypeInfo info = { 0, };
82 type = g_type_register_static(G_TYPE_INT, "AdgDress", &info, 0);
84 g_value_register_transform_func(type, G_TYPE_STRING, dress_to_string);
85 g_value_register_transform_func(G_TYPE_STRING, type, string_to_dress);
88 return type;
91 /**
92 * adg_dress_new:
93 * @name: the dress name
94 * @style: the style to associate
96 * Creates a new dress. It is a convenient wrapper of adg_dress_new_full()
97 * that uses as ancestor the G_TYPE_FROM_INSTANCE() of @style.
99 * After a succesfull call, a new reference is added to @style.
101 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
103 AdgDress
104 adg_dress_new(const gchar *name, AdgStyle *style)
106 return adg_dress_new_full(name, style, G_TYPE_FROM_INSTANCE(style));
110 * adg_dress_new_full:
111 * @name: the dress name
112 * @style: the style to associate
113 * @ancestor_type: the common ancestor type
115 * Creates a new dress, explicitely setting the ancestor type.
116 * @ancestor_type must be present in the @style hierarchy: check
117 * out the adg_dress_set_style() documentation to know what the
118 * ancestor type is used for.
120 * If a dress with the same name exists, a warning is raised and
121 * #ADG_DRESS_UNDEFINED is returned without further actions.
123 * After a succesfull call, a new reference is added to @style.
125 * Returns: the new #AdgDress value or #ADG_DRESS_UNDEFINED on errors
127 AdgDress
128 adg_dress_new_full(const gchar *name, AdgStyle *style, GType ancestor_type)
130 GQuark quark;
131 AdgDress dress;
132 AdgDressPrivate data;
134 g_return_val_if_fail(name != NULL, 0);
135 g_return_val_if_fail(style != NULL, 0);
136 g_return_val_if_fail(G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type), 0);
138 quark = g_quark_from_string(name);
139 dress = quark_to_dress(quark);
141 if (dress > 0) {
142 g_warning("%s: `%s' name yet used by the `%d' dress",
143 G_STRLOC, name, dress);
144 return ADG_DRESS_UNDEFINED;
147 data.quark = quark;
148 data.style = style;
149 data.ancestor_type = ancestor_type;
151 g_object_ref(style);
153 return array_append(&data) - 1;
157 * adg_dress_are_related:
158 * @dress1: an #AdgDress
159 * @dress2: another #AdgDress
161 * Checks whether @dress1 and @dress2 are related, that is
162 * if they have the same ancestor type as returned by
163 * adg_dress_get_ancestor_type().
165 * Returns: %TRUE if the dresses are related, %FALSE otherwise
167 gboolean
168 adg_dress_are_related(AdgDress dress1, AdgDress dress2)
170 GType ancestor_type1, ancestor_type2;
172 ancestor_type1 = adg_dress_get_ancestor_type(dress1);
173 if (ancestor_type1 <= 0)
174 return FALSE;
176 ancestor_type2 = adg_dress_get_ancestor_type(dress2);
177 if (ancestor_type2 <= 0)
178 return FALSE;
180 return ancestor_type1 == ancestor_type2;
184 * adg_dress_set:
185 * @dress: a pointer to an #AdgDress
186 * @src: the source dress
188 * Copies @src in @dress. This operation can be succesful only if
189 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
190 * to @src, that is adg_dress_are_related() returns %TRUE.
192 * Returns: %TRUE on copy done, %FALSE on copy failed or not needed
194 gboolean
195 adg_dress_set(AdgDress *dress, AdgDress src)
197 if (*dress == src)
198 return FALSE;
200 if (*dress != ADG_DRESS_UNDEFINED &&
201 !adg_dress_are_related(*dress, src)) {
202 const gchar *dress_name;
203 const gchar *src_name;
205 dress_name = adg_dress_name(*dress);
206 if (dress_name == NULL)
207 dress_name = "UNDEFINED";
209 src_name = adg_dress_name(src);
210 if (src_name == NULL)
211 src_name = "UNDEFINED";
213 g_warning("%s: `%d' (%s) and `%d' (%s) dresses are not related",
214 G_STRLOC, *dress, dress_name, src, src_name);
216 return FALSE;
219 *dress = src;
221 return TRUE;
225 * adg_dress_name:
226 * @dress: an #AdgDress
228 * Gets the name associated to @dress. No warnings are raised if
229 * @dress is not found.
231 * Returns: the requested name or %NULL if not found
233 const gchar *
234 adg_dress_name(AdgDress dress)
236 if (dress <= 0 || dress >= array_len())
237 return NULL;
239 return g_quark_to_string(array_lookup(dress)->quark);
243 * adg_dress_from_name:
244 * @name: the name of a dress
246 * Gets the dress binded to a @name string. No warnings are raised
247 * if the dress is not found.
249 * Returns: the #AdgDress code or #ADG_DRESS_UNDEFINED if not found
251 AdgDress
252 adg_dress_from_name(const gchar *name)
254 return quark_to_dress(g_quark_try_string(name));
258 * adg_dress_get_ancestor_type:
259 * @dress: an #AdgDress
261 * Gets the base type that should be present in every #AdgStyle
262 * acceptable by @dress.
264 * Returns: the ancestor type
266 GType
267 adg_dress_get_ancestor_type(AdgDress dress)
269 AdgDressPrivate *data;
271 if (dress <= 0 || dress >= array_len())
272 return 0;
274 data = array_lookup(dress);
276 return data->ancestor_type;
280 * adg_dress_get_style:
281 * @dress: an #AdgDress
283 * Gets the style associated to @dress. No warnings are raised
284 * if the dress is not found.
286 * Returns: the requested #AdgStyle derived instance or %NULL if not found
288 AdgStyle *
289 adg_dress_get_style(AdgDress dress)
291 AdgDressPrivate *data;
293 if (dress <= 0 || dress >= array_len())
294 return NULL;
296 data = array_lookup(dress);
298 return data->style;
302 * adg_dress_set_style:
303 * @dress: an #AdgDress
304 * @style: the new style
306 * Associates a new @style to @dress. If the dress does not exist
307 * (it was not previously created by adg_dress_new()), a warning
308 * message is raised and the function fails.
310 * @style is checked for compatibily with @dress. Any dress holds
311 * an ancestor type: if this type is not found in the @style
312 * hierarchy, a warning message is raised the function fails.
314 * After a succesfull call, the reference to the old style (if any)
315 * is dropped while a new reference to @style is added.
317 void
318 adg_dress_set_style(AdgDress dress, AdgStyle *style)
320 AdgDressPrivate *data;
322 if (dress <= 0 || dress >= array_len()) {
323 g_warning("%s: `%d' dress undefined", G_STRLOC, dress);
324 return;
327 data = array_lookup(dress);
329 if (data->style == style)
330 return;
332 /* Check if the new style is compatible with this dress */
333 if (!G_TYPE_CHECK_INSTANCE_TYPE(style, data->ancestor_type)) {
334 g_warning("%s: `%s' is not compatible with `%s' for `%s' dress",
335 G_STRLOC, g_type_name(G_TYPE_FROM_INSTANCE(style)),
336 g_type_name(data->ancestor_type), adg_dress_name(dress));
337 return;
340 if (data->style != NULL)
341 g_object_unref(data->style);
343 data->style = style;
345 if (data->style != NULL)
346 g_object_ref(data->style);
350 * adg_dress_accept_style:
351 * @dress: an #AdgDress
352 * @style: the #AdgStyle to check
354 * Checks whether @style could be associated to @dress using
355 * adg_dress_set_style().
357 * Returns: %TRUE if @dress can accept @style, %FALSE otherwise
359 gboolean
360 adg_dress_accept_style(AdgDress dress, AdgStyle *style)
362 GType ancestor_type = adg_dress_get_ancestor_type(dress);
364 g_return_val_if_fail(ancestor_type > 0, FALSE);
365 g_return_val_if_fail(ADG_IS_STYLE(style), FALSE);
367 return G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type);
371 static AdgDress
372 quark_to_dress(GQuark quark)
374 AdgDress dress;
375 AdgDressPrivate *data;
377 for (dress = 0; dress < array_len(); ++dress) {
378 data = array_lookup(dress);
380 if (data->quark == quark)
381 return dress;
384 return ADG_DRESS_UNDEFINED;
387 static void
388 dress_to_string(const GValue *src, GValue *dst)
390 g_value_set_string(dst, adg_dress_name(g_value_get_int(src)));
393 static void
394 string_to_dress(const GValue *src, GValue *dst)
396 g_value_set_int(dst, adg_dress_from_name(g_value_get_string(src)));
400 typedef struct _AdgParamSpecDress AdgParamSpecDress;
402 struct _AdgParamSpecDress {
403 GParamSpecInt parent;
404 AdgDress source_dress;
408 GType
409 _adg_param_spec_dress_get_type(void)
411 static GType type = 0;
413 if (G_UNLIKELY(type == 0)) {
414 const GTypeInfo info = {
415 sizeof(GParamSpecClass),
416 NULL,
417 NULL,
418 (GClassInitFunc) param_class_init,
419 NULL,
420 NULL,
421 sizeof(AdgParamSpecDress),
425 type = g_type_register_static(G_TYPE_PARAM_INT,
426 "AdgParamSpecDress", &info, 0);
429 return type;
432 static void
433 param_class_init(GParamSpecClass *klass)
435 klass->value_type = ADG_TYPE_DRESS;
436 klass->value_validate = value_validate;
439 static gboolean
440 value_validate(GParamSpec *spec, GValue *value)
442 AdgParamSpecDress *fspec;
443 AdgDress dress, new_dress;
445 fspec = (AdgParamSpecDress *) spec;
446 dress = value->data[0].v_int;
447 new_dress = ADG_DRESS_UNDEFINED;
449 adg_dress_set(&new_dress, dress);
450 value->data[0].v_int = new_dress;
452 return dress != new_dress;
457 * adg_param_spec_dress:
458 * @name: canonical name
459 * @nick: nickname of the param
460 * @blurb: brief desciption
461 * @dress: the #AdgDress dress
462 * @flags: a combination of #GParamFlags
464 * Creates a param spec to hold a dress value.
466 * Returns: the newly allocated #GParamSpec
468 GParamSpec *
469 adg_param_spec_dress(const gchar *name, const gchar *nick, const gchar *blurb,
470 AdgDress dress, GParamFlags flags)
472 AdgParamSpecDress *fspec;
474 if (dress <= 0 || dress >= array_len()) {
475 g_warning("%s: `%d' dress undefined", G_STRLOC, dress);
476 return NULL;
479 fspec = g_param_spec_internal(ADG_TYPE_PARAM_SPEC_DRESS,
480 name, nick, blurb, flags);
481 fspec->source_dress = dress;
483 return (GParamSpec *) fspec;
488 * ADG_DRESS_UNDEFINED:
490 * A value reperesenting an undefined #AdgDress.
494 * ADG_DRESS_COLOR_REGULAR:
496 * The default builtin #AdgDress color. This dress will be resolved
497 * to an #AdgColorStyle instance.
499 AdgDress
500 _adg_dress_color_regular(void)
502 static AdgDress dress = 0;
504 if (G_UNLIKELY(dress == 0)) {
505 AdgStyle *style = g_object_new(ADG_TYPE_COLOR_STYLE, NULL);
507 dress = adg_dress_new("color-regular", style);
508 g_object_unref(style);
511 return dress;
515 * ADG_DRESS_COLOR_DIMENSION:
517 * The builtin #AdgDress color used by dimensioning items. This dress
518 * will be resolved to an #AdgColorStyle instance.
520 AdgDress
521 _adg_dress_color_dimension(void)
523 static AdgDress dress = 0;
525 if (G_UNLIKELY(dress == 0)) {
526 AdgStyle *style = g_object_new(ADG_TYPE_COLOR_STYLE,
527 "red", 0.75, NULL);
529 dress = adg_dress_new("color-dimension", style);
530 g_object_unref(style);
533 return dress;
537 * ADG_DRESS_LINE_REGULAR:
539 * The default builtin #AdgDress line. This dress will be resolved
540 * to an #AdgLineStyle instance.
542 AdgDress
543 _adg_dress_line_regular(void)
545 static AdgDress dress = 0;
547 if (G_UNLIKELY(dress == 0)) {
548 AdgStyle *style = g_object_new(ADG_TYPE_LINE_STYLE,
549 "width", 2., NULL);
551 dress = adg_dress_new("line-regular", style);
552 g_object_unref(style);
555 return dress;
559 * ADG_DRESS_LINE_DIMENSION:
561 * The builtin #AdgDress line type used by base and extension lines in
562 * dimensioning. This dress will be resolved to an #AdgLineStyle instance.
564 AdgDress
565 _adg_dress_line_dimension(void)
567 static AdgDress dress = 0;
569 if (G_UNLIKELY(dress == 0)) {
570 AdgStyle *style = g_object_new(ADG_TYPE_LINE_STYLE,
571 "color-dress", ADG_DRESS_COLOR_DIMENSION,
572 "antialias", CAIRO_ANTIALIAS_NONE,
573 "width", 1., NULL);
575 dress = adg_dress_new("line-dimension", style);
576 g_object_unref(style);
579 return dress;
583 * ADG_DRESS_TEXT_REGULAR:
585 * The default builtin #AdgDress font. This dress will be resolved
586 * to an #AdgFontStyle instance.
588 AdgDress
589 _adg_dress_text_regular(void)
591 static AdgDress dress = 0;
593 if (G_UNLIKELY(dress == 0)) {
594 AdgStyle *style = g_object_new(ADG_TYPE_FONT_STYLE,
595 "family", "Serif",
596 "size", 14., NULL);
598 dress = adg_dress_new("text-regular", style);
599 g_object_unref(style);
602 return dress;
606 * ADG_DRESS_TEXT_VALUE:
608 * The builtin #AdgDress font used to render the nominal value of a
609 * dimension. This dress will be resolved to an #AdgFontStyle instance.
611 AdgDress
612 _adg_dress_text_value(void)
614 static AdgDress dress = 0;
616 if (G_UNLIKELY(dress == 0)) {
617 AdgStyle *style = g_object_new(ADG_TYPE_FONT_STYLE,
618 "family", "Sans",
619 "weight", CAIRO_FONT_WEIGHT_BOLD,
620 "size", 12., NULL);
622 dress = adg_dress_new("text-value", style);
623 g_object_unref(style);
626 return dress;
630 * ADG_DRESS_TEXT_LIMIT:
632 * The builtin #AdgDress font used to render the tolerances or both
633 * the upper and lower values of a dimension. This dress will be
634 * resolved to an #AdgFontStyle instance.
636 AdgDress
637 _adg_dress_text_limit(void)
639 static AdgDress dress = 0;
641 if (G_UNLIKELY(dress == 0)) {
642 AdgStyle *style = g_object_new(ADG_TYPE_FONT_STYLE,
643 "family", "Sans",
644 "size", 8., NULL);
646 dress = adg_dress_new("text-limit", style);
647 g_object_unref(style);
650 return dress;
654 * ADG_DRESS_DIMENSION_REGULAR:
656 * The default builtin #AdgDress dimensioning style. This dress
657 * will be resolved to an #AdgDimStyle instance.
659 AdgDress
660 _adg_dress_dimension_regular(void)
662 static AdgDress dress = 0;
664 if (G_UNLIKELY(dress == 0)) {
665 AdgMarker *arrow = g_object_new(ADG_TYPE_ARROW, NULL);
666 AdgStyle *style = g_object_new(ADG_TYPE_DIM_STYLE, NULL);
668 adg_dim_style_use_marker1((AdgDimStyle *) style, arrow);
669 adg_marker_set_pos(arrow, 1);
670 adg_dim_style_use_marker2((AdgDimStyle *) style, arrow);
672 dress = adg_dress_new("dimension-regular", style);
673 g_object_unref(style);
674 g_object_unref(arrow);
677 return dress;
681 static GArray * array_singleton (void) G_GNUC_CONST;
683 static GArray *
684 array_singleton(void)
686 static GArray *array = NULL;
688 if (array == NULL) {
689 const AdgDressPrivate data = { 0, };
691 array = g_array_new(FALSE, FALSE, sizeof(AdgDressPrivate));
693 /* Reserve the first item for the undefined dress */
694 g_array_append_val(array, data);
697 return array;
700 static guint
701 array_append(AdgDressPrivate *data)
703 return g_array_append_val(array_singleton(), *data)->len;
706 static AdgDressPrivate *
707 array_lookup(guint n)
709 return &g_array_index(array_singleton(), AdgDressPrivate, n);
712 static guint
713 array_len(void)
715 return array_singleton()->len;