gtkrange.c 71 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/. 
 */

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

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

39
enum {
Alexander Larsson's avatar
Alexander Larsson committed
40
  PROP_0,
41
  PROP_UPDATE_POLICY,
42 43
  PROP_ADJUSTMENT,
  PROP_INVERTED
44
};
Elliot Lee's avatar
Elliot Lee committed
45

46
enum {
47
  VALUE_CHANGED,
48
  ADJUST_BOUNDS,
49 50 51 52
  MOVE_SLIDER,
  LAST_SIGNAL
};

53 54 55 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
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 */
};

88

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
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);
107 108
static void gtk_range_map            (GtkWidget        *widget);
static void gtk_range_unmap          (GtkWidget        *widget);
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
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);
static gint gtk_range_scroll_event   (GtkWidget        *widget,
                                      GdkEventScroll   *event);
static void gtk_range_style_set      (GtkWidget        *widget,
                                      GtkStyle         *previous_style);
125 126 127
static void update_slider_position   (GtkRange	       *range,
				      gint              mouse_x,
				      gint              mouse_y);
128 129 130


/* Range methods */
131

132
static void gtk_range_move_slider              (GtkRange         *range,
133 134 135 136 137 138
                                                GtkScrollType     scroll);

/* Internals */
static void          gtk_range_scroll                   (GtkRange      *range,
                                                         GtkScrollType  scroll);
static gboolean      gtk_range_update_mouse_location    (GtkRange      *range);
139 140
static void          gtk_range_calc_layout              (GtkRange      *range,
							 gdouble	adjustment_value);
141 142 143 144
static void          gtk_range_get_props                (GtkRange      *range,
                                                         gint          *slider_width,
                                                         gint          *stepper_size,
                                                         gint          *trough_border,
145 146 147
                                                         gint          *stepper_spacing,
							 gint          *arrow_displacement_x,
							 gint	       *arrow_displacement_y);
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
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
172 173

static GtkWidgetClass *parent_class = NULL;
174
static guint signals[LAST_SIGNAL];
Elliot Lee's avatar
Elliot Lee committed
175 176


Manish Singh's avatar
Manish Singh committed
177
GType
178
gtk_range_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
179
{
Manish Singh's avatar
Manish Singh committed
180
  static GType range_type = 0;
Elliot Lee's avatar
Elliot Lee committed
181 182 183

  if (!range_type)
    {
184
      static const GTypeInfo range_info =
Elliot Lee's avatar
Elliot Lee committed
185 186
      {
	sizeof (GtkRangeClass),
Manish Singh's avatar
Manish Singh committed
187 188
	NULL,		/* base_init */
	NULL,		/* base_finalize */
189
	(GClassInitFunc) gtk_range_class_init,
Manish Singh's avatar
Manish Singh committed
190 191
	NULL,		/* class_finalize */
	NULL,		/* class_data */
192
	sizeof (GtkRange),
Manish Singh's avatar
Manish Singh committed
193
	0,		/* n_preallocs */
194
	(GInstanceInitFunc) gtk_range_init,
Manish Singh's avatar
Manish Singh committed
195
	NULL,		/* value_table */
Elliot Lee's avatar
Elliot Lee committed
196 197
      };

198 199
      range_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkRange",
					   &range_info, G_TYPE_FLAG_ABSTRACT);
Elliot Lee's avatar
Elliot Lee committed
200 201 202 203 204 205 206 207
    }

  return range_type;
}

