[AdgDim] Improved rendering of limit values
[adg.git] / src / adg / adg-dim.c
blob0fa9224e1a14778e47d625627e4be46160314458
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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-internal.h"
37 #include "adg-dim.h"
38 #include "adg-dim-private.h"
39 #include "adg-dim-style.h"
40 #include "adg-dress-builtins.h"
41 #include "adg-toy-text.h"
42 #include "adg-type-builtins.h"
44 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_dim_parent_class)
45 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_dim_parent_class)
48 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
50 enum {
51 PROP_0,
52 PROP_DIM_DRESS,
53 PROP_REF1,
54 PROP_REF2,
55 PROP_POS,
56 PROP_LEVEL,
57 PROP_OUTSIDE,
58 PROP_DETACHED,
59 PROP_VALUE,
60 PROP_MIN,
61 PROP_MAX
65 static void _adg_dispose (GObject *object);
66 static void _adg_finalize (GObject *object);
67 static void _adg_get_property (GObject *object,
68 guint param_id,
69 GValue *value,
70 GParamSpec *pspec);
71 static void _adg_set_property (GObject *object,
72 guint param_id,
73 const GValue *value,
74 GParamSpec *pspec);
75 static void _adg_global_changed (AdgEntity *entity);
76 static void _adg_local_changed (AdgEntity *entity);
77 static void _adg_invalidate (AdgEntity *entity);
78 static void _adg_arrange (AdgEntity *entity);
79 static gchar * _adg_default_value (AdgDim *dim);
80 static gdouble _adg_quote_angle (gdouble angle);
81 static gboolean _adg_set_outside (AdgDim *dim,
82 AdgThreeState outside);
83 static gboolean _adg_set_detached (AdgDim *dim,
84 AdgThreeState detached);
85 static gboolean _adg_set_value (AdgDim *dim,
86 const gchar *value);
87 static gboolean _adg_set_min (AdgDim *dim,
88 const gchar *min);
89 static gboolean _adg_set_max (AdgDim *dim,
90 const gchar *max);
93 static void
94 adg_dim_class_init(AdgDimClass *klass)
96 GObjectClass *gobject_class;
97 AdgEntityClass *entity_class;
98 GParamSpec *param;
100 gobject_class = (GObjectClass *) klass;
101 entity_class = (AdgEntityClass *) klass;
103 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
105 gobject_class->dispose = _adg_dispose;
106 gobject_class->finalize = _adg_finalize;
107 gobject_class->get_property = _adg_get_property;
108 gobject_class->set_property = _adg_set_property;
110 entity_class->global_changed = _adg_global_changed;
111 entity_class->local_changed = _adg_local_changed;
112 entity_class->invalidate = _adg_invalidate;
113 entity_class->arrange = _adg_arrange;
115 klass->quote_angle = _adg_quote_angle;
116 klass->default_value = _adg_default_value;
118 param = adg_param_spec_dress("dim-dress",
119 P_("Dimension Dress"),
120 P_("The dress to use for rendering this dimension"),
121 ADG_DRESS_DIMENSION,
122 G_PARAM_READWRITE);
123 g_object_class_install_property(gobject_class, PROP_DIM_DRESS, param);
125 param = g_param_spec_boxed("ref1",
126 P_("First Reference"),
127 P_("First reference point of the dimension"),
128 ADG_TYPE_POINT,
129 G_PARAM_READWRITE);
130 g_object_class_install_property(gobject_class, PROP_REF1, param);
132 param = g_param_spec_boxed("ref2",
133 P_("Second Reference"),
134 P_("Second reference point of the dimension"),
135 ADG_TYPE_POINT,
136 G_PARAM_READWRITE);
137 g_object_class_install_property(gobject_class, PROP_REF2, param);
139 param = g_param_spec_boxed("pos",
140 P_("Position"),
141 P_("The reference position of the quote: it will be combined with \"level\" to get the real quote position"),
142 ADG_TYPE_POINT,
143 G_PARAM_READWRITE);
144 g_object_class_install_property(gobject_class, PROP_POS, param);
146 param = g_param_spec_double("level",
147 P_("Level"),
148 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"),
149 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
150 G_PARAM_READWRITE);
151 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
153 param = g_param_spec_enum("outside",
154 P_("Outside"),
155 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"),
156 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
157 G_PARAM_READWRITE);
158 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
160 param = g_param_spec_enum("detached",
161 P_("Detached Quote"),
162 P_("Where the quote must be positioned: in the middle of the base line (ADG_THREE_STATE_OFF), near the pos point (ADG_THREE_STATE_ON) or should be automatically deducted depending on the available space"),
163 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
164 G_PARAM_READWRITE);
165 g_object_class_install_property(gobject_class, PROP_DETACHED, param);
167 param = g_param_spec_string("value",
168 P_("Value Template"),
169 P_("The template string to be used for generating the set value of the quote"),
170 NULL,
171 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
172 g_object_class_install_property(gobject_class, PROP_VALUE, param);
174 param = g_param_spec_string("min",
175 P_("Minimum Value or Low Tolerance"),
176 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
177 NULL,
178 G_PARAM_READWRITE);
179 g_object_class_install_property(gobject_class, PROP_MIN, param);
181 param = g_param_spec_string("max",
182 P_("Maximum Value or High Tolerance"),
183 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
184 NULL,
185 G_PARAM_READWRITE);
186 g_object_class_install_property(gobject_class, PROP_MAX, param);
189 static void
190 adg_dim_init(AdgDim *dim)
192 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
193 AdgDimPrivate);
195 data->dim_dress = ADG_DRESS_DIMENSION;
196 data->ref1 = NULL;
197 data->ref2 = NULL;
198 data->pos = NULL;
199 data->level = 1;
200 data->outside = ADG_THREE_STATE_UNKNOWN;
201 data->detached = ADG_THREE_STATE_UNKNOWN;
202 data->min = NULL;
203 data->max = NULL;
205 #if 0
206 /* These ones are G_PARAM_CONSTRUCT, so initialized by GObject */
207 data->value = NULL
208 #endif
210 dim->data = data;
213 static void
214 _adg_dispose(GObject *object)
216 AdgEntity *entity;
217 AdgDimPrivate *data;
219 entity = (AdgEntity *) object;
220 data = ((AdgDim *) object)->data;
222 if (data->quote.entity) {
223 g_object_unref(data->quote.entity);
224 data->quote.entity = NULL;
227 if (data->ref1)
228 data->ref1 = adg_entity_point(entity, data->ref1, NULL);
230 if (data->ref2)
231 data->ref2 = adg_entity_point(entity, data->ref2, NULL);
233 if (data->pos)
234 data->pos = adg_entity_point(entity, data->pos, NULL);
236 if (_ADG_OLD_OBJECT_CLASS->dispose)
237 _ADG_OLD_OBJECT_CLASS->dispose(object);
240 static void
241 _adg_finalize(GObject *object)
243 AdgDimPrivate *data = ((AdgDim *) object)->data;
245 g_free(data->value);
246 g_free(data->min);
247 g_free(data->max);
249 if (_ADG_OLD_OBJECT_CLASS->finalize)
250 _ADG_OLD_OBJECT_CLASS->finalize(object);
253 static void
254 _adg_get_property(GObject *object, guint prop_id,
255 GValue *value, GParamSpec *pspec)
257 AdgDimPrivate *data = ((AdgDim *) object)->data;
259 switch (prop_id) {
260 case PROP_DIM_DRESS:
261 g_value_set_int(value, data->dim_dress);
262 break;
263 case PROP_REF1:
264 g_value_set_boxed(value, data->ref1);
265 break;
266 case PROP_REF2:
267 g_value_set_boxed(value, data->ref2);
268 break;
269 case PROP_POS:
270 g_value_set_boxed(value, data->pos);
271 break;
272 case PROP_LEVEL:
273 g_value_set_double(value, data->level);
274 break;
275 case PROP_OUTSIDE:
276 g_value_set_enum(value, data->outside);
277 break;
278 case PROP_DETACHED:
279 g_value_set_enum(value, data->detached);
280 break;
281 case PROP_VALUE:
282 g_value_set_string(value, data->value);
283 break;
284 case PROP_MIN:
285 g_value_set_string(value, data->min);
286 break;
287 case PROP_MAX:
288 g_value_set_string(value, data->max);
289 break;
290 default:
291 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
292 break;
296 static void
297 _adg_set_property(GObject *object, guint prop_id,
298 const GValue *value, GParamSpec *pspec)
300 AdgEntity *entity;
301 AdgDim *dim;
302 AdgDimPrivate *data;
304 entity = (AdgEntity *) object;
305 dim = (AdgDim *) object;
306 data = dim->data;
308 switch (prop_id) {
309 case PROP_DIM_DRESS:
310 data->dim_dress = g_value_get_int(value);
311 break;
312 case PROP_REF1:
313 data->ref1 = adg_entity_point(entity, data->ref1,
314 g_value_get_boxed(value));
315 break;
316 case PROP_REF2:
317 data->ref2 = adg_entity_point(entity, data->ref2,
318 g_value_get_boxed(value));
319 break;
320 case PROP_POS:
321 data->pos = adg_entity_point(entity, data->pos,
322 g_value_get_boxed(value));
323 break;
324 case PROP_LEVEL:
325 data->level = g_value_get_double(value);
326 break;
327 case PROP_OUTSIDE:
328 _adg_set_outside(dim, g_value_get_enum(value));
329 break;
330 case PROP_DETACHED:
331 _adg_set_detached(dim, g_value_get_enum(value));
332 break;
333 case PROP_VALUE:
334 _adg_set_value(dim, g_value_get_string(value));
335 break;
336 case PROP_MIN:
337 _adg_set_min(dim, g_value_get_string(value));
338 break;
339 case PROP_MAX:
340 _adg_set_max(dim, g_value_get_string(value));
341 break;
342 default:
343 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
344 break;
350 * adg_dim_set_dim_dress:
351 * @dim: an #AdgDim
352 * @dress: the new #AdgDress to use
354 * Sets a new dimension dress to @dim. The new dress must be
355 * related to the original dress for this property: you cannot
356 * set a dress used for line styles to a dress managing fonts.
358 * The check is done by calling adg_dress_are_related() with
359 * @dress and the previous dress as arguments. Check out its
360 * documentation for details on what is a related dress.
362 void
363 adg_dim_set_dim_dress(AdgDim *dim, AdgDress dress)
365 g_return_if_fail(ADG_IS_DIM(dim));
366 g_object_set((GObject *) dim, "dim-dress", dress, NULL);
370 * adg_dim_get_dim_dress:
371 * @dim: an #AdgDim
373 * Gets the dimension dress to be used in rendering @dim.
375 * Returns: the current dimension dress
377 AdgDress
378 adg_dim_get_dim_dress(AdgDim *dim)
380 AdgDimPrivate *data;
382 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_DRESS_UNDEFINED);
384 data = dim->data;
386 return data->dim_dress;
390 * adg_dim_set_ref1:
391 * @dim: an #AdgDim
392 * @ref1: the new point to use as first reference
394 * Sets the #AdgDim:ref1 property to @ref1. The old point
395 * is silently discarded, unreferencing its model if that
396 * point was bound to a named pair (hence, possibly destroying
397 * the model if this was the last reference).
399 * @ref1 can be %NULL, in which case the point is destroyed.
401 void
402 adg_dim_set_ref1(AdgDim *dim, const AdgPoint *ref1)
404 g_return_if_fail(ADG_IS_DIM(dim));
405 g_object_set((GObject *) dim, "ref1", ref1, NULL);
409 * adg_dim_set_ref1_explicit:
410 * @dim: an #AdgDim
411 * @x: x coordinate of the first reference point
412 * @y: y coordinate of the first reference point
414 * Sets the #AdgDim:ref1 property to the (@x, @y) explicit
415 * coordinates. The old point is silently discarded,
416 * unreferencing its model if that point was bound to a named
417 * pair (hence, possibly destroying the model if this was the
418 * last reference).
420 void
421 adg_dim_set_ref1_explicit(AdgDim *dim, gdouble x, gdouble y)
423 AdgPoint *point = adg_point_new();
425 adg_point_set_pair_explicit(point, x, y);
426 adg_dim_set_ref1(dim, point);
428 adg_point_destroy(point);
432 * adg_dim_set_ref1_from_pair:
433 * @dim: an #AdgDim
434 * @ref1: the coordinates pair of the first reference point
436 * Convenient function to set the #AdgDim:ref1 property using a
437 * pair instead of explicit coordinates.
439 void
440 adg_dim_set_ref1_from_pair(AdgDim *dim, const AdgPair *ref1)
442 g_return_if_fail(ref1 != NULL);
444 adg_dim_set_ref1_explicit(dim, ref1->x, ref1->y);
448 * adg_dim_set_ref1_from_model:
449 * @dim: an #AdgDim
450 * @model: the source #AdgModel
451 * @ref1: a named pair in @model
453 * Binds #AdgDim:ref1 to the @ref1 named pair of @model. If @model
454 * is %NULL, the point will be unset. In any case, the old point
455 * is silently discarded, unreferencing its model if that point
456 * was bound to a named pair (hence, possibly destroying the model
457 * if this was the last reference).
459 * The assignment is lazy so @ref1 could be not be present in @model.
460 * Anyway, at the first access to this point an error will be raised
461 * if the named pair is still missing.
463 void
464 adg_dim_set_ref1_from_model(AdgDim *dim, AdgModel *model, const gchar *ref1)
466 AdgPoint *point = adg_point_new();
468 adg_point_set_pair_from_model(point, model, ref1);
469 adg_dim_set_ref1(dim, point);
471 adg_point_destroy(point);
475 * adg_dim_get_ref1:
476 * @dim: an #AdgDim
478 * Gets the #AdgDim:ref1 point. The returned point is internally owned
479 * and must not be freed or modified. Anyway, it is not const because
480 * adg_point_get_pair() must be able to modify the internal cache of
481 * the returned point.
483 * Returns: the first reference point
485 AdgPoint *
486 adg_dim_get_ref1(AdgDim *dim)
488 AdgDimPrivate *data;
490 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
492 data = dim->data;
494 return data->ref1;
498 * adg_dim_set_ref2:
499 * @dim: an #AdgDim
500 * @ref2: the new point to use as second reference
502 * Sets the #AdgDim:ref2 property to @ref2. The old point
503 * is silently discarded, unreferencing its model if that
504 * point was bound to a named pair (hence, possibly destroying
505 * the model if it was the last reference).
507 * @ref2 can be %NULL, in which case the point is destroyed.
509 void
510 adg_dim_set_ref2(AdgDim *dim, const AdgPoint *ref2)
512 g_return_if_fail(ADG_IS_DIM(dim));
513 g_object_set((GObject *) dim, "ref2", ref2, NULL);
517 * adg_dim_set_ref2_explicit:
518 * @dim: an #AdgDim
519 * @x: x coordinate of the second reference point
520 * @y: y coordinate of the second reference point
522 * Sets the #AdgDim:ref2 property to the (@x, @y) explicit
523 * coordinates. The old point is silently discarded,
524 * unreferencing its model if that point was bound to a named
525 * pair (hence, possibly destroying the model if this was the
526 * last reference).
528 void
529 adg_dim_set_ref2_explicit(AdgDim *dim, gdouble x, gdouble y)
531 AdgPoint *point = adg_point_new();
533 adg_point_set_pair_explicit(point, x, y);
534 adg_dim_set_ref2(dim, point);
536 adg_point_destroy(point);
540 * adg_dim_set_ref2_from_pair:
541 * @dim: an #AdgDim
542 * @ref2: the coordinates pair of the second reference point
544 * Convenient function to set the #AdgDim:ref2 property using a
545 * pair instead of explicit coordinates.
547 void
548 adg_dim_set_ref2_from_pair(AdgDim *dim, const AdgPair *ref2)
550 g_return_if_fail(ref2 != NULL);
552 adg_dim_set_ref2_explicit(dim, ref2->x, ref2->y);
556 * adg_dim_set_ref2_from_model:
557 * @dim: an #AdgDim
558 * @model: the source #AdgModel
559 * @ref2: a named pair in @model
561 * Binds #AdgDim:ref2 to the @ref2 named pair of @model. If @model
562 * is %NULL, the point will be unset. In any case, the old point
563 * is silently discarded, unreferencing its model if that point
564 * was bound to a named pair (hence, possibly destroying the model
565 * if this was the last reference).
567 * The assignment is lazy so @ref2 could be not be present in @model.
568 * Anyway, at the first access to this point an error will be raised
569 * if the named pair is still missing.
571 void
572 adg_dim_set_ref2_from_model(AdgDim *dim, AdgModel *model, const gchar *ref2)
574 AdgPoint *point = adg_point_new();
576 adg_point_set_pair_from_model(point, model, ref2);
577 adg_dim_set_ref2(dim, point);
579 adg_point_destroy(point);
583 * adg_dim_get_ref2:
584 * @dim: an #AdgDim
586 * Gets the #AdgDim:ref2 point. The returned point is internally owned
587 * and must not be freed or modified. Anyway, it is not const because
588 * adg_point_get_pair() must be able to modify the internal cache of
589 * the returned point.
591 * Returns: the second reference point
593 AdgPoint *
594 adg_dim_get_ref2(AdgDim *dim)
596 AdgDimPrivate *data;
598 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
600 data = dim->data;
602 return data->ref2;
606 * adg_dim_set_pos:
607 * @dim: an #AdgDim
608 * @pos: the new point to use as position
610 * Sets the #AdgDim:pos property to @pos. The old point
611 * is silently discarded, unreferencing its model if that
612 * point was bound to a named pair (hence, possibly destroying
613 * the model if it was the last reference).
615 * @pos can be %NULL, in which case the point is destroyed.
617 void
618 adg_dim_set_pos(AdgDim *dim, const AdgPoint *pos)
620 g_return_if_fail(ADG_IS_DIM(dim));
621 g_object_set((GObject *) dim, "pos", pos, NULL);
625 * adg_dim_set_pos_explicit:
626 * @dim: an #AdgDim
627 * @x: x coordinate of the position
628 * @y: y coordinate of the position
630 * Sets the #AdgDim:pos property to the (@x, @y) explicit
631 * coordinates. The old point is silently discarded,
632 * unreferencing its model if that point was bound to a named
633 * pair (hence, possibly destroying the model if this was the
634 * last reference).
636 void
637 adg_dim_set_pos_explicit(AdgDim *dim, gdouble x, gdouble y)
639 AdgPoint *point = adg_point_new();
641 adg_point_set_pair_explicit(point, x, y);
642 adg_dim_set_pos(dim, point);
644 adg_point_destroy(point);
648 * adg_dim_set_pos_from_pair:
649 * @dim: an #AdgDim
650 * @pos: the coordinates pair of the position point
652 * Convenient function to set the #AdgDim:pos property using a
653 * pair instead of explicit coordinates.
655 void
656 adg_dim_set_pos_from_pair(AdgDim *dim, const AdgPair *pos)
658 g_return_if_fail(pos != NULL);
660 adg_dim_set_pos_explicit(dim, pos->x, pos->y);
664 * adg_dim_set_pos_from_model:
665 * @dim: an #AdgDim
666 * @model: the source #AdgModel
667 * @pos: a named pair in @model
669 * Binds #AdgDim:pos to the @pos named pair of @model. If @model
670 * is %NULL, the point will be unset. In any case, the old point
671 * is silently discarded, unreferencing its model if that point
672 * was bound to a named pair (hence, possibly destroying the model
673 * if this was the last reference).
675 * The assignment is lazy so @pos could be not be present in @model.
676 * Anyway, at the first access to this point an error will be raised
677 * if the named pair is still missing.
679 void
680 adg_dim_set_pos_from_model(AdgDim *dim, AdgModel *model, const gchar *pos)
682 AdgPoint *point = adg_point_new();
684 adg_point_set_pair_from_model(point, model, pos);
685 adg_dim_set_pos(dim, point);
687 adg_point_destroy(point);
691 * adg_dim_get_pos:
692 * @dim: an #AdgDim
694 * Gets the #AdgDim:pos point. The returned point is internally owned
695 * and must not be freed or modified. Anyway, it is not const because
696 * adg_point_get_pair() must be able to modify the internal cache of
697 * the returned point.
699 * Returns: the position point
701 AdgPoint *
702 adg_dim_get_pos(AdgDim *dim)
704 AdgDimPrivate *data;
706 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
708 data = dim->data;
710 return data->pos;
714 * adg_dim_set_level:
715 * @dim: an #AdgDim
716 * @level: the new level
718 * Sets a new level for this dimension. The level is used to
719 * stack the quotes using a spacing value from dim_style
720 * (specified in global space).
722 void
723 adg_dim_set_level(AdgDim *dim, gdouble level)
725 AdgDimPrivate *data;
727 g_return_if_fail(ADG_IS_DIM(dim));
729 data = dim->data;
730 data->level = level;
732 g_object_notify((GObject *) dim, "level");
736 * adg_dim_get_level:
737 * @dim: an #AdgDim
739 * Gets the level of this dimension.
741 * Returns: the level value
743 gdouble
744 adg_dim_get_level(AdgDim *dim)
746 AdgDimPrivate *data;
748 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
750 data = dim->data;
752 return data->level;
756 * adg_dim_set_outside:
757 * @dim: an #AdgDim
758 * @outside: the new outside state
760 * Sets a new state for the #AdgDim:outside flag: check the property
761 * documentation for further details.
763 void
764 adg_dim_set_outside(AdgDim *dim, AdgThreeState outside)
766 g_return_if_fail(ADG_IS_DIM(dim));
768 if (_adg_set_outside(dim, outside))
769 g_object_notify((GObject *) dim, "outside");
773 * adg_dim_get_outside:
774 * @dim: an #AdgDim
776 * Gets the state of the #AdgDim:outside property: check the property
777 * documentation for further details.
779 * Returns: the current flag state
781 AdgThreeState
782 adg_dim_get_outside(AdgDim *dim)
784 AdgDimPrivate *data;
786 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
788 data = dim->data;
790 return data->outside;
794 * adg_dim_set_detached:
795 * @dim: an #AdgDim
796 * @detached: the new detached state
798 * Sets a new state for the #AdgDim:detached flag: check the property
799 * documentation for further details.
801 * This is used only by dimensions where detaching has meaning.
802 * In some cases, such as with #AdgRDim dimensions, this property is
803 * not used.
805 void
806 adg_dim_set_detached(AdgDim *dim, AdgThreeState detached)
808 g_return_if_fail(ADG_IS_DIM(dim));
810 if (_adg_set_detached(dim, detached))
811 g_object_notify((GObject *) dim, "detached");
815 * adg_dim_get_detached:
816 * @dim: an #AdgDim
818 * Gets the state of the #AdgDim:detached property: check the property
819 * documentation for further details.
821 * Returns: the current flag state
823 AdgThreeState
824 adg_dim_get_detached(AdgDim *dim)
826 AdgDimPrivate *data;
828 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
830 data = dim->data;
832 return data->detached;
836 * adg_dim_set_value:
837 * @dim: an #AdgDim
838 * @value: the value text
840 * Explicitely sets the text to use as value. If @value is %NULL or
841 * was never set, an automatic text is calculated using the format
842 * specified in the current #AdgDimStyle and getting its value by
843 * calling the default_value() virtual method.
845 * Inside the template string, the "<>" tag (or whatever specified
846 * by the #AdgDimStyle:number-tag property) is substituted with the
847 * string returned by the default_value() virtual method.
849 void
850 adg_dim_set_value(AdgDim *dim, const gchar *value)
852 g_return_if_fail(ADG_IS_DIM(dim));
854 if (_adg_set_value(dim, value))
855 g_object_notify((GObject *) dim, "value");
859 * adg_dim_get_value:
860 * @dim: an #AdgDim
862 * Gets the value text. The string is internally owned and
863 * must not be freed or modified.
865 * Returns: the value text
867 const gchar *
868 adg_dim_get_value(AdgDim *dim)
870 AdgDimPrivate *data;
872 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
874 data = dim->data;
876 return data->value;
880 * adg_dim_set_limits:
881 * @dim: an #AdgDim
882 * @min: the new minumum value
883 * @max: the new maximum value
885 * Shortcut to set both the limits at once.
887 void
888 adg_dim_set_limits(AdgDim *dim, const gchar *min, const gchar *max)
890 g_return_if_fail(ADG_IS_DIM(dim));
892 g_object_freeze_notify((GObject *) dim);
893 adg_dim_set_min(dim, min);
894 adg_dim_set_max(dim, max);
895 g_object_thaw_notify((GObject *) dim);
899 * adg_dim_set_min:
900 * @dim: an #AdgDim
901 * @min: the new minimum limit
903 * Sets the minimum value. Use %NULL as @min to disable it.
905 void
906 adg_dim_set_min(AdgDim *dim, const gchar *min)
908 g_return_if_fail(ADG_IS_DIM(dim));
910 if (_adg_set_min(dim, min))
911 g_object_notify((GObject *) dim, "min");
915 * adg_dim_get_min:
916 * @dim: an #AdgDim
918 * Gets the minimum value text or %NULL on minimum value disabled.
919 * The string is internally owned and must not be freed or modified.
921 * Returns: the mimimum value text
923 const gchar *
924 adg_dim_get_min(AdgDim *dim)
926 AdgDimPrivate *data;
928 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
930 data = dim->data;
932 return data->min;
936 * adg_dim_set_max:
937 * @dim: an #AdgDim
938 * @max: the new maximum value
940 * Sets the maximum value. Use %NULL as @max to disable it.
942 void
943 adg_dim_set_max(AdgDim *dim, const gchar *max)
945 g_return_if_fail(ADG_IS_DIM(dim));
947 if (_adg_set_max(dim, max))
948 g_object_notify((GObject *) dim, "max");
952 * adg_dim_get_max:
953 * @dim: an #AdgDim
955 * Gets the maximum value text or %NULL on maximum value disabled.
956 * The string is internally owned and must not be freed or modified.
958 * Returns: the maximum value text
960 const gchar *
961 adg_dim_get_max(AdgDim *dim)
963 AdgDimPrivate *data;
965 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
967 data = dim->data;
969 return data->max;
973 * adg_dim_get_quote:
974 * @dim: an #AdgDim
976 * <note><para>
977 * This function is only useful in new dimension implementations.
978 * </para></note>
980 * Gets the quote entity, if any. This function is valid only after
981 * the #AdgDim implementation of the arrange() virtual method has
982 * been called.
984 * Returns: the quote entity
986 AdgAlignment *
987 adg_dim_get_quote(AdgDim *dim)
989 AdgDimPrivate *data;
991 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
993 data = dim->data;
995 return data->quote.entity;
999 * adg_dim_quote_angle:
1000 * @dim: an #AdgDim
1001 * @angle: an angle (in radians)
1003 * <note><para>
1004 * This function is only useful in new dimension implementations.
1005 * </para></note>
1007 * Converts @angle accordling to the style of @dim. Any quote angle
1008 * should be validated by this method because every dimensioning
1009 * style has its own convention regardling the text rotation.
1011 * Returns: the angle to use (always in radians)
1013 gdouble
1014 adg_dim_quote_angle(AdgDim *dim, gdouble angle)
1016 AdgDimClass *klass;
1018 g_return_val_if_fail(ADG_IS_DIM(dim), angle);
1020 klass = ADG_DIM_GET_CLASS(dim);
1022 if (klass->quote_angle == NULL)
1023 return angle;
1025 return klass->quote_angle(angle);
1029 static void
1030 _adg_global_changed(AdgEntity *entity)
1032 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1034 if (_ADG_OLD_ENTITY_CLASS->global_changed)
1035 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
1037 if (data->quote.entity)
1038 adg_entity_global_changed((AdgEntity *) data->quote.entity);
1041 static void
1042 _adg_local_changed(AdgEntity *entity)
1044 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1046 if (_ADG_OLD_ENTITY_CLASS->local_changed)
1047 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
1049 if (data->quote.entity)
1050 adg_entity_local_changed((AdgEntity *) data->quote.entity);
1053 static void
1054 _adg_invalidate(AdgEntity *entity)
1056 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1058 if (data->quote.value) {
1059 g_object_unref(data->quote.value);
1060 data->quote.value = NULL;
1062 if (data->quote.entity)
1063 adg_entity_invalidate((AdgEntity *) data->quote.entity);
1064 if (data->ref1)
1065 adg_point_invalidate(data->ref1);
1066 if (data->ref2)
1067 adg_point_invalidate(data->ref2);
1068 if (data->pos)
1069 adg_point_invalidate(data->pos);
1071 if (_ADG_OLD_ENTITY_CLASS->invalidate)
1072 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
1075 static void
1076 _adg_arrange(AdgEntity *entity)
1078 AdgDim *dim;
1079 AdgDimPrivate *data;
1080 AdgEntity *quote_entity;
1081 AdgContainer *quote_container;
1082 AdgEntity *value_entity;
1083 AdgEntity *min_entity;
1084 AdgEntity *max_entity;
1085 const AdgPair *shift;
1086 AdgMatrix map;
1088 dim = (AdgDim *) entity;
1089 data = dim->data;
1091 /* Resolve the dim style */
1092 if (data->dim_style == NULL)
1093 data->dim_style = (AdgDimStyle *)
1094 adg_entity_style(entity, data->dim_dress);
1096 if (data->quote.entity == NULL)
1097 data->quote.entity = g_object_new(ADG_TYPE_ALIGNMENT,
1098 "local-method", ADG_MIX_NONE,
1099 "parent", dim, NULL);
1101 quote_entity = (AdgEntity *) data->quote.entity;
1102 quote_container = (AdgContainer *) data->quote.entity;
1104 if (data->quote.value == NULL) {
1105 AdgDimClass *klass;
1106 AdgDress dress;
1107 const gchar *tag;
1108 gchar *value;
1109 gchar *text;
1111 klass = ADG_DIM_GET_CLASS(dim);
1112 dress = adg_dim_style_get_value_dress(data->dim_style);
1113 tag = adg_dim_style_get_number_tag(data->dim_style);
1114 value = klass->default_value ? klass->default_value(dim) : NULL;
1116 data->quote.value = g_object_new(ADG_TYPE_TOY_TEXT,
1117 "local-method", ADG_MIX_PARENT,
1118 "font-dress", dress, NULL);
1119 adg_container_add(quote_container, (AdgEntity *) data->quote.value);
1121 if (data->value)
1122 text = adg_string_replace(data->value, tag, value);
1123 else
1124 text = g_strdup(value);
1126 g_free(value);
1128 adg_toy_text_set_label(data->quote.value, text);
1129 g_free(text);
1132 if (data->quote.min == NULL && data->min) {
1133 AdgDress dress = adg_dim_style_get_min_dress(data->dim_style);
1135 data->quote.min = g_object_new(ADG_TYPE_TOY_TEXT,
1136 "local-method", ADG_MIX_PARENT,
1137 "font-dress", dress, NULL);
1139 adg_container_add(quote_container, (AdgEntity *) data->quote.min);
1140 adg_toy_text_set_label(data->quote.min, data->min);
1143 if (data->quote.max == NULL && data->max) {
1144 AdgDress dress = adg_dim_style_get_max_dress(data->dim_style);
1146 data->quote.max = g_object_new(ADG_TYPE_TOY_TEXT,
1147 "local-method", ADG_MIX_PARENT,
1148 "font-dress", dress, NULL);
1150 adg_container_add(quote_container, (AdgEntity *) data->quote.max);
1151 adg_toy_text_set_label(data->quote.max, data->max);
1154 value_entity = (AdgEntity *) data->quote.value;
1155 min_entity = (AdgEntity *) data->quote.min;
1156 max_entity = (AdgEntity *) data->quote.max;
1157 shift = adg_dim_style_get_quote_shift(data->dim_style);
1159 adg_entity_set_global_map(quote_entity, adg_matrix_identity());
1160 adg_entity_global_changed(quote_entity);
1162 cairo_matrix_init_translate(&map, 0, shift->y);
1163 adg_entity_set_global_map(value_entity, &map);
1164 adg_entity_arrange(value_entity);
1166 /* Limit values (min and max) */
1167 if (min_entity || max_entity) {
1168 const AdgPair *limits_shift;
1169 gdouble spacing;
1170 AdgPair size;
1171 AdgMatrix unglobal;
1172 AdgPair org_min, org_max;
1174 limits_shift = adg_dim_style_get_limits_shift(data->dim_style);
1175 spacing = adg_dim_style_get_limits_spacing(data->dim_style);
1176 size = adg_entity_get_extents(value_entity)->size;
1177 adg_matrix_copy(&unglobal, adg_entity_get_global_matrix(entity));
1178 cairo_matrix_invert(&unglobal);
1179 cpml_vector_transform(&size, &unglobal);
1180 org_min.x = size.x + limits_shift->x;
1181 org_min.y = -size.y / 2 + limits_shift->y;
1182 org_max = org_min;
1184 if (min_entity && max_entity) {
1185 /* Prearrange the min entity to get its extents */
1186 adg_entity_arrange(min_entity);
1187 size = adg_entity_get_extents(min_entity)->size;
1188 cpml_vector_transform(&size, &unglobal);
1190 org_min.y += spacing / 2;
1191 org_max.y = org_min.y - size.y - spacing / 2;
1194 if (min_entity) {
1195 cairo_matrix_init_translate(&map, org_min.x, org_min.y);
1196 adg_entity_set_global_map(min_entity, &map);
1197 adg_entity_arrange(min_entity);
1200 if (max_entity) {
1201 cairo_matrix_init_translate(&map, org_max.x, org_max.y);
1202 adg_entity_set_global_map(max_entity, &map);
1203 adg_entity_arrange(max_entity);
1207 adg_entity_arrange(quote_entity);
1210 static gchar *
1211 _adg_default_value(AdgDim *dim)
1213 g_warning("AdgDim::default_value not implemented for `%s'",
1214 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1215 return g_strdup("undef");
1218 static gdouble
1219 _adg_quote_angle(gdouble angle)
1221 angle = cpml_angle(angle);
1223 if (angle > G_PI_4 * 4 / 3 || angle <= -G_PI_4 * 3)
1224 angle = cpml_angle(angle + G_PI);
1226 return angle;
1229 static gboolean
1230 _adg_set_outside(AdgDim *dim, AdgThreeState outside)
1232 AdgDimPrivate *data;
1234 g_return_val_if_fail(adg_is_enum_value(outside, ADG_TYPE_THREE_STATE),
1235 FALSE);
1237 data = dim->data;
1239 if (data->outside == outside)
1240 return FALSE;
1242 data->outside = outside;
1244 return TRUE;
1247 static gboolean
1248 _adg_set_detached(AdgDim *dim, AdgThreeState detached)
1250 AdgDimPrivate *data;
1252 g_return_val_if_fail(adg_is_enum_value(detached, ADG_TYPE_THREE_STATE),
1253 FALSE);
1255 data = dim->data;
1257 if (data->detached == detached)
1258 return FALSE;
1260 data->detached = detached;
1262 return TRUE;
1265 static gboolean
1266 _adg_set_value(AdgDim *dim, const gchar *value)
1268 AdgDimPrivate *data;
1270 data = dim->data;
1272 if (g_strcmp0(value, data->value) == 0)
1273 return FALSE;
1275 g_free(data->value);
1276 data->value = g_strdup(value);
1278 if (data->quote.value) {
1279 g_object_unref(data->quote.value);
1280 data->quote.value = NULL;
1283 return TRUE;
1286 static gboolean
1287 _adg_set_min(AdgDim *dim, const gchar *min)
1289 AdgDimPrivate *data = dim->data;
1291 if (g_strcmp0(min, data->min) == 0)
1292 return FALSE;
1294 g_free(data->min);
1295 data->min = g_strdup(min);
1297 if (data->quote.min) {
1298 g_object_unref(data->quote.min);
1299 data->quote.min = NULL;
1302 return TRUE;
1305 static gboolean
1306 _adg_set_max(AdgDim *dim, const gchar *max)
1308 AdgDimPrivate *data = dim->data;
1310 if (g_strcmp0(max, data->max) == 0)
1311 return FALSE;
1313 g_free(data->max);
1314 data->max = g_strdup(max);
1316 if (data->quote.max) {
1317 g_object_unref(data->quote.max);
1318 data->quote.max = NULL;
1321 return TRUE;