gtkscale.c 16.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/. 
 */

Elliot Lee's avatar
Elliot Lee committed
28
#include <math.h>
29
#include "gtkintl.h"
Elliot Lee's avatar
Elliot Lee committed
30
#include "gtkscale.h"
31
#include "gtkmarshalers.h"
32 33
#include "gdk/gdkkeysyms.h"
#include "gtkbindings.h"
Elliot Lee's avatar
Elliot Lee committed
34

35 36 37 38 39 40 41

#define	MAX_DIGITS	(64)	/* don't change this,
				 * a) you don't need to and
				 * b) you might cause buffer owerflows in
				 *    unrelated code portions otherwise
				 */

42
enum {
43 44 45 46
  PROP_0,
  PROP_DIGITS,
  PROP_DRAW_VALUE,
  PROP_VALUE_POS
47 48
};

Havoc Pennington's avatar
Havoc Pennington committed
49 50 51 52 53 54
enum {
  FORMAT_VALUE,
  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];
Elliot Lee's avatar
Elliot Lee committed
55 56
static GtkRangeClass *parent_class = NULL;

57 58 59 60 61 62 63 64 65 66 67 68 69 70
static void gtk_scale_class_init       (GtkScaleClass *klass);
static void gtk_scale_init             (GtkScale      *scale);
static void gtk_scale_set_property     (GObject       *object,
                                        guint          prop_id,
                                        const GValue  *value,
                                        GParamSpec    *pspec);
static void gtk_scale_get_property     (GObject       *object,
                                        guint          prop_id,
                                        GValue        *value,
                                        GParamSpec    *pspec);
static void gtk_scale_style_set        (GtkWidget     *widget,
                                        GtkStyle      *previous);
static void gtk_scale_get_range_border (GtkRange      *range,
                                        GtkBorder     *border);
Elliot Lee's avatar
Elliot Lee committed
71

Manish Singh's avatar
Manish Singh committed
72
GType
73
gtk_scale_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
74
{
Manish Singh's avatar
Manish Singh committed
75
  static GType scale_type = 0;
Elliot Lee's avatar
Elliot Lee committed
76 77 78

  if (!scale_type)
    {
79
      static const GTypeInfo scale_info =
Elliot Lee's avatar
Elliot Lee committed
80 81
      {
	sizeof (GtkScaleClass),
Manish Singh's avatar
Manish Singh committed
82 83
	NULL,		/* base_init */
	NULL,		/* base_finalize */
84
	(GClassInitFunc) gtk_scale_class_init,
Manish Singh's avatar
Manish Singh committed
85 86
	NULL,		/* class_finalize */
	NULL,		/* class_data */
87
	sizeof (GtkScale),
Manish Singh's avatar
Manish Singh committed
88
	0,		/* n_preallocs */
89
	(GInstanceInitFunc) gtk_scale_init,
Manish Singh's avatar
Manish Singh committed
90
	NULL,		/* value_table */
Elliot Lee's avatar
Elliot Lee committed
91 92
      };

93 94
      scale_type = g_type_register_static (GTK_TYPE_RANGE, "GtkScale",
					   &scale_info, G_TYPE_FLAG_ABSTRACT);
Elliot Lee's avatar
Elliot Lee committed
95 96 97 98 99
    }

  return scale_type;
}

100
static gboolean
Havoc Pennington's avatar
Havoc Pennington committed
101 102 103 104 105 106
single_string_accumulator (GSignalInvocationHint *ihint,
                           GValue                *return_accu,
                           const GValue          *handler_return,
                           gpointer               dummy)
{
  gboolean continue_emission;
107
  const gchar *str;
Havoc Pennington's avatar
Havoc Pennington committed
108 109 110 111 112 113 114 115
  
  str = g_value_get_string (handler_return);
  g_value_set_string (return_accu, str);
  continue_emission = str == NULL;
  
  return continue_emission;
}

116 117 118 119 120 121

#define add_slider_binding(binding_set, keyval, mask, scroll)          \
  gtk_binding_entry_add_signal (binding_set, keyval, mask,             \
                                "move_slider", 1,                      \
                                GTK_TYPE_SCROLL_TYPE, scroll)

