gtkscale.c 56 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2
 * 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
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18 19

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

26
#include "config.h"
27

Elliot Lee's avatar
Elliot Lee committed
28
#include <math.h>
29 30
#include <stdlib.h>

31
#include "gtkscaleprivate.h"
32 33 34 35 36

#include "gtkadjustment.h"
#include "gtkbindings.h"
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
37 38
#include "gtkiconfactory.h"
#include "gtkicontheme.h"
39
#include "gtkintl.h"
40
#include "gtkmarshalers.h"
41
#include "gtkorientable.h"
42
#include "gtkprivate.h"
43
#include "gtktypebuiltins.h"
44

45 46
#include "a11y/gtkscaleaccessible.h"

47 48 49

/**
 * SECTION:gtkscale
50
 * @Short_description: A slider widget for selecting a value from a range
51 52
 * @Title: GtkScale
 *
53
 * A GtkScale is a slider control used to select a numeric value. 
54
 * To use it, you'll probably want to investigate the methods on
55
 * its base class, #GtkRange, in addition to the methods for GtkScale itself.
56 57
 * To set the value of a scale, you would normally use gtk_range_set_value().
 * To detect changes to the value, you would normally use the
58
 * #GtkRange::value-changed signal.
59
 *
60 61 62 63 64
 * Note that using the same upper and lower bounds for the #GtkScale (through
 * the #GtkRange methods) will hide the slider itself. This is useful for
 * applications that want to show an undeterminate value on the scale, without
 * changing the layout of the application (such as movie or music players).
 *
65 66 67 68 69 70 71 72 73 74 75
 * <refsect2 id="GtkScale-BUILDER-UI"><title>GtkScale as GtkBuildable</title>
 * GtkScale supports a custom &lt;marks&gt; element, which
 * can contain multiple &lt;mark&gt; elements. The "value" and "position"
 * attributes have the same meaning as gtk_scale_add_mark() parameters of the
 * same name. If the element is not empty, its content is taken as the markup
 * to show at the mark. It can be translated with the usual "translatable and
 * "context" attributes.
 * </refsect2>
 */


76 77 78 79 80 81
#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
				 */

82 83
typedef struct _GtkScaleMark GtkScaleMark;

84
struct _GtkScalePrivate
85 86 87
{
  PangoLayout  *layout;

88
  GSList       *marks;
89 90 91 92 93 94 95

  gint          digits;

  guint         draw_value : 1;
  guint         value_pos  : 2;
};

96 97 98
struct _GtkScaleMark
{
  gdouble          value;
99
  gchar           *markup;
100
  GtkPositionType  position; /* always GTK_POS_TOP or GTK_POS_BOTTOM */
101 102
};

103
enum {
104 105 106
  PROP_0,
  PROP_DIGITS,
  PROP_DRAW_VALUE,
107
  PROP_HAS_ORIGIN,
108
  PROP_VALUE_POS
109 110
};

Havoc Pennington's avatar
Havoc Pennington committed
111 112 113 114 115 116
enum {
  FORMAT_VALUE,
  LAST_SIGNAL
};

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

118 119 120 121 122 123 124 125
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);
126 127 128 129 130 131
static void     gtk_scale_get_preferred_width     (GtkWidget      *widget,
                                                   gint           *minimum,
                                                   gint           *natural);
static void     gtk_scale_get_preferred_height    (GtkWidget      *widget,
                                                   gint           *minimum,
                                                   gint           *natural);
132
static void     gtk_scale_style_updated           (GtkWidget      *widget);
133 134
static void     gtk_scale_get_range_border        (GtkRange       *range,
                                                   GtkBorder      *border);
135 136 137 138 139 140 141 142
static void     gtk_scale_get_mark_label_size     (GtkScale        *scale,
                                                   GtkPositionType  position,
                                                   gint            *count1,
                                                   gint            *width1,
                                                   gint            *height1,
                                                   gint            *count2,
                                                   gint            *width2,
                                                   gint            *height2);
143 144 145
static void     gtk_scale_finalize                (GObject        *object);
static void     gtk_scale_screen_changed          (GtkWidget      *widget,
                                                   GdkScreen      *old_screen);
146 147
static gboolean gtk_scale_draw                    (GtkWidget      *widget,
                                                   cairo_t        *cr);
148 149 150
static void     gtk_scale_real_get_layout_offsets (GtkScale       *scale,
                                                   gint           *x,
                                                   gint           *y);
151 152 153 154 155 156 157 158 159 160 161 162 163 164
static void     gtk_scale_buildable_interface_init   (GtkBuildableIface *iface);
static gboolean gtk_scale_buildable_custom_tag_start (GtkBuildable  *buildable,
                                                      GtkBuilder    *builder,
                                                      GObject       *child,
                                                      const gchar   *tagname,
                                                      GMarkupParser *parser,
                                                      gpointer      *data);
static void     gtk_scale_buildable_custom_finished  (GtkBuildable  *buildable,
                                                      GtkBuilder    *builder,
                                                      GObject       *child,
                                                      const gchar   *tagname,
                                                      gpointer       user_data);


165 166 167
G_DEFINE_TYPE_WITH_CODE (GtkScale, gtk_scale, GTK_TYPE_RANGE,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                gtk_scale_buildable_interface_init))
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
static gint
compare_marks (gconstpointer a, gconstpointer b, gpointer data)
{
  gboolean inverted = GPOINTER_TO_INT (data);
  gint val;
  const GtkScaleMark *ma, *mb;

  val = inverted ? -1 : 1;

  ma = a; mb = b;

  return (ma->value > mb->value) ? val : ((ma->value < mb->value) ? -val : 0);
}

