gtkvruler.c 8.94 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

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

Elliot Lee's avatar
Elliot Lee committed
27 28 29 30
#include <math.h>
#include <string.h>
#include "gtkvruler.h"

31 32
#include <glib/gprintf.h>

Elliot Lee's avatar
Elliot Lee committed
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

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

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


static void gtk_vruler_class_init    (GtkVRulerClass *klass);
static void gtk_vruler_init          (GtkVRuler      *vruler);
static gint gtk_vruler_motion_notify (GtkWidget      *widget,
				      GdkEventMotion *event);
static void gtk_vruler_draw_ticks    (GtkRuler       *ruler);
static void gtk_vruler_draw_pos      (GtkRuler       *ruler);


Manish Singh's avatar
Manish Singh committed
50
GType
51
gtk_vruler_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
52
{
Manish Singh's avatar
Manish Singh committed
53
  static GType vruler_type = 0;
Elliot Lee's avatar
Elliot Lee committed
54 55 56

  if (!vruler_type)
    {
Manish Singh's avatar
Manish Singh committed
57
      static const GTypeInfo vruler_info =
Elliot Lee's avatar
Elliot Lee committed
58 59
      {
	sizeof (GtkVRulerClass),
Manish Singh's avatar
Manish Singh committed
60 61 62 63 64 65 66 67
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_vruler_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkVRuler),
	0,		/* n_preallocs */
	(GInstanceInitFunc) gtk_vruler_init,
Elliot Lee's avatar
Elliot Lee committed
68 69
      };

Manish Singh's avatar
Manish Singh committed
70 71
      vruler_type = g_type_register_static (GTK_TYPE_RULER, "GtkVRuler",
					    &vruler_info, 0);
Elliot Lee's avatar
Elliot Lee committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    }

  return vruler_type;
}

static void
gtk_vruler_class_init (GtkVRulerClass *klass)
{
  GtkWidgetClass *widget_class;
  GtkRulerClass *ruler_class;

  widget_class = (GtkWidgetClass*) klass;
  ruler_class = (GtkRulerClass*) klass;

  widget_class->motion_notify_event = gtk_vruler_motion_notify;

  ruler_class->draw_ticks = gtk_vruler_draw_ticks;
  ruler_class->draw_pos = gtk_vruler_draw_pos;
}

static void
gtk_vruler_init (GtkVRuler *vruler)
{
  GtkWidget *widget;

  widget = GTK_WIDGET (vruler);
98 99
  widget->requisition.width = widget->style->xthickness * 2 + RULER_WIDTH;
  widget->requisition.height = widget->style->ythickness * 2 + 1;
Elliot Lee's avatar
Elliot Lee committed
100 101 102
}

GtkWidget*
103
gtk_vruler_new (void)
Elliot Lee's avatar
Elliot Lee committed
104
{
Manish Singh's avatar
Manish Singh committed
105
  return g_object_new (GTK_TYPE_VRULER, NULL);
Elliot Lee's avatar
Elliot Lee committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
}


static gint
gtk_vruler_motion_notify (GtkWidget      *widget,
			  GdkEventMotion *event)
{
  GtkRuler *ruler;
  gint y;

  ruler = GTK_RULER (widget);

  if (event->is_hint)
    gdk_window_get_pointer (widget->window, NULL, &y, NULL);
  else
    y = event->y;

  ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * y) / widget->allocation.height;
124
  g_object_notify (G_OBJECT (ruler), "position");
Elliot Lee's avatar
Elliot Lee committed
125 126 127 128 129 130 131 132 133 134 135 136

  /*  Make sure the ruler has been allocated already  */
  if (ruler->backing_store != NULL)
    gtk_ruler_draw_pos (ruler);

  return FALSE;
}

