gtkrange.c 126 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 77 78 79 80 81 82 83 84 85 86
typedef enum {
  MOUSE_OUTSIDE,
  MOUSE_STEPPER_A,
  MOUSE_STEPPER_B,
  MOUSE_STEPPER_C,
  MOUSE_STEPPER_D,
  MOUSE_TROUGH,
  MOUSE_SLIDER,
  MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
} MouseLocation;

87
struct _GtkRangePrivate
88 89
{
  MouseLocation      mouse_location;
90
  /* last mouse coords we got, or G_MININT if mouse is outside the range */
91 92 93
  gint  mouse_x;
  gint  mouse_y;
  MouseLocation      grab_location;   /* "grabbed" mouse location, OUTSIDE for no grab */
94

95
  GtkRangeStepTimer *timer;
96

97 98 99 100 101
  GtkAdjustment     *adjustment;
  GtkSensitivityType lower_sensitivity;
  GtkSensitivityType upper_sensitivity;

  GdkWindow         *event_window;
102

103 104 105
  /* Steppers are: < > ---- < >
   *               a b      c d
   */
106
  GtkCssGadget *gadget;
107
  GtkCssGadget *contents_gadget;
108 109 110 111 112 113 114 115
  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;
116

117
  GtkOrientation     orientation;
118 119 120 121 122 123 124 125 126

  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;
127
  gint  slide_initial_coordinate_delta;
128

129 130 131
  guint flippable              : 1;
  guint inverted               : 1;
  guint slider_size_fixed      : 1;
132
  guint slider_use_min_size    : 1;
133
  guint trough_click_forward   : 1;  /* trough click was on the forward side of slider */
134 135

  /* Stepper sensitivity */
136 137
  guint lower_sensitive        : 1;
  guint upper_sensitive        : 1;
138

139 140 141
  /* The range has an origin, should be drawn differently. Used by GtkScale */
  guint has_origin             : 1;

142 143
  /* Whether we're doing fine adjustment */
  guint zoom                   : 1;
144

145
  /* Fill level */
146
  guint show_fill_level        : 1;
147
  guint restrict_to_fill_level : 1;
148 149 150

  /* Whether dragging is ongoing */
  guint in_drag                : 1;
151 152 153 154 155 156 157

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

  GtkScrollType autoscroll_mode;
  guint autoscroll_id;
158
};
159

160

161 162 163 164 165 166 167 168
enum {
  PROP_0,
  PROP_ADJUSTMENT,
  PROP_INVERTED,
  PROP_LOWER_STEPPER_SENSITIVITY,
  PROP_UPPER_STEPPER_SENSITIVITY,
  PROP_SHOW_FILL_LEVEL,
  PROP_RESTRICT_TO_FILL_LEVEL,
169
  PROP_FILL_LEVEL,
170 171 172
  PROP_ROUND_DIGITS,
  PROP_ORIENTATION,
  LAST_PROP = PROP_ORIENTATION
173
};
174

175 176 177 178 179 180
enum {
  VALUE_CHANGED,
  ADJUST_BOUNDS,
  MOVE_SLIDER,
  CHANGE_VALUE,
  LAST_SIGNAL
181 182
};

183
typedef enum {
184 185 186 187
  STEPPER_A = MOUSE_STEPPER_A,
  STEPPER_B = MOUSE_STEPPER_B,
  STEPPER_C = MOUSE_STEPPER_C,
  STEPPER_D = MOUSE_STEPPER_D
188
} Stepper;
189

190 191 192 193 194 195 196 197
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);
198
static void gtk_range_destroy        (GtkWidget        *widget);
199 200 201 202 203 204 205 206
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);
207 208 209 210
static void gtk_range_size_allocate  (GtkWidget        *widget,
                                      GtkAllocation    *allocation);
static void gtk_range_realize        (GtkWidget        *widget);
static void gtk_range_unrealize      (GtkWidget        *widget);
211 212
static void gtk_range_map            (GtkWidget        *widget);
static void gtk_range_unmap          (GtkWidget        *widget);
213 214
static gboolean gtk_range_draw       (GtkWidget        *widget,
                                      cairo_t          *cr);
215 216 217 218 219 220 221 222 223 224 225

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);
226 227 228 229
static void gtk_range_drag_gesture_begin          (GtkGestureDrag       *gesture,
                                                   gdouble               offset_x,
                                                   gdouble               offset_y,
                                                   GtkRange             *range);
