]> Pileus Git - ~andy/gtk/blob - gtk/gtkrange.c
Bug 569240 - Crasher when using markers
[~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   gtk_adjustment_set_value (range->adjustment, value);
994   gtk_adjustment_changed (range->adjustment);
995 }
996
997 /**
998  * gtk_range_set_value:
999  * @range: a #GtkRange
1000  * @value: new value of the range
1001  *
1002  * Sets the current value of the range; if the value is outside the
1003  * minimum or maximum range values, it will be clamped to fit inside
1004  * them. The range emits the #GtkRange::value-changed signal if the 
1005  * value changes.
1006  **/
1007 void
1008 gtk_range_set_value (GtkRange *range,
1009                      gdouble   value)
1010 {
1011   g_return_if_fail (GTK_IS_RANGE (range));
1012
1013   if (range->layout->restrict_to_fill_level)
1014     value = MIN (value, MAX (range->adjustment->lower,
1015                              range->layout->fill_level));
1016
1017   gtk_adjustment_set_value (range->adjustment, value);
1018 }
1019
1020 /**
1021  * gtk_range_get_value:
1022  * @range: a #GtkRange
1023  * 
1024  * Gets the current value of the range.
1025  * 
1026  * Return value: current value of the range.
1027  **/
1028 gdouble
1029 gtk_range_get_value (GtkRange *range)
1030 {
1031   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1032
1033   return range->adjustment->value;
1034 }
1035
1036 /**
1037  * gtk_range_set_show_fill_level:
1038  * @range:           A #GtkRange
1039  * @show_fill_level: Whether a fill level indicator graphics is shown.
1040  *
1041  * Sets whether a graphical fill level is show on the trough. See
1042  * gtk_range_set_fill_level() for a general description of the fill
1043  * level concept.
1044  *
1045  * Since: 2.12
1046  **/
1047 void
1048 gtk_range_set_show_fill_level (GtkRange *range,
1049                                gboolean  show_fill_level)
1050 {
1051   g_return_if_fail (GTK_IS_RANGE (range));
1052
1053   show_fill_level = show_fill_level ? TRUE : FALSE;
1054
1055   if (show_fill_level != range->layout->show_fill_level)
1056     {
1057       range->layout->show_fill_level = show_fill_level;
1058       g_object_notify (G_OBJECT (range), "show-fill-level");
1059       gtk_widget_queue_draw (GTK_WIDGET (range));
1060     }
1061 }
1062
1063 /**
1064  * gtk_range_get_show_fill_level:
1065  * @range: A #GtkRange
1066  *
1067  * Gets whether the range displays the fill level graphically.
1068  *
1069  * Return value: %TRUE if @range shows the fill level.
1070  *
1071  * Since: 2.12
1072  **/
1073 gboolean
1074 gtk_range_get_show_fill_level (GtkRange *range)
1075 {
1076   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1077
1078   return range->layout->show_fill_level;
1079 }
1080
1081 /**
1082  * gtk_range_set_restrict_to_fill_level:
1083  * @range:                  A #GtkRange
1084  * @restrict_to_fill_level: Whether the fill level restricts slider movement.
1085  *
1086  * Sets whether the slider is restricted to the fill level. See
1087  * gtk_range_set_fill_level() for a general description of the fill
1088  * level concept.
1089  *
1090  * Since: 2.12
1091  **/
1092 void
1093 gtk_range_set_restrict_to_fill_level (GtkRange *range,
1094                                       gboolean  restrict_to_fill_level)
1095 {
1096   g_return_if_fail (GTK_IS_RANGE (range));
1097
1098   restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
1099
1100   if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
1101     {
1102       range->layout->restrict_to_fill_level = restrict_to_fill_level;
1103       g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
1104
1105       gtk_range_set_value (range, gtk_range_get_value (range));
1106     }
1107 }
1108
1109 /**
1110  * gtk_range_get_restrict_to_fill_level:
1111  * @range: A #GtkRange
1112  *
1113  * Gets whether the range is restricted to the fill level.
1114  *
1115  * Return value: %TRUE if @range is restricted to the fill level.
1116  *
1117  * Since: 2.12
1118  **/
1119 gboolean
1120 gtk_range_get_restrict_to_fill_level (GtkRange *range)
1121 {
1122   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1123
1124   return range->layout->restrict_to_fill_level;
1125 }
1126
1127 /**
1128  * gtk_range_set_fill_level:
1129  * @range: a #GtkRange
1130  * @fill_level: the new position of the fill level indicator
1131  *
1132  * Set the new position of the fill level indicator.
1133  *
1134  * The "fill level" is probably best described by its most prominent
1135  * use case, which is an indicator for the amount of pre-buffering in
1136  * a streaming media player. In that use case, the value of the range
1137  * would indicate the current play position, and the fill level would
1138  * be the position up to which the file/stream has been downloaded.
1139  *
1140  * This amount of prebuffering can be displayed on the range's trough
1141  * and is themeable separately from the trough. To enable fill level
1142  * display, use gtk_range_set_show_fill_level(). The range defaults
1143  * to not showing the fill level.
1144  *
1145  * Additionally, it's possible to restrict the range's slider position
1146  * to values which are smaller than the fill level. This is controller
1147  * by gtk_range_set_restrict_to_fill_level() and is by default
1148  * enabled.
1149  *
1150  * Since: 2.12
1151  **/
1152 void
1153 gtk_range_set_fill_level (GtkRange *range,
1154                           gdouble   fill_level)
1155 {
1156   g_return_if_fail (GTK_IS_RANGE (range));
1157
1158   if (fill_level != range->layout->fill_level)
1159     {
1160       range->layout->fill_level = fill_level;
1161       g_object_notify (G_OBJECT (range), "fill-level");
1162
1163       if (range->layout->show_fill_level)
1164         gtk_widget_queue_draw (GTK_WIDGET (range));
1165
1166       if (range->layout->restrict_to_fill_level)
1167         gtk_range_set_value (range, gtk_range_get_value (range));
1168     }
1169 }
1170
1171 /**
1172  * gtk_range_get_fill_level:
1173  * @range : A #GtkRange
1174  *
1175  * Gets the current position of the fill level indicator.
1176  *
1177  * Return value: The current fill level
1178  *
1179  * Since: 2.12
1180  **/
1181 gdouble
1182 gtk_range_get_fill_level (GtkRange *range)
1183 {
1184   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1185
1186   return range->layout->fill_level;
1187 }
1188
1189 static gboolean
1190 should_invert (GtkRange *range)
1191 {  
1192   if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1193     return
1194       (range->inverted && !range->flippable) ||
1195       (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
1196       (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1197   else
1198     return range->inverted;
1199 }
1200
1201 static void
1202 gtk_range_destroy (GtkObject *object)
1203 {
1204   GtkRange *range = GTK_RANGE (object);
1205
1206   gtk_range_remove_step_timer (range);
1207   gtk_range_remove_update_timer (range);
1208
1209   if (range->layout->repaint_id)
1210     g_source_remove (range->layout->repaint_id);
1211   range->layout->repaint_id = 0;
1212
1213   if (range->adjustment)
1214     {
1215       g_signal_handlers_disconnect_by_func (range->adjustment,
1216                                             gtk_range_adjustment_changed,
1217                                             range);
1218       g_signal_handlers_disconnect_by_func (range->adjustment,
1219                                             gtk_range_adjustment_value_changed,
1220                                             range);
1221       g_object_unref (range->adjustment);
1222       range->adjustment = NULL;
1223     }
1224
1225   if (range->layout->n_marks)
1226     {
1227       g_free (range->layout->marks);
1228       range->layout->marks = NULL;
1229       g_free (range->layout->mark_pos);
1230       range->layout->mark_pos = NULL;
1231       range->layout->n_marks = 0;
1232     }
1233
1234   GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
1235 }
1236
1237 static void
1238 gtk_range_size_request (GtkWidget      *widget,
1239                         GtkRequisition *requisition)
1240 {
1241   GtkRange *range;
1242   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1243   GdkRectangle range_rect;
1244   GtkBorder border;
1245   
1246   range = GTK_RANGE (widget);
1247   
1248   gtk_range_get_props (range,
1249                        &slider_width, &stepper_size,
1250                        &focus_width, &trough_border,
1251                        &stepper_spacing, NULL,
1252                        NULL, NULL);
1253
1254   gtk_range_calc_request (range, 
1255                           slider_width, stepper_size,
1256                           focus_width, trough_border, stepper_spacing,
1257                           &range_rect, &border, NULL, NULL, NULL, NULL);
1258
1259   requisition->width = range_rect.width + border.left + border.right;
1260   requisition->height = range_rect.height + border.top + border.bottom;
1261 }
1262
1263 static void
1264 gtk_range_size_allocate (GtkWidget     *widget,
1265                          GtkAllocation *allocation)
1266 {
1267   GtkRange *range;
1268
1269   range = GTK_RANGE (widget);
1270
1271   widget->allocation = *allocation;
1272   
1273   range->layout->recalc_marks = TRUE;
1274
1275   range->need_recalc = TRUE;
1276   gtk_range_calc_layout (range, range->adjustment->value);
1277
1278   if (GTK_WIDGET_REALIZED (range))
1279     gdk_window_move_resize (range->event_window,
1280                             widget->allocation.x,
1281                             widget->allocation.y,
1282                             widget->allocation.width,
1283                             widget->allocation.height);
1284 }
1285
1286 static void
1287 gtk_range_realize (GtkWidget *widget)
1288 {
1289   GtkRange *range;
1290   GdkWindowAttr attributes;
1291   gint attributes_mask;  
1292
1293   range = GTK_RANGE (widget);
1294
1295   gtk_range_calc_layout (range, range->adjustment->value);
1296   
1297   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1298
1299   widget->window = gtk_widget_get_parent_window (widget);
1300   g_object_ref (widget->window);
1301   
1302   attributes.window_type = GDK_WINDOW_CHILD;
1303   attributes.x = widget->allocation.x;
1304   attributes.y = widget->allocation.y;
1305   attributes.width = widget->allocation.width;
1306   attributes.height = widget->allocation.height;
1307   attributes.wclass = GDK_INPUT_ONLY;
1308   attributes.event_mask = gtk_widget_get_events (widget);
1309   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1310                             GDK_BUTTON_RELEASE_MASK |
1311                             GDK_ENTER_NOTIFY_MASK |
1312                             GDK_LEAVE_NOTIFY_MASK |
1313                             GDK_POINTER_MOTION_MASK |
1314                             GDK_POINTER_MOTION_HINT_MASK);
1315
1316   attributes_mask = GDK_WA_X | GDK_WA_Y;
1317
1318   range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1319                                         &attributes, attributes_mask);
1320   gdk_window_set_user_data (range->event_window, range);
1321
1322   widget->style = gtk_style_attach (widget->style, widget->window);
1323 }
1324
1325 static void
1326 gtk_range_unrealize (GtkWidget *widget)
1327 {
1328   GtkRange *range = GTK_RANGE (widget);
1329
1330   gtk_range_remove_step_timer (range);
1331   gtk_range_remove_update_timer (range);
1332   
1333   gdk_window_set_user_data (range->event_window, NULL);
1334   gdk_window_destroy (range->event_window);
1335   range->event_window = NULL;
1336
1337   GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1338 }
1339
1340 static void
1341 gtk_range_map (GtkWidget *widget)
1342 {
1343   GtkRange *range = GTK_RANGE (widget);
1344   
1345   gdk_window_show (range->event_window);
1346
1347   GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1348 }
1349
1350 static void
1351 gtk_range_unmap (GtkWidget *widget)
1352 {
1353   GtkRange *range = GTK_RANGE (widget);
1354     
1355   stop_scrolling (range);
1356
1357   gdk_window_hide (range->event_window);
1358
1359   GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1360 }
1361
1362 static const gchar *
1363 gtk_range_get_slider_detail (GtkRange *range)
1364 {
1365   const gchar *slider_detail;
1366
1367   if (range->layout->slider_detail_quark)
1368     return g_quark_to_string (range->layout->slider_detail_quark);
1369
1370   slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
1371
1372   if (slider_detail && slider_detail[0] == 'X')
1373     {
1374       gchar *detail = g_strdup (slider_detail);
1375
1376       detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1377
1378       range->layout->slider_detail_quark = g_quark_from_string (detail);
1379
1380       g_free (detail);
1381
1382       return g_quark_to_string (range->layout->slider_detail_quark);
1383     }
1384
1385   return slider_detail;
1386 }
1387
1388 static const gchar *
1389 gtk_range_get_stepper_detail (GtkRange *range)
1390 {
1391   const gchar *stepper_detail;
1392
1393   if (range->layout->stepper_detail_quark)
1394     return g_quark_to_string (range->layout->stepper_detail_quark);
1395
1396   stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
1397
1398   if (stepper_detail && stepper_detail[0] == 'X')
1399     {
1400       gchar *detail = g_strdup (stepper_detail);
1401
1402       detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1403
1404       range->layout->stepper_detail_quark = g_quark_from_string (detail);
1405
1406       g_free (detail);
1407
1408       return g_quark_to_string (range->layout->stepper_detail_quark);
1409     }
1410
1411   return stepper_detail;
1412 }
1413
1414 static void
1415 draw_stepper (GtkRange     *range,
1416               GdkRectangle *rect,
1417               GtkArrowType  arrow_type,
1418               gboolean      clicked,
1419               gboolean      prelighted,
1420               GdkRectangle *area)
1421 {
1422   GtkStateType state_type;
1423   GtkShadowType shadow_type;
1424   GdkRectangle intersection;
1425   GtkWidget *widget = GTK_WIDGET (range);
1426   gfloat arrow_scaling;
1427
1428   gint arrow_x;
1429   gint arrow_y;
1430   gint arrow_width;
1431   gint arrow_height;
1432
1433   gboolean arrow_sensitive = TRUE;
1434
1435   /* More to get the right clip region than for efficiency */
1436   if (!gdk_rectangle_intersect (area, rect, &intersection))
1437     return;
1438
1439   intersection.x += widget->allocation.x;
1440   intersection.y += widget->allocation.y;
1441
1442   if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1443                             arrow_type == GTK_ARROW_RIGHT)) ||
1444       (range->inverted  && (arrow_type == GTK_ARROW_UP ||
1445                             arrow_type == GTK_ARROW_LEFT)))
1446     {
1447       arrow_sensitive = range->layout->upper_sensitive;
1448     }
1449   else
1450     {
1451       arrow_sensitive = range->layout->lower_sensitive;
1452     }
1453
1454   if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
1455     state_type = GTK_STATE_INSENSITIVE;
1456   else if (clicked)
1457     state_type = GTK_STATE_ACTIVE;
1458   else if (prelighted)
1459     state_type = GTK_STATE_PRELIGHT;
1460   else 
1461     state_type = GTK_STATE_NORMAL;
1462
1463   if (clicked && arrow_sensitive)
1464     shadow_type = GTK_SHADOW_IN;
1465   else
1466     shadow_type = GTK_SHADOW_OUT;
1467
1468   gtk_paint_box (widget->style,
1469                  widget->window,
1470                  state_type, shadow_type,
1471                  &intersection, widget,
1472                  gtk_range_get_stepper_detail (range),
1473                  widget->allocation.x + rect->x,
1474                  widget->allocation.y + rect->y,
1475                  rect->width,
1476                  rect->height);
1477
1478   gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1479
1480   arrow_width = rect->width * arrow_scaling;
1481   arrow_height = rect->height * arrow_scaling;
1482   arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1483   arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1484
1485   if (clicked && arrow_sensitive)
1486     {
1487       gint arrow_displacement_x;
1488       gint arrow_displacement_y;
1489
1490       gtk_range_get_props (GTK_RANGE (widget),
1491                            NULL, NULL, NULL, NULL, NULL, NULL,
1492                            &arrow_displacement_x, &arrow_displacement_y);
1493       
1494       arrow_x += arrow_displacement_x;
1495       arrow_y += arrow_displacement_y;
1496     }
1497   
1498   gtk_paint_arrow (widget->style,
1499                    widget->window,
1500                    state_type, shadow_type, 
1501                    &intersection, widget,
1502                    gtk_range_get_stepper_detail (range),
1503                    arrow_type,
1504                    TRUE,
1505                    arrow_x, arrow_y, arrow_width, arrow_height);
1506 }
1507
1508 static gboolean
1509 gtk_range_expose (GtkWidget      *widget,
1510                   GdkEventExpose *event)
1511 {
1512   GtkRange *range = GTK_RANGE (widget);
1513   gboolean sensitive;
1514   GtkStateType state;
1515   GtkShadowType shadow_type;
1516   GdkRectangle expose_area;     /* Relative to widget->allocation */
1517   GdkRectangle area;
1518   gint focus_line_width = 0;
1519   gint focus_padding = 0;
1520   gboolean touchscreen;
1521
1522   g_object_get (gtk_widget_get_settings (widget),
1523                 "gtk-touchscreen-mode", &touchscreen,
1524                 NULL);
1525   if (GTK_WIDGET_CAN_FOCUS (range))
1526     gtk_widget_style_get (GTK_WIDGET (range),
1527                           "focus-line-width", &focus_line_width,
1528                           "focus-padding", &focus_padding,
1529                           NULL);
1530
1531   /* we're now exposing, so there's no need to force early repaints */
1532   if (range->layout->repaint_id)
1533     g_source_remove (range->layout->repaint_id);
1534   range->layout->repaint_id = 0;
1535
1536   expose_area = event->area;
1537   expose_area.x -= widget->allocation.x;
1538   expose_area.y -= widget->allocation.y;
1539   
1540   gtk_range_calc_marks (range);
1541   gtk_range_calc_layout (range, range->adjustment->value);
1542
1543   sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
1544
1545   /* Just to be confusing, we draw the trough for the whole
1546    * range rectangle, not the trough rectangle (the trough
1547    * rectangle is just for hit detection)
1548    */
1549   /* The gdk_rectangle_intersect is more to get the right
1550    * clip region (limited to range_rect) than for efficiency
1551    */
1552   if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1553                                &area))
1554     {
1555       gint     x      = (widget->allocation.x + range->range_rect.x +
1556                          focus_line_width + focus_padding);
1557       gint     y      = (widget->allocation.y + range->range_rect.y +
1558                          focus_line_width + focus_padding);
1559       gint     width  = (range->range_rect.width -
1560                          2 * (focus_line_width + focus_padding));
1561       gint     height = (range->range_rect.height -
1562                          2 * (focus_line_width + focus_padding));
1563       gboolean trough_side_details;
1564       gboolean trough_under_steppers;
1565       gint     stepper_size;
1566       gint     stepper_spacing;
1567
1568       area.x += widget->allocation.x;
1569       area.y += widget->allocation.y;
1570
1571       gtk_widget_style_get (GTK_WIDGET (range),
1572                             "trough-side-details",   &trough_side_details,
1573                             "trough-under-steppers", &trough_under_steppers,
1574                             "stepper-size",          &stepper_size,
1575                             "stepper-spacing",       &stepper_spacing,
1576                             NULL);
1577
1578       if (stepper_spacing > 0)
1579         trough_under_steppers = FALSE;
1580
1581       if (! trough_under_steppers)
1582         {
1583           gint offset  = 0;
1584           gint shorter = 0;
1585
1586           if (range->has_stepper_a)
1587             offset += stepper_size;
1588
1589           if (range->has_stepper_b)
1590             offset += stepper_size;
1591
1592           shorter += offset;
1593
1594           if (range->has_stepper_c)
1595             shorter += stepper_size;
1596
1597           if (range->has_stepper_d)
1598             shorter += stepper_size;
1599
1600           if (range->has_stepper_a || range->has_stepper_b)
1601             {
1602               offset  += stepper_spacing;
1603               shorter += stepper_spacing;
1604             }
1605
1606           if (range->has_stepper_c || range->has_stepper_d)
1607             {
1608               shorter += stepper_spacing;
1609             }
1610
1611           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1612             {
1613               x     += offset;
1614               width -= shorter;
1615             }
1616           else
1617             {
1618               y      += offset;
1619               height -= shorter;
1620             }
1621         }
1622
1623       if (! trough_side_details)
1624         {
1625           gtk_paint_box (widget->style,
1626                          widget->window,
1627                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1628                          GTK_SHADOW_IN,
1629                          &area, GTK_WIDGET(range), "trough",
1630                          x, y,
1631                          width, height);
1632         }
1633       else
1634         {
1635           gint trough_change_pos_x = width;
1636           gint trough_change_pos_y = height;
1637
1638           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1639             trough_change_pos_x = (range->layout->slider.x +
1640                                    range->layout->slider.width / 2 -
1641                                    (x - widget->allocation.x));
1642           else
1643             trough_change_pos_y = (range->layout->slider.y +
1644                                    range->layout->slider.height / 2 -
1645                                    (y - widget->allocation.y));
1646
1647           gtk_paint_box (widget->style,
1648                          widget->window,
1649                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1650                          GTK_SHADOW_IN,
1651                          &area, GTK_WIDGET (range),
1652                          should_invert (range) ? "trough-upper" : "trough-lower",
1653                          x, y,
1654                          trough_change_pos_x, trough_change_pos_y);
1655
1656           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1657             trough_change_pos_y = 0;
1658           else
1659             trough_change_pos_x = 0;
1660
1661           gtk_paint_box (widget->style,
1662                          widget->window,
1663                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1664                          GTK_SHADOW_IN,
1665                          &area, GTK_WIDGET (range),
1666                          should_invert (range) ? "trough-lower" : "trough-upper",
1667                          x + trough_change_pos_x, y + trough_change_pos_y,
1668                          width - trough_change_pos_x,
1669                          height - trough_change_pos_y);
1670         }
1671
1672       if (range->layout->show_fill_level &&
1673           range->adjustment->upper - range->adjustment->page_size -
1674           range->adjustment->lower != 0)
1675         {
1676           gdouble  fill_level  = range->layout->fill_level;
1677           gint     fill_x      = x;
1678           gint     fill_y      = y;
1679           gint     fill_width  = width;
1680           gint     fill_height = height;
1681           gchar   *fill_detail;
1682
1683           fill_level = CLAMP (fill_level, range->adjustment->lower,
1684                               range->adjustment->upper -
1685                               range->adjustment->page_size);
1686
1687           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1688             {
1689               fill_x     = widget->allocation.x + range->layout->trough.x;
1690               fill_width = (range->layout->slider.width +
1691                             (fill_level - range->adjustment->lower) /
1692                             (range->adjustment->upper -
1693                              range->adjustment->lower -
1694                              range->adjustment->page_size) *
1695                             (range->layout->trough.width -
1696                              range->layout->slider.width));
1697
1698               if (should_invert (range))
1699                 fill_x += range->layout->trough.width - fill_width;
1700             }
1701           else
1702             {
1703               fill_y      = widget->allocation.y + range->layout->trough.y;
1704               fill_height = (range->layout->slider.height +
1705                              (fill_level - range->adjustment->lower) /
1706                              (range->adjustment->upper -
1707                               range->adjustment->lower -
1708                               range->adjustment->page_size) *
1709                              (range->layout->trough.height -
1710                               range->layout->slider.height));
1711
1712               if (should_invert (range))
1713                 fill_y += range->layout->trough.height - fill_height;
1714             }
1715
1716           if (fill_level < range->adjustment->upper - range->adjustment->page_size)
1717             fill_detail = "trough-fill-level-full";
1718           else
1719             fill_detail = "trough-fill-level";
1720
1721           gtk_paint_box (widget->style,
1722                          widget->window,
1723                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1724                          GTK_SHADOW_OUT,
1725                          &area, GTK_WIDGET (range), fill_detail,
1726                          fill_x, fill_y,
1727                          fill_width, fill_height);
1728         }
1729
1730       if (sensitive &&
1731           GTK_WIDGET_HAS_FOCUS (range))
1732         gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
1733                          &area, widget, "trough",
1734                          widget->allocation.x + range->range_rect.x,
1735                          widget->allocation.y + range->range_rect.y,
1736                          range->range_rect.width,
1737                          range->range_rect.height);
1738     }
1739
1740   shadow_type = GTK_SHADOW_OUT;
1741
1742   if (!sensitive)
1743     state = GTK_STATE_INSENSITIVE;
1744   else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1745     state = GTK_STATE_PRELIGHT;
1746   else
1747     state = GTK_STATE_NORMAL;
1748
1749   if (range->layout->grab_location == MOUSE_SLIDER)
1750     {
1751       gboolean activate_slider;
1752
1753       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
1754
1755       if (activate_slider)
1756         {
1757           state = GTK_STATE_ACTIVE;
1758           shadow_type = GTK_SHADOW_IN;
1759         }
1760     }
1761
1762   if (gdk_rectangle_intersect (&expose_area,
1763                                &range->layout->slider,
1764                                &area))
1765     {
1766       area.x += widget->allocation.x;
1767       area.y += widget->allocation.y;
1768       
1769       gtk_paint_slider (widget->style,
1770                         widget->window,
1771                         state,
1772                         shadow_type,
1773                         &area,
1774                         widget,
1775                         gtk_range_get_slider_detail (range),
1776                         widget->allocation.x + range->layout->slider.x,
1777                         widget->allocation.y + range->layout->slider.y,
1778                         range->layout->slider.width,
1779                         range->layout->slider.height,
1780                         range->orientation);
1781     }
1782   
1783   if (range->has_stepper_a)
1784     draw_stepper (range, &range->layout->stepper_a,
1785                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1786                   range->layout->grab_location == MOUSE_STEPPER_A,
1787                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
1788                   &expose_area);
1789
1790   if (range->has_stepper_b)
1791     draw_stepper (range, &range->layout->stepper_b,
1792                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1793                   range->layout->grab_location == MOUSE_STEPPER_B,
1794                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
1795                   &expose_area);
1796
1797   if (range->has_stepper_c)
1798     draw_stepper (range, &range->layout->stepper_c,
1799                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1800                   range->layout->grab_location == MOUSE_STEPPER_C,
1801                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
1802                   &expose_area);
1803
1804   if (range->has_stepper_d)
1805     draw_stepper (range, &range->layout->stepper_d,
1806                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1807                   range->layout->grab_location == MOUSE_STEPPER_D,
1808                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
1809                   &expose_area);
1810   
1811   return FALSE;
1812 }
1813
1814 static void
1815 range_grab_add (GtkRange      *range,
1816                 MouseLocation  location,
1817                 gint           button)
1818 {
1819   /* we don't actually gtk_grab, since a button is down */
1820
1821   gtk_grab_add (GTK_WIDGET (range));
1822   
1823   range->layout->grab_location = location;
1824   range->layout->grab_button = button;
1825   
1826   if (gtk_range_update_mouse_location (range))
1827     gtk_widget_queue_draw (GTK_WIDGET (range));
1828 }
1829
1830 static void
1831 range_grab_remove (GtkRange *range)
1832 {
1833   MouseLocation location;
1834
1835   gtk_grab_remove (GTK_WIDGET (range));
1836  
1837   location = range->layout->grab_location; 
1838   range->layout->grab_location = MOUSE_OUTSIDE;
1839   range->layout->grab_button = 0;
1840
1841   if (gtk_range_update_mouse_location (range) ||
1842       location != MOUSE_OUTSIDE)
1843     gtk_widget_queue_draw (GTK_WIDGET (range));
1844 }
1845
1846 static GtkScrollType
1847 range_get_scroll_for_grab (GtkRange      *range)
1848
1849   gboolean invert;
1850
1851   invert = should_invert (range);
1852   switch (range->layout->grab_location)
1853     {
1854       /* Backward stepper */
1855     case MOUSE_STEPPER_A:
1856     case MOUSE_STEPPER_C:
1857       switch (range->layout->grab_button)
1858         {
1859         case 1:
1860           return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
1861           break;
1862         case 2:
1863           return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
1864           break;
1865         case 3:
1866           return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
1867           break;
1868         }
1869       break;
1870
1871       /* Forward stepper */
1872     case MOUSE_STEPPER_B:
1873     case MOUSE_STEPPER_D:
1874       switch (range->layout->grab_button)
1875         {
1876         case 1:
1877           return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
1878           break;
1879         case 2:
1880           return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
1881           break;
1882         case 3:
1883           return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
1884           break;
1885        }
1886       break;
1887
1888       /* In the trough */
1889     case MOUSE_TROUGH:
1890       {
1891         if (range->trough_click_forward)
1892           return GTK_SCROLL_PAGE_FORWARD;
1893         else
1894           return GTK_SCROLL_PAGE_BACKWARD;
1895       }
1896       break;
1897
1898     case MOUSE_OUTSIDE:
1899     case MOUSE_SLIDER:
1900     case MOUSE_WIDGET:
1901       break;
1902     }
1903
1904   return GTK_SCROLL_NONE;
1905 }
1906
1907 static gdouble
1908 coord_to_value (GtkRange *range,
1909                 gint      coord)
1910 {
1911   gdouble frac;
1912   gdouble value;
1913   gint    trough_length;
1914   gint    trough_start;
1915   gint    slider_length;
1916   gint    trough_border;
1917   gint    trough_under_steppers;
1918
1919   if (range->orientation == GTK_ORIENTATION_VERTICAL)
1920     {
1921       trough_length = range->layout->trough.height;
1922       trough_start  = range->layout->trough.y;
1923       slider_length = range->layout->slider.height;
1924     }
1925   else
1926     {
1927       trough_length = range->layout->trough.width;
1928       trough_start  = range->layout->trough.x;
1929       slider_length = range->layout->slider.width;
1930     }
1931
1932   gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
1933                        &trough_under_steppers, NULL, NULL);
1934
1935   if (! trough_under_steppers)
1936     {
1937       trough_start += trough_border;
1938       trough_length -= 2 * trough_border;
1939     }
1940
1941   if (trough_length == slider_length)
1942     frac = 1.0;
1943   else
1944     frac = (MAX (0, coord - trough_start) /
1945             (gdouble) (trough_length - slider_length));
1946
1947   if (should_invert (range))
1948     frac = 1.0 - frac;
1949
1950   value = range->adjustment->lower + frac * (range->adjustment->upper -
1951                                              range->adjustment->lower -
1952                                              range->adjustment->page_size);
1953
1954   return value;
1955 }
1956
1957 static gboolean
1958 gtk_range_key_press (GtkWidget   *widget,
1959                      GdkEventKey *event)
1960 {
1961   GtkRange *range = GTK_RANGE (widget);
1962
1963   if (event->keyval == GDK_Escape &&
1964       range->layout->grab_location != MOUSE_OUTSIDE)
1965     {
1966       stop_scrolling (range);
1967
1968       update_slider_position (range,
1969                               range->slide_initial_coordinate,
1970                               range->slide_initial_coordinate);
1971
1972       return TRUE;
1973     }
1974
1975   return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
1976 }
1977
1978 static gint
1979 gtk_range_button_press (GtkWidget      *widget,
1980                         GdkEventButton *event)
1981 {
1982   GtkRange *range = GTK_RANGE (widget);
1983   
1984   if (!GTK_WIDGET_HAS_FOCUS (widget))
1985     gtk_widget_grab_focus (widget);
1986
1987   /* ignore presses when we're already doing something else. */
1988   if (range->layout->grab_location != MOUSE_OUTSIDE)
1989     return FALSE;
1990
1991   range->layout->mouse_x = event->x;
1992   range->layout->mouse_y = event->y;
1993   if (gtk_range_update_mouse_location (range))
1994     gtk_widget_queue_draw (widget);
1995     
1996   if (range->layout->mouse_location == MOUSE_TROUGH  &&
1997       event->button == 1)
1998     {
1999       /* button 1 steps by page increment, as with button 2 on a stepper
2000        */
2001       GtkScrollType scroll;
2002       gdouble click_value;
2003       
2004       click_value = coord_to_value (range,
2005                                     range->orientation == GTK_ORIENTATION_VERTICAL ?
2006                                     event->y : event->x);
2007       
2008       range->trough_click_forward = click_value > range->adjustment->value;
2009       range_grab_add (range, MOUSE_TROUGH, event->button);
2010       
2011       scroll = range_get_scroll_for_grab (range);
2012       
2013       gtk_range_add_step_timer (range, scroll);
2014
2015       return TRUE;
2016     }
2017   else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
2018             range->layout->mouse_location == MOUSE_STEPPER_B ||
2019             range->layout->mouse_location == MOUSE_STEPPER_C ||
2020             range->layout->mouse_location == MOUSE_STEPPER_D) &&
2021            (event->button == 1 || event->button == 2 || event->button == 3))
2022     {
2023       GdkRectangle *stepper_area;
2024       GtkScrollType scroll;
2025       
2026       range_grab_add (range, range->layout->mouse_location, event->button);
2027
2028       stepper_area = get_area (range, range->layout->mouse_location);
2029       gtk_widget_queue_draw_area (widget,
2030                                   widget->allocation.x + stepper_area->x,
2031                                   widget->allocation.y + stepper_area->y,
2032                                   stepper_area->width,
2033                                   stepper_area->height);
2034
2035       scroll = range_get_scroll_for_grab (range);
2036       if (scroll != GTK_SCROLL_NONE)
2037         gtk_range_add_step_timer (range, scroll);
2038       
2039       return TRUE;
2040     }
2041   else if ((range->layout->mouse_location == MOUSE_TROUGH &&
2042             event->button == 2) ||
2043            range->layout->mouse_location == MOUSE_SLIDER)
2044     {
2045       gboolean need_value_update = FALSE;
2046       gboolean activate_slider;
2047
2048       /* Any button can be used to drag the slider, but you can start
2049        * dragging the slider with a trough click using button 2;
2050        * On button 2 press, we warp the slider to mouse position,
2051        * then begin the slider drag.
2052        */
2053       if (event->button == 2)
2054         {
2055           gdouble slider_low_value, slider_high_value, new_value;
2056           
2057           slider_high_value =
2058             coord_to_value (range,
2059                             range->orientation == GTK_ORIENTATION_VERTICAL ?
2060                             event->y : event->x);
2061           slider_low_value =
2062             coord_to_value (range,
2063                             range->orientation == GTK_ORIENTATION_VERTICAL ?
2064                             event->y - range->layout->slider.height :
2065                             event->x - range->layout->slider.width);
2066           
2067           /* compute new value for warped slider */
2068           new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
2069
2070           /* recalc slider, so we can set slide_initial_slider_position
2071            * properly
2072            */
2073           range->need_recalc = TRUE;
2074           gtk_range_calc_layout (range, new_value);
2075
2076           /* defer adjustment updates to update_slider_position() in order
2077            * to keep pixel quantisation
2078            */
2079           need_value_update = TRUE;
2080         }
2081       
2082       if (range->orientation == GTK_ORIENTATION_VERTICAL)
2083         {
2084           range->slide_initial_slider_position = range->layout->slider.y;
2085           range->slide_initial_coordinate = event->y;
2086         }
2087       else
2088         {
2089           range->slide_initial_slider_position = range->layout->slider.x;
2090           range->slide_initial_coordinate = event->x;
2091         }
2092
2093       range_grab_add (range, MOUSE_SLIDER, event->button);
2094
2095       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2096
2097       /* force a redraw, if the active slider is drawn differently to the
2098        * prelight one
2099        */
2100       if (activate_slider)
2101         gtk_widget_queue_draw (widget);
2102
2103       if (need_value_update)
2104         update_slider_position (range, event->x, event->y);
2105
2106       return TRUE;
2107     }
2108   
2109   return FALSE;
2110 }
2111
2112 /* During a slide, move the slider as required given new mouse position */
2113 static void
2114 update_slider_position (GtkRange *range,
2115                         gint      mouse_x,
2116                         gint      mouse_y)
2117 {
2118   gint delta;
2119   gint c;
2120   gdouble new_value;
2121   gboolean handled;
2122   gdouble next_value;
2123   gdouble mark_value;
2124   gdouble mark_delta;
2125   gint i;
2126
2127   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2128     delta = mouse_y - range->slide_initial_coordinate;
2129   else
2130     delta = mouse_x - range->slide_initial_coordinate;
2131
2132   c = range->slide_initial_slider_position + delta;
2133
2134   new_value = coord_to_value (range, c);
2135   next_value = coord_to_value (range, c + 1);
2136   mark_delta = fabs (next_value - new_value); 
2137
2138   for (i = 0; i < range->layout->n_marks; i++)
2139     {
2140       mark_value = range->layout->marks[i];
2141
2142       if (fabs (range->adjustment->value - mark_value) < 3 * mark_delta)
2143         {
2144           if (fabs (new_value - mark_value) < (range->slider_end - range->slider_start) * 0.5 * mark_delta)
2145             {
2146               new_value = mark_value;
2147               break;
2148             }
2149         }
2150     }  
2151
2152   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2153                  &handled);
2154 }
2155
2156 static void 
2157 stop_scrolling (GtkRange *range)
2158 {
2159   range_grab_remove (range);
2160   gtk_range_remove_step_timer (range);
2161   /* Flush any pending discontinuous/delayed updates */
2162   gtk_range_update_value (range);
2163 }
2164
2165 static gboolean
2166 gtk_range_grab_broken (GtkWidget          *widget,
2167                        GdkEventGrabBroken *event)
2168 {
2169   GtkRange *range = GTK_RANGE (widget);
2170
2171   if (range->layout->grab_location != MOUSE_OUTSIDE)
2172     {
2173       if (range->layout->grab_location == MOUSE_SLIDER)
2174         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2175       
2176       stop_scrolling (range);
2177       
2178       return TRUE;
2179     }
2180   
2181   return FALSE;
2182 }
2183
2184 static gint
2185 gtk_range_button_release (GtkWidget      *widget,
2186                           GdkEventButton *event)
2187 {
2188   GtkRange *range = GTK_RANGE (widget);
2189
2190   if (event->window == range->event_window)
2191     {
2192       range->layout->mouse_x = event->x;
2193       range->layout->mouse_y = event->y;
2194     }
2195   else
2196     {
2197       gdk_window_get_pointer (range->event_window,
2198                               &range->layout->mouse_x,
2199                               &range->layout->mouse_y,
2200                               NULL);
2201     }
2202   
2203   if (range->layout->grab_button == event->button)
2204     {
2205       if (range->layout->grab_location == MOUSE_SLIDER)
2206         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2207
2208       stop_scrolling (range);
2209       
2210       return TRUE;
2211     }
2212
2213   return FALSE;
2214 }
2215
2216 /**
2217  * _gtk_range_get_wheel_delta:
2218  * @range: a #GtkRange
2219  * @direction: A #GdkScrollDirection
2220  * 
2221  * Returns a good step value for the mouse wheel.
2222  * 
2223  * Return value: A good step value for the mouse wheel. 
2224  * 
2225  * Since: 2.4
2226  **/
2227 gdouble
2228 _gtk_range_get_wheel_delta (GtkRange           *range,
2229                             GdkScrollDirection  direction)
2230 {
2231   GtkAdjustment *adj = range->adjustment;
2232   gdouble delta;
2233
2234   if (GTK_IS_SCROLLBAR (range))
2235     delta = pow (adj->page_size, 2.0 / 3.0);
2236   else
2237     delta = adj->step_increment * 2;
2238   
2239   if (direction == GDK_SCROLL_UP ||
2240       direction == GDK_SCROLL_LEFT)
2241     delta = - delta;
2242   
2243   if (range->inverted)
2244     delta = - delta;
2245
2246   return delta;
2247 }
2248       
2249 static gboolean
2250 gtk_range_scroll_event (GtkWidget      *widget,
2251                         GdkEventScroll *event)
2252 {
2253   GtkRange *range = GTK_RANGE (widget);
2254
2255   if (GTK_WIDGET_REALIZED (range))
2256     {
2257       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2258       gdouble delta;
2259       gboolean handled;
2260
2261       delta = _gtk_range_get_wheel_delta (range, event->direction);
2262
2263       g_signal_emit (range, signals[CHANGE_VALUE], 0,
2264                      GTK_SCROLL_JUMP, adj->value + delta,
2265                      &handled);
2266       
2267       /* Policy DELAYED makes sense with scroll events,
2268        * but DISCONTINUOUS doesn't, so we update immediately
2269        * for DISCONTINUOUS
2270        */
2271       if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2272         gtk_range_update_value (range);
2273     }
2274
2275   return TRUE;
2276 }
2277
2278 static gboolean
2279 gtk_range_motion_notify (GtkWidget      *widget,
2280                          GdkEventMotion *event)
2281 {
2282   GtkRange *range;
2283   gint x, y;
2284
2285   range = GTK_RANGE (widget);
2286
2287   gdk_window_get_pointer (range->event_window, &x, &y, NULL);
2288   
2289   range->layout->mouse_x = x;
2290   range->layout->mouse_y = y;
2291
2292   if (gtk_range_update_mouse_location (range))
2293     gtk_widget_queue_draw (widget);
2294
2295   if (range->layout->grab_location == MOUSE_SLIDER)
2296     update_slider_position (range, x, y);
2297
2298   /* We handled the event if the mouse was in the range_rect */
2299   return range->layout->mouse_location != MOUSE_OUTSIDE;
2300 }
2301
2302 static gboolean
2303 gtk_range_enter_notify (GtkWidget        *widget,
2304                         GdkEventCrossing *event)
2305 {
2306   GtkRange *range = GTK_RANGE (widget);
2307
2308   range->layout->mouse_x = event->x;
2309   range->layout->mouse_y = event->y;
2310
2311   if (gtk_range_update_mouse_location (range))
2312     gtk_widget_queue_draw (widget);
2313   
2314   return TRUE;
2315 }
2316
2317 static gboolean
2318 gtk_range_leave_notify (GtkWidget        *widget,
2319                         GdkEventCrossing *event)
2320 {
2321   GtkRange *range = GTK_RANGE (widget);
2322
2323   range->layout->mouse_x = -1;
2324   range->layout->mouse_y = -1;
2325
2326   if (gtk_range_update_mouse_location (range))
2327     gtk_widget_queue_draw (widget);
2328   
2329   return TRUE;
2330 }
2331
2332 static void
2333 gtk_range_grab_notify (GtkWidget *widget,
2334                        gboolean   was_grabbed)
2335 {
2336   if (!was_grabbed)
2337     stop_scrolling (GTK_RANGE (widget));
2338 }
2339
2340 static void
2341 gtk_range_state_changed (GtkWidget    *widget,
2342                          GtkStateType  previous_state)
2343 {
2344   if (!GTK_WIDGET_IS_SENSITIVE (widget)) 
2345     stop_scrolling (GTK_RANGE (widget));
2346 }
2347
2348 #define check_rectangle(rectangle1, rectangle2)              \
2349   {                                                          \
2350     if (rectangle1.x != rectangle2.x) return TRUE;           \
2351     if (rectangle1.y != rectangle2.y) return TRUE;           \
2352     if (rectangle1.width  != rectangle2.width)  return TRUE; \
2353     if (rectangle1.height != rectangle2.height) return TRUE; \
2354   }
2355
2356 static gboolean
2357 layout_changed (GtkRangeLayout *layout1, 
2358                 GtkRangeLayout *layout2)
2359 {
2360   check_rectangle (layout1->slider, layout2->slider);
2361   check_rectangle (layout1->trough, layout2->trough);
2362   check_rectangle (layout1->stepper_a, layout2->stepper_a);
2363   check_rectangle (layout1->stepper_d, layout2->stepper_d);
2364   check_rectangle (layout1->stepper_b, layout2->stepper_b);
2365   check_rectangle (layout1->stepper_c, layout2->stepper_c);
2366
2367   if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2368   if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2369
2370   return FALSE;
2371 }
2372
2373 static void
2374 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2375                               gpointer       data)
2376 {
2377   GtkRange *range = GTK_RANGE (data);
2378   /* create a copy of the layout */
2379   GtkRangeLayout layout = *range->layout;
2380
2381   range->layout->recalc_marks = TRUE;
2382   range->need_recalc = TRUE;
2383   gtk_range_calc_layout (range, range->adjustment->value);
2384   
2385   /* now check whether the layout changed  */
2386   if (layout_changed (range->layout, &layout))
2387     gtk_widget_queue_draw (GTK_WIDGET (range));
2388
2389   /* Note that we don't round off to range->round_digits here.
2390    * that's because it's really broken to change a value
2391    * in response to a change signal on that value; round_digits
2392    * is therefore defined to be a filter on what the GtkRange
2393    * can input into the adjustment, not a filter that the GtkRange
2394    * will enforce on the adjustment.
2395    */
2396 }
2397
2398 static gboolean
2399 force_repaint (gpointer data)
2400 {
2401   GtkRange *range = GTK_RANGE (data);
2402   range->layout->repaint_id = 0;
2403   if (GTK_WIDGET_DRAWABLE (range))
2404     gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2405   return FALSE;
2406 }
2407
2408 static void
2409 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2410                                     gpointer       data)
2411 {
2412   GtkRange *range = GTK_RANGE (data);
2413   /* create a copy of the layout */
2414   GtkRangeLayout layout = *range->layout;
2415
2416   range->need_recalc = TRUE;
2417   gtk_range_calc_layout (range, range->adjustment->value);
2418   
2419   /* now check whether the layout changed  */
2420   if (layout_changed (range->layout, &layout))
2421     {
2422       gtk_widget_queue_draw (GTK_WIDGET (range));
2423       /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2424       if (!range->layout->repaint_id)
2425         range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
2426     }
2427   
2428   /* Note that we don't round off to range->round_digits here.
2429    * that's because it's really broken to change a value
2430    * in response to a change signal on that value; round_digits
2431    * is therefore defined to be a filter on what the GtkRange
2432    * can input into the adjustment, not a filter that the GtkRange
2433    * will enforce on the adjustment.
2434    */
2435
2436   g_signal_emit (range, signals[VALUE_CHANGED], 0);
2437 }
2438
2439 static void
2440 gtk_range_style_set (GtkWidget *widget,
2441                      GtkStyle  *previous_style)
2442 {
2443   GtkRange *range = GTK_RANGE (widget);
2444
2445   range->need_recalc = TRUE;
2446
2447   GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
2448 }
2449
2450 static void
2451 apply_marks (GtkRange *range, 
2452              gdouble   oldval,
2453              gdouble  *newval)
2454 {
2455   gint i;
2456   gdouble mark;
2457
2458   for (i = 0; i < range->layout->n_marks; i++)
2459     {
2460       mark = range->layout->marks[i];
2461       if ((oldval < mark && mark < *newval) ||
2462           (oldval > mark && mark > *newval))
2463         {
2464           *newval = mark;
2465           return;
2466         }
2467     }
2468 }
2469
2470 static void
2471 step_back (GtkRange *range)
2472 {
2473   gdouble newval;
2474   gboolean handled;
2475   
2476   newval = range->adjustment->value - range->adjustment->step_increment;
2477   apply_marks (range, range->adjustment->value, &newval);
2478   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2479                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2480 }
2481
2482 static void
2483 step_forward (GtkRange *range)
2484 {
2485   gdouble newval;
2486   gboolean handled;
2487
2488   newval = range->adjustment->value + range->adjustment->step_increment;
2489   apply_marks (range, range->adjustment->value, &newval);
2490   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2491                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
2492 }
2493
2494
2495 static void
2496 page_back (GtkRange *range)
2497 {
2498   gdouble newval;
2499   gboolean handled;
2500
2501   newval = range->adjustment->value - range->adjustment->page_increment;
2502   apply_marks (range, range->adjustment->value, &newval);
2503   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2504                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2505 }
2506
2507 static void
2508 page_forward (GtkRange *range)
2509 {
2510   gdouble newval;
2511   gboolean handled;
2512
2513   newval = range->adjustment->value + range->adjustment->page_increment;
2514   apply_marks (range, range->adjustment->value, &newval);
2515   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2516                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2517 }
2518
2519 static void
2520 scroll_begin (GtkRange *range)
2521 {
2522   gboolean handled;
2523   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2524                  GTK_SCROLL_START, range->adjustment->lower,
2525                  &handled);
2526 }
2527
2528 static void
2529 scroll_end (GtkRange *range)
2530 {
2531   gdouble newval;
2532   gboolean handled;
2533
2534   newval = range->adjustment->upper - range->adjustment->page_size;
2535   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2536                  &handled);
2537 }
2538
2539 static gboolean
2540 gtk_range_scroll (GtkRange     *range,
2541                   GtkScrollType scroll)
2542 {
2543   gdouble old_value = range->adjustment->value;
2544
2545   switch (scroll)
2546     {
2547     case GTK_SCROLL_STEP_LEFT:
2548       if (should_invert (range))
2549         step_forward (range);
2550       else
2551         step_back (range);
2552       break;
2553                     
2554     case GTK_SCROLL_STEP_UP:
2555       if (should_invert (range))
2556         step_forward (range);
2557       else
2558         step_back (range);
2559       break;
2560
2561     case GTK_SCROLL_STEP_RIGHT:
2562       if (should_invert (range))
2563         step_back (range);
2564       else
2565         step_forward (range);
2566       break;
2567                     
2568     case GTK_SCROLL_STEP_DOWN:
2569       if (should_invert (range))
2570         step_back (range);
2571       else
2572         step_forward (range);
2573       break;
2574                   
2575     case GTK_SCROLL_STEP_BACKWARD:
2576       step_back (range);
2577       break;
2578                   
2579     case GTK_SCROLL_STEP_FORWARD:
2580       step_forward (range);
2581       break;
2582
2583     case GTK_SCROLL_PAGE_LEFT:
2584       if (should_invert (range))
2585         page_forward (range);
2586       else
2587         page_back (range);
2588       break;
2589                     
2590     case GTK_SCROLL_PAGE_UP:
2591       if (should_invert (range))
2592         page_forward (range);
2593       else
2594         page_back (range);
2595       break;
2596
2597     case GTK_SCROLL_PAGE_RIGHT:
2598       if (should_invert (range))
2599         page_back (range);
2600       else
2601         page_forward (range);
2602       break;
2603                     
2604     case GTK_SCROLL_PAGE_DOWN:
2605       if (should_invert (range))
2606         page_back (range);
2607       else
2608         page_forward (range);
2609       break;
2610                   
2611     case GTK_SCROLL_PAGE_BACKWARD:
2612       page_back (range);
2613       break;
2614                   
2615     case GTK_SCROLL_PAGE_FORWARD:
2616       page_forward (range);
2617       break;
2618
2619     case GTK_SCROLL_START:
2620       scroll_begin (range);
2621       break;
2622
2623     case GTK_SCROLL_END:
2624       scroll_end (range);
2625       break;
2626
2627     case GTK_SCROLL_JUMP:
2628       /* Used by CList, range doesn't use it. */
2629       break;
2630
2631     case GTK_SCROLL_NONE:
2632       break;
2633     }
2634
2635   return range->adjustment->value != old_value;
2636 }
2637
2638 static void
2639 gtk_range_move_slider (GtkRange     *range,
2640                        GtkScrollType scroll)
2641 {
2642   gboolean cursor_only;
2643
2644   g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2645                 "gtk-keynav-cursor-only", &cursor_only,
2646                 NULL);
2647
2648   if (cursor_only)
2649     {
2650       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2651
2652       if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2653         {
2654           if (scroll == GTK_SCROLL_STEP_UP ||
2655               scroll == GTK_SCROLL_STEP_DOWN)
2656             {
2657               if (toplevel)
2658                 gtk_widget_child_focus (toplevel,
2659                                         scroll == GTK_SCROLL_STEP_UP ?
2660                                         GTK_DIR_UP : GTK_DIR_DOWN);
2661               return;
2662             }
2663         }
2664       else
2665         {
2666           if (scroll == GTK_SCROLL_STEP_LEFT ||
2667               scroll == GTK_SCROLL_STEP_RIGHT)
2668             {
2669               if (toplevel)
2670                 gtk_widget_child_focus (toplevel,
2671                                         scroll == GTK_SCROLL_STEP_LEFT ?
2672                                         GTK_DIR_LEFT : GTK_DIR_RIGHT);
2673               return;
2674             }
2675         }
2676     }
2677
2678   if (! gtk_range_scroll (range, scroll))
2679     gtk_widget_error_bell (GTK_WIDGET (range));
2680
2681   /* Policy DELAYED makes sense with key events,
2682    * but DISCONTINUOUS doesn't, so we update immediately
2683    * for DISCONTINUOUS
2684    */
2685   if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2686     gtk_range_update_value (range);
2687 }
2688
2689 static void
2690 gtk_range_get_props (GtkRange  *range,
2691                      gint      *slider_width,
2692                      gint      *stepper_size,
2693                      gint      *focus_width,
2694                      gint      *trough_border,
2695                      gint      *stepper_spacing,
2696                      gboolean  *trough_under_steppers,
2697                      gint      *arrow_displacement_x,
2698                      gint      *arrow_displacement_y)
2699 {
2700   GtkWidget *widget =  GTK_WIDGET (range);
2701   gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
2702   gint tmp_stepper_spacing, tmp_trough_under_steppers;
2703   gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2704   
2705   gtk_widget_style_get (widget,
2706                         "slider-width", &tmp_slider_width,
2707                         "trough-border", &tmp_trough_border,
2708                         "stepper-size", &tmp_stepper_size,
2709                         "stepper-spacing", &tmp_stepper_spacing,
2710                         "trough-under-steppers", &tmp_trough_under_steppers,
2711                         "arrow-displacement-x", &tmp_arrow_displacement_x,
2712                         "arrow-displacement-y", &tmp_arrow_displacement_y,
2713                         NULL);
2714
2715   if (tmp_stepper_spacing > 0)
2716     tmp_trough_under_steppers = FALSE;
2717
2718   if (GTK_WIDGET_CAN_FOCUS (range))
2719     {
2720       gint focus_line_width;
2721       gint focus_padding;
2722       
2723       gtk_widget_style_get (GTK_WIDGET (range),
2724                             "focus-line-width", &focus_line_width,
2725                             "focus-padding", &focus_padding,
2726                             NULL);
2727
2728       tmp_focus_width = focus_line_width + focus_padding;
2729     }
2730   else
2731     {
2732       tmp_focus_width = 0;
2733     }
2734   
2735   if (slider_width)
2736     *slider_width = tmp_slider_width;
2737
2738   if (focus_width)
2739     *focus_width = tmp_focus_width;
2740
2741   if (trough_border)
2742     *trough_border = tmp_trough_border;
2743
2744   if (stepper_size)
2745     *stepper_size = tmp_stepper_size;
2746
2747   if (stepper_spacing)
2748     *stepper_spacing = tmp_stepper_spacing;
2749
2750   if (trough_under_steppers)
2751     *trough_under_steppers = tmp_trough_under_steppers;
2752
2753   if (arrow_displacement_x)
2754     *arrow_displacement_x = tmp_arrow_displacement_x;
2755
2756   if (arrow_displacement_y)
2757     *arrow_displacement_y = tmp_arrow_displacement_y;
2758 }
2759
2760 #define POINT_IN_RECT(xcoord, ycoord, rect) \
2761  ((xcoord) >= (rect).x &&                   \
2762   (xcoord) <  ((rect).x + (rect).width) &&  \
2763   (ycoord) >= (rect).y &&                   \
2764   (ycoord) <  ((rect).y + (rect).height))
2765
2766 /* Update mouse location, return TRUE if it changes */
2767 static gboolean
2768 gtk_range_update_mouse_location (GtkRange *range)
2769 {
2770   gint x, y;
2771   MouseLocation old;
2772   GtkWidget *widget;
2773
2774   widget = GTK_WIDGET (range);
2775   
2776   old = range->layout->mouse_location;
2777   
2778   x = range->layout->mouse_x;
2779   y = range->layout->mouse_y;
2780
2781   if (range->layout->grab_location != MOUSE_OUTSIDE)
2782     range->layout->mouse_location = range->layout->grab_location;
2783   else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
2784     range->layout->mouse_location = MOUSE_STEPPER_A;
2785   else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
2786     range->layout->mouse_location = MOUSE_STEPPER_B;
2787   else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
2788     range->layout->mouse_location = MOUSE_STEPPER_C;
2789   else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
2790     range->layout->mouse_location = MOUSE_STEPPER_D;
2791   else if (POINT_IN_RECT (x, y, range->layout->slider))
2792     range->layout->mouse_location = MOUSE_SLIDER;
2793   else if (POINT_IN_RECT (x, y, range->layout->trough))
2794     range->layout->mouse_location = MOUSE_TROUGH;
2795   else if (POINT_IN_RECT (x, y, widget->allocation))
2796     range->layout->mouse_location = MOUSE_WIDGET;
2797   else
2798     range->layout->mouse_location = MOUSE_OUTSIDE;
2799
2800   return old != range->layout->mouse_location;
2801 }
2802
2803 /* Clamp rect, border inside widget->allocation, such that we prefer
2804  * to take space from border not rect in all directions, and prefer to
2805  * give space to border over rect in one direction.
2806  */
2807 static void
2808 clamp_dimensions (GtkWidget    *widget,
2809                   GdkRectangle *rect,
2810                   GtkBorder    *border,
2811                   gboolean      border_expands_horizontally)
2812 {
2813   gint extra, shortage;
2814   
2815   g_return_if_fail (rect->x == 0);
2816   g_return_if_fail (rect->y == 0);  
2817   g_return_if_fail (rect->width >= 0);
2818   g_return_if_fail (rect->height >= 0);
2819
2820   /* Width */
2821   
2822   extra = widget->allocation.width - border->left - border->right - rect->width;
2823   if (extra > 0)
2824     {
2825       if (border_expands_horizontally)
2826         {
2827           border->left += extra / 2;
2828           border->right += extra / 2 + extra % 2;
2829         }
2830       else
2831         {
2832           rect->width += extra;
2833         }
2834     }
2835   
2836   /* See if we can fit rect, if not kill the border */
2837   shortage = rect->width - widget->allocation.width;
2838   if (shortage > 0)
2839     {
2840       rect->width = widget->allocation.width;
2841       /* lose the border */
2842       border->left = 0;
2843       border->right = 0;
2844     }
2845   else
2846     {
2847       /* See if we can fit rect with borders */
2848       shortage = rect->width + border->left + border->right -
2849         widget->allocation.width;
2850       if (shortage > 0)
2851         {
2852           /* Shrink borders */
2853           border->left -= shortage / 2;
2854           border->right -= shortage / 2 + shortage % 2;
2855         }
2856     }
2857
2858   /* Height */
2859   
2860   extra = widget->allocation.height - border->top - border->bottom - rect->height;
2861   if (extra > 0)
2862     {
2863       if (border_expands_horizontally)
2864         {
2865           /* don't expand border vertically */
2866           rect->height += extra;
2867         }
2868       else
2869         {
2870           border->top += extra / 2;
2871           border->bottom += extra / 2 + extra % 2;
2872         }
2873     }
2874   
2875   /* See if we can fit rect, if not kill the border */
2876   shortage = rect->height - widget->allocation.height;
2877   if (shortage > 0)
2878     {
2879       rect->height = widget->allocation.height;
2880       /* lose the border */
2881       border->top = 0;
2882       border->bottom = 0;
2883     }
2884   else
2885     {
2886       /* See if we can fit rect with borders */
2887       shortage = rect->height + border->top + border->bottom -
2888         widget->allocation.height;
2889       if (shortage > 0)
2890         {
2891           /* Shrink borders */
2892           border->top -= shortage / 2;
2893           border->bottom -= shortage / 2 + shortage % 2;
2894         }
2895     }
2896 }
2897
2898 static void
2899 gtk_range_calc_request (GtkRange      *range,
2900                         gint           slider_width,
2901                         gint           stepper_size,
2902                         gint           focus_width,
2903                         gint           trough_border,
2904                         gint           stepper_spacing,
2905                         GdkRectangle  *range_rect,
2906                         GtkBorder     *border,
2907                         gint          *n_steppers_p,
2908                         gboolean      *has_steppers_ab,
2909                         gboolean      *has_steppers_cd,
2910                         gint          *slider_length_p)
2911 {
2912   gint slider_length;
2913   gint n_steppers;
2914   gint n_steppers_ab;
2915   gint n_steppers_cd;
2916
2917   border->left = 0;
2918   border->right = 0;
2919   border->top = 0;
2920   border->bottom = 0;
2921
2922   if (GTK_RANGE_GET_CLASS (range)->get_range_border)
2923     GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
2924
2925   n_steppers_ab = 0;
2926   n_steppers_cd = 0;
2927
2928   if (range->has_stepper_a)
2929     n_steppers_ab += 1;
2930   if (range->has_stepper_b)
2931     n_steppers_ab += 1;
2932   if (range->has_stepper_c)
2933     n_steppers_cd += 1;
2934   if (range->has_stepper_d)
2935     n_steppers_cd += 1;
2936
2937   n_steppers = n_steppers_ab + n_steppers_cd;
2938
2939   slider_length = range->min_slider_size;
2940
2941   range_rect->x = 0;
2942   range_rect->y = 0;
2943   
2944   /* We never expand to fill available space in the small dimension
2945    * (i.e. vertical scrollbars are always a fixed width)
2946    */
2947   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2948     {
2949       range_rect->width = (focus_width + trough_border) * 2 + slider_width;
2950       range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2951
2952       if (n_steppers_ab > 0)
2953         range_rect->height += stepper_spacing;
2954
2955       if (n_steppers_cd > 0)
2956         range_rect->height += stepper_spacing;
2957     }
2958   else
2959     {
2960       range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2961       range_rect->height = (focus_width + trough_border) * 2 + slider_width;
2962
2963       if (n_steppers_ab > 0)
2964         range_rect->width += stepper_spacing;
2965
2966       if (n_steppers_cd > 0)
2967         range_rect->width += stepper_spacing;
2968     }
2969
2970   if (n_steppers_p)
2971     *n_steppers_p = n_steppers;
2972
2973   if (has_steppers_ab)
2974     *has_steppers_ab = (n_steppers_ab > 0);
2975
2976   if (has_steppers_cd)
2977     *has_steppers_cd = (n_steppers_cd > 0);
2978
2979   if (slider_length_p)
2980     *slider_length_p = slider_length;
2981 }
2982
2983 static void
2984 gtk_range_calc_layout (GtkRange *range,
2985                        gdouble   adjustment_value)
2986 {
2987   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
2988   gint slider_length;
2989   GtkBorder border;
2990   gint n_steppers;
2991   gboolean has_steppers_ab;
2992   gboolean has_steppers_cd;
2993   gboolean trough_under_steppers;
2994   GdkRectangle range_rect;
2995   GtkRangeLayout *layout;
2996   GtkWidget *widget;
2997   
2998   if (!range->need_recalc)
2999     return;
3000
3001   /* If we have a too-small allocation, we prefer the steppers over
3002    * the trough/slider, probably the steppers are a more useful
3003    * feature in small spaces.
3004    *
3005    * Also, we prefer to draw the range itself rather than the border
3006    * areas if there's a conflict, since the borders will be decoration
3007    * not controls. Though this depends on subclasses cooperating by
3008    * not drawing on range->range_rect.
3009    */
3010
3011   widget = GTK_WIDGET (range);
3012   layout = range->layout;
3013   
3014   gtk_range_get_props (range,
3015                        &slider_width, &stepper_size,
3016                        &focus_width, &trough_border,
3017                        &stepper_spacing, &trough_under_steppers,
3018                        NULL, NULL);
3019
3020   gtk_range_calc_request (range, 
3021                           slider_width, stepper_size,
3022                           focus_width, trough_border, stepper_spacing,
3023                           &range_rect, &border, &n_steppers,
3024                           &has_steppers_ab, &has_steppers_cd, &slider_length);
3025   
3026   /* We never expand to fill available space in the small dimension
3027    * (i.e. vertical scrollbars are always a fixed width)
3028    */
3029   if (range->orientation == GTK_ORIENTATION_VERTICAL)
3030     {
3031       clamp_dimensions (widget, &range_rect, &border, TRUE);
3032     }
3033   else
3034     {
3035       clamp_dimensions (widget, &range_rect, &border, FALSE);
3036     }
3037   
3038   range_rect.x = border.left;
3039   range_rect.y = border.top;
3040   
3041   range->range_rect = range_rect;
3042   
3043   if (range->orientation == GTK_ORIENTATION_VERTICAL)
3044     {
3045       gint stepper_width, stepper_height;
3046
3047       /* Steppers are the width of the range, and stepper_size in
3048        * height, or if we don't have enough height, divided equally
3049        * among available space.
3050        */
3051       stepper_width = range_rect.width - focus_width * 2;
3052
3053       if (trough_under_steppers)
3054         stepper_width -= trough_border * 2;
3055
3056       if (stepper_width < 1)
3057         stepper_width = range_rect.width; /* screw the trough border */
3058
3059       if (n_steppers == 0)
3060         stepper_height = 0; /* avoid divide by n_steppers */
3061       else
3062         stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
3063
3064       /* Stepper A */
3065       
3066       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3067       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3068
3069       if (range->has_stepper_a)
3070         {
3071           layout->stepper_a.width = stepper_width;
3072           layout->stepper_a.height = stepper_height;
3073         }
3074       else
3075         {
3076           layout->stepper_a.width = 0;
3077           layout->stepper_a.height = 0;
3078         }
3079
3080       /* Stepper B */
3081       
3082       layout->stepper_b.x = layout->stepper_a.x;
3083       layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
3084
3085       if (range->has_stepper_b)
3086         {
3087           layout->stepper_b.width = stepper_width;
3088           layout->stepper_b.height = stepper_height;
3089         }
3090       else
3091         {
3092           layout->stepper_b.width = 0;
3093           layout->stepper_b.height = 0;
3094         }
3095
3096       /* Stepper D */
3097
3098       if (range->has_stepper_d)
3099         {
3100           layout->stepper_d.width = stepper_width;
3101           layout->stepper_d.height = stepper_height;
3102         }
3103       else
3104         {
3105           layout->stepper_d.width = 0;
3106           layout->stepper_d.height = 0;
3107         }
3108       
3109       layout->stepper_d.x = layout->stepper_a.x;
3110       layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
3111
3112       /* Stepper C */
3113
3114       if (range->has_stepper_c)
3115         {
3116           layout->stepper_c.width = stepper_width;
3117           layout->stepper_c.height = stepper_height;
3118         }
3119       else
3120         {
3121           layout->stepper_c.width = 0;
3122           layout->stepper_c.height = 0;
3123         }
3124       
3125       layout->stepper_c.x = layout->stepper_a.x;
3126       layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
3127
3128       /* Now the trough is the remaining space between steppers B and C,
3129        * if any, minus spacing
3130        */
3131       layout->trough.x = range_rect.x;
3132       layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
3133       layout->trough.width = range_rect.width;
3134       layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
3135
3136       /* Slider fits into the trough, with stepper_spacing on either side,
3137        * and the size/position based on the adjustment or fixed, depending.
3138        */
3139       layout->slider.x = layout->trough.x + focus_width + trough_border;
3140       layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
3141
3142       /* Compute slider position/length */
3143       {
3144         gint y, bottom, top, height;
3145         
3146         top = layout->trough.y;
3147         bottom = layout->trough.y + layout->trough.height;
3148
3149         if (! trough_under_steppers)
3150           {
3151             top += trough_border;
3152             bottom -= trough_border;
3153           }
3154
3155         /* slider height is the fraction (page_size /
3156          * total_adjustment_range) times the trough height in pixels
3157          */
3158
3159         if (range->adjustment->upper - range->adjustment->lower != 0)
3160           height = ((bottom - top) * (range->adjustment->page_size /
3161                                        (range->adjustment->upper - range->adjustment->lower)));
3162         else
3163           height = range->min_slider_size;
3164         
3165         if (height < range->min_slider_size ||
3166             range->slider_size_fixed)
3167           height = range->min_slider_size;
3168
3169         height = MIN (height, layout->trough.height);
3170         
3171         y = top;
3172         
3173         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3174           y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
3175                                           (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3176         
3177         y = CLAMP (y, top, bottom);
3178         
3179         if (should_invert (range))
3180           y = bottom - (y - top + height);
3181         
3182         layout->slider.y = y;
3183         layout->slider.height = height;
3184
3185         /* These are publically exported */
3186         range->slider_start = layout->slider.y;
3187         range->slider_end = layout->slider.y + layout->slider.height;
3188       }
3189     }
3190   else
3191     {
3192       gint stepper_width, stepper_height;
3193
3194       /* Steppers are the height of the range, and stepper_size in
3195        * width, or if we don't have enough width, divided equally
3196        * among available space.
3197        */
3198       stepper_height = range_rect.height + focus_width * 2;
3199
3200       if (trough_under_steppers)
3201         stepper_height -= trough_border * 2;
3202
3203       if (stepper_height < 1)
3204         stepper_height = range_rect.height; /* screw the trough border */
3205
3206       if (n_steppers == 0)
3207         stepper_width = 0; /* avoid divide by n_steppers */
3208       else
3209         stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3210
3211       /* Stepper A */
3212       
3213       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3214       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3215
3216       if (range->has_stepper_a)
3217         {
3218           layout->stepper_a.width = stepper_width;
3219           layout->stepper_a.height = stepper_height;
3220         }
3221       else
3222         {
3223           layout->stepper_a.width = 0;
3224           layout->stepper_a.height = 0;
3225         }
3226
3227       /* Stepper B */
3228       
3229       layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3230       layout->stepper_b.y = layout->stepper_a.y;
3231
3232       if (range->has_stepper_b)
3233         {
3234           layout->stepper_b.width = stepper_width;
3235           layout->stepper_b.height = stepper_height;
3236         }
3237       else
3238         {
3239           layout->stepper_b.width = 0;
3240           layout->stepper_b.height = 0;
3241         }
3242
3243       /* Stepper D */
3244
3245       if (range->has_stepper_d)
3246         {
3247           layout->stepper_d.width = stepper_width;
3248           layout->stepper_d.height = stepper_height;
3249         }
3250       else
3251         {
3252           layout->stepper_d.width = 0;
3253           layout->stepper_d.height = 0;
3254         }
3255
3256       layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3257       layout->stepper_d.y = layout->stepper_a.y;
3258
3259
3260       /* Stepper C */
3261
3262       if (range->has_stepper_c)
3263         {
3264           layout->stepper_c.width = stepper_width;
3265           layout->stepper_c.height = stepper_height;
3266         }
3267       else
3268         {
3269           layout->stepper_c.width = 0;
3270           layout->stepper_c.height = 0;
3271         }
3272       
3273       layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3274       layout->stepper_c.y = layout->stepper_a.y;
3275
3276       /* Now the trough is the remaining space between steppers B and C,
3277        * if any
3278        */
3279       layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3280       layout->trough.y = range_rect.y;
3281
3282       layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3283       layout->trough.height = range_rect.height;
3284
3285       /* Slider fits into the trough, with stepper_spacing on either side,
3286        * and the size/position based on the adjustment or fixed, depending.
3287        */
3288       layout->slider.y = layout->trough.y + focus_width + trough_border;
3289       layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3290
3291       /* Compute slider position/length */
3292       {
3293         gint x, left, right, width;
3294         
3295         left = layout->trough.x;
3296         right = layout->trough.x + layout->trough.width;
3297
3298         if (! trough_under_steppers)
3299           {
3300             left += trough_border;
3301             right -= trough_border;
3302           }
3303
3304         /* slider width is the fraction (page_size /
3305          * total_adjustment_range) times the trough width in pixels
3306          */
3307         
3308         if (range->adjustment->upper - range->adjustment->lower != 0)
3309           width = ((right - left) * (range->adjustment->page_size /
3310                                    (range->adjustment->upper - range->adjustment->lower)));
3311         else
3312           width = range->min_slider_size;
3313         
3314         if (width < range->min_slider_size ||
3315             range->slider_size_fixed)
3316           width = range->min_slider_size;
3317         
3318         width = MIN (width, layout->trough.width);
3319         
3320         x = left;
3321         
3322         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3323           x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3324                                          (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3325         
3326         x = CLAMP (x, left, right);
3327         
3328         if (should_invert (range))
3329           x = right - (x - left + width);
3330         
3331         layout->slider.x = x;
3332         layout->slider.width = width;
3333
3334         /* These are publically exported */
3335         range->slider_start = layout->slider.x;
3336         range->slider_end = layout->slider.x + layout->slider.width;
3337       }
3338     }
3339   
3340   gtk_range_update_mouse_location (range);
3341
3342   switch (range->layout->upper_sensitivity)
3343     {
3344     case GTK_SENSITIVITY_AUTO:
3345       range->layout->upper_sensitive =
3346         (range->adjustment->value <
3347          (range->adjustment->upper - range->adjustment->page_size));
3348       break;
3349
3350     case GTK_SENSITIVITY_ON:
3351       range->layout->upper_sensitive = TRUE;
3352       break;
3353
3354     case GTK_SENSITIVITY_OFF:
3355       range->layout->upper_sensitive = FALSE;
3356       break;
3357     }
3358
3359   switch (range->layout->lower_sensitivity)
3360     {
3361     case GTK_SENSITIVITY_AUTO:
3362       range->layout->lower_sensitive =
3363         (range->adjustment->value > range->adjustment->lower);
3364       break;
3365
3366     case GTK_SENSITIVITY_ON:
3367       range->layout->lower_sensitive = TRUE;
3368       break;
3369
3370     case GTK_SENSITIVITY_OFF:
3371       range->layout->lower_sensitive = FALSE;
3372       break;
3373     }
3374 }
3375
3376 static GdkRectangle*
3377 get_area (GtkRange     *range,
3378           MouseLocation location)
3379 {
3380   switch (location)
3381     {
3382     case MOUSE_STEPPER_A:
3383       return &range->layout->stepper_a;
3384     case MOUSE_STEPPER_B:
3385       return &range->layout->stepper_b;
3386     case MOUSE_STEPPER_C:
3387       return &range->layout->stepper_c;
3388     case MOUSE_STEPPER_D:
3389       return &range->layout->stepper_d;
3390     case MOUSE_TROUGH:
3391       return &range->layout->trough;
3392     case MOUSE_SLIDER:
3393       return &range->layout->slider;
3394     case MOUSE_WIDGET:
3395     case MOUSE_OUTSIDE:
3396       break;
3397     }
3398
3399   g_warning (G_STRLOC": bug");
3400   return NULL;
3401 }
3402
3403 static void
3404 gtk_range_calc_marks (GtkRange *range)
3405 {
3406   gint i;
3407   
3408   if (!range->layout->recalc_marks)
3409     return;
3410
3411   range->layout->recalc_marks = FALSE;
3412
3413   for (i = 0; i < range->layout->n_marks; i++)
3414     {
3415       range->need_recalc = TRUE;
3416       gtk_range_calc_layout (range, range->layout->marks[i]);
3417       if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
3418         range->layout->mark_pos[i] = range->layout->slider.x + range->layout->slider.width / 2;
3419       else
3420         range->layout->mark_pos[i] = range->layout->slider.y + range->layout->slider.height / 2;
3421     }
3422
3423   range->need_recalc = TRUE;
3424 }
3425
3426 static gboolean
3427 gtk_range_real_change_value (GtkRange     *range,
3428                              GtkScrollType scroll,
3429                              gdouble       value)
3430 {
3431   /* potentially adjust the bounds _before we clamp */
3432   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3433
3434   if (range->layout->restrict_to_fill_level)
3435     value = MIN (value, MAX (range->adjustment->lower,
3436                              range->layout->fill_level));
3437
3438   value = CLAMP (value, range->adjustment->lower,
3439                  (range->adjustment->upper - range->adjustment->page_size));
3440
3441   if (range->round_digits >= 0)
3442     {
3443       gdouble power;
3444       gint i;
3445
3446       i = range->round_digits;
3447       power = 1;
3448       while (i--)
3449         power *= 10;
3450       
3451       value = floor ((value * power) + 0.5) / power;
3452     }
3453   
3454   if (range->adjustment->value != value)
3455     {
3456       range->need_recalc = TRUE;
3457
3458       gtk_widget_queue_draw (GTK_WIDGET (range));
3459       
3460       switch (range->update_policy)
3461         {
3462         case GTK_UPDATE_CONTINUOUS:
3463           gtk_adjustment_set_value (range->adjustment, value);
3464           break;
3465
3466           /* Delayed means we update after a period of inactivity */
3467         case GTK_UPDATE_DELAYED:
3468           gtk_range_reset_update_timer (range);
3469           /* FALL THRU */
3470
3471           /* Discontinuous means we update on button release */
3472         case GTK_UPDATE_DISCONTINUOUS:
3473           /* don't emit value_changed signal */
3474           range->adjustment->value = value;
3475           range->update_pending = TRUE;
3476           break;
3477         }
3478     }
3479   return FALSE;
3480 }
3481
3482 static void
3483 gtk_range_update_value (GtkRange *range)
3484 {
3485   gtk_range_remove_update_timer (range);
3486   
3487   if (range->update_pending)
3488     {
3489       gtk_adjustment_value_changed (range->adjustment);
3490
3491       range->update_pending = FALSE;
3492     }
3493 }
3494
3495 struct _GtkRangeStepTimer
3496 {
3497   guint timeout_id;
3498   GtkScrollType step;
3499 };
3500
3501 static gboolean
3502 second_timeout (gpointer data)
3503 {
3504   GtkRange *range;
3505
3506   range = GTK_RANGE (data);
3507   gtk_range_scroll (range, range->timer->step);
3508   
3509   return TRUE;
3510 }
3511
3512 static gboolean
3513 initial_timeout (gpointer data)
3514 {
3515   GtkRange    *range;
3516   GtkSettings *settings;
3517   guint        timeout;
3518
3519   settings = gtk_widget_get_settings (GTK_WIDGET (data));
3520   g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3521
3522   range = GTK_RANGE (data);
3523   range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3524                                             second_timeout,
3525                                             range);
3526   /* remove self */
3527   return FALSE;
3528 }
3529
3530 static void
3531 gtk_range_add_step_timer (GtkRange      *range,
3532                           GtkScrollType  step)
3533 {
3534   GtkSettings *settings;
3535   guint        timeout;
3536
3537   g_return_if_fail (range->timer == NULL);
3538   g_return_if_fail (step != GTK_SCROLL_NONE);
3539
3540   settings = gtk_widget_get_settings (GTK_WIDGET (range));
3541   g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3542
3543   range->timer = g_new (GtkRangeStepTimer, 1);
3544
3545   range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3546                                             initial_timeout,
3547                                             range);
3548   range->timer->step = step;
3549
3550   gtk_range_scroll (range, range->timer->step);
3551 }
3552
3553 static void
3554 gtk_range_remove_step_timer (GtkRange *range)
3555 {
3556   if (range->timer)
3557     {
3558       if (range->timer->timeout_id != 0)
3559         g_source_remove (range->timer->timeout_id);
3560
3561       g_free (range->timer);
3562
3563       range->timer = NULL;
3564     }
3565 }
3566
3567 static gboolean
3568 update_timeout (gpointer data)
3569 {
3570   GtkRange *range;
3571
3572   range = GTK_RANGE (data);
3573   gtk_range_update_value (range);
3574   range->update_timeout_id = 0;
3575
3576   /* self-remove */
3577   return FALSE;
3578 }
3579
3580 static void
3581 gtk_range_reset_update_timer (GtkRange *range)
3582 {
3583   gtk_range_remove_update_timer (range);
3584
3585   range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3586                                             update_timeout,
3587                                             range);
3588 }
3589
3590 static void
3591 gtk_range_remove_update_timer (GtkRange *range)
3592 {
3593   if (range->update_timeout_id != 0)
3594     {
3595       g_source_remove (range->update_timeout_id);
3596       range->update_timeout_id = 0;
3597     }
3598 }
3599
3600 void
3601 _gtk_range_set_stop_values (GtkRange *range,
3602                             gdouble  *values,
3603                             gint      n_values)
3604 {
3605   gint i;
3606
3607   g_free (range->layout->marks);
3608   range->layout->marks = g_new (gdouble, n_values);
3609
3610   g_free (range->layout->mark_pos);
3611   range->layout->mark_pos = g_new (gint, n_values);
3612
3613   range->layout->n_marks = n_values;
3614
3615   for (i = 0; i < n_values; i++) 
3616     range->layout->marks[i] = values[i];
3617
3618   range->layout->recalc_marks = TRUE;
3619 }
3620
3621 gint
3622 _gtk_range_get_stop_positions (GtkRange  *range,
3623                                gint     **values)
3624 {
3625   gtk_range_calc_marks (range);
3626
3627   if (values)
3628     *values = g_memdup (range->layout->mark_pos, range->layout->n_marks * sizeof (gint));
3629
3630   return range->layout->n_marks;
3631 }
3632
3633 #define __GTK_RANGE_C__
3634 #include "gtkaliasdef.c"