Elliot Lee's avatar
Elliot Lee committed
122 123 124
static void
gtk_scale_class_init (GtkScaleClass *class)
{
125
  GObjectClass   *gobject_class;
126
  GtkWidgetClass *widget_class;
Manish Singh's avatar
Manish Singh committed
127 128
  GtkRangeClass  *range_class;
  GtkBindingSet  *binding_set;
129
  
130
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
131
  range_class = (GtkRangeClass*) class;
132
  widget_class = (GtkWidgetClass*) class;
133
  
Manish Singh's avatar
Manish Singh committed
134
  parent_class = g_type_class_peek_parent (class);
135
  
136 137 138
  gobject_class->set_property = gtk_scale_set_property;
  gobject_class->get_property = gtk_scale_get_property;

139
  widget_class->style_set = gtk_scale_style_set;
140

141 142
  range_class->get_range_border = gtk_scale_get_range_border;
  
143
  signals[FORMAT_VALUE] =
144
    g_signal_new ("format_value",
Manish Singh's avatar
Manish Singh committed
145
                  G_TYPE_FROM_CLASS (gobject_class),
146 147 148
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkScaleClass, format_value),
                  single_string_accumulator, NULL,
149
                  _gtk_marshal_STRING__DOUBLE,
150 151
                  G_TYPE_STRING, 1,
                  G_TYPE_DOUBLE);
152

153 154 155 156 157
  g_object_class_install_property (gobject_class,
                                   PROP_DIGITS,
                                   g_param_spec_int ("digits",
						     _("Digits"),
						     _("The number of decimal places that are displayed in the value"),
158 159 160
						     -1,
						     MAX_DIGITS,
						     1,
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
						     G_PARAM_READWRITE));
  
  g_object_class_install_property (gobject_class,
                                   PROP_DRAW_VALUE,
                                   g_param_spec_boolean ("draw_value",
							 _("Draw Value"),
							 _("Whether the current value is displayed as a string next to the slider"),
							 FALSE,
							 G_PARAM_READWRITE));
  
  g_object_class_install_property (gobject_class,
                                   PROP_VALUE_POS,
                                   g_param_spec_enum ("value_pos",
						      _("Value Position"),
						      _("The position in which the current value is displayed"),
						      GTK_TYPE_POSITION_TYPE,
						      GTK_POS_LEFT,
						      G_PARAM_READWRITE));
179

180 181 182 183 184 185 186 187
  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("slider_length",
							     _("Slider Length"),
							     _("Length of scale's slider"),
							     0,
							     G_MAXINT,
							     31,
							     G_PARAM_READABLE));
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

  gtk_widget_class_install_style_property (widget_class,
					   g_param_spec_int ("value_spacing",
							     _("Value spacing"),
							     _("Space between value text and the slider/trough area"),
							     0,
							     G_MAXINT,
							     2,
							     G_PARAM_READABLE));
  
  /* All bindings (even arrow keys) are on both h/v scale, because
   * blind users etc. don't care about scale orientation.
   */
  
  binding_set = gtk_binding_set_by_class (class);

  add_slider_binding (binding_set, GDK_Left, 0,
                      GTK_SCROLL_STEP_LEFT);

  add_slider_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_LEFT);

  add_slider_binding (binding_set, GDK_KP_Left, 0,
                      GTK_SCROLL_STEP_LEFT);

  add_slider_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_LEFT);

  add_slider_binding (binding_set, GDK_Right, 0,
                      GTK_SCROLL_STEP_RIGHT);

  add_slider_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_RIGHT);

  add_slider_binding (binding_set, GDK_KP_Right, 0,
                      GTK_SCROLL_STEP_RIGHT);

  add_slider_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_RIGHT);

  add_slider_binding (binding_set, GDK_Up, 0,
                      GTK_SCROLL_STEP_UP);

  add_slider_binding (binding_set, GDK_Up, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_UP);

  add_slider_binding (binding_set, GDK_KP_Up, 0,
                      GTK_SCROLL_STEP_UP);

  add_slider_binding (binding_set, GDK_KP_Up, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_UP);

  add_slider_binding (binding_set, GDK_Down, 0,
                      GTK_SCROLL_STEP_DOWN);

  add_slider_binding (binding_set, GDK_Down, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_DOWN);

  add_slider_binding (binding_set, GDK_KP_Down, 0,
                      GTK_SCROLL_STEP_DOWN);

  add_slider_binding (binding_set, GDK_KP_Down, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_DOWN);
   
  add_slider_binding (binding_set, GDK_Page_Up, 0,
253
                      GTK_SCROLL_PAGE_LEFT);
