Code

fix squashed vruler: add requisition methon
[inkscape.git] / src / widgets / ruler.cpp
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     //TODO: switch to GObject
45     // GtkType and such calls were deprecated a while back with the
46     // introduction of GObject as a separate layer, with GType instead. --JonCruz
48   static GtkType hruler_type = 0;
50   if (!hruler_type)
51     {
52       static const GtkTypeInfo hruler_info =
53       {
54         (gchar*) "SPHRuler",
55         sizeof (SPHRuler),
56         sizeof (SPHRulerClass),
57         (GtkClassInitFunc) sp_hruler_class_init,
58         (GtkObjectInitFunc) sp_hruler_init,
59         /* reserved_1 */ NULL,
60         /* reserved_2 */ NULL,
61         (GtkClassInitFunc) NULL,
62       };
63   
64       hruler_type = gtk_type_unique (gtk_ruler_get_type (), &hruler_info);
65     }
67   return hruler_type;
68 }
70 static void
71 sp_hruler_class_init (SPHRulerClass *klass)
72 {
73   GtkWidgetClass *widget_class;
74   GtkRulerClass *ruler_class;
76   hruler_parent_class = (GtkWidgetClass *) gtk_type_class (GTK_TYPE_RULER);
78   widget_class = (GtkWidgetClass*) klass;
79   ruler_class = (GtkRulerClass*) klass;
81   widget_class->motion_notify_event = sp_hruler_motion_notify;
82   widget_class->size_allocate = sp_hruler_size_allocate;
84   ruler_class->draw_ticks = sp_hruler_draw_ticks;
85   ruler_class->draw_pos = sp_hruler_draw_pos;
86 }
88 static void
89 sp_hruler_init (SPHRuler *hruler)
90 {
91   GtkWidget *widget;
93   widget = GTK_WIDGET (hruler);
94   widget->requisition.width = widget->style->xthickness * 2 + 1;
95   widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT;
96 }
99 GtkWidget*
100 sp_hruler_new (void)
102   return GTK_WIDGET (gtk_type_new (sp_hruler_get_type ()));
105 static gint
106 sp_hruler_motion_notify (GtkWidget      *widget,
107                           GdkEventMotion *event)
109   GtkRuler *ruler;
110   
111   g_return_val_if_fail (widget != NULL, FALSE);
112   g_return_val_if_fail (SP_IS_HRULER (widget), FALSE);
113   g_return_val_if_fail (event != NULL, FALSE);
115   ruler = GTK_RULER (widget);
116   double x = event->x; //Although event->x is double according to the docs, it only appears to return integers
117   ruler->position = ruler->lower + (ruler->upper - ruler->lower) * (x + UNUSED_PIXELS) / (widget->allocation.width + 2*UNUSED_PIXELS);
118   
119   /*  Make sure the ruler has been allocated already  */
120   if (ruler->backing_store != NULL)
121     gtk_ruler_draw_pos (ruler);
123   return FALSE;
126 static void
127 sp_hruler_draw_ticks (GtkRuler *ruler)
129   GtkWidget *widget;
130   GdkGC *gc, *bg_gc;
131   PangoFontDescription *pango_desc;
132   PangoContext *pango_context;
133   PangoLayout *pango_layout;
134   gint i, tick_index;
135   gint width, height;
136   gint xthickness;
137   gint ythickness;
138   gint length, ideal_length;
139   double lower, upper;          /* Upper and lower limits, in ruler units */
140   double increment;             /* Number of pixels per unit */
141   gint scale;                   /* Number of units per major unit */
142   double subd_incr;
143   double start, end, cur;
144   gchar unit_str[32];
145   gint digit_height;
146   gint text_width;
147   gint pos;
149   g_return_if_fail (ruler != NULL);
150   g_return_if_fail (SP_IS_HRULER (ruler));
152   if (!GTK_WIDGET_DRAWABLE (ruler)) 
153     return;
155   widget = GTK_WIDGET (ruler);
157   gc = widget->style->fg_gc[GTK_STATE_NORMAL];
158   bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
159   
160   pango_desc = widget->style->font_desc;
161   
162   // Create the pango layout
163   pango_context = gtk_widget_get_pango_context (widget);
165   pango_layout = pango_layout_new (pango_context);
167   PangoFontDescription *fs = pango_font_description_new ();
168   pango_font_description_set_size (fs, RULER_FONT_SIZE);
169   pango_layout_set_font_description (pango_layout, fs);
170   pango_font_description_free (fs);
172   digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
174   xthickness = widget->style->xthickness;
175   ythickness = widget->style->ythickness;
177   width = widget->allocation.width; // in pixels; is apparently 2 pixels shorter than the canvas at each end
178   height = widget->allocation.height;// - ythickness * 2;
179     
180   gtk_paint_box (widget->style, ruler->backing_store,
181                  GTK_STATE_NORMAL, GTK_SHADOW_NONE, 
182                  NULL, widget, "hruler",
183                  0, 0, 
184                  widget->allocation.width, widget->allocation.height);
186   upper = ruler->upper / ruler->metric->pixels_per_unit; // upper and lower are expressed in ruler units
187   lower = ruler->lower / ruler->metric->pixels_per_unit;
188   /* "pixels_per_unit" should be "points_per_unit". This is the size of the unit
189    * in 1/72nd's of an inch and has nothing to do with screen pixels */
190   
191   if ((upper - lower) == 0) 
192     return;
193   increment = (double) (width + 2*UNUSED_PIXELS) / (upper - lower); // screen pixels per ruler unit
194   
195    /* determine the scale
196    *  We calculate the text size as for the vruler instead of using
197    *  text_width = gdk_string_width(font, unit_str), so that the result
198    *  for the scale looks consistent with an accompanying vruler
199    */
200   scale = (int)(ceil (ruler->max_size / ruler->metric->pixels_per_unit));
201   sprintf (unit_str, "%d", scale);
202   text_width = strlen (unit_str) * digit_height + 1;
204   for (scale = 0; scale < MAXIMUM_SCALES; scale++)
205     if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
206       break;
208   if (scale == MAXIMUM_SCALES)
209     scale = MAXIMUM_SCALES - 1;
211   /* drawing starts here */
212   length = 0;
213   for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
214     {
215       subd_incr = ruler->metric->ruler_scale[scale] / 
216                   ruler->metric->subdivide[i];
217       if (subd_incr * fabs(increment) <= MINIMUM_INCR) 
218     continue;
220       /* Calculate the length of the tickmarks. Make sure that
221        * this length increases for each set of ticks
222        */
223       ideal_length = height / (i + 1) - 1;
224       if (ideal_length > ++length)
225         length = ideal_length;
227       if (lower < upper)
228         {
229           start = floor (lower / subd_incr) * subd_incr;
230           end   = ceil  (upper / subd_incr) * subd_incr;
231         }
232       else
233         {
234           start = floor (upper / subd_incr) * subd_incr;
235           end   = ceil  (lower / subd_incr) * subd_incr;
236         }
238     tick_index = 0;
239     cur = start; // location (in ruler units) of the first invisible tick at the left side of the canvas 
241         while (cur <= end)
242         {
243           // due to the typical values for cur, lower and increment, pos will often end up to
244       // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h)
245       // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a
246       // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-9 (that's only one nanopixel ;-))
247       pos = int(Inkscape::round((cur - lower) * increment + 1e-12)) - UNUSED_PIXELS; 
248       gdk_draw_line (ruler->backing_store, gc,
249                          pos, height + ythickness, 
250                          pos, height - length + ythickness);
252           /* draw label */
253         double label_spacing_px = (increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i];
254           if (i == 0 && 
255                                 (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) && 
256                                 (label_spacing_px > 3*digit_height || tick_index%4 == 0 ||  cur == 0))
257             {
258                                 if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
259                                         sprintf (unit_str, "%dk", ((int) cur)/1000);
260                                 else
261                                         sprintf (unit_str, "%d", (int) cur);
262         
263                                 pango_layout_set_text (pango_layout, unit_str, -1);
264               
265                                 gdk_draw_layout (ruler->backing_store, gc,
266                                pos + 2, 0, pango_layout);
267             }
269       /* Calculate cur from start rather than incrementing by subd_incr
270        * in each iteration. This is to avoid propagation of floating point 
271        * errors in subd_incr.
272        */
273       ++tick_index;
274       cur = start + tick_index * subd_incr;
275         }
276     }
279 static void
280 sp_hruler_draw_pos (GtkRuler *ruler)
282   GtkWidget *widget;
283   GdkGC *gc;
284   int i;
285   gint x, y;
286   gint width, height;
287   gint bs_width, bs_height;
288   gint xthickness;
289   gint ythickness;
290   gfloat increment;
292   g_return_if_fail (ruler != NULL);
293   g_return_if_fail (SP_IS_HRULER (ruler));
295   if (GTK_WIDGET_DRAWABLE (ruler))
296     {
297       widget = GTK_WIDGET (ruler);
299       gc = widget->style->fg_gc[GTK_STATE_NORMAL];
300       xthickness = widget->style->xthickness;
301       ythickness = widget->style->ythickness;
302       width = widget->allocation.width; // in pixels; is apparently 2 pixels shorter than the canvas at each end
303       height = widget->allocation.height - ythickness * 2;
305       bs_width = height / 2;
306       bs_width |= 1;  /* make sure it's odd */
307       bs_height = bs_width / 2 + 1;
309       if ((bs_width > 0) && (bs_height > 0))
310         {
311           /*  If a backing store exists, restore the ruler  */
312           if (ruler->backing_store && ruler->non_gr_exp_gc)
313             gdk_draw_pixmap (ruler->widget.window,
314                              ruler->non_gr_exp_gc,
315                              ruler->backing_store,
316                              ruler->xsrc, ruler->ysrc,
317                              ruler->xsrc, ruler->ysrc,
318                              bs_width, bs_height);
320           increment = (gfloat) (width + 2*UNUSED_PIXELS) / (ruler->upper - ruler->lower);
322           // Calculate the coordinates (x, y, in pixels) of the tip of the triangle
323       x = int(Inkscape::round((ruler->position - ruler->lower) * increment + double(xthickness - bs_width) / 2.0) - UNUSED_PIXELS);
324           y = (height + bs_height) / 2 + ythickness;
326           for (i = 0; i < bs_height; i++)
327             gdk_draw_line (widget->window, gc,
328                            x + i, y + i,
329                            x + bs_width - 1 - i, y + i);
332           ruler->xsrc = x;
333           ruler->ysrc = y;
334         }
335     }
338 /**
339  * The hruler widget's size_allocate callback.
340  */
341 static void
342 sp_hruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
344     g_assert (widget != NULL);
345     g_assert (SP_IS_HRULER (widget));    
346     
347     // First call the default gtk_widget_size_allocate() method (which is being overridden here)
348     if (GTK_WIDGET_CLASS (hruler_parent_class)->size_allocate)
349         (* GTK_WIDGET_CLASS (hruler_parent_class)->size_allocate) (widget, allocation);
350  
351     // Now the size of the ruler has changed, the ruler bounds (upper & lower) need to be updated
352     // For this we first need to obtain a pointer to the desktop, by walking up the tree of ancestors    
353     GtkWidget *parent = gtk_widget_get_parent(widget);    
354     do {
355         if (SP_IS_DESKTOP_WIDGET(parent)) {
356             // Now we've found the desktop widget we can have the ruler boundaries updated 
357             sp_desktop_widget_update_hruler(SP_DESKTOP_WIDGET(parent));
358             // If the size of the ruler has increased, then a blank part is uncovered; therefore
359             // it must be redrawn  
360             sp_hruler_draw_ticks(GTK_RULER(widget));
361             break;   
362         }
363         parent = gtk_widget_get_parent(parent);
364     } while (parent != NULL);
370 // vruler
372 static void sp_vruler_class_init    (SPVRulerClass *klass);
373 static void sp_vruler_init          (SPVRuler      *vruler);
374 static gint sp_vruler_motion_notify (GtkWidget      *widget,
375                                       GdkEventMotion *event);
376 static void sp_vruler_draw_ticks    (GtkRuler       *ruler);
377 static void sp_vruler_draw_pos      (GtkRuler       *ruler);
378 static void sp_vruler_size_request (GtkWidget *widget, GtkRequisition *requisition);
379 static void sp_vruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
381 static GtkWidgetClass *vruler_parent_class;
383 GtkType
384 sp_vruler_get_type (void)
386     //TODO: switch to GObject
387     // GtkType and such calls were deprecated a while back with the
388     // introduction of GObject as a separate layer, with GType instead. --JonCruz
390   static GtkType vruler_type = 0;
392   if (!vruler_type)
393     {
394       static const GtkTypeInfo vruler_info =
395       {
396         (gchar*) "SPVRuler",
397         sizeof (SPVRuler),
398         sizeof (SPVRulerClass),
399         (GtkClassInitFunc) sp_vruler_class_init,
400         (GtkObjectInitFunc) sp_vruler_init,
401         /* reserved_1 */ NULL,
402         /* reserved_2 */ NULL,
403         (GtkClassInitFunc) NULL,
404       };
406       vruler_type = gtk_type_unique (gtk_ruler_get_type (), &vruler_info);
407     }
409   return vruler_type;
412 static void
413 sp_vruler_class_init (SPVRulerClass *klass)
415   GtkWidgetClass *widget_class;
416   GtkRulerClass *ruler_class;
418   vruler_parent_class = (GtkWidgetClass *) gtk_type_class (GTK_TYPE_RULER);
420   widget_class = (GtkWidgetClass*) klass;
421   ruler_class = (GtkRulerClass*) klass;
423   widget_class->motion_notify_event = sp_vruler_motion_notify;
424   widget_class->size_allocate = sp_vruler_size_allocate;
425   widget_class->size_request = sp_vruler_size_request;
427   ruler_class->draw_ticks = sp_vruler_draw_ticks;
428   ruler_class->draw_pos = sp_vruler_draw_pos;
431 static void
432 sp_vruler_init (SPVRuler *vruler)
434   GtkWidget *widget;
436   widget = GTK_WIDGET (vruler);
437   widget->requisition.width = widget->style->xthickness * 2 + RULER_WIDTH;
438   widget->requisition.height = widget->style->ythickness * 2 + 1;
441 GtkWidget*
442 sp_vruler_new (void)
444   return GTK_WIDGET (gtk_type_new (sp_vruler_get_type ()));
448 static gint
449 sp_vruler_motion_notify (GtkWidget      *widget,
450                           GdkEventMotion *event)
452   GtkRuler *ruler;
453   
454   g_return_val_if_fail (widget != NULL, FALSE);
455   g_return_val_if_fail (SP_IS_VRULER (widget), FALSE);
456   g_return_val_if_fail (event != NULL, FALSE);
458   ruler = GTK_RULER (widget);
459   double y = event->y; //Although event->y is double according to the docs, it only appears to return integers
460   ruler->position = ruler->lower + (ruler->upper - ruler->lower) * (y + UNUSED_PIXELS) / (widget->allocation.height + 2*UNUSED_PIXELS);
462   /*  Make sure the ruler has been allocated already  */
463   if (ruler->backing_store != NULL)
464     gtk_ruler_draw_pos (ruler);
466   return FALSE;
469 static void
470 sp_vruler_draw_ticks (GtkRuler *ruler)
472   GtkWidget *widget;
473   GdkGC *gc, *bg_gc;
474   PangoFontDescription *pango_desc;
475   PangoContext *pango_context;
476   PangoLayout *pango_layout;
477   gint i, j, tick_index;
478   gint width, height;
479   gint xthickness;
480   gint ythickness;
481   gint length, ideal_length;
482   double lower, upper;          /* Upper and lower limits, in ruler units */
483   double increment;             /* Number of pixels per unit */
484   gint scale;                   /* Number of units per major unit */
485   double subd_incr;
486   double start, end, cur;
487   gchar unit_str[32];
488   gchar digit_str[2] = { '\0', '\0' };
489   gint digit_height;
490   gint text_height;
491   gint pos;
493   g_return_if_fail (ruler != NULL);
494   g_return_if_fail (SP_IS_VRULER (ruler));
496   if (!GTK_WIDGET_DRAWABLE (ruler)) 
497     return;
499   widget = GTK_WIDGET (ruler);
501   gc = widget->style->fg_gc[GTK_STATE_NORMAL];
502   bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
503   
504   pango_desc = widget->style->font_desc;
505   
506   // Create the pango layout
507   pango_context = gtk_widget_get_pango_context (widget);
509   pango_layout = pango_layout_new (pango_context);
511   PangoFontDescription *fs = pango_font_description_new ();
512   pango_font_description_set_size (fs, RULER_FONT_SIZE);
513   pango_layout_set_font_description (pango_layout, fs);
514   pango_font_description_free (fs);
516   digit_height = (int) floor (RULER_FONT_SIZE * RULER_FONT_VERTICAL_SPACING / PANGO_SCALE + 0.5);
517   
518   xthickness = widget->style->xthickness;
519   ythickness = widget->style->ythickness;
521   width = widget->allocation.height; //in pixels; is apparently 2 pixels shorter than the canvas at each end
522   height = widget->allocation.width;// - ythickness * 2;
524   gtk_paint_box (widget->style, ruler->backing_store,
525                  GTK_STATE_NORMAL, GTK_SHADOW_NONE, 
526                  NULL, widget, "vruler",
527                  0, 0, 
528                  widget->allocation.width, widget->allocation.height);
529   
530   upper = ruler->upper / ruler->metric->pixels_per_unit; // upper and lower are expressed in ruler units
531   lower = ruler->lower / ruler->metric->pixels_per_unit;
532   /* "pixels_per_unit" should be "points_per_unit". This is the size of the unit
533    * in 1/72nd's of an inch and has nothing to do with screen pixels */
534   
535   if ((upper - lower) == 0)
536     return;
537   increment = (double) (width + 2*UNUSED_PIXELS) / (upper - lower); // screen pixels per ruler unit
539   /* determine the scale
540    *   use the maximum extents of the ruler to determine the largest
541    *   possible number to be displayed.  Calculate the height in pixels
542    *   of this displayed text. Use this height to find a scale which
543    *   leaves sufficient room for drawing the ruler.  
544    */
545   scale = (int)ceil (ruler->max_size / ruler->metric->pixels_per_unit);
546   sprintf (unit_str, "%d", scale);
547   text_height = strlen (unit_str) * digit_height + 1;
549   for (scale = 0; scale < MAXIMUM_SCALES; scale++)
550     if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
551       break;
553   if (scale == MAXIMUM_SCALES)
554     scale = MAXIMUM_SCALES - 1;
556   /* drawing starts here */
557   length = 0;
558   for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--) {
559                 subd_incr = (double) ruler->metric->ruler_scale[scale] / 
560                         (double) ruler->metric->subdivide[i];
561                 if (subd_incr * fabs(increment) <= MINIMUM_INCR) 
562                         continue;
564                 /* Calculate the length of the tickmarks. Make sure that
565                  * this length increases for each set of ticks
566                  */
567                 ideal_length = height / (i + 1) - 1;
568                 if (ideal_length > ++length)
569                         length = ideal_length;
571                 if (lower < upper)
572                         {
573                                 start = floor (lower / subd_incr) * subd_incr;
574                                 end   = ceil  (upper / subd_incr) * subd_incr;
575                         }
576                 else
577                         {
578                                 start = floor (upper / subd_incr) * subd_incr;
579                                 end   = ceil  (lower / subd_incr) * subd_incr;
580                         }
582     tick_index = 0;
583     cur = start; // location (in ruler units) of the first invisible tick at the top side of the canvas       
585     while (cur < end) {
586           // due to the typical values for cur, lower and increment, pos will often end up to
587           // be e.g. 641.50000000000; rounding behaviour is not defined in such a case (see round.h)
588           // and jitter will be apparent (upon redrawing some of the lines on the ruler might jump a
589           // by a pixel, and jump back on the next redraw). This is suppressed by adding 1e-9 (that's only one nanopixel ;-))
590             pos = int(Inkscape::round((cur - lower) * increment + 1e-12)) - UNUSED_PIXELS;
592                         gdk_draw_line (ruler->backing_store, gc,
593                                                                                  height + xthickness - length, pos,
594                                                                                  height + xthickness, pos);
596                         /* draw label */
597                         double label_spacing_px = fabs((increment*(double)ruler->metric->ruler_scale[scale])/ruler->metric->subdivide[i]);
598                         if (i == 0 && 
599                                         (label_spacing_px > 6*digit_height || tick_index%2 == 0 || cur == 0) && 
600                                         (label_spacing_px > 3*digit_height || tick_index%4 == 0 || cur == 0))
601                                 {
602                                         if (fabs((int)cur) >= 2000 && (((int) cur)/1000)*1000 == ((int) cur))
603                                                 sprintf (unit_str, "%dk", ((int) cur)/1000);
604                                         else
605                                                 sprintf (unit_str, "%d", (int) cur);
606                                         for (j = 0; j < (int) strlen (unit_str); j++)
607                                                 {
608                                                         digit_str[0] = unit_str[j];
609                   
610                                                         pango_layout_set_text (pango_layout, digit_str, 1);
611       
612                                                         gdk_draw_layout (ruler->backing_store, gc,
613                                                                                                                          xthickness + 1, 
614                                                                                                                          pos + digit_height * (j) + 1,
615                                                                                                                          pango_layout); 
616                                                 }
617                                 }
619                         /* Calculate cur from start rather than incrementing by subd_incr
620                          * in each iteration. This is to avoid propagation of floating point 
621                          * errors in subd_incr.
622                          */
623                         ++tick_index;
624                         cur = start + tick_index * subd_incr;
625                 }
626         }
629 static void
630 sp_vruler_draw_pos (GtkRuler *ruler)
632   GtkWidget *widget;
633   GdkGC *gc;
634   int i;
635   gint x, y;
636   gint width, height;
637   gint bs_width, bs_height;
638   gint xthickness;
639   gint ythickness;
640   gfloat increment;
642   g_return_if_fail (ruler != NULL);
643   g_return_if_fail (SP_IS_VRULER (ruler));
645   if (GTK_WIDGET_DRAWABLE (ruler))
646     {
647       widget = GTK_WIDGET (ruler);
649       gc = widget->style->fg_gc[GTK_STATE_NORMAL];
650       xthickness = widget->style->xthickness;
651       ythickness = widget->style->ythickness;
652       width = widget->allocation.width - xthickness * 2;
653       height = widget->allocation.height; // in pixels; is apparently 2 pixels shorter than the canvas at each end
655       bs_height = width / 2;
656       bs_height |= 1;  /* make sure it's odd */
657       bs_width = bs_height / 2 + 1;
659       if ((bs_width > 0) && (bs_height > 0))
660         {
661           /*  If a backing store exists, restore the ruler  */
662           if (ruler->backing_store && ruler->non_gr_exp_gc)
663             gdk_draw_pixmap (ruler->widget.window,
664                              ruler->non_gr_exp_gc,
665                              ruler->backing_store,
666                              ruler->xsrc, ruler->ysrc,
667                              ruler->xsrc, ruler->ysrc,
668                              bs_width, bs_height);
670           increment = (gfloat) (height + 2*UNUSED_PIXELS) / (ruler->upper - ruler->lower);
672           // Calculate the coordinates (x, y, in pixels) of the tip of the triangle
673       x = (width + bs_width) / 2 + xthickness;
674           y = int(Inkscape::round((ruler->position - ruler->lower) * increment + double(ythickness - bs_height) / 2.0) - UNUSED_PIXELS);
676           for (i = 0; i < bs_width; i++)
677             gdk_draw_line (widget->window, gc,
678                            x + i, y + i,
679                            x + i, y + bs_height - 1 - i);
681           ruler->xsrc = x;
682           ruler->ysrc = y;
683         }
684     }
687 static void
688 sp_vruler_size_request (GtkWidget *widget, GtkRequisition *requisition)
690   requisition->width = widget->style->xthickness * 2 + RULER_WIDTH;
694 /**
695  * The vruler widget's size_allocate callback.
696  */
697 static void
698 sp_vruler_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
700     g_assert (widget != NULL);
701     g_assert (SP_IS_VRULER (widget));    
702     
703     // First call the default gtk_widget_size_allocate() method (which is being overridden here)
704     if (GTK_WIDGET_CLASS (vruler_parent_class)->size_allocate)
705         (* GTK_WIDGET_CLASS (vruler_parent_class)->size_allocate) (widget, allocation);
707     // Now the size of the ruler has changed, the ruler bounds (upper & lower) need to be updated
708     // For this we first need to obtain a pointer to the desktop, by walking up the tree of ancestors    
709     GtkWidget *parent = gtk_widget_get_parent(widget);    
710     do {
711         if (SP_IS_DESKTOP_WIDGET(parent)) {
712             // Now we've found the desktop widget we can have the ruler boundaries updated 
713             sp_desktop_widget_update_vruler(SP_DESKTOP_WIDGET(parent));
714             // If the size of the ruler has increased, then a blank part is uncovered; therefore
715             // it must be redrawn  
716             sp_vruler_draw_ticks(GTK_RULER(widget));
717             break;   
718         }
719         parent = gtk_widget_get_parent(parent);
720     } while (parent != NULL);
723 //TODO: warning: deprecated conversion from string constant to ‘gchar*’
724 //
725 //Turn out to be warnings that we should probably leave in place. The
726 // pointers/types used need to be read-only. So until we correct the using
727 // code, those warnings are actually desired. They say "Hey! Fix this". We
728 // definitely don't want to hide/ignore them. --JonCruz
730 /// Ruler metrics.
731 static GtkRulerMetric const sp_ruler_metrics[] = {
732   // NOTE: the order of records in this struct must correspond to the SPMetric enum.
733   {"NONE",                      "", 1, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
734   {"millimeters",       "mm", PX_PER_MM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
735   {"centimeters",       "cm", PX_PER_CM, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
736   {"inches",            "in", PX_PER_IN, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
737   {"feet",                      "ft", PX_PER_FT, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
738   {"points",            "pt", PX_PER_PT, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
739   {"picas",                     "pc", PX_PER_PC, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
740   {"pixels",            "px", PX_PER_PX, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
741   {"meters",            "m",  PX_PER_M,  { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
742 };
744 void
745 sp_ruler_set_metric (GtkRuler *ruler,
746                      SPMetric  metric)
748   g_return_if_fail (ruler != NULL);
749   g_return_if_fail (GTK_IS_RULER (ruler));
750   g_return_if_fail((unsigned) metric < G_N_ELEMENTS(sp_ruler_metrics));
752   if (metric == 0) 
753         return;
755   ruler->metric = const_cast<GtkRulerMetric *>(&sp_ruler_metrics[metric]);
757   if (GTK_WIDGET_DRAWABLE (ruler))
758     gtk_widget_queue_draw (GTK_WIDGET (ruler));