230 231 232 233 234 235 236 237 238 239
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);


240
static gboolean gtk_range_scroll_event   (GtkWidget        *widget,
241
                                      GdkEventScroll   *event);
242 243
static gboolean gtk_range_event       (GtkWidget       *widget,
                                       GdkEvent        *event);
244 245 246
static void update_slider_position   (GtkRange	       *range,
				      gint              mouse_x,
				      gint              mouse_y);
247
static void stop_scrolling           (GtkRange         *range);
248 249
static void add_autoscroll           (GtkRange         *range);
static void remove_autoscroll        (GtkRange         *range);
250 251

/* Range methods */
252

253
static void gtk_range_move_slider              (GtkRange         *range,
254 255 256
                                                GtkScrollType     scroll);

/* Internals */
257 258 259
static void          gtk_range_compute_slider_position  (GtkRange      *range,
                                                         gdouble        adjustment_value,
                                                         GdkRectangle  *slider_rect);
260
static gboolean      gtk_range_scroll                   (GtkRange      *range,
261
                                                         GtkScrollType  scroll);
262
static void          gtk_range_update_mouse_location    (GtkRange      *range);
263 264
static void          gtk_range_calc_slider              (GtkRange      *range);
static void          gtk_range_calc_stepper_sensitivity (GtkRange      *range);
265
static void          gtk_range_calc_marks               (GtkRange      *range);
266 267 268 269 270 271 272
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);
Cosimo Cecchi's avatar
Cosimo Cecchi committed
273
static void          gtk_range_queue_allocate_location  (GtkRange      *range,
274
                                                         MouseLocation  location);
275 276 277
static gboolean      gtk_range_real_change_value        (GtkRange      *range,
                                                         GtkScrollType  scroll,
                                                         gdouble        value);
278 279
static gboolean      gtk_range_key_press                (GtkWidget     *range,
							 GdkEventKey   *event);
280 281
static void          gtk_range_state_flags_changed      (GtkWidget     *widget,
                                                         GtkStateFlags  previous_state);
282 283
static void          gtk_range_direction_changed        (GtkWidget     *widget,
                                                         GtkTextDirection  previous_direction);
284 285
static void          gtk_range_measure_trough           (GtkCssGadget   *gadget,
                                                         GtkOrientation  orientation,
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
                                                         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);
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
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
324

325
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
326
                                  G_ADD_PRIVATE (GtkRange)
327 328 329
                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
                                                         NULL))

330
static guint signals[LAST_SIGNAL];
331
static GParamSpec *properties[LAST_PROP];
Elliot Lee's avatar
Elliot Lee committed
332 333 334 335

static void
gtk_range_class_init (GtkRangeClass *class)
{
Alexander Larsson's avatar
Alexander Larsson committed
336
  GObjectClass   *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
337 338
  GtkWidgetClass *widget_class;

Alexander Larsson's avatar
Alexander Larsson committed
339
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
340 341
  widget_class = (GtkWidgetClass*) class;

Alexander Larsson's avatar
Alexander Larsson committed
342 343
  gobject_class->set_property = gtk_range_set_property;
  gobject_class->get_property = gtk_range_get_property;
344

345
  widget_class->destroy = gtk_range_destroy;
346 347
  widget_class->get_preferred_width = gtk_range_get_preferred_width;
  widget_class->get_preferred_height = gtk_range_get_preferred_height;
348 349
  widget_class->size_allocate = gtk_range_size_allocate;
  widget_class->realize = gtk_range_realize;
350
  widget_class->unrealize = gtk_range_unrealize;
351 352
  widget_class->map = gtk_range_map;
  widget_class->unmap = gtk_range_unmap;
353
  widget_class->draw = gtk_range_draw;
354
  widget_class->event = gtk_range_event;
355
  widget_class->scroll_event = gtk_range_scroll_event;
356
  widget_class->key_press_event = gtk_range_key_press;
357
  widget_class->state_flags_changed = gtk_range_state_flags_changed;
358
  widget_class->direction_changed = gtk_range_direction_changed;
359 360

  class->move_slider = gtk_range_move_slider;
361
  class->change_value = gtk_range_real_change_value;
362

Matthias Clasen's avatar
Matthias Clasen committed
363 364
  /**
   * GtkRange::value-changed:
365
   * @range: the #GtkRange that received the signal
Matthias Clasen's avatar
Matthias Clasen committed
366 367 368
   *
   * Emitted when the range value changes.
   */
369
  signals[VALUE_CHANGED] =
370
    g_signal_new (I_("value-changed"),
Manish Singh's avatar
Manish Singh committed
371
                  G_TYPE_FROM_CLASS (gobject_class),
372 373 374
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, value_changed),
                  NULL, NULL,
375
                  _gtk_marshal_VOID__VOID,
376
                  G_TYPE_NONE, 0);
