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/.
33 #include "gtkmarshalers.h"
36 #include "gtkscrollbar.h"
37 #include "gtkprivate.h"
40 #define SCROLL_DELAY_FACTOR 5 /* Scroll repeat multiplier */
41 #define UPDATE_DELAY 300 /* Delay for queued update */
48 PROP_LOWER_STEPPER_SENSITIVITY,
49 PROP_UPPER_STEPPER_SENSITIVITY
68 MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
71 struct _GtkRangeLayout
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
84 /* Layout-related state */
86 MouseLocation mouse_location;
87 /* last mouse coords we got, or -1 if mouse is outside the range */
90 /* "grabbed" mouse location, OUTSIDE for no grab */
91 MouseLocation grab_location;
92 gint grab_button; /* 0 if none */
94 /* Stepper sensitivity */
95 GtkSensitivityType lower_sensitivity;
96 GtkSensitivityType upper_sensitivity;
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,
106 static void gtk_range_get_property (GObject *object,
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,
145 static void stop_scrolling (GtkRange *range);
149 static void gtk_range_move_slider (GtkRange *range,
150 GtkScrollType scroll);
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,
162 gint *stepper_spacing,
163 gint *arrow_displacement_x,
164 gint *arrow_displacement_y);
165 static void gtk_range_calc_request (GtkRange *range,
169 gint stepper_spacing,
170 GdkRectangle *range_rect,
173 gint *slider_length_p);
174 static void gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
176 static void gtk_range_adjustment_changed (GtkAdjustment *adjustment,
178 static void gtk_range_add_step_timer (GtkRange *range,
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,
188 static void gtk_range_update_value (GtkRange *range);
191 static GtkWidgetClass *parent_class = NULL;
192 static guint signals[LAST_SIGNAL];
196 gtk_range_get_type (void)
198 static GType range_type = 0;
202 static const GTypeInfo range_info =
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 */
212 (GInstanceInitFunc) gtk_range_init,
213 NULL, /* value_table */
216 range_type = g_type_register_static (GTK_TYPE_WIDGET, I_("GtkRange"),
217 &range_info, G_TYPE_FLAG_ABSTRACT);
224 gtk_range_class_init (GtkRangeClass *class)
226 GObjectClass *gobject_class;
227 GtkObjectClass *object_class;
228 GtkWidgetClass *widget_class;
230 gobject_class = G_OBJECT_CLASS (class);
231 object_class = (GtkObjectClass*) class;
232 widget_class = (GtkWidgetClass*) class;
234 parent_class = g_type_class_peek_parent (class);
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;
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;
259 class->move_slider = gtk_range_move_slider;
260 class->change_value = gtk_range_real_change_value;
262 class->slider_detail = "slider";
263 class->stepper_detail = "stepper";
265 signals[VALUE_CHANGED] =
266 g_signal_new (I_("value_changed"),
267 G_TYPE_FROM_CLASS (gobject_class),
269 G_STRUCT_OFFSET (GtkRangeClass, value_changed),
271 _gtk_marshal_NONE__NONE,
274 signals[ADJUST_BOUNDS] =
275 g_signal_new (I_("adjust_bounds"),
276 G_TYPE_FROM_CLASS (gobject_class),
278 G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
280 _gtk_marshal_VOID__DOUBLE,
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),
290 _gtk_marshal_VOID__ENUM,
292 GTK_TYPE_SCROLL_TYPE);
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.
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
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.
315 * It is not possible to use delayed update policies in an overridden
316 * ::change-value handler.
320 signals[CHANGE_VALUE] =
321 g_signal_new (I_("change_value"),
322 G_TYPE_FROM_CLASS (gobject_class),
324 G_STRUCT_OFFSET (GtkRangeClass, change_value),
325 _gtk_boolean_handled_accumulator, NULL,
326 _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
328 GTK_TYPE_SCROLL_TYPE,
331 g_object_class_install_property (gobject_class,
333 g_param_spec_enum ("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));
340 g_object_class_install_property (gobject_class,
342 g_param_spec_object ("adjustment",
344 P_("The GtkAdjustment that contains the current value of this range object"),
346 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
348 g_object_class_install_property (gobject_class,
350 g_param_spec_boolean ("inverted",
352 P_("Invert direction slider moves to increase range value"),
354 GTK_PARAM_READWRITE));
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));
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));
374 gtk_widget_class_install_style_property (widget_class,
375 g_param_spec_int ("slider-width",
377 P_("Width of scrollbar or scale thumb"),
381 GTK_PARAM_READABLE));
382 gtk_widget_class_install_style_property (widget_class,
383 g_param_spec_int ("trough-border",
385 P_("Spacing between thumb/steppers and outer trough bevel"),
389 GTK_PARAM_READABLE));
390 gtk_widget_class_install_style_property (widget_class,
391 g_param_spec_int ("stepper-size",
393 P_("Length of step buttons at ends"),
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"),
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"),
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"),
421 GTK_PARAM_READABLE));
425 gtk_range_set_property (GObject *object,
432 range = GTK_RANGE (object);
436 case PROP_UPDATE_POLICY:
437 gtk_range_set_update_policy (range, g_value_get_enum (value));
439 case PROP_ADJUSTMENT:
440 gtk_range_set_adjustment (range, g_value_get_object (value));
443 gtk_range_set_inverted (range, g_value_get_boolean (value));
445 case PROP_LOWER_STEPPER_SENSITIVITY:
446 gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
448 case PROP_UPPER_STEPPER_SENSITIVITY:
449 gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
452 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
458 gtk_range_get_property (GObject *object,
465 range = GTK_RANGE (object);
469 case PROP_UPDATE_POLICY:
470 g_value_set_enum (value, range->update_policy);
472 case PROP_ADJUSTMENT:
473 g_value_set_object (value, range->adjustment);
476 g_value_set_boolean (value, range->inverted);
478 case PROP_LOWER_STEPPER_SENSITIVITY:
479 g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
481 case PROP_UPPER_STEPPER_SENSITIVITY:
482 g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
485 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
491 gtk_range_init (GtkRange *range)
493 GTK_WIDGET_SET_FLAGS (range, GTK_NO_WINDOW);
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;
518 * gtk_range_get_adjustment:
519 * @range: a #GtkRange
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
526 * Return value: a #GtkAdjustment
529 gtk_range_get_adjustment (GtkRange *range)
531 g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
533 if (!range->adjustment)
534 gtk_range_set_adjustment (range, NULL);
536 return range->adjustment;
540 * gtk_range_set_update_policy:
541 * @range: a #GtkRange
542 * @policy: update policy
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
555 gtk_range_set_update_policy (GtkRange *range,
556 GtkUpdateType policy)
558 g_return_if_fail (GTK_IS_RANGE (range));
560 if (range->update_policy != policy)
562 range->update_policy = policy;
563 g_object_notify (G_OBJECT (range), "update-policy");
568 * gtk_range_get_update_policy:
569 * @range: a #GtkRange
571 * Gets the update policy of @range. See gtk_range_set_update_policy().
573 * Return value: the current update policy
576 gtk_range_get_update_policy (GtkRange *range)
578 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
580 return range->update_policy;
584 * gtk_range_set_adjustment:
585 * @range: a #GtkRange
586 * @adjustment: a #GtkAdjustment
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.
598 gtk_range_set_adjustment (GtkRange *range,
599 GtkAdjustment *adjustment)
601 g_return_if_fail (GTK_IS_RANGE (range));
604 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
606 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
608 if (range->adjustment != adjustment)
610 if (range->adjustment)
612 g_signal_handlers_disconnect_by_func (range->adjustment,
613 gtk_range_adjustment_changed,
615 g_signal_handlers_disconnect_by_func (range->adjustment,
616 gtk_range_adjustment_value_changed,
618 g_object_unref (range->adjustment);
621 range->adjustment = adjustment;
622 g_object_ref_sink (adjustment);
624 g_signal_connect (adjustment, "changed",
625 G_CALLBACK (gtk_range_adjustment_changed),
627 g_signal_connect (adjustment, "value_changed",
628 G_CALLBACK (gtk_range_adjustment_value_changed),
631 gtk_range_adjustment_changed (adjustment, range);
632 g_object_notify (G_OBJECT (range), "adjustment");
637 * gtk_range_set_inverted:
638 * @range: a #GtkRange
639 * @setting: %TRUE to invert the range
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.
648 gtk_range_set_inverted (GtkRange *range,
651 g_return_if_fail (GTK_IS_RANGE (range));
653 setting = setting != FALSE;
655 if (setting != range->inverted)
657 range->inverted = setting;
658 g_object_notify (G_OBJECT (range), "inverted");
659 gtk_widget_queue_resize (GTK_WIDGET (range));
664 * gtk_range_get_inverted:
665 * @range: a #GtkRange
667 * Gets the value set by gtk_range_set_inverted().
669 * Return value: %TRUE if the range is inverted
672 gtk_range_get_inverted (GtkRange *range)
674 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
676 return range->inverted;
680 * gtk_range_set_lower_stepper_sensitivity:
681 * @range: a #GtkRange
682 * @sensitivity: the lower stepper's sensitivity policy.
684 * Sets the sensitivity policy for the stepper that points to the
685 * 'lower' end of the GtkRange's adjustment.
690 gtk_range_set_lower_stepper_sensitivity (GtkRange *range,
691 GtkSensitivityType sensitivity)
693 g_return_if_fail (GTK_IS_RANGE (range));
695 if (range->layout->lower_sensitivity != sensitivity)
697 range->layout->lower_sensitivity = sensitivity;
698 gtk_widget_queue_draw (GTK_WIDGET (range));
699 g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
704 * gtk_range_get_lower_stepper_sensitivity:
705 * @range: a #GtkRange
707 * Gets the sensitivity policy for the stepper that points to the
708 * 'lower' end of the GtkRange's adjustment.
710 * Return value: The lower stepper's sensitivity policy.
715 gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
717 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
719 return range->layout->lower_sensitivity;
723 * gtk_range_set_upper_stepper_sensitivity:
724 * @range: a #GtkRange
725 * @sensitivity: the upper stepper's sensitivity policy.
727 * Sets the sensitivity policy for the stepper that points to the
728 * 'upper' end of the GtkRange's adjustment.
733 gtk_range_set_upper_stepper_sensitivity (GtkRange *range,
734 GtkSensitivityType sensitivity)
736 g_return_if_fail (GTK_IS_RANGE (range));
738 if (range->layout->upper_sensitivity != sensitivity)
740 range->layout->upper_sensitivity = sensitivity;
741 gtk_widget_queue_draw (GTK_WIDGET (range));
742 g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
747 * gtk_range_get_upper_stepper_sensitivity:
748 * @range: a #GtkRange
750 * Gets the sensitivity policy for the stepper that points to the
751 * 'upper' end of the GtkRange's adjustment.
753 * Return value: The upper stepper's sensitivity policy.
758 gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
760 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
762 return range->layout->upper_sensitivity;
766 * gtk_range_set_increments:
767 * @range: a #GtkRange
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.
778 gtk_range_set_increments (GtkRange *range,
782 g_return_if_fail (GTK_IS_RANGE (range));
784 range->adjustment->step_increment = step;
785 range->adjustment->page_increment = page;
787 gtk_adjustment_changed (range->adjustment);
791 * gtk_range_set_range:
792 * @range: a #GtkRange
793 * @min: minimum range value
794 * @max: maximum range value
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.)
801 gtk_range_set_range (GtkRange *range,
807 g_return_if_fail (GTK_IS_RANGE (range));
808 g_return_if_fail (min < max);
810 range->adjustment->lower = min;
811 range->adjustment->upper = max;
813 value = CLAMP (range->adjustment->value,
814 range->adjustment->lower,
815 (range->adjustment->upper - range->adjustment->page_size));
817 gtk_adjustment_set_value (range->adjustment, value);
818 gtk_adjustment_changed (range->adjustment);
822 * gtk_range_set_value:
823 * @range: a #GtkRange
824 * @value: new value of the range
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
833 gtk_range_set_value (GtkRange *range,
836 g_return_if_fail (GTK_IS_RANGE (range));
838 value = CLAMP (value, range->adjustment->lower,
839 (range->adjustment->upper - range->adjustment->page_size));
841 gtk_adjustment_set_value (range->adjustment, value);
845 * gtk_range_get_value:
846 * @range: a #GtkRange
848 * Gets the current value of the range.
850 * Return value: current value of the range.
853 gtk_range_get_value (GtkRange *range)
855 g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
857 return range->adjustment->value;
861 should_invert (GtkRange *range)
863 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
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);
869 return range->inverted;
873 gtk_range_finalize (GObject *object)
875 GtkRange *range = GTK_RANGE (object);
877 g_free (range->layout);
879 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
883 gtk_range_destroy (GtkObject *object)
885 GtkRange *range = GTK_RANGE (object);
887 gtk_range_remove_step_timer (range);
888 gtk_range_remove_update_timer (range);
890 if (range->adjustment)
892 g_signal_handlers_disconnect_by_func (range->adjustment,
893 gtk_range_adjustment_changed,
895 g_signal_handlers_disconnect_by_func (range->adjustment,
896 gtk_range_adjustment_value_changed,
898 g_object_unref (range->adjustment);
899 range->adjustment = NULL;
902 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
906 gtk_range_size_request (GtkWidget *widget,
907 GtkRequisition *requisition)
910 gint slider_width, stepper_size, trough_border, stepper_spacing;
911 GdkRectangle range_rect;
914 range = GTK_RANGE (widget);
916 gtk_range_get_props (range,
917 &slider_width, &stepper_size, &trough_border, &stepper_spacing,
920 gtk_range_calc_request (range,
921 slider_width, stepper_size, trough_border, stepper_spacing,
922 &range_rect, &border, NULL, NULL);
924 requisition->width = range_rect.width + border.left + border.right;
925 requisition->height = range_rect.height + border.top + border.bottom;
929 gtk_range_size_allocate (GtkWidget *widget,
930 GtkAllocation *allocation)
934 range = GTK_RANGE (widget);
936 widget->allocation = *allocation;
938 range->need_recalc = TRUE;
939 gtk_range_calc_layout (range, range->adjustment->value);
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);
950 gtk_range_realize (GtkWidget *widget)
953 GdkWindowAttr attributes;
954 gint attributes_mask;
956 range = GTK_RANGE (widget);
958 gtk_range_calc_layout (range, range->adjustment->value);
960 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
962 widget->window = gtk_widget_get_parent_window (widget);
963 g_object_ref (widget->window);
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);
979 attributes_mask = GDK_WA_X | GDK_WA_Y;
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);
985 widget->style = gtk_style_attach (widget->style, widget->window);
989 gtk_range_unrealize (GtkWidget *widget)
991 GtkRange *range = GTK_RANGE (widget);
993 gtk_range_remove_step_timer (range);
994 gtk_range_remove_update_timer (range);
996 gdk_window_set_user_data (range->event_window, NULL);
997 gdk_window_destroy (range->event_window);
998 range->event_window = NULL;
1000 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1001 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1005 gtk_range_map (GtkWidget *widget)
1007 GtkRange *range = GTK_RANGE (widget);
1009 gdk_window_show (range->event_window);
1011 GTK_WIDGET_CLASS (parent_class)->map (widget);
1015 gtk_range_unmap (GtkWidget *widget)
1017 GtkRange *range = GTK_RANGE (widget);
1019 stop_scrolling (range);
1021 gdk_window_hide (range->event_window);
1023 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
1027 draw_stepper (GtkRange *range,
1029 GtkArrowType arrow_type,
1031 gboolean prelighted,
1034 GtkStateType state_type;
1035 GtkShadowType shadow_type;
1036 GdkRectangle intersection;
1037 GtkWidget *widget = GTK_WIDGET (range);
1044 gboolean arrow_sensitive = TRUE;
1046 /* More to get the right clip region than for efficiency */
1047 if (!gdk_rectangle_intersect (area, rect, &intersection))
1050 intersection.x += widget->allocation.x;
1051 intersection.y += widget->allocation.y;
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)))
1058 switch (range->layout->upper_sensitivity)
1060 case GTK_SENSITIVITY_AUTO:
1062 (range->adjustment->value <
1063 (range->adjustment->upper - range->adjustment->page_size));
1066 case GTK_SENSITIVITY_ON:
1067 arrow_sensitive = TRUE;
1070 case GTK_SENSITIVITY_OFF:
1071 arrow_sensitive = FALSE;
1077 switch (range->layout->lower_sensitivity)
1079 case GTK_SENSITIVITY_AUTO:
1081 (range->adjustment->value > range->adjustment->lower);
1084 case GTK_SENSITIVITY_ON:
1085 arrow_sensitive = TRUE;
1088 case GTK_SENSITIVITY_OFF:
1089 arrow_sensitive = FALSE;
1094 if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
1095 state_type = GTK_STATE_INSENSITIVE;
1097 state_type = GTK_STATE_ACTIVE;
1098 else if (prelighted)
1099 state_type = GTK_STATE_PRELIGHT;
1101 state_type = GTK_STATE_NORMAL;
1103 if (clicked && arrow_sensitive)
1104 shadow_type = GTK_SHADOW_IN;
1106 shadow_type = GTK_SHADOW_OUT;
1108 gtk_paint_box (widget->style,
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,
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;
1123 if (clicked && arrow_sensitive)
1125 gint arrow_displacement_x;
1126 gint arrow_displacement_y;
1128 gtk_range_get_props (GTK_RANGE (widget), NULL, NULL, NULL, NULL,
1129 &arrow_displacement_x, &arrow_displacement_y);
1131 arrow_x += arrow_displacement_x;
1132 arrow_y += arrow_displacement_y;
1135 gtk_paint_arrow (widget->style,
1137 state_type, shadow_type,
1138 &intersection, widget,
1139 GTK_RANGE_GET_CLASS (range)->stepper_detail,
1142 arrow_x, arrow_y, arrow_width, arrow_height);
1146 gtk_range_expose (GtkWidget *widget,
1147 GdkEventExpose *event)
1152 GdkRectangle expose_area; /* Relative to widget->allocation */
1154 gint focus_line_width = 0;
1155 gint focus_padding = 0;
1156 gboolean touchscreen;
1158 g_object_get (gtk_widget_get_settings (widget),
1159 "gtk-touchscreen-mode", &touchscreen,
1162 range = GTK_RANGE (widget);
1164 if (GTK_WIDGET_CAN_FOCUS (range))
1166 gtk_widget_style_get (GTK_WIDGET (range),
1167 "focus-line-width", &focus_line_width,
1168 "focus-padding", &focus_padding,
1172 expose_area = event->area;
1173 expose_area.x -= widget->allocation.x;
1174 expose_area.y -= widget->allocation.y;
1176 gtk_range_calc_layout (range, range->adjustment->value);
1178 sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
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)
1184 /* The gdk_rectangle_intersect is more to get the right
1185 * clip region (limited to range_rect) than for efficiency
1187 if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1190 area.x += widget->allocation.x;
1191 area.y += widget->allocation.y;
1193 gtk_paint_box (widget->style,
1195 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
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));
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);
1215 state = GTK_STATE_INSENSITIVE;
1216 else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1217 state = GTK_STATE_PRELIGHT;
1219 state = GTK_STATE_NORMAL;
1221 if (gdk_rectangle_intersect (&expose_area,
1222 &range->layout->slider,
1225 area.x += widget->allocation.x;
1226 area.y += widget->allocation.y;
1228 gtk_paint_slider (widget->style,
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);
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,
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,
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,
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,
1274 range_grab_add (GtkRange *range,
1275 MouseLocation location,
1278 /* we don't actually gtk_grab, since a button is down */
1280 gtk_grab_add (GTK_WIDGET (range));
1282 range->layout->grab_location = location;
1283 range->layout->grab_button = button;
1285 if (gtk_range_update_mouse_location (range))
1286 gtk_widget_queue_draw (GTK_WIDGET (range));
1290 range_grab_remove (GtkRange *range)
1292 gtk_grab_remove (GTK_WIDGET (range));
1294 range->layout->grab_location = MOUSE_OUTSIDE;
1295 range->layout->grab_button = 0;
1297 if (gtk_range_update_mouse_location (range))
1298 gtk_widget_queue_draw (GTK_WIDGET (range));
1301 static GtkScrollType
1302 range_get_scroll_for_grab (GtkRange *range)
1306 invert = should_invert (range);
1307 switch (range->layout->grab_location)
1309 /* Backward stepper */
1310 case MOUSE_STEPPER_A:
1311 case MOUSE_STEPPER_C:
1312 switch (range->layout->grab_button)
1315 return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
1318 return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
1321 return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
1326 /* Forward stepper */
1327 case MOUSE_STEPPER_B:
1328 case MOUSE_STEPPER_D:
1329 switch (range->layout->grab_button)
1332 return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
1335 return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
1338 return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
1346 if (range->trough_click_forward)
1347 return GTK_SCROLL_PAGE_FORWARD;
1349 return GTK_SCROLL_PAGE_BACKWARD;
1359 return GTK_SCROLL_NONE;
1363 coord_to_value (GtkRange *range,
1369 if (range->orientation == GTK_ORIENTATION_VERTICAL)
1370 if (range->layout->trough.height == range->layout->slider.height)
1373 frac = ((coord - range->layout->trough.y) /
1374 (gdouble) (range->layout->trough.height - range->layout->slider.height));
1376 if (range->layout->trough.width == range->layout->slider.width)
1379 frac = ((coord - range->layout->trough.x) /
1380 (gdouble) (range->layout->trough.width - range->layout->slider.width));
1382 if (should_invert (range))
1385 value = range->adjustment->lower +
1386 frac * (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size);
1392 gtk_range_button_press (GtkWidget *widget,
1393 GdkEventButton *event)
1395 GtkRange *range = GTK_RANGE (widget);
1397 if (!GTK_WIDGET_HAS_FOCUS (widget))
1398 gtk_widget_grab_focus (widget);
1400 /* ignore presses when we're already doing something else. */
1401 if (range->layout->grab_location != MOUSE_OUTSIDE)
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);
1409 if (range->layout->mouse_location == MOUSE_TROUGH &&
1412 /* button 1 steps by page increment, as with button 2 on a stepper
1414 GtkScrollType scroll;
1415 gdouble click_value;
1417 click_value = coord_to_value (range,
1418 range->orientation == GTK_ORIENTATION_VERTICAL ?
1419 event->y : event->x);
1421 range->trough_click_forward = click_value > range->adjustment->value;
1422 range_grab_add (range, MOUSE_TROUGH, event->button);
1424 scroll = range_get_scroll_for_grab (range);
1426 gtk_range_add_step_timer (range, scroll);
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))
1436 GdkRectangle *stepper_area;
1437 GtkScrollType scroll;
1439 range_grab_add (range, range->layout->mouse_location, event->button);
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);
1448 scroll = range_get_scroll_for_grab (range);
1449 if (scroll != GTK_SCROLL_NONE)
1450 gtk_range_add_step_timer (range, scroll);
1454 else if ((range->layout->mouse_location == MOUSE_TROUGH &&
1455 event->button == 2) ||
1456 range->layout->mouse_location == MOUSE_SLIDER)
1458 gboolean need_value_update = FALSE;
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.
1465 if (event->button == 2)
1467 gdouble slider_low_value, slider_high_value, new_value;
1470 coord_to_value (range,
1471 range->orientation == GTK_ORIENTATION_VERTICAL ?
1472 event->y : event->x);
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);
1479 /* compute new value for warped slider */
1480 new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
1482 /* recalc slider, so we can set slide_initial_slider_position
1485 range->need_recalc = TRUE;
1486 gtk_range_calc_layout (range, new_value);
1488 /* defer adjustment updates to update_slider_position() in order
1489 * to keep pixel quantisation
1491 need_value_update = TRUE;
1494 if (range->orientation == GTK_ORIENTATION_VERTICAL)
1496 range->slide_initial_slider_position = range->layout->slider.y;
1497 range->slide_initial_coordinate = event->y;
1501 range->slide_initial_slider_position = range->layout->slider.x;
1502 range->slide_initial_coordinate = event->x;
1505 if (need_value_update)
1506 update_slider_position (range, event->x, event->y);
1508 range_grab_add (range, MOUSE_SLIDER, event->button);
1516 /* During a slide, move the slider as required given new mouse position */
1518 update_slider_position (GtkRange *range,
1527 if (range->orientation == GTK_ORIENTATION_VERTICAL)
1528 delta = mouse_y - range->slide_initial_coordinate;
1530 delta = mouse_x - range->slide_initial_coordinate;
1532 c = range->slide_initial_slider_position + delta;
1534 new_value = coord_to_value (range, c);
1536 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
1541 stop_scrolling (GtkRange *range)
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);
1548 /* Just be lazy about this, if we scrolled it will all redraw anyway,
1549 * so no point optimizing the button deactivate case
1551 gtk_widget_queue_draw (GTK_WIDGET (range));
1555 gtk_range_grab_broken (GtkWidget *widget,
1556 GdkEventGrabBroken *event)
1558 GtkRange *range = GTK_RANGE (widget);
1560 if (range->layout->grab_location != MOUSE_OUTSIDE)
1562 if (range->layout->grab_location == MOUSE_SLIDER)
1563 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
1565 stop_scrolling (range);
1574 gtk_range_button_release (GtkWidget *widget,
1575 GdkEventButton *event)
1577 GtkRange *range = GTK_RANGE (widget);
1579 if (event->window == range->event_window)
1581 range->layout->mouse_x = event->x;
1582 range->layout->mouse_y = event->y;
1586 gdk_window_get_pointer (range->event_window,
1587 &range->layout->mouse_x,
1588 &range->layout->mouse_y,
1592 if (range->layout->grab_button == event->button)
1594 if (range->layout->grab_location == MOUSE_SLIDER)
1595 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
1597 stop_scrolling (range);
1606 * _gtk_range_get_wheel_delta:
1607 * @range: a #GtkRange
1608 * @direction: A #GdkScrollDirection
1610 * Returns a good step value for the mouse wheel.
1612 * Return value: A good step value for the mouse wheel.
1617 _gtk_range_get_wheel_delta (GtkRange *range,
1618 GdkScrollDirection direction)
1620 GtkAdjustment *adj = range->adjustment;
1623 if (GTK_IS_SCROLLBAR (range))
1624 delta = pow (adj->page_size, 2.0 / 3.0);
1626 delta = adj->step_increment * 2;
1628 if (direction == GDK_SCROLL_UP ||
1629 direction == GDK_SCROLL_LEFT)
1632 if (range->inverted)
1639 gtk_range_scroll_event (GtkWidget *widget,
1640 GdkEventScroll *event)
1642 GtkRange *range = GTK_RANGE (widget);
1644 if (GTK_WIDGET_REALIZED (range))
1646 GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
1650 delta = _gtk_range_get_wheel_delta (range, event->direction);
1652 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1653 GTK_SCROLL_JUMP, adj->value + delta,
1656 /* Policy DELAYED makes sense with scroll events,
1657 * but DISCONTINUOUS doesn't, so we update immediately
1660 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
1661 gtk_range_update_value (range);
1668 gtk_range_motion_notify (GtkWidget *widget,
1669 GdkEventMotion *event)
1674 range = GTK_RANGE (widget);
1676 gdk_window_get_pointer (range->event_window, &x, &y, NULL);
1678 range->layout->mouse_x = x;
1679 range->layout->mouse_y = y;
1681 if (gtk_range_update_mouse_location (range))
1682 gtk_widget_queue_draw (widget);
1684 if (range->layout->grab_location == MOUSE_SLIDER)
1685 update_slider_position (range, x, y);
1687 /* We handled the event if the mouse was in the range_rect */
1688 return range->layout->mouse_location != MOUSE_OUTSIDE;
1692 gtk_range_enter_notify (GtkWidget *widget,
1693 GdkEventCrossing *event)
1695 GtkRange *range = GTK_RANGE (widget);
1697 range->layout->mouse_x = event->x;
1698 range->layout->mouse_y = event->y;
1700 if (gtk_range_update_mouse_location (range))
1701 gtk_widget_queue_draw (widget);
1707 gtk_range_leave_notify (GtkWidget *widget,
1708 GdkEventCrossing *event)
1710 GtkRange *range = GTK_RANGE (widget);
1712 range->layout->mouse_x = -1;
1713 range->layout->mouse_y = -1;
1715 if (gtk_range_update_mouse_location (range))
1716 gtk_widget_queue_draw (widget);
1722 gtk_range_grab_notify (GtkWidget *widget,
1723 gboolean was_grabbed)
1726 stop_scrolling (GTK_RANGE (widget));
1730 gtk_range_state_changed (GtkWidget *widget,
1731 GtkStateType previous_state)
1733 if (!GTK_WIDGET_IS_SENSITIVE (widget))
1734 stop_scrolling (GTK_RANGE (widget));
1737 #define check_rectangle(rectangle1, rectangle2) \
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; \
1746 layout_changed (GtkRangeLayout *layout1,
1747 GtkRangeLayout *layout2)
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);
1760 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
1763 GtkRange *range = GTK_RANGE (data);
1764 /* create a copy of the layout */
1765 GtkRangeLayout layout = *range->layout;
1767 range->need_recalc = TRUE;
1768 gtk_range_calc_layout (range, range->adjustment->value);
1770 /* now check whether the layout changed */
1771 if (layout_changed (range->layout, &layout))
1772 gtk_widget_queue_draw (GTK_WIDGET (range));
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.
1784 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
1787 GtkRange *range = GTK_RANGE (data);
1788 /* create a copy of the layout */
1789 GtkRangeLayout layout = *range->layout;
1791 range->need_recalc = TRUE;
1792 gtk_range_calc_layout (range, range->adjustment->value);
1794 /* now check whether the layout changed */
1795 if (layout_changed (range->layout, &layout))
1797 gtk_widget_queue_draw (GTK_WIDGET (range));
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);
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.
1812 g_signal_emit (range, signals[VALUE_CHANGED], 0);
1816 gtk_range_style_set (GtkWidget *widget,
1817 GtkStyle *previous_style)
1819 GtkRange *range = GTK_RANGE (widget);
1821 range->need_recalc = TRUE;
1823 (* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1827 step_back (GtkRange *range)
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);
1838 step_forward (GtkRange *range)
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);
1850 page_back (GtkRange *range)
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);
1861 page_forward (GtkRange *range)
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);
1872 scroll_begin (GtkRange *range)
1875 g_signal_emit (range, signals[CHANGE_VALUE], 0,
1876 GTK_SCROLL_START, range->adjustment->lower,
1881 scroll_end (GtkRange *range)
1886 newval = range->adjustment->upper - range->adjustment->page_size;
1887 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
1892 gtk_range_scroll (GtkRange *range,
1893 GtkScrollType scroll)
1897 case GTK_SCROLL_STEP_LEFT:
1898 if (should_invert (range))
1899 step_forward (range);
1904 case GTK_SCROLL_STEP_UP:
1905 if (should_invert (range))
1906 step_forward (range);
1911 case GTK_SCROLL_STEP_RIGHT:
1912 if (should_invert (range))
1915 step_forward (range);
1918 case GTK_SCROLL_STEP_DOWN:
1919 if (should_invert (range))
1922 step_forward (range);
1925 case GTK_SCROLL_STEP_BACKWARD:
1929 case GTK_SCROLL_STEP_FORWARD:
1930 step_forward (range);
1933 case GTK_SCROLL_PAGE_LEFT:
1934 if (should_invert (range))
1935 page_forward (range);
1940 case GTK_SCROLL_PAGE_UP:
1941 if (should_invert (range))
1942 page_forward (range);
1947 case GTK_SCROLL_PAGE_RIGHT:
1948 if (should_invert (range))
1951 page_forward (range);
1954 case GTK_SCROLL_PAGE_DOWN:
1955 if (should_invert (range))
1958 page_forward (range);
1961 case GTK_SCROLL_PAGE_BACKWARD:
1965 case GTK_SCROLL_PAGE_FORWARD:
1966 page_forward (range);
1969 case GTK_SCROLL_START:
1970 scroll_begin (range);
1973 case GTK_SCROLL_END:
1977 case GTK_SCROLL_JUMP:
1978 /* Used by CList, range doesn't use it. */
1981 case GTK_SCROLL_NONE:
1987 gtk_range_move_slider (GtkRange *range,
1988 GtkScrollType scroll)
1990 gtk_range_scroll (range, scroll);
1992 /* Policy DELAYED makes sense with key events,
1993 * but DISCONTINUOUS doesn't, so we update immediately
1996 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
1997 gtk_range_update_value (range);
2001 gtk_range_get_props (GtkRange *range,
2004 gint *trough_border,
2005 gint *stepper_spacing,
2006 gint *arrow_displacement_x,
2007 gint *arrow_displacement_y)
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;
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,
2022 if (GTK_WIDGET_CAN_FOCUS (range))
2024 gint focus_line_width;
2027 gtk_widget_style_get (GTK_WIDGET (range),
2028 "focus-line-width", &focus_line_width,
2029 "focus-padding", &focus_padding,
2032 tmp_trough_border += focus_line_width + focus_padding;
2036 *slider_width = tmp_slider_width;
2039 *trough_border = tmp_trough_border;
2042 *stepper_size = tmp_stepper_size;
2044 if (stepper_spacing)
2045 *stepper_spacing = tmp_stepper_spacing;
2047 if (arrow_displacement_x)
2048 *arrow_displacement_x = tmp_arrow_displacement_x;
2050 if (arrow_displacement_y)
2051 *arrow_displacement_y = tmp_arrow_displacement_y;
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))
2060 /* Update mouse location, return TRUE if it changes */
2062 gtk_range_update_mouse_location (GtkRange *range)
2068 widget = GTK_WIDGET (range);
2070 old = range->layout->mouse_location;
2072 x = range->layout->mouse_x;
2073 y = range->layout->mouse_y;
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;
2092 range->layout->mouse_location = MOUSE_OUTSIDE;
2094 return old != range->layout->mouse_location;
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.
2102 clamp_dimensions (GtkWidget *widget,
2105 gboolean border_expands_horizontally)
2107 gint extra, shortage;
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);
2116 extra = widget->allocation.width - border->left - border->right - rect->width;
2119 if (border_expands_horizontally)
2121 border->left += extra / 2;
2122 border->right += extra / 2 + extra % 2;
2126 rect->width += extra;
2130 /* See if we can fit rect, if not kill the border */
2131 shortage = rect->width - widget->allocation.width;
2134 rect->width = widget->allocation.width;
2135 /* lose the border */
2141 /* See if we can fit rect with borders */
2142 shortage = rect->width + border->left + border->right -
2143 widget->allocation.width;
2146 /* Shrink borders */
2147 border->left -= shortage / 2;
2148 border->right -= shortage / 2 + shortage % 2;
2154 extra = widget->allocation.height - border->top - border->bottom - rect->height;
2157 if (border_expands_horizontally)
2159 /* don't expand border vertically */
2160 rect->height += extra;
2164 border->top += extra / 2;
2165 border->bottom += extra / 2 + extra % 2;
2169 /* See if we can fit rect, if not kill the border */
2170 shortage = rect->height - widget->allocation.height;
2173 rect->height = widget->allocation.height;
2174 /* lose the border */
2180 /* See if we can fit rect with borders */
2181 shortage = rect->height + border->top + border->bottom -
2182 widget->allocation.height;
2185 /* Shrink borders */
2186 border->top -= shortage / 2;
2187 border->bottom -= shortage / 2 + shortage % 2;
2193 gtk_range_calc_request (GtkRange *range,
2197 gint stepper_spacing,
2198 GdkRectangle *range_rect,
2201 gint *slider_length_p)
2211 if (GTK_RANGE_GET_CLASS (range)->get_range_border)
2212 (* GTK_RANGE_GET_CLASS (range)->get_range_border) (range, border);
2215 if (range->has_stepper_a)
2217 if (range->has_stepper_b)
2219 if (range->has_stepper_c)
2221 if (range->has_stepper_d)
2224 slider_length = range->min_slider_size;
2229 /* We never expand to fill available space in the small dimension
2230 * (i.e. vertical scrollbars are always a fixed width)
2232 if (range->orientation == GTK_ORIENTATION_VERTICAL)
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;
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;
2244 *n_steppers_p = n_steppers;
2246 if (slider_length_p)
2247 *slider_length_p = slider_length;
2251 gtk_range_calc_layout (GtkRange *range,
2252 gdouble adjustment_value)
2254 gint slider_width, stepper_size, trough_border, stepper_spacing;
2258 GdkRectangle range_rect;
2259 GtkRangeLayout *layout;
2262 if (!range->need_recalc)
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.
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.
2275 widget = GTK_WIDGET (range);
2276 layout = range->layout;
2278 gtk_range_get_props (range,
2279 &slider_width, &stepper_size, &trough_border, &stepper_spacing,
2282 gtk_range_calc_request (range,
2283 slider_width, stepper_size, trough_border, stepper_spacing,
2284 &range_rect, &border, &n_steppers, &slider_length);
2286 /* We never expand to fill available space in the small dimension
2287 * (i.e. vertical scrollbars are always a fixed width)
2289 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2291 clamp_dimensions (widget, &range_rect, &border, TRUE);
2295 clamp_dimensions (widget, &range_rect, &border, FALSE);
2298 range_rect.x = border.left;
2299 range_rect.y = border.top;
2301 range->range_rect = range_rect;
2303 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2305 gint stepper_width, stepper_height;
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.
2311 stepper_width = range_rect.width - trough_border * 2;
2313 if (stepper_width < 1)
2314 stepper_width = range_rect.width; /* screw the trough border */
2316 if (n_steppers == 0)
2317 stepper_height = 0; /* avoid divide by n_steppers */
2319 stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
2323 layout->stepper_a.x = range_rect.x + trough_border;
2324 layout->stepper_a.y = range_rect.y + trough_border;
2326 if (range->has_stepper_a)
2328 layout->stepper_a.width = stepper_width;
2329 layout->stepper_a.height = stepper_height;
2333 layout->stepper_a.width = 0;
2334 layout->stepper_a.height = 0;
2339 layout->stepper_b.x = layout->stepper_a.x;
2340 layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
2342 if (range->has_stepper_b)
2344 layout->stepper_b.width = stepper_width;
2345 layout->stepper_b.height = stepper_height;
2349 layout->stepper_b.width = 0;
2350 layout->stepper_b.height = 0;
2355 if (range->has_stepper_d)
2357 layout->stepper_d.width = stepper_width;
2358 layout->stepper_d.height = stepper_height;
2362 layout->stepper_d.width = 0;
2363 layout->stepper_d.height = 0;
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;
2371 if (range->has_stepper_c)
2373 layout->stepper_c.width = stepper_width;
2374 layout->stepper_c.height = stepper_height;
2378 layout->stepper_c.width = 0;
2379 layout->stepper_c.height = 0;
2382 layout->stepper_c.x = layout->stepper_a.x;
2383 layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
2385 /* Now the trough is the remaining space between steppers B and C,
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);
2393 /* Slider fits into the trough, with stepper_spacing on either side,
2394 * and the size/position based on the adjustment or fixed, depending.
2396 layout->slider.x = layout->trough.x + trough_border;
2397 layout->slider.width = layout->trough.width - trough_border * 2;
2399 /* Compute slider position/length */
2401 gint y, bottom, top, height;
2403 top = layout->trough.y + stepper_spacing;
2404 bottom = layout->trough.y + layout->trough.height - stepper_spacing;
2406 /* slider height is the fraction (page_size /
2407 * total_adjustment_range) times the trough height in pixels
2410 if (range->adjustment->upper - range->adjustment->lower != 0)
2411 height = ((bottom - top) * (range->adjustment->page_size /
2412 (range->adjustment->upper - range->adjustment->lower)));
2414 height = range->min_slider_size;
2416 if (height < range->min_slider_size ||
2417 range->slider_size_fixed)
2418 height = range->min_slider_size;
2420 height = MIN (height, (layout->trough.height - stepper_spacing * 2));
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));
2428 y = CLAMP (y, top, bottom);
2430 if (should_invert (range))
2431 y = bottom - (y - top + height);
2433 layout->slider.y = y;
2434 layout->slider.height = height;
2436 /* These are publically exported */
2437 range->slider_start = layout->slider.y;
2438 range->slider_end = layout->slider.y + layout->slider.height;
2443 gint stepper_width, stepper_height;
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.
2449 stepper_height = range_rect.height - trough_border * 2;
2451 if (stepper_height < 1)
2452 stepper_height = range_rect.height; /* screw the trough border */
2454 if (n_steppers == 0)
2455 stepper_width = 0; /* avoid divide by n_steppers */
2457 stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
2461 layout->stepper_a.x = range_rect.x + trough_border;
2462 layout->stepper_a.y = range_rect.y + trough_border;
2464 if (range->has_stepper_a)
2466 layout->stepper_a.width = stepper_width;
2467 layout->stepper_a.height = stepper_height;
2471 layout->stepper_a.width = 0;
2472 layout->stepper_a.height = 0;
2477 layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
2478 layout->stepper_b.y = layout->stepper_a.y;
2480 if (range->has_stepper_b)
2482 layout->stepper_b.width = stepper_width;
2483 layout->stepper_b.height = stepper_height;
2487 layout->stepper_b.width = 0;
2488 layout->stepper_b.height = 0;
2493 if (range->has_stepper_d)
2495 layout->stepper_d.width = stepper_width;
2496 layout->stepper_d.height = stepper_height;
2500 layout->stepper_d.width = 0;
2501 layout->stepper_d.height = 0;
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;
2510 if (range->has_stepper_c)
2512 layout->stepper_c.width = stepper_width;
2513 layout->stepper_c.height = stepper_height;
2517 layout->stepper_c.width = 0;
2518 layout->stepper_c.height = 0;
2521 layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
2522 layout->stepper_c.y = layout->stepper_a.y;
2524 /* Now the trough is the remaining space between steppers B and C,
2527 layout->trough.x = layout->stepper_b.x + layout->stepper_b.width;
2528 layout->trough.y = range_rect.y;
2530 layout->trough.width = layout->stepper_c.x - (layout->stepper_b.x + layout->stepper_b.width);
2531 layout->trough.height = range_rect.height;
2533 /* Slider fits into the trough, with stepper_spacing on either side,
2534 * and the size/position based on the adjustment or fixed, depending.
2536 layout->slider.y = layout->trough.y + trough_border;
2537 layout->slider.height = layout->trough.height - trough_border * 2;
2539 /* Compute slider position/length */
2541 gint x, left, right, width;
2543 left = layout->trough.x + stepper_spacing;
2544 right = layout->trough.x + layout->trough.width - stepper_spacing;
2546 /* slider width is the fraction (page_size /
2547 * total_adjustment_range) times the trough width in pixels
2550 if (range->adjustment->upper - range->adjustment->lower != 0)
2551 width = ((right - left) * (range->adjustment->page_size /
2552 (range->adjustment->upper - range->adjustment->lower)));
2554 width = range->min_slider_size;
2556 if (width < range->min_slider_size ||
2557 range->slider_size_fixed)
2558 width = range->min_slider_size;
2560 width = MIN (width, (layout->trough.width - stepper_spacing * 2));
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));
2568 x = CLAMP (x, left, right);
2570 if (should_invert (range))
2571 x = right - (x - left + width);
2573 layout->slider.x = x;
2574 layout->slider.width = width;
2576 /* These are publically exported */
2577 range->slider_start = layout->slider.x;
2578 range->slider_end = layout->slider.x + layout->slider.width;
2582 gtk_range_update_mouse_location (range);
2585 static GdkRectangle*
2586 get_area (GtkRange *range,
2587 MouseLocation location)
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;
2600 return &range->layout->trough;
2602 return &range->layout->slider;
2608 g_warning (G_STRLOC": bug");
2613 gtk_range_real_change_value (GtkRange *range,
2614 GtkScrollType scroll,
2617 /* potentially adjust the bounds _before we clamp */
2618 g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
2620 value = CLAMP (value, range->adjustment->lower,
2621 (range->adjustment->upper - range->adjustment->page_size));
2623 if (range->round_digits >= 0)
2628 i = range->round_digits;
2633 value = floor ((value * power) + 0.5) / power;
2636 if (range->adjustment->value != value)
2638 range->need_recalc = TRUE;
2640 gtk_widget_queue_draw (GTK_WIDGET (range));
2642 switch (range->update_policy)
2644 case GTK_UPDATE_CONTINUOUS:
2645 gtk_adjustment_set_value (range->adjustment, value);
2648 /* Delayed means we update after a period of inactivity */
2649 case GTK_UPDATE_DELAYED:
2650 gtk_range_reset_update_timer (range);
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;
2665 gtk_range_update_value (GtkRange *range)
2667 gtk_range_remove_update_timer (range);
2669 if (range->update_pending)
2671 gtk_adjustment_value_changed (range->adjustment);
2673 range->update_pending = FALSE;
2677 struct _GtkRangeStepTimer
2684 second_timeout (gpointer data)
2688 GDK_THREADS_ENTER ();
2689 range = GTK_RANGE (data);
2690 gtk_range_scroll (range, range->timer->step);
2691 GDK_THREADS_LEAVE ();
2697 initial_timeout (gpointer data)
2700 GtkSettings *settings;
2703 GDK_THREADS_ENTER ();
2704 settings = gtk_widget_get_settings (GTK_WIDGET (data));
2705 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2707 range = GTK_RANGE (data);
2708 range->timer->timeout_id = g_timeout_add (timeout * SCROLL_DELAY_FACTOR,
2711 GDK_THREADS_LEAVE ();
2718 gtk_range_add_step_timer (GtkRange *range,
2721 GtkSettings *settings;
2724 g_return_if_fail (range->timer == NULL);
2725 g_return_if_fail (step != GTK_SCROLL_NONE);
2727 settings = gtk_widget_get_settings (GTK_WIDGET (range));
2728 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2730 range->timer = g_new (GtkRangeStepTimer, 1);
2732 range->timer->timeout_id = g_timeout_add (timeout,
2735 range->timer->step = step;
2737 gtk_range_scroll (range, range->timer->step);
2741 gtk_range_remove_step_timer (GtkRange *range)
2745 if (range->timer->timeout_id != 0)
2746 g_source_remove (range->timer->timeout_id);
2748 g_free (range->timer);
2750 range->timer = NULL;
2755 update_timeout (gpointer data)
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 ();
2770 gtk_range_reset_update_timer (GtkRange *range)
2772 gtk_range_remove_update_timer (range);
2774 range->update_timeout_id = g_timeout_add (UPDATE_DELAY,
2780 gtk_range_remove_update_timer (GtkRange *range)
2782 if (range->update_timeout_id != 0)
2784 g_source_remove (range->update_timeout_id);
2785 range->update_timeout_id = 0;
2789 #define __GTK_RANGE_C__
2790 #include "gtkaliasdef.c"