Tab expansion in all the source files
[adg.git] / adg / adg-dim.c
blob53d1890667c9f8102a2c108cb510144f87ad848c
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2008, 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 Library 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 * Library 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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, 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"
33 #define PARENT_CLASS ((AdgEntityClass *) adg_dim_parent_class)
36 enum {
37 PROP_0,
38 PROP_REF1,
39 PROP_REF2,
40 PROP_POS1,
41 PROP_POS2,
42 PROP_LEVEL,
43 PROP_QUOTE,
44 PROP_TOLERANCE_UP,
45 PROP_TOLERANCE_DOWN,
46 PROP_NOTE
50 static void finalize (GObject *object);
51 static void get_property (GObject *object,
52 guint param_id,
53 GValue *value,
54 GParamSpec *pspec);
55 static void set_property (GObject *object,
56 guint param_id,
57 const GValue *value,
58 GParamSpec *pspec);
59 static void invalidate (AdgDim *dim);
60 static void invalidate_quote (AdgDim *dim);
61 static gchar * default_quote (AdgDim *dim);
62 static void quote_layout (AdgDim *dim,
63 cairo_t *cr);
66 G_DEFINE_ABSTRACT_TYPE(AdgDim, adg_dim, ADG_TYPE_ENTITY);
69 static void
70 adg_dim_class_init(AdgDimClass *klass)
72 GObjectClass *gobject_class;
73 AdgEntityClass *entity_class;
74 GParamSpec *param;
76 gobject_class = (GObjectClass *) klass;
77 entity_class = (AdgEntityClass *) klass;
79 g_type_class_add_private(klass, sizeof(AdgDimPrivate));
81 gobject_class->finalize = finalize;
82 gobject_class->get_property = get_property;
83 gobject_class->set_property = set_property;
85 klass->default_quote = default_quote;
86 klass->quote_layout = quote_layout;
88 param = g_param_spec_boxed("ref1",
89 P_("Reference 1"),
90 P_("First reference point of the dimension"),
91 ADG_TYPE_PAIR,
92 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
93 g_object_class_install_property(gobject_class, PROP_REF1, param);
95 param = g_param_spec_boxed("ref2",
96 P_("Reference 2"),
97 P_("Second reference point of the dimension"),
98 ADG_TYPE_PAIR,
99 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
100 g_object_class_install_property(gobject_class, PROP_REF2, param);
102 param = g_param_spec_boxed("pos1",
103 P_("Position 1"),
104 P_("First position point: it will be computed with the level property to get the real dimension position"),
105 ADG_TYPE_PAIR, G_PARAM_READWRITE);
106 g_object_class_install_property(gobject_class, PROP_POS1, param);
108 param = g_param_spec_boxed("pos2",
109 P_("Position 2"),
110 P_("Second position point: it will be computed with the level property to get the real dimension position"),
111 ADG_TYPE_PAIR, G_PARAM_READWRITE);
112 g_object_class_install_property(gobject_class, PROP_POS2, param);
114 param = g_param_spec_double("level",
115 P_("Level"),
116 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"),
117 -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
118 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
119 g_object_class_install_property(gobject_class, PROP_LEVEL, param);
121 param = g_param_spec_string("quote",
122 P_("Quote"),
123 P_("The quote to display: set to NULL to get the default quote"),
124 NULL, G_PARAM_READWRITE);
125 g_object_class_install_property(gobject_class, PROP_QUOTE, param);
127 param = g_param_spec_string("tolerance-up",
128 P_("Up Tolerance"),
129 P_("The up tolerance of the quote: set to NULL to suppress"),
130 NULL, G_PARAM_READWRITE);
131 g_object_class_install_property(gobject_class, PROP_TOLERANCE_UP,
132 param);
134 param = g_param_spec_string("tolerance-down",
135 P_("Down Tolerance"),
136 P_("The down tolerance of the quote: set to NULL to suppress"),
137 NULL, G_PARAM_READWRITE);
138 g_object_class_install_property(gobject_class, PROP_TOLERANCE_DOWN,
139 param);
141 param = g_param_spec_string("note",
142 P_("Note"),
143 P_("A custom note appended to the dimension quote"),
144 NULL, G_PARAM_READWRITE);
145 g_object_class_install_property(gobject_class, PROP_NOTE, param);
148 static void
149 adg_dim_init(AdgDim *dim)
151 AdgDimPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE(dim, ADG_TYPE_DIM,
152 AdgDimPrivate);
154 priv->ref1.x = priv->ref1.y = 0.;
155 priv->ref2.x = priv->ref2.y = 0.;
156 priv->pos1.x = priv->pos1.y = 0.;
157 priv->pos2.x = priv->pos2.y = 0.;
158 priv->level = 1.;
159 priv->quote = NULL;
160 priv->tolerance_up = NULL;
161 priv->tolerance_down = NULL;
162 priv->note = NULL;
164 dim->priv = priv;
165 invalidate(dim);
168 static void
169 finalize(GObject *object)
171 AdgDimPrivate *priv = ((AdgDim *) object)->priv;
173 g_free(priv->quote);
174 g_free(priv->tolerance_up);
175 g_free(priv->tolerance_down);
177 ((GObjectClass *) PARENT_CLASS)->finalize(object);
180 static void
181 get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
183 AdgDim *dim = (AdgDim *) object;
185 switch (prop_id) {
186 case PROP_REF1:
187 g_value_set_boxed(value, &dim->priv->ref1);
188 break;
189 case PROP_REF2:
190 g_value_set_boxed(value, &dim->priv->ref2);
191 break;
192 case PROP_POS1:
193 g_value_set_boxed(value, &dim->priv->pos1);
194 break;
195 case PROP_POS2:
196 g_value_set_boxed(value, &dim->priv->pos1);
197 break;
198 case PROP_LEVEL:
199 g_value_set_double(value, dim->priv->level);
200 break;
201 case PROP_QUOTE:
202 g_value_set_string(value, dim->priv->quote);
203 break;
204 case PROP_TOLERANCE_UP:
205 g_value_set_string(value, dim->priv->tolerance_up);
206 break;
207 case PROP_TOLERANCE_DOWN:
208 g_value_set_string(value, dim->priv->tolerance_down);
209 break;
210 case PROP_NOTE:
211 g_value_set_string(value, dim->priv->note);
212 break;
213 default:
214 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
215 break;
219 static void
220 set_property(GObject *object,
221 guint prop_id, const GValue *value, GParamSpec *pspec)
223 AdgDim *dim = (AdgDim *) object;
225 switch (prop_id) {
226 case PROP_REF1:
227 cpml_pair_copy(&dim->priv->ref1,
228 (AdgPair *) g_value_get_boxed(value));
229 invalidate(dim);
230 break;
231 case PROP_REF2:
232 cpml_pair_copy(&dim->priv->ref2,
233 (AdgPair *) g_value_get_boxed(value));
234 invalidate(dim);
235 break;
236 case PROP_POS1:
237 cpml_pair_copy(&dim->priv->pos1,
238 (AdgPair *) g_value_get_boxed(value));
239 invalidate(dim);
240 break;
241 case PROP_POS2:
242 cpml_pair_copy(&dim->priv->pos2,
243 (AdgPair *) g_value_get_boxed(value));
244 invalidate(dim);
245 break;
246 case PROP_LEVEL:
247 dim->priv->level = g_value_get_double(value);
248 invalidate(dim);
249 break;
250 case PROP_QUOTE:
251 g_free(dim->priv->quote);
252 dim->priv->quote = g_value_dup_string(value);
253 invalidate_quote(dim);
254 break;
255 case PROP_TOLERANCE_UP:
256 g_free(dim->priv->tolerance_up);
257 dim->priv->tolerance_up = g_value_dup_string(value);
258 invalidate_quote(dim);
259 break;
260 case PROP_TOLERANCE_DOWN:
261 g_free(dim->priv->tolerance_down);
262 dim->priv->tolerance_down = g_value_dup_string(value);
263 invalidate_quote(dim);
264 break;
265 case PROP_NOTE:
266 g_free(dim->priv->note);
267 dim->priv->note = g_value_dup_string(value);
268 invalidate_quote(dim);
269 break;
270 default:
271 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
272 break;
277 static void
278 invalidate(AdgDim *dim)
280 dim->priv->quote_org.x = dim->priv->quote_org.y = 0.;
281 dim->priv->quote_angle = 0.;
282 invalidate_quote(dim);
285 static void
286 invalidate_quote(AdgDim *dim)
288 dim->priv->quote_shift.x = dim->priv->quote_shift.y = 0.;
289 dim->priv->tolerance_up_shift.x = dim->priv->tolerance_up_shift.y = 0.;
290 dim->priv->tolerance_down_shift.x = dim->priv->tolerance_down_shift.y =
292 dim->priv->note_shift.x = dim->priv->note_shift.y = 0.;
296 static gchar *
297 default_quote(AdgDim *dim)
299 g_warning("AdgDim::default_quote not implemented for `%s'",
300 g_type_name(G_TYPE_FROM_INSTANCE(dim)));
301 return g_strdup("undef");
304 static void
305 quote_layout(AdgDim *dim, cairo_t *cr)
307 AdgDimStyle *dim_style;
308 AdgPair offset;
309 AdgPair cp;
310 cairo_text_extents_t extents;
312 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
313 ADG_SLOT_DIM_STYLE);
315 /* Compute the quote */
316 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
318 cairo_text_extents(cr, dim->priv->quote, &extents);
319 cairo_user_to_device_distance(cr, &extents.width, &extents.height);
320 cp.x = extents.width;
321 cp.y = -extents.height / 2.;
323 /* Compute the tolerances */
324 if (dim->priv->tolerance_up != NULL
325 || dim->priv->tolerance_down != NULL) {
326 double width;
327 double midspacing;
329 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
331 width = 0.0;
332 midspacing = adg_dim_style_get_tolerance_spacing(dim_style) / 2.;
333 cpml_pair_copy(&offset,
334 adg_dim_style_get_tolerance_shift(dim_style));
335 cp.x += offset.x;
337 if (dim->priv->tolerance_up != NULL) {
338 cairo_text_extents(cr, dim->priv->tolerance_up, &extents);
339 cairo_user_to_device_distance(cr, &extents.width,
340 &extents.height);
341 dim->priv->tolerance_up_shift.x = cp.x;
342 dim->priv->tolerance_up_shift.y = cp.y + offset.y - midspacing;
343 width = extents.width;
346 if (dim->priv->tolerance_down != NULL) {
347 cairo_text_extents(cr, dim->priv->tolerance_down, &extents);
348 cairo_user_to_device_distance(cr, &extents.width,
349 &extents.height);
350 dim->priv->tolerance_down_shift.x = cp.x;
351 dim->priv->tolerance_down_shift.y =
352 cp.y + offset.y + midspacing + extents.height;
354 if (extents.width > width)
355 width = extents.width;
358 cp.x += width;
361 /* Compute the note */
362 if (dim->priv->note != NULL) {
363 adg_style_apply(adg_dim_style_get_note_style(dim_style), cr);
365 cpml_pair_copy(&offset, adg_dim_style_get_note_shift(dim_style));
366 cp.x += offset.x;
368 cairo_text_extents(cr, dim->priv->note, &extents);
369 cairo_user_to_device_distance(cr, &extents.width, &extents.height);
370 dim->priv->note_shift.x = cp.x;
371 dim->priv->note_shift.y = cp.y + offset.y + extents.height / 2.;
373 cp.x += extents.width;
376 /* Calculating the offsets */
377 cpml_pair_copy(&offset, adg_dim_style_get_quote_shift(dim_style));
378 offset.x -= cp.x / 2.;
380 cpml_pair_copy(&dim->priv->quote_shift, &offset);
382 dim->priv->tolerance_up_shift.x += offset.x;
383 dim->priv->tolerance_up_shift.y += offset.y;
385 dim->priv->tolerance_down_shift.x += offset.x;
386 dim->priv->tolerance_down_shift.y += offset.y;
388 dim->priv->note_shift.x += offset.x;
389 dim->priv->note_shift.y += offset.y;
393 const AdgPair *
394 adg_dim_get_ref1(AdgDim *dim)
396 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
398 return &dim->priv->ref1;
401 const AdgPair *
402 adg_dim_get_ref2(AdgDim *dim)
404 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
406 return &dim->priv->ref2;
409 void
410 adg_dim_set_ref(AdgDim *dim, const AdgPair *ref1, const AdgPair *ref2)
412 GObject *object;
414 g_return_if_fail(ADG_IS_DIM(dim));
416 object = G_OBJECT(dim);
418 if (ref1 != NULL) {
419 dim->priv->ref1 = *ref1;
420 g_object_notify(object, "ref1");
423 if (ref2 != NULL) {
424 dim->priv->ref2 = *ref2;
425 g_object_notify(object, "ref2");
429 void
430 adg_dim_set_ref_explicit(AdgDim *dim, double ref1_x, double ref1_y,
431 double ref2_x, double ref2_y)
433 AdgPair ref1;
434 AdgPair ref2;
436 ref1.x = ref1_x;
437 ref1.y = ref1_y;
438 ref2.x = ref2_x;
439 ref2.y = ref2_y;
441 adg_dim_set_ref(dim, &ref1, &ref2);
444 const AdgPair *
445 adg_dim_get_pos1(AdgDim *dim)
447 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
449 return &dim->priv->pos1;
452 const AdgPair *
453 adg_dim_get_pos2(AdgDim *dim)
455 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
457 return &dim->priv->pos2;
460 void
461 adg_dim_set_pos(AdgDim *dim, AdgPair *pos1, AdgPair *pos2)
463 GObject *object;
465 g_return_if_fail(ADG_IS_DIM(dim));
467 object = G_OBJECT(dim);
469 g_object_freeze_notify(object);
471 if (pos1 != NULL) {
472 dim->priv->pos1 = *pos1;
473 g_object_notify(object, "pos1");
476 if (pos2 != NULL) {
477 dim->priv->pos2 = *pos2;
478 g_object_notify(object, "pos2");
481 g_object_thaw_notify(object);
484 void
485 adg_dim_set_pos_explicit(AdgDim *dim, double pos1_x, double pos1_y,
486 double pos2_x, double pos2_y)
488 AdgPair pos1;
489 AdgPair pos2;
491 pos1.x = pos1_x;
492 pos1.y = pos1_y;
493 pos2.x = pos2_x;
494 pos2.y = pos2_y;
496 adg_dim_set_pos(dim, &pos1, &pos2);
499 double
500 adg_dim_get_level(AdgDim *dim)
502 g_return_val_if_fail(ADG_IS_DIM(dim), 0.0);
504 return dim->priv->level;
507 void
508 adg_dim_set_level(AdgDim *dim, double level)
510 g_return_if_fail(ADG_IS_DIM(dim));
512 dim->priv->level = level;
513 g_object_notify((GObject *) dim, "level");
516 const gchar *
517 adg_dim_get_quote(AdgDim *dim)
519 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
521 return dim->priv->quote;
524 void
525 adg_dim_set_quote(AdgDim *dim, const gchar *quote)
527 g_return_if_fail(ADG_IS_DIM(dim));
529 g_free(dim->priv->quote);
530 dim->priv->quote = g_strdup(quote);
531 invalidate_quote(dim);
533 g_object_notify((GObject *) dim, "quote");
536 const gchar *
537 adg_dim_get_tolerance_up(AdgDim *dim)
539 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
541 return dim->priv->tolerance_up;
544 void
545 adg_dim_set_tolerance_up(AdgDim *dim, const gchar *tolerance_up)
547 g_return_if_fail(ADG_IS_DIM(dim));
549 g_free(dim->priv->tolerance_up);
550 dim->priv->tolerance_down = g_strdup(tolerance_up);
551 invalidate_quote(dim);
553 g_object_notify((GObject *) dim, "tolerance-up");
556 const gchar *
557 adg_dim_get_tolerance_down(AdgDim *dim)
559 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
561 return dim->priv->tolerance_down;
564 void
565 adg_dim_set_tolerance_down(AdgDim *dim, const gchar *tolerance_down)
567 g_return_if_fail(ADG_IS_DIM(dim));
569 g_free(dim->priv->tolerance_down);
570 dim->priv->tolerance_down = g_strdup(tolerance_down);
571 invalidate_quote(dim);
573 g_object_notify((GObject *) dim, "tolerance-down");
576 void
577 adg_dim_set_tolerances(AdgDim *dim,
578 const gchar *tolerance_up, const gchar *tolerance_down)
580 GObject *object;
582 g_return_if_fail(ADG_IS_DIM(dim));
584 object = G_OBJECT(dim);
586 g_object_freeze_notify(object);
588 g_free(dim->priv->tolerance_up);
589 dim->priv->tolerance_up = g_strdup(tolerance_up);
590 g_object_notify(object, "tolerance-up");
592 g_free(dim->priv->tolerance_down);
593 dim->priv->tolerance_down = g_strdup(tolerance_down);
594 g_object_notify(object, "tolerance-down");
596 invalidate_quote(dim);
598 g_object_thaw_notify(object);
601 const gchar *
602 adg_dim_get_note(AdgDim *dim)
604 g_return_val_if_fail(ADG_IS_DIM(dim), NULL);
606 return dim->priv->note;
609 void
610 adg_dim_set_note(AdgDim *dim, const gchar *note)
612 g_return_if_fail(ADG_IS_DIM(dim));
614 g_free(dim->priv->note);
615 dim->priv->note = g_strdup(note);
616 invalidate_quote(dim);
618 g_object_notify((GObject *) dim, "note");
623 * adg_dim_render_quote:
624 * @dim: an #AdgDim object
625 * @cr: a #cairo_t drawing context
627 * Renders the quote of @dim at the @org position. This function
628 * is only useful in new dimension implementations.
630 void
631 adg_dim_render_quote(AdgDim *dim, cairo_t *cr)
633 AdgDimStyle *dim_style;
634 AdgPair quote_shift;
635 AdgPair tolerance_up_shift;
636 AdgPair tolerance_down_shift;
637 AdgPair note_shift;
639 g_return_if_fail(ADG_IS_DIM(dim));
641 dim_style = (AdgDimStyle *) adg_entity_get_style((AdgEntity *) dim,
642 ADG_SLOT_DIM_STYLE);
644 if (dim->priv->quote == NULL) {
645 dim->priv->quote = ADG_DIM_GET_CLASS(dim)->default_quote(dim);
646 invalidate_quote(dim);
647 g_object_notify((GObject *) dim, "quote");
650 ADG_DIM_GET_CLASS(dim)->quote_layout(dim, cr);
652 cpml_pair_copy(&quote_shift, &dim->priv->quote_shift);
653 cairo_device_to_user_distance(cr, &quote_shift.x, &quote_shift.y);
654 cpml_pair_copy(&tolerance_up_shift, &dim->priv->tolerance_up_shift);
655 cairo_device_to_user_distance(cr, &tolerance_up_shift.x,
656 &tolerance_up_shift.y);
657 cpml_pair_copy(&tolerance_down_shift,
658 &dim->priv->tolerance_down_shift);
659 cairo_device_to_user_distance(cr, &tolerance_down_shift.x,
660 &tolerance_down_shift.y);
662 cairo_save(cr);
663 cairo_translate(cr, dim->priv->quote_org.x, dim->priv->quote_org.y);
664 cairo_rotate(cr, dim->priv->quote_angle);
666 /* Rendering quote */
667 adg_style_apply(adg_dim_style_get_quote_style(dim_style), cr);
668 cairo_move_to(cr, quote_shift.x, quote_shift.y);
669 cairo_show_text(cr, dim->priv->quote);
671 /* Rendering tolerances */
672 if (dim->priv->tolerance_up != NULL
673 || dim->priv->tolerance_down != NULL) {
674 adg_style_apply(adg_dim_style_get_tolerance_style(dim_style), cr);
676 if (dim->priv->tolerance_up != NULL) {
677 cairo_move_to(cr, tolerance_up_shift.x, tolerance_up_shift.y);
678 cairo_show_text(cr, dim->priv->tolerance_up);
680 if (dim->priv->tolerance_down != NULL) {
681 cairo_move_to(cr, tolerance_down_shift.x,
682 tolerance_down_shift.y);
683 cairo_show_text(cr, dim->priv->tolerance_down);
687 /* Rendering note */
688 if (dim->priv->note != NULL) {
689 cairo_move_to(cr, note_shift.x, note_shift.y);
690 cairo_show_text(cr, dim->priv->note);
693 cairo_restore(cr);