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