[docs] Improved the ADG docs
[adg.git] / adg / adg-dim.c
blob10ca73d845d42ad28ed62193a8b7dd2662f78a52
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 paper_matrix_changed (AdgEntity *entity,
67 AdgMatrix *parent_matrix);
68 static void invalidate (AdgEntity *entity);
69 static void clear (AdgDim *entity);
70 static gchar * default_quote (AdgDim *dim);
71 static void quote_layout (AdgDim *dim,
72 cairo_t *cr);
73 static void text_cache_init (AdgTextCache *text_cache);
74 static gboolean text_cache_update (AdgTextCache *text_cache,
75 const gchar *text,
76 cairo_t *cr,
77 AdgStyle *style);
78 static void text_cache_clear (AdgTextCache *text_cache);
79 static void text_cache_move_to (AdgTextCache *text_cache,
80 const CpmlPair *to);
81 static void text_cache_render (AdgTextCache *text_cache,
82 cairo_t *cr);
85 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
88 static void
89 adg_dim_class_init(AdgDimClass *klass)
91 GObjectClass *gobject_class;
92 AdgEntityClass *entity_class;
93 GParamSpec *param;
95 gobject_class = (GObjectClass *) klass;
96 entity_class = (AdgEntityClass *) klass;
98 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
100 gobject_class->finalize = finalize;
101 gobject_class->get_property = get_property;
102 gobject_class->set_property = set_property;
104 entity_class->paper_matrix_changed = paper_matrix_changed;
105 entity_class->invalidate = invalidate;
107 klass->default_quote = default_quote;
108 klass->quote_layout = quote_layout;
110 param = g_param_spec_boxed("ref1",
111 P_("Reference 1"),
112 P_("First reference point of the dimension"),
113 ADG_TYPE_PAIR,
114 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
115 g_object_class_install_property(gobject_class, PROP_REF1, param);
117 param = g_param_spec_boxed("ref2",
118 P_("Reference 2"),
119 P_("Second reference point of the dimension"),
120 ADG_TYPE_PAIR,
121 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
122 g_object_class_install_property(gobject_class, PROP_REF2, param);
124 param = g_param_spec_boxed("pos1",
125 P_("Position 1"),
126 P_("First position point: it will be computed with the level property to get the real dimension position"),
127 ADG_TYPE_PAIR, G_PARAM_READWRITE);
128 g_object_class_install_property(gobject_class, PROP_POS1, param);
130 param = g_param_spec_boxed("pos2",
131 P_("Position 2"),
132 P_("Second position point: it will be computed with the level property to get the real dimension position"),
133 ADG_TYPE_PAIR, G_PARAM_READWRITE);
134 g_object_class_install_property(gobject_class, PROP_POS2, param);
136 param = g_param_spec_double("level",
137 P_("Level"),
138 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"),
139 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
140 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
141 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
143 param = g_param_spec_string("quote",
144 P_("Quote"),
145 P_("The quote to display: set to NULL to get the default quote"),
146 NULL, G_PARAM_READWRITE);
147 g_object_class_install_property(gobject_class, PROP_QUOTE, param);
149 param = g_param_spec_string("tolerance-up",
150 P_("Up Tolerance"),
151 P_("The up tolerance of the quote: set to NULL to suppress"),
152 NULL, G_PARAM_READWRITE);
153 g_object_class_install_property(gobject_class, PROP_TOLERANCE_UP, param);
155 param = g_param_spec_string("tolerance-down",
156 P_("Down Tolerance"),
157 P_("The down tolerance of the quote: set to NULL to suppress"),
158 NULL, G_PARAM_READWRITE);
159 g_object_class_install_property(gobject_class, PROP_TOLERANCE_DOWN, param);
161 param = g_param_spec_string("note",
162 P_("Note"),
163 P_("A custom note appended to the dimension quote"),
164 NULL, G_PARAM_READWRITE);
165 g_object_class_install_property(gobject_class, PROP_NOTE, param);
168 static void
169 adg_dim_init(AdgDim *dim)
171 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
172 AdgDimPrivate);
174 data->ref1.x = data->ref1.y = 0;
175 data->ref2.x = data->ref2.y = 0;
176 data->pos1.x = data->pos1.y = 0;
177 data->pos2.x = data->pos2.y = 0;
178 data->level = 1.;
179 data->quote = NULL;
180 data->tolerance_up = NULL;
181 data->tolerance_down = NULL;
182 data->note = NULL;
184 data->org.x = data->org.y = 0;
185 data->angle = 0;
186 text_cache_init(&data->quote_cache);
187 text_cache_init(&data->tolerance_up_cache);
188 text_cache_init(&data->tolerance_down_cache);
189 text_cache_init(&data->note_cache);
191 dim->data = data;
194 static void
195 finalize(GObject *object)
197 AdgDim *dim;
198 AdgDimPrivate *data;
199 GObjectClass *object_class;
201 dim = (AdgDim *) object;
202 data = dim->data;
203 object_class = (GObjectClass *) adg_dim_parent_class;
205 g_free(data->quote);
206 g_free(data->tolerance_up);
207 g_free(data->tolerance_down);
209 clear(dim);
211 if (object_class->finalize != NULL)
212 object_class->finalize(object);
215 static void
216 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
218 AdgDimPrivate *data = ((AdgDim *) object)->data;
220 switch (prop_id) {
221 case PROP_REF1:
222 g_value_set_boxed(value, &data->ref1);
223 break;
224 case PROP_REF2:
225 g_value_set_boxed(value, &data->ref2);
226 break;
227 case PROP_POS1:
228 g_value_set_boxed(value, &data->pos1);
229 break;
230 case PROP_POS2:
231 g_value_set_boxed(value, &data->pos1);
232 break;
233 case PROP_LEVEL:
234 g_value_set_double(value, data->level);
235 break;
236 case PROP_QUOTE:
237 g_value_set_string(value, data->quote);
238 break;
239 case PROP_TOLERANCE_UP:
240 g_value_set_string(value, data->tolerance_up);
241 break;
242 case PROP_TOLERANCE_DOWN:
243 g_value_set_string(value, data->tolerance_down);
244 break;
245 case PROP_NOTE:
246 g_value_set_string(value, data->note);
247 break;
248 default:
249 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
250 break;
254 static void
255 set_property(GObject *object, guint prop_id,
256 const GValue *value, GParamSpec *pspec)
258 AdgDim *dim;
259 AdgDimPrivate *data;
261 dim = (AdgDim *) object;
262 data = dim->data;
264 switch (prop_id) {
265 case PROP_REF1:
266 cpml_pair_copy(&data->ref1, (AdgPair *) g_value_get_boxed(value));
267 clear(dim);
268 break;
269 case PROP_REF2:
270 cpml_pair_copy(&data->ref2, (AdgPair *) g_value_get_boxed(value));
271 clear(dim);
272 break;
273 case PROP_POS1:
274 cpml_pair_copy(&data->pos1, (AdgPair *) g_value_get_boxed(value));
275 clear(dim);
276 break;
277 case PROP_POS2:
278 cpml_pair_copy(&data->pos2, (AdgPair *) g_value_get_boxed(value));
279 clear(dim);
280 break;
281 case PROP_LEVEL:
282 data->level = g_value_get_double(value);
283 clear(dim);
284 break;
285 case PROP_QUOTE:
286 g_free(data->quote);
287 data->quote = g_value_dup_string(value);
288 text_cache_clear(&data->quote_cache);
289 break;
290 case PROP_TOLERANCE_UP:
291 g_free(data->tolerance_up);
292 data->tolerance_up = g_value_dup_string(value);
293 text_cache_clear(&data->tolerance_up_cache);
294 break;
295 case PROP_TOLERANCE_DOWN:
296 g_free(data->tolerance_down);
297 data->tolerance_down = g_value_dup_string(value);
298 text_cache_clear(&data->tolerance_down_cache);
299 break;
300 case PROP_NOTE:
301 g_free(data->note);
302 data->note = g_value_dup_string(value);
303 text_cache_clear(&data->note_cache);
304 break;
305 default:
306 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
307 break;
313 * adg_dim_get_org:
314 * @dim: an #AdgDim
316 * Gets the origin (org) coordinates. The returned pair is internally
317 * owned and must not be freed or modified. This function is only
318 * useful in new dimension implementations.
320 * Return value: the org coordinates
322 const AdgPair *
323 adg_dim_get_org(AdgDim *dim)
325 AdgDimPrivate *data;
327 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
329 data = dim->data;
331 return &data->org;
335 * adg_dim_set_org:
336 * @dim: an #AdgDim
337 * @org: the org coordinates
339 * Sets new org coordinates. This function is only useful
340 * in new dimension implementations.
342 void
343 adg_dim_set_org(AdgDim *dim, const AdgPair *org)
345 AdgDimPrivate *data;
347 g_return_if_fail(ADG_IS_DIM(dim));
348 g_return_if_fail(org != NULL);
350 data = dim->data;
351 data->org = *org;
355 * adg_dim_set_org_explicit:
356 * @dim: an #AdgDim
357 * @org_x: x component of org
358 * @org_y: y component of org
360 * Explicitely sets new org coordinates. This function is only useful
361 * in new dimension implementations.
363 void
364 adg_dim_set_org_explicit(AdgDim *dim, gdouble org_x, gdouble org_y)
366 AdgDimPrivate *data;
368 g_return_if_fail(ADG_IS_DIM(dim));
370 data = dim->data;
371 data->org.x = org_x;
372 data->org.y = org_y;
376 * adg_dim_get_angle:
377 * @dim: an #AdgDim
379 * Gets the dimension angle. This function is only useful
380 * in new dimension implementations.
382 * Return value: the angle (in radians)
384 gdouble
385 adg_dim_get_angle(AdgDim *dim)
387 AdgDimPrivate *data;
389 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
391 data = dim->data;
393 return data->angle;
397 * adg_dim_set_angle:
398 * @dim: an #AdgDim
399 * @angle: the new angle (in radians)
401 * Sets a new dimension angle. This function is only useful
402 * in new dimension implementations.
404 void
405 adg_dim_set_angle(AdgDim *dim, gdouble angle)
407 AdgDimPrivate *data;
409 g_return_if_fail(ADG_IS_DIM(dim));
411 data = dim->data;
412 data->angle = angle;
416 * adg_dim_get_ref1:
417 * @dim: an #AdgDim
419 * Gets the ref1 coordinates. The returned pair is internally owned
420 * and must not be freed or modified.
422 * Return value: the ref1 coordinates
424 const AdgPair *
425 adg_dim_get_ref1(AdgDim *dim)
427 AdgDimPrivate *data;
429 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
431 data = dim->data;
433 return &data->ref1;
437 * adg_dim_get_ref2:
438 * @dim: an #AdgDim
440 * Gets the ref2 coordinates. The returned pair is internally owned
441 * and must not be freed or modified.
443 * Return value: the ref2 coordinates
445 const AdgPair *
446 adg_dim_get_ref2(AdgDim *dim)
448 AdgDimPrivate *data;
450 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
452 data = dim->data;
454 return &data->ref2;
458 * adg_dim_set_ref:
459 * @dim: an #AdgDim
460 * @ref1: the ref1 coordinates
461 * @ref2: the ref2 coordinates
463 * Shortcut to set ref1 and ref2 points at once.
465 void
466 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
468 g_return_if_fail(ADG_IS_DIM(dim));
470 if (ref1 != NULL || ref2 != NULL) {
471 GObject *object;
472 AdgDimPrivate *data;
474 data = dim->data;
475 object = (GObject *) dim;
477 g_object_freeze_notify(object);
479 if (ref1 != NULL) {
480 data->ref1 = *ref1;
481 g_object_notify(object, "ref1");
484 if (ref2 != NULL) {
485 data->ref2 = *ref2;
486 g_object_notify(object, "ref2");
489 g_object_thaw_notify(object);
490 clear(dim);
495 * adg_dim_set_ref_explicit:
496 * @dim: an #AdgDim
497 * @ref1_x: x component of pos1
498 * @ref1_y: y component of pos1
499 * @ref2_x: x component of pos2
500 * @ref2_y: y component of pos2
502 * Shortcut to set ref1 and ref2 points at once,
503 * using explicit coordinates.
505 void
506 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
507 gdouble ref2_x, gdouble ref2_y)
509 AdgPair ref1;
510 AdgPair ref2;
512 ref1.x = ref1_x;
513 ref1.y = ref1_y;
514 ref2.x = ref2_x;
515 ref2.y = ref2_y;
517 adg_dim_set_ref(dim, &ref1, &ref2);
521 * adg_dim_get_pos1:
522 * @dim: an #AdgDim
524 * Gets the pos1 coordinates. The returned pair is internally owned
525 * and must not be freed or modified.
527 * Return value: the pos1 coordinates
529 const AdgPair *
530 adg_dim_get_pos1(AdgDim *dim)
532 AdgDimPrivate *data;
534 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
536 data = dim->data;
538 return &data->pos1;
542 * adg_dim_get_pos2:
543 * @dim: an #AdgDim
545 * Gets the pos2 coordinates. The returned pair is internally owned
546 * and must not be freed or modified.
548 * Return value: the pos2 coordinates
550 const AdgPair *
551 adg_dim_get_pos2(AdgDim *dim)
553 AdgDimPrivate *data;
555 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
557 data = dim->data;
559 return &data->pos2;
563 * adg_dim_set_pos:
564 * @dim: an #AdgDim
565 * @pos1: the pos1 coordinates
566 * @pos2: the pos2 coordinates
568 * Shortcut to set pos1 and pos2 points at once.
570 void
571 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
573 g_return_if_fail(ADG_IS_DIM(dim));
575 if (pos1 != NULL || pos2 != NULL) {
576 AdgDimPrivate *data;
577 GObject *object;
579 data = dim->data;
580 object = (GObject *) dim;
582 g_object_freeze_notify(object);
584 if (pos1 != NULL) {
585 data->pos1 = *pos1;
586 g_object_notify(object, "pos1");
588 if (pos2 != NULL) {
589 data->pos2 = *pos2;
590 g_object_notify(object, "pos2");
593 g_object_thaw_notify(object);
594 clear(dim);
599 * adg_dim_set_pos_explicit:
600 * @dim: an #AdgDim
601 * @pos1_x: x component of pos1
602 * @pos1_y: y component of pos1
603 * @pos2_x: x component of pos2
604 * @pos2_y: y component of pos2
606 * Shortcut to set pos1 and pos2 points at once,
607 * using explicit coordinates.
609 void
610 adg_dim_set_pos_explicit(AdgDim *dim, gdouble pos1_x, gdouble pos1_y,
611 gdouble pos2_x, gdouble pos2_y)
613 AdgPair pos1;
614 AdgPair pos2;
616 pos1.x = pos1_x;
617 pos1.y = pos1_y;
618 pos2.x = pos2_x;
619 pos2.y = pos2_y;
621 adg_dim_set_pos(dim, &pos1, &pos2);
625 * adg_dim_get_level:
626 * @dim: an #AdgDim
628 * Gets the level of this dimension.
630 * Return value: the level value
632 gdouble
633 adg_dim_get_level(AdgDim *dim)
635 AdgDimPrivate *data;
637 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
639 data = dim->data;
641 return data->level;
645 * adg_dim_set_level:
646 * @dim: an #AdgDim
647 * @level: the new level
649 * Sets a new level for this dimension. The level is used to
650 * stack the quotes using a spacing value from dim_style
651 * (specified in paper space).
653 void
654 adg_dim_set_level(AdgDim *dim, gdouble level)
656 AdgDimPrivate *data;
658 g_return_if_fail(ADG_IS_DIM(dim));
660 data = dim->data;
661 data->level = level;
663 g_object_notify((GObject *) dim, "level");
664 clear(dim);
668 * adg_dim_get_quote:
669 * @dim: an #AdgDim
671 * Gets the quote text. The string is internally owned and
672 * must not be freed or modified.
674 * Return value: the quote text
676 const gchar *
677 adg_dim_get_quote(AdgDim *dim)
679 AdgDimPrivate *data;
681 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
683 data = dim->data;
685 return data->quote;
689 * adg_dim_set_quote:
690 * @dim: an #AdgDim
691 * @quote: the quote text
693 * Explicitely sets the text to use as quote. If @quote is %NULL or
694 * was never set, an automatic text is calculated using the format as
695 * specified by the dim_style associated to this entity and getting
696 * the number from the construction points (ref1, ref2, pos1 and pos2).
698 void
699 adg_dim_set_quote(AdgDim *dim, const gchar *quote)
701 AdgDimPrivate *data;
703 g_return_if_fail(ADG_IS_DIM(dim));
705 data = dim->data;
707 g_free(data->quote);
708 data->quote = g_strdup(quote);
709 g_object_notify((GObject *) dim, "quote");
711 text_cache_clear(&data->quote_cache);
715 * adg_dim_get_tolerance_up:
716 * @dim: an #AdgDim
718 * Gets the upper tolerance text or %NULL on upper tolerance disabled.
719 * The string is internally owned and must not be freed or modified.
721 * Return value: the tolerance text
723 const gchar *
724 adg_dim_get_tolerance_up(AdgDim *dim)
726 AdgDimPrivate *data;
728 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
730 data = dim->data;
732 return data->tolerance_up;
736 * adg_dim_set_tolerance_up:
737 * @dim: an #AdgDim
738 * @tolerance_up: the upper tolerance
740 * Sets the upper tolerance. Use %NULL as @tolerance_up to disable it.
742 void
743 adg_dim_set_tolerance_up(AdgDim *dim, const gchar *tolerance_up)
745 AdgDimPrivate *data;
747 g_return_if_fail(ADG_IS_DIM(dim));
749 data = dim->data;
751 g_free(data->tolerance_up);
752 data->tolerance_up = g_strdup(tolerance_up);
753 g_object_notify((GObject *) dim, "tolerance-up");
755 text_cache_clear(&data->tolerance_up_cache);
759 * adg_dim_get_tolerance_down:
760 * @dim: an #AdgDim
762 * Gets the lower tolerance text or %NULL on lower tolerance disabled.
763 * The string is internally owned and must not be freed or modified.
765 * Return value: the tolerance text
767 const gchar *
768 adg_dim_get_tolerance_down(AdgDim *dim)
770 AdgDimPrivate *data;
772 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
774 data = dim->data;
776 return data->tolerance_down;
780 * adg_dim_set_tolerance_down:
781 * @dim: an #AdgDim
782 * @tolerance_down: the lower tolerance
784 * Sets the lower tolerance. Use %NULL as @tolerance_down to disable it.
786 void
787 adg_dim_set_tolerance_down(AdgDim *dim, const gchar *tolerance_down)
789 AdgDimPrivate *data;
791 g_return_if_fail(ADG_IS_DIM(dim));
793 data = dim->data;
795 g_free(data->tolerance_down);
796 data->tolerance_down = g_strdup(tolerance_down);
797 g_object_notify((GObject *) dim, "tolerance-down");
799 text_cache_clear(&data->tolerance_down_cache);
803 * adg_dim_set_tolerances:
804 * @dim: an #AdgDim
805 * @tolerance_up: the upper tolerance text
806 * @tolerance_down: the lower tolerance text
808 * Shortcut to set both the tolerance at once.
810 void
811 adg_dim_set_tolerances(AdgDim *dim, const gchar *tolerance_up,
812 const gchar *tolerance_down)
814 g_return_if_fail(ADG_IS_DIM(dim));
816 g_object_freeze_notify((GObject *) dim);
817 adg_dim_set_tolerance_up(dim, tolerance_up);
818 adg_dim_set_tolerance_down(dim, tolerance_down);
819 g_object_thaw_notify((GObject *) dim);
823 * adg_dim_get_note:
824 * @dim: and #AdgDim
826 * Gets the note text or %NULL if the note is not used. The string is
827 * internally owned and must not be freed or modified.
829 * Return value: the note text
831 const gchar *
832 adg_dim_get_note(AdgDim *dim)
834 AdgDimPrivate *data;
836 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
838 data = dim->data;
840 return data->note;
844 * adg_dim_set_note:
845 * @dim: an #AdgDim
846 * @note: the new note
848 * Sets a new note text, usually appended at the end of the dimension text.
850 void
851 adg_dim_set_note(AdgDim *dim, const gchar *note)
853 AdgDimPrivate *data;
855 g_return_if_fail(ADG_IS_DIM(dim));
857 data = dim->data;
859 g_free(data->note);
860 data->note = g_strdup(note);
861 g_object_notify((GObject *) dim, "note");
863 text_cache_clear(&data->note_cache);
867 * adg_dim_render_quote:
868 * @dim: an #AdgDim object
869 * @cr: a #cairo_t drawing context
871 * Renders the quote of @dim at the @org position. This function
872 * is only useful in new dimension implementations.
874 void
875 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
877 AdgDimPrivate *data;
878 AdgEntity *entity;
879 AdgDimStyle *dim_style;
880 const AdgMatrix *paper;
881 cairo_matrix_t matrix;
883 g_return_if_fail(ADG_IS_DIM(dim));
885 data = dim->data;
886 entity = (AdgEntity *) dim;
887 dim_style = (AdgDimStyle *) adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
888 paper = adg_entity_get_paper_matrix(entity);
890 if (data->quote == NULL)
891 adg_dim_set_quote(dim, ADG_DIM_GET_CLASS(dim)->default_quote(dim));
893 cairo_save(cr);
895 cairo_set_matrix(cr, paper);
896 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
897 cairo_set_matrix(cr, adg_entity_get_model_matrix(entity));
899 cairo_translate(cr, data->org.x, data->org.y);
900 cairo_get_matrix(cr, &matrix);
901 matrix.xx = paper->xx;
902 matrix.yy = paper->yy;
903 cairo_set_matrix(cr, &matrix);
904 cairo_rotate(cr, -data->angle);
906 /* Rendering quote */
907 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
908 text_cache_render(&data->quote_cache, cr);
910 /* Rendering tolerances */
911 if (data->tolerance_up != NULL || data->tolerance_down != NULL) {
912 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
914 if (data->tolerance_up != NULL)
915 text_cache_render(&data->tolerance_up_cache, cr);
917 if (data->tolerance_down != NULL)
918 text_cache_render(&data->tolerance_down_cache, cr);
921 /* Rendering the note */
922 if (data->note != NULL) {
923 adg_style_apply(adg_dim_style_get_note_style(dim_style), cr);
924 text_cache_render(&data->note_cache, cr);
927 cairo_restore(cr);
931 static void
932 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
934 AdgEntityClass *entity_class = (AdgEntityClass *) adg_dim_parent_class;
936 clear((AdgDim *) entity);
938 if (entity_class->paper_matrix_changed != NULL)
939 entity_class->paper_matrix_changed(entity, parent_matrix);
942 static void
943 invalidate(AdgEntity *entity)
945 AdgEntityClass *entity_class = (AdgEntityClass *) adg_dim_parent_class;
947 clear((AdgDim *) entity);
949 if (entity_class->invalidate != NULL)
950 entity_class->invalidate(entity);
953 static void
954 clear(AdgDim *dim)
956 AdgDimPrivate *data = dim->data;
958 text_cache_clear(&data->quote_cache);
959 text_cache_clear(&data->tolerance_up_cache);
960 text_cache_clear(&data->tolerance_down_cache);
961 text_cache_clear(&data->note_cache);
964 static gchar *
965 default_quote(AdgDim *dim)
967 g_warning("AdgDim::default_quote not implemented for `%s'",
968 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
969 return g_strdup("undef");
972 static void
973 quote_layout(AdgDim *dim, cairo_t *cr)
975 AdgDimPrivate *data;
976 AdgDimStyle *dim_style;
977 AdgPair shift;
978 CpmlPair cp;
979 CpmlPair tolerance_up_org, tolerance_down_org, note_org;
981 data = dim->data;
982 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
983 ADG_SLOT_DIM_STYLE);
984 tolerance_up_org.x = tolerance_up_org.y = 0;
985 tolerance_down_org.x = tolerance_down_org.y = 0;
986 note_org.x = note_org.y = 0;
988 /* Compute the quote */
989 if (text_cache_update(&data->quote_cache, data->quote, cr,
990 adg_dim_style_get_quote_style(dim_style))) {
991 cp.x = data->quote_cache.extents.width;
992 cp.y = data->quote_cache.extents.height / -2.;
993 } else {
994 cp.x = 0;
995 cp.y = 0;
998 /* Compute the tolerances */
999 if (data->tolerance_up != NULL || data->tolerance_down != NULL) {
1000 gdouble width;
1001 gdouble midspacing;
1003 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
1005 width = 0;
1006 midspacing = adg_dim_style_get_tolerance_spacing(dim_style) / 2.;
1007 cpml_pair_copy(&shift, adg_dim_style_get_tolerance_shift(dim_style));
1008 cp.x += shift.x;
1010 if (text_cache_update(&data->tolerance_up_cache,
1011 data->tolerance_up, cr, NULL)) {
1012 tolerance_up_org.x = cp.x;
1013 tolerance_up_org.y = cp.y + shift.y - midspacing;
1015 width = data->tolerance_up_cache.extents.width;
1018 if (text_cache_update(&data->tolerance_down_cache,
1019 data->tolerance_down, cr, NULL)) {
1020 tolerance_down_org.x = cp.x;
1021 tolerance_down_org.y = cp.y + shift.y + midspacing +
1022 data->tolerance_down_cache.extents.height;
1024 if (data->tolerance_down_cache.extents.width > width)
1025 width = data->tolerance_down_cache.extents.width;
1028 cp.x += width;
1031 /* Compute the note */
1032 if (text_cache_update(&data->note_cache, data->note, cr,
1033 adg_dim_style_get_note_style(dim_style))) {
1034 cpml_pair_copy(&shift, adg_dim_style_get_note_shift(dim_style));
1035 cp.x += shift.x;
1037 note_org.x = cp.x;
1038 note_org.y = cp.y + shift.y + data->note_cache.extents.height / 2.;
1040 cp.x += data->note_cache.extents.width;
1043 /* Centering and shifting the whole group */
1044 cpml_pair_copy(&shift, adg_dim_style_get_quote_shift(dim_style));
1045 shift.x -= cp.x / 2.;
1047 if (data->quote_cache.glyphs) {
1048 text_cache_move_to(&data->quote_cache, &shift);
1051 if (data->tolerance_up_cache.glyphs) {
1052 tolerance_up_org.x += shift.x;
1053 tolerance_up_org.y += shift.y;
1054 text_cache_move_to(&data->tolerance_up_cache, &tolerance_up_org);
1057 if (data->tolerance_down_cache.glyphs) {
1058 tolerance_down_org.x += shift.x;
1059 tolerance_down_org.y += shift.y;
1060 text_cache_move_to(&data->tolerance_down_cache, &tolerance_down_org);
1063 if (data->note_cache.glyphs) {
1064 note_org.x += shift.x;
1065 note_org.y += shift.y;
1066 text_cache_move_to(&data->note_cache, &note_org);
1070 static void
1071 text_cache_init(AdgTextCache *text_cache)
1073 text_cache->utf8 = NULL;
1074 text_cache->utf8_len = -1;
1075 text_cache->glyphs = NULL;
1076 text_cache->num_glyphs = 0;
1077 text_cache->clusters = NULL;
1078 text_cache->num_clusters = 0;
1079 text_cache->cluster_flags = 0;
1080 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1083 static gboolean
1084 text_cache_update(AdgTextCache *text_cache, const gchar *text,
1085 cairo_t *cr, AdgStyle *style)
1087 if (!text)
1088 return FALSE;
1090 text_cache->utf8 = text;
1091 text_cache->utf8_len = g_utf8_strlen(text, -1);
1093 if (style)
1094 adg_style_apply(style, cr);
1096 if (!text_cache->glyphs) {
1097 cairo_status_t status;
1099 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
1100 0, 0,
1101 text_cache->utf8,
1102 text_cache->utf8_len,
1103 &text_cache->glyphs,
1104 &text_cache->num_glyphs,
1105 &text_cache->clusters,
1106 &text_cache->num_clusters,
1107 &text_cache->cluster_flags);
1109 if (status != CAIRO_STATUS_SUCCESS) {
1110 g_error("Unable to build glyphs (cairo message: %s)",
1111 cairo_status_to_string(status));
1112 return FALSE;
1115 cairo_glyph_extents(cr, text_cache->glyphs, text_cache->num_glyphs,
1116 &text_cache->extents);
1119 return TRUE;
1122 static void
1123 text_cache_clear(AdgTextCache *text_cache)
1125 text_cache->utf8 = NULL;
1126 text_cache->utf8_len = -1;
1128 if (text_cache->glyphs) {
1129 cairo_glyph_free(text_cache->glyphs);
1130 text_cache->glyphs = NULL;
1131 text_cache->num_glyphs = 0;
1133 if (text_cache->clusters) {
1134 cairo_text_cluster_free(text_cache->clusters);
1135 text_cache->clusters = NULL;
1136 text_cache->num_clusters = 0;
1137 text_cache->cluster_flags = 0;
1139 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1142 static void
1143 text_cache_move_to(AdgTextCache *text_cache, const CpmlPair *to)
1145 cairo_glyph_t *glyph;
1146 int cnt;
1147 gdouble x, y;
1149 glyph = text_cache->glyphs;
1150 cnt = text_cache->num_glyphs;
1151 x = to->x - glyph->x;
1152 y = to->y - glyph->y;
1154 while (cnt --) {
1155 glyph->x += x;
1156 glyph->y += y;
1157 ++ glyph;
1161 static void
1162 text_cache_render(AdgTextCache *text_cache, cairo_t *cr)
1164 cairo_show_text_glyphs(cr, text_cache->utf8, text_cache->utf8_len,
1165 text_cache->glyphs, text_cache->num_glyphs,
1166 text_cache->clusters, text_cache->num_clusters,
1167 text_cache->cluster_flags);