254 255

  add_slider_binding (binding_set, GDK_KP_Page_Up, 0,
256 257 258 259
                      GTK_SCROLL_PAGE_LEFT);  

  add_slider_binding (binding_set, GDK_Page_Up, 0,
                      GTK_SCROLL_PAGE_UP);
260

261 262 263
  add_slider_binding (binding_set, GDK_KP_Page_Up, 0,
                      GTK_SCROLL_PAGE_UP);
  
264
  add_slider_binding (binding_set, GDK_Page_Down, 0,
265
                      GTK_SCROLL_PAGE_RIGHT);
266 267

  add_slider_binding (binding_set, GDK_KP_Page_Down, 0,
268 269 270 271
                      GTK_SCROLL_PAGE_RIGHT);

  add_slider_binding (binding_set, GDK_Page_Down, 0,
                      GTK_SCROLL_PAGE_DOWN);
272

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
  add_slider_binding (binding_set, GDK_KP_Page_Down, 0,
                      GTK_SCROLL_PAGE_DOWN);

  /* Logical bindings (vs. visual bindings above) */

  add_slider_binding (binding_set, GDK_plus, 0,
                      GTK_SCROLL_STEP_FORWARD);  

  add_slider_binding (binding_set, GDK_minus, 0,
                      GTK_SCROLL_STEP_BACKWARD);  

  add_slider_binding (binding_set, GDK_plus, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_FORWARD);  

  add_slider_binding (binding_set, GDK_minus, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_BACKWARD);


  add_slider_binding (binding_set, GDK_KP_Add, 0,
                      GTK_SCROLL_STEP_FORWARD);  

  add_slider_binding (binding_set, GDK_KP_Subtract, 0,
                      GTK_SCROLL_STEP_BACKWARD);  

  add_slider_binding (binding_set, GDK_KP_Add, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_FORWARD);  

  add_slider_binding (binding_set, GDK_KP_Subtract, GDK_CONTROL_MASK,
                      GTK_SCROLL_PAGE_BACKWARD);
  
  
304 305 306 307 308 309 310 311 312 313 314
  add_slider_binding (binding_set, GDK_Home, 0,
                      GTK_SCROLL_START);

  add_slider_binding (binding_set, GDK_KP_Home, 0,
                      GTK_SCROLL_START);

  add_slider_binding (binding_set, GDK_End, 0,
                      GTK_SCROLL_END);

  add_slider_binding (binding_set, GDK_KP_End, 0,
                      GTK_SCROLL_END);
Elliot Lee's avatar
Elliot Lee committed
315 316
}

317
static void
318 319 320 321
gtk_scale_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
322 323 324 325 326
{
  GtkScale *scale;

  scale = GTK_SCALE (object);

327
  switch (prop_id)
328
    {
329 330
    case PROP_DIGITS:
      gtk_scale_set_digits (scale, g_value_get_int (value));
331
      break;
332 333
    case PROP_DRAW_VALUE:
      gtk_scale_set_draw_value (scale, g_value_get_boolean (value));
334
      break;
335 336
    case PROP_VALUE_POS:
      gtk_scale_set_value_pos (scale, g_value_get_enum (value));
337 338
      break;
    default:
339
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
340 341 342 343 344
      break;
    }
}

static void
345 346 347 348
gtk_scale_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
349 350 351 352 353
{
  GtkScale *scale;

  scale = GTK_SCALE (object);

354
  switch (prop_id)
355
    {
356
    case PROP_DIGITS:
357
      g_value_set_int (value, scale->digits);
358
      break;
359 360
    case PROP_DRAW_VALUE:
      g_value_set_boolean (value, scale->draw_value);
361
      break;
362 363
    case PROP_VALUE_POS:
      g_value_set_enum (value, scale->value_pos);
364 365
      break;
    default:
366
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367 368 369 370
      break;
    }
}