static void
gtk_scale_notify (GObject    *object,
                  GParamSpec *pspec)
{
  if (strcmp (pspec->name, "orientation") == 0)
    {
      GtkOrientation orientation;

      orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (object));
      gtk_range_set_flippable (GTK_RANGE (object),
                               orientation == GTK_ORIENTATION_HORIZONTAL);
    }
  else if (strcmp (pspec->name, "inverted") == 0)
    {
      GtkScale *scale = GTK_SCALE (object);
198 199 200 201
      GtkScaleMark *mark;
      GSList *m;
      gint i, n;
      gdouble *values;
202 203 204 205

      scale->priv->marks = g_slist_sort_with_data (scale->priv->marks,
                                                   compare_marks,
                                                   GINT_TO_POINTER (gtk_range_get_inverted (GTK_RANGE (scale))));
206 207 208 209 210 211 212 213 214 215 216 217

      n = g_slist_length (scale->priv->marks);
      values = g_new (gdouble, n);
      for (m = scale->priv->marks, i = 0; m; m = m->next, i++)
        {
          mark = m->data;
          values[i] = mark->value;
        }

      _gtk_range_set_stop_values (GTK_RANGE (scale), values, n);

      g_free (values);
218
    }
219 220

  if (G_OBJECT_CLASS (gtk_scale_parent_class)->notify)
221 222 223
    G_OBJECT_CLASS (gtk_scale_parent_class)->notify (object, pspec);
}

Elliot Lee's avatar
Elliot Lee committed
224

225 226
#define add_slider_binding(binding_set, keyval, mask, scroll)              \
  gtk_binding_entry_add_signal (binding_set, keyval, mask,                 \
227
                                I_("move-slider"), 1, \
228 229
                                GTK_TYPE_SCROLL_TYPE, scroll)

Elliot Lee's avatar
Elliot Lee committed
230 231 232
static void
gtk_scale_class_init (GtkScaleClass *class)
{
233
  GObjectClass   *gobject_class;
234
  GtkWidgetClass *widget_class;
Manish Singh's avatar
Manish Singh committed
235 236
  GtkRangeClass  *range_class;
  GtkBindingSet  *binding_set;
237
  
238
  gobject_class = G_OBJECT_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
239
  range_class = (GtkRangeClass*) class;
240
  widget_class = (GtkWidgetClass*) class;
241
  
242 243
  gobject_class->set_property = gtk_scale_set_property;
  gobject_class->get_property = gtk_scale_get_property;
244
  gobject_class->notify = gtk_scale_notify;
245
  gobject_class->finalize = gtk_scale_finalize;
246

247
  widget_class->style_updated = gtk_scale_style_updated;
248
  widget_class->screen_changed = gtk_scale_screen_changed;
249
  widget_class->draw = gtk_scale_draw;
250 251
  widget_class->get_preferred_width = gtk_scale_get_preferred_width;
  widget_class->get_preferred_height = gtk_scale_get_preferred_height;
252

253
  range_class->slider_detail = "Xscale";
254
  range_class->get_range_border = gtk_scale_get_range_border;
255 256 257

  class->get_layout_offsets = gtk_scale_real_get_layout_offsets;

Matthias Clasen's avatar
Matthias Clasen committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
  /**
   * GtkScale::format-value:
   * @scale: the object which received the signal
   * @value: the value to format
   *
   * Signal which allows you to change how the scale value is displayed.
   * Connect a signal handler which returns an allocated string representing 
   * @value. That string will then be used to display the scale's value.
   *
   * Here's an example signal handler which displays a value 1.0 as
   * with "--&gt;1.0&lt;--".
   * |[
   * static gchar*
   * format_value_callback (GtkScale *scale,
   *                        gdouble   value)
   * {
   *   return g_strdup_printf ("--&gt;&percnt;0.*g&lt;--",
   *                           gtk_scale_get_digits (scale), value);
   *  }
   * ]|
   *
   * Return value: allocated string representing @value
   */
281
  signals[FORMAT_VALUE] =
282
    g_signal_new (I_("format-value"),
Manish Singh's avatar
Manish Singh committed
283
                  G_TYPE_FROM_CLASS (gobject_class),
284 285
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GtkScaleClass, format_value),
286
                  _gtk_single_string_accumulator, NULL,
287
                  _gtk_marshal_STRING__DOUBLE,
288 289
                  G_TYPE_STRING, 1,
                  G_TYPE_DOUBLE);
290

291 292 293
  g_object_class_install_property (gobject_class,
                                   PROP_DIGITS,
                                   g_param_spec_int ("digits",
294 295
						     P_("Digits"),
						     P_("The number of decimal places that are displayed in the value"),
296 297 298
						     -1,
						     MAX_DIGITS,
						     1,
299
						     GTK_PARAM_READWRITE));
300 301 302
  
  g_object_class_install_property (gobject_class,
                                   PROP_DRAW_VALUE,
303
                                   g_param_spec_boolean ("draw-value",
304 305
							 P_("Draw Value"),
							 P_("Whether the current value is displayed as a string next to the slider"),
306
							 TRUE,
307
							 GTK_PARAM_READWRITE));
308 309 310 311 312 313 314 315 316

  g_object_class_install_property (gobject_class,
                                   PROP_HAS_ORIGIN,
                                   g_param_spec_boolean ("has-origin",
                                                         P_("Has Origin"),
                                                         P_("Whether the scale has an origin"),
                                                         TRUE,
                                                         GTK_PARAM_READWRITE));

317 318
  g_object_class_install_property (gobject_class,
                                   PROP_VALUE_POS,
319
                                   g_param_spec_enum ("value-pos",
320 321
						      P_("Value Position"),
						      P_("The position in which the current value is displayed"),
322
						      GTK_TYPE_POSITION_TYPE,
323
						      GTK_POS_TOP,
324
						      GTK_PARAM_READWRITE));
