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