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