~ubuntu-branches/ubuntu/precise/gtk+2.0/precise-updates

« back to all changes in this revision

Viewing changes to .pc/044_grips.patch/gtk/gtkrange.c

  • Committer: Package Import Robot
  • Author(s): Sebastien Bacher
  • Date: 2012-02-23 22:24:09 UTC
  • Revision ID: package-import@ubuntu.com-20120223222409-p6e5zvz25r0q8cut
Tags: 2.24.10-0ubuntu4
* debian/patches/044_grips.patch:
  - revert dropping, there is a public api in there and we need to fix the
    rdepends before dropping it

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* GTK - The GIMP Toolkit
 
2
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 
3
 * Copyright (C) 2001 Red Hat, Inc.
 
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
 
7
 * License as published by the Free Software Foundation; either
 
8
 * version 2 of the License, or (at your option) any later version.
 
9
 *
 
10
 * This library is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
13
 * Lesser General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU Lesser General Public
 
16
 * License along with this library; if not, write to the
 
17
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
18
 * Boston, MA 02111-1307, USA.
 
19
 */
 
20
 
 
21
/*
 
22
 * Modified by the GTK+ Team and others 1997-2004.  See the AUTHORS
 
23
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 
24
 * files for a list of changes.  These files are distributed with
 
25
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 
26
 */
 
27
 
 
28
#include "config.h"
 
29
 
 
30
#include <stdio.h>
 
31
#include <math.h>
 
32
 
 
33
#undef GTK_DISABLE_DEPRECATED
 
34
 
 
35
#include <gdk/gdkkeysyms.h>
 
36
#include "gtkmain.h"
 
37
#include "gtkmarshalers.h"
 
38
#include "gtkorientable.h"
 
39
#include "gtkrange.h"
 
40
#include "gtkscale.h"
 
41
#include "gtkscrollbar.h"
 
42
#include "gtkprivate.h"
 
43
#include "gtkintl.h"
 
44
#include "gtkalias.h"
 
45
 
 
46
#define SCROLL_DELAY_FACTOR 5    /* Scroll repeat multiplier */
 
47
#define UPDATE_DELAY        300  /* Delay for queued update */
 
48
 
 
49
enum {
 
50
  PROP_0,
 
51
  PROP_ORIENTATION,
 
52
  PROP_UPDATE_POLICY,
 
53
  PROP_ADJUSTMENT,
 
54
  PROP_INVERTED,
 
55
  PROP_LOWER_STEPPER_SENSITIVITY,
 
56
  PROP_UPPER_STEPPER_SENSITIVITY,
 
57
  PROP_SHOW_FILL_LEVEL,
 
58
  PROP_RESTRICT_TO_FILL_LEVEL,
 
59
  PROP_FILL_LEVEL,
 
60
  PROP_ROUND_DIGITS
 
61
};
 
62
 
 
63
enum {
 
64
  VALUE_CHANGED,
 
65
  ADJUST_BOUNDS,
 
66
  MOVE_SLIDER,
 
67
  CHANGE_VALUE,
 
68
  LAST_SIGNAL
 
69
};
 
70
 
 
71
typedef enum {
 
72
  MOUSE_OUTSIDE,
 
73
  MOUSE_STEPPER_A,
 
74
  MOUSE_STEPPER_B,
 
75
  MOUSE_STEPPER_C,
 
76
  MOUSE_STEPPER_D,
 
77
  MOUSE_TROUGH,
 
78
  MOUSE_SLIDER,
 
79
  MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
 
80
} MouseLocation;
 
81
 
 
82
typedef enum {
 
83
  STEPPER_A,
 
84
  STEPPER_B,
 
85
  STEPPER_C,
 
86
  STEPPER_D
 
87
} Stepper;
 
88
 
 
89
#define GTK_RANGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RANGE, GtkRangeLayout))
 
90
 
 
91
struct _GtkRangeLayout
 
92
{
 
93
  /* These are in widget->window coordinates */
 
94
  GdkRectangle stepper_a;
 
95
  GdkRectangle stepper_b;
 
96
  GdkRectangle stepper_c;
 
97
  GdkRectangle stepper_d;
 
98
  /* The trough rectangle is the area the thumb can slide in, not the
 
99
   * entire range_rect
 
100
   */
 
101
  GdkRectangle trough;
 
102
  GdkRectangle slider;
 
103
 
 
104
  /* Layout-related state */
 
105
 
 
106
  MouseLocation mouse_location;
 
107
  /* last mouse coords we got, or -1 if mouse is outside the range */
 
108
  gint mouse_x;
 
109
  gint mouse_y;
 
110
 
 
111
  /* "grabbed" mouse location, OUTSIDE for no grab */
 
112
  MouseLocation grab_location;
 
113
  guint grab_button : 8; /* 0 if none */
 
114
 
 
115
  /* Stepper sensitivity */
 
116
  guint lower_sensitive : 1;
 
117
  guint upper_sensitive : 1;
 
118
 
 
119
  /* Fill level */
 
120
  guint show_fill_level : 1;
 
121
  guint restrict_to_fill_level : 1;
 
122
 
 
123
  GtkSensitivityType lower_sensitivity;
 
124
  GtkSensitivityType upper_sensitivity;
 
125
  guint repaint_id;
 
126
 
 
127
  gdouble fill_level;
 
128
 
 
129
  GQuark slider_detail_quark;
 
130
  GQuark stepper_detail_quark[4];
 
131
 
 
132
  gdouble *marks;
 
133
  gint *mark_pos;
 
134
  gint n_marks;
 
135
  gboolean recalc_marks;
 
136
};
 
137
 
 
138
 
 
139
static void gtk_range_set_property   (GObject          *object,
 
140
                                      guint             prop_id,
 
141
                                      const GValue     *value,
 
142
                                      GParamSpec       *pspec);
 
143
static void gtk_range_get_property   (GObject          *object,
 
144
                                      guint             prop_id,
 
145
                                      GValue           *value,
 
146
                                      GParamSpec       *pspec);
 
147
static void gtk_range_destroy        (GtkObject        *object);
 
148
static void gtk_range_size_request   (GtkWidget        *widget,
 
149
                                      GtkRequisition   *requisition);
 
150
static void gtk_range_size_allocate  (GtkWidget        *widget,
 
151
                                      GtkAllocation    *allocation);
 
152
static void gtk_range_realize        (GtkWidget        *widget);
 
153
static void gtk_range_unrealize      (GtkWidget        *widget);
 
154
static void gtk_range_map            (GtkWidget        *widget);
 
155
static void gtk_range_unmap          (GtkWidget        *widget);
 
156
static gboolean gtk_range_expose         (GtkWidget        *widget,
 
157
                                      GdkEventExpose   *event);
 
158
static gboolean gtk_range_button_press   (GtkWidget        *widget,
 
159
                                      GdkEventButton   *event);
 
160
static gboolean gtk_range_button_release (GtkWidget        *widget,
 
161
                                      GdkEventButton   *event);
 
162
static gboolean gtk_range_motion_notify  (GtkWidget        *widget,
 
163
                                      GdkEventMotion   *event);
 
164
static gboolean gtk_range_enter_notify   (GtkWidget        *widget,
 
165
                                      GdkEventCrossing *event);
 
166
static gboolean gtk_range_leave_notify   (GtkWidget        *widget,
 
167
                                      GdkEventCrossing *event);
 
168
static gboolean gtk_range_grab_broken (GtkWidget          *widget,
 
169
                                       GdkEventGrabBroken *event);
 
170
static void gtk_range_grab_notify    (GtkWidget          *widget,
 
171
                                      gboolean            was_grabbed);
 
172
static void gtk_range_state_changed  (GtkWidget          *widget,
 
173
                                      GtkStateType        previous_state);
 
174
static gboolean gtk_range_scroll_event   (GtkWidget        *widget,
 
175
                                      GdkEventScroll   *event);
 
176
static void gtk_range_style_set      (GtkWidget        *widget,
 
177
                                      GtkStyle         *previous_style);
 
178
static void update_slider_position   (GtkRange         *range,
 
179
                                      gint              mouse_x,
 
180
                                      gint              mouse_y);
 
181
static void stop_scrolling           (GtkRange         *range);
 
182
 
 
183
/* Range methods */
 
184
 
 
185
static void gtk_range_move_slider              (GtkRange         *range,
 
186
                                                GtkScrollType     scroll);
 
187
 
 
188
/* Internals */
 
189
static gboolean      gtk_range_scroll                   (GtkRange      *range,
 
190
                                                         GtkScrollType  scroll);
 
191
static gboolean      gtk_range_update_mouse_location    (GtkRange      *range);
 
192
static void          gtk_range_calc_layout              (GtkRange      *range,
 
193
                                                         gdouble        adjustment_value);
 
194
static void          gtk_range_calc_marks               (GtkRange      *range);
 
195
static void          gtk_range_get_props                (GtkRange      *range,
 
196
                                                         gint          *slider_width,
 
197
                                                         gint          *stepper_size,
 
198
                                                         gint          *focus_width,
 
199
                                                         gint          *trough_border,
 
200
                                                         gint          *stepper_spacing,
 
201
                                                         gboolean      *trough_under_steppers,
 
202
                                                         gint          *arrow_displacement_x,
 
203
                                                         gint          *arrow_displacement_y);
 
204
static void          gtk_range_calc_request             (GtkRange      *range,
 
205
                                                         gint           slider_width,
 
206
                                                         gint           stepper_size,
 
207
                                                         gint           focus_width,
 
208
                                                         gint           trough_border,
 
209
                                                         gint           stepper_spacing,
 
210
                                                         GdkRectangle  *range_rect,
 
211
                                                         GtkBorder     *border,
 
212
                                                         gint          *n_steppers_p,
 
213
                                                         gboolean      *has_steppers_ab,
 
214
                                                         gboolean      *has_steppers_cd,
 
215
                                                         gint          *slider_length_p);
 
216
static void          gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
 
217
                                                         gpointer       data);
 
218
static void          gtk_range_adjustment_changed       (GtkAdjustment *adjustment,
 
219
                                                         gpointer       data);
 
220
static void          gtk_range_add_step_timer           (GtkRange      *range,
 
221
                                                         GtkScrollType  step);
 
222
static void          gtk_range_remove_step_timer        (GtkRange      *range);
 
223
static void          gtk_range_reset_update_timer       (GtkRange      *range);
 
224
static void          gtk_range_remove_update_timer      (GtkRange      *range);
 
225
static GdkRectangle* get_area                           (GtkRange      *range,
 
226
                                                         MouseLocation  location);
 
227
static gboolean      gtk_range_real_change_value        (GtkRange      *range,
 
228
                                                         GtkScrollType  scroll,
 
229
                                                         gdouble        value);
 
230
static void          gtk_range_update_value             (GtkRange      *range);
 
231
static gboolean      gtk_range_key_press                (GtkWidget     *range,
 
232
                                                         GdkEventKey   *event);
 
233
 
 
234
 
 
235
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
 
236
                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
 
237
                                                         NULL))
 
238
 
 
239
static guint signals[LAST_SIGNAL];
 
240
 
 
241
 
 
242
static void
 
243
gtk_range_class_init (GtkRangeClass *class)
 
