gtkruler.c 26.5 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * 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
18
 */
19 20

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

27
#include "config.h"
28 29 30 31 32

#include <math.h>
#include <string.h>

#include "gtkorientable.h"
Elliot Lee's avatar
Elliot Lee committed
33
#include "gtkruler.h"
34
#include "gtkprivate.h"
35
#include "gtkintl.h"
36 37 38 39 40 41 42 43

#define RULER_WIDTH           14
#define MINIMUM_INCR          5
#define MAXIMUM_SUBDIVIDE     5
#define MAXIMUM_SCALES        10

#define ROUND(x) ((int) ((x) + 0.5))

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
struct _GtkRulerPriv
{
  GtkOrientation        orientation;
  GtkRulerMetric       *metric;

  GdkPixmap            *backing_store;

  gint                  slider_size;
  gint                  xsrc;
  gint                  ysrc;

  gdouble               lower;          /* The upper limit of the ruler (in points) */
  gdouble               max_size;       /* The maximum size of the ruler */
  gdouble               position;       /* The position of the mark on the ruler */
  gdouble               upper;          /* The lower limit of the ruler */
};

61
enum {
62
  PROP_0,
63
  PROP_ORIENTATION,
64 65 66
  PROP_LOWER,
  PROP_UPPER,
  PROP_POSITION,
67 68
  PROP_MAX_SIZE,
  PROP_METRIC
69
};
Elliot Lee's avatar
Elliot Lee committed
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

static void     gtk_ruler_set_property    (GObject        *object,
                                           guint            prop_id,
                                           const GValue   *value,
                                           GParamSpec     *pspec);
static void     gtk_ruler_get_property    (GObject        *object,
                                           guint           prop_id,
                                           GValue         *value,
                                           GParamSpec     *pspec);
static void     gtk_ruler_realize         (GtkWidget      *widget);
static void     gtk_ruler_unrealize       (GtkWidget      *widget);
static void     gtk_ruler_size_request    (GtkWidget      *widget,
                                           GtkRequisition *requisition);
static void     gtk_ruler_size_allocate   (GtkWidget      *widget,
                                           GtkAllocation  *allocation);
static gboolean gtk_ruler_motion_notify   (GtkWidget      *widget,
                                           GdkEventMotion *event);
static gboolean gtk_ruler_expose          (GtkWidget      *widget,
                                           GdkEventExpose *event);
static void     gtk_ruler_make_pixmap     (GtkRuler       *ruler);
static void     gtk_ruler_real_draw_ticks (GtkRuler       *ruler);
static void     gtk_ruler_real_draw_pos   (GtkRuler       *ruler);

Elliot Lee's avatar
Elliot Lee committed
94

