gtkrange.c 105 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2
/* GTK - The GIMP Toolkit
 * 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
16 17 18
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
19
 */
20 21

/*
22
 * Modified by the GTK+ Team and others 1997-2004.  See the AUTHORS
23 24 25 26 27
 * 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/. 
 */

28
#include <config.h>
Elliot Lee's avatar
Elliot Lee committed
29
#include <stdio.h>
30
#include <math.h>
31
#include <gdk/gdkkeysyms.h>
32
#include "gtkintl.h"
Elliot Lee's avatar
Elliot Lee committed
33
#include "gtkmain.h"
34
#include "gtkmarshalers.h"
Elliot Lee's avatar
Elliot Lee committed
35
#include "gtkrange.h"
36
#include "gtkscrollbar.h"
37
#include "gtkprivate.h"
38
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
39

40 41
#define SCROLL_DELAY_FACTOR 5    /* Scroll repeat multiplier */
#define UPDATE_DELAY        300  /* Delay for queued update */
Elliot Lee's avatar
Elliot Lee committed
42

43
enum {
Alexander Larsson's avatar
Alexander Larsson committed
44
  PROP_0,
45
  PROP_UPDATE_POLICY,
Havoc Pennington's avatar
Havoc Pennington committed
46
  PROP_ADJUSTMENT,
47 48
  PROP_INVERTED,
  PROP_LOWER_STEPPER_SENSITIVITY,
49 50 51 52
  PROP_UPPER_STEPPER_SENSITIVITY,
  PROP_SHOW_FILL_LEVEL,
  PROP_RESTRICT_TO_FILL_LEVEL,
  PROP_FILL_LEVEL
53
};
Elliot Lee's avatar
Elliot Lee committed
54

55
enum {
56
  VALUE_CHANGED,
57
  ADJUST_BOUNDS,
58
  MOVE_SLIDER,
59
  CHANGE_VALUE,
60 61 62
  LAST_SIGNAL
};

63 64 65 66 67 68 69 70 71 72 73
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;

74 75
#define GTK_RANGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RANGE, GtkRangeLayout))

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
struct _GtkRangeLayout
{
  /* These are in widget->window coordinates */
  GdkRectangle stepper_a;
  GdkRectangle stepper_b;
  GdkRectangle stepper_c;
  GdkRectangle stepper_d;
  /* The trough rectangle is the area the thumb can slide in, not the
   * entire range_rect
   */
  GdkRectangle trough;
  GdkRectangle slider;

  /* Layout-related state */
  
  MouseLocation mouse_location;
  /* last mouse coords we got, or -1 if mouse is outside the range */
  gint mouse_x;
  gint mouse_y;
95

96 97
  /* "grabbed" mouse location, OUTSIDE for no grab */
  MouseLocation grab_location;
98
  guint grab_button : 8; /* 0 if none */
99 100

  /* Stepper sensitivity */
101 102 103
  guint lower_sensitive : 1;
  guint upper_sensitive : 1;

104 105 106 107
  /* Fill level */
  guint show_fill_level : 1;
  guint restrict_to_fill_level : 1;

108 109
  GtkSensitivityType lower_sensitivity;
  GtkSensitivityType upper_sensitivity;
110
  guint repaint_id;
111 112

  gdouble fill_level;
113 114
};

115

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
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);
static void gtk_range_destroy        (GtkObject        *object);
static void gtk_range_size_request   (GtkWidget        *widget,
                                      GtkRequisition   *requisition);
static void gtk_range_size_allocate  (GtkWidget        *widget,
                                      GtkAllocation    *allocation);
static void gtk_range_realize        (GtkWidget        *widget);
static void gtk_range_unrealize      (GtkWidget        *widget);
131 132
static void gtk_range_map            (GtkWidget        *widget);
static void gtk_range_unmap          (GtkWidget        *widget);
Matthias Clasen's avatar
Matthias Clasen committed
133
static gboolean gtk_range_expose         (GtkWidget        *widget,
134
                                      GdkEventExpose   *event);
Matthias Clasen's avatar
Matthias Clasen committed
135
static gboolean gtk_range_button_press   (GtkWidget        *widget,
136
                                      GdkEventButton   *event);
Matthias Clasen's avatar
Matthias Clasen committed
137
static gboolean gtk_range_button_release (GtkWidget        *widget,
138
                                      GdkEventButton   *event);
Matthias Clasen's avatar
Matthias Clasen committed
139
static gboolean gtk_range_motion_notify  (GtkWidget        *widget,
140
                                      GdkEventMotion   *event);
Matthias Clasen's avatar
Matthias Clasen committed
141
static gboolean gtk_range_enter_notify   (GtkWidget        *widget,
142
                                      GdkEventCrossing *event);
Matthias Clasen's avatar
Matthias Clasen committed
143
static gboolean gtk_range_leave_notify   (GtkWidget        *widget,
144
                                      GdkEventCrossing *event);
Matthias Clasen's avatar
Matthias Clasen committed
145 146
static gboolean gtk_range_grab_broken (GtkWidget          *widget,
				       GdkEventGrabBroken *event);
Matthias Clasen's avatar
Matthias Clasen committed
147 148 149 150
static void gtk_range_grab_notify    (GtkWidget          *widget,
				      gboolean            was_grabbed);
static void gtk_range_state_changed  (GtkWidget          *widget,
				      GtkStateType        previous_state);
Matthias Clasen's avatar
Matthias Clasen committed
151
static gboolean gtk_range_scroll_event   (GtkWidget        *widget,
152 153 154
                                      GdkEventScroll   *event);
static void gtk_range_style_set      (GtkWidget        *widget,
                                      GtkStyle         *previous_style);
155 156 157
static void update_slider_position   (GtkRange	       *range,
				      gint              mouse_x,
				      gint              mouse_y);
158
static void stop_scrolling           (GtkRange         *range);
159 160

