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