static void
gtk_range_class_init (GtkRangeClass *class)
{
Alexander Larsson's avatar
Alexander Larsson committed
208
  GObjectClass   *gobject_class;
Elliot Lee's avatar
Elliot Lee committed
209 210 211
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;

Alexander Larsson's avatar
Alexander Larsson committed
212
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
213 214 215
  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;

216
  parent_class = g_type_class_peek_parent (class);
Elliot Lee's avatar
Elliot Lee committed
217

Alexander Larsson's avatar
Alexander Larsson committed
218 219
  gobject_class->set_property = gtk_range_set_property;
  gobject_class->get_property = gtk_range_get_property;
220
  gobject_class->finalize = gtk_range_finalize;
221
  object_class->destroy = gtk_range_destroy;
Elliot Lee's avatar
Elliot Lee committed
222

223 224 225 226
  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;  
227 228
  widget_class->map = gtk_range_map;
  widget_class->unmap = gtk_range_unmap;
Elliot Lee's avatar
Elliot Lee committed
229 230 231 232
  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;
233
  widget_class->scroll_event = gtk_range_scroll_event;
Elliot Lee's avatar
Elliot Lee committed
234 235
  widget_class->enter_notify_event = gtk_range_enter_notify;
  widget_class->leave_notify_event = gtk_range_leave_notify;
236
  widget_class->style_set = gtk_range_style_set;
237 238

  class->move_slider = gtk_range_move_slider;
239

240 241
  class->slider_detail = "slider";
  class->stepper_detail = "stepper";
242 243

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

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

347
static void
Alexander Larsson's avatar
Alexander Larsson committed
348 349 350 351
gtk_range_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
352 353 354 355 356
{
  GtkRange *range;

  range = GTK_RANGE (object);

Alexander Larsson's avatar
Alexander Larsson committed
357
  switch (prop_id)
358
    {
Alexander Larsson's avatar
Alexander Larsson committed
359 360
    case PROP_UPDATE_POLICY:
      gtk_range_set_update_policy (range, g_value_get_enum (value));
361
      break;
362 363 364
    case PROP_ADJUSTMENT:
      gtk_range_set_adjustment (range, g_value_get_object (value));
      break;
365 366 367
    case PROP_INVERTED:
      gtk_range_set_inverted (range, g_value_get_boolean (value));
      break;
368
    default:
Alexander Larsson's avatar
Alexander Larsson committed
369
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370 371 372 373 374
      break;
    }
}

static void
Alexander Larsson's avatar
Alexander Larsson committed
375 376 377 378
gtk_range_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
379 380 381 382 383
{
  GtkRange *range;

  range = GTK_RANGE (object);

Alexander Larsson's avatar
Alexander Larsson committed
384
  switch (prop_id)
385
    {
Alexander Larsson's avatar
Alexander Larsson committed
386
    case PROP_UPDATE_POLICY:
387
      g_value_set_enum (value, range->update_policy);
388
      break;
389
    case PROP_ADJUSTMENT:
390
      g_value_set_object (value, range->adjustment);
391
      break;
392 393 394
    case PROP_INVERTED:
      g_value_set_boolean (value, range->inverted);
      break;
395
    default:
Alexander Larsson's avatar
Alexander Larsson committed
396
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
397 398 399 400
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
401 402 403
static void
gtk_range_init (GtkRange *range)
{
404 405
  GTK_WIDGET_SET_FLAGS (range, GTK_NO_WINDOW);

Elliot Lee's avatar
Elliot Lee committed
406
  range->adjustment = NULL;
407 408 409 410 411 412 413 414 415 416 417
  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);
418 419 420
  range->layout->mouse_location = MOUSE_OUTSIDE;
  range->layout->mouse_x = -1;
  range->layout->mouse_y = -1;
421 422 423
  range->layout->grab_location = MOUSE_OUTSIDE;
  range->layout->grab_button = 0;
  range->timer = NULL;  
Elliot Lee's avatar
Elliot Lee committed
424 425
}

426 427 428 429 430 431 432 433 434 435 436
/**
 * 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
437 438 439 440 441
GtkAdjustment*
gtk_range_get_adjustment (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), NULL);

442 443 444
  if (!range->adjustment)
    gtk_range_set_adjustment (range, NULL);

Elliot Lee's avatar
Elliot Lee committed
445 446 447
  return range->adjustment;
}

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
/**
 * 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
463 464 465 466 467 468
void
gtk_range_set_update_policy (GtkRange      *range,
			     GtkUpdateType  policy)
{
  g_return_if_fail (GTK_IS_RANGE (range));

469
  if (range->update_policy != policy)
Alexander Larsson's avatar
Alexander Larsson committed
470
    {
471
      range->update_policy = policy;
Alexander Larsson's avatar
Alexander Larsson committed
472 473
      g_object_notify (G_OBJECT (range), "update_policy");
    }
Elliot Lee's avatar
Elliot Lee committed
474 475
}

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
/**
 * 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;
}

492 493 494 495 496 497 498 499 500 501 502 503 504 505
/**
 * 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
506 507 508 509 510
void
gtk_range_set_adjustment (GtkRange      *range,
			  GtkAdjustment *adjustment)
{
  g_return_if_fail (GTK_IS_RANGE (range));
511 512 513 514 515
  
  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
516

517
  if (range->adjustment != adjustment)
Elliot Lee's avatar
Elliot Lee committed
518
    {
519 520
      if (range->adjustment)
	{
Manish Singh's avatar
Manish Singh committed
521 522 523 524 525 526 527
	  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);
528
	}
529

530
      range->adjustment = adjustment;
Manish Singh's avatar
Manish Singh committed
531
      g_object_ref (adjustment);
532 533
      gtk_object_sink (GTK_OBJECT (adjustment));
      
Manish Singh's avatar
Manish Singh committed
534 535 536 537 538 539
      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);
540
      
541
      gtk_range_adjustment_changed (adjustment, range);
542
      g_object_notify (G_OBJECT (range), "adjustment");
Elliot Lee's avatar
Elliot Lee committed
543 544 545
    }
}

546 547 548 549 550 551 552 553 554 555 556
/**
 * 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.
 * 
 **/
557 558 559 560 561 562 563 564 565 566 567
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;
568
      g_object_notify (G_OBJECT (range), "inverted");
569 570 571 572
      gtk_widget_queue_resize (GTK_WIDGET (range));
    }
}

