1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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.
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
37 #include "adg-dim-private.h"
38 #include "adg-dim-style.h"
39 #include "adg-toy-text.h"
40 #include "adg-type-builtins.h"
43 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_dim_parent_class)
44 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_dim_parent_class)
61 static void dispose (GObject
*object
);
62 static void finalize (GObject
*object
);
63 static void get_property (GObject
*object
,
67 static void set_property (GObject
*object
,
71 static void global_changed (AdgEntity
*entity
);
72 static void local_changed (AdgEntity
*entity
);
73 static void invalidate (AdgEntity
*entity
);
74 static void arrange (AdgEntity
*entity
);
75 static gchar
* default_value (AdgDim
*dim
);
76 static gdouble
quote_angle (gdouble angle
);
77 static gboolean
set_dress (AdgDim
*dim
,
79 static gboolean
set_value (AdgDim
*dim
,
81 static gboolean
set_min (AdgDim
*dim
,
83 static gboolean
set_max (AdgDim
*dim
,
87 G_DEFINE_ABSTRACT_TYPE(AdgDim
, adg_dim
, ADG_TYPE_ENTITY
);
91 adg_dim_class_init(AdgDimClass
*klass
)
93 GObjectClass
*gobject_class
;
94 AdgEntityClass
*entity_class
;
97 gobject_class
= (GObjectClass
*) klass
;
98 entity_class
= (AdgEntityClass
*) klass
;
100 g_type_class_add_private(klass
, sizeof(AdgDimPrivate
));
102 gobject_class
->dispose
= dispose
;
103 gobject_class
->finalize
= finalize
;
104 gobject_class
->get_property
= get_property
;
105 gobject_class
->set_property
= set_property
;
107 entity_class
->global_changed
= global_changed
;
108 entity_class
->local_changed
= local_changed
;
109 entity_class
->invalidate
= invalidate
;
110 entity_class
->arrange
= arrange
;
112 klass
->quote_angle
= quote_angle
;
113 klass
->default_value
= default_value
;
115 param
= adg_param_spec_dress("dress",
117 P_("The dress to use for rendering this dimension"),
118 ADG_DRESS_DIMENSION_REGULAR
,
120 g_object_class_install_property(gobject_class
, PROP_DRESS
, param
);
122 param
= g_param_spec_boxed("ref1",
124 P_("First reference point of the dimension"),
126 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
);
127 g_object_class_install_property(gobject_class
, PROP_REF1
, param
);
129 param
= g_param_spec_boxed("ref2",
131 P_("Second reference point of the dimension"),
133 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
);
134 g_object_class_install_property(gobject_class
, PROP_REF2
, param
);
136 param
= g_param_spec_boxed("pos",
138 P_("The reference position in local space of the quote: it will be combined with \"level\" to get the real quote position"),
139 ADG_TYPE_PAIR
, G_PARAM_READWRITE
);
140 g_object_class_install_property(gobject_class
, PROP_POS
, param
);
142 param
= g_param_spec_double("level",
144 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"),
145 -G_MAXDOUBLE
, G_MAXDOUBLE
, 1.0,
147 g_object_class_install_property(gobject_class
, PROP_LEVEL
, param
);
149 param
= g_param_spec_enum("outside",
151 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"),
152 ADG_TYPE_THREE_STATE
, ADG_THREE_STATE_UNKNOWN
,
154 g_object_class_install_property(gobject_class
, PROP_OUTSIDE
, param
);
156 param
= g_param_spec_string("value",
158 P_("The theoretically exact value for this quote: set to NULL to automatically get the default value"),
159 NULL
, G_PARAM_READWRITE
);
160 g_object_class_install_property(gobject_class
, PROP_VALUE
, param
);
162 param
= g_param_spec_string("value-min",
163 P_("Minimum Value or Low Tolerance"),
164 P_("The minimum value allowed or the lowest tolerance from value (depending of the dimension style): set to NULL to suppress"),
165 NULL
, G_PARAM_READWRITE
);
166 g_object_class_install_property(gobject_class
, PROP_MIN
, param
);
168 param
= g_param_spec_string("value-max",
169 P_("Maximum Value or High Tolerance"),
170 P_("The maximum value allowed or the highest tolerance from value (depending of the dimension style): set to NULL to suppress"),
171 NULL
, G_PARAM_READWRITE
);
172 g_object_class_install_property(gobject_class
, PROP_MAX
, param
);
176 adg_dim_init(AdgDim
*dim
)
178 AdgDimPrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(dim
, ADG_TYPE_DIM
,
181 data
->dress
= ADG_DRESS_DIMENSION_REGULAR
;
182 data
->ref1
.x
= data
->ref1
.y
= 0;
183 data
->ref2
.x
= data
->ref2
.y
= 0;
184 data
->pos
.x
= data
->pos
.y
= 0;
186 data
->outside
= ADG_THREE_STATE_UNKNOWN
;
195 dispose(GObject
*object
)
197 AdgDimPrivate
*data
= ((AdgDim
*) object
)->data
;
199 if (data
->quote
.container
!= NULL
) {
200 g_object_unref(data
->quote
.container
);
201 data
->quote
.container
= NULL
;
204 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
205 PARENT_OBJECT_CLASS
->dispose(object
);
209 finalize(GObject
*object
)
211 AdgDimPrivate
*data
= ((AdgDim
*) object
)->data
;
217 if (PARENT_OBJECT_CLASS
->finalize
!= NULL
)
218 PARENT_OBJECT_CLASS
->finalize(object
);
222 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
224 AdgDimPrivate
*data
= ((AdgDim
*) object
)->data
;
228 g_value_set_int(value
, data
->dress
);
231 g_value_set_boxed(value
, &data
->ref1
);
234 g_value_set_boxed(value
, &data
->ref2
);
237 g_value_set_boxed(value
, &data
->pos
);
240 g_value_set_double(value
, data
->level
);
243 g_value_set_enum(value
, data
->outside
);
246 g_value_set_string(value
, data
->value
);
249 g_value_set_string(value
, data
->min
);
252 g_value_set_string(value
, data
->max
);
255 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
261 set_property(GObject
*object
, guint prop_id
,
262 const GValue
*value
, GParamSpec
*pspec
)
267 dim
= (AdgDim
*) object
;
272 set_dress(dim
, g_value_get_int(value
));
275 cpml_pair_copy(&data
->ref1
, (AdgPair
*) g_value_get_boxed(value
));
278 cpml_pair_copy(&data
->ref2
, (AdgPair
*) g_value_get_boxed(value
));
281 cpml_pair_copy(&data
->pos
, (AdgPair
*) g_value_get_boxed(value
));
284 data
->level
= g_value_get_double(value
);
287 data
->outside
= g_value_get_enum(value
);
290 set_value(dim
, g_value_get_string(value
));
293 set_min(dim
, g_value_get_string(value
));
296 set_max(dim
, g_value_get_string(value
));
299 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
309 * Gets the dimension dress to be used in rendering @dim.
311 * Returns: the current dimension dress
314 adg_dim_get_dress(AdgDim
*dim
)
318 g_return_val_if_fail(ADG_IS_DIM(dim
), ADG_DRESS_UNDEFINED
);
328 * @dress: the new #AdgDress to use
330 * Sets a new dimension dress to @dim. The new dress must be
331 * related to the original dress for this property: you cannot
332 * set a dress used for line styles to a dress managing fonts.
334 * The check is done by calling adg_dress_are_related() with
335 * @dress and the previous dress as arguments. Check out its
336 * documentation for details on what is a related dress.
339 adg_dim_set_dress(AdgDim
*dim
, AdgDress dress
)
341 g_return_if_fail(ADG_IS_DIM(dim
));
343 if (set_dress(dim
, dress
))
344 g_object_notify((GObject
*) dim
, "dress");
351 * Gets the ref1 coordinates. The returned pair is internally owned
352 * and must not be freed or modified.
354 * Returns: the ref1 coordinates
357 adg_dim_get_ref1(AdgDim
*dim
)
361 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
372 * Gets the ref2 coordinates. The returned pair is internally owned
373 * and must not be freed or modified.
375 * Returns: the ref2 coordinates
378 adg_dim_get_ref2(AdgDim
*dim
)
382 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
392 * @ref1: the ref1 coordinates
393 * @ref2: the ref2 coordinates
395 * Shortcut to set ref1 and ref2 points at once.
398 adg_dim_set_ref(AdgDim
*dim
, const AdgPair
*ref1
, const AdgPair
*ref2
)
400 g_return_if_fail(ADG_IS_DIM(dim
));
402 if (ref1
!= NULL
|| ref2
!= NULL
) {
407 object
= (GObject
*) dim
;
409 g_object_freeze_notify(object
);
413 g_object_notify(object
, "ref1");
418 g_object_notify(object
, "ref2");
421 g_object_thaw_notify(object
);
426 * adg_dim_set_ref_explicit:
428 * @ref1_x: x coordinate of ref1
429 * @ref1_y: y coordinate of ref1
430 * @ref2_x: x coordinate of ref2
431 * @ref2_y: y coordinate of ref2
433 * Shortcut to set ref1 and ref2 points at once,
434 * using explicit coordinates.
437 adg_dim_set_ref_explicit(AdgDim
*dim
, gdouble ref1_x
, gdouble ref1_y
,
438 gdouble ref2_x
, gdouble ref2_y
)
448 adg_dim_set_ref(dim
, &ref1
, &ref2
);
455 * Gets the position coordinates. The returned pair is internally owned
456 * and must not be freed or modified.
458 * Returns: the pos coordinates
461 adg_dim_get_pos(AdgDim
*dim
)
465 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
475 * @pos: the pos coordinates
477 * Sets a new #AdgDim:pos position.
480 adg_dim_set_pos(AdgDim
*dim
, const AdgPair
*pos
)
484 g_return_if_fail(ADG_IS_DIM(dim
));
485 g_return_if_fail(pos
!= NULL
);
491 g_object_notify((GObject
*) dim
, "pos");
495 * adg_dim_set_pos_explicit:
497 * @pos_x: x coordinate of pos
498 * @pos_y: y coordinate of pos
500 * Shortcut to set #AdgDim:pos using explicit coordinates.
503 adg_dim_set_pos_explicit(AdgDim
*dim
, gdouble x
, gdouble y
)
510 adg_dim_set_pos(dim
, &pos
);
517 * Gets the level of this dimension.
519 * Returns: the level value
522 adg_dim_get_level(AdgDim
*dim
)
526 g_return_val_if_fail(ADG_IS_DIM(dim
), 0);
536 * @level: the new level
538 * Sets a new level for this dimension. The level is used to
539 * stack the quotes using a spacing value from dim_style
540 * (specified in global space).
543 adg_dim_set_level(AdgDim
*dim
, gdouble level
)
547 g_return_if_fail(ADG_IS_DIM(dim
));
552 g_object_notify((GObject
*) dim
, "level");
556 * adg_dim_get_outside:
559 * Gets the state of the #AdgDim:outside property: check the property
560 * documentation for further details.
562 * Returns: the current flag state
565 adg_dim_get_outside(AdgDim
*dim
)
569 g_return_val_if_fail(ADG_IS_DIM(dim
), ADG_THREE_STATE_UNKNOWN
);
573 return data
->outside
;
577 * adg_dim_set_outside:
579 * @outside: the new outside state
581 * Sets a new state for the #AdgDim:outside flag: check the property
582 * documentation for further details.
585 adg_dim_set_outside(AdgDim
*dim
, AdgThreeState outside
)
589 g_return_if_fail(ADG_IS_DIM(dim
));
592 data
->outside
= outside
;
594 g_object_notify((GObject
*) dim
, "outside");
601 * Gets the value text. The string is internally owned and
602 * must not be freed or modified.
604 * Returns: the value text
607 adg_dim_get_value(AdgDim
*dim
)
611 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
621 * @value: the value text
623 * Explicitely sets the text to use as value. If @value is %NULL or
624 * was never set, an automatic text is calculated using the format
625 * specified in the current #AdgDimStyle and getting its value by
626 * calling the default_value() virtual method.
629 adg_dim_set_value(AdgDim
*dim
, const gchar
*value
)
631 g_return_if_fail(ADG_IS_DIM(dim
));
633 if (set_value(dim
, value
))
634 g_object_notify((GObject
*) dim
, "value");
641 * Gets the minimum value text or %NULL on minimum value disabled.
642 * The string is internally owned and must not be freed or modified.
644 * Returns: the mimimum value text
647 adg_dim_get_min(AdgDim
*dim
)
651 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
661 * @min: the new minimum limit
663 * Sets the minimum value. Use %NULL as @min to disable it.
666 adg_dim_set_min(AdgDim
*dim
, const gchar
*min
)
668 g_return_if_fail(ADG_IS_DIM(dim
));
670 if (set_min(dim
, min
))
671 g_object_notify((GObject
*) dim
, "value-min");
678 * Gets the maximum value text or %NULL on maximum value disabled.
679 * The string is internally owned and must not be freed or modified.
681 * Returns: the maximum value text
684 adg_dim_get_max(AdgDim
*dim
)
688 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
698 * @max: the new maximum value
700 * Sets the maximum value. Use %NULL as @max to disable it.
703 adg_dim_set_max(AdgDim
*dim
, const gchar
*max
)
705 g_return_if_fail(ADG_IS_DIM(dim
));
707 if (set_max(dim
, max
))
708 g_object_notify((GObject
*) dim
, "value-max");
712 * adg_dim_set_limits:
714 * @min: the new minumum value
715 * @max: the new maximum value
717 * Shortcut to set both the limits at once.
720 adg_dim_set_limits(AdgDim
*dim
, const gchar
*min
, const gchar
*max
)
722 g_return_if_fail(ADG_IS_DIM(dim
));
724 g_object_freeze_notify((GObject
*) dim
);
725 adg_dim_set_min(dim
, min
);
726 adg_dim_set_max(dim
, max
);
727 g_object_thaw_notify((GObject
*) dim
);
731 * adg_dim_get_dim_style:
732 * @dim: an #AdgDim entity
734 * Gets the #AdgDimStyle associated to @dim. The dress to style
735 * resolution is done in the arrange() method so this value is
736 * typically available in render() or in a derived arrange()
737 * method, after the #AdgDim arrange() function has been chained up.
739 * Returns: the dim style of @entity
742 adg_dim_get_dim_style(AdgDim
*dim
)
746 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
750 return data
->dim_style
;
758 * This function is only useful in new dimension implementations.
761 * Gets the quote container, if any. This function is valid only
762 * after the #AdgDim implementation of the arrange() virtual method
765 * Returns: the quote container
768 adg_dim_get_quote(AdgDim
*dim
)
772 g_return_val_if_fail(ADG_IS_DIM(dim
), NULL
);
776 return data
->quote
.container
;
780 * adg_dim_quote_angle:
782 * @angle: an angle (in radians)
785 * This function is only useful in new dimension implementations.
788 * Converts @angle accordling to the style of @dim. Any quote angle
789 * should be validated by this method because every dimensioning
790 * style has its own convention regardling the text rotation.
792 * Returns: the angle to use (always in radians)
795 adg_dim_quote_angle(AdgDim
*dim
, gdouble angle
)
799 g_return_val_if_fail(ADG_IS_DIM(dim
), angle
);
801 klass
= ADG_DIM_GET_CLASS(dim
);
803 if (klass
->quote_angle
== NULL
)
806 return klass
->quote_angle(angle
);
810 arrange(AdgEntity
*entity
)
814 AdgEntity
*container_entity
;
815 AdgEntity
*value_entity
;
816 AdgEntity
*min_entity
;
817 AdgEntity
*max_entity
;
820 const AdgPair
*shift
;
822 dim
= (AdgDim
*) entity
;
825 /* Resolve the dim style */
826 if (data
->dim_style
== NULL
)
827 data
->dim_style
= (AdgDimStyle
*) adg_entity_style(entity
, data
->dress
);
829 if (data
->quote
.container
== NULL
)
830 data
->quote
.container
= g_object_new(ADG_TYPE_CONTAINER
,
831 "parent", dim
, NULL
);
833 if (data
->quote
.value
== NULL
) {
834 AdgDress dress
= adg_dim_style_get_value_dress(data
->dim_style
);
836 data
->quote
.value
= g_object_new(ADG_TYPE_TOY_TEXT
,
837 "dress", dress
, NULL
);
839 adg_container_add(data
->quote
.container
,
840 (AdgEntity
*) data
->quote
.value
);
842 if (data
->value
== NULL
) {
843 /* Automatically generate the value text */
844 gchar
*text
= ADG_DIM_GET_CLASS(dim
)->default_value(dim
);
845 adg_toy_text_set_label(data
->quote
.value
, text
);
848 adg_toy_text_set_label(data
->quote
.value
, data
->value
);
852 if (data
->quote
.min
== NULL
&& data
->min
!= NULL
) {
853 AdgDress dress
= adg_dim_style_get_min_dress(data
->dim_style
);
855 data
->quote
.min
= g_object_new(ADG_TYPE_TOY_TEXT
, "dress", dress
, NULL
);
857 adg_container_add(data
->quote
.container
, (AdgEntity
*) data
->quote
.min
);
858 adg_toy_text_set_label(data
->quote
.min
, data
->min
);
861 if (data
->quote
.max
== NULL
&& data
->max
!= NULL
) {
862 AdgDress dress
= adg_dim_style_get_max_dress(data
->dim_style
);
864 data
->quote
.max
= g_object_new(ADG_TYPE_TOY_TEXT
, "dress", dress
, NULL
);
866 adg_container_add(data
->quote
.container
, (AdgEntity
*) data
->quote
.max
);
867 adg_toy_text_set_label(data
->quote
.max
, data
->max
);
870 container_entity
= (AdgEntity
*) data
->quote
.container
;
871 value_entity
= (AdgEntity
*) data
->quote
.value
;
872 min_entity
= (AdgEntity
*) data
->quote
.min
;
873 max_entity
= (AdgEntity
*) data
->quote
.max
;
876 adg_entity_get_extents(value_entity
, &extents
);
878 /* Limit values (min and max) */
879 if (min_entity
!= NULL
|| max_entity
!= NULL
) {
880 CpmlExtents min_extents
= { 0 };
881 CpmlExtents max_extents
= { 0 };
885 if (min_entity
!= NULL
)
886 adg_entity_get_extents(min_entity
, &min_extents
);
889 if (max_entity
!= NULL
)
890 adg_entity_get_extents(max_entity
, &max_extents
);
892 shift
= adg_dim_style_get_limits_shift(data
->dim_style
);
893 if (min_entity
!= NULL
&& max_entity
!= NULL
)
894 spacing
= adg_dim_style_get_limits_spacing(data
->dim_style
);
896 cairo_matrix_init_translate(&map
, extents
.size
.x
+ shift
->x
,
897 (spacing
+ min_extents
.size
.y
+
898 max_extents
.size
.y
) / 2 +
899 shift
->y
- extents
.size
.y
/ 2);
901 if (min_entity
!= NULL
)
902 adg_entity_set_global_map(min_entity
, &map
);
904 if (max_entity
!= NULL
) {
905 cairo_matrix_translate(&map
, 0, -min_extents
.size
.y
- spacing
);
906 adg_entity_set_global_map(max_entity
, &map
);
909 extents
.size
.x
+= shift
->x
+ MAX(min_extents
.size
.x
, max_extents
.size
.x
);
912 /* Center and apply the style displacements */
913 shift
= adg_dim_style_get_quote_shift(data
->dim_style
);
914 cairo_matrix_init_translate(&map
, shift
->x
- extents
.size
.x
/ 2, shift
->y
);
915 adg_entity_set_global_map(container_entity
, &map
);
917 adg_entity_arrange(container_entity
);
922 global_changed(AdgEntity
*entity
)
924 AdgDimPrivate
*data
= ((AdgDim
*) entity
)->data
;
926 PARENT_ENTITY_CLASS
->global_changed(entity
);
928 if (data
->quote
.container
!= NULL
)
929 adg_entity_global_changed((AdgEntity
*) data
->quote
.container
);
933 local_changed(AdgEntity
*entity
)
935 AdgDimPrivate
*data
= ((AdgDim
*) entity
)->data
;
937 PARENT_ENTITY_CLASS
->local_changed(entity
);
939 if (data
->quote
.container
!= NULL
)
940 adg_entity_local_changed((AdgEntity
*) data
->quote
.container
);
944 invalidate(AdgEntity
*entity
)
946 AdgDimPrivate
*data
= ((AdgDim
*) entity
)->data
;
948 if (data
->quote
.container
!= NULL
)
949 adg_entity_invalidate((AdgEntity
*) data
->quote
.container
);
953 default_value(AdgDim
*dim
)
955 g_warning("AdgDim::default_value not implemented for `%s'",
956 g_type_name(G_TYPE_FROM_INSTANCE(dim
)));
957 return g_strdup("undef");
961 quote_angle(gdouble angle
)
963 angle
= cpml_angle(angle
);
965 if (angle
> G_PI_4
|| angle
<= -G_PI_4
* 3)
966 angle
= cpml_angle(angle
+ G_PI
);
972 set_dress(AdgDim
*dim
, AdgDress dress
)
974 AdgDimPrivate
*data
= dim
->data
;
976 if (adg_dress_set(&data
->dress
, dress
)) {
977 data
->dim_style
= NULL
;
985 set_value(AdgDim
*dim
, const gchar
*value
)
991 if (adg_strcmp(value
, data
->value
) == 0)
995 data
->value
= g_strdup(value
);
997 if (data
->quote
.value
!= NULL
) {
998 g_object_unref(data
->quote
.value
);
999 data
->quote
.value
= NULL
;
1006 set_min(AdgDim
*dim
, const gchar
*min
)
1008 AdgDimPrivate
*data
= dim
->data
;
1010 if (adg_strcmp(min
, data
->min
) == 0)
1014 data
->min
= g_strdup(min
);
1016 if (data
->quote
.min
!= NULL
) {
1017 g_object_unref(data
->quote
.min
);
1018 data
->quote
.min
= NULL
;
1025 set_max(AdgDim
*dim
, const gchar
*max
)
1027 AdgDimPrivate
*data
= dim
->data
;
1029 if (adg_strcmp(max
, data
->max
) == 0)
1033 data
->max
= g_strdup(max
);
1035 if (data
->quote
.max
!= NULL
) {
1036 g_object_unref(data
->quote
.max
);
1037 data
->quote
.max
= NULL
;