gtkrange.c 72.5 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3
 * Copyright (C) 2001 Red Hat, Inc.
Elliot Lee's avatar
Elliot Lee committed
4 5
 *
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10 11 12
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16 17 18
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
19
 */
20 21

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

28
#include <config.h>
Elliot Lee's avatar
Elliot Lee committed
29
#include <stdio.h>
30
#include <math.h>
31
#include "gtkintl.h"
Elliot Lee's avatar
Elliot Lee committed
32
#include "gtkmain.h"
33
#include "gtkmarshalers.h"
Elliot Lee's avatar
Elliot Lee committed
34
#include "gtkrange.h"
Alexander Larsson's avatar
Alexander Larsson committed
35
#include "gtkintl.h"
36
#include "gtkscrollbar.h"
Elliot Lee's avatar
Elliot Lee committed
37

38 39
#define SCROLL_INITIAL_DELAY 250  /* must hold button this long before ... */
#define SCROLL_LATER_DELAY   100  /* ... it starts repeating at this rate  */
40
#define UPDATE_DELAY         300  /* Delay for queued update */
Elliot Lee's avatar
Elliot Lee committed
41

42
enum {
Alexander Larsson's avatar
Alexander Larsson committed
43
  PROP_0,
44
  PROP_UPDATE_POLICY,
Havoc Pennington's avatar
Havoc Pennington committed
45 46
  PROP_ADJUSTMENT,
  PROP_INVERTED
47
};
Elliot Lee's avatar
Elliot Lee committed
48

49
enum {
50
  VALUE_CHANGED,
51
  ADJUST_BOUNDS,
52 53 54 55
  MOVE_SLIDER,
  LAST_SIGNAL
};

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
typedef enum {
  MOUSE_OUTSIDE,
  MOUSE_STEPPER_A,
  MOUSE_STEPPER_B,
  MOUSE_STEPPER_C,
  MOUSE_STEPPER_D,
  MOUSE_TROUGH,
  MOUSE_SLIDER,
  MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
} MouseLocation;

struct _GtkRangeLayout
{
  /* These are in widget->window coordinates */
  GdkRectangle stepper_a;
  GdkRectangle stepper_b;
  GdkRectangle stepper_c;
  GdkRectangle stepper_d;
  /* The trough rectangle is the area the thumb can slide in, not the
   * entire range_rect
   */
  GdkRectangle trough;
  GdkRectangle slider;

  /* Layout-related state */
  
  MouseLocation mouse_location;
  /* last mouse coords we got, or -1 if mouse is outside the range */
  gint mouse_x;
  gint mouse_y;
  /* "grabbed" mouse location, OUTSIDE for no grab */
  MouseLocation grab_location;
  gint grab_button; /* 0 if none */
};

91

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
static void gtk_range_class_init     (GtkRangeClass    *klass);
static void gtk_range_init           (GtkRange         *range);
static void gtk_range_set_property   (GObject          *object,
                                      guint             prop_id,
                                      const GValue     *value,
                                      GParamSpec       *pspec);
static void gtk_range_get_property   (GObject          *object,
                                      guint             prop_id,
                                      GValue           *value,
                                      GParamSpec       *pspec);
static void gtk_range_destroy        (GtkObject        *object);
static void gtk_range_finalize       (GObject          *object);
static void gtk_range_size_request   (GtkWidget        *widget,
                                      GtkRequisition   *requisition);
static void gtk_range_size_allocate  (GtkWidget        *widget,
                                      GtkAllocation    *allocation);
static void gtk_range_realize        (GtkWidget        *widget);
static void gtk_range_unrealize      (GtkWidget        *widget);
110 111
static void gtk_range_map            (GtkWidget        *widget);
static void gtk_range_unmap          (GtkWidget        *widget);
112 113 114 115 116 117 118 119 120 121 122 123
static gint gtk_range_expose         (GtkWidget        *widget,
                                      GdkEventExpose   *event);
static gint gtk_range_button_press   (GtkWidget        *widget,
                                      GdkEventButton   *event);
static gint gtk_range_button_release (GtkWidget        *widget,
                                      GdkEventButton   *event);
static gint gtk_range_motion_notify  (GtkWidget        *widget,
                                      GdkEventMotion   *event);
static gint gtk_range_enter_notify   (GtkWidget        *widget,
                                      GdkEventCrossing *event);
static gint gtk_range_leave_notify   (GtkWidget        *widget,
                                      GdkEventCrossing *event);
Matthias Clasen's avatar
Matthias Clasen committed
124 125 126 127
static void gtk_range_grab_notify    (GtkWidget          *widget,
				      gboolean            was_grabbed);
static void gtk_range_state_changed  (GtkWidget          *widget,
				      GtkStateType        previous_state);
128 129 130 131
static gint gtk_range_scroll_event   (GtkWidget        *widget,
                                      GdkEventScroll   *event);
static void gtk_range_style_set      (GtkWidget        *widget,
                                      GtkStyle         *previous_style);
132 133 134
static void update_slider_position   (GtkRange	       *range,
				      gint              mouse_x,
				      gint              mouse_y);
135 136 137


/* Range methods */
138

139
static void gtk_range_move_slider              (GtkRange         *range,
140 141 142 143 144 145
                                                GtkScrollType     scroll);

/* Internals */
static void          gtk_range_scroll                   (GtkRange      *range,
                                                         GtkScrollType  scroll);
static gboolean      gtk_range_update_mouse_location    (GtkRange      *range);
146 147
static void          gtk_range_calc_layout              (GtkRange      *range,
							 gdouble	adjustment_value);