/* Range methods */
161

162
static void gtk_range_move_slider              (GtkRange         *range,
163 164 165
                                                GtkScrollType     scroll);

/* Internals */
166
static gboolean      gtk_range_scroll                   (GtkRange      *range,
167 168
                                                         GtkScrollType  scroll);
static gboolean      gtk_range_update_mouse_location    (GtkRange      *range);
169 170
static void          gtk_range_calc_layout              (GtkRange      *range,
							 gdouble	adjustment_value);
171 172 173
static void          gtk_range_get_props                (GtkRange      *range,
                                                         gint          *slider_width,
                                                         gint          *stepper_size,
174
                                                         gint          *focus_width,
175
                                                         gint          *trough_border,
176
                                                         gint          *stepper_spacing,
177
                                                         gboolean      *trough_under_steppers,
178 179
							 gint          *arrow_displacement_x,
							 gint	       *arrow_displacement_y);
180 181 182
static void          gtk_range_calc_request             (GtkRange      *range,
                                                         gint           slider_width,
                                                         gint           stepper_size,
183
                                                         gint           focus_width,
184 185 186 187 188
                                                         gint           trough_border,
                                                         gint           stepper_spacing,
                                                         GdkRectangle  *range_rect,
                                                         GtkBorder     *border,
                                                         gint          *n_steppers_p,
189 190
                                                         gboolean      *has_steppers_ab,
                                                         gboolean      *has_steppers_cd,
191 192 193 194 195 196 197 198 199 200 201 202
                                                         gint          *slider_length_p);
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);
static void          gtk_range_reset_update_timer       (GtkRange      *range);
static void          gtk_range_remove_update_timer      (GtkRange      *range);
static GdkRectangle* get_area                           (GtkRange      *range,
                                                         MouseLocation  location);
203 204 205
static gboolean      gtk_range_real_change_value        (GtkRange      *range,
                                                         GtkScrollType  scroll,
                                                         gdouble        value);
206
static void          gtk_range_update_value             (GtkRange      *range);
207 208
static gboolean      gtk_range_key_press                (GtkWidget     *range,
							 GdkEventKey   *event);
209

Elliot Lee's avatar
Elliot Lee committed
210

211
static guint signals[LAST_SIGNAL];
Elliot Lee's avatar
Elliot Lee committed
212

Matthias Clasen's avatar
Matthias Clasen committed
213
G_DEFINE_ABSTRACT_TYPE (GtkRange, gtk_range, GTK_TYPE_WIDGET)
Elliot Lee's avatar
Elliot Lee committed
214 215 216 217

static void
gtk_range_class_init (GtkRangeClass *class)
{
Alexander Larsson's avatar
Alexander Larsson committed
218
  GObjectClass   *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
219 220 221
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;

Alexander Larsson's avatar
Alexander Larsson committed
222
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
223 224 225
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;

Alexander Larsson's avatar
Alexander Larsson committed
226 227
  gobject_class->set_property = gtk_range_set_property;
  gobject_class->get_property = gtk_range_get_property;
228
  object_class->destroy = gtk_range_destroy;
Elliot Lee's avatar
Elliot Lee committed
229

230 231 232 233
  widget_class->size_request = gtk_range_size_request;
  widget_class->size_allocate = gtk_range_size_allocate;
  widget_class->realize = gtk_range_realize;
  widget_class->unrealize = gtk_range_unrealize;  
234 235
  widget_class->map = gtk_range_map;
  widget_class->unmap = gtk_range_unmap;
Elliot Lee's avatar
Elliot Lee committed
236 237 238 239
  widget_class->expose_event = gtk_range_expose;
  widget_class->button_press_event = gtk_range_button_press;
  widget_class->button_release_event = gtk_range_button_release;
  widget_class->motion_notify_event = gtk_range_motion_notify;
240
  widget_class->scroll_event = gtk_range_scroll_event;
Elliot Lee's avatar
Elliot Lee committed
241 242
  widget_class->enter_notify_event = gtk_range_enter_notify;
  widget_class->leave_notify_event = gtk_range_leave_notify;
Matthias Clasen's avatar
Matthias Clasen committed
243
  widget_class->grab_broken_event = gtk_range_grab_broken;
Matthias Clasen's avatar
Matthias Clasen committed
244 245
  widget_class->grab_notify = gtk_range_grab_notify;
  widget_class->state_changed = gtk_range_state_changed;
246
  widget_class->style_set = gtk_range_style_set;
247
  widget_class->key_press_event = gtk_range_key_press;
248 249

  class->move_slider = gtk_range_move_slider;
250
  class->change_value = gtk_range_real_change_value;
251

252 253
  class->slider_detail = "slider";
  class->stepper_detail = "stepper";
254

Matthias Clasen's avatar
Matthias Clasen committed
255 256 257 258 259 260
  /**
   * GtkRange::value-changed:
   * @range: the #GtkRange
   *
   * Emitted when the range value changes.
   */
261
  signals[VALUE_CHANGED] =
Matthias Clasen's avatar
Matthias Clasen committed
262
    g_signal_new (I_("value_changed"),
Manish Singh's avatar
Manish Singh committed
263
                  G_TYPE_FROM_CLASS (gobject_class),
264 265 266
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, value_changed),
                  NULL, NULL,
267
                  _gtk_marshal_NONE__NONE,
268
                  G_TYPE_NONE, 0);
269
  
270
  signals[ADJUST_BOUNDS] =
Matthias Clasen's avatar
Matthias Clasen committed
271
    g_signal_new (I_("adjust_bounds"),
Manish Singh's avatar
Manish Singh committed
272
                  G_TYPE_FROM_CLASS (gobject_class),
273 274 275 276 277 278 279
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
                  NULL, NULL,
                  _gtk_marshal_VOID__DOUBLE,
                  G_TYPE_NONE, 1,
                  G_TYPE_DOUBLE);
  