244
{
 
245
  GObjectClass   *gobject_class;
 
246
  GtkObjectClass *object_class;
 
247
  GtkWidgetClass *widget_class;
 
248
 
 
249
  gobject_class = G_OBJECT_CLASS (class);
 
250
  object_class = (GtkObjectClass*) class;
 
251
  widget_class = (GtkWidgetClass*) class;
 
252
 
 
253
  gobject_class->set_property = gtk_range_set_property;
 
254
  gobject_class->get_property = gtk_range_get_property;
 
255
 
 
256
  object_class->destroy = gtk_range_destroy;
 
257
 
 
258
  widget_class->size_request = gtk_range_size_request;
 
259
  widget_class->size_allocate = gtk_range_size_allocate;
 
260
  widget_class->realize = gtk_range_realize;
 
261
  widget_class->unrealize = gtk_range_unrealize;  
 
262
  widget_class->map = gtk_range_map;
 
263
  widget_class->unmap = gtk_range_unmap;
 
264
  widget_class->expose_event = gtk_range_expose;
 
265
  widget_class->button_press_event = gtk_range_button_press;
 
266
  widget_class->button_release_event = gtk_range_button_release;
 
267
  widget_class->motion_notify_event = gtk_range_motion_notify;
 
268
  widget_class->scroll_event = gtk_range_scroll_event;
 
269
  widget_class->enter_notify_event = gtk_range_enter_notify;
 
270
  widget_class->leave_notify_event = gtk_range_leave_notify;
 
271
  widget_class->grab_broken_event = gtk_range_grab_broken;
 
272
  widget_class->grab_notify = gtk_range_grab_notify;
 
273
  widget_class->state_changed = gtk_range_state_changed;
 
274
  widget_class->style_set = gtk_range_style_set;
 
275
  widget_class->key_press_event = gtk_range_key_press;
 
276
 
 
277
  class->move_slider = gtk_range_move_slider;
 
278
  class->change_value = gtk_range_real_change_value;
 
279
 
 
280
  class->slider_detail = "slider";
 
281
  class->stepper_detail = "stepper";
 
282
 
 
283
  /**
 
284
   * GtkRange::value-changed:
 
285
   * @range: the #GtkRange
 
286
   *
 
287
   * Emitted when the range value changes.
 
288
   */
 
289
  signals[VALUE_CHANGED] =
 
290
    g_signal_new (I_("value-changed"),
 
291
                  G_TYPE_FROM_CLASS (gobject_class),
 
292
                  G_SIGNAL_RUN_LAST,
 
293
                  G_STRUCT_OFFSET (GtkRangeClass, value_changed),
 
294
                  NULL, NULL,
 
295
                  _gtk_marshal_VOID__VOID,
 
296
                  G_TYPE_NONE, 0);
 
297
  
 
298
  signals[ADJUST_BOUNDS] =
 
299
    g_signal_new (I_("adjust-bounds"),
 
300
                  G_TYPE_FROM_CLASS (gobject_class),
 
301
                  G_SIGNAL_RUN_LAST,
 
302
                  G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
 
303
                  NULL, NULL,
 
304
                  _gtk_marshal_VOID__DOUBLE,
 
305
                  G_TYPE_NONE, 1,
 
306
                  G_TYPE_DOUBLE);
 
307
  
 
308
  /**
 
309
   * GtkRange::move-slider:
 
310
   * @range: the #GtkRange
 
311
   * @step: how to move the slider
 
312
   *
 
313
   * Virtual function that moves the slider. Used for keybindings.
 
314
   */
 
315
  signals[MOVE_SLIDER] =
 
316
    g_signal_new (I_("move-slider"),
 
317
                  G_TYPE_FROM_CLASS (gobject_class),
 
318
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 
319
                  G_STRUCT_OFFSET (GtkRangeClass, move_slider),
 
320
                  NULL, NULL,
 
321
                  _gtk_marshal_VOID__ENUM,
 
322
                  G_TYPE_NONE, 1,
 
323
                  GTK_TYPE_SCROLL_TYPE);
 
324
 
 
325
  /**
 
326
   * GtkRange::change-value:
 
327
   * @range: the range that received the signal
 
328
   * @scroll: the type of scroll action that was performed
 
329
   * @value: the new value resulting from the scroll action
 
330
   * @returns: %TRUE to prevent other handlers from being invoked for the
 
331
   * signal, %FALSE to propagate the signal further
 
332
   *
 
333
   * The ::change-value signal is emitted when a scroll action is
 
334
   * performed on a range.  It allows an application to determine the
 
335
   * type of scroll event that occurred and the resultant new value.
 
336
   * The application can handle the event itself and return %TRUE to
 
337
   * prevent further processing.  Or, by returning %FALSE, it can pass
 
338
   * the event to other handlers until the default GTK+ handler is
 
339
   * reached.
 
340
   *
 
341
   * The value parameter is unrounded.  An application that overrides
 
342
   * the ::change-value signal is responsible for clamping the value to
 
343
   * the desired number of decimal digits; the default GTK+ handler
 
344
   * clamps the value based on #GtkRange:round_digits.
 
345
   *
 
346
   * It is not possible to use delayed update policies in an overridden
 
347
   * ::change-value handler.
 
348
   *
 
349
   * Since: 2.6
 
350
   */
 
351
  signals[CHANGE_VALUE] =
 
352
    g_signal_new (I_("change-value"),
 
353
                  G_TYPE_FROM_CLASS (gobject_class),
 
354
                  G_SIGNAL_RUN_LAST,
 
355
                  G_STRUCT_OFFSET (GtkRangeClass, change_value),
 
356
                  _gtk_boolean_handled_accumulator, NULL,
 
357
                  _gtk_marshal_BOOLEAN__ENUM_DOUBLE,
 
358
                  G_TYPE_BOOLEAN, 2,
 
359
                  GTK_TYPE_SCROLL_TYPE,
 
360
                  G_TYPE_DOUBLE);
 
361
 
 
362
  g_object_class_override_property (gobject_class,
 
363
                                    PROP_ORIENTATION,
 
364
                                    "orientation");
 
365
 
 
366
  g_object_class_install_property (gobject_class,
 
367
                                   PROP_UPDATE_POLICY,
 
368
                                   g_param_spec_enum ("update-policy",
 
369
                                                      P_("Update policy"),
 
370
                                                      P_("How the range should be updated on the screen"),
 
371
                                                      GTK_TYPE_UPDATE_TYPE,
 
372
                                                      GTK_UPDATE_CONTINUOUS,
 
373
                                                      GTK_PARAM_READWRITE));
 
374
  
 
375
  g_object_class_install_property (gobject_class,
 
376
                                   PROP_ADJUSTMENT,
 
377
                                   g_param_spec_object ("adjustment",
 
378
                                                        P_("Adjustment"),
 
379
                                                        P_("The GtkAdjustment that contains the current value of this range object"),
 
380
                                                        GTK_TYPE_ADJUSTMENT,
 
381
                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
382
 
 
383
  g_object_class_install_property (gobject_class,
 
384
                                   PROP_INVERTED,
 
385
                                   g_param_spec_boolean ("inverted",
 
386
                                                        P_("Inverted"),
 
387
                                                        P_("Invert direction slider moves to increase range value"),
 
388
                                                         FALSE,
 
389
                                                         GTK_PARAM_READWRITE));
 
390
 
 
391
  g_object_class_install_property (gobject_class,
 
392
                                   PROP_LOWER_STEPPER_SENSITIVITY,
 
393
                                   g_param_spec_enum ("lower-stepper-sensitivity",
 
394
                                                      P_("Lower stepper sensitivity"),
 
395
                                                      P_("The sensitivity policy for the stepper that points to the adjustment's lower side"),
 
396
                                                      GTK_TYPE_SENSITIVITY_TYPE,
 
397
                                                      GTK_SENSITIVITY_AUTO,
 
398
                                                      GTK_PARAM_READWRITE));
 
399
 
 
400
  g_object_class_install_property (gobject_class,
 
401
                                   PROP_UPPER_STEPPER_SENSITIVITY,
 
402
                                   g_param_spec_enum ("upper-stepper-sensitivity",
 
403
                                                      P_("Upper stepper sensitivity"),
 
404
                                                      P_("The sensitivity policy for the stepper that points to the adjustment's upper side"),
 
405
                                                      GTK_TYPE_SENSITIVITY_TYPE,
 
406
                                                      GTK_SENSITIVITY_AUTO,
 
407
                                                      GTK_PARAM_READWRITE));
 
408
 
 
409
  /**
 
410
   * GtkRange:show-fill-level:
 
411
   *
 
412
   * The show-fill-level property controls whether fill level indicator
 
413
   * graphics are displayed on the trough. See
 
414
   * gtk_range_set_show_fill_level().
 
415
   *
 
416
   * Since: 2.12
 
417
   **/
 
418
  g_object_class_install_property (gobject_class,
 
419
                                   PROP_SHOW_FILL_LEVEL,
 
420
                                   g_param_spec_boolean ("show-fill-level",
 
421
                                                         P_("Show Fill Level"),
 
422
                                                         P_("Whether to display a fill level indicator graphics on trough."),
 
423
                                                         FALSE,
 
424
                                                         GTK_PARAM_READWRITE));
 
425
 
 
426
  /**
 
427
   * GtkRange:restrict-to-fill-level:
 
428
   *
 
429
   * The restrict-to-fill-level property controls whether slider
 
430
   * movement is restricted to an upper boundary set by the
 
431
   * fill level. See gtk_range_set_restrict_to_fill_level().
 
432
   *
 
433
   * Since: 2.12
 
434
   **/
 
435
  g_object_class_install_property (gobject_class,
 
436
                                   PROP_RESTRICT_TO_FILL_LEVEL,
 
437
                                   g_param_spec_boolean ("restrict-to-fill-level",
 
438
                                                         P_("Restrict to Fill Level"),
 
439
                                                         P_("Whether to restrict the upper boundary to the fill level."),
 
440
                                                         TRUE,
 
441
                                                         GTK_PARAM_READWRITE));
 
442
 
 
443
  /**
 
444
   * GtkRange:fill-level:
 
445
   *
 
446
   * The fill level (e.g. prebuffering of a network stream).
 
447
   * See gtk_range_set_fill_level().
 
448
   *
 
449
   * Since: 2.12
 
450
   **/
 
451
  g_object_class_install_property (gobject_class,
 
452
                                   PROP_FILL_LEVEL,
 
453
                                   g_param_spec_double ("fill-level",
 
454
                                                        P_("Fill Level"),
 
455
                                                        P_("The fill level."),
 
456
                                                        -G_MAXDOUBLE,
 
457
                                                        G_MAXDOUBLE,
 
458
                                                        G_MAXDOUBLE,
 
459
                                                        GTK_PARAM_READWRITE));
 
460
 
 
461
  /**
 
462
   * GtkRange:round-digits:
 
463
   *
 
464
   * The number of digits to round the value to when
 
465
   * it changes, or -1. See #GtkRange::change-value.
 
466
   *
 
467
   * Since: 2.24
 
468
   */
 
469
  g_object_class_install_property (gobject_class,
 
470
                                   PROP_ROUND_DIGITS,
 
471
                                   g_param_spec_int ("round-digits",
 
472
                                                     P_("Round Digits"),
 
473
                                                     P_("The number of digits to round the value to."),
 
474
                                                     -1,
 
475
                                                     G_MAXINT,
 
476
                                                     -1,
 
477
                                                     GTK_PARAM_READWRITE));
 
478
 
 
479
  gtk_widget_class_install_style_property (widget_class,
 
480
                                           g_param_spec_int ("slider-width",
 
481
                                                             P_("Slider Width"),
 
482
                                                             P_("Width of scrollbar or scale thumb"),
 
483
                                                             0,
 
484
                                                             G_MAXINT,
 
485
                                                             14,
 
486
                                                             GTK_PARAM_READABLE));
 
487
  gtk_widget_class_install_style_property (widget_class,
 
488
                                           g_param_spec_int ("trough-border",
 
489
                                                             P_("Trough Border"),
 
490
                                                             P_("Spacing between thumb/steppers and outer trough bevel"),
 
491
                                                             0,
 
492
                                                             G_MAXINT,
 
493
                                                             1,
 
494
                                                             GTK_PARAM_READABLE));
 
495
  gtk_widget_class_install_style_property (widget_class,
 
496
                                           g_param_spec_int ("stepper-size",
 
497
                                                             P_("Stepper Size"),
 
498
                                                             P_("Length of step buttons at ends"),
 
499
                                                             0,
 
500
                                                             G_MAXINT,
 
501
                                                             14,
 
502
                                                             GTK_PARAM_READABLE));
 
503
  /**
 
504
   * GtkRange:stepper-spacing:
 
505
   *
 
506
   * The spacing between the stepper buttons and thumb. Note that
 
507
   * setting this value to anything > 0 will automatically set the
 
508
   * trough-under-steppers style property to %TRUE as well. Also,
 
509
   * stepper-spacing won't have any effect if there are no steppers.
 
510
   */
 
511
  gtk_widget_class_install_style_property (widget_class,
 
512
                                           g_param_spec_int ("stepper-spacing",
 
513
                                                             P_("Stepper Spacing"),
 
514
                                                             P_("Spacing between step buttons and thumb"),
 
515
                                                             0,
 
516
                                                             G_MAXINT,
 
517
                                                             0,
 
518
                                                             GTK_PARAM_READABLE));
 
519
  gtk_widget_class_install_style_property (widget_class,
 
520
                                           g_param_spec_int ("arrow-displacement-x",
 
521
                                                             P_("Arrow X Displacement"),
 
522
                                                             P_("How far in the x direction to move the arrow when the button is depressed"),
 
523
                                                             G_MININT,
 
524
                                                             G_MAXINT,
 
525
                                                             0,
 
526
                                                             GTK_PARAM_READABLE));
 
527
  gtk_widget_class_install_style_property (widget_class,
 
528
                                           g_param_spec_int ("arrow-displacement-y",
 
529
                                                             P_("Arrow Y Displacement"),
 
530
                                                             P_("How far in the y direction to move the arrow when the button is depressed"),
 
531
                                                             G_MININT,
 
532
                                                             G_MAXINT,
 
533
                                                             0,
 
534
                                                             GTK_PARAM_READABLE));
 
535
 
 
536
  /**
 
537
   * GtkRange:activate-slider:
 
538
   *
 
539
   * When %TRUE, sliders will be drawn active and with shadow in
 
540
   * while they are dragged.
 
541
   *
 
542
   * Deprecated: 2.22: This style property will be removed in GTK+ 3
 
543
   */
 
544
  gtk_widget_class_install_style_property (widget_class,
 
545
                                           g_param_spec_boolean ("activate-slider",
 
546
                                                                 P_("Draw slider ACTIVE during drag"),
 
547
                                                                 P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
 
548
                                                                 FALSE,
 
549
                                                                 GTK_PARAM_READABLE));
 
550
 
 
551
  /**
 
552
   * GtkRange:trough-side-details:
 
553
   *
 
554
   * When %TRUE, the parts of the trough on the two sides of the 
 
555
   * slider are drawn with different details.
 
556
   *
 
557
   * Since: 2.10
 
558
   *
 
559
   * Deprecated: 2.22: This style property will be removed in GTK+ 3
 
560
   */
 
561
  gtk_widget_class_install_style_property (widget_class,
 
562
                                           g_param_spec_boolean ("trough-side-details",
 
563
                                                                 P_("Trough Side Details"),
 
564
                                                                 P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"),
 
565
                                                                 FALSE,
 
566
                                                                 GTK_PARAM_READABLE));
 
567
 
 
568
  /**
 
569
   * GtkRange:trough-under-steppers:
 
570
   *
 
571
   * Whether to draw the trough across the full length of the range or
 
572
   * to exclude the steppers and their spacing. Note that setting the
 
573
   * #GtkRange:stepper-spacing style property to any value > 0 will
 
574
   * automatically enable trough-under-steppers too.
 
575
   *
 
576
   * Since: 2.10
 
577
   */
 
578
  gtk_widget_class_install_style_property (widget_class,
 
579
                                           g_param_spec_boolean ("trough-under-steppers",
 
580
                                                                 P_("Trough Under Steppers"),
 
581
                                                                 P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
 
582
                                                                 TRUE,
 
583
                                                                 GTK_PARAM_READABLE));
 
584
 
 
585
  /**
 
586
   * GtkRange:arrow-scaling:
 
587
   *
 
588
   * The arrow size proportion relative to the scroll button size.
 
589
   *
 
590
   * Since: 2.14
 
591
   */
 
592
  gtk_widget_class_install_style_property (widget_class,
 
593
                                           g_param_spec_float ("arrow-scaling",
 
594
                                                               P_("Arrow scaling"),
 
595
                                                               P_("Arrow scaling with regard to scroll button size"),
 
596
                                                               0.0, 1.0, 0.5,
 
597
                                                               GTK_PARAM_READABLE));
 
598
 
 
599
  /**
 
600
   * GtkRange:stepper-position-details:
 
601
   *
 
602
   * When %TRUE, the detail string for rendering the steppers will be
 
603
   * suffixed with information about the stepper position.
 
604
   *
 
605
   * Since: 2.22
 
606
   *
 
607
   * Deprecated: 2.22: This style property will be removed in GTK+ 3
 
608
   */
 
609
  gtk_widget_class_install_style_property (widget_class,
 
610
                                           g_param_spec_boolean ("stepper-position-details",
 
611
                                                                 P_("Stepper Position Details"),
 
612
                                                                 P_("When TRUE, the detail string for rendering the steppers is suffixed with position information"),
 
613
                                                                 FALSE,
 
614
                                                                 GTK_PARAM_READABLE));
 
615
 
 
616
  g_type_class_add_private (class, sizeof (GtkRangeLayout));
 
617
}
 
618
 
 
619
static void
 
620
gtk_range_set_property (GObject      *object,
 
621
                        guint         prop_id,
 
622
                        const GValue *value,
 
623
                        GParamSpec   *pspec)
 
624
{
 
625
  GtkRange *range = GTK_RANGE (object);
 
626
 
 
627
  switch (prop_id)
 
628
    {
 
629
    case PROP_ORIENTATION:
 
630
      range->orientation = g_value_get_enum (value);
 
631
 
 
632
      range->layout->slider_detail_quark = 0;
 
633
      range->layout->stepper_detail_quark[0] = 0;
 
634
      range->layout->stepper_detail_quark[1] = 0;
 
635
      range->layout->stepper_detail_quark[2] = 0;
 
636
      range->layout->stepper_detail_quark[3] = 0;
 
637
 
 
638
      gtk_widget_queue_resize (GTK_WIDGET (range));
 
639
      break;
 
640
    case PROP_UPDATE_POLICY:
 
641
      gtk_range_set_update_policy (range, g_value_get_enum (value));
 
642
      break;
 
643
    case PROP_ADJUSTMENT:
 
644
      gtk_range_set_adjustment (range, g_value_get_object (value));
 
645
      break;
 
646
    case PROP_INVERTED:
 
647
      gtk_range_set_inverted (range, g_value_get_boolean (value));
 
648
      break;
 
649
    case PROP_LOWER_STEPPER_SENSITIVITY:
 
650
      gtk_range_set_lower_stepper_sensitivity (range, g_value_get_enum (value));
 
651
      break;
 
652
    case PROP_UPPER_STEPPER_SENSITIVITY:
 
653
      gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
 
654
      break;
 
655
    case PROP_SHOW_FILL_LEVEL:
 
656
      gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
 
657
      break;
 
658
    case PROP_RESTRICT_TO_FILL_LEVEL:
 
659
      gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
 
660
      break;
 
661
    case PROP_FILL_LEVEL:
 
662
      gtk_range_set_fill_level (range, g_value_get_double (value));
 
663
      break;
 
664
    case PROP_ROUND_DIGITS:
 
665
      gtk_range_set_round_digits (range, g_value_get_int (value));
 
666
      break;
 
667
    default:
 
668
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
669
      break;
 
670
    }
 
671
}
 
672
 
 
673
static void
 
674
gtk_range_get_property (GObject      *object,
 
675
                        guint         prop_id,
 
676
                        GValue       *value,
 
677
                        GParamSpec   *pspec)
 
678
{
 
679
  GtkRange *range = GTK_RANGE (object);
 
680
 
 
681
  switch (prop_id)
 
682
    {
 
683
    case PROP_ORIENTATION:
 
684
      g_value_set_enum (value, range->orientation);
 
685
      break;
 
686
    case PROP_UPDATE_POLICY:
 
687
      g_value_set_enum (value, range->update_policy);
 
688
      break;
 
689
    case PROP_ADJUSTMENT:
 
690
      g_value_set_object (value, range->adjustment);
 
691
      break;
 
692
    case PROP_INVERTED:
 
693
      g_value_set_boolean (value, range->inverted);
 
694
      break;
 
695
    case PROP_LOWER_STEPPER_SENSITIVITY:
 
696
      g_value_set_enum (value, gtk_range_get_lower_stepper_sensitivity (range));
 
697
      break;
 
698
    case PROP_UPPER_STEPPER_SENSITIVITY:
 
699
      g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
 
700
      break;
 
701
    case PROP_SHOW_FILL_LEVEL:
 
702
      g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
 
703
      break;
 
704
    case PROP_RESTRICT_TO_FILL_LEVEL:
 
705
      g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
 
706
      break;
 
707
    case PROP_FILL_LEVEL:
 
708
      g_value_set_double (value, gtk_range_get_fill_level (range));
 
709
      break;
 
710
    case PROP_ROUND_DIGITS:
 
711
      g_value_set_int (value, gtk_range_get_round_digits (range));
 
712
      break;
 
713
    default:
 
714
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
715
      break;
 
716
    }
 
717
}
 
718
 
 
719
static void
 
720
gtk_range_init (GtkRange *range)
 
721
{
 
722
  gtk_widget_set_has_window (GTK_WIDGET (range), FALSE);
 
723
 
 
724
  range->orientation = GTK_ORIENTATION_HORIZONTAL;
 
725
  range->adjustment = NULL;
 
726
  range->update_policy = GTK_UPDATE_CONTINUOUS;
 
727
  range->inverted = FALSE;
 
728
  range->flippable = FALSE;
 
729
  range->min_slider_size = 1;
 
730
  range->has_stepper_a = FALSE;
 
731
  range->has_stepper_b = FALSE;
 
732
  range->has_stepper_c = FALSE;
 
733
  range->has_stepper_d = FALSE;
 
734
  range->need_recalc = TRUE;
 
735
  range->round_digits = -1;
 
736
  range->layout = GTK_RANGE_GET_PRIVATE (range);
 
737
  range->layout->mouse_location = MOUSE_OUTSIDE;
 
738
  range->layout->mouse_x = -1;
 
739
  range->layout->mouse_y = -1;
 
740
  range->layout->grab_location = MOUSE_OUTSIDE;
 
741
  range->layout->grab_button = 0;
 
742
  range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
 
743
  range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
 
744
  range->layout->lower_sensitive = TRUE;
 
745
  range->layout->upper_sensitive = TRUE;
 
746
  range->layout->show_fill_level = FALSE;
 
747
  range->layout->restrict_to_fill_level = TRUE;
 
748
  range->layout->fill_level = G_MAXDOUBLE;
 
749
  range->timer = NULL;  
 
750
}
 
751
 
 
752
/**
 
753
 * gtk_range_get_adjustment:
 
754
 * @range: a #GtkRange
 
755
 * 
 
756
 * Get the #GtkAdjustment which is the "model" object for #GtkRange.
 
757
 * See gtk_range_set_adjustment() for details.
 
758
 * The return value does not have a reference added, so should not
 
759
 * be unreferenced.
 
760
 * 
 
761
 * Return value: (transfer none): a #GtkAdjustment
 
762
 **/
 
763
GtkAdjustment*
 
764
gtk_range_get_adjustment (GtkRange *range)
 
765
{
 
766
  g_return_val_if_fail (GTK_IS_RANGE (range), NULL);
 
767
 
 
768
  if (!range->adjustment)
 
769
    gtk_range_set_adjustment (range, NULL);
 
770
 
 
771
  return range->adjustment;
 
772
}
 
773
 
 
774
/**
 
775
 * gtk_range_set_update_policy:
 
776
 * @range: a #GtkRange
 
777
 * @policy: update policy
 
778
 *
 
779
 * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
 
780
 * anytime the range slider is moved, the range value will change and the
 
781
 * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
 
782
 * the value will be updated after a brief timeout where no slider motion
 
783
 * occurs, so updates are spaced by a short time rather than
 
784
 * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
 
785
 * be updated when the user releases the button and ends the slider
 
786
 * drag operation.
 
787
 *
 
788
 * Deprecated: 2.24: There is no replacement. If you require delayed
 
789
 *   updates, you need to code it yourself.
 
790
 **/
 
791
void
 
792
gtk_range_set_update_policy (GtkRange      *range,
 
793
                             GtkUpdateType  policy)
 
794
{
 
795
  g_return_if_fail (GTK_IS_RANGE (range));
 
796
 
 
797
  if (range->update_policy != policy)
 
798
    {
 
799
      range->update_policy = policy;
 
800
      g_object_notify (G_OBJECT (range), "update-policy");
 
801
    }
 
802
}
 
803
 
 
804
/**
 
805
 * gtk_range_get_update_policy:
 
806
 * @range: a #GtkRange
 
807
 *
 
808
 * Gets the update policy of @range. See gtk_range_set_update_policy().
 
809
 *
 
810
 * Return value: the current update policy
 
811
 *
 
812
 * Deprecated: 2.24: There is no replacement. If you require delayed
 
813
 *   updates, you need to code it yourself.
 
814
 **/
 
815
GtkUpdateType
 
816
gtk_range_get_update_policy (GtkRange *range)
 
817
{
 
818
  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
 
819
 
 
820
  return range->update_policy;
 
821
}
 
822
 
 
823
/**
 
824
 * gtk_range_set_adjustment:
 
825
 * @range: a #GtkRange
 
826
 * @adjustment: a #GtkAdjustment
 
827
 *
 
828
 * Sets the adjustment to be used as the "model" object for this range
 
829
 * widget. The adjustment indicates the current range value, the
 
830
 * minimum and maximum range values, the step/page increments used
 
831
 * for keybindings and scrolling, and the page size. The page size
 
832
 * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
 
833
 * indicates the size of the visible area of the widget being scrolled.
 
834
 * The page size affects the size of the scrollbar slider.
 
835
 **/
 
836
void
 
837
gtk_range_set_adjustment (GtkRange      *range,
 
838
                          GtkAdjustment *adjustment)
 
839
{
 
840
  g_return_if_fail (GTK_IS_RANGE (range));
 
841
  
 
842
  if (!adjustment)
 
843
    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
 
844
  else
 
845
    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
 
846
 
 
847
  if (range->adjustment != adjustment)
 
848
    {
 
849
      if (range->adjustment)
 
850
        {
 
851
          g_signal_handlers_disconnect_by_func (range->adjustment,
 
852
                                                gtk_range_adjustment_changed,
 
853
                                                range);
 
854
          g_signal_handlers_disconnect_by_func (range->adjustment,
 
855
                                                gtk_range_adjustment_value_changed,
 
856
                                                range);
 
857
          g_object_unref (range->adjustment);
 
858
        }
 
859
 
 
860
      range->adjustment = adjustment;
 
861
      g_object_ref_sink (adjustment);
 
862
      
 
863
      g_signal_connect (adjustment, "changed",
 
864
                        G_CALLBACK (gtk_range_adjustment_changed),
 
865
                        range);
 
866
      g_signal_connect (adjustment, "value-changed",
 
867
                        G_CALLBACK (gtk_range_adjustment_value_changed),
 
868
                        range);
 
869
      
 
870
      gtk_range_adjustment_changed (adjustment, range);
 
871
      g_object_notify (G_OBJECT (range), "adjustment");
 
872
    }
 
873
}
 
874
 
 
875
/**
 
876
 * gtk_range_set_inverted:
 
877
 * @range: a #GtkRange
 
878
 * @setting: %TRUE to invert the range
 
879
 *
 
880
 * Ranges normally move from lower to higher values as the
 
881
 * slider moves from top to bottom or left to right. Inverted
 
882
 * ranges have higher values at the top or on the right rather than
 
883
 * on the bottom or left.
 
884
 **/
 
885
void
 
886
gtk_range_set_inverted (GtkRange *range,
 
887
                        gboolean  setting)
 
888
{
 
889
  g_return_if_fail (GTK_IS_RANGE (range));
 
890
  
 
891
  setting = setting != FALSE;
 
892
 
 
893
  if (setting != range->inverted)
 
894
    {
 
895
      range->inverted = setting;
 
896
      g_object_notify (G_OBJECT (range), "inverted");
 
897
      gtk_widget_queue_resize (GTK_WIDGET (range));
 
898
    }
 
899
}
 
900
 
 
901
/**
 
902
 * gtk_range_get_inverted:
 
903
 * @range: a #GtkRange
 
904
 * 
 
905
 * Gets the value set by gtk_range_set_inverted().
 
906
 * 
 
907
 * Return value: %TRUE if the range is inverted
 
908
 **/
 
909
gboolean
 
910
gtk_range_get_inverted (GtkRange *range)
 
911
{
 
912
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
 
913
 
 
914
  return range->inverted;
 
915
}
 
916
 
 
917
/**
 
918
 * gtk_range_set_flippable:
 
919
 * @range: a #GtkRange
 
920
 * @flippable: %TRUE to make the range flippable
 
921
 *
 
922
 * If a range is flippable, it will switch its direction if it is
 
923
 * horizontal and its direction is %GTK_TEXT_DIR_RTL.
 
924
 *
 
925
 * See gtk_widget_get_direction().
 
926
 *
 
927
 * Since: 2.18
 
928
 **/
 
929
void
 
930
gtk_range_set_flippable (GtkRange *range,
 
931
                         gboolean  flippable)
 
932
{
 
933
  g_return_if_fail (GTK_IS_RANGE (range));
 
934
 
 
935
  flippable = flippable ? TRUE : FALSE;
 
936
 
 
937
  if (flippable != range->flippable)
 
938
    {
 
939
      range->flippable = flippable;
 
940
 
 
941
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
942
    }
 
943
}
 
944
 
 
945
/**
 
946
 * gtk_range_get_flippable:
 
947
 * @range: a #GtkRange
 
948
 *
 
949
 * Gets the value set by gtk_range_set_flippable().
 
950
 *
 
951
 * Return value: %TRUE if the range is flippable
 
952
 *
 
953
 * Since: 2.18
 
954
 **/
 
955
gboolean
 
956
gtk_range_get_flippable (GtkRange *range)
 
957
{
 
958
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
 
959
 
 
960
  return range->flippable;
 
961
}
 
962
 
 
963
/**
 
964
 * gtk_range_set_slider_size_fixed:
 
965
 * @range: a #GtkRange
 
966
 * @size_fixed: %TRUE to make the slider size constant
 
967
 *
 
968
 * Sets whether the range's slider has a fixed size, or a size that
 
969
 * depends on it's adjustment's page size.
 
970
 *
 
971
 * This function is useful mainly for #GtkRange subclasses.
 
972
 *
 
973
 * Since: 2.20
 
974
 **/
 
975
void
 
976
gtk_range_set_slider_size_fixed (GtkRange *range,
 
977
                                 gboolean  size_fixed)
 
978
{
 
979
  g_return_if_fail (GTK_IS_RANGE (range));
 
980
 
 
981
  if (size_fixed != range->slider_size_fixed)
 
982
    {
 
983
      range->slider_size_fixed = size_fixed ? TRUE : FALSE;
 
984
 
 
985
      range->need_recalc = TRUE;
 
986
      gtk_range_calc_layout (range, range->adjustment->value);
 
987
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
988
    }
 
989
}
 
990
 
 
991
/**
 
992
 * gtk_range_get_slider_size_fixed:
 
993
 * @range: a #GtkRange
 
994
 *
 
995
 * This function is useful mainly for #GtkRange subclasses.
 
996
 *
 
997
 * See gtk_range_set_slider_size_fixed().
 
998
 *
 
999
 * Return value: whether the range's slider has a fixed size.
 
1000
 *
 
1001
 * Since: 2.20
 
1002
 **/
 
1003
gboolean
 
1004
gtk_range_get_slider_size_fixed (GtkRange *range)
 
1005
{
 
1006
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
 
1007
 
 
1008
  return range->slider_size_fixed;
 
1009
}
 
1010
 
 
1011
/**
 
1012
 * gtk_range_set_min_slider_size:
 
1013
 * @range: a #GtkRange
 
1014
 * @min_size: The slider's minimum size
 
1015
 *
 
1016
 * Sets the minimum size of the range's slider.
 
1017
 *
 
1018
 * This function is useful mainly for #GtkRange subclasses.
 
1019
 *
 
1020
 * Since: 2.20
 
1021
 **/
 
1022
void
 
1023
gtk_range_set_min_slider_size (GtkRange *range,
 
1024
                               gboolean  min_size)
 
1025
{
 
1026
  g_return_if_fail (GTK_IS_RANGE (range));
 
1027
  g_return_if_fail (min_size > 0);
 
1028
 
 
1029
  if (min_size != range->min_slider_size)
 
1030
    {
 
1031
      range->min_slider_size = min_size;
 
1032
 
 
1033
      range->need_recalc = TRUE;
 
1034
      gtk_range_calc_layout (range, range->adjustment->value);
 
1035
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
1036
    }
 
1037
}
 
1038
 
 
1039
/**
 
1040
 * gtk_range_get_min_slider_size:
 
1041
 * @range: a #GtkRange
 
1042
 *
 
1043
 * This function is useful mainly for #GtkRange subclasses.
 
1044
 *
 
1045
 * See gtk_range_set_min_slider_size().
 
1046
 *
 
1047
 * Return value: The minimum size of the range's slider.
 
1048
 *
 
1049
 * Since: 2.20
 
1050
 **/
 
1051
gint
 
1052
gtk_range_get_min_slider_size (GtkRange *range)
 
1053
{
 
1054
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
 
1055
 
 
1056
  return range->min_slider_size;
 
1057
}
 
1058
 
 
1059
/**
 
1060
 * gtk_range_get_range_rect:
 
1061
 * @range: a #GtkRange
 
1062
 * @range_rect: (out): return location for the range rectangle
 
1063
 *
 
1064
 * This function returns the area that contains the range's trough
 
1065
 * and its steppers, in widget->window coordinates.
 
1066
 *
 
1067
 * This function is useful mainly for #GtkRange subclasses.
 
1068
 *
 
1069
 * Since: 2.20
 
1070
 **/
 
1071
void
 
1072
gtk_range_get_range_rect (GtkRange     *range,
 
1073
                          GdkRectangle *range_rect)
 
1074
{
 
1075
  g_return_if_fail (GTK_IS_RANGE (range));
 
1076
  g_return_if_fail (range_rect != NULL);
 
1077
 
 
1078
  gtk_range_calc_layout (range, range->adjustment->value);
 
1079
 
 
1080
  *range_rect = range->range_rect;
 
1081
}
 
1082
 
 
1083
/**
 
1084
 * gtk_range_get_slider_range:
 
1085
 * @range: a #GtkRange
 
1086
 * @slider_start: (out) (allow-none): return location for the slider's
 
1087
 *     start, or %NULL
 
1088
 * @slider_end: (out) (allow-none): return location for the slider's
 
1089
 *     end, or %NULL
 
1090
 *
 
1091
 * This function returns sliders range along the long dimension,
 
1092
 * in widget->window coordinates.
 
1093
 *
 
1094
 * This function is useful mainly for #GtkRange subclasses.
 
1095
 *
 
1096
 * Since: 2.20
 
1097
 **/
 
1098
void
 
1099
gtk_range_get_slider_range (GtkRange *range,
 
1100
                            gint     *slider_start,
 
1101
                            gint     *slider_end)
 
1102
{
 
1103
  g_return_if_fail (GTK_IS_RANGE (range));
 
1104
 
 
1105
  gtk_range_calc_layout (range, range->adjustment->value);
 
1106
 
 
1107
  if (slider_start)
 
1108
    *slider_start = range->slider_start;
 
1109
 
 
1110
  if (slider_end)
 
1111
    *slider_end = range->slider_end;
 
1112
}
 
1113
 
 
1114
/**
 
1115
 * gtk_range_set_lower_stepper_sensitivity:
 
1116
 * @range:       a #GtkRange
 
1117
 * @sensitivity: the lower stepper's sensitivity policy.
 
1118
 *
 
1119
 * Sets the sensitivity policy for the stepper that points to the
 
1120
 * 'lower' end of the GtkRange's adjustment.
 
1121
 *
 
1122
 * Since: 2.10
 
1123
 **/
 
1124
void
 
1125
gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
 
1126
                                         GtkSensitivityType  sensitivity)
 
1127
{
 
1128
  g_return_if_fail (GTK_IS_RANGE (range));
 
1129
 
 
1130
  if (range->layout->lower_sensitivity != sensitivity)
 
1131
    {
 
1132
      range->layout->lower_sensitivity = sensitivity;
 
1133
 
 
1134
      range->need_recalc = TRUE;
 
1135
      gtk_range_calc_layout (range, range->adjustment->value);
 
1136
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
1137
 
 
1138
      g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
 
1139
    }
 
1140
}
 
1141
 
 
1142
/**
 
1143
 * gtk_range_get_lower_stepper_sensitivity:
 
1144
 * @range: a #GtkRange
 
1145
 *
 
1146
 * Gets the sensitivity policy for the stepper that points to the
 
1147
 * 'lower' end of the GtkRange's adjustment.
 
1148
 *
 
1149
 * Return value: The lower stepper's sensitivity policy.
 
1150
 *
 
1151
 * Since: 2.10
 
1152
 **/
 
1153
GtkSensitivityType
 
1154
gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
 
1155
{
 
1156
  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
 
1157
 
 
1158
  return range->layout->lower_sensitivity;
 
1159
}
 
1160
 
 
1161
/**
 
1162
 * gtk_range_set_upper_stepper_sensitivity:
 
1163
 * @range:       a #GtkRange
 
1164
 * @sensitivity: the upper stepper's sensitivity policy.
 
1165
 *
 
1166
 * Sets the sensitivity policy for the stepper that points to the
 
1167
 * 'upper' end of the GtkRange's adjustment.
 
1168
 *
 
1169
 * Since: 2.10
 
1170
 **/
 
1171
void
 
1172
gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
 
1173
                                         GtkSensitivityType  sensitivity)
 