148 149 150 151
static void          gtk_range_get_props                (GtkRange      *range,
                                                         gint          *slider_width,
                                                         gint          *stepper_size,
                                                         gint          *trough_border,
152 153 154
                                                         gint          *stepper_spacing,
							 gint          *arrow_displacement_x,
							 gint	       *arrow_displacement_y);
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
static void          gtk_range_calc_request             (GtkRange      *range,
                                                         gint           slider_width,
                                                         gint           stepper_size,
                                                         gint           trough_border,
                                                         gint           stepper_spacing,
                                                         GdkRectangle  *range_rect,
                                                         GtkBorder     *border,
                                                         gint          *n_steppers_p,
                                                         gint          *slider_length_p);
static void          gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
                                                         gpointer       data);
static void          gtk_range_adjustment_changed       (GtkAdjustment *adjustment,
                                                         gpointer       data);
static void          gtk_range_add_step_timer           (GtkRange      *range,
                                                         GtkScrollType  step);
static void          gtk_range_remove_step_timer        (GtkRange      *range);
static void          gtk_range_reset_update_timer       (GtkRange      *range);
static void          gtk_range_remove_update_timer      (GtkRange      *range);
static GdkRectangle* get_area                           (GtkRange      *range,
                                                         MouseLocation  location);
static void          gtk_range_internal_set_value       (GtkRange      *range,
                                                         gdouble        value);
static void          gtk_range_update_value             (GtkRange      *range);

Elliot Lee's avatar
Elliot Lee committed
179 180

static GtkWidgetClass *parent_class = NULL;
181
static guint signals[LAST_SIGNAL];
Elliot Lee's avatar
Elliot Lee committed
182 183


Manish Singh's avatar
Manish Singh committed
184
GType
185
gtk_range_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
186
{
Manish Singh's avatar
Manish Singh committed
187
  static GType range_type = 0;
Elliot Lee's avatar
Elliot Lee committed
188 189 190

  if (!range_type)
    {
191
      static const GTypeInfo range_info =
Elliot Lee's avatar
Elliot Lee committed
192 193
      {
	sizeof (GtkRangeClass),
Manish Singh's avatar
Manish Singh committed
194 195
	NULL,		/* base_init */
	NULL,		/* base_finalize */
196
	(GClassInitFunc) gtk_range_class_init,
Manish Singh's avatar
Manish Singh committed
197 198
	NULL,		/* class_finalize */
	NULL,		/* class_data */
199
	sizeof (GtkRange),
Manish Singh's avatar
Manish Singh committed
200
	0,		/* n_preallocs */
201
	(GInstanceInitFunc) gtk_range_init,
Manish Singh's avatar
Manish Singh committed
202
	NULL,		/* value_table */
Elliot Lee's avatar
Elliot Lee committed
203 204
      };

205 206
      range_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkRange",
					   &range_info, G_TYPE_FLAG_ABSTRACT);
Elliot Lee's avatar
Elliot Lee committed
207 208 209 210 211 212 213 214
    }

  return range_type;
}

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

Alexander Larsson's avatar
Alexander Larsson committed
219
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
220 221 222
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;

223
  parent_class = g_type_class_peek_parent (class);
Elliot Lee's avatar
Elliot Lee committed
224

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

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

  class->move_slider = gtk_range_move_slider;
248

249 250
  class->slider_detail = "slider";
  class->stepper_detail = "stepper";
251 252

  signals[VALUE_CHANGED] =
253
    g_signal_new ("value_changed",
Manish Singh's avatar
Manish Singh committed
254
                  G_TYPE_FROM_CLASS (gobject_class),
255 256 257
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, value_changed),
                  NULL, NULL,
258
                  _gtk_marshal_NONE__NONE,
259
                  G_TYPE_NONE, 0);
260
  
261 262
  signals[ADJUST_BOUNDS] =
    g_signal_new ("adjust_bounds",
Manish Singh's avatar
Manish Singh committed
263
                  G_TYPE_FROM_CLASS (gobject_class),
264 265 266 267 268 269 270
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
                  NULL, NULL,
                  _gtk_marshal_VOID__DOUBLE,
                  G_TYPE_NONE, 1,
                  G_TYPE_DOUBLE);
  
271
  signals[MOVE_SLIDER] =
272
    g_signal_new ("move_slider",
Manish Singh's avatar
Manish Singh committed
273
                  G_TYPE_FROM_CLASS (gobject_class),
274 275 276
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkRangeClass, move_slider),
                  NULL, NULL,
277
                  _gtk_marshal_VOID__ENUM,
278 279 280
                  G_TYPE_NONE, 1,
                  GTK_TYPE_SCROLL_TYPE);
  
Alexander Larsson's avatar
Alexander Larsson committed
281 282 283
  g_object_class_install_property (gobject_class,
                                   PROP_UPDATE_POLICY,
                                   g_param_spec_enum ("update_policy",
284 285
						      P_("Update policy"),
						      P_("How the range should be updated on the screen"),
Alexander Larsson's avatar
Alexander Larsson committed
286 287
						      GTK_TYPE_UPDATE_TYPE,
						      GTK_UPDATE_CONTINUOUS,
288 289
						      G_PARAM_READWRITE));
  
290 291 292
  g_object_class_install_property (gobject_class,
                                   PROP_ADJUSTMENT,
                                   g_param_spec_object ("adjustment",
293 294
							P_("Adjustment"),
							P_("The GtkAdjustment that contains the current value of this range object"),
295
                                                        GTK_TYPE_ADJUSTMENT,
296
                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
297

Havoc Pennington's avatar
Havoc Pennington committed
298 299 300
  g_object_class_install_property (gobject_class,
                                   PROP_INVERTED,
                                   g_param_spec_boolean ("inverted",
301 302
							P_("Inverted"),
							P_("Invert direction slider moves to increase range value"),
Havoc Pennington's avatar
Havoc Pennington committed
303 304 305
                                                         FALSE,
                                                         G_PARAM_READWRITE));
  
306 307
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("slider_width",
308 309
							     P_("Slider Width"),
							     P_("Width of scrollbar or scale thumb"),
310 311
							     0,
							     G_MAXINT,
312
							     14,
313 314 315
							     G_PARAM_READABLE));
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("trough_border",
316 317
                                                             P_("Trough Border"),
                                                             P_("Spacing between thumb/steppers and outer trough bevel"),
318 319 320 321
                                                             0,
                                                             G_MAXINT,
                                                             1,
                                                             G_PARAM_READABLE));