Matthias Clasen's avatar
Matthias Clasen committed
280 281 282 283 284 285 286
  /**
   * GtkRange::move-slider:
   * @range: the #GtkRange
   * @step: how to move the slider
   *
   * Virtual function that moves the slider. Used for keybindings.
   */
287
  signals[MOVE_SLIDER] =
Matthias Clasen's avatar
Matthias Clasen committed
288
    g_signal_new (I_("move_slider"),
Manish Singh's avatar
Manish Singh committed
289
                  G_TYPE_FROM_CLASS (gobject_class),
290 291 292
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkRangeClass, move_slider),
                  NULL, NULL,
293
                  _gtk_marshal_VOID__ENUM,
294 295
                  G_TYPE_NONE, 1,
                  GTK_TYPE_SCROLL_TYPE);
296 297 298

  /**
   * GtkRange::change-value:
Matthias Clasen's avatar
Matthias Clasen committed
299 300 301
   * @range: the range that received the signal
   * @scroll: the type of scroll action that was performed
   * @value: the new value resulting from the scroll action
302
   * @returns: %TRUE to prevent other handlers from being invoked for the
Matthias Clasen's avatar
Matthias Clasen committed
303
   * signal, %FALSE to propagate the signal further
304 305 306 307 308 309 310 311 312 313 314
   *
   * The ::change-value signal is emitted when a scroll action is
   * 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
   * the ::change-value signal is responsible for clamping the value to
315 316
   * the desired number of decimal digits; the default GTK+ handler 
   * clamps the value based on @range->round_digits.
317 318 319 320 321 322 323
   *
   * It is not possible to use delayed update policies in an overridden
   * ::change-value handler.
   *
   * Since: 2.6
   */
  signals[CHANGE_VALUE] =
Matthias Clasen's avatar
Matthias Clasen committed
324
    g_signal_new (I_("change_value"),
325 326 327 328 329 330 331 332
                  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);
333
  
Alexander Larsson's avatar
Alexander Larsson committed
334 335
  g_object_class_install_property (gobject_class,
                                   PROP_UPDATE_POLICY,
336
                                   g_param_spec_enum ("update-policy",
337 338
						      P_("Update policy"),
						      P_("How the range should be updated on the screen"),
Alexander Larsson's avatar
Alexander Larsson committed
339 340
						      GTK_TYPE_UPDATE_TYPE,
						      GTK_UPDATE_CONTINUOUS,
341
						      GTK_PARAM_READWRITE));
342
  
343 344 345
  g_object_class_install_property (gobject_class,
                                   PROP_ADJUSTMENT,
                                   g_param_spec_object ("adjustment",
346 347
							P_("Adjustment"),
							P_("The GtkAdjustment that contains the current value of this range object"),
348
                                                        GTK_TYPE_ADJUSTMENT,
349
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
350

Havoc Pennington's avatar
Havoc Pennington committed
351 352 353
  g_object_class_install_property (gobject_class,
                                   PROP_INVERTED,
                                   g_param_spec_boolean ("inverted",
354 355
							P_("Inverted"),
							P_("Invert direction slider moves to increase range value"),
Havoc Pennington's avatar
Havoc Pennington committed
356
                                                         FALSE,
357
                                                         GTK_PARAM_READWRITE));
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376

  g_object_class_install_property (gobject_class,
                                   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_object_class_install_property (gobject_class,
                                   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));

377 378 379
  /**
   * GtkRange:show-fill-level:
   *
Matthias Clasen's avatar
Matthias Clasen committed
380
   * The show-fill-level property controls whether fill level indicator
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
   * graphics are displayed on the trough. See
   * gtk_range_set_show_fill_level().
   *
   * Since: 2.12
   **/
  g_object_class_install_property (gobject_class,
                                   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));

  /**
   * GtkRange:restrict-to-fill-level:
   *
Matthias Clasen's avatar
Matthias Clasen committed
397
   * The restrict-to-fill-level property controls whether slider
398
   * movement is restricted to an upper boundary set by the
Matthias Clasen's avatar
Matthias Clasen committed
399
   * fill level. See gtk_range_set_restrict_to_fill_level().
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
   *
   * Since: 2.12
   **/
  g_object_class_install_property (gobject_class,
                                   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));

  /**
   * GtkRange:fill-level:
   *
   * The fill level (e.g. prebuffering of a network stream).
   * See gtk_range_set_fill_level().
   *
   * Since: 2.12
   **/
  g_object_class_install_property (gobject_class,
                                   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));

429
  gtk_widget_class_install_style_property (widget_class,
430
					   g_param_spec_int ("slider-width",
431 432
							     P_("Slider Width"),
							     P_("Width of scrollbar or scale thumb"),
433 434
							     0,
							     G_MAXINT,
435
							     14,
436
							     GTK_PARAM_READABLE));
437
  gtk_widget_class_install_style_property (widget_class,
438
					   g_param_spec_int ("trough-border",
439 440
                                                             P_("Trough Border"),
                                                             P_("Spacing between thumb/steppers and outer trough bevel"),
441 442 443
                                                             0,
                                                             G_MAXINT,
                                                             1,
444
                                                             GTK_PARAM_READABLE));
445
  gtk_widget_class_install_style_property (widget_class,
446
					   g_param_spec_int ("stepper-size",
447 448
							     P_("Stepper Size"),
							     P_("Length of step buttons at ends"),
449 450
							     0,
							     G_MAXINT,
451
							     14,
452
							     GTK_PARAM_READABLE));
453 454 455 456 457
  /**
   * GtkRange:stepper-spacing:
   *
   * The spacing between the stepper buttons and thumb. Note that
   * setting this value to anything > 0 will automatically set the
Matthias Clasen's avatar
Matthias Clasen committed
458
   * trough-under-steppers style property to %TRUE as well. Also,
459 460
   * stepper-spacing won't have any effect if there are no steppers.
   */