573 574 575 576 577 578 579 580
/**
 * gtk_range_get_inverted:
 * @range: a #GtkRange
 * 
 * Gets the value set by gtk_range_set_inverted().
 * 
 * Return value: %TRUE if the range is inverted
 **/
581 582 583 584 585 586 587 588
gboolean
gtk_range_get_inverted (GtkRange *range)
{
  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);

  return range->inverted;
}

589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
/**
 * 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
621 622
 * 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.)
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
 **/
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));
640 641 642

  gtk_adjustment_set_value (range->adjustment, value);
  gtk_adjustment_changed (range->adjustment);
643 644 645 646 647 648 649 650 651 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
}

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

684 685 686 687 688 689 690 691 692 693
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
694 695
}

696 697
static void
gtk_range_finalize (GObject *object)
698
{
699
  GtkRange *range = GTK_RANGE (object);
Elliot Lee's avatar
Elliot Lee committed
700

701 702 703
  g_free (range->layout);

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

706 707
static void
gtk_range_destroy (GtkObject *object)
Elliot Lee's avatar
Elliot Lee committed
708
{
709
  GtkRange *range = GTK_RANGE (object);
710 711 712 713 714 715

  gtk_range_remove_step_timer (range);
  gtk_range_remove_update_timer (range);
  
  if (range->adjustment)
    {
Manish Singh's avatar
Manish Singh committed
716 717 718 719 720 721 722
      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);
723 724
      range->adjustment = NULL;
    }
Elliot Lee's avatar
Elliot Lee committed
725

726
  (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
Elliot Lee's avatar
Elliot Lee committed
727 728
}

729 730 731
static void
gtk_range_size_request (GtkWidget      *widget,
                        GtkRequisition *requisition)
Elliot Lee's avatar
Elliot Lee committed
732
{
733 734 735 736 737 738 739 740
  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,
741 742
                       &slider_width, &stepper_size, &trough_border, &stepper_spacing,
		       NULL, NULL);
743 744 745 746

  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
747

748 749
  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
750 751
}

