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