doc: update copyright line for 2019
[adg.git] / src / adg / adg-dim.c
blob9e0911d840beed6b3ff88fb304cf5904a685b42c
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2019 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"
77 #include <string.h>
81 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_dim_parent_class)
82 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_dim_parent_class)
84 /* A convenience macro for ORing two AdgThreeState values */
85 #define OR_3S(a,b) ( \
86 ((a) == ADG_THREE_STATE_ON || (b) == ADG_THREE_STATE_ON) ? ADG_THREE_STATE_ON : \
87 ((a) == ADG_THREE_STATE_UNKNOWN && (b) == ADG_THREE_STATE_UNKNOWN) ? ADG_THREE_STATE_UNKNOWN : \
88 ADG_THREE_STATE_OFF )
91 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY)
93 enum {
94 PROP_0,
95 PROP_DIM_DRESS,
96 PROP_REF1,
97 PROP_REF2,
98 PROP_POS,
99 PROP_LEVEL,
100 PROP_OUTSIDE,
101 PROP_DETACHED,
102 PROP_VALUE,
103 PROP_MIN,
104 PROP_MAX
108 static void _adg_dispose (GObject *object);
109 static void _adg_finalize (GObject *object);
110 static void _adg_get_property (GObject *object,
111 guint param_id,
112 GValue *value,
113 GParamSpec *pspec);
114 static void _adg_set_property (GObject *object,
115 guint param_id,
116 const GValue *value,
117 GParamSpec *pspec);
118 static void _adg_global_changed (AdgEntity *entity);
119 static void _adg_local_changed (AdgEntity *entity);
120 static void _adg_invalidate (AdgEntity *entity);
121 static void _adg_arrange (AdgEntity *entity);
122 static gboolean _adg_compute_geometry (AdgDim *dim);
123 static gchar * _adg_default_value (AdgDim *dim);
124 static gdouble _adg_quote_angle (gdouble angle);
125 static gboolean _adg_set_outside (AdgDim *dim,
126 AdgThreeState outside);
127 static gboolean _adg_set_detached (AdgDim *dim,
128 AdgThreeState detached);
129 static gboolean _adg_set_value (AdgDim *dim,
130 const gchar *value);
131 static gboolean _adg_set_min (AdgDim *dim,
132 const gchar *min);
133 static gboolean _adg_set_max (AdgDim *dim,
134 const gchar *max);
135 static gboolean _adg_replace (const GMatchInfo *match_info,
136 GString *result,
137 gpointer user_data);
138 static gchar * _adg_text_expand (AdgDimReplaceData *data);
141 static void
142 adg_dim_class_init(AdgDimClass *klass)
144 GObjectClass *gobject_class;
145 AdgEntityClass *entity_class;
146 GParamSpec *param;
148 gobject_class = (GObjectClass *) klass;
149 entity_class = (AdgEntityClass *) klass;
151 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
153 gobject_class->dispose = _adg_dispose;
154 gobject_class->finalize = _adg_finalize;
155 gobject_class->get_property = _adg_get_property;
156 gobject_class->set_property = _adg_set_property;
158 entity_class->global_changed = _adg_global_changed;
159 entity_class->local_changed = _adg_local_changed;
160 entity_class->invalidate = _adg_invalidate;
161 entity_class->arrange = _adg_arrange;
163 klass->compute_geometry = _adg_compute_geometry;
164 klass->quote_angle = _adg_quote_angle;
165 klass->default_value = _adg_default_value;
167 param = adg_param_spec_dress("dim-dress",
168 P_("Dimension Dress"),
169 P_("The dress to use for rendering this dimension"),
170 ADG_DRESS_DIMENSION,
171 G_PARAM_READWRITE);
172 g_object_class_install_property(gobject_class, PROP_DIM_DRESS, param);
174 param = g_param_spec_boxed("ref1",
175 P_("First Reference"),
176 P_("First reference point of the dimension"),
177 ADG_TYPE_POINT,
178 G_PARAM_READWRITE);
179 g_object_class_install_property(gobject_class, PROP_REF1, param);
181 param = g_param_spec_boxed("ref2",
182 P_("Second Reference"),
183 P_("Second reference point of the dimension"),
184 ADG_TYPE_POINT,
185 G_PARAM_READWRITE);
186 g_object_class_install_property(gobject_class, PROP_REF2, param);
188 param = g_param_spec_boxed("pos",
189 P_("Position"),
190 P_("The reference position of the quote: it will be combined with \"level\" to get the real quote position"),
191 ADG_TYPE_POINT,
192 G_PARAM_READWRITE);
193 g_object_class_install_property(gobject_class, PROP_POS, param);
195 param = g_param_spec_double("level",
196 P_("Level"),
197 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"),
198 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
199 G_PARAM_READWRITE);
200 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
202 param = g_param_spec_enum("outside",
203 P_("Outside"),
204 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"),
205 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
206 G_PARAM_READWRITE);
207 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
209 param = g_param_spec_enum("detached",
210 P_("Detached Quote"),
211 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"),
212 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
213 G_PARAM_READWRITE);
214 g_object_class_install_property(gobject_class, PROP_DETACHED, param);
216 param = g_param_spec_string("value",
217 P_("Value Template"),
218 P_("The template string to be used for generating the set value of the quote"),
219 NULL,
220 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
221 g_object_class_install_property(gobject_class, PROP_VALUE, param);
223 param = g_param_spec_string("min",
224 P_("Minimum Value or Low Tolerance"),
225 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
226 NULL,
227 G_PARAM_READWRITE);
228 g_object_class_install_property(gobject_class, PROP_MIN, param);
230 param = g_param_spec_string("max",
231 P_("Maximum Value or High Tolerance"),
232 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
233 NULL,
234 G_PARAM_READWRITE);
235 g_object_class_install_property(gobject_class, PROP_MAX, param);
238 static void
239 adg_dim_init(AdgDim *dim)
241 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
242 AdgDimPrivate);
244 data->dim_dress = ADG_DRESS_DIMENSION;
245 data->ref1 = NULL;
246 data->ref2 = NULL;
247 data->pos = NULL;
248 data->level = 1;
249 data->outside = ADG_THREE_STATE_UNKNOWN;
250 data->detached = ADG_THREE_STATE_UNKNOWN;
251 data->min = NULL;
252 data->max = NULL;
253 data->geometry.computed = FALSE;
254 data->geometry.notice = NULL;
256 #if 0
257 /* This one is G_PARAM_CONSTRUCT, so set by property inizialization */
258 data->value = NULL
259 #endif
261 dim->data = data;
264 static void
265 _adg_dispose(GObject *object)
267 AdgEntity *entity;
268 AdgDimPrivate *data;
270 entity = (AdgEntity *) object;
271 data = ((AdgDim *) object)->data;
273 if (data->quote.entity) {
274 g_object_unref(data->quote.entity);
275 data->quote.entity = NULL;
278 if (data->ref1)
279 data->ref1 = adg_entity_point(entity, data->ref1, NULL);
281 if (data->ref2)
282 data->ref2 = adg_entity_point(entity, data->ref2, NULL);
284 if (data->pos)
285 data->pos = adg_entity_point(entity, data->pos, NULL);
287 if (_ADG_OLD_OBJECT_CLASS->dispose)
288 _ADG_OLD_OBJECT_CLASS->dispose(object);
291 static void
292 _adg_finalize(GObject *object)
294 AdgDimPrivate *data = ((AdgDim *) object)->data;
296 g_free(data->value);
297 g_free(data->min);
298 g_free(data->max);
300 if (_ADG_OLD_OBJECT_CLASS->finalize)
301 _ADG_OLD_OBJECT_CLASS->finalize(object);
304 static void
305 _adg_get_property(GObject *object, guint prop_id,
306 GValue *value, GParamSpec *pspec)
308 AdgDimPrivate *data = ((AdgDim *) object)->data;
310 switch (prop_id) {
311 case PROP_DIM_DRESS:
312 g_value_set_enum(value, data->dim_dress);
313 break;
314 case PROP_REF1:
315 g_value_set_boxed(value, data->ref1);
316 break;
317 case PROP_REF2:
318 g_value_set_boxed(value, data->ref2);
319 break;
320 case PROP_POS:
321 g_value_set_boxed(value, data->pos);
322 break;
323 case PROP_LEVEL:
324 g_value_set_double(value, data->level);
325 break;
326 case PROP_OUTSIDE:
327 g_value_set_enum(value, data->outside);
328 break;
329 case PROP_DETACHED:
330 g_value_set_enum(value, data->detached);
331 break;
332 case PROP_VALUE:
333 g_value_set_string(value, data->value);
334 break;
335 case PROP_MIN:
336 g_value_set_string(value, data->min);
337 break;
338 case PROP_MAX:
339 g_value_set_string(value, data->max);
340 break;
341 default:
342 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
343 break;
347 static void
348 _adg_set_property(GObject *object, guint prop_id,
349 const GValue *value, GParamSpec *pspec)
351 AdgEntity *entity;
352 AdgDim *dim;
353 AdgDimPrivate *data;
355 entity = (AdgEntity *) object;
356 dim = (AdgDim *) object;
357 data = dim->data;
359 switch (prop_id) {
360 case PROP_DIM_DRESS:
361 data->dim_dress = g_value_get_enum(value);
362 break;
363 case PROP_REF1:
364 data->ref1 = adg_entity_point(entity, data->ref1,
365 g_value_get_boxed(value));
366 break;
367 case PROP_REF2:
368 data->ref2 = adg_entity_point(entity, data->ref2,
369 g_value_get_boxed(value));
370 break;
371 case PROP_POS:
372 data->pos = adg_entity_point(entity, data->pos,
373 g_value_get_boxed(value));
374 break;
375 case PROP_LEVEL:
376 data->level = g_value_get_double(value);
377 break;
378 case PROP_OUTSIDE:
379 _adg_set_outside(dim, g_value_get_enum(value));
380 break;
381 case PROP_DETACHED:
382 _adg_set_detached(dim, g_value_get_enum(value));
383 break;
384 case PROP_VALUE:
385 _adg_set_value(dim, g_value_get_string(value));
386 break;
387 case PROP_MIN:
388 _adg_set_min(dim, g_value_get_string(value));
389 break;
390 case PROP_MAX:
391 _adg_set_max(dim, g_value_get_string(value));
392 break;
393 default:
394 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
395 break;
401 * adg_dim_set_dim_dress:
402 * @dim: an #AdgDim
403 * @dress: the new #AdgDress to use
405 * Sets a new dimension dress to @dim. The new dress must be
406 * related to the original dress for this property: you cannot
407 * set a dress used for line styles to a dress managing fonts.
409 * The check is done by calling adg_dress_are_related() with
410 * @dress and the previous dress as arguments. Check out its
411 * documentation for details on what is a related dress.
413 * Since: 1.0
415 void
416 adg_dim_set_dim_dress(AdgDim *dim, AdgDress dress)
418 g_return_if_fail(ADG_IS_DIM(dim));
419 g_object_set(dim, "dim-dress", dress, NULL);
423 * adg_dim_get_dim_dress:
424 * @dim: an #AdgDim
426 * Gets the dimension dress to be used in rendering @dim.
428 * Returns: (transfer none): the current dimension dress.
430 * Since: 1.0
432 AdgDress
433 adg_dim_get_dim_dress(AdgDim *dim)
435 AdgDimPrivate *data;
437 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_DRESS_UNDEFINED);
439 data = dim->data;
441 return data->dim_dress;
445 * adg_dim_set_ref1:
446 * @dim: an #AdgDim
447 * @ref1: the new point to use as first reference
449 * Sets the #AdgDim:ref1 property to @ref1. The old point
450 * is silently discarded, unreferencing its model if that
451 * point was bound to a named pair (hence, possibly destroying
452 * the model if this was the last reference).
454 * @ref1 can be <constant>NULL</constant>, in which case the
455 * point is destroyed.
457 * Since: 1.0
459 void
460 adg_dim_set_ref1(AdgDim *dim, const AdgPoint *ref1)
462 g_return_if_fail(ADG_IS_DIM(dim));
463 g_object_set(dim, "ref1", ref1, NULL);
467 * adg_dim_set_ref1_explicit:
468 * @dim: an #AdgDim
469 * @x: x coordinate of the first reference point
470 * @y: y coordinate of the first reference point
472 * Sets the #AdgDim:ref1 property to the (@x, @y) explicit
473 * coordinates. The old point is silently discarded,
474 * unreferencing its model if that point was bound to a named
475 * pair (hence, possibly destroying the model if this was the
476 * last reference).
478 * Since: 1.0
480 void
481 adg_dim_set_ref1_explicit(AdgDim *dim, gdouble x, gdouble y)
483 AdgPoint *point = adg_point_new();
485 adg_point_set_pair_explicit(point, x, y);
486 adg_dim_set_ref1(dim, point);
488 adg_point_destroy(point);
492 * adg_dim_set_ref1_from_pair:
493 * @dim: an #AdgDim
494 * @ref1: the coordinates pair of the first reference point
496 * Convenient function to set the #AdgDim:ref1 property using a
497 * pair instead of explicit coordinates.
499 * Since: 1.0
501 void
502 adg_dim_set_ref1_from_pair(AdgDim *dim, const CpmlPair *ref1)
504 g_return_if_fail(ref1 != NULL);
506 adg_dim_set_ref1_explicit(dim, ref1->x, ref1->y);
510 * adg_dim_set_ref1_from_model:
511 * @dim: an #AdgDim
512 * @model: the source #AdgModel
513 * @ref1: a named pair in @model
515 * Binds #AdgDim:ref1 to the @ref1 named pair of @model. If @model
516 * is <constant>NULL</constant>, the point will be unset. In any case,
517 * the old point is silently discarded, unreferencing its model
518 * if that point was bound to a named pair (hence, possibly destroying
519 * the model if this was the last reference).
521 * The assignment is lazy so @ref1 could be not be present in @model.
522 * Anyway, at the first access to this point an error will be raised
523 * if the named pair is still missing.
525 * Since: 1.0
527 void
528 adg_dim_set_ref1_from_model(AdgDim *dim, AdgModel *model, const gchar *ref1)
530 AdgPoint *point = adg_point_new();
532 adg_point_set_pair_from_model(point, model, ref1);
533 adg_dim_set_ref1(dim, point);
535 adg_point_destroy(point);
539 * adg_dim_get_ref1:
540 * @dim: an #AdgDim
542 * Gets the #AdgDim:ref1 point of @dim.
544 * The returned point is internally owned and must not be freed
545 * or modified. Anyway it is not const because a call to
546 * adg_point_update() with the returned value must be able to
547 * modify the internal cache.
549 * Returns: (transfer none): the first reference point.
551 * Since: 1.0
553 AdgPoint *
554 adg_dim_get_ref1(AdgDim *dim)
556 AdgDimPrivate *data;
558 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
560 data = dim->data;
562 return data->ref1;
566 * adg_dim_set_ref2:
567 * @dim: an #AdgDim
568 * @ref2: the new point to use as second reference
570 * Sets the #AdgDim:ref2 property to @ref2. The old point
571 * is silently discarded, unreferencing its model if that
572 * point was bound to a named pair (hence, possibly destroying
573 * the model if it was the last reference).
575 * @ref2 can be <constant>NULL</constant>, in which case
576 * the point is destroyed.
578 * Since: 1.0
580 void
581 adg_dim_set_ref2(AdgDim *dim, const AdgPoint *ref2)
583 g_return_if_fail(ADG_IS_DIM(dim));
584 g_object_set(dim, "ref2", ref2, NULL);
588 * adg_dim_set_ref2_explicit:
589 * @dim: an #AdgDim
590 * @x: x coordinate of the second reference point
591 * @y: y coordinate of the second reference point
593 * Sets the #AdgDim:ref2 property to the (@x, @y) explicit
594 * coordinates. The old point is silently discarded,
595 * unreferencing its model if that point was bound to a named
596 * pair (hence, possibly destroying the model if this was the
597 * last reference).
599 * Since: 1.0
601 void
602 adg_dim_set_ref2_explicit(AdgDim *dim, gdouble x, gdouble y)
604 AdgPoint *point = adg_point_new();
606 adg_point_set_pair_explicit(point, x, y);
607 adg_dim_set_ref2(dim, point);
609 adg_point_destroy(point);
613 * adg_dim_set_ref2_from_pair:
614 * @dim: an #AdgDim
615 * @ref2: the coordinates pair of the second reference point
617 * Convenient function to set the #AdgDim:ref2 property using a
618 * pair instead of explicit coordinates.
620 * Since: 1.0
622 void
623 adg_dim_set_ref2_from_pair(AdgDim *dim, const CpmlPair *ref2)
625 g_return_if_fail(ref2 != NULL);
627 adg_dim_set_ref2_explicit(dim, ref2->x, ref2->y);
631 * adg_dim_set_ref2_from_model:
632 * @dim: an #AdgDim
633 * @model: the source #AdgModel
634 * @ref2: a named pair in @model
636 * Binds #AdgDim:ref2 to the @ref2 named pair of @model. If @model
637 * is <constant>NULL</constant>, the point will be unset. In any
638 * case, the old point is silently discarded, unreferencing its
639 * model if that point was bound to a named pair (hence, possibly
640 * destroying the model if this was the last reference).
642 * The assignment is lazy so @ref2 could be not be present in @model.
643 * Anyway, at the first access to this point an error will be raised
644 * if the named pair is still missing.
646 * Since: 1.0
648 void
649 adg_dim_set_ref2_from_model(AdgDim *dim, AdgModel *model, const gchar *ref2)
651 AdgPoint *point = adg_point_new();
653 adg_point_set_pair_from_model(point, model, ref2);
654 adg_dim_set_ref2(dim, point);
656 adg_point_destroy(point);
660 * adg_dim_get_ref2:
661 * @dim: an #AdgDim
663 * Gets the #AdgDim:ref2 point of @dim.
665 * The returned point is internally owned and must not be freed
666 * or modified. Anyway it is not const because a call to
667 * adg_point_update() with the returned value must be able to
668 * modify the internal cache.
670 * Returns: (transfer none): the second reference point.
672 * Since: 1.0
674 AdgPoint *
675 adg_dim_get_ref2(AdgDim *dim)
677 AdgDimPrivate *data;
679 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
681 data = dim->data;
683 return data->ref2;
687 * adg_dim_set_pos:
688 * @dim: an #AdgDim
689 * @pos: the new point to use as position
691 * Sets the #AdgDim:pos property of @dim to @pos. The old point
692 * is silently discarded, unreferencing its model if that
693 * point was bound to a named pair (hence, possibly destroying
694 * the model if it was the last reference).
696 * @pos can be <constant>NULL</constant>, in which case the
697 * point is destroyed.
699 * Since: 1.0
701 void
702 adg_dim_set_pos(AdgDim *dim, const AdgPoint *pos)
704 g_return_if_fail(ADG_IS_DIM(dim));
705 g_object_set(dim, "pos", pos, NULL);
709 * adg_dim_set_pos_explicit:
710 * @dim: an #AdgDim
711 * @x: x coordinate of the position
712 * @y: y coordinate of the position
714 * Sets the #AdgDim:pos property to the (@x, @y) explicit
715 * coordinates. The old point is silently discarded,
716 * unreferencing its model if that point was bound to a named
717 * pair (hence, possibly destroying the model if this was the
718 * last reference).
720 * Since: 1.0
722 void
723 adg_dim_set_pos_explicit(AdgDim *dim, gdouble x, gdouble y)
725 AdgPoint *point = adg_point_new();
727 adg_point_set_pair_explicit(point, x, y);
728 adg_dim_set_pos(dim, point);
730 adg_point_destroy(point);
734 * adg_dim_set_pos_from_pair:
735 * @dim: an #AdgDim
736 * @pos: the coordinates pair of the position point
738 * Convenient function to set the #AdgDim:pos property using a
739 * pair instead of explicit coordinates.
741 * Since: 1.0
743 void
744 adg_dim_set_pos_from_pair(AdgDim *dim, const CpmlPair *pos)
746 g_return_if_fail(pos != NULL);
748 adg_dim_set_pos_explicit(dim, pos->x, pos->y);
752 * adg_dim_set_pos_from_model:
753 * @dim: an #AdgDim
754 * @model: the source #AdgModel
755 * @pos: a named pair in @model
757 * Binds #AdgDim:pos to the @pos named pair of @model. If @model
758 * is <constant>NULL</constant>, the point will be unset. In any
759 * case, the old point is silently discarded, unreferencing its
760 * model if that point was bound to a named pair (hence,
761 * possibly destroying the model if this was the last reference).
763 * The assignment is lazy so @pos could be not be present in @model.
764 * Anyway, at the first access to this point an error will be raised
765 * if the named pair is still missing.
767 * Since: 1.0
769 void
770 adg_dim_set_pos_from_model(AdgDim *dim, AdgModel *model, const gchar *pos)
772 AdgPoint *point = adg_point_new();
774 adg_point_set_pair_from_model(point, model, pos);
775 adg_dim_set_pos(dim, point);
777 adg_point_destroy(point);
781 * adg_dim_get_pos:
782 * @dim: an #AdgDim
784 * Gets the #AdgDim:pos point of @dim.
786 * The returned point is internally owned and must not be freed
787 * or modified. Anyway it is not const because a call to
788 * adg_point_update() with the returned value must be able to
789 * modify the internal cache.
791 * Returns: (transfer none): the position point.
793 * Since: 1.0
795 AdgPoint *
796 adg_dim_get_pos(AdgDim *dim)
798 AdgDimPrivate *data;
800 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
802 data = dim->data;
804 return data->pos;
808 * adg_dim_set_level:
809 * @dim: an #AdgDim
810 * @level: the new level
812 * Sets a new level for this dimension. The level is used to
813 * stack the quotes using a spacing value from dim_style
814 * (specified in global space).
816 * Since: 1.0
818 void
819 adg_dim_set_level(AdgDim *dim, gdouble level)
821 AdgDimPrivate *data;
823 g_return_if_fail(ADG_IS_DIM(dim));
825 data = dim->data;
826 data->level = level;
828 g_object_notify((GObject *) dim, "level");
832 * adg_dim_get_level:
833 * @dim: an #AdgDim
835 * Gets the level of this dimension.
837 * Returns: the level value.
839 * Since: 1.0
841 gdouble
842 adg_dim_get_level(AdgDim *dim)
844 AdgDimPrivate *data;
846 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
848 data = dim->data;
850 return data->level;
854 * adg_dim_set_outside:
855 * @dim: an #AdgDim
856 * @outside: the new outside state
858 * Sets a new state for the #AdgDim:outside flag: check the property
859 * documentation for further details.
861 * Since: 1.0
863 void
864 adg_dim_set_outside(AdgDim *dim, AdgThreeState outside)
866 g_return_if_fail(ADG_IS_DIM(dim));
868 if (_adg_set_outside(dim, outside))
869 g_object_notify((GObject *) dim, "outside");
873 * adg_dim_get_outside:
874 * @dim: an #AdgDim
876 * Gets the state of the #AdgDim:outside property: check the property
877 * documentation for further details.
879 * Returns: the current flag state.
881 * Since: 1.0
883 AdgThreeState
884 adg_dim_get_outside(AdgDim *dim)
886 AdgDimPrivate *data;
888 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
890 data = dim->data;
892 return data->outside;
896 * adg_dim_set_detached:
897 * @dim: an #AdgDim
898 * @detached: the new detached state
900 * Sets a new state for the #AdgDim:detached flag: check the property
901 * documentation for further details.
903 * This is used only by dimensions where detaching has meaning.
904 * In some cases, such as with #AdgRDim dimensions, this property is
905 * not used.
907 * Since: 1.0
909 void
910 adg_dim_set_detached(AdgDim *dim, AdgThreeState detached)
912 g_return_if_fail(ADG_IS_DIM(dim));
914 if (_adg_set_detached(dim, detached))
915 g_object_notify((GObject *) dim, "detached");
919 * adg_dim_get_detached:
920 * @dim: an #AdgDim
922 * Gets the state of the #AdgDim:detached property: check the property
923 * documentation for further details.
925 * Returns: the current flag state.
927 * Since: 1.0
929 AdgThreeState
930 adg_dim_get_detached(AdgDim *dim)
932 AdgDimPrivate *data;
934 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
936 data = dim->data;
938 return data->detached;
942 * adg_dim_set_value:
943 * @dim: an #AdgDim
944 * @value: (allow-none): the value text
946 * Explicitely sets the text to use as value. If @value
947 * is <constant>NULL</constant> or was never set, an automatic
948 * text is calculated using the format specified in the current
949 * #AdgDimStyle and getting its value by calling
950 * the <function>default_value</function> virtual method.
952 * Inside the template string, the "<>" tag (or whatever specified
953 * by the #AdgDimStyle:number-tag property) is substituted with the
954 * string returned by <function>default_value</function>.
956 * Since: 1.0
958 void
959 adg_dim_set_value(AdgDim *dim, const gchar *value)
961 g_return_if_fail(ADG_IS_DIM(dim));
963 if (_adg_set_value(dim, value))
964 g_object_notify((GObject *) dim, "value");
968 * adg_dim_get_value:
969 * @dim: an #AdgDim
971 * Gets the value text. The string is internally owned and
972 * must not be freed or modified.
974 * Returns: (transfer none): the value text.
976 * Since: 1.0
978 const gchar *
979 adg_dim_get_value(AdgDim *dim)
981 AdgDimPrivate *data;
983 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
985 data = dim->data;
987 return data->value;
991 * adg_dim_get_text:
992 * @dim: an #AdgDim
993 * @value: the raw value of the quote
995 * Gets the final text to show as nominal value into the quote. The string is
996 * the same returned by adg_dim_get_value() with the tag properly expanded.
998 * The string substituted to the tag is formatted according to the
999 * #AdgDimStyle:number-format and #AdgDimStyle:number-arguments properties.
1000 * See the #AdgDimStyle documentation for further details.
1002 * Returns: (transfer full): the final text of the quote.
1004 * Since: 1.0
1006 gchar *
1007 adg_dim_get_text(AdgDim *dim, gdouble value)
1009 AdgDimStyle *dim_style;
1010 const gchar *format;
1011 const gchar *arguments;
1012 AdgDimReplaceData data;
1013 gchar *raw, *result;
1014 GRegex *regex;
1016 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1018 dim_style = _ADG_GET_DIM_STYLE(dim);
1019 if (dim_style == NULL) {
1020 dim_style = (AdgDimStyle *) adg_entity_style((AdgEntity *) dim,
1021 adg_dim_get_dim_dress(dim));
1024 format = adg_dim_style_get_number_format(dim_style);
1025 if (format == NULL) {
1026 return NULL;
1029 arguments = adg_dim_style_get_number_arguments(dim_style);
1030 if (arguments == NULL) {
1031 return g_strdup(format);
1034 /* Expand the values */
1035 data.dim_style = dim_style;
1036 data.value = value;
1037 data.format = format;
1038 data.argument = arguments;
1039 data.regex = g_regex_new("(?<!\\\\)%.*[eEfFgG]", G_REGEX_UNGREEDY, 0, NULL);
1040 raw = _adg_text_expand(&data);
1041 g_regex_unref(data.regex);
1043 /* Check that all format string has been parsed, otherwise there are
1044 * likely too many close parenthesis */
1045 if (*data.format != '\0') {
1046 g_free(raw);
1047 g_return_val_if_reached(NULL);
1048 return NULL;
1051 /* Substitute the escape sequences ("\%", "\(" and "\)") */
1052 regex = g_regex_new("\\\\([%()])", G_REGEX_UNGREEDY, 0, NULL);
1053 result = g_regex_replace(regex, raw, -1, 0, "\\1", 0, NULL);
1054 g_free(raw);
1055 g_regex_unref(regex);
1057 return result;
1061 * adg_dim_set_limits:
1062 * @dim: an #AdgDim
1063 * @min: (allow-none): the new minumum value
1064 * @max: (allow-none): the new maximum value
1066 * Shortcut to set both the limits at once.
1068 * Since: 1.0
1070 void
1071 adg_dim_set_limits(AdgDim *dim, const gchar *min, const gchar *max)
1073 g_return_if_fail(ADG_IS_DIM(dim));
1075 g_object_freeze_notify((GObject *) dim);
1076 adg_dim_set_min(dim, min);
1077 adg_dim_set_max(dim, max);
1078 g_object_thaw_notify((GObject *) dim);
1082 * adg_dim_set_min:
1083 * @dim: an #AdgDim
1084 * @min: (allow-none): the new minimum limit
1086 * Sets the minimum value. Use <constant>NULL</constant>
1087 * as @min to disable it.
1089 * Since: 1.0
1091 void
1092 adg_dim_set_min(AdgDim *dim, const gchar *min)
1094 g_return_if_fail(ADG_IS_DIM(dim));
1096 if (_adg_set_min(dim, min))
1097 g_object_notify((GObject *) dim, "min");
1101 * adg_dim_get_min:
1102 * @dim: an #AdgDim
1104 * Gets the minimum value text or <constant>NULL</constant>
1105 * on minimum value disabled.
1107 * The string is internally owned and must not be freed or modified.
1109 * Returns: (transfer none): the mimimum value text.
1111 * Since: 1.0
1113 const gchar *
1114 adg_dim_get_min(AdgDim *dim)
1116 AdgDimPrivate *data;
1118 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1120 data = dim->data;
1122 return data->min;
1126 * adg_dim_set_max:
1127 * @dim: an #AdgDim
1128 * @max: (allow-none): the new maximum value
1130 * Sets the maximum value. Use <constant>NULL</constant>
1131 * as @max to disable it.
1133 * Since: 1.0
1135 void
1136 adg_dim_set_max(AdgDim *dim, const gchar *max)
1138 g_return_if_fail(ADG_IS_DIM(dim));
1140 if (_adg_set_max(dim, max))
1141 g_object_notify((GObject *) dim, "max");
1145 * adg_dim_get_max:
1146 * @dim: an #AdgDim
1148 * Gets the maximum value text or <constant>NULL</constant>
1149 * on maximum value disabled.
1151 * The string is internally owned and must not be freed or modified.
1153 * Returns: (transfer none): the maximum value text.
1155 * Since: 1.0
1157 const gchar *
1158 adg_dim_get_max(AdgDim *dim)
1160 AdgDimPrivate *data;
1162 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1164 data = dim->data;
1166 return data->max;
1170 * adg_dim_get_quote:
1171 * @dim: an #AdgDim
1173 * Gets the quote entity, if any. This function is valid only after
1174 * the #AdgDim implementation of the arrange() virtual method has
1175 * been called.
1177 * The returned entity is owned by @dim and should not be
1178 * modified or freed.
1180 * <note><para>
1181 * This function is only useful in new dimension implementations.
1182 * </para></note>
1184 * Returns: (transfer none): the quote entity.
1186 * Since: 1.0
1188 AdgAlignment *
1189 adg_dim_get_quote(AdgDim *dim)
1191 AdgDimPrivate *data;
1193 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1195 data = dim->data;
1197 return data->quote.entity;
1201 * adg_dim_quote_angle:
1202 * @dim: an #AdgDim
1203 * @angle: an angle (in radians)
1205 * <note><para>
1206 * This function is only useful in new dimension implementations.
1207 * </para></note>
1209 * Converts @angle accordling to the style of @dim. Any quote angle
1210 * should be validated by this method because every dimensioning
1211 * style has its own convention regardling the text rotation.
1213 * Returns: the angle to use (always in radians).
1215 * Since: 1.0
1217 gdouble
1218 adg_dim_quote_angle(AdgDim *dim, gdouble angle)
1220 AdgDimClass *klass;
1222 g_return_val_if_fail(ADG_IS_DIM(dim), angle);
1224 klass = ADG_DIM_GET_CLASS(dim);
1226 if (klass->quote_angle == NULL)
1227 return angle;
1229 return klass->quote_angle(angle);
1233 * adg_dim_has_geometry:
1234 * @dim: an #AdgDim
1236 * <note><para>
1237 * This function is only useful in new dimension implementations.
1238 * </para></note>
1240 * Checks if the geometry data of @dim has already been computed.
1242 * Returns: TRUE if the geometry has already been computed, FALSE otherwise.
1244 * Since: 1.0
1246 gboolean
1247 adg_dim_has_geometry(AdgDim *dim)
1249 AdgDimPrivate *data;
1251 g_return_val_if_fail(ADG_IS_DIM(dim), FALSE);
1253 data = dim->data;
1255 return data->geometry.computed;
1259 * adg_dim_switch_geometry:
1260 * @dim: an #AdgDim
1261 * @computed: the new computed state
1263 * <note><para>
1264 * This function is only useful in new dimension implementations.
1265 * </para></note>
1267 * Sets the computed state of @dim to @computed. This is an internal flag that
1268 * keeps track of when the geometry data is up to date.
1270 * Since: 1.0
1272 void
1273 adg_dim_switch_geometry(AdgDim *dim, gboolean computed)
1275 AdgDimPrivate *data;
1277 g_return_if_fail(ADG_IS_DIM(dim));
1279 data = dim->data;
1281 data->geometry.computed = computed;
1285 * adg_dim_get_geometry_notice:
1286 * @dim: an #AdgDim
1288 * Gets the geometry message of @dim, i.e. a notice that explains why a
1289 * geometry computation has failed. This message can be used for debugging
1290 * purposes, e.g. to know why it is not possible to draw a dimension.
1292 * Returns: the geometry notice or NULL on no notification.
1294 * Since: 1.0
1296 const gchar *
1297 adg_dim_get_geometry_notice(AdgDim *dim)
1299 AdgDimPrivate *data;
1301 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1303 data = dim->data;
1305 return data->geometry.notice;
1309 * adg_dim_set_geometry_notice:
1310 * @dim: an #AdgDim
1311 * @notice: new notice message
1313 * <note><para>
1314 * This function is only useful in new dimension implementations.
1315 * </para></note>
1317 * Sets the geometry message of @dim to @notice whenever a geometry
1318 * computation has failed. This message can later be read with
1319 * adg_dim_get_geometry_notice(): see its documentation for further details.
1321 * Since: 1.0
1323 void
1324 adg_dim_set_geometry_notice(AdgDim *dim, const gchar *notice)
1326 AdgDimPrivate *data;
1328 g_return_if_fail(ADG_IS_DIM(dim));
1330 data = dim->data;
1332 if (data->geometry.notice)
1333 g_free(data->geometry.notice);
1335 data->geometry.notice = g_strdup(notice);
1339 * adg_dim_geometry_missing:
1340 * @dim: an #AdgDim
1341 * @subject: what is missing
1343 * <note><para>
1344 * This function is only useful in new dimension implementations.
1345 * </para></note>
1347 * Wrapper around adg_dim_set_geometry_notice() that sets a default
1348 * notification message when a reference is missing.
1350 * Since: 1.0
1352 void
1353 adg_dim_geometry_missing(AdgDim *dim, const gchar *subject)
1355 gchar *notice;
1357 g_return_if_fail(subject != NULL);
1359 notice = g_strdup_printf(_("'%s' is missing"), subject);
1360 adg_dim_set_geometry_notice(dim, notice);
1361 g_free(notice);
1365 * adg_dim_geometry_coincidents:
1366 * @dim: an #AdgDim
1367 * @subject: what is missing
1369 * <note><para>
1370 * This function is only useful in new dimension implementations.
1371 * </para></note>
1373 * Wrapper around adg_dim_set_geometry_notice() that sets a default
1374 * notification message when two references that must be different are
1375 * coincidents.
1377 * Since: 1.0
1379 void
1380 adg_dim_geometry_coincident(AdgDim *dim,
1381 const gchar *first, const gchar *second,
1382 const CpmlPair *pos)
1384 gchar *notice;
1386 g_return_if_fail(first != NULL);
1387 g_return_if_fail(second != NULL);
1388 g_return_if_fail(pos != NULL);
1390 notice = g_strdup_printf(_("'%s' and '%s' cannot be coincident (%lf, %lf)"),
1391 first, second, pos->x, pos->y);
1392 adg_dim_set_geometry_notice(dim, notice);
1393 g_free(notice);
1397 * adg_dim_compute_geometry:
1398 * @dim: an #AdgDim
1400 * <note><para>
1401 * This function is only useful in new dimension implementations.
1402 * </para></note>
1404 * Updates the geometry data of @dim, i.e. a set of support data (coordinates,
1405 * offsets, shifts) needed in the arrange() phase to build up the entity.
1407 * The data is cached, so further calls just return TRUE. Use
1408 * adg_entity_invalidate() to force a recomputation.
1410 * Returns: TRUE if the geometry has already been computed, FALSE otherwise.
1412 * Since: 1.0
1414 gboolean
1415 adg_dim_compute_geometry(AdgDim *dim)
1417 AdgDimPrivate *data;
1418 AdgDimClass *klass;
1420 g_return_val_if_fail(ADG_IS_DIM(dim), FALSE);
1422 data = dim->data;
1423 if (data->geometry.computed)
1424 return TRUE;
1426 /* compute_geometry virtual method explicitely set to NULL means the
1427 * entity does not have any geometry data, so just set computed to TRUE */
1428 klass = ADG_DIM_GET_CLASS(dim);
1429 if (klass->compute_geometry != NULL && ! klass->compute_geometry(dim))
1430 return FALSE;
1432 data->geometry.computed = TRUE;
1433 return TRUE;
1437 static void
1438 _adg_global_changed(AdgEntity *entity)
1440 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1442 if (_ADG_OLD_ENTITY_CLASS->global_changed)
1443 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
1445 if (data->quote.entity)
1446 adg_entity_global_changed((AdgEntity *) data->quote.entity);
1449 static void
1450 _adg_local_changed(AdgEntity *entity)
1452 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1454 if (_ADG_OLD_ENTITY_CLASS->local_changed)
1455 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
1457 if (data->quote.entity)
1458 adg_entity_local_changed((AdgEntity *) data->quote.entity);
1461 static void
1462 _adg_invalidate(AdgEntity *entity)
1464 AdgDimPrivate *data = ((AdgDim *) entity)->data;
1466 if (data->quote.value) {
1467 g_object_unref(data->quote.value);
1468 data->quote.value = NULL;
1470 if (data->quote.entity)
1471 adg_entity_invalidate((AdgEntity *) data->quote.entity);
1472 if (data->ref1)
1473 adg_point_invalidate(data->ref1);
1474 if (data->ref2)
1475 adg_point_invalidate(data->ref2);
1476 if (data->pos)
1477 adg_point_invalidate(data->pos);
1479 data->geometry.computed = FALSE;
1480 if (data->geometry.notice != NULL) {
1481 g_free(data->geometry.notice);
1482 data->geometry.notice = NULL;
1485 if (_ADG_OLD_ENTITY_CLASS->invalidate)
1486 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
1489 static void
1490 _adg_arrange(AdgEntity *entity)
1492 AdgDim *dim;
1493 AdgDimPrivate *data;
1494 AdgEntity *quote_entity;
1495 AdgContainer *quote_container;
1496 AdgEntity *value_entity;
1497 AdgEntity *min_entity;
1498 AdgEntity *max_entity;
1499 const CpmlPair *shift;
1500 cairo_matrix_t map;
1502 dim = (AdgDim *) entity;
1503 data = dim->data;
1505 /* Resolve the dim style */
1506 if (data->dim_style == NULL)
1507 data->dim_style = (AdgDimStyle *)
1508 adg_entity_style(entity, data->dim_dress);
1510 if (data->quote.entity == NULL)
1511 data->quote.entity = g_object_new(ADG_TYPE_ALIGNMENT,
1512 "local-mix", ADG_MIX_NONE,
1513 "parent", dim, NULL);
1515 quote_entity = (AdgEntity *) data->quote.entity;
1516 quote_container = (AdgContainer *) data->quote.entity;
1518 if (data->quote.value == NULL) {
1519 AdgDimClass *klass;
1520 AdgDress dress;
1521 const gchar *tag;
1522 gchar *value;
1523 gchar *text;
1525 klass = ADG_DIM_GET_CLASS(dim);
1526 dress = adg_dim_style_get_value_dress(data->dim_style);
1527 tag = adg_dim_style_get_number_tag(data->dim_style);
1528 value = klass->default_value ? klass->default_value(dim) : NULL;
1530 data->quote.value = g_object_new(ADG_TYPE_BEST_TEXT,
1531 "local-mix", ADG_MIX_PARENT,
1532 "font-dress", dress, NULL);
1533 adg_container_add(quote_container, (AdgEntity *) data->quote.value);
1535 if (data->value)
1536 text = adg_string_replace(data->value, tag, value);
1537 else
1538 text = g_strdup(value);
1540 g_free(value);
1542 adg_textual_set_text(data->quote.value, text);
1543 g_free(text);
1546 if (data->quote.min == NULL && data->min != NULL) {
1547 AdgDress dress = adg_dim_style_get_min_dress(data->dim_style);
1549 data->quote.min = g_object_new(ADG_TYPE_BEST_TEXT,
1550 "local-mix", ADG_MIX_PARENT,
1551 "font-dress", dress, NULL);
1553 adg_container_add(quote_container, (AdgEntity *) data->quote.min);
1554 adg_textual_set_text(data->quote.min, data->min);
1557 if (data->quote.max == NULL && data->max != NULL) {
1558 AdgDress dress = adg_dim_style_get_max_dress(data->dim_style);
1560 data->quote.max = g_object_new(ADG_TYPE_BEST_TEXT,
1561 "local-mix", ADG_MIX_PARENT,
1562 "font-dress", dress, NULL);
1564 adg_container_add(quote_container, (AdgEntity *) data->quote.max);
1565 adg_textual_set_text(data->quote.max, data->max);
1568 value_entity = (AdgEntity *) data->quote.value;
1569 min_entity = (AdgEntity *) data->quote.min;
1570 max_entity = (AdgEntity *) data->quote.max;
1571 shift = adg_dim_style_get_quote_shift(data->dim_style);
1573 adg_entity_set_global_map(quote_entity, adg_matrix_identity());
1574 adg_entity_global_changed(quote_entity);
1576 cairo_matrix_init_translate(&map, 0, shift->y);
1577 adg_entity_set_global_map(value_entity, &map);
1578 adg_entity_arrange(value_entity);
1580 /* Limit values (min and max) */
1581 if (min_entity != NULL || max_entity != NULL) {
1582 const CpmlPair *limits_shift;
1583 gdouble spacing;
1584 CpmlPair size;
1585 CpmlPair org_min, org_max;
1587 limits_shift = adg_dim_style_get_limits_shift(data->dim_style);
1588 spacing = adg_dim_style_get_limits_spacing(data->dim_style);
1589 size = adg_entity_get_extents(value_entity)->size;
1590 org_min.x = size.x + limits_shift->x;
1591 org_min.y = -size.y / 2 + limits_shift->y;
1592 org_max = org_min;
1594 if (min_entity && max_entity) {
1595 /* Prearrange the min entity to get its extents */
1596 adg_entity_arrange(min_entity);
1597 size = adg_entity_get_extents(min_entity)->size;
1599 org_min.y += (size.y + spacing) / 2;
1600 org_max.y = org_min.y - size.y - spacing;
1603 if (min_entity != NULL) {
1604 cairo_matrix_init_translate(&map, org_min.x, org_min.y);
1605 adg_entity_set_global_map(min_entity, &map);
1606 adg_entity_arrange(min_entity);
1609 if (max_entity != NULL) {
1610 cairo_matrix_init_translate(&map, org_max.x, org_max.y);
1611 adg_entity_set_global_map(max_entity, &map);
1612 adg_entity_arrange(max_entity);
1617 static gboolean
1618 _adg_compute_geometry(AdgDim *dim)
1620 g_warning(_("AdgDim::compute_geometry not implemented for '%s'"),
1621 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1622 return FALSE;
1625 static gchar *
1626 _adg_default_value(AdgDim *dim)
1628 g_warning(_("AdgDim::default_value not implemented for '%s'"),
1629 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1630 return g_strdup("undef");
1633 static gdouble
1634 _adg_quote_angle(gdouble angle)
1636 angle = cpml_angle(angle);
1638 if (angle > G_PI / 3 || angle <= -G_PI_4 * 3)
1639 angle = cpml_angle(angle + G_PI);
1641 return angle;
1644 static gboolean
1645 _adg_set_outside(AdgDim *dim, AdgThreeState outside)
1647 AdgDimPrivate *data;
1649 g_return_val_if_fail(adg_is_enum_value(outside, ADG_TYPE_THREE_STATE),
1650 FALSE);
1652 data = dim->data;
1654 if (data->outside == outside)
1655 return FALSE;
1657 data->outside = outside;
1659 return TRUE;
1662 static gboolean
1663 _adg_set_detached(AdgDim *dim, AdgThreeState detached)
1665 AdgDimPrivate *data;
1667 g_return_val_if_fail(adg_is_enum_value(detached, ADG_TYPE_THREE_STATE),
1668 FALSE);
1670 data = dim->data;
1672 if (data->detached == detached)
1673 return FALSE;
1675 data->detached = detached;
1677 return TRUE;
1680 static gboolean
1681 _adg_set_value(AdgDim *dim, const gchar *value)
1683 AdgDimPrivate *data;
1685 data = dim->data;
1687 if (g_strcmp0(value, data->value) == 0)
1688 return FALSE;
1690 g_free(data->value);
1691 data->value = g_strdup(value);
1693 if (data->quote.value) {
1694 g_object_unref(data->quote.value);
1695 data->quote.value = NULL;
1698 return TRUE;
1701 static gboolean
1702 _adg_set_min(AdgDim *dim, const gchar *min)
1704 AdgDimPrivate *data = dim->data;
1706 if (g_strcmp0(min, data->min) == 0)
1707 return FALSE;
1709 g_free(data->min);
1710 data->min = g_strdup(min);
1712 if (data->quote.min) {
1713 g_object_unref(data->quote.min);
1714 data->quote.min = NULL;
1717 return TRUE;
1720 static gboolean
1721 _adg_set_max(AdgDim *dim, const gchar *max)
1723 AdgDimPrivate *data = dim->data;
1725 if (g_strcmp0(max, data->max) == 0)
1726 return FALSE;
1728 g_free(data->max);
1729 data->max = g_strdup(max);
1731 if (data->quote.max) {
1732 g_object_unref(data->quote.max);
1733 data->quote.max = NULL;
1736 return TRUE;
1739 static gboolean
1740 _adg_replace(const GMatchInfo *match_info, GString *result, gpointer user_data)
1742 AdgDimReplaceData *data;
1743 gdouble value;
1744 gchar *format;
1745 gchar buffer[256];
1747 data = (AdgDimReplaceData *) user_data;
1748 value = data->value;
1750 if (! adg_dim_style_convert(data->dim_style, &value, *data->argument)) {
1751 /* Conversion failed: invalid argument? */
1752 g_return_val_if_reached(TRUE);
1753 return TRUE;
1756 format = g_match_info_fetch(match_info, 0);
1758 /* This should never happen */
1759 g_return_val_if_fail(format != NULL, TRUE);
1761 /* Consume the recently used argument */
1762 ++ data->argument;
1764 g_ascii_formatd(buffer, 256, format, value);
1765 g_free(format);
1766 g_string_append(result, buffer);
1768 /* Set the valorized flag */
1769 if (value != 0) {
1770 data->valorized = ADG_THREE_STATE_ON;
1771 } else if (data->valorized == ADG_THREE_STATE_UNKNOWN) {
1772 data->valorized = ADG_THREE_STATE_OFF;
1775 return FALSE;
1778 static gchar *
1779 _adg_text_expand(AdgDimReplaceData *data)
1781 GString *result;
1782 const gchar *bog, *eog;
1783 gchar *string;
1784 AdgThreeState valorized;
1785 gssize len;
1787 valorized = ADG_THREE_STATE_UNKNOWN;
1788 result = g_string_new("");
1789 eog = adg_unescaped_strchr(data->format, ')');
1791 /* Expand eventual groups found in the same nesting level */
1792 while ((bog = adg_unescaped_strchr(data->format, '(')) != NULL) {
1793 /* If eog precedes bog, it means that bog is in another nest */
1794 if (eog != NULL && eog < bog) {
1795 break;
1798 len = bog - data->format;
1800 /* Parse template before the bog */
1801 data->valorized = ADG_THREE_STATE_UNKNOWN;
1802 string = g_regex_replace_eval(data->regex,
1803 data->format, len, 0,
1805 _adg_replace, data,
1806 NULL);
1807 valorized = OR_3S(valorized, data->valorized);
1809 data->format += len+1;
1810 g_string_append(result, string);
1811 g_free(string);
1813 /* Recursively expand the group */
1814 string = _adg_text_expand(data);
1815 valorized = OR_3S(valorized, data->valorized);
1817 g_string_append(result, string);
1818 g_free(string);
1820 /* Ensure there is a matching closing parenthesis */
1821 if (*data->format != ')') {
1822 g_string_free(result, TRUE);
1823 g_return_val_if_reached(NULL);
1824 return NULL;
1827 /* Skip the closing parenthesis */
1828 ++ data->format;
1829 eog = adg_unescaped_strchr(data->format, ')');
1832 /* Expand until closing parenthesis (End Of Group) or '\0' */
1833 len = eog == NULL ? strlen(data->format) : (eog - data->format);
1834 data->valorized = ADG_THREE_STATE_UNKNOWN;
1835 string = g_regex_replace_eval(data->regex,
1836 data->format, len, 0,
1838 _adg_replace, data,
1839 NULL);
1841 data->format += len;
1842 g_string_append(result, string);
1843 g_free(string);
1845 /* Store the final valorized state */
1846 valorized = OR_3S(valorized, data->valorized);
1847 data->valorized = valorized;
1849 /* Drop the result only if we are inside a group */
1850 if (*data->format && valorized == ADG_THREE_STATE_OFF) {
1851 g_string_free(result, TRUE);
1852 return g_strdup("");
1855 return g_string_free(result, FALSE);