static void
gtk_vruler_draw_ticks (GtkRuler *ruler)
{
  GtkWidget *widget;
137
  GdkGC *gc, *bg_gc;
Elliot Lee's avatar
Elliot Lee committed
138 139 140 141
  gint i, j;
  gint width, height;
  gint xthickness;
  gint ythickness;
142
  gint length, ideal_length;
143 144
  gdouble lower, upper;		/* Upper and lower limits, in ruler units */
  gdouble increment;		/* Number of pixels per unit */
145
  gint scale;			/* Number of units per major unit */
146 147
  gdouble subd_incr;
  gdouble start, end, cur;
148
  gchar unit_str[32];
Elliot Lee's avatar
Elliot Lee committed
149
  gint digit_height;
150
  gint digit_offset;
151
  gint text_height;
Elliot Lee's avatar
Elliot Lee committed
152
  gint pos;
153 154
  PangoLayout *layout;
  PangoRectangle logical_rect, ink_rect;
Elliot Lee's avatar
Elliot Lee committed
155

156 157 158 159 160 161 162
  if (!GTK_WIDGET_DRAWABLE (ruler)) 
    return;

  widget = GTK_WIDGET (ruler);

  gc = widget->style->fg_gc[GTK_STATE_NORMAL];
  bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
163 164 165

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

167
  layout = gtk_widget_create_pango_layout (widget, "012456789");
168 169
  pango_layout_get_extents (layout, &ink_rect, &logical_rect);
  
170
  digit_height = PANGO_PIXELS (ink_rect.height) + 2;
171
  digit_offset = ink_rect.y;
172 173 174 175

  width = widget->allocation.height;
  height = widget->allocation.width - ythickness * 2;

176 177 178 179
  gtk_paint_box (widget->style, ruler->backing_store,
		 GTK_STATE_NORMAL, GTK_SHADOW_OUT, 
		 NULL, widget, "vruler",
		 0, 0, 
180
		  widget->allocation.width, widget->allocation.height);
181 182
  
  gdk_draw_line (ruler->backing_store, gc,
183 184 185 186
		 height + xthickness,
		 ythickness,
		 height + xthickness,
		 widget->allocation.height - ythickness);
187
  
188 189 190 191 192
  upper = ruler->upper / ruler->metric->pixels_per_unit;
  lower = ruler->lower / ruler->metric->pixels_per_unit;

  if ((upper - lower) == 0)
    return;
193
  increment = (gdouble) width / (upper - lower);
194 195 196 197 198 199 200 201

  /* determine the scale
   *   use the maximum extents of the ruler to determine the largest
   *   possible number to be displayed.  Calculate the height in pixels
   *   of this displayed text. Use this height to find a scale which
   *   leaves sufficient room for drawing the ruler.  
   */
  scale = ceil (ruler->max_size / ruler->metric->pixels_per_unit);
202
  g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
203 204 205 206 207 208 209 210 211 212 213 214
  text_height = strlen (unit_str) * digit_height + 1;

  for (scale = 0; scale < MAXIMUM_SCALES; scale++)
    if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
      break;

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

  /* drawing starts here */
  length = 0;
  for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
Elliot Lee's avatar
Elliot Lee committed
215
    {
216 217
      subd_incr = (gdouble) ruler->metric->ruler_scale[scale] / 
	          (gdouble) ruler->metric->subdivide[i];
218 219
      if (subd_incr * fabs(increment) <= MINIMUM_INCR) 
	continue;
Elliot Lee's avatar
Elliot Lee committed
220

221 222
      /* Calculate the length of the tickmarks. Make sure that
       * this length increases for each set of ticks
Elliot Lee's avatar
Elliot Lee committed
223
       */
224 225 226
      ideal_length = height / (i + 1) - 1;
      if (ideal_length > ++length)
	length = ideal_length;
Elliot Lee's avatar
Elliot Lee committed
227

228
      if (lower < upper)
Elliot Lee's avatar
Elliot Lee committed
229
	{
230 231 232 233 234 235 236 237
	  start = floor (lower / subd_incr) * subd_incr;
	  end   = ceil  (upper / subd_incr) * subd_incr;
	}
      else
	{
	  start = floor (upper / subd_incr) * subd_incr;
	  end   = ceil  (lower / subd_incr) * subd_incr;
	}
Elliot Lee's avatar
Elliot Lee committed
238

239 240 241
      for (cur = start; cur <= end; cur += subd_incr)
	{
	  pos = ROUND ((cur - lower) * increment);
Elliot Lee's avatar
Elliot Lee committed
242

243 244 245
	  gdk_draw_line (ruler->backing_store, gc,
			 height + xthickness - length, pos,
			 height + xthickness, pos);
Elliot Lee's avatar
Elliot Lee committed
246

247 248
	  /* draw label */
	  if (i == 0)
Elliot Lee's avatar
Elliot Lee committed
249
	    {
250
	      g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
251
	      
252
	      for (j = 0; j < (int) strlen (unit_str); j++)
Elliot Lee's avatar
Elliot Lee committed
253
		{
254 255
		  pango_layout_set_text (layout, unit_str + j, 1);
		  pango_layout_get_extents (layout, NULL, &logical_rect);
256 257 258 259 260

      
                  gtk_paint_layout (widget->style,
                                    ruler->backing_store,
                                    GTK_WIDGET_STATE (widget),
261
				    FALSE,
262 263 264 265 266 267
                                    NULL,
                                    widget,
                                    "vruler",
                                    xthickness + 1,
                                    pos + digit_height * j + 2 + PANGO_PIXELS (logical_rect.y - digit_offset),
                                    layout);
Elliot Lee's avatar
Elliot Lee committed
268 269 270 271
		}
	    }
	}
    }
272

Manish Singh's avatar
Manish Singh committed
273
  g_object_unref (layout);
Elliot Lee's avatar
Elliot Lee committed
274 275
}

