1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
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/.
32 #include "gtkorientable.h"
34 #include "gtkprivate.h"
39 #define RULER_WIDTH 14
40 #define MINIMUM_INCR 5
41 #define MAXIMUM_SUBDIVIDE 5
42 #define MAXIMUM_SCALES 10
44 #define ROUND(x) ((int) ((x) + 0.5))
56 typedef struct _GtkRulerPrivate GtkRulerPrivate;
58 struct _GtkRulerPrivate
60 GtkOrientation orientation;
63 #define GTK_RULER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RULER, GtkRulerPrivate))
66 static void gtk_ruler_set_property (GObject *object,
70 static void gtk_ruler_get_property (GObject *object,
74 static void gtk_ruler_realize (GtkWidget *widget);
75 static void gtk_ruler_unrealize (GtkWidget *widget);
76 static void gtk_ruler_size_request (GtkWidget *widget,
77 GtkRequisition *requisition);
78 static void gtk_ruler_size_allocate (GtkWidget *widget,
79 GtkAllocation *allocation);
80 static gboolean gtk_ruler_motion_notify (GtkWidget *widget,
81 GdkEventMotion *event);
82 static gboolean gtk_ruler_expose (GtkWidget *widget,
83 GdkEventExpose *event);
84 static void gtk_ruler_make_pixmap (GtkRuler *ruler);
85 static void gtk_ruler_real_draw_ticks (GtkRuler *ruler);
86 static void gtk_ruler_real_draw_pos (GtkRuler *ruler);
89 static const GtkRulerMetric ruler_metrics[] =
91 { "Pixel", "Pi", 1.0, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
92 { "Inches", "In", 72.0, { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }, { 1, 2, 4, 8, 16 }},
93 { "Centimeters", "Cn", 28.35, { 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000 }, { 1, 5, 10, 50, 100 }},
97 G_DEFINE_TYPE_WITH_CODE (GtkRuler, gtk_ruler, GTK_TYPE_WIDGET,
98 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
103 gtk_ruler_class_init (GtkRulerClass *class)
105 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
106 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
108 gobject_class->set_property = gtk_ruler_set_property;
109 gobject_class->get_property = gtk_ruler_get_property;
111 widget_class->realize = gtk_ruler_realize;
112 widget_class->unrealize = gtk_ruler_unrealize;
113 widget_class->size_request = gtk_ruler_size_request;
114 widget_class->size_allocate = gtk_ruler_size_allocate;
115 widget_class->motion_notify_event = gtk_ruler_motion_notify;
116 widget_class->expose_event = gtk_ruler_expose;
118 class->draw_ticks = gtk_ruler_real_draw_ticks;
119 class->draw_pos = gtk_ruler_real_draw_pos;
121 g_object_class_override_property (gobject_class,
125 g_object_class_install_property (gobject_class,
127 g_param_spec_double ("lower",
129 P_("Lower limit of ruler"),
133 GTK_PARAM_READWRITE));
135 g_object_class_install_property (gobject_class,
137 g_param_spec_double ("upper",
139 P_("Upper limit of ruler"),
143 GTK_PARAM_READWRITE));
145 g_object_class_install_property (gobject_class,
147 g_param_spec_double ("position",
149 P_("Position of mark on the ruler"),
153 GTK_PARAM_READWRITE));
155 g_object_class_install_property (gobject_class,
157 g_param_spec_double ("max-size",
159 P_("Maximum size of the ruler"),
163 GTK_PARAM_READWRITE));
167 * The metric used for the ruler.
171 g_object_class_install_property (gobject_class,
173 g_param_spec_enum ("metric",
175 P_("The metric used for the ruler"),
176 GTK_TYPE_METRIC_TYPE,
178 GTK_PARAM_READWRITE));
180 g_type_class_add_private (gobject_class, sizeof (GtkRulerPrivate));
184 gtk_ruler_init (GtkRuler *ruler)
186 GtkWidget *widget = GTK_WIDGET (ruler);
187 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
189 private->orientation = GTK_ORIENTATION_HORIZONTAL;
191 widget->requisition.width = widget->style->xthickness * 2 + 1;
192 widget->requisition.height = widget->style->ythickness * 2 + RULER_WIDTH;
194 ruler->backing_store = NULL;
197 ruler->slider_size = 0;
203 gtk_ruler_set_metric (ruler, GTK_PIXELS);
207 gtk_ruler_set_property (GObject *object,
212 GtkRuler *ruler = GTK_RULER (object);
213 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
217 case PROP_ORIENTATION:
218 private->orientation = g_value_get_enum (value);
219 gtk_widget_queue_resize (GTK_WIDGET (ruler));
222 gtk_ruler_set_range (ruler, g_value_get_double (value), ruler->upper,
223 ruler->position, ruler->max_size);
226 gtk_ruler_set_range (ruler, ruler->lower, g_value_get_double (value),
227 ruler->position, ruler->max_size);
230 gtk_ruler_set_range (ruler, ruler->lower, ruler->upper,
231 g_value_get_double (value), ruler->max_size);
234 gtk_ruler_set_range (ruler, ruler->lower, ruler->upper,
235 ruler->position, g_value_get_double (value));
238 gtk_ruler_set_metric (ruler, g_value_get_enum (value));
241 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
247 gtk_ruler_get_property (GObject *object,
252 GtkRuler *ruler = GTK_RULER (object);
253 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
257 case PROP_ORIENTATION:
258 g_value_set_enum (value, private->orientation);
261 g_value_set_double (value, ruler->lower);
264 g_value_set_double (value, ruler->upper);
267 g_value_set_double (value, ruler->position);
270 g_value_set_double (value, ruler->max_size);
273 g_value_set_enum (value, gtk_ruler_get_metric (ruler));
276 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
284 * @orientation: the ruler's orientation.
286 * Creates a new #GtkRuler with the given orientation.
288 * Return value: a new #GtkRuler.
293 gtk_ruler_new (GtkOrientation orientation)
295 return g_object_new (GTK_TYPE_RULER,
296 "orientation", orientation,
302 gtk_ruler_set_metric (GtkRuler *ruler,
303 GtkMetricType metric)
305 g_return_if_fail (GTK_IS_RULER (ruler));
307 ruler->metric = (GtkRulerMetric *) &ruler_metrics[metric];
309 if (GTK_WIDGET_DRAWABLE (ruler))
310 gtk_widget_queue_draw (GTK_WIDGET (ruler));
312 g_object_notify (G_OBJECT (ruler), "metric");
316 * gtk_ruler_get_metric:
317 * @ruler: a #GtkRuler
319 * Gets the units used for a #GtkRuler. See gtk_ruler_set_metric().
321 * Return value: the units currently used for @ruler
324 gtk_ruler_get_metric (GtkRuler *ruler)
328 g_return_val_if_fail (GTK_IS_RULER (ruler), 0);
330 for (i = 0; i < G_N_ELEMENTS (ruler_metrics); i++)
331 if (ruler->metric == &ruler_metrics[i])
334 g_assert_not_reached ();
340 * gtk_ruler_set_range:
341 * @ruler: the gtkruler
342 * @lower: the lower limit of the ruler
343 * @upper: the upper limit of the ruler
344 * @position: the mark on the ruler
345 * @max_size: the maximum size of the ruler used when calculating the space to
348 * This sets the range of the ruler.
351 gtk_ruler_set_range (GtkRuler *ruler,
357 g_return_if_fail (GTK_IS_RULER (ruler));
359 g_object_freeze_notify (G_OBJECT (ruler));
360 if (ruler->lower != lower)
362 ruler->lower = lower;
363 g_object_notify (G_OBJECT (ruler), "lower");
365 if (ruler->upper != upper)
367 ruler->upper = upper;
368 g_object_notify (G_OBJECT (ruler), "upper");
370 if (ruler->position != position)
372 ruler->position = position;
373 g_object_notify (G_OBJECT (ruler), "position");
375 if (ruler->max_size != max_size)
377 ruler->max_size = max_size;
378 g_object_notify (G_OBJECT (ruler), "max-size");
380 g_object_thaw_notify (G_OBJECT (ruler));
382 if (GTK_WIDGET_DRAWABLE (ruler))
383 gtk_widget_queue_draw (GTK_WIDGET (ruler));
387 * gtk_ruler_get_range:
388 * @ruler: a #GtkRuler
389 * @lower: location to store lower limit of the ruler, or %NULL
390 * @upper: location to store upper limit of the ruler, or %NULL
391 * @position: location to store the current position of the mark on the ruler, or %NULL
392 * @max_size: location to store the maximum size of the ruler used when calculating
393 * the space to leave for the text, or %NULL.
395 * Retrieves values indicating the range and current position of a #GtkRuler.
396 * See gtk_ruler_set_range().
399 gtk_ruler_get_range (GtkRuler *ruler,
405 g_return_if_fail (GTK_IS_RULER (ruler));
408 *lower = ruler->lower;
410 *upper = ruler->upper;
412 *position = ruler->position;
414 *max_size = ruler->max_size;
418 gtk_ruler_draw_ticks (GtkRuler *ruler)
420 g_return_if_fail (GTK_IS_RULER (ruler));
422 if (GTK_RULER_GET_CLASS (ruler)->draw_ticks)
423 GTK_RULER_GET_CLASS (ruler)->draw_ticks (ruler);
427 gtk_ruler_draw_pos (GtkRuler *ruler)
429 g_return_if_fail (GTK_IS_RULER (ruler));
431 if (GTK_RULER_GET_CLASS (ruler)->draw_pos)
432 GTK_RULER_GET_CLASS (ruler)->draw_pos (ruler);
437 gtk_ruler_realize (GtkWidget *widget)
440 GdkWindowAttr attributes;
441 gint attributes_mask;
443 ruler = GTK_RULER (widget);
444 GTK_WIDGET_SET_FLAGS (ruler, GTK_REALIZED);
446 attributes.window_type = GDK_WINDOW_CHILD;
447 attributes.x = widget->allocation.x;
448 attributes.y = widget->allocation.y;
449 attributes.width = widget->allocation.width;
450 attributes.height = widget->allocation.height;
451 attributes.wclass = GDK_INPUT_OUTPUT;
452 attributes.visual = gtk_widget_get_visual (widget);
453 attributes.colormap = gtk_widget_get_colormap (widget);
454 attributes.event_mask = gtk_widget_get_events (widget);
455 attributes.event_mask |= (GDK_EXPOSURE_MASK |
456 GDK_POINTER_MOTION_MASK |
457 GDK_POINTER_MOTION_HINT_MASK);
459 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
461 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
462 gdk_window_set_user_data (widget->window, ruler);
464 widget->style = gtk_style_attach (widget->style, widget->window);
465 gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
467 gtk_ruler_make_pixmap (ruler);
471 gtk_ruler_unrealize (GtkWidget *widget)
473 GtkRuler *ruler = GTK_RULER (widget);
475 if (ruler->backing_store)
477 g_object_unref (ruler->backing_store);
478 ruler->backing_store = NULL;
481 if (ruler->non_gr_exp_gc)
483 g_object_unref (ruler->non_gr_exp_gc);
484 ruler->non_gr_exp_gc = NULL;
487 GTK_WIDGET_CLASS (gtk_ruler_parent_class)->unrealize (widget);
491 gtk_ruler_size_request (GtkWidget *widget,
492 GtkRequisition *requisition)
494 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (widget);
496 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
498 requisition->width = widget->style->xthickness * 2 + 1;
499 requisition->height = widget->style->ythickness * 2 + RULER_WIDTH;
503 requisition->width = widget->style->xthickness * 2 + RULER_WIDTH;
504 requisition->height = widget->style->ythickness * 2 + 1;
509 gtk_ruler_size_allocate (GtkWidget *widget,
510 GtkAllocation *allocation)
512 GtkRuler *ruler = GTK_RULER (widget);
514 widget->allocation = *allocation;
516 if (GTK_WIDGET_REALIZED (widget))
518 gdk_window_move_resize (widget->window,
519 allocation->x, allocation->y,
520 allocation->width, allocation->height);
522 gtk_ruler_make_pixmap (ruler);
527 gtk_ruler_motion_notify (GtkWidget *widget,
528 GdkEventMotion *event)
530 GtkRuler *ruler = GTK_RULER (widget);
531 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (widget);
535 gdk_event_request_motions (event);
539 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
540 ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * x) / widget->allocation.width;
542 ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * y) / widget->allocation.height;
544 g_object_notify (G_OBJECT (ruler), "position");
546 /* Make sure the ruler has been allocated already */
547 if (ruler->backing_store != NULL)
548 gtk_ruler_draw_pos (ruler);
554 gtk_ruler_expose (GtkWidget *widget,
555 GdkEventExpose *event)
557 if (GTK_WIDGET_DRAWABLE (widget))
559 GtkRuler *ruler = GTK_RULER (widget);
561 gtk_ruler_draw_ticks (ruler);
563 gdk_draw_drawable (widget->window,
564 ruler->non_gr_exp_gc,
565 ruler->backing_store,
567 widget->allocation.width,
568 widget->allocation.height);
570 gtk_ruler_draw_pos (ruler);
577 gtk_ruler_make_pixmap (GtkRuler *ruler)
583 widget = GTK_WIDGET (ruler);
585 if (ruler->backing_store)
587 gdk_drawable_get_size (ruler->backing_store, &width, &height);
588 if ((width == widget->allocation.width) &&
589 (height == widget->allocation.height))
592 g_object_unref (ruler->backing_store);
595 ruler->backing_store = gdk_pixmap_new (widget->window,
596 widget->allocation.width,
597 widget->allocation.height,
603 if (!ruler->non_gr_exp_gc)
605 ruler->non_gr_exp_gc = gdk_gc_new (widget->window);
606 gdk_gc_set_exposures (ruler->non_gr_exp_gc, FALSE);
611 gtk_ruler_real_draw_ticks (GtkRuler *ruler)
613 GtkWidget *widget = GTK_WIDGET (ruler);
614 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
620 gint length, ideal_length;
621 gdouble lower, upper; /* Upper and lower limits, in ruler units */
622 gdouble increment; /* Number of pixels per unit */
623 gint scale; /* Number of units per major unit */
625 gdouble start, end, cur;
633 PangoRectangle logical_rect, ink_rect;
635 if (!GTK_WIDGET_DRAWABLE (ruler))
638 xthickness = widget->style->xthickness;
639 ythickness = widget->style->ythickness;
641 layout = gtk_widget_create_pango_layout (widget, "012456789");
642 pango_layout_get_extents (layout, &ink_rect, &logical_rect);
644 digit_height = PANGO_PIXELS (ink_rect.height) + 2;
645 digit_offset = ink_rect.y;
647 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
649 width = widget->allocation.width;
650 height = widget->allocation.height - ythickness * 2;
654 width = widget->allocation.height;
655 height = widget->allocation.width - ythickness * 2;
658 #define DETAILE(private) (private->orientation == GTK_ORIENTATION_HORIZONTAL ? "hruler" : "vruler");
660 gtk_paint_box (widget->style, ruler->backing_store,
661 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
663 private->orientation == GTK_ORIENTATION_HORIZONTAL ?
666 widget->allocation.width, widget->allocation.height);
668 cr = gdk_cairo_create (ruler->backing_store);
669 gdk_cairo_set_source_color (cr, &widget->style->fg[widget->state]);
671 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
676 widget->allocation.width - 2 * xthickness,
685 widget->allocation.height - 2 * ythickness);
688 upper = ruler->upper / ruler->metric->pixels_per_unit;
689 lower = ruler->lower / ruler->metric->pixels_per_unit;
691 if ((upper - lower) == 0)
694 increment = (gdouble) width / (upper - lower);
696 /* determine the scale H
697 * We calculate the text size as for the vruler instead of using
698 * text_width = gdk_string_width(font, unit_str), so that the result
699 * for the scale looks consistent with an accompanying vruler
701 /* determine the scale V
702 * use the maximum extents of the ruler to determine the largest
703 * possible number to be displayed. Calculate the height in pixels
704 * of this displayed text. Use this height to find a scale which
705 * leaves sufficient room for drawing the ruler.
707 scale = ceil (ruler->max_size / ruler->metric->pixels_per_unit);
708 g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
710 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
712 text_width = strlen (unit_str) * digit_height + 1;
714 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
715 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
720 text_height = strlen (unit_str) * digit_height + 1;
722 for (scale = 0; scale < MAXIMUM_SCALES; scale++)
723 if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
727 if (scale == MAXIMUM_SCALES)
728 scale = MAXIMUM_SCALES - 1;
730 /* drawing starts here */
732 for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
734 subd_incr = (gdouble) ruler->metric->ruler_scale[scale] /
735 (gdouble) ruler->metric->subdivide[i];
736 if (subd_incr * fabs(increment) <= MINIMUM_INCR)
739 /* Calculate the length of the tickmarks. Make sure that
740 * this length increases for each set of ticks
742 ideal_length = height / (i + 1) - 1;
743 if (ideal_length > ++length)
744 length = ideal_length;
748 start = floor (lower / subd_incr) * subd_incr;
749 end = ceil (upper / subd_incr) * subd_incr;
753 start = floor (upper / subd_incr) * subd_incr;
754 end = ceil (lower / subd_incr) * subd_incr;
757 for (cur = start; cur <= end; cur += subd_incr)
759 pos = ROUND ((cur - lower) * increment);
761 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
764 pos, height + ythickness - length,
770 height + xthickness - length, pos,
777 g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
779 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
781 pango_layout_set_text (layout, unit_str, -1);
782 pango_layout_get_extents (layout, &logical_rect, NULL);
784 gtk_paint_layout (widget->style,
785 ruler->backing_store,
786 GTK_WIDGET_STATE (widget),
791 pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
796 for (j = 0; j < (int) strlen (unit_str); j++)
798 pango_layout_set_text (layout, unit_str + j, 1);
799 pango_layout_get_extents (layout, NULL, &logical_rect);
801 gtk_paint_layout (widget->style,
802 ruler->backing_store,
803 GTK_WIDGET_STATE (widget),
809 pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset),
821 g_object_unref (layout);
825 gtk_ruler_real_draw_pos (GtkRuler *ruler)
827 GtkWidget *widget = GTK_WIDGET (ruler);
828 GtkRulerPrivate *private = GTK_RULER_GET_PRIVATE (ruler);
831 gint bs_width, bs_height;
836 if (GTK_WIDGET_DRAWABLE (ruler))
838 xthickness = widget->style->xthickness;
839 ythickness = widget->style->ythickness;
840 width = widget->allocation.width;
841 height = widget->allocation.height;
843 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
845 height -= ythickness * 2;
847 bs_width = height / 2 + 2;
848 bs_width |= 1; /* make sure it's odd */
849 bs_height = bs_width / 2 + 1;
853 width -= xthickness * 2;
855 bs_height = width / 2 + 2;
856 bs_height |= 1; /* make sure it's odd */
857 bs_width = bs_height / 2 + 1;
860 if ((bs_width > 0) && (bs_height > 0))
862 cairo_t *cr = gdk_cairo_create (widget->window);
864 /* If a backing store exists, restore the ruler */
865 if (ruler->backing_store)
866 gdk_draw_drawable (widget->window,
867 widget->style->black_gc,
868 ruler->backing_store,
869 ruler->xsrc, ruler->ysrc,
870 ruler->xsrc, ruler->ysrc,
871 bs_width, bs_height);
873 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
875 increment = (gdouble) width / (ruler->upper - ruler->lower);
877 x = ROUND ((ruler->position - ruler->lower) * increment) + (xthickness - bs_width) / 2 - 1;
878 y = (height + bs_height) / 2 + ythickness;
882 increment = (gdouble) height / (ruler->upper - ruler->lower);
884 x = (width + bs_width) / 2 + xthickness;
885 y = ROUND ((ruler->position - ruler->lower) * increment) + (ythickness - bs_height) / 2 - 1;
888 gdk_cairo_set_source_color (cr, &widget->style->fg[widget->state]);
890 cairo_move_to (cr, x, y);
892 if (private->orientation == GTK_ORIENTATION_HORIZONTAL)
894 cairo_line_to (cr, x + bs_width / 2.0, y + bs_height);
895 cairo_line_to (cr, x + bs_width, y);
899 cairo_line_to (cr, x + bs_width, y + bs_height / 2.0);
900 cairo_line_to (cr, x, y + bs_height);
913 #define __GTK_RULER_C__
914 #include "gtkaliasdef.c"