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 <gdk/gdkkeysyms.h>
35 #include "gtkmarshalers.h"
36 #include "gtkorientable.h"
39 #include "gtkscrollbar.h"
40 #include "gtkprivate.h"
47 * @Short_description: Base class for widgets which visualize an adjustment
50 * #GtkRange is the common base class for widgets which visualize an
51 * adjustment, e.g #GtkScale or #GtkScroolbar.
53 * Apart from signals for monitoring the parameters of the adjustment,
54 * #GtkRange provides properties and methods for influencing the sensitivity
55 * of the "steppers". It also provides properties and methods for setting a
56 * "fill level" on range widgets. See gtk_range_set_fill_level().
60 #define SCROLL_DELAY_FACTOR 5 /* Scroll repeat multiplier */
61 #define UPDATE_DELAY 300 /* Delay for queued update */
69 PROP_LOWER_STEPPER_SENSITIVITY,
70 PROP_UPPER_STEPPER_SENSITIVITY,
72 PROP_RESTRICT_TO_FILL_LEVEL,
92 MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
95 #define GTK_RANGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RANGE, GtkRangeLayout))
97 struct _GtkRangeLayout
99 /* These are in widget->window coordinates */
100 GdkRectangle stepper_a;
101 GdkRectangle stepper_b;
102 GdkRectangle stepper_c;
103 GdkRectangle stepper_d;
104 /* The trough rectangle is the area the thumb can slide in, not the
110 /* Layout-related state */
112 MouseLocation mouse_location;
113 /* last mouse coords we got, or -1 if mouse is outside the range */
117 /* "grabbed" mouse location, OUTSIDE for no grab */
118 MouseLocation grab_location;
119 guint grab_button : 8; /* 0 if none */
121 /* Stepper sensitivity */
122 guint lower_sensitive : 1;
123 guint upper_sensitive : 1;
126 guint show_fill_level : 1;
127 guint restrict_to_fill_level : 1;
129 GtkSensitivityType lower_sensitivity;
130 GtkSensitivityType upper_sensitivity;
135 GQuark slider_detail_quark;
136 GQuark stepper_detail_quark;
141 gboolean recalc_marks;
145 static void gtk_range_set_property (GObject *object,
149 static void gtk_range_get_property (GObject *object,
153 static void gtk_range_destroy (GtkObject *object);
154 static void gtk_range_size_request (GtkWidget *widget,
155 GtkRequisition *requisition);
156 static void gtk_range_size_allocate (GtkWidget *widget,
157 GtkAllocation *allocation);
158 static void gtk_range_realize (GtkWidget *widget);
159 static void gtk_range_unrealize (GtkWidget *widget);
160 static void gtk_range_map (GtkWidget *widget);
161 static void gtk_range_unmap (GtkWidget *widget);
162 static gboolean gtk_range_expose (GtkWidget *widget,
163 GdkEventExpose *event);
164 static gboolean gtk_range_button_press (GtkWidget *widget,
165 GdkEventButton *event);
166 static gboolean gtk_range_button_release (GtkWidget *widget,
167 GdkEventButton *event);
168 static gboolean gtk_range_motion_notify (GtkWidget *widget,
169 GdkEventMotion *event);
170 static gboolean gtk_range_enter_notify (GtkWidget *widget,
171 GdkEventCrossing *event);
172 static gboolean gtk_range_leave_notify (GtkWidget *widget,
173 GdkEventCrossing *event);
174 static gboolean gtk_range_grab_broken (GtkWidget *widget,
175 GdkEventGrabBroken *event);
176 static void gtk_range_grab_notify (GtkWidget *widget,
177 gboolean was_grabbed);
178 static void gtk_range_state_changed (GtkWidget *widget,
179 GtkStateType previous_state);
180 static gboolean gtk_range_scroll_event (GtkWidget *widget,
181 GdkEventScroll *event);
182 static void gtk_range_style_set (GtkWidget *widget,
183 GtkStyle *previous_style);
184 static void update_slider_position (GtkRange *range,
187 static void stop_scrolling (GtkRange *range);
191 static void gtk_range_move_slider (GtkRange *range,
192 GtkScrollType scroll);
195 static gboolean gtk_range_scroll (GtkRange *range,
196 GtkScrollType scroll);
197 static gboolean gtk_range_update_mouse_location (GtkRange *range);
198 static void gtk_range_calc_layout (GtkRange *range,
199 gdouble adjustment_value);
200 static void gtk_range_calc_marks (GtkRange *range);
201 static void gtk_range_get_props (GtkRange *range,
206 gint *stepper_spacing,
207 gboolean *trough_under_steppers,
208 gint *arrow_displacement_x,
209 gint *arrow_displacement_y);
210 static void gtk_range_calc_request (GtkRange *range,
215 gint stepper_spacing,
216 GdkRectangle *range_rect,
219 gboolean *has_steppers_ab,
220 gboolean *has_steppers_cd,
221 gint *slider_length_p);
222 static void gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
224 static void gtk_range_adjustment_changed (GtkAdjustment *adjustment,
226 static void gtk_range_add_step_timer (GtkRange *range,
228 static void gtk_range_remove_step_timer (GtkRange *range);
229 static void gtk_range_reset_update_timer (GtkRange *range);
230 static void gtk_range_remove_update_timer (GtkRange *range);
231 static GdkRectangle* get_area (GtkRange *range,
232 MouseLocation location);
233 static gboolean gtk_range_real_change_value (GtkRange *range,
234 GtkScrollType scroll,
236 static void gtk_range_update_value (GtkRange *range);
237 static gboolean gtk_range_key_press (GtkWidget *range,
241 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
242 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
245 static guint signals[LAST_SIGNAL];
249 gtk_range_class_init (GtkRangeClass *class)
251 GObjectClass *gobject_class;
252 GtkObjectClass *object_class;
253 GtkWidgetClass *widget_class;
255 gobject_class = G_OBJECT_CLASS (class);
256 object_class = (GtkObjectClass*) class;
257 widget_class = (GtkWidgetClass*) class;
259 gobject_class->set_property = gtk_range_set_property;
260 gobject_class->get_property = gtk_range_get_property;
262 object_class->destroy = gtk_range_destroy;
264 widget_class->size_request = gtk_range_size_request;
265 widget_class->size_allocate = gtk_range_size_allocate;
266 widget_class->realize = gtk_range_realize;
267 widget_class->unrealize = gtk_range_unrealize;
268 widget_class->map = gtk_range_map;
269 widget_class->unmap = gtk_range_unmap;
270 widget_class->expose_event = gtk_range_expose;
271 widget_class->button_press_event = gtk_range_button_press;
272 widget_class->button_release_event = gtk_range_button_release;
273 widget_class->motion_notify_event = gtk_range_motion_notify;
274 widget_class->scroll_event = gtk_range_scroll_event;
275 widget_class->enter_notify_event = gtk_range_enter_notify;
276 widget_class->leave_notify_event = gtk_range_leave_notify;
277 widget_class->grab_broken_event = gtk_range_grab_broken;
278 widget_class->grab_notify = gtk_range_grab_notify;
279 widget_class->state_changed = gtk_range_state_changed;
280 widget_class->style_set = gtk_range_style_set;
281 widget_class->key_press_event = gtk_range_key_press;
283 class->move_slider = gtk_range_move_slider;
284 class->change_value = gtk_range_real_change_value;
286 class->slider_detail = "slider";
287 class->stepper_detail = "stepper";
290 * GtkRange::value-changed:
291 * @range: the #GtkRange that received the signal
293 * Emitted when the range value changes.
295 signals[VALUE_CHANGED] =
296 g_signal_new (I_("value-changed"),
297 G_TYPE_FROM_CLASS (gobject_class),
299 G_STRUCT_OFFSET (GtkRangeClass, value_changed),
301 _gtk_marshal_VOID__VOID,
305 * GtkRange::adjust-bounds:
306 * @range: the #GtkRange that received the signal
307 * @value: the value before we clamp
309 * Emitted before clamping a value, to give the application a
310 * chance to adjust the bounds.
312 signals[ADJUST_BOUNDS] =
313 g_signal_new (I_("adjust-bounds"),
314 G_TYPE_FROM_CLASS (gobject_class),
316 G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
318 _gtk_marshal_VOID__DOUBLE,
323 * GtkRange::move-slider:
324 * @range: the #GtkRange that received the signal
325 * @step: how to move the slider
327 * Virtual function that moves the slider. Used for keybindings.
329 signals[MOVE_SLIDER] =
330 g_signal_new (I_("move-slider"),
331 G_TYPE_FROM_CLASS (gobject_class),
332 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
333 G_STRUCT_OFFSET (GtkRangeClass, move_slider),
335 _gtk_marshal_VOID__ENUM,
337 GTK_TYPE_SCROLL_TYPE);
340 * GtkRange::change-value:
341 * @range: the #GtkRange that received the signal
342 * @scroll: the type of scroll action that was performed
343 * @value: the new value resulting from the scroll action
344 * @returns: %TRUE to prevent other handlers from being invoked for the
345 * signal, %FALSE to propagate the signal further
347 * The ::change-value signal is emitted when a scroll action is
348 * performed on a range. It allows an application to determine the
349 * type of scroll event that occurred and the resultant new value.
350 * The application can handle the event itself and return %TRUE to
351 * prevent further processing. Or, by returning %FALSE, it can pass
352 * the event to other handlers until the default GTK+ handler is
355 * The value parameter is unrounded. An application that overrides
356 * the ::change-value signal is responsible for clamping the value to
357 * the desired number of decimal digits; the default GTK+ handler
358 * clamps the value based on @range->round_digits.
360 * It is not possible to use delayed update policies in an overridden
361 * ::change-value handler.
365 signals[CHANGE_VALUE] =
366 g_signal_new (I_("change-value"),
367 G_TYPE_FROM_CLASS (gobject_class),
369 G_STRUCT_OFFSET (GtkRangeClass, change_value),
370 _gtk_boolean_handled_accumulator, NULL,
371 _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
373 GTK_TYPE_SCROLL_TYPE,
376 g_object_class_override_property (gobject_class,
380 g_object_class_install_property (gobject_class,
382 g_param_spec_enum ("update-policy",
384 P_("How the range should be updated on the screen"),
385 GTK_TYPE_UPDATE_TYPE,
386 GTK_UPDATE_CONTINUOUS,
387 GTK_PARAM_READWRITE));
389 g_object_class_install_property (gobject_class,
391 g_param_spec_object ("adjustment",
393 P_("The GtkAdjustment that contains the current value of this range object"),
395 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
397 g_object_class_install_property (gobject_class,
399 g_param_spec_boolean ("inverted",
401 P_("Invert direction slider moves to increase range value"),
403 GTK_PARAM_READWRITE));
405 g_object_class_install_property (gobject_class,
406 PROP_LOWER_STEPPER_SENSITIVITY,
407 g_param_spec_enum ("lower-stepper-sensitivity",
408 P_("Lower stepper sensitivity"),
409 P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
410 GTK_TYPE_SENSITIVITY_TYPE,
411 GTK_SENSITIVITY_AUTO,
412 GTK_PARAM_READWRITE));
414 g_object_class_install_property (gobject_class,
415 PROP_UPPER_STEPPER_SENSITIVITY,
416 g_param_spec_enum ("upper-stepper-sensitivity",
417 P_("Upper stepper sensitivity"),
418 P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
419 GTK_TYPE_SENSITIVITY_TYPE,
420 GTK_SENSITIVITY_AUTO,
421 GTK_PARAM_READWRITE));
424 * GtkRange:show-fill-level:
426 * The show-fill-level property controls whether fill level indicator
427 * graphics are displayed on the trough. See
428 * gtk_range_set_show_fill_level().
432 g_object_class_install_property (gobject_class,
433 PROP_SHOW_FILL_LEVEL,
434 g_param_spec_boolean ("show-fill-level",
435 P_("Show Fill Level"),
436 P_("Whether to display a fill level indicator graphics on trough."),
438 GTK_PARAM_READWRITE));
441 * GtkRange:restrict-to-fill-level:
443 * The restrict-to-fill-level property controls whether slider
444 * movement is restricted to an upper boundary set by the
445 * fill level. See gtk_range_set_restrict_to_fill_level().
449 g_object_class_install_property (gobject_class,
450 PROP_RESTRICT_TO_FILL_LEVEL,
451 g_param_spec_boolean ("restrict-to-fill-level",
452 P_("Restrict to Fill Level"),
453 P_("Whether to restrict the upper boundary to the fill level."),
455 GTK_PARAM_READWRITE));
458 * GtkRange:fill-level:
460 * The fill level (e.g. prebuffering of a network stream).
461 * See gtk_range_set_fill_level().
465 g_object_class_install_property (gobject_class,
467 g_param_spec_double ("fill-level",
469 P_("The fill level."),
473 GTK_PARAM_READWRITE));
475 gtk_widget_class_install_style_property (widget_class,
476 g_param_spec_int ("slider-width",
478 P_("Width of scrollbar or scale thumb"),
482 GTK_PARAM_READABLE));
483 gtk_widget_class_install_style_property (widget_class,
484 g_param_spec_int ("trough-border",
486 P_("Spacing between thumb/steppers and outer trough bevel"),
490 GTK_PARAM_READABLE));
491 gtk_widget_class_install_style_property (widget_class,
492 g_param_spec_int ("stepper-size",
494 P_("Length of step buttons at ends"),
498 GTK_PARAM_READABLE));
500 * GtkRange:stepper-spacing:
502 * The spacing between the stepper buttons and thumb. Note that
503 * setting this value to anything > 0 will automatically set the
504 * trough-under-steppers style property to %TRUE as well. Also,
505 * stepper-spacing won't have any effect if there are no steppers.
507 gtk_widget_class_install_style_property (widget_class,
508 g_param_spec_int ("stepper-spacing",
509 P_("Stepper Spacing"),
510 P_("Spacing between step buttons and thumb"),
514 GTK_PARAM_READABLE));
515 gtk_widget_class_install_style_property (widget_class,
516 g_param_spec_int ("arrow-displacement-x",
517 P_("Arrow X Displacement"),
518 P_("How far in the x direction to move the arrow when the button is depressed"),
522 GTK_PARAM_READABLE));
523 gtk_widget_class_install_style_property (widget_class,
524 g_param_spec_int ("arrow-displacement-y",
525 P_("Arrow Y Displacement"),
526 P_("How far in the y direction to move the arrow when the button is depressed"),
530 GTK_PARAM_READABLE));
532 gtk_widget_class_install_style_property (widget_class,
533 g_param_spec_boolean ("activate-slider",
534 P_("Draw slider ACTIVE during drag"),
535 P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
537 GTK_PARAM_READABLE));
540 * GtkRange:trough-side-details:
542 * When %TRUE, the parts of the trough on the two sides of the
543 * slider are drawn with different details.
547 gtk_widget_class_install_style_property (widget_class,
548 g_param_spec_boolean ("trough-side-details",
549 P_("Trough Side Details"),
550 P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"),
552 GTK_PARAM_READABLE));
555 * GtkRange:trough-under-steppers:
557 * Whether to draw the trough across the full length of the range or
558 * to exclude the steppers and their spacing. Note that setting the
559 * #GtkRange:stepper-spacing style property to any value > 0 will
560 * automatically enable trough-under-steppers too.
564 gtk_widget_class_install_style_property (widget_class,
565 g_param_spec_boolean ("trough-under-steppers",
566 P_("Trough Under Steppers"),
567 P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
569 GTK_PARAM_READABLE));
572 * GtkRange:arrow-scaling:
574 * The arrow size proportion relative to the scroll button size.
578 gtk_widget_class_install_style_property (widget_class,
579 g_param_spec_float ("arrow-scaling",
581 P_("Arrow scaling with regard to scroll button size"),
583 GTK_PARAM_READABLE));
585 g_type_class_add_private (class, sizeof (GtkRangeLayout));
589 gtk_range_set_property (GObject *object,
594 GtkRange *range = GTK_RANGE (object);
598 case PROP_ORIENTATION:
599 range->orientation = g_value_get_enum (value);
601 range->layout->slider_detail_quark = 0;
602 range->layout->stepper_detail_quark = 0;
604 gtk_widget_queue_resize (GTK_WIDGET (range));
606 case PROP_UPDATE_POLICY:
607 gtk_range_set_update_policy (range, g_value_get_enum (value));
609 case PROP_ADJUSTMENT:
610 gtk_range_set_adjustment (range, g_value_get_object (value));
613 gtk_range_set_inverted (range, g_value_get_boolean (value));
615 case PROP_LOWER_STEPPER_SENSITIVITY:
616 gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
618 case PROP_UPPER_STEPPER_SENSITIVITY:
619 gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
621 case PROP_SHOW_FILL_LEVEL:
622 gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
624 case PROP_RESTRICT_TO_FILL_LEVEL:
625 gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
627 case PROP_FILL_LEVEL:
628 gtk_range_set_fill_level (range, g_value_get_double (value));
631 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
637 gtk_range_get_property (GObject *object,
642 GtkRange *range = GTK_RANGE (object);
646 case PROP_ORIENTATION:
647 g_value_set_enum (value, range->orientation);
649 case PROP_UPDATE_POLICY:
650 g_value_set_enum (value, range->update_policy);
652 case PROP_ADJUSTMENT:
653 g_value_set_object (value, range->adjustment);
656 g_value_set_boolean (value, range->inverted);
658 case PROP_LOWER_STEPPER_SENSITIVITY:
659 g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
661 case PROP_UPPER_STEPPER_SENSITIVITY:
662 g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
664 case PROP_SHOW_FILL_LEVEL:
665 g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
667 case PROP_RESTRICT_TO_FILL_LEVEL:
668 g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
670 case PROP_FILL_LEVEL:
671 g_value_set_double (value, gtk_range_get_fill_level (range));
674 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
680 gtk_range_init (GtkRange *range)
682 gtk_widget_set_has_window (GTK_WIDGET (range), FALSE);
684 range->orientation = GTK_ORIENTATION_HORIZONTAL;
685 range->adjustment = NULL;
686 range->update_policy = GTK_UPDATE_CONTINUOUS;
687 range->inverted = FALSE;
688 range->flippable = FALSE;
689 range->min_slider_size = 1;
690 range->has_stepper_a = FALSE;
691 range->has_stepper_b = FALSE;
692 range->has_stepper_c = FALSE;
693 range->has_stepper_d = FALSE;
694 range->need_recalc = TRUE;
695 range->round_digits = -1;
696 range->layout = GTK_RANGE_GET_PRIVATE (range);
697 range->layout->mouse_location = MOUSE_OUTSIDE;
698 range->layout->mouse_x = -1;
699 range->layout->mouse_y = -1;
700 range->layout->grab_location = MOUSE_OUTSIDE;
701 range->layout->grab_button = 0;
702 range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
703 range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
704 range->layout->lower_sensitive = TRUE;
705 range->layout->upper_sensitive = TRUE;
706 range->layout->show_fill_level = FALSE;
707 range->layout->restrict_to_fill_level = TRUE;
708 range->layout->fill_level = G_MAXDOUBLE;
713 * gtk_range_get_adjustment:
714 * @range: a #GtkRange
716 * Get the #GtkAdjustment which is the "model" object for #GtkRange.
717 * See gtk_range_set_adjustment() for details.
718 * The return value does not have a reference added, so should not
721 * Return value: a #GtkAdjustment
724 gtk_range_get_adjustment (GtkRange *range)
726 g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
728 if (!range->adjustment)
729 gtk_range_set_adjustment (range, NULL);
731 return range->adjustment;
735 * gtk_range_set_update_policy:
736 * @range: a #GtkRange
737 * @policy: update policy
739 * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
740 * anytime the range slider is moved, the range value will change and the
741 * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
742 * the value will be updated after a brief timeout where no slider motion
743 * occurs, so updates are spaced by a short time rather than
744 * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
745 * be updated when the user releases the button and ends the slider
749 gtk_range_set_update_policy (GtkRange *range,
750 GtkUpdateType policy)
752 g_return_if_fail (GTK_IS_RANGE (range));
754 if (range->update_policy != policy)
756 range->update_policy = policy;
757 g_object_notify (G_OBJECT (range), "update-policy");
762 * gtk_range_get_update_policy:
763 * @range: a #GtkRange
765 * Gets the update policy of @range. See gtk_range_set_update_policy().
767 * Return value: the current update policy
770 gtk_range_get_update_policy (GtkRange *range)
772 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
774 return range->update_policy;
778 * gtk_range_set_adjustment:
779 * @range: a #GtkRange
780 * @adjustment: a #GtkAdjustment
782 * Sets the adjustment to be used as the "model" object for this range
783 * widget. The adjustment indicates the current range value, the
784 * minimum and maximum range values, the step/page increments used
785 * for keybindings and scrolling, and the page size. The page size
786 * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
787 * indicates the size of the visible area of the widget being scrolled.
788 * The page size affects the size of the scrollbar slider.
791 gtk_range_set_adjustment (GtkRange *range,
792 GtkAdjustment *adjustment)
794 g_return_if_fail (GTK_IS_RANGE (range));
797 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
799 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
801 if (range->adjustment != adjustment)
803 if (range->adjustment)
805 g_signal_handlers_disconnect_by_func (range->adjustment,
806 gtk_range_adjustment_changed,
808 g_signal_handlers_disconnect_by_func (range->adjustment,
809 gtk_range_adjustment_value_changed,
811 g_object_unref (range->adjustment);
814 range->adjustment = adjustment;
815 g_object_ref_sink (adjustment);
817 g_signal_connect (adjustment, "changed",
818 G_CALLBACK (gtk_range_adjustment_changed),
820 g_signal_connect (adjustment, "value-changed",
821 G_CALLBACK (gtk_range_adjustment_value_changed),
824 gtk_range_adjustment_changed (adjustment, range);
825 g_object_notify (G_OBJECT (range), "adjustment");
830 * gtk_range_set_inverted:
831 * @range: a #GtkRange
832 * @setting: %TRUE to invert the range
834 * Ranges normally move from lower to higher values as the
835 * slider moves from top to bottom or left to right. Inverted
836 * ranges have higher values at the top or on the right rather than
837 * on the bottom or left.
840 gtk_range_set_inverted (GtkRange *range,
843 g_return_if_fail (GTK_IS_RANGE (range));
845 setting = setting != FALSE;
847 if (setting != range->inverted)
849 range->inverted = setting;
850 g_object_notify (G_OBJECT (range), "inverted");
851 gtk_widget_queue_resize (GTK_WIDGET (range));
856 * gtk_range_get_inverted:
857 * @range: a #GtkRange
859 * Gets the value set by gtk_range_set_inverted().
861 * Return value: %TRUE if the range is inverted
864 gtk_range_get_inverted (GtkRange *range)
866 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
868 return range->inverted;
872 * gtk_range_set_flippable:
873 * @range: a #GtkRange
874 * @flippable: %TRUE to make the range flippable
876 * If a range is flippable, it will switch its direction if it is
877 * horizontal and its direction is %GTK_TEXT_DIR_RTL.
879 * See gtk_widget_get_direction().
884 gtk_range_set_flippable (GtkRange *range,
887 g_return_if_fail (GTK_IS_RANGE (range));
889 flippable = flippable ? TRUE : FALSE;
891 if (flippable != range->flippable)
893 range->flippable = flippable;
895 gtk_widget_queue_draw (GTK_WIDGET (range));
900 * gtk_range_get_flippable:
901 * @range: a #GtkRange
903 * Gets the value set by gtk_range_set_flippable().
905 * Return value: %TRUE if the range is flippable
910 gtk_range_get_flippable (GtkRange *range)
912 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
914 return range->flippable;
918 * gtk_range_set_slider_size_fixed:
919 * @range: a #GtkRange
920 * @size_fixed: %TRUE to make the slider size constant
922 * Sets whether the range's slider has a fixed size, or a size that
923 * depends on it's adjustment's page size.
925 * This function is useful mainly for #GtkRange subclasses.
930 gtk_range_set_slider_size_fixed (GtkRange *range,
933 g_return_if_fail (GTK_IS_RANGE (range));
935 if (size_fixed != range->slider_size_fixed)
937 range->slider_size_fixed = size_fixed ? TRUE : FALSE;
939 range->need_recalc = TRUE;
940 gtk_range_calc_layout (range, range->adjustment->value);
941 gtk_widget_queue_draw (GTK_WIDGET (range));
946 * gtk_range_get_slider_size_fixed:
947 * @range: a #GtkRange
949 * This function is useful mainly for #GtkRange subclasses.
951 * See gtk_range_set_slider_size_fixed().
953 * Return value: whether the range's slider has a fixed size.
958 gtk_range_get_slider_size_fixed (GtkRange *range)
960 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
962 return range->slider_size_fixed;
966 * gtk_range_set_min_slider_size:
967 * @range: a #GtkRange
968 * @min_size: The slider's minimum size
970 * Sets the minimum size of the range's slider.
972 * This function is useful mainly for #GtkRange subclasses.
977 gtk_range_set_min_slider_size (GtkRange *range,
980 g_return_if_fail (GTK_IS_RANGE (range));
981 g_return_if_fail (min_size > 0);
983 if (min_size != range->min_slider_size)
985 range->min_slider_size = min_size;
987 range->need_recalc = TRUE;
988 gtk_range_calc_layout (range, range->adjustment->value);
989 gtk_widget_queue_draw (GTK_WIDGET (range));
994 * gtk_range_get_min_slider_size:
995 * @range: a #GtkRange
997 * This function is useful mainly for #GtkRange subclasses.
999 * See gtk_range_set_min_slider_size().
1001 * Return value: The minimum size of the range's slider.
1006 gtk_range_get_min_slider_size (GtkRange *range)
1008 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1010 return range->min_slider_size;
1014 * gtk_range_get_range_rect:
1015 * @range: a #GtkRange
1016 * @range_rect: return location for the range rectangle
1018 * This function returns the area that contains the range's trough
1019 * and its steppers, in widget->window coordinates.
1021 * This function is useful mainly for #GtkRange subclasses.
1026 gtk_range_get_range_rect (GtkRange *range,
1027 GdkRectangle *range_rect)
1029 g_return_if_fail (GTK_IS_RANGE (range));
1030 g_return_if_fail (range_rect != NULL);
1032 gtk_range_calc_layout (range, range->adjustment->value);
1034 *range_rect = range->range_rect;
1038 * gtk_range_get_slider_range:
1039 * @range: a #GtkRange
1040 * @slider_start: (allow-none): return location for the slider's start, or %NULL
1041 * @slider_end: (allow-none): return location for the slider's end, or %NULL
1043 * This function returns sliders range along the long dimension,
1044 * in widget->window coordinates.
1046 * This function is useful mainly for #GtkRange subclasses.
1051 gtk_range_get_slider_range (GtkRange *range,
1055 g_return_if_fail (GTK_IS_RANGE (range));
1057 gtk_range_calc_layout (range, range->adjustment->value);
1060 *slider_start = range->slider_start;
1063 *slider_end = range->slider_end;
1067 * gtk_range_set_lower_stepper_sensitivity:
1068 * @range: a #GtkRange
1069 * @sensitivity: the lower stepper's sensitivity policy.
1071 * Sets the sensitivity policy for the stepper that points to the
1072 * 'lower' end of the GtkRange's adjustment.
1077 gtk_range_set_lower_stepper_sensitivity (GtkRange *range,
1078 GtkSensitivityType sensitivity)
1080 g_return_if_fail (GTK_IS_RANGE (range));
1082 if (range->layout->lower_sensitivity != sensitivity)
1084 range->layout->lower_sensitivity = sensitivity;
1086 range->need_recalc = TRUE;
1087 gtk_range_calc_layout (range, range->adjustment->value);
1088 gtk_widget_queue_draw (GTK_WIDGET (range));
1090 g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
1095 * gtk_range_get_lower_stepper_sensitivity:
1096 * @range: a #GtkRange
1098 * Gets the sensitivity policy for the stepper that points to the
1099 * 'lower' end of the GtkRange's adjustment.
1101 * Return value: The lower stepper's sensitivity policy.
1106 gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
1108 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1110 return range->layout->lower_sensitivity;
1114 * gtk_range_set_upper_stepper_sensitivity:
1115 * @range: a #GtkRange
1116 * @sensitivity: the upper stepper's sensitivity policy.
1118 * Sets the sensitivity policy for the stepper that points to the
1119 * 'upper' end of the GtkRange's adjustment.
1124 gtk_range_set_upper_stepper_sensitivity (GtkRange *range,
1125 GtkSensitivityType sensitivity)
1127 g_return_if_fail (GTK_IS_RANGE (range));
1129 if (range->layout->upper_sensitivity != sensitivity)
1131 range->layout->upper_sensitivity = sensitivity;
1133 range->need_recalc = TRUE;
1134 gtk_range_calc_layout (range, range->adjustment->value);
1135 gtk_widget_queue_draw (GTK_WIDGET (range));
1137 g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
1142 * gtk_range_get_upper_stepper_sensitivity:
1143 * @range: a #GtkRange
1145 * Gets the sensitivity policy for the stepper that points to the
1146 * 'upper' end of the GtkRange's adjustment.
1148 * Return value: The upper stepper's sensitivity policy.
1153 gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
1155 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1157 return range->layout->upper_sensitivity;
1161 * gtk_range_set_increments:
1162 * @range: a #GtkRange
1166 * Sets the step and page sizes for the range.
1167 * The step size is used when the user clicks the #GtkScrollbar
1168 * arrows or moves #GtkScale via arrow keys. The page size
1169 * is used for example when moving via Page Up or Page Down keys.
1172 gtk_range_set_increments (GtkRange *range,
1176 g_return_if_fail (GTK_IS_RANGE (range));
1178 range->adjustment->step_increment = step;
1179 range->adjustment->page_increment = page;
1181 gtk_adjustment_changed (range->adjustment);
1185 * gtk_range_set_range:
1186 * @range: a #GtkRange
1187 * @min: minimum range value
1188 * @max: maximum range value
1190 * Sets the allowable values in the #GtkRange, and clamps the range
1191 * value to be between @min and @max. (If the range has a non-zero
1192 * page size, it is clamped between @min and @max - page-size.)
1195 gtk_range_set_range (GtkRange *range,
1201 g_return_if_fail (GTK_IS_RANGE (range));
1202 g_return_if_fail (min < max);
1204 range->adjustment->lower = min;
1205 range->adjustment->upper = max;
1207 value = range->adjustment->value;
1209 if (range->layout->restrict_to_fill_level)
1210 value = MIN (value, MAX (range->adjustment->lower,
1211 range->layout->fill_level));
1213 gtk_adjustment_set_value (range->adjustment, value);
1214 gtk_adjustment_changed (range->adjustment);
1218 * gtk_range_set_value:
1219 * @range: a #GtkRange
1220 * @value: new value of the range
1222 * Sets the current value of the range; if the value is outside the
1223 * minimum or maximum range values, it will be clamped to fit inside
1224 * them. The range emits the #GtkRange::value-changed signal if the
1228 gtk_range_set_value (GtkRange *range,
1231 g_return_if_fail (GTK_IS_RANGE (range));
1233 if (range->layout->restrict_to_fill_level)
1234 value = MIN (value, MAX (range->adjustment->lower,
1235 range->layout->fill_level));
1237 gtk_adjustment_set_value (range->adjustment, value);
1241 * gtk_range_get_value:
1242 * @range: a #GtkRange
1244 * Gets the current value of the range.
1246 * Return value: current value of the range.
1249 gtk_range_get_value (GtkRange *range)
1251 g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1253 return range->adjustment->value;
1257 * gtk_range_set_show_fill_level:
1258 * @range: A #GtkRange
1259 * @show_fill_level: Whether a fill level indicator graphics is shown.
1261 * Sets whether a graphical fill level is show on the trough. See
1262 * gtk_range_set_fill_level() for a general description of the fill
1268 gtk_range_set_show_fill_level (GtkRange *range,
1269 gboolean show_fill_level)
1271 g_return_if_fail (GTK_IS_RANGE (range));
1273 show_fill_level = show_fill_level ? TRUE : FALSE;
1275 if (show_fill_level != range->layout->show_fill_level)
1277 range->layout->show_fill_level = show_fill_level;
1278 g_object_notify (G_OBJECT (range), "show-fill-level");
1279 gtk_widget_queue_draw (GTK_WIDGET (range));
1284 * gtk_range_get_show_fill_level:
1285 * @range: A #GtkRange
1287 * Gets whether the range displays the fill level graphically.
1289 * Return value: %TRUE if @range shows the fill level.
1294 gtk_range_get_show_fill_level (GtkRange *range)
1296 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1298 return range->layout->show_fill_level;
1302 * gtk_range_set_restrict_to_fill_level:
1303 * @range: A #GtkRange
1304 * @restrict_to_fill_level: Whether the fill level restricts slider movement.
1306 * Sets whether the slider is restricted to the fill level. See
1307 * gtk_range_set_fill_level() for a general description of the fill
1313 gtk_range_set_restrict_to_fill_level (GtkRange *range,
1314 gboolean restrict_to_fill_level)
1316 g_return_if_fail (GTK_IS_RANGE (range));
1318 restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
1320 if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
1322 range->layout->restrict_to_fill_level = restrict_to_fill_level;
1323 g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
1325 gtk_range_set_value (range, gtk_range_get_value (range));
1330 * gtk_range_get_restrict_to_fill_level:
1331 * @range: A #GtkRange
1333 * Gets whether the range is restricted to the fill level.
1335 * Return value: %TRUE if @range is restricted to the fill level.
1340 gtk_range_get_restrict_to_fill_level (GtkRange *range)
1342 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1344 return range->layout->restrict_to_fill_level;
1348 * gtk_range_set_fill_level:
1349 * @range: a #GtkRange
1350 * @fill_level: the new position of the fill level indicator
1352 * Set the new position of the fill level indicator.
1354 * The "fill level" is probably best described by its most prominent
1355 * use case, which is an indicator for the amount of pre-buffering in
1356 * a streaming media player. In that use case, the value of the range
1357 * would indicate the current play position, and the fill level would
1358 * be the position up to which the file/stream has been downloaded.
1360 * This amount of prebuffering can be displayed on the range's trough
1361 * and is themeable separately from the trough. To enable fill level
1362 * display, use gtk_range_set_show_fill_level(). The range defaults
1363 * to not showing the fill level.
1365 * Additionally, it's possible to restrict the range's slider position
1366 * to values which are smaller than the fill level. This is controller
1367 * by gtk_range_set_restrict_to_fill_level() and is by default
1373 gtk_range_set_fill_level (GtkRange *range,
1376 g_return_if_fail (GTK_IS_RANGE (range));
1378 if (fill_level != range->layout->fill_level)
1380 range->layout->fill_level = fill_level;
1381 g_object_notify (G_OBJECT (range), "fill-level");
1383 if (range->layout->show_fill_level)
1384 gtk_widget_queue_draw (GTK_WIDGET (range));
1386 if (range->layout->restrict_to_fill_level)
1387 gtk_range_set_value (range, gtk_range_get_value (range));
1392 * gtk_range_get_fill_level:
1393 * @range : A #GtkRange
1395 * Gets the current position of the fill level indicator.
1397 * Return value: The current fill level
1402 gtk_range_get_fill_level (GtkRange *range)
1404 g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1406 return range->layout->fill_level;
1410 should_invert (GtkRange *range)
1412 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1414 (range->inverted && !range->flippable) ||
1415 (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
1416 (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1418 return range->inverted;
1422 gtk_range_destroy (GtkObject *object)
1424 GtkRange *range = GTK_RANGE (object);
1426 gtk_range_remove_step_timer (range);
1427 gtk_range_remove_update_timer (range);
1429 if (range->layout->repaint_id)
1430 g_source_remove (range->layout->repaint_id);
1431 range->layout->repaint_id = 0;
1433 if (range->adjustment)
1435 g_signal_handlers_disconnect_by_func (range->adjustment,
1436 gtk_range_adjustment_changed,
1438 g_signal_handlers_disconnect_by_func (range->adjustment,
1439 gtk_range_adjustment_value_changed,
1441 g_object_unref (range->adjustment);
1442 range->adjustment = NULL;
1445 if (range->layout->n_marks)
1447 g_free (range->layout->marks);
1448 range->layout->marks = NULL;
1449 g_free (range->layout->mark_pos);
1450 range->layout->mark_pos = NULL;
1451 range->layout->n_marks = 0;
1454 GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
1458 gtk_range_size_request (GtkWidget *widget,
1459 GtkRequisition *requisition)
1462 gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1463 GdkRectangle range_rect;
1466 range = GTK_RANGE (widget);
1468 gtk_range_get_props (range,
1469 &slider_width, &stepper_size,
1470 &focus_width, &trough_border,
1471 &stepper_spacing, NULL,
1474 gtk_range_calc_request (range,
1475 slider_width, stepper_size,
1476 focus_width, trough_border, stepper_spacing,
1477 &range_rect, &border, NULL, NULL, NULL, NULL);
1479 requisition->width = range_rect.width + border.left + border.right;
1480 requisition->height = range_rect.height + border.top + border.bottom;
1484 gtk_range_size_allocate (GtkWidget *widget,
1485 GtkAllocation *allocation)
1489 range = GTK_RANGE (widget);
1491 widget->allocation = *allocation;
1493 range->layout->recalc_marks = TRUE;
1495 range->need_recalc = TRUE;
1496 gtk_range_calc_layout (range, range->adjustment->value);
1498 if (gtk_widget_get_realized (widget))
1499 gdk_window_move_resize (range->event_window,
1500 widget->allocation.x,
1501 widget->allocation.y,
1502 widget->allocation.width,
1503 widget->allocation.height);
1507 gtk_range_realize (GtkWidget *widget)
1510 GdkWindowAttr attributes;
1511 gint attributes_mask;
1513 range = GTK_RANGE (widget);
1515 gtk_range_calc_layout (range, range->adjustment->value);
1517 gtk_widget_set_realized (widget, TRUE);
1519 widget->window = gtk_widget_get_parent_window (widget);
1520 g_object_ref (widget->window);
1522 attributes.window_type = GDK_WINDOW_CHILD;
1523 attributes.x = widget->allocation.x;
1524 attributes.y = widget->allocation.y;
1525 attributes.width = widget->allocation.width;
1526 attributes.height = widget->allocation.height;
1527 attributes.wclass = GDK_INPUT_ONLY;
1528 attributes.event_mask = gtk_widget_get_events (widget);
1529 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1530 GDK_BUTTON_RELEASE_MASK |
1531 GDK_ENTER_NOTIFY_MASK |
1532 GDK_LEAVE_NOTIFY_MASK |
1533 GDK_POINTER_MOTION_MASK |
1534 GDK_POINTER_MOTION_HINT_MASK);
1536 attributes_mask = GDK_WA_X | GDK_WA_Y;
1538 range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1539 &attributes, attributes_mask);
1540 gdk_window_set_user_data (range->event_window, range);
1542 widget->style = gtk_style_attach (widget->style, widget->window);
1546 gtk_range_unrealize (GtkWidget *widget)
1548 GtkRange *range = GTK_RANGE (widget);
1550 gtk_range_remove_step_timer (range);
1551 gtk_range_remove_update_timer (range);
1553 gdk_window_set_user_data (range->event_window, NULL);
1554 gdk_window_destroy (range->event_window);
1555 range->event_window = NULL;
1557 GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1561 gtk_range_map (GtkWidget *widget)
1563 GtkRange *range = GTK_RANGE (widget);
1565 gdk_window_show (range->event_window);
1567 GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1571 gtk_range_unmap (GtkWidget *widget)
1573 GtkRange *range = GTK_RANGE (widget);
1575 stop_scrolling (range);
1577 gdk_window_hide (range->event_window);
1579 GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1582 static const gchar *
1583 gtk_range_get_slider_detail (GtkRange *range)
1585 const gchar *slider_detail;
1587 if (range->layout->slider_detail_quark)
1588 return g_quark_to_string (range->layout->slider_detail_quark);
1590 slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
1592 if (slider_detail && slider_detail[0] == 'X')
1594 gchar *detail = g_strdup (slider_detail);
1596 detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1598 range->layout->slider_detail_quark = g_quark_from_string (detail);
1602 return g_quark_to_string (range->layout->slider_detail_quark);
1605 return slider_detail;
1608 static const gchar *
1609 gtk_range_get_stepper_detail (GtkRange *range)
1611 const gchar *stepper_detail;
1613 if (range->layout->stepper_detail_quark)
1614 return g_quark_to_string (range->layout->stepper_detail_quark);
1616 stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
1618 if (stepper_detail && stepper_detail[0] == 'X')
1620 gchar *detail = g_strdup (stepper_detail);
1622 detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1624 range->layout->stepper_detail_quark = g_quark_from_string (detail);
1628 return g_quark_to_string (range->layout->stepper_detail_quark);
1631 return stepper_detail;
1635 draw_stepper (GtkRange *range,
1637 GtkArrowType arrow_type,
1639 gboolean prelighted,
1642 GtkStateType state_type;
1643 GtkShadowType shadow_type;
1644 GdkRectangle intersection;
1645 GtkWidget *widget = GTK_WIDGET (range);
1646 gfloat arrow_scaling;
1653 gboolean arrow_sensitive = TRUE;
1655 /* More to get the right clip region than for efficiency */
1656 if (!gdk_rectangle_intersect (area, rect, &intersection))
1659 intersection.x += widget->allocation.x;
1660 intersection.y += widget->allocation.y;
1662 if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1663 arrow_type == GTK_ARROW_RIGHT)) ||
1664 (range->inverted && (arrow_type == GTK_ARROW_UP ||
1665 arrow_type == GTK_ARROW_LEFT)))
1667 arrow_sensitive = range->layout->upper_sensitive;
1671 arrow_sensitive = range->layout->lower_sensitive;
1674 if (!gtk_widget_is_sensitive (GTK_WIDGET (range)) || !arrow_sensitive)
1675 state_type = GTK_STATE_INSENSITIVE;
1677 state_type = GTK_STATE_ACTIVE;
1678 else if (prelighted)
1679 state_type = GTK_STATE_PRELIGHT;
1681 state_type = GTK_STATE_NORMAL;
1683 if (clicked && arrow_sensitive)
1684 shadow_type = GTK_SHADOW_IN;
1686 shadow_type = GTK_SHADOW_OUT;
1688 gtk_paint_box (widget->style,
1690 state_type, shadow_type,
1691 &intersection, widget,
1692 gtk_range_get_stepper_detail (range),
1693 widget->allocation.x + rect->x,
1694 widget->allocation.y + rect->y,
1698 gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1700 arrow_width = rect->width * arrow_scaling;
1701 arrow_height = rect->height * arrow_scaling;
1702 arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1703 arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1705 if (clicked && arrow_sensitive)
1707 gint arrow_displacement_x;
1708 gint arrow_displacement_y;
1710 gtk_range_get_props (GTK_RANGE (widget),
1711 NULL, NULL, NULL, NULL, NULL, NULL,
1712 &arrow_displacement_x, &arrow_displacement_y);
1714 arrow_x += arrow_displacement_x;
1715 arrow_y += arrow_displacement_y;
1718 gtk_paint_arrow (widget->style,
1720 state_type, shadow_type,
1721 &intersection, widget,
1722 gtk_range_get_stepper_detail (range),
1725 arrow_x, arrow_y, arrow_width, arrow_height);
1729 gtk_range_expose (GtkWidget *widget,
1730 GdkEventExpose *event)
1732 GtkRange *range = GTK_RANGE (widget);
1735 GtkShadowType shadow_type;
1736 GdkRectangle expose_area; /* Relative to widget->allocation */
1738 gint focus_line_width = 0;
1739 gint focus_padding = 0;
1740 gboolean touchscreen;
1742 g_object_get (gtk_widget_get_settings (widget),
1743 "gtk-touchscreen-mode", &touchscreen,
1745 if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
1746 gtk_widget_style_get (GTK_WIDGET (range),
1747 "focus-line-width", &focus_line_width,
1748 "focus-padding", &focus_padding,
1751 /* we're now exposing, so there's no need to force early repaints */
1752 if (range->layout->repaint_id)
1753 g_source_remove (range->layout->repaint_id);
1754 range->layout->repaint_id = 0;
1756 expose_area = event->area;
1757 expose_area.x -= widget->allocation.x;
1758 expose_area.y -= widget->allocation.y;
1760 gtk_range_calc_marks (range);
1761 gtk_range_calc_layout (range, range->adjustment->value);
1763 sensitive = gtk_widget_is_sensitive (widget);
1765 /* Just to be confusing, we draw the trough for the whole
1766 * range rectangle, not the trough rectangle (the trough
1767 * rectangle is just for hit detection)
1769 /* The gdk_rectangle_intersect is more to get the right
1770 * clip region (limited to range_rect) than for efficiency
1772 if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1775 gint x = (widget->allocation.x + range->range_rect.x +
1776 focus_line_width + focus_padding);
1777 gint y = (widget->allocation.y + range->range_rect.y +
1778 focus_line_width + focus_padding);
1779 gint width = (range->range_rect.width -
1780 2 * (focus_line_width + focus_padding));
1781 gint height = (range->range_rect.height -
1782 2 * (focus_line_width + focus_padding));
1783 gboolean trough_side_details;
1784 gboolean trough_under_steppers;
1786 gint stepper_spacing;
1788 area.x += widget->allocation.x;
1789 area.y += widget->allocation.y;
1791 gtk_widget_style_get (GTK_WIDGET (range),
1792 "trough-side-details", &trough_side_details,
1793 "trough-under-steppers", &trough_under_steppers,
1794 "stepper-size", &stepper_size,
1795 "stepper-spacing", &stepper_spacing,
1798 if (stepper_spacing > 0)
1799 trough_under_steppers = FALSE;
1801 if (! trough_under_steppers)
1806 if (range->has_stepper_a)
1807 offset += stepper_size;
1809 if (range->has_stepper_b)
1810 offset += stepper_size;
1814 if (range->has_stepper_c)
1815 shorter += stepper_size;
1817 if (range->has_stepper_d)
1818 shorter += stepper_size;
1820 if (range->has_stepper_a || range->has_stepper_b)
1822 offset += stepper_spacing;
1823 shorter += stepper_spacing;
1826 if (range->has_stepper_c || range->has_stepper_d)
1828 shorter += stepper_spacing;
1831 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1843 if (! trough_side_details)
1845 gtk_paint_box (widget->style,
1847 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1849 &area, GTK_WIDGET(range), "trough",
1855 gint trough_change_pos_x = width;
1856 gint trough_change_pos_y = height;
1858 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1859 trough_change_pos_x = (range->layout->slider.x +
1860 range->layout->slider.width / 2 -
1861 (x - widget->allocation.x));
1863 trough_change_pos_y = (range->layout->slider.y +
1864 range->layout->slider.height / 2 -
1865 (y - widget->allocation.y));
1867 gtk_paint_box (widget->style,
1869 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1871 &area, GTK_WIDGET (range),
1872 should_invert (range) ? "trough-upper" : "trough-lower",
1874 trough_change_pos_x, trough_change_pos_y);
1876 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1877 trough_change_pos_y = 0;
1879 trough_change_pos_x = 0;
1881 gtk_paint_box (widget->style,
1883 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1885 &area, GTK_WIDGET (range),
1886 should_invert (range) ? "trough-lower" : "trough-upper",
1887 x + trough_change_pos_x, y + trough_change_pos_y,
1888 width - trough_change_pos_x,
1889 height - trough_change_pos_y);
1892 if (range->layout->show_fill_level &&
1893 range->adjustment->upper - range->adjustment->page_size -
1894 range->adjustment->lower != 0)
1896 gdouble fill_level = range->layout->fill_level;
1899 gint fill_width = width;
1900 gint fill_height = height;
1903 fill_level = CLAMP (fill_level, range->adjustment->lower,
1904 range->adjustment->upper -
1905 range->adjustment->page_size);
1907 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1909 fill_x = widget->allocation.x + range->layout->trough.x;
1910 fill_width = (range->layout->slider.width +
1911 (fill_level - range->adjustment->lower) /
1912 (range->adjustment->upper -
1913 range->adjustment->lower -
1914 range->adjustment->page_size) *
1915 (range->layout->trough.width -
1916 range->layout->slider.width));
1918 if (should_invert (range))
1919 fill_x += range->layout->trough.width - fill_width;
1923 fill_y = widget->allocation.y + range->layout->trough.y;
1924 fill_height = (range->layout->slider.height +
1925 (fill_level - range->adjustment->lower) /
1926 (range->adjustment->upper -
1927 range->adjustment->lower -
1928 range->adjustment->page_size) *
1929 (range->layout->trough.height -
1930 range->layout->slider.height));
1932 if (should_invert (range))
1933 fill_y += range->layout->trough.height - fill_height;
1936 if (fill_level < range->adjustment->upper - range->adjustment->page_size)
1937 fill_detail = "trough-fill-level-full";
1939 fill_detail = "trough-fill-level";
1941 gtk_paint_box (widget->style,
1943 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1945 &area, GTK_WIDGET (range), fill_detail,
1947 fill_width, fill_height);
1950 if (sensitive && gtk_widget_has_focus (widget))
1951 gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
1952 &area, widget, "trough",
1953 widget->allocation.x + range->range_rect.x,
1954 widget->allocation.y + range->range_rect.y,
1955 range->range_rect.width,
1956 range->range_rect.height);
1959 shadow_type = GTK_SHADOW_OUT;
1962 state = GTK_STATE_INSENSITIVE;
1963 else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1964 state = GTK_STATE_PRELIGHT;
1966 state = GTK_STATE_NORMAL;
1968 if (range->layout->grab_location == MOUSE_SLIDER)
1970 gboolean activate_slider;
1972 gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
1974 if (activate_slider)
1976 state = GTK_STATE_ACTIVE;
1977 shadow_type = GTK_SHADOW_IN;
1981 if (gdk_rectangle_intersect (&expose_area,
1982 &range->layout->slider,
1985 area.x += widget->allocation.x;
1986 area.y += widget->allocation.y;
1988 gtk_paint_slider (widget->style,
1994 gtk_range_get_slider_detail (range),
1995 widget->allocation.x + range->layout->slider.x,
1996 widget->allocation.y + range->layout->slider.y,
1997 range->layout->slider.width,
1998 range->layout->slider.height,
1999 range->orientation);
2002 if (range->has_stepper_a)
2003 draw_stepper (range, &range->layout->stepper_a,
2004 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2005 range->layout->grab_location == MOUSE_STEPPER_A,
2006 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
2009 if (range->has_stepper_b)
2010 draw_stepper (range, &range->layout->stepper_b,
2011 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2012 range->layout->grab_location == MOUSE_STEPPER_B,
2013 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
2016 if (range->has_stepper_c)
2017 draw_stepper (range, &range->layout->stepper_c,
2018 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2019 range->layout->grab_location == MOUSE_STEPPER_C,
2020 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
2023 if (range->has_stepper_d)
2024 draw_stepper (range, &range->layout->stepper_d,
2025 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2026 range->layout->grab_location == MOUSE_STEPPER_D,
2027 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
2034 range_grab_add (GtkRange *range,
2035 MouseLocation location,
2038 /* we don't actually gtk_grab, since a button is down */
2040 gtk_grab_add (GTK_WIDGET (range));
2042 range->layout->grab_location = location;
2043 range->layout->grab_button = button;
2045 if (gtk_range_update_mouse_location (range))
2046 gtk_widget_queue_draw (GTK_WIDGET (range));
2050 range_grab_remove (GtkRange *range)
2052 MouseLocation location;
2054 gtk_grab_remove (GTK_WIDGET (range));
2056 location = range->layout->grab_location;
2057 range->layout->grab_location = MOUSE_OUTSIDE;
2058 range->layout->grab_button = 0;
2060 if (gtk_range_update_mouse_location (range) ||
2061 location != MOUSE_OUTSIDE)
2062 gtk_widget_queue_draw (GTK_WIDGET (range));
2065 static GtkScrollType
2066 range_get_scroll_for_grab (GtkRange *range)
2070 invert = should_invert (range);
2071 switch (range->layout->grab_location)
2073 /* Backward stepper */
2074 case MOUSE_STEPPER_A:
2075 case MOUSE_STEPPER_C:
2076 switch (range->layout->grab_button)
2079 return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
2082 return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
2085 return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
2090 /* Forward stepper */
2091 case MOUSE_STEPPER_B:
2092 case MOUSE_STEPPER_D:
2093 switch (range->layout->grab_button)
2096 return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
2099 return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
2102 return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
2110 if (range->trough_click_forward)
2111 return GTK_SCROLL_PAGE_FORWARD;
2113 return GTK_SCROLL_PAGE_BACKWARD;
2123 return GTK_SCROLL_NONE;
2127 coord_to_value (GtkRange *range,
2136 gint trough_under_steppers;
2138 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2140 trough_length = range->layout->trough.height;
2141 trough_start = range->layout->trough.y;
2142 slider_length = range->layout->slider.height;
2146 trough_length = range->layout->trough.width;
2147 trough_start = range->layout->trough.x;
2148 slider_length = range->layout->slider.width;
2151 gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
2152 &trough_under_steppers, NULL, NULL);
2154 if (! trough_under_steppers)
2156 trough_start += trough_border;
2157 trough_length -= 2 * trough_border;
2160 if (trough_length == slider_length)
2163 frac = (MAX (0, coord - trough_start) /
2164 (gdouble) (trough_length - slider_length));
2166 if (should_invert (range))
2169 value = range->adjustment->lower + frac * (range->adjustment->upper -
2170 range->adjustment->lower -
2171 range->adjustment->page_size);
2177 gtk_range_key_press (GtkWidget *widget,
2180 GtkRange *range = GTK_RANGE (widget);
2182 if (event->keyval == GDK_Escape &&
2183 range->layout->grab_location != MOUSE_OUTSIDE)
2185 stop_scrolling (range);
2187 update_slider_position (range,
2188 range->slide_initial_coordinate,
2189 range->slide_initial_coordinate);
2194 return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
2198 gtk_range_button_press (GtkWidget *widget,
2199 GdkEventButton *event)
2201 GtkRange *range = GTK_RANGE (widget);
2203 if (!gtk_widget_has_focus (widget))
2204 gtk_widget_grab_focus (widget);
2206 /* ignore presses when we're already doing something else. */
2207 if (range->layout->grab_location != MOUSE_OUTSIDE)
2210 range->layout->mouse_x = event->x;
2211 range->layout->mouse_y = event->y;
2212 if (gtk_range_update_mouse_location (range))
2213 gtk_widget_queue_draw (widget);
2215 if (range->layout->mouse_location == MOUSE_TROUGH &&
2218 /* button 1 steps by page increment, as with button 2 on a stepper
2220 GtkScrollType scroll;
2221 gdouble click_value;
2223 click_value = coord_to_value (range,
2224 range->orientation == GTK_ORIENTATION_VERTICAL ?
2225 event->y : event->x);
2227 range->trough_click_forward = click_value > range->adjustment->value;
2228 range_grab_add (range, MOUSE_TROUGH, event->button);
2230 scroll = range_get_scroll_for_grab (range);
2232 gtk_range_add_step_timer (range, scroll);
2236 else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
2237 range->layout->mouse_location == MOUSE_STEPPER_B ||
2238 range->layout->mouse_location == MOUSE_STEPPER_C ||
2239 range->layout->mouse_location == MOUSE_STEPPER_D) &&
2240 (event->button == 1 || event->button == 2 || event->button == 3))
2242 GdkRectangle *stepper_area;
2243 GtkScrollType scroll;
2245 range_grab_add (range, range->layout->mouse_location, event->button);
2247 stepper_area = get_area (range, range->layout->mouse_location);
2248 gtk_widget_queue_draw_area (widget,
2249 widget->allocation.x + stepper_area->x,
2250 widget->allocation.y + stepper_area->y,
2251 stepper_area->width,
2252 stepper_area->height);
2254 scroll = range_get_scroll_for_grab (range);
2255 if (scroll != GTK_SCROLL_NONE)
2256 gtk_range_add_step_timer (range, scroll);
2260 else if ((range->layout->mouse_location == MOUSE_TROUGH &&
2261 event->button == 2) ||
2262 range->layout->mouse_location == MOUSE_SLIDER)
2264 gboolean need_value_update = FALSE;
2265 gboolean activate_slider;
2267 /* Any button can be used to drag the slider, but you can start
2268 * dragging the slider with a trough click using button 2;
2269 * On button 2 press, we warp the slider to mouse position,
2270 * then begin the slider drag.
2272 if (event->button == 2)
2274 gdouble slider_low_value, slider_high_value, new_value;
2277 coord_to_value (range,
2278 range->orientation == GTK_ORIENTATION_VERTICAL ?
2279 event->y : event->x);
2281 coord_to_value (range,
2282 range->orientation == GTK_ORIENTATION_VERTICAL ?
2283 event->y - range->layout->slider.height :
2284 event->x - range->layout->slider.width);
2286 /* compute new value for warped slider */
2287 new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
2289 /* recalc slider, so we can set slide_initial_slider_position
2292 range->need_recalc = TRUE;
2293 gtk_range_calc_layout (range, new_value);
2295 /* defer adjustment updates to update_slider_position() in order
2296 * to keep pixel quantisation
2298 need_value_update = TRUE;
2301 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2303 range->slide_initial_slider_position = range->layout->slider.y;
2304 range->slide_initial_coordinate = event->y;
2308 range->slide_initial_slider_position = range->layout->slider.x;
2309 range->slide_initial_coordinate = event->x;
2312 range_grab_add (range, MOUSE_SLIDER, event->button);
2314 gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2316 /* force a redraw, if the active slider is drawn differently to the
2319 if (activate_slider)
2320 gtk_widget_queue_draw (widget);
2322 if (need_value_update)
2323 update_slider_position (range, event->x, event->y);
2331 /* During a slide, move the slider as required given new mouse position */
2333 update_slider_position (GtkRange *range,
2346 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2347 delta = mouse_y - range->slide_initial_coordinate;
2349 delta = mouse_x - range->slide_initial_coordinate;
2351 c = range->slide_initial_slider_position + delta;
2353 new_value = coord_to_value (range, c);
2354 next_value = coord_to_value (range, c + 1);
2355 mark_delta = fabs (next_value - new_value);
2357 for (i = 0; i < range->layout->n_marks; i++)
2359 mark_value = range->layout->marks[i];
2361 if (fabs (range->adjustment->value - mark_value) < 3 * mark_delta)
2363 if (fabs (new_value - mark_value) < (range->slider_end - range->slider_start) * 0.5 * mark_delta)
2365 new_value = mark_value;
2371 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2376 stop_scrolling (GtkRange *range)
2378 range_grab_remove (range);
2379 gtk_range_remove_step_timer (range);
2380 /* Flush any pending discontinuous/delayed updates */
2381 gtk_range_update_value (range);
2385 gtk_range_grab_broken (GtkWidget *widget,
2386 GdkEventGrabBroken *event)
2388 GtkRange *range = GTK_RANGE (widget);
2390 if (range->layout->grab_location != MOUSE_OUTSIDE)
2392 if (range->layout->grab_location == MOUSE_SLIDER)
2393 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2395 stop_scrolling (range);
2404 gtk_range_button_release (GtkWidget *widget,
2405 GdkEventButton *event)
2407 GtkRange *range = GTK_RANGE (widget);
2409 if (event->window == range->event_window)
2411 range->layout->mouse_x = event->x;
2412 range->layout->mouse_y = event->y;
2416 gdk_window_get_pointer (range->event_window,
2417 &range->layout->mouse_x,
2418 &range->layout->mouse_y,
2422 if (range->layout->grab_button == event->button)
2424 if (range->layout->grab_location == MOUSE_SLIDER)
2425 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2427 stop_scrolling (range);
2436 * _gtk_range_get_wheel_delta:
2437 * @range: a #GtkRange
2438 * @direction: A #GdkScrollDirection
2440 * Returns a good step value for the mouse wheel.
2442 * Return value: A good step value for the mouse wheel.
2447 _gtk_range_get_wheel_delta (GtkRange *range,
2448 GdkScrollDirection direction)
2450 GtkAdjustment *adj = range->adjustment;
2453 if (GTK_IS_SCROLLBAR (range))
2454 delta = pow (adj->page_size, 2.0 / 3.0);
2456 delta = adj->step_increment * 2;
2458 if (direction == GDK_SCROLL_UP ||
2459 direction == GDK_SCROLL_LEFT)
2462 if (range->inverted)
2469 gtk_range_scroll_event (GtkWidget *widget,
2470 GdkEventScroll *event)
2472 GtkRange *range = GTK_RANGE (widget);
2474 if (gtk_widget_get_realized (widget))
2476 GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2480 delta = _gtk_range_get_wheel_delta (range, event->direction);
2482 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2483 GTK_SCROLL_JUMP, adj->value + delta,
2486 /* Policy DELAYED makes sense with scroll events,
2487 * but DISCONTINUOUS doesn't, so we update immediately
2490 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2491 gtk_range_update_value (range);
2498 gtk_range_motion_notify (GtkWidget *widget,
2499 GdkEventMotion *event)
2503 range = GTK_RANGE (widget);
2505 gdk_event_request_motions (event);
2507 range->layout->mouse_x = event->x;
2508 range->layout->mouse_y = event->y;
2510 if (gtk_range_update_mouse_location (range))
2511 gtk_widget_queue_draw (widget);
2513 if (range->layout->grab_location == MOUSE_SLIDER)
2514 update_slider_position (range, event->x, event->y);
2516 /* We handled the event if the mouse was in the range_rect */
2517 return range->layout->mouse_location != MOUSE_OUTSIDE;
2521 gtk_range_enter_notify (GtkWidget *widget,
2522 GdkEventCrossing *event)
2524 GtkRange *range = GTK_RANGE (widget);
2526 range->layout->mouse_x = event->x;
2527 range->layout->mouse_y = event->y;
2529 if (gtk_range_update_mouse_location (range))
2530 gtk_widget_queue_draw (widget);
2536 gtk_range_leave_notify (GtkWidget *widget,
2537 GdkEventCrossing *event)
2539 GtkRange *range = GTK_RANGE (widget);
2541 range->layout->mouse_x = -1;
2542 range->layout->mouse_y = -1;
2544 if (gtk_range_update_mouse_location (range))
2545 gtk_widget_queue_draw (widget);
2551 gtk_range_grab_notify (GtkWidget *widget,
2552 gboolean was_grabbed)
2555 stop_scrolling (GTK_RANGE (widget));
2559 gtk_range_state_changed (GtkWidget *widget,
2560 GtkStateType previous_state)
2562 if (!gtk_widget_is_sensitive (widget))
2563 stop_scrolling (GTK_RANGE (widget));
2566 #define check_rectangle(rectangle1, rectangle2) \
2568 if (rectangle1.x != rectangle2.x) return TRUE; \
2569 if (rectangle1.y != rectangle2.y) return TRUE; \
2570 if (rectangle1.width != rectangle2.width) return TRUE; \
2571 if (rectangle1.height != rectangle2.height) return TRUE; \
2575 layout_changed (GtkRangeLayout *layout1,
2576 GtkRangeLayout *layout2)
2578 check_rectangle (layout1->slider, layout2->slider);
2579 check_rectangle (layout1->trough, layout2->trough);
2580 check_rectangle (layout1->stepper_a, layout2->stepper_a);
2581 check_rectangle (layout1->stepper_d, layout2->stepper_d);
2582 check_rectangle (layout1->stepper_b, layout2->stepper_b);
2583 check_rectangle (layout1->stepper_c, layout2->stepper_c);
2585 if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2586 if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2592 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2595 GtkRange *range = GTK_RANGE (data);
2596 /* create a copy of the layout */
2597 GtkRangeLayout layout = *range->layout;
2599 range->layout->recalc_marks = TRUE;
2600 range->need_recalc = TRUE;
2601 gtk_range_calc_layout (range, range->adjustment->value);
2603 /* now check whether the layout changed */
2604 if (layout_changed (range->layout, &layout))
2605 gtk_widget_queue_draw (GTK_WIDGET (range));
2607 /* Note that we don't round off to range->round_digits here.
2608 * that's because it's really broken to change a value
2609 * in response to a change signal on that value; round_digits
2610 * is therefore defined to be a filter on what the GtkRange
2611 * can input into the adjustment, not a filter that the GtkRange
2612 * will enforce on the adjustment.
2617 force_repaint (gpointer data)
2619 GtkRange *range = GTK_RANGE (data);
2621 range->layout->repaint_id = 0;
2622 if (gtk_widget_is_drawable (GTK_WIDGET (range)))
2623 gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2629 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2632 GtkRange *range = GTK_RANGE (data);
2633 /* create a copy of the layout */
2634 GtkRangeLayout layout = *range->layout;
2636 range->need_recalc = TRUE;
2637 gtk_range_calc_layout (range, range->adjustment->value);
2639 /* now check whether the layout changed */
2640 if (layout_changed (range->layout, &layout) ||
2641 (GTK_IS_SCALE (range) && GTK_SCALE (range)->draw_value))
2643 gtk_widget_queue_draw (GTK_WIDGET (range));
2644 /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2645 if (!range->layout->repaint_id)
2646 range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
2649 /* Note that we don't round off to range->round_digits here.
2650 * that's because it's really broken to change a value
2651 * in response to a change signal on that value; round_digits
2652 * is therefore defined to be a filter on what the GtkRange
2653 * can input into the adjustment, not a filter that the GtkRange
2654 * will enforce on the adjustment.
2657 g_signal_emit (range, signals[VALUE_CHANGED], 0);
2661 gtk_range_style_set (GtkWidget *widget,
2662 GtkStyle *previous_style)
2664 GtkRange *range = GTK_RANGE (widget);
2666 range->need_recalc = TRUE;
2668 GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
2672 apply_marks (GtkRange *range,
2679 for (i = 0; i < range->layout->n_marks; i++)
2681 mark = range->layout->marks[i];
2682 if ((oldval < mark && mark < *newval) ||
2683 (oldval > mark && mark > *newval))
2692 step_back (GtkRange *range)
2697 newval = range->adjustment->value - range->adjustment->step_increment;
2698 apply_marks (range, range->adjustment->value, &newval);
2699 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2700 GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2704 step_forward (GtkRange *range)
2709 newval = range->adjustment->value + range->adjustment->step_increment;
2710 apply_marks (range, range->adjustment->value, &newval);
2711 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2712 GTK_SCROLL_STEP_FORWARD, newval, &handled);
2717 page_back (GtkRange *range)
2722 newval = range->adjustment->value - range->adjustment->page_increment;
2723 apply_marks (range, range->adjustment->value, &newval);
2724 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2725 GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2729 page_forward (GtkRange *range)
2734 newval = range->adjustment->value + range->adjustment->page_increment;
2735 apply_marks (range, range->adjustment->value, &newval);
2736 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2737 GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2741 scroll_begin (GtkRange *range)
2744 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2745 GTK_SCROLL_START, range->adjustment->lower,
2750 scroll_end (GtkRange *range)
2755 newval = range->adjustment->upper - range->adjustment->page_size;
2756 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2761 gtk_range_scroll (GtkRange *range,
2762 GtkScrollType scroll)
2764 gdouble old_value = range->adjustment->value;
2768 case GTK_SCROLL_STEP_LEFT:
2769 if (should_invert (range))
2770 step_forward (range);
2775 case GTK_SCROLL_STEP_UP:
2776 if (should_invert (range))
2777 step_forward (range);
2782 case GTK_SCROLL_STEP_RIGHT:
2783 if (should_invert (range))
2786 step_forward (range);
2789 case GTK_SCROLL_STEP_DOWN:
2790 if (should_invert (range))
2793 step_forward (range);
2796 case GTK_SCROLL_STEP_BACKWARD:
2800 case GTK_SCROLL_STEP_FORWARD:
2801 step_forward (range);
2804 case GTK_SCROLL_PAGE_LEFT:
2805 if (should_invert (range))
2806 page_forward (range);
2811 case GTK_SCROLL_PAGE_UP:
2812 if (should_invert (range))
2813 page_forward (range);
2818 case GTK_SCROLL_PAGE_RIGHT:
2819 if (should_invert (range))
2822 page_forward (range);
2825 case GTK_SCROLL_PAGE_DOWN:
2826 if (should_invert (range))
2829 page_forward (range);
2832 case GTK_SCROLL_PAGE_BACKWARD:
2836 case GTK_SCROLL_PAGE_FORWARD:
2837 page_forward (range);
2840 case GTK_SCROLL_START:
2841 scroll_begin (range);
2844 case GTK_SCROLL_END:
2848 case GTK_SCROLL_JUMP:
2849 /* Used by CList, range doesn't use it. */
2852 case GTK_SCROLL_NONE:
2856 return range->adjustment->value != old_value;
2860 gtk_range_move_slider (GtkRange *range,
2861 GtkScrollType scroll)
2863 gboolean cursor_only;
2865 g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2866 "gtk-keynav-cursor-only", &cursor_only,
2871 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2873 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2875 if (scroll == GTK_SCROLL_STEP_UP ||
2876 scroll == GTK_SCROLL_STEP_DOWN)
2879 gtk_widget_child_focus (toplevel,
2880 scroll == GTK_SCROLL_STEP_UP ?
2881 GTK_DIR_UP : GTK_DIR_DOWN);
2887 if (scroll == GTK_SCROLL_STEP_LEFT ||
2888 scroll == GTK_SCROLL_STEP_RIGHT)
2891 gtk_widget_child_focus (toplevel,
2892 scroll == GTK_SCROLL_STEP_LEFT ?
2893 GTK_DIR_LEFT : GTK_DIR_RIGHT);
2899 if (! gtk_range_scroll (range, scroll))
2900 gtk_widget_error_bell (GTK_WIDGET (range));
2902 /* Policy DELAYED makes sense with key events,
2903 * but DISCONTINUOUS doesn't, so we update immediately
2906 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2907 gtk_range_update_value (range);
2911 gtk_range_get_props (GtkRange *range,
2915 gint *trough_border,
2916 gint *stepper_spacing,
2917 gboolean *trough_under_steppers,
2918 gint *arrow_displacement_x,
2919 gint *arrow_displacement_y)
2921 GtkWidget *widget = GTK_WIDGET (range);
2922 gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
2923 gint tmp_stepper_spacing, tmp_trough_under_steppers;
2924 gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2926 gtk_widget_style_get (widget,
2927 "slider-width", &tmp_slider_width,
2928 "trough-border", &tmp_trough_border,
2929 "stepper-size", &tmp_stepper_size,
2930 "stepper-spacing", &tmp_stepper_spacing,
2931 "trough-under-steppers", &tmp_trough_under_steppers,
2932 "arrow-displacement-x", &tmp_arrow_displacement_x,
2933 "arrow-displacement-y", &tmp_arrow_displacement_y,
2936 if (tmp_stepper_spacing > 0)
2937 tmp_trough_under_steppers = FALSE;
2939 if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
2941 gint focus_line_width;
2944 gtk_widget_style_get (GTK_WIDGET (range),
2945 "focus-line-width", &focus_line_width,
2946 "focus-padding", &focus_padding,
2949 tmp_focus_width = focus_line_width + focus_padding;
2953 tmp_focus_width = 0;
2957 *slider_width = tmp_slider_width;
2960 *focus_width = tmp_focus_width;
2963 *trough_border = tmp_trough_border;
2966 *stepper_size = tmp_stepper_size;
2968 if (stepper_spacing)
2969 *stepper_spacing = tmp_stepper_spacing;
2971 if (trough_under_steppers)
2972 *trough_under_steppers = tmp_trough_under_steppers;
2974 if (arrow_displacement_x)
2975 *arrow_displacement_x = tmp_arrow_displacement_x;
2977 if (arrow_displacement_y)
2978 *arrow_displacement_y = tmp_arrow_displacement_y;
2981 #define POINT_IN_RECT(xcoord, ycoord, rect) \
2982 ((xcoord) >= (rect).x && \
2983 (xcoord) < ((rect).x + (rect).width) && \
2984 (ycoord) >= (rect).y && \
2985 (ycoord) < ((rect).y + (rect).height))
2987 /* Update mouse location, return TRUE if it changes */
2989 gtk_range_update_mouse_location (GtkRange *range)
2995 widget = GTK_WIDGET (range);
2997 old = range->layout->mouse_location;
2999 x = range->layout->mouse_x;
3000 y = range->layout->mouse_y;
3002 if (range->layout->grab_location != MOUSE_OUTSIDE)
3003 range->layout->mouse_location = range->layout->grab_location;
3004 else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
3005 range->layout->mouse_location = MOUSE_STEPPER_A;
3006 else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
3007 range->layout->mouse_location = MOUSE_STEPPER_B;
3008 else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
3009 range->layout->mouse_location = MOUSE_STEPPER_C;
3010 else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
3011 range->layout->mouse_location = MOUSE_STEPPER_D;
3012 else if (POINT_IN_RECT (x, y, range->layout->slider))
3013 range->layout->mouse_location = MOUSE_SLIDER;
3014 else if (POINT_IN_RECT (x, y, range->layout->trough))
3015 range->layout->mouse_location = MOUSE_TROUGH;
3016 else if (POINT_IN_RECT (x, y, widget->allocation))
3017 range->layout->mouse_location = MOUSE_WIDGET;
3019 range->layout->mouse_location = MOUSE_OUTSIDE;
3021 return old != range->layout->mouse_location;
3024 /* Clamp rect, border inside widget->allocation, such that we prefer
3025 * to take space from border not rect in all directions, and prefer to
3026 * give space to border over rect in one direction.
3029 clamp_dimensions (GtkWidget *widget,
3032 gboolean border_expands_horizontally)
3034 gint extra, shortage;
3036 g_return_if_fail (rect->x == 0);
3037 g_return_if_fail (rect->y == 0);
3038 g_return_if_fail (rect->width >= 0);
3039 g_return_if_fail (rect->height >= 0);
3043 extra = widget->allocation.width - border->left - border->right - rect->width;
3046 if (border_expands_horizontally)
3048 border->left += extra / 2;
3049 border->right += extra / 2 + extra % 2;
3053 rect->width += extra;
3057 /* See if we can fit rect, if not kill the border */
3058 shortage = rect->width - widget->allocation.width;
3061 rect->width = widget->allocation.width;
3062 /* lose the border */
3068 /* See if we can fit rect with borders */
3069 shortage = rect->width + border->left + border->right -
3070 widget->allocation.width;
3073 /* Shrink borders */
3074 border->left -= shortage / 2;
3075 border->right -= shortage / 2 + shortage % 2;
3081 extra = widget->allocation.height - border->top - border->bottom - rect->height;
3084 if (border_expands_horizontally)
3086 /* don't expand border vertically */
3087 rect->height += extra;
3091 border->top += extra / 2;
3092 border->bottom += extra / 2 + extra % 2;
3096 /* See if we can fit rect, if not kill the border */
3097 shortage = rect->height - widget->allocation.height;
3100 rect->height = widget->allocation.height;
3101 /* lose the border */
3107 /* See if we can fit rect with borders */
3108 shortage = rect->height + border->top + border->bottom -
3109 widget->allocation.height;
3112 /* Shrink borders */
3113 border->top -= shortage / 2;
3114 border->bottom -= shortage / 2 + shortage % 2;
3120 gtk_range_calc_request (GtkRange *range,
3125 gint stepper_spacing,
3126 GdkRectangle *range_rect,
3129 gboolean *has_steppers_ab,
3130 gboolean *has_steppers_cd,
3131 gint *slider_length_p)
3143 if (GTK_RANGE_GET_CLASS (range)->get_range_border)
3144 GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
3149 if (range->has_stepper_a)
3151 if (range->has_stepper_b)
3153 if (range->has_stepper_c)
3155 if (range->has_stepper_d)
3158 n_steppers = n_steppers_ab + n_steppers_cd;
3160 slider_length = range->min_slider_size;
3165 /* We never expand to fill available space in the small dimension
3166 * (i.e. vertical scrollbars are always a fixed width)
3168 if (range->orientation == GTK_ORIENTATION_VERTICAL)
3170 range_rect->width = (focus_width + trough_border) * 2 + slider_width;
3171 range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3173 if (n_steppers_ab > 0)
3174 range_rect->height += stepper_spacing;
3176 if (n_steppers_cd > 0)
3177 range_rect->height += stepper_spacing;
3181 range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3182 range_rect->height = (focus_width + trough_border) * 2 + slider_width;
3184 if (n_steppers_ab > 0)
3185 range_rect->width += stepper_spacing;
3187 if (n_steppers_cd > 0)
3188 range_rect->width += stepper_spacing;
3192 *n_steppers_p = n_steppers;
3194 if (has_steppers_ab)
3195 *has_steppers_ab = (n_steppers_ab > 0);
3197 if (has_steppers_cd)
3198 *has_steppers_cd = (n_steppers_cd > 0);
3200 if (slider_length_p)
3201 *slider_length_p = slider_length;
3205 gtk_range_calc_layout (GtkRange *range,
3206 gdouble adjustment_value)
3208 gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
3212 gboolean has_steppers_ab;
3213 gboolean has_steppers_cd;
3214 gboolean trough_under_steppers;
3215 GdkRectangle range_rect;
3216 GtkRangeLayout *layout;
3219 if (!range->need_recalc)
3222 /* If we have a too-small allocation, we prefer the steppers over
3223 * the trough/slider, probably the steppers are a more useful
3224 * feature in small spaces.
3226 * Also, we prefer to draw the range itself rather than the border
3227 * areas if there's a conflict, since the borders will be decoration
3228 * not controls. Though this depends on subclasses cooperating by
3229 * not drawing on range->range_rect.
3232 widget = GTK_WIDGET (range);
3233 layout = range->layout;
3235 gtk_range_get_props (range,
3236 &slider_width, &stepper_size,
3237 &focus_width, &trough_border,
3238 &stepper_spacing, &trough_under_steppers,
3241 gtk_range_calc_request (range,
3242 slider_width, stepper_size,
3243 focus_width, trough_border, stepper_spacing,
3244 &range_rect, &border, &n_steppers,
3245 &has_steppers_ab, &has_steppers_cd, &slider_length);
3247 /* We never expand to fill available space in the small dimension
3248 * (i.e. vertical scrollbars are always a fixed width)
3250 if (range->orientation == GTK_ORIENTATION_VERTICAL)
3252 clamp_dimensions (widget, &range_rect, &border, TRUE);
3256 clamp_dimensions (widget, &range_rect, &border, FALSE);
3259 range_rect.x = border.left;
3260 range_rect.y = border.top;
3262 range->range_rect = range_rect;
3264 if (range->orientation == GTK_ORIENTATION_VERTICAL)
3266 gint stepper_width, stepper_height;
3268 /* Steppers are the width of the range, and stepper_size in
3269 * height, or if we don't have enough height, divided equally
3270 * among available space.
3272 stepper_width = range_rect.width - focus_width * 2;
3274 if (trough_under_steppers)
3275 stepper_width -= trough_border * 2;
3277 if (stepper_width < 1)
3278 stepper_width = range_rect.width; /* screw the trough border */
3280 if (n_steppers == 0)
3281 stepper_height = 0; /* avoid divide by n_steppers */
3283 stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
3287 layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3288 layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3290 if (range->has_stepper_a)
3292 layout->stepper_a.width = stepper_width;
3293 layout->stepper_a.height = stepper_height;
3297 layout->stepper_a.width = 0;
3298 layout->stepper_a.height = 0;
3303 layout->stepper_b.x = layout->stepper_a.x;
3304 layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
3306 if (range->has_stepper_b)
3308 layout->stepper_b.width = stepper_width;
3309 layout->stepper_b.height = stepper_height;
3313 layout->stepper_b.width = 0;
3314 layout->stepper_b.height = 0;
3319 if (range->has_stepper_d)
3321 layout->stepper_d.width = stepper_width;
3322 layout->stepper_d.height = stepper_height;
3326 layout->stepper_d.width = 0;
3327 layout->stepper_d.height = 0;
3330 layout->stepper_d.x = layout->stepper_a.x;
3331 layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
3335 if (range->has_stepper_c)
3337 layout->stepper_c.width = stepper_width;
3338 layout->stepper_c.height = stepper_height;
3342 layout->stepper_c.width = 0;
3343 layout->stepper_c.height = 0;
3346 layout->stepper_c.x = layout->stepper_a.x;
3347 layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
3349 /* Now the trough is the remaining space between steppers B and C,
3350 * if any, minus spacing
3352 layout->trough.x = range_rect.x;
3353 layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
3354 layout->trough.width = range_rect.width;
3355 layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
3357 /* Slider fits into the trough, with stepper_spacing on either side,
3358 * and the size/position based on the adjustment or fixed, depending.
3360 layout->slider.x = layout->trough.x + focus_width + trough_border;
3361 layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
3363 /* Compute slider position/length */
3365 gint y, bottom, top, height;
3367 top = layout->trough.y;
3368 bottom = layout->trough.y + layout->trough.height;
3370 if (! trough_under_steppers)
3372 top += trough_border;
3373 bottom -= trough_border;
3376 /* slider height is the fraction (page_size /
3377 * total_adjustment_range) times the trough height in pixels
3380 if (range->adjustment->upper - range->adjustment->lower != 0)
3381 height = ((bottom - top) * (range->adjustment->page_size /
3382 (range->adjustment->upper - range->adjustment->lower)));
3384 height = range->min_slider_size;
3386 if (height < range->min_slider_size ||
3387 range->slider_size_fixed)
3388 height = range->min_slider_size;
3390 height = MIN (height, layout->trough.height);
3394 if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3395 y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
3396 (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3398 y = CLAMP (y, top, bottom);
3400 if (should_invert (range))
3401 y = bottom - (y - top + height);
3403 layout->slider.y = y;
3404 layout->slider.height = height;
3406 /* These are publically exported */
3407 range->slider_start = layout->slider.y;
3408 range->slider_end = layout->slider.y + layout->slider.height;
3413 gint stepper_width, stepper_height;
3415 /* Steppers are the height of the range, and stepper_size in
3416 * width, or if we don't have enough width, divided equally
3417 * among available space.
3419 stepper_height = range_rect.height + focus_width * 2;
3421 if (trough_under_steppers)
3422 stepper_height -= trough_border * 2;
3424 if (stepper_height < 1)
3425 stepper_height = range_rect.height; /* screw the trough border */
3427 if (n_steppers == 0)
3428 stepper_width = 0; /* avoid divide by n_steppers */
3430 stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3434 layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3435 layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3437 if (range->has_stepper_a)
3439 layout->stepper_a.width = stepper_width;
3440 layout->stepper_a.height = stepper_height;
3444 layout->stepper_a.width = 0;
3445 layout->stepper_a.height = 0;
3450 layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3451 layout->stepper_b.y = layout->stepper_a.y;
3453 if (range->has_stepper_b)
3455 layout->stepper_b.width = stepper_width;
3456 layout->stepper_b.height = stepper_height;
3460 layout->stepper_b.width = 0;
3461 layout->stepper_b.height = 0;
3466 if (range->has_stepper_d)
3468 layout->stepper_d.width = stepper_width;
3469 layout->stepper_d.height = stepper_height;
3473 layout->stepper_d.width = 0;
3474 layout->stepper_d.height = 0;
3477 layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3478 layout->stepper_d.y = layout->stepper_a.y;
3483 if (range->has_stepper_c)
3485 layout->stepper_c.width = stepper_width;
3486 layout->stepper_c.height = stepper_height;
3490 layout->stepper_c.width = 0;
3491 layout->stepper_c.height = 0;
3494 layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3495 layout->stepper_c.y = layout->stepper_a.y;
3497 /* Now the trough is the remaining space between steppers B and C,
3500 layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3501 layout->trough.y = range_rect.y;
3503 layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3504 layout->trough.height = range_rect.height;
3506 /* Slider fits into the trough, with stepper_spacing on either side,
3507 * and the size/position based on the adjustment or fixed, depending.
3509 layout->slider.y = layout->trough.y + focus_width + trough_border;
3510 layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3512 /* Compute slider position/length */
3514 gint x, left, right, width;
3516 left = layout->trough.x;
3517 right = layout->trough.x + layout->trough.width;
3519 if (! trough_under_steppers)
3521 left += trough_border;
3522 right -= trough_border;
3525 /* slider width is the fraction (page_size /
3526 * total_adjustment_range) times the trough width in pixels
3529 if (range->adjustment->upper - range->adjustment->lower != 0)
3530 width = ((right - left) * (range->adjustment->page_size /
3531 (range->adjustment->upper - range->adjustment->lower)));
3533 width = range->min_slider_size;
3535 if (width < range->min_slider_size ||
3536 range->slider_size_fixed)
3537 width = range->min_slider_size;
3539 width = MIN (width, layout->trough.width);
3543 if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3544 x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3545 (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3547 x = CLAMP (x, left, right);
3549 if (should_invert (range))
3550 x = right - (x - left + width);
3552 layout->slider.x = x;
3553 layout->slider.width = width;
3555 /* These are publically exported */
3556 range->slider_start = layout->slider.x;
3557 range->slider_end = layout->slider.x + layout->slider.width;
3561 gtk_range_update_mouse_location (range);
3563 switch (range->layout->upper_sensitivity)
3565 case GTK_SENSITIVITY_AUTO:
3566 range->layout->upper_sensitive =
3567 (range->adjustment->value <
3568 (range->adjustment->upper - range->adjustment->page_size));
3571 case GTK_SENSITIVITY_ON:
3572 range->layout->upper_sensitive = TRUE;
3575 case GTK_SENSITIVITY_OFF:
3576 range->layout->upper_sensitive = FALSE;
3580 switch (range->layout->lower_sensitivity)
3582 case GTK_SENSITIVITY_AUTO:
3583 range->layout->lower_sensitive =
3584 (range->adjustment->value > range->adjustment->lower);
3587 case GTK_SENSITIVITY_ON:
3588 range->layout->lower_sensitive = TRUE;
3591 case GTK_SENSITIVITY_OFF:
3592 range->layout->lower_sensitive = FALSE;
3597 static GdkRectangle*
3598 get_area (GtkRange *range,
3599 MouseLocation location)
3603 case MOUSE_STEPPER_A:
3604 return &range->layout->stepper_a;
3605 case MOUSE_STEPPER_B:
3606 return &range->layout->stepper_b;
3607 case MOUSE_STEPPER_C:
3608 return &range->layout->stepper_c;
3609 case MOUSE_STEPPER_D:
3610 return &range->layout->stepper_d;
3612 return &range->layout->trough;
3614 return &range->layout->slider;
3620 g_warning (G_STRLOC": bug");
3625 gtk_range_calc_marks (GtkRange *range)
3629 if (!range->layout->recalc_marks)
3632 range->layout->recalc_marks = FALSE;
3634 for (i = 0; i < range->layout->n_marks; i++)
3636 range->need_recalc = TRUE;
3637 gtk_range_calc_layout (range, range->layout->marks[i]);
3638 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
3639 range->layout->mark_pos[i] = range->layout->slider.x + range->layout->slider.width / 2;
3641 range->layout->mark_pos[i] = range->layout->slider.y + range->layout->slider.height / 2;
3644 range->need_recalc = TRUE;
3648 gtk_range_real_change_value (GtkRange *range,
3649 GtkScrollType scroll,
3652 /* potentially adjust the bounds _before_ we clamp */
3653 g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3655 if (range->layout->restrict_to_fill_level)
3656 value = MIN (value, MAX (range->adjustment->lower,
3657 range->layout->fill_level));
3659 value = CLAMP (value, range->adjustment->lower,
3660 (range->adjustment->upper - range->adjustment->page_size));
3662 if (range->round_digits >= 0)
3667 i = range->round_digits;
3672 value = floor ((value * power) + 0.5) / power;
3675 if (range->adjustment->value != value)
3677 range->need_recalc = TRUE;
3679 gtk_widget_queue_draw (GTK_WIDGET (range));
3681 switch (range->update_policy)
3683 case GTK_UPDATE_CONTINUOUS:
3684 gtk_adjustment_set_value (range->adjustment, value);
3687 /* Delayed means we update after a period of inactivity */
3688 case GTK_UPDATE_DELAYED:
3689 gtk_range_reset_update_timer (range);
3692 /* Discontinuous means we update on button release */
3693 case GTK_UPDATE_DISCONTINUOUS:
3694 /* don't emit value_changed signal */
3695 range->adjustment->value = value;
3696 range->update_pending = TRUE;
3704 gtk_range_update_value (GtkRange *range)
3706 gtk_range_remove_update_timer (range);
3708 if (range->update_pending)
3710 gtk_adjustment_value_changed (range->adjustment);
3712 range->update_pending = FALSE;
3716 struct _GtkRangeStepTimer
3723 second_timeout (gpointer data)
3727 range = GTK_RANGE (data);
3728 gtk_range_scroll (range, range->timer->step);
3734 initial_timeout (gpointer data)
3737 GtkSettings *settings;
3740 settings = gtk_widget_get_settings (GTK_WIDGET (data));
3741 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3743 range = GTK_RANGE (data);
3744 range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3752 gtk_range_add_step_timer (GtkRange *range,
3755 GtkSettings *settings;
3758 g_return_if_fail (range->timer == NULL);
3759 g_return_if_fail (step != GTK_SCROLL_NONE);
3761 settings = gtk_widget_get_settings (GTK_WIDGET (range));
3762 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3764 range->timer = g_new (GtkRangeStepTimer, 1);
3766 range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3769 range->timer->step = step;
3771 gtk_range_scroll (range, range->timer->step);
3775 gtk_range_remove_step_timer (GtkRange *range)
3779 if (range->timer->timeout_id != 0)
3780 g_source_remove (range->timer->timeout_id);
3782 g_free (range->timer);
3784 range->timer = NULL;
3789 update_timeout (gpointer data)
3793 range = GTK_RANGE (data);
3794 gtk_range_update_value (range);
3795 range->update_timeout_id = 0;
3802 gtk_range_reset_update_timer (GtkRange *range)
3804 gtk_range_remove_update_timer (range);
3806 range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3812 gtk_range_remove_update_timer (GtkRange *range)
3814 if (range->update_timeout_id != 0)
3816 g_source_remove (range->update_timeout_id);
3817 range->update_timeout_id = 0;
3822 _gtk_range_set_stop_values (GtkRange *range,
3828 g_free (range->layout->marks);
3829 range->layout->marks = g_new (gdouble, n_values);
3831 g_free (range->layout->mark_pos);
3832 range->layout->mark_pos = g_new (gint, n_values);
3834 range->layout->n_marks = n_values;
3836 for (i = 0; i < n_values; i++)
3837 range->layout->marks[i] = values[i];
3839 range->layout->recalc_marks = TRUE;
3843 _gtk_range_get_stop_positions (GtkRange *range,
3846 gtk_range_calc_marks (range);
3849 *values = g_memdup (range->layout->mark_pos, range->layout->n_marks * sizeof (gint));
3851 return range->layout->n_marks;
3854 #define __GTK_RANGE_C__
3855 #include "gtkaliasdef.c"