~ubuntu-branches/ubuntu/jaunty/gimp/jaunty-security

« back to all changes in this revision

Viewing changes to libgimpwidgets/gimpruler.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2008-10-06 13:30:41 UTC
  • mto: This revision was merged to the branch mainline in revision 35.
  • Revision ID: james.westby@ubuntu.com-20081006133041-3panbkcanaymfsmp
Tags: upstream-2.6.0
ImportĀ upstreamĀ versionĀ 2.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* LIBGIMP - The GIMP Library
 
2
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library; if not, write to the
 
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
17
 * Boston, MA 02111-1307, USA.
 
18
 */
 
19
 
 
20
#include "config.h"
 
21
 
 
22
#include <string.h>
 
23
 
 
24
#include <gtk/gtk.h>
 
25
 
 
26
#include "libgimpbase/gimpbase.h"
 
27
#include "libgimpmath/gimpmath.h"
 
28
 
 
29
#include "gimpwidgetstypes.h"
 
30
 
 
31
#include "gimpruler.h"
 
32
 
 
33
 
 
34
#define RULER_WIDTH  13
 
35
#define MINIMUM_INCR  5
 
36
 
 
37
 
 
38
enum
 
39
{
 
40
  PROP_0,
 
41
  PROP_ORIENTATION,
 
42
  PROP_UNIT,
 
43
  PROP_LOWER,
 
44
  PROP_UPPER,
 
45
  PROP_POSITION,
 
46
  PROP_MAX_SIZE
 
47
};
 
48
 
 
49
 
 
50
/* All distances below are in 1/72nd's of an inch. (According to
 
51
 * Adobe that's a point, but points are really 1/72.27 in.)
 
52
 */
 
53
typedef struct
 
54
{
 
55
  GtkOrientation  orientation;
 
56
  GimpUnit        unit;
 
57
  gdouble         lower;
 
58
  gdouble         upper;
 
59
  gdouble         position;
 
60
  gdouble         max_size;
 
61
 
 
62
  GdkPixmap      *backing_store;
 
63
  GdkGC          *non_gr_exp_gc;
 
64
 
 
65
  gint            xsrc;
 
66
  gint            ysrc;
 
67
} GimpRulerPrivate;
 
68
 
 
69
 
 
70
static const struct
 
71
{
 
72
  gdouble  ruler_scale[16];
 
73
  gint     subdivide[5];
 
74
} ruler_metric =
 
75
{
 
76
  { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000 },
 
77
  { 1, 5, 10, 50, 100 }
 
78
};
 
79
 
 
80
 
 
81
static void          gimp_ruler_set_property  (GObject        *object,
 
82
                                               guint            prop_id,
 
83
                                               const GValue   *value,
 
84
                                               GParamSpec     *pspec);
 
85
static void          gimp_ruler_get_property  (GObject        *object,
 
86
                                               guint           prop_id,
 
87
                                               GValue         *value,
 
88
                                               GParamSpec     *pspec);
 
89
 
 
90
static void          gimp_ruler_realize       (GtkWidget      *widget);
 
91
static void          gimp_ruler_unrealize     (GtkWidget      *widget);
 
92
static void          gimp_ruler_size_allocate (GtkWidget      *widget,
 
93
                                               GtkAllocation  *allocation);
 
94
static gboolean      gimp_ruler_motion_notify (GtkWidget      *widget,
 
95
                                               GdkEventMotion *event);
 
96
static gboolean      gimp_ruler_expose        (GtkWidget      *widget,
 
97
                                               GdkEventExpose *event);
 
98
 
 
99
static void          gimp_ruler_draw_ticks    (GimpRuler      *ruler);
 
100
static void          gimp_ruler_draw_pos      (GimpRuler      *ruler);
 
101
static void          gimp_ruler_make_pixmap   (GimpRuler      *ruler);
 
102
static PangoLayout * gimp_ruler_create_layout (GtkWidget      *widget,
 
103
                                               const gchar    *text);
 
104
 
 
105
 
 
106
G_DEFINE_TYPE (GimpRuler, gimp_ruler, GTK_TYPE_WIDGET)
 
107
 
 
108
#define GIMP_RULER_GET_PRIVATE(ruler) \
 
109
  G_TYPE_INSTANCE_GET_PRIVATE (ruler, GIMP_TYPE_RULER, GimpRulerPrivate)
 
110
 
 
111
 
 
112
static void
 
113
gimp_ruler_class_init (GimpRulerClass *klass)
 
