1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3 * Copyright (C) 2001 Red Hat, Inc.
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.
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.
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.
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/.
31 #include <gdk/gdkkeysyms.h>
34 #include "gtkmarshalers.h"
37 #include "gtkscrollbar.h"
38 #include "gtkprivate.h"
41 #define SCROLL_DELAY_FACTOR 5 /* Scroll repeat multiplier */
42 #define UPDATE_DELAY 300 /* Delay for queued update */
49 PROP_LOWER_STEPPER_SENSITIVITY,
50 PROP_UPPER_STEPPER_SENSITIVITY
69 MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
72 struct _GtkRangeLayout
74 /* These are in widget->window coordinates */
75 GdkRectangle stepper_a;
76 GdkRectangle stepper_b;
77 GdkRectangle stepper_c;
78 GdkRectangle stepper_d;
79 /* The trough rectangle is the area the thumb can slide in, not the
85 /* Layout-related state */
87 MouseLocation mouse_location;
88 /* last mouse coords we got, or -1 if mouse is outside the range */
91 /* "grabbed" mouse location, OUTSIDE for no grab */
92 MouseLocation grab_location;
93 gint grab_button; /* 0 if none */
95 /* Stepper sensitivity */
96 GtkSensitivityType lower_sensitivity;
97 GtkSensitivityType upper_sensitivity;
101 static void gtk_range_set_property (GObject *object,
105 static void gtk_range_get_property (GObject *object,
109 static void gtk_range_destroy (GtkObject *object);
110 static void gtk_range_finalize (GObject *object);
111 static void gtk_range_size_request (GtkWidget *widget,
112 GtkRequisition *requisition);
113 static void gtk_range_size_allocate (GtkWidget *widget,
114 GtkAllocation *allocation);
115 static void gtk_range_realize (GtkWidget *widget);
116 static void gtk_range_unrealize (GtkWidget *widget);
117 static void gtk_range_map (GtkWidget *widget);
118 static void gtk_range_unmap (GtkWidget *widget);
119 static gint gtk_range_expose (GtkWidget *widget,
120 GdkEventExpose *event);
121 static gint gtk_range_button_press (GtkWidget *widget,
122 GdkEventButton *event);
123 static gint gtk_range_button_release (GtkWidget *widget,
124 GdkEventButton *event);
125 static gint gtk_range_motion_notify (GtkWidget *widget,
126 GdkEventMotion *event);
127 static gint gtk_range_enter_notify (GtkWidget *widget,
128 GdkEventCrossing *event);
129 static gint gtk_range_leave_notify (GtkWidget *widget,
130 GdkEventCrossing *event);
131 static gboolean gtk_range_grab_broken (GtkWidget *widget,
132 GdkEventGrabBroken *event);
133 static void gtk_range_grab_notify (GtkWidget *widget,
134 gboolean was_grabbed);
135 static void gtk_range_state_changed (GtkWidget *widget,
136 GtkStateType previous_state);
137 static gint gtk_range_scroll_event (GtkWidget *widget,
138 GdkEventScroll *event);
139 static void gtk_range_style_set (GtkWidget *widget,
140 GtkStyle *previous_style);
141 static void update_slider_position (GtkRange *range,
144 static void stop_scrolling (GtkRange *range);
148 static void gtk_range_move_slider (GtkRange *range,
149 GtkScrollType scroll);
152 static void gtk_range_scroll (GtkRange *range,
153 GtkScrollType scroll);
154 static gboolean gtk_range_update_mouse_location (GtkRange *range);
155 static void gtk_range_calc_layout (GtkRange *range,
156 gdouble adjustment_value);
157 static void gtk_range_get_props (GtkRange *range,
161 gint *stepper_spacing,
162 gint *arrow_displacement_x,
163 gint *arrow_displacement_y);
164 static void gtk_range_calc_request (GtkRange *range,
168 gint stepper_spacing,
169 GdkRectangle *range_rect,
172 gint *slider_length_p);
173 static void gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
175 static void gtk_range_adjustment_changed (GtkAdjustment *adjustment,
177 static void gtk_range_add_step_timer (GtkRange *range,
179 static void gtk_range_remove_step_timer (GtkRange *range);
180 static void gtk_range_reset_update_timer (GtkRange *range);
181 static void gtk_range_remove_update_timer (GtkRange *range);
182 static GdkRectangle* get_area (GtkRange *range,
183 MouseLocation location);
184 static gboolean gtk_range_real_change_value (GtkRange *range,
185 GtkScrollType scroll,
187 static void gtk_range_update_value (GtkRange *range);
188 static gboolean gtk_range_key_press (GtkWidget *range,
192 static guint signals[LAST_SIGNAL];
194 G_DEFINE_TYPE (GtkRange, gtk_range, GTK_TYPE_WIDGET);
197 gtk_range_class_init (GtkRangeClass *class)
199 GObjectClass *gobject_class;
200 GtkObjectClass *object_class;
201 GtkWidgetClass *widget_class;
203 gobject_class = G_OBJECT_CLASS (class);
204 object_class = (GtkObjectClass*) class;
205 widget_class = (GtkWidgetClass*) class;
207 gobject_class->set_property = gtk_range_set_property;
208 gobject_class->get_property = gtk_range_get_property;
209 gobject_class->finalize = gtk_range_finalize;
210 object_class->destroy = gtk_range_destroy;
212 widget_class->size_request = gtk_range_size_request;
213 widget_class->size_allocate = gtk_range_size_allocate;
214 widget_class->realize = gtk_range_realize;
215 widget_class->unrealize = gtk_range_unrealize;
216 widget_class->map = gtk_range_map;
217 widget_class->unmap = gtk_range_unmap;
218 widget_class->expose_event = gtk_range_expose;
219 widget_class->button_press_event = gtk_range_button_press;
220 widget_class->button_release_event = gtk_range_button_release;
221 widget_class->motion_notify_event = gtk_range_motion_notify;
222 widget_class->scroll_event = gtk_range_scroll_event;
223 widget_class->enter_notify_event = gtk_range_enter_notify;
224 widget_class->leave_notify_event = gtk_range_leave_notify;
225 widget_class->grab_broken_event = gtk_range_grab_broken;
226 widget_class->grab_notify = gtk_range_grab_notify;
227 widget_class->state_changed = gtk_range_state_changed;
228 widget_class->style_set = gtk_range_style_set;
229 widget_class->key_press_event = gtk_range_key_press;
231 class->move_slider = gtk_range_move_slider;
232 class->change_value = gtk_range_real_change_value;
234 class->slider_detail = "slider";
235 class->stepper_detail = "stepper";
237 signals[VALUE_CHANGED] =
238 g_signal_new (I_("value_changed"),
239 G_TYPE_FROM_CLASS (gobject_class),
241 G_STRUCT_OFFSET (GtkRangeClass, value_changed),
243 _gtk_marshal_NONE__NONE,
246 signals[ADJUST_BOUNDS] =
247 g_signal_new (I_("adjust_bounds"),
248 G_TYPE_FROM_CLASS (gobject_class),
250 G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
252 _gtk_marshal_VOID__DOUBLE,
256 signals[MOVE_SLIDER] =
257 g_signal_new (I_("move_slider"),
258 G_TYPE_FROM_CLASS (gobject_class),
259 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
260 G_STRUCT_OFFSET (GtkRangeClass, move_slider),
262 _gtk_marshal_VOID__ENUM,
264 GTK_TYPE_SCROLL_TYPE);
267 * GtkRange::change-value:
268 * @range: the range that received the signal.
269 * @scroll: the type of scroll action that was performed.
270 * @value: the new value resulting from the scroll action.
271 * @returns: %TRUE to prevent other handlers from being invoked for the
272 * signal. %FALSE to propagate the signal further.
274 * The ::change-value signal is emitted when a scroll action is
275 * performed on a range. It allows an application to determine the
276 * type of scroll event that occurred and the resultant new value.
277 * The application can handle the event itself and return %TRUE to
278 * prevent further processing. Or, by returning %FALSE, it can pass
279 * the event to other handlers until the default GTK+ handler is
282 * The value parameter is unrounded. An application that overrides
283 * the ::change-value signal is responsible for clamping the value to
284 * the desired number of decimal digits; the default GTK+ handler
285 * clamps the value based on @range->round_digits.
287 * It is not possible to use delayed update policies in an overridden
288 * ::change-value handler.
292 signals[CHANGE_VALUE] =
293 g_signal_new (I_("change_value"),
294 G_TYPE_FROM_CLASS (gobject_class),
296 G_STRUCT_OFFSET (GtkRangeClass, change_value),
297 _gtk_boolean_handled_accumulator, NULL,
298 _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
300 GTK_TYPE_SCROLL_TYPE,
303 g_object_class_install_property (gobject_class,
305 g_param_spec_enum ("update-policy",
307 P_("How the range should be updated on the screen"),
308 GTK_TYPE_UPDATE_TYPE,
309 GTK_UPDATE_CONTINUOUS,
310 GTK_PARAM_READWRITE));
312 g_object_class_install_property (gobject_class,
314 g_param_spec_object ("adjustment",
316 P_("The GtkAdjustment that contains the current value of this range object"),
318 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
320 g_object_class_install_property (gobject_class,
322 g_param_spec_boolean ("inverted",
324 P_("Invert direction slider moves to increase range value"),
326 GTK_PARAM_READWRITE));
328 g_object_class_install_property (gobject_class,
329 PROP_LOWER_STEPPER_SENSITIVITY,
330 g_param_spec_enum ("lower-stepper-sensitivity",
331 P_("Lower stepper sensitivity"),
332 P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
333 GTK_TYPE_SENSITIVITY_TYPE,
334 GTK_SENSITIVITY_AUTO,
335 GTK_PARAM_READWRITE));
337 g_object_class_install_property (gobject_class,
338 PROP_UPPER_STEPPER_SENSITIVITY,
339 g_param_spec_enum ("upper-stepper-sensitivity",
340 P_("Upper stepper sensitivity"),
341 P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
342 GTK_TYPE_SENSITIVITY_TYPE,
343 GTK_SENSITIVITY_AUTO,
344 GTK_PARAM_READWRITE));
346 gtk_widget_class_install_style_property (widget_class,
347 g_param_spec_int ("slider-width",
349 P_("Width of scrollbar or scale thumb"),
353 GTK_PARAM_READABLE));
354 gtk_widget_class_install_style_property (widget_class,
355 g_param_spec_int ("trough-border",
357 P_("Spacing between thumb/steppers and outer trough bevel"),
361 GTK_PARAM_READABLE));
362 gtk_widget_class_install_style_property (widget_class,
363 g_param_spec_int ("stepper-size",
365 P_("Length of step buttons at ends"),
369 GTK_PARAM_READABLE));
370 gtk_widget_class_install_style_property (widget_class,
371 g_param_spec_int ("stepper-spacing",
372 P_("Stepper Spacing"),
373 P_("Spacing between step buttons and thumb"),
377 GTK_PARAM_READABLE));
378 gtk_widget_class_install_style_property (widget_class,
379 g_param_spec_int ("arrow-displacement-x",
380 P_("Arrow X Displacement"),
381 P_("How far in the x direction to move the arrow when the button is depressed"),
385 GTK_PARAM_READABLE));
386 gtk_widget_class_install_style_property (widget_class,
387 g_param_spec_int ("arrow-displacement-y",
388 P_("Arrow Y Displacement"),
389 P_("How far in the y direction to move the arrow when the button is depressed"),
393 GTK_PARAM_READABLE));
395 gtk_widget_class_install_style_property (widget_class,
396 g_param_spec_boolean ("activate_slider",
397 P_("Draw slider ACTIVE during drag"),
398 P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
404 gtk_range_set_property (GObject *object,
411 range = GTK_RANGE (object);
415 case PROP_UPDATE_POLICY:
416 gtk_range_set_update_policy (range, g_value_get_enum (value));
418 case PROP_ADJUSTMENT:
419 gtk_range_set_adjustment (range, g_value_get_object (value));
422 gtk_range_set_inverted (range, g_value_get_boolean (value));
424 case PROP_LOWER_STEPPER_SENSITIVITY:
425 gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
427 case PROP_UPPER_STEPPER_SENSITIVITY:
428 gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
431 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
437 gtk_range_get_property (GObject *object,
444 range = GTK_RANGE (object);
448 case PROP_UPDATE_POLICY:
449 g_value_set_enum (value, range->update_policy);
451 case PROP_ADJUSTMENT:
452 g_value_set_object (value, range->adjustment);
455 g_value_set_boolean (value, range->inverted);
457 case PROP_LOWER_STEPPER_SENSITIVITY:
458 g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
460 case PROP_UPPER_STEPPER_SENSITIVITY:
461 g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
464 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
470 gtk_range_init (GtkRange *range)
472 GTK_WIDGET_SET_FLAGS (range, GTK_NO_WINDOW);
474 range->adjustment = NULL;
475 range->update_policy = GTK_UPDATE_CONTINUOUS;
476 range->inverted = FALSE;
477 range->flippable = FALSE;
478 range->min_slider_size = 1;
479 range->has_stepper_a = FALSE;
480 range->has_stepper_b = FALSE;
481 range->has_stepper_c = FALSE;
482 range->has_stepper_d = FALSE;
483 range->need_recalc = TRUE;
484 range->round_digits = -1;
485 range->layout = g_new0 (GtkRangeLayout, 1);
486 range->layout->mouse_location = MOUSE_OUTSIDE;
487 range->layout->mouse_x = -1;
488 range->layout->mouse_y = -1;
489 range->layout->grab_location = MOUSE_OUTSIDE;
490 range->layout->grab_button = 0;
491 range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
492 range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
497 * gtk_range_get_adjustment:
498 * @range: a #GtkRange
500 * Get the #GtkAdjustment which is the "model" object for #GtkRange.
501 * See gtk_range_set_adjustment() for details.
502 * The return value does not have a reference added, so should not
505 * Return value: a #GtkAdjustment
508 gtk_range_get_adjustment (GtkRange *range)
510 g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
512 if (!range->adjustment)
513 gtk_range_set_adjustment (range, NULL);
515 return range->adjustment;
519 * gtk_range_set_update_policy:
520 * @range: a #GtkRange
521 * @policy: update policy
523 * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
524 * anytime the range slider is moved, the range value will change and the
525 * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
526 * the value will be updated after a brief timeout where no slider motion
527 * occurs, so updates are spaced by a short time rather than
528 * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
529 * be updated when the user releases the button and ends the slider
534 gtk_range_set_update_policy (GtkRange *range,
535 GtkUpdateType policy)
537 g_return_if_fail (GTK_IS_RANGE (range));
539 if (range->update_policy != policy)
541 range->update_policy = policy;
542 g_object_notify (G_OBJECT (range), "update-policy");
547 * gtk_range_get_update_policy:
548 * @range: a #GtkRange
550 * Gets the update policy of @range. See gtk_range_set_update_policy().
552 * Return value: the current update policy
555 gtk_range_get_update_policy (GtkRange *range)
557 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
559 return range->update_policy;
563 * gtk_range_set_adjustment:
564 * @range: a #GtkRange
565 * @adjustment: a #GtkAdjustment
567 * Sets the adjustment to be used as the "model" object for this range
568 * widget. The adjustment indicates the current range value, the
569 * minimum and maximum range values, the step/page increments used
570 * for keybindings and scrolling, and the page size. The page size
571 * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
572 * indicates the size of the visible area of the widget being scrolled.
573 * The page size affects the size of the scrollbar slider.
577 gtk_range_set_adjustment (GtkRange *range,
578 GtkAdjustment *adjustment)
580 g_return_if_fail (GTK_IS_RANGE (range));
583 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
585 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
587 if (range->adjustment != adjustment)
589 if (range->adjustment)
591 g_signal_handlers_disconnect_by_func (range->adjustment,
592 gtk_range_adjustment_changed,
594 g_signal_handlers_disconnect_by_func (range->adjustment,
595 gtk_range_adjustment_value_changed,
597 g_object_unref (range->adjustment);
600 range->adjustment = adjustment;
601 g_object_ref_sink (adjustment);
603 g_signal_connect (adjustment, "changed",
604 G_CALLBACK (gtk_range_adjustment_changed),
606 g_signal_connect (adjustment, "value_changed",
607 G_CALLBACK (gtk_range_adjustment_value_changed),
610 gtk_range_adjustment_changed (adjustment, range);
611 g_object_notify (G_OBJECT (range), "adjustment");
616 * gtk_range_set_inverted:
617 * @range: a #GtkRange
618 * @setting: %TRUE to invert the range
620 * Ranges normally move from lower to higher values as the
621 * slider moves from top to bottom or left to right. Inverted
622 * ranges have higher values at the top or on the right rather than
623 * on the bottom or left.
627 gtk_range_set_inverted (GtkRange *range,
630 g_return_if_fail (GTK_IS_RANGE (range));
632 setting = setting != FALSE;
634 if (setting != range->inverted)
636 range->inverted = setting;
637 g_object_notify (G_OBJECT (range), "inverted");
638 gtk_widget_queue_resize (GTK_WIDGET (range));
643 * gtk_range_get_inverted:
644 * @range: a #GtkRange
646 * Gets the value set by gtk_range_set_inverted().
648 * Return value: %TRUE if the range is inverted
651 gtk_range_get_inverted (GtkRange *range)
653 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
655 return range->inverted;
659 * gtk_range_set_lower_stepper_sensitivity:
660 * @range: a #GtkRange
661 * @sensitivity: the lower stepper's sensitivity policy.
663 * Sets the sensitivity policy for the stepper that points to the
664 * 'lower' end of the GtkRange's adjustment.
669 gtk_range_set_lower_stepper_sensitivity (GtkRange *range,
670 GtkSensitivityType sensitivity)
672 g_return_if_fail (GTK_IS_RANGE (range));
674 if (range->layout->lower_sensitivity != sensitivity)
676 range->layout->lower_sensitivity = sensitivity;
677 gtk_widget_queue_draw (GTK_WIDGET (range));
678 g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
683 * gtk_range_get_lower_stepper_sensitivity:
684 * @range: a #GtkRange
686 * Gets the sensitivity policy for the stepper that points to the
687 * 'lower' end of the GtkRange's adjustment.
689 * Return value: The lower stepper's sensitivity policy.
694 gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
696 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
698 return range->layout->lower_sensitivity;
702 * gtk_range_set_upper_stepper_sensitivity:
703 * @range: a #GtkRange
704 * @sensitivity: the upper stepper's sensitivity policy.
706 * Sets the sensitivity policy for the stepper that points to the
707 * 'upper' end of the GtkRange's adjustment.
712 gtk_range_set_upper_stepper_sensitivity (GtkRange *range,
713 GtkSensitivityType sensitivity)
715 g_return_if_fail (GTK_IS_RANGE (range));
717 if (range->layout->upper_sensitivity != sensitivity)
719 range->layout->upper_sensitivity = sensitivity;
720 gtk_widget_queue_draw (GTK_WIDGET (range));
721 g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
726 * gtk_range_get_upper_stepper_sensitivity:
727 * @range: a #GtkRange
729 * Gets the sensitivity policy for the stepper that points to the
730 * 'upper' end of the GtkRange's adjustment.
732 * Return value: The upper stepper's sensitivity policy.
737 gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
739 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
741 return range->layout->upper_sensitivity;
745 * gtk_range_set_increments:
746 * @range: a #GtkRange
750 * Sets the step and page sizes for the range.
751 * The step size is used when the user clicks the #GtkScrollbar
752 * arrows or moves #GtkScale via arrow keys. The page size
753 * is used for example when moving via Page Up or Page Down keys.
757 gtk_range_set_increments (GtkRange *range,
761 g_return_if_fail (GTK_IS_RANGE (range));
763 range->adjustment->step_increment = step;
764 range->adjustment->page_increment = page;
766 gtk_adjustment_changed (range->adjustment);
770 * gtk_range_set_range:
771 * @range: a #GtkRange
772 * @min: minimum range value
773 * @max: maximum range value
775 * Sets the allowable values in the #GtkRange, and clamps the range
776 * value to be between @min and @max. (If the range has a non-zero
777 * page size, it is clamped between @min and @max - page-size.)
780 gtk_range_set_range (GtkRange *range,
786 g_return_if_fail (GTK_IS_RANGE (range));
787 g_return_if_fail (min < max);
789 range->adjustment->lower = min;
790 range->adjustment->upper = max;
792 value = CLAMP (range->adjustment->value,
793 range->adjustment->lower,
794 (range->adjustment->upper - range->adjustment->page_size));
796 gtk_adjustment_set_value (range->adjustment, value);
797 gtk_adjustment_changed (range->adjustment);
801 * gtk_range_set_value:
802 * @range: a #GtkRange
803 * @value: new value of the range
805 * Sets the current value of the range; if the value is outside the
806 * minimum or maximum range values, it will be clamped to fit inside
807 * them. The range emits the "value_changed" signal if the value
812 gtk_range_set_value (GtkRange *range,
815 g_return_if_fail (GTK_IS_RANGE (range));
817 value = CLAMP (value, range->adjustment->lower,
818 (range->adjustment->upper - range->adjustment->page_size));
820 gtk_adjustment_set_value (range->adjustment, value);
824 * gtk_range_get_value:
825 * @range: a #GtkRange
827 * Gets the current value of the range.
829 * Return value: current value of the range.
832 gtk_range_get_value (GtkRange *range)
834 g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
836 return range->adjustment->value;
840 should_invert (GtkRange *range)
842 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
844 (range->inverted && !range->flippable) ||
845 (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
846 (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
848 return range->inverted;
852 gtk_range_finalize (GObject *object)
854 GtkRange *range = GTK_RANGE (object);
856 g_free (range->layout);
858 (* G_OBJECT_CLASS (gtk_range_parent_class)->finalize) (object);
862 gtk_range_destroy (GtkObject *object)
864 GtkRange *range = GTK_RANGE (object);
866 gtk_range_remove_step_timer (range);
867 gtk_range_remove_update_timer (range);
869 if (range->adjustment)
871 g_signal_handlers_disconnect_by_func (range->adjustment,
872 gtk_range_adjustment_changed,
874 g_signal_handlers_disconnect_by_func (range->adjustment,
875 gtk_range_adjustment_value_changed,
877 g_object_unref (range->adjustment);
878 range->adjustment = NULL;
881 (* GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy) (object);
885 gtk_range_size_request (GtkWidget *widget,
886 GtkRequisition *requisition)
889 gint slider_width, stepper_size, trough_border, stepper_spacing;
890 GdkRectangle range_rect;
893 range = GTK_RANGE (widget);
895 gtk_range_get_props (range,
896 &slider_width, &stepper_size, &trough_border, &stepper_spacing,
899 gtk_range_calc_request (range,
900 slider_width, stepper_size, trough_border, stepper_spacing,
901 &range_rect, &border, NULL, NULL);
903 requisition->width = range_rect.width + border.left + border.right;
904 requisition->height = range_rect.height + border.top + border.bottom;
908 gtk_range_size_allocate (GtkWidget *widget,
909 GtkAllocation *allocation)
913 range = GTK_RANGE (widget);
915 widget->allocation = *allocation;
917 range->need_recalc = TRUE;
918 gtk_range_calc_layout (range, range->adjustment->value);
920 if (GTK_WIDGET_REALIZED (range))
921 gdk_window_move_resize (range->event_window,
922 widget->allocation.x,
923 widget->allocation.y,
924 widget->allocation.width,
925 widget->allocation.height);
929 gtk_range_realize (GtkWidget *widget)
932 GdkWindowAttr attributes;
933 gint attributes_mask;
935 range = GTK_RANGE (widget);
937 gtk_range_calc_layout (range, range->adjustment->value);
939 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
941 widget->window = gtk_widget_get_parent_window (widget);
942 g_object_ref (widget->window);
944 attributes.window_type = GDK_WINDOW_CHILD;
945 attributes.x = widget->allocation.x;
946 attributes.y = widget->allocation.y;
947 attributes.width = widget->allocation.width;
948 attributes.height = widget->allocation.height;
949 attributes.wclass = GDK_INPUT_ONLY;
950 attributes.event_mask = gtk_widget_get_events (widget);
951 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
952 GDK_BUTTON_RELEASE_MASK |
953 GDK_ENTER_NOTIFY_MASK |
954 GDK_LEAVE_NOTIFY_MASK |
955 GDK_POINTER_MOTION_MASK |
956 GDK_POINTER_MOTION_HINT_MASK);
958 attributes_mask = GDK_WA_X | GDK_WA_Y;
960 range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
961 &attributes, attributes_mask);
962 gdk_window_set_user_data (range->event_window, range);
964 widget->style = gtk_style_attach (widget->style, widget->window);
968 gtk_range_unrealize (GtkWidget *widget)
970 GtkRange *range = GTK_RANGE (widget);
972 gtk_range_remove_step_timer (range);
973 gtk_range_remove_update_timer (range);
975 gdk_window_set_user_data (range->event_window, NULL);
976 gdk_window_destroy (range->event_window);
977 range->event_window = NULL;
979 if (GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize)
980 (* GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize) (widget);
984 gtk_range_map (GtkWidget *widget)
986 GtkRange *range = GTK_RANGE (widget);
988 gdk_window_show (range->event_window);
990 GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
994 gtk_range_unmap (GtkWidget *widget)
996 GtkRange *range = GTK_RANGE (widget);
998 stop_scrolling (range);
1000 gdk_window_hide (range->event_window);
1002 GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1006 draw_stepper (GtkRange *range,
1008 GtkArrowType arrow_type,
1010 gboolean prelighted,
1013 GtkStateType state_type;
1014 GtkShadowType shadow_type;
1015 GdkRectangle intersection;
1016 GtkWidget *widget = GTK_WIDGET (range);
1023 gboolean arrow_sensitive = TRUE;
1025 /* More to get the right clip region than for efficiency */
1026 if (!gdk_rectangle_intersect (area, rect, &intersection))
1029 intersection.x += widget->allocation.x;
1030 intersection.y += widget->allocation.y;
1032 if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1033 arrow_type == GTK_ARROW_RIGHT)) ||
1034 (range->inverted && (arrow_type == GTK_ARROW_UP ||
1035 arrow_type == GTK_ARROW_LEFT)))
1037 switch (range->layout->upper_sensitivity)
1039 case GTK_SENSITIVITY_AUTO:
1041 (range->adjustment->value <
1042 (range->adjustment->upper - range->adjustment->page_size));
1045 case GTK_SENSITIVITY_ON:
1046 arrow_sensitive = TRUE;
1049 case GTK_SENSITIVITY_OFF:
1050 arrow_sensitive = FALSE;
1056 switch (range->layout->lower_sensitivity)
1058 case GTK_SENSITIVITY_AUTO:
1060 (range->adjustment->value > range->adjustment->lower);
1063 case GTK_SENSITIVITY_ON:
1064 arrow_sensitive = TRUE;
1067 case GTK_SENSITIVITY_OFF:
1068 arrow_sensitive = FALSE;
1073 if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
1074 state_type = GTK_STATE_INSENSITIVE;
1076 state_type = GTK_STATE_ACTIVE;
1077 else if (prelighted)
1078 state_type = GTK_STATE_PRELIGHT;
1080 state_type = GTK_STATE_NORMAL;
1082 if (clicked && arrow_sensitive)
1083 shadow_type = GTK_SHADOW_IN;
1085 shadow_type = GTK_SHADOW_OUT;
1087 gtk_paint_box (widget->style,
1089 state_type, shadow_type,
1090 &intersection, widget,
1091 GTK_RANGE_GET_CLASS (range)->stepper_detail,
1092 widget->allocation.x + rect->x,
1093 widget->allocation.y + rect->y,
1097 arrow_width = rect->width / 2;
1098 arrow_height = rect->height / 2;
1099 arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1100 arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1102 if (clicked && arrow_sensitive)
1104 gint arrow_displacement_x;
1105 gint arrow_displacement_y;
1107 gtk_range_get_props (GTK_RANGE (widget), NULL, NULL, NULL, NULL,
1108 &arrow_displacement_x, &arrow_displacement_y);
1110 arrow_x += arrow_displacement_x;
1111 arrow_y += arrow_displacement_y;
1114 gtk_paint_arrow (widget->style,
1116 state_type, shadow_type,
1117 &intersection, widget,
1118 GTK_RANGE_GET_CLASS (range)->stepper_detail,
1121 arrow_x, arrow_y, arrow_width, arrow_height);
1125 gtk_range_expose (GtkWidget *widget,
1126 GdkEventExpose *event)
1131 GtkShadowType shadow_type;
1132 GdkRectangle expose_area; /* Relative to widget->allocation */
1134 gint focus_line_width = 0;
1135 gint focus_padding = 0;
1136 gboolean touchscreen;
1137 gboolean activate_slider;
1139 g_object_get (gtk_widget_get_settings (widget),
1140 "gtk-touchscreen-mode", &touchscreen,
1143 range = GTK_RANGE (widget);
1145 if (GTK_WIDGET_CAN_FOCUS (range))
1147 gtk_widget_style_get (GTK_WIDGET (range),
1148 "focus-line-width", &focus_line_width,
1149 "focus-padding", &focus_padding,
1153 expose_area = event->area;
1154 expose_area.x -= widget->allocation.x;
1155 expose_area.y -= widget->allocation.y;
1157 gtk_range_calc_layout (range, range->adjustment->value);
1159 sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
1161 /* Just to be confusing, we draw the trough for the whole
1162 * range rectangle, not the trough rectangle (the trough
1163 * rectangle is just for hit detection)
1165 /* The gdk_rectangle_intersect is more to get the right
1166 * clip region (limited to range_rect) than for efficiency
1168 if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1171 area.x += widget->allocation.x;
1172 area.y += widget->allocation.y;
1174 gtk_paint_box (widget->style,
1176 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1178 &area, GTK_WIDGET(range), "trough",
1179 widget->allocation.x + range->range_rect.x + focus_line_width + focus_padding,
1180 widget->allocation.y + range->range_rect.y + focus_line_width + focus_padding,
1181 range->range_rect.width - 2 * (focus_line_width + focus_padding),
1182 range->range_rect.height - 2 * (focus_line_width + focus_padding));
1186 GTK_WIDGET_HAS_FOCUS (range))
1187 gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
1188 &area, widget, "trough",
1189 widget->allocation.x + range->range_rect.x,
1190 widget->allocation.y + range->range_rect.y,
1191 range->range_rect.width,
1192 range->range_rect.height);
1195 shadow_type = GTK_SHADOW_OUT;
1198 state = GTK_STATE_INSENSITIVE;
1199 else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1200 state = GTK_STATE_PRELIGHT;
1202 state = GTK_STATE_NORMAL;
1204 if (range->layout->grab_location == MOUSE_SLIDER)
1206 gtk_widget_style_get (widget, "activate_slider", &activate_slider, NULL);
1208 if (activate_slider)
1210 state = GTK_STATE_ACTIVE;
1211 shadow_type = GTK_SHADOW_IN;
1215 if (gdk_rectangle_intersect (&expose_area,
1216 &range->layout->slider,
1219 area.x += widget->allocation.x;
1220 area.y += widget->allocation.y;
1222 gtk_paint_slider (widget->style,
1228 GTK_RANGE_GET_CLASS (range)->slider_detail,
1229 widget->allocation.x + range->layout->slider.x,
1230 widget->allocation.y + range->layout->slider.y,
1231 range->layout->slider.width,
1232 range->layout->slider.height,
1233 range->orientation);
1236 if (range->has_stepper_a)
1237 draw_stepper (range, &range->layout->stepper_a,
1238 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1239 range->layout->grab_location == MOUSE_STEPPER_A,
1240 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
1243 if (range->has_stepper_b)
1244 draw_stepper (range, &range->layout->stepper_b,
1245 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1246 range->layout->grab_location == MOUSE_STEPPER_B,
1247 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
1250 if (range->has_stepper_c)
1251 draw_stepper (range, &range->layout->stepper_c,
1252 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
1253 range->layout->grab_location == MOUSE_STEPPER_C,
1254 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
1257 if (range->has_stepper_d)
1258 draw_stepper (range, &range->layout->stepper_d,
1259 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
1260 range->layout->grab_location == MOUSE_STEPPER_D,
1261 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
1268 range_grab_add (GtkRange *range,
1269 MouseLocation location,
1272 /* we don't actually gtk_grab, since a button is down */
1274 gtk_grab_add (GTK_WIDGET (range));
1276 range->layout->grab_location = location;
1277 range->layout->grab_button = button;
1279 if (gtk_range_update_mouse_location (range))
1280 gtk_widget_queue_draw (GTK_WIDGET (range));
1284 range_grab_remove (GtkRange *range)
1286 gtk_grab_remove (GTK_WIDGET (range));
1288 range->layout->grab_location = MOUSE_OUTSIDE;
1289 range->layout->grab_button = 0;
1291 if (gtk_range_update_mouse_location (range))
1292 gtk_widget_queue_draw (GTK_WIDGET (range));
1295 static GtkScrollType
1296 range_get_scroll_for_grab (GtkRange *range)
1300 invert = should_invert (range);
1301 switch (range->layout->grab_location)
1303 /* Backward stepper */
1304 case MOUSE_STEPPER_A:
1305 case MOUSE_STEPPER_C:
1306 switch (range->layout->grab_button)
1309 return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
1312 return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
1315 return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
1320 /* Forward stepper */
1321 case MOUSE_STEPPER_B:
1322 case MOUSE_STEPPER_D:
1323 switch (range->layout->grab_button)
1326 return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
1329 return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
1332 return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
1340 if (range->trough_click_forward)
1341 return GTK_SCROLL_PAGE_FORWARD;
1343 return GTK_SCROLL_PAGE_BACKWARD;
1353 return GTK_SCROLL_NONE;
1357 coord_to_value (GtkRange *range,
1363 if (range->orientation == GTK_ORIENTATION_VERTICAL)
1364 if (range->layout->trough.height == range->layout->slider.height)
1367 frac = ((coord - range->layout->trough.y) /
1368 (gdouble) (range->layout->trough.height - range->layout->slider.height));
1370 if (range->layout->trough.width == range->layout->slider.width)
1373 frac = ((coord - range->layout->trough.x) /
1374 (gdouble) (range->layout->trough.width - range->layout->slider.width));
1376 if (should_invert (range))
1379 value = range->adjustment->lower +
1380 frac * (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size);
1386 gtk_range_key_press (GtkWidget *widget,
1389 GtkRange *range = (GtkRange *)widget;
1391 if (event->keyval == GDK_Escape)
1393 stop_scrolling (range);
1395 update_slider_position (range,
1396 range->slide_initial_coordinate,
1397 range->slide_initial_coordinate);
1406 gtk_range_button_press (GtkWidget *widget,
1407 GdkEventButton *event)
1409 GtkRange *range = GTK_RANGE (widget);
1411 if (!GTK_WIDGET_HAS_FOCUS (widget))
1412 gtk_widget_grab_focus (widget);
1414 /* ignore presses when we're already doing something else. */
1415 if (range->layout->grab_location != MOUSE_OUTSIDE)
1418 range->layout->mouse_x = event->x;
1419 range->layout->mouse_y = event->y;
1420 if (gtk_range_update_mouse_location (range))
1421 gtk_widget_queue_draw (widget);
1423 if (range->layout->mouse_location == MOUSE_TROUGH &&
1426 /* button 1 steps by page increment, as with button 2 on a stepper
1428 GtkScrollType scroll;
1429 gdouble click_value;
1431 click_value = coord_to_value (range,
1432 range->orientation == GTK_ORIENTATION_VERTICAL ?
1433 event->y : event->x);
1435 range->trough_click_forward = click_value > range->adjustment->value;
1436 range_grab_add (range, MOUSE_TROUGH, event->button);
1438 scroll = range_get_scroll_for_grab (range);
1440 gtk_range_add_step_timer (range, scroll);
1444 else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
1445 range->layout->mouse_location == MOUSE_STEPPER_B ||
1446 range->layout->mouse_location == MOUSE_STEPPER_C ||
1447 range->layout->mouse_location == MOUSE_STEPPER_D) &&
1448 (event->button == 1 || event->button == 2 || event->button == 3))
1450 GdkRectangle *stepper_area;
1451 GtkScrollType scroll;
1453 range_grab_add (range, range->layout->mouse_location, event->button);
1455 stepper_area = get_area (range, range->layout->mouse_location);
1456 gtk_widget_queue_draw_area (widget,
1457 widget->allocation.x + stepper_area->x,
1458 widget->allocation.y + stepper_area->y,
1459 stepper_area->width,
1460 stepper_area->height);
1462 scroll = range_get_scroll_for_grab (range);
1463 if (scroll != GTK_SCROLL_NONE)
1464 gtk_range_add_step_timer (range, scroll);
1468 else if ((range->layout->mouse_location == MOUSE_TROUGH &&
1469 event->button == 2) ||
1470 range->layout->mouse_location == MOUSE_SLIDER)
1472 gboolean need_value_update = FALSE;
1473 gboolean activate_slider;
1475 /* Any button can be used to drag the slider, but you can start
1476 * dragging the slider with a trough click using button 2;
1477 * On button 2 press, we warp the slider to mouse position,
1478 * then begin the slider drag.
1480 if (event->button == 2)
1482 gdouble slider_low_value, slider_high_value, new_value;
1485 coord_to_value (range,
1486 range->orientation == GTK_ORIENTATION_VERTICAL ?
1487 event->y : event->x);
1489 coord_to_value (range,
1490 range->orientation == GTK_ORIENTATION_VERTICAL ?
1491 event->y - range->layout->slider.height :
1492 event->x - range->layout->slider.width);
1494 /* compute new value for warped slider */
1495 new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
1497 /* recalc slider, so we can set slide_initial_slider_position
1500 range->need_recalc = TRUE;
1501 gtk_range_calc_layout (range, new_value);
1503 /* defer adjustment updates to update_slider_position() in order
1504 * to keep pixel quantisation
1506 need_value_update = TRUE;
1509 if (range->orientation == GTK_ORIENTATION_VERTICAL)
1511 range->slide_initial_slider_position = range->layout->slider.y;
1512 range->slide_initial_coordinate = event->y;
1516 range->slide_initial_slider_position = range->layout->slider.x;
1517 range->slide_initial_coordinate = event->x;
1520 range_grab_add (range, MOUSE_SLIDER, event->button);
1522 gtk_widget_style_get (widget, "activate_slider", &activate_slider, NULL);
1524 /* force a redraw, if the active slider is drawn differently to the prelight one */
1525 if (activate_slider)
1526 gtk_widget_queue_draw (widget);
1528 if (need_value_update)
1529 update_slider_position (range, event->x, event->y);
1537 /* During a slide, move the slider as required given new mouse position */
1539 update_slider_position (GtkRange *range,
1548 if (range->orientation == GTK_ORIENTATION_VERTICAL)
1549 delta = mouse_y - range->slide_initial_coordinate;
1551 delta = mouse_x - range->slide_initial_coordinate;
1553 c = range->slide_initial_slider_position + delta;
1555 new_value = coord_to_value (range, c);
1557 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
1562 stop_scrolling (GtkRange *range)
1564 range_grab_remove (range);
1565 gtk_range_remove_step_timer (range);
1566 /* Flush any pending discontinuous/delayed updates */
1567 gtk_range_update_value (range);
1569 /* Just be lazy about this, if we scrolled it will all redraw anyway,
1570 * so no point optimizing the button deactivate case
1572 gtk_widget_queue_draw (GTK_WIDGET (range));
1576 gtk_range_grab_broken (GtkWidget *widget,
1577 GdkEventGrabBroken *event)
1579 GtkRange *range = GTK_RANGE (widget);
1581 if (range->layout->grab_location != MOUSE_OUTSIDE)
1583 if (range->layout->grab_location == MOUSE_SLIDER)
1584 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
1586 stop_scrolling (range);
1595 gtk_range_button_release (GtkWidget *widget,
1596 GdkEventButton *event)
1598 GtkRange *range = GTK_RANGE (widget);
1600 if (event->window == range->event_window)
1602 range->layout->mouse_x = event->x;
1603 range->layout->mouse_y = event->y;
1607 gdk_window_get_pointer (range->event_window,
1608 &range->layout->mouse_x,
1609 &range->layout->mouse_y,
1613 if (range->layout->grab_button == event->button)
1615 if (range->layout->grab_location == MOUSE_SLIDER)
1616 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
1618 stop_scrolling (range);
1627 * _gtk_range_get_wheel_delta:
1628 * @range: a #GtkRange
1629 * @direction: A #GdkScrollDirection
1631 * Returns a good step value for the mouse wheel.
1633 * Return value: A good step value for the mouse wheel.
1638 _gtk_range_get_wheel_delta (GtkRange *range,
1639 GdkScrollDirection direction)
1641 GtkAdjustment *adj = range->adjustment;
1644 if (GTK_IS_SCROLLBAR (range))
1645 delta = pow (adj->page_size, 2.0 / 3.0);
1647 delta = adj->step_increment * 2;
1649 if (direction == GDK_SCROLL_UP ||
1650 direction == GDK_SCROLL_LEFT)
1653 if (range->inverted)
1660 gtk_range_scroll_event (GtkWidget *widget,
1661 GdkEventScroll *event)
1663 GtkRange *range = GTK_RANGE (widget);
1665 if (GTK_WIDGET_REALIZED (range))
1667 GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
1671 delta = _gtk_range_get_wheel_delta (range, event->direction);
1673 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1674 GTK_SCROLL_JUMP, adj->value + delta,
1677 /* Policy DELAYED makes sense with scroll events,
1678 * but DISCONTINUOUS doesn't, so we update immediately
1681 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
1682 gtk_range_update_value (range);
1689 gtk_range_motion_notify (GtkWidget *widget,
1690 GdkEventMotion *event)
1695 range = GTK_RANGE (widget);
1697 gdk_window_get_pointer (range->event_window, &x, &y, NULL);
1699 range->layout->mouse_x = x;
1700 range->layout->mouse_y = y;
1702 if (gtk_range_update_mouse_location (range))
1703 gtk_widget_queue_draw (widget);
1705 if (range->layout->grab_location == MOUSE_SLIDER)
1706 update_slider_position (range, x, y);
1708 /* We handled the event if the mouse was in the range_rect */
1709 return range->layout->mouse_location != MOUSE_OUTSIDE;
1713 gtk_range_enter_notify (GtkWidget *widget,
1714 GdkEventCrossing *event)
1716 GtkRange *range = GTK_RANGE (widget);
1718 range->layout->mouse_x = event->x;
1719 range->layout->mouse_y = event->y;
1721 if (gtk_range_update_mouse_location (range))
1722 gtk_widget_queue_draw (widget);
1728 gtk_range_leave_notify (GtkWidget *widget,
1729 GdkEventCrossing *event)
1731 GtkRange *range = GTK_RANGE (widget);
1733 range->layout->mouse_x = -1;
1734 range->layout->mouse_y = -1;
1736 if (gtk_range_update_mouse_location (range))
1737 gtk_widget_queue_draw (widget);
1743 gtk_range_grab_notify (GtkWidget *widget,
1744 gboolean was_grabbed)
1747 stop_scrolling (GTK_RANGE (widget));
1751 gtk_range_state_changed (GtkWidget *widget,
1752 GtkStateType previous_state)
1754 if (!GTK_WIDGET_IS_SENSITIVE (widget))
1755 stop_scrolling (GTK_RANGE (widget));
1758 #define check_rectangle(rectangle1, rectangle2) \
1760 if (rectangle1.x != rectangle2.x) return TRUE; \
1761 if (rectangle1.y != rectangle2.y) return TRUE; \
1762 if (rectangle1.width != rectangle2.width) return TRUE; \
1763 if (rectangle1.height != rectangle2.height) return TRUE; \
1767 layout_changed (GtkRangeLayout *layout1,
1768 GtkRangeLayout *layout2)
1770 check_rectangle (layout1->slider, layout2->slider);
1771 check_rectangle (layout1->trough, layout2->trough);
1772 check_rectangle (layout1->stepper_a, layout2->stepper_a);
1773 check_rectangle (layout1->stepper_d, layout2->stepper_d);
1774 check_rectangle (layout1->stepper_b, layout2->stepper_b);
1775 check_rectangle (layout1->stepper_c, layout2->stepper_c);
1781 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
1784 GtkRange *range = GTK_RANGE (data);
1785 /* create a copy of the layout */
1786 GtkRangeLayout layout = *range->layout;
1788 range->need_recalc = TRUE;
1789 gtk_range_calc_layout (range, range->adjustment->value);
1791 /* now check whether the layout changed */
1792 if (layout_changed (range->layout, &layout))
1793 gtk_widget_queue_draw (GTK_WIDGET (range));
1795 /* Note that we don't round off to range->round_digits here.
1796 * that's because it's really broken to change a value
1797 * in response to a change signal on that value; round_digits
1798 * is therefore defined to be a filter on what the GtkRange
1799 * can input into the adjustment, not a filter that the GtkRange
1800 * will enforce on the adjustment.
1805 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
1808 GtkRange *range = GTK_RANGE (data);
1809 /* create a copy of the layout */
1810 GtkRangeLayout layout = *range->layout;
1812 range->need_recalc = TRUE;
1813 gtk_range_calc_layout (range, range->adjustment->value);
1815 /* now check whether the layout changed */
1816 if (layout_changed (range->layout, &layout))
1818 gtk_widget_queue_draw (GTK_WIDGET (range));
1820 /* This is so we don't lag the widget being scrolled. */
1821 if (GTK_WIDGET_REALIZED (range))
1822 gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
1825 /* Note that we don't round off to range->round_digits here.
1826 * that's because it's really broken to change a value
1827 * in response to a change signal on that value; round_digits
1828 * is therefore defined to be a filter on what the GtkRange
1829 * can input into the adjustment, not a filter that the GtkRange
1830 * will enforce on the adjustment.
1833 g_signal_emit (range, signals[VALUE_CHANGED], 0);
1837 gtk_range_style_set (GtkWidget *widget,
1838 GtkStyle *previous_style)
1840 GtkRange *range = GTK_RANGE (widget);
1842 range->need_recalc = TRUE;
1844 (* GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set) (widget, previous_style);
1848 step_back (GtkRange *range)
1853 newval = range->adjustment->value - range->adjustment->step_increment;
1854 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1855 GTK_SCROLL_STEP_BACKWARD, newval, &handled);
1859 step_forward (GtkRange *range)
1864 newval = range->adjustment->value + range->adjustment->step_increment;
1865 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1866 GTK_SCROLL_STEP_FORWARD, newval, &handled);
1871 page_back (GtkRange *range)
1876 newval = range->adjustment->value - range->adjustment->page_increment;
1877 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1878 GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
1882 page_forward (GtkRange *range)
1887 newval = range->adjustment->value + range->adjustment->page_increment;
1888 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1889 GTK_SCROLL_PAGE_FORWARD, newval, &handled);
1893 scroll_begin (GtkRange *range)
1896 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1897 GTK_SCROLL_START, range->adjustment->lower,
1902 scroll_end (GtkRange *range)
1907 newval = range->adjustment->upper - range->adjustment->page_size;
1908 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
1913 gtk_range_scroll (GtkRange *range,
1914 GtkScrollType scroll)
1918 case GTK_SCROLL_STEP_LEFT:
1919 if (should_invert (range))
1920 step_forward (range);
1925 case GTK_SCROLL_STEP_UP:
1926 if (should_invert (range))
1927 step_forward (range);
1932 case GTK_SCROLL_STEP_RIGHT:
1933 if (should_invert (range))
1936 step_forward (range);
1939 case GTK_SCROLL_STEP_DOWN:
1940 if (should_invert (range))
1943 step_forward (range);
1946 case GTK_SCROLL_STEP_BACKWARD:
1950 case GTK_SCROLL_STEP_FORWARD:
1951 step_forward (range);
1954 case GTK_SCROLL_PAGE_LEFT:
1955 if (should_invert (range))
1956 page_forward (range);
1961 case GTK_SCROLL_PAGE_UP:
1962 if (should_invert (range))
1963 page_forward (range);
1968 case GTK_SCROLL_PAGE_RIGHT:
1969 if (should_invert (range))
1972 page_forward (range);
1975 case GTK_SCROLL_PAGE_DOWN:
1976 if (should_invert (range))
1979 page_forward (range);
1982 case GTK_SCROLL_PAGE_BACKWARD:
1986 case GTK_SCROLL_PAGE_FORWARD:
1987 page_forward (range);
1990 case GTK_SCROLL_START:
1991 scroll_begin (range);
1994 case GTK_SCROLL_END:
1998 case GTK_SCROLL_JUMP:
1999 /* Used by CList, range doesn't use it. */
2002 case GTK_SCROLL_NONE:
2008 gtk_range_move_slider (GtkRange *range,
2009 GtkScrollType scroll)
2011 gtk_range_scroll (range, scroll);
2013 /* Policy DELAYED makes sense with key events,
2014 * but DISCONTINUOUS doesn't, so we update immediately
2017 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2018 gtk_range_update_value (range);
2022 gtk_range_get_props (GtkRange *range,
2025 gint *trough_border,
2026 gint *stepper_spacing,
2027 gint *arrow_displacement_x,
2028 gint *arrow_displacement_y)
2030 GtkWidget *widget = GTK_WIDGET (range);
2031 gint tmp_slider_width, tmp_stepper_size, tmp_trough_border, tmp_stepper_spacing;
2032 gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2034 gtk_widget_style_get (widget,
2035 "slider-width", &tmp_slider_width,
2036 "trough-border", &tmp_trough_border,
2037 "stepper-size", &tmp_stepper_size,
2038 "stepper-spacing", &tmp_stepper_spacing,
2039 "arrow-displacement-x", &tmp_arrow_displacement_x,
2040 "arrow-displacement-y", &tmp_arrow_displacement_y,
2043 if (GTK_WIDGET_CAN_FOCUS (range))
2045 gint focus_line_width;
2048 gtk_widget_style_get (GTK_WIDGET (range),
2049 "focus-line-width", &focus_line_width,
2050 "focus-padding", &focus_padding,
2053 tmp_trough_border += focus_line_width + focus_padding;
2057 *slider_width = tmp_slider_width;
2060 *trough_border = tmp_trough_border;
2063 *stepper_size = tmp_stepper_size;
2065 if (stepper_spacing)
2066 *stepper_spacing = tmp_stepper_spacing;
2068 if (arrow_displacement_x)
2069 *arrow_displacement_x = tmp_arrow_displacement_x;
2071 if (arrow_displacement_y)
2072 *arrow_displacement_y = tmp_arrow_displacement_y;
2075 #define POINT_IN_RECT(xcoord, ycoord, rect) \
2076 ((xcoord) >= (rect).x && \
2077 (xcoord) < ((rect).x + (rect).width) && \
2078 (ycoord) >= (rect).y && \
2079 (ycoord) < ((rect).y + (rect).height))
2081 /* Update mouse location, return TRUE if it changes */
2083 gtk_range_update_mouse_location (GtkRange *range)
2089 widget = GTK_WIDGET (range);
2091 old = range->layout->mouse_location;
2093 x = range->layout->mouse_x;
2094 y = range->layout->mouse_y;
2096 if (range->layout->grab_location != MOUSE_OUTSIDE)
2097 range->layout->mouse_location = range->layout->grab_location;
2098 else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
2099 range->layout->mouse_location = MOUSE_STEPPER_A;
2100 else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
2101 range->layout->mouse_location = MOUSE_STEPPER_B;
2102 else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
2103 range->layout->mouse_location = MOUSE_STEPPER_C;
2104 else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
2105 range->layout->mouse_location = MOUSE_STEPPER_D;
2106 else if (POINT_IN_RECT (x, y, range->layout->slider))
2107 range->layout->mouse_location = MOUSE_SLIDER;
2108 else if (POINT_IN_RECT (x, y, range->layout->trough))
2109 range->layout->mouse_location = MOUSE_TROUGH;
2110 else if (POINT_IN_RECT (x, y, widget->allocation))
2111 range->layout->mouse_location = MOUSE_WIDGET;
2113 range->layout->mouse_location = MOUSE_OUTSIDE;
2115 return old != range->layout->mouse_location;
2118 /* Clamp rect, border inside widget->allocation, such that we prefer
2119 * to take space from border not rect in all directions, and prefer to
2120 * give space to border over rect in one direction.
2123 clamp_dimensions (GtkWidget *widget,
2126 gboolean border_expands_horizontally)
2128 gint extra, shortage;
2130 g_return_if_fail (rect->x == 0);
2131 g_return_if_fail (rect->y == 0);
2132 g_return_if_fail (rect->width >= 0);
2133 g_return_if_fail (rect->height >= 0);
2137 extra = widget->allocation.width - border->left - border->right - rect->width;
2140 if (border_expands_horizontally)
2142 border->left += extra / 2;
2143 border->right += extra / 2 + extra % 2;
2147 rect->width += extra;
2151 /* See if we can fit rect, if not kill the border */
2152 shortage = rect->width - widget->allocation.width;
2155 rect->width = widget->allocation.width;
2156 /* lose the border */
2162 /* See if we can fit rect with borders */
2163 shortage = rect->width + border->left + border->right -
2164 widget->allocation.width;
2167 /* Shrink borders */
2168 border->left -= shortage / 2;
2169 border->right -= shortage / 2 + shortage % 2;
2175 extra = widget->allocation.height - border->top - border->bottom - rect->height;
2178 if (border_expands_horizontally)
2180 /* don't expand border vertically */
2181 rect->height += extra;
2185 border->top += extra / 2;
2186 border->bottom += extra / 2 + extra % 2;
2190 /* See if we can fit rect, if not kill the border */
2191 shortage = rect->height - widget->allocation.height;
2194 rect->height = widget->allocation.height;
2195 /* lose the border */
2201 /* See if we can fit rect with borders */
2202 shortage = rect->height + border->top + border->bottom -
2203 widget->allocation.height;
2206 /* Shrink borders */
2207 border->top -= shortage / 2;
2208 border->bottom -= shortage / 2 + shortage % 2;
2214 gtk_range_calc_request (GtkRange *range,
2218 gint stepper_spacing,
2219 GdkRectangle *range_rect,
2222 gint *slider_length_p)
2232 if (GTK_RANGE_GET_CLASS (range)->get_range_border)
2233 (* GTK_RANGE_GET_CLASS (range)->get_range_border) (range, border);
2236 if (range->has_stepper_a)
2238 if (range->has_stepper_b)
2240 if (range->has_stepper_c)
2242 if (range->has_stepper_d)
2245 slider_length = range->min_slider_size;
2250 /* We never expand to fill available space in the small dimension
2251 * (i.e. vertical scrollbars are always a fixed width)
2253 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2255 range_rect->width = trough_border * 2 + slider_width;
2256 range_rect->height = stepper_size * n_steppers + stepper_spacing * 2 + trough_border * 2 + slider_length;
2260 range_rect->width = stepper_size * n_steppers + stepper_spacing * 2 + trough_border * 2 + slider_length;
2261 range_rect->height = trough_border * 2 + slider_width;
2265 *n_steppers_p = n_steppers;
2267 if (slider_length_p)
2268 *slider_length_p = slider_length;
2272 gtk_range_calc_layout (GtkRange *range,
2273 gdouble adjustment_value)
2275 gint slider_width, stepper_size, trough_border, stepper_spacing;
2279 GdkRectangle range_rect;
2280 GtkRangeLayout *layout;
2283 if (!range->need_recalc)
2286 /* If we have a too-small allocation, we prefer the steppers over
2287 * the trough/slider, probably the steppers are a more useful
2288 * feature in small spaces.
2290 * Also, we prefer to draw the range itself rather than the border
2291 * areas if there's a conflict, since the borders will be decoration
2292 * not controls. Though this depends on subclasses cooperating by
2293 * not drawing on range->range_rect.
2296 widget = GTK_WIDGET (range);
2297 layout = range->layout;
2299 gtk_range_get_props (range,
2300 &slider_width, &stepper_size, &trough_border, &stepper_spacing,
2303 gtk_range_calc_request (range,
2304 slider_width, stepper_size, trough_border, stepper_spacing,
2305 &range_rect, &border, &n_steppers, &slider_length);
2307 /* We never expand to fill available space in the small dimension
2308 * (i.e. vertical scrollbars are always a fixed width)
2310 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2312 clamp_dimensions (widget, &range_rect, &border, TRUE);
2316 clamp_dimensions (widget, &range_rect, &border, FALSE);
2319 range_rect.x = border.left;
2320 range_rect.y = border.top;
2322 range->range_rect = range_rect;
2324 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2326 gint stepper_width, stepper_height;
2328 /* Steppers are the width of the range, and stepper_size in
2329 * height, or if we don't have enough height, divided equally
2330 * among available space.
2332 stepper_width = range_rect.width - trough_border * 2;
2334 if (stepper_width < 1)
2335 stepper_width = range_rect.width; /* screw the trough border */
2337 if (n_steppers == 0)
2338 stepper_height = 0; /* avoid divide by n_steppers */
2340 stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
2344 layout->stepper_a.x = range_rect.x + trough_border;
2345 layout->stepper_a.y = range_rect.y + trough_border;
2347 if (range->has_stepper_a)
2349 layout->stepper_a.width = stepper_width;
2350 layout->stepper_a.height = stepper_height;
2354 layout->stepper_a.width = 0;
2355 layout->stepper_a.height = 0;
2360 layout->stepper_b.x = layout->stepper_a.x;
2361 layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
2363 if (range->has_stepper_b)
2365 layout->stepper_b.width = stepper_width;
2366 layout->stepper_b.height = stepper_height;
2370 layout->stepper_b.width = 0;
2371 layout->stepper_b.height = 0;
2376 if (range->has_stepper_d)
2378 layout->stepper_d.width = stepper_width;
2379 layout->stepper_d.height = stepper_height;
2383 layout->stepper_d.width = 0;
2384 layout->stepper_d.height = 0;
2387 layout->stepper_d.x = layout->stepper_a.x;
2388 layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - trough_border;
2392 if (range->has_stepper_c)
2394 layout->stepper_c.width = stepper_width;
2395 layout->stepper_c.height = stepper_height;
2399 layout->stepper_c.width = 0;
2400 layout->stepper_c.height = 0;
2403 layout->stepper_c.x = layout->stepper_a.x;
2404 layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
2406 /* Now the trough is the remaining space between steppers B and C,
2409 layout->trough.x = range_rect.x;
2410 layout->trough.y = layout->stepper_b.y + layout->stepper_b.height;
2411 layout->trough.width = range_rect.width;
2412 layout->trough.height = layout->stepper_c.y - (layout->stepper_b.y + layout->stepper_b.height);
2414 /* Slider fits into the trough, with stepper_spacing on either side,
2415 * and the size/position based on the adjustment or fixed, depending.
2417 layout->slider.x = layout->trough.x + trough_border;
2418 layout->slider.width = layout->trough.width - trough_border * 2;
2420 /* Compute slider position/length */
2422 gint y, bottom, top, height;
2424 top = layout->trough.y + stepper_spacing;
2425 bottom = layout->trough.y + layout->trough.height - stepper_spacing;
2427 /* slider height is the fraction (page_size /
2428 * total_adjustment_range) times the trough height in pixels
2431 if (range->adjustment->upper - range->adjustment->lower != 0)
2432 height = ((bottom - top) * (range->adjustment->page_size /
2433 (range->adjustment->upper - range->adjustment->lower)));
2435 height = range->min_slider_size;
2437 if (height < range->min_slider_size ||
2438 range->slider_size_fixed)
2439 height = range->min_slider_size;
2441 height = MIN (height, (layout->trough.height - stepper_spacing * 2));
2445 if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
2446 y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
2447 (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
2449 y = CLAMP (y, top, bottom);
2451 if (should_invert (range))
2452 y = bottom - (y - top + height);
2454 layout->slider.y = y;
2455 layout->slider.height = height;
2457 /* These are publically exported */
2458 range->slider_start = layout->slider.y;
2459 range->slider_end = layout->slider.y + layout->slider.height;
2464 gint stepper_width, stepper_height;
2466 /* Steppers are the height of the range, and stepper_size in
2467 * width, or if we don't have enough width, divided equally
2468 * among available space.
2470 stepper_height = range_rect.height - trough_border * 2;
2472 if (stepper_height < 1)
2473 stepper_height = range_rect.height; /* screw the trough border */
2475 if (n_steppers == 0)
2476 stepper_width = 0; /* avoid divide by n_steppers */
2478 stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
2482 layout->stepper_a.x = range_rect.x + trough_border;
2483 layout->stepper_a.y = range_rect.y + trough_border;
2485 if (range->has_stepper_a)
2487 layout->stepper_a.width = stepper_width;
2488 layout->stepper_a.height = stepper_height;
2492 layout->stepper_a.width = 0;
2493 layout->stepper_a.height = 0;
2498 layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
2499 layout->stepper_b.y = layout->stepper_a.y;
2501 if (range->has_stepper_b)
2503 layout->stepper_b.width = stepper_width;
2504 layout->stepper_b.height = stepper_height;
2508 layout->stepper_b.width = 0;
2509 layout->stepper_b.height = 0;
2514 if (range->has_stepper_d)
2516 layout->stepper_d.width = stepper_width;
2517 layout->stepper_d.height = stepper_height;
2521 layout->stepper_d.width = 0;
2522 layout->stepper_d.height = 0;
2525 layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - trough_border;
2526 layout->stepper_d.y = layout->stepper_a.y;
2531 if (range->has_stepper_c)
2533 layout->stepper_c.width = stepper_width;
2534 layout->stepper_c.height = stepper_height;
2538 layout->stepper_c.width = 0;
2539 layout->stepper_c.height = 0;
2542 layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
2543 layout->stepper_c.y = layout->stepper_a.y;
2545 /* Now the trough is the remaining space between steppers B and C,
2548 layout->trough.x = layout->stepper_b.x + layout->stepper_b.width;
2549 layout->trough.y = range_rect.y;
2551 layout->trough.width = layout->stepper_c.x - (layout->stepper_b.x + layout->stepper_b.width);
2552 layout->trough.height = range_rect.height;
2554 /* Slider fits into the trough, with stepper_spacing on either side,
2555 * and the size/position based on the adjustment or fixed, depending.
2557 layout->slider.y = layout->trough.y + trough_border;
2558 layout->slider.height = layout->trough.height - trough_border * 2;
2560 /* Compute slider position/length */
2562 gint x, left, right, width;
2564 left = layout->trough.x + stepper_spacing;
2565 right = layout->trough.x + layout->trough.width - stepper_spacing;
2567 /* slider width is the fraction (page_size /
2568 * total_adjustment_range) times the trough width in pixels
2571 if (range->adjustment->upper - range->adjustment->lower != 0)
2572 width = ((right - left) * (range->adjustment->page_size /
2573 (range->adjustment->upper - range->adjustment->lower)));
2575 width = range->min_slider_size;
2577 if (width < range->min_slider_size ||
2578 range->slider_size_fixed)
2579 width = range->min_slider_size;
2581 width = MIN (width, (layout->trough.width - stepper_spacing * 2));
2585 if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
2586 x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
2587 (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
2589 x = CLAMP (x, left, right);
2591 if (should_invert (range))
2592 x = right - (x - left + width);
2594 layout->slider.x = x;
2595 layout->slider.width = width;
2597 /* These are publically exported */
2598 range->slider_start = layout->slider.x;
2599 range->slider_end = layout->slider.x + layout->slider.width;
2603 gtk_range_update_mouse_location (range);
2606 static GdkRectangle*
2607 get_area (GtkRange *range,
2608 MouseLocation location)
2612 case MOUSE_STEPPER_A:
2613 return &range->layout->stepper_a;
2614 case MOUSE_STEPPER_B:
2615 return &range->layout->stepper_b;
2616 case MOUSE_STEPPER_C:
2617 return &range->layout->stepper_c;
2618 case MOUSE_STEPPER_D:
2619 return &range->layout->stepper_d;
2621 return &range->layout->trough;
2623 return &range->layout->slider;
2629 g_warning (G_STRLOC": bug");
2634 gtk_range_real_change_value (GtkRange *range,
2635 GtkScrollType scroll,
2638 /* potentially adjust the bounds _before we clamp */
2639 g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
2641 value = CLAMP (value, range->adjustment->lower,
2642 (range->adjustment->upper - range->adjustment->page_size));
2644 if (range->round_digits >= 0)
2649 i = range->round_digits;
2654 value = floor ((value * power) + 0.5) / power;
2657 if (range->adjustment->value != value)
2659 range->need_recalc = TRUE;
2661 gtk_widget_queue_draw (GTK_WIDGET (range));
2663 switch (range->update_policy)
2665 case GTK_UPDATE_CONTINUOUS:
2666 gtk_adjustment_set_value (range->adjustment, value);
2669 /* Delayed means we update after a period of inactivity */
2670 case GTK_UPDATE_DELAYED:
2671 gtk_range_reset_update_timer (range);
2674 /* Discontinuous means we update on button release */
2675 case GTK_UPDATE_DISCONTINUOUS:
2676 /* don't emit value_changed signal */
2677 range->adjustment->value = value;
2678 range->update_pending = TRUE;
2686 gtk_range_update_value (GtkRange *range)
2688 gtk_range_remove_update_timer (range);
2690 if (range->update_pending)
2692 gtk_adjustment_value_changed (range->adjustment);
2694 range->update_pending = FALSE;
2698 struct _GtkRangeStepTimer
2705 second_timeout (gpointer data)
2709 GDK_THREADS_ENTER ();
2710 range = GTK_RANGE (data);
2711 gtk_range_scroll (range, range->timer->step);
2712 GDK_THREADS_LEAVE ();
2718 initial_timeout (gpointer data)
2721 GtkSettings *settings;
2724 GDK_THREADS_ENTER ();
2725 settings = gtk_widget_get_settings (GTK_WIDGET (data));
2726 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2728 range = GTK_RANGE (data);
2729 range->timer->timeout_id = g_timeout_add (timeout * SCROLL_DELAY_FACTOR,
2732 GDK_THREADS_LEAVE ();
2739 gtk_range_add_step_timer (GtkRange *range,
2742 GtkSettings *settings;
2745 g_return_if_fail (range->timer == NULL);
2746 g_return_if_fail (step != GTK_SCROLL_NONE);
2748 settings = gtk_widget_get_settings (GTK_WIDGET (range));
2749 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2751 range->timer = g_new (GtkRangeStepTimer, 1);
2753 range->timer->timeout_id = g_timeout_add (timeout,
2756 range->timer->step = step;
2758 gtk_range_scroll (range, range->timer->step);
2762 gtk_range_remove_step_timer (GtkRange *range)
2766 if (range->timer->timeout_id != 0)
2767 g_source_remove (range->timer->timeout_id);
2769 g_free (range->timer);
2771 range->timer = NULL;
2776 update_timeout (gpointer data)
2780 GDK_THREADS_ENTER ();
2781 range = GTK_RANGE (data);
2782 gtk_range_update_value (range);
2783 range->update_timeout_id = 0;
2784 GDK_THREADS_LEAVE ();
2791 gtk_range_reset_update_timer (GtkRange *range)
2793 gtk_range_remove_update_timer (range);
2795 range->update_timeout_id = g_timeout_add (UPDATE_DELAY,
2801 gtk_range_remove_update_timer (GtkRange *range)
2803 if (range->update_timeout_id != 0)
2805 g_source_remove (range->update_timeout_id);
2806 range->update_timeout_id = 0;
2810 #define __GTK_RANGE_C__
2811 #include "gtkaliasdef.c"