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.
23 * @short_description: Root abstract class for all dimension entities
25 * The #AdgDim class is the base stub of all the dimension entities.
33 * All fields are private and should not be used directly.
34 * Use its public methods instead.
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.
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"
75 #include "adg-dim-private.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 : \
91 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(AdgDim
, adg_dim
, ADG_TYPE_ENTITY
)
108 static void _adg_dispose (GObject
*object
);
109 static void _adg_finalize (GObject
*object
);
110 static void _adg_get_property (GObject
*object
,
114 static void _adg_set_property (GObject
*object
,
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
,
131 static gboolean
_adg_set_min (AdgDim
*dim
,
133 static gboolean
_adg_set_max (AdgDim
*dim
,
135 static gboolean
_adg_replace (const GMatchInfo
*match_info
,
138 static gchar
* _adg_text_expand (AdgDimReplaceData
*data
);
142 adg_dim_class_init(AdgDimClass
*klass
)
144 GObjectClass
*gobject_class
;
145 AdgEntityClass
*entity_class
;
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"),
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"),
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"),
184 g_object_class_install_property(gobject_class
, PROP_REF2
, param
);
186 param
= g_param_spec_boxed("pos",
188 P_("The reference position of the quote: it will be combined with \"level\" to get the real quote position"),
191 g_object_class_install_property(gobject_class
, PROP_POS
, param
);
193 param
= g_param_spec_double("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,
198 g_object_class_install_property(gobject_class
, PROP_LEVEL
, param
);
200 param
= g_param_spec_enum("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
,
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
,
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"),
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"),
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"),
233 g_object_class_install_property(gobject_class
, PROP_MAX
, param
);
237 adg_dim_init(AdgDim
*dim
)
239 AdgDimPrivate
*data
= adg_dim_get_instance_private(dim
);
240 data
->dim_dress
= ADG_DRESS_DIMENSION
;
245 data
->outside
= ADG_THREE_STATE_UNKNOWN
;
246 data
->detached
= ADG_THREE_STATE_UNKNOWN
;
249 data
->geometry
.computed
= FALSE
;
250 data
->geometry
.notice
= NULL
;
252 /* This one is G_PARAM_CONSTRUCT, so set by property inizialization */
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
;
269 data
->ref1
= adg_entity_point(entity
, data
->ref1
, NULL
);
272 data
->ref2
= adg_entity_point(entity
, data
->ref2
, NULL
);
275 data
->pos
= adg_entity_point(entity
, data
->pos
, NULL
);
277 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
278 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
282 _adg_finalize(GObject
*object
)
284 AdgDimPrivate
*data
= adg_dim_get_instance_private((AdgDim
*) object
);
289 if (_ADG_OLD_OBJECT_CLASS
->finalize
)
290 _ADG_OLD_OBJECT_CLASS
->finalize(object
);
294 _adg_get_property(GObject
*object
, guint prop_id
,
295 GValue
*value
, GParamSpec
*pspec
)
297 AdgDimPrivate
*data
= adg_dim_get_instance_private((AdgDim
*) object
);
301 g_value_set_enum(value
, data
->dim_dress
);
304 g_value_set_boxed(value
, data
->ref1
);
307 g_value_set_boxed(value
, data
->ref2
);
310 g_value_set_boxed(value
, data
->pos
);
313 g_value_set_double(value
, data
->level
);
316 g_value_set_enum(value
, data
->outside
);
319 g_value_set_enum(value
, data
->detached
);
322 g_value_set_string(value
, data
->value
);
325 g_value_set_string(value
, data
->min
);
328 g_value_set_string(value
, data
->max
);
331 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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
);
346 data
->dim_dress
= g_value_get_enum(value
);
349 data
->ref1
= adg_entity_point(entity
, data
->ref1
,
350 g_value_get_boxed(value
));
353 data
->ref2
= adg_entity_point(entity
, data
->ref2
,
354 g_value_get_boxed(value
));
357 data
->pos
= adg_entity_point(entity
, data
->pos
,
358 g_value_get_boxed(value
));
361 data
->level
= g_value_get_double(value
);
364 _adg_set_outside(dim
, g_value_get_enum(value
));
367 _adg_set_detached(dim
, g_value_get_enum(value
));
370 _adg_set_value(dim
, g_value_get_string(value
));
373 _adg_set_min(dim
, g_value_get_string(value
));
376 _adg_set_max(dim
, g_value_get_string(value
));
379 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
386 * adg_dim_get_dim_style:
389 * Gets the internal cached dim style of @dim.
391 * Returns: (transfer none): the internal AdgDimStyle style.
396 adg_dim_get_dim_style(AdgDim
*dim
)
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:
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.
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:
432 * Gets the dimension dress to be used in rendering @dim.
434 * Returns: (transfer none): the current dimension dress.
439 adg_dim_get_dim_dress(AdgDim
*dim
)
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
;
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.
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:
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
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:
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.
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:
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.
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
);
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.
559 adg_dim_get_ref1(AdgDim
*dim
)
563 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
565 data
= adg_dim_get_instance_private(dim
);
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.
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:
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
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:
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.
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:
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.
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
);
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.
679 adg_dim_get_ref2(AdgDim
*dim
)
683 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
685 data
= adg_dim_get_instance_private(dim
);
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.
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:
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
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:
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.
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:
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.
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
);
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.
799 adg_dim_get_pos(AdgDim
*dim
)
803 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
805 data
= adg_dim_get_instance_private(dim
);
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).
821 adg_dim_set_level(AdgDim
*dim
, gdouble level
)
825 g_return_if_fail(ADG_IS_DIM(dim
));
827 data
= adg_dim_get_instance_private(dim
);
829 g_object_notify((GObject
*) dim
, "level");
836 * Gets the level of this dimension.
838 * Returns: the level value.
843 adg_dim_get_level(AdgDim
*dim
)
847 g_return_val_if_fail(ADG_IS_DIM(dim
), 0);
849 data
= adg_dim_get_instance_private(dim
);
854 * adg_dim_set_outside:
856 * @outside: the new outside state
858 * Sets a new state for the #AdgDim:outside flag: check the property
859 * documentation for further details.
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:
876 * Gets the state of the #AdgDim:outside property: check the property
877 * documentation for further details.
879 * Returns: the current flag state.
884 adg_dim_get_outside(AdgDim
*dim
)
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:
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
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:
921 * Gets the state of the #AdgDim:detached property: check the property
922 * documentation for further details.
924 * Returns: the current flag state.
929 adg_dim_get_detached(AdgDim
*dim
)
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
;
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>.
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");
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.
977 adg_dim_get_value(AdgDim
*dim
)
981 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
983 data
= adg_dim_get_instance_private(dim
);
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.
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
;
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
) {
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
;
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') {
1044 g_return_val_if_reached(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
);
1052 g_regex_unref(regex
);
1058 * adg_dim_set_limits:
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.
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
);
1081 * @min: (allow-none): the new minimum limit
1083 * Sets the minimum value. Use <constant>NULL</constant>
1084 * as @min to disable it.
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");
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.
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
);
1124 * @max: (allow-none): the new maximum value
1126 * Sets the maximum value. Use <constant>NULL</constant>
1127 * as @max to disable it.
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");
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.
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
);
1165 * adg_dim_get_quote:
1168 * Gets the quote entity, if any. This function is valid only after
1169 * the #AdgDim implementation of the arrange() virtual method has
1172 * The returned entity is owned by @dim and should not be
1173 * modified or freed.
1176 * This function is only useful in new dimension implementations.
1179 * Returns: (transfer none): the quote entity.
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:
1197 * @angle: an angle (in radians)
1200 * This function is only useful in new dimension implementations.
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).
1212 adg_dim_quote_angle(AdgDim
*dim
, gdouble angle
)
1216 g_return_val_if_fail(ADG_IS_DIM(dim
), angle
);
1218 klass
= ADG_DIM_GET_CLASS(dim
);
1220 if (klass
->quote_angle
== NULL
)
1223 return klass
->quote_angle(angle
);
1227 * adg_dim_has_geometry:
1231 * This function is only useful in new dimension implementations.
1234 * Checks if the geometry data of @dim has already been computed.
1236 * Returns: TRUE if the geometry has already been computed, FALSE otherwise.
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:
1254 * @computed: the new computed state
1257 * This function is only useful in new dimension implementations.
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.
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:
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.
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:
1302 * @notice: new notice message
1305 * This function is only useful in new dimension implementations.
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.
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:
1332 * @subject: what is missing
1335 * This function is only useful in new dimension implementations.
1338 * Wrapper around adg_dim_set_geometry_notice() that sets a default
1339 * notification message when a reference is missing.
1344 adg_dim_geometry_missing(AdgDim
*dim
, const gchar
*subject
)
1348 g_return_if_fail(subject
!= NULL
);
1350 notice
= g_strdup_printf(_("'%s' is missing"), subject
);
1351 adg_dim_set_geometry_notice(dim
, notice
);
1356 * adg_dim_geometry_coincidents:
1358 * @subject: what is missing
1361 * This function is only useful in new dimension implementations.
1364 * Wrapper around adg_dim_set_geometry_notice() that sets a default
1365 * notification message when two references that must be different are
1371 adg_dim_geometry_coincident(AdgDim
*dim
,
1372 const gchar
*first
, const gchar
*second
,
1373 const CpmlPair
*pos
)
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
);
1388 * adg_dim_compute_geometry:
1392 * This function is only useful in new dimension implementations.
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.
1406 adg_dim_compute_geometry(AdgDim
*dim
)
1408 AdgDimPrivate
*data
;
1411 g_return_val_if_fail(ADG_IS_DIM(dim
), FALSE
);
1413 data
= adg_dim_get_instance_private(dim
);
1414 if (data
->geometry
.computed
)
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
))
1423 data
->geometry
.computed
= TRUE
;
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
);
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
);
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
);
1464 adg_point_invalidate(data
->ref1
);
1466 adg_point_invalidate(data
->ref2
);
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
);
1481 _adg_arrange(AdgEntity
*entity
)
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
;
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
) {
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
);
1527 text
= adg_string_replace(data
->value
, tag
, value
);
1529 text
= g_strdup(value
);
1533 adg_textual_set_text(data
->quote
.value
, 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
;
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
;
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
);
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
)));
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");
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
);
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
),
1643 data
= adg_dim_get_instance_private(dim
);
1645 if (data
->outside
== outside
)
1648 data
->outside
= outside
;
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
)
1664 data
->detached
= detached
;
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)
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
;
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)
1696 data
->min
= g_strdup(min
);
1698 if (data
->quote
.min
) {
1699 g_object_unref(data
->quote
.min
);
1700 data
->quote
.min
= NULL
;
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)
1715 data
->max
= g_strdup(max
);
1717 if (data
->quote
.max
) {
1718 g_object_unref(data
->quote
.max
);
1719 data
->quote
.max
= NULL
;
1726 _adg_replace(const GMatchInfo
*match_info
, GString
*result
, gpointer user_data
)
1728 AdgDimReplaceData
*data
;
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
);
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 */
1750 g_ascii_formatd(buffer
, 256, format
, value
);
1752 g_string_append(result
, buffer
);
1754 /* Set the valorized flag */
1756 data
->valorized
= ADG_THREE_STATE_ON
;
1757 } else if (data
->valorized
== ADG_THREE_STATE_UNKNOWN
) {
1758 data
->valorized
= ADG_THREE_STATE_OFF
;
1765 _adg_text_expand(AdgDimReplaceData
*data
)
1768 const gchar
*bog
, *eog
;
1770 AdgThreeState valorized
;
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
) {
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,
1793 valorized
= OR_3S(valorized
, data
->valorized
);
1795 data
->format
+= len
+1;
1796 g_string_append(result
, 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
);
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
);
1813 /* Skip the closing parenthesis */
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,
1827 data
->format
+= len
;
1828 g_string_append(result
, 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
);