[AdgLDim] Avoid arrange() on the same data
[adg.git] / adg / adg-dim.c
blobd981661c19518e0eabae8ea382b7a21340491f17
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_POS,
53 PROP_LEVEL,
54 PROP_OUTSIDE,
55 PROP_VALUE,
56 PROP_MIN,
57 PROP_MAX
61 static void dispose (GObject *object);
62 static void finalize (GObject *object);
63 static void get_property (GObject *object,
64 guint param_id,
65 GValue *value,
66 GParamSpec *pspec);
67 static void set_property (GObject *object,
68 guint param_id,
69 const GValue *value,
70 GParamSpec *pspec);
71 static void global_changed (AdgEntity *entity);
72 static void local_changed (AdgEntity *entity);
73 static void invalidate (AdgEntity *entity);
74 static void arrange (AdgEntity *entity);
75 static gchar * default_value (AdgDim *dim);
76 static gdouble quote_angle (gdouble angle);
77 static gboolean set_dress (AdgDim *dim,
78 AdgDress dress);
79 static gboolean set_value (AdgDim *dim,
80 const gchar *value);
81 static gboolean set_min (AdgDim *dim,
82 const gchar *min);
83 static gboolean set_max (AdgDim *dim,
84 const gchar *max);
87 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
90 static void
91 adg_dim_class_init(AdgDimClass *klass)
93 GObjectClass *gobject_class;
94 AdgEntityClass *entity_class;
95 GParamSpec *param;
97 gobject_class = (GObjectClass *) klass;
98 entity_class = (AdgEntityClass *) klass;
100 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
102 gobject_class->dispose = dispose;
103 gobject_class->finalize = finalize;
104 gobject_class->get_property = get_property;
105 gobject_class->set_property = set_property;
107 entity_class->global_changed = global_changed;
108 entity_class->local_changed = local_changed;
109 entity_class->invalidate = invalidate;
110 entity_class->arrange = arrange;
112 klass->quote_angle = quote_angle;
113 klass->default_value = default_value;
115 param = adg_param_spec_dress("dress",
116 P_("Dress Style"),
117 P_("The dress to use for rendering this dimension"),
118 ADG_DRESS_DIMENSION_REGULAR,
119 G_PARAM_READWRITE);
120 g_object_class_install_property(gobject_class, PROP_DRESS, param);
122 param = g_param_spec_boxed("ref1",
123 P_("Reference 1"),
124 P_("First reference point of the dimension"),
125 ADG_TYPE_PAIR,
126 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
127 g_object_class_install_property(gobject_class, PROP_REF1, param);
129 param = g_param_spec_boxed("ref2",
130 P_("Reference 2"),
131 P_("Second reference point of the dimension"),
132 ADG_TYPE_PAIR,
133 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
134 g_object_class_install_property(gobject_class, PROP_REF2, param);
136 param = g_param_spec_boxed("pos",
137 P_("Position"),
138 P_("The reference position in local space of the quote: it will be combined with \"level\" to get the real quote position"),
139 ADG_TYPE_PAIR, G_PARAM_READWRITE);
140 g_object_class_install_property(gobject_class, PROP_POS, param);
142 param = g_param_spec_double("level",
143 P_("Level"),
144 P_("The dimension level, that is the factor to multiply the baseline spacing (defined in the dimension style) to get the offset from pos where the quote should be rendered"),
145 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
146 G_PARAM_READWRITE);
147 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
149 param = g_param_spec_enum("outside",
150 P_("Outside"),
151 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"),
152 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
153 G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
156 param = g_param_spec_string("value",
157 P_("Basic Value"),
158 P_("The theoretically exact value for this quote: set to NULL to automatically get the default value"),
159 NULL, G_PARAM_READWRITE);
160 g_object_class_install_property(gobject_class, PROP_VALUE, param);
162 param = g_param_spec_string("value-min",
163 P_("Minimum Value or Low Tolerance"),
164 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
165 NULL, G_PARAM_READWRITE);
166 g_object_class_install_property(gobject_class, PROP_MIN, param);
168 param = g_param_spec_string("value-max",
169 P_("Maximum Value or High Tolerance"),
170 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
171 NULL, G_PARAM_READWRITE);
172 g_object_class_install_property(gobject_class, PROP_MAX, param);
175 static void
176 adg_dim_init(AdgDim *dim)
178 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
179 AdgDimPrivate);
181 data->dress = ADG_DRESS_DIMENSION_REGULAR;
182 data->ref1.x = data->ref1.y = 0;
183 data->ref2.x = data->ref2.y = 0;
184 data->pos.x = data->pos.y = 0;
185 data->level = 1;
186 data->outside = ADG_THREE_STATE_UNKNOWN;
187 data->value = NULL;
188 data->min = NULL;
189 data->max = NULL;
191 dim->data = data;
194 static void
195 dispose(GObject *object)
197 AdgDimPrivate *data = ((AdgDim *) object)->data;
199 if (data->quote.container != NULL) {
200 g_object_unref(data->quote.container);
201 data->quote.container = NULL;
204 if (PARENT_OBJECT_CLASS->dispose != NULL)
205 PARENT_OBJECT_CLASS->dispose(object);
208 static void
209 finalize(GObject *object)
211 AdgDimPrivate *data = ((AdgDim *) object)->data;
213 g_free(data->value);
214 g_free(data->min);
215 g_free(data->max);
217 if (PARENT_OBJECT_CLASS->finalize != NULL)
218 PARENT_OBJECT_CLASS->finalize(object);
221 static void
222 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
224 AdgDimPrivate *data = ((AdgDim *) object)->data;
226 switch (prop_id) {
227 case PROP_DRESS:
228 g_value_set_int(value, data->dress);
229 break;
230 case PROP_REF1:
231 g_value_set_boxed(value, &data->ref1);
232 break;
233 case PROP_REF2:
234 g_value_set_boxed(value, &data->ref2);
235 break;
236 case PROP_POS:
237 g_value_set_boxed(value, &data->pos);
238 break;
239 case PROP_LEVEL:
240 g_value_set_double(value, data->level);
241 break;
242 case PROP_OUTSIDE:
243 g_value_set_enum(value, data->outside);
244 break;
245 case PROP_VALUE:
246 g_value_set_string(value, data->value);
247 break;
248 case PROP_MIN:
249 g_value_set_string(value, data->min);
250 break;
251 case PROP_MAX:
252 g_value_set_string(value, data->max);
253 break;
254 default:
255 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
256 break;
260 static void
261 set_property(GObject *object, guint prop_id,
262 const GValue *value, GParamSpec *pspec)
264 AdgDim *dim;
265 AdgDimPrivate *data;
267 dim = (AdgDim *) object;
268 data = dim->data;
270 switch (prop_id) {
271 case PROP_DRESS:
272 set_dress(dim, g_value_get_int(value));
273 break;
274 case PROP_REF1:
275 cpml_pair_copy(&data->ref1, (AdgPair *) g_value_get_boxed(value));
276 break;
277 case PROP_REF2:
278 cpml_pair_copy(&data->ref2, (AdgPair *) g_value_get_boxed(value));
279 break;
280 case PROP_POS:
281 cpml_pair_copy(&data->pos, (AdgPair *) g_value_get_boxed(value));
282 break;
283 case PROP_LEVEL:
284 data->level = g_value_get_double(value);
285 break;
286 case PROP_OUTSIDE:
287 data->outside = g_value_get_enum(value);
288 break;
289 case PROP_VALUE:
290 set_value(dim, g_value_get_string(value));
291 break;
292 case PROP_MIN:
293 set_min(dim, g_value_get_string(value));
294 break;
295 case PROP_MAX:
296 set_max(dim, g_value_get_string(value));
297 break;
298 default:
299 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
300 break;
306 * adg_dim_get_dress:
307 * @dim: an #AdgDim
309 * Gets the dimension dress to be used in rendering @dim.
311 * Returns: the current dimension dress
313 AdgDress
314 adg_dim_get_dress(AdgDim *dim)
316 AdgDimPrivate *data;
318 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_DRESS_UNDEFINED);
320 data = dim->data;
322 return data->dress;
326 * adg_dim_set_dress:
327 * @dim: an #AdgDim
328 * @dress: the new #AdgDress to use
330 * Sets a new dimension dress to @dim. The new dress must be
331 * related to the original dress for this property: you cannot
332 * set a dress used for line styles to a dress managing fonts.
334 * The check is done by calling adg_dress_are_related() with
335 * @dress and the previous dress as arguments. Check out its
336 * documentation for details on what is a related dress.
338 void
339 adg_dim_set_dress(AdgDim *dim, AdgDress dress)
341 g_return_if_fail(ADG_IS_DIM(dim));
343 if (set_dress(dim, dress))
344 g_object_notify((GObject *) dim, "dress");
348 * adg_dim_get_ref1:
349 * @dim: an #AdgDim
351 * Gets the ref1 coordinates. The returned pair is internally owned
352 * and must not be freed or modified.
354 * Returns: the ref1 coordinates
356 const AdgPair *
357 adg_dim_get_ref1(AdgDim *dim)
359 AdgDimPrivate *data;
361 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
363 data = dim->data;
365 return &data->ref1;
369 * adg_dim_get_ref2:
370 * @dim: an #AdgDim
372 * Gets the ref2 coordinates. The returned pair is internally owned
373 * and must not be freed or modified.
375 * Returns: the ref2 coordinates
377 const AdgPair *
378 adg_dim_get_ref2(AdgDim *dim)
380 AdgDimPrivate *data;
382 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
384 data = dim->data;
386 return &data->ref2;
390 * adg_dim_set_ref:
391 * @dim: an #AdgDim
392 * @ref1: the ref1 coordinates
393 * @ref2: the ref2 coordinates
395 * Shortcut to set ref1 and ref2 points at once.
397 void
398 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
400 g_return_if_fail(ADG_IS_DIM(dim));
402 if (ref1 != NULL || ref2 != NULL) {
403 GObject *object;
404 AdgDimPrivate *data;
406 data = dim->data;
407 object = (GObject *) dim;
409 g_object_freeze_notify(object);
411 if (ref1 != NULL) {
412 data->ref1 = *ref1;
413 g_object_notify(object, "ref1");
416 if (ref2 != NULL) {
417 data->ref2 = *ref2;
418 g_object_notify(object, "ref2");
421 g_object_thaw_notify(object);
426 * adg_dim_set_ref_explicit:
427 * @dim: an #AdgDim
428 * @ref1_x: x coordinate of ref1
429 * @ref1_y: y coordinate of ref1
430 * @ref2_x: x coordinate of ref2
431 * @ref2_y: y coordinate of ref2
433 * Shortcut to set ref1 and ref2 points at once,
434 * using explicit coordinates.
436 void
437 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
438 gdouble ref2_x, gdouble ref2_y)
440 AdgPair ref1;
441 AdgPair ref2;
443 ref1.x = ref1_x;
444 ref1.y = ref1_y;
445 ref2.x = ref2_x;
446 ref2.y = ref2_y;
448 adg_dim_set_ref(dim, &ref1, &ref2);
452 * adg_dim_get_pos:
453 * @dim: an #AdgDim
455 * Gets the position coordinates. The returned pair is internally owned
456 * and must not be freed or modified.
458 * Returns: the pos coordinates
460 const AdgPair *
461 adg_dim_get_pos(AdgDim *dim)
463 AdgDimPrivate *data;
465 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
467 data = dim->data;
469 return &data->pos;
473 * adg_dim_set_pos:
474 * @dim: an #AdgDim
475 * @pos: the pos coordinates
477 * Sets a new #AdgDim:pos position.
479 void
480 adg_dim_set_pos(AdgDim *dim, const AdgPair *pos)
482 AdgDimPrivate *data;
484 g_return_if_fail(ADG_IS_DIM(dim));
485 g_return_if_fail(pos != NULL);
487 data = dim->data;
489 data->pos = *pos;
491 g_object_notify((GObject *) dim, "pos");
495 * adg_dim_set_pos_explicit:
496 * @dim: an #AdgDim
497 * @pos_x: x coordinate of pos
498 * @pos_y: y coordinate of pos
500 * Shortcut to set #AdgDim:pos using explicit coordinates.
502 void
503 adg_dim_set_pos_explicit(AdgDim *dim, gdouble x, gdouble y)
505 AdgPair pos;
507 pos.x = x;
508 pos.y = y;
510 adg_dim_set_pos(dim, &pos);
514 * adg_dim_get_level:
515 * @dim: an #AdgDim
517 * Gets the level of this dimension.
519 * Returns: the level value
521 gdouble
522 adg_dim_get_level(AdgDim *dim)
524 AdgDimPrivate *data;
526 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
528 data = dim->data;
530 return data->level;
534 * adg_dim_set_level:
535 * @dim: an #AdgDim
536 * @level: the new level
538 * Sets a new level for this dimension. The level is used to
539 * stack the quotes using a spacing value from dim_style
540 * (specified in global space).
542 void
543 adg_dim_set_level(AdgDim *dim, gdouble level)
545 AdgDimPrivate *data;
547 g_return_if_fail(ADG_IS_DIM(dim));
549 data = dim->data;
550 data->level = level;
552 g_object_notify((GObject *) dim, "level");
556 * adg_dim_get_outside:
557 * @dim: an #AdgDim
559 * Gets the state of the #AdgDim:outside property: check the property
560 * documentation for further details.
562 * Returns: the current flag state
564 AdgThreeState
565 adg_dim_get_outside(AdgDim *dim)
567 AdgDimPrivate *data;
569 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
571 data = dim->data;
573 return data->outside;
577 * adg_dim_set_outside:
578 * @dim: an #AdgDim
579 * @outside: the new outside state
581 * Sets a new state for the #AdgDim:outside flag: check the property
582 * documentation for further details.
584 void
585 adg_dim_set_outside(AdgDim *dim, AdgThreeState outside)
587 AdgDimPrivate *data;
589 g_return_if_fail(ADG_IS_DIM(dim));
591 data = dim->data;
592 data->outside = outside;
594 g_object_notify((GObject *) dim, "outside");
598 * adg_dim_get_value:
599 * @dim: an #AdgDim
601 * Gets the value text. The string is internally owned and
602 * must not be freed or modified.
604 * Returns: the value text
606 const gchar *
607 adg_dim_get_value(AdgDim *dim)
609 AdgDimPrivate *data;
611 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
613 data = dim->data;
615 return data->value;
619 * adg_dim_set_value:
620 * @dim: an #AdgDim
621 * @value: the value text
623 * Explicitely sets the text to use as value. If @value is %NULL or
624 * was never set, an automatic text is calculated using the format
625 * specified in the current #AdgDimStyle and getting its value by
626 * calling the default_value() virtual method.
628 void
629 adg_dim_set_value(AdgDim *dim, const gchar *value)
631 g_return_if_fail(ADG_IS_DIM(dim));
633 if (set_value(dim, value))
634 g_object_notify((GObject *) dim, "value");
638 * adg_dim_get_min:
639 * @dim: an #AdgDim
641 * Gets the minimum value text or %NULL on minimum value disabled.
642 * The string is internally owned and must not be freed or modified.
644 * Returns: the mimimum value text
646 const gchar *
647 adg_dim_get_min(AdgDim *dim)
649 AdgDimPrivate *data;
651 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
653 data = dim->data;
655 return data->min;
659 * adg_dim_set_min:
660 * @dim: an #AdgDim
661 * @min: the new minimum limit
663 * Sets the minimum value. Use %NULL as @min to disable it.
665 void
666 adg_dim_set_min(AdgDim *dim, const gchar *min)
668 g_return_if_fail(ADG_IS_DIM(dim));
670 if (set_min(dim, min))
671 g_object_notify((GObject *) dim, "value-min");
675 * adg_dim_get_max:
676 * @dim: an #AdgDim
678 * Gets the maximum value text or %NULL on maximum value disabled.
679 * The string is internally owned and must not be freed or modified.
681 * Returns: the maximum value text
683 const gchar *
684 adg_dim_get_max(AdgDim *dim)
686 AdgDimPrivate *data;
688 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
690 data = dim->data;
692 return data->max;
696 * adg_dim_set_max:
697 * @dim: an #AdgDim
698 * @max: the new maximum value
700 * Sets the maximum value. Use %NULL as @max to disable it.
702 void
703 adg_dim_set_max(AdgDim *dim, const gchar *max)
705 g_return_if_fail(ADG_IS_DIM(dim));
707 if (set_max(dim, max))
708 g_object_notify((GObject *) dim, "value-max");
712 * adg_dim_set_limits:
713 * @dim: an #AdgDim
714 * @min: the new minumum value
715 * @max: the new maximum value
717 * Shortcut to set both the limits at once.
719 void
720 adg_dim_set_limits(AdgDim *dim, const gchar *min, const gchar *max)
722 g_return_if_fail(ADG_IS_DIM(dim));
724 g_object_freeze_notify((GObject *) dim);
725 adg_dim_set_min(dim, min);
726 adg_dim_set_max(dim, max);
727 g_object_thaw_notify((GObject *) dim);
731 * adg_dim_get_dim_style:
732 * @dim: an #AdgDim entity
734 * Gets the #AdgDimStyle associated to @dim. The dress to style
735 * resolution is done in the arrange() method so this value is
736 * typically available in render() or in a derived arrange()
737 * method, after the #AdgDim arrange() function has been chained up.
739 * Returns: the dim style of @entity
741 AdgDimStyle *
742 adg_dim_get_dim_style(AdgDim *dim)
744 AdgDimPrivate *data;
746 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
748 data = dim->data;
750 return data->dim_style;
754 * adg_dim_get_quote:
755 * @dim: an #AdgDim
757 * <note><para>
758 * This function is only useful in new dimension implementations.
759 * </para></note>
761 * Gets the quote container, if any. This function is valid only
762 * after the #AdgDim implementation of the arrange() virtual method
763 * has been called.
765 * Returns: the quote container
767 AdgContainer *
768 adg_dim_get_quote(AdgDim *dim)
770 AdgDimPrivate *data;
772 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
774 data = dim->data;
776 return data->quote.container;
780 * adg_dim_quote_angle:
781 * @dim: an #AdgDim
782 * @angle: an angle (in radians)
784 * <note><para>
785 * This function is only useful in new dimension implementations.
786 * </para></note>
788 * Converts @angle accordling to the style of @dim. Any quote angle
789 * should be validated by this method because every dimensioning
790 * style has its own convention regardling the text rotation.
792 * Returns: the angle to use (always in radians)
794 gdouble
795 adg_dim_quote_angle(AdgDim *dim, gdouble angle)
797 AdgDimClass *klass;
799 g_return_val_if_fail(ADG_IS_DIM(dim), angle);
801 klass = ADG_DIM_GET_CLASS(dim);
803 if (klass->quote_angle == NULL)
804 return angle;
806 return klass->quote_angle(angle);
809 static void
810 arrange(AdgEntity *entity)
812 AdgDim *dim;
813 AdgDimPrivate *data;
814 AdgEntity *container_entity;
815 AdgEntity *value_entity;
816 AdgEntity *min_entity;
817 AdgEntity *max_entity;
818 CpmlExtents extents;
819 AdgMatrix map;
820 const AdgPair *shift;
822 dim = (AdgDim *) entity;
823 data = dim->data;
825 /* Resolve the dim style */
826 if (data->dim_style == NULL)
827 data->dim_style = (AdgDimStyle *) adg_entity_style(entity, data->dress);
829 if (data->quote.container == NULL)
830 data->quote.container = g_object_new(ADG_TYPE_CONTAINER,
831 "parent", dim, NULL);
833 if (data->quote.value == NULL) {
834 AdgDress dress = adg_dim_style_get_value_dress(data->dim_style);
836 data->quote.value = g_object_new(ADG_TYPE_TOY_TEXT,
837 "dress", dress, NULL);
839 adg_container_add(data->quote.container,
840 (AdgEntity *) data->quote.value);
842 if (data->value == NULL) {
843 /* Automatically generate the value text */
844 gchar *text = ADG_DIM_GET_CLASS(dim)->default_value(dim);
845 adg_toy_text_set_label(data->quote.value, text);
846 g_free(text);
847 } else {
848 adg_toy_text_set_label(data->quote.value, data->value);
852 if (data->quote.min == NULL && data->min != NULL) {
853 AdgDress dress = adg_dim_style_get_min_dress(data->dim_style);
855 data->quote.min = g_object_new(ADG_TYPE_TOY_TEXT, "dress", dress, NULL);
857 adg_container_add(data->quote.container, (AdgEntity *) data->quote.min);
858 adg_toy_text_set_label(data->quote.min, data->min);
861 if (data->quote.max == NULL && data->max != NULL) {
862 AdgDress dress = adg_dim_style_get_max_dress(data->dim_style);
864 data->quote.max = g_object_new(ADG_TYPE_TOY_TEXT, "dress", dress, NULL);
866 adg_container_add(data->quote.container, (AdgEntity *) data->quote.max);
867 adg_toy_text_set_label(data->quote.max, data->max);
870 container_entity = (AdgEntity *) data->quote.container;
871 value_entity = (AdgEntity *) data->quote.value;
872 min_entity = (AdgEntity *) data->quote.min;
873 max_entity = (AdgEntity *) data->quote.max;
875 /* Basic value */
876 adg_entity_get_extents(value_entity, &extents);
878 /* Limit values (min and max) */
879 if (min_entity != NULL || max_entity != NULL) {
880 CpmlExtents min_extents = { 0 };
881 CpmlExtents max_extents = { 0 };
882 gdouble spacing = 0;
884 /* Minimum limit */
885 if (min_entity != NULL)
886 adg_entity_get_extents(min_entity, &min_extents);
888 /* Maximum limit */
889 if (max_entity != NULL)
890 adg_entity_get_extents(max_entity, &max_extents);
892 shift = adg_dim_style_get_limits_shift(data->dim_style);
893 if (min_entity != NULL && max_entity != NULL)
894 spacing = adg_dim_style_get_limits_spacing(data->dim_style);
896 cairo_matrix_init_translate(&map, extents.size.x + shift->x,
897 (spacing + min_extents.size.y +
898 max_extents.size.y) / 2 +
899 shift->y - extents.size.y / 2);
901 if (min_entity != NULL)
902 adg_entity_set_global_map(min_entity, &map);
904 if (max_entity != NULL) {
905 cairo_matrix_translate(&map, 0, -min_extents.size.y - spacing);
906 adg_entity_set_global_map(max_entity, &map);
909 extents.size.x += shift->x + MAX(min_extents.size.x, max_extents.size.x);
912 /* Center and apply the style displacements */
913 shift = adg_dim_style_get_quote_shift(data->dim_style);
914 cairo_matrix_init_translate(&map, shift->x - extents.size.x / 2, shift->y);
915 adg_entity_set_global_map(container_entity, &map);
917 adg_entity_arrange(container_entity);
921 static void
922 global_changed(AdgEntity *entity)
924 AdgDimPrivate *data = ((AdgDim *) entity)->data;
926 PARENT_ENTITY_CLASS->global_changed(entity);
928 if (data->quote.container != NULL)
929 adg_entity_global_changed((AdgEntity *) data->quote.container);
932 static void
933 local_changed(AdgEntity *entity)
935 AdgDimPrivate *data = ((AdgDim *) entity)->data;
937 PARENT_ENTITY_CLASS->local_changed(entity);
939 if (data->quote.container != NULL)
940 adg_entity_local_changed((AdgEntity *) data->quote.container);
943 static void
944 invalidate(AdgEntity *entity)
946 AdgDimPrivate *data = ((AdgDim *) entity)->data;
948 if (data->quote.container != NULL)
949 adg_entity_invalidate((AdgEntity *) data->quote.container);
952 static gchar *
953 default_value(AdgDim *dim)
955 g_warning("AdgDim::default_value not implemented for `%s'",
956 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
957 return g_strdup("undef");
960 static gdouble
961 quote_angle(gdouble angle)
963 angle = cpml_angle(angle);
965 if (angle > G_PI_4 || angle <= -G_PI_4 * 3)
966 angle = cpml_angle(angle + G_PI);
968 return angle;
971 static gboolean
972 set_dress(AdgDim *dim, AdgDress dress)
974 AdgDimPrivate *data = dim->data;
976 if (adg_dress_set(&data->dress, dress)) {
977 data->dim_style = NULL;
978 return TRUE;
981 return FALSE;
984 static gboolean
985 set_value(AdgDim *dim, const gchar *value)
987 AdgDimPrivate *data;
989 data = dim->data;
991 if (adg_strcmp(value, data->value) == 0)
992 return FALSE;
994 g_free(data->value);
995 data->value = g_strdup(value);
997 if (data->quote.value != NULL) {
998 g_object_unref(data->quote.value);
999 data->quote.value = NULL;
1002 return TRUE;
1005 static gboolean
1006 set_min(AdgDim *dim, const gchar *min)
1008 AdgDimPrivate *data = dim->data;
1010 if (adg_strcmp(min, data->min) == 0)
1011 return FALSE;
1013 g_free(data->min);
1014 data->min = g_strdup(min);
1016 if (data->quote.min != NULL) {
1017 g_object_unref(data->quote.min);
1018 data->quote.min = NULL;
1021 return TRUE;
1024 static gboolean
1025 set_max(AdgDim *dim, const gchar *max)
1027 AdgDimPrivate *data = dim->data;
1029 if (adg_strcmp(max, data->max) == 0)
1030 return FALSE;
1032 g_free(data->max);
1033 data->max = g_strdup(max);
1035 if (data->quote.max != NULL) {
1036 g_object_unref(data->quote.max);
1037 data->quote.max = NULL;
1040 return TRUE;