[AdgDim] Added "centered" property
[adg.git] / adg / adg-dim.c
blob799bdf6a543a31130f77ac715a97ac3ad9e31869
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-dress-builtins.h"
40 #include "adg-toy-text.h"
41 #include "adg-type-builtins.h"
42 #include "adg-intl.h"
44 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_dim_parent_class)
45 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_dim_parent_class)
48 enum {
49 PROP_0,
50 PROP_DIM_DRESS,
51 PROP_REF1,
52 PROP_REF2,
53 PROP_POS,
54 PROP_LEVEL,
55 PROP_OUTSIDE,
56 PROP_CENTERED,
57 PROP_VALUE,
58 PROP_MIN,
59 PROP_MAX
63 static void dispose (GObject *object);
64 static void finalize (GObject *object);
65 static void get_property (GObject *object,
66 guint param_id,
67 GValue *value,
68 GParamSpec *pspec);
69 static void set_property (GObject *object,
70 guint param_id,
71 const GValue *value,
72 GParamSpec *pspec);
73 static void global_changed (AdgEntity *entity);
74 static void local_changed (AdgEntity *entity);
75 static void invalidate (AdgEntity *entity);
76 static void arrange (AdgEntity *entity);
77 static gchar * default_value (AdgDim *dim);
78 static gdouble quote_angle (gdouble angle);
79 static gboolean set_dim_dress (AdgDim *dim,
80 AdgDress dress);
81 static gboolean set_value (AdgDim *dim,
82 const gchar *value);
83 static gboolean set_min (AdgDim *dim,
84 const gchar *min);
85 static gboolean set_max (AdgDim *dim,
86 const gchar *max);
89 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
92 static void
93 adg_dim_class_init(AdgDimClass *klass)
95 GObjectClass *gobject_class;
96 AdgEntityClass *entity_class;
97 GParamSpec *param;
99 gobject_class = (GObjectClass *) klass;
100 entity_class = (AdgEntityClass *) klass;
102 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
104 gobject_class->dispose = dispose;
105 gobject_class->finalize = finalize;
106 gobject_class->get_property = get_property;
107 gobject_class->set_property = set_property;
109 entity_class->global_changed = global_changed;
110 entity_class->local_changed = local_changed;
111 entity_class->invalidate = invalidate;
112 entity_class->arrange = arrange;
114 klass->quote_angle = quote_angle;
115 klass->default_value = default_value;
117 param = adg_param_spec_dress("dim-dress",
118 P_("Dimension Dress"),
119 P_("The dress to use for rendering this dimension"),
120 ADG_DRESS_DIMENSION,
121 G_PARAM_READWRITE);
122 g_object_class_install_property(gobject_class, PROP_DIM_DRESS, param);
124 param = g_param_spec_boxed("ref1",
125 P_("Reference 1"),
126 P_("First reference point of the dimension"),
127 ADG_TYPE_POINT,
128 G_PARAM_READWRITE);
129 g_object_class_install_property(gobject_class, PROP_REF1, param);
131 param = g_param_spec_boxed("ref2",
132 P_("Reference 2"),
133 P_("Second reference point of the dimension"),
134 ADG_TYPE_POINT,
135 G_PARAM_READWRITE);
136 g_object_class_install_property(gobject_class, PROP_REF2, param);
138 param = g_param_spec_boxed("pos",
139 P_("Position"),
140 P_("The reference position in local space of the quote: it will be combined with \"level\" to get the real quote position"),
141 ADG_TYPE_POINT,
142 G_PARAM_READWRITE);
143 g_object_class_install_property(gobject_class, PROP_POS, param);
145 param = g_param_spec_double("level",
146 P_("Level"),
147 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"),
148 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
149 G_PARAM_READWRITE);
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);
157 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
159 param = g_param_spec_enum("centered",
160 P_("Centered"),
161 P_("Where the quote must be positioned: in the middle of the base line (ADG_THREE_STATE_ON), near the pos point (ADG_THREE_STATE_OFF) or should be automatically deducted depending on the available space"),
162 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
163 G_PARAM_READWRITE);
164 g_object_class_install_property(gobject_class, PROP_CENTERED, param);
166 param = g_param_spec_string("value",
167 P_("Basic Value"),
168 P_("The theoretically exact value for this quote: set to NULL to automatically get the default value"),
169 NULL,
170 G_PARAM_READWRITE);
171 g_object_class_install_property(gobject_class, PROP_VALUE, param);
173 param = g_param_spec_string("value-min",
174 P_("Minimum Value or Low Tolerance"),
175 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
176 NULL,
177 G_PARAM_READWRITE);
178 g_object_class_install_property(gobject_class, PROP_MIN, param);
180 param = g_param_spec_string("value-max",
181 P_("Maximum Value or High Tolerance"),
182 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
183 NULL,
184 G_PARAM_READWRITE);
185 g_object_class_install_property(gobject_class, PROP_MAX, param);
188 static void
189 adg_dim_init(AdgDim *dim)
191 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
192 AdgDimPrivate);
194 data->dim_dress = ADG_DRESS_DIMENSION;
195 data->ref1 = NULL;
196 data->ref2 = NULL;
197 data->pos = NULL;
198 data->level = 1;
199 data->outside = ADG_THREE_STATE_UNKNOWN;
200 data->centered = ADG_THREE_STATE_UNKNOWN;
201 data->value = NULL;
202 data->min = NULL;
203 data->max = NULL;
205 dim->data = data;
208 static void
209 dispose(GObject *object)
211 AdgDimPrivate *data = ((AdgDim *) object)->data;
213 if (data->quote.entity != NULL) {
214 g_object_unref(data->quote.entity);
215 data->quote.entity = NULL;
217 if (data->ref1 != NULL) {
218 adg_point_destroy(data->ref1);
219 data->ref1 = NULL;
221 if (data->ref2 != NULL) {
222 adg_point_destroy(data->ref2);
223 data->ref2 = NULL;
225 if (data->pos != NULL) {
226 adg_point_destroy(data->pos);
227 data->pos = NULL;
230 if (PARENT_OBJECT_CLASS->dispose)
231 PARENT_OBJECT_CLASS->dispose(object);
234 static void
235 finalize(GObject *object)
237 AdgDimPrivate *data = ((AdgDim *) object)->data;
239 g_free(data->value);
240 g_free(data->min);
241 g_free(data->max);
243 if (PARENT_OBJECT_CLASS->finalize)
244 PARENT_OBJECT_CLASS->finalize(object);
247 static void
248 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
250 AdgDimPrivate *data = ((AdgDim *) object)->data;
252 switch (prop_id) {
253 case PROP_DIM_DRESS:
254 g_value_set_int(value, data->dim_dress);
255 break;
256 case PROP_REF1:
257 g_value_set_boxed(value, data->ref1);
258 break;
259 case PROP_REF2:
260 g_value_set_boxed(value, data->ref2);
261 break;
262 case PROP_POS:
263 g_value_set_boxed(value, data->pos);
264 break;
265 case PROP_LEVEL:
266 g_value_set_double(value, data->level);
267 break;
268 case PROP_OUTSIDE:
269 g_value_set_enum(value, data->outside);
270 break;
271 case PROP_CENTERED:
272 g_value_set_enum(value, data->centered);
273 break;
274 case PROP_VALUE:
275 g_value_set_string(value, data->value);
276 break;
277 case PROP_MIN:
278 g_value_set_string(value, data->min);
279 break;
280 case PROP_MAX:
281 g_value_set_string(value, data->max);
282 break;
283 default:
284 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
285 break;
289 static void
290 set_property(GObject *object, guint prop_id,
291 const GValue *value, GParamSpec *pspec)
293 AdgDim *dim;
294 AdgDimPrivate *data;
296 dim = (AdgDim *) object;
297 data = dim->data;
299 switch (prop_id) {
300 case PROP_DIM_DRESS:
301 set_dim_dress(dim, g_value_get_int(value));
302 break;
303 case PROP_REF1:
304 if (data->ref1 != NULL)
305 adg_point_destroy(data->ref1);
306 data->ref1 = g_value_dup_boxed(value);
307 break;
308 case PROP_REF2:
309 if (data->ref2 != NULL)
310 adg_point_destroy(data->ref2);
311 data->ref2 = g_value_dup_boxed(value);
312 break;
313 case PROP_POS:
314 if (data->pos != NULL)
315 adg_point_destroy(data->pos);
316 data->pos = g_value_dup_boxed(value);
317 break;
318 case PROP_LEVEL:
319 data->level = g_value_get_double(value);
320 break;
321 case PROP_OUTSIDE:
322 data->outside = g_value_get_enum(value);
323 break;
324 case PROP_CENTERED:
325 data->centered = g_value_get_enum(value);
326 break;
327 case PROP_VALUE:
328 set_value(dim, g_value_get_string(value));
329 break;
330 case PROP_MIN:
331 set_min(dim, g_value_get_string(value));
332 break;
333 case PROP_MAX:
334 set_max(dim, g_value_get_string(value));
335 break;
336 default:
337 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
338 break;
344 * adg_dim_set_dim_dress:
345 * @dim: an #AdgDim
346 * @dress: the new #AdgDress to use
348 * Sets a new dimension dress to @dim. The new dress must be
349 * related to the original dress for this property: you cannot
350 * set a dress used for line styles to a dress managing fonts.
352 * The check is done by calling adg_dress_are_related() with
353 * @dress and the previous dress as arguments. Check out its
354 * documentation for details on what is a related dress.
356 void
357 adg_dim_set_dim_dress(AdgDim *dim, AdgDress dress)
359 g_return_if_fail(ADG_IS_DIM(dim));
361 if (set_dim_dress(dim, dress))
362 g_object_notify((GObject *) dim, "dim-dress");
366 * adg_dim_get_dim_dress:
367 * @dim: an #AdgDim
369 * Gets the dimension dress to be used in rendering @dim.
371 * Returns: the current dimension dress
373 AdgDress
374 adg_dim_get_dim_dress(AdgDim *dim)
376 AdgDimPrivate *data;
378 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_DRESS_UNDEFINED);
380 data = dim->data;
382 return data->dim_dress;
386 * adg_dim_set_ref:
387 * @dim: an #AdgDim
388 * @ref1: the ref1 coordinates
389 * @ref2: the ref2 coordinates
391 * Sets the #AdgDim:ref1 and #AdgDim:ref2 reference points
392 * using @ref1 and @ref2 pairs. @ref1 or @ref2 could be
393 * %NULL (but not both), in which case only the non-null
394 * reference point is changed.
396 void
397 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
399 GObject *object;
400 AdgDimPrivate *data;
402 g_return_if_fail(ADG_IS_DIM(dim));
403 g_return_if_fail(ref1 != NULL || ref2 != NULL);
405 data = dim->data;
406 object = (GObject *) dim;
408 g_object_freeze_notify(object);
410 if (ref1 != NULL) {
411 if (data->ref1 == NULL)
412 data->ref1 = adg_point_new();
414 adg_point_set(data->ref1, ref1);
416 g_object_notify(object, "ref1");
419 if (ref2 != NULL) {
420 if (data->ref2 == NULL)
421 data->ref2 = adg_point_new();
423 adg_point_set(data->ref2, ref2);
425 g_object_notify(object, "ref2");
428 g_object_thaw_notify(object);
432 * adg_dim_set_ref_explicit:
433 * @dim: an #AdgDim
434 * @ref1_x: x coordinate of ref1
435 * @ref1_y: y coordinate of ref1
436 * @ref2_x: x coordinate of ref2
437 * @ref2_y: y coordinate of ref2
439 * Works in the same way as adg_dim_set_ref() but using
440 * explicit coordinates instead of #AdgPair args. The
441 * notable difference is that, by using gdouble values,
442 * you can't set only a single reference point.
444 void
445 adg_dim_set_ref_explicit(AdgDim *dim,
446 gdouble ref1_x, gdouble ref1_y,
447 gdouble ref2_x, gdouble ref2_y)
449 AdgPair ref1, ref2;
451 ref1.x = ref1_x;
452 ref1.y = ref1_y;
453 ref2.x = ref2_x;
454 ref2.y = ref2_y;
456 adg_dim_set_ref(dim, &ref1, &ref2);
460 * adg_dim_set_ref_from_model:
461 * @dim: an #AdgDim
462 * @model: the source #AdgModel
463 * @ref1: name of the pair in @model to use as ref1
464 * @ref2: name of the pair in @model to use as ref2
466 * Sets #AdgDim:ref1 and #AdgDim:ref2 properties by linking
467 * them to the @ref1 and @ref2 named pairs in @model. @ref1
468 * or @ref2 could be %NULL (but not both), in which case
469 * only the non-null reference point is changed.
471 * Using this function twice you can also link the reference
472 * points to named pairs taken from different models:
474 * |[
475 * adg_dim_set_ref_from_model(dim, model1, ref1, NULL);
476 * adg_dim_set_ref_from_model(dim, model2, NULL, ref2);
477 * ]|
479 void
480 adg_dim_set_ref_from_model(AdgDim *dim, AdgModel *model,
481 const gchar *ref1, const gchar *ref2)
483 GObject *object;
484 AdgDimPrivate *data;
486 g_return_if_fail(ADG_IS_DIM(dim));
487 g_return_if_fail(ADG_IS_MODEL(model));
488 g_return_if_fail(ref1 != NULL || ref2 != NULL);
490 object = (GObject *) dim;
491 data = dim->data;
493 g_object_freeze_notify(object);
495 if (ref1 != NULL) {
496 if (data->ref1 == NULL)
497 data->ref1 = adg_point_new();
499 adg_point_set_from_model(data->ref1, model, ref1);
501 g_object_notify(object, "ref1");
504 if (ref2 != NULL) {
505 if (data->ref2 == NULL)
506 data->ref2 = adg_point_new();
508 adg_point_set_from_model(data->ref2, model, ref2);
510 g_object_notify(object, "ref2");
513 g_object_thaw_notify(object);
517 * adg_dim_get_ref1:
518 * @dim: an #AdgDim
520 * Gets the ref1 coordinates. The returned pair is internally owned
521 * and must not be freed or modified.
523 * Returns: the ref1 coordinates
525 const AdgPair *
526 adg_dim_get_ref1(AdgDim *dim)
528 AdgDimPrivate *data;
530 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
532 data = dim->data;
534 return adg_point_pair(data->ref1);
538 * adg_dim_get_ref2:
539 * @dim: an #AdgDim
541 * Gets the ref2 coordinates. The returned pair is internally owned
542 * and must not be freed or modified.
544 * Returns: the ref2 coordinates
546 const AdgPair *
547 adg_dim_get_ref2(AdgDim *dim)
549 AdgDimPrivate *data;
551 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
553 data = dim->data;
555 return adg_point_pair(data->ref2);
559 * adg_dim_set_pos:
560 * @dim: an #AdgDim
561 * @pos: the pos coordinates
563 * Sets a new #AdgDim:pos position.
565 void
566 adg_dim_set_pos(AdgDim *dim, const AdgPair *pos)
568 AdgDimPrivate *data;
570 g_return_if_fail(ADG_IS_DIM(dim));
571 g_return_if_fail(pos != NULL);
573 data = dim->data;
575 if (data->pos == NULL)
576 data->pos = adg_point_new();
578 adg_point_set(data->pos, pos);
580 g_object_notify((GObject *) dim, "pos");
584 * adg_dim_set_pos_explicit:
585 * @dim: an #AdgDim
586 * @pos_x: x coordinate of pos
587 * @pos_y: y coordinate of pos
589 * Shortcut to set #AdgDim:pos using explicit coordinates.
591 void
592 adg_dim_set_pos_explicit(AdgDim *dim, gdouble x, gdouble y)
594 AdgPair pos;
596 pos.x = x;
597 pos.y = y;
599 adg_dim_set_pos(dim, &pos);
603 * adg_dim_set_pos_from_model:
604 * @dim: an #AdgDim
605 * @model: the source #AdgModel
606 * @ref1: name of the pair in @model to use as pos
608 * Sets #AdgDim:pos by linking it to the @pos named pair
609 * in @model.
611 void
612 adg_dim_set_pos_from_model(AdgDim *dim, AdgModel *model, const gchar *pos)
614 AdgDimPrivate *data;
616 g_return_if_fail(ADG_IS_DIM(dim));
617 g_return_if_fail(ADG_IS_MODEL(model));
618 g_return_if_fail(pos != NULL);
620 data = dim->data;
622 if (data->pos == NULL)
623 data->pos = adg_point_new();
625 adg_point_set_from_model(data->pos, model, pos);
627 g_object_notify((GObject *) dim, "pos");
631 * adg_dim_get_pos:
632 * @dim: an #AdgDim
634 * Gets the position coordinates. The returned pair is internally owned
635 * and must not be freed or modified.
637 * Returns: the pos coordinates
639 const AdgPair *
640 adg_dim_get_pos(AdgDim *dim)
642 AdgDimPrivate *data;
644 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
646 data = dim->data;
648 return adg_point_pair(data->pos);
652 * adg_dim_set_level:
653 * @dim: an #AdgDim
654 * @level: the new level
656 * Sets a new level for this dimension. The level is used to
657 * stack the quotes using a spacing value from dim_style
658 * (specified in global space).
660 void
661 adg_dim_set_level(AdgDim *dim, gdouble level)
663 AdgDimPrivate *data;
665 g_return_if_fail(ADG_IS_DIM(dim));
667 data = dim->data;
668 data->level = level;
670 g_object_notify((GObject *) dim, "level");
674 * adg_dim_get_level:
675 * @dim: an #AdgDim
677 * Gets the level of this dimension.
679 * Returns: the level value
681 gdouble
682 adg_dim_get_level(AdgDim *dim)
684 AdgDimPrivate *data;
686 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
688 data = dim->data;
690 return data->level;
694 * adg_dim_set_outside:
695 * @dim: an #AdgDim
696 * @outside: the new outside state
698 * Sets a new state for the #AdgDim:outside flag: check the property
699 * documentation for further details.
701 void
702 adg_dim_set_outside(AdgDim *dim, AdgThreeState outside)
704 AdgDimPrivate *data;
706 g_return_if_fail(ADG_IS_DIM(dim));
708 data = dim->data;
709 data->outside = outside;
711 g_object_notify((GObject *) dim, "outside");
715 * adg_dim_get_outside:
716 * @dim: an #AdgDim
718 * Gets the state of the #AdgDim:outside property: check the property
719 * documentation for further details.
721 * Returns: the current flag state
723 AdgThreeState
724 adg_dim_get_outside(AdgDim *dim)
726 AdgDimPrivate *data;
728 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
730 data = dim->data;
732 return data->outside;
736 * adg_dim_set_centered:
737 * @dim: an #AdgDim
738 * @centered: the new centered state
740 * Sets a new state for the #AdgDim:centered flag: check the property
741 * documentation for further details.
743 * This is used by dimensions where the centering of the quote is
744 * applicable. In the other cases (such as with #AdgRDim dimensions)
745 * the property is not used.
747 void
748 adg_dim_set_centered(AdgDim *dim, AdgThreeState centered)
750 AdgDimPrivate *data;
752 g_return_if_fail(ADG_IS_DIM(dim));
754 data = dim->data;
755 data->centered = centered;
757 g_object_notify((GObject *) dim, "centered");
761 * adg_dim_get_centered:
762 * @dim: an #AdgDim
764 * Gets the state of the #AdgDim:centered property: check the property
765 * documentation for further details.
767 * Returns: the current flag state
769 AdgThreeState
770 adg_dim_get_centered(AdgDim *dim)
772 AdgDimPrivate *data;
774 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
776 data = dim->data;
778 return data->centered;
782 * adg_dim_set_value:
783 * @dim: an #AdgDim
784 * @value: the value text
786 * Explicitely sets the text to use as value. If @value is %NULL or
787 * was never set, an automatic text is calculated using the format
788 * specified in the current #AdgDimStyle and getting its value by
789 * calling the default_value() virtual method.
791 void
792 adg_dim_set_value(AdgDim *dim, const gchar *value)
794 g_return_if_fail(ADG_IS_DIM(dim));
796 if (set_value(dim, value))
797 g_object_notify((GObject *) dim, "value");
801 * adg_dim_get_value:
802 * @dim: an #AdgDim
804 * Gets the value text. The string is internally owned and
805 * must not be freed or modified.
807 * Returns: the value text
809 const gchar *
810 adg_dim_get_value(AdgDim *dim)
812 AdgDimPrivate *data;
814 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
816 data = dim->data;
818 return data->value;
822 * adg_dim_set_limits:
823 * @dim: an #AdgDim
824 * @min: the new minumum value
825 * @max: the new maximum value
827 * Shortcut to set both the limits at once.
829 void
830 adg_dim_set_limits(AdgDim *dim, const gchar *min, const gchar *max)
832 g_return_if_fail(ADG_IS_DIM(dim));
834 g_object_freeze_notify((GObject *) dim);
835 adg_dim_set_min(dim, min);
836 adg_dim_set_max(dim, max);
837 g_object_thaw_notify((GObject *) dim);
841 * adg_dim_set_min:
842 * @dim: an #AdgDim
843 * @min: the new minimum limit
845 * Sets the minimum value. Use %NULL as @min to disable it.
847 void
848 adg_dim_set_min(AdgDim *dim, const gchar *min)
850 g_return_if_fail(ADG_IS_DIM(dim));
852 if (set_min(dim, min))
853 g_object_notify((GObject *) dim, "value-min");
857 * adg_dim_get_min:
858 * @dim: an #AdgDim
860 * Gets the minimum value text or %NULL on minimum value disabled.
861 * The string is internally owned and must not be freed or modified.
863 * Returns: the mimimum value text
865 const gchar *
866 adg_dim_get_min(AdgDim *dim)
868 AdgDimPrivate *data;
870 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
872 data = dim->data;
874 return data->min;
878 * adg_dim_set_max:
879 * @dim: an #AdgDim
880 * @max: the new maximum value
882 * Sets the maximum value. Use %NULL as @max to disable it.
884 void
885 adg_dim_set_max(AdgDim *dim, const gchar *max)
887 g_return_if_fail(ADG_IS_DIM(dim));
889 if (set_max(dim, max))
890 g_object_notify((GObject *) dim, "value-max");
894 * adg_dim_get_max:
895 * @dim: an #AdgDim
897 * Gets the maximum value text or %NULL on maximum value disabled.
898 * The string is internally owned and must not be freed or modified.
900 * Returns: the maximum value text
902 const gchar *
903 adg_dim_get_max(AdgDim *dim)
905 AdgDimPrivate *data;
907 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
909 data = dim->data;
911 return data->max;
915 * adg_dim_get_quote:
916 * @dim: an #AdgDim
918 * <note><para>
919 * This function is only useful in new dimension implementations.
920 * </para></note>
922 * Gets the quote entity, if any. This function is valid only after
923 * the #AdgDim implementation of the arrange() virtual method has
924 * been called.
926 * Returns: the quote entity
928 AdgAlignment *
929 adg_dim_get_quote(AdgDim *dim)
931 AdgDimPrivate *data;
933 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
935 data = dim->data;
937 return data->quote.entity;
941 * adg_dim_quote_angle:
942 * @dim: an #AdgDim
943 * @angle: an angle (in radians)
945 * <note><para>
946 * This function is only useful in new dimension implementations.
947 * </para></note>
949 * Converts @angle accordling to the style of @dim. Any quote angle
950 * should be validated by this method because every dimensioning
951 * style has its own convention regardling the text rotation.
953 * Returns: the angle to use (always in radians)
955 gdouble
956 adg_dim_quote_angle(AdgDim *dim, gdouble angle)
958 AdgDimClass *klass;
960 g_return_val_if_fail(ADG_IS_DIM(dim), angle);
962 klass = ADG_DIM_GET_CLASS(dim);
964 if (klass->quote_angle == NULL)
965 return angle;
967 return klass->quote_angle(angle);
971 static void
972 global_changed(AdgEntity *entity)
974 AdgDimPrivate *data = ((AdgDim *) entity)->data;
976 if (PARENT_ENTITY_CLASS->global_changed)
977 PARENT_ENTITY_CLASS->global_changed(entity);
979 if (data->quote.entity != NULL)
980 adg_entity_global_changed((AdgEntity *) data->quote.entity);
983 static void
984 local_changed(AdgEntity *entity)
986 AdgDimPrivate *data = ((AdgDim *) entity)->data;
988 if (PARENT_ENTITY_CLASS->local_changed)
989 PARENT_ENTITY_CLASS->local_changed(entity);
991 if (data->quote.entity != NULL)
992 adg_entity_local_changed((AdgEntity *) data->quote.entity);
995 static void
996 invalidate(AdgEntity *entity)
998 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1000 if (PARENT_ENTITY_CLASS->invalidate)
1001 PARENT_ENTITY_CLASS->invalidate(entity);
1003 if (data->quote.entity != NULL)
1004 adg_entity_invalidate((AdgEntity *) data->quote.entity);
1006 adg_point_invalidate(data->ref1);
1007 adg_point_invalidate(data->ref2);
1008 adg_point_invalidate(data->pos);
1011 static void
1012 arrange(AdgEntity *entity)
1014 AdgDim *dim;
1015 AdgDimPrivate *data;
1016 AdgEntity *quote_entity;
1017 AdgContainer *quote_container;
1018 AdgEntity *value_entity;
1019 AdgEntity *min_entity;
1020 AdgEntity *max_entity;
1021 const AdgPair *shift;
1022 AdgMatrix map;
1024 dim = (AdgDim *) entity;
1025 data = dim->data;
1027 /* Resolve the dim style */
1028 if (data->dim_style == NULL)
1029 data->dim_style = (AdgDimStyle *)
1030 adg_entity_style(entity, data->dim_dress);
1032 if (data->quote.entity == NULL) {
1033 AdgPair factor = { 0.5, 0 };
1034 data->quote.entity = g_object_new(ADG_TYPE_ALIGNMENT,
1035 "local-method", ADG_MIX_NONE,
1036 "factor", &factor,
1037 "parent", dim, NULL);
1040 quote_entity = (AdgEntity *) data->quote.entity;
1041 quote_container = (AdgContainer *) data->quote.entity;
1043 if (data->quote.value == NULL) {
1044 AdgDress dress = adg_dim_style_get_value_dress(data->dim_style);
1046 data->quote.value = g_object_new(ADG_TYPE_TOY_TEXT,
1047 "local-method", ADG_MIX_PARENT,
1048 "font-dress", dress, NULL);
1050 adg_container_add(quote_container, (AdgEntity *) data->quote.value);
1052 if (data->value) {
1053 adg_toy_text_set_label(data->quote.value, data->value);
1054 } else {
1055 AdgDimClass *klass = ADG_DIM_GET_CLASS(dim);
1057 if (klass->default_value) {
1058 /* Automatically generate the value text */
1059 gchar *text = klass->default_value(dim);
1060 adg_toy_text_set_label(data->quote.value, text);
1061 g_free(text);
1066 if (data->quote.min == NULL && data->min != NULL) {
1067 AdgDress dress = adg_dim_style_get_min_dress(data->dim_style);
1069 data->quote.min = g_object_new(ADG_TYPE_TOY_TEXT,
1070 "local-method", ADG_MIX_PARENT,
1071 "font-dress", dress, NULL);
1073 adg_container_add(quote_container, (AdgEntity *) data->quote.min);
1074 adg_toy_text_set_label(data->quote.min, data->min);
1077 if (data->quote.max == NULL && data->max != NULL) {
1078 AdgDress dress = adg_dim_style_get_max_dress(data->dim_style);
1080 data->quote.max = g_object_new(ADG_TYPE_TOY_TEXT,
1081 "local-method", ADG_MIX_PARENT,
1082 "font-dress", dress, NULL);
1084 adg_container_add(quote_container, (AdgEntity *) data->quote.max);
1085 adg_toy_text_set_label(data->quote.max, data->max);
1088 value_entity = (AdgEntity *) data->quote.value;
1089 min_entity = (AdgEntity *) data->quote.min;
1090 max_entity = (AdgEntity *) data->quote.max;
1091 shift = adg_dim_style_get_quote_shift(data->dim_style);
1093 adg_entity_set_global_map(quote_entity, adg_matrix_identity());
1094 adg_entity_global_changed(quote_entity);
1096 cairo_matrix_init_translate(&map, shift->x, shift->y);
1097 adg_entity_set_global_map(value_entity, &map);
1098 adg_entity_arrange(value_entity);
1100 /* Limit values (min and max) */
1101 if (min_entity != NULL || max_entity != NULL) {
1102 const CpmlExtents *extents = adg_entity_get_extents(value_entity);
1103 //CpmlExtents min_extents = { 0 };
1104 //CpmlExtents max_extents = { 0 };
1105 const AdgPair *limits_shift = adg_dim_style_get_limits_shift(data->dim_style);
1107 #if 0
1108 if (min_entity != NULL)
1109 cpml_extents_copy(&min_extents, adg_entity_get_extents(min_entity));
1111 if (max_entity != NULL)
1112 cpml_extents_copy(&max_extents, adg_entity_get_extents(max_entity));
1114 if (min_entity != NULL && max_entity != NULL)
1115 spacing = adg_dim_style_get_limits_spacing(data->dim_style);
1117 cairo_matrix_init_translate(&map,
1118 extents->size.x +
1119 shift->x + limit_shift->x,
1120 (spacing + min_extents.size.y +
1121 max_extents.size.y - extents->size.y) / 2 +
1122 shift->y + limit_shift->y);
1123 #endif
1124 cairo_matrix_init_translate(&map, extents->size.x + limits_shift->x,
1125 -extents->size.y / 2 + limits_shift->y);
1127 if (min_entity != NULL) {
1128 adg_entity_set_global_map(min_entity, &map);
1129 adg_entity_arrange(min_entity);
1130 extents = adg_entity_get_extents(min_entity);
1131 map.y0 -= extents->size.y +
1132 adg_dim_style_get_limits_spacing(data->dim_style);
1135 if (max_entity != NULL) {
1136 adg_entity_set_global_map(max_entity, &map);
1137 adg_entity_arrange(max_entity);
1141 adg_entity_arrange(quote_entity);
1144 static gchar *
1145 default_value(AdgDim *dim)
1147 g_warning("AdgDim::default_value not implemented for `%s'",
1148 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1149 return g_strdup("undef");
1152 static gdouble
1153 quote_angle(gdouble angle)
1155 angle = cpml_angle(angle);
1157 if (angle > G_PI_4 * 4 / 3 || angle <= -G_PI_4 * 3)
1158 angle = cpml_angle(angle + G_PI);
1160 return angle;
1163 static gboolean
1164 set_dim_dress(AdgDim *dim, AdgDress dress)
1166 AdgDimPrivate *data = dim->data;
1168 if (adg_dress_set(&data->dim_dress, dress)) {
1169 data->dim_style = NULL;
1170 return TRUE;
1173 return FALSE;
1176 static gboolean
1177 set_value(AdgDim *dim, const gchar *value)
1179 AdgDimPrivate *data;
1181 data = dim->data;
1183 if (adg_strcmp(value, data->value) == 0)
1184 return FALSE;
1186 g_free(data->value);
1187 data->value = g_strdup(value);
1189 if (data->quote.value != NULL) {
1190 g_object_unref(data->quote.value);
1191 data->quote.value = NULL;
1194 return TRUE;
1197 static gboolean
1198 set_min(AdgDim *dim, const gchar *min)
1200 AdgDimPrivate *data = dim->data;
1202 if (adg_strcmp(min, data->min) == 0)
1203 return FALSE;
1205 g_free(data->min);
1206 data->min = g_strdup(min);
1208 if (data->quote.min != NULL) {
1209 g_object_unref(data->quote.min);
1210 data->quote.min = NULL;
1213 return TRUE;
1216 static gboolean
1217 set_max(AdgDim *dim, const gchar *max)
1219 AdgDimPrivate *data = dim->data;
1221 if (adg_strcmp(max, data->max) == 0)
1222 return FALSE;
1224 g_free(data->max);
1225 data->max = g_strdup(max);
1227 if (data->quote.max != NULL) {
1228 g_object_unref(data->quote.max);
1229 data->quote.max = NULL;
1232 return TRUE;