325

326
  gtk_widget_class_install_style_property (widget_class,
327
					   g_param_spec_int ("slider-length",
328 329
							     P_("Slider Length"),
							     P_("Length of scale's slider"),
330 331 332
							     0,
							     G_MAXINT,
							     31,
333
							     GTK_PARAM_READABLE));
334 335

  gtk_widget_class_install_style_property (widget_class,
336
					   g_param_spec_int ("value-spacing",
337 338
							     P_("Value spacing"),
							     P_("Space between value text and the slider/trough area"),
339 340 341
							     0,
							     G_MAXINT,
							     2,
342
							     GTK_PARAM_READABLE));
343 344 345 346 347 348 349
  
  /* 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);

350
  add_slider_binding (binding_set, GDK_KEY_Left, 0,
351 352
                      GTK_SCROLL_STEP_LEFT);

353
  add_slider_binding (binding_set, GDK_KEY_Left, GDK_CONTROL_MASK,
354 355
                      GTK_SCROLL_PAGE_LEFT);

356
  add_slider_binding (binding_set, GDK_KEY_KP_Left, 0,
357 358
                      GTK_SCROLL_STEP_LEFT);

359
  add_slider_binding (binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK,
360 361
                      GTK_SCROLL_PAGE_LEFT);

362
  add_slider_binding (binding_set, GDK_KEY_Right, 0,
363 364
                      GTK_SCROLL_STEP_RIGHT);

365
  add_slider_binding (binding_set, GDK_KEY_Right, GDK_CONTROL_MASK,
366 367
                      GTK_SCROLL_PAGE_RIGHT);

368
  add_slider_binding (binding_set, GDK_KEY_KP_Right, 0,
369 370
                      GTK_SCROLL_STEP_RIGHT);

371
  add_slider_binding (binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
372 373
                      GTK_SCROLL_PAGE_RIGHT);

374
  add_slider_binding (binding_set, GDK_KEY_Up, 0,
375 376
                      GTK_SCROLL_STEP_UP);

377
  add_slider_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
378 379
                      GTK_SCROLL_PAGE_UP);

380
  add_slider_binding (binding_set, GDK_KEY_KP_Up, 0,
381 382
                      GTK_SCROLL_STEP_UP);

383
  add_slider_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
384 385
                      GTK_SCROLL_PAGE_UP);

386
  add_slider_binding (binding_set, GDK_KEY_Down, 0,
387 388
                      GTK_SCROLL_STEP_DOWN);

389
  add_slider_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
390 391
                      GTK_SCROLL_PAGE_DOWN);

392
  add_slider_binding (binding_set, GDK_KEY_KP_Down, 0,
393 394
                      GTK_SCROLL_STEP_DOWN);

395
  add_slider_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
396 397
                      GTK_SCROLL_PAGE_DOWN);
   
398
  add_slider_binding (binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK,
399
                      GTK_SCROLL_PAGE_LEFT);
400

401
  add_slider_binding (binding_set, GDK_KEY_KP_Page_Up, GDK_CONTROL_MASK,
402 403
                      GTK_SCROLL_PAGE_LEFT);  

404
  add_slider_binding (binding_set, GDK_KEY_Page_Up, 0,
405
                      GTK_SCROLL_PAGE_UP);
406

407
  add_slider_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
408 409
                      GTK_SCROLL_PAGE_UP);
  
410
  add_slider_binding (binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK,
411
                      GTK_SCROLL_PAGE_RIGHT);
412

413
  add_slider_binding (binding_set, GDK_KEY_KP_Page_Down, GDK_CONTROL_MASK,
414 415
                      GTK_SCROLL_PAGE_RIGHT);

416
  add_slider_binding (binding_set, GDK_KEY_Page_Down, 0,
417
                      GTK_SCROLL_PAGE_DOWN);
418

419
  add_slider_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
420 421 422 423
                      GTK_SCROLL_PAGE_DOWN);

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

424
  add_slider_binding (binding_set, GDK_KEY_plus, 0,
425 426
                      GTK_SCROLL_STEP_FORWARD);  

427
  add_slider_binding (binding_set, GDK_KEY_minus, 0,
428 429
                      GTK_SCROLL_STEP_BACKWARD);  

430
  add_slider_binding (binding_set, GDK_KEY_plus, GDK_CONTROL_MASK,
431 432
                      GTK_SCROLL_PAGE_FORWARD);  

433
  add_slider_binding (binding_set, GDK_KEY_minus, GDK_CONTROL_MASK,
434 435 436
                      GTK_SCROLL_PAGE_BACKWARD);


437
  add_slider_binding (binding_set, GDK_KEY_KP_Add, 0,
438 439
                      GTK_SCROLL_STEP_FORWARD);  

440
  add_slider_binding (binding_set, GDK_KEY_KP_Subtract, 0,
441 442
                      GTK_SCROLL_STEP_BACKWARD);  

443
  add_slider_binding (binding_set, GDK_KEY_KP_Add, GDK_CONTROL_MASK,
444 445
                      GTK_SCROLL_PAGE_FORWARD);  

446
  add_slider_binding (binding_set, GDK_KEY_KP_Subtract, GDK_CONTROL_MASK,
447 448 449
                      GTK_SCROLL_PAGE_BACKWARD);
  
  
450
  add_slider_binding (binding_set, GDK_KEY_Home, 0,
451 452
                      GTK_SCROLL_START);

453
  add_slider_binding (binding_set, GDK_KEY_KP_Home, 0,
454 455
                      GTK_SCROLL_START);

456
  add_slider_binding (binding_set, GDK_KEY_End, 0,
457 458
                      GTK_SCROLL_END);

459
  add_slider_binding (binding_set, GDK_KEY_KP_End, 0,
460
                      GTK_SCROLL_END);
461

462
  g_type_class_add_private (gobject_class, sizeof (GtkScalePrivate));
463 464

  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SCALE_ACCESSIBLE);
Elliot Lee's avatar
Elliot Lee committed
465 466
}

467 468 469
static void
gtk_scale_init (GtkScale *scale)
{
470
  GtkScalePrivate *priv;
471
  GtkRange *range = GTK_RANGE (scale);
472
  GtkStyleContext *context;
473

474 475
  scale->priv = G_TYPE_INSTANCE_GET_PRIVATE (scale,
                                             GTK_TYPE_SCALE,
476
                                             GtkScalePrivate);
477 478
  priv = scale->priv;

479
  gtk_widget_set_can_focus (GTK_WIDGET (scale), TRUE);
480

481
  gtk_range_set_slider_size_fixed (range, TRUE);
482

483 484
  _gtk_range_set_has_origin (range, TRUE);

485 486 487
  priv->draw_value = TRUE;
  priv->value_pos = GTK_POS_TOP;
  priv->digits = 1;
488
  gtk_range_set_round_digits (range, priv->digits);
489

490 491
  gtk_range_set_flippable (range,
                           gtk_orientable_get_orientation (GTK_ORIENTABLE (range))== GTK_ORIENTATION_HORIZONTAL);
492 493 494

  context = gtk_widget_get_style_context (GTK_WIDGET (scale));
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_SCALE);
495 496
}

497
static void
498 499 500 501
gtk_scale_set_property (GObject      *object,
			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
502 503 504 505 506
{
  GtkScale *scale;

  scale = GTK_SCALE (object);

507
  switch (prop_id)
508
    {
509 510
    case PROP_DIGITS:
      gtk_scale_set_digits (scale, g_value_get_int (value));
511
      break;
512 513
    case PROP_DRAW_VALUE:
      gtk_scale_set_draw_value (scale, g_value_get_boolean (value));
514
      break;
515 516 517
    case PROP_HAS_ORIGIN:
      gtk_scale_set_has_origin (scale, g_value_get_boolean (value));
      break;
518 519
    case PROP_VALUE_POS:
      gtk_scale_set_value_pos (scale, g_value_get_enum (value));
520 521
      break;
    default:
522
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
523 524 525 526 527
      break;
    }
}

static void
528 529 530 531
gtk_scale_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
532
{
533
  GtkScale *scale = GTK_SCALE (object);
534
  GtkScalePrivate *priv = scale->priv;
535

536
  switch (prop_id)
537
    {
538
    case PROP_DIGITS:
539
      g_value_set_int (value, priv->digits);
540
      break;
541
    case PROP_DRAW_VALUE:
542
      g_value_set_boolean (value, priv->draw_value);
543
      break;
544 545 546
    case PROP_HAS_ORIGIN:
      g_value_set_boolean (value, gtk_scale_get_has_origin (scale));
      break;
547
    case PROP_VALUE_POS:
548
      g_value_set_enum (value, priv->value_pos);
549 550
      break;
    default:
551
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
552 553 554 555
      break;
    }
}

556 557 558
/**
 * gtk_scale_new:
 * @orientation: the scale's orientation.
559 560
 * @adjustment: (allow-none): the #GtkAdjustment which sets the range
 *              of the scale, or %NULL to create a new adjustment.
561 562 563 564 565
 *
 * Creates a new #GtkScale.
 *
 * Return value: a new #GtkScale
 *
566
 * Since: 3.0
567 568 569 570
 **/
