X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;ds=sidebyside;f=gtk%2Fgtkrange.c;h=7fa75303a10bef5ee7e80489ba92efaaf318a276;hb=f72dd13a36526c2e811cf24609bdc577266b5a42;hp=fd6fb0d7ee24ba2b21f35c63d3ff3df9f1a76fa2;hpb=d76f2aa4e6223148a752ec7a220199fa6de2a83b;p=~andy%2Fgtk diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c index fd6fb0d7e..7fa75303a 100644 --- a/gtk/gtkrange.c +++ b/gtk/gtkrange.c @@ -25,7 +25,7 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -#include +#include "config.h" #include #include #include @@ -33,7 +33,6 @@ #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkrange.h" -#include "gtkintl.h" #include "gtkscrollbar.h" #include "gtkprivate.h" #include "gtkalias.h" @@ -47,7 +46,10 @@ enum { PROP_ADJUSTMENT, PROP_INVERTED, PROP_LOWER_STEPPER_SENSITIVITY, - PROP_UPPER_STEPPER_SENSITIVITY + PROP_UPPER_STEPPER_SENSITIVITY, + PROP_SHOW_FILL_LEVEL, + PROP_RESTRICT_TO_FILL_LEVEL, + PROP_FILL_LEVEL }; enum { @@ -69,6 +71,8 @@ typedef enum { MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */ } MouseLocation; +#define GTK_RANGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RANGE, GtkRangeLayout)) + struct _GtkRangeLayout { /* These are in widget->window coordinates */ @@ -88,13 +92,24 @@ struct _GtkRangeLayout /* last mouse coords we got, or -1 if mouse is outside the range */ gint mouse_x; gint mouse_y; + /* "grabbed" mouse location, OUTSIDE for no grab */ MouseLocation grab_location; - gint grab_button; /* 0 if none */ + guint grab_button : 8; /* 0 if none */ /* Stepper sensitivity */ + guint lower_sensitive : 1; + guint upper_sensitive : 1; + + /* Fill level */ + guint show_fill_level : 1; + guint restrict_to_fill_level : 1; + GtkSensitivityType lower_sensitivity; GtkSensitivityType upper_sensitivity; + guint repaint_id; + + gdouble fill_level; }; @@ -107,7 +122,6 @@ static void gtk_range_get_property (GObject *object, GValue *value, GParamSpec *pspec); static void gtk_range_destroy (GtkObject *object); -static void gtk_range_finalize (GObject *object); static void gtk_range_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_range_size_allocate (GtkWidget *widget, @@ -116,17 +130,17 @@ static void gtk_range_realize (GtkWidget *widget); static void gtk_range_unrealize (GtkWidget *widget); static void gtk_range_map (GtkWidget *widget); static void gtk_range_unmap (GtkWidget *widget); -static gint gtk_range_expose (GtkWidget *widget, +static gboolean gtk_range_expose (GtkWidget *widget, GdkEventExpose *event); -static gint gtk_range_button_press (GtkWidget *widget, +static gboolean gtk_range_button_press (GtkWidget *widget, GdkEventButton *event); -static gint gtk_range_button_release (GtkWidget *widget, +static gboolean gtk_range_button_release (GtkWidget *widget, GdkEventButton *event); -static gint gtk_range_motion_notify (GtkWidget *widget, +static gboolean gtk_range_motion_notify (GtkWidget *widget, GdkEventMotion *event); -static gint gtk_range_enter_notify (GtkWidget *widget, +static gboolean gtk_range_enter_notify (GtkWidget *widget, GdkEventCrossing *event); -static gint gtk_range_leave_notify (GtkWidget *widget, +static gboolean gtk_range_leave_notify (GtkWidget *widget, GdkEventCrossing *event); static gboolean gtk_range_grab_broken (GtkWidget *widget, GdkEventGrabBroken *event); @@ -134,7 +148,7 @@ static void gtk_range_grab_notify (GtkWidget *widget, gboolean was_grabbed); static void gtk_range_state_changed (GtkWidget *widget, GtkStateType previous_state); -static gint gtk_range_scroll_event (GtkWidget *widget, +static gboolean gtk_range_scroll_event (GtkWidget *widget, GdkEventScroll *event); static void gtk_range_style_set (GtkWidget *widget, GtkStyle *previous_style); @@ -149,7 +163,7 @@ static void gtk_range_move_slider (GtkRange *range, GtkScrollType scroll); /* Internals */ -static void gtk_range_scroll (GtkRange *range, +static gboolean gtk_range_scroll (GtkRange *range, GtkScrollType scroll); static gboolean gtk_range_update_mouse_location (GtkRange *range); static void gtk_range_calc_layout (GtkRange *range, @@ -157,18 +171,23 @@ static void gtk_range_calc_layout (GtkRange *range, static void gtk_range_get_props (GtkRange *range, gint *slider_width, gint *stepper_size, + gint *focus_width, gint *trough_border, gint *stepper_spacing, + gboolean *trough_under_steppers, gint *arrow_displacement_x, gint *arrow_displacement_y); static void gtk_range_calc_request (GtkRange *range, gint slider_width, gint stepper_size, + gint focus_width, gint trough_border, gint stepper_spacing, GdkRectangle *range_rect, GtkBorder *border, gint *n_steppers_p, + gboolean *has_steppers_ab, + gboolean *has_steppers_cd, gint *slider_length_p); static void gtk_range_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data); @@ -191,7 +210,7 @@ static gboolean gtk_range_key_press (GtkWidget *range, static guint signals[LAST_SIGNAL]; -G_DEFINE_ABSTRACT_TYPE (GtkRange, gtk_range, GTK_TYPE_WIDGET); +G_DEFINE_ABSTRACT_TYPE (GtkRange, gtk_range, GTK_TYPE_WIDGET) static void gtk_range_class_init (GtkRangeClass *class) @@ -206,7 +225,6 @@ gtk_range_class_init (GtkRangeClass *class) gobject_class->set_property = gtk_range_set_property; gobject_class->get_property = gtk_range_get_property; - gobject_class->finalize = gtk_range_finalize; object_class->destroy = gtk_range_destroy; widget_class->size_request = gtk_range_size_request; @@ -234,17 +252,23 @@ gtk_range_class_init (GtkRangeClass *class) class->slider_detail = "slider"; class->stepper_detail = "stepper"; + /** + * GtkRange::value-changed: + * @range: the #GtkRange + * + * Emitted when the range value changes. + */ signals[VALUE_CHANGED] = - g_signal_new (I_("value_changed"), + g_signal_new (I_("value-changed"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkRangeClass, value_changed), NULL, NULL, - _gtk_marshal_NONE__NONE, + _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[ADJUST_BOUNDS] = - g_signal_new (I_("adjust_bounds"), + g_signal_new (I_("adjust-bounds"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds), @@ -253,8 +277,15 @@ gtk_range_class_init (GtkRangeClass *class) G_TYPE_NONE, 1, G_TYPE_DOUBLE); + /** + * GtkRange::move-slider: + * @range: the #GtkRange + * @step: how to move the slider + * + * Virtual function that moves the slider. Used for keybindings. + */ signals[MOVE_SLIDER] = - g_signal_new (I_("move_slider"), + g_signal_new (I_("move-slider"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GtkRangeClass, move_slider), @@ -265,11 +296,11 @@ gtk_range_class_init (GtkRangeClass *class) /** * GtkRange::change-value: - * @range: the range that received the signal. - * @scroll: the type of scroll action that was performed. - * @value: the new value resulting from the scroll action. + * @range: the range that received the signal + * @scroll: the type of scroll action that was performed + * @value: the new value resulting from the scroll action * @returns: %TRUE to prevent other handlers from being invoked for the - * signal. %FALSE to propagate the signal further. + * signal, %FALSE to propagate the signal further * * The ::change-value signal is emitted when a scroll action is * performed on a range. It allows an application to determine the @@ -290,7 +321,7 @@ gtk_range_class_init (GtkRangeClass *class) * Since: 2.6 */ signals[CHANGE_VALUE] = - g_signal_new (I_("change_value"), + g_signal_new (I_("change-value"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkRangeClass, change_value), @@ -299,7 +330,7 @@ gtk_range_class_init (GtkRangeClass *class) G_TYPE_BOOLEAN, 2, GTK_TYPE_SCROLL_TYPE, G_TYPE_DOUBLE); - + g_object_class_install_property (gobject_class, PROP_UPDATE_POLICY, g_param_spec_enum ("update-policy", @@ -343,6 +374,58 @@ gtk_range_class_init (GtkRangeClass *class) GTK_SENSITIVITY_AUTO, GTK_PARAM_READWRITE)); + /** + * GtkRange:show-fill-level: + * + * The show-fill-level property controls whether fill level indicator + * graphics are displayed on the trough. See + * gtk_range_set_show_fill_level(). + * + * Since: 2.12 + **/ + g_object_class_install_property (gobject_class, + PROP_SHOW_FILL_LEVEL, + g_param_spec_boolean ("show-fill-level", + P_("Show Fill Level"), + P_("Whether to display a fill level indicator graphics on trough."), + FALSE, + GTK_PARAM_READWRITE)); + + /** + * GtkRange:restrict-to-fill-level: + * + * The restrict-to-fill-level property controls whether slider + * movement is restricted to an upper boundary set by the + * fill level. See gtk_range_set_restrict_to_fill_level(). + * + * Since: 2.12 + **/ + g_object_class_install_property (gobject_class, + PROP_RESTRICT_TO_FILL_LEVEL, + g_param_spec_boolean ("restrict-to-fill-level", + P_("Restrict to Fill Level"), + P_("Whether to restrict the upper boundary to the fill level."), + TRUE, + GTK_PARAM_READWRITE)); + + /** + * GtkRange:fill-level: + * + * The fill level (e.g. prebuffering of a network stream). + * See gtk_range_set_fill_level(). + * + * Since: 2.12 + **/ + g_object_class_install_property (gobject_class, + PROP_FILL_LEVEL, + g_param_spec_double ("fill-level", + P_("Fill Level"), + P_("The fill level."), + -G_MAXDOUBLE, + G_MAXDOUBLE, + G_MAXDOUBLE, + GTK_PARAM_READWRITE)); + gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("slider-width", P_("Slider Width"), @@ -367,6 +450,14 @@ gtk_range_class_init (GtkRangeClass *class) G_MAXINT, 14, GTK_PARAM_READABLE)); + /** + * GtkRange:stepper-spacing: + * + * The spacing between the stepper buttons and thumb. Note that + * setting this value to anything > 0 will automatically set the + * trough-under-steppers style property to %TRUE as well. Also, + * stepper-spacing won't have any effect if there are no steppers. + */ gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("stepper-spacing", P_("Stepper Spacing"), @@ -393,11 +484,59 @@ gtk_range_class_init (GtkRangeClass *class) GTK_PARAM_READABLE)); gtk_widget_class_install_style_property (widget_class, - g_param_spec_boolean ("activate_slider", + g_param_spec_boolean ("activate-slider", P_("Draw slider ACTIVE during drag"), P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"), FALSE, - G_PARAM_READABLE)); + GTK_PARAM_READABLE)); + + /** + * GtkRange:trough-side-details: + * + * When %TRUE, the parts of the trough on the two sides of the + * slider are drawn with different details. + * + * Since: 2.10 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("trough-side-details", + P_("Trough Side Details"), + P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"), + FALSE, + GTK_PARAM_READABLE)); + + /** + * GtkRange:trough-under-steppers: + * + * Whether to draw the trough across the full length of the range or + * to exclude the steppers and their spacing. Note that setting the + * #GtkRange:stepper-spacing style property to any value > 0 will + * automatically enable trough-under-steppers too. + * + * Since: 2.10 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("trough-under-steppers", + P_("Trough Under Steppers"), + P_("Whether to draw trough for full length of range or exclude the steppers and spacing"), + TRUE, + GTK_PARAM_READABLE)); + + /** + * GtkRange:arrow-scaling: + * + * The arrow size proportion relative to the scroll button size. + * + * Since: 2.14 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_float ("arrow-scaling", + P_("Arrow scaling"), + P_("Arrow scaling with regard to scroll button size"), + 0.0, 1.0, 0.5, + GTK_PARAM_READABLE)); + + g_type_class_add_private (class, sizeof (GtkRangeLayout)); } static void @@ -427,6 +566,15 @@ gtk_range_set_property (GObject *object, case PROP_UPPER_STEPPER_SENSITIVITY: gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value)); break; + case PROP_SHOW_FILL_LEVEL: + gtk_range_set_show_fill_level (range, g_value_get_boolean (value)); + break; + case PROP_RESTRICT_TO_FILL_LEVEL: + gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value)); + break; + case PROP_FILL_LEVEL: + gtk_range_set_fill_level (range, g_value_get_double (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -460,6 +608,15 @@ gtk_range_get_property (GObject *object, case PROP_UPPER_STEPPER_SENSITIVITY: g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range)); break; + case PROP_SHOW_FILL_LEVEL: + g_value_set_boolean (value, gtk_range_get_show_fill_level (range)); + break; + case PROP_RESTRICT_TO_FILL_LEVEL: + g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range)); + break; + case PROP_FILL_LEVEL: + g_value_set_double (value, gtk_range_get_fill_level (range)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -482,7 +639,7 @@ gtk_range_init (GtkRange *range) range->has_stepper_d = FALSE; range->need_recalc = TRUE; range->round_digits = -1; - range->layout = g_new0 (GtkRangeLayout, 1); + range->layout = GTK_RANGE_GET_PRIVATE (range); range->layout->mouse_location = MOUSE_OUTSIDE; range->layout->mouse_x = -1; range->layout->mouse_y = -1; @@ -490,6 +647,11 @@ gtk_range_init (GtkRange *range) range->layout->grab_button = 0; range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO; range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO; + range->layout->lower_sensitive = TRUE; + range->layout->upper_sensitive = TRUE; + range->layout->show_fill_level = FALSE; + range->layout->restrict_to_fill_level = TRUE; + range->layout->fill_level = G_MAXDOUBLE; range->timer = NULL; } @@ -528,7 +690,6 @@ gtk_range_get_adjustment (GtkRange *range) * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only * be updated when the user releases the button and ends the slider * drag operation. - * **/ void gtk_range_set_update_policy (GtkRange *range, @@ -571,7 +732,6 @@ gtk_range_get_update_policy (GtkRange *range) * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and * indicates the size of the visible area of the widget being scrolled. * The page size affects the size of the scrollbar slider. - * **/ void gtk_range_set_adjustment (GtkRange *range, @@ -603,7 +763,7 @@ gtk_range_set_adjustment (GtkRange *range, g_signal_connect (adjustment, "changed", G_CALLBACK (gtk_range_adjustment_changed), range); - g_signal_connect (adjustment, "value_changed", + g_signal_connect (adjustment, "value-changed", G_CALLBACK (gtk_range_adjustment_value_changed), range); @@ -621,7 +781,6 @@ gtk_range_set_adjustment (GtkRange *range, * slider moves from top to bottom or left to right. Inverted * ranges have higher values at the top or on the right rather than * on the bottom or left. - * **/ void gtk_range_set_inverted (GtkRange *range, @@ -663,7 +822,7 @@ gtk_range_get_inverted (GtkRange *range) * Sets the sensitivity policy for the stepper that points to the * 'lower' end of the GtkRange's adjustment. * - * Sine: 2.10 + * Since: 2.10 **/ void gtk_range_set_lower_stepper_sensitivity (GtkRange *range, @@ -674,7 +833,11 @@ gtk_range_set_lower_stepper_sensitivity (GtkRange *range, if (range->layout->lower_sensitivity != sensitivity) { range->layout->lower_sensitivity = sensitivity; + + range->need_recalc = TRUE; + gtk_range_calc_layout (range, range->adjustment->value); gtk_widget_queue_draw (GTK_WIDGET (range)); + g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity"); } } @@ -706,7 +869,7 @@ gtk_range_get_lower_stepper_sensitivity (GtkRange *range) * Sets the sensitivity policy for the stepper that points to the * 'upper' end of the GtkRange's adjustment. * - * Sine: 2.10 + * Since: 2.10 **/ void gtk_range_set_upper_stepper_sensitivity (GtkRange *range, @@ -717,7 +880,11 @@ gtk_range_set_upper_stepper_sensitivity (GtkRange *range, if (range->layout->upper_sensitivity != sensitivity) { range->layout->upper_sensitivity = sensitivity; + + range->need_recalc = TRUE; + gtk_range_calc_layout (range, range->adjustment->value); gtk_widget_queue_draw (GTK_WIDGET (range)); + g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity"); } } @@ -751,7 +918,6 @@ gtk_range_get_upper_stepper_sensitivity (GtkRange *range) * The step size is used when the user clicks the #GtkScrollbar * arrows or moves #GtkScale via arrow keys. The page size * is used for example when moving via Page Up or Page Down keys. - * **/ void gtk_range_set_increments (GtkRange *range, @@ -789,9 +955,11 @@ gtk_range_set_range (GtkRange *range, range->adjustment->lower = min; range->adjustment->upper = max; - value = CLAMP (range->adjustment->value, - range->adjustment->lower, - (range->adjustment->upper - range->adjustment->page_size)); + value = range->adjustment->value; + + if (range->layout->restrict_to_fill_level) + value = MIN (value, MAX (range->adjustment->lower, + range->layout->fill_level)); gtk_adjustment_set_value (range->adjustment, value); gtk_adjustment_changed (range->adjustment); @@ -804,18 +972,18 @@ gtk_range_set_range (GtkRange *range, * * Sets the current value of the range; if the value is outside the * minimum or maximum range values, it will be clamped to fit inside - * them. The range emits the "value_changed" signal if the value - * changes. - * + * them. The range emits the #GtkRange::value-changed signal if the + * value changes. **/ void gtk_range_set_value (GtkRange *range, gdouble value) { g_return_if_fail (GTK_IS_RANGE (range)); - - value = CLAMP (value, range->adjustment->lower, - (range->adjustment->upper - range->adjustment->page_size)); + + if (range->layout->restrict_to_fill_level) + value = MIN (value, MAX (range->adjustment->lower, + range->layout->fill_level)); gtk_adjustment_set_value (range->adjustment, value); } @@ -836,6 +1004,159 @@ gtk_range_get_value (GtkRange *range) return range->adjustment->value; } +/** + * gtk_range_set_show_fill_level: + * @range: A #GtkRange + * @show_fill_level: Whether a fill level indicator graphics is shown. + * + * Sets whether a graphical fill level is show on the trough. See + * gtk_range_set_fill_level() for a general description of the fill + * level concept. + * + * Since: 2.12 + **/ +void +gtk_range_set_show_fill_level (GtkRange *range, + gboolean show_fill_level) +{ + g_return_if_fail (GTK_IS_RANGE (range)); + + show_fill_level = show_fill_level ? TRUE : FALSE; + + if (show_fill_level != range->layout->show_fill_level) + { + range->layout->show_fill_level = show_fill_level; + g_object_notify (G_OBJECT (range), "show-fill-level"); + gtk_widget_queue_draw (GTK_WIDGET (range)); + } +} + +/** + * gtk_range_get_show_fill_level: + * @range: A #GtkRange + * + * Gets whether the range displays the fill level graphically. + * + * Return value: %TRUE if @range shows the fill level. + * + * Since: 2.12 + **/ +gboolean +gtk_range_get_show_fill_level (GtkRange *range) +{ + g_return_val_if_fail (GTK_IS_RANGE (range), FALSE); + + return range->layout->show_fill_level; +} + +/** + * gtk_range_set_restrict_to_fill_level: + * @range: A #GtkRange + * @restrict_to_fill_level: Whether the fill level restricts slider movement. + * + * Sets whether the slider is restricted to the fill level. See + * gtk_range_set_fill_level() for a general description of the fill + * level concept. + * + * Since: 2.12 + **/ +void +gtk_range_set_restrict_to_fill_level (GtkRange *range, + gboolean restrict_to_fill_level) +{ + g_return_if_fail (GTK_IS_RANGE (range)); + + restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE; + + if (restrict_to_fill_level != range->layout->restrict_to_fill_level) + { + range->layout->restrict_to_fill_level = restrict_to_fill_level; + g_object_notify (G_OBJECT (range), "restrict-to-fill-level"); + + gtk_range_set_value (range, gtk_range_get_value (range)); + } +} + +/** + * gtk_range_get_restrict_to_fill_level: + * @range: A #GtkRange + * + * Gets whether the range is restricted to the fill level. + * + * Return value: %TRUE if @range is restricted to the fill level. + * + * Since: 2.12 + **/ +gboolean +gtk_range_get_restrict_to_fill_level (GtkRange *range) +{ + g_return_val_if_fail (GTK_IS_RANGE (range), FALSE); + + return range->layout->restrict_to_fill_level; +} + +/** + * gtk_range_set_fill_level: + * @range: a #GtkRange + * @fill_level: the new position of the fill level indicator + * + * Set the new position of the fill level indicator. + * + * The "fill level" is probably best described by its most prominent + * use case, which is an indicator for the amount of pre-buffering in + * a streaming media player. In that use case, the value of the range + * would indicate the current play position, and the fill level would + * be the position up to which the file/stream has been downloaded. + * + * This amount of prebuffering can be displayed on the range's trough + * and is themeable separately from the trough. To enable fill level + * display, use gtk_range_set_show_fill_level(). The range defaults + * to not showing the fill level. + * + * Additionally, it's possible to restrict the range's slider position + * to values which are smaller than the fill level. This is controller + * by gtk_range_set_restrict_to_fill_level() and is by default + * enabled. + * + * Since: 2.12 + **/ +void +gtk_range_set_fill_level (GtkRange *range, + gdouble fill_level) +{ + g_return_if_fail (GTK_IS_RANGE (range)); + + if (fill_level != range->layout->fill_level) + { + range->layout->fill_level = fill_level; + g_object_notify (G_OBJECT (range), "fill-level"); + + if (range->layout->show_fill_level) + gtk_widget_queue_draw (GTK_WIDGET (range)); + + if (range->layout->restrict_to_fill_level) + gtk_range_set_value (range, gtk_range_get_value (range)); + } +} + +/** + * gtk_range_get_fill_level: + * @range : A #GtkRange + * + * Gets the current position of the fill level indicator. + * + * Return value: The current fill level + * + * Since: 2.12 + **/ +gdouble +gtk_range_get_fill_level (GtkRange *range) +{ + g_return_val_if_fail (GTK_IS_RANGE (range), 0.0); + + return range->layout->fill_level; +} + static gboolean should_invert (GtkRange *range) { @@ -848,16 +1169,6 @@ should_invert (GtkRange *range) return range->inverted; } -static void -gtk_range_finalize (GObject *object) -{ - GtkRange *range = GTK_RANGE (object); - - g_free (range->layout); - - (* G_OBJECT_CLASS (gtk_range_parent_class)->finalize) (object); -} - static void gtk_range_destroy (GtkObject *object) { @@ -865,7 +1176,11 @@ gtk_range_destroy (GtkObject *object) gtk_range_remove_step_timer (range); gtk_range_remove_update_timer (range); - + + if (range->layout->repaint_id) + g_source_remove (range->layout->repaint_id); + range->layout->repaint_id = 0; + if (range->adjustment) { g_signal_handlers_disconnect_by_func (range->adjustment, @@ -878,7 +1193,7 @@ gtk_range_destroy (GtkObject *object) range->adjustment = NULL; } - (* GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy) (object); + GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object); } static void @@ -886,19 +1201,22 @@ gtk_range_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkRange *range; - gint slider_width, stepper_size, trough_border, stepper_spacing; + gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing; GdkRectangle range_rect; GtkBorder border; range = GTK_RANGE (widget); gtk_range_get_props (range, - &slider_width, &stepper_size, &trough_border, &stepper_spacing, - NULL, NULL); + &slider_width, &stepper_size, + &focus_width, &trough_border, + &stepper_spacing, NULL, + NULL, NULL); gtk_range_calc_request (range, - slider_width, stepper_size, trough_border, stepper_spacing, - &range_rect, &border, NULL, NULL); + slider_width, stepper_size, + focus_width, trough_border, stepper_spacing, + &range_rect, &border, NULL, NULL, NULL, NULL); requisition->width = range_rect.width + border.left + border.right; requisition->height = range_rect.height + border.top + border.bottom; @@ -975,9 +1293,8 @@ gtk_range_unrealize (GtkWidget *widget) gdk_window_set_user_data (range->event_window, NULL); gdk_window_destroy (range->event_window); range->event_window = NULL; - - if (GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize) - (* GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize) (widget); + + GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget); } static void @@ -1014,6 +1331,7 @@ draw_stepper (GtkRange *range, GtkShadowType shadow_type; GdkRectangle intersection; GtkWidget *widget = GTK_WIDGET (range); + gfloat arrow_scaling; gint arrow_x; gint arrow_y; @@ -1034,40 +1352,11 @@ draw_stepper (GtkRange *range, (range->inverted && (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_LEFT))) { - switch (range->layout->upper_sensitivity) - { - case GTK_SENSITIVITY_AUTO: - arrow_sensitive = - (range->adjustment->value < - (range->adjustment->upper - range->adjustment->page_size)); - break; - - case GTK_SENSITIVITY_ON: - arrow_sensitive = TRUE; - break; - - case GTK_SENSITIVITY_OFF: - arrow_sensitive = FALSE; - break; - } + arrow_sensitive = range->layout->upper_sensitive; } else { - switch (range->layout->lower_sensitivity) - { - case GTK_SENSITIVITY_AUTO: - arrow_sensitive = - (range->adjustment->value > range->adjustment->lower); - break; - - case GTK_SENSITIVITY_ON: - arrow_sensitive = TRUE; - break; - - case GTK_SENSITIVITY_OFF: - arrow_sensitive = FALSE; - break; - } + arrow_sensitive = range->layout->lower_sensitive; } if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive) @@ -1094,17 +1383,20 @@ draw_stepper (GtkRange *range, rect->width, rect->height); - arrow_width = rect->width / 2; - arrow_height = rect->height / 2; + gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL); + + arrow_width = rect->width * arrow_scaling; + arrow_height = rect->height * arrow_scaling; arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2; arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2; - + if (clicked && arrow_sensitive) { gint arrow_displacement_x; gint arrow_displacement_y; - gtk_range_get_props (GTK_RANGE (widget), NULL, NULL, NULL, NULL, + gtk_range_get_props (GTK_RANGE (widget), + NULL, NULL, NULL, NULL, NULL, NULL, &arrow_displacement_x, &arrow_displacement_y); arrow_x += arrow_displacement_x; @@ -1121,11 +1413,11 @@ draw_stepper (GtkRange *range, arrow_x, arrow_y, arrow_width, arrow_height); } -static gint +static gboolean gtk_range_expose (GtkWidget *widget, GdkEventExpose *event) { - GtkRange *range; + GtkRange *range = GTK_RANGE (widget); gboolean sensitive; GtkStateType state; GtkShadowType shadow_type; @@ -1134,22 +1426,21 @@ gtk_range_expose (GtkWidget *widget, gint focus_line_width = 0; gint focus_padding = 0; gboolean touchscreen; - gboolean activate_slider; g_object_get (gtk_widget_get_settings (widget), "gtk-touchscreen-mode", &touchscreen, NULL); + if (GTK_WIDGET_CAN_FOCUS (range)) + gtk_widget_style_get (GTK_WIDGET (range), + "focus-line-width", &focus_line_width, + "focus-padding", &focus_padding, + NULL); - range = GTK_RANGE (widget); + /* we're now exposing, so there's no need to force early repaints */ + if (range->layout->repaint_id) + g_source_remove (range->layout->repaint_id); + range->layout->repaint_id = 0; - if (GTK_WIDGET_CAN_FOCUS (range)) - { - gtk_widget_style_get (GTK_WIDGET (range), - "focus-line-width", &focus_line_width, - "focus-padding", &focus_padding, - NULL); - } - expose_area = event->area; expose_area.x -= widget->allocation.x; expose_area.y -= widget->allocation.y; @@ -1168,20 +1459,181 @@ gtk_range_expose (GtkWidget *widget, if (gdk_rectangle_intersect (&expose_area, &range->range_rect, &area)) { + gint x = (widget->allocation.x + range->range_rect.x + + focus_line_width + focus_padding); + gint y = (widget->allocation.y + range->range_rect.y + + focus_line_width + focus_padding); + gint width = (range->range_rect.width - + 2 * (focus_line_width + focus_padding)); + gint height = (range->range_rect.height - + 2 * (focus_line_width + focus_padding)); + gboolean trough_side_details; + gboolean trough_under_steppers; + gint stepper_size; + gint stepper_spacing; + area.x += widget->allocation.x; area.y += widget->allocation.y; - - gtk_paint_box (widget->style, - widget->window, - sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, - GTK_SHADOW_IN, - &area, GTK_WIDGET(range), "trough", - widget->allocation.x + range->range_rect.x + focus_line_width + focus_padding, - widget->allocation.y + range->range_rect.y + focus_line_width + focus_padding, - range->range_rect.width - 2 * (focus_line_width + focus_padding), - range->range_rect.height - 2 * (focus_line_width + focus_padding)); - - + + gtk_widget_style_get (GTK_WIDGET (range), + "trough-side-details", &trough_side_details, + "trough-under-steppers", &trough_under_steppers, + "stepper-size", &stepper_size, + "stepper-spacing", &stepper_spacing, + NULL); + + if (stepper_spacing > 0) + trough_under_steppers = FALSE; + + if (! trough_under_steppers) + { + gint offset = 0; + gint shorter = 0; + + if (range->has_stepper_a) + offset += stepper_size; + + if (range->has_stepper_b) + offset += stepper_size; + + shorter += offset; + + if (range->has_stepper_c) + shorter += stepper_size; + + if (range->has_stepper_d) + shorter += stepper_size; + + if (range->has_stepper_a || range->has_stepper_b) + { + offset += stepper_spacing; + shorter += stepper_spacing; + } + + if (range->has_stepper_c || range->has_stepper_d) + { + shorter += stepper_spacing; + } + + if (range->orientation == GTK_ORIENTATION_HORIZONTAL) + { + x += offset; + width -= shorter; + } + else + { + y += offset; + height -= shorter; + } + } + + if (! trough_side_details) + { + gtk_paint_box (widget->style, + widget->window, + sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, + GTK_SHADOW_IN, + &area, GTK_WIDGET(range), "trough", + x, y, + width, height); + } + else + { + gint trough_change_pos_x = width; + gint trough_change_pos_y = height; + + if (range->orientation == GTK_ORIENTATION_HORIZONTAL) + trough_change_pos_x = (range->layout->slider.x + + range->layout->slider.width / 2 - + (x - widget->allocation.x)); + else + trough_change_pos_y = (range->layout->slider.y + + range->layout->slider.height / 2 - + (y - widget->allocation.y)); + + gtk_paint_box (widget->style, + widget->window, + sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, + GTK_SHADOW_IN, + &area, GTK_WIDGET (range), + should_invert (range) ? "trough-upper" : "trough-lower", + x, y, + trough_change_pos_x, trough_change_pos_y); + + if (range->orientation == GTK_ORIENTATION_HORIZONTAL) + trough_change_pos_y = 0; + else + trough_change_pos_x = 0; + + gtk_paint_box (widget->style, + widget->window, + sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, + GTK_SHADOW_IN, + &area, GTK_WIDGET (range), + should_invert (range) ? "trough-lower" : "trough-upper", + x + trough_change_pos_x, y + trough_change_pos_y, + width - trough_change_pos_x, + height - trough_change_pos_y); + } + + if (range->layout->show_fill_level && + range->adjustment->upper - range->adjustment->page_size - + range->adjustment->lower != 0) + { + gdouble fill_level = range->layout->fill_level; + gint fill_x = x; + gint fill_y = y; + gint fill_width = width; + gint fill_height = height; + gchar *fill_detail; + + fill_level = CLAMP (fill_level, range->adjustment->lower, + range->adjustment->upper - + range->adjustment->page_size); + + if (range->orientation == GTK_ORIENTATION_HORIZONTAL) + { + fill_x = widget->allocation.x + range->layout->trough.x; + fill_width = (range->layout->slider.width + + (fill_level - range->adjustment->lower) / + (range->adjustment->upper - + range->adjustment->lower - + range->adjustment->page_size) * + (range->layout->trough.width - + range->layout->slider.width)); + + if (should_invert (range)) + fill_x += range->layout->trough.width - fill_width; + } + else + { + fill_y = widget->allocation.y + range->layout->trough.y; + fill_height = (range->layout->slider.height + + (fill_level - range->adjustment->lower) / + (range->adjustment->upper - + range->adjustment->lower - + range->adjustment->page_size) * + (range->layout->trough.height - + range->layout->slider.height)); + + if (should_invert (range)) + fill_y += range->layout->trough.height - fill_height; + } + + if (fill_level < range->adjustment->upper - range->adjustment->page_size) + fill_detail = "trough-fill-level-full"; + else + fill_detail = "trough-fill-level"; + + gtk_paint_box (widget->style, + widget->window, + sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, + GTK_SHADOW_OUT, + &area, GTK_WIDGET (range), fill_detail, + fill_x, fill_y, + fill_width, fill_height); + } + if (sensitive && GTK_WIDGET_HAS_FOCUS (range)) gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget), @@ -1203,8 +1655,10 @@ gtk_range_expose (GtkWidget *widget, if (range->layout->grab_location == MOUSE_SLIDER) { - gtk_widget_style_get (widget, "activate_slider", &activate_slider, NULL); - + gboolean activate_slider; + + gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL); + if (activate_slider) { state = GTK_STATE_ACTIVE; @@ -1283,12 +1737,16 @@ range_grab_add (GtkRange *range, static void range_grab_remove (GtkRange *range) { + MouseLocation location; + gtk_grab_remove (GTK_WIDGET (range)); - + + location = range->layout->grab_location; range->layout->grab_location = MOUSE_OUTSIDE; range->layout->grab_button = 0; - if (gtk_range_update_mouse_location (range)) + if (gtk_range_update_mouse_location (range) || + location != MOUSE_OUTSIDE) gtk_widget_queue_draw (GTK_WIDGET (range)); } @@ -1359,25 +1817,46 @@ coord_to_value (GtkRange *range, { gdouble frac; gdouble value; - + gint trough_length; + gint trough_start; + gint slider_length; + gint trough_border; + gint trough_under_steppers; + if (range->orientation == GTK_ORIENTATION_VERTICAL) - if (range->layout->trough.height == range->layout->slider.height) - frac = 1.0; - else - frac = ((coord - range->layout->trough.y) / - (gdouble) (range->layout->trough.height - range->layout->slider.height)); + { + trough_length = range->layout->trough.height; + trough_start = range->layout->trough.y; + slider_length = range->layout->slider.height; + } + else + { + trough_length = range->layout->trough.width; + trough_start = range->layout->trough.x; + slider_length = range->layout->slider.width; + } + + gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL, + &trough_under_steppers, NULL, NULL); + + if (! trough_under_steppers) + { + trough_start += trough_border; + trough_length -= 2 * trough_border; + } + + if (trough_length == slider_length) + frac = 1.0; else - if (range->layout->trough.width == range->layout->slider.width) - frac = 1.0; - else - frac = ((coord - range->layout->trough.x) / - (gdouble) (range->layout->trough.width - range->layout->slider.width)); + frac = (MAX (0, coord - trough_start) / + (gdouble) (trough_length - slider_length)); if (should_invert (range)) frac = 1.0 - frac; - - value = range->adjustment->lower + - frac * (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size); + + value = range->adjustment->lower + frac * (range->adjustment->upper - + range->adjustment->lower - + range->adjustment->page_size); return value; } @@ -1386,20 +1865,21 @@ static gboolean gtk_range_key_press (GtkWidget *widget, GdkEventKey *event) { - GtkRange *range = (GtkRange *)widget; + GtkRange *range = GTK_RANGE (widget); - if (event->keyval == GDK_Escape) + if (event->keyval == GDK_Escape && + range->layout->grab_location != MOUSE_OUTSIDE) { stop_scrolling (range); - update_slider_position (range, + update_slider_position (range, range->slide_initial_coordinate, range->slide_initial_coordinate); return TRUE; } - return FALSE; + return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event); } static gint @@ -1518,13 +1998,15 @@ gtk_range_button_press (GtkWidget *widget, } range_grab_add (range, MOUSE_SLIDER, event->button); - - gtk_widget_style_get (widget, "activate_slider", &activate_slider, NULL); - - /* force a redraw, if the active slider is drawn differently to the prelight one */ + + gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL); + + /* force a redraw, if the active slider is drawn differently to the + * prelight one + */ if (activate_slider) gtk_widget_queue_draw (widget); - + if (need_value_update) update_slider_position (range, event->x, event->y); @@ -1565,11 +2047,6 @@ stop_scrolling (GtkRange *range) gtk_range_remove_step_timer (range); /* Flush any pending discontinuous/delayed updates */ gtk_range_update_value (range); - - /* Just be lazy about this, if we scrolled it will all redraw anyway, - * so no point optimizing the button deactivate case - */ - gtk_widget_queue_draw (GTK_WIDGET (range)); } static gboolean @@ -1656,7 +2133,7 @@ _gtk_range_get_wheel_delta (GtkRange *range, return delta; } -static gint +static gboolean gtk_range_scroll_event (GtkWidget *widget, GdkEventScroll *event) { @@ -1685,7 +2162,7 @@ gtk_range_scroll_event (GtkWidget *widget, return TRUE; } -static gint +static gboolean gtk_range_motion_notify (GtkWidget *widget, GdkEventMotion *event) { @@ -1709,7 +2186,7 @@ gtk_range_motion_notify (GtkWidget *widget, return range->layout->mouse_location != MOUSE_OUTSIDE; } -static gint +static gboolean gtk_range_enter_notify (GtkWidget *widget, GdkEventCrossing *event) { @@ -1724,7 +2201,7 @@ gtk_range_enter_notify (GtkWidget *widget, return TRUE; } -static gint +static gboolean gtk_range_leave_notify (GtkWidget *widget, GdkEventCrossing *event) { @@ -1774,6 +2251,9 @@ layout_changed (GtkRangeLayout *layout1, check_rectangle (layout1->stepper_b, layout2->stepper_b); check_rectangle (layout1->stepper_c, layout2->stepper_c); + if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE; + if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE; + return FALSE; } @@ -1801,6 +2281,16 @@ gtk_range_adjustment_changed (GtkAdjustment *adjustment, */ } +static gboolean +force_repaint (gpointer data) +{ + GtkRange *range = GTK_RANGE (data); + range->layout->repaint_id = 0; + if (GTK_WIDGET_DRAWABLE (range)) + gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE); + return FALSE; +} + static void gtk_range_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data) @@ -1816,10 +2306,9 @@ gtk_range_adjustment_value_changed (GtkAdjustment *adjustment, if (layout_changed (range->layout, &layout)) { gtk_widget_queue_draw (GTK_WIDGET (range)); - - /* This is so we don't lag the widget being scrolled. */ - if (GTK_WIDGET_REALIZED (range)) - gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE); + /* setup a timer to ensure the range isn't lagging too much behind the scroll position */ + if (!range->layout->repaint_id) + range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL); } /* Note that we don't round off to range->round_digits here. @@ -1841,7 +2330,7 @@ gtk_range_style_set (GtkWidget *widget, range->need_recalc = TRUE; - (* GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set) (widget, previous_style); + GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style); } static void @@ -1909,10 +2398,12 @@ scroll_end (GtkRange *range) &handled); } -static void +static gboolean gtk_range_scroll (GtkRange *range, GtkScrollType scroll) { + gdouble old_value = range->adjustment->value; + switch (scroll) { case GTK_SCROLL_STEP_LEFT: @@ -2002,13 +2493,52 @@ gtk_range_scroll (GtkRange *range, case GTK_SCROLL_NONE: break; } + + return range->adjustment->value != old_value; } static void gtk_range_move_slider (GtkRange *range, GtkScrollType scroll) { - gtk_range_scroll (range, scroll); + gboolean cursor_only; + + g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)), + "gtk-keynav-cursor-only", &cursor_only, + NULL); + + if (cursor_only) + { + GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range)); + + if (range->orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (scroll == GTK_SCROLL_STEP_UP || + scroll == GTK_SCROLL_STEP_DOWN) + { + if (toplevel) + gtk_widget_child_focus (toplevel, + scroll == GTK_SCROLL_STEP_UP ? + GTK_DIR_UP : GTK_DIR_DOWN); + return; + } + } + else + { + if (scroll == GTK_SCROLL_STEP_LEFT || + scroll == GTK_SCROLL_STEP_RIGHT) + { + if (toplevel) + gtk_widget_child_focus (toplevel, + scroll == GTK_SCROLL_STEP_LEFT ? + GTK_DIR_LEFT : GTK_DIR_RIGHT); + return; + } + } + } + + if (! gtk_range_scroll (range, scroll)) + gtk_widget_error_bell (GTK_WIDGET (range)); /* Policy DELAYED makes sense with key events, * but DISCONTINUOUS doesn't, so we update immediately @@ -2022,13 +2552,16 @@ static void gtk_range_get_props (GtkRange *range, gint *slider_width, gint *stepper_size, + gint *focus_width, gint *trough_border, gint *stepper_spacing, + gboolean *trough_under_steppers, gint *arrow_displacement_x, gint *arrow_displacement_y) { GtkWidget *widget = GTK_WIDGET (range); - gint tmp_slider_width, tmp_stepper_size, tmp_trough_border, tmp_stepper_spacing; + gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border; + gint tmp_stepper_spacing, tmp_trough_under_steppers; gint tmp_arrow_displacement_x, tmp_arrow_displacement_y; gtk_widget_style_get (widget, @@ -2036,10 +2569,14 @@ gtk_range_get_props (GtkRange *range, "trough-border", &tmp_trough_border, "stepper-size", &tmp_stepper_size, "stepper-spacing", &tmp_stepper_spacing, + "trough-under-steppers", &tmp_trough_under_steppers, "arrow-displacement-x", &tmp_arrow_displacement_x, "arrow-displacement-y", &tmp_arrow_displacement_y, NULL); - + + if (tmp_stepper_spacing > 0) + tmp_trough_under_steppers = FALSE; + if (GTK_WIDGET_CAN_FOCUS (range)) { gint focus_line_width; @@ -2049,13 +2586,20 @@ gtk_range_get_props (GtkRange *range, "focus-line-width", &focus_line_width, "focus-padding", &focus_padding, NULL); - - tmp_trough_border += focus_line_width + focus_padding; + + tmp_focus_width = focus_line_width + focus_padding; + } + else + { + tmp_focus_width = 0; } if (slider_width) *slider_width = tmp_slider_width; + if (focus_width) + *focus_width = tmp_focus_width; + if (trough_border) *trough_border = tmp_trough_border; @@ -2065,6 +2609,9 @@ gtk_range_get_props (GtkRange *range, if (stepper_spacing) *stepper_spacing = tmp_stepper_spacing; + if (trough_under_steppers) + *trough_under_steppers = tmp_trough_under_steppers; + if (arrow_displacement_x) *arrow_displacement_x = tmp_arrow_displacement_x; @@ -2214,15 +2761,20 @@ static void gtk_range_calc_request (GtkRange *range, gint slider_width, gint stepper_size, + gint focus_width, gint trough_border, gint stepper_spacing, GdkRectangle *range_rect, GtkBorder *border, gint *n_steppers_p, + gboolean *has_steppers_ab, + gboolean *has_steppers_cd, gint *slider_length_p) { gint slider_length; gint n_steppers; + gint n_steppers_ab; + gint n_steppers_cd; border->left = 0; border->right = 0; @@ -2230,17 +2782,21 @@ gtk_range_calc_request (GtkRange *range, border->bottom = 0; if (GTK_RANGE_GET_CLASS (range)->get_range_border) - (* GTK_RANGE_GET_CLASS (range)->get_range_border) (range, border); - - n_steppers = 0; + GTK_RANGE_GET_CLASS (range)->get_range_border (range, border); + + n_steppers_ab = 0; + n_steppers_cd = 0; + if (range->has_stepper_a) - n_steppers += 1; + n_steppers_ab += 1; if (range->has_stepper_b) - n_steppers += 1; + n_steppers_ab += 1; if (range->has_stepper_c) - n_steppers += 1; + n_steppers_cd += 1; if (range->has_stepper_d) - n_steppers += 1; + n_steppers_cd += 1; + + n_steppers = n_steppers_ab + n_steppers_cd; slider_length = range->min_slider_size; @@ -2252,18 +2808,36 @@ gtk_range_calc_request (GtkRange *range, */ if (range->orientation == GTK_ORIENTATION_VERTICAL) { - range_rect->width = trough_border * 2 + slider_width; - range_rect->height = stepper_size * n_steppers + stepper_spacing * 2 + trough_border * 2 + slider_length; + range_rect->width = (focus_width + trough_border) * 2 + slider_width; + range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length; + + if (n_steppers_ab > 0) + range_rect->height += stepper_spacing; + + if (n_steppers_cd > 0) + range_rect->height += stepper_spacing; } else { - range_rect->width = stepper_size * n_steppers + stepper_spacing * 2 + trough_border * 2 + slider_length; - range_rect->height = trough_border * 2 + slider_width; + range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length; + range_rect->height = (focus_width + trough_border) * 2 + slider_width; + + if (n_steppers_ab > 0) + range_rect->width += stepper_spacing; + + if (n_steppers_cd > 0) + range_rect->width += stepper_spacing; } if (n_steppers_p) *n_steppers_p = n_steppers; + if (has_steppers_ab) + *has_steppers_ab = (n_steppers_ab > 0); + + if (has_steppers_cd) + *has_steppers_cd = (n_steppers_cd > 0); + if (slider_length_p) *slider_length_p = slider_length; } @@ -2272,10 +2846,13 @@ static void gtk_range_calc_layout (GtkRange *range, gdouble adjustment_value) { - gint slider_width, stepper_size, trough_border, stepper_spacing; + gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing; gint slider_length; GtkBorder border; gint n_steppers; + gboolean has_steppers_ab; + gboolean has_steppers_cd; + gboolean trough_under_steppers; GdkRectangle range_rect; GtkRangeLayout *layout; GtkWidget *widget; @@ -2297,12 +2874,16 @@ gtk_range_calc_layout (GtkRange *range, layout = range->layout; gtk_range_get_props (range, - &slider_width, &stepper_size, &trough_border, &stepper_spacing, + &slider_width, &stepper_size, + &focus_width, &trough_border, + &stepper_spacing, &trough_under_steppers, NULL, NULL); gtk_range_calc_request (range, - slider_width, stepper_size, trough_border, stepper_spacing, - &range_rect, &border, &n_steppers, &slider_length); + slider_width, stepper_size, + focus_width, trough_border, stepper_spacing, + &range_rect, &border, &n_steppers, + &has_steppers_ab, &has_steppers_cd, &slider_length); /* We never expand to fill available space in the small dimension * (i.e. vertical scrollbars are always a fixed width) @@ -2329,7 +2910,10 @@ gtk_range_calc_layout (GtkRange *range, * height, or if we don't have enough height, divided equally * among available space. */ - stepper_width = range_rect.width - trough_border * 2; + stepper_width = range_rect.width - focus_width * 2; + + if (trough_under_steppers) + stepper_width -= trough_border * 2; if (stepper_width < 1) stepper_width = range_rect.width; /* screw the trough border */ @@ -2341,8 +2925,8 @@ gtk_range_calc_layout (GtkRange *range, /* Stepper A */ - layout->stepper_a.x = range_rect.x + trough_border; - layout->stepper_a.y = range_rect.y + trough_border; + layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers; + layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers; if (range->has_stepper_a) { @@ -2385,7 +2969,7 @@ gtk_range_calc_layout (GtkRange *range, } layout->stepper_d.x = layout->stepper_a.x; - layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - trough_border; + layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers; /* Stepper C */ @@ -2404,26 +2988,32 @@ gtk_range_calc_layout (GtkRange *range, layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height; /* Now the trough is the remaining space between steppers B and C, - * if any + * if any, minus spacing */ layout->trough.x = range_rect.x; - layout->trough.y = layout->stepper_b.y + layout->stepper_b.height; + layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab; layout->trough.width = range_rect.width; - layout->trough.height = layout->stepper_c.y - (layout->stepper_b.y + layout->stepper_b.height); - + layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd; + /* Slider fits into the trough, with stepper_spacing on either side, * and the size/position based on the adjustment or fixed, depending. */ - layout->slider.x = layout->trough.x + trough_border; - layout->slider.width = layout->trough.width - trough_border * 2; + layout->slider.x = layout->trough.x + focus_width + trough_border; + layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2; /* Compute slider position/length */ { gint y, bottom, top, height; - top = layout->trough.y + stepper_spacing; - bottom = layout->trough.y + layout->trough.height - stepper_spacing; - + top = layout->trough.y; + bottom = layout->trough.y + layout->trough.height; + + if (! trough_under_steppers) + { + top += trough_border; + bottom -= trough_border; + } + /* slider height is the fraction (page_size / * total_adjustment_range) times the trough height in pixels */ @@ -2437,8 +3027,8 @@ gtk_range_calc_layout (GtkRange *range, if (height < range->min_slider_size || range->slider_size_fixed) height = range->min_slider_size; - - height = MIN (height, (layout->trough.height - stepper_spacing * 2)); + + height = MIN (height, layout->trough.height); y = top; @@ -2467,7 +3057,10 @@ gtk_range_calc_layout (GtkRange *range, * width, or if we don't have enough width, divided equally * among available space. */ - stepper_height = range_rect.height - trough_border * 2; + stepper_height = range_rect.height + focus_width * 2; + + if (trough_under_steppers) + stepper_height -= trough_border * 2; if (stepper_height < 1) stepper_height = range_rect.height; /* screw the trough border */ @@ -2479,8 +3072,8 @@ gtk_range_calc_layout (GtkRange *range, /* Stepper A */ - layout->stepper_a.x = range_rect.x + trough_border; - layout->stepper_a.y = range_rect.y + trough_border; + layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers; + layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers; if (range->has_stepper_a) { @@ -2522,7 +3115,7 @@ gtk_range_calc_layout (GtkRange *range, layout->stepper_d.height = 0; } - layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - trough_border; + layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers; layout->stepper_d.y = layout->stepper_a.y; @@ -2545,25 +3138,31 @@ gtk_range_calc_layout (GtkRange *range, /* Now the trough is the remaining space between steppers B and C, * if any */ - layout->trough.x = layout->stepper_b.x + layout->stepper_b.width; + layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab; layout->trough.y = range_rect.y; - layout->trough.width = layout->stepper_c.x - (layout->stepper_b.x + layout->stepper_b.width); + layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd; layout->trough.height = range_rect.height; - + /* Slider fits into the trough, with stepper_spacing on either side, * and the size/position based on the adjustment or fixed, depending. */ - layout->slider.y = layout->trough.y + trough_border; - layout->slider.height = layout->trough.height - trough_border * 2; + layout->slider.y = layout->trough.y + focus_width + trough_border; + layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2; /* Compute slider position/length */ { gint x, left, right, width; - left = layout->trough.x + stepper_spacing; - right = layout->trough.x + layout->trough.width - stepper_spacing; - + left = layout->trough.x; + right = layout->trough.x + layout->trough.width; + + if (! trough_under_steppers) + { + left += trough_border; + right -= trough_border; + } + /* slider width is the fraction (page_size / * total_adjustment_range) times the trough width in pixels */ @@ -2578,7 +3177,7 @@ gtk_range_calc_layout (GtkRange *range, range->slider_size_fixed) width = range->min_slider_size; - width = MIN (width, (layout->trough.width - stepper_spacing * 2)); + width = MIN (width, layout->trough.width); x = left; @@ -2601,6 +3200,39 @@ gtk_range_calc_layout (GtkRange *range, } gtk_range_update_mouse_location (range); + + switch (range->layout->upper_sensitivity) + { + case GTK_SENSITIVITY_AUTO: + range->layout->upper_sensitive = + (range->adjustment->value < + (range->adjustment->upper - range->adjustment->page_size)); + break; + + case GTK_SENSITIVITY_ON: + range->layout->upper_sensitive = TRUE; + break; + + case GTK_SENSITIVITY_OFF: + range->layout->upper_sensitive = FALSE; + break; + } + + switch (range->layout->lower_sensitivity) + { + case GTK_SENSITIVITY_AUTO: + range->layout->lower_sensitive = + (range->adjustment->value > range->adjustment->lower); + break; + + case GTK_SENSITIVITY_ON: + range->layout->lower_sensitive = TRUE; + break; + + case GTK_SENSITIVITY_OFF: + range->layout->lower_sensitive = FALSE; + break; + } } static GdkRectangle* @@ -2638,6 +3270,10 @@ gtk_range_real_change_value (GtkRange *range, /* potentially adjust the bounds _before we clamp */ g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value); + if (range->layout->restrict_to_fill_level) + value = MIN (value, MAX (range->adjustment->lower, + range->layout->fill_level)); + value = CLAMP (value, range->adjustment->lower, (range->adjustment->upper - range->adjustment->page_size)); @@ -2706,10 +3342,8 @@ second_timeout (gpointer data) { GtkRange *range; - GDK_THREADS_ENTER (); range = GTK_RANGE (data); gtk_range_scroll (range, range->timer->step); - GDK_THREADS_LEAVE (); return TRUE; } @@ -2721,16 +3355,13 @@ initial_timeout (gpointer data) GtkSettings *settings; guint timeout; - GDK_THREADS_ENTER (); settings = gtk_widget_get_settings (GTK_WIDGET (data)); g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL); range = GTK_RANGE (data); - range->timer->timeout_id = g_timeout_add (timeout * SCROLL_DELAY_FACTOR, + range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR, second_timeout, range); - GDK_THREADS_LEAVE (); - /* remove self */ return FALSE; } @@ -2750,7 +3381,7 @@ gtk_range_add_step_timer (GtkRange *range, range->timer = g_new (GtkRangeStepTimer, 1); - range->timer->timeout_id = g_timeout_add (timeout, + range->timer->timeout_id = gdk_threads_add_timeout (timeout, initial_timeout, range); range->timer->step = step; @@ -2777,11 +3408,9 @@ update_timeout (gpointer data) { GtkRange *range; - GDK_THREADS_ENTER (); range = GTK_RANGE (data); gtk_range_update_value (range); range->update_timeout_id = 0; - GDK_THREADS_LEAVE (); /* self-remove */ return FALSE; @@ -2792,7 +3421,7 @@ gtk_range_reset_update_timer (GtkRange *range) { gtk_range_remove_update_timer (range); - range->update_timeout_id = g_timeout_add (UPDATE_DELAY, + range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY, update_timeout, range); }