322 323
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("stepper_size",
324 325
							     P_("Stepper Size"),
							     P_("Length of step buttons at ends"),
326 327
							     0,
							     G_MAXINT,
328
							     14,
329 330 331
							     G_PARAM_READABLE));
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("stepper_spacing",
332 333
							     P_("Stepper Spacing"),
							     P_("Spacing between step buttons and thumb"),
334
                                                             0,
335
							     G_MAXINT,
336
							     0,
337
							     G_PARAM_READABLE));
338 339
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("arrow_displacement_x",
340 341
							     P_("Arrow X Displacement"),
							     P_("How far in the x direction to move the arrow when the button is depressed"),
342 343 344 345 346 347
							     G_MININT,
							     G_MAXINT,
							     0,
							     G_PARAM_READABLE));
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("arrow_displacement_y",
348 349
							     P_("Arrow Y Displacement"),
							     P_("How far in the y direction to move the arrow when the button is depressed"),
350 351 352 353
							     G_MININT,
							     G_MAXINT,
							     0,
							     G_PARAM_READABLE));
Elliot Lee's avatar
Elliot Lee committed
354 355
}

356
static void
Alexander Larsson's avatar
Alexander Larsson committed
357 358 359 360
gtk_range_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
361 362 363 364 365
{
  GtkRange *range;

  range = GTK_RANGE (object);

Alexander Larsson's avatar
Alexander Larsson committed
366
  switch (prop_id)
367
    {
Alexander Larsson's avatar
Alexander Larsson committed
368 369
    case PROP_UPDATE_POLICY:
      gtk_range_set_update_policy (range, g_value_get_enum (value));
370
      break;
371 372 373
    case PROP_ADJUSTMENT:
      gtk_range_set_adjustment (range, g_value_get_object (value));
      break;
Havoc Pennington's avatar
Havoc Pennington committed
374 375 376
    case PROP_INVERTED:
      gtk_range_set_inverted (range, g_value_get_boolean (value));
      break;
377
    default:
Alexander Larsson's avatar
Alexander Larsson committed
378
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
379 380 381 382 383
      break;
    }
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
384 385 386 387
gtk_range_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
388 389 390 391 392
{
  GtkRange *range;

  range = GTK_RANGE (object);

Alexander Larsson's avatar
Alexander Larsson committed
393
  switch (prop_id)
394
    {
Alexander Larsson's avatar
Alexander Larsson committed
395
    case PROP_UPDATE_POLICY:
396
      g_value_set_enum (value, range->update_policy);
397
      break;
398
    case PROP_ADJUSTMENT:
399
      g_value_set_object (value, range->adjustment);
400
      break;
Havoc Pennington's avatar
Havoc Pennington committed
401 402 403
    case PROP_INVERTED:
      g_value_set_boolean (value, range->inverted);
      break;
404
    default:
Alexander Larsson's avatar
Alexander Larsson committed
405
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
406 407 408 409
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
410 411 412
static void
gtk_range_init (GtkRange *range)
{
413 414
  GTK_WIDGET_SET_FLAGS (range, GTK_NO_WINDOW);

Elliot Lee's avatar
Elliot Lee committed
415
  range->adjustment = NULL;
416 417 418 419 420 421 422 423 424 425 426
  range->update_policy = GTK_UPDATE_CONTINUOUS;
  range->inverted = FALSE;
  range->flippable = FALSE;
  range->min_slider_size = 1;
  range->has_stepper_a = FALSE;
  range->has_stepper_b = FALSE;
  range->has_stepper_c = FALSE;
  range->has_stepper_d = FALSE;
  range->need_recalc = TRUE;
  range->round_digits = -1;
  range->layout = g_new0 (GtkRangeLayout, 1);
427 428 429
  range->layout->mouse_location = MOUSE_OUTSIDE;
  range->layout->mouse_x = -1;
  range->layout->mouse_y = -1;
430 431 432
  range->layout->grab_location = MOUSE_OUTSIDE;
  range->layout->grab_button = 0;
  range->timer = NULL;  
Elliot Lee's avatar
Elliot Lee committed
433 434
}

435 436 437 438 439 440 441 442 443 444 445
/**
 * gtk_range_get_adjustment:
 * @range: a #GtkRange
 * 
 * Get the #GtkAdjustment which is the "model" object for #GtkRange.
 * See gtk_range_set_adjustment() for details.
 * The return value does not have a reference added, so should not
 * be unreferenced.
 * 
 * Return value: a #GtkAdjustment
 **/
Elliot Lee's avatar
Elliot Lee committed
446 447 448 449 450
GtkAdjustment*
gtk_range_get_adjustment (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), NULL);

451 452 453
  if (!range->adjustment)
    gtk_range_set_adjustment (range, NULL);

Elliot Lee's avatar
Elliot Lee committed
454 455 456
  return range->adjustment;
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
/**
 * gtk_range_set_update_policy:
 * @range: a #GtkRange
 * @policy: update policy
 *
 * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
 * anytime the range slider is moved, the range value will change and the
 * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
 * the value will be updated after a brief timeout where no slider motion
 * occurs, so updates are spaced by a short time rather than
 * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
 * be updated when the user releases the button and ends the slider
 * drag operation.
 * 
 **/
Elliot Lee's avatar
Elliot Lee committed
472 473 474 475 476 477
void
gtk_range_set_update_policy (GtkRange      *range,
			     GtkUpdateType  policy)
{
  g_return_if_fail (GTK_IS_RANGE (range));

478
  if (range->update_policy != policy)
Alexander Larsson's avatar
Alexander Larsson committed
479
    {
480
      range->update_policy = policy;
Alexander Larsson's avatar
Alexander Larsson committed
481 482
      g_object_notify (G_OBJECT (range), "update_policy");
    }
Elliot Lee's avatar
Elliot Lee committed
483 484
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
/**
 * gtk_range_get_update_policy:
 * @range: a #GtkRange
 *
 * Gets the update policy of @range. See gtk_range_set_update_policy().
 *
 * Return value: the current update policy
 **/
GtkUpdateType
gtk_range_get_update_policy (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);

  return range->update_policy;
}

501 502 503 504 505 506 507 508 509 510 511 512 513 514
/**
 * gtk_range_set_adjustment:
 * @range: a #GtkRange
 * @adjustment: a #GtkAdjustment
 *
 * Sets the adjustment to be used as the "model" object for this range
 * widget. The adjustment indicates the current range value, the
 * minimum and maximum range values, the step/page increments used
 * for keybindings and scrolling, and the page size. The page size
 * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
 * indicates the size of the visible area of the widget being scrolled.
 * The page size affects the size of the scrollbar slider.
 * 
 **/
Elliot Lee's avatar
Elliot Lee committed
515 516 517 518 519
void
gtk_range_set_adjustment (GtkRange      *range,
			  GtkAdjustment *adjustment)
{
  g_return_if_fail (GTK_IS_RANGE (range));
520 521 522 523 524
  
  if (!adjustment)
    adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
  else
    g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
Elliot Lee's avatar
Elliot Lee committed
525

526
  if (range->adjustment != adjustment)
Elliot Lee's avatar
Elliot Lee committed
527
    {
528 529
      if (range->adjustment)
	{
Manish Singh's avatar
Manish Singh committed
530 531 532 533 534 535 536
	  g_signal_handlers_disconnect_by_func (range->adjustment,
						gtk_range_adjustment_changed,
						range);
	  g_signal_handlers_disconnect_by_func (range->adjustment,
						gtk_range_adjustment_value_changed,
						range);
	  g_object_unref (range->adjustment);
537
	}
538

539
      range->adjustment = adjustment;
Manish Singh's avatar
Manish Singh committed
540
      g_object_ref (adjustment);
541 542
      gtk_object_sink (GTK_OBJECT (adjustment));
      
Manish Singh's avatar
Manish Singh committed
543 544 545 546 547 548
      g_signal_connect (adjustment, "changed",
			G_CALLBACK (gtk_range_adjustment_changed),
			range);
      g_signal_connect (adjustment, "value_changed",
			G_CALLBACK (gtk_range_adjustment_value_changed),
			range);
549
      
550
      gtk_range_adjustment_changed (adjustment, range);
551
      g_object_notify (G_OBJECT (range), "adjustment");
Elliot Lee's avatar
Elliot Lee committed
552 553 554
    }
}

555 556 557 558 559 560 561 562 563 564 565
/**
 * gtk_range_set_inverted:
 * @range: a #GtkRange
 * @setting: %TRUE to invert the range
 *
 * Ranges normally move from lower to higher values as the
 * slider moves from top to bottom or left to right. Inverted
 * ranges have higher values at the top or on the right rather than
 * on the bottom or left.
 * 
 **/
566 567 568 569 570 571 572 573 574 575 576
void
gtk_range_set_inverted (GtkRange *range,
                        gboolean  setting)
{
  g_return_if_fail (GTK_IS_RANGE (range));
  
  setting = setting != FALSE;

  if (setting != range->inverted)
    {
      range->inverted = setting;
Havoc Pennington's avatar
Havoc Pennington committed
577
      g_object_notify (G_OBJECT (range), "inverted");
578 579 580 581
      gtk_widget_queue_resize (GTK_WIDGET (range));
    }
}

582 583 584 585 586 587 588 589
/**
 * gtk_range_get_inverted:
 * @range: a #GtkRange
 * 
 * Gets the value set by gtk_range_set_inverted().
 * 
 * Return value: %TRUE if the range is inverted
 **/
590 591 592 593 594 595 596 597
gboolean
gtk_range_get_inverted (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

  return range->inverted;
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
/**
 * gtk_range_set_increments:
 * @range: a #GtkRange
 * @step: step size
 * @page: page size
 *
 * Sets the step and page sizes for the range.
 * The step size is used when the user clicks the #GtkScrollbar
 * arrows or moves #GtkScale via arrow keys. The page size
 * is used for example when moving via Page Up or Page Down keys.
 * 
 **/
void
gtk_range_set_increments (GtkRange *range,
                          gdouble   step,
                          gdouble   page)
{
  g_return_if_fail (GTK_IS_RANGE (range));

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

  gtk_adjustment_changed (range->adjustment);
}

/**
 * gtk_range_set_range:
 * @range: a #GtkRange
 * @min: minimum range value
 * @max: maximum range value
 * 
 * Sets the allowable values in the #GtkRange, and clamps the range
630 631
 * value to be between @min and @max. (If the range has a non-zero
 * page size, it is clamped between @min and @max - page-size.)
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
 **/
void
gtk_range_set_range (GtkRange *range,
                     gdouble   min,
                     gdouble   max)
{
  gdouble value;
  
  g_return_if_fail (GTK_IS_RANGE (range));
  g_return_if_fail (min < max);
  
  range->adjustment->lower = min;
  range->adjustment->upper = max;

  value = CLAMP (range->adjustment->value,
                 range->adjustment->lower,
                 (range->adjustment->upper - range->adjustment->page_size));
649 650 651

  gtk_adjustment_set_value (range->adjustment, value);
  gtk_adjustment_changed (range->adjustment);
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
}

/**
 * gtk_range_set_value:
 * @range: a #GtkRange
 * @value: new value of the range
 *
 * Sets the current value of the range; if the value is outside the
 * minimum or maximum range values, it will be clamped to fit inside
 * them. The range emits the "value_changed" signal if the value
 * changes.
 * 
 **/
void
gtk_range_set_value (GtkRange *range,
                     gdouble   value)
{
  g_return_if_fail (GTK_IS_RANGE (range));
  
  value = CLAMP (value, range->adjustment->lower,
                 (range->adjustment->upper - range->adjustment->page_size));

  gtk_adjustment_set_value (range->adjustment, value);
}

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

  return range->adjustment->value;
}

693 694 695 696 697 698 699 700 701 702
static gboolean
should_invert (GtkRange *range)
{  
  if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
    return
      (range->inverted && !range->flippable) ||
      (range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_LTR) ||
      (!range->inverted && range->flippable && gtk_widget_get_direction (GTK_WIDGET (range)) == GTK_TEXT_DIR_RTL);
  else
    return range->inverted;
Elliot Lee's avatar
Elliot Lee committed
703 704
}

705 706
static void
gtk_range_finalize (GObject *object)
707
{
708
  GtkRange *range = GTK_RANGE (object);
Elliot Lee's avatar
Elliot Lee committed
709

710 711 712
  g_free (range->layout);

  (* G_OBJECT_CLASS (parent_class)->finalize) (object);
Elliot Lee's avatar
Elliot Lee committed
713 714
}

715 716
static void
gtk_range_destroy (GtkObject *object)
Elliot Lee's avatar
Elliot Lee committed
717
{
718
  GtkRange *range = GTK_RANGE (object);
719 720 721 722 723 724

  gtk_range_remove_step_timer (range);
  gtk_range_remove_update_timer (range);
  
  if (range->adjustment)
    {
Manish Singh's avatar
Manish Singh committed
725 726 727 728 729 730 731
      g_signal_handlers_disconnect_by_func (range->adjustment,
					    gtk_range_adjustment_changed,
					    range);
      g_signal_handlers_disconnect_by_func (range->adjustment,
					    gtk_range_adjustment_value_changed,
					    range);
      g_object_unref (range->adjustment);
732 733
      range->adjustment = NULL;
    }
Elliot Lee's avatar
Elliot Lee committed
734

735
  (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
Elliot Lee's avatar
Elliot Lee committed
736 737
}

738 739 740
static void
gtk_range_size_request (GtkWidget      *widget,
                        GtkRequisition *requisition)
Elliot Lee's avatar
Elliot Lee committed
741
{
742 743 744 745 746 747 748 749
  GtkRange *range;
  gint slider_width, stepper_size, trough_border, stepper_spacing;
  GdkRectangle range_rect;
  GtkBorder border;
  
  range = GTK_RANGE (widget);
  
  gtk_range_get_props (range,
750 751
                       &slider_width, &stepper_size, &trough_border, &stepper_spacing,
		       NULL, NULL);
752 753 754 755

  gtk_range_calc_request (range, 
                          slider_width, stepper_size, trough_border, stepper_spacing,
                          &range_rect, &border, NULL, NULL);
Elliot Lee's avatar
Elliot Lee committed
756

757 758
  requisition->width = range_rect.width + border.left + border.right;
  requisition->height = range_rect.height + border.top + border.bottom;
Elliot Lee's avatar
Elliot Lee committed
759 760
}

761 762 763
static void
gtk_range_size_allocate (GtkWidget     *widget,
                         GtkAllocation *allocation)
Elliot Lee's avatar
Elliot Lee committed
764
{
765 766 767 768
  GtkRange *range;

  range = GTK_RANGE (widget);

769 770
  widget->allocation = *allocation;
  
771
  range->need_recalc = TRUE;
772
  gtk_range_calc_layout (range, range->adjustment->value);
Elliot Lee's avatar
Elliot Lee committed
773

774 775 776 777 778 779
  if (GTK_WIDGET_REALIZED (range))
    gdk_window_move_resize (range->event_window,
			    widget->allocation.x,
			    widget->allocation.y,
			    widget->allocation.width,
			    widget->allocation.height);
Elliot Lee's avatar
Elliot Lee committed
780 781
}

782 783
static void
gtk_range_realize (GtkWidget *widget)
Elliot Lee's avatar
Elliot Lee committed
784
{
785 786 787
  GtkRange *range;
  GdkWindowAttr attributes;
  gint attributes_mask;  
Elliot Lee's avatar
Elliot Lee committed
788

789
  range = GTK_RANGE (widget);
Elliot Lee's avatar
Elliot Lee committed
790

791
  gtk_range_calc_layout (range, range->adjustment->value);
792 793 794
  
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

795
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
796
  g_object_ref (widget->window);
797
  
798 799 800 801 802
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
803
  attributes.wclass = GDK_INPUT_ONLY;
804
  attributes.event_mask = gtk_widget_get_events (widget);
805
  attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
806 807 808 809 810 811
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
                            GDK_POINTER_MOTION_MASK |
                            GDK_POINTER_MOTION_HINT_MASK);

812
  attributes_mask = GDK_WA_X | GDK_WA_Y;
813

814 815 816
  range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
					&attributes, attributes_mask);
  gdk_window_set_user_data (range->event_window, range);
817 818

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

821 822
static void
gtk_range_unrealize (GtkWidget *widget)
823
{
824
  GtkRange *range = GTK_RANGE (widget);
825

826 827 828
  gtk_range_remove_step_timer (range);
  gtk_range_remove_update_timer (range);
  
829 830 831 832
  gdk_window_set_user_data (range->event_window, NULL);
  gdk_window_destroy (range->event_window);
  range->event_window = NULL;
  
833 834
  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
835 836
}

837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
static void
gtk_range_map (GtkWidget *widget)
{
  GtkRange *range = GTK_RANGE (widget);
  
  g_return_if_fail (GTK_IS_RANGE (widget));

  gdk_window_show (range->event_window);

  GTK_WIDGET_CLASS (parent_class)->map (widget);
}

static void
gtk_range_unmap (GtkWidget *widget)
{
  GtkRange *range = GTK_RANGE (widget);
    
  g_return_if_fail (GTK_IS_RANGE (widget));

  gdk_window_hide (range->event_window);

  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}

861
static void
862 863 864 865 866 867
draw_stepper (GtkRange     *range,
              GdkRectangle *rect,
              GtkArrowType  arrow_type,
              gboolean      clicked,
              gboolean      prelighted,
              GdkRectangle *area)
868
{
869 870 871
  GtkStateType state_type;
  GtkShadowType shadow_type;
  GdkRectangle intersection;
872
  GtkWidget *widget = GTK_WIDGET (range);
873

874 875 876 877 878
  gint arrow_x;
  gint arrow_y;
  gint arrow_width;
  gint arrow_height;

879 880 881
  /* More to get the right clip region than for efficiency */
  if (!gdk_rectangle_intersect (area, rect, &intersection))
    return;
882 883 884

  intersection.x += widget->allocation.x;
  intersection.y += widget->allocation.y;
885 886 887 888 889 890 891 892 893 894 895 896
  
  if (!GTK_WIDGET_IS_SENSITIVE (range))
    state_type = GTK_STATE_INSENSITIVE;
  else if (clicked)
    state_type = GTK_STATE_ACTIVE;
  else if (prelighted)
    state_type = GTK_STATE_PRELIGHT;
  else 
    state_type = GTK_STATE_NORMAL;
  
  if (clicked)
    shadow_type = GTK_SHADOW_IN;
897
  else
898
    shadow_type = GTK_SHADOW_OUT;
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913

  gtk_paint_box (widget->style,
		 widget->window,
		 state_type, shadow_type,
		 &intersection, widget,
		 GTK_RANGE_GET_CLASS (range)->stepper_detail,
		 widget->allocation.x + rect->x,
		 widget->allocation.y + rect->y,
		 rect->width,
		 rect->height);

  arrow_width = rect->width / 2;
  arrow_height = rect->height / 2;
  arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
  arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
914
  
915 916 917 918 919 920 921 922 923 924 925 926 927 928
  if (clicked)
    {
      gint arrow_displacement_x;
      gint arrow_displacement_y;

      gtk_range_get_props (GTK_RANGE (widget), NULL, NULL, NULL, NULL,
			   &arrow_displacement_x, &arrow_displacement_y);
      
      arrow_x += arrow_displacement_x;
      arrow_y += arrow_displacement_y;
    }
  
  gtk_paint_arrow (widget->style,
                   widget->window,
929
                   state_type, shadow_type, 
930
                   &intersection, widget,
931 932
                   GTK_RANGE_GET_CLASS (range)->stepper_detail,
                   arrow_type,
933
                   TRUE,
934
		   arrow_x, arrow_y, arrow_width, arrow_height);
935 936
}

937 938 939
static gint
gtk_range_expose (GtkWidget      *widget,
		  GdkEventExpose *event)
Elliot Lee's avatar
Elliot Lee committed
940
{
941 942 943
  GtkRange *range;
  gboolean sensitive;
  GtkStateType state;
944
  GdkRectangle expose_area;	/* Relative to widget->allocation */
945
  GdkRectangle area;
946 947
  gint focus_line_width = 0;
  gint focus_padding = 0;
948

949
  range = GTK_RANGE (widget);
Elliot Lee's avatar
Elliot Lee committed
950

951 952 953 954 955 956 957 958
  if (GTK_WIDGET_CAN_FOCUS (range))
    {
      gtk_widget_style_get (GTK_WIDGET (range),
			    "focus-line-width", &focus_line_width,
			    "focus-padding", &focus_padding,
			    NULL);
    }
  
959 960 961 962
  expose_area = event->area;
  expose_area.x -= widget->allocation.x;
  expose_area.y -= widget->allocation.y;
  
963
  gtk_range_calc_layout (range, range->adjustment->value);
964

965
  sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
966

967 968 969 970 971 972 973
  /* Just to be confusing, we draw the trough for the whole
   * range rectangle, not the trough rectangle (the trough
   * rectangle is just for hit detection)
   */
  /* The gdk_rectangle_intersect is more to get the right
   * clip region (limited to range_rect) than for efficiency
   */
974
  if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
975
                               &area))
Elliot Lee's avatar
Elliot Lee committed
976
    {
977 978 979
      area.x += widget->allocation.x;
      area.y += widget->allocation.y;
      
980 981 982 983 984
      gtk_paint_box (widget->style,
                     widget->window,
                     sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
                     GTK_SHADOW_IN,
                     &area, GTK_WIDGET(range), "trough",
985 986 987 988
                     widget->allocation.x + range->range_rect.x + focus_line_width + focus_padding,
                     widget->allocation.y + range->range_rect.y + focus_line_width + focus_padding,
                     range->range_rect.width - 2 * (focus_line_width + focus_padding),
                     range->range_rect.height - 2 * (focus_line_width + focus_padding));
989
      
990 991 992
                 
      if (sensitive &&
          GTK_WIDGET_HAS_FOCUS (range))
993
        gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
994
                         &area, widget, "trough",
995 996
                         widget->allocation.x + range->range_rect.x,
                         widget->allocation.y + range->range_rect.y,
997 998
                         range->range_rect.width,
                         range->range_rect.height);
Elliot Lee's avatar
Elliot Lee committed
999 1000
    }