752 753 754
static void
gtk_range_size_allocate (GtkWidget     *widget,
                         GtkAllocation *allocation)
Elliot Lee's avatar
Elliot Lee committed
755
{
756 757 758 759
  GtkRange *range;

  range = GTK_RANGE (widget);

760 761
  widget->allocation = *allocation;
  
762
  range->need_recalc = TRUE;
763
  gtk_range_calc_layout (range, range->adjustment->value);
Elliot Lee's avatar
Elliot Lee committed
764

765 766 767 768 769 770
  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
771 772
}

773 774
static void
gtk_range_realize (GtkWidget *widget)
Elliot Lee's avatar
Elliot Lee committed
775
{
776 777 778
  GtkRange *range;
  GdkWindowAttr attributes;
  gint attributes_mask;  
Elliot Lee's avatar
Elliot Lee committed
779

780
  range = GTK_RANGE (widget);
Elliot Lee's avatar
Elliot Lee committed
781

782
  gtk_range_calc_layout (range, range->adjustment->value);
783 784 785
  
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);

786
  widget->window = gtk_widget_get_parent_window (widget);
Manish Singh's avatar
Manish Singh committed
787
  g_object_ref (widget->window);
788
  
789 790 791 792 793
  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;
794
  attributes.wclass = GDK_INPUT_ONLY;
795 796 797 798 799 800 801 802 803
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
			    GDK_BUTTON_PRESS_MASK |
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_ENTER_NOTIFY_MASK |
			    GDK_LEAVE_NOTIFY_MASK |
                            GDK_POINTER_MOTION_MASK |
                            GDK_POINTER_MOTION_HINT_MASK);

804
  attributes_mask = GDK_WA_X | GDK_WA_Y;
805

806 807 808
  range->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
					&attributes, attributes_mask);
  gdk_window_set_user_data (range->event_window, range);
809 810

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

813 814
static void
gtk_range_unrealize (GtkWidget *widget)
815
{
816
  GtkRange *range = GTK_RANGE (widget);
817

818 819 820
  gtk_range_remove_step_timer (range);
  gtk_range_remove_update_timer (range);
  
821 822 823 824
  gdk_window_set_user_data (range->event_window, NULL);
  gdk_window_destroy (range->event_window);
  range->event_window = NULL;
  
825 826
  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
827 828
}

829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
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);
}

853
static void
854 855 856 857 858 859
draw_stepper (GtkRange     *range,
              GdkRectangle *rect,
              GtkArrowType  arrow_type,
              gboolean      clicked,
              gboolean      prelighted,
              GdkRectangle *area)
860
{
861 862 863
  GtkStateType state_type;
  GtkShadowType shadow_type;
  GdkRectangle intersection;
864
  GtkWidget *widget = GTK_WIDGET (range);
865

866 867 868 869 870
  gint arrow_x;
  gint arrow_y;
  gint arrow_width;
  gint arrow_height;

871 872 873
  /* More to get the right clip region than for efficiency */
  if (!gdk_rectangle_intersect (area, rect, &intersection))
    return;
874 875 876

  intersection.x += widget->allocation.x;
  intersection.y += widget->allocation.y;
877 878 879 880 881 882 883 884 885 886 887 888
  
  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;
889
  else
890
    shadow_type = GTK_SHADOW_OUT;
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905

  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;
906
  
907 908 909 910 911 912 913 914 915 916 917 918 919 920
  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,
921
                   state_type, shadow_type, 
922
                   &intersection, widget,
923 924
                   GTK_RANGE_GET_CLASS (range)->stepper_detail,
                   arrow_type,
925
                   TRUE,
926
		   arrow_x, arrow_y, arrow_width, arrow_height);
927 928
}

929 930 931
static gint
gtk_range_expose (GtkWidget      *widget,
		  GdkEventExpose *event)