114
{
 
115
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
116
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
117
 
 
118
  object_class->set_property        = gimp_ruler_set_property;
 
119
  object_class->get_property        = gimp_ruler_get_property;
 
120
 
 
121
  widget_class->realize             = gimp_ruler_realize;
 
122
  widget_class->unrealize           = gimp_ruler_unrealize;
 
123
  widget_class->size_allocate       = gimp_ruler_size_allocate;
 
124
  widget_class->motion_notify_event = gimp_ruler_motion_notify;
 
125
  widget_class->expose_event        = gimp_ruler_expose;
 
126
 
 
127
  g_type_class_add_private (object_class, sizeof (GimpRulerPrivate));
 
128
 
 
129
  g_object_class_install_property (object_class,
 
130
                                   PROP_ORIENTATION,
 
131
                                   g_param_spec_enum ("orientation",
 
132
                                                      "Orientation",
 
133
                                                      "The orientation of the ruler",
 
134
                                                      GTK_TYPE_ORIENTATION,
 
135
                                                      GTK_ORIENTATION_HORIZONTAL,
 
136
                                                      GIMP_PARAM_READWRITE));
 
137
 
 
138
  g_object_class_install_property (object_class,
 
139
                                   PROP_LOWER,
 
140
                                   gimp_param_spec_unit ("unit",
 
141
                                                         "Unit",
 
142
                                                         "Unit of ruler",
 
143
                                                         TRUE, TRUE,
 
144
                                                         GIMP_UNIT_PIXEL,
 
145
                                                         GIMP_PARAM_READWRITE));
 
146
 
 
147
  g_object_class_install_property (object_class,
 
148
                                   PROP_LOWER,
 
149
                                   g_param_spec_double ("lower",
 
150
                                                        "Lower",
 
151
                                                        "Lower limit of ruler",
 
152
                                                        -G_MAXDOUBLE,
 
153
                                                        G_MAXDOUBLE,
 
154
                                                        0.0,
 
155
                                                        GIMP_PARAM_READWRITE));
 
156
 
 
157
  g_object_class_install_property (object_class,
 
158
                                   PROP_UPPER,
 
159
                                   g_param_spec_double ("upper",
 
160
                                                        "Upper",
 
161
                                                        "Upper limit of ruler",
 
162
                                                        -G_MAXDOUBLE,
 
163
                                                        G_MAXDOUBLE,
 
164
                                                        0.0,
 
165
                                                        GIMP_PARAM_READWRITE));
 
166
 
 
167
  g_object_class_install_property (object_class,
 
168
                                   PROP_POSITION,
 
169
                                   g_param_spec_double ("position",
 
170
                                                        "Position",
 
171
                                                        "Position of mark on the ruler",
 
172
                                                        -G_MAXDOUBLE,
 
173
                                                        G_MAXDOUBLE,
 
174
                                                        0.0,
 
175
                                                        GIMP_PARAM_READWRITE));
 
176
 
 
177
  g_object_class_install_property (object_class,
 
178
                                   PROP_MAX_SIZE,
 
179
                                   g_param_spec_double ("max-size",
 
180
                                                        "Max Size",
 
181
                                                        "Maximum size of the ruler",
 
182
                                                        -G_MAXDOUBLE,
 
183
                                                        G_MAXDOUBLE,
 
184
                                                        0.0,
 
185
                                                        GIMP_PARAM_READWRITE));
 
186
}
 
187
 
 
188
static void
 
189
gimp_ruler_init (GimpRuler *ruler)
 
190
{
 
191
  GtkWidget        *widget = GTK_WIDGET (ruler);
 
192
  GtkStyle         *style  = gtk_widget_get_style (widget);
 
193
  GimpRulerPrivate *priv   = GIMP_RULER_GET_PRIVATE (ruler);
 
194
 
 
195
  widget->requisition.width  = style->xthickness * 2 + 1;
 
196
  widget->requisition.height = style->ythickness * 2 + RULER_WIDTH;
 
197
 
 
198
  priv->orientation   = GTK_ORIENTATION_HORIZONTAL;
 
199
  priv->unit          = GIMP_PIXELS;
 
200
  priv->lower         = 0;
 
201
  priv->upper         = 0;
 
202
  priv->position      = 0;
 
203
  priv->max_size      = 0;
 
204
  priv->backing_store = NULL;
 
205
  priv->non_gr_exp_gc = NULL;
 
206
}
 
207
 
 
208
static void
 
209
gimp_ruler_set_property (GObject      *object,
 
210
                         guint         prop_id,
 
211
                         const GValue *value,
 
212
                         GParamSpec   *pspec)
 