1001 1002 1003 1004 1005 1006
  if (!sensitive)
    state = GTK_STATE_INSENSITIVE;
  else if (range->layout->mouse_location == MOUSE_SLIDER)
    state = GTK_STATE_PRELIGHT;
  else
    state = GTK_STATE_NORMAL;
1007

1008
  if (gdk_rectangle_intersect (&expose_area,
1009 1010
                               &range->layout->slider,
                               &area))
Elliot Lee's avatar
Elliot Lee committed
1011
    {
1012 1013 1014
      area.x += widget->allocation.x;
      area.y += widget->allocation.y;
      
1015 1016 1017 1018
      gtk_paint_slider (widget->style,
                        widget->window,
                        state,
                        GTK_SHADOW_OUT,
1019
                        &area,
1020 1021
                        widget,
                        GTK_RANGE_GET_CLASS (range)->slider_detail,
1022 1023
                        widget->allocation.x + range->layout->slider.x,
                        widget->allocation.y + range->layout->slider.y,
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
                        range->layout->slider.width,
                        range->layout->slider.height,
                        range->orientation);
    }
  
  if (range->has_stepper_a)
    draw_stepper (range, &range->layout->stepper_a,
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
                  range->layout->grab_location == MOUSE_STEPPER_A,
                  range->layout->mouse_location == MOUSE_STEPPER_A,
1034
                  &expose_area);
