[AdgPoint] Removed from ADG
[adg.git] / adg / adg-dim.c
blob21f561b2e2b0482c92c990ee29032f202f17ecb6
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 * @title: AdgDim
24 * @short_description: Root abstract class for all dimension entities
26 * The #AdgDim class is the base stub of all the dimension entities.
29 /**
30 * AdgDim:
32 * All fields are private and should not be used directly.
33 * Use its public methods instead.
34 **/
37 #include "adg-dim.h"
38 #include "adg-dim-private.h"
39 #include "adg-util.h"
40 #include "adg-intl.h"
41 #include <string.h>
44 enum {
45 PROP_0,
46 PROP_REF1,
47 PROP_REF2,
48 PROP_POS1,
49 PROP_POS2,
50 PROP_LEVEL,
51 PROP_QUOTE,
52 PROP_TOLERANCE_UP,
53 PROP_TOLERANCE_DOWN,
54 PROP_NOTE
58 static void finalize (GObject *object);
59 static void get_property (GObject *object,
60 guint param_id,
61 GValue *value,
62 GParamSpec *pspec);
63 static void set_property (GObject *object,
64 guint param_id,
65 const GValue *value,
66 GParamSpec *pspec);
67 static void paper_matrix_changed (AdgEntity *entity,
68 AdgMatrix *parent_matrix);
69 static void invalidate (AdgEntity *entity);
70 static void clear (AdgDim *entity);
71 static gchar * default_quote (AdgDim *dim);
72 static void quote_layout (AdgDim *dim,
73 cairo_t *cr);
74 static void text_cache_init (AdgTextCache *text_cache);
75 static gboolean text_cache_update (AdgTextCache *text_cache,
76 const gchar *text,
77 cairo_t *cr,
78 AdgStyle *style);
79 static void text_cache_clear (AdgTextCache *text_cache);
80 static void text_cache_move_to (AdgTextCache *text_cache,
81 const CpmlPair *to);
82 static void text_cache_render (AdgTextCache *text_cache,
83 cairo_t *cr);
86 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
89 static void
90 adg_dim_class_init(AdgDimClass *klass)
92 GObjectClass *gobject_class;
93 AdgEntityClass *entity_class;
94 GParamSpec *param;
96 gobject_class = (GObjectClass *) klass;
97 entity_class = (AdgEntityClass *) klass;
99 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
101 gobject_class->finalize = finalize;
102 gobject_class->get_property = get_property;
103 gobject_class->set_property = set_property;
105 entity_class->paper_matrix_changed = paper_matrix_changed;
106 entity_class->invalidate = invalidate;
108 klass->default_quote = default_quote;
109 klass->quote_layout = quote_layout;
111 param = g_param_spec_boxed("ref1",
112 P_("Reference 1"),
113 P_("First reference point of the dimension"),
114 ADG_TYPE_PAIR,
115 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
116 g_object_class_install_property(gobject_class, PROP_REF1, param);
118 param = g_param_spec_boxed("ref2",
119 P_("Reference 2"),
120 P_("Second reference point of the dimension"),
121 ADG_TYPE_PAIR,
122 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
123 g_object_class_install_property(gobject_class, PROP_REF2, param);
125 param = g_param_spec_boxed("pos1",
126 P_("Position 1"),
127 P_("First position point: it will be computed with the level property to get the real dimension position"),
128 ADG_TYPE_PAIR, G_PARAM_READWRITE);
129 g_object_class_install_property(gobject_class, PROP_POS1, param);
131 param = g_param_spec_boxed("pos2",
132 P_("Position 2"),
133 P_("Second position point: it will be computed with the level property to get the real dimension position"),
134 ADG_TYPE_PAIR, G_PARAM_READWRITE);
135 g_object_class_install_property(gobject_class, PROP_POS2, param);
137 param = g_param_spec_double("level",
138 P_("Level"),
139 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"),
140 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
141 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
142 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
144 param = g_param_spec_string("quote",
145 P_("Quote"),
146 P_("The quote to display: set to NULL to get the default quote"),
147 NULL, G_PARAM_READWRITE);
148 g_object_class_install_property(gobject_class, PROP_QUOTE, param);
150 param = g_param_spec_string("tolerance-up",
151 P_("Up Tolerance"),
152 P_("The up tolerance of the quote: set to NULL to suppress"),
153 NULL, G_PARAM_READWRITE);
154 g_object_class_install_property(gobject_class, PROP_TOLERANCE_UP, param);
156 param = g_param_spec_string("tolerance-down",
157 P_("Down Tolerance"),
158 P_("The down tolerance of the quote: set to NULL to suppress"),
159 NULL, G_PARAM_READWRITE);
160 g_object_class_install_property(gobject_class, PROP_TOLERANCE_DOWN, param);
162 param = g_param_spec_string("note",
163 P_("Note"),
164 P_("A custom note appended to the dimension quote"),
165 NULL, G_PARAM_READWRITE);
166 g_object_class_install_property(gobject_class, PROP_NOTE, param);
169 static void
170 adg_dim_init(AdgDim *dim)
172 AdgDimPrivate *data = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
173 AdgDimPrivate);
175 data->ref1.x = data->ref1.y = 0;
176 data->ref2.x = data->ref2.y = 0;
177 data->pos1.x = data->pos1.y = 0;
178 data->pos2.x = data->pos2.y = 0;
179 data->level = 1.;
180 data->quote = NULL;
181 data->tolerance_up = NULL;
182 data->tolerance_down = NULL;
183 data->note = NULL;
185 data->org.x = data->org.y = 0;
186 data->angle = 0;
187 text_cache_init(&data->quote_cache);
188 text_cache_init(&data->tolerance_up_cache);
189 text_cache_init(&data->tolerance_down_cache);
190 text_cache_init(&data->note_cache);
192 dim->data = data;
195 static void
196 finalize(GObject *object)
198 AdgDim *dim;
199 AdgDimPrivate *data;
200 GObjectClass *object_class;
202 dim = (AdgDim *) object;
203 data = dim->data;
204 object_class = (GObjectClass *) adg_dim_parent_class;
206 g_free(data->quote);
207 g_free(data->tolerance_up);
208 g_free(data->tolerance_down);
210 clear(dim);
212 if (object_class->finalize != NULL)
213 object_class->finalize(object);
216 static void
217 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
219 AdgDimPrivate *data = ((AdgDim *) object)->data;
221 switch (prop_id) {
222 case PROP_REF1:
223 g_value_set_boxed(value, &data->ref1);
224 break;
225 case PROP_REF2:
226 g_value_set_boxed(value, &data->ref2);
227 break;
228 case PROP_POS1:
229 g_value_set_boxed(value, &data->pos1);
230 break;
231 case PROP_POS2:
232 g_value_set_boxed(value, &data->pos1);
233 break;
234 case PROP_LEVEL:
235 g_value_set_double(value, data->level);
236 break;
237 case PROP_QUOTE:
238 g_value_set_string(value, data->quote);
239 break;
240 case PROP_TOLERANCE_UP:
241 g_value_set_string(value, data->tolerance_up);
242 break;
243 case PROP_TOLERANCE_DOWN:
244 g_value_set_string(value, data->tolerance_down);
245 break;
246 case PROP_NOTE:
247 g_value_set_string(value, data->note);
248 break;
249 default:
250 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
251 break;
255 static void
256 set_property(GObject *object, guint prop_id,
257 const GValue *value, GParamSpec *pspec)
259 AdgDim *dim;
260 AdgDimPrivate *data;
262 dim = (AdgDim *) object;
263 data = dim->data;
265 switch (prop_id) {
266 case PROP_REF1:
267 cpml_pair_copy(&data->ref1, (AdgPair *) g_value_get_boxed(value));
268 clear(dim);
269 break;
270 case PROP_REF2:
271 cpml_pair_copy(&data->ref2, (AdgPair *) g_value_get_boxed(value));
272 clear(dim);
273 break;
274 case PROP_POS1:
275 cpml_pair_copy(&data->pos1, (AdgPair *) g_value_get_boxed(value));
276 clear(dim);
277 break;
278 case PROP_POS2:
279 cpml_pair_copy(&data->pos2, (AdgPair *) g_value_get_boxed(value));
280 clear(dim);
281 break;
282 case PROP_LEVEL:
283 data->level = g_value_get_double(value);
284 clear(dim);
285 break;
286 case PROP_QUOTE:
287 g_free(data->quote);
288 data->quote = g_value_dup_string(value);
289 text_cache_clear(&data->quote_cache);
290 break;
291 case PROP_TOLERANCE_UP:
292 g_free(data->tolerance_up);
293 data->tolerance_up = g_value_dup_string(value);
294 text_cache_clear(&data->tolerance_up_cache);
295 break;
296 case PROP_TOLERANCE_DOWN:
297 g_free(data->tolerance_down);
298 data->tolerance_down = g_value_dup_string(value);
299 text_cache_clear(&data->tolerance_down_cache);
300 break;
301 case PROP_NOTE:
302 g_free(data->note);
303 data->note = g_value_dup_string(value);
304 text_cache_clear(&data->note_cache);
305 break;
306 default:
307 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
308 break;
314 * adg_dim_get_org:
315 * @dim: an #AdgDim
317 * Gets the origin (org) coordinates. The returned pair is internally
318 * owned and must not be freed or modified. This function is only
319 * useful in new dimension implementations.
321 * Return value: the org coordinates
323 const AdgPair *
324 adg_dim_get_org(AdgDim *dim)
326 AdgDimPrivate *data;
328 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
330 data = dim->data;
332 return &data->org;
336 * adg_dim_set_org:
337 * @dim: an #AdgDim
338 * @org: the org coordinates
340 * Sets new org coordinates. This function is only useful
341 * in new dimension implementations.
343 void
344 adg_dim_set_org(AdgDim *dim, const AdgPair *org)
346 AdgDimPrivate *data;
348 g_return_if_fail(ADG_IS_DIM(dim));
349 g_return_if_fail(org != NULL);
351 data = dim->data;
352 data->org = *org;
356 * adg_dim_set_org_explicit:
357 * @dim: an #AdgDim
358 * @org_x: x component of org
359 * @org_y: y component of org
361 * Explicitely sets new org coordinates. This function is only useful
362 * in new dimension implementations.
364 void
365 adg_dim_set_org_explicit(AdgDim *dim, gdouble org_x, gdouble org_y)
367 AdgDimPrivate *data;
369 g_return_if_fail(ADG_IS_DIM(dim));
371 data = dim->data;
372 data->org.x = org_x;
373 data->org.y = org_y;
377 * adg_dim_get_angle:
378 * @dim: an #AdgDim
380 * Gets the dimension angle. This function is only useful
381 * in new dimension implementations.
383 * Return value: the angle (in radians)
385 gdouble
386 adg_dim_get_angle(AdgDim *dim)
388 AdgDimPrivate *data;
390 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
392 data = dim->data;
394 return data->angle;
398 * adg_dim_set_angle:
399 * @dim: an #AdgDim
400 * @angle: the new angle (in radians)
402 * Sets a new dimension angle. This function is only useful
403 * in new dimension implementations.
405 void
406 adg_dim_set_angle(AdgDim *dim, gdouble angle)
408 AdgDimPrivate *data;
410 g_return_if_fail(ADG_IS_DIM(dim));
412 data = dim->data;
413 data->angle = angle;
417 * adg_dim_get_ref1:
418 * @dim: an #AdgDim
420 * Gets the ref1 coordinates. The returned pair is internally owned
421 * and must not be freed or modified.
423 * Return value: the ref1 coordinates
425 const AdgPair *
426 adg_dim_get_ref1(AdgDim *dim)
428 AdgDimPrivate *data;
430 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
432 data = dim->data;
434 return &data->ref1;
438 * adg_dim_get_ref2:
439 * @dim: an #AdgDim
441 * Gets the ref2 coordinates. The returned pair is internally owned
442 * and must not be freed or modified.
444 * Return value: the ref2 coordinates
446 const AdgPair *
447 adg_dim_get_ref2(AdgDim *dim)
449 AdgDimPrivate *data;
451 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
453 data = dim->data;
455 return &data->ref2;
459 * adg_dim_set_ref:
460 * @dim: an #AdgDim
461 * @ref1: the ref1 coordinates
462 * @ref2: the ref2 coordinates
464 * Shortcut to set ref1 and ref2 points at once.
466 void
467 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
469 g_return_if_fail(ADG_IS_DIM(dim));
471 if (ref1 != NULL || ref2 != NULL) {
472 GObject *object;
473 AdgDimPrivate *data;
475 data = dim->data;
476 object = (GObject *) dim;
478 g_object_freeze_notify(object);
480 if (ref1 != NULL) {
481 data->ref1 = *ref1;
482 g_object_notify(object, "ref1");
485 if (ref2 != NULL) {
486 data->ref2 = *ref2;
487 g_object_notify(object, "ref2");
490 g_object_thaw_notify(object);
491 clear(dim);
496 * adg_dim_set_ref_explicit:
497 * @dim: an #AdgDim
498 * @ref1_x: x component of pos1
499 * @ref1_y: y component of pos1
500 * @ref2_x: x component of pos2
501 * @ref2_y: y component of pos2
503 * Shortcut to set ref1 and ref2 points at once,
504 * using explicit coordinates.
506 void
507 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
508 gdouble ref2_x, gdouble ref2_y)
510 AdgPair ref1;
511 AdgPair ref2;
513 ref1.x = ref1_x;
514 ref1.y = ref1_y;
515 ref2.x = ref2_x;
516 ref2.y = ref2_y;
518 adg_dim_set_ref(dim, &ref1, &ref2);
522 * adg_dim_get_pos1:
523 * @dim: an #AdgDim
525 * Gets the pos1 coordinates. The returned pair is internally owned
526 * and must not be freed or modified.
528 * Return value: the pos1 coordinates
530 const AdgPair *
531 adg_dim_get_pos1(AdgDim *dim)
533 AdgDimPrivate *data;
535 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
537 data = dim->data;
539 return &data->pos1;
543 * adg_dim_get_pos2:
544 * @dim: an #AdgDim
546 * Gets the pos2 coordinates. The returned pair is internally owned
547 * and must not be freed or modified.
549 * Return value: the pos2 coordinates
551 const AdgPair *
552 adg_dim_get_pos2(AdgDim *dim)
554 AdgDimPrivate *data;
556 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
558 data = dim->data;
560 return &data->pos2;
564 * adg_dim_set_pos:
565 * @dim: an #AdgDim
566 * @pos1: the pos1 coordinates
567 * @pos2: the pos2 coordinates
569 * Shortcut to set pos1 and pos2 points at once.
571 void
572 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
574 g_return_if_fail(ADG_IS_DIM(dim));
576 if (pos1 != NULL || pos2 != NULL) {
577 AdgDimPrivate *data;
578 GObject *object;
580 data = dim->data;
581 object = (GObject *) dim;
583 g_object_freeze_notify(object);
585 if (pos1 != NULL) {
586 data->pos1 = *pos1;
587 g_object_notify(object, "pos1");
589 if (pos2 != NULL) {
590 data->pos2 = *pos2;
591 g_object_notify(object, "pos2");
594 g_object_thaw_notify(object);
595 clear(dim);
600 * adg_dim_set_pos_explicit:
601 * @dim: an #AdgDim
602 * @pos1_x: x component of pos1
603 * @pos1_y: y component of pos1
604 * @pos2_x: x component of pos2
605 * @pos2_y: y component of pos2
607 * Shortcut to set pos1 and pos2 points at once,
608 * using explicit coordinates.
610 void
611 adg_dim_set_pos_explicit(AdgDim *dim, gdouble pos1_x, gdouble pos1_y,
612 gdouble pos2_x, gdouble pos2_y)
614 AdgPair pos1;
615 AdgPair pos2;
617 pos1.x = pos1_x;
618 pos1.y = pos1_y;
619 pos2.x = pos2_x;
620 pos2.y = pos2_y;
622 adg_dim_set_pos(dim, &pos1, &pos2);
626 * adg_dim_get_level:
627 * @dim: an #AdgDim
629 * Gets the level of this dimension.
631 * Return value: the level value
633 gdouble
634 adg_dim_get_level(AdgDim *dim)
636 AdgDimPrivate *data;
638 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
640 data = dim->data;
642 return data->level;
646 * adg_dim_set_level:
647 * @dim: an #AdgDim
648 * @level: the new level
650 * Sets a new level for this dimension. The level is used to
651 * stack the quotes using a spacing value from dim_style
652 * (specified in paper space).
654 void
655 adg_dim_set_level(AdgDim *dim, gdouble level)
657 AdgDimPrivate *data;
659 g_return_if_fail(ADG_IS_DIM(dim));
661 data = dim->data;
662 data->level = level;
664 g_object_notify((GObject *) dim, "level");
665 clear(dim);
669 * adg_dim_get_quote:
670 * @dim: an #AdgDim
672 * Gets the quote text. The string is internally owned and
673 * must not be freed or modified.
675 * Return value: the quote text
677 const gchar *
678 adg_dim_get_quote(AdgDim *dim)
680 AdgDimPrivate *data;
682 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
684 data = dim->data;
686 return data->quote;
690 * adg_dim_set_quote:
691 * @dim: an #AdgDim
692 * @quote: the quote text
694 * Explicitely sets the text to use as quote. If @quote is %NULL or
695 * was never set, an automatic text is calculated using the format as
696 * specified by the dim_style associated to this entity and getting
697 * the number from the construction points (ref1, ref2, pos1 and pos2).
699 void
700 adg_dim_set_quote(AdgDim *dim, const gchar *quote)
702 AdgDimPrivate *data;
704 g_return_if_fail(ADG_IS_DIM(dim));
706 data = dim->data;
708 g_free(data->quote);
709 data->quote = g_strdup(quote);
710 g_object_notify((GObject *) dim, "quote");
712 text_cache_clear(&data->quote_cache);
716 * adg_dim_get_tolerance_up:
717 * @dim: an #AdgDim
719 * Gets the upper tolerance text or %NULL on upper tolerance disabled.
720 * The string is internally owned and must not be freed or modified.
722 * Return value: the tolerance text
724 const gchar *
725 adg_dim_get_tolerance_up(AdgDim *dim)
727 AdgDimPrivate *data;
729 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
731 data = dim->data;
733 return data->tolerance_up;
737 * adg_dim_set_tolerance_up:
738 * @dim: an #AdgDim
739 * @tolerance_up: the upper tolerance
741 * Sets the upper tolerance. Use %NULL as @tolerance_up to disable it.
743 void
744 adg_dim_set_tolerance_up(AdgDim *dim, const gchar *tolerance_up)
746 AdgDimPrivate *data;
748 g_return_if_fail(ADG_IS_DIM(dim));
750 data = dim->data;
752 g_free(data->tolerance_up);
753 data->tolerance_up = g_strdup(tolerance_up);
754 g_object_notify((GObject *) dim, "tolerance-up");
756 text_cache_clear(&data->tolerance_up_cache);
760 * adg_dim_get_tolerance_down:
761 * @dim: an #AdgDim
763 * Gets the lower tolerance text or %NULL on lower tolerance disabled.
764 * The string is internally owned and must not be freed or modified.
766 * Return value: the tolerance text
768 const gchar *
769 adg_dim_get_tolerance_down(AdgDim *dim)
771 AdgDimPrivate *data;
773 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
775 data = dim->data;
777 return data->tolerance_down;
781 * adg_dim_set_tolerance_down:
782 * @dim: an #AdgDim
783 * @tolerance_down: the lower tolerance
785 * Sets the lower tolerance. Use %NULL as @tolerance_down to disable it.
787 void
788 adg_dim_set_tolerance_down(AdgDim *dim, const gchar *tolerance_down)
790 AdgDimPrivate *data;
792 g_return_if_fail(ADG_IS_DIM(dim));
794 data = dim->data;
796 g_free(data->tolerance_down);
797 data->tolerance_down = g_strdup(tolerance_down);
798 g_object_notify((GObject *) dim, "tolerance-down");
800 text_cache_clear(&data->tolerance_down_cache);
804 * adg_dim_set_tolerances:
805 * @dim: an #AdgDim
806 * @tolerance_up: the upper tolerance text
807 * @tolerance_down: the lower tolerance text
809 * Shortcut to set both the tolerance at once.
811 void
812 adg_dim_set_tolerances(AdgDim *dim, const gchar *tolerance_up,
813 const gchar *tolerance_down)
815 g_return_if_fail(ADG_IS_DIM(dim));
817 g_object_freeze_notify((GObject *) dim);
818 adg_dim_set_tolerance_up(dim, tolerance_up);
819 adg_dim_set_tolerance_down(dim, tolerance_down);
820 g_object_thaw_notify((GObject *) dim);
824 * adg_dim_get_note:
825 * @dim: and #AdgDim
827 * Gets the note text or %NULL if the note is not used. The string is
828 * internally owned and must not be freed or modified.
830 * Return value: the note text
832 const gchar *
833 adg_dim_get_note(AdgDim *dim)
835 AdgDimPrivate *data;
837 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
839 data = dim->data;
841 return data->note;
845 * adg_dim_set_note:
846 * @dim: an #AdgDim
847 * @note: the new note
849 * Sets a new note text, usually appended at the end of the dimension text.
851 void
852 adg_dim_set_note(AdgDim *dim, const gchar *note)
854 AdgDimPrivate *data;
856 g_return_if_fail(ADG_IS_DIM(dim));
858 data = dim->data;
860 g_free(data->note);
861 data->note = g_strdup(note);
862 g_object_notify((GObject *) dim, "note");
864 text_cache_clear(&data->note_cache);
868 * adg_dim_render_quote:
869 * @dim: an #AdgDim object
870 * @cr: a #cairo_t drawing context
872 * Renders the quote of @dim at the @org position. This function
873 * is only useful in new dimension implementations.
875 void
876 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
878 AdgDimPrivate *data;
879 AdgEntity *entity;
880 AdgDimStyle *dim_style;
881 const AdgMatrix *paper;
882 cairo_matrix_t matrix;
884 g_return_if_fail(ADG_IS_DIM(dim));
886 data = dim->data;
887 entity = (AdgEntity *) dim;
888 dim_style = (AdgDimStyle *) adg_entity_get_style(entity, ADG_SLOT_DIM_STYLE);
889 paper = adg_entity_get_paper_matrix(entity);
891 if (data->quote == NULL)
892 adg_dim_set_quote(dim, ADG_DIM_GET_CLASS(dim)->default_quote(dim));
894 cairo_save(cr);
896 cairo_set_matrix(cr, paper);
897 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
898 cairo_set_matrix(cr, adg_entity_get_model_matrix(entity));
900 cairo_translate(cr, data->org.x, data->org.y);
901 cairo_get_matrix(cr, &matrix);
902 matrix.xx = paper->xx;
903 matrix.yy = paper->yy;
904 cairo_set_matrix(cr, &matrix);
905 cairo_rotate(cr, -data->angle);
907 /* Rendering quote */
908 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
909 text_cache_render(&data->quote_cache, cr);
911 /* Rendering tolerances */
912 if (data->tolerance_up != NULL || data->tolerance_down != NULL) {
913 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
915 if (data->tolerance_up != NULL)
916 text_cache_render(&data->tolerance_up_cache, cr);
918 if (data->tolerance_down != NULL)
919 text_cache_render(&data->tolerance_down_cache, cr);
922 /* Rendering the note */
923 if (data->note != NULL) {
924 adg_style_apply(adg_dim_style_get_note_style(dim_style), cr);
925 text_cache_render(&data->note_cache, cr);
928 cairo_restore(cr);
932 static void
933 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
935 AdgEntityClass *entity_class = (AdgEntityClass *) adg_dim_parent_class;
937 clear((AdgDim *) entity);
939 if (entity_class->paper_matrix_changed != NULL)
940 entity_class->paper_matrix_changed(entity, parent_matrix);
943 static void
944 invalidate(AdgEntity *entity)
946 AdgEntityClass *entity_class = (AdgEntityClass *) adg_dim_parent_class;
948 clear((AdgDim *) entity);
950 if (entity_class->invalidate != NULL)
951 entity_class->invalidate(entity);
954 static void
955 clear(AdgDim *dim)
957 AdgDimPrivate *data = dim->data;
959 text_cache_clear(&data->quote_cache);
960 text_cache_clear(&data->tolerance_up_cache);
961 text_cache_clear(&data->tolerance_down_cache);
962 text_cache_clear(&data->note_cache);
965 static gchar *
966 default_quote(AdgDim *dim)
968 g_warning("AdgDim::default_quote not implemented for `%s'",
969 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
970 return g_strdup("undef");
973 static void
974 quote_layout(AdgDim *dim, cairo_t *cr)
976 AdgDimPrivate *data;
977 AdgDimStyle *dim_style;
978 AdgPair shift;
979 CpmlPair cp;
980 CpmlPair tolerance_up_org, tolerance_down_org, note_org;
982 data = dim->data;
983 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
984 ADG_SLOT_DIM_STYLE);
985 tolerance_up_org.x = tolerance_up_org.y = 0;
986 tolerance_down_org.x = tolerance_down_org.y = 0;
987 note_org.x = note_org.y = 0;
989 /* Compute the quote */
990 if (text_cache_update(&data->quote_cache, data->quote, cr,
991 adg_dim_style_get_quote_style(dim_style))) {
992 cp.x = data->quote_cache.extents.width;
993 cp.y = data->quote_cache.extents.height / -2.;
994 } else {
995 cp.x = 0;
996 cp.y = 0;
999 /* Compute the tolerances */
1000 if (data->tolerance_up != NULL || data->tolerance_down != NULL) {
1001 gdouble width;
1002 gdouble midspacing;
1004 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
1006 width = 0;
1007 midspacing = adg_dim_style_get_tolerance_spacing(dim_style) / 2.;
1008 cpml_pair_copy(&shift, adg_dim_style_get_tolerance_shift(dim_style));
1009 cp.x += shift.x;
1011 if (text_cache_update(&data->tolerance_up_cache,
1012 data->tolerance_up, cr, NULL)) {
1013 tolerance_up_org.x = cp.x;
1014 tolerance_up_org.y = cp.y + shift.y - midspacing;
1016 width = data->tolerance_up_cache.extents.width;
1019 if (text_cache_update(&data->tolerance_down_cache,
1020 data->tolerance_down, cr, NULL)) {
1021 tolerance_down_org.x = cp.x;
1022 tolerance_down_org.y = cp.y + shift.y + midspacing +
1023 data->tolerance_down_cache.extents.height;
1025 if (data->tolerance_down_cache.extents.width > width)
1026 width = data->tolerance_down_cache.extents.width;
1029 cp.x += width;
1032 /* Compute the note */
1033 if (text_cache_update(&data->note_cache, data->note, cr,
1034 adg_dim_style_get_note_style(dim_style))) {
1035 cpml_pair_copy(&shift, adg_dim_style_get_note_shift(dim_style));
1036 cp.x += shift.x;
1038 note_org.x = cp.x;
1039 note_org.y = cp.y + shift.y + data->note_cache.extents.height / 2.;
1041 cp.x += data->note_cache.extents.width;
1044 /* Centering and shifting the whole group */
1045 cpml_pair_copy(&shift, adg_dim_style_get_quote_shift(dim_style));
1046 shift.x -= cp.x / 2.;
1048 if (data->quote_cache.glyphs) {
1049 text_cache_move_to(&data->quote_cache, &shift);
1052 if (data->tolerance_up_cache.glyphs) {
1053 tolerance_up_org.x += shift.x;
1054 tolerance_up_org.y += shift.y;
1055 text_cache_move_to(&data->tolerance_up_cache, &tolerance_up_org);
1058 if (data->tolerance_down_cache.glyphs) {
1059 tolerance_down_org.x += shift.x;
1060 tolerance_down_org.y += shift.y;
1061 text_cache_move_to(&data->tolerance_down_cache, &tolerance_down_org);
1064 if (data->note_cache.glyphs) {
1065 note_org.x += shift.x;
1066 note_org.y += shift.y;
1067 text_cache_move_to(&data->note_cache, &note_org);
1071 static void
1072 text_cache_init(AdgTextCache *text_cache)
1074 text_cache->utf8 = NULL;
1075 text_cache->utf8_len = -1;
1076 text_cache->glyphs = NULL;
1077 text_cache->num_glyphs = 0;
1078 text_cache->clusters = NULL;
1079 text_cache->num_clusters = 0;
1080 text_cache->cluster_flags = 0;
1081 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1084 static gboolean
1085 text_cache_update(AdgTextCache *text_cache, const gchar *text,
1086 cairo_t *cr, AdgStyle *style)
1088 if (!text)
1089 return FALSE;
1091 text_cache->utf8 = text;
1092 text_cache->utf8_len = g_utf8_strlen(text, -1);
1094 if (style)
1095 adg_style_apply(style, cr);
1097 if (!text_cache->glyphs) {
1098 cairo_status_t status;
1100 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
1101 0, 0,
1102 text_cache->utf8,
1103 text_cache->utf8_len,
1104 &text_cache->glyphs,
1105 &text_cache->num_glyphs,
1106 &text_cache->clusters,
1107 &text_cache->num_clusters,
1108 &text_cache->cluster_flags);
1110 if (status != CAIRO_STATUS_SUCCESS) {
1111 g_error("Unable to build glyphs (cairo message: %s)",
1112 cairo_status_to_string(status));
1113 return FALSE;
1116 cairo_glyph_extents(cr, text_cache->glyphs, text_cache->num_glyphs,
1117 &text_cache->extents);
1120 return TRUE;
1123 static void
1124 text_cache_clear(AdgTextCache *text_cache)
1126 text_cache->utf8 = NULL;
1127 text_cache->utf8_len = -1;
1129 if (text_cache->glyphs) {
1130 cairo_glyph_free(text_cache->glyphs);
1131 text_cache->glyphs = NULL;
1132 text_cache->num_glyphs = 0;
1134 if (text_cache->clusters) {
1135 cairo_text_cluster_free(text_cache->clusters);
1136 text_cache->clusters = NULL;
1137 text_cache->num_clusters = 0;
1138 text_cache->cluster_flags = 0;
1140 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1143 static void
1144 text_cache_move_to(AdgTextCache *text_cache, const CpmlPair *to)
1146 cairo_glyph_t *glyph;
1147 int cnt;
1148 gdouble x, y;
1150 glyph = text_cache->glyphs;
1151 cnt = text_cache->num_glyphs;
1152 x = to->x - glyph->x;
1153 y = to->y - glyph->y;
1155 while (cnt --) {
1156 glyph->x += x;
1157 glyph->y += y;
1158 ++ glyph;
1162 static void
1163 text_cache_render(AdgTextCache *text_cache, cairo_t *cr)
1165 cairo_show_text_glyphs(cr, text_cache->utf8, text_cache->utf8_len,
1166 text_cache->glyphs, text_cache->num_glyphs,
1167 text_cache->clusters, text_cache->num_clusters,
1168 text_cache->cluster_flags);