]> Pileus Git - ~andy/gtk/blob - gtk/gtkrange.c
Merge branch 'master' into toolpalette
[~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_lower_stepper_sensitivity:
894  * @range:       a #GtkRange
895  * @sensitivity: the lower stepper's sensitivity policy.
896  *
897  * Sets the sensitivity policy for the stepper that points to the
898  * 'lower' end of the GtkRange's adjustment.
899  *
900  * Since: 2.10
901  **/
902 void
903 gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
904                                          GtkSensitivityType  sensitivity)
905 {
906   g_return_if_fail (GTK_IS_RANGE (range));
907
908   if (range->layout->lower_sensitivity != sensitivity)
909     {
910       range->layout->lower_sensitivity = sensitivity;
911
912       range->need_recalc = TRUE;
913       gtk_range_calc_layout (range, range->adjustment->value);
914       gtk_widget_queue_draw (GTK_WIDGET (range));
915
916       g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
917     }
918 }
919
920 /**
921  * gtk_range_get_lower_stepper_sensitivity:
922  * @range: a #GtkRange
923  *
924  * Gets the sensitivity policy for the stepper that points to the
925  * 'lower' end of the GtkRange's adjustment.
926  *
927  * Return value: The lower stepper's sensitivity policy.
928  *
929  * Since: 2.10
930  **/
931 GtkSensitivityType
932 gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
933 {
934   g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
935
936   return range->layout->lower_sensitivity;
937 }
938
939 /**
940  * gtk_range_set_upper_stepper_sensitivity:
941  * @range:       a #GtkRange
942  * @sensitivity: the upper stepper's sensitivity policy.
943  *
944  * Sets the sensitivity policy for the stepper that points to the
945  * 'upper' end of the GtkRange's adjustment.
946  *
947  * Since: 2.10
948  **/
949 void
950 gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
951                                          GtkSensitivityType  sensitivity)
952 {
953   g_return_if_fail (GTK_IS_RANGE (range));
954
955   if (range->layout->upper_sensitivity != sensitivity)
956     {
957       range->layout->upper_sensitivity = sensitivity;
958
959       range->need_recalc = TRUE;
960       gtk_range_calc_layout (range, range->adjustment->value);
961       gtk_widget_queue_draw (GTK_WIDGET (range));
962
963       g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
964     }
965 }
966
967 /**
968  * gtk_range_get_upper_stepper_sensitivity:
969  * @range: a #GtkRange
970  *
971  * Gets the sensitivity policy for the stepper that points to the
972  * 'upper' end of the GtkRange's adjustment.
973  *
974  * Return value: The upper stepper's sensitivity policy.
975  *
976  * Since: 2.10
977  **/
978 GtkSensitivityType
979 gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
980 {
981   g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
982
983   return range->layout->upper_sensitivity;
984 }
985
986 /**
987  * gtk_range_set_increments:
988  * @range: a #GtkRange
989  * @step: step size
990  * @page: page size
991  *
992  * Sets the step and page sizes for the range.
993  * The step size is used when the user clicks the #GtkScrollbar
994  * arrows or moves #GtkScale via arrow keys. The page size
995  * is used for example when moving via Page Up or Page Down keys.
996  **/
997 void
998 gtk_range_set_increments (GtkRange *range,
999                           gdouble   step,
1000                           gdouble   page)
1001 {
1002   g_return_if_fail (GTK_IS_RANGE (range));
1003
1004   range->adjustment->step_increment = step;
1005   range->adjustment->page_increment = page;
1006
1007   gtk_adjustment_changed (range->adjustment);
1008 }
1009
1010 /**
1011  * gtk_range_set_range:
1012  * @range: a #GtkRange
1013  * @min: minimum range value
1014  * @max: maximum range value
1015  * 
1016  * Sets the allowable values in the #GtkRange, and clamps the range
1017  * value to be between @min and @max. (If the range has a non-zero
1018  * page size, it is clamped between @min and @max - page-size.)
1019  **/
1020 void
1021 gtk_range_set_range (GtkRange *range,
1022                      gdouble   min,
1023                      gdouble   max)
1024 {
1025   gdouble value;
1026   
1027   g_return_if_fail (GTK_IS_RANGE (range));
1028   g_return_if_fail (min < max);
1029   
1030   range->adjustment->lower = min;
1031   range->adjustment->upper = max;
1032
1033   value = range->adjustment->value;
1034
1035   if (range->layout->restrict_to_fill_level)
1036     value = MIN (value, MAX (range->adjustment->lower,
1037                              range->layout->fill_level));
1038
1039   value = CLAMP (value, range->adjustment->lower,
1040                  (range->adjustment->upper - range->adjustment->page_size));
1041
1042   gtk_adjustment_set_value (range->adjustment, value);
1043   gtk_adjustment_changed (range->adjustment);
1044 }
1045
1046 /**
1047  * gtk_range_set_value:
1048  * @range: a #GtkRange
1049  * @value: new value of the range
1050  *
1051  * Sets the current value of the range; if the value is outside the
1052  * minimum or maximum range values, it will be clamped to fit inside
1053  * them. The range emits the #GtkRange::value-changed signal if the 
1054  * value changes.
1055  **/
1056 void
1057 gtk_range_set_value (GtkRange *range,
1058                      gdouble   value)
1059 {
1060   g_return_if_fail (GTK_IS_RANGE (range));
1061
1062   if (range->layout->restrict_to_fill_level)
1063     value = MIN (value, MAX (range->adjustment->lower,
1064                              range->layout->fill_level));
1065
1066   value = CLAMP (value, range->adjustment->lower,
1067                  (range->adjustment->upper - range->adjustment->page_size));
1068
1069   gtk_adjustment_set_value (range->adjustment, value);
1070 }
1071
1072 /**
1073  * gtk_range_get_value:
1074  * @range: a #GtkRange
1075  * 
1076  * Gets the current value of the range.
1077  * 
1078  * Return value: current value of the range.
1079  **/
1080 gdouble
1081 gtk_range_get_value (GtkRange *range)
1082 {
1083   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1084
1085   return range->adjustment->value;
1086 }
1087
1088 /**
1089  * gtk_range_set_show_fill_level:
1090  * @range:           A #GtkRange
1091  * @show_fill_level: Whether a fill level indicator graphics is shown.
1092  *
1093  * Sets whether a graphical fill level is show on the trough. See
1094  * gtk_range_set_fill_level() for a general description of the fill
1095  * level concept.
1096  *
1097  * Since: 2.12
1098  **/
1099 void
1100 gtk_range_set_show_fill_level (GtkRange *range,
1101                                gboolean  show_fill_level)
1102 {
1103   g_return_if_fail (GTK_IS_RANGE (range));
1104
1105   show_fill_level = show_fill_level ? TRUE : FALSE;
1106
1107   if (show_fill_level != range->layout->show_fill_level)
1108     {
1109       range->layout->show_fill_level = show_fill_level;
1110       g_object_notify (G_OBJECT (range), "show-fill-level");
1111       gtk_widget_queue_draw (GTK_WIDGET (range));
1112     }
1113 }
1114
1115 /**
1116  * gtk_range_get_show_fill_level:
1117  * @range: A #GtkRange
1118  *
1119  * Gets whether the range displays the fill level graphically.
1120  *
1121  * Return value: %TRUE if @range shows the fill level.
1122  *
1123  * Since: 2.12
1124  **/
1125 gboolean
1126 gtk_range_get_show_fill_level (GtkRange *range)
1127 {
1128   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1129
1130   return range->layout->show_fill_level;
1131 }
1132
1133 /**
1134  * gtk_range_set_restrict_to_fill_level:
1135  * @range:                  A #GtkRange
1136  * @restrict_to_fill_level: Whether the fill level restricts slider movement.
1137  *
1138  * Sets whether the slider is restricted to the fill level. See
1139  * gtk_range_set_fill_level() for a general description of the fill
1140  * level concept.
1141  *
1142  * Since: 2.12
1143  **/
1144 void
1145 gtk_range_set_restrict_to_fill_level (GtkRange *range,
1146                                       gboolean  restrict_to_fill_level)
1147 {
1148   g_return_if_fail (GTK_IS_RANGE (range));
1149
1150   restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
1151
1152   if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
1153     {
1154       range->layout->restrict_to_fill_level = restrict_to_fill_level;
1155       g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
1156
1157       gtk_range_set_value (range, gtk_range_get_value (range));
1158     }
1159 }
1160
1161 /**
1162  * gtk_range_get_restrict_to_fill_level:
1163  * @range: A #GtkRange
1164  *
1165  * Gets whether the range is restricted to the fill level.
1166  *
1167  * Return value: %TRUE if @range is restricted to the fill level.
1168  *
1169  * Since: 2.12
1170  **/
1171 gboolean
1172 gtk_range_get_restrict_to_fill_level (GtkRange *range)
1173 {
1174   g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1175
1176   return range->layout->restrict_to_fill_level;
1177 }
1178
1179 /**
1180  * gtk_range_set_fill_level:
1181  * @range: a #GtkRange
1182  * @fill_level: the new position of the fill level indicator
1183  *
1184  * Set the new position of the fill level indicator.
1185  *
1186  * The "fill level" is probably best described by its most prominent
1187  * use case, which is an indicator for the amount of pre-buffering in
1188  * a streaming media player. In that use case, the value of the range
1189  * would indicate the current play position, and the fill level would
1190  * be the position up to which the file/stream has been downloaded.
1191  *
1192  * This amount of prebuffering can be displayed on the range's trough
1193  * and is themeable separately from the trough. To enable fill level
1194  * display, use gtk_range_set_show_fill_level(). The range defaults
1195  * to not showing the fill level.
1196  *
1197  * Additionally, it's possible to restrict the range's slider position
1198  * to values which are smaller than the fill level. This is controller
1199  * by gtk_range_set_restrict_to_fill_level() and is by default
1200  * enabled.
1201  *
1202  * Since: 2.12
1203  **/
1204 void
1205 gtk_range_set_fill_level (GtkRange *range,
1206                           gdouble   fill_level)
1207 {
1208   g_return_if_fail (GTK_IS_RANGE (range));
1209
1210   if (fill_level != range->layout->fill_level)
1211     {
1212       range->layout->fill_level = fill_level;
1213       g_object_notify (G_OBJECT (range), "fill-level");
1214
1215       if (range->layout->show_fill_level)
1216         gtk_widget_queue_draw (GTK_WIDGET (range));
1217
1218       if (range->layout->restrict_to_fill_level)
1219         gtk_range_set_value (range, gtk_range_get_value (range));
1220     }
1221 }
1222
1223 /**
1224  * gtk_range_get_fill_level:
1225  * @range : A #GtkRange
1226  *
1227  * Gets the current position of the fill level indicator.
1228  *
1229  * Return value: The current fill level
1230  *
1231  * Since: 2.12
1232  **/
1233 gdouble
1234 gtk_range_get_fill_level (GtkRange *range)
1235 {
1236   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1237
1238   return range->layout->fill_level;
1239 }
1240
1241 static gboolean
1242 should_invert (GtkRange *range)
1243 {  
1244   if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1245     return
1246       (range->inverted && !range->flippable) ||
1247       (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
1248       (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1249   else
1250     return range->inverted;
1251 }
1252
1253 static void
1254 gtk_range_destroy (GtkObject *object)
1255 {
1256   GtkRange *range = GTK_RANGE (object);
1257
1258   gtk_range_remove_step_timer (range);
1259   gtk_range_remove_update_timer (range);
1260
1261   if (range->layout->repaint_id)
1262     g_source_remove (range->layout->repaint_id);
1263   range->layout->repaint_id = 0;
1264
1265   if (range->adjustment)
1266     {
1267       g_signal_handlers_disconnect_by_func (range->adjustment,
1268                                             gtk_range_adjustment_changed,
1269                                             range);
1270       g_signal_handlers_disconnect_by_func (range->adjustment,
1271                                             gtk_range_adjustment_value_changed,
1272                                             range);
1273       g_object_unref (range->adjustment);
1274       range->adjustment = NULL;
1275     }
1276
1277   if (range->layout->n_marks)
1278     {
1279       g_free (range->layout->marks);
1280       range->layout->marks = NULL;
1281       g_free (range->layout->mark_pos);
1282       range->layout->mark_pos = NULL;
1283       range->layout->n_marks = 0;
1284     }
1285
1286   GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
1287 }
1288
1289 static void
1290 gtk_range_size_request (GtkWidget      *widget,
1291                         GtkRequisition *requisition)
1292 {
1293   GtkRange *range;
1294   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1295   GdkRectangle range_rect;
1296   GtkBorder border;
1297   
1298   range = GTK_RANGE (widget);
1299   
1300   gtk_range_get_props (range,
1301                        &slider_width, &stepper_size,
1302                        &focus_width, &trough_border,
1303                        &stepper_spacing, NULL,
1304                        NULL, NULL);
1305
1306   gtk_range_calc_request (range, 
1307                           slider_width, stepper_size,
1308                           focus_width, trough_border, stepper_spacing,
1309                           &range_rect, &border, NULL, NULL, NULL, NULL);
1310
1311   requisition->width = range_rect.width + border.left + border.right;
1312   requisition->height = range_rect.height + border.top + border.bottom;
1313 }
1314
1315 static void
1316 gtk_range_size_allocate (GtkWidget     *widget,
1317                          GtkAllocation *allocation)
1318 {
1319   GtkRange *range;
1320
1321   range = GTK_RANGE (widget);
1322
1323   widget->allocation = *allocation;
1324   
1325   range->layout->recalc_marks = TRUE;
1326
1327   range->need_recalc = TRUE;
1328   gtk_range_calc_layout (range, range->adjustment->value);
1329
1330   if (GTK_WIDGET_REALIZED (range))
1331     gdk_window_move_resize (range->event_window,
1332                             widget->allocation.x,
1333                             widget->allocation.y,
1334                             widget->allocation.width,
1335                             widget->allocation.height);
1336 }
1337
1338 static void
1339 gtk_range_realize (GtkWidget *widget)
1340 {
1341   GtkRange *range;
1342   GdkWindowAttr attributes;
1343   gint attributes_mask;  
1344
1345   range = GTK_RANGE (widget);
1346
1347   gtk_range_calc_layout (range, range->adjustment->value);
1348   
1349   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1350
1351   widget->window = gtk_widget_get_parent_window (widget);
1352   g_object_ref (widget->window);
1353   
1354   attributes.window_type = GDK_WINDOW_CHILD;
1355   attributes.x = widget->allocation.x;
1356   attributes.y = widget->allocation.y;
1357   attributes.width = widget->allocation.width;
1358   attributes.height = widget->allocation.height;
1359   attributes.wclass = GDK_INPUT_ONLY;
1360   attributes.event_mask = gtk_widget_get_events (widget);
1361   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1362                             GDK_BUTTON_RELEASE_MASK |
1363                             GDK_ENTER_NOTIFY_MASK |
1364                             GDK_LEAVE_NOTIFY_MASK |
1365                             GDK_POINTER_MOTION_MASK |
1366                             GDK_POINTER_MOTION_HINT_MASK);
1367
1368   attributes_mask = GDK_WA_X | GDK_WA_Y;
1369
1370   range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1371                                         &attributes, attributes_mask);
1372   gdk_window_set_user_data (range->event_window, range);
1373
1374   widget->style = gtk_style_attach (widget->style, widget->window);
1375 }
1376
1377 static void
1378 gtk_range_unrealize (GtkWidget *widget)
1379 {
1380   GtkRange *range = GTK_RANGE (widget);
1381
1382   gtk_range_remove_step_timer (range);
1383   gtk_range_remove_update_timer (range);
1384   
1385   gdk_window_set_user_data (range->event_window, NULL);
1386   gdk_window_destroy (range->event_window);
1387   range->event_window = NULL;
1388
1389   GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1390 }
1391
1392 static void
1393 gtk_range_map (GtkWidget *widget)
1394 {
1395   GtkRange *range = GTK_RANGE (widget);
1396   
1397   gdk_window_show (range->event_window);
1398
1399   GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1400 }
1401
1402 static void
1403 gtk_range_unmap (GtkWidget *widget)
1404 {
1405   GtkRange *range = GTK_RANGE (widget);
1406     
1407   stop_scrolling (range);
1408
1409   gdk_window_hide (range->event_window);
1410
1411   GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1412 }
1413
1414 static const gchar *
1415 gtk_range_get_slider_detail (GtkRange *range)
1416 {
1417   const gchar *slider_detail;
1418
1419   if (range->layout->slider_detail_quark)
1420     return g_quark_to_string (range->layout->slider_detail_quark);
1421
1422   slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
1423
1424   if (slider_detail && slider_detail[0] == 'X')
1425     {
1426       gchar *detail = g_strdup (slider_detail);
1427
1428       detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1429
1430       range->layout->slider_detail_quark = g_quark_from_string (detail);
1431
1432       g_free (detail);
1433
1434       return g_quark_to_string (range->layout->slider_detail_quark);
1435     }
1436
1437   return slider_detail;
1438 }
1439
1440 static const gchar *
1441 gtk_range_get_stepper_detail (GtkRange *range)
1442 {
1443   const gchar *stepper_detail;
1444
1445   if (range->layout->stepper_detail_quark)
1446     return g_quark_to_string (range->layout->stepper_detail_quark);
1447
1448   stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
1449
1450   if (stepper_detail && stepper_detail[0] == 'X')
1451     {
1452       gchar *detail = g_strdup (stepper_detail);
1453
1454       detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1455
1456       range->layout->stepper_detail_quark = g_quark_from_string (detail);
1457
1458       g_free (detail);
1459
1460       return g_quark_to_string (range->layout->stepper_detail_quark);
1461     }
1462
1463   return stepper_detail;
1464 }
1465
1466 static void
1467 draw_stepper (GtkRange     *range,
1468               GdkRectangle *rect,
1469               GtkArrowType  arrow_type,
1470               gboolean      clicked,
1471               gboolean      prelighted,
1472               GdkRectangle *area)
1473 {
1474   GtkStateType state_type;
1475   GtkShadowType shadow_type;
1476   GdkRectangle intersection;
1477   GtkWidget *widget = GTK_WIDGET (range);
1478   gfloat arrow_scaling;
1479
1480   gint arrow_x;
1481   gint arrow_y;
1482   gint arrow_width;
1483   gint arrow_height;
1484
1485   gboolean arrow_sensitive = TRUE;
1486
1487   /* More to get the right clip region than for efficiency */
1488   if (!gdk_rectangle_intersect (area, rect, &intersection))
1489     return;
1490
1491   intersection.x += widget->allocation.x;
1492   intersection.y += widget->allocation.y;
1493
1494   if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1495                             arrow_type == GTK_ARROW_RIGHT)) ||
1496       (range->inverted  && (arrow_type == GTK_ARROW_UP ||
1497                             arrow_type == GTK_ARROW_LEFT)))
1498     {
1499       arrow_sensitive = range->layout->upper_sensitive;
1500     }
1501   else
1502     {
1503       arrow_sensitive = range->layout->lower_sensitive;
1504     }
1505
1506   if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
1507     state_type = GTK_STATE_INSENSITIVE;
1508   else if (clicked)
1509     state_type = GTK_STATE_ACTIVE;
1510   else if (prelighted)
1511     state_type = GTK_STATE_PRELIGHT;
1512   else 
1513     state_type = GTK_STATE_NORMAL;
1514
1515   if (clicked && arrow_sensitive)
1516     shadow_type = GTK_SHADOW_IN;
1517   else
1518     shadow_type = GTK_SHADOW_OUT;
1519
1520   gtk_paint_box (widget->style,
1521                  widget->window,
1522                  state_type, shadow_type,
1523                  &intersection, widget,
1524                  gtk_range_get_stepper_detail (range),
1525                  widget->allocation.x + rect->x,
1526                  widget->allocation.y + rect->y,
1527                  rect->width,
1528                  rect->height);
1529
1530   gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1531
1532   arrow_width = rect->width * arrow_scaling;
1533   arrow_height = rect->height * arrow_scaling;
1534   arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1535   arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1536
1537   if (clicked && arrow_sensitive)
1538     {
1539       gint arrow_displacement_x;
1540       gint arrow_displacement_y;
1541
1542       gtk_range_get_props (GTK_RANGE (widget),
1543                            NULL, NULL, NULL, NULL, NULL, NULL,
1544                            &arrow_displacement_x, &arrow_displacement_y);
1545       
1546       arrow_x += arrow_displacement_x;
1547       arrow_y += arrow_displacement_y;
1548     }
1549   
1550   gtk_paint_arrow (widget->style,
1551                    widget->window,
1552                    state_type, shadow_type, 
1553                    &intersection, widget,
1554                    gtk_range_get_stepper_detail (range),
1555                    arrow_type,
1556                    TRUE,
1557                    arrow_x, arrow_y, arrow_width, arrow_height);
1558 }
1559
1560 static gboolean
1561 gtk_range_expose (GtkWidget      *widget,
1562                   GdkEventExpose *event)
1563 {
1564   GtkRange *range = GTK_RANGE (widget);
1565   gboolean sensitive;
1566   GtkStateType state;
1567   GtkShadowType shadow_type;
1568   GdkRectangle expose_area;     /* Relative to widget->allocation */
1569   GdkRectangle area;
1570   gint focus_line_width = 0;
1571   gint focus_padding = 0;
1572   gboolean touchscreen;
1573
1574   g_object_get (gtk_widget_get_settings (widget),
1575                 "gtk-touchscreen-mode", &touchscreen,
1576                 NULL);
1577   if (GTK_WIDGET_CAN_FOCUS (range))
1578     gtk_widget_style_get (GTK_WIDGET (range),
1579                           "focus-line-width", &focus_line_width,
1580                           "focus-padding", &focus_padding,
1581                           NULL);
1582
1583   /* we're now exposing, so there's no need to force early repaints */
1584   if (range->layout->repaint_id)
1585     g_source_remove (range->layout->repaint_id);
1586   range->layout->repaint_id = 0;
1587
1588   expose_area = event->area;
1589   expose_area.x -= widget->allocation.x;
1590   expose_area.y -= widget->allocation.y;
1591   
1592   gtk_range_calc_marks (range);
1593   gtk_range_calc_layout (range, range->adjustment->value);
1594
1595   sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
1596
1597   /* Just to be confusing, we draw the trough for the whole
1598    * range rectangle, not the trough rectangle (the trough
1599    * rectangle is just for hit detection)
1600    */
1601   /* The gdk_rectangle_intersect is more to get the right
1602    * clip region (limited to range_rect) than for efficiency
1603    */
1604   if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1605                                &area))
1606     {
1607       gint     x      = (widget->allocation.x + range->range_rect.x +
1608                          focus_line_width + focus_padding);
1609       gint     y      = (widget->allocation.y + range->range_rect.y +
1610                          focus_line_width + focus_padding);
1611       gint     width  = (range->range_rect.width -
1612                          2 * (focus_line_width + focus_padding));
1613       gint     height = (range->range_rect.height -
1614                          2 * (focus_line_width + focus_padding));
1615       gboolean trough_side_details;
1616       gboolean trough_under_steppers;
1617       gint     stepper_size;
1618       gint     stepper_spacing;
1619
1620       area.x += widget->allocation.x;
1621       area.y += widget->allocation.y;
1622
1623       gtk_widget_style_get (GTK_WIDGET (range),
1624                             "trough-side-details",   &trough_side_details,
1625                             "trough-under-steppers", &trough_under_steppers,
1626                             "stepper-size",          &stepper_size,
1627                             "stepper-spacing",       &stepper_spacing,
1628                             NULL);
1629
1630       if (stepper_spacing > 0)
1631         trough_under_steppers = FALSE;
1632
1633       if (! trough_under_steppers)
1634         {
1635           gint offset  = 0;
1636           gint shorter = 0;
1637
1638           if (range->has_stepper_a)
1639             offset += stepper_size;
1640
1641           if (range->has_stepper_b)
1642             offset += stepper_size;
1643
1644           shorter += offset;
1645
1646           if (range->has_stepper_c)
1647             shorter += stepper_size;
1648
1649           if (range->has_stepper_d)
1650             shorter += stepper_size;
1651
1652           if (range->has_stepper_a || range->has_stepper_b)
1653             {
1654               offset  += stepper_spacing;
1655               shorter += stepper_spacing;
1656             }
1657
1658           if (range->has_stepper_c || range->has_stepper_d)
1659             {
1660               shorter += stepper_spacing;
1661             }
1662
1663           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1664             {
1665               x     += offset;
1666               width -= shorter;
1667             }
1668           else
1669             {
1670               y      += offset;
1671               height -= shorter;
1672             }
1673         }
1674
1675       if (! trough_side_details)
1676         {
1677           gtk_paint_box (widget->style,
1678                          widget->window,
1679                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1680                          GTK_SHADOW_IN,
1681                          &area, GTK_WIDGET(range), "trough",
1682                          x, y,
1683                          width, height);
1684         }
1685       else
1686         {
1687           gint trough_change_pos_x = width;
1688           gint trough_change_pos_y = height;
1689
1690           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1691             trough_change_pos_x = (range->layout->slider.x +
1692                                    range->layout->slider.width / 2 -
1693                                    (x - widget->allocation.x));
1694           else
1695             trough_change_pos_y = (range->layout->slider.y +
1696                                    range->layout->slider.height / 2 -
1697                                    (y - widget->allocation.y));
1698
1699           gtk_paint_box (widget->style,
1700                          widget->window,
1701                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1702                          GTK_SHADOW_IN,
1703                          &area, GTK_WIDGET (range),
1704                          should_invert (range) ? "trough-upper" : "trough-lower",
1705                          x, y,
1706                          trough_change_pos_x, trough_change_pos_y);
1707
1708           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1709             trough_change_pos_y = 0;
1710           else
1711             trough_change_pos_x = 0;
1712
1713           gtk_paint_box (widget->style,
1714                          widget->window,
1715                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1716                          GTK_SHADOW_IN,
1717                          &area, GTK_WIDGET (range),
1718                          should_invert (range) ? "trough-lower" : "trough-upper",
1719                          x + trough_change_pos_x, y + trough_change_pos_y,
1720                          width - trough_change_pos_x,
1721                          height - trough_change_pos_y);
1722         }
1723
1724       if (range->layout->show_fill_level &&
1725           range->adjustment->upper - range->adjustment->page_size -
1726           range->adjustment->lower != 0)
1727         {
1728           gdouble  fill_level  = range->layout->fill_level;
1729           gint     fill_x      = x;
1730           gint     fill_y      = y;
1731           gint     fill_width  = width;
1732           gint     fill_height = height;
1733           gchar   *fill_detail;
1734
1735           fill_level = CLAMP (fill_level, range->adjustment->lower,
1736                               range->adjustment->upper -
1737                               range->adjustment->page_size);
1738
1739           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1740             {
1741               fill_x     = widget->allocation.x + range->layout->trough.x;
1742               fill_width = (range->layout->slider.width +
1743                             (fill_level - range->adjustment->lower) /
1744                             (range->adjustment->upper -
1745                              range->adjustment->lower -
1746                              range->adjustment->page_size) *
1747                             (range->layout->trough.width -
1748                              range->layout->slider.width));
1749
1750               if (should_invert (range))
1751                 fill_x += range->layout->trough.width - fill_width;
1752             }
1753           else
1754             {
1755               fill_y      = widget->allocation.y + range->layout->trough.y;
1756               fill_height = (range->layout->slider.height +
1757                              (fill_level - range->adjustment->lower) /
1758                              (range->adjustment->upper -
1759                               range->adjustment->lower -
1760                               range->adjustment->page_size) *
1761                              (range->layout->trough.height -
1762                               range->layout->slider.height));
1763
1764               if (should_invert (range))
1765                 fill_y += range->layout->trough.height - fill_height;
1766             }
1767
1768           if (fill_level < range->adjustment->upper - range->adjustment->page_size)
1769             fill_detail = "trough-fill-level-full";
1770           else
1771             fill_detail = "trough-fill-level";
1772
1773           gtk_paint_box (widget->style,
1774                          widget->window,
1775                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1776                          GTK_SHADOW_OUT,
1777                          &area, GTK_WIDGET (range), fill_detail,
1778                          fill_x, fill_y,
1779                          fill_width, fill_height);
1780         }
1781
1782       if (sensitive &&
1783           GTK_WIDGET_HAS_FOCUS (range))
1784         gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
1785                          &area, widget, "trough",
1786                          widget->allocation.x + range->range_rect.x,
1787                          widget->allocation.y + range->range_rect.y,
1788                          range->range_rect.width,
1789                          range->range_rect.height);
1790     }
1791
1792   shadow_type = GTK_SHADOW_OUT;
1793
1794   if (!sensitive)
1795     state = GTK_STATE_INSENSITIVE;
1796   else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1797     state = GTK_STATE_PRELIGHT;
1798   else
1799     state = GTK_STATE_NORMAL;
1800
1801   if (range->layout->grab_location == MOUSE_SLIDER)
1802     {
1803       gboolean activate_slider;
1804
1805       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
1806
1807       if (activate_slider)
1808         {
1809           state = GTK_STATE_ACTIVE;
1810           shadow_type = GTK_SHADOW_IN;
1811         }
1812     }
1813
1814   if (gdk_rectangle_intersect (&expose_area,
1815                                &range->layout->slider,
1816                                &area))
1817     {
1818       area.x += widget->allocation.x;
1819       area.y += widget->allocation.y;
1820       
1821       gtk_paint_slider (widget->style,
1822                         widget->window,
1823                         state,
1824                         shadow_type,
1825                         &area,
1826                         widget,
1827                         gtk_range_get_slider_detail (range),
1828                         widget->allocation.x + range->layout->slider.x,
1829                         widget->allocation.y + range->layout->slider.y,
1830                         range->layout->slider.width,
1831                         range->layout->slider.height,
1832                         range->orientation);
1833     }
1834   
1835   if (range->has_stepper_a)
1836     draw_stepper (range, &range->layout->stepper_a,
1837                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1838                   range->layout->grab_location == MOUSE_STEPPER_A,
1839                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
1840                   &expose_area);
1841
1842   if (range->has_stepper_b)
1843     draw_stepper (range, &range->layout->stepper_b,
1844                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1845                   range->layout->grab_location == MOUSE_STEPPER_B,
1846                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
1847                   &expose_area);
1848
1849   if (range->has_stepper_c)
1850     draw_stepper (range, &range->layout->stepper_c,
1851                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1852                   range->layout->grab_location == MOUSE_STEPPER_C,
1853                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
1854                   &expose_area);
1855
1856   if (range->has_stepper_d)
1857     draw_stepper (range, &range->layout->stepper_d,
1858                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1859                   range->layout->grab_location == MOUSE_STEPPER_D,
1860                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
1861                   &expose_area);
1862   
1863   return FALSE;
1864 }
1865
1866 static void
1867 range_grab_add (GtkRange      *range,
1868                 MouseLocation  location,
1869                 gint           button)
1870 {
1871   /* we don't actually gtk_grab, since a button is down */
1872
1873   gtk_grab_add (GTK_WIDGET (range));
1874   
1875   range->layout->grab_location = location;
1876   range->layout->grab_button = button;
1877   
1878   if (gtk_range_update_mouse_location (range))
1879     gtk_widget_queue_draw (GTK_WIDGET (range));
1880 }
1881
1882 static void
1883 range_grab_remove (GtkRange *range)
1884 {
1885   MouseLocation location;
1886
1887   gtk_grab_remove (GTK_WIDGET (range));
1888  
1889   location = range->layout->grab_location; 
1890   range->layout->grab_location = MOUSE_OUTSIDE;
1891   range->layout->grab_button = 0;
1892
1893   if (gtk_range_update_mouse_location (range) ||
1894       location != MOUSE_OUTSIDE)
1895     gtk_widget_queue_draw (GTK_WIDGET (range));
1896 }
1897
1898 static GtkScrollType
1899 range_get_scroll_for_grab (GtkRange      *range)
1900
1901   gboolean invert;
1902
1903   invert = should_invert (range);
1904   switch (range->layout->grab_location)
1905     {
1906       /* Backward stepper */
1907     case MOUSE_STEPPER_A:
1908     case MOUSE_STEPPER_C:
1909       switch (range->layout->grab_button)
1910         {
1911         case 1:
1912           return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
1913           break;
1914         case 2:
1915           return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
1916           break;
1917         case 3:
1918           return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
1919           break;
1920         }
1921       break;
1922
1923       /* Forward stepper */
1924     case MOUSE_STEPPER_B:
1925     case MOUSE_STEPPER_D:
1926       switch (range->layout->grab_button)
1927         {
1928         case 1:
1929           return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
1930           break;
1931         case 2:
1932           return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
1933           break;
1934         case 3:
1935           return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
1936           break;
1937        }
1938       break;
1939
1940       /* In the trough */
1941     case MOUSE_TROUGH:
1942       {
1943         if (range->trough_click_forward)
1944           return GTK_SCROLL_PAGE_FORWARD;
1945         else
1946           return GTK_SCROLL_PAGE_BACKWARD;
1947       }
1948       break;
1949
1950     case MOUSE_OUTSIDE:
1951     case MOUSE_SLIDER:
1952     case MOUSE_WIDGET:
1953       break;
1954     }
1955
1956   return GTK_SCROLL_NONE;
1957 }
1958
1959 static gdouble
1960 coord_to_value (GtkRange *range,
1961                 gint      coord)
1962 {
1963   gdouble frac;
1964   gdouble value;
1965   gint    trough_length;
1966   gint    trough_start;
1967   gint    slider_length;
1968   gint    trough_border;
1969   gint    trough_under_steppers;
1970
1971   if (range->orientation == GTK_ORIENTATION_VERTICAL)
1972     {
1973       trough_length = range->layout->trough.height;
1974       trough_start  = range->layout->trough.y;
1975       slider_length = range->layout->slider.height;
1976     }
1977   else
1978     {
1979       trough_length = range->layout->trough.width;
1980       trough_start  = range->layout->trough.x;
1981       slider_length = range->layout->slider.width;
1982     }
1983
1984   gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
1985                        &trough_under_steppers, NULL, NULL);
1986
1987   if (! trough_under_steppers)
1988     {
1989       trough_start += trough_border;
1990       trough_length -= 2 * trough_border;
1991     }
1992
1993   if (trough_length == slider_length)
1994     frac = 1.0;
1995   else
1996     frac = (MAX (0, coord - trough_start) /
1997             (gdouble) (trough_length - slider_length));
1998
1999   if (should_invert (range))
2000     frac = 1.0 - frac;
2001
2002   value = range->adjustment->lower + frac * (range->adjustment->upper -
2003                                              range->adjustment->lower -
2004                                              range->adjustment->page_size);
2005
2006   return value;
2007 }
2008
2009 static gboolean
2010 gtk_range_key_press (GtkWidget   *widget,
2011                      GdkEventKey *event)
2012 {
2013   GtkRange *range = GTK_RANGE (widget);
2014
2015   if (event->keyval == GDK_Escape &&
2016       range->layout->grab_location != MOUSE_OUTSIDE)
2017     {
2018       stop_scrolling (range);
2019
2020       update_slider_position (range,
2021                               range->slide_initial_coordinate,
2022                               range->slide_initial_coordinate);
2023
2024       return TRUE;
2025     }
2026
2027   return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
2028 }
2029
2030 static gint
2031 gtk_range_button_press (GtkWidget      *widget,
2032                         GdkEventButton *event)
2033 {
2034   GtkRange *range = GTK_RANGE (widget);
2035   
2036   if (!GTK_WIDGET_HAS_FOCUS (widget))
2037     gtk_widget_grab_focus (widget);
2038
2039   /* ignore presses when we're already doing something else. */
2040   if (range->layout->grab_location != MOUSE_OUTSIDE)
2041     return FALSE;
2042
2043   range->layout->mouse_x = event->x;
2044   range->layout->mouse_y = event->y;
2045   if (gtk_range_update_mouse_location (range))
2046     gtk_widget_queue_draw (widget);
2047     
2048   if (range->layout->mouse_location == MOUSE_TROUGH  &&
2049       event->button == 1)
2050     {
2051       /* button 1 steps by page increment, as with button 2 on a stepper
2052        */
2053       GtkScrollType scroll;
2054       gdouble click_value;
2055       
2056       click_value = coord_to_value (range,
2057                                     range->orientation == GTK_ORIENTATION_VERTICAL ?
2058                                     event->y : event->x);
2059       
2060       range->trough_click_forward = click_value > range->adjustment->value;
2061       range_grab_add (range, MOUSE_TROUGH, event->button);
2062       
2063       scroll = range_get_scroll_for_grab (range);
2064       
2065       gtk_range_add_step_timer (range, scroll);
2066
2067       return TRUE;
2068     }
2069   else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
2070             range->layout->mouse_location == MOUSE_STEPPER_B ||
2071             range->layout->mouse_location == MOUSE_STEPPER_C ||
2072             range->layout->mouse_location == MOUSE_STEPPER_D) &&
2073            (event->button == 1 || event->button == 2 || event->button == 3))
2074     {
2075       GdkRectangle *stepper_area;
2076       GtkScrollType scroll;
2077       
2078       range_grab_add (range, range->layout->mouse_location, event->button);
2079
2080       stepper_area = get_area (range, range->layout->mouse_location);
2081       gtk_widget_queue_draw_area (widget,
2082                                   widget->allocation.x + stepper_area->x,
2083                                   widget->allocation.y + stepper_area->y,
2084                                   stepper_area->width,
2085                                   stepper_area->height);
2086
2087       scroll = range_get_scroll_for_grab (range);
2088       if (scroll != GTK_SCROLL_NONE)
2089         gtk_range_add_step_timer (range, scroll);
2090       
2091       return TRUE;
2092     }
2093   else if ((range->layout->mouse_location == MOUSE_TROUGH &&
2094             event->button == 2) ||
2095            range->layout->mouse_location == MOUSE_SLIDER)
2096     {
2097       gboolean need_value_update = FALSE;
2098       gboolean activate_slider;
2099
2100       /* Any button can be used to drag the slider, but you can start
2101        * dragging the slider with a trough click using button 2;
2102        * On button 2 press, we warp the slider to mouse position,
2103        * then begin the slider drag.
2104        */
2105       if (event->button == 2)
2106         {
2107           gdouble slider_low_value, slider_high_value, new_value;
2108           
2109           slider_high_value =
2110             coord_to_value (range,
2111                             range->orientation == GTK_ORIENTATION_VERTICAL ?
2112                             event->y : event->x);
2113           slider_low_value =
2114             coord_to_value (range,
2115                             range->orientation == GTK_ORIENTATION_VERTICAL ?
2116                             event->y - range->layout->slider.height :
2117                             event->x - range->layout->slider.width);
2118           
2119           /* compute new value for warped slider */
2120           new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
2121
2122           /* recalc slider, so we can set slide_initial_slider_position
2123            * properly
2124            */
2125           range->need_recalc = TRUE;
2126           gtk_range_calc_layout (range, new_value);
2127
2128           /* defer adjustment updates to update_slider_position() in order
2129            * to keep pixel quantisation
2130            */
2131           need_value_update = TRUE;
2132         }
2133       
2134       if (range->orientation == GTK_ORIENTATION_VERTICAL)
2135         {
2136           range->slide_initial_slider_position = range->layout->slider.y;
2137           range->slide_initial_coordinate = event->y;
2138         }
2139       else
2140         {
2141           range->slide_initial_slider_position = range->layout->slider.x;
2142           range->slide_initial_coordinate = event->x;
2143         }
2144
2145       range_grab_add (range, MOUSE_SLIDER, event->button);
2146
2147       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2148
2149       /* force a redraw, if the active slider is drawn differently to the
2150        * prelight one
2151        */
2152       if (activate_slider)
2153         gtk_widget_queue_draw (widget);
2154
2155       if (need_value_update)
2156         update_slider_position (range, event->x, event->y);
2157
2158       return TRUE;
2159     }
2160   
2161   return FALSE;
2162 }
2163
2164 /* During a slide, move the slider as required given new mouse position */
2165 static void
2166 update_slider_position (GtkRange *range,
2167                         gint      mouse_x,
2168                         gint      mouse_y)
2169 {
2170   gint delta;
2171   gint c;
2172   gdouble new_value;
2173   gboolean handled;
2174   gdouble next_value;
2175   gdouble mark_value;
2176   gdouble mark_delta;
2177   gint i;
2178
2179   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2180     delta = mouse_y - range->slide_initial_coordinate;
2181   else
2182     delta = mouse_x - range->slide_initial_coordinate;
2183
2184   c = range->slide_initial_slider_position + delta;
2185
2186   new_value = coord_to_value (range, c);
2187   next_value = coord_to_value (range, c + 1);
2188   mark_delta = fabs (next_value - new_value); 
2189
2190   for (i = 0; i < range->layout->n_marks; i++)
2191     {
2192       mark_value = range->layout->marks[i];
2193
2194       if (fabs (range->adjustment->value - mark_value) < 3 * mark_delta)
2195         {
2196           if (fabs (new_value - mark_value) < (range->slider_end - range->slider_start) * 0.5 * mark_delta)
2197             {
2198               new_value = mark_value;
2199               break;
2200             }
2201         }
2202     }  
2203
2204   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2205                  &handled);
2206 }
2207
2208 static void 
2209 stop_scrolling (GtkRange *range)
2210 {
2211   range_grab_remove (range);
2212   gtk_range_remove_step_timer (range);
2213   /* Flush any pending discontinuous/delayed updates */
2214   gtk_range_update_value (range);
2215 }
2216
2217 static gboolean
2218 gtk_range_grab_broken (GtkWidget          *widget,
2219                        GdkEventGrabBroken *event)
2220 {
2221   GtkRange *range = GTK_RANGE (widget);
2222
2223   if (range->layout->grab_location != MOUSE_OUTSIDE)
2224     {
2225       if (range->layout->grab_location == MOUSE_SLIDER)
2226         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2227       
2228       stop_scrolling (range);
2229       
2230       return TRUE;
2231     }
2232   
2233   return FALSE;
2234 }
2235
2236 static gint
2237 gtk_range_button_release (GtkWidget      *widget,
2238                           GdkEventButton *event)
2239 {
2240   GtkRange *range = GTK_RANGE (widget);
2241
2242   if (event->window == range->event_window)
2243     {
2244       range->layout->mouse_x = event->x;
2245       range->layout->mouse_y = event->y;
2246     }
2247   else
2248     {
2249       gdk_window_get_pointer (range->event_window,
2250                               &range->layout->mouse_x,
2251                               &range->layout->mouse_y,
2252                               NULL);
2253     }
2254   
2255   if (range->layout->grab_button == event->button)
2256     {
2257       if (range->layout->grab_location == MOUSE_SLIDER)
2258         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2259
2260       stop_scrolling (range);
2261       
2262       return TRUE;
2263     }
2264
2265   return FALSE;
2266 }
2267
2268 /**
2269  * _gtk_range_get_wheel_delta:
2270  * @range: a #GtkRange
2271  * @direction: A #GdkScrollDirection
2272  * 
2273  * Returns a good step value for the mouse wheel.
2274  * 
2275  * Return value: A good step value for the mouse wheel. 
2276  * 
2277  * Since: 2.4
2278  **/
2279 gdouble
2280 _gtk_range_get_wheel_delta (GtkRange           *range,
2281                             GdkScrollDirection  direction)
2282 {
2283   GtkAdjustment *adj = range->adjustment;
2284   gdouble delta;
2285
2286   if (GTK_IS_SCROLLBAR (range))
2287     delta = pow (adj->page_size, 2.0 / 3.0);
2288   else
2289     delta = adj->step_increment * 2;
2290   
2291   if (direction == GDK_SCROLL_UP ||
2292       direction == GDK_SCROLL_LEFT)
2293     delta = - delta;
2294   
2295   if (range->inverted)
2296     delta = - delta;
2297
2298   return delta;
2299 }
2300       
2301 static gboolean
2302 gtk_range_scroll_event (GtkWidget      *widget,
2303                         GdkEventScroll *event)
2304 {
2305   GtkRange *range = GTK_RANGE (widget);
2306
2307   if (GTK_WIDGET_REALIZED (range))
2308     {
2309       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2310       gdouble delta;
2311       gboolean handled;
2312
2313       delta = _gtk_range_get_wheel_delta (range, event->direction);
2314
2315       g_signal_emit (range, signals[CHANGE_VALUE], 0,
2316                      GTK_SCROLL_JUMP, adj->value + delta,
2317                      &handled);
2318       
2319       /* Policy DELAYED makes sense with scroll events,
2320        * but DISCONTINUOUS doesn't, so we update immediately
2321        * for DISCONTINUOUS
2322        */
2323       if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2324         gtk_range_update_value (range);
2325     }
2326
2327   return TRUE;
2328 }
2329
2330 static gboolean
2331 gtk_range_motion_notify (GtkWidget      *widget,
2332                          GdkEventMotion *event)
2333 {
2334   GtkRange *range;
2335
2336   range = GTK_RANGE (widget);
2337
2338   gdk_event_request_motions (event);
2339   
2340   range->layout->mouse_x = event->x;
2341   range->layout->mouse_y = event->y;
2342
2343   if (gtk_range_update_mouse_location (range))
2344     gtk_widget_queue_draw (widget);
2345
2346   if (range->layout->grab_location == MOUSE_SLIDER)
2347     update_slider_position (range, event->x, event->y);
2348
2349   /* We handled the event if the mouse was in the range_rect */
2350   return range->layout->mouse_location != MOUSE_OUTSIDE;
2351 }
2352
2353 static gboolean
2354 gtk_range_enter_notify (GtkWidget        *widget,
2355                         GdkEventCrossing *event)
2356 {
2357   GtkRange *range = GTK_RANGE (widget);
2358
2359   range->layout->mouse_x = event->x;
2360   range->layout->mouse_y = event->y;
2361
2362   if (gtk_range_update_mouse_location (range))
2363     gtk_widget_queue_draw (widget);
2364   
2365   return TRUE;
2366 }
2367
2368 static gboolean
2369 gtk_range_leave_notify (GtkWidget        *widget,
2370                         GdkEventCrossing *event)
2371 {
2372   GtkRange *range = GTK_RANGE (widget);
2373
2374   range->layout->mouse_x = -1;
2375   range->layout->mouse_y = -1;
2376
2377   if (gtk_range_update_mouse_location (range))
2378     gtk_widget_queue_draw (widget);
2379   
2380   return TRUE;
2381 }
2382
2383 static void
2384 gtk_range_grab_notify (GtkWidget *widget,
2385                        gboolean   was_grabbed)
2386 {
2387   if (!was_grabbed)
2388     stop_scrolling (GTK_RANGE (widget));
2389 }
2390
2391 static void
2392 gtk_range_state_changed (GtkWidget    *widget,
2393                          GtkStateType  previous_state)
2394 {
2395   if (!GTK_WIDGET_IS_SENSITIVE (widget)) 
2396     stop_scrolling (GTK_RANGE (widget));
2397 }
2398
2399 #define check_rectangle(rectangle1, rectangle2)              \
2400   {                                                          \
2401     if (rectangle1.x != rectangle2.x) return TRUE;           \
2402     if (rectangle1.y != rectangle2.y) return TRUE;           \
2403     if (rectangle1.width  != rectangle2.width)  return TRUE; \
2404     if (rectangle1.height != rectangle2.height) return TRUE; \
2405   }
2406
2407 static gboolean
2408 layout_changed (GtkRangeLayout *layout1, 
2409                 GtkRangeLayout *layout2)
2410 {
2411   check_rectangle (layout1->slider, layout2->slider);
2412   check_rectangle (layout1->trough, layout2->trough);
2413   check_rectangle (layout1->stepper_a, layout2->stepper_a);
2414   check_rectangle (layout1->stepper_d, layout2->stepper_d);
2415   check_rectangle (layout1->stepper_b, layout2->stepper_b);
2416   check_rectangle (layout1->stepper_c, layout2->stepper_c);
2417
2418   if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2419   if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2420
2421   return FALSE;
2422 }
2423
2424 static void
2425 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2426                               gpointer       data)
2427 {
2428   GtkRange *range = GTK_RANGE (data);
2429   /* create a copy of the layout */
2430   GtkRangeLayout layout = *range->layout;
2431
2432   range->layout->recalc_marks = TRUE;
2433   range->need_recalc = TRUE;
2434   gtk_range_calc_layout (range, range->adjustment->value);
2435   
2436   /* now check whether the layout changed  */
2437   if (layout_changed (range->layout, &layout))
2438     gtk_widget_queue_draw (GTK_WIDGET (range));
2439
2440   /* Note that we don't round off to range->round_digits here.
2441    * that's because it's really broken to change a value
2442    * in response to a change signal on that value; round_digits
2443    * is therefore defined to be a filter on what the GtkRange
2444    * can input into the adjustment, not a filter that the GtkRange
2445    * will enforce on the adjustment.
2446    */
2447 }
2448
2449 static gboolean
2450 force_repaint (gpointer data)
2451 {
2452   GtkRange *range = GTK_RANGE (data);
2453   range->layout->repaint_id = 0;
2454   if (GTK_WIDGET_DRAWABLE (range))
2455     gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2456   return FALSE;
2457 }
2458
2459 static void
2460 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2461                                     gpointer       data)
2462 {
2463   GtkRange *range = GTK_RANGE (data);
2464   /* create a copy of the layout */
2465   GtkRangeLayout layout = *range->layout;
2466
2467   range->need_recalc = TRUE;
2468   gtk_range_calc_layout (range, range->adjustment->value);
2469   
2470   /* now check whether the layout changed  */
2471   if (layout_changed (range->layout, &layout))
2472     {
2473       gtk_widget_queue_draw (GTK_WIDGET (range));
2474       /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2475       if (!range->layout->repaint_id)
2476         range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
2477     }
2478   
2479   /* Note that we don't round off to range->round_digits here.
2480    * that's because it's really broken to change a value
2481    * in response to a change signal on that value; round_digits
2482    * is therefore defined to be a filter on what the GtkRange
2483    * can input into the adjustment, not a filter that the GtkRange
2484    * will enforce on the adjustment.
2485    */
2486
2487   g_signal_emit (range, signals[VALUE_CHANGED], 0);
2488 }
2489
2490 static void
2491 gtk_range_style_set (GtkWidget *widget,
2492                      GtkStyle  *previous_style)
2493 {
2494   GtkRange *range = GTK_RANGE (widget);
2495
2496   range->need_recalc = TRUE;
2497
2498   GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
2499 }
2500
2501 static void
2502 apply_marks (GtkRange *range, 
2503              gdouble   oldval,
2504              gdouble  *newval)
2505 {
2506   gint i;
2507   gdouble mark;
2508
2509   for (i = 0; i < range->layout->n_marks; i++)
2510     {
2511       mark = range->layout->marks[i];
2512       if ((oldval < mark && mark < *newval) ||
2513           (oldval > mark && mark > *newval))
2514         {
2515           *newval = mark;
2516           return;
2517         }
2518     }
2519 }
2520
2521 static void
2522 step_back (GtkRange *range)
2523 {
2524   gdouble newval;
2525   gboolean handled;
2526   
2527   newval = range->adjustment->value - range->adjustment->step_increment;
2528   apply_marks (range, range->adjustment->value, &newval);
2529   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2530                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2531 }
2532
2533 static void
2534 step_forward (GtkRange *range)
2535 {
2536   gdouble newval;
2537   gboolean handled;
2538
2539   newval = range->adjustment->value + range->adjustment->step_increment;
2540   apply_marks (range, range->adjustment->value, &newval);
2541   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2542                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
2543 }
2544
2545
2546 static void
2547 page_back (GtkRange *range)
2548 {
2549   gdouble newval;
2550   gboolean handled;
2551
2552   newval = range->adjustment->value - range->adjustment->page_increment;
2553   apply_marks (range, range->adjustment->value, &newval);
2554   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2555                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2556 }
2557
2558 static void
2559 page_forward (GtkRange *range)
2560 {
2561   gdouble newval;
2562   gboolean handled;
2563
2564   newval = range->adjustment->value + range->adjustment->page_increment;
2565   apply_marks (range, range->adjustment->value, &newval);
2566   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2567                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2568 }
2569
2570 static void
2571 scroll_begin (GtkRange *range)
2572 {
2573   gboolean handled;
2574   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2575                  GTK_SCROLL_START, range->adjustment->lower,
2576                  &handled);
2577 }
2578
2579 static void
2580 scroll_end (GtkRange *range)
2581 {
2582   gdouble newval;
2583   gboolean handled;
2584
2585   newval = range->adjustment->upper - range->adjustment->page_size;
2586   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2587                  &handled);
2588 }
2589
2590 static gboolean
2591 gtk_range_scroll (GtkRange     *range,
2592                   GtkScrollType scroll)
2593 {
2594   gdouble old_value = range->adjustment->value;
2595
2596   switch (scroll)
2597     {
2598     case GTK_SCROLL_STEP_LEFT:
2599       if (should_invert (range))
2600         step_forward (range);
2601       else
2602         step_back (range);
2603       break;
2604                     
2605     case GTK_SCROLL_STEP_UP:
2606       if (should_invert (range))
2607         step_forward (range);
2608       else
2609         step_back (range);
2610       break;
2611
2612     case GTK_SCROLL_STEP_RIGHT:
2613       if (should_invert (range))
2614         step_back (range);
2615       else
2616         step_forward (range);
2617       break;
2618                     
2619     case GTK_SCROLL_STEP_DOWN:
2620       if (should_invert (range))
2621         step_back (range);
2622       else
2623         step_forward (range);
2624       break;
2625                   
2626     case GTK_SCROLL_STEP_BACKWARD:
2627       step_back (range);
2628       break;
2629                   
2630     case GTK_SCROLL_STEP_FORWARD:
2631       step_forward (range);
2632       break;
2633
2634     case GTK_SCROLL_PAGE_LEFT:
2635       if (should_invert (range))
2636         page_forward (range);
2637       else
2638         page_back (range);
2639       break;
2640                     
2641     case GTK_SCROLL_PAGE_UP:
2642       if (should_invert (range))
2643         page_forward (range);
2644       else
2645         page_back (range);
2646       break;
2647
2648     case GTK_SCROLL_PAGE_RIGHT:
2649       if (should_invert (range))
2650         page_back (range);
2651       else
2652         page_forward (range);
2653       break;
2654                     
2655     case GTK_SCROLL_PAGE_DOWN:
2656       if (should_invert (range))
2657         page_back (range);
2658       else
2659         page_forward (range);
2660       break;
2661                   
2662     case GTK_SCROLL_PAGE_BACKWARD:
2663       page_back (range);
2664       break;
2665                   
2666     case GTK_SCROLL_PAGE_FORWARD:
2667       page_forward (range);
2668       break;
2669
2670     case GTK_SCROLL_START:
2671       scroll_begin (range);
2672       break;
2673
2674     case GTK_SCROLL_END:
2675       scroll_end (range);
2676       break;
2677
2678     case GTK_SCROLL_JUMP:
2679       /* Used by CList, range doesn't use it. */
2680       break;
2681
2682     case GTK_SCROLL_NONE:
2683       break;
2684     }
2685
2686   return range->adjustment->value != old_value;
2687 }
2688
2689 static void
2690 gtk_range_move_slider (GtkRange     *range,
2691                        GtkScrollType scroll)
2692 {
2693   gboolean cursor_only;
2694
2695   g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2696                 "gtk-keynav-cursor-only", &cursor_only,
2697                 NULL);
2698
2699   if (cursor_only)
2700     {
2701       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2702
2703       if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2704         {
2705           if (scroll == GTK_SCROLL_STEP_UP ||
2706               scroll == GTK_SCROLL_STEP_DOWN)
2707             {
2708               if (toplevel)
2709                 gtk_widget_child_focus (toplevel,
2710                                         scroll == GTK_SCROLL_STEP_UP ?
2711                                         GTK_DIR_UP : GTK_DIR_DOWN);
2712               return;
2713             }
2714         }
2715       else
2716         {
2717           if (scroll == GTK_SCROLL_STEP_LEFT ||
2718               scroll == GTK_SCROLL_STEP_RIGHT)
2719             {
2720               if (toplevel)
2721                 gtk_widget_child_focus (toplevel,
2722                                         scroll == GTK_SCROLL_STEP_LEFT ?
2723                                         GTK_DIR_LEFT : GTK_DIR_RIGHT);
2724               return;
2725             }
2726         }
2727     }
2728
2729   if (! gtk_range_scroll (range, scroll))
2730     gtk_widget_error_bell (GTK_WIDGET (range));
2731
2732   /* Policy DELAYED makes sense with key events,
2733    * but DISCONTINUOUS doesn't, so we update immediately
2734    * for DISCONTINUOUS
2735    */
2736   if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2737     gtk_range_update_value (range);
2738 }
2739
2740 static void
2741 gtk_range_get_props (GtkRange  *range,
2742                      gint      *slider_width,
2743                      gint      *stepper_size,
2744                      gint      *focus_width,
2745                      gint      *trough_border,
2746                      gint      *stepper_spacing,
2747                      gboolean  *trough_under_steppers,
2748                      gint      *arrow_displacement_x,
2749                      gint      *arrow_displacement_y)
2750 {
2751   GtkWidget *widget =  GTK_WIDGET (range);
2752   gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
2753   gint tmp_stepper_spacing, tmp_trough_under_steppers;
2754   gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2755   
2756   gtk_widget_style_get (widget,
2757                         "slider-width", &tmp_slider_width,
2758                         "trough-border", &tmp_trough_border,
2759                         "stepper-size", &tmp_stepper_size,
2760                         "stepper-spacing", &tmp_stepper_spacing,
2761                         "trough-under-steppers", &tmp_trough_under_steppers,
2762                         "arrow-displacement-x", &tmp_arrow_displacement_x,
2763                         "arrow-displacement-y", &tmp_arrow_displacement_y,
2764                         NULL);
2765
2766   if (tmp_stepper_spacing > 0)
2767     tmp_trough_under_steppers = FALSE;
2768
2769   if (GTK_WIDGET_CAN_FOCUS (range))
2770     {
2771       gint focus_line_width;
2772       gint focus_padding;
2773       
2774       gtk_widget_style_get (GTK_WIDGET (range),
2775                             "focus-line-width", &focus_line_width,
2776                             "focus-padding", &focus_padding,
2777                             NULL);
2778
2779       tmp_focus_width = focus_line_width + focus_padding;
2780     }
2781   else
2782     {
2783       tmp_focus_width = 0;
2784     }
2785   
2786   if (slider_width)
2787     *slider_width = tmp_slider_width;
2788
2789   if (focus_width)
2790     *focus_width = tmp_focus_width;
2791
2792   if (trough_border)
2793     *trough_border = tmp_trough_border;
2794
2795   if (stepper_size)
2796     *stepper_size = tmp_stepper_size;
2797
2798   if (stepper_spacing)
2799     *stepper_spacing = tmp_stepper_spacing;
2800
2801   if (trough_under_steppers)
2802     *trough_under_steppers = tmp_trough_under_steppers;
2803
2804   if (arrow_displacement_x)
2805     *arrow_displacement_x = tmp_arrow_displacement_x;
2806
2807   if (arrow_displacement_y)
2808     *arrow_displacement_y = tmp_arrow_displacement_y;
2809 }
2810
2811 #define POINT_IN_RECT(xcoord, ycoord, rect) \
2812  ((xcoord) >= (rect).x &&                   \
2813   (xcoord) <  ((rect).x + (rect).width) &&  \
2814   (ycoord) >= (rect).y &&                   \
2815   (ycoord) <  ((rect).y + (rect).height))
2816
2817 /* Update mouse location, return TRUE if it changes */
2818 static gboolean
2819 gtk_range_update_mouse_location (GtkRange *range)
2820 {
2821   gint x, y;
2822   MouseLocation old;
2823   GtkWidget *widget;
2824
2825   widget = GTK_WIDGET (range);
2826   
2827   old = range->layout->mouse_location;
2828   
2829   x = range->layout->mouse_x;
2830   y = range->layout->mouse_y;
2831
2832   if (range->layout->grab_location != MOUSE_OUTSIDE)
2833     range->layout->mouse_location = range->layout->grab_location;
2834   else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
2835     range->layout->mouse_location = MOUSE_STEPPER_A;
2836   else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
2837     range->layout->mouse_location = MOUSE_STEPPER_B;
2838   else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
2839     range->layout->mouse_location = MOUSE_STEPPER_C;
2840   else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
2841     range->layout->mouse_location = MOUSE_STEPPER_D;
2842   else if (POINT_IN_RECT (x, y, range->layout->slider))
2843     range->layout->mouse_location = MOUSE_SLIDER;
2844   else if (POINT_IN_RECT (x, y, range->layout->trough))
2845     range->layout->mouse_location = MOUSE_TROUGH;
2846   else if (POINT_IN_RECT (x, y, widget->allocation))
2847     range->layout->mouse_location = MOUSE_WIDGET;
2848   else
2849     range->layout->mouse_location = MOUSE_OUTSIDE;
2850
2851   return old != range->layout->mouse_location;
2852 }
2853
2854 /* Clamp rect, border inside widget->allocation, such that we prefer
2855  * to take space from border not rect in all directions, and prefer to
2856  * give space to border over rect in one direction.
2857  */
2858 static void
2859 clamp_dimensions (GtkWidget    *widget,
2860                   GdkRectangle *rect,
2861                   GtkBorder    *border,
2862                   gboolean      border_expands_horizontally)
2863 {
2864   gint extra, shortage;
2865   
2866   g_return_if_fail (rect->x == 0);
2867   g_return_if_fail (rect->y == 0);  
2868   g_return_if_fail (rect->width >= 0);
2869   g_return_if_fail (rect->height >= 0);
2870
2871   /* Width */
2872   
2873   extra = widget->allocation.width - border->left - border->right - rect->width;
2874   if (extra > 0)
2875     {
2876       if (border_expands_horizontally)
2877         {
2878           border->left += extra / 2;
2879           border->right += extra / 2 + extra % 2;
2880         }
2881       else
2882         {
2883           rect->width += extra;
2884         }
2885     }
2886   
2887   /* See if we can fit rect, if not kill the border */
2888   shortage = rect->width - widget->allocation.width;
2889   if (shortage > 0)
2890     {
2891       rect->width = widget->allocation.width;
2892       /* lose the border */
2893       border->left = 0;
2894       border->right = 0;
2895     }
2896   else
2897     {
2898       /* See if we can fit rect with borders */
2899       shortage = rect->width + border->left + border->right -
2900         widget->allocation.width;
2901       if (shortage > 0)
2902         {
2903           /* Shrink borders */
2904           border->left -= shortage / 2;
2905           border->right -= shortage / 2 + shortage % 2;
2906         }
2907     }
2908
2909   /* Height */
2910   
2911   extra = widget->allocation.height - border->top - border->bottom - rect->height;
2912   if (extra > 0)
2913     {
2914       if (border_expands_horizontally)
2915         {
2916           /* don't expand border vertically */
2917           rect->height += extra;
2918         }
2919       else
2920         {
2921           border->top += extra / 2;
2922           border->bottom += extra / 2 + extra % 2;
2923         }
2924     }
2925   
2926   /* See if we can fit rect, if not kill the border */
2927   shortage = rect->height - widget->allocation.height;
2928   if (shortage > 0)
2929     {
2930       rect->height = widget->allocation.height;
2931       /* lose the border */
2932       border->top = 0;
2933       border->bottom = 0;
2934     }
2935   else
2936     {
2937       /* See if we can fit rect with borders */
2938       shortage = rect->height + border->top + border->bottom -
2939         widget->allocation.height;
2940       if (shortage > 0)
2941         {
2942           /* Shrink borders */
2943           border->top -= shortage / 2;
2944           border->bottom -= shortage / 2 + shortage % 2;
2945         }
2946     }
2947 }
2948
2949 static void
2950 gtk_range_calc_request (GtkRange      *range,
2951                         gint           slider_width,
2952                         gint           stepper_size,
2953                         gint           focus_width,
2954                         gint           trough_border,
2955                         gint           stepper_spacing,
2956                         GdkRectangle  *range_rect,
2957                         GtkBorder     *border,
2958                         gint          *n_steppers_p,
2959                         gboolean      *has_steppers_ab,
2960                         gboolean      *has_steppers_cd,
2961                         gint          *slider_length_p)
2962 {
2963   gint slider_length;
2964   gint n_steppers;
2965   gint n_steppers_ab;
2966   gint n_steppers_cd;
2967
2968   border->left = 0;
2969   border->right = 0;
2970   border->top = 0;
2971   border->bottom = 0;
2972
2973   if (GTK_RANGE_GET_CLASS (range)->get_range_border)
2974     GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
2975
2976   n_steppers_ab = 0;
2977   n_steppers_cd = 0;
2978
2979   if (range->has_stepper_a)
2980     n_steppers_ab += 1;
2981   if (range->has_stepper_b)
2982     n_steppers_ab += 1;
2983   if (range->has_stepper_c)
2984     n_steppers_cd += 1;
2985   if (range->has_stepper_d)
2986     n_steppers_cd += 1;
2987
2988   n_steppers = n_steppers_ab + n_steppers_cd;
2989
2990   slider_length = range->min_slider_size;
2991
2992   range_rect->x = 0;
2993   range_rect->y = 0;
2994   
2995   /* We never expand to fill available space in the small dimension
2996    * (i.e. vertical scrollbars are always a fixed width)
2997    */
2998   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2999     {
3000       range_rect->width = (focus_width + trough_border) * 2 + slider_width;
3001       range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3002
3003       if (n_steppers_ab > 0)
3004         range_rect->height += stepper_spacing;
3005
3006       if (n_steppers_cd > 0)
3007         range_rect->height += stepper_spacing;
3008     }
3009   else
3010     {
3011       range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3012       range_rect->height = (focus_width + trough_border) * 2 + slider_width;
3013
3014       if (n_steppers_ab > 0)
3015         range_rect->width += stepper_spacing;
3016
3017       if (n_steppers_cd > 0)
3018         range_rect->width += stepper_spacing;
3019     }
3020
3021   if (n_steppers_p)
3022     *n_steppers_p = n_steppers;
3023
3024   if (has_steppers_ab)
3025     *has_steppers_ab = (n_steppers_ab > 0);
3026
3027   if (has_steppers_cd)
3028     *has_steppers_cd = (n_steppers_cd > 0);
3029
3030   if (slider_length_p)
3031     *slider_length_p = slider_length;
3032 }
3033
3034 static void
3035 gtk_range_calc_layout (GtkRange *range,
3036                        gdouble   adjustment_value)
3037 {
3038   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
3039   gint slider_length;
3040   GtkBorder border;
3041   gint n_steppers;
3042   gboolean has_steppers_ab;
3043   gboolean has_steppers_cd;
3044   gboolean trough_under_steppers;
3045   GdkRectangle range_rect;
3046   GtkRangeLayout *layout;
3047   GtkWidget *widget;
3048   
3049   if (!range->need_recalc)
3050     return;
3051
3052   /* If we have a too-small allocation, we prefer the steppers over
3053    * the trough/slider, probably the steppers are a more useful
3054    * feature in small spaces.
3055    *
3056    * Also, we prefer to draw the range itself rather than the border
3057    * areas if there's a conflict, since the borders will be decoration
3058    * not controls. Though this depends on subclasses cooperating by
3059    * not drawing on range->range_rect.
3060    */
3061
3062   widget = GTK_WIDGET (range);
3063   layout = range->layout;
3064   
3065   gtk_range_get_props (range,
3066                        &slider_width, &stepper_size,
3067                        &focus_width, &trough_border,
3068                        &stepper_spacing, &trough_under_steppers,
3069                        NULL, NULL);
3070
3071   gtk_range_calc_request (range, 
3072                           slider_width, stepper_size,
3073                           focus_width, trough_border, stepper_spacing,
3074                           &range_rect, &border, &n_steppers,
3075                           &has_steppers_ab, &has_steppers_cd, &slider_length);
3076   
3077   /* We never expand to fill available space in the small dimension
3078    * (i.e. vertical scrollbars are always a fixed width)
3079    */
3080   if (range->orientation == GTK_ORIENTATION_VERTICAL)
3081     {
3082       clamp_dimensions (widget, &range_rect, &border, TRUE);
3083     }
3084   else
3085     {
3086       clamp_dimensions (widget, &range_rect, &border, FALSE);
3087     }
3088   
3089   range_rect.x = border.left;
3090   range_rect.y = border.top;
3091   
3092   range->range_rect = range_rect;
3093   
3094   if (range->orientation == GTK_ORIENTATION_VERTICAL)
3095     {
3096       gint stepper_width, stepper_height;
3097
3098       /* Steppers are the width of the range, and stepper_size in
3099        * height, or if we don't have enough height, divided equally
3100        * among available space.
3101        */
3102       stepper_width = range_rect.width - focus_width * 2;
3103
3104       if (trough_under_steppers)
3105         stepper_width -= trough_border * 2;
3106
3107       if (stepper_width < 1)
3108         stepper_width = range_rect.width; /* screw the trough border */
3109
3110       if (n_steppers == 0)
3111         stepper_height = 0; /* avoid divide by n_steppers */
3112       else
3113         stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
3114
3115       /* Stepper A */
3116       
3117       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3118       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3119
3120       if (range->has_stepper_a)
3121         {
3122           layout->stepper_a.width = stepper_width;
3123           layout->stepper_a.height = stepper_height;
3124         }
3125       else
3126         {
3127           layout->stepper_a.width = 0;
3128           layout->stepper_a.height = 0;
3129         }
3130
3131       /* Stepper B */
3132       
3133       layout->stepper_b.x = layout->stepper_a.x;
3134       layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
3135
3136       if (range->has_stepper_b)
3137         {
3138           layout->stepper_b.width = stepper_width;
3139           layout->stepper_b.height = stepper_height;
3140         }
3141       else
3142         {
3143           layout->stepper_b.width = 0;
3144           layout->stepper_b.height = 0;
3145         }
3146
3147       /* Stepper D */
3148
3149       if (range->has_stepper_d)
3150         {
3151           layout->stepper_d.width = stepper_width;
3152           layout->stepper_d.height = stepper_height;
3153         }
3154       else
3155         {
3156           layout->stepper_d.width = 0;
3157           layout->stepper_d.height = 0;
3158         }
3159       
3160       layout->stepper_d.x = layout->stepper_a.x;
3161       layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
3162
3163       /* Stepper C */
3164
3165       if (range->has_stepper_c)
3166         {
3167           layout->stepper_c.width = stepper_width;
3168           layout->stepper_c.height = stepper_height;
3169         }
3170       else
3171         {
3172           layout->stepper_c.width = 0;
3173           layout->stepper_c.height = 0;
3174         }
3175       
3176       layout->stepper_c.x = layout->stepper_a.x;
3177       layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
3178
3179       /* Now the trough is the remaining space between steppers B and C,
3180        * if any, minus spacing
3181        */
3182       layout->trough.x = range_rect.x;
3183       layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
3184       layout->trough.width = range_rect.width;
3185       layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
3186
3187       /* Slider fits into the trough, with stepper_spacing on either side,
3188        * and the size/position based on the adjustment or fixed, depending.
3189        */
3190       layout->slider.x = layout->trough.x + focus_width + trough_border;
3191       layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
3192
3193       /* Compute slider position/length */
3194       {
3195         gint y, bottom, top, height;
3196         
3197         top = layout->trough.y;
3198         bottom = layout->trough.y + layout->trough.height;
3199
3200         if (! trough_under_steppers)
3201           {
3202             top += trough_border;
3203             bottom -= trough_border;
3204           }
3205
3206         /* slider height is the fraction (page_size /
3207          * total_adjustment_range) times the trough height in pixels
3208          */
3209
3210         if (range->adjustment->upper - range->adjustment->lower != 0)
3211           height = ((bottom - top) * (range->adjustment->page_size /
3212                                        (range->adjustment->upper - range->adjustment->lower)));
3213         else
3214           height = range->min_slider_size;
3215         
3216         if (height < range->min_slider_size ||
3217             range->slider_size_fixed)
3218           height = range->min_slider_size;
3219
3220         height = MIN (height, layout->trough.height);
3221         
3222         y = top;
3223         
3224         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3225           y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
3226                                           (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3227         
3228         y = CLAMP (y, top, bottom);
3229         
3230         if (should_invert (range))
3231           y = bottom - (y - top + height);
3232         
3233         layout->slider.y = y;
3234         layout->slider.height = height;
3235
3236         /* These are publically exported */
3237         range->slider_start = layout->slider.y;
3238         range->slider_end = layout->slider.y + layout->slider.height;
3239       }
3240     }
3241   else
3242     {
3243       gint stepper_width, stepper_height;
3244
3245       /* Steppers are the height of the range, and stepper_size in
3246        * width, or if we don't have enough width, divided equally
3247        * among available space.
3248        */
3249       stepper_height = range_rect.height + focus_width * 2;
3250
3251       if (trough_under_steppers)
3252         stepper_height -= trough_border * 2;
3253
3254       if (stepper_height < 1)
3255         stepper_height = range_rect.height; /* screw the trough border */
3256
3257       if (n_steppers == 0)
3258         stepper_width = 0; /* avoid divide by n_steppers */
3259       else
3260         stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3261
3262       /* Stepper A */
3263       
3264       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3265       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3266
3267       if (range->has_stepper_a)
3268         {
3269           layout->stepper_a.width = stepper_width;
3270           layout->stepper_a.height = stepper_height;
3271         }
3272       else
3273         {
3274           layout->stepper_a.width = 0;
3275           layout->stepper_a.height = 0;
3276         }
3277
3278       /* Stepper B */
3279       
3280       layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3281       layout->stepper_b.y = layout->stepper_a.y;
3282
3283       if (range->has_stepper_b)
3284         {
3285           layout->stepper_b.width = stepper_width;
3286           layout->stepper_b.height = stepper_height;
3287         }
3288       else
3289         {
3290           layout->stepper_b.width = 0;
3291           layout->stepper_b.height = 0;
3292         }
3293
3294       /* Stepper D */
3295
3296       if (range->has_stepper_d)
3297         {
3298           layout->stepper_d.width = stepper_width;
3299           layout->stepper_d.height = stepper_height;
3300         }
3301       else
3302         {
3303           layout->stepper_d.width = 0;
3304           layout->stepper_d.height = 0;
3305         }
3306
3307       layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3308       layout->stepper_d.y = layout->stepper_a.y;
3309
3310
3311       /* Stepper C */
3312
3313       if (range->has_stepper_c)
3314         {
3315           layout->stepper_c.width = stepper_width;
3316           layout->stepper_c.height = stepper_height;
3317         }
3318       else
3319         {
3320           layout->stepper_c.width = 0;
3321           layout->stepper_c.height = 0;
3322         }
3323       
3324       layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3325       layout->stepper_c.y = layout->stepper_a.y;
3326
3327       /* Now the trough is the remaining space between steppers B and C,
3328        * if any
3329        */
3330       layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3331       layout->trough.y = range_rect.y;
3332
3333       layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3334       layout->trough.height = range_rect.height;
3335
3336       /* Slider fits into the trough, with stepper_spacing on either side,
3337        * and the size/position based on the adjustment or fixed, depending.
3338        */
3339       layout->slider.y = layout->trough.y + focus_width + trough_border;
3340       layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3341
3342       /* Compute slider position/length */
3343       {
3344         gint x, left, right, width;
3345         
3346         left = layout->trough.x;
3347         right = layout->trough.x + layout->trough.width;
3348
3349         if (! trough_under_steppers)
3350           {
3351             left += trough_border;
3352             right -= trough_border;
3353           }
3354
3355         /* slider width is the fraction (page_size /
3356          * total_adjustment_range) times the trough width in pixels
3357          */
3358         
3359         if (range->adjustment->upper - range->adjustment->lower != 0)
3360           width = ((right - left) * (range->adjustment->page_size /
3361                                    (range->adjustment->upper - range->adjustment->lower)));
3362         else
3363           width = range->min_slider_size;
3364         
3365         if (width < range->min_slider_size ||
3366             range->slider_size_fixed)
3367           width = range->min_slider_size;
3368         
3369         width = MIN (width, layout->trough.width);
3370         
3371         x = left;
3372         
3373         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3374           x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3375                                          (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3376         
3377         x = CLAMP (x, left, right);
3378         
3379         if (should_invert (range))
3380           x = right - (x - left + width);
3381         
3382         layout->slider.x = x;
3383         layout->slider.width = width;
3384
3385         /* These are publically exported */
3386         range->slider_start = layout->slider.x;
3387         range->slider_end = layout->slider.x + layout->slider.width;
3388       }
3389     }
3390   
3391   gtk_range_update_mouse_location (range);
3392
3393   switch (range->layout->upper_sensitivity)
3394     {
3395     case GTK_SENSITIVITY_AUTO:
3396       range->layout->upper_sensitive =
3397         (range->adjustment->value <
3398          (range->adjustment->upper - range->adjustment->page_size));
3399       break;
3400
3401     case GTK_SENSITIVITY_ON:
3402       range->layout->upper_sensitive = TRUE;
3403       break;
3404
3405     case GTK_SENSITIVITY_OFF:
3406       range->layout->upper_sensitive = FALSE;
3407       break;
3408     }
3409
3410   switch (range->layout->lower_sensitivity)
3411     {
3412     case GTK_SENSITIVITY_AUTO:
3413       range->layout->lower_sensitive =
3414         (range->adjustment->value > range->adjustment->lower);
3415       break;
3416
3417     case GTK_SENSITIVITY_ON:
3418       range->layout->lower_sensitive = TRUE;
3419       break;
3420
3421     case GTK_SENSITIVITY_OFF:
3422       range->layout->lower_sensitive = FALSE;
3423       break;
3424     }
3425 }
3426
3427 static GdkRectangle*
3428 get_area (GtkRange     *range,
3429           MouseLocation location)
3430 {
3431   switch (location)
3432     {
3433     case MOUSE_STEPPER_A:
3434       return &range->layout->stepper_a;
3435     case MOUSE_STEPPER_B:
3436       return &range->layout->stepper_b;
3437     case MOUSE_STEPPER_C:
3438       return &range->layout->stepper_c;
3439     case MOUSE_STEPPER_D:
3440       return &range->layout->stepper_d;
3441     case MOUSE_TROUGH:
3442       return &range->layout->trough;
3443     case MOUSE_SLIDER:
3444       return &range->layout->slider;
3445     case MOUSE_WIDGET:
3446     case MOUSE_OUTSIDE:
3447       break;
3448     }
3449
3450   g_warning (G_STRLOC": bug");
3451   return NULL;
3452 }
3453
3454 static void
3455 gtk_range_calc_marks (GtkRange *range)
3456 {
3457   gint i;
3458   
3459   if (!range->layout->recalc_marks)
3460     return;
3461
3462   range->layout->recalc_marks = FALSE;
3463
3464   for (i = 0; i < range->layout->n_marks; i++)
3465     {
3466       range->need_recalc = TRUE;
3467       gtk_range_calc_layout (range, range->layout->marks[i]);
3468       if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
3469         range->layout->mark_pos[i] = range->layout->slider.x + range->layout->slider.width / 2;
3470       else
3471         range->layout->mark_pos[i] = range->layout->slider.y + range->layout->slider.height / 2;
3472     }
3473
3474   range->need_recalc = TRUE;
3475 }
3476
3477 static gboolean
3478 gtk_range_real_change_value (GtkRange     *range,
3479                              GtkScrollType scroll,
3480                              gdouble       value)
3481 {
3482   /* potentially adjust the bounds _before we clamp */
3483   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3484
3485   if (range->layout->restrict_to_fill_level)
3486     value = MIN (value, MAX (range->adjustment->lower,
3487                              range->layout->fill_level));
3488
3489   value = CLAMP (value, range->adjustment->lower,
3490                  (range->adjustment->upper - range->adjustment->page_size));
3491
3492   if (range->round_digits >= 0)
3493     {
3494       gdouble power;
3495       gint i;
3496
3497       i = range->round_digits;
3498       power = 1;
3499       while (i--)
3500         power *= 10;
3501       
3502       value = floor ((value * power) + 0.5) / power;
3503     }
3504   
3505   if (range->adjustment->value != value)
3506     {
3507       range->need_recalc = TRUE;
3508
3509       gtk_widget_queue_draw (GTK_WIDGET (range));
3510       
3511       switch (range->update_policy)
3512         {
3513         case GTK_UPDATE_CONTINUOUS:
3514           gtk_adjustment_set_value (range->adjustment, value);
3515           break;
3516
3517           /* Delayed means we update after a period of inactivity */
3518         case GTK_UPDATE_DELAYED:
3519           gtk_range_reset_update_timer (range);
3520           /* FALL THRU */
3521
3522           /* Discontinuous means we update on button release */
3523         case GTK_UPDATE_DISCONTINUOUS:
3524           /* don't emit value_changed signal */
3525           range->adjustment->value = value;
3526           range->update_pending = TRUE;
3527           break;
3528         }
3529     }
3530   return FALSE;
3531 }
3532
3533 static void
3534 gtk_range_update_value (GtkRange *range)
3535 {
3536   gtk_range_remove_update_timer (range);
3537   
3538   if (range->update_pending)
3539     {
3540       gtk_adjustment_value_changed (range->adjustment);
3541
3542       range->update_pending = FALSE;
3543     }
3544 }
3545
3546 struct _GtkRangeStepTimer
3547 {
3548   guint timeout_id;
3549   GtkScrollType step;
3550 };
3551
3552 static gboolean
3553 second_timeout (gpointer data)
3554 {
3555   GtkRange *range;
3556
3557   range = GTK_RANGE (data);
3558   gtk_range_scroll (range, range->timer->step);
3559   
3560   return TRUE;
3561 }
3562
3563 static gboolean
3564 initial_timeout (gpointer data)
3565 {
3566   GtkRange    *range;
3567   GtkSettings *settings;
3568   guint        timeout;
3569
3570   settings = gtk_widget_get_settings (GTK_WIDGET (data));
3571   g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3572
3573   range = GTK_RANGE (data);
3574   range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3575                                             second_timeout,
3576                                             range);
3577   /* remove self */
3578   return FALSE;
3579 }
3580
3581 static void
3582 gtk_range_add_step_timer (GtkRange      *range,
3583                           GtkScrollType  step)
3584 {
3585   GtkSettings *settings;
3586   guint        timeout;
3587
3588   g_return_if_fail (range->timer == NULL);
3589   g_return_if_fail (step != GTK_SCROLL_NONE);
3590
3591   settings = gtk_widget_get_settings (GTK_WIDGET (range));
3592   g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3593
3594   range->timer = g_new (GtkRangeStepTimer, 1);
3595
3596   range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3597                                             initial_timeout,
3598                                             range);
3599   range->timer->step = step;
3600
3601   gtk_range_scroll (range, range->timer->step);
3602 }
3603
3604 static void
3605 gtk_range_remove_step_timer (GtkRange *range)
3606 {
3607   if (range->timer)
3608     {
3609       if (range->timer->timeout_id != 0)
3610         g_source_remove (range->timer->timeout_id);
3611
3612       g_free (range->timer);
3613
3614       range->timer = NULL;
3615     }
3616 }
3617
3618 static gboolean
3619 update_timeout (gpointer data)
3620 {
3621   GtkRange *range;
3622
3623   range = GTK_RANGE (data);
3624   gtk_range_update_value (range);
3625   range->update_timeout_id = 0;
3626
3627   /* self-remove */
3628   return FALSE;
3629 }
3630
3631 static void
3632 gtk_range_reset_update_timer (GtkRange *range)
3633 {
3634   gtk_range_remove_update_timer (range);
3635
3636   range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3637                                             update_timeout,
3638                                             range);
3639 }
3640
3641 static void
3642 gtk_range_remove_update_timer (GtkRange *range)
3643 {
3644   if (range->update_timeout_id != 0)
3645     {
3646       g_source_remove (range->update_timeout_id);
3647       range->update_timeout_id = 0;
3648     }
3649 }
3650
3651 void
3652 _gtk_range_set_stop_values (GtkRange *range,
3653                             gdouble  *values,
3654                             gint      n_values)
3655 {
3656   gint i;
3657
3658   g_free (range->layout->marks);
3659   range->layout->marks = g_new (gdouble, n_values);
3660
3661   g_free (range->layout->mark_pos);
3662   range->layout->mark_pos = g_new (gint, n_values);
3663
3664   range->layout->n_marks = n_values;
3665
3666   for (i = 0; i < n_values; i++) 
3667     range->layout->marks[i] = values[i];
3668
3669   range->layout->recalc_marks = TRUE;
3670 }
3671
3672 gint
3673 _gtk_range_get_stop_positions (GtkRange  *range,
3674                                gint     **values)
3675 {
3676   gtk_range_calc_marks (range);
3677
3678   if (values)
3679     *values = g_memdup (range->layout->mark_pos, range->layout->n_marks * sizeof (gint));
3680
3681   return range->layout->n_marks;
3682 }
3683
3684 #define __GTK_RANGE_C__
3685 #include "gtkaliasdef.c"