]> Pileus Git - ~andy/gtk/blob - gtk/gtkrange.c
7fa75303a10bef5ee7e80489ba92efaaf318a276
[~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_VOID__VOID,
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   GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1298 }
1299
1300 static void
1301 gtk_range_map (GtkWidget *widget)
1302 {
1303   GtkRange *range = GTK_RANGE (widget);
1304   
1305   gdk_window_show (range->event_window);
1306
1307   GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1308 }
1309
1310 static void
1311 gtk_range_unmap (GtkWidget *widget)
1312 {
1313   GtkRange *range = GTK_RANGE (widget);
1314     
1315   stop_scrolling (range);
1316
1317   gdk_window_hide (range->event_window);
1318
1319   GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1320 }
1321
1322 static void
1323 draw_stepper (GtkRange     *range,
1324               GdkRectangle *rect,
1325               GtkArrowType  arrow_type,
1326               gboolean      clicked,
1327               gboolean      prelighted,
1328               GdkRectangle *area)
1329 {
1330   GtkStateType state_type;
1331   GtkShadowType shadow_type;
1332   GdkRectangle intersection;
1333   GtkWidget *widget = GTK_WIDGET (range);
1334   gfloat arrow_scaling;
1335
1336   gint arrow_x;
1337   gint arrow_y;
1338   gint arrow_width;
1339   gint arrow_height;
1340
1341   gboolean arrow_sensitive = TRUE;
1342
1343   /* More to get the right clip region than for efficiency */
1344   if (!gdk_rectangle_intersect (area, rect, &intersection))
1345     return;
1346
1347   intersection.x += widget->allocation.x;
1348   intersection.y += widget->allocation.y;
1349
1350   if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1351                             arrow_type == GTK_ARROW_RIGHT)) ||
1352       (range->inverted  && (arrow_type == GTK_ARROW_UP ||
1353                             arrow_type == GTK_ARROW_LEFT)))
1354     {
1355       arrow_sensitive = range->layout->upper_sensitive;
1356     }
1357   else
1358     {
1359       arrow_sensitive = range->layout->lower_sensitive;
1360     }
1361
1362   if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
1363     state_type = GTK_STATE_INSENSITIVE;
1364   else if (clicked)
1365     state_type = GTK_STATE_ACTIVE;
1366   else if (prelighted)
1367     state_type = GTK_STATE_PRELIGHT;
1368   else 
1369     state_type = GTK_STATE_NORMAL;
1370
1371   if (clicked && arrow_sensitive)
1372     shadow_type = GTK_SHADOW_IN;
1373   else
1374     shadow_type = GTK_SHADOW_OUT;
1375
1376   gtk_paint_box (widget->style,
1377                  widget->window,
1378                  state_type, shadow_type,
1379                  &intersection, widget,
1380                  GTK_RANGE_GET_CLASS (range)->stepper_detail,
1381                  widget->allocation.x + rect->x,
1382                  widget->allocation.y + rect->y,
1383                  rect->width,
1384                  rect->height);
1385
1386   gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1387
1388   arrow_width = rect->width * arrow_scaling;
1389   arrow_height = rect->height * arrow_scaling;
1390   arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1391   arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1392
1393   if (clicked && arrow_sensitive)
1394     {
1395       gint arrow_displacement_x;
1396       gint arrow_displacement_y;
1397
1398       gtk_range_get_props (GTK_RANGE (widget),
1399                            NULL, NULL, NULL, NULL, NULL, NULL,
1400                            &arrow_displacement_x, &arrow_displacement_y);
1401       
1402       arrow_x += arrow_displacement_x;
1403       arrow_y += arrow_displacement_y;
1404     }
1405   
1406   gtk_paint_arrow (widget->style,
1407                    widget->window,
1408                    state_type, shadow_type, 
1409                    &intersection, widget,
1410                    GTK_RANGE_GET_CLASS (range)->stepper_detail,
1411                    arrow_type,
1412                    TRUE,
1413                    arrow_x, arrow_y, arrow_width, arrow_height);
1414 }
1415
1416 static gboolean
1417 gtk_range_expose (GtkWidget      *widget,
1418                   GdkEventExpose *event)
1419 {
1420   GtkRange *range = GTK_RANGE (widget);
1421   gboolean sensitive;
1422   GtkStateType state;
1423   GtkShadowType shadow_type;
1424   GdkRectangle expose_area;     /* Relative to widget->allocation */
1425   GdkRectangle area;
1426   gint focus_line_width = 0;
1427   gint focus_padding = 0;
1428   gboolean touchscreen;
1429
1430   g_object_get (gtk_widget_get_settings (widget),
1431                 "gtk-touchscreen-mode", &touchscreen,
1432                 NULL);
1433   if (GTK_WIDGET_CAN_FOCUS (range))
1434     gtk_widget_style_get (GTK_WIDGET (range),
1435                           "focus-line-width", &focus_line_width,
1436                           "focus-padding", &focus_padding,
1437                           NULL);
1438
1439   /* we're now exposing, so there's no need to force early repaints */
1440   if (range->layout->repaint_id)
1441     g_source_remove (range->layout->repaint_id);
1442   range->layout->repaint_id = 0;
1443
1444   expose_area = event->area;
1445   expose_area.x -= widget->allocation.x;
1446   expose_area.y -= widget->allocation.y;
1447   
1448   gtk_range_calc_layout (range, range->adjustment->value);
1449
1450   sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
1451
1452   /* Just to be confusing, we draw the trough for the whole
1453    * range rectangle, not the trough rectangle (the trough
1454    * rectangle is just for hit detection)
1455    */
1456   /* The gdk_rectangle_intersect is more to get the right
1457    * clip region (limited to range_rect) than for efficiency
1458    */
1459   if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1460                                &area))
1461     {
1462       gint     x      = (widget->allocation.x + range->range_rect.x +
1463                          focus_line_width + focus_padding);
1464       gint     y      = (widget->allocation.y + range->range_rect.y +
1465                          focus_line_width + focus_padding);
1466       gint     width  = (range->range_rect.width -
1467                          2 * (focus_line_width + focus_padding));
1468       gint     height = (range->range_rect.height -
1469                          2 * (focus_line_width + focus_padding));
1470       gboolean trough_side_details;
1471       gboolean trough_under_steppers;
1472       gint     stepper_size;
1473       gint     stepper_spacing;
1474
1475       area.x += widget->allocation.x;
1476       area.y += widget->allocation.y;
1477
1478       gtk_widget_style_get (GTK_WIDGET (range),
1479                             "trough-side-details",   &trough_side_details,
1480                             "trough-under-steppers", &trough_under_steppers,
1481                             "stepper-size",          &stepper_size,
1482                             "stepper-spacing",       &stepper_spacing,
1483                             NULL);
1484
1485       if (stepper_spacing > 0)
1486         trough_under_steppers = FALSE;
1487
1488       if (! trough_under_steppers)
1489         {
1490           gint offset  = 0;
1491           gint shorter = 0;
1492
1493           if (range->has_stepper_a)
1494             offset += stepper_size;
1495
1496           if (range->has_stepper_b)
1497             offset += stepper_size;
1498
1499           shorter += offset;
1500
1501           if (range->has_stepper_c)
1502             shorter += stepper_size;
1503
1504           if (range->has_stepper_d)
1505             shorter += stepper_size;
1506
1507           if (range->has_stepper_a || range->has_stepper_b)
1508             {
1509               offset  += stepper_spacing;
1510               shorter += stepper_spacing;
1511             }
1512
1513           if (range->has_stepper_c || range->has_stepper_d)
1514             {
1515               shorter += stepper_spacing;
1516             }
1517
1518           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1519             {
1520               x     += offset;
1521               width -= shorter;
1522             }
1523           else
1524             {
1525               y      += offset;
1526               height -= shorter;
1527             }
1528         }
1529
1530       if (! trough_side_details)
1531         {
1532           gtk_paint_box (widget->style,
1533                          widget->window,
1534                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1535                          GTK_SHADOW_IN,
1536                          &area, GTK_WIDGET(range), "trough",
1537                          x, y,
1538                          width, height);
1539         }
1540       else
1541         {
1542           gint trough_change_pos_x = width;
1543           gint trough_change_pos_y = height;
1544
1545           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1546             trough_change_pos_x = (range->layout->slider.x +
1547                                    range->layout->slider.width / 2 -
1548                                    (x - widget->allocation.x));
1549           else
1550             trough_change_pos_y = (range->layout->slider.y +
1551                                    range->layout->slider.height / 2 -
1552                                    (y - widget->allocation.y));
1553
1554           gtk_paint_box (widget->style,
1555                          widget->window,
1556                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1557                          GTK_SHADOW_IN,
1558                          &area, GTK_WIDGET (range),
1559                          should_invert (range) ? "trough-upper" : "trough-lower",
1560                          x, y,
1561                          trough_change_pos_x, trough_change_pos_y);
1562
1563           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1564             trough_change_pos_y = 0;
1565           else
1566             trough_change_pos_x = 0;
1567
1568           gtk_paint_box (widget->style,
1569                          widget->window,
1570                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1571                          GTK_SHADOW_IN,
1572                          &area, GTK_WIDGET (range),
1573                          should_invert (range) ? "trough-lower" : "trough-upper",
1574                          x + trough_change_pos_x, y + trough_change_pos_y,
1575                          width - trough_change_pos_x,
1576                          height - trough_change_pos_y);
1577         }
1578
1579       if (range->layout->show_fill_level &&
1580           range->adjustment->upper - range->adjustment->page_size -
1581           range->adjustment->lower != 0)
1582         {
1583           gdouble  fill_level  = range->layout->fill_level;
1584           gint     fill_x      = x;
1585           gint     fill_y      = y;
1586           gint     fill_width  = width;
1587           gint     fill_height = height;
1588           gchar   *fill_detail;
1589
1590           fill_level = CLAMP (fill_level, range->adjustment->lower,
1591                               range->adjustment->upper -
1592                               range->adjustment->page_size);
1593
1594           if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1595             {
1596               fill_x     = widget->allocation.x + range->layout->trough.x;
1597               fill_width = (range->layout->slider.width +
1598                             (fill_level - range->adjustment->lower) /
1599                             (range->adjustment->upper -
1600                              range->adjustment->lower -
1601                              range->adjustment->page_size) *
1602                             (range->layout->trough.width -
1603                              range->layout->slider.width));
1604
1605               if (should_invert (range))
1606                 fill_x += range->layout->trough.width - fill_width;
1607             }
1608           else
1609             {
1610               fill_y      = widget->allocation.y + range->layout->trough.y;
1611               fill_height = (range->layout->slider.height +
1612                              (fill_level - range->adjustment->lower) /
1613                              (range->adjustment->upper -
1614                               range->adjustment->lower -
1615                               range->adjustment->page_size) *
1616                              (range->layout->trough.height -
1617                               range->layout->slider.height));
1618
1619               if (should_invert (range))
1620                 fill_y += range->layout->trough.height - fill_height;
1621             }
1622
1623           if (fill_level < range->adjustment->upper - range->adjustment->page_size)
1624             fill_detail = "trough-fill-level-full";
1625           else
1626             fill_detail = "trough-fill-level";
1627
1628           gtk_paint_box (widget->style,
1629                          widget->window,
1630                          sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1631                          GTK_SHADOW_OUT,
1632                          &area, GTK_WIDGET (range), fill_detail,
1633                          fill_x, fill_y,
1634                          fill_width, fill_height);
1635         }
1636
1637       if (sensitive &&
1638           GTK_WIDGET_HAS_FOCUS (range))
1639         gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
1640                          &area, widget, "trough",
1641                          widget->allocation.x + range->range_rect.x,
1642                          widget->allocation.y + range->range_rect.y,
1643                          range->range_rect.width,
1644                          range->range_rect.height);
1645     }
1646
1647   shadow_type = GTK_SHADOW_OUT;
1648
1649   if (!sensitive)
1650     state = GTK_STATE_INSENSITIVE;
1651   else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1652     state = GTK_STATE_PRELIGHT;
1653   else
1654     state = GTK_STATE_NORMAL;
1655
1656   if (range->layout->grab_location == MOUSE_SLIDER)
1657     {
1658       gboolean activate_slider;
1659
1660       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
1661
1662       if (activate_slider)
1663         {
1664           state = GTK_STATE_ACTIVE;
1665           shadow_type = GTK_SHADOW_IN;
1666         }
1667     }
1668
1669   if (gdk_rectangle_intersect (&expose_area,
1670                                &range->layout->slider,
1671                                &area))
1672     {
1673       area.x += widget->allocation.x;
1674       area.y += widget->allocation.y;
1675       
1676       gtk_paint_slider (widget->style,
1677                         widget->window,
1678                         state,
1679                         shadow_type,
1680                         &area,
1681                         widget,
1682                         GTK_RANGE_GET_CLASS (range)->slider_detail,
1683                         widget->allocation.x + range->layout->slider.x,
1684                         widget->allocation.y + range->layout->slider.y,
1685                         range->layout->slider.width,
1686                         range->layout->slider.height,
1687                         range->orientation);
1688     }
1689   
1690   if (range->has_stepper_a)
1691     draw_stepper (range, &range->layout->stepper_a,
1692                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1693                   range->layout->grab_location == MOUSE_STEPPER_A,
1694                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
1695                   &expose_area);
1696
1697   if (range->has_stepper_b)
1698     draw_stepper (range, &range->layout->stepper_b,
1699                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1700                   range->layout->grab_location == MOUSE_STEPPER_B,
1701                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
1702                   &expose_area);
1703
1704   if (range->has_stepper_c)
1705     draw_stepper (range, &range->layout->stepper_c,
1706                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1707                   range->layout->grab_location == MOUSE_STEPPER_C,
1708                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
1709                   &expose_area);
1710
1711   if (range->has_stepper_d)
1712     draw_stepper (range, &range->layout->stepper_d,
1713                   range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1714                   range->layout->grab_location == MOUSE_STEPPER_D,
1715                   !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
1716                   &expose_area);
1717   
1718   return FALSE;
1719 }
1720
1721 static void
1722 range_grab_add (GtkRange      *range,
1723                 MouseLocation  location,
1724                 gint           button)
1725 {
1726   /* we don't actually gtk_grab, since a button is down */
1727
1728   gtk_grab_add (GTK_WIDGET (range));
1729   
1730   range->layout->grab_location = location;
1731   range->layout->grab_button = button;
1732   
1733   if (gtk_range_update_mouse_location (range))
1734     gtk_widget_queue_draw (GTK_WIDGET (range));
1735 }
1736
1737 static void
1738 range_grab_remove (GtkRange *range)
1739 {
1740   MouseLocation location;
1741
1742   gtk_grab_remove (GTK_WIDGET (range));
1743  
1744   location = range->layout->grab_location; 
1745   range->layout->grab_location = MOUSE_OUTSIDE;
1746   range->layout->grab_button = 0;
1747
1748   if (gtk_range_update_mouse_location (range) ||
1749       location != MOUSE_OUTSIDE)
1750     gtk_widget_queue_draw (GTK_WIDGET (range));
1751 }
1752
1753 static GtkScrollType
1754 range_get_scroll_for_grab (GtkRange      *range)
1755
1756   gboolean invert;
1757
1758   invert = should_invert (range);
1759   switch (range->layout->grab_location)
1760     {
1761       /* Backward stepper */
1762     case MOUSE_STEPPER_A:
1763     case MOUSE_STEPPER_C:
1764       switch (range->layout->grab_button)
1765         {
1766         case 1:
1767           return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
1768           break;
1769         case 2:
1770           return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
1771           break;
1772         case 3:
1773           return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
1774           break;
1775         }
1776       break;
1777
1778       /* Forward stepper */
1779     case MOUSE_STEPPER_B:
1780     case MOUSE_STEPPER_D:
1781       switch (range->layout->grab_button)
1782         {
1783         case 1:
1784           return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
1785           break;
1786         case 2:
1787           return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
1788           break;
1789         case 3:
1790           return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
1791           break;
1792        }
1793       break;
1794
1795       /* In the trough */
1796     case MOUSE_TROUGH:
1797       {
1798         if (range->trough_click_forward)
1799           return GTK_SCROLL_PAGE_FORWARD;
1800         else
1801           return GTK_SCROLL_PAGE_BACKWARD;
1802       }
1803       break;
1804
1805     case MOUSE_OUTSIDE:
1806     case MOUSE_SLIDER:
1807     case MOUSE_WIDGET:
1808       break;
1809     }
1810
1811   return GTK_SCROLL_NONE;
1812 }
1813
1814 static gdouble
1815 coord_to_value (GtkRange *range,
1816                 gint      coord)
1817 {
1818   gdouble frac;
1819   gdouble value;
1820   gint    trough_length;
1821   gint    trough_start;
1822   gint    slider_length;
1823   gint    trough_border;
1824   gint    trough_under_steppers;
1825
1826   if (range->orientation == GTK_ORIENTATION_VERTICAL)
1827     {
1828       trough_length = range->layout->trough.height;
1829       trough_start  = range->layout->trough.y;
1830       slider_length = range->layout->slider.height;
1831     }
1832   else
1833     {
1834       trough_length = range->layout->trough.width;
1835       trough_start  = range->layout->trough.x;
1836       slider_length = range->layout->slider.width;
1837     }
1838
1839   gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
1840                        &trough_under_steppers, NULL, NULL);
1841
1842   if (! trough_under_steppers)
1843     {
1844       trough_start += trough_border;
1845       trough_length -= 2 * trough_border;
1846     }
1847
1848   if (trough_length == slider_length)
1849     frac = 1.0;
1850   else
1851     frac = (MAX (0, coord - trough_start) /
1852             (gdouble) (trough_length - slider_length));
1853
1854   if (should_invert (range))
1855     frac = 1.0 - frac;
1856
1857   value = range->adjustment->lower + frac * (range->adjustment->upper -
1858                                              range->adjustment->lower -
1859                                              range->adjustment->page_size);
1860
1861   return value;
1862 }
1863
1864 static gboolean
1865 gtk_range_key_press (GtkWidget   *widget,
1866                      GdkEventKey *event)
1867 {
1868   GtkRange *range = GTK_RANGE (widget);
1869
1870   if (event->keyval == GDK_Escape &&
1871       range->layout->grab_location != MOUSE_OUTSIDE)
1872     {
1873       stop_scrolling (range);
1874
1875       update_slider_position (range,
1876                               range->slide_initial_coordinate,
1877                               range->slide_initial_coordinate);
1878
1879       return TRUE;
1880     }
1881
1882   return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
1883 }
1884
1885 static gint
1886 gtk_range_button_press (GtkWidget      *widget,
1887                         GdkEventButton *event)
1888 {
1889   GtkRange *range = GTK_RANGE (widget);
1890   
1891   if (!GTK_WIDGET_HAS_FOCUS (widget))
1892     gtk_widget_grab_focus (widget);
1893
1894   /* ignore presses when we're already doing something else. */
1895   if (range->layout->grab_location != MOUSE_OUTSIDE)
1896     return FALSE;
1897
1898   range->layout->mouse_x = event->x;
1899   range->layout->mouse_y = event->y;
1900   if (gtk_range_update_mouse_location (range))
1901     gtk_widget_queue_draw (widget);
1902     
1903   if (range->layout->mouse_location == MOUSE_TROUGH  &&
1904       event->button == 1)
1905     {
1906       /* button 1 steps by page increment, as with button 2 on a stepper
1907        */
1908       GtkScrollType scroll;
1909       gdouble click_value;
1910       
1911       click_value = coord_to_value (range,
1912                                     range->orientation == GTK_ORIENTATION_VERTICAL ?
1913                                     event->y : event->x);
1914       
1915       range->trough_click_forward = click_value > range->adjustment->value;
1916       range_grab_add (range, MOUSE_TROUGH, event->button);
1917       
1918       scroll = range_get_scroll_for_grab (range);
1919       
1920       gtk_range_add_step_timer (range, scroll);
1921
1922       return TRUE;
1923     }
1924   else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
1925             range->layout->mouse_location == MOUSE_STEPPER_B ||
1926             range->layout->mouse_location == MOUSE_STEPPER_C ||
1927             range->layout->mouse_location == MOUSE_STEPPER_D) &&
1928            (event->button == 1 || event->button == 2 || event->button == 3))
1929     {
1930       GdkRectangle *stepper_area;
1931       GtkScrollType scroll;
1932       
1933       range_grab_add (range, range->layout->mouse_location, event->button);
1934
1935       stepper_area = get_area (range, range->layout->mouse_location);
1936       gtk_widget_queue_draw_area (widget,
1937                                   widget->allocation.x + stepper_area->x,
1938                                   widget->allocation.y + stepper_area->y,
1939                                   stepper_area->width,
1940                                   stepper_area->height);
1941
1942       scroll = range_get_scroll_for_grab (range);
1943       if (scroll != GTK_SCROLL_NONE)
1944         gtk_range_add_step_timer (range, scroll);
1945       
1946       return TRUE;
1947     }
1948   else if ((range->layout->mouse_location == MOUSE_TROUGH &&
1949             event->button == 2) ||
1950            range->layout->mouse_location == MOUSE_SLIDER)
1951     {
1952       gboolean need_value_update = FALSE;
1953       gboolean activate_slider;
1954
1955       /* Any button can be used to drag the slider, but you can start
1956        * dragging the slider with a trough click using button 2;
1957        * On button 2 press, we warp the slider to mouse position,
1958        * then begin the slider drag.
1959        */
1960       if (event->button == 2)
1961         {
1962           gdouble slider_low_value, slider_high_value, new_value;
1963           
1964           slider_high_value =
1965             coord_to_value (range,
1966                             range->orientation == GTK_ORIENTATION_VERTICAL ?
1967                             event->y : event->x);
1968           slider_low_value =
1969             coord_to_value (range,
1970                             range->orientation == GTK_ORIENTATION_VERTICAL ?
1971                             event->y - range->layout->slider.height :
1972                             event->x - range->layout->slider.width);
1973           
1974           /* compute new value for warped slider */
1975           new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
1976
1977           /* recalc slider, so we can set slide_initial_slider_position
1978            * properly
1979            */
1980           range->need_recalc = TRUE;
1981           gtk_range_calc_layout (range, new_value);
1982
1983           /* defer adjustment updates to update_slider_position() in order
1984            * to keep pixel quantisation
1985            */
1986           need_value_update = TRUE;
1987         }
1988       
1989       if (range->orientation == GTK_ORIENTATION_VERTICAL)
1990         {
1991           range->slide_initial_slider_position = range->layout->slider.y;
1992           range->slide_initial_coordinate = event->y;
1993         }
1994       else
1995         {
1996           range->slide_initial_slider_position = range->layout->slider.x;
1997           range->slide_initial_coordinate = event->x;
1998         }
1999
2000       range_grab_add (range, MOUSE_SLIDER, event->button);
2001
2002       gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2003
2004       /* force a redraw, if the active slider is drawn differently to the
2005        * prelight one
2006        */
2007       if (activate_slider)
2008         gtk_widget_queue_draw (widget);
2009
2010       if (need_value_update)
2011         update_slider_position (range, event->x, event->y);
2012
2013       return TRUE;
2014     }
2015   
2016   return FALSE;
2017 }
2018
2019 /* During a slide, move the slider as required given new mouse position */
2020 static void
2021 update_slider_position (GtkRange *range,
2022                         gint      mouse_x,
2023                         gint      mouse_y)
2024 {
2025   gint delta;
2026   gint c;
2027   gdouble new_value;
2028   gboolean handled;
2029
2030   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2031     delta = mouse_y - range->slide_initial_coordinate;
2032   else
2033     delta = mouse_x - range->slide_initial_coordinate;
2034
2035   c = range->slide_initial_slider_position + delta;
2036
2037   new_value = coord_to_value (range, c);
2038   
2039   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2040                  &handled);
2041 }
2042
2043 static void 
2044 stop_scrolling (GtkRange *range)
2045 {
2046   range_grab_remove (range);
2047   gtk_range_remove_step_timer (range);
2048   /* Flush any pending discontinuous/delayed updates */
2049   gtk_range_update_value (range);
2050 }
2051
2052 static gboolean
2053 gtk_range_grab_broken (GtkWidget          *widget,
2054                        GdkEventGrabBroken *event)
2055 {
2056   GtkRange *range = GTK_RANGE (widget);
2057
2058   if (range->layout->grab_location != MOUSE_OUTSIDE)
2059     {
2060       if (range->layout->grab_location == MOUSE_SLIDER)
2061         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2062       
2063       stop_scrolling (range);
2064       
2065       return TRUE;
2066     }
2067   
2068   return FALSE;
2069 }
2070
2071 static gint
2072 gtk_range_button_release (GtkWidget      *widget,
2073                           GdkEventButton *event)
2074 {
2075   GtkRange *range = GTK_RANGE (widget);
2076
2077   if (event->window == range->event_window)
2078     {
2079       range->layout->mouse_x = event->x;
2080       range->layout->mouse_y = event->y;
2081     }
2082   else
2083     {
2084       gdk_window_get_pointer (range->event_window,
2085                               &range->layout->mouse_x,
2086                               &range->layout->mouse_y,
2087                               NULL);
2088     }
2089   
2090   if (range->layout->grab_button == event->button)
2091     {
2092       if (range->layout->grab_location == MOUSE_SLIDER)
2093         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2094
2095       stop_scrolling (range);
2096       
2097       return TRUE;
2098     }
2099
2100   return FALSE;
2101 }
2102
2103 /**
2104  * _gtk_range_get_wheel_delta:
2105  * @range: a #GtkRange
2106  * @direction: A #GdkScrollDirection
2107  * 
2108  * Returns a good step value for the mouse wheel.
2109  * 
2110  * Return value: A good step value for the mouse wheel. 
2111  * 
2112  * Since: 2.4
2113  **/
2114 gdouble
2115 _gtk_range_get_wheel_delta (GtkRange           *range,
2116                             GdkScrollDirection  direction)
2117 {
2118   GtkAdjustment *adj = range->adjustment;
2119   gdouble delta;
2120
2121   if (GTK_IS_SCROLLBAR (range))
2122     delta = pow (adj->page_size, 2.0 / 3.0);
2123   else
2124     delta = adj->step_increment * 2;
2125   
2126   if (direction == GDK_SCROLL_UP ||
2127       direction == GDK_SCROLL_LEFT)
2128     delta = - delta;
2129   
2130   if (range->inverted)
2131     delta = - delta;
2132
2133   return delta;
2134 }
2135       
2136 static gboolean
2137 gtk_range_scroll_event (GtkWidget      *widget,
2138                         GdkEventScroll *event)
2139 {
2140   GtkRange *range = GTK_RANGE (widget);
2141
2142   if (GTK_WIDGET_REALIZED (range))
2143     {
2144       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2145       gdouble delta;
2146       gboolean handled;
2147
2148       delta = _gtk_range_get_wheel_delta (range, event->direction);
2149
2150       g_signal_emit (range, signals[CHANGE_VALUE], 0,
2151                      GTK_SCROLL_JUMP, adj->value + delta,
2152                      &handled);
2153       
2154       /* Policy DELAYED makes sense with scroll events,
2155        * but DISCONTINUOUS doesn't, so we update immediately
2156        * for DISCONTINUOUS
2157        */
2158       if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2159         gtk_range_update_value (range);
2160     }
2161
2162   return TRUE;
2163 }
2164
2165 static gboolean
2166 gtk_range_motion_notify (GtkWidget      *widget,
2167                          GdkEventMotion *event)
2168 {
2169   GtkRange *range;
2170   gint x, y;
2171
2172   range = GTK_RANGE (widget);
2173
2174   gdk_window_get_pointer (range->event_window, &x, &y, NULL);
2175   
2176   range->layout->mouse_x = x;
2177   range->layout->mouse_y = y;
2178
2179   if (gtk_range_update_mouse_location (range))
2180     gtk_widget_queue_draw (widget);
2181
2182   if (range->layout->grab_location == MOUSE_SLIDER)
2183     update_slider_position (range, x, y);
2184
2185   /* We handled the event if the mouse was in the range_rect */
2186   return range->layout->mouse_location != MOUSE_OUTSIDE;
2187 }
2188
2189 static gboolean
2190 gtk_range_enter_notify (GtkWidget        *widget,
2191                         GdkEventCrossing *event)
2192 {
2193   GtkRange *range = GTK_RANGE (widget);
2194
2195   range->layout->mouse_x = event->x;
2196   range->layout->mouse_y = event->y;
2197
2198   if (gtk_range_update_mouse_location (range))
2199     gtk_widget_queue_draw (widget);
2200   
2201   return TRUE;
2202 }
2203
2204 static gboolean
2205 gtk_range_leave_notify (GtkWidget        *widget,
2206                         GdkEventCrossing *event)
2207 {
2208   GtkRange *range = GTK_RANGE (widget);
2209
2210   range->layout->mouse_x = -1;
2211   range->layout->mouse_y = -1;
2212
2213   if (gtk_range_update_mouse_location (range))
2214     gtk_widget_queue_draw (widget);
2215   
2216   return TRUE;
2217 }
2218
2219 static void
2220 gtk_range_grab_notify (GtkWidget *widget,
2221                        gboolean   was_grabbed)
2222 {
2223   if (!was_grabbed)
2224     stop_scrolling (GTK_RANGE (widget));
2225 }
2226
2227 static void
2228 gtk_range_state_changed (GtkWidget    *widget,
2229                          GtkStateType  previous_state)
2230 {
2231   if (!GTK_WIDGET_IS_SENSITIVE (widget)) 
2232     stop_scrolling (GTK_RANGE (widget));
2233 }
2234
2235 #define check_rectangle(rectangle1, rectangle2)              \
2236   {                                                          \
2237     if (rectangle1.x != rectangle2.x) return TRUE;           \
2238     if (rectangle1.y != rectangle2.y) return TRUE;           \
2239     if (rectangle1.width  != rectangle2.width)  return TRUE; \
2240     if (rectangle1.height != rectangle2.height) return TRUE; \
2241   }
2242
2243 static gboolean
2244 layout_changed (GtkRangeLayout *layout1, 
2245                 GtkRangeLayout *layout2)
2246 {
2247   check_rectangle (layout1->slider, layout2->slider);
2248   check_rectangle (layout1->trough, layout2->trough);
2249   check_rectangle (layout1->stepper_a, layout2->stepper_a);
2250   check_rectangle (layout1->stepper_d, layout2->stepper_d);
2251   check_rectangle (layout1->stepper_b, layout2->stepper_b);
2252   check_rectangle (layout1->stepper_c, layout2->stepper_c);
2253
2254   if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2255   if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2256
2257   return FALSE;
2258 }
2259
2260 static void
2261 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2262                               gpointer       data)
2263 {
2264   GtkRange *range = GTK_RANGE (data);
2265   /* create a copy of the layout */
2266   GtkRangeLayout layout = *range->layout;
2267
2268   range->need_recalc = TRUE;
2269   gtk_range_calc_layout (range, range->adjustment->value);
2270   
2271   /* now check whether the layout changed  */
2272   if (layout_changed (range->layout, &layout))
2273     gtk_widget_queue_draw (GTK_WIDGET (range));
2274
2275   /* Note that we don't round off to range->round_digits here.
2276    * that's because it's really broken to change a value
2277    * in response to a change signal on that value; round_digits
2278    * is therefore defined to be a filter on what the GtkRange
2279    * can input into the adjustment, not a filter that the GtkRange
2280    * will enforce on the adjustment.
2281    */
2282 }
2283
2284 static gboolean
2285 force_repaint (gpointer data)
2286 {
2287   GtkRange *range = GTK_RANGE (data);
2288   range->layout->repaint_id = 0;
2289   if (GTK_WIDGET_DRAWABLE (range))
2290     gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2291   return FALSE;
2292 }
2293
2294 static void
2295 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2296                                     gpointer       data)
2297 {
2298   GtkRange *range = GTK_RANGE (data);
2299   /* create a copy of the layout */
2300   GtkRangeLayout layout = *range->layout;
2301
2302   range->need_recalc = TRUE;
2303   gtk_range_calc_layout (range, range->adjustment->value);
2304   
2305   /* now check whether the layout changed  */
2306   if (layout_changed (range->layout, &layout))
2307     {
2308       gtk_widget_queue_draw (GTK_WIDGET (range));
2309       /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2310       if (!range->layout->repaint_id)
2311         range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
2312     }
2313   
2314   /* Note that we don't round off to range->round_digits here.
2315    * that's because it's really broken to change a value
2316    * in response to a change signal on that value; round_digits
2317    * is therefore defined to be a filter on what the GtkRange
2318    * can input into the adjustment, not a filter that the GtkRange
2319    * will enforce on the adjustment.
2320    */
2321
2322   g_signal_emit (range, signals[VALUE_CHANGED], 0);
2323 }
2324
2325 static void
2326 gtk_range_style_set (GtkWidget *widget,
2327                      GtkStyle  *previous_style)
2328 {
2329   GtkRange *range = GTK_RANGE (widget);
2330
2331   range->need_recalc = TRUE;
2332
2333   GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
2334 }
2335
2336 static void
2337 step_back (GtkRange *range)
2338 {
2339   gdouble newval;
2340   gboolean handled;
2341   
2342   newval = range->adjustment->value - range->adjustment->step_increment;
2343   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2344                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2345 }
2346
2347 static void
2348 step_forward (GtkRange *range)
2349 {
2350   gdouble newval;
2351   gboolean handled;
2352
2353   newval = range->adjustment->value + range->adjustment->step_increment;
2354   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2355                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
2356 }
2357
2358
2359 static void
2360 page_back (GtkRange *range)
2361 {
2362   gdouble newval;
2363   gboolean handled;
2364
2365   newval = range->adjustment->value - range->adjustment->page_increment;
2366   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2367                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2368 }
2369
2370 static void
2371 page_forward (GtkRange *range)
2372 {
2373   gdouble newval;
2374   gboolean handled;
2375
2376   newval = range->adjustment->value + range->adjustment->page_increment;
2377   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2378                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2379 }
2380
2381 static void
2382 scroll_begin (GtkRange *range)
2383 {
2384   gboolean handled;
2385   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2386                  GTK_SCROLL_START, range->adjustment->lower,
2387                  &handled);
2388 }
2389
2390 static void
2391 scroll_end (GtkRange *range)
2392 {
2393   gdouble newval;
2394   gboolean handled;
2395
2396   newval = range->adjustment->upper - range->adjustment->page_size;
2397   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2398                  &handled);
2399 }
2400
2401 static gboolean
2402 gtk_range_scroll (GtkRange     *range,
2403                   GtkScrollType scroll)
2404 {
2405   gdouble old_value = range->adjustment->value;
2406
2407   switch (scroll)
2408     {
2409     case GTK_SCROLL_STEP_LEFT:
2410       if (should_invert (range))
2411         step_forward (range);
2412       else
2413         step_back (range);
2414       break;
2415                     
2416     case GTK_SCROLL_STEP_UP:
2417       if (should_invert (range))
2418         step_forward (range);
2419       else
2420         step_back (range);
2421       break;
2422
2423     case GTK_SCROLL_STEP_RIGHT:
2424       if (should_invert (range))
2425         step_back (range);
2426       else
2427         step_forward (range);
2428       break;
2429                     
2430     case GTK_SCROLL_STEP_DOWN:
2431       if (should_invert (range))
2432         step_back (range);
2433       else
2434         step_forward (range);
2435       break;
2436                   
2437     case GTK_SCROLL_STEP_BACKWARD:
2438       step_back (range);
2439       break;
2440                   
2441     case GTK_SCROLL_STEP_FORWARD:
2442       step_forward (range);
2443       break;
2444
2445     case GTK_SCROLL_PAGE_LEFT:
2446       if (should_invert (range))
2447         page_forward (range);
2448       else
2449         page_back (range);
2450       break;
2451                     
2452     case GTK_SCROLL_PAGE_UP:
2453       if (should_invert (range))
2454         page_forward (range);
2455       else
2456         page_back (range);
2457       break;
2458
2459     case GTK_SCROLL_PAGE_RIGHT:
2460       if (should_invert (range))
2461         page_back (range);
2462       else
2463         page_forward (range);
2464       break;
2465                     
2466     case GTK_SCROLL_PAGE_DOWN:
2467       if (should_invert (range))
2468         page_back (range);
2469       else
2470         page_forward (range);
2471       break;
2472                   
2473     case GTK_SCROLL_PAGE_BACKWARD:
2474       page_back (range);
2475       break;
2476                   
2477     case GTK_SCROLL_PAGE_FORWARD:
2478       page_forward (range);
2479       break;
2480
2481     case GTK_SCROLL_START:
2482       scroll_begin (range);
2483       break;
2484
2485     case GTK_SCROLL_END:
2486       scroll_end (range);
2487       break;
2488
2489     case GTK_SCROLL_JUMP:
2490       /* Used by CList, range doesn't use it. */
2491       break;
2492
2493     case GTK_SCROLL_NONE:
2494       break;
2495     }
2496
2497   return range->adjustment->value != old_value;
2498 }
2499
2500 static void
2501 gtk_range_move_slider (GtkRange     *range,
2502                        GtkScrollType scroll)
2503 {
2504   gboolean cursor_only;
2505
2506   g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2507                 "gtk-keynav-cursor-only", &cursor_only,
2508                 NULL);
2509
2510   if (cursor_only)
2511     {
2512       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2513
2514       if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2515         {
2516           if (scroll == GTK_SCROLL_STEP_UP ||
2517               scroll == GTK_SCROLL_STEP_DOWN)
2518             {
2519               if (toplevel)
2520                 gtk_widget_child_focus (toplevel,
2521                                         scroll == GTK_SCROLL_STEP_UP ?
2522                                         GTK_DIR_UP : GTK_DIR_DOWN);
2523               return;
2524             }
2525         }
2526       else
2527         {
2528           if (scroll == GTK_SCROLL_STEP_LEFT ||
2529               scroll == GTK_SCROLL_STEP_RIGHT)
2530             {
2531               if (toplevel)
2532                 gtk_widget_child_focus (toplevel,
2533                                         scroll == GTK_SCROLL_STEP_LEFT ?
2534                                         GTK_DIR_LEFT : GTK_DIR_RIGHT);
2535               return;
2536             }
2537         }
2538     }
2539
2540   if (! gtk_range_scroll (range, scroll))
2541     gtk_widget_error_bell (GTK_WIDGET (range));
2542
2543   /* Policy DELAYED makes sense with key events,
2544    * but DISCONTINUOUS doesn't, so we update immediately
2545    * for DISCONTINUOUS
2546    */
2547   if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2548     gtk_range_update_value (range);
2549 }
2550
2551 static void
2552 gtk_range_get_props (GtkRange  *range,
2553                      gint      *slider_width,
2554                      gint      *stepper_size,
2555                      gint      *focus_width,
2556                      gint      *trough_border,
2557                      gint      *stepper_spacing,
2558                      gboolean  *trough_under_steppers,
2559                      gint      *arrow_displacement_x,
2560                      gint      *arrow_displacement_y)
2561 {
2562   GtkWidget *widget =  GTK_WIDGET (range);
2563   gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
2564   gint tmp_stepper_spacing, tmp_trough_under_steppers;
2565   gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2566   
2567   gtk_widget_style_get (widget,
2568                         "slider-width", &tmp_slider_width,
2569                         "trough-border", &tmp_trough_border,
2570                         "stepper-size", &tmp_stepper_size,
2571                         "stepper-spacing", &tmp_stepper_spacing,
2572                         "trough-under-steppers", &tmp_trough_under_steppers,
2573                         "arrow-displacement-x", &tmp_arrow_displacement_x,
2574                         "arrow-displacement-y", &tmp_arrow_displacement_y,
2575                         NULL);
2576
2577   if (tmp_stepper_spacing > 0)
2578     tmp_trough_under_steppers = FALSE;
2579
2580   if (GTK_WIDGET_CAN_FOCUS (range))
2581     {
2582       gint focus_line_width;
2583       gint focus_padding;
2584       
2585       gtk_widget_style_get (GTK_WIDGET (range),
2586                             "focus-line-width", &focus_line_width,
2587                             "focus-padding", &focus_padding,
2588                             NULL);
2589
2590       tmp_focus_width = focus_line_width + focus_padding;
2591     }
2592   else
2593     {
2594       tmp_focus_width = 0;
2595     }
2596   
2597   if (slider_width)
2598     *slider_width = tmp_slider_width;
2599
2600   if (focus_width)
2601     *focus_width = tmp_focus_width;
2602
2603   if (trough_border)
2604     *trough_border = tmp_trough_border;
2605
2606   if (stepper_size)
2607     *stepper_size = tmp_stepper_size;
2608
2609   if (stepper_spacing)
2610     *stepper_spacing = tmp_stepper_spacing;
2611
2612   if (trough_under_steppers)
2613     *trough_under_steppers = tmp_trough_under_steppers;
2614
2615   if (arrow_displacement_x)
2616     *arrow_displacement_x = tmp_arrow_displacement_x;
2617
2618   if (arrow_displacement_y)
2619     *arrow_displacement_y = tmp_arrow_displacement_y;
2620 }
2621
2622 #define POINT_IN_RECT(xcoord, ycoord, rect) \
2623  ((xcoord) >= (rect).x &&                   \
2624   (xcoord) <  ((rect).x + (rect).width) &&  \
2625   (ycoord) >= (rect).y &&                   \
2626   (ycoord) <  ((rect).y + (rect).height))
2627
2628 /* Update mouse location, return TRUE if it changes */
2629 static gboolean
2630 gtk_range_update_mouse_location (GtkRange *range)
2631 {
2632   gint x, y;
2633   MouseLocation old;
2634   GtkWidget *widget;
2635
2636   widget = GTK_WIDGET (range);
2637   
2638   old = range->layout->mouse_location;
2639   
2640   x = range->layout->mouse_x;
2641   y = range->layout->mouse_y;
2642
2643   if (range->layout->grab_location != MOUSE_OUTSIDE)
2644     range->layout->mouse_location = range->layout->grab_location;
2645   else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
2646     range->layout->mouse_location = MOUSE_STEPPER_A;
2647   else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
2648     range->layout->mouse_location = MOUSE_STEPPER_B;
2649   else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
2650     range->layout->mouse_location = MOUSE_STEPPER_C;
2651   else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
2652     range->layout->mouse_location = MOUSE_STEPPER_D;
2653   else if (POINT_IN_RECT (x, y, range->layout->slider))
2654     range->layout->mouse_location = MOUSE_SLIDER;
2655   else if (POINT_IN_RECT (x, y, range->layout->trough))
2656     range->layout->mouse_location = MOUSE_TROUGH;
2657   else if (POINT_IN_RECT (x, y, widget->allocation))
2658     range->layout->mouse_location = MOUSE_WIDGET;
2659   else
2660     range->layout->mouse_location = MOUSE_OUTSIDE;
2661
2662   return old != range->layout->mouse_location;
2663 }
2664
2665 /* Clamp rect, border inside widget->allocation, such that we prefer
2666  * to take space from border not rect in all directions, and prefer to
2667  * give space to border over rect in one direction.
2668  */
2669 static void
2670 clamp_dimensions (GtkWidget    *widget,
2671                   GdkRectangle *rect,
2672                   GtkBorder    *border,
2673                   gboolean      border_expands_horizontally)
2674 {
2675   gint extra, shortage;
2676   
2677   g_return_if_fail (rect->x == 0);
2678   g_return_if_fail (rect->y == 0);  
2679   g_return_if_fail (rect->width >= 0);
2680   g_return_if_fail (rect->height >= 0);
2681
2682   /* Width */
2683   
2684   extra = widget->allocation.width - border->left - border->right - rect->width;
2685   if (extra > 0)
2686     {
2687       if (border_expands_horizontally)
2688         {
2689           border->left += extra / 2;
2690           border->right += extra / 2 + extra % 2;
2691         }
2692       else
2693         {
2694           rect->width += extra;
2695         }
2696     }
2697   
2698   /* See if we can fit rect, if not kill the border */
2699   shortage = rect->width - widget->allocation.width;
2700   if (shortage > 0)
2701     {
2702       rect->width = widget->allocation.width;
2703       /* lose the border */
2704       border->left = 0;
2705       border->right = 0;
2706     }
2707   else
2708     {
2709       /* See if we can fit rect with borders */
2710       shortage = rect->width + border->left + border->right -
2711         widget->allocation.width;
2712       if (shortage > 0)
2713         {
2714           /* Shrink borders */
2715           border->left -= shortage / 2;
2716           border->right -= shortage / 2 + shortage % 2;
2717         }
2718     }
2719
2720   /* Height */
2721   
2722   extra = widget->allocation.height - border->top - border->bottom - rect->height;
2723   if (extra > 0)
2724     {
2725       if (border_expands_horizontally)
2726         {
2727           /* don't expand border vertically */
2728           rect->height += extra;
2729         }
2730       else
2731         {
2732           border->top += extra / 2;
2733           border->bottom += extra / 2 + extra % 2;
2734         }
2735     }
2736   
2737   /* See if we can fit rect, if not kill the border */
2738   shortage = rect->height - widget->allocation.height;
2739   if (shortage > 0)
2740     {
2741       rect->height = widget->allocation.height;
2742       /* lose the border */
2743       border->top = 0;
2744       border->bottom = 0;
2745     }
2746   else
2747     {
2748       /* See if we can fit rect with borders */
2749       shortage = rect->height + border->top + border->bottom -
2750         widget->allocation.height;
2751       if (shortage > 0)
2752         {
2753           /* Shrink borders */
2754           border->top -= shortage / 2;
2755           border->bottom -= shortage / 2 + shortage % 2;
2756         }
2757     }
2758 }
2759
2760 static void
2761 gtk_range_calc_request (GtkRange      *range,
2762                         gint           slider_width,
2763                         gint           stepper_size,
2764                         gint           focus_width,
2765                         gint           trough_border,
2766                         gint           stepper_spacing,
2767                         GdkRectangle  *range_rect,
2768                         GtkBorder     *border,
2769                         gint          *n_steppers_p,
2770                         gboolean      *has_steppers_ab,
2771                         gboolean      *has_steppers_cd,
2772                         gint          *slider_length_p)
2773 {
2774   gint slider_length;
2775   gint n_steppers;
2776   gint n_steppers_ab;
2777   gint n_steppers_cd;
2778
2779   border->left = 0;
2780   border->right = 0;
2781   border->top = 0;
2782   border->bottom = 0;
2783
2784   if (GTK_RANGE_GET_CLASS (range)->get_range_border)
2785     GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
2786
2787   n_steppers_ab = 0;
2788   n_steppers_cd = 0;
2789
2790   if (range->has_stepper_a)
2791     n_steppers_ab += 1;
2792   if (range->has_stepper_b)
2793     n_steppers_ab += 1;
2794   if (range->has_stepper_c)
2795     n_steppers_cd += 1;
2796   if (range->has_stepper_d)
2797     n_steppers_cd += 1;
2798
2799   n_steppers = n_steppers_ab + n_steppers_cd;
2800
2801   slider_length = range->min_slider_size;
2802
2803   range_rect->x = 0;
2804   range_rect->y = 0;
2805   
2806   /* We never expand to fill available space in the small dimension
2807    * (i.e. vertical scrollbars are always a fixed width)
2808    */
2809   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2810     {
2811       range_rect->width = (focus_width + trough_border) * 2 + slider_width;
2812       range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2813
2814       if (n_steppers_ab > 0)
2815         range_rect->height += stepper_spacing;
2816
2817       if (n_steppers_cd > 0)
2818         range_rect->height += stepper_spacing;
2819     }
2820   else
2821     {
2822       range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2823       range_rect->height = (focus_width + trough_border) * 2 + slider_width;
2824
2825       if (n_steppers_ab > 0)
2826         range_rect->width += stepper_spacing;
2827
2828       if (n_steppers_cd > 0)
2829         range_rect->width += stepper_spacing;
2830     }
2831
2832   if (n_steppers_p)
2833     *n_steppers_p = n_steppers;
2834
2835   if (has_steppers_ab)
2836     *has_steppers_ab = (n_steppers_ab > 0);
2837
2838   if (has_steppers_cd)
2839     *has_steppers_cd = (n_steppers_cd > 0);
2840
2841   if (slider_length_p)
2842     *slider_length_p = slider_length;
2843 }
2844
2845 static void
2846 gtk_range_calc_layout (GtkRange *range,
2847                        gdouble   adjustment_value)
2848 {
2849   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
2850   gint slider_length;
2851   GtkBorder border;
2852   gint n_steppers;
2853   gboolean has_steppers_ab;
2854   gboolean has_steppers_cd;
2855   gboolean trough_under_steppers;
2856   GdkRectangle range_rect;
2857   GtkRangeLayout *layout;
2858   GtkWidget *widget;
2859   
2860   if (!range->need_recalc)
2861     return;
2862
2863   /* If we have a too-small allocation, we prefer the steppers over
2864    * the trough/slider, probably the steppers are a more useful
2865    * feature in small spaces.
2866    *
2867    * Also, we prefer to draw the range itself rather than the border
2868    * areas if there's a conflict, since the borders will be decoration
2869    * not controls. Though this depends on subclasses cooperating by
2870    * not drawing on range->range_rect.
2871    */
2872
2873   widget = GTK_WIDGET (range);
2874   layout = range->layout;
2875   
2876   gtk_range_get_props (range,
2877                        &slider_width, &stepper_size,
2878                        &focus_width, &trough_border,
2879                        &stepper_spacing, &trough_under_steppers,
2880                        NULL, NULL);
2881
2882   gtk_range_calc_request (range, 
2883                           slider_width, stepper_size,
2884                           focus_width, trough_border, stepper_spacing,
2885                           &range_rect, &border, &n_steppers,
2886                           &has_steppers_ab, &has_steppers_cd, &slider_length);
2887   
2888   /* We never expand to fill available space in the small dimension
2889    * (i.e. vertical scrollbars are always a fixed width)
2890    */
2891   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2892     {
2893       clamp_dimensions (widget, &range_rect, &border, TRUE);
2894     }
2895   else
2896     {
2897       clamp_dimensions (widget, &range_rect, &border, FALSE);
2898     }
2899   
2900   range_rect.x = border.left;
2901   range_rect.y = border.top;
2902   
2903   range->range_rect = range_rect;
2904   
2905   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2906     {
2907       gint stepper_width, stepper_height;
2908
2909       /* Steppers are the width of the range, and stepper_size in
2910        * height, or if we don't have enough height, divided equally
2911        * among available space.
2912        */
2913       stepper_width = range_rect.width - focus_width * 2;
2914
2915       if (trough_under_steppers)
2916         stepper_width -= trough_border * 2;
2917
2918       if (stepper_width < 1)
2919         stepper_width = range_rect.width; /* screw the trough border */
2920
2921       if (n_steppers == 0)
2922         stepper_height = 0; /* avoid divide by n_steppers */
2923       else
2924         stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
2925
2926       /* Stepper A */
2927       
2928       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
2929       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
2930
2931       if (range->has_stepper_a)
2932         {
2933           layout->stepper_a.width = stepper_width;
2934           layout->stepper_a.height = stepper_height;
2935         }
2936       else
2937         {
2938           layout->stepper_a.width = 0;
2939           layout->stepper_a.height = 0;
2940         }
2941
2942       /* Stepper B */
2943       
2944       layout->stepper_b.x = layout->stepper_a.x;
2945       layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
2946
2947       if (range->has_stepper_b)
2948         {
2949           layout->stepper_b.width = stepper_width;
2950           layout->stepper_b.height = stepper_height;
2951         }
2952       else
2953         {
2954           layout->stepper_b.width = 0;
2955           layout->stepper_b.height = 0;
2956         }
2957
2958       /* Stepper D */
2959
2960       if (range->has_stepper_d)
2961         {
2962           layout->stepper_d.width = stepper_width;
2963           layout->stepper_d.height = stepper_height;
2964         }
2965       else
2966         {
2967           layout->stepper_d.width = 0;
2968           layout->stepper_d.height = 0;
2969         }
2970       
2971       layout->stepper_d.x = layout->stepper_a.x;
2972       layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
2973
2974       /* Stepper C */
2975
2976       if (range->has_stepper_c)
2977         {
2978           layout->stepper_c.width = stepper_width;
2979           layout->stepper_c.height = stepper_height;
2980         }
2981       else
2982         {
2983           layout->stepper_c.width = 0;
2984           layout->stepper_c.height = 0;
2985         }
2986       
2987       layout->stepper_c.x = layout->stepper_a.x;
2988       layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
2989
2990       /* Now the trough is the remaining space between steppers B and C,
2991        * if any, minus spacing
2992        */
2993       layout->trough.x = range_rect.x;
2994       layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
2995       layout->trough.width = range_rect.width;
2996       layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
2997
2998       /* Slider fits into the trough, with stepper_spacing on either side,
2999        * and the size/position based on the adjustment or fixed, depending.
3000        */
3001       layout->slider.x = layout->trough.x + focus_width + trough_border;
3002       layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
3003
3004       /* Compute slider position/length */
3005       {
3006         gint y, bottom, top, height;
3007         
3008         top = layout->trough.y;
3009         bottom = layout->trough.y + layout->trough.height;
3010
3011         if (! trough_under_steppers)
3012           {
3013             top += trough_border;
3014             bottom -= trough_border;
3015           }
3016
3017         /* slider height is the fraction (page_size /
3018          * total_adjustment_range) times the trough height in pixels
3019          */
3020
3021         if (range->adjustment->upper - range->adjustment->lower != 0)
3022           height = ((bottom - top) * (range->adjustment->page_size /
3023                                        (range->adjustment->upper - range->adjustment->lower)));
3024         else
3025           height = range->min_slider_size;
3026         
3027         if (height < range->min_slider_size ||
3028             range->slider_size_fixed)
3029           height = range->min_slider_size;
3030
3031         height = MIN (height, layout->trough.height);
3032         
3033         y = top;
3034         
3035         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3036           y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
3037                                           (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3038         
3039         y = CLAMP (y, top, bottom);
3040         
3041         if (should_invert (range))
3042           y = bottom - (y - top + height);
3043         
3044         layout->slider.y = y;
3045         layout->slider.height = height;
3046
3047         /* These are publically exported */
3048         range->slider_start = layout->slider.y;
3049         range->slider_end = layout->slider.y + layout->slider.height;
3050       }
3051     }
3052   else
3053     {
3054       gint stepper_width, stepper_height;
3055
3056       /* Steppers are the height of the range, and stepper_size in
3057        * width, or if we don't have enough width, divided equally
3058        * among available space.
3059        */
3060       stepper_height = range_rect.height + focus_width * 2;
3061
3062       if (trough_under_steppers)
3063         stepper_height -= trough_border * 2;
3064
3065       if (stepper_height < 1)
3066         stepper_height = range_rect.height; /* screw the trough border */
3067
3068       if (n_steppers == 0)
3069         stepper_width = 0; /* avoid divide by n_steppers */
3070       else
3071         stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3072
3073       /* Stepper A */
3074       
3075       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3076       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3077
3078       if (range->has_stepper_a)
3079         {
3080           layout->stepper_a.width = stepper_width;
3081           layout->stepper_a.height = stepper_height;
3082         }
3083       else
3084         {
3085           layout->stepper_a.width = 0;
3086           layout->stepper_a.height = 0;
3087         }
3088
3089       /* Stepper B */
3090       
3091       layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3092       layout->stepper_b.y = layout->stepper_a.y;
3093
3094       if (range->has_stepper_b)
3095         {
3096           layout->stepper_b.width = stepper_width;
3097           layout->stepper_b.height = stepper_height;
3098         }
3099       else
3100         {
3101           layout->stepper_b.width = 0;
3102           layout->stepper_b.height = 0;
3103         }
3104
3105       /* Stepper D */
3106
3107       if (range->has_stepper_d)
3108         {
3109           layout->stepper_d.width = stepper_width;
3110           layout->stepper_d.height = stepper_height;
3111         }
3112       else
3113         {
3114           layout->stepper_d.width = 0;
3115           layout->stepper_d.height = 0;
3116         }
3117
3118       layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3119       layout->stepper_d.y = layout->stepper_a.y;
3120
3121
3122       /* Stepper C */
3123
3124       if (range->has_stepper_c)
3125         {
3126           layout->stepper_c.width = stepper_width;
3127           layout->stepper_c.height = stepper_height;
3128         }
3129       else
3130         {
3131           layout->stepper_c.width = 0;
3132           layout->stepper_c.height = 0;
3133         }
3134       
3135       layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3136       layout->stepper_c.y = layout->stepper_a.y;
3137
3138       /* Now the trough is the remaining space between steppers B and C,
3139        * if any
3140        */
3141       layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3142       layout->trough.y = range_rect.y;
3143
3144       layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3145       layout->trough.height = range_rect.height;
3146
3147       /* Slider fits into the trough, with stepper_spacing on either side,
3148        * and the size/position based on the adjustment or fixed, depending.
3149        */
3150       layout->slider.y = layout->trough.y + focus_width + trough_border;
3151       layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3152
3153       /* Compute slider position/length */
3154       {
3155         gint x, left, right, width;
3156         
3157         left = layout->trough.x;
3158         right = layout->trough.x + layout->trough.width;
3159
3160         if (! trough_under_steppers)
3161           {
3162             left += trough_border;
3163             right -= trough_border;
3164           }
3165
3166         /* slider width is the fraction (page_size /
3167          * total_adjustment_range) times the trough width in pixels
3168          */
3169         
3170         if (range->adjustment->upper - range->adjustment->lower != 0)
3171           width = ((right - left) * (range->adjustment->page_size /
3172                                    (range->adjustment->upper - range->adjustment->lower)));
3173         else
3174           width = range->min_slider_size;
3175         
3176         if (width < range->min_slider_size ||
3177             range->slider_size_fixed)
3178           width = range->min_slider_size;
3179         
3180         width = MIN (width, layout->trough.width);
3181         
3182         x = left;
3183         
3184         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3185           x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3186                                          (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3187         
3188         x = CLAMP (x, left, right);
3189         
3190         if (should_invert (range))
3191           x = right - (x - left + width);
3192         
3193         layout->slider.x = x;
3194         layout->slider.width = width;
3195
3196         /* These are publically exported */
3197         range->slider_start = layout->slider.x;
3198         range->slider_end = layout->slider.x + layout->slider.width;
3199       }
3200     }
3201   
3202   gtk_range_update_mouse_location (range);
3203
3204   switch (range->layout->upper_sensitivity)
3205     {
3206     case GTK_SENSITIVITY_AUTO:
3207       range->layout->upper_sensitive =
3208         (range->adjustment->value <
3209          (range->adjustment->upper - range->adjustment->page_size));
3210       break;
3211
3212     case GTK_SENSITIVITY_ON:
3213       range->layout->upper_sensitive = TRUE;
3214       break;
3215
3216     case GTK_SENSITIVITY_OFF:
3217       range->layout->upper_sensitive = FALSE;
3218       break;
3219     }
3220
3221   switch (range->layout->lower_sensitivity)
3222     {
3223     case GTK_SENSITIVITY_AUTO:
3224       range->layout->lower_sensitive =
3225         (range->adjustment->value > range->adjustment->lower);
3226       break;
3227
3228     case GTK_SENSITIVITY_ON:
3229       range->layout->lower_sensitive = TRUE;
3230       break;
3231
3232     case GTK_SENSITIVITY_OFF:
3233       range->layout->lower_sensitive = FALSE;
3234       break;
3235     }
3236 }
3237
3238 static GdkRectangle*
3239 get_area (GtkRange     *range,
3240           MouseLocation location)
3241 {
3242   switch (location)
3243     {
3244     case MOUSE_STEPPER_A:
3245       return &range->layout->stepper_a;
3246     case MOUSE_STEPPER_B:
3247       return &range->layout->stepper_b;
3248     case MOUSE_STEPPER_C:
3249       return &range->layout->stepper_c;
3250     case MOUSE_STEPPER_D:
3251       return &range->layout->stepper_d;
3252     case MOUSE_TROUGH:
3253       return &range->layout->trough;
3254     case MOUSE_SLIDER:
3255       return &range->layout->slider;
3256     case MOUSE_WIDGET:
3257     case MOUSE_OUTSIDE:
3258       break;
3259     }
3260
3261   g_warning (G_STRLOC": bug");
3262   return NULL;
3263 }
3264
3265 static gboolean
3266 gtk_range_real_change_value (GtkRange     *range,
3267                              GtkScrollType scroll,
3268                              gdouble       value)
3269 {
3270   /* potentially adjust the bounds _before we clamp */
3271   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3272
3273   if (range->layout->restrict_to_fill_level)
3274     value = MIN (value, MAX (range->adjustment->lower,
3275                              range->layout->fill_level));
3276
3277   value = CLAMP (value, range->adjustment->lower,
3278                  (range->adjustment->upper - range->adjustment->page_size));
3279
3280   if (range->round_digits >= 0)
3281     {
3282       gdouble power;
3283       gint i;
3284
3285       i = range->round_digits;
3286       power = 1;
3287       while (i--)
3288         power *= 10;
3289       
3290       value = floor ((value * power) + 0.5) / power;
3291     }
3292   
3293   if (range->adjustment->value != value)
3294     {
3295       range->need_recalc = TRUE;
3296
3297       gtk_widget_queue_draw (GTK_WIDGET (range));
3298       
3299       switch (range->update_policy)
3300         {
3301         case GTK_UPDATE_CONTINUOUS:
3302           gtk_adjustment_set_value (range->adjustment, value);
3303           break;
3304
3305           /* Delayed means we update after a period of inactivity */
3306         case GTK_UPDATE_DELAYED:
3307           gtk_range_reset_update_timer (range);
3308           /* FALL THRU */
3309
3310           /* Discontinuous means we update on button release */
3311         case GTK_UPDATE_DISCONTINUOUS:
3312           /* don't emit value_changed signal */
3313           range->adjustment->value = value;
3314           range->update_pending = TRUE;
3315           break;
3316         }
3317     }
3318   return FALSE;
3319 }
3320
3321 static void
3322 gtk_range_update_value (GtkRange *range)
3323 {
3324   gtk_range_remove_update_timer (range);
3325   
3326   if (range->update_pending)
3327     {
3328       gtk_adjustment_value_changed (range->adjustment);
3329
3330       range->update_pending = FALSE;
3331     }
3332 }
3333
3334 struct _GtkRangeStepTimer
3335 {
3336   guint timeout_id;
3337   GtkScrollType step;
3338 };
3339
3340 static gboolean
3341 second_timeout (gpointer data)
3342 {
3343   GtkRange *range;
3344
3345   range = GTK_RANGE (data);
3346   gtk_range_scroll (range, range->timer->step);
3347   
3348   return TRUE;
3349 }
3350
3351 static gboolean
3352 initial_timeout (gpointer data)
3353 {
3354   GtkRange    *range;
3355   GtkSettings *settings;
3356   guint        timeout;
3357
3358   settings = gtk_widget_get_settings (GTK_WIDGET (data));
3359   g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3360
3361   range = GTK_RANGE (data);
3362   range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3363                                             second_timeout,
3364                                             range);
3365   /* remove self */
3366   return FALSE;
3367 }
3368
3369 static void
3370 gtk_range_add_step_timer (GtkRange      *range,
3371                           GtkScrollType  step)
3372 {
3373   GtkSettings *settings;
3374   guint        timeout;
3375
3376   g_return_if_fail (range->timer == NULL);
3377   g_return_if_fail (step != GTK_SCROLL_NONE);
3378
3379   settings = gtk_widget_get_settings (GTK_WIDGET (range));
3380   g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3381
3382   range->timer = g_new (GtkRangeStepTimer, 1);
3383
3384   range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3385                                             initial_timeout,
3386                                             range);
3387   range->timer->step = step;
3388
3389   gtk_range_scroll (range, range->timer->step);
3390 }
3391
3392 static void
3393 gtk_range_remove_step_timer (GtkRange *range)
3394 {
3395   if (range->timer)
3396     {
3397       if (range->timer->timeout_id != 0)
3398         g_source_remove (range->timer->timeout_id);
3399
3400       g_free (range->timer);
3401
3402       range->timer = NULL;
3403     }
3404 }
3405
3406 static gboolean
3407 update_timeout (gpointer data)
3408 {
3409   GtkRange *range;
3410
3411   range = GTK_RANGE (data);
3412   gtk_range_update_value (range);
3413   range->update_timeout_id = 0;
3414
3415   /* self-remove */
3416   return FALSE;
3417 }
3418
3419 static void
3420 gtk_range_reset_update_timer (GtkRange *range)
3421 {
3422   gtk_range_remove_update_timer (range);
3423
3424   range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3425                                             update_timeout,
3426                                             range);
3427 }
3428
3429 static void
3430 gtk_range_remove_update_timer (GtkRange *range)
3431 {
3432   if (range->update_timeout_id != 0)
3433     {
3434       g_source_remove (range->update_timeout_id);
3435       range->update_timeout_id = 0;
3436     }
3437 }
3438
3439 #define __GTK_RANGE_C__
3440 #include "gtkaliasdef.c"