1035 1036 1037 1038 1039 1040

  if (range->has_stepper_b)
    draw_stepper (range, &range->layout->stepper_b,
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
                  range->layout->grab_location == MOUSE_STEPPER_B,
                  range->layout->mouse_location == MOUSE_STEPPER_B,
1041
                  &expose_area);
1042 1043 1044 1045 1046 1047

  if (range->has_stepper_c)
    draw_stepper (range, &range->layout->stepper_c,
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
                  range->layout->grab_location == MOUSE_STEPPER_C,
                  range->layout->mouse_location == MOUSE_STEPPER_C,
1048
                  &expose_area);
1049 1050 1051 1052 1053 1054

  if (range->has_stepper_d)
    draw_stepper (range, &range->layout->stepper_d,
                  range->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
                  range->layout->grab_location == MOUSE_STEPPER_D,
                  range->layout->mouse_location == MOUSE_STEPPER_D,
1055
                  &expose_area);
1056 1057 1058
  
  return FALSE;
}
Elliot Lee's avatar
Elliot Lee committed
1059

1060 1061 1062 1063 1064 1065
static void
range_grab_add (GtkRange      *range,
                MouseLocation  location,
                gint           button)
{
  /* we don't actually gtk_grab, since a button is down */
Elliot Lee's avatar
Elliot Lee committed
1066

1067 1068
  gtk_grab_add (GTK_WIDGET (range));
  
1069 1070 1071 1072 1073
  range->layout->grab_location = location;
  range->layout->grab_button = button;
  
  if (gtk_range_update_mouse_location (range))
    gtk_widget_queue_draw (GTK_WIDGET (range));
Elliot Lee's avatar
Elliot Lee committed
1074 1075
}