213
{
 
214
  GimpRuler        *ruler = GIMP_RULER (object);
 
215
  GimpRulerPrivate *priv  = GIMP_RULER_GET_PRIVATE (ruler);
 
216
 
 
217
  switch (prop_id)
 
218
    {
 
219
    case PROP_ORIENTATION:
 
220
      {
 
221
        GtkWidget *widget = GTK_WIDGET (ruler);
 
222
        GtkStyle  *style  = gtk_widget_get_style (widget);
 
223
 
 
224
        priv->orientation = g_value_get_enum (value);
 
225
        if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
226
          {
 
227
            widget->requisition.width  = style->xthickness * 2 + 1;
 
228
            widget->requisition.height = style->ythickness * 2 + RULER_WIDTH;
 
229
          }
 
230
        else
 
231
          {
 
232
            widget->requisition.width  = style->xthickness * 2 + RULER_WIDTH;
 
233
            widget->requisition.height = style->ythickness * 2 + 1;
 
234
          }
 
235
        gtk_widget_queue_resize (widget);
 
236
      }
 
237
      break;
 
238
    case PROP_UNIT:
 
239
      gimp_ruler_set_unit (ruler, g_value_get_int (value));
 
240
      break;
 
241
    case PROP_LOWER:
 
242
      gimp_ruler_set_range (ruler,
 
243
                            g_value_get_double (value),
 
244
                            priv->upper,
 
245
                            priv->max_size);
 
246
      break;
 
247
    case PROP_UPPER:
 
248
      gimp_ruler_set_range (ruler,
 
249
                            priv->lower,
 
250
                            g_value_get_double (value),
 
251
                            priv->max_size);
 
252
      break;
 
253
    case PROP_POSITION:
 
254
      gimp_ruler_set_position (ruler, g_value_get_double (value));
 
255
      break;
 
256
    case PROP_MAX_SIZE:
 
257
      gimp_ruler_set_range (ruler,
 
258
                            priv->lower,
 
259
                            priv->upper,
 
260
                            g_value_get_double (value));
 
261
      break;
 
262
 
 
263
    default:
 
264
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
265
      break;
 
266
    }
 
267
}
 
268
 
 
269
static void
 
270
gimp_ruler_get_property (GObject      *object,
 
271
                         guint         prop_id,
 
272
                         GValue       *value,
 
273
                         GParamSpec   *pspec)
 
274
{
 
275
  GimpRuler        *ruler = GIMP_RULER (object);
 
276
  GimpRulerPrivate *priv  = GIMP_RULER_GET_PRIVATE (ruler);
 
277
 
 
278
  switch (prop_id)
 
279
    {
 
280
    case PROP_ORIENTATION:
 
281
      g_value_set_enum (value, priv->orientation);
 
282
      break;
 
283
    case PROP_UNIT:
 
284
      g_value_set_int (value, priv->unit);
 
285
      break;
 
286
    case PROP_LOWER:
 
287
      g_value_set_double (value, priv->lower);
 
288
      break;
 
289
    case PROP_UPPER:
 
290
      g_value_set_double (value, priv->upper);
 
291
      break;
 
292
    case PROP_POSITION:
 
293
      g_value_set_double (value, priv->position);
 
294
      break;
 
295
    case PROP_MAX_SIZE:
 
296
      g_value_set_double (value, priv->max_size);
 
297
      break;
 
298
 
 
299
    default:
 
300
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
301
      break;
 
302
    }
 
303
}
 
304
 
 
305
/**
 
306
 * gimp_ruler_new:
 
307
 * @orientation: the ruler's orientation.
 
308
 *
 
309
 * Creates a new ruler.
 
310
 *
 
311
 * Return value: a new #GimpRuler widget.
 
312
 *
 
313
 * Since: GIMP 2.8
 
314
 **/
 
315
GtkWidget *
 
316
gimp_ruler_new (GtkOrientation orientation)
 
317
{
 
318
  return g_object_new (GIMP_TYPE_RULER,
 
319
                       "orientation", orientation,
 
320
                       NULL);
 
321
}
 
322
 
 
323
/**
 
324
 * gimp_ruler_set_position:
 
325
 * @ruler: a #GimpRuler
 
326
 * @unit:  the #GimpUnit to set the ruler to
 
327
 *
 
328
 * This sets the unit of the ruler.
 
329
 *
 
330
 * Since: GIMP 2.8
 
331
 */
 
332
void
 
333
gimp_ruler_set_unit (GimpRuler *ruler,
 
334
                     GimpUnit   unit)
 
335
{
 
336
  GimpRulerPrivate *priv;
 
337
 
 
338
  g_return_if_fail (GIMP_IS_RULER (ruler));
 
339
 
 
340
  priv = GIMP_RULER_GET_PRIVATE (ruler);
 
341
 
 
342
  if (priv->unit != unit)
 
343
    {
 
344
      priv->unit = unit;
 
345
      g_object_notify (G_OBJECT (ruler), "unit");
 
346
 
 
347
      gtk_widget_queue_draw (GTK_WIDGET (ruler));
 
348
    }
 
349
}
 
350
 
 
351
/**
 
352
 * gimp_ruler_get_unit:
 
353
 * @ruler: a #GimpRuler
 
354
 *
 
355
 * Return value: the unit currently used in the @ruler widget.
 
356
 *
 
357
 * Since: GIMP 2.8
 
358
 **/
 
359
GimpUnit
 
360
gimp_ruler_get_unit (GimpRuler *ruler)
 
361
{
 
362
  g_return_val_if_fail (GIMP_IS_RULER (ruler), 0);
 
363
 
 
364
  return GIMP_RULER_GET_PRIVATE (ruler)->unit;
 
365
}
 
