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