1076 1077
static void
range_grab_remove (GtkRange *range)
Elliot Lee's avatar
Elliot Lee committed
1078
{
1079 1080
  gtk_grab_remove (GTK_WIDGET (range));
  
1081 1082
  range->layout->grab_location = MOUSE_OUTSIDE;
  range->layout->grab_button = 0;
Elliot Lee's avatar
Elliot Lee committed
1083

1084 1085 1086
  if (gtk_range_update_mouse_location (range))
    gtk_widget_queue_draw (GTK_WIDGET (range));
}
Elliot Lee's avatar
Elliot Lee committed
1087

1088 1089
static GtkScrollType
range_get_scroll_for_grab (GtkRange      *range)
1090 1091 1092 1093
{ 
  gboolean invert;

  invert = should_invert (range);
1094 1095 1096 1097 1098 1099 1100 1101
  switch (range->layout->grab_location)
    {
      /* Backward stepper */
    case MOUSE_STEPPER_A:
    case MOUSE_STEPPER_C:
      switch (range->layout->grab_button)
        {
        case 1:
1102
          return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
1103 1104
          break;
        case 2:
1105
          return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
1106 1107
          break;
        case 3:
1108
          return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
1109 1110 1111
          break;
        }
      break;
Elliot Lee's avatar
Elliot Lee committed
1112

1113 1114 1115 1116 1117 1118
      /* Forward stepper */
    case MOUSE_STEPPER_B:
    case MOUSE_STEPPER_D:
      switch (range->layout->grab_button)
        {
        case 1:
1119
          return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
1120 1121
          break;
        case 2:
1122
          return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
1123 1124
          break;
        case 3:
1125
          return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
1126
          break;
1127
       }
1128
      break;
1129

1130 1131 1132 1133
      /* In the trough */
    case MOUSE_TROUGH:
      {
        if (range->trough_click_forward)
1134
	  return GTK_SCROLL_PAGE_FORWARD;
1135
        else
1136
	  return GTK_SCROLL_PAGE_BACKWARD;
1137 1138
      }
      break;
1139

1140 1141 1142 1143
    case MOUSE_OUTSIDE:
    case MOUSE_SLIDER:
    case MOUSE_WIDGET:
      break;
Elliot Lee's avatar
Elliot Lee committed
1144 1145
    }

1146
  return GTK_SCROLL_NONE;
Elliot Lee's avatar
Elliot Lee committed
1147 1148
}