GtkWidget *
gtk_scale_new (GtkOrientation  orientation,
               GtkAdjustment  *adjustment)
571
{
572 573
  g_return_val_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment),
                        NULL);
574

575 576 577 578 579
  return g_object_new (GTK_TYPE_SCALE,
                       "orientation", orientation,
                       "adjustment",  adjustment,
                       NULL);
}
580

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
/**
 * gtk_scale_new_with_range:
 * @orientation: the scale's orientation.
 * @min: minimum value
 * @max: maximum value
 * @step: step increment (tick size) used with keyboard shortcuts
 *
 * Creates a new scale widget with the given orientation that lets the
 * user input a number between @min and @max (including @min and @max)
 * with the increment @step.  @step must be nonzero; it's the distance
 * the slider moves when using the arrow keys to adjust the scale
 * value.
 *
 * Note that the way in which the precision is derived works best if @step
 * is a power of ten. If the resulting precision is not suitable for your
 * needs, use gtk_scale_set_digits() to correct it.
 *
 * Return value: a new #GtkScale
 *
600 601
 * Since: 3.0
 */
602 603 604 605 606 607
GtkWidget *
gtk_scale_new_with_range (GtkOrientation orientation,
                          gdouble        min,
                          gdouble        max,
                          gdouble        step)
{
608
  GtkAdjustment *adj;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
  gint digits;

  g_return_val_if_fail (min < max, NULL);
  g_return_val_if_fail (step != 0.0, NULL);

  adj = gtk_adjustment_new (min, min, max, step, 10 * step, 0);

  if (fabs (step) >= 1.0 || step == 0.0)
    {
      digits = 0;
    }
  else
    {
      digits = abs ((gint) floor (log10 (fabs (step))));
      if (digits > 5)
        digits = 5;
    }

  return g_object_new (GTK_TYPE_SCALE,
                       "orientation", orientation,
                       "adjustment",  adj,
                       "digits",      digits,
                       NULL);
632 633
}