366
 
 
367
/**
 
368
 * gimp_ruler_set_position:
 
369
 * @ruler: a #GimpRuler
 
370
 * @position: the position to set the ruler to
 
371
 *
 
372
 * This sets the position of the ruler.
 
373
 *
 
374
 * Since: GIMP 2.8
 
375
 */
 
376
void
 
377
gimp_ruler_set_position (GimpRuler *ruler,
 
378
                         gdouble    position)
 
379
{
 
380
  GimpRulerPrivate *priv;
 
381
 
 
382
  g_return_if_fail (GIMP_IS_RULER (ruler));
 
383
 
 
384
  priv = GIMP_RULER_GET_PRIVATE (ruler);
 
385
 
 
386
  if (priv->position != position)
 
387
    {
 
388
      priv->position = position;
 
389
      g_object_notify (G_OBJECT (ruler), "position");
 
390
 
 
391
      gimp_ruler_draw_pos (ruler);
 
392
    }
 
393
}
 
394
 
 
395
/**
 
396
 * gimp_ruler_get_position:
 
397
 * @ruler: a #GimpRuler
 
398
 *
 
399
 * Return value: the current position of the @ruler widget.
 
400
 *
 
401
 * Since: GIMP 2.8
 
402
 **/
 
403
gdouble
 
404
gimp_ruler_get_position (GimpRuler *ruler)
 
405
{
 
406
  g_return_val_if_fail (GIMP_IS_RULER (ruler), 0.0);
 
407
 
 
408
  return GIMP_RULER_GET_PRIVATE (ruler)->position;
 
409
}
 
410
 
 
411
/**
 
412
 * gimp_ruler_set_range:
 
413
 * @ruler: a #GimpRuler
 
414
 * @lower: the lower limit of the ruler
 
415
 * @upper: the upper limit of the ruler
 
416
 * @max_size: the maximum size of the ruler used when calculating the space to
 
417
 * leave for the text
 
418
 *
 
419
 * This sets the range of the ruler.
 
420
 *
 
421
 * Since: GIMP 2.8
 
422
 */
 
423
void
 
424
gimp_ruler_set_range (GimpRuler *ruler,
 
425
                      gdouble    lower,
 
426
                      gdouble    upper,
 
427
                      gdouble    max_size)
 
428
{
 
429
  GimpRulerPrivate *priv;
 
430
 
 
431
  g_return_if_fail (GIMP_IS_RULER (ruler));
 
432
 
 
433
  priv = GIMP_RULER_GET_PRIVATE (ruler);
 
434
 
 
435
  g_object_freeze_notify (G_OBJECT (ruler));
 
436
  if (priv->lower != lower)
 
437
    {
 
438
      priv->lower = lower;
 
439
      g_object_notify (G_OBJECT (ruler), "lower");
 
440
    }
 
441
  if (priv->upper != upper)
 
442
    {
 
443
      priv->upper = upper;
 
444
      g_object_notify (G_OBJECT (ruler), "upper");
 
445
    }
 
446
  if (priv->max_size != max_size)
 
447
    {
 
448
      priv->max_size = max_size;
 
449
      g_object_notify (G_OBJECT (ruler), "max-size");
 
450
    }
 
451
  g_object_thaw_notify (G_OBJECT (ruler));
 
452
 
 
453
  gtk_widget_queue_draw (GTK_WIDGET (ruler));
 
454
}
 
455
 
 
456
/**
 
457
 * gimp_ruler_get_range:
 
458
 * @ruler: a #GimpRuler
 
459
 * @lower: location to store lower limit of the ruler, or %NULL
 
460
 * @upper: location to store upper limit of the ruler, or %NULL
 
461
 * @max_size: location to store the maximum size of the ruler used when
 
462
 *            calculating the space to leave for the text, or %NULL.
 
463
 *
 
464
 * Retrieves values indicating the range and current position of a #GimpRuler.
 
465
 * See gimp_ruler_set_range().
 
466
 *
 
467
 * Since: GIMP 2.8
 
468
 **/
 
469
void
 
470
gimp_ruler_get_range (GimpRuler *ruler,
 
471
                      gdouble   *lower,
 
472
                      gdouble   *upper,
 
473
                      gdouble   *max_size)
 
474
{
 
475
  GimpRulerPrivate *priv;
 
476
 
 
477
  g_return_if_fail (GIMP_IS_RULER (ruler));
 
478
 
 
479
  priv = GIMP_RULER_GET_PRIVATE (ruler);
 
480
 
 
481
  if (lower)
 
482
    *lower = priv->lower;
 
483
  if (upper)
 
484
    *upper = priv->upper;
 
485
  if (max_size)
 
486
    *max_size = priv->max_size;
 
487
}
 
488
 
 
489
static void
 
490
gimp_ruler_realize (GtkWidget *widget)
 