1174
{
 
1175
  g_return_if_fail (GTK_IS_RANGE (range));
 
1176
 
 
1177
  if (range->layout->upper_sensitivity != sensitivity)
 
1178
    {
 
1179
      range->layout->upper_sensitivity = sensitivity;
 
1180
 
 
1181
      range->need_recalc = TRUE;
 
1182
      gtk_range_calc_layout (range, range->adjustment->value);
 
1183
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
1184
 
 
1185
      g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
 
1186
    }
 
1187
}
 
1188
 
 
1189
/**
 
1190
 * gtk_range_get_upper_stepper_sensitivity:
 
1191
 * @range: a #GtkRange
 
1192
 *
 
1193
 * Gets the sensitivity policy for the stepper that points to the
 
1194
 * 'upper' end of the GtkRange's adjustment.
 
1195
 *
 
1196
 * Return value: The upper stepper's sensitivity policy.
 
1197
 *
 
1198
 * Since: 2.10
 
1199
 **/
 
1200
GtkSensitivityType
 
1201
gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
 
1202
{
 
1203
  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_SENSITIVITY_AUTO);
 
1204
 
 
1205
  return range->layout->upper_sensitivity;
 
1206
}
 
1207
 
 
1208
/**
 
1209
 * gtk_range_set_increments:
 
1210
 * @range: a #GtkRange
 
1211
 * @step: step size
 
1212
 * @page: page size
 
1213
 *
 
1214
 * Sets the step and page sizes for the range.
 
1215
 * The step size is used when the user clicks the #GtkScrollbar
 
1216
 * arrows or moves #GtkScale via arrow keys. The page size
 
1217
 * is used for example when moving via Page Up or Page Down keys.
 
1218
 **/
 
1219
void
 
1220
gtk_range_set_increments (GtkRange *range,
 
1221
                          gdouble   step,
 
1222
                          gdouble   page)
 
1223
{
 
1224
  g_return_if_fail (GTK_IS_RANGE (range));
 
1225
 
 
1226
  range->adjustment->step_increment = step;
 
1227
  range->adjustment->page_increment = page;
 
1228
 
 
1229
  gtk_adjustment_changed (range->adjustment);
 
1230
}
 
1231
 
 
1232
/**
 
1233
 * gtk_range_set_range:
 
1234
 * @range: a #GtkRange
 
1235
 * @min: minimum range value
 
1236
 * @max: maximum range value
 
1237
 * 
 
1238
 * Sets the allowable values in the #GtkRange, and clamps the range
 
1239
 * value to be between @min and @max. (If the range has a non-zero
 
1240
 * page size, it is clamped between @min and @max - page-size.)
 
1241
 **/
 
1242
void
 
1243
gtk_range_set_range (GtkRange *range,
 
1244
                     gdouble   min,
 
1245
                     gdouble   max)
 
1246
{
 
1247
  gdouble value;
 
1248
  
 
1249
  g_return_if_fail (GTK_IS_RANGE (range));
 
1250
  g_return_if_fail (min < max);
 
1251
  
 
1252
  range->adjustment->lower = min;
 
1253
  range->adjustment->upper = max;
 
1254
 
 
1255
  value = range->adjustment->value;
 
1256
 
 
1257
  if (range->layout->restrict_to_fill_level)
 
1258
    value = MIN (value, MAX (range->adjustment->lower,
 
1259
                             range->layout->fill_level));
 
1260
 
 
1261
  value = CLAMP (value, range->adjustment->lower,
 
1262
                 (range->adjustment->upper - range->adjustment->page_size));
 
1263
 
 
1264
  gtk_adjustment_set_value (range->adjustment, value);
 
1265
  gtk_adjustment_changed (range->adjustment);
 
1266
}
 
1267
 
 
1268
/**
 
1269
 * gtk_range_set_value:
 
1270
 * @range: a #GtkRange
 
1271
 * @value: new value of the range
 
1272
 *
 
1273
 * Sets the current value of the range; if the value is outside the
 
1274
 * minimum or maximum range values, it will be clamped to fit inside
 
1275
 * them. The range emits the #GtkRange::value-changed signal if the 
 
1276
 * value changes.
 
1277
 **/
 
1278
void
 
1279
gtk_range_set_value (GtkRange *range,
 
1280
                     gdouble   value)
 
1281
{
 
1282
  g_return_if_fail (GTK_IS_RANGE (range));
 
1283
 
 
1284
  if (range->layout->restrict_to_fill_level)
 
1285
    value = MIN (value, MAX (range->adjustment->lower,
 
1286
                             range->layout->fill_level));
 
1287
 
 
1288
  value = CLAMP (value, range->adjustment->lower,
 
1289
                 (range->adjustment->upper - range->adjustment->page_size));
 
1290
 
 
1291
  gtk_adjustment_set_value (range->adjustment, value);
 
1292
}
 
1293
 
 
1294
/**
 
1295
 * gtk_range_get_value:
 
1296
 * @range: a #GtkRange
 
1297
 * 
 
1298
 * Gets the current value of the range.
 
1299
 * 
 
1300
 * Return value: current value of the range.
 
1301
 **/
 
1302
gdouble
 
1303
gtk_range_get_value (GtkRange *range)
 
1304
{
 
1305
  g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
 
1306
 
 
1307
  return range->adjustment->value;
 
1308
}
 
1309
 
 
1310
/**
 
1311
 * gtk_range_set_show_fill_level:
 
1312
 * @range:           A #GtkRange
 
1313
 * @show_fill_level: Whether a fill level indicator graphics is shown.
 
1314
 *
 
1315
 * Sets whether a graphical fill level is show on the trough. See
 
1316
 * gtk_range_set_fill_level() for a general description of the fill
 
1317
 * level concept.
 
1318
 *
 
1319
 * Since: 2.12
 
1320
 **/
 
1321
void
 
1322
gtk_range_set_show_fill_level (GtkRange *range,
 
1323
                               gboolean  show_fill_level)
 
1324
{
 
1325
  g_return_if_fail (GTK_IS_RANGE (range));
 
1326
 
 
1327
  show_fill_level = show_fill_level ? TRUE : FALSE;
 
1328
 
 
1329
  if (show_fill_level != range->layout->show_fill_level)
 
1330
    {
 
1331
      range->layout->show_fill_level = show_fill_level;
 
1332
      g_object_notify (G_OBJECT (range), "show-fill-level");
 
1333
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
1334
    }
 
1335
}
 
1336
 
 
1337
/**
 
1338
 * gtk_range_get_show_fill_level:
 
1339
 * @range: A #GtkRange
 
1340
 *
 
1341
 * Gets whether the range displays the fill level graphically.
 
1342
 *
 
1343
 * Return value: %TRUE if @range shows the fill level.
 
1344
 *
 
1345
 * Since: 2.12
 
1346
 **/
 
1347
gboolean
 
1348
gtk_range_get_show_fill_level (GtkRange *range)
 
1349
{
 
1350
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
 
1351
 
 
1352
  return range->layout->show_fill_level;
 
1353
}
 
1354
 
 
1355
/**
 
1356
 * gtk_range_set_restrict_to_fill_level:
 
1357
 * @range:                  A #GtkRange
 
1358
 * @restrict_to_fill_level: Whether the fill level restricts slider movement.
 
1359
 *
 
1360
 * Sets whether the slider is restricted to the fill level. See
 
1361
 * gtk_range_set_fill_level() for a general description of the fill
 
1362
 * level concept.
 
1363
 *
 
1364
 * Since: 2.12
 
1365
 **/
 
1366
void
 
1367
gtk_range_set_restrict_to_fill_level (GtkRange *range,
 
1368
                                      gboolean  restrict_to_fill_level)
 
1369
{
 
1370
  g_return_if_fail (GTK_IS_RANGE (range));
 
1371
 
 
1372
  restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
 
1373
 
 
1374
  if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
 
1375
    {
 
1376
      range->layout->restrict_to_fill_level = restrict_to_fill_level;
 
1377
      g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
 
1378
 
 
1379
      gtk_range_set_value (range, gtk_range_get_value (range));
 
1380
    }
 
1381
}
 
1382
 
 
1383
/**
 
1384
 * gtk_range_get_restrict_to_fill_level:
 
1385
 * @range: A #GtkRange
 
1386
 *
 
1387
 * Gets whether the range is restricted to the fill level.
 
1388
 *
 
1389
 * Return value: %TRUE if @range is restricted to the fill level.
 
1390
 *
 
1391
 * Since: 2.12
 
1392
 **/
 
1393
gboolean
 
1394
gtk_range_get_restrict_to_fill_level (GtkRange *range)
 
1395
{
 
1396
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
 
1397
 
 
1398
  return range->layout->restrict_to_fill_level;
 
1399
}
 
1400
 
 
1401
/**
 
1402
 * gtk_range_set_fill_level:
 
1403
 * @range: a #GtkRange
 
1404
 * @fill_level: the new position of the fill level indicator
 
1405
 *
 
1406
 * Set the new position of the fill level indicator.
 
1407
 *
 
1408
 * The "fill level" is probably best described by its most prominent
 
1409
 * use case, which is an indicator for the amount of pre-buffering in
 
1410
 * a streaming media player. In that use case, the value of the range
 
1411
 * would indicate the current play position, and the fill level would
 
1412
 * be the position up to which the file/stream has been downloaded.
 
1413
 *
 
1414
 * This amount of prebuffering can be displayed on the range's trough
 
1415
 * and is themeable separately from the trough. To enable fill level
 
1416
 * display, use gtk_range_set_show_fill_level(). The range defaults
 
1417
 * to not showing the fill level.
 
1418
 *
 
1419
 * Additionally, it's possible to restrict the range's slider position
 
1420
 * to values which are smaller than the fill level. This is controller
 
1421
 * by gtk_range_set_restrict_to_fill_level() and is by default
 
1422
 * enabled.
 
1423
 *
 
1424
 * Since: 2.12
 
1425
 **/
 
1426
void
 
1427
gtk_range_set_fill_level (GtkRange *range,
 
1428
                          gdouble   fill_level)
 
