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