1149 1150 1151
static gdouble
coord_to_value (GtkRange *range,
                gint      coord)
Elliot Lee's avatar
Elliot Lee committed
1152
{
1153 1154
  gdouble frac;
  gdouble value;
1155
  
1156
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
Owen Taylor's avatar
Owen Taylor committed
1157 1158 1159 1160 1161
    if (range->layout->trough.height == range->layout->slider.height)
      frac = 1.0;
    else 
      frac = ((coord - range->layout->trough.y) /
	      (gdouble) (range->layout->trough.height - range->layout->slider.height));
1162
  else
Owen Taylor's avatar
Owen Taylor committed
1163 1164 1165 1166 1167
    if (range->layout->trough.width == range->layout->slider.width)
      frac = 1.0;
    else
      frac = ((coord - range->layout->trough.x) /
	      (gdouble) (range->layout->trough.width - range->layout->slider.width));
1168

1169 1170
  if (should_invert (range))
    frac = 1.0 - frac;
1171
  
1172 1173
  value = range->adjustment->lower +
    frac * (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size);
Elliot Lee's avatar
Elliot Lee committed
1174

1175
  return value;
Elliot Lee's avatar
Elliot Lee committed
1176 1177
}

1178 1179 1180
static gint
gtk_range_button_press (GtkWidget      *widget,
			GdkEventButton *event)