Matthias Clasen's avatar
Matthias Clasen committed
634 635 636
/**
 * gtk_scale_set_digits:
 * @scale: a #GtkScale
637
 * @digits: the number of decimal places to display,
Matthias Clasen's avatar
Matthias Clasen committed
638
 *     e.g. use 1 to display 1.0, 2 to display 1.00, etc
639
 *
Matthias Clasen's avatar
Matthias Clasen committed
640 641 642 643
 * Sets the number of decimal places that are displayed in the value.
 * Also causes the value of the adjustment to be rounded off to this
 * number of digits, so the retrieved value matches the value the user saw.
 */
Elliot Lee's avatar
Elliot Lee committed
644 645 646 647
void
gtk_scale_set_digits (GtkScale *scale,
		      gint      digits)
{
648
  GtkScalePrivate *priv;
649
  GtkRange *range;
650

Elliot Lee's avatar
Elliot Lee committed
651 652
  g_return_if_fail (GTK_IS_SCALE (scale));

653
  priv = scale->priv;
654 655
  range = GTK_RANGE (scale);
  
656
  digits = CLAMP (digits, -1, MAX_DIGITS);
657

658
  if (priv->digits != digits)
Elliot Lee's avatar
Elliot Lee committed
659
    {
660 661
      priv->digits = digits;
      if (priv->draw_value)
662
        gtk_range_set_round_digits (range, digits);
663

664
      _gtk_scale_clear_layout (scale);
665
      gtk_widget_queue_resize (GTK_WIDGET (scale));
666 667

      g_object_notify (G_OBJECT (scale), "digits");
Elliot Lee's avatar
Elliot Lee committed
668 669 670
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
671 672 673 674 675 676 677 678
/**
 * gtk_scale_get_digits:
 * @scale: a #GtkScale
 *
 * Gets the number of decimal places that are displayed in the value.
 *
 * Returns: the number of decimal places that are displayed
 */
679 680 681 682 683
gint
gtk_scale_get_digits (GtkScale *scale)
{
  g_return_val_if_fail (GTK_IS_SCALE (scale), -1);

684
  return scale->priv->digits;
685 686
}

Matthias Clasen's avatar
Matthias Clasen committed
687 688 689
/**
 * gtk_scale_set_draw_value:
 * @scale: a #GtkScale
Matthias Clasen's avatar
Matthias Clasen committed
690
 * @draw_value: %TRUE to draw the value
Matthias Clasen's avatar
Matthias Clasen committed
691 692 693 694
 * 
 * Specifies whether the current value is displayed as a string next 
 * to the slider.
 */
Elliot Lee's avatar
Elliot Lee committed
695 696
void
gtk_scale_set_draw_value (GtkScale *scale,
697
			  gboolean  draw_value)
Elliot Lee's avatar
Elliot Lee committed
698
{
699
  GtkScalePrivate *priv;
700

Elliot Lee's avatar
Elliot Lee committed
701 702
  g_return_if_fail (GTK_IS_SCALE (scale));

703 704
  priv = scale->priv;

705 706
  draw_value = draw_value != FALSE;

707
  if (priv->draw_value != draw_value)
Elliot Lee's avatar
Elliot Lee committed
708
    {
709
      priv->draw_value = draw_value;
710
      if (draw_value)
711
        gtk_range_set_round_digits (GTK_RANGE (scale), priv->digits);
712
      else
713
        gtk_range_set_round_digits (GTK_RANGE (scale), -1);
Elliot Lee's avatar
Elliot Lee committed
714

715 716
      _gtk_scale_clear_layout (scale);

717
      gtk_widget_queue_resize (GTK_WIDGET (scale));
718

719
      g_object_notify (G_OBJECT (scale), "draw-value");
Elliot Lee's avatar
Elliot Lee committed
720 721 722
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
723 724 725 726 727 728 729 730 731
/**
 * gtk_scale_get_draw_value:
 * @scale: a #GtkScale
 *
 * Returns whether the current value is displayed as a string 
 * next to the slider.
 *
 * Returns: whether the current value is displayed as a string
 */
732 733 734 735 736
gboolean
gtk_scale_get_draw_value (GtkScale *scale)
{
  g_return_val_if_fail (GTK_IS_SCALE (scale), FALSE);

737
  return scale->priv->draw_value;
738 739
}

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
/**
 * gtk_scale_set_has_origin:
 * @scale: a #GtkScale
 * @has_origin: %TRUE if the scale has an origin
 * 
 * If @has_origin is set to %TRUE (the default),
 * the scale will highlight the part of the scale
 * between the origin (bottom or left side) of the scale
 * and the current value.
 *
 * Since: 3.4
 */
void
gtk_scale_set_has_origin (GtkScale *scale,
                          gboolean  has_origin)
{
  g_return_if_fail (GTK_IS_SCALE (scale));

  has_origin = has_origin != FALSE;

  if (_gtk_range_get_has_origin (GTK_RANGE (scale)) != has_origin)
    {
      _gtk_range_set_has_origin (GTK_RANGE (scale), has_origin);

      gtk_widget_queue_draw (GTK_WIDGET (scale));

      g_object_notify (G_OBJECT (scale), "has-origin");
    }
}

/**
 * gtk_scale_get_has_origin:
 * @scale: a #GtkScale
 *
 * Returns whether the scale has an origin.
 *
 * Returns: %TRUE if the scale has an origin.
 * 
 * Since: 3.4
 */
gboolean
gtk_scale_get_has_origin (GtkScale *scale)
{
  g_return_val_if_fail (GTK_IS_SCALE (scale), FALSE);

  return _gtk_range_get_has_origin (GTK_RANGE (scale));
}

Matthias Clasen's avatar
Matthias Clasen committed
788 789 790 791 792 793 794
/**
 * gtk_scale_set_value_pos:
 * @scale: a #GtkScale
 * @pos: the position in which the current value is displayed
 * 
 * Sets the position in which the current value is displayed.
 */
Elliot Lee's avatar
Elliot Lee committed
795 796 797 798
void
gtk_scale_set_value_pos (GtkScale        *scale,
			 GtkPositionType  pos)
{
799
  GtkScalePrivate *priv;
800 801
  GtkWidget *widget;

Elliot Lee's avatar
Elliot Lee committed
802 803
  g_return_if_fail (GTK_IS_SCALE (scale));

804 805 806
  priv = scale->priv;

  if (priv->value_pos != pos)
Elliot Lee's avatar
Elliot Lee committed
807
    {
808
      priv->value_pos = pos;
809
      widget = GTK_WIDGET (scale);
Elliot Lee's avatar
Elliot Lee committed
810

811
      _gtk_scale_clear_layout (scale);
812
      if (gtk_widget_get_visible (widget) && gtk_widget_get_mapped (widget))
813
	gtk_widget_queue_resize (widget);
814

815
      g_object_notify (G_OBJECT (scale), "value-pos");
Elliot Lee's avatar
Elliot Lee committed
816 817 818
    }
}

Matthias Clasen's avatar
Matthias Clasen committed
819 820 821 822 823 824 825 826
/**
 * gtk_scale_get_value_pos:
 * @scale: a #GtkScale
 *
 * Gets the position in which the current value is displayed.
 *
 * Returns: the position in which the current value is displayed
 */
827 828 829 830 831
GtkPositionType
gtk_scale_get_value_pos (GtkScale *scale)
{
  g_return_val_if_fail (GTK_IS_SCALE (scale), 0);

832
  return scale->priv->value_pos;
833 834 835 836 837 838
}

static void
gtk_scale_get_range_border (GtkRange  *range,
                            GtkBorder *border)
{
839
  GtkScalePrivate *priv;
840 841 842 843 844 845
  GtkWidget *widget;
  GtkScale *scale;
  gint w, h;
  
  widget = GTK_WIDGET (range);
  scale = GTK_SCALE (range);
846
  priv = scale->priv;
847 848 849 850 851 852 853 854

  _gtk_scale_get_value_size (scale, &w, &h);

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

855
  if (priv->draw_value)
856 857
    {
      gint value_spacing;
858
      gtk_widget_style_get (widget, "value-spacing", &value_spacing, NULL);
859

860
      switch (priv->value_pos)
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
        {
        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;
        }
    }
876 877 878 879 880 881 882 883 884 885 886 887 888

  if (priv->marks)
    {
      gint slider_width;
      gint value_spacing;
      gint n1, w1, h1, n2, w2, h2;
  
      gtk_widget_style_get (widget, 
                            "slider-width", &slider_width,
                            "value-spacing", &value_spacing, 
                            NULL);


889 890
      gtk_scale_get_mark_label_size (scale, GTK_POS_TOP, &n1, &w1, &h1, &n2, &w2, &h2);

891
      if (gtk_orientable_get_orientation (GTK_ORIENTABLE (scale)) == GTK_ORIENTATION_HORIZONTAL)
892 893 894 895
        {
          if (n1 > 0)
            border->top += h1 + value_spacing + slider_width / 2;
          if (n2 > 0)
896
            border->bottom += h2 + value_spacing + slider_width / 2;
897 898 899 900 901 902 903 904 905
        }
      else
        {
          if (n1 > 0)
            border->left += w1 + value_spacing + slider_width / 2;
          if (n2 > 0)
            border->right += w2 + value_spacing + slider_width / 2;
        }
    }
906 907 908
}

/* FIXME this could actually be static at the moment. */
909
void
910 911 912
_gtk_scale_get_value_size (GtkScale *scale,
                           gint     *width,
                           gint     *height)
Elliot Lee's avatar
Elliot Lee committed
913
{
914
  GtkScalePrivate *priv = scale->priv;
Elliot Lee's avatar
Elliot Lee committed
915 916
  GtkRange *range;

917
  if (priv->draw_value)
Elliot Lee's avatar
Elliot Lee committed
918
    {
919
      GtkAdjustment *adjustment;
920 921
      PangoLayout *layout;
      PangoRectangle logical_rect;
Havoc Pennington's avatar
Havoc Pennington committed
922
      gchar *txt;
923
      
Elliot Lee's avatar
Elliot Lee committed
924 925
      range = GTK_RANGE (scale);

926
      layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale), NULL);
927
      adjustment = gtk_range_get_adjustment (range);
928

929
      txt = _gtk_scale_format_value (scale, gtk_adjustment_get_lower (adjustment));
Havoc Pennington's avatar
Havoc Pennington committed
930 931 932
      pango_layout_set_text (layout, txt, -1);
      g_free (txt);
      
933
      pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
Elliot Lee's avatar
Elliot Lee committed
934

935
      if (width)
936
	*width = logical_rect.width;
937
      if (height)
Havoc Pennington's avatar
Havoc Pennington committed
938 939
	*height = logical_rect.height;

940
      txt = _gtk_scale_format_value (scale, gtk_adjustment_get_upper (adjustment));
Havoc Pennington's avatar
Havoc Pennington committed
941 942
      pango_layout_set_text (layout, txt, -1);
      g_free (txt);
943
      
944
      pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
945 946

      if (width)
947
	*width = MAX (*width, logical_rect.width);
948
      if (height)
949
	*height = MAX (*height, logical_rect.height);
950

Manish Singh's avatar
Manish Singh committed
951
      g_object_unref (layout);
Elliot Lee's avatar
Elliot Lee committed
952
    }
953 954 955 956 957 958 959 960 961 962
  else
    {
      if (width)
	*width = 0;
      if (height)
	*height = 0;
    }

}

963 964 965 966 967 968 969 970 971 972
static void
gtk_scale_get_mark_label_size (GtkScale        *scale,
                               GtkPositionType  position,
                               gint            *count1,
                               gint            *width1,
                               gint            *height1,
                               gint            *count2,
                               gint            *width2,
                               gint            *height2)
{
973
  GtkScalePrivate *priv = scale->priv;
974 975
  PangoLayout *layout;
  PangoRectangle logical_rect;
976
  GSList *m;
977 978 979 980 981 982 983 984 985 986 987 988
  gint w, h;

  *count1 = *count2 = 0;
  *width1 = *width2 = 0;
  *height1 = *height2 = 0;

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

  for (m = priv->marks; m; m = m->next)
    {
      GtkScaleMark *mark = m->data;

989
      if (mark->markup && *mark->markup)
990 991 992 993
        {
          pango_layout_set_markup (layout, mark->markup, -1);
          pango_layout_get_pixel_extents (layout, NULL, &logical_rect);

994 995
          w = logical_rect.width;
          h = logical_rect.height;
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
        }
      else
        {
          w = 0;
          h = 0;
        }

      if (mark->position == position)
        {
          (*count1)++;
          *width1 = MAX (*width1, w);
          *height1 = MAX (*height1, h);
        }
      else
        {
          (*count2)++;
          *width2 = MAX (*width2, w);
          *height2 = MAX (*height2, h);
        }
    }

  g_object_unref (layout);
}

1020
static void
1021
gtk_scale_style_updated (GtkWidget *widget)
1022
{
1023 1024
  gint slider_length;
  GtkRange *range;
1025

1026
  range = GTK_RANGE (widget);
1027
  
1028
  gtk_widget_style_get (widget,
1029
                        "slider-length", &slider_length,
1030
                        NULL);
1031 1032 1033

  gtk_range_set_min_slider_size (range, slider_length);

1034 1035
  _gtk_scale_clear_layout (GTK_SCALE (widget));

1036
  GTK_WIDGET_CLASS (gtk_scale_parent_class)->style_updated (widget);
Elliot Lee's avatar
Elliot Lee committed
1037 1038
}

1039 1040 1041 1042 1043 1044
static void
gtk_scale_screen_changed (GtkWidget *widget,
                          GdkScreen *old_screen)
{
  _gtk_scale_clear_layout (GTK_SCALE (widget));
}
Elliot Lee's avatar
Elliot Lee committed
1045

1046
static void
1047 1048 1049
gtk_scale_get_preferred_width (GtkWidget *widget,
                               gint      *minimum,
                               gint      *natural)
1050
{
1051
  GTK_WIDGET_CLASS (gtk_scale_parent_class)->get_preferred_width (widget, minimum, natural);
1052
  
1053 1054 1055 1056 1057
  if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL)
    {
      gint n1, w1, h1, n2, w2, h2;
      gint slider_length;
      gint w;
1058

1059
      gtk_widget_style_get (widget, "slider-length", &slider_length, NULL);
1060 1061 1062 1063 1064

      gtk_scale_get_mark_label_size (GTK_SCALE (widget), GTK_POS_TOP, &n1, &w1, &h1, &n2, &w2, &h2);

      w1 = (n1 - 1) * w1 + MAX (w1, slider_length);
      w2 = (n2 - 1) * w2 + MAX (w2, slider_length);
1065 1066 1067 1068
      w = MAX (w1, w2);

      *minimum = MAX (*minimum, w);
      *natural = MAX (*natural, w);
1069
    }
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
}

static void
gtk_scale_get_preferred_height (GtkWidget *widget,
                                gint      *minimum,
                                gint      *natural)
{
  GTK_WIDGET_CLASS (gtk_scale_parent_class)->get_preferred_height (widget, minimum, natural);


  if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_VERTICAL)
1081
    {
1082 1083 1084 1085 1086 1087
      gint n1, w1, h1, n2, w2, h2;
      gint slider_length;
      gint h;

      gtk_widget_style_get (widget, "slider-length", &slider_length, NULL);

1088
      gtk_scale_get_mark_label_size (GTK_SCALE (widget), GTK_POS_TOP, &n1, &w1, &h1, &n2, &w2, &h2);
1089 1090
      h1 = (n1 - 1) * h1 + MAX (h1, slider_length);
      h2 = (n2 - 1) * h1 + MAX (h2, slider_length);
1091 1092 1093 1094
      h = MAX (h1, h2);

      *minimum = MAX (*minimum, h);
      *natural = MAX (*natural, h);
1095 1096 1097
    }
}

