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