461
  gtk_widget_class_install_style_property (widget_class,
462
					   g_param_spec_int ("stepper-spacing",
463 464
							     P_("Stepper Spacing"),
							     P_("Spacing between step buttons and thumb"),
465
                                                             0,
466
							     G_MAXINT,
467
							     0,
468
							     GTK_PARAM_READABLE));
469
  gtk_widget_class_install_style_property (widget_class,
470
					   g_param_spec_int ("arrow-displacement-x",
471 472
							     P_("Arrow X Displacement"),
							     P_("How far in the x direction to move the arrow when the button is depressed"),
473 474 475
							     G_MININT,
							     G_MAXINT,
							     0,
476
							     GTK_PARAM_READABLE));
477
  gtk_widget_class_install_style_property (widget_class,
478
					   g_param_spec_int ("arrow-displacement-y",
479 480
							     P_("Arrow Y Displacement"),
							     P_("How far in the y direction to move the arrow when the button is depressed"),
481 482 483
							     G_MININT,
							     G_MAXINT,
							     0,
484
							     GTK_PARAM_READABLE));
485 486

  gtk_widget_class_install_style_property (widget_class,
487
					   g_param_spec_boolean ("activate-slider",
488 489 490
                                                                 P_("Draw slider ACTIVE during drag"),
							         P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
							         FALSE,
491
							         GTK_PARAM_READABLE));
492 493 494 495

  /**
   * GtkRange:trough-side-details:
   *
Matthias Clasen's avatar
Matthias Clasen committed
496 497 498
   * When %TRUE, the parts of the trough on the two sides of the 
   * slider are drawn with different details.
   *
499 500 501 502 503 504 505 506 507 508 509 510 511 512
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boolean ("trough-side-details",
                                                                 P_("Trough Side Details"),
                                                                 P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"),
                                                                 FALSE,
                                                                 GTK_PARAM_READABLE));

  /**
   * GtkRange:trough-under-steppers:
   *
   * Whether to draw the trough across the full length of the range or
   * to exclude the steppers and their spacing. Note that setting the
Matthias Clasen's avatar
Matthias Clasen committed
513
   * #GtkRange:stepper-spacing style property to any value > 0 will
514 515 516 517 518 519 520
   * automatically enable trough-under-steppers too.
   *
   * Since: 2.10
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boolean ("trough-under-steppers",
                                                                 P_("Trough Under Steppers"),
521
                                                                 P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
522 523
                                                                 TRUE,
                                                                 GTK_PARAM_READABLE));
524 525

  g_type_class_add_private (class, sizeof (GtkRangeLayout));
Elliot Lee's avatar
Elliot Lee committed
526 527
}

528
static void
Alexander Larsson's avatar
Alexander Larsson committed
529 530 531 532
gtk_range_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
533 534 535 536 537
{
  GtkRange *range;

  range = GTK_RANGE (object);

Alexander Larsson's avatar
Alexander Larsson committed
538
  switch (prop_id)
539
    {
Alexander Larsson's avatar
Alexander Larsson committed
540 541
    case PROP_UPDATE_POLICY:
      gtk_range_set_update_policy (range, g_value_get_enum (value));
542
      break;
543 544 545
    case PROP_ADJUSTMENT:
      gtk_range_set_adjustment (range, g_value_get_object (value));
      break;
Havoc Pennington's avatar
Havoc Pennington committed
546 547 548
    case PROP_INVERTED:
      gtk_range_set_inverted (range, g_value_get_boolean (value));
      break;
549 550 551 552 553 554
    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;
555 556 557 558 559 560 561 562 563
    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;
564
    default:
Alexander Larsson's avatar
Alexander Larsson committed
565
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
566 567 568 569 570
      break;
    }
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
571 572 573 574
gtk_range_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
575 576 577 578 579
{
  GtkRange *range;

  range = GTK_RANGE (object);

Alexander Larsson's avatar
Alexander Larsson committed
580
  switch (prop_id)
581
    {
Alexander Larsson's avatar
Alexander Larsson committed
582
    case PROP_UPDATE_POLICY:
583
      g_value_set_enum (value, range->update_policy);
584
      break;
585
    case PROP_ADJUSTMENT:
586
      g_value_set_object (value, range->adjustment);
587
      break;
Havoc Pennington's avatar
Havoc Pennington committed
588 589 590
    case PROP_INVERTED:
      g_value_set_boolean (value, range->inverted);
      break;
591 592 593 594 595 596
    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;
597 598 599 600 601 602 603 604 605
    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;
606
    default:
Alexander Larsson's avatar
Alexander Larsson committed
607
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
608 609 610 611
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
612 613 614
static void
gtk_range_init (GtkRange *range)
{
615 616
  GTK_WIDGET_SET_FLAGS (range, GTK_NO_WINDOW);

Elliot Lee's avatar
Elliot Lee committed
617
  range->adjustment = NULL;
618 619 620 621 622 623 624 625 626 627
  range->update_policy = GTK_UPDATE_CONTINUOUS;
  range->inverted = FALSE;
  range->flippable = FALSE;
  range->min_slider_size = 1;
  range->has_stepper_a = FALSE;
  range->has_stepper_b = FALSE;
  range->has_stepper_c = FALSE;
  range->has_stepper_d = FALSE;
  range->need_recalc = TRUE;
  range->round_digits = -1;
628
  range->layout = GTK_RANGE_GET_PRIVATE (range);
629 630 631
  range->layout->mouse_location = MOUSE_OUTSIDE;
  range->layout->mouse_x = -1;
  range->layout->mouse_y = -1;
632 633
  range->layout->grab_location = MOUSE_OUTSIDE;
  range->layout->grab_button = 0;
634 635
  range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
  range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
636 637
  range->layout->lower_sensitive = TRUE;
  range->layout->upper_sensitive = TRUE;
638 639 640
  range->layout->show_fill_level = FALSE;
  range->layout->restrict_to_fill_level = TRUE;
  range->layout->fill_level = G_MAXDOUBLE;
641
  range->timer = NULL;  
Elliot Lee's avatar
Elliot Lee committed
642 643
}

644 645 646 647 648 649 650 651 652 653 654
/**
 * gtk_range_get_adjustment:
 * @range: a #GtkRange
 * 
 * Get the #GtkAdjustment which is the "model" object for #GtkRange.
 * See gtk_range_set_adjustment() for details.
 * The return value does not have a reference added, so should not
 * be unreferenced.
 * 
 * Return value: a #GtkAdjustment
 **/