276

Elliot Lee's avatar
Elliot Lee committed
277 278 279 280 281 282 283 284 285 286 287
static void
gtk_vruler_draw_pos (GtkRuler *ruler)
{
  GtkWidget *widget;
  GdkGC *gc;
  int i;
  gint x, y;
  gint width, height;
  gint bs_width, bs_height;
  gint xthickness;
  gint ythickness;
288
  gdouble increment;
Elliot Lee's avatar
Elliot Lee committed
289 290 291 292 293 294

  if (GTK_WIDGET_DRAWABLE (ruler))
    {
      widget = GTK_WIDGET (ruler);

      gc = widget->style->fg_gc[GTK_STATE_NORMAL];
295 296
      xthickness = widget->style->xthickness;
      ythickness = widget->style->ythickness;
Elliot Lee's avatar
Elliot Lee committed
297 298 299 300 301 302 303 304 305 306 307
      width = widget->allocation.width - xthickness * 2;
      height = widget->allocation.height;

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

      if ((bs_width > 0) && (bs_height > 0))
	{
	  /*  If a backing store exists, restore the ruler  */
	  if (ruler->backing_store && ruler->non_gr_exp_gc)
Manish Singh's avatar
Manish Singh committed
308 309 310 311 312 313
	    gdk_draw_drawable (ruler->widget.window,
			       ruler->non_gr_exp_gc,
			       ruler->backing_store,
			       ruler->xsrc, ruler->ysrc,
			       ruler->xsrc, ruler->ysrc,
			       bs_width, bs_height);
Elliot Lee's avatar
Elliot Lee committed
314

315
	  increment = (gdouble) height / (ruler->upper - ruler->lower);
Elliot Lee's avatar
Elliot Lee committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329

	  x = (width + bs_width) / 2 + xthickness;
	  y = ROUND ((ruler->position - ruler->lower) * increment) + (ythickness - bs_height) / 2 - 1;

	  for (i = 0; i < bs_width; i++)
	    gdk_draw_line (widget->window, gc,
			   x + i, y + i,
			   x + i, y + bs_height - 1 - i);

	  ruler->xsrc = x;
	  ruler->ysrc = y;
	}
    }
}