gtkrange.c 125 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3
 * Copyright (C) 2001 Red Hat, Inc.
Elliot Lee's avatar
Elliot Lee committed
4 5
 *
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10 11 12
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18 19

/*
20
 * Modified by the GTK+ Team and others 1997-2004.  See the AUTHORS
21 22 23 24 25
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

26
#include "config.h"
27

Elliot Lee's avatar
Elliot Lee committed
28
#include <stdio.h>
29
#include <math.h>
30

31
#include "gtkrange.h"
32
#include "gtkrangeprivate.h"
33

34
#include "gtkadjustmentprivate.h"
35
#include "gtkboxgadgetprivate.h"
36 37
#include "gtkbuiltiniconprivate.h"
#include "gtkcsscustomgadgetprivate.h"
38 39
#include "gtkcolorscaleprivate.h"
#include "gtkintl.h"
40
#include "gtkgesturelongpressprivate.h"
41
#include "gtkmain.h"
42
#include "gtkmarshalers.h"
43
#include "gtkorientableprivate.h"
44
#include "gtkprivate.h"
45
#include "gtkscale.h"
46
#include "gtkscrollbar.h"
47
#include "gtktypebuiltins.h"
48
#include "gtkwindow.h"
49
#include "gtkwidgetprivate.h"
50
#include "a11y/gtkrangeaccessible.h"
51
#include "gtkcssstylepropertyprivate.h"
52 53 54 55 56 57 58

/**
 * SECTION:gtkrange
 * @Short_description: Base class for widgets which visualize an adjustment
 * @Title: GtkRange
 *
 * #GtkRange is the common base class for widgets which visualize an
59
 * adjustment, e.g #GtkScale or #GtkScrollbar.
60 61 62
 *
 * Apart from signals for monitoring the parameters of the adjustment,
 * #GtkRange provides properties and methods for influencing the sensitivity
63 64
 * of the “steppers”. It also provides properties and methods for setting a
 * “fill level” on range widgets. See gtk_range_set_fill_level().
65 66 67
 */


68
#define TIMEOUT_INITIAL     500
Benjamin Otte's avatar
Benjamin Otte committed
69
#define TIMEOUT_REPEAT      250
70
#define AUTOSCROLL_FACTOR   20
Benjamin Otte's avatar
Benjamin Otte committed
71
#define SCROLL_EDGE_SIZE    15
72
#define MARK_SNAP_LENGTH    12
Elliot Lee's avatar
Elliot Lee committed
73

74
typedef struct _GtkRangeStepTimer GtkRangeStepTimer;
75

76
struct _GtkRangePrivate
77
{
78
  GtkCssGadget *mouse_location;
79
  /* last mouse coords we got, or G_MININT if mouse is outside the range */
80 81
  gint  mouse_x;
  gint  mouse_y;
82
  GtkCssGadget *grab_location;   /* "grabbed" mouse location, NULL for no grab */
83

84
  GtkRangeStepTimer *timer;
85

86 87 88 89 90
  GtkAdjustment     *adjustment;
  GtkSensitivityType lower_sensitivity;
  GtkSensitivityType upper_sensitivity;

  GdkWindow         *event_window;
91

92 93 94
  /* Steppers are: < > ---- < >
   *               a b      c d
   */
95
  GtkCssGadget *gadget;
96
  GtkCssGadget *contents_gadget;
97 98 99 100 101 102 103 104
  GtkCssGadget *stepper_a_gadget;
  GtkCssGadget *stepper_b_gadget;
  GtkCssGadget *stepper_c_gadget;
  GtkCssGadget *stepper_d_gadget;
  GtkCssGadget *trough_gadget;
  GtkCssGadget *fill_gadget;
  GtkCssGadget *highlight_gadget;
  GtkCssGadget *slider_gadget;
105

106
  GtkOrientation     orientation;
107 108 109 110 111 112 113 114 115

  gdouble  fill_level;
  gdouble *marks;

  gint *mark_pos;
  gint  min_slider_size;
  gint  n_marks;
  gint  round_digits;                /* Round off value to this many digits, -1 for no rounding */
  gint  slide_initial_slider_position;
116
  gint  slide_initial_coordinate_delta;
117

118 119 120
  guint flippable              : 1;
  guint inverted               : 1;
  guint slider_size_fixed      : 1;
121
  guint slider_use_min_size    : 1;
122
  guint trough_click_forward   : 1;  /* trough click was on the forward side of slider */
123 124

  /* Stepper sensitivity */
125 126
  guint lower_sensitive        : 1;
  guint upper_sensitive        : 1;
127

128 129 130
  /* The range has an origin, should be drawn differently. Used by GtkScale */
  guint has_origin             : 1;

131 132
  /* Whether we're doing fine adjustment */
  guint zoom                   : 1;
133

134
  /* Fill level */
135
  guint show_fill_level        : 1;
136
  guint restrict_to_fill_level : 1;
137 138 139

  /* Whether dragging is ongoing */
  guint in_drag                : 1;
140 141 142 143 144 145 146

  GtkGesture *long_press_gesture;
  GtkGesture *multipress_gesture;
  GtkGesture *drag_gesture;

  GtkScrollType autoscroll_mode;
  guint autoscroll_id;
147
};
148

149

150 151 152 153 154 155 156 157
enum {
  PROP_0,
  PROP_ADJUSTMENT,
  PROP_INVERTED,
  PROP_LOWER_STEPPER_SENSITIVITY,
  PROP_UPPER_STEPPER_SENSITIVITY,
  PROP_SHOW_FILL_LEVEL,
  PROP_RESTRICT_TO_FILL_LEVEL,
158
  PROP_FILL_LEVEL,
159 160 161
  PROP_ROUND_DIGITS,
  PROP_ORIENTATION,
  LAST_PROP = PROP_ORIENTATION
162
};
163

164 165 166 167 168 169
enum {
  VALUE_CHANGED,
  ADJUST_BOUNDS,
  MOVE_SLIDER,
  CHANGE_VALUE,
  LAST_SIGNAL
170 171 172 173 174 175 176 177 178 179
};

static void gtk_range_set_property   (GObject          *object,
                                      guint             prop_id,
                                      const GValue     *value,
                                      GParamSpec       *pspec);
