]> Pileus Git - ~andy/gtk/blob - gtk/gtkruler.c
Updated galician translations
[~andy/gtk] / gtk / gtkruler.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
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.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include "config.h"
28
29 #include <math.h>
30 #include <string.h>
31
32 #include "gtkorientable.h"
33 #include "gtkruler.h"
34 #include "gtktypeutils.h"
35 #include "gtkprivate.h"
36 #include "gtkintl.h"
37
38 #define RULER_WIDTH           14
39 #define MINIMUM_INCR          5
40 #define MAXIMUM_SUBDIVIDE     5
41 #define MAXIMUM_SCALES        10
42
43 #define ROUND(x) ((int) ((x) + 0.5))
44
45 struct _GtkRulerPrivate
46 {
47   GtkOrientation        orientation;
48   GtkRulerMetric       *metric;
49
50   cairo_surface_t      *backing_store;
51
52   gint                  slider_size;
53   gint                  xsrc;
54   gint                  ysrc;
55
56   gdouble               lower;          /* The upper limit of the ruler (in points) */
57   gdouble               max_size;       /* The maximum size of the ruler */
58   gdouble               position;       /* The position of the mark on the ruler */
59   gdouble               upper;          /* The lower limit of the ruler */
60 };
61
62 enum {
63   PROP_0,
64   PROP_ORIENTATION,
65   PROP_LOWER,
66   PROP_UPPER,
67   PROP_POSITION,
68   PROP_MAX_SIZE,
69   PROP_METRIC
70 };
71
72
73 static void     gtk_ruler_set_property    (GObject        *object,
74                                            guint            prop_id,
75                                            const GValue   *value,
76                                            GParamSpec     *pspec);
77 static void     gtk_ruler_get_property    (GObject        *object,
78                                            guint           prop_id,
79                                            GValue         *value,
80                                            GParamSpec     *pspec);
81 static void     gtk_ruler_realize         (GtkWidget      *widget);
82 static void     gtk_ruler_unrealize       (GtkWidget      *widget);
83 static void     gtk_ruler_size_request    (GtkWidget      *widget,
84                                            GtkRequisition *requisition);
85 static void     gtk_ruler_size_allocate   (GtkWidget      *widget,
86                                            GtkAllocation  *allocation);
87 static gboolean gtk_ruler_motion_notify   (GtkWidget      *widget,
88                                            GdkEventMotion *event);
89 static gboolean gtk_ruler_draw            (GtkWidget      *widget,
90                                            cairo_t        *cr);
91 static void     gtk_ruler_make_pixmap     (GtkRuler       *ruler);
92 static void     gtk_ruler_draw_ticks      (GtkRuler       *ruler);
93 static void     gtk_ruler_real_draw_ticks (GtkRuler       *ruler,
94                                            cairo_t        *cr);
95 static void     gtk_ruler_real_draw_pos   (GtkRuler       *ruler,
96                                            cairo_t        *cr);
97
98
99 static const GtkRulerMetric ruler_metrics[] =
100 {
101   { "Pixel", "Pi", 1.0, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
102   { "Inches", "In", 72.0, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
103   { "Centimeters", "Cn", 28.35, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
104 };
105
106
107 G_DEFINE_TYPE_WITH_CODE (GtkRuler, gtk_ruler, GTK_TYPE_WIDGET,
108                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
109                                                 NULL))
110
111
112 static void
113 gtk_ruler_class_init (GtkRulerClass *class)
114 {
115   GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
116   GtkWidgetClass *widget_class  = GTK_WIDGET_CLASS (class);
117
118   gobject_class->set_property = gtk_ruler_set_property;
119   gobject_class->get_property = gtk_ruler_get_property;
120
121   widget_class->realize = gtk_ruler_realize;
122   widget_class->unrealize = gtk_ruler_unrealize;
123   widget_class->size_request = gtk_ruler_size_request;
124   widget_class->size_allocate = gtk_ruler_size_allocate;
125   widget_class->motion_notify_event = gtk_ruler_motion_notify;
126   widget_class->draw = gtk_ruler_draw;
127
128   class->draw_ticks = gtk_ruler_real_draw_ticks;
129   class->draw_pos = gtk_ruler_real_draw_pos;
130
131   g_object_class_override_property (gobject_class,
132                                     PROP_ORIENTATION,
133                                     "orientation");
134
135   g_object_class_install_property (gobject_class,
136                                    PROP_LOWER,
137                                    g_param_spec_double ("lower",
138                                                         P_("Lower"),
139                                                         P_("Lower limit of ruler"),
140                                                         -G_MAXDOUBLE,
141                                                         G_MAXDOUBLE,
142                                                         0.0,
143                                                         GTK_PARAM_READWRITE));  
144
145   g_object_class_install_property (gobject_class,
146                                    PROP_UPPER,
147                                    g_param_spec_double ("upper",
148                                                         P_("Upper"),
149                                                         P_("Upper limit of ruler"),
150                                                         -G_MAXDOUBLE,
151                                                         G_MAXDOUBLE,
152                                                         0.0,
153                                                         GTK_PARAM_READWRITE));  
154
155   g_object_class_install_property (gobject_class,
156                                    PROP_POSITION,
157                                    g_param_spec_double ("position",
158                                                         P_("Position"),
159                                                         P_("Position of mark on the ruler"),
160                                                         -G_MAXDOUBLE,
161                                                         G_MAXDOUBLE,
162                                                         0.0,
163                                                         GTK_PARAM_READWRITE));  
164
165   g_object_class_install_property (gobject_class,
166                                    PROP_MAX_SIZE,
167                                    g_param_spec_double ("max-size",
168                                                         P_("Max Size"),
169                                                         P_("Maximum size of the ruler"),
170                                                         -G_MAXDOUBLE,
171                                                         G_MAXDOUBLE,
172                                                         0.0,
173                                                         GTK_PARAM_READWRITE));  
174   /**
175    * GtkRuler:metric:
176    *
177    * The metric used for the ruler.
178    *
179    * Since: 2.8
180    */
181   g_object_class_install_property (gobject_class,
182                                    PROP_METRIC,
183                                    g_param_spec_enum ("metric",
184                                                       P_("Metric"),
185                                                       P_("The metric used for the ruler"),
186                                                       GTK_TYPE_METRIC_TYPE, 
187                                                       GTK_PIXELS,
188                                                       GTK_PARAM_READWRITE));  
189
190   g_type_class_add_private (gobject_class, sizeof (GtkRulerPrivate));
191 }
192
193 static void
194 gtk_ruler_init (GtkRuler *ruler)
195 {
196   GtkRulerPrivate *priv;
197
198   ruler->priv = G_TYPE_INSTANCE_GET_PRIVATE (ruler,
199                                              GTK_TYPE_RULER,
200                                              GtkRulerPrivate);
201   priv = ruler->priv;
202
203   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
204
205   priv->backing_store = NULL;
206   priv->xsrc = 0;
207   priv->ysrc = 0;
208   priv->slider_size = 0;
209   priv->lower = 0;
210   priv->upper = 0;
211   priv->position = 0;
212   priv->max_size = 0;
213
214   gtk_ruler_set_metric (ruler, GTK_PIXELS);
215 }
216
217 static void
218 gtk_ruler_set_property (GObject      *object,
219                         guint         prop_id,
220                         const GValue *value,
221                         GParamSpec   *pspec)
222 {
223   GtkRuler *ruler = GTK_RULER (object);
224   GtkRulerPrivate *priv = ruler->priv;
225
226   switch (prop_id)
227     {
228     case PROP_ORIENTATION:
229       priv->orientation = g_value_get_enum (value);
230       gtk_widget_queue_resize (GTK_WIDGET (ruler));
231       break;
232     case PROP_LOWER:
233       gtk_ruler_set_range (ruler, g_value_get_double (value), priv->upper,
234                            priv->position, priv->max_size);
235       break;
236     case PROP_UPPER:
237       gtk_ruler_set_range (ruler, priv->lower, g_value_get_double (value),
238                            priv->position, priv->max_size);
239       break;
240     case PROP_POSITION:
241       gtk_ruler_set_range (ruler, priv->lower, priv->upper,
242                            g_value_get_double (value), priv->max_size);
243       break;
244     case PROP_MAX_SIZE:
245       gtk_ruler_set_range (ruler, priv->lower, priv->upper,
246                            priv->position,  g_value_get_double (value));
247       break;
248     case PROP_METRIC:
249       gtk_ruler_set_metric (ruler, g_value_get_enum (value));
250       break;
251     default:
252       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253       break;
254     }
255 }
256
257 static void
258 gtk_ruler_get_property (GObject      *object,
259                         guint         prop_id,
260                         GValue       *value,
261                         GParamSpec   *pspec)
262 {
263   GtkRuler *ruler = GTK_RULER (object);
264   GtkRulerPrivate *priv = ruler->priv;
265
266   switch (prop_id)
267     {
268     case PROP_ORIENTATION:
269       g_value_set_enum (value, priv->orientation);
270       break;
271     case PROP_LOWER:
272       g_value_set_double (value, priv->lower);
273       break;
274     case PROP_UPPER:
275       g_value_set_double (value, priv->upper);
276       break;
277     case PROP_POSITION:
278       g_value_set_double (value, priv->position);
279       break;
280     case PROP_MAX_SIZE:
281       g_value_set_double (value, priv->max_size);
282       break;
283     case PROP_METRIC:
284       g_value_set_enum (value, gtk_ruler_get_metric (ruler));
285       break;
286     default:
287       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288       break;
289     }
290 }
291
292 /**
293  * gtk_ruler_new:
294  * @orientation: the ruler's orientation.
295  *
296  * Creates a new #GtkRuler with the given orientation.
297  *
298  * Return value: a new #GtkRuler.
299  *
300  * Since: 3.0
301  **/
302 GtkWidget *
303 gtk_ruler_new (GtkOrientation orientation)
304 {
305   return g_object_new (GTK_TYPE_RULER,
306                        "orientation", orientation,
307                        NULL);
308 }
309
310 /**
311  * gtk_ruler_invalidate_ticks:
312  * @ruler: the ruler to invalidate
313  *
314  * For performance reasons, #GtkRuler keeps a backbuffer containing the
315  * prerendered contents of the ticks. To cause a repaint this buffer,
316  * call this function instead of gtk_widget_queue_draw().
317  **/
318 static void
319 gtk_ruler_invalidate_ticks (GtkRuler *ruler)
320 {
321   g_return_if_fail (GTK_IS_RULER (ruler));
322
323   if (ruler->priv->backing_store == NULL)
324     return;
325
326   gtk_ruler_draw_ticks (ruler);
327   gtk_widget_queue_draw (GTK_WIDGET (ruler));
328 }
329
330 void
331 gtk_ruler_set_metric (GtkRuler      *ruler,
332                       GtkMetricType  metric)
333 {
334   GtkRulerPrivate *priv;
335
336   g_return_if_fail (GTK_IS_RULER (ruler));
337
338   priv = ruler->priv;
339
340   priv->metric = (GtkRulerMetric *) &ruler_metrics[metric];
341
342   g_object_notify (G_OBJECT (ruler), "metric");
343
344   gtk_ruler_invalidate_ticks (ruler);
345 }
346
347 /**
348  * gtk_ruler_get_metric:
349  * @ruler: a #GtkRuler
350  *
351  * Gets the units used for a #GtkRuler. See gtk_ruler_set_metric().
352  *
353  * Return value: the units currently used for @ruler
354  **/
355 GtkMetricType
356 gtk_ruler_get_metric (GtkRuler *ruler)
357 {
358   GtkRulerPrivate *priv;
359   gint i;
360
361   g_return_val_if_fail (GTK_IS_RULER (ruler), 0);
362
363   priv = ruler->priv;
364
365   for (i = 0; i < G_N_ELEMENTS (ruler_metrics); i++)
366     if (priv->metric == &ruler_metrics[i])
367       return i;
368
369   g_assert_not_reached ();
370
371   return 0;
372 }
373
374 /**
375  * gtk_ruler_set_range:
376  * @ruler: the gtkruler
377  * @lower: the lower limit of the ruler
378  * @upper: the upper limit of the ruler
379  * @position: the mark on the ruler
380  * @max_size: the maximum size of the ruler used when calculating the space to
381  * leave for the text
382  *
383  * This sets the range of the ruler. 
384  */
385 void
386 gtk_ruler_set_range (GtkRuler *ruler,
387                      gdouble   lower,
388                      gdouble   upper,
389                      gdouble   position,
390                      gdouble   max_size)
391 {
392   GtkRulerPrivate *priv;
393
394   g_return_if_fail (GTK_IS_RULER (ruler));
395
396   priv = ruler->priv;
397
398   g_object_freeze_notify (G_OBJECT (ruler));
399   if (priv->lower != lower)
400     {
401       priv->lower = lower;
402       g_object_notify (G_OBJECT (ruler), "lower");
403     }
404   if (priv->upper != upper)
405     {
406       priv->upper = upper;
407       g_object_notify (G_OBJECT (ruler), "upper");
408     }
409   if (priv->position != position)
410     {
411       priv->position = position;
412       g_object_notify (G_OBJECT (ruler), "position");
413     }
414   if (priv->max_size != max_size)
415     {
416       priv->max_size = max_size;
417       g_object_notify (G_OBJECT (ruler), "max-size");
418     }
419   g_object_thaw_notify (G_OBJECT (ruler));
420
421   gtk_ruler_invalidate_ticks (ruler);
422 }
423
424 /**
425  * gtk_ruler_get_range:
426  * @ruler: a #GtkRuler
427  * @lower: (allow-none): location to store lower limit of the ruler, or %NULL
428  * @upper: (allow-none): location to store upper limit of the ruler, or %NULL
429  * @position: (allow-none): location to store the current position of the mark on the ruler, or %NULL
430  * @max_size: location to store the maximum size of the ruler used when calculating
431  *            the space to leave for the text, or %NULL.
432  *
433  * Retrieves values indicating the range and current position of a #GtkRuler.
434  * See gtk_ruler_set_range().
435  **/
436 void
437 gtk_ruler_get_range (GtkRuler *ruler,
438                      gdouble  *lower,
439                      gdouble  *upper,
440                      gdouble  *position,
441                      gdouble  *max_size)
442 {
443   GtkRulerPrivate *priv;
444
445   g_return_if_fail (GTK_IS_RULER (ruler));
446
447   priv = ruler->priv;
448
449   if (lower)
450     *lower = priv->lower;
451   if (upper)
452     *upper = priv->upper;
453   if (position)
454     *position = priv->position;
455   if (max_size)
456     *max_size = priv->max_size;
457 }
458
459 static void
460 gtk_ruler_draw_ticks (GtkRuler *ruler)
461 {
462   GtkRulerPrivate *priv = ruler->priv;
463   cairo_t *cr;
464
465   g_return_if_fail (GTK_IS_RULER (ruler));
466
467   if (priv->backing_store == NULL)
468     return;
469   
470   cr = cairo_create (priv->backing_store);
471
472   if (GTK_RULER_GET_CLASS (ruler)->draw_ticks)
473     GTK_RULER_GET_CLASS (ruler)->draw_ticks (ruler, cr);
474
475   cairo_destroy (cr);
476 }
477
478 static void
479 gtk_ruler_realize (GtkWidget *widget)
480 {
481   GtkAllocation allocation;
482   GtkRuler *ruler;
483   GdkWindow *window;
484   GdkWindowAttr attributes;
485   gint attributes_mask;
486
487   ruler = GTK_RULER (widget);
488
489   gtk_widget_set_realized (widget, TRUE);
490
491   gtk_widget_get_allocation (widget, &allocation);
492
493   attributes.window_type = GDK_WINDOW_CHILD;
494   attributes.x = allocation.x;
495   attributes.y = allocation.y;
496   attributes.width = allocation.width;
497   attributes.height = allocation.height;
498   attributes.wclass = GDK_INPUT_OUTPUT;
499   attributes.visual = gtk_widget_get_visual (widget);
500   attributes.event_mask = gtk_widget_get_events (widget);
501   attributes.event_mask |= (GDK_EXPOSURE_MASK |
502                             GDK_POINTER_MOTION_MASK |
503                             GDK_POINTER_MOTION_HINT_MASK);
504
505   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
506
507   window = gdk_window_new (gtk_widget_get_parent_window (widget),
508                            &attributes, attributes_mask);
509   gtk_widget_set_window (widget, window);
510   gdk_window_set_user_data (window, ruler);
511
512   gtk_widget_style_attach (widget);
513   gtk_style_set_background (gtk_widget_get_style (widget),
514                             window, GTK_STATE_ACTIVE);
515
516   gtk_ruler_make_pixmap (ruler);
517 }
518
519 static void
520 gtk_ruler_unrealize (GtkWidget *widget)
521 {
522   GtkRuler *ruler = GTK_RULER (widget);
523   GtkRulerPrivate *priv = ruler->priv;
524
525   if (priv->backing_store)
526     {
527       cairo_surface_destroy (priv->backing_store);
528       priv->backing_store = NULL;
529     }
530
531   GTK_WIDGET_CLASS (gtk_ruler_parent_class)->unrealize (widget);
532 }
533
534 static void
535 gtk_ruler_size_request (GtkWidget      *widget,
536                         GtkRequisition *requisition)
537 {
538   GtkRuler *ruler = GTK_RULER (widget);
539   GtkRulerPrivate *priv = ruler->priv;
540   GtkStyle *style;
541
542   style = gtk_widget_get_style (widget);
543
544   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
545     {
546       requisition->width  = style->xthickness * 2 + 1;
547       requisition->height = style->ythickness * 2 + RULER_WIDTH;
548     }
549   else
550     {
551       requisition->width  = style->xthickness * 2 + RULER_WIDTH;
552       requisition->height = style->ythickness * 2 + 1;
553     }
554 }
555
556 static void
557 gtk_ruler_size_allocate (GtkWidget     *widget,
558                          GtkAllocation *allocation)
559 {
560   GtkRuler *ruler = GTK_RULER (widget);
561   GtkAllocation old_allocation;
562   gboolean resized;
563
564   gtk_widget_get_allocation (widget, &old_allocation);
565   resized = (old_allocation.width != allocation->width ||
566              old_allocation.height != allocation->height);
567
568   gtk_widget_set_allocation (widget, allocation);
569
570   if (gtk_widget_get_realized (widget))
571     {
572       gdk_window_move_resize (gtk_widget_get_window (widget),
573                               allocation->x, allocation->y,
574                               allocation->width, allocation->height);
575
576       if (resized)
577         gtk_ruler_make_pixmap (ruler);
578     }
579 }
580
581 static gboolean
582 gtk_ruler_motion_notify (GtkWidget      *widget,
583                          GdkEventMotion *event)
584 {
585   GtkAllocation allocation;
586   GtkRuler *ruler = GTK_RULER (widget);
587   GtkRulerPrivate *priv = ruler->priv;
588   gint x;
589   gint y;
590
591   gdk_event_request_motions (event);
592   x = event->x;
593   y = event->y;
594
595   gtk_widget_get_allocation (widget, &allocation);
596   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
597     priv->position = priv->lower + ((priv->upper - priv->lower) * x) / allocation.width;
598   else
599     priv->position = priv->lower + ((priv->upper - priv->lower) * y) / allocation.height;
600
601   g_object_notify (G_OBJECT (ruler), "position");
602
603   gtk_widget_queue_draw (widget);
604
605   return FALSE;
606 }
607
608 static gboolean
609 gtk_ruler_draw (GtkWidget *widget,
610                 cairo_t   *cr)
611 {
612   GtkRuler *ruler = GTK_RULER (widget);
613   GtkRulerPrivate *priv = ruler->priv;
614
615   cairo_set_source_surface (cr, priv->backing_store, 0, 0);
616   cairo_paint (cr);
617   
618   if (GTK_RULER_GET_CLASS (ruler)->draw_pos)
619      GTK_RULER_GET_CLASS (ruler)->draw_pos (ruler, cr);
620
621   return FALSE;
622 }
623
624 static void
625 gtk_ruler_make_pixmap (GtkRuler *ruler)
626 {
627   GtkRulerPrivate *priv = ruler->priv;
628   GtkAllocation allocation;
629   GtkWidget *widget;
630
631   widget = GTK_WIDGET (ruler);
632
633   gtk_widget_get_allocation (widget, &allocation);
634
635   if (priv->backing_store)
636     cairo_surface_destroy (priv->backing_store);
637
638   priv->backing_store = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
639                                                            CAIRO_CONTENT_COLOR,
640                                                            allocation.width,
641                                                            allocation.height);
642
643   priv->xsrc = 0;
644   priv->ysrc = 0;
645
646   gtk_ruler_draw_ticks (ruler);
647 }
648
649 static void
650 gtk_ruler_real_draw_ticks (GtkRuler *ruler,
651                            cairo_t  *cr)
652 {
653   GtkWidget *widget = GTK_WIDGET (ruler);
654   GtkRulerPrivate *priv = ruler->priv;
655   GtkStyle *style;
656   gint i, j;
657   gint w, h;
658   gint width, height;
659   gint xthickness;
660   gint ythickness;
661   gint length, ideal_length;
662   gdouble lower, upper;         /* Upper and lower limits, in ruler units */
663   gdouble increment;            /* Number of pixels per unit */
664   gint scale;                   /* Number of units per major unit */
665   gdouble subd_incr;
666   gdouble start, end, cur;
667   gchar unit_str[32];
668   gint digit_height;
669   gint digit_offset;
670   gint text_width;
671   gint text_height;
672   gint pos;
673   PangoLayout *layout;
674   PangoRectangle logical_rect, ink_rect;
675
676   style = gtk_widget_get_style (widget);
677
678   xthickness = style->xthickness;
679   ythickness = style->ythickness;
680
681   layout = gtk_widget_create_pango_layout (widget, "012456789");
682   pango_layout_get_extents (layout, &ink_rect, &logical_rect);
683
684   digit_height = PANGO_PIXELS (ink_rect.height) + 2;
685   digit_offset = ink_rect.y;
686
687   w = gtk_widget_get_allocated_width (widget);
688   h = gtk_widget_get_allocated_height (widget);
689
690   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
691     {
692       width = w;
693       height = h - ythickness * 2;
694     }
695   else
696     {
697       width = h;
698       height = w - ythickness * 2;
699     }
700
701 #define DETAILE(private) (priv->orientation == GTK_ORIENTATION_HORIZONTAL ? "hruler" : "vruler");
702
703   gdk_cairo_set_source_color (cr, &style->fg[gtk_widget_get_state (widget)]);
704
705   gtk_paint_box (style, cr,
706                        GTK_STATE_NORMAL, GTK_SHADOW_OUT,
707                        widget,
708                        priv->orientation == GTK_ORIENTATION_HORIZONTAL ?
709                        "hruler" : "vruler",
710                        0, 0,
711                        w, h);
712
713   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
714     {
715       cairo_rectangle (cr,
716                        xthickness,
717                        height + ythickness,
718                        w - 2 * xthickness,
719                        1);
720     }
721   else
722     {
723       cairo_rectangle (cr,
724                        height + xthickness,
725                        ythickness,
726                        1,
727                        h - 2 * ythickness);
728     }
729
730   upper = priv->upper / priv->metric->pixels_per_unit;
731   lower = priv->lower / priv->metric->pixels_per_unit;
732
733   if ((upper - lower) == 0)
734     goto out;
735
736   increment = (gdouble) width / (upper - lower);
737
738   /* determine the scale H
739    *  We calculate the text size as for the vruler, so that the result
740    *  for the scale looks consistent with an accompanying vruler
741    */
742   /* determine the scale V
743    *   use the maximum extents of the ruler to determine the largest
744    *   possible number to be displayed.  Calculate the height in pixels
745    *   of this displayed text. Use this height to find a scale which
746    *   leaves sufficient room for drawing the ruler.
747    */
748   scale = ceil (priv->max_size / priv->metric->pixels_per_unit);
749   g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
750
751   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
752     {
753       text_width = strlen (unit_str) * digit_height + 1;
754
755       for (scale = 0; scale < MAXIMUM_SCALES; scale++)
756         if (priv->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
757           break;
758     }
759   else
760     {
761       text_height = strlen (unit_str) * digit_height + 1;
762
763       for (scale = 0; scale < MAXIMUM_SCALES; scale++)
764         if (priv->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
765           break;
766     }
767
768   if (scale == MAXIMUM_SCALES)
769     scale = MAXIMUM_SCALES - 1;
770
771   /* drawing starts here */
772   length = 0;
773   for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
774     {
775       subd_incr = (gdouble) priv->metric->ruler_scale[scale] /
776                   (gdouble) priv->metric->subdivide[i];
777       if (subd_incr * fabs(increment) <= MINIMUM_INCR)
778         continue;
779
780       /* Calculate the length of the tickmarks. Make sure that
781        * this length increases for each set of ticks
782        */
783       ideal_length = height / (i + 1) - 1;
784       if (ideal_length > ++length)
785         length = ideal_length;
786
787       if (lower < upper)
788         {
789           start = floor (lower / subd_incr) * subd_incr;
790           end   = ceil  (upper / subd_incr) * subd_incr;
791         }
792       else
793         {
794           start = floor (upper / subd_incr) * subd_incr;
795           end   = ceil  (lower / subd_incr) * subd_incr;
796         }
797
798       for (cur = start; cur <= end; cur += subd_incr)
799         {
800           pos = ROUND ((cur - lower) * increment);
801
802           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
803             {
804               cairo_rectangle (cr,
805                                pos, height + ythickness - length,
806                                1,   length);
807             }
808           else
809             {
810               cairo_rectangle (cr,
811                                height + xthickness - length, pos,
812                                length,                       1);
813             }
814           cairo_fill (cr);
815
816           /* draw label */
817           if (i == 0)
818             {
819               g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
820
821               if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
822                 {
823                   pango_layout_set_text (layout, unit_str, -1);
824                   pango_layout_get_extents (layout, &logical_rect, NULL);
825
826                   gtk_paint_layout (style,
827                                           cr,
828                                           gtk_widget_get_state (widget),
829                                           FALSE,
830                                           widget,
831                                           "hruler",
832                                           pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
833                                           layout);
834                 }
835               else
836                 {
837                   for (j = 0; j < (int) strlen (unit_str); j++)
838                     {
839                       pango_layout_set_text (layout, unit_str + j, 1);
840                       pango_layout_get_extents (layout, NULL, &logical_rect);
841
842                       gtk_paint_layout (style,
843                                               cr,
844                                               gtk_widget_get_state (widget),
845                                               FALSE,
846                                               widget,
847                                               "vruler",
848                                               xthickness + 1,
849                                               pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset),
850                                               layout);
851                     }
852                 }
853             }
854         }
855     }
856
857   cairo_fill (cr);
858
859 out:
860   g_object_unref (layout);
861 }
862
863 static void
864 gtk_ruler_real_draw_pos (GtkRuler *ruler,
865                          cairo_t  *cr)
866 {
867   GtkWidget *widget = GTK_WIDGET (ruler);
868   GtkRulerPrivate *priv = ruler->priv;
869   GtkStyle *style;
870   gint x, y, width, height;
871   gint bs_width, bs_height;
872   gint xthickness;
873   gint ythickness;
874   gdouble increment;
875
876   style = gtk_widget_get_style (widget);
877   xthickness = style->xthickness;
878   ythickness = style->ythickness;
879   width = gtk_widget_get_allocated_width (widget);
880   height = gtk_widget_get_allocated_height (widget);
881
882   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
883     {
884       height -= ythickness * 2;
885
886       bs_width = height / 2 + 2;
887       bs_width |= 1;  /* make sure it's odd */
888       bs_height = bs_width / 2 + 1;
889     }
890   else
891     {
892       width -= xthickness * 2;
893
894       bs_height = width / 2 + 2;
895       bs_height |= 1;  /* make sure it's odd */
896       bs_width = bs_height / 2 + 1;
897     }
898
899   if ((bs_width > 0) && (bs_height > 0))
900     {
901       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
902         {
903           increment = (gdouble) width / (priv->upper - priv->lower);
904
905           x = ROUND ((priv->position - priv->lower) * increment) + (xthickness - bs_width) / 2 - 1;
906           y = (height + bs_height) / 2 + ythickness;
907         }
908       else
909         {
910           increment = (gdouble) height / (priv->upper - priv->lower);
911
912           x = (width + bs_width) / 2 + xthickness;
913           y = ROUND ((priv->position - priv->lower) * increment) + (ythickness - bs_height) / 2 - 1;
914         }
915
916       gdk_cairo_set_source_color (cr, &style->fg[gtk_widget_get_state (widget)]);
917
918       cairo_move_to (cr, x, y);
919
920       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
921         {
922           cairo_line_to (cr, x + bs_width / 2.0, y + bs_height);
923           cairo_line_to (cr, x + bs_width,       y);
924         }
925       else
926         {
927           cairo_line_to (cr, x + bs_width, y + bs_height / 2.0);
928           cairo_line_to (cr, x,            y + bs_height);
929         }
930
931       cairo_fill (cr);
932
933       priv->xsrc = x;
934       priv->ysrc = y;
935     }
936 }