Elliot Lee's avatar
Elliot Lee committed
655 656 657 658 659
GtkAdjustment*
gtk_range_get_adjustment (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), NULL);

660 661 662
  if (!range->adjustment)
    gtk_range_set_adjustment (range, NULL);

Elliot Lee's avatar
Elliot Lee committed
663 664 665
  return range->adjustment;
}

666 667 668 669 670 671 672 673 674 675 676 677 678 679
/**
 * gtk_range_set_update_policy:
 * @range: a #GtkRange
 * @policy: update policy
 *
 * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
 * anytime the range slider is moved, the range value will change and the
 * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
 * the value will be updated after a brief timeout where no slider motion
 * occurs, so updates are spaced by a short time rather than
 * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
 * be updated when the user releases the button and ends the slider
 * drag operation.
 **/
Elliot Lee's avatar
Elliot Lee committed
680 681 682 683 684 685
void
gtk_range_set_update_policy (GtkRange      *range,
			     GtkUpdateType  policy)
{
  g_return_if_fail (GTK_IS_RANGE (range));

686
  if (range->update_policy != policy)
Alexander Larsson's avatar
Alexander Larsson committed
687
    {
688
      range->update_policy = policy;
689
      g_object_notify (G_OBJECT (range), "update-policy");
Alexander Larsson's avatar
Alexander Larsson committed
690
    }
Elliot Lee's avatar
Elliot Lee committed
691 692
}

693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
/**
 * gtk_range_get_update_policy:
 * @range: a #GtkRange
 *
 * Gets the update policy of @range. See gtk_range_set_update_policy().
 *
 * Return value: the current update policy
 **/
GtkUpdateType
gtk_range_get_update_policy (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);

  return range->update_policy;
}

709 710 711 712 713 714 715 716 717 718 719 720 721
/**
 * gtk_range_set_adjustment:
 * @range: a #GtkRange
 * @adjustment: a #GtkAdjustment
 *
 * Sets the adjustment to be used as the "model" object for this range
 * 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
722 723 724 725 726
void
gtk_range_set_adjustment (GtkRange      *range,
			  GtkAdjustment *adjustment)
{
  g_return_if_fail (GTK_IS_RANGE (range));
727 728 729 730 731
  
  if (!adjustment)
    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
  else
    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
Elliot Lee's avatar
Elliot Lee committed
732

733
  if (range->adjustment != adjustment)
Elliot Lee's avatar
Elliot Lee committed
734
    {
735 736
      if (range->adjustment)
	{
Manish Singh's avatar
Manish Singh committed
737 738 739 740 741 742 743
	  g_signal_handlers_disconnect_by_func (range->adjustment,
						gtk_range_adjustment_changed,
						range);
	  g_signal_handlers_disconnect_by_func (range->adjustment,
						gtk_range_adjustment_value_changed,
						range);
	  g_object_unref (range->adjustment);
744
	}
745

746
      range->adjustment = adjustment;
747
      g_object_ref_sink (adjustment);
748
      
Manish Singh's avatar
Manish Singh committed
749 750 751 752 753 754
      g_signal_connect (adjustment, "changed",
			G_CALLBACK (gtk_range_adjustment_changed),
			range);
      g_signal_connect (adjustment, "value_changed",
			G_CALLBACK (gtk_range_adjustment_value_changed),
			range);
755
      
756
      gtk_range_adjustment_changed (adjustment, range);
757
      g_object_notify (G_OBJECT (range), "adjustment");
Elliot Lee's avatar
Elliot Lee committed
758 759 760
    }
}

761 762 763 764 765 766 767 768 769 770
/**
 * 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.
 **/
771 772 773 774 775 776 777 778 779 780 781
void
gtk_range_set_inverted (GtkRange *range,
                        gboolean  setting)
{
  g_return_if_fail (GTK_IS_RANGE (range));
  
  setting = setting != FALSE;

  if (setting != range->inverted)
    {
      range->inverted = setting;
Havoc Pennington's avatar
Havoc Pennington committed
782
      g_object_notify (G_OBJECT (range), "inverted");
783 784 785 786
      gtk_widget_queue_resize (GTK_WIDGET (range));
    }
}

787 788 789 790 791 792 793 794
/**
 * gtk_range_get_inverted:
 * @range: a #GtkRange
 * 
 * Gets the value set by gtk_range_set_inverted().
 * 
 * Return value: %TRUE if the range is inverted
 **/
795 796 797 798 799 800 801 802
gboolean
gtk_range_get_inverted (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

  return range->inverted;
}

803 804 805 806 807 808 809 810
/**
 * gtk_range_set_lower_stepper_sensitivity:
 * @range:       a #GtkRange
 * @sensitivity: the lower stepper's sensitivity policy.
 *
 * Sets the sensitivity policy for the stepper that points to the
 * 'lower' end of the GtkRange's adjustment.
 *
Matthias Clasen's avatar
Matthias Clasen committed
811
 * Since: 2.10
812 813 814 815 816 817 818 819 820 821
 **/
