adg: add adg_dim_get_text() helper method
[adg.git] / src / adg / adg-dim.c
blobb992693c4b8e7beb05e040d2d24165389f381132
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 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.
27 * Since: 1.0
28 **/
30 /**
31 * AdgDim:
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
36 * Since: 1.0
37 **/
39 /**
40 * AdgDimClass:
41 * @quote_angle: virtual method that must return the rotation angle of the
42 * quote (in radians) of the current dimension.
43 * @default_value: abstract virtual method that must return the default value
44 * (as a newly allocated string to be freed with g_free()) of
45 * the current dimension.
47 * The default implementation of @quote_angle flips the quote if it should be
48 * rotated in the bottom right half of the circle, that is:
50 * <informalexample><programlisting>
51 * if 1/3 PI <= angle <= -3/4 PI; then angle += PI.
52 * </programlisting></informalexample>
54 * The virtual method @default_value instead *must* be implemented by any
55 * derived class. The default implementation will trigger an error if called.
57 * Since: 1.0
58 **/
61 #include "adg-internal.h"
62 #include "adg-text-internal.h"
64 #include "adg-container.h"
65 #include "adg-alignment.h"
66 #include "adg-model.h"
67 #include "adg-trail.h"
68 #include "adg-point.h"
69 #include "adg-marker.h"
70 #include "adg-dim-style.h"
71 #include "adg-dress.h"
72 #include "adg-param-dress.h"
74 #include "adg-dim.h"
75 #include "adg-dim-private.h"
79 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_dim_parent_class)
80 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_dim_parent_class)
83 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY)
85 enum {
86 PROP_0,
87 PROP_DIM_DRESS,
88 PROP_REF1,
89 PROP_REF2,
90 PROP_POS,
91 PROP_LEVEL,
92 PROP_OUTSIDE,
93 PROP_DETACHED,
94 PROP_VALUE,
95 PROP_MIN,
96 PROP_MAX
100 static void _adg_dispose (GObject *object);
101 static void _adg_finalize (GObject *object);
102 static void _adg_get_property (GObject *object,
103 guint param_id,
104 GValue *value,
105 GParamSpec *pspec);
106 static void _adg_set_property (GObject *object,
107 guint param_id,
108 const GValue *value,
109 GParamSpec *pspec);
110 static void _adg_global_changed (AdgEntity *entity);
111 static void _adg_local_changed (AdgEntity *entity);
112 static void _adg_invalidate (AdgEntity *entity);
113 static void _adg_arrange (AdgEntity *entity);
114 static gchar * _adg_default_value (AdgDim *dim);
115 static gdouble _adg_quote_angle (gdouble angle);
116 static gboolean _adg_set_outside (AdgDim *dim,
117 AdgThreeState outside);
118 static gboolean _adg_set_detached (AdgDim *dim,
119 AdgThreeState detached);
120 static gboolean _adg_set_value (AdgDim *dim,
121 const gchar *value);
122 static gboolean _adg_set_min (AdgDim *dim,
123 const gchar *min);
124 static gboolean _adg_set_max (AdgDim *dim,
125 const gchar *max);
126 static gboolean _adg_replace (const GMatchInfo *match_info,
127 GString *result,
128 gpointer user_data);
131 static void
132 adg_dim_class_init(AdgDimClass *klass)
134 GObjectClass *gobject_class;
135 AdgEntityClass *entity_class;
136 GParamSpec *param;
138 gobject_class = (GObjectClass *) klass;
139 entity_class = (AdgEntityClass *) klass;
141 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
143 gobject_class->dispose = _adg_dispose;
144 gobject_class->finalize = _adg_finalize;
145 gobject_class->get_property = _adg_get_property;
146 gobject_class->set_property = _adg_set_property;
148 entity_class->global_changed = _adg_global_changed;
149 entity_class->local_changed = _adg_local_changed;
150 entity_class->invalidate = _adg_invalidate;
151 entity_class->arrange = _adg_arrange;
153 klass->quote_angle = _adg_quote_angle;
154 klass->default_value = _adg_default_value;
156 param = adg_param_spec_dress("dim-dress",
157 P_("Dimension Dress"),
158 P_("The dress to use for rendering this dimension"),
159 ADG_DRESS_DIMENSION,
160 G_PARAM_READWRITE);
161 g_object_class_install_property(gobject_class, PROP_DIM_DRESS, param);
163 param = g_param_spec_boxed("ref1",
164 P_("First Reference"),
165 P_("First reference point of the dimension"),
166 ADG_TYPE_POINT,
167 G_PARAM_READWRITE);
168 g_object_class_install_property(gobject_class, PROP_REF1, param);
170 param = g_param_spec_boxed("ref2",
171 P_("Second Reference"),
172 P_("Second reference point of the dimension"),
173 ADG_TYPE_POINT,
174 G_PARAM_READWRITE);
175 g_object_class_install_property(gobject_class, PROP_REF2, param);
177 param = g_param_spec_boxed("pos",
178 P_("Position"),
179 P_("The reference position of the quote: it will be combined with \"level\" to get the real quote position"),
180 ADG_TYPE_POINT,
181 G_PARAM_READWRITE);
182 g_object_class_install_property(gobject_class, PROP_POS, param);
184 param = g_param_spec_double("level",
185 P_("Level"),
186 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"),
187 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
188 G_PARAM_READWRITE);
189 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
191 param = g_param_spec_enum("outside",
192 P_("Outside"),
193 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"),
194 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
195 G_PARAM_READWRITE);
196 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
198 param = g_param_spec_enum("detached",
199 P_("Detached Quote"),
200 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"),
201 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
202 G_PARAM_READWRITE);
203 g_object_class_install_property(gobject_class, PROP_DETACHED, param);
205 param = g_param_spec_string("value",
206 P_("Value Template"),
207 P_("The template string to be used for generating the set value of the quote"),
208 NULL,
209 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
210 g_object_class_install_property(gobject_class, PROP_VALUE, param);
212 param = g_param_spec_string("min",
213 P_("Minimum Value or Low Tolerance"),
214 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
215 NULL,
216 G_PARAM_READWRITE);
217 g_object_class_install_property(gobject_class, PROP_MIN, param);
219 param = g_param_spec_string("max",
220 P_("Maximum Value or High Tolerance"),
221 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
222 NULL,
223 G_PARAM_READWRITE);
224 g_object_class_install_property(gobject_class, PROP_MAX, param);
227 static void
228 adg_dim_init(AdgDim *dim)
230 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
231 AdgDimPrivate);
233 data->dim_dress = ADG_DRESS_DIMENSION;
234 data->ref1 = NULL;
235 data->ref2 = NULL;
236 data->pos = NULL;
237 data->level = 1;
238 data->outside = ADG_THREE_STATE_UNKNOWN;
239 data->detached = ADG_THREE_STATE_UNKNOWN;
240 data->min = NULL;
241 data->max = NULL;
243 #if 0
244 /* This one is G_PARAM_CONSTRUCT, so set by property inizialization */
245 data->value = NULL
246 #endif
248 dim->data = data;
251 static void
252 _adg_dispose(GObject *object)
254 AdgEntity *entity;
255 AdgDimPrivate *data;
257 entity = (AdgEntity *) object;
258 data = ((AdgDim *) object)->data;
260 if (data->quote.entity) {
261 g_object_unref(data->quote.entity);
262 data->quote.entity = NULL;
265 if (data->ref1)
266 data->ref1 = adg_entity_point(entity, data->ref1, NULL);
268 if (data->ref2)
269 data->ref2 = adg_entity_point(entity, data->ref2, NULL);
271 if (data->pos)
272 data->pos = adg_entity_point(entity, data->pos, NULL);
274 if (_ADG_OLD_OBJECT_CLASS->dispose)
275 _ADG_OLD_OBJECT_CLASS->dispose(object);
278 static void
279 _adg_finalize(GObject *object)
281 AdgDimPrivate *data = ((AdgDim *) object)->data;
283 g_free(data->value);
284 g_free(data->min);
285 g_free(data->max);
287 if (_ADG_OLD_OBJECT_CLASS->finalize)
288 _ADG_OLD_OBJECT_CLASS->finalize(object);
291 static void
292 _adg_get_property(GObject *object, guint prop_id,
293 GValue *value, GParamSpec *pspec)
295 AdgDimPrivate *data = ((AdgDim *) object)->data;
297 switch (prop_id) {
298 case PROP_DIM_DRESS:
299 g_value_set_enum(value, data->dim_dress);
300 break;
301 case PROP_REF1:
302 g_value_set_boxed(value, data->ref1);
303 break;
304 case PROP_REF2:
305 g_value_set_boxed(value, data->ref2);
306 break;
307 case PROP_POS:
308 g_value_set_boxed(value, data->pos);
309 break;
310 case PROP_LEVEL:
311 g_value_set_double(value, data->level);
312 break;
313 case PROP_OUTSIDE:
314 g_value_set_enum(value, data->outside);
315 break;
316 case PROP_DETACHED:
317 g_value_set_enum(value, data->detached);
318 break;
319 case PROP_VALUE:
320 g_value_set_string(value, data->value);
321 break;
322 case PROP_MIN:
323 g_value_set_string(value, data->min);
324 break;
325 case PROP_MAX:
326 g_value_set_string(value, data->max);
327 break;
328 default:
329 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
330 break;
334 static void
335 _adg_set_property(GObject *object, guint prop_id,
336 const GValue *value, GParamSpec *pspec)
338 AdgEntity *entity;
339 AdgDim *dim;
340 AdgDimPrivate *data;
342 entity = (AdgEntity *) object;
343 dim = (AdgDim *) object;
344 data = dim->data;
346 switch (prop_id) {
347 case PROP_DIM_DRESS:
348 data->dim_dress = g_value_get_enum(value);
349 break;
350 case PROP_REF1:
351 data->ref1 = adg_entity_point(entity, data->ref1,
352 g_value_get_boxed(value));
353 break;
354 case PROP_REF2:
355 data->ref2 = adg_entity_point(entity, data->ref2,
356 g_value_get_boxed(value));
357 break;
358 case PROP_POS:
359 data->pos = adg_entity_point(entity, data->pos,
360 g_value_get_boxed(value));
361 break;
362 case PROP_LEVEL:
363 data->level = g_value_get_double(value);
364 break;
365 case PROP_OUTSIDE:
366 _adg_set_outside(dim, g_value_get_enum(value));
367 break;
368 case PROP_DETACHED:
369 _adg_set_detached(dim, g_value_get_enum(value));
370 break;
371 case PROP_VALUE:
372 _adg_set_value(dim, g_value_get_string(value));
373 break;
374 case PROP_MIN:
375 _adg_set_min(dim, g_value_get_string(value));
376 break;
377 case PROP_MAX:
378 _adg_set_max(dim, g_value_get_string(value));
379 break;
380 default:
381 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
382 break;
388 * adg_dim_set_dim_dress:
389 * @dim: an #AdgDim
390 * @dress: the new #AdgDress to use
392 * Sets a new dimension dress to @dim. The new dress must be
393 * related to the original dress for this property: you cannot
394 * set a dress used for line styles to a dress managing fonts.
396 * The check is done by calling adg_dress_are_related() with
397 * @dress and the previous dress as arguments. Check out its
398 * documentation for details on what is a related dress.
400 * Since: 1.0
402 void
403 adg_dim_set_dim_dress(AdgDim *dim, AdgDress dress)
405 g_return_if_fail(ADG_IS_DIM(dim));
406 g_object_set(dim, "dim-dress", dress, NULL);
410 * adg_dim_get_dim_dress:
411 * @dim: an #AdgDim
413 * Gets the dimension dress to be used in rendering @dim.
415 * Returns: (transfer none): the current dimension dress.
417 * Since: 1.0
419 AdgDress
420 adg_dim_get_dim_dress(AdgDim *dim)
422 AdgDimPrivate *data;
424 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_DRESS_UNDEFINED);
426 data = dim->data;
428 return data->dim_dress;
432 * adg_dim_set_ref1:
433 * @dim: an #AdgDim
434 * @ref1: the new point to use as first reference
436 * Sets the #AdgDim:ref1 property to @ref1. The old point
437 * is silently discarded, unreferencing its model if that
438 * point was bound to a named pair (hence, possibly destroying
439 * the model if this was the last reference).
441 * @ref1 can be <constant>NULL</constant>, in which case the
442 * point is destroyed.
444 * Since: 1.0
446 void
447 adg_dim_set_ref1(AdgDim *dim, const AdgPoint *ref1)
449 g_return_if_fail(ADG_IS_DIM(dim));
450 g_object_set(dim, "ref1", ref1, NULL);
454 * adg_dim_set_ref1_explicit:
455 * @dim: an #AdgDim
456 * @x: x coordinate of the first reference point
457 * @y: y coordinate of the first reference point
459 * Sets the #AdgDim:ref1 property to the (@x, @y) explicit
460 * coordinates. The old point is silently discarded,
461 * unreferencing its model if that point was bound to a named
462 * pair (hence, possibly destroying the model if this was the
463 * last reference).
465 * Since: 1.0
467 void
468 adg_dim_set_ref1_explicit(AdgDim *dim, gdouble x, gdouble y)
470 AdgPoint *point = adg_point_new();
472 adg_point_set_pair_explicit(point, x, y);
473 adg_dim_set_ref1(dim, point);
475 adg_point_destroy(point);
479 * adg_dim_set_ref1_from_pair:
480 * @dim: an #AdgDim
481 * @ref1: the coordinates pair of the first reference point
483 * Convenient function to set the #AdgDim:ref1 property using a
484 * pair instead of explicit coordinates.
486 * Since: 1.0
488 void
489 adg_dim_set_ref1_from_pair(AdgDim *dim, const CpmlPair *ref1)
491 g_return_if_fail(ref1 != NULL);
493 adg_dim_set_ref1_explicit(dim, ref1->x, ref1->y);
497 * adg_dim_set_ref1_from_model:
498 * @dim: an #AdgDim
499 * @model: the source #AdgModel
500 * @ref1: a named pair in @model
502 * Binds #AdgDim:ref1 to the @ref1 named pair of @model. If @model
503 * is <constant>NULL</constant>, the point will be unset. In any case,
504 * the old point is silently discarded, unreferencing its model
505 * if that point was bound to a named pair (hence, possibly destroying
506 * the model if this was the last reference).
508 * The assignment is lazy so @ref1 could be not be present in @model.
509 * Anyway, at the first access to this point an error will be raised
510 * if the named pair is still missing.
512 * Since: 1.0
514 void
515 adg_dim_set_ref1_from_model(AdgDim *dim, AdgModel *model, const gchar *ref1)
517 AdgPoint *point = adg_point_new();
519 adg_point_set_pair_from_model(point, model, ref1);
520 adg_dim_set_ref1(dim, point);
522 adg_point_destroy(point);
526 * adg_dim_get_ref1:
527 * @dim: an #AdgDim
529 * Gets the #AdgDim:ref1 point of @dim.
531 * The returned point is internally owned and must not be freed
532 * or modified. Anyway it is not const because a call to
533 * adg_point_update() with the returned value must be able to
534 * modify the internal cache.
536 * Returns: (transfer none): the first reference point.
538 * Since: 1.0
540 AdgPoint *
541 adg_dim_get_ref1(AdgDim *dim)
543 AdgDimPrivate *data;
545 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
547 data = dim->data;
549 return data->ref1;
553 * adg_dim_set_ref2:
554 * @dim: an #AdgDim
555 * @ref2: the new point to use as second reference
557 * Sets the #AdgDim:ref2 property to @ref2. The old point
558 * is silently discarded, unreferencing its model if that
559 * point was bound to a named pair (hence, possibly destroying
560 * the model if it was the last reference).
562 * @ref2 can be <constant>NULL</constant>, in which case
563 * the point is destroyed.
565 * Since: 1.0
567 void
568 adg_dim_set_ref2(AdgDim *dim, const AdgPoint *ref2)
570 g_return_if_fail(ADG_IS_DIM(dim));
571 g_object_set(dim, "ref2", ref2, NULL);
575 * adg_dim_set_ref2_explicit:
576 * @dim: an #AdgDim
577 * @x: x coordinate of the second reference point
578 * @y: y coordinate of the second reference point
580 * Sets the #AdgDim:ref2 property to the (@x, @y) explicit
581 * coordinates. The old point is silently discarded,
582 * unreferencing its model if that point was bound to a named
583 * pair (hence, possibly destroying the model if this was the
584 * last reference).
586 * Since: 1.0
588 void
589 adg_dim_set_ref2_explicit(AdgDim *dim, gdouble x, gdouble y)
591 AdgPoint *point = adg_point_new();
593 adg_point_set_pair_explicit(point, x, y);
594 adg_dim_set_ref2(dim, point);
596 adg_point_destroy(point);
600 * adg_dim_set_ref2_from_pair:
601 * @dim: an #AdgDim
602 * @ref2: the coordinates pair of the second reference point
604 * Convenient function to set the #AdgDim:ref2 property using a
605 * pair instead of explicit coordinates.
607 * Since: 1.0
609 void
610 adg_dim_set_ref2_from_pair(AdgDim *dim, const CpmlPair *ref2)
612 g_return_if_fail(ref2 != NULL);
614 adg_dim_set_ref2_explicit(dim, ref2->x, ref2->y);
618 * adg_dim_set_ref2_from_model:
619 * @dim: an #AdgDim
620 * @model: the source #AdgModel
621 * @ref2: a named pair in @model
623 * Binds #AdgDim:ref2 to the @ref2 named pair of @model. If @model
624 * is <constant>NULL</constant>, the point will be unset. In any
625 * case, the old point is silently discarded, unreferencing its
626 * model if that point was bound to a named pair (hence, possibly
627 * destroying the model if this was the last reference).
629 * The assignment is lazy so @ref2 could be not be present in @model.
630 * Anyway, at the first access to this point an error will be raised
631 * if the named pair is still missing.
633 * Since: 1.0
635 void
636 adg_dim_set_ref2_from_model(AdgDim *dim, AdgModel *model, const gchar *ref2)
638 AdgPoint *point = adg_point_new();
640 adg_point_set_pair_from_model(point, model, ref2);
641 adg_dim_set_ref2(dim, point);
643 adg_point_destroy(point);
647 * adg_dim_get_ref2:
648 * @dim: an #AdgDim
650 * Gets the #AdgDim:ref2 point of @dim.
652 * The returned point is internally owned and must not be freed
653 * or modified. Anyway it is not const because a call to
654 * adg_point_update() with the returned value must be able to
655 * modify the internal cache.
657 * Returns: (transfer none): the second reference point.
659 * Since: 1.0
661 AdgPoint *
662 adg_dim_get_ref2(AdgDim *dim)
664 AdgDimPrivate *data;
666 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
668 data = dim->data;
670 return data->ref2;
674 * adg_dim_set_pos:
675 * @dim: an #AdgDim
676 * @pos: the new point to use as position
678 * Sets the #AdgDim:pos property of @dim to @pos. The old point
679 * is silently discarded, unreferencing its model if that
680 * point was bound to a named pair (hence, possibly destroying
681 * the model if it was the last reference).
683 * @pos can be <constant>NULL</constant>, in which case the
684 * point is destroyed.
686 * Since: 1.0
688 void
689 adg_dim_set_pos(AdgDim *dim, const AdgPoint *pos)
691 g_return_if_fail(ADG_IS_DIM(dim));
692 g_object_set(dim, "pos", pos, NULL);
696 * adg_dim_set_pos_explicit:
697 * @dim: an #AdgDim
698 * @x: x coordinate of the position
699 * @y: y coordinate of the position
701 * Sets the #AdgDim:pos property to the (@x, @y) explicit
702 * coordinates. The old point is silently discarded,
703 * unreferencing its model if that point was bound to a named
704 * pair (hence, possibly destroying the model if this was the
705 * last reference).
707 * Since: 1.0
709 void
710 adg_dim_set_pos_explicit(AdgDim *dim, gdouble x, gdouble y)
712 AdgPoint *point = adg_point_new();
714 adg_point_set_pair_explicit(point, x, y);
715 adg_dim_set_pos(dim, point);
717 adg_point_destroy(point);
721 * adg_dim_set_pos_from_pair:
722 * @dim: an #AdgDim
723 * @pos: the coordinates pair of the position point
725 * Convenient function to set the #AdgDim:pos property using a
726 * pair instead of explicit coordinates.
728 * Since: 1.0
730 void
731 adg_dim_set_pos_from_pair(AdgDim *dim, const CpmlPair *pos)
733 g_return_if_fail(pos != NULL);
735 adg_dim_set_pos_explicit(dim, pos->x, pos->y);
739 * adg_dim_set_pos_from_model:
740 * @dim: an #AdgDim
741 * @model: the source #AdgModel
742 * @pos: a named pair in @model
744 * Binds #AdgDim:pos to the @pos named pair of @model. If @model
745 * is <constant>NULL</constant>, the point will be unset. In any
746 * case, the old point is silently discarded, unreferencing its
747 * model if that point was bound to a named pair (hence,
748 * possibly destroying the model if this was the last reference).
750 * The assignment is lazy so @pos could be not be present in @model.
751 * Anyway, at the first access to this point an error will be raised
752 * if the named pair is still missing.
754 * Since: 1.0
756 void
757 adg_dim_set_pos_from_model(AdgDim *dim, AdgModel *model, const gchar *pos)
759 AdgPoint *point = adg_point_new();
761 adg_point_set_pair_from_model(point, model, pos);
762 adg_dim_set_pos(dim, point);
764 adg_point_destroy(point);
768 * adg_dim_get_pos:
769 * @dim: an #AdgDim
771 * Gets the #AdgDim:pos point of @dim.
773 * The returned point is internally owned and must not be freed
774 * or modified. Anyway it is not const because a call to
775 * adg_point_update() with the returned value must be able to
776 * modify the internal cache.
778 * Returns: (transfer none): the position point.
780 * Since: 1.0
782 AdgPoint *
783 adg_dim_get_pos(AdgDim *dim)
785 AdgDimPrivate *data;
787 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
789 data = dim->data;
791 return data->pos;
795 * adg_dim_set_level:
796 * @dim: an #AdgDim
797 * @level: the new level
799 * Sets a new level for this dimension. The level is used to
800 * stack the quotes using a spacing value from dim_style
801 * (specified in global space).
803 * Since: 1.0
805 void
806 adg_dim_set_level(AdgDim *dim, gdouble level)
808 AdgDimPrivate *data;
810 g_return_if_fail(ADG_IS_DIM(dim));
812 data = dim->data;
813 data->level = level;
815 g_object_notify((GObject *) dim, "level");
819 * adg_dim_get_level:
820 * @dim: an #AdgDim
822 * Gets the level of this dimension.
824 * Returns: the level value.
826 * Since: 1.0
828 gdouble
829 adg_dim_get_level(AdgDim *dim)
831 AdgDimPrivate *data;
833 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
835 data = dim->data;
837 return data->level;
841 * adg_dim_set_outside:
842 * @dim: an #AdgDim
843 * @outside: the new outside state
845 * Sets a new state for the #AdgDim:outside flag: check the property
846 * documentation for further details.
848 * Since: 1.0
850 void
851 adg_dim_set_outside(AdgDim *dim, AdgThreeState outside)
853 g_return_if_fail(ADG_IS_DIM(dim));
855 if (_adg_set_outside(dim, outside))
856 g_object_notify((GObject *) dim, "outside");
860 * adg_dim_get_outside:
861 * @dim: an #AdgDim
863 * Gets the state of the #AdgDim:outside property: check the property
864 * documentation for further details.
866 * Returns: the current flag state.
868 * Since: 1.0
870 AdgThreeState
871 adg_dim_get_outside(AdgDim *dim)
873 AdgDimPrivate *data;
875 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
877 data = dim->data;
879 return data->outside;
883 * adg_dim_set_detached:
884 * @dim: an #AdgDim
885 * @detached: the new detached state
887 * Sets a new state for the #AdgDim:detached flag: check the property
888 * documentation for further details.
890 * This is used only by dimensions where detaching has meaning.
891 * In some cases, such as with #AdgRDim dimensions, this property is
892 * not used.
894 * Since: 1.0
896 void
897 adg_dim_set_detached(AdgDim *dim, AdgThreeState detached)
899 g_return_if_fail(ADG_IS_DIM(dim));
901 if (_adg_set_detached(dim, detached))
902 g_object_notify((GObject *) dim, "detached");
906 * adg_dim_get_detached:
907 * @dim: an #AdgDim
909 * Gets the state of the #AdgDim:detached property: check the property
910 * documentation for further details.
912 * Returns: the current flag state.
914 * Since: 1.0
916 AdgThreeState
917 adg_dim_get_detached(AdgDim *dim)
919 AdgDimPrivate *data;
921 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
923 data = dim->data;
925 return data->detached;
929 * adg_dim_set_value:
930 * @dim: an #AdgDim
931 * @value: (allow-none): the value text
933 * Explicitely sets the text to use as value. If @value
934 * is <constant>NULL</constant> or was never set, an automatic
935 * text is calculated using the format specified in the current
936 * #AdgDimStyle and getting its value by calling
937 * the <function>default_value</function> virtual method.
939 * Inside the template string, the "<>" tag (or whatever specified
940 * by the #AdgDimStyle:number-tag property) is substituted with the
941 * string returned by <function>default_value</function>.
943 * Since: 1.0
945 void
946 adg_dim_set_value(AdgDim *dim, const gchar *value)
948 g_return_if_fail(ADG_IS_DIM(dim));
950 if (_adg_set_value(dim, value))
951 g_object_notify((GObject *) dim, "value");
955 * adg_dim_get_value:
956 * @dim: an #AdgDim
958 * Gets the value text. The string is internally owned and
959 * must not be freed or modified.
961 * Returns: (transfer none): the value text.
963 * Since: 1.0
965 const gchar *
966 adg_dim_get_value(AdgDim *dim)
968 AdgDimPrivate *data;
970 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
972 data = dim->data;
974 return data->value;
978 * adg_dim_get_text:
979 * @dim: an #AdgDim
980 * @value: the raw value of the quote
982 * Gets the final text to show as nominal value into the quote. The string is
983 * the same returned by adg_dim_get_value() with the tag properly expanded.
985 * The string substituted to the tag is formatted according to the
986 * #AdgDimStyle:number-format and #AdgDimStyle:number-arguments properties.
987 * See the #AdgDimStyle documentation for further details.
989 * Returns: (transfer full): the final text of the quote.
991 * Since: 1.0
993 gchar *
994 adg_dim_get_text(AdgDim *dim, gdouble value)
996 AdgDimStyle *dim_style;
997 const gchar *format;
998 const gchar *arguments;
999 AdgDimReplaceData replace_data;
1000 GRegex *regex;
1001 gchar *result;
1003 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1005 dim_style = _ADG_GET_DIM_STYLE(dim);
1006 if (dim_style == NULL) {
1007 dim_style = (AdgDimStyle *) adg_entity_style((AdgEntity *) dim,
1008 adg_dim_get_dim_dress(dim));
1011 format = adg_dim_style_get_number_format(dim_style);
1012 if (format == NULL) {
1013 return NULL;
1016 arguments = adg_dim_style_get_number_arguments(dim_style);
1017 if (arguments == NULL) {
1018 return g_strdup(format);
1021 replace_data.dim_style = dim_style;
1022 replace_data.value = value;
1023 replace_data.argument = arguments;
1025 regex = g_regex_new("(?<!%)%.*[eEfFgG]", G_REGEX_UNGREEDY, 0, NULL);
1026 result = g_regex_replace_eval(regex, format, -1, 0, 0,
1027 _adg_replace, &replace_data,
1028 NULL);
1029 g_regex_unref(regex);
1031 return result;
1035 * adg_dim_set_limits:
1036 * @dim: an #AdgDim
1037 * @min: (allow-none): the new minumum value
1038 * @max: (allow-none): the new maximum value
1040 * Shortcut to set both the limits at once.
1042 * Since: 1.0
1044 void
1045 adg_dim_set_limits(AdgDim *dim, const gchar *min, const gchar *max)
1047 g_return_if_fail(ADG_IS_DIM(dim));
1049 g_object_freeze_notify((GObject *) dim);
1050 adg_dim_set_min(dim, min);
1051 adg_dim_set_max(dim, max);
1052 g_object_thaw_notify((GObject *) dim);
1056 * adg_dim_set_min:
1057 * @dim: an #AdgDim
1058 * @min: (allow-none): the new minimum limit
1060 * Sets the minimum value. Use <constant>NULL</constant>
1061 * as @min to disable it.
1063 * Since: 1.0
1065 void
1066 adg_dim_set_min(AdgDim *dim, const gchar *min)
1068 g_return_if_fail(ADG_IS_DIM(dim));
1070 if (_adg_set_min(dim, min))
1071 g_object_notify((GObject *) dim, "min");
1075 * adg_dim_get_min:
1076 * @dim: an #AdgDim
1078 * Gets the minimum value text or <constant>NULL</constant>
1079 * on minimum value disabled.
1081 * The string is internally owned and must not be freed or modified.
1083 * Returns: (transfer none): the mimimum value text.
1085 * Since: 1.0
1087 const gchar *
1088 adg_dim_get_min(AdgDim *dim)
1090 AdgDimPrivate *data;
1092 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1094 data = dim->data;
1096 return data->min;
1100 * adg_dim_set_max:
1101 * @dim: an #AdgDim
1102 * @max: (allow-none): the new maximum value
1104 * Sets the maximum value. Use <constant>NULL</constant>
1105 * as @max to disable it.
1107 * Since: 1.0
1109 void
1110 adg_dim_set_max(AdgDim *dim, const gchar *max)
1112 g_return_if_fail(ADG_IS_DIM(dim));
1114 if (_adg_set_max(dim, max))
1115 g_object_notify((GObject *) dim, "max");
1119 * adg_dim_get_max:
1120 * @dim: an #AdgDim
1122 * Gets the maximum value text or <constant>NULL</constant>
1123 * on maximum value disabled.
1125 * The string is internally owned and must not be freed or modified.
1127 * Returns: (transfer none): the maximum value text.
1129 * Since: 1.0
1131 const gchar *
1132 adg_dim_get_max(AdgDim *dim)
1134 AdgDimPrivate *data;
1136 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1138 data = dim->data;
1140 return data->max;
1144 * adg_dim_get_quote:
1145 * @dim: an #AdgDim
1147 * Gets the quote entity, if any. This function is valid only after
1148 * the #AdgDim implementation of the arrange() virtual method has
1149 * been called.
1151 * The returned entity is owned by @dim and should not be
1152 * modified or freed.
1154 * <note><para>
1155 * This function is only useful in new dimension implementations.
1156 * </para></note>
1158 * Returns: (transfer none): the quote entity.
1160 * Since: 1.0
1162 AdgAlignment *
1163 adg_dim_get_quote(AdgDim *dim)
1165 AdgDimPrivate *data;
1167 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1169 data = dim->data;
1171 return data->quote.entity;
1175 * adg_dim_quote_angle:
1176 * @dim: an #AdgDim
1177 * @angle: an angle (in radians)
1179 * <note><para>
1180 * This function is only useful in new dimension implementations.
1181 * </para></note>
1183 * Converts @angle accordling to the style of @dim. Any quote angle
1184 * should be validated by this method because every dimensioning
1185 * style has its own convention regardling the text rotation.
1187 * Returns: the angle to use (always in radians).
1189 * Since: 1.0
1191 gdouble
1192 adg_dim_quote_angle(AdgDim *dim, gdouble angle)
1194 AdgDimClass *klass;
1196 g_return_val_if_fail(ADG_IS_DIM(dim), angle);
1198 klass = ADG_DIM_GET_CLASS(dim);
1200 if (klass->quote_angle == NULL)
1201 return angle;
1203 return klass->quote_angle(angle);
1207 static void
1208 _adg_global_changed(AdgEntity *entity)
1210 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1212 if (_ADG_OLD_ENTITY_CLASS->global_changed)
1213 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
1215 if (data->quote.entity)
1216 adg_entity_global_changed((AdgEntity *) data->quote.entity);
1219 static void
1220 _adg_local_changed(AdgEntity *entity)
1222 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1224 if (_ADG_OLD_ENTITY_CLASS->local_changed)
1225 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
1227 if (data->quote.entity)
1228 adg_entity_local_changed((AdgEntity *) data->quote.entity);
1231 static void
1232 _adg_invalidate(AdgEntity *entity)
1234 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1236 if (data->quote.value) {
1237 g_object_unref(data->quote.value);
1238 data->quote.value = NULL;
1240 if (data->quote.entity)
1241 adg_entity_invalidate((AdgEntity *) data->quote.entity);
1242 if (data->ref1)
1243 adg_point_invalidate(data->ref1);
1244 if (data->ref2)
1245 adg_point_invalidate(data->ref2);
1246 if (data->pos)
1247 adg_point_invalidate(data->pos);
1249 if (_ADG_OLD_ENTITY_CLASS->invalidate)
1250 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
1253 static void
1254 _adg_arrange(AdgEntity *entity)
1256 AdgDim *dim;
1257 AdgDimPrivate *data;
1258 AdgEntity *quote_entity;
1259 AdgContainer *quote_container;
1260 AdgEntity *value_entity;
1261 AdgEntity *min_entity;
1262 AdgEntity *max_entity;
1263 const CpmlPair *shift;
1264 cairo_matrix_t map;
1266 dim = (AdgDim *) entity;
1267 data = dim->data;
1269 /* Resolve the dim style */
1270 if (data->dim_style == NULL)
1271 data->dim_style = (AdgDimStyle *)
1272 adg_entity_style(entity, data->dim_dress);
1274 if (data->quote.entity == NULL)
1275 data->quote.entity = g_object_new(ADG_TYPE_ALIGNMENT,
1276 "local-mix", ADG_MIX_NONE,
1277 "parent", dim, NULL);
1279 quote_entity = (AdgEntity *) data->quote.entity;
1280 quote_container = (AdgContainer *) data->quote.entity;
1282 if (data->quote.value == NULL) {
1283 AdgDimClass *klass;
1284 AdgDress dress;
1285 const gchar *tag;
1286 gchar *value;
1287 gchar *text;
1289 klass = ADG_DIM_GET_CLASS(dim);
1290 dress = adg_dim_style_get_value_dress(data->dim_style);
1291 tag = adg_dim_style_get_number_tag(data->dim_style);
1292 value = klass->default_value ? klass->default_value(dim) : NULL;
1294 data->quote.value = g_object_new(ADG_TYPE_BEST_TEXT,
1295 "local-mix", ADG_MIX_PARENT,
1296 "font-dress", dress, NULL);
1297 adg_container_add(quote_container, (AdgEntity *) data->quote.value);
1299 if (data->value)
1300 text = adg_string_replace(data->value, tag, value);
1301 else
1302 text = g_strdup(value);
1304 g_free(value);
1306 adg_textual_set_text(data->quote.value, text);
1307 g_free(text);
1310 if (data->quote.min == NULL && data->min != NULL) {
1311 AdgDress dress = adg_dim_style_get_min_dress(data->dim_style);
1313 data->quote.min = g_object_new(ADG_TYPE_BEST_TEXT,
1314 "local-mix", ADG_MIX_PARENT,
1315 "font-dress", dress, NULL);
1317 adg_container_add(quote_container, (AdgEntity *) data->quote.min);
1318 adg_textual_set_text(data->quote.min, data->min);
1321 if (data->quote.max == NULL && data->max != NULL) {
1322 AdgDress dress = adg_dim_style_get_max_dress(data->dim_style);
1324 data->quote.max = g_object_new(ADG_TYPE_BEST_TEXT,
1325 "local-mix", ADG_MIX_PARENT,
1326 "font-dress", dress, NULL);
1328 adg_container_add(quote_container, (AdgEntity *) data->quote.max);
1329 adg_textual_set_text(data->quote.max, data->max);
1332 value_entity = (AdgEntity *) data->quote.value;
1333 min_entity = (AdgEntity *) data->quote.min;
1334 max_entity = (AdgEntity *) data->quote.max;
1335 shift = adg_dim_style_get_quote_shift(data->dim_style);
1337 adg_entity_set_global_map(quote_entity, adg_matrix_identity());
1338 adg_entity_global_changed(quote_entity);
1340 cairo_matrix_init_translate(&map, 0, shift->y);
1341 adg_entity_set_global_map(value_entity, &map);
1342 adg_entity_arrange(value_entity);
1344 /* Limit values (min and max) */
1345 if (min_entity != NULL || max_entity != NULL) {
1346 const CpmlPair *limits_shift;
1347 gdouble spacing;
1348 CpmlPair size;
1349 CpmlPair org_min, org_max;
1351 limits_shift = adg_dim_style_get_limits_shift(data->dim_style);
1352 spacing = adg_dim_style_get_limits_spacing(data->dim_style);
1353 size = adg_entity_get_extents(value_entity)->size;
1354 org_min.x = size.x + limits_shift->x;
1355 org_min.y = -size.y / 2 + limits_shift->y;
1356 org_max = org_min;
1358 if (min_entity && max_entity) {
1359 /* Prearrange the min entity to get its extents */
1360 adg_entity_arrange(min_entity);
1361 size = adg_entity_get_extents(min_entity)->size;
1363 org_min.y += spacing / 2;
1364 org_max.y = org_min.y - size.y - spacing / 2;
1367 if (min_entity != NULL) {
1368 cairo_matrix_init_translate(&map, org_min.x, org_min.y);
1369 adg_entity_set_global_map(min_entity, &map);
1370 adg_entity_arrange(min_entity);
1373 if (max_entity != NULL) {
1374 cairo_matrix_init_translate(&map, org_max.x, org_max.y);
1375 adg_entity_set_global_map(max_entity, &map);
1376 adg_entity_arrange(max_entity);
1381 static gchar *
1382 _adg_default_value(AdgDim *dim)
1384 g_warning(_("AdgDim::default_value not implemented for '%s'"),
1385 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1386 return g_strdup("undef");
1389 static gdouble
1390 _adg_quote_angle(gdouble angle)
1392 angle = cpml_angle(angle);
1394 if (angle > G_PI_4 * 4 / 3 || angle <= -G_PI_4 * 3)
1395 angle = cpml_angle(angle + G_PI);
1397 return angle;
1400 static gboolean
1401 _adg_set_outside(AdgDim *dim, AdgThreeState outside)
1403 AdgDimPrivate *data;
1405 g_return_val_if_fail(adg_is_enum_value(outside, ADG_TYPE_THREE_STATE),
1406 FALSE);
1408 data = dim->data;
1410 if (data->outside == outside)
1411 return FALSE;
1413 data->outside = outside;
1415 return TRUE;
1418 static gboolean
1419 _adg_set_detached(AdgDim *dim, AdgThreeState detached)
1421 AdgDimPrivate *data;
1423 g_return_val_if_fail(adg_is_enum_value(detached, ADG_TYPE_THREE_STATE),
1424 FALSE);
1426 data = dim->data;
1428 if (data->detached == detached)
1429 return FALSE;
1431 data->detached = detached;
1433 return TRUE;
1436 static gboolean
1437 _adg_set_value(AdgDim *dim, const gchar *value)
1439 AdgDimPrivate *data;
1441 data = dim->data;
1443 if (g_strcmp0(value, data->value) == 0)
1444 return FALSE;
1446 g_free(data->value);
1447 data->value = g_strdup(value);
1449 if (data->quote.value) {
1450 g_object_unref(data->quote.value);
1451 data->quote.value = NULL;
1454 return TRUE;
1457 static gboolean
1458 _adg_set_min(AdgDim *dim, const gchar *min)
1460 AdgDimPrivate *data = dim->data;
1462 if (g_strcmp0(min, data->min) == 0)
1463 return FALSE;
1465 g_free(data->min);
1466 data->min = g_strdup(min);
1468 if (data->quote.min) {
1469 g_object_unref(data->quote.min);
1470 data->quote.min = NULL;
1473 return TRUE;
1476 static gboolean
1477 _adg_set_max(AdgDim *dim, const gchar *max)
1479 AdgDimPrivate *data = dim->data;
1481 if (g_strcmp0(max, data->max) == 0)
1482 return FALSE;
1484 g_free(data->max);
1485 data->max = g_strdup(max);
1487 if (data->quote.max) {
1488 g_object_unref(data->quote.max);
1489 data->quote.max = NULL;
1492 return TRUE;
1495 static gboolean
1496 _adg_replace(const GMatchInfo *match_info, GString *result, gpointer user_data)
1498 AdgDimReplaceData *replace_data;
1499 gdouble value;
1500 gchar *format;
1501 gchar buffer[256];
1503 replace_data = (AdgDimReplaceData *) user_data;
1504 value = replace_data->value;
1506 if (! adg_dim_style_convert(replace_data->dim_style, &value, *replace_data->argument)) {
1507 /* Conversion failed: invalid argument? */
1508 g_return_val_if_reached(TRUE);
1509 return TRUE;
1512 format = g_match_info_fetch(match_info, 0);
1514 /* This should never happen */
1515 g_return_val_if_fail(format != NULL, TRUE);
1517 /* Consume the recently used argument */
1518 ++ replace_data->argument;
1520 g_ascii_formatd(buffer, 256, format, value);
1521 g_free(format);
1522 g_string_append(result, buffer);
1523 return FALSE;