]> Pileus Git - ~andy/gtk/blob - gtk/gtkrange.c
Bug 559914 – eog doesn't apply paper setup.
[~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 <gdk/gdkkeysyms.h>
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkorientable.h"
37 #include "gtkrange.h"
38 #include "gtkscrollbar.h"
39 #include "gtkprivate.h"
40 #include "gtkintl.h"
41 #include "gtkalias.h"
42
43 #define SCROLL_DELAY_FACTOR 5    /* Scroll repeat multiplier */
44 #define UPDATE_DELAY        300  /* Delay for queued update */
45
46 enum {
47   PROP_0,
48   PROP_ORIENTATION,
49   PROP_UPDATE_POLICY,
50   PROP_ADJUSTMENT,
51   PROP_INVERTED,
52   PROP_LOWER_STEPPER_SENSITIVITY,
53   PROP_UPPER_STEPPER_SENSITIVITY,
54   PROP_SHOW_FILL_LEVEL,
55   PROP_RESTRICT_TO_FILL_LEVEL,
56   PROP_FILL_LEVEL
57 };
58
59 enum {
60   VALUE_CHANGED,
61   ADJUST_BOUNDS,
62   MOVE_SLIDER,
63   CHANGE_VALUE,
64   LAST_SIGNAL
65 };
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 #define GTK_RANGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RANGE, GtkRangeLayout))
79
80 struct _GtkRangeLayout
81 {
82   /* These are in widget->window coordinates */
83   GdkRectangle stepper_a;
84   GdkRectangle stepper_b;
85   GdkRectangle stepper_c;
86   GdkRectangle stepper_d;
87   /* The trough rectangle is the area the thumb can slide in, not the
88    * entire range_rect
89    */
90   GdkRectangle trough;
91   GdkRectangle slider;
92
93   /* Layout-related state */
94   
95   MouseLocation mouse_location;
96   /* last mouse coords we got, or -1 if mouse is outside the range */
97   gint mouse_x;
98   gint mouse_y;
99
100   /* "grabbed" mouse location, OUTSIDE for no grab */
101   MouseLocation grab_location;
102   guint grab_button : 8; /* 0 if none */
103
104   /* Stepper sensitivity */
105   guint lower_sensitive : 1;
106   guint upper_sensitive : 1;
107
108   /* Fill level */
109   guint show_fill_level : 1;
110   guint restrict_to_fill_level : 1;
111
112   GtkSensitivityType lower_sensitivity;
113   GtkSensitivityType upper_sensitivity;
114   guint repaint_id;
115
116   gdouble fill_level;
117
118   GQuark slider_detail_quark;
119   GQuark stepper_detail_quark;
120 };
121
122
123 static void gtk_range_set_property   (GObject          *object,
124                                       guint             prop_id,
125                                       const GValue     *value,
126                                       GParamSpec       *pspec);
127 static void gtk_range_get_property   (GObject          *object,
128                                       guint             prop_id,
129                                       GValue           *value,
130                                       GParamSpec       *pspec);
131 static void gtk_range_destroy        (GtkObject        *object);
132 static void gtk_range_size_request   (GtkWidget        *widget,
133                                       GtkRequisition   *requisition);
134 static void gtk_range_size_allocate  (GtkWidget        *widget,
135                                       GtkAllocation    *allocation);
136 static void gtk_range_realize        (GtkWidget        *widget);
137 static void gtk_range_unrealize      (GtkWidget        *widget);
138 static void gtk_range_map            (GtkWidget        *widget);
139 static void gtk_range_unmap          (GtkWidget        *widget);
140 static gboolean gtk_range_expose         (GtkWidget        *widget,
141                                       GdkEventExpose   *event);
142 static gboolean gtk_range_button_press   (GtkWidget        *widget,
143                                       GdkEventButton   *event);
144 static gboolean gtk_range_button_release (GtkWidget        *widget,
145                                       GdkEventButton   *event);
146 static gboolean gtk_range_motion_notify  (GtkWidget        *widget,
147                                       GdkEventMotion   *event);
148 static gboolean gtk_range_enter_notify   (GtkWidget        *widget,
149                                       GdkEventCrossing *event);
150 static gboolean gtk_range_leave_notify   (GtkWidget        *widget,
151                                       GdkEventCrossing *event);
152 static gboolean gtk_range_grab_broken (GtkWidget          *widget,
153                                        GdkEventGrabBroken *event);
154 static void gtk_range_grab_notify    (GtkWidget          *widget,
155                                       gboolean            was_grabbed);
156 static void gtk_range_state_changed  (GtkWidget          *widget,
157                                       GtkStateType        previous_state);
158 static gboolean gtk_range_scroll_event   (GtkWidget        *widget,
159                                       GdkEventScroll   *event);
160 static void gtk_range_style_set      (GtkWidget        *widget,
161                                       GtkStyle         *previous_style);
162 static void update_slider_position   (GtkRange         *range,
163                                       gint              mouse_x,
164                                       gint              mouse_y);
165 static void stop_scrolling           (GtkRange         *range);
166
167 /* Range methods */
168
169 static void gtk_range_move_slider              (GtkRange         *range,
170                                                 GtkScrollType     scroll);
171
172 /* Internals */
173 static gboolean      gtk_range_scroll                   (GtkRange      *range,
174                                                          GtkScrollType  scroll);
175 static gboolean      gtk_range_update_mouse_location    (GtkRange      *range);
176 static void          gtk_range_calc_layout              (GtkRange      *range,
177                                                          gdouble        adjustment_value);
178 static void          gtk_range_get_props                (GtkRange      *range,
179                                                          gint          *slider_width,
180                                                          gint          *stepper_size,
181                                                          gint          *focus_width,
182                                                          gint          *trough_border,
183                                                          gint          *stepper_spacing,
184                                                          gboolean      *trough_under_steppers,
185                                                          gint          *arrow_displacement_x,
186                                                          gint          *arrow_displacement_y);
187 static void          gtk_range_calc_request             (GtkRange      *range,
188                                                          gint           slider_width,
189                                                          gint           stepper_size,
190                                                          gint           focus_width,
191                                                          gint           trough_border,
192                                                          gint           stepper_spacing,
193                                                          GdkRectangle  *range_rect,
194                                                          GtkBorder     *border,
195                                                          gint          *n_steppers_p,
196                                                          gboolean      *has_steppers_ab,
197                                                          gboolean      *has_steppers_cd,
198                                                          gint          *slider_length_p);
199 static void          gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
200                                                          gpointer       data);
201 static void          gtk_range_adjustment_changed       (GtkAdjustment *adjustment,
202                                                          gpointer       data);
203 static void          gtk_range_add_step_timer           (GtkRange      *range,
204                                                          GtkScrollType  step);
205 static void          gtk_range_remove_step_timer        (GtkRange      *range);
206 static void          gtk_range_reset_update_timer       (GtkRange      *range);
207 static void          gtk_range_remove_update_timer      (GtkRange      *range);
208 static GdkRectangle* get_area                           (GtkRange      *range,
209                                                          MouseLocation  location);
210 static gboolean      gtk_range_real_change_value        (GtkRange      *range,
211                                                          GtkScrollType  scroll,
212                                                          gdouble        value);
213 static void          gtk_range_update_value             (GtkRange      *range);
214 static gboolean      gtk_range_key_press                (GtkWidget     *range,
215                                                          GdkEventKey   *event);
216
217
218 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
219                                   G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
220                                                          NULL))
221
222 static guint signals[LAST_SIGNAL];
223
224
225 static void
226 gtk_range_class_init (GtkRangeClass *class)
227 {
228   GObjectClass   *gobject_class;
229   GtkObjectClass *object_class;
230   GtkWidgetClass *widget_class;
231
232   gobject_class = G_OBJECT_CLASS (class);
233   object_class = (GtkObjectClass*) class;
234   widget_class = (GtkWidgetClass*) class;
235
236   gobject_class->set_property = gtk_range_set_property;
237   gobject_class->get_property = gtk_range_get_property;
238
239   object_class->destroy = gtk_range_destroy;
240
241   widget_class->size_request = gtk_range_size_request;
242   widget_class->size_allocate = gtk_range_size_allocate;
243   widget_class->realize = gtk_range_realize;
244   widget_class->unrealize = gtk_range_unrealize;  
245   widget_class->map = gtk_range_map;
246   widget_class->unmap = gtk_range_unmap;
247   widget_class->expose_event = gtk_range_expose;
248   widget_class->button_press_event = gtk_range_button_press;
249   widget_class->button_release_event = gtk_range_button_release;
250   widget_class->motion_notify_event = gtk_range_motion_notify;
251   widget_class->scroll_event = gtk_range_scroll_event;
252   widget_class->enter_notify_event = gtk_range_enter_notify;
253   widget_class->leave_notify_event = gtk_range_leave_notify;
254   widget_class->grab_broken_event = gtk_range_grab_broken;
255   widget_class->grab_notify = gtk_range_grab_notify;
256   widget_class->state_changed = gtk_range_state_changed;
257   widget_class->style_set = gtk_range_style_set;
258   widget_class->key_press_event = gtk_range_key_press;
259
260   class->move_slider = gtk_range_move_slider;
261   class->change_value = gtk_range_real_change_value;
262
263   class->slider_detail = "slider";
264   class->stepper_detail = "stepper";
265
266   /**
267    * GtkRange::value-changed:
268    * @range: the #GtkRange
269    *
270    * Emitted when the range value changes.
271    */
272   signals[VALUE_CHANGED] =
273     g_signal_new (I_("value-changed"),
274                   G_TYPE_FROM_CLASS (gobject_class),
275                   G_SIGNAL_RUN_LAST,
276                   G_STRUCT_OFFSET (GtkRangeClass, value_changed),
277                   NULL, NULL,
278                   _gtk_marshal_VOID__VOID,
279                   G_TYPE_NONE, 0);
280   
281   signals[ADJUST_BOUNDS] =
282     g_signal_new (I_("adjust-bounds"),
283                   G_TYPE_FROM_CLASS (gobject_class),
284                   G_SIGNAL_RUN_LAST,
285                   G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
286                   NULL, NULL,
287                   _gtk_marshal_VOID__DOUBLE,
288                   G_TYPE_NONE, 1,
289                   G_TYPE_DOUBLE);
290   
291   /**
292    * GtkRange::move-slider:
293    * @range: the #GtkRange
294    * @step: how to move the slider
295    *
296    * Virtual function that moves the slider. Used for keybindings.
297    */
298   signals[MOVE_SLIDER] =
299     g_signal_new (I_("move-slider"),
300                   G_TYPE_FROM_CLASS (gobject_class),
301                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
302                   G_STRUCT_OFFSET (GtkRangeClass, move_slider),
303                   NULL, NULL,
304                   _gtk_marshal_VOID__ENUM,
305                   G_TYPE_NONE, 1,
306                   GTK_TYPE_SCROLL_TYPE);
307
308   /**
309    * GtkRange::change-value:
310    * @range: the range that received the signal
311    * @scroll: the type of scroll action that was performed
312    * @value: the new value resulting from the scroll action
313    * @returns: %TRUE to prevent other handlers from being invoked for the
314    * signal, %FALSE to propagate the signal further
315    *
316    * The ::change-value signal is emitted when a scroll action is
317    * performed on a range.  It allows an application to determine the
318    * type of scroll event that occurred and the resultant new value.
319    * The application can handle the event itself and return %TRUE to
320    * prevent further processing.  Or, by returning %FALSE, it can pass
321    * the event to other handlers until the default GTK+ handler is
322    * reached.
323    *
324    * The value parameter is unrounded.  An application that overrides
325    * the ::change-value signal is responsible for clamping the value to
326    * the desired number of decimal digits; the default GTK+ handler 
327    * clamps the value based on @range->round_digits.
328    *
329    * It is not possible to use delayed update policies in an overridden
330    * ::change-value handler.
331    *
332    * Since: 2.6
333    */
334   signals[CHANGE_VALUE] =
335     g_signal_new (I_("change-value"),
336                   G_TYPE_FROM_CLASS (gobject_class),
337                   G_SIGNAL_RUN_LAST,
338                   G_STRUCT_OFFSET (GtkRangeClass, change_value),
339                   _gtk_boolean_handled_accumulator, NULL,
340                   _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
341                   G_TYPE_BOOLEAN, 2,
342                   GTK_TYPE_SCROLL_TYPE,
343                   G_TYPE_DOUBLE);
344
345   g_object_class_override_property (gobject_class,
346                                     PROP_ORIENTATION,
347                                     "orientation");
348
349   g_object_class_install_property (gobject_class,
350                                    PROP_UPDATE_POLICY,
351                                    g_param_spec_enum ("update-policy",
352                                                       P_("Update policy"),
353                                                       P_("How the range should be updated on the screen"),
354                                                       GTK_TYPE_UPDATE_TYPE,
355                                                       GTK_UPDATE_CONTINUOUS,
356                                                       GTK_PARAM_READWRITE));
357   
358   g_object_class_install_property (gobject_class,
359                                    PROP_ADJUSTMENT,
360                                    g_param_spec_object ("adjustment",
361                                                         P_("Adjustment"),
362                                                         P_("The GtkAdjustment that contains the current value of this range object"),
363                                                         GTK_TYPE_ADJUSTMENT,
364                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
365
366   g_object_class_install_property (gobject_class,
367                                    PROP_INVERTED,
368                                    g_param_spec_boolean ("inverted",
369                                                         P_("Inverted"),
370                                                         P_("Invert direction slider moves to increase range value"),
371                                                          FALSE,
372                                                          GTK_PARAM_READWRITE));
373
374   g_object_class_install_property (gobject_class,
375                                    PROP_LOWER_STEPPER_SENSITIVITY,
376                                    g_param_spec_enum ("lower-stepper-sensitivity",
377                                                       P_("Lower stepper sensitivity"),
378                                                       P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
379                                                       GTK_TYPE_SENSITIVITY_TYPE,
380                                                       GTK_SENSITIVITY_AUTO,
381                                                       GTK_PARAM_READWRITE));
382
383   g_object_class_install_property (gobject_class,
384                                    PROP_UPPER_STEPPER_SENSITIVITY,
385                                    g_param_spec_enum ("upper-stepper-sensitivity",
386                                                       P_("Upper stepper sensitivity"),
387                                                       P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
388                                                       GTK_TYPE_SENSITIVITY_TYPE,
389                                                       GTK_SENSITIVITY_AUTO,
390                                                       GTK_PARAM_READWRITE));
391
392   /**
393    * GtkRange:show-fill-level:
394    *
395    * The show-fill-level property controls whether fill level indicator
396    * graphics are displayed on the trough. See
397    * gtk_range_set_show_fill_level().
398    *
399    * Since: 2.12
400    **/
401   g_object_class_install_property (gobject_class,
402                                    PROP_SHOW_FILL_LEVEL,
403                                    g_param_spec_boolean ("show-fill-level",
404                                                          P_("Show Fill Level"),
405                                                          P_("Whether to display a fill level indicator graphics on trough."),
406                                                          FALSE,
407                                                          GTK_PARAM_READWRITE));
408
409   /**
410    * GtkRange:restrict-to-fill-level:
411    *
412    * The restrict-to-fill-level property controls whether slider
413    * movement is restricted to an upper boundary set by the
414    * fill level. See gtk_range_set_restrict_to_fill_level().
415    *
416    * Since: 2.12
417    **/
418   g_object_class_install_property (gobject_class,
419                                    PROP_RESTRICT_TO_FILL_LEVEL,
420                                    g_param_spec_boolean ("restrict-to-fill-level",
421                                                          P_("Restrict to Fill Level"),
422                                                          P_("Whether to restrict the upper boundary to the fill level."),
423                                                          TRUE,
424                                                          GTK_PARAM_READWRITE));
425
426   /**
427    * GtkRange:fill-level:
428    *
429    * The fill level (e.g. prebuffering of a network stream).
430    * See gtk_range_set_fill_level().
431    *
432    * Since: 2.12
433    **/
434   g_object_class_install_property (gobject_class,
435                                    PROP_FILL_LEVEL,
436                                    g_param_spec_double ("fill-level",
437                                                         P_("Fill Level"),
438                                                         P_("The fill level."),
439                                                         -G_MAXDOUBLE,
440                                                         G_MAXDOUBLE,
441                                                         G_MAXDOUBLE,
442                                                         GTK_PARAM_READWRITE));
443
444   gtk_widget_class_install_style_property (widget_class,
445                                            g_param_spec_int ("slider-width",
446                                                              P_("Slider Width"),
447                                                              P_("Width of scrollbar or scale thumb"),
448                                                              0,
449                                                              G_MAXINT,
450                                                              14,
451                                                              GTK_PARAM_READABLE));
452   gtk_widget_class_install_style_property (widget_class,
453                                            g_param_spec_int ("trough-border",
454                                                              P_("Trough Border"),
455                                                              P_("Spacing between thumb/steppers and outer trough bevel"),
456                                                              0,
457                                                              G_MAXINT,
458                                                              1,
459                                                              GTK_PARAM_READABLE));
460   gtk_widget_class_install_style_property (widget_class,
461                                            g_param_spec_int ("stepper-size",
462                                                              P_("Stepper Size"),
463                                                              P_("Length of step buttons at ends"),
464                                                              0,
465                                                              G_MAXINT,
466                                                              14,
467                                                              GTK_PARAM_READABLE));
468   /**
469    * GtkRange:stepper-spacing:
470    *
471    * The spacing between the stepper buttons and thumb. Note that
472    * setting this value to anything > 0 will automatically set the
473    * trough-under-steppers style property to %TRUE as well. Also,
474    * stepper-spacing won't have any effect if there are no steppers.
475    */
476   gtk_widget_class_install_style_property (widget_class,
477                                            g_param_spec_int ("stepper-spacing",
478                                                              P_("Stepper Spacing"),
479                                                              P_("Spacing between step buttons and thumb"),
480                                                              0,
481                                                              G_MAXINT,
482                                                              0,
483                                                              GTK_PARAM_READABLE));
484   gtk_widget_class_install_style_property (widget_class,
485                                            g_param_spec_int ("arrow-displacement-x",
486                                                              P_("Arrow X Displacement"),
487                                                              P_("How far in the x direction to move the arrow when the button is depressed"),
488                                                              G_MININT,
489                                                              G_MAXINT,
490                                                              0,
491                                                              GTK_PARAM_READABLE));
492   gtk_widget_class_install_style_property (widget_class,
493                                            g_param_spec_int ("arrow-displacement-y",
494                                                              P_("Arrow Y Displacement"),
495                                                              P_("How far in the y direction to move the arrow when the button is depressed"),
496                                                              G_MININT,
497                                                              G_MAXINT,
498                                                              0,
499                                                              GTK_PARAM_READABLE));
500
501   gtk_widget_class_install_style_property (widget_class,
502                                            g_param_spec_boolean ("activate-slider",
503                                                                  P_("Draw slider ACTIVE during drag"),
504                                                                  P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
505                                                                  FALSE,
506                                                                  GTK_PARAM_READABLE));
507
508   /**
509    * GtkRange:trough-side-details:
510    *
511    * When %TRUE, the parts of the trough on the two sides of the 
512    * slider are drawn with different details.
513    *
514    * Since: 2.10
515    */
516   gtk_widget_class_install_style_property (widget_class,
517                                            g_param_spec_boolean ("trough-side-details",
518                                                                  P_("Trough Side Details"),
519                                                                  P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"),
520                                                                  FALSE,
521                                                                  GTK_PARAM_READABLE));
522
523   /**
524    * GtkRange:trough-under-steppers:
525    *
526    * Whether to draw the trough across the full length of the range or
527    * to exclude the steppers and their spacing. Note that setting the
528    * #GtkRange:stepper-spacing style property to any value > 0 will
529    * automatically enable trough-under-steppers too.
530    *
531    * Since: 2.10
532    */
533   gtk_widget_class_install_style_property (widget_class,
534                                            g_param_spec_boolean ("trough-under-steppers",
535                                                                  P_("Trough Under Steppers"),
536                                                                  P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
537                                                                  TRUE,
538                                                                  GTK_PARAM_READABLE));
539
540   /**
541    * GtkRange:arrow-scaling:
542    *
543    * The arrow size proportion relative to the scroll button size.
544    *
545    * Since: 2.14
546    */
547   gtk_widget_class_install_style_property (widget_class,
548                                            g_param_spec_float ("arrow-scaling",
549                                                                P_("Arrow scaling"),
550                                                                P_("Arrow scaling with regard to scroll button size"),
551                                                                0.0, 1.0, 0.5,
552                                                                GTK_PARAM_READABLE));
553
554   g_type_class_add_private (class, sizeof (GtkRangeLayout));
555 }
556
557 static void
558 gtk_range_set_property (GObject      *object,
559                         guint         prop_id,
560                         const GValue *value,
561                         GParamSpec   *pspec)
562 {
563   GtkRange *range = GTK_RANGE (object);
564
565   switch (prop_id)
566     {
567     case PROP_ORIENTATION:
568       range->orientation = g_value_get_enum (value);
569
570       range->layout->slider_detail_quark = 0;
571       range->layout->stepper_detail_quark = 0;
572
573       gtk_widget_queue_resize (GTK_WIDGET (range));
574       break;
575     case PROP_UPDATE_POLICY:
576       gtk_range_set_update_policy (range, g_value_get_enum (value));
577       break;
578     case PROP_ADJUSTMENT:
579       gtk_range_set_adjustment (range, g_value_get_object (value));
580       break;
581     case PROP_INVERTED:
582       gtk_range_set_inverted (range, g_value_get_boolean (value));
583       break;
584     case PROP_LOWER_STEPPER_SENSITIVITY:
585       gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
586       break;
587     case PROP_UPPER_STEPPER_SENSITIVITY:
588       gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
589       break;
590     case PROP_SHOW_FILL_LEVEL:
591       gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
592       break;
593     case PROP_RESTRICT_TO_FILL_LEVEL:
594       gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
595       break;
596     case PROP_FILL_LEVEL:
597       gtk_range_set_fill_level (range, g_value_get_double (value));
598       break;
599     default:
600       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601       break;
602     }
603 }
604
605 static void
606 gtk_range_get_property (GObject      *object,
607                         guint         prop_id,
608                         GValue       *value,
609                         GParamSpec   *pspec)
610 {
611   GtkRange *range = GTK_RANGE (object);
612
613   switch (prop_id)
614     {
615     case PROP_ORIENTATION:
616       g_value_set_enum (value, range->orientation);
617       break;
618     case PROP_UPDATE_POLICY:
619       g_value_set_enum (value, range->update_policy);
620       break;
621     case PROP_ADJUSTMENT:
622       g_value_set_object (value, range->adjustment);
623       break;
624     case PROP_INVERTED:
625       g_value_set_boolean (value, range->inverted);
626       break;
627     case PROP_LOWER_STEPPER_SENSITIVITY:
628       g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
629       break;
630     case PROP_UPPER_STEPPER_SENSITIVITY:
631       g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
632       break;
633     case PROP_SHOW_FILL_LEVEL:
634       g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
635       break;
636     case PROP_RESTRICT_TO_FILL_LEVEL:
637       g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
638       break;
639     case PROP_FILL_LEVEL:
640       g_value_set_double (value, gtk_range_get_fill_level (range));
641       break;
642     default:
643       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
644       break;
645     }
646 }
647
648 static void
649 gtk_range_init (GtkRange *range)
650 {
651   GTK_WIDGET_SET_FLAGS (range, GTK_NO_WINDOW);
652
653   range->orientation = GTK_ORIENTATION_HORIZONTAL;
654   range->adjustment = NULL;
655   range->update_policy = GTK_UPDATE_CONTINUOUS;
656   range->inverted = FALSE;
657   range->flippable = FALSE;
658   range->min_slider_size = 1;
659   range->has_stepper_a = FALSE;
660   range->has_stepper_b = FALSE;
661   range->has_stepper_c = FALSE;
662   range->has_stepper_d = FALSE;
663   range->need_recalc = TRUE;
664   range->round_digits = -1;
665   range->layout = GTK_RANGE_GET_PRIVATE (range);
666   range->layout->mouse_location = MOUSE_OUTSIDE;
667   range->layout->mouse_x = -1;
668   range->layout->mouse_y = -1;
669   range->layout->grab_location = MOUSE_OUTSIDE;
670   range->layout->grab_button = 0;
671   range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
672   range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
673   range->layout->lower_sensitive = TRUE;
674   range->layout->upper_sensitive = TRUE;
675   range->layout->show_fill_level = FALSE;
676   range->layout->restrict_to_fill_level = TRUE;
677   range->layout->fill_level = G_MAXDOUBLE;
678   range->timer = NULL;  
679 }
680
681 /**
682  * gtk_range_get_adjustment:
683  * @range: a #GtkRange
684  * 
685  * Get the #GtkAdjustment which is the "model" object for #GtkRange.
686  * See gtk_range_set_adjustment() for details.
687  * The return value does not have a reference added, so should not
688  * be unreferenced.
689  * 
690  * Return value: a #GtkAdjustment
691  **/
692 GtkAdjustment*
693 gtk_range_get_adjustment (GtkRange *range)
694 {
695   g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
696
697   if (!range->adjustment)
698     gtk_range_set_adjustment (range, NULL);
699
700   return range->adjustment;
701 }
702
703 /**
704  * gtk_range_set_update_policy:
705  * @range: a #GtkRange
706  * @policy: update policy
707  *
708  * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
709  * anytime the range slider is moved, the range value will change and the
710  * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
711  * the value will be updated after a brief timeout where no slider motion
712  * occurs, so updates are spaced by a short time rather than
713  * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
714  * be updated when the user releases the button and ends the slider
715  * drag operation.
716  **/
717 void
718 gtk_range_set_update_policy (GtkRange      *range,
719                              GtkUpdateType  policy)
720 {
721   g_return_if_fail (GTK_IS_RANGE (range));
722
723   if (range->update_policy != policy)
724     {
725       range->update_policy = policy;
726       g_object_notify (G_OBJECT (range), "update-policy");
727     }
728 }
729
730 /**
731  * gtk_range_get_update_policy:
732  * @range: a #GtkRange
733  *
734  * Gets the update policy of @range. See gtk_range_set_update_policy().
735  *
736  * Return value: the current update policy
737  **/
738 GtkUpdateType
739 gtk_range_get_update_policy (GtkRange *range)
740 {
741   g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
742
743   return range->update_policy;
744 }
745
746 /**
747  * gtk_range_set_adjustment:
748  * @range: a #GtkRange
749  * @adjustment: a #GtkAdjustment
750  *
751  * Sets the adjustment to be used as the "model" object for this range
752  * widget. The adjustment indicates the current range value, the
753  * minimum and maximum range values, the step/page increments used
754  * for keybindings and scrolling, and the page size. The page size
755  * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
756  * indicates the size of the visible area of the widget being scrolled.
757  * The page size affects the size of the scrollbar slider.
758  **/
759 void
760 gtk_range_set_adjustment (GtkRange      *range,
761                           GtkAdjustment *adjustment)
762 {
763   g_return_if_fail (GTK_IS_RANGE (range));
764   
765   if (!adjustment)
766     adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
767   else
768     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
769
770   if (range->adjustment != adjustment)
771     {
772       if (range->adjustment)
773         {
774           g_signal_handlers_disconnect_by_func (range->adjustment,
775                                                 gtk_range_adjustment_changed,
776                                                 range);
777           g_signal_handlers_disconnect_by_func (range->adjustment,
778                                                 gtk_range_adjustment_value_changed,
779                                                 range);
780           g_object_unref (range->adjustment);
781         }
782
783       range->adjustment = adjustment;
784       g_object_ref_sink (adjustment);
785       
786       g_signal_connect (adjustment, "changed",
787                         G_CALLBACK (gtk_range_adjustment_changed),
788                         range);
789       g_signal_connect (adjustment, "value-changed",
790                         G_CALLBACK (gtk_range_adjustment_value_changed),
791                         range);
792       
793       gtk_range_adjustment_changed (adjustment, range);
794       g_object_notify (G_OBJECT (range), "adjustment");
795     }
796 }
797
798 /**
799  * gtk_range_set_inverted:
800  * @range: a #GtkRange
801  * @setting: %TRUE to invert the range
802  *
803  * Ranges normally move from lower to higher values as the
804  * slider moves from top to bottom or left to right. Inverted
805  * ranges have higher values at the top or on the right rather than
806  * on the bottom or left.
807  **/
808 void
809 gtk_range_set_inverted (GtkRange *range,
810                         gboolean  setting)
811 {
812   g_return_if_fail (GTK_IS_RANGE (range));
813   
814   setting = setting != FALSE;
815
816   if (setting != range->inverted)
817     {
818       range->inverted = setting;
819       g_object_notify (G_OBJECT (range), "inverted");
820       gtk_widget_queue_resize (GTK_WIDGET (range));
821     }
822 }
823
824 /**
825  * gtk_range_get_inverted:
826  * @range: a #GtkRange
827  * 
828  * Gets the value set by gtk_range_set_inverted().
829  * 
830  * Return value: %TRUE if the range is inverted
831  **/
832 gboolean
833 gtk_range_get_inverted (GtkRange *range)
834 {
835   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
836
837   return range->inverted;
838 }
839
840 /**
841  * gtk_range_set_lower_stepper_sensitivity:
842  * @range:       a #GtkRange
843  * @sensitivity: the lower stepper's sensitivity policy.
844  *
845  * Sets the sensitivity policy for the stepper that points to the
846  * 'lower' end of the GtkRange's adjustment.
847  *
848  * Since: 2.10
849  **/
850 void
851 gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
852                                          GtkSensitivityType  sensitivity)
853 {
854   g_return_if_fail (GTK_IS_RANGE (range));
855
856   if (range->layout->lower_sensitivity != sensitivity)
857     {
858       range->layout->lower_sensitivity = sensitivity;
859
860       range->need_recalc = TRUE;
861       gtk_range_calc_layout (range, range->adjustment->value);
862       gtk_widget_queue_draw (GTK_WIDGET (range));
863
864       g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
865     }
866 }
867
868 /**
869  * gtk_range_get_lower_stepper_sensitivity:
870  * @range: a #GtkRange
871  *
872  * Gets the sensitivity policy for the stepper that points to the
873  * 'lower' end of the GtkRange's adjustment.
874  *
875  * Return value: The lower stepper's sensitivity policy.
876  *
877  * Since: 2.10
878  **/
879 GtkSensitivityType
880 gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
881 {
882   g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
883
884   return range->layout->lower_sensitivity;
885 }
886
887 /**
888  * gtk_range_set_upper_stepper_sensitivity:
889  * @range:       a #GtkRange
890  * @sensitivity: the upper stepper's sensitivity policy.
891  *
892  * Sets the sensitivity policy for the stepper that points to the
893  * 'upper' end of the GtkRange's adjustment.
894  *
895  * Since: 2.10
896  **/
897 void
898 gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
899                                          GtkSensitivityType  sensitivity)
900 {
901   g_return_if_fail (GTK_IS_RANGE (range));
902
903   if (range->layout->upper_sensitivity != sensitivity)
904     {
905       range->layout->upper_sensitivity = sensitivity;
906
907       range->need_recalc = TRUE;
908       gtk_range_calc_layout (range, range->adjustment->value);
909       gtk_widget_queue_draw (GTK_WIDGET (range));
910
911       g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
912     }
913 }
914
915 /**
916  * gtk_range_get_upper_stepper_sensitivity:
917  * @range: a #GtkRange
918  *
919  * Gets the sensitivity policy for the stepper that points to the
920  * 'upper' end of the GtkRange's adjustment.
921  *
922  * Return value: The upper stepper's sensitivity policy.
923  *
924  * Since: 2.10
925  **/
926 GtkSensitivityType
927 gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
928 {
929   g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
930
931   return range->layout->upper_sensitivity;
932 }
933
934 /**
935  * gtk_range_set_increments:
936  * @range: a #GtkRange
937  * @step: step size
938  * @page: page size
939  *
940  * Sets the step and page sizes for the range.
941  * The step size is used when the user clicks the #GtkScrollbar
942  * arrows or moves #GtkScale via arrow keys. The page size
943  * is used for example when moving via Page Up or Page Down keys.
944  **/
945 void
946 gtk_range_set_increments (GtkRange *range,
947                           gdouble   step,
948                           gdouble   page)
949 {
950   g_return_if_fail (GTK_IS_RANGE (range));
951
952   range->adjustment->step_increment = step;
953   range->adjustment->page_increment = page;
954
955   gtk_adjustment_changed (range->adjustment);
956 }
957
958 /**
959  * gtk_range_set_range:
960  * @range: a #GtkRange
961  * @min: minimum range value
962  * @max: maximum range value
963  * 
964  * Sets the allowable values in the #GtkRange, and clamps the range
965  * value to be between @min and @max. (If the range has a non-zero
966  * page size, it is clamped between @min and @max - page-size.)
967  **/
968 void
969 gtk_range_set_range (GtkRange *range,
970                      gdouble   min,
971                      gdouble   max)
972 {
973   gdouble value;
974   
975   g_return_if_fail (GTK_IS_RANGE (range));
976   g_return_if_fail (min < max);
977   
978   range->adjustment->lower = min;
979   range->adjustment->upper = max;
980
981   value = range->adjustment->value;
982
983   if (range->layout->restrict_to_fill_level)
984     value = MIN (value, MAX (range->adjustment->lower,
985                              range->layout->fill_level));
986
987   gtk_adjustment_set_value (range->adjustment, value);
988   gtk_adjustment_changed (range->adjustment);
989 }
990
991 /**
992  * gtk_range_set_value:
993  * @range: a #GtkRange
994  * @value: new value of the range
995  *
996  * Sets the current value of the range; if the value is outside the
997  * minimum or maximum range values, it will be clamped to fit inside
998  * them. The range emits the #GtkRange::value-changed signal if the 
999  * value changes.
1000  **/
1001 void
1002 gtk_range_set_value (GtkRange *range,
1003                      gdouble   value)
1004 {
1005   g_return_if_fail (GTK_IS_RANGE (range));
1006
1007   if (range->layout->restrict_to_fill_level)
1008     value = MIN (value, MAX (range->adjustment->lower,
1009                              range->layout->fill_level));
1010
1011   gtk_adjustment_set_value (range->adjustment, value);
1012 }
1013
1014 /**
1015  * gtk_range_get_value:
1016  * @range: a #GtkRange
1017  * 
1018  * Gets the current value of the range.
1019  * 
1020  * Return value: current value of the range.
1021  **/
1022 gdouble
1023 gtk_range_get_value (GtkRange *range)
1024 {
1025   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1026
1027   return range->adjustment->value;
1028 }
1029
1030 /**
1031  * gtk_range_set_show_fill_level:
1032  * @range:           A #GtkRange
1033  * @show_fill_level: Whether a fill level indicator graphics is shown.
1034  *
1035  * Sets whether a graphical fill level is show on the trough. See
1036  * gtk_range_set_fill_level() for a general description of the fill
1037  * level concept.
1038  *
1039  * Since: 2.12
1040  **/
1041 void
1042 gtk_range_set_show_fill_level (GtkRange *range,
1043                                gboolean  show_fill_level)
1044 {
1045   g_return_if_fail (GTK_IS_RANGE (range));
1046
1047   show_fill_level = show_fill_level ? TRUE : FALSE;
1048
1049   if (show_fill_level != range->layout->show_fill_level)
1050     {
1051       range->layout->show_fill_level = show_fill_level;
1052       g_object_notify (G_OBJECT (range), "show-fill-level");
1053       gtk_widget_queue_draw (GTK_WIDGET (range));
1054     }
1055 }
1056
1057 /**
1058  * gtk_range_get_show_fill_level:
1059  * @range: A #GtkRange
1060  *
1061  * Gets whether the range displays the fill level graphically.
1062  *
1063  * Return value: %TRUE if @range shows the fill level.
1064  *
1065  * Since: 2.12
1066  **/
1067 gboolean
1068 gtk_range_get_show_fill_level (GtkRange *range)
1069 {
1070   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1071
1072   return range->layout->show_fill_level;
1073 }
1074
1075 /**
1076  * gtk_range_set_restrict_to_fill_level:
1077  * @range:                  A #GtkRange
1078  * @restrict_to_fill_level: Whether the fill level restricts slider movement.
1079  *
1080  * Sets whether the slider is restricted to the fill level. See
1081  * gtk_range_set_fill_level() for a general description of the fill
1082  * level concept.
1083  *
1084  * Since: 2.12
1085  **/
1086 void
1087 gtk_range_set_restrict_to_fill_level (GtkRange *range,
1088                                       gboolean  restrict_to_fill_level)
1089 {
1090   g_return_if_fail (GTK_IS_RANGE (range));
1091
1092   restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
1093
1094   if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
1095     {
1096       range->layout->restrict_to_fill_level = restrict_to_fill_level;
1097       g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
1098
1099       gtk_range_set_value (range, gtk_range_get_value (range));
1100     }
1101 }
1102
1103 /**
1104  * gtk_range_get_restrict_to_fill_level:
1105  * @range: A #GtkRange
1106  *
1107  * Gets whether the range is restricted to the fill level.
1108  *
1109  * Return value: %TRUE if @range is restricted to the fill level.
1110  *
1111  * Since: 2.12
1112  **/
1113 gboolean
1114 gtk_range_get_restrict_to_fill_level (GtkRange *range)
1115 {
1116   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1117
1118   return range->layout->restrict_to_fill_level;
1119 }
1120
1121 /**
1122  * gtk_range_set_fill_level:
1123  * @range: a #GtkRange
1124  * @fill_level: the new position of the fill level indicator
1125  *
1126  * Set the new position of the fill level indicator.
1127  *
1128  * The "fill level" is probably best described by its most prominent
1129  * use case, which is an indicator for the amount of pre-buffering in
1130  * a streaming media player. In that use case, the value of the range
1131  * would indicate the current play position, and the fill level would
1132  * be the position up to which the file/stream has been downloaded.
1133  *
1134  * This amount of prebuffering can be displayed on the range's trough
1135  * and is themeable separately from the trough. To enable fill level
1136  * display, use gtk_range_set_show_fill_level(). The range defaults
1137  * to not showing the fill level.
1138  *
1139  * Additionally, it's possible to restrict the range's slider position
1140  * to values which are smaller than the fill level. This is controller
1141  * by gtk_range_set_restrict_to_fill_level() and is by default
1142  * enabled.
1143  *
1144  * Since: 2.12
1145  **/
1146 void
1147 gtk_range_set_fill_level (GtkRange *range,
1148                           gdouble   fill_level)
1149 {
1150   g_return_if_fail (GTK_IS_RANGE (range));
1151
1152   if (fill_level != range->layout->fill_level)
1153     {
1154       range->layout->fill_level = fill_level;
1155       g_object_notify (G_OBJECT (range), "fill-level");
1156
1157       if (range->layout->show_fill_level)
1158         gtk_widget_queue_draw (GTK_WIDGET (range));
1159
1160       if (range->layout->restrict_to_fill_level)
1161         gtk_range_set_value (range, gtk_range_get_value (range));
1162     }
1163 }
1164
1165 /**
1166  * gtk_range_get_fill_level:
1167  * @range : A #GtkRange
1168  *
1169  * Gets the current position of the fill level indicator.
1170  *
1171  * Return value: The current fill level
1172  *
1173  * Since: 2.12
1174  **/
1175 gdouble
1176 gtk_range_get_fill_level (GtkRange *range)
1177 {
1178   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1179
1180   return range->layout->fill_level;
1181 }
1182
1183 static gboolean
1184 should_invert (GtkRange *range)
1185 {  
1186   if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1187     return
1188       (range->inverted && !range->flippable) ||
1189       (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
1190       (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1191   else
1192     return range->inverted;
1193 }
1194
1195 static void
1196 gtk_range_destroy (GtkObject *object)
1197 {
1198   GtkRange *range = GTK_RANGE (object);
1199
1200   gtk_range_remove_step_timer (range);
1201   gtk_range_remove_update_timer (range);
1202
1203   if (range->layout->repaint_id)
1204     g_source_remove (range->layout->repaint_id);
1205   range->layout->repaint_id = 0;
1206
1207   if (range->adjustment)
1208     {
1209       g_signal_handlers_disconnect_by_func (range->adjustment,
1210                                             gtk_range_adjustment_changed,
1211                                             range);
1212       g_signal_handlers_disconnect_by_func (range->adjustment,
1213                                             gtk_range_adjustment_value_changed,
1214                                             range);
1215       g_object_unref (range->adjustment);
1216       range->adjustment = NULL;
1217     }
1218
1219   GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
1220 }
1221
1222 static void
1223 gtk_range_size_request (GtkWidget      *widget,
1224                         GtkRequisition *requisition)
1225 {
1226   GtkRange *range;
1227   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1228   GdkRectangle range_rect;
1229   GtkBorder border;
1230   
1231   range = GTK_RANGE (widget);
1232   
1233   gtk_range_get_props (range,
1234                        &slider_width, &stepper_size,
1235                        &focus_width, &trough_border,
1236                        &stepper_spacing, NULL,
1237                        NULL, NULL);
1238
1239   gtk_range_calc_request (range, 
1240                           slider_width, stepper_size,
1241                           focus_width, trough_border, stepper_spacing,
1242                           &range_rect, &border, NULL, NULL, NULL, NULL);
1243
1244   requisition->width = range_rect.width + border.left + border.right;
1245   requisition->height = range_rect.height + border.top + border.bottom;
1246 }
1247
1248 static void
1249 gtk_range_size_allocate (GtkWidget     *widget,
1250                          GtkAllocation *allocation)
1251 {
1252   GtkRange *range;
1253
1254   range = GTK_RANGE (widget);
1255
1256   widget->allocation = *allocation;
1257   
1258   range->need_recalc = TRUE;
1259   gtk_range_calc_layout (range, range->adjustment->value);
1260
1261   if (GTK_WIDGET_REALIZED (range))
1262     gdk_window_move_resize (range->event_window,
1263                             widget->allocation.x,
1264                             widget->allocation.y,
1265                             widget->allocation.width,
1266                             widget->allocation.height);
1267 }
1268
1269 static void
1270 gtk_range_realize (GtkWidget *widget)
1271 {
1272   GtkRange *range;
1273   GdkWindowAttr attributes;
1274   gint attributes_mask;  
1275
1276   range = GTK_RANGE (widget);
1277
1278   gtk_range_calc_layout (range, range->adjustment->value);
1279   
1280   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1281
1282   widget->window = gtk_widget_get_parent_window (widget);
1283   g_object_ref (widget->window);
1284   
1285   attributes.window_type = GDK_WINDOW_CHILD;
1286   attributes.x = widget->allocation.x;
1287   attributes.y = widget->allocation.y;
1288   attributes.width = widget->allocation.width;
1289   attributes.height = widget->allocation.height;
1290   attributes.wclass = GDK_INPUT_ONLY;
1291   attributes.event_mask = gtk_widget_get_events (widget);
1292   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1293                             GDK_BUTTON_RELEASE_MASK |
1294                             GDK_ENTER_NOTIFY_MASK |
1295                             GDK_LEAVE_NOTIFY_MASK |
1296                             GDK_POINTER_MOTION_MASK |
1297                             GDK_POINTER_MOTION_HINT_MASK);
1298
1299   attributes_mask = GDK_WA_X | GDK_WA_Y;
1300
1301   range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1302                                         &attributes, attributes_mask);
1303   gdk_window_set_user_data (range->event_window, range);
1304
1305   widget->style = gtk_style_attach (widget->style, widget->window);
1306 }
1307
1308 static void
1309 gtk_range_unrealize (GtkWidget *widget)
1310 {
1311   GtkRange *range = GTK_RANGE (widget);
1312
1313   gtk_range_remove_step_timer (range);
1314   gtk_range_remove_update_timer (range);
1315   
1316   gdk_window_set_user_data (range->event_window, NULL);
1317   gdk_window_destroy (range->event_window);
1318   range->event_window = NULL;
1319
1320   GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1321 }
1322
1323 static void
1324 gtk_range_map (GtkWidget *widget)
1325 {
1326   GtkRange *range = GTK_RANGE (widget);
1327   
1328   gdk_window_show (range->event_window);
1329
1330   GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1331 }
1332
1333 static void
1334 gtk_range_unmap (GtkWidget *widget)
1335 {
1336   GtkRange *range = GTK_RANGE (widget);
1337     
1338   stop_scrolling (range);
1339
1340   gdk_window_hide (range->event_window);
1341
1342   GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1343 }
1344
1345 static const gchar *
1346 gtk_range_get_slider_detail (GtkRange *range)
1347 {
1348   const gchar *slider_detail;
1349
1350   if (range->layout->slider_detail_quark)
1351     return g_quark_to_string (range->layout->slider_detail_quark);
1352
1353   slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
1354
1355   if (slider_detail && slider_detail[0] == 'X')
1356     {
1357       gchar *detail = g_strdup (slider_detail);
1358
1359       detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1360
1361       range->layout->slider_detail_quark = g_quark_from_string (detail);
1362
1363       g_free (detail);
1364
1365       return g_quark_to_string (range->layout->slider_detail_quark);
1366     }
1367
1368   return slider_detail;
1369 }
1370
1371 static const gchar *
1372 gtk_range_get_stepper_detail (GtkRange *range)
1373 {
1374   const gchar *stepper_detail;
1375
1376   if (range->layout->stepper_detail_quark)
1377     return g_quark_to_string (range->layout->stepper_detail_quark);
1378
1379   stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
1380
1381   if (stepper_detail && stepper_detail[0] == 'X')
1382     {
1383       gchar *detail = g_strdup (stepper_detail);
1384
1385       detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1386
1387       range->layout->stepper_detail_quark = g_quark_from_string (detail);
1388
1389       g_free (detail);
1390
1391       return g_quark_to_string (range->layout->stepper_detail_quark);
1392     }
1393
1394   return stepper_detail;
1395 }
1396
1397 static void
1398 draw_stepper (GtkRange     *range,
1399               GdkRectangle *rect,
1400               GtkArrowType  arrow_type,
1401               gboolean      clicked,
1402               gboolean      prelighted,
1403               GdkRectangle *area)
1404 {
1405   GtkStateType state_type;
1406   GtkShadowType shadow_type;
1407   GdkRectangle intersection;
1408   GtkWidget *widget = GTK_WIDGET (range);
1409   gfloat arrow_scaling;
1410
1411   gint arrow_x;
1412   gint arrow_y;
1413   gint arrow_width;
1414   gint arrow_height;
1415
1416   gboolean arrow_sensitive = TRUE;
1417
1418   /* More to get the right clip region than for efficiency */
1419   if (!gdk_rectangle_intersect (area, rect, &intersection))
1420     return;
1421
1422   intersection.x += widget->allocation.x;
1423   intersection.y += widget->allocation.y;
1424
1425   if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1426                             arrow_type == GTK_ARROW_RIGHT)) ||
1427       (range->inverted  && (arrow_type == GTK_ARROW_UP ||
1428                             arrow_type == GTK_ARROW_LEFT)))
1429     {
1430       arrow_sensitive = range->layout->upper_sensitive;
1431     }
1432   else
1433     {
1434       arrow_sensitive = range->layout->lower_sensitive;
1435     }
1436
1437   if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
1438     state_type = GTK_STATE_INSENSITIVE;
1439   else if (clicked)
1440     state_type = GTK_STATE_ACTIVE;
1441   else if (prelighted)
1442     state_type = GTK_STATE_PRELIGHT;
1443   else 
1444     state_type = GTK_STATE_NORMAL;
1445
1446   if (clicked && arrow_sensitive)
1447     shadow_type = GTK_SHADOW_IN;
1448   else
1449     shadow_type = GTK_SHADOW_OUT;
1450
1451   gtk_paint_box (widget->style,
1452                  widget->window,
1453                  state_type, shadow_type,
1454                  &intersection, widget,
1455                  gtk_range_get_stepper_detail (range),
1456                  widget->allocation.x + rect->x,
1457                  widget->allocation.y + rect->y,
1458                  rect->width,
1459                  rect->height);
1460
1461   gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1462
1463   arrow_width = rect->width * arrow_scaling;
1464   arrow_height = rect->height * arrow_scaling;
1465   arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1466   arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1467
1468   if (clicked && arrow_sensitive)
1469     {
1470       gint arrow_displacement_x;
1471       gint arrow_displacement_y;
1472
1473       gtk_range_get_props (GTK_RANGE (widget),
1474                            NULL, NULL, NULL, NULL, NULL, NULL,
1475                            &arrow_displacement_x, &arrow_displacement_y);
1476       
1477       arrow_x += arrow_displacement_x;
1478       arrow_y += arrow_displacement_y;
1479     }
1480   
1481   gtk_paint_arrow (widget->style,
1482                    widget->window,
1483                    state_type, shadow_type, 
1484                    &intersection, widget,
1485                    gtk_range_get_stepper_detail (range),
1486                    arrow_type,
1487                    TRUE,
1488                    arrow_x, arrow_y, arrow_width, arrow_height);
1489 }
1490
1491 static gboolean
1492 gtk_range_expose (GtkWidget      *widget,
1493                   GdkEventExpose *event)
1494 {
1495   GtkRange *range = GTK_RANGE (widget);
1496   gboolean sensitive;
1497   GtkStateType state;
1498   GtkShadowType shadow_type;
1499   GdkRectangle expose_area;     /* Relative to widget->allocation */
1500   GdkRectangle area;
1501   gint focus_line_width = 0;
1502   gint focus_padding = 0;
1503   gboolean touchscreen;
1504
1505   g_object_get (gtk_widget_get_settings (widget),
1506                 "gtk-touchscreen-mode", &touchscreen,
1507                 NULL);
1508   if (GTK_WIDGET_CAN_FOCUS (range))
1509     gtk_widget_style_get (GTK_WIDGET (range),
1510                           "focus-line-width", &focus_line_width,
1511                           "focus-padding", &focus_padding,
1512                           NULL);
1513
1514   /* we're now exposing, so there's no need to force early repaints */
1515   if (range->layout->repaint_id)
1516     g_source_remove (range->layout->repaint_id);
1517   range->layout->repaint_id = 0;
1518
1519   expose_area = event->area;
1520   expose_area.x -= widget->allocation.x;
1521   expose_area.y -= widget->allocation.y;
1522   
1523   gtk_range_calc_layout (range, range->adjustment->value);
1524
1525   sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
1526
1527   /* Just to be confusing, we draw the trough for the whole
1528    * range rectangle, not the trough rectangle (the trough
1529    * rectangle is just for hit detection)
1530    */
1531   /* The gdk_rectangle_intersect is more to get the right
1532    * clip region (limited to range_rect) than for efficiency
1533    */
1534   if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1535                                &area))
1536     {
1537       gint     x      = (widget->allocation.x + range->range_rect.x +
1538                          focus_line_width + focus_padding);
1539       gint     y      = (widget->allocation.y + range->range_rect.y +
1540                          focus_line_width + focus_padding);
1541       gint     width  = (range->range_rect.width -
1542                          2 * (focus_line_width + focus_padding));
1543       gint     height = (range->range_rect.height -
1544                          2 * (focus_line_width + focus_padding));
1545       gboolean trough_side_details;
1546       gboolean trough_under_steppers;
1547       gint     stepper_size;
1548       gint     stepper_spacing;
1549
1550       area.x += widget->allocation.x;
1551       area.y += widget->allocation.y;
1552
1553       gtk_widget_style_get (GTK_WIDGET (range),
1554                             "trough-side-details",   &trough_side_details,
1555                             "trough-under-steppers", &trough_under_steppers,
1556                             "stepper-size",          &stepper_size,
1557                             "stepper-spacing",       &stepper_spacing,
1558                             NULL);
1559
1560       if (stepper_spacing > 0)
1561         trough_under_steppers = FALSE;
1562
1563       if (! trough_under_steppers)
1564         {
1565           gint offset  = 0;
1566           gint shorter = 0;
1567
1568           if (range->has_stepper_a)
1569             offset += stepper_size;
1570
1571           if (range->has_stepper_b)
1572             offset += stepper_size;
1573
1574           shorter += offset;
1575
1576           if (range->has_stepper_c)
1577             shorter += stepper_size;
1578
1579           if (range->has_stepper_d)
1580             shorter += stepper_size;
1581
1582           if (range->has_stepper_a || range->has_stepper_b)
1583             {
1584               offset  += stepper_spacing;
1585               shorter += stepper_spacing;
1586             }
1587
1588           if (range->has_stepper_c || range->has_stepper_d)
1589             {
1590               shorter += stepper_spacing;
1591             }
1592
1593           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1594             {
1595               x     += offset;
1596               width -= shorter;
1597             }
1598           else
1599             {
1600               y      += offset;
1601               height -= shorter;
1602             }
1603         }
1604
1605       if (! trough_side_details)
1606         {
1607           gtk_paint_box (widget->style,
1608                          widget->window,
1609                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1610                          GTK_SHADOW_IN,
1611                          &area, GTK_WIDGET(range), "trough",
1612                          x, y,
1613                          width, height);
1614         }
1615       else
1616         {
1617           gint trough_change_pos_x = width;
1618           gint trough_change_pos_y = height;
1619
1620           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1621             trough_change_pos_x = (range->layout->slider.x +
1622                                    range->layout->slider.width / 2 -
1623                                    (x - widget->allocation.x));
1624           else
1625             trough_change_pos_y = (range->layout->slider.y +
1626                                    range->layout->slider.height / 2 -
1627                                    (y - widget->allocation.y));
1628
1629           gtk_paint_box (widget->style,
1630                          widget->window,
1631                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1632                          GTK_SHADOW_IN,
1633                          &area, GTK_WIDGET (range),
1634                          should_invert (range) ? "trough-upper" : "trough-lower",
1635                          x, y,
1636                          trough_change_pos_x, trough_change_pos_y);
1637
1638           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1639             trough_change_pos_y = 0;
1640           else
1641             trough_change_pos_x = 0;
1642
1643           gtk_paint_box (widget->style,
1644                          widget->window,
1645                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1646                          GTK_SHADOW_IN,
1647                          &area, GTK_WIDGET (range),
1648                          should_invert (range) ? "trough-lower" : "trough-upper",
1649                          x + trough_change_pos_x, y + trough_change_pos_y,
1650                          width - trough_change_pos_x,
1651                          height - trough_change_pos_y);
1652         }
1653
1654       if (range->layout->show_fill_level &&
1655           range->adjustment->upper - range->adjustment->page_size -
1656           range->adjustment->lower != 0)
1657         {
1658           gdouble  fill_level  = range->layout->fill_level;
1659           gint     fill_x      = x;
1660           gint     fill_y      = y;
1661           gint     fill_width  = width;
1662           gint     fill_height = height;
1663           gchar   *fill_detail;
1664
1665           fill_level = CLAMP (fill_level, range->adjustment->lower,
1666                               range->adjustment->upper -
1667                               range->adjustment->page_size);
1668
1669           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1670             {
1671               fill_x     = widget->allocation.x + range->layout->trough.x;
1672               fill_width = (range->layout->slider.width +
1673                             (fill_level - range->adjustment->lower) /
1674                             (range->adjustment->upper -
1675                              range->adjustment->lower -
1676                              range->adjustment->page_size) *
1677                             (range->layout->trough.width -
1678                              range->layout->slider.width));
1679
1680               if (should_invert (range))
1681                 fill_x += range->layout->trough.width - fill_width;
1682             }
1683           else
1684             {
1685               fill_y      = widget->allocation.y + range->layout->trough.y;
1686               fill_height = (range->layout->slider.height +
1687                              (fill_level - range->adjustment->lower) /
1688                              (range->adjustment->upper -
1689                               range->adjustment->lower -
1690                               range->adjustment->page_size) *
1691                              (range->layout->trough.height -
1692                               range->layout->slider.height));
1693
1694               if (should_invert (range))
1695                 fill_y += range->layout->trough.height - fill_height;
1696             }
1697
1698           if (fill_level < range->adjustment->upper - range->adjustment->page_size)
1699             fill_detail = "trough-fill-level-full";
1700           else
1701             fill_detail = "trough-fill-level";
1702
1703           gtk_paint_box (widget->style,
1704                          widget->window,
1705                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1706                          GTK_SHADOW_OUT,
1707                          &area, GTK_WIDGET (range), fill_detail,
1708                          fill_x, fill_y,
1709                          fill_width, fill_height);
1710         }
1711
1712       if (sensitive &&
1713           GTK_WIDGET_HAS_FOCUS (range))
1714         gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
1715                          &area, widget, "trough",
1716                          widget->allocation.x + range->range_rect.x,
1717                          widget->allocation.y + range->range_rect.y,
1718                          range->range_rect.width,
1719                          range->range_rect.height);
1720     }
1721
1722   shadow_type = GTK_SHADOW_OUT;
1723
1724   if (!sensitive)
1725     state = GTK_STATE_INSENSITIVE;
1726   else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1727     state = GTK_STATE_PRELIGHT;
1728   else
1729     state = GTK_STATE_NORMAL;
1730
1731   if (range->layout->grab_location == MOUSE_SLIDER)
1732     {
1733       gboolean activate_slider;
1734
1735       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
1736
1737       if (activate_slider)
1738         {
1739           state = GTK_STATE_ACTIVE;
1740           shadow_type = GTK_SHADOW_IN;
1741         }
1742     }
1743
1744   if (gdk_rectangle_intersect (&expose_area,
1745                                &range->layout->slider,
1746                                &area))
1747     {
1748       area.x += widget->allocation.x;
1749       area.y += widget->allocation.y;
1750       
1751       gtk_paint_slider (widget->style,
1752                         widget->window,
1753                         state,
1754                         shadow_type,
1755                         &area,
1756                         widget,
1757                         gtk_range_get_slider_detail (range),
1758                         widget->allocation.x + range->layout->slider.x,
1759                         widget->allocation.y + range->layout->slider.y,
1760                         range->layout->slider.width,
1761                         range->layout->slider.height,
1762                         range->orientation);
1763     }
1764   
1765   if (range->has_stepper_a)
1766     draw_stepper (range, &range->layout->stepper_a,
1767                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1768                   range->layout->grab_location == MOUSE_STEPPER_A,
1769                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
1770                   &expose_area);
1771
1772   if (range->has_stepper_b)
1773     draw_stepper (range, &range->layout->stepper_b,
1774                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1775                   range->layout->grab_location == MOUSE_STEPPER_B,
1776                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
1777                   &expose_area);
1778
1779   if (range->has_stepper_c)
1780     draw_stepper (range, &range->layout->stepper_c,
1781                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1782                   range->layout->grab_location == MOUSE_STEPPER_C,
1783                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
1784                   &expose_area);
1785
1786   if (range->has_stepper_d)
1787     draw_stepper (range, &range->layout->stepper_d,
1788                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1789                   range->layout->grab_location == MOUSE_STEPPER_D,
1790                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
1791                   &expose_area);
1792   
1793   return FALSE;
1794 }
1795
1796 static void
1797 range_grab_add (GtkRange      *range,
1798                 MouseLocation  location,
1799                 gint           button)
1800 {
1801   /* we don't actually gtk_grab, since a button is down */
1802
1803   gtk_grab_add (GTK_WIDGET (range));
1804   
1805   range->layout->grab_location = location;
1806   range->layout->grab_button = button;
1807   
1808   if (gtk_range_update_mouse_location (range))
1809     gtk_widget_queue_draw (GTK_WIDGET (range));
1810 }
1811
1812 static void
1813 range_grab_remove (GtkRange *range)
1814 {
1815   MouseLocation location;
1816
1817   gtk_grab_remove (GTK_WIDGET (range));
1818  
1819   location = range->layout->grab_location; 
1820   range->layout->grab_location = MOUSE_OUTSIDE;
1821   range->layout->grab_button = 0;
1822
1823   if (gtk_range_update_mouse_location (range) ||
1824       location != MOUSE_OUTSIDE)
1825     gtk_widget_queue_draw (GTK_WIDGET (range));
1826 }
1827
1828 static GtkScrollType
1829 range_get_scroll_for_grab (GtkRange      *range)
1830
1831   gboolean invert;
1832
1833   invert = should_invert (range);
1834   switch (range->layout->grab_location)
1835     {
1836       /* Backward stepper */
1837     case MOUSE_STEPPER_A:
1838     case MOUSE_STEPPER_C:
1839       switch (range->layout->grab_button)
1840         {
1841         case 1:
1842           return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
1843           break;
1844         case 2:
1845           return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
1846           break;
1847         case 3:
1848           return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
1849           break;
1850         }
1851       break;
1852
1853       /* Forward stepper */
1854     case MOUSE_STEPPER_B:
1855     case MOUSE_STEPPER_D:
1856       switch (range->layout->grab_button)
1857         {
1858         case 1:
1859           return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
1860           break;
1861         case 2:
1862           return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
1863           break;
1864         case 3:
1865           return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
1866           break;
1867        }
1868       break;
1869
1870       /* In the trough */
1871     case MOUSE_TROUGH:
1872       {
1873         if (range->trough_click_forward)
1874           return GTK_SCROLL_PAGE_FORWARD;
1875         else
1876           return GTK_SCROLL_PAGE_BACKWARD;
1877       }
1878       break;
1879
1880     case MOUSE_OUTSIDE:
1881     case MOUSE_SLIDER:
1882     case MOUSE_WIDGET:
1883       break;
1884     }
1885
1886   return GTK_SCROLL_NONE;
1887 }
1888
1889 static gdouble
1890 coord_to_value (GtkRange *range,
1891                 gint      coord)
1892 {
1893   gdouble frac;
1894   gdouble value;
1895   gint    trough_length;
1896   gint    trough_start;
1897   gint    slider_length;
1898   gint    trough_border;
1899   gint    trough_under_steppers;
1900
1901   if (range->orientation == GTK_ORIENTATION_VERTICAL)
1902     {
1903       trough_length = range->layout->trough.height;
1904       trough_start  = range->layout->trough.y;
1905       slider_length = range->layout->slider.height;
1906     }
1907   else
1908     {
1909       trough_length = range->layout->trough.width;
1910       trough_start  = range->layout->trough.x;
1911       slider_length = range->layout->slider.width;
1912     }
1913
1914   gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
1915                        &trough_under_steppers, NULL, NULL);
1916
1917   if (! trough_under_steppers)
1918     {
1919       trough_start += trough_border;
1920       trough_length -= 2 * trough_border;
1921     }
1922
1923   if (trough_length == slider_length)
1924     frac = 1.0;
1925   else
1926     frac = (MAX (0, coord - trough_start) /
1927             (gdouble) (trough_length - slider_length));
1928
1929   if (should_invert (range))
1930     frac = 1.0 - frac;
1931
1932   value = range->adjustment->lower + frac * (range->adjustment->upper -
1933                                              range->adjustment->lower -
1934                                              range->adjustment->page_size);
1935
1936   return value;
1937 }
1938
1939 static gboolean
1940 gtk_range_key_press (GtkWidget   *widget,
1941                      GdkEventKey *event)
1942 {
1943   GtkRange *range = GTK_RANGE (widget);
1944
1945   if (event->keyval == GDK_Escape &&
1946       range->layout->grab_location != MOUSE_OUTSIDE)
1947     {
1948       stop_scrolling (range);
1949
1950       update_slider_position (range,
1951                               range->slide_initial_coordinate,
1952                               range->slide_initial_coordinate);
1953
1954       return TRUE;
1955     }
1956
1957   return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
1958 }
1959
1960 static gint
1961 gtk_range_button_press (GtkWidget      *widget,
1962                         GdkEventButton *event)
1963 {
1964   GtkRange *range = GTK_RANGE (widget);
1965   
1966   if (!GTK_WIDGET_HAS_FOCUS (widget))
1967     gtk_widget_grab_focus (widget);
1968
1969   /* ignore presses when we're already doing something else. */
1970   if (range->layout->grab_location != MOUSE_OUTSIDE)
1971     return FALSE;
1972
1973   range->layout->mouse_x = event->x;
1974   range->layout->mouse_y = event->y;
1975   if (gtk_range_update_mouse_location (range))
1976     gtk_widget_queue_draw (widget);
1977     
1978   if (range->layout->mouse_location == MOUSE_TROUGH  &&
1979       event->button == 1)
1980     {
1981       /* button 1 steps by page increment, as with button 2 on a stepper
1982        */
1983       GtkScrollType scroll;
1984       gdouble click_value;
1985       
1986       click_value = coord_to_value (range,
1987                                     range->orientation == GTK_ORIENTATION_VERTICAL ?
1988                                     event->y : event->x);
1989       
1990       range->trough_click_forward = click_value > range->adjustment->value;
1991       range_grab_add (range, MOUSE_TROUGH, event->button);
1992       
1993       scroll = range_get_scroll_for_grab (range);
1994       
1995       gtk_range_add_step_timer (range, scroll);
1996
1997       return TRUE;
1998     }
1999   else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
2000             range->layout->mouse_location == MOUSE_STEPPER_B ||
2001             range->layout->mouse_location == MOUSE_STEPPER_C ||
2002             range->layout->mouse_location == MOUSE_STEPPER_D) &&
2003            (event->button == 1 || event->button == 2 || event->button == 3))
2004     {
2005       GdkRectangle *stepper_area;
2006       GtkScrollType scroll;
2007       
2008       range_grab_add (range, range->layout->mouse_location, event->button);
2009
2010       stepper_area = get_area (range, range->layout->mouse_location);
2011       gtk_widget_queue_draw_area (widget,
2012                                   widget->allocation.x + stepper_area->x,
2013                                   widget->allocation.y + stepper_area->y,
2014                                   stepper_area->width,
2015                                   stepper_area->height);
2016
2017       scroll = range_get_scroll_for_grab (range);
2018       if (scroll != GTK_SCROLL_NONE)
2019         gtk_range_add_step_timer (range, scroll);
2020       
2021       return TRUE;
2022     }
2023   else if ((range->layout->mouse_location == MOUSE_TROUGH &&
2024             event->button == 2) ||
2025            range->layout->mouse_location == MOUSE_SLIDER)
2026     {
2027       gboolean need_value_update = FALSE;
2028       gboolean activate_slider;
2029
2030       /* Any button can be used to drag the slider, but you can start
2031        * dragging the slider with a trough click using button 2;
2032        * On button 2 press, we warp the slider to mouse position,
2033        * then begin the slider drag.
2034        */
2035       if (event->button == 2)
2036         {
2037           gdouble slider_low_value, slider_high_value, new_value;
2038           
2039           slider_high_value =
2040             coord_to_value (range,
2041                             range->orientation == GTK_ORIENTATION_VERTICAL ?
2042                             event->y : event->x);
2043           slider_low_value =
2044             coord_to_value (range,
2045                             range->orientation == GTK_ORIENTATION_VERTICAL ?
2046                             event->y - range->layout->slider.height :
2047                             event->x - range->layout->slider.width);
2048           
2049           /* compute new value for warped slider */
2050           new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
2051
2052           /* recalc slider, so we can set slide_initial_slider_position
2053            * properly
2054            */
2055           range->need_recalc = TRUE;
2056           gtk_range_calc_layout (range, new_value);
2057
2058           /* defer adjustment updates to update_slider_position() in order
2059            * to keep pixel quantisation
2060            */
2061           need_value_update = TRUE;
2062         }
2063       
2064       if (range->orientation == GTK_ORIENTATION_VERTICAL)
2065         {
2066           range->slide_initial_slider_position = range->layout->slider.y;
2067           range->slide_initial_coordinate = event->y;
2068         }
2069       else
2070         {
2071           range->slide_initial_slider_position = range->layout->slider.x;
2072           range->slide_initial_coordinate = event->x;
2073         }
2074
2075       range_grab_add (range, MOUSE_SLIDER, event->button);
2076
2077       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2078
2079       /* force a redraw, if the active slider is drawn differently to the
2080        * prelight one
2081        */
2082       if (activate_slider)
2083         gtk_widget_queue_draw (widget);
2084
2085       if (need_value_update)
2086         update_slider_position (range, event->x, event->y);
2087
2088       return TRUE;
2089     }
2090   
2091   return FALSE;
2092 }
2093
2094 /* During a slide, move the slider as required given new mouse position */
2095 static void
2096 update_slider_position (GtkRange *range,
2097                         gint      mouse_x,
2098                         gint      mouse_y)
2099 {
2100   gint delta;
2101   gint c;
2102   gdouble new_value;
2103   gboolean handled;
2104
2105   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2106     delta = mouse_y - range->slide_initial_coordinate;
2107   else
2108     delta = mouse_x - range->slide_initial_coordinate;
2109
2110   c = range->slide_initial_slider_position + delta;
2111
2112   new_value = coord_to_value (range, c);
2113   
2114   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2115                  &handled);
2116 }
2117
2118 static void 
2119 stop_scrolling (GtkRange *range)
2120 {
2121   range_grab_remove (range);
2122   gtk_range_remove_step_timer (range);
2123   /* Flush any pending discontinuous/delayed updates */
2124   gtk_range_update_value (range);
2125 }
2126
2127 static gboolean
2128 gtk_range_grab_broken (GtkWidget          *widget,
2129                        GdkEventGrabBroken *event)
2130 {
2131   GtkRange *range = GTK_RANGE (widget);
2132
2133   if (range->layout->grab_location != MOUSE_OUTSIDE)
2134     {
2135       if (range->layout->grab_location == MOUSE_SLIDER)
2136         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2137       
2138       stop_scrolling (range);
2139       
2140       return TRUE;
2141     }
2142   
2143   return FALSE;
2144 }
2145
2146 static gint
2147 gtk_range_button_release (GtkWidget      *widget,
2148                           GdkEventButton *event)
2149 {
2150   GtkRange *range = GTK_RANGE (widget);
2151
2152   if (event->window == range->event_window)
2153     {
2154       range->layout->mouse_x = event->x;
2155       range->layout->mouse_y = event->y;
2156     }
2157   else
2158     {
2159       gdk_window_get_pointer (range->event_window,
2160                               &range->layout->mouse_x,
2161                               &range->layout->mouse_y,
2162                               NULL);
2163     }
2164   
2165   if (range->layout->grab_button == event->button)
2166     {
2167       if (range->layout->grab_location == MOUSE_SLIDER)
2168         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2169
2170       stop_scrolling (range);
2171       
2172       return TRUE;
2173     }
2174
2175   return FALSE;
2176 }
2177
2178 /**
2179  * _gtk_range_get_wheel_delta:
2180  * @range: a #GtkRange
2181  * @direction: A #GdkScrollDirection
2182  * 
2183  * Returns a good step value for the mouse wheel.
2184  * 
2185  * Return value: A good step value for the mouse wheel. 
2186  * 
2187  * Since: 2.4
2188  **/
2189 gdouble
2190 _gtk_range_get_wheel_delta (GtkRange           *range,
2191                             GdkScrollDirection  direction)
2192 {
2193   GtkAdjustment *adj = range->adjustment;
2194   gdouble delta;
2195
2196   if (GTK_IS_SCROLLBAR (range))
2197     delta = pow (adj->page_size, 2.0 / 3.0);
2198   else
2199     delta = adj->step_increment * 2;
2200   
2201   if (direction == GDK_SCROLL_UP ||
2202       direction == GDK_SCROLL_LEFT)
2203     delta = - delta;
2204   
2205   if (range->inverted)
2206     delta = - delta;
2207
2208   return delta;
2209 }
2210       
2211 static gboolean
2212 gtk_range_scroll_event (GtkWidget      *widget,
2213                         GdkEventScroll *event)
2214 {
2215   GtkRange *range = GTK_RANGE (widget);
2216
2217   if (GTK_WIDGET_REALIZED (range))
2218     {
2219       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2220       gdouble delta;
2221       gboolean handled;
2222
2223       delta = _gtk_range_get_wheel_delta (range, event->direction);
2224
2225       g_signal_emit (range, signals[CHANGE_VALUE], 0,
2226                      GTK_SCROLL_JUMP, adj->value + delta,
2227                      &handled);
2228       
2229       /* Policy DELAYED makes sense with scroll events,
2230        * but DISCONTINUOUS doesn't, so we update immediately
2231        * for DISCONTINUOUS
2232        */
2233       if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2234         gtk_range_update_value (range);
2235     }
2236
2237   return TRUE;
2238 }
2239
2240 static gboolean
2241 gtk_range_motion_notify (GtkWidget      *widget,
2242                          GdkEventMotion *event)
2243 {
2244   GtkRange *range;
2245   gint x, y;
2246
2247   range = GTK_RANGE (widget);
2248
2249   gdk_window_get_pointer (range->event_window, &x, &y, NULL);
2250   
2251   range->layout->mouse_x = x;
2252   range->layout->mouse_y = y;
2253
2254   if (gtk_range_update_mouse_location (range))
2255     gtk_widget_queue_draw (widget);
2256
2257   if (range->layout->grab_location == MOUSE_SLIDER)
2258     update_slider_position (range, x, y);
2259
2260   /* We handled the event if the mouse was in the range_rect */
2261   return range->layout->mouse_location != MOUSE_OUTSIDE;
2262 }
2263
2264 static gboolean
2265 gtk_range_enter_notify (GtkWidget        *widget,
2266                         GdkEventCrossing *event)
2267 {
2268   GtkRange *range = GTK_RANGE (widget);
2269
2270   range->layout->mouse_x = event->x;
2271   range->layout->mouse_y = event->y;
2272
2273   if (gtk_range_update_mouse_location (range))
2274     gtk_widget_queue_draw (widget);
2275   
2276   return TRUE;
2277 }
2278
2279 static gboolean
2280 gtk_range_leave_notify (GtkWidget        *widget,
2281                         GdkEventCrossing *event)
2282 {
2283   GtkRange *range = GTK_RANGE (widget);
2284
2285   range->layout->mouse_x = -1;
2286   range->layout->mouse_y = -1;
2287
2288   if (gtk_range_update_mouse_location (range))
2289     gtk_widget_queue_draw (widget);
2290   
2291   return TRUE;
2292 }
2293
2294 static void
2295 gtk_range_grab_notify (GtkWidget *widget,
2296                        gboolean   was_grabbed)
2297 {
2298   if (!was_grabbed)
2299     stop_scrolling (GTK_RANGE (widget));
2300 }
2301
2302 static void
2303 gtk_range_state_changed (GtkWidget    *widget,
2304                          GtkStateType  previous_state)
2305 {
2306   if (!GTK_WIDGET_IS_SENSITIVE (widget)) 
2307     stop_scrolling (GTK_RANGE (widget));
2308 }
2309
2310 #define check_rectangle(rectangle1, rectangle2)              \
2311   {                                                          \
2312     if (rectangle1.x != rectangle2.x) return TRUE;           \
2313     if (rectangle1.y != rectangle2.y) return TRUE;           \
2314     if (rectangle1.width  != rectangle2.width)  return TRUE; \
2315     if (rectangle1.height != rectangle2.height) return TRUE; \
2316   }
2317
2318 static gboolean
2319 layout_changed (GtkRangeLayout *layout1, 
2320                 GtkRangeLayout *layout2)
2321 {
2322   check_rectangle (layout1->slider, layout2->slider);
2323   check_rectangle (layout1->trough, layout2->trough);
2324   check_rectangle (layout1->stepper_a, layout2->stepper_a);
2325   check_rectangle (layout1->stepper_d, layout2->stepper_d);
2326   check_rectangle (layout1->stepper_b, layout2->stepper_b);
2327   check_rectangle (layout1->stepper_c, layout2->stepper_c);
2328
2329   if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2330   if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2331
2332   return FALSE;
2333 }
2334
2335 static void
2336 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2337                               gpointer       data)
2338 {
2339   GtkRange *range = GTK_RANGE (data);
2340   /* create a copy of the layout */
2341   GtkRangeLayout layout = *range->layout;
2342
2343   range->need_recalc = TRUE;
2344   gtk_range_calc_layout (range, range->adjustment->value);
2345   
2346   /* now check whether the layout changed  */
2347   if (layout_changed (range->layout, &layout))
2348     gtk_widget_queue_draw (GTK_WIDGET (range));
2349
2350   /* Note that we don't round off to range->round_digits here.
2351    * that's because it's really broken to change a value
2352    * in response to a change signal on that value; round_digits
2353    * is therefore defined to be a filter on what the GtkRange
2354    * can input into the adjustment, not a filter that the GtkRange
2355    * will enforce on the adjustment.
2356    */
2357 }
2358
2359 static gboolean
2360 force_repaint (gpointer data)
2361 {
2362   GtkRange *range = GTK_RANGE (data);
2363   range->layout->repaint_id = 0;
2364   if (GTK_WIDGET_DRAWABLE (range))
2365     gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2366   return FALSE;
2367 }
2368
2369 static void
2370 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2371                                     gpointer       data)
2372 {
2373   GtkRange *range = GTK_RANGE (data);
2374   /* create a copy of the layout */
2375   GtkRangeLayout layout = *range->layout;
2376
2377   range->need_recalc = TRUE;
2378   gtk_range_calc_layout (range, range->adjustment->value);
2379   
2380   /* now check whether the layout changed  */
2381   if (layout_changed (range->layout, &layout))
2382     {
2383       gtk_widget_queue_draw (GTK_WIDGET (range));
2384       /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2385       if (!range->layout->repaint_id)
2386         range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
2387     }
2388   
2389   /* Note that we don't round off to range->round_digits here.
2390    * that's because it's really broken to change a value
2391    * in response to a change signal on that value; round_digits
2392    * is therefore defined to be a filter on what the GtkRange
2393    * can input into the adjustment, not a filter that the GtkRange
2394    * will enforce on the adjustment.
2395    */
2396
2397   g_signal_emit (range, signals[VALUE_CHANGED], 0);
2398 }
2399
2400 static void
2401 gtk_range_style_set (GtkWidget *widget,
2402                      GtkStyle  *previous_style)
2403 {
2404   GtkRange *range = GTK_RANGE (widget);
2405
2406   range->need_recalc = TRUE;
2407
2408   GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
2409 }
2410
2411 static void
2412 step_back (GtkRange *range)
2413 {
2414   gdouble newval;
2415   gboolean handled;
2416   
2417   newval = range->adjustment->value - range->adjustment->step_increment;
2418   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2419                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2420 }
2421
2422 static void
2423 step_forward (GtkRange *range)
2424 {
2425   gdouble newval;
2426   gboolean handled;
2427
2428   newval = range->adjustment->value + range->adjustment->step_increment;
2429   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2430                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
2431 }
2432
2433
2434 static void
2435 page_back (GtkRange *range)
2436 {
2437   gdouble newval;
2438   gboolean handled;
2439
2440   newval = range->adjustment->value - range->adjustment->page_increment;
2441   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2442                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2443 }
2444
2445 static void
2446 page_forward (GtkRange *range)
2447 {
2448   gdouble newval;
2449   gboolean handled;
2450
2451   newval = range->adjustment->value + range->adjustment->page_increment;
2452   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2453                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2454 }
2455
2456 static void
2457 scroll_begin (GtkRange *range)
2458 {
2459   gboolean handled;
2460   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2461                  GTK_SCROLL_START, range->adjustment->lower,
2462                  &handled);
2463 }
2464
2465 static void
2466 scroll_end (GtkRange *range)
2467 {
2468   gdouble newval;
2469   gboolean handled;
2470
2471   newval = range->adjustment->upper - range->adjustment->page_size;
2472   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2473                  &handled);
2474 }
2475
2476 static gboolean
2477 gtk_range_scroll (GtkRange     *range,
2478                   GtkScrollType scroll)
2479 {
2480   gdouble old_value = range->adjustment->value;
2481
2482   switch (scroll)
2483     {
2484     case GTK_SCROLL_STEP_LEFT:
2485       if (should_invert (range))
2486         step_forward (range);
2487       else
2488         step_back (range);
2489       break;
2490                     
2491     case GTK_SCROLL_STEP_UP:
2492       if (should_invert (range))
2493         step_forward (range);
2494       else
2495         step_back (range);
2496       break;
2497
2498     case GTK_SCROLL_STEP_RIGHT:
2499       if (should_invert (range))
2500         step_back (range);
2501       else
2502         step_forward (range);
2503       break;
2504                     
2505     case GTK_SCROLL_STEP_DOWN:
2506       if (should_invert (range))
2507         step_back (range);
2508       else
2509         step_forward (range);
2510       break;
2511                   
2512     case GTK_SCROLL_STEP_BACKWARD:
2513       step_back (range);
2514       break;
2515                   
2516     case GTK_SCROLL_STEP_FORWARD:
2517       step_forward (range);
2518       break;
2519
2520     case GTK_SCROLL_PAGE_LEFT:
2521       if (should_invert (range))
2522         page_forward (range);
2523       else
2524         page_back (range);
2525       break;
2526                     
2527     case GTK_SCROLL_PAGE_UP:
2528       if (should_invert (range))
2529         page_forward (range);
2530       else
2531         page_back (range);
2532       break;
2533
2534     case GTK_SCROLL_PAGE_RIGHT:
2535       if (should_invert (range))
2536         page_back (range);
2537       else
2538         page_forward (range);
2539       break;
2540                     
2541     case GTK_SCROLL_PAGE_DOWN:
2542       if (should_invert (range))
2543         page_back (range);
2544       else
2545         page_forward (range);
2546       break;
2547                   
2548     case GTK_SCROLL_PAGE_BACKWARD:
2549       page_back (range);
2550       break;
2551                   
2552     case GTK_SCROLL_PAGE_FORWARD:
2553       page_forward (range);
2554       break;
2555
2556     case GTK_SCROLL_START:
2557       scroll_begin (range);
2558       break;
2559
2560     case GTK_SCROLL_END:
2561       scroll_end (range);
2562       break;
2563
2564     case GTK_SCROLL_JUMP:
2565       /* Used by CList, range doesn't use it. */
2566       break;
2567
2568     case GTK_SCROLL_NONE:
2569       break;
2570     }
2571
2572   return range->adjustment->value != old_value;
2573 }
2574
2575 static void
2576 gtk_range_move_slider (GtkRange     *range,
2577                        GtkScrollType scroll)
2578 {
2579   gboolean cursor_only;
2580
2581   g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2582                 "gtk-keynav-cursor-only", &cursor_only,
2583                 NULL);
2584
2585   if (cursor_only)
2586     {
2587       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2588
2589       if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2590         {
2591           if (scroll == GTK_SCROLL_STEP_UP ||
2592               scroll == GTK_SCROLL_STEP_DOWN)
2593             {
2594               if (toplevel)
2595                 gtk_widget_child_focus (toplevel,
2596                                         scroll == GTK_SCROLL_STEP_UP ?
2597                                         GTK_DIR_UP : GTK_DIR_DOWN);
2598               return;
2599             }
2600         }
2601       else
2602         {
2603           if (scroll == GTK_SCROLL_STEP_LEFT ||
2604               scroll == GTK_SCROLL_STEP_RIGHT)
2605             {
2606               if (toplevel)
2607                 gtk_widget_child_focus (toplevel,
2608                                         scroll == GTK_SCROLL_STEP_LEFT ?
2609                                         GTK_DIR_LEFT : GTK_DIR_RIGHT);
2610               return;
2611             }
2612         }
2613     }
2614
2615   if (! gtk_range_scroll (range, scroll))
2616     gtk_widget_error_bell (GTK_WIDGET (range));
2617
2618   /* Policy DELAYED makes sense with key events,
2619    * but DISCONTINUOUS doesn't, so we update immediately
2620    * for DISCONTINUOUS
2621    */
2622   if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2623     gtk_range_update_value (range);
2624 }
2625
2626 static void
2627 gtk_range_get_props (GtkRange  *range,
2628                      gint      *slider_width,
2629                      gint      *stepper_size,
2630                      gint      *focus_width,
2631                      gint      *trough_border,
2632                      gint      *stepper_spacing,
2633                      gboolean  *trough_under_steppers,
2634                      gint      *arrow_displacement_x,
2635                      gint      *arrow_displacement_y)
2636 {
2637   GtkWidget *widget =  GTK_WIDGET (range);
2638   gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
2639   gint tmp_stepper_spacing, tmp_trough_under_steppers;
2640   gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2641   
2642   gtk_widget_style_get (widget,
2643                         "slider-width", &tmp_slider_width,
2644                         "trough-border", &tmp_trough_border,
2645                         "stepper-size", &tmp_stepper_size,
2646                         "stepper-spacing", &tmp_stepper_spacing,
2647                         "trough-under-steppers", &tmp_trough_under_steppers,
2648                         "arrow-displacement-x", &tmp_arrow_displacement_x,
2649                         "arrow-displacement-y", &tmp_arrow_displacement_y,
2650                         NULL);
2651
2652   if (tmp_stepper_spacing > 0)
2653     tmp_trough_under_steppers = FALSE;
2654
2655   if (GTK_WIDGET_CAN_FOCUS (range))
2656     {
2657       gint focus_line_width;
2658       gint focus_padding;
2659       
2660       gtk_widget_style_get (GTK_WIDGET (range),
2661                             "focus-line-width", &focus_line_width,
2662                             "focus-padding", &focus_padding,
2663                             NULL);
2664
2665       tmp_focus_width = focus_line_width + focus_padding;
2666     }
2667   else
2668     {
2669       tmp_focus_width = 0;
2670     }
2671   
2672   if (slider_width)
2673     *slider_width = tmp_slider_width;
2674
2675   if (focus_width)
2676     *focus_width = tmp_focus_width;
2677
2678   if (trough_border)
2679     *trough_border = tmp_trough_border;
2680
2681   if (stepper_size)
2682     *stepper_size = tmp_stepper_size;
2683
2684   if (stepper_spacing)
2685     *stepper_spacing = tmp_stepper_spacing;
2686
2687   if (trough_under_steppers)
2688     *trough_under_steppers = tmp_trough_under_steppers;
2689
2690   if (arrow_displacement_x)
2691     *arrow_displacement_x = tmp_arrow_displacement_x;
2692
2693   if (arrow_displacement_y)
2694     *arrow_displacement_y = tmp_arrow_displacement_y;
2695 }
2696
2697 #define POINT_IN_RECT(xcoord, ycoord, rect) \
2698  ((xcoord) >= (rect).x &&                   \
2699   (xcoord) <  ((rect).x + (rect).width) &&  \
2700   (ycoord) >= (rect).y &&                   \
2701   (ycoord) <  ((rect).y + (rect).height))
2702
2703 /* Update mouse location, return TRUE if it changes */
2704 static gboolean
2705 gtk_range_update_mouse_location (GtkRange *range)
2706 {
2707   gint x, y;
2708   MouseLocation old;
2709   GtkWidget *widget;
2710
2711   widget = GTK_WIDGET (range);
2712   
2713   old = range->layout->mouse_location;
2714   
2715   x = range->layout->mouse_x;
2716   y = range->layout->mouse_y;
2717
2718   if (range->layout->grab_location != MOUSE_OUTSIDE)
2719     range->layout->mouse_location = range->layout->grab_location;
2720   else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
2721     range->layout->mouse_location = MOUSE_STEPPER_A;
2722   else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
2723     range->layout->mouse_location = MOUSE_STEPPER_B;
2724   else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
2725     range->layout->mouse_location = MOUSE_STEPPER_C;
2726   else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
2727     range->layout->mouse_location = MOUSE_STEPPER_D;
2728   else if (POINT_IN_RECT (x, y, range->layout->slider))
2729     range->layout->mouse_location = MOUSE_SLIDER;
2730   else if (POINT_IN_RECT (x, y, range->layout->trough))
2731     range->layout->mouse_location = MOUSE_TROUGH;
2732   else if (POINT_IN_RECT (x, y, widget->allocation))
2733     range->layout->mouse_location = MOUSE_WIDGET;
2734   else
2735     range->layout->mouse_location = MOUSE_OUTSIDE;
2736
2737   return old != range->layout->mouse_location;
2738 }
2739
2740 /* Clamp rect, border inside widget->allocation, such that we prefer
2741  * to take space from border not rect in all directions, and prefer to
2742  * give space to border over rect in one direction.
2743  */
2744 static void
2745 clamp_dimensions (GtkWidget    *widget,
2746                   GdkRectangle *rect,
2747                   GtkBorder    *border,
2748                   gboolean      border_expands_horizontally)
2749 {
2750   gint extra, shortage;
2751   
2752   g_return_if_fail (rect->x == 0);
2753   g_return_if_fail (rect->y == 0);  
2754   g_return_if_fail (rect->width >= 0);
2755   g_return_if_fail (rect->height >= 0);
2756
2757   /* Width */
2758   
2759   extra = widget->allocation.width - border->left - border->right - rect->width;
2760   if (extra > 0)
2761     {
2762       if (border_expands_horizontally)
2763         {
2764           border->left += extra / 2;
2765           border->right += extra / 2 + extra % 2;
2766         }
2767       else
2768         {
2769           rect->width += extra;
2770         }
2771     }
2772   
2773   /* See if we can fit rect, if not kill the border */
2774   shortage = rect->width - widget->allocation.width;
2775   if (shortage > 0)
2776     {
2777       rect->width = widget->allocation.width;
2778       /* lose the border */
2779       border->left = 0;
2780       border->right = 0;
2781     }
2782   else
2783     {
2784       /* See if we can fit rect with borders */
2785       shortage = rect->width + border->left + border->right -
2786         widget->allocation.width;
2787       if (shortage > 0)
2788         {
2789           /* Shrink borders */
2790           border->left -= shortage / 2;
2791           border->right -= shortage / 2 + shortage % 2;
2792         }
2793     }
2794
2795   /* Height */
2796   
2797   extra = widget->allocation.height - border->top - border->bottom - rect->height;
2798   if (extra > 0)
2799     {
2800       if (border_expands_horizontally)
2801         {
2802           /* don't expand border vertically */
2803           rect->height += extra;
2804         }
2805       else
2806         {
2807           border->top += extra / 2;
2808           border->bottom += extra / 2 + extra % 2;
2809         }
2810     }
2811   
2812   /* See if we can fit rect, if not kill the border */
2813   shortage = rect->height - widget->allocation.height;
2814   if (shortage > 0)
2815     {
2816       rect->height = widget->allocation.height;
2817       /* lose the border */
2818       border->top = 0;
2819       border->bottom = 0;
2820     }
2821   else
2822     {
2823       /* See if we can fit rect with borders */
2824       shortage = rect->height + border->top + border->bottom -
2825         widget->allocation.height;
2826       if (shortage > 0)
2827         {
2828           /* Shrink borders */
2829           border->top -= shortage / 2;
2830           border->bottom -= shortage / 2 + shortage % 2;
2831         }
2832     }
2833 }
2834
2835 static void
2836 gtk_range_calc_request (GtkRange      *range,
2837                         gint           slider_width,
2838                         gint           stepper_size,
2839                         gint           focus_width,
2840                         gint           trough_border,
2841                         gint           stepper_spacing,
2842                         GdkRectangle  *range_rect,
2843                         GtkBorder     *border,
2844                         gint          *n_steppers_p,
2845                         gboolean      *has_steppers_ab,
2846                         gboolean      *has_steppers_cd,
2847                         gint          *slider_length_p)
2848 {
2849   gint slider_length;
2850   gint n_steppers;
2851   gint n_steppers_ab;
2852   gint n_steppers_cd;
2853
2854   border->left = 0;
2855   border->right = 0;
2856   border->top = 0;
2857   border->bottom = 0;
2858
2859   if (GTK_RANGE_GET_CLASS (range)->get_range_border)
2860     GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
2861
2862   n_steppers_ab = 0;
2863   n_steppers_cd = 0;
2864
2865   if (range->has_stepper_a)
2866     n_steppers_ab += 1;
2867   if (range->has_stepper_b)
2868     n_steppers_ab += 1;
2869   if (range->has_stepper_c)
2870     n_steppers_cd += 1;
2871   if (range->has_stepper_d)
2872     n_steppers_cd += 1;
2873
2874   n_steppers = n_steppers_ab + n_steppers_cd;
2875
2876   slider_length = range->min_slider_size;
2877
2878   range_rect->x = 0;
2879   range_rect->y = 0;
2880   
2881   /* We never expand to fill available space in the small dimension
2882    * (i.e. vertical scrollbars are always a fixed width)
2883    */
2884   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2885     {
2886       range_rect->width = (focus_width + trough_border) * 2 + slider_width;
2887       range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2888
2889       if (n_steppers_ab > 0)
2890         range_rect->height += stepper_spacing;
2891
2892       if (n_steppers_cd > 0)
2893         range_rect->height += stepper_spacing;
2894     }
2895   else
2896     {
2897       range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2898       range_rect->height = (focus_width + trough_border) * 2 + slider_width;
2899
2900       if (n_steppers_ab > 0)
2901         range_rect->width += stepper_spacing;
2902
2903       if (n_steppers_cd > 0)
2904         range_rect->width += stepper_spacing;
2905     }
2906
2907   if (n_steppers_p)
2908     *n_steppers_p = n_steppers;
2909
2910   if (has_steppers_ab)
2911     *has_steppers_ab = (n_steppers_ab > 0);
2912
2913   if (has_steppers_cd)
2914     *has_steppers_cd = (n_steppers_cd > 0);
2915
2916   if (slider_length_p)
2917     *slider_length_p = slider_length;
2918 }
2919
2920 static void
2921 gtk_range_calc_layout (GtkRange *range,
2922                        gdouble   adjustment_value)
2923 {
2924   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
2925   gint slider_length;
2926   GtkBorder border;
2927   gint n_steppers;
2928   gboolean has_steppers_ab;
2929   gboolean has_steppers_cd;
2930   gboolean trough_under_steppers;
2931   GdkRectangle range_rect;
2932   GtkRangeLayout *layout;
2933   GtkWidget *widget;
2934   
2935   if (!range->need_recalc)
2936     return;
2937
2938   /* If we have a too-small allocation, we prefer the steppers over
2939    * the trough/slider, probably the steppers are a more useful
2940    * feature in small spaces.
2941    *
2942    * Also, we prefer to draw the range itself rather than the border
2943    * areas if there's a conflict, since the borders will be decoration
2944    * not controls. Though this depends on subclasses cooperating by
2945    * not drawing on range->range_rect.
2946    */
2947
2948   widget = GTK_WIDGET (range);
2949   layout = range->layout;
2950   
2951   gtk_range_get_props (range,
2952                        &slider_width, &stepper_size,
2953                        &focus_width, &trough_border,
2954                        &stepper_spacing, &trough_under_steppers,
2955                        NULL, NULL);
2956
2957   gtk_range_calc_request (range, 
2958                           slider_width, stepper_size,
2959                           focus_width, trough_border, stepper_spacing,
2960                           &range_rect, &border, &n_steppers,
2961                           &has_steppers_ab, &has_steppers_cd, &slider_length);
2962   
2963   /* We never expand to fill available space in the small dimension
2964    * (i.e. vertical scrollbars are always a fixed width)
2965    */
2966   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2967     {
2968       clamp_dimensions (widget, &range_rect, &border, TRUE);
2969     }
2970   else
2971     {
2972       clamp_dimensions (widget, &range_rect, &border, FALSE);
2973     }
2974   
2975   range_rect.x = border.left;
2976   range_rect.y = border.top;
2977   
2978   range->range_rect = range_rect;
2979   
2980   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2981     {
2982       gint stepper_width, stepper_height;
2983
2984       /* Steppers are the width of the range, and stepper_size in
2985        * height, or if we don't have enough height, divided equally
2986        * among available space.
2987        */
2988       stepper_width = range_rect.width - focus_width * 2;
2989
2990       if (trough_under_steppers)
2991         stepper_width -= trough_border * 2;
2992
2993       if (stepper_width < 1)
2994         stepper_width = range_rect.width; /* screw the trough border */
2995
2996       if (n_steppers == 0)
2997         stepper_height = 0; /* avoid divide by n_steppers */
2998       else
2999         stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
3000
3001       /* Stepper A */
3002       
3003       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3004       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3005
3006       if (range->has_stepper_a)
3007         {
3008           layout->stepper_a.width = stepper_width;
3009           layout->stepper_a.height = stepper_height;
3010         }
3011       else
3012         {
3013           layout->stepper_a.width = 0;
3014           layout->stepper_a.height = 0;
3015         }
3016
3017       /* Stepper B */
3018       
3019       layout->stepper_b.x = layout->stepper_a.x;
3020       layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
3021
3022       if (range->has_stepper_b)
3023         {
3024           layout->stepper_b.width = stepper_width;
3025           layout->stepper_b.height = stepper_height;
3026         }
3027       else
3028         {
3029           layout->stepper_b.width = 0;
3030           layout->stepper_b.height = 0;
3031         }
3032
3033       /* Stepper D */
3034
3035       if (range->has_stepper_d)
3036         {
3037           layout->stepper_d.width = stepper_width;
3038           layout->stepper_d.height = stepper_height;
3039         }
3040       else
3041         {
3042           layout->stepper_d.width = 0;
3043           layout->stepper_d.height = 0;
3044         }
3045       
3046       layout->stepper_d.x = layout->stepper_a.x;
3047       layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
3048
3049       /* Stepper C */
3050
3051       if (range->has_stepper_c)
3052         {
3053           layout->stepper_c.width = stepper_width;
3054           layout->stepper_c.height = stepper_height;
3055         }
3056       else
3057         {
3058           layout->stepper_c.width = 0;
3059           layout->stepper_c.height = 0;
3060         }
3061       
3062       layout->stepper_c.x = layout->stepper_a.x;
3063       layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
3064
3065       /* Now the trough is the remaining space between steppers B and C,
3066        * if any, minus spacing
3067        */
3068       layout->trough.x = range_rect.x;
3069       layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
3070       layout->trough.width = range_rect.width;
3071       layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
3072
3073       /* Slider fits into the trough, with stepper_spacing on either side,
3074        * and the size/position based on the adjustment or fixed, depending.
3075        */
3076       layout->slider.x = layout->trough.x + focus_width + trough_border;
3077       layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
3078
3079       /* Compute slider position/length */
3080       {
3081         gint y, bottom, top, height;
3082         
3083         top = layout->trough.y;
3084         bottom = layout->trough.y + layout->trough.height;
3085
3086         if (! trough_under_steppers)
3087           {
3088             top += trough_border;
3089             bottom -= trough_border;
3090           }
3091
3092         /* slider height is the fraction (page_size /
3093          * total_adjustment_range) times the trough height in pixels
3094          */
3095
3096         if (range->adjustment->upper - range->adjustment->lower != 0)
3097           height = ((bottom - top) * (range->adjustment->page_size /
3098                                        (range->adjustment->upper - range->adjustment->lower)));
3099         else
3100           height = range->min_slider_size;
3101         
3102         if (height < range->min_slider_size ||
3103             range->slider_size_fixed)
3104           height = range->min_slider_size;
3105
3106         height = MIN (height, layout->trough.height);
3107         
3108         y = top;
3109         
3110         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3111           y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
3112                                           (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3113         
3114         y = CLAMP (y, top, bottom);
3115         
3116         if (should_invert (range))
3117           y = bottom - (y - top + height);
3118         
3119         layout->slider.y = y;
3120         layout->slider.height = height;
3121
3122         /* These are publically exported */
3123         range->slider_start = layout->slider.y;
3124         range->slider_end = layout->slider.y + layout->slider.height;
3125       }
3126     }
3127   else
3128     {
3129       gint stepper_width, stepper_height;
3130
3131       /* Steppers are the height of the range, and stepper_size in
3132        * width, or if we don't have enough width, divided equally
3133        * among available space.
3134        */
3135       stepper_height = range_rect.height + focus_width * 2;
3136
3137       if (trough_under_steppers)
3138         stepper_height -= trough_border * 2;
3139
3140       if (stepper_height < 1)
3141         stepper_height = range_rect.height; /* screw the trough border */
3142
3143       if (n_steppers == 0)
3144         stepper_width = 0; /* avoid divide by n_steppers */
3145       else
3146         stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3147
3148       /* Stepper A */
3149       
3150       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3151       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3152
3153       if (range->has_stepper_a)
3154         {
3155           layout->stepper_a.width = stepper_width;
3156           layout->stepper_a.height = stepper_height;
3157         }
3158       else
3159         {
3160           layout->stepper_a.width = 0;
3161           layout->stepper_a.height = 0;
3162         }
3163
3164       /* Stepper B */
3165       
3166       layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3167       layout->stepper_b.y = layout->stepper_a.y;
3168
3169       if (range->has_stepper_b)
3170         {
3171           layout->stepper_b.width = stepper_width;
3172           layout->stepper_b.height = stepper_height;
3173         }
3174       else
3175         {
3176           layout->stepper_b.width = 0;
3177           layout->stepper_b.height = 0;
3178         }
3179
3180       /* Stepper D */
3181
3182       if (range->has_stepper_d)
3183         {
3184           layout->stepper_d.width = stepper_width;
3185           layout->stepper_d.height = stepper_height;
3186         }
3187       else
3188         {
3189           layout->stepper_d.width = 0;
3190           layout->stepper_d.height = 0;
3191         }
3192
3193       layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3194       layout->stepper_d.y = layout->stepper_a.y;
3195
3196
3197       /* Stepper C */
3198
3199       if (range->has_stepper_c)
3200         {
3201           layout->stepper_c.width = stepper_width;
3202           layout->stepper_c.height = stepper_height;
3203         }
3204       else
3205         {
3206           layout->stepper_c.width = 0;
3207           layout->stepper_c.height = 0;
3208         }
3209       
3210       layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3211       layout->stepper_c.y = layout->stepper_a.y;
3212
3213       /* Now the trough is the remaining space between steppers B and C,
3214        * if any
3215        */
3216       layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3217       layout->trough.y = range_rect.y;
3218
3219       layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3220       layout->trough.height = range_rect.height;
3221
3222       /* Slider fits into the trough, with stepper_spacing on either side,
3223        * and the size/position based on the adjustment or fixed, depending.
3224        */
3225       layout->slider.y = layout->trough.y + focus_width + trough_border;
3226       layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3227
3228       /* Compute slider position/length */
3229       {
3230         gint x, left, right, width;
3231         
3232         left = layout->trough.x;
3233         right = layout->trough.x + layout->trough.width;
3234
3235         if (! trough_under_steppers)
3236           {
3237             left += trough_border;
3238             right -= trough_border;
3239           }
3240
3241         /* slider width is the fraction (page_size /
3242          * total_adjustment_range) times the trough width in pixels
3243          */
3244         
3245         if (range->adjustment->upper - range->adjustment->lower != 0)
3246           width = ((right - left) * (range->adjustment->page_size /
3247                                    (range->adjustment->upper - range->adjustment->lower)));
3248         else
3249           width = range->min_slider_size;
3250         
3251         if (width < range->min_slider_size ||
3252             range->slider_size_fixed)
3253           width = range->min_slider_size;
3254         
3255         width = MIN (width, layout->trough.width);
3256         
3257         x = left;
3258         
3259         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3260           x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3261                                          (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3262         
3263         x = CLAMP (x, left, right);
3264         
3265         if (should_invert (range))
3266           x = right - (x - left + width);
3267         
3268         layout->slider.x = x;
3269         layout->slider.width = width;
3270
3271         /* These are publically exported */
3272         range->slider_start = layout->slider.x;
3273         range->slider_end = layout->slider.x + layout->slider.width;
3274       }
3275     }
3276   
3277   gtk_range_update_mouse_location (range);
3278
3279   switch (range->layout->upper_sensitivity)
3280     {
3281     case GTK_SENSITIVITY_AUTO:
3282       range->layout->upper_sensitive =
3283         (range->adjustment->value <
3284          (range->adjustment->upper - range->adjustment->page_size));
3285       break;
3286
3287     case GTK_SENSITIVITY_ON:
3288       range->layout->upper_sensitive = TRUE;
3289       break;
3290
3291     case GTK_SENSITIVITY_OFF:
3292       range->layout->upper_sensitive = FALSE;
3293       break;
3294     }
3295
3296   switch (range->layout->lower_sensitivity)
3297     {
3298     case GTK_SENSITIVITY_AUTO:
3299       range->layout->lower_sensitive =
3300         (range->adjustment->value > range->adjustment->lower);
3301       break;
3302
3303     case GTK_SENSITIVITY_ON:
3304       range->layout->lower_sensitive = TRUE;
3305       break;
3306
3307     case GTK_SENSITIVITY_OFF:
3308       range->layout->lower_sensitive = FALSE;
3309       break;
3310     }
3311 }
3312
3313 static GdkRectangle*
3314 get_area (GtkRange     *range,
3315           MouseLocation location)
3316 {
3317   switch (location)
3318     {
3319     case MOUSE_STEPPER_A:
3320       return &range->layout->stepper_a;
3321     case MOUSE_STEPPER_B:
3322       return &range->layout->stepper_b;
3323     case MOUSE_STEPPER_C:
3324       return &range->layout->stepper_c;
3325     case MOUSE_STEPPER_D:
3326       return &range->layout->stepper_d;
3327     case MOUSE_TROUGH:
3328       return &range->layout->trough;
3329     case MOUSE_SLIDER:
3330       return &range->layout->slider;
3331     case MOUSE_WIDGET:
3332     case MOUSE_OUTSIDE:
3333       break;
3334     }
3335
3336   g_warning (G_STRLOC": bug");
3337   return NULL;
3338 }
3339
3340 static gboolean
3341 gtk_range_real_change_value (GtkRange     *range,
3342                              GtkScrollType scroll,
3343                              gdouble       value)
3344 {
3345   /* potentially adjust the bounds _before we clamp */
3346   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3347
3348   if (range->layout->restrict_to_fill_level)
3349     value = MIN (value, MAX (range->adjustment->lower,
3350                              range->layout->fill_level));
3351
3352   value = CLAMP (value, range->adjustment->lower,
3353                  (range->adjustment->upper - range->adjustment->page_size));
3354
3355   if (range->round_digits >= 0)
3356     {
3357       gdouble power;
3358       gint i;
3359
3360       i = range->round_digits;
3361       power = 1;
3362       while (i--)
3363         power *= 10;
3364       
3365       value = floor ((value * power) + 0.5) / power;
3366     }
3367   
3368   if (range->adjustment->value != value)
3369     {
3370       range->need_recalc = TRUE;
3371
3372       gtk_widget_queue_draw (GTK_WIDGET (range));
3373       
3374       switch (range->update_policy)
3375         {
3376         case GTK_UPDATE_CONTINUOUS:
3377           gtk_adjustment_set_value (range->adjustment, value);
3378           break;
3379
3380           /* Delayed means we update after a period of inactivity */
3381         case GTK_UPDATE_DELAYED:
3382           gtk_range_reset_update_timer (range);
3383           /* FALL THRU */
3384
3385           /* Discontinuous means we update on button release */
3386         case GTK_UPDATE_DISCONTINUOUS:
3387           /* don't emit value_changed signal */
3388           range->adjustment->value = value;
3389           range->update_pending = TRUE;
3390           break;
3391         }
3392     }
3393   return FALSE;
3394 }
3395
3396 static void
3397 gtk_range_update_value (GtkRange *range)
3398 {
3399   gtk_range_remove_update_timer (range);
3400   
3401   if (range->update_pending)
3402     {
3403       gtk_adjustment_value_changed (range->adjustment);
3404
3405       range->update_pending = FALSE;
3406     }
3407 }
3408
3409 struct _GtkRangeStepTimer
3410 {
3411   guint timeout_id;
3412   GtkScrollType step;
3413 };
3414
3415 static gboolean
3416 second_timeout (gpointer data)
3417 {
3418   GtkRange *range;
3419
3420   range = GTK_RANGE (data);
3421   gtk_range_scroll (range, range->timer->step);
3422   
3423   return TRUE;
3424 }
3425
3426 static gboolean
3427 initial_timeout (gpointer data)
3428 {
3429   GtkRange    *range;
3430   GtkSettings *settings;
3431   guint        timeout;
3432
3433   settings = gtk_widget_get_settings (GTK_WIDGET (data));
3434   g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3435
3436   range = GTK_RANGE (data);
3437   range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3438                                             second_timeout,
3439                                             range);
3440   /* remove self */
3441   return FALSE;
3442 }
3443
3444 static void
3445 gtk_range_add_step_timer (GtkRange      *range,
3446                           GtkScrollType  step)
3447 {
3448   GtkSettings *settings;
3449   guint        timeout;
3450
3451   g_return_if_fail (range->timer == NULL);
3452   g_return_if_fail (step != GTK_SCROLL_NONE);
3453
3454   settings = gtk_widget_get_settings (GTK_WIDGET (range));
3455   g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3456
3457   range->timer = g_new (GtkRangeStepTimer, 1);
3458
3459   range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3460                                             initial_timeout,
3461                                             range);
3462   range->timer->step = step;
3463
3464   gtk_range_scroll (range, range->timer->step);
3465 }
3466
3467 static void
3468 gtk_range_remove_step_timer (GtkRange *range)
3469 {
3470   if (range->timer)
3471     {
3472       if (range->timer->timeout_id != 0)
3473         g_source_remove (range->timer->timeout_id);
3474
3475       g_free (range->timer);
3476
3477       range->timer = NULL;
3478     }
3479 }
3480
3481 static gboolean
3482 update_timeout (gpointer data)
3483 {
3484   GtkRange *range;
3485
3486   range = GTK_RANGE (data);
3487   gtk_range_update_value (range);
3488   range->update_timeout_id = 0;
3489
3490   /* self-remove */
3491   return FALSE;
3492 }
3493
3494 static void
3495 gtk_range_reset_update_timer (GtkRange *range)
3496 {
3497   gtk_range_remove_update_timer (range);
3498
3499   range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3500                                             update_timeout,
3501                                             range);
3502 }
3503
3504 static void
3505 gtk_range_remove_update_timer (GtkRange *range)
3506 {
3507   if (range->update_timeout_id != 0)
3508     {
3509       g_source_remove (range->update_timeout_id);
3510       range->update_timeout_id = 0;
3511     }
3512 }
3513
3514 #define __GTK_RANGE_C__
3515 #include "gtkaliasdef.c"