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