[build] Ignoring libtool m4 files
[adg.git] / adg / adg-dim.c
blobc28d7179b42c19577618c0a6fc0ca7cfbfcedb06
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>
35 enum {
36 PROP_0,
37 PROP_REF1,
38 PROP_REF2,
39 PROP_POS1,
40 PROP_POS2,
41 PROP_LEVEL,
42 PROP_QUOTE,
43 PROP_TOLERANCE_UP,
44 PROP_TOLERANCE_DOWN,
45 PROP_NOTE
49 static void finalize (GObject *object);
50 static void get_property (GObject *object,
51 guint param_id,
52 GValue *value,
53 GParamSpec *pspec);
54 static void set_property (GObject *object,
55 guint param_id,
56 const GValue *value,
57 GParamSpec *pspec);
58 static void paper_matrix_changed (AdgEntity *entity,
59 AdgMatrix *parent_matrix);
60 static void invalidate (AdgEntity *entity);
61 static void clear (AdgDim *entity);
62 static gchar * default_quote (AdgDim *dim);
63 static void quote_layout (AdgDim *dim,
64 cairo_t *cr);
65 static void text_cache_init (AdgTextCache *text_cache);
66 static gboolean text_cache_update (AdgTextCache *text_cache,
67 const gchar *text,
68 cairo_t *cr,
69 AdgStyle *style);
70 static void text_cache_clear (AdgTextCache *text_cache);
71 static void text_cache_move_to (AdgTextCache *text_cache,
72 const CpmlPair *to);
73 static void text_cache_render (AdgTextCache *text_cache,
74 cairo_t *cr);
77 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
80 static void
81 adg_dim_class_init(AdgDimClass *klass)
83 GObjectClass *gobject_class;
84 AdgEntityClass *entity_class;
85 GParamSpec *param;
87 gobject_class = (GObjectClass *) klass;
88 entity_class = (AdgEntityClass *) klass;
90 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
92 gobject_class->finalize = finalize;
93 gobject_class->get_property = get_property;
94 gobject_class->set_property = set_property;
96 entity_class->paper_matrix_changed = paper_matrix_changed;
97 entity_class->invalidate = invalidate;
99 klass->default_quote = default_quote;
100 klass->quote_layout = quote_layout;
102 param = g_param_spec_boxed("ref1",
103 P_("Reference 1"),
104 P_("First reference point of the dimension"),
105 ADG_TYPE_PAIR,
106 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
107 g_object_class_install_property(gobject_class, PROP_REF1, param);
109 param = g_param_spec_boxed("ref2",
110 P_("Reference 2"),
111 P_("Second reference point of the dimension"),
112 ADG_TYPE_PAIR,
113 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
114 g_object_class_install_property(gobject_class, PROP_REF2, param);
116 param = g_param_spec_boxed("pos1",
117 P_("Position 1"),
118 P_("First position point: it will be computed with the level property to get the real dimension position"),
119 ADG_TYPE_PAIR, G_PARAM_READWRITE);
120 g_object_class_install_property(gobject_class, PROP_POS1, param);
122 param = g_param_spec_boxed("pos2",
123 P_("Position 2"),
124 P_("Second position point: it will be computed with the level property to get the real dimension position"),
125 ADG_TYPE_PAIR, G_PARAM_READWRITE);
126 g_object_class_install_property(gobject_class, PROP_POS2, param);
128 param = g_param_spec_double("level",
129 P_("Level"),
130 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"),
131 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
132 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
133 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
135 param = g_param_spec_string("quote",
136 P_("Quote"),
137 P_("The quote to display: set to NULL to get the default quote"),
138 NULL, G_PARAM_READWRITE);
139 g_object_class_install_property(gobject_class, PROP_QUOTE, param);
141 param = g_param_spec_string("tolerance-up",
142 P_("Up Tolerance"),
143 P_("The up tolerance of the quote: set to NULL to suppress"),
144 NULL, G_PARAM_READWRITE);
145 g_object_class_install_property(gobject_class, PROP_TOLERANCE_UP, param);
147 param = g_param_spec_string("tolerance-down",
148 P_("Down Tolerance"),
149 P_("The down tolerance of the quote: set to NULL to suppress"),
150 NULL, G_PARAM_READWRITE);
151 g_object_class_install_property(gobject_class, PROP_TOLERANCE_DOWN, param);
153 param = g_param_spec_string("note",
154 P_("Note"),
155 P_("A custom note appended to the dimension quote"),
156 NULL, G_PARAM_READWRITE);
157 g_object_class_install_property(gobject_class, PROP_NOTE, param);
160 static void
161 adg_dim_init(AdgDim *dim)
163 AdgDimPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
164 AdgDimPrivate);
166 priv->ref1.x = priv->ref1.y = 0;
167 priv->ref2.x = priv->ref2.y = 0;
168 priv->pos1.x = priv->pos1.y = 0;
169 priv->pos2.x = priv->pos2.y = 0;
170 priv->level = 1.;
171 priv->quote = NULL;
172 priv->tolerance_up = NULL;
173 priv->tolerance_down = NULL;
174 priv->note = NULL;
176 priv->org.x = priv->org.y = 0;
177 priv->angle = 0;
178 text_cache_init(&priv->quote_cache);
179 text_cache_init(&priv->tolerance_up_cache);
180 text_cache_init(&priv->tolerance_down_cache);
181 text_cache_init(&priv->note_cache);
183 dim->priv = priv;
186 static void
187 finalize(GObject *object)
189 AdgDim *dim;
190 GObjectClass *object_class;
192 dim = (AdgDim *) object;
193 object_class = (GObjectClass *) adg_dim_parent_class;
195 g_free(dim->priv->quote);
196 g_free(dim->priv->tolerance_up);
197 g_free(dim->priv->tolerance_down);
199 clear(dim);
201 if (object_class->finalize != NULL)
202 object_class->finalize(object);
205 static void
206 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
208 AdgDim *dim = (AdgDim *) object;
210 switch (prop_id) {
211 case PROP_REF1:
212 g_value_set_boxed(value, &dim->priv->ref1);
213 break;
214 case PROP_REF2:
215 g_value_set_boxed(value, &dim->priv->ref2);
216 break;
217 case PROP_POS1:
218 g_value_set_boxed(value, &dim->priv->pos1);
219 break;
220 case PROP_POS2:
221 g_value_set_boxed(value, &dim->priv->pos1);
222 break;
223 case PROP_LEVEL:
224 g_value_set_double(value, dim->priv->level);
225 break;
226 case PROP_QUOTE:
227 g_value_set_string(value, dim->priv->quote);
228 break;
229 case PROP_TOLERANCE_UP:
230 g_value_set_string(value, dim->priv->tolerance_up);
231 break;
232 case PROP_TOLERANCE_DOWN:
233 g_value_set_string(value, dim->priv->tolerance_down);
234 break;
235 case PROP_NOTE:
236 g_value_set_string(value, dim->priv->note);
237 break;
238 default:
239 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
240 break;
244 static void
245 set_property(GObject *object, guint prop_id,
246 const GValue *value, GParamSpec *pspec)
248 AdgEntity *entity;
249 AdgDim *dim;
251 entity = (AdgEntity *) object;
252 dim = (AdgDim *) object;
254 switch (prop_id) {
255 case PROP_REF1:
256 cpml_pair_copy(&dim->priv->ref1, (AdgPair *) g_value_get_boxed(value));
257 clear(dim);
258 break;
259 case PROP_REF2:
260 cpml_pair_copy(&dim->priv->ref2, (AdgPair *) g_value_get_boxed(value));
261 clear(dim);
262 break;
263 case PROP_POS1:
264 cpml_pair_copy(&dim->priv->pos1, (AdgPair *) g_value_get_boxed(value));
265 clear(dim);
266 break;
267 case PROP_POS2:
268 cpml_pair_copy(&dim->priv->pos2, (AdgPair *) g_value_get_boxed(value));
269 clear(dim);
270 break;
271 case PROP_LEVEL:
272 dim->priv->level = g_value_get_double(value);
273 clear(dim);
274 break;
275 case PROP_QUOTE:
276 g_free(dim->priv->quote);
277 dim->priv->quote = g_value_dup_string(value);
278 text_cache_clear(&dim->priv->quote_cache);
279 break;
280 case PROP_TOLERANCE_UP:
281 g_free(dim->priv->tolerance_up);
282 dim->priv->tolerance_up = g_value_dup_string(value);
283 text_cache_clear(&dim->priv->tolerance_up_cache);
284 break;
285 case PROP_TOLERANCE_DOWN:
286 g_free(dim->priv->tolerance_down);
287 dim->priv->tolerance_down = g_value_dup_string(value);
288 text_cache_clear(&dim->priv->tolerance_down_cache);
289 break;
290 case PROP_NOTE:
291 g_free(dim->priv->note);
292 dim->priv->note = g_value_dup_string(value);
293 text_cache_clear(&dim->priv->note_cache);
294 break;
295 default:
296 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
297 break;
303 * adg_dim_get_org:
304 * @dim: an #AdgDim
306 * Gets the origin (org) coordinates. The returned pair is internally
307 * owned and must not be freed or modified. This function is only
308 * useful in new dimension implementations.
310 * Return value: the org coordinates
312 const AdgPair *
313 adg_dim_get_org(AdgDim *dim)
315 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
317 return &dim->priv->org;
321 * adg_dim_set_org:
322 * @dim: an #AdgDim
323 * @org: the org coordinates
325 * Sets new org coordinates. This function is only useful
326 * in new dimension implementations.
328 void
329 adg_dim_set_org(AdgDim *dim, const AdgPair *org)
331 g_return_if_fail(ADG_IS_DIM(dim));
332 g_return_if_fail(org != NULL);
334 dim->priv->org = *org;
338 * adg_dim_set_org_explicit:
339 * @dim: an #AdgDim
340 * @org_x: x component of org
341 * @org_y: y component of org
343 * Explicitely sets new org coordinates. This function is only useful
344 * in new dimension implementations.
346 void
347 adg_dim_set_org_explicit(AdgDim *dim, gdouble org_x, gdouble org_y)
349 g_return_if_fail(ADG_IS_DIM(dim));
351 dim->priv->org.x = org_x;
352 dim->priv->org.y = org_y;
356 * adg_dim_get_angle:
357 * @dim: an #AdgDim
359 * Gets the dimension angle. This function is only useful
360 * in new dimension implementations.
362 * Return value: the angle (in radians)
364 gdouble
365 adg_dim_get_angle(AdgDim *dim)
367 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
369 return dim->priv->angle;
373 * adg_dim_set_angle:
374 * @dim: an #AdgDim
375 * @angle: the new angle (in radians)
377 * Sets a new dimension angle. This function is only useful
378 * in new dimension implementations.
380 void
381 adg_dim_set_angle(AdgDim *dim, gdouble angle)
383 g_return_if_fail(ADG_IS_DIM(dim));
385 dim->priv->angle = angle;
389 * adg_dim_get_ref1:
390 * @dim: an #AdgDim
392 * Gets the ref1 coordinates. The returned pair is internally owned
393 * and must not be freed or modified.
395 * Return value: the ref1 coordinates
397 const AdgPair *
398 adg_dim_get_ref1(AdgDim *dim)
400 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
402 return &dim->priv->ref1;
406 * adg_dim_get_ref2:
407 * @dim: an #AdgDim
409 * Gets the ref2 coordinates. The returned pair is internally owned
410 * and must not be freed or modified.
412 * Return value: the ref2 coordinates
414 const AdgPair *
415 adg_dim_get_ref2(AdgDim *dim)
417 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
419 return &dim->priv->ref2;
423 * adg_dim_set_ref:
424 * @dim: an #AdgDim
425 * @ref1: the ref1 coordinates
426 * @ref2: the ref2 coordinates
428 * Shortcut to set ref1 and ref2 points at once.
430 void
431 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
433 g_return_if_fail(ADG_IS_DIM(dim));
435 if (ref1 != NULL || ref2 != NULL) {
436 GObject *object = (GObject *) dim;
438 g_object_freeze_notify(object);
440 if (ref1 != NULL) {
441 dim->priv->ref1 = *ref1;
442 g_object_notify(object, "ref1");
445 if (ref2 != NULL) {
446 dim->priv->ref2 = *ref2;
447 g_object_notify(object, "ref2");
450 g_object_thaw_notify(object);
451 clear(dim);
456 * adg_dim_set_ref_explicit:
457 * @dim: an #AdgDim
458 * @ref1_x: x component of pos1
459 * @ref1_y: y component of pos1
460 * @ref2_x: x component of pos2
461 * @ref2_y: y component of pos2
463 * Shortcut to set ref1 and ref2 points at once,
464 * using explicit coordinates.
466 void
467 adg_dim_set_ref_explicit(AdgDim *dim, gdouble ref1_x, gdouble ref1_y,
468 gdouble ref2_x, gdouble ref2_y)
470 AdgPair ref1;
471 AdgPair ref2;
473 ref1.x = ref1_x;
474 ref1.y = ref1_y;
475 ref2.x = ref2_x;
476 ref2.y = ref2_y;
478 adg_dim_set_ref(dim, &ref1, &ref2);
482 * adg_dim_get_pos1:
483 * @dim: an #AdgDim
485 * Gets the pos1 coordinates. The returned pair is internally owned
486 * and must not be freed or modified.
488 * Return value: the pos1 coordinates
490 const AdgPair *
491 adg_dim_get_pos1(AdgDim *dim)
493 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
495 return &dim->priv->pos1;
499 * adg_dim_get_pos2:
500 * @dim: an #AdgDim
502 * Gets the pos2 coordinates. The returned pair is internally owned
503 * and must not be freed or modified.
505 * Return value: the pos2 coordinates
507 const AdgPair *
508 adg_dim_get_pos2(AdgDim *dim)
510 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
512 return &dim->priv->pos2;
516 * adg_dim_set_pos:
517 * @dim: an #AdgDim
518 * @pos1: the pos1 coordinates
519 * @pos2: the pos2 coordinates
521 * Shortcut to set pos1 and pos2 points at once.
523 void
524 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
526 g_return_if_fail(ADG_IS_DIM(dim));
528 if (pos1 != NULL || pos2 != NULL) {
529 GObject *object = (GObject *) dim;
531 g_object_freeze_notify(object);
533 if (pos1 != NULL) {
534 dim->priv->pos1 = *pos1;
535 g_object_notify(object, "pos1");
537 if (pos2 != NULL) {
538 dim->priv->pos2 = *pos2;
539 g_object_notify(object, "pos2");
542 g_object_thaw_notify(object);
543 clear(dim);
548 * adg_dim_set_pos_explicit:
549 * @dim: an #AdgDim
550 * @pos1_x: x component of pos1
551 * @pos1_y: y component of pos1
552 * @pos2_x: x component of pos2
553 * @pos2_y: y component of pos2
555 * Shortcut to set pos1 and pos2 points at once,
556 * using explicit coordinates.
558 void
559 adg_dim_set_pos_explicit(AdgDim *dim, gdouble pos1_x, gdouble pos1_y,
560 gdouble pos2_x, gdouble pos2_y)
562 AdgPair pos1;
563 AdgPair pos2;
565 pos1.x = pos1_x;
566 pos1.y = pos1_y;
567 pos2.x = pos2_x;
568 pos2.y = pos2_y;
570 adg_dim_set_pos(dim, &pos1, &pos2);
574 * adg_dim_get_level:
575 * @dim: an #AdgDim
577 * Gets the level of this dimension.
579 * Return value: the level value
581 gdouble
582 adg_dim_get_level(AdgDim *dim)
584 g_return_val_if_fail(ADG_IS_DIM(dim), 0);
586 return dim->priv->level;
590 * adg_dim_set_level:
591 * @dim: an #AdgDim
592 * @level: the new level
594 * Sets a new level for this dimension. The level is used to
595 * stack the quotes using a spacing value from dim_style
596 * (specified in paper space).
598 void
599 adg_dim_set_level(AdgDim *dim, gdouble level)
601 g_return_if_fail(ADG_IS_DIM(dim));
603 dim->priv->level = level;
604 g_object_notify((GObject *) dim, "level");
605 clear(dim);
609 * adg_dim_get_quote:
610 * @dim: an #AdgDim
612 * Gets the quote text. The string is internally owned and
613 * must not be freed or modified.
615 * Return value: the quote text
617 const gchar *
618 adg_dim_get_quote(AdgDim *dim)
620 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
622 return dim->priv->quote;
626 * adg_dim_set_quote:
627 * @dim: an #AdgDim
628 * @quote: the quote text
630 * Explicitely sets the text to use as quote. If @quote is %NULL or
631 * was never set, an automatic text is calculated using the format as
632 * specified by the dim_style associated to this entity and getting
633 * the number from the construction points (ref1, ref2, pos1 and pos2).
635 void
636 adg_dim_set_quote(AdgDim *dim, const gchar *quote)
638 g_return_if_fail(ADG_IS_DIM(dim));
640 g_free(dim->priv->quote);
641 dim->priv->quote = g_strdup(quote);
642 g_object_notify((GObject *) dim, "quote");
644 text_cache_clear(&dim->priv->quote_cache);
648 * adg_dim_get_tolerance_up:
649 * @dim: an #AdgDim
651 * Gets the upper tolerance text or %NULL on upper tolerance disabled.
652 * The string is internally owned and must not be freed or modified.
654 * Return value: the tolerance text
656 const gchar *
657 adg_dim_get_tolerance_up(AdgDim *dim)
659 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
661 return dim->priv->tolerance_up;
665 * adg_dim_set_tolerance_up:
666 * @dim: an #AdgDim
667 * @tolerance_up: the upper tolerance
669 * Sets the upper tolerance. Use %NULL as @tolerance_up to disable it.
671 void
672 adg_dim_set_tolerance_up(AdgDim *dim, const gchar *tolerance_up)
674 g_return_if_fail(ADG_IS_DIM(dim));
676 g_free(dim->priv->tolerance_up);
677 dim->priv->tolerance_up = g_strdup(tolerance_up);
678 g_object_notify((GObject *) dim, "tolerance-up");
680 text_cache_clear(&dim->priv->tolerance_up_cache);
684 * adg_dim_get_tolerance_down:
685 * @dim: an #AdgDim
687 * Gets the lower tolerance text or %NULL on lower tolerance disabled.
688 * The string is internally owned and must not be freed or modified.
690 * Return value: the tolerance text
692 const gchar *
693 adg_dim_get_tolerance_down(AdgDim *dim)
695 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
697 return dim->priv->tolerance_down;
701 * adg_dim_set_tolerance_down:
702 * @dim: an #AdgDim
703 * @tolerance_down: the lower tolerance
705 * Sets the lower tolerance. Use %NULL as @tolerance_down to disable it.
707 void
708 adg_dim_set_tolerance_down(AdgDim *dim, const gchar *tolerance_down)
710 g_return_if_fail(ADG_IS_DIM(dim));
712 g_free(dim->priv->tolerance_down);
713 dim->priv->tolerance_down = g_strdup(tolerance_down);
714 g_object_notify((GObject *) dim, "tolerance-down");
716 text_cache_clear(&dim->priv->tolerance_down_cache);
720 * adg_dim_set_tolerances:
721 * @dim: an #AdgDim
722 * @tolerance_up: the upper tolerance text
723 * @tolerance_down: the lower tolerance text
725 * Shortcut to set both the tolerance at once.
727 void
728 adg_dim_set_tolerances(AdgDim *dim, const gchar *tolerance_up,
729 const gchar *tolerance_down)
731 g_return_if_fail(ADG_IS_DIM(dim));
733 g_object_freeze_notify((GObject *) dim);
734 adg_dim_set_tolerance_up(dim, tolerance_up);
735 adg_dim_set_tolerance_down(dim, tolerance_down);
736 g_object_thaw_notify((GObject *) dim);
740 * adg_dim_get_note:
741 * @dim: and #AdgDim
743 * Gets the note text or %NULL if the note is not used. The string is
744 * internally owned and must not be freed or modified.
746 * Return value: the note text
748 const gchar *
749 adg_dim_get_note(AdgDim *dim)
751 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
753 return dim->priv->note;
757 * adg_dim_set_note:
758 * @dim: an #AdgDim
759 * @note: the new note
761 * Sets a new note text, usually appended at the end of the dimension text.
763 void
764 adg_dim_set_note(AdgDim *dim, const gchar *note)
766 g_return_if_fail(ADG_IS_DIM(dim));
768 g_free(dim->priv->note);
769 dim->priv->note = g_strdup(note);
770 g_object_notify((GObject *) dim, "note");
772 text_cache_clear(&dim->priv->note_cache);
776 * adg_dim_render_quote:
777 * @dim: an #AdgDim object
778 * @cr: a #cairo_t drawing context
780 * Renders the quote of @dim at the @org position. This function
781 * is only useful in new dimension implementations.
783 void
784 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
786 AdgDimPrivate *priv;
787 AdgDimStyle *dim_style;
789 g_return_if_fail(ADG_IS_DIM(dim));
791 priv = dim->priv;
792 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
793 ADG_SLOT_DIM_STYLE);
795 if (priv->quote == NULL)
796 adg_dim_set_quote(dim, ADG_DIM_GET_CLASS(dim)->default_quote(dim));
798 cairo_save(cr);
800 cairo_set_matrix(cr, adg_entity_get_paper_matrix((AdgEntity *) dim));
801 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
802 cairo_set_matrix(cr, adg_entity_get_model_matrix((AdgEntity *) dim));
804 cairo_translate(cr, priv->org.x, priv->org.y);
805 adg_entity_scale_to_paper((AdgEntity *) dim, cr);
806 cairo_rotate(cr, -priv->angle);
808 /* Rendering quote */
809 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
810 text_cache_render(&priv->quote_cache, cr);
812 /* Rendering tolerances */
813 if (priv->tolerance_up != NULL || priv->tolerance_down != NULL) {
814 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
816 if (priv->tolerance_up != NULL)
817 text_cache_render(&priv->tolerance_up_cache, cr);
819 if (priv->tolerance_down != NULL)
820 text_cache_render(&priv->tolerance_down_cache, cr);
823 /* Rendering the note */
824 if (priv->note != NULL) {
825 adg_style_apply(adg_dim_style_get_note_style(dim_style), cr);
826 text_cache_render(&priv->note_cache, cr);
829 cairo_restore(cr);
833 static void
834 paper_matrix_changed(AdgEntity *entity, AdgMatrix *parent_matrix)
836 AdgEntityClass *entity_class = (AdgEntityClass *) adg_dim_parent_class;
838 clear((AdgDim *) entity);
840 if (entity_class->paper_matrix_changed != NULL)
841 entity_class->paper_matrix_changed(entity, parent_matrix);
844 static void
845 invalidate(AdgEntity *entity)
847 AdgEntityClass *entity_class = (AdgEntityClass *) adg_dim_parent_class;
849 clear((AdgDim *) entity);
851 if (entity_class->invalidate != NULL)
852 entity_class->invalidate(entity);
855 static void
856 clear(AdgDim *dim)
858 text_cache_clear(&dim->priv->quote_cache);
859 text_cache_clear(&dim->priv->tolerance_up_cache);
860 text_cache_clear(&dim->priv->tolerance_down_cache);
861 text_cache_clear(&dim->priv->note_cache);
864 static gchar *
865 default_quote(AdgDim *dim)
867 g_warning("AdgDim::default_quote not implemented for `%s'",
868 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
869 return g_strdup("undef");
872 static void
873 quote_layout(AdgDim *dim, cairo_t *cr)
875 AdgDimPrivate *priv;
876 AdgDimStyle *dim_style;
877 AdgPair shift;
878 CpmlPair cp;
879 CpmlPair tolerance_up_org, tolerance_down_org, note_org;
881 priv = dim->priv;
882 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
883 ADG_SLOT_DIM_STYLE);
885 /* Compute the quote */
886 if (text_cache_update(&priv->quote_cache, priv->quote, cr,
887 adg_dim_style_get_quote_style(dim_style))) {
888 cp.x = priv->quote_cache.extents.width;
889 cp.y = priv->quote_cache.extents.height / -2.;
890 } else {
891 cp.x = 0;
892 cp.y = 0;
895 /* Compute the tolerances */
896 if (priv->tolerance_up != NULL || priv->tolerance_down != NULL) {
897 gdouble width;
898 gdouble midspacing;
900 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
902 width = 0;
903 midspacing = adg_dim_style_get_tolerance_spacing(dim_style) / 2.;
904 cpml_pair_copy(&shift, adg_dim_style_get_tolerance_shift(dim_style));
905 cp.x += shift.x;
907 if (text_cache_update(&priv->tolerance_up_cache,
908 priv->tolerance_up, cr, NULL)) {
909 tolerance_up_org.x = cp.x;
910 tolerance_up_org.y = cp.y + shift.y - midspacing;
912 width = priv->tolerance_up_cache.extents.width;
915 if (text_cache_update(&priv->tolerance_down_cache,
916 priv->tolerance_down, cr, NULL)) {
917 tolerance_down_org.x = cp.x;
918 tolerance_down_org.y = cp.y + shift.y + midspacing +
919 priv->tolerance_down_cache.extents.height;
921 if (priv->tolerance_down_cache.extents.width > width)
922 width = priv->tolerance_down_cache.extents.width;
925 cp.x += width;
928 /* Compute the note */
929 if (text_cache_update(&priv->note_cache, priv->note, cr,
930 adg_dim_style_get_note_style(dim_style))) {
931 cpml_pair_copy(&shift, adg_dim_style_get_note_shift(dim_style));
932 cp.x += shift.x;
934 note_org.x = cp.x;
935 note_org.y = cp.y + shift.y + priv->note_cache.extents.height / 2.;
937 cp.x += priv->note_cache.extents.width;
940 /* Centering and shifting the whole group */
941 cpml_pair_copy(&shift, adg_dim_style_get_quote_shift(dim_style));
942 shift.x -= cp.x / 2.;
944 if (priv->quote_cache.glyphs) {
945 text_cache_move_to(&priv->quote_cache, &shift);
948 if (priv->tolerance_up_cache.glyphs) {
949 tolerance_up_org.x += shift.x;
950 tolerance_up_org.y += shift.y;
951 text_cache_move_to(&priv->tolerance_up_cache, &tolerance_up_org);
954 if (priv->tolerance_down_cache.glyphs) {
955 tolerance_down_org.x += shift.x;
956 tolerance_down_org.y += shift.y;
957 text_cache_move_to(&priv->tolerance_down_cache, &tolerance_down_org);
960 if (priv->note_cache.glyphs) {
961 note_org.x += shift.x;
962 note_org.y += shift.y;
963 text_cache_move_to(&priv->note_cache, &note_org);
967 static void
968 text_cache_init(AdgTextCache *text_cache)
970 text_cache->utf8 = NULL;
971 text_cache->utf8_len = -1;
972 text_cache->glyphs = NULL;
973 text_cache->num_glyphs = 0;
974 text_cache->clusters = NULL;
975 text_cache->num_clusters = 0;
976 text_cache->cluster_flags = 0;
977 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
980 static gboolean
981 text_cache_update(AdgTextCache *text_cache, const gchar *text,
982 cairo_t *cr, AdgStyle *style)
984 if (!text)
985 return FALSE;
987 text_cache->utf8 = text;
988 text_cache->utf8_len = g_utf8_strlen(text, -1);
990 if (style)
991 adg_style_apply(style, cr);
993 if (!text_cache->glyphs) {
994 cairo_status_t status;
996 status = cairo_scaled_font_text_to_glyphs(cairo_get_scaled_font(cr),
997 0, 0,
998 text_cache->utf8,
999 text_cache->utf8_len,
1000 &text_cache->glyphs,
1001 &text_cache->num_glyphs,
1002 &text_cache->clusters,
1003 &text_cache->num_clusters,
1004 &text_cache->cluster_flags);
1006 if (status != CAIRO_STATUS_SUCCESS) {
1007 g_error("Unable to build glyphs (cairo message: %s)",
1008 cairo_status_to_string(status));
1009 return FALSE;
1012 cairo_glyph_extents(cr, text_cache->glyphs, text_cache->num_glyphs,
1013 &text_cache->extents);
1016 return TRUE;
1019 static void
1020 text_cache_clear(AdgTextCache *text_cache)
1022 text_cache->utf8 = NULL;
1023 text_cache->utf8_len = -1;
1025 if (text_cache->glyphs) {
1026 cairo_glyph_free(text_cache->glyphs);
1027 text_cache->glyphs = NULL;
1028 text_cache->num_glyphs = 0;
1030 if (text_cache->clusters) {
1031 cairo_text_cluster_free(text_cache->clusters);
1032 text_cache->clusters = NULL;
1033 text_cache->num_clusters = 0;
1034 text_cache->cluster_flags = 0;
1036 memset(&text_cache->extents, 0, sizeof(text_cache->extents));
1039 static void
1040 text_cache_move_to(AdgTextCache *text_cache, const CpmlPair *to)
1042 cairo_glyph_t *glyph;
1043 int cnt;
1044 gdouble x, y;
1046 glyph = text_cache->glyphs;
1047 cnt = text_cache->num_glyphs;
1048 x = to->x - glyph->x;
1049 y = to->y - glyph->y;
1051 while (cnt --) {
1052 glyph->x += x;
1053 glyph->y += y;
1054 ++ glyph;
1058 static void
1059 text_cache_render(AdgTextCache *text_cache, cairo_t *cr)
1061 cairo_show_text_glyphs(cr, text_cache->utf8, text_cache->utf8_len,
1062 text_cache->glyphs, text_cache->num_glyphs,
1063 text_cache->clusters, text_cache->num_clusters,
1064 text_cache->cluster_flags);