491
{
 
492
  GimpRuler     *ruler = GIMP_RULER (widget);
 
493
  GdkWindowAttr  attributes;
 
494
  gint           attributes_mask;
 
495
 
 
496
  GTK_WIDGET_SET_FLAGS (ruler, GTK_REALIZED);
 
497
 
 
498
  attributes.window_type = GDK_WINDOW_CHILD;
 
499
  attributes.x           = widget->allocation.x;
 
500
  attributes.y           = widget->allocation.y;
 
501
  attributes.width       = widget->allocation.width;
 
502
  attributes.height      = widget->allocation.height;
 
503
  attributes.wclass      = GDK_INPUT_OUTPUT;
 
504
  attributes.visual      = gtk_widget_get_visual (widget);
 
505
  attributes.colormap    = gtk_widget_get_colormap (widget);
 
506
  attributes.event_mask  = (gtk_widget_get_events (widget) |
 
507
                            GDK_EXPOSURE_MASK              |
 
508
                            GDK_POINTER_MOTION_MASK        |
 
509
                            GDK_POINTER_MOTION_HINT_MASK);
 
510
 
 
511
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
 
512
 
 
513
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
 
514
                                   &attributes, attributes_mask);
 
515
  gdk_window_set_user_data (widget->window, ruler);
 
516
 
 
517
  widget->style = gtk_style_attach (widget->style, widget->window);
 
518
  gtk_style_set_background (gtk_widget_get_style (widget), widget->window,
 
519
                            GTK_STATE_ACTIVE);
 
520
 
 
521
  gimp_ruler_make_pixmap (ruler);
 
522
}
 
523
 
 
524
static void
 
525
gimp_ruler_unrealize (GtkWidget *widget)
 
526
{
 
527
  GimpRuler        *ruler = GIMP_RULER (widget);
 
528
  GimpRulerPrivate *priv  = GIMP_RULER_GET_PRIVATE (ruler);
 
529
 
 
530
  if (priv->backing_store)
 
531
    {
 
532
      g_object_unref (priv->backing_store);
 
533
      priv->backing_store = NULL;
 
534
    }
 
535
 
 
536
  if (priv->non_gr_exp_gc)
 
537
    {
 
538
      g_object_unref (priv->non_gr_exp_gc);
 
539
      priv->non_gr_exp_gc = NULL;
 
540
    }
 
541
 
 
542
  GTK_WIDGET_CLASS (gimp_ruler_parent_class)->unrealize (widget);
 
543
}
 
544
 
 
545
static void
 
546
gimp_ruler_size_allocate (GtkWidget     *widget,
 
547
                          GtkAllocation *allocation)
 
548
{
 
549
  GimpRuler *ruler = GIMP_RULER (widget);
 
550
 
 
551
  widget->allocation = *allocation;
 
552
 
 
553
  if (GTK_WIDGET_REALIZED (widget))
 
554
    {
 
555
      gdk_window_move_resize (widget->window,
 
556
                              allocation->x, allocation->y,
 
557
                              allocation->width, allocation->height);
 
558
 
 
559
      gimp_ruler_make_pixmap (ruler);
 
560
    }
 
561
}
 
562
 
 
563
static gboolean
 
564
gimp_ruler_motion_notify (GtkWidget      *widget,
 
565
                          GdkEventMotion *event)
 
566
{
 
567
  GimpRuler        *ruler = GIMP_RULER (widget);
 
568
  GimpRulerPrivate *priv  = GIMP_RULER_GET_PRIVATE (ruler);
 
569
  gdouble           lower;
 
570
  gdouble           upper;
 
571
 
 
572
  gdk_event_request_motions (event);
 
573
 
 
574
  gimp_ruler_get_range (ruler, &lower, &upper, NULL);
 
575
 
 
576
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
577
    {
 
578
      gimp_ruler_set_position (ruler,
 
579
                               lower +
 
580
                               (upper - lower) * event->x /
 
581
                               widget->allocation.width);
 
582
    }
 
583
  else
 
584
    {
 
585
      gimp_ruler_set_position (ruler,
 
586
                               lower +
 
587
                               (upper - lower) * event->y /
 
588
                               widget->allocation.height);
 
589
    }
 
590
 
 
591
  return FALSE;
 
592
}
 
593
 
 
594
static gboolean
 
595
gimp_ruler_expose (GtkWidget      *widget,
 
596
                   GdkEventExpose *event)
 
597
{
 
598
  if (GTK_WIDGET_DRAWABLE (widget))
 
599
    {
 
600
      GimpRuler        *ruler = GIMP_RULER (widget);
 
601
      GimpRulerPrivate *priv  = GIMP_RULER_GET_PRIVATE (ruler);
 
602
 
 
603
      gimp_ruler_draw_ticks (ruler);
 
604
 
 
605
      gdk_draw_drawable (widget->window,
 
606
                         priv->non_gr_exp_gc,
 
607
                         priv->backing_store,
 
608
                         0, 0, 0, 0,
 
609
                         widget->allocation.width,
 
610
                         widget->allocation.height);
 
611
 
 
612
      gimp_ruler_draw_pos (ruler);
 
613
    }
 
614
 
 
615
  return FALSE;
 
616
}
 
