]> Pileus Git - ~andy/gtk/blob - gtk/gtkrange.c
Typo fix. (#352121, Clytie Siddall)
[~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 gboolean gtk_range_expose         (GtkWidget        *widget,
133                                       GdkEventExpose   *event);
134 static gboolean gtk_range_button_press   (GtkWidget        *widget,
135                                       GdkEventButton   *event);
136 static gboolean gtk_range_button_release (GtkWidget        *widget,
137                                       GdkEventButton   *event);
138 static gboolean gtk_range_motion_notify  (GtkWidget        *widget,
139                                       GdkEventMotion   *event);
140 static gboolean gtk_range_enter_notify   (GtkWidget        *widget,
141                                       GdkEventCrossing *event);
142 static gboolean 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 gboolean 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 trough 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 gboolean
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
2016 static gboolean
2017 gtk_range_grab_broken (GtkWidget          *widget,
2018                        GdkEventGrabBroken *event)
2019 {
2020   GtkRange *range = GTK_RANGE (widget);
2021
2022   if (range->layout->grab_location != MOUSE_OUTSIDE)
2023     {
2024       if (range->layout->grab_location == MOUSE_SLIDER)
2025         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2026       
2027       stop_scrolling (range);
2028       
2029       return TRUE;
2030     }
2031   
2032   return FALSE;
2033 }
2034
2035 static gint
2036 gtk_range_button_release (GtkWidget      *widget,
2037                           GdkEventButton *event)
2038 {
2039   GtkRange *range = GTK_RANGE (widget);
2040
2041   if (event->window == range->event_window)
2042     {
2043       range->layout->mouse_x = event->x;
2044       range->layout->mouse_y = event->y;
2045     }
2046   else
2047     {
2048       gdk_window_get_pointer (range->event_window,
2049                               &range->layout->mouse_x,
2050                               &range->layout->mouse_y,
2051                               NULL);
2052     }
2053   
2054   if (range->layout->grab_button == event->button)
2055     {
2056       if (range->layout->grab_location == MOUSE_SLIDER)
2057         update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2058
2059       stop_scrolling (range);
2060       
2061       return TRUE;
2062     }
2063
2064   return FALSE;
2065 }
2066
2067 /**
2068  * _gtk_range_get_wheel_delta:
2069  * @range: a #GtkRange
2070  * @direction: A #GdkScrollDirection
2071  * 
2072  * Returns a good step value for the mouse wheel.
2073  * 
2074  * Return value: A good step value for the mouse wheel. 
2075  * 
2076  * Since: 2.4
2077  **/
2078 gdouble
2079 _gtk_range_get_wheel_delta (GtkRange           *range,
2080                             GdkScrollDirection  direction)
2081 {
2082   GtkAdjustment *adj = range->adjustment;
2083   gdouble delta;
2084
2085   if (GTK_IS_SCROLLBAR (range))
2086     delta = pow (adj->page_size, 2.0 / 3.0);
2087   else
2088     delta = adj->step_increment * 2;
2089   
2090   if (direction == GDK_SCROLL_UP ||
2091       direction == GDK_SCROLL_LEFT)
2092     delta = - delta;
2093   
2094   if (range->inverted)
2095     delta = - delta;
2096
2097   return delta;
2098 }
2099       
2100 static gboolean
2101 gtk_range_scroll_event (GtkWidget      *widget,
2102                         GdkEventScroll *event)
2103 {
2104   GtkRange *range = GTK_RANGE (widget);
2105
2106   if (GTK_WIDGET_REALIZED (range))
2107     {
2108       GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2109       gdouble delta;
2110       gboolean handled;
2111
2112       delta = _gtk_range_get_wheel_delta (range, event->direction);
2113
2114       g_signal_emit (range, signals[CHANGE_VALUE], 0,
2115                      GTK_SCROLL_JUMP, adj->value + delta,
2116                      &handled);
2117       
2118       /* Policy DELAYED makes sense with scroll events,
2119        * but DISCONTINUOUS doesn't, so we update immediately
2120        * for DISCONTINUOUS
2121        */
2122       if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2123         gtk_range_update_value (range);
2124     }
2125
2126   return TRUE;
2127 }
2128
2129 static gboolean
2130 gtk_range_motion_notify (GtkWidget      *widget,
2131                          GdkEventMotion *event)
2132 {
2133   GtkRange *range;
2134   gint x, y;
2135
2136   range = GTK_RANGE (widget);
2137
2138   gdk_window_get_pointer (range->event_window, &x, &y, NULL);
2139   
2140   range->layout->mouse_x = x;
2141   range->layout->mouse_y = y;
2142
2143   if (gtk_range_update_mouse_location (range))
2144     gtk_widget_queue_draw (widget);
2145
2146   if (range->layout->grab_location == MOUSE_SLIDER)
2147     update_slider_position (range, x, y);
2148
2149   /* We handled the event if the mouse was in the range_rect */
2150   return range->layout->mouse_location != MOUSE_OUTSIDE;
2151 }
2152
2153 static gboolean
2154 gtk_range_enter_notify (GtkWidget        *widget,
2155                         GdkEventCrossing *event)
2156 {
2157   GtkRange *range = GTK_RANGE (widget);
2158
2159   range->layout->mouse_x = event->x;
2160   range->layout->mouse_y = event->y;
2161
2162   if (gtk_range_update_mouse_location (range))
2163     gtk_widget_queue_draw (widget);
2164   
2165   return TRUE;
2166 }
2167
2168 static gboolean
2169 gtk_range_leave_notify (GtkWidget        *widget,
2170                         GdkEventCrossing *event)
2171 {
2172   GtkRange *range = GTK_RANGE (widget);
2173
2174   range->layout->mouse_x = -1;
2175   range->layout->mouse_y = -1;
2176
2177   if (gtk_range_update_mouse_location (range))
2178     gtk_widget_queue_draw (widget);
2179   
2180   return TRUE;
2181 }
2182
2183 static void
2184 gtk_range_grab_notify (GtkWidget *widget,
2185                        gboolean   was_grabbed)
2186 {
2187   if (!was_grabbed)
2188     stop_scrolling (GTK_RANGE (widget));
2189 }
2190
2191 static void
2192 gtk_range_state_changed (GtkWidget    *widget,
2193                          GtkStateType  previous_state)
2194 {
2195   if (!GTK_WIDGET_IS_SENSITIVE (widget)) 
2196     stop_scrolling (GTK_RANGE (widget));
2197 }
2198
2199 #define check_rectangle(rectangle1, rectangle2)              \
2200   {                                                          \
2201     if (rectangle1.x != rectangle2.x) return TRUE;           \
2202     if (rectangle1.y != rectangle2.y) return TRUE;           \
2203     if (rectangle1.width  != rectangle2.width)  return TRUE; \
2204     if (rectangle1.height != rectangle2.height) return TRUE; \
2205   }
2206
2207 static gboolean
2208 layout_changed (GtkRangeLayout *layout1, 
2209                 GtkRangeLayout *layout2)
2210 {
2211   check_rectangle (layout1->slider, layout2->slider);
2212   check_rectangle (layout1->trough, layout2->trough);
2213   check_rectangle (layout1->stepper_a, layout2->stepper_a);
2214   check_rectangle (layout1->stepper_d, layout2->stepper_d);
2215   check_rectangle (layout1->stepper_b, layout2->stepper_b);
2216   check_rectangle (layout1->stepper_c, layout2->stepper_c);
2217
2218   if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2219   if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2220
2221   return FALSE;
2222 }
2223
2224 static void
2225 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2226                               gpointer       data)
2227 {
2228   GtkRange *range = GTK_RANGE (data);
2229   /* create a copy of the layout */
2230   GtkRangeLayout layout = *range->layout;
2231
2232   range->need_recalc = TRUE;
2233   gtk_range_calc_layout (range, range->adjustment->value);
2234   
2235   /* now check whether the layout changed  */
2236   if (layout_changed (range->layout, &layout))
2237     gtk_widget_queue_draw (GTK_WIDGET (range));
2238
2239   /* Note that we don't round off to range->round_digits here.
2240    * that's because it's really broken to change a value
2241    * in response to a change signal on that value; round_digits
2242    * is therefore defined to be a filter on what the GtkRange
2243    * can input into the adjustment, not a filter that the GtkRange
2244    * will enforce on the adjustment.
2245    */
2246 }
2247
2248 static void
2249 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2250                                     gpointer       data)
2251 {
2252   GtkRange *range = GTK_RANGE (data);
2253   /* create a copy of the layout */
2254   GtkRangeLayout layout = *range->layout;
2255
2256   range->need_recalc = TRUE;
2257   gtk_range_calc_layout (range, range->adjustment->value);
2258   
2259   /* now check whether the layout changed  */
2260   if (layout_changed (range->layout, &layout))
2261     {
2262       gtk_widget_queue_draw (GTK_WIDGET (range));
2263       
2264       /* This is so we don't lag the widget being scrolled. */
2265       if (GTK_WIDGET_REALIZED (range))
2266         gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2267     }
2268   
2269   /* Note that we don't round off to range->round_digits here.
2270    * that's because it's really broken to change a value
2271    * in response to a change signal on that value; round_digits
2272    * is therefore defined to be a filter on what the GtkRange
2273    * can input into the adjustment, not a filter that the GtkRange
2274    * will enforce on the adjustment.
2275    */
2276
2277   g_signal_emit (range, signals[VALUE_CHANGED], 0);
2278 }
2279
2280 static void
2281 gtk_range_style_set (GtkWidget *widget,
2282                      GtkStyle  *previous_style)
2283 {
2284   GtkRange *range = GTK_RANGE (widget);
2285
2286   range->need_recalc = TRUE;
2287
2288   (* GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set) (widget, previous_style);
2289 }
2290
2291 static void
2292 step_back (GtkRange *range)
2293 {
2294   gdouble newval;
2295   gboolean handled;
2296   
2297   newval = range->adjustment->value - range->adjustment->step_increment;
2298   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2299                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2300 }
2301
2302 static void
2303 step_forward (GtkRange *range)
2304 {
2305   gdouble newval;
2306   gboolean handled;
2307
2308   newval = range->adjustment->value + range->adjustment->step_increment;
2309   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2310                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
2311 }
2312
2313
2314 static void
2315 page_back (GtkRange *range)
2316 {
2317   gdouble newval;
2318   gboolean handled;
2319
2320   newval = range->adjustment->value - range->adjustment->page_increment;
2321   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2322                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2323 }
2324
2325 static void
2326 page_forward (GtkRange *range)
2327 {
2328   gdouble newval;
2329   gboolean handled;
2330
2331   newval = range->adjustment->value + range->adjustment->page_increment;
2332   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2333                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2334 }
2335
2336 static void
2337 scroll_begin (GtkRange *range)
2338 {
2339   gboolean handled;
2340   g_signal_emit (range, signals[CHANGE_VALUE], 0,
2341                  GTK_SCROLL_START, range->adjustment->lower,
2342                  &handled);
2343 }
2344
2345 static void
2346 scroll_end (GtkRange *range)
2347 {
2348   gdouble newval;
2349   gboolean handled;
2350
2351   newval = range->adjustment->upper - range->adjustment->page_size;
2352   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2353                  &handled);
2354 }
2355
2356 static void
2357 gtk_range_scroll (GtkRange     *range,
2358                   GtkScrollType scroll)
2359 {
2360   switch (scroll)
2361     {
2362     case GTK_SCROLL_STEP_LEFT:
2363       if (should_invert (range))
2364         step_forward (range);
2365       else
2366         step_back (range);
2367       break;
2368                     
2369     case GTK_SCROLL_STEP_UP:
2370       if (should_invert (range))
2371         step_forward (range);
2372       else
2373         step_back (range);
2374       break;
2375
2376     case GTK_SCROLL_STEP_RIGHT:
2377       if (should_invert (range))
2378         step_back (range);
2379       else
2380         step_forward (range);
2381       break;
2382                     
2383     case GTK_SCROLL_STEP_DOWN:
2384       if (should_invert (range))
2385         step_back (range);
2386       else
2387         step_forward (range);
2388       break;
2389                   
2390     case GTK_SCROLL_STEP_BACKWARD:
2391       step_back (range);
2392       break;
2393                   
2394     case GTK_SCROLL_STEP_FORWARD:
2395       step_forward (range);
2396       break;
2397
2398     case GTK_SCROLL_PAGE_LEFT:
2399       if (should_invert (range))
2400         page_forward (range);
2401       else
2402         page_back (range);
2403       break;
2404                     
2405     case GTK_SCROLL_PAGE_UP:
2406       if (should_invert (range))
2407         page_forward (range);
2408       else
2409         page_back (range);
2410       break;
2411
2412     case GTK_SCROLL_PAGE_RIGHT:
2413       if (should_invert (range))
2414         page_back (range);
2415       else
2416         page_forward (range);
2417       break;
2418                     
2419     case GTK_SCROLL_PAGE_DOWN:
2420       if (should_invert (range))
2421         page_back (range);
2422       else
2423         page_forward (range);
2424       break;
2425                   
2426     case GTK_SCROLL_PAGE_BACKWARD:
2427       page_back (range);
2428       break;
2429                   
2430     case GTK_SCROLL_PAGE_FORWARD:
2431       page_forward (range);
2432       break;
2433
2434     case GTK_SCROLL_START:
2435       scroll_begin (range);
2436       break;
2437
2438     case GTK_SCROLL_END:
2439       scroll_end (range);
2440       break;
2441
2442     case GTK_SCROLL_JUMP:
2443       /* Used by CList, range doesn't use it. */
2444       break;
2445
2446     case GTK_SCROLL_NONE:
2447       break;
2448     }
2449 }
2450
2451 static void
2452 gtk_range_move_slider (GtkRange     *range,
2453                        GtkScrollType scroll)
2454 {
2455   gboolean cursor_only;
2456
2457   g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2458                 "gtk-keynav-cursor-only", &cursor_only,
2459                 NULL);
2460
2461   if (cursor_only)
2462     {
2463       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2464
2465       if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2466         {
2467           if (scroll == GTK_SCROLL_STEP_UP ||
2468               scroll == GTK_SCROLL_STEP_DOWN)
2469             {
2470               if (toplevel)
2471                 gtk_widget_child_focus (toplevel,
2472                                         scroll == GTK_SCROLL_STEP_UP ?
2473                                         GTK_DIR_UP : GTK_DIR_DOWN);
2474               return;
2475             }
2476         }
2477       else
2478         {
2479           if (scroll == GTK_SCROLL_STEP_LEFT ||
2480               scroll == GTK_SCROLL_STEP_RIGHT)
2481             {
2482               if (toplevel)
2483                 gtk_widget_child_focus (toplevel,
2484                                         scroll == GTK_SCROLL_STEP_LEFT ?
2485                                         GTK_DIR_LEFT : GTK_DIR_RIGHT);
2486               return;
2487             }
2488         }
2489     }
2490
2491   gtk_range_scroll (range, scroll);
2492
2493   /* Policy DELAYED makes sense with key events,
2494    * but DISCONTINUOUS doesn't, so we update immediately
2495    * for DISCONTINUOUS
2496    */
2497   if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2498     gtk_range_update_value (range);
2499 }
2500
2501 static void
2502 gtk_range_get_props (GtkRange  *range,
2503                      gint      *slider_width,
2504                      gint      *stepper_size,
2505                      gint      *focus_width,
2506                      gint      *trough_border,
2507                      gint      *stepper_spacing,
2508                      gboolean  *trough_under_steppers,
2509                      gint      *arrow_displacement_x,
2510                      gint      *arrow_displacement_y)
2511 {
2512   GtkWidget *widget =  GTK_WIDGET (range);
2513   gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
2514   gint tmp_stepper_spacing, tmp_trough_under_steppers;
2515   gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2516   
2517   gtk_widget_style_get (widget,
2518                         "slider-width", &tmp_slider_width,
2519                         "trough-border", &tmp_trough_border,
2520                         "stepper-size", &tmp_stepper_size,
2521                         "stepper-spacing", &tmp_stepper_spacing,
2522                         "trough-under-steppers", &tmp_trough_under_steppers,
2523                         "arrow-displacement-x", &tmp_arrow_displacement_x,
2524                         "arrow-displacement-y", &tmp_arrow_displacement_y,
2525                         NULL);
2526
2527   if (tmp_stepper_spacing > 0)
2528     tmp_trough_under_steppers = FALSE;
2529
2530   if (GTK_WIDGET_CAN_FOCUS (range))
2531     {
2532       gint focus_line_width;
2533       gint focus_padding;
2534       
2535       gtk_widget_style_get (GTK_WIDGET (range),
2536                             "focus-line-width", &focus_line_width,
2537                             "focus-padding", &focus_padding,
2538                             NULL);
2539
2540       tmp_focus_width = focus_line_width + focus_padding;
2541     }
2542   else
2543     {
2544       tmp_focus_width = 0;
2545     }
2546   
2547   if (slider_width)
2548     *slider_width = tmp_slider_width;
2549
2550   if (focus_width)
2551     *focus_width = tmp_focus_width;
2552
2553   if (trough_border)
2554     *trough_border = tmp_trough_border;
2555
2556   if (stepper_size)
2557     *stepper_size = tmp_stepper_size;
2558
2559   if (stepper_spacing)
2560     *stepper_spacing = tmp_stepper_spacing;
2561
2562   if (trough_under_steppers)
2563     *trough_under_steppers = tmp_trough_under_steppers;
2564
2565   if (arrow_displacement_x)
2566     *arrow_displacement_x = tmp_arrow_displacement_x;
2567
2568   if (arrow_displacement_y)
2569     *arrow_displacement_y = tmp_arrow_displacement_y;
2570 }
2571
2572 #define POINT_IN_RECT(xcoord, ycoord, rect) \
2573  ((xcoord) >= (rect).x &&                   \
2574   (xcoord) <  ((rect).x + (rect).width) &&  \
2575   (ycoord) >= (rect).y &&                   \
2576   (ycoord) <  ((rect).y + (rect).height))
2577
2578 /* Update mouse location, return TRUE if it changes */
2579 static gboolean
2580 gtk_range_update_mouse_location (GtkRange *range)
2581 {
2582   gint x, y;
2583   MouseLocation old;
2584   GtkWidget *widget;
2585
2586   widget = GTK_WIDGET (range);
2587   
2588   old = range->layout->mouse_location;
2589   
2590   x = range->layout->mouse_x;
2591   y = range->layout->mouse_y;
2592
2593   if (range->layout->grab_location != MOUSE_OUTSIDE)
2594     range->layout->mouse_location = range->layout->grab_location;
2595   else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
2596     range->layout->mouse_location = MOUSE_STEPPER_A;
2597   else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
2598     range->layout->mouse_location = MOUSE_STEPPER_B;
2599   else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
2600     range->layout->mouse_location = MOUSE_STEPPER_C;
2601   else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
2602     range->layout->mouse_location = MOUSE_STEPPER_D;
2603   else if (POINT_IN_RECT (x, y, range->layout->slider))
2604     range->layout->mouse_location = MOUSE_SLIDER;
2605   else if (POINT_IN_RECT (x, y, range->layout->trough))
2606     range->layout->mouse_location = MOUSE_TROUGH;
2607   else if (POINT_IN_RECT (x, y, widget->allocation))
2608     range->layout->mouse_location = MOUSE_WIDGET;
2609   else
2610     range->layout->mouse_location = MOUSE_OUTSIDE;
2611
2612   return old != range->layout->mouse_location;
2613 }
2614
2615 /* Clamp rect, border inside widget->allocation, such that we prefer
2616  * to take space from border not rect in all directions, and prefer to
2617  * give space to border over rect in one direction.
2618  */
2619 static void
2620 clamp_dimensions (GtkWidget    *widget,
2621                   GdkRectangle *rect,
2622                   GtkBorder    *border,
2623                   gboolean      border_expands_horizontally)
2624 {
2625   gint extra, shortage;
2626   
2627   g_return_if_fail (rect->x == 0);
2628   g_return_if_fail (rect->y == 0);  
2629   g_return_if_fail (rect->width >= 0);
2630   g_return_if_fail (rect->height >= 0);
2631
2632   /* Width */
2633   
2634   extra = widget->allocation.width - border->left - border->right - rect->width;
2635   if (extra > 0)
2636     {
2637       if (border_expands_horizontally)
2638         {
2639           border->left += extra / 2;
2640           border->right += extra / 2 + extra % 2;
2641         }
2642       else
2643         {
2644           rect->width += extra;
2645         }
2646     }
2647   
2648   /* See if we can fit rect, if not kill the border */
2649   shortage = rect->width - widget->allocation.width;
2650   if (shortage > 0)
2651     {
2652       rect->width = widget->allocation.width;
2653       /* lose the border */
2654       border->left = 0;
2655       border->right = 0;
2656     }
2657   else
2658     {
2659       /* See if we can fit rect with borders */
2660       shortage = rect->width + border->left + border->right -
2661         widget->allocation.width;
2662       if (shortage > 0)
2663         {
2664           /* Shrink borders */
2665           border->left -= shortage / 2;
2666           border->right -= shortage / 2 + shortage % 2;
2667         }
2668     }
2669
2670   /* Height */
2671   
2672   extra = widget->allocation.height - border->top - border->bottom - rect->height;
2673   if (extra > 0)
2674     {
2675       if (border_expands_horizontally)
2676         {
2677           /* don't expand border vertically */
2678           rect->height += extra;
2679         }
2680       else
2681         {
2682           border->top += extra / 2;
2683           border->bottom += extra / 2 + extra % 2;
2684         }
2685     }
2686   
2687   /* See if we can fit rect, if not kill the border */
2688   shortage = rect->height - widget->allocation.height;
2689   if (shortage > 0)
2690     {
2691       rect->height = widget->allocation.height;
2692       /* lose the border */
2693       border->top = 0;
2694       border->bottom = 0;
2695     }
2696   else
2697     {
2698       /* See if we can fit rect with borders */
2699       shortage = rect->height + border->top + border->bottom -
2700         widget->allocation.height;
2701       if (shortage > 0)
2702         {
2703           /* Shrink borders */
2704           border->top -= shortage / 2;
2705           border->bottom -= shortage / 2 + shortage % 2;
2706         }
2707     }
2708 }
2709
2710 static void
2711 gtk_range_calc_request (GtkRange      *range,
2712                         gint           slider_width,
2713                         gint           stepper_size,
2714                         gint           focus_width,
2715                         gint           trough_border,
2716                         gint           stepper_spacing,
2717                         GdkRectangle  *range_rect,
2718                         GtkBorder     *border,
2719                         gint          *n_steppers_p,
2720                         gboolean      *has_steppers_ab,
2721                         gboolean      *has_steppers_cd,
2722                         gint          *slider_length_p)
2723 {
2724   gint slider_length;
2725   gint n_steppers;
2726   gint n_steppers_ab;
2727   gint n_steppers_cd;
2728
2729   border->left = 0;
2730   border->right = 0;
2731   border->top = 0;
2732   border->bottom = 0;
2733
2734   if (GTK_RANGE_GET_CLASS (range)->get_range_border)
2735     (* GTK_RANGE_GET_CLASS (range)->get_range_border) (range, border);
2736
2737   n_steppers_ab = 0;
2738   n_steppers_cd = 0;
2739
2740   if (range->has_stepper_a)
2741     n_steppers_ab += 1;
2742   if (range->has_stepper_b)
2743     n_steppers_ab += 1;
2744   if (range->has_stepper_c)
2745     n_steppers_cd += 1;
2746   if (range->has_stepper_d)
2747     n_steppers_cd += 1;
2748
2749   n_steppers = n_steppers_ab + n_steppers_cd;
2750
2751   slider_length = range->min_slider_size;
2752
2753   range_rect->x = 0;
2754   range_rect->y = 0;
2755   
2756   /* We never expand to fill available space in the small dimension
2757    * (i.e. vertical scrollbars are always a fixed width)
2758    */
2759   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2760     {
2761       range_rect->width = (focus_width + trough_border) * 2 + slider_width;
2762       range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2763
2764       if (n_steppers_ab > 0)
2765         range_rect->height += stepper_spacing;
2766
2767       if (n_steppers_cd > 0)
2768         range_rect->height += stepper_spacing;
2769     }
2770   else
2771     {
2772       range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
2773       range_rect->height = (focus_width + trough_border) * 2 + slider_width;
2774
2775       if (n_steppers_ab > 0)
2776         range_rect->width += stepper_spacing;
2777
2778       if (n_steppers_cd > 0)
2779         range_rect->width += stepper_spacing;
2780     }
2781
2782   if (n_steppers_p)
2783     *n_steppers_p = n_steppers;
2784
2785   if (has_steppers_ab)
2786     *has_steppers_ab = (n_steppers_ab > 0);
2787
2788   if (has_steppers_cd)
2789     *has_steppers_cd = (n_steppers_cd > 0);
2790
2791   if (slider_length_p)
2792     *slider_length_p = slider_length;
2793 }
2794
2795 static void
2796 gtk_range_calc_layout (GtkRange *range,
2797                        gdouble   adjustment_value)
2798 {
2799   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
2800   gint slider_length;
2801   GtkBorder border;
2802   gint n_steppers;
2803   gboolean has_steppers_ab;
2804   gboolean has_steppers_cd;
2805   gboolean trough_under_steppers;
2806   GdkRectangle range_rect;
2807   GtkRangeLayout *layout;
2808   GtkWidget *widget;
2809   
2810   if (!range->need_recalc)
2811     return;
2812
2813   /* If we have a too-small allocation, we prefer the steppers over
2814    * the trough/slider, probably the steppers are a more useful
2815    * feature in small spaces.
2816    *
2817    * Also, we prefer to draw the range itself rather than the border
2818    * areas if there's a conflict, since the borders will be decoration
2819    * not controls. Though this depends on subclasses cooperating by
2820    * not drawing on range->range_rect.
2821    */
2822
2823   widget = GTK_WIDGET (range);
2824   layout = range->layout;
2825   
2826   gtk_range_get_props (range,
2827                        &slider_width, &stepper_size,
2828                        &focus_width, &trough_border,
2829                        &stepper_spacing, &trough_under_steppers,
2830                        NULL, NULL);
2831
2832   gtk_range_calc_request (range, 
2833                           slider_width, stepper_size,
2834                           focus_width, trough_border, stepper_spacing,
2835                           &range_rect, &border, &n_steppers,
2836                           &has_steppers_ab, &has_steppers_cd, &slider_length);
2837   
2838   /* We never expand to fill available space in the small dimension
2839    * (i.e. vertical scrollbars are always a fixed width)
2840    */
2841   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2842     {
2843       clamp_dimensions (widget, &range_rect, &border, TRUE);
2844     }
2845   else
2846     {
2847       clamp_dimensions (widget, &range_rect, &border, FALSE);
2848     }
2849   
2850   range_rect.x = border.left;
2851   range_rect.y = border.top;
2852   
2853   range->range_rect = range_rect;
2854   
2855   if (range->orientation == GTK_ORIENTATION_VERTICAL)
2856     {
2857       gint stepper_width, stepper_height;
2858
2859       /* Steppers are the width of the range, and stepper_size in
2860        * height, or if we don't have enough height, divided equally
2861        * among available space.
2862        */
2863       stepper_width = range_rect.width - focus_width * 2;
2864
2865       if (trough_under_steppers)
2866         stepper_width -= trough_border * 2;
2867
2868       if (stepper_width < 1)
2869         stepper_width = range_rect.width; /* screw the trough border */
2870
2871       if (n_steppers == 0)
2872         stepper_height = 0; /* avoid divide by n_steppers */
2873       else
2874         stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
2875
2876       /* Stepper A */
2877       
2878       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
2879       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
2880
2881       if (range->has_stepper_a)
2882         {
2883           layout->stepper_a.width = stepper_width;
2884           layout->stepper_a.height = stepper_height;
2885         }
2886       else
2887         {
2888           layout->stepper_a.width = 0;
2889           layout->stepper_a.height = 0;
2890         }
2891
2892       /* Stepper B */
2893       
2894       layout->stepper_b.x = layout->stepper_a.x;
2895       layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
2896
2897       if (range->has_stepper_b)
2898         {
2899           layout->stepper_b.width = stepper_width;
2900           layout->stepper_b.height = stepper_height;
2901         }
2902       else
2903         {
2904           layout->stepper_b.width = 0;
2905           layout->stepper_b.height = 0;
2906         }
2907
2908       /* Stepper D */
2909
2910       if (range->has_stepper_d)
2911         {
2912           layout->stepper_d.width = stepper_width;
2913           layout->stepper_d.height = stepper_height;
2914         }
2915       else
2916         {
2917           layout->stepper_d.width = 0;
2918           layout->stepper_d.height = 0;
2919         }
2920       
2921       layout->stepper_d.x = layout->stepper_a.x;
2922       layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
2923
2924       /* Stepper C */
2925
2926       if (range->has_stepper_c)
2927         {
2928           layout->stepper_c.width = stepper_width;
2929           layout->stepper_c.height = stepper_height;
2930         }
2931       else
2932         {
2933           layout->stepper_c.width = 0;
2934           layout->stepper_c.height = 0;
2935         }
2936       
2937       layout->stepper_c.x = layout->stepper_a.x;
2938       layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
2939
2940       /* Now the trough is the remaining space between steppers B and C,
2941        * if any, minus spacing
2942        */
2943       layout->trough.x = range_rect.x;
2944       layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
2945       layout->trough.width = range_rect.width;
2946       layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
2947
2948       /* Slider fits into the trough, with stepper_spacing on either side,
2949        * and the size/position based on the adjustment or fixed, depending.
2950        */
2951       layout->slider.x = layout->trough.x + focus_width + trough_border;
2952       layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
2953
2954       /* Compute slider position/length */
2955       {
2956         gint y, bottom, top, height;
2957         
2958         top = layout->trough.y;
2959         bottom = layout->trough.y + layout->trough.height;
2960
2961         if (! trough_under_steppers)
2962           {
2963             top += trough_border;
2964             bottom -= trough_border;
2965           }
2966
2967         /* slider height is the fraction (page_size /
2968          * total_adjustment_range) times the trough height in pixels
2969          */
2970
2971         if (range->adjustment->upper - range->adjustment->lower != 0)
2972           height = ((bottom - top) * (range->adjustment->page_size /
2973                                        (range->adjustment->upper - range->adjustment->lower)));
2974         else
2975           height = range->min_slider_size;
2976         
2977         if (height < range->min_slider_size ||
2978             range->slider_size_fixed)
2979           height = range->min_slider_size;
2980
2981         height = MIN (height, layout->trough.height);
2982         
2983         y = top;
2984         
2985         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
2986           y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
2987                                           (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
2988         
2989         y = CLAMP (y, top, bottom);
2990         
2991         if (should_invert (range))
2992           y = bottom - (y - top + height);
2993         
2994         layout->slider.y = y;
2995         layout->slider.height = height;
2996
2997         /* These are publically exported */
2998         range->slider_start = layout->slider.y;
2999         range->slider_end = layout->slider.y + layout->slider.height;
3000       }
3001     }
3002   else
3003     {
3004       gint stepper_width, stepper_height;
3005
3006       /* Steppers are the height of the range, and stepper_size in
3007        * width, or if we don't have enough width, divided equally
3008        * among available space.
3009        */
3010       stepper_height = range_rect.height + focus_width * 2;
3011
3012       if (trough_under_steppers)
3013         stepper_height -= trough_border * 2;
3014
3015       if (stepper_height < 1)
3016         stepper_height = range_rect.height; /* screw the trough border */
3017
3018       if (n_steppers == 0)
3019         stepper_width = 0; /* avoid divide by n_steppers */
3020       else
3021         stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3022
3023       /* Stepper A */
3024       
3025       layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3026       layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3027
3028       if (range->has_stepper_a)
3029         {
3030           layout->stepper_a.width = stepper_width;
3031           layout->stepper_a.height = stepper_height;
3032         }
3033       else
3034         {
3035           layout->stepper_a.width = 0;
3036           layout->stepper_a.height = 0;
3037         }
3038
3039       /* Stepper B */
3040       
3041       layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3042       layout->stepper_b.y = layout->stepper_a.y;
3043
3044       if (range->has_stepper_b)
3045         {
3046           layout->stepper_b.width = stepper_width;
3047           layout->stepper_b.height = stepper_height;
3048         }
3049       else
3050         {
3051           layout->stepper_b.width = 0;
3052           layout->stepper_b.height = 0;
3053         }
3054
3055       /* Stepper D */
3056
3057       if (range->has_stepper_d)
3058         {
3059           layout->stepper_d.width = stepper_width;
3060           layout->stepper_d.height = stepper_height;
3061         }
3062       else
3063         {
3064           layout->stepper_d.width = 0;
3065           layout->stepper_d.height = 0;
3066         }
3067
3068       layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3069       layout->stepper_d.y = layout->stepper_a.y;
3070
3071
3072       /* Stepper C */
3073
3074       if (range->has_stepper_c)
3075         {
3076           layout->stepper_c.width = stepper_width;
3077           layout->stepper_c.height = stepper_height;
3078         }
3079       else
3080         {
3081           layout->stepper_c.width = 0;
3082           layout->stepper_c.height = 0;
3083         }
3084       
3085       layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3086       layout->stepper_c.y = layout->stepper_a.y;
3087
3088       /* Now the trough is the remaining space between steppers B and C,
3089        * if any
3090        */
3091       layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3092       layout->trough.y = range_rect.y;
3093
3094       layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3095       layout->trough.height = range_rect.height;
3096
3097       /* Slider fits into the trough, with stepper_spacing on either side,
3098        * and the size/position based on the adjustment or fixed, depending.
3099        */
3100       layout->slider.y = layout->trough.y + focus_width + trough_border;
3101       layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3102
3103       /* Compute slider position/length */
3104       {
3105         gint x, left, right, width;
3106         
3107         left = layout->trough.x;
3108         right = layout->trough.x + layout->trough.width;
3109
3110         if (! trough_under_steppers)
3111           {
3112             left += trough_border;
3113             right -= trough_border;
3114           }
3115
3116         /* slider width is the fraction (page_size /
3117          * total_adjustment_range) times the trough width in pixels
3118          */
3119         
3120         if (range->adjustment->upper - range->adjustment->lower != 0)
3121           width = ((right - left) * (range->adjustment->page_size /
3122                                    (range->adjustment->upper - range->adjustment->lower)));
3123         else
3124           width = range->min_slider_size;
3125         
3126         if (width < range->min_slider_size ||
3127             range->slider_size_fixed)
3128           width = range->min_slider_size;
3129         
3130         width = MIN (width, layout->trough.width);
3131         
3132         x = left;
3133         
3134         if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3135           x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3136                                          (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3137         
3138         x = CLAMP (x, left, right);
3139         
3140         if (should_invert (range))
3141           x = right - (x - left + width);
3142         
3143         layout->slider.x = x;
3144         layout->slider.width = width;
3145
3146         /* These are publically exported */
3147         range->slider_start = layout->slider.x;
3148         range->slider_end = layout->slider.x + layout->slider.width;
3149       }
3150     }
3151   
3152   gtk_range_update_mouse_location (range);
3153
3154   switch (range->layout->upper_sensitivity)
3155     {
3156     case GTK_SENSITIVITY_AUTO:
3157       range->layout->upper_sensitive =
3158         (range->adjustment->value <
3159          (range->adjustment->upper - range->adjustment->page_size));
3160       break;
3161
3162     case GTK_SENSITIVITY_ON:
3163       range->layout->upper_sensitive = TRUE;
3164       break;
3165
3166     case GTK_SENSITIVITY_OFF:
3167       range->layout->upper_sensitive = FALSE;
3168       break;
3169     }
3170
3171   switch (range->layout->lower_sensitivity)
3172     {
3173     case GTK_SENSITIVITY_AUTO:
3174       range->layout->lower_sensitive =
3175         (range->adjustment->value > range->adjustment->lower);
3176       break;
3177
3178     case GTK_SENSITIVITY_ON:
3179       range->layout->lower_sensitive = TRUE;
3180       break;
3181
3182     case GTK_SENSITIVITY_OFF:
3183       range->layout->lower_sensitive = FALSE;
3184       break;
3185     }
3186 }
3187
3188 static GdkRectangle*
3189 get_area (GtkRange     *range,
3190           MouseLocation location)
3191 {
3192   switch (location)
3193     {
3194     case MOUSE_STEPPER_A:
3195       return &range->layout->stepper_a;
3196     case MOUSE_STEPPER_B:
3197       return &range->layout->stepper_b;
3198     case MOUSE_STEPPER_C:
3199       return &range->layout->stepper_c;
3200     case MOUSE_STEPPER_D:
3201       return &range->layout->stepper_d;
3202     case MOUSE_TROUGH:
3203       return &range->layout->trough;
3204     case MOUSE_SLIDER:
3205       return &range->layout->slider;
3206     case MOUSE_WIDGET:
3207     case MOUSE_OUTSIDE:
3208       break;
3209     }
3210
3211   g_warning (G_STRLOC": bug");
3212   return NULL;
3213 }
3214
3215 static gboolean
3216 gtk_range_real_change_value (GtkRange     *range,
3217                              GtkScrollType scroll,
3218                              gdouble       value)
3219 {
3220   /* potentially adjust the bounds _before we clamp */
3221   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3222
3223   if (range->layout->restrict_to_fill_level)
3224     value = MIN (value, MAX (range->adjustment->lower,
3225                              range->layout->fill_level));
3226
3227   value = CLAMP (value, range->adjustment->lower,
3228                  (range->adjustment->upper - range->adjustment->page_size));
3229
3230   if (range->round_digits >= 0)
3231     {
3232       gdouble power;
3233       gint i;
3234
3235       i = range->round_digits;
3236       power = 1;
3237       while (i--)
3238         power *= 10;
3239       
3240       value = floor ((value * power) + 0.5) / power;
3241     }
3242   
3243   if (range->adjustment->value != value)
3244     {
3245       range->need_recalc = TRUE;
3246
3247       gtk_widget_queue_draw (GTK_WIDGET (range));
3248       
3249       switch (range->update_policy)
3250         {
3251         case GTK_UPDATE_CONTINUOUS:
3252           gtk_adjustment_set_value (range->adjustment, value);
3253           break;
3254
3255           /* Delayed means we update after a period of inactivity */
3256         case GTK_UPDATE_DELAYED:
3257           gtk_range_reset_update_timer (range);
3258           /* FALL THRU */
3259
3260           /* Discontinuous means we update on button release */
3261         case GTK_UPDATE_DISCONTINUOUS:
3262           /* don't emit value_changed signal */
3263           range->adjustment->value = value;
3264           range->update_pending = TRUE;
3265           break;
3266         }
3267     }
3268   return FALSE;
3269 }
3270
3271 static void
3272 gtk_range_update_value (GtkRange *range)
3273 {
3274   gtk_range_remove_update_timer (range);
3275   
3276   if (range->update_pending)
3277     {
3278       gtk_adjustment_value_changed (range->adjustment);
3279
3280       range->update_pending = FALSE;
3281     }
3282 }
3283
3284 struct _GtkRangeStepTimer
3285 {
3286   guint timeout_id;
3287   GtkScrollType step;
3288 };
3289
3290 static gboolean
3291 second_timeout (gpointer data)
3292 {
3293   GtkRange *range;
3294
3295   range = GTK_RANGE (data);
3296   gtk_range_scroll (range, range->timer->step);
3297   
3298   return TRUE;
3299 }
3300
3301 static gboolean
3302 initial_timeout (gpointer data)
3303 {
3304   GtkRange    *range;
3305   GtkSettings *settings;
3306   guint        timeout;
3307
3308   settings = gtk_widget_get_settings (GTK_WIDGET (data));
3309   g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3310
3311   range = GTK_RANGE (data);
3312   range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3313                                             second_timeout,
3314                                             range);
3315   /* remove self */
3316   return FALSE;
3317 }
3318
3319 static void
3320 gtk_range_add_step_timer (GtkRange      *range,
3321                           GtkScrollType  step)
3322 {
3323   GtkSettings *settings;
3324   guint        timeout;
3325
3326   g_return_if_fail (range->timer == NULL);
3327   g_return_if_fail (step != GTK_SCROLL_NONE);
3328
3329   settings = gtk_widget_get_settings (GTK_WIDGET (range));
3330   g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3331
3332   range->timer = g_new (GtkRangeStepTimer, 1);
3333
3334   range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3335                                             initial_timeout,
3336                                             range);
3337   range->timer->step = step;
3338
3339   gtk_range_scroll (range, range->timer->step);
3340 }
3341
3342 static void
3343 gtk_range_remove_step_timer (GtkRange *range)
3344 {
3345   if (range->timer)
3346     {
3347       if (range->timer->timeout_id != 0)
3348         g_source_remove (range->timer->timeout_id);
3349
3350       g_free (range->timer);
3351
3352       range->timer = NULL;
3353     }
3354 }
3355
3356 static gboolean
3357 update_timeout (gpointer data)
3358 {
3359   GtkRange *range;
3360
3361   range = GTK_RANGE (data);
3362   gtk_range_update_value (range);
3363   range->update_timeout_id = 0;
3364
3365   /* self-remove */
3366   return FALSE;
3367 }
3368
3369 static void
3370 gtk_range_reset_update_timer (GtkRange *range)
3371 {
3372   gtk_range_remove_update_timer (range);
3373
3374   range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3375                                             update_timeout,
3376                                             range);
3377 }
3378
3379 static void
3380 gtk_range_remove_update_timer (GtkRange *range)
3381 {
3382   if (range->update_timeout_id != 0)
3383     {
3384       g_source_remove (range->update_timeout_id);
3385       range->update_timeout_id = 0;
3386     }
3387 }
3388
3389 #define __GTK_RANGE_C__
3390 #include "gtkaliasdef.c"