1098
static gint
1099
find_next_pos (GtkWidget       *widget,
1100
               GSList          *list,
1101
               gint            *marks,
1102
               GtkPositionType  pos)
1103
{
1104
  GtkAllocation allocation;
1105
  GSList *m;
1106 1107 1108 1109 1110 1111
  gint i;

  for (m = list->next, i = 1; m; m = m->next, i++)
    {
      GtkScaleMark *mark = m->data;

1112
      if (mark->position == pos)
1113 1114 1115
        return marks[i];
    }

1116
  gtk_widget_get_allocation (widget, &allocation);
1117
  if (gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)) == GTK_ORIENTATION_HORIZONTAL)
1118
    return allocation.width;
1119
  else
1120
    return allocation.height;
1121 1122
}

1123
static gboolean
1124 1125
gtk_scale_draw (GtkWidget *widget,
                cairo_t   *cr)
1126 1127
{
  GtkScale *scale = GTK_SCALE (widget);
1128
  GtkScalePrivate *priv = scale->priv;
1129
  GtkRange *range = GTK_RANGE (scale);
1130
  GtkStyleContext *context;
1131 1132 1133 1134
  gint *marks;
  gint focus_padding;
  gint slider_width;
  gint value_spacing;
1135
  gint min_sep = 4;
1136

1137
  context = gtk_widget_get_style_context (widget);
1138 1139
  gtk_widget_style_get (widget,
                        "focus-padding", &focus_padding,
1140 1141
                        "slider-width", &slider_width,
                        "value-spacing", &value_spacing,
1142
                        NULL);
1143 1144 1145 1146

  /* We need to chain up _first_ so the various geometry members of
   * GtkRange struct are updated.
   */
1147
  GTK_WIDGET_CLASS (gtk_scale_parent_class)->draw (widget, cr);
1148

1149 1150
  if (priv->marks)
    {
1151 1152
      GtkOrientation orientation;
      GdkRectangle range_rect;
1153 1154 1155 1156
      gint i;
      gint x1, x2, x3, y1, y2, y3;
      PangoLayout *layout;
      PangoRectangle logical_rect;
1157
      GSList *m;
1158 1159
      gint min_pos_before, min_pos_after;
      gint min_pos, max_pos;
1160

1161
      orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (range));
1162
      _gtk_range_get_stop_positions (range, &marks);
1163

1164
      layout = gtk_widget_create_pango_layout (widget, NULL);
1165
      gtk_range_get_range_rect (range, &range_rect);
1166

1167 1168
      min_pos_before = min_pos_after = 0;

1169 1170 1171
      for (m = priv->marks, i = 0; m; m = m->next, i++)
        {
          GtkScaleMark *mark = m->data;
1172 1173

          if (orientation == GTK_ORIENTATION_HORIZONTAL)
1174
            {
1175
              x1 = marks[i];
1176 1177
              if (mark->position == GTK_POS_TOP)
                {
1178
                  y1 = range_rect.y;
1179
                  y2 = y1 - slider_width / 2;
1180
                  min_pos = min_pos_before;
1181
                  max_pos = find_next_pos (widget, m, marks + i, GTK_POS_TOP) - min_sep;
1182 1183 1184
                }
              else
                {
1185
                  y1 = range_rect.y + range_rect.height;
1186
                  y2 = y1 + slider_width / 2;
1187
                  min_pos = min_pos_after;
1188
                  max_pos = find_next_pos (widget, m, marks + i, GTK_POS_BOTTOM) - min_sep;
1189 1190
                }

1191 1192 1193
              gtk_style_context_save (context);
              gtk_style_context_add_class (context, GTK_STYLE_CLASS_MARK);

1194
              gtk_render_line (context, cr, x1, y1, x1, y2);
1195 1196 1197 1198 1199

              if (mark->markup)
                {
                  pango_layout_set_markup (layout, mark->markup, -1);
                  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1200

1201
                  x3 = x1 - logical_rect.width / 2;
1202 1203 1204 1205
                  if (x3 < min_pos)
                    x3 = min_pos;
                  if (x3 + logical_rect.width > max_pos)
                        x3 = max_pos - logical_rect.width;
1206 1207
                  if (x3 < 0)
                     x3 = 0;
1208
                  if (mark->position == GTK_POS_TOP)
1209 1210 1211 1212
                    {
                      y3 = y2 - value_spacing - logical_rect.height;
                      min_pos_before = x3 + logical_rect.width + min_sep;
                    }
1213
                  else
1214 1215 1216 1217
                    {
                      y3 = y2 + value_spacing;
                      min_pos_after = x3 + logical_rect.width + min_sep;
                    }
1218

1219
                  gtk_render_layout (context, cr, x3, y3, layout);
1220
                }
1221 1222

              gtk_style_context_restore (context);
1223 1224 1225
            }
          else
            {
1226
              if (mark->position == GTK_POS_TOP)
1227
                {
1228 1229
                  x1 = range_rect.x;
                  x2 = range_rect.x - slider_width / 2;
1230
                  min_pos = min_pos_before;
1231
                  max_pos = find_next_pos (widget, m, marks + i, GTK_POS_TOP) - min_sep;
1232 1233 1234
                }
              else
                {
1235 1236
                  x1 = range_rect.x + range_rect.width;
                  x2 = range_rect.x + range_rect.width + slider_width / 2;
1237
                  min_pos = min_pos_after;
1238
                  max_pos = find_next_pos (widget, m, marks + i, GTK_POS_BOTTOM) - min_sep;
1239
                }
1240
              y1 = marks[i];
1241

1242 1243 1244
              gtk_style_context_save (context);
              gtk_style_context_add_class (context, GTK_STYLE_CLASS_MARK);

1245
              gtk_render_line (context, cr, x1, y1, x2, y1);
1246 1247 1248 1249 1250

              if (mark->markup)
                {
                  pango_layout_set_markup (layout, mark->markup, -1);
                  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1251

1252 1253 1254 1255 1256
                  y3 = y1 - logical_rect.height / 2;
                  if (y3 < min_pos)
                    y3 = min_pos;
                  if (y3 + logical_rect.height > max_pos)
                    y3 = max_pos - logical_rect.height;
1257 1258
                  if (y3 < 0)
                    y3 = 0;
1259
                  if (mark->position == GTK_POS_TOP)
1260 1261 1262 1263
                    {
                      x3 = x2 - value_spacing - logical_rect.width;
                      min_pos_before = y3 + logical_rect.height + min_sep;
                    }
1264
                  else
1265 1266 1267 1268
                    {
                      x3 = x2 + value_spacing;
                      min_pos_after = y3 + logical_rect.height + min_sep;
                    }
1269

1270
                  gtk_render_layout (context, cr, x3, y3, layout);
1271
                }
1272 1273

              gtk_style_context_restore (context);