1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2015 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
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"
79 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_dim_parent_class)
80 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_dim_parent_class)
83 G_DEFINE_ABSTRACT_TYPE(AdgDim
, adg_dim
, ADG_TYPE_ENTITY
)
100 static void _adg_dispose (GObject
*object
);
101 static void _adg_finalize (GObject
*object
);
102 static void _adg_get_property (GObject
*object
,
106 static void _adg_set_property (GObject
*object
,
110 static void _adg_global_changed (AdgEntity
*entity
);
111 static void _adg_local_changed (AdgEntity
*entity
);
112 static void _adg_invalidate (AdgEntity
*entity
);
113 static void _adg_arrange (AdgEntity
*entity
);
114 static gchar
* _adg_default_value (AdgDim
*dim
);
115 static gdouble
_adg_quote_angle (gdouble angle
);
116 static gboolean
_adg_set_outside (AdgDim
*dim
,
117 AdgThreeState outside
);
118 static gboolean
_adg_set_detached (AdgDim
*dim
,
119 AdgThreeState detached
);
120 static gboolean
_adg_set_value (AdgDim
*dim
,
122 static gboolean
_adg_set_min (AdgDim
*dim
,
124 static gboolean
_adg_set_max (AdgDim
*dim
,
126 static gboolean
_adg_replace (const GMatchInfo
*match_info
,
132 adg_dim_class_init(AdgDimClass
*klass
)
134 GObjectClass
*gobject_class
;
135 AdgEntityClass
*entity_class
;
138 gobject_class
= (GObjectClass
*) klass
;
139 entity_class
= (AdgEntityClass
*) klass
;
141 g_type_class_add_private(klass
, sizeof(AdgDimPrivate
));
143 gobject_class
->dispose
= _adg_dispose
;
144 gobject_class
->finalize
= _adg_finalize
;
145 gobject_class
->get_property
= _adg_get_property
;
146 gobject_class
->set_property
= _adg_set_property
;
148 entity_class
->global_changed
= _adg_global_changed
;
149 entity_class
->local_changed
= _adg_local_changed
;
150 entity_class
->invalidate
= _adg_invalidate
;
151 entity_class
->arrange
= _adg_arrange
;
153 klass
->quote_angle
= _adg_quote_angle
;
154 klass
->default_value
= _adg_default_value
;
156 param
= adg_param_spec_dress("dim-dress",
157 P_("Dimension Dress"),
158 P_("The dress to use for rendering this dimension"),
161 g_object_class_install_property(gobject_class
, PROP_DIM_DRESS
, param
);
163 param
= g_param_spec_boxed("ref1",
164 P_("First Reference"),
165 P_("First reference point of the dimension"),
168 g_object_class_install_property(gobject_class
, PROP_REF1
, param
);
170 param
= g_param_spec_boxed("ref2",
171 P_("Second Reference"),
172 P_("Second reference point of the dimension"),
175 g_object_class_install_property(gobject_class
, PROP_REF2
, param
);
177 param
= g_param_spec_boxed("pos",
179 P_("The reference position of the quote: it will be combined with \"level\" to get the real quote position"),
182 g_object_class_install_property(gobject_class
, PROP_POS
, param
);
184 param
= g_param_spec_double("level",
186 P_("The dimension level, that is the factor to multiply the baseline spacing (defined in the dimension style) to get the offset from pos where the quote should be rendered"),
187 -G_MAXDOUBLE
, G_MAXDOUBLE
, 1.0,
189 g_object_class_install_property(gobject_class
, PROP_LEVEL
, param
);
191 param
= g_param_spec_enum("outside",
193 P_("Whether the arrows must be inside the extension lines (ADG_THREE_STATE_OFF), must be extended outside the extension lines (ADG_THREE_STATE_ON) or should be automatically handled depending on the available space"),
194 ADG_TYPE_THREE_STATE
, ADG_THREE_STATE_UNKNOWN
,
196 g_object_class_install_property(gobject_class
, PROP_OUTSIDE
, param
);
198 param
= g_param_spec_enum("detached",
199 P_("Detached Quote"),
200 P_("Where the quote must be positioned: in the middle of the base line (ADG_THREE_STATE_OFF), near the pos point (ADG_THREE_STATE_ON) or should be automatically deducted depending on the available space"),
201 ADG_TYPE_THREE_STATE
, ADG_THREE_STATE_UNKNOWN
,
203 g_object_class_install_property(gobject_class
, PROP_DETACHED
, param
);
205 param
= g_param_spec_string("value",
206 P_("Value Template"),
207 P_("The template string to be used for generating the set value of the quote"),
209 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
);
210 g_object_class_install_property(gobject_class
, PROP_VALUE
, param
);
212 param
= g_param_spec_string("min",
213 P_("Minimum Value or Low Tolerance"),
214 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
217 g_object_class_install_property(gobject_class
, PROP_MIN
, param
);
219 param
= g_param_spec_string("max",
220 P_("Maximum Value or High Tolerance"),
221 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
224 g_object_class_install_property(gobject_class
, PROP_MAX
, param
);
228 adg_dim_init(AdgDim
*dim
)
230 AdgDimPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(dim
, ADG_TYPE_DIM
,
233 data
->dim_dress
= ADG_DRESS_DIMENSION
;
238 data
->outside
= ADG_THREE_STATE_UNKNOWN
;
239 data
->detached
= ADG_THREE_STATE_UNKNOWN
;
244 /* This one is G_PARAM_CONSTRUCT, so set by property inizialization */
252 _adg_dispose(GObject
*object
)
257 entity
= (AdgEntity
*) object
;
258 data
= ((AdgDim
*) object
)->data
;
260 if (data
->quote
.entity
) {
261 g_object_unref(data
->quote
.entity
);
262 data
->quote
.entity
= NULL
;
266 data
->ref1
= adg_entity_point(entity
, data
->ref1
, NULL
);
269 data
->ref2
= adg_entity_point(entity
, data
->ref2
, NULL
);
272 data
->pos
= adg_entity_point(entity
, data
->pos
, NULL
);
274 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
275 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
279 _adg_finalize(GObject
*object
)
281 AdgDimPrivate
*data
= ((AdgDim
*) object
)->data
;
287 if (_ADG_OLD_OBJECT_CLASS
->finalize
)
288 _ADG_OLD_OBJECT_CLASS
->finalize(object
);
292 _adg_get_property(GObject
*object
, guint prop_id
,
293 GValue
*value
, GParamSpec
*pspec
)
295 AdgDimPrivate
*data
= ((AdgDim
*) object
)->data
;
299 g_value_set_enum(value
, data
->dim_dress
);
302 g_value_set_boxed(value
, data
->ref1
);
305 g_value_set_boxed(value
, data
->ref2
);
308 g_value_set_boxed(value
, data
->pos
);
311 g_value_set_double(value
, data
->level
);
314 g_value_set_enum(value
, data
->outside
);
317 g_value_set_enum(value
, data
->detached
);
320 g_value_set_string(value
, data
->value
);
323 g_value_set_string(value
, data
->min
);
326 g_value_set_string(value
, data
->max
);
329 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
335 _adg_set_property(GObject
*object
, guint prop_id
,
336 const GValue
*value
, GParamSpec
*pspec
)
342 entity
= (AdgEntity
*) object
;
343 dim
= (AdgDim
*) object
;
348 data
->dim_dress
= g_value_get_enum(value
);
351 data
->ref1
= adg_entity_point(entity
, data
->ref1
,
352 g_value_get_boxed(value
));
355 data
->ref2
= adg_entity_point(entity
, data
->ref2
,
356 g_value_get_boxed(value
));
359 data
->pos
= adg_entity_point(entity
, data
->pos
,
360 g_value_get_boxed(value
));
363 data
->level
= g_value_get_double(value
);
366 _adg_set_outside(dim
, g_value_get_enum(value
));
369 _adg_set_detached(dim
, g_value_get_enum(value
));
372 _adg_set_value(dim
, g_value_get_string(value
));
375 _adg_set_min(dim
, g_value_get_string(value
));
378 _adg_set_max(dim
, g_value_get_string(value
));
381 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
388 * adg_dim_set_dim_dress:
390 * @dress: the new #AdgDress to use
392 * Sets a new dimension dress to @dim. The new dress must be
393 * related to the original dress for this property: you cannot
394 * set a dress used for line styles to a dress managing fonts.
396 * The check is done by calling adg_dress_are_related() with
397 * @dress and the previous dress as arguments. Check out its
398 * documentation for details on what is a related dress.
403 adg_dim_set_dim_dress(AdgDim
*dim
, AdgDress dress
)
405 g_return_if_fail(ADG_IS_DIM(dim
));
406 g_object_set(dim
, "dim-dress", dress
, NULL
);
410 * adg_dim_get_dim_dress:
413 * Gets the dimension dress to be used in rendering @dim.
415 * Returns: (transfer none): the current dimension dress.
420 adg_dim_get_dim_dress(AdgDim
*dim
)
424 g_return_val_if_fail(ADG_IS_DIM(dim
), ADG_DRESS_UNDEFINED
);
428 return data
->dim_dress
;
434 * @ref1: the new point to use as first reference
436 * Sets the #AdgDim:ref1 property to @ref1. The old point
437 * is silently discarded, unreferencing its model if that
438 * point was bound to a named pair (hence, possibly destroying
439 * the model if this was the last reference).
441 * @ref1 can be <constant>NULL</constant>, in which case the
442 * point is destroyed.
447 adg_dim_set_ref1(AdgDim
*dim
, const AdgPoint
*ref1
)
449 g_return_if_fail(ADG_IS_DIM(dim
));
450 g_object_set(dim
, "ref1", ref1
, NULL
);
454 * adg_dim_set_ref1_explicit:
456 * @x: x coordinate of the first reference point
457 * @y: y coordinate of the first reference point
459 * Sets the #AdgDim:ref1 property to the (@x, @y) explicit
460 * coordinates. The old point is silently discarded,
461 * unreferencing its model if that point was bound to a named
462 * pair (hence, possibly destroying the model if this was the
468 adg_dim_set_ref1_explicit(AdgDim
*dim
, gdouble x
, gdouble y
)
470 AdgPoint
*point
= adg_point_new();
472 adg_point_set_pair_explicit(point
, x
, y
);
473 adg_dim_set_ref1(dim
, point
);
475 adg_point_destroy(point
);
479 * adg_dim_set_ref1_from_pair:
481 * @ref1: the coordinates pair of the first reference point
483 * Convenient function to set the #AdgDim:ref1 property using a
484 * pair instead of explicit coordinates.
489 adg_dim_set_ref1_from_pair(AdgDim
*dim
, const CpmlPair
*ref1
)
491 g_return_if_fail(ref1
!= NULL
);
493 adg_dim_set_ref1_explicit(dim
, ref1
->x
, ref1
->y
);
497 * adg_dim_set_ref1_from_model:
499 * @model: the source #AdgModel
500 * @ref1: a named pair in @model
502 * Binds #AdgDim:ref1 to the @ref1 named pair of @model. If @model
503 * is <constant>NULL</constant>, the point will be unset. In any case,
504 * the old point is silently discarded, unreferencing its model
505 * if that point was bound to a named pair (hence, possibly destroying
506 * the model if this was the last reference).
508 * The assignment is lazy so @ref1 could be not be present in @model.
509 * Anyway, at the first access to this point an error will be raised
510 * if the named pair is still missing.
515 adg_dim_set_ref1_from_model(AdgDim
*dim
, AdgModel
*model
, const gchar
*ref1
)
517 AdgPoint
*point
= adg_point_new();
519 adg_point_set_pair_from_model(point
, model
, ref1
);
520 adg_dim_set_ref1(dim
, point
);
522 adg_point_destroy(point
);
529 * Gets the #AdgDim:ref1 point of @dim.
531 * The returned point is internally owned and must not be freed
532 * or modified. Anyway it is not const because a call to
533 * adg_point_update() with the returned value must be able to
534 * modify the internal cache.
536 * Returns: (transfer none): the first reference point.
541 adg_dim_get_ref1(AdgDim
*dim
)
545 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
555 * @ref2: the new point to use as second reference
557 * Sets the #AdgDim:ref2 property to @ref2. The old point
558 * is silently discarded, unreferencing its model if that
559 * point was bound to a named pair (hence, possibly destroying
560 * the model if it was the last reference).
562 * @ref2 can be <constant>NULL</constant>, in which case
563 * the point is destroyed.
568 adg_dim_set_ref2(AdgDim
*dim
, const AdgPoint
*ref2
)
570 g_return_if_fail(ADG_IS_DIM(dim
));
571 g_object_set(dim
, "ref2", ref2
, NULL
);
575 * adg_dim_set_ref2_explicit:
577 * @x: x coordinate of the second reference point
578 * @y: y coordinate of the second reference point
580 * Sets the #AdgDim:ref2 property to the (@x, @y) explicit
581 * coordinates. The old point is silently discarded,
582 * unreferencing its model if that point was bound to a named
583 * pair (hence, possibly destroying the model if this was the
589 adg_dim_set_ref2_explicit(AdgDim
*dim
, gdouble x
, gdouble y
)
591 AdgPoint
*point
= adg_point_new();
593 adg_point_set_pair_explicit(point
, x
, y
);
594 adg_dim_set_ref2(dim
, point
);
596 adg_point_destroy(point
);
600 * adg_dim_set_ref2_from_pair:
602 * @ref2: the coordinates pair of the second reference point
604 * Convenient function to set the #AdgDim:ref2 property using a
605 * pair instead of explicit coordinates.
610 adg_dim_set_ref2_from_pair(AdgDim
*dim
, const CpmlPair
*ref2
)
612 g_return_if_fail(ref2
!= NULL
);
614 adg_dim_set_ref2_explicit(dim
, ref2
->x
, ref2
->y
);
618 * adg_dim_set_ref2_from_model:
620 * @model: the source #AdgModel
621 * @ref2: a named pair in @model
623 * Binds #AdgDim:ref2 to the @ref2 named pair of @model. If @model
624 * is <constant>NULL</constant>, the point will be unset. In any
625 * case, the old point is silently discarded, unreferencing its
626 * model if that point was bound to a named pair (hence, possibly
627 * destroying the model if this was the last reference).
629 * The assignment is lazy so @ref2 could be not be present in @model.
630 * Anyway, at the first access to this point an error will be raised
631 * if the named pair is still missing.
636 adg_dim_set_ref2_from_model(AdgDim
*dim
, AdgModel
*model
, const gchar
*ref2
)
638 AdgPoint
*point
= adg_point_new();
640 adg_point_set_pair_from_model(point
, model
, ref2
);
641 adg_dim_set_ref2(dim
, point
);
643 adg_point_destroy(point
);
650 * Gets the #AdgDim:ref2 point of @dim.
652 * The returned point is internally owned and must not be freed
653 * or modified. Anyway it is not const because a call to
654 * adg_point_update() with the returned value must be able to
655 * modify the internal cache.
657 * Returns: (transfer none): the second reference point.
662 adg_dim_get_ref2(AdgDim
*dim
)
666 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
676 * @pos: the new point to use as position
678 * Sets the #AdgDim:pos property of @dim to @pos. The old point
679 * is silently discarded, unreferencing its model if that
680 * point was bound to a named pair (hence, possibly destroying
681 * the model if it was the last reference).
683 * @pos can be <constant>NULL</constant>, in which case the
684 * point is destroyed.
689 adg_dim_set_pos(AdgDim
*dim
, const AdgPoint
*pos
)
691 g_return_if_fail(ADG_IS_DIM(dim
));
692 g_object_set(dim
, "pos", pos
, NULL
);
696 * adg_dim_set_pos_explicit:
698 * @x: x coordinate of the position
699 * @y: y coordinate of the position
701 * Sets the #AdgDim:pos property to the (@x, @y) explicit
702 * coordinates. The old point is silently discarded,
703 * unreferencing its model if that point was bound to a named
704 * pair (hence, possibly destroying the model if this was the
710 adg_dim_set_pos_explicit(AdgDim
*dim
, gdouble x
, gdouble y
)
712 AdgPoint
*point
= adg_point_new();
714 adg_point_set_pair_explicit(point
, x
, y
);
715 adg_dim_set_pos(dim
, point
);
717 adg_point_destroy(point
);
721 * adg_dim_set_pos_from_pair:
723 * @pos: the coordinates pair of the position point
725 * Convenient function to set the #AdgDim:pos property using a
726 * pair instead of explicit coordinates.
731 adg_dim_set_pos_from_pair(AdgDim
*dim
, const CpmlPair
*pos
)
733 g_return_if_fail(pos
!= NULL
);
735 adg_dim_set_pos_explicit(dim
, pos
->x
, pos
->y
);
739 * adg_dim_set_pos_from_model:
741 * @model: the source #AdgModel
742 * @pos: a named pair in @model
744 * Binds #AdgDim:pos to the @pos named pair of @model. If @model
745 * is <constant>NULL</constant>, the point will be unset. In any
746 * case, the old point is silently discarded, unreferencing its
747 * model if that point was bound to a named pair (hence,
748 * possibly destroying the model if this was the last reference).
750 * The assignment is lazy so @pos could be not be present in @model.
751 * Anyway, at the first access to this point an error will be raised
752 * if the named pair is still missing.
757 adg_dim_set_pos_from_model(AdgDim
*dim
, AdgModel
*model
, const gchar
*pos
)
759 AdgPoint
*point
= adg_point_new();
761 adg_point_set_pair_from_model(point
, model
, pos
);
762 adg_dim_set_pos(dim
, point
);
764 adg_point_destroy(point
);
771 * Gets the #AdgDim:pos point of @dim.
773 * The returned point is internally owned and must not be freed
774 * or modified. Anyway it is not const because a call to
775 * adg_point_update() with the returned value must be able to
776 * modify the internal cache.
778 * Returns: (transfer none): the position point.
783 adg_dim_get_pos(AdgDim
*dim
)
787 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
797 * @level: the new level
799 * Sets a new level for this dimension. The level is used to
800 * stack the quotes using a spacing value from dim_style
801 * (specified in global space).
806 adg_dim_set_level(AdgDim
*dim
, gdouble level
)
810 g_return_if_fail(ADG_IS_DIM(dim
));
815 g_object_notify((GObject
*) dim
, "level");
822 * Gets the level of this dimension.
824 * Returns: the level value.
829 adg_dim_get_level(AdgDim
*dim
)
833 g_return_val_if_fail(ADG_IS_DIM(dim
), 0);
841 * adg_dim_set_outside:
843 * @outside: the new outside state
845 * Sets a new state for the #AdgDim:outside flag: check the property
846 * documentation for further details.
851 adg_dim_set_outside(AdgDim
*dim
, AdgThreeState outside
)
853 g_return_if_fail(ADG_IS_DIM(dim
));
855 if (_adg_set_outside(dim
, outside
))
856 g_object_notify((GObject
*) dim
, "outside");
860 * adg_dim_get_outside:
863 * Gets the state of the #AdgDim:outside property: check the property
864 * documentation for further details.
866 * Returns: the current flag state.
871 adg_dim_get_outside(AdgDim
*dim
)
875 g_return_val_if_fail(ADG_IS_DIM(dim
), ADG_THREE_STATE_UNKNOWN
);
879 return data
->outside
;
883 * adg_dim_set_detached:
885 * @detached: the new detached state
887 * Sets a new state for the #AdgDim:detached flag: check the property
888 * documentation for further details.
890 * This is used only by dimensions where detaching has meaning.
891 * In some cases, such as with #AdgRDim dimensions, this property is
897 adg_dim_set_detached(AdgDim
*dim
, AdgThreeState detached
)
899 g_return_if_fail(ADG_IS_DIM(dim
));
901 if (_adg_set_detached(dim
, detached
))
902 g_object_notify((GObject
*) dim
, "detached");
906 * adg_dim_get_detached:
909 * Gets the state of the #AdgDim:detached property: check the property
910 * documentation for further details.
912 * Returns: the current flag state.
917 adg_dim_get_detached(AdgDim
*dim
)
921 g_return_val_if_fail(ADG_IS_DIM(dim
), ADG_THREE_STATE_UNKNOWN
);
925 return data
->detached
;
931 * @value: (allow-none): the value text
933 * Explicitely sets the text to use as value. If @value
934 * is <constant>NULL</constant> or was never set, an automatic
935 * text is calculated using the format specified in the current
936 * #AdgDimStyle and getting its value by calling
937 * the <function>default_value</function> virtual method.
939 * Inside the template string, the "<>" tag (or whatever specified
940 * by the #AdgDimStyle:number-tag property) is substituted with the
941 * string returned by <function>default_value</function>.
946 adg_dim_set_value(AdgDim
*dim
, const gchar
*value
)
948 g_return_if_fail(ADG_IS_DIM(dim
));
950 if (_adg_set_value(dim
, value
))
951 g_object_notify((GObject
*) dim
, "value");
958 * Gets the value text. The string is internally owned and
959 * must not be freed or modified.
961 * Returns: (transfer none): the value text.
966 adg_dim_get_value(AdgDim
*dim
)
970 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
980 * @value: the raw value of the quote
982 * Gets the final text to show as nominal value into the quote. The string is
983 * the same returned by adg_dim_get_value() with the tag properly expanded.
985 * The string substituted to the tag is formatted according to the
986 * #AdgDimStyle:number-format and #AdgDimStyle:number-arguments properties.
987 * See the #AdgDimStyle documentation for further details.
989 * Returns: (transfer full): the final text of the quote.
994 adg_dim_get_text(AdgDim
*dim
, gdouble value
)
996 AdgDimStyle
*dim_style
;
998 const gchar
*arguments
;
999 AdgDimReplaceData replace_data
;
1003 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
1005 dim_style
= _ADG_GET_DIM_STYLE(dim
);
1006 if (dim_style
== NULL
) {
1007 dim_style
= (AdgDimStyle
*) adg_entity_style((AdgEntity
*) dim
,
1008 adg_dim_get_dim_dress(dim
));
1011 format
= adg_dim_style_get_number_format(dim_style
);
1012 if (format
== NULL
) {
1016 arguments
= adg_dim_style_get_number_arguments(dim_style
);
1017 if (arguments
== NULL
) {
1018 return g_strdup(format
);
1021 replace_data
.dim_style
= dim_style
;
1022 replace_data
.value
= value
;
1023 replace_data
.argument
= arguments
;
1025 regex
= g_regex_new("(?<!%)%.*[eEfFgG]", G_REGEX_UNGREEDY
, 0, NULL
);
1026 result
= g_regex_replace_eval(regex
, format
, -1, 0, 0,
1027 _adg_replace
, &replace_data
,
1029 g_regex_unref(regex
);
1035 * adg_dim_set_limits:
1037 * @min: (allow-none): the new minumum value
1038 * @max: (allow-none): the new maximum value
1040 * Shortcut to set both the limits at once.
1045 adg_dim_set_limits(AdgDim
*dim
, const gchar
*min
, const gchar
*max
)
1047 g_return_if_fail(ADG_IS_DIM(dim
));
1049 g_object_freeze_notify((GObject
*) dim
);
1050 adg_dim_set_min(dim
, min
);
1051 adg_dim_set_max(dim
, max
);
1052 g_object_thaw_notify((GObject
*) dim
);
1058 * @min: (allow-none): the new minimum limit
1060 * Sets the minimum value. Use <constant>NULL</constant>
1061 * as @min to disable it.
1066 adg_dim_set_min(AdgDim
*dim
, const gchar
*min
)
1068 g_return_if_fail(ADG_IS_DIM(dim
));
1070 if (_adg_set_min(dim
, min
))
1071 g_object_notify((GObject
*) dim
, "min");
1078 * Gets the minimum value text or <constant>NULL</constant>
1079 * on minimum value disabled.
1081 * The string is internally owned and must not be freed or modified.
1083 * Returns: (transfer none): the mimimum value text.
1088 adg_dim_get_min(AdgDim
*dim
)
1090 AdgDimPrivate
*data
;
1092 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
1102 * @max: (allow-none): the new maximum value
1104 * Sets the maximum value. Use <constant>NULL</constant>
1105 * as @max to disable it.
1110 adg_dim_set_max(AdgDim
*dim
, const gchar
*max
)
1112 g_return_if_fail(ADG_IS_DIM(dim
));
1114 if (_adg_set_max(dim
, max
))
1115 g_object_notify((GObject
*) dim
, "max");
1122 * Gets the maximum value text or <constant>NULL</constant>
1123 * on maximum value disabled.
1125 * The string is internally owned and must not be freed or modified.
1127 * Returns: (transfer none): the maximum value text.
1132 adg_dim_get_max(AdgDim
*dim
)
1134 AdgDimPrivate
*data
;
1136 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
1144 * adg_dim_get_quote:
1147 * Gets the quote entity, if any. This function is valid only after
1148 * the #AdgDim implementation of the arrange() virtual method has
1151 * The returned entity is owned by @dim and should not be
1152 * modified or freed.
1155 * This function is only useful in new dimension implementations.
1158 * Returns: (transfer none): the quote entity.
1163 adg_dim_get_quote(AdgDim
*dim
)
1165 AdgDimPrivate
*data
;
1167 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
1171 return data
->quote
.entity
;
1175 * adg_dim_quote_angle:
1177 * @angle: an angle (in radians)
1180 * This function is only useful in new dimension implementations.
1183 * Converts @angle accordling to the style of @dim. Any quote angle
1184 * should be validated by this method because every dimensioning
1185 * style has its own convention regardling the text rotation.
1187 * Returns: the angle to use (always in radians).
1192 adg_dim_quote_angle(AdgDim
*dim
, gdouble angle
)
1196 g_return_val_if_fail(ADG_IS_DIM(dim
), angle
);
1198 klass
= ADG_DIM_GET_CLASS(dim
);
1200 if (klass
->quote_angle
== NULL
)
1203 return klass
->quote_angle(angle
);
1208 _adg_global_changed(AdgEntity
*entity
)
1210 AdgDimPrivate
*data
= ((AdgDim
*) entity
)->data
;
1212 if (_ADG_OLD_ENTITY_CLASS
->global_changed
)
1213 _ADG_OLD_ENTITY_CLASS
->global_changed(entity
);
1215 if (data
->quote
.entity
)
1216 adg_entity_global_changed((AdgEntity
*) data
->quote
.entity
);
1220 _adg_local_changed(AdgEntity
*entity
)
1222 AdgDimPrivate
*data
= ((AdgDim
*) entity
)->data
;
1224 if (_ADG_OLD_ENTITY_CLASS
->local_changed
)
1225 _ADG_OLD_ENTITY_CLASS
->local_changed(entity
);
1227 if (data
->quote
.entity
)
1228 adg_entity_local_changed((AdgEntity
*) data
->quote
.entity
);
1232 _adg_invalidate(AdgEntity
*entity
)
1234 AdgDimPrivate
*data
= ((AdgDim
*) entity
)->data
;
1236 if (data
->quote
.value
) {
1237 g_object_unref(data
->quote
.value
);
1238 data
->quote
.value
= NULL
;
1240 if (data
->quote
.entity
)
1241 adg_entity_invalidate((AdgEntity
*) data
->quote
.entity
);
1243 adg_point_invalidate(data
->ref1
);
1245 adg_point_invalidate(data
->ref2
);
1247 adg_point_invalidate(data
->pos
);
1249 if (_ADG_OLD_ENTITY_CLASS
->invalidate
)
1250 _ADG_OLD_ENTITY_CLASS
->invalidate(entity
);
1254 _adg_arrange(AdgEntity
*entity
)
1257 AdgDimPrivate
*data
;
1258 AdgEntity
*quote_entity
;
1259 AdgContainer
*quote_container
;
1260 AdgEntity
*value_entity
;
1261 AdgEntity
*min_entity
;
1262 AdgEntity
*max_entity
;
1263 const CpmlPair
*shift
;
1266 dim
= (AdgDim
*) entity
;
1269 /* Resolve the dim style */
1270 if (data
->dim_style
== NULL
)
1271 data
->dim_style
= (AdgDimStyle
*)
1272 adg_entity_style(entity
, data
->dim_dress
);
1274 if (data
->quote
.entity
== NULL
)
1275 data
->quote
.entity
= g_object_new(ADG_TYPE_ALIGNMENT
,
1276 "local-mix", ADG_MIX_NONE
,
1277 "parent", dim
, NULL
);
1279 quote_entity
= (AdgEntity
*) data
->quote
.entity
;
1280 quote_container
= (AdgContainer
*) data
->quote
.entity
;
1282 if (data
->quote
.value
== NULL
) {
1289 klass
= ADG_DIM_GET_CLASS(dim
);
1290 dress
= adg_dim_style_get_value_dress(data
->dim_style
);
1291 tag
= adg_dim_style_get_number_tag(data
->dim_style
);
1292 value
= klass
->default_value
? klass
->default_value(dim
) : NULL
;
1294 data
->quote
.value
= g_object_new(ADG_TYPE_BEST_TEXT
,
1295 "local-mix", ADG_MIX_PARENT
,
1296 "font-dress", dress
, NULL
);
1297 adg_container_add(quote_container
, (AdgEntity
*) data
->quote
.value
);
1300 text
= adg_string_replace(data
->value
, tag
, value
);
1302 text
= g_strdup(value
);
1306 adg_textual_set_text(data
->quote
.value
, text
);
1310 if (data
->quote
.min
== NULL
&& data
->min
!= NULL
) {
1311 AdgDress dress
= adg_dim_style_get_min_dress(data
->dim_style
);
1313 data
->quote
.min
= g_object_new(ADG_TYPE_BEST_TEXT
,
1314 "local-mix", ADG_MIX_PARENT
,
1315 "font-dress", dress
, NULL
);
1317 adg_container_add(quote_container
, (AdgEntity
*) data
->quote
.min
);
1318 adg_textual_set_text(data
->quote
.min
, data
->min
);
1321 if (data
->quote
.max
== NULL
&& data
->max
!= NULL
) {
1322 AdgDress dress
= adg_dim_style_get_max_dress(data
->dim_style
);
1324 data
->quote
.max
= g_object_new(ADG_TYPE_BEST_TEXT
,
1325 "local-mix", ADG_MIX_PARENT
,
1326 "font-dress", dress
, NULL
);
1328 adg_container_add(quote_container
, (AdgEntity
*) data
->quote
.max
);
1329 adg_textual_set_text(data
->quote
.max
, data
->max
);
1332 value_entity
= (AdgEntity
*) data
->quote
.value
;
1333 min_entity
= (AdgEntity
*) data
->quote
.min
;
1334 max_entity
= (AdgEntity
*) data
->quote
.max
;
1335 shift
= adg_dim_style_get_quote_shift(data
->dim_style
);
1337 adg_entity_set_global_map(quote_entity
, adg_matrix_identity());
1338 adg_entity_global_changed(quote_entity
);
1340 cairo_matrix_init_translate(&map
, 0, shift
->y
);
1341 adg_entity_set_global_map(value_entity
, &map
);
1342 adg_entity_arrange(value_entity
);
1344 /* Limit values (min and max) */
1345 if (min_entity
!= NULL
|| max_entity
!= NULL
) {
1346 const CpmlPair
*limits_shift
;
1349 CpmlPair org_min
, org_max
;
1351 limits_shift
= adg_dim_style_get_limits_shift(data
->dim_style
);
1352 spacing
= adg_dim_style_get_limits_spacing(data
->dim_style
);
1353 size
= adg_entity_get_extents(value_entity
)->size
;
1354 org_min
.x
= size
.x
+ limits_shift
->x
;
1355 org_min
.y
= -size
.y
/ 2 + limits_shift
->y
;
1358 if (min_entity
&& max_entity
) {
1359 /* Prearrange the min entity to get its extents */
1360 adg_entity_arrange(min_entity
);
1361 size
= adg_entity_get_extents(min_entity
)->size
;
1363 org_min
.y
+= spacing
/ 2;
1364 org_max
.y
= org_min
.y
- size
.y
- spacing
/ 2;
1367 if (min_entity
!= NULL
) {
1368 cairo_matrix_init_translate(&map
, org_min
.x
, org_min
.y
);
1369 adg_entity_set_global_map(min_entity
, &map
);
1370 adg_entity_arrange(min_entity
);
1373 if (max_entity
!= NULL
) {
1374 cairo_matrix_init_translate(&map
, org_max
.x
, org_max
.y
);
1375 adg_entity_set_global_map(max_entity
, &map
);
1376 adg_entity_arrange(max_entity
);
1382 _adg_default_value(AdgDim
*dim
)
1384 g_warning(_("AdgDim::default_value not implemented for '%s'"),
1385 g_type_name(G_TYPE_FROM_INSTANCE(dim
)));
1386 return g_strdup("undef");
1390 _adg_quote_angle(gdouble angle
)
1392 angle
= cpml_angle(angle
);
1394 if (angle
> G_PI_4
* 4 / 3 || angle
<= -G_PI_4
* 3)
1395 angle
= cpml_angle(angle
+ G_PI
);
1401 _adg_set_outside(AdgDim
*dim
, AdgThreeState outside
)
1403 AdgDimPrivate
*data
;
1405 g_return_val_if_fail(adg_is_enum_value(outside
, ADG_TYPE_THREE_STATE
),
1410 if (data
->outside
== outside
)
1413 data
->outside
= outside
;
1419 _adg_set_detached(AdgDim
*dim
, AdgThreeState detached
)
1421 AdgDimPrivate
*data
;
1423 g_return_val_if_fail(adg_is_enum_value(detached
, ADG_TYPE_THREE_STATE
),
1428 if (data
->detached
== detached
)
1431 data
->detached
= detached
;
1437 _adg_set_value(AdgDim
*dim
, const gchar
*value
)
1439 AdgDimPrivate
*data
;
1443 if (g_strcmp0(value
, data
->value
) == 0)
1446 g_free(data
->value
);
1447 data
->value
= g_strdup(value
);
1449 if (data
->quote
.value
) {
1450 g_object_unref(data
->quote
.value
);
1451 data
->quote
.value
= NULL
;
1458 _adg_set_min(AdgDim
*dim
, const gchar
*min
)
1460 AdgDimPrivate
*data
= dim
->data
;
1462 if (g_strcmp0(min
, data
->min
) == 0)
1466 data
->min
= g_strdup(min
);
1468 if (data
->quote
.min
) {
1469 g_object_unref(data
->quote
.min
);
1470 data
->quote
.min
= NULL
;
1477 _adg_set_max(AdgDim
*dim
, const gchar
*max
)
1479 AdgDimPrivate
*data
= dim
->data
;
1481 if (g_strcmp0(max
, data
->max
) == 0)
1485 data
->max
= g_strdup(max
);
1487 if (data
->quote
.max
) {
1488 g_object_unref(data
->quote
.max
);
1489 data
->quote
.max
= NULL
;
1496 _adg_replace(const GMatchInfo
*match_info
, GString
*result
, gpointer user_data
)
1498 AdgDimReplaceData
*replace_data
;
1503 replace_data
= (AdgDimReplaceData
*) user_data
;
1504 value
= replace_data
->value
;
1506 if (! adg_dim_style_convert(replace_data
->dim_style
, &value
, *replace_data
->argument
)) {
1507 /* Conversion failed: invalid argument? */
1508 g_return_val_if_reached(TRUE
);
1512 format
= g_match_info_fetch(match_info
, 0);
1514 /* This should never happen */
1515 g_return_val_if_fail(format
!= NULL
, TRUE
);
1517 /* Consume the recently used argument */
1518 ++ replace_data
->argument
;
1520 g_ascii_formatd(buffer
, 256, format
, value
);
1522 g_string_append(result
, buffer
);