]> Pileus Git - ~andy/gtk/blob - gtk/gtkrange.c
range: Don't perform a GTK+ grab
[~andy/gtk] / gtk / gtkrange.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  * Copyright (C) 2001 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2004.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include "config.h"
29
30 #include <stdio.h>
31 #include <math.h>
32
33 #include "gtkmain.h"
34 #include "gtkmarshalers.h"
35 #include "gtkorientableprivate.h"
36 #include "gtkrange.h"
37 #include "gtkscale.h"
38 #include "gtkcolorscaleprivate.h"
39 #include "gtkscrollbar.h"
40 #include "gtkwindow.h"
41 #include "gtkprivate.h"
42 #include "gtkintl.h"
43 #include "gtkmain.h"
44 #include "gtktypebuiltins.h"
45 #include "a11y/gtkrangeaccessible.h"
46
47 /**
48  * SECTION:gtkrange
49  * @Short_description: Base class for widgets which visualize an adjustment
50  * @Title: GtkRange
51  *
52  * #GtkRange is the common base class for widgets which visualize an
53  * adjustment, e.g #GtkScale or #GtkScrollbar.
54  *
55  * Apart from signals for monitoring the parameters of the adjustment,
56  * #GtkRange provides properties and methods for influencing the sensitivity
57  * of the "steppers". It also provides properties and methods for setting a
58  * "fill level" on range widgets. See gtk_range_set_fill_level().
59  */
60
61
62 #define SCROLL_DELAY_FACTOR 5    /* Scroll repeat multiplier */
63 #define UPDATE_DELAY        300  /* Delay for queued update */
64
65 typedef struct _GtkRangeStepTimer GtkRangeStepTimer;
66
67 typedef enum {
68   MOUSE_OUTSIDE,
69   MOUSE_STEPPER_A,
70   MOUSE_STEPPER_B,
71   MOUSE_STEPPER_C,
72   MOUSE_STEPPER_D,
73   MOUSE_TROUGH,
74   MOUSE_SLIDER,
75   MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
76 } MouseLocation;
77
78 struct _GtkRangePrivate
79 {
80   MouseLocation      mouse_location;
81   /* last mouse coords we got, or -1 if mouse is outside the range */
82   gint  mouse_x;
83   gint  mouse_y;
84   MouseLocation      grab_location;   /* "grabbed" mouse location, OUTSIDE for no grab */
85
86   GtkRangeStepTimer *timer;
87
88   GtkAdjustment     *adjustment;
89   GtkSensitivityType lower_sensitivity;
90   GtkSensitivityType upper_sensitivity;
91
92   GdkDevice         *grab_device;
93   GdkRectangle       range_rect;     /* Area of entire stepper + trough assembly in widget->window coords */
94   /* These are in widget->window coordinates */
95   GdkRectangle       stepper_a;
96   GdkRectangle       stepper_b;
97   GdkRectangle       stepper_c;
98   GdkRectangle       stepper_d;
99   GdkRectangle       trough;         /* The trough rectangle is the area the thumb can slide in, not the entire range_rect */
100   GdkRectangle       slider;
101   GdkWindow         *event_window;
102
103   GQuark             slider_detail_quark;
104   GQuark             stepper_detail_quark[4];
105
106   GtkOrientation     orientation;
107
108   gdouble  fill_level;
109   gdouble *marks;
110
111   gint *mark_pos;
112   gint  min_slider_size;
113   gint  n_marks;
114   gint  round_digits;                /* Round off value to this many digits, -1 for no rounding */
115   gint  slide_initial_slider_position;
116   gint  slide_initial_coordinate;
117   gint  slider_start;                /* Slider range along the long dimension, in widget->window coords */
118   gint  slider_end;
119
120   guint repaint_id;
121
122   /* Steppers are: < > ---- < >
123    *               a b      c d
124    */
125   guint has_stepper_a          : 1;
126   guint has_stepper_b          : 1;
127   guint has_stepper_c          : 1;
128   guint has_stepper_d          : 1;
129
130   guint flippable              : 1;
131   guint inverted               : 1;
132   guint need_recalc            : 1;
133   guint recalc_marks           : 1;
134   guint slider_size_fixed      : 1;
135   guint trough_click_forward   : 1;  /* trough click was on the forward side of slider */
136
137   /* Stepper sensitivity */
138   guint lower_sensitive        : 1;
139   guint upper_sensitive        : 1;
140
141   /* The range has an origin, should be drawn differently. Used by GtkScale */
142   guint has_origin             : 1;
143
144   /* Fill level */
145   guint show_fill_level        : 1;
146   guint restrict_to_fill_level : 1;
147
148   guint grab_button            : 8; /* 0 if none */
149 };
150
151
152 enum {
153   PROP_0,
154   PROP_ORIENTATION,
155   PROP_ADJUSTMENT,
156   PROP_INVERTED,
157   PROP_LOWER_STEPPER_SENSITIVITY,
158   PROP_UPPER_STEPPER_SENSITIVITY,
159   PROP_SHOW_FILL_LEVEL,
160   PROP_RESTRICT_TO_FILL_LEVEL,
161   PROP_FILL_LEVEL,
162   PROP_ROUND_DIGITS
163 };
164
165 enum {
166   VALUE_CHANGED,
167   ADJUST_BOUNDS,
168   MOVE_SLIDER,
169   CHANGE_VALUE,
170   LAST_SIGNAL
171 };
172
173 typedef enum {
174   STEPPER_A,
175   STEPPER_B,
176   STEPPER_C,
177   STEPPER_D
178 } Stepper;
179
180 static void gtk_range_set_property   (GObject          *object,
181                                       guint             prop_id,
182                                       const GValue     *value,
183                                       GParamSpec       *pspec);
184 static void gtk_range_get_property   (GObject          *object,
185                                       guint             prop_id,
186                                       GValue           *value,
187                                       GParamSpec       *pspec);
188 static void gtk_range_destroy        (GtkWidget        *widget);
189 static void gtk_range_get_preferred_width
190                                      (GtkWidget        *widget,
191                                       gint             *minimum,
192                                       gint             *natural);
193 static void gtk_range_get_preferred_height
194                                      (GtkWidget        *widget,
195                                       gint             *minimum,
196                                       gint             *natural);
197 static void gtk_range_size_allocate  (GtkWidget        *widget,
198                                       GtkAllocation    *allocation);
199 static void gtk_range_hierarchy_changed (GtkWidget     *widget,
200                                          GtkWidget     *previous_toplevel);
201 static void gtk_range_realize        (GtkWidget        *widget);
202 static void gtk_range_unrealize      (GtkWidget        *widget);
203 static void gtk_range_map            (GtkWidget        *widget);
204 static void gtk_range_unmap          (GtkWidget        *widget);
205 static gboolean gtk_range_draw       (GtkWidget        *widget,
206                                       cairo_t          *cr);
207 static gboolean gtk_range_button_press   (GtkWidget        *widget,
208                                       GdkEventButton   *event);
209 static gboolean gtk_range_button_release (GtkWidget        *widget,
210                                       GdkEventButton   *event);
211 static gboolean gtk_range_motion_notify  (GtkWidget        *widget,
212                                       GdkEventMotion   *event);
213 static gboolean gtk_range_enter_notify   (GtkWidget        *widget,
214                                       GdkEventCrossing *event);
215 static gboolean gtk_range_leave_notify   (GtkWidget        *widget,
216                                       GdkEventCrossing *event);
217 static gboolean gtk_range_grab_broken (GtkWidget          *widget,
218                                        GdkEventGrabBroken *event);
219 static void gtk_range_grab_notify    (GtkWidget          *widget,
220                                       gboolean            was_grabbed);
221 static void gtk_range_state_flags_changed  (GtkWidget      *widget,
222                                             GtkStateFlags   previous_state);
223 static gboolean gtk_range_scroll_event   (GtkWidget        *widget,
224                                       GdkEventScroll   *event);
225 static void gtk_range_style_updated  (GtkWidget        *widget);
226 static void update_slider_position   (GtkRange         *range,
227                                       gint              mouse_x,
228                                       gint              mouse_y);
229 static void stop_scrolling           (GtkRange         *range);
230 static gboolean modify_allocation_for_window_grip (GtkWidget     *widget,
231                                                    GtkAllocation *allocation);
232
233 /* Range methods */
234
235 static void gtk_range_move_slider              (GtkRange         *range,
236                                                 GtkScrollType     scroll);
237
238 /* Internals */
239 static gboolean      gtk_range_scroll                   (GtkRange      *range,
240                                                          GtkScrollType  scroll);
241 static gboolean      gtk_range_update_mouse_location    (GtkRange      *range);
242 static void          gtk_range_calc_layout              (GtkRange      *range,
243                                                          gdouble        adjustment_value);
244 static void          gtk_range_calc_marks               (GtkRange      *range);
245 static void          gtk_range_get_props                (GtkRange      *range,
246                                                          gint          *slider_width,
247                                                          gint          *stepper_size,
248                                                          gint          *focus_width,
249                                                          gint          *trough_border,
250                                                          gint          *stepper_spacing,
251                                                          gboolean      *trough_under_steppers,
252                                                          gint          *arrow_displacement_x,
253                                                          gint          *arrow_displacement_y);
254 static void          gtk_range_calc_request             (GtkRange      *range,
255                                                          gint           slider_width,
256                                                          gint           stepper_size,
257                                                          gint           focus_width,
258                                                          gint           trough_border,
259                                                          gint           stepper_spacing,
260                                                          GdkRectangle  *range_rect,
261                                                          GtkBorder     *border,
262                                                          gint          *n_steppers_p,
263                                                          gboolean      *has_steppers_ab,
264                                                          gboolean      *has_steppers_cd,
265                                                          gint          *slider_length_p);
266 static void          gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
267                                                          gpointer       data);
268 static void          gtk_range_adjustment_changed       (GtkAdjustment *adjustment,
269                                                          gpointer       data);
270 static void          gtk_range_add_step_timer           (GtkRange      *range,
271                                                          GtkScrollType  step);
272 static void          gtk_range_remove_step_timer        (GtkRange      *range);
273 static GdkRectangle* get_area                           (GtkRange      *range,
274                                                          MouseLocation  location);
275 static gboolean      gtk_range_real_change_value        (GtkRange      *range,
276                                                          GtkScrollType  scroll,
277                                                          gdouble        value);
278 static gboolean      gtk_range_key_press                (GtkWidget     *range,
279                                                          GdkEventKey   *event);
280
281
282 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
283                                   G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
284                                                          NULL))
285
286 static guint signals[LAST_SIGNAL];
287
288
289 static void
290 gtk_range_class_init (GtkRangeClass *class)
291 {
292   GObjectClass   *gobject_class;
293   GtkWidgetClass *widget_class;
294
295   gobject_class = G_OBJECT_CLASS (class);
296   widget_class = (GtkWidgetClass*) class;
297
298   gobject_class->set_property = gtk_range_set_property;
299   gobject_class->get_property = gtk_range_get_property;
300
301   widget_class->destroy = gtk_range_destroy;
302   widget_class->get_preferred_width = gtk_range_get_preferred_width;
303   widget_class->get_preferred_height = gtk_range_get_preferred_height;
304   widget_class->size_allocate = gtk_range_size_allocate;
305   widget_class->hierarchy_changed = gtk_range_hierarchy_changed;
306   widget_class->realize = gtk_range_realize;
307   widget_class->unrealize = gtk_range_unrealize;
308   widget_class->map = gtk_range_map;
309   widget_class->unmap = gtk_range_unmap;
310   widget_class->draw = gtk_range_draw;
311   widget_class->button_press_event = gtk_range_button_press;
312   widget_class->button_release_event = gtk_range_button_release;
313   widget_class->motion_notify_event = gtk_range_motion_notify;
314   widget_class->scroll_event = gtk_range_scroll_event;
315   widget_class->enter_notify_event = gtk_range_enter_notify;
316   widget_class->leave_notify_event = gtk_range_leave_notify;
317   widget_class->grab_broken_event = gtk_range_grab_broken;
318   widget_class->grab_notify = gtk_range_grab_notify;
319   widget_class->state_flags_changed = gtk_range_state_flags_changed;
320   widget_class->style_updated = gtk_range_style_updated;
321   widget_class->key_press_event = gtk_range_key_press;
322
323   class->move_slider = gtk_range_move_slider;
324   class->change_value = gtk_range_real_change_value;
325
326   class->slider_detail = "slider";
327   class->stepper_detail = "stepper";
328
329   /**
330    * GtkRange::value-changed:
331    * @range: the #GtkRange that received the signal
332    *
333    * Emitted when the range value changes.
334    */
335   signals[VALUE_CHANGED] =
336     g_signal_new (I_("value-changed"),
337                   G_TYPE_FROM_CLASS (gobject_class),
338                   G_SIGNAL_RUN_LAST,
339                   G_STRUCT_OFFSET (GtkRangeClass, value_changed),
340                   NULL, NULL,
341                   _gtk_marshal_VOID__VOID,
342                   G_TYPE_NONE, 0);
343
344   /**
345    * GtkRange::adjust-bounds:
346    * @range: the #GtkRange that received the signal
347    * @value: the value before we clamp
348    *
349    * Emitted before clamping a value, to give the application a
350    * chance to adjust the bounds.
351    */
352   signals[ADJUST_BOUNDS] =
353     g_signal_new (I_("adjust-bounds"),
354                   G_TYPE_FROM_CLASS (gobject_class),
355                   G_SIGNAL_RUN_LAST,
356                   G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
357                   NULL, NULL,
358                   _gtk_marshal_VOID__DOUBLE,
359                   G_TYPE_NONE, 1,
360                   G_TYPE_DOUBLE);
361   
362   /**
363    * GtkRange::move-slider:
364    * @range: the #GtkRange that received the signal
365    * @step: how to move the slider
366    *
367    * Virtual function that moves the slider. Used for keybindings.
368    */
369   signals[MOVE_SLIDER] =
370     g_signal_new (I_("move-slider"),
371                   G_TYPE_FROM_CLASS (gobject_class),
372                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
373                   G_STRUCT_OFFSET (GtkRangeClass, move_slider),
374                   NULL, NULL,
375                   _gtk_marshal_VOID__ENUM,
376                   G_TYPE_NONE, 1,
377                   GTK_TYPE_SCROLL_TYPE);
378
379   /**
380    * GtkRange::change-value:
381    * @range: the #GtkRange that received the signal
382    * @scroll: the type of scroll action that was performed
383    * @value: the new value resulting from the scroll action
384    *
385    * The #GtkRange::change-value signal is emitted when a scroll action is
386    * performed on a range.  It allows an application to determine the
387    * type of scroll event that occurred and the resultant new value.
388    * The application can handle the event itself and return %TRUE to
389    * prevent further processing.  Or, by returning %FALSE, it can pass
390    * the event to other handlers until the default GTK+ handler is
391    * reached.
392    *
393    * The value parameter is unrounded.  An application that overrides
394    * the GtkRange::change-value signal is responsible for clamping the
395    * value to the desired number of decimal digits; the default GTK+
396    * handler clamps the value based on #GtkRange:round-digits.
397    *
398    * It is not possible to use delayed update policies in an overridden
399    * #GtkRange::change-value handler.
400    *
401    * Returns: %TRUE to prevent other handlers from being invoked for
402    *     the signal, %FALSE to propagate the signal further
403    *
404    * Since: 2.6
405    */
406   signals[CHANGE_VALUE] =
407     g_signal_new (I_("change-value"),
408                   G_TYPE_FROM_CLASS (gobject_class),
409                   G_SIGNAL_RUN_LAST,
410                   G_STRUCT_OFFSET (GtkRangeClass, change_value),
411                   _gtk_boolean_handled_accumulator, NULL,
412                   _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
413                   G_TYPE_BOOLEAN, 2,
414                   GTK_TYPE_SCROLL_TYPE,
415                   G_TYPE_DOUBLE);
416
417   g_object_class_override_property (gobject_class,
418                                     PROP_ORIENTATION,
419                                     "orientation");
420
421   g_object_class_install_property (gobject_class,
422                                    PROP_ADJUSTMENT,
423                                    g_param_spec_object ("adjustment",
424                                                         P_("Adjustment"),
425                                                         P_("The GtkAdjustment that contains the current value of this range object"),
426                                                         GTK_TYPE_ADJUSTMENT,
427                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
428
429   g_object_class_install_property (gobject_class,
430                                    PROP_INVERTED,
431                                    g_param_spec_boolean ("inverted",
432                                                         P_("Inverted"),
433                                                         P_("Invert direction slider moves to increase range value"),
434                                                          FALSE,
435                                                          GTK_PARAM_READWRITE));
436
437   g_object_class_install_property (gobject_class,
438                                    PROP_LOWER_STEPPER_SENSITIVITY,
439                                    g_param_spec_enum ("lower-stepper-sensitivity",
440                                                       P_("Lower stepper sensitivity"),
441                                                       P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
442                                                       GTK_TYPE_SENSITIVITY_TYPE,
443                                                       GTK_SENSITIVITY_AUTO,
444                                                       GTK_PARAM_READWRITE));
445
446   g_object_class_install_property (gobject_class,
447                                    PROP_UPPER_STEPPER_SENSITIVITY,
448                                    g_param_spec_enum ("upper-stepper-sensitivity",
449                                                       P_("Upper stepper sensitivity"),
450                                                       P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
451                                                       GTK_TYPE_SENSITIVITY_TYPE,
452                                                       GTK_SENSITIVITY_AUTO,
453                                                       GTK_PARAM_READWRITE));
454
455   /**
456    * GtkRange:show-fill-level:
457    *
458    * The show-fill-level property controls whether fill level indicator
459    * graphics are displayed on the trough. See
460    * gtk_range_set_show_fill_level().
461    *
462    * Since: 2.12
463    **/
464   g_object_class_install_property (gobject_class,
465                                    PROP_SHOW_FILL_LEVEL,
466                                    g_param_spec_boolean ("show-fill-level",
467                                                          P_("Show Fill Level"),
468                                                          P_("Whether to display a fill level indicator graphics on trough."),
469                                                          FALSE,
470                                                          GTK_PARAM_READWRITE));
471
472   /**
473    * GtkRange:restrict-to-fill-level:
474    *
475    * The restrict-to-fill-level property controls whether slider
476    * movement is restricted to an upper boundary set by the
477    * fill level. See gtk_range_set_restrict_to_fill_level().
478    *
479    * Since: 2.12
480    **/
481   g_object_class_install_property (gobject_class,
482                                    PROP_RESTRICT_TO_FILL_LEVEL,
483                                    g_param_spec_boolean ("restrict-to-fill-level",
484                                                          P_("Restrict to Fill Level"),
485                                                          P_("Whether to restrict the upper boundary to the fill level."),
486                                                          TRUE,
487                                                          GTK_PARAM_READWRITE));
488
489   /**
490    * GtkRange:fill-level:
491    *
492    * The fill level (e.g. prebuffering of a network stream).
493    * See gtk_range_set_fill_level().
494    *
495    * Since: 2.12
496    **/
497   g_object_class_install_property (gobject_class,
498                                    PROP_FILL_LEVEL,
499                                    g_param_spec_double ("fill-level",
500                                                         P_("Fill Level"),
501                                                         P_("The fill level."),
502                                                         -G_MAXDOUBLE,
503                                                         G_MAXDOUBLE,
504                                                         G_MAXDOUBLE,
505                                                         GTK_PARAM_READWRITE));
506
507   /**
508    * GtkRange:round-digits:
509    *
510    * The number of digits to round the value to when
511    * it changes, or -1. See #GtkRange::change-value.
512    *
513    * Since: 2.24
514    */
515   g_object_class_install_property (gobject_class,
516                                    PROP_ROUND_DIGITS,
517                                    g_param_spec_int ("round-digits",
518                                                      P_("Round Digits"),
519                                                      P_("The number of digits to round the value to."),
520                                                      -1,
521                                                      G_MAXINT,
522                                                      -1,
523                                                      GTK_PARAM_READWRITE));
524
525   gtk_widget_class_install_style_property (widget_class,
526                                            g_param_spec_int ("slider-width",
527                                                              P_("Slider Width"),
528                                                              P_("Width of scrollbar or scale thumb"),
529                                                              0,
530                                                              G_MAXINT,
531                                                              14,
532                                                              GTK_PARAM_READABLE));
533   gtk_widget_class_install_style_property (widget_class,
534                                            g_param_spec_int ("trough-border",
535                                                              P_("Trough Border"),
536                                                              P_("Spacing between thumb/steppers and outer trough bevel"),
537                                                              0,
538                                                              G_MAXINT,
539                                                              1,
540                                                              GTK_PARAM_READABLE));
541   gtk_widget_class_install_style_property (widget_class,
542                                            g_param_spec_int ("stepper-size",
543                                                              P_("Stepper Size"),
544                                                              P_("Length of step buttons at ends"),
545                                                              0,
546                                                              G_MAXINT,
547                                                              14,
548                                                              GTK_PARAM_READABLE));
549   /**
550    * GtkRange:stepper-spacing:
551    *
552    * The spacing between the stepper buttons and thumb. Note that
553    * stepper-spacing won't have any effect if there are no steppers.
554    */
555   gtk_widget_class_install_style_property (widget_class,
556                                            g_param_spec_int ("stepper-spacing",
557                                                              P_("Stepper Spacing"),
558                                                              P_("Spacing between step buttons and thumb"),
559                                                              0,
560                                                              G_MAXINT,
561                                                              0,
562                                                              GTK_PARAM_READABLE));
563   gtk_widget_class_install_style_property (widget_class,
564                                            g_param_spec_int ("arrow-displacement-x",
565                                                              P_("Arrow X Displacement"),
566                                                              P_("How far in the x direction to move the arrow when the button is depressed"),
567                                                              G_MININT,
568                                                              G_MAXINT,
569                                                              0,
570                                                              GTK_PARAM_READABLE));
571   gtk_widget_class_install_style_property (widget_class,
572                                            g_param_spec_int ("arrow-displacement-y",
573                                                              P_("Arrow Y Displacement"),
574                                                              P_("How far in the y direction to move the arrow when the button is depressed"),
575                                                              G_MININT,
576                                                              G_MAXINT,
577                                                              0,
578                                                              GTK_PARAM_READABLE));
579
580   /**
581    * GtkRange:trough-under-steppers:
582    *
583    * Whether to draw the trough across the full length of the range or
584    * to exclude the steppers and their spacing.
585    *
586    * Since: 2.10
587    */
588   gtk_widget_class_install_style_property (widget_class,
589                                            g_param_spec_boolean ("trough-under-steppers",
590                                                                  P_("Trough Under Steppers"),
591                                                                  P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
592                                                                  TRUE,
593                                                                  GTK_PARAM_READABLE));
594
595   /**
596    * GtkRange:arrow-scaling:
597    *
598    * The arrow size proportion relative to the scroll button size.
599    *
600    * Since: 2.14
601    */
602   gtk_widget_class_install_style_property (widget_class,
603                                            g_param_spec_float ("arrow-scaling",
604                                                                P_("Arrow scaling"),
605                                                                P_("Arrow scaling with regard to scroll button size"),
606                                                                0.0, 1.0, 0.5,
607                                                                GTK_PARAM_READABLE));
608
609   g_type_class_add_private (class, sizeof (GtkRangePrivate));
610
611   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_RANGE_ACCESSIBLE);
612 }
613
614 static void
615 gtk_range_set_property (GObject      *object,
616                         guint         prop_id,
617                         const GValue *value,
618                         GParamSpec   *pspec)
619 {
620   GtkRange *range = GTK_RANGE (object);
621   GtkRangePrivate *priv = range->priv;
622
623   switch (prop_id)
624     {
625     case PROP_ORIENTATION:
626       priv->orientation = g_value_get_enum (value);
627
628       priv->slider_detail_quark = 0;
629       priv->stepper_detail_quark[0] = 0;
630       priv->stepper_detail_quark[1] = 0;
631       priv->stepper_detail_quark[2] = 0;
632       priv->stepper_detail_quark[3] = 0;
633
634       _gtk_orientable_set_style_classes (GTK_ORIENTABLE (range));
635       gtk_widget_queue_resize (GTK_WIDGET (range));
636       break;
637     case PROP_ADJUSTMENT:
638       gtk_range_set_adjustment (range, g_value_get_object (value));
639       break;
640     case PROP_INVERTED:
641       gtk_range_set_inverted (range, g_value_get_boolean (value));
642       break;
643     case PROP_LOWER_STEPPER_SENSITIVITY:
644       gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
645       break;
646     case PROP_UPPER_STEPPER_SENSITIVITY:
647       gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
648       break;
649     case PROP_SHOW_FILL_LEVEL:
650       gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
651       break;
652     case PROP_RESTRICT_TO_FILL_LEVEL:
653       gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
654       break;
655     case PROP_FILL_LEVEL:
656       gtk_range_set_fill_level (range, g_value_get_double (value));
657       break;
658     case PROP_ROUND_DIGITS:
659       gtk_range_set_round_digits (range, g_value_get_int (value));
660       break;
661     default:
662       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
663       break;
664     }
665 }
666
667 static void
668 gtk_range_get_property (GObject      *object,
669                         guint         prop_id,
670                         GValue       *value,
671                         GParamSpec   *pspec)
672 {
673   GtkRange *range = GTK_RANGE (object);
674   GtkRangePrivate *priv = range->priv;
675
676   switch (prop_id)
677     {
678     case PROP_ORIENTATION:
679       g_value_set_enum (value, priv->orientation);
680       break;
681     case PROP_ADJUSTMENT:
682       g_value_set_object (value, priv->adjustment);
683       break;
684     case PROP_INVERTED:
685       g_value_set_boolean (value, priv->inverted);
686       break;
687     case PROP_LOWER_STEPPER_SENSITIVITY:
688       g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
689       break;
690     case PROP_UPPER_STEPPER_SENSITIVITY:
691       g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
692       break;
693     case PROP_SHOW_FILL_LEVEL:
694       g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
695       break;
696     case PROP_RESTRICT_TO_FILL_LEVEL:
697       g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
698       break;
699     case PROP_FILL_LEVEL:
700       g_value_set_double (value, gtk_range_get_fill_level (range));
701       break;
702     case PROP_ROUND_DIGITS:
703       g_value_set_int (value, gtk_range_get_round_digits (range));
704       break;
705     default:
706       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
707       break;
708     }
709 }
710
711 static void
712 gtk_range_init (GtkRange *range)
713 {
714   GtkRangePrivate *priv;
715
716   range->priv = G_TYPE_INSTANCE_GET_PRIVATE (range,
717                                              GTK_TYPE_RANGE,
718                                              GtkRangePrivate);
719   priv = range->priv;
720
721   gtk_widget_set_has_window (GTK_WIDGET (range), FALSE);
722
723   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
724   priv->adjustment = NULL;
725   priv->inverted = FALSE;
726   priv->flippable = FALSE;
727   priv->min_slider_size = 1;
728   priv->has_stepper_a = FALSE;
729   priv->has_stepper_b = FALSE;
730   priv->has_stepper_c = FALSE;
731   priv->has_stepper_d = FALSE;
732   priv->need_recalc = TRUE;
733   priv->round_digits = -1;
734   priv->mouse_location = MOUSE_OUTSIDE;
735   priv->mouse_x = -1;
736   priv->mouse_y = -1;
737   priv->grab_location = MOUSE_OUTSIDE;
738   priv->grab_button = 0;
739   priv->lower_sensitivity = GTK_SENSITIVITY_AUTO;
740   priv->upper_sensitivity = GTK_SENSITIVITY_AUTO;
741   priv->lower_sensitive = TRUE;
742   priv->upper_sensitive = TRUE;
743   priv->has_origin = FALSE;
744   priv->show_fill_level = FALSE;
745   priv->restrict_to_fill_level = TRUE;
746   priv->fill_level = G_MAXDOUBLE;
747   priv->timer = NULL;
748 }
749
750 /**
751  * gtk_range_get_adjustment:
752  * @range: a #GtkRange
753  * 
754  * Get the #GtkAdjustment which is the "model" object for #GtkRange.
755  * See gtk_range_set_adjustment() for details.
756  * The return value does not have a reference added, so should not
757  * be unreferenced.
758  * 
759  * Return value: (transfer none): a #GtkAdjustment
760  **/
761 GtkAdjustment*
762 gtk_range_get_adjustment (GtkRange *range)
763 {
764   GtkRangePrivate *priv;
765
766   g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
767
768   priv = range->priv;
769
770   if (!priv->adjustment)
771     gtk_range_set_adjustment (range, NULL);
772
773   return priv->adjustment;
774 }
775
776 /**
777  * gtk_range_set_adjustment:
778  * @range: a #GtkRange
779  * @adjustment: a #GtkAdjustment
780  *
781  * Sets the adjustment to be used as the "model" object for this range
782  * widget. The adjustment indicates the current range value, the
783  * minimum and maximum range values, the step/page increments used
784  * for keybindings and scrolling, and the page size. The page size
785  * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
786  * indicates the size of the visible area of the widget being scrolled.
787  * The page size affects the size of the scrollbar slider.
788  **/
789 void
790 gtk_range_set_adjustment (GtkRange      *range,
791                           GtkAdjustment *adjustment)
792 {
793   GtkRangePrivate *priv;
794
795   g_return_if_fail (GTK_IS_RANGE (range));
796
797   priv = range->priv;
798
799   if (!adjustment)
800     adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
801   else
802     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
803
804   if (priv->adjustment != adjustment)
805     {
806       if (priv->adjustment)
807         {
808           g_signal_handlers_disconnect_by_func (priv->adjustment,
809                                                 gtk_range_adjustment_changed,
810                                                 range);
811           g_signal_handlers_disconnect_by_func (priv->adjustment,
812                                                 gtk_range_adjustment_value_changed,
813                                                 range);
814           g_object_unref (priv->adjustment);
815         }
816
817       priv->adjustment = adjustment;
818       g_object_ref_sink (adjustment);
819       
820       g_signal_connect (adjustment, "changed",
821                         G_CALLBACK (gtk_range_adjustment_changed),
822                         range);
823       g_signal_connect (adjustment, "value-changed",
824                         G_CALLBACK (gtk_range_adjustment_value_changed),
825                         range);
826       
827       gtk_range_adjustment_changed (adjustment, range);
828       g_object_notify (G_OBJECT (range), "adjustment");
829     }
830 }
831
832 /**
833  * gtk_range_set_inverted:
834  * @range: a #GtkRange
835  * @setting: %TRUE to invert the range
836  *
837  * Ranges normally move from lower to higher values as the
838  * slider moves from top to bottom or left to right. Inverted
839  * ranges have higher values at the top or on the right rather than
840  * on the bottom or left.
841  **/
842 void
843 gtk_range_set_inverted (GtkRange *range,
844                         gboolean  setting)
845 {
846   GtkRangePrivate *priv;
847
848   g_return_if_fail (GTK_IS_RANGE (range));
849
850   priv = range->priv;
851
852   setting = setting != FALSE;
853
854   if (setting != priv->inverted)
855     {
856       priv->inverted = setting;
857       g_object_notify (G_OBJECT (range), "inverted");
858       gtk_widget_queue_resize (GTK_WIDGET (range));
859     }
860 }
861
862 /**
863  * gtk_range_get_inverted:
864  * @range: a #GtkRange
865  * 
866  * Gets the value set by gtk_range_set_inverted().
867  * 
868  * Return value: %TRUE if the range is inverted
869  **/
870 gboolean
871 gtk_range_get_inverted (GtkRange *range)
872 {
873   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
874
875   return range->priv->inverted;
876 }
877
878 /**
879  * gtk_range_set_flippable:
880  * @range: a #GtkRange
881  * @flippable: %TRUE to make the range flippable
882  *
883  * If a range is flippable, it will switch its direction if it is
884  * horizontal and its direction is %GTK_TEXT_DIR_RTL.
885  *
886  * See gtk_widget_get_direction().
887  *
888  * Since: 2.18
889  **/
890 void
891 gtk_range_set_flippable (GtkRange *range,
892                          gboolean  flippable)
893 {
894   GtkRangePrivate *priv;
895
896   g_return_if_fail (GTK_IS_RANGE (range));
897
898   priv = range->priv;
899
900   flippable = flippable ? TRUE : FALSE;
901
902   if (flippable != priv->flippable)
903     {
904       priv->flippable = flippable;
905
906       gtk_widget_queue_draw (GTK_WIDGET (range));
907     }
908 }
909
910 /**
911  * gtk_range_get_flippable:
912  * @range: a #GtkRange
913  *
914  * Gets the value set by gtk_range_set_flippable().
915  *
916  * Return value: %TRUE if the range is flippable
917  *
918  * Since: 2.18
919  **/
920 gboolean
921 gtk_range_get_flippable (GtkRange *range)
922 {
923   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
924
925   return range->priv->flippable;
926 }
927
928 /**
929  * gtk_range_set_slider_size_fixed:
930  * @range: a #GtkRange
931  * @size_fixed: %TRUE to make the slider size constant
932  *
933  * Sets whether the range's slider has a fixed size, or a size that
934  * depends on its adjustment's page size.
935  *
936  * This function is useful mainly for #GtkRange subclasses.
937  *
938  * Since: 2.20
939  **/
940 void
941 gtk_range_set_slider_size_fixed (GtkRange *range,
942                                  gboolean  size_fixed)
943 {
944   GtkRangePrivate *priv;
945
946   g_return_if_fail (GTK_IS_RANGE (range));
947
948   priv = range->priv;
949
950   if (size_fixed != priv->slider_size_fixed)
951     {
952       priv->slider_size_fixed = size_fixed ? TRUE : FALSE;
953
954       if (priv->adjustment && gtk_widget_get_mapped (GTK_WIDGET (range)))
955         {
956           priv->need_recalc = TRUE;
957           gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
958           gtk_widget_queue_draw (GTK_WIDGET (range));
959         }
960     }
961 }
962
963 /**
964  * gtk_range_get_slider_size_fixed:
965  * @range: a #GtkRange
966  *
967  * This function is useful mainly for #GtkRange subclasses.
968  *
969  * See gtk_range_set_slider_size_fixed().
970  *
971  * Return value: whether the range's slider has a fixed size.
972  *
973  * Since: 2.20
974  **/
975 gboolean
976 gtk_range_get_slider_size_fixed (GtkRange *range)
977 {
978   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
979
980   return range->priv->slider_size_fixed;
981 }
982
983 /**
984  * gtk_range_set_min_slider_size:
985  * @range: a #GtkRange
986  * @min_size: The slider's minimum size
987  *
988  * Sets the minimum size of the range's slider.
989  *
990  * This function is useful mainly for #GtkRange subclasses.
991  *
992  * Since: 2.20
993  **/
994 void
995 gtk_range_set_min_slider_size (GtkRange *range,
996                                gint      min_size)
997 {
998   GtkRangePrivate *priv;
999
1000   g_return_if_fail (GTK_IS_RANGE (range));
1001   g_return_if_fail (min_size > 0);
1002
1003   priv = range->priv;
1004
1005   if (min_size != priv->min_slider_size)
1006     {
1007       priv->min_slider_size = min_size;
1008
1009       if (gtk_widget_is_drawable (GTK_WIDGET (range)))
1010         {
1011           priv->need_recalc = TRUE;
1012           gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
1013           gtk_widget_queue_draw (GTK_WIDGET (range));
1014         }
1015     }
1016 }
1017
1018 /**
1019  * gtk_range_get_min_slider_size:
1020  * @range: a #GtkRange
1021  *
1022  * This function is useful mainly for #GtkRange subclasses.
1023  *
1024  * See gtk_range_set_min_slider_size().
1025  *
1026  * Return value: The minimum size of the range's slider.
1027  *
1028  * Since: 2.20
1029  **/
1030 gint
1031 gtk_range_get_min_slider_size (GtkRange *range)
1032 {
1033   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1034
1035   return range->priv->min_slider_size;
1036 }
1037
1038 /**
1039  * gtk_range_get_range_rect:
1040  * @range: a #GtkRange
1041  * @range_rect: (out): return location for the range rectangle
1042  *
1043  * This function returns the area that contains the range's trough
1044  * and its steppers, in widget->window coordinates.
1045  *
1046  * This function is useful mainly for #GtkRange subclasses.
1047  *
1048  * Since: 2.20
1049  **/
1050 void
1051 gtk_range_get_range_rect (GtkRange     *range,
1052                           GdkRectangle *range_rect)
1053 {
1054   GtkRangePrivate *priv;
1055
1056   g_return_if_fail (GTK_IS_RANGE (range));
1057   g_return_if_fail (range_rect != NULL);
1058
1059   priv = range->priv;
1060
1061   gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
1062
1063   *range_rect = priv->range_rect;
1064 }
1065
1066 /**
1067  * gtk_range_get_slider_range:
1068  * @range: a #GtkRange
1069  * @slider_start: (out) (allow-none): return location for the slider's
1070  *     start, or %NULL
1071  * @slider_end: (out) (allow-none): return location for the slider's
1072  *     end, or %NULL
1073  *
1074  * This function returns sliders range along the long dimension,
1075  * in widget->window coordinates.
1076  *
1077  * This function is useful mainly for #GtkRange subclasses.
1078  *
1079  * Since: 2.20
1080  **/
1081 void
1082 gtk_range_get_slider_range (GtkRange *range,
1083                             gint     *slider_start,
1084                             gint     *slider_end)
1085 {
1086   GtkRangePrivate *priv;
1087
1088   g_return_if_fail (GTK_IS_RANGE (range));
1089
1090   priv = range->priv;
1091
1092   gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
1093
1094   if (slider_start)
1095     *slider_start = priv->slider_start;
1096
1097   if (slider_end)
1098     *slider_end = priv->slider_end;
1099 }
1100
1101 /**
1102  * gtk_range_set_lower_stepper_sensitivity:
1103  * @range:       a #GtkRange
1104  * @sensitivity: the lower stepper's sensitivity policy.
1105  *
1106  * Sets the sensitivity policy for the stepper that points to the
1107  * 'lower' end of the GtkRange's adjustment.
1108  *
1109  * Since: 2.10
1110  **/
1111 void
1112 gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
1113                                          GtkSensitivityType  sensitivity)
1114 {
1115   GtkRangePrivate *priv;
1116
1117   g_return_if_fail (GTK_IS_RANGE (range));
1118
1119   priv = range->priv;
1120
1121   if (priv->lower_sensitivity != sensitivity)
1122     {
1123       priv->lower_sensitivity = sensitivity;
1124
1125       priv->need_recalc = TRUE;
1126       gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
1127       gtk_widget_queue_draw (GTK_WIDGET (range));
1128
1129       g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
1130     }
1131 }
1132
1133 /**
1134  * gtk_range_get_lower_stepper_sensitivity:
1135  * @range: a #GtkRange
1136  *
1137  * Gets the sensitivity policy for the stepper that points to the
1138  * 'lower' end of the GtkRange's adjustment.
1139  *
1140  * Return value: The lower stepper's sensitivity policy.
1141  *
1142  * Since: 2.10
1143  **/
1144 GtkSensitivityType
1145 gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
1146 {
1147   g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1148
1149   return range->priv->lower_sensitivity;
1150 }
1151
1152 /**
1153  * gtk_range_set_upper_stepper_sensitivity:
1154  * @range:       a #GtkRange
1155  * @sensitivity: the upper stepper's sensitivity policy.
1156  *
1157  * Sets the sensitivity policy for the stepper that points to the
1158  * 'upper' end of the GtkRange's adjustment.
1159  *
1160  * Since: 2.10
1161  **/
1162 void
1163 gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
1164                                          GtkSensitivityType  sensitivity)
1165 {
1166   GtkRangePrivate *priv;
1167
1168   g_return_if_fail (GTK_IS_RANGE (range));
1169
1170   priv = range->priv;
1171
1172   if (priv->upper_sensitivity != sensitivity)
1173     {
1174       priv->upper_sensitivity = sensitivity;
1175
1176       priv->need_recalc = TRUE;
1177       gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
1178       gtk_widget_queue_draw (GTK_WIDGET (range));
1179
1180       g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
1181     }
1182 }
1183
1184 /**
1185  * gtk_range_get_upper_stepper_sensitivity:
1186  * @range: a #GtkRange
1187  *
1188  * Gets the sensitivity policy for the stepper that points to the
1189  * 'upper' end of the GtkRange's adjustment.
1190  *
1191  * Return value: The upper stepper's sensitivity policy.
1192  *
1193  * Since: 2.10
1194  **/
1195 GtkSensitivityType
1196 gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
1197 {
1198   g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1199
1200   return range->priv->upper_sensitivity;
1201 }
1202
1203 /**
1204  * gtk_range_set_increments:
1205  * @range: a #GtkRange
1206  * @step: step size
1207  * @page: page size
1208  *
1209  * Sets the step and page sizes for the range.
1210  * The step size is used when the user clicks the #GtkScrollbar
1211  * arrows or moves #GtkScale via arrow keys. The page size
1212  * is used for example when moving via Page Up or Page Down keys.
1213  **/
1214 void
1215 gtk_range_set_increments (GtkRange *range,
1216                           gdouble   step,
1217                           gdouble   page)
1218 {
1219   GtkAdjustment *adjustment;
1220
1221   g_return_if_fail (GTK_IS_RANGE (range));
1222
1223   adjustment = range->priv->adjustment;
1224
1225   gtk_adjustment_configure (adjustment,
1226                             gtk_adjustment_get_value (adjustment),
1227                             gtk_adjustment_get_lower (adjustment),
1228                             gtk_adjustment_get_upper (adjustment),
1229                             step,
1230                             page,
1231                             gtk_adjustment_get_page_size (adjustment));
1232 }
1233
1234 /**
1235  * gtk_range_set_range:
1236  * @range: a #GtkRange
1237  * @min: minimum range value
1238  * @max: maximum range value
1239  * 
1240  * Sets the allowable values in the #GtkRange, and clamps the range
1241  * value to be between @min and @max. (If the range has a non-zero
1242  * page size, it is clamped between @min and @max - page-size.)
1243  **/
1244 void
1245 gtk_range_set_range (GtkRange *range,
1246                      gdouble   min,
1247                      gdouble   max)
1248 {
1249   GtkRangePrivate *priv;
1250   GtkAdjustment *adjustment;
1251   gdouble value;
1252   
1253   g_return_if_fail (GTK_IS_RANGE (range));
1254   g_return_if_fail (min <= max);
1255
1256   priv = range->priv;
1257   adjustment = priv->adjustment;
1258
1259   value = gtk_adjustment_get_value (adjustment);
1260   if (priv->restrict_to_fill_level)
1261     value = MIN (value, MAX (gtk_adjustment_get_lower (adjustment),
1262                              priv->fill_level));
1263
1264   gtk_adjustment_configure (adjustment,
1265                             value,
1266                             min,
1267                             max,
1268                             gtk_adjustment_get_step_increment (adjustment),
1269                             gtk_adjustment_get_page_increment (adjustment),
1270                             gtk_adjustment_get_page_size (adjustment));
1271 }
1272
1273 /**
1274  * gtk_range_set_value:
1275  * @range: a #GtkRange
1276  * @value: new value of the range
1277  *
1278  * Sets the current value of the range; if the value is outside the
1279  * minimum or maximum range values, it will be clamped to fit inside
1280  * them. The range emits the #GtkRange::value-changed signal if the 
1281  * value changes.
1282  **/
1283 void
1284 gtk_range_set_value (GtkRange *range,
1285                      gdouble   value)
1286 {
1287   GtkRangePrivate *priv;
1288
1289   g_return_if_fail (GTK_IS_RANGE (range));
1290
1291   priv = range->priv;
1292
1293   if (priv->restrict_to_fill_level)
1294     value = MIN (value, MAX (gtk_adjustment_get_lower (priv->adjustment),
1295                              priv->fill_level));
1296
1297   gtk_adjustment_set_value (priv->adjustment, value);
1298 }
1299
1300 /**
1301  * gtk_range_get_value:
1302  * @range: a #GtkRange
1303  * 
1304  * Gets the current value of the range.
1305  * 
1306  * Return value: current value of the range.
1307  **/
1308 gdouble
1309 gtk_range_get_value (GtkRange *range)
1310 {
1311   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1312
1313   return gtk_adjustment_get_value (range->priv->adjustment);
1314 }
1315
1316 /**
1317  * gtk_range_set_show_fill_level:
1318  * @range:           A #GtkRange
1319  * @show_fill_level: Whether a fill level indicator graphics is shown.
1320  *
1321  * Sets whether a graphical fill level is show on the trough. See
1322  * gtk_range_set_fill_level() for a general description of the fill
1323  * level concept.
1324  *
1325  * Since: 2.12
1326  **/
1327 void
1328 gtk_range_set_show_fill_level (GtkRange *range,
1329                                gboolean  show_fill_level)
1330 {
1331   GtkRangePrivate *priv;
1332
1333   g_return_if_fail (GTK_IS_RANGE (range));
1334
1335   priv = range->priv;
1336
1337   show_fill_level = show_fill_level ? TRUE : FALSE;
1338
1339   if (show_fill_level != priv->show_fill_level)
1340     {
1341       priv->show_fill_level = show_fill_level;
1342       g_object_notify (G_OBJECT (range), "show-fill-level");
1343       gtk_widget_queue_draw (GTK_WIDGET (range));
1344     }
1345 }
1346
1347 /**
1348  * gtk_range_get_show_fill_level:
1349  * @range: A #GtkRange
1350  *
1351  * Gets whether the range displays the fill level graphically.
1352  *
1353  * Return value: %TRUE if @range shows the fill level.
1354  *
1355  * Since: 2.12
1356  **/
1357 gboolean
1358 gtk_range_get_show_fill_level (GtkRange *range)
1359 {
1360   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1361
1362   return range->priv->show_fill_level;
1363 }
1364
1365 /**
1366  * gtk_range_set_restrict_to_fill_level:
1367  * @range:                  A #GtkRange
1368  * @restrict_to_fill_level: Whether the fill level restricts slider movement.
1369  *
1370  * Sets whether the slider is restricted to the fill level. See
1371  * gtk_range_set_fill_level() for a general description of the fill
1372  * level concept.
1373  *
1374  * Since: 2.12
1375  **/
1376 void
1377 gtk_range_set_restrict_to_fill_level (GtkRange *range,
1378                                       gboolean  restrict_to_fill_level)
1379 {
1380   GtkRangePrivate *priv;
1381
1382   g_return_if_fail (GTK_IS_RANGE (range));
1383
1384   priv = range->priv;
1385
1386   restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
1387
1388   if (restrict_to_fill_level != priv->restrict_to_fill_level)
1389     {
1390       priv->restrict_to_fill_level = restrict_to_fill_level;
1391       g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
1392
1393       gtk_range_set_value (range, gtk_range_get_value (range));
1394     }
1395 }
1396
1397 /**
1398  * gtk_range_get_restrict_to_fill_level:
1399  * @range: A #GtkRange
1400  *
1401  * Gets whether the range is restricted to the fill level.
1402  *
1403  * Return value: %TRUE if @range is restricted to the fill level.
1404  *
1405  * Since: 2.12
1406  **/
1407 gboolean
1408 gtk_range_get_restrict_to_fill_level (GtkRange *range)
1409 {
1410   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1411
1412   return range->priv->restrict_to_fill_level;
1413 }
1414
1415 /**
1416  * gtk_range_set_fill_level:
1417  * @range: a #GtkRange
1418  * @fill_level: the new position of the fill level indicator
1419  *
1420  * Set the new position of the fill level indicator.
1421  *
1422  * The "fill level" is probably best described by its most prominent
1423  * use case, which is an indicator for the amount of pre-buffering in
1424  * a streaming media player. In that use case, the value of the range
1425  * would indicate the current play position, and the fill level would
1426  * be the position up to which the file/stream has been downloaded.
1427  *
1428  * This amount of prebuffering can be displayed on the range's trough
1429  * and is themeable separately from the trough. To enable fill level
1430  * display, use gtk_range_set_show_fill_level(). The range defaults
1431  * to not showing the fill level.
1432  *
1433  * Additionally, it's possible to restrict the range's slider position
1434  * to values which are smaller than the fill level. This is controller
1435  * by gtk_range_set_restrict_to_fill_level() and is by default
1436  * enabled.
1437  *
1438  * Since: 2.12
1439  **/
1440 void
1441 gtk_range_set_fill_level (GtkRange *range,
1442                           gdouble   fill_level)
1443 {
1444   GtkRangePrivate *priv;
1445
1446   g_return_if_fail (GTK_IS_RANGE (range));
1447
1448   priv = range->priv;
1449
1450   if (fill_level != priv->fill_level)
1451     {
1452       priv->fill_level = fill_level;
1453       g_object_notify (G_OBJECT (range), "fill-level");
1454
1455       if (priv->show_fill_level)
1456         gtk_widget_queue_draw (GTK_WIDGET (range));
1457
1458       if (priv->restrict_to_fill_level)
1459         gtk_range_set_value (range, gtk_range_get_value (range));
1460     }
1461 }
1462
1463 /**
1464  * gtk_range_get_fill_level:
1465  * @range: A #GtkRange
1466  *
1467  * Gets the current position of the fill level indicator.
1468  *
1469  * Return value: The current fill level
1470  *
1471  * Since: 2.12
1472  **/
1473 gdouble
1474 gtk_range_get_fill_level (GtkRange *range)
1475 {
1476   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1477
1478   return range->priv->fill_level;
1479 }
1480
1481 static gboolean
1482 should_invert (GtkRange *range)
1483 {
1484   GtkRangePrivate *priv = range->priv;
1485
1486   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1487     return
1488       (priv->inverted && !priv->flippable) ||
1489       (priv->inverted && priv->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
1490       (!priv->inverted && priv->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1491   else
1492     return priv->inverted;
1493 }
1494
1495 static void
1496 gtk_range_destroy (GtkWidget *widget)
1497 {
1498   GtkRange *range = GTK_RANGE (widget);
1499   GtkRangePrivate *priv = range->priv;
1500
1501   gtk_range_remove_step_timer (range);
1502
1503   if (priv->repaint_id)
1504     g_source_remove (priv->repaint_id);
1505   priv->repaint_id = 0;
1506
1507   if (priv->adjustment)
1508     {
1509       g_signal_handlers_disconnect_by_func (priv->adjustment,
1510                                             gtk_range_adjustment_changed,
1511                                             range);
1512       g_signal_handlers_disconnect_by_func (priv->adjustment,
1513                                             gtk_range_adjustment_value_changed,
1514                                             range);
1515       g_object_unref (priv->adjustment);
1516       priv->adjustment = NULL;
1517     }
1518
1519   if (priv->n_marks)
1520     {
1521       g_free (priv->marks);
1522       priv->marks = NULL;
1523       g_free (priv->mark_pos);
1524       priv->mark_pos = NULL;
1525       priv->n_marks = 0;
1526     }
1527
1528   GTK_WIDGET_CLASS (gtk_range_parent_class)->destroy (widget);
1529 }
1530
1531 static void
1532 gtk_range_get_preferred_width (GtkWidget *widget,
1533                                gint      *minimum,
1534                                gint      *natural)
1535 {
1536   GtkRange *range;
1537   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1538   GdkRectangle range_rect;
1539   GtkBorder border;
1540
1541   range = GTK_RANGE (widget);
1542
1543   gtk_range_get_props (range,
1544                        &slider_width, &stepper_size,
1545                        &focus_width, &trough_border,
1546                        &stepper_spacing, NULL,
1547                        NULL, NULL);
1548
1549   gtk_range_calc_request (range,
1550                           slider_width, stepper_size,
1551                           focus_width, trough_border, stepper_spacing,
1552                           &range_rect, &border, NULL, NULL, NULL, NULL);
1553
1554   *minimum = *natural = range_rect.width + border.left + border.right;
1555 }
1556
1557 static void
1558 gtk_range_get_preferred_height (GtkWidget *widget,
1559                                 gint      *minimum,
1560                                 gint      *natural)
1561 {
1562   GtkRange *range;
1563   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1564   GdkRectangle range_rect;
1565   GtkBorder border;
1566
1567   range = GTK_RANGE (widget);
1568
1569   gtk_range_get_props (range,
1570                        &slider_width, &stepper_size,
1571                        &focus_width, &trough_border,
1572                        &stepper_spacing, NULL,
1573                        NULL, NULL);
1574
1575   gtk_range_calc_request (range,
1576                           slider_width, stepper_size,
1577                           focus_width, trough_border, stepper_spacing,
1578                           &range_rect, &border, NULL, NULL, NULL, NULL);
1579
1580   *minimum = *natural = range_rect.height + border.top + border.bottom;
1581 }
1582
1583 static gboolean
1584 modify_allocation_for_window_grip (GtkWidget     *widget,
1585                                    GtkAllocation *allocation)
1586 {
1587   GtkRange *range = GTK_RANGE (widget);
1588   GtkRangePrivate *priv = range->priv;
1589   GtkWidget *window, *parent;
1590   GdkRectangle grip_rect;
1591   GdkRectangle translated_rect;
1592   gint x;
1593   gint y;
1594
1595   window = gtk_widget_get_toplevel (widget);
1596   if (!GTK_IS_WINDOW (window))
1597     return FALSE;
1598
1599   if (!gtk_window_resize_grip_is_visible (GTK_WINDOW (window)))
1600     return FALSE;
1601
1602   /* Get the area of the window's corner grip */
1603   gtk_window_get_resize_grip_area (GTK_WINDOW (window), &grip_rect);
1604
1605   x = 0;
1606   y = 0;
1607
1608   /* Translate the stepper's area into window coords.
1609    * This is slightly tricky. We can't just use
1610    * gtk_widget_translate_coordinates (widget, window, 0, 0, &x, &y)
1611    * since that translates wrt to the _current_ allocation
1612    * and will lead to alternating between overlap and nonoverlap
1613    * for successive allocations.
1614    * Therefore, we find the window-widget to whose window allocation
1615    * is relative, and translate from there upwards.
1616    */
1617   parent = widget;
1618   while (gtk_widget_get_window (parent) == gtk_widget_get_window (widget) &&
1619          parent != window)
1620     {
1621       parent = gtk_widget_get_parent (parent);
1622     }
1623
1624   if (parent == window)
1625     translated_rect = *allocation;
1626   else
1627     {
1628       gtk_widget_translate_coordinates (parent,
1629                                         window,
1630                                         allocation->x, allocation->y,
1631                                         &x, &y);
1632       translated_rect.x = x;
1633       translated_rect.y = y;
1634       translated_rect.width = allocation->width;
1635       translated_rect.height = allocation->height;
1636     }
1637
1638   /* If the stepper button intersects the window resize grip.. */
1639   if (gdk_rectangle_intersect (&grip_rect, &translated_rect, &grip_rect))
1640     {
1641       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
1642         {
1643           allocation->width -= grip_rect.width;
1644           if (gtk_widget_get_direction (window) == GTK_TEXT_DIR_RTL)
1645             allocation->x += grip_rect.width;
1646         }
1647       else
1648         {
1649           allocation->height -= grip_rect.height;
1650         }
1651
1652       return TRUE;
1653     }
1654
1655   return FALSE;
1656 }
1657
1658 static void
1659 gtk_range_size_allocate (GtkWidget     *widget,
1660                          GtkAllocation *allocation)
1661 {
1662   GtkRange *range = GTK_RANGE (widget);
1663   GtkRangePrivate *priv = range->priv;
1664
1665   modify_allocation_for_window_grip (widget, allocation);
1666   gtk_widget_set_allocation (widget, allocation);
1667
1668   priv->recalc_marks = TRUE;
1669
1670   priv->need_recalc = TRUE;
1671   gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
1672
1673   if (gtk_widget_get_realized (widget))
1674     gdk_window_move_resize (priv->event_window,
1675                             allocation->x, allocation->y,
1676                             allocation->width, allocation->height);
1677 }
1678
1679 static void
1680 resize_grip_visible_changed (GObject    *object,
1681                              GParamSpec *pspec,
1682                              gpointer    user_data)
1683 {
1684   gtk_widget_queue_resize (GTK_WIDGET (user_data));
1685 }
1686
1687 static void
1688 gtk_range_hierarchy_changed (GtkWidget *widget,
1689                              GtkWidget *previous_toplevel)
1690 {
1691   GtkWidget *window;
1692
1693   if (previous_toplevel)
1694     g_signal_handlers_disconnect_by_func (previous_toplevel,
1695                                           G_CALLBACK (resize_grip_visible_changed),
1696                                           widget);
1697   window = gtk_widget_get_toplevel (widget);
1698   if (gtk_widget_is_toplevel (window))
1699     g_signal_connect (window, "notify::resize-grip-visible",
1700                       G_CALLBACK (resize_grip_visible_changed), widget);
1701 }
1702
1703 static void
1704 gtk_range_realize (GtkWidget *widget)
1705 {
1706   GtkAllocation allocation;
1707   GtkRange *range = GTK_RANGE (widget);
1708   GtkRangePrivate *priv = range->priv;
1709   GdkWindow *window;
1710   GdkWindowAttr attributes;
1711   gint attributes_mask;
1712
1713   gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
1714
1715   gtk_widget_set_realized (widget, TRUE);
1716
1717   window = gtk_widget_get_parent_window (widget);
1718   gtk_widget_set_window (widget, window);
1719   g_object_ref (window);
1720
1721   gtk_widget_get_allocation (widget, &allocation);
1722   if (modify_allocation_for_window_grip (widget, &allocation))
1723     gtk_widget_set_allocation (widget, &allocation);
1724
1725   attributes.window_type = GDK_WINDOW_CHILD;
1726   attributes.x = allocation.x;
1727   attributes.y = allocation.y;
1728   attributes.width = allocation.width;
1729   attributes.height = allocation.height;
1730   attributes.wclass = GDK_INPUT_ONLY;
1731   attributes.event_mask = gtk_widget_get_events (widget);
1732   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1733                             GDK_BUTTON_RELEASE_MASK |
1734                             GDK_ENTER_NOTIFY_MASK |
1735                             GDK_LEAVE_NOTIFY_MASK |
1736                             GDK_POINTER_MOTION_MASK |
1737                             GDK_POINTER_MOTION_HINT_MASK);
1738
1739   attributes_mask = GDK_WA_X | GDK_WA_Y;
1740
1741   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1742                                         &attributes, attributes_mask);
1743   gdk_window_set_user_data (priv->event_window, range);
1744 }
1745
1746 static void
1747 gtk_range_unrealize (GtkWidget *widget)
1748 {
1749   GtkRange *range = GTK_RANGE (widget);
1750   GtkRangePrivate *priv = range->priv;
1751
1752   gtk_range_remove_step_timer (range);
1753
1754   gdk_window_set_user_data (priv->event_window, NULL);
1755   gdk_window_destroy (priv->event_window);
1756   priv->event_window = NULL;
1757
1758   GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1759 }
1760
1761 static void
1762 gtk_range_map (GtkWidget *widget)
1763 {
1764   GtkRange *range = GTK_RANGE (widget);
1765   GtkRangePrivate *priv = range->priv;
1766
1767   gdk_window_show (priv->event_window);
1768
1769   GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1770 }
1771
1772 static void
1773 gtk_range_unmap (GtkWidget *widget)
1774 {
1775   GtkRange *range = GTK_RANGE (widget);
1776   GtkRangePrivate *priv = range->priv;
1777
1778   stop_scrolling (range);
1779
1780   gdk_window_hide (priv->event_window);
1781
1782   GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1783 }
1784
1785 static void
1786 _gtk_range_update_context_for_stepper (GtkRange        *range,
1787                                        GtkStyleContext *context,
1788                                        Stepper          stepper)
1789 {
1790   GtkRangePrivate *priv = range->priv;
1791   GtkJunctionSides sides = 0;
1792   gboolean vertical, is_rtl;
1793
1794   vertical = (priv->orientation == GTK_ORIENTATION_VERTICAL);
1795   is_rtl = (gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1796
1797   /* Take junction sides from what's been
1798    * previously set to the widget itself
1799    */
1800   sides = gtk_style_context_get_junction_sides (context);
1801
1802   if (vertical)
1803     sides &= ~(GTK_JUNCTION_TOP | GTK_JUNCTION_BOTTOM);
1804   else
1805     sides &= ~(GTK_JUNCTION_LEFT | GTK_JUNCTION_RIGHT);
1806
1807   switch (stepper)
1808     {
1809     case STEPPER_A:
1810       if (vertical)
1811         sides |= GTK_JUNCTION_BOTTOM;
1812       else
1813         sides |= (is_rtl) ? GTK_JUNCTION_LEFT : GTK_JUNCTION_RIGHT;
1814       break;
1815     case STEPPER_B:
1816       if (priv->has_stepper_a)
1817         {
1818           if (vertical)
1819             sides |= GTK_JUNCTION_TOP;
1820           else
1821             sides |= (is_rtl) ? GTK_JUNCTION_RIGHT : GTK_JUNCTION_LEFT;
1822         }
1823
1824       if (vertical)
1825         sides |= GTK_JUNCTION_BOTTOM;
1826       else
1827         sides |= (is_rtl) ? GTK_JUNCTION_LEFT : GTK_JUNCTION_RIGHT;
1828       break;
1829     case STEPPER_C:
1830       if (priv->has_stepper_d)
1831         {
1832           if (vertical)
1833             sides |= GTK_JUNCTION_BOTTOM;
1834           else
1835             sides |= (is_rtl) ? GTK_JUNCTION_LEFT : GTK_JUNCTION_RIGHT;
1836         }
1837
1838       if (vertical)
1839         sides |= GTK_JUNCTION_TOP;
1840       else
1841         sides |= (is_rtl) ? GTK_JUNCTION_RIGHT : GTK_JUNCTION_LEFT;
1842       break;
1843     case STEPPER_D:
1844       if (vertical)
1845         sides |= GTK_JUNCTION_TOP;
1846       else
1847         sides |= (is_rtl) ? GTK_JUNCTION_RIGHT : GTK_JUNCTION_LEFT;
1848       break;
1849     }
1850
1851   gtk_style_context_set_junction_sides (context, sides);
1852 }
1853
1854 static void
1855 draw_stepper (GtkRange     *range,
1856               Stepper       stepper,
1857               cairo_t      *cr,
1858               GtkArrowType  arrow_type,
1859               gboolean      clicked,
1860               gboolean      prelighted,
1861               GtkStateFlags state)
1862 {
1863   GtkRangePrivate *priv = range->priv;
1864   GtkAllocation allocation;
1865   GtkStyleContext *context;
1866   GtkWidget *widget = GTK_WIDGET (range);
1867   gfloat arrow_scaling;
1868   GdkRectangle *rect;
1869   gdouble arrow_x;
1870   gdouble arrow_y;
1871   gdouble arrow_size, angle;
1872   gboolean arrow_sensitive;
1873
1874   switch (stepper)
1875     {
1876     case STEPPER_A:
1877       rect = &priv->stepper_a;
1878       break;
1879     case STEPPER_B:
1880       rect = &priv->stepper_b;
1881       break;
1882     case STEPPER_C:
1883       rect = &priv->stepper_c;
1884       break;
1885     case STEPPER_D:
1886       rect = &priv->stepper_d;
1887       break;
1888     default:
1889       g_assert_not_reached ();
1890     };
1891
1892   arrow_sensitive = TRUE;
1893
1894   gtk_widget_get_allocation (widget, &allocation);
1895
1896   if ((!priv->inverted && (arrow_type == GTK_ARROW_DOWN ||
1897                             arrow_type == GTK_ARROW_RIGHT)) ||
1898       (priv->inverted  && (arrow_type == GTK_ARROW_UP ||
1899                             arrow_type == GTK_ARROW_LEFT)))
1900     {
1901       arrow_sensitive = priv->upper_sensitive;
1902     }
1903   else
1904     {
1905       arrow_sensitive = priv->lower_sensitive;
1906     }
1907
1908   state &= ~(GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT);
1909
1910   if ((state & GTK_STATE_FLAG_INSENSITIVE) || !arrow_sensitive)
1911     {
1912       state |= GTK_STATE_FLAG_INSENSITIVE;
1913     }
1914   else
1915     {
1916       if (clicked)
1917         state |= GTK_STATE_FLAG_ACTIVE;
1918       if (prelighted)
1919         state |= GTK_STATE_FLAG_PRELIGHT;
1920     }
1921
1922   context = gtk_widget_get_style_context (widget);
1923
1924   gtk_style_context_save (context);
1925
1926   /* don't set juction sides on scrollbar steppers */
1927   if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_SCROLLBAR))
1928     gtk_style_context_set_junction_sides (context, GTK_JUNCTION_NONE);
1929   else
1930     _gtk_range_update_context_for_stepper (range, context, stepper);
1931
1932   gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
1933   gtk_style_context_set_state (context, state);
1934
1935   switch (arrow_type)
1936     {
1937     case GTK_ARROW_RIGHT:
1938       gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
1939       break;
1940     case GTK_ARROW_DOWN:
1941       gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
1942       break;
1943     case GTK_ARROW_LEFT:
1944       gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
1945       break;
1946     case GTK_ARROW_UP:
1947     default:
1948       gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
1949       break;
1950     }
1951
1952   gtk_render_background (context, cr,
1953                          rect->x, rect->y,
1954                          rect->width, rect->height);
1955   gtk_render_frame (context, cr,
1956                     rect->x, rect->y,
1957                     rect->width, rect->height);
1958
1959   gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1960
1961   arrow_size = MIN (rect->width, rect->height) * arrow_scaling;
1962   arrow_x = rect->x + (rect->width - arrow_size) / 2;
1963   arrow_y = rect->y + (rect->height - arrow_size) / 2;
1964
1965   if (clicked && arrow_sensitive)
1966     {
1967       gint arrow_displacement_x;
1968       gint arrow_displacement_y;
1969
1970       gtk_range_get_props (GTK_RANGE (widget),
1971                            NULL, NULL, NULL, NULL, NULL, NULL,
1972                            &arrow_displacement_x, &arrow_displacement_y);
1973
1974       arrow_x += arrow_displacement_x;
1975       arrow_y += arrow_displacement_y;
1976     }
1977
1978   switch (arrow_type)
1979     {
1980     case GTK_ARROW_RIGHT:
1981       angle = G_PI / 2;
1982       break;
1983     case GTK_ARROW_DOWN:
1984       angle = G_PI;
1985       break;
1986     case GTK_ARROW_LEFT:
1987       angle = 3 * (G_PI / 2);
1988       break;
1989     case GTK_ARROW_UP:
1990     default:
1991       angle = 0;
1992       break;
1993     }
1994
1995   gtk_render_arrow (context, cr,
1996                     angle,
1997                     arrow_x, arrow_y,
1998                     arrow_size);
1999
2000   gtk_style_context_restore (context);
2001 }
2002
2003 static gboolean
2004 gtk_range_draw (GtkWidget *widget,
2005                 cairo_t   *cr)
2006 {
2007   GtkRange *range = GTK_RANGE (widget);
2008   GtkRangePrivate *priv = range->priv;
2009   GtkStateFlags widget_state;
2010   gint focus_line_width = 0;
2011   gint focus_padding = 0;
2012   gboolean touchscreen;
2013   gboolean draw_trough = TRUE;
2014   gboolean draw_slider = TRUE;
2015   GtkStyleContext *context;
2016
2017   context = gtk_widget_get_style_context (widget);
2018   g_object_get (gtk_widget_get_settings (widget),
2019                 "gtk-touchscreen-mode", &touchscreen,
2020                 NULL);
2021
2022   if (GTK_IS_SCALE (widget) &&
2023       gtk_adjustment_get_upper (priv->adjustment) == gtk_adjustment_get_lower (priv->adjustment))
2024     {
2025       draw_trough = TRUE;
2026       draw_slider = FALSE;
2027     }
2028   if (GTK_IS_COLOR_SCALE (widget))
2029     {
2030       draw_trough = FALSE;
2031       draw_slider = TRUE;
2032     }
2033
2034   if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
2035     gtk_widget_style_get (GTK_WIDGET (range),
2036                           "focus-line-width", &focus_line_width,
2037                           "focus-padding", &focus_padding,
2038                           NULL);
2039
2040   /* we're now exposing, so there's no need to force early repaints */
2041   if (priv->repaint_id)
2042     g_source_remove (priv->repaint_id);
2043   priv->repaint_id = 0;
2044
2045   gtk_range_calc_marks (range);
2046   gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
2047
2048   widget_state = gtk_widget_get_state_flags (widget);
2049
2050   /* Just to be confusing, we draw the trough for the whole
2051    * range rectangle, not the trough rectangle (the trough
2052    * rectangle is just for hit detection)
2053    */
2054   cairo_save (cr);
2055   gdk_cairo_rectangle (cr, &priv->range_rect);
2056   cairo_clip (cr);
2057
2058     {
2059       gint     x      = (priv->range_rect.x +
2060                          focus_line_width + focus_padding);
2061       gint     y      = (priv->range_rect.y +
2062                          focus_line_width + focus_padding);
2063       gint     width  = (priv->range_rect.width -
2064                          2 * (focus_line_width + focus_padding));
2065       gint     height = (priv->range_rect.height -
2066                          2 * (focus_line_width + focus_padding));
2067       gboolean trough_under_steppers;
2068       gint     stepper_size;
2069       gint     stepper_spacing;
2070
2071       gtk_widget_style_get (GTK_WIDGET (range),
2072                             "trough-under-steppers", &trough_under_steppers,
2073                             "stepper-size",          &stepper_size,
2074                             "stepper-spacing",       &stepper_spacing,
2075                             NULL);
2076
2077       if (!trough_under_steppers)
2078         {
2079           gint offset  = 0;
2080           gint shorter = 0;
2081
2082           if (priv->has_stepper_a)
2083             offset += stepper_size;
2084
2085           if (priv->has_stepper_b)
2086             offset += stepper_size;
2087
2088           shorter += offset;
2089
2090           if (priv->has_stepper_c)
2091             shorter += stepper_size;
2092
2093           if (priv->has_stepper_d)
2094             shorter += stepper_size;
2095
2096           if (priv->has_stepper_a || priv->has_stepper_b)
2097             {
2098               offset  += stepper_spacing;
2099               shorter += stepper_spacing;
2100             }
2101
2102           if (priv->has_stepper_c || priv->has_stepper_d)
2103             {
2104               shorter += stepper_spacing;
2105             }
2106
2107           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2108             {
2109               x     += offset;
2110               width -= shorter;
2111             }
2112           else
2113             {
2114               y      += offset;
2115               height -= shorter;
2116             }
2117         }
2118
2119       gtk_style_context_save (context);
2120       gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
2121
2122       if (draw_trough)
2123         {
2124           if (!priv->has_origin)
2125             {
2126               gtk_render_background (context, cr,
2127                                      x, y, width, height);
2128
2129               gtk_render_frame (context, cr,
2130                                 x, y, width, height);
2131             }
2132           else
2133             {
2134               gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2135
2136               gint trough_change_pos_x = width;
2137               gint trough_change_pos_y = height;
2138
2139               if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2140                 trough_change_pos_x = (priv->slider.x +
2141                                        priv->slider.width / 2 -
2142                                        x);
2143               else
2144                 trough_change_pos_y = (priv->slider.y +
2145                                        priv->slider.height / 2 -
2146                                        y);
2147
2148               gtk_style_context_save (context);
2149               if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2150                 {
2151                   gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
2152
2153                   if (!is_rtl)
2154                     gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
2155                 }
2156               else
2157                 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
2158
2159               gtk_render_background (context, cr, x, y,
2160                                      trough_change_pos_x,
2161                                      trough_change_pos_y);
2162
2163               gtk_render_frame (context, cr, x, y,
2164                                 trough_change_pos_x,
2165                                 trough_change_pos_y);
2166
2167               gtk_style_context_restore (context);
2168
2169               if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2170                 trough_change_pos_y = 0;
2171               else
2172                 trough_change_pos_x = 0;
2173
2174               gtk_style_context_save (context);
2175               if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2176                 {
2177                   gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
2178
2179                   if (is_rtl)
2180                     gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
2181                 }
2182               else
2183                 {
2184                   gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
2185                   gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
2186                 }
2187
2188               gtk_render_background (context, cr,
2189                                      x + trough_change_pos_x, y + trough_change_pos_y,
2190                                      width - trough_change_pos_x,
2191                                      height - trough_change_pos_y);
2192
2193               gtk_render_frame (context, cr,
2194                                 x + trough_change_pos_x, y + trough_change_pos_y,
2195                                 width - trough_change_pos_x,
2196                                 height - trough_change_pos_y);
2197
2198               gtk_style_context_restore (context);
2199             }
2200         }
2201
2202       gtk_style_context_restore (context);
2203
2204       if (priv->show_fill_level &&
2205           gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment) -
2206           gtk_adjustment_get_lower (priv->adjustment) != 0)
2207         {
2208           gdouble  fill_level  = priv->fill_level;
2209           gint     fill_x      = x;
2210           gint     fill_y      = y;
2211           gint     fill_width  = width;
2212           gint     fill_height = height;
2213
2214           gtk_style_context_save (context);
2215           gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
2216
2217           fill_level = CLAMP (fill_level, gtk_adjustment_get_lower (priv->adjustment),
2218                               gtk_adjustment_get_upper (priv->adjustment) -
2219                               gtk_adjustment_get_page_size (priv->adjustment));
2220
2221           if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2222             {
2223               fill_x     = priv->trough.x;
2224               fill_width = (priv->slider.width +
2225                             (fill_level - gtk_adjustment_get_lower (priv->adjustment)) /
2226                             (gtk_adjustment_get_upper (priv->adjustment) -
2227                              gtk_adjustment_get_lower (priv->adjustment) -
2228                              gtk_adjustment_get_page_size (priv->adjustment)) *
2229                             (priv->trough.width -
2230                              priv->slider.width));
2231
2232               if (should_invert (range))
2233                 fill_x += priv->trough.width - fill_width;
2234             }
2235           else
2236             {
2237               fill_y      = priv->trough.y;
2238               fill_height = (priv->slider.height +
2239                              (fill_level - gtk_adjustment_get_lower (priv->adjustment)) /
2240                              (gtk_adjustment_get_upper (priv->adjustment) -
2241                               gtk_adjustment_get_lower (priv->adjustment) -
2242                               gtk_adjustment_get_page_size (priv->adjustment)) *
2243                              (priv->trough.height -
2244                               priv->slider.height));
2245
2246               if (should_invert (range))
2247                 fill_y += priv->trough.height - fill_height;
2248             }
2249
2250 #if 0
2251           if (fill_level < gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment))
2252             fill_detail = "trough-fill-level-full";
2253           else
2254             fill_detail = "trough-fill-level";
2255 #endif
2256
2257           gtk_render_activity (context, cr,
2258                                fill_x, fill_y,
2259                                fill_width, fill_height);
2260
2261           gtk_style_context_restore (context);
2262         }
2263
2264       if (!(widget_state & GTK_STATE_FLAG_INSENSITIVE) && gtk_widget_has_visible_focus (widget))
2265         {
2266           gtk_render_focus (context, cr,
2267                             priv->range_rect.x,
2268                             priv->range_rect.y,
2269                             priv->range_rect.width,
2270                             priv->range_rect.height);
2271         }
2272     }
2273
2274   cairo_restore (cr);
2275
2276   if (draw_slider)
2277     {
2278       GtkStateFlags state = widget_state;
2279
2280       state &= ~(GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_ACTIVE);
2281
2282       if (!touchscreen && priv->mouse_location == MOUSE_SLIDER && !(state & GTK_STATE_FLAG_INSENSITIVE))
2283         state |= GTK_STATE_FLAG_PRELIGHT;
2284
2285       if (priv->grab_location == MOUSE_SLIDER)
2286         state |= GTK_STATE_FLAG_ACTIVE;
2287
2288       cairo_save (cr);
2289       gdk_cairo_rectangle (cr, &priv->slider);
2290       cairo_clip (cr);
2291
2292       gtk_style_context_save (context);
2293       gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
2294       gtk_style_context_set_state (context, state);
2295
2296       gtk_render_slider (context, cr,
2297                          priv->slider.x,
2298                          priv->slider.y,
2299                          priv->slider.width,
2300                          priv->slider.height,
2301                          priv->orientation);
2302
2303       gtk_style_context_restore (context);
2304
2305       cairo_restore (cr);
2306     }
2307
2308   if (priv->has_stepper_a)
2309     draw_stepper (range, STEPPER_A, cr,
2310                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2311                   priv->grab_location == MOUSE_STEPPER_A,
2312                   !touchscreen && priv->mouse_location == MOUSE_STEPPER_A,
2313                   widget_state);
2314
2315   if (priv->has_stepper_b)
2316     draw_stepper (range, STEPPER_B, cr,
2317                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2318                   priv->grab_location == MOUSE_STEPPER_B,
2319                   !touchscreen && priv->mouse_location == MOUSE_STEPPER_B,
2320                   widget_state);
2321
2322   if (priv->has_stepper_c)
2323     draw_stepper (range, STEPPER_C, cr,
2324                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2325                   priv->grab_location == MOUSE_STEPPER_C,
2326                   !touchscreen && priv->mouse_location == MOUSE_STEPPER_C,
2327                   widget_state);
2328
2329   if (priv->has_stepper_d)
2330     draw_stepper (range, STEPPER_D, cr,
2331                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2332                   priv->grab_location == MOUSE_STEPPER_D,
2333                   !touchscreen && priv->mouse_location == MOUSE_STEPPER_D,
2334                   widget_state);
2335
2336   return FALSE;
2337 }
2338
2339 static void
2340 range_grab_add (GtkRange      *range,
2341                 GdkDevice     *device,
2342                 MouseLocation  location,
2343                 gint           button)
2344 {
2345   GtkRangePrivate *priv = range->priv;
2346
2347   if (device == priv->grab_device)
2348     return;
2349
2350   /* Don't perform any GDK/GTK+ grab here. Since a button
2351    * is down, there's an ongoing implicit grab on
2352    * priv->event_window, which pretty much guarantees this
2353    * is the only widget receiving the pointer events.
2354    */
2355   priv->grab_location = location;
2356   priv->grab_button = button;
2357   priv->grab_device = device;
2358
2359   if (gtk_range_update_mouse_location (range))
2360     gtk_widget_queue_draw (GTK_WIDGET (range));
2361 }
2362
2363 static void
2364 range_grab_remove (GtkRange *range)
2365 {
2366   GtkRangePrivate *priv = range->priv;
2367   MouseLocation location;
2368
2369   if (priv->grab_device)
2370     {
2371       gtk_device_grab_remove (GTK_WIDGET (range),
2372                               priv->grab_device);
2373       priv->grab_device = NULL;
2374     }
2375
2376   location = priv->grab_location;
2377   priv->grab_location = MOUSE_OUTSIDE;
2378   priv->grab_button = 0;
2379
2380   if (gtk_range_update_mouse_location (range) ||
2381       location != MOUSE_OUTSIDE)
2382     gtk_widget_queue_draw (GTK_WIDGET (range));
2383 }
2384
2385 static GtkScrollType
2386 range_get_scroll_for_grab (GtkRange      *range)
2387 {
2388   GtkRangePrivate *priv = range->priv;
2389   gboolean invert;
2390
2391   invert = should_invert (range);
2392   switch (priv->grab_location)
2393     {
2394       /* Backward stepper */
2395     case MOUSE_STEPPER_A:
2396     case MOUSE_STEPPER_C:
2397       switch (priv->grab_button)
2398         {
2399         case GDK_BUTTON_PRIMARY:
2400           return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
2401           break;
2402         case GDK_BUTTON_MIDDLE:
2403           return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
2404           break;
2405         case GDK_BUTTON_SECONDARY:
2406           return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
2407           break;
2408         }
2409       break;
2410
2411       /* Forward stepper */
2412     case MOUSE_STEPPER_B:
2413     case MOUSE_STEPPER_D:
2414       switch (priv->grab_button)
2415         {
2416         case GDK_BUTTON_PRIMARY:
2417           return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
2418           break;
2419         case GDK_BUTTON_MIDDLE:
2420           return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
2421           break;
2422         case GDK_BUTTON_SECONDARY:
2423           return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
2424           break;
2425        }
2426       break;
2427
2428       /* In the trough */
2429     case MOUSE_TROUGH:
2430       {
2431         if (priv->trough_click_forward)
2432           return GTK_SCROLL_PAGE_FORWARD;
2433         else
2434           return GTK_SCROLL_PAGE_BACKWARD;
2435       }
2436       break;
2437
2438     case MOUSE_OUTSIDE:
2439     case MOUSE_SLIDER:
2440     case MOUSE_WIDGET:
2441       break;
2442     }
2443
2444   return GTK_SCROLL_NONE;
2445 }
2446
2447 static gdouble
2448 coord_to_value (GtkRange *range,
2449                 gint      coord)
2450 {
2451   GtkRangePrivate *priv = range->priv;
2452   gdouble frac;
2453   gdouble value;
2454   gint    trough_length;
2455   gint    trough_start;
2456   gint    slider_length;
2457   gint    trough_border;
2458   gint    trough_under_steppers;
2459
2460   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
2461     {
2462       trough_length = priv->trough.height;
2463       trough_start  = priv->trough.y;
2464       slider_length = priv->slider.height;
2465     }
2466   else
2467     {
2468       trough_length = priv->trough.width;
2469       trough_start  = priv->trough.x;
2470       slider_length = priv->slider.width;
2471     }
2472
2473   gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
2474                        &trough_under_steppers, NULL, NULL);
2475
2476   if (! trough_under_steppers)
2477     {
2478       trough_start += trough_border;
2479       trough_length -= 2 * trough_border;
2480     }
2481
2482   if (trough_length == slider_length)
2483     frac = 1.0;
2484   else
2485     frac = (MAX (0, coord - trough_start) /
2486             (gdouble) (trough_length - slider_length));
2487
2488   if (should_invert (range))
2489     frac = 1.0 - frac;
2490
2491   value = gtk_adjustment_get_lower (priv->adjustment) + frac * (gtk_adjustment_get_upper (priv->adjustment) -
2492                                             gtk_adjustment_get_lower (priv->adjustment) -
2493                                             gtk_adjustment_get_page_size (priv->adjustment));
2494
2495   return value;
2496 }
2497
2498 static gboolean
2499 gtk_range_key_press (GtkWidget   *widget,
2500                      GdkEventKey *event)
2501 {
2502   GdkDevice *device;
2503   GtkRange *range = GTK_RANGE (widget);
2504   GtkRangePrivate *priv = range->priv;
2505
2506   device = gdk_event_get_device ((GdkEvent *) event);
2507   device = gdk_device_get_associated_device (device);
2508
2509   if (device == priv->grab_device &&
2510       event->keyval == GDK_KEY_Escape &&
2511       priv->grab_location != MOUSE_OUTSIDE)
2512     {
2513       stop_scrolling (range);
2514
2515       update_slider_position (range,
2516                               priv->slide_initial_coordinate,
2517                               priv->slide_initial_coordinate);
2518
2519       return TRUE;
2520     }
2521
2522   return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
2523 }
2524
2525 static gint
2526 gtk_range_button_press (GtkWidget      *widget,
2527                         GdkEventButton *event)
2528 {
2529   GtkRange *range = GTK_RANGE (widget);
2530   GtkRangePrivate *priv = range->priv;
2531   GdkDevice *device;
2532
2533   if (!gtk_widget_has_focus (widget))
2534     gtk_widget_grab_focus (widget);
2535
2536   /* ignore presses when we're already doing something else. */
2537   if (priv->grab_location != MOUSE_OUTSIDE)
2538     return FALSE;
2539
2540   device = gdk_event_get_device ((GdkEvent *) event);
2541   priv->mouse_x = event->x;
2542   priv->mouse_y = event->y;
2543
2544   if (gtk_range_update_mouse_location (range))
2545     gtk_widget_queue_draw (widget);
2546
2547   if (priv->mouse_location == MOUSE_SLIDER &&
2548       gdk_event_triggers_context_menu ((GdkEvent *)event))
2549     {
2550       gboolean handled;
2551
2552       g_signal_emit_by_name (widget, "popup-menu", &handled);
2553
2554       return TRUE;
2555     }
2556
2557   if (priv->mouse_location == MOUSE_TROUGH  &&
2558       event->button == GDK_BUTTON_PRIMARY)
2559     {
2560       /* button 1 steps by page increment, as with button 2 on a stepper
2561        */
2562       GtkScrollType scroll;
2563       gdouble click_value;
2564       
2565       click_value = coord_to_value (range,
2566                                     priv->orientation == GTK_ORIENTATION_VERTICAL ?
2567                                     event->y : event->x);
2568
2569       priv->trough_click_forward = click_value > gtk_adjustment_get_value (priv->adjustment);
2570       range_grab_add (range, device, MOUSE_TROUGH, event->button);
2571       
2572       scroll = range_get_scroll_for_grab (range);
2573       
2574       gtk_range_add_step_timer (range, scroll);
2575
2576       return TRUE;
2577     }
2578   else if ((priv->mouse_location == MOUSE_STEPPER_A ||
2579             priv->mouse_location == MOUSE_STEPPER_B ||
2580             priv->mouse_location == MOUSE_STEPPER_C ||
2581             priv->mouse_location == MOUSE_STEPPER_D) &&
2582            (event->button == GDK_BUTTON_PRIMARY ||
2583             event->button == GDK_BUTTON_MIDDLE ||
2584             event->button == GDK_BUTTON_SECONDARY))
2585     {
2586       GtkAllocation allocation;
2587       GdkRectangle *stepper_area;
2588       GtkScrollType scroll;
2589
2590       range_grab_add (range, device, priv->mouse_location, event->button);
2591
2592       gtk_widget_get_allocation (widget, &allocation);
2593       stepper_area = get_area (range, priv->mouse_location);
2594
2595       gtk_widget_queue_draw_area (widget,
2596                                   allocation.x + stepper_area->x,
2597                                   allocation.y + stepper_area->y,
2598                                   stepper_area->width,
2599                                   stepper_area->height);
2600
2601       scroll = range_get_scroll_for_grab (range);
2602       if (scroll != GTK_SCROLL_NONE)
2603         gtk_range_add_step_timer (range, scroll);
2604       
2605       return TRUE;
2606     }
2607   else if ((priv->mouse_location == MOUSE_TROUGH &&
2608             event->button == GDK_BUTTON_MIDDLE) ||
2609            priv->mouse_location == MOUSE_SLIDER)
2610     {
2611       gboolean need_value_update = FALSE;
2612
2613       /* Any button can be used to drag the slider, but you can start
2614        * dragging the slider with a trough click using button 2;
2615        * On button 2 press, we warp the slider to mouse position,
2616        * then begin the slider drag.
2617        */
2618       if (event->button == GDK_BUTTON_MIDDLE)
2619         {
2620           gdouble slider_low_value, slider_high_value, new_value;
2621           
2622           slider_high_value =
2623             coord_to_value (range,
2624                             priv->orientation == GTK_ORIENTATION_VERTICAL ?
2625                             event->y : event->x);
2626           slider_low_value =
2627             coord_to_value (range,
2628                             priv->orientation == GTK_ORIENTATION_VERTICAL ?
2629                             event->y - priv->slider.height :
2630                             event->x - priv->slider.width);
2631
2632           /* compute new value for warped slider */
2633           new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
2634
2635           /* recalc slider, so we can set slide_initial_slider_position
2636            * properly
2637            */
2638           priv->need_recalc = TRUE;
2639           gtk_range_calc_layout (range, new_value);
2640
2641           /* defer adjustment updates to update_slider_position() in order
2642            * to keep pixel quantisation
2643            */
2644           need_value_update = TRUE;
2645         }
2646
2647       if (priv->orientation == GTK_ORIENTATION_VERTICAL)
2648         {
2649           priv->slide_initial_slider_position = priv->slider.y;
2650           priv->slide_initial_coordinate = event->y;
2651         }
2652       else
2653         {
2654           priv->slide_initial_slider_position = priv->slider.x;
2655           priv->slide_initial_coordinate = event->x;
2656         }
2657
2658       range_grab_add (range, device, MOUSE_SLIDER, event->button);
2659
2660       gtk_widget_queue_draw (widget);
2661
2662       if (need_value_update)
2663         update_slider_position (range, event->x, event->y);
2664
2665       return TRUE;
2666     }
2667   
2668   return FALSE;
2669 }
2670
2671 /* During a slide, move the slider as required given new mouse position */
2672 static void
2673 update_slider_position (GtkRange *range,
2674                         gint      mouse_x,
2675                         gint      mouse_y)
2676 {
2677   GtkRangePrivate *priv = range->priv;
2678   gint delta;
2679   gint c;
2680   gdouble new_value;
2681   gboolean handled;
2682   gdouble next_value;
2683   gdouble mark_value;
2684   gdouble mark_delta;
2685   gint i;
2686
2687   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
2688     delta = mouse_y - priv->slide_initial_coordinate;
2689   else
2690     delta = mouse_x - priv->slide_initial_coordinate;
2691
2692   c = priv->slide_initial_slider_position + delta;
2693
2694   new_value = coord_to_value (range, c);
2695   next_value = coord_to_value (range, c + 1);
2696   mark_delta = fabs (next_value - new_value); 
2697
2698   for (i = 0; i < priv->n_marks; i++)
2699     {
2700       mark_value = priv->marks[i];
2701
2702       if (fabs (gtk_adjustment_get_value (priv->adjustment) - mark_value) < 3 * mark_delta)
2703         {
2704           if (fabs (new_value - mark_value) < (priv->slider_end - priv->slider_start) * 0.5 * mark_delta)
2705             {
2706               new_value = mark_value;
2707               break;
2708             }
2709         }
2710     }  
2711
2712   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2713                  &handled);
2714 }
2715
2716 static void 
2717 stop_scrolling (GtkRange *range)
2718 {
2719   range_grab_remove (range);
2720   gtk_range_remove_step_timer (range);
2721 }
2722
2723 static gboolean
2724 gtk_range_grab_broken (GtkWidget          *widget,
2725                        GdkEventGrabBroken *event)
2726 {
2727   GtkRange *range = GTK_RANGE (widget);
2728   GtkRangePrivate *priv = range->priv;
2729   GdkDevice *device;
2730
2731   device = gdk_event_get_device ((GdkEvent *) event);
2732
2733   if (device == priv->grab_device &&
2734       priv->grab_location != MOUSE_OUTSIDE)
2735     {
2736       if (priv->grab_location == MOUSE_SLIDER)
2737         update_slider_position (range, priv->mouse_x, priv->mouse_y);
2738
2739       stop_scrolling (range);
2740       
2741       return TRUE;
2742     }
2743   
2744   return FALSE;
2745 }
2746
2747 static gint
2748 gtk_range_button_release (GtkWidget      *widget,
2749                           GdkEventButton *event)
2750 {
2751   GtkRange *range = GTK_RANGE (widget);
2752   GtkRangePrivate *priv = range->priv;
2753   GdkDevice *device;
2754
2755   if (event->window == priv->event_window)
2756     {
2757       priv->mouse_x = event->x;
2758       priv->mouse_y = event->y;
2759     }
2760   else
2761     {
2762       gdk_window_get_device_position (priv->event_window,
2763                                       event->device,
2764                                       &priv->mouse_x,
2765                                       &priv->mouse_y,
2766                                       NULL);
2767     }
2768
2769   device = gdk_event_get_device ((GdkEvent *) event);
2770
2771   if (priv->grab_device == device &&
2772       priv->grab_button == event->button)
2773     {
2774       if (priv->grab_location == MOUSE_SLIDER)
2775         update_slider_position (range, priv->mouse_x, priv->mouse_y);
2776
2777       stop_scrolling (range);
2778       
2779       return TRUE;
2780     }
2781
2782   return FALSE;
2783 }
2784
2785 /**
2786  * _gtk_range_get_wheel_delta:
2787  * @range: a #GtkRange
2788  * @direction: A #GdkScrollDirection
2789  * 
2790  * Returns a good step value for the mouse wheel.
2791  * 
2792  * Return value: A good step value for the mouse wheel. 
2793  * 
2794  * Since: 2.4
2795  **/
2796 gdouble
2797 _gtk_range_get_wheel_delta (GtkRange           *range,
2798                             GdkScrollDirection  direction)
2799 {
2800   GtkRangePrivate *priv = range->priv;
2801   GtkAdjustment *adjustment = priv->adjustment;
2802   gdouble delta;
2803
2804   if (GTK_IS_SCROLLBAR (range))
2805     delta = pow (gtk_adjustment_get_page_size (adjustment), 2.0 / 3.0);
2806   else
2807     delta = gtk_adjustment_get_step_increment (adjustment) * 2;
2808   
2809   if (direction == GDK_SCROLL_UP ||
2810       direction == GDK_SCROLL_LEFT)
2811     delta = - delta;
2812   
2813   if (priv->inverted)
2814     delta = - delta;
2815
2816   return delta;
2817 }
2818       
2819 static gboolean
2820 gtk_range_scroll_event (GtkWidget      *widget,
2821                         GdkEventScroll *event)
2822 {
2823   GtkRange *range = GTK_RANGE (widget);
2824   GtkRangePrivate *priv = range->priv;
2825
2826   if (gtk_widget_get_realized (widget))
2827     {
2828       gdouble delta;
2829       gboolean handled;
2830
2831       delta = _gtk_range_get_wheel_delta (range, event->direction);
2832
2833       g_signal_emit (range, signals[CHANGE_VALUE], 0,
2834                      GTK_SCROLL_JUMP, gtk_adjustment_get_value (priv->adjustment) + delta,
2835                      &handled);
2836     }
2837
2838   return TRUE;
2839 }
2840
2841 static gboolean
2842 gtk_range_motion_notify (GtkWidget      *widget,
2843                          GdkEventMotion *event)
2844 {
2845   GtkRange *range = GTK_RANGE (widget);
2846   GtkRangePrivate *priv = range->priv;
2847
2848   gdk_event_request_motions (event);
2849
2850   priv->mouse_x = event->x;
2851   priv->mouse_y = event->y;
2852
2853   if (gtk_range_update_mouse_location (range))
2854     gtk_widget_queue_draw (widget);
2855
2856   if (priv->grab_location == MOUSE_SLIDER)
2857     update_slider_position (range, event->x, event->y);
2858
2859   /* We handled the event if the mouse was in the range_rect */
2860   return priv->mouse_location != MOUSE_OUTSIDE;
2861 }
2862
2863 static gboolean
2864 gtk_range_enter_notify (GtkWidget        *widget,
2865                         GdkEventCrossing *event)
2866 {
2867   GtkRange *range = GTK_RANGE (widget);
2868   GtkRangePrivate *priv = range->priv;
2869
2870   priv->mouse_x = event->x;
2871   priv->mouse_y = event->y;
2872
2873   if (gtk_range_update_mouse_location (range))
2874     gtk_widget_queue_draw (widget);
2875   
2876   return TRUE;
2877 }
2878
2879 static gboolean
2880 gtk_range_leave_notify (GtkWidget        *widget,
2881                         GdkEventCrossing *event)
2882 {
2883   GtkRange *range = GTK_RANGE (widget);
2884   GtkRangePrivate *priv = range->priv;
2885
2886   priv->mouse_x = -1;
2887   priv->mouse_y = -1;
2888
2889   if (gtk_range_update_mouse_location (range))
2890     gtk_widget_queue_draw (widget);
2891   
2892   return TRUE;
2893 }
2894
2895 static void
2896 gtk_range_grab_notify (GtkWidget *widget,
2897                        gboolean   was_grabbed)
2898 {
2899   GtkRangePrivate *priv = GTK_RANGE (widget)->priv;
2900
2901   if (priv->grab_device &&
2902       gtk_widget_device_is_shadowed (widget, priv->grab_device))
2903     stop_scrolling (GTK_RANGE (widget));
2904 }
2905
2906 static void
2907 gtk_range_state_flags_changed (GtkWidget     *widget,
2908                                GtkStateFlags  previous_state)
2909 {
2910   if (!gtk_widget_is_sensitive (widget))
2911     stop_scrolling (GTK_RANGE (widget));
2912 }
2913
2914 #define check_rectangle(rectangle1, rectangle2)              \
2915   {                                                          \
2916     if (rectangle1.x != rectangle2.x) return TRUE;           \
2917     if (rectangle1.y != rectangle2.y) return TRUE;           \
2918     if (rectangle1.width  != rectangle2.width)  return TRUE; \
2919     if (rectangle1.height != rectangle2.height) return TRUE; \
2920   }
2921
2922 static gboolean
2923 layout_changed (GtkRangePrivate *priv1,
2924                 GtkRangePrivate *priv2)
2925 {
2926   check_rectangle (priv1->slider, priv2->slider);
2927   check_rectangle (priv1->trough, priv2->trough);
2928   check_rectangle (priv1->stepper_a, priv2->stepper_a);
2929   check_rectangle (priv1->stepper_d, priv2->stepper_d);
2930   check_rectangle (priv1->stepper_b, priv2->stepper_b);
2931   check_rectangle (priv1->stepper_c, priv2->stepper_c);
2932
2933   if (priv1->upper_sensitive != priv2->upper_sensitive) return TRUE;
2934   if (priv1->lower_sensitive != priv2->lower_sensitive) return TRUE;
2935
2936   return FALSE;
2937 }
2938
2939 static void
2940 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2941                               gpointer       data)
2942 {
2943   GtkRange *range = GTK_RANGE (data);
2944   GtkRangePrivate *priv = range->priv;
2945   GtkRangePrivate priv_aux = *priv;
2946
2947   priv->recalc_marks = TRUE;
2948   priv->need_recalc = TRUE;
2949   gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
2950
2951   /* now check whether the layout changed  */
2952   if (layout_changed (priv, &priv_aux))
2953     gtk_widget_queue_draw (GTK_WIDGET (range));
2954
2955   /* Note that we don't round off to priv->round_digits here.
2956    * that's because it's really broken to change a value
2957    * in response to a change signal on that value; round_digits
2958    * is therefore defined to be a filter on what the GtkRange
2959    * can input into the adjustment, not a filter that the GtkRange
2960    * will enforce on the adjustment.
2961    */
2962 }
2963
2964 static gboolean
2965 force_repaint (gpointer data)
2966 {
2967   GtkRange *range = GTK_RANGE (data);
2968   GtkRangePrivate *priv = range->priv;
2969   GtkWidget *widget = GTK_WIDGET (range);
2970
2971   priv->repaint_id = 0;
2972   if (gtk_widget_is_drawable (widget))
2973     gdk_window_process_updates (gtk_widget_get_window (widget), FALSE);
2974
2975   return FALSE;
2976 }
2977
2978 static void
2979 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2980                                     gpointer       data)
2981 {
2982   GtkRange *range = GTK_RANGE (data);
2983   GtkRangePrivate *priv = range->priv;
2984   GtkRangePrivate priv_aux = *priv;
2985
2986   priv->need_recalc = TRUE;
2987   gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
2988   
2989   /* now check whether the layout changed  */
2990   if (layout_changed (priv, &priv_aux) ||
2991       (GTK_IS_SCALE (range) && gtk_scale_get_draw_value (GTK_SCALE (range))))
2992     {
2993       gtk_widget_queue_draw (GTK_WIDGET (range));
2994       /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2995       if (!priv->repaint_id)
2996         priv->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS,
2997                                                          181, force_repaint,
2998                                                          range, NULL);
2999     }
3000
3001   /* Note that we don't round off to priv->round_digits here.
3002    * that's because it's really broken to change a value
3003    * in response to a change signal on that value; round_digits
3004    * is therefore defined to be a filter on what the GtkRange
3005    * can input into the adjustment, not a filter that the GtkRange
3006    * will enforce on the adjustment.
3007    */
3008
3009   g_signal_emit (range, signals[VALUE_CHANGED], 0);
3010 }
3011
3012 static void
3013 gtk_range_style_updated (GtkWidget *widget)
3014 {
3015   GtkRange *range = GTK_RANGE (widget);
3016   GtkRangePrivate *priv = range->priv;
3017
3018   priv->need_recalc = TRUE;
3019
3020   GTK_WIDGET_CLASS (gtk_range_parent_class)->style_updated (widget);
3021 }
3022
3023 static void
3024 apply_marks (GtkRange *range, 
3025              gdouble   oldval,
3026              gdouble  *newval)
3027 {
3028   GtkRangePrivate *priv = range->priv;
3029   gint i;
3030   gdouble mark;
3031
3032   for (i = 0; i < priv->n_marks; i++)
3033     {
3034       mark = priv->marks[i];
3035       if ((oldval < mark && mark < *newval) ||
3036           (oldval > mark && mark > *newval))
3037         {
3038           *newval = mark;
3039           return;
3040         }
3041     }
3042 }
3043
3044 static void
3045 step_back (GtkRange *range)
3046 {
3047   GtkRangePrivate *priv = range->priv;
3048   gdouble newval;
3049   gboolean handled;
3050
3051   newval = gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_step_increment (priv->adjustment);
3052   apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
3053   g_signal_emit (range, signals[CHANGE_VALUE], 0,
3054                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
3055 }
3056
3057 static void
3058 step_forward (GtkRange *range)
3059 {
3060   GtkRangePrivate *priv = range->priv;
3061   gdouble newval;
3062   gboolean handled;
3063
3064   newval = gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_step_increment (priv->adjustment);
3065   apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
3066   g_signal_emit (range, signals[CHANGE_VALUE], 0,
3067                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
3068 }
3069
3070
3071 static void
3072 page_back (GtkRange *range)
3073 {
3074   GtkRangePrivate *priv = range->priv;
3075   gdouble newval;
3076   gboolean handled;
3077
3078   newval = gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_page_increment (priv->adjustment);
3079   apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
3080   g_signal_emit (range, signals[CHANGE_VALUE], 0,
3081                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
3082 }
3083
3084 static void
3085 page_forward (GtkRange *range)
3086 {
3087   GtkRangePrivate *priv = range->priv;
3088   gdouble newval;
3089   gboolean handled;
3090
3091   newval = gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_page_increment (priv->adjustment);
3092   apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
3093   g_signal_emit (range, signals[CHANGE_VALUE], 0,
3094                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
3095 }
3096
3097 static void
3098 scroll_begin (GtkRange *range)
3099 {
3100   GtkRangePrivate *priv = range->priv;
3101   gboolean handled;
3102
3103   g_signal_emit (range, signals[CHANGE_VALUE], 0,
3104                  GTK_SCROLL_START, gtk_adjustment_get_lower (priv->adjustment),
3105                  &handled);
3106 }
3107
3108 static void
3109 scroll_end (GtkRange *range)
3110 {
3111   GtkRangePrivate *priv = range->priv;
3112   gdouble newval;
3113   gboolean handled;
3114
3115   newval = gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment);
3116   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
3117                  &handled);
3118 }
3119
3120 static gboolean
3121 gtk_range_scroll (GtkRange     *range,
3122                   GtkScrollType scroll)
3123 {
3124   GtkRangePrivate *priv = range->priv;
3125   gdouble old_value = gtk_adjustment_get_value (priv->adjustment);
3126
3127   switch (scroll)
3128     {
3129     case GTK_SCROLL_STEP_LEFT:
3130       if (should_invert (range))
3131         step_forward (range);
3132       else
3133         step_back (range);
3134       break;
3135                     
3136     case GTK_SCROLL_STEP_UP:
3137       if (should_invert (range))
3138         step_forward (range);
3139       else
3140         step_back (range);
3141       break;
3142
3143     case GTK_SCROLL_STEP_RIGHT:
3144       if (should_invert (range))
3145         step_back (range);
3146       else
3147         step_forward (range);
3148       break;
3149                     
3150     case GTK_SCROLL_STEP_DOWN:
3151       if (should_invert (range))
3152         step_back (range);
3153       else
3154         step_forward (range);
3155       break;
3156                   
3157     case GTK_SCROLL_STEP_BACKWARD:
3158       step_back (range);
3159       break;
3160                   
3161     case GTK_SCROLL_STEP_FORWARD:
3162       step_forward (range);
3163       break;
3164
3165     case GTK_SCROLL_PAGE_LEFT:
3166       if (should_invert (range))
3167         page_forward (range);
3168       else
3169         page_back (range);
3170       break;
3171                     
3172     case GTK_SCROLL_PAGE_UP:
3173       if (should_invert (range))
3174         page_forward (range);
3175       else
3176         page_back (range);
3177       break;
3178
3179     case GTK_SCROLL_PAGE_RIGHT:
3180       if (should_invert (range))
3181         page_back (range);
3182       else
3183         page_forward (range);
3184       break;
3185                     
3186     case GTK_SCROLL_PAGE_DOWN:
3187       if (should_invert (range))
3188         page_back (range);
3189       else
3190         page_forward (range);
3191       break;
3192                   
3193     case GTK_SCROLL_PAGE_BACKWARD:
3194       page_back (range);
3195       break;
3196                   
3197     case GTK_SCROLL_PAGE_FORWARD:
3198       page_forward (range);
3199       break;
3200
3201     case GTK_SCROLL_START:
3202       scroll_begin (range);
3203       break;
3204
3205     case GTK_SCROLL_END:
3206       scroll_end (range);
3207       break;
3208
3209     case GTK_SCROLL_JUMP:
3210       /* Used by CList, range doesn't use it. */
3211       break;
3212
3213     case GTK_SCROLL_NONE:
3214       break;
3215     }
3216
3217   return gtk_adjustment_get_value (priv->adjustment) != old_value;
3218 }
3219
3220 static void
3221 gtk_range_move_slider (GtkRange     *range,
3222                        GtkScrollType scroll)
3223 {
3224   GtkRangePrivate *priv = range->priv;
3225   gboolean cursor_only;
3226
3227   g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
3228                 "gtk-keynav-cursor-only", &cursor_only,
3229                 NULL);
3230
3231   if (cursor_only)
3232     {
3233       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
3234
3235       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3236         {
3237           if (scroll == GTK_SCROLL_STEP_UP ||
3238               scroll == GTK_SCROLL_STEP_DOWN)
3239             {
3240               if (toplevel)
3241                 gtk_widget_child_focus (toplevel,
3242                                         scroll == GTK_SCROLL_STEP_UP ?
3243                                         GTK_DIR_UP : GTK_DIR_DOWN);
3244               return;
3245             }
3246         }
3247       else
3248         {
3249           if (scroll == GTK_SCROLL_STEP_LEFT ||
3250               scroll == GTK_SCROLL_STEP_RIGHT)
3251             {
3252               if (toplevel)
3253                 gtk_widget_child_focus (toplevel,
3254                                         scroll == GTK_SCROLL_STEP_LEFT ?
3255                                         GTK_DIR_LEFT : GTK_DIR_RIGHT);
3256               return;
3257             }
3258         }
3259     }
3260
3261   if (! gtk_range_scroll (range, scroll))
3262     gtk_widget_error_bell (GTK_WIDGET (range));
3263 }
3264
3265 static void
3266 gtk_range_get_props (GtkRange  *range,
3267                      gint      *slider_width,
3268                      gint      *stepper_size,
3269                      gint      *focus_width,
3270                      gint      *trough_border,
3271                      gint      *stepper_spacing,
3272                      gboolean  *trough_under_steppers,
3273                      gint      *arrow_displacement_x,
3274                      gint      *arrow_displacement_y)
3275 {
3276   GtkWidget *widget =  GTK_WIDGET (range);
3277   gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
3278   gint tmp_stepper_spacing, tmp_trough_under_steppers;
3279   gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
3280   
3281   gtk_widget_style_get (widget,
3282                         "slider-width", &tmp_slider_width,
3283                         "trough-border", &tmp_trough_border,
3284                         "stepper-size", &tmp_stepper_size,
3285                         "stepper-spacing", &tmp_stepper_spacing,
3286                         "trough-under-steppers", &tmp_trough_under_steppers,
3287                         "arrow-displacement-x", &tmp_arrow_displacement_x,
3288                         "arrow-displacement-y", &tmp_arrow_displacement_y,
3289                         NULL);
3290
3291   if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
3292     {
3293       gint focus_line_width;
3294       gint focus_padding;
3295       
3296       gtk_widget_style_get (GTK_WIDGET (range),
3297                             "focus-line-width", &focus_line_width,
3298                             "focus-padding", &focus_padding,
3299                             NULL);
3300
3301       tmp_focus_width = focus_line_width + focus_padding;
3302     }
3303   else
3304     {
3305       tmp_focus_width = 0;
3306     }
3307   
3308   if (slider_width)
3309     *slider_width = tmp_slider_width;
3310
3311   if (focus_width)
3312     *focus_width = tmp_focus_width;
3313
3314   if (trough_border)
3315     *trough_border = tmp_trough_border;
3316
3317   if (stepper_size)
3318     *stepper_size = tmp_stepper_size;
3319
3320   if (stepper_spacing)
3321     *stepper_spacing = tmp_stepper_spacing;
3322
3323   if (trough_under_steppers)
3324     *trough_under_steppers = tmp_trough_under_steppers;
3325
3326   if (arrow_displacement_x)
3327     *arrow_displacement_x = tmp_arrow_displacement_x;
3328
3329   if (arrow_displacement_y)
3330     *arrow_displacement_y = tmp_arrow_displacement_y;
3331 }
3332
3333 #define POINT_IN_RECT(xcoord, ycoord, rect) \
3334  ((xcoord) >= (rect).x &&                   \
3335   (xcoord) <  ((rect).x + (rect).width) &&  \
3336   (ycoord) >= (rect).y &&                   \
3337   (ycoord) <  ((rect).y + (rect).height))
3338
3339 /* Update mouse location, return TRUE if it changes */
3340 static gboolean
3341 gtk_range_update_mouse_location (GtkRange *range)
3342 {
3343   GtkRangePrivate *priv = range->priv;
3344   GtkAllocation allocation;
3345   gint x, y;
3346   MouseLocation old;
3347   GtkWidget *widget = GTK_WIDGET (range);
3348
3349   old = priv->mouse_location;
3350
3351   x = priv->mouse_x;
3352   y = priv->mouse_y;
3353
3354   gtk_widget_get_allocation (widget, &allocation);
3355
3356   if (priv->grab_location != MOUSE_OUTSIDE)
3357     priv->mouse_location = priv->grab_location;
3358   else if (POINT_IN_RECT (x, y, priv->stepper_a))
3359     priv->mouse_location = MOUSE_STEPPER_A;
3360   else if (POINT_IN_RECT (x, y, priv->stepper_b))
3361     priv->mouse_location = MOUSE_STEPPER_B;
3362   else if (POINT_IN_RECT (x, y, priv->stepper_c))
3363     priv->mouse_location = MOUSE_STEPPER_C;
3364   else if (POINT_IN_RECT (x, y, priv->stepper_d))
3365     priv->mouse_location = MOUSE_STEPPER_D;
3366   else if (POINT_IN_RECT (x, y, priv->slider))
3367     priv->mouse_location = MOUSE_SLIDER;
3368   else if (POINT_IN_RECT (x, y, priv->trough))
3369     priv->mouse_location = MOUSE_TROUGH;
3370   else if (POINT_IN_RECT (x, y, allocation))
3371     priv->mouse_location = MOUSE_WIDGET;
3372   else
3373     priv->mouse_location = MOUSE_OUTSIDE;
3374
3375   return old != priv->mouse_location;
3376 }
3377
3378 /* Clamp rect, border inside widget->allocation, such that we prefer
3379  * to take space from border not rect in all directions, and prefer to
3380  * give space to border over rect in one direction.
3381  */
3382 static void
3383 clamp_dimensions (GtkWidget    *widget,
3384                   GdkRectangle *rect,
3385                   GtkBorder    *border,
3386                   gboolean      border_expands_horizontally)
3387 {
3388   GtkAllocation allocation;
3389   gint extra, shortage;
3390   
3391   g_return_if_fail (rect->x == 0);
3392   g_return_if_fail (rect->y == 0);  
3393   g_return_if_fail (rect->width >= 0);
3394   g_return_if_fail (rect->height >= 0);
3395
3396   gtk_widget_get_allocation (widget, &allocation);
3397
3398   /* Width */
3399
3400   extra = allocation.width - border->left - border->right - rect->width;
3401   if (extra > 0)
3402     {
3403       if (border_expands_horizontally)
3404         {
3405           border->left += extra / 2;
3406           border->right += extra / 2 + extra % 2;
3407         }
3408       else
3409         {
3410           rect->width += extra;
3411         }
3412     }
3413   
3414   /* See if we can fit rect, if not kill the border */
3415   shortage = rect->width - allocation.width;
3416   if (shortage > 0)
3417     {
3418       rect->width = allocation.width;
3419       /* lose the border */
3420       border->left = 0;
3421       border->right = 0;
3422     }
3423   else
3424     {
3425       /* See if we can fit rect with borders */
3426       shortage = rect->width + border->left + border->right - allocation.width;
3427       if (shortage > 0)
3428         {
3429           /* Shrink borders */
3430           border->left -= shortage / 2;
3431           border->right -= shortage / 2 + shortage % 2;
3432         }
3433     }
3434
3435   /* Height */
3436
3437   extra = allocation.height - border->top - border->bottom - rect->height;
3438   if (extra > 0)
3439     {
3440       if (border_expands_horizontally)
3441         {
3442           /* don't expand border vertically */
3443           rect->height += extra;
3444         }
3445       else
3446         {
3447           border->top += extra / 2;
3448           border->bottom += extra / 2 + extra % 2;
3449         }
3450     }
3451   
3452   /* See if we can fit rect, if not kill the border */
3453   shortage = rect->height - allocation.height;
3454   if (shortage > 0)
3455     {
3456       rect->height = allocation.height;
3457       /* lose the border */
3458       border->top = 0;
3459       border->bottom = 0;
3460     }
3461   else
3462     {
3463       /* See if we can fit rect with borders */
3464       shortage = rect->height + border->top + border->bottom - allocation.height;
3465       if (shortage > 0)
3466         {
3467           /* Shrink borders */
3468           border->top -= shortage / 2;
3469           border->bottom -= shortage / 2 + shortage % 2;
3470         }
3471     }
3472 }
3473
3474 static void
3475 gtk_range_calc_request (GtkRange      *range,
3476                         gint           slider_width,
3477                         gint           stepper_size,
3478                         gint           focus_width,
3479                         gint           trough_border,
3480                         gint           stepper_spacing,
3481                         GdkRectangle  *range_rect,
3482                         GtkBorder     *border,
3483                         gint          *n_steppers_p,
3484                         gboolean      *has_steppers_ab,
3485                         gboolean      *has_steppers_cd,
3486                         gint          *slider_length_p)
3487 {
3488   GtkRangePrivate *priv = range->priv;
3489   gint slider_length;
3490   gint n_steppers;
3491   gint n_steppers_ab;
3492   gint n_steppers_cd;
3493
3494   border->left = 0;
3495   border->right = 0;
3496   border->top = 0;
3497   border->bottom = 0;
3498
3499   if (GTK_RANGE_GET_CLASS (range)->get_range_border)
3500     GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
3501
3502   n_steppers_ab = 0;
3503   n_steppers_cd = 0;
3504
3505   if (priv->has_stepper_a)
3506     n_steppers_ab += 1;
3507   if (priv->has_stepper_b)
3508     n_steppers_ab += 1;
3509   if (priv->has_stepper_c)
3510     n_steppers_cd += 1;
3511   if (priv->has_stepper_d)
3512     n_steppers_cd += 1;
3513
3514   n_steppers = n_steppers_ab + n_steppers_cd;
3515
3516   slider_length = priv->min_slider_size;
3517
3518   range_rect->x = 0;
3519   range_rect->y = 0;
3520   
3521   /* We never expand to fill available space in the small dimension
3522    * (i.e. vertical scrollbars are always a fixed width)
3523    */
3524   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
3525     {
3526       range_rect->width = (focus_width + trough_border) * 2 + slider_width;
3527       range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3528
3529       if (n_steppers_ab > 0)
3530         range_rect->height += stepper_spacing;
3531
3532       if (n_steppers_cd > 0)
3533         range_rect->height += stepper_spacing;
3534     }
3535   else
3536     {
3537       range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3538       range_rect->height = (focus_width + trough_border) * 2 + slider_width;
3539
3540       if (n_steppers_ab > 0)
3541         range_rect->width += stepper_spacing;
3542
3543       if (n_steppers_cd > 0)
3544         range_rect->width += stepper_spacing;
3545     }
3546
3547   if (n_steppers_p)
3548     *n_steppers_p = n_steppers;
3549
3550   if (has_steppers_ab)
3551     *has_steppers_ab = (n_steppers_ab > 0);
3552
3553   if (has_steppers_cd)
3554     *has_steppers_cd = (n_steppers_cd > 0);
3555
3556   if (slider_length_p)
3557     *slider_length_p = slider_length;
3558 }
3559
3560 static void
3561 gtk_range_calc_layout (GtkRange *range,
3562                        gdouble   adjustment_value)
3563 {
3564   GtkRangePrivate *priv = range->priv;
3565   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
3566   gint slider_length;
3567   GtkBorder border;
3568   gint n_steppers;
3569   gboolean has_steppers_ab;
3570   gboolean has_steppers_cd;
3571   gboolean trough_under_steppers;
3572   GdkRectangle range_rect;
3573   GtkWidget *widget;
3574
3575   if (!priv->need_recalc)
3576     return;
3577
3578   /* If we have a too-small allocation, we prefer the steppers over
3579    * the trough/slider, probably the steppers are a more useful
3580    * feature in small spaces.
3581    *
3582    * Also, we prefer to draw the range itself rather than the border
3583    * areas if there's a conflict, since the borders will be decoration
3584    * not controls. Though this depends on subclasses cooperating by
3585    * not drawing on priv->range_rect.
3586    */
3587
3588   widget = GTK_WIDGET (range);
3589
3590   gtk_range_get_props (range,
3591                        &slider_width, &stepper_size,
3592                        &focus_width, &trough_border,
3593                        &stepper_spacing, &trough_under_steppers,
3594                        NULL, NULL);
3595
3596   gtk_range_calc_request (range, 
3597                           slider_width, stepper_size,
3598                           focus_width, trough_border, stepper_spacing,
3599                           &range_rect, &border, &n_steppers,
3600                           &has_steppers_ab, &has_steppers_cd, &slider_length);
3601   
3602   /* We never expand to fill available space in the small dimension
3603    * (i.e. vertical scrollbars are always a fixed width)
3604    */
3605   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
3606     {
3607       clamp_dimensions (widget, &range_rect, &border, TRUE);
3608     }
3609   else
3610     {
3611       clamp_dimensions (widget, &range_rect, &border, FALSE);
3612     }
3613   
3614   range_rect.x = border.left;
3615   range_rect.y = border.top;
3616
3617   priv->range_rect = range_rect;
3618
3619   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
3620     {
3621       gint stepper_width, stepper_height;
3622
3623       /* Steppers are the width of the range, and stepper_size in
3624        * height, or if we don't have enough height, divided equally
3625        * among available space.
3626        */
3627       stepper_width = range_rect.width - focus_width * 2;
3628
3629       if (trough_under_steppers)
3630         stepper_width -= trough_border * 2;
3631
3632       if (stepper_width < 1)
3633         stepper_width = range_rect.width; /* screw the trough border */
3634
3635       if (n_steppers == 0)
3636         stepper_height = 0; /* avoid divide by n_steppers */
3637       else
3638         stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
3639
3640       /* Stepper A */
3641       
3642       priv->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3643       priv->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3644
3645       if (priv->has_stepper_a)
3646         {
3647           priv->stepper_a.width = stepper_width;
3648           priv->stepper_a.height = stepper_height;
3649         }
3650       else
3651         {
3652           priv->stepper_a.width = 0;
3653           priv->stepper_a.height = 0;
3654         }
3655
3656       /* Stepper B */
3657       
3658       priv->stepper_b.x = priv->stepper_a.x;
3659       priv->stepper_b.y = priv->stepper_a.y + priv->stepper_a.height;
3660
3661       if (priv->has_stepper_b)
3662         {
3663           priv->stepper_b.width = stepper_width;
3664           priv->stepper_b.height = stepper_height;
3665         }
3666       else
3667         {
3668           priv->stepper_b.width = 0;
3669           priv->stepper_b.height = 0;
3670         }
3671
3672       /* Stepper D */
3673
3674       if (priv->has_stepper_d)
3675         {
3676           priv->stepper_d.width = stepper_width;
3677           priv->stepper_d.height = stepper_height;
3678         }
3679       else
3680         {
3681           priv->stepper_d.width = 0;
3682           priv->stepper_d.height = 0;
3683         }
3684       
3685       priv->stepper_d.x = priv->stepper_a.x;
3686       priv->stepper_d.y = range_rect.y + range_rect.height - priv->stepper_d.height - focus_width - trough_border * trough_under_steppers;
3687
3688       /* Stepper C */
3689
3690       if (priv->has_stepper_c)
3691         {
3692           priv->stepper_c.width = stepper_width;
3693           priv->stepper_c.height = stepper_height;
3694         }
3695       else
3696         {
3697           priv->stepper_c.width = 0;
3698           priv->stepper_c.height = 0;
3699         }
3700       
3701       priv->stepper_c.x = priv->stepper_a.x;
3702       priv->stepper_c.y = priv->stepper_d.y - priv->stepper_c.height;
3703
3704       /* Now the trough is the remaining space between steppers B and C,
3705        * if any, minus spacing
3706        */
3707       priv->trough.x = range_rect.x;
3708       priv->trough.y = priv->stepper_b.y + priv->stepper_b.height + stepper_spacing * has_steppers_ab;
3709       priv->trough.width = range_rect.width;
3710       priv->trough.height = priv->stepper_c.y - priv->trough.y - stepper_spacing * has_steppers_cd;
3711
3712       /* Slider fits into the trough, with stepper_spacing on either side,
3713        * and the size/position based on the adjustment or fixed, depending.
3714        */
3715       priv->slider.x = priv->trough.x + focus_width + trough_border;
3716       priv->slider.width = priv->trough.width - (focus_width + trough_border) * 2;
3717
3718       /* Compute slider position/length */
3719       {
3720         gint y, bottom, top, height;
3721         
3722         top = priv->trough.y;
3723         bottom = priv->trough.y + priv->trough.height;
3724
3725         if (! trough_under_steppers)
3726           {
3727             top += trough_border;
3728             bottom -= trough_border;
3729           }
3730
3731         /* slider height is the fraction (page_size /
3732          * total_adjustment_range) times the trough height in pixels
3733          */
3734
3735         if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
3736           height = ((bottom - top) * (gtk_adjustment_get_page_size (priv->adjustment) /
3737                                        (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment))));
3738         else
3739           height = priv->min_slider_size;
3740
3741         if (height < priv->min_slider_size ||
3742             priv->slider_size_fixed)
3743           height = priv->min_slider_size;
3744
3745         height = MIN (height, priv->trough.height);
3746         
3747         y = top;
3748
3749         if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment) != 0)
3750           y += (bottom - top - height) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
3751                                           (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
3752
3753         y = CLAMP (y, top, bottom);
3754         
3755         if (should_invert (range))
3756           y = bottom - (y - top + height);
3757         
3758         priv->slider.y = y;
3759         priv->slider.height = height;
3760
3761         /* These are publically exported */
3762         priv->slider_start = priv->slider.y;
3763         priv->slider_end = priv->slider.y + priv->slider.height;
3764       }
3765     }
3766   else
3767     {
3768       gint stepper_width, stepper_height;
3769
3770       /* Steppers are the height of the range, and stepper_size in
3771        * width, or if we don't have enough width, divided equally
3772        * among available space.
3773        */
3774       stepper_height = range_rect.height + focus_width * 2;
3775
3776       if (trough_under_steppers)
3777         stepper_height -= trough_border * 2;
3778
3779       if (stepper_height < 1)
3780         stepper_height = range_rect.height; /* screw the trough border */
3781
3782       if (n_steppers == 0)
3783         stepper_width = 0; /* avoid divide by n_steppers */
3784       else
3785         stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3786
3787       /* Stepper A */
3788       
3789       priv->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3790       priv->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3791
3792       if (priv->has_stepper_a)
3793         {
3794           priv->stepper_a.width = stepper_width;
3795           priv->stepper_a.height = stepper_height;
3796         }
3797       else
3798         {
3799           priv->stepper_a.width = 0;
3800           priv->stepper_a.height = 0;
3801         }
3802
3803       /* Stepper B */
3804       
3805       priv->stepper_b.x = priv->stepper_a.x + priv->stepper_a.width;
3806       priv->stepper_b.y = priv->stepper_a.y;
3807
3808       if (priv->has_stepper_b)
3809         {
3810           priv->stepper_b.width = stepper_width;
3811           priv->stepper_b.height = stepper_height;
3812         }
3813       else
3814         {
3815           priv->stepper_b.width = 0;
3816           priv->stepper_b.height = 0;
3817         }
3818
3819       /* Stepper D */
3820
3821       if (priv->has_stepper_d)
3822         {
3823           priv->stepper_d.width = stepper_width;
3824           priv->stepper_d.height = stepper_height;
3825         }
3826       else
3827         {
3828           priv->stepper_d.width = 0;
3829           priv->stepper_d.height = 0;
3830         }
3831
3832       priv->stepper_d.x = range_rect.x + range_rect.width - priv->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3833       priv->stepper_d.y = priv->stepper_a.y;
3834
3835
3836       /* Stepper C */
3837
3838       if (priv->has_stepper_c)
3839         {
3840           priv->stepper_c.width = stepper_width;
3841           priv->stepper_c.height = stepper_height;
3842         }
3843       else
3844         {
3845           priv->stepper_c.width = 0;
3846           priv->stepper_c.height = 0;
3847         }
3848       
3849       priv->stepper_c.x = priv->stepper_d.x - priv->stepper_c.width;
3850       priv->stepper_c.y = priv->stepper_a.y;
3851
3852       /* Now the trough is the remaining space between steppers B and C,
3853        * if any
3854        */
3855       priv->trough.x = priv->stepper_b.x + priv->stepper_b.width + stepper_spacing * has_steppers_ab;
3856       priv->trough.y = range_rect.y;
3857
3858       priv->trough.width = priv->stepper_c.x - priv->trough.x - stepper_spacing * has_steppers_cd;
3859       priv->trough.height = range_rect.height;
3860
3861       /* Slider fits into the trough, with stepper_spacing on either side,
3862        * and the size/position based on the adjustment or fixed, depending.
3863        */
3864       priv->slider.y = priv->trough.y + focus_width + trough_border;
3865       priv->slider.height = priv->trough.height - (focus_width + trough_border) * 2;
3866
3867       /* Compute slider position/length */
3868       {
3869         gint x, left, right, width;
3870         
3871         left = priv->trough.x;
3872         right = priv->trough.x + priv->trough.width;
3873
3874         if (! trough_under_steppers)
3875           {
3876             left += trough_border;
3877             right -= trough_border;
3878           }
3879
3880         /* slider width is the fraction (page_size /
3881          * total_adjustment_range) times the trough width in pixels
3882          */
3883
3884         if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
3885           width = ((right - left) * (gtk_adjustment_get_page_size (priv->adjustment) /
3886                                    (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment))));
3887         else
3888           width = priv->min_slider_size;
3889
3890         if (width < priv->min_slider_size ||
3891             priv->slider_size_fixed)
3892           width = priv->min_slider_size;
3893
3894         width = MIN (width, priv->trough.width);
3895         
3896         x = left;
3897
3898         if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment) != 0)
3899           x += (right - left - width) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
3900                                          (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
3901         
3902         x = CLAMP (x, left, right);
3903         
3904         if (should_invert (range))
3905           x = right - (x - left + width);
3906         
3907         priv->slider.x = x;
3908         priv->slider.width = width;
3909
3910         /* These are publically exported */
3911         priv->slider_start = priv->slider.x;
3912         priv->slider_end = priv->slider.x + priv->slider.width;
3913       }
3914     }
3915   
3916   gtk_range_update_mouse_location (range);
3917
3918   switch (priv->upper_sensitivity)
3919     {
3920     case GTK_SENSITIVITY_AUTO:
3921       priv->upper_sensitive =
3922         (gtk_adjustment_get_value (priv->adjustment) <
3923          (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
3924       break;
3925
3926     case GTK_SENSITIVITY_ON:
3927       priv->upper_sensitive = TRUE;
3928       break;
3929
3930     case GTK_SENSITIVITY_OFF:
3931       priv->upper_sensitive = FALSE;
3932       break;
3933     }
3934
3935   switch (priv->lower_sensitivity)
3936     {
3937     case GTK_SENSITIVITY_AUTO:
3938       priv->lower_sensitive =
3939         (gtk_adjustment_get_value (priv->adjustment) > gtk_adjustment_get_lower (priv->adjustment));
3940       break;
3941
3942     case GTK_SENSITIVITY_ON:
3943       priv->lower_sensitive = TRUE;
3944       break;
3945
3946     case GTK_SENSITIVITY_OFF:
3947       priv->lower_sensitive = FALSE;
3948       break;
3949     }
3950 }
3951
3952 static GdkRectangle*
3953 get_area (GtkRange     *range,
3954           MouseLocation location)
3955 {
3956   GtkRangePrivate *priv = range->priv;
3957
3958   switch (location)
3959     {
3960     case MOUSE_STEPPER_A:
3961       return &priv->stepper_a;
3962     case MOUSE_STEPPER_B:
3963       return &priv->stepper_b;
3964     case MOUSE_STEPPER_C:
3965       return &priv->stepper_c;
3966     case MOUSE_STEPPER_D:
3967       return &priv->stepper_d;
3968     case MOUSE_TROUGH:
3969       return &priv->trough;
3970     case MOUSE_SLIDER:
3971       return &priv->slider;
3972     case MOUSE_WIDGET:
3973     case MOUSE_OUTSIDE:
3974       break;
3975     }
3976
3977   g_warning (G_STRLOC": bug");
3978   return NULL;
3979 }
3980
3981 static void
3982 gtk_range_calc_marks (GtkRange *range)
3983 {
3984   GtkRangePrivate *priv = range->priv;
3985   gint i;
3986
3987   if (!priv->recalc_marks)
3988     return;
3989
3990   priv->recalc_marks = FALSE;
3991
3992   for (i = 0; i < priv->n_marks; i++)
3993     {
3994       priv->need_recalc = TRUE;
3995       gtk_range_calc_layout (range, priv->marks[i]);
3996       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
3997         priv->mark_pos[i] = priv->slider.x + priv->slider.width / 2;
3998       else
3999         priv->mark_pos[i] = priv->slider.y + priv->slider.height / 2;
4000     }
4001
4002   priv->need_recalc = TRUE;
4003 }
4004
4005 static gboolean
4006 gtk_range_real_change_value (GtkRange     *range,
4007                              GtkScrollType scroll,
4008                              gdouble       value)
4009 {
4010   GtkRangePrivate *priv = range->priv;
4011
4012   /* potentially adjust the bounds _before_ we clamp */
4013   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
4014
4015   if (priv->restrict_to_fill_level)
4016     value = MIN (value, MAX (gtk_adjustment_get_lower (priv->adjustment),
4017                              priv->fill_level));
4018
4019   value = CLAMP (value, gtk_adjustment_get_lower (priv->adjustment),
4020                  (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
4021
4022   if (priv->round_digits >= 0)
4023     {
4024       gdouble power;
4025       gint i;
4026
4027       i = priv->round_digits;
4028       power = 1;
4029       while (i--)
4030         power *= 10;
4031       
4032       value = floor ((value * power) + 0.5) / power;
4033     }
4034
4035   if (gtk_adjustment_get_value (priv->adjustment) != value)
4036     {
4037       priv->need_recalc = TRUE;
4038
4039       gtk_widget_queue_draw (GTK_WIDGET (range));
4040
4041       gtk_adjustment_set_value (priv->adjustment, value);
4042     }
4043   return FALSE;
4044 }
4045
4046 struct _GtkRangeStepTimer
4047 {
4048   guint timeout_id;
4049   GtkScrollType step;
4050 };
4051
4052 static gboolean
4053 second_timeout (gpointer data)
4054 {
4055   GtkRange *range = GTK_RANGE (data);
4056   GtkRangePrivate *priv = range->priv;
4057
4058   gtk_range_scroll (range, priv->timer->step);
4059
4060   return TRUE;
4061 }
4062
4063 static gboolean
4064 initial_timeout (gpointer data)
4065 {
4066   GtkRange *range = GTK_RANGE (data);
4067   GtkRangePrivate *priv = range->priv;
4068   GtkSettings *settings;
4069   guint        timeout;
4070
4071   settings = gtk_widget_get_settings (GTK_WIDGET (data));
4072   g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4073
4074   priv->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4075                                                      second_timeout,
4076                                                      range);
4077   /* remove self */
4078   return FALSE;
4079 }
4080
4081 static void
4082 gtk_range_add_step_timer (GtkRange      *range,
4083                           GtkScrollType  step)
4084 {
4085   GtkRangePrivate *priv = range->priv;
4086   GtkSettings *settings;
4087   guint        timeout;
4088
4089   g_return_if_fail (priv->timer == NULL);
4090   g_return_if_fail (step != GTK_SCROLL_NONE);
4091
4092   settings = gtk_widget_get_settings (GTK_WIDGET (range));
4093   g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4094
4095   priv->timer = g_new (GtkRangeStepTimer, 1);
4096
4097   priv->timer->timeout_id = gdk_threads_add_timeout (timeout,
4098                                                      initial_timeout,
4099                                                      range);
4100   priv->timer->step = step;
4101
4102   gtk_range_scroll (range, priv->timer->step);
4103 }
4104
4105 static void
4106 gtk_range_remove_step_timer (GtkRange *range)
4107 {
4108   GtkRangePrivate *priv = range->priv;
4109
4110   if (priv->timer)
4111     {
4112       if (priv->timer->timeout_id != 0)
4113         g_source_remove (priv->timer->timeout_id);
4114
4115       g_free (priv->timer);
4116
4117       priv->timer = NULL;
4118     }
4119 }
4120
4121 void
4122 _gtk_range_set_has_origin (GtkRange *range,
4123                            gboolean  has_origin)
4124 {
4125   range->priv->has_origin = has_origin;
4126 }
4127
4128 gboolean
4129 _gtk_range_get_has_origin (GtkRange *range)
4130 {
4131   return range->priv->has_origin;
4132 }
4133
4134 void
4135 _gtk_range_set_stop_values (GtkRange *range,
4136                             gdouble  *values,
4137                             gint      n_values)
4138 {
4139   GtkRangePrivate *priv = range->priv;
4140   gint i;
4141
4142   g_free (priv->marks);
4143   priv->marks = g_new (gdouble, n_values);
4144
4145   g_free (priv->mark_pos);
4146   priv->mark_pos = g_new (gint, n_values);
4147
4148   priv->n_marks = n_values;
4149
4150   for (i = 0; i < n_values; i++) 
4151     priv->marks[i] = values[i];
4152
4153   priv->recalc_marks = TRUE;
4154 }
4155
4156 gint
4157 _gtk_range_get_stop_positions (GtkRange  *range,
4158                                gint     **values)
4159 {
4160   GtkRangePrivate *priv = range->priv;
4161
4162   gtk_range_calc_marks (range);
4163
4164   if (values)
4165     *values = g_memdup (priv->mark_pos, priv->n_marks * sizeof (gint));
4166
4167   return priv->n_marks;
4168 }
4169
4170 /**
4171  * gtk_range_set_round_digits:
4172  * @range: a #GtkRange
4173  * @round_digits: the precision in digits, or -1
4174  *
4175  * Sets the number of digits to round the value to when
4176  * it changes. See #GtkRange::change-value.
4177  *
4178  * Since: 2.24
4179  */
4180 void
4181 gtk_range_set_round_digits (GtkRange *range,
4182                             gint      round_digits)
4183 {
4184   g_return_if_fail (GTK_IS_RANGE (range));
4185   g_return_if_fail (round_digits >= -1);
4186
4187   range->priv->round_digits = round_digits;
4188
4189   g_object_notify (G_OBJECT (range), "round-digits");
4190 }
4191
4192 /**
4193  * gtk_range_get_round_digits:
4194  * @range: a #GtkRange
4195  *
4196  * Gets the number of digits to round the value to when
4197  * it changes. See #GtkRange::change-value.
4198  *
4199  * Return value: the number of digits to round to
4200  *
4201  * Since: 2.24
4202  */
4203 gint
4204 gtk_range_get_round_digits (GtkRange *range)
4205 {
4206   g_return_val_if_fail (GTK_IS_RANGE (range), -1);
4207
4208   return range->priv->round_digits;
4209 }
4210
4211 void
4212 _gtk_range_set_steppers (GtkRange      *range,
4213                          gboolean       has_a,
4214                          gboolean       has_b,
4215                          gboolean       has_c,
4216                          gboolean       has_d)
4217 {
4218   range->priv->has_stepper_a = has_a;
4219   range->priv->has_stepper_b = has_b;
4220   range->priv->has_stepper_c = has_c;
4221   range->priv->has_stepper_d = has_d;
4222 }