adg: use G_PRIVATE_ADD and friends
[adg.git] / src / adg / adg-dim.c
blob27c7e7e6d6d513ab851dd032631b508ced6d1342
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_WITH_PRIVATE(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 gobject_class->dispose = _adg_dispose;
152 gobject_class->finalize = _adg_finalize;
153 gobject_class->get_property = _adg_get_property;
154 gobject_class->set_property = _adg_set_property;
156 entity_class->global_changed = _adg_global_changed;
157 entity_class->local_changed = _adg_local_changed;
158 entity_class->invalidate = _adg_invalidate;
159 entity_class->arrange = _adg_arrange;
161 klass->compute_geometry = _adg_compute_geometry;
162 klass->quote_angle = _adg_quote_angle;
163 klass->default_value = _adg_default_value;
165 param = adg_param_spec_dress("dim-dress",
166 P_("Dimension Dress"),
167 P_("The dress to use for rendering this dimension"),
168 ADG_DRESS_DIMENSION,
169 G_PARAM_READWRITE);
170 g_object_class_install_property(gobject_class, PROP_DIM_DRESS, param);
172 param = g_param_spec_boxed("ref1",
173 P_("First Reference"),
174 P_("First reference point of the dimension"),
175 ADG_TYPE_POINT,
176 G_PARAM_READWRITE);
177 g_object_class_install_property(gobject_class, PROP_REF1, param);
179 param = g_param_spec_boxed("ref2",
180 P_("Second Reference"),
181 P_("Second reference point of the dimension"),
182 ADG_TYPE_POINT,
183 G_PARAM_READWRITE);
184 g_object_class_install_property(gobject_class, PROP_REF2, param);
186 param = g_param_spec_boxed("pos",
187 P_("Position"),
188 P_("The reference position of the quote: it will be combined with \"level\" to get the real quote position"),
189 ADG_TYPE_POINT,
190 G_PARAM_READWRITE);
191 g_object_class_install_property(gobject_class, PROP_POS, param);
193 param = g_param_spec_double("level",
194 P_("Level"),
195 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"),
196 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
197 G_PARAM_READWRITE);
198 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
200 param = g_param_spec_enum("outside",
201 P_("Outside"),
202 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"),
203 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
204 G_PARAM_READWRITE);
205 g_object_class_install_property(gobject_class, PROP_OUTSIDE, param);
207 param = g_param_spec_enum("detached",
208 P_("Detached Quote"),
209 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"),
210 ADG_TYPE_THREE_STATE, ADG_THREE_STATE_UNKNOWN,
211 G_PARAM_READWRITE);
212 g_object_class_install_property(gobject_class, PROP_DETACHED, param);
214 param = g_param_spec_string("value",
215 P_("Value Template"),
216 P_("The template string to be used for generating the set value of the quote"),
217 NULL,
218 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
219 g_object_class_install_property(gobject_class, PROP_VALUE, param);
221 param = g_param_spec_string("min",
222 P_("Minimum Value or Low Tolerance"),
223 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
224 NULL,
225 G_PARAM_READWRITE);
226 g_object_class_install_property(gobject_class, PROP_MIN, param);
228 param = g_param_spec_string("max",
229 P_("Maximum Value or High Tolerance"),
230 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
231 NULL,
232 G_PARAM_READWRITE);
233 g_object_class_install_property(gobject_class, PROP_MAX, param);
236 static void
237 adg_dim_init(AdgDim *dim)
239 AdgDimPrivate *data = adg_dim_get_instance_private(dim);
240 data->dim_dress = ADG_DRESS_DIMENSION;
241 data->ref1 = NULL;
242 data->ref2 = NULL;
243 data->pos = NULL;
244 data->level = 1;
245 data->outside = ADG_THREE_STATE_UNKNOWN;
246 data->detached = ADG_THREE_STATE_UNKNOWN;
247 data->min = NULL;
248 data->max = NULL;
249 data->geometry.computed = FALSE;
250 data->geometry.notice = NULL;
251 #if 0
252 /* This one is G_PARAM_CONSTRUCT, so set by property inizialization */
253 data->value = NULL
254 #endif
257 static void
258 _adg_dispose(GObject *object)
260 AdgEntity *entity = (AdgEntity *) object;
261 AdgDimPrivate *data = adg_dim_get_instance_private((AdgDim *) object);
263 if (data->quote.entity) {
264 g_object_unref(data->quote.entity);
265 data->quote.entity = NULL;
268 if (data->ref1)
269 data->ref1 = adg_entity_point(entity, data->ref1, NULL);
271 if (data->ref2)
272 data->ref2 = adg_entity_point(entity, data->ref2, NULL);
274 if (data->pos)
275 data->pos = adg_entity_point(entity, data->pos, NULL);
277 if (_ADG_OLD_OBJECT_CLASS->dispose)
278 _ADG_OLD_OBJECT_CLASS->dispose(object);
281 static void
282 _adg_finalize(GObject *object)
284 AdgDimPrivate *data = adg_dim_get_instance_private((AdgDim *) object);
285 g_free(data->value);
286 g_free(data->min);
287 g_free(data->max);
289 if (_ADG_OLD_OBJECT_CLASS->finalize)
290 _ADG_OLD_OBJECT_CLASS->finalize(object);
293 static void
294 _adg_get_property(GObject *object, guint prop_id,
295 GValue *value, GParamSpec *pspec)
297 AdgDimPrivate *data = adg_dim_get_instance_private((AdgDim *) object);
299 switch (prop_id) {
300 case PROP_DIM_DRESS:
301 g_value_set_enum(value, data->dim_dress);
302 break;
303 case PROP_REF1:
304 g_value_set_boxed(value, data->ref1);
305 break;
306 case PROP_REF2:
307 g_value_set_boxed(value, data->ref2);
308 break;
309 case PROP_POS:
310 g_value_set_boxed(value, data->pos);
311 break;
312 case PROP_LEVEL:
313 g_value_set_double(value, data->level);
314 break;
315 case PROP_OUTSIDE:
316 g_value_set_enum(value, data->outside);
317 break;
318 case PROP_DETACHED:
319 g_value_set_enum(value, data->detached);
320 break;
321 case PROP_VALUE:
322 g_value_set_string(value, data->value);
323 break;
324 case PROP_MIN:
325 g_value_set_string(value, data->min);
326 break;
327 case PROP_MAX:
328 g_value_set_string(value, data->max);
329 break;
330 default:
331 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
332 break;
336 static void
337 _adg_set_property(GObject *object, guint prop_id,
338 const GValue *value, GParamSpec *pspec)
340 AdgEntity *entity = (AdgEntity *) object;
341 AdgDim *dim = (AdgDim *) object;
342 AdgDimPrivate *data = adg_dim_get_instance_private(dim);
344 switch (prop_id) {
345 case PROP_DIM_DRESS:
346 data->dim_dress = g_value_get_enum(value);
347 break;
348 case PROP_REF1:
349 data->ref1 = adg_entity_point(entity, data->ref1,
350 g_value_get_boxed(value));
351 break;
352 case PROP_REF2:
353 data->ref2 = adg_entity_point(entity, data->ref2,
354 g_value_get_boxed(value));
355 break;
356 case PROP_POS:
357 data->pos = adg_entity_point(entity, data->pos,
358 g_value_get_boxed(value));
359 break;
360 case PROP_LEVEL:
361 data->level = g_value_get_double(value);
362 break;
363 case PROP_OUTSIDE:
364 _adg_set_outside(dim, g_value_get_enum(value));
365 break;
366 case PROP_DETACHED:
367 _adg_set_detached(dim, g_value_get_enum(value));
368 break;
369 case PROP_VALUE:
370 _adg_set_value(dim, g_value_get_string(value));
371 break;
372 case PROP_MIN:
373 _adg_set_min(dim, g_value_get_string(value));
374 break;
375 case PROP_MAX:
376 _adg_set_max(dim, g_value_get_string(value));
377 break;
378 default:
379 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
380 break;
386 * adg_dim_get_dim_style:
387 * @dim: an #AdgDim
389 * Gets the internal cached dim style of @dim.
391 * Returns: (transfer none): the internal AdgDimStyle style.
393 * Since: 1.0
395 AdgDimStyle *
396 adg_dim_get_dim_style(AdgDim *dim)
398 AdgDimPrivate *data;
400 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
402 data = adg_dim_get_instance_private(dim);
403 return data->dim_style;
407 * adg_dim_set_dim_dress:
408 * @dim: an #AdgDim
409 * @dress: the new #AdgDress to use
411 * Sets a new dimension dress to @dim. The new dress must be
412 * related to the original dress for this property: you cannot
413 * set a dress used for line styles to a dress managing fonts.
415 * The check is done by calling adg_dress_are_related() with
416 * @dress and the previous dress as arguments. Check out its
417 * documentation for details on what is a related dress.
419 * Since: 1.0
421 void
422 adg_dim_set_dim_dress(AdgDim *dim, AdgDress dress)
424 g_return_if_fail(ADG_IS_DIM(dim));
425 g_object_set(dim, "dim-dress", dress, NULL);
429 * adg_dim_get_dim_dress:
430 * @dim: an #AdgDim
432 * Gets the dimension dress to be used in rendering @dim.
434 * Returns: (transfer none): the current dimension dress.
436 * Since: 1.0
438 AdgDress
439 adg_dim_get_dim_dress(AdgDim *dim)
441 AdgDimPrivate *data;
443 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_DRESS_UNDEFINED);
445 data = adg_dim_get_instance_private(dim);
446 return data->dim_dress;
450 * adg_dim_set_ref1:
451 * @dim: an #AdgDim
452 * @ref1: the new point to use as first reference
454 * Sets the #AdgDim:ref1 property to @ref1. The old point
455 * is silently discarded, unreferencing its model if that
456 * point was bound to a named pair (hence, possibly destroying
457 * the model if this was the last reference).
459 * @ref1 can be <constant>NULL</constant>, in which case the
460 * point is destroyed.
462 * Since: 1.0
464 void
465 adg_dim_set_ref1(AdgDim *dim, const AdgPoint *ref1)
467 g_return_if_fail(ADG_IS_DIM(dim));
468 g_object_set(dim, "ref1", ref1, NULL);
472 * adg_dim_set_ref1_explicit:
473 * @dim: an #AdgDim
474 * @x: x coordinate of the first reference point
475 * @y: y coordinate of the first reference point
477 * Sets the #AdgDim:ref1 property to the (@x, @y) explicit
478 * coordinates. The old point is silently discarded,
479 * unreferencing its model if that point was bound to a named
480 * pair (hence, possibly destroying the model if this was the
481 * last reference).
483 * Since: 1.0
485 void
486 adg_dim_set_ref1_explicit(AdgDim *dim, gdouble x, gdouble y)
488 AdgPoint *point = adg_point_new();
490 adg_point_set_pair_explicit(point, x, y);
491 adg_dim_set_ref1(dim, point);
493 adg_point_destroy(point);
497 * adg_dim_set_ref1_from_pair:
498 * @dim: an #AdgDim
499 * @ref1: the coordinates pair of the first reference point
501 * Convenient function to set the #AdgDim:ref1 property using a
502 * pair instead of explicit coordinates.
504 * Since: 1.0
506 void
507 adg_dim_set_ref1_from_pair(AdgDim *dim, const CpmlPair *ref1)
509 g_return_if_fail(ref1 != NULL);
511 adg_dim_set_ref1_explicit(dim, ref1->x, ref1->y);
515 * adg_dim_set_ref1_from_model:
516 * @dim: an #AdgDim
517 * @model: the source #AdgModel
518 * @ref1: a named pair in @model
520 * Binds #AdgDim:ref1 to the @ref1 named pair of @model. If @model
521 * is <constant>NULL</constant>, the point will be unset. In any case,
522 * the old point is silently discarded, unreferencing its model
523 * if that point was bound to a named pair (hence, possibly destroying
524 * the model if this was the last reference).
526 * The assignment is lazy so @ref1 could be not be present in @model.
527 * Anyway, at the first access to this point an error will be raised
528 * if the named pair is still missing.
530 * Since: 1.0
532 void
533 adg_dim_set_ref1_from_model(AdgDim *dim, AdgModel *model, const gchar *ref1)
535 AdgPoint *point = adg_point_new();
537 adg_point_set_pair_from_model(point, model, ref1);
538 adg_dim_set_ref1(dim, point);
540 adg_point_destroy(point);
544 * adg_dim_get_ref1:
545 * @dim: an #AdgDim
547 * Gets the #AdgDim:ref1 point of @dim.
549 * The returned point is internally owned and must not be freed
550 * or modified. Anyway it is not const because a call to
551 * adg_point_update() with the returned value must be able to
552 * modify the internal cache.
554 * Returns: (transfer none): the first reference point.
556 * Since: 1.0
558 AdgPoint *
559 adg_dim_get_ref1(AdgDim *dim)
561 AdgDimPrivate *data;
563 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
565 data = adg_dim_get_instance_private(dim);
566 return data->ref1;
570 * adg_dim_set_ref2:
571 * @dim: an #AdgDim
572 * @ref2: the new point to use as second reference
574 * Sets the #AdgDim:ref2 property to @ref2. The old point
575 * is silently discarded, unreferencing its model if that
576 * point was bound to a named pair (hence, possibly destroying
577 * the model if it was the last reference).
579 * @ref2 can be <constant>NULL</constant>, in which case
580 * the point is destroyed.
582 * Since: 1.0
584 void
585 adg_dim_set_ref2(AdgDim *dim, const AdgPoint *ref2)
587 g_return_if_fail(ADG_IS_DIM(dim));
588 g_object_set(dim, "ref2", ref2, NULL);
592 * adg_dim_set_ref2_explicit:
593 * @dim: an #AdgDim
594 * @x: x coordinate of the second reference point
595 * @y: y coordinate of the second reference point
597 * Sets the #AdgDim:ref2 property to the (@x, @y) explicit
598 * coordinates. The old point is silently discarded,
599 * unreferencing its model if that point was bound to a named
600 * pair (hence, possibly destroying the model if this was the
601 * last reference).
603 * Since: 1.0
605 void
606 adg_dim_set_ref2_explicit(AdgDim *dim, gdouble x, gdouble y)
608 AdgPoint *point = adg_point_new();
610 adg_point_set_pair_explicit(point, x, y);
611 adg_dim_set_ref2(dim, point);
613 adg_point_destroy(point);
617 * adg_dim_set_ref2_from_pair:
618 * @dim: an #AdgDim
619 * @ref2: the coordinates pair of the second reference point
621 * Convenient function to set the #AdgDim:ref2 property using a
622 * pair instead of explicit coordinates.
624 * Since: 1.0
626 void
627 adg_dim_set_ref2_from_pair(AdgDim *dim, const CpmlPair *ref2)
629 g_return_if_fail(ref2 != NULL);
631 adg_dim_set_ref2_explicit(dim, ref2->x, ref2->y);
635 * adg_dim_set_ref2_from_model:
636 * @dim: an #AdgDim
637 * @model: the source #AdgModel
638 * @ref2: a named pair in @model
640 * Binds #AdgDim:ref2 to the @ref2 named pair of @model. If @model
641 * is <constant>NULL</constant>, the point will be unset. In any
642 * case, the old point is silently discarded, unreferencing its
643 * model if that point was bound to a named pair (hence, possibly
644 * destroying the model if this was the last reference).
646 * The assignment is lazy so @ref2 could be not be present in @model.
647 * Anyway, at the first access to this point an error will be raised
648 * if the named pair is still missing.
650 * Since: 1.0
652 void
653 adg_dim_set_ref2_from_model(AdgDim *dim, AdgModel *model, const gchar *ref2)
655 AdgPoint *point = adg_point_new();
657 adg_point_set_pair_from_model(point, model, ref2);
658 adg_dim_set_ref2(dim, point);
660 adg_point_destroy(point);
664 * adg_dim_get_ref2:
665 * @dim: an #AdgDim
667 * Gets the #AdgDim:ref2 point of @dim.
669 * The returned point is internally owned and must not be freed
670 * or modified. Anyway it is not const because a call to
671 * adg_point_update() with the returned value must be able to
672 * modify the internal cache.
674 * Returns: (transfer none): the second reference point.
676 * Since: 1.0
678 AdgPoint *
679 adg_dim_get_ref2(AdgDim *dim)
681 AdgDimPrivate *data;
683 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
685 data = adg_dim_get_instance_private(dim);
686 return data->ref2;
690 * adg_dim_set_pos:
691 * @dim: an #AdgDim
692 * @pos: the new point to use as position
694 * Sets the #AdgDim:pos property of @dim to @pos. The old point
695 * is silently discarded, unreferencing its model if that
696 * point was bound to a named pair (hence, possibly destroying
697 * the model if it was the last reference).
699 * @pos can be <constant>NULL</constant>, in which case the
700 * point is destroyed.
702 * Since: 1.0
704 void
705 adg_dim_set_pos(AdgDim *dim, const AdgPoint *pos)
707 g_return_if_fail(ADG_IS_DIM(dim));
708 g_object_set(dim, "pos", pos, NULL);
712 * adg_dim_set_pos_explicit:
713 * @dim: an #AdgDim
714 * @x: x coordinate of the position
715 * @y: y coordinate of the position
717 * Sets the #AdgDim:pos property to the (@x, @y) explicit
718 * coordinates. The old point is silently discarded,
719 * unreferencing its model if that point was bound to a named
720 * pair (hence, possibly destroying the model if this was the
721 * last reference).
723 * Since: 1.0
725 void
726 adg_dim_set_pos_explicit(AdgDim *dim, gdouble x, gdouble y)
728 AdgPoint *point = adg_point_new();
730 adg_point_set_pair_explicit(point, x, y);
731 adg_dim_set_pos(dim, point);
733 adg_point_destroy(point);
737 * adg_dim_set_pos_from_pair:
738 * @dim: an #AdgDim
739 * @pos: the coordinates pair of the position point
741 * Convenient function to set the #AdgDim:pos property using a
742 * pair instead of explicit coordinates.
744 * Since: 1.0
746 void
747 adg_dim_set_pos_from_pair(AdgDim *dim, const CpmlPair *pos)
749 g_return_if_fail(pos != NULL);
751 adg_dim_set_pos_explicit(dim, pos->x, pos->y);
755 * adg_dim_set_pos_from_model:
756 * @dim: an #AdgDim
757 * @model: the source #AdgModel
758 * @pos: a named pair in @model
760 * Binds #AdgDim:pos to the @pos named pair of @model. If @model
761 * is <constant>NULL</constant>, the point will be unset. In any
762 * case, the old point is silently discarded, unreferencing its
763 * model if that point was bound to a named pair (hence,
764 * possibly destroying the model if this was the last reference).
766 * The assignment is lazy so @pos could be not be present in @model.
767 * Anyway, at the first access to this point an error will be raised
768 * if the named pair is still missing.
770 * Since: 1.0
772 void
773 adg_dim_set_pos_from_model(AdgDim *dim, AdgModel *model, const gchar *pos)
775 AdgPoint *point = adg_point_new();
777 adg_point_set_pair_from_model(point, model, pos);
778 adg_dim_set_pos(dim, point);
780 adg_point_destroy(point);
784 * adg_dim_get_pos:
785 * @dim: an #AdgDim
787 * Gets the #AdgDim:pos point of @dim.
789 * The returned point is internally owned and must not be freed
790 * or modified. Anyway it is not const because a call to
791 * adg_point_update() with the returned value must be able to
792 * modify the internal cache.
794 * Returns: (transfer none): the position point.
796 * Since: 1.0
798 AdgPoint *
799 adg_dim_get_pos(AdgDim *dim)
801 AdgDimPrivate *data;
803 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
805 data = adg_dim_get_instance_private(dim);
806 return data->pos;
810 * adg_dim_set_level:
811 * @dim: an #AdgDim
812 * @level: the new level
814 * Sets a new level for this dimension. The level is used to
815 * stack the quotes using a spacing value from dim_style
816 * (specified in global space).
818 * Since: 1.0
820 void
821 adg_dim_set_level(AdgDim *dim, gdouble level)
823 AdgDimPrivate *data;
825 g_return_if_fail(ADG_IS_DIM(dim));
827 data = adg_dim_get_instance_private(dim);
828 data->level = level;
829 g_object_notify((GObject *) dim, "level");
833 * adg_dim_get_level:
834 * @dim: an #AdgDim
836 * Gets the level of this dimension.
838 * Returns: the level value.
840 * Since: 1.0
842 gdouble
843 adg_dim_get_level(AdgDim *dim)
845 AdgDimPrivate *data;
847 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
849 data = adg_dim_get_instance_private(dim);
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 = adg_dim_get_instance_private(dim);
891 return data->outside;
895 * adg_dim_set_detached:
896 * @dim: an #AdgDim
897 * @detached: the new detached state
899 * Sets a new state for the #AdgDim:detached flag: check the property
900 * documentation for further details.
902 * This is used only by dimensions where detaching has meaning.
903 * In some cases, such as with #AdgRDim dimensions, this property is
904 * not used.
906 * Since: 1.0
908 void
909 adg_dim_set_detached(AdgDim *dim, AdgThreeState detached)
911 g_return_if_fail(ADG_IS_DIM(dim));
913 if (_adg_set_detached(dim, detached))
914 g_object_notify((GObject *) dim, "detached");
918 * adg_dim_get_detached:
919 * @dim: an #AdgDim
921 * Gets the state of the #AdgDim:detached property: check the property
922 * documentation for further details.
924 * Returns: the current flag state.
926 * Since: 1.0
928 AdgThreeState
929 adg_dim_get_detached(AdgDim *dim)
931 AdgDimPrivate *data;
933 g_return_val_if_fail(ADG_IS_DIM(dim), ADG_THREE_STATE_UNKNOWN);
935 data = adg_dim_get_instance_private(dim);
936 return data->detached;
940 * adg_dim_set_value:
941 * @dim: an #AdgDim
942 * @value: (allow-none): the value text
944 * Explicitely sets the text to use as value. If @value
945 * is <constant>NULL</constant> or was never set, an automatic
946 * text is calculated using the format specified in the current
947 * #AdgDimStyle and getting its value by calling
948 * the <function>default_value</function> virtual method.
950 * Inside the template string, the "<>" tag (or whatever specified
951 * by the #AdgDimStyle:number-tag property) is substituted with the
952 * string returned by <function>default_value</function>.
954 * Since: 1.0
956 void
957 adg_dim_set_value(AdgDim *dim, const gchar *value)
959 g_return_if_fail(ADG_IS_DIM(dim));
961 if (_adg_set_value(dim, value))
962 g_object_notify((GObject *) dim, "value");
966 * adg_dim_get_value:
967 * @dim: an #AdgDim
969 * Gets the value text. The string is internally owned and
970 * must not be freed or modified.
972 * Returns: (transfer none): the value text.
974 * Since: 1.0
976 const gchar *
977 adg_dim_get_value(AdgDim *dim)
979 AdgDimPrivate *data;
981 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
983 data = adg_dim_get_instance_private(dim);
984 return data->value;
988 * adg_dim_get_text:
989 * @dim: an #AdgDim
990 * @value: the raw value of the quote
992 * Gets the final text to show as nominal value into the quote. The string is
993 * the same returned by adg_dim_get_value() with the tag properly expanded.
995 * The string substituted to the tag is formatted according to the
996 * #AdgDimStyle:number-format and #AdgDimStyle:number-arguments properties.
997 * See the #AdgDimStyle documentation for further details.
999 * Returns: (transfer full): the final text of the quote.
1001 * Since: 1.0
1003 gchar *
1004 adg_dim_get_text(AdgDim *dim, gdouble value)
1006 AdgDimStyle *dim_style;
1007 const gchar *format;
1008 const gchar *arguments;
1009 AdgDimReplaceData data;
1010 gchar *raw, *result;
1011 GRegex *regex;
1013 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1015 dim_style = adg_dim_get_dim_style(dim);
1016 if (dim_style == NULL) {
1017 dim_style = (AdgDimStyle *) adg_entity_style((AdgEntity *) dim,
1018 adg_dim_get_dim_dress(dim));
1021 format = adg_dim_style_get_number_format(dim_style);
1022 if (format == NULL) {
1023 return NULL;
1026 arguments = adg_dim_style_get_number_arguments(dim_style);
1027 if (arguments == NULL) {
1028 return g_strdup(format);
1031 /* Expand the values */
1032 data.dim_style = dim_style;
1033 data.value = value;
1034 data.format = format;
1035 data.argument = arguments;
1036 data.regex = g_regex_new("(?<!\\\\)%.*[eEfFgG]", G_REGEX_UNGREEDY, 0, NULL);
1037 raw = _adg_text_expand(&data);
1038 g_regex_unref(data.regex);
1040 /* Check that all format string has been parsed, otherwise there are
1041 * likely too many close parenthesis */
1042 if (*data.format != '\0') {
1043 g_free(raw);
1044 g_return_val_if_reached(NULL);
1045 return NULL;
1048 /* Substitute the escape sequences ("\%", "\(" and "\)") */
1049 regex = g_regex_new("\\\\([%()])", G_REGEX_UNGREEDY, 0, NULL);
1050 result = g_regex_replace(regex, raw, -1, 0, "\\1", 0, NULL);
1051 g_free(raw);
1052 g_regex_unref(regex);
1054 return result;
1058 * adg_dim_set_limits:
1059 * @dim: an #AdgDim
1060 * @min: (allow-none): the new minumum value
1061 * @max: (allow-none): the new maximum value
1063 * Shortcut to set both the limits at once.
1065 * Since: 1.0
1067 void
1068 adg_dim_set_limits(AdgDim *dim, const gchar *min, const gchar *max)
1070 g_return_if_fail(ADG_IS_DIM(dim));
1072 g_object_freeze_notify((GObject *) dim);
1073 adg_dim_set_min(dim, min);
1074 adg_dim_set_max(dim, max);
1075 g_object_thaw_notify((GObject *) dim);
1079 * adg_dim_set_min:
1080 * @dim: an #AdgDim
1081 * @min: (allow-none): the new minimum limit
1083 * Sets the minimum value. Use <constant>NULL</constant>
1084 * as @min to disable it.
1086 * Since: 1.0
1088 void
1089 adg_dim_set_min(AdgDim *dim, const gchar *min)
1091 g_return_if_fail(ADG_IS_DIM(dim));
1093 if (_adg_set_min(dim, min))
1094 g_object_notify((GObject *) dim, "min");
1098 * adg_dim_get_min:
1099 * @dim: an #AdgDim
1101 * Gets the minimum value text or <constant>NULL</constant>
1102 * on minimum value disabled.
1104 * The string is internally owned and must not be freed or modified.
1106 * Returns: (transfer none): the mimimum value text.
1108 * Since: 1.0
1110 const gchar *
1111 adg_dim_get_min(AdgDim *dim)
1113 AdgDimPrivate *data;
1115 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1117 data = adg_dim_get_instance_private(dim);
1118 return data->min;
1122 * adg_dim_set_max:
1123 * @dim: an #AdgDim
1124 * @max: (allow-none): the new maximum value
1126 * Sets the maximum value. Use <constant>NULL</constant>
1127 * as @max to disable it.
1129 * Since: 1.0
1131 void
1132 adg_dim_set_max(AdgDim *dim, const gchar *max)
1134 g_return_if_fail(ADG_IS_DIM(dim));
1136 if (_adg_set_max(dim, max))
1137 g_object_notify((GObject *) dim, "max");
1141 * adg_dim_get_max:
1142 * @dim: an #AdgDim
1144 * Gets the maximum value text or <constant>NULL</constant>
1145 * on maximum value disabled.
1147 * The string is internally owned and must not be freed or modified.
1149 * Returns: (transfer none): the maximum value text.
1151 * Since: 1.0
1153 const gchar *
1154 adg_dim_get_max(AdgDim *dim)
1156 AdgDimPrivate *data;
1158 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1160 data = adg_dim_get_instance_private(dim);
1161 return data->max;
1165 * adg_dim_get_quote:
1166 * @dim: an #AdgDim
1168 * Gets the quote entity, if any. This function is valid only after
1169 * the #AdgDim implementation of the arrange() virtual method has
1170 * been called.
1172 * The returned entity is owned by @dim and should not be
1173 * modified or freed.
1175 * <note><para>
1176 * This function is only useful in new dimension implementations.
1177 * </para></note>
1179 * Returns: (transfer none): the quote entity.
1181 * Since: 1.0
1183 AdgAlignment *
1184 adg_dim_get_quote(AdgDim *dim)
1186 AdgDimPrivate *data;
1188 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1190 data = adg_dim_get_instance_private(dim);
1191 return data->quote.entity;
1195 * adg_dim_quote_angle:
1196 * @dim: an #AdgDim
1197 * @angle: an angle (in radians)
1199 * <note><para>
1200 * This function is only useful in new dimension implementations.
1201 * </para></note>
1203 * Converts @angle accordling to the style of @dim. Any quote angle
1204 * should be validated by this method because every dimensioning
1205 * style has its own convention regardling the text rotation.
1207 * Returns: the angle to use (always in radians).
1209 * Since: 1.0
1211 gdouble
1212 adg_dim_quote_angle(AdgDim *dim, gdouble angle)
1214 AdgDimClass *klass;
1216 g_return_val_if_fail(ADG_IS_DIM(dim), angle);
1218 klass = ADG_DIM_GET_CLASS(dim);
1220 if (klass->quote_angle == NULL)
1221 return angle;
1223 return klass->quote_angle(angle);
1227 * adg_dim_has_geometry:
1228 * @dim: an #AdgDim
1230 * <note><para>
1231 * This function is only useful in new dimension implementations.
1232 * </para></note>
1234 * Checks if the geometry data of @dim has already been computed.
1236 * Returns: TRUE if the geometry has already been computed, FALSE otherwise.
1238 * Since: 1.0
1240 gboolean
1241 adg_dim_has_geometry(AdgDim *dim)
1243 AdgDimPrivate *data;
1245 g_return_val_if_fail(ADG_IS_DIM(dim), FALSE);
1247 data = adg_dim_get_instance_private(dim);
1248 return data->geometry.computed;
1252 * adg_dim_switch_geometry:
1253 * @dim: an #AdgDim
1254 * @computed: the new computed state
1256 * <note><para>
1257 * This function is only useful in new dimension implementations.
1258 * </para></note>
1260 * Sets the computed state of @dim to @computed. This is an internal flag that
1261 * keeps track of when the geometry data is up to date.
1263 * Since: 1.0
1265 void
1266 adg_dim_switch_geometry(AdgDim *dim, gboolean computed)
1268 AdgDimPrivate *data;
1270 g_return_if_fail(ADG_IS_DIM(dim));
1272 data = adg_dim_get_instance_private(dim);
1273 data->geometry.computed = computed;
1277 * adg_dim_get_geometry_notice:
1278 * @dim: an #AdgDim
1280 * Gets the geometry message of @dim, i.e. a notice that explains why a
1281 * geometry computation has failed. This message can be used for debugging
1282 * purposes, e.g. to know why it is not possible to draw a dimension.
1284 * Returns: the geometry notice or NULL on no notification.
1286 * Since: 1.0
1288 const gchar *
1289 adg_dim_get_geometry_notice(AdgDim *dim)
1291 AdgDimPrivate *data;
1293 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
1295 data = adg_dim_get_instance_private(dim);
1296 return data->geometry.notice;
1300 * adg_dim_set_geometry_notice:
1301 * @dim: an #AdgDim
1302 * @notice: new notice message
1304 * <note><para>
1305 * This function is only useful in new dimension implementations.
1306 * </para></note>
1308 * Sets the geometry message of @dim to @notice whenever a geometry
1309 * computation has failed. This message can later be read with
1310 * adg_dim_get_geometry_notice(): see its documentation for further details.
1312 * Since: 1.0
1314 void
1315 adg_dim_set_geometry_notice(AdgDim *dim, const gchar *notice)
1317 AdgDimPrivate *data;
1319 g_return_if_fail(ADG_IS_DIM(dim));
1321 data = adg_dim_get_instance_private(dim);
1323 if (data->geometry.notice != NULL)
1324 g_free(data->geometry.notice);
1326 data->geometry.notice = g_strdup(notice);
1330 * adg_dim_geometry_missing:
1331 * @dim: an #AdgDim
1332 * @subject: what is missing
1334 * <note><para>
1335 * This function is only useful in new dimension implementations.
1336 * </para></note>
1338 * Wrapper around adg_dim_set_geometry_notice() that sets a default
1339 * notification message when a reference is missing.
1341 * Since: 1.0
1343 void
1344 adg_dim_geometry_missing(AdgDim *dim, const gchar *subject)
1346 gchar *notice;
1348 g_return_if_fail(subject != NULL);
1350 notice = g_strdup_printf(_("'%s' is missing"), subject);
1351 adg_dim_set_geometry_notice(dim, notice);
1352 g_free(notice);
1356 * adg_dim_geometry_coincidents:
1357 * @dim: an #AdgDim
1358 * @subject: what is missing
1360 * <note><para>
1361 * This function is only useful in new dimension implementations.
1362 * </para></note>
1364 * Wrapper around adg_dim_set_geometry_notice() that sets a default
1365 * notification message when two references that must be different are
1366 * coincidents.
1368 * Since: 1.0
1370 void
1371 adg_dim_geometry_coincident(AdgDim *dim,
1372 const gchar *first, const gchar *second,
1373 const CpmlPair *pos)
1375 gchar *notice;
1377 g_return_if_fail(first != NULL);
1378 g_return_if_fail(second != NULL);
1379 g_return_if_fail(pos != NULL);
1381 notice = g_strdup_printf(_("'%s' and '%s' cannot be coincident (%lf, %lf)"),
1382 first, second, pos->x, pos->y);
1383 adg_dim_set_geometry_notice(dim, notice);
1384 g_free(notice);
1388 * adg_dim_compute_geometry:
1389 * @dim: an #AdgDim
1391 * <note><para>
1392 * This function is only useful in new dimension implementations.
1393 * </para></note>
1395 * Updates the geometry data of @dim, i.e. a set of support data (coordinates,
1396 * offsets, shifts) needed in the arrange() phase to build up the entity.
1398 * The data is cached, so further calls just return TRUE. Use
1399 * adg_entity_invalidate() to force a recomputation.
1401 * Returns: TRUE if the geometry has already been computed, FALSE otherwise.
1403 * Since: 1.0
1405 gboolean
1406 adg_dim_compute_geometry(AdgDim *dim)
1408 AdgDimPrivate *data;
1409 AdgDimClass *klass;
1411 g_return_val_if_fail(ADG_IS_DIM(dim), FALSE);
1413 data = adg_dim_get_instance_private(dim);
1414 if (data->geometry.computed)
1415 return TRUE;
1417 /* compute_geometry virtual method explicitely set to NULL means the
1418 * entity does not have any geometry data, so just set computed to TRUE */
1419 klass = ADG_DIM_GET_CLASS(dim);
1420 if (klass->compute_geometry != NULL && ! klass->compute_geometry(dim))
1421 return FALSE;
1423 data->geometry.computed = TRUE;
1424 return TRUE;
1428 static void
1429 _adg_global_changed(AdgEntity *entity)
1431 AdgDimPrivate *data = adg_dim_get_instance_private((AdgDim *) entity);
1433 if (_ADG_OLD_ENTITY_CLASS->global_changed)
1434 _ADG_OLD_ENTITY_CLASS->global_changed(entity);
1436 if (data->quote.entity)
1437 adg_entity_global_changed((AdgEntity *) data->quote.entity);
1440 static void
1441 _adg_local_changed(AdgEntity *entity)
1443 AdgDimPrivate *data = adg_dim_get_instance_private((AdgDim *) entity);
1445 if (_ADG_OLD_ENTITY_CLASS->local_changed)
1446 _ADG_OLD_ENTITY_CLASS->local_changed(entity);
1448 if (data->quote.entity)
1449 adg_entity_local_changed((AdgEntity *) data->quote.entity);
1452 static void
1453 _adg_invalidate(AdgEntity *entity)
1455 AdgDimPrivate *data = adg_dim_get_instance_private((AdgDim *) entity);
1457 if (data->quote.value) {
1458 g_object_unref(data->quote.value);
1459 data->quote.value = NULL;
1461 if (data->quote.entity)
1462 adg_entity_invalidate((AdgEntity *) data->quote.entity);
1463 if (data->ref1)
1464 adg_point_invalidate(data->ref1);
1465 if (data->ref2)
1466 adg_point_invalidate(data->ref2);
1467 if (data->pos)
1468 adg_point_invalidate(data->pos);
1470 data->geometry.computed = FALSE;
1471 if (data->geometry.notice != NULL) {
1472 g_free(data->geometry.notice);
1473 data->geometry.notice = NULL;
1476 if (_ADG_OLD_ENTITY_CLASS->invalidate)
1477 _ADG_OLD_ENTITY_CLASS->invalidate(entity);
1480 static void
1481 _adg_arrange(AdgEntity *entity)
1483 AdgDim *dim;
1484 AdgDimPrivate *data;
1485 AdgEntity *quote_entity;
1486 AdgContainer *quote_container;
1487 AdgEntity *value_entity;
1488 AdgEntity *min_entity;
1489 AdgEntity *max_entity;
1490 const CpmlPair *shift;
1491 cairo_matrix_t map;
1493 dim = (AdgDim *) entity;
1494 data = adg_dim_get_instance_private(dim);
1496 /* Resolve the dim style */
1497 if (data->dim_style == NULL)
1498 data->dim_style = (AdgDimStyle *)
1499 adg_entity_style(entity, data->dim_dress);
1501 if (data->quote.entity == NULL)
1502 data->quote.entity = g_object_new(ADG_TYPE_ALIGNMENT,
1503 "local-mix", ADG_MIX_NONE,
1504 "parent", dim, NULL);
1506 quote_entity = (AdgEntity *) data->quote.entity;
1507 quote_container = (AdgContainer *) data->quote.entity;
1509 if (data->quote.value == NULL) {
1510 AdgDimClass *klass;
1511 AdgDress dress;
1512 const gchar *tag;
1513 gchar *value;
1514 gchar *text;
1516 klass = ADG_DIM_GET_CLASS(dim);
1517 dress = adg_dim_style_get_value_dress(data->dim_style);
1518 tag = adg_dim_style_get_number_tag(data->dim_style);
1519 value = klass->default_value ? klass->default_value(dim) : NULL;
1521 data->quote.value = g_object_new(ADG_TYPE_BEST_TEXT,
1522 "local-mix", ADG_MIX_PARENT,
1523 "font-dress", dress, NULL);
1524 adg_container_add(quote_container, (AdgEntity *) data->quote.value);
1526 if (data->value)
1527 text = adg_string_replace(data->value, tag, value);
1528 else
1529 text = g_strdup(value);
1531 g_free(value);
1533 adg_textual_set_text(data->quote.value, text);
1534 g_free(text);
1537 if (data->quote.min == NULL && data->min != NULL) {
1538 AdgDress dress = adg_dim_style_get_min_dress(data->dim_style);
1540 data->quote.min = g_object_new(ADG_TYPE_BEST_TEXT,
1541 "local-mix", ADG_MIX_PARENT,
1542 "font-dress", dress, NULL);
1544 adg_container_add(quote_container, (AdgEntity *) data->quote.min);
1545 adg_textual_set_text(data->quote.min, data->min);
1548 if (data->quote.max == NULL && data->max != NULL) {
1549 AdgDress dress = adg_dim_style_get_max_dress(data->dim_style);
1551 data->quote.max = g_object_new(ADG_TYPE_BEST_TEXT,
1552 "local-mix", ADG_MIX_PARENT,
1553 "font-dress", dress, NULL);
1555 adg_container_add(quote_container, (AdgEntity *) data->quote.max);
1556 adg_textual_set_text(data->quote.max, data->max);
1559 value_entity = (AdgEntity *) data->quote.value;
1560 min_entity = (AdgEntity *) data->quote.min;
1561 max_entity = (AdgEntity *) data->quote.max;
1562 shift = adg_dim_style_get_quote_shift(data->dim_style);
1564 adg_entity_set_global_map(quote_entity, adg_matrix_identity());
1565 adg_entity_global_changed(quote_entity);
1567 cairo_matrix_init_translate(&map, 0, shift->y);
1568 adg_entity_set_global_map(value_entity, &map);
1569 adg_entity_arrange(value_entity);
1571 /* Limit values (min and max) */
1572 if (min_entity != NULL || max_entity != NULL) {
1573 const CpmlPair *limits_shift;
1574 gdouble spacing;
1575 CpmlPair size;
1576 CpmlPair org_min, org_max;
1578 limits_shift = adg_dim_style_get_limits_shift(data->dim_style);
1579 spacing = adg_dim_style_get_limits_spacing(data->dim_style);
1580 size = adg_entity_get_extents(value_entity)->size;
1581 org_min.x = size.x + limits_shift->x;
1582 org_min.y = -size.y / 2 + limits_shift->y;
1583 org_max = org_min;
1585 if (min_entity && max_entity) {
1586 /* Prearrange the min entity to get its extents */
1587 adg_entity_arrange(min_entity);
1588 size = adg_entity_get_extents(min_entity)->size;
1590 org_min.y += (size.y + spacing) / 2;
1591 org_max.y = org_min.y - size.y - spacing;
1594 if (min_entity != NULL) {
1595 cairo_matrix_init_translate(&map, org_min.x, org_min.y);
1596 adg_entity_set_global_map(min_entity, &map);
1597 adg_entity_arrange(min_entity);
1600 if (max_entity != NULL) {
1601 cairo_matrix_init_translate(&map, org_max.x, org_max.y);
1602 adg_entity_set_global_map(max_entity, &map);
1603 adg_entity_arrange(max_entity);
1608 static gboolean
1609 _adg_compute_geometry(AdgDim *dim)
1611 g_warning(_("AdgDim::compute_geometry not implemented for '%s'"),
1612 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1613 return FALSE;
1616 static gchar *
1617 _adg_default_value(AdgDim *dim)
1619 g_warning(_("AdgDim::default_value not implemented for '%s'"),
1620 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
1621 return g_strdup("undef");
1624 static gdouble
1625 _adg_quote_angle(gdouble angle)
1627 angle = cpml_angle(angle);
1629 if (angle > G_PI / 3 || angle <= -G_PI_4 * 3)
1630 angle = cpml_angle(angle + G_PI);
1632 return angle;
1635 static gboolean
1636 _adg_set_outside(AdgDim *dim, AdgThreeState outside)
1638 AdgDimPrivate *data;
1640 g_return_val_if_fail(adg_is_enum_value(outside, ADG_TYPE_THREE_STATE),
1641 FALSE);
1643 data = adg_dim_get_instance_private(dim);
1645 if (data->outside == outside)
1646 return FALSE;
1648 data->outside = outside;
1649 return TRUE;
1652 static gboolean
1653 _adg_set_detached(AdgDim *dim, AdgThreeState detached)
1655 AdgDimPrivate *data;
1657 g_return_val_if_fail(adg_is_enum_value(detached, ADG_TYPE_THREE_STATE), FALSE);
1659 data = adg_dim_get_instance_private(dim);
1661 if (data->detached == detached)
1662 return FALSE;
1664 data->detached = detached;
1665 return TRUE;
1668 static gboolean
1669 _adg_set_value(AdgDim *dim, const gchar *value)
1671 AdgDimPrivate *data = adg_dim_get_instance_private(dim);
1673 if (g_strcmp0(value, data->value) == 0)
1674 return FALSE;
1676 g_free(data->value);
1677 data->value = g_strdup(value);
1679 if (data->quote.value) {
1680 g_object_unref(data->quote.value);
1681 data->quote.value = NULL;
1684 return TRUE;
1687 static gboolean
1688 _adg_set_min(AdgDim *dim, const gchar *min)
1690 AdgDimPrivate *data = adg_dim_get_instance_private(dim);
1692 if (g_strcmp0(min, data->min) == 0)
1693 return FALSE;
1695 g_free(data->min);
1696 data->min = g_strdup(min);
1698 if (data->quote.min) {
1699 g_object_unref(data->quote.min);
1700 data->quote.min = NULL;
1703 return TRUE;
1706 static gboolean
1707 _adg_set_max(AdgDim *dim, const gchar *max)
1709 AdgDimPrivate *data = adg_dim_get_instance_private(dim);
1711 if (g_strcmp0(max, data->max) == 0)
1712 return FALSE;
1714 g_free(data->max);
1715 data->max = g_strdup(max);
1717 if (data->quote.max) {
1718 g_object_unref(data->quote.max);
1719 data->quote.max = NULL;
1722 return TRUE;
1725 static gboolean
1726 _adg_replace(const GMatchInfo *match_info, GString *result, gpointer user_data)
1728 AdgDimReplaceData *data;
1729 gdouble value;
1730 gchar *format;
1731 gchar buffer[256];
1733 data = (AdgDimReplaceData *) user_data;
1734 value = data->value;
1736 if (! adg_dim_style_convert(data->dim_style, &value, *data->argument)) {
1737 /* Conversion failed: invalid argument? */
1738 g_return_val_if_reached(TRUE);
1739 return TRUE;
1742 format = g_match_info_fetch(match_info, 0);
1744 /* This should never happen */
1745 g_return_val_if_fail(format != NULL, TRUE);
1747 /* Consume the recently used argument */
1748 ++ data->argument;
1750 g_ascii_formatd(buffer, 256, format, value);
1751 g_free(format);
1752 g_string_append(result, buffer);
1754 /* Set the valorized flag */
1755 if (value != 0) {
1756 data->valorized = ADG_THREE_STATE_ON;
1757 } else if (data->valorized == ADG_THREE_STATE_UNKNOWN) {
1758 data->valorized = ADG_THREE_STATE_OFF;
1761 return FALSE;
1764 static gchar *
1765 _adg_text_expand(AdgDimReplaceData *data)
1767 GString *result;
1768 const gchar *bog, *eog;
1769 gchar *string;
1770 AdgThreeState valorized;
1771 gssize len;
1773 valorized = ADG_THREE_STATE_UNKNOWN;
1774 result = g_string_new("");
1775 eog = adg_unescaped_strchr(data->format, ')');
1777 /* Expand eventual groups found in the same nesting level */
1778 while ((bog = adg_unescaped_strchr(data->format, '(')) != NULL) {
1779 /* If eog precedes bog, it means that bog is in another nest */
1780 if (eog != NULL && eog < bog) {
1781 break;
1784 len = bog - data->format;
1786 /* Parse template before the bog */
1787 data->valorized = ADG_THREE_STATE_UNKNOWN;
1788 string = g_regex_replace_eval(data->regex,
1789 data->format, len, 0,
1791 _adg_replace, data,
1792 NULL);
1793 valorized = OR_3S(valorized, data->valorized);
1795 data->format += len+1;
1796 g_string_append(result, string);
1797 g_free(string);
1799 /* Recursively expand the group */
1800 string = _adg_text_expand(data);
1801 valorized = OR_3S(valorized, data->valorized);
1803 g_string_append(result, string);
1804 g_free(string);
1806 /* Ensure there is a matching closing parenthesis */
1807 if (*data->format != ')') {
1808 g_string_free(result, TRUE);
1809 g_return_val_if_reached(NULL);
1810 return NULL;
1813 /* Skip the closing parenthesis */
1814 ++ data->format;
1815 eog = adg_unescaped_strchr(data->format, ')');
1818 /* Expand until closing parenthesis (End Of Group) or '\0' */
1819 len = eog == NULL ? strlen(data->format) : (eog - data->format);
1820 data->valorized = ADG_THREE_STATE_UNKNOWN;
1821 string = g_regex_replace_eval(data->regex,
1822 data->format, len, 0,
1824 _adg_replace, data,
1825 NULL);
1827 data->format += len;
1828 g_string_append(result, string);
1829 g_free(string);
1831 /* Store the final valorized state */
1832 valorized = OR_3S(valorized, data->valorized);
1833 data->valorized = valorized;
1835 /* Drop the result only if we are inside a group */
1836 if (*data->format && valorized == ADG_THREE_STATE_OFF) {
1837 g_string_free(result, TRUE);
1838 return g_strdup("");
1841 return g_string_free(result, FALSE);