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