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