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
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
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;
}

952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
static gboolean
should_invert_move (GtkRange       *range,
                    GtkOrientation  move_orientation)
{
  GtkRangePrivate *priv = range->priv;

  /* If the move is parallel to the range, use general check for inversion */
  if (move_orientation == priv->orientation)
    return should_invert (range);

  /* H range/V move: Always invert, so down/up always dec/increase the value */
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
    return TRUE;

  /* V range/H move: Left/right always dec/increase the value */
  return FALSE;
}

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
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);
    }
}

1010
static void
1011 1012
update_stepper_state (GtkRange     *range,
                      GtkCssGadget *gadget)
1013 1014 1015 1016 1017 1018 1019
{
  GtkRangePrivate *priv = range->priv;
  GtkStateFlags state;
  gboolean arrow_sensitive;

  state = gtk_widget_get_state_flags (GTK_WIDGET (range));

1020 1021 1022 1023
  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)))
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
    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
    {
1036
      if (priv->grab_location == gadget)
1037
        state |= GTK_STATE_FLAG_ACTIVE;
1038
      if (priv->mouse_location == gadget)
1039 1040 1041
        state |= GTK_STATE_FLAG_PRELIGHT;
    }

1042
  gtk_css_gadget_set_state (gadget, state);
1043 1044 1045 1046 1047 1048 1049
}

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

1050
  if (priv->stepper_a_gadget)
1051
    update_stepper_state (range, priv->stepper_a_gadget);
1052
  if (priv->stepper_b_gadget)
1053
    update_stepper_state (range, priv->stepper_b_gadget);
1054
  if (priv->stepper_c_gadget)
1055
    update_stepper_state (range, priv->stepper_c_gadget);
1056
  if (priv->stepper_d_gadget)
1057
    update_stepper_state (range, priv->stepper_d_gadget);
1058 1059
}

1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
/**
 * 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.
 **/
1070 1071 1072 1073
void
gtk_range_set_inverted (GtkRange *range,
                        gboolean  setting)
{
1074
  GtkRangePrivate *priv;
1075

1076
  g_return_if_fail (GTK_IS_RANGE (range));
1077 1078 1079

  priv = range->priv;

1080 1081
  setting = setting != FALSE;

1082
  if (setting != priv->inverted)
1083
    {
1084
      priv->inverted = setting;
1085 1086

      update_steppers_state (range);
1087 1088 1089
      update_fill_position (range);
      update_highlight_position (range);

1090
      gtk_widget_queue_resize (GTK_WIDGET (range));
1091 1092

      g_object_notify_by_pspec (G_OBJECT (range), properties[PROP_INVERTED]);
1093 1094 1095
    }
}

1096 1097 1098 1099 1100 1101
/**
 * gtk_range_get_inverted:
 * @range: a #GtkRange
 * 
 * Gets the value set by gtk_range_set_inverted().
 * 
1102
 * Returns: %TRUE if the range is inverted
1103
 **/
1104 1105 1106 1107 1108
gboolean
gtk_range_get_inverted (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1109
  return range->priv->inverted;
1110 1111
}

1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
/**
 * 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)
{
1128
  GtkRangePrivate *priv;
1129

1130 1131
  g_return_if_fail (GTK_IS_RANGE (range));

1132 1133
  priv = range->priv;

1134 1135
  flippable = flippable ? TRUE : FALSE;

1136
  if (flippable != priv->flippable)
1137
    {
1138
      priv->flippable = flippable;
1139 1140
      update_fill_position (range);
      update_highlight_position (range);
1141

1142
      gtk_widget_queue_allocate (GTK_WIDGET (range));
1143 1144 1145 1146 1147 1148 1149 1150 1151
    }
}

/**
 * gtk_range_get_flippable:
 * @range: a #GtkRange
 *
 * Gets the value set by gtk_range_set_flippable().
 *
1152
 * Returns: %TRUE if the range is flippable
1153 1154 1155 1156 1157 1158 1159 1160
 *
 * Since: 2.18
 **/
gboolean
gtk_range_get_flippable (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1161
  return range->priv->flippable;
1162 1163
}

1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
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);
    }
}

1177 1178 1179 1180 1181
/**
 * gtk_range_set_slider_size_fixed:
 * @range: a #GtkRange
 * @size_fixed: %TRUE to make the slider size constant
 *
1182 1183
 * Sets whether the range’s slider has a fixed size, or a size that
 * depends on its adjustment’s page size.
1184 1185 1186 1187 1188 1189 1190 1191 1192
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
 **/
void
gtk_range_set_slider_size_fixed (GtkRange *range,
                                 gboolean  size_fixed)
{
1193
  GtkRangePrivate *priv;
1194

1195 1196
  g_return_if_fail (GTK_IS_RANGE (range));

1197 1198 1199
  priv = range->priv;

  if (size_fixed != priv->slider_size_fixed)
1200
    {
1201
      priv->slider_size_fixed = size_fixed ? TRUE : FALSE;
1202

1203
      if (priv->adjustment && gtk_widget_get_mapped (GTK_WIDGET (range)))
1204
        gtk_css_gadget_queue_allocate (priv->slider_gadget);
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
    }
}

/**
 * gtk_range_get_slider_size_fixed:
 * @range: a #GtkRange
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * See gtk_range_set_slider_size_fixed().
 *
1216
 * Returns: whether the range’s slider has a fixed size.
1217 1218 1219 1220 1221 1222 1223 1224
 *
 * Since: 2.20
 **/
gboolean
gtk_range_get_slider_size_fixed (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1225
  return range->priv->slider_size_fixed;
1226 1227 1228 1229 1230
}

/**
 * gtk_range_set_min_slider_size:
 * @range: a #GtkRange
1231
 * @min_size: The slider’s minimum size
1232
 *
1233
 * Sets the minimum size of the range’s slider.
1234 1235 1236 1237
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * Since: 2.20
1238 1239 1240
 *
 * Deprecated: 3.20: Use the min-height/min-width CSS properties on the slider
 *   node.
1241 1242 1243
 **/
void
gtk_range_set_min_slider_size (GtkRange *range,
1244
                               gint      min_size)
1245
{
1246
  GtkRangePrivate *priv;
1247

1248 1249 1250
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (min_size > 0);

1251 1252 1253
  priv = range->priv;

  if (min_size != priv->min_slider_size)
1254
    {
1255
      priv->min_slider_size = min_size;
1256

1257
      gtk_widget_queue_resize (GTK_WIDGET (range));
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
    }
}

/**
 * gtk_range_get_min_slider_size:
 * @range: a #GtkRange
 *
 * This function is useful mainly for #GtkRange subclasses.
 *
 * See gtk_range_set_min_slider_size().
 *
1269
 * Returns: The minimum size of the range’s slider.
1270 1271
 *
 * Since: 2.20
1272 1273 1274
 *
 * Deprecated: 3.20: Use the min-height/min-width CSS properties on the slider
 *   node.
1275 1276 1277 1278 1279 1280
 **/
gint
gtk_range_get_min_slider_size (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

1281
  return range->priv->min_slider_size;
1282 1283
}

1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
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);
}

1299 1300 1301
/**
 * gtk_range_get_range_rect:
 * @range: a #GtkRange
1302
 * @range_rect: (out): return location for the range rectangle
1303
 *
1304
 * This function returns the area that contains the range’s trough
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314
 * 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)
{
1315
  GtkRangePrivate *priv;
1316

1317 1318 1319
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (range_rect != NULL);

1320 1321
  priv = range->priv;

1322
  gtk_css_gadget_get_margin_box (priv->contents_gadget, range_rect);
1323 1324 1325 1326 1327
}

/**
 * gtk_range_get_slider_range:
 * @range: a #GtkRange
1328 1329 1330 1331
 * @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
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344
 *
 * 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)
{
1345
  GtkRangePrivate *priv;
1346
  GtkAllocation slider_alloc;
1347

1348 1349
  g_return_if_fail (GTK_IS_RANGE (range));

1350 1351
  priv = range->priv;

1352
  gtk_css_gadget_get_margin_box (priv->slider_gadget, &slider_alloc);
1353

1354 1355 1356
  if (priv->orientation == GTK_ORIENTATION_VERTICAL)
    {
      if (slider_start)
1357
        *slider_start = slider_alloc.y;
1358
      if (slider_end)
1359
        *slider_end = slider_alloc.y + slider_alloc.height;
1360 1361 1362 1363
    }
  else
    {
      if (slider_start)
1364
        *slider_start = slider_alloc.x;
1365
      if (slider_end)
1366
        *slider_end = slider_alloc.x + slider_alloc.width;
1367
    }
Michael Natterer's avatar