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