1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 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.
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.
38 * @ADG_DRESS_UNDEFINED: undefined dress, used for notifying invalid
40 * @ADG_DRESS_COLOR: default built-in color. This is a
41 * pass-through dress, that is it does not
42 * change the cairo context when it is
43 * applied. This dress will be resolved to an
44 * #AdgColorStyle instance.
45 * @ADG_DRESS_COLOR_BACKGROUND: default built-in color to be used as the
46 * #AdgCanvas background. This dress will be
47 * resolved to an #AdgColorStyle instance.
48 * @ADG_DRESS_COLOR_STROKE: default built-in color for #AdgStroke
49 * entities. This dress will be resolved to
50 * an #AdgColorStyle instance.
51 * @ADG_DRESS_COLOR_DIMENSION: built-in color used by default in
52 * #AdgDimStyle. This dress will be resolved
53 * to an #AdgColorStyle instance.
54 * @ADG_DRESS_COLOR_ANNOTATION: built-in color used for rendering
55 * helper entities such as #AdgToyText,
56 * #AdgTable and #AdgTitleBlock. This dress
57 * will be resolved to an #AdgColorStyle
59 * @ADG_DRESS_COLOR_FILL: built-in color used by default by
60 * #AdgFillStyle based styles. This dress
61 * will be resolved to an #AdgColorStyle
63 * @ADG_DRESS_COLOR_AXIS: default built-in color for stroking
64 * #ADG_DRESS_LINE_AXIS lines. This dress
65 * will be resolved to an #AdgColorStyle
67 * @ADG_DRESS_COLOR_HIDDEN: default built-in color for stroking
68 * #ADG_DRESS_LINE_HIDDEN lines. This dress
69 * will be resolved to an #AdgColorStyle
71 * @ADG_DRESS_LINE: default built-in line. This is a
72 * pass-through dress, that is it does not
73 * change the cairo context when it is
74 * applied. This dress will be resolved to
75 * an #AdgLineStyle instance.
76 * @ADG_DRESS_LINE_STROKE: built-in line type to be used by default
77 * for rendering #AdgStroke entities.
78 * This dress will be resolved to an
79 * #AdgLineStyle instance.
80 * @ADG_DRESS_LINE_DIMENSION: built-in line type used by default for
81 * rendering base and extension lines of
82 * dimensions. This dress will be resolved
83 * to an #AdgLineStyle instance.
84 * @ADG_DRESS_LINE_FILL: built-in line type used by #AdgFillStyle.
85 * This dress will be resolved to an
86 * #AdgLineStyle instance.
87 * @ADG_DRESS_LINE_GRID: built-in line type used for rendering
88 * the grid of #AdgTable entities, that is
89 * the frame of the cells. This dress will
90 * be resolved to an #AdgLineStyle instance.
91 * @ADG_DRESS_LINE_FRAME: built-in line type used for rendering the
92 * frame of #AdgTable entities, that is the
93 * frame around the whole table. This dress
94 * will be resolved to an #AdgLineStyle
96 * @ADG_DRESS_LINE_AXIS: built-in line type used for rendering axis
97 * and centerlines. This dress will be
98 * resolved to an #AdgLineStyle instance.
99 * @ADG_DRESS_LINE_HIDDEN: built-in line type used for rendering
100 * hidden lines and edges. This dress will be
101 * resolved to an #AdgLineStyle instance.
102 * @ADG_DRESS_FONT: default built-in font. This dress will be
103 * resolved to an #AdgFontStyle instance.
104 * @ADG_DRESS_FONT_TEXT: built-in font used by default for
105 * rendering common text such as #AdgToyText
106 * or the value of #AdgTable entities. This
107 * dress will be resolved to an #AdgFontStyle
109 * @ADG_DRESS_FONT_ANNOTATION: built-in font used for rendering auxiliary
110 * text, such as the titles on #AdgTable
111 * entities. This dress will be resolved to
112 * an #AdgFontStyle instance.
113 * @ADG_DRESS_FONT_QUOTE_TEXT: built-in font used for rendering regular
114 * text on dimension entities, such as the
115 * nominal value and the notes of a quote.
116 * This dress will be resolved to an
117 * #AdgFontStyle instance.
118 * @ADG_DRESS_FONT_QUOTE_ANNOTATION: built-in font used for rendering auxiliary
119 * text on dimension entities, such as the
120 * min and max limits of a quote. This dress
121 * will be resolved to an #AdgFontStyle
123 * @ADG_DRESS_DIMENSION: default built-in for dimensions. This
124 * dress will be resolved to an #AdgDimStyle
126 * @ADG_DRESS_DIMENSION_ANGULAR: default built-in for angular dimensions.
127 * This dress will be resolved to an
128 * #AdgDimStyle instance.
129 * @ADG_DRESS_FILL: default built-in for filling. This is a
130 * pass-through dress, that is it does not
131 * change the cairo context when it is
132 * applied. This dress will be resolved to an
133 * #AdgFillStyle derived instance.
134 * @ADG_DRESS_FILL_HATCH: built-in dress used by default by
135 * #AdgHatch instances. This dress will be
136 * resolved to an #AdgFillStyle derived
138 * @ADG_DRESS_TABLE: default built-in for tables. This dress
139 * will be resolved to an #AdgTableStyle
142 * An index representing a virtual #AdgStyle. The ADG comes equipped
143 * with some built-in dress.
151 * The type used to express a dress index. It is defined only for GObject
152 * internal and should not be used directly (at least, as far as I know).
158 * ADG_VALUE_HOLDS_DRESS:
161 * Checks whether a #GValue is actually holding an #AdgDress value or not.
163 * Returns: <constant>TRUE</constant> is @value is holding an #AdgDress, <constant>FALSE</constant> otherwise.
169 #include "adg-internal.h"
170 #include "adg-dash.h"
171 #include "adg-model.h"
172 #include "adg-trail.h"
173 #include "adg-marker.h"
174 #include "adg-arrow.h"
175 #include "adg-style.h"
176 #include "adg-color-style.h"
177 #include "adg-line-style.h"
178 #include "adg-text-internal.h"
179 #include "adg-dim-style.h"
180 #include "adg-fill-style.h"
181 #include "adg-ruled-fill.h"
182 #include "adg-table-style.h"
184 #include "adg-dress.h"
185 #include "adg-dress-private.h"
187 #define MM *2.83464566927
190 static GArray
* _adg_data_array (void);
191 static void _adg_data_register (AdgDress dress
,
193 GType ancestor_type
);
194 static void _adg_data_register_builtins (void);
195 static AdgDressPrivate
*_adg_data_lookup (AdgDress dress
);
199 * adg_dress_from_name:
200 * @name: the name of a dress
202 * Gets the dress bound to a @name string. No warnings are raised
203 * if the dress is not found.
205 * Returns: the #AdgDress value or #ADG_DRESS_UNDEFINED if not found.
210 adg_dress_from_name(const gchar
*name
)
212 GEnumClass
*dress_class
= g_type_class_ref(ADG_TYPE_DRESS
);
213 GEnumValue
*enum_value
= g_enum_get_value_by_name(dress_class
, name
);
214 g_type_class_unref(dress_class
);
215 return enum_value
!= NULL
? enum_value
->value
: ADG_DRESS_UNDEFINED
;
219 * adg_dress_are_related:
220 * @dress1: an #AdgDress
221 * @dress2: another #AdgDress
223 * Checks whether @dress1 and @dress2 are related, that is
224 * if they have the same ancestor type as returned by
225 * adg_dress_get_ancestor_type().
227 * Returns: <constant>TRUE</constant> if the dresses are related, <constant>FALSE</constant> otherwise.
232 adg_dress_are_related(AdgDress dress1
, AdgDress dress2
)
234 GType ancestor_type1
, ancestor_type2
;
236 ancestor_type1
= adg_dress_get_ancestor_type(dress1
);
237 if (ancestor_type1
<= 0)
240 ancestor_type2
= adg_dress_get_ancestor_type(dress2
);
241 if (ancestor_type2
<= 0)
244 return ancestor_type1
== ancestor_type2
;
249 * @dress: a pointer to an #AdgDress
250 * @src: the source dress
252 * Copies @src in @dress. This operation can be successful only if
253 * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related
254 * to @src, i.e. adg_dress_are_related() returns <constant>TRUE</constant>.
256 * Returns: <constant>TRUE</constant> on copy done, <constant>FALSE</constant> on copy failed or not needed.
261 adg_dress_set(AdgDress
*dress
, AdgDress src
)
266 if (*dress
!= ADG_DRESS_UNDEFINED
&& !adg_dress_are_related(*dress
, src
))
274 * adg_dress_get_name:
275 * @dress: an #AdgDress
277 * Gets the name associated to @dress. No warnings are raised if
278 * @dress is not found.
280 * Returns: the requested name or <constant>NULL</constant> if not found.
285 adg_dress_get_name(AdgDress dress
)
287 GEnumClass
*dress_class
= g_type_class_ref(ADG_TYPE_DRESS
);
288 GEnumValue
*enum_value
= g_enum_get_value(dress_class
, dress
);
289 g_type_class_unref(dress_class
);
290 return enum_value
!= NULL
? enum_value
->value_name
: NULL
;
294 * adg_dress_get_ancestor_type:
295 * @dress: an #AdgDress
297 * Gets the base type that should be present in every #AdgStyle
298 * acceptable by @dress. No warnings are raised if @dress
301 * Returns: the ancestor type or 0 on errors.
306 adg_dress_get_ancestor_type(AdgDress dress
)
308 AdgDressPrivate
*data
= _adg_data_lookup(dress
);
309 return data
!= NULL
? data
->ancestor_type
: 0;
313 * adg_dress_set_fallback:
314 * @dress: an #AdgDress
315 * @fallback: (transfer full): the new fallback style
317 * Associates a new @fallback style to @dress. If the dress is
318 * not valid, a warning message is raised and the function fails.
320 * @fallback is checked for compatibily with @dress. Any dress holds
321 * an ancestor type: if this type is not found in the @fallback
322 * hierarchy, a warning message is raised and the function fails.
324 * After a succesfull call, the reference to the previous fallback
325 * (if any) is dropped while a new reference to @fallback is added.
330 adg_dress_set_fallback(AdgDress dress
, AdgStyle
*fallback
)
332 AdgDressPrivate
*data
= _adg_data_lookup(dress
);
334 g_return_if_fail(data
!= NULL
);
336 if (data
->fallback
== fallback
)
339 /* Check if the new fallback style is compatible with this dress */
340 if (fallback
!= NULL
&& !adg_dress_style_is_compatible(dress
, fallback
)) {
341 g_warning(_("%s: the fallback style of '%s' dress (%d) must be a '%s' derived type, but a '%s' has been provided"),
342 G_STRLOC
, adg_dress_get_name(dress
), dress
,
343 g_type_name(data
->ancestor_type
),
344 g_type_name(G_TYPE_FROM_INSTANCE(fallback
)));
348 if (data
->fallback
!= NULL
)
349 g_object_unref(data
->fallback
);
351 data
->fallback
= fallback
;
353 if (data
->fallback
!= NULL
)
354 g_object_ref(data
->fallback
);
358 * adg_dress_get_fallback:
359 * @dress: an #AdgDress
361 * Gets the fallback style associated to @dress. No warnings
362 * are raised if the dress is not found. The returned style
363 * is owned by dress and should not be freed or modified.
365 * Returns: (transfer none): the requested #AdgStyle derived instance or <constant>NULL</constant> if not set.
370 adg_dress_get_fallback(AdgDress dress
)
372 AdgDressPrivate
*data
= _adg_data_lookup(dress
);
373 return data
!= NULL
? data
->fallback
: NULL
;
377 * adg_dress_style_is_compatible:
378 * @dress: an #AdgDress
379 * @style: (transfer none): the #AdgStyle to check
381 * Checks whether @style is compatible with @dress, that is if
382 * @style has the ancestor style type (as returned by
383 * adg_dress_get_ancestor_type()) in its hierarchy.
385 * Returns: <constant>TRUE</constant> if @dress can accept @style, <constant>FALSE</constant> otherwise.
390 adg_dress_style_is_compatible(AdgDress dress
, AdgStyle
*style
)
392 GType ancestor_type
= adg_dress_get_ancestor_type(dress
);
394 g_return_val_if_fail(ancestor_type
> 0, FALSE
);
395 g_return_val_if_fail(ADG_IS_STYLE(style
), FALSE
);
397 return G_TYPE_CHECK_INSTANCE_TYPE(style
, ancestor_type
);
402 _adg_data_array(void)
404 /* The following register keeps track of the metadata bound to every
405 * #AdgDress value, such as the fallback style and the ancestor type.
407 * The AdgDress value is cohincident with the index of its metadata
408 * inside this register, that is if %ADG_DRESS_COLOR_BACKGROUND is 2,
409 * array->data[2] will contain its metadata.
411 static GArray
*array
= NULL
;
413 if (G_UNLIKELY(array
== NULL
)) {
414 array
= g_array_new(FALSE
, FALSE
, sizeof(AdgDressPrivate
));
415 _adg_data_register_builtins();
422 _adg_data_register(AdgDress dress
, AdgStyle
*fallback
, GType ancestor_type
)
424 GArray
*array
= _adg_data_array();
425 AdgDressPrivate data
;
427 data
.fallback
= fallback
;
428 data
.ancestor_type
= ancestor_type
;
430 g_array_insert_vals(array
, dress
, g_memdup(&data
, sizeof(data
)), 1);
434 _adg_data_register_builtins(void)
437 AdgMarker
*arrow1
, *arrow2
;
439 _adg_data_register(ADG_DRESS_UNDEFINED
,
444 /* Predefined colors */
446 _adg_data_register(ADG_DRESS_COLOR
,
448 ADG_TYPE_COLOR_STYLE
);
450 _adg_data_register(ADG_DRESS_COLOR_BACKGROUND
,
451 g_object_new(ADG_TYPE_COLOR_STYLE
,
456 ADG_TYPE_COLOR_STYLE
);
458 _adg_data_register(ADG_DRESS_COLOR_STROKE
,
459 g_object_new(ADG_TYPE_COLOR_STYLE
,
461 ADG_TYPE_COLOR_STYLE
);
463 _adg_data_register(ADG_DRESS_COLOR_DIMENSION
,
464 g_object_new(ADG_TYPE_COLOR_STYLE
,
469 ADG_TYPE_COLOR_STYLE
);
471 _adg_data_register(ADG_DRESS_COLOR_ANNOTATION
,
472 g_object_new(ADG_TYPE_COLOR_STYLE
,
477 ADG_TYPE_COLOR_STYLE
);
479 _adg_data_register(ADG_DRESS_COLOR_FILL
,
480 g_object_new(ADG_TYPE_COLOR_STYLE
,
485 ADG_TYPE_COLOR_STYLE
);
487 _adg_data_register(ADG_DRESS_COLOR_AXIS
,
488 g_object_new(ADG_TYPE_COLOR_STYLE
,
493 ADG_TYPE_COLOR_STYLE
);
495 _adg_data_register(ADG_DRESS_COLOR_HIDDEN
,
496 g_object_new(ADG_TYPE_COLOR_STYLE
,
501 ADG_TYPE_COLOR_STYLE
);
504 /* Predefined lines */
506 _adg_data_register(ADG_DRESS_LINE
,
508 ADG_TYPE_LINE_STYLE
);
510 _adg_data_register(ADG_DRESS_LINE_STROKE
,
511 g_object_new(ADG_TYPE_LINE_STYLE
,
512 "color-dress", ADG_DRESS_COLOR_STROKE
,
515 ADG_TYPE_LINE_STYLE
);
517 _adg_data_register(ADG_DRESS_LINE_DIMENSION
,
518 g_object_new(ADG_TYPE_LINE_STYLE
,
521 ADG_TYPE_LINE_STYLE
);
523 _adg_data_register(ADG_DRESS_LINE_FILL
,
524 g_object_new(ADG_TYPE_LINE_STYLE
,
525 "color-dress", ADG_DRESS_COLOR_FILL
,
528 ADG_TYPE_LINE_STYLE
);
530 _adg_data_register(ADG_DRESS_LINE_GRID
,
531 g_object_new(ADG_TYPE_LINE_STYLE
,
532 "antialias", CAIRO_ANTIALIAS_NONE
,
535 ADG_TYPE_LINE_STYLE
);
537 _adg_data_register(ADG_DRESS_LINE_FRAME
,
538 g_object_new(ADG_TYPE_LINE_STYLE
,
539 "color-dress", ADG_DRESS_COLOR_ANNOTATION
,
540 "antialias", CAIRO_ANTIALIAS_NONE
,
543 ADG_TYPE_LINE_STYLE
);
546 dash
= adg_dash_new_with_dashes(4, 2 MM
, 2 MM
, 10 MM
, 2 MM
);
547 _adg_data_register(ADG_DRESS_LINE_AXIS
,
548 g_object_new(ADG_TYPE_LINE_STYLE
,
549 "color-dress", ADG_DRESS_COLOR_AXIS
,
553 ADG_TYPE_LINE_STYLE
);
554 adg_dash_destroy(dash
);
556 dash
= adg_dash_new_with_dashes(2, 6 MM
, 6 MM
);
557 _adg_data_register(ADG_DRESS_LINE_HIDDEN
,
558 g_object_new(ADG_TYPE_LINE_STYLE
,
559 "color-dress", ADG_DRESS_COLOR_HIDDEN
,
563 ADG_TYPE_LINE_STYLE
);
564 adg_dash_destroy(dash
);
567 /* Predefined fonts */
569 _adg_data_register(ADG_DRESS_FONT
,
570 g_object_new(ADG_TYPE_BEST_FONT_STYLE
,
574 ADG_TYPE_BEST_FONT_STYLE
);
576 _adg_data_register(ADG_DRESS_FONT_TEXT
,
577 g_object_new(ADG_TYPE_BEST_FONT_STYLE
,
578 "color-dress", ADG_DRESS_COLOR_ANNOTATION
,
580 "weight", CAIRO_FONT_WEIGHT_BOLD
,
583 ADG_TYPE_BEST_FONT_STYLE
);
585 _adg_data_register(ADG_DRESS_FONT_ANNOTATION
,
586 g_object_new(ADG_TYPE_BEST_FONT_STYLE
,
587 "color-dress", ADG_DRESS_COLOR_ANNOTATION
,
591 ADG_TYPE_BEST_FONT_STYLE
);
593 _adg_data_register(ADG_DRESS_FONT_QUOTE_TEXT
,
594 g_object_new(ADG_TYPE_BEST_FONT_STYLE
,
596 "weight", CAIRO_FONT_WEIGHT_BOLD
,
599 ADG_TYPE_BEST_FONT_STYLE
);
601 _adg_data_register(ADG_DRESS_FONT_QUOTE_ANNOTATION
,
602 g_object_new(ADG_TYPE_BEST_FONT_STYLE
,
606 ADG_TYPE_BEST_FONT_STYLE
);
609 /* Predefined dimension styles */
611 arrow1
= (AdgMarker
*) adg_arrow_new();
612 arrow2
= (AdgMarker
*) adg_arrow_new();
613 adg_marker_set_pos(arrow2
, 1);
614 _adg_data_register(ADG_DRESS_DIMENSION
,
615 g_object_new(ADG_TYPE_DIM_STYLE
,
620 g_object_unref(arrow1
);
621 g_object_unref(arrow2
);
623 /* Predefined angular dimension styles */
625 arrow1
= (AdgMarker
*) adg_arrow_new();
626 arrow2
= (AdgMarker
*) adg_arrow_new();
627 adg_marker_set_pos(arrow2
, 1);
628 _adg_data_register(ADG_DRESS_DIMENSION_ANGULAR
,
629 g_object_new(ADG_TYPE_DIM_STYLE
,
632 "number-arguments", "d",
633 "number-format", "%g°",
636 g_object_unref(arrow1
);
637 g_object_unref(arrow2
);
640 /* Predefined fill styles */
642 _adg_data_register(ADG_DRESS_FILL
,
644 ADG_TYPE_FILL_STYLE
);
646 _adg_data_register(ADG_DRESS_FILL_HATCH
,
647 g_object_new(ADG_TYPE_RULED_FILL
,
648 "line-dress", ADG_DRESS_LINE_FILL
,
650 ADG_TYPE_FILL_STYLE
);
653 /* Predefined table styles */
655 _adg_data_register(ADG_DRESS_TABLE
,
656 g_object_new(ADG_TYPE_TABLE_STYLE
, NULL
),
657 ADG_TYPE_TABLE_STYLE
);
660 static AdgDressPrivate
*
661 _adg_data_lookup(AdgDress dress
)
663 GArray
*array
= _adg_data_array();
665 if (dress
>= array
->len
)
668 return ((AdgDressPrivate
*) array
->data
) + dress
;