static void gtk_range_get_property   (GObject          *object,
                                      guint             prop_id,
                                      GValue           *value,
                                      GParamSpec       *pspec);
180
static void gtk_range_finalize       (GObject          *object);
181
static void gtk_range_destroy        (GtkWidget        *widget);
182 183 184 185 186 187 188 189
static void gtk_range_get_preferred_width
                                     (GtkWidget        *widget,
                                      gint             *minimum,
                                      gint             *natural);
static void gtk_range_get_preferred_height
                                     (GtkWidget        *widget,
                                      gint             *minimum,
                                      gint             *natural);
190 191 192 193
static void gtk_range_size_allocate  (GtkWidget        *widget,
                                      GtkAllocation    *allocation);
static void gtk_range_realize        (GtkWidget        *widget);
static void gtk_range_unrealize      (GtkWidget        *widget);
194 195
static void gtk_range_map            (GtkWidget        *widget);
static void gtk_range_unmap          (GtkWidget        *widget);
196 197
static gboolean gtk_range_draw       (GtkWidget        *widget,
                                      cairo_t          *cr);
198 199 200 201 202 203 204 205 206 207 208

static void gtk_range_multipress_gesture_pressed  (GtkGestureMultiPress *gesture,
                                                   guint                 n_press,
                                                   gdouble               x,
                                                   gdouble               y,
                                                   GtkRange             *range);
static void gtk_range_multipress_gesture_released (GtkGestureMultiPress *gesture,
                                                   guint                 n_press,
                                                   gdouble               x,
                                                   gdouble               y,
                                                   GtkRange             *range);
209 210 211 212
static void gtk_range_drag_gesture_begin          (GtkGestureDrag       *gesture,
                                                   gdouble               offset_x,
                                                   gdouble               offset_y,
                                                   GtkRange             *range);
213 214 215 216 217 218 219 220 221 222
static void gtk_range_drag_gesture_update         (GtkGestureDrag       *gesture,
                                                   gdouble               offset_x,
                                                   gdouble               offset_y,
                                                   GtkRange             *range);
static void gtk_range_long_press_gesture_pressed  (GtkGestureLongPress  *gesture,
                                                   gdouble               x,
                                                   gdouble               y,
                                                   GtkRange             *range);


223
static gboolean gtk_range_scroll_event   (GtkWidget        *widget,
224
                                      GdkEventScroll   *event);
225 226
static gboolean gtk_range_event       (GtkWidget       *widget,
                                       GdkEvent        *event);
227 228 229
static void update_slider_position   (GtkRange	       *range,
				      gint              mouse_x,
				      gint              mouse_y);
230
static void stop_scrolling           (GtkRange         *range);
231 232
static void add_autoscroll           (GtkRange         *range);
static void remove_autoscroll        (GtkRange         *range);
233 234

/* Range methods */
235

236
static void gtk_range_move_slider              (GtkRange         *range,
237 238 239
                                                GtkScrollType     scroll);

/* Internals */
240 241 242
static void          gtk_range_compute_slider_position  (GtkRange      *range,
                                                         gdouble        adjustment_value,
                                                         GdkRectangle  *slider_rect);
243
static gboolean      gtk_range_scroll                   (GtkRange      *range,
244
                                                         GtkScrollType  scroll);
245
static void          gtk_range_update_mouse_location    (GtkRange      *range);
246 247
static void          gtk_range_calc_slider              (GtkRange      *range);
static void          gtk_range_calc_stepper_sensitivity (GtkRange      *range);
248
static void          gtk_range_calc_marks               (GtkRange      *range);
249 250 251 252 253 254 255
static void          gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
                                                         gpointer       data);
static void          gtk_range_adjustment_changed       (GtkAdjustment *adjustment,
                                                         gpointer       data);
static void          gtk_range_add_step_timer           (GtkRange      *range,
                                                         GtkScrollType  step);
static void          gtk_range_remove_step_timer        (GtkRange      *range);
256 257 258
static gboolean      gtk_range_real_change_value        (GtkRange      *range,
                                                         GtkScrollType  scroll,
                                                         gdouble        value);
259 260
static gboolean      gtk_range_key_press                (GtkWidget     *range,
							 GdkEventKey   *event);
261 262
static void          gtk_range_state_flags_changed      (GtkWidget     *widget,
                                                         GtkStateFlags  previous_state);
263 264
static void          gtk_range_direction_changed        (GtkWidget     *widget,
                                                         GtkTextDirection  previous_direction);
265 266
static void          gtk_range_measure_trough           (GtkCssGadget   *gadget,
                                                         GtkOrientation  orientation,
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
                                                         gint            for_size,
                                                         gint           *minimum,
                                                         gint           *natural,
                                                         gint           *minimum_baseline,
                                                         gint           *natural_baseline,
                                                         gpointer        user_data);
static void          gtk_range_allocate_trough          (GtkCssGadget        *gadget,
                                                         const GtkAllocation *allocation,
                                                         int                  baseline,
                                                         GtkAllocation       *out_clip,
                                                         gpointer             data);
static gboolean      gtk_range_render_trough            (GtkCssGadget *gadget,
                                                         cairo_t      *cr,
                                                         int           x,
                                                         int           y,
                                                         int           width,
                                                         int           height,
                                                         gpointer      user_data);
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
static void          gtk_range_measure                  (GtkCssGadget   *gadget,
                                                         GtkOrientation  orientation,
                                                         gint            for_size,
                                                         gint           *minimum,
                                                         gint           *natural,
                                                         gint           *minimum_baseline,
                                                         gint           *natural_baseline,
                                                         gpointer        user_data);
static void          gtk_range_allocate                 (GtkCssGadget        *gadget,
                                                         const GtkAllocation *allocation,
                                                         int                  baseline,
                                                         GtkAllocation       *out_clip,
                                                         gpointer             data);
static gboolean      gtk_range_render                   (GtkCssGadget *gadget,
                                                         cairo_t      *cr,
                                                         int           x,
                                                         int           y,
                                                         int           width,
                                                         int           height,
                                                         gpointer      user_data);
Elliot Lee's avatar
Elliot Lee committed
305

306
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
307
                                  G_ADD_PRIVATE (GtkRange)
308 309 310
                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
                                                         NULL))