1429
{
 
1430
  g_return_if_fail (GTK_IS_RANGE (range));
 
1431
 
 
1432
  if (fill_level != range->layout->fill_level)
 
1433
    {
 
1434
      range->layout->fill_level = fill_level;
 
1435
      g_object_notify (G_OBJECT (range), "fill-level");
 
1436
 
 
1437
      if (range->layout->show_fill_level)
 
1438
        gtk_widget_queue_draw (GTK_WIDGET (range));
 
1439
 
 
1440
      if (range->layout->restrict_to_fill_level)
 
1441
        gtk_range_set_value (range, gtk_range_get_value (range));
 
1442
    }
 
1443
}
 
1444
 
 
1445
/**
 
1446
 * gtk_range_get_fill_level:
 
1447
 * @range: A #GtkRange
 
1448
 *
 
1449
 * Gets the current position of the fill level indicator.
 
1450
 *
 
1451
 * Return value: The current fill level
 
1452
 *
 
1453
 * Since: 2.12
 
1454
 **/
 
1455
gdouble
 
1456
gtk_range_get_fill_level (GtkRange *range)
 
1457
{
 
1458
  g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
 
1459
 
 
1460
  return range->layout->fill_level;
 
1461
}
 
1462
 
 
1463
static gboolean
 
1464
should_invert (GtkRange *range)
 
1465
{  
 
1466
  if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
 
1467
    return
 
1468
      (range->inverted && !range->flippable) ||
 
1469
      (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
 
1470
      (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
 
1471
  else
 
1472
    return range->inverted;
 
1473
}
 
1474
 
 
1475
static void
 
1476
gtk_range_destroy (GtkObject *object)
 
1477
{
 
1478
  GtkRange *range = GTK_RANGE (object);
 
1479
 
 
1480
  gtk_range_remove_step_timer (range);
 
1481
  gtk_range_remove_update_timer (range);
 
1482
 
 
1483
  if (range->layout->repaint_id)
 
1484
    g_source_remove (range->layout->repaint_id);
 
1485
  range->layout->repaint_id = 0;
 
1486
 
 
1487
  if (range->adjustment)
 
1488
    {
 
1489
      g_signal_handlers_disconnect_by_func (range->adjustment,
 
1490
                                            gtk_range_adjustment_changed,
 
1491
                                            range);
 
1492
      g_signal_handlers_disconnect_by_func (range->adjustment,
 
1493
                                            gtk_range_adjustment_value_changed,
 
1494
                                            range);
 
1495
      g_object_unref (range->adjustment);
 
1496
      range->adjustment = NULL;
 
1497
    }
 
1498
 
 
1499
  if (range->layout->n_marks)
 
1500
    {
 
1501
      g_free (range->layout->marks);
 
1502
      range->layout->marks = NULL;
 
1503
      g_free (range->layout->mark_pos);
 
1504
      range->layout->mark_pos = NULL;
 
1505
      range->layout->n_marks = 0;
 
1506
    }
 
1507
 
 
1508
  GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
 
1509
}
 
1510
 
 
1511
static void
 
1512
gtk_range_size_request (GtkWidget      *widget,
 
1513
                        GtkRequisition *requisition)
 
1514
{
 
1515
  GtkRange *range;
 
1516
  gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
 
1517
  GdkRectangle range_rect;
 
1518
  GtkBorder border;
 
1519
  
 
1520
  range = GTK_RANGE (widget);
 
1521
  
 
1522
  gtk_range_get_props (range,
 
1523
                       &slider_width, &stepper_size,
 
1524
                       &focus_width, &trough_border,
 
1525
                       &stepper_spacing, NULL,
 
1526
                       NULL, NULL);
 
1527
 
 
1528
  gtk_range_calc_request (range, 
 
1529
                          slider_width, stepper_size,
 
1530
                          focus_width, trough_border, stepper_spacing,
 
1531
                          &range_rect, &border, NULL, NULL, NULL, NULL);
 
1532
 
 
1533
  requisition->width = range_rect.width + border.left + border.right;
 
1534
  requisition->height = range_rect.height + border.top + border.bottom;
 
1535
}
 
1536
 
 
1537
static void
 
1538
gtk_range_size_allocate (GtkWidget     *widget,
 
1539
                         GtkAllocation *allocation)
 
1540
{
 
1541
  GtkRange *range;
 
1542
 
 
1543
  range = GTK_RANGE (widget);
 
1544
 
 
1545
  widget->allocation = *allocation;
 
1546
  
 
1547
  range->layout->recalc_marks = TRUE;
 
1548
 
 
1549
  range->need_recalc = TRUE;
 
1550
  gtk_range_calc_layout (range, range->adjustment->value);
 
1551
 
 
1552
  if (gtk_widget_get_realized (widget))
 
1553
    gdk_window_move_resize (range->event_window,
 
1554
                            widget->allocation.x,
 
1555
                            widget->allocation.y,
 
1556
                            widget->allocation.width,
 
1557
                            widget->allocation.height);
 
1558
}
 
1559
 
 
1560
static void
 
1561
gtk_range_realize (GtkWidget *widget)
 
1562
{
 
1563
  GtkRange *range;
 
1564
  GdkWindowAttr attributes;
 
1565
  gint attributes_mask;  
 
1566
 
 
1567
  range = GTK_RANGE (widget);
 
1568
 
 
1569
  gtk_range_calc_layout (range, range->adjustment->value);
 
1570
  
 
1571
  gtk_widget_set_realized (widget, TRUE);
 
1572
 
 
1573
  widget->window = gtk_widget_get_parent_window (widget);
 
1574
  g_object_ref (widget->window);
 
1575
  
 
1576
  attributes.window_type = GDK_WINDOW_CHILD;
 
1577
  attributes.x = widget->allocation.x;
 
1578
  attributes.y = widget->allocation.y;
 
1579
  attributes.width = widget->allocation.width;
 
1580
  attributes.height = widget->allocation.height;
 
1581
  attributes.wclass = GDK_INPUT_ONLY;
 
1582
  attributes.event_mask = gtk_widget_get_events (widget);
 
1583
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
 
1584
                            GDK_BUTTON_RELEASE_MASK |
 
1585
                            GDK_ENTER_NOTIFY_MASK |
 
1586
                            GDK_LEAVE_NOTIFY_MASK |
 
1587
                            GDK_POINTER_MOTION_MASK |
 
1588
                            GDK_POINTER_MOTION_HINT_MASK);
 
1589
 
 
1590
  attributes_mask = GDK_WA_X | GDK_WA_Y;
 
1591
 
 
1592
  range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
 
1593
                                        &attributes, attributes_mask);
 
1594
  gdk_window_set_user_data (range->event_window, range);
 
1595
 
 
1596
  widget->style = gtk_style_attach (widget->style, widget->window);
 
1597
}
 
1598
 
 
1599
static void
 
1600
gtk_range_unrealize (GtkWidget *widget)
 
1601
{
 
1602
  GtkRange *range = GTK_RANGE (widget);
 
1603
 
 
1604
  gtk_range_remove_step_timer (range);
 
1605
  gtk_range_remove_update_timer (range);
 
1606
  
 
1607
  gdk_window_set_user_data (range->event_window, NULL);
 
1608
  gdk_window_destroy (range->event_window);
 
1609
  range->event_window = NULL;
 
1610
 
 
1611
  GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
 
1612
}
 
1613
 
 
1614
static void
 
1615
gtk_range_map (GtkWidget *widget)
 
1616
{
 
1617
  GtkRange *range = GTK_RANGE (widget);
 
1618
  
 
1619
  gdk_window_show (range->event_window);
 
1620
 
 
1621
  GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
 
1622
}
 
1623
 
 
1624
static void
 
1625
gtk_range_unmap (GtkWidget *widget)
 
1626
{
 
1627
  GtkRange *range = GTK_RANGE (widget);
 
1628
    
 
1629
  stop_scrolling (range);
 
1630
 
 
1631
  gdk_window_hide (range->event_window);
 
1632
 
 
1633
  GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
 
1634
}
 
1635
 
 
1636
static const gchar *
 
1637
gtk_range_get_slider_detail (GtkRange *range)
 
1638
{
 
1639
  const gchar *slider_detail;
 
1640
 
 
1641
  if (range->layout->slider_detail_quark)
 
1642
    return g_quark_to_string (range->layout->slider_detail_quark);
 
1643
 
 
1644
  slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
 
1645
 
 
1646
  if (slider_detail && slider_detail[0] == 'X')
 
1647
    {
 
1648
      gchar *detail = g_strdup (slider_detail);
 
1649
 
 
1650
      detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
 
1651
 
 
1652
      range->layout->slider_detail_quark = g_quark_from_string (detail);
 
1653
 
 
1654
      g_free (detail);
 
1655
 
 
1656
      return g_quark_to_string (range->layout->slider_detail_quark);
 
1657
    }
 
1658
 
 
1659
  return slider_detail;
 
1660
}
 
1661
 
 
1662
static const gchar *
 
1663
gtk_range_get_stepper_detail (GtkRange *range,
 
1664
                              Stepper   stepper)
 
1665
{
 
1666
  const gchar *stepper_detail;
 
1667
  gboolean need_orientation;
 
1668
  gboolean need_position;
 
1669
 
 
1670
  if (range->layout->stepper_detail_quark[stepper])
 
1671
    return g_quark_to_string (range->layout->stepper_detail_quark[stepper]);
 
1672
 
 
1673
  stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
 
1674
 
 
1675
  need_orientation = stepper_detail && stepper_detail[0] == 'X';
 
1676
 
 
1677
  gtk_widget_style_get (GTK_WIDGET (range),
 
1678
                        "stepper-position-details", &need_position,
 
1679
                        NULL);
 
1680
 
 
1681
  if (need_orientation || need_position)
 
1682
    {
 
1683
      gchar *detail;
 
1684
      const gchar *position = NULL;
 
1685
 
 
1686
      if (need_position)
 
1687
        {
 
1688
          switch (stepper)
 
1689
            {
 
1690
            case STEPPER_A:
 
1691
              position = "_start";
 
1692
              break;
 
1693
            case STEPPER_B:
 
1694
              if (range->has_stepper_a)
 
1695
                position = "_middle";
 
1696
              else
 
1697
                position = "_start";
 
1698
              break;
 
1699
            case STEPPER_C:
 
1700
              if (range->has_stepper_d)
 
1701
                position = "_middle";
 
1702
              else
 
1703
                position = "_end";
 
1704
              break;
 
1705
            case STEPPER_D:
 
1706
              position = "_end";
 
1707
              break;
 
1708
            default:
 
1709
              g_assert_not_reached ();
 
1710
            }
 
1711
        }
 
1712
 
 
1713
      detail = g_strconcat (stepper_detail, position, NULL);
 
1714
 
 
1715
      if (need_orientation)
 
1716
        detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
 
1717
 
 
1718
      range->layout->stepper_detail_quark[stepper] = g_quark_from_string (detail);
 
1719
 
 
1720
      g_free (detail);
 
1721
 
 
1722
      return g_quark_to_string (range->layout->stepper_detail_quark[stepper]);
 
1723
    }
 
1724
 
 
1725
  return stepper_detail;
 
1726
}
 
1727
 
 
1728
static void
 
1729
draw_stepper (GtkRange     *range,
 
1730
              Stepper       stepper,
 
1731
              GtkArrowType  arrow_type,
 
1732
              gboolean      clicked,
 
1733
              gboolean      prelighted,
 
1734
              GdkRectangle *area)
 
1735
{
 
1736
  GtkStateType state_type;
 
1737
  GtkShadowType shadow_type;
 
1738
  GdkRectangle intersection;
 
1739
  GtkWidget *widget = GTK_WIDGET (range);
 
1740
  gfloat arrow_scaling;
 
1741
  GdkRectangle *rect;
 
1742
  gint arrow_x;
 
1743
  gint arrow_y;
 
1744
  gint arrow_width;
 
1745
  gint arrow_height;
 
1746
  gboolean arrow_sensitive = TRUE;
 
1747
 
 
1748
  switch (stepper)
 
1749
    {
 
1750
    case STEPPER_A:
 
1751
      rect = &range->layout->stepper_a;
 
1752
      break;
 
1753
    case STEPPER_B:
 
1754
      rect = &range->layout->stepper_b;
 
1755
      break;
 
1756
    case STEPPER_C:
 
1757
      rect = &range->layout->stepper_c;
 
1758
      break;
 
1759
    case STEPPER_D:
 
1760
      rect = &range->layout->stepper_d;
 
1761
      break;
 
1762
    default:
 
1763
      g_assert_not_reached ();
 
1764
    };
 
1765
 
 
1766
  /* More to get the right clip region than for efficiency */
 
1767
  if (!gdk_rectangle_intersect (area, rect, &intersection))
 
1768
    return;
 
1769
 
 
1770
  intersection.x += widget->allocation.x;
 
1771
  intersection.y += widget->allocation.y;
 
1772
 
 
1773
  if ((!range->inverted && (arrow_type == GTK_ARROW_DOWN ||
 
1774
                            arrow_type == GTK_ARROW_RIGHT)) ||
 
1775
      (range->inverted  && (arrow_type == GTK_ARROW_UP ||
 
1776
                            arrow_type == GTK_ARROW_LEFT)))
 
1777
    {
 
1778
      arrow_sensitive = range->layout->upper_sensitive;
 
1779
    }
 
1780
  else
 
1781
    {
 
1782
      arrow_sensitive = range->layout->lower_sensitive;
 
1783
    }
 
1784
 
 
1785
  if (!gtk_widget_is_sensitive (GTK_WIDGET (range)) || !arrow_sensitive)
 
1786
    state_type = GTK_STATE_INSENSITIVE;
 
1787
  else if (clicked)
 
1788
    state_type = GTK_STATE_ACTIVE;
 
1789
  else if (prelighted)
 
1790
    state_type = GTK_STATE_PRELIGHT;
 
1791
  else 
 
1792
    state_type = GTK_STATE_NORMAL;
 
1793
 
 
1794
  if (clicked && arrow_sensitive)
 
1795
    shadow_type = GTK_SHADOW_IN;
 
1796
  else
 
1797
    shadow_type = GTK_SHADOW_OUT;
 
1798
 
 
1799
  gtk_paint_box (widget->style,
 
1800
                 widget->window,
 
1801
                 state_type, shadow_type,
 
1802
                 &intersection, widget,
 
1803
                 gtk_range_get_stepper_detail (range, stepper),
 
1804
                 widget->allocation.x + rect->x,
 
1805
                 widget->allocation.y + rect->y,
 
1806
                 rect->width,
 
1807
                 rect->height);
 
1808
 
 
1809
  gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
 
1810
 
 
1811
  arrow_width = rect->width * arrow_scaling;
 
1812
  arrow_height = rect->height * arrow_scaling;
 
1813
  arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
 
1814
  arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
 
1815
 
 
1816
  if (clicked && arrow_sensitive)
 
1817
    {
 
1818
      gint arrow_displacement_x;
 
1819
      gint arrow_displacement_y;
 
1820
 
 
1821
      gtk_range_get_props (GTK_RANGE (widget),
 
1822
                           NULL, NULL, NULL, NULL, NULL, NULL,
 
1823
                           &arrow_displacement_x, &arrow_displacement_y);
 
1824
      
 
1825
      arrow_x += arrow_displacement_x;
 
1826
      arrow_y += arrow_displacement_y;
 
1827
    }
 
1828
  
 
1829
  gtk_paint_arrow (widget->style,
 
1830
                   widget->window,
 
1831
                   state_type, shadow_type,
 
1832
                   &intersection, widget,
 
1833
                   gtk_range_get_stepper_detail (range, stepper),
 
1834
                   arrow_type,
 
1835
                   TRUE,
 
1836
                   arrow_x, arrow_y, arrow_width, arrow_height);
 
1837
}
 
1838
 
 
1839
static gboolean
 
1840
gtk_range_expose (GtkWidget      *widget,
 
1841
                  GdkEventExpose *event)
 
1842
{
 
1843
  GtkRange *range = GTK_RANGE (widget);
 
1844
  gboolean sensitive;
 
1845
  GtkStateType state;
 
1846
  GtkShadowType shadow_type;
 
1847
  GdkRectangle expose_area;     /* Relative to widget->allocation */
 
1848
  GdkRectangle area;
 
1849
  gint focus_line_width = 0;
 
1850
  gint focus_padding = 0;
 
1851
  gboolean touchscreen;
 
1852
 
 
1853
  g_object_get (gtk_widget_get_settings (widget),
 
1854
                "gtk-touchscreen-mode", &touchscreen,
 
1855
                NULL);
 
1856
  if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
 
1857
    gtk_widget_style_get (GTK_WIDGET (range),
 
1858
                          "focus-line-width", &focus_line_width,
 
1859
                          "focus-padding", &focus_padding,
 
1860
                          NULL);
 
1861
 
 
1862
  /* we're now exposing, so there's no need to force early repaints */
 
1863
  if (range->layout->repaint_id)
 
1864
    g_source_remove (range->layout->repaint_id);
 
1865
  range->layout->repaint_id = 0;
 
1866
 
 
1867
  expose_area = event->area;
 
1868
  expose_area.x -= widget->allocation.x;
 
1869
  expose_area.y -= widget->allocation.y;
 
1870
  
 
1871
  gtk_range_calc_marks (range);
 
1872
  gtk_range_calc_layout (range, range->adjustment->value);
 
1873
 
 
1874
  sensitive = gtk_widget_is_sensitive (widget);
 
1875
 
 
1876
  /* Just to be confusing, we draw the trough for the whole
 
1877
   * range rectangle, not the trough rectangle (the trough
 
1878
   * rectangle is just for hit detection)
 
1879
   */
 
1880
  /* The gdk_rectangle_intersect is more to get the right
 
1881
   * clip region (limited to range_rect) than for efficiency
 
1882
   */
 
1883
  if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
 
1884
                               &area))
 
