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 * Diederik van Lierop <mail@diedenrezi.nl>
11 *
12 * Copyright (C) 1999-2008 authors
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #include <cstring>
18 #include <cmath>
19 #include <cstdio>
21 #include "widget-sizes.h"
22 #include "desktop-widget.h"
23 #include "ruler.h"
24 #include "unit-constants.h"
25 #include "round.h"
27 #define MINIMUM_INCR 5
28 #define MAXIMUM_SUBDIVIDE 5
29 #define MAXIMUM_SCALES 10
30 #define UNUSED_PIXELS 2 // There appear to be two pixels that are not being used at each end of the ruler
32 static void sp_hruler_class_init (SPHRulerClass *klass);
33 static void sp_hruler_init (SPHRuler *hruler);
34 static gint sp_hruler_motion_notify (GtkWidget *widget, GdkEventMotion *event);
35 static void sp_hruler_draw_ticks (GtkRuler *ruler);
36 static void sp_hruler_draw_pos (GtkRuler *ruler);
37 static void sp_hruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
39 static GtkWidgetClass *hruler_parent_class;
41 GtkType
42 sp_hruler_get_type (void)
43 {
44 static GtkType hruler_type = 0;
46 if (!hruler_type)
47 {
48 static const GtkTypeInfo hruler_info =
49 {
50 "SPHRuler",
51 sizeof (SPHRuler),
52 sizeof (SPHRulerClass),
53 (GtkClassInitFunc) sp_hruler_class_init,
54 (GtkObjectInitFunc) sp_hruler_init,
55 /* reserved_1 */ NULL,
56 /* reserved_2 */ NULL,
57 (GtkClassInitFunc) NULL,
58 };
60 hruler_type = gtk_type_unique (gtk_ruler_get_type (), &hruler_info);
61 }
63 return hruler_type;
64 }
66 static void
67 sp_hruler_class_init (SPHRulerClass *klass)
68 {
69 GtkWidgetClass *widget_class;
70 GtkRulerClass *ruler_class;
72 hruler_parent_class = (GtkWidgetClass *) gtk_type_class (GTK_TYPE_RULER);
74 widget_class = (GtkWidgetClass*) klass;
75 ruler_class = (GtkRulerClass*) klass;
77 widget_class->motion_notify_event = sp_hruler_motion_notify;
78 widget_class->size_allocate = sp_hruler_size_allocate;
80 ruler_class->draw_ticks = sp_hruler_draw_ticks;
81 ruler_class->draw_pos = sp_hruler_draw_pos;
82 }
84 static void
85 sp_hruler_init (SPHRuler *hruler)
86 {
87 GtkWidget *widget;
89 widget = GTK_WIDGET (hruler);
90 widget->requisition.width = widget->style->xthickness * 2 + 1;
91 widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT;
92 }
95 GtkWidget*
96 sp_hruler_new (void)
97 {
98 return GTK_WIDGET (gtk_type_new (sp_hruler_get_type ()));
99 }
101 static gint
102 sp_hruler_motion_notify (GtkWidget *widget,
103 GdkEventMotion *event)
104 {
105 GtkRuler *ruler;
107 g_return_val_if_fail (widget != NULL, FALSE);
108 g_return_val_if_fail (SP_IS_HRULER (widget), FALSE);
109 g_return_val_if_fail (event != NULL, FALSE);
111 ruler = GTK_RULER (widget);
112 double x = event->x; //Although event->x is double according to the docs, it only appears to return integers
113 ruler->position = ruler->lower + (ruler->upper - ruler->lower) * (x + UNUSED_PIXELS) / (widget->allocation.width + 2*UNUSED_PIXELS);
115 /* Make sure the ruler has been allocated already */
116 if (ruler->backing_store != NULL)
117 gtk_ruler_draw_pos (ruler);
119 return FALSE;
120 }
122 static void
123 sp_hruler_draw_ticks (GtkRuler *ruler)
124 {
125 GtkWidget *widget;
126 GdkGC *gc, *bg_gc;
127 PangoFontDescription *pango_desc;
128 PangoContext *pango_context;
129 PangoLayout *pango_layout;
130 gint i, tick_index;
131 gint width, height;
132 gint xthickness;
133 gint ythickness;
134 gint length, ideal_length;
135 double lower, upper; /* Upper and lower limits, in ruler units */
136 double increment; /* Number of pixels per unit */
137 gint scale; /* Number of units per major unit */
138 double subd_incr;
139 double start, end, cur;
140 gchar unit_str[32];
141 gint digit_height;
142 gint text_width;
143 gint pos;
145 g_return_if_fail (ruler != NULL);
146 g_return_if_fail (SP_IS_HRULER (ruler));
148 if (!GTK_WIDGET_DRAWABLE (ruler))
149 return;
151 widget = GTK_WIDGET (ruler);
153 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
154 bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
156 pango_desc = widget->style->font_desc;
158 // Create the pango layout
159 pango_context = gtk_widget_get_pango_context (widget);
161 pango_layout = pango_layout_new (pango_context);
163 PangoFontDescription *fs = pango_font_description_new ();
164 pango_font_description_set_size (fs, RULER_FONT_SIZE);
165 pango_layout_set_font_description (pango_layout, fs);
166 pango_font_description_free (fs);
168 digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
170 xthickness = widget->style->xthickness;
171 ythickness = widget->style->ythickness;
173 width = widget->allocation.width; // in pixels; is apparently 2 pixels shorter than the canvas at each end
174 height = widget->allocation.height;// - ythickness * 2;
176 gtk_paint_box (widget->style, ruler->backing_store,
177 GTK_STATE_NORMAL, GTK_SHADOW_NONE,
178 NULL, widget, "hruler",
179 0, 0,
180 widget->allocation.width, widget->allocation.height);
182 upper = ruler->upper / ruler->metric->pixels_per_unit; // upper and lower are expressed in ruler units
183 lower = ruler->lower / ruler->metric->pixels_per_unit;
184 /* "pixels_per_unit" should be "points_per_unit". This is the size of the unit
185 * in 1/72nd's of an inch and has nothing to do with screen pixels */
187 if ((upper - lower) == 0)
188 return;
189 increment = (double) (width + 2*UNUSED_PIXELS) / (upper - lower); // screen pixels per ruler unit
191 /* determine the scale
192 * We calculate the text size as for the vruler instead of using
193 * text_width = gdk_string_width(font, unit_str), so that the result
194 * for the scale looks consistent with an accompanying vruler
195 */
196 scale = (int)(ceil (ruler->max_size / ruler->metric->pixels_per_unit));
197 sprintf (unit_str, "%d", scale);
198 text_width = strlen (unit_str) * digit_height + 1;
200 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
201 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
202 break;
204 if (scale == MAXIMUM_SCALES)
205 scale = MAXIMUM_SCALES - 1;
207 /* drawing starts here */
208 length = 0;
209 for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
210 {
211 subd_incr = ruler->metric->ruler_scale[scale] /
212 ruler->metric->subdivide[i];
213 if (subd_incr * fabs(increment) <= MINIMUM_INCR)
214 continue;
216 /* Calculate the length of the tickmarks. Make sure that
217 * this length increases for each set of ticks
218 */
219 ideal_length = height / (i + 1) - 1;
220 if (ideal_length > ++length)
221 length = ideal_length;
223 if (lower < upper)
224 {
225 start = floor (lower / subd_incr) * subd_incr;
226 end = ceil (upper / subd_incr) * subd_incr;
227 }
228 else
229 {
230 start = floor (upper / subd_incr) * subd_incr;
231 end = ceil (lower / subd_incr) * subd_incr;
232 }
234 tick_index = 0;
235 cur = start; // location (in ruler units) of the first invisible tick at the left side of the canvas
237 while (cur <= end)
238 {
239 // due to the typical values for cur, lower and increment, pos will often end up to
240 // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h)
241 // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a
242 // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-9 (that's only one nanopixel ;-))
243 pos = int(Inkscape::round((cur - lower) * increment + 1e-12)) - UNUSED_PIXELS;
244 gdk_draw_line (ruler->backing_store, gc,
245 pos, height + ythickness,
246 pos, height - length + ythickness);
248 /* draw label */
249 double label_spacing_px = (increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i];
250 if (i == 0 &&
251 (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) &&
252 (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0))
253 {
254 if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
255 sprintf (unit_str, "%dk", ((int) cur)/1000);
256 else
257 sprintf (unit_str, "%d", (int) cur);
259 pango_layout_set_text (pango_layout, unit_str, -1);
261 gdk_draw_layout (ruler->backing_store, gc,
262 pos + 2, 0, pango_layout);
263 }
265 /* Calculate cur from start rather than incrementing by subd_incr
266 * in each iteration. This is to avoid propagation of floating point
267 * errors in subd_incr.
268 */
269 ++tick_index;
270 cur = start + tick_index * subd_incr;
271 }
272 }
273 }
275 static void
276 sp_hruler_draw_pos (GtkRuler *ruler)
277 {
278 GtkWidget *widget;
279 GdkGC *gc;
280 int i;
281 gint x, y;
282 gint width, height;
283 gint bs_width, bs_height;
284 gint xthickness;
285 gint ythickness;
286 gfloat increment;
288 g_return_if_fail (ruler != NULL);
289 g_return_if_fail (SP_IS_HRULER (ruler));
291 if (GTK_WIDGET_DRAWABLE (ruler))
292 {
293 widget = GTK_WIDGET (ruler);
295 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
296 xthickness = widget->style->xthickness;
297 ythickness = widget->style->ythickness;
298 width = widget->allocation.width; // in pixels; is apparently 2 pixels shorter than the canvas at each end
299 height = widget->allocation.height - ythickness * 2;
301 bs_width = height / 2;
302 bs_width |= 1; /* make sure it's odd */
303 bs_height = bs_width / 2 + 1;
305 if ((bs_width > 0) && (bs_height > 0))
306 {
307 /* If a backing store exists, restore the ruler */
308 if (ruler->backing_store && ruler->non_gr_exp_gc)
309 gdk_draw_pixmap (ruler->widget.window,
310 ruler->non_gr_exp_gc,
311 ruler->backing_store,
312 ruler->xsrc, ruler->ysrc,
313 ruler->xsrc, ruler->ysrc,
314 bs_width, bs_height);
316 increment = (gfloat) (width + 2*UNUSED_PIXELS) / (ruler->upper - ruler->lower);
318 // Calculate the coordinates (x, y, in pixels) of the tip of the triangle
319 x = int(Inkscape::round((ruler->position - ruler->lower) * increment + double(xthickness - bs_width) / 2.0) - UNUSED_PIXELS);
320 y = (height + bs_height) / 2 + ythickness;
322 for (i = 0; i < bs_height; i++)
323 gdk_draw_line (widget->window, gc,
324 x + i, y + i,
325 x + bs_width - 1 - i, y + i);
328 ruler->xsrc = x;
329 ruler->ysrc = y;
330 }
331 }
332 }
334 /**
335 * The hruler widget's size_allocate callback.
336 */
337 static void
338 sp_hruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
339 {
340 g_assert (widget != NULL);
341 g_assert (SP_IS_HRULER (widget));
343 // First call the default gtk_widget_size_allocate() method (which is being overridden here)
344 if (GTK_WIDGET_CLASS (hruler_parent_class)->size_allocate)
345 (* GTK_WIDGET_CLASS (hruler_parent_class)->size_allocate) (widget, allocation);
347 // Now the size of the ruler has changed, the ruler bounds (upper & lower) need to be updated
348 // For this we first need to obtain a pointer to the desktop, by walking up the tree of ancestors
349 GtkWidget *parent = gtk_widget_get_parent(widget);
350 do {
351 if (SP_IS_DESKTOP_WIDGET(parent)) {
352 // Now we've found the desktop widget we can have the ruler boundaries updated
353 sp_desktop_widget_update_hruler(SP_DESKTOP_WIDGET(parent));
354 // If the size of the ruler has increased, then a blank part is uncovered; therefore
355 // it must be redrawn
356 sp_hruler_draw_ticks(GTK_RULER(widget));
357 break;
358 }
359 parent = gtk_widget_get_parent(parent);
360 } while (parent != NULL);
361 }
366 // vruler
368 static void sp_vruler_class_init (SPVRulerClass *klass);
369 static void sp_vruler_init (SPVRuler *vruler);
370 static gint sp_vruler_motion_notify (GtkWidget *widget,
371 GdkEventMotion *event);
372 static void sp_vruler_draw_ticks (GtkRuler *ruler);
373 static void sp_vruler_draw_pos (GtkRuler *ruler);
374 static void sp_vruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
376 static GtkWidgetClass *vruler_parent_class;
378 GtkType
379 sp_vruler_get_type (void)
380 {
381 static GtkType vruler_type = 0;
383 if (!vruler_type)
384 {
385 static const GtkTypeInfo vruler_info =
386 {
387 "SPVRuler",
388 sizeof (SPVRuler),
389 sizeof (SPVRulerClass),
390 (GtkClassInitFunc) sp_vruler_class_init,
391 (GtkObjectInitFunc) sp_vruler_init,
392 /* reserved_1 */ NULL,
393 /* reserved_2 */ NULL,
394 (GtkClassInitFunc) NULL,
395 };
397 vruler_type = gtk_type_unique (gtk_ruler_get_type (), &vruler_info);
398 }
400 return vruler_type;
401 }
403 static void
404 sp_vruler_class_init (SPVRulerClass *klass)
405 {
406 GtkWidgetClass *widget_class;
407 GtkRulerClass *ruler_class;
409 vruler_parent_class = (GtkWidgetClass *) gtk_type_class (GTK_TYPE_RULER);
411 widget_class = (GtkWidgetClass*) klass;
412 ruler_class = (GtkRulerClass*) klass;
414 widget_class->motion_notify_event = sp_vruler_motion_notify;
415 widget_class->size_allocate = sp_vruler_size_allocate;
417 ruler_class->draw_ticks = sp_vruler_draw_ticks;
418 ruler_class->draw_pos = sp_vruler_draw_pos;
419 }
421 static void
422 sp_vruler_init (SPVRuler *vruler)
423 {
424 GtkWidget *widget;
426 widget = GTK_WIDGET (vruler);
427 widget->requisition.width = widget->style->xthickness * 2 + RULER_WIDTH;
428 widget->requisition.height = widget->style->ythickness * 2 + 1;
429 }
431 GtkWidget*
432 sp_vruler_new (void)
433 {
434 return GTK_WIDGET (gtk_type_new (sp_vruler_get_type ()));
435 }
438 static gint
439 sp_vruler_motion_notify (GtkWidget *widget,
440 GdkEventMotion *event)
441 {
442 GtkRuler *ruler;
444 g_return_val_if_fail (widget != NULL, FALSE);
445 g_return_val_if_fail (SP_IS_VRULER (widget), FALSE);
446 g_return_val_if_fail (event != NULL, FALSE);
448 ruler = GTK_RULER (widget);
449 double y = event->y; //Although event->y is double according to the docs, it only appears to return integers
450 ruler->position = ruler->lower + (ruler->upper - ruler->lower) * (y + UNUSED_PIXELS) / (widget->allocation.height + 2*UNUSED_PIXELS);
452 /* Make sure the ruler has been allocated already */
453 if (ruler->backing_store != NULL)
454 gtk_ruler_draw_pos (ruler);
456 return FALSE;
457 }
459 static void
460 sp_vruler_draw_ticks (GtkRuler *ruler)
461 {
462 GtkWidget *widget;
463 GdkGC *gc, *bg_gc;
464 PangoFontDescription *pango_desc;
465 PangoContext *pango_context;
466 PangoLayout *pango_layout;
467 gint i, j, tick_index;
468 gint width, height;
469 gint xthickness;
470 gint ythickness;
471 gint length, ideal_length;
472 double lower, upper; /* Upper and lower limits, in ruler units */
473 double increment; /* Number of pixels per unit */
474 gint scale; /* Number of units per major unit */
475 double subd_incr;
476 double start, end, cur;
477 gchar unit_str[32];
478 gchar digit_str[2] = { '\0', '\0' };
479 gint digit_height;
480 gint text_height;
481 gint pos;
483 g_return_if_fail (ruler != NULL);
484 g_return_if_fail (SP_IS_VRULER (ruler));
486 if (!GTK_WIDGET_DRAWABLE (ruler))
487 return;
489 widget = GTK_WIDGET (ruler);
491 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
492 bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
494 pango_desc = widget->style->font_desc;
496 // Create the pango layout
497 pango_context = gtk_widget_get_pango_context (widget);
499 pango_layout = pango_layout_new (pango_context);
501 PangoFontDescription *fs = pango_font_description_new ();
502 pango_font_description_set_size (fs, RULER_FONT_SIZE);
503 pango_layout_set_font_description (pango_layout, fs);
504 pango_font_description_free (fs);
506 digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
508 xthickness = widget->style->xthickness;
509 ythickness = widget->style->ythickness;
511 width = widget->allocation.height; //in pixels; is apparently 2 pixels shorter than the canvas at each end
512 height = widget->allocation.width;// - ythickness * 2;
514 gtk_paint_box (widget->style, ruler->backing_store,
515 GTK_STATE_NORMAL, GTK_SHADOW_NONE,
516 NULL, widget, "vruler",
517 0, 0,
518 widget->allocation.width, widget->allocation.height);
520 upper = ruler->upper / ruler->metric->pixels_per_unit; // upper and lower are expressed in ruler units
521 lower = ruler->lower / ruler->metric->pixels_per_unit;
522 /* "pixels_per_unit" should be "points_per_unit". This is the size of the unit
523 * in 1/72nd's of an inch and has nothing to do with screen pixels */
525 if ((upper - lower) == 0)
526 return;
527 increment = (double) (width + 2*UNUSED_PIXELS) / (upper - lower); // screen pixels per ruler unit
529 /* determine the scale
530 * use the maximum extents of the ruler to determine the largest
531 * possible number to be displayed. Calculate the height in pixels
532 * of this displayed text. Use this height to find a scale which
533 * leaves sufficient room for drawing the ruler.
534 */
535 scale = (int)ceil (ruler->max_size / ruler->metric->pixels_per_unit);
536 sprintf (unit_str, "%d", scale);
537 text_height = strlen (unit_str) * digit_height + 1;
539 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
540 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
541 break;
543 if (scale == MAXIMUM_SCALES)
544 scale = MAXIMUM_SCALES - 1;
546 /* drawing starts here */
547 length = 0;
548 for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--) {
549 subd_incr = (double) ruler->metric->ruler_scale[scale] /
550 (double) ruler->metric->subdivide[i];
551 if (subd_incr * fabs(increment) <= MINIMUM_INCR)
552 continue;
554 /* Calculate the length of the tickmarks. Make sure that
555 * this length increases for each set of ticks
556 */
557 ideal_length = height / (i + 1) - 1;
558 if (ideal_length > ++length)
559 length = ideal_length;
561 if (lower < upper)
562 {
563 start = floor (lower / subd_incr) * subd_incr;
564 end = ceil (upper / subd_incr) * subd_incr;
565 }
566 else
567 {
568 start = floor (upper / subd_incr) * subd_incr;
569 end = ceil (lower / subd_incr) * subd_incr;
570 }
572 tick_index = 0;
573 cur = start; // location (in ruler units) of the first invisible tick at the top side of the canvas
575 while (cur < end) {
576 // due to the typical values for cur, lower and increment, pos will often end up to
577 // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h)
578 // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a
579 // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-9 (that's only one nanopixel ;-))
580 pos = int(Inkscape::round((cur - lower) * increment + 1e-12)) - UNUSED_PIXELS;
582 gdk_draw_line (ruler->backing_store, gc,
583 height + xthickness - length, pos,
584 height + xthickness, pos);
586 /* draw label */
587 double label_spacing_px = fabs((increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i]);
588 if (i == 0 &&
589 (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) &&
590 (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0))
591 {
592 if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
593 sprintf (unit_str, "%dk", ((int) cur)/1000);
594 else
595 sprintf (unit_str, "%d", (int) cur);
596 for (j = 0; j < (int) strlen (unit_str); j++)
597 {
598 digit_str[0] = unit_str[j];
600 pango_layout_set_text (pango_layout, digit_str, 1);
602 gdk_draw_layout (ruler->backing_store, gc,
603 xthickness + 1,
604 pos + digit_height * (j) + 1,
605 pango_layout);
606 }
607 }
609 /* Calculate cur from start rather than incrementing by subd_incr
610 * in each iteration. This is to avoid propagation of floating point
611 * errors in subd_incr.
612 */
613 ++tick_index;
614 cur = start + tick_index * subd_incr;
615 }
616 }
617 }
619 static void
620 sp_vruler_draw_pos (GtkRuler *ruler)
621 {
622 GtkWidget *widget;
623 GdkGC *gc;
624 int i;
625 gint x, y;
626 gint width, height;
627 gint bs_width, bs_height;
628 gint xthickness;
629 gint ythickness;
630 gfloat increment;
632 g_return_if_fail (ruler != NULL);
633 g_return_if_fail (SP_IS_VRULER (ruler));
635 if (GTK_WIDGET_DRAWABLE (ruler))
636 {
637 widget = GTK_WIDGET (ruler);
639 gc = widget->style->fg_gc[GTK_STATE_NORMAL];
640 xthickness = widget->style->xthickness;
641 ythickness = widget->style->ythickness;
642 width = widget->allocation.width - xthickness * 2;
643 height = widget->allocation.height; // in pixels; is apparently 2 pixels shorter than the canvas at each end
645 bs_height = width / 2;
646 bs_height |= 1; /* make sure it's odd */
647 bs_width = bs_height / 2 + 1;
649 if ((bs_width > 0) && (bs_height > 0))
650 {
651 /* If a backing store exists, restore the ruler */
652 if (ruler->backing_store && ruler->non_gr_exp_gc)
653 gdk_draw_pixmap (ruler->widget.window,
654 ruler->non_gr_exp_gc,
655 ruler->backing_store,
656 ruler->xsrc, ruler->ysrc,
657 ruler->xsrc, ruler->ysrc,
658 bs_width, bs_height);
660 increment = (gfloat) (height + 2*UNUSED_PIXELS) / (ruler->upper - ruler->lower);
662 // Calculate the coordinates (x, y, in pixels) of the tip of the triangle
663 x = (width + bs_width) / 2 + xthickness;
664 y = int(Inkscape::round((ruler->position - ruler->lower) * increment + double(ythickness - bs_height) / 2.0) - UNUSED_PIXELS);
666 for (i = 0; i < bs_width; i++)
667 gdk_draw_line (widget->window, gc,
668 x + i, y + i,
669 x + i, y + bs_height - 1 - i);
671 ruler->xsrc = x;
672 ruler->ysrc = y;
673 }
674 }
675 }
677 /**
678 * The vruler widget's size_allocate callback.
679 */
680 static void
681 sp_vruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
682 {
683 g_assert (widget != NULL);
684 g_assert (SP_IS_VRULER (widget));
686 // First call the default gtk_widget_size_allocate() method (which is being overridden here)
687 if (GTK_WIDGET_CLASS (vruler_parent_class)->size_allocate)
688 (* GTK_WIDGET_CLASS (vruler_parent_class)->size_allocate) (widget, allocation);
690 // Now the size of the ruler has changed, the ruler bounds (upper & lower) need to be updated
691 // For this we first need to obtain a pointer to the desktop, by walking up the tree of ancestors
692 GtkWidget *parent = gtk_widget_get_parent(widget);
693 do {
694 if (SP_IS_DESKTOP_WIDGET(parent)) {
695 // Now we've found the desktop widget we can have the ruler boundaries updated
696 sp_desktop_widget_update_vruler(SP_DESKTOP_WIDGET(parent));
697 // If the size of the ruler has increased, then a blank part is uncovered; therefore
698 // it must be redrawn
699 sp_vruler_draw_ticks(GTK_RULER(widget));
700 break;
701 }
702 parent = gtk_widget_get_parent(parent);
703 } while (parent != NULL);
704 }
707 /// Ruler metrics.
708 static GtkRulerMetric const sp_ruler_metrics[] = {
709 // NOTE: the order of records in this struct must correspond to the SPMetric enum.
710 {"NONE", "", 1, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
711 {"millimeters", "mm", PX_PER_MM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
712 {"centimeters", "cm", PX_PER_CM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
713 {"inches", "in", PX_PER_IN, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
714 {"feet", "ft", PX_PER_FT, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
715 {"points", "pt", PX_PER_PT, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
716 {"picas", "pc", PX_PER_PC, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
717 {"pixels", "px", PX_PER_PX, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
718 {"meters", "m", PX_PER_M, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
719 };
721 void
722 sp_ruler_set_metric (GtkRuler *ruler,
723 SPMetric metric)
724 {
725 g_return_if_fail (ruler != NULL);
726 g_return_if_fail (GTK_IS_RULER (ruler));
727 g_return_if_fail((unsigned) metric < G_N_ELEMENTS(sp_ruler_metrics));
729 if (metric == 0)
730 return;
732 ruler->metric = const_cast<GtkRulerMetric *>(&sp_ruler_metrics[metric]);
734 if (GTK_WIDGET_DRAWABLE (ruler))
735 gtk_widget_queue_draw (GTK_WIDGET (ruler));
736 }