Elliot Lee's avatar
Elliot Lee committed
371 372
static void
gtk_scale_init (GtkScale *scale)
373 374 375
{
  GtkRange *range;

376 377 378
  range = GTK_RANGE (scale);
  
  GTK_WIDGET_SET_FLAGS (scale, GTK_CAN_FOCUS);
379

380 381 382 383 384
  range->slider_size_fixed = TRUE;
  range->has_stepper_a = FALSE;
  range->has_stepper_b = FALSE;
  range->has_stepper_c = FALSE;
  range->has_stepper_d = FALSE;
385
  
386 387
  scale->draw_value = TRUE;
  scale->value_pos = GTK_POS_TOP;
388 389
  scale->digits = 1;
  range->round_digits = scale->digits;
390 391
}

Elliot Lee's avatar
Elliot Lee committed
392 393 394 395
void
gtk_scale_set_digits (GtkScale *scale,
		      gint      digits)
{
396 397
  GtkRange *range;
  
Elliot Lee's avatar
Elliot Lee committed
398 399
  g_return_if_fail (GTK_IS_SCALE (scale));

400 401
  range = GTK_RANGE (scale);
  
402
  digits = CLAMP (digits, -1, MAX_DIGITS);
403

404
  if (scale->digits != digits)
Elliot Lee's avatar
Elliot Lee committed
405
    {
406 407 408
      scale->digits = digits;
      if (scale->draw_value)
	range->round_digits = digits;
409
      
410
      gtk_widget_queue_resize (GTK_WIDGET (scale));
411 412

      g_object_notify (G_OBJECT (scale), "digits");
Elliot Lee's avatar
Elliot Lee committed
413 414 415
    }
}

416 417 418 419 420
gint
gtk_scale_get_digits (GtkScale *scale)
{
  g_return_val_if_fail (GTK_IS_SCALE (scale), -1);

421
  return scale->digits;
422 423
}

Elliot Lee's avatar
Elliot Lee committed
424 425
void
gtk_scale_set_draw_value (GtkScale *scale,
426
			  gboolean  draw_value)
Elliot Lee's avatar
Elliot Lee committed
427 428 429
{
  g_return_if_fail (GTK_IS_SCALE (scale));

430 431
  draw_value = draw_value != FALSE;

Elliot Lee's avatar
Elliot Lee committed
432 433
  if (scale->draw_value != draw_value)
    {
434
      scale->draw_value = draw_value;
435 436 437 438
      if (draw_value)
	GTK_RANGE (scale)->round_digits = scale->digits;
      else
	GTK_RANGE (scale)->round_digits = -1;
Elliot Lee's avatar
Elliot Lee committed
439

440
      gtk_widget_queue_resize (GTK_WIDGET (scale));
441 442

      g_object_notify (G_OBJECT (scale), "draw_value");
Elliot Lee's avatar
Elliot Lee committed
443 444 445
    }
}

446 447 448 449 450 451 452 453
gboolean
gtk_scale_get_draw_value (GtkScale *scale)
{
  g_return_val_if_fail (GTK_IS_SCALE (scale), FALSE);

  return scale->draw_value;
}

Elliot Lee's avatar
Elliot Lee committed
454 455 456 457 458 459 460 461 462 463 464 465
void
gtk_scale_set_value_pos (GtkScale        *scale,
			 GtkPositionType  pos)
{
  g_return_if_fail (GTK_IS_SCALE (scale));

  if (scale->value_pos != pos)
    {
      scale->value_pos = pos;

      if (GTK_WIDGET_VISIBLE (scale) && GTK_WIDGET_MAPPED (scale))
	gtk_widget_queue_resize (GTK_WIDGET (scale));
466 467

      g_object_notify (G_OBJECT (scale), "value_pos");
Elliot Lee's avatar
Elliot Lee committed
468 469 470
    }
}

471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
GtkPositionType
gtk_scale_get_value_pos (GtkScale *scale)
{
  g_return_val_if_fail (GTK_IS_SCALE (scale), 0);

  return scale->value_pos;
}