1885
    {
 
1886
      gint     x      = (widget->allocation.x + range->range_rect.x +
 
1887
                         focus_line_width + focus_padding);
 
1888
      gint     y      = (widget->allocation.y + range->range_rect.y +
 
1889
                         focus_line_width + focus_padding);
 
1890
      gint     width  = (range->range_rect.width -
 
1891
                         2 * (focus_line_width + focus_padding));
 
1892
      gint     height = (range->range_rect.height -
 
1893
                         2 * (focus_line_width + focus_padding));
 
1894
      gboolean trough_side_details;
 
1895
      gboolean trough_under_steppers;
 
1896
      gint     stepper_size;
 
1897
      gint     stepper_spacing;
 
1898
 
 
1899
      area.x += widget->allocation.x;
 
1900
      area.y += widget->allocation.y;
 
1901
 
 
1902
      gtk_widget_style_get (GTK_WIDGET (range),
 
1903
                            "trough-side-details",   &trough_side_details,
 
1904
                            "trough-under-steppers", &trough_under_steppers,
 
1905
                            "stepper-size",          &stepper_size,
 
1906
                            "stepper-spacing",       &stepper_spacing,
 
1907
                            NULL);
 
1908
 
 
1909
      if (stepper_spacing > 0)
 
1910
        trough_under_steppers = FALSE;
 
1911
 
 
1912
      if (! trough_under_steppers)
 
1913
        {
 
1914
          gint offset  = 0;
 
1915
          gint shorter = 0;
 
1916
 
 
1917
          if (range->has_stepper_a)
 
1918
            offset += stepper_size;
 
1919
 
 
1920
          if (range->has_stepper_b)
 
1921
            offset += stepper_size;
 
1922
 
 
1923
          shorter += offset;
 
1924
 
 
1925
          if (range->has_stepper_c)
 
1926
            shorter += stepper_size;
 
1927
 
 
1928
          if (range->has_stepper_d)
 
1929
            shorter += stepper_size;
 
1930
 
 
1931
          if (range->has_stepper_a || range->has_stepper_b)
 
1932
            {
 
1933
              offset  += stepper_spacing;
 
1934
              shorter += stepper_spacing;
 
1935
            }
 
1936
 
 
1937
          if (range->has_stepper_c || range->has_stepper_d)
 
1938
            {
 
1939
              shorter += stepper_spacing;
 
1940
            }
 
1941
 
 
1942
          if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
 
1943
            {
 
1944
              x     += offset;
 
1945
              width -= shorter;
 
1946
            }
 
1947
          else
 
1948
            {
 
1949
              y      += offset;
 
1950
              height -= shorter;
 
1951
            }
 
1952
        }
 
1953
 
 
1954
      if (! trough_side_details)
 
1955
        {
 
1956
          gtk_paint_box (widget->style,
 
1957
                         widget->window,
 
1958
                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
 
1959
                         GTK_SHADOW_IN,
 
1960
                         &area, GTK_WIDGET(range), "trough",
 
1961
                         x, y,
 
1962
                         width, height);
 
1963
        }
 
1964
      else
 
1965
        {
 
1966
          gint trough_change_pos_x = width;
 
1967
          gint trough_change_pos_y = height;
 
1968
 
 
1969
          if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
 
1970
            trough_change_pos_x = (range->layout->slider.x +
 
1971
                                   range->layout->slider.width / 2 -
 
1972
                                   (x - widget->allocation.x));
 
1973
          else
 
1974
            trough_change_pos_y = (range->layout->slider.y +
 
1975
                                   range->layout->slider.height / 2 -
 
1976
                                   (y - widget->allocation.y));
 
1977
 
 
1978
          gtk_paint_box (widget->style,
 
1979
                         widget->window,
 
1980
                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
 
1981
                         GTK_SHADOW_IN,
 
1982
                         &area, GTK_WIDGET (range),
 
1983
                         should_invert (range) ? "trough-upper" : "trough-lower",
 
1984
                         x, y,
 
1985
                         trough_change_pos_x, trough_change_pos_y);
 
1986
 
 
1987
          if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
 
1988
            trough_change_pos_y = 0;
 
1989
          else
 
1990
            trough_change_pos_x = 0;
 
1991
 
 
1992
          gtk_paint_box (widget->style,
 
1993
                         widget->window,
 
1994
                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
 
1995
                         GTK_SHADOW_IN,
 
1996
                         &area, GTK_WIDGET (range),
 
1997
                         should_invert (range) ? "trough-lower" : "trough-upper",
 
1998
                         x + trough_change_pos_x, y + trough_change_pos_y,
 
1999
                         width - trough_change_pos_x,
 
2000
                         height - trough_change_pos_y);
 
2001
        }
 
2002
 
 
2003
      if (range->layout->show_fill_level &&
 
2004
          range->adjustment->upper - range->adjustment->page_size -
 
2005
          range->adjustment->lower != 0)
 
2006
        {
 
2007
          gdouble  fill_level  = range->layout->fill_level;
 
2008
          gint     fill_x      = x;
 
2009
          gint     fill_y      = y;
 
2010
          gint     fill_width  = width;
 
2011
          gint     fill_height = height;
 
2012
          gchar   *fill_detail;
 
2013
 
 
2014
          fill_level = CLAMP (fill_level, range->adjustment->lower,
 
2015
                              range->adjustment->upper -
 
2016
                              range->adjustment->page_size);
 
2017
 
 
2018
          if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
 
2019
            {
 
2020
              fill_x     = widget->allocation.x + range->layout->trough.x;
 
2021
              fill_width = (range->layout->slider.width +
 
2022
                            (fill_level - range->adjustment->lower) /
 
2023
                            (range->adjustment->upper -
 
2024
                             range->adjustment->lower -
 
2025
                             range->adjustment->page_size) *
 
2026
                            (range->layout->trough.width -
 
2027
                             range->layout->slider.width));
 
2028
 
 
2029
              if (should_invert (range))
 
2030
                fill_x += range->layout->trough.width - fill_width;
 
2031
            }
 
2032
          else
 
2033
            {
 
2034
              fill_y      = widget->allocation.y + range->layout->trough.y;
 
2035
              fill_height = (range->layout->slider.height +
 
2036
                             (fill_level - range->adjustment->lower) /
 
2037
                             (range->adjustment->upper -
 
2038
                              range->adjustment->lower -
 
2039
                              range->adjustment->page_size) *
 
2040
                             (range->layout->trough.height -
 
2041
                              range->layout->slider.height));
 
2042
 
 
2043
              if (should_invert (range))
 
2044
                fill_y += range->layout->trough.height - fill_height;
 
2045
            }
 
2046
 
 
2047
          if (fill_level < range->adjustment->upper - range->adjustment->page_size)
 
2048
            fill_detail = "trough-fill-level-full";
 
2049
          else
 
2050
            fill_detail = "trough-fill-level";
 
2051
 
 
2052
          gtk_paint_box (widget->style,
 
2053
                         widget->window,
 
2054
                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
 
2055
                         GTK_SHADOW_OUT,
 
2056
                         &area, GTK_WIDGET (range), fill_detail,
 
2057
                         fill_x, fill_y,
 
2058
                         fill_width, fill_height);
 
2059
        }
 
2060
 
 
2061
      if (sensitive && gtk_widget_has_focus (widget))
 
2062
        gtk_paint_focus (widget->style, widget->window, gtk_widget_get_state (widget),
 
2063
                         &area, widget, "trough",
 
2064
                         widget->allocation.x + range->range_rect.x,
 
2065
                         widget->allocation.y + range->range_rect.y,
 
2066
                         range->range_rect.width,
 
2067
                         range->range_rect.height);
 
2068
    }
 
2069
 
 
2070
  shadow_type = GTK_SHADOW_OUT;
 
2071
 
 
2072
  if (!sensitive)
 
2073
    state = GTK_STATE_INSENSITIVE;
 
2074
  else if (!touchscreen && range->layout->mouse_location == MOUSE_SLIDER)
 
2075
    state = GTK_STATE_PRELIGHT;
 
2076
  else
 
2077
    state = GTK_STATE_NORMAL;
 
2078
 
 
2079
  if (range->layout->grab_location == MOUSE_SLIDER)
 
2080
    {
 
2081
      gboolean activate_slider;
 
2082
 
 
2083
      gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
 
2084
 
 
2085
      if (activate_slider)
 
2086
        {
 
2087
          state = GTK_STATE_ACTIVE;
 
2088
          shadow_type = GTK_SHADOW_IN;
 
2089
        }
 
2090
    }
 
2091
 
 
2092
  if (gdk_rectangle_intersect (&expose_area,
 
2093
                               &range->layout->slider,
 
2094
                               &area))
 
2095
    {
 
2096
      area.x += widget->allocation.x;
 
2097
      area.y += widget->allocation.y;
 
2098
      
 
2099
      gtk_paint_slider (widget->style,
 
2100
                        widget->window,
 
2101
                        state,
 
2102
                        shadow_type,
 
2103
                        &area,
 
2104
                        widget,
 
2105
                        gtk_range_get_slider_detail (range),
 
2106
                        widget->allocation.x + range->layout->slider.x,
 
2107
                        widget->allocation.y + range->layout->slider.y,
 
2108
                        range->layout->slider.width,
 
2109
                        range->layout->slider.height,
 
2110
                        range->orientation);
 
2111
    }
 
2112
  
 
2113
  if (range->has_stepper_a)
 
2114
    draw_stepper (range, STEPPER_A,
 
2115
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
 
2116
                  range->layout->grab_location == MOUSE_STEPPER_A,
 
2117
                  !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_A,
 
2118
                  &expose_area);
 
2119
 
 
2120
  if (range->has_stepper_b)
 
2121
    draw_stepper (range, STEPPER_B,
 
2122
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
 
2123
                  range->layout->grab_location == MOUSE_STEPPER_B,
 
2124
                  !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_B,
 
2125
                  &expose_area);
 
2126
 
 
2127
  if (range->has_stepper_c)
 
2128
    draw_stepper (range, STEPPER_C,
 
2129
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
 
2130
                  range->layout->grab_location == MOUSE_STEPPER_C,
 
2131
                  !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_C,
 
2132
                  &expose_area);
 
2133
 
 
2134
  if (range->has_stepper_d)
 
2135
    draw_stepper (range, STEPPER_D,
 
2136
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
 
2137
                  range->layout->grab_location == MOUSE_STEPPER_D,
 
2138
                  !touchscreen && range->layout->mouse_location == MOUSE_STEPPER_D,
 
2139
                  &expose_area);
 
2140
  
 
2141
  return FALSE;
 
2142
}
 
2143
 
 
2144
static void
 
2145
range_grab_add (GtkRange      *range,
 
2146
                MouseLocation  location,
 
2147
                gint           button)
 
2148
{
 
2149
  /* we don't actually gtk_grab, since a button is down */
 
2150
 
 
2151
  gtk_grab_add (GTK_WIDGET (range));
 
2152
  
 
2153
  range->layout->grab_location = location;
 
2154
  range->layout->grab_button = button;
 
2155
  
 
2156
  if (gtk_range_update_mouse_location (range))
 
2157
    gtk_widget_queue_draw (GTK_WIDGET (range));
 
2158
}
 
2159
 
 
2160
static void
 
2161
range_grab_remove (GtkRange *range)
 
2162
{
 
2163
  MouseLocation location;
 
2164
 
 
2165
  gtk_grab_remove (GTK_WIDGET (range));
 
2166
 
 
2167
  location = range->layout->grab_location; 
 
2168
  range->layout->grab_location = MOUSE_OUTSIDE;
 
2169
  range->layout->grab_button = 0;
 
2170
 
 
2171
  if (gtk_range_update_mouse_location (range) ||
 
2172
      location != MOUSE_OUTSIDE)
 
2173
    gtk_widget_queue_draw (GTK_WIDGET (range));
 
2174
}
 
2175
 
 
2176
static GtkScrollType
 
2177
range_get_scroll_for_grab (GtkRange      *range)
 
2178
 
2179
  gboolean invert;
 
2180
 
 
2181
  invert = should_invert (range);
 
2182
  switch (range->layout->grab_location)
 
2183
    {
 
2184
      /* Backward stepper */
 
2185
    case MOUSE_STEPPER_A:
 
2186
    case MOUSE_STEPPER_C:
 
2187
      switch (range->layout->grab_button)
 
2188
        {
 
2189
        case 1:
 
2190
          return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
 
2191
          break;
 
2192
        case 2:
 
2193
          return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
 
2194
          break;
 
2195
        case 3:
 
2196
          return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
 
2197
          break;
 
2198
        }
 
2199
      break;
 
2200
 
 
2201
      /* Forward stepper */
 
2202
    case MOUSE_STEPPER_B:
 
2203
    case MOUSE_STEPPER_D:
 
2204
      switch (range->layout->grab_button)
 
2205
        {
 
2206
        case 1:
 
2207
          return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
 
2208
          break;
 
2209
        case 2:
 
2210
          return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
 
2211
          break;
 
2212
        case 3:
 
2213
          return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
 
2214
          break;
 
2215
       }
 
2216
      break;
 
2217
 
 
2218
      /* In the trough */
 
2219
    case MOUSE_TROUGH:
 
2220
      {
 
2221
        if (range->trough_click_forward)
 
2222
          return GTK_SCROLL_PAGE_FORWARD;
 
2223
        else
 
2224
          return GTK_SCROLL_PAGE_BACKWARD;
 
2225
      }
 
2226
      break;
 
2227
 
 
2228
    case MOUSE_OUTSIDE:
 
2229
    case MOUSE_SLIDER:
 
2230
    case MOUSE_WIDGET:
 
2231
      break;
 
2232
    }
 
2233
 
 
2234
  return GTK_SCROLL_NONE;
 
2235
}
 
2236
 
 
2237
static gdouble
 
2238
coord_to_value (GtkRange *range,
 
2239
                gint      coord)
 
2240
{
 
2241
  gdouble frac;
 
2242
  gdouble value;
 
2243
  gint    trough_length;
 
2244
  gint    trough_start;
 
2245
  gint    slider_length;
 
2246
  gint    trough_border;
 
2247
  gint    trough_under_steppers;
 
2248
 
 
2249
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
 
2250
    {
 
2251
      trough_length = range->layout->trough.height;
 
2252
      trough_start  = range->layout->trough.y;
 
2253
      slider_length = range->layout->slider.height;
 
2254
    }
 
2255
  else
 
2256
    {
 
2257
      trough_length = range->layout->trough.width;
 
2258
      trough_start  = range->layout->trough.x;
 
2259
      slider_length = range->layout->slider.width;
 
2260
    }
 
2261
 
 
2262
  gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
 
2263
                       &trough_under_steppers, NULL, NULL);
 
2264
 
 
2265
  if (! trough_under_steppers)
 
2266
    {
 
2267
      trough_start += trough_border;
 
2268
      trough_length -= 2 * trough_border;
 
2269
    }
 
2270
 
 
2271
  if (trough_length == slider_length)
 
2272
    frac = 1.0;
 
2273
  else
 
2274
    frac = (MAX (0, coord - trough_start) /
 
2275
            (gdouble) (trough_length - slider_length));
 
2276
 
 
2277
  if (should_invert (range))
 
2278
    frac = 1.0 - frac;
 
2279
 
 
2280
  value = range->adjustment->lower + frac * (range->adjustment->upper -
 
2281
                                             range->adjustment->lower -
 
2282
                                             range->adjustment->page_size);
 
2283
 
 
2284
  return value;
 
2285
}
 
2286
 
 
2287
static gboolean
 
2288
gtk_range_key_press (GtkWidget   *widget,
 
2289
                     GdkEventKey *event)
 
2290
{
 
2291
  GtkRange *range = GTK_RANGE (widget);
 
2292
 
 
2293
  if (event->keyval == GDK_Escape &&
 
2294
      range->layout->grab_location != MOUSE_OUTSIDE)
 
2295
    {
 
2296
      stop_scrolling (range);
 
2297
 
 
2298
      update_slider_position (range,
 
2299
                              range->slide_initial_coordinate,
 
2300
                              range->slide_initial_coordinate);
 
2301
 
 
2302
      return TRUE;
 
2303
    }
 
2304
 
 
2305
  return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
 
2306
}
 
2307
 
 
2308
static gint
 
2309
gtk_range_button_press (GtkWidget      *widget,
 
2310
                        GdkEventButton *event)
 
2311
{
 
2312
  GtkRange *range = GTK_RANGE (widget);
 
2313
  
 
2314
  if (!gtk_widget_has_focus (widget))
 
2315
    gtk_widget_grab_focus (widget);
 
2316
 
 
2317
  /* ignore presses when we're already doing something else. */
 
2318
  if (range->layout->grab_location != MOUSE_OUTSIDE)
 
2319
    return FALSE;
 
2320
 
 
2321
  range->layout->mouse_x = event->x;
 
2322
  range->layout->mouse_y = event->y;
 
2323
  if (gtk_range_update_mouse_location (range))
 
2324
    gtk_widget_queue_draw (widget);
 
2325
    
 
2326
  if (range->layout->mouse_location == MOUSE_TROUGH  &&
 
2327
      event->button == 1)
 
2328
    {
 
2329
      /* button 1 steps by page increment, as with button 2 on a stepper
 
2330
       */
 
2331
      GtkScrollType scroll;
 
2332
      gdouble click_value;
 
2333
      
 
2334
      click_value = coord_to_value (range,
 
2335
                                    range->orientation == GTK_ORIENTATION_VERTICAL ?
 
2336
                                    event->y : event->x);
 
2337
      
 
2338
      range->trough_click_forward = click_value > range->adjustment->value;
 
2339
      range_grab_add (range, MOUSE_TROUGH, event->button);
 
2340
      
 
2341
      scroll = range_get_scroll_for_grab (range);
 
2342
      
 
2343
      gtk_range_add_step_timer (range, scroll);
 
2344
 
 
2345
      return TRUE;
 
2346
    }
 
2347
  else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
 
2348
            range->layout->mouse_location == MOUSE_STEPPER_B ||
 
2349
            range->layout->mouse_location == MOUSE_STEPPER_C ||
 
2350
            range->layout->mouse_location == MOUSE_STEPPER_D) &&
 
2351
           (event->button == 1 || event->button == 2 || event->button == 3))
 
2352
    {
 
2353
      GdkRectangle *stepper_area;
 
2354
      GtkScrollType scroll;
 
2355
      
 
2356
      range_grab_add (range, range->layout->mouse_location, event->button);
 
2357
 
 
2358
      stepper_area = get_area (range, range->layout->mouse_location);
 
2359
      gtk_widget_queue_draw_area (widget,
 
2360
                                  widget->allocation.x + stepper_area->x,
 
2361
                                  widget->allocation.y + stepper_area->y,
 
2362
                                  stepper_area->width,
 
2363
                                  stepper_area->height);
 
2364
 
 
2365
      scroll = range_get_scroll_for_grab (range);
 
2366
      if (scroll != GTK_SCROLL_NONE)
 
2367
        gtk_range_add_step_timer (range, scroll);
 
2368
      
 
2369
      return TRUE;
 
2370
    }
 
2371
  else if ((range->layout->mouse_location == MOUSE_TROUGH &&
 
2372
            event->button == 2) ||
 
2373
           range->layout->mouse_location == MOUSE_SLIDER)
 
2374
    {
 
2375
      gboolean need_value_update = FALSE;
 
2376
      gboolean activate_slider;
 
2377
 
 
2378
      /* Any button can be used to drag the slider, but you can start
 
2379
       * dragging the slider with a trough click using button 2;
 
2380
       * On button 2 press, we warp the slider to mouse position,
 
2381
       * then begin the slider drag.
 
2382
       */
 
2383
      if (event->button == 2)
 
2384
        {
 
2385
          gdouble slider_low_value, slider_high_value, new_value;
 
2386
          
 
2387
          slider_high_value =
 
2388
            coord_to_value (range,
 
2389
                            range->orientation == GTK_ORIENTATION_VERTICAL ?
 
2390
                            event->y : event->x);
 
2391
          slider_low_value =
 
2392
            coord_to_value (range,
 
2393
                            range->orientation == GTK_ORIENTATION_VERTICAL ?
 
2394
                            event->y - range->layout->slider.height :
 
2395
                            event->x - range->layout->slider.width);
 
2396
          
 
2397
          /* compute new value for warped slider */
 
2398
          new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
 
2399
 
 
2400
          /* recalc slider, so we can set slide_initial_slider_position
 
2401
           * properly
 
2402
           */
 
2403
          range->need_recalc = TRUE;
 
2404
          gtk_range_calc_layout (range, new_value);
 
2405
 
 
2406
          /* defer adjustment updates to update_slider_position() in order
 
2407
           * to keep pixel quantisation
 
2408
           */
 
2409
          need_value_update = TRUE;
 
2410
        }
 
2411
      
 
2412
      if (range->orientation == GTK_ORIENTATION_VERTICAL)
 
2413
        {
 
2414
          range->slide_initial_slider_position = range->layout->slider.y;
 
2415
          range->slide_initial_coordinate = event->y;
 
2416
        }
 
2417
      else
 
2418
        {
 
2419
          range->slide_initial_slider_position = range->layout->slider.x;
 
2420
          range->slide_initial_coordinate = event->x;
 
2421
        }
 
2422
 
 
2423
      range_grab_add (range, MOUSE_SLIDER, event->button);
 
2424
 
 
2425
      gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
 
2426
 
 
2427
      /* force a redraw, if the active slider is drawn differently to the
 
2428
       * prelight one
 
2429
       */
 
2430
      if (activate_slider)
 
2431
        gtk_widget_queue_draw (widget);
 
2432
 
 
2433
      if (need_value_update)
 
2434
        update_slider_position (range, event->x, event->y);
 
2435
 
 
2436
      return TRUE;
 
2437
    }
 
2438
  
 
2439
  return FALSE;
 
2440
}
 
2441
 
 
2442
/* During a slide, move the slider as required given new mouse position */
 
2443
static void
 
