[CpmlPair] Removed @length from cpml_vector_from_angle()
[adg.git] / adg / adg-dim.c
blob32f2e7e6549e6e5fbb14f84d28b3458883d32f10
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-dim
23 * @short_description: Root abstract class for all dimension entities
25 * The #AdgDim class is the base stub of all the dimension entities.
26 **/
28 /**
29 * AdgDim:
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
33 **/
36 #include "adg-dim.h"
37 #include "adg-dim-private.h"
38 #include "adg-dim-style.h"
39 #include "adg-toy-text.h"
40 #include "adg-type-builtins.h"
41 #include "adg-intl.h"
43 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_dim_parent_class)
44 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_dim_parent_class)
47 enum {
48 PROP_0,
49 PROP_DRESS,
50 PROP_REF1,
51 PROP_REF2,
52 PROP_POS1,
53 PROP_POS2,
54 PROP_LEVEL,
55 PROP_OUTSIDE,
56 PROP_VALUE,
57 PROP_MIN,
58 PROP_MAX
62 static void dispose (GObject *object);
63 static void finalize (GObject *object);
64 static void get_property (GObject *object,
65 guint param_id,
66 GValue *value,
67 GParamSpec *pspec);
68 static void set_property (GObject *object,
69 guint param_id,
70 const GValue *value,
71 GParamSpec *pspec);
72 static void global_changed (AdgEntity *entity);
73 static void local_changed (AdgEntity *entity);
74 static void invalidate (AdgEntity *entity);
75 static void arrange (AdgEntity *entity);
76 static gchar * default_value (AdgDim *dim);
77 static gdouble quote_angle (gdouble angle);
78 static gboolean set_dress (AdgDim *dim,
79 AdgDress dress);
80 static gboolean set_value (AdgDim *dim,
81 const gchar *value);
82 static gboolean set_min (AdgDim *dim,
83 const gchar *min);
84 static gboolean set_max (AdgDim *dim,
85 const gchar *max);
88 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
91 static void
92 adg_dim_class_init(AdgDimClass *klass)
94 GObjectClass *gobject_class;
95 AdgEntityClass *entity_class;
96 GParamSpec *param;
98 gobject_class = (GObjectClass *) klass;
99 entity_class = (AdgEntityClass *) klass;
101 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
103 gobject_class->dispose = dispose;
104 gobject_class->finalize = finalize;
105 gobject_class->get_property = get_property;
106 gobject_class->set_property = set_property;
108 entity_class->global_changed = global_changed;
109 entity_class->local_changed = local_changed;
110 entity_class->invalidate = invalidate;
111 entity_class->arrange = arrange;
113 klass->quote_angle = quote_angle;
114 klass->default_value = default_value;
116 param = adg_param_spec_dress("dress",
117 P_("Dress Style"),
118 P_("The dress to use for rendering this dimension"),
119 ADG_DRESS_DIMENSION_REGULAR,
120 G_PARAM_READWRITE);
121 g_object_class_install_property(gobject_class, PROP_DRESS, param);
123 param = g_param_spec_boxed("ref1",
124 P_("Reference 1"),
125 P_("First reference point of the dimension"),
126 ADG_TYPE_PAIR,
127 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
128 g_object_class_install_property(gobject_class, PROP_REF1, param);
130 param = g_param_spec_boxed("ref2",
131 P_("Reference 2"),
132 P_("Second reference point of the dimension"),
133 ADG_TYPE_PAIR,
134 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
135 g_object_class_install_property(gobject_class, PROP_REF2, param);
137 param = g_param_spec_boxed("pos1",
138 P_("Position 1"),
139 P_("First position point: it will be computed with the level property to get the real dimension position"),
140 ADG_TYPE_PAIR, G_PARAM_READWRITE);
141 g_object_class_install_property(gobject_class, PROP_POS1, param);
143 param = g_param_spec_boxed("pos2",
144 P_("Position 2"),
145 P_("Second position point: it will be computed with the level property to get the real dimension position"),
146 ADG_TYPE_PAIR, G_PARAM_READWRITE);
147 g_object_class_install_property(gobject_class, PROP_POS2, param);
149 param = g_param_spec_double("level",
150 P_("Level"),
151 P_("The dimension level, that is the factor to multiply dim_style->baseline_spacing to get the offset (in device units) from pos1..pos2 where render the dimension baseline"),
152 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
153 G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
156 param = g_param_spec_enum("outside",
157 P_("Outside"),
158 P_("Whether the arrows must be inside the extension lines (ADG_THREE_STATE_OFF), must be extended outside the extension lines (ADG_THREE_STATE_ON) or should be automatically handled depending on the available space"),
159 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
160 G_PARAM_READWRITE);
161 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
163 param = g_param_spec_string("value",
164 P_("Basic Value"),
165 P_("The theoretically exact value for this quote: set to NULL to automatically get the default value"),
166 NULL, G_PARAM_READWRITE);
167 g_object_class_install_property(gobject_class, PROP_VALUE, param);
169 param = g_param_spec_string("value-min",
170 P_("Minimum Value or Low Tolerance"),
171 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
172 NULL, G_PARAM_READWRITE);
173 g_object_class_install_property(gobject_class, PROP_MIN, param);
175 param = g_param_spec_string("value-max",
176 P_("Maximum Value or High Tolerance"),
177 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
178 NULL, G_PARAM_READWRITE);
179 g_object_class_install_property(gobject_class, PROP_MAX, param);
182 static void
183 adg_dim_init(AdgDim *dim)
185 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
186 AdgDimPrivate);
188 data->dress = ADG_DRESS_DIMENSION_REGULAR;
189 data->ref1.x = data->ref1.y = 0;
190 data->ref2.x = data->ref2.y = 0;
191 data->pos1.x = data->pos1.y = 0;
192 data->pos2.x = data->pos2.y = 0;
193 data->level = 1;
194 data->outside = ADG_THREE_STATE_UNKNOWN;
195 data->value = NULL;
196 data->min = NULL;
197 data->max = NULL;
199 dim->data = data;
202 static void
203 dispose(GObject *object)
205 AdgDimPrivate *data = ((AdgDim *) object)->data;
207 if (data->quote.container != NULL) {
208 g_object_unref(data->quote.container);
209 data->quote.container = NULL;
212 if (PARENT_OBJECT_CLASS->dispose != NULL)
213 PARENT_OBJECT_CLASS->dispose(object);
216 static void
217 finalize(GObject *object)
219 AdgDimPrivate *data = ((AdgDim *) object)->data;
221 g_free(data->value);
222 g_free(data->min);
223 g_free(data->max);
225 if (PARENT_OBJECT_CLASS->finalize != NULL)
226 PARENT_OBJECT_CLASS->finalize(object);
229 static void
230 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
232 AdgDimPrivate *data = ((AdgDim *) object)->data;
234 switch (prop_id) {
235 case PROP_DRESS:
236 g_value_set_int(value, data->dress);
237 break;
238 case PROP_REF1:
239 g_value_set_boxed(value, &data->ref1);
240 break;
241 case PROP_REF2:
242 g_value_set_boxed(value, &data->ref2);
243 break;
244 case PROP_POS1:
245 g_value_set_boxed(value, &data->pos1);
246 break;
247 case PROP_POS2:
248 g_value_set_boxed(value, &data->pos1);
249 break;
250 case PROP_LEVEL:
251 g_value_set_double(value, data->level);
252 break;
253 case PROP_OUTSIDE:
254 g_value_set_enum(value, data->outside);
255 break;
256 case PROP_VALUE:
257 g_value_set_string(value, data->value);
258 break;
259 case PROP_MIN:
260 g_value_set_string(value, data->min);
261 break;
262 case PROP_MAX:
263 g_value_set_string(value, data->max);
264 break;
265 default:
266 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
267 break;
271 static void
272 set_property(GObject *object, guint prop_id,
273 const GValue *value, GParamSpec *pspec)
275 AdgDim *dim;
276 AdgDimPrivate *data;
278 dim = (AdgDim *) object;
279 data = dim->data;
281 switch (prop_id) {
282 case PROP_DRESS:
283 set_dress(dim, g_value_get_int(value));
284 break;
285 case PROP_REF1:
286 cpml_pair_copy(&data->ref1, (AdgPair *) g_value_get_boxed(value));
287 break;
288 case PROP_REF2:
289 cpml_pair_copy(&data->ref2, (AdgPair *) g_value_get_boxed(value));
290 break;
291 case PROP_POS1:
292 cpml_pair_copy(&data->pos1, (AdgPair *) g_value_get_boxed(value));
293 break;
294 case PROP_POS2:
295 cpml_pair_copy(&data->pos2, (AdgPair *) g_value_get_boxed(value));
296 break;
297 case PROP_LEVEL:
298 data->level = g_value_get_double(value);
299 break;
300 case PROP_OUTSIDE:
301 data->outside = g_value_get_enum(value);
302 break;
303 case PROP_VALUE:
304 set_value(dim, g_value_get_string(value));
305 break;
306 case PROP_MIN:
307 set_min(dim, g_value_get_string(value));
308 break;
309 case PROP_MAX:
310 set_max(dim, g_value_get_string(value));
311 break;
312 default:
313 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
314 break;
320 * adg_dim_get_dress:
321 * @dim: an #AdgDim
323 * Gets the dimension dress to be used in rendering @dim.
325 * Returns: the current dimension dress
327 AdgDress
328 adg_dim_get_dress(AdgDim *dim)
330 AdgDimPrivate *data;
332 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_DRESS_UNDEFINED);
334 data = dim->data;
336 return data->dress;
340 * adg_dim_set_dress:
341 * @dim: an #AdgDim
342 * @dress: the new #AdgDress to use
344 * Sets a new dimension dress to @dim. The new dress must be
345 * related to the original dress for this property: you cannot
346 * set a dress used for line styles to a dress managing fonts.
348 * The check is done by calling adg_dress_are_related() with
349 * @dress and the previous dress as arguments. Check out its
350 * documentation for details on what is a related dress.
352 void
353 adg_dim_set_dress(AdgDim *dim, AdgDress dress)
355 g_return_if_fail(ADG_IS_DIM(dim));
357 if (set_dress(dim, dress))
358 g_object_notify((GObject *) dim, "dress");
362 * adg_dim_get_ref1:
363 * @dim: an #AdgDim
365 * Gets the ref1 coordinates. The returned pair is internally owned
366 * and must not be freed or modified.
368 * Returns: the ref1 coordinates
370 const AdgPair *
371 adg_dim_get_ref1(AdgDim *dim)
373 AdgDimPrivate *data;
375 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
377 data = dim->data;
379 return &data->ref1;
383 * adg_dim_get_ref2:
384 * @dim: an #AdgDim
386 * Gets the ref2 coordinates. The returned pair is internally owned
387 * and must not be freed or modified.
389 * Returns: the ref2 coordinates
391 const AdgPair *
392 adg_dim_get_ref2(AdgDim *dim)
394 AdgDimPrivate *data;
396 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
398 data = dim->data;
400 return &data->ref2;
404 * adg_dim_set_ref:
405 * @dim: an #AdgDim
406 * @ref1: the ref1 coordinates
407 * @ref2: the ref2 coordinates
409 * Shortcut to set ref1 and ref2 points at once.
411 void
412 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
414 g_return_if_fail(ADG_IS_DIM(dim));
416 if (ref1 != NULL || ref2 != NULL) {
417 GObject *object;
418 AdgDimPrivate *data;
420 data = dim->data;
421 object = (GObject *) dim;
423 g_object_freeze_notify(object);
425 if (ref1 != NULL) {
426 data->ref1 = *ref1;
427 g_object_notify(object, "ref1");
430 if (ref2 != NULL) {
431 data->ref2 = *ref2;
432 g_object_notify(object, "ref2");
435 g_object_thaw_notify(object);
440 * adg_dim_set_ref_explicit:
441 * @dim: an #AdgDim
442 * @ref1_x: x component of pos1
443 * @ref1_y: y component of pos1
444 * @ref2_x: x component of pos2
445 * @ref2_y: y component of pos2
447 * Shortcut to set ref1 and ref2 points at once,
448 * using explicit coordinates.
450 void
451 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
452 gdouble ref2_x, gdouble ref2_y)
454 AdgPair ref1;
455 AdgPair ref2;
457 ref1.x = ref1_x;
458 ref1.y = ref1_y;
459 ref2.x = ref2_x;
460 ref2.y = ref2_y;
462 adg_dim_set_ref(dim, &ref1, &ref2);
466 * adg_dim_get_pos1:
467 * @dim: an #AdgDim
469 * Gets the pos1 coordinates. The returned pair is internally owned
470 * and must not be freed or modified.
472 * Returns: the pos1 coordinates
474 const AdgPair *
475 adg_dim_get_pos1(AdgDim *dim)
477 AdgDimPrivate *data;
479 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
481 data = dim->data;
483 return &data->pos1;
487 * adg_dim_get_pos2:
488 * @dim: an #AdgDim
490 * Gets the pos2 coordinates. The returned pair is internally owned
491 * and must not be freed or modified.
493 * Returns: the pos2 coordinates
495 const AdgPair *
496 adg_dim_get_pos2(AdgDim *dim)
498 AdgDimPrivate *data;
500 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
502 data = dim->data;
504 return &data->pos2;
508 * adg_dim_set_pos:
509 * @dim: an #AdgDim
510 * @pos1: the pos1 coordinates
511 * @pos2: the pos2 coordinates
513 * Shortcut to set pos1 and pos2 points at once.
515 void
516 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
518 g_return_if_fail(ADG_IS_DIM(dim));
520 if (pos1 != NULL || pos2 != NULL) {
521 AdgDimPrivate *data;
522 GObject *object;
524 data = dim->data;
525 object = (GObject *) dim;
527 g_object_freeze_notify(object);
529 if (pos1 != NULL) {
530 data->pos1 = *pos1;
531 g_object_notify(object, "pos1");
533 if (pos2 != NULL) {
534 data->pos2 = *pos2;
535 g_object_notify(object, "pos2");
538 g_object_thaw_notify(object);
543 * adg_dim_set_pos_explicit:
544 * @dim: an #AdgDim
545 * @pos1_x: x component of pos1
546 * @pos1_y: y component of pos1
547 * @pos2_x: x component of pos2
548 * @pos2_y: y component of pos2
550 * Shortcut to set pos1 and pos2 points at once,
551 * using explicit coordinates.
553 void
554 adg_dim_set_pos_explicit(AdgDim *dim, gdouble pos1_x, gdouble pos1_y,
555 gdouble pos2_x, gdouble pos2_y)
557 AdgPair pos1;
558 AdgPair pos2;
560 pos1.x = pos1_x;
561 pos1.y = pos1_y;
562 pos2.x = pos2_x;
563 pos2.y = pos2_y;
565 adg_dim_set_pos(dim, &pos1, &pos2);
569 * adg_dim_get_level:
570 * @dim: an #AdgDim
572 * Gets the level of this dimension.
574 * Returns: the level value
576 gdouble
577 adg_dim_get_level(AdgDim *dim)
579 AdgDimPrivate *data;
581 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
583 data = dim->data;
585 return data->level;
589 * adg_dim_set_level:
590 * @dim: an #AdgDim
591 * @level: the new level
593 * Sets a new level for this dimension. The level is used to
594 * stack the quotes using a spacing value from dim_style
595 * (specified in global space).
597 void
598 adg_dim_set_level(AdgDim *dim, gdouble level)
600 AdgDimPrivate *data;
602 g_return_if_fail(ADG_IS_DIM(dim));
604 data = dim->data;
605 data->level = level;
607 g_object_notify((GObject *) dim, "level");
611 * adg_dim_get_outside:
612 * @dim: an #AdgDim
614 * Gets the state of the #AdgDim:outside property: check the property
615 * documentation for further details.
617 * Returns: the current flag state
619 AdgThreeState
620 adg_dim_get_outside(AdgDim *dim)
622 AdgDimPrivate *data;
624 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
626 data = dim->data;
628 return data->outside;
632 * adg_dim_set_outside:
633 * @dim: an #AdgDim
634 * @outside: the new outside state
636 * Sets a new state for the #AdgDim:outside flag: check the property
637 * documentation for further details.
639 void
640 adg_dim_set_outside(AdgDim *dim, AdgThreeState outside)
642 AdgDimPrivate *data;
644 g_return_if_fail(ADG_IS_DIM(dim));
646 data = dim->data;
647 data->outside = outside;
649 g_object_notify((GObject *) dim, "outside");
653 * adg_dim_get_value:
654 * @dim: an #AdgDim
656 * Gets the value text. The string is internally owned and
657 * must not be freed or modified.
659 * Returns: the value text
661 const gchar *
662 adg_dim_get_value(AdgDim *dim)
664 AdgDimPrivate *data;
666 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
668 data = dim->data;
670 return data->value;
674 * adg_dim_set_value:
675 * @dim: an #AdgDim
676 * @value: the value text
678 * Explicitely sets the text to use as value. If @value is %NULL or
679 * was never set, an automatic text is calculated using the format
680 * specified in the current #AdgDimStyle and getting its value by
681 * calling the default_value() virtual method.
683 void
684 adg_dim_set_value(AdgDim *dim, const gchar *value)
686 g_return_if_fail(ADG_IS_DIM(dim));
688 if (set_value(dim, value))
689 g_object_notify((GObject *) dim, "value");
693 * adg_dim_get_min:
694 * @dim: an #AdgDim
696 * Gets the minimum value text or %NULL on minimum value disabled.
697 * The string is internally owned and must not be freed or modified.
699 * Returns: the mimimum value text
701 const gchar *
702 adg_dim_get_min(AdgDim *dim)
704 AdgDimPrivate *data;
706 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
708 data = dim->data;
710 return data->min;
714 * adg_dim_set_min:
715 * @dim: an #AdgDim
716 * @min: the new minimum limit
718 * Sets the minimum value. Use %NULL as @min to disable it.
720 void
721 adg_dim_set_min(AdgDim *dim, const gchar *min)
723 g_return_if_fail(ADG_IS_DIM(dim));
725 if (set_min(dim, min))
726 g_object_notify((GObject *) dim, "value-min");
730 * adg_dim_get_max:
731 * @dim: an #AdgDim
733 * Gets the maximum value text or %NULL on maximum value disabled.
734 * The string is internally owned and must not be freed or modified.
736 * Returns: the maximum value text
738 const gchar *
739 adg_dim_get_max(AdgDim *dim)
741 AdgDimPrivate *data;
743 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
745 data = dim->data;
747 return data->max;
751 * adg_dim_set_max:
752 * @dim: an #AdgDim
753 * @max: the new maximum value
755 * Sets the maximum value. Use %NULL as @max to disable it.
757 void
758 adg_dim_set_max(AdgDim *dim, const gchar *max)
760 g_return_if_fail(ADG_IS_DIM(dim));
762 if (set_max(dim, max))
763 g_object_notify((GObject *) dim, "value-max");
767 * adg_dim_set_limits:
768 * @dim: an #AdgDim
769 * @min: the new minumum value
770 * @max: the new maximum value
772 * Shortcut to set both the limits at once.
774 void
775 adg_dim_set_limits(AdgDim *dim, const gchar *min, const gchar *max)
777 g_return_if_fail(ADG_IS_DIM(dim));
779 g_object_freeze_notify((GObject *) dim);
780 adg_dim_set_min(dim, min);
781 adg_dim_set_max(dim, max);
782 g_object_thaw_notify((GObject *) dim);
786 * adg_dim_get_dim_style:
787 * @dim: an #AdgDim entity
789 * Gets the #AdgDimStyle associated to @dim. The dress to style
790 * resolution is done in the arrange() method so this value is
791 * typically available in render() or in a derived arrange()
792 * method, after the #AdgDim arrange() function has been chained up.
794 * Returns: the dim style of @entity
796 AdgDimStyle *
797 adg_dim_get_dim_style(AdgDim *dim)
799 AdgDimPrivate *data;
801 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
803 data = dim->data;
805 return data->dim_style;
809 * adg_dim_get_quote:
810 * @dim: an #AdgDim
812 * <note><para>
813 * This function is only useful in new dimension implementations.
814 * </para></note>
816 * Gets the quote container, if any. This function is valid only
817 * after the #AdgDim implementation of the arrange() virtual method
818 * has been called.
820 * Returns: the quote container
822 AdgContainer *
823 adg_dim_get_quote(AdgDim *dim)
825 AdgDimPrivate *data;
827 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
829 data = dim->data;
831 return data->quote.container;
835 * adg_dim_quote_angle:
836 * @dim: an #AdgDim
837 * @angle: an angle (in radians)
839 * <note><para>
840 * This function is only useful in new dimension implementations.
841 * </para></note>
843 * Converts @angle accordling to the style of @dim. Any quote angle
844 * should be validated by this method because every dimensioning
845 * style has its own convention regardling the text rotation.
847 * Returns: the angle to use (always in radians)
849 gdouble
850 adg_dim_quote_angle(AdgDim *dim, gdouble angle)
852 AdgDimClass *klass;
854 g_return_val_if_fail(ADG_IS_DIM(dim), angle);
856 klass = ADG_DIM_GET_CLASS(dim);
858 if (klass->quote_angle == NULL)
859 return angle;
861 return klass->quote_angle(angle);
864 static void
865 arrange(AdgEntity *entity)
867 AdgDim *dim;
868 AdgDimPrivate *data;
869 AdgEntity *container_entity;
870 AdgEntity *value_entity;
871 AdgEntity *min_entity;
872 AdgEntity *max_entity;
873 CpmlExtents extents;
874 AdgMatrix map;
875 const AdgPair *shift;
877 dim = (AdgDim *) entity;
878 data = dim->data;
880 /* Resolve the dim style */
881 if (data->dim_style == NULL)
882 data->dim_style = (AdgDimStyle *) adg_entity_style(entity, data->dress);
884 if (data->quote.container == NULL)
885 data->quote.container = g_object_new(ADG_TYPE_CONTAINER,
886 "parent", dim, NULL);
888 if (data->quote.value == NULL) {
889 AdgDress dress = adg_dim_style_get_value_dress(data->dim_style);
891 data->quote.value = g_object_new(ADG_TYPE_TOY_TEXT,
892 "dress", dress, NULL);
894 adg_container_add(data->quote.container,
895 (AdgEntity *) data->quote.value);
897 if (data->value == NULL) {
898 /* Automatically generate the value text */
899 gchar *text = ADG_DIM_GET_CLASS(dim)->default_value(dim);
900 adg_toy_text_set_label(data->quote.value, text);
901 g_free(text);
902 } else {
903 adg_toy_text_set_label(data->quote.value, data->value);
907 if (data->quote.min == NULL && data->min != NULL) {
908 AdgDress dress = adg_dim_style_get_min_dress(data->dim_style);
910 data->quote.min = g_object_new(ADG_TYPE_TOY_TEXT, "dress", dress, NULL);
912 adg_container_add(data->quote.container, (AdgEntity *) data->quote.min);
913 adg_toy_text_set_label(data->quote.min, data->min);
916 if (data->quote.max == NULL && data->max != NULL) {
917 AdgDress dress = adg_dim_style_get_max_dress(data->dim_style);
919 data->quote.max = g_object_new(ADG_TYPE_TOY_TEXT, "dress", dress, NULL);
921 adg_container_add(data->quote.container, (AdgEntity *) data->quote.max);
922 adg_toy_text_set_label(data->quote.max, data->max);
925 container_entity = (AdgEntity *) data->quote.container;
926 value_entity = (AdgEntity *) data->quote.value;
927 min_entity = (AdgEntity *) data->quote.min;
928 max_entity = (AdgEntity *) data->quote.max;
930 /* Basic value */
931 adg_entity_get_extents(value_entity, &extents);
933 /* Limit values (min and max) */
934 if (min_entity != NULL || max_entity != NULL) {
935 CpmlExtents min_extents = { 0 };
936 CpmlExtents max_extents = { 0 };
937 gdouble spacing = 0;
939 /* Minimum limit */
940 if (min_entity != NULL)
941 adg_entity_get_extents(min_entity, &min_extents);
943 /* Maximum limit */
944 if (max_entity != NULL)
945 adg_entity_get_extents(max_entity, &max_extents);
947 shift = adg_dim_style_get_limits_shift(data->dim_style);
948 if (min_entity != NULL && max_entity != NULL)
949 spacing = adg_dim_style_get_limits_spacing(data->dim_style);
951 cairo_matrix_init_translate(&map, extents.size.x + shift->x,
952 (spacing + min_extents.size.y +
953 max_extents.size.y) / 2 +
954 shift->y - extents.size.y / 2);
956 if (min_entity != NULL)
957 adg_entity_set_global_map(min_entity, &map);
959 if (max_entity != NULL) {
960 cairo_matrix_translate(&map, 0, -min_extents.size.y - spacing);
961 adg_entity_set_global_map(max_entity, &map);
964 extents.size.x += shift->x + MAX(min_extents.size.x, max_extents.size.x);
967 /* Center and apply the style displacements */
968 shift = adg_dim_style_get_quote_shift(data->dim_style);
969 cairo_matrix_init_translate(&map, shift->x - extents.size.x / 2, shift->y);
970 adg_entity_set_global_map(container_entity, &map);
972 adg_entity_arrange(container_entity);
976 static void
977 global_changed(AdgEntity *entity)
979 AdgDimPrivate *data = ((AdgDim *) entity)->data;
981 PARENT_ENTITY_CLASS->global_changed(entity);
983 if (data->quote.container != NULL)
984 adg_entity_global_changed((AdgEntity *) data->quote.container);
987 static void
988 local_changed(AdgEntity *entity)
990 AdgDimPrivate *data = ((AdgDim *) entity)->data;
992 PARENT_ENTITY_CLASS->local_changed(entity);
994 if (data->quote.container != NULL)
995 adg_entity_local_changed((AdgEntity *) data->quote.container);
998 static void
999 invalidate(AdgEntity *entity)
1001 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1003 if (data->quote.container != NULL)
1004 adg_entity_invalidate((AdgEntity *) data->quote.container);
1007 static gchar *
1008 default_value(AdgDim *dim)
1010 g_warning("AdgDim::default_value not implemented for `%s'",
1011 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1012 return g_strdup("undef");
1015 static gdouble
1016 quote_angle(gdouble angle)
1018 angle = cpml_angle(angle);
1020 if (angle > G_PI_4 || angle <= -G_PI_4 * 3)
1021 angle = cpml_angle(angle + G_PI);
1023 return angle;
1026 static gboolean
1027 set_dress(AdgDim *dim, AdgDress dress)
1029 AdgDimPrivate *data = dim->data;
1031 if (adg_dress_set(&data->dress, dress)) {
1032 data->dim_style = NULL;
1033 return TRUE;
1036 return FALSE;
1039 static gboolean
1040 set_value(AdgDim *dim, const gchar *value)
1042 AdgDimPrivate *data;
1044 data = dim->data;
1046 if (adg_strcmp(value, data->value) == 0)
1047 return FALSE;
1049 g_free(data->value);
1050 data->value = g_strdup(value);
1052 if (data->quote.value != NULL) {
1053 g_object_unref(data->quote.value);
1054 data->quote.value = NULL;
1057 return TRUE;
1060 static gboolean
1061 set_min(AdgDim *dim, const gchar *min)
1063 AdgDimPrivate *data = dim->data;
1065 if (adg_strcmp(min, data->min) == 0)
1066 return FALSE;
1068 g_free(data->min);
1069 data->min = g_strdup(min);
1071 if (data->quote.min != NULL) {
1072 g_object_unref(data->quote.min);
1073 data->quote.min = NULL;
1076 return TRUE;
1079 static gboolean
1080 set_max(AdgDim *dim, const gchar *max)
1082 AdgDimPrivate *data = dim->data;
1084 if (adg_strcmp(max, data->max) == 0)
1085 return FALSE;
1087 g_free(data->max);
1088 data->max = g_strdup(max);
1090 if (data->quote.max != NULL) {
1091 g_object_unref(data->quote.max);
1092 data->quote.max = NULL;
1095 return TRUE;