617
 
 
618
static void
 
619
gimp_ruler_draw_ticks (GimpRuler *ruler)
 
620
{
 
621
  GtkWidget        *widget = GTK_WIDGET (ruler);
 
622
  GtkStyle         *style  = gtk_widget_get_style (widget);
 
623
  GimpRulerPrivate *priv   = GIMP_RULER_GET_PRIVATE (ruler);
 
624
  cairo_t          *cr;
 
625
  gint              i;
 
626
  gint              width, height;
 
627
  gint              xthickness;
 
628
  gint              ythickness;
 
629
  gint              length, ideal_length;
 
630
  gdouble           lower, upper;  /* Upper and lower limits, in ruler units */
 
631
  gdouble           increment;     /* Number of pixels per unit */
 
632
  gint              scale;         /* Number of units per major unit */
 
633
  gdouble           start, end, cur;
 
634
  gchar             unit_str[32];
 
635
  gint              digit_height;
 
636
  gint              digit_offset;
 
637
  gint              text_size;
 
638
  gint              pos;
 
639
  gdouble           max_size;
 
640
  GimpUnit          unit;
 
641
  PangoLayout      *layout;
 
642
  PangoRectangle    logical_rect, ink_rect;
 
643
 
 
644
  if (! GTK_WIDGET_DRAWABLE (widget))
 
645
    return;
 
646
 
 
647
  xthickness = style->xthickness;
 
648
  ythickness = style->ythickness;
 
649
 
 
650
  layout = gimp_ruler_create_layout (widget, "012456789");
 
651
  pango_layout_get_extents (layout, &ink_rect, &logical_rect);
 
652
 
 
653
  digit_height = PANGO_PIXELS (ink_rect.height) + 2;
 
654
  digit_offset = ink_rect.y;
 
655
 
 
656
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
657
    {
 
658
      width  = widget->allocation.width;
 
659
      height = widget->allocation.height - ythickness * 2;
 
660
    }
 
661
  else
 
662
    {
 
663
      width  = widget->allocation.height;
 
664
      height = widget->allocation.width - ythickness * 2;
 
665
    }
 
666
 
 
667
  gtk_paint_box (style, priv->backing_store,
 
668
                 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
 
669
                 NULL, widget,
 
670
                 priv->orientation == GTK_ORIENTATION_HORIZONTAL ?
 
671
                 "hruler" : "vruler",
 
672
                 0, 0,
 
673
                 widget->allocation.width, widget->allocation.height);
 
674
 
 
675
  cr = gdk_cairo_create (priv->backing_store);
 
676
  gdk_cairo_set_source_color (cr, &style->fg[widget->state]);
 
677
 
 
678
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
679
    {
 
680
      cairo_rectangle (cr,
 
681
                       xthickness,
 
682
                       height + ythickness,
 
683
                       widget->allocation.width - 2 * xthickness,
 
684
                       1);
 
685
    }
 
686
  else
 
687
    {
 
688
      cairo_rectangle (cr,
 
689
                       height + xthickness,
 
690
                       ythickness,
 
691
                       1,
 
692
                       widget->allocation.height - 2 * ythickness);
 
693
    }
 
694
 
 
695
  gimp_ruler_get_range (ruler, &lower, &upper, &max_size);
 
696
 
 
697
  if ((upper - lower) == 0)
 
698
    goto out;
 
699
 
 
700
  increment = (gdouble) width / (upper - lower);
 
701
 
 
702
  /* determine the scale
 
703
   *   use the maximum extents of the ruler to determine the largest
 
704
   *   possible number to be displayed.  Calculate the height in pixels
 
705
   *   of this displayed text. Use this height to find a scale which
 
706
   *   leaves sufficient room for drawing the ruler.
 
707
   *
 
708
   *   We calculate the text size as for the vruler instead of using
 
709
   *   text_size = gdk_string_width(font, unit_str), so that the result
 
710
   *   for the scale looks consistent with an accompanying vruler
 
711
   */
 
712
  scale = ceil (max_size);
 
713
  g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
 
714
  text_size = strlen (unit_str) * digit_height + 1;
 
715
 
 
716
  for (scale = 0; scale < G_N_ELEMENTS (ruler_metric.ruler_scale); scale++)
 
717
    if (ruler_metric.ruler_scale[scale] * fabs (increment) > 2 * text_size)
 
718
      break;
 
719
 
 
720
  if (scale == G_N_ELEMENTS (ruler_metric.ruler_scale))
 
721
    scale = G_N_ELEMENTS (ruler_metric.ruler_scale) - 1;
 
722
 
 
723
  unit = gimp_ruler_get_unit (ruler);
 
724
 
 
725
  /* drawing starts here */
 
726
  length = 0;
 
727
  for (i = G_N_ELEMENTS (ruler_metric.subdivide) - 1; i >= 0; i--)
 
728
    {
 
729
      gdouble subd_incr;
 
730
 
 
731
      /* hack to get proper subdivisions at full pixels */
 
732
      if (unit == GIMP_UNIT_PIXEL && scale == 1 && i == 1)
 
733
        subd_incr = 1.0;
 
734
      else
 
735
        subd_incr = ((gdouble) ruler_metric.ruler_scale[scale] /
 
736
                     (gdouble) ruler_metric.subdivide[i]);
 
737
 
 
738
      if (subd_incr * fabs (increment) <= MINIMUM_INCR)
 
739
        continue;
 
740
 
 
741
      /* don't subdivide pixels */
 
742
      if (unit == GIMP_UNIT_PIXEL && subd_incr < 1.0)
 
743
        continue;
 
744
 
 
745
      /* Calculate the length of the tickmarks. Make sure that
 
746
       * this length increases for each set of ticks
 
747
       */
 
748
      ideal_length = height / (i + 1) - 1;
 
749
      if (ideal_length > ++length)
 
750
        length = ideal_length;
 
751
 
 
752
      if (lower < upper)
 
753
        {
 
754
          start = floor (lower / subd_incr) * subd_incr;
 
755
          end   = ceil  (upper / subd_incr) * subd_incr;
 
756
        }
 
757
      else
 
758
        {
 
759
          start = floor (upper / subd_incr) * subd_incr;
 
760
          end   = ceil  (lower / subd_incr) * subd_incr;
 
761
        }
 
762
 
 
763
      for (cur = start; cur <= end; cur += subd_incr)
 
764
        {
 
765
          pos = ROUND ((cur - lower) * increment);
 
766
 
 
767
          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
768
            {
 
769
              cairo_rectangle (cr,
 
770
                               pos, height + ythickness - length,
 
771
                               1,   length);
 
772
            }
 
773
          else
 
774
            {
 
775
              cairo_rectangle (cr,
 
776
                               height + xthickness - length, pos,
 
777
                               length,                       1);
 
778
            }
 
779
 
 
780
          /* draw label */
 
781
          if (i == 0)
 
782
            {
 
783
              g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
 
784
 
 
785
              if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
786
                {
 
787
                  pango_layout_set_text (layout, unit_str, -1);
 
788
                  pango_layout_get_extents (layout, &logical_rect, NULL);
 
789
 
 
790
                  gtk_paint_layout (style,
 
791
                                    priv->backing_store,
 
792
                                    GTK_WIDGET_STATE (widget),
 
793
                                    FALSE,
 
794
                                    NULL,
 
795
                                    widget,
 
796
                                    "hruler",
 
797
                                    pos + 2,
 
798
                                    ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
 
799
                                    layout);
 
800
                }
 
801
              else
 
802
                {
 
803
                  gint j;
 
804
 
 
805
                  for (j = 0; j < (int) strlen (unit_str); j++)
 
806
                    {
 
807
                      pango_layout_set_text (layout, unit_str + j, 1);
 
808
                      pango_layout_get_extents (layout, NULL, &logical_rect);
 
809
 
 
810
                      gtk_paint_layout (style,
 
811
                                        priv->backing_store,
 
812
                                        GTK_WIDGET_STATE (widget),
 
813
                                        FALSE,
 
814
                                        NULL,
 
815
                                        widget,
 
816
                                        "vruler",
 
817
                                        xthickness + 1,
 
818
                                        pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset),
 
819
                                        layout);
 
820
                    }
 
821
                }
 
822
            }
 
823
        }
 
824
    }
 
