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
#undef GTK_DISABLE_DEPRECATED
35
#include <gdk/gdkkeysyms.h>
37
#include "gtkmarshalers.h"
38
#include "gtkorientable.h"
41
#include "gtkscrollbar.h"
42
#include "gtkprivate.h"
46
#define SCROLL_DELAY_FACTOR 5 /* Scroll repeat multiplier */
47
#define UPDATE_DELAY 300 /* Delay for queued update */
55
PROP_LOWER_STEPPER_SENSITIVITY,
56
PROP_UPPER_STEPPER_SENSITIVITY,
58
PROP_RESTRICT_TO_FILL_LEVEL,
79
MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
89
#define GTK_RANGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RANGE, GtkRangeLayout))
91
struct _GtkRangeLayout
93
/* These are in widget->window coordinates */
94
GdkRectangle stepper_a;
95
GdkRectangle stepper_b;
96
GdkRectangle stepper_c;
97
GdkRectangle stepper_d;
98
/* The trough rectangle is the area the thumb can slide in, not the
104
/* Layout-related state */
106
MouseLocation mouse_location;
107
/* last mouse coords we got, or -1 if mouse is outside the range */
111
/* "grabbed" mouse location, OUTSIDE for no grab */
112
MouseLocation grab_location;
113
guint grab_button : 8; /* 0 if none */
115
/* Stepper sensitivity */
116
guint lower_sensitive : 1;
117
guint upper_sensitive : 1;
120
guint show_fill_level : 1;
121
guint restrict_to_fill_level : 1;
123
GtkSensitivityType lower_sensitivity;
124
GtkSensitivityType upper_sensitivity;
129
GQuark slider_detail_quark;
130
GQuark stepper_detail_quark[4];
135
gboolean recalc_marks;
139
static void gtk_range_set_property (GObject *object,
143
static void gtk_range_get_property (GObject *object,
147
static void gtk_range_destroy (GtkObject *object);
148
static void gtk_range_size_request (GtkWidget *widget,
149
GtkRequisition *requisition);
150
static void gtk_range_size_allocate (GtkWidget *widget,
151
GtkAllocation *allocation);
152
static void gtk_range_realize (GtkWidget *widget);
153
static void gtk_range_unrealize (GtkWidget *widget);
154
static void gtk_range_map (GtkWidget *widget);
155
static void gtk_range_unmap (GtkWidget *widget);
156
static gboolean gtk_range_expose (GtkWidget *widget,
157
GdkEventExpose *event);
158
static gboolean gtk_range_button_press (GtkWidget *widget,
159
GdkEventButton *event);
160
static gboolean gtk_range_button_release (GtkWidget *widget,
161
GdkEventButton *event);
162
static gboolean gtk_range_motion_notify (GtkWidget *widget,
163
GdkEventMotion *event);
164
static gboolean gtk_range_enter_notify (GtkWidget *widget,
165
GdkEventCrossing *event);
166
static gboolean gtk_range_leave_notify (GtkWidget *widget,
167
GdkEventCrossing *event);
168
static gboolean gtk_range_grab_broken (GtkWidget *widget,
169
GdkEventGrabBroken *event);
170
static void gtk_range_grab_notify (GtkWidget *widget,
171
gboolean was_grabbed);
172
static void gtk_range_state_changed (GtkWidget *widget,
173
GtkStateType previous_state);
174
static gboolean gtk_range_scroll_event (GtkWidget *widget,
175
GdkEventScroll *event);
176
static void gtk_range_style_set (GtkWidget *widget,
177
GtkStyle *previous_style);
178
static void update_slider_position (GtkRange *range,
181
static void stop_scrolling (GtkRange *range);
185
static void gtk_range_move_slider (GtkRange *range,
186
GtkScrollType scroll);
189
static gboolean gtk_range_scroll (GtkRange *range,
190
GtkScrollType scroll);
191
static gboolean gtk_range_update_mouse_location (GtkRange *range);
192
static void gtk_range_calc_layout (GtkRange *range,
193
gdouble adjustment_value);
194
static void gtk_range_calc_marks (GtkRange *range);
195
static void gtk_range_get_props (GtkRange *range,
200
gint *stepper_spacing,
201
gboolean *trough_under_steppers,
202
gint *arrow_displacement_x,
203
gint *arrow_displacement_y);
204
static void gtk_range_calc_request (GtkRange *range,
209
gint stepper_spacing,
210
GdkRectangle *range_rect,
213
gboolean *has_steppers_ab,
214
gboolean *has_steppers_cd,
215
gint *slider_length_p);
216
static void gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
218
static void gtk_range_adjustment_changed (GtkAdjustment *adjustment,
220
static void gtk_range_add_step_timer (GtkRange *range,
222
static void gtk_range_remove_step_timer (GtkRange *range);
223
static void gtk_range_reset_update_timer (GtkRange *range);
224
static void gtk_range_remove_update_timer (GtkRange *range);
225
static GdkRectangle* get_area (GtkRange *range,
226
MouseLocation location);
227
static gboolean gtk_range_real_change_value (GtkRange *range,
228
GtkScrollType scroll,
230
static void gtk_range_update_value (GtkRange *range);
231
static gboolean gtk_range_key_press (GtkWidget *range,
235
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
236
G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
239
static guint signals[LAST_SIGNAL];
243
gtk_range_class_init (GtkRangeClass *class)
245
GObjectClass *gobject_class;
246
GtkObjectClass *object_class;
247
GtkWidgetClass *widget_class;
249
gobject_class = G_OBJECT_CLASS (class);
250
object_class = (GtkObjectClass*) class;
251
widget_class = (GtkWidgetClass*) class;
253
gobject_class->set_property = gtk_range_set_property;
254
gobject_class->get_property = gtk_range_get_property;
256
object_class->destroy = gtk_range_destroy;
258
widget_class->size_request = gtk_range_size_request;
259
widget_class->size_allocate = gtk_range_size_allocate;
260
widget_class->realize = gtk_range_realize;
261
widget_class->unrealize = gtk_range_unrealize;
262
widget_class->map = gtk_range_map;
263
widget_class->unmap = gtk_range_unmap;
264
widget_class->expose_event = gtk_range_expose;
265
widget_class->button_press_event = gtk_range_button_press;
266
widget_class->button_release_event = gtk_range_button_release;
267
widget_class->motion_notify_event = gtk_range_motion_notify;
268
widget_class->scroll_event = gtk_range_scroll_event;
269
widget_class->enter_notify_event = gtk_range_enter_notify;
270
widget_class->leave_notify_event = gtk_range_leave_notify;
271
widget_class->grab_broken_event = gtk_range_grab_broken;
272
widget_class->grab_notify = gtk_range_grab_notify;
273
widget_class->state_changed = gtk_range_state_changed;
274
widget_class->style_set = gtk_range_style_set;
275
widget_class->key_press_event = gtk_range_key_press;
277
class->move_slider = gtk_range_move_slider;
278
class->change_value = gtk_range_real_change_value;
280
class->slider_detail = "slider";
281
class->stepper_detail = "stepper";
284
* GtkRange::value-changed:
285
* @range: the #GtkRange
287
* Emitted when the range value changes.
289
signals[VALUE_CHANGED] =
290
g_signal_new (I_("value-changed"),
291
G_TYPE_FROM_CLASS (gobject_class),
293
G_STRUCT_OFFSET (GtkRangeClass, value_changed),
295
_gtk_marshal_VOID__VOID,
298
signals[ADJUST_BOUNDS] =
299
g_signal_new (I_("adjust-bounds"),
300
G_TYPE_FROM_CLASS (gobject_class),
302
G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
304
_gtk_marshal_VOID__DOUBLE,
309
* GtkRange::move-slider:
310
* @range: the #GtkRange
311
* @step: how to move the slider
313
* Virtual function that moves the slider. Used for keybindings.
315
signals[MOVE_SLIDER] =
316
g_signal_new (I_("move-slider"),
317
G_TYPE_FROM_CLASS (gobject_class),
318
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
319
G_STRUCT_OFFSET (GtkRangeClass, move_slider),
321
_gtk_marshal_VOID__ENUM,
323
GTK_TYPE_SCROLL_TYPE);
326
* GtkRange::change-value:
327
* @range: the range that received the signal
328
* @scroll: the type of scroll action that was performed
329
* @value: the new value resulting from the scroll action
330
* @returns: %TRUE to prevent other handlers from being invoked for the
331
* signal, %FALSE to propagate the signal further
333
* The ::change-value signal is emitted when a scroll action is
334
* performed on a range. It allows an application to determine the
335
* type of scroll event that occurred and the resultant new value.
336
* The application can handle the event itself and return %TRUE to
337
* prevent further processing. Or, by returning %FALSE, it can pass
338
* the event to other handlers until the default GTK+ handler is
341
* The value parameter is unrounded. An application that overrides
342
* the ::change-value signal is responsible for clamping the value to
343
* the desired number of decimal digits; the default GTK+ handler
344
* clamps the value based on #GtkRange:round_digits.
346
* It is not possible to use delayed update policies in an overridden
347
* ::change-value handler.
351
signals[CHANGE_VALUE] =
352
g_signal_new (I_("change-value"),
353
G_TYPE_FROM_CLASS (gobject_class),
355
G_STRUCT_OFFSET (GtkRangeClass, change_value),
356
_gtk_boolean_handled_accumulator, NULL,
357
_gtk_marshal_BOOLEAN__ENUM_DOUBLE,
359
GTK_TYPE_SCROLL_TYPE,
362
g_object_class_override_property (gobject_class,
366
g_object_class_install_property (gobject_class,
368
g_param_spec_enum ("update-policy",
370
P_("How the range should be updated on the screen"),
371
GTK_TYPE_UPDATE_TYPE,
372
GTK_UPDATE_CONTINUOUS,
373
GTK_PARAM_READWRITE));
375
g_object_class_install_property (gobject_class,
377
g_param_spec_object ("adjustment",
379
P_("The GtkAdjustment that contains the current value of this range object"),
381
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
383
g_object_class_install_property (gobject_class,
385
g_param_spec_boolean ("inverted",
387
P_("Invert direction slider moves to increase range value"),
389
GTK_PARAM_READWRITE));
391
g_object_class_install_property (gobject_class,
392
PROP_LOWER_STEPPER_SENSITIVITY,
393
g_param_spec_enum ("lower-stepper-sensitivity",
394
P_("Lower stepper sensitivity"),
395
P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
396
GTK_TYPE_SENSITIVITY_TYPE,
397
GTK_SENSITIVITY_AUTO,
398
GTK_PARAM_READWRITE));
400
g_object_class_install_property (gobject_class,
401
PROP_UPPER_STEPPER_SENSITIVITY,
402
g_param_spec_enum ("upper-stepper-sensitivity",
403
P_("Upper stepper sensitivity"),
404
P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
405
GTK_TYPE_SENSITIVITY_TYPE,
406
GTK_SENSITIVITY_AUTO,
407
GTK_PARAM_READWRITE));
410
* GtkRange:show-fill-level:
412
* The show-fill-level property controls whether fill level indicator
413
* graphics are displayed on the trough. See
414
* gtk_range_set_show_fill_level().
418
g_object_class_install_property (gobject_class,
419
PROP_SHOW_FILL_LEVEL,
420
g_param_spec_boolean ("show-fill-level",
421
P_("Show Fill Level"),
422
P_("Whether to display a fill level indicator graphics on trough."),
424
GTK_PARAM_READWRITE));
427
* GtkRange:restrict-to-fill-level:
429
* The restrict-to-fill-level property controls whether slider
430
* movement is restricted to an upper boundary set by the
431
* fill level. See gtk_range_set_restrict_to_fill_level().
435
g_object_class_install_property (gobject_class,
436
PROP_RESTRICT_TO_FILL_LEVEL,
437
g_param_spec_boolean ("restrict-to-fill-level",
438
P_("Restrict to Fill Level"),
439
P_("Whether to restrict the upper boundary to the fill level."),
441
GTK_PARAM_READWRITE));
444
* GtkRange:fill-level:
446
* The fill level (e.g. prebuffering of a network stream).
447
* See gtk_range_set_fill_level().
451
g_object_class_install_property (gobject_class,
453
g_param_spec_double ("fill-level",
455
P_("The fill level."),
459
GTK_PARAM_READWRITE));
462
* GtkRange:round-digits:
464
* The number of digits to round the value to when
465
* it changes, or -1. See #GtkRange::change-value.
469
g_object_class_install_property (gobject_class,
471
g_param_spec_int ("round-digits",
473
P_("The number of digits to round the value to."),
477
GTK_PARAM_READWRITE));
479
gtk_widget_class_install_style_property (widget_class,
480
g_param_spec_int ("slider-width",
482
P_("Width of scrollbar or scale thumb"),
486
GTK_PARAM_READABLE));
487
gtk_widget_class_install_style_property (widget_class,
488
g_param_spec_int ("trough-border",
490
P_("Spacing between thumb/steppers and outer trough bevel"),
494
GTK_PARAM_READABLE));
495
gtk_widget_class_install_style_property (widget_class,
496
g_param_spec_int ("stepper-size",
498
P_("Length of step buttons at ends"),
502
GTK_PARAM_READABLE));
504
* GtkRange:stepper-spacing:
506
* The spacing between the stepper buttons and thumb. Note that
507
* setting this value to anything > 0 will automatically set the
508
* trough-under-steppers style property to %TRUE as well. Also,
509
* stepper-spacing won't have any effect if there are no steppers.
511
gtk_widget_class_install_style_property (widget_class,
512
g_param_spec_int ("stepper-spacing",
513
P_("Stepper Spacing"),
514
P_("Spacing between step buttons and thumb"),
518
GTK_PARAM_READABLE));
519
gtk_widget_class_install_style_property (widget_class,
520
g_param_spec_int ("arrow-displacement-x",
521
P_("Arrow X Displacement"),
522
P_("How far in the x direction to move the arrow when the button is depressed"),
526
GTK_PARAM_READABLE));
527
gtk_widget_class_install_style_property (widget_class,
528
g_param_spec_int ("arrow-displacement-y",
529
P_("Arrow Y Displacement"),
530
P_("How far in the y direction to move the arrow when the button is depressed"),
534
GTK_PARAM_READABLE));
537
* GtkRange:activate-slider:
539
* When %TRUE, sliders will be drawn active and with shadow in
540
* while they are dragged.
542
* Deprecated: 2.22: This style property will be removed in GTK+ 3
544
gtk_widget_class_install_style_property (widget_class,
545
g_param_spec_boolean ("activate-slider",
546
P_("Draw slider ACTIVE during drag"),
547
P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
549
GTK_PARAM_READABLE));
552
* GtkRange:trough-side-details:
554
* When %TRUE, the parts of the trough on the two sides of the
555
* slider are drawn with different details.
559
* Deprecated: 2.22: This style property will be removed in GTK+ 3
561
gtk_widget_class_install_style_property (widget_class,
562
g_param_spec_boolean ("trough-side-details",
563
P_("Trough Side Details"),
564
P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"),
566
GTK_PARAM_READABLE));
569
* GtkRange:trough-under-steppers:
571
* Whether to draw the trough across the full length of the range or
572
* to exclude the steppers and their spacing. Note that setting the
573
* #GtkRange:stepper-spacing style property to any value > 0 will
574
* automatically enable trough-under-steppers too.
578
gtk_widget_class_install_style_property (widget_class,
579
g_param_spec_boolean ("trough-under-steppers",
580
P_("Trough Under Steppers"),
581
P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
583
GTK_PARAM_READABLE));
586
* GtkRange:arrow-scaling:
588
* The arrow size proportion relative to the scroll button size.
592
gtk_widget_class_install_style_property (widget_class,
593
g_param_spec_float ("arrow-scaling",
595
P_("Arrow scaling with regard to scroll button size"),
597
GTK_PARAM_READABLE));
600
* GtkRange:stepper-position-details:
602
* When %TRUE, the detail string for rendering the steppers will be
603
* suffixed with information about the stepper position.
607
* Deprecated: 2.22: This style property will be removed in GTK+ 3
609
gtk_widget_class_install_style_property (widget_class,
610
g_param_spec_boolean ("stepper-position-details",
611
P_("Stepper Position Details"),
612
P_("When TRUE, the detail string for rendering the steppers is suffixed with position information"),
614
GTK_PARAM_READABLE));
616
g_type_class_add_private (class, sizeof (GtkRangeLayout));
620
gtk_range_set_property (GObject *object,
625
GtkRange *range = GTK_RANGE (object);
629
case PROP_ORIENTATION:
630
range->orientation = g_value_get_enum (value);
632
range->layout->slider_detail_quark = 0;
633
range->layout->stepper_detail_quark[0] = 0;
634
range->layout->stepper_detail_quark[1] = 0;
635
range->layout->stepper_detail_quark[2] = 0;
636
range->layout->stepper_detail_quark[3] = 0;
638
gtk_widget_queue_resize (GTK_WIDGET (range));
640
case PROP_UPDATE_POLICY:
641
gtk_range_set_update_policy (range, g_value_get_enum (value));
643
case PROP_ADJUSTMENT:
644
gtk_range_set_adjustment (range, g_value_get_object (value));
647
gtk_range_set_inverted (range, g_value_get_boolean (value));
649
case PROP_LOWER_STEPPER_SENSITIVITY:
650
gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
652
case PROP_UPPER_STEPPER_SENSITIVITY:
653
gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
655
case PROP_SHOW_FILL_LEVEL:
656
gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
658
case PROP_RESTRICT_TO_FILL_LEVEL:
659
gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
661
case PROP_FILL_LEVEL:
662
gtk_range_set_fill_level (range, g_value_get_double (value));
664
case PROP_ROUND_DIGITS:
665
gtk_range_set_round_digits (range, g_value_get_int (value));
668
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
674
gtk_range_get_property (GObject *object,
679
GtkRange *range = GTK_RANGE (object);
683
case PROP_ORIENTATION:
684
g_value_set_enum (value, range->orientation);
686
case PROP_UPDATE_POLICY:
687
g_value_set_enum (value, range->update_policy);
689
case PROP_ADJUSTMENT:
690
g_value_set_object (value, range->adjustment);
693
g_value_set_boolean (value, range->inverted);
695
case PROP_LOWER_STEPPER_SENSITIVITY:
696
g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
698
case PROP_UPPER_STEPPER_SENSITIVITY:
699
g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
701
case PROP_SHOW_FILL_LEVEL:
702
g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
704
case PROP_RESTRICT_TO_FILL_LEVEL:
705
g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
707
case PROP_FILL_LEVEL:
708
g_value_set_double (value, gtk_range_get_fill_level (range));
710
case PROP_ROUND_DIGITS:
711
g_value_set_int (value, gtk_range_get_round_digits (range));
714
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
720
gtk_range_init (GtkRange *range)
722
gtk_widget_set_has_window (GTK_WIDGET (range), FALSE);
724
range->orientation = GTK_ORIENTATION_HORIZONTAL;
725
range->adjustment = NULL;
726
range->update_policy = GTK_UPDATE_CONTINUOUS;
727
range->inverted = FALSE;
728
range->flippable = FALSE;
729
range->min_slider_size = 1;
730
range->has_stepper_a = FALSE;
731
range->has_stepper_b = FALSE;
732
range->has_stepper_c = FALSE;
733
range->has_stepper_d = FALSE;
734
range->need_recalc = TRUE;
735
range->round_digits = -1;
736
range->layout = GTK_RANGE_GET_PRIVATE (range);
737
range->layout->mouse_location = MOUSE_OUTSIDE;
738
range->layout->mouse_x = -1;
739
range->layout->mouse_y = -1;
740
range->layout->grab_location = MOUSE_OUTSIDE;
741
range->layout->grab_button = 0;
742
range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
743
range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
744
range->layout->lower_sensitive = TRUE;
745
range->layout->upper_sensitive = TRUE;
746
range->layout->show_fill_level = FALSE;
747
range->layout->restrict_to_fill_level = TRUE;
748
range->layout->fill_level = G_MAXDOUBLE;
753
* gtk_range_get_adjustment:
754
* @range: a #GtkRange
756
* Get the #GtkAdjustment which is the "model" object for #GtkRange.
757
* See gtk_range_set_adjustment() for details.
758
* The return value does not have a reference added, so should not
761
* Return value: (transfer none): a #GtkAdjustment
764
gtk_range_get_adjustment (GtkRange *range)
766
g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
768
if (!range->adjustment)
769
gtk_range_set_adjustment (range, NULL);
771
return range->adjustment;
775
* gtk_range_set_update_policy:
776
* @range: a #GtkRange
777
* @policy: update policy
779
* Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
780
* anytime the range slider is moved, the range value will change and the
781
* value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
782
* the value will be updated after a brief timeout where no slider motion
783
* occurs, so updates are spaced by a short time rather than
784
* continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
785
* be updated when the user releases the button and ends the slider
788
* Deprecated: 2.24: There is no replacement. If you require delayed
789
* updates, you need to code it yourself.
792
gtk_range_set_update_policy (GtkRange *range,
793
GtkUpdateType policy)
795
g_return_if_fail (GTK_IS_RANGE (range));
797
if (range->update_policy != policy)
799
range->update_policy = policy;
800
g_object_notify (G_OBJECT (range), "update-policy");
805
* gtk_range_get_update_policy:
806
* @range: a #GtkRange
808
* Gets the update policy of @range. See gtk_range_set_update_policy().
810
* Return value: the current update policy
812
* Deprecated: 2.24: There is no replacement. If you require delayed
813
* updates, you need to code it yourself.
816
gtk_range_get_update_policy (GtkRange *range)
818
g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
820
return range->update_policy;
824
* gtk_range_set_adjustment:
825
* @range: a #GtkRange
826
* @adjustment: a #GtkAdjustment
828
* Sets the adjustment to be used as the "model" object for this range
829
* widget. The adjustment indicates the current range value, the
830
* minimum and maximum range values, the step/page increments used
831
* for keybindings and scrolling, and the page size. The page size
832
* is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
833
* indicates the size of the visible area of the widget being scrolled.
834
* The page size affects the size of the scrollbar slider.
837
gtk_range_set_adjustment (GtkRange *range,
838
GtkAdjustment *adjustment)
840
g_return_if_fail (GTK_IS_RANGE (range));
843
adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
845
g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
847
if (range->adjustment != adjustment)
849
if (range->adjustment)
851
g_signal_handlers_disconnect_by_func (range->adjustment,
852
gtk_range_adjustment_changed,
854
g_signal_handlers_disconnect_by_func (range->adjustment,
855
gtk_range_adjustment_value_changed,
857
g_object_unref (range->adjustment);
860
range->adjustment = adjustment;
861
g_object_ref_sink (adjustment);
863
g_signal_connect (adjustment, "changed",
864
G_CALLBACK (gtk_range_adjustment_changed),
866
g_signal_connect (adjustment, "value-changed",
867
G_CALLBACK (gtk_range_adjustment_value_changed),
870
gtk_range_adjustment_changed (adjustment, range);
871
g_object_notify (G_OBJECT (range), "adjustment");
876
* gtk_range_set_inverted:
877
* @range: a #GtkRange
878
* @setting: %TRUE to invert the range
880
* Ranges normally move from lower to higher values as the
881
* slider moves from top to bottom or left to right. Inverted
882
* ranges have higher values at the top or on the right rather than
883
* on the bottom or left.
886
gtk_range_set_inverted (GtkRange *range,
889
g_return_if_fail (GTK_IS_RANGE (range));
891
setting = setting != FALSE;
893
if (setting != range->inverted)
895
range->inverted = setting;
896
g_object_notify (G_OBJECT (range), "inverted");
897
gtk_widget_queue_resize (GTK_WIDGET (range));
902
* gtk_range_get_inverted:
903
* @range: a #GtkRange
905
* Gets the value set by gtk_range_set_inverted().
907
* Return value: %TRUE if the range is inverted
910
gtk_range_get_inverted (GtkRange *range)
912
g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
914
return range->inverted;
918
* gtk_range_set_flippable:
919
* @range: a #GtkRange
920
* @flippable: %TRUE to make the range flippable
922
* If a range is flippable, it will switch its direction if it is
923
* horizontal and its direction is %GTK_TEXT_DIR_RTL.
925
* See gtk_widget_get_direction().
930
gtk_range_set_flippable (GtkRange *range,
933
g_return_if_fail (GTK_IS_RANGE (range));
935
flippable = flippable ? TRUE : FALSE;
937
if (flippable != range->flippable)
939
range->flippable = flippable;
941
gtk_widget_queue_draw (GTK_WIDGET (range));
946
* gtk_range_get_flippable:
947
* @range: a #GtkRange
949
* Gets the value set by gtk_range_set_flippable().
951
* Return value: %TRUE if the range is flippable
956
gtk_range_get_flippable (GtkRange *range)
958
g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
960
return range->flippable;
964
* gtk_range_set_slider_size_fixed:
965
* @range: a #GtkRange
966
* @size_fixed: %TRUE to make the slider size constant
968
* Sets whether the range's slider has a fixed size, or a size that
969
* depends on it's adjustment's page size.
971
* This function is useful mainly for #GtkRange subclasses.
976
gtk_range_set_slider_size_fixed (GtkRange *range,
979
g_return_if_fail (GTK_IS_RANGE (range));
981
if (size_fixed != range->slider_size_fixed)
983
range->slider_size_fixed = size_fixed ? TRUE : FALSE;
985
range->need_recalc = TRUE;
986
gtk_range_calc_layout (range, range->adjustment->value);
987
gtk_widget_queue_draw (GTK_WIDGET (range));
992
* gtk_range_get_slider_size_fixed:
993
* @range: a #GtkRange
995
* This function is useful mainly for #GtkRange subclasses.
997
* See gtk_range_set_slider_size_fixed().
999
* Return value: whether the range's slider has a fixed size.
1004
gtk_range_get_slider_size_fixed (GtkRange *range)
1006
g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1008
return range->slider_size_fixed;
1012
* gtk_range_set_min_slider_size:
1013
* @range: a #GtkRange
1014
* @min_size: The slider's minimum size
1016
* Sets the minimum size of the range's slider.
1018
* This function is useful mainly for #GtkRange subclasses.
1023
gtk_range_set_min_slider_size (GtkRange *range,
1026
g_return_if_fail (GTK_IS_RANGE (range));
1027
g_return_if_fail (min_size > 0);
1029
if (min_size != range->min_slider_size)
1031
range->min_slider_size = min_size;
1033
range->need_recalc = TRUE;
1034
gtk_range_calc_layout (range, range->adjustment->value);
1035
gtk_widget_queue_draw (GTK_WIDGET (range));
1040
* gtk_range_get_min_slider_size:
1041
* @range: a #GtkRange
1043
* This function is useful mainly for #GtkRange subclasses.
1045
* See gtk_range_set_min_slider_size().
1047
* Return value: The minimum size of the range's slider.
1052
gtk_range_get_min_slider_size (GtkRange *range)
1054
g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1056
return range->min_slider_size;
1060
* gtk_range_get_range_rect:
1061
* @range: a #GtkRange
1062
* @range_rect: (out): return location for the range rectangle
1064
* This function returns the area that contains the range's trough
1065
* and its steppers, in widget->window coordinates.
1067
* This function is useful mainly for #GtkRange subclasses.
1072
gtk_range_get_range_rect (GtkRange *range,
1073
GdkRectangle *range_rect)
1075
g_return_if_fail (GTK_IS_RANGE (range));
1076
g_return_if_fail (range_rect != NULL);
1078
gtk_range_calc_layout (range, range->adjustment->value);
1080
*range_rect = range->range_rect;
1084
* gtk_range_get_slider_range:
1085
* @range: a #GtkRange
1086
* @slider_start: (out) (allow-none): return location for the slider's
1088
* @slider_end: (out) (allow-none): return location for the slider's
1091
* This function returns sliders range along the long dimension,
1092
* in widget->window coordinates.
1094
* This function is useful mainly for #GtkRange subclasses.
1099
gtk_range_get_slider_range (GtkRange *range,
1103
g_return_if_fail (GTK_IS_RANGE (range));
1105
gtk_range_calc_layout (range, range->adjustment->value);
1108
*slider_start = range->slider_start;
1111
*slider_end = range->slider_end;
1115
* gtk_range_set_lower_stepper_sensitivity:
1116
* @range: a #GtkRange
1117
* @sensitivity: the lower stepper's sensitivity policy.
1119
* Sets the sensitivity policy for the stepper that points to the
1120
* 'lower' end of the GtkRange's adjustment.
1125
gtk_range_set_lower_stepper_sensitivity (GtkRange *range,
1126
GtkSensitivityType sensitivity)
1128
g_return_if_fail (GTK_IS_RANGE (range));
1130
if (range->layout->lower_sensitivity != sensitivity)
1132
range->layout->lower_sensitivity = sensitivity;
1134
range->need_recalc = TRUE;
1135
gtk_range_calc_layout (range, range->adjustment->value);
1136
gtk_widget_queue_draw (GTK_WIDGET (range));
1138
g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
1143
* gtk_range_get_lower_stepper_sensitivity:
1144
* @range: a #GtkRange
1146
* Gets the sensitivity policy for the stepper that points to the
1147
* 'lower' end of the GtkRange's adjustment.
1149
* Return value: The lower stepper's sensitivity policy.
1154
gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
1156
g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1158
return range->layout->lower_sensitivity;
1162
* gtk_range_set_upper_stepper_sensitivity:
1163
* @range: a #GtkRange
1164
* @sensitivity: the upper stepper's sensitivity policy.
1166
* Sets the sensitivity policy for the stepper that points to the
1167
* 'upper' end of the GtkRange's adjustment.
1172
gtk_range_set_upper_stepper_sensitivity (GtkRange *range,
1173
GtkSensitivityType sensitivity)
1175
g_return_if_fail (GTK_IS_RANGE (range));
1177
if (range->layout->upper_sensitivity != sensitivity)
1179
range->layout->upper_sensitivity = sensitivity;
1181
range->need_recalc = TRUE;
1182
gtk_range_calc_layout (range, range->adjustment->value);
1183
gtk_widget_queue_draw (GTK_WIDGET (range));
1185
g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
1190
* gtk_range_get_upper_stepper_sensitivity:
1191
* @range: a #GtkRange
1193
* Gets the sensitivity policy for the stepper that points to the
1194
* 'upper' end of the GtkRange's adjustment.
1196
* Return value: The upper stepper's sensitivity policy.
1201
gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
1203
g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
1205
return range->layout->upper_sensitivity;
1209
* gtk_range_set_increments:
1210
* @range: a #GtkRange
1214
* Sets the step and page sizes for the range.
1215
* The step size is used when the user clicks the #GtkScrollbar
1216
* arrows or moves #GtkScale via arrow keys. The page size
1217
* is used for example when moving via Page Up or Page Down keys.
1220
gtk_range_set_increments (GtkRange *range,
1224
g_return_if_fail (GTK_IS_RANGE (range));
1226
range->adjustment->step_increment = step;
1227
range->adjustment->page_increment = page;
1229
gtk_adjustment_changed (range->adjustment);
1233
* gtk_range_set_range:
1234
* @range: a #GtkRange
1235
* @min: minimum range value
1236
* @max: maximum range value
1238
* Sets the allowable values in the #GtkRange, and clamps the range
1239
* value to be between @min and @max. (If the range has a non-zero
1240
* page size, it is clamped between @min and @max - page-size.)
1243
gtk_range_set_range (GtkRange *range,
1249
g_return_if_fail (GTK_IS_RANGE (range));
1250
g_return_if_fail (min < max);
1252
range->adjustment->lower = min;
1253
range->adjustment->upper = max;
1255
value = range->adjustment->value;
1257
if (range->layout->restrict_to_fill_level)
1258
value = MIN (value, MAX (range->adjustment->lower,
1259
range->layout->fill_level));
1261
value = CLAMP (value, range->adjustment->lower,
1262
(range->adjustment->upper - range->adjustment->page_size));
1264
gtk_adjustment_set_value (range->adjustment, value);
1265
gtk_adjustment_changed (range->adjustment);
1269
* gtk_range_set_value:
1270
* @range: a #GtkRange
1271
* @value: new value of the range
1273
* Sets the current value of the range; if the value is outside the
1274
* minimum or maximum range values, it will be clamped to fit inside
1275
* them. The range emits the #GtkRange::value-changed signal if the
1279
gtk_range_set_value (GtkRange *range,
1282
g_return_if_fail (GTK_IS_RANGE (range));
1284
if (range->layout->restrict_to_fill_level)
1285
value = MIN (value, MAX (range->adjustment->lower,
1286
range->layout->fill_level));
1288
value = CLAMP (value, range->adjustment->lower,
1289
(range->adjustment->upper - range->adjustment->page_size));
1291
gtk_adjustment_set_value (range->adjustment, value);
1295
* gtk_range_get_value:
1296
* @range: a #GtkRange
1298
* Gets the current value of the range.
1300
* Return value: current value of the range.
1303
gtk_range_get_value (GtkRange *range)
1305
g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1307
return range->adjustment->value;
1311
* gtk_range_set_show_fill_level:
1312
* @range: A #GtkRange
1313
* @show_fill_level: Whether a fill level indicator graphics is shown.
1315
* Sets whether a graphical fill level is show on the trough. See
1316
* gtk_range_set_fill_level() for a general description of the fill
1322
gtk_range_set_show_fill_level (GtkRange *range,
1323
gboolean show_fill_level)
1325
g_return_if_fail (GTK_IS_RANGE (range));
1327
show_fill_level = show_fill_level ? TRUE : FALSE;
1329
if (show_fill_level != range->layout->show_fill_level)
1331
range->layout->show_fill_level = show_fill_level;
1332
g_object_notify (G_OBJECT (range), "show-fill-level");
1333
gtk_widget_queue_draw (GTK_WIDGET (range));
1338
* gtk_range_get_show_fill_level:
1339
* @range: A #GtkRange
1341
* Gets whether the range displays the fill level graphically.
1343
* Return value: %TRUE if @range shows the fill level.
1348
gtk_range_get_show_fill_level (GtkRange *range)
1350
g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1352
return range->layout->show_fill_level;
1356
* gtk_range_set_restrict_to_fill_level:
1357
* @range: A #GtkRange
1358
* @restrict_to_fill_level: Whether the fill level restricts slider movement.
1360
* Sets whether the slider is restricted to the fill level. See
1361
* gtk_range_set_fill_level() for a general description of the fill
1367
gtk_range_set_restrict_to_fill_level (GtkRange *range,
1368
gboolean restrict_to_fill_level)
1370
g_return_if_fail (GTK_IS_RANGE (range));
1372
restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
1374
if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
1376
range->layout->restrict_to_fill_level = restrict_to_fill_level;
1377
g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
1379
gtk_range_set_value (range, gtk_range_get_value (range));
1384
* gtk_range_get_restrict_to_fill_level:
1385
* @range: A #GtkRange
1387
* Gets whether the range is restricted to the fill level.
1389
* Return value: %TRUE if @range is restricted to the fill level.
1394
gtk_range_get_restrict_to_fill_level (GtkRange *range)
1396
g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
1398
return range->layout->restrict_to_fill_level;
1402
* gtk_range_set_fill_level:
1403
* @range: a #GtkRange
1404
* @fill_level: the new position of the fill level indicator
1406
* Set the new position of the fill level indicator.
1408
* The "fill level" is probably best described by its most prominent
1409
* use case, which is an indicator for the amount of pre-buffering in
1410
* a streaming media player. In that use case, the value of the range
1411
* would indicate the current play position, and the fill level would
1412
* be the position up to which the file/stream has been downloaded.
1414
* This amount of prebuffering can be displayed on the range's trough
1415
* and is themeable separately from the trough. To enable fill level
1416
* display, use gtk_range_set_show_fill_level(). The range defaults
1417
* to not showing the fill level.
1419
* Additionally, it's possible to restrict the range's slider position
1420
* to values which are smaller than the fill level. This is controller
1421
* by gtk_range_set_restrict_to_fill_level() and is by default
1427
gtk_range_set_fill_level (GtkRange *range,
1430
g_return_if_fail (GTK_IS_RANGE (range));
1432
if (fill_level != range->layout->fill_level)
1434
range->layout->fill_level = fill_level;
1435
g_object_notify (G_OBJECT (range), "fill-level");
1437
if (range->layout->show_fill_level)
1438
gtk_widget_queue_draw (GTK_WIDGET (range));
1440
if (range->layout->restrict_to_fill_level)
1441
gtk_range_set_value (range, gtk_range_get_value (range));
1446
* gtk_range_get_fill_level:
1447
* @range: A #GtkRange
1449
* Gets the current position of the fill level indicator.
1451
* Return value: The current fill level
1456
gtk_range_get_fill_level (GtkRange *range)
1458
g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
1460
return range->layout->fill_level;
1464
should_invert (GtkRange *range)
1466
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1468
(range->inverted && !range->flippable) ||
1469
(range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
1470
(!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
1472
return range->inverted;
1476
gtk_range_destroy (GtkObject *object)
1478
GtkRange *range = GTK_RANGE (object);
1480
gtk_range_remove_step_timer (range);
1481
gtk_range_remove_update_timer (range);
1483
if (range->layout->repaint_id)
1484
g_source_remove (range->layout->repaint_id);
1485
range->layout->repaint_id = 0;
1487
if (range->adjustment)
1489
g_signal_handlers_disconnect_by_func (range->adjustment,
1490
gtk_range_adjustment_changed,
1492
g_signal_handlers_disconnect_by_func (range->adjustment,
1493
gtk_range_adjustment_value_changed,
1495
g_object_unref (range->adjustment);
1496
range->adjustment = NULL;
1499
if (range->layout->n_marks)
1501
g_free (range->layout->marks);
1502
range->layout->marks = NULL;
1503
g_free (range->layout->mark_pos);
1504
range->layout->mark_pos = NULL;
1505
range->layout->n_marks = 0;
1508
GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
1512
gtk_range_size_request (GtkWidget *widget,
1513
GtkRequisition *requisition)
1516
gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1517
GdkRectangle range_rect;
1520
range = GTK_RANGE (widget);
1522
gtk_range_get_props (range,
1523
&slider_width, &stepper_size,
1524
&focus_width, &trough_border,
1525
&stepper_spacing, NULL,
1528
gtk_range_calc_request (range,
1529
slider_width, stepper_size,
1530
focus_width, trough_border, stepper_spacing,
1531
&range_rect, &border, NULL, NULL, NULL, NULL);
1533
requisition->width = range_rect.width + border.left + border.right;
1534
requisition->height = range_rect.height + border.top + border.bottom;
1538
gtk_range_size_allocate (GtkWidget *widget,
1539
GtkAllocation *allocation)
1543
range = GTK_RANGE (widget);
1545
widget->allocation = *allocation;
1547
range->layout->recalc_marks = TRUE;
1549
range->need_recalc = TRUE;
1550
gtk_range_calc_layout (range, range->adjustment->value);
1552
if (gtk_widget_get_realized (widget))
1553
gdk_window_move_resize (range->event_window,
1554
widget->allocation.x,
1555
widget->allocation.y,
1556
widget->allocation.width,
1557
widget->allocation.height);
1561
gtk_range_realize (GtkWidget *widget)
1564
GdkWindowAttr attributes;
1565
gint attributes_mask;
1567
range = GTK_RANGE (widget);
1569
gtk_range_calc_layout (range, range->adjustment->value);
1571
gtk_widget_set_realized (widget, TRUE);
1573
widget->window = gtk_widget_get_parent_window (widget);
1574
g_object_ref (widget->window);
1576
attributes.window_type = GDK_WINDOW_CHILD;
1577
attributes.x = widget->allocation.x;
1578
attributes.y = widget->allocation.y;
1579
attributes.width = widget->allocation.width;
1580
attributes.height = widget->allocation.height;
1581
attributes.wclass = GDK_INPUT_ONLY;
1582
attributes.event_mask = gtk_widget_get_events (widget);
1583
attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1584
GDK_BUTTON_RELEASE_MASK |
1585
GDK_ENTER_NOTIFY_MASK |
1586
GDK_LEAVE_NOTIFY_MASK |
1587
GDK_POINTER_MOTION_MASK |
1588
GDK_POINTER_MOTION_HINT_MASK);
1590
attributes_mask = GDK_WA_X | GDK_WA_Y;
1592
range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1593
&attributes, attributes_mask);
1594
gdk_window_set_user_data (range->event_window, range);
1596
widget->style = gtk_style_attach (widget->style, widget->window);
1600
gtk_range_unrealize (GtkWidget *widget)
1602
GtkRange *range = GTK_RANGE (widget);
1604
gtk_range_remove_step_timer (range);
1605
gtk_range_remove_update_timer (range);
1607
gdk_window_set_user_data (range->event_window, NULL);
1608
gdk_window_destroy (range->event_window);
1609
range->event_window = NULL;
1611
GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
1615
gtk_range_map (GtkWidget *widget)
1617
GtkRange *range = GTK_RANGE (widget);
1619
gdk_window_show (range->event_window);
1621
GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1625
gtk_range_unmap (GtkWidget *widget)
1627
GtkRange *range = GTK_RANGE (widget);
1629
stop_scrolling (range);
1631
gdk_window_hide (range->event_window);
1633
GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1636
static const gchar *
1637
gtk_range_get_slider_detail (GtkRange *range)
1639
const gchar *slider_detail;
1641
if (range->layout->slider_detail_quark)
1642
return g_quark_to_string (range->layout->slider_detail_quark);
1644
slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
1646
if (slider_detail && slider_detail[0] == 'X')
1648
gchar *detail = g_strdup (slider_detail);
1650
detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1652
range->layout->slider_detail_quark = g_quark_from_string (detail);
1656
return g_quark_to_string (range->layout->slider_detail_quark);
1659
return slider_detail;
1662
static const gchar *
1663
gtk_range_get_stepper_detail (GtkRange *range,
1666
const gchar *stepper_detail;
1667
gboolean need_orientation;
1668
gboolean need_position;
1670
if (range->layout->stepper_detail_quark[stepper])
1671
return g_quark_to_string (range->layout->stepper_detail_quark[stepper]);
1673
stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
1675
need_orientation = stepper_detail && stepper_detail[0] == 'X';
1677
gtk_widget_style_get (GTK_WIDGET (range),
1678
"stepper-position-details", &need_position,
1681
if (need_orientation || need_position)
1684
const gchar *position = NULL;
1691
position = "_start";
1694
if (range->has_stepper_a)
1695
position = "_middle";
1697
position = "_start";
1700
if (range->has_stepper_d)
1701
position = "_middle";
1709
g_assert_not_reached ();
1713
detail = g_strconcat (stepper_detail, position, NULL);
1715
if (need_orientation)
1716
detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
1718
range->layout->stepper_detail_quark[stepper] = g_quark_from_string (detail);
1722
return g_quark_to_string (range->layout->stepper_detail_quark[stepper]);
1725
return stepper_detail;
1729
draw_stepper (GtkRange *range,
1731
GtkArrowType arrow_type,
1733
gboolean prelighted,
1736
GtkStateType state_type;
1737
GtkShadowType shadow_type;
1738
GdkRectangle intersection;
1739
GtkWidget *widget = GTK_WIDGET (range);
1740
gfloat arrow_scaling;
1746
gboolean arrow_sensitive = TRUE;
1751
rect = &range->layout->stepper_a;
1754
rect = &range->layout->stepper_b;
1757
rect = &range->layout->stepper_c;
1760
rect = &range->layout->stepper_d;
1763
g_assert_not_reached ();
1766
/* More to get the right clip region than for efficiency */
1767
if (!gdk_rectangle_intersect (area, rect, &intersection))
1770
intersection.x += widget->allocation.x;
1771
intersection.y += widget->allocation.y;
1773
if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
1774
arrow_type == GTK_ARROW_RIGHT)) ||
1775
(range->inverted && (arrow_type == GTK_ARROW_UP ||
1776
arrow_type == GTK_ARROW_LEFT)))
1778
arrow_sensitive = range->layout->upper_sensitive;
1782
arrow_sensitive = range->layout->lower_sensitive;
1785
if (!gtk_widget_is_sensitive (GTK_WIDGET (range)) || !arrow_sensitive)
1786
state_type = GTK_STATE_INSENSITIVE;
1788
state_type = GTK_STATE_ACTIVE;
1789
else if (prelighted)
1790
state_type = GTK_STATE_PRELIGHT;
1792
state_type = GTK_STATE_NORMAL;
1794
if (clicked && arrow_sensitive)
1795
shadow_type = GTK_SHADOW_IN;
1797
shadow_type = GTK_SHADOW_OUT;
1799
gtk_paint_box (widget->style,
1801
state_type, shadow_type,
1802
&intersection, widget,
1803
gtk_range_get_stepper_detail (range, stepper),
1804
widget->allocation.x + rect->x,
1805
widget->allocation.y + rect->y,
1809
gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
1811
arrow_width = rect->width * arrow_scaling;
1812
arrow_height = rect->height * arrow_scaling;
1813
arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
1814
arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
1816
if (clicked && arrow_sensitive)
1818
gint arrow_displacement_x;
1819
gint arrow_displacement_y;
1821
gtk_range_get_props (GTK_RANGE (widget),
1822
NULL, NULL, NULL, NULL, NULL, NULL,
1823
&arrow_displacement_x, &arrow_displacement_y);
1825
arrow_x += arrow_displacement_x;
1826
arrow_y += arrow_displacement_y;
1829
gtk_paint_arrow (widget->style,
1831
state_type, shadow_type,
1832
&intersection, widget,
1833
gtk_range_get_stepper_detail (range, stepper),
1836
arrow_x, arrow_y, arrow_width, arrow_height);
1840
gtk_range_expose (GtkWidget *widget,
1841
GdkEventExpose *event)
1843
GtkRange *range = GTK_RANGE (widget);
1846
GtkShadowType shadow_type;
1847
GdkRectangle expose_area; /* Relative to widget->allocation */
1849
gint focus_line_width = 0;
1850
gint focus_padding = 0;
1851
gboolean touchscreen;
1853
g_object_get (gtk_widget_get_settings (widget),
1854
"gtk-touchscreen-mode", &touchscreen,
1856
if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
1857
gtk_widget_style_get (GTK_WIDGET (range),
1858
"focus-line-width", &focus_line_width,
1859
"focus-padding", &focus_padding,
1862
/* we're now exposing, so there's no need to force early repaints */
1863
if (range->layout->repaint_id)
1864
g_source_remove (range->layout->repaint_id);
1865
range->layout->repaint_id = 0;
1867
expose_area = event->area;
1868
expose_area.x -= widget->allocation.x;
1869
expose_area.y -= widget->allocation.y;
1871
gtk_range_calc_marks (range);
1872
gtk_range_calc_layout (range, range->adjustment->value);
1874
sensitive = gtk_widget_is_sensitive (widget);
1876
/* Just to be confusing, we draw the trough for the whole
1877
* range rectangle, not the trough rectangle (the trough
1878
* rectangle is just for hit detection)
1880
/* The gdk_rectangle_intersect is more to get the right
1881
* clip region (limited to range_rect) than for efficiency
1883
if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
1886
gint x = (widget->allocation.x + range->range_rect.x +
1887
focus_line_width + focus_padding);
1888
gint y = (widget->allocation.y + range->range_rect.y +
1889
focus_line_width + focus_padding);
1890
gint width = (range->range_rect.width -
1891
2 * (focus_line_width + focus_padding));
1892
gint height = (range->range_rect.height -
1893
2 * (focus_line_width + focus_padding));
1894
gboolean trough_side_details;
1895
gboolean trough_under_steppers;
1897
gint stepper_spacing;
1899
area.x += widget->allocation.x;
1900
area.y += widget->allocation.y;
1902
gtk_widget_style_get (GTK_WIDGET (range),
1903
"trough-side-details", &trough_side_details,
1904
"trough-under-steppers", &trough_under_steppers,
1905
"stepper-size", &stepper_size,
1906
"stepper-spacing", &stepper_spacing,
1909
if (stepper_spacing > 0)
1910
trough_under_steppers = FALSE;
1912
if (! trough_under_steppers)
1917
if (range->has_stepper_a)
1918
offset += stepper_size;
1920
if (range->has_stepper_b)
1921
offset += stepper_size;
1925
if (range->has_stepper_c)
1926
shorter += stepper_size;
1928
if (range->has_stepper_d)
1929
shorter += stepper_size;
1931
if (range->has_stepper_a || range->has_stepper_b)
1933
offset += stepper_spacing;
1934
shorter += stepper_spacing;
1937
if (range->has_stepper_c || range->has_stepper_d)
1939
shorter += stepper_spacing;
1942
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1954
if (! trough_side_details)
1956
gtk_paint_box (widget->style,
1958
sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1960
&area, GTK_WIDGET(range), "trough",
1966
gint trough_change_pos_x = width;
1967
gint trough_change_pos_y = height;
1969
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1970
trough_change_pos_x = (range->layout->slider.x +
1971
range->layout->slider.width / 2 -
1972
(x - widget->allocation.x));
1974
trough_change_pos_y = (range->layout->slider.y +
1975
range->layout->slider.height / 2 -
1976
(y - widget->allocation.y));
1978
gtk_paint_box (widget->style,
1980
sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1982
&area, GTK_WIDGET (range),
1983
should_invert (range) ? "trough-upper" : "trough-lower",
1985
trough_change_pos_x, trough_change_pos_y);
1987
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
1988
trough_change_pos_y = 0;
1990
trough_change_pos_x = 0;
1992
gtk_paint_box (widget->style,
1994
sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
1996
&area, GTK_WIDGET (range),
1997
should_invert (range) ? "trough-lower" : "trough-upper",
1998
x + trough_change_pos_x, y + trough_change_pos_y,
1999
width - trough_change_pos_x,
2000
height - trough_change_pos_y);
2003
if (range->layout->show_fill_level &&
2004
range->adjustment->upper - range->adjustment->page_size -
2005
range->adjustment->lower != 0)
2007
gdouble fill_level = range->layout->fill_level;
2010
gint fill_width = width;
2011
gint fill_height = height;
2014
fill_level = CLAMP (fill_level, range->adjustment->lower,
2015
range->adjustment->upper -
2016
range->adjustment->page_size);
2018
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2020
fill_x = widget->allocation.x + range->layout->trough.x;
2021
fill_width = (range->layout->slider.width +
2022
(fill_level - range->adjustment->lower) /
2023
(range->adjustment->upper -
2024
range->adjustment->lower -
2025
range->adjustment->page_size) *
2026
(range->layout->trough.width -
2027
range->layout->slider.width));
2029
if (should_invert (range))
2030
fill_x += range->layout->trough.width - fill_width;
2034
fill_y = widget->allocation.y + range->layout->trough.y;
2035
fill_height = (range->layout->slider.height +
2036
(fill_level - range->adjustment->lower) /
2037
(range->adjustment->upper -
2038
range->adjustment->lower -
2039
range->adjustment->page_size) *
2040
(range->layout->trough.height -
2041
range->layout->slider.height));
2043
if (should_invert (range))
2044
fill_y += range->layout->trough.height - fill_height;
2047
if (fill_level < range->adjustment->upper - range->adjustment->page_size)
2048
fill_detail = "trough-fill-level-full";
2050
fill_detail = "trough-fill-level";
2052
gtk_paint_box (widget->style,
2054
sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
2056
&area, GTK_WIDGET (range), fill_detail,
2058
fill_width, fill_height);
2061
if (sensitive && gtk_widget_has_focus (widget))
2062
gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
2063
&area, widget, "trough",
2064
widget->allocation.x + range->range_rect.x,
2065
widget->allocation.y + range->range_rect.y,
2066
range->range_rect.width,
2067
range->range_rect.height);
2070
shadow_type = GTK_SHADOW_OUT;
2073
state = GTK_STATE_INSENSITIVE;
2074
else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
2075
state = GTK_STATE_PRELIGHT;
2077
state = GTK_STATE_NORMAL;
2079
if (range->layout->grab_location == MOUSE_SLIDER)
2081
gboolean activate_slider;
2083
gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2085
if (activate_slider)
2087
state = GTK_STATE_ACTIVE;
2088
shadow_type = GTK_SHADOW_IN;
2092
if (gdk_rectangle_intersect (&expose_area,
2093
&range->layout->slider,
2096
area.x += widget->allocation.x;
2097
area.y += widget->allocation.y;
2099
gtk_paint_slider (widget->style,
2105
gtk_range_get_slider_detail (range),
2106
widget->allocation.x + range->layout->slider.x,
2107
widget->allocation.y + range->layout->slider.y,
2108
range->layout->slider.width,
2109
range->layout->slider.height,
2110
range->orientation);
2113
if (range->has_stepper_a)
2114
draw_stepper (range, STEPPER_A,
2115
range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2116
range->layout->grab_location == MOUSE_STEPPER_A,
2117
!touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
2120
if (range->has_stepper_b)
2121
draw_stepper (range, STEPPER_B,
2122
range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2123
range->layout->grab_location == MOUSE_STEPPER_B,
2124
!touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
2127
if (range->has_stepper_c)
2128
draw_stepper (range, STEPPER_C,
2129
range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
2130
range->layout->grab_location == MOUSE_STEPPER_C,
2131
!touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
2134
if (range->has_stepper_d)
2135
draw_stepper (range, STEPPER_D,
2136
range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
2137
range->layout->grab_location == MOUSE_STEPPER_D,
2138
!touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
2145
range_grab_add (GtkRange *range,
2146
MouseLocation location,
2149
/* we don't actually gtk_grab, since a button is down */
2151
gtk_grab_add (GTK_WIDGET (range));
2153
range->layout->grab_location = location;
2154
range->layout->grab_button = button;
2156
if (gtk_range_update_mouse_location (range))
2157
gtk_widget_queue_draw (GTK_WIDGET (range));
2161
range_grab_remove (GtkRange *range)
2163
MouseLocation location;
2165
gtk_grab_remove (GTK_WIDGET (range));
2167
location = range->layout->grab_location;
2168
range->layout->grab_location = MOUSE_OUTSIDE;
2169
range->layout->grab_button = 0;
2171
if (gtk_range_update_mouse_location (range) ||
2172
location != MOUSE_OUTSIDE)
2173
gtk_widget_queue_draw (GTK_WIDGET (range));
2176
static GtkScrollType
2177
range_get_scroll_for_grab (GtkRange *range)
2181
invert = should_invert (range);
2182
switch (range->layout->grab_location)
2184
/* Backward stepper */
2185
case MOUSE_STEPPER_A:
2186
case MOUSE_STEPPER_C:
2187
switch (range->layout->grab_button)
2190
return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
2193
return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
2196
return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
2201
/* Forward stepper */
2202
case MOUSE_STEPPER_B:
2203
case MOUSE_STEPPER_D:
2204
switch (range->layout->grab_button)
2207
return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
2210
return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
2213
return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
2221
if (range->trough_click_forward)
2222
return GTK_SCROLL_PAGE_FORWARD;
2224
return GTK_SCROLL_PAGE_BACKWARD;
2234
return GTK_SCROLL_NONE;
2238
coord_to_value (GtkRange *range,
2247
gint trough_under_steppers;
2249
if (range->orientation == GTK_ORIENTATION_VERTICAL)
2251
trough_length = range->layout->trough.height;
2252
trough_start = range->layout->trough.y;
2253
slider_length = range->layout->slider.height;
2257
trough_length = range->layout->trough.width;
2258
trough_start = range->layout->trough.x;
2259
slider_length = range->layout->slider.width;
2262
gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
2263
&trough_under_steppers, NULL, NULL);
2265
if (! trough_under_steppers)
2267
trough_start += trough_border;
2268
trough_length -= 2 * trough_border;
2271
if (trough_length == slider_length)
2274
frac = (MAX (0, coord - trough_start) /
2275
(gdouble) (trough_length - slider_length));
2277
if (should_invert (range))
2280
value = range->adjustment->lower + frac * (range->adjustment->upper -
2281
range->adjustment->lower -
2282
range->adjustment->page_size);
2288
gtk_range_key_press (GtkWidget *widget,
2291
GtkRange *range = GTK_RANGE (widget);
2293
if (event->keyval == GDK_Escape &&
2294
range->layout->grab_location != MOUSE_OUTSIDE)
2296
stop_scrolling (range);
2298
update_slider_position (range,
2299
range->slide_initial_coordinate,
2300
range->slide_initial_coordinate);
2305
return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
2309
gtk_range_button_press (GtkWidget *widget,
2310
GdkEventButton *event)
2312
GtkRange *range = GTK_RANGE (widget);
2314
if (!gtk_widget_has_focus (widget))
2315
gtk_widget_grab_focus (widget);
2317
/* ignore presses when we're already doing something else. */
2318
if (range->layout->grab_location != MOUSE_OUTSIDE)
2321
range->layout->mouse_x = event->x;
2322
range->layout->mouse_y = event->y;
2323
if (gtk_range_update_mouse_location (range))
2324
gtk_widget_queue_draw (widget);
2326
if (range->layout->mouse_location == MOUSE_TROUGH &&
2329
/* button 1 steps by page increment, as with button 2 on a stepper
2331
GtkScrollType scroll;
2332
gdouble click_value;
2334
click_value = coord_to_value (range,
2335
range->orientation == GTK_ORIENTATION_VERTICAL ?
2336
event->y : event->x);
2338
range->trough_click_forward = click_value > range->adjustment->value;
2339
range_grab_add (range, MOUSE_TROUGH, event->button);
2341
scroll = range_get_scroll_for_grab (range);
2343
gtk_range_add_step_timer (range, scroll);
2347
else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
2348
range->layout->mouse_location == MOUSE_STEPPER_B ||
2349
range->layout->mouse_location == MOUSE_STEPPER_C ||
2350
range->layout->mouse_location == MOUSE_STEPPER_D) &&
2351
(event->button == 1 || event->button == 2 || event->button == 3))
2353
GdkRectangle *stepper_area;
2354
GtkScrollType scroll;
2356
range_grab_add (range, range->layout->mouse_location, event->button);
2358
stepper_area = get_area (range, range->layout->mouse_location);
2359
gtk_widget_queue_draw_area (widget,
2360
widget->allocation.x + stepper_area->x,
2361
widget->allocation.y + stepper_area->y,
2362
stepper_area->width,
2363
stepper_area->height);
2365
scroll = range_get_scroll_for_grab (range);
2366
if (scroll != GTK_SCROLL_NONE)
2367
gtk_range_add_step_timer (range, scroll);
2371
else if ((range->layout->mouse_location == MOUSE_TROUGH &&
2372
event->button == 2) ||
2373
range->layout->mouse_location == MOUSE_SLIDER)
2375
gboolean need_value_update = FALSE;
2376
gboolean activate_slider;
2378
/* Any button can be used to drag the slider, but you can start
2379
* dragging the slider with a trough click using button 2;
2380
* On button 2 press, we warp the slider to mouse position,
2381
* then begin the slider drag.
2383
if (event->button == 2)
2385
gdouble slider_low_value, slider_high_value, new_value;
2388
coord_to_value (range,
2389
range->orientation == GTK_ORIENTATION_VERTICAL ?
2390
event->y : event->x);
2392
coord_to_value (range,
2393
range->orientation == GTK_ORIENTATION_VERTICAL ?
2394
event->y - range->layout->slider.height :
2395
event->x - range->layout->slider.width);
2397
/* compute new value for warped slider */
2398
new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
2400
/* recalc slider, so we can set slide_initial_slider_position
2403
range->need_recalc = TRUE;
2404
gtk_range_calc_layout (range, new_value);
2406
/* defer adjustment updates to update_slider_position() in order
2407
* to keep pixel quantisation
2409
need_value_update = TRUE;
2412
if (range->orientation == GTK_ORIENTATION_VERTICAL)
2414
range->slide_initial_slider_position = range->layout->slider.y;
2415
range->slide_initial_coordinate = event->y;
2419
range->slide_initial_slider_position = range->layout->slider.x;
2420
range->slide_initial_coordinate = event->x;
2423
range_grab_add (range, MOUSE_SLIDER, event->button);
2425
gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
2427
/* force a redraw, if the active slider is drawn differently to the
2430
if (activate_slider)
2431
gtk_widget_queue_draw (widget);
2433
if (need_value_update)
2434
update_slider_position (range, event->x, event->y);
2442
/* During a slide, move the slider as required given new mouse position */
2444
update_slider_position (GtkRange *range,
2457
if (range->orientation == GTK_ORIENTATION_VERTICAL)
2458
delta = mouse_y - range->slide_initial_coordinate;
2460
delta = mouse_x - range->slide_initial_coordinate;
2462
c = range->slide_initial_slider_position + delta;
2464
new_value = coord_to_value (range, c);
2465
next_value = coord_to_value (range, c + 1);
2466
mark_delta = fabs (next_value - new_value);
2468
for (i = 0; i < range->layout->n_marks; i++)
2470
mark_value = range->layout->marks[i];
2472
if (fabs (range->adjustment->value - mark_value) < 3 * mark_delta)
2474
if (fabs (new_value - mark_value) < (range->slider_end - range->slider_start) * 0.5 * mark_delta)
2476
new_value = mark_value;
2482
g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
2487
stop_scrolling (GtkRange *range)
2489
range_grab_remove (range);
2490
gtk_range_remove_step_timer (range);
2491
/* Flush any pending discontinuous/delayed updates */
2492
gtk_range_update_value (range);
2496
gtk_range_grab_broken (GtkWidget *widget,
2497
GdkEventGrabBroken *event)
2499
GtkRange *range = GTK_RANGE (widget);
2501
if (range->layout->grab_location != MOUSE_OUTSIDE)
2503
if (range->layout->grab_location == MOUSE_SLIDER)
2504
update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2506
stop_scrolling (range);
2515
gtk_range_button_release (GtkWidget *widget,
2516
GdkEventButton *event)
2518
GtkRange *range = GTK_RANGE (widget);
2520
if (event->window == range->event_window)
2522
range->layout->mouse_x = event->x;
2523
range->layout->mouse_y = event->y;
2527
gdk_window_get_pointer (range->event_window,
2528
&range->layout->mouse_x,
2529
&range->layout->mouse_y,
2533
if (range->layout->grab_button == event->button)
2535
if (range->layout->grab_location == MOUSE_SLIDER)
2536
update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
2538
stop_scrolling (range);
2547
* _gtk_range_get_wheel_delta:
2548
* @range: a #GtkRange
2549
* @direction: A #GdkScrollDirection
2551
* Returns a good step value for the mouse wheel.
2553
* Return value: A good step value for the mouse wheel.
2558
_gtk_range_get_wheel_delta (GtkRange *range,
2559
GdkScrollDirection direction)
2561
GtkAdjustment *adj = range->adjustment;
2564
if (GTK_IS_SCROLLBAR (range))
2565
delta = pow (adj->page_size, 2.0 / 3.0);
2567
delta = adj->step_increment * 2;
2569
if (direction == GDK_SCROLL_UP ||
2570
direction == GDK_SCROLL_LEFT)
2573
if (range->inverted)
2580
gtk_range_scroll_event (GtkWidget *widget,
2581
GdkEventScroll *event)
2583
GtkRange *range = GTK_RANGE (widget);
2585
if (gtk_widget_get_realized (widget))
2587
GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
2591
delta = _gtk_range_get_wheel_delta (range, event->direction);
2593
g_signal_emit (range, signals[CHANGE_VALUE], 0,
2594
GTK_SCROLL_JUMP, adj->value + delta,
2597
/* Policy DELAYED makes sense with scroll events,
2598
* but DISCONTINUOUS doesn't, so we update immediately
2601
if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
2602
gtk_range_update_value (range);
2609
gtk_range_motion_notify (GtkWidget *widget,
2610
GdkEventMotion *event)
2614
range = GTK_RANGE (widget);
2616
gdk_event_request_motions (event);
2618
range->layout->mouse_x = event->x;
2619
range->layout->mouse_y = event->y;
2621
if (gtk_range_update_mouse_location (range))
2622
gtk_widget_queue_draw (widget);
2624
if (range->layout->grab_location == MOUSE_SLIDER)
2625
update_slider_position (range, event->x, event->y);
2627
/* We handled the event if the mouse was in the range_rect */
2628
return range->layout->mouse_location != MOUSE_OUTSIDE;
2632
gtk_range_enter_notify (GtkWidget *widget,
2633
GdkEventCrossing *event)
2635
GtkRange *range = GTK_RANGE (widget);
2637
range->layout->mouse_x = event->x;
2638
range->layout->mouse_y = event->y;
2640
if (gtk_range_update_mouse_location (range))
2641
gtk_widget_queue_draw (widget);
2647
gtk_range_leave_notify (GtkWidget *widget,
2648
GdkEventCrossing *event)
2650
GtkRange *range = GTK_RANGE (widget);
2652
range->layout->mouse_x = -1;
2653
range->layout->mouse_y = -1;
2655
if (gtk_range_update_mouse_location (range))
2656
gtk_widget_queue_draw (widget);
2662
gtk_range_grab_notify (GtkWidget *widget,
2663
gboolean was_grabbed)
2666
stop_scrolling (GTK_RANGE (widget));
2670
gtk_range_state_changed (GtkWidget *widget,
2671
GtkStateType previous_state)
2673
if (!gtk_widget_is_sensitive (widget))
2674
stop_scrolling (GTK_RANGE (widget));
2677
#define check_rectangle(rectangle1, rectangle2) \
2679
if (rectangle1.x != rectangle2.x) return TRUE; \
2680
if (rectangle1.y != rectangle2.y) return TRUE; \
2681
if (rectangle1.width != rectangle2.width) return TRUE; \
2682
if (rectangle1.height != rectangle2.height) return TRUE; \
2686
layout_changed (GtkRangeLayout *layout1,
2687
GtkRangeLayout *layout2)
2689
check_rectangle (layout1->slider, layout2->slider);
2690
check_rectangle (layout1->trough, layout2->trough);
2691
check_rectangle (layout1->stepper_a, layout2->stepper_a);
2692
check_rectangle (layout1->stepper_d, layout2->stepper_d);
2693
check_rectangle (layout1->stepper_b, layout2->stepper_b);
2694
check_rectangle (layout1->stepper_c, layout2->stepper_c);
2696
if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
2697
if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
2703
gtk_range_adjustment_changed (GtkAdjustment *adjustment,
2706
GtkRange *range = GTK_RANGE (data);
2707
/* create a copy of the layout */
2708
GtkRangeLayout layout = *range->layout;
2710
range->layout->recalc_marks = TRUE;
2711
range->need_recalc = TRUE;
2712
gtk_range_calc_layout (range, range->adjustment->value);
2714
/* now check whether the layout changed */
2715
if (layout_changed (range->layout, &layout))
2716
gtk_widget_queue_draw (GTK_WIDGET (range));
2718
/* Note that we don't round off to range->round_digits here.
2719
* that's because it's really broken to change a value
2720
* in response to a change signal on that value; round_digits
2721
* is therefore defined to be a filter on what the GtkRange
2722
* can input into the adjustment, not a filter that the GtkRange
2723
* will enforce on the adjustment.
2728
force_repaint (gpointer data)
2730
GtkRange *range = GTK_RANGE (data);
2732
range->layout->repaint_id = 0;
2733
if (gtk_widget_is_drawable (GTK_WIDGET (range)))
2734
gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
2740
gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
2743
GtkRange *range = GTK_RANGE (data);
2744
/* create a copy of the layout */
2745
GtkRangeLayout layout = *range->layout;
2747
range->need_recalc = TRUE;
2748
gtk_range_calc_layout (range, range->adjustment->value);
2750
/* now check whether the layout changed */
2751
if (layout_changed (range->layout, &layout) ||
2752
(GTK_IS_SCALE (range) && GTK_SCALE (range)->draw_value))
2754
gtk_widget_queue_draw (GTK_WIDGET (range));
2755
/* setup a timer to ensure the range isn't lagging too much behind the scroll position */
2756
if (!range->layout->repaint_id)
2757
range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
2760
/* Note that we don't round off to range->round_digits here.
2761
* that's because it's really broken to change a value
2762
* in response to a change signal on that value; round_digits
2763
* is therefore defined to be a filter on what the GtkRange
2764
* can input into the adjustment, not a filter that the GtkRange
2765
* will enforce on the adjustment.
2768
g_signal_emit (range, signals[VALUE_CHANGED], 0);
2772
gtk_range_style_set (GtkWidget *widget,
2773
GtkStyle *previous_style)
2775
GtkRange *range = GTK_RANGE (widget);
2777
range->need_recalc = TRUE;
2779
GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
2783
apply_marks (GtkRange *range,
2790
for (i = 0; i < range->layout->n_marks; i++)
2792
mark = range->layout->marks[i];
2793
if ((oldval < mark && mark < *newval) ||
2794
(oldval > mark && mark > *newval))
2803
step_back (GtkRange *range)
2808
newval = range->adjustment->value - range->adjustment->step_increment;
2809
apply_marks (range, range->adjustment->value, &newval);
2810
g_signal_emit (range, signals[CHANGE_VALUE], 0,
2811
GTK_SCROLL_STEP_BACKWARD, newval, &handled);
2815
step_forward (GtkRange *range)
2820
newval = range->adjustment->value + range->adjustment->step_increment;
2821
apply_marks (range, range->adjustment->value, &newval);
2822
g_signal_emit (range, signals[CHANGE_VALUE], 0,
2823
GTK_SCROLL_STEP_FORWARD, newval, &handled);
2828
page_back (GtkRange *range)
2833
newval = range->adjustment->value - range->adjustment->page_increment;
2834
apply_marks (range, range->adjustment->value, &newval);
2835
g_signal_emit (range, signals[CHANGE_VALUE], 0,
2836
GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
2840
page_forward (GtkRange *range)
2845
newval = range->adjustment->value + range->adjustment->page_increment;
2846
apply_marks (range, range->adjustment->value, &newval);
2847
g_signal_emit (range, signals[CHANGE_VALUE], 0,
2848
GTK_SCROLL_PAGE_FORWARD, newval, &handled);
2852
scroll_begin (GtkRange *range)
2855
g_signal_emit (range, signals[CHANGE_VALUE], 0,
2856
GTK_SCROLL_START, range->adjustment->lower,
2861
scroll_end (GtkRange *range)
2866
newval = range->adjustment->upper - range->adjustment->page_size;
2867
g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
2872
gtk_range_scroll (GtkRange *range,
2873
GtkScrollType scroll)
2875
gdouble old_value = range->adjustment->value;
2879
case GTK_SCROLL_STEP_LEFT:
2880
if (should_invert (range))
2881
step_forward (range);
2886
case GTK_SCROLL_STEP_UP:
2887
if (should_invert (range))
2888
step_forward (range);
2893
case GTK_SCROLL_STEP_RIGHT:
2894
if (should_invert (range))
2897
step_forward (range);
2900
case GTK_SCROLL_STEP_DOWN:
2901
if (should_invert (range))
2904
step_forward (range);
2907
case GTK_SCROLL_STEP_BACKWARD:
2911
case GTK_SCROLL_STEP_FORWARD:
2912
step_forward (range);
2915
case GTK_SCROLL_PAGE_LEFT:
2916
if (should_invert (range))
2917
page_forward (range);
2922
case GTK_SCROLL_PAGE_UP:
2923
if (should_invert (range))
2924
page_forward (range);
2929
case GTK_SCROLL_PAGE_RIGHT:
2930
if (should_invert (range))
2933
page_forward (range);
2936
case GTK_SCROLL_PAGE_DOWN:
2937
if (should_invert (range))
2940
page_forward (range);
2943
case GTK_SCROLL_PAGE_BACKWARD:
2947
case GTK_SCROLL_PAGE_FORWARD:
2948
page_forward (range);
2951
case GTK_SCROLL_START:
2952
scroll_begin (range);
2955
case GTK_SCROLL_END:
2959
case GTK_SCROLL_JUMP:
2960
/* Used by CList, range doesn't use it. */
2963
case GTK_SCROLL_NONE:
2967
return range->adjustment->value != old_value;
2971
gtk_range_move_slider (GtkRange *range,
2972
GtkScrollType scroll)
2974
gboolean cursor_only;
2976
g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
2977
"gtk-keynav-cursor-only", &cursor_only,
2982
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
2984
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
2986
if (scroll == GTK_SCROLL_STEP_UP ||
2987
scroll == GTK_SCROLL_STEP_DOWN)
2990
gtk_widget_child_focus (toplevel,
2991
scroll == GTK_SCROLL_STEP_UP ?
2992
GTK_DIR_UP : GTK_DIR_DOWN);
2998
if (scroll == GTK_SCROLL_STEP_LEFT ||
2999
scroll == GTK_SCROLL_STEP_RIGHT)
3002
gtk_widget_child_focus (toplevel,
3003
scroll == GTK_SCROLL_STEP_LEFT ?
3004
GTK_DIR_LEFT : GTK_DIR_RIGHT);
3010
if (! gtk_range_scroll (range, scroll))
3011
gtk_widget_error_bell (GTK_WIDGET (range));
3013
/* Policy DELAYED makes sense with key events,
3014
* but DISCONTINUOUS doesn't, so we update immediately
3017
if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
3018
gtk_range_update_value (range);
3022
gtk_range_get_props (GtkRange *range,
3026
gint *trough_border,
3027
gint *stepper_spacing,
3028
gboolean *trough_under_steppers,
3029
gint *arrow_displacement_x,
3030
gint *arrow_displacement_y)
3032
GtkWidget *widget = GTK_WIDGET (range);
3033
gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
3034
gint tmp_stepper_spacing, tmp_trough_under_steppers;
3035
gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
3037
gtk_widget_style_get (widget,
3038
"slider-width", &tmp_slider_width,
3039
"trough-border", &tmp_trough_border,
3040
"stepper-size", &tmp_stepper_size,
3041
"stepper-spacing", &tmp_stepper_spacing,
3042
"trough-under-steppers", &tmp_trough_under_steppers,
3043
"arrow-displacement-x", &tmp_arrow_displacement_x,
3044
"arrow-displacement-y", &tmp_arrow_displacement_y,
3047
if (tmp_stepper_spacing > 0)
3048
tmp_trough_under_steppers = FALSE;
3050
if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
3052
gint focus_line_width;
3055
gtk_widget_style_get (GTK_WIDGET (range),
3056
"focus-line-width", &focus_line_width,
3057
"focus-padding", &focus_padding,
3060
tmp_focus_width = focus_line_width + focus_padding;
3064
tmp_focus_width = 0;
3068
*slider_width = tmp_slider_width;
3071
*focus_width = tmp_focus_width;
3074
*trough_border = tmp_trough_border;
3077
*stepper_size = tmp_stepper_size;
3079
if (stepper_spacing)
3080
*stepper_spacing = tmp_stepper_spacing;
3082
if (trough_under_steppers)
3083
*trough_under_steppers = tmp_trough_under_steppers;
3085
if (arrow_displacement_x)
3086
*arrow_displacement_x = tmp_arrow_displacement_x;
3088
if (arrow_displacement_y)
3089
*arrow_displacement_y = tmp_arrow_displacement_y;
3092
#define POINT_IN_RECT(xcoord, ycoord, rect) \
3093
((xcoord) >= (rect).x && \
3094
(xcoord) < ((rect).x + (rect).width) && \
3095
(ycoord) >= (rect).y && \
3096
(ycoord) < ((rect).y + (rect).height))
3098
/* Update mouse location, return TRUE if it changes */
3100
gtk_range_update_mouse_location (GtkRange *range)
3106
widget = GTK_WIDGET (range);
3108
old = range->layout->mouse_location;
3110
x = range->layout->mouse_x;
3111
y = range->layout->mouse_y;
3113
if (range->layout->grab_location != MOUSE_OUTSIDE)
3114
range->layout->mouse_location = range->layout->grab_location;
3115
else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
3116
range->layout->mouse_location = MOUSE_STEPPER_A;
3117
else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
3118
range->layout->mouse_location = MOUSE_STEPPER_B;
3119
else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
3120
range->layout->mouse_location = MOUSE_STEPPER_C;
3121
else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
3122
range->layout->mouse_location = MOUSE_STEPPER_D;
3123
else if (POINT_IN_RECT (x, y, range->layout->slider))
3124
range->layout->mouse_location = MOUSE_SLIDER;
3125
else if (POINT_IN_RECT (x, y, range->layout->trough))
3126
range->layout->mouse_location = MOUSE_TROUGH;
3127
else if (POINT_IN_RECT (x, y, widget->allocation))
3128
range->layout->mouse_location = MOUSE_WIDGET;
3130
range->layout->mouse_location = MOUSE_OUTSIDE;
3132
return old != range->layout->mouse_location;
3135
/* Clamp rect, border inside widget->allocation, such that we prefer
3136
* to take space from border not rect in all directions, and prefer to
3137
* give space to border over rect in one direction.
3140
clamp_dimensions (GtkWidget *widget,
3143
gboolean border_expands_horizontally)
3145
gint extra, shortage;
3147
g_return_if_fail (rect->x == 0);
3148
g_return_if_fail (rect->y == 0);
3149
g_return_if_fail (rect->width >= 0);
3150
g_return_if_fail (rect->height >= 0);
3154
extra = widget->allocation.width - border->left - border->right - rect->width;
3157
if (border_expands_horizontally)
3159
border->left += extra / 2;
3160
border->right += extra / 2 + extra % 2;
3164
rect->width += extra;
3168
/* See if we can fit rect, if not kill the border */
3169
shortage = rect->width - widget->allocation.width;
3172
rect->width = widget->allocation.width;
3173
/* lose the border */
3179
/* See if we can fit rect with borders */
3180
shortage = rect->width + border->left + border->right -
3181
widget->allocation.width;
3184
/* Shrink borders */
3185
border->left -= shortage / 2;
3186
border->right -= shortage / 2 + shortage % 2;
3192
extra = widget->allocation.height - border->top - border->bottom - rect->height;
3195
if (border_expands_horizontally)
3197
/* don't expand border vertically */
3198
rect->height += extra;
3202
border->top += extra / 2;
3203
border->bottom += extra / 2 + extra % 2;
3207
/* See if we can fit rect, if not kill the border */
3208
shortage = rect->height - widget->allocation.height;
3211
rect->height = widget->allocation.height;
3212
/* lose the border */
3218
/* See if we can fit rect with borders */
3219
shortage = rect->height + border->top + border->bottom -
3220
widget->allocation.height;
3223
/* Shrink borders */
3224
border->top -= shortage / 2;
3225
border->bottom -= shortage / 2 + shortage % 2;
3231
gtk_range_calc_request (GtkRange *range,
3236
gint stepper_spacing,
3237
GdkRectangle *range_rect,
3240
gboolean *has_steppers_ab,
3241
gboolean *has_steppers_cd,
3242
gint *slider_length_p)
3254
if (GTK_RANGE_GET_CLASS (range)->get_range_border)
3255
GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
3260
if (range->has_stepper_a)
3262
if (range->has_stepper_b)
3264
if (range->has_stepper_c)
3266
if (range->has_stepper_d)
3269
n_steppers = n_steppers_ab + n_steppers_cd;
3271
slider_length = range->min_slider_size;
3276
/* We never expand to fill available space in the small dimension
3277
* (i.e. vertical scrollbars are always a fixed width)
3279
if (range->orientation == GTK_ORIENTATION_VERTICAL)
3281
range_rect->width = (focus_width + trough_border) * 2 + slider_width;
3282
range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3284
if (n_steppers_ab > 0)
3285
range_rect->height += stepper_spacing;
3287
if (n_steppers_cd > 0)
3288
range_rect->height += stepper_spacing;
3292
range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
3293
range_rect->height = (focus_width + trough_border) * 2 + slider_width;
3295
if (n_steppers_ab > 0)
3296
range_rect->width += stepper_spacing;
3298
if (n_steppers_cd > 0)
3299
range_rect->width += stepper_spacing;
3303
*n_steppers_p = n_steppers;
3305
if (has_steppers_ab)
3306
*has_steppers_ab = (n_steppers_ab > 0);
3308
if (has_steppers_cd)
3309
*has_steppers_cd = (n_steppers_cd > 0);
3311
if (slider_length_p)
3312
*slider_length_p = slider_length;
3316
gtk_range_calc_layout (GtkRange *range,
3317
gdouble adjustment_value)
3319
gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
3323
gboolean has_steppers_ab;
3324
gboolean has_steppers_cd;
3325
gboolean trough_under_steppers;
3326
GdkRectangle range_rect;
3327
GtkRangeLayout *layout;
3330
if (!range->need_recalc)
3333
/* If we have a too-small allocation, we prefer the steppers over
3334
* the trough/slider, probably the steppers are a more useful
3335
* feature in small spaces.
3337
* Also, we prefer to draw the range itself rather than the border
3338
* areas if there's a conflict, since the borders will be decoration
3339
* not controls. Though this depends on subclasses cooperating by
3340
* not drawing on range->range_rect.
3343
widget = GTK_WIDGET (range);
3344
layout = range->layout;
3346
gtk_range_get_props (range,
3347
&slider_width, &stepper_size,
3348
&focus_width, &trough_border,
3349
&stepper_spacing, &trough_under_steppers,
3352
gtk_range_calc_request (range,
3353
slider_width, stepper_size,
3354
focus_width, trough_border, stepper_spacing,
3355
&range_rect, &border, &n_steppers,
3356
&has_steppers_ab, &has_steppers_cd, &slider_length);
3358
/* We never expand to fill available space in the small dimension
3359
* (i.e. vertical scrollbars are always a fixed width)
3361
if (range->orientation == GTK_ORIENTATION_VERTICAL)
3363
clamp_dimensions (widget, &range_rect, &border, TRUE);
3367
clamp_dimensions (widget, &range_rect, &border, FALSE);
3370
range_rect.x = border.left;
3371
range_rect.y = border.top;
3373
range->range_rect = range_rect;
3375
if (range->orientation == GTK_ORIENTATION_VERTICAL)
3377
gint stepper_width, stepper_height;
3379
/* Steppers are the width of the range, and stepper_size in
3380
* height, or if we don't have enough height, divided equally
3381
* among available space.
3383
stepper_width = range_rect.width - focus_width * 2;
3385
if (trough_under_steppers)
3386
stepper_width -= trough_border * 2;
3388
if (stepper_width < 1)
3389
stepper_width = range_rect.width; /* screw the trough border */
3391
if (n_steppers == 0)
3392
stepper_height = 0; /* avoid divide by n_steppers */
3394
stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
3398
layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3399
layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3401
if (range->has_stepper_a)
3403
layout->stepper_a.width = stepper_width;
3404
layout->stepper_a.height = stepper_height;
3408
layout->stepper_a.width = 0;
3409
layout->stepper_a.height = 0;
3414
layout->stepper_b.x = layout->stepper_a.x;
3415
layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
3417
if (range->has_stepper_b)
3419
layout->stepper_b.width = stepper_width;
3420
layout->stepper_b.height = stepper_height;
3424
layout->stepper_b.width = 0;
3425
layout->stepper_b.height = 0;
3430
if (range->has_stepper_d)
3432
layout->stepper_d.width = stepper_width;
3433
layout->stepper_d.height = stepper_height;
3437
layout->stepper_d.width = 0;
3438
layout->stepper_d.height = 0;
3441
layout->stepper_d.x = layout->stepper_a.x;
3442
layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
3446
if (range->has_stepper_c)
3448
layout->stepper_c.width = stepper_width;
3449
layout->stepper_c.height = stepper_height;
3453
layout->stepper_c.width = 0;
3454
layout->stepper_c.height = 0;
3457
layout->stepper_c.x = layout->stepper_a.x;
3458
layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
3460
/* Now the trough is the remaining space between steppers B and C,
3461
* if any, minus spacing
3463
layout->trough.x = range_rect.x;
3464
layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
3465
layout->trough.width = range_rect.width;
3466
layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
3468
/* Slider fits into the trough, with stepper_spacing on either side,
3469
* and the size/position based on the adjustment or fixed, depending.
3471
layout->slider.x = layout->trough.x + focus_width + trough_border;
3472
layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
3474
/* Compute slider position/length */
3476
gint y, bottom, top, height;
3478
top = layout->trough.y;
3479
bottom = layout->trough.y + layout->trough.height;
3481
if (! trough_under_steppers)
3483
top += trough_border;
3484
bottom -= trough_border;
3487
/* slider height is the fraction (page_size /
3488
* total_adjustment_range) times the trough height in pixels
3491
if (range->adjustment->upper - range->adjustment->lower != 0)
3492
height = ((bottom - top) * (range->adjustment->page_size /
3493
(range->adjustment->upper - range->adjustment->lower)));
3495
height = range->min_slider_size;
3497
if (height < range->min_slider_size ||
3498
range->slider_size_fixed)
3499
height = range->min_slider_size;
3501
height = MIN (height, layout->trough.height);
3505
if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3506
y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
3507
(range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3509
y = CLAMP (y, top, bottom);
3511
if (should_invert (range))
3512
y = bottom - (y - top + height);
3514
layout->slider.y = y;
3515
layout->slider.height = height;
3517
/* These are publically exported */
3518
range->slider_start = layout->slider.y;
3519
range->slider_end = layout->slider.y + layout->slider.height;
3524
gint stepper_width, stepper_height;
3526
/* Steppers are the height of the range, and stepper_size in
3527
* width, or if we don't have enough width, divided equally
3528
* among available space.
3530
stepper_height = range_rect.height + focus_width * 2;
3532
if (trough_under_steppers)
3533
stepper_height -= trough_border * 2;
3535
if (stepper_height < 1)
3536
stepper_height = range_rect.height; /* screw the trough border */
3538
if (n_steppers == 0)
3539
stepper_width = 0; /* avoid divide by n_steppers */
3541
stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
3545
layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
3546
layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
3548
if (range->has_stepper_a)
3550
layout->stepper_a.width = stepper_width;
3551
layout->stepper_a.height = stepper_height;
3555
layout->stepper_a.width = 0;
3556
layout->stepper_a.height = 0;
3561
layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
3562
layout->stepper_b.y = layout->stepper_a.y;
3564
if (range->has_stepper_b)
3566
layout->stepper_b.width = stepper_width;
3567
layout->stepper_b.height = stepper_height;
3571
layout->stepper_b.width = 0;
3572
layout->stepper_b.height = 0;
3577
if (range->has_stepper_d)
3579
layout->stepper_d.width = stepper_width;
3580
layout->stepper_d.height = stepper_height;
3584
layout->stepper_d.width = 0;
3585
layout->stepper_d.height = 0;
3588
layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
3589
layout->stepper_d.y = layout->stepper_a.y;
3594
if (range->has_stepper_c)
3596
layout->stepper_c.width = stepper_width;
3597
layout->stepper_c.height = stepper_height;
3601
layout->stepper_c.width = 0;
3602
layout->stepper_c.height = 0;
3605
layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
3606
layout->stepper_c.y = layout->stepper_a.y;
3608
/* Now the trough is the remaining space between steppers B and C,
3611
layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
3612
layout->trough.y = range_rect.y;
3614
layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
3615
layout->trough.height = range_rect.height;
3617
/* Slider fits into the trough, with stepper_spacing on either side,
3618
* and the size/position based on the adjustment or fixed, depending.
3620
layout->slider.y = layout->trough.y + focus_width + trough_border;
3621
layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
3623
/* Compute slider position/length */
3625
gint x, left, right, width;
3627
left = layout->trough.x;
3628
right = layout->trough.x + layout->trough.width;
3630
if (! trough_under_steppers)
3632
left += trough_border;
3633
right -= trough_border;
3636
/* slider width is the fraction (page_size /
3637
* total_adjustment_range) times the trough width in pixels
3640
if (range->adjustment->upper - range->adjustment->lower != 0)
3641
width = ((right - left) * (range->adjustment->page_size /
3642
(range->adjustment->upper - range->adjustment->lower)));
3644
width = range->min_slider_size;
3646
if (width < range->min_slider_size ||
3647
range->slider_size_fixed)
3648
width = range->min_slider_size;
3650
width = MIN (width, layout->trough.width);
3654
if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
3655
x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
3656
(range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
3658
x = CLAMP (x, left, right);
3660
if (should_invert (range))
3661
x = right - (x - left + width);
3663
layout->slider.x = x;
3664
layout->slider.width = width;
3666
/* These are publically exported */
3667
range->slider_start = layout->slider.x;
3668
range->slider_end = layout->slider.x + layout->slider.width;
3672
gtk_range_update_mouse_location (range);
3674
switch (range->layout->upper_sensitivity)
3676
case GTK_SENSITIVITY_AUTO:
3677
range->layout->upper_sensitive =
3678
(range->adjustment->value <
3679
(range->adjustment->upper - range->adjustment->page_size));
3682
case GTK_SENSITIVITY_ON:
3683
range->layout->upper_sensitive = TRUE;
3686
case GTK_SENSITIVITY_OFF:
3687
range->layout->upper_sensitive = FALSE;
3691
switch (range->layout->lower_sensitivity)
3693
case GTK_SENSITIVITY_AUTO:
3694
range->layout->lower_sensitive =
3695
(range->adjustment->value > range->adjustment->lower);
3698
case GTK_SENSITIVITY_ON:
3699
range->layout->lower_sensitive = TRUE;
3702
case GTK_SENSITIVITY_OFF:
3703
range->layout->lower_sensitive = FALSE;
3708
static GdkRectangle*
3709
get_area (GtkRange *range,
3710
MouseLocation location)
3714
case MOUSE_STEPPER_A:
3715
return &range->layout->stepper_a;
3716
case MOUSE_STEPPER_B:
3717
return &range->layout->stepper_b;
3718
case MOUSE_STEPPER_C:
3719
return &range->layout->stepper_c;
3720
case MOUSE_STEPPER_D:
3721
return &range->layout->stepper_d;
3723
return &range->layout->trough;
3725
return &range->layout->slider;
3731
g_warning (G_STRLOC": bug");
3736
gtk_range_calc_marks (GtkRange *range)
3740
if (!range->layout->recalc_marks)
3743
range->layout->recalc_marks = FALSE;
3745
for (i = 0; i < range->layout->n_marks; i++)
3747
range->need_recalc = TRUE;
3748
gtk_range_calc_layout (range, range->layout->marks[i]);
3749
if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
3750
range->layout->mark_pos[i] = range->layout->slider.x + range->layout->slider.width / 2;
3752
range->layout->mark_pos[i] = range->layout->slider.y + range->layout->slider.height / 2;
3755
range->need_recalc = TRUE;
3759
gtk_range_real_change_value (GtkRange *range,
3760
GtkScrollType scroll,
3763
/* potentially adjust the bounds _before we clamp */
3764
g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
3766
if (range->layout->restrict_to_fill_level)
3767
value = MIN (value, MAX (range->adjustment->lower,
3768
range->layout->fill_level));
3770
value = CLAMP (value, range->adjustment->lower,
3771
(range->adjustment->upper - range->adjustment->page_size));
3773
if (range->round_digits >= 0)
3778
i = range->round_digits;
3783
value = floor ((value * power) + 0.5) / power;
3786
if (range->adjustment->value != value)
3788
range->need_recalc = TRUE;
3790
gtk_widget_queue_draw (GTK_WIDGET (range));
3792
switch (range->update_policy)
3794
case GTK_UPDATE_CONTINUOUS:
3795
gtk_adjustment_set_value (range->adjustment, value);
3798
/* Delayed means we update after a period of inactivity */
3799
case GTK_UPDATE_DELAYED:
3800
gtk_range_reset_update_timer (range);
3803
/* Discontinuous means we update on button release */
3804
case GTK_UPDATE_DISCONTINUOUS:
3805
/* don't emit value_changed signal */
3806
range->adjustment->value = value;
3807
range->update_pending = TRUE;
3815
gtk_range_update_value (GtkRange *range)
3817
gtk_range_remove_update_timer (range);
3819
if (range->update_pending)
3821
gtk_adjustment_value_changed (range->adjustment);
3823
range->update_pending = FALSE;
3827
struct _GtkRangeStepTimer
3834
second_timeout (gpointer data)
3838
range = GTK_RANGE (data);
3839
gtk_range_scroll (range, range->timer->step);
3845
initial_timeout (gpointer data)
3848
GtkSettings *settings;
3851
settings = gtk_widget_get_settings (GTK_WIDGET (data));
3852
g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3854
range = GTK_RANGE (data);
3855
range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3863
gtk_range_add_step_timer (GtkRange *range,
3866
GtkSettings *settings;
3869
g_return_if_fail (range->timer == NULL);
3870
g_return_if_fail (step != GTK_SCROLL_NONE);
3872
settings = gtk_widget_get_settings (GTK_WIDGET (range));
3873
g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
3875
range->timer = g_new (GtkRangeStepTimer, 1);
3877
range->timer->timeout_id = gdk_threads_add_timeout (timeout,
3880
range->timer->step = step;
3882
gtk_range_scroll (range, range->timer->step);
3886
gtk_range_remove_step_timer (GtkRange *range)
3890
if (range->timer->timeout_id != 0)
3891
g_source_remove (range->timer->timeout_id);
3893
g_free (range->timer);
3895
range->timer = NULL;
3900
update_timeout (gpointer data)
3904
range = GTK_RANGE (data);
3905
gtk_range_update_value (range);
3906
range->update_timeout_id = 0;
3913
gtk_range_reset_update_timer (GtkRange *range)
3915
gtk_range_remove_update_timer (range);
3917
range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
3923
gtk_range_remove_update_timer (GtkRange *range)
3925
if (range->update_timeout_id != 0)
3927
g_source_remove (range->update_timeout_id);
3928
range->update_timeout_id = 0;
3933
_gtk_range_set_stop_values (GtkRange *range,
3939
g_free (range->layout->marks);
3940
range->layout->marks = g_new (gdouble, n_values);
3942
g_free (range->layout->mark_pos);
3943
range->layout->mark_pos = g_new (gint, n_values);
3945
range->layout->n_marks = n_values;
3947
for (i = 0; i < n_values; i++)
3948
range->layout->marks[i] = values[i];
3950
range->layout->recalc_marks = TRUE;
3954
_gtk_range_get_stop_positions (GtkRange *range,
3957
gtk_range_calc_marks (range);
3960
*values = g_memdup (range->layout->mark_pos, range->layout->n_marks * sizeof (gint));
3962
return range->layout->n_marks;
3966
* gtk_range_set_round_digits:
3967
* @range: a #GtkRange
3968
* @round_digits: the precision in digits, or -1
3970
* Sets the number of digits to round the value to when
3971
* it changes. See #GtkRange::change-value.
3976
gtk_range_set_round_digits (GtkRange *range,
3979
g_return_if_fail (GTK_IS_RANGE (range));
3980
g_return_if_fail (round_digits >= -1);
3982
range->round_digits = round_digits;
3984
g_object_notify (G_OBJECT (range), "round-digits");
3988
* gtk_range_get_round_digits:
3989
* @range: a #GtkRange
3991
* Gets the number of digits to round the value to when
3992
* it changes. See #GtkRange::change-value.
3994
* Return value: the number of digits to round to
3999
gtk_range_get_round_digits (GtkRange *range)
4001
g_return_val_if_fail (GTK_IS_RANGE (range), -1);
4003
return range->round_digits;
4007
#define __GTK_RANGE_C__
4008
#include "gtkaliasdef.c"