Elliot Lee's avatar
Elliot Lee committed
932
{
933 934 935
  GtkRange *range;
  gboolean sensitive;
  GtkStateType state;
936
  GdkRectangle expose_area;	/* Relative to widget->allocation */
937
  GdkRectangle area;
938 939
  gint focus_line_width = 0;
  gint focus_padding = 0;
940

941
  range = GTK_RANGE (widget);
Elliot Lee's avatar
Elliot Lee committed
942

943 944 945 946 947 948 949 950
  if (GTK_WIDGET_CAN_FOCUS (range))
    {
      gtk_widget_style_get (GTK_WIDGET (range),
			    "focus-line-width", &focus_line_width,
			    "focus-padding", &focus_padding,
			    NULL);
    }
  
951 952 953 954
  expose_area = event->area;
  expose_area.x -= widget->allocation.x;
  expose_area.y -= widget->allocation.y;
  
955
  gtk_range_calc_layout (range, range->adjustment->value);
956

957
  sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
958

959 960 961 962 963 964 965
  /* 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
   */
966
  if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
967
                               &area))
Elliot Lee's avatar
Elliot Lee committed
968
    {
969 970 971
      area.x += widget->allocation.x;
      area.y += widget->allocation.y;
      
972 973 974 975 976
      gtk_paint_box (widget->style,
                     widget->window,
                     sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
                     GTK_SHADOW_IN,
                     &area, GTK_WIDGET(range), "trough",
977 978 979 980
                     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));
981
      
982 983 984
                 
      if (sensitive &&
          GTK_WIDGET_HAS_FOCUS (range))
985
        gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
986
                         &area, widget, "trough",
987 988
                         widget->allocation.x + range->range_rect.x,
                         widget->allocation.y + range->range_rect.y,
989 990
                         range->range_rect.width,
                         range->range_rect.height);
Elliot Lee's avatar
Elliot Lee committed
991 992
    }

993 994 995 996 997 998
  if (!sensitive)
    state = GTK_STATE_INSENSITIVE;
  else if (range->layout->mouse_location == MOUSE_SLIDER)
    state = GTK_STATE_PRELIGHT;
  else
    state = GTK_STATE_NORMAL;
999

1000
  if (gdk_rectangle_intersect (&expose_area,
1001 1002
                               &range->layout->slider,
                               &area))
Elliot Lee's avatar
Elliot Lee committed
1003
    {
1004 1005 1006
      area.x += widget->allocation.x;
      area.y += widget->allocation.y;
      
1007 1008 1009 1010
      gtk_paint_slider (widget->style,
                        widget->window,
                        state,
                        GTK_SHADOW_OUT,
1011
                        &area,
1012 1013
                        widget,
                        GTK_RANGE_GET_CLASS (range)->slider_detail,
1014 1015
                        widget->allocation.x + range->layout->slider.x,
                        widget->allocation.y + range->layout->slider.y,
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
                        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,
1026
                  &expose_area);
1027 1028 1029 1030 1031 1032

  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,
1033
                  &expose_area);
1034 1035 1036 1037 1038 1039

  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,
1040
                  &expose_area);
1041 1042 1043 1044 1045 1046

  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,
1047
                  &expose_area);
1048 1049 1050
  
  return FALSE;
}
Elliot Lee's avatar
Elliot Lee committed
1051

1052 1053 1054 1055 1056 1057
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
1058

1059 1060
  gtk_grab_add (GTK_WIDGET (range));
  
1061 1062 1063 1064 1065
  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
1066 1067
}

1068 1069
static void
range_grab_remove (GtkRange *range)
Elliot Lee's avatar
Elliot Lee committed
1070
{
1071 1072
  gtk_grab_remove (GTK_WIDGET (range));
  
1073 1074
  range->layout->grab_location = MOUSE_OUTSIDE;
  range->layout->grab_button = 0;
Elliot Lee's avatar
Elliot Lee committed
1075

1076 1077 1078
  if (gtk_range_update_mouse_location (range))
    gtk_widget_queue_draw (GTK_WIDGET (range));
}
Elliot Lee's avatar
Elliot Lee committed
1079

