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