95
static const GtkRulerMetric ruler_metrics[] =
Elliot Lee's avatar
Elliot Lee committed
96
{
97 98 99
  { "Pixel", "Pi", 1.0, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
  { "Inches", "In", 72.0, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
  { "Centimeters", "Cn", 28.35, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
Elliot Lee's avatar
Elliot Lee committed
100 101
};

102 103 104 105 106

G_DEFINE_TYPE_WITH_CODE (GtkRuler, gtk_ruler, GTK_TYPE_WIDGET,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
                                                NULL))

Elliot Lee's avatar
Elliot Lee committed
107 108 109 110

static void
gtk_ruler_class_init (GtkRulerClass *class)
{
111 112
  GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class  = GTK_WIDGET_CLASS (class);
Elliot Lee's avatar
Elliot Lee committed
113

114 115
  gobject_class->set_property = gtk_ruler_set_property;
  gobject_class->get_property = gtk_ruler_get_property;
116

Elliot Lee's avatar
Elliot Lee committed
117 118
  widget_class->realize = gtk_ruler_realize;
  widget_class->unrealize = gtk_ruler_unrealize;
119
  widget_class->size_request = gtk_ruler_size_request;
Elliot Lee's avatar
Elliot Lee committed
120
  widget_class->size_allocate = gtk_ruler_size_allocate;
121
  widget_class->motion_notify_event = gtk_ruler_motion_notify;
Elliot Lee's avatar
Elliot Lee committed
122 123
  widget_class->expose_event = gtk_ruler_expose;

124 125 126 127 128 129
  class->draw_ticks = gtk_ruler_real_draw_ticks;
  class->draw_pos = gtk_ruler_real_draw_pos;

  g_object_class_override_property (gobject_class,
                                    PROP_ORIENTATION,
                                    "orientation");
130

131 132 133
  g_object_class_install_property (gobject_class,
                                   PROP_LOWER,
                                   g_param_spec_double ("lower",
134 135
							P_("Lower"),
							P_("Lower limit of ruler"),
136 137 138
							-G_MAXDOUBLE,
							G_MAXDOUBLE,
							0.0,
139
							GTK_PARAM_READWRITE));  
140 141 142 143

  g_object_class_install_property (gobject_class,
                                   PROP_UPPER,
                                   g_param_spec_double ("upper",
144 145
							P_("Upper"),
							P_("Upper limit of ruler"),
146 147 148
							-G_MAXDOUBLE,
							G_MAXDOUBLE,
							0.0,
149
							GTK_PARAM_READWRITE));  
150 151 152 153

  g_object_class_install_property (gobject_class,
                                   PROP_POSITION,
                                   g_param_spec_double ("position",
154 155
							P_("Position"),
							P_("Position of mark on the ruler"),
156 157 158
							-G_MAXDOUBLE,
							G_MAXDOUBLE,
							0.0,
159
							GTK_PARAM_READWRITE));  
160 161 162

  g_object_class_install_property (gobject_class,
                                   PROP_MAX_SIZE,
163
                                   g_param_spec_double ("max-size",
164 165
							P_("Max Size"),
							P_("Maximum size of the ruler"),
166 167 168
							-G_MAXDOUBLE,
							G_MAXDOUBLE,
							0.0,
169
							GTK_PARAM_READWRITE));  
Matthias Clasen's avatar
Matthias Clasen committed
170 171 172 173 174 175 176
  /**
   * GtkRuler:metric:
   *
   * The metric used for the ruler.
   *
   * Since: 2.8
   */
177 178 179 180 181 182 183 184
  g_object_class_install_property (gobject_class,
                                   PROP_METRIC,
                                   g_param_spec_enum ("metric",
						      P_("Metric"),
						      P_("The metric used for the ruler"),
						      GTK_TYPE_METRIC_TYPE, 
						      GTK_PIXELS,
						      GTK_PARAM_READWRITE));  
185

186
  g_type_class_add_private (gobject_class, sizeof (GtkRulerPriv));
Elliot Lee's avatar
Elliot Lee committed
187 188 189 190 191
}

static void
gtk_ruler_init (GtkRuler *ruler)
{
192
  GtkWidget *widget = GTK_WIDGET (ruler);
193 194 195 196 197 198
  GtkRulerPriv *priv;

  ruler->priv = G_TYPE_INSTANCE_GET_PRIVATE (ruler,
                                             GTK_TYPE_RULER,
                                             GtkRulerPriv);
  priv = ruler->priv;
199

200
  priv->orientation = GTK_ORIENTATION_HORIZONTAL;
201 202 203 204

  widget->requisition.width  = widget->style->xthickness * 2 + 1;
  widget->requisition.height = widget->style->ythickness * 2 + RULER_WIDTH;

205 206 207 208 209 210 211 212
  priv->backing_store = NULL;
  priv->xsrc = 0;
  priv->ysrc = 0;
  priv->slider_size = 0;
  priv->lower = 0;
  priv->upper = 0;
  priv->position = 0;
  priv->max_size = 0;
Elliot Lee's avatar
Elliot Lee committed
213 214 215 216

  gtk_ruler_set_metric (ruler, GTK_PIXELS);
}

217
static void
218 219 220 221
gtk_ruler_set_property (GObject      *object,
 			guint         prop_id,
			const GValue *value,
			GParamSpec   *pspec)
222 223
{
  GtkRuler *ruler = GTK_RULER (object);
224
  GtkRulerPriv *priv = ruler->priv;
225

226
  switch (prop_id)
227
    {
228
    case PROP_ORIENTATION:
229
      priv->orientation = g_value_get_enum (value);
230 231
      gtk_widget_queue_resize (GTK_WIDGET (ruler));
      break;
232
    case PROP_LOWER:
233 234
      gtk_ruler_set_range (ruler, g_value_get_double (value), priv->upper,
                           priv->position, priv->max_size);
235
      break;
236
    case PROP_UPPER:
237 238
      gtk_ruler_set_range (ruler, priv->lower, g_value_get_double (value),
                           priv->position, priv->max_size);
239
      break;
240
    case PROP_POSITION:
241 242
      gtk_ruler_set_range (ruler, priv->lower, priv->upper,
                           g_value_get_double (value), priv->max_size);
243
      break;
244
    case PROP_MAX_SIZE:
245 246
      gtk_ruler_set_range (ruler, priv->lower, priv->upper,
                           priv->position,  g_value_get_double (value));
247
      break;
248 249 250 251 252 253
    case PROP_METRIC:
      gtk_ruler_set_metric (ruler, g_value_get_enum (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
254 255 256 257
    }
}

static void
258 259 260 261
gtk_ruler_get_property (GObject      *object,
			guint         prop_id,
			GValue       *value,
			GParamSpec   *pspec)
262 263
{
  GtkRuler *ruler = GTK_RULER (object);
264
  GtkRulerPriv *priv = ruler->priv;
265

266
  switch (prop_id)
267
    {
268
    case PROP_ORIENTATION:
269
      g_value_set_enum (value, priv->orientation);
270
      break;
271
    case PROP_LOWER:
272
      g_value_set_double (value, priv->lower);
273
      break;
274
    case PROP_UPPER:
275
      g_value_set_double (value, priv->upper);
276
      break;
277
    case PROP_POSITION:
278
      g_value_set_double (value, priv->position);
279
      break;
280
    case PROP_MAX_SIZE:
281
      g_value_set_double (value, priv->max_size);
282
      break;
283 284 285
    case PROP_METRIC:
      g_value_set_enum (value, gtk_ruler_get_metric (ruler));
      break;
286
    default:
287
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288 289 290 291
      break;
    }
}

292 293 294 295 296 297 298 299
/**
 * gtk_ruler_new:
 * @orientation: the ruler's orientation.
 *
 * Creates a new #GtkRuler with the given orientation.
 *
 * Return value: a new #GtkRuler.
 *
300
 * Since: 3.0
301 302 303 304 305 306 307 308 309
 **/
GtkWidget *
gtk_ruler_new (GtkOrientation orientation)
{
  return g_object_new (GTK_TYPE_RULER,
                       "orientation", orientation,
                       NULL);
}

Elliot Lee's avatar
Elliot Lee committed
310 311 312 313
void
gtk_ruler_set_metric (GtkRuler      *ruler,
		      GtkMetricType  metric)
{
314 315
  GtkRulerPriv *priv;

Elliot Lee's avatar
Elliot Lee committed
316 317
  g_return_if_fail (GTK_IS_RULER (ruler));

318 319 320
  priv = ruler->priv;

  priv->metric = (GtkRulerMetric *) &ruler_metrics[metric];
Elliot Lee's avatar
Elliot Lee committed
321

322
  if (gtk_widget_is_drawable (GTK_WIDGET (ruler)))
Elliot Lee's avatar
Elliot Lee committed
323
    gtk_widget_queue_draw (GTK_WIDGET (ruler));
324 325

  g_object_notify (G_OBJECT (ruler), "metric");
Elliot Lee's avatar
Elliot Lee committed
326 327
}

328 329 330 331
/**
 * gtk_ruler_get_metric:
 * @ruler: a #GtkRuler
 *
332
 * Gets the units used for a #GtkRuler. See gtk_ruler_set_metric().
333 334 335 336 337 338
 *
 * Return value: the units currently used for @ruler
 **/
GtkMetricType
gtk_ruler_get_metric (GtkRuler *ruler)
{
339
  GtkRulerPriv *priv;
340 341 342 343
  gint i;

  g_return_val_if_fail (GTK_IS_RULER (ruler), 0);

344 345
  priv = ruler->priv;

346
  for (i = 0; i < G_N_ELEMENTS (ruler_metrics); i++)
347
    if (priv->metric == &ruler_metrics[i])
348 349 350 351 352 353 354
      return i;

  g_assert_not_reached ();

  return 0;
}

355 356 357 358 359 360 361 362 363 364 365
/**
 * gtk_ruler_set_range:
 * @ruler: the gtkruler
 * @lower: the lower limit of the ruler
 * @upper: the upper limit of the ruler
 * @position: the mark on the ruler
 * @max_size: the maximum size of the ruler used when calculating the space to
 * leave for the text
 *
 * This sets the range of the ruler. 
 */
Elliot Lee's avatar
Elliot Lee committed
366 367
void
gtk_ruler_set_range (GtkRuler *ruler,
368 369 370 371
		     gdouble   lower,
		     gdouble   upper,
		     gdouble   position,
		     gdouble   max_size)
Elliot Lee's avatar
Elliot Lee committed
372
{
373 374
  GtkRulerPriv *priv;

Elliot Lee's avatar
Elliot Lee committed
375 376
  g_return_if_fail (GTK_IS_RULER (ruler));

377 378
  priv = ruler->priv;

379
  g_object_freeze_notify (G_OBJECT (ruler));
380
  if (priv->lower != lower)
381
    {
382
      priv->lower = lower;
383 384
      g_object_notify (G_OBJECT (ruler), "lower");
    }
385
  if (priv->upper != upper)
386
    {
387
      priv->upper = upper;
388 389
      g_object_notify (G_OBJECT (ruler), "upper");
    }
390
  if (priv->position != position)
391
    {
392
      priv->position = position;
393 394
      g_object_notify (G_OBJECT (ruler), "position");
    }
395
  if (priv->max_size != max_size)
396
    {
397
      priv->max_size = max_size;
398
      g_object_notify (G_OBJECT (ruler), "max-size");
399
    }
400
  g_object_thaw_notify (G_OBJECT (ruler));
Elliot Lee's avatar
Elliot Lee committed
401

402
  if (gtk_widget_is_drawable (GTK_WIDGET (ruler)))
Elliot Lee's avatar
Elliot Lee committed
403 404 405
    gtk_widget_queue_draw (GTK_WIDGET (ruler));
}

406 407 408
/**
 * gtk_ruler_get_range:
 * @ruler: a #GtkRuler
409 410 411
 * @lower: (allow-none): location to store lower limit of the ruler, or %NULL
 * @upper: (allow-none): location to store upper limit of the ruler, or %NULL
 * @position: (allow-none): location to store the current position of the mark on the ruler, or %NULL
412 413 414 415 416 417 418 419 420 421 422 423 424
 * @max_size: location to store the maximum size of the ruler used when calculating
 *            the space to leave for the text, or %NULL.
 *
 * Retrieves values indicating the range and current position of a #GtkRuler.
 * See gtk_ruler_set_range().
 **/
void
gtk_ruler_get_range (GtkRuler *ruler,
		     gdouble  *lower,
		     gdouble  *upper,
		     gdouble  *position,
		     gdouble  *max_size)
{
425 426
  GtkRulerPriv *priv;

427 428
  g_return_if_fail (GTK_IS_RULER (ruler));

429 430
  priv = ruler->priv;

431
  if (lower)
432
    *lower = priv->lower;
433
  if (upper)
434
    *upper = priv->upper;
435
  if (position)
436
    *position = priv->position;
437
  if (max_size)
438
    *max_size = priv->max_size;
439 440
}

Elliot Lee's avatar
Elliot Lee committed
441 442 443 444 445
void
gtk_ruler_draw_ticks (GtkRuler *ruler)
{
  g_return_if_fail (GTK_IS_RULER (ruler));

446 447
  if (GTK_RULER_GET_CLASS (ruler)->draw_ticks)
    GTK_RULER_GET_CLASS (ruler)->draw_ticks (ruler);
Elliot Lee's avatar
Elliot Lee committed
448 449 450 451 452 453 454
}

void
gtk_ruler_draw_pos (GtkRuler *ruler)
{
  g_return_if_fail (GTK_IS_RULER (ruler));

455 456
  if (GTK_RULER_GET_CLASS (ruler)->draw_pos)
     GTK_RULER_GET_CLASS (ruler)->draw_pos (ruler);
Elliot Lee's avatar
Elliot Lee committed
457 458 459 460 461 462 463 464 465 466 467
}


static void
gtk_ruler_realize (GtkWidget *widget)
{
  GtkRuler *ruler;
  GdkWindowAttr attributes;
  gint attributes_mask;

  ruler = GTK_RULER (widget);
468 469

  gtk_widget_set_realized (widget, TRUE);
Elliot Lee's avatar
Elliot Lee committed
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485

  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;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK |
			    GDK_POINTER_MOTION_MASK |
			    GDK_POINTER_MOTION_HINT_MASK);

  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;

486
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
Elliot Lee's avatar
Elliot Lee committed
487 488 489 490 491 492 493 494 495 496 497
  gdk_window_set_user_data (widget->window, ruler);

  widget->style = gtk_style_attach (widget->style, widget->window);
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);

  gtk_ruler_make_pixmap (ruler);
}

static void
gtk_ruler_unrealize (GtkWidget *widget)
{
498
  GtkRuler *ruler = GTK_RULER (widget);
499
  GtkRulerPriv *priv = ruler->priv;
Elliot Lee's avatar
Elliot Lee committed
500

501
  if (priv->backing_store)
502
    {
503 504
      g_object_unref (priv->backing_store);
      priv->backing_store = NULL;
505
    }
Elliot Lee's avatar
Elliot Lee committed
506

507
  GTK_WIDGET_CLASS (gtk_ruler_parent_class)->unrealize (widget);
Elliot Lee's avatar
Elliot Lee committed
508 509
}

510 511 512 513
static void
gtk_ruler_size_request (GtkWidget      *widget,
                        GtkRequisition *requisition)
{
514 515
  GtkRuler *ruler = GTK_RULER (widget);
  GtkRulerPriv *priv = ruler->priv;
516

517
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
518 519 520 521 522 523 524 525 526 527 528
    {
      requisition->width  = widget->style->xthickness * 2 + 1;
      requisition->height = widget->style->ythickness * 2 + RULER_WIDTH;
    }
  else
    {
      requisition->width  = widget->style->xthickness * 2 + RULER_WIDTH;
      requisition->height = widget->style->ythickness * 2 + 1;
    }
}

Elliot Lee's avatar
Elliot Lee committed
529 530 531 532
static void
gtk_ruler_size_allocate (GtkWidget     *widget,
			 GtkAllocation *allocation)
{
533
  GtkRuler *ruler = GTK_RULER (widget);
Elliot Lee's avatar
Elliot Lee committed
534 535 536

  widget->allocation = *allocation;

537
  if (gtk_widget_get_realized (widget))
Elliot Lee's avatar
Elliot Lee committed
538 539 540 541 542 543 544 545 546
    {
      gdk_window_move_resize (widget->window,
			      allocation->x, allocation->y,
			      allocation->width, allocation->height);

      gtk_ruler_make_pixmap (ruler);
    }
}

547 548 549 550 551
static gboolean
gtk_ruler_motion_notify (GtkWidget      *widget,
                         GdkEventMotion *event)
{
  GtkRuler *ruler = GTK_RULER (widget);
552
  GtkRulerPriv *priv = ruler->priv;
553 554 555 556 557 558 559
  gint x;
  gint y;

  gdk_event_request_motions (event);
  x = event->x;
  y = event->y;

560 561
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
    priv->position = priv->lower + ((priv->upper - priv->lower) * x) / widget->allocation.width;
562
  else
563
    priv->position = priv->lower + ((priv->upper - priv->lower) * y) / widget->allocation.height;
564 565 566 567

  g_object_notify (G_OBJECT (ruler), "position");

  /*  Make sure the ruler has been allocated already  */
568
  if (priv->backing_store != NULL)
569 570 571 572 573 574
    gtk_ruler_draw_pos (ruler);

  return FALSE;
}

static gboolean
Elliot Lee's avatar
Elliot Lee committed
575 576 577
gtk_ruler_expose (GtkWidget      *widget,
		  GdkEventExpose *event)
{
578
  if (gtk_widget_is_drawable (widget))
Elliot Lee's avatar
Elliot Lee committed
579
    {
580
      GtkRuler *ruler = GTK_RULER (widget);
581
      GtkRulerPriv *priv = ruler->priv;
582
      cairo_t *cr;
Elliot Lee's avatar
Elliot Lee committed
583 584

      gtk_ruler_draw_ticks (ruler);
585
      
586 587 588 589 590
      cr = gdk_cairo_create (widget->window);
      gdk_cairo_set_source_pixmap (cr, priv->backing_store, 0, 0);
      gdk_cairo_rectangle (cr, &event->area);
      cairo_fill (cr);
      cairo_destroy (cr);
591
      
Elliot Lee's avatar
Elliot Lee committed
592 593 594 595 596 597 598 599 600
      gtk_ruler_draw_pos (ruler);
    }

  return FALSE;
}

static void
gtk_ruler_make_pixmap (GtkRuler *ruler)
{
601
  GtkRulerPriv *priv = ruler->priv;
Elliot Lee's avatar
Elliot Lee committed
602 603 604 605 606 607
  GtkWidget *widget;
  gint width;
  gint height;

  widget = GTK_WIDGET (ruler);

608
  if (priv->backing_store)
Elliot Lee's avatar
Elliot Lee committed
609
    {
610
      gdk_drawable_get_size (priv->backing_store, &width, &height);
Elliot Lee's avatar
Elliot Lee committed
611 612 613 614
      if ((width == widget->allocation.width) &&
	  (height == widget->allocation.height))
	return;

615
      g_object_unref (priv->backing_store);
Elliot Lee's avatar
Elliot Lee committed
616 617
    }

618 619 620 621
  priv->backing_store = gdk_pixmap_new (widget->window,
                                        widget->allocation.width,
                                        widget->allocation.height,
                                        -1);
Elliot Lee's avatar
Elliot Lee committed
622

623 624
  priv->xsrc = 0;
  priv->ysrc = 0;
Elliot Lee's avatar
Elliot Lee committed
625
}
626

627 628 629 630
static void
gtk_ruler_real_draw_ticks (GtkRuler *ruler)
{
  GtkWidget *widget = GTK_WIDGET (ruler);
631
  GtkRulerPriv *priv = ruler->priv;
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
  cairo_t *cr;
  gint i, j;
  gint width, height;
  gint xthickness;
  gint ythickness;
  gint length, ideal_length;
  gdouble lower, upper;		/* Upper and lower limits, in ruler units */
  gdouble increment;		/* Number of pixels per unit */
  gint scale;			/* Number of units per major unit */
  gdouble subd_incr;
  gdouble start, end, cur;
  gchar unit_str[32];
  gint digit_height;
  gint digit_offset;
  gint text_width;
  gint text_height;
  gint pos;
  PangoLayout *layout;
  PangoRectangle logical_rect, ink_rect;

652
  if (!gtk_widget_is_drawable (widget))
653 654 655 656 657 658 659 660 661 662 663
    return;

  xthickness = widget->style->xthickness;
  ythickness = widget->style->ythickness;

  layout = gtk_widget_create_pango_layout (widget, "012456789");
  pango_layout_get_extents (layout, &ink_rect, &logical_rect);

  digit_height = PANGO_PIXELS (ink_rect.height) + 2;
  digit_offset = ink_rect.y;

664
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
665 666 667 668 669 670 671 672 673 674
    {
      width = widget->allocation.width;
      height = widget->allocation.height - ythickness * 2;
    }
  else
    {
      width = widget->allocation.height;
      height = widget->allocation.width - ythickness * 2;
    }

675
#define DETAILE(private) (priv->orientation == GTK_ORIENTATION_HORIZONTAL ? "hruler" : "vruler");
676

677
  gtk_paint_box (widget->style, priv->backing_store,
678 679
		 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
		 NULL, widget,
680
                 priv->orientation == GTK_ORIENTATION_HORIZONTAL ?
681 682 683 684
                 "hruler" : "vruler",
		 0, 0,
		 widget->allocation.width, widget->allocation.height);

685
  cr = gdk_cairo_create (priv->backing_store);
686 687
  gdk_cairo_set_source_color (cr, &widget->style->fg[widget->state]);

688
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
    {
      cairo_rectangle (cr,
                       xthickness,
                       height + ythickness,
                       widget->allocation.width - 2 * xthickness,
                       1);
    }
  else
    {
      cairo_rectangle (cr,
                       height + xthickness,
                       ythickness,
                       1,
                       widget->allocation.height - 2 * ythickness);
    }

705 706
  upper = priv->upper / priv->metric->pixels_per_unit;
  lower = priv->lower / priv->metric->pixels_per_unit;
707 708 709 710 711 712 713

  if ((upper - lower) == 0)
    goto out;

  increment = (gdouble) width / (upper - lower);

  /* determine the scale H
714
   *  We calculate the text size as for the vruler, so that the result
715 716 717 718 719 720 721 722
   *  for the scale looks consistent with an accompanying vruler
   */
  /* determine the scale V
   *   use the maximum extents of the ruler to determine the largest
   *   possible number to be displayed.  Calculate the height in pixels
   *   of this displayed text. Use this height to find a scale which
   *   leaves sufficient room for drawing the ruler.
   */
723
  scale = ceil (priv->max_size / priv->metric->pixels_per_unit);
724 725
  g_snprintf (unit_str, sizeof (unit_str), "%d", scale);

726
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
727 728 729 730
    {
      text_width = strlen (unit_str) * digit_height + 1;

      for (scale = 0; scale < MAXIMUM_SCALES; scale++)
731
        if (priv->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
732 733 734 735 736 737 738
          break;
    }
  else
    {
      text_height = strlen (unit_str) * digit_height + 1;

      for (scale = 0; scale < MAXIMUM_SCALES; scale++)
739
        if (priv->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
740 741 742 743 744 745 746 747 748 749
          break;
    }

  if (scale == MAXIMUM_SCALES)
    scale = MAXIMUM_SCALES - 1;

  /* drawing starts here */
  length = 0;
  for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
    {
750 751
      subd_incr = (gdouble) priv->metric->ruler_scale[scale] /
	          (gdouble) priv->metric->subdivide[i];
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
      if (subd_incr * fabs(increment) <= MINIMUM_INCR)
	continue;

      /* Calculate the length of the tickmarks. Make sure that
       * this length increases for each set of ticks
       */
      ideal_length = height / (i + 1) - 1;
      if (ideal_length > ++length)
	length = ideal_length;

      if (lower < upper)
	{
	  start = floor (lower / subd_incr) * subd_incr;
	  end   = ceil  (upper / subd_incr) * subd_incr;
	}
      else
	{
	  start = floor (upper / subd_incr) * subd_incr;
	  end   = ceil  (lower / subd_incr) * subd_incr;
	}

      for (cur = start; cur <= end; cur += subd_incr)
	{
	  pos = ROUND ((cur - lower) * increment);

777
          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
            {
              cairo_rectangle (cr,
                               pos, height + ythickness - length,
                               1,   length);
            }
          else
            {
              cairo_rectangle (cr,
                               height + xthickness - length, pos,
                               length,                       1);
            }

	  /* draw label */
	  if (i == 0)
	    {
	      g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);

795
              if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
796 797 798 799 800
                {
                  pango_layout_set_text (layout, unit_str, -1);
                  pango_layout_get_extents (layout, &logical_rect, NULL);

                  gtk_paint_layout (widget->style,
801
                                    priv->backing_store,
802
                                    gtk_widget_get_state (widget),
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
                                    FALSE,
                                    NULL,
                                    widget,
                                    "hruler",
                                    pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
                                    layout);
                }
              else
                {
                  for (j = 0; j < (int) strlen (unit_str); j++)
                    {
                      pango_layout_set_text (layout, unit_str + j, 1);
                      pango_layout_get_extents (layout, NULL, &logical_rect);

                      gtk_paint_layout (widget->style,
818
                                        priv->backing_store,
819
                                        gtk_widget_get_state (widget),
820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
                                        FALSE,
                                        NULL,
                                        widget,
                                        "vruler",
                                        xthickness + 1,
                                        pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset),
                                        layout);
                    }
                }
	    }
	}
    }

  cairo_fill (cr);
out:
  cairo_destroy (cr);

  g_object_unref (layout);
}

static void
gtk_ruler_real_draw_pos (GtkRuler *ruler)
{
  GtkWidget *widget = GTK_WIDGET (ruler);
844
  GtkRulerPriv *priv = ruler->priv;
845 846 847 848 849 850 851
  gint x, y;
  gint width, height;
  gint bs_width, bs_height;
  gint xthickness;
  gint ythickness;
  gdouble increment;

852
  if (gtk_widget_is_drawable (widget))
853 854 855 856 857 858
    {
      xthickness = widget->style->xthickness;
      ythickness = widget->style->ythickness;
      width = widget->allocation.width;
      height = widget->allocation.height;

859
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
        {
          height -= ythickness * 2;

          bs_width = height / 2 + 2;
          bs_width |= 1;  /* make sure it's odd */
          bs_height = bs_width / 2 + 1;
        }
      else
        {
          width -= xthickness * 2;

          bs_height = width / 2 + 2;
          bs_height |= 1;  /* make sure it's odd */
          bs_width = bs_height / 2 + 1;
        }

      if ((bs_width > 0) && (bs_height > 0))
	{
	  cairo_t *cr = gdk_cairo_create (widget->window);

	  /*  If a backing store exists, restore the ruler  */
881 882 883 884 885 886 887 888 889
	  if (priv->backing_store) {
            cairo_t *cr = gdk_cairo_create (widget->window);

            gdk_cairo_set_source_pixmap (cr, priv->backing_store, 0, 0);
            cairo_rectangle (cr, priv->xsrc, priv->ysrc, bs_width, bs_height);
            cairo_fill (cr);

            cairo_destroy (cr);
          }
890

891
          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
892
            {
893
              increment = (gdouble) width / (priv->upper - priv->lower);
894

895
              x = ROUND ((priv->position - priv->lower) * increment) + (xthickness - bs_width) / 2 - 1;
896 897 898 899
              y = (height + bs_height) / 2 + ythickness;
            }
          else
            {
900
              increment = (gdouble) height / (priv->upper - priv->lower);
901 902

              x = (width + bs_width) / 2 + xthickness;
903
              y = ROUND ((priv->position - priv->lower) * increment) + (ythickness - bs_height) / 2 - 1;
904 905 906 907 908 909
            }

	  gdk_cairo_set_source_color (cr, &widget->style->fg[widget->state]);

	  cairo_move_to (cr, x, y);

910
          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
911 912 913 914 915 916 917 918 919 920 921 922 923 924
            {
              cairo_line_to (cr, x + bs_width / 2.0, y + bs_height);
              cairo_line_to (cr, x + bs_width,       y);
            }
          else
            {
              cairo_line_to (cr, x + bs_width, y + bs_height / 2.0);
              cairo_line_to (cr, x,            y + bs_height);
            }

	  cairo_fill (cr);

	  cairo_destroy (cr);

925 926
	  priv->xsrc = x;
	  priv->ysrc = y;
927 928 929
	}
    }
}