377 378 379 380 381

  /**
   * GtkRange::adjust-bounds:
   * @range: the #GtkRange that received the signal
   * @value: the value before we clamp
382 383 384
   *
   * Emitted before clamping a value, to give the application a
   * chance to adjust the bounds.
385
   */
386
  signals[ADJUST_BOUNDS] =
387
    g_signal_new (I_("adjust-bounds"),
Manish Singh's avatar
Manish Singh committed
388
                  G_TYPE_FROM_CLASS (gobject_class),
389 390 391 392 393 394
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
                  NULL, NULL,
                  _gtk_marshal_VOID__DOUBLE,
                  G_TYPE_NONE, 1,
                  G_TYPE_DOUBLE);
395

Matthias Clasen's avatar
Matthias Clasen committed
396 397
  /**
   * GtkRange::move-slider:
398
   * @range: the #GtkRange that received the signal
Matthias Clasen's avatar
Matthias Clasen committed
399 400 401 402
   * @step: how to move the slider
   *
   * Virtual function that moves the slider. Used for keybindings.
   */
403
  signals[MOVE_SLIDER] =
404
    g_signal_new (I_("move-slider"),
Manish Singh's avatar
Manish Singh committed
405
                  G_TYPE_FROM_CLASS (gobject_class),
406 407 408
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkRangeClass, move_slider),
                  NULL, NULL,
409
                  _gtk_marshal_VOID__ENUM,
410 411
                  G_TYPE_NONE, 1,
                  GTK_TYPE_SCROLL_TYPE);
412 413 414

  /**
   * GtkRange::change-value:
415
   * @range: the #GtkRange that received the signal
Matthias Clasen's avatar
Matthias Clasen committed
416 417
   * @scroll: the type of scroll action that was performed
   * @value: the new value resulting from the scroll action
418
   *
419
   * The #GtkRange::change-value signal is emitted when a scroll action is
420 421 422 423 424 425 426 427
   * 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
428 429 430
   * 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.
431 432
   *
   * It is not possible to use delayed update policies in an overridden
433
   * #GtkRange::change-value handler.
434
   *
435 436 437
   * Returns: %TRUE to prevent other handlers from being invoked for
   *     the signal, %FALSE to propagate the signal further
   *
438 439 440
   * Since: 2.6
   */
  signals[CHANGE_VALUE] =
441
    g_signal_new (I_("change-value"),
442 443 444 445 446 447 448 449
                  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
450

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
  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);
482

483 484 485
  /**
   * GtkRange:show-fill-level:
   *
Matthias Clasen's avatar
Matthias Clasen committed
486
   * The show-fill-level property controls whether fill level indicator
487 488 489 490 491
   * graphics are displayed on the trough. See
   * gtk_range_set_show_fill_level().
   *
   * Since: 2.12
   **/
492 493 494 495 496 497
  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);
498 499 500 501

  /**
   * GtkRange:restrict-to-fill-level:
   *
Matthias Clasen's avatar
Matthias Clasen committed
502
   * The restrict-to-fill-level property controls whether slider
503
   * movement is restricted to an upper boundary set by the
Matthias Clasen's avatar
Matthias Clasen committed
504
   * fill level. See gtk_range_set_restrict_to_fill_level().
505 506 507
   *
   * Since: 2.12
   **/
508 509 510 511 512 513
  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);
514 515 516 517 518 519 520 521 522

  /**
   * GtkRange:fill-level:
   *
   * The fill level (e.g. prebuffering of a network stream).
   * See gtk_range_set_fill_level().
   *
   * Since: 2.12
   **/
523 524 525 526 527 528 529
  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);
530

531 532 533 534 535 536 537 538
  /**
   * GtkRange:round-digits:
   *
   * The number of digits to round the value to when
   * it changes, or -1. See #GtkRange::change-value.
   *
   * Since: 2.24
   */
