1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 /*
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * 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 #include <math.h>
30 #include <string.h>
31
32 #undef GDK_DISABLE_DEPRECATED /* We need gdk_drawable_get_size() */
33 #undef GTK_DISABLE_DEPRECATED
34
35 #include "gtkorientable.h"
36 #include "gtkruler.h"
37 #include "gtkprivate.h"
38 #include "gtkintl.h"
39 #include "gtkalias.h"
40
41
42 #define RULER_WIDTH 14
43 #define MINIMUM_INCR 5
44 #define MAXIMUM_SUBDIVIDE 5
45 #define MAXIMUM_SCALES 10
46
47 #define ROUND(x) ((int) ((x) + 0.5))
48
49 enum {
50 PROP_0,
51 PROP_ORIENTATION,
52 PROP_LOWER,
53 PROP_UPPER,
54 PROP_POSITION,
55 PROP_MAX_SIZE,
56 PROP_METRIC
57 };
58
59 typedef struct _GtkRulerPrivate GtkRulerPrivate;
60
61 struct _GtkRulerPrivate
62 {
63 GtkOrientation orientation;
64 };
65
66 #define GTK_RULER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RULER, GtkRulerPrivate))
67
68
69 static void gtk_ruler_set_property (GObject *object,
70 guint prop_id,
71 const GValue *value,
72 GParamSpec *pspec);
73 static void gtk_ruler_get_property (GObject *object,
74 guint prop_id,
75 GValue *value,
76 GParamSpec *pspec);
77 static void gtk_ruler_realize (GtkWidget *widget);
78 static void gtk_ruler_unrealize (GtkWidget *widget);
79 static void gtk_ruler_size_request (GtkWidget *widget,
80 GtkRequisition *requisition);
81 static void gtk_ruler_size_allocate (GtkWidget *widget,
82 GtkAllocation *allocation);
83 static gboolean gtk_ruler_motion_notify (GtkWidget *widget,
84 GdkEventMotion *event);
85 static gboolean gtk_ruler_expose (GtkWidget *widget,
86 GdkEventExpose *event);
87 static void gtk_ruler_make_pixmap (GtkRuler *ruler);
88 static void gtk_ruler_real_draw_ticks (GtkRuler *ruler);
89 static void gtk_ruler_real_draw_pos (GtkRuler *ruler);
90
91
92 static const GtkRulerMetric ruler_metrics[] =
93 {
94 { "Pixel", "Pi", 1.0, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
95 { "Inches", "In", 72.0, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
96 { "Centimeters", "Cn", 28.35, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
97 };
98
99
G_DEFINE_TYPE_WITH_CODE(GtkRuler,gtk_ruler,GTK_TYPE_WIDGET,G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,NULL))100 G_DEFINE_TYPE_WITH_CODE (GtkRuler, gtk_ruler, GTK_TYPE_WIDGET,
101 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
102 NULL))
103
104
105 static void
106 gtk_ruler_class_init (GtkRulerClass *class)
107 {
108 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
109 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
110
111 gobject_class->set_property = gtk_ruler_set_property;
112 gobject_class->get_property = gtk_ruler_get_property;
113
114 widget_class->realize = gtk_ruler_realize;
115 widget_class->unrealize = gtk_ruler_unrealize;
116 widget_class->size_request = gtk_ruler_size_request;
117 widget_class->size_allocate = gtk_ruler_size_allocate;
118 widget_class->motion_notify_event = gtk_ruler_motion_notify;
119 widget_class->expose_event = gtk_ruler_expose;
120
121 class->draw_ticks = gtk_ruler_real_draw_ticks;
122 class->draw_pos = gtk_ruler_real_draw_pos;
123
124 g_object_class_override_property (gobject_class,
125 PROP_ORIENTATION,
126 "orientation");
127
128 g_object_class_install_property (gobject_class,
129 PROP_LOWER,
130 g_param_spec_double ("lower",
131 P_("Lower"),
132 P_("Lower limit of ruler"),
133 -G_MAXDOUBLE,
134 G_MAXDOUBLE,
135 0.0,
136 GTK_PARAM_READWRITE));
137
138 g_object_class_install_property (gobject_class,
139 PROP_UPPER,
140 g_param_spec_double ("upper",
141 P_("Upper"),
142 P_("Upper limit of ruler"),
143 -G_MAXDOUBLE,
144 G_MAXDOUBLE,
145 0.0,
146 GTK_PARAM_READWRITE));
147
148 g_object_class_install_property (gobject_class,
149 PROP_POSITION,
150 g_param_spec_double ("position",
151 P_("Position"),
152 P_("Position of mark on the ruler"),
153 -G_MAXDOUBLE,
154 G_MAXDOUBLE,
155 0.0,
156 GTK_PARAM_READWRITE));
157
158 g_object_class_install_property (gobject_class,
159 PROP_MAX_SIZE,
160 g_param_spec_double ("max-size",
161 P_("Max Size"),
162 P_("Maximum size of the ruler"),
163 -G_MAXDOUBLE,
164 G_MAXDOUBLE,
165 0.0,
166 GTK_PARAM_READWRITE));
167 /**
168 * GtkRuler:metric:
169 *
170 * The metric used for the ruler.
171 *
172 * Since: 2.8
173 */
174 g_object_class_install_property (gobject_class,
175 PROP_METRIC,
176 g_param_spec_enum ("metric",
177 P_("Metric"),
178 P_("The metric used for the ruler"),
179 GTK_TYPE_METRIC_TYPE,
180 GTK_PIXELS,
181 GTK_PARAM_READWRITE));
182
183 g_type_class_add_private (gobject_class, sizeof (GtkRulerPrivate));
184 }
185
186 static void
gtk_ruler_init(GtkRuler * ruler)187 gtk_ruler_init (GtkRuler *ruler)
188 {
189 GtkWidget *widget = GTK_WIDGET (ruler);
190 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
191
192 private->orientation = GTK_ORIENTATION_HORIZONTAL;
193
194 widget->requisition.width = widget->style->xthickness * 2 + 1;
195 widget->requisition.height = widget->style->ythickness * 2 + RULER_WIDTH;
196
197 ruler->backing_store = NULL;
198 ruler->xsrc = 0;
199 ruler->ysrc = 0;
200 ruler->slider_size = 0;
201 ruler->lower = 0;
202 ruler->upper = 0;
203 ruler->position = 0;
204 ruler->max_size = 0;
205
206 gtk_ruler_set_metric (ruler, GTK_PIXELS);
207 }
208
209 static void
gtk_ruler_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)210 gtk_ruler_set_property (GObject *object,
211 guint prop_id,
212 const GValue *value,
213 GParamSpec *pspec)
214 {
215 GtkRuler *ruler = GTK_RULER (object);
216 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
217
218 switch (prop_id)
219 {
220 case PROP_ORIENTATION:
221 private->orientation = g_value_get_enum (value);
222 gtk_widget_queue_resize (GTK_WIDGET (ruler));
223 break;
224 case PROP_LOWER:
225 gtk_ruler_set_range (ruler, g_value_get_double (value), ruler->upper,
226 ruler->position, ruler->max_size);
227 break;
228 case PROP_UPPER:
229 gtk_ruler_set_range (ruler, ruler->lower, g_value_get_double (value),
230 ruler->position, ruler->max_size);
231 break;
232 case PROP_POSITION:
233 gtk_ruler_set_range (ruler, ruler->lower, ruler->upper,
234 g_value_get_double (value), ruler->max_size);
235 break;
236 case PROP_MAX_SIZE:
237 gtk_ruler_set_range (ruler, ruler->lower, ruler->upper,
238 ruler->position, g_value_get_double (value));
239 break;
240 case PROP_METRIC:
241 gtk_ruler_set_metric (ruler, g_value_get_enum (value));
242 break;
243 default:
244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
245 break;
246 }
247 }
248
249 static void
gtk_ruler_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)250 gtk_ruler_get_property (GObject *object,
251 guint prop_id,
252 GValue *value,
253 GParamSpec *pspec)
254 {
255 GtkRuler *ruler = GTK_RULER (object);
256 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
257
258 switch (prop_id)
259 {
260 case PROP_ORIENTATION:
261 g_value_set_enum (value, private->orientation);
262 break;
263 case PROP_LOWER:
264 g_value_set_double (value, ruler->lower);
265 break;
266 case PROP_UPPER:
267 g_value_set_double (value, ruler->upper);
268 break;
269 case PROP_POSITION:
270 g_value_set_double (value, ruler->position);
271 break;
272 case PROP_MAX_SIZE:
273 g_value_set_double (value, ruler->max_size);
274 break;
275 case PROP_METRIC:
276 g_value_set_enum (value, gtk_ruler_get_metric (ruler));
277 break;
278 default:
279 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
280 break;
281 }
282 }
283
284 void
gtk_ruler_set_metric(GtkRuler * ruler,GtkMetricType metric)285 gtk_ruler_set_metric (GtkRuler *ruler,
286 GtkMetricType metric)
287 {
288 g_return_if_fail (GTK_IS_RULER (ruler));
289
290 ruler->metric = (GtkRulerMetric *) &ruler_metrics[metric];
291
292 if (gtk_widget_is_drawable (GTK_WIDGET (ruler)))
293 gtk_widget_queue_draw (GTK_WIDGET (ruler));
294
295 g_object_notify (G_OBJECT (ruler), "metric");
296 }
297
298 /**
299 * gtk_ruler_get_metric:
300 * @ruler: a #GtkRuler
301 *
302 * Gets the units used for a #GtkRuler. See gtk_ruler_set_metric().
303 *
304 * Return value: the units currently used for @ruler
305 *
306 * @Deprecated: 2.24: #GtkRuler has been removed from GTK 3 for being
307 * unmaintained and too specialized. There is no replacement.
308 **/
309 GtkMetricType
gtk_ruler_get_metric(GtkRuler * ruler)310 gtk_ruler_get_metric (GtkRuler *ruler)
311 {
312 gint i;
313
314 g_return_val_if_fail (GTK_IS_RULER (ruler), 0);
315
316 for (i = 0; i < G_N_ELEMENTS (ruler_metrics); i++)
317 if (ruler->metric == &ruler_metrics[i])
318 return i;
319
320 g_assert_not_reached ();
321
322 return 0;
323 }
324
325 /**
326 * gtk_ruler_set_range:
327 * @ruler: the gtkruler
328 * @lower: the lower limit of the ruler
329 * @upper: the upper limit of the ruler
330 * @position: the mark on the ruler
331 * @max_size: the maximum size of the ruler used when calculating the space to
332 * leave for the text
333 *
334 * This sets the range of the ruler.
335 *
336 * @Deprecated: 2.24: #GtkRuler has been removed from GTK 3 for being
337 * unmaintained and too specialized. There is no replacement.
338 */
339 void
gtk_ruler_set_range(GtkRuler * ruler,gdouble lower,gdouble upper,gdouble position,gdouble max_size)340 gtk_ruler_set_range (GtkRuler *ruler,
341 gdouble lower,
342 gdouble upper,
343 gdouble position,
344 gdouble max_size)
345 {
346 g_return_if_fail (GTK_IS_RULER (ruler));
347
348 g_object_freeze_notify (G_OBJECT (ruler));
349 if (ruler->lower != lower)
350 {
351 ruler->lower = lower;
352 g_object_notify (G_OBJECT (ruler), "lower");
353 }
354 if (ruler->upper != upper)
355 {
356 ruler->upper = upper;
357 g_object_notify (G_OBJECT (ruler), "upper");
358 }
359 if (ruler->position != position)
360 {
361 ruler->position = position;
362 g_object_notify (G_OBJECT (ruler), "position");
363 }
364 if (ruler->max_size != max_size)
365 {
366 ruler->max_size = max_size;
367 g_object_notify (G_OBJECT (ruler), "max-size");
368 }
369 g_object_thaw_notify (G_OBJECT (ruler));
370
371 if (gtk_widget_is_drawable (GTK_WIDGET (ruler)))
372 gtk_widget_queue_draw (GTK_WIDGET (ruler));
373 }
374
375 /**
376 * gtk_ruler_get_range:
377 * @ruler: a #GtkRuler
378 * @lower: (allow-none): location to store lower limit of the ruler, or %NULL
379 * @upper: (allow-none): location to store upper limit of the ruler, or %NULL
380 * @position: (allow-none): location to store the current position of the mark on the ruler, or %NULL
381 * @max_size: location to store the maximum size of the ruler used when calculating
382 * the space to leave for the text, or %NULL.
383 *
384 * Retrieves values indicating the range and current position of a #GtkRuler.
385 * See gtk_ruler_set_range().
386 *
387 * @Deprecated: 2.24: #GtkRuler has been removed from GTK 3 for being
388 * unmaintained and too specialized. There is no replacement.
389 **/
390 void
gtk_ruler_get_range(GtkRuler * ruler,gdouble * lower,gdouble * upper,gdouble * position,gdouble * max_size)391 gtk_ruler_get_range (GtkRuler *ruler,
392 gdouble *lower,
393 gdouble *upper,
394 gdouble *position,
395 gdouble *max_size)
396 {
397 g_return_if_fail (GTK_IS_RULER (ruler));
398
399 if (lower)
400 *lower = ruler->lower;
401 if (upper)
402 *upper = ruler->upper;
403 if (position)
404 *position = ruler->position;
405 if (max_size)
406 *max_size = ruler->max_size;
407 }
408
409 void
gtk_ruler_draw_ticks(GtkRuler * ruler)410 gtk_ruler_draw_ticks (GtkRuler *ruler)
411 {
412 g_return_if_fail (GTK_IS_RULER (ruler));
413
414 if (GTK_RULER_GET_CLASS (ruler)->draw_ticks)
415 GTK_RULER_GET_CLASS (ruler)->draw_ticks (ruler);
416 }
417
418 void
gtk_ruler_draw_pos(GtkRuler * ruler)419 gtk_ruler_draw_pos (GtkRuler *ruler)
420 {
421 g_return_if_fail (GTK_IS_RULER (ruler));
422
423 if (GTK_RULER_GET_CLASS (ruler)->draw_pos)
424 GTK_RULER_GET_CLASS (ruler)->draw_pos (ruler);
425 }
426
427
428 static void
gtk_ruler_realize(GtkWidget * widget)429 gtk_ruler_realize (GtkWidget *widget)
430 {
431 GtkRuler *ruler;
432 GdkWindowAttr attributes;
433 gint attributes_mask;
434
435 ruler = GTK_RULER (widget);
436
437 gtk_widget_set_realized (widget, TRUE);
438
439 attributes.window_type = GDK_WINDOW_CHILD;
440 attributes.x = widget->allocation.x;
441 attributes.y = widget->allocation.y;
442 attributes.width = widget->allocation.width;
443 attributes.height = widget->allocation.height;
444 attributes.wclass = GDK_INPUT_OUTPUT;
445 attributes.visual = gtk_widget_get_visual (widget);
446 attributes.colormap = gtk_widget_get_colormap (widget);
447 attributes.event_mask = gtk_widget_get_events (widget);
448 attributes.event_mask |= (GDK_EXPOSURE_MASK |
449 GDK_POINTER_MOTION_MASK |
450 GDK_POINTER_MOTION_HINT_MASK);
451
452 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
453
454 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
455 gdk_window_set_user_data (widget->window, ruler);
456
457 widget->style = gtk_style_attach (widget->style, widget->window);
458 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
459
460 gtk_ruler_make_pixmap (ruler);
461 }
462
463 static void
gtk_ruler_unrealize(GtkWidget * widget)464 gtk_ruler_unrealize (GtkWidget *widget)
465 {
466 GtkRuler *ruler = GTK_RULER (widget);
467
468 if (ruler->backing_store)
469 {
470 g_object_unref (ruler->backing_store);
471 ruler->backing_store = NULL;
472 }
473
474 GTK_WIDGET_CLASS (gtk_ruler_parent_class)->unrealize (widget);
475 }
476
477 static void
gtk_ruler_size_request(GtkWidget * widget,GtkRequisition * requisition)478 gtk_ruler_size_request (GtkWidget *widget,
479 GtkRequisition *requisition)
480 {
481 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (widget);
482
483 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
484 {
485 requisition->width = widget->style->xthickness * 2 + 1;
486 requisition->height = widget->style->ythickness * 2 + RULER_WIDTH;
487 }
488 else
489 {
490 requisition->width = widget->style->xthickness * 2 + RULER_WIDTH;
491 requisition->height = widget->style->ythickness * 2 + 1;
492 }
493 }
494
495 static void
gtk_ruler_size_allocate(GtkWidget * widget,GtkAllocation * allocation)496 gtk_ruler_size_allocate (GtkWidget *widget,
497 GtkAllocation *allocation)
498 {
499 GtkRuler *ruler = GTK_RULER (widget);
500
501 widget->allocation = *allocation;
502
503 if (gtk_widget_get_realized (widget))
504 {
505 gdk_window_move_resize (widget->window,
506 allocation->x, allocation->y,
507 allocation->width, allocation->height);
508
509 gtk_ruler_make_pixmap (ruler);
510 }
511 }
512
513 static gboolean
gtk_ruler_motion_notify(GtkWidget * widget,GdkEventMotion * event)514 gtk_ruler_motion_notify (GtkWidget *widget,
515 GdkEventMotion *event)
516 {
517 GtkRuler *ruler = GTK_RULER (widget);
518 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (widget);
519 gint x;
520 gint y;
521
522 gdk_event_request_motions (event);
523 x = event->x;
524 y = event->y;
525
526 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
527 ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * x) / widget->allocation.width;
528 else
529 ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * y) / widget->allocation.height;
530
531 g_object_notify (G_OBJECT (ruler), "position");
532
533 /* Make sure the ruler has been allocated already */
534 if (ruler->backing_store != NULL)
535 gtk_ruler_draw_pos (ruler);
536
537 return FALSE;
538 }
539
540 static gboolean
gtk_ruler_expose(GtkWidget * widget,GdkEventExpose * event)541 gtk_ruler_expose (GtkWidget *widget,
542 GdkEventExpose *event)
543 {
544 if (gtk_widget_is_drawable (widget))
545 {
546 GtkRuler *ruler = GTK_RULER (widget);
547 cairo_t *cr;
548
549 gtk_ruler_draw_ticks (ruler);
550
551 cr = gdk_cairo_create (widget->window);
552 gdk_cairo_set_source_pixmap (cr, ruler->backing_store, 0, 0);
553 gdk_cairo_rectangle (cr, &event->area);
554 cairo_fill (cr);
555 cairo_destroy (cr);
556
557 gtk_ruler_draw_pos (ruler);
558 }
559
560 return FALSE;
561 }
562
563 static void
gtk_ruler_make_pixmap(GtkRuler * ruler)564 gtk_ruler_make_pixmap (GtkRuler *ruler)
565 {
566 GtkWidget *widget;
567 gint width;
568 gint height;
569
570 widget = GTK_WIDGET (ruler);
571
572 if (ruler->backing_store)
573 {
574 gdk_drawable_get_size (ruler->backing_store, &width, &height);
575 if ((width == widget->allocation.width) &&
576 (height == widget->allocation.height))
577 return;
578
579 g_object_unref (ruler->backing_store);
580 }
581
582 ruler->backing_store = gdk_pixmap_new (widget->window,
583 widget->allocation.width,
584 widget->allocation.height,
585 -1);
586
587 ruler->xsrc = 0;
588 ruler->ysrc = 0;
589 }
590
591 static void
gtk_ruler_real_draw_ticks(GtkRuler * ruler)592 gtk_ruler_real_draw_ticks (GtkRuler *ruler)
593 {
594 GtkWidget *widget = GTK_WIDGET (ruler);
595 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
596 cairo_t *cr;
597 gint i, j;
598 gint width, height;
599 gint xthickness;
600 gint ythickness;
601 gint length, ideal_length;
602 gdouble lower, upper; /* Upper and lower limits, in ruler units */
603 gdouble increment; /* Number of pixels per unit */
604 gint scale; /* Number of units per major unit */
605 gdouble subd_incr;
606 gdouble start, end, cur;
607 gchar unit_str[32];
608 gint digit_height;
609 gint digit_offset;
610 gint text_width;
611 gint text_height;
612 gint pos;
613 PangoLayout *layout;
614 PangoRectangle logical_rect, ink_rect;
615
616 if (!gtk_widget_is_drawable (widget))
617 return;
618
619 xthickness = widget->style->xthickness;
620 ythickness = widget->style->ythickness;
621
622 layout = gtk_widget_create_pango_layout (widget, "012456789");
623 pango_layout_get_extents (layout, &ink_rect, &logical_rect);
624
625 digit_height = PANGO_PIXELS (ink_rect.height) + 2;
626 digit_offset = ink_rect.y;
627
628 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
629 {
630 width = widget->allocation.width;
631 height = widget->allocation.height - ythickness * 2;
632 }
633 else
634 {
635 width = widget->allocation.height;
636 height = widget->allocation.width - ythickness * 2;
637 }
638
639 #define DETAILE(private) (private->orientation == GTK_ORIENTATION_HORIZONTAL ? "hruler" : "vruler");
640
641 gtk_paint_box (widget->style, ruler->backing_store,
642 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
643 NULL, widget,
644 private->orientation == GTK_ORIENTATION_HORIZONTAL ?
645 "hruler" : "vruler",
646 0, 0,
647 widget->allocation.width, widget->allocation.height);
648
649 cr = gdk_cairo_create (ruler->backing_store);
650 gdk_cairo_set_source_color (cr, &widget->style->fg[widget->state]);
651
652 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
653 {
654 cairo_rectangle (cr,
655 xthickness,
656 height + ythickness,
657 widget->allocation.width - 2 * xthickness,
658 1);
659 }
660 else
661 {
662 cairo_rectangle (cr,
663 height + xthickness,
664 ythickness,
665 1,
666 widget->allocation.height - 2 * ythickness);
667 }
668
669 upper = ruler->upper / ruler->metric->pixels_per_unit;
670 lower = ruler->lower / ruler->metric->pixels_per_unit;
671
672 if ((upper - lower) == 0)
673 goto out;
674
675 increment = (gdouble) width / (upper - lower);
676
677 /* determine the scale H
678 * We calculate the text size as for the vruler instead of using
679 * text_width = gdk_string_width(font, unit_str), so that the result
680 * for the scale looks consistent with an accompanying vruler
681 */
682 /* determine the scale V
683 * use the maximum extents of the ruler to determine the largest
684 * possible number to be displayed. Calculate the height in pixels
685 * of this displayed text. Use this height to find a scale which
686 * leaves sufficient room for drawing the ruler.
687 */
688 scale = ceil (ruler->max_size / ruler->metric->pixels_per_unit);
689 g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
690
691 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
692 {
693 text_width = strlen (unit_str) * digit_height + 1;
694
695 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
696 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
697 break;
698 }
699 else
700 {
701 text_height = strlen (unit_str) * digit_height + 1;
702
703 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
704 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
705 break;
706 }
707
708 if (scale == MAXIMUM_SCALES)
709 scale = MAXIMUM_SCALES - 1;
710
711 /* drawing starts here */
712 length = 0;
713 for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
714 {
715 subd_incr = (gdouble) ruler->metric->ruler_scale[scale] /
716 (gdouble) ruler->metric->subdivide[i];
717 if (subd_incr * fabs(increment) <= MINIMUM_INCR)
718 continue;
719
720 /* Calculate the length of the tickmarks. Make sure that
721 * this length increases for each set of ticks
722 */
723 ideal_length = height / (i + 1) - 1;
724 if (ideal_length > ++length)
725 length = ideal_length;
726
727 if (lower < upper)
728 {
729 start = floor (lower / subd_incr) * subd_incr;
730 end = ceil (upper / subd_incr) * subd_incr;
731 }
732 else
733 {
734 start = floor (upper / subd_incr) * subd_incr;
735 end = ceil (lower / subd_incr) * subd_incr;
736 }
737
738 for (cur = start; cur <= end; cur += subd_incr)
739 {
740 pos = ROUND ((cur - lower) * increment);
741
742 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
743 {
744 cairo_rectangle (cr,
745 pos, height + ythickness - length,
746 1, length);
747 }
748 else
749 {
750 cairo_rectangle (cr,
751 height + xthickness - length, pos,
752 length, 1);
753 }
754
755 /* draw label */
756 if (i == 0)
757 {
758 g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
759
760 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
761 {
762 pango_layout_set_text (layout, unit_str, -1);
763 pango_layout_get_extents (layout, &logical_rect, NULL);
764
765 gtk_paint_layout (widget->style,
766 ruler->backing_store,
767 gtk_widget_get_state (widget),
768 FALSE,
769 NULL,
770 widget,
771 "hruler",
772 pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
773 layout);
774 }
775 else
776 {
777 for (j = 0; j < (int) strlen (unit_str); j++)
778 {
779 pango_layout_set_text (layout, unit_str + j, 1);
780 pango_layout_get_extents (layout, NULL, &logical_rect);
781
782 gtk_paint_layout (widget->style,
783 ruler->backing_store,
784 gtk_widget_get_state (widget),
785 FALSE,
786 NULL,
787 widget,
788 "vruler",
789 xthickness + 1,
790 pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset),
791 layout);
792 }
793 }
794 }
795 }
796 }
797
798 cairo_fill (cr);
799 out:
800 cairo_destroy (cr);
801
802 g_object_unref (layout);
803 }
804
805 static void
gtk_ruler_real_draw_pos(GtkRuler * ruler)806 gtk_ruler_real_draw_pos (GtkRuler *ruler)
807 {
808 GtkWidget *widget = GTK_WIDGET (ruler);
809 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
810 gint x, y;
811 gint width, height;
812 gint bs_width, bs_height;
813 gint xthickness;
814 gint ythickness;
815 gdouble increment;
816
817 if (gtk_widget_is_drawable (widget))
818 {
819 xthickness = widget->style->xthickness;
820 ythickness = widget->style->ythickness;
821 width = widget->allocation.width;
822 height = widget->allocation.height;
823
824 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
825 {
826 height -= ythickness * 2;
827
828 bs_width = height / 2 + 2;
829 bs_width |= 1; /* make sure it's odd */
830 bs_height = bs_width / 2 + 1;
831 }
832 else
833 {
834 width -= xthickness * 2;
835
836 bs_height = width / 2 + 2;
837 bs_height |= 1; /* make sure it's odd */
838 bs_width = bs_height / 2 + 1;
839 }
840
841 if ((bs_width > 0) && (bs_height > 0))
842 {
843 cairo_t *cr = gdk_cairo_create (widget->window);
844
845 /* If a backing store exists, restore the ruler */
846 if (ruler->backing_store)
847 {
848 cairo_t *cr = gdk_cairo_create (widget->window);
849
850 gdk_cairo_set_source_pixmap (cr, ruler->backing_store, 0, 0);
851 cairo_rectangle (cr, ruler->xsrc, ruler->ysrc, bs_width, bs_height);
852 cairo_fill (cr);
853
854 cairo_destroy (cr);
855 }
856
857 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
858 {
859 increment = (gdouble) width / (ruler->upper - ruler->lower);
860
861 x = ROUND ((ruler->position - ruler->lower) * increment) + (xthickness - bs_width) / 2 - 1;
862 y = (height + bs_height) / 2 + ythickness;
863 }
864 else
865 {
866 increment = (gdouble) height / (ruler->upper - ruler->lower);
867
868 x = (width + bs_width) / 2 + xthickness;
869 y = ROUND ((ruler->position - ruler->lower) * increment) + (ythickness - bs_height) / 2 - 1;
870 }
871
872 gdk_cairo_set_source_color (cr, &widget->style->fg[widget->state]);
873
874 cairo_move_to (cr, x, y);
875
876 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
877 {
878 cairo_line_to (cr, x + bs_width / 2.0, y + bs_height);
879 cairo_line_to (cr, x + bs_width, y);
880 }
881 else
882 {
883 cairo_line_to (cr, x + bs_width, y + bs_height / 2.0);
884 cairo_line_to (cr, x, y + bs_height);
885 }
886
887 cairo_fill (cr);
888
889 cairo_destroy (cr);
890
891 ruler->xsrc = x;
892 ruler->ysrc = y;
893 }
894 }
895 }
896
897 #define __GTK_RULER_C__
898 #include "gtkaliasdef.c"
899