1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
static GtkScrollType
range_get_scroll_for_grab (GtkRange      *range)
{  
  switch (range->layout->grab_location)
    {
      /* Backward stepper */
    case MOUSE_STEPPER_A:
    case MOUSE_STEPPER_C:
      switch (range->layout->grab_button)
        {
        case 1:
          return GTK_SCROLL_STEP_BACKWARD;
          break;
        case 2:
          return GTK_SCROLL_PAGE_BACKWARD;
          break;
        case 3:
          return GTK_SCROLL_START;
          break;
        }
      break;
Elliot Lee's avatar
Elliot Lee committed
1101

1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
      /* Forward stepper */
    case MOUSE_STEPPER_B:
    case MOUSE_STEPPER_D:
      switch (range->layout->grab_button)
        {
        case 1:
          return GTK_SCROLL_STEP_FORWARD;
          break;
        case 2:
          return GTK_SCROLL_PAGE_FORWARD;
          break;
        case 3:
          return GTK_SCROLL_END;
          break;
1116
       }
1117
      break;
1118

1119 1120 1121 1122
      /* In the trough */
    case MOUSE_TROUGH:
      {
        if (range->trough_click_forward)
1123
	  return GTK_SCROLL_PAGE_FORWARD;
1124
        else
1125
	  return GTK_SCROLL_PAGE_BACKWARD;
1126 1127
      }
      break;
1128

1129 1130 1131 1132
    case MOUSE_OUTSIDE:
    case MOUSE_SLIDER:
    case MOUSE_WIDGET:
      break;
Elliot Lee's avatar
Elliot Lee committed
1133 1134
    }

1135
  return GTK_SCROLL_NONE;
Elliot Lee's avatar
Elliot Lee committed
1136 1137
}

1138 1139 1140
static gdouble
coord_to_value (GtkRange *range,
                gint      coord)
Elliot Lee's avatar
Elliot Lee committed
1141
{
1142 1143
  gdouble frac;
  gdouble value;
1144
  
1145
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
Owen Taylor's avatar
Owen Taylor committed
1146 1147 1148 1149 1150
    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));
1151
  else
Owen Taylor's avatar
Owen Taylor committed
1152 1153 1154 1155 1156
    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));
1157

1158 1159
  if (should_invert (range))
    frac = 1.0 - frac;
1160
  
1161 1162
  value = range->adjustment->lower +
    frac * (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size);
Elliot Lee's avatar
Elliot Lee committed
1163

1164
  return value;
Elliot Lee's avatar
Elliot Lee committed
1165 1166
}

1167 1168 1169
static gint
gtk_range_button_press (GtkWidget      *widget,
			GdkEventButton *event)
Elliot Lee's avatar
Elliot Lee committed
1170
{
1171
  GtkRange *range = GTK_RANGE (widget);
1172 1173 1174
  
  if (!GTK_WIDGET_HAS_FOCUS (widget))
    gtk_widget_grab_focus (widget);
Elliot Lee's avatar
Elliot Lee committed
1175

1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
  /* 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  &&
1186
      event->button == 1)
Elliot Lee's avatar
Elliot Lee committed
1187
    {
1188
      /* button 1 steps by page increment, as with button 2 on a stepper
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
       */
      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
1203

1204
      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1205
    }
1206 1207 1208 1209 1210
  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
1211
    {
1212 1213 1214 1215 1216 1217 1218
      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,
1219 1220
                                  widget->allocation.x + stepper_area->x,
                                  widget->allocation.y + stepper_area->y,
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
                                  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)
    {
1234 1235
      gboolean need_value_update = FALSE;

1236 1237 1238 1239 1240 1241 1242
      /* 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)
        {
1243
          gdouble slider_low_value, slider_high_value, new_value;
1244
          
1245 1246 1247 1248 1249 1250 1251 1252 1253
          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);
1254
          
1255 1256
          /* compute new value for warped slider */
          new_value = slider_low_value + (slider_high_value - slider_low_value) / 2;
1257

1258
	  /* recalc slider, so we can set slide_initial_slider_position
1259 1260
           * properly
           */
1261 1262 1263 1264 1265 1266 1267
	  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;
1268 1269 1270 1271 1272 1273 1274
        }
      
      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
