[AdgDim] Added "outside" property
[adg.git] / adg / adg-dim.c
blob5daf9da7e3cec5c99fd6dc1c18215c651528ce5b
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)
46 enum {
47 PROP_0,
48 PROP_REF1,
49 PROP_REF2,
50 PROP_POS1,
51 PROP_POS2,
52 PROP_ANGLE,
53 PROP_LEVEL,
54 PROP_OUTSIDE,
55 PROP_VALUE,
56 PROP_VALUE_MIN,
57 PROP_VALUE_MAX,
58 PROP_NOTE
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 gboolean invalidate (AdgEntity *entity);
73 static gchar * default_value (AdgDim *dim);
74 static void quote_layout (AdgDim *dim,
75 cairo_t *cr);
76 static gboolean set_value (AdgDim *dim,
77 const gchar *value);
78 static gboolean set_value_min (AdgDim *dim,
79 const gchar *value_min);
80 static gboolean set_value_max (AdgDim *dim,
81 const gchar *value_max);
82 static gboolean set_note (AdgDim *dim,
83 const gchar *note);
84 static void detach_entity (AdgEntity **p_entity);
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->invalidate = invalidate;
109 klass->default_value = default_value;
110 klass->quote_layout = quote_layout;
112 param = g_param_spec_boxed("ref1",
113 P_("Reference 1"),
114 P_("First reference point of the dimension"),
115 ADG_TYPE_PAIR,
116 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
117 g_object_class_install_property(gobject_class, PROP_REF1, param);
119 param = g_param_spec_boxed("ref2",
120 P_("Reference 2"),
121 P_("Second reference point of the dimension"),
122 ADG_TYPE_PAIR,
123 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
124 g_object_class_install_property(gobject_class, PROP_REF2, param);
126 param = g_param_spec_boxed("pos1",
127 P_("Position 1"),
128 P_("First position point: it will be computed with the level property to get the real dimension position"),
129 ADG_TYPE_PAIR, G_PARAM_READWRITE);
130 g_object_class_install_property(gobject_class, PROP_POS1, param);
132 param = g_param_spec_boxed("pos2",
133 P_("Position 2"),
134 P_("Second position point: it will be computed with the level property to get the real dimension position"),
135 ADG_TYPE_PAIR, G_PARAM_READWRITE);
136 g_object_class_install_property(gobject_class, PROP_POS2, param);
138 param = g_param_spec_double("angle",
139 P_("Angle"),
140 P_("The dimension direction, if relevant"),
141 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
142 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
143 g_object_class_install_property(gobject_class, PROP_ANGLE, param);
145 param = g_param_spec_double("level",
146 P_("Level"),
147 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"),
148 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
149 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
150 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
152 param = g_param_spec_enum("outside",
153 P_("Outside"),
154 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"),
155 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
156 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
157 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
159 param = g_param_spec_string("value",
160 P_("Basic Value"),
161 P_("The theoretically exact value for this quote: set to NULL to automatically get the default value"),
162 NULL, G_PARAM_READWRITE);
163 g_object_class_install_property(gobject_class, PROP_VALUE, param);
165 param = g_param_spec_string("value-min",
166 P_("Minimum Value or Low Tolerance"),
167 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
168 NULL, G_PARAM_READWRITE);
169 g_object_class_install_property(gobject_class, PROP_VALUE_MIN, param);
171 param = g_param_spec_string("value-max",
172 P_("Maximum Value or High Tolerance"),
173 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
174 NULL, G_PARAM_READWRITE);
175 g_object_class_install_property(gobject_class, PROP_VALUE_MAX, param);
177 param = g_param_spec_string("note",
178 P_("Note"),
179 P_("A custom note appended to the end of the quote"),
180 NULL, G_PARAM_READWRITE);
181 g_object_class_install_property(gobject_class, PROP_NOTE, param);
184 static void
185 adg_dim_init(AdgDim *dim)
187 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
188 AdgDimPrivate);
190 data->ref1.x = data->ref1.y = 0;
191 data->ref2.x = data->ref2.y = 0;
192 data->pos1.x = data->pos1.y = 0;
193 data->pos2.x = data->pos2.y = 0;
194 data->angle = 0;
195 data->level = 1;
196 data->value = NULL;
197 data->value_min = NULL;
198 data->value_max = NULL;
199 data->note = NULL;
201 data->value_entity = g_object_new(ADG_TYPE_TOY_TEXT,
202 "parent", dim,
203 "font-style", ADG_FONT_STYLE_VALUE,
204 NULL);
205 data->value_min_entity = g_object_new(ADG_TYPE_TOY_TEXT,
206 "parent", dim,
207 "font-style", ADG_FONT_STYLE_TOLERANCE,
208 NULL);
209 data->value_max_entity = g_object_new(ADG_TYPE_TOY_TEXT,
210 "parent", dim,
211 "font-style", ADG_FONT_STYLE_TOLERANCE,
212 NULL);
213 data->note_entity = g_object_new(ADG_TYPE_TOY_TEXT,
214 "parent", dim,
215 "font-style", ADG_FONT_STYLE_NOTE,
216 NULL);
218 dim->data = data;
221 static void
222 dispose(GObject *object)
224 AdgDimPrivate *data = ((AdgDim *) object)->data;
226 detach_entity(&data->value_entity);
227 detach_entity(&data->value_min_entity);
228 detach_entity(&data->value_max_entity);
229 detach_entity(&data->note_entity);
231 if (PARENT_OBJECT_CLASS->dispose != NULL)
232 PARENT_OBJECT_CLASS->dispose(object);
235 static void
236 finalize(GObject *object)
238 AdgDimPrivate *data = ((AdgDim *) object)->data;
240 g_free(data->value);
241 g_free(data->value_min);
242 g_free(data->value_max);
243 g_free(data->note);
245 if (PARENT_OBJECT_CLASS->finalize != NULL)
246 PARENT_OBJECT_CLASS->finalize(object);
249 static void
250 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
252 AdgDimPrivate *data = ((AdgDim *) object)->data;
254 switch (prop_id) {
255 case PROP_REF1:
256 g_value_set_boxed(value, &data->ref1);
257 break;
258 case PROP_REF2:
259 g_value_set_boxed(value, &data->ref2);
260 break;
261 case PROP_POS1:
262 g_value_set_boxed(value, &data->pos1);
263 break;
264 case PROP_POS2:
265 g_value_set_boxed(value, &data->pos1);
266 break;
267 case PROP_ANGLE:
268 g_value_set_double(value, data->angle);
269 break;
270 case PROP_LEVEL:
271 g_value_set_double(value, data->level);
272 break;
273 case PROP_OUTSIDE:
274 g_value_set_enum(value, data->outside);
275 break;
276 case PROP_VALUE:
277 g_value_set_string(value, data->value);
278 break;
279 case PROP_VALUE_MIN:
280 g_value_set_string(value, data->value_min);
281 break;
282 case PROP_VALUE_MAX:
283 g_value_set_string(value, data->value_max);
284 break;
285 case PROP_NOTE:
286 g_value_set_string(value, data->note);
287 break;
288 default:
289 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
290 break;
294 static void
295 set_property(GObject *object, guint prop_id,
296 const GValue *value, GParamSpec *pspec)
298 AdgDim *dim;
299 AdgDimPrivate *data;
301 dim = (AdgDim *) object;
302 data = dim->data;
304 switch (prop_id) {
305 case PROP_REF1:
306 cpml_pair_copy(&data->ref1, (AdgPair *) g_value_get_boxed(value));
307 break;
308 case PROP_REF2:
309 cpml_pair_copy(&data->ref2, (AdgPair *) g_value_get_boxed(value));
310 break;
311 case PROP_POS1:
312 cpml_pair_copy(&data->pos1, (AdgPair *) g_value_get_boxed(value));
313 break;
314 case PROP_POS2:
315 cpml_pair_copy(&data->pos2, (AdgPair *) g_value_get_boxed(value));
316 break;
317 case PROP_ANGLE:
318 data->angle = g_value_get_double(value);
319 break;
320 case PROP_LEVEL:
321 data->level = g_value_get_double(value);
322 break;
323 case PROP_OUTSIDE:
324 data->outside = g_value_get_enum(value);
325 break;
326 case PROP_VALUE:
327 set_value(dim, g_value_get_string(value));
328 break;
329 case PROP_VALUE_MIN:
330 set_value_min(dim, g_value_get_string(value));
331 break;
332 case PROP_VALUE_MAX:
333 set_value_max(dim, g_value_get_string(value));
334 break;
335 case PROP_NOTE:
336 set_note(dim, g_value_get_string(value));
337 break;
338 default:
339 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
340 break;
346 * adg_dim_get_org:
347 * @dim: an #AdgDim
349 * Gets the origin (org) coordinates. The returned pair is internally
350 * owned and must not be freed or modified. This function is only
351 * useful in new dimension implementations.
353 * Returns: the org coordinates
355 const AdgPair *
356 adg_dim_get_org(AdgDim *dim)
358 AdgDimPrivate *data;
360 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
362 data = dim->data;
364 return &data->org;
368 * adg_dim_set_org:
369 * @dim: an #AdgDim
370 * @org: the org coordinates
372 * Sets new org coordinates. This function is only useful
373 * in new dimension implementations.
375 void
376 adg_dim_set_org(AdgDim *dim, const AdgPair *org)
378 AdgDimPrivate *data;
380 g_return_if_fail(ADG_IS_DIM(dim));
381 g_return_if_fail(org != NULL);
383 data = dim->data;
384 data->org = *org;
388 * adg_dim_set_org_explicit:
389 * @dim: an #AdgDim
390 * @org_x: x component of org
391 * @org_y: y component of org
393 * Explicitely sets new org coordinates. This function is only useful
394 * in new dimension implementations.
396 void
397 adg_dim_set_org_explicit(AdgDim *dim, gdouble org_x, gdouble org_y)
399 AdgDimPrivate *data;
401 g_return_if_fail(ADG_IS_DIM(dim));
403 data = dim->data;
404 data->org.x = org_x;
405 data->org.y = org_y;
409 * adg_dim_get_ref1:
410 * @dim: an #AdgDim
412 * Gets the ref1 coordinates. The returned pair is internally owned
413 * and must not be freed or modified.
415 * Returns: the ref1 coordinates
417 const AdgPair *
418 adg_dim_get_ref1(AdgDim *dim)
420 AdgDimPrivate *data;
422 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
424 data = dim->data;
426 return &data->ref1;
430 * adg_dim_get_ref2:
431 * @dim: an #AdgDim
433 * Gets the ref2 coordinates. The returned pair is internally owned
434 * and must not be freed or modified.
436 * Returns: the ref2 coordinates
438 const AdgPair *
439 adg_dim_get_ref2(AdgDim *dim)
441 AdgDimPrivate *data;
443 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
445 data = dim->data;
447 return &data->ref2;
451 * adg_dim_set_ref:
452 * @dim: an #AdgDim
453 * @ref1: the ref1 coordinates
454 * @ref2: the ref2 coordinates
456 * Shortcut to set ref1 and ref2 points at once.
458 void
459 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
461 g_return_if_fail(ADG_IS_DIM(dim));
463 if (ref1 != NULL || ref2 != NULL) {
464 GObject *object;
465 AdgDimPrivate *data;
467 data = dim->data;
468 object = (GObject *) dim;
470 g_object_freeze_notify(object);
472 if (ref1 != NULL) {
473 data->ref1 = *ref1;
474 g_object_notify(object, "ref1");
477 if (ref2 != NULL) {
478 data->ref2 = *ref2;
479 g_object_notify(object, "ref2");
482 g_object_thaw_notify(object);
487 * adg_dim_set_ref_explicit:
488 * @dim: an #AdgDim
489 * @ref1_x: x component of pos1
490 * @ref1_y: y component of pos1
491 * @ref2_x: x component of pos2
492 * @ref2_y: y component of pos2
494 * Shortcut to set ref1 and ref2 points at once,
495 * using explicit coordinates.
497 void
498 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
499 gdouble ref2_x, gdouble ref2_y)
501 AdgPair ref1;
502 AdgPair ref2;
504 ref1.x = ref1_x;
505 ref1.y = ref1_y;
506 ref2.x = ref2_x;
507 ref2.y = ref2_y;
509 adg_dim_set_ref(dim, &ref1, &ref2);
513 * adg_dim_get_pos1:
514 * @dim: an #AdgDim
516 * Gets the pos1 coordinates. The returned pair is internally owned
517 * and must not be freed or modified.
519 * Returns: the pos1 coordinates
521 const AdgPair *
522 adg_dim_get_pos1(AdgDim *dim)
524 AdgDimPrivate *data;
526 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
528 data = dim->data;
530 return &data->pos1;
534 * adg_dim_get_pos2:
535 * @dim: an #AdgDim
537 * Gets the pos2 coordinates. The returned pair is internally owned
538 * and must not be freed or modified.
540 * Returns: the pos2 coordinates
542 const AdgPair *
543 adg_dim_get_pos2(AdgDim *dim)
545 AdgDimPrivate *data;
547 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
549 data = dim->data;
551 return &data->pos2;
555 * adg_dim_set_pos:
556 * @dim: an #AdgDim
557 * @pos1: the pos1 coordinates
558 * @pos2: the pos2 coordinates
560 * Shortcut to set pos1 and pos2 points at once.
562 void
563 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
565 g_return_if_fail(ADG_IS_DIM(dim));
567 if (pos1 != NULL || pos2 != NULL) {
568 AdgDimPrivate *data;
569 GObject *object;
571 data = dim->data;
572 object = (GObject *) dim;
574 g_object_freeze_notify(object);
576 if (pos1 != NULL) {
577 data->pos1 = *pos1;
578 g_object_notify(object, "pos1");
580 if (pos2 != NULL) {
581 data->pos2 = *pos2;
582 g_object_notify(object, "pos2");
585 g_object_thaw_notify(object);
590 * adg_dim_set_pos_explicit:
591 * @dim: an #AdgDim
592 * @pos1_x: x component of pos1
593 * @pos1_y: y component of pos1
594 * @pos2_x: x component of pos2
595 * @pos2_y: y component of pos2
597 * Shortcut to set pos1 and pos2 points at once,
598 * using explicit coordinates.
600 void
601 adg_dim_set_pos_explicit(AdgDim *dim, gdouble pos1_x, gdouble pos1_y,
602 gdouble pos2_x, gdouble pos2_y)
604 AdgPair pos1;
605 AdgPair pos2;
607 pos1.x = pos1_x;
608 pos1.y = pos1_y;
609 pos2.x = pos2_x;
610 pos2.y = pos2_y;
612 adg_dim_set_pos(dim, &pos1, &pos2);
616 * adg_dim_get_angle:
617 * @dim: an #AdgDim
619 * Gets the dimension angle. This function is only useful
620 * in new dimension implementations.
622 * Returns: the angle (in radians)
624 gdouble
625 adg_dim_get_angle(AdgDim *dim)
627 AdgDimPrivate *data;
629 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
631 data = dim->data;
633 return data->angle;
637 * adg_dim_set_angle:
638 * @dim: an #AdgDim
639 * @angle: the new angle (in radians)
641 * Sets a new dimension angle. This function is only useful
642 * in new dimension implementations.
644 void
645 adg_dim_set_angle(AdgDim *dim, gdouble angle)
647 AdgDimPrivate *data;
649 g_return_if_fail(ADG_IS_DIM(dim));
651 data = dim->data;
652 data->angle = angle;
654 g_object_notify((GObject *) dim, "angle");
658 * adg_dim_get_level:
659 * @dim: an #AdgDim
661 * Gets the level of this dimension.
663 * Returns: the level value
665 gdouble
666 adg_dim_get_level(AdgDim *dim)
668 AdgDimPrivate *data;
670 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
672 data = dim->data;
674 return data->level;
678 * adg_dim_set_level:
679 * @dim: an #AdgDim
680 * @level: the new level
682 * Sets a new level for this dimension. The level is used to
683 * stack the quotes using a spacing value from dim_style
684 * (specified in global space).
686 void
687 adg_dim_set_level(AdgDim *dim, gdouble level)
689 AdgDimPrivate *data;
691 g_return_if_fail(ADG_IS_DIM(dim));
693 data = dim->data;
694 data->level = level;
696 g_object_notify((GObject *) dim, "level");
700 * adg_dim_get_outside:
701 * @dim: an #AdgDim
703 * Gets the state of the #AdgDim:outside property: check the property
704 * documentation for further details.
706 * Returns: the current flag state
708 AdgThreeState
709 adg_dim_get_outside(AdgDim *dim)
711 AdgDimPrivate *data;
713 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
715 data = dim->data;
717 return data->outside;
721 * adg_dim_set_outside:
722 * @dim: an #AdgDim
723 * @outside: the new outside state
725 * Sets a new state for the #AdgDim:outside flag: check the property
726 * documentation for further details.
728 void
729 adg_dim_set_outside(AdgDim *dim, AdgThreeState outside)
731 AdgDimPrivate *data;
733 g_return_if_fail(ADG_IS_DIM(dim));
735 data = dim->data;
736 data->outside = outside;
738 g_object_notify((GObject *) dim, "outside");
742 * adg_dim_get_value:
743 * @dim: an #AdgDim
745 * Gets the value text. The string is internally owned and
746 * must not be freed or modified.
748 * Returns: the value text
750 const gchar *
751 adg_dim_get_value(AdgDim *dim)
753 AdgDimPrivate *data;
755 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
757 data = dim->data;
759 return data->value;
763 * adg_dim_set_value:
764 * @dim: an #AdgDim
765 * @value: the value text
767 * Explicitely sets the text to use as value. If @value is %NULL or
768 * was never set, an automatic text is calculated using the format
769 * specified in the current #AdgDimStyle and getting its value by
770 * calling the default_value() virtual method.
772 void
773 adg_dim_set_value(AdgDim *dim, const gchar *value)
775 g_return_if_fail(ADG_IS_DIM(dim));
777 if (set_value(dim, value))
778 g_object_notify((GObject *) dim, "value");
782 * adg_dim_get_value_min:
783 * @dim: an #AdgDim
785 * Gets the minimum value text or %NULL on minimum value disabled.
786 * The string is internally owned and must not be freed or modified.
788 * Returns: the mimimum value text
790 const gchar *
791 adg_dim_get_value_min(AdgDim *dim)
793 AdgDimPrivate *data;
795 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
797 data = dim->data;
799 return data->value_min;
803 * adg_dim_set_value_min:
804 * @dim: an #AdgDim
805 * @value_min: the new minimum value
807 * Sets the minimum value. Use %NULL as @value_min to disable it.
809 void
810 adg_dim_set_value_min(AdgDim *dim, const gchar *value_min)
812 g_return_if_fail(ADG_IS_DIM(dim));
814 if (set_value_min(dim, value_min))
815 g_object_notify((GObject *) dim, "value-min");
819 * adg_dim_get_value_max:
820 * @dim: an #AdgDim
822 * Gets the maximum value text or %NULL on maximum value disabled.
823 * The string is internally owned and must not be freed or modified.
825 * Returns: the maximum value text
827 const gchar *
828 adg_dim_get_value_max(AdgDim *dim)
830 AdgDimPrivate *data;
832 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
834 data = dim->data;
836 return data->value_max;
840 * adg_dim_set_value_max:
841 * @dim: an #AdgDim
842 * @value_max: the new maximum value
844 * Sets the maximum value. Use %NULL as @value_max to disable it.
846 void
847 adg_dim_set_value_max(AdgDim *dim, const gchar *value_max)
849 g_return_if_fail(ADG_IS_DIM(dim));
851 if (set_value_max(dim, value_max))
852 g_object_notify((GObject *) dim, "value-max");
856 * adg_dim_set_tolerances:
857 * @dim: an #AdgDim
858 * @value_min: the new minumum value
859 * @value_max: the new maximum value
861 * Shortcut to set both the tolerances at once.
863 void
864 adg_dim_set_tolerances(AdgDim *dim,
865 const gchar *value_min, const gchar *value_max)
867 g_return_if_fail(ADG_IS_DIM(dim));
869 g_object_freeze_notify((GObject *) dim);
870 adg_dim_set_value_min(dim, value_min);
871 adg_dim_set_value_max(dim, value_max);
872 g_object_thaw_notify((GObject *) dim);
876 * adg_dim_get_note:
877 * @dim: and #AdgDim
879 * Gets the note text or %NULL if the note is not used. The string is
880 * internally owned and must not be freed or modified.
882 * Returns: the note text
884 const gchar *
885 adg_dim_get_note(AdgDim *dim)
887 AdgDimPrivate *data;
889 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
891 data = dim->data;
893 return data->note;
897 * adg_dim_set_note:
898 * @dim: an #AdgDim
899 * @note: the new note
901 * Sets a new note text, usually appended at the end of the dimension text.
903 void
904 adg_dim_set_note(AdgDim *dim, const gchar *note)
906 g_return_if_fail(ADG_IS_DIM(dim));
908 if (set_note(dim, note))
909 g_object_notify((GObject *) dim, "note");
913 * adg_dim_render_quote:
914 * @dim: an #AdgDim object
915 * @cr: a #cairo_t drawing context
917 * Renders the quote of @dim at the @org position. This function
918 * is only useful in new dimension implementations.
920 void
921 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
923 AdgDimPrivate *data;
925 g_return_if_fail(ADG_IS_DIM(dim));
927 data = dim->data;
929 /* Check if the basic value text needs to be automatically generated */
930 if (data->value == NULL) {
931 gchar *text = ADG_DIM_GET_CLASS(dim)->default_value(dim);
932 adg_toy_text_set_label((AdgToyText *) data->value_entity, text);
933 g_free(text);
936 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
938 adg_entity_render(data->value_entity, cr);
939 adg_entity_render(data->value_min_entity, cr);
940 adg_entity_render(data->value_max_entity, cr);
941 adg_entity_render(data->note_entity, cr);
944 static gboolean
945 invalidate(AdgEntity *entity)
947 AdgDimPrivate *data = ((AdgDim *) entity)->data;
949 adg_entity_invalidate(data->value_entity);
950 adg_entity_invalidate(data->value_min_entity);
951 adg_entity_invalidate(data->value_max_entity);
952 adg_entity_invalidate(data->note_entity);
954 return TRUE;
957 static gchar *
958 default_value(AdgDim *dim)
960 g_warning("AdgDim::default_value not implemented for `%s'",
961 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
962 return g_strdup("undef");
965 static void
966 quote_layout(AdgDim *dim, cairo_t *cr)
968 AdgDimPrivate *data;
969 AdgDimStyle *dim_style;
970 cairo_text_extents_t extents;
971 AdgMatrix map;
972 const AdgPair *shift;
974 data = dim->data;
975 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
976 ADG_SLOT_DIM_STYLE);
978 /* Initialize local maps to the origin */
979 cairo_matrix_init_translate(&map, data->org.x, data->org.y);
981 adg_entity_set_local_map(data->value_entity, &map);
982 adg_entity_set_local_map(data->value_min_entity, &map);
983 adg_entity_set_local_map(data->value_max_entity, &map);
984 adg_entity_set_local_map(data->note_entity, &map);
986 /* Initialize global maps to the quote rotation angle:
987 * XXX: check why I had to invert the angle */
988 cairo_matrix_init_rotate(&map, -data->angle);
990 adg_entity_set_global_map(data->value_entity, &map);
991 adg_entity_set_global_map(data->value_min_entity, &map);
992 adg_entity_set_global_map(data->value_max_entity, &map);
993 adg_entity_set_global_map(data->note_entity, &map);
995 /* Basic value */
996 adg_toy_text_get_extents((AdgToyText *) data->value_entity, cr, &extents);
998 /* Limit values (value_min and value_max) */
999 if (data->value_min != NULL || data->value_max != NULL) {
1000 cairo_text_extents_t min_extents = { 0 };
1001 cairo_text_extents_t max_extents = { 0 };
1002 gdouble spacing = 0;
1004 /* Low tolerance */
1005 if (data->value_min != NULL)
1006 adg_toy_text_get_extents((AdgToyText *) data->value_min_entity,
1007 cr, &min_extents);
1009 /* High tolerance */
1010 if (data->value_max != NULL)
1011 adg_toy_text_get_extents((AdgToyText *) data->value_max_entity,
1012 cr, &max_extents);
1014 shift = adg_dim_style_get_tolerance_shift(dim_style);
1015 if (data->value_min != NULL && data->value_max != NULL)
1016 spacing = adg_dim_style_get_tolerance_spacing(dim_style);
1018 cairo_matrix_init_translate(&map, extents.width + shift->x,
1019 (spacing + min_extents.height +
1020 max_extents.height) / 2 +
1021 shift->y - extents.height / 2);
1022 adg_entity_transform_global_map(data->value_min_entity, &map);
1023 cairo_matrix_translate(&map, 0, -min_extents.height - spacing);
1024 adg_entity_transform_global_map(data->value_max_entity, &map);
1026 extents.width += shift->x + MAX(min_extents.width, max_extents.width);
1029 /* Note */
1030 if (data->note != NULL) {
1031 cairo_text_extents_t note_extents;
1033 adg_toy_text_get_extents((AdgToyText *) data->note_entity,
1034 cr, &note_extents);
1035 shift = adg_dim_style_get_note_shift(dim_style);
1037 cairo_matrix_init_translate(&map, extents.width + shift->x, shift->y);
1038 adg_entity_transform_global_map(data->note_entity, &map);
1040 extents.width += shift->x + note_extents.width;
1043 /* Center and apply the style displacements */
1044 shift = adg_dim_style_get_quote_shift(dim_style);
1045 cairo_matrix_init_translate(&map, shift->x - extents.width / 2, shift->y);
1047 adg_entity_transform_global_map(data->value_entity, &map);
1048 adg_entity_transform_global_map(data->value_min_entity, &map);
1049 adg_entity_transform_global_map(data->value_max_entity, &map);
1050 adg_entity_transform_global_map(data->note_entity, &map);
1053 static gboolean
1054 set_value(AdgDim *dim, const gchar *value)
1056 AdgDimPrivate *data;
1058 data = dim->data;
1060 if (adg_strcmp(value, data->value) == 0)
1061 return FALSE;
1063 g_free(data->value);
1064 data->value = g_strdup(value);
1065 adg_toy_text_set_label((AdgToyText *) data->value_entity, value);
1067 return TRUE;
1070 static gboolean
1071 set_value_min(AdgDim *dim, const gchar *value_min)
1073 AdgDimPrivate *data = dim->data;
1075 if (adg_strcmp(value_min, data->value_min) == 0)
1076 return FALSE;
1078 g_free(data->value_min);
1079 data->value_min = g_strdup(value_min);
1080 adg_toy_text_set_label((AdgToyText *) data->value_min_entity, value_min);
1082 return TRUE;
1085 static gboolean
1086 set_value_max(AdgDim *dim, const gchar *value_max)
1088 AdgDimPrivate *data = dim->data;
1090 if (adg_strcmp(value_max, data->value_max) == 0)
1091 return FALSE;
1093 g_free(data->value_max);
1094 data->value_max = g_strdup(value_max);
1095 adg_toy_text_set_label((AdgToyText *) data->value_max_entity, value_max);
1097 return TRUE;
1100 static gboolean
1101 set_note(AdgDim *dim, const gchar *note)
1103 AdgDimPrivate *data;
1105 data = dim->data;
1107 if (adg_strcmp(note, data->note) == 0)
1108 return FALSE;
1110 g_free(data->note);
1111 data->note = g_strdup(note);
1112 adg_toy_text_set_label((AdgToyText *) data->note_entity, note);
1114 return TRUE;
1117 static void
1118 detach_entity(AdgEntity **p_entity)
1120 if (*p_entity != NULL) {
1121 adg_entity_set_parent(*p_entity, NULL);
1122 g_object_unref(*p_entity);
1123 *p_entity = NULL;