2444
update_slider_position (GtkRange *range,
 
2445
                        gint      mouse_x,
 
2446
                        gint      mouse_y)
 
2447
{
 
2448
  gint delta;
 
2449
  gint c;
 
2450
  gdouble new_value;
 
2451
  gboolean handled;
 
2452
  gdouble next_value;
 
2453
  gdouble mark_value;
 
2454
  gdouble mark_delta;
 
2455
  gint i;
 
2456
 
 
2457
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
 
2458
    delta = mouse_y - range->slide_initial_coordinate;
 
2459
  else
 
2460
    delta = mouse_x - range->slide_initial_coordinate;
 
2461
 
 
2462
  c = range->slide_initial_slider_position + delta;
 
2463
 
 
2464
  new_value = coord_to_value (range, c);
 
2465
  next_value = coord_to_value (range, c + 1);
 
2466
  mark_delta = fabs (next_value - new_value); 
 
2467
 
 
2468
  for (i = 0; i < range->layout->n_marks; i++)
 
2469
    {
 
2470
      mark_value = range->layout->marks[i];
 
2471
 
 
2472
      if (fabs (range->adjustment->value - mark_value) < 3 * mark_delta)
 
2473
        {
 
2474
          if (fabs (new_value - mark_value) < (range->slider_end - range->slider_start) * 0.5 * mark_delta)
 
2475
            {
 
2476
              new_value = mark_value;
 
2477
              break;
 
2478
            }
 
2479
        }
 
2480
    }  
 
2481
 
 
2482
  g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
 
2483
                 &handled);
 
2484
}
 
2485
 
 
2486
static void 
 
2487
stop_scrolling (GtkRange *range)
 
2488
{
 
2489
  range_grab_remove (range);
 
2490
  gtk_range_remove_step_timer (range);
 
2491
  /* Flush any pending discontinuous/delayed updates */
 
2492
  gtk_range_update_value (range);
 
2493
}
 
2494
 
 
2495
static gboolean
 
2496
gtk_range_grab_broken (GtkWidget          *widget,
 
2497
                       GdkEventGrabBroken *event)
 
2498
{
 
2499
  GtkRange *range = GTK_RANGE (widget);
 
2500
 
 
2501
  if (range->layout->grab_location != MOUSE_OUTSIDE)
 
2502
    {
 
2503
      if (range->layout->grab_location == MOUSE_SLIDER)
 
2504
        update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
 
2505
      
 
2506
      stop_scrolling (range);
 
2507
      
 
2508
      return TRUE;
 
2509
    }
 
2510
  
 
2511
  return FALSE;
 
2512
}
 
2513
 
 
2514
static gint
 
2515
gtk_range_button_release (GtkWidget      *widget,
 
2516
                          GdkEventButton *event)
 
2517
{
 
2518
  GtkRange *range = GTK_RANGE (widget);
 
2519
 
 
2520
  if (event->window == range->event_window)
 
2521
    {
 
2522
      range->layout->mouse_x = event->x;
 
2523
      range->layout->mouse_y = event->y;
 
2524
    }
 
2525
  else
 
2526
    {
 
2527
      gdk_window_get_pointer (range->event_window,
 
2528
                              &range->layout->mouse_x,
 
2529
                              &range->layout->mouse_y,
 
2530
                              NULL);
 
2531
    }
 
2532
  
 
2533
  if (range->layout->grab_button == event->button)
 
2534
    {
 
2535
      if (range->layout->grab_location == MOUSE_SLIDER)
 
2536
        update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
 
2537
 
 
2538
      stop_scrolling (range);
 
2539
      
 
2540
      return TRUE;
 
2541
    }
 
2542
 
 
2543
  return FALSE;
 
2544
}
 
2545
 
 
2546
/**
 
2547
 * _gtk_range_get_wheel_delta:
 
2548
 * @range: a #GtkRange
 
2549
 * @direction: A #GdkScrollDirection
 
2550
 * 
 
2551
 * Returns a good step value for the mouse wheel.
 
2552
 * 
 
2553
 * Return value: A good step value for the mouse wheel. 
 
2554
 * 
 
2555
 * Since: 2.4
 
2556
 **/
 
2557
gdouble
 
2558
_gtk_range_get_wheel_delta (GtkRange           *range,
 
2559
                            GdkScrollDirection  direction)
 
2560
{
 
2561
  GtkAdjustment *adj = range->adjustment;
 
2562
  gdouble delta;
 
2563
 
 
2564
  if (GTK_IS_SCROLLBAR (range))
 
2565
    delta = pow (adj->page_size, 2.0 / 3.0);
 
2566
  else
 
2567
    delta = adj->step_increment * 2;
 
2568
  
 
2569
  if (direction == GDK_SCROLL_UP ||
 
2570
      direction == GDK_SCROLL_LEFT)
 
2571
    delta = - delta;
 
2572
  
 
2573
  if (range->inverted)
 
2574
    delta = - delta;
 
2575
 
 
2576
  return delta;
 
2577
}
 
2578
      
 
2579
static gboolean
 
2580
gtk_range_scroll_event (GtkWidget      *widget,
 
2581
                        GdkEventScroll *event)
 
2582
{
 
2583
  GtkRange *range = GTK_RANGE (widget);
 
2584
 
 
2585
  if (gtk_widget_get_realized (widget))
 
2586
    {
 
2587
      GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
 
2588
      gdouble delta;
 
2589
      gboolean handled;
 
2590
 
 
2591
      delta = _gtk_range_get_wheel_delta (range, event->direction);
 
2592
 
 
2593
      g_signal_emit (range, signals[CHANGE_VALUE], 0,
 
2594
                     GTK_SCROLL_JUMP, adj->value + delta,
 
2595
                     &handled);
 
2596
      
 
2597
      /* Policy DELAYED makes sense with scroll events,
 
2598
       * but DISCONTINUOUS doesn't, so we update immediately
 
2599
       * for DISCONTINUOUS
 
2600
       */
 
2601
      if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
 
2602
        gtk_range_update_value (range);
 
2603
    }
 
2604
 
 
2605
  return TRUE;
 
2606
}
 
2607
 
 
2608
static gboolean
 
2609
gtk_range_motion_notify (GtkWidget      *widget,
 
2610
                         GdkEventMotion *event)
 
2611
{
 
2612
  GtkRange *range;
 
2613
 
 
2614
  range = GTK_RANGE (widget);
 
2615
 
 
2616
  gdk_event_request_motions (event);
 
2617
  
 
2618
  range->layout->mouse_x = event->x;
 
2619
  range->layout->mouse_y = event->y;
 
2620
 
 
2621
  if (gtk_range_update_mouse_location (range))
 
2622
    gtk_widget_queue_draw (widget);
 
2623
 
 
2624
  if (range->layout->grab_location == MOUSE_SLIDER)
 
2625
    update_slider_position (range, event->x, event->y);
 
2626
 
 
2627
  /* We handled the event if the mouse was in the range_rect */
 
2628
  return range->layout->mouse_location != MOUSE_OUTSIDE;
 
2629
}
 
2630
 
 
2631
static gboolean
 
2632
gtk_range_enter_notify (GtkWidget        *widget,
 
2633
                        GdkEventCrossing *event)
 
2634
{
 
2635
  GtkRange *range = GTK_RANGE (widget);
 
2636
 
 
2637
  range->layout->mouse_x = event->x;
 
2638
  range->layout->mouse_y = event->y;
 
2639
 
 
2640
  if (gtk_range_update_mouse_location (range))
 
2641
    gtk_widget_queue_draw (widget);
 
2642
  
 
2643
  return TRUE;
 
2644
}
 
2645
 
 
2646
static gboolean
 
2647
gtk_range_leave_notify (GtkWidget        *widget,
 
2648
                        GdkEventCrossing *event)
 
2649
{
 
2650
  GtkRange *range = GTK_RANGE (widget);
 
2651
 
 
2652
  range->layout->mouse_x = -1;
 
2653
  range->layout->mouse_y = -1;
 
2654
 
 
2655
  if (gtk_range_update_mouse_location (range))
 
2656
    gtk_widget_queue_draw (widget);
 
2657
  
 
2658
  return TRUE;
 
2659
}
 
2660
 
 
2661
static void
 
2662
gtk_range_grab_notify (GtkWidget *widget,
 
2663
                       gboolean   was_grabbed)
 
2664
{
 
2665
  if (!was_grabbed)
 
2666
    stop_scrolling (GTK_RANGE (widget));
 
2667
}
 
2668
 
 
2669
static void
 
2670
gtk_range_state_changed (GtkWidget    *widget,
 
2671
                         GtkStateType  previous_state)
 
2672
{
 
2673
  if (!gtk_widget_is_sensitive (widget))
 
2674
    stop_scrolling (GTK_RANGE (widget));
 
2675
}
 
2676
 
 
2677
#define check_rectangle(rectangle1, rectangle2)              \
 
2678
  {                                                          \
 
2679
    if (rectangle1.x != rectangle2.x) return TRUE;           \
 
2680
    if (rectangle1.y != rectangle2.y) return TRUE;           \
 
2681
    if (rectangle1.width  != rectangle2.width)  return TRUE; \
 
2682
    if (rectangle1.height != rectangle2.height) return TRUE; \
 
2683
  }
 
2684
 
 
2685
static gboolean
 
2686
layout_changed (GtkRangeLayout *layout1, 
 
2687
                GtkRangeLayout *layout2)
 
2688
{
 
2689
  check_rectangle (layout1->slider, layout2->slider);
 
2690
  check_rectangle (layout1->trough, layout2->trough);
 
2691
  check_rectangle (layout1->stepper_a, layout2->stepper_a);
 
2692
  check_rectangle (layout1->stepper_d, layout2->stepper_d);
 
2693
  check_rectangle (layout1->stepper_b, layout2->stepper_b);
 
2694
  check_rectangle (layout1->stepper_c, layout2->stepper_c);
 
2695
 
 
2696
  if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
 
2697
  if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
 
2698
 
 
2699
  return FALSE;
 
2700
}
 
2701
 
 
2702
static void
 
2703
gtk_range_adjustment_changed (GtkAdjustment *adjustment,
 
2704
                              gpointer       data)
 
2705
{
 
2706
  GtkRange *range = GTK_RANGE (data);
 
2707
  /* create a copy of the layout */
 
2708
  GtkRangeLayout layout = *range->layout;
 
2709
 
 
2710
  range->layout->recalc_marks = TRUE;
 
2711
  range->need_recalc = TRUE;
 
2712
  gtk_range_calc_layout (range, range->adjustment->value);
 
2713
  
 
2714
  /* now check whether the layout changed  */
 
2715
  if (layout_changed (range->layout, &layout))
 
2716
    gtk_widget_queue_draw (GTK_WIDGET (range));
 
2717
 
 
2718
  /* Note that we don't round off to range->round_digits here.
 
2719
   * that's because it's really broken to change a value
 
2720
   * in response to a change signal on that value; round_digits
 
2721
   * is therefore defined to be a filter on what the GtkRange
 
2722
   * can input into the adjustment, not a filter that the GtkRange
 
2723
   * will enforce on the adjustment.
 
2724
   */
 
2725
}
 
2726
 
 
2727
static gboolean
 
2728
force_repaint (gpointer data)
 
2729
{
 
2730
  GtkRange *range = GTK_RANGE (data);
 
2731
 
 
2732
  range->layout->repaint_id = 0;
 
2733
  if (gtk_widget_is_drawable (GTK_WIDGET (range)))
 
2734
    gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
 
2735
 
 
2736
  return FALSE;
 
2737
}
 
2738
 
 
2739
static void
 
2740
gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
 
2741
                                    gpointer       data)
 
2742
{
 
2743
  GtkRange *range = GTK_RANGE (data);
 
2744
  /* create a copy of the layout */
 
2745
  GtkRangeLayout layout = *range->layout;
 
2746
 
 
2747
  range->need_recalc = TRUE;
 
2748
  gtk_range_calc_layout (range, range->adjustment->value);
 
2749
  
 
2750
  /* now check whether the layout changed  */
 
2751
  if (layout_changed (range->layout, &layout) ||
 
2752
      (GTK_IS_SCALE (range) && GTK_SCALE (range)->draw_value))
 
2753
    {
 
2754
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
2755
      /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
 
2756
      if (!range->layout->repaint_id)
 
2757
        range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
 
2758
    }
 
2759
  
 
2760
  /* Note that we don't round off to range->round_digits here.
 
2761
   * that's because it's really broken to change a value
 
2762
   * in response to a change signal on that value; round_digits
 
2763
   * is therefore defined to be a filter on what the GtkRange
 
2764
   * can input into the adjustment, not a filter that the GtkRange
 
2765
   * will enforce on the adjustment.
 
2766
   */
 
2767
 
 
2768
  g_signal_emit (range, signals[VALUE_CHANGED], 0);
 
2769
}
 
2770
 
 
2771
static void
 
2772
gtk_range_style_set (GtkWidget *widget,
 
2773
                     GtkStyle  *previous_style)
 
2774
{
 
2775
  GtkRange *range = GTK_RANGE (widget);
 
2776
 
 
2777
  range->need_recalc = TRUE;
 
2778
 
 
2779
  GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
 
2780
}
 
2781
 
 
2782
static void
 
2783
apply_marks (GtkRange *range, 
 
2784
             gdouble   oldval,
 
2785
             gdouble  *newval)
 
2786
{
 
2787
  gint i;
 
2788
  gdouble mark;
 
2789
 
 
2790
  for (i = 0; i < range->layout->n_marks; i++)
 
2791
    {
 
2792
      mark = range->layout->marks[i];
 
2793
      if ((oldval < mark && mark < *newval) ||
 
2794
          (oldval > mark && mark > *newval))
 
2795
        {
 
2796
          *newval = mark;
 
2797
          return;
 
2798
        }
 
2799
    }
 
2800
}
 
2801
 
 
2802
static void
 
2803
step_back (GtkRange *range)
 
2804
{
 
2805
  gdouble newval;
 
2806
  gboolean handled;
 
2807
  
 
2808
  newval = range->adjustment->value - range->adjustment->step_increment;
 
2809
  apply_marks (range, range->adjustment->value, &newval);
 
2810
  g_signal_emit (range, signals[CHANGE_VALUE], 0,
 
2811
                 GTK_SCROLL_STEP_BACKWARD, newval, &handled);
 
2812
}
 
2813
 
 
2814
static void
 
2815
step_forward (GtkRange *range)
 
2816
{
 
2817
  gdouble newval;
 
2818
  gboolean handled;
 
2819
 
 
2820
  newval = range->adjustment->value + range->adjustment->step_increment;
 
2821
  apply_marks (range, range->adjustment->value, &newval);
 
2822
  g_signal_emit (range, signals[CHANGE_VALUE], 0,
 
2823
                 GTK_SCROLL_STEP_FORWARD, newval, &handled);
 
2824
}
 
2825
 
 
2826
 
 
2827
static void
 
2828
page_back (GtkRange *range)
 
2829
{
 
2830
  gdouble newval;
 
2831
  gboolean handled;
 
2832
 
 
2833
  newval = range->adjustment->value - range->adjustment->page_increment;
 
2834
  apply_marks (range, range->adjustment->value, &newval);
 
2835
  g_signal_emit (range, signals[CHANGE_VALUE], 0,
 
2836
                 GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
 
2837
}
 
2838
 
 
2839
static void
 
2840
page_forward (GtkRange *range)
 
2841
{
 
2842
  gdouble newval;
 
2843
  gboolean handled;
 
2844
 
 
2845
  newval = range->adjustment->value + range->adjustment->page_increment;
 
2846
  apply_marks (range, range->adjustment->value, &newval);
 
2847
  g_signal_emit (range, signals[CHANGE_VALUE], 0,
 
2848
                 GTK_SCROLL_PAGE_FORWARD, newval, &handled);
 
2849
}
 
2850
 
 
2851
static void
 
2852
scroll_begin (GtkRange *range)
 
2853
{
 
2854
  gboolean handled;
 
2855
  g_signal_emit (range, signals[CHANGE_VALUE], 0,
 
2856
                 GTK_SCROLL_START, range->adjustment->lower,
 
2857
                 &handled);
 
2858
}
 
2859
 
 
2860
static void
 
2861
scroll_end (GtkRange *range)
 
2862
{
 
2863
  gdouble newval;
 
2864
  gboolean handled;
 
2865
 
 
2866
  newval = range->adjustment->upper - range->adjustment->page_size;
 
2867
  g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
 
2868
                 &handled);
 
2869
}
 
2870
 
 
2871
static gboolean
 
2872
gtk_range_scroll (GtkRange     *range,
 
2873
                  GtkScrollType scroll)
 
2874
{
 
2875
  gdouble old_value = range->adjustment->value;
 
2876
 
 
2877
  switch (scroll)
 
2878
    {
 
2879
    case GTK_SCROLL_STEP_LEFT:
 
2880
      if (should_invert (range))
 
2881
        step_forward (range);
 
2882
      else
 
2883
        step_back (range);
 
2884
      break;
 
2885
                    
 
2886
    case GTK_SCROLL_STEP_UP:
 
2887
      if (should_invert (range))
 
2888
        step_forward (range);
 
2889
      else
 
2890
        step_back (range);
 
2891
      break;
 
2892
 
 
2893
    case GTK_SCROLL_STEP_RIGHT:
 
2894
      if (should_invert (range))
 
2895
        step_back (range);
 
2896
      else
 
2897
        step_forward (range);
 
2898
      break;
 
2899
                    
 
2900
    case GTK_SCROLL_STEP_DOWN:
 
2901
      if (should_invert (range))
 
2902
        step_back (range);
 
2903
      else
 
2904
        step_forward (range);
 
2905
      break;
 
2906
                  
 
2907
    case GTK_SCROLL_STEP_BACKWARD:
 
2908
      step_back (range);
 
2909
      break;
 
2910
                  
 
2911
    case GTK_SCROLL_STEP_FORWARD:
 
2912
      step_forward (range);
 
2913
      break;
 
2914
 
 
2915
    case GTK_SCROLL_PAGE_LEFT:
 
2916
      if (should_invert (range))
 
2917
        page_forward (range);
 
2918
      else
 
2919
        page_back (range);
 
2920
      break;
 
2921
                    
 
2922
    case GTK_SCROLL_PAGE_UP:
 
2923
      if (should_invert (range))
 
2924
        page_forward (range);
 
2925
      else
 
2926
        page_back (range);
 
2927
      break;
 
2928
 
 
2929
    case GTK_SCROLL_PAGE_RIGHT:
 
2930
      if (should_invert (range))
 
2931
        page_back (range);
 
2932
      else
 
2933
        page_forward (range);
 
2934
      break;
 
2935
                    
 
2936
    case GTK_SCROLL_PAGE_DOWN:
 
2937
      if (should_invert (range))
 
2938
        page_back (range);
 
2939
      else
 
2940
        page_forward (range);
 
2941
      break;
 
2942
                  
 
2943
    case GTK_SCROLL_PAGE_BACKWARD:
 
2944
      page_back (range);
 
2945
      break;
 
2946
                  
 
2947
    case GTK_SCROLL_PAGE_FORWARD:
 
2948
      page_forward (range);
 
2949
      break;
 
2950
 
 
2951
    case GTK_SCROLL_START:
 
2952
      scroll_begin (range);
 
2953
      break;
 
2954
 
 
2955
    case GTK_SCROLL_END:
 
2956
      scroll_end (range);
 
2957
      break;
 
2958
 
 
2959
    case GTK_SCROLL_JUMP:
 
2960
      /* Used by CList, range doesn't use it. */
 
2961
      break;
 
2962
 
 
2963
    case GTK_SCROLL_NONE:
 
2964
      break;
 
2965
    }
 
2966
 
 
2967
  return range->adjustment->value != old_value;
 
2968
}
 
2969
 
 
2970
static void
 
2971
gtk_range_move_slider (GtkRange     *range,
 
2972
                       GtkScrollType scroll)
 