void
gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
                                         GtkSensitivityType  sensitivity)
{
  g_return_if_fail (GTK_IS_RANGE (range));

  if (range->layout->lower_sensitivity != sensitivity)
    {
      range->layout->lower_sensitivity = sensitivity;
822 823 824

      range->need_recalc = TRUE;
      gtk_range_calc_layout (range, range->adjustment->value);
825
      gtk_widget_queue_draw (GTK_WIDGET (range));
826

827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
      g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
    }
}

/**
 * gtk_range_get_lower_stepper_sensitivity:
 * @range: a #GtkRange
 *
 * Gets the sensitivity policy for the stepper that points to the
 * 'lower' end of the GtkRange's adjustment.
 *
 * Return value: The lower stepper's sensitivity policy.
 *
 * Since: 2.10
 **/
GtkSensitivityType
gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);

  return range->layout->lower_sensitivity;
}

/**
 * gtk_range_set_upper_stepper_sensitivity:
 * @range:       a #GtkRange
 * @sensitivity: the upper stepper's sensitivity policy.
 *
 * Sets the sensitivity policy for the stepper that points to the
 * 'upper' end of the GtkRange's adjustment.
 *
Matthias Clasen's avatar
Matthias Clasen committed
858
 * Since: 2.10
859 860 861 862 863 864 865 866 867 868
 **/
void
gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
                                         GtkSensitivityType  sensitivity)
{
  g_return_if_fail (GTK_IS_RANGE (range));

  if (range->layout->upper_sensitivity != sensitivity)
    {
      range->layout->upper_sensitivity = sensitivity;
869 870 871

      range->need_recalc = TRUE;
      gtk_range_calc_layout (range, range->adjustment->value);
872
      gtk_widget_queue_draw (GTK_WIDGET (range));
873

874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
      g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
    }
}

/**
 * gtk_range_get_upper_stepper_sensitivity:
 * @range: a #GtkRange
 *
 * Gets the sensitivity policy for the stepper that points to the
 * 'upper' end of the GtkRange's adjustment.
 *
 * Return value: The upper stepper's sensitivity policy.
 *
 * Since: 2.10
 **/
GtkSensitivityType
gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);

  return range->layout->upper_sensitivity;
}

897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
/**
 * gtk_range_set_increments:
 * @range: a #GtkRange
 * @step: step size
 * @page: page size
 *
 * Sets the step and page sizes for the range.
 * The step size is used when the user clicks the #GtkScrollbar
 * arrows or moves #GtkScale via arrow keys. The page size
 * is used for example when moving via Page Up or Page Down keys.
 **/
void
gtk_range_set_increments (GtkRange *range,
                          gdouble   step,
                          gdouble   page)
{
  g_return_if_fail (GTK_IS_RANGE (range));

  range->adjustment->step_increment = step;
  range->adjustment->page_increment = page;

  gtk_adjustment_changed (range->adjustment);
}

/**
 * gtk_range_set_range:
 * @range: a #GtkRange
 * @min: minimum range value
 * @max: maximum range value
 * 
 * Sets the allowable values in the #GtkRange, and clamps the range
928 929
 * value to be between @min and @max. (If the range has a non-zero
 * page size, it is clamped between @min and @max - page-size.)
930 931 932 933 934 935 936 937 938 939 940 941 942 943
 **/
void
gtk_range_set_range (GtkRange *range,
                     gdouble   min,
                     gdouble   max)
{
  gdouble value;
  
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (min < max);
  
  range->adjustment->lower = min;
  range->adjustment->upper = max;

944 945 946 947 948 949 950
  value = range->adjustment->value;

  if (range->layout->restrict_to_fill_level)
    value = MIN (value, MAX (range->adjustment->lower,
                             range->layout->fill_level));

  value = CLAMP (value, range->adjustment->lower,
951
                 (range->adjustment->upper - range->adjustment->page_size));
952 953 954

  gtk_adjustment_set_value (range->adjustment, value);
  gtk_adjustment_changed (range->adjustment);
955 956 957 958 959 960 961 962 963
}

/**
 * gtk_range_set_value:
 * @range: a #GtkRange
 * @value: new value of the range
 *
 * Sets the current value of the range; if the value is outside the
 * minimum or maximum range values, it will be clamped to fit inside
Matthias Clasen's avatar
Matthias Clasen committed
964 965
 * them. The range emits the #GtkRange::value-changed signal if the 
 * value changes.
966 967 968 969 970 971
 **/
void
gtk_range_set_value (GtkRange *range,
                     gdouble   value)
{
  g_return_if_fail (GTK_IS_RANGE (range));
972 973 974 975 976

  if (range->layout->restrict_to_fill_level)
    value = MIN (value, MAX (range->adjustment->lower,
                             range->layout->fill_level));

977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998
  value = CLAMP (value, range->adjustment->lower,
                 (range->adjustment->upper - range->adjustment->page_size));

  gtk_adjustment_set_value (range->adjustment, value);
}

/**
 * gtk_range_get_value:
 * @range: a #GtkRange
 * 
 * Gets the current value of the range.
 * 
 * Return value: current value of the range.
 **/
gdouble
gtk_range_get_value (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);

  return range->adjustment->value;
}

999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
/**
 * gtk_range_set_show_fill_level:
 * @range:           A #GtkRange
 * @show_fill_level: Whether a fill level indicator graphics is shown.
 *
 * Sets whether a graphical fill level is show on the trough. See
 * gtk_range_set_fill_level() for a general description of the fill
 * level concept.
 *
 * Since: 2.12
 **/
void
gtk_range_set_show_fill_level (GtkRange *range,
                               gboolean  show_fill_level)
{
  g_return_if_fail (GTK_IS_RANGE (range));

  show_fill_level = show_fill_level ? TRUE : FALSE;

  if (show_fill_level != range->layout->show_fill_level)
    {
      range->layout->show_fill_level = show_fill_level;
      g_object_notify (G_OBJECT (range), "show-fill-level");
      gtk_widget_queue_draw (GTK_WIDGET (range));
    }
}

