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