825
 
 
826
  cairo_fill (cr);
 
827
out:
 
828
  cairo_destroy (cr);
 
829
 
 
830
  g_object_unref (layout);
 
831
}
 
832
 
 
833
static void
 
834
gimp_ruler_draw_pos (GimpRuler *ruler)
 
835
{
 
836
  GtkWidget        *widget = GTK_WIDGET (ruler);
 
837
  GtkStyle         *style  = gtk_widget_get_style (widget);
 
838
  GimpRulerPrivate *priv   = GIMP_RULER_GET_PRIVATE (ruler);
 
839
  gint              x, y;
 
840
  gint              width, height;
 
841
  gint              bs_width, bs_height;
 
842
  gint              xthickness;
 
843
  gint              ythickness;
 
844
 
 
845
  if (! GTK_WIDGET_DRAWABLE (ruler))
 
846
    return;
 
847
 
 
848
  xthickness = style->xthickness;
 
849
  ythickness = style->ythickness;
 
850
 
 
851
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
852
    {
 
853
      width  = widget->allocation.width;
 
854
      height = widget->allocation.height - ythickness * 2;
 
855
 
 
856
      bs_width = height / 2 + 2;
 
857
      bs_width |= 1;  /* make sure it's odd */
 
858
      bs_height = bs_width / 2 + 1;
 
859
    }
 
860
  else
 
861
    {
 
862
      width  = widget->allocation.width - xthickness * 2;
 
863
      height = widget->allocation.height;
 
864
 
 
865
      bs_height = width / 2 + 2;
 
866
      bs_height |= 1;  /* make sure it's odd */
 
867
      bs_width = bs_height / 2 + 1;
 
868
    }
 
869
 
 
870
  if ((bs_width > 0) && (bs_height > 0))
 
871
    {
 
872
      cairo_t *cr = gdk_cairo_create (widget->window);
 
873
      gdouble  lower;
 
874
      gdouble  upper;
 
875
      gdouble  position;
 
876
      gdouble  increment;
 
877
 
 
878
      /*  If a backing store exists, restore the ruler  */
 
879
      if (priv->backing_store)
 
880
        gdk_draw_drawable (widget->window,
 
881
                           style->black_gc,
 
882
                           priv->backing_store,
 
883
                           priv->xsrc, priv->ysrc,
 
884
                           priv->xsrc, priv->ysrc,
 
885
                           bs_width, bs_height);
 
886
 
 
887
      position = gimp_ruler_get_position (ruler);
 
888
 
 
889
      gimp_ruler_get_range (ruler, &lower, &upper, NULL);
 
890
 
 
891
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
892
        {
 
893
          increment = (gdouble) width / (upper - lower);
 
894
 
 
895
          x = ROUND ((position - lower) * increment) + (xthickness - bs_width) / 2 - 1;
 
896
          y = (height + bs_height) / 2 + ythickness;
 
897
        }
 
898
      else
 
899
        {
 
900
          increment = (gdouble) height / (upper - lower);
 
901
 
 
902
          x = (width + bs_width) / 2 + xthickness;
 
903
          y = ROUND ((position - lower) * increment) + (ythickness - bs_height) / 2 - 1;
 
904
        }
 
905
 
 
906
      gdk_cairo_set_source_color (cr, &style->fg[widget->state]);
 
907
 
 
908
      cairo_move_to (cr, x, y);
 
909
 
 
910
      if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
911
        {
 
912
          cairo_line_to (cr, x + bs_width / 2.0, y + bs_height);
 
913
          cairo_line_to (cr, x + bs_width,       y);
 
914
        }
 
915
      else
 
916
        {
 
917
          cairo_line_to (cr, x + bs_width, y + bs_height / 2.0);
 
918
          cairo_line_to (cr, x,            y + bs_height);
 
919
        }
 
920
 
 
921
      cairo_fill (cr);
 
922
 
 
923
      cairo_destroy (cr);
 
924
 
 
925
      priv->xsrc = x;
 
926
      priv->ysrc = y;
 
927
    }
 
928
}
 
