Improve some sieve-related translations
[claws.git] / src / gtk / gtkshruler.c
blob31ddd3ed9d57d7842a0a751fe4f9a982319453cb
1 /* LIBGTK - The GTK Library
2 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
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 3 of the License, or (at your option) any later version.
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.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
19 #include "config.h"
20 #include "claws-features.h"
22 #include <math.h>
23 #include <string.h>
25 #include <gtk/gtk.h>
27 #include "gtkutils.h"
28 #include "gtkshruler.h"
29 #include "gtkunit.h"
31 #define ROUND(x) ((int) ((x) + 0.5))
33 /**
34 * SECTION: gimpparam
35 * @title: gimpparam
36 * @short_description: Definitions of useful #GParamFlags.
38 * Definitions of useful #GParamFlags.
39 **/
42 /**
43 * GTK_PARAM_STATIC_STRINGS:
45 * Since: GTK 2.4
46 **/
47 #define GTK_PARAM_STATIC_STRINGS (G_PARAM_STATIC_NAME | \
48 G_PARAM_STATIC_NICK | \
49 G_PARAM_STATIC_BLURB)
51 /**
52 * GTK_PARAM_READABLE:
54 * Since: GTK 2.4
55 **/
56 #define GTK_PARAM_READABLE (G_PARAM_READABLE | \
57 GTK_PARAM_STATIC_STRINGS)
59 /**
60 * GTK_PARAM_WRITABLE:
62 * Since: GTK 2.4
63 **/
64 #define GTK_PARAM_WRITABLE (G_PARAM_WRITABLE | \
65 GTK_PARAM_STATIC_STRINGS)
67 /**
68 * GTK_PARAM_READWRITE:
70 * Since: GTK 2.4
71 **/
72 #define GTK_PARAM_READWRITE (G_PARAM_READWRITE | \
73 GTK_PARAM_STATIC_STRINGS)
76 /**
77 * SECTION: gimpruler
78 * @title: GtkSHRuler
79 * @short_description: A ruler widget with configurable unit and orientation.
81 * A ruler widget with configurable unit and orientation.
82 **/
85 #define DEFAULT_RULER_FONT_SCALE PANGO_SCALE_SMALL
86 #define MINIMUM_INCR 5
89 enum
91 PROP_0,
92 PROP_ORIENTATION,
93 PROP_UNIT,
94 PROP_LOWER,
95 PROP_UPPER,
96 PROP_POSITION,
97 PROP_MAX_SIZE
101 /* All distances below are in 1/72nd's of an inch. (According to
102 * Adobe that's a point, but points are really 1/72.27 in.)
104 typedef struct
106 GtkOrientation orientation;
107 GtkCMUnit unit;
108 gdouble lower;
109 gdouble upper;
110 gdouble position;
111 gdouble max_size;
113 GdkWindow *input_window;
114 cairo_surface_t *backing_store;
115 PangoLayout *layout;
116 gdouble font_scale;
118 gint xsrc;
119 gint ysrc;
120 } GtkSHRulerPrivate;
122 #define GTK_SHRULER_GET_PRIVATE(ruler) \
123 G_TYPE_INSTANCE_GET_PRIVATE (ruler, GTK_TYPE_SHRULER, GtkSHRulerPrivate)
126 static const struct
128 const gdouble ruler_scale[16];
129 const gint subdivide[3];
130 } ruler_metric =
132 { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 25000, 50000, 100000 },
133 { 1, 5, 10 }
137 static void gtk_shruler_dispose (GObject *object);
138 static void gtk_shruler_set_property (GObject *object,
139 guint prop_id,
140 const GValue *value,
141 GParamSpec *pspec);
142 static void gtk_shruler_get_property (GObject *object,
143 guint prop_id,
144 GValue *value,
145 GParamSpec *pspec);
147 static void gtk_shruler_realize (GtkWidget *widget);
148 static void gtk_shruler_unrealize (GtkWidget *widget);
149 static void gtk_shruler_map (GtkWidget *widget);
150 static void gtk_shruler_unmap (GtkWidget *widget);
151 static void gtk_shruler_size_allocate (GtkWidget *widget,
152 GtkAllocation *allocation);
153 static void gtk_shruler_get_preferred_height (GtkWidget *widget,
154 gint *minimal_height,
155 gint *natural_height);
156 static void gtk_shruler_get_preferred_width (GtkWidget *widget,
157 gint *minimal_width,
158 gint *natural_width);
159 static void gtk_shruler_size_request (GtkWidget *widget,
160 GtkRequisition *requisition);
161 static void gtk_shruler_style_set (GtkWidget *widget,
162 GtkStyle *prev_style);
163 static gboolean gtk_shruler_motion_notify (GtkWidget *widget,
164 GdkEventMotion *event);
165 static gboolean gtk_shruler_expose (GtkWidget *widget,
166 cairo_t *cr);
168 static void gtk_shruler_draw_ticks (GtkSHRuler *ruler);
169 static void gtk_shruler_make_pixmap (GtkSHRuler *ruler);
171 static PangoLayout * gtk_shruler_get_layout (GtkWidget *widget,
172 const gchar *text);
174 G_DEFINE_TYPE_WITH_CODE (GtkSHRuler, gtk_shruler, GTK_TYPE_WIDGET,
175 G_ADD_PRIVATE(GtkSHRuler))
177 #define parent_class gtk_shruler_parent_class
180 static void
181 gtk_shruler_class_init (GtkSHRulerClass *klass)
183 GObjectClass *object_class = G_OBJECT_CLASS (klass);
184 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
186 object_class->dispose = gtk_shruler_dispose;
187 object_class->set_property = gtk_shruler_set_property;
188 object_class->get_property = gtk_shruler_get_property;
190 widget_class->realize = gtk_shruler_realize;
191 widget_class->unrealize = gtk_shruler_unrealize;
192 widget_class->map = gtk_shruler_map;
193 widget_class->unmap = gtk_shruler_unmap;
194 widget_class->size_allocate = gtk_shruler_size_allocate;
195 widget_class->get_preferred_width = gtk_shruler_get_preferred_width;
196 widget_class->get_preferred_height = gtk_shruler_get_preferred_height;
197 widget_class->style_set = gtk_shruler_style_set;
198 widget_class->motion_notify_event = gtk_shruler_motion_notify;
199 widget_class->draw = gtk_shruler_expose;
201 g_object_class_install_property (object_class,
202 PROP_ORIENTATION,
203 g_param_spec_enum ("orientation",
204 "Orientation",
205 "The orientation of the ruler",
206 GTK_TYPE_ORIENTATION,
207 GTK_ORIENTATION_HORIZONTAL,
208 GTK_PARAM_READWRITE));
210 g_object_class_install_property (object_class,
211 PROP_LOWER,
212 gtk_param_spec_unit ("unit",
213 "Unit",
214 "Unit of ruler",
215 TRUE, TRUE,
216 CM_UNIT_PIXEL,
217 GTK_PARAM_READWRITE));
219 g_object_class_install_property (object_class,
220 PROP_LOWER,
221 g_param_spec_double ("lower",
222 "Lower",
223 "Lower limit of ruler",
224 -G_MAXDOUBLE,
225 G_MAXDOUBLE,
226 0.0,
227 GTK_PARAM_READWRITE));
229 g_object_class_install_property (object_class,
230 PROP_UPPER,
231 g_param_spec_double ("upper",
232 "Upper",
233 "Upper limit of ruler",
234 -G_MAXDOUBLE,
235 G_MAXDOUBLE,
236 0.0,
237 GTK_PARAM_READWRITE));
239 g_object_class_install_property (object_class,
240 PROP_POSITION,
241 g_param_spec_double ("position",
242 "Position",
243 "Position of mark on the ruler",
244 -G_MAXDOUBLE,
245 G_MAXDOUBLE,
246 0.0,
247 GTK_PARAM_READWRITE));
249 g_object_class_install_property (object_class,
250 PROP_MAX_SIZE,
251 g_param_spec_double ("max-size",
252 "Max Size",
253 "Maximum size of the ruler",
254 -G_MAXDOUBLE,
255 G_MAXDOUBLE,
256 0.0,
257 GTK_PARAM_READWRITE));
259 gtk_widget_class_install_style_property (widget_class,
260 g_param_spec_double ("font-scale",
261 NULL, NULL,
262 0.0,
263 G_MAXDOUBLE,
264 DEFAULT_RULER_FONT_SCALE,
265 GTK_PARAM_READABLE));
268 static void
269 gtk_shruler_init (GtkSHRuler *ruler)
271 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
273 gtk_widget_set_has_window (GTK_WIDGET (ruler), FALSE);
275 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
276 priv->unit = 0;
277 priv->lower = 0;
278 priv->upper = 0;
279 priv->position = 0;
280 priv->max_size = 0;
281 priv->backing_store = NULL;
282 priv->font_scale = DEFAULT_RULER_FONT_SCALE;
285 static void
286 gtk_shruler_dispose (GObject *object)
288 G_OBJECT_CLASS (parent_class)->dispose (object);
291 static void
292 gtk_shruler_set_property (GObject *object,
293 guint prop_id,
294 const GValue *value,
295 GParamSpec *pspec)
297 GtkSHRuler *ruler = GTK_SHRULER (object);
298 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
300 switch (prop_id)
302 case PROP_ORIENTATION:
303 priv->orientation = g_value_get_enum (value);
304 gtk_widget_queue_resize (GTK_WIDGET (ruler));
305 break;
307 case PROP_UNIT:
308 gtk_shruler_set_unit (ruler, g_value_get_int (value));
309 break;
311 case PROP_LOWER:
312 gtk_shruler_set_range (ruler,
313 g_value_get_double (value),
314 priv->upper,
315 priv->max_size);
316 break;
317 case PROP_UPPER:
318 gtk_shruler_set_range (ruler,
319 priv->lower,
320 g_value_get_double (value),
321 priv->max_size);
322 break;
324 case PROP_POSITION:
325 gtk_shruler_set_position (ruler, g_value_get_double (value));
326 break;
328 case PROP_MAX_SIZE:
329 gtk_shruler_set_range (ruler,
330 priv->lower,
331 priv->upper,
332 g_value_get_double (value));
333 break;
335 default:
336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
337 break;
341 static void
342 gtk_shruler_get_property (GObject *object,
343 guint prop_id,
344 GValue *value,
345 GParamSpec *pspec)
347 GtkSHRuler *ruler = GTK_SHRULER (object);
348 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
350 switch (prop_id)
352 case PROP_ORIENTATION:
353 g_value_set_enum (value, priv->orientation);
354 break;
356 case PROP_UNIT:
357 g_value_set_int (value, priv->unit);
358 break;
360 case PROP_LOWER:
361 g_value_set_double (value, priv->lower);
362 break;
364 case PROP_UPPER:
365 g_value_set_double (value, priv->upper);
366 break;
368 case PROP_POSITION:
369 g_value_set_double (value, priv->position);
370 break;
372 case PROP_MAX_SIZE:
373 g_value_set_double (value, priv->max_size);
374 break;
376 default:
377 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378 break;
383 * gtk_shruler_new:
384 * @orientation: the ruler's orientation.
386 * Creates a new ruler.
388 * Return value: a new #GtkSHRuler widget.
390 * Since: GTK 2.8
392 GtkWidget *
393 gtk_shruler_new (GtkOrientation orientation)
395 return g_object_new (GTK_TYPE_SHRULER,
396 "orientation", orientation,
397 NULL);
400 static void
401 gtk_shruler_update_position (GtkSHRuler *ruler,
402 gdouble x,
403 gdouble y)
405 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
406 GtkAllocation allocation;
407 gdouble lower;
408 gdouble upper;
410 gtk_widget_get_allocation (GTK_WIDGET (ruler), &allocation);
411 gtk_shruler_get_range (ruler, &lower, &upper, NULL);
413 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
415 gtk_shruler_set_position (ruler,
416 lower +
417 (upper - lower) * x / allocation.width);
419 else
421 gtk_shruler_set_position (ruler,
422 lower +
423 (upper - lower) * y / allocation.height);
428 * gtk_shruler_set_unit:
429 * @ruler: a #GtkSHRuler
430 * @unit: the #GtkCMUnit to set the ruler to
432 * This sets the unit of the ruler.
434 * Since: GTK 2.8
436 void
437 gtk_shruler_set_unit (GtkSHRuler *ruler,
438 GtkCMUnit unit)
440 GtkSHRulerPrivate *priv;
442 g_return_if_fail (GTK_IS_SHRULER (ruler));
444 priv = GTK_SHRULER_GET_PRIVATE (ruler);
446 if (priv->unit != unit)
448 priv->unit = unit;
449 g_object_notify (G_OBJECT (ruler), "unit");
451 gtk_widget_queue_draw (GTK_WIDGET (ruler));
456 * gtk_shruler_get_unit:
457 * @ruler: a #GtkSHRuler
459 * Return value: the unit currently used in the @ruler widget.
461 * Since: GTK 2.8
463 GtkCMUnit
464 gtk_shruler_get_unit (GtkSHRuler *ruler)
466 g_return_val_if_fail (GTK_IS_SHRULER (ruler), 0);
468 return GTK_SHRULER_GET_PRIVATE (ruler)->unit;
472 * gtk_shruler_set_position:
473 * @ruler: a #GtkSHRuler
474 * @position: the position to set the ruler to
476 * This sets the position of the ruler.
478 * Since: GTK 2.8
480 void
481 gtk_shruler_set_position (GtkSHRuler *ruler,
482 gdouble position)
484 GtkSHRulerPrivate *priv;
486 g_return_if_fail (GTK_IS_SHRULER (ruler));
488 priv = GTK_SHRULER_GET_PRIVATE (ruler);
490 if (priv->position != position)
492 priv->position = position;
493 g_object_notify (G_OBJECT (ruler), "position");
498 * gtk_shruler_get_position:
499 * @ruler: a #GtkSHRuler
501 * Return value: the current position of the @ruler widget.
503 * Since: GTK 2.8
505 gdouble
506 gtk_shruler_get_position (GtkSHRuler *ruler)
508 g_return_val_if_fail (GTK_IS_SHRULER (ruler), 0.0);
510 return GTK_SHRULER_GET_PRIVATE (ruler)->position;
514 * gtk_shruler_set_range:
515 * @ruler: a #GtkSHRuler
516 * @lower: the lower limit of the ruler
517 * @upper: the upper limit of the ruler
518 * @max_size: the maximum size of the ruler used when calculating the space to
519 * leave for the text
521 * This sets the range of the ruler.
523 * Since: GTK 2.8
525 void
526 gtk_shruler_set_range (GtkSHRuler *ruler,
527 gdouble lower,
528 gdouble upper,
529 gdouble max_size)
531 GtkSHRulerPrivate *priv;
533 g_return_if_fail (GTK_IS_SHRULER (ruler));
535 priv = GTK_SHRULER_GET_PRIVATE (ruler);
537 g_object_freeze_notify (G_OBJECT (ruler));
538 if (priv->lower != lower)
540 priv->lower = lower;
541 g_object_notify (G_OBJECT (ruler), "lower");
543 if (priv->upper != upper)
545 priv->upper = upper;
546 g_object_notify (G_OBJECT (ruler), "upper");
548 if (priv->max_size != max_size)
550 priv->max_size = max_size;
551 g_object_notify (G_OBJECT (ruler), "max-size");
553 g_object_thaw_notify (G_OBJECT (ruler));
555 gtk_widget_queue_draw (GTK_WIDGET (ruler));
559 * gtk_shruler_get_range:
560 * @ruler: a #GtkSHRuler
561 * @lower: location to store lower limit of the ruler, or %NULL
562 * @upper: location to store upper limit of the ruler, or %NULL
563 * @max_size: location to store the maximum size of the ruler used when
564 * calculating the space to leave for the text, or %NULL.
566 * Retrieves values indicating the range and current position of a #GtkSHRuler.
567 * See gtk_shruler_set_range().
569 * Since: GTK 2.8
571 void
572 gtk_shruler_get_range (GtkSHRuler *ruler,
573 gdouble *lower,
574 gdouble *upper,
575 gdouble *max_size)
577 GtkSHRulerPrivate *priv;
579 g_return_if_fail (GTK_IS_SHRULER (ruler));
581 priv = GTK_SHRULER_GET_PRIVATE (ruler);
583 if (lower)
584 *lower = priv->lower;
585 if (upper)
586 *upper = priv->upper;
587 if (max_size)
588 *max_size = priv->max_size;
591 static void
592 gtk_shruler_realize (GtkWidget *widget)
594 GtkSHRuler *ruler = GTK_SHRULER (widget);
595 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
596 GtkAllocation allocation;
597 GdkWindowAttr attributes;
598 gint attributes_mask;
600 GTK_WIDGET_CLASS (gtk_shruler_parent_class)->realize (widget);
602 gtk_widget_get_allocation (widget, &allocation);
604 attributes.window_type = GDK_WINDOW_CHILD;
605 attributes.x = allocation.x;
606 attributes.y = allocation.y;
607 attributes.width = allocation.width;
608 attributes.height = allocation.height;
609 attributes.wclass = GDK_INPUT_ONLY;
610 attributes.event_mask = (gtk_widget_get_events (widget) |
611 GDK_EXPOSURE_MASK |
612 GDK_POINTER_MOTION_MASK |
613 GDK_POINTER_MOTION_HINT_MASK);
615 attributes_mask = GDK_WA_X | GDK_WA_Y;
617 priv->input_window = gdk_window_new (gtk_widget_get_window (widget),
618 &attributes, attributes_mask);
619 gtk_widget_register_window (widget, priv->input_window);
621 gtk_shruler_make_pixmap (ruler);
624 static void
625 gtk_shruler_unrealize (GtkWidget *widget)
627 GtkSHRuler *ruler = GTK_SHRULER (widget);
628 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
630 if (priv->backing_store)
632 cairo_surface_destroy (priv->backing_store);
633 priv->backing_store = NULL;
636 if (priv->layout)
638 g_object_unref (priv->layout);
639 priv->layout = NULL;
642 if (priv->input_window)
644 gtk_widget_unregister_window (widget, priv->input_window);
645 gdk_window_destroy (priv->input_window);
646 priv->input_window = NULL;
649 GTK_WIDGET_CLASS (gtk_shruler_parent_class)->unrealize (widget);
652 static void
653 gtk_shruler_map (GtkWidget *widget)
655 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (widget);
657 GTK_WIDGET_CLASS (parent_class)->map (widget);
659 if (priv->input_window)
660 gdk_window_show (priv->input_window);
663 static void
664 gtk_shruler_unmap (GtkWidget *widget)
666 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (widget);
668 if (priv->input_window)
669 gdk_window_hide (priv->input_window);
671 GTK_WIDGET_CLASS (parent_class)->unmap (widget);
674 static void
675 gtk_shruler_size_allocate (GtkWidget *widget,
676 GtkAllocation *allocation)
678 GtkSHRuler *ruler = GTK_SHRULER (widget);
679 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
681 gtk_widget_set_allocation (widget, allocation);
683 if (gtk_widget_get_realized (widget))
685 gdk_window_move_resize (priv->input_window,
686 allocation->x, allocation->y,
687 allocation->width, allocation->height);
689 gtk_shruler_make_pixmap (ruler);
693 static void
694 gtk_shruler_get_preferred_width (GtkWidget *widget,
695 gint *minimal_width,
696 gint *natural_width)
698 GtkRequisition requisition;
700 gtk_shruler_size_request (widget, &requisition);
702 *minimal_width = *natural_width = requisition.width;
705 static void
706 gtk_shruler_get_preferred_height (GtkWidget *widget,
707 gint *minimal_height,
708 gint *natural_height)
710 GtkRequisition requisition;
712 gtk_shruler_size_request (widget, &requisition);
714 *minimal_height = *natural_height = requisition.height;
717 static void
718 gtk_shruler_size_request (GtkWidget *widget,
719 GtkRequisition *requisition)
721 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (widget);
722 GtkStyle *style = gtk_widget_get_style (widget);
723 PangoLayout *layout;
724 PangoRectangle ink_rect;
725 gint size;
727 layout = gtk_shruler_get_layout (widget, "0123456789");
728 pango_layout_get_pixel_extents (layout, &ink_rect, NULL);
730 size = 2 + ink_rect.height * 1.7;
732 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
734 requisition->width = style->xthickness * 2 + 1;
735 requisition->height = style->ythickness * 2 + size;
737 else
739 requisition->width = style->xthickness * 2 + size;
740 requisition->height = style->ythickness * 2 + 1;
744 static void
745 gtk_shruler_style_set (GtkWidget *widget,
746 GtkStyle *prev_style)
748 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (widget);
750 GTK_WIDGET_CLASS (gtk_shruler_parent_class)->style_set (widget, prev_style);
752 gtk_widget_style_get (widget,
753 "font-scale", &priv->font_scale,
754 NULL);
756 if (priv->layout)
758 g_object_unref (priv->layout);
759 priv->layout = NULL;
763 static gboolean
764 gtk_shruler_motion_notify (GtkWidget *widget,
765 GdkEventMotion *event)
767 GtkSHRuler *ruler = GTK_SHRULER (widget);
769 gdk_event_request_motions (event);
771 gtk_shruler_update_position (ruler, event->x, event->y);
773 return FALSE;
776 static gboolean
777 gtk_shruler_expose (GtkWidget *widget,
778 cairo_t *cr)
780 if (gtk_widget_is_drawable (widget))
782 GtkSHRuler *ruler = GTK_SHRULER (widget);
783 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
784 GtkAllocation allocation;
786 gtk_shruler_draw_ticks (ruler);
788 gtk_widget_get_allocation (widget, &allocation);
790 cairo_set_source_surface (cr, priv->backing_store, 0, 0);
791 cairo_paint (cr);
794 return FALSE;
797 static void
798 gtk_shruler_draw_ticks (GtkSHRuler *ruler)
800 GtkWidget *widget = GTK_WIDGET (ruler);
801 GtkStyle *style = gtk_widget_get_style (widget);
802 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
803 GtkStateType state = gtk_widget_get_state (widget);
804 GtkAllocation allocation;
805 cairo_t *cr;
806 gint i;
807 gint width, height;
808 gint xthickness;
809 gint ythickness;
810 gint length;
811 gdouble lower, upper; /* Upper and lower limits, in ruler units */
812 gdouble increment; /* Number of pixels per unit */
813 gint scale; /* Number of units per major unit */
814 gdouble start, end, cur;
815 gchar unit_str[32];
816 gint digit_height;
817 gint digit_offset;
818 gint text_size;
819 gint pos;
820 gdouble max_size;
821 GtkCMUnit unit;
822 PangoLayout *layout;
823 PangoRectangle logical_rect, ink_rect;
825 if (! gtk_widget_is_drawable (widget))
826 return;
828 gtk_widget_get_allocation (widget, &allocation);
830 xthickness = style->xthickness;
831 ythickness = style->ythickness;
833 layout = gtk_shruler_get_layout (widget, "0123456789");
834 pango_layout_get_extents (layout, &ink_rect, &logical_rect);
836 digit_height = PANGO_PIXELS (ink_rect.height) + 2;
837 digit_offset = ink_rect.y;
839 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
841 width = allocation.width;
842 height = allocation.height - ythickness * 2;
844 else
846 width = allocation.height;
847 height = allocation.width - ythickness * 2;
850 cr = cairo_create (priv->backing_store);
851 gdk_cairo_set_source_color (cr, &style->bg[state]);
853 cairo_paint (cr);
855 gdk_cairo_set_source_color (cr, &style->fg[state]);
857 gtk_shruler_get_range (ruler, &lower, &upper, &max_size);
859 if ((upper - lower) == 0)
860 goto out;
862 increment = (gdouble) width / (upper - lower);
864 /* determine the scale
865 * use the maximum extents of the ruler to determine the largest
866 * possible number to be displayed. Calculate the height in pixels
867 * of this displayed text. Use this height to find a scale which
868 * leaves sufficient room for drawing the ruler.
870 * We calculate the text size as for the vruler instead of
871 * actually measuring the text width, so that the result for the
872 * scale looks consistent with an accompanying vruler.
874 scale = ceil (max_size);
875 g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
876 text_size = strlen (unit_str) * digit_height + 1;
878 for (scale = 0; scale < G_N_ELEMENTS (ruler_metric.ruler_scale); scale++)
879 if (ruler_metric.ruler_scale[scale] * fabs (increment) > 2 * text_size)
880 break;
882 if (scale == G_N_ELEMENTS (ruler_metric.ruler_scale))
883 scale = G_N_ELEMENTS (ruler_metric.ruler_scale) - 1;
885 unit = gtk_shruler_get_unit (ruler);
887 /* drawing starts here */
888 length = 0;
889 for (i = G_N_ELEMENTS (ruler_metric.subdivide) - 1; i >= 0; i--)
891 gdouble subd_incr;
893 /* hack to get proper subdivisions at full pixels */
894 if (unit == CM_UNIT_PIXEL && scale == 1 && i == 1)
895 subd_incr = 1.0;
896 else
897 subd_incr = ((gdouble) ruler_metric.ruler_scale[scale] /
898 (gdouble) ruler_metric.subdivide[i]);
900 if (subd_incr * fabs (increment) <= MINIMUM_INCR)
901 continue;
903 /* don't subdivide pixels */
904 if (unit == CM_UNIT_PIXEL && subd_incr < 1.0)
905 continue;
907 if (lower < upper)
909 start = floor (lower / subd_incr) * subd_incr;
910 end = ceil (upper / subd_incr) * subd_incr;
912 else
914 start = floor (upper / subd_incr) * subd_incr;
915 end = ceil (lower / subd_incr) * subd_incr;
918 for (cur = start; cur <= end; cur += subd_incr)
920 if (((int)cur) % 10 == 0)
921 length = height * 2 / 3;
922 else if (((int)cur) % 5 == 0)
923 length = height / 3;
924 else
925 length = height / 4;
927 pos = ROUND ((cur - lower) * increment);
929 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
931 cairo_rectangle (cr,
932 pos, height + ythickness - length,
933 1, length);
935 else
937 cairo_rectangle (cr,
938 height + xthickness - length, pos,
939 length, 1);
942 /* draw label */
943 if (i == 0 && ((int)cur) % 10 == 0)
945 g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
947 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
949 pango_layout_set_text (layout, unit_str, -1);
950 pango_layout_get_extents (layout, &logical_rect, NULL);
952 cairo_move_to (cr,
953 pos + 2,
954 ythickness + PANGO_PIXELS (logical_rect.y - digit_offset));
955 pango_cairo_show_layout (cr, layout);
957 else
959 gint j;
961 for (j = 0; j < (int) strlen (unit_str); j++)
963 pango_layout_set_text (layout, unit_str + j, 1);
964 pango_layout_get_extents (layout, NULL, &logical_rect);
966 cairo_move_to (cr,
967 xthickness + 1,
968 pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset));
969 pango_cairo_show_layout (cr, layout);
976 cairo_fill (cr);
977 out:
978 cairo_destroy (cr);
981 static void
982 gtk_shruler_make_pixmap (GtkSHRuler *ruler)
984 GtkWidget *widget = GTK_WIDGET (ruler);
985 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (ruler);
986 GtkAllocation allocation;
988 gtk_widget_get_allocation (widget, &allocation);
990 if (priv->backing_store)
991 cairo_surface_destroy (priv->backing_store);
993 priv->backing_store =
994 gdk_window_create_similar_surface (gtk_widget_get_window (widget),
995 CAIRO_CONTENT_COLOR,
996 allocation.width,
997 allocation.height);
1001 static PangoLayout *
1002 gtk_shruler_create_layout (GtkWidget *widget,
1003 const gchar *text)
1005 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (widget);
1006 PangoLayout *layout;
1007 PangoAttrList *attrs;
1008 PangoAttribute *attr;
1010 layout = gtk_widget_create_pango_layout (widget, text);
1012 attrs = pango_attr_list_new ();
1014 attr = pango_attr_scale_new (priv->font_scale);
1015 attr->start_index = 0;
1016 attr->end_index = -1;
1017 pango_attr_list_insert (attrs, attr);
1019 pango_layout_set_attributes (layout, attrs);
1020 pango_attr_list_unref (attrs);
1022 return layout;
1025 static PangoLayout *
1026 gtk_shruler_get_layout (GtkWidget *widget,
1027 const gchar *text)
1029 GtkSHRulerPrivate *priv = GTK_SHRULER_GET_PRIVATE (widget);
1031 if (priv->layout)
1033 pango_layout_set_text (priv->layout, text, -1);
1034 return priv->layout;
1037 priv->layout = gtk_shruler_create_layout (widget, text);
1039 return priv->layout;