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