/**
 * gtk_range_get_show_fill_level:
 * @range: A #GtkRange
 *
Matthias Clasen's avatar
Matthias Clasen committed
1030 1031 1032
 * Gets whether the range displays the fill level graphically.
 *
 * Return value: %TRUE if @range shows the fill level.
1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
 *
 * Since: 2.12
 **/
gboolean
gtk_range_get_show_fill_level (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

  return range->layout->show_fill_level;
}

/**
 * gtk_range_set_restrict_to_fill_level:
 * @range:                  A #GtkRange
 * @restrict_to_fill_level: Whether the fill level restricts slider movement.
 *
 * Sets whether the slider is restricted to the fill level. See
 * gtk_range_set_fill_level() for a general description of the fill
 * level concept.
 *
 * Since: 2.12
 **/
void
gtk_range_set_restrict_to_fill_level (GtkRange *range,
                                      gboolean  restrict_to_fill_level)
{
  g_return_if_fail (GTK_IS_RANGE (range));

  restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;

  if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
    {
      range->layout->restrict_to_fill_level = restrict_to_fill_level;
      g_object_notify (G_OBJECT (range), "restrict-to-fill-level");

      gtk_range_set_value (range, gtk_range_get_value (range));
    }
}

/**
 * gtk_range_get_restrict_to_fill_level:
 * @range: A #GtkRange
 *
Matthias Clasen's avatar
Matthias Clasen committed
1076 1077 1078
 * Gets whether the range is restricted to the fill level.
 *
 * Return value: %TRUE if @range is restricted to the fill level.
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
 *
 * Since: 2.12
 **/
gboolean
gtk_range_get_restrict_to_fill_level (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

  return range->layout->restrict_to_fill_level;
}

/**
 * gtk_range_set_fill_level:
Matthias Clasen's avatar
Matthias Clasen committed
1092 1093
 * @range: a #GtkRange
 * @fill_level: the new position of the fill level indicator
1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
 *
 * Set the new position of the fill level indicator.
 *
 * The "fill level" is probably best described by its most prominent
 * use case, which is an indicator for the amount of pre-buffering in
 * a streaming media player. In that use case, the value of the range
 * would indicate the current play position, and the fill level would
 * be the position up to which the file/stream has been downloaded.
 *
 * This amount of prebuffering can be displayed on the range's trough
 * and is themeable separately from the trough. To enable fill level
 * display, use gtk_range_set_show_fill_level(). The range defaults
 * to not showing the fill level.
 *
 * Additionally, it's possible to restrict the range's slider position
 * to values which are smaller than the fill level. This is controller
 * by gtk_range_set_restrict_to_fill_level() and is by default
 * enabled.
 *
 * Since: 2.12
 **/
void
gtk_range_set_fill_level (GtkRange *range,
                          gdouble   fill_level)
{
  g_return_if_fail (GTK_IS_RANGE (range));

  if (fill_level != range->layout->fill_level)
    {
      range->layout->fill_level = fill_level;
      g_object_notify (G_OBJECT (range), "fill-level");

      if (range->layout->show_fill_level)
        gtk_widget_queue_draw (GTK_WIDGET (range));

      if (range->layout->restrict_to_fill_level)
        gtk_range_set_value (range, gtk_range_get_value (range));
    }
}

/**
 * gtk_range_get_fill_level:
 * @range : A #GtkRange
 *
Matthias Clasen's avatar
Matthias Clasen committed
1138 1139 1140
 * Gets the current position of the fill level indicator.
 *
 * Return value: The current fill level
1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151
 *
 * Since: 2.12
 **/
gdouble
gtk_range_get_fill_level (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);

  return range->layout->fill_level;
}