311
static guint signals[LAST_SIGNAL];
312
static GParamSpec *properties[LAST_PROP];
Elliot Lee's avatar
Elliot Lee committed
313 314 315 316

static void
gtk_range_class_init (GtkRangeClass *class)
{
Alexander Larsson's avatar
Alexander Larsson committed
317
  GObjectClass   *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
318 319
  GtkWidgetClass *widget_class;

Alexander Larsson's avatar
Alexander Larsson committed
320
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
321 322
  widget_class = (GtkWidgetClass*) class;

Alexander Larsson's avatar
Alexander Larsson committed
323 324
  gobject_class->set_property = gtk_range_set_property;
  gobject_class->get_property = gtk_range_get_property;
325
  gobject_class->finalize = gtk_range_finalize;
326

327
  widget_class->destroy = gtk_range_destroy;
328 329
  widget_class->get_preferred_width = gtk_range_get_preferred_width;
  widget_class->get_preferred_height = gtk_range_get_preferred_height;
330 331
  widget_class->size_allocate = gtk_range_size_allocate;
  widget_class->realize = gtk_range_realize;
332
  widget_class->unrealize = gtk_range_unrealize;
333 334
  widget_class->map = gtk_range_map;
  widget_class->unmap = gtk_range_unmap;
335
  widget_class->draw = gtk_range_draw;
336
  widget_class->event = gtk_range_event;
337
  widget_class->scroll_event = gtk_range_scroll_event;
338
  widget_class->key_press_event = gtk_range_key_press;
339
  widget_class->state_flags_changed = gtk_range_state_flags_changed;
340
  widget_class->direction_changed = gtk_range_direction_changed;
341 342

  class->move_slider = gtk_range_move_slider;
343
  class->change_value = gtk_range_real_change_value;
344

Matthias Clasen's avatar
Matthias Clasen committed
345 346
  /**
   * GtkRange::value-changed:
347
   * @range: the #GtkRange that received the signal
Matthias Clasen's avatar
Matthias Clasen committed
348 349 350
   *
   * Emitted when the range value changes.
   */
351
  signals[VALUE_CHANGED] =
352
    g_signal_new (I_("value-changed"),
Manish Singh's avatar
Manish Singh committed
353
                  G_TYPE_FROM_CLASS (gobject_class),
354 355 356
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, value_changed),
                  NULL, NULL,
357
                  NULL,
358
                  G_TYPE_NONE, 0);
359 360 361 362 363

  /**
   * GtkRange::adjust-bounds:
   * @range: the #GtkRange that received the signal
   * @value: the value before we clamp
364 365 366
   *
   * Emitted before clamping a value, to give the application a
   * chance to adjust the bounds.
367
   */
368
  signals[ADJUST_BOUNDS] =
369
    g_signal_new (I_("adjust-bounds"),
Manish Singh's avatar
Manish Singh committed
370
                  G_TYPE_FROM_CLASS (gobject_class),
371 372 373
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
                  NULL, NULL,
374
                  NULL,
375 376
                  G_TYPE_NONE, 1,
                  G_TYPE_DOUBLE);
377

Matthias Clasen's avatar
Matthias Clasen committed
378 379
  /**
   * GtkRange::move-slider:
380
   * @range: the #GtkRange that received the signal
Matthias Clasen's avatar
Matthias Clasen committed
381 382 383 384
   * @step: how to move the slider
   *
   * Virtual function that moves the slider. Used for keybindings.
   */
385
  signals[MOVE_SLIDER] =
386
    g_signal_new (I_("move-slider"),
Manish Singh's avatar
Manish Singh committed
387
                  G_TYPE_FROM_CLASS (gobject_class),
388 389 390
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkRangeClass, move_slider),
                  NULL, NULL,
391
                  NULL,
392 393
                  G_TYPE_NONE, 1,
                  GTK_TYPE_SCROLL_TYPE);
394 395 396

  /**
   * GtkRange::change-value:
397
   * @range: the #GtkRange that received the signal
Matthias Clasen's avatar
Matthias Clasen committed
398 399
   * @scroll: the type of scroll action that was performed
   * @value: the new value resulting from the scroll action
400
   *
401
   * The #GtkRange::change-value signal is emitted when a scroll action is
402 403 404 405 406 407 408 409
   * performed on a range.  It allows an application to determine the
   * type of scroll event that occurred and the resultant new value.
   * The application can handle the event itself and return %TRUE to
   * prevent further processing.  Or, by returning %FALSE, it can pass
   * the event to other handlers until the default GTK+ handler is
   * reached.
   *
   * The value parameter is unrounded.  An application that overrides
410 411 412
   * the GtkRange::change-value signal is responsible for clamping the
   * value to the desired number of decimal digits; the default GTK+
   * handler clamps the value based on #GtkRange:round-digits.
413
   *
414 415 416
   * Returns: %TRUE to prevent other handlers from being invoked for
   *     the signal, %FALSE to propagate the signal further
   *
417 418 419
   * Since: 2.6
   */
  signals[CHANGE_VALUE] =
420
    g_signal_new (I_("change-value"),
421 422 423 424 425 426 427 428
                  G_TYPE_FROM_CLASS (gobject_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, change_value),
                  _gtk_boolean_handled_accumulator, NULL,
                  _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
                  G_TYPE_BOOLEAN, 2,
                  GTK_TYPE_SCROLL_TYPE,
                  G_TYPE_DOUBLE);