1275
      else
1276 1277 1278 1279 1280
        {
          range->slide_initial_slider_position = range->layout->slider.x;
          range->slide_initial_coordinate = event->x;
        }

1281 1282 1283
      if (need_value_update)
        update_slider_position (range, event->x, event->y);

1284 1285 1286
      range_grab_add (range, MOUSE_SLIDER, event->button);
      
      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1287
    }
1288 1289
  
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
1290 1291
}

1292 1293 1294 1295 1296
/* During a slide, move the slider as required given new mouse position */
static void
update_slider_position (GtkRange *range,
                        gint      mouse_x,
                        gint      mouse_y)
Elliot Lee's avatar
Elliot Lee committed
1297
{
1298 1299 1300 1301 1302 1303 1304 1305
  gint delta;
  gint c;
  gdouble new_value;
  
  if (range->orientation == GTK_ORIENTATION_VERTICAL)
    delta = mouse_y - range->slide_initial_coordinate;
  else
    delta = mouse_x - range->slide_initial_coordinate;
Elliot Lee's avatar
Elliot Lee committed
1306

1307
  c = range->slide_initial_slider_position + delta;
Elliot Lee's avatar
Elliot Lee committed
1308

1309 1310 1311 1312
  new_value = coord_to_value (range, c);
  
  gtk_range_internal_set_value (range, new_value);
}
Elliot Lee's avatar
Elliot Lee committed
1313

1314 1315 1316 1317
static gint
gtk_range_button_release (GtkWidget      *widget,
			  GdkEventButton *event)
{
1318
  GtkRange *range = GTK_RANGE (widget);
Elliot Lee's avatar
Elliot Lee committed
1319

1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331
  if (event->window == range->event_window)
    {
      range->layout->mouse_x = event->x;
      range->layout->mouse_y = event->y;
    }
  else
    {
      gdk_window_get_pointer (range->event_window,
			      &range->layout->mouse_x,
			      &range->layout->mouse_y,
			      NULL);
    }
1332
  
1333
  if (range->layout->grab_button == event->button)
Elliot Lee's avatar
Elliot Lee committed
1334
    {
1335
      MouseLocation grab_location;
Elliot Lee's avatar
Elliot Lee committed
1336

1337
      grab_location = range->layout->grab_location;
Elliot Lee's avatar
Elliot Lee committed
1338

1339 1340 1341 1342
      range_grab_remove (range);
      gtk_range_remove_step_timer (range);
      
      if (grab_location == MOUSE_SLIDER)
1343
        update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
Elliot Lee's avatar
Elliot Lee committed
1344

1345 1346 1347 1348 1349 1350 1351 1352 1353
      /* Flush any pending discontinuous/delayed updates */
      gtk_range_update_value (range);
      
      /* Just be lazy about this, if we scrolled it will all redraw anyway,
       * so no point optimizing the button deactivate case
       */
      gtk_widget_queue_draw (widget);
      
      return TRUE;
1354
    }
1355

Elliot Lee's avatar
Elliot Lee committed
1356 1357 1358 1359
  return FALSE;
}

static gint
1360 1361
gtk_range_scroll_event (GtkWidget      *widget,
			GdkEventScroll *event)