1152 1153 1154 1155 1156 1157 1158 1159 1160 1161
static gboolean
should_invert (GtkRange *range)
{  
  if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
    return
      (range->inverted && !range->flippable) ||
      (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
      (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
  else
    return range->inverted;
Elliot Lee's avatar
Elliot Lee committed
1162 1163
}

1164 1165
static void
gtk_range_destroy (GtkObject *object)
Elliot Lee's avatar
Elliot Lee committed
1166
{
1167
  GtkRange *range = GTK_RANGE (object);
1168 1169 1170

  gtk_range_remove_step_timer (range);
  gtk_range_remove_update_timer (range);
1171 1172 1173 1174 1175

  if (range->layout->repaint_id)
    g_source_remove (range->layout->repaint_id);
  range->layout->repaint_id = 0;

1176 1177
  if (range->adjustment)
    {
Manish Singh's avatar
Manish Singh committed
1178 1179 1180 1181 1182 1183 1184
      g_signal_handlers_disconnect_by_func (range->adjustment,
					    gtk_range_adjustment_changed,
					    range);
      g_signal_handlers_disconnect_by_func (range->adjustment,
					    gtk_range_adjustment_value_changed,
					    range);
      g_object_unref (range->adjustment);
1185 1186
      range->adjustment = NULL;
    }
Elliot Lee's avatar
Elliot Lee committed
1187

Matthias Clasen's avatar
Matthias Clasen committed
1188
  (* GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy) (object);
Elliot Lee's avatar
Elliot Lee committed
1189 1190
}

1191 1192 1193
static void
gtk_range_size_request (GtkWidget      *widget,
                        GtkRequisition *requisition)
Elliot Lee's avatar
Elliot Lee committed
1194
{
1195
  GtkRange *range;
1196
  gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
1197 1198 1199 1200 1201 1202
  GdkRectangle range_rect;
  GtkBorder border;
  
  range = GTK_RANGE (widget);
  
  gtk_range_get_props (range,
1203 1204 1205 1206
                       &slider_width, &stepper_size,
                       &focus_width, &trough_border,
                       &stepper_spacing, NULL,
                       NULL, NULL);
1207 1208

  gtk_range_calc_request (range, 
1209 1210 1211
                          slider_width, stepper_size,
                          focus_width, trough_border, stepper_spacing,
                          &range_rect, &border, NULL, NULL, NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
1212

1213 1214
  requisition->width = range_rect.width + border.left + border.right;
  requisition->height = range_rect.height + border.top + border.bottom;
Elliot Lee's avatar
Elliot Lee committed
1215 1216
}

1217 1218 1219
static void
gtk_range_size_allocate (GtkWidget     *widget,
                         GtkAllocation *allocation)
Elliot Lee's avatar
Elliot Lee committed
1220
{
1221 1222 1223 1224
  GtkRange *range;

  range = GTK_RANGE (widget);

1225 1226
  widget->allocation = *allocation;
  
1227
  range->need_recalc = TRUE;
1228
  gtk_range_calc_layout (range, range->adjustment->value);
Elliot Lee's avatar
Elliot Lee committed
1229

1230 1231 1232 1233 1234 1235
  if (GTK_WIDGET_REALIZED (range))
    gdk_window_move_resize (range->event_window,
			    widget->allocation.x,
			    widget->allocation.y,
			    widget->allocation.width,
			    widget->allocation.height);
Elliot Lee's avatar
Elliot Lee committed
1236 1237
}

1238 1239
static void
gtk_range_realize (GtkWidget *widget)
Elliot Lee's avatar
Elliot Lee committed
1240
{
1241 1242 1243
  GtkRange *range;
  GdkWindowAttr attributes;
  gint attributes_mask;  
Elliot Lee's avatar
Elliot Lee committed
1244

1245
  range = GTK_RANGE (widget);
Elliot Lee's avatar
Elliot Lee committed
1246

1247
  gtk_range_calc_layout (range, range->adjustment->value);
1248 1249 1250
  
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

1251
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
1252
  g_object_ref (widget->window);
1253
  
1254 1255 1256 1257 1258
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
1259
  attributes.wclass = GDK_INPUT_ONLY;
1260
  attributes.event_mask = gtk_widget_get_events (widget);
1261
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1262 1263 1264 1265 1266 1267
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
                            GDK_POINTER_MOTION_MASK |
                            GDK_POINTER_MOTION_HINT_MASK);

1268
  attributes_mask = GDK_WA_X | GDK_WA_Y;
1269

1270 1271 1272
  range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
					&attributes, attributes_mask);
  gdk_window_set_user_data (range->event_window, range);
1273 1274

  widget->style = gtk_style_attach (widget->style, widget->window);
Elliot Lee's avatar
Elliot Lee committed
1275 1276
}

1277 1278
static void
gtk_range_unrealize (GtkWidget *widget)
1279
{
1280
  GtkRange *range = GTK_RANGE (widget);
1281

1282 1283 1284
  gtk_range_remove_step_timer (range);
  gtk_range_remove_update_timer (range);
  
1285 1286 1287 1288
  gdk_window_set_user_data (range->event_window, NULL);
  gdk_window_destroy (range->event_window);
  range->event_window = NULL;
  
Matthias Clasen's avatar
Matthias Clasen committed
1289 1290
  if (GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize) (widget);
1291 1292
}

1293 1294 1295 1296 1297 1298 1299
static void
gtk_range_map (GtkWidget *widget)
{
  GtkRange *range = GTK_RANGE (widget);
  
  gdk_window_show (range->event_window);

Matthias Clasen's avatar
Matthias Clasen committed
1300
  GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
1301 1302 1303 1304 1305 1306 1307
}

static void
gtk_range_unmap (GtkWidget *widget)
{
  GtkRange *range = GTK_RANGE (widget);
    
1308 1309
  stop_scrolling (range);

1310 1311
  gdk_window_hide (range->event_window);

Matthias Clasen's avatar
Matthias Clasen committed
1312
  GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
1313 1314
}

1315
static void
1316 1317 1318 1319 1320 1321
draw_stepper (GtkRange     *range,
              GdkRectangle *rect,
              GtkArrowType  arrow_type,
              gboolean      clicked,
              gboolean      prelighted,
              GdkRectangle *area)
1322
{
1323 1324 1325
  GtkStateType state_type;
  GtkShadowType shadow_type;
  GdkRectangle intersection;
1326
  GtkWidget *widget = GTK_WIDGET (range);
1327

1328 1329 1330 1331 1332
  gint arrow_x;
  gint arrow_y;
  gint arrow_width;
  gint arrow_height;

1333
  gboolean arrow_sensitive = TRUE;
1334

1335 1336 1337
  /* More to get the right clip region than for efficiency */
  if (!gdk_rectangle_intersect (area, rect, &intersection))
    return;
1338 1339 1340

  intersection.x += widget->allocation.x;
  intersection.y += widget->allocation.y;
1341

1342 1343 1344 1345
  if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
                            arrow_type == GTK_ARROW_RIGHT)) ||
      (range->inverted  && (arrow_type == GTK_ARROW_UP ||
                            arrow_type == GTK_ARROW_LEFT)))
1346
    {
1347
      arrow_sensitive = range->layout->upper_sensitive;
1348
    }
1349
  else
1350
    {
1351
      arrow_sensitive = range->layout->lower_sensitive;
1352 1353
    }

1354
  if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
1355 1356 1357 1358 1359 1360 1361
    state_type = GTK_STATE_INSENSITIVE;
  else if (clicked)
    state_type = GTK_STATE_ACTIVE;
  else if (prelighted)
    state_type = GTK_STATE_PRELIGHT;
  else 
    state_type = GTK_STATE_NORMAL;
1362

1363
  if (clicked && arrow_sensitive)
1364
    shadow_type = GTK_SHADOW_IN;
1365
  else
1366
    shadow_type = GTK_SHA