2973
{
 
2974
  gboolean cursor_only;
 
2975
 
 
2976
  g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
 
2977
                "gtk-keynav-cursor-only", &cursor_only,
 
2978
                NULL);
 
2979
 
 
2980
  if (cursor_only)
 
2981
    {
 
2982
      GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
 
2983
 
 
2984
      if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
 
2985
        {
 
2986
          if (scroll == GTK_SCROLL_STEP_UP ||
 
2987
              scroll == GTK_SCROLL_STEP_DOWN)
 
2988
            {
 
2989
              if (toplevel)
 
2990
                gtk_widget_child_focus (toplevel,
 
2991
                                        scroll == GTK_SCROLL_STEP_UP ?
 
2992
                                        GTK_DIR_UP : GTK_DIR_DOWN);
 
2993
              return;
 
2994
            }
 
2995
        }
 
2996
      else
 
2997
        {
 
2998
          if (scroll == GTK_SCROLL_STEP_LEFT ||
 
2999
              scroll == GTK_SCROLL_STEP_RIGHT)
 
3000
            {
 
3001
              if (toplevel)
 
3002
                gtk_widget_child_focus (toplevel,
 
3003
                                        scroll == GTK_SCROLL_STEP_LEFT ?
 
3004
                                        GTK_DIR_LEFT : GTK_DIR_RIGHT);
 
3005
              return;
 
3006
            }
 
3007
        }
 
3008
    }
 
3009
 
 
3010
  if (! gtk_range_scroll (range, scroll))
 
3011
    gtk_widget_error_bell (GTK_WIDGET (range));
 
3012
 
 
3013
  /* Policy DELAYED makes sense with key events,
 
3014
   * but DISCONTINUOUS doesn't, so we update immediately
 
3015
   * for DISCONTINUOUS
 
3016
   */
 
3017
  if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
 
3018
    gtk_range_update_value (range);
 
3019
}
 
3020
 
 
3021
static void
 
3022
gtk_range_get_props (GtkRange  *range,
 
3023
                     gint      *slider_width,
 
3024
                     gint      *stepper_size,
 
3025
                     gint      *focus_width,
 
3026
                     gint      *trough_border,
 
3027
                     gint      *stepper_spacing,
 
3028
                     gboolean  *trough_under_steppers,
 
3029
                     gint      *arrow_displacement_x,
 
3030
                     gint      *arrow_displacement_y)
 
3031
{
 
3032
  GtkWidget *widget =  GTK_WIDGET (range);
 
3033
  gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
 
3034
  gint tmp_stepper_spacing, tmp_trough_under_steppers;
 
3035
  gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
 
3036
  
 
3037
  gtk_widget_style_get (widget,
 
3038
                        "slider-width", &tmp_slider_width,
 
3039
                        "trough-border", &tmp_trough_border,
 
3040
                        "stepper-size", &tmp_stepper_size,
 
3041
                        "stepper-spacing", &tmp_stepper_spacing,
 
3042
                        "trough-under-steppers", &tmp_trough_under_steppers,
 
3043
                        "arrow-displacement-x", &tmp_arrow_displacement_x,
 
3044
                        "arrow-displacement-y", &tmp_arrow_displacement_y,
 
3045
                        NULL);
 
3046
 
 
3047
  if (tmp_stepper_spacing > 0)
 
3048
    tmp_trough_under_steppers = FALSE;
 
3049
 
 
3050
  if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
 
3051
    {
 
3052
      gint focus_line_width;
 
3053
      gint focus_padding;
 
3054
      
 
3055
      gtk_widget_style_get (GTK_WIDGET (range),
 
3056
                            "focus-line-width", &focus_line_width,
 
3057
                            "focus-padding", &focus_padding,
 
3058
                            NULL);
 
3059
 
 
3060
      tmp_focus_width = focus_line_width + focus_padding;
 
3061
    }
 
3062
  else
 
3063
    {
 
3064
      tmp_focus_width = 0;
 
3065
    }
 
3066
  
 
3067
  if (slider_width)
 
3068
    *slider_width = tmp_slider_width;
 
3069
 
 
3070
  if (focus_width)
 
3071
    *focus_width = tmp_focus_width;
 
3072
 
 
3073
  if (trough_border)
 
3074
    *trough_border = tmp_trough_border;
 
3075
 
 
3076
  if (stepper_size)
 
3077
    *stepper_size = tmp_stepper_size;
 
3078
 
 
3079
  if (stepper_spacing)
 
3080
    *stepper_spacing = tmp_stepper_spacing;
 
3081
 
 
3082
  if (trough_under_steppers)
 
3083
    *trough_under_steppers = tmp_trough_under_steppers;
 
3084
 
 
3085
  if (arrow_displacement_x)
 
3086
    *arrow_displacement_x = tmp_arrow_displacement_x;
 
3087
 
 
3088
  if (arrow_displacement_y)
 
3089
    *arrow_displacement_y = tmp_arrow_displacement_y;
 
3090
}
 
3091
 
 
3092
#define POINT_IN_RECT(xcoord, ycoord, rect) \
 
3093
 ((xcoord) >= (rect).x &&                   \
 
3094
  (xcoord) <  ((rect).x + (rect).width) &&  \
 
3095
  (ycoord) >= (rect).y &&                   \
 
3096
  (ycoord) <  ((rect).y + (rect).height))
 
3097
 
 
3098
/* Update mouse location, return TRUE if it changes */
 
3099
static gboolean
 
3100
gtk_range_update_mouse_location (GtkRange *range)
 
3101
{
 
3102
  gint x, y;
 
3103
  MouseLocation old;
 
3104
  GtkWidget *widget;
 
3105
 
 
3106
  widget = GTK_WIDGET (range);
 
3107
  
 
3108
  old = range->layout->mouse_location;
 
3109
  
 
3110
  x = range->layout->mouse_x;
 
3111
  y = range->layout->mouse_y;
 
3112
 
 
3113
  if (range->layout->grab_location != MOUSE_OUTSIDE)
 
3114
    range->layout->mouse_location = range->layout->grab_location;
 
3115
  else if (POINT_IN_RECT (x, y, range->layout->stepper_a))
 
3116
    range->layout->mouse_location = MOUSE_STEPPER_A;
 
3117
  else if (POINT_IN_RECT (x, y, range->layout->stepper_b))
 
3118
    range->layout->mouse_location = MOUSE_STEPPER_B;
 
3119
  else if (POINT_IN_RECT (x, y, range->layout->stepper_c))
 
3120
    range->layout->mouse_location = MOUSE_STEPPER_C;
 
3121
  else if (POINT_IN_RECT (x, y, range->layout->stepper_d))
 
3122
    range->layout->mouse_location = MOUSE_STEPPER_D;
 
3123
  else if (POINT_IN_RECT (x, y, range->layout->slider))
 
3124
    range->layout->mouse_location = MOUSE_SLIDER;
 
3125
  else if (POINT_IN_RECT (x, y, range->layout->trough))
 
3126
    range->layout->mouse_location = MOUSE_TROUGH;
 
3127
  else if (POINT_IN_RECT (x, y, widget->allocation))
 
3128
    range->layout->mouse_location = MOUSE_WIDGET;
 
3129
  else
 
3130
    range->layout->mouse_location = MOUSE_OUTSIDE;
 
3131
 
 
3132
  return old != range->layout->mouse_location;
 
3133
}
 
3134
 
 
3135
/* Clamp rect, border inside widget->allocation, such that we prefer
 
3136
 * to take space from border not rect in all directions, and prefer to
 
3137
 * give space to border over rect in one direction.
 
3138
 */
 
3139
static void
 
3140
clamp_dimensions (GtkWidget    *widget,
 
3141
                  GdkRectangle *rect,
 
3142
                  GtkBorder    *border,
 
3143
                  gboolean      border_expands_horizontally)
 
3144
{
 
3145
  gint extra, shortage;
 
3146
  
 
3147
  g_return_if_fail (rect->x == 0);
 
3148
  g_return_if_fail (rect->y == 0);  
 
3149
  g_return_if_fail (rect->width >= 0);
 
3150
  g_return_if_fail (rect->height >= 0);
 
3151
 
 
3152
  /* Width */
 
3153
  
 
3154
  extra = widget->allocation.width - border->left - border->right - rect->width;
 
3155
  if (extra > 0)
 
3156
    {
 
3157
      if (border_expands_horizontally)
 
3158
        {
 
3159
          border->left += extra / 2;
 
3160
          border->right += extra / 2 + extra % 2;
 
3161
        }
 
3162
      else
 
3163
        {
 
3164
          rect->width += extra;
 
3165
        }
 
3166
    }
 
3167
  
 
3168
  /* See if we can fit rect, if not kill the border */
 
3169
  shortage = rect->width - widget->allocation.width;
 
3170
  if (shortage > 0)
 
3171
    {
 
3172
      rect->width = widget->allocation.width;
 
3173
      /* lose the border */
 
3174
      border->left = 0;
 
3175
      border->right = 0;
 
3176
    }
 
3177
  else
 
3178
    {
 
3179
      /* See if we can fit rect with borders */
 
3180
      shortage = rect->width + border->left + border->right -
 
3181
        widget->allocation.width;
 
3182
      if (shortage > 0)
 
3183
        {
 
3184
          /* Shrink borders */
 
3185
          border->left -= shortage / 2;
 
3186
          border->right -= shortage / 2 + shortage % 2;
 
3187
        }
 
3188
    }
 
3189
 
 
3190
  /* Height */
 
3191
  
 
3192
  extra = widget->allocation.height - border->top - border->bottom - rect->height;
 
3193
  if (extra > 0)
 
3194
    {
 
3195
      if (border_expands_horizontally)
 
3196
        {
 
3197
          /* don't expand border vertically */
 
3198
          rect->height += extra;
 
3199
        }
 
3200
      else
 
3201
        {
 
3202
          border->top += extra / 2;
 
3203
          border->bottom += extra / 2 + extra % 2;
 
3204
        }
 
3205
    }
 
3206
  
 
3207
  /* See if we can fit rect, if not kill the border */
 
3208
  shortage = rect->height - widget->allocation.height;
 
3209
  if (shortage > 0)
 
3210
    {
 
3211
      rect->height = widget->allocation.height;
 
3212
      /* lose the border */
 
3213
      border->top = 0;
 
3214
      border->bottom = 0;
 
3215
    }
 
3216
  else
 
3217
    {
 
3218
      /* See if we can fit rect with borders */
 
3219
      shortage = rect->height + border->top + border->bottom -
 
3220
        widget->allocation.height;
 
3221
      if (shortage > 0)
 
3222
        {
 
3223
          /* Shrink borders */
 
3224
          border->top -= shortage / 2;
 
3225
          border->bottom -= shortage / 2 + shortage % 2;
 
3226
        }
 
3227
    }
 
3228
}
 
3229
 
 
3230
static void
 
3231
gtk_range_calc_request (GtkRange      *range,
 
3232
                        gint           slider_width,
 
3233
                        gint           stepper_size,
 
3234
                        gint           focus_width,
 
3235
                        gint           trough_border,
 
3236
                        gint           stepper_spacing,
 
3237
                        GdkRectangle  *range_rect,
 
3238
                        GtkBorder     *border,
 
3239
                        gint          *n_steppers_p,
 
3240
                        gboolean      *has_steppers_ab,
 
3241
                        gboolean      *has_steppers_cd,
 
3242
                        gint          *slider_length_p)
 
3243
{
 
3244
  gint slider_length;
 
3245
  gint n_steppers;
 
3246
  gint n_steppers_ab;
 
3247
  gint n_steppers_cd;
 
3248
 
 
3249
  border->left = 0;
 
3250
  border->right = 0;
 
3251
  border->top = 0;
 
3252
  border->bottom = 0;
 
3253
 
 
3254
  if (GTK_RANGE_GET_CLASS (range)->get_range_border)
 
3255
    GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
 
3256
 
 
3257
  n_steppers_ab = 0;
 
3258
  n_steppers_cd = 0;
 
3259
 
 
3260
  if (range->has_stepper_a)
 
3261
    n_steppers_ab += 1;
 
3262
  if (range->has_stepper_b)
 
3263
    n_steppers_ab += 1;
 
3264
  if (range->has_stepper_c)
 
3265
    n_steppers_cd += 1;
 
3266
  if (range->has_stepper_d)
 
3267
    n_steppers_cd += 1;
 
3268
 
 
3269
  n_steppers = n_steppers_ab + n_steppers_cd;
 
3270
 
 
3271
  slider_length = range->min_slider_size;
 
3272
 
 
3273
  range_rect->x = 0;
 
3274
  range_rect->y = 0;
 
3275
  
 
3276
  /* We never expand to fill available space in the small dimension
 
3277
   * (i.e. vertical scrollbars are always a fixed width)
 
3278
   */
 
3279
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
 
3280
    {
 
3281
      range_rect->width = (focus_width + trough_border) * 2 + slider_width;
 
3282
      range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
 
3283
 
 
3284
      if (n_steppers_ab > 0)
 
3285
        range_rect->height += stepper_spacing;
 
3286
 
 
3287
      if (n_steppers_cd > 0)
 
3288
        range_rect->height += stepper_spacing;
 
3289
    }
 
3290
  else
 
3291
    {
 
3292
      range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
 
3293
      range_rect->height = (focus_width + trough_border) * 2 + slider_width;
 
3294
 
 
3295
      if (n_steppers_ab > 0)
 
3296
        range_rect->width += stepper_spacing;
 
3297
 
 
3298
      if (n_steppers_cd > 0)
 
3299
        range_rect->width += stepper_spacing;
 
3300
    }
 
3301
 
 
3302
  if (n_steppers_p)
 
3303
    *n_steppers_p = n_steppers;
 
3304
 
 
3305
  if (has_steppers_ab)
 
3306
    *has_steppers_ab = (n_steppers_ab > 0);
 
3307
 
 
3308
  if (has_steppers_cd)
 
3309
    *has_steppers_cd = (n_steppers_cd > 0);
 
3310
 
 
3311
  if (slider_length_p)
 
3312
    *slider_length_p = slider_length;
 
3313
}
 
3314
 
 
3315
static void
 
3316
gtk_range_calc_layout (GtkRange *range,
 
3317
                       gdouble   adjustment_value)
 
