1 #define __SP_RULER_C__
3 /*
4 * Customized ruler class for inkscape
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Frank Felfe <innerspace@iname.com>
9 * bulia byak <buliabyak@users.sf.net>
10 *
11 * Copyright (C) 1999-2005 authors
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #include <cstring>
17 #include <cmath>
18 #include <cstdio>
20 #include "widget-sizes.h"
21 #include "desktop-widget.h"
22 #include "ruler.h"
23 #include "unit-constants.h"
24 #include "round.h"
26 #define MINIMUM_INCR 5
27 #define MAXIMUM_SUBDIVIDE 5
28 #define MAXIMUM_SCALES 10
29 #define UNUSED_PIXELS 2 // There appear to be two pixels that are not being used at each end of the ruler
31 static void sp_hruler_class_init (SPHRulerClass *klass);
32 static void sp_hruler_init (SPHRuler *hruler);
33 static gint sp_hruler_motion_notify (GtkWidget *widget, GdkEventMotion *event);
34 static void sp_hruler_draw_ticks (GtkRuler *ruler);
35 static void sp_hruler_draw_pos (GtkRuler *ruler);
36 static void sp_hruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
38 static GtkWidgetClass *hruler_parent_class;
40 GtkType
41 sp_hruler_get_type (void)
42 {
43 static GtkType hruler_type = 0;
45 if (!hruler_type)
46 {
47 static const GtkTypeInfo hruler_info =
48 {
49 "SPHRuler",
50 sizeof (SPHRuler),
51 sizeof (SPHRulerClass),
52 (GtkClassInitFunc) sp_hruler_class_init,
53 (GtkObjectInitFunc) sp_hruler_init,
54 /* reserved_1 */ NULL,
55 /* reserved_2 */ NULL,
56 (GtkClassInitFunc) NULL,
57 };
59 hruler_type = gtk_type_unique (gtk_ruler_get_type (), &hruler_info);
60 }
62 return hruler_type;
63 }
65 static void
66 sp_hruler_class_init (SPHRulerClass *klass)
67 {
68 GtkWidgetClass *widget_class;
69 GtkRulerClass *ruler_class;
71 hruler_parent_class = (GtkWidgetClass *) gtk_type_class (GTK_TYPE_RULER);
73 widget_class = (GtkWidgetClass*) klass;
74 ruler_class = (GtkRulerClass*) klass;
76 widget_class->motion_notify_event = sp_hruler_motion_notify;
77 widget_class->size_allocate = sp_hruler_size_allocate;
79 ruler_class->draw_ticks = sp_hruler_draw_ticks;
80 ruler_class->draw_pos = sp_hruler_draw_pos;
81 }
83 static void
84 sp_hruler_init (SPHRuler *hruler)
85 {
86 GtkWidget *widget;
88 widget = GTK_WIDGET (hruler);
89 widget->requisition.width = widget->style->xthickness * 2 + 1;
90 widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT;
91 }
94 GtkWidget*
95 sp_hruler_new (void)
96 {
97 return GTK_WIDGET (gtk_type_new (sp_hruler_get_type ()));
98 }
100 static gint
101 sp_hruler_motion_notify (GtkWidget *widget,
102 GdkEventMotion *event)
103 {
104 GtkRuler *ruler;
106 g_return_val_if_fail (widget != NULL, FALSE);
107 g_return_val_if_fail (SP_IS_HRULER (widget), FALSE);
108 g_return_val_if_fail (event != NULL, FALSE);
110 ruler = GTK_RULER (widget);
111 double x = event->x; //Although event->x is double according to the docs, it only appears to return integers
112 ruler->position = ruler->lower + (ruler->upper - ruler->lower) * (x + UNUSED_PIXELS) / (widget->allocation.width + 2*UNUSED_PIXELS);
114 /* Make sure the ruler has been allocated already */
115 if (ruler->backing_store != NULL)
116 gtk_ruler_draw_pos (ruler);
118 return FALSE;
119 }
121 static void
122 sp_hruler_draw_ticks (GtkRuler *ruler)
123 {
124 GtkWidget *widget;
125 GdkGC *gc, *bg_gc;
126 PangoFontDescription *pango_desc;
127 PangoContext *pango_context;
128 PangoLayout *pango_layout;
129 gint i, tick_index;
130 gint width, height;
131 gint xthickness;
132 gint ythickness;
133 gint length, ideal_length;
134 double lower, upper; /* Upper and lower limits, in ruler units */
135 double increment; /* Number of pixels per unit */
136 gint scale; /* Number of units per major unit */
137 double subd_incr;
138 double start, end, cur;
139 gchar unit_str[32];
140 gint digit_height;
141 gint text_width;
142 gint pos;
144 g_return_if_fail (ruler != NULL);
145 g_return_if_fail (SP_IS_HRULER (ruler));
147 if (!GTK_WIDGET_DRAWABLE (ruler))
148 return;
150 widget = GTK_WIDGET (ruler);
152 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
153 bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
155 pango_desc = widget->style->font_desc;
157 // Create the pango layout
158 pango_context = gtk_widget_get_pango_context (widget);
160 pango_layout = pango_layout_new (pango_context);
162 PangoFontDescription *fs = pango_font_description_new ();
163 pango_font_description_set_size (fs, RULER_FONT_SIZE);
164 pango_layout_set_font_description (pango_layout, fs);
165 pango_font_description_free (fs);
167 digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
169 xthickness = widget->style->xthickness;
170 ythickness = widget->style->ythickness;
172 width = widget->allocation.width; // in pixels; is apparently 2 pixels shorter than the canvas at each end
173 height = widget->allocation.height;// - ythickness * 2;
175 gtk_paint_box (widget->style, ruler->backing_store,
176 GTK_STATE_NORMAL, GTK_SHADOW_NONE,
177 NULL, widget, "hruler",
178 0, 0,
179 widget->allocation.width, widget->allocation.height);
181 upper = ruler->upper / ruler->metric->pixels_per_unit;
182 lower = ruler->lower / ruler->metric->pixels_per_unit;
184 if ((upper - lower) == 0)
185 return;
186 increment = (double) (width + 2*UNUSED_PIXELS) / (upper - lower);
188 /* determine the scale
189 * We calculate the text size as for the vruler instead of using
190 * text_width = gdk_string_width(font, unit_str), so that the result
191 * for the scale looks consistent with an accompanying vruler
192 */
193 scale = (int)(ceil (ruler->max_size / ruler->metric->pixels_per_unit));
194 sprintf (unit_str, "%d", scale);
195 text_width = strlen (unit_str) * digit_height + 1;
197 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
198 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
199 break;
201 if (scale == MAXIMUM_SCALES)
202 scale = MAXIMUM_SCALES - 1;
204 /* drawing starts here */
205 length = 0;
206 for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
207 {
208 subd_incr = ruler->metric->ruler_scale[scale] /
209 ruler->metric->subdivide[i];
210 if (subd_incr * fabs(increment) <= MINIMUM_INCR)
211 continue;
213 /* Calculate the length of the tickmarks. Make sure that
214 * this length increases for each set of ticks
215 */
216 ideal_length = height / (i + 1) - 1;
217 if (ideal_length > ++length)
218 length = ideal_length;
220 if (lower < upper)
221 {
222 start = floor (lower / subd_incr) * subd_incr;
223 end = ceil (upper / subd_incr) * subd_incr;
224 }
225 else
226 {
227 start = floor (upper / subd_incr) * subd_incr;
228 end = ceil (lower / subd_incr) * subd_incr;
229 }
231 tick_index = 0;
232 cur = start;
234 while (cur <= end)
235 {
236 // due to the typical values for cur, lower and increment, pos will often end up to
237 // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h)
238 // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a
239 // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-12
240 pos = int(Inkscape::round((cur - lower) * increment + 1e-12)) - UNUSED_PIXELS;
242 gdk_draw_line (ruler->backing_store, gc,
243 pos, height + ythickness,
244 pos, height - length + ythickness);
246 /* draw label */
247 double label_spacing_px = (increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i];
248 if (i == 0 &&
249 (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) &&
250 (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0))
251 {
252 if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
253 sprintf (unit_str, "%dk", ((int) cur)/1000);
254 else
255 sprintf (unit_str, "%d", (int) cur);
257 pango_layout_set_text (pango_layout, unit_str, -1);
259 gdk_draw_layout (ruler->backing_store, gc,
260 pos + 2, 0, pango_layout);
261 }
263 /* Calculate cur from start rather than incrementing by subd_incr
264 * in each iteration. This is to avoid propagation of floating point
265 * errors in subd_incr.
266 */
267 ++tick_index;
268 cur = start + tick_index * subd_incr;
269 }
270 }
271 }
273 static void
274 sp_hruler_draw_pos (GtkRuler *ruler)
275 {
276 GtkWidget *widget;
277 GdkGC *gc;
278 int i;
279 gint x, y;
280 gint width, height;
281 gint bs_width, bs_height;
282 gint xthickness;
283 gint ythickness;
284 gfloat increment;
286 g_return_if_fail (ruler != NULL);
287 g_return_if_fail (SP_IS_HRULER (ruler));
289 if (GTK_WIDGET_DRAWABLE (ruler))
290 {
291 widget = GTK_WIDGET (ruler);
293 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
294 xthickness = widget->style->xthickness;
295 ythickness = widget->style->ythickness;
296 width = widget->allocation.width; // in pixels; is apparently 2 pixels shorter than the canvas at each end
297 height = widget->allocation.height - ythickness * 2;
299 bs_width = height / 2;
300 bs_width |= 1; /* make sure it's odd */
301 bs_height = bs_width / 2 + 1;
303 if ((bs_width > 0) && (bs_height > 0))
304 {
305 /* If a backing store exists, restore the ruler */
306 if (ruler->backing_store && ruler->non_gr_exp_gc)
307 gdk_draw_pixmap (ruler->widget.window,
308 ruler->non_gr_exp_gc,
309 ruler->backing_store,
310 ruler->xsrc, ruler->ysrc,
311 ruler->xsrc, ruler->ysrc,
312 bs_width, bs_height);
314 increment = (gfloat) (width + 2*UNUSED_PIXELS) / (ruler->upper - ruler->lower);
316 // Calculate the coordinates (x, y, in pixels) of the tip of the triangle
317 x = int(Inkscape::round((ruler->position - ruler->lower) * increment + double(xthickness - bs_width) / 2.0) - UNUSED_PIXELS);
318 y = (height + bs_height) / 2 + ythickness;
320 for (i = 0; i < bs_height; i++)
321 gdk_draw_line (widget->window, gc,
322 x + i, y + i,
323 x + bs_width - 1 - i, y + i);
326 ruler->xsrc = x;
327 ruler->ysrc = y;
328 }
329 }
330 }
332 /**
333 * The hruler widget's size_allocate callback.
334 */
335 static void
336 sp_hruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
337 {
338 g_assert (widget != NULL);
339 g_assert (SP_IS_HRULER (widget));
341 // First call the default gtk_widget_size_allocate() method (which is being overridden here)
342 if (GTK_WIDGET_CLASS (hruler_parent_class)->size_allocate)
343 (* GTK_WIDGET_CLASS (hruler_parent_class)->size_allocate) (widget, allocation);
345 // Now the size of the ruler has changed, the ruler bounds (upper & lower) need to be updated
346 // For this we first need to obtain a pointer to the desktop, by walking up the tree of ancestors
347 GtkWidget *parent = gtk_widget_get_parent(widget);
348 do {
349 if (SP_IS_DESKTOP_WIDGET(parent)) {
350 // Now we've found the desktop widget we can have the ruler boundaries updated
351 sp_desktop_widget_update_hruler(SP_DESKTOP_WIDGET(parent));
352 // If the size of the ruler has increased, then a blank part is uncovered; therefore
353 // it must be redrawn
354 sp_hruler_draw_ticks(GTK_RULER(widget));
355 break;
356 }
357 parent = gtk_widget_get_parent(parent);
358 } while (parent != NULL);
359 }
364 // vruler
366 static void sp_vruler_class_init (SPVRulerClass *klass);
367 static void sp_vruler_init (SPVRuler *vruler);
368 static gint sp_vruler_motion_notify (GtkWidget *widget,
369 GdkEventMotion *event);
370 static void sp_vruler_draw_ticks (GtkRuler *ruler);
371 static void sp_vruler_draw_pos (GtkRuler *ruler);
372 static void sp_vruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
374 static GtkWidgetClass *vruler_parent_class;
376 GtkType
377 sp_vruler_get_type (void)
378 {
379 static GtkType vruler_type = 0;
381 if (!vruler_type)
382 {
383 static const GtkTypeInfo vruler_info =
384 {
385 "SPVRuler",
386 sizeof (SPVRuler),
387 sizeof (SPVRulerClass),
388 (GtkClassInitFunc) sp_vruler_class_init,
389 (GtkObjectInitFunc) sp_vruler_init,
390 /* reserved_1 */ NULL,
391 /* reserved_2 */ NULL,
392 (GtkClassInitFunc) NULL,
393 };
395 vruler_type = gtk_type_unique (gtk_ruler_get_type (), &vruler_info);
396 }
398 return vruler_type;
399 }
401 static void
402 sp_vruler_class_init (SPVRulerClass *klass)
403 {
404 GtkWidgetClass *widget_class;
405 GtkRulerClass *ruler_class;
407 vruler_parent_class = (GtkWidgetClass *) gtk_type_class (GTK_TYPE_RULER);
409 widget_class = (GtkWidgetClass*) klass;
410 ruler_class = (GtkRulerClass*) klass;
412 widget_class->motion_notify_event = sp_vruler_motion_notify;
413 widget_class->size_allocate = sp_vruler_size_allocate;
415 ruler_class->draw_ticks = sp_vruler_draw_ticks;
416 ruler_class->draw_pos = sp_vruler_draw_pos;
417 }
419 static void
420 sp_vruler_init (SPVRuler *vruler)
421 {
422 GtkWidget *widget;
424 widget = GTK_WIDGET (vruler);
425 widget->requisition.width = widget->style->xthickness * 2 + RULER_WIDTH;
426 widget->requisition.height = widget->style->ythickness * 2 + 1;
427 }
429 GtkWidget*
430 sp_vruler_new (void)
431 {
432 return GTK_WIDGET (gtk_type_new (sp_vruler_get_type ()));
433 }
436 static gint
437 sp_vruler_motion_notify (GtkWidget *widget,
438 GdkEventMotion *event)
439 {
440 GtkRuler *ruler;
442 g_return_val_if_fail (widget != NULL, FALSE);
443 g_return_val_if_fail (SP_IS_VRULER (widget), FALSE);
444 g_return_val_if_fail (event != NULL, FALSE);
446 ruler = GTK_RULER (widget);
447 double y = event->y; //Although event->y is double according to the docs, it only appears to return integers
448 ruler->position = ruler->lower + (ruler->upper - ruler->lower) * (y + UNUSED_PIXELS) / (widget->allocation.height + 2*UNUSED_PIXELS);
450 /* Make sure the ruler has been allocated already */
451 if (ruler->backing_store != NULL)
452 gtk_ruler_draw_pos (ruler);
454 return FALSE;
455 }
457 static void
458 sp_vruler_draw_ticks (GtkRuler *ruler)
459 {
460 GtkWidget *widget;
461 GdkGC *gc, *bg_gc;
462 PangoFontDescription *pango_desc;
463 PangoContext *pango_context;
464 PangoLayout *pango_layout;
465 gint i, j, tick_index;
466 gint width, height;
467 gint xthickness;
468 gint ythickness;
469 gint length, ideal_length;
470 double lower, upper; /* Upper and lower limits, in ruler units */
471 double increment; /* Number of pixels per unit */
472 gint scale; /* Number of units per major unit */
473 double subd_incr;
474 double start, end, cur;
475 gchar unit_str[32];
476 gchar digit_str[2] = { '\0', '\0' };
477 gint digit_height;
478 gint text_height;
479 gint pos;
481 g_return_if_fail (ruler != NULL);
482 g_return_if_fail (SP_IS_VRULER (ruler));
484 if (!GTK_WIDGET_DRAWABLE (ruler))
485 return;
487 widget = GTK_WIDGET (ruler);
489 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
490 bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
492 pango_desc = widget->style->font_desc;
494 // Create the pango layout
495 pango_context = gtk_widget_get_pango_context (widget);
497 pango_layout = pango_layout_new (pango_context);
499 PangoFontDescription *fs = pango_font_description_new ();
500 pango_font_description_set_size (fs, RULER_FONT_SIZE);
501 pango_layout_set_font_description (pango_layout, fs);
502 pango_font_description_free (fs);
504 digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
506 xthickness = widget->style->xthickness;
507 ythickness = widget->style->ythickness;
509 width = widget->allocation.height; //in pixels; is apparently 2 pixels shorter than the canvas at each end
510 height = widget->allocation.width;// - ythickness * 2;
512 gtk_paint_box (widget->style, ruler->backing_store,
513 GTK_STATE_NORMAL, GTK_SHADOW_NONE,
514 NULL, widget, "vruler",
515 0, 0,
516 widget->allocation.width, widget->allocation.height);
518 upper = ruler->upper / ruler->metric->pixels_per_unit;
519 lower = ruler->lower / ruler->metric->pixels_per_unit;
521 if ((upper - lower) == 0)
522 return;
523 increment = (double) (width + 2*UNUSED_PIXELS) / (upper - lower);
525 /* determine the scale
526 * use the maximum extents of the ruler to determine the largest
527 * possible number to be displayed. Calculate the height in pixels
528 * of this displayed text. Use this height to find a scale which
529 * leaves sufficient room for drawing the ruler.
530 */
531 scale = (int)ceil (ruler->max_size / ruler->metric->pixels_per_unit);
532 sprintf (unit_str, "%d", scale);
533 text_height = strlen (unit_str) * digit_height + 1;
535 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
536 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
537 break;
539 if (scale == MAXIMUM_SCALES)
540 scale = MAXIMUM_SCALES - 1;
542 /* drawing starts here */
543 length = 0;
544 for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--) {
545 subd_incr = (double) ruler->metric->ruler_scale[scale] /
546 (double) ruler->metric->subdivide[i];
547 if (subd_incr * fabs(increment) <= MINIMUM_INCR)
548 continue;
550 /* Calculate the length of the tickmarks. Make sure that
551 * this length increases for each set of ticks
552 */
553 ideal_length = height / (i + 1) - 1;
554 if (ideal_length > ++length)
555 length = ideal_length;
557 if (lower < upper)
558 {
559 start = floor (lower / subd_incr) * subd_incr;
560 end = ceil (upper / subd_incr) * subd_incr;
561 }
562 else
563 {
564 start = floor (upper / subd_incr) * subd_incr;
565 end = ceil (lower / subd_incr) * subd_incr;
566 }
568 tick_index = 0;
569 cur = start;
571 while (cur < end) {
572 // due to the typical values for cur, lower and increment, pos will often end up to
573 // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h)
574 // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a
575 // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-12
576 pos = int(Inkscape::round((cur - lower) * increment + 1e-12)) - UNUSED_PIXELS;
578 gdk_draw_line (ruler->backing_store, gc,
579 height + xthickness - length, pos,
580 height + xthickness, pos);
582 /* draw label */
583 double label_spacing_px = fabs((increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i]);
584 if (i == 0 &&
585 (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) &&
586 (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0))
587 {
588 if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
589 sprintf (unit_str, "%dk", ((int) cur)/1000);
590 else
591 sprintf (unit_str, "%d", (int) cur);
592 for (j = 0; j < (int) strlen (unit_str); j++)
593 {
594 digit_str[0] = unit_str[j];
596 pango_layout_set_text (pango_layout, digit_str, 1);
598 gdk_draw_layout (ruler->backing_store, gc,
599 xthickness + 1,
600 pos + digit_height * (j) + 1,
601 pango_layout);
602 }
603 }
605 /* Calculate cur from start rather than incrementing by subd_incr
606 * in each iteration. This is to avoid propagation of floating point
607 * errors in subd_incr.
608 */
609 ++tick_index;
610 cur = start + tick_index * subd_incr;
611 }
612 }
613 }
615 static void
616 sp_vruler_draw_pos (GtkRuler *ruler)
617 {
618 GtkWidget *widget;
619 GdkGC *gc;
620 int i;
621 gint x, y;
622 gint width, height;
623 gint bs_width, bs_height;
624 gint xthickness;
625 gint ythickness;
626 gfloat increment;
628 g_return_if_fail (ruler != NULL);
629 g_return_if_fail (SP_IS_VRULER (ruler));
631 if (GTK_WIDGET_DRAWABLE (ruler))
632 {
633 widget = GTK_WIDGET (ruler);
635 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
636 xthickness = widget->style->xthickness;
637 ythickness = widget->style->ythickness;
638 width = widget->allocation.width - xthickness * 2;
639 height = widget->allocation.height; // in pixels; is apparently 2 pixels shorter than the canvas at each end
641 bs_height = width / 2;
642 bs_height |= 1; /* make sure it's odd */
643 bs_width = bs_height / 2 + 1;
645 if ((bs_width > 0) && (bs_height > 0))
646 {
647 /* If a backing store exists, restore the ruler */
648 if (ruler->backing_store && ruler->non_gr_exp_gc)
649 gdk_draw_pixmap (ruler->widget.window,
650 ruler->non_gr_exp_gc,
651 ruler->backing_store,
652 ruler->xsrc, ruler->ysrc,
653 ruler->xsrc, ruler->ysrc,
654 bs_width, bs_height);
656 increment = (gfloat) (height + 2*UNUSED_PIXELS) / (ruler->upper - ruler->lower);
658 // Calculate the coordinates (x, y, in pixels) of the tip of the triangle
659 x = (width + bs_width) / 2 + xthickness;
660 y = int(Inkscape::round((ruler->position - ruler->lower) * increment + double(ythickness - bs_height) / 2.0) - UNUSED_PIXELS);
662 for (i = 0; i < bs_width; i++)
663 gdk_draw_line (widget->window, gc,
664 x + i, y + i,
665 x + i, y + bs_height - 1 - i);
667 ruler->xsrc = x;
668 ruler->ysrc = y;
669 }
670 }
671 }
673 /**
674 * The vruler widget's size_allocate callback.
675 */
676 static void
677 sp_vruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
678 {
679 g_assert (widget != NULL);
680 g_assert (SP_IS_VRULER (widget));
682 // First call the default gtk_widget_size_allocate() method (which is being overridden here)
683 if (GTK_WIDGET_CLASS (vruler_parent_class)->size_allocate)
684 (* GTK_WIDGET_CLASS (vruler_parent_class)->size_allocate) (widget, allocation);
686 // Now the size of the ruler has changed, the ruler bounds (upper & lower) need to be updated
687 // For this we first need to obtain a pointer to the desktop, by walking up the tree of ancestors
688 GtkWidget *parent = gtk_widget_get_parent(widget);
689 do {
690 if (SP_IS_DESKTOP_WIDGET(parent)) {
691 // Now we've found the desktop widget we can have the ruler boundaries updated
692 sp_desktop_widget_update_vruler(SP_DESKTOP_WIDGET(parent));
693 // If the size of the ruler has increased, then a blank part is uncovered; therefore
694 // it must be redrawn
695 sp_vruler_draw_ticks(GTK_RULER(widget));
696 break;
697 }
698 parent = gtk_widget_get_parent(parent);
699 } while (parent != NULL);
700 }
703 /// Ruler metrics.
704 static GtkRulerMetric const sp_ruler_metrics[] = {
705 // NOTE: the order of records in this struct must correspond to the SPMetric enum.
706 {"NONE", "", 1, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
707 {"millimeters", "mm", PX_PER_MM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
708 {"centimeters", "cm", PX_PER_CM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
709 {"inches", "in", PX_PER_IN, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
710 {"points", "pt", PX_PER_PT, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
711 {"pixels", "px", PX_PER_PX, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
712 {"meters", "m", PX_PER_M, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
713 };
715 void
716 sp_ruler_set_metric (GtkRuler *ruler,
717 SPMetric metric)
718 {
719 g_return_if_fail (ruler != NULL);
720 g_return_if_fail (GTK_IS_RULER (ruler));
721 g_return_if_fail((unsigned) metric < G_N_ELEMENTS(sp_ruler_metrics));
723 if (metric == 0)
724 return;
726 ruler->metric = const_cast<GtkRulerMetric *>(&sp_ruler_metrics[metric]);
728 if (GTK_WIDGET_DRAWABLE (ruler))
729 gtk_widget_queue_draw (GTK_WIDGET (ruler));
730 }