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