Elliot Lee's avatar
Elliot Lee committed
1181
{
1182
  GtkRange *range = GTK_RANGE (widget);
1183 1184 1185
  
  if (!GTK_WIDGET_HAS_FOCUS (widget))
    gtk_widget_grab_focus (widget);
Elliot Lee's avatar
Elliot Lee committed
1186

1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
  /* ignore presses when we're already doing something else. */
  if (range->layout->grab_location != MOUSE_OUTSIDE)
    return FALSE;

  range->layout->mouse_x = event->x;
  range->layout->mouse_y = event->y;
  if (gtk_range_update_mouse_location (range))
    gtk_widget_queue_draw (widget);
    
  if (range->layout->mouse_location == MOUSE_TROUGH  &&
1197
      event->button == 1)
Elliot Lee's avatar
Elliot Lee committed
1198
    {
1199
      /* button 1 steps by page increment, as with button 2 on a stepper
1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
       */
      GtkScrollType scroll;
      gdouble click_value;
      
      click_value = coord_to_value (range,
                                    range->orientation == GTK_ORIENTATION_VERTICAL ?
                                    event->y : event->x);
      
      range->trough_click_forward = click_value > range->adjustment->value;
      range_grab_add (range, MOUSE_TROUGH, event->button);
      
      scroll = range_get_scroll_for_grab (range);
      
      gtk_range_add_step_timer (range, scroll);
Elliot Lee's avatar
Elliot Lee committed
1214

1215
      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1216
    }
1217 1218 1219 1220 1221
  else if ((range->layout->mouse_location == MOUSE_STEPPER_A ||
            range->layout->mouse_location == MOUSE_STEPPER_B ||
            range->layout->mouse_location == MOUSE_STEPPER_C ||
            range->layout->mouse_location == MOUSE_STEPPER_D) &&
           (event->button == 1 || event->button == 2 || event->button == 3))
Elliot Lee's avatar
Elliot Lee committed
1222
    {
1223 1224 1225 1226 1227 1228 1229
      GdkRectangle *stepper_area;
      GtkScrollType scroll;
      
      range_grab_add (range, range->layout->mouse_location, event->button);

      stepper_area = get_area (range, range->layout->mouse_location);
      gtk_widget_queue_draw_area (widget,
1230 1231
                                  widget->allocation.x + stepper_area->x,
                                  widget->allocation.y + stepper_area->y,
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
                                  stepper_area->width,
                                  stepper_area->height);

      scroll = range_get_scroll_for_grab (range);
      if (scroll != GTK_SCROLL_NONE)
        gtk_range_add_step_timer (range, scroll);
      
      return TRUE;
    }
  else if ((range->layout->mouse_location == MOUSE_TROUGH &&
            event->button == 2) ||
           range->layout->mouse_location == MOUSE_SLIDER)
    {
1245 1246
      gboolean need_value_update = FALSE;

1247 1248 1249 1250 1251 1252 1253
      /* Any button can be used to drag the slider, but you can start
       * dragging the slider with a trough click using button 2;
       * On button 2 press, we warp the slider to mouse position,
       * then begin the slider drag.
       */
      if (event->button == 2)
        {
1254
          gdouble slider_low_value, slider_high_value, new_value;
1255
          
1256 1257 1258 1259 1260 1261 1262 1263 1264
          slider_high_value =
            coord_to_value (range,
                            range->orientation == GTK_ORIENTATION_VERTICAL ?
                            event->y : event->x);
          slider_low_value =
            coord_to_value (range,
                            range->orientation == GTK_ORIENTATION_VERTICAL ?
                            event->y - range->layout->slider.height :
                            event->x - range->layout->slider.width);
1265
          
1266 1267
          /* compute new value for warped slider */
          new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
1268

1269
	  /* recalc slider, so we can set slide_initial_slider_position
1270 1271
           * properly
           */
1272 1273 1274 1275 1276 1277 1278
	  range->need_recalc = TRUE;
          gtk_range_calc_layout (range, new_value);

	  /* defer adjustment updates to update_slider_position() in order
	   * to keep pixel quantisation
	   */
	  need_value_update = TRUE;
1279 1280 1281 1282 1283 1284 1285
        }
      
      if (range->orientation == GTK_ORIENTATION_VERTICAL)
        {
          range->slide_initial_slider_position = range->layout->slider.y;
          range->slide_initial_coordinate = event->y;
        }
Elliot Lee's avatar
Elliot Lee committed
1286
      else
1287 1288 1289 1290 1291
        {
          range->slide_initial_slider_position = range->layout->slider.x;
          range->slide_initial_coordinate = event->x;
        }

1292 1293 1294
      if (need_value_update)
        update_slider_position (range, event->x, event->y);

1295 1296 1297
      range_grab_add (range, MOUSE_SLIDER, event->button);
      
      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1298
    }
1299 1300
  
  return FALSE;
Elliot Lee's avatar