3318
{
 
3319
  gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
 
3320
  gint slider_length;
 
3321
  GtkBorder border;
 
3322
  gint n_steppers;
 
3323
  gboolean has_steppers_ab;
 
3324
  gboolean has_steppers_cd;
 
3325
  gboolean trough_under_steppers;
 
3326
  GdkRectangle range_rect;
 
3327
  GtkRangeLayout *layout;
 
3328
  GtkWidget *widget;
 
3329
  
 
3330
  if (!range->need_recalc)
 
3331
    return;
 
3332
 
 
3333
  /* If we have a too-small allocation, we prefer the steppers over
 
3334
   * the trough/slider, probably the steppers are a more useful
 
3335
   * feature in small spaces.
 
3336
   *
 
3337
   * Also, we prefer to draw the range itself rather than the border
 
3338
   * areas if there's a conflict, since the borders will be decoration
 
3339
   * not controls. Though this depends on subclasses cooperating by
 
3340
   * not drawing on range->range_rect.
 
3341
   */
 
3342
 
 
3343
  widget = GTK_WIDGET (range);
 
3344
  layout = range->layout;
 
3345
  
 
3346
  gtk_range_get_props (range,
 
3347
                       &slider_width, &stepper_size,
 
3348
                       &focus_width, &trough_border,
 
3349
                       &stepper_spacing, &trough_under_steppers,
 
3350
                       NULL, NULL);
 
3351
 
 
3352
  gtk_range_calc_request (range, 
 
3353
                          slider_width, stepper_size,
 
3354
                          focus_width, trough_border, stepper_spacing,
 
3355
                          &range_rect, &border, &n_steppers,
 
3356
                          &has_steppers_ab, &has_steppers_cd, &slider_length);
 
3357
  
 
3358
  /* We never expand to fill available space in the small dimension
 
3359
   * (i.e. vertical scrollbars are always a fixed width)
 
3360
   */
 
3361
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
 
3362
    {
 
3363
      clamp_dimensions (widget, &range_rect, &border, TRUE);
 
3364
    }
 
3365
  else
 
3366
    {
 
3367
      clamp_dimensions (widget, &range_rect, &border, FALSE);
 
3368
    }
 
3369
  
 
3370
  range_rect.x = border.left;
 
3371
  range_rect.y = border.top;
 
3372
  
 
3373
  range->range_rect = range_rect;
 
3374
  
 
3375
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
 
3376
    {
 
3377
      gint stepper_width, stepper_height;
 
3378
 
 
3379
      /* Steppers are the width of the range, and stepper_size in
 
3380
       * height, or if we don't have enough height, divided equally
 
3381
       * among available space.
 
3382
       */
 
3383
      stepper_width = range_rect.width - focus_width * 2;
 
3384
 
 
3385
      if (trough_under_steppers)
 
3386
        stepper_width -= trough_border * 2;
 
3387
 
 
3388
      if (stepper_width < 1)
 
3389
        stepper_width = range_rect.width; /* screw the trough border */
 
3390
 
 
3391
      if (n_steppers == 0)
 
3392
        stepper_height = 0; /* avoid divide by n_steppers */
 
3393
      else
 
3394
        stepper_height = MIN (stepper_size, (range_rect.height / n_steppers));
 
3395
 
 
3396
      /* Stepper A */
 
3397
      
 
3398
      layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
 
3399
      layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
 
3400
 
 
3401
      if (range->has_stepper_a)
 
3402
        {
 
3403
          layout->stepper_a.width = stepper_width;
 
3404
          layout->stepper_a.height = stepper_height;
 
3405
        }
 
3406
      else
 
3407
        {
 
3408
          layout->stepper_a.width = 0;
 
3409
          layout->stepper_a.height = 0;
 
3410
        }
 
3411
 
 
3412
      /* Stepper B */
 
3413
      
 
3414
      layout->stepper_b.x = layout->stepper_a.x;
 
3415
      layout->stepper_b.y = layout->stepper_a.y + layout->stepper_a.height;
 
3416
 
 
3417
      if (range->has_stepper_b)
 
3418
        {
 
3419
          layout->stepper_b.width = stepper_width;
 
3420
          layout->stepper_b.height = stepper_height;
 
3421
        }
 
3422
      else
 
3423
        {
 
3424
          layout->stepper_b.width = 0;
 
3425
          layout->stepper_b.height = 0;
 
3426
        }
 
3427
 
 
3428
      /* Stepper D */
 
3429
 
 
3430
      if (range->has_stepper_d)
 
3431
        {
 
3432
          layout->stepper_d.width = stepper_width;
 
3433
          layout->stepper_d.height = stepper_height;
 
3434
        }
 
3435
      else
 
3436
        {
 
3437
          layout->stepper_d.width = 0;
 
3438
          layout->stepper_d.height = 0;
 
3439
        }
 
3440
      
 
3441
      layout->stepper_d.x = layout->stepper_a.x;
 
3442
      layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
 
3443
 
 
3444
      /* Stepper C */
 
3445
 
 
3446
      if (range->has_stepper_c)
 
3447
        {
 
3448
          layout->stepper_c.width = stepper_width;
 
3449
          layout->stepper_c.height = stepper_height;
 
3450
        }
 
3451
      else
 
3452
        {
 
3453
          layout->stepper_c.width = 0;
 
3454
          layout->stepper_c.height = 0;
 
3455
        }
 
3456
      
 
3457
      layout->stepper_c.x = layout->stepper_a.x;
 
3458
      layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
 
3459
 
 
3460
      /* Now the trough is the remaining space between steppers B and C,
 
3461
       * if any, minus spacing
 
3462
       */
 
3463
      layout->trough.x = range_rect.x;
 
3464
      layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
 
3465
      layout->trough.width = range_rect.width;
 
3466
      layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
 
3467
 
 
3468
      /* Slider fits into the trough, with stepper_spacing on either side,
 
3469
       * and the size/position based on the adjustment or fixed, depending.
 
3470
       */
 
3471
      layout->slider.x = layout->trough.x + focus_width + trough_border;
 
3472
      layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
 
3473
 
 
3474
      /* Compute slider position/length */
 
3475
      {
 
3476
        gint y, bottom, top, height;
 
3477
        
 
3478
        top = layout->trough.y;
 
3479
        bottom = layout->trough.y + layout->trough.height;
 
3480
 
 
3481
        if (! trough_under_steppers)
 
3482
          {
 
3483
            top += trough_border;
 
3484
            bottom -= trough_border;
 
3485
          }
 
3486
 
 
3487
        /* slider height is the fraction (page_size /
 
3488
         * total_adjustment_range) times the trough height in pixels
 
3489
         */
 
3490
 
 
3491
        if (range->adjustment->upper - range->adjustment->lower != 0)
 
3492
          height = ((bottom - top) * (range->adjustment->page_size /
 
3493
                                       (range->adjustment->upper - range->adjustment->lower)));
 
3494
        else
 
3495
          height = range->min_slider_size;
 
3496
        
 
3497
        if (height < range->min_slider_size ||
 
3498
            range->slider_size_fixed)
 
3499
          height = range->min_slider_size;
 
3500
 
 
3501
        height = MIN (height, layout->trough.height);
 
3502
        
 
3503
        y = top;
 
3504
        
 
3505
        if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
 
3506
          y += (bottom - top - height) * ((adjustment_value - range->adjustment->lower) /
 
3507
                                          (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
 
3508
        
 
3509
        y = CLAMP (y, top, bottom);
 
3510
        
 
3511
        if (should_invert (range))
 
3512
          y = bottom - (y - top + height);
 
3513
        
 
3514
        layout->slider.y = y;
 
3515
        layout->slider.height = height;
 
3516
 
 
3517
        /* These are publically exported */
 
3518
        range->slider_start = layout->slider.y;
 
3519
        range->slider_end = layout->slider.y + layout->slider.height;
 
3520
      }
 
3521
    }
 
3522
  else
 
3523
    {
 
3524
      gint stepper_width, stepper_height;
 
3525
 
 
3526
      /* Steppers are the height of the range, and stepper_size in
 
3527
       * width, or if we don't have enough width, divided equally
 
3528
       * among available space.
 
3529
       */
 
3530
      stepper_height = range_rect.height + focus_width * 2;
 
3531
 
 
3532
      if (trough_under_steppers)
 
3533
        stepper_height -= trough_border * 2;
 
3534
 
 
3535
      if (stepper_height < 1)
 
3536
        stepper_height = range_rect.height; /* screw the trough border */
 
3537
 
 
3538
      if (n_steppers == 0)
 
3539
        stepper_width = 0; /* avoid divide by n_steppers */
 
3540
      else
 
3541
        stepper_width = MIN (stepper_size, (range_rect.width / n_steppers));
 
3542
 
 
3543
      /* Stepper A */
 
3544
      
 
3545
      layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
 
3546
      layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
 
3547
 
 
3548
      if (range->has_stepper_a)
 
3549
        {
 
3550
          layout->stepper_a.width = stepper_width;
 
3551
          layout->stepper_a.height = stepper_height;
 
3552
        }
 
3553
      else
 
3554
        {
 
3555
          layout->stepper_a.width = 0;
 
3556
          layout->stepper_a.height = 0;
 
3557
        }
 
3558
 
 
3559
      /* Stepper B */
 
3560
      
 
3561
      layout->stepper_b.x = layout->stepper_a.x + layout->stepper_a.width;
 
3562
      layout->stepper_b.y = layout->stepper_a.y;
 
3563
 
 
3564
      if (range->has_stepper_b)
 
3565
        {
 
3566
          layout->stepper_b.width = stepper_width;
 
3567
          layout->stepper_b.height = stepper_height;
 
3568
        }
 
3569
      else
 
3570
        {
 
3571
          layout->stepper_b.width = 0;
 
3572
          layout->stepper_b.height = 0;
 
3573
        }
 
3574
 
 
3575
      /* Stepper D */
 
3576
 
 
3577
      if (range->has_stepper_d)
 
3578
        {
 
3579
          layout->stepper_d.width = stepper_width;
 
3580
          layout->stepper_d.height = stepper_height;
 
3581
        }
 
3582
      else
 
3583
        {
 
3584
          layout->stepper_d.width = 0;
 
3585
          layout->stepper_d.height = 0;
 
3586
        }
 
3587
 
 
3588
      layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
 
3589
      layout->stepper_d.y = layout->stepper_a.y;
 
3590
 
 
3591
 
 
3592
      /* Stepper C */
 
3593
 
 
3594
      if (range->has_stepper_c)
 
3595
        {
 
3596
          layout->stepper_c.width = stepper_width;
 
3597
          layout->stepper_c.height = stepper_height;
 
3598
        }
 
3599
      else
 
3600
        {
 
3601
          layout->stepper_c.width = 0;
 
3602
          layout->stepper_c.height = 0;
 
3603
        }
 
3604
      
 
3605
      layout->stepper_c.x = layout->stepper_d.x - layout->stepper_c.width;
 
3606
      layout->stepper_c.y = layout->stepper_a.y;
 
3607
 
 
3608
      /* Now the trough is the remaining space between steppers B and C,
 
3609
       * if any
 
3610
       */
 
3611
      layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
 
3612
      layout->trough.y = range_rect.y;
 
3613
 
 
3614
      layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
 
3615
      layout->trough.height = range_rect.height;
 
3616
 
 
3617
      /* Slider fits into the trough, with stepper_spacing on either side,
 
3618
       * and the size/position based on the adjustment or fixed, depending.
 
3619
       */
 
3620
      layout->slider.y = layout->trough.y + focus_width + trough_border;
 
3621
      layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
 
3622
 
 
3623
      /* Compute slider position/length */
 
3624
      {
 
3625
        gint x, left, right, width;
 
3626
        
 
3627
        left = layout->trough.x;
 
3628
        right = layout->trough.x + layout->trough.width;
 
3629
 
 
3630
        if (! trough_under_steppers)
 
3631
          {
 
3632
            left += trough_border;
 
3633
            right -= trough_border;
 
3634
          }
 
3635
 
 
3636
        /* slider width is the fraction (page_size /
 
3637
         * total_adjustment_range) times the trough width in pixels
 
3638
         */
 
3639
        
 
3640
        if (range->adjustment->upper - range->adjustment->lower != 0)
 
3641
          width = ((right - left) * (range->adjustment->page_size /
 
3642
                                   (range->adjustment->upper - range->adjustment->lower)));
 
3643
        else
 
3644
          width = range->min_slider_size;
 
3645
        
 
3646
        if (width < range->min_slider_size ||
 
3647
            range->slider_size_fixed)
 
3648
          width = range->min_slider_size;
 
3649
        
 
3650
        width = MIN (width, layout->trough.width);
 
3651
        
 
3652
        x = left;
 
3653
        
 
3654
        if (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size != 0)
 
3655
          x += (right - left - width) * ((adjustment_value - range->adjustment->lower) /
 
3656
                                         (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size));
 
3657
        
 
3658
        x = CLAMP (x, left, right);
 
3659
        
 
3660
        if (should_invert (range))
 
3661
          x = right - (x - left + width);
 
3662
        
 
3663
        layout->slider.x = x;
 
3664
        layout->slider.width = width;
 
3665
 
 
3666
        /* These are publically exported */
 
3667
        range->slider_start = layout->slider.x;
 
3668
        range->slider_end = layout->slider.x + layout->slider.width;
 
3669
      }
 
3670
    }
 
3671
  
 
3672
  gtk_range_update_mouse_location (range);
 
3673
 
 
3674
  switch (range->layout->upper_sensitivity)
 
3675
    {
 
3676
    case GTK_SENSITIVITY_AUTO:
 
3677
      range->layout->upper_sensitive =
 
3678
        (range->adjustment->value <
 
3679
         (range->adjustment->upper - range->adjustment->page_size));
 
3680
      break;
 
3681
 
 
3682
    case GTK_SENSITIVITY_ON:
 
3683
      range->layout->upper_sensitive = TRUE;
 
3684
      break;
 
3685
 
 
3686
    case GTK_SENSITIVITY_OFF:
 
3687
      range->layout->upper_sensitive = FALSE;
 
3688
      break;
 
3689
    }
 
3690
 
 
3691
  switch (range->layout->lower_sensitivity)
 
3692
    {
 
3693
    case GTK_SENSITIVITY_AUTO:
 
3694
      range->layout->lower_sensitive =
 
3695
        (range->adjustment->value > range->adjustment->lower);
 
3696
      break;
 
3697
 
 
3698
    case GTK_SENSITIVITY_ON:
 
3699
      range->layout->lower_sensitive = TRUE;
 
3700
      break;
 
3701
 
 
3702
    case GTK_SENSITIVITY_OFF:
 
3703
      range->layout->lower_sensitive = FALSE;
 
3704
      break;
 
3705
    }
 
3706
}
 
3707
 
 
3708
static GdkRectangle*
 
3709
get_area (GtkRange     *range,
 
3710
          MouseLocation location)
 
3711
{
 
3712
  switch (location)
 
3713
    {
 
3714
    case MOUSE_STEPPER_A:
 
3715
      return &range->layout->stepper_a;
 
3716
    case MOUSE_STEPPER_B:
 
3717
      return &range->layout->stepper_b;
 
3718
    case MOUSE_STEPPER_C:
 
3719
      return &range->layout->stepper_c;
 
3720
    case MOUSE_STEPPER_D:
 
3721
      return &range->layout->stepper_d;
 
3722
    case MOUSE_TROUGH:
 
3723
      return &range->layout->trough;
 
3724
    case MOUSE_SLIDER:
 
3725
      return &range->layout->slider;
 
3726
    case MOUSE_WIDGET:
 
3727
    case MOUSE_OUTSIDE:
 
3728
      break;
 
3729
    }
 
3730
 
 
3731
  g_warning (G_STRLOC": bug");
 
3732
  return NULL;
 
3733
}
 
3734
 
 
3735
static void
 
3736
gtk_range_calc_marks (GtkRange *range)
 
3737
{
 
3738
  gint i;
 
3739
  
 
3740
  if (!range->layout->recalc_marks)
 
3741
    return;
 
3742
 
 
3743
  range->layout->recalc_marks = FALSE;
 
3744
 
 
3745
  for (i = 0; i < range->layout->n_marks; i++)
 
3746
    {
 
3747
      range->need_recalc = TRUE;
 
3748
      gtk_range_calc_layout (range, range->layout->marks[i]);
 
3749
      if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
 
3750
        range->layout->mark_pos[i] = range->layout->slider.x + range->layout->slider.width / 2;
 
3751
      else
 
3752
        range->layout->mark_pos[i] = range->layout->slider.y + range->layout->slider.height / 2;
 
3753
    }
 
3754
 
 
3755
  range->need_recalc = TRUE;
 
3756
}
 
3757
 
 
3758
static gboolean
 
3759
gtk_range_real_change_value (GtkRange     *range,
 
3760
                             GtkScrollType scroll,
 
3761
                             gdouble       value)
 
3762
{
 
3763
  /* potentially adjust the bounds _before we clamp */
 
3764
  g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
 
3765
 
 
3766
  if (range->layout->restrict_to_fill_level)
 
3767
    value = MIN (value, MAX (range->adjustment->lower,
 
3768
                             range->layout->fill_level));
 
3769
 
 
3770
  value = CLAMP (value, range->adjustment->lower,
 
3771
                 (range->adjustment->upper - range->adjustment->page_size));
 
3772
 
 
3773
  if (range->round_digits >= 0)
 
3774
    {
 
3775
      gdouble power;
 
3776
      gint i;
 
3777
 
 
3778
      i = range->round_digits;
 
3779
      power = 1;
 
3780
      while (i--)
 
3781
        power *= 10;
 
3782
      
 
3783
      value = floor ((value * power) + 0.5) / power;
 
3784
    }
 
3785
  
 
3786
  if (range->adjustment->value != value)
 
3787
    {
 
3788
      range->need_recalc = TRUE;
 
3789
 
 
3790
      gtk_widget_queue_draw (GTK_WIDGET (range));
 
3791
      
 
3792
      switch (range->update_policy)
 
3793
        {
 
3794
        case GTK_UPDATE_CONTINUOUS:
 
3795
          gtk_adjustment_set_value (range->adjustment, value);
 
3796
          break;
 
3797
 
 
3798
          /* Delayed means we update after a period of inactivity */
 
3799
        case GTK_UPDATE_DELAYED:
 
3800
          gtk_range_reset_update_timer (range);
 
3801
          /* FALL THRU */
 
3802
 
 
3803
          /* Discontinuous means we update on button release */
 
3804
        case GTK_UPDATE_DISCONTINUOUS:
 
3805
          /* don't emit value_changed signal */
 
3806
          range->adjustment->value = value;
 
3807
          range->update_pending = TRUE;
 
3808
          break;
 
3809
        }
 
3810
    }
 
3811
  return FALSE;
 
3812
}
 
3813
 
 
3814
static void
 
3815
gtk_range_update_value (GtkRange *range)
 
3816
{
 
3817
  gtk_range_remove_update_timer (range);
 
3818
  
 
3819
  if (range->update_pending)
 
3820
    {
 
3821
      gtk_adjustment_value_changed (range->adjustment);
 
3822
 
 
3823
      range->update_pending = FALSE;
 
3824
    }
 
3825
}
 
3826
 
 
3827
struct _GtkRangeStepTimer
 
3828
{
 
3829
  guint timeout_id;
 
3830
  GtkScrollType step;
 
3831
};
 
3832
 
 
3833
static gboolean
 
3834
second_timeout (gpointer data)
 
3835
{
 
3836
  GtkRange *range;
 
3837
 
 
3838
  range = GTK_RANGE (data);
 
3839
  gtk_range_scroll (range, range->timer->step);
 
3840
  
 
3841
  return TRUE;
 
3842
}
 
3843
 
 
3844
static gboolean
 
3845
initial_timeout (gpointer data)
 
3846
{
 
3847
  GtkRange    *range;
 
3848
  GtkSettings *settings;
 
3849
  guint        timeout;
 
3850
 
 
3851
  settings = gtk_widget_get_settings (GTK_WIDGET (data));
 
3852
  g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
 
3853
 
 
3854
  range = GTK_RANGE (data);
 
3855
  range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
 
3856
                                            second_timeout,
 
3857
                                            range);
 
3858
  /* remove self */
 
3859
  return FALSE;
 
3860
}
 
3861
 
 
3862
static void
 
3863
gtk_range_add_step_timer (GtkRange      *range,
 
3864
                          GtkScrollType  step)
 
3865
{
 
3866
  GtkSettings *settings;
 
3867
  guint        timeout;
 
3868
 
 
3869
  g_return_if_fail (range->timer == NULL);
 
3870
  g_return_if_fail (step != GTK_SCROLL_NONE);
 
3871
 
 
3872
  settings = gtk_widget_get_settings (GTK_WIDGET (range));
 
3873
  g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
 
3874
 
 
3875
  range->timer = g_new (GtkRangeStepTimer, 1);
 
3876
 
 
3877
  range->timer->timeout_id = gdk_threads_add_timeout (timeout,
 
3878
                                            initial_timeout,
 
3879
                                            range);
 
3880
  range->timer->step = step;
 
3881
 
 
3882
  gtk_range_scroll (range, range->timer->step);
 
3883
}
 
3884
 
 
3885
static void
 
3886
gtk_range_remove_step_timer (GtkRange *range)
 
3887
{
 
3888
  if (range->timer)
 
3889
    {
 
3890
      if (range->timer->timeout_id != 0)
 
3891
        g_source_remove (range->timer->timeout_id);
 
3892
 
 
3893
      g_free (range->timer);
 
3894
 
 
3895
      range->timer = NULL;
 
3896
    }
 
3897
}
 
3898
 
 
3899
static gboolean
 
3900
update_timeout (gpointer data)
 
3901
{
 
3902
  GtkRange *range;
 
3903
 
 
3904
  range = GTK_RANGE (data);
 
3905
  gtk_range_update_value (range);
 
3906
  range->update_timeout_id = 0;
 
3907
 
 
3908
  /* self-remove */
 
3909
  return FALSE;
 
3910
}
 
3911
 
 
3912
static void
 
3913
gtk_range_reset_update_timer (GtkRange *range)
 
3914
{
 
3915
  gtk_range_remove_update_timer (range);
 
3916
 
 
3917
  range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
 
3918
                                            update_timeout,
 
3919
                                            range);
 
3920
}
 
3921
 
 
3922
static void
 
3923
gtk_range_remove_update_timer (GtkRange *range)
 
3924
{
 
3925
  if (range->update_timeout_id != 0)
 
3926
    {
 
3927
      g_source_remove (range->update_timeout_id);
 
3928
      range->update_timeout_id = 0;
 
3929
    }
 
3930
}
 
3931
 
 
3932
void
 
3933
_gtk_range_set_stop_values (GtkRange *range,
 
3934
                            gdouble  *values,
 
3935
                            gint      n_values)
 
3936
{
 
3937
  gint i;
 
3938
 
 
3939
  g_free (range->layout->marks);
 
3940
  range->layout->marks = g_new (gdouble, n_values);
 
3941
 
 
3942
  g_free (range->layout->mark_pos);
 
3943
  range->layout->mark_pos = g_new (gint, n_values);
 
3944
 
 
3945
  range->layout->n_marks = n_values;
 
3946
 
 
3947
  for (i = 0; i < n_values; i++) 
 
3948
    range->layout->marks[i] = values[i];
 
3949
 
 
3950
  range->layout->recalc_marks = TRUE;
 
3951
}
 
3952
 
 
3953
gint
 
3954
_gtk_range_get_stop_positions (GtkRange  *range,
 
3955
                               gint     **values)
 
3956
{
 
3957
  gtk_range_calc_marks (range);
 
3958
 
 
3959
  if (values)
 
3960
    *values = g_memdup (range->layout->mark_pos, range->layout->n_marks * sizeof (gint));
 
3961
 
 
3962
  return range->layout->n_marks;
 
3963
}
 
3964
 
 
3965
/**
 
3966
 * gtk_range_set_round_digits:
 
3967
 * @range: a #GtkRange
 
3968
 * @round_digits: the precision in digits, or -1
 
3969
 *
 
3970
 * Sets the number of digits to round the value to when
 
3971
 * it changes. See #GtkRange::change-value.
 
3972
 *
 
3973
 * Since: 2.24
 
3974
 */
 
3975
void
 
3976
gtk_range_set_round_digits (GtkRange *range,
 
3977
                            gint      round_digits)
 
3978
{
 
3979
  g_return_if_fail (GTK_IS_RANGE (range));
 
3980
  g_return_if_fail (round_digits >= -1);
 
3981
 
 
3982
  range->round_digits = round_digits;
 
3983
 
 
3984
  g_object_notify (G_OBJECT (range), "round-digits");
 
3985
}
 
3986
 
 
3987
/**
 
3988
 * gtk_range_get_round_digits:
 
3989
 * @range: a #GtkRange
 
3990
 *
 
3991
 * Gets the number of digits to round the value to when
 
3992
 * it changes. See #GtkRange::change-value.
 
3993
 *
 
3994
 * Return value: the number of digits to round to
 
3995
 *
 
3996
 * Since: 2.24
 
3997
 */
 
3998
gint
 
3999
gtk_range_get_round_digits (GtkRange *range)
 
4000
{
 
4001
  g_return_val_if_fail (GTK_IS_RANGE (range), -1);
 
4002
 
 
4003
  return range->round_digits;
 
4004
}
 
4005
 
 
4006
 
 
4007
#define __GTK_RANGE_C__
 
4008
#include "gtkaliasdef.c"