[AdgPath] Refactored AdgPath to support arc primitives
[adg.git] / adg / adg-dim.c
blob1465e70bc2188bff3bb54993844a51d65c3533d2
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.
20 /**
21 * SECTION:dim
22 * @title: AdgDim
23 * @short_description: Root abstract class for all dimension entities
25 * The #AdgDim class is the base stub of all the dimension entities.
28 #include "adg-dim.h"
29 #include "adg-dim-private.h"
30 #include "adg-util.h"
31 #include "adg-intl.h"
32 #include <string.h>
34 #define PARENT_CLASS ((AdgEntityClass *) adg_dim_parent_class)
37 enum {
38 PROP_0,
39 PROP_REF1,
40 PROP_REF2,
41 PROP_POS1,
42 PROP_POS2,
43 PROP_LEVEL,
44 PROP_QUOTE,
45 PROP_TOLERANCE_UP,
46 PROP_TOLERANCE_DOWN,
47 PROP_NOTE
51 static void finalize (GObject *object);
52 static void get_property (GObject *object,
53 guint param_id,
54 GValue *value,
55 GParamSpec *pspec);
56 static void set_property (GObject *object,
57 guint param_id,
58 const GValue *value,
59 GParamSpec *pspec);
60 static void paper_matrix_changed (AdgEntity *entity,
61 AdgMatrix *parent_matrix);
62 static void invalidate (AdgEntity *entity);
63 static void clear (AdgDim *entity);
64 static gchar * default_quote (AdgDim *dim);
65 static void quote_layout (AdgDim *dim,
66 cairo_t *cr);
67 static void text_cache_init (AdgTextCache *text_cache);
68 static gboolean text_cache_update (AdgTextCache *text_cache,
69 const gchar *text,
70 cairo_t *cr,
71 AdgStyle *style);
72 static void text_cache_clear (AdgTextCache *text_cache);
73 static void text_cache_move_to (AdgTextCache *text_cache,
74 const CpmlPair *to);
75 static void text_cache_render (AdgTextCache *text_cache,
76 cairo_t *cr);
79 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
82 static void
83 adg_dim_class_init(AdgDimClass *klass)
85 GObjectClass *gobject_class;
86 AdgEntityClass *entity_class;
87 GParamSpec *param;
89 gobject_class = (GObjectClass *) klass;
90 entity_class = (AdgEntityClass *) klass;
92 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
94 gobject_class->finalize = finalize;
95 gobject_class->get_property = get_property;
96 gobject_class->set_property = set_property;
98 entity_class->paper_matrix_changed = paper_matrix_changed;
99 entity_class->invalidate = invalidate;
101 klass->default_quote = default_quote;
102 klass->quote_layout = quote_layout;
104 param = g_param_spec_boxed("ref1",
105 P_("Reference 1"),
106 P_("First reference point of the dimension"),
107 ADG_TYPE_PAIR,
108 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
109 g_object_class_install_property(gobject_class, PROP_REF1, param);
111 param = g_param_spec_boxed("ref2",
112 P_("Reference 2"),
113 P_("Second 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_REF2, param);
118 param = g_param_spec_boxed("pos1",
119 P_("Position 1"),
120 P_("First position point: it will be computed with the level property to get the real dimension position"),
121 ADG_TYPE_PAIR, G_PARAM_READWRITE);
122 g_object_class_install_property(gobject_class, PROP_POS1, param);
124 param = g_param_spec_boxed("pos2",
125 P_("Position 2"),
126 P_("Second 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_POS2, param);
130 param = g_param_spec_double("level",
131 P_("Level"),
132 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"),
133 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
134 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
135 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
137 param = g_param_spec_string("quote",
138 P_("Quote"),
139 P_("The quote to display: set to NULL to get the default quote"),
140 NULL, G_PARAM_READWRITE);
141 g_object_class_install_property(gobject_class, PROP_QUOTE, param);
143 param = g_param_spec_string("tolerance-up",
144 P_("Up Tolerance"),
145 P_("The up tolerance of the quote: set to NULL to suppress"),
146 NULL, G_PARAM_READWRITE);
147 g_object_class_install_property(gobject_class, PROP_TOLERANCE_UP, param);
149 param = g_param_spec_string("tolerance-down",
150 P_("Down Tolerance"),
151 P_("The down tolerance of the quote: set to NULL to suppress"),
152 NULL, G_PARAM_READWRITE);
153 g_object_class_install_property(gobject_class, PROP_TOLERANCE_DOWN, param);
155 param = g_param_spec_string("note",
156 P_("Note"),
157 P_("A custom note appended to the dimension quote"),
158 NULL, G_PARAM_READWRITE);
159 g_object_class_install_property(gobject_class, PROP_NOTE, param);
162 static void
163 adg_dim_init(AdgDim *dim)
165 AdgDimPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
166 AdgDimPrivate);
168 priv->ref1.x = priv->ref1.y = 0;
169 priv->ref2.x = priv->ref2.y = 0;
170 priv->pos1.x = priv->pos1.y = 0;
171 priv->pos2.x = priv->pos2.y = 0;
172 priv->level = 1.;
173 priv->quote = NULL;
174 priv->tolerance_up = NULL;
175 priv->tolerance_down = NULL;
176 priv->note = NULL;
178 priv->org.x = priv->org.y = 0;
179 priv->angle = 0;
180 text_cache_init(&priv->quote_cache);
181 text_cache_init(&priv->tolerance_up_cache);
182 text_cache_init(&priv->tolerance_down_cache);
183 text_cache_init(&priv->note_cache);
185 dim->priv = priv;
188 static void
189 finalize(GObject *object)
191 AdgDim *dim = (AdgDim *) object;
193 g_free(dim->priv->quote);
194 g_free(dim->priv->tolerance_up);
195 g_free(dim->priv->tolerance_down);
197 clear(dim);
199 ((GObjectClass *) PARENT_CLASS)->finalize(object);
202 static void
203 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
205 AdgDim *dim = (AdgDim *) object;
207 switch (prop_id) {
208 case PROP_REF1:
209 g_value_set_boxed(value, &dim->priv->ref1);
210 break;
211 case PROP_REF2:
212 g_value_set_boxed(value, &dim->priv->ref2);
213 break;
214 case PROP_POS1:
215 g_value_set_boxed(value, &dim->priv->pos1);
216 break;
217 case PROP_POS2:
218 g_value_set_boxed(value, &dim->priv->pos1);
219 break;
220 case PROP_LEVEL:
221 g_value_set_double(value, dim->priv->level);
222 break;
223 case PROP_QUOTE:
224 g_value_set_string(value, dim->priv->quote);
225 break;
226 case PROP_TOLERANCE_UP:
227 g_value_set_string(value, dim->priv->tolerance_up);
228 break;
229 case PROP_TOLERANCE_DOWN:
230 g_value_set_string(value, dim->priv->tolerance_down);
231 break;
232 case PROP_NOTE:
233 g_value_set_string(value, dim->priv->note);
234 break;
235 default:
236 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
237 break;
241 static void
242 set_property(GObject *object, guint prop_id,
243 const GValue *value, GParamSpec *pspec)
245 AdgEntity *entity;
246 AdgDim *dim;
248 entity = (AdgEntity *) object;
249 dim = (AdgDim *) object;
251 switch (prop_id) {
252 case PROP_REF1:
253 cpml_pair_copy(&dim->priv->ref1, (AdgPair *) g_value_get_boxed(value));
254 clear(dim);
255 break;
256 case PROP_REF2:
257 cpml_pair_copy(&dim->priv->ref2, (AdgPair *) g_value_get_boxed(value));
258 clear(dim);
259 break;
260 case PROP_POS1:
261 cpml_pair_copy(&dim->priv->pos1, (AdgPair *) g_value_get_boxed(value));
262 clear(dim);
263 break;
264 case PROP_POS2:
265 cpml_pair_copy(&dim->priv->pos2, (AdgPair *) g_value_get_boxed(value));
266 clear(dim);
267 break;
268 case PROP_LEVEL:
269 dim->priv->level = g_value_get_double(value);
270 clear(dim);
271 break;
272 case PROP_QUOTE:
273 g_free(dim->priv->quote);
274 dim->priv->quote = g_value_dup_string(value);
275 text_cache_clear(&dim->priv->quote_cache);
276 break;
277 case PROP_TOLERANCE_UP:
278 g_free(dim->priv->tolerance_up);
279 dim->priv->tolerance_up = g_value_dup_string(value);
280 text_cache_clear(&dim->priv->tolerance_up_cache);
281 break;
282 case PROP_TOLERANCE_DOWN:
283 g_free(dim->priv->tolerance_down);
284 dim->priv->tolerance_down = g_value_dup_string(value);
285 text_cache_clear(&dim->priv->tolerance_down_cache);
286 break;
287 case PROP_NOTE:
288 g_free(dim->priv->note);
289 dim->priv->note = g_value_dup_string(value);
290 text_cache_clear(&dim->priv->note_cache);
291 break;
292 default:
293 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
294 break;
300 * adg_dim_get_org:
301 * @dim: an #AdgDim
303 * Gets the origin (org) coordinates. The returned pair is internally
304 * owned and must not be freed or modified. This function is only
305 * useful in new dimension implementations.
307 * Return value: the org coordinates
309 const AdgPair *
310 adg_dim_get_org(AdgDim *dim)
312 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
314 return &dim->priv->org;
318 * adg_dim_set_org:
319 * @dim: an #AdgDim
320 * @org: the org coordinates
322 * Sets new org coordinates. This function is only useful
323 * in new dimension implementations.
325 void
326 adg_dim_set_org(AdgDim *dim, const AdgPair *org)
328 g_return_if_fail(ADG_IS_DIM(dim));
329 g_return_if_fail(org != NULL);
331 dim->priv->org = *org;
335 * adg_dim_set_org_explicit:
336 * @dim: an #AdgDim
337 * @org_x: x component of org
338 * @org_y: y component of org
340 * Explicitely sets new org coordinates. This function is only useful
341 * in new dimension implementations.
343 void
344 adg_dim_set_org_explicit(AdgDim *dim, gdouble org_x, gdouble org_y)
346 g_return_if_fail(ADG_IS_DIM(dim));
348 dim->priv->org.x = org_x;
349 dim->priv->org.y = org_y;
353 * adg_dim_get_angle:
354 * @dim: an #AdgDim
356 * Gets the dimension angle. This function is only useful
357 * in new dimension implementations.
359 * Return value: the angle (in radians)
361 gdouble
362 adg_dim_get_angle(AdgDim *dim)
364 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
366 return dim->priv->angle;
370 * adg_dim_set_angle:
371 * @dim: an #AdgDim
372 * @angle: the new angle (in radians)
374 * Sets a new dimension angle. This function is only useful
375 * in new dimension implementations.
377 void
378 adg_dim_set_angle(AdgDim *dim, gdouble angle)
380 g_return_if_fail(ADG_IS_DIM(dim));
382 dim->priv->angle = angle;
386 * adg_dim_get_ref1:
387 * @dim: an #AdgDim
389 * Gets the ref1 coordinates. The returned pair is internally owned
390 * and must not be freed or modified.
392 * Return value: the ref1 coordinates
394 const AdgPair *
395 adg_dim_get_ref1(AdgDim *dim)
397 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
399 return &dim->priv->ref1;
403 * adg_dim_get_ref2:
404 * @dim: an #AdgDim
406 * Gets the ref2 coordinates. The returned pair is internally owned
407 * and must not be freed or modified.
409 * Return value: the ref2 coordinates
411 const AdgPair *
412 adg_dim_get_ref2(AdgDim *dim)
414 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
416 return &dim->priv->ref2;
420 * adg_dim_set_ref:
421 * @dim: an #AdgDim
422 * @ref1: the ref1 coordinates
423 * @ref2: the ref2 coordinates
425 * Shortcut to set ref1 and ref2 points at once.
427 void
428 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
430 g_return_if_fail(ADG_IS_DIM(dim));
432 if (ref1 != NULL || ref2 != NULL) {
433 GObject *object = (GObject *) dim;
435 g_object_freeze_notify(object);
437 if (ref1 != NULL) {
438 dim->priv->ref1 = *ref1;
439 g_object_notify(object, "ref1");
442 if (ref2 != NULL) {
443 dim->priv->ref2 = *ref2;
444 g_object_notify(object, "ref2");
447 g_object_thaw_notify(object);
448 clear(dim);
453 * adg_dim_set_ref_explicit:
454 * @dim: an #AdgDim
455 * @ref1_x: x component of pos1
456 * @ref1_y: y component of pos1
457 * @ref2_x: x component of pos2
458 * @ref2_y: y component of pos2
460 * Shortcut to set ref1 and ref2 points at once,
461 * using explicit coordinates.
463 void
464 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
465 gdouble ref2_x, gdouble ref2_y)
467 AdgPair ref1;
468 AdgPair ref2;
470 ref1.x = ref1_x;
471 ref1.y = ref1_y;
472 ref2.x = ref2_x;
473 ref2.y = ref2_y;
475 adg_dim_set_ref(dim, &ref1, &ref2);
479 * adg_dim_get_pos1:
480 * @dim: an #AdgDim
482 * Gets the pos1 coordinates. The returned pair is internally owned
483 * and must not be freed or modified.
485 * Return value: the pos1 coordinates
487 const AdgPair *
488 adg_dim_get_pos1(AdgDim *dim)
490 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
492 return &dim->priv->pos1;
496 * adg_dim_get_pos2:
497 * @dim: an #AdgDim
499 * Gets the pos2 coordinates. The returned pair is internally owned
500 * and must not be freed or modified.
502 * Return value: the pos2 coordinates
504 const AdgPair *
505 adg_dim_get_pos2(AdgDim *dim)
507 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
509 return &dim->priv->pos2;
513 * adg_dim_set_pos:
514 * @dim: an #AdgDim
515 * @pos1: the pos1 coordinates
516 * @pos2: the pos2 coordinates
518 * Shortcut to set pos1 and pos2 points at once.
520 void
521 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
523 g_return_if_fail(ADG_IS_DIM(dim));
525 if (pos1 != NULL || pos2 != NULL) {
526 GObject *object = (GObject *) dim;
528 g_object_freeze_notify(object);
530 if (pos1 != NULL) {
531 dim->priv->pos1 = *pos1;
532 g_object_notify(object, "pos1");
534 if (pos2 != NULL) {
535 dim->priv->pos2 = *pos2;
536 g_object_notify(object, "pos2");
539 g_object_thaw_notify(object);
540 clear(dim);
545 * adg_dim_set_pos_explicit:
546 * @dim: an #AdgDim
547 * @pos1_x: x component of pos1
548 * @pos1_y: y component of pos1
549 * @pos2_x: x component of pos2
550 * @pos2_y: y component of pos2
552 * Shortcut to set pos1 and pos2 points at once,
553 * using explicit coordinates.
555 void
556 adg_dim_set_pos_explicit(AdgDim *dim, gdouble pos1_x, gdouble pos1_y,
557 gdouble pos2_x, gdouble pos2_y)
559 AdgPair pos1;
560 AdgPair pos2;
562 pos1.x = pos1_x;
563 pos1.y = pos1_y;
564 pos2.x = pos2_x;
565 pos2.y = pos2_y;
567 adg_dim_set_pos(dim, &pos1, &pos2);
571 * adg_dim_get_level:
572 * @dim: an #AdgDim
574 * Gets the level of this dimension.
576 * Return value: the level value
578 gdouble
579 adg_dim_get_level(AdgDim *dim)
581 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
583 return dim->priv->level;
587 * adg_dim_set_level:
588 * @dim: an #AdgDim
589 * @level: the new level
591 * Sets a new level for this dimension. The level is used to
592 * stack the quotes using a spacing value from dim_style
593 * (specified in paper space).
595 void
596 adg_dim_set_level(AdgDim *dim, gdouble level)
598 g_return_if_fail(ADG_IS_DIM(dim));
600 dim->priv->level = level;
601 g_object_notify((GObject *) dim, "level");
602 clear(dim);
606 * adg_dim_get_quote:
607 * @dim: an #AdgDim
609 * Gets the quote text. The string is internally owned and
610 * must not be freed or modified.
612 * Return value: the quote text
614 const gchar *
615 adg_dim_get_quote(AdgDim *dim)
617 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
619 return dim->priv->quote;
623 * adg_dim_set_quote:
624 * @dim: an #AdgDim
625 * @quote: the quote text
627 * Explicitely sets the text to use as quote. If @quote is %NULL or
628 * was never set, an automatic text is calculated using the format as
629 * specified by the dim_style associated to this entity and getting
630 * the number from the construction points (ref1, ref2, pos1 and pos2).
632 void
633 adg_dim_set_quote(AdgDim *dim, const gchar *quote)
635 g_return_if_fail(ADG_IS_DIM(dim));
637 g_free(dim->priv->quote);
638 dim->priv->quote = g_strdup(quote);
639 g_object_notify((GObject *) dim, "quote");
641 text_cache_clear(&dim->priv->quote_cache);
645 * adg_dim_get_tolerance_up:
646 * @dim: an #AdgDim
648 * Gets the upper tolerance text or %NULL on upper tolerance disabled.
649 * The string is internally owned and must not be freed or modified.
651 * Return value: the tolerance text
653 const gchar *
654 adg_dim_get_tolerance_up(AdgDim *dim)
656 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
658 return dim->priv->tolerance_up;
662 * adg_dim_set_tolerance_up:
663 * @dim: an #AdgDim
664 * @tolerance_up: the upper tolerance
666 * Sets the upper tolerance. Use %NULL as @tolerance_up to disable it.
668 void
669 adg_dim_set_tolerance_up(AdgDim *dim, const gchar *tolerance_up)
671 g_return_if_fail(ADG_IS_DIM(dim));
673 g_free(dim->priv->tolerance_up);
674 dim->priv->tolerance_up = g_strdup(tolerance_up);
675 g_object_notify((GObject *) dim, "tolerance-up");
677 text_cache_clear(&dim->priv->tolerance_up_cache);
681 * adg_dim_get_tolerance_down:
682 * @dim: an #AdgDim
684 * Gets the lower tolerance text or %NULL on lower tolerance disabled.
685 * The string is internally owned and must not be freed or modified.
687 * Return value: the tolerance text
689 const gchar *
690 adg_dim_get_tolerance_down(AdgDim *dim)
692 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
694 return dim->priv->tolerance_down;
698 * adg_dim_set_tolerance_down:
699 * @dim: an #AdgDim
700 * @tolerance_down: the lower tolerance
702 * Sets the lower tolerance. Use %NULL as @tolerance_down to disable it.
704 void
705 adg_dim_set_tolerance_down(AdgDim *dim, const gchar *tolerance_down)
707 g_return_if_fail(ADG_IS_DIM(dim));
709 g_free(dim->priv->tolerance_down);
710 dim->priv->tolerance_down = g_strdup(tolerance_down);
711 g_object_notify((GObject *) dim, "tolerance-down");
713 text_cache_clear(&dim->priv->tolerance_down_cache);
717 * adg_dim_set_tolerances:
718 * @dim: an #AdgDim
719 * @tolerance_up: the upper tolerance text
720 * @tolerance_down: the lower tolerance text
722 * Shortcut to set both the tolerance at once.
724 void
725 adg_dim_set_tolerances(AdgDim *dim, const gchar *tolerance_up,
726 const gchar *tolerance_down)
728 g_return_if_fail(ADG_IS_DIM(dim));
730 g_object_freeze_notify((GObject *) dim);
731 adg_dim_set_tolerance_up(dim, tolerance_up);
732 adg_dim_set_tolerance_down(dim, tolerance_down);
733 g_object_thaw_notify((GObject *) dim);
737 * adg_dim_get_note:
738 * @dim: and #AdgDim
740 * Gets the note text or %NULL if the note is not used. The string is
741 * internally owned and must not be freed or modified.
743 * Return value: the note text
745 const gchar *
746 adg_dim_get_note(AdgDim *dim)
748 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
750 return dim->priv->note;
754 * adg_dim_set_note:
755 * @dim: an #AdgDim
756 * @note: the new note
758 * Sets a new note text, usually appended at the end of the dimension text.
760 void
761 adg_dim_set_note(AdgDim *dim, const gchar *note)
763 g_return_if_fail(ADG_IS_DIM(dim));
765 g_free(dim->priv->note);
766 dim->priv->note = g_strdup(note);
767 g_object_notify((GObject *) dim, "note");
769 text_cache_clear(&dim->priv->note_cache);
773 * adg_dim_render_quote:
774 * @dim: an #AdgDim object
775 * @cr: a #cairo_t drawing context
777 * Renders the quote of @dim at the @org position. This function
778 * is only useful in new dimension implementations.
780 void
781 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
783 AdgDimPrivate *priv;
784 AdgDimStyle *dim_style;
786 g_return_if_fail(ADG_IS_DIM(dim));
788 priv = dim->priv;
789 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
790 ADG_SLOT_DIM_STYLE);
792 if (priv->quote == NULL)
793 adg_dim_set_quote(dim, ADG_DIM_GET_CLASS(dim)->default_quote(dim));
795 cairo_save(cr);
797 cairo_set_matrix(cr, adg_entity_get_paper_matrix((AdgEntity *) dim));
798 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
799 cairo_set_matrix(cr, adg_entity_get_model_matrix((AdgEntity *) dim));
801 cairo_translate(cr, priv->org.x, priv->org.y);
802 adg_entity_scale_to_paper((AdgEntity *) dim, cr);
803 cairo_rotate(cr, -priv->angle);
805 /* Rendering quote */
806 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
807 text_cache_render(&priv->quote_cache, cr);
809 /* Rendering tolerances */
810 if (priv->tolerance_up != NULL || priv->tolerance_down != NULL) {
811 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
813 if (priv->tolerance_up != NULL)
814 text_cache_render(&priv->tolerance_up_cache, cr);
816 if (priv->tolerance_down != NULL)
817 text_cache_render(&priv->tolerance_down_cache, cr);
820 /* Rendering the note */
821 if (priv->note != NULL) {
822 adg_style_apply(adg_dim_style_get_note_style(dim_style), cr);
823 text_cache_render(&priv->note_cache, cr);
826 cairo_restore(cr);
830 static void
831 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
833 clear((AdgDim *) entity);
834 PARENT_CLASS->paper_matrix_changed(entity, parent_matrix);
837 static void
838 invalidate(AdgEntity *entity)
840 clear((AdgDim *) entity);
841 PARENT_CLASS->invalidate(entity);
844 static void
845 clear(AdgDim *dim)
847 text_cache_clear(&dim->priv->quote_cache);
848 text_cache_clear(&dim->priv->tolerance_up_cache);
849 text_cache_clear(&dim->priv->tolerance_down_cache);
850 text_cache_clear(&dim->priv->note_cache);
853 static gchar *
854 default_quote(AdgDim *dim)
856 g_warning("AdgDim::default_quote not implemented for `%s'",
857 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
858 return g_strdup("undef");
861 static void
862 quote_layout(AdgDim *dim, cairo_t *cr)
864 AdgDimPrivate *priv;
865 AdgDimStyle *dim_style;
866 AdgPair shift;
867 CpmlPair cp;
868 CpmlPair tolerance_up_org, tolerance_down_org, note_org;
870 priv = dim->priv;
871 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
872 ADG_SLOT_DIM_STYLE);
874 /* Compute the quote */
875 if (text_cache_update(&priv->quote_cache, priv->quote, cr,
876 adg_dim_style_get_quote_style(dim_style))) {
877 cp.x = priv->quote_cache.extents.width;
878 cp.y = priv->quote_cache.extents.height / -2.;
879 } else {
880 cp.x = 0;
881 cp.y = 0;
884 /* Compute the tolerances */
885 if (priv->tolerance_up != NULL || priv->tolerance_down != NULL) {
886 gdouble width;
887 gdouble midspacing;
889 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
891 width = 0;
892 midspacing = adg_dim_style_get_tolerance_spacing(dim_style) / 2.;
893 cpml_pair_copy(&shift, adg_dim_style_get_tolerance_shift(dim_style));
894 cp.x += shift.x;
896 if (text_cache_update(&priv->tolerance_up_cache,
897 priv->tolerance_up, cr, NULL)) {
898 tolerance_up_org.x = cp.x;
899 tolerance_up_org.y = cp.y + shift.y - midspacing;
901 width = priv->tolerance_up_cache.extents.width;
904 if (text_cache_update(&priv->tolerance_down_cache,
905 priv->tolerance_down, cr, NULL)) {
906 tolerance_down_org.x = cp.x;
907 tolerance_down_org.y = cp.y + shift.y + midspacing +
908 priv->tolerance_down_cache.extents.height;
910 if (priv->tolerance_down_cache.extents.width > width)
911 width = priv->tolerance_down_cache.extents.width;
914 cp.x += width;
917 /* Compute the note */
918 if (text_cache_update(&priv->note_cache, priv->note, cr,
919 adg_dim_style_get_note_style(dim_style))) {
920 cpml_pair_copy(&shift, adg_dim_style_get_note_shift(dim_style));
921 cp.x += shift.x;
923 note_org.x = cp.x;
924 note_org.y = cp.y + shift.y + priv->note_cache.extents.height / 2.;
926 cp.x += priv->note_cache.extents.width;
929 /* Centering and shifting the whole group */
930 cpml_pair_copy(&shift, adg_dim_style_get_quote_shift(dim_style));
931 shift.x -= cp.x / 2.;
933 if (priv->quote_cache.glyphs) {
934 text_cache_move_to(&priv->quote_cache, &shift);
937 if (priv->tolerance_up_cache.glyphs) {
938 tolerance_up_org.x += shift.x;
939 tolerance_up_org.y += shift.y;
940 text_cache_move_to(&priv->tolerance_up_cache, &tolerance_up_org);
943 if (priv->tolerance_down_cache.glyphs) {
944 tolerance_down_org.x += shift.x;
945 tolerance_down_org.y += shift.y;
946 text_cache_move_to(&priv->tolerance_down_cache, &tolerance_down_org);
949 if (priv->note_cache.glyphs) {
950 note_org.x += shift.x;
951 note_org.y += shift.y;
952 text_cache_move_to(&priv->note_cache, &note_org);
956 static void
957 text_cache_init(AdgTextCache *text_cache)
959 text_cache->utf8 = NULL;
960 text_cache->utf8_len = -1;
961 text_cache->glyphs = NULL;
962 text_cache->num_glyphs = 0;
963 text_cache->clusters = NULL;
964 text_cache->num_clusters = 0;
965 text_cache->cluster_flags = 0;
966 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
969 static gboolean
970 text_cache_update(AdgTextCache *text_cache, const gchar *text,
971 cairo_t *cr, AdgStyle *style)
973 if (!text)
974 return FALSE;
976 text_cache->utf8 = text;
977 text_cache->utf8_len = g_utf8_strlen(text, -1);
979 if (style)
980 adg_style_apply(style, cr);
982 if (!text_cache->glyphs) {
983 cairo_status_t status;
985 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
986 0, 0,
987 text_cache->utf8,
988 text_cache->utf8_len,
989 &text_cache->glyphs,
990 &text_cache->num_glyphs,
991 &text_cache->clusters,
992 &text_cache->num_clusters,
993 &text_cache->cluster_flags);
995 if (status != CAIRO_STATUS_SUCCESS) {
996 g_error("Unable to build glyphs (cairo message: %s)",
997 cairo_status_to_string(status));
998 return FALSE;
1001 cairo_glyph_extents(cr, text_cache->glyphs, text_cache->num_glyphs,
1002 &text_cache->extents);
1005 return TRUE;
1008 static void
1009 text_cache_clear(AdgTextCache *text_cache)
1011 text_cache->utf8 = NULL;
1012 text_cache->utf8_len = -1;
1014 if (text_cache->glyphs) {
1015 cairo_glyph_free(text_cache->glyphs);
1016 text_cache->glyphs = NULL;
1017 text_cache->num_glyphs = 0;
1019 if (text_cache->clusters) {
1020 cairo_text_cluster_free(text_cache->clusters);
1021 text_cache->clusters = NULL;
1022 text_cache->num_clusters = 0;
1023 text_cache->cluster_flags = 0;
1025 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1028 static void
1029 text_cache_move_to(AdgTextCache *text_cache, const CpmlPair *to)
1031 cairo_glyph_t *glyph;
1032 int cnt;
1033 gdouble x, y;
1035 glyph = text_cache->glyphs;
1036 cnt = text_cache->num_glyphs;
1037 x = to->x - glyph->x;
1038 y = to->y - glyph->y;
1040 while (cnt --) {
1041 glyph->x += x;
1042 glyph->y += y;
1043 ++ glyph;
1047 static void
1048 text_cache_render(AdgTextCache *text_cache, cairo_t *cr)
1050 cairo_show_text_glyphs(cr, text_cache->utf8, text_cache->utf8_len,
1051 text_cache->glyphs, text_cache->num_glyphs,
1052 text_cache->clusters, text_cache->num_clusters,
1053 text_cache->cluster_flags);