Elliot Lee's avatar
Elliot Lee committed
1362
{
1363
  GtkRange *range = GTK_RANGE (widget);
1364 1365

  if (GTK_WIDGET_REALIZED (range))
Elliot Lee's avatar
Elliot Lee committed
1366
    {
1367
      GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
1368 1369 1370 1371 1372 1373 1374 1375 1376
      gdouble increment = ((event->direction == GDK_SCROLL_UP ||
			    event->direction == GDK_SCROLL_LEFT) ? 
			   -adj->page_increment / 2: 
			   adj->page_increment / 2);
      
      if (range->inverted)
	increment = -increment;
	  
      gtk_range_internal_set_value (range, adj->value + increment);
Elliot Lee's avatar
Elliot Lee committed
1377

1378 1379 1380 1381 1382 1383
      /* Policy DELAYED makes sense with scroll events,
       * but DISCONTINUOUS doesn't, so we update immediately
       * for DISCONTINOUS
       */
      if (range->update_policy == GTK_UPDATE_DISCONTINUOUS)
        gtk_range_update_value (range);
Elliot Lee's avatar
Elliot Lee committed
1384 1385
    }

1386
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1387 1388 1389
}

static gint
1390 1391
gtk_range_motion_notify (GtkWidget      *widget,
			 GdkEventMotion *event)
Elliot Lee's avatar
Elliot Lee committed
1392 1393
{
  GtkRange *range;
1394
  gint x, y;
Elliot Lee's avatar
Elliot Lee committed
1395 1396 1397

  range = GTK_RANGE (widget);

1398
  gdk_window_get_pointer (range->event_window, &x, &y, NULL);
1399 1400 1401
  
  range->layout->mouse_x = x;
  range->layout->mouse_y = y;
Elliot Lee's avatar
Elliot Lee committed
1402

1403 1404
  if (gtk_range_update_mouse_location (range))
    gtk_widget_queue_draw (widget);
Elliot Lee's avatar
Elliot Lee committed
1405

1406
  if (range->layout->grab_location == MOUSE_SLIDER)
1407
    update_slider_position (range, x, y);
Elliot Lee's avatar
Elliot Lee committed
1408

1409 1410
  /* We handled the event if the mouse was in the range_rect */
  return range->layout->mouse_location != MOUSE_OUTSIDE;
Elliot Lee's avatar
Elliot Lee committed
1411 1412
}

1413
static gint
1414 1415
gtk_range_enter_notify (GtkWidget        *widget,
			GdkEventCrossing *event)
1416
{
1417
  GtkRange *range = GTK_RANGE (widget);
1418

1419 1420
  range->layout->mouse_x = event->x;
  range->layout->mouse_y = event->y;
1421

1422 1423 1424
  if (gtk_range_update_mouse_location (range))
    gtk_widget_queue_draw (widget);
  
1425
  return TRUE;
1426 1427
}

Elliot Lee's avatar
Elliot Lee committed
1428
static gint
1429 1430
gtk_range_leave_notify (GtkWidget        *widget,
			GdkEventCrossing *event)
Elliot Lee's avatar
Elliot Lee committed
1431
{
1432
  GtkRange *range = GTK_RANGE (widget);
Elliot Lee's avatar
Elliot Lee committed
1433

1434 1435
  range->layout->mouse_x = -1;
  range->layout->mouse_y = -1;
Elliot Lee's avatar
Elliot Lee committed
1436

1437 1438 1439
  if (gtk_range_update_mouse_location (range))
    gtk_widget_queue_draw (widget);
  
1440
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1441 1442
}

1443
static void
1444 1445
gtk_range_adjustment_changed (GtkAdjustment *adjustment,
			      gpointer       data)
Elliot Lee's avatar
Elliot Lee committed
1446
{
1447
  GtkRange *range = GTK_RANGE (data);
1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458

  range->need_recalc = TRUE;
  gtk_widget_queue_draw (GTK_WIDGET (range));

  /* Note that we don't round off to range->round_digits here.
   * that's because it's really broken to change a value
   * in response to a change signal on that value; round_digits
   * is therefore defined to be a filter on what the GtkRange
   * can input into the adjustment, not a filter that the GtkRange
   * will enforce on the adjustment.
   */
Elliot Lee's avatar
Elliot Lee committed
1459 1460
}

1461 1462 1463
static void
gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
				    gpointer       data)
Elliot Lee's avatar
Elliot Lee committed
1464
{
1465
  GtkRange *range = GTK_RANGE (data);
Elliot Lee's avatar
Elliot Lee committed
1466