929
 
 
930
static void
 
931
gimp_ruler_make_pixmap (GimpRuler *ruler)
 
932
{
 
933
  GtkWidget        *widget = GTK_WIDGET (ruler);
 
934
  GimpRulerPrivate *priv   = GIMP_RULER_GET_PRIVATE (ruler);
 
935
  gint              width;
 
936
  gint              height;
 
937
 
 
938
  if (priv->backing_store)
 
939
    {
 
940
      gdk_drawable_get_size (priv->backing_store, &width, &height);
 
941
      if ((width == widget->allocation.width) &&
 
942
          (height == widget->allocation.height))
 
943
        return;
 
944
 
 
945
      g_object_unref (priv->backing_store);
 
946
    }
 
947
 
 
948
  priv->backing_store = gdk_pixmap_new (widget->window,
 
949
                                        widget->allocation.width,
 
950
                                        widget->allocation.height,
 
951
                                        -1);
 
952
 
 
953
  if (!priv->non_gr_exp_gc)
 
954
    {
 
955
      priv->non_gr_exp_gc = gdk_gc_new (widget->window);
 
956
      gdk_gc_set_exposures (priv->non_gr_exp_gc, FALSE);
 
957
    }
 
958
}
 
959
 
 
960
static PangoLayout *
 
961
gimp_ruler_create_layout (GtkWidget   *widget,
 
962
                          const gchar *text)
 
963
{
 
964
  PangoLayout    *layout;
 
965
  PangoAttrList  *attrs;
 
966
  PangoAttribute *attr;
 
967
 
 
968
  layout = gtk_widget_create_pango_layout (widget, text);
 
969
 
 
970
  attrs = pango_attr_list_new ();
 
971
 
 
972
  attr = pango_attr_scale_new (PANGO_SCALE_X_SMALL);
 
973
  attr->start_index = 0;
 
974
  attr->end_index   = -1;
 
975
  pango_attr_list_insert (attrs, attr);
 
976
 
 
977
  pango_layout_set_attributes (layout, attrs);
 
978
  pango_attr_list_unref (attrs);
 
979
 
 
980
  return layout;
 
981
}