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 #GtkScrollbar.
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;
143 GdkDevice *grab_device;
147 static void gtk_range_set_property (GObject *object,
151 static void gtk_range_get_property (GObject *object,
155 static void gtk_range_destroy (GtkObject *object);
156 static void gtk_range_size_request (GtkWidget *widget,
157 GtkRequisition *requisition);
158 static void gtk_range_size_allocate (GtkWidget *widget,
159 GtkAllocation *allocation);
160 static void gtk_range_realize (GtkWidget *widget);
161 static void gtk_range_unrealize (GtkWidget *widget);
162 static void gtk_range_map (GtkWidget *widget);
163 static void gtk_range_unmap (GtkWidget *widget);
164 static gboolean gtk_range_expose (GtkWidget *widget,
165 GdkEventExpose *event);
166 static gboolean gtk_range_button_press (GtkWidget *widget,
167 GdkEventButton *event);
168 static gboolean gtk_range_button_release (GtkWidget *widget,
169 GdkEventButton *event);
170 static gboolean gtk_range_motion_notify (GtkWidget *widget,
171 GdkEventMotion *event);
172 static gboolean gtk_range_enter_notify (GtkWidget *widget,
173 GdkEventCrossing *event);
174 static gboolean gtk_range_leave_notify (GtkWidget *widget,
175 GdkEventCrossing *event);
176 static gboolean gtk_range_grab_broken (GtkWidget *widget,
177 GdkEventGrabBroken *event);
178 static void gtk_range_grab_notify (GtkWidget *widget,
179 gboolean was_grabbed);
180 static void gtk_range_state_changed (GtkWidget *widget,
181 GtkStateType previous_state);
182 static gboolean gtk_range_scroll_event (GtkWidget *widget,
183 GdkEventScroll *event);
184 static void gtk_range_style_set (GtkWidget *widget,
185 GtkStyle *previous_style);
186 static void update_slider_position (GtkRange *range,
189 static void stop_scrolling (GtkRange *range);
193 static void gtk_range_move_slider (GtkRange *range,
194 GtkScrollType scroll);
197 static gboolean gtk_range_scroll (GtkRange *range,
198 GtkScrollType scroll);
199 static gboolean gtk_range_update_mouse_location (GtkRange *range);
200 static void gtk_range_calc_layout (GtkRange *range,
201 gdouble adjustment_value);
202 static void gtk_range_calc_marks (GtkRange *range);
203 static void gtk_range_get_props (GtkRange *range,
208 gint *stepper_spacing,
209 gboolean *trough_under_steppers,
210 gint *arrow_displacement_x,
211 gint *arrow_displacement_y);
212 static void gtk_range_calc_request (GtkRange *range,
217 gint stepper_spacing,
218 GdkRectangle *range_rect,
221 gboolean *has_steppers_ab,
222 gboolean *has_steppers_cd,
223 gint *slider_length_p);
224 static void gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
226 static void gtk_range_adjustment_changed (GtkAdjustment *adjustment,
228 static void gtk_range_add_step_timer (GtkRange *range,
230 static void gtk_range_remove_step_timer (GtkRange *range);
231 static void gtk_range_reset_update_timer (GtkRange *range);
232 static void gtk_range_remove_update_timer (GtkRange *range);
233 static GdkRectangle* get_area (GtkRange *range,
234 MouseLocation location);
235 static gboolean gtk_range_real_change_value (GtkRange *range,
236 GtkScrollType scroll,
238 static void gtk_range_update_value (GtkRange *range);
239 static gboolean gtk_range_key_press (GtkWidget *range,
243 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
244 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
247 static guint signals[LAST_SIGNAL];
251 gtk_range_class_init (GtkRangeClass *class)
253 GObjectClass *gobject_class;
254 GtkObjectClass *object_class;
255 GtkWidgetClass *widget_class;
257 gobject_class = G_OBJECT_CLASS (class);
258 object_class = (GtkObjectClass*) class;
259 widget_class = (GtkWidgetClass*) class;
261 gobject_class->set_property = gtk_range_set_property;
262 gobject_class->get_property = gtk_range_get_property;
264 object_class->destroy = gtk_range_destroy;
266 widget_class->size_request = gtk_range_size_request;
267 widget_class->size_allocate = gtk_range_size_allocate;
268 widget_class->realize = gtk_range_realize;
269 widget_class->unrealize = gtk_range_unrealize;
270 widget_class->map = gtk_range_map;
271 widget_class->unmap = gtk_range_unmap;
272 widget_class->expose_event = gtk_range_expose;
273 widget_class->button_press_event = gtk_range_button_press;
274 widget_class->button_release_event = gtk_range_button_release;
275 widget_class->motion_notify_event = gtk_range_motion_notify;
276 widget_class->scroll_event = gtk_range_scroll_event;
277 widget_class->enter_notify_event = gtk_range_enter_notify;
278 widget_class->leave_notify_event = gtk_range_leave_notify;
279 widget_class->grab_broken_event = gtk_range_grab_broken;
280 widget_class->grab_notify = gtk_range_grab_notify;
281 widget_class->state_changed = gtk_range_state_changed;
282 widget_class->style_set = gtk_range_style_set;
283 widget_class->key_press_event = gtk_range_key_press;
285 class->move_slider = gtk_range_move_slider;
286 class->change_value = gtk_range_real_change_value;
288 class->slider_detail = "slider";
289 class->stepper_detail = "stepper";
292 * GtkRange::value-changed:
293 * @range: the #GtkRange that received the signal
295 * Emitted when the range value changes.
297 signals[VALUE_CHANGED] =
298 g_signal_new (I_("value-changed"),
299 G_TYPE_FROM_CLASS (gobject_class),
301 G_STRUCT_OFFSET (GtkRangeClass, value_changed),
303 _gtk_marshal_VOID__VOID,
307 * GtkRange::adjust-bounds:
308 * @range: the #GtkRange that received the signal
309 * @value: the value before we clamp
311 * Emitted before clamping a value, to give the application a
312 * chance to adjust the bounds.
314 signals[ADJUST_BOUNDS] =
315 g_signal_new (I_("adjust-bounds"),
316 G_TYPE_FROM_CLASS (gobject_class),
318 G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
320 _gtk_marshal_VOID__DOUBLE,
325 * GtkRange::move-slider:
326 * @range: the #GtkRange that received the signal
327 * @step: how to move the slider
329 * Virtual function that moves the slider. Used for keybindings.
331 signals[MOVE_SLIDER] =
332 g_signal_new (I_("move-slider"),
333 G_TYPE_FROM_CLASS (gobject_class),
334 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
335 G_STRUCT_OFFSET (GtkRangeClass, move_slider),
337 _gtk_marshal_VOID__ENUM,
339 GTK_TYPE_SCROLL_TYPE);
342 * GtkRange::change-value:
343 * @range: the #GtkRange that received the signal
344 * @scroll: the type of scroll action that was performed
345 * @value: the new value resulting from the scroll action
346 * @returns: %TRUE to prevent other handlers from being invoked for the
347 * signal, %FALSE to propagate the signal further
349 * The ::change-value signal is emitted when a scroll action is
350 * performed on a range. It allows an application to determine the
351 * type of scroll event that occurred and the resultant new value.
352 * The application can handle the event itself and return %TRUE to
353 * prevent further processing. Or, by returning %FALSE, it can pass
354 * the event to other handlers until the default GTK+ handler is
357 * The value parameter is unrounded. An application that overrides
358 * the ::change-value signal is responsible for clamping the value to
359 * the desired number of decimal digits; the default GTK+ handler
360 * clamps the value based on @range->round_digits.
362 * It is not possible to use delayed update policies in an overridden
363 * ::change-value handler.
367 signals[CHANGE_VALUE] =
368 g_signal_new (I_("change-value"),
369 G_TYPE_FROM_CLASS (gobject_class),
371 G_STRUCT_OFFSET (GtkRangeClass, change_value),
372 _gtk_boolean_handled_accumulator, NULL,
373 _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
375 GTK_TYPE_SCROLL_TYPE,
378 g_object_class_override_property (gobject_class,
382 g_object_class_install_property (gobject_class,
384 g_param_spec_enum ("update-policy",
386 P_("How the range should be updated on the screen"),
387 GTK_TYPE_UPDATE_TYPE,
388 GTK_UPDATE_CONTINUOUS,
389 GTK_PARAM_READWRITE));
391 g_object_class_install_property (gobject_class,
393 g_param_spec_object ("adjustment",
395 P_("The GtkAdjustment that contains the current value of this range object"),
397 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
399 g_object_class_install_property (gobject_class,
401 g_param_spec_boolean ("inverted",
403 P_("Invert direction slider moves to increase range value"),
405 GTK_PARAM_READWRITE));
407 g_object_class_install_property (gobject_class,
408 PROP_LOWER_STEPPER_SENSITIVITY,
409 g_param_spec_enum ("lower-stepper-sensitivity",
410 P_("Lower stepper sensitivity"),
411 P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
412 GTK_TYPE_SENSITIVITY_TYPE,
413 GTK_SENSITIVITY_AUTO,
414 GTK_PARAM_READWRITE));
416 g_object_class_install_property (gobject_class,
417 PROP_UPPER_STEPPER_SENSITIVITY,
418 g_param_spec_enum ("upper-stepper-sensitivity",
419 P_("Upper stepper sensitivity"),
420 P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
421 GTK_TYPE_SENSITIVITY_TYPE,
422 GTK_SENSITIVITY_AUTO,
423 GTK_PARAM_READWRITE));
426 * GtkRange:show-fill-level:
428 * The show-fill-level property controls whether fill level indicator
429 * graphics are displayed on the trough. See
430 * gtk_range_set_show_fill_level().
434 g_object_class_install_property (gobject_class,
435 PROP_SHOW_FILL_LEVEL,
436 g_param_spec_boolean ("show-fill-level",
437 P_("Show Fill Level"),
438 P_("Whether to display a fill level indicator graphics on trough."),
440 GTK_PARAM_READWRITE));
443 * GtkRange:restrict-to-fill-level:
445 * The restrict-to-fill-level property controls whether slider
446 * movement is restricted to an upper boundary set by the
447 * fill level. See gtk_range_set_restrict_to_fill_level().
451 g_object_class_install_property (gobject_class,
452 PROP_RESTRICT_TO_FILL_LEVEL,
453 g_param_spec_boolean ("restrict-to-fill-level",
454 P_("Restrict to Fill Level"),
455 P_("Whether to restrict the upper boundary to the fill level."),
457 GTK_PARAM_READWRITE));
460 * GtkRange:fill-level:
462 * The fill level (e.g. prebuffering of a network stream).
463 * See gtk_range_set_fill_level().
467 g_object_class_install_property (gobject_class,
469 g_param_spec_double ("fill-level",
471 P_("The fill level."),
475 GTK_PARAM_READWRITE));
477 gtk_widget_class_install_style_property (widget_class,
478 g_param_spec_int ("slider-width",
480 P_("Width of scrollbar or scale thumb"),
484 GTK_PARAM_READABLE));
485 gtk_widget_class_install_style_property (widget_class,
486 g_param_spec_int ("trough-border",
488 P_("Spacing between thumb/steppers and outer trough bevel"),
492 GTK_PARAM_READABLE));
493 gtk_widget_class_install_style_property (widget_class,
494 g_param_spec_int ("stepper-size",
496 P_("Length of step buttons at ends"),
500 GTK_PARAM_READABLE));
502 * GtkRange:stepper-spacing:
504 * The spacing between the stepper buttons and thumb. Note that
505 * setting this value to anything > 0 will automatically set the
506 * trough-under-steppers style property to %TRUE as well. Also,
507 * stepper-spacing won't have any effect if there are no steppers.
509 gtk_widget_class_install_style_property (widget_class,
510 g_param_spec_int ("stepper-spacing",
511 P_("Stepper Spacing"),
512 P_("Spacing between step buttons and thumb"),
516 GTK_PARAM_READABLE));
517 gtk_widget_class_install_style_property (widget_class,
518 g_param_spec_int ("arrow-displacement-x",
519 P_("Arrow X Displacement"),
520 P_("How far in the x direction to move the arrow when the button is depressed"),
524 GTK_PARAM_READABLE));
525 gtk_widget_class_install_style_property (widget_class,
526 g_param_spec_int ("arrow-displacement-y",
527 P_("Arrow Y Displacement"),
528 P_("How far in the y direction to move the arrow when the button is depressed"),
532 GTK_PARAM_READABLE));
534 gtk_widget_class_install_style_property (widget_class,
535 g_param_spec_boolean ("activate-slider",
536 P_("Draw slider ACTIVE during drag"),
537 P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
539 GTK_PARAM_READABLE));
542 * GtkRange:trough-side-details:
544 * When %TRUE, the parts of the trough on the two sides of the
545 * slider are drawn with different details.
549 gtk_widget_class_install_style_property (widget_class,
550 g_param_spec_boolean ("trough-side-details",
551 P_("Trough Side Details"),
552 P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"),
554 GTK_PARAM_READABLE));
557 * GtkRange:trough-under-steppers:
559 * Whether to draw the trough across the full length of the range or
560 * to exclude the steppers and their spacing. Note that setting the
561 * #GtkRange:stepper-spacing style property to any value > 0 will
562 * automatically enable trough-under-steppers too.
566 gtk_widget_class_install_style_property (widget_class,
567 g_param_spec_boolean ("trough-under-steppers",
568 P_("Trough Under Steppers"),
569 P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
571 GTK_PARAM_READABLE));
574 * GtkRange:arrow-scaling:
576 * The arrow size proportion relative to the scroll button size.
580 gtk_widget_class_install_style_property (widget_class,
581 g_param_spec_float ("arrow-scaling",
583 P_("Arrow scaling with regard to scroll button size"),
585 GTK_PARAM_READABLE));
587 g_type_class_add_private (class, sizeof (GtkRangeLayout));
591 gtk_range_set_property (GObject *object,
596 GtkRange *range = GTK_RANGE (object);
600 case PROP_ORIENTATION:
601 range->orientation = g_value_get_enum (value);
603 range->layout->slider_detail_quark = 0;
604 range->layout->stepper_detail_quark = 0;
606 gtk_widget_queue_resize (GTK_WIDGET (range));
608 case PROP_UPDATE_POLICY:
609 gtk_range_set_update_policy (range, g_value_get_enum (value));
611 case PROP_ADJUSTMENT:
612 gtk_range_set_adjustment (range, g_value_get_object (value));
615 gtk_range_set_inverted (range, g_value_get_boolean (value));
617 case PROP_LOWER_STEPPER_SENSITIVITY:
618 gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
620 case PROP_UPPER_STEPPER_SENSITIVITY:
621 gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
623 case PROP_SHOW_FILL_LEVEL:
624 gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
626 case PROP_RESTRICT_TO_FILL_LEVEL:
627 gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
629 case PROP_FILL_LEVEL:
630 gtk_range_set_fill_level (range, g_value_get_double (value));
633 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
639 gtk_range_get_property (GObject *object,
644 GtkRange *range = GTK_RANGE (object);
648 case PROP_ORIENTATION:
649 g_value_set_enum (value, range->orientation);
651 case PROP_UPDATE_POLICY:
652 g_value_set_enum (value, range->update_policy);
654 case PROP_ADJUSTMENT:
655 g_value_set_object (value, range->adjustment);
658 g_value_set_boolean (value, range->inverted);
660 case PROP_LOWER_STEPPER_SENSITIVITY:
661 g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
663 case PROP_UPPER_STEPPER_SENSITIVITY:
664 g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
666 case PROP_SHOW_FILL_LEVEL:
667 g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
669 case PROP_RESTRICT_TO_FILL_LEVEL:
670 g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
672 case PROP_FILL_LEVEL:
673 g_value_set_double (value, gtk_range_get_fill_level (range));
676 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
682 gtk_range_init (GtkRange *range)
684 gtk_widget_set_has_window (GTK_WIDGET (range), FALSE);
686 range->orientation = GTK_ORIENTATION_HORIZONTAL;
687 range->adjustment = NULL;
688 range->update_policy = GTK_UPDATE_CONTINUOUS;
689 range->inverted = FALSE;
690 range->flippable = FALSE;
691 range->min_slider_size = 1;
692 range->has_stepper_a = FALSE;
693 range->has_stepper_b = FALSE;
694 range->has_stepper_c = FALSE;
695 range->has_stepper_d = FALSE;
696 range->need_recalc = TRUE;
697 range->round_digits = -1;
698 range->layout = GTK_RANGE_GET_PRIVATE (range);
699 range->layout->mouse_location = MOUSE_OUTSIDE;
700 range->layout->mouse_x = -1;
701 range->layout->mouse_y = -1;
702 range->layout->grab_location = MOUSE_OUTSIDE;
703 range->layout->grab_button = 0;
704 range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
705 range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
706 range->layout->lower_sensitive = TRUE;
707 range->layout->upper_sensitive = TRUE;
708 range->layout->show_fill_level = FALSE;
709 range->layout->restrict_to_fill_level = TRUE;
710 range->layout->fill_level = G_MAXDOUBLE;
715 * gtk_range_get_adjustment:
716 * @range: a #GtkRange
718 * Get the #GtkAdjustment which is the "model" object for #GtkRange.
719 * See gtk_range_set_adjustment() for details.
720 * The return value does not have a reference added, so should not
723 * Return value: a #GtkAdjustment
726 gtk_range_get_adjustment (GtkRange *range)
728 g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
730 if (!range->adjustment)
731 gtk_range_set_adjustment (range, NULL);
733 return range->adjustment;
737 * gtk_range_set_update_policy:
738 * @range: a #GtkRange
739 * @policy: update policy
741 * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
742 * anytime the range slider is moved, the range value will change and the
743 * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
744 * the value will be updated after a brief timeout where no slider motion
745 * occurs, so updates are spaced by a short time rather than
746 * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
747 * be updated when the user releases the button and ends the slider
751 gtk_range_set_update_policy (GtkRange *range,
752 GtkUpdateType policy)
754 g_return_if_fail (GTK_IS_RANGE (range));
756 if (range->update_policy != policy)
758 range->update_policy = policy;
759 g_object_notify (G_OBJECT (range), "update-policy");
764 * gtk_range_get_update_policy:
765 * @range: a #GtkRange
767 * Gets the update policy of @range. See gtk_range_set_update_policy().
769 * Return value: the current update policy
772 gtk_range_get_update_policy (GtkRange *range)
774 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
776 return range->update_policy;
780 * gtk_range_set_adjustment:
781 * @range: a #GtkRange
782 * @adjustment: a #GtkAdjustment
784 * Sets the adjustment to be used as the "model" object for this range
785 * widget. The adjustment indicates the current range value, the
786 * minimum and maximum range values, the step/page increments used
787 * for keybindings and scrolling, and the page size. The page size
788 * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
789 * indicates the size of the visible area of the widget being scrolled.
790 * The page size affects the size of the scrollbar slider.
793 gtk_range_set_adjustment (GtkRange *range,
794 GtkAdjustment *adjustment)
796 g_return_if_fail (GTK_IS_RANGE (range));
799 adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
801 g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
803 if (range->adjustment != adjustment)
805 if (range->adjustment)
807 g_signal_handlers_disconnect_by_func (range->adjustment,
808 gtk_range_adjustment_changed,
810 g_signal_handlers_disconnect_by_func (range->adjustment,
811 gtk_range_adjustment_value_changed,
813 g_object_unref (range->adjustment);
816 range->adjustment = adjustment;
817 g_object_ref_sink (adjustment);
819 g_signal_connect (adjustment, "changed",
820 G_CALLBACK (gtk_range_adjustment_changed),
822 g_signal_connect (adjustment, "value-changed",
823 G_CALLBACK (gtk_range_adjustment_value_changed),
826 gtk_range_adjustment_changed (adjustment, range);
827 g_object_notify (G_OBJECT (range), "adjustment");
832 * gtk_range_set_inverted:
833 * @range: a #GtkRange
834 * @setting: %TRUE to invert the range
836 * Ranges normally move from lower to higher values as the
837 * slider moves from top to bottom or left to right. Inverted
838 * ranges have higher values at the top or on the right rather than
839 * on the bottom or left.
842 gtk_range_set_inverted (GtkRange *range,
845 g_return_if_fail (GTK_IS_RANGE (range));
847 setting = setting != FALSE;
849 if (setting != range->inverted)
851 range->inverted = setting;
852 g_object_notify (G_OBJECT (range), "inverted");
853 gtk_widget_queue_resize (GTK_WIDGET (range));
858 * gtk_range_get_inverted:
859 * @range: a #GtkRange
861 * Gets the value set by gtk_range_set_inverted().
863 * Return value: %TRUE if the range is inverted
866 gtk_range_get_inverted (GtkRange *range)
868 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
870 return range->inverted;
874 * gtk_range_set_flippable:
875 * @range: a #GtkRange
876 * @flippable: %TRUE to make the range flippable
878 * If a range is flippable, it will switch its direction if it is
879 * horizontal and its direction is %GTK_TEXT_DIR_RTL.
881 * See gtk_widget_get_direction().
886 gtk_range_set_flippable (GtkRange *range,
889 g_return_if_fail (GTK_IS_RANGE (range));
891 flippable = flippable ? TRUE : FALSE;
893 if (flippable != range->flippable)
895 range->flippable = flippable;
897 gtk_widget_queue_draw (GTK_WIDGET (range));
902 * gtk_range_get_flippable:
903 * @range: a #GtkRange
905 * Gets the value set by gtk_range_set_flippable().
907 * Return value: %TRUE if the range is flippable
912 gtk_range_get_flippable (GtkRange *range)
914 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
916 return range->flippable;
920 * gtk_range_set_slider_size_fixed:
921 * @range: a #GtkRange
922 * @size_fixed: %TRUE to make the slider size constant
924 * Sets whether the range's slider has a fixed size, or a size that
925 * depends on it's adjustment's page size.
927 * This function is useful mainly for #GtkRange subclasses.
932 gtk_range_set_slider_size_fixed (GtkRange *range,
935 g_return_if_fail (GTK_IS_RANGE (range));
937 if (size_fixed != range->slider_size_fixed)
939 range->slider_size_fixed = size_fixed ? TRUE : FALSE;
941 range->need_recalc = TRUE;
942 gtk_range_calc_layout (range, range->adjustment->value);
943 gtk_widget_queue_draw (GTK_WIDGET (range));
948 * gtk_range_get_slider_size_fixed:
949 * @range: a #GtkRange
951 * This function is useful mainly for #GtkRange subclasses.
953 * See gtk_range_set_slider_size_fixed().
955 * Return value: whether the range's slider has a fixed size.
960 gtk_range_get_slider_size_fixed (GtkRange *range)
962 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
964 return range->slider_size_fixed;
968 * gtk_range_set_min_slider_size:
969 * @range: a #GtkRange
970 * @min_size: The slider's minimum size
972 * Sets the minimum size of the range's slider.
974 * This function is useful mainly for #GtkRange subclasses.
979 gtk_range_set_min_slider_size (GtkRange *range,
982 g_return_if_fail (GTK_IS_RANGE (range));
983 g_return_if_fail (min_size > 0);
985 if (min_size != range->min_slider_size)
987 range->min_slider_size = min_size;
989 range->need_recalc = TRUE;
990 gtk_range_calc_layout (range, range->adjustment->value);
991 gtk_widget_queue_draw (GTK_WIDGET (range));
996 * gtk_range_get_min_slider_size:
997 * @range: a #GtkRange
999 * This function is useful mainly for #GtkRange subclasses.
1001 * See gtk_range_set_min_slider_size().
1003 * Return value: The minimum size of the range's slider.
1008 gtk_range_get_min_slider_size (GtkRange *range)
1010 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1012 return range->min_slider_size;
1016 * gtk_range_get_range_rect:
1017 * @range: a #GtkRange
1018 * @range_rect: return location for the range rectangle
1020 * This function returns the area that contains the range's trough
1021 * and its steppers, in widget->window coordinates.
1023 * This function is useful mainly for #GtkRange subclasses.
1028 gtk_range_get_range_rect (GtkRange *range,
1029 GdkRectangle *range_rect)
1031 g_return_if_fail (GTK_IS_RANGE (range));
1032 g_return_if_fail (range_rect != NULL);
1034 gtk_range_calc_layout (range, range->adjustment->value);
1036 *range_rect = range->range_rect;
1040 * gtk_range_get_slider_range:
1041 * @range: a #GtkRange
1042 * @slider_start: (allow-none): return location for the slider's start, or %NULL
1043 * @slider_end: (allow-none): return location for the slider's end, or %NULL
1045 * This function returns sliders range along the long dimension,
1046 * in widget->window coordinates.
1048 * This function is useful mainly for #GtkRange subclasses.
1053 gtk_range_get_slider_range (GtkRange *range,
1057 g_return_if_fail (GTK_IS_RANGE (range));
1059 gtk_range_calc_layout (range, range->adjustment->value);
1062 *slider_start = range->slider_start;
1065 *slider_end = range->slider_end;
1069 * gtk_range_set_lower_stepper_sensitivity:
1070 * @range: a #GtkRange
1071 * @sensitivity: the lower stepper's sensitivity policy.
1073 * Sets the sensitivity policy for the stepper that points to the
1074 * 'lower' end of the GtkRange's adjustment.
1079 gtk_range_set_lower_stepper_sensitivity (GtkRange *range,
1080 GtkSensitivityType sensitivity)
1082 g_return_if_fail (GTK_IS_RANGE (range));
1084 if (range->layout->lower_sensitivity != sensitivity)
1086 range->layout->lower_sensitivity = sensitivity;
1088 range->need_recalc = TRUE;
1089 gtk_range_calc_layout (range, range->adjustment->value);
1090 gtk_widget_queue_draw (GTK_WIDGET (range));
1092 g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
1097 * gtk_range_get_lower_stepper_sensitivity:
1098 * @range: a #GtkRange
1100 * Gets the sensitivity policy for the stepper that points to the
1101 * 'lower' end of the GtkRange's adjustment.
1103 * Return value: The lower stepper's sensitivity policy.
1108 gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
1110 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1112 return range->layout->lower_sensitivity;
1116 * gtk_range_set_upper_stepper_sensitivity:
1117 * @range: a #GtkRange
1118 * @sensitivity: the upper stepper's sensitivity policy.
1120 * Sets the sensitivity policy for the stepper that points to the
1121 * 'upper' end of the GtkRange's adjustment.
1126 gtk_range_set_upper_stepper_sensitivity (GtkRange *range,
1127 GtkSensitivityType sensitivity)
1129 g_return_if_fail (GTK_IS_RANGE (range));
1131 if (range->layout->upper_sensitivity != sensitivity)
1133 range->layout->upper_sensitivity = sensitivity;
1135 range->need_recalc = TRUE;
1136 gtk_range_calc_layout (range, range->adjustment->value);
1137 gtk_widget_queue_draw (GTK_WIDGET (range));
1139 g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
1144 * gtk_range_get_upper_stepper_sensitivity:
1145 * @range: a #GtkRange
1147 * Gets the sensitivity policy for the stepper that points to the
1148 * 'upper' end of the GtkRange's adjustment.
1150 * Return value: The upper stepper's sensitivity policy.
1155 gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
1157 g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1159 return range->layout->upper_sensitivity;
1163 * gtk_range_set_increments:
1164 * @range: a #GtkRange
1168 * Sets the step and page sizes for the range.
1169 * The step size is used when the user clicks the #GtkScrollbar
1170 * arrows or moves #GtkScale via arrow keys. The page size
1171 * is used for example when moving via Page Up or Page Down keys.
1174 gtk_range_set_increments (GtkRange *range,
1178 g_return_if_fail (GTK_IS_RANGE (range));
1180 range->adjustment->step_increment = step;
1181 range->adjustment->page_increment = page;
1183 gtk_adjustment_changed (range->adjustment);
1187 * gtk_range_set_range:
1188 * @range: a #GtkRange
1189 * @min: minimum range value
1190 * @max: maximum range value
1192 * Sets the allowable values in the #GtkRange, and clamps the range
1193 * value to be between @min and @max. (If the range has a non-zero
1194 * page size, it is clamped between @min and @max - page-size.)
1197 gtk_range_set_range (GtkRange *range,
1203 g_return_if_fail (GTK_IS_RANGE (range));
1204 g_return_if_fail (min < max);
1206 range->adjustment->lower = min;
1207 range->adjustment->upper = max;
1209 value = range->adjustment->value;
1211 if (range->layout->restrict_to_fill_level)
1212 value = MIN (value, MAX (range->adjustment->lower,
1213 range->layout->fill_level));
1215 gtk_adjustment_set_value (range->adjustment, value);
1216 gtk_adjustment_changed (range->adjustment);
1220 * gtk_range_set_value:
1221 * @range: a #GtkRange
1222 * @value: new value of the range
1224 * Sets the current value of the range; if the value is outside the
1225 * minimum or maximum range values, it will be clamped to fit inside
1226 * them. The range emits the #GtkRange::value-changed signal if the
1230 gtk_range_set_value (GtkRange *range,
1233 g_return_if_fail (GTK_IS_RANGE (range));
1235 if (range->layout->restrict_to_fill_level)
1236 value = MIN (value, MAX (range->adjustment->lower,
1237 range->layout->fill_level));
1239 gtk_adjustment_set_value (range->adjustment, value);
1243 * gtk_range_get_value:
1244 * @range: a #GtkRange
1246 * Gets the current value of the range.
1248 * Return value: current value of the range.
1251 gtk_range_get_value (GtkRange *range)
1253 g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1255 return range->adjustment->value;
1259 * gtk_range_set_show_fill_level:
1260 * @range: A #GtkRange
1261 * @show_fill_level: Whether a fill level indicator graphics is shown.
1263 * Sets whether a graphical fill level is show on the trough. See
1264 * gtk_range_set_fill_level() for a general description of the fill
1270 gtk_range_set_show_fill_level (GtkRange *range,
1271 gboolean show_fill_level)
1273 g_return_if_fail (GTK_IS_RANGE (range));
1275 show_fill_level = show_fill_level ? TRUE : FALSE;
1277 if (show_fill_level != range->layout->show_fill_level)
1279 range->layout->show_fill_level = show_fill_level;
1280 g_object_notify (G_OBJECT (range), "show-fill-level");
1281 gtk_widget_queue_draw (GTK_WIDGET (range));
1286 * gtk_range_get_show_fill_level:
1287 * @range: A #GtkRange
1289 * Gets whether the range displays the fill level graphically.
1291 * Return value: %TRUE if @range shows the fill level.
1296 gtk_range_get_show_fill_level (GtkRange *range)
1298 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1300 return range->layout->show_fill_level;
1304 * gtk_range_set_restrict_to_fill_level:
1305 * @range: A #GtkRange
1306 * @restrict_to_fill_level: Whether the fill level restricts slider movement.
1308 * Sets whether the slider is restricted to the fill level. See
1309 * gtk_range_set_fill_level() for a general description of the fill
1315 gtk_range_set_restrict_to_fill_level (GtkRange *range,
1316 gboolean restrict_to_fill_level)
1318 g_return_if_fail (GTK_IS_RANGE (range));
1320 restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
1322 if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
1324 range->layout->restrict_to_fill_level = restrict_to_fill_level;
1325 g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
1327 gtk_range_set_value (range, gtk_range_get_value (range));
1332 * gtk_range_get_restrict_to_fill_level:
1333 * @range: A #GtkRange
1335 * Gets whether the range is restricted to the fill level.
1337 * Return value: %TRUE if @range is restricted to the fill level.
1342 gtk_range_get_restrict_to_fill_level (GtkRange *range)
1344 g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1346 return range->layout->restrict_to_fill_level;
1350 * gtk_range_set_fill_level:
1351 * @range: a #GtkRange
1352 * @fill_level: the new position of the fill level indicator
1354 * Set the new position of the fill level indicator.
1356 * The "fill level" is probably best described by its most prominent
1357 * use case, which is an indicator for the amount of pre-buffering in
1358 * a streaming media player. In that use case, the value of the range
1359 * would indicate the current play position, and the fill level would
1360 * be the position up to which the file/stream has been downloaded.
1362 * This amount of prebuffering can be displayed on the range's trough
1363 * and is themeable separately from the trough. To enable fill level
1364 * display, use gtk_range_set_show_fill_level(). The range defaults
1365 * to not showing the fill level.
1367 * Additionally, it's possible to restrict the range's slider position
1368 * to values which are smaller than the fill level. This is controller
1369 * by gtk_range_set_restrict_to_fill_level() and is by default
1375 gtk_range_set_fill_level (GtkRange *range,
1378 g_return_if_fail (GTK_IS_RANGE (range));
1380 if (fill_level != range->layout->fill_level)
1382 range->layout->fill_level = fill_level;
1383 g_object_notify (G_OBJECT (range), "fill-level");
1385 if (range->layout->show_fill_level)
1386 gtk_widget_queue_draw (GTK_WIDGET (range));
1388 if (range->layout->restrict_to_fill_level)
1389 gtk_range_set_value (range, gtk_range_get_value (range));
1394 * gtk_range_get_fill_level:
1395 * @range : A #GtkRange
1397 * Gets the current position of the fill level indicator.
1399 * Return value: The current fill level
1404 gtk_range_get_fill_level (GtkRange *range)
1406 g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1408 return range->layout->fill_level;
1412 should_invert (GtkRange *range)
1414 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1416 (range->inverted && !range->flippable) ||
1417 (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
1418 (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1420 return range->inverted;
1424 gtk_range_destroy (GtkObject *object)
1426 GtkRange *range = GTK_RANGE (object);
1428 gtk_range_remove_step_timer (range);
1429 gtk_range_remove_update_timer (range);
1431 if (range->layout->repaint_id)
1432 g_source_remove (range->layout->repaint_id);
1433 range->layout->repaint_id = 0;
1435 if (range->adjustment)
1437 g_signal_handlers_disconnect_by_func (range->adjustment,
1438 gtk_range_adjustment_changed,
1440 g_signal_handlers_disconnect_by_func (range->adjustment,
1441 gtk_range_adjustment_value_changed,
1443 g_object_unref (range->adjustment);
1444 range->adjustment = NULL;
1447 if (range->layout->n_marks)
1449 g_free (range->layout->marks);
1450 range->layout->marks = NULL;
1451 g_free (range->layout->mark_pos);
1452 range->layout->mark_pos = NULL;
1453 range->layout->n_marks = 0;
1456 GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
1460 gtk_range_size_request (GtkWidget *widget,
1461 GtkRequisition *requisition)
1464 gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1465 GdkRectangle range_rect;
1468 range = GTK_RANGE (widget);
1470 gtk_range_get_props (range,
1471 &slider_width, &stepper_size,
1472 &focus_width, &trough_border,
1473 &stepper_spacing, NULL,
1476 gtk_range_calc_request (range,
1477 slider_width, stepper_size,
1478 focus_width, trough_border, stepper_spacing,
1479 &range_rect, &border, NULL, NULL, NULL, NULL);
1481 requisition->width = range_rect.width + border.left + border.right;
1482 requisition->height = range_rect.height + border.top + border.bottom;
1486 gtk_range_size_allocate (GtkWidget *widget,
1487 GtkAllocation *allocation)
1491 range = GTK_RANGE (widget);
1493 widget->allocation = *allocation;
1495 range->layout->recalc_marks = TRUE;
1497 range->need_recalc = TRUE;
1498 gtk_range_calc_layout (range, range->adjustment->value);
1500 if (gtk_widget_get_realized (widget))
1501 gdk_window_move_resize (range->event_window,
1502 widget->allocation.x,
1503 widget->allocation.y,
1504 widget->allocation.width,
1505 widget->allocation.height);
1509 gtk_range_realize (GtkWidget *widget)
1512 GdkWindowAttr attributes;
1513 gint attributes_mask;
1515 range = GTK_RANGE (widget);
1517 gtk_range_calc_layout (range, range->adjustment->value);
1519 gtk_widget_set_realized (widget, TRUE);
1521 widget->window = gtk_widget_get_parent_window (widget);
1522 g_object_ref (widget->window);
1524 attributes.window_type = GDK_WINDOW_CHILD;
1525 attributes.x = widget->allocation.x;
1526 attributes.y = widget->allocation.y;
1527 attributes.width = widget->allocation.width;
1528 attributes.height = widget->allocation.height;
1529 attributes.wclass = GDK_INPUT_ONLY;
1530 attributes.event_mask = gtk_widget_get_events (widget);
1531 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1532 GDK_BUTTON_RELEASE_MASK |
1533 GDK_ENTER_NOTIFY_MASK |
1534 GDK_LEAVE_NOTIFY_MASK |
1535 GDK_POINTER_MOTION_MASK |
1536 GDK_POINTER_MOTION_HINT_MASK);
1538 attributes_mask = GDK_WA_X | GDK_WA_Y;
1540 range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1541 &attributes, attributes_mask);
1542 gdk_window_set_user_data (range->event_window, range);
1544 widget->style = gtk_style_attach (widget->style, widget->window);
1548 gtk_range_unrealize (GtkWidget *widget)
1550 GtkRange *range = GTK_RANGE (widget);
1552 gtk_range_remove_step_timer (range);
1553 gtk_range_remove_update_timer (range);
1555 gdk_window_set_user_data (range->event_window, NULL);
1556 gdk_window_destroy (range->event_window);
1557 range->event_window = NULL;
1559 GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1563 gtk_range_map (GtkWidget *widget)
1565 GtkRange *range = GTK_RANGE (widget);
1567 gdk_window_show (range->event_window);
1569 GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1573 gtk_range_unmap (GtkWidget *widget)
1575 GtkRange *range = GTK_RANGE (widget);
1577 stop_scrolling (range);
1579 gdk_window_hide (range->event_window);
1581 GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1584 static const gchar *
1585 gtk_range_get_slider_detail (GtkRange *range)
1587 const gchar *slider_detail;
1589 if (range->layout->slider_detail_quark)
1590 return g_quark_to_string (range->layout->slider_detail_quark);
1592 slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
1594 if (slider_detail && slider_detail[0] == 'X')
1596 gchar *detail = g_strdup (slider_detail);
1598 detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1600 range->layout->slider_detail_quark = g_quark_from_string (detail);
1604 return g_quark_to_string (range->layout->slider_detail_quark);
1607 return slider_detail;
1610 static const gchar *
1611 gtk_range_get_stepper_detail (GtkRange *range)
1613 const gchar *stepper_detail;
1615 if (range->layout->stepper_detail_quark)
1616 return g_quark_to_string (range->layout->stepper_detail_quark);
1618 stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
1620 if (stepper_detail && stepper_detail[0] == 'X')
1622 gchar *detail = g_strdup (stepper_detail);
1624 detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1626 range->layout->stepper_detail_quark = g_quark_from_string (detail);
1630 return g_quark_to_string (range->layout->stepper_detail_quark);
1633 return stepper_detail;
1637 draw_stepper (GtkRange *range,
1639 GtkArrowType arrow_type,
1641 gboolean prelighted,
1644 GtkStateType state_type;
1645 GtkShadowType shadow_type;
1646 GdkRectangle intersection;
1647 GtkWidget *widget = GTK_WIDGET (range);
1648 gfloat arrow_scaling;
1655 gboolean arrow_sensitive = TRUE;
1657 /* More to get the right clip region than for efficiency */
1658 if (!gdk_rectangle_intersect (area, rect, &intersection))
1661 intersection.x += widget->allocation.x;
1662 intersection.y += widget->allocation.y;
1664 if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1665 arrow_type == GTK_ARROW_RIGHT)) ||
1666 (range->inverted && (arrow_type == GTK_ARROW_UP ||
1667 arrow_type == GTK_ARROW_LEFT)))
1669 arrow_sensitive = range->layout->upper_sensitive;
1673 arrow_sensitive = range->layout->lower_sensitive;
1676 if (!gtk_widget_is_sensitive (GTK_WIDGET (range)) || !arrow_sensitive)
1677 state_type = GTK_STATE_INSENSITIVE;
1679 state_type = GTK_STATE_ACTIVE;
1680 else if (prelighted)
1681 state_type = GTK_STATE_PRELIGHT;
1683 state_type = GTK_STATE_NORMAL;
1685 if (clicked && arrow_sensitive)
1686 shadow_type = GTK_SHADOW_IN;
1688 shadow_type = GTK_SHADOW_OUT;
1690 gtk_paint_box (widget->style,
1692 state_type, shadow_type,
1693 &intersection, widget,
1694 gtk_range_get_stepper_detail (range),
1695 widget->allocation.x + rect->x,
1696 widget->allocation.y + rect->y,
1700 gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1702 arrow_width = rect->width * arrow_scaling;
1703 arrow_height = rect->height * arrow_scaling;
1704 arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1705 arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1707 if (clicked && arrow_sensitive)
1709 gint arrow_displacement_x;
1710 gint arrow_displacement_y;
1712 gtk_range_get_props (GTK_RANGE (widget),
1713 NULL, NULL, NULL, NULL, NULL, NULL,
1714 &arrow_displacement_x, &arrow_displacement_y);
1716 arrow_x += arrow_displacement_x;
1717 arrow_y += arrow_displacement_y;
1720 gtk_paint_arrow (widget->style,
1722 state_type, shadow_type,
1723 &intersection, widget,
1724 gtk_range_get_stepper_detail (range),
1727 arrow_x, arrow_y, arrow_width, arrow_height);
1731 gtk_range_expose (GtkWidget *widget,
1732 GdkEventExpose *event)
1734 GtkRange *range = GTK_RANGE (widget);
1737 GtkShadowType shadow_type;
1738 GdkRectangle expose_area; /* Relative to widget->allocation */
1740 gint focus_line_width = 0;
1741 gint focus_padding = 0;
1742 gboolean touchscreen;
1744 g_object_get (gtk_widget_get_settings (widget),
1745 "gtk-touchscreen-mode", &touchscreen,
1747 if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
1748 gtk_widget_style_get (GTK_WIDGET (range),
1749 "focus-line-width", &focus_line_width,
1750 "focus-padding", &focus_padding,
1753 /* we're now exposing, so there's no need to force early repaints */
1754 if (range->layout->repaint_id)
1755 g_source_remove (range->layout->repaint_id);
1756 range->layout->repaint_id = 0;
1758 expose_area = event->area;
1759 expose_area.x -= widget->allocation.x;
1760 expose_area.y -= widget->allocation.y;
1762 gtk_range_calc_marks (range);
1763 gtk_range_calc_layout (range, range->adjustment->value);
1765 sensitive = gtk_widget_is_sensitive (widget);
1767 /* Just to be confusing, we draw the trough for the whole
1768 * range rectangle, not the trough rectangle (the trough
1769 * rectangle is just for hit detection)
1771 /* The gdk_rectangle_intersect is more to get the right
1772 * clip region (limited to range_rect) than for efficiency
1774 if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1777 gint x = (widget->allocation.x + range->range_rect.x +
1778 focus_line_width + focus_padding);
1779 gint y = (widget->allocation.y + range->range_rect.y +
1780 focus_line_width + focus_padding);
1781 gint width = (range->range_rect.width -
1782 2 * (focus_line_width + focus_padding));
1783 gint height = (range->range_rect.height -
1784 2 * (focus_line_width + focus_padding));
1785 gboolean trough_side_details;
1786 gboolean trough_under_steppers;
1788 gint stepper_spacing;
1790 area.x += widget->allocation.x;
1791 area.y += widget->allocation.y;
1793 gtk_widget_style_get (GTK_WIDGET (range),
1794 "trough-side-details", &trough_side_details,
1795 "trough-under-steppers", &trough_under_steppers,
1796 "stepper-size", &stepper_size,
1797 "stepper-spacing", &stepper_spacing,
1800 if (stepper_spacing > 0)
1801 trough_under_steppers = FALSE;
1803 if (! trough_under_steppers)
1808 if (range->has_stepper_a)
1809 offset += stepper_size;
1811 if (range->has_stepper_b)
1812 offset += stepper_size;
1816 if (range->has_stepper_c)
1817 shorter += stepper_size;
1819 if (range->has_stepper_d)
1820 shorter += stepper_size;
1822 if (range->has_stepper_a || range->has_stepper_b)
1824 offset += stepper_spacing;
1825 shorter += stepper_spacing;
1828 if (range->has_stepper_c || range->has_stepper_d)
1830 shorter += stepper_spacing;
1833 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1845 if (! trough_side_details)
1847 gtk_paint_box (widget->style,
1849 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1851 &area, GTK_WIDGET(range), "trough",
1857 gint trough_change_pos_x = width;
1858 gint trough_change_pos_y = height;
1860 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1861 trough_change_pos_x = (range->layout->slider.x +
1862 range->layout->slider.width / 2 -
1863 (x - widget->allocation.x));
1865 trough_change_pos_y = (range->layout->slider.y +
1866 range->layout->slider.height / 2 -
1867 (y - widget->allocation.y));
1869 gtk_paint_box (widget->style,
1871 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1873 &area, GTK_WIDGET (range),
1874 should_invert (range) ? "trough-upper" : "trough-lower",
1876 trough_change_pos_x, trough_change_pos_y);
1878 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1879 trough_change_pos_y = 0;
1881 trough_change_pos_x = 0;
1883 gtk_paint_box (widget->style,
1885 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1887 &area, GTK_WIDGET (range),
1888 should_invert (range) ? "trough-lower" : "trough-upper",
1889 x + trough_change_pos_x, y + trough_change_pos_y,
1890 width - trough_change_pos_x,
1891 height - trough_change_pos_y);
1894 if (range->layout->show_fill_level &&
1895 range->adjustment->upper - range->adjustment->page_size -
1896 range->adjustment->lower != 0)
1898 gdouble fill_level = range->layout->fill_level;
1901 gint fill_width = width;
1902 gint fill_height = height;
1905 fill_level = CLAMP (fill_level, range->adjustment->lower,
1906 range->adjustment->upper -
1907 range->adjustment->page_size);
1909 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1911 fill_x = widget->allocation.x + range->layout->trough.x;
1912 fill_width = (range->layout->slider.width +
1913 (fill_level - range->adjustment->lower) /
1914 (range->adjustment->upper -
1915 range->adjustment->lower -
1916 range->adjustment->page_size) *
1917 (range->layout->trough.width -
1918 range->layout->slider.width));
1920 if (should_invert (range))
1921 fill_x += range->layout->trough.width - fill_width;
1925 fill_y = widget->allocation.y + range->layout->trough.y;
1926 fill_height = (range->layout->slider.height +
1927 (fill_level - range->adjustment->lower) /
1928 (range->adjustment->upper -
1929 range->adjustment->lower -
1930 range->adjustment->page_size) *
1931 (range->layout->trough.height -
1932 range->layout->slider.height));
1934 if (should_invert (range))
1935 fill_y += range->layout->trough.height - fill_height;
1938 if (fill_level < range->adjustment->upper - range->adjustment->page_size)
1939 fill_detail = "trough-fill-level-full";
1941 fill_detail = "trough-fill-level";
1943 gtk_paint_box (widget->style,
1945 sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1947 &area, GTK_WIDGET (range), fill_detail,
1949 fill_width, fill_height);
1952 if (sensitive && gtk_widget_has_focus (widget))
1953 gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
1954 &area, widget, "trough",
1955 widget->allocation.x + range->range_rect.x,
1956 widget->allocation.y + range->range_rect.y,
1957 range->range_rect.width,
1958 range->range_rect.height);
1961 shadow_type = GTK_SHADOW_OUT;
1964 state = GTK_STATE_INSENSITIVE;
1965 else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
1966 state = GTK_STATE_PRELIGHT;
1968 state = GTK_STATE_NORMAL;
1970 if (range->layout->grab_location == MOUSE_SLIDER)
1972 gboolean activate_slider;
1974 gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
1976 if (activate_slider)
1978 state = GTK_STATE_ACTIVE;
1979 shadow_type = GTK_SHADOW_IN;
1983 if (gdk_rectangle_intersect (&expose_area,
1984 &range->layout->slider,
1987 area.x += widget->allocation.x;
1988 area.y += widget->allocation.y;
1990 gtk_paint_slider (widget->style,
1996 gtk_range_get_slider_detail (range),
1997 widget->allocation.x + range->layout->slider.x,
1998 widget->allocation.y + range->layout->slider.y,
1999 range->layout->slider.width,
2000 range->layout->slider.height,
2001 range->orientation);
2004 if (range->has_stepper_a)
2005 draw_stepper (range, &range->layout->stepper_a,
2006 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2007 range->layout->grab_location == MOUSE_STEPPER_A,
2008 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
2011 if (range->has_stepper_b)
2012 draw_stepper (range, &range->layout->stepper_b,
2013 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2014 range->layout->grab_location == MOUSE_STEPPER_B,
2015 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
2018 if (range->has_stepper_c)
2019 draw_stepper (range, &range->layout->stepper_c,
2020 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2021 range->layout->grab_location == MOUSE_STEPPER_C,
2022 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
2025 if (range->has_stepper_d)
2026 draw_stepper (range, &range->layout->stepper_d,
2027 range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2028 range->layout->grab_location == MOUSE_STEPPER_D,
2029 !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
2036 range_grab_add (GtkRange *range,
2038 MouseLocation location,
2041 GtkRangeLayout *layout = range->layout;
2043 if (device == layout->grab_device)
2046 if (layout->grab_device != NULL)
2048 g_warning ("GtkRange already had a grab device, releasing device grab");
2049 gtk_device_grab_remove (GTK_WIDGET (range), layout->grab_device);
2052 /* we don't actually gdk_grab, since a button is down */
2053 gtk_device_grab_add (GTK_WIDGET (range), device, TRUE);
2055 range->layout->grab_location = location;
2056 range->layout->grab_button = button;
2057 range->layout->grab_device = device;
2059 if (gtk_range_update_mouse_location (range))
2060 gtk_widget_queue_draw (GTK_WIDGET (range));
2064 range_grab_remove (GtkRange *range)
2066 GtkRangeLayout *layout = range->layout;
2067 MouseLocation location;
2069 if (layout->grab_device)
2071 gtk_device_grab_remove (GTK_WIDGET (range),
2072 layout->grab_device);
2073 layout->grab_device = NULL;
2076 location = range->layout->grab_location;
2077 range->layout->grab_location = MOUSE_OUTSIDE;
2078 range->layout->grab_button = 0;
2080 if (gtk_range_update_mouse_location (range) ||
2081 location != MOUSE_OUTSIDE)
2082 gtk_widget_queue_draw (GTK_WIDGET (range));
2085 static GtkScrollType
2086 range_get_scroll_for_grab (GtkRange *range)
2090 invert = should_invert (range);
2091 switch (range->layout->grab_location)
2093 /* Backward stepper */
2094 case MOUSE_STEPPER_A:
2095 case MOUSE_STEPPER_C:
2096 switch (range->layout->grab_button)
2099 return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
2102 return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
2105 return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
2110 /* Forward stepper */
2111 case MOUSE_STEPPER_B:
2112 case MOUSE_STEPPER_D:
2113 switch (range->layout->grab_button)
2116 return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
2119 return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
2122 return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
2130 if (range->trough_click_forward)
2131 return GTK_SCROLL_PAGE_FORWARD;
2133 return GTK_SCROLL_PAGE_BACKWARD;
2143 return GTK_SCROLL_NONE;
2147 coord_to_value (GtkRange *range,
2156 gint trough_under_steppers;
2158 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2160 trough_length = range->layout->trough.height;
2161 trough_start = range->layout->trough.y;
2162 slider_length = range->layout->slider.height;
2166 trough_length = range->layout->trough.width;
2167 trough_start = range->layout->trough.x;
2168 slider_length = range->layout->slider.width;
2171 gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
2172 &trough_under_steppers, NULL, NULL);
2174 if (! trough_under_steppers)
2176 trough_start += trough_border;
2177 trough_length -= 2 * trough_border;
2180 if (trough_length == slider_length)
2183 frac = (MAX (0, coord - trough_start) /
2184 (gdouble) (trough_length - slider_length));
2186 if (should_invert (range))
2189 value = range->adjustment->lower + frac * (range->adjustment->upper -
2190 range->adjustment->lower -
2191 range->adjustment->page_size);
2197 gtk_range_key_press (GtkWidget *widget,
2201 GtkRange *range = GTK_RANGE (widget);
2202 GtkRangeLayout *layout = range->layout;
2204 device = gdk_event_get_device ((GdkEvent *) event);
2205 device = gdk_device_get_associated_device (device);
2207 if (device == layout->grab_device &&
2208 event->keyval == GDK_Escape &&
2209 range->layout->grab_location != MOUSE_OUTSIDE)
2211 stop_scrolling (range);
2213 update_slider_position (range,
2214 range->slide_initial_coordinate,
2215 range->slide_initial_coordinate);
2220 return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
2224 gtk_range_button_press (GtkWidget *widget,
2225 GdkEventButton *event)
2227 GtkRange *range = GTK_RANGE (widget);
2230 if (!gtk_widget_has_focus (widget))
2231 gtk_widget_grab_focus (widget);
2233 /* ignore presses when we're already doing something else. */
2234 if (range->layout->grab_location != MOUSE_OUTSIDE)
2237 device = gdk_event_get_device ((GdkEvent *) event);
2238 range->layout->mouse_x = event->x;
2239 range->layout->mouse_y = event->y;
2241 if (gtk_range_update_mouse_location (range))
2242 gtk_widget_queue_draw (widget);
2244 if (range->layout->mouse_location == MOUSE_TROUGH &&
2247 /* button 1 steps by page increment, as with button 2 on a stepper
2249 GtkScrollType scroll;
2250 gdouble click_value;
2252 click_value = coord_to_value (range,
2253 range->orientation == GTK_ORIENTATION_VERTICAL ?
2254 event->y : event->x);
2256 range->trough_click_forward = click_value > range->adjustment->value;
2257 range_grab_add (range, device, MOUSE_TROUGH, event->button);
2259 scroll = range_get_scroll_for_grab (range);
2261 gtk_range_add_step_timer (range, scroll);
2265 else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
2266 range->layout->mouse_location == MOUSE_STEPPER_B ||
2267 range->layout->mouse_location == MOUSE_STEPPER_C ||
2268 range->layout->mouse_location == MOUSE_STEPPER_D) &&
2269 (event->button == 1 || event->button == 2 || event->button == 3))
2271 GdkRectangle *stepper_area;
2272 GtkScrollType scroll;
2274 range_grab_add (range, device, range->layout->mouse_location, event->button);
2276 stepper_area = get_area (range, range->layout->mouse_location);
2277 gtk_widget_queue_draw_area (widget,
2278 widget->allocation.x + stepper_area->x,
2279 widget->allocation.y + stepper_area->y,
2280 stepper_area->width,
2281 stepper_area->height);
2283 scroll = range_get_scroll_for_grab (range);
2284 if (scroll != GTK_SCROLL_NONE)
2285 gtk_range_add_step_timer (range, scroll);
2289 else if ((range->layout->mouse_location == MOUSE_TROUGH &&
2290 event->button == 2) ||
2291 range->layout->mouse_location == MOUSE_SLIDER)
2293 gboolean need_value_update = FALSE;
2294 gboolean activate_slider;
2296 /* Any button can be used to drag the slider, but you can start
2297 * dragging the slider with a trough click using button 2;
2298 * On button 2 press, we warp the slider to mouse position,
2299 * then begin the slider drag.
2301 if (event->button == 2)
2303 gdouble slider_low_value, slider_high_value, new_value;
2306 coord_to_value (range,
2307 range->orientation == GTK_ORIENTATION_VERTICAL ?
2308 event->y : event->x);
2310 coord_to_value (range,
2311 range->orientation == GTK_ORIENTATION_VERTICAL ?
2312 event->y - range->layout->slider.height :
2313 event->x - range->layout->slider.width);
2315 /* compute new value for warped slider */
2316 new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
2318 /* recalc slider, so we can set slide_initial_slider_position
2321 range->need_recalc = TRUE;
2322 gtk_range_calc_layout (range, new_value);
2324 /* defer adjustment updates to update_slider_position() in order
2325 * to keep pixel quantisation
2327 need_value_update = TRUE;
2330 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2332 range->slide_initial_slider_position = range->layout->slider.y;
2333 range->slide_initial_coordinate = event->y;
2337 range->slide_initial_slider_position = range->layout->slider.x;
2338 range->slide_initial_coordinate = event->x;
2341 range_grab_add (range, device, MOUSE_SLIDER, event->button);
2343 gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2345 /* force a redraw, if the active slider is drawn differently to the
2348 if (activate_slider)
2349 gtk_widget_queue_draw (widget);
2351 if (need_value_update)
2352 update_slider_position (range, event->x, event->y);
2360 /* During a slide, move the slider as required given new mouse position */
2362 update_slider_position (GtkRange *range,
2375 if (range->orientation == GTK_ORIENTATION_VERTICAL)
2376 delta = mouse_y - range->slide_initial_coordinate;
2378 delta = mouse_x - range->slide_initial_coordinate;
2380 c = range->slide_initial_slider_position + delta;
2382 new_value = coord_to_value (range, c);
2383 next_value = coord_to_value (range, c + 1);
2384 mark_delta = fabs (next_value - new_value);
2386 for (i = 0; i < range->layout->n_marks; i++)
2388 mark_value = range->layout->marks[i];
2390 if (fabs (range->adjustment->value - mark_value) < 3 * mark_delta)
2392 if (fabs (new_value - mark_value) < (range->slider_end - range->slider_start) * 0.5 * mark_delta)
2394 new_value = mark_value;
2400 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2405 stop_scrolling (GtkRange *range)
2407 range_grab_remove (range);
2408 gtk_range_remove_step_timer (range);
2409 /* Flush any pending discontinuous/delayed updates */
2410 gtk_range_update_value (range);
2414 gtk_range_grab_broken (GtkWidget *widget,
2415 GdkEventGrabBroken *event)
2417 GtkRange *range = GTK_RANGE (widget);
2420 device = gdk_event_get_device ((GdkEvent *) event);
2422 if (device == range->layout->grab_device &&
2423 range->layout->grab_location != MOUSE_OUTSIDE)
2425 if (range->layout->grab_location == MOUSE_SLIDER)
2426 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2428 stop_scrolling (range);
2437 gtk_range_button_release (GtkWidget *widget,
2438 GdkEventButton *event)
2440 GtkRange *range = GTK_RANGE (widget);
2443 if (event->window == range->event_window)
2445 range->layout->mouse_x = event->x;
2446 range->layout->mouse_y = event->y;
2450 gdk_window_get_device_position (range->event_window,
2452 &range->layout->mouse_x,
2453 &range->layout->mouse_y,
2457 device = gdk_event_get_device ((GdkEvent *) event);
2459 if (range->layout->grab_device == device &&
2460 range->layout->grab_button == event->button)
2462 if (range->layout->grab_location == MOUSE_SLIDER)
2463 update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2465 stop_scrolling (range);
2474 * _gtk_range_get_wheel_delta:
2475 * @range: a #GtkRange
2476 * @direction: A #GdkScrollDirection
2478 * Returns a good step value for the mouse wheel.
2480 * Return value: A good step value for the mouse wheel.
2485 _gtk_range_get_wheel_delta (GtkRange *range,
2486 GdkScrollDirection direction)
2488 GtkAdjustment *adj = range->adjustment;
2491 if (GTK_IS_SCROLLBAR (range))
2492 delta = pow (adj->page_size, 2.0 / 3.0);
2494 delta = adj->step_increment * 2;
2496 if (direction == GDK_SCROLL_UP ||
2497 direction == GDK_SCROLL_LEFT)
2500 if (range->inverted)
2507 gtk_range_scroll_event (GtkWidget *widget,
2508 GdkEventScroll *event)
2510 GtkRange *range = GTK_RANGE (widget);
2512 if (gtk_widget_get_realized (widget))
2514 GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2518 delta = _gtk_range_get_wheel_delta (range, event->direction);
2520 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2521 GTK_SCROLL_JUMP, adj->value + delta,
2524 /* Policy DELAYED makes sense with scroll events,
2525 * but DISCONTINUOUS doesn't, so we update immediately
2528 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2529 gtk_range_update_value (range);
2536 gtk_range_motion_notify (GtkWidget *widget,
2537 GdkEventMotion *event)
2541 range = GTK_RANGE (widget);
2543 gdk_event_request_motions (event);
2545 range->layout->mouse_x = event->x;
2546 range->layout->mouse_y = event->y;
2548 if (gtk_range_update_mouse_location (range))
2549 gtk_widget_queue_draw (widget);
2551 if (range->layout->grab_location == MOUSE_SLIDER)
2552 update_slider_position (range, event->x, event->y);
2554 /* We handled the event if the mouse was in the range_rect */
2555 return range->layout->mouse_location != MOUSE_OUTSIDE;
2559 gtk_range_enter_notify (GtkWidget *widget,
2560 GdkEventCrossing *event)
2562 GtkRange *range = GTK_RANGE (widget);
2564 range->layout->mouse_x = event->x;
2565 range->layout->mouse_y = event->y;
2567 if (gtk_range_update_mouse_location (range))
2568 gtk_widget_queue_draw (widget);
2574 gtk_range_leave_notify (GtkWidget *widget,
2575 GdkEventCrossing *event)
2577 GtkRange *range = GTK_RANGE (widget);
2579 range->layout->mouse_x = -1;
2580 range->layout->mouse_y = -1;
2582 if (gtk_range_update_mouse_location (range))
2583 gtk_widget_queue_draw (widget);
2589 gtk_range_grab_notify (GtkWidget *widget,
2590 gboolean was_grabbed)
2592 GtkRangeLayout *layout = GTK_RANGE (widget)->layout;
2594 if (layout->grab_device &&
2595 gtk_widget_device_is_shadowed (widget, layout->grab_device))
2596 stop_scrolling (GTK_RANGE (widget));
2600 gtk_range_state_changed (GtkWidget *widget,
2601 GtkStateType previous_state)
2603 if (!gtk_widget_is_sensitive (widget))
2604 stop_scrolling (GTK_RANGE (widget));
2607 #define check_rectangle(rectangle1, rectangle2) \
2609 if (rectangle1.x != rectangle2.x) return TRUE; \
2610 if (rectangle1.y != rectangle2.y) return TRUE; \
2611 if (rectangle1.width != rectangle2.width) return TRUE; \
2612 if (rectangle1.height != rectangle2.height) return TRUE; \
2616 layout_changed (GtkRangeLayout *layout1,
2617 GtkRangeLayout *layout2)
2619 check_rectangle (layout1->slider, layout2->slider);
2620 check_rectangle (layout1->trough, layout2->trough);
2621 check_rectangle (layout1->stepper_a, layout2->stepper_a);
2622 check_rectangle (layout1->stepper_d, layout2->stepper_d);
2623 check_rectangle (layout1->stepper_b, layout2->stepper_b);
2624 check_rectangle (layout1->stepper_c, layout2->stepper_c);
2626 if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2627 if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2633 gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2636 GtkRange *range = GTK_RANGE (data);
2637 /* create a copy of the layout */
2638 GtkRangeLayout layout = *range->layout;
2640 range->layout->recalc_marks = TRUE;
2641 range->need_recalc = TRUE;
2642 gtk_range_calc_layout (range, range->adjustment->value);
2644 /* now check whether the layout changed */
2645 if (layout_changed (range->layout, &layout))
2646 gtk_widget_queue_draw (GTK_WIDGET (range));
2648 /* Note that we don't round off to range->round_digits here.
2649 * that's because it's really broken to change a value
2650 * in response to a change signal on that value; round_digits
2651 * is therefore defined to be a filter on what the GtkRange
2652 * can input into the adjustment, not a filter that the GtkRange
2653 * will enforce on the adjustment.
2658 force_repaint (gpointer data)
2660 GtkRange *range = GTK_RANGE (data);
2662 range->layout->repaint_id = 0;
2663 if (gtk_widget_is_drawable (GTK_WIDGET (range)))
2664 gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2670 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2673 GtkRange *range = GTK_RANGE (data);
2674 /* create a copy of the layout */
2675 GtkRangeLayout layout = *range->layout;
2677 range->need_recalc = TRUE;
2678 gtk_range_calc_layout (range, range->adjustment->value);
2680 /* now check whether the layout changed */
2681 if (layout_changed (range->layout, &layout) ||
2682 (GTK_IS_SCALE (range) && GTK_SCALE (range)->draw_value))
2684 gtk_widget_queue_draw (GTK_WIDGET (range));
2685 /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2686 if (!range->layout->repaint_id)
2687 range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
2690 /* Note that we don't round off to range->round_digits here.
2691 * that's because it's really broken to change a value
2692 * in response to a change signal on that value; round_digits
2693 * is therefore defined to be a filter on what the GtkRange
2694 * can input into the adjustment, not a filter that the GtkRange
2695 * will enforce on the adjustment.
2698 g_signal_emit (range, signals[VALUE_CHANGED], 0);
2702 gtk_range_style_set (GtkWidget *widget,
2703 GtkStyle *previous_style)
2705 GtkRange *range = GTK_RANGE (widget);
2707 range->need_recalc = TRUE;
2709 GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
2713 apply_marks (GtkRange *range,
2720 for (i = 0; i < range->layout->n_marks; i++)
2722 mark = range->layout->marks[i];
2723 if ((oldval < mark && mark < *newval) ||
2724 (oldval > mark && mark > *newval))
2733 step_back (GtkRange *range)
2738 newval = range->adjustment->value - range->adjustment->step_increment;
2739 apply_marks (range, range->adjustment->value, &newval);
2740 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2741 GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2745 step_forward (GtkRange *range)
2750 newval = range->adjustment->value + range->adjustment->step_increment;
2751 apply_marks (range, range->adjustment->value, &newval);
2752 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2753 GTK_SCROLL_STEP_FORWARD, newval, &handled);
2758 page_back (GtkRange *range)
2763 newval = range->adjustment->value - range->adjustment->page_increment;
2764 apply_marks (range, range->adjustment->value, &newval);
2765 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2766 GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2770 page_forward (GtkRange *range)
2775 newval = range->adjustment->value + range->adjustment->page_increment;
2776 apply_marks (range, range->adjustment->value, &newval);
2777 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2778 GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2782 scroll_begin (GtkRange *range)
2785 g_signal_emit (range, signals[CHANGE_VALUE], 0,
2786 GTK_SCROLL_START, range->adjustment->lower,
2791 scroll_end (GtkRange *range)
2796 newval = range->adjustment->upper - range->adjustment->page_size;
2797 g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2802 gtk_range_scroll (GtkRange *range,
2803 GtkScrollType scroll)
2805 gdouble old_value = range->adjustment->value;
2809 case GTK_SCROLL_STEP_LEFT:
2810 if (should_invert (range))
2811 step_forward (range);
2816 case GTK_SCROLL_STEP_UP:
2817 if (should_invert (range))
2818 step_forward (range);
2823 case GTK_SCROLL_STEP_RIGHT:
2824 if (should_invert (range))
2827 step_forward (range);
2830 case GTK_SCROLL_STEP_DOWN:
2831 if (should_invert (range))
2834 step_forward (range);
2837 case GTK_SCROLL_STEP_BACKWARD:
2841 case GTK_SCROLL_STEP_FORWARD:
2842 step_forward (range);
2845 case GTK_SCROLL_PAGE_LEFT:
2846 if (should_invert (range))
2847 page_forward (range);
2852 case GTK_SCROLL_PAGE_UP:
2853 if (should_invert (range))
2854 page_forward (range);
2859 case GTK_SCROLL_PAGE_RIGHT:
2860 if (should_invert (range))
2863 page_forward (range);
2866 case GTK_SCROLL_PAGE_DOWN:
2867 if (should_invert (range))
2870 page_forward (range);
2873 case GTK_SCROLL_PAGE_BACKWARD:
2877 case GTK_SCROLL_PAGE_FORWARD:
2878 page_forward (range);
2881 case GTK_SCROLL_START:
2882 scroll_begin (range);
2885 case GTK_SCROLL_END:
2889 case GTK_SCROLL_JUMP:
2890 /* Used by CList, range doesn't use it. */
2893 case GTK_SCROLL_NONE:
2897 return range->adjustment->value != old_value;
2901 gtk_range_move_slider (GtkRange *range,
2902 GtkScrollType scroll)
2904 gboolean cursor_only;
2906 g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2907 "gtk-keynav-cursor-only", &cursor_only,
2912 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2914 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2916 if (scroll == GTK_SCROLL_STEP_UP ||
2917 scroll == GTK_SCROLL_STEP_DOWN)
2920 gtk_widget_child_focus (toplevel,
2921 scroll == GTK_SCROLL_STEP_UP ?
2922 GTK_DIR_UP : GTK_DIR_DOWN);
2928 if (scroll == GTK_SCROLL_STEP_LEFT ||
2929 scroll == GTK_SCROLL_STEP_RIGHT)
2932 gtk_widget_child_focus (toplevel,
2933 scroll == GTK_SCROLL_STEP_LEFT ?
2934 GTK_DIR_LEFT : GTK_DIR_RIGHT);
2940 if (! gtk_range_scroll (range, scroll))
2941 gtk_widget_error_bell (GTK_WIDGET (range));
2943 /* Policy DELAYED makes sense with key events,
2944 * but DISCONTINUOUS doesn't, so we update immediately
2947 if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2948 gtk_range_update_value (range);
2952 gtk_range_get_props (GtkRange *range,
2956 gint *trough_border,
2957 gint *stepper_spacing,
2958 gboolean *trough_under_steppers,
2959 gint *arrow_displacement_x,
2960 gint *arrow_displacement_y)
2962 GtkWidget *widget = GTK_WIDGET (range);
2963 gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
2964 gint tmp_stepper_spacing, tmp_trough_under_steppers;
2965 gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
2967 gtk_widget_style_get (widget,
2968 "slider-width", &tmp_slider_width,
2969 "trough-border", &tmp_trough_border,
2970 "stepper-size", &tmp_stepper_size,
2971 "stepper-spacing", &tmp_stepper_spacing,
2972 "trough-under-steppers", &tmp_trough_under_steppers,
2973 "arrow-displacement-x", &tmp_arrow_displacement_x,
2974 "arrow-displacement-y", &tmp_arrow_displacement_y,
2977 if (tmp_stepper_spacing > 0)
2978 tmp_trough_under_steppers = FALSE;
2980 if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
2982 gint focus_line_width;
2985 gtk_widget_style_get (GTK_WIDGET (range),
2986 "focus-line-width", &focus_line_width,
2987 "focus-padding", &focus_padding,
2990 tmp_focus_width = focus_line_width + focus_padding;
2994 tmp_focus_width = 0;
2998 *slider_width = tmp_slider_width;
3001 *focus_width = tmp_focus_width;
3004 *trough_border = tmp_trough_border;
3007 *stepper_size = tmp_stepper_size;
3009 if (stepper_spacing)
3010 *stepper_spacing = tmp_stepper_spacing;
3012 if (trough_under_steppers)
3013 *trough_under_steppers = tmp_trough_under_steppers;
3015 if (arrow_displacement_x)
3016 *arrow_displacement_x = tmp_arrow_displacement_x;
3018 if (arrow_displacement_y)
3019 *arrow_displacement_y = tmp_arrow_displacement_y;
3022 #define POINT_IN_RECT(xcoord, ycoord, rect) \
3023 ((xcoord) >= (rect).x && \
3024 (xcoord) < ((rect).x + (rect).width) && \
3025 (ycoord) >= (rect).y && \
3026 (ycoord) < ((rect).y + (rect).height))
3028 /* Update mouse location, return TRUE if it changes */
3030 gtk_range_update_mouse_location (GtkRange *range)
3036 widget = GTK_WIDGET (range);
3038 old = range->layout->mouse_location;
3040 x = range->layout->mouse_x;
3041 y = range->layout->mouse_y;
3043 if (range->layout->grab_location != MOUSE_OUTSIDE)
3044 range->layout->mouse_location = range->layout->grab_location;
3045 else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
3046 range->layout->mouse_location = MOUSE_STEPPER_A;
3047 else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
3048 range->layout->mouse_location = MOUSE_STEPPER_B;
3049 else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
3050 range->layout->mouse_location = MOUSE_STEPPER_C;
3051 else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
3052 range->layout->mouse_location = MOUSE_STEPPER_D;
3053 else if (POINT_IN_RECT (x, y, range->layout->slider))
3054 range->layout->mouse_location = MOUSE_SLIDER;
3055 else if (POINT_IN_RECT (x, y, range->layout->trough))
3056 range->layout->mouse_location = MOUSE_TROUGH;
3057 else if (POINT_IN_RECT (x, y, widget->allocation))
3058 range->layout->mouse_location = MOUSE_WIDGET;
3060 range->layout->mouse_location = MOUSE_OUTSIDE;
3062 return old != range->layout->mouse_location;
3065 /* Clamp rect, border inside widget->allocation, such that we prefer
3066 * to take space from border not rect in all directions, and prefer to
3067 * give space to border over rect in one direction.
3070 clamp_dimensions (GtkWidget *widget,
3073 gboolean border_expands_horizontally)
3075 gint extra, shortage;
3077 g_return_if_fail (rect->x == 0);
3078 g_return_if_fail (rect->y == 0);
3079 g_return_if_fail (rect->width >= 0);
3080 g_return_if_fail (rect->height >= 0);
3084 extra = widget->allocation.width - border->left - border->right - rect->width;
3087 if (border_expands_horizontally)
3089 border->left += extra / 2;
3090 border->right += extra / 2 + extra % 2;
3094 rect->width += extra;
3098 /* See if we can fit rect, if not kill the border */
3099 shortage = rect->width - widget->allocation.width;
3102 rect->width = widget->allocation.width;
3103 /* lose the border */
3109 /* See if we can fit rect with borders */
3110 shortage = rect->width + border->left + border->right -
3111 widget->allocation.width;
3114 /* Shrink borders */
3115 border->left -= shortage / 2;
3116 border->right -= shortage / 2 + shortage % 2;
3122 extra = widget->allocation.height - border->top - border->bottom - rect->height;
3125 if (border_expands_horizontally)
3127 /* don't expand border vertically */
3128 rect->height += extra;
3132 border->top += extra / 2;
3133 border->bottom += extra / 2 + extra % 2;
3137 /* See if we can fit rect, if not kill the border */
3138 shortage = rect->height - widget->allocation.height;
3141 rect->height = widget->allocation.height;
3142 /* lose the border */
3148 /* See if we can fit rect with borders */
3149 shortage = rect->height + border->top + border->bottom -
3150 widget->allocation.height;
3153 /* Shrink borders */
3154 border->top -= shortage / 2;
3155 border->bottom -= shortage / 2 + shortage % 2;
3161 gtk_range_calc_request (GtkRange *range,
3166 gint stepper_spacing,
3167 GdkRectangle *range_rect,
3170 gboolean *has_steppers_ab,
3171 gboolean *has_steppers_cd,
3172 gint *slider_length_p)
3184 if (GTK_RANGE_GET_CLASS (range)->get_range_border)
3185 GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
3190 if (range->has_stepper_a)
3192 if (range->has_stepper_b)
3194 if (range->has_stepper_c)
3196 if (range->has_stepper_d)
3199 n_steppers = n_steppers_ab + n_steppers_cd;
3201 slider_length = range->min_slider_size;
3206 /* We never expand to fill available space in the small dimension
3207 * (i.e. vertical scrollbars are always a fixed width)
3209 if (range->orientation == GTK_ORIENTATION_VERTICAL)
3211 range_rect->width = (focus_width + trough_border) * 2 + slider_width;
3212 range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3214 if (n_steppers_ab > 0)
3215 range_rect->height += stepper_spacing;
3217 if (n_steppers_cd > 0)
3218 range_rect->height += stepper_spacing;
3222 range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3223 range_rect->height = (focus_width + trough_border) * 2 + slider_width;
3225 if (n_steppers_ab > 0)
3226 range_rect->width += stepper_spacing;
3228 if (n_steppers_cd > 0)
3229 range_rect->width += stepper_spacing;
3233 *n_steppers_p = n_steppers;
3235 if (has_steppers_ab)
3236 *has_steppers_ab = (n_steppers_ab > 0);
3238 if (has_steppers_cd)
3239 *has_steppers_cd = (n_steppers_cd > 0);
3241 if (slider_length_p)
3242 *slider_length_p = slider_length;
3246 gtk_range_calc_layout (GtkRange *range,
3247 gdouble adjustment_value)
3249 gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
3253 gboolean has_steppers_ab;
3254 gboolean has_steppers_cd;
3255 gboolean trough_under_steppers;
3256 GdkRectangle range_rect;
3257 GtkRangeLayout *layout;
3260 if (!range->need_recalc)
3263 /* If we have a too-small allocation, we prefer the steppers over
3264 * the trough/slider, probably the steppers are a more useful
3265 * feature in small spaces.
3267 * Also, we prefer to draw the range itself rather than the border
3268 * areas if there's a conflict, since the borders will be decoration
3269 * not controls. Though this depends on subclasses cooperating by
3270 * not drawing on range->range_rect.
3273 widget = GTK_WIDGET (range);
3274 layout = range->layout;
3276 gtk_range_get_props (range,
3277 &slider_width, &stepper_size,
3278 &focus_width, &trough_border,
3279 &stepper_spacing, &trough_under_steppers,
3282 gtk_range_calc_request (range,
3283 slider_width, stepper_size,
3284 focus_width, trough_border, stepper_spacing,
3285 &range_rect, &border, &n_steppers,
3286 &has_steppers_ab, &has_steppers_cd, &slider_length);
3288 /* We never expand to fill available space in the small dimension
3289 * (i.e. vertical scrollbars are always a fixed width)
3291 if (range->orientation == GTK_ORIENTATION_VERTICAL)
3293 clamp_dimensions (widget, &range_rect, &border, TRUE);
3297 clamp_dimensions (widget, &range_rect, &border, FALSE);
3300 range_rect.x = border.left;
3301 range_rect.y = border.top;
3303 range->range_rect = range_rect;
3305 if (range->orientation == GTK_ORIENTATION_VERTICAL)
3307 gint stepper_width, stepper_height;
3309 /* Steppers are the width of the range, and stepper_size in
3310 * height, or if we don't have enough height, divided equally
3311 * among available space.
3313 stepper_width = range_rect.width - focus_width * 2;
3315 if (trough_under_steppers)
3316 stepper_width -= trough_border * 2;
3318 if (stepper_width < 1)
3319 stepper_width = range_rect.width; /* screw the trough border */
3321 if (n_steppers == 0)
3322 stepper_height = 0; /* avoid divide by n_steppers */
3324 stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
3328 layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3329 layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3331 if (range->has_stepper_a)
3333 layout->stepper_a.width = stepper_width;
3334 layout->stepper_a.height = stepper_height;
3338 layout->stepper_a.width = 0;
3339 layout->stepper_a.height = 0;
3344 layout->stepper_b.x = layout->stepper_a.x;
3345 layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
3347 if (range->has_stepper_b)
3349 layout->stepper_b.width = stepper_width;
3350 layout->stepper_b.height = stepper_height;
3354 layout->stepper_b.width = 0;
3355 layout->stepper_b.height = 0;
3360 if (range->has_stepper_d)
3362 layout->stepper_d.width = stepper_width;
3363 layout->stepper_d.height = stepper_height;
3367 layout->stepper_d.width = 0;
3368 layout->stepper_d.height = 0;
3371 layout->stepper_d.x = layout->stepper_a.x;
3372 layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
3376 if (range->has_stepper_c)
3378 layout->stepper_c.width = stepper_width;
3379 layout->stepper_c.height = stepper_height;
3383 layout->stepper_c.width = 0;
3384 layout->stepper_c.height = 0;
3387 layout->stepper_c.x = layout->stepper_a.x;
3388 layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
3390 /* Now the trough is the remaining space between steppers B and C,
3391 * if any, minus spacing
3393 layout->trough.x = range_rect.x;
3394 layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
3395 layout->trough.width = range_rect.width;
3396 layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
3398 /* Slider fits into the trough, with stepper_spacing on either side,
3399 * and the size/position based on the adjustment or fixed, depending.
3401 layout->slider.x = layout->trough.x + focus_width + trough_border;
3402 layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
3404 /* Compute slider position/length */
3406 gint y, bottom, top, height;
3408 top = layout->trough.y;
3409 bottom = layout->trough.y + layout->trough.height;
3411 if (! trough_under_steppers)
3413 top += trough_border;
3414 bottom -= trough_border;
3417 /* slider height is the fraction (page_size /
3418 * total_adjustment_range) times the trough height in pixels
3421 if (range->adjustment->upper - range->adjustment->lower != 0)
3422 height = ((bottom - top) * (range->adjustment->page_size /
3423 (range->adjustment->upper - range->adjustment->lower)));
3425 height = range->min_slider_size;
3427 if (height < range->min_slider_size ||
3428 range->slider_size_fixed)
3429 height = range->min_slider_size;
3431 height = MIN (height, layout->trough.height);
3435 if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3436 y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
3437 (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3439 y = CLAMP (y, top, bottom);
3441 if (should_invert (range))
3442 y = bottom - (y - top + height);
3444 layout->slider.y = y;
3445 layout->slider.height = height;
3447 /* These are publically exported */
3448 range->slider_start = layout->slider.y;
3449 range->slider_end = layout->slider.y + layout->slider.height;
3454 gint stepper_width, stepper_height;
3456 /* Steppers are the height of the range, and stepper_size in
3457 * width, or if we don't have enough width, divided equally
3458 * among available space.
3460 stepper_height = range_rect.height + focus_width * 2;
3462 if (trough_under_steppers)
3463 stepper_height -= trough_border * 2;
3465 if (stepper_height < 1)
3466 stepper_height = range_rect.height; /* screw the trough border */
3468 if (n_steppers == 0)
3469 stepper_width = 0; /* avoid divide by n_steppers */
3471 stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3475 layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3476 layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3478 if (range->has_stepper_a)
3480 layout->stepper_a.width = stepper_width;
3481 layout->stepper_a.height = stepper_height;
3485 layout->stepper_a.width = 0;
3486 layout->stepper_a.height = 0;
3491 layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3492 layout->stepper_b.y = layout->stepper_a.y;
3494 if (range->has_stepper_b)
3496 layout->stepper_b.width = stepper_width;
3497 layout->stepper_b.height = stepper_height;
3501 layout->stepper_b.width = 0;
3502 layout->stepper_b.height = 0;
3507 if (range->has_stepper_d)
3509 layout->stepper_d.width = stepper_width;
3510 layout->stepper_d.height = stepper_height;
3514 layout->stepper_d.width = 0;
3515 layout->stepper_d.height = 0;
3518 layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3519 layout->stepper_d.y = layout->stepper_a.y;
3524 if (range->has_stepper_c)
3526 layout->stepper_c.width = stepper_width;
3527 layout->stepper_c.height = stepper_height;
3531 layout->stepper_c.width = 0;
3532 layout->stepper_c.height = 0;
3535 layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3536 layout->stepper_c.y = layout->stepper_a.y;
3538 /* Now the trough is the remaining space between steppers B and C,
3541 layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3542 layout->trough.y = range_rect.y;
3544 layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3545 layout->trough.height = range_rect.height;
3547 /* Slider fits into the trough, with stepper_spacing on either side,
3548 * and the size/position based on the adjustment or fixed, depending.
3550 layout->slider.y = layout->trough.y + focus_width + trough_border;
3551 layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3553 /* Compute slider position/length */
3555 gint x, left, right, width;
3557 left = layout->trough.x;
3558 right = layout->trough.x + layout->trough.width;
3560 if (! trough_under_steppers)
3562 left += trough_border;
3563 right -= trough_border;
3566 /* slider width is the fraction (page_size /
3567 * total_adjustment_range) times the trough width in pixels
3570 if (range->adjustment->upper - range->adjustment->lower != 0)
3571 width = ((right - left) * (range->adjustment->page_size /
3572 (range->adjustment->upper - range->adjustment->lower)));
3574 width = range->min_slider_size;
3576 if (width < range->min_slider_size ||
3577 range->slider_size_fixed)
3578 width = range->min_slider_size;
3580 width = MIN (width, layout->trough.width);
3584 if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3585 x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3586 (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3588 x = CLAMP (x, left, right);
3590 if (should_invert (range))
3591 x = right - (x - left + width);
3593 layout->slider.x = x;
3594 layout->slider.width = width;
3596 /* These are publically exported */
3597 range->slider_start = layout->slider.x;
3598 range->slider_end = layout->slider.x + layout->slider.width;
3602 gtk_range_update_mouse_location (range);
3604 switch (range->layout->upper_sensitivity)
3606 case GTK_SENSITIVITY_AUTO:
3607 range->layout->upper_sensitive =
3608 (range->adjustment->value <
3609 (range->adjustment->upper - range->adjustment->page_size));
3612 case GTK_SENSITIVITY_ON:
3613 range->layout->upper_sensitive = TRUE;
3616 case GTK_SENSITIVITY_OFF:
3617 range->layout->upper_sensitive = FALSE;
3621 switch (range->layout->lower_sensitivity)
3623 case GTK_SENSITIVITY_AUTO:
3624 range->layout->lower_sensitive =
3625 (range->adjustment->value > range->adjustment->lower);
3628 case GTK_SENSITIVITY_ON:
3629 range->layout->lower_sensitive = TRUE;
3632 case GTK_SENSITIVITY_OFF:
3633 range->layout->lower_sensitive = FALSE;
3638 static GdkRectangle*
3639 get_area (GtkRange *range,
3640 MouseLocation location)
3644 case MOUSE_STEPPER_A:
3645 return &range->layout->stepper_a;
3646 case MOUSE_STEPPER_B:
3647 return &range->layout->stepper_b;
3648 case MOUSE_STEPPER_C:
3649 return &range->layout->stepper_c;
3650 case MOUSE_STEPPER_D:
3651 return &range->layout->stepper_d;
3653 return &range->layout->trough;
3655 return &range->layout->slider;
3661 g_warning (G_STRLOC": bug");
3666 gtk_range_calc_marks (GtkRange *range)
3670 if (!range->layout->recalc_marks)
3673 range->layout->recalc_marks = FALSE;
3675 for (i = 0; i < range->layout->n_marks; i++)
3677 range->need_recalc = TRUE;
3678 gtk_range_calc_layout (range, range->layout->marks[i]);
3679 if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
3680 range->layout->mark_pos[i] = range->layout->slider.x + range->layout->slider.width / 2;
3682 range->layout->mark_pos[i] = range->layout->slider.y + range->layout->slider.height / 2;
3685 range->need_recalc = TRUE;
3689 gtk_range_real_change_value (GtkRange *range,
3690 GtkScrollType scroll,
3693 /* potentially adjust the bounds _before_ we clamp */
3694 g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3696 if (range->layout->restrict_to_fill_level)
3697 value = MIN (value, MAX (range->adjustment->lower,
3698 range->layout->fill_level));
3700 value = CLAMP (value, range->adjustment->lower,
3701 (range->adjustment->upper - range->adjustment->page_size));
3703 if (range->round_digits >= 0)
3708 i = range->round_digits;
3713 value = floor ((value * power) + 0.5) / power;
3716 if (range->adjustment->value != value)
3718 range->need_recalc = TRUE;
3720 gtk_widget_queue_draw (GTK_WIDGET (range));
3722 switch (range->update_policy)
3724 case GTK_UPDATE_CONTINUOUS:
3725 gtk_adjustment_set_value (range->adjustment, value);
3728 /* Delayed means we update after a period of inactivity */
3729 case GTK_UPDATE_DELAYED:
3730 gtk_range_reset_update_timer (range);
3733 /* Discontinuous means we update on button release */
3734 case GTK_UPDATE_DISCONTINUOUS:
3735 /* don't emit value_changed signal */
3736 range->adjustment->value = value;
3737 range->update_pending = TRUE;
3745 gtk_range_update_value (GtkRange *range)
3747 gtk_range_remove_update_timer (range);
3749 if (range->update_pending)
3751 gtk_adjustment_value_changed (range->adjustment);
3753 range->update_pending = FALSE;
3757 struct _GtkRangeStepTimer
3764 second_timeout (gpointer data)
3768 range = GTK_RANGE (data);
3769 gtk_range_scroll (range, range->timer->step);
3775 initial_timeout (gpointer data)
3778 GtkSettings *settings;
3781 settings = gtk_widget_get_settings (GTK_WIDGET (data));
3782 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3784 range = GTK_RANGE (data);
3785 range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3793 gtk_range_add_step_timer (GtkRange *range,
3796 GtkSettings *settings;
3799 g_return_if_fail (range->timer == NULL);
3800 g_return_if_fail (step != GTK_SCROLL_NONE);
3802 settings = gtk_widget_get_settings (GTK_WIDGET (range));
3803 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3805 range->timer = g_new (GtkRangeStepTimer, 1);
3807 range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3810 range->timer->step = step;
3812 gtk_range_scroll (range, range->timer->step);
3816 gtk_range_remove_step_timer (GtkRange *range)
3820 if (range->timer->timeout_id != 0)
3821 g_source_remove (range->timer->timeout_id);
3823 g_free (range->timer);
3825 range->timer = NULL;
3830 update_timeout (gpointer data)
3834 range = GTK_RANGE (data);
3835 gtk_range_update_value (range);
3836 range->update_timeout_id = 0;
3843 gtk_range_reset_update_timer (GtkRange *range)
3845 gtk_range_remove_update_timer (range);
3847 range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3853 gtk_range_remove_update_timer (GtkRange *range)
3855 if (range->update_timeout_id != 0)
3857 g_source_remove (range->update_timeout_id);
3858 range->update_timeout_id = 0;
3863 _gtk_range_set_stop_values (GtkRange *range,
3869 g_free (range->layout->marks);
3870 range->layout->marks = g_new (gdouble, n_values);
3872 g_free (range->layout->mark_pos);
3873 range->layout->mark_pos = g_new (gint, n_values);
3875 range->layout->n_marks = n_values;
3877 for (i = 0; i < n_values; i++)
3878 range->layout->marks[i] = values[i];
3880 range->layout->recalc_marks = TRUE;
3884 _gtk_range_get_stop_positions (GtkRange *range,
3887 gtk_range_calc_marks (range);
3890 *values = g_memdup (range->layout->mark_pos, range->layout->n_marks * sizeof (gint));
3892 return range->layout->n_marks;
3895 #define __GTK_RANGE_C__
3896 #include "gtkaliasdef.c"