[AdgDim] Refactored using maps
[adg.git] / adg / adg-dim.c
blob60e6745cc2387405622eaccf16f8c7f11c30d0f6
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.
21 /**
22 * SECTION:adg-dim
23 * @short_description: Root abstract class for all dimension entities
25 * The #AdgDim class is the base stub of all the dimension entities.
28 /**
29 * AdgDim:
31 * All fields are private and should not be used directly.
32 * Use its public methods instead.
33 **/
36 #include "adg-dim.h"
37 #include "adg-dim-private.h"
38 #include "adg-util.h"
39 #include "adg-intl.h"
40 #include <string.h>
43 enum {
44 PROP_0,
45 PROP_REF1,
46 PROP_REF2,
47 PROP_POS1,
48 PROP_POS2,
49 PROP_LEVEL,
50 PROP_QUOTE,
51 PROP_TOLERANCE_UP,
52 PROP_TOLERANCE_DOWN,
53 PROP_NOTE
57 static void finalize (GObject *object);
58 static void get_property (GObject *object,
59 guint param_id,
60 GValue *value,
61 GParamSpec *pspec);
62 static void set_property (GObject *object,
63 guint param_id,
64 const GValue *value,
65 GParamSpec *pspec);
66 static void invalidate (AdgEntity *entity);
67 static void clear (AdgDim *entity);
68 static gchar * default_quote (AdgDim *dim);
69 static void quote_layout (AdgDim *dim,
70 cairo_t *cr);
71 static void text_cache_init (AdgTextCache *text_cache);
72 static gboolean text_cache_update (AdgTextCache *text_cache,
73 const gchar *text,
74 cairo_t *cr,
75 AdgStyle *style);
76 static void text_cache_clear (AdgTextCache *text_cache);
77 static void text_cache_move_to (AdgTextCache *text_cache,
78 const CpmlPair *to);
79 static void text_cache_render (AdgTextCache *text_cache,
80 cairo_t *cr);
83 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
86 static void
87 adg_dim_class_init(AdgDimClass *klass)
89 GObjectClass *gobject_class;
90 AdgEntityClass *entity_class;
91 GParamSpec *param;
93 gobject_class = (GObjectClass *) klass;
94 entity_class = (AdgEntityClass *) klass;
96 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
98 gobject_class->finalize = finalize;
99 gobject_class->get_property = get_property;
100 gobject_class->set_property = set_property;
102 entity_class->invalidate = invalidate;
104 klass->default_quote = default_quote;
105 klass->quote_layout = quote_layout;
107 param = g_param_spec_boxed("ref1",
108 P_("Reference 1"),
109 P_("First reference point of the dimension"),
110 ADG_TYPE_PAIR,
111 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
112 g_object_class_install_property(gobject_class, PROP_REF1, param);
114 param = g_param_spec_boxed("ref2",
115 P_("Reference 2"),
116 P_("Second reference point of the dimension"),
117 ADG_TYPE_PAIR,
118 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
119 g_object_class_install_property(gobject_class, PROP_REF2, param);
121 param = g_param_spec_boxed("pos1",
122 P_("Position 1"),
123 P_("First position point: it will be computed with the level property to get the real dimension position"),
124 ADG_TYPE_PAIR, G_PARAM_READWRITE);
125 g_object_class_install_property(gobject_class, PROP_POS1, param);
127 param = g_param_spec_boxed("pos2",
128 P_("Position 2"),
129 P_("Second position point: it will be computed with the level property to get the real dimension position"),
130 ADG_TYPE_PAIR, G_PARAM_READWRITE);
131 g_object_class_install_property(gobject_class, PROP_POS2, param);
133 param = g_param_spec_double("level",
134 P_("Level"),
135 P_("The dimension level, that is the factor to multiply dim_style->baseline_spacing to get the offset (in device units) from pos1..pos2 where render the dimension baseline"),
136 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
137 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
138 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
140 param = g_param_spec_string("quote",
141 P_("Quote"),
142 P_("The quote to display: set to NULL to get the default quote"),
143 NULL, G_PARAM_READWRITE);
144 g_object_class_install_property(gobject_class, PROP_QUOTE, param);
146 param = g_param_spec_string("tolerance-up",
147 P_("Up Tolerance"),
148 P_("The up tolerance of the quote: set to NULL to suppress"),
149 NULL, G_PARAM_READWRITE);
150 g_object_class_install_property(gobject_class, PROP_TOLERANCE_UP, param);
152 param = g_param_spec_string("tolerance-down",
153 P_("Down Tolerance"),
154 P_("The down tolerance of the quote: set to NULL to suppress"),
155 NULL, G_PARAM_READWRITE);
156 g_object_class_install_property(gobject_class, PROP_TOLERANCE_DOWN, param);
158 param = g_param_spec_string("note",
159 P_("Note"),
160 P_("A custom note appended to the dimension quote"),
161 NULL, G_PARAM_READWRITE);
162 g_object_class_install_property(gobject_class, PROP_NOTE, param);
165 static void
166 adg_dim_init(AdgDim *dim)
168 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
169 AdgDimPrivate);
171 data->ref1.x = data->ref1.y = 0;
172 data->ref2.x = data->ref2.y = 0;
173 data->pos1.x = data->pos1.y = 0;
174 data->pos2.x = data->pos2.y = 0;
175 data->level = 1.;
176 data->quote = NULL;
177 data->tolerance_up = NULL;
178 data->tolerance_down = NULL;
179 data->note = NULL;
181 data->org.x = data->org.y = 0;
182 data->angle = 0;
183 text_cache_init(&data->quote_cache);
184 text_cache_init(&data->tolerance_up_cache);
185 text_cache_init(&data->tolerance_down_cache);
186 text_cache_init(&data->note_cache);
188 dim->data = data;
191 static void
192 finalize(GObject *object)
194 AdgDim *dim;
195 AdgDimPrivate *data;
196 GObjectClass *object_class;
198 dim = (AdgDim *) object;
199 data = dim->data;
200 object_class = (GObjectClass *) adg_dim_parent_class;
202 g_free(data->quote);
203 g_free(data->tolerance_up);
204 g_free(data->tolerance_down);
206 clear(dim);
208 if (object_class->finalize != NULL)
209 object_class->finalize(object);
212 static void
213 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
215 AdgDimPrivate *data = ((AdgDim *) object)->data;
217 switch (prop_id) {
218 case PROP_REF1:
219 g_value_set_boxed(value, &data->ref1);
220 break;
221 case PROP_REF2:
222 g_value_set_boxed(value, &data->ref2);
223 break;
224 case PROP_POS1:
225 g_value_set_boxed(value, &data->pos1);
226 break;
227 case PROP_POS2:
228 g_value_set_boxed(value, &data->pos1);
229 break;
230 case PROP_LEVEL:
231 g_value_set_double(value, data->level);
232 break;
233 case PROP_QUOTE:
234 g_value_set_string(value, data->quote);
235 break;
236 case PROP_TOLERANCE_UP:
237 g_value_set_string(value, data->tolerance_up);
238 break;
239 case PROP_TOLERANCE_DOWN:
240 g_value_set_string(value, data->tolerance_down);
241 break;
242 case PROP_NOTE:
243 g_value_set_string(value, data->note);
244 break;
245 default:
246 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
247 break;
251 static void
252 set_property(GObject *object, guint prop_id,
253 const GValue *value, GParamSpec *pspec)
255 AdgDim *dim;
256 AdgDimPrivate *data;
258 dim = (AdgDim *) object;
259 data = dim->data;
261 switch (prop_id) {
262 case PROP_REF1:
263 cpml_pair_copy(&data->ref1, (AdgPair *) g_value_get_boxed(value));
264 clear(dim);
265 break;
266 case PROP_REF2:
267 cpml_pair_copy(&data->ref2, (AdgPair *) g_value_get_boxed(value));
268 clear(dim);
269 break;
270 case PROP_POS1:
271 cpml_pair_copy(&data->pos1, (AdgPair *) g_value_get_boxed(value));
272 clear(dim);
273 break;
274 case PROP_POS2:
275 cpml_pair_copy(&data->pos2, (AdgPair *) g_value_get_boxed(value));
276 clear(dim);
277 break;
278 case PROP_LEVEL:
279 data->level = g_value_get_double(value);
280 clear(dim);
281 break;
282 case PROP_QUOTE:
283 g_free(data->quote);
284 data->quote = g_value_dup_string(value);
285 text_cache_clear(&data->quote_cache);
286 break;
287 case PROP_TOLERANCE_UP:
288 g_free(data->tolerance_up);
289 data->tolerance_up = g_value_dup_string(value);
290 text_cache_clear(&data->tolerance_up_cache);
291 break;
292 case PROP_TOLERANCE_DOWN:
293 g_free(data->tolerance_down);
294 data->tolerance_down = g_value_dup_string(value);
295 text_cache_clear(&data->tolerance_down_cache);
296 break;
297 case PROP_NOTE:
298 g_free(data->note);
299 data->note = g_value_dup_string(value);
300 text_cache_clear(&data->note_cache);
301 break;
302 default:
303 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
304 break;
310 * adg_dim_get_org:
311 * @dim: an #AdgDim
313 * Gets the origin (org) coordinates. The returned pair is internally
314 * owned and must not be freed or modified. This function is only
315 * useful in new dimension implementations.
317 * Return value: the org coordinates
319 const AdgPair *
320 adg_dim_get_org(AdgDim *dim)
322 AdgDimPrivate *data;
324 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
326 data = dim->data;
328 return &data->org;
332 * adg_dim_set_org:
333 * @dim: an #AdgDim
334 * @org: the org coordinates
336 * Sets new org coordinates. This function is only useful
337 * in new dimension implementations.
339 void
340 adg_dim_set_org(AdgDim *dim, const AdgPair *org)
342 AdgDimPrivate *data;
344 g_return_if_fail(ADG_IS_DIM(dim));
345 g_return_if_fail(org != NULL);
347 data = dim->data;
348 data->org = *org;
352 * adg_dim_set_org_explicit:
353 * @dim: an #AdgDim
354 * @org_x: x component of org
355 * @org_y: y component of org
357 * Explicitely sets new org coordinates. This function is only useful
358 * in new dimension implementations.
360 void
361 adg_dim_set_org_explicit(AdgDim *dim, gdouble org_x, gdouble org_y)
363 AdgDimPrivate *data;
365 g_return_if_fail(ADG_IS_DIM(dim));
367 data = dim->data;
368 data->org.x = org_x;
369 data->org.y = org_y;
373 * adg_dim_get_angle:
374 * @dim: an #AdgDim
376 * Gets the dimension angle. This function is only useful
377 * in new dimension implementations.
379 * Return value: the angle (in radians)
381 gdouble
382 adg_dim_get_angle(AdgDim *dim)
384 AdgDimPrivate *data;
386 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
388 data = dim->data;
390 return data->angle;
394 * adg_dim_set_angle:
395 * @dim: an #AdgDim
396 * @angle: the new angle (in radians)
398 * Sets a new dimension angle. This function is only useful
399 * in new dimension implementations.
401 void
402 adg_dim_set_angle(AdgDim *dim, gdouble angle)
404 AdgDimPrivate *data;
406 g_return_if_fail(ADG_IS_DIM(dim));
408 data = dim->data;
409 data->angle = angle;
413 * adg_dim_get_ref1:
414 * @dim: an #AdgDim
416 * Gets the ref1 coordinates. The returned pair is internally owned
417 * and must not be freed or modified.
419 * Return value: the ref1 coordinates
421 const AdgPair *
422 adg_dim_get_ref1(AdgDim *dim)
424 AdgDimPrivate *data;
426 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
428 data = dim->data;
430 return &data->ref1;
434 * adg_dim_get_ref2:
435 * @dim: an #AdgDim
437 * Gets the ref2 coordinates. The returned pair is internally owned
438 * and must not be freed or modified.
440 * Return value: the ref2 coordinates
442 const AdgPair *
443 adg_dim_get_ref2(AdgDim *dim)
445 AdgDimPrivate *data;
447 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
449 data = dim->data;
451 return &data->ref2;
455 * adg_dim_set_ref:
456 * @dim: an #AdgDim
457 * @ref1: the ref1 coordinates
458 * @ref2: the ref2 coordinates
460 * Shortcut to set ref1 and ref2 points at once.
462 void
463 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
465 g_return_if_fail(ADG_IS_DIM(dim));
467 if (ref1 != NULL || ref2 != NULL) {
468 GObject *object;
469 AdgDimPrivate *data;
471 data = dim->data;
472 object = (GObject *) dim;
474 g_object_freeze_notify(object);
476 if (ref1 != NULL) {
477 data->ref1 = *ref1;
478 g_object_notify(object, "ref1");
481 if (ref2 != NULL) {
482 data->ref2 = *ref2;
483 g_object_notify(object, "ref2");
486 g_object_thaw_notify(object);
487 clear(dim);
492 * adg_dim_set_ref_explicit:
493 * @dim: an #AdgDim
494 * @ref1_x: x component of pos1
495 * @ref1_y: y component of pos1
496 * @ref2_x: x component of pos2
497 * @ref2_y: y component of pos2
499 * Shortcut to set ref1 and ref2 points at once,
500 * using explicit coordinates.
502 void
503 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
504 gdouble ref2_x, gdouble ref2_y)
506 AdgPair ref1;
507 AdgPair ref2;
509 ref1.x = ref1_x;
510 ref1.y = ref1_y;
511 ref2.x = ref2_x;
512 ref2.y = ref2_y;
514 adg_dim_set_ref(dim, &ref1, &ref2);
518 * adg_dim_get_pos1:
519 * @dim: an #AdgDim
521 * Gets the pos1 coordinates. The returned pair is internally owned
522 * and must not be freed or modified.
524 * Return value: the pos1 coordinates
526 const AdgPair *
527 adg_dim_get_pos1(AdgDim *dim)
529 AdgDimPrivate *data;
531 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
533 data = dim->data;
535 return &data->pos1;
539 * adg_dim_get_pos2:
540 * @dim: an #AdgDim
542 * Gets the pos2 coordinates. The returned pair is internally owned
543 * and must not be freed or modified.
545 * Return value: the pos2 coordinates
547 const AdgPair *
548 adg_dim_get_pos2(AdgDim *dim)
550 AdgDimPrivate *data;
552 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
554 data = dim->data;
556 return &data->pos2;
560 * adg_dim_set_pos:
561 * @dim: an #AdgDim
562 * @pos1: the pos1 coordinates
563 * @pos2: the pos2 coordinates
565 * Shortcut to set pos1 and pos2 points at once.
567 void
568 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
570 g_return_if_fail(ADG_IS_DIM(dim));
572 if (pos1 != NULL || pos2 != NULL) {
573 AdgDimPrivate *data;
574 GObject *object;
576 data = dim->data;
577 object = (GObject *) dim;
579 g_object_freeze_notify(object);
581 if (pos1 != NULL) {
582 data->pos1 = *pos1;
583 g_object_notify(object, "pos1");
585 if (pos2 != NULL) {
586 data->pos2 = *pos2;
587 g_object_notify(object, "pos2");
590 g_object_thaw_notify(object);
591 clear(dim);
596 * adg_dim_set_pos_explicit:
597 * @dim: an #AdgDim
598 * @pos1_x: x component of pos1
599 * @pos1_y: y component of pos1
600 * @pos2_x: x component of pos2
601 * @pos2_y: y component of pos2
603 * Shortcut to set pos1 and pos2 points at once,
604 * using explicit coordinates.
606 void
607 adg_dim_set_pos_explicit(AdgDim *dim, gdouble pos1_x, gdouble pos1_y,
608 gdouble pos2_x, gdouble pos2_y)
610 AdgPair pos1;
611 AdgPair pos2;
613 pos1.x = pos1_x;
614 pos1.y = pos1_y;
615 pos2.x = pos2_x;
616 pos2.y = pos2_y;
618 adg_dim_set_pos(dim, &pos1, &pos2);
622 * adg_dim_get_level:
623 * @dim: an #AdgDim
625 * Gets the level of this dimension.
627 * Return value: the level value
629 gdouble
630 adg_dim_get_level(AdgDim *dim)
632 AdgDimPrivate *data;
634 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
636 data = dim->data;
638 return data->level;
642 * adg_dim_set_level:
643 * @dim: an #AdgDim
644 * @level: the new level
646 * Sets a new level for this dimension. The level is used to
647 * stack the quotes using a spacing value from dim_style
648 * (specified in global space).
650 void
651 adg_dim_set_level(AdgDim *dim, gdouble level)
653 AdgDimPrivate *data;
655 g_return_if_fail(ADG_IS_DIM(dim));
657 data = dim->data;
658 data->level = level;
660 g_object_notify((GObject *) dim, "level");
661 clear(dim);
665 * adg_dim_get_quote:
666 * @dim: an #AdgDim
668 * Gets the quote text. The string is internally owned and
669 * must not be freed or modified.
671 * Return value: the quote text
673 const gchar *
674 adg_dim_get_quote(AdgDim *dim)
676 AdgDimPrivate *data;
678 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
680 data = dim->data;
682 return data->quote;
686 * adg_dim_set_quote:
687 * @dim: an #AdgDim
688 * @quote: the quote text
690 * Explicitely sets the text to use as quote. If @quote is %NULL or
691 * was never set, an automatic text is calculated using the format as
692 * specified by the dim_style associated to this entity and getting
693 * the number from the construction points (ref1, ref2, pos1 and pos2).
695 void
696 adg_dim_set_quote(AdgDim *dim, const gchar *quote)
698 AdgDimPrivate *data;
700 g_return_if_fail(ADG_IS_DIM(dim));
702 data = dim->data;
704 g_free(data->quote);
705 data->quote = g_strdup(quote);
706 g_object_notify((GObject *) dim, "quote");
708 text_cache_clear(&data->quote_cache);
712 * adg_dim_get_tolerance_up:
713 * @dim: an #AdgDim
715 * Gets the upper tolerance text or %NULL on upper tolerance disabled.
716 * The string is internally owned and must not be freed or modified.
718 * Return value: the tolerance text
720 const gchar *
721 adg_dim_get_tolerance_up(AdgDim *dim)
723 AdgDimPrivate *data;
725 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
727 data = dim->data;
729 return data->tolerance_up;
733 * adg_dim_set_tolerance_up:
734 * @dim: an #AdgDim
735 * @tolerance_up: the upper tolerance
737 * Sets the upper tolerance. Use %NULL as @tolerance_up to disable it.
739 void
740 adg_dim_set_tolerance_up(AdgDim *dim, const gchar *tolerance_up)
742 AdgDimPrivate *data;
744 g_return_if_fail(ADG_IS_DIM(dim));
746 data = dim->data;
748 g_free(data->tolerance_up);
749 data->tolerance_up = g_strdup(tolerance_up);
750 g_object_notify((GObject *) dim, "tolerance-up");
752 text_cache_clear(&data->tolerance_up_cache);
756 * adg_dim_get_tolerance_down:
757 * @dim: an #AdgDim
759 * Gets the lower tolerance text or %NULL on lower tolerance disabled.
760 * The string is internally owned and must not be freed or modified.
762 * Return value: the tolerance text
764 const gchar *
765 adg_dim_get_tolerance_down(AdgDim *dim)
767 AdgDimPrivate *data;
769 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
771 data = dim->data;
773 return data->tolerance_down;
777 * adg_dim_set_tolerance_down:
778 * @dim: an #AdgDim
779 * @tolerance_down: the lower tolerance
781 * Sets the lower tolerance. Use %NULL as @tolerance_down to disable it.
783 void
784 adg_dim_set_tolerance_down(AdgDim *dim, const gchar *tolerance_down)
786 AdgDimPrivate *data;
788 g_return_if_fail(ADG_IS_DIM(dim));
790 data = dim->data;
792 g_free(data->tolerance_down);
793 data->tolerance_down = g_strdup(tolerance_down);
794 g_object_notify((GObject *) dim, "tolerance-down");
796 text_cache_clear(&data->tolerance_down_cache);
800 * adg_dim_set_tolerances:
801 * @dim: an #AdgDim
802 * @tolerance_up: the upper tolerance text
803 * @tolerance_down: the lower tolerance text
805 * Shortcut to set both the tolerance at once.
807 void
808 adg_dim_set_tolerances(AdgDim *dim, const gchar *tolerance_up,
809 const gchar *tolerance_down)
811 g_return_if_fail(ADG_IS_DIM(dim));
813 g_object_freeze_notify((GObject *) dim);
814 adg_dim_set_tolerance_up(dim, tolerance_up);
815 adg_dim_set_tolerance_down(dim, tolerance_down);
816 g_object_thaw_notify((GObject *) dim);
820 * adg_dim_get_note:
821 * @dim: and #AdgDim
823 * Gets the note text or %NULL if the note is not used. The string is
824 * internally owned and must not be freed or modified.
826 * Return value: the note text
828 const gchar *
829 adg_dim_get_note(AdgDim *dim)
831 AdgDimPrivate *data;
833 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
835 data = dim->data;
837 return data->note;
841 * adg_dim_set_note:
842 * @dim: an #AdgDim
843 * @note: the new note
845 * Sets a new note text, usually appended at the end of the dimension text.
847 void
848 adg_dim_set_note(AdgDim *dim, const gchar *note)
850 AdgDimPrivate *data;
852 g_return_if_fail(ADG_IS_DIM(dim));
854 data = dim->data;
856 g_free(data->note);
857 data->note = g_strdup(note);
858 g_object_notify((GObject *) dim, "note");
860 text_cache_clear(&data->note_cache);
864 * adg_dim_render_quote:
865 * @dim: an #AdgDim object
866 * @cr: a #cairo_t drawing context
868 * Renders the quote of @dim at the @org position. This function
869 * is only useful in new dimension implementations.
871 void
872 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
874 AdgDimPrivate *data;
875 AdgEntity *entity;
876 AdgDimStyle *dim_style;
877 AdgMatrix global, local;
878 cairo_matrix_t matrix;
880 g_return_if_fail(ADG_IS_DIM(dim));
882 data = dim->data;
883 entity = (AdgEntity *) dim;
884 dim_style = (AdgDimStyle *) adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
885 adg_entity_get_global_matrix(entity, &global);
886 adg_entity_get_local_matrix(entity, &local);
888 if (data->quote == NULL)
889 adg_dim_set_quote(dim, ADG_DIM_GET_CLASS(dim)->default_quote(dim));
891 cairo_save(cr);
893 cairo_set_matrix(cr, &global);
894 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
895 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
897 cairo_set_matrix(cr, &local);
898 cairo_translate(cr, data->org.x, data->org.y);
899 cairo_get_matrix(cr, &matrix);
900 matrix.xx = global.xx;
901 matrix.yy = global.yy;
902 cairo_set_matrix(cr, &matrix);
903 cairo_rotate(cr, -data->angle);
905 /* Rendering quote */
906 text_cache_render(&data->quote_cache, cr);
908 /* Rendering tolerances */
909 if (data->tolerance_up != NULL || data->tolerance_down != NULL) {
910 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
912 if (data->tolerance_up != NULL)
913 text_cache_render(&data->tolerance_up_cache, cr);
915 if (data->tolerance_down != NULL)
916 text_cache_render(&data->tolerance_down_cache, cr);
919 /* Rendering the note */
920 if (data->note != NULL) {
921 adg_style_apply(adg_dim_style_get_note_style(dim_style), cr);
922 text_cache_render(&data->note_cache, cr);
925 cairo_restore(cr);
929 static void
930 invalidate(AdgEntity *entity)
932 AdgEntityClass *entity_class = (AdgEntityClass *) adg_dim_parent_class;
934 clear((AdgDim *) entity);
936 if (entity_class->invalidate != NULL)
937 entity_class->invalidate(entity);
940 static void
941 clear(AdgDim *dim)
943 AdgDimPrivate *data = dim->data;
945 text_cache_clear(&data->quote_cache);
946 text_cache_clear(&data->tolerance_up_cache);
947 text_cache_clear(&data->tolerance_down_cache);
948 text_cache_clear(&data->note_cache);
951 static gchar *
952 default_quote(AdgDim *dim)
954 g_warning("AdgDim::default_quote not implemented for `%s'",
955 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
956 return g_strdup("undef");
959 static void
960 quote_layout(AdgDim *dim, cairo_t *cr)
962 AdgDimPrivate *data;
963 AdgDimStyle *dim_style;
964 AdgPair shift;
965 CpmlPair cp;
966 CpmlPair tolerance_up_org, tolerance_down_org, note_org;
968 data = dim->data;
969 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
970 ADG_SLOT_DIM_STYLE);
971 tolerance_up_org.x = tolerance_up_org.y = 0;
972 tolerance_down_org.x = tolerance_down_org.y = 0;
973 note_org.x = note_org.y = 0;
975 /* Compute the quote */
976 if (text_cache_update(&data->quote_cache, data->quote, cr,
977 adg_dim_style_get_quote_style(dim_style))) {
978 cp.x = data->quote_cache.extents.width;
979 cp.y = data->quote_cache.extents.height / -2.;
980 } else {
981 cp.x = 0;
982 cp.y = 0;
985 /* Compute the tolerances */
986 if (data->tolerance_up != NULL || data->tolerance_down != NULL) {
987 gdouble width;
988 gdouble midspacing;
990 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
992 width = 0;
993 midspacing = adg_dim_style_get_tolerance_spacing(dim_style) / 2.;
994 cpml_pair_copy(&shift, adg_dim_style_get_tolerance_shift(dim_style));
995 cp.x += shift.x;
997 if (text_cache_update(&data->tolerance_up_cache,
998 data->tolerance_up, cr, NULL)) {
999 tolerance_up_org.x = cp.x;
1000 tolerance_up_org.y = cp.y + shift.y - midspacing;
1002 width = data->tolerance_up_cache.extents.width;
1005 if (text_cache_update(&data->tolerance_down_cache,
1006 data->tolerance_down, cr, NULL)) {
1007 tolerance_down_org.x = cp.x;
1008 tolerance_down_org.y = cp.y + shift.y + midspacing +
1009 data->tolerance_down_cache.extents.height;
1011 if (data->tolerance_down_cache.extents.width > width)
1012 width = data->tolerance_down_cache.extents.width;
1015 cp.x += width;
1018 /* Compute the note */
1019 if (text_cache_update(&data->note_cache, data->note, cr,
1020 adg_dim_style_get_note_style(dim_style))) {
1021 cpml_pair_copy(&shift, adg_dim_style_get_note_shift(dim_style));
1022 cp.x += shift.x;
1024 note_org.x = cp.x;
1025 note_org.y = cp.y + shift.y + data->note_cache.extents.height / 2.;
1027 cp.x += data->note_cache.extents.width;
1030 /* Centering and shifting the whole group */
1031 cpml_pair_copy(&shift, adg_dim_style_get_quote_shift(dim_style));
1032 shift.x -= cp.x / 2.;
1034 if (data->quote_cache.glyphs) {
1035 text_cache_move_to(&data->quote_cache, &shift);
1038 if (data->tolerance_up_cache.glyphs) {
1039 tolerance_up_org.x += shift.x;
1040 tolerance_up_org.y += shift.y;
1041 text_cache_move_to(&data->tolerance_up_cache, &tolerance_up_org);
1044 if (data->tolerance_down_cache.glyphs) {
1045 tolerance_down_org.x += shift.x;
1046 tolerance_down_org.y += shift.y;
1047 text_cache_move_to(&data->tolerance_down_cache, &tolerance_down_org);
1050 if (data->note_cache.glyphs) {
1051 note_org.x += shift.x;
1052 note_org.y += shift.y;
1053 text_cache_move_to(&data->note_cache, &note_org);
1057 static void
1058 text_cache_init(AdgTextCache *text_cache)
1060 text_cache->utf8 = NULL;
1061 text_cache->utf8_len = -1;
1062 text_cache->glyphs = NULL;
1063 text_cache->num_glyphs = 0;
1064 text_cache->clusters = NULL;
1065 text_cache->num_clusters = 0;
1066 text_cache->cluster_flags = 0;
1067 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1070 static gboolean
1071 text_cache_update(AdgTextCache *text_cache, const gchar *text,
1072 cairo_t *cr, AdgStyle *style)
1074 if (!text)
1075 return FALSE;
1077 text_cache->utf8 = text;
1078 text_cache->utf8_len = g_utf8_strlen(text, -1);
1080 if (style)
1081 adg_style_apply(style, cr);
1083 if (!text_cache->glyphs) {
1084 cairo_status_t status;
1086 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
1087 0, 0,
1088 text_cache->utf8,
1089 text_cache->utf8_len,
1090 &text_cache->glyphs,
1091 &text_cache->num_glyphs,
1092 &text_cache->clusters,
1093 &text_cache->num_clusters,
1094 &text_cache->cluster_flags);
1096 if (status != CAIRO_STATUS_SUCCESS) {
1097 g_error("Unable to build glyphs (cairo message: %s)",
1098 cairo_status_to_string(status));
1099 return FALSE;
1102 cairo_glyph_extents(cr, text_cache->glyphs, text_cache->num_glyphs,
1103 &text_cache->extents);
1106 return TRUE;
1109 static void
1110 text_cache_clear(AdgTextCache *text_cache)
1112 text_cache->utf8 = NULL;
1113 text_cache->utf8_len = -1;
1115 if (text_cache->glyphs) {
1116 cairo_glyph_free(text_cache->glyphs);
1117 text_cache->glyphs = NULL;
1118 text_cache->num_glyphs = 0;
1120 if (text_cache->clusters) {
1121 cairo_text_cluster_free(text_cache->clusters);
1122 text_cache->clusters = NULL;
1123 text_cache->num_clusters = 0;
1124 text_cache->cluster_flags = 0;
1126 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1129 static void
1130 text_cache_move_to(AdgTextCache *text_cache, const CpmlPair *to)
1132 cairo_glyph_t *glyph;
1133 int cnt;
1134 gdouble x, y;
1136 glyph = text_cache->glyphs;
1137 cnt = text_cache->num_glyphs;
1138 x = to->x - glyph->x;
1139 y = to->y - glyph->y;
1141 while (cnt --) {
1142 glyph->x += x;
1143 glyph->y += y;
1144 ++ glyph;
1148 static void
1149 text_cache_render(AdgTextCache *text_cache, cairo_t *cr)
1151 cairo_show_text_glyphs(cr, text_cache->utf8, text_cache->utf8_len,
1152 text_cache->glyphs, text_cache->num_glyphs,
1153 text_cache->clusters, text_cache->num_clusters,
1154 text_cache->cluster_flags);