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