539 540 541 542 543 544 545 546 547
  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);
548

549 550 551 552 553
  /**
   * GtkRange:slider-width:
   *
   * Width of scrollbar or scale thumb.
   *
Timm Bäder's avatar
Timm Bäder committed
554
   * Deprecated: 3.20: Use the min-height/min-width CSS properties on the
555 556
   *   slider element. The value of this style property is ignored.
   */
557
  gtk_widget_class_install_style_property (widget_class,
558
					   g_param_spec_int ("slider-width",
559 560
							     P_("Slider Width"),
							     P_("Width of scrollbar or scale thumb"),
561 562
							     0,
							     G_MAXINT,
563
							     14,
564 565 566 567 568 569
							     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
570
   * Deprecated: 3.20: Use the margin/padding CSS properties on the trough and
571 572
   *   stepper elements. The value of this style property is ignored.
   */
573
  gtk_widget_class_install_style_property (widget_class,
574
					   g_param_spec_int ("trough-border",
575 576
                                                             P_("Trough Border"),
                                                             P_("Spacing between thumb/steppers and outer trough bevel"),
577 578 579
                                                             0,
                                                             G_MAXINT,
                                                             1,
580 581 582 583 584 585
                                                             GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
  /**
   * GtkRange:stepper-size:
   *
   * Length of step buttons at ends.
   *
Timm Bäder's avatar
Timm Bäder committed
586
   * Deprecated: 3.20: Use the min-height/min-width CSS properties on the
587 588
   *   stepper elements. The value of this style property is ignored.
   */
589
  gtk_widget_class_install_style_property (widget_class,
590
					   g_param_spec_int ("stepper-size",
591 592
							     P_("Stepper Size"),
							     P_("Length of step buttons at ends"),
593 594
							     0,
							     G_MAXINT,
595
							     14,
596
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
597 598 599 600 601
  /**
   * 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.
602
   *
Timm Bäder's avatar
Timm Bäder committed
603
   * Deprecated: 3.20: Use the margin CSS property on the stepper elements.
604
   *   The value of this style property is ignored.
605
   */
606
  gtk_widget_class_install_style_property (widget_class,
607
					   g_param_spec_int ("stepper-spacing",
608 609
							     P_("Stepper Spacing"),
							     P_("Spacing between step buttons and thumb"),
610
                                                             0,
611
							     G_MAXINT,
612
							     0,
613
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
614 615 616 617 618 619 620 621

  /**
   * 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.
   */
622
  gtk_widget_class_install_style_property (widget_class,
623
					   g_param_spec_int ("arrow-displacement-x",
624 625
							     P_("Arrow X Displacement"),
							     P_("How far in the x direction to move the arrow when the button is depressed"),
626 627 628
							     G_MININT,
							     G_MAXINT,
							     0,
629
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
630 631 632 633 634 635 636 637

  /**
   * 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.
   */
638
  gtk_widget_class_install_style_property (widget_class,
639
					   g_param_spec_int ("arrow-displacement-y",
640 641
							     P_("Arrow Y Displacement"),
							     P_("How far in the y direction to move the arrow when the button is depressed"),
642 643 644
							     G_MININT,
							     G_MAXINT,
							     0,
645
							     GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
646

647 648 649 650
  /**
   * GtkRange:trough-under-steppers:
   *
   * Whether to draw the trough across the full length of the range or
651
   * to exclude the steppers and their spacing.
652 653
   *
   * Since: 2.10
654 655 656
   *
   * Deprecated: 3.20: The value of this style property is ignored, and the
   *   widget will behave as if it was set to %TRUE.
657 658 659 660
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boolean ("trough-under-steppers",
                                                                 P_("Trough Under Steppers"),
661
                                                                 P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
662
                                                                 TRUE,
663
                                                                 GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
664

665 666 667 668 669 670
  /**
   * GtkRange:arrow-scaling:
   *
   * The arrow size proportion relative to the scroll button size.
   *
   * Since: 2.14
671 672 673
   *
   * Deprecated: 3.20: Use min-width/min-height on the "button" node instead.
   *   The value of this style property is ignored.
674 675 676 677 678 679
   */
  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,
680
							       GTK_PARAM_READABLE|G_PARAM_DEPRECATED));
681

682
  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_RANGE_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
683 684
}

685 686 687 688 689 690 691 692
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));
693
  gtk_box_gadget_set_orientation (GTK_BOX_GADGET (priv->contents_gadget), orientation);
694 695

  if (orientation == GTK_ORIENTATION_VERTICAL)
696
    gtk_box_gadget_set_gadget_expand (GTK_BOX_GADGET (priv->contents_gadget),
697
                                      priv->trough_gadget, FALSE, TRUE);
698
  else
699
    gtk_box_gadget_set_gadget_expand (GTK_BOX_GADGET (priv->contents_gadget),
700
                                      priv->trough_gadget, TRUE, FALSE);
701 702
}

703
static void
Alexander Larsson's avatar
Alexander Larsson committed
704 705 706 707
gtk_range_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
708
{
709
  GtkRange *range = GTK_RANGE (object);
710
  GtkRangePrivate *priv = range->priv;
711

Alexander Larsson's avatar
Alexander Larsson committed
712
  switch (prop_id)
713
    {
714
    case PROP_ORIENTATION:
715 716 717
      if (priv->orientation != g_value_get_enum (value))
        {
          priv->orientation = g_value_get_enum (value);
718
          gtk_range_sync_orientation (range);
719 720 721
          gtk_widget_queue_resize (GTK_WIDGET (range));
          g_object_notify_by_pspec (object, pspec);
        }
722
      break;
723 724 725
    case PROP_ADJUSTMENT:
      gtk_range_set_adjustment (range, g_value_get_object (value));
      break;
726 727 728
    case PROP_INVERTED:
      gtk_range_set_inverted (range, g_value_get_boolean (value));
      break;
729 730 731 732 733 734
    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;
735 736 737 738 739 740 741 742 743
    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;
744 745 746
    case PROP_ROUND_DIGITS:
      gtk_range_set_round_digits (range, g_value_get_int (value));
      break;
747
    default:
Alexander Larsson's avatar
Alexander Larsson committed
748
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
749 750 751 752 753
      break;
    }
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
754 755 756 757
gtk_range_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
758
{
759
  GtkRange *range = GTK_RANGE (object);
760
  GtkRangePrivate *priv = range->priv;
761

Alexander Larsson's avatar
Alexander Larsson committed
762
  switch (prop_id)
763
    {
764
    case PROP_ORIENTATION:
765
      g_value_set_enum (value, priv->orientation);
766
      break;
767
    case PROP_ADJUSTMENT:
768
      g_value_set_object (value, priv->adjustment);
769
      break;
770
    case PROP_INVERTED:
771
      g_value_set_boolean (value, priv->inverted);
772
      break;
773 774 775 776 777 778
    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;
779 780 781 782 783 784 785 786 787
    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;
788 789 790
    case PROP_ROUND_DIGITS:
      g_value_set_int (value, gtk_range_get_round_digits (range));
      break;
791
    default:
Alexander Larsson's avatar
Alexander Larsson committed
792
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
793 794 795 796
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
797 798 799
static void
gtk_range_init (GtkRange *range)
{
800
  GtkRangePrivate *priv;
801
  GtkCssNode *widget_node;
802

803
  range->priv = gtk_range_get_instance_private (range);
804 805
  priv = range->priv;

806
  gtk_widget_set_has_window (GTK_WIDGET (range), FALSE);
807

808 809 810 811 812 813 814
  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
  priv->adjustment = NULL;
  priv->inverted = FALSE;
  priv->flippable = FALSE;
  priv->min_slider_size = 1;
  priv->round_digits = -1;
  priv->mouse_location = MOUSE_OUTSIDE;
815 816
  priv->mouse_x = G_MININT;
  priv->mouse_y = G_MININT;
817 818 819 820 821
  priv->grab_location = MOUSE_OUTSIDE;
  priv->lower_sensitivity = GTK_SENSITIVITY_AUTO;
  priv->upper_sensitivity = GTK_SENSITIVITY_AUTO;
  priv->lower_sensitive = TRUE;
  priv->upper_sensitive = TRUE;
822
  priv->has_origin = FALSE;
823 824 825 826
  priv->show_fill_level = FALSE;
  priv->restrict_to_fill_level = TRUE;
  priv->fill_level = G_MAXDOUBLE;
  priv->timer = NULL;
827

828 829
  _gtk_orientable_set_style_classes (GTK_ORIENTABLE (range));

830
  widget_node = gtk_widget_get_css_node (GTK_WIDGET (range));
831 832 833 834 835 836 837 838 839
  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);
840 841 842 843
  priv->trough_gadget = gtk_css_custom_gadget_new ("trough",
                                                   GTK_WIDGET (range),
                                                   NULL, NULL,
                                                   gtk_range_measure_trough,
844 845
                                                   gtk_range_allocate_trough,
                                                   gtk_range_render_trough,
846
                                                   NULL, NULL);
847 848
  gtk_css_gadget_set_state (priv->trough_gadget,
                            gtk_css_node_get_state (widget_node));
849
  gtk_box_gadget_insert_gadget (GTK_BOX_GADGET (priv->contents_gadget), -1, priv->trough_gadget,
850
                                TRUE, FALSE, GTK_ALIGN_CENTER);
851 852 853 854

  priv->slider_gadget = gtk_builtin_icon_new ("slider",
                                              GTK_WIDGET (range),
                                              priv->trough_gadget, NULL);
855 856
  gtk_css_gadget_set_state (priv->slider_gadget,
                            gtk_css_node_get_state (widget_node));
857

858 859 860 861 862 863 864
  /* 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));
865
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->drag_gesture), 0);
866 867 868 869 870
  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);

871
  priv->multipress_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (range));
872
  gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (priv->multipress_gesture), 0);
873
  gtk_gesture_group (priv->drag_gesture, priv->multipress_gesture);
874 875 876 877 878 879
  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));
880
  g_object_set (priv->long_press_gesture, "delay-factor", 2.0, NULL);
881 882 883
  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
884 885
}

886 887 888 889
/**
 * gtk_range_get_adjustment:
 * @range: a #GtkRange
 * 
890
 * Get the #GtkAdjustment which is the “model” object for #GtkRange.
891 892 893 894
 * See gtk_range_set_adjustment() for details.
 * The return value does not have a reference added, so should not
 * be unreferenced.
 * 
895
 * Returns: (transfer none): a #GtkAdjustment
896
 **/
Elliot Lee's avatar
Elliot Lee committed
897 898 899
GtkAdjustment*
gtk_range_get_adjustment (GtkRange *range)
{
900
  GtkRangePrivate *priv;
901

Elliot Lee's avatar
Elliot Lee committed
902 903
  g_return_val_if_fail (GTK_IS_RANGE (range), NULL);

904 905 906
  priv = range->priv;

  if (!priv->adjustment)
907 908
    gtk_range_set_adjustment (range, NULL);

909
  return priv->adjustment;
Elliot Lee's avatar
Elliot Lee committed
910 911
}

912 913 914 915 916
/**
 * gtk_range_set_adjustment:
 * @range: a #GtkRange
 * @adjustment: a #GtkAdjustment
 *
917
 * Sets the adjustment to be used as the “model” object for this range
918 919 920 921 922 923 924
 * 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
925 926 927 928
void
gtk_range_set_adjustment (GtkRange      *range,
			  GtkAdjustment *adjustment)
{
929
  GtkRangePrivate *priv;
930

Elliot Lee's avatar
Elliot Lee committed
931
  g_return_if_fail (GTK_IS_RANGE (range));
932 933 934

  priv = range->priv;

935
  if (!adjustment)
Javier Jardón's avatar
Javier Jardón committed
936
    adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
937 938
  else
    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
Elliot Lee's avatar
Elliot Lee committed
939

940
  if (priv->adjustment != adjustment)
Elliot Lee's avatar
Elliot Lee committed
941
    {
942
      if (priv->adjustment)
943
	{
944
	  g_signal_handlers_disconnect_by_func (priv->adjustment,
Manish Singh's avatar
Manish Singh committed
945 946
						gtk_range_adjustment_changed,
						range);
947
	  g_signal_handlers_disconnect_by_func (priv->adjustment,
Manish Singh's avatar
Manish Singh committed
948 949
						gtk_range_adjustment_value_changed,
						range);
950
	  g_object_unref (priv->adjustment);
951
	}
952

953
      priv->adjustment = adjustment;
954
      g_object_ref_sink (adjustment);
955
      
Manish Singh's avatar
Manish Singh committed
956 957 958
      g_signal_connect (adjustment, "changed",
			G_CALLBACK (gtk_range_adjustment_changed),
			range);
959
      g_signal_connect (adjustment, "value-changed",
Manish Singh's avatar
Manish Singh committed
960 961
			G_CALLBACK (gtk_range_adjustment_value_changed),
			range);
962
      
963
      gtk_range_adjustment_changed (adjustment, range);
964
      g_object_notify_by_pspec (G_OBJECT (range), properties[PROP_ADJUSTMENT]);
Elliot Lee's avatar
Elliot Lee committed
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 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
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);
    }
}

1022
static void
1023 1024 1025
update_stepper_state (GtkRange     *range,
                      Stepper       stepper,
                      GtkCssGadget *gadget)
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
{
  GtkRangePrivate *priv = range->priv;
  GtkStateFlags state;
  gboolean arrow_sensitive;

  state = gtk_widget_get_state_flags (GTK_WIDGET (range));

  if ((!priv->inverted && (stepper == STEPPER_A ||
                           stepper == STEPPER_C)) ||
      (priv->inverted && (stepper == STEPPER_B ||
                          stepper == STEPPER_D)))
    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
    {
      if (priv->grab_location == (MouseLocation)stepper)
        state |= GTK_STATE_FLAG_ACTIVE;
      if (priv->mouse_location == (MouseLocation)stepper)
        state |= GTK_STATE_FLAG_PRELIGHT;
    }

1055
  gtk_css_gadget_set_state (gadget, state);
1056 1057 1058 1059 1060 1061 1062
}

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

1063 1064 1065 1066 1067 1068 1069 1070
  if (priv->stepper_a_gadget)
    update_stepper_state (range, STEPPER_A, priv->stepper_a_gadget);
  if (priv->stepper_b_gadget)
    update_stepper_state (range, STEPPER_B, priv->stepper_b_gadget);
  if (priv->stepper_c_gadget)
    update_stepper_state (range, STEPPER_C, priv->stepper_c_gadget);
  if (priv->stepper_d_gadget)
    update_stepper_state (range, STEPPER_D, priv->stepper_d_gadget);
1071 1072
}

1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
/**
 * 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.
 **/
1083 1084 1085 1086
void
gtk_range_set_inverted (GtkRange *range,
                        gboolean  setting)
{
1087
  GtkRangePrivate *priv;
1088

1089
  g_return_if_fail (GTK_IS_RANGE (range));
1090 1091 1092

  priv = range->priv;

1093 1094
  setting = setting != FALSE;

1095
  if (setting != priv->inverted)
1096
    {
1097
      priv->inverted = setting;
1098 1099

      update_steppers_state (range);
1100 1101 1102
      update_fill_position (range);
      update_highlight_position (range);

1103
      gtk_widget_queue_resize (GTK_WIDGET (range));
1104 1105

      g_object_notify_by_pspec (G_OBJECT (range), properties[PROP_INVERTED]);
1106 1107 1108
    }
}

1109 1110 1111 1112 1113 1114
/**
 * gtk_range_get_inverted:
 * @range: a #GtkRange
 * 
 * Gets the value set by gtk_range_set_inverted().
 * 
1115
 * Returns: %TRUE if the range is inverted
1116
 **/
1117 1118 1119 1120 1121
gboolean
gtk_range_get_inverted (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1122
  return range->priv->inverted;
1123 1124
}

1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
/**
 * 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)
{
1141
  GtkRangePrivate *priv;
1142

1143 1144
  g_return_if_fail (GTK_IS_RANGE (range));

1145 1146
  priv = range->priv;

1147 1148
  flippable = flippable ? TRUE : FALSE;

1149
  if (flippable != priv->flippable)
1150
    {
1151
      priv->flippable = flippable;
1152 1153
      update_fill_position (range);
      update_highlight_position (range);
1154

1155
      gtk_widget_queue_allocate (GTK_WIDGET (range));
1156 1157 1158 1159 1160 1161 1162 1163 1164
    }
}

/**
 * gtk_range_get_flippable:
 * @range: a #GtkRange
 *
 * Gets the value set by gtk_range_set_flippable().
 *
1165
 * Returns: %TRUE if the range is flippable
1166 1167 1168 1169 1170 1171 1172 1173
 *
 * Since: 2.18
 **/
gboolean
gtk_range_get_flippable (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1174
  return range->priv->flippable;
1175 1176
}

1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
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);
    }
}

1190 1191 1192 1193 1194
/**
 * gtk_range_set_slider_size_fixed:
 * @range: a #GtkRange
 * @size_fixed: %TRUE to make the slider size constant
 *
1195 1196
 * Sets whether the range’s slider has a fixed size, or a size that
 * depends on its adjustment’s page size.
1197 1198 1199 1200 1201 1202 1203 1204 1205
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
 **/
void
gtk_range_set_slider_size_fixed (GtkRange *range,
                                 gboolean  size_fixed)
{
1206
  GtkRangePrivate *priv;
1207

1208 1209
  g_return_if_fail (GTK_IS_RANGE (range));

1210 1211 1212
  priv = range->priv;

  if (size_fixed != priv->slider_size_fixed)
1213
    {
1214
      priv->slider_size_fixed = size_fixed ? TRUE : FALSE;
1215

1216
      if (priv->adjustment && gtk_widget_get_mapped (GTK_WIDGET (range)))
1217
        gtk_css_gadget_queue_allocate (priv->slider_gadget);
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
    }
}

/**
 * gtk_range_get_slider_size_fixed:
 * @range: a #GtkRange
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * See gtk_range_set_slider_size_fixed().
 *
1229
 * Returns: whether the range’s slider has a fixed size.
1230 1231 1232 1233 1234 1235 1236 1237
 *
 * Since: 2.20
 **/
gboolean
gtk_range_get_slider_size_fixed (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1238
  return range->priv->slider_size_fixed;
1239 1240 1241 1242 1243
}

/**
 * gtk_range_set_min_slider_size:
 * @range: a #GtkRange
1244
 * @min_size: The slider’s minimum size
1245
 *
1246
 * Sets the minimum size of the range’s slider.
1247 1248 1249 1250
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
1251 1252 1253
 *
 * Deprecated: 3.20: Use the min-height/min-width CSS properties on the slider
 *   node.
1254 1255 1256
 **/
void
gtk_range_set_min_slider_size (GtkRange *range,
1257
                               gint      min_size)
1258
{
1259
  GtkRangePrivate *priv;
1260

1261 1262 1263
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (min_size > 0);

1264 1265 1266
  priv = range->priv;

  if (min_size != priv->min_slider_size)
1267
    {
1268
      priv->min_slider_size = min_size;
1269

1270
      gtk_widget_queue_resize (GTK_WIDGET (range));
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
    }
}

/**
 * gtk_range_get_min_slider_size:
 * @range: a #GtkRange
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * See gtk_range_set_min_slider_size().
 *
1282
 * Returns: The minimum size of the range’s slider.
1283 1284
 *
 * Since: 2.20
1285 1286 1287
 *
 * Deprecated: 3.20: Use the min-height/min-width CSS properties on the slider
 *   node.
1288 1289 1290 1291 1292 1293
 **/
gint
gtk_range_get_min_slider_size (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1294
  return range->priv->min_slider_size;
1295 1296
}

1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
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);
}

1312 1313 1314
/**
 * gtk_range_get_range_rect:
 * @range: a #GtkRange
1315
 * @range_rect: (out): return location for the range rectangle
1316
 *
1317
 * This function returns the area that contains the range’s trough
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
 * 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)
{
1328
  GtkRangePrivate *priv;
1329

1330 1331 1332
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (range_rect != NULL);

1333 1334
  priv = range->priv;

1335
  gtk_css_gadget_get_margin_box (priv->contents_gadget, range_rect);
1336 1337 1338 1339 1340
}

/**
 * gtk_range_get_slider_range:
 * @range: a #GtkRange
1341 1342 1343 1344
 * @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
1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
 *
 * 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)
{
1358
  GtkRangePrivate *priv;
1359
  GtkAllocation slider_alloc;
1360

1361 1362
  g_return_if_fail (GTK_IS_RANGE (range));

1363 1364
  priv = range->priv;

1365
  gtk_css_gadget_get_margin_box (priv->slider_gadget, &slider_alloc);
1366

1367 1368 1369
  if (priv->orientation == GTK_ORIENTATION_VERTICAL)
    {
      if (slider_start)
1370
        *slider_start = slider_alloc.y;
1371
      if (slider_end)
1372
        *slider_end = slider_alloc.y + slider_alloc.height;
1373 1374 1375 1376
    }
  else
    {
      if (slider_start)
1377
        *slider_start = slider_alloc.x;
1378
      if (slider_end)
1379
        *slider_end = slider_alloc.x + slider_alloc.width;
1380
    }