Tim Janik's avatar
Tim Janik committed
429

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
  g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");

  properties[PROP_ADJUSTMENT] =
      g_param_spec_object ("adjustment",
                           P_("Adjustment"),
                           P_("The GtkAdjustment that contains the current value of this range object"),
                           GTK_TYPE_ADJUSTMENT,
                           GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT);

  properties[PROP_INVERTED] =
      g_param_spec_boolean ("inverted",
                            P_("Inverted"),
                            P_("Invert direction slider moves to increase range value"),
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  properties[PROP_LOWER_STEPPER_SENSITIVITY] =
      g_param_spec_enum ("lower-stepper-sensitivity",
                         P_("Lower stepper sensitivity"),
                         P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
                         GTK_TYPE_SENSITIVITY_TYPE,
                         GTK_SENSITIVITY_AUTO,
                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  properties[PROP_UPPER_STEPPER_SENSITIVITY] =
      g_param_spec_enum ("upper-stepper-sensitivity",
                         P_("Upper stepper sensitivity"),
                         P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
                         GTK_TYPE_SENSITIVITY_TYPE,
                         GTK_SENSITIVITY_AUTO,
                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
461

462 463 464
  /**
   * GtkRange:show-fill-level:
   *
Matthias Clasen's avatar
Matthias Clasen committed
465
   * The show-fill-level property controls whether fill level indicator
466 467 468 469 470
   * graphics are displayed on the trough. See
   * gtk_range_set_show_fill_level().
   *
   * Since: 2.12
   **/
471 472 473 474 475 476
  properties[PROP_SHOW_FILL_LEVEL] =
      g_param_spec_boolean ("show-fill-level",
                            P_("Show Fill Level"),
                            P_("Whether to display a fill level indicator graphics on trough."),
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
477 478 479 480

  /**
   * GtkRange:restrict-to-fill-level:
   *
Matthias Clasen's avatar
Matthias Clasen committed
481
   * The restrict-to-fill-level property controls whether slider
482
   * movement is restricted to an upper boundary set by the
Matthias Clasen's avatar
Matthias Clasen committed
483
   * fill level. See gtk_range_set_restrict_to_fill_level().
484 485 486
   *
   * Since: 2.12
   **/
487 488 489 490 491 492
  properties[PROP_RESTRICT_TO_FILL_LEVEL] =
      g_param_spec_boolean ("restrict-to-fill-level",
                            P_("Restrict to Fill Level"),
                            P_("Whether to restrict the upper boundary to the fill level."),
                            TRUE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
493 494 495 496 497 498 499 500 501

  /**
   * GtkRange:fill-level:
   *
   * The fill level (e.g. prebuffering of a network stream).
   * See gtk_range_set_fill_level().
   *
   * Since: 2.12
   **/
502 503 504 505 506 507 508
  properties[PROP_FILL_LEVEL] =
      g_param_spec_double ("fill-level",
                           P_("Fill Level"),
                           P_("The fill level."),
                           -G_MAXDOUBLE, G_MAXDOUBLE,
                           G_MAXDOUBLE,
                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
509

510 511 512 513 514 515 516 517
  /**
   * GtkRange:round-digits:
   *
   * The number of digits to round the value to when
   * it changes, or -1. See #GtkRange::change-value.
   *
   * Since: 2.24
   */
518 519 520 521 522 523 524 525 526
  properties[PROP_ROUND_DIGITS] =
      g_param_spec_int ("round-digits",
                        P_("Round Digits"),
                        P_("The number of digits to round the value to."),
                        -1, G_MAXINT,
                        -1,
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  g_object_class_install_properties (gobject_class, LAST_PROP, properties);
527

528 529 530 531 532
  /**
   * GtkRange:slider-width:
   *
   * Width of scrollbar or scale thumb.
   *
Timm Bäder's avatar
Timm Bäder committed
533
   * Deprecated: 3.20: Use the min-height/min-width CSS properties on the
534 535
   *   slider element. The value of this style property is ignored.
   */
536
  gtk_widget_class_install_style_property (widget_class,
537
					   g_param_spec_int ("slider-width",
538 539
							     P_("Slider Width"),
							     P_("Width of scrollbar or scale thumb"),
540 541
							     0,
							     G_MAXINT,
542
							     14,
543 544 545 546 547 548
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
  /**
   * GtkRange:trough-border:
   *
   * Spacing between thumb/steppers and outer trough bevel.
   *
Timm Bäder's avatar
Timm Bäder committed
549
   * Deprecated: 3.20: Use the margin/padding CSS properties on the trough and
550 551
   *   stepper elements. The value of this style property is ignored.
   */
552
  gtk_widget_class_install_style_property (widget_class,
553
					   g_param_spec_int ("trough-border",
554 555
                                                             P_("Trough Border"),
                                                             P_("Spacing between thumb/steppers and outer trough bevel"),
556 557 558
                                                             0,
                                                             G_MAXINT,
                                                             1,
559 560 561 562 563 564
                                                             GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
  /**
   * GtkRange:stepper-size:
   *
   * Length of step buttons at ends.
   *
Timm Bäder's avatar
Timm Bäder committed
565
   * Deprecated: 3.20: Use the min-height/min-width CSS properties on the
566 567
   *   stepper elements. The value of this style property is ignored.
   */
568
  gtk_widget_class_install_style_property (widget_class,
569
					   g_param_spec_int ("stepper-size",
570 571
							     P_("Stepper Size"),
							     P_("Length of step buttons at ends"),
572 573
							     0,
							     G_MAXINT,
574
							     14,
575
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
576 577 578 579 580
  /**
   * GtkRange:stepper-spacing:
   *
   * The spacing between the stepper buttons and thumb. Note that
   * stepper-spacing won't have any effect if there are no steppers.
581
   *
Timm Bäder's avatar
Timm Bäder committed
582
   * Deprecated: 3.20: Use the margin CSS property on the stepper elements.
583
   *   The value of this style property is ignored.
584
   */
585
  gtk_widget_class_install_style_property (widget_class,
586
					   g_param_spec_int ("stepper-spacing",
587 588
							     P_("Stepper Spacing"),
							     P_("Spacing between step buttons and thumb"),
589
                                                             0,
590
							     G_MAXINT,
591
							     0,
592
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
593 594 595 596 597 598 599 600

  /**
   * GtkRange:arrow-displacement-x:
   *
   * How far in the x direction to move the arrow when the button is depressed.
   *
   * Deprecated: 3.20: The value of this style property is ignored.
   */
601
  gtk_widget_class_install_style_property (widget_class,
602
					   g_param_spec_int ("arrow-displacement-x",
603 604
							     P_("Arrow X Displacement"),
							     P_("How far in the x direction to move the arrow when the button is depressed"),
605 606 607
							     G_MININT,
							     G_MAXINT,
							     0,
608
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
609 610 611 612 613 614 615 616

  /**
   * GtkRange:arrow-displacement-y:
   *
   * How far in the y direction to move the arrow when the button is depressed.
   *
   * Deprecated: 3.20: The value of this style property is ignored.
   */
617
  gtk_widget_class_install_style_property (widget_class,
618
					   g_param_spec_int ("arrow-displacement-y",
619 620
							     P_("Arrow Y Displacement"),
							     P_("How far in the y direction to move the arrow when the button is depressed"),
621 622 623
							     G_MININT,
							     G_MAXINT,
							     0,
624
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
625

626 627 628 629
  /**
   * GtkRange:trough-under-steppers:
   *
   * Whether to draw the trough across the full length of the range or
630
   * to exclude the steppers and their spacing.
631 632
   *
   * Since: 2.10
633 634 635
   *
   * Deprecated: 3.20: The value of this style property is ignored, and the
   *   widget will behave as if it was set to %TRUE.
636 637 638 639
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boolean ("trough-under-steppers",
                                                                 P_("Trough Under Steppers"),
640
                                                                 P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
641
                                                                 TRUE,
642
                                                                 GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
643

644 645 646 647 648 649
  /**
   * GtkRange:arrow-scaling:
   *
   * The arrow size proportion relative to the scroll button size.
   *
   * Since: 2.14
650 651 652
   *
   * Deprecated: 3.20: Use min-width/min-height on the "button" node instead.
   *   The value of this style property is ignored.
653 654 655 656 657 658
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_float ("arrow-scaling",
							       P_("Arrow scaling"),
							       P_("Arrow scaling with regard to scroll button size"),
							       0.0, 1.0, 0.5,
659
							       GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
660

661
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_RANGE_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
662 663
}

664 665 666 667 668 669 670 671
static void
gtk_range_sync_orientation (GtkRange *range)
{
  GtkRangePrivate *priv = range->priv;
  GtkOrientation orientation;

  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (range));
  _gtk_orientable_set_style_classes (GTK_ORIENTABLE (range));
672
  gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->contents_gadget), orientation);
673 674
}

675
static void
Alexander Larsson's avatar
Alexander Larsson committed
676 677 678 679
gtk_range_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
680
{
681
  GtkRange *range = GTK_RANGE (object);
682
  GtkRangePrivate *priv = range->priv;
683

Alexander Larsson's avatar
Alexander Larsson committed
684
  switch (prop_id)
685
    {
686
    case PROP_ORIENTATION:
687 688 689
      if (priv->orientation != g_value_get_enum (value))
        {
          priv->orientation = g_value_get_enum (value);
690
          gtk_range_sync_orientation (range);
691 692 693
          gtk_widget_queue_resize (GTK_WIDGET (range));
          g_object_notify_by_pspec (object, pspec);
        }
694
      break;
695 696 697
    case PROP_ADJUSTMENT:
      gtk_range_set_adjustment (range, g_value_get_object (value));
      break;
698 699 700
    case PROP_INVERTED:
      gtk_range_set_inverted (range, g_value_get_boolean (value));
      break;
701 702 703 704 705 706
    case PROP_LOWER_STEPPER_SENSITIVITY:
      gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
      break;
    case PROP_UPPER_STEPPER_SENSITIVITY:
      gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
      break;
707 708 709 710 711 712 713 714 715
    case PROP_SHOW_FILL_LEVEL:
      gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
      break;
    case PROP_RESTRICT_TO_FILL_LEVEL:
      gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
      break;
    case PROP_FILL_LEVEL:
      gtk_range_set_fill_level (range, g_value_get_double (value));
      break;
716 717 718
    case PROP_ROUND_DIGITS:
      gtk_range_set_round_digits (range, g_value_get_int (value));
      break;
719
    default:
Alexander Larsson's avatar
Alexander Larsson committed
720
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
721 722 723 724 725
      break;
    }
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
726 727 728 729
gtk_range_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
730
{
731
  GtkRange *range = GTK_RANGE (object);
732
  GtkRangePrivate *priv = range->priv;
733

Alexander Larsson's avatar
Alexander Larsson committed
734
  switch (prop_id)
735
    {
736
    case PROP_ORIENTATION:
737
      g_value_set_enum (value, priv->orientation);
738
      break;
739
    case PROP_ADJUSTMENT:
740
      g_value_set_object (value, priv->adjustment);
741
      break;
742
    case PROP_INVERTED:
743
      g_value_set_boolean (value, priv->inverted);
744
      break;
745 746 747 748 749 750
    case PROP_LOWER_STEPPER_SENSITIVITY:
      g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
      break;
    case PROP_UPPER_STEPPER_SENSITIVITY:
      g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
      break;
751 752 753 754 755 756 757 758 759
    case PROP_SHOW_FILL_LEVEL:
      g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
      break;
    case PROP_RESTRICT_TO_FILL_LEVEL:
      g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
      break;
    case PROP_FILL_LEVEL:
      g_value_set_double (value, gtk_range_get_fill_level (range));
      break;
760 761 762
    case PROP_ROUND_DIGITS:
      g_value_set_int (value, gtk_range_get_round_digits (range));
      break;
763
    default:
Alexander Larsson's avatar
Alexander Larsson committed
764
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
765 766 767 768
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
769 770 771
static void
gtk_range_init (GtkRange *range)
{
772
  GtkRangePrivate *priv;
773
  GtkCssNode *widget_node;
774

775
  range->priv = gtk_range_get_instance_private (range);
776 777
  priv = range->priv;

778
  gtk_widget_set_has_window (GTK_WIDGET (range), FALSE);
779

780 781 782 783 784 785
  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
  priv->adjustment = NULL;
  priv->inverted = FALSE;
  priv->flippable = FALSE;
  priv->min_slider_size = 1;
  priv->round_digits = -1;
786 787
  priv->mouse_x = G_MININT;
  priv->mouse_y = G_MININT;
788 789 790 791
  priv->lower_sensitivity = GTK_SENSITIVITY_AUTO;
  priv->upper_sensitivity = GTK_SENSITIVITY_AUTO;
  priv->lower_sensitive = TRUE;
  priv->upper_sensitive = TRUE;
792
  priv->has_origin = FALSE;
793 794 795 796
  priv->show_fill_level = FALSE;
  priv->restrict_to_fill_level = TRUE;
  priv->fill_level = G_MAXDOUBLE;
  priv->timer = NULL;
797

798 799
  _gtk_orientable_set_style_classes (GTK_ORIENTABLE (range));

800
  widget_node = gtk_widget_get_css_node (GTK_WIDGET (range));
801 802 803 804 805 806 807 808 809
  priv->gadget = gtk_css_custom_gadget_new_for_node (widget_node,
                                                     GTK_WIDGET (range),
                                                     gtk_range_measure,
                                                     gtk_range_allocate,
                                                     gtk_range_render,
                                                     NULL, NULL);
  priv->contents_gadget = gtk_box_gadget_new ("contents",
                                              GTK_WIDGET (range),
                                              priv->gadget, NULL);
810 811 812 813
  priv->trough_gadget = gtk_css_custom_gadget_new ("trough",
                                                   GTK_WIDGET (range),
                                                   NULL, NULL,
                                                   gtk_range_measure_trough,
814 815
                                                   gtk_range_allocate_trough,
                                                   gtk_range_render_trough,
816
                                                   NULL, NULL);
817 818
  gtk_css_gadget_set_state (priv->trough_gadget,
                            gtk_css_node_get_state (widget_node));
819
  gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->contents_gadget), -1, priv->trough_gadget,
820
                                TRUE, GTK_ALIGN_CENTER);
821 822 823 824

  priv->slider_gadget = gtk_builtin_icon_new ("slider",
                                              GTK_WIDGET (range),
                                              priv->trough_gadget, NULL);
825 826
  gtk_css_gadget_set_state (priv->slider_gadget,
                            gtk_css_node_get_state (widget_node));
827

828 829 830 831 832 833 834
  /* Note: Order is important here.
   * The ::drag-begin handler relies on the state set up by the
   * multipress ::pressed handler. Gestures are handling events
   * in the oppposite order in which they are added to their
   * widget.
   */
  priv->drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (range));
835
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->drag_gesture), 0);
836 837 838 839 840
  g_signal_connect (priv->drag_gesture, "drag-begin",
                    G_CALLBACK (gtk_range_drag_gesture_begin), range);
  g_signal_connect (priv->drag_gesture, "drag-update",
                    G_CALLBACK (gtk_range_drag_gesture_update), range);

841
  priv->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (range));
842
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->multipress_gesture), 0);
843
  gtk_gesture_group (priv->drag_gesture, priv->multipress_gesture);
844 845 846 847 848 849
  g_signal_connect (priv->multipress_gesture, "pressed",
                    G_CALLBACK (gtk_range_multipress_gesture_pressed), range);
  g_signal_connect (priv->multipress_gesture, "released",
                    G_CALLBACK (gtk_range_multipress_gesture_released), range);

  priv->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (range));
850
  g_object_set (priv->long_press_gesture, "delay-factor", 2.0, NULL);
851 852 853
  gtk_gesture_group (priv->drag_gesture, priv->long_press_gesture);
  g_signal_connect (priv->long_press_gesture, "pressed",
                    G_CALLBACK (gtk_range_long_press_gesture_pressed), range);
Elliot Lee's avatar
Elliot Lee committed
854 855
}

856 857 858 859
/**
 * gtk_range_get_adjustment:
 * @range: a #GtkRange
 * 
860
 * Get the #GtkAdjustment which is the “model” object for #GtkRange.
861 862 863 864
 * See gtk_range_set_adjustment() for details.
 * The return value does not have a reference added, so should not
 * be unreferenced.
 * 
865
 * Returns: (transfer none): a #GtkAdjustment
866
 **/
Elliot Lee's avatar
Elliot Lee committed
867 868 869
GtkAdjustment*
gtk_range_get_adjustment (GtkRange *range)
{
870
  GtkRangePrivate *priv;
871

Elliot Lee's avatar
Elliot Lee committed
872 873
  g_return_val_if_fail (GTK_IS_RANGE (range), NULL);

874 875 876
  priv = range->priv;

  if (!priv->adjustment)
877 878
    gtk_range_set_adjustment (range, NULL);

879
  return priv->adjustment;
Elliot Lee's avatar
Elliot Lee committed
880 881
}

882 883 884 885 886
/**
 * gtk_range_set_adjustment:
 * @range: a #GtkRange
 * @adjustment: a #GtkAdjustment
 *
887
 * Sets the adjustment to be used as the “model” object for this range
888 889 890 891 892 893 894
 * widget. The adjustment indicates the current range value, the
 * minimum and maximum range values, the step/page increments used
 * for keybindings and scrolling, and the page size. The page size
 * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
 * indicates the size of the visible area of the widget being scrolled.
 * The page size affects the size of the scrollbar slider.
 **/
Elliot Lee's avatar
Elliot Lee committed
895 896 897 898
void
gtk_range_set_adjustment (GtkRange      *range,
			  GtkAdjustment *adjustment)
{
899
  GtkRangePrivate *priv;
900

Elliot Lee's avatar
Elliot Lee committed
901
  g_return_if_fail (GTK_IS_RANGE (range));
902 903 904

  priv = range->priv;

905
  if (!adjustment)
Javier Jardón's avatar
Javier Jardón committed
906
    adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
907 908
  else
    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
Elliot Lee's avatar
Elliot Lee committed
909

910
  if (priv->adjustment != adjustment)
Elliot Lee's avatar
Elliot Lee committed
911
    {
912
      if (priv->adjustment)
913
	{
914
	  g_signal_handlers_disconnect_by_func (priv->adjustment,
Manish Singh's avatar
Manish Singh committed
915 916
						gtk_range_adjustment_changed,
						range);
917
	  g_signal_handlers_disconnect_by_func (priv->adjustment,
Manish Singh's avatar
Manish Singh committed
918 919
						gtk_range_adjustment_value_changed,
						range);
920
	  g_object_unref (priv->adjustment);
921
	}
922

923
      priv->adjustment = adjustment;
924
      g_object_ref_sink (adjustment);
925
      
Manish Singh's avatar
Manish Singh committed
926 927 928
      g_signal_connect (adjustment, "changed",
			G_CALLBACK (gtk_range_adjustment_changed),
			range);
929
      g_signal_connect (adjustment, "value-changed",
Manish Singh's avatar
Manish Singh committed
930 931
			G_CALLBACK (gtk_range_adjustment_value_changed),
			range);
932
      
933
      gtk_range_adjustment_changed (adjustment, range);
934
      g_object_notify_by_pspec (G_OBJECT (range), properties[PROP_ADJUSTMENT]);
Elliot Lee's avatar
Elliot Lee committed
935 936 937
    }
}

938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
static gboolean
should_invert (GtkRange *range)
{
  GtkRangePrivate *priv = range->priv;

  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
    return
      (priv->inverted && !priv->flippable) ||
      (priv->inverted && priv->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
      (!priv->inverted && priv->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
  else
    return priv->inverted;
}

static void
update_highlight_position (GtkRange *range)
{
  GtkRangePrivate *priv = range->priv;

  if (!priv->highlight_gadget)
    return;

  if (should_invert (range))
    {
      gtk_css_gadget_remove_class (priv->highlight_gadget, GTK_STYLE_CLASS_TOP);
      gtk_css_gadget_add_class (priv->highlight_gadget, GTK_STYLE_CLASS_BOTTOM);
    }
  else
    {
      gtk_css_gadget_remove_class (priv->highlight_gadget, GTK_STYLE_CLASS_BOTTOM);
      gtk_css_gadget_add_class (priv->highlight_gadget, GTK_STYLE_CLASS_TOP);
    }
}

static void
update_fill_position (GtkRange *range)
{
  GtkRangePrivate *priv = range->priv;

  if (!priv->fill_gadget)
    return;

  if (should_invert (range))
    {
      gtk_css_gadget_remove_class (priv->fill_gadget, GTK_STYLE_CLASS_TOP);
      gtk_css_gadget_add_class (priv->fill_gadget, GTK_STYLE_CLASS_BOTTOM);
    }
  else
    {
      gtk_css_gadget_remove_class (priv->fill_gadget, GTK_STYLE_CLASS_BOTTOM);
      gtk_css_gadget_add_class (priv->fill_gadget, GTK_STYLE_CLASS_TOP);
    }
}

992
static void
993 994
update_stepper_state (GtkRange     *range,
                      GtkCssGadget *gadget)
995 996 997 998 999 1000 1001
{
  GtkRangePrivate *priv = range->priv;
  GtkStateFlags state;
  gboolean arrow_sensitive;

  state = gtk_widget_get_state_flags (GTK_WIDGET (range));

1002 1003 1004 1005
  if ((!priv->inverted &&
       (gadget == priv->stepper_a_gadget || gadget == priv->stepper_c_gadget)) ||
      (priv->inverted &&
       (gadget == priv->stepper_b_gadget || gadget == priv->stepper_d_gadget)))
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
    arrow_sensitive = priv->lower_sensitive;
  else
    arrow_sensitive = priv->upper_sensitive;

  state &= ~(GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT);

  if ((state & GTK_STATE_FLAG_INSENSITIVE) || !arrow_sensitive)
    {
      state |= GTK_STATE_FLAG_INSENSITIVE;
    }
  else
    {
1018
      if (priv->grab_location == gadget)
1019
        state |= GTK_STATE_FLAG_ACTIVE;
1020
      if (priv->mouse_location == gadget)
1021 1022 1023
        state |= GTK_STATE_FLAG_PRELIGHT;
    }

1024
  gtk_css_gadget_set_state (gadget, state);
1025 1026 1027 1028 1029 1030 1031
}

static void
update_steppers_state (GtkRange *range)
{
  GtkRangePrivate *priv = range->priv;

1032
  if (priv->stepper_a_gadget)
1033
    update_stepper_state (range, priv->stepper_a_gadget);
1034
  if (priv->stepper_b_gadget)
1035
    update_stepper_state (range, priv->stepper_b_gadget);
1036
  if (priv->stepper_c_gadget)
1037
    update_stepper_state (range, priv->stepper_c_gadget);
1038
  if (priv->stepper_d_gadget)
1039
    update_stepper_state (range, priv->stepper_d_gadget);
1040 1041
}

1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
/**
 * gtk_range_set_inverted:
 * @range: a #GtkRange
 * @setting: %TRUE to invert the range
 *
 * Ranges normally move from lower to higher values as the
 * slider moves from top to bottom or left to right. Inverted
 * ranges have higher values at the top or on the right rather than
 * on the bottom or left.
 **/
1052 1053 1054 1055
void
gtk_range_set_inverted (GtkRange *range,
                        gboolean  setting)
{
1056
  GtkRangePrivate *priv;
1057

1058
  g_return_if_fail (GTK_IS_RANGE (range));
1059 1060 1061

  priv = range->priv;

1062 1063
  setting = setting != FALSE;

1064
  if (setting != priv->inverted)
1065
    {
1066
      priv->inverted = setting;
1067 1068

      update_steppers_state (range);
1069 1070 1071
      update_fill_position (range);
      update_highlight_position (range);

1072
      gtk_widget_queue_resize (GTK_WIDGET (range));
1073 1074

      g_object_notify_by_pspec (G_OBJECT (range), properties[PROP_INVERTED]);
1075 1076 1077
    }
}

1078 1079 1080 1081 1082 1083
/**
 * gtk_range_get_inverted:
 * @range: a #GtkRange
 * 
 * Gets the value set by gtk_range_set_inverted().
 * 
1084
 * Returns: %TRUE if the range is inverted
1085
 **/
1086 1087 1088 1089 1090
gboolean
gtk_range_get_inverted (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1091
  return range->priv->inverted;
1092 1093
}

1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
/**
 * gtk_range_set_flippable:
 * @range: a #GtkRange
 * @flippable: %TRUE to make the range flippable
 *
 * If a range is flippable, it will switch its direction if it is
 * horizontal and its direction is %GTK_TEXT_DIR_RTL.
 *
 * See gtk_widget_get_direction().
 *
 * Since: 2.18
 **/
void
gtk_range_set_flippable (GtkRange *range,
                         gboolean  flippable)
{
1110
  GtkRangePrivate *priv;
1111

1112 1113
  g_return_if_fail (GTK_IS_RANGE (range));

1114 1115
  priv = range->priv;

1116 1117
  flippable = flippable ? TRUE : FALSE;

1118
  if (flippable != priv->flippable)
1119
    {
1120
      priv->flippable = flippable;
1121 1122
      update_fill_position (range);
      update_highlight_position (range);
1123

1124
      gtk_widget_queue_allocate (GTK_WIDGET (range));
1125 1126 1127 1128 1129 1130 1131 1132 1133
    }
}

/**
 * gtk_range_get_flippable:
 * @range: a #GtkRange
 *
 * Gets the value set by gtk_range_set_flippable().
 *
1134
 * Returns: %TRUE if the range is flippable
1135 1136 1137 1138 1139 1140 1141 1142
 *
 * Since: 2.18
 **/
gboolean
gtk_range_get_flippable (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1143
  return range->priv->flippable;
1144 1145
}

1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
void
gtk_range_set_slider_use_min_size (GtkRange *range,
                                   gboolean  use_min_size)
{
  GtkRangePrivate *priv = range->priv;

  if (use_min_size != priv->slider_use_min_size)
    {
      priv->slider_use_min_size = use_min_size;
      gtk_css_gadget_queue_resize (priv->slider_gadget);
    }
}

1159 1160 1161 1162 1163
/**
 * gtk_range_set_slider_size_fixed:
 * @range: a #GtkRange
 * @size_fixed: %TRUE to make the slider size constant
 *
1164 1165
 * Sets whether the range’s slider has a fixed size, or a size that
 * depends on its adjustment’s page size.
1166 1167 1168 1169 1170 1171 1172 1173 1174
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
 **/
void
gtk_range_set_slider_size_fixed (GtkRange *range,
                                 gboolean  size_fixed)
{
1175
  GtkRangePrivate *priv;
1176

1177 1178
  g_return_if_fail (GTK_IS_RANGE (range));

1179 1180 1181
  priv = range->priv;

  if (size_fixed != priv->slider_size_fixed)
1182
    {
1183
      priv->slider_size_fixed = size_fixed ? TRUE : FALSE;
1184

1185
      if (priv->adjustment && gtk_widget_get_mapped (GTK_WIDGET (range)))
1186
        gtk_css_gadget_queue_allocate (priv->slider_gadget);
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197
    }
}

/**
 * gtk_range_get_slider_size_fixed:
 * @range: a #GtkRange
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * See gtk_range_set_slider_size_fixed().
 *
1198
 * Returns: whether the range’s slider has a fixed size.
1199 1200 1201 1202 1203 1204 1205 1206
 *
 * Since: 2.20
 **/
gboolean
gtk_range_get_slider_size_fixed (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1207
  return range->priv->slider_size_fixed;
1208 1209 1210 1211 1212
}

/**
 * gtk_range_set_min_slider_size:
 * @range: a #GtkRange
1213
 * @min_size: The slider’s minimum size
1214
 *
1215
 * Sets the minimum size of the range’s slider.
1216 1217 1218 1219
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
1220 1221 1222
 *
 * Deprecated: 3.20: Use the min-height/min-width CSS properties on the slider
 *   node.
1223 1224 1225
 **/
void
gtk_range_set_min_slider_size (GtkRange *range,
1226
                               gint      min_size)
1227
{
1228
  GtkRangePrivate *priv;
1229

1230 1231 1232
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (min_size > 0);

1233 1234 1235
  priv = range->priv;

  if (min_size != priv->min_slider_size)
1236
    {
1237
      priv->min_slider_size = min_size;
1238

1239
      gtk_widget_queue_resize (GTK_WIDGET (range));
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
    }
}

/**
 * gtk_range_get_min_slider_size:
 * @range: a #GtkRange
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * See gtk_range_set_min_slider_size().
 *
1251
 * Returns: The minimum size of the range’s slider.
1252 1253
 *
 * Since: 2.20
1254 1255 1256
 *
 * Deprecated: 3.20: Use the min-height/min-width CSS properties on the slider
 *   node.
1257 1258 1259 1260 1261 1262
 **/
gint
gtk_range_get_min_slider_size (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1263
  return range->priv->min_slider_size;
1264 1265
}

1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
static void
measure_one_gadget (GtkCssGadget *gadget,
                    int          *width_out,
                    int          *height_out)
{
  gtk_css_gadget_get_preferred_size (gadget,
                                     GTK_ORIENTATION_HORIZONTAL, -1,
                                     width_out, NULL,
                                     NULL, NULL);
  gtk_css_gadget_get_preferred_size (gadget,
                                     GTK_ORIENTATION_VERTICAL, -1,
                                     height_out, NULL,
                                     NULL, NULL);
}

1281 1282 1283
/**
 * gtk_range_get_range_rect:
 * @range: a #GtkRange
1284
 * @range_rect: (out): return location for the range rectangle
1285
 *
1286
 * This function returns the area that contains the range’s trough
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
 * and its steppers, in widget->window coordinates.
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
 **/
void
gtk_range_get_range_rect (GtkRange     *range,
                          GdkRectangle *range_rect)
{
1297
  GtkRangePrivate *priv;
1298

1299 1300 1301
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (range_rect != NULL);

1302 1303
  priv = range->priv;

1304
  gtk_css_gadget_get_margin_box (priv->contents_gadget, range_rect);
1305 1306 1307 1308 1309
}

/**
 * gtk_range_get_slider_range:
 * @range: a #GtkRange
1310 1311 1312 1313
 * @slider_start: (out) (allow-none): return location for the slider's
 *     start, or %NULL
 * @slider_end: (out) (allow-none): return location for the slider's
 *     end, or %NULL
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326
 *
 * This function returns sliders range along the long dimension,
 * in widget->window coordinates.
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
 **/
void
gtk_range_get_slider_range (GtkRange *range,
                            gint     *slider_start,
                            gint     *slider_end)
{
1327
  GtkRangePrivate *priv;
1328
  GtkAllocation slider_alloc;
1329

1330 1331
  g_return_if_fail (GTK_IS_RANGE (range));

1332 1333
  priv = range->priv;

1334
  gtk_css_gadget_get_margin_box (priv->slider_gadget, &slider_alloc);
1335

1336 1337 1338
  if (priv->orientation == GTK_ORIENTATION_VERTICAL)
    {
      if (slider_start)
1339
        *slider_start = slider_alloc.y;
1340
      if (slider_end)
1341
        *slider_end = slider_alloc.y + slider_alloc.height;
1342 1343 1344 1345
    }
  else
    {
      if (slider_start)
1346
        *slider_start = slider_alloc.x;
1347
      if (slider_end)
1348
        *slider_end = slider_alloc.x + slider_alloc.width;
1349
    }
1350 1351
}

1352 1353 1354
/**
 * gtk_range_set_lower_stepper_sensitivity:
 * @range:       a #GtkRange