static void
gtk_scale_get_range_border (GtkRange  *range,
                            GtkBorder *border)
{
  GtkWidget *widget;
  GtkScale *scale;
  gint w, h;
  
  widget = GTK_WIDGET (range);
  scale = GTK_SCALE (range);

  _gtk_scale_get_value_size (scale, &w, &h);

  border->left = 0;
  border->right = 0;
  border->top = 0;
  border->bottom = 0;

  if (scale->draw_value)
    {
      gint value_spacing;
      gtk_widget_style_get (widget, "value_spacing", &value_spacing, NULL);

      switch (scale->value_pos)
        {
        case GTK_POS_LEFT:
          border->left += w + value_spacing;
          break;
        case GTK_POS_RIGHT:
          border->right += w + value_spacing;
          break;
        case GTK_POS_TOP:
          border->top += h + value_spacing;
          break;
        case GTK_POS_BOTTOM:
          border->bottom += h + value_spacing;
          break;
        }
    }
}

/* FIXME this could actually be static at the moment. */
521
void
522 523 524
_gtk_scale_get_value_size (GtkScale *scale,
                           gint     *width,
                           gint     *height)
Elliot Lee's avatar
Elliot Lee committed
525 526 527
{
  GtkRange *range;

528
  g_return_if_fail (GTK_IS_SCALE (scale));
Elliot Lee's avatar
Elliot Lee committed
529 530 531

  if (scale->draw_value)
    {
532 533
      PangoLayout *layout;
      PangoRectangle logical_rect;
Havoc Pennington's avatar
Havoc Pennington committed
534
      gchar *txt;
535
      
Elliot Lee's avatar
Elliot Lee committed
536 537
      range = GTK_RANGE (scale);

538
      layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale), NULL);
539

Havoc Pennington's avatar
Havoc Pennington committed
540 541 542 543
      txt = _gtk_scale_format_value (scale, range->adjustment->lower);
      pango_layout_set_text (layout, txt, -1);
      g_free (txt);
      
544
      pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
Elliot Lee's avatar
Elliot Lee committed
545

546
      if (width)
547
	*width = logical_rect.width;
548
      if (height)
Havoc Pennington's avatar
Havoc Pennington committed
549 550 551 552 553
	*height = logical_rect.height;

      txt = _gtk_scale_format_value (scale, range->adjustment->upper);
      pango_layout_set_text (layout, txt, -1);
      g_free (txt);
554
      
555
      pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
556 557

      if (width)
558
	*width = MAX (*width, logical_rect.width);
559
      if (height)
560
	*height = MAX (*height, logical_rect.height);
561

Manish Singh's avatar
Manish Singh committed
562
      g_object_unref (layout);
Elliot Lee's avatar
Elliot Lee committed
563
    }
564 565 566 567 568 569 570 571 572 573
  else
    {
      if (width)
	*width = 0;
      if (height)
	*height = 0;
    }

}

574 575 576
static void
gtk_scale_style_set (GtkWidget *widget,
                     GtkStyle  *previous)
577
{
578 579
  gint slider_length;
  GtkRange *range;
580

581
  range = GTK_RANGE (widget);
582
  
583 584 585 586 587 588 589
  gtk_widget_style_get (widget,
                        "slider_length", &slider_length,
                        NULL);
  
  range->min_slider_size = slider_length;
  
  (* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous);
Elliot Lee's avatar
Elliot Lee committed
590 591 592
}


Havoc Pennington's avatar
Havoc Pennington committed
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
/**
 * _gtk_scale_format_value:
 * @scale: a #GtkScale
 * @value: adjustment value
 * 
 * Emits "format_value" signal to format the value, if no user
 * signal handlers, falls back to a default format.
 * 
 * Return value: formatted value
 **/
gchar*
_gtk_scale_format_value (GtkScale *scale,
                         gdouble   value)
{
  gchar *fmt = NULL;

Manish Singh's avatar
Manish Singh committed
609
  g_signal_emit (scale,
Havoc Pennington's avatar
Havoc Pennington committed
610 611 612 613 614 615 616 617
                 signals[FORMAT_VALUE],
                 0,
                 value,
                 &fmt);

  if (fmt)
    return fmt;
  else
618
    return g_strdup_printf ("%0.*f", scale->digits,
Havoc Pennington's avatar
Havoc Pennington committed
619 620
                            value);
}