gtkhruler.c 8.77 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/. 
 */

27
#include <config.h>
Elliot Lee's avatar
Elliot Lee committed
28
#include <math.h>
29
#include <glib/gprintf.h>
Elliot Lee's avatar
Elliot Lee committed
30
#include <string.h>
31
#include "gtkalias.h"
Elliot Lee's avatar
Elliot Lee committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
#include "gtkhruler.h"


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

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


static void gtk_hruler_class_init    (GtkHRulerClass *klass);
static void gtk_hruler_init          (GtkHRuler      *hruler);
static gint gtk_hruler_motion_notify (GtkWidget      *widget,
				      GdkEventMotion *event);
static void gtk_hruler_draw_ticks    (GtkRuler       *ruler);
static void gtk_hruler_draw_pos      (GtkRuler       *ruler);


Manish Singh's avatar
Manish Singh committed
51
GType
52
gtk_hruler_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
53
{
Manish Singh's avatar
Manish Singh committed
54
  static GType hruler_type = 0;
Elliot Lee's avatar
Elliot Lee committed
55 56 57

  if (!hruler_type)
    {
Manish Singh's avatar
Manish Singh committed
58
      static const GTypeInfo hruler_info =
Elliot Lee's avatar
Elliot Lee committed
59 60
      {
	sizeof (GtkHRulerClass),
Manish Singh's avatar
Manish Singh committed
61 62 63 64 65 66 67 68
	NULL,		/* base_init */
	NULL,		/* base_finalize */
	(GClassInitFunc) gtk_hruler_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data */
	sizeof (GtkHRuler),
	0,		/* n_preallocs */
	(GInstanceInitFunc) gtk_hruler_init,
Elliot Lee's avatar
Elliot Lee committed
69 70
      };

Manish Singh's avatar
Manish Singh committed
71 72
      hruler_type = g_type_register_static (GTK_TYPE_RULER, "GtkHRuler",
					    &hruler_info, 0);
Elliot Lee's avatar
Elliot Lee committed
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 98
    }

  return hruler_type;
}

static void
gtk_hruler_class_init (GtkHRulerClass *klass)
{
  GtkWidgetClass *widget_class;
  GtkRulerClass *ruler_class;

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

  widget_class->motion_notify_event = gtk_hruler_motion_notify;

  ruler_class->draw_ticks = gtk_hruler_draw_ticks;
  ruler_class->draw_pos = gtk_hruler_draw_pos;
}

static void
gtk_hruler_init (GtkHRuler *hruler)
{
  GtkWidget *widget;

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


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

static gint
gtk_hruler_motion_notify (GtkWidget      *widget,
			  GdkEventMotion *event)
{
  GtkRuler *ruler;
  gint x;

  ruler = GTK_RULER (widget);

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

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

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

  return FALSE;
}

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

157 158 159 160 161 162 163 164
  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];

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

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

  width = widget->allocation.width;
  height = widget->allocation.height - ythickness * 2;
176
   
177 178 179 180 181 182 183 184
  gtk_paint_box (widget->style, ruler->backing_store,
		 GTK_STATE_NORMAL, GTK_SHADOW_OUT, 
		 NULL, widget, "hruler",
		 0, 0, 
		 widget->allocation.width, widget->allocation.height);
  
  
  gdk_draw_line (ruler->backing_store, gc,
185 186 187 188 189 190 191 192 193 194
		 xthickness,
		 height + ythickness,
		 widget->allocation.width - xthickness,
		 height + ythickness);

  upper = ruler->upper / ruler->metric->pixels_per_unit;
  lower = ruler->lower / ruler->metric->pixels_per_unit;

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

  /* determine the scale
   *  We calculate the text size as for the vruler instead of using
   *  text_width = gdk_string_width(font, unit_str), so that the result
   *  for the scale looks consistent with an accompanying vruler
   */
  scale = ceil (ruler->max_size / ruler->metric->pixels_per_unit);
203
  g_snprintf (unit_str, sizeof (unit_str), "%d", scale);
204 205 206 207 208 209 210 211 212 213 214 215
  text_width = strlen (unit_str) * digit_height + 1;

  for (scale = 0; scale < MAXIMUM_SCALES; scale++)
    if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_width)
      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
216
    {
217 218
      subd_incr = (gdouble) ruler->metric->ruler_scale[scale] / 
	          (gdouble) ruler->metric->subdivide[i];
219 220
      if (subd_incr * fabs(increment) <= MINIMUM_INCR) 
	continue;
Elliot Lee's avatar
Elliot Lee committed
221

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

229 230 231 232 233 234 235 236 237 238
      if (lower < upper)
	{
	  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
239

240 241
  
      for (cur = start; cur <= end; cur += subd_incr)
Elliot Lee's avatar
Elliot Lee committed
242
	{
243 244 245 246 247 248 249 250
	  pos = ROUND ((cur - lower) * increment);

	  gdk_draw_line (ruler->backing_store, gc,
			 pos, height + ythickness, 
			 pos, height - length + ythickness);

	  /* draw label */
	  if (i == 0)
Elliot Lee's avatar
Elliot Lee committed
251
	    {
252
	      g_snprintf (unit_str, sizeof (unit_str), "%d", (int) cur);
253 254 255 256
	      
	      pango_layout_set_text (layout, unit_str, -1);
	      pango_layout_get_extents (layout, &logical_rect, NULL);

257 258 259
              gtk_paint_layout (widget->style,
                                ruler->backing_store,
                                GTK_WIDGET_STATE (widget),
260
				FALSE,
261 262 263 264 265
                                NULL,
                                widget,
                                "hruler",
                                pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
                                layout);
Elliot Lee's avatar
Elliot Lee committed
266 267 268
	    }
	}
    }
269

Manish Singh's avatar
Manish Singh committed
270
  g_object_unref (layout);
Elliot Lee's avatar
Elliot Lee committed
271 272 273 274 275 276 277 278 279 280 281 282 283
}

static void
gtk_hruler_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;
284
  gdouble increment;
Elliot Lee's avatar
Elliot Lee committed
285 286 287 288 289 290

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

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

      bs_width = height / 2;
      bs_width |= 1;  /* make sure it's odd */
      bs_height = bs_width / 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
304 305 306 307 308 309
	    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
310

311
	  increment = (gdouble) width / (ruler->upper - ruler->lower);
Elliot Lee's avatar
Elliot Lee committed
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326

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

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


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