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