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

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

26
#include "config.h"
27

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

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

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

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


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

73
typedef struct _GtkRangeStepTimer GtkRangeStepTimer;
74

75 76 77 78 79 80 81 82 83 84 85
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;

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

94
  GtkRangeStepTimer *timer;
95

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

  GdkWindow         *event_window;
101

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

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

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

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

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

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

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

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

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

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

  GtkScrollType autoscroll_mode;
  guint autoscroll_id;
157
};
158

159

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

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

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

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

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


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

/* Range methods */
251

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

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

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

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

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

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

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

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

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

Matthias Clasen's avatar
Matthias Clasen committed
362 363
  /**
   * GtkRange::value-changed:
364
   * @range: the #GtkRange that received the signal
Matthias Clasen's avatar
Matthias Clasen committed
365 366 367
   *
   * Emitted when the range value changes.
   */
368
  signals[VALUE_CHANGED] =
369
    g_signal_new (I_("value-changed"),
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, value_changed),
                  NULL, NULL,
374
                  _gtk_marshal_VOID__VOID,
375
                  G_TYPE_NONE, 0);
376 377 378 379 380

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

807 808 809 810 811 812 813
  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;
814 815
  priv->mouse_x = G_MININT;
  priv->mouse_y = G_MININT;
816 817 818 819 820
  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;
821
  priv->has_origin = FALSE;
822 823 824 825
  priv->show_fill_level = FALSE;
  priv->restrict_to_fill_level = TRUE;
  priv->fill_level = G_MAXDOUBLE;
  priv->timer = NULL;
826

827 828
  _gtk_orientable_set_style_classes (GTK_ORIENTABLE (range));

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

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

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

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

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

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

903 904 905
  priv = range->priv;

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

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

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

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

  priv = range->priv;

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

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

952
      priv->adjustment = adjustment;
953
      g_object_ref_sink (adjustment);
954
      
Manish Singh's avatar
Manish Singh committed
955 956 957
      g_signal_connect (adjustment, "changed",
			G_CALLBACK (gtk_range_adjustment_changed),
			range);
958
      g_signal_connect (adjustment, "value-changed",
Manish Singh's avatar
Manish Singh committed
959 960
			G_CALLBACK (gtk_range_adjustment_value_changed),
			range);
961
      
962
      gtk_range_adjustment_changed (adjustment, range);
963
      g_object_notify_by_pspec (G_OBJECT (range), properties[PROP_ADJUSTMENT]);
Elliot Lee's avatar
Elliot Lee committed
964 965 966
    }
}

967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 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
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);
    }
}

1021
static void
1022 1023 1024
update_stepper_state (GtkRange     *range,
                      Stepper       stepper,
                      GtkCssGadget *gadget)
1025 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
{
  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;
    }

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

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

1062 1063 1064 1065 1066 1067 1068 1069
  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);
1070 1071
}

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

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

  priv = range->priv;

1092 1093
  setting = setting != FALSE;

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

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

1102
      gtk_widget_queue_resize (GTK_WIDGET (range));
1103 1104

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

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

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

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

1142 1143
  g_return_if_fail (GTK_IS_RANGE (range));

1144 1145
  priv = range->priv;

1146 1147
  flippable = flippable ? TRUE : FALSE;

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

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

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

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

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

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

1207 1208
  g_return_if_fail (GTK_IS_RANGE (range));

1209 1210 1211
  priv = range->priv;

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

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

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

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

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

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

1263 1264 1265
  priv = range->priv;

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

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

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

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

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

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

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

1332 1333
  priv = range->priv;

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

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

1360 1361
  g_return_if_fail (GTK_IS_RANGE (range));

1362 1363
  priv = range->priv;

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

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

1382 1383 1384
/**
 * gtk_range_set_lower_stepper_sensitivity:
 * @range:       a #GtkRange
1385
 * @sensitivity: the lower stepper’s sensitivity policy.
1386 1387
 *
 * Sets the sensitivity policy for the stepper that points to the
1388
 * 'lower' end of the GtkRange’s adjustment.
1389
 *
Matthias Clasen's avatar
Matthias Clasen committed
1390
 * Since: 2.10