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