[anjuta] foocanvas: Added foocanvas library to be used by anjuta plugins



commit 4ed9d4e2f59fb1515e47fe87cd0ba544829ca036
Author: Naba Kumar <naba gnome org>
Date:   Fri May 7 15:14:38 2010 +0300

    foocanvas: Added foocanvas library to be used by anjuta plugins

 Makefile.am                            |    2 +-
 configure.in                           |   20 +
 libfoocanvas/Makefile.am               |   62 +
 libfoocanvas/foo-canvas-i18n.h         |   68 +
 libfoocanvas/foo-canvas-line.c         | 1289 ++++++++++
 libfoocanvas/foo-canvas-line.h         |  150 ++
 libfoocanvas/foo-canvas-marshal.c      |  131 ++
 libfoocanvas/foo-canvas-marshal.h      |   28 +
 libfoocanvas/foo-canvas-marshal.list   |    2 +
 libfoocanvas/foo-canvas-pixbuf.c       |  825 +++++++
 libfoocanvas/foo-canvas-pixbuf.h       |   62 +
 libfoocanvas/foo-canvas-polygon.c      |  826 +++++++
 libfoocanvas/foo-canvas-polygon.h      |  121 +
 libfoocanvas/foo-canvas-rect-ellipse.c | 1489 ++++++++++++
 libfoocanvas/foo-canvas-rect-ellipse.h |  174 ++
 libfoocanvas/foo-canvas-text.c         | 1587 +++++++++++++
 libfoocanvas/foo-canvas-text.h         |  168 ++
 libfoocanvas/foo-canvas-util.c         |  399 ++++
 libfoocanvas/foo-canvas-util.h         |  105 +
 libfoocanvas/foo-canvas-widget.c       |  589 +++++
 libfoocanvas/foo-canvas-widget.h       |  103 +
 libfoocanvas/foo-canvas.c              | 3999 ++++++++++++++++++++++++++++++++
 libfoocanvas/foo-canvas.h              |  530 +++++
 libfoocanvas/libfoocanvas.h            |   45 +
 libfoocanvas/libfoocanvastypes.c       |   43 +
 25 files changed, 12816 insertions(+), 1 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 0e3b578..836698c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@
 
 SUBDIRS = \
 	pixmaps scripts data \
-	doc libanjuta plugins src manuals mime launcher po
+	doc libfoocanvas libanjuta plugins src manuals mime launcher po
 
 anjutadocdir = $(docdir)
 anjutadoc_DATA = \
diff --git a/configure.in b/configure.in
index 2beb79b..64d486e 100644
--- a/configure.in
+++ b/configure.in
@@ -27,6 +27,7 @@ AC_SUBST(ANJUTA_VERSION)
 GLIB_REQUIRED=2.18.0
 GDK_PIXBUF_REQUIRED=2.0.0
 GTK_REQUIRED=2.17.10
+PANGO_REQUIRED=0.23
 ORBIT_REQUIRED=2.6.0
 GCONF_REQUIRED=2.12.0
 VTE_REQUIRED=0.9.0
@@ -49,6 +50,7 @@ VALA_REQUIRED=0.7.8
 AC_SUBST(GLIB_REQUIRED)
 AC_SUBST(GDK_PIXBUF_REQUIRED)
 AC_SUBST(GTK_REQUIRED)
+AC_SUBST(PANGO_REQUIRED)
 AC_SUBST(ORBIT_REQUIRED)
 AC_SUBST(VTE_REQUIRED)
 AC_SUBST(LIBXML_REQUIRED)
@@ -148,6 +150,8 @@ PKG_CHECK_MODULES([DBUS_GLIB], [dbus-glib-1])
 
 PKG_CHECK_MODULES(GTK, gtk+-2.0 >= $GTK_REQUIRED)
 
+PKG_CHECK_MODULES(PANGO, pango >= $PANGO_REQUIRED)
+
 PKG_CHECK_MODULES(GDK_PIXBUF, gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED)
 
 PKG_CHECK_MODULES(ORBIT, ORBit-2.0 >= $ORBIT_REQUIRED)
@@ -296,6 +300,21 @@ LIBANJUTA_LIBS='$(top_builddir)/libanjuta/libanjuta.la'
 AC_SUBST(LIBANJUTA_CFLAGS)
 AC_SUBST(LIBANJUTA_LIBS)
 
+dnl Setup FooCanvas Library flags
+dnl --------------------------
+LIBFOOCANVAS_CFLAGS='-I$(top_srcdir)'
+LIBFOOCANVAS_LIBS='$(top_builddir)/foocanvas/libanjuta-foocanvas.la'
+AC_SUBST(LIBFOOCANVAS_CFLAGS)
+AC_SUBST(LIBFOOCANVAS_LIBS)
+
+dnl Checks for Xft/XRender for foocanvas
+dnl
+AC_CHECK_LIB(Xrender, XRenderFindFormat, 
+	[AC_SUBST(RENDER_LIBS, "-lXrender -lXext")
+	 AC_DEFINE(HAVE_RENDER, 1, [Define if libXrender is available.])],
+	[AC_SUBST(RENDER_LIBS, "")],
+	[-lXext])
+
 dnl Setup Plugin directories
 dnl ------------------------
 anjuta_plugin_dir='$(libdir)/anjuta'
@@ -765,6 +784,7 @@ dnl manuals/ja/anjuta-manual/Makefile
 AC_OUTPUT([
 Makefile
 po/Makefile.in
+libfoocanvas/Makefile
 libanjuta/Makefile
 libanjuta/libanjuta-1.0.pc
 libanjuta/anjuta-version.h
diff --git a/libfoocanvas/Makefile.am b/libfoocanvas/Makefile.am
new file mode 100644
index 0000000..25f2de1
--- /dev/null
+++ b/libfoocanvas/Makefile.am
@@ -0,0 +1,62 @@
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_builddir) \
+	$(WARN_CFLAGS) \
+	$(GTK_CFLAGS) \
+	$(PANG_CFLAGS) \
+    -DFOOCANVASLIBDIR=\""$(libdir)"\" \
+    -DFOOCANVASDATADIR=\""$(datadir)"\" \
+    -DFOOCANVASPIXMAPDIR=\""$(datadir)/pixmaps"\" \
+    -DFOOCANVASBINDIR=\""$(bindir)"\" \
+    -DFOOCANVASLOCALSTATEDIR=\""$(localstatedir)"\" \
+    -DFOOCANVASLOCALEDIR=\""$(gnomelocaledir)"\" \
+	-DG_LOG_DOMAIN=\"Foocanvas\" \
+	-DVERSION=\"$(VERSION)\"
+
+lib_LTLIBRARIES = libanjuta-foocanvas.la
+
+libanjuta_foocanvas_la_LIBADD = \
+	$(RENDER_LIBS) \
+	$(GTK_LIBS) \
+	$(PANGO_LIBS)
+
+libanjuta_foocanvas_la_SOURCES = \
+	foo-canvas-line.h \
+	foo-canvas-pixbuf.h \
+	foo-canvas-polygon.h \
+	foo-canvas-rect-ellipse.h \
+	foo-canvas-text.h \
+	foo-canvas-util.h \
+	foo-canvas-widget.h \
+	foo-canvas.h \
+	libfoocanvas.h \
+	foo-canvas-marshal.list \
+	foo-canvas-i18n.h \
+	foo-canvas-line.c \
+	foo-canvas-pixbuf.c \
+	foo-canvas-polygon.c \
+	foo-canvas-rect-ellipse.c \
+	foo-canvas-text.c \
+	foo-canvas-util.c \
+	foo-canvas-widget.c \
+	foo-canvas.c \
+	libfoocanvastypes.c
+
+GENMARSHAL_COMMAND = $(GLIB_GENMARSHAL) --prefix=foo_canvas_marshal
+SUFFIXES = .list
+
+.list.h:
+	$(GENMARSHAL_COMMAND) --header $< >$@
+
+.list.c:
+	(echo '/* This file has been automatically generated.  Do not edit. */' && \
+	echo '#include "$*.h"' && \
+	$(GENMARSHAL_COMMAND) --body $< ) >$@
+
+# A hint is needed to build the header first:
+BUILT_SOURCES = foo-canvas-marshal.h
+
+# Another hint, see bugs #172211 and #172212:
+non-intermediate: foo-canvas-marshal.c
+
+CLEANFILES = foo-canvas-marshal.h foo-canvas-marshal.c
diff --git a/libfoocanvas/foo-canvas-i18n.h b/libfoocanvas/foo-canvas-i18n.h
new file mode 100644
index 0000000..1675bc4
--- /dev/null
+++ b/libfoocanvas/foo-canvas-i18n.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/*
+ * Handles all of the internationalization configuration options.
+ * Author: Tom Tromey <tromey creche cygnus com>
+ */
+
+#ifndef __LIBFOO_CANVAS_I18N_H__
+#define __LIBFOO_CANVAS_I18N_H__ 
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !defined(__LIBFOO_CANVAS_I18NP_H__)
+
+#ifdef ENABLE_NLS
+#    include <libintl.h>
+#    ifdef GNOME_EXPLICIT_TRANSLATION_DOMAIN
+#        undef _
+#        define _(String) dgettext (GNOME_EXPLICIT_TRANSLATION_DOMAIN, String)
+#    else 
+#        define _(String) gettext (String)
+#    endif
+#    ifdef gettext_noop
+#        define N_(String) gettext_noop (String)
+#    else
+#        define N_(String) (String)
+#    endif
+#else
+/* Stubs that do something close enough.  */
+#    define textdomain(String) (String)
+#    define gettext(String) (String)
+#    define dgettext(Domain,Message) (Message)
+#    define dcgettext(Domain,Message,Type) (Message)
+#    define bindtextdomain(Domain,Directory) (Domain)
+#    define _(String) (String)
+#    define N_(String) (String)
+#endif
+
+#endif
+
+G_END_DECLS
+
+#endif /* __LIBFOO_CANVAS_I18N_H__ */
diff --git a/libfoocanvas/foo-canvas-line.c b/libfoocanvas/foo-canvas-line.c
new file mode 100644
index 0000000..570ead1
--- /dev/null
+++ b/libfoocanvas/foo-canvas-line.c
@@ -0,0 +1,1289 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/* Line/curve item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "libfoocanvas.h"
+
+#define noVERBOSE
+
+#define DEFAULT_SPLINE_STEPS 12		/* this is what Tk uses */
+#define NUM_ARROW_POINTS     6		/* number of points in an arrowhead */
+#define NUM_STATIC_POINTS    256	/* number of static points to use to avoid allocating arrays */
+
+
+#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) {	\
+	if (x < bx1)				\
+		bx1 = x;			\
+						\
+	if (x > bx2)				\
+		bx2 = x;			\
+						\
+	if (y < by1)				\
+		by1 = y;			\
+						\
+	if (y > by2)				\
+		by2 = y;			\
+}
+
+
+enum {
+	PROP_0,
+	PROP_POINTS,
+	PROP_FILL_COLOR,
+	PROP_FILL_COLOR_GDK,
+	PROP_FILL_COLOR_RGBA,
+	PROP_FILL_STIPPLE,
+	PROP_WIDTH_PIXELS,
+	PROP_WIDTH_UNITS,
+	PROP_CAP_STYLE,
+	PROP_JOIN_STYLE,
+	PROP_LINE_STYLE,
+	PROP_FIRST_ARROWHEAD,
+	PROP_LAST_ARROWHEAD,
+	PROP_SMOOTH,
+	PROP_SPLINE_STEPS,
+	PROP_ARROW_SHAPE_A,
+	PROP_ARROW_SHAPE_B,
+	PROP_ARROW_SHAPE_C
+};
+
+
+static void foo_canvas_line_class_init   (FooCanvasLineClass *klass);
+static void foo_canvas_line_init         (FooCanvasLine      *line);
+static void foo_canvas_line_destroy      (GtkObject            *object);
+static void foo_canvas_line_set_property (GObject              *object,
+					    guint                 param_id,
+					    const GValue         *value,
+					    GParamSpec           *pspec);
+static void foo_canvas_line_get_property (GObject              *object,
+					    guint                 param_id,
+					    GValue               *value,
+					    GParamSpec           *pspec);
+
+static void   foo_canvas_line_update      (FooCanvasItem *item,
+					     double i2w_dx, double i2w_dy,
+					     int flags);
+static void   foo_canvas_line_realize     (FooCanvasItem *item);
+static void   foo_canvas_line_unrealize   (FooCanvasItem *item);
+static void   foo_canvas_line_draw        (FooCanvasItem *item, GdkDrawable *drawable,
+					     GdkEventExpose   *event);
+static double foo_canvas_line_point       (FooCanvasItem *item, double x, double y,
+					     int cx, int cy, FooCanvasItem **actual_item);
+static void   foo_canvas_line_translate   (FooCanvasItem *item, double dx, double dy);
+static void   foo_canvas_line_bounds      (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasLine, foo_canvas_line, FOO_TYPE_CANVAS_ITEM)
+
+static void
+foo_canvas_line_class_init (FooCanvasLineClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	FooCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (FooCanvasItemClass *) klass;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_line_set_property;
+	gobject_class->get_property = foo_canvas_line_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_POINTS,
+                 g_param_spec_boxed ("points", NULL, NULL,
+				     FOO_TYPE_CANVAS_POINTS,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_string ("fill-color", NULL, NULL,
+                                      NULL,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_GDK,
+                 g_param_spec_boxed ("fill-color-gdk", NULL, NULL,
+				     GDK_TYPE_COLOR,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_RGBA,
+                 g_param_spec_uint ("fill-color-rgba", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_STIPPLE,
+                 g_param_spec_object ("fill-stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_PIXELS,
+                 g_param_spec_uint ("width-pixels", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_UNITS,
+                 g_param_spec_double ("width-units", NULL, NULL,
+				      0.0, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CAP_STYLE,
+                 g_param_spec_enum ("cap-style", NULL, NULL,
+                                    GDK_TYPE_CAP_STYLE,
+                                    GDK_CAP_BUTT,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_JOIN_STYLE,
+                 g_param_spec_enum ("join-style", NULL, NULL,
+                                    GDK_TYPE_JOIN_STYLE,
+                                    GDK_JOIN_MITER,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_LINE_STYLE,
+                 g_param_spec_enum ("line-style", NULL, NULL,
+                                    GDK_TYPE_LINE_STYLE,
+                                    GDK_LINE_SOLID,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FIRST_ARROWHEAD,
+                 g_param_spec_boolean ("first-arrowhead", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_LAST_ARROWHEAD,
+                 g_param_spec_boolean ("last-arrowhead", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_SMOOTH,
+                 g_param_spec_boolean ("smooth", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_SPLINE_STEPS,
+                 g_param_spec_uint ("spline-steps", NULL, NULL,
+				    0, G_MAXUINT, DEFAULT_SPLINE_STEPS,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ARROW_SHAPE_A,
+                 g_param_spec_double ("arrow-shape-a", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ARROW_SHAPE_B,
+                 g_param_spec_double ("arrow-shape-b", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ARROW_SHAPE_C,
+                 g_param_spec_double ("arrow-shape-c", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+
+	object_class->destroy = foo_canvas_line_destroy;
+
+	item_class->update = foo_canvas_line_update;
+	item_class->realize = foo_canvas_line_realize;
+	item_class->unrealize = foo_canvas_line_unrealize;
+	item_class->draw = foo_canvas_line_draw;
+	item_class->point = foo_canvas_line_point;
+	item_class->translate = foo_canvas_line_translate;
+	item_class->bounds = foo_canvas_line_bounds;
+}
+
+static void
+foo_canvas_line_init (FooCanvasLine *line)
+{
+	line->width = 0.0;
+	line->cap = GDK_CAP_BUTT;
+	line->join = GDK_JOIN_MITER;
+	line->line_style = GDK_LINE_SOLID;
+	line->shape_a = 0.0;
+	line->shape_b = 0.0;
+	line->shape_c = 0.0;
+	line->spline_steps = DEFAULT_SPLINE_STEPS;
+}
+
+static void
+foo_canvas_line_destroy (GtkObject *object)
+{
+	FooCanvasLine *line;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_LINE (object));
+
+	line = FOO_CANVAS_LINE (object);
+
+	/* remember, destroy can be run multiple times! */
+
+	if (line->coords)
+		g_free (line->coords);
+	line->coords = NULL;
+
+	if (line->first_coords)
+		g_free (line->first_coords);
+	line->first_coords = NULL;
+
+	if (line->last_coords)
+		g_free (line->last_coords);
+	line->last_coords = NULL;
+
+	if (line->stipple)
+		g_object_unref (line->stipple);
+	line->stipple = NULL;
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* Computes the bounding box of the line, including its arrow points.  Assumes that the number of
+ * points in the line is not zero.
+ */
+static void
+get_bounds (FooCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2)
+{
+	double *coords;
+	double x1, y1, x2, y2;
+	double width;
+	int i;
+
+	if (!line->coords) {
+	    *bx1 = *by1 = *bx2 = *by2 = 0.0;
+	    return;
+	}
+	
+	/* Find bounding box of line's points */
+
+	x1 = x2 = line->coords[0];
+	y1 = y2 = line->coords[1];
+
+	for (i = 1, coords = line->coords + 2; i < line->num_points; i++, coords += 2)
+		GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+	/* Add possible over-estimate for wide lines */
+
+	if (line->width_pixels)
+		width = line->width / line->item.canvas->pixels_per_unit;
+	else
+		width = line->width;
+
+	x1 -= width;
+	y1 -= width;
+	x2 += width;
+	y2 += width;
+
+	/* For mitered lines, make a second pass through all the points.  Compute the location of
+	 * the two miter vertex points and add them to the bounding box.
+	 */
+
+	if (line->join == GDK_JOIN_MITER)
+		for (i = line->num_points, coords = line->coords; i >= 3; i--, coords += 2) {
+			double mx1, my1, mx2, my2;
+
+			if (foo_canvas_get_miter_points (coords[0], coords[1],
+							   coords[2], coords[3],
+							   coords[4], coords[5],
+							   width,
+							   &mx1, &my1, &mx2, &my2)) {
+				GROW_BOUNDS (x1, y1, x2, y2, mx1, my1);
+				GROW_BOUNDS (x1, y1, x2, y2, mx2, my2);
+			}
+		}
+
+	/* Add the arrow points, if any */
+
+	if (line->first_arrow && line->first_coords)
+		for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+			GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+	if (line->last_arrow && line->last_coords)
+		for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+			GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+	/* Done */
+
+	*bx1 = x1;
+	*by1 = y1;
+	*bx2 = x2;
+	*by2 = y2;
+}
+
+/* Computes the bounding box of the line, in canvas coordinates.  Assumes that the number of points in the polygon is
+ * not zero. 
+ */
+static void
+get_bounds_canvas (FooCanvasLine *line,
+		   double *bx1, double *by1, double *bx2, double *by2,
+		   double i2w_dx, double i2w_dy)
+{
+	FooCanvasItem *item;
+	double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+
+	item = FOO_CANVAS_ITEM (line);
+
+	get_bounds (line, &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1);
+
+	bbox_x0 += i2w_dx; 
+	bbox_y0 += i2w_dy; 
+	bbox_x1 += i2w_dx; 
+	bbox_y1 += i2w_dy; 
+
+	foo_canvas_w2c_rect_d (item->canvas,
+				 &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1);
+	
+	/* include 1 pixel of fudge */
+	*bx1 = bbox_x0 - 1;
+	*by1 = bbox_y0 - 1;
+	*bx2 = bbox_x1 + 1;
+	*by2 = bbox_y1 + 1;
+}
+
+/* Recalculates the arrow polygons for the line */
+static void
+reconfigure_arrows (FooCanvasLine *line)
+{
+	double *poly, *coords;
+	double dx, dy, length;
+	double sin_theta, cos_theta, tmp;
+	double frac_height;	/* Line width as fraction of arrowhead width */
+	double backup;		/* Distance to backup end points so the line ends in the middle of the arrowhead */
+	double vx, vy;		/* Position of arrowhead vertex */
+	double shape_a, shape_b, shape_c;
+	double width;
+	int i;
+
+	if (line->num_points == 0)
+		return;
+
+	/* Set up things */
+
+	if (line->first_arrow) {
+		if (line->first_coords) {
+			line->coords[0] = line->first_coords[0];
+			line->coords[1] = line->first_coords[1];
+		} else
+			line->first_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+	} else if (line->first_coords) {
+		line->coords[0] = line->first_coords[0];
+		line->coords[1] = line->first_coords[1];
+
+		g_free (line->first_coords);
+		line->first_coords = NULL;
+	}
+
+	i = 2 * (line->num_points - 1);
+
+	if (line->last_arrow) {
+		if (line->last_coords) {
+			line->coords[i] = line->last_coords[0];
+			line->coords[i + 1] = line->last_coords[1];
+		} else
+			line->last_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+	} else if (line->last_coords) {
+		line->coords[i] = line->last_coords[0];
+		line->coords[i + 1] = line->last_coords[1];
+
+		g_free (line->last_coords);
+		line->last_coords = NULL;
+	}
+
+	if (!line->first_arrow && !line->last_arrow)
+		return;
+
+	if (line->width_pixels)
+		width = line->width / line->item.canvas->pixels_per_unit;
+	else
+		width = line->width;
+
+	/* Add fudge value for better-looking results */
+
+	shape_a = line->shape_a;
+	shape_b = line->shape_b;
+	shape_c = line->shape_c + width / 2.0;
+
+	if (line->width_pixels) {
+		shape_a /= line->item.canvas->pixels_per_unit;
+		shape_b /= line->item.canvas->pixels_per_unit;
+		shape_c /= line->item.canvas->pixels_per_unit;
+	}
+
+	shape_a += 0.001;
+	shape_b += 0.001;
+	shape_c += 0.001;
+
+	/* Compute the polygon for the first arrowhead and adjust the first point in the line so
+	 * that the line does not stick out past the leading edge of the arrowhead.
+	 */
+
+	frac_height = (line->width / 2.0) / shape_c;
+	backup = frac_height * shape_b + shape_a * (1.0 - frac_height) / 2.0;
+
+	if (line->first_arrow) {
+		poly = line->first_coords;
+		poly[0] = poly[10] = line->coords[0];
+		poly[1] = poly[11] = line->coords[1];
+
+		dx = poly[0] - line->coords[2];
+		dy = poly[1] - line->coords[3];
+		length = sqrt (dx * dx + dy * dy);
+		if (length < FOO_CANVAS_EPSILON)
+			sin_theta = cos_theta = 0.0;
+		else {
+			sin_theta = dy / length;
+			cos_theta = dx / length;
+		}
+
+		vx = poly[0] - shape_a * cos_theta;
+		vy = poly[1] - shape_a * sin_theta;
+
+		tmp = shape_c * sin_theta;
+
+		poly[2] = poly[0] - shape_b * cos_theta + tmp;
+		poly[8] = poly[2] - 2.0 * tmp;
+
+		tmp = shape_c * cos_theta;
+
+		poly[3] = poly[1] - shape_b * sin_theta - tmp;
+		poly[9] = poly[3] + 2.0 * tmp;
+
+		poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+		poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+		poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+		poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+		/* Move the first point towards the second so that the corners at the end of the
+		 * line are inside the arrowhead.
+		 */
+
+		line->coords[0] = poly[0] - backup * cos_theta;
+		line->coords[1] = poly[1] - backup * sin_theta;
+	}
+
+	/* Same process for last arrowhead */
+
+	if (line->last_arrow) {
+		coords = line->coords + 2 * (line->num_points - 2);
+		poly = line->last_coords;
+		poly[0] = poly[10] = coords[2];
+		poly[1] = poly[11] = coords[3];
+
+		dx = poly[0] - coords[0];
+		dy = poly[1] - coords[1];
+		length = sqrt (dx * dx + dy * dy);
+		if (length < FOO_CANVAS_EPSILON)
+			sin_theta = cos_theta = 0.0;
+		else {
+			sin_theta = dy / length;
+			cos_theta = dx / length;
+		}
+
+		vx = poly[0] - shape_a * cos_theta;
+		vy = poly[1] - shape_a * sin_theta;
+
+		tmp = shape_c * sin_theta;
+
+		poly[2] = poly[0] - shape_b * cos_theta + tmp;
+		poly[8] = poly[2] - 2.0 * tmp;
+
+		tmp = shape_c * cos_theta;
+
+		poly[3] = poly[1] - shape_b * sin_theta - tmp;
+		poly[9] = poly[3] + 2.0 * tmp;
+
+		poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+		poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+		poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+		poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+		coords[2] = poly[0] - backup * cos_theta;
+		coords[3] = poly[1] - backup * sin_theta;
+	}
+}
+
+/* Convenience function to set the line's GC's foreground color */
+static void
+set_line_gc_foreground (FooCanvasLine *line)
+{
+	GdkColor c;
+
+	if (!line->gc)
+		return;
+
+	c.pixel = line->fill_pixel;
+	gdk_gc_set_foreground (line->gc, &c);
+}
+
+/* Recalculate the line's width and set it in its GC */
+static void
+set_line_gc_width (FooCanvasLine *line)
+{
+	int width;
+
+	if (!line->gc)
+		return;
+
+	if (line->width_pixels)
+		width = (int) line->width;
+	else
+		width = (int) (line->width * line->item.canvas->pixels_per_unit + 0.5);
+
+	gdk_gc_set_line_attributes (line->gc,
+				    width,
+				    line->line_style,
+				    (line->first_arrow || line->last_arrow) ? GDK_CAP_BUTT : line->cap,
+				    line->join);
+}
+
+/* Sets the stipple pattern for the line */
+static void
+set_stipple (FooCanvasLine *line, GdkBitmap *stipple, int reconfigure)
+{
+	if (line->stipple && !reconfigure)
+		g_object_unref (line->stipple);
+
+	line->stipple = stipple;
+	if (stipple && !reconfigure)
+		g_object_ref (stipple);
+
+	if (line->gc) {
+		if (stipple) {
+			gdk_gc_set_stipple (line->gc, stipple);
+			gdk_gc_set_fill (line->gc, GDK_STIPPLED);
+		} else
+			gdk_gc_set_fill (line->gc, GDK_SOLID);
+	}
+}
+
+static void
+foo_canvas_line_set_property (GObject              *object,
+				guint                 param_id,
+				const GValue         *value,
+				GParamSpec           *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasLine *line;
+	FooCanvasPoints *points;
+	GdkColor color = { 0, 0, 0, 0, };
+	GdkColor *pcolor;
+	gboolean color_changed;
+	int have_pixel;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_LINE (object));
+
+	item = FOO_CANVAS_ITEM (object);
+	line = FOO_CANVAS_LINE (object);
+
+	color_changed = FALSE;
+	have_pixel = FALSE;
+
+	switch (param_id) {
+	case PROP_POINTS:
+		points = g_value_get_boxed (value);
+
+		if (line->coords) {
+			g_free (line->coords);
+			line->coords = NULL;
+		}
+
+		if (!points)
+			line->num_points = 0;
+		else {
+			line->num_points = points->num_points;
+			line->coords = g_new (double, 2 * line->num_points);
+			memcpy (line->coords, points->coords, 2 * line->num_points * sizeof (double));
+		}
+
+		/* Drop the arrowhead polygons if they exist -- they will be regenerated */
+
+		if (line->first_coords) {
+			g_free (line->first_coords);
+			line->first_coords = NULL;
+		}
+
+		if (line->last_coords) {
+			g_free (line->last_coords);
+			line->last_coords = NULL;
+		}
+
+		/* Since the line's points have changed, we need to re-generate arrowheads in
+		 * addition to recalculating the bounds.
+		 */
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_FILL_COLOR:
+		if (g_value_get_string (value))
+			gdk_color_parse (g_value_get_string (value), &color);
+		line->fill_rgba = ((color.red & 0xff00) << 16 |
+				   (color.green & 0xff00) << 8 |
+				   (color.blue & 0xff00) |
+				   0xff);
+		color_changed = TRUE;
+		break;
+
+	case PROP_FILL_COLOR_GDK:
+		pcolor = g_value_get_boxed (value);
+		if (pcolor) {
+			GdkColormap *colormap;
+			color = *pcolor;
+
+			colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+			gdk_rgb_find_color (colormap, &color);
+
+			have_pixel = TRUE;
+		}
+
+		line->fill_rgba = ((color.red & 0xff00) << 16 |
+				   (color.green & 0xff00) << 8 |
+				   (color.blue & 0xff00) |
+				   0xff);
+		color_changed = TRUE;
+		break;
+
+	case PROP_FILL_COLOR_RGBA:
+		line->fill_rgba = g_value_get_uint (value);
+		color_changed = TRUE;
+		break;
+
+	case PROP_FILL_STIPPLE:
+		set_stipple (line, (GdkBitmap *) g_value_get_object (value), FALSE);
+		foo_canvas_item_request_redraw (item);		
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		line->width = g_value_get_uint (value);
+		line->width_pixels = TRUE;
+		set_line_gc_width (line);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_UNITS:
+		line->width = fabs (g_value_get_double (value));
+		line->width_pixels = FALSE;
+		set_line_gc_width (line);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_CAP_STYLE:
+		line->cap = g_value_get_enum (value);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_JOIN_STYLE:
+		line->join = g_value_get_enum (value);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_LINE_STYLE:
+		line->line_style = g_value_get_enum (value);
+		set_line_gc_width (line);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_FIRST_ARROWHEAD:
+		line->first_arrow = g_value_get_boolean (value);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_LAST_ARROWHEAD:
+		line->last_arrow = g_value_get_boolean (value);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_SMOOTH:
+		/* FIXME */
+		break;
+
+	case PROP_SPLINE_STEPS:
+		/* FIXME */
+		break;
+
+	case PROP_ARROW_SHAPE_A:
+		line->shape_a = fabs (g_value_get_double (value));
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_ARROW_SHAPE_B:
+		line->shape_b = fabs (g_value_get_double (value));
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_ARROW_SHAPE_C:
+		line->shape_c = fabs (g_value_get_double (value));
+		foo_canvas_item_request_update (item);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+
+	if (color_changed) {
+		if (have_pixel)
+			line->fill_pixel = color.pixel;
+		else
+			line->fill_pixel = foo_canvas_get_color_pixel (item->canvas,
+									 line->fill_rgba);
+
+		set_line_gc_foreground (line);
+
+		foo_canvas_item_request_redraw (item);		
+	}
+}
+
+/* Returns a copy of the line's points without the endpoint adjustments for
+ * arrowheads.
+ */
+static FooCanvasPoints *
+get_points (FooCanvasLine *line)
+{
+	FooCanvasPoints *points;
+	int start_ofs, end_ofs;
+
+	if (line->num_points == 0)
+		return NULL;
+
+	start_ofs = end_ofs = 0;
+
+	points = foo_canvas_points_new (line->num_points);
+
+	/* Invariant:  if first_coords or last_coords exist, then the line's
+	 * endpoints have been adjusted.
+	 */
+
+	if (line->first_coords) {
+		start_ofs = 1;
+
+		points->coords[0] = line->first_coords[0];
+		points->coords[1] = line->first_coords[1];
+	}
+
+	if (line->last_coords) {
+		end_ofs = 1;
+
+		points->coords[2 * (line->num_points - 1)] = line->last_coords[0];
+		points->coords[2 * (line->num_points - 1) + 1] = line->last_coords[1];
+	}
+
+	memcpy (points->coords + 2 * start_ofs,
+		line->coords + 2 * start_ofs,
+		2 * (line->num_points - (start_ofs + end_ofs)) * sizeof (double));
+
+	return points;
+}
+
+static void
+foo_canvas_line_get_property (GObject              *object,
+				guint                 param_id,
+				GValue               *value,
+				GParamSpec           *pspec)
+{
+	FooCanvasLine *line;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_LINE (object));
+
+	line = FOO_CANVAS_LINE (object);
+
+	switch (param_id) {
+	case PROP_POINTS:
+		g_value_set_boxed (value, get_points (line));
+		break;
+
+	case PROP_FILL_COLOR:
+		g_value_take_string (value,
+				     g_strdup_printf ("#%02x%02x%02x",
+						      line->fill_rgba >> 24,
+						      (line->fill_rgba >> 16) & 0xff,
+						      (line->fill_rgba >> 8) & 0xff));
+		break;
+
+	case PROP_FILL_COLOR_GDK: {
+		FooCanvas *canvas = FOO_CANVAS_ITEM (line)->canvas;
+		GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+		GdkColor color;
+
+		gdk_colormap_query_color (colormap, line->fill_pixel, &color);
+		g_value_set_boxed (value, &color);
+		break;
+	}
+
+	case PROP_FILL_COLOR_RGBA:
+		g_value_set_uint (value, line->fill_rgba);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		g_value_set_object (value, line->stipple);
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		g_value_set_uint (value, line->width);
+		break;
+		
+	case PROP_WIDTH_UNITS:
+		g_value_set_double (value, line->width);
+		break;
+		
+	case PROP_CAP_STYLE:
+		g_value_set_enum (value, line->cap);
+		break;
+
+	case PROP_JOIN_STYLE:
+		g_value_set_enum (value, line->join);
+		break;
+
+	case PROP_LINE_STYLE:
+		g_value_set_enum (value, line->line_style);
+		break;
+
+	case PROP_FIRST_ARROWHEAD:
+		g_value_set_boolean (value, line->first_arrow);
+		break;
+
+	case PROP_LAST_ARROWHEAD:
+		g_value_set_boolean (value, line->last_arrow);
+		break;
+
+	case PROP_SMOOTH:
+		g_value_set_boolean (value, line->smooth);
+		break;
+
+	case PROP_SPLINE_STEPS:
+		g_value_set_uint (value, line->spline_steps);
+		break;
+
+	case PROP_ARROW_SHAPE_A:
+		g_value_set_double (value, line->shape_a);
+		break;
+
+	case PROP_ARROW_SHAPE_B:
+		g_value_set_double (value, line->shape_b);
+		break;
+
+	case PROP_ARROW_SHAPE_C:
+		g_value_set_double (value, line->shape_c);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+foo_canvas_line_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	FooCanvasLine *line;
+	double x1, y1, x2, y2;
+
+	line = FOO_CANVAS_LINE (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+	reconfigure_arrows (line);
+
+	set_line_gc_foreground (line);
+	set_line_gc_width (line);
+	set_stipple (line, line->stipple, TRUE);
+	
+	get_bounds_canvas (line, &x1, &y1, &x2, &y2, i2w_dx, i2w_dy);
+	foo_canvas_update_bbox (item, x1, y1, x2, y2);
+}
+
+static void
+foo_canvas_line_realize (FooCanvasItem *item)
+{
+	FooCanvasLine *line;
+
+	line = FOO_CANVAS_LINE (item);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (item);
+
+	line->gc = gdk_gc_new (item->canvas->layout.bin_window);
+/* FIXME FIXME FIXME Need to recalc pixel values, set colours, etc. */
+
+#if 0
+	(* FOO_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+foo_canvas_line_unrealize (FooCanvasItem *item)
+{
+	FooCanvasLine *line;
+
+	line = FOO_CANVAS_LINE (item);
+
+	g_object_unref (line->gc);
+	line->gc = NULL;
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (item);
+}
+
+static void
+item_to_canvas (FooCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
+		int *num_drawn_points, double i2w_dx, double i2w_dy)
+{
+	int i;
+	int old_cx, old_cy;
+	int cx, cy;
+
+	/* the first point is always drawn */
+	foo_canvas_w2c (canvas,
+			  item_coords[0] + i2w_dx,
+			  item_coords[1] + i2w_dy,
+			  &canvas_coords->x, &canvas_coords->y);
+	old_cx = canvas_coords->x;
+	old_cy = canvas_coords->y;
+	canvas_coords++;
+	*num_drawn_points = 1;
+
+	for (i = 1; i < num_points; i++) {
+		foo_canvas_w2c (canvas,
+				  item_coords[i*2] + i2w_dx,
+				  item_coords[i*2+1] + i2w_dy,
+				  &cx, &cy);
+		if (old_cx != cx || old_cy != cy) {
+			canvas_coords->x = cx;
+			canvas_coords->y = cy;
+			old_cx = cx;
+			old_cy = cy;
+			canvas_coords++;
+			(*num_drawn_points)++;
+		}
+	}
+}
+
+static void
+foo_canvas_line_draw (FooCanvasItem *item, GdkDrawable *drawable,
+			GdkEventExpose *event)
+{
+	FooCanvasLine *line;
+	GdkPoint static_points[NUM_STATIC_POINTS];
+	GdkPoint *points;
+	int actual_num_points_drawn;
+	double i2w_dx, i2w_dy;
+	
+	line = FOO_CANVAS_LINE (item);
+
+	if (line->num_points == 0)
+		return;
+
+	/* Build array of canvas pixel coordinates */
+
+	if (line->num_points <= NUM_STATIC_POINTS)
+		points = static_points;
+	else
+		points = g_new (GdkPoint, line->num_points);
+
+	i2w_dx = 0.0;
+	i2w_dy = 0.0;
+	foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+ 
+	item_to_canvas (item->canvas, line->coords, points, line->num_points,
+			&actual_num_points_drawn, i2w_dx, i2w_dy);
+
+	if (line->stipple)
+		foo_canvas_set_stipple_origin (item->canvas, line->gc);
+
+	gdk_draw_lines (drawable, line->gc, points, actual_num_points_drawn);
+
+	if (points != static_points)
+		g_free (points);
+
+	/* Draw arrowheads */
+
+	points = static_points;
+
+	if (line->first_arrow) {
+		item_to_canvas (item->canvas, line->first_coords, points, NUM_ARROW_POINTS,
+				&actual_num_points_drawn, i2w_dx, i2w_dy);
+		gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+	}
+
+	if (line->last_arrow) {
+		item_to_canvas (item->canvas, line->last_coords, points, NUM_ARROW_POINTS,
+				&actual_num_points_drawn, i2w_dx, i2w_dy);
+		gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+	}
+}
+
+static double
+foo_canvas_line_point (FooCanvasItem *item, double x, double y,
+			 int cx, int cy, FooCanvasItem **actual_item)
+{
+	FooCanvasLine *line;
+	double *line_points = NULL, *coords;
+	double static_points[2 * NUM_STATIC_POINTS];
+	double poly[10];
+	double best, dist;
+	double dx, dy;
+	double width;
+	int num_points = 0, i;
+	int changed_miter_to_bevel;
+
+#ifdef VERBOSE
+	g_print ("foo_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
+#endif
+
+	line = FOO_CANVAS_LINE (item);
+
+	*actual_item = item;
+
+	best = 1.0e36;
+
+	/* Handle smoothed lines by generating an expanded set ot points */
+
+	if (line->smooth && (line->num_points > 2)) {
+		/* FIXME */
+	} else {
+		num_points = line->num_points;
+		line_points = line->coords;
+	}
+
+	/* Compute a polygon for each edge of the line and test the point against it.  The effective
+	 * width of the line is adjusted so that it will be at least one pixel thick (so that zero
+	 * pixel-wide lines can be pickedup as well).
+	 */
+
+	if (line->width_pixels)
+		width = line->width / item->canvas->pixels_per_unit;
+	else
+		width = line->width;
+
+	if (width < (1.0 / item->canvas->pixels_per_unit))
+		width = 1.0 / item->canvas->pixels_per_unit;
+
+	changed_miter_to_bevel = 0;
+
+	for (i = num_points, coords = line_points; i >= 2; i--, coords += 2) {
+		/* If rounding is done around the first point, then compute distance between the
+		 * point and the first point.
+		 */
+
+		if (((line->cap == GDK_CAP_ROUND) && (i == num_points))
+		    || ((line->join == GDK_JOIN_ROUND) && (i != num_points))) {
+			dx = coords[0] - x;
+			dy = coords[1] - y;
+			dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+			if (dist < FOO_CANVAS_EPSILON) {
+				best = 0.0;
+				goto done;
+			} else if (dist < best)
+				best = dist;
+		}
+
+		/* Compute the polygonal shape corresponding to this edge, with two points for the
+		 * first point of the edge and two points for the last point of the edge.
+		 */
+
+		if (i == num_points)
+			foo_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+						      width, (line->cap == GDK_CAP_PROJECTING),
+						      poly, poly + 1, poly + 2, poly + 3);
+		else if ((line->join == GDK_JOIN_MITER) && !changed_miter_to_bevel) {
+			poly[0] = poly[6];
+			poly[1] = poly[7];
+			poly[2] = poly[4];
+			poly[3] = poly[5];
+		} else {
+			foo_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+						      width, FALSE,
+						      poly, poly + 1, poly + 2, poly + 3);
+
+			/* If this line uses beveled joints, then check the distance to a polygon
+			 * comprising the last two points of the previous polygon and the first two
+			 * from this polygon; this checks the wedges that fill the mitered point.
+			 */
+
+			if ((line->join == GDK_JOIN_BEVEL) || changed_miter_to_bevel) {
+				poly[8] = poly[0];
+				poly[9] = poly[1];
+
+				dist = foo_canvas_polygon_to_point (poly, 5, x, y);
+				if (dist < FOO_CANVAS_EPSILON) {
+					best = 0.0;
+					goto done;
+				} else if (dist < best)
+					best = dist;
+
+				changed_miter_to_bevel = FALSE;
+			}
+		}
+
+		if (i == 2)
+			foo_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+						      width, (line->cap == GDK_CAP_PROJECTING),
+						      poly + 4, poly + 5, poly + 6, poly + 7);
+		else if (line->join == GDK_JOIN_MITER) {
+			if (!foo_canvas_get_miter_points (coords[0], coords[1],
+							    coords[2], coords[3],
+							    coords[4], coords[5],
+							    width,
+							    poly + 4, poly + 5, poly + 6, poly + 7)) {
+				changed_miter_to_bevel = TRUE;
+				foo_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+							      width, FALSE,
+							      poly + 4, poly + 5, poly + 6, poly + 7);
+			}
+		} else
+			foo_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+						      width, FALSE,
+						      poly + 4, poly + 5, poly + 6, poly + 7);
+
+		poly[8] = poly[0];
+		poly[9] = poly[1];
+
+		dist = foo_canvas_polygon_to_point (poly, 5, x, y);
+		if (dist < FOO_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else if (dist < best)
+			best = dist;
+	}
+
+	/* If caps are rounded, check the distance to the cap around the final end point of the line */
+
+	if (line->cap == GDK_CAP_ROUND) {
+		dx = coords[0] - x;
+		dy = coords[1] - y;
+		dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+		if (dist < FOO_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else
+			best = dist;
+	}
+
+	/* sometimes the FooCanvasItem::update signal will not have
+           been processed between deleting the arrow points and a call
+           to this routine -- this can cause a segfault here */
+	if ((line->first_arrow && !line->first_coords) ||
+	    (line->last_arrow && !line->last_coords))
+		reconfigure_arrows(line);
+
+	/* If there are arrowheads, check the distance to them */
+
+	if (line->first_arrow) {
+		dist = foo_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
+		if (dist < FOO_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else
+			best = dist;
+	}
+
+	if (line->last_arrow) {
+		dist = foo_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
+		if (dist < FOO_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else
+			best = dist;
+	}
+
+done:
+
+	if ((line_points != static_points) && (line_points != line->coords))
+		g_free (line_points);
+
+	return best;
+}
+
+static void
+foo_canvas_line_translate (FooCanvasItem *item, double dx, double dy)
+{
+        FooCanvasLine *line;
+        int i;
+        double *coords;
+
+        line = FOO_CANVAS_LINE (item);
+
+        for (i = 0, coords = line->coords; i < line->num_points; i++, coords += 2) {
+                coords[0] += dx;
+                coords[1] += dy;
+        }
+
+        if (line->first_arrow)
+                for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2) {
+                        coords[0] += dx;
+                        coords[1] += dy;
+                }
+
+        if (line->last_arrow)
+                for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2) {
+                        coords[0] += dx;
+                        coords[1] += dy;
+                }
+}
+
+static void
+foo_canvas_line_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	FooCanvasLine *line;
+
+	line = FOO_CANVAS_LINE (item);
+
+	if (line->num_points == 0) {
+		*x1 = *y1 = *x2 = *y2 = 0.0;
+		return;
+	}
+
+	get_bounds (line, x1, y1, x2, y2);
+}
diff --git a/libfoocanvas/foo-canvas-line.h b/libfoocanvas/foo-canvas-line.h
new file mode 100644
index 0000000..42dfd74
--- /dev/null
+++ b/libfoocanvas/foo-canvas-line.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/* Line/curve item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_LINE_H
+#define FOO_CANVAS_LINE_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Line item for the canvas.  This is a polyline with configurable width, cap/join styles, and arrowheads.
+ * If arrowheads are enabled, then three values are used to specify their shape:
+ *
+ *	arrow_shape_a:  Distance from tip of arrowhead to the center point.
+ *	arrow_shape_b:  Distance from tip of arrowhead to trailing point, measured along the shaft.
+ *	arrow_shape_c:	Distance of trailing point from outside edge of shaft.
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * points		FooCanvasPoints*	RW		Pointer to a FooCanvasPoints structure.
+ *								This can be created by a call to
+ *								foo_canvas_points_new() (in foo-canvas-util.h).
+ *								X coordinates are in the even indices of the
+ *								points->coords array, Y coordinates are in
+ *								the odd indices.
+ * fill_color		string			W		X color specification for line
+ * fill_color_gdk	GdkColor*		RW		Pointer to an allocated GdkColor
+ * fill_stipple		GdkBitmap*		RW		Stipple pattern for the line
+ * width_pixels		uint			R		Width of the line in pixels.  The line width
+ *								will not be scaled when the canvas zoom factor changes.
+ * width_units		double			R		Width of the line in canvas units.  The line width
+ *								will be scaled when the canvas zoom factor changes.
+ * cap_style		GdkCapStyle		RW		Cap ("endpoint") style for the line.
+ * join_style		GdkJoinStyle		RW		Join ("vertex") style for the line.
+ * line_style		GdkLineStyle		RW		Line dash style
+ * first_arrowhead	boolean			RW		Specifies whether to draw an arrowhead on the
+ *								first point of the line.
+ * last_arrowhead	boolean			RW		Specifies whether to draw an arrowhead on the
+ *								last point of the line.
+ * smooth		boolean			RW		Specifies whether to smooth the line using
+ *								parabolic splines.
+ * spline_steps		uint			RW		Specifies the number of steps to use when rendering curves.
+ * arrow_shape_a	double			RW		First arrow shape specifier.
+ * arrow_shape_b	double			RW		Second arrow shape specifier.
+ * arrow_shape_c	double			RW		Third arrow shape specifier.
+ */
+
+
+#define FOO_TYPE_CANVAS_LINE            (foo_canvas_line_get_type ())
+#define FOO_CANVAS_LINE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_LINE, FooCanvasLine))
+#define FOO_CANVAS_LINE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_LINE, FooCanvasLineClass))
+#define FOO_IS_CANVAS_LINE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_LINE))
+#define FOO_IS_CANVAS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_LINE))
+#define FOO_CANVAS_LINE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_LINE, FooCanvasLineClass))
+
+
+typedef struct _FooCanvasLine FooCanvasLine;
+typedef struct _FooCanvasLineClass FooCanvasLineClass;
+
+struct _FooCanvasLine {
+	FooCanvasItem item;
+
+	double *coords;		/* Array of coordinates for the line's points.  X coords are in the
+				 * even indices, Y coords are in the odd indices.  If the line has
+				 * arrowheads then the first and last points have been adjusted to
+				 * refer to the necks of the arrowheads rather than their tips.  The
+				 * actual endpoints are stored in the first_arrow and last_arrow
+				 * arrays, if they exist.
+				 */
+
+	double *first_coords;	/* Array of points describing polygon for the first arrowhead */
+	double *last_coords;	/* Array of points describing polygon for the last arrowhead */
+
+	GdkGC *gc;		/* GC for drawing line */
+
+	GdkBitmap *stipple;	/* Stipple pattern */
+
+	double width;		/* Width of the line */
+
+	double shape_a;		/* Distance from tip of arrowhead to center */
+	double shape_b;		/* Distance from tip of arrowhead to trailing point, measured along shaft */
+	double shape_c;		/* Distance of trailing points from outside edge of shaft */
+
+	GdkCapStyle cap;	/* Cap style for line */
+	GdkJoinStyle join;	/* Join style for line */
+	GdkLineStyle line_style;/* Style for the line */
+
+	gulong fill_pixel;	/* Color for line */
+
+	guint32 fill_rgba;		/* RGBA color for outline */ /*AA*/
+
+	int num_points;		/* Number of points in the line */
+	guint fill_color;	/* Fill color, RGBA */
+
+	int spline_steps;	/* Number of steps in each spline segment */
+
+	guint width_pixels : 1;	/* Is the width specified in pixels or units? */
+	guint first_arrow : 1;	/* Draw first arrowhead? */
+	guint last_arrow : 1;	/* Draw last arrowhead? */
+	guint smooth : 1;	/* Smooth line (with parabolic splines)? */
+};
+
+struct _FooCanvasLineClass {
+	FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_line_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-marshal.c b/libfoocanvas/foo-canvas-marshal.c
new file mode 100644
index 0000000..d95d474
--- /dev/null
+++ b/libfoocanvas/foo-canvas-marshal.c
@@ -0,0 +1,131 @@
+/* This file has been automatically generated.  Do not edit. */
+#include "foo-canvas-marshal.h"
+
+#include	<glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* VOID:INT,INT,INT,INT (foo-canvas-marshal.list:1) */
+void
+foo_canvas_marshal_VOID__INT_INT_INT_INT (GClosure     *closure,
+                                          GValue       *return_value G_GNUC_UNUSED,
+                                          guint         n_param_values,
+                                          const GValue *param_values,
+                                          gpointer      invocation_hint G_GNUC_UNUSED,
+                                          gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__INT_INT_INT_INT) (gpointer     data1,
+                                                      gint         arg_1,
+                                                      gint         arg_2,
+                                                      gint         arg_3,
+                                                      gint         arg_4,
+                                                      gpointer     data2);
+  register GMarshalFunc_VOID__INT_INT_INT_INT callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 5);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__INT_INT_INT_INT) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_int (param_values + 1),
+            g_marshal_value_peek_int (param_values + 2),
+            g_marshal_value_peek_int (param_values + 3),
+            g_marshal_value_peek_int (param_values + 4),
+            data2);
+}
+
+/* BOOLEAN:BOXED (foo-canvas-marshal.list:2) */
+void
+foo_canvas_marshal_BOOLEAN__BOXED (GClosure     *closure,
+                                   GValue       *return_value G_GNUC_UNUSED,
+                                   guint         n_param_values,
+                                   const GValue *param_values,
+                                   gpointer      invocation_hint G_GNUC_UNUSED,
+                                   gpointer      marshal_data)
+{
+  typedef gboolean (*GMarshalFunc_BOOLEAN__BOXED) (gpointer     data1,
+                                                   gpointer     arg_1,
+                                                   gpointer     data2);
+  register GMarshalFunc_BOOLEAN__BOXED callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+  gboolean v_return;
+
+  g_return_if_fail (return_value != NULL);
+  g_return_if_fail (n_param_values == 2);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_BOOLEAN__BOXED) (marshal_data ? marshal_data : cc->callback);
+
+  v_return = callback (data1,
+                       g_marshal_value_peek_boxed (param_values + 1),
+                       data2);
+
+  g_value_set_boolean (return_value, v_return);
+}
+
diff --git a/libfoocanvas/foo-canvas-marshal.h b/libfoocanvas/foo-canvas-marshal.h
new file mode 100644
index 0000000..b8972b1
--- /dev/null
+++ b/libfoocanvas/foo-canvas-marshal.h
@@ -0,0 +1,28 @@
+
+#ifndef __foo_canvas_marshal_MARSHAL_H__
+#define __foo_canvas_marshal_MARSHAL_H__
+
+#include	<glib-object.h>
+
+G_BEGIN_DECLS
+
+/* VOID:INT,INT,INT,INT (foo-canvas-marshal.list:1) */
+extern void foo_canvas_marshal_VOID__INT_INT_INT_INT (GClosure     *closure,
+                                                      GValue       *return_value,
+                                                      guint         n_param_values,
+                                                      const GValue *param_values,
+                                                      gpointer      invocation_hint,
+                                                      gpointer      marshal_data);
+
+/* BOOLEAN:BOXED (foo-canvas-marshal.list:2) */
+extern void foo_canvas_marshal_BOOLEAN__BOXED (GClosure     *closure,
+                                               GValue       *return_value,
+                                               guint         n_param_values,
+                                               const GValue *param_values,
+                                               gpointer      invocation_hint,
+                                               gpointer      marshal_data);
+
+G_END_DECLS
+
+#endif /* __foo_canvas_marshal_MARSHAL_H__ */
+
diff --git a/libfoocanvas/foo-canvas-marshal.list b/libfoocanvas/foo-canvas-marshal.list
new file mode 100644
index 0000000..86f67df
--- /dev/null
+++ b/libfoocanvas/foo-canvas-marshal.list
@@ -0,0 +1,2 @@
+VOID:INT,INT,INT,INT
+BOOLEAN:BOXED
diff --git a/libfoocanvas/foo-canvas-pixbuf.c b/libfoocanvas/foo-canvas-pixbuf.c
new file mode 100644
index 0000000..186e0d7
--- /dev/null
+++ b/libfoocanvas/foo-canvas-pixbuf.c
@@ -0,0 +1,825 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico gimp org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <libfoocanvas/foo-canvas.h>
+#include <libfoocanvas/foo-canvas-util.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "foo-canvas-pixbuf.h"
+
+/* Private part of the FooCanvasPixbuf structure */
+typedef struct {
+	/* Our gdk-pixbuf */
+	GdkPixbuf *pixbuf, *pixbuf_scaled;
+
+	/* Width value */
+	double width;
+
+	/* Height value */
+	double height;
+
+	/* X translation */
+	double x;
+
+	/* Y translation */
+	double y;
+
+	/* Whether dimensions are set and whether they are in pixels or units */
+	guint width_set : 1;
+	guint width_in_pixels : 1;
+	guint height_set : 1;
+	guint height_in_pixels : 1;
+	guint x_in_pixels : 1;
+	guint y_in_pixels : 1;
+
+	/* Whether the pixbuf has changed */
+	guint need_pixbuf_update : 1;
+
+	/* Whether the transformation or size have changed */
+	guint need_xform_update : 1;
+
+	/* Should the point method ignore transparent areas */
+	guint point_ignores_alpha : 1;
+
+	/* Anchor */
+	GtkAnchorType anchor;
+
+	/* Approximation method used for transformations */
+	GdkInterpType interp_type;
+
+} PixbufPrivate;
+
+/* Object argument IDs */
+enum {
+	PROP_0,
+	PROP_PIXBUF,
+	PROP_WIDTH,
+	PROP_WIDTH_SET,
+	PROP_WIDTH_IN_PIXELS,
+	PROP_HEIGHT,
+	PROP_HEIGHT_SET,
+	PROP_HEIGHT_IN_PIXELS,
+	PROP_X,
+	PROP_X_IN_PIXELS,
+	PROP_Y,
+	PROP_Y_IN_PIXELS,
+	PROP_ANCHOR,
+	PROP_INTERP_TYPE,
+	PROP_POINT_IGNORES_ALPHA
+};
+
+static void foo_canvas_pixbuf_class_init (FooCanvasPixbufClass *klass);
+static void foo_canvas_pixbuf_init (FooCanvasPixbuf *cpb);
+static void foo_canvas_pixbuf_destroy (GtkObject *object);
+static void foo_canvas_pixbuf_set_property (GObject *object,
+					    guint param_id,
+					    const GValue *value,
+					    GParamSpec *pspec);
+static void foo_canvas_pixbuf_get_property (GObject *object,
+					    guint param_id,
+					    GValue *value,
+					    GParamSpec *pspec);
+
+static void foo_canvas_pixbuf_update    (FooCanvasItem *item,
+					 double i2w_dx, double i2w_dy,
+					 int flags);
+static void foo_canvas_pixbuf_draw      (FooCanvasItem *item, GdkDrawable *drawable,
+					 GdkEventExpose *expose);
+static double foo_canvas_pixbuf_point   (FooCanvasItem *item, double x, double y, int cx, int cy,
+					 FooCanvasItem **actual_item);
+static void foo_canvas_pixbuf_translate (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_pixbuf_bounds    (FooCanvasItem *item,
+					 double *x1, double *y1, double *x2, double *y2);
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasPixbuf, foo_canvas_pixbuf, FOO_TYPE_CANVAS_ITEM)
+
+/* Class initialization function for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_class_init (FooCanvasPixbufClass *klass)
+{
+        GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	FooCanvasItemClass *item_class;
+
+        gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (FooCanvasItemClass *) klass;
+
+	parent_class = gtk_type_class (foo_canvas_item_get_type ());
+
+	gobject_class->set_property = foo_canvas_pixbuf_set_property;
+	gobject_class->get_property = foo_canvas_pixbuf_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_PIXBUF,
+                 g_param_spec_object ("pixbuf", NULL, NULL,
+                                      GDK_TYPE_PIXBUF,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH,
+                 g_param_spec_double ("width", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_SET,
+                 g_param_spec_boolean ("width-set", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_IN_PIXELS,
+                 g_param_spec_boolean ("width-in-pixels", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT,
+                 g_param_spec_double ("height", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT_SET,
+                 g_param_spec_boolean ("height-set", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT_IN_PIXELS,
+                 g_param_spec_boolean ("height-in-pixels", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X,
+                 g_param_spec_double ("x", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X_IN_PIXELS,
+                 g_param_spec_boolean ("x-in-pixels", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y,
+                 g_param_spec_double ("y", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y_IN_PIXELS,
+                 g_param_spec_boolean ("y-in-pixels", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ANCHOR,
+                 g_param_spec_enum ("anchor", NULL, NULL,
+                                    GTK_TYPE_ANCHOR_TYPE,
+                                    GTK_ANCHOR_NW,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_INTERP_TYPE,
+                 g_param_spec_enum ("interp-type", NULL, NULL,
+                                    GDK_TYPE_INTERP_TYPE,
+                                    GDK_INTERP_BILINEAR,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+		 PROP_POINT_IGNORES_ALPHA,
+                 g_param_spec_boolean ("point-ignores-alpha", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+
+	object_class->destroy = foo_canvas_pixbuf_destroy;
+
+	item_class->update = foo_canvas_pixbuf_update;
+	item_class->draw = foo_canvas_pixbuf_draw;
+	item_class->point = foo_canvas_pixbuf_point;
+	item_class->translate = foo_canvas_pixbuf_translate;
+	item_class->bounds = foo_canvas_pixbuf_bounds;
+}
+
+/* Object initialization function for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_init (FooCanvasPixbuf *gcp)
+{
+	PixbufPrivate *priv;
+
+	priv = g_new0 (PixbufPrivate, 1);
+	gcp->priv = priv;
+
+	priv->width = 0.0;
+	priv->height = 0.0;
+	priv->x = 0.0;
+	priv->y = 0.0;
+	priv->anchor = GTK_ANCHOR_NW;
+	priv->interp_type = GDK_INTERP_BILINEAR;
+	priv->point_ignores_alpha = FALSE;
+}
+
+/* Destroy handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_destroy (GtkObject *object)
+{
+	FooCanvasItem *item;
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_PIXBUF (object));
+
+	item = FOO_CANVAS_ITEM (object);
+	gcp = (FOO_CANVAS_PIXBUF (object));
+	priv = gcp->priv;
+
+	/* remember, destroy can be run multiple times! */
+
+	if (priv) {
+	    foo_canvas_item_request_redraw (item);
+
+	    if (priv->pixbuf)
+		g_object_unref (priv->pixbuf);
+	    if (priv->pixbuf_scaled)
+		g_object_unref (priv->pixbuf_scaled);
+
+	    g_free (priv);
+	    gcp->priv = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+
+/* Set_property handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_set_property (GObject            *object,
+				guint               param_id,
+				const GValue       *value,
+				GParamSpec         *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	GdkPixbuf *pixbuf;
+	double val;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_PIXBUF (object));
+
+	item = FOO_CANVAS_ITEM (object);
+	gcp = FOO_CANVAS_PIXBUF (object);
+	priv = gcp->priv;
+
+	switch (param_id) {
+	case PROP_PIXBUF:
+		if (g_value_get_object (value))
+			pixbuf = GDK_PIXBUF (g_value_get_object (value));
+		else
+			pixbuf = NULL;
+		if (pixbuf != priv->pixbuf) {
+			if (pixbuf) {
+				g_return_if_fail
+				    (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
+				g_return_if_fail
+				    (gdk_pixbuf_get_n_channels (pixbuf) == 3
+				     || gdk_pixbuf_get_n_channels (pixbuf) == 4);
+				g_return_if_fail
+				    (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
+
+				g_object_ref (pixbuf);
+			}
+
+			if (priv->pixbuf)
+				g_object_unref (priv->pixbuf);
+			priv->pixbuf = pixbuf;
+
+			if (priv->pixbuf_scaled) {
+				g_object_unref (priv->pixbuf_scaled);
+				priv->pixbuf_scaled = NULL;
+			}
+		}
+
+		priv->need_pixbuf_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH:
+		val = g_value_get_double (value);
+		g_return_if_fail (val >= 0.0);
+		priv->width = val;
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_SET:
+		priv->width_set = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_IN_PIXELS:
+		priv->width_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_HEIGHT:
+		val = g_value_get_double (value);
+		g_return_if_fail (val >= 0.0);
+		priv->height = val;
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_HEIGHT_SET:
+		priv->height_set = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_HEIGHT_IN_PIXELS:
+		priv->height_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_X:
+		priv->x = g_value_get_double (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_X_IN_PIXELS:
+		priv->x_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y:
+		priv->y = g_value_get_double (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y_IN_PIXELS:
+		priv->y_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_ANCHOR:
+		priv->anchor = g_value_get_enum (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_INTERP_TYPE:
+		priv->interp_type = g_value_get_enum (value);
+		priv->need_xform_update = TRUE;
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_POINT_IGNORES_ALPHA:
+		priv->point_ignores_alpha = g_value_get_boolean (value);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* Get_property handler for the pixbuf canvasi item */
+static void
+foo_canvas_pixbuf_get_property (GObject            *object,
+				guint               param_id,
+				GValue             *value,
+				GParamSpec         *pspec)
+{
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_PIXBUF (object));
+
+	gcp = FOO_CANVAS_PIXBUF (object);
+	priv = gcp->priv;
+
+	switch (param_id) {
+	case PROP_PIXBUF:
+		g_value_set_object (value, G_OBJECT (priv->pixbuf));
+		break;
+
+	case PROP_WIDTH:
+		g_value_set_double (value, priv->width);
+		break;
+
+	case PROP_WIDTH_SET:
+		g_value_set_boolean (value, priv->width_set);
+		break;
+
+	case PROP_WIDTH_IN_PIXELS:
+		g_value_set_boolean (value, priv->width_in_pixels);
+		break;
+
+	case PROP_HEIGHT:
+		g_value_set_double (value, priv->height);
+		break;
+
+	case PROP_HEIGHT_SET:
+		g_value_set_boolean (value, priv->height_set);
+		break;
+
+	case PROP_HEIGHT_IN_PIXELS:
+		g_value_set_boolean (value, priv->height_in_pixels);
+		break;
+
+	case PROP_X:
+		g_value_set_double (value, priv->x);
+		break;
+
+	case PROP_X_IN_PIXELS:
+		g_value_set_boolean (value, priv->x_in_pixels);
+		break;
+
+	case PROP_Y:
+		g_value_set_double (value, priv->y);
+		break;
+
+	case PROP_Y_IN_PIXELS:
+		g_value_set_boolean (value, priv->y_in_pixels);
+		break;
+
+	case PROP_ANCHOR:
+		g_value_set_enum (value, priv->anchor);
+		break;
+
+	case PROP_INTERP_TYPE:
+		g_value_set_enum (value, priv->interp_type);
+		break;
+
+	case PROP_POINT_IGNORES_ALPHA:
+		g_value_set_boolean (value, priv->point_ignores_alpha);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+
+
+/* Bounds and utilities */
+
+
+/* Recomputes the bounding box of a pixbuf canvas item.  The horizontal and
+ * vertical dimensions may be specified in units or pixels, separately, so we
+ * have to compute the components individually for each dimension.
+ *
+ * Returns the coordinates with respect to the parent items coordinates.
+ */
+static void
+compute_bounding_box (FooCanvasPixbuf *gcp,
+		      double i2w_dx, double i2w_dy,
+		      double *bbox_x0, double *bbox_y0,
+		      double *bbox_x1, double *bbox_y1)
+{
+	FooCanvasItem *item;
+	PixbufPrivate *priv;
+	double x, y;
+	double width, height;
+
+	item = FOO_CANVAS_ITEM (gcp);
+	priv = gcp->priv;
+
+	if (!priv->pixbuf) {
+		*bbox_x0 = *bbox_y0 = *bbox_x1 = *bbox_y1 = 0.0;
+		return;
+	}
+
+	if (priv->x_in_pixels) {
+		x = i2w_dx + priv->x / item->canvas->pixels_per_unit;
+	} else {
+		x = i2w_dx + priv->x;
+	}
+
+	if (priv->y_in_pixels) {
+		y = i2w_dy + priv->y / item->canvas->pixels_per_unit;
+	} else {
+		y = i2w_dy + priv->y;
+	}
+
+	if (priv->width_set) {
+		width = priv->width;
+	} else {
+		width = gdk_pixbuf_get_width (priv->pixbuf);
+	}
+
+	if (priv->width_in_pixels)
+		width /= item->canvas->pixels_per_unit;
+
+	if (priv->height_set) {
+		height = priv->height;
+	} else {
+		height = gdk_pixbuf_get_height (priv->pixbuf);
+	}
+
+	if (priv->height_in_pixels)
+		height /= item->canvas->pixels_per_unit;
+
+
+	switch (priv->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		x -= width / 2.0;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		x -= width;
+		break;
+
+        default:
+                break;
+	}
+
+	switch (priv->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		y -= height / 2.0;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		y -= height;
+		break;
+
+        default:
+                break;
+	}
+
+	*bbox_x0 = x;
+	*bbox_y0 = y;
+	*bbox_x1 = x + width;
+	*bbox_y1 = y + height;
+}
+
+
+
+/* Update sequence */
+
+/* Update handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_update (FooCanvasItem *item,
+			    double i2w_dx, double i2w_dy,
+			    int flags)
+{
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+	int w, h;
+
+	gcp = FOO_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (parent_class->update)
+		(* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+	/* If we need a pixbuf update, or if the item changed visibility to
+	 * shown, recompute the bounding box.
+	 */
+	if (priv->need_pixbuf_update || priv->need_xform_update ||
+	    (flags & FOO_CANVAS_UPDATE_DEEP)) {
+
+		foo_canvas_item_request_redraw (item);
+
+		compute_bounding_box (gcp, i2w_dx, i2w_dy,
+				      &bbox_x0, &bbox_y0,
+				      &bbox_x1, &bbox_y1);
+
+		foo_canvas_w2c_d (item->canvas,
+				    bbox_x0, bbox_y0,
+				    &item->x1, &item->y1);
+
+		foo_canvas_w2c_d (item->canvas,
+				    bbox_x1, bbox_y1,
+				    &item->x2, &item->y2);
+
+		item->x1 = floor (item->x1 + .5);
+		item->y1 = floor (item->y1 + .5);
+		item->x2 = floor (item->x2 + .5);
+		item->y2 = floor (item->y2 + .5);
+
+#ifdef FOO_CANVAS_PIXBUF_VERBOSE
+		g_print ("BBox is %g %g %g %g\n", item->x1, item->y1, item->x2, item->y2);
+#endif
+
+		if (priv->pixbuf) {
+			w = item->x2 - item->x1;
+			h = item->y2 - item->y1;
+
+			if (priv->pixbuf_scaled)
+				g_object_unref (priv->pixbuf_scaled);
+			if (gdk_pixbuf_get_width (priv->pixbuf) != w ||
+			    gdk_pixbuf_get_height (priv->pixbuf) != h)
+				priv->pixbuf_scaled = gdk_pixbuf_scale_simple (
+					priv->pixbuf, w, h, priv->interp_type);
+			else
+				priv->pixbuf_scaled = g_object_ref (priv->pixbuf);
+		}
+
+		foo_canvas_item_request_redraw (item);
+
+		priv->need_pixbuf_update = FALSE;
+		priv->need_xform_update = FALSE;
+	}
+}
+
+
+
+/* Draw handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_draw (FooCanvasItem *item, GdkDrawable *drawable,
+			  GdkEventExpose *expose)
+{
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	GdkRectangle display_rect, draw_rect;
+	GdkRegion *draw_region;
+	int w, h;
+
+	gcp = FOO_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (!priv->pixbuf)
+		return;
+
+	/* Compute the area we need to repaint */
+
+	w = item->x2 - item->x1;
+	h = item->y2 - item->y1;
+
+	display_rect.x = item->x1;
+	display_rect.y = item->y1;
+	display_rect.width  = w;
+	display_rect.height = h;
+	draw_region = gdk_region_rectangle (&display_rect);
+	gdk_region_intersect (draw_region, expose->region);
+	if (!gdk_region_empty (draw_region)) {
+		gdk_region_get_clipbox (draw_region, &draw_rect);
+		gdk_draw_pixbuf (drawable, NULL, priv->pixbuf_scaled,
+			/* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
+			     draw_rect.x - display_rect.x,
+			     draw_rect.y - display_rect.y,
+			     draw_rect.x,
+			     draw_rect.y,
+			     draw_rect.width,
+			     draw_rect.height,
+			     GDK_RGB_DITHER_NORMAL, 0, 0);
+	}
+	gdk_region_destroy (draw_region);
+}
+
+
+
+
+/* Point handler for the pixbuf canvas item */
+static double
+foo_canvas_pixbuf_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+			   FooCanvasItem **actual_item)
+{
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	double x1, y1, x2, y2;
+	int px, py;
+	double no_hit;
+	guchar *src;
+	GdkPixbuf *pixbuf;
+
+	gcp = FOO_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+	pixbuf = priv->pixbuf;
+
+	*actual_item = item;
+
+	no_hit = item->canvas->pixels_per_unit * 2 + 10;
+
+	if (!priv->pixbuf)
+		return no_hit;
+
+	compute_bounding_box (gcp, 0.0, 0.0,
+			      &x1, &y1, &x2, &y2);
+
+
+	if (x < x1 || x >= x2 ||
+	    y < y1 || y >= y2)
+		return no_hit;
+
+	if (!gdk_pixbuf_get_has_alpha (pixbuf) || priv->point_ignores_alpha)
+		return 0.0;
+
+	px = (x - x1) * gdk_pixbuf_get_width (pixbuf) / (x2 - x1);
+	py = (y - y1) * gdk_pixbuf_get_height (pixbuf) / (y2 - y1);
+
+	src = gdk_pixbuf_get_pixels (pixbuf) +
+		py * gdk_pixbuf_get_rowstride (pixbuf) +
+		px * gdk_pixbuf_get_n_channels (pixbuf);
+
+	if (src[3] < 128)
+		return no_hit;
+	else
+		return 0.0;
+}
+
+
+
+static void
+foo_canvas_pixbuf_translate (FooCanvasItem *item, double dx, double dy)
+{
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+
+	gcp = FOO_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (priv->x_in_pixels) {
+		priv->x += dx * item->canvas->pixels_per_unit;
+	} else {
+		priv->x += dx;
+	}
+
+	if (priv->y_in_pixels) {
+		priv->y += dy * item->canvas->pixels_per_unit;
+	} else {
+		priv->y += dy;
+	}
+
+	priv->need_xform_update = TRUE;
+}
+
+
+
+/* Bounds handler for the pixbuf canvas item */
+static void
+foo_canvas_pixbuf_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	FooCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+
+	gcp = FOO_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (!priv->pixbuf) {
+		*x1 = *y1 = *x2 = *y2 = 0.0;
+		return;
+	}
+
+	compute_bounding_box (gcp, 0.0, 0.0,
+			      x1, y1, x2, y2);
+}
diff --git a/libfoocanvas/foo-canvas-pixbuf.h b/libfoocanvas/foo-canvas-pixbuf.h
new file mode 100644
index 0000000..c477445
--- /dev/null
+++ b/libfoocanvas/foo-canvas-pixbuf.h
@@ -0,0 +1,62 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico gimp org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FOO_CANVAS_PIXBUF_H
+#define FOO_CANVAS_PIXBUF_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+G_BEGIN_DECLS
+
+
+
+#define FOO_TYPE_CANVAS_PIXBUF            (foo_canvas_pixbuf_get_type ())
+#define FOO_CANVAS_PIXBUF(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_PIXBUF, FooCanvasPixbuf))
+#define FOO_CANVAS_PIXBUF_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_PIXBUF, FooCanvasPixbufClass))
+#define FOO_IS_CANVAS_PIXBUF(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_PIXBUF))
+#define FOO_IS_CANVAS_PIXBUF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_PIXBUF))
+#define FOO_CANVAS_PIXBUF_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_PIXBUF, FooCanvasPixbufClass))
+
+
+typedef struct _FooCanvasPixbuf FooCanvasPixbuf;
+typedef struct _FooCanvasPixbufClass FooCanvasPixbufClass;
+
+struct _FooCanvasPixbuf {
+	FooCanvasItem item;
+
+	/* Private data */
+	gpointer priv;
+};
+
+struct _FooCanvasPixbufClass {
+	FooCanvasItemClass parent_class;
+};
+
+
+GType foo_canvas_pixbuf_get_type (void) G_GNUC_CONST;
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-polygon.c b/libfoocanvas/foo-canvas-polygon.c
new file mode 100644
index 0000000..679d7bb
--- /dev/null
+++ b/libfoocanvas/foo-canvas-polygon.c
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Polygon item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "libfoocanvas.h"
+
+
+#define NUM_STATIC_POINTS 256	/* Number of static points to use to avoid allocating arrays */
+
+
+#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) {	\
+	if (x < bx1)				\
+		bx1 = x;			\
+						\
+	if (x > bx2)				\
+		bx2 = x;			\
+						\
+	if (y < by1)				\
+		by1 = y;			\
+						\
+	if (y > by2)				\
+		by2 = y;			\
+}
+
+
+enum {
+	PROP_0,
+	PROP_POINTS,
+	PROP_FILL_COLOR,
+	PROP_FILL_COLOR_GDK,
+	PROP_FILL_COLOR_RGBA,
+	PROP_OUTLINE_COLOR,
+	PROP_OUTLINE_COLOR_GDK,
+	PROP_OUTLINE_COLOR_RGBA,
+	PROP_FILL_STIPPLE,
+	PROP_OUTLINE_STIPPLE,
+	PROP_WIDTH_PIXELS,
+	PROP_WIDTH_UNITS
+};
+
+
+static void foo_canvas_polygon_class_init (FooCanvasPolygonClass *klass);
+static void foo_canvas_polygon_init       (FooCanvasPolygon      *poly);
+static void foo_canvas_polygon_destroy    (GtkObject               *object);
+static void foo_canvas_polygon_set_property (GObject              *object,
+					       guint                 param_id,
+					       const GValue         *value,
+					       GParamSpec           *pspec);
+static void foo_canvas_polygon_get_property (GObject              *object,
+					       guint                 param_id,
+					       GValue               *value,
+					       GParamSpec           *pspec);
+
+static void   foo_canvas_polygon_update      (FooCanvasItem *item,
+						double i2w_dx, double i2w_dy,
+						int flags);
+static void   foo_canvas_polygon_realize     (FooCanvasItem *item);
+static void   foo_canvas_polygon_unrealize   (FooCanvasItem *item);
+static void   foo_canvas_polygon_draw        (FooCanvasItem *item, GdkDrawable *drawable,
+						GdkEventExpose *expose);
+static double foo_canvas_polygon_point       (FooCanvasItem *item, double x, double y,
+						int cx, int cy, FooCanvasItem **actual_item);
+static void   foo_canvas_polygon_translate   (FooCanvasItem *item, double dx, double dy);
+static void   foo_canvas_polygon_bounds      (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasPolygon, foo_canvas_polygon, FOO_TYPE_CANVAS_ITEM)
+
+static void
+foo_canvas_polygon_class_init (FooCanvasPolygonClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	FooCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (FooCanvasItemClass *) klass;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_polygon_set_property;
+	gobject_class->get_property = foo_canvas_polygon_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_POINTS,
+                 g_param_spec_boxed ("points", NULL, NULL,
+				     FOO_TYPE_CANVAS_POINTS,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_string ("fill-color", NULL, NULL,
+                                      NULL,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_GDK,
+                 g_param_spec_boxed ("fill-color-gdk", NULL, NULL,
+				     GDK_TYPE_COLOR,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_RGBA,
+                 g_param_spec_uint ("fill-color-rgba", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_COLOR,
+                 g_param_spec_string ("outline-color", NULL, NULL,
+                                      NULL,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_COLOR_GDK,
+                 g_param_spec_boxed ("outline-color-gdk", NULL, NULL,
+				     GDK_TYPE_COLOR,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_COLOR_RGBA,
+                 g_param_spec_uint ("outline-color-rgba", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_STIPPLE,
+                 g_param_spec_object ("fill-stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_STIPPLE,
+                 g_param_spec_object ("outline-stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_PIXELS,
+                 g_param_spec_uint ("width-pixels", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_UNITS,
+                 g_param_spec_double ("width-units", NULL, NULL,
+				      0.0, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+
+	object_class->destroy = foo_canvas_polygon_destroy;
+
+	item_class->update = foo_canvas_polygon_update;
+	item_class->realize = foo_canvas_polygon_realize;
+	item_class->unrealize = foo_canvas_polygon_unrealize;
+	item_class->draw = foo_canvas_polygon_draw;
+	item_class->point = foo_canvas_polygon_point;
+	item_class->translate = foo_canvas_polygon_translate;
+	item_class->bounds = foo_canvas_polygon_bounds;
+}
+
+static void
+foo_canvas_polygon_init (FooCanvasPolygon *poly)
+{
+	poly->width = 0.0;
+}
+
+static void
+foo_canvas_polygon_destroy (GtkObject *object)
+{
+	FooCanvasPolygon *poly;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
+
+	poly = FOO_CANVAS_POLYGON (object);
+
+	/* remember, destroy can be run multiple times! */
+
+	if (poly->coords)
+		g_free (poly->coords);
+	poly->coords = NULL;
+
+	if (poly->fill_stipple)
+		g_object_unref (poly->fill_stipple);
+	poly->fill_stipple = NULL;
+
+	if (poly->outline_stipple)
+		g_object_unref (poly->outline_stipple);
+	poly->outline_stipple = NULL;
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* Computes the bounding box of the polygon.  Assumes that the number of points in the polygon is
+ * not zero.
+ */
+static gboolean
+get_bounds (FooCanvasPolygon *poly, double *bx1, double *by1, double *bx2, double *by2)
+{
+	double *coords;
+	double x1, y1, x2, y2;
+	double width;
+	int i;
+
+	if (poly->num_points == 0)
+		return FALSE;
+
+	/* Compute bounds of vertices */
+
+	x1 = x2 = poly->coords[0];
+	y1 = y2 = poly->coords[1];
+
+	for (i = 1, coords = poly->coords + 2; i < poly->num_points; i++, coords += 2) {
+		GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+	}
+
+	/* Add outline width */
+
+	if (poly->width_pixels)
+		width = poly->width / poly->item.canvas->pixels_per_unit;
+	else
+		width = poly->width;
+
+	width /= 2.0;
+
+	x1 -= width;
+	y1 -= width;
+	x2 += width;
+	y2 += width;
+
+	/* Done */
+
+	*bx1 = x1;
+	*by1 = y1;
+	*bx2 = x2;
+	*by2 = y2;
+	return TRUE;
+}
+
+/* Computes the bounding box of the polygon, in canvas coordinates.  Assumes that the number of points in the polygon is
+ * not zero.
+ */
+static gboolean
+get_bounds_canvas (FooCanvasPolygon *poly,
+		   double *bx1, double *by1, double *bx2, double *by2,
+		   double i2w_dx, double i2w_dy)
+{
+	FooCanvasItem *item;
+	double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+
+	item = FOO_CANVAS_ITEM (poly);
+
+	if (!get_bounds (poly, &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1))
+		return FALSE;
+
+	bbox_x0 += i2w_dx; 
+	bbox_y0 += i2w_dy; 
+	bbox_x1 += i2w_dx; 
+	bbox_y1 += i2w_dy; 
+
+	foo_canvas_w2c_rect_d (item->canvas,
+				 &bbox_x0, &bbox_y0, &bbox_x1, &bbox_y1);
+	
+	/* include 1 pixel of fudge */
+	*bx1 = bbox_x0 - 1;
+	*by1 = bbox_y0 - 1;
+	*bx2 = bbox_x1 + 1;
+	*by2 = bbox_y1 + 1;
+	return TRUE;
+}
+
+/* Sets the points of the polygon item to the specified ones.  If needed, it will add a point to
+ * close the polygon.
+ */
+static void
+set_points (FooCanvasPolygon *poly, FooCanvasPoints *points)
+{
+	int duplicate;
+
+	/* See if we need to duplicate the first point */
+
+	duplicate = ((points->coords[0] != points->coords[2 * points->num_points - 2])
+		     || (points->coords[1] != points->coords[2 * points->num_points - 1]));
+
+	if (duplicate)
+		poly->num_points = points->num_points + 1;
+	else
+		poly->num_points = points->num_points;
+
+	poly->coords = g_new (double, 2 * poly->num_points);
+	memcpy (poly->coords, points->coords, 2 * points->num_points * sizeof (double));
+
+	if (duplicate) {
+		poly->coords[2 * poly->num_points - 2] = poly->coords[0];
+		poly->coords[2 * poly->num_points - 1] = poly->coords[1];
+	}
+}
+
+/* Convenience function to set a GC's foreground color to the specified pixel value */
+static void
+set_gc_foreground (GdkGC *gc, gulong pixel)
+{
+	GdkColor c;
+
+	if (!gc)
+		return;
+
+	c.pixel = pixel;
+	gdk_gc_set_foreground (gc, &c);
+}
+
+/* Sets the stipple pattern for the specified gc */
+static void
+set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure)
+{
+	if (*internal_stipple && !reconfigure)
+		g_object_unref (*internal_stipple);
+
+	*internal_stipple = stipple;
+	if (stipple && !reconfigure)
+		g_object_ref (stipple);
+
+	if (gc) {
+		if (stipple) {
+			gdk_gc_set_stipple (gc, stipple);
+			gdk_gc_set_fill (gc, GDK_STIPPLED);
+		} else
+			gdk_gc_set_fill (gc, GDK_SOLID);
+	}
+}
+
+/* Recalculate the outline width of the polygon and set it in its GC */
+static void
+set_outline_gc_width (FooCanvasPolygon *poly)
+{
+	int width;
+
+	if (!poly->outline_gc)
+		return;
+
+	if (poly->width_pixels)
+		width = (int) poly->width;
+	else
+		width = (int) (poly->width * poly->item.canvas->pixels_per_unit + 0.5);
+
+	gdk_gc_set_line_attributes (poly->outline_gc, width,
+				    GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
+}
+
+static void
+foo_canvas_polygon_set_property (GObject              *object,
+				   guint                 param_id,
+				   const GValue         *value,
+				   GParamSpec           *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasPolygon *poly;
+	FooCanvasPoints *points;
+	GdkColor color = { 0, 0, 0, 0, };
+	GdkColor *pcolor;
+	int have_pixel;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
+
+	item = FOO_CANVAS_ITEM (object);
+	poly = FOO_CANVAS_POLYGON (object);
+	have_pixel = FALSE;
+
+	switch (param_id) {
+	case PROP_POINTS:
+		points = g_value_get_boxed (value);
+
+		if (poly->coords) {
+			g_free (poly->coords);
+			poly->coords = NULL;
+		}
+
+		if (!points)
+			poly->num_points = 0;
+		else
+			set_points (poly, points);
+
+		foo_canvas_item_request_update (item);
+		break;
+
+        case PROP_FILL_COLOR:
+	case PROP_FILL_COLOR_GDK:
+	case PROP_FILL_COLOR_RGBA:
+		switch (param_id) {
+		case PROP_FILL_COLOR:
+			if (g_value_get_string (value) &&
+			    gdk_color_parse (g_value_get_string (value), &color))
+				poly->fill_set = TRUE;
+			else
+				poly->fill_set = FALSE;
+
+			poly->fill_color = ((color.red & 0xff00) << 16 |
+					    (color.green & 0xff00) << 8 |
+					    (color.blue & 0xff00) |
+					    0xff);
+			break;
+
+		case PROP_FILL_COLOR_GDK:
+			pcolor = g_value_get_boxed (value);
+			poly->fill_set = pcolor != NULL;
+
+			if (pcolor) {
+				GdkColormap *colormap;
+
+				color = *pcolor;
+				colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+				gdk_rgb_find_color (colormap, &color);
+				have_pixel = TRUE;
+			}
+
+			poly->fill_color = ((color.red & 0xff00) << 16 |
+					    (color.green & 0xff00) << 8 |
+					    (color.blue & 0xff00) |
+					    0xff);
+			break;
+
+		case PROP_FILL_COLOR_RGBA:
+			poly->fill_set = TRUE;
+			poly->fill_color = g_value_get_uint (value);
+			break;
+		}
+#ifdef VERBOSE
+		g_print ("poly fill color = %08x\n", poly->fill_color);
+#endif
+		if (have_pixel)
+			poly->fill_pixel = color.pixel;
+		else
+			poly->fill_pixel = foo_canvas_get_color_pixel (item->canvas,
+									 poly->fill_color);
+
+		set_gc_foreground (poly->fill_gc, poly->fill_pixel);
+		foo_canvas_item_request_redraw (item);		
+		break;
+
+        case PROP_OUTLINE_COLOR:
+	case PROP_OUTLINE_COLOR_GDK:
+	case PROP_OUTLINE_COLOR_RGBA:
+		switch (param_id) {
+		case PROP_OUTLINE_COLOR:
+			if (g_value_get_string (value) &&
+			    gdk_color_parse (g_value_get_string (value), &color))
+				poly->outline_set = TRUE;
+			else
+				poly->outline_set = FALSE;
+
+			poly->outline_color = ((color.red & 0xff00) << 16 |
+					       (color.green & 0xff00) << 8 |
+					       (color.blue & 0xff00) |
+					       0xff);
+			break;
+
+		case PROP_OUTLINE_COLOR_GDK:
+			pcolor = g_value_get_boxed (value);
+			poly->outline_set = pcolor != NULL;
+
+			if (pcolor) {
+				GdkColormap *colormap;
+
+				color = *pcolor;
+				colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+				gdk_rgb_find_color (colormap, &color);
+				have_pixel = TRUE;
+			}
+
+			poly->outline_color = ((color.red & 0xff00) << 16 |
+					       (color.green & 0xff00) << 8 |
+					       (color.blue & 0xff00) |
+					       0xff);
+			break;
+
+		case PROP_OUTLINE_COLOR_RGBA:
+			poly->outline_set = TRUE;
+			poly->outline_color = g_value_get_uint (value);
+			break;
+		}
+#ifdef VERBOSE
+		g_print ("poly outline color = %08x\n", poly->outline_color);
+#endif
+		if (have_pixel)
+			poly->outline_pixel = color.pixel;
+		else
+			poly->outline_pixel = foo_canvas_get_color_pixel (item->canvas,
+									    poly->outline_color);
+
+		set_gc_foreground (poly->outline_gc, poly->outline_pixel);
+		foo_canvas_item_request_redraw (item);		
+		break;
+
+	case PROP_FILL_STIPPLE:
+		set_stipple (poly->fill_gc, &poly->fill_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_OUTLINE_STIPPLE:
+		set_stipple (poly->outline_gc, &poly->outline_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		poly->width = g_value_get_uint (value);
+		poly->width_pixels = TRUE;
+		set_outline_gc_width (poly);
+#ifdef OLD_XFORM
+		recalc_bounds (poly);
+#else
+		foo_canvas_item_request_update (item);
+#endif
+		break;
+
+	case PROP_WIDTH_UNITS:
+		poly->width = fabs (g_value_get_double (value));
+		poly->width_pixels = FALSE;
+		set_outline_gc_width (poly);
+#ifdef OLD_XFORM
+		recalc_bounds (poly);
+#else
+		foo_canvas_item_request_update (item);
+#endif
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* Allocates a GdkColor structure filled with the specified pixel, and puts it into the specified
+ * value for returning it in the get_property method.
+ */
+static void
+get_color_value (FooCanvasPolygon *poly, gulong pixel, GValue *value)
+{
+	GdkColor *color;
+	GdkColormap *colormap;
+
+	color = g_new (GdkColor, 1);
+	color->pixel = pixel;
+
+	colormap = gtk_widget_get_colormap (GTK_WIDGET (poly));
+	gdk_rgb_find_color (colormap, color);
+	g_value_set_boxed (value, color);
+}
+
+static void
+foo_canvas_polygon_get_property (GObject              *object,
+				   guint                 param_id,
+				   GValue               *value,
+				   GParamSpec           *pspec)
+{
+	FooCanvasPolygon *poly;
+	FooCanvasPoints *points;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_POLYGON (object));
+
+	poly = FOO_CANVAS_POLYGON (object);
+
+	switch (param_id) {
+	case PROP_POINTS:
+		if (poly->num_points != 0) {
+			points = foo_canvas_points_new (poly->num_points);
+			memcpy (points->coords, poly->coords, 2 * poly->num_points * sizeof (double));
+			g_value_set_boxed (value, points);
+		} else
+			g_value_set_boxed (value, NULL);
+		break;
+
+	case PROP_FILL_COLOR_GDK:
+		get_color_value (poly, poly->fill_pixel, value);
+		break;
+
+	case PROP_OUTLINE_COLOR_GDK:
+		get_color_value (poly, poly->outline_pixel, value);
+		break;
+
+	case PROP_FILL_COLOR_RGBA:
+		g_value_set_uint (value, poly->fill_color);
+		break;
+
+	case PROP_OUTLINE_COLOR_RGBA:
+		g_value_set_uint (value, poly->outline_color);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		g_value_set_object (value, (GObject *) poly->fill_stipple);
+		break;
+
+	case PROP_OUTLINE_STIPPLE:
+		g_value_set_object (value, (GObject *) poly->outline_stipple);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+foo_canvas_polygon_update (FooCanvasItem *item,
+			     double i2w_dx, double i2w_dy,
+			     int flags)
+{
+	FooCanvasPolygon *poly;
+	double x1, y1, x2, y2;
+
+	poly = FOO_CANVAS_POLYGON (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+	set_outline_gc_width (poly);
+	set_gc_foreground (poly->fill_gc, poly->fill_pixel);
+	set_gc_foreground (poly->outline_gc, poly->outline_pixel);
+	set_stipple (poly->fill_gc, &poly->fill_stipple, poly->fill_stipple, TRUE);
+	set_stipple (poly->outline_gc, &poly->outline_stipple, poly->outline_stipple, TRUE);
+	
+	if (get_bounds_canvas (poly, &x1, &y1, &x2, &y2, i2w_dx, i2w_dy))
+		foo_canvas_update_bbox (item, x1, y1, x2, y2);
+}
+
+static void
+foo_canvas_polygon_realize (FooCanvasItem *item)
+{
+	FooCanvasPolygon *poly;
+
+	poly = FOO_CANVAS_POLYGON (item);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (item);
+
+	poly->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+	poly->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+/* FIXME FIXME FIXME Need to recalc pixel values, set colours, etc. */
+
+#ifdef OLD_XFORM
+	(* FOO_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+foo_canvas_polygon_unrealize (FooCanvasItem *item)
+{
+	FooCanvasPolygon *poly;
+
+	poly = FOO_CANVAS_POLYGON (item);
+
+	g_object_unref (poly->fill_gc);
+	poly->fill_gc = NULL;
+	g_object_unref (poly->outline_gc);
+	poly->outline_gc = NULL;
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (item);
+}
+
+/* Converts an array of world coordinates into an array of canvas pixel coordinates.  Takes in the
+ * item->world deltas and the drawable deltas.
+ */
+static void
+item_to_canvas (FooCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
+		double i2w_dx, double i2w_dy)
+{
+	int i;
+
+	for (i = 0; i < num_points; i++) {
+		foo_canvas_w2c (canvas,
+				  item_coords[i*2] + i2w_dx,
+				  item_coords[i*2+1] + i2w_dy,
+				  &canvas_coords->x, &canvas_coords->y);
+		canvas_coords++;
+	}
+}
+
+static void
+foo_canvas_polygon_draw (FooCanvasItem *item, GdkDrawable *drawable,
+			   GdkEventExpose *expose)
+{
+	FooCanvasPolygon *poly;
+	GdkPoint static_points[NUM_STATIC_POINTS];
+	GdkPoint *points;
+	double i2w_dx, i2w_dy;
+
+	poly = FOO_CANVAS_POLYGON (item);
+
+	if (poly->num_points == 0)
+		return;
+
+	/* Build array of canvas pixel coordinates */
+
+	if (poly->num_points <= NUM_STATIC_POINTS)
+		points = static_points;
+	else
+		points = g_new (GdkPoint, poly->num_points);
+
+	i2w_dx = 0.0;
+	i2w_dy = 0.0;
+	foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+
+	item_to_canvas (item->canvas,
+			poly->coords, points, poly->num_points,
+			i2w_dx, i2w_dy);
+
+	if (poly->fill_set) {
+		if (poly->fill_stipple)
+			foo_canvas_set_stipple_origin (item->canvas, poly->fill_gc);
+
+		gdk_draw_polygon (drawable, poly->fill_gc, TRUE, points, poly->num_points);
+	}
+
+	if (poly->outline_set) {
+		if (poly->outline_stipple)
+			foo_canvas_set_stipple_origin (item->canvas, poly->outline_gc);
+
+		gdk_draw_polygon (drawable, poly->outline_gc, FALSE, points, poly->num_points);
+	}
+
+	/* Done */
+
+	if (points != static_points)
+		g_free (points);
+}
+
+static double
+foo_canvas_polygon_point (FooCanvasItem *item, double x, double y,
+			    int cx, int cy, FooCanvasItem **actual_item)
+{
+	FooCanvasPolygon *poly;
+	double dist;
+	double width;
+
+	poly = FOO_CANVAS_POLYGON (item);
+
+	*actual_item = item;
+
+	dist = foo_canvas_polygon_to_point (poly->coords, poly->num_points, x, y);
+
+	if (poly->outline_set) {
+		if (poly->width_pixels)
+			width = poly->width / item->canvas->pixels_per_unit;
+		else
+			width = poly->width;
+
+		dist -= width / 2.0;
+
+		if (dist < 0.0)
+			dist = 0.0;
+	}
+
+	return dist;
+}
+
+static void
+foo_canvas_polygon_translate (FooCanvasItem *item, double dx, double dy)
+{
+        FooCanvasPolygon *poly;
+        int i;
+        double *coords;
+
+        poly = FOO_CANVAS_POLYGON (item);
+
+        for (i = 0, coords = poly->coords; i < poly->num_points; i++, coords += 2) {
+                coords[0] += dx;
+                coords[1] += dy;
+        }
+
+}
+
+static void
+foo_canvas_polygon_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	FooCanvasPolygon *poly;
+
+	g_return_if_fail (item != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_POLYGON (item));
+
+	poly = FOO_CANVAS_POLYGON (item);
+
+	if (poly->num_points == 0) {
+		*x1 = *y1 = *x2 = *y2 = 0.0;
+		return;
+	}
+
+	get_bounds (poly, x1, y1, x2, y2);
+}
diff --git a/libfoocanvas/foo-canvas-polygon.h b/libfoocanvas/foo-canvas-polygon.h
new file mode 100644
index 0000000..d56fd45
--- /dev/null
+++ b/libfoocanvas/foo-canvas-polygon.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Polygon item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_POLYGON_H
+#define FOO_CANVAS_POLYGON_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+G_BEGIN_DECLS
+
+
+/* Polygon item for the canvas.  A polygon is a bit different from rectangles and ellipses in that
+ * points inside it will always be considered "inside", even if the fill color is not set.  If you
+ * want to have a hollow polygon, use a line item instead.
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * points		FooCanvasPoints*	RW		Pointer to a FooCanvasPoints structure.
+ *								This can be created by a call to
+ *								foo_canvas_points_new() (in foo-canvas-util.h).
+ *								X coordinates are in the even indices of the
+ *								points->coords array, Y coordinates are in
+ *								the odd indices.
+ * fill_color		string			W		X color specification for fill color,
+ *								or NULL pointer for no color (transparent).
+ * fill_color_gdk	GdkColor*		RW		Allocated GdkColor for fill.
+ * outline_color	string			W		X color specification for outline color,
+ *								or NULL pointer for no color (transparent).
+ * outline_color_gdk	GdkColor*		RW		Allocated GdkColor for outline.
+ * fill_stipple		GdkBitmap*		RW		Stipple pattern for fill
+ * outline_stipple	GdkBitmap*		RW		Stipple pattern for outline
+ * width_pixels		uint			RW		Width of the outline in pixels.  The outline will
+ *								not be scaled when the canvas zoom factor is changed.
+ * width_units		double			RW		Width of the outline in canvas units.  The outline
+ *								will be scaled when the canvas zoom factor is changed.
+ */
+
+#define FOO_TYPE_CANVAS_POLYGON            (foo_canvas_polygon_get_type ())
+#define FOO_CANVAS_POLYGON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_POLYGON, FooCanvasPolygon))
+#define FOO_CANVAS_POLYGON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_POLYGON, FooCanvasPolygonClass))
+#define FOO_IS_CANVAS_POLYGON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_POLYGON))
+#define FOO_IS_CANVAS_POLYGON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_POLYGON))
+#define FOO_CANVAS_POLYGON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_POLYGON, FooCanvasPolygonClass))
+
+
+typedef struct _FooCanvasPolygon FooCanvasPolygon;
+typedef struct _FooCanvasPolygonClass FooCanvasPolygonClass;
+
+struct _FooCanvasPolygon {
+	FooCanvasItem item;
+
+	double *coords;			/* Array of coordinates for the polygon's points.  X coords
+					 * are in the even indices, Y coords are in the odd indices.
+					 */
+	GdkBitmap *fill_stipple;	/* Stipple for fill */
+	GdkBitmap *outline_stipple;	/* Stipple for outline */
+
+	GdkGC *fill_gc;			/* GC for filling */
+	GdkGC *outline_gc;		/* GC for outline */
+
+	gulong fill_pixel;		/* Color for fill */
+	gulong outline_pixel;		/* Color for outline */
+	double width;			/* Width of polygon's outline */
+
+	int num_points;			/* Number of points in the polygon */
+	guint fill_color;		/* Fill color, RGBA */
+	guint outline_color;		/* Outline color, RGBA */
+
+        guint32 fill_rgba;		/* RGBA color for filling */ /*AA*/
+	guint32 outline_rgba;		/* RGBA color for outline */ /*AA*/
+
+	guint fill_set : 1;		/* Is fill color set? */
+	guint outline_set : 1;		/* Is outline color set? */
+	guint width_pixels : 1;		/* Is outline width specified in pixels or units? */
+};
+
+struct _FooCanvasPolygonClass {
+	FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_polygon_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-rect-ellipse.c b/libfoocanvas/foo-canvas-rect-ellipse.c
new file mode 100644
index 0000000..1c18949
--- /dev/null
+++ b/libfoocanvas/foo-canvas-rect-ellipse.c
@@ -0,0 +1,1489 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Rectangle and ellipse item types for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include "foo-canvas-rect-ellipse.h"
+#include "foo-canvas-util.h"
+#include <string.h>
+
+#ifdef HAVE_RENDER
+#include <gdk/gdkx.h>
+#include <X11/extensions/Xrender.h>
+#endif
+
+/* Base class for rectangle and ellipse item types */
+
+#define noVERBOSE
+
+enum {
+	PROP_0,
+	PROP_X1,
+	PROP_Y1,
+	PROP_X2,
+	PROP_Y2,
+	PROP_FILL_COLOR,
+	PROP_FILL_COLOR_GDK,
+	PROP_FILL_COLOR_RGBA,
+	PROP_OUTLINE_COLOR,
+	PROP_OUTLINE_COLOR_GDK,
+	PROP_OUTLINE_COLOR_RGBA,
+	PROP_FILL_STIPPLE,
+	PROP_OUTLINE_STIPPLE,
+	PROP_WIDTH_PIXELS,
+	PROP_WIDTH_UNITS
+};
+
+
+static void foo_canvas_re_class_init (FooCanvasREClass *klass);
+static void foo_canvas_re_init       (FooCanvasRE      *re);
+static void foo_canvas_re_destroy    (GtkObject          *object);
+static void foo_canvas_re_set_property (GObject              *object,
+					  guint                 param_id,
+					  const GValue         *value,
+					  GParamSpec           *pspec);
+static void foo_canvas_re_get_property (GObject              *object,
+					  guint                 param_id,
+					  GValue               *value,
+					  GParamSpec           *pspec);
+
+static void foo_canvas_re_update_shared (FooCanvasItem *item,
+					   double i2w_dx, double i2w_dy, int flags);
+static void foo_canvas_re_realize     (FooCanvasItem *item);
+static void foo_canvas_re_unrealize   (FooCanvasItem *item);
+static void foo_canvas_re_bounds      (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+static void foo_canvas_re_translate   (FooCanvasItem *item, double dx, double dy);
+static void foo_canvas_rect_update      (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+static void foo_canvas_ellipse_update      (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+
+typedef struct {
+  /*< public >*/
+  int x0, y0, x1, y1;
+}  Rect;
+
+static Rect make_rect (int x0, int y0, int x1, int y1);
+static void  diff_rects (Rect r1, Rect r2, int *count, Rect result[4]);
+
+static FooCanvasItemClass *re_parent_class;
+static FooCanvasREClass *rect_parent_class;
+
+
+GType
+foo_canvas_re_get_type (void)
+{
+	static GType re_type = 0;
+
+	if (!re_type) {
+		GTypeInfo re_info = {
+		  sizeof (FooCanvasREClass),
+		  (GBaseInitFunc) NULL,
+		  (GBaseFinalizeFunc) NULL,
+		  (GClassInitFunc) foo_canvas_re_class_init,
+		  NULL,           /* class_finalize */
+		  NULL,           /* class_data */
+		  sizeof (FooCanvasRE),
+		  0,              /* n_preallocs */
+		  (GInstanceInitFunc) foo_canvas_re_init
+		};
+
+		re_type = g_type_register_static (foo_canvas_item_get_type (),
+						  "FooCanvasRE",
+						  &re_info,
+						  0);
+	}
+
+	return re_type;
+}
+
+static void
+foo_canvas_re_class_init (FooCanvasREClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	FooCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (FooCanvasItemClass *) klass;
+
+	re_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_re_set_property;
+	gobject_class->get_property = foo_canvas_re_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X1,
+                 g_param_spec_double ("x1", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y1,
+                 g_param_spec_double ("y1", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X2,
+                 g_param_spec_double ("x2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y2,
+                 g_param_spec_double ("y2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_string ("fill-color", NULL, NULL,
+                                      NULL,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_GDK,
+                 g_param_spec_boxed ("fill-color-gdk", NULL, NULL,
+				     GDK_TYPE_COLOR,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_RGBA,
+                 g_param_spec_uint ("fill-color-rgba", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_STIPPLE,
+                 g_param_spec_object ("fill-stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_COLOR,
+                 g_param_spec_string ("outline-color", NULL, NULL,
+                                      NULL,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_COLOR_GDK,
+                 g_param_spec_boxed ("outline-color-gdk", NULL, NULL,
+				     GDK_TYPE_COLOR,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_COLOR_RGBA,
+                 g_param_spec_uint ("outline-color-rgba", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_STIPPLE,
+                 g_param_spec_object ("outline-stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_PIXELS,
+                 g_param_spec_uint ("width-pixels", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_UNITS,
+                 g_param_spec_double ("width-units", NULL, NULL,
+				      0.0, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+
+	object_class->destroy = foo_canvas_re_destroy;
+
+	item_class->realize = foo_canvas_re_realize;
+	item_class->unrealize = foo_canvas_re_unrealize;
+	item_class->translate = foo_canvas_re_translate;
+	item_class->bounds = foo_canvas_re_bounds;
+}
+
+static void
+foo_canvas_re_init (FooCanvasRE *re)
+{
+	re->x1 = 0.0;
+	re->y1 = 0.0;
+	re->x2 = 0.0;
+	re->y2 = 0.0;
+	re->width = 0.0;
+}
+
+static void
+foo_canvas_re_destroy (GtkObject *object)
+{
+	FooCanvasRE *re;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_RE (object));
+
+	re = FOO_CANVAS_RE (object);
+
+	/* remember, destroy can be run multiple times! */
+
+	if (re->fill_stipple)
+		g_object_unref (re->fill_stipple);
+	re->fill_stipple = NULL;
+
+	if (re->outline_stipple)
+		g_object_unref (re->outline_stipple);
+	re->outline_stipple = NULL;
+
+	if (GTK_OBJECT_CLASS (re_parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (re_parent_class)->destroy) (object);
+}
+
+static void get_bounds (FooCanvasRE *re, double *px1, double *py1, double *px2, double *py2)
+{
+	FooCanvasItem *item;
+	double x1, y1, x2, y2;
+	int cx1, cy1, cx2, cy2;
+	double hwidth;
+
+#ifdef VERBOSE
+	g_print ("re get_bounds\n");
+#endif
+	item = FOO_CANVAS_ITEM (re);
+
+	if (re->width_pixels)
+		hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0;
+	else
+		hwidth = re->width / 2.0;
+
+	x1 = re->x1;
+	y1 = re->y1;
+	x2 = re->x2;
+	y2 = re->y2;
+
+	foo_canvas_item_i2w (item, &x1, &y1);
+	foo_canvas_item_i2w (item, &x2, &y2);
+	foo_canvas_w2c (item->canvas, x1 - hwidth, y1 - hwidth, &cx1, &cy1);
+	foo_canvas_w2c (item->canvas, x2 + hwidth, y2 + hwidth, &cx2, &cy2);
+	*px1 = cx1;
+	*py1 = cy1;
+	*px2 = cx2;
+	*py2 = cy2;
+
+	/* Some safety fudging */
+
+	*px1 -= 2;
+	*py1 -= 2;
+	*px2 += 2;
+	*py2 += 2;
+}
+
+/* Convenience function to set a GC's foreground color to the specified pixel value */
+static void
+set_gc_foreground (GdkGC *gc, gulong pixel)
+{
+	GdkColor c;
+
+	if (!gc)
+		return;
+
+	c.pixel = pixel;
+	gdk_gc_set_foreground (gc, &c);
+}
+
+/* Sets the stipple pattern for the specified gc */
+static void
+set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure)
+{
+	if (*internal_stipple && !reconfigure)
+		g_object_unref (*internal_stipple);
+
+	*internal_stipple = stipple;
+	if (stipple && !reconfigure)
+		g_object_ref (stipple);
+
+	if (gc) {
+		if (stipple) {
+			gdk_gc_set_stipple (gc, stipple);
+			gdk_gc_set_fill (gc, GDK_STIPPLED);
+		} else
+			gdk_gc_set_fill (gc, GDK_SOLID);
+	}
+}
+
+/* Recalculate the outline width of the rectangle/ellipse and set it in its GC */
+static void
+set_outline_gc_width (FooCanvasRE *re)
+{
+	int width;
+
+	if (!re->outline_gc)
+		return;
+
+	if (re->width_pixels)
+		width = (int) re->width;
+	else
+		width = (int) (re->width * re->item.canvas->pixels_per_unit + 0.5);
+
+	gdk_gc_set_line_attributes (re->outline_gc, width,
+				    GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
+}
+
+static void
+foo_canvas_re_set_fill (FooCanvasRE *re, gboolean fill_set)
+{
+	if (re->fill_set != fill_set) {
+		re->fill_set = fill_set;
+		foo_canvas_item_request_update (FOO_CANVAS_ITEM (re));
+	}
+}
+
+static void
+foo_canvas_re_set_outline (FooCanvasRE *re, gboolean outline_set)
+{
+	if (re->outline_set != outline_set) {
+		re->outline_set = outline_set;
+		foo_canvas_item_request_update (FOO_CANVAS_ITEM (re));
+	}
+}
+
+static void
+foo_canvas_re_set_property (GObject              *object,
+			      guint                 param_id,
+			      const GValue         *value,
+			      GParamSpec           *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasRE *re;
+	GdkColor color = { 0, 0, 0, 0, };
+	GdkColor *pcolor;
+	int have_pixel;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_RE (object));
+
+	item = FOO_CANVAS_ITEM (object);
+	re = FOO_CANVAS_RE (object);
+	have_pixel = FALSE;
+
+	switch (param_id) {
+	case PROP_X1:
+		re->x1 = g_value_get_double (value);
+
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y1:
+		re->y1 = g_value_get_double (value);
+
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_X2:
+		re->x2 = g_value_get_double (value);
+
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y2:
+		re->y2 = g_value_get_double (value);
+
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_FILL_COLOR:
+	case PROP_FILL_COLOR_GDK:
+	case PROP_FILL_COLOR_RGBA:
+		switch (param_id) {
+		case PROP_FILL_COLOR:
+			if (g_value_get_string (value) &&
+			    gdk_color_parse (g_value_get_string (value), &color))
+				foo_canvas_re_set_fill (re, TRUE);
+			else
+				foo_canvas_re_set_fill (re, FALSE);
+
+			re->fill_color = ((color.red & 0xff00) << 16 |
+					  (color.green & 0xff00) << 8 |
+					  (color.blue & 0xff00) |
+					  0xff);
+			break;
+
+		case PROP_FILL_COLOR_GDK:
+			pcolor = g_value_get_boxed (value);
+			foo_canvas_re_set_fill (re, pcolor != NULL);
+
+			if (pcolor) {
+				GdkColormap *colormap;
+
+				color = *pcolor;
+				colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+				gdk_rgb_find_color (colormap, &color);
+				have_pixel = TRUE;
+			}
+
+			re->fill_color = ((color.red & 0xff00) << 16 |
+					  (color.green & 0xff00) << 8 |
+					  (color.blue & 0xff00) |
+					  0xff);
+			break;
+
+		case PROP_FILL_COLOR_RGBA:
+			foo_canvas_re_set_fill (re, TRUE);
+			re->fill_color = g_value_get_uint (value);
+			break;
+		}
+#ifdef VERBOSE
+		g_print ("re fill color = %08x\n", re->fill_color);
+#endif
+		if (have_pixel)
+			re->fill_pixel = color.pixel;
+		else
+			re->fill_pixel = foo_canvas_get_color_pixel (item->canvas, re->fill_color);
+
+		set_gc_foreground (re->fill_gc, re->fill_pixel);
+
+		foo_canvas_item_request_redraw (item);		
+		break;
+
+	case PROP_OUTLINE_COLOR:
+	case PROP_OUTLINE_COLOR_GDK:
+	case PROP_OUTLINE_COLOR_RGBA:
+		switch (param_id) {
+		case PROP_OUTLINE_COLOR:
+			if (g_value_get_string (value) &&
+			    gdk_color_parse (g_value_get_string (value), &color))
+				foo_canvas_re_set_outline (re, TRUE);
+			else
+				foo_canvas_re_set_outline (re, FALSE);
+
+			re->outline_color = ((color.red & 0xff00) << 16 |
+					     (color.green & 0xff00) << 8 |
+					     (color.blue & 0xff00) |
+					     0xff);
+			break;
+
+		case PROP_OUTLINE_COLOR_GDK:
+			pcolor = g_value_get_boxed (value);
+			foo_canvas_re_set_outline (re, pcolor != NULL);
+
+			if (pcolor) {
+				GdkColormap *colormap;
+
+				color = *pcolor;
+				colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+				gdk_rgb_find_color (colormap, &color);
+
+				have_pixel = TRUE;
+			}
+
+			re->outline_color = ((color.red & 0xff00) << 16 |
+					     (color.green & 0xff00) << 8 |
+					     (color.blue & 0xff00) |
+					     0xff);
+			break;
+
+		case PROP_OUTLINE_COLOR_RGBA:
+			foo_canvas_re_set_outline (re, TRUE);
+			re->outline_color = g_value_get_uint (value);
+			break;
+		}
+#ifdef VERBOSE
+		g_print ("re outline color %x %x %x\n", color.red, color.green, color.blue);
+#endif
+		if (have_pixel)
+			re->outline_pixel = color.pixel;
+		else
+			re->outline_pixel = foo_canvas_get_color_pixel (item->canvas,
+									  re->outline_color);
+
+		set_gc_foreground (re->outline_gc, re->outline_pixel);
+
+		foo_canvas_item_request_redraw (item);		
+		break;
+
+	case PROP_FILL_STIPPLE:
+	        set_stipple (re->fill_gc, &re->fill_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
+
+		break;
+
+	case PROP_OUTLINE_STIPPLE:
+	        set_stipple (re->outline_gc, &re->outline_stipple, (GdkBitmap *) g_value_get_object (value), FALSE);
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		re->width = g_value_get_uint (value);
+		re->width_pixels = TRUE;
+		set_outline_gc_width (re);
+
+		foo_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_UNITS:
+		re->width = fabs (g_value_get_double (value));
+		re->width_pixels = FALSE;
+		set_outline_gc_width (re);
+
+		foo_canvas_item_request_update (item);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* Allocates a GdkColor structure filled with the specified pixel, and puts it into the specified
+ * value for returning it in the get_property method.
+ */
+static void
+get_color_value (FooCanvasRE *re, gulong pixel, GValue *value)
+{
+	GdkColor color;
+	FooCanvasItem *item = (FooCanvasItem *) re;
+	GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+
+	gdk_colormap_query_color (colormap, pixel, &color);
+	g_value_set_boxed (value, &color);
+}
+
+static void
+foo_canvas_re_get_property (GObject              *object,
+			      guint                 param_id,
+			      GValue               *value,
+			      GParamSpec           *pspec)
+{
+	FooCanvasRE *re;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_RE (object));
+
+	re = FOO_CANVAS_RE (object);
+
+	switch (param_id) {
+	case PROP_X1:
+		g_value_set_double (value,  re->x1);
+		break;
+
+	case PROP_Y1:
+		g_value_set_double (value,  re->y1);
+		break;
+
+	case PROP_X2:
+		g_value_set_double (value,  re->x2);
+		break;
+
+	case PROP_Y2:
+		g_value_set_double (value,  re->y2);
+		break;
+
+	case PROP_FILL_COLOR_GDK:
+		get_color_value (re, re->fill_pixel, value);
+		break;
+
+	case PROP_OUTLINE_COLOR_GDK:
+		get_color_value (re, re->outline_pixel, value);
+		break;
+
+	case PROP_FILL_COLOR_RGBA:
+		g_value_set_uint (value,  re->fill_color);
+		break;
+
+	case PROP_OUTLINE_COLOR_RGBA:
+		g_value_set_uint (value,  re->outline_color);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		g_value_set_object (value,  (GObject *) re->fill_stipple);
+		break;
+
+	case PROP_OUTLINE_STIPPLE:
+		g_value_set_object (value,  (GObject *) re->outline_stipple);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+set_colors_and_stipples (FooCanvasRE *re)
+{
+	set_gc_foreground (re->fill_gc, re->fill_pixel);
+	set_gc_foreground (re->outline_gc, re->outline_pixel);
+	set_stipple (re->fill_gc, &re->fill_stipple, re->fill_stipple, TRUE);
+	set_stipple (re->outline_gc, &re->outline_stipple, re->outline_stipple, TRUE);
+	set_outline_gc_width (re);
+}
+
+static void
+foo_canvas_re_update_shared (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	FooCanvasRE *re;
+
+#ifdef VERBOSE
+	g_print ("foo_canvas_re_update_shared\n");
+#endif
+	re = FOO_CANVAS_RE (item);
+
+	if (re_parent_class->update)
+		(* re_parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+	set_colors_and_stipples (re);
+
+#ifdef OLD_XFORM
+	recalc_bounds (re);
+#endif
+}
+
+static void
+foo_canvas_re_realize (FooCanvasItem *item)
+{
+	FooCanvasRE *re;
+
+#ifdef VERBOSE
+	g_print ("foo_canvas_re_realize\n");
+#endif
+	re = FOO_CANVAS_RE (item);
+
+	if (re_parent_class->realize)
+		(* re_parent_class->realize) (item);
+
+	re->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+	re->fill_pixel = foo_canvas_get_color_pixel (item->canvas, re->fill_color);
+	re->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+	re->outline_pixel = foo_canvas_get_color_pixel (item->canvas, re->outline_color);
+	set_colors_and_stipples (re);
+
+#ifdef OLD_XFORM
+	(* FOO_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+foo_canvas_re_unrealize (FooCanvasItem *item)
+{
+	FooCanvasRE *re;
+
+	re = FOO_CANVAS_RE (item);
+
+	g_object_unref (re->fill_gc);
+	re->fill_gc = NULL;
+	g_object_unref (re->outline_gc);
+	re->outline_gc = NULL;
+
+	if (re_parent_class->unrealize)
+		(* re_parent_class->unrealize) (item);
+}
+
+static void
+foo_canvas_re_translate (FooCanvasItem *item, double dx, double dy)
+{
+	FooCanvasRE *re;
+
+#ifdef VERBOSE
+	g_print ("foo_canvas_re_translate\n");
+#endif
+	re = FOO_CANVAS_RE (item);
+
+	re->x1 += dx;
+	re->y1 += dy;
+	re->x2 += dx;
+	re->y2 += dy;
+}
+
+
+static void
+foo_canvas_re_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	FooCanvasRE *re;
+	double hwidth;
+
+#ifdef VERBOSE
+	g_print ("foo_canvas_re_bounds\n");
+#endif
+	re = FOO_CANVAS_RE (item);
+
+	if (re->width_pixels)
+		hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0;
+	else
+		hwidth = re->width / 2.0;
+
+	*x1 = re->x1 - hwidth;
+	*y1 = re->y1 - hwidth;
+	*x2 = re->x2 + hwidth;
+	*y2 = re->y2 + hwidth;
+}
+
+/* Rectangle item */
+
+
+static void foo_canvas_rect_class_init (FooCanvasRectClass *klass);
+static void foo_canvas_rect_init (FooCanvasRect *rect);
+static void foo_canvas_rect_finalize (GObject *object);
+static void foo_canvas_rect_realize  (FooCanvasItem *item);
+
+static void   foo_canvas_rect_draw   (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+static double foo_canvas_rect_point  (FooCanvasItem *item, double x, double y, int cx, int cy,
+				        FooCanvasItem **actual_item);
+
+struct _FooCanvasRectPrivate {
+	Rect last_update_rect;
+	Rect last_outline_update_rect;
+	int last_outline_update_width;
+
+#ifdef HAVE_RENDER
+	gboolean use_render;
+	XRenderPictFormat *format;
+#endif
+};
+
+GType
+foo_canvas_rect_get_type (void)
+{
+	static GType rect_type = 0;
+
+	if (!rect_type) {
+		GTypeInfo rect_info = {
+		  sizeof (FooCanvasRectClass),
+		  (GBaseInitFunc) NULL,
+		  (GBaseFinalizeFunc) NULL,
+		  (GClassInitFunc) foo_canvas_rect_class_init,
+		  NULL,           /* class_finalize */
+		  NULL,           /* class_data */
+		  sizeof (FooCanvasRect),
+		  0,              /* n_preallocs */
+		  (GInstanceInitFunc) foo_canvas_rect_init
+		};
+
+		rect_type = g_type_register_static (foo_canvas_re_get_type (),
+						    "FooCanvasRect",
+						    &rect_info,
+						    0);
+	}
+
+	return rect_type;
+}
+
+static void
+foo_canvas_rect_class_init (FooCanvasRectClass *klass)
+{
+	FooCanvasItemClass *item_class;
+
+	rect_parent_class = g_type_class_peek_parent (klass);
+
+	item_class = (FooCanvasItemClass *) klass;
+
+	item_class->draw = foo_canvas_rect_draw;
+	item_class->point = foo_canvas_rect_point;
+	item_class->update = foo_canvas_rect_update;
+	item_class->realize = foo_canvas_rect_realize;
+
+	G_OBJECT_CLASS (klass)->finalize = foo_canvas_rect_finalize;
+	
+}
+
+static void
+foo_canvas_rect_init (FooCanvasRect *rect)
+{
+	rect->priv = g_new0 (FooCanvasRectPrivate, 1);
+}
+
+static void
+foo_canvas_rect_finalize (GObject *object)
+{
+	FooCanvasRect *rect = FOO_CANVAS_RECT (object);
+
+	if (rect->priv) {
+		g_free (rect->priv);
+	}
+
+	G_OBJECT_CLASS (rect_parent_class)->finalize (object);
+}
+
+static void
+foo_canvas_rect_realize  (FooCanvasItem *item)
+{
+#ifdef HAVE_RENDER
+	FooCanvasRectPrivate *priv;
+	int event_base, error_base;
+	Display *dpy;
+
+	priv = FOO_CANVAS_RECT (item)->priv;
+
+	dpy = gdk_x11_drawable_get_xdisplay (GTK_WIDGET (item->canvas)->window);
+	priv->use_render = XRenderQueryExtension (dpy, &event_base, &error_base);
+
+	if (priv->use_render) {
+		GdkVisual *gdk_visual;
+		Visual *visual;
+
+		gdk_visual = gtk_widget_get_visual (GTK_WIDGET (item->canvas));
+		visual = gdk_x11_visual_get_xvisual (gdk_visual);
+
+		priv->format = XRenderFindVisualFormat (dpy, visual);
+	}
+#endif
+	
+	if (FOO_CANVAS_ITEM_CLASS (rect_parent_class)->realize) {
+		(* FOO_CANVAS_ITEM_CLASS (rect_parent_class)->realize) (item);
+	}
+}
+
+
+static void
+render_rect_alpha (FooCanvasRect *rect,
+		   GdkDrawable *drawable,
+		   int x, int y,
+		   int width, int height,
+		   guint32 rgba)
+{
+	GdkPixbuf *pixbuf;
+	guchar *data;
+	int rowstride, i;
+	guchar r, g, b, a;
+	FooCanvasRectPrivate *priv;
+
+	if (width <= 0 || height <= 0 ) {
+		return;
+	}
+	
+	priv = rect->priv;
+
+	r = (rgba >> 24) & 0xff;
+	g = (rgba >> 16) & 0xff;
+	b = (rgba >> 8) & 0xff;
+	a = (rgba >> 0) & 0xff;
+
+#ifdef HAVE_RENDER
+	/* Every visual is not guaranteed to have a matching
+	 * XRenderPictFormat. So make sure that format is not null before
+	 * trying to render using Xrender calls.
+	 */
+	if (priv->use_render && (priv->format != NULL)) {
+		GdkDrawable *real_drawable;
+		int x_offset, y_offset;
+
+		Display *dpy;
+		Picture  pict;
+		XRenderPictureAttributes attributes;
+		XRenderColor color;
+
+		gdk_window_get_internal_paint_info (drawable, &real_drawable,
+						    &x_offset, &y_offset);
+
+		dpy = gdk_x11_drawable_get_xdisplay (real_drawable);
+
+		pict = XRenderCreatePicture (dpy,
+					     gdk_x11_drawable_get_xid (real_drawable),
+					     priv->format,
+					     0,
+					     &attributes);
+
+
+		/* Convert to premultiplied alpha: */
+		r = r * a / 255;
+		g = g * a / 255;
+		b = b * a / 255;
+		
+		color.red = (r << 8) + r;
+		color.green = (g << 8) + g;
+		color.blue = (b << 8) + b;
+		color.alpha = (a << 8) + a;
+		
+		XRenderFillRectangle (dpy,
+				      PictOpOver,
+				      pict,
+				      &color,
+				      x - x_offset, y - y_offset,
+				      width, height);
+		
+		XRenderFreePicture (dpy, pict);
+
+		return;
+	}
+#endif
+	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+	data = gdk_pixbuf_get_pixels (pixbuf);
+	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	
+	r = (rgba >> 24) & 0xff;
+	g = (rgba >> 16) & 0xff;
+	b = (rgba >> 8) & 0xff;
+	a = (rgba >> 0) & 0xff;
+	
+	for (i = 0; i < width*4; ) {
+		data[i++] = r;
+		data[i++] = g;
+		data[i++] = b;
+		data[i++] = a;
+	}
+	
+	for (i = 1; i < height; i++) {
+		memcpy (data + i*rowstride, data, width*4);
+	}
+	
+	gdk_draw_pixbuf (drawable, NULL, pixbuf,
+			 0, 0, x, y, width, height,
+			 GDK_RGB_DITHER_NONE, 0, 0);
+	g_object_unref (pixbuf);
+}
+
+
+static void
+foo_canvas_rect_draw (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose)
+{
+	FooCanvasRE *re;
+	double x1, y1, x2, y2;
+	int cx1, cy1, cx2, cy2;
+	double i2w_dx, i2w_dy;
+
+	re = FOO_CANVAS_RE (item);
+
+	/* Get canvas pixel coordinates */
+	i2w_dx = 0.0;
+	i2w_dy = 0.0;
+	foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+	
+	x1 = re->x1 + i2w_dx;
+	y1 = re->y1 + i2w_dy;
+	x2 = re->x2 + i2w_dx;
+	y2 = re->y2 + i2w_dy;
+
+	foo_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+	foo_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
+	
+	if (re->fill_set) {
+		if ((re->fill_color & 0xff) != 255) {
+			GdkRectangle *rectangles;
+			gint i, n_rectangles;
+			GdkRectangle draw_rect;
+			GdkRectangle part;
+
+			draw_rect.x = cx1;
+			draw_rect.y = cy1;
+			draw_rect.width = cx2 - cx1 + 1;
+			draw_rect.height = cy2 - cy1 + 1;
+			
+			/* For alpha mode, only render the parts of the region
+			   that are actually exposed */
+			gdk_region_get_rectangles (expose->region,
+						   &rectangles,
+						   &n_rectangles);
+
+			for (i = 0; i < n_rectangles; i++) {
+				if (gdk_rectangle_intersect (&rectangles[i],
+							     &draw_rect,
+							     &part)) {
+					render_rect_alpha (FOO_CANVAS_RECT (item),
+							   drawable,
+							   part.x, part.y,
+							   part.width, part.height,
+							   re->fill_color);
+				}
+			}
+			
+			g_free (rectangles);
+		} else {
+			if (re->fill_stipple)
+				foo_canvas_set_stipple_origin (item->canvas, re->fill_gc);
+
+			gdk_draw_rectangle (drawable,
+					    re->fill_gc,
+					    TRUE,
+					    cx1, cy1,
+					    cx2 - cx1 + 1,
+					    cy2 - cy1 + 1);
+		}
+	}
+
+	if (re->outline_set) {
+		if (re->outline_stipple)
+			foo_canvas_set_stipple_origin (item->canvas, re->outline_gc);
+
+		gdk_draw_rectangle (drawable,
+				    re->outline_gc,
+				    FALSE,
+				    cx1,
+				    cy1,
+				    cx2 - cx1,
+				    cy2 - cy1);
+	}
+}
+
+static double
+foo_canvas_rect_point (FooCanvasItem *item, double x, double y, int cx, int cy, FooCanvasItem **actual_item)
+{
+	FooCanvasRE *re;
+	double x1, y1, x2, y2;
+	double hwidth;
+	double dx, dy;
+	double tmp;
+
+#ifdef VERBOSE
+	g_print ("foo_canvas_rect_point\n");
+#endif
+	re = FOO_CANVAS_RE (item);
+
+	*actual_item = item;
+
+	/* Find the bounds for the rectangle plus its outline width */
+
+	x1 = re->x1;
+	y1 = re->y1;
+	x2 = re->x2;
+	y2 = re->y2;
+
+	if (re->outline_set) {
+		if (re->width_pixels)
+			hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0;
+		else
+			hwidth = re->width / 2.0;
+
+		x1 -= hwidth;
+		y1 -= hwidth;
+		x2 += hwidth;
+		y2 += hwidth;
+	} else
+		hwidth = 0.0;
+
+	/* Is point inside rectangle (which can be hollow if it has no fill set)? */
+
+	if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
+		if (re->fill_set || !re->outline_set)
+			return 0.0;
+
+		dx = x - x1;
+		tmp = x2 - x;
+		if (tmp < dx)
+			dx = tmp;
+
+		dy = y - y1;
+		tmp = y2 - y;
+		if (tmp < dy)
+			dy = tmp;
+
+		if (dy < dx)
+			dx = dy;
+
+		dx -= 2.0 * hwidth;
+
+		if (dx < 0.0)
+			return 0.0;
+		else
+			return dx;
+	}
+
+	/* Point is outside rectangle */
+
+	if (x < x1)
+		dx = x1 - x;
+	else if (x > x2)
+		dx = x - x2;
+	else
+		dx = 0.0;
+
+	if (y < y1)
+		dy = y1 - y;
+	else if (y > y2)
+		dy = y - y2;
+	else
+		dy = 0.0;
+
+	return sqrt (dx * dx + dy * dy);
+}
+
+static void
+request_redraw_borders (FooCanvas *canvas,
+			Rect     *update_rect,
+			int     width)
+{
+	foo_canvas_request_redraw (canvas,
+				   update_rect->x0, update_rect->y0,
+				   update_rect->x1, update_rect->y0 + width);
+	foo_canvas_request_redraw (canvas,
+				   update_rect->x0, update_rect->y1-width,
+				   update_rect->x1, update_rect->y1);
+	foo_canvas_request_redraw (canvas,
+				   update_rect->x0,       update_rect->y0,
+				   update_rect->x0+width, update_rect->y1);
+	foo_canvas_request_redraw (canvas,
+				   update_rect->x1-width, update_rect->y0,
+				   update_rect->x1,       update_rect->y1);
+}
+
+
+static void
+foo_canvas_rect_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, gint flags)
+{
+	FooCanvasRE *re;
+	double x1, y1, x2, y2;
+	int cx1, cy1, cx2, cy2;
+	int repaint_rects_count, i;
+	int width_pixels;
+	int width_lt, width_rb;
+	Rect update_rect, repaint_rects[4];
+	FooCanvasRectPrivate *priv;
+
+	foo_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags);
+
+	re = FOO_CANVAS_RE (item);
+	priv = FOO_CANVAS_RECT (item)->priv;
+	
+	x1 = re->x1 + i2w_dx;
+	y1 = re->y1 + i2w_dy;
+	x2 = re->x2 + i2w_dx;
+	y2 = re->y2 + i2w_dy;
+
+	foo_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+	foo_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
+
+	update_rect = make_rect (cx1, cy1, cx2+1, cy2+1);
+#if 0
+	foo_canvas_request_redraw (item->canvas,
+				   update_rect.x0, update_rect.y0,
+				   update_rect.x1, update_rect.y1);
+	foo_canvas_request_redraw (item->canvas,
+				   priv->last_update_rect.x0, priv->last_update_rect.y0,
+				   priv->last_update_rect.x1, priv->last_update_rect.y1);
+#else
+	diff_rects (update_rect, priv->last_update_rect,
+		    &repaint_rects_count, repaint_rects);
+	for (i = 0; i < repaint_rects_count; i++) {
+		foo_canvas_request_redraw (item->canvas,
+					   repaint_rects[i].x0, repaint_rects[i].y0,
+					   repaint_rects[i].x1, repaint_rects[i].y1);
+	}
+#endif
+	priv->last_update_rect = update_rect;
+
+	if (re->outline_set) {
+		/* Outline and bounding box */
+		if (re->width_pixels)
+			width_pixels = (int) re->width;
+		else
+			width_pixels = (int) floor (re->width * re->item.canvas->pixels_per_unit + 0.5);
+
+		width_lt = width_pixels / 2;
+		width_rb = (width_pixels + 1) / 2;
+		
+		cx1 -= width_lt;
+		cy1 -= width_lt;
+		cx2 += width_rb;
+		cy2 += width_rb;
+
+		update_rect = make_rect (cx1, cy1, cx2, cy2);
+		request_redraw_borders (item->canvas, &update_rect,
+					(width_lt + width_rb));
+		request_redraw_borders (item->canvas, &priv->last_outline_update_rect,
+					priv->last_outline_update_width);
+		priv->last_outline_update_rect = update_rect;
+		priv->last_outline_update_width = width_lt + width_rb;
+		
+		item->x1 = cx1;
+		item->y1 = cy1;
+		item->x2 = cx2+1;
+		item->y2 = cy2+1;
+	} else {
+		item->x1 = cx1;
+		item->y1 = cy1;
+		item->x2 = cx2+1;
+		item->y2 = cy2+1;
+	}
+}
+
+/* Ellipse item */
+
+
+static void foo_canvas_ellipse_class_init (FooCanvasEllipseClass *klass);
+
+static void   foo_canvas_ellipse_draw   (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+static double foo_canvas_ellipse_point  (FooCanvasItem *item, double x, double y, int cx, int cy,
+					   FooCanvasItem **actual_item);
+
+
+GType
+foo_canvas_ellipse_get_type (void)
+{
+	static GType ellipse_type = 0;
+
+	if (!ellipse_type) {
+		GTypeInfo ellipse_info = {
+		  sizeof (FooCanvasEllipseClass),
+		  (GBaseInitFunc) NULL,
+		  (GBaseFinalizeFunc) NULL,
+		  (GClassInitFunc) foo_canvas_ellipse_class_init,
+		  NULL,           /* class_finalize */
+		  NULL,           /* class_data */
+		  sizeof (FooCanvasEllipse),
+		  0,              /* n_preallocs */
+		  (GInstanceInitFunc) NULL
+
+		};
+
+		ellipse_type = g_type_register_static (foo_canvas_re_get_type (),
+						       "FooCanvasEllipse",
+						       &ellipse_info,
+						       0);
+	}
+
+	return ellipse_type;
+}
+
+static void
+foo_canvas_ellipse_class_init (FooCanvasEllipseClass *klass)
+{
+	FooCanvasItemClass *item_class;
+
+	item_class = (FooCanvasItemClass *) klass;
+
+	item_class->draw = foo_canvas_ellipse_draw;
+	item_class->point = foo_canvas_ellipse_point;
+	item_class->update = foo_canvas_ellipse_update;
+}
+
+static void
+foo_canvas_ellipse_draw (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose)
+{
+	FooCanvasRE *re;
+	int x1, y1, x2, y2;
+	double i2w_dx, i2w_dy;
+
+	re = FOO_CANVAS_RE (item);
+
+	/* Get canvas pixel coordinates */
+
+	i2w_dx = 0.0;
+	i2w_dy = 0.0;
+	foo_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+	
+	foo_canvas_w2c (item->canvas,
+			  re->x1 + i2w_dx,
+			  re->y1 + i2w_dy,
+			  &x1, &y1);
+	foo_canvas_w2c (item->canvas,
+			  re->x2 + i2w_dx,
+			  re->y2 + i2w_dy,
+			  &x2, &y2);
+
+	if (re->fill_set) {
+		if (re->fill_stipple)
+			foo_canvas_set_stipple_origin (item->canvas, re->fill_gc);
+
+		gdk_draw_arc (drawable,
+			      re->fill_gc,
+			      TRUE,
+			      x1,
+			      y1,
+			      x2 - x1,
+			      y2 - y1,
+			      0 * 64,
+			      360 * 64);
+	}
+
+	if (re->outline_set) {
+		if (re->outline_stipple)
+			foo_canvas_set_stipple_origin (item->canvas, re->outline_gc);
+
+		gdk_draw_arc (drawable,
+			      re->outline_gc,
+			      FALSE,
+			      x1,
+			      y1,
+			      x2 - x1,
+			      y2 - y1,
+			      0 * 64,
+			      360 * 64);
+	}
+}
+
+static double
+foo_canvas_ellipse_point (FooCanvasItem *item, double x, double y, int cx, int cy, FooCanvasItem **actual_item)
+{
+	FooCanvasRE *re;
+	double dx, dy;
+	double scaled_dist;
+	double outline_dist;
+	double center_dist;
+	double width;
+	double a, b;
+	double diamx, diamy;
+
+	re = FOO_CANVAS_RE (item);
+
+	*actual_item = item;
+
+	if (re->outline_set) {
+		if (re->width_pixels)
+			width = re->width / item->canvas->pixels_per_unit;
+		else
+			width = re->width;
+	} else
+		width = 0.0;
+
+	/* Compute the distance between the center of the ellipse and the point, with the ellipse
+	 * considered as being scaled to a circle.
+	 */
+
+	dx = x - (re->x1 + re->x2) / 2.0;
+	dy = y - (re->y1 + re->y2) / 2.0;
+	center_dist = sqrt (dx * dx + dy * dy);
+
+	a = dx / ((re->x2 + width - re->x1) / 2.0);
+	b = dy / ((re->y2 + width - re->y1) / 2.0);
+	scaled_dist = sqrt (a * a + b * b);
+
+	/* If the scaled distance is greater than 1, then we are outside.  Compute the distance from
+	 * the point to the edge of the circle, then scale back to the original un-scaled coordinate
+	 * system.
+	 */
+
+	if (scaled_dist > 1.0)
+		return (center_dist / scaled_dist) * (scaled_dist - 1.0);
+
+	/* We are inside the outer edge of the ellipse.  If it is filled, then we are "inside".
+	 * Otherwise, do the same computation as above, but also check whether we are inside the
+	 * outline.
+	 */
+
+	if (re->fill_set)
+		return 0.0;
+
+	if (scaled_dist > FOO_CANVAS_EPSILON)
+		outline_dist = (center_dist / scaled_dist) * (1.0 - scaled_dist) - width;
+	else {
+		/* Handle very small distance */
+
+		diamx = re->x2 - re->x1;
+		diamy = re->y2 - re->y1;
+
+		if (diamx < diamy)
+			outline_dist = (diamx - width) / 2.0;
+		else
+			outline_dist = (diamy - width) / 2.0;
+	}
+
+	if (outline_dist < 0.0)
+		return 0.0;
+
+	return outline_dist;
+}
+
+static void
+foo_canvas_ellipse_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, gint flags)
+{
+	FooCanvasRE *re;
+	double x0, y0, x1, y1;
+
+#ifdef VERBOSE
+	g_print ("foo_canvas_sllipse_update item %x\n", item);
+#endif
+
+	foo_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags);
+	re = FOO_CANVAS_RE (item);
+
+	get_bounds (re, &x0, &y0, &x1, &y1);
+	foo_canvas_update_bbox (item, x0, y0, x1, y1);
+}
+
+static int
+rect_empty (const Rect *src) {
+  return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+static Rect
+make_rect (int x0, int y0, int x1, int y1)
+{
+	Rect r;
+
+	r.x0 = x0;
+	r.y0 = y0;
+	r.x1 = x1;
+	r.y1 = y1;
+	return r;
+}
+
+static gboolean
+rects_intersect (Rect r1, Rect r2)
+{
+	if (r1.x0 >= r2.x1) {
+		return FALSE;
+	}
+	if (r2.x0 >= r1.x1) {
+		return FALSE;
+	}
+	if (r1.y0 >= r2.y1) {
+		return FALSE;
+	}
+	if (r2.y0 >= r1.y1) {
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static void
+diff_rects_guts (Rect ra, Rect rb, int *count, Rect result[4])
+{
+	if (ra.x0 < rb.x0) {
+		result[(*count)++] = make_rect (ra.x0, ra.y0, rb.x0, ra.y1);
+	}
+	if (ra.y0 < rb.y0) {
+		result[(*count)++] = make_rect (ra.x0, ra.y0, ra.x1, rb.y0);
+	}
+	if (ra.x1 < rb.x1) {
+		result[(*count)++] = make_rect (ra.x1, rb.y0, rb.x1, rb.y1);
+	}
+	if (ra.y1 < rb.y1) {
+		result[(*count)++] = make_rect (rb.x0, ra.y1, rb.x1, rb.y1);
+	}
+}
+
+static void
+diff_rects (Rect r1, Rect r2, int *count, Rect result[4])
+{
+	g_assert (count != NULL);
+	g_assert (result != NULL);
+
+	*count = 0;
+
+	if (rects_intersect (r1, r2)) {
+		diff_rects_guts (r1, r2, count, result);
+		diff_rects_guts (r2, r1, count, result);
+	} else {
+		if (!rect_empty (&r1)) {
+			result[(*count)++] = r1;
+		}
+		if (!rect_empty (&r2)) {
+			result[(*count)++] = r2;
+		}
+	}
+}
diff --git a/libfoocanvas/foo-canvas-rect-ellipse.h b/libfoocanvas/foo-canvas-rect-ellipse.h
new file mode 100644
index 0000000..5ee8a25
--- /dev/null
+++ b/libfoocanvas/foo-canvas-rect-ellipse.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Rectangle and ellipse item types for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_RECT_ELLIPSE_H
+#define FOO_CANVAS_RECT_ELLIPSE_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+G_BEGIN_DECLS
+
+
+/* Base class for rectangle and ellipse item types.  These are defined by their top-left and
+ * bottom-right corners.  Rectangles and ellipses share the following arguments:
+ *
+ * name			type		read/write	description
+ * ------------------------------------------------------------------------------------------
+ * x1			double		RW		Leftmost coordinate of rectangle or ellipse
+ * y1			double		RW		Topmost coordinate of rectangle or ellipse
+ * x2			double		RW		Rightmost coordinate of rectangle or ellipse
+ * y2			double		RW		Bottommost coordinate of rectangle or ellipse
+ * fill_color		string		W		X color specification for fill color,
+ *							or NULL pointer for no color (transparent)
+ * fill_color_gdk	GdkColor*	RW		Allocated GdkColor for fill
+ * outline_color	string		W		X color specification for outline color,
+ *							or NULL pointer for no color (transparent)
+ * outline_color_gdk	GdkColor*	RW		Allocated GdkColor for outline
+ * fill_stipple		GdkBitmap*	RW		Stipple pattern for fill
+ * outline_stipple	GdkBitmap*	RW		Stipple pattern for outline
+ * width_pixels		uint		RW		Width of the outline in pixels.  The outline will
+ *							not be scaled when the canvas zoom factor is changed.
+ * width_units		double		RW		Width of the outline in canvas units.  The outline
+ *							will be scaled when the canvas zoom factor is changed.
+ */
+
+
+#define FOO_TYPE_CANVAS_RE            (foo_canvas_re_get_type ())
+#define FOO_CANVAS_RE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_RE, FooCanvasRE))
+#define FOO_CANVAS_RE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_RE, FooCanvasREClass))
+#define FOO_IS_CANVAS_RE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_RE))
+#define FOO_IS_CANVAS_RE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_RE))
+#define FOO_CANVAS_RE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_RE, FooCanvasREClass))
+
+
+typedef struct _FooCanvasRE      FooCanvasRE;
+typedef struct _FooCanvasREClass FooCanvasREClass;
+
+struct _FooCanvasRE {
+	FooCanvasItem item;
+
+	GdkBitmap *fill_stipple;	/* Stipple for fill */
+	GdkBitmap *outline_stipple;	/* Stipple for outline */
+
+	GdkGC *fill_gc;			/* GC for filling */
+	GdkGC *outline_gc;		/* GC for outline */
+
+	gulong fill_pixel;		/* Fill color */
+	gulong outline_pixel;		/* Outline color */
+
+	double x1, y1, x2, y2;		/* Corners of item */
+	double width;			/* Outline width */
+
+	guint fill_color;		/* Fill color, RGBA */
+	guint outline_color;		/* Outline color, RGBA */
+
+	/* Configuration flags */
+
+	unsigned int fill_set : 1;	/* Is fill color set? */
+	unsigned int outline_set : 1;	/* Is outline color set? */
+	unsigned int width_pixels : 1;	/* Is outline width specified in pixels or units? */
+};
+
+struct _FooCanvasREClass {
+	FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_re_get_type (void) G_GNUC_CONST;
+
+
+/* Rectangle item.  No configurable or queryable arguments are available (use those in
+ * FooCanvasRE).
+ */
+
+
+#define FOO_TYPE_CANVAS_RECT            (foo_canvas_rect_get_type ())
+#define FOO_CANVAS_RECT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_RECT, FooCanvasRect))
+#define FOO_CANVAS_RECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_RECT, FooCanvasRectClass))
+#define FOO_IS_CANVAS_RECT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_RECT))
+#define FOO_IS_CANVAS_RECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_RECT))
+#define FOO_CANVAS_RECT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_RECT, FooCanvasRectClass))
+
+
+typedef struct _FooCanvasRect FooCanvasRect;
+typedef struct _FooCanvasRectPrivate FooCanvasRectPrivate;
+typedef struct _FooCanvasRectClass FooCanvasRectClass;
+
+struct _FooCanvasRect {
+	FooCanvasRE re;
+	FooCanvasRectPrivate *priv;
+};
+
+struct _FooCanvasRectClass {
+	FooCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_rect_get_type (void) G_GNUC_CONST;
+
+
+/* Ellipse item.  No configurable or queryable arguments are available (use those in
+ * FooCanvasRE).
+ */
+
+
+#define FOO_TYPE_CANVAS_ELLIPSE            (foo_canvas_ellipse_get_type ())
+#define FOO_CANVAS_ELLIPSE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_ELLIPSE, FooCanvasEllipse))
+#define FOO_CANVAS_ELLIPSE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_ELLIPSE, FooCanvasEllipseClass))
+#define FOO_IS_CANVAS_ELLIPSE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_ELLIPSE))
+#define FOO_IS_CANVAS_ELLIPSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_ELLIPSE))
+#define FOO_CANVAS_ELLIPSE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_ELLIPSE, FooCanvasEllipseClass))
+
+
+typedef struct _FooCanvasEllipse FooCanvasEllipse;
+typedef struct _FooCanvasEllipseClass FooCanvasEllipseClass;
+
+struct _FooCanvasEllipse {
+	FooCanvasRE re;
+};
+
+struct _FooCanvasEllipseClass {
+	FooCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_ellipse_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-text.c b/libfoocanvas/foo-canvas-text.c
new file mode 100644
index 0000000..ca876db
--- /dev/null
+++ b/libfoocanvas/foo-canvas-text.c
@@ -0,0 +1,1587 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * $Id$
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Text item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas
+ * widget.  Tk is copyrighted by the Regents of the University of California,
+ * Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ * Port to Pango co-done by Gergõ Érdi <cactus cactus rulez org>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "foo-canvas-text.h"
+
+#include "foo-canvas-util.h"
+#include "foo-canvas-i18n.h"
+
+
+
+/* Object argument IDs */
+enum {
+	PROP_0,
+
+	/* Text contents */
+	PROP_TEXT,
+	PROP_MARKUP,
+
+	/* Position */
+	PROP_X,
+	PROP_Y,
+
+	/* Font */
+	PROP_FONT,
+	PROP_FONT_DESC,
+	PROP_FAMILY, PROP_FAMILY_SET,
+
+	/* Style */
+	PROP_ATTRIBUTES,
+	PROP_STYLE,         PROP_STYLE_SET,
+	PROP_VARIANT,       PROP_VARIANT_SET,
+	PROP_WEIGHT,        PROP_WEIGHT_SET,
+	PROP_STRETCH,	    PROP_STRETCH_SET,
+	PROP_SIZE,          PROP_SIZE_SET,
+	PROP_SIZE_POINTS,
+	PROP_STRIKETHROUGH, PROP_STRIKETHROUGH_SET,
+	PROP_UNDERLINE,     PROP_UNDERLINE_SET,
+	PROP_RISE,          PROP_RISE_SET,
+	PROP_SCALE,         PROP_SCALE_SET,
+
+	/* Clipping */
+	PROP_ANCHOR,
+	PROP_JUSTIFICATION,
+	PROP_CLIP_WIDTH,
+	PROP_CLIP_HEIGHT,
+	PROP_CLIP,
+	PROP_WRAP_WIDTH,
+	PROP_X_OFFSET,
+	PROP_Y_OFFSET,
+
+	/* Coloring */
+	PROP_FILL_COLOR,
+	PROP_FILL_COLOR_GDK,
+	PROP_FILL_COLOR_RGBA,
+	PROP_FILL_STIPPLE,
+
+	/* Rendered size accessors */
+	PROP_TEXT_WIDTH,
+	PROP_TEXT_HEIGHT
+};
+
+struct _FooCanvasTextPrivate {
+	gint placeholder;
+};
+
+static void foo_canvas_text_class_init (FooCanvasTextClass *klass);
+static void foo_canvas_text_init (FooCanvasText *text);
+static void foo_canvas_text_destroy (GtkObject *object);
+static void foo_canvas_text_set_property (GObject            *object,
+					    guint               param_id,
+					    const GValue       *value,
+					    GParamSpec         *pspec);
+static void foo_canvas_text_get_property (GObject            *object,
+					    guint               param_id,
+					    GValue             *value,
+					    GParamSpec         *pspec);
+
+static void   foo_canvas_text_update    (FooCanvasItem  *item,
+					   double            i2w_dx,
+					   double            i2w_dy,
+					   int               flags);
+static void   foo_canvas_text_realize   (FooCanvasItem  *item);
+static void   foo_canvas_text_unrealize (FooCanvasItem  *item);
+static void   foo_canvas_text_draw      (FooCanvasItem  *item,
+					   GdkDrawable      *drawable,
+					   GdkEventExpose   *expose);
+static double foo_canvas_text_point     (FooCanvasItem  *item,
+					   double            x,
+					   double            y,
+					   int               cx,
+					   int               cy,
+					   FooCanvasItem **actual_item);
+static void   foo_canvas_text_translate (FooCanvasItem  *item,
+					   double            dx,
+					   double            dy);
+static void   foo_canvas_text_bounds    (FooCanvasItem  *item,
+					   double           *x1,
+					   double           *y1,
+					   double           *x2,
+					   double           *y2);
+
+static void foo_canvas_text_set_markup (FooCanvasText *textitem,
+					  const gchar     *markup);
+
+static void foo_canvas_text_set_font_desc    (FooCanvasText *textitem,
+					        PangoFontDescription *font_desc);
+
+static void foo_canvas_text_apply_font_desc  (FooCanvasText *textitem);
+static void foo_canvas_text_apply_attributes (FooCanvasText *textitem);
+
+static void add_attr (PangoAttrList  *attr_list,
+		      PangoAttribute *attr);
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasText, foo_canvas_text, FOO_TYPE_CANVAS_ITEM)
+
+/* Class initialization function for the text item */
+static void
+foo_canvas_text_class_init (FooCanvasTextClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	FooCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (FooCanvasItemClass *) klass;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_text_set_property;
+	gobject_class->get_property = foo_canvas_text_get_property;
+
+	/* Text */
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_TEXT,
+                 g_param_spec_string ("text",
+				      _("Text"),
+				      _("Text to render"),
+                                      NULL,
+                                      G_PARAM_READWRITE));
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_MARKUP,
+                 g_param_spec_string ("markup",
+				      _("Markup"),
+				      _("Marked up text to render"),
+				      NULL,
+                                      (G_PARAM_WRITABLE)));
+
+	/* Position */
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X,
+                 g_param_spec_double ("x", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y,
+                 g_param_spec_double ("y", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+
+
+	/* Font */
+	g_object_class_install_property
+                (gobject_class,
+                 PROP_FONT,
+                 g_param_spec_string ("font",
+				      _("Font"),
+				      _("Font description as a string"),
+                                      NULL,
+                                      G_PARAM_READWRITE));
+
+        g_object_class_install_property
+		(gobject_class,
+		 PROP_FONT_DESC,
+		 g_param_spec_boxed ("font-desc",
+				     _("Font description"),
+				     _("Font description as a PangoFontDescription struct"),
+				     PANGO_TYPE_FONT_DESCRIPTION,
+				     G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_FAMILY,
+		 g_param_spec_string ("family",
+				      _("Font family"),
+				      _("Name of the font family, e.g. Sans, Helvetica, Times, Monospace"),
+				      NULL,
+				      G_PARAM_READWRITE));
+
+	/* Style */
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ATTRIBUTES,
+                 g_param_spec_boxed ("attributes", NULL, NULL,
+				     PANGO_TYPE_ATTR_LIST,
+				     G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_STYLE,
+		 g_param_spec_enum ("style",
+				    _("Font style"),
+				    _("Font style"),
+				    PANGO_TYPE_STYLE,
+				    PANGO_STYLE_NORMAL,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_VARIANT,
+		 g_param_spec_enum ("variant",
+				    _("Font variant"),
+				    _("Font variant"),
+				    PANGO_TYPE_VARIANT,
+				    PANGO_VARIANT_NORMAL,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_WEIGHT,
+		 g_param_spec_int ("weight",
+				   _("Font weight"),
+				   _("Font weight"),
+				   0,
+				   G_MAXINT,
+				   PANGO_WEIGHT_NORMAL,
+				   G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_STRETCH,
+		 g_param_spec_enum ("stretch",
+				    _("Font stretch"),
+				    _("Font stretch"),
+				    PANGO_TYPE_STRETCH,
+				    PANGO_STRETCH_NORMAL,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_SIZE,
+		 g_param_spec_int ("size",
+				   _("Font size"),
+				   _("Font size"),
+				   0,
+				   G_MAXINT,
+				   0,
+				   G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		PROP_SIZE_POINTS,
+		g_param_spec_double ("size-points",
+				     _("Font points"),
+				     _("Font size in points"),
+				     0.0,
+				     G_MAXDOUBLE,
+				     0.0,
+				     G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_RISE,
+		 g_param_spec_int ("rise",
+				   _("Rise"),
+				   _("Offset of text above the baseline (below the baseline if rise is negative)"),
+				   -G_MAXINT,
+				   G_MAXINT,
+				   0,
+				   G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_STRIKETHROUGH,
+		 g_param_spec_boolean ("strikethrough",
+				       _("Strikethrough"),
+				       _("Whether to strike through the text"),
+				       FALSE,
+				       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_UNDERLINE,
+		 g_param_spec_enum ("underline",
+				    _("Underline"),
+				    _("Style of underline for this text"),
+				    PANGO_TYPE_UNDERLINE,
+				    PANGO_UNDERLINE_NONE,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_SCALE,
+		 g_param_spec_double ("scale",
+				      _("Scale"),
+				      _("Size of font, relative to default size"),
+				      0.0,
+				      G_MAXDOUBLE,
+				      1.0,
+				      G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+        g_object_class_install_property
+		(gobject_class,
+                 PROP_ANCHOR,
+                 g_param_spec_enum ("anchor", NULL, NULL,
+                                    GTK_TYPE_ANCHOR_TYPE,
+                                    GTK_ANCHOR_CENTER,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_JUSTIFICATION,
+                 g_param_spec_enum ("justification", NULL, NULL,
+                                    GTK_TYPE_JUSTIFICATION,
+                                    GTK_JUSTIFY_LEFT,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CLIP_WIDTH,
+                 g_param_spec_double ("clip-width", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CLIP_HEIGHT,
+                 g_param_spec_double ("clip-height", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CLIP,
+                 g_param_spec_boolean ("clip", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WRAP_WIDTH,
+                 g_param_spec_double ("wrap-width", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X_OFFSET,
+                 g_param_spec_double ("x-offset", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y_OFFSET,
+                 g_param_spec_double ("y-offset", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_string ("fill-color",
+				      _("Color"),
+				      _("Text color, as string"),
+                                      NULL,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_GDK,
+                 g_param_spec_boxed ("fill-color-gdk",
+				     _("Color"),
+				     _("Text color, as a GdkColor"),
+				     GDK_TYPE_COLOR,
+				     G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_RGBA,
+                 g_param_spec_uint ("fill-color-rgba",
+				    _("Color"),
+				    _("Text color, as an R/G/B/A combined integer"),
+				    0, G_MAXUINT, 0,
+				    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_STIPPLE,
+                 g_param_spec_object ("fill-stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_TEXT_WIDTH,
+                 g_param_spec_double ("text-width",
+				      _("Text width"),
+				      _("Width of the rendered text"),
+				      0.0, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_TEXT_HEIGHT,
+                 g_param_spec_double ("text-height",
+				      _("Text height"),
+				      _("Height of the rendered text"),
+				      0.0, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+
+	/* Style props are set (explicitly applied) or not */
+#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (gobject_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE))
+
+	ADD_SET_PROP ("family-set", PROP_FAMILY_SET,
+		      _("Font family set"),
+		      _("Whether this tag affects the font family"));
+
+	ADD_SET_PROP ("style-set", PROP_STYLE_SET,
+		      _("Font style set"),
+		      _("Whether this tag affects the font style"));
+
+	ADD_SET_PROP ("variant-set", PROP_VARIANT_SET,
+		      _("Font variant set"),
+		      _("Whether this tag affects the font variant"));
+
+	ADD_SET_PROP ("weight-set", PROP_WEIGHT_SET,
+		      _("Font weight set"),
+		      _("Whether this tag affects the font weight"));
+
+	ADD_SET_PROP ("stretch-set", PROP_STRETCH_SET,
+		      _("Font stretch set"),
+		      _("Whether this tag affects the font stretch"));
+
+	ADD_SET_PROP ("size-set", PROP_SIZE_SET,
+		      _("Font size set"),
+		      _("Whether this tag affects the font size"));
+
+	ADD_SET_PROP ("rise-set", PROP_RISE_SET,
+		      _("Rise set"),
+		      _("Whether this tag affects the rise"));
+
+	ADD_SET_PROP ("strikethrough-set", PROP_STRIKETHROUGH_SET,
+		      _("Strikethrough set"),
+		      _("Whether this tag affects strikethrough"));
+
+	ADD_SET_PROP ("underline-set", PROP_UNDERLINE_SET,
+		      _("Underline set"),
+		      _("Whether this tag affects underlining"));
+
+	ADD_SET_PROP ("scale-set", PROP_SCALE_SET,
+		      _("Scale set"),
+		      _("Whether this tag affects font scaling"));
+#undef ADD_SET_PROP
+
+	object_class->destroy = foo_canvas_text_destroy;
+
+	item_class->update = foo_canvas_text_update;
+	item_class->realize = foo_canvas_text_realize;
+	item_class->unrealize = foo_canvas_text_unrealize;
+	item_class->draw = foo_canvas_text_draw;
+	item_class->point = foo_canvas_text_point;
+	item_class->translate = foo_canvas_text_translate;
+	item_class->bounds = foo_canvas_text_bounds;
+}
+
+/* Object initialization function for the text item */
+static void
+foo_canvas_text_init (FooCanvasText *text)
+{
+	text->x = 0.0;
+	text->y = 0.0;
+	text->anchor = GTK_ANCHOR_CENTER;
+	text->justification = GTK_JUSTIFY_LEFT;
+	text->clip_width = 0.0;
+	text->clip_height = 0.0;
+	text->xofs = 0.0;
+	text->yofs = 0.0;
+	text->layout = NULL;
+
+	text->font_desc = NULL;
+
+	text->underline     = PANGO_UNDERLINE_NONE;
+	text->strikethrough = FALSE;
+	text->rise          = 0;
+
+	text->underline_set = FALSE;
+	text->strike_set    = FALSE;
+	text->rise_set      = FALSE;
+
+	text->priv = g_new (FooCanvasTextPrivate, 1);
+}
+
+/* Destroy handler for the text item */
+static void
+foo_canvas_text_destroy (GtkObject *object)
+{
+	FooCanvasText *text;
+
+	g_return_if_fail (FOO_IS_CANVAS_TEXT (object));
+
+	text = FOO_CANVAS_TEXT (object);
+
+	/* remember, destroy can be run multiple times! */
+
+	g_free (text->text);
+	text->text = NULL;
+
+	if (text->layout)
+	    g_object_unref (G_OBJECT (text->layout));
+	text->layout = NULL;
+
+	if (text->font_desc) {
+		pango_font_description_free (text->font_desc);
+		text->font_desc = NULL;
+	}
+
+	if (text->attr_list)
+		pango_attr_list_unref (text->attr_list);
+	text->attr_list = NULL;
+
+	if (text->stipple)
+		g_object_unref (text->stipple);
+	text->stipple = NULL;
+
+	g_free (text->priv);
+	text->priv = NULL;
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+get_bounds (FooCanvasText *text, double *px1, double *py1, double *px2, double *py2)
+{
+	FooCanvasItem *item;
+	double wx, wy;
+
+	item = FOO_CANVAS_ITEM (text);
+
+	/* Get canvas pixel coordinates for text position */
+
+
+	wx = text->x;
+	wy = text->y;
+	foo_canvas_item_i2w (item, &wx, &wy);
+	foo_canvas_w2c (item->canvas, wx + text->xofs, wy + text->yofs, &text->cx, &text->cy);
+
+	/* Get canvas pixel coordinates for clip rectangle position */
+
+	foo_canvas_w2c (item->canvas, wx, wy, &text->clip_cx, &text->clip_cy);
+	text->clip_cwidth = text->clip_width * item->canvas->pixels_per_unit;
+	text->clip_cheight = text->clip_height * item->canvas->pixels_per_unit;
+
+	/* Anchor text */
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		text->cx -= text->max_width / 2;
+		text->clip_cx -= text->clip_cwidth / 2;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		text->cx -= text->max_width;
+		text->clip_cx -= text->clip_cwidth;
+		break;
+
+	default:
+		break;
+	}
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		text->cy -= text->height / 2;
+		text->clip_cy -= text->clip_cheight / 2;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		text->cy -= text->height;
+		text->clip_cy -= text->clip_cheight;
+		break;
+
+	default:
+		break;
+	}
+
+	/* Bounds */
+
+	if (text->clip) {
+		*px1 = text->clip_cx;
+		*py1 = text->clip_cy;
+		*px2 = text->clip_cx + text->clip_cwidth;
+		*py2 = text->clip_cy + text->clip_cheight;
+	} else {
+		*px1 = text->cx;
+		*py1 = text->cy;
+		*px2 = text->cx + text->max_width;
+		*py2 = text->cy + text->height;
+	}
+}
+
+/* Convenience function to set the text's GC's foreground color */
+static void
+set_text_gc_foreground (FooCanvasText *text)
+{
+	GdkColor c;
+
+	if (!text->gc)
+		return;
+
+	c.pixel = text->pixel;
+	gdk_gc_set_foreground (text->gc, &c);
+}
+
+/* Sets the stipple pattern for the text */
+static void
+set_stipple (FooCanvasText *text, GdkBitmap *stipple, int reconfigure)
+{
+	if (text->stipple && !reconfigure)
+		g_object_unref (text->stipple);
+
+	text->stipple = stipple;
+	if (stipple && !reconfigure)
+		g_object_ref (stipple);
+
+	if (text->gc) {
+		if (stipple) {
+			gdk_gc_set_stipple (text->gc, stipple);
+			gdk_gc_set_fill (text->gc, GDK_STIPPLED);
+		} else
+			gdk_gc_set_fill (text->gc, GDK_SOLID);
+	}
+}
+
+static PangoFontMask
+get_property_font_set_mask (guint prop_id)
+{
+  switch (prop_id)
+    {
+    case PROP_FAMILY_SET:
+      return PANGO_FONT_MASK_FAMILY;
+    case PROP_STYLE_SET:
+      return PANGO_FONT_MASK_STYLE;
+    case PROP_VARIANT_SET:
+      return PANGO_FONT_MASK_VARIANT;
+    case PROP_WEIGHT_SET:
+      return PANGO_FONT_MASK_WEIGHT;
+    case PROP_STRETCH_SET:
+      return PANGO_FONT_MASK_STRETCH;
+    case PROP_SIZE_SET:
+      return PANGO_FONT_MASK_SIZE;
+    }
+
+  return 0;
+}
+
+static void
+ensure_font (FooCanvasText *text)
+{
+	if (!text->font_desc)
+		text->font_desc = pango_font_description_new ();
+}
+
+/* Set_arg handler for the text item */
+static void
+foo_canvas_text_set_property (GObject            *object,
+				guint               param_id,
+				const GValue       *value,
+				GParamSpec         *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasText *text;
+	GdkColor color = { 0, 0, 0, 0, };
+	GdkColor *pcolor;
+	gboolean color_changed;
+	int have_pixel;
+	PangoAlignment align;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_TEXT (object));
+
+	item = FOO_CANVAS_ITEM (object);
+	text = FOO_CANVAS_TEXT (object);
+
+	color_changed = FALSE;
+	have_pixel = FALSE;
+
+
+	if (!text->layout) {
+		text->layout = gtk_widget_create_pango_layout  (GTK_WIDGET (item->canvas), NULL);
+	}
+
+	switch (param_id) {
+	case PROP_TEXT:
+		if (text->text)
+			g_free (text->text);
+
+		text->text = g_value_dup_string (value);
+		pango_layout_set_text (text->layout, text->text, -1);
+
+		break;
+
+	case PROP_MARKUP:
+		foo_canvas_text_set_markup (text,
+					      g_value_get_string (value));
+		break;
+
+	case PROP_X:
+		text->x = g_value_get_double (value);
+		break;
+
+	case PROP_Y:
+		text->y = g_value_get_double (value);
+		break;
+
+	case PROP_FONT: {
+		const char *font_name;
+		PangoFontDescription *font_desc;
+
+		font_name = g_value_get_string (value);
+		if (font_name)
+			font_desc = pango_font_description_from_string (font_name);
+		else
+			font_desc = NULL;
+
+		foo_canvas_text_set_font_desc (text, font_desc);
+		if (font_desc)
+			pango_font_description_free (font_desc);
+
+		break;
+	}
+
+	case PROP_FONT_DESC:
+		foo_canvas_text_set_font_desc (text, g_value_peek_pointer (value));
+		break;
+
+	case PROP_FAMILY:
+	case PROP_STYLE:
+	case PROP_VARIANT:
+	case PROP_WEIGHT:
+	case PROP_STRETCH:
+	case PROP_SIZE:
+	case PROP_SIZE_POINTS:
+		ensure_font (text);
+
+		switch (param_id) {
+		case PROP_FAMILY:
+			pango_font_description_set_family (text->font_desc,
+							   g_value_get_string (value));
+			break;
+		case PROP_STYLE:
+			pango_font_description_set_style (text->font_desc,
+							  g_value_get_enum (value));
+			break;
+		case PROP_VARIANT:
+			pango_font_description_set_variant (text->font_desc,
+							    g_value_get_enum (value));
+			break;
+		case PROP_WEIGHT:
+			pango_font_description_set_weight (text->font_desc,
+							   g_value_get_int (value));
+			break;
+		case PROP_STRETCH:
+			pango_font_description_set_stretch (text->font_desc,
+							    g_value_get_enum (value));
+			break;
+		case PROP_SIZE:
+			/* FIXME: This is bogus! It should be pixels, not points/PANGO_SCALE! */
+			pango_font_description_set_size (text->font_desc,
+							 g_value_get_int (value));
+			break;
+		case PROP_SIZE_POINTS:
+			pango_font_description_set_size (text->font_desc,
+							 g_value_get_double (value) * PANGO_SCALE);
+			break;
+		}
+
+		foo_canvas_text_apply_font_desc (text);
+		break;
+
+	case PROP_FAMILY_SET:
+	case PROP_STYLE_SET:
+	case PROP_VARIANT_SET:
+	case PROP_WEIGHT_SET:
+	case PROP_STRETCH_SET:
+	case PROP_SIZE_SET:
+		if (!g_value_get_boolean (value) && text->font_desc)
+			pango_font_description_unset_fields (text->font_desc,
+							     get_property_font_set_mask (param_id));
+		break;
+
+	case PROP_SCALE:
+		text->scale = g_value_get_double (value);
+		text->scale_set = TRUE;
+
+		foo_canvas_text_apply_font_desc (text);
+		break;
+
+	case PROP_SCALE_SET:
+		text->scale_set = g_value_get_boolean (value);
+
+		foo_canvas_text_apply_font_desc (text);
+		break;
+
+	case PROP_UNDERLINE:
+		text->underline = g_value_get_enum (value);
+		text->underline_set = TRUE;
+
+		foo_canvas_text_apply_attributes (text);
+		break;
+
+	case PROP_UNDERLINE_SET:
+		text->underline_set = g_value_get_boolean (value);
+
+		foo_canvas_text_apply_attributes (text);
+		break;
+
+	case PROP_STRIKETHROUGH:
+		text->strikethrough = g_value_get_boolean (value);
+		text->strike_set = TRUE;
+
+		foo_canvas_text_apply_attributes (text);
+		break;
+
+	case PROP_STRIKETHROUGH_SET:
+		text->strike_set = g_value_get_boolean (value);
+
+		foo_canvas_text_apply_attributes (text);
+		break;
+
+	case PROP_RISE:
+		text->rise = g_value_get_int (value);
+		text->rise_set = TRUE;
+
+		foo_canvas_text_apply_attributes (text);
+		break;
+
+	case PROP_RISE_SET:
+		text->rise_set = TRUE;
+
+		foo_canvas_text_apply_attributes (text);
+		break;
+
+	case PROP_ATTRIBUTES:
+		if (text->attr_list)
+			pango_attr_list_unref (text->attr_list);
+
+		text->attr_list = g_value_peek_pointer (value);
+		if (text->attr_list)
+			pango_attr_list_ref (text->attr_list);
+
+		foo_canvas_text_apply_attributes (text);
+		break;
+
+	case PROP_ANCHOR:
+		text->anchor = g_value_get_enum (value);
+		break;
+
+	case PROP_JUSTIFICATION:
+		text->justification = g_value_get_enum (value);
+
+		switch (text->justification) {
+		case GTK_JUSTIFY_LEFT:
+		        align = PANGO_ALIGN_LEFT;
+			break;
+		case GTK_JUSTIFY_CENTER:
+		        align = PANGO_ALIGN_CENTER;
+			break;
+		case GTK_JUSTIFY_RIGHT:
+		        align = PANGO_ALIGN_RIGHT;
+			break;
+		default:
+		        /* GTK_JUSTIFY_FILL isn't supported yet. */
+		        align = PANGO_ALIGN_LEFT;
+			break;
+		}
+		pango_layout_set_alignment (text->layout, align);
+		break;
+
+	case PROP_CLIP_WIDTH:
+		text->clip_width = fabs (g_value_get_double (value));
+		break;
+
+	case PROP_CLIP_HEIGHT:
+		text->clip_height = fabs (g_value_get_double (value));
+		break;
+
+	case PROP_CLIP:
+		text->clip = g_value_get_boolean (value);
+		break;
+
+	case PROP_WRAP_WIDTH: {
+		double w = fabs (g_value_get_double (value));
+		pango_layout_set_width (text->layout,
+			w * text->item.canvas->pixels_per_unit * PANGO_SCALE);
+
+		break;
+	}
+
+	case PROP_X_OFFSET:
+		text->xofs = g_value_get_double (value);
+		break;
+
+	case PROP_Y_OFFSET:
+		text->yofs = g_value_get_double (value);
+		break;
+
+        case PROP_FILL_COLOR: {
+		const char *color_name;
+
+		color_name = g_value_get_string (value);
+		if (color_name) {
+			gdk_color_parse (color_name, &color);
+
+			text->rgba = ((color.red & 0xff00) << 16 |
+				      (color.green & 0xff00) << 8 |
+				      (color.blue & 0xff00) |
+				      0xff);
+			color_changed = TRUE;
+		}
+		break;
+	}
+
+	case PROP_FILL_COLOR_GDK:
+		pcolor = g_value_get_boxed (value);
+		if (pcolor) {
+		    GdkColormap *colormap;
+
+		    color = *pcolor;
+		    colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+		    gdk_rgb_find_color (colormap, &color);
+		    have_pixel = TRUE;
+		}
+
+		text->rgba = ((color.red & 0xff00) << 16 |
+			      (color.green & 0xff00) << 8|
+			      (color.blue & 0xff00) |
+			      0xff);
+		color_changed = TRUE;
+		break;
+
+        case PROP_FILL_COLOR_RGBA:
+		text->rgba = g_value_get_uint (value);
+		color_changed = TRUE;
+		break;
+
+	case PROP_FILL_STIPPLE:
+		set_stipple (text, (GdkBitmap *)g_value_get_object (value), FALSE);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+
+	if (color_changed) {
+		if (have_pixel)
+			text->pixel = color.pixel;
+		else
+			text->pixel = foo_canvas_get_color_pixel (item->canvas, text->rgba);
+
+		set_text_gc_foreground (text);
+	}
+
+	/* Calculate text dimensions */
+
+	if (text->layout)
+	        pango_layout_get_pixel_size (text->layout,
+					     &text->max_width,
+					     &text->height);
+	else {
+		text->max_width = 0;
+		text->height = 0;
+	}
+
+	foo_canvas_item_request_update (item);
+}
+
+/* Get_arg handler for the text item */
+static void
+foo_canvas_text_get_property (GObject            *object,
+				guint               param_id,
+				GValue             *value,
+				GParamSpec         *pspec)
+{
+	FooCanvasText *text;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_TEXT (object));
+
+	text = FOO_CANVAS_TEXT (object);
+
+	switch (param_id) {
+	case PROP_TEXT:
+		g_value_set_string (value, text->text);
+		break;
+
+	case PROP_X:
+		g_value_set_double (value, text->x);
+		break;
+
+	case PROP_Y:
+		g_value_set_double (value, text->y);
+		break;
+
+	case PROP_FONT:
+	case PROP_FONT_DESC:
+	case PROP_FAMILY:
+	case PROP_STYLE:
+	case PROP_VARIANT:
+	case PROP_WEIGHT:
+	case PROP_STRETCH:
+	case PROP_SIZE:
+	case PROP_SIZE_POINTS:
+		ensure_font (text);
+
+		switch (param_id) {
+		case PROP_FONT:
+		{
+			/* FIXME GValue imposes a totally gratuitous string copy
+			 * here, we could just hand off string ownership
+			 */
+			gchar *str;
+
+			str = pango_font_description_to_string (text->font_desc);
+			g_value_set_string (value, str);
+			g_free (str);
+
+			break;
+		}
+
+		case PROP_FONT_DESC:
+			g_value_set_boxed (value, text->font_desc);
+			break;
+
+		case PROP_FAMILY:
+			g_value_set_string (value, pango_font_description_get_family (text->font_desc));
+			break;
+
+		case PROP_STYLE:
+			g_value_set_enum (value, pango_font_description_get_style (text->font_desc));
+			break;
+
+		case PROP_VARIANT:
+			g_value_set_enum (value, pango_font_description_get_variant (text->font_desc));
+			break;
+
+		case PROP_WEIGHT:
+			g_value_set_int (value, pango_font_description_get_weight (text->font_desc));
+			break;
+
+		case PROP_STRETCH:
+			g_value_set_enum (value, pango_font_description_get_stretch (text->font_desc));
+			break;
+
+		case PROP_SIZE:
+			g_value_set_int (value, pango_font_description_get_size (text->font_desc));
+			break;
+
+		case PROP_SIZE_POINTS:
+			g_value_set_double (value, ((double)pango_font_description_get_size (text->font_desc)) / (double)PANGO_SCALE);
+			break;
+		}
+		break;
+
+	case PROP_FAMILY_SET:
+	case PROP_STYLE_SET:
+	case PROP_VARIANT_SET:
+	case PROP_WEIGHT_SET:
+	case PROP_STRETCH_SET:
+	case PROP_SIZE_SET:
+	{
+		PangoFontMask set_mask = text->font_desc ? pango_font_description_get_set_fields (text->font_desc) : 0;
+		PangoFontMask test_mask = get_property_font_set_mask (param_id);
+		g_value_set_boolean (value, (set_mask & test_mask) != 0);
+
+		break;
+	}
+
+	case PROP_SCALE:
+		g_value_set_double (value, text->scale);
+		break;
+	case PROP_SCALE_SET:
+		g_value_set_boolean (value, text->scale_set);
+		break;
+
+	case PROP_UNDERLINE:
+		g_value_set_enum (value, text->underline);
+		break;
+	case PROP_UNDERLINE_SET:
+		g_value_set_boolean (value, text->underline_set);
+		break;
+
+	case PROP_STRIKETHROUGH:
+		g_value_set_boolean (value, text->strikethrough);
+		break;
+	case PROP_STRIKETHROUGH_SET:
+		g_value_set_boolean (value, text->strike_set);
+		break;
+
+	case PROP_RISE:
+		g_value_set_int (value, text->rise);
+		break;
+	case PROP_RISE_SET:
+		g_value_set_boolean (value, text->rise_set);
+		break;
+
+	case PROP_ATTRIBUTES:
+		g_value_set_boxed (value, text->attr_list);
+		break;
+
+	case PROP_ANCHOR:
+		g_value_set_enum (value, text->anchor);
+		break;
+
+	case PROP_JUSTIFICATION:
+		g_value_set_enum (value, text->justification);
+		break;
+
+	case PROP_CLIP_WIDTH:
+		g_value_set_double (value, text->clip_width);
+		break;
+
+	case PROP_CLIP_HEIGHT:
+		g_value_set_double (value, text->clip_height);
+		break;
+
+	case PROP_CLIP:
+		g_value_set_boolean (value, text->clip);
+		break;
+
+	case PROP_WRAP_WIDTH:
+		g_value_set_double (value,
+			pango_layout_get_width (text->layout) / PANGO_SCALE);
+		break;
+
+	case PROP_X_OFFSET:
+		g_value_set_double (value, text->xofs);
+		break;
+
+	case PROP_Y_OFFSET:
+		g_value_set_double (value, text->yofs);
+		break;
+
+	case PROP_FILL_COLOR:
+                g_value_take_string (value,
+				     g_strdup_printf ("#%02x%02x%02x",
+						      text->rgba >> 24,
+						      (text->rgba >> 16) & 0xff,
+						      (text->rgba >> 8) & 0xff));
+		break;
+
+	case PROP_FILL_COLOR_GDK: {
+		FooCanvas *canvas = FOO_CANVAS_ITEM (text)->canvas;
+		GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+		GdkColor color;
+
+		gdk_colormap_query_color (colormap, text->pixel, &color);
+		g_value_set_boxed (value, &color);
+		break;
+	}
+	case PROP_FILL_COLOR_RGBA:
+		g_value_set_uint (value, text->rgba);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		g_value_set_object (value, text->stipple);
+		break;
+
+	case PROP_TEXT_WIDTH:
+		g_value_set_double (value, text->max_width / text->item.canvas->pixels_per_unit);
+		break;
+
+	case PROP_TEXT_HEIGHT:
+		g_value_set_double (value, text->height / text->item.canvas->pixels_per_unit);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* */
+static void
+foo_canvas_text_apply_font_desc (FooCanvasText *text)
+{
+	PangoFontDescription *font_desc =
+		pango_font_description_copy (
+			GTK_WIDGET (FOO_CANVAS_ITEM (text)->canvas)->style->font_desc);
+
+	if (text->font_desc)
+		pango_font_description_merge (font_desc, text->font_desc, TRUE);
+
+	pango_layout_set_font_description (text->layout, font_desc);
+	pango_font_description_free (font_desc);
+}
+
+static void
+add_attr (PangoAttrList  *attr_list,
+	  PangoAttribute *attr)
+{
+	attr->start_index = 0;
+	attr->end_index = G_MAXINT;
+
+	pango_attr_list_insert (attr_list, attr);
+}
+
+/* */
+static void
+foo_canvas_text_apply_attributes (FooCanvasText *text)
+{
+	PangoAttrList *attr_list;
+	double zoom;
+
+	if (text->attr_list)
+		attr_list = pango_attr_list_copy (text->attr_list);
+	else
+		attr_list = pango_attr_list_new ();
+
+	if (text->underline_set)
+		add_attr (attr_list, pango_attr_underline_new (text->underline));
+	if (text->strike_set)
+		add_attr (attr_list, pango_attr_strikethrough_new (text->strikethrough));
+	if (text->rise_set)
+		add_attr (attr_list, pango_attr_rise_new (text->rise));
+
+	zoom = text->item.canvas->pixels_per_unit;
+	if (fabs (zoom - 1.) > 1e-4) {
+		PangoAttribute *attr = pango_attr_scale_new (zoom);
+		attr->start_index = 0;
+		attr->end_index = -1;
+		pango_attr_list_insert_before (attr_list, attr);
+	}
+
+	pango_layout_set_attributes (text->layout, attr_list);
+	pango_attr_list_unref (attr_list);
+}
+
+static void
+foo_canvas_text_set_font_desc (FooCanvasText      *text,
+				 PangoFontDescription *font_desc)
+{
+	if (text->font_desc)
+		pango_font_description_free (text->font_desc);
+
+	if (font_desc)
+		text->font_desc = pango_font_description_copy (font_desc);
+	else
+		text->font_desc = NULL;
+
+	foo_canvas_text_apply_font_desc (text);
+}
+
+/* Setting the text from a Pango markup string */
+static void
+foo_canvas_text_set_markup (FooCanvasText *textitem,
+			      const gchar     *markup)
+{
+	PangoAttrList *attr_list = NULL;
+	gchar         *text = NULL;
+	GError        *error = NULL;
+
+	if (textitem->text)
+		g_free (textitem->text);
+	if (textitem->attr_list)
+		pango_attr_list_unref (textitem->attr_list);
+
+	if (markup && !pango_parse_markup (markup, -1,
+					   0,
+					   &attr_list, &text, NULL,
+					   &error))
+	{
+		g_warning ("Failed to set cell text from markup due to error parsing markup: %s",
+			   error->message);
+		g_error_free (error);
+		return;
+	}
+
+	textitem->text = text;
+	textitem->attr_list = attr_list;
+
+	pango_layout_set_text (textitem->layout, text, -1);
+
+	foo_canvas_text_apply_attributes (textitem);
+}
+
+/* Update handler for the text item */
+static void
+foo_canvas_text_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	FooCanvasText *text;
+	double x1, y1, x2, y2;
+
+	text = FOO_CANVAS_TEXT (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+	set_text_gc_foreground (text);
+	set_stipple (text, text->stipple, TRUE);
+	get_bounds (text, &x1, &y1, &x2, &y2);
+
+	foo_canvas_update_bbox (item,
+				floor (x1+.5), floor (y1+.5),
+				floor (x2+.5), floor (y2+.5));
+}
+
+/* Realize handler for the text item */
+static void
+foo_canvas_text_realize (FooCanvasItem *item)
+{
+	FooCanvasText *text;
+
+	text = FOO_CANVAS_TEXT (item);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (item);
+
+	text->gc = gdk_gc_new (item->canvas->layout.bin_window);
+}
+
+/* Unrealize handler for the text item */
+static void
+foo_canvas_text_unrealize (FooCanvasItem *item)
+{
+	FooCanvasText *text;
+
+	text = FOO_CANVAS_TEXT (item);
+
+	g_object_unref (text->gc);
+	text->gc = NULL;
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (item);
+}
+
+/* Draw handler for the text item */
+static void
+foo_canvas_text_draw (FooCanvasItem *item, GdkDrawable *drawable,
+			GdkEventExpose   *expose)
+{
+	FooCanvasText *text;
+	GdkRectangle rect;
+
+	text = FOO_CANVAS_TEXT (item);
+
+	if (!text->text)
+		return;
+
+	if (text->clip) {
+		rect.x = text->clip_cx;
+		rect.y = text->clip_cy;
+		rect.width = text->clip_cwidth;
+		rect.height = text->clip_cheight;
+
+		gdk_gc_set_clip_rectangle (text->gc, &rect);
+	}
+
+	if (text->stipple)
+		foo_canvas_set_stipple_origin (item->canvas, text->gc);
+
+
+	gdk_draw_layout (drawable, text->gc, text->cx, text->cy, text->layout);
+
+	if (text->clip)
+		gdk_gc_set_clip_rectangle (text->gc, NULL);
+}
+
+/* Point handler for the text item */
+static double
+foo_canvas_text_point (FooCanvasItem *item, double x, double y,
+			 int cx, int cy, FooCanvasItem **actual_item)
+{
+	FooCanvasText *text;
+	PangoLayoutIter *iter;
+	int x1, y1, x2, y2;
+	int dx, dy;
+	double dist, best;
+
+	text = FOO_CANVAS_TEXT (item);
+
+	*actual_item = item;
+
+	/* The idea is to build bounding rectangles for each of the lines of
+	 * text (clipped by the clipping rectangle, if it is activated) and see
+	 * whether the point is inside any of these.  If it is, we are done.
+	 * Otherwise, calculate the distance to the nearest rectangle.
+	 */
+
+	best = 1.0e36;
+
+	iter = pango_layout_get_iter (text->layout);
+	do {
+ 	        PangoRectangle log_rect;
+
+		pango_layout_iter_get_line_extents (iter, NULL, &log_rect);
+
+		if (text->clip) {
+			x1 = PANGO_PIXELS (log_rect.x);
+			y1 = PANGO_PIXELS (log_rect.y);
+			x2 = PANGO_PIXELS (log_rect.x+log_rect.width);
+			y2 = PANGO_PIXELS (log_rect.y+log_rect.height);
+
+
+			if (x1 < text->clip_cx)
+				x1 = text->clip_cx;
+
+			if (y1 < text->clip_cy)
+				y1 = text->clip_cy;
+
+			if (x2 > (text->clip_cx + text->clip_width))
+				x2 = text->clip_cx + text->clip_width;
+
+			if (y2 > (text->clip_cy + text->clip_height))
+				y2 = text->clip_cy + text->clip_height;
+
+			if ((x1 >= x2) || (y1 >= y2))
+				continue;
+		} else {
+			x1 = text->x;
+			y1 = text->y;
+			x2 = log_rect.width;
+			y2 = log_rect.height;
+		}
+
+		/* Calculate distance from point to rectangle */
+
+		if (cx < x1)
+			dx = x1 - cx;
+		else if (cx >= x2)
+			dx = cx - x2 + 1;
+		else
+			dx = 0;
+
+		if (cy < y1)
+			dy = y1 - cy;
+		else if (cy >= y2)
+			dy = cy - y2 + 1;
+		else
+			dy = 0;
+
+		if ((dx == 0) && (dy == 0)) {
+			pango_layout_iter_free(iter);
+			return 0.0;
+		}
+
+		dist = sqrt (dx * dx + dy * dy);
+		if (dist < best)
+			best = dist;
+
+	} while (pango_layout_iter_next_line(iter));
+
+	pango_layout_iter_free(iter);
+
+	return best / item->canvas->pixels_per_unit;
+}
+
+static void
+foo_canvas_text_translate (FooCanvasItem *item, double dx, double dy)
+{
+	FooCanvasText *text;
+
+	text = FOO_CANVAS_TEXT (item);
+
+	text->x += dx;
+	text->y += dy;
+}
+
+/* Bounds handler for the text item */
+static void
+foo_canvas_text_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	FooCanvasText *text;
+	double width, height;
+
+	text = FOO_CANVAS_TEXT (item);
+
+	*x1 = text->x;
+	*y1 = text->y;
+
+	if (text->clip) {
+		width = text->clip_width;
+		height = text->clip_height;
+	} else {
+		width = text->max_width / item->canvas->pixels_per_unit;
+		height = text->height / item->canvas->pixels_per_unit;
+	}
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		*x1 -= width / 2.0;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		*x1 -= width;
+		break;
+
+	default:
+		break;
+	}
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		*y1 -= height / 2.0;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		*y1 -= height;
+		break;
+
+	default:
+		break;
+	}
+
+	*x2 = *x1 + width;
+	*y2 = *y1 + height;
+}
diff --git a/libfoocanvas/foo-canvas-text.h b/libfoocanvas/foo-canvas-text.h
new file mode 100644
index 0000000..571c642
--- /dev/null
+++ b/libfoocanvas/foo-canvas-text.h
@@ -0,0 +1,168 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Text item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ * Port to Pango co-done by Gergõ Érdi <cactus cactus rulez org>
+ */
+
+#ifndef FOO_CANVAS_TEXT_H
+#define FOO_CANVAS_TEXT_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Text item for the canvas.  Text items are positioned by an anchor point and an anchor direction.
+ *
+ * A clipping rectangle may be specified for the text.  The rectangle is anchored at the text's anchor
+ * point, and is specified by clipping width and height parameters.  If the clipping rectangle is
+ * enabled, it will clip the text.
+ *
+ * In addition, x and y offset values may be specified.  These specify an offset from the anchor
+ * position.  If used in conjunction with the clipping rectangle, these could be used to implement
+ * simple scrolling of the text within the clipping rectangle.
+ *
+ * Properties marked with [*] also have _set properties associated
+ * with them, that determine if the specified value should be used
+ * instead of the default (style-defined) values
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * text			string			RW		The string of the text label
+ * markup		string			 W		A Pango markup string for the text label
+ *
+ * x			double			RW		X coordinate of anchor point
+ * y			double			RW		Y coordinate of anchor point
+ *
+ * font			string			 W		A string describing the font
+ * font_desc	        PangoFontDescription*	RW		Pointer to a PangoFontDescriptor
+ * attributes           PangoAttrList*          RW		Pointer to a Pango attribute list
+ * style		PangoStyle		RW		Pango style of font to use	[*]
+ * variant		PangoVariant		RW		Pango variant of font to use	[*]
+ * weight		int			RW		Pango weight of font to use	[*]
+ * stretch		PangoStretch		RW		Pango stretch of font to use	[*]
+ * size			int			RW		Size (in pixels) of font	[*]
+ * size_points		double			RW		Size (in points) of font
+ * scale                double                  RW              Ratio to scale font		[*]
+ *
+ * anchor		GtkAnchorType		RW		Anchor side for the text
+ * justification	GtkJustification	RW		Justification for multiline text
+ * clip_width		double			RW		Width of clip rectangle
+ * clip_height		double			RW		Height of clip rectangle
+ * clip			boolean			RW		Use clipping rectangle?
+ * x_offset		double			RW		Horizontal offset distance from anchor position
+ * y_offset		double			RW		Vertical offset distance from anchor position
+ *
+ * text_width		double			R		Used to query the width of the rendered text
+ * text_height		double			R		Used to query the rendered height of the text
+ *
+ * fill_color		string			 W		X color specification for text
+ * fill_color_gdk	GdkColor*		RW		Pointer to an allocated GdkColor
+ * fill_color_rgba	guint   		RW		RGBA value used for AA color.
+ * fill_stipple		GdkBitmap*		RW		Stipple pattern for filling the text
+ */
+
+#define FOO_TYPE_CANVAS_TEXT            (foo_canvas_text_get_type ())
+#define FOO_CANVAS_TEXT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_TEXT, FooCanvasText))
+#define FOO_CANVAS_TEXT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_TEXT, FooCanvasTextClass))
+#define FOO_IS_CANVAS_TEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_TEXT))
+#define FOO_IS_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_TEXT))
+#define FOO_CANVAS_TEXT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_TEXT, FooCanvasTextClass))
+
+
+typedef struct _FooCanvasText FooCanvasText;
+typedef struct _FooCanvasTextClass FooCanvasTextClass;
+
+typedef struct _FooCanvasTextPrivate FooCanvasTextPrivate;
+
+struct _FooCanvasText {
+	FooCanvasItem item;
+
+	PangoFontDescription *font_desc; /* Font description for text */
+	PangoAttrList *attr_list;        /* Attribute list of the text (caching) */
+	PangoUnderline underline;
+	gboolean       strikethrough;
+	int            rise;
+	double         scale;
+	
+	char *text;			/* Text to display */
+	GdkBitmap *stipple;		/* Stipple for text */
+	GdkGC *gc;			/* GC for drawing text */
+        PangoLayout *layout;            /* The PangoLayout containing the text */
+
+	gulong pixel;			/* Fill color */
+
+	double x, y;			/* Position at anchor */
+
+	double clip_width;		/* Width of optional clip rectangle */
+	double clip_height;		/* Height of optional clip rectangle */
+
+	double xofs, yofs;		/* Text offset distance from anchor position */
+
+	GtkAnchorType anchor;		/* Anchor side for text */
+	GtkJustification justification;	/* Justification for text */
+
+	int cx, cy;			/* Top-left canvas coordinates for text */
+	int clip_cx, clip_cy;		/* Top-left canvas coordinates for clip rectangle */
+	int clip_cwidth, clip_cheight;	/* Size of clip rectangle in pixels */
+	int max_width;			/* Maximum width of text lines */
+	int height;			/* Rendered text height in pixels */
+
+        guint32 rgba;			/* RGBA color for text */ /*AA*/
+
+	guint clip : 1;			/* Use clip rectangle? */
+
+	guint underline_set : 1;        /* Apply specified underline style? */
+	guint strike_set    : 1;        /* Apply specified strikethrough style? */
+	guint rise_set      : 1;        /* Apply specified ascension/descension? */
+
+	guint scale_set     : 1;        /* Apply specified font scaling ratio? */
+
+	FooCanvasTextPrivate *priv;	
+};
+
+struct _FooCanvasTextClass {
+	FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_text_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-util.c b/libfoocanvas/foo-canvas-util.c
new file mode 100644
index 0000000..879db57
--- /dev/null
+++ b/libfoocanvas/foo-canvas-util.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Miscellaneous utility functions for the FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <glib.h>
+#include <math.h>
+#include "foo-canvas.h"
+#include "foo-canvas-util.h"
+
+/*
+ * Ok, so some systems require magic incantations for M_PI to be defined.
+ * It's not important enough to worry about.
+ */
+#ifndef M_PI
+#define M_PI 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117
+#endif
+
+/**
+ * foo_canvas_points_new:
+ * @num_points: The number of points to allocate space for in the array.
+ * 
+ * Creates a structure that should be used to pass an array of points to
+ * items.
+ * 
+ * Return value: A newly-created array of points.  It should be filled in
+ * by the user.
+ **/
+FooCanvasPoints *
+foo_canvas_points_new (int num_points)
+{
+	FooCanvasPoints *points;
+
+	g_return_val_if_fail (num_points > 1, NULL);
+
+	points = g_new (FooCanvasPoints, 1);
+	points->num_points = num_points;
+	points->coords = g_new (double, 2 * num_points);
+	points->ref_count = 1;
+
+	return points;
+}
+
+/**
+ * foo_canvas_points_ref:
+ * @points: A canvas points structure.
+ * 
+ * Increases the reference count of the specified points structure.
+ * 
+ * Return value: The canvas points structure itself.
+ **/
+FooCanvasPoints *
+foo_canvas_points_ref (FooCanvasPoints *points)
+{
+	g_return_val_if_fail (points != NULL, NULL);
+
+	points->ref_count += 1;
+	return points;
+}
+
+/**
+ * foo_canvas_points_free:
+ * @points: A canvas points structure.
+ * 
+ * Decreases the reference count of the specified points structure.  If it
+ * reaches zero, then the structure is freed.
+ **/
+void
+foo_canvas_points_free (FooCanvasPoints *points)
+{
+	g_return_if_fail (points != NULL);
+
+	points->ref_count -= 1;
+	if (points->ref_count == 0) {
+		g_free (points->coords);
+		g_free (points);
+	}
+}
+
+/**
+ * foo_canvas_get_miter_points:
+ * @x1: X coordinate of the first point
+ * @y1: Y coordinate of the first point
+ * @x2: X coordinate of the second (angle) point
+ * @y2: Y coordinate of the second (angle) point
+ * @x3: X coordinate of the third point
+ * @y3: Y coordinate of the third point
+ * @width: Width of the line
+ * @mx1: The X coordinate of the first miter point is returned here.
+ * @my1: The Y coordinate of the first miter point is returned here.
+ * @mx2: The X coordinate of the second miter point is returned here.
+ * @my2: The Y coordinate of the second miter point is returned here.
+ * 
+ * Given three points forming an angle, computes the coordinates of the inside
+ * and outside points of the mitered corner formed by a line of a given width at
+ * that angle.
+ * 
+ * Return value: FALSE if the angle is less than 11 degrees (this is the same
+ * threshold as X uses.  If this occurs, the return points are not modified.
+ * Otherwise, returns TRUE.
+ **/
+int
+foo_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+			     double width,
+			     double *mx1, double *my1, double *mx2, double *my2)
+{
+	double theta1;		/* angle of segment p2-p1 */
+	double theta2;		/* angle of segment p2-p3 */
+	double theta;		/* angle between line segments */
+	double theta3;		/* angle that bisects theta1 and theta2 and points to p1 */
+	double dist;		/* distance of miter points from p2 */
+	double dx, dy;		/* x and y offsets corresponding to dist */
+
+	double ELEVEN_DEGREES = 11.0 * M_PI / 180.0;
+
+	/* Degenerate cases.  */
+	if ((x1 == x2 && y1 == y2) || (x2 == x3 && y2 == y3))
+		return FALSE;
+
+	theta1 = atan2 (y1 - y2, x1 - x2);
+	theta2 = atan2 (y3 - y2, x3 - x2);
+	theta = theta1 - theta2;
+
+	/* Normalize to (-pi; pi].  */
+	if (theta > M_PI)
+		theta -= 2.0 * M_PI;
+	else if (theta <= -M_PI)
+		theta += 2.0 * M_PI;
+
+	if (fabs (theta) < ELEVEN_DEGREES)
+		return FALSE;
+
+	dist = fabs (0.5 * width / sin (0.5 * theta));
+
+	theta3 = (theta1 + theta2) / 2.0;
+	if (sin (theta3 - theta1) > 0.0)
+		theta3 += M_PI;
+
+	dx = dist * cos (theta3);
+	dy = dist * sin (theta3);
+
+	*mx1 = x2 + dx;
+	*mx2 = x2 - dx;
+	*my1 = y2 + dy;
+	*my2 = y2 - dy;
+
+	return TRUE;
+}
+
+/**
+ * foo_canvas_get_butt_points:
+ * @x1: X coordinate of first point in the line
+ * @y1: Y cooordinate of first point in the line
+ * @x2: X coordinate of second point (endpoint) of the line
+ * @y2: Y coordinate of second point (endpoint) of the line
+ * @width: Width of the line
+ * @project: Whether the butt points should project out by width/2 distance
+ * @bx1: X coordinate of first butt point is returned here
+ * @by1: Y coordinate of first butt point is returned here
+ * @bx2: X coordinate of second butt point is returned here
+ * @by2: Y coordinate of second butt point is returned here
+ * 
+ * Computes the butt points of a line segment.
+ **/
+void
+foo_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+			      double width, int project,
+			      double *bx1, double *by1, double *bx2, double *by2)
+{
+	double length;
+	double dx, dy;
+
+	width *= 0.5;
+	dx = x2 - x1;
+	dy = y2 - y1;
+	length = sqrt (dx * dx + dy * dy);
+
+	if (length < FOO_CANVAS_EPSILON) {
+		*bx1 = *bx2 = x2;
+		*by1 = *by2 = y2;
+	} else {
+		dx = -width * (y2 - y1) / length;
+		dy = width * (x2 - x1) / length;
+
+		*bx1 = x2 + dx;
+		*bx2 = x2 - dx;
+		*by1 = y2 + dy;
+		*by2 = y2 - dy;
+
+		if (project) {
+			*bx1 += dy;
+			*bx2 += dy;
+			*by1 -= dx;
+			*by2 -= dx;
+		}
+	}
+}
+
+/**
+ * foo_canvas_polygon_to_point:
+ * @poly: Vertices of the polygon.  X coordinates are in the even indices, and Y
+ * coordinates are in the odd indices
+ * @num_points: Number of points in the polygon
+ * @x: X coordinate of the point
+ * @y: Y coordinate of the point
+ * 
+ * Computes the distance between a point and a polygon.
+ * 
+ * Return value: The distance from the point to the polygon, or zero if the
+ * point is inside the polygon.
+ **/
+double
+foo_canvas_polygon_to_point (double *poly, int num_points, double x, double y)
+{
+	double best;
+	int intersections;
+	int i;
+	double *p;
+	double dx, dy;
+
+	/* Iterate through all the edges in the polygon, updating best and intersections.
+	 *
+	 * When computing intersections, include left X coordinate of line within its range, but not
+	 * Y coordinate.  Otherwise if the point lies exactly below a vertex we'll count it as two
+	 * intersections.
+	 */
+
+	best = 1.0e36;
+	if (poly == NULL)
+		return best;
+
+	intersections = 0;
+
+	for (i = num_points, p = poly; i > 1; i--, p += 2) {
+		double px, py, dist;
+
+		/* Compute the point on the current edge closest to the point and update the
+		 * intersection count.  This must be done separately for vertical edges, horizontal
+		 * edges, and others.
+		 */
+
+		if (p[2] == p[0]) {
+			/* Vertical edge */
+
+			px = p[0];
+
+			if (p[1] >= p[3]) {
+				py = MIN (p[1], y);
+				py = MAX (py, p[3]);
+			} else {
+				py = MIN (p[3], y);
+				py = MAX (py, p[1]);
+			}
+		} else if (p[3] == p[1]) {
+			/* Horizontal edge */
+
+			py = p[1];
+
+			if (p[0] >= p[2]) {
+				px = MIN (p[0], x);
+				px = MAX (px, p[2]);
+
+				if ((y < py) && (x < p[0]) && (x >= p[2]))
+					intersections++;
+			} else {
+				px = MIN (p[2], x);
+				px = MAX (px, p[0]);
+
+				if ((y < py) && (x < p[2]) && (x >= p[0]))
+					intersections++;
+			}
+		} else {
+			double m1, b1, m2, b2;
+			int lower;
+
+			/* Diagonal edge.  Convert the edge to a line equation (y = m1*x + b1), then
+			 * compute a line perpendicular to this edge but passing through the point,
+			 * (y = m2*x + b2).
+			 */
+
+			m1 = (p[3] - p[1]) / (p[2] - p[0]);
+			b1 = p[1] - m1 * p[0];
+
+			m2 = -1.0 / m1;
+			b2 = y - m2 * x;
+
+			px = (b2 - b1) / (m1 - m2);
+			py = m1 * px + b1;
+
+			if (p[0] > p[2]) {
+				if (px > p[0]) {
+					px = p[0];
+					py = p[1];
+				} else if (px < p[2]) {
+					px = p[2];
+					py = p[3];
+				}
+			} else {
+				if (px > p[2]) {
+					px = p[2];
+					py = p[3];
+				} else if (px < p[0]) {
+					px = p[0];
+					py = p[1];
+				}
+			}
+
+			lower = (m1 * x + b1) > y;
+
+			if (lower && (x >= MIN (p[0], p[2])) && (x < MAX (p[0], p[2])))
+				intersections++;
+		}
+
+		/* Compute the distance to the closest point, and see if that is the best so far */
+
+		dx = x - px;
+		dy = y - py;
+		dist = sqrt (dx * dx + dy * dy);
+		if (dist < best)
+			best = dist;
+	}
+
+	/* We've processed all the points.  If the number of intersections is odd, the point is
+	 * inside the polygon.
+	 */
+
+	if (intersections & 0x1)
+		return 0.0;
+	else
+		return best;
+}
+
+/**
+ * foo_canvas_item_reset_bounds:
+ * @item: A canvas item
+ * 
+ * Resets the bounding box of a canvas item to an empty rectangle.
+ **/
+void
+foo_canvas_item_reset_bounds (FooCanvasItem *item)
+{
+	item->x1 = 0.0;
+	item->y1 = 0.0;
+	item->x2 = 0.0;
+	item->y2 = 0.0;
+}
+
+/**
+ * foo_canvas_update_bbox:
+ * @canvas: the canvas needing update
+ * @x1: Left coordinate of the new bounding box
+ * @y1: Top coordinate of the new bounding box
+ * @x2: Right coordinate of the new bounding box
+ * @y2: Bottom coordinate of the new bounding box
+ *
+ * Sets the bbox to the new value, requesting full repaint.
+ **/
+void
+foo_canvas_update_bbox (FooCanvasItem *item, int x1, int y1, int x2, int y2)
+{
+	foo_canvas_item_request_redraw (item);
+	item->x1 = x1;
+	item->y1 = y1;
+	item->x2 = x2;
+	item->y2 = y2;
+	foo_canvas_item_request_redraw (item);
+}
+
diff --git a/libfoocanvas/foo-canvas-util.h b/libfoocanvas/foo-canvas-util.h
new file mode 100644
index 0000000..63fc3ac
--- /dev/null
+++ b/libfoocanvas/foo-canvas-util.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Miscellaneous utility functions for the FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_UTIL_H
+#define FOO_CANVAS_UTIL_H
+
+
+G_BEGIN_DECLS
+
+
+/* This structure defines an array of points.  X coordinates are stored in the even-numbered
+ * indices, and Y coordinates are stored in the odd-numbered indices.  num_points indicates the
+ * number of points, so the array is 2*num_points elements big.
+ */
+typedef struct {
+	double *coords;
+	int num_points;
+	int ref_count;
+} FooCanvasPoints;
+
+
+/* Allocate a new FooCanvasPoints structure with enough space for the specified number of points */
+FooCanvasPoints *foo_canvas_points_new (int num_points);
+
+/* Increate ref count */
+FooCanvasPoints *foo_canvas_points_ref (FooCanvasPoints *points);
+#define foo_canvas_points_unref foo_canvas_points_free
+
+/* Decrease ref count and free structure if it has reached zero */
+void foo_canvas_points_free (FooCanvasPoints *points);
+
+/* Given three points forming an angle, compute the coordinates of the inside and outside points of
+ * the mitered corner formed by a line of a given width at that angle.
+ *
+ * If the angle is less than 11 degrees, then FALSE is returned and the return points are not
+ * modified.  Otherwise, TRUE is returned.
+ */
+int foo_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+				   double width,
+				   double *mx1, double *my1, double *mx2, double *my2);
+
+/* Compute the butt points of a line segment.  If project is FALSE, then the results are as follows:
+ *
+ *            -------------------* (bx1, by1)
+ *                               |
+ *   (x1, y1) *------------------* (x2, y2)
+ *                               |
+ *            -------------------* (bx2, by2)
+ *
+ * that is, the line is not projected beyond (x2, y2).  If project is TRUE, then the results are as
+ * follows:
+ *
+ *            -------------------* (bx1, by1)
+ *                      (x2, y2) |
+ *   (x1, y1) *-------------*    |
+ *                               |
+ *            -------------------* (bx2, by2)
+ */
+void foo_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+				   double width, int project,
+				   double *bx1, double *by1, double *bx2, double *by2);
+
+/* Calculate the distance from a polygon to a point.  The polygon's X coordinates are in the even
+ * indices of the poly array, and the Y coordinates are in the odd indices.
+ */
+double foo_canvas_polygon_to_point (double *poly, int num_points, double x, double y);
+
+
+void foo_canvas_item_reset_bounds (FooCanvasItem *item);
+
+/* Sets the bbox to the new value, requesting full repaint. */
+void foo_canvas_update_bbox (FooCanvasItem *item, int x1, int y1, int x2, int y2);
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas-widget.c b/libfoocanvas/foo-canvas-widget.c
new file mode 100644
index 0000000..9e5d337
--- /dev/null
+++ b/libfoocanvas/foo-canvas-widget.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Widget item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <glib-object.h>
+#include "foo-canvas-widget.h"
+
+enum {
+	PROP_0,
+	PROP_WIDGET,
+	PROP_X,
+	PROP_Y,
+	PROP_WIDTH,
+	PROP_HEIGHT,
+	PROP_ANCHOR,
+	PROP_SIZE_PIXELS
+};
+
+
+static void foo_canvas_widget_class_init (FooCanvasWidgetClass *klass);
+static void foo_canvas_widget_init       (FooCanvasWidget      *witem);
+static void foo_canvas_widget_destroy    (GtkObject              *object);
+static void foo_canvas_widget_get_property (GObject            *object,
+					      guint               param_id,
+					      GValue             *value,
+					      GParamSpec         *pspec);
+static void foo_canvas_widget_set_property (GObject            *object,
+					      guint               param_id,
+					      const GValue       *value,
+					      GParamSpec         *pspec);
+
+static void   foo_canvas_widget_update      (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+static double foo_canvas_widget_point       (FooCanvasItem *item, double x, double y,
+					       int cx, int cy, FooCanvasItem **actual_item);
+static void   foo_canvas_widget_translate   (FooCanvasItem *item, double dx, double dy);
+static void   foo_canvas_widget_bounds      (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+static void foo_canvas_widget_draw (FooCanvasItem *item,
+				      GdkDrawable *drawable,
+				      GdkEventExpose   *event);
+static void foo_canvas_widget_map   (FooCanvasItem *item);
+static void foo_canvas_widget_unmap (FooCanvasItem *item);
+
+static FooCanvasItemClass *parent_class;
+
+G_DEFINE_TYPE (FooCanvasWidget, foo_canvas_widget, FOO_TYPE_CANVAS_ITEM)
+
+static void
+foo_canvas_widget_class_init (FooCanvasWidgetClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	FooCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (FooCanvasItemClass *) klass;
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_widget_set_property;
+	gobject_class->get_property = foo_canvas_widget_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDGET,
+                 g_param_spec_object ("widget", NULL, NULL,
+                                      GTK_TYPE_WIDGET,
+                                      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X,
+                 g_param_spec_double ("x", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y,
+                 g_param_spec_double ("y", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH,
+                 g_param_spec_double ("width", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT,
+                 g_param_spec_double ("height", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ANCHOR,
+                 g_param_spec_enum ("anchor", NULL, NULL,
+                                    GTK_TYPE_ANCHOR_TYPE,
+                                    GTK_ANCHOR_NW,
+                                    G_PARAM_READWRITE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_SIZE_PIXELS,
+                 g_param_spec_boolean ("size-pixels", NULL, NULL,
+				       FALSE,
+				       G_PARAM_READWRITE));
+
+	object_class->destroy = foo_canvas_widget_destroy;
+
+	item_class->update = foo_canvas_widget_update;
+	item_class->point = foo_canvas_widget_point;
+	item_class->translate = foo_canvas_widget_translate;
+	item_class->bounds = foo_canvas_widget_bounds;
+	item_class->draw = foo_canvas_widget_draw;
+	item_class->map   = foo_canvas_widget_map;
+	item_class->unmap = foo_canvas_widget_unmap;
+}
+
+static void
+foo_canvas_widget_init (FooCanvasWidget *witem)
+{
+	witem->x = 0.0;
+	witem->y = 0.0;
+	witem->width = 0.0;
+	witem->height = 0.0;
+	witem->anchor = GTK_ANCHOR_NW;
+	witem->size_pixels = FALSE;
+}
+
+static void
+foo_canvas_widget_destroy (GtkObject *object)
+{
+	FooCanvasWidget *witem;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_WIDGET (object));
+
+	witem = FOO_CANVAS_WIDGET (object);
+
+	if (witem->widget && !witem->in_destroy) {
+		g_signal_handler_disconnect (G_OBJECT (witem->widget), witem->destroy_id);
+		gtk_widget_destroy (witem->widget);
+		witem->widget = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+recalc_bounds (FooCanvasWidget *witem)
+{
+	FooCanvasItem *item;
+	double wx, wy;
+
+	item = FOO_CANVAS_ITEM (witem);
+
+	/* Get world coordinates */
+
+	wx = witem->x;
+	wy = witem->y;
+	foo_canvas_item_i2w (item, &wx, &wy);
+
+	/* Get canvas pixel coordinates */
+
+	foo_canvas_w2c (item->canvas, wx, wy, &witem->cx, &witem->cy);
+
+	/* Anchor widget item */
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		witem->cx -= witem->cwidth / 2;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		witem->cx -= witem->cwidth;
+		break;
+
+        default:
+                break;
+	}
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		witem->cy -= witem->cheight / 2;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		witem->cy -= witem->cheight;
+		break;
+
+        default:
+                break;
+	}
+
+	/* Bounds */
+
+	item->x1 = witem->cx;
+	item->y1 = witem->cy;
+	item->x2 = witem->cx + witem->cwidth;
+	item->y2 = witem->cy + witem->cheight;
+
+	if (witem->widget)
+		gtk_layout_move (GTK_LAYOUT (item->canvas), witem->widget,
+				 witem->cx,
+				 witem->cy);
+}
+
+static void
+do_destroy (GtkObject *object, gpointer data)
+{
+	FooCanvasWidget *witem;
+
+	witem = data;
+
+	witem->in_destroy = TRUE;
+
+	gtk_object_destroy (data);
+}
+
+static void
+foo_canvas_widget_set_property (GObject            *object,
+				  guint               param_id,
+				  const GValue       *value,
+				  GParamSpec         *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasWidget *witem;
+	GObject *obj;
+	int update;
+	int calc_bounds;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_WIDGET (object));
+
+	item = FOO_CANVAS_ITEM (object);
+	witem = FOO_CANVAS_WIDGET (object);
+
+	update = FALSE;
+	calc_bounds = FALSE;
+
+	switch (param_id) {
+	case PROP_WIDGET:
+		if (witem->widget) {
+			g_signal_handler_disconnect (G_OBJECT (witem->widget), witem->destroy_id);
+			gtk_container_remove (GTK_CONTAINER (item->canvas), witem->widget);
+		}
+
+		obj = g_value_get_object (value);
+		if (obj) {
+			witem->widget = GTK_WIDGET (obj);
+			witem->destroy_id = g_signal_connect (G_OBJECT (obj),
+				"destroy", G_CALLBACK (do_destroy), witem);
+			gtk_layout_put (GTK_LAYOUT (item->canvas), witem->widget,
+					witem->cx + item->canvas->zoom_xofs,
+					witem->cy + item->canvas->zoom_yofs);
+		}
+
+		update = TRUE;
+		break;
+
+	case PROP_X:
+	        if (witem->x != g_value_get_double (value))
+		{
+		        witem->x = g_value_get_double (value);
+			calc_bounds = TRUE;
+		}
+		break;
+
+	case PROP_Y:
+	        if (witem->y != g_value_get_double (value))
+		{
+		        witem->y = g_value_get_double (value);
+			calc_bounds = TRUE;
+		}
+		break;
+
+	case PROP_WIDTH:
+	        if (witem->width != fabs (g_value_get_double (value)))
+		{
+		        witem->width = fabs (g_value_get_double (value));
+			update = TRUE;
+		}
+		break;
+
+	case PROP_HEIGHT:
+	        if (witem->height != fabs (g_value_get_double (value)))
+		{
+		        witem->height = fabs (g_value_get_double (value));
+			update = TRUE;
+		}
+		break;
+
+	case PROP_ANCHOR:
+	        if (witem->anchor != (GtkAnchorType)g_value_get_enum (value))
+		{
+		        witem->anchor = g_value_get_enum (value);
+			update = TRUE;
+		}
+		break;
+
+	case PROP_SIZE_PIXELS:
+	        if (witem->size_pixels != g_value_get_boolean (value))
+		{
+		        witem->size_pixels = g_value_get_boolean (value);
+			update = TRUE;
+		}
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+
+	if (update)
+		(* FOO_CANVAS_ITEM_GET_CLASS (item)->update) (item, 0, 0, 0);
+
+	if (calc_bounds)
+		recalc_bounds (witem);
+}
+
+static void
+foo_canvas_widget_get_property (GObject            *object,
+				  guint               param_id,
+				  GValue             *value,
+				  GParamSpec         *pspec)
+{
+	FooCanvasWidget *witem;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (FOO_IS_CANVAS_WIDGET (object));
+
+	witem = FOO_CANVAS_WIDGET (object);
+
+	switch (param_id) {
+	case PROP_WIDGET:
+		g_value_set_object (value, (GObject *) witem->widget);
+		break;
+
+	case PROP_X:
+		g_value_set_double (value, witem->x);
+		break;
+
+	case PROP_Y:
+		g_value_set_double (value, witem->y);
+		break;
+
+	case PROP_WIDTH:
+		g_value_set_double (value, witem->width);
+		break;
+
+	case PROP_HEIGHT:
+		g_value_set_double (value, witem->height);
+		break;
+
+	case PROP_ANCHOR:
+		g_value_set_enum (value, witem->anchor);
+		break;
+
+	case PROP_SIZE_PIXELS:
+		g_value_set_boolean (value, witem->size_pixels);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+foo_canvas_widget_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	FooCanvasWidget *witem;
+
+	witem = FOO_CANVAS_WIDGET (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+	if (witem->widget) {
+		if (witem->size_pixels) {
+			witem->cwidth = (int) (witem->width + 0.5);
+			witem->cheight = (int) (witem->height + 0.5);
+		} else {
+			witem->cwidth = (int) (witem->width * item->canvas->pixels_per_unit + 0.5);
+			witem->cheight = (int) (witem->height * item->canvas->pixels_per_unit + 0.5);
+		}
+
+		gtk_widget_set_usize (witem->widget, witem->cwidth, witem->cheight);
+	} else {
+		witem->cwidth = 0.0;
+		witem->cheight = 0.0;
+	}
+
+	recalc_bounds (witem);
+}
+
+static void
+foo_canvas_widget_draw (FooCanvasItem *item,
+			  GdkDrawable *drawable,
+			  GdkEventExpose *event)
+{
+#if 0
+	FooCanvasWidget *witem;
+
+	witem = FOO_CANVAS_WIDGET (item);
+
+	if (witem->widget)
+		gtk_widget_queue_draw (witem->widget);
+#endif
+}
+static void
+foo_canvas_widget_map (FooCanvasItem *item)
+{
+	FooCanvasWidget *witem = FOO_CANVAS_WIDGET (item);
+	if (parent_class->map)
+		(* parent_class->map) (item);
+	if (witem->widget && GTK_WIDGET_VISIBLE (witem->widget))
+		gtk_widget_map (witem->widget);
+}
+
+static void
+foo_canvas_widget_unmap (FooCanvasItem *item)
+{
+	FooCanvasWidget *witem = FOO_CANVAS_WIDGET (item);
+	if (parent_class->unmap)
+		(* parent_class->unmap) (item);
+	gtk_widget_unmap (witem->widget);
+}
+
+static double
+foo_canvas_widget_point (FooCanvasItem *item, double x, double y,
+			   int cx, int cy, FooCanvasItem **actual_item)
+{
+	FooCanvasWidget *witem;
+	double x1, y1, x2, y2;
+	double dx, dy;
+
+	witem = FOO_CANVAS_WIDGET (item);
+
+	*actual_item = item;
+
+	foo_canvas_c2w (item->canvas, witem->cx, witem->cy, &x1, &y1);
+
+	x2 = x1 + (witem->cwidth - 1) / item->canvas->pixels_per_unit;
+	y2 = y1 + (witem->cheight - 1) / item->canvas->pixels_per_unit;
+
+	/* Is point inside widget bounds? */
+
+	if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2))
+		return 0.0;
+
+	/* Point is outside widget bounds */
+
+	if (x < x1)
+		dx = x1 - x;
+	else if (x > x2)
+		dx = x - x2;
+	else
+		dx = 0.0;
+
+	if (y < y1)
+		dy = y1 - y;
+	else if (y > y2)
+		dy = y - y2;
+	else
+		dy = 0.0;
+
+	return sqrt (dx * dx + dy * dy);
+}
+
+static void
+foo_canvas_widget_translate (FooCanvasItem *item, double dx, double dy)
+{
+	FooCanvasWidget *witem;
+
+	witem = FOO_CANVAS_WIDGET (item);
+
+	witem->x += dx;
+	witem->y += dy;
+}
+
+
+static void
+foo_canvas_widget_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	FooCanvasWidget *witem;
+
+	witem = FOO_CANVAS_WIDGET (item);
+
+	*x1 = witem->x;
+	*y1 = witem->y;
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		*x1 -= witem->width / 2.0;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		*x1 -= witem->width;
+		break;
+
+        default:
+                break;
+	}
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		*y1 -= witem->height / 2.0;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		*y1 -= witem->height;
+		break;
+
+        default:
+                break;
+	}
+
+	*x2 = *x1 + witem->width;
+	*y2 = *y1 + witem->height;
+}
diff --git a/libfoocanvas/foo-canvas-widget.h b/libfoocanvas/foo-canvas-widget.h
new file mode 100644
index 0000000..09612ee
--- /dev/null
+++ b/libfoocanvas/foo-canvas-widget.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Widget item type for FooCanvas widget
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef FOO_CANVAS_WIDGET_H
+#define FOO_CANVAS_WIDGET_H
+
+
+#include <libfoocanvas/foo-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Widget item for canvas.  The widget is positioned with respect to an anchor point.
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * widget		GtkWidget*		RW		Pointer to the widget
+ * x			double			RW		X coordinate of anchor point
+ * y			double			RW		Y coordinate of anchor point
+ * width		double			RW		Width of widget (see below)
+ * height		double			RW		Height of widget (see below)
+ * anchor		GtkAnchorType		RW		Anchor side for widget
+ * size_pixels		boolean			RW		Specifies whether the widget size
+ *								is specified in pixels or canvas units.
+ *								If it is in pixels, then the widget will not
+ *								be scaled when the canvas zoom factor changes.
+ *								Otherwise, it will be scaled.
+ */
+
+
+#define FOO_TYPE_CANVAS_WIDGET            (foo_canvas_widget_get_type ())
+#define FOO_CANVAS_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_WIDGET, FooCanvasWidget))
+#define FOO_CANVAS_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_WIDGET, FooCanvasWidgetClass))
+#define FOO_IS_CANVAS_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_WIDGET))
+#define FOO_IS_CANVAS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_WIDGET))
+#define FOO_CANVAS_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_WIDGET, FooCanvasWidgetClass))
+
+
+typedef struct _FooCanvasWidget FooCanvasWidget;
+typedef struct _FooCanvasWidgetClass FooCanvasWidgetClass;
+
+struct _FooCanvasWidget {
+	FooCanvasItem item;
+
+	GtkWidget *widget;		/* The child widget */
+
+	double x, y;			/* Position at anchor */
+	double width, height;		/* Dimensions of widget */
+	GtkAnchorType anchor;		/* Anchor side for widget */
+
+	int cx, cy;			/* Top-left canvas coordinates for widget */
+	int cwidth, cheight;		/* Size of widget in pixels */
+
+	guint destroy_id;		/* Signal connection id for destruction of child widget */
+
+	guint size_pixels : 1;		/* Is size specified in (unchanging) pixels or units (get scaled)? */
+	guint in_destroy : 1;		/* Is child widget being destroyed? */
+};
+
+struct _FooCanvasWidgetClass {
+	FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_widget_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/foo-canvas.c b/libfoocanvas/foo-canvas.c
new file mode 100644
index 0000000..e666f8b
--- /dev/null
+++ b/libfoocanvas/foo-canvas.c
@@ -0,0 +1,3999 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/*
+ * FooCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph gimp org>
+ */
+
+/*
+ * TO-DO list for the canvas:
+ *
+ * - Allow to specify whether FooCanvasImage sizes are in units or pixels (scale or don't scale).
+ *
+ * - Implement a flag for foo_canvas_item_reparent() that tells the function to keep the item
+ *   visually in the same place, that is, to keep it in the same place with respect to the canvas
+ *   origin.
+ *
+ * - GC put functions for items.
+ *
+ * - Widget item (finish it).
+ *
+ * - GList *foo_canvas_gimme_all_items_contained_in_this_area (FooCanvas *canvas, Rectangle area);
+ *
+ * - Retrofit all the primitive items with microtile support.
+ *
+ * - Curve support for line item.
+ *
+ * - Arc item (Havoc has it; to be integrated in FooCanvasEllipse).
+ *
+ * - Sane font handling API.
+ *
+ * - Get_arg methods for items:
+ *   - How to fetch the outline width and know whether it is in pixels or units?
+ */
+
+#include <config.h>
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <gdk/gdkprivate.h>
+#include <gtk/gtk.h>
+#include "foo-canvas.h"
+#include "foo-canvas-i18n.h"
+
+#include "foo-canvas-marshal.h"
+
+static void foo_canvas_request_update (FooCanvas      *canvas);
+static void group_add                   (FooCanvasGroup *group,
+					 FooCanvasItem  *item);
+static void group_remove                (FooCanvasGroup *group,
+					 FooCanvasItem  *item);
+static void redraw_and_repick_if_mapped (FooCanvasItem *item);
+
+/*** FooCanvasItem ***/
+
+/* Some convenience stuff */
+#define GCI_UPDATE_MASK (FOO_CANVAS_UPDATE_REQUESTED | FOO_CANVAS_UPDATE_DEEP)
+#define GCI_EPSILON 1e-18
+
+enum {
+	ITEM_PROP_0,
+	ITEM_PROP_PARENT,
+	ITEM_PROP_VISIBLE
+};
+
+enum {
+	ITEM_EVENT,
+	ITEM_LAST_SIGNAL
+};
+
+static void foo_canvas_item_class_init     (FooCanvasItemClass *klass);
+static void foo_canvas_item_init           (FooCanvasItem      *item);
+static int  emit_event                       (FooCanvas *canvas, GdkEvent *event);
+
+static guint item_signals[ITEM_LAST_SIGNAL];
+
+static GtkObjectClass *item_parent_class;
+
+static gpointer accessible_item_parent_class;
+static gpointer accessible_parent_class;
+
+
+/**
+ * foo_canvas_item_get_type:
+ *
+ * Registers the &FooCanvasItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &FooCanvasItem class.
+ **/
+GType
+foo_canvas_item_get_type (void)
+{
+	static GType canvas_item_type = 0;
+
+	if (!canvas_item_type) {
+		static const GTypeInfo canvas_item_info = {
+			sizeof (FooCanvasItemClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) foo_canvas_item_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (FooCanvasItem),
+			0,              /* n_preallocs */
+			(GInstanceInitFunc) foo_canvas_item_init
+		};
+
+		canvas_item_type = g_type_register_static (gtk_object_get_type (),
+							   "FooCanvasItem",
+							   &canvas_item_info,
+							   0);
+	}
+
+	return canvas_item_type;
+}
+
+/* Object initialization function for FooCanvasItem */
+static void
+foo_canvas_item_init (FooCanvasItem *item)
+{
+	item->object.flags |= FOO_CANVAS_ITEM_VISIBLE;
+}
+
+/**
+ * foo_canvas_item_new:
+ * @parent: The parent group for the new item.
+ * @type: The object type of the item.
+ * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
+ * used to configure the item.  For example, "fill_color", "black",
+ * "width_units", 5.0, NULL.
+ * @Varargs:
+ *
+ * Creates a new canvas item with @parent as its parent group.  The item is
+ * created at the top of its parent's stack, and starts up as visible.  The item
+ * is of the specified @type, for example, it can be
+ * foo_canvas_rect_get_type().  The list of object arguments/value pairs is
+ * used to configure the item.
+ *
+ * Return value: The newly-created item.
+ **/
+FooCanvasItem *
+foo_canvas_item_new (FooCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
+{
+	FooCanvasItem *item;
+	va_list args;
+
+	g_return_val_if_fail (FOO_IS_CANVAS_GROUP (parent), NULL);
+	g_return_val_if_fail (g_type_is_a (type, foo_canvas_item_get_type ()), NULL);
+
+	item = FOO_CANVAS_ITEM (g_object_new (type, NULL));
+
+	va_start (args, first_arg_name);
+	foo_canvas_item_construct (item, parent, first_arg_name, args);
+	va_end (args);
+
+	return item;
+}
+
+
+/* Performs post-creation operations on a canvas item (adding it to its parent
+ * group, etc.)
+ */
+static void
+item_post_create_setup (FooCanvasItem *item)
+{
+	group_add (FOO_CANVAS_GROUP (item->parent), item);
+
+	redraw_and_repick_if_mapped (item);
+}
+
+/* Set_property handler for canvas items */
+static void
+foo_canvas_item_set_property (GObject *gobject, guint param_id,
+			      const GValue *value, GParamSpec *pspec)
+{
+	FooCanvasItem *item;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (gobject));
+
+	item = FOO_CANVAS_ITEM (gobject);
+
+	switch (param_id) {
+	case ITEM_PROP_PARENT:
+		if (item->parent != NULL) {
+		    g_warning ("Cannot set `parent' argument after item has "
+			       "already been constructed.");
+		} else if (g_value_get_object (value)) {
+			item->parent = FOO_CANVAS_ITEM (g_value_get_object (value));
+			item->canvas = item->parent->canvas;
+			item_post_create_setup (item);
+		}
+		break;
+	case ITEM_PROP_VISIBLE:
+		if (g_value_get_boolean (value)) {
+			foo_canvas_item_show (item);
+		} else {
+			foo_canvas_item_hide (item);
+		}
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/* Get_property handler for canvas items */
+static void
+foo_canvas_item_get_property (GObject *gobject, guint param_id,
+			      GValue *value, GParamSpec *pspec)
+{
+	FooCanvasItem *item;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (gobject));
+
+	item = FOO_CANVAS_ITEM (gobject);
+
+	switch (param_id) {
+	case ITEM_PROP_VISIBLE:
+		g_value_set_boolean (value, item->object.flags & FOO_CANVAS_ITEM_VISIBLE);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/**
+ * foo_canvas_item_construct:
+ * @item: An unconstructed canvas item.
+ * @parent: The parent group for the item.
+ * @first_arg_name: The name of the first argument for configuring the item.
+ * @args: The list of arguments used to configure the item.
+ *
+ * Constructs a canvas item; meant for use only by item implementations.
+ **/
+void
+foo_canvas_item_construct (FooCanvasItem *item, FooCanvasGroup *parent,
+			   const gchar *first_arg_name, va_list args)
+{
+	g_return_if_fail (FOO_IS_CANVAS_GROUP (parent));
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	item->parent = FOO_CANVAS_ITEM (parent);
+	item->canvas = item->parent->canvas;
+
+	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+	item_post_create_setup (item);
+}
+
+
+static void
+redraw_and_repick_if_mapped (FooCanvasItem *item)
+{
+	if (item->object.flags & FOO_CANVAS_ITEM_MAPPED) {
+		foo_canvas_item_request_redraw (item);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+/* Dispose handler for canvas items */
+static void
+foo_canvas_item_dispose (GObject *object)
+{
+	FooCanvasItem *item;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (object));
+
+	item = FOO_CANVAS_ITEM (object);
+
+	if (item->canvas) {
+		foo_canvas_item_request_redraw (item);
+
+		/* Make the canvas forget about us */
+
+		if (item == item->canvas->current_item) {
+			item->canvas->current_item = NULL;
+			item->canvas->need_repick = TRUE;
+		}
+
+		if (item == item->canvas->new_current_item) {
+			item->canvas->new_current_item = NULL;
+			item->canvas->need_repick = TRUE;
+		}
+
+		if (item == item->canvas->grabbed_item) {
+			GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
+			item->canvas->grabbed_item = NULL;
+			gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+		}
+
+		if (item == item->canvas->focused_item)
+			item->canvas->focused_item = NULL;
+
+		/* Normal destroy stuff */
+
+		if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+			(* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+		if (item->object.flags & FOO_CANVAS_ITEM_REALIZED)
+			(* FOO_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+		if (item->parent)
+			group_remove (FOO_CANVAS_GROUP (item->parent), item);
+
+		item->canvas = NULL;
+	}
+
+	G_OBJECT_CLASS (item_parent_class)->dispose (object);
+}
+
+
+/* Realize handler for canvas items */
+static void
+foo_canvas_item_realize (FooCanvasItem *item)
+{
+	if (item->parent && !(item->parent->object.flags & FOO_CANVAS_ITEM_REALIZED))
+		(* FOO_CANVAS_ITEM_GET_CLASS (item->parent)->realize) (item->parent);
+	
+	if (item->parent == NULL && !GTK_WIDGET_REALIZED (GTK_WIDGET (item->canvas)))
+		gtk_widget_realize (GTK_WIDGET (item->canvas));
+	
+	GTK_OBJECT_SET_FLAGS (item, FOO_CANVAS_ITEM_REALIZED);
+
+	foo_canvas_item_request_update (item);
+}
+
+/* Unrealize handler for canvas items */
+static void
+foo_canvas_item_unrealize (FooCanvasItem *item)
+{
+	if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+		(* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+	
+	GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_REALIZED);
+}
+
+/* Map handler for canvas items */
+static void
+foo_canvas_item_map (FooCanvasItem *item)
+{
+	GTK_OBJECT_SET_FLAGS (item, FOO_CANVAS_ITEM_MAPPED);
+}
+
+/* Unmap handler for canvas items */
+static void
+foo_canvas_item_unmap (FooCanvasItem *item)
+{
+	GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_MAPPED);
+}
+
+/* Update handler for canvas items */
+static void
+foo_canvas_item_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_NEED_UPDATE);
+	GTK_OBJECT_UNSET_FLAGS (item, FOO_CANVAS_ITEM_NEED_DEEP_UPDATE);
+}
+
+/*
+ * This routine invokes the update method of the item
+ * Please notice, that we take parent to canvas pixel matrix as argument
+ * unlike virtual method ::update, whose argument is item 2 canvas pixel
+ * matrix
+ *
+ * I will try to force somewhat meaningful naming for affines (Lauris)
+ * General naming rule is FROM2TO, where FROM and TO are abbreviations
+ * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
+ * I hope that this helps to keep track of what really happens
+ *
+ */
+
+static void
+foo_canvas_item_invoke_update (FooCanvasItem *item,
+			       double i2w_dx,
+			       double i2w_dy,
+			       int flags)
+{
+	int child_flags;
+
+	child_flags = flags;
+
+	/* apply object flags to child flags */
+	child_flags &= ~FOO_CANVAS_UPDATE_REQUESTED;
+
+	if (item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE)
+		child_flags |= FOO_CANVAS_UPDATE_REQUESTED;
+
+	if (item->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)
+		child_flags |= FOO_CANVAS_UPDATE_DEEP;
+
+	if (child_flags & GCI_UPDATE_MASK) {
+		if (FOO_CANVAS_ITEM_GET_CLASS (item)->update)
+			FOO_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
+	}
+ 
+	/* If this fail you probably forgot to chain up to
+	 * FooCanvasItem::update from a derived class */
+ 	g_return_if_fail (!(item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE));
+}
+
+/*
+ * This routine invokes the point method of the item.
+ * The arguments x, y should be in the parent item local coordinates.
+ */
+
+static double
+foo_canvas_item_invoke_point (FooCanvasItem *item, double x, double y, int cx, int cy, FooCanvasItem **actual_item)
+{
+	/* Calculate x & y in item local coordinates */
+
+	if (FOO_CANVAS_ITEM_GET_CLASS (item)->point)
+		return FOO_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
+
+	return 1e18;
+}
+
+/**
+ * foo_canvas_item_set:
+ * @item: A canvas item.
+ * @first_arg_name: The list of object argument name/value pairs used to configure the item.
+ * @Varargs:
+ *
+ * Configures a canvas item.  The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+foo_canvas_item_set (FooCanvasItem *item, const gchar *first_arg_name, ...)
+{
+	va_list args;
+
+	va_start (args, first_arg_name);
+	foo_canvas_item_set_valist (item, first_arg_name, args);
+	va_end (args);
+}
+
+
+/**
+ * foo_canvas_item_set_valist:
+ * @item: A canvas item.
+ * @first_arg_name: The name of the first argument used to configure the item.
+ * @args: The list of object argument name/value pairs used to configure the item.
+ *
+ * Configures a canvas item.  The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+foo_canvas_item_set_valist (FooCanvasItem *item, const gchar *first_arg_name, va_list args)
+{
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+#if 0
+	/* I commented this out, because item implementations have to schedule update/redraw */
+	foo_canvas_item_request_redraw (item);
+#endif
+
+	item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * foo_canvas_item_move:
+ * @item: A canvas item.
+ * @dx: Horizontal offset.
+ * @dy: Vertical offset.
+ *
+ * Moves a canvas item by creating an affine transformation matrix for
+ * translation by using the specified values. This happens in item
+ * local coordinate system, so if you have nontrivial transform, it
+ * most probably does not do, what you want.
+ **/
+void
+foo_canvas_item_move (FooCanvasItem *item, double dx, double dy)
+{
+        g_return_if_fail (item != NULL);
+        g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+        if (!FOO_CANVAS_ITEM_GET_CLASS (item)->translate) {
+                g_warning ("Item type %s does not implement translate method.\n",
+                           g_type_name (GTK_OBJECT_TYPE (item)));
+                return;
+        }
+
+        (* FOO_CANVAS_ITEM_GET_CLASS (item)->translate) (item, dx, dy);
+
+	if (item->object.flags & FOO_CANVAS_ITEM_MAPPED) 
+		item->canvas->need_repick = TRUE;
+
+	if (!(item->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+		item->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		if (item->parent != NULL)
+			foo_canvas_item_request_update (item->parent);
+		else
+			foo_canvas_request_update (item->canvas);
+	}
+
+}
+
+/* Convenience function to reorder items in a group's child list.  This puts the
+ * specified link after the "before" link. Returns TRUE if the list was changed.
+ */
+static gboolean
+put_item_after (GList *link, GList *before)
+{
+	FooCanvasGroup *parent;
+
+	if (link == before)
+		return FALSE;
+
+	parent = FOO_CANVAS_GROUP (FOO_CANVAS_ITEM (link->data)->parent);
+
+	if (before == NULL) {
+		if (link == parent->item_list)
+			return FALSE;
+
+		link->prev->next = link->next;
+
+		if (link->next)
+			link->next->prev = link->prev;
+		else
+			parent->item_list_end = link->prev;
+
+		link->prev = before;
+		link->next = parent->item_list;
+		link->next->prev = link;
+		parent->item_list = link;
+	} else {
+		if ((link == parent->item_list_end) && (before == parent->item_list_end->prev))
+			return FALSE;
+
+		if (link->next)
+			link->next->prev = link->prev;
+
+		if (link->prev)
+			link->prev->next = link->next;
+		else {
+			parent->item_list = link->next;
+			parent->item_list->prev = NULL;
+		}
+
+		link->prev = before;
+		link->next = before->next;
+
+		link->prev->next = link;
+
+		if (link->next)
+			link->next->prev = link;
+		else
+			parent->item_list_end = link;
+	}
+	return TRUE;
+}
+
+
+/**
+ * foo_canvas_item_raise:
+ * @item: A canvas item.
+ * @positions: Number of steps to raise the item.
+ *
+ * Raises the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the top of the
+ * stack, then the item is put at the top.
+ **/
+void
+foo_canvas_item_raise (FooCanvasItem *item, int positions)
+{
+	GList *link, *before;
+	FooCanvasGroup *parent;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+	g_return_if_fail (positions >= 0);
+
+	if (!item->parent || positions == 0)
+		return;
+
+	parent = FOO_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	for (before = link; positions && before; positions--)
+		before = before->next;
+
+	if (!before)
+		before = parent->item_list_end;
+
+	if (put_item_after (link, before)) {
+		redraw_and_repick_if_mapped (item);
+	}
+}
+
+
+/**
+ * foo_canvas_item_lower:
+ * @item: A canvas item.
+ * @positions: Number of steps to lower the item.
+ *
+ * Lowers the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the bottom of the
+ * stack, then the item is put at the bottom.
+ **/
+void
+foo_canvas_item_lower (FooCanvasItem *item, int positions)
+{
+	GList *link, *before;
+	FooCanvasGroup *parent;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+	g_return_if_fail (positions >= 1);
+
+	if (!item->parent || positions == 0)
+		return;
+
+	parent = FOO_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	if (link->prev)
+		for (before = link->prev; positions && before; positions--)
+			before = before->prev;
+	else
+		before = NULL;
+
+	if (put_item_after (link, before)) {
+		redraw_and_repick_if_mapped (item);
+	}
+}
+
+
+/**
+ * foo_canvas_item_raise_to_top:
+ * @item: A canvas item.
+ *
+ * Raises an item to the top of its parent's stack.
+ **/
+void
+foo_canvas_item_raise_to_top (FooCanvasItem *item)
+{
+	GList *link;
+	FooCanvasGroup *parent;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	if (!item->parent)
+		return;
+
+	parent = FOO_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	if (put_item_after (link, parent->item_list_end)) {
+		redraw_and_repick_if_mapped (item);
+	}
+}
+
+
+/**
+ * foo_canvas_item_lower_to_bottom:
+ * @item: A canvas item.
+ *
+ * Lowers an item to the bottom of its parent's stack.
+ **/
+void
+foo_canvas_item_lower_to_bottom (FooCanvasItem *item)
+{
+	GList *link;
+	FooCanvasGroup *parent;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	if (!item->parent)
+		return;
+
+	parent = FOO_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	if (put_item_after (link, NULL)) {
+		redraw_and_repick_if_mapped (item);
+	}
+}
+
+/**
+ * foo_canvas_item_send_behind:
+ * @item: A canvas item.
+ * @behind_item: The canvas item to put item behind, or NULL
+ *
+ * Moves item to a in the position in the stacking order so that
+ * it is placed immediately below behind_item, or at the top if
+ * behind_item is NULL.
+ **/
+void
+foo_canvas_item_send_behind (FooCanvasItem *item,
+			     FooCanvasItem *behind_item)
+{
+	GList *item_list;
+	int item_position, behind_position;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	if (behind_item == NULL) {
+		foo_canvas_item_raise_to_top (item);
+		return;
+	}
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (behind_item));
+	g_return_if_fail (item->parent == behind_item->parent);
+
+	item_list = FOO_CANVAS_GROUP (item->parent)->item_list;
+
+	item_position = g_list_index (item_list, item);
+	g_assert (item_position != -1);
+	behind_position = g_list_index (item_list, behind_item);
+	g_assert (behind_position != -1);
+	g_assert (item_position != behind_position);
+
+	if (item_position == behind_position - 1) {
+		return;
+	}
+
+	if (item_position < behind_position) {
+		foo_canvas_item_raise (item, (behind_position - 1) - item_position);
+	} else {
+		foo_canvas_item_lower (item, item_position - behind_position);
+	}
+}
+
+/**
+ * foo_canvas_item_show:
+ * @item: A canvas item.
+ *
+ * Shows a canvas item.  If the item was already shown, then no action is taken.
+ **/
+void
+foo_canvas_item_show (FooCanvasItem *item)
+{
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	if (!(item->object.flags & FOO_CANVAS_ITEM_VISIBLE)) {
+		item->object.flags |= FOO_CANVAS_ITEM_VISIBLE;
+		
+		if (!(item->object.flags & FOO_CANVAS_ITEM_REALIZED))
+			(* FOO_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+
+		if (item->parent != NULL) {
+			if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+			    item->parent->object.flags & FOO_CANVAS_ITEM_MAPPED)
+				(* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+		} else {
+			if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+			    GTK_WIDGET_MAPPED (GTK_WIDGET (item->canvas)))
+				(* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+		}
+
+		redraw_and_repick_if_mapped (item);
+	}
+}
+
+
+/**
+ * foo_canvas_item_hide:
+ * @item: A canvas item.
+ *
+ * Hides a canvas item.  If the item was already hidden, then no action is
+ * taken.
+ **/
+void
+foo_canvas_item_hide (FooCanvasItem *item)
+{
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE) {
+		item->object.flags &= ~FOO_CANVAS_ITEM_VISIBLE;
+
+		redraw_and_repick_if_mapped (item);
+		
+		if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+			(* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+		/* No need to unrealize when we just want to hide */
+	}
+}
+
+
+/**
+ * foo_canvas_item_grab:
+ * @item: A canvas item.
+ * @event_mask: Mask of events that will be sent to this item.
+ * @cursor: If non-NULL, the cursor that will be used while the grab is active.
+ * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
+ *
+ * Specifies that all events that match the specified event mask should be sent
+ * to the specified item, and also grabs the mouse by calling
+ * gdk_pointer_grab().  The event mask is also used when grabbing the pointer.
+ * If @cursor is not NULL, then that cursor is used while the grab is active.
+ * The @etime parameter is the timestamp required for grabbing the mouse.
+ *
+ * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED.  If
+ * the specified item was hidden by calling foo_canvas_item_hide(), then it
+ * returns %GDK_GRAB_NOT_VIEWABLE.  Else, it returns the result of calling
+ * gdk_pointer_grab().
+ **/
+int
+foo_canvas_item_grab (FooCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
+{
+	int retval;
+
+	g_return_val_if_fail (FOO_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
+	g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), GDK_GRAB_NOT_VIEWABLE);
+
+	if (item->canvas->grabbed_item)
+		return GDK_GRAB_ALREADY_GRABBED;
+
+	if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED))
+		return GDK_GRAB_NOT_VIEWABLE;
+
+	retval = gdk_pointer_grab (item->canvas->layout.bin_window,
+				   FALSE,
+				   event_mask,
+				   NULL,
+				   cursor,
+				   etime);
+
+	if (retval != GDK_GRAB_SUCCESS)
+		return retval;
+
+	item->canvas->grabbed_item = item;
+	item->canvas->grabbed_event_mask = event_mask;
+	item->canvas->current_item = item; /* So that events go to the grabbed item */
+
+	return retval;
+}
+
+
+/**
+ * foo_canvas_item_ungrab:
+ * @item: A canvas item that holds a grab.
+ * @etime: The timestamp for ungrabbing the mouse.
+ *
+ * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
+ * mouse.
+ **/
+void
+foo_canvas_item_ungrab (FooCanvasItem *item, guint32 etime)
+{
+	GdkDisplay *display;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	if (item->canvas->grabbed_item != item)
+		return;
+
+	display = gtk_widget_get_display (GTK_WIDGET (item->canvas));
+	item->canvas->grabbed_item = NULL;
+	gdk_display_pointer_ungrab (display, etime);
+}
+
+
+/**
+ * foo_canvas_item_w2i:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from world coordinates to item-relative
+ * coordinates.
+ **/
+void
+foo_canvas_item_w2i (FooCanvasItem *item, double *x, double *y)
+{
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+	g_return_if_fail (x != NULL);
+	g_return_if_fail (y != NULL);
+
+	item = item->parent;
+	while (item) {
+		if (FOO_IS_CANVAS_GROUP (item)) {
+			*x -= FOO_CANVAS_GROUP (item)->xpos;
+			*y -= FOO_CANVAS_GROUP (item)->ypos;
+		}
+
+		item = item->parent;
+	}
+}
+
+
+/**
+ * foo_canvas_item_i2w:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from item-relative coordinates to world
+ * coordinates.
+ **/
+void
+foo_canvas_item_i2w (FooCanvasItem *item, double *x, double *y)
+{
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+	g_return_if_fail (x != NULL);
+	g_return_if_fail (y != NULL);
+
+	item = item->parent;
+	while (item) {
+		if (FOO_IS_CANVAS_GROUP (item)) {
+			*x += FOO_CANVAS_GROUP (item)->xpos;
+			*y += FOO_CANVAS_GROUP (item)->ypos;
+		}
+
+		item = item->parent;
+	}
+}
+
+/* Returns whether the item is an inferior of or is equal to the parent. */
+static int
+is_descendant (FooCanvasItem *item, FooCanvasItem *parent)
+{
+	for (; item; item = item->parent)
+		if (item == parent)
+			return TRUE;
+
+	return FALSE;
+}
+
+/**
+ * foo_canvas_item_reparent:
+ * @item: A canvas item.
+ * @new_group: A canvas group.
+ *
+ * Changes the parent of the specified item to be the new group.  The item keeps
+ * its group-relative coordinates as for its old parent, so the item may change
+ * its absolute position within the canvas.
+ **/
+void
+foo_canvas_item_reparent (FooCanvasItem *item, FooCanvasGroup *new_group)
+{
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+	g_return_if_fail (FOO_IS_CANVAS_GROUP (new_group));
+
+	/* Both items need to be in the same canvas */
+	g_return_if_fail (item->canvas == FOO_CANVAS_ITEM (new_group)->canvas);
+
+	/* The group cannot be an inferior of the item or be the item itself --
+	 * this also takes care of the case where the item is the root item of
+	 * the canvas.  */
+	g_return_if_fail (!is_descendant (FOO_CANVAS_ITEM (new_group), item));
+
+	/* Everything is ok, now actually reparent the item */
+
+	g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
+
+	foo_canvas_item_request_redraw (item);
+
+	group_remove (FOO_CANVAS_GROUP (item->parent), item);
+	item->parent = FOO_CANVAS_ITEM (new_group);
+	/* item->canvas is unchanged.  */
+	group_add (new_group, item);
+
+	/* Redraw and repick */
+
+	redraw_and_repick_if_mapped (item);
+
+	g_object_unref (G_OBJECT (item));
+}
+
+/**
+ * foo_canvas_item_grab_focus:
+ * @item: A canvas item.
+ *
+ * Makes the specified item take the keyboard focus, so all keyboard events will
+ * be sent to it.  If the canvas widget itself did not have the focus, it grabs
+ * it as well.
+ **/
+void
+foo_canvas_item_grab_focus (FooCanvasItem *item)
+{
+	FooCanvasItem *focused_item;
+	GdkEvent ev;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+	g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
+
+	focused_item = item->canvas->focused_item;
+
+	if (focused_item) {
+		ev.focus_change.type = GDK_FOCUS_CHANGE;
+		ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+		ev.focus_change.send_event = FALSE;
+		ev.focus_change.in = FALSE;
+
+		emit_event (item->canvas, &ev);
+	}
+
+	item->canvas->focused_item = item;
+	gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
+
+	if (focused_item) {                                                     
+		ev.focus_change.type = GDK_FOCUS_CHANGE;                        
+		ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+		ev.focus_change.send_event = FALSE;                             
+		ev.focus_change.in = TRUE;                                      
+
+		emit_event (item->canvas, &ev);                          
+	}                               
+}
+
+
+/**
+ * foo_canvas_item_get_bounds:
+ * @item: A canvas item.
+ * @x1: Leftmost edge of the bounding box (return value).
+ * @y1: Upper edge of the bounding box (return value).
+ * @x2: Rightmost edge of the bounding box (return value).
+ * @y2: Lower edge of the bounding box (return value).
+ *
+ * Queries the bounding box of a canvas item.  The bounds are returned in the
+ * coordinate system of the item's parent.
+ **/
+void
+foo_canvas_item_get_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	double tx1, ty1, tx2, ty2;
+
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	tx1 = ty1 = tx2 = ty2 = 0.0;
+
+	/* Get the item's bounds in its coordinate system */
+
+	if (FOO_CANVAS_ITEM_GET_CLASS (item)->bounds)
+		(* FOO_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
+
+	/* Return the values */
+
+	if (x1)
+		*x1 = tx1;
+
+	if (y1)
+		*y1 = ty1;
+
+	if (x2)
+		*x2 = tx2;
+
+	if (y2)
+		*y2 = ty2;
+}
+
+
+/**
+ * foo_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * To be used only by item implementations.  Requests that the canvas queue an
+ * update for the specified item.
+ **/
+void
+foo_canvas_item_request_update (FooCanvasItem *item)
+{
+	if (NULL == item->canvas)
+		return;
+
+	g_return_if_fail (!item->canvas->doing_update);
+
+	if (item->object.flags & FOO_CANVAS_ITEM_NEED_UPDATE)
+		return;
+
+	item->object.flags |= FOO_CANVAS_ITEM_NEED_UPDATE;
+
+	if (item->parent != NULL) {
+		/* Recurse up the tree */
+		foo_canvas_item_request_update (item->parent);
+	} else {
+		/* Have reached the top of the tree, make sure the update call gets scheduled. */
+		foo_canvas_request_update (item->canvas);
+	}
+}
+
+/**
+ * foo_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * Convenience function that informs a canvas that the specified item needs
+ * to be repainted. To be used by item implementations
+ **/
+void
+foo_canvas_item_request_redraw (FooCanvasItem *item)
+{
+	if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+		foo_canvas_request_redraw (item->canvas,
+					   item->x1, item->y1,
+					   item->x2 + 1, item->y2 + 1);
+}
+
+
+
+/*** FooCanvasGroup ***/
+
+
+enum {
+	GROUP_PROP_0,
+	GROUP_PROP_X,
+	GROUP_PROP_Y
+};
+
+
+static void foo_canvas_group_class_init  (FooCanvasGroupClass *klass);
+static void foo_canvas_group_init        (FooCanvasGroup      *group);
+static void foo_canvas_group_set_property(GObject               *object, 
+					    guint                  param_id,
+					    const GValue          *value,
+					    GParamSpec            *pspec);
+static void foo_canvas_group_get_property(GObject               *object,
+					    guint                  param_id,
+					    GValue                *value,
+					    GParamSpec            *pspec);
+
+static void foo_canvas_group_destroy     (GtkObject             *object);
+
+static void   foo_canvas_group_update      (FooCanvasItem *item,
+					      double           i2w_dx,
+					      double           i2w_dy,
+					      int              flags);
+static void   foo_canvas_group_unrealize   (FooCanvasItem *item);
+static void   foo_canvas_group_map         (FooCanvasItem *item);
+static void   foo_canvas_group_unmap       (FooCanvasItem *item);
+static void   foo_canvas_group_draw        (FooCanvasItem *item, GdkDrawable *drawable,
+					      GdkEventExpose *expose);
+static double foo_canvas_group_point       (FooCanvasItem *item, double x, double y,
+					      int cx, int cy,
+					      FooCanvasItem **actual_item);
+static void   foo_canvas_group_translate   (FooCanvasItem *item, double dx, double dy);
+static void   foo_canvas_group_bounds      (FooCanvasItem *item, double *x1, double *y1,
+					      double *x2, double *y2);
+
+
+static FooCanvasItemClass *group_parent_class;
+
+
+/**
+ * foo_canvas_group_get_type:
+ *
+ * Registers the &FooCanvasGroup class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &FooCanvasGroup class.
+ **/
+GType
+foo_canvas_group_get_type (void)
+{
+	static GType group_type = 0;
+
+	if (!group_type) {
+		static const GTypeInfo group_info = {
+			sizeof (FooCanvasGroupClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) foo_canvas_group_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (FooCanvasGroup),
+			0,              /* n_preallocs */
+			(GInstanceInitFunc) foo_canvas_group_init
+
+	
+		};
+
+		group_type = g_type_register_static (foo_canvas_item_get_type (),
+						     "FooCanvasGroup",
+						     &group_info,
+						     0);
+	}
+
+	return group_type;
+}
+
+/* Class initialization function for FooCanvasGroupClass */
+static void
+foo_canvas_group_class_init (FooCanvasGroupClass *klass)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	FooCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (FooCanvasItemClass *) klass;
+
+	group_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_group_set_property;
+	gobject_class->get_property = foo_canvas_group_get_property;
+
+	g_object_class_install_property
+		(gobject_class, GROUP_PROP_X,
+		 g_param_spec_double ("x",
+				      _("X"),
+				      _("X"),
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+	g_object_class_install_property
+		(gobject_class, GROUP_PROP_Y,
+		 g_param_spec_double ("y",
+				      _("Y"),
+				      _("Y"),
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READWRITE));
+
+	object_class->destroy = foo_canvas_group_destroy;
+
+	item_class->update = foo_canvas_group_update;
+	item_class->unrealize = foo_canvas_group_unrealize;
+	item_class->map = foo_canvas_group_map;
+	item_class->unmap = foo_canvas_group_unmap;
+	item_class->draw = foo_canvas_group_draw;
+	item_class->point = foo_canvas_group_point;
+	item_class->translate = foo_canvas_group_translate;
+	item_class->bounds = foo_canvas_group_bounds;
+}
+
+/* Object initialization function for FooCanvasGroup */
+static void
+foo_canvas_group_init (FooCanvasGroup *group)
+{
+	group->xpos = 0.0;
+	group->ypos = 0.0;
+}
+
+/* Set_property handler for canvas groups */
+static void
+foo_canvas_group_set_property (GObject *gobject, guint param_id,
+			       const GValue *value, GParamSpec *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasGroup *group;
+	double old;
+	gboolean moved;
+
+	g_return_if_fail (FOO_IS_CANVAS_GROUP (gobject));
+
+	item = FOO_CANVAS_ITEM (gobject);
+	group = FOO_CANVAS_GROUP (gobject);
+
+	moved = FALSE;
+	switch (param_id) {
+	case GROUP_PROP_X:
+		old = group->xpos;
+		group->xpos = g_value_get_double (value);
+		if (old != group->xpos)
+			moved = TRUE;
+		break;
+
+	case GROUP_PROP_Y:
+		old = group->ypos;
+		group->ypos = g_value_get_double (value);
+		if (old != group->ypos)
+			moved = TRUE;
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+
+	if (moved) {
+		item->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		if (item->parent != NULL)
+			foo_canvas_item_request_update (item->parent);
+		else
+			foo_canvas_request_update (item->canvas);
+	}
+}
+
+/* Get_property handler for canvas groups */
+static void
+foo_canvas_group_get_property (GObject *gobject, guint param_id,
+				 GValue *value, GParamSpec *pspec)
+{
+	FooCanvasItem *item;
+	FooCanvasGroup *group;
+
+	g_return_if_fail (FOO_IS_CANVAS_GROUP (gobject));
+
+	item = FOO_CANVAS_ITEM (gobject);
+	group = FOO_CANVAS_GROUP (gobject);
+
+	switch (param_id) {
+	case GROUP_PROP_X:
+		g_value_set_double (value, group->xpos);
+		break;
+
+	case GROUP_PROP_Y:
+		g_value_set_double (value, group->ypos);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/* Destroy handler for canvas groups */
+static void
+foo_canvas_group_destroy (GtkObject *object)
+{
+	FooCanvasGroup *group;
+	FooCanvasItem *child;
+	GList *list;
+
+	g_return_if_fail (FOO_IS_CANVAS_GROUP (object));
+
+	group = FOO_CANVAS_GROUP (object);
+
+	list = group->item_list;
+	while (list) {
+		child = list->data;
+		list = list->next;
+
+		gtk_object_destroy (GTK_OBJECT (child));
+	}
+
+	if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
+}
+
+/* Update handler for canvas groups */
+static void
+foo_canvas_group_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	FooCanvasGroup *group;
+	GList *list;
+	FooCanvasItem *i;
+	double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+	gboolean first = TRUE;
+
+	group = FOO_CANVAS_GROUP (item);
+
+	(* group_parent_class->update) (item, i2w_dx, i2w_dy, flags);
+
+	bbox_x0 = 0;
+	bbox_y0 = 0;
+	bbox_x1 = 0;
+	bbox_y1 = 0;
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		foo_canvas_item_invoke_update (i, i2w_dx + group->xpos, i2w_dy + group->ypos, flags);
+
+		if (first) {
+			first = FALSE;
+			bbox_x0 = i->x1;
+			bbox_y0 = i->y1;
+			bbox_x1 = i->x2;
+			bbox_y1 = i->y2;
+		} else {
+			bbox_x0 = MIN (bbox_x0, i->x1);
+			bbox_y0 = MIN (bbox_y0, i->y1);
+			bbox_x1 = MAX (bbox_x1, i->x2);
+			bbox_y1 = MAX (bbox_y1, i->y2);
+		}
+	}
+	item->x1 = bbox_x0;
+	item->y1 = bbox_y0;
+	item->x2 = bbox_x1;
+	item->y2 = bbox_y1;
+}
+
+/* Unrealize handler for canvas groups */
+static void
+foo_canvas_group_unrealize (FooCanvasItem *item)
+{
+	FooCanvasGroup *group;
+	GList *list;
+	FooCanvasItem *i;
+
+	group = FOO_CANVAS_GROUP (item);
+
+	/* Unmap group before children to avoid flash */
+	if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+		(* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & FOO_CANVAS_ITEM_REALIZED)
+			(* FOO_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
+	}
+
+	(* group_parent_class->unrealize) (item);
+}
+
+/* Map handler for canvas groups */
+static void
+foo_canvas_group_map (FooCanvasItem *item)
+{
+	FooCanvasGroup *group;
+	GList *list;
+	FooCanvasItem *i;
+
+	group = FOO_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
+		    !(i->object.flags & FOO_CANVAS_ITEM_MAPPED)) {
+			if (!(i->object.flags & FOO_CANVAS_ITEM_REALIZED))
+				(* FOO_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
+				
+			(* FOO_CANVAS_ITEM_GET_CLASS (i)->map) (i);
+		}
+	}
+
+	(* group_parent_class->map) (item);
+}
+
+/* Unmap handler for canvas groups */
+static void
+foo_canvas_group_unmap (FooCanvasItem *item)
+{
+	FooCanvasGroup *group;
+	GList *list;
+	FooCanvasItem *i;
+
+	group = FOO_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & FOO_CANVAS_ITEM_MAPPED)
+			(* FOO_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
+	}
+
+	(* group_parent_class->unmap) (item);
+}
+
+/* Draw handler for canvas groups */
+static void
+foo_canvas_group_draw (FooCanvasItem *item, GdkDrawable *drawable,
+			 GdkEventExpose *expose)
+{
+	FooCanvasGroup *group;
+	GList *list;
+	FooCanvasItem *child = NULL;
+
+	group = FOO_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if ((child->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+		    (FOO_CANVAS_ITEM_GET_CLASS (child)->draw)) {
+			GdkRectangle child_rect;
+			
+			child_rect.x = child->x1;
+			child_rect.y = child->y1;
+			child_rect.width = child->x2 - child->x1 + 1;
+			child_rect.height = child->y2 - child->y1 + 1;
+
+			if (gdk_region_rect_in (expose->region, &child_rect) != GDK_OVERLAP_RECTANGLE_OUT)
+				(* FOO_CANVAS_ITEM_GET_CLASS (child)->draw) (child, drawable, expose);
+		}
+	}
+}
+
+/* Point handler for canvas groups */
+static double
+foo_canvas_group_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+			FooCanvasItem **actual_item)
+{
+	FooCanvasGroup *group;
+	GList *list;
+	FooCanvasItem *child, *point_item;
+	int x1, y1, x2, y2;
+	double gx, gy;
+	double dist, best;
+	int has_point;
+
+	group = FOO_CANVAS_GROUP (item);
+
+	x1 = cx - item->canvas->close_enough;
+	y1 = cy - item->canvas->close_enough;
+	x2 = cx + item->canvas->close_enough;
+	y2 = cy + item->canvas->close_enough;
+
+	best = 0.0;
+	*actual_item = NULL;
+
+	gx = x - group->xpos;
+	gy = y - group->ypos;
+
+	dist = 0.0; /* keep gcc happy */
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
+			continue;
+
+		point_item = NULL; /* cater for incomplete item implementations */
+
+		if ((child->object.flags & FOO_CANVAS_ITEM_MAPPED)
+		    && FOO_CANVAS_ITEM_GET_CLASS (child)->point) {
+			dist = foo_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
+			has_point = TRUE;
+		} else
+			has_point = FALSE;
+
+		if (has_point
+		    && point_item
+		    && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
+			<= item->canvas->close_enough)) {
+			best = dist;
+			*actual_item = point_item;
+		}
+	}
+
+	return best;
+}
+
+void
+foo_canvas_group_translate (FooCanvasItem *item, double dx, double dy)
+{
+        FooCanvasGroup *group;
+
+        group = FOO_CANVAS_GROUP (item);
+
+        group->xpos += dx;
+        group->ypos += dy;
+}
+
+/* Bounds handler for canvas groups */
+static void
+foo_canvas_group_bounds (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	FooCanvasGroup *group;
+	FooCanvasItem *child;
+	GList *list;
+	double tx1, ty1, tx2, ty2;
+	double minx, miny, maxx, maxy;
+	int set;
+
+	group = FOO_CANVAS_GROUP (item);
+
+	/* Get the bounds of the first visible item */
+
+	child = NULL; /* Unnecessary but eliminates a warning. */
+
+	set = FALSE;
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if (child->object.flags & FOO_CANVAS_ITEM_MAPPED) {
+			set = TRUE;
+			foo_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
+			break;
+		}
+	}
+
+	/* If there were no visible items, return an empty bounding box */
+
+	if (!set) {
+		*x1 = *y1 = *x2 = *y2 = 0.0;
+		return;
+	}
+
+	/* Now we can grow the bounds using the rest of the items */
+
+	list = list->next;
+
+	for (; list; list = list->next) {
+		child = list->data;
+
+		if (!(child->object.flags & FOO_CANVAS_ITEM_MAPPED))
+			continue;
+
+		foo_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
+
+		if (tx1 < minx)
+			minx = tx1;
+
+		if (ty1 < miny)
+			miny = ty1;
+
+		if (tx2 > maxx)
+			maxx = tx2;
+
+		if (ty2 > maxy)
+			maxy = ty2;
+	}
+
+	/* Make the bounds be relative to our parent's coordinate system */
+
+	if (item->parent) {
+		minx += group->xpos;
+		miny += group->ypos;
+		maxx += group->xpos;
+		maxy += group->ypos;
+	}
+	
+	*x1 = minx;
+	*y1 = miny;
+	*x2 = maxx;
+	*y2 = maxy;
+}
+
+/* Adds an item to a group */
+static void
+group_add (FooCanvasGroup *group, FooCanvasItem *item)
+{
+#if GLIB_CHECK_VERSION(2,10,0) && GTK_CHECK_VERSION(2,8,14)
+	g_object_ref_sink (item);
+#else
+	g_object_ref (item);
+	gtk_object_sink (GTK_OBJECT (item));
+#endif
+
+	if (!group->item_list) {
+		group->item_list = g_list_append (group->item_list, item);
+		group->item_list_end = group->item_list;
+	} else
+		group->item_list_end = g_list_append (group->item_list_end, item)->next;
+
+	if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
+	    group->item.object.flags & FOO_CANVAS_ITEM_MAPPED) {
+		if (!(item->object.flags & FOO_CANVAS_ITEM_REALIZED))
+			(* FOO_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+		
+		if (!(item->object.flags & FOO_CANVAS_ITEM_MAPPED))
+			(* FOO_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+	}
+}
+
+/* Removes an item from a group */
+static void
+group_remove (FooCanvasGroup *group, FooCanvasItem *item)
+{
+	GList *children;
+
+	g_return_if_fail (FOO_IS_CANVAS_GROUP (group));
+	g_return_if_fail (FOO_IS_CANVAS_ITEM (item));
+
+	for (children = group->item_list; children; children = children->next)
+		if (children->data == item) {
+			if (item->object.flags & FOO_CANVAS_ITEM_MAPPED)
+				(* FOO_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+			if (item->object.flags & FOO_CANVAS_ITEM_REALIZED)
+				(* FOO_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+			/* Unparent the child */
+
+			item->parent = NULL;
+			/* item->canvas = NULL; */
+			g_object_unref (G_OBJECT (item));
+
+			/* Remove it from the list */
+
+			if (children == group->item_list_end)
+				group->item_list_end = children->prev;
+
+			group->item_list = g_list_remove_link (group->item_list, children);
+			g_list_free (children);
+			break;
+		}
+}
+
+
+/*** FooCanvas ***/
+
+
+enum {
+	DRAW_BACKGROUND,
+	LAST_SIGNAL
+};
+
+static void foo_canvas_class_init          (FooCanvasClass *klass);
+static void foo_canvas_init                (FooCanvas      *canvas);
+static void foo_canvas_destroy             (GtkObject        *object);
+static void foo_canvas_map                 (GtkWidget        *widget);
+static void foo_canvas_unmap               (GtkWidget        *widget);
+static void foo_canvas_realize             (GtkWidget        *widget);
+static void foo_canvas_unrealize           (GtkWidget        *widget);
+static void foo_canvas_size_allocate       (GtkWidget        *widget,
+					      GtkAllocation    *allocation);
+static gint foo_canvas_button              (GtkWidget        *widget,
+					      GdkEventButton   *event);
+static gint foo_canvas_motion              (GtkWidget        *widget,
+					      GdkEventMotion   *event);
+static gint foo_canvas_expose              (GtkWidget        *widget,
+					      GdkEventExpose   *event);
+static gint foo_canvas_key                 (GtkWidget        *widget,
+					      GdkEventKey      *event);
+static gint foo_canvas_crossing            (GtkWidget        *widget,
+					      GdkEventCrossing *event);
+static gint foo_canvas_focus_in            (GtkWidget        *widget,
+					      GdkEventFocus    *event);
+static gint foo_canvas_focus_out           (GtkWidget        *widget,
+					      GdkEventFocus    *event);
+static void foo_canvas_request_update_real (FooCanvas      *canvas);
+static void foo_canvas_draw_background     (FooCanvas      *canvas,
+					      int               x,
+					      int               y,
+					      int               width,
+					      int               height);
+
+
+static GtkLayoutClass *canvas_parent_class;
+
+static guint canvas_signals[LAST_SIGNAL];
+
+/**
+ * foo_canvas_get_type:
+ *
+ * Registers the &FooCanvas class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &FooCanvas class.
+ **/
+GType
+foo_canvas_get_type (void)
+{
+	static GType canvas_type = 0;
+
+	if (!canvas_type) {
+		static const GTypeInfo canvas_info = {
+			sizeof (FooCanvasClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) foo_canvas_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (FooCanvas),
+			0,              /* n_preallocs */
+			(GInstanceInitFunc) foo_canvas_init
+		};
+
+		canvas_type = g_type_register_static (gtk_layout_get_type (),
+						      "FooCanvas",
+						      &canvas_info,
+						      0);
+	}
+
+	return canvas_type;
+}
+
+static void
+foo_canvas_get_property (GObject    *object, 
+			   guint       prop_id,
+			   GValue     *value,
+			   GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+foo_canvas_set_property (GObject      *object, 
+			   guint         prop_id,
+			   const GValue *value,
+			   GParamSpec   *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+foo_canvas_accessible_adjustment_changed (GtkAdjustment *adjustment,
+					  gpointer       data)
+{
+	AtkObject *atk_obj;
+
+	/* The scrollbars have changed */
+	atk_obj = ATK_OBJECT (data);
+
+	g_signal_emit_by_name (atk_obj, "visible_data_changed");
+}
+
+static void
+foo_canvas_accessible_initialize (AtkObject *obj, 
+				  gpointer   data)
+{
+	FooCanvas *canvas;
+
+	if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL) 
+		ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data);
+
+	canvas = FOO_CANVAS (data);
+	g_signal_connect (canvas->layout.hadjustment,
+			  "value_changed",
+			  G_CALLBACK (foo_canvas_accessible_adjustment_changed),
+			  obj);
+	g_signal_connect (canvas->layout.vadjustment,
+			  "value_changed",
+			  G_CALLBACK (foo_canvas_accessible_adjustment_changed),
+			  obj);
+	
+	obj->role = ATK_ROLE_LAYERED_PANE;
+}
+
+static gint
+foo_canvas_accessible_get_n_children (AtkObject* obj)
+{
+ 	GtkAccessible *accessible;
+	GtkWidget *widget;
+	FooCanvas *canvas;
+	FooCanvasGroup *root_group;
+
+	accessible = GTK_ACCESSIBLE (obj);
+	widget = accessible->widget;
+	if (widget == NULL) {
+		/* State is defunct */
+		return 0;
+	}
+
+	g_return_val_if_fail (FOO_IS_CANVAS (widget), 0);
+
+	canvas = FOO_CANVAS (widget);
+	root_group = foo_canvas_root (canvas);
+	g_return_val_if_fail (root_group, 0);
+	return 1;
+}
+
+static AtkObject*
+foo_canvas_accessible_ref_child (AtkObject *obj,
+                                 gint       i)
+{
+	GtkAccessible *accessible;
+	GtkWidget *widget;
+	FooCanvas *canvas;
+	FooCanvasGroup *root_group;
+	AtkObject *atk_object;
+
+	/* Canvas only has one child, so return NULL if index is non zero */
+	if (i != 0) {
+        	return NULL;
+	}
+
+	accessible = GTK_ACCESSIBLE (obj);
+	widget = accessible->widget;
+	if (widget == NULL) {
+		/* State is defunct */
+		return NULL;
+	}
+
+	canvas = FOO_CANVAS (widget);
+	root_group = foo_canvas_root (canvas);
+	g_return_val_if_fail (root_group, NULL);
+	atk_object = atk_gobject_accessible_for_object (G_OBJECT (root_group));
+	g_object_ref (atk_object);
+	
+	g_warning ("Accessible support for FooGroup needs to be implemented");
+	
+	return atk_object;
+}
+
+static void
+foo_canvas_accessible_class_init (AtkObjectClass *klass)
+{
+ 	accessible_parent_class = g_type_class_peek_parent (klass);
+
+	klass->initialize = foo_canvas_accessible_initialize;
+	klass->get_n_children = foo_canvas_accessible_get_n_children;
+	klass->ref_child = foo_canvas_accessible_ref_child;
+}
+
+static GType
+foo_canvas_accessible_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		AtkObjectFactory *factory;
+		GType parent_atk_type;
+		GTypeQuery query;
+		GTypeInfo tinfo = { 0 };
+
+		factory = atk_registry_get_factory (atk_get_default_registry(),
+						    GTK_TYPE_WIDGET);
+		if (!factory) {
+			return G_TYPE_INVALID;
+		}
+		parent_atk_type = atk_object_factory_get_accessible_type (factory);
+		if (!parent_atk_type) {
+			return G_TYPE_INVALID;
+		}
+		g_type_query (parent_atk_type, &query);
+		tinfo.class_init = (GClassInitFunc) foo_canvas_accessible_class_init;
+		tinfo.class_size = query.class_size;
+		tinfo.instance_size = query.instance_size;
+		type = g_type_register_static (parent_atk_type,
+					       "FooCanvasAccessibility",
+					       &tinfo, 0);
+	}
+	return type;
+}
+
+static AtkObject *
+foo_canvas_accessible_create (GObject *for_object)
+{
+	GType type;
+	AtkObject *accessible;
+	FooCanvas *canvas;
+
+	canvas = FOO_CANVAS (for_object);
+	g_return_val_if_fail (canvas != NULL, NULL);
+
+	type = foo_canvas_accessible_get_type ();
+
+	if (type == G_TYPE_INVALID) {
+		return atk_no_op_object_new (for_object);
+	}
+
+	accessible = g_object_new (type, NULL);
+	atk_object_initialize (accessible, for_object);
+	return accessible;
+}
+
+static GType
+foo_canvas_accessible_factory_get_accessible_type (void)
+{
+	return foo_canvas_accessible_get_type ();
+}
+
+static AtkObject*
+foo_canvas_accessible_factory_create_accessible (GObject *obj)
+{
+	AtkObject *accessible;
+
+	g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
+
+	accessible = foo_canvas_accessible_create (obj);
+
+	return accessible;
+}
+
+static void
+foo_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+	klass->create_accessible = foo_canvas_accessible_factory_create_accessible;
+	klass->get_accessible_type = foo_canvas_accessible_factory_get_accessible_type;
+}
+ 
+static GType
+foo_canvas_accessible_factory_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static const GTypeInfo tinfo = {
+			sizeof (AtkObjectFactoryClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) foo_canvas_accessible_factory_class_init,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			sizeof (AtkObjectFactory),
+			0,		/* n_preallocs */
+			NULL
+		};
+		type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
+					       "FooCanvasAccessibilityFactory",
+					       &tinfo, 0);
+	}
+
+	return type;
+}
+
+
+/* Class initialization function for FooCanvasClass */
+static void
+foo_canvas_class_init (FooCanvasClass *klass)
+{
+	GObjectClass   *gobject_class;
+	GtkObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+
+	gobject_class = (GObjectClass *)klass;
+	object_class  = (GtkObjectClass *) klass;
+	widget_class  = (GtkWidgetClass *) klass;
+
+	canvas_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_set_property;
+	gobject_class->get_property = foo_canvas_get_property;
+
+	object_class->destroy = foo_canvas_destroy;
+
+	widget_class->map = foo_canvas_map;
+	widget_class->unmap = foo_canvas_unmap;
+	widget_class->realize = foo_canvas_realize;
+	widget_class->unrealize = foo_canvas_unrealize;
+	widget_class->size_allocate = foo_canvas_size_allocate;
+	widget_class->button_press_event = foo_canvas_button;
+	widget_class->button_release_event = foo_canvas_button;
+	widget_class->motion_notify_event = foo_canvas_motion;
+	widget_class->expose_event = foo_canvas_expose;
+	widget_class->key_press_event = foo_canvas_key;
+	widget_class->key_release_event = foo_canvas_key;
+	widget_class->enter_notify_event = foo_canvas_crossing;
+	widget_class->leave_notify_event = foo_canvas_crossing;
+	widget_class->focus_in_event = foo_canvas_focus_in;
+	widget_class->focus_out_event = foo_canvas_focus_out;
+
+	klass->draw_background = foo_canvas_draw_background;
+	klass->request_update = foo_canvas_request_update_real;
+
+	canvas_signals[DRAW_BACKGROUND] =
+		g_signal_new ("draw_background",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (FooCanvasClass, draw_background),
+			      NULL, NULL,
+			      foo_canvas_marshal_VOID__INT_INT_INT_INT,
+			      G_TYPE_NONE, 4, 
+			      G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+
+	atk_registry_set_factory_type (atk_get_default_registry (),
+				       FOO_TYPE_CANVAS,
+				       foo_canvas_accessible_factory_get_type ());
+}
+
+/* Callback used when the root item of a canvas is destroyed.  The user should
+ * never ever do this, so we panic if this happens.
+ */
+static void
+panic_root_destroyed (GtkObject *object, gpointer data)
+{
+	g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
+}
+
+/* Object initialization function for FooCanvas */
+static void
+foo_canvas_init (FooCanvas *canvas)
+{
+	GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
+
+	gtk_widget_set_redraw_on_allocate (GTK_WIDGET (canvas), FALSE);
+
+	canvas->scroll_x1 = 0.0;
+	canvas->scroll_y1 = 0.0;
+	canvas->scroll_x2 = canvas->layout.width;
+	canvas->scroll_y2 = canvas->layout.height;
+
+	canvas->pixels_per_unit = 1.0;
+
+	canvas->pick_event.type = GDK_LEAVE_NOTIFY;
+	canvas->pick_event.crossing.x = 0;
+	canvas->pick_event.crossing.y = 0;
+
+	gtk_layout_set_hadjustment (GTK_LAYOUT (canvas), NULL);
+	gtk_layout_set_vadjustment (GTK_LAYOUT (canvas), NULL);
+
+	/* Create the root item as a special case */
+
+	canvas->root = FOO_CANVAS_ITEM (g_object_new (foo_canvas_group_get_type (), NULL));
+	canvas->root->canvas = canvas;
+
+#if GLIB_CHECK_VERSION(2,10,0) && GTK_CHECK_VERSION(2,8,14)
+	g_object_ref_sink (canvas->root);
+#else
+	g_object_ref (canvas->root);
+	gtk_object_sink (GTK_OBJECT (canvas->root));
+#endif
+
+	canvas->root_destroy_id = g_signal_connect (G_OBJECT (canvas->root),
+		"destroy", G_CALLBACK (panic_root_destroyed), canvas);
+
+	canvas->need_repick = TRUE;
+	canvas->doing_update = FALSE;
+}
+
+/* Convenience function to remove the idle handler of a canvas */
+static void
+remove_idle (FooCanvas *canvas)
+{
+	if (canvas->idle_id == 0)
+		return;
+
+	g_source_remove (canvas->idle_id);
+	canvas->idle_id = 0;
+}
+
+/* Removes the transient state of the canvas (idle handler, grabs). */
+static void
+shutdown_transients (FooCanvas *canvas)
+{
+	/* We turn off the need_redraw flag, since if the canvas is mapped again
+	 * it will request a redraw anyways.  We do not turn off the need_update
+	 * flag, though, because updates are not queued when the canvas remaps
+	 * itself.
+	 */
+	if (canvas->need_redraw) {
+		canvas->need_redraw = FALSE;
+	}
+
+	if (canvas->grabbed_item) {
+		GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (canvas));
+		canvas->grabbed_item = NULL;
+		gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+	}
+
+	remove_idle (canvas);
+}
+
+/* Destroy handler for FooCanvas */
+static void
+foo_canvas_destroy (GtkObject *object)
+{
+	FooCanvas *canvas;
+
+	g_return_if_fail (FOO_IS_CANVAS (object));
+
+	/* remember, destroy can be run multiple times! */
+
+	canvas = FOO_CANVAS (object);
+
+	if (canvas->root_destroy_id) {
+		g_signal_handler_disconnect (G_OBJECT (canvas->root), canvas->root_destroy_id);
+		canvas->root_destroy_id = 0;
+	}
+	if (canvas->root) {
+		FooCanvasItem *root = canvas->root;
+		canvas->root = NULL;
+		gtk_object_destroy (GTK_OBJECT (root));
+		g_object_unref (root);
+	}
+
+	shutdown_transients (canvas);
+
+	if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
+}
+
+/**
+ * foo_canvas_new:
+ * @void:
+ *
+ * Creates a new empty canvas.  If you wish to use the
+ * &FooCanvasImage item inside this canvas, then you must push the gdk_imlib
+ * visual and colormap before calling this function, and they can be popped
+ * afterwards.
+ *
+ * Return value: A newly-created canvas.
+ **/
+GtkWidget *
+foo_canvas_new (void)
+{
+	return GTK_WIDGET (g_object_new (foo_canvas_get_type (), NULL));
+}
+
+/* Map handler for the canvas */
+static void
+foo_canvas_map (GtkWidget *widget)
+{
+	FooCanvas *canvas;
+
+	g_return_if_fail (FOO_IS_CANVAS (widget));
+
+	/* Normal widget mapping stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
+
+	canvas = FOO_CANVAS (widget);
+
+	/* Map items */
+
+	if (canvas->root->object.flags & FOO_CANVAS_ITEM_VISIBLE &&
+	    !(canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED) &&
+	    FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
+		(* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
+}
+
+/* Unmap handler for the canvas */
+static void
+foo_canvas_unmap (GtkWidget *widget)
+{
+	FooCanvas *canvas;
+
+	g_return_if_fail (FOO_IS_CANVAS (widget));
+
+	canvas = FOO_CANVAS (widget);
+
+	shutdown_transients (canvas);
+
+	/* Unmap items */
+
+	if (FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
+		(* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
+
+	/* Normal widget unmapping stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
+}
+
+/* Realize handler for the canvas */
+static void
+foo_canvas_realize (GtkWidget *widget)
+{
+	FooCanvas *canvas;
+
+	g_return_if_fail (FOO_IS_CANVAS (widget));
+
+	/* Normal widget realization stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
+
+	canvas = FOO_CANVAS (widget);
+
+	gdk_window_set_events (canvas->layout.bin_window,
+			       (gdk_window_get_events (canvas->layout.bin_window)
+				 | GDK_EXPOSURE_MASK
+				 | GDK_BUTTON_PRESS_MASK
+				 | GDK_BUTTON_RELEASE_MASK
+				 | GDK_POINTER_MOTION_MASK
+				 | GDK_KEY_PRESS_MASK
+				 | GDK_KEY_RELEASE_MASK
+				 | GDK_ENTER_NOTIFY_MASK
+				 | GDK_LEAVE_NOTIFY_MASK
+				 | GDK_FOCUS_CHANGE_MASK));
+
+	/* Create our own temporary pixmap gc and realize all the items */
+
+	canvas->pixmap_gc = gdk_gc_new (canvas->layout.bin_window);
+
+	(* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
+}
+
+/* Unrealize handler for the canvas */
+static void
+foo_canvas_unrealize (GtkWidget *widget)
+{
+	FooCanvas *canvas;
+
+	g_return_if_fail (FOO_IS_CANVAS (widget));
+
+	canvas = FOO_CANVAS (widget);
+
+	shutdown_transients (canvas);
+
+	/* Unrealize items and parent widget */
+
+	(* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
+
+	g_object_unref (canvas->pixmap_gc);
+	canvas->pixmap_gc = NULL;
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
+}
+
+/* Handles scrolling of the canvas.  Adjusts the scrolling and zooming offset to
+ * keep as much as possible of the canvas scrolling region in view.
+ */
+static void
+scroll_to (FooCanvas *canvas, int cx, int cy)
+{
+	int scroll_width, scroll_height;
+	int right_limit, bottom_limit;
+	int old_zoom_xofs, old_zoom_yofs;
+	int changed_x = FALSE, changed_y = FALSE;
+	int canvas_width, canvas_height;
+
+	canvas_width = GTK_WIDGET (canvas)->allocation.width;
+	canvas_height = GTK_WIDGET (canvas)->allocation.height;
+
+	scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit + 0.5);
+	scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit + 0.5);
+
+	right_limit = scroll_width - canvas_width;
+	bottom_limit = scroll_height - canvas_height;
+
+	old_zoom_xofs = canvas->zoom_xofs;
+	old_zoom_yofs = canvas->zoom_yofs;
+
+	if (right_limit < 0) {
+		cx = 0;
+		if (canvas->center_scroll_region) {
+			canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
+			scroll_width = canvas_width;
+		} else {
+			canvas->zoom_xofs = 0;
+		}
+	} else if (cx < 0) {
+		cx = 0;
+		canvas->zoom_xofs = 0;
+	} else if (cx > right_limit) {
+		cx = right_limit;
+		canvas->zoom_xofs = 0;
+	} else
+		canvas->zoom_xofs = 0;
+
+	if (bottom_limit < 0) {
+		cy = 0;
+		if (canvas->center_scroll_region) {
+			canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
+			scroll_height = canvas_height;
+		} else {
+			canvas->zoom_yofs = 0;
+		}
+	} else if (cy < 0) {
+		cy = 0;
+		canvas->zoom_yofs = 0;
+	} else if (cy > bottom_limit) {
+		cy = bottom_limit;
+		canvas->zoom_yofs = 0;
+	} else
+		canvas->zoom_yofs = 0;
+
+	if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
+		/* This can only occur, if either canvas size or widget size changes */
+		/* So I think we can request full redraw here */
+		/* More stuff - we have to mark root as needing fresh affine (Lauris) */
+		if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+			canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+			foo_canvas_request_update (canvas);
+		}
+		gtk_widget_queue_draw (GTK_WIDGET (canvas));
+	}
+
+	if (((int) canvas->layout.hadjustment->value) != cx) {
+		canvas->layout.hadjustment->value = cx;
+		changed_x = TRUE;
+	}
+
+	if (((int) canvas->layout.vadjustment->value) != cy) {
+		canvas->layout.vadjustment->value = cy;
+		changed_y = TRUE;
+	}
+
+	if ((scroll_width != (int) canvas->layout.width) || (scroll_height != (int) canvas->layout.height)) {
+		gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
+	}
+	
+	/* Signal GtkLayout that it should do a redraw. */
+	if (changed_x)
+		g_signal_emit_by_name (G_OBJECT (canvas->layout.hadjustment), "value_changed");
+	if (changed_y)
+		g_signal_emit_by_name (G_OBJECT (canvas->layout.vadjustment), "value_changed");
+}
+
+/* Size allocation handler for the canvas */
+static void
+foo_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+	FooCanvas *canvas;
+
+	g_return_if_fail (FOO_IS_CANVAS (widget));
+	g_return_if_fail (allocation != NULL);
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
+
+	canvas = FOO_CANVAS (widget);
+
+	/* Recenter the view, if appropriate */
+
+	canvas->layout.hadjustment->page_size = allocation->width;
+	canvas->layout.hadjustment->page_increment = allocation->width / 2;
+
+	canvas->layout.vadjustment->page_size = allocation->height;
+	canvas->layout.vadjustment->page_increment = allocation->height / 2;
+
+	scroll_to (canvas,
+		   canvas->layout.hadjustment->value,
+		   canvas->layout.vadjustment->value);
+
+	g_signal_emit_by_name (G_OBJECT (canvas->layout.hadjustment), "changed");
+	g_signal_emit_by_name (G_OBJECT (canvas->layout.vadjustment), "changed");
+}
+
+/* Emits an event for an item in the canvas, be it the current item, grabbed
+ * item, or focused item, as appropriate.
+ */
+
+static int
+emit_event (FooCanvas *canvas, GdkEvent *event)
+{
+	GdkEvent ev;
+	gint finished;
+	FooCanvasItem *item;
+	FooCanvasItem *parent;
+	guint mask;
+
+	/* Could be an old pick event */
+	if (!GTK_WIDGET_REALIZED (canvas)) {
+		return FALSE;
+	}
+
+	/* Perform checks for grabbed items */
+
+	if (canvas->grabbed_item &&
+	    !is_descendant (canvas->current_item, canvas->grabbed_item)) {
+		return FALSE;
+        }
+
+	if (canvas->grabbed_item) {
+		switch (event->type) {
+		case GDK_ENTER_NOTIFY:
+			mask = GDK_ENTER_NOTIFY_MASK;
+			break;
+
+		case GDK_LEAVE_NOTIFY:
+			mask = GDK_LEAVE_NOTIFY_MASK;
+			break;
+
+		case GDK_MOTION_NOTIFY:
+			mask = GDK_POINTER_MOTION_MASK;
+			break;
+
+		case GDK_BUTTON_PRESS:
+		case GDK_2BUTTON_PRESS:
+		case GDK_3BUTTON_PRESS:
+			mask = GDK_BUTTON_PRESS_MASK;
+			break;
+
+		case GDK_BUTTON_RELEASE:
+			mask = GDK_BUTTON_RELEASE_MASK;
+			break;
+
+		case GDK_KEY_PRESS:
+			mask = GDK_KEY_PRESS_MASK;
+			break;
+
+		case GDK_KEY_RELEASE:
+			mask = GDK_KEY_RELEASE_MASK;
+			break;
+
+		default:
+			mask = 0;
+			break;
+		}
+
+		if (!(mask & canvas->grabbed_event_mask))
+			return FALSE;
+	}
+
+	/* Convert to world coordinates -- we have two cases because of diferent
+	 * offsets of the fields in the event structures.
+	 */
+
+	ev = *event;
+
+	switch (ev.type)
+        {
+	case GDK_ENTER_NOTIFY:
+	case GDK_LEAVE_NOTIFY:
+		foo_canvas_window_to_world (canvas,
+					      ev.crossing.x, ev.crossing.y,
+					      &ev.crossing.x, &ev.crossing.y);
+		break;
+
+	case GDK_MOTION_NOTIFY:
+                foo_canvas_window_to_world (canvas,
+                                              ev.motion.x, ev.motion.y,
+                                              &ev.motion.x, &ev.motion.y);
+                break;
+
+	case GDK_BUTTON_PRESS:
+	case GDK_2BUTTON_PRESS:
+	case GDK_3BUTTON_PRESS:
+                foo_canvas_window_to_world (canvas,
+                                              ev.motion.x, ev.motion.y,
+                                              &ev.motion.x, &ev.motion.y);
+                break;
+
+	case GDK_BUTTON_RELEASE:
+		foo_canvas_window_to_world (canvas,
+					      ev.motion.x, ev.motion.y,
+					      &ev.motion.x, &ev.motion.y);
+		break;
+
+	default:
+		break;
+	}
+
+	/* Choose where we send the event */
+
+	item = canvas->current_item;
+
+	if (canvas->focused_item
+	    && ((event->type == GDK_KEY_PRESS) ||
+		(event->type == GDK_KEY_RELEASE) ||
+		(event->type == GDK_FOCUS_CHANGE)))
+		item = canvas->focused_item;
+
+	/* The event is propagated up the hierarchy (for if someone connected to
+	 * a group instead of a leaf event), and emission is stopped if a
+	 * handler returns TRUE, just like for GtkWidget events.
+	 */
+
+	finished = FALSE;
+
+	while (item && !finished) {
+		g_object_ref (GTK_OBJECT (item));
+
+		g_signal_emit (
+		       G_OBJECT (item), item_signals[ITEM_EVENT], 0,
+			&ev, &finished);
+		
+		parent = item->parent;
+		g_object_unref (GTK_OBJECT (item));
+
+		item = parent;
+	}
+
+	return finished;
+}
+
+/* Re-picks the current item in the canvas, based on the event's coordinates.
+ * Also emits enter/leave events for items as appropriate.
+ */
+static int
+pick_current_item (FooCanvas *canvas, GdkEvent *event)
+{
+	int button_down;
+	double x, y;
+	int cx, cy;
+	int retval;
+
+	retval = FALSE;
+
+	/* If a button is down, we'll perform enter and leave events on the
+	 * current item, but not enter on any other item.  This is more or less
+	 * like X pointer grabbing for canvas items.
+	 */
+	button_down = canvas->state & (GDK_BUTTON1_MASK
+				       | GDK_BUTTON2_MASK
+				       | GDK_BUTTON3_MASK
+				       | GDK_BUTTON4_MASK
+				       | GDK_BUTTON5_MASK);
+	if (!button_down)
+		canvas->left_grabbed_item = FALSE;
+
+	/* Save the event in the canvas.  This is used to synthesize enter and
+	 * leave events in case the current item changes.  It is also used to
+	 * re-pick the current item if the current one gets deleted.  Also,
+	 * synthesize an enter event.
+	 */
+	if (event != &canvas->pick_event) {
+		if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
+			/* these fields have the same offsets in both types of events */
+
+			canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
+			canvas->pick_event.crossing.window     = event->motion.window;
+			canvas->pick_event.crossing.send_event = event->motion.send_event;
+			canvas->pick_event.crossing.subwindow  = NULL;
+			canvas->pick_event.crossing.x          = event->motion.x;
+			canvas->pick_event.crossing.y          = event->motion.y;
+			canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
+			canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
+			canvas->pick_event.crossing.focus      = FALSE;
+			canvas->pick_event.crossing.state      = event->motion.state;
+
+			/* these fields don't have the same offsets in both types of events */
+
+			if (event->type == GDK_MOTION_NOTIFY) {
+				canvas->pick_event.crossing.x_root = event->motion.x_root;
+				canvas->pick_event.crossing.y_root = event->motion.y_root;
+			} else {
+				canvas->pick_event.crossing.x_root = event->button.x_root;
+				canvas->pick_event.crossing.y_root = event->button.y_root;
+			}
+		} else
+			canvas->pick_event = *event;
+	}
+
+	/* Don't do anything else if this is a recursive call */
+
+	if (canvas->in_repick)
+		return retval;
+
+	/* LeaveNotify means that there is no current item, so we don't look for one */
+
+	if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
+		/* these fields don't have the same offsets in both types of events */
+
+		if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
+			x = canvas->pick_event.crossing.x;
+			y = canvas->pick_event.crossing.y;
+		} else {
+			x = canvas->pick_event.motion.x;
+			y = canvas->pick_event.motion.y;
+		}
+
+		/* canvas pixel coords */
+
+		cx = (int) (x + 0.5);
+		cy = (int) (y + 0.5);
+
+		/* world coords */
+		foo_canvas_c2w (canvas, cx, cy, &x, &y);
+
+		/* find the closest item */
+		if (canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED)
+			foo_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
+							&canvas->new_current_item);
+		else
+			canvas->new_current_item = NULL;
+	} else
+		canvas->new_current_item = NULL;
+
+	if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
+		return retval; /* current item did not change */
+
+	/* Synthesize events for old and new current items */
+
+	if ((canvas->new_current_item != canvas->current_item)
+	    && (canvas->current_item != NULL)
+	    && !canvas->left_grabbed_item) {
+		GdkEvent new_event;
+		FooCanvasItem *item;
+
+		item = canvas->current_item;
+
+		new_event = canvas->pick_event;
+		new_event.type = GDK_LEAVE_NOTIFY;
+
+		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+		new_event.crossing.subwindow = NULL;
+		canvas->in_repick = TRUE;
+		retval = emit_event (canvas, &new_event);
+		canvas->in_repick = FALSE;
+	}
+
+	/* new_current_item may have been set to NULL during the call to emit_event() above */
+
+	if ((canvas->new_current_item != canvas->current_item) && button_down) {
+		canvas->left_grabbed_item = TRUE;
+		return retval;
+	}
+
+	/* Handle the rest of cases */
+
+	canvas->left_grabbed_item = FALSE;
+	canvas->current_item = canvas->new_current_item;
+
+	if (canvas->current_item != NULL) {
+		GdkEvent new_event;
+
+		new_event = canvas->pick_event;
+		new_event.type = GDK_ENTER_NOTIFY;
+		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+		new_event.crossing.subwindow = NULL;
+		retval = emit_event (canvas, &new_event);
+	}
+
+	return retval;
+}
+
+/* Button event handler for the canvas */
+static gint
+foo_canvas_button (GtkWidget *widget, GdkEventButton *event)
+{
+	FooCanvas *canvas;
+	int mask;
+	int retval;
+
+	g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	retval = FALSE;
+
+	canvas = FOO_CANVAS (widget);
+
+	/*
+	 * dispatch normally regardless of the event's window if an item has
+	 * has a pointer grab in effect
+	 */
+	if (!canvas->grabbed_item && event->window != canvas->layout.bin_window)
+		return retval;
+
+	switch (event->button) {
+	case 1:
+		mask = GDK_BUTTON1_MASK;
+		break;
+	case 2:
+		mask = GDK_BUTTON2_MASK;
+		break;
+	case 3:
+		mask = GDK_BUTTON3_MASK;
+		break;
+	case 4:
+		mask = GDK_BUTTON4_MASK;
+		break;
+	case 5:
+		mask = GDK_BUTTON5_MASK;
+		break;
+	default:
+		mask = 0;
+	}
+
+	switch (event->type) {
+	case GDK_BUTTON_PRESS:
+	case GDK_2BUTTON_PRESS:
+	case GDK_3BUTTON_PRESS:
+		/* Pick the current item as if the button were not pressed, and
+		 * then process the event.
+		 */
+		canvas->state = event->state;
+		pick_current_item (canvas, (GdkEvent *) event);
+		canvas->state ^= mask;
+		retval = emit_event (canvas, (GdkEvent *) event);
+		break;
+
+	case GDK_BUTTON_RELEASE:
+		/* Process the event as if the button were pressed, then repick
+		 * after the button has been released
+		 */
+		canvas->state = event->state;
+		retval = emit_event (canvas, (GdkEvent *) event);
+		event->state ^= mask;
+		canvas->state = event->state;
+		pick_current_item (canvas, (GdkEvent *) event);
+		event->state ^= mask;
+		break;
+
+	default:
+		g_assert_not_reached ();
+	}
+
+	return retval;
+}
+
+/* Motion event handler for the canvas */
+static gint
+foo_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+	FooCanvas *canvas;
+
+	g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	canvas = FOO_CANVAS (widget);
+
+	if (event->window != canvas->layout.bin_window)
+		return FALSE;
+
+	canvas->state = event->state;
+	pick_current_item (canvas, (GdkEvent *) event);
+	return emit_event (canvas, (GdkEvent *) event);
+}
+
+/* Key event handler for the canvas */
+static gint
+foo_canvas_key (GtkWidget *widget, GdkEventKey *event)
+{
+	FooCanvas *canvas;
+	
+	g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	canvas = FOO_CANVAS (widget);
+	
+	if (emit_event (canvas, (GdkEvent *) event))
+		return TRUE;
+	if (event->type == GDK_KEY_RELEASE)
+		return GTK_WIDGET_CLASS (canvas_parent_class)->key_release_event (widget, event);
+	else
+		return GTK_WIDGET_CLASS (canvas_parent_class)->key_press_event (widget, event);
+}
+
+
+/* Crossing event handler for the canvas */
+static gint
+foo_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
+{
+	FooCanvas *canvas;
+
+	g_return_val_if_fail (FOO_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	canvas = FOO_CANVAS (widget);
+
+	if (event->window != canvas->layout.bin_window)
+		return FALSE;
+
+	canvas->state = event->state;
+	return pick_current_item (canvas, (GdkEvent *) event);
+}
+
+/* Focus in handler for the canvas */
+static gint
+foo_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+	FooCanvas *canvas;
+
+	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	canvas = FOO_CANVAS (widget);
+
+	if (canvas->focused_item)
+		return emit_event (canvas, (GdkEvent *) event);
+	else
+		return FALSE;
+}
+
+/* Focus out handler for the canvas */
+static gint
+foo_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+	FooCanvas *canvas;
+
+	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	canvas = FOO_CANVAS (widget);
+
+	if (canvas->focused_item)
+		return emit_event (canvas, (GdkEvent *) event);
+	else
+		return FALSE;
+}
+
+/* Expose handler for the canvas */
+static gint
+foo_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+	FooCanvas *canvas;
+
+	canvas = FOO_CANVAS (widget);
+
+	if (!GTK_WIDGET_DRAWABLE (widget) || (event->window != canvas->layout.bin_window)) return FALSE;
+
+#ifdef VERBOSE
+	g_print ("Expose\n");
+#endif
+	/* If there are any outstanding items that need updating, do them now */
+	if (canvas->idle_id) {
+		g_source_remove (canvas->idle_id);
+		canvas->idle_id = 0;
+	}
+	if (canvas->need_update) {
+		g_return_val_if_fail (!canvas->doing_update, FALSE);
+
+		canvas->doing_update = TRUE;
+		foo_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+		g_return_val_if_fail (canvas->doing_update, FALSE);
+
+		canvas->doing_update = FALSE;
+
+		canvas->need_update = FALSE;
+	}
+
+	/* Hmmm. Would like to queue antiexposes if the update marked
+	   anything that is gonna get redrawn as invalid */
+	
+	
+	g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, 
+		       event->area.x, event->area.y,
+		       event->area.width, event->area.height);
+	
+	if (canvas->root->object.flags & FOO_CANVAS_ITEM_MAPPED)
+		(* FOO_CANVAS_ITEM_GET_CLASS (canvas->root)->draw) (canvas->root,
+								      canvas->layout.bin_window,
+								      event);
+
+
+
+	/* Chain up to get exposes on child widgets */
+	GTK_WIDGET_CLASS (canvas_parent_class)->expose_event (widget, event);
+
+	return FALSE;
+}
+
+static void
+foo_canvas_draw_background (FooCanvas *canvas,
+			    int x, int y, int width, int height)
+{
+	/* By default, we use the style background. */
+	gdk_gc_set_foreground (canvas->pixmap_gc,
+			       &GTK_WIDGET (canvas)->style->bg[GTK_STATE_NORMAL]);
+	gdk_draw_rectangle (canvas->layout.bin_window,
+			    canvas->pixmap_gc,
+			    TRUE,
+			    x, y,
+			    width, height);
+}
+
+static void
+do_update (FooCanvas *canvas)
+{
+	/* Cause the update if necessary */
+
+update_again:
+	if (canvas->need_update) {
+		g_return_if_fail (!canvas->doing_update);
+
+		canvas->doing_update = TRUE;
+		foo_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+		g_return_if_fail (canvas->doing_update);
+
+		canvas->doing_update = FALSE;
+
+		canvas->need_update = FALSE;
+	}
+
+	/* Pick new current item */
+
+	while (canvas->need_repick) {
+		canvas->need_repick = FALSE;
+		pick_current_item (canvas, &canvas->pick_event);
+	}
+
+	/* it is possible that during picking we emitted an event in which
+	   the user then called some function which then requested update
+	   of something.  Without this we'd be left in a state where
+	   need_update would have been left TRUE and the canvas would have
+	   been left unpainted. */
+	if (canvas->need_update) {
+		goto update_again;
+	}
+}
+
+/* Idle handler for the canvas.  It deals with pending updates and redraws. */
+static gint
+idle_handler (gpointer data)
+{
+	FooCanvas *canvas;
+
+	GDK_THREADS_ENTER ();
+
+	canvas = FOO_CANVAS (data);
+	do_update (canvas);
+
+	/* Reset idle id */
+	canvas->idle_id = 0;
+
+	GDK_THREADS_LEAVE ();
+
+	return FALSE;
+}
+
+/* Convenience function to add an idle handler to a canvas */
+static void
+add_idle (FooCanvas *canvas)
+{
+	if (!canvas->idle_id) {
+		/* We let the update idle handler have higher priority
+		 * than the redraw idle handler so the canvas state
+		 * will be updated during the expose event.  canvas in
+		 * expose_event.
+		 */
+		canvas->idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW - 20,
+						   idle_handler, canvas, NULL);
+	}
+}
+
+/**
+ * foo_canvas_root:
+ * @canvas: A canvas.
+ *
+ * Queries the root group of a canvas.
+ *
+ * Return value: The root group of the specified canvas.
+ **/
+FooCanvasGroup *
+foo_canvas_root (FooCanvas *canvas)
+{
+	g_return_val_if_fail (FOO_IS_CANVAS (canvas), NULL);
+
+	return FOO_CANVAS_GROUP (canvas->root);
+}
+
+
+/**
+ * foo_canvas_set_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region.
+ * @y1: Upper limit of the scrolling region.
+ * @x2: Rightmost limit of the scrolling region.
+ * @y2: Lower limit of the scrolling region.
+ *
+ * Sets the scrolling region of a canvas to the specified rectangle.  The canvas
+ * will then be able to scroll only within this region.  The view of the canvas
+ * is adjusted as appropriate to display as much of the new region as possible.
+ **/
+void
+foo_canvas_set_scroll_region (FooCanvas *canvas, double x1, double y1, double x2, double y2)
+{
+	double wxofs, wyofs;
+	int xofs, yofs;
+
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	if ((canvas->scroll_x1 == x1) && (canvas->scroll_y1 == y1) &&
+	    (canvas->scroll_x2 == x2) && (canvas->scroll_y2 == y2)) {
+		return;
+	}
+	
+	/*
+	 * Set the new scrolling region.  If possible, do not move the visible contents of the
+	 * canvas.
+	 */
+
+	foo_canvas_c2w (canvas,
+			  GTK_LAYOUT (canvas)->hadjustment->value + canvas->zoom_xofs,
+			  GTK_LAYOUT (canvas)->vadjustment->value + canvas->zoom_yofs,
+			  /*canvas->zoom_xofs,
+			  canvas->zoom_yofs,*/
+			  &wxofs, &wyofs);
+
+	canvas->scroll_x1 = x1;
+	canvas->scroll_y1 = y1;
+	canvas->scroll_x2 = x2;
+	canvas->scroll_y2 = y2;
+
+	foo_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
+
+	scroll_to (canvas, xofs, yofs);
+
+	canvas->need_repick = TRUE;
+
+	if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+		canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		foo_canvas_request_update (canvas);
+	}
+}
+
+
+/**
+ * foo_canvas_get_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region (return value).
+ * @y1: Upper limit of the scrolling region (return value).
+ * @x2: Rightmost limit of the scrolling region (return value).
+ * @y2: Lower limit of the scrolling region (return value).
+ *
+ * Queries the scrolling region of a canvas.
+ **/
+void
+foo_canvas_get_scroll_region (FooCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	if (x1)
+		*x1 = canvas->scroll_x1;
+
+	if (y1)
+		*y1 = canvas->scroll_y1;
+
+	if (x2)
+		*x2 = canvas->scroll_x2;
+
+	if (y2)
+		*y2 = canvas->scroll_y2;
+}
+
+void
+foo_canvas_set_center_scroll_region (FooCanvas *canvas,
+				     gboolean center_scroll_region)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	canvas->center_scroll_region = center_scroll_region != 0;
+
+	scroll_to (canvas,
+		   canvas->layout.hadjustment->value,
+		   canvas->layout.vadjustment->value);
+}
+
+
+/**
+ * foo_canvas_set_pixels_per_unit:
+ * @canvas: A canvas.
+ * @n: The number of pixels that correspond to one canvas unit.
+ *
+ * Sets the zooming factor of a canvas by specifying the number of pixels that
+ * correspond to one canvas unit.
+ **/
+void
+foo_canvas_set_pixels_per_unit (FooCanvas *canvas, double n)
+{
+	GtkWidget *widget;
+	double cx, cy;
+	int x1, y1;
+	int center_x, center_y;
+	GdkWindow *window;
+	GdkWindowAttr attributes;
+	gint attributes_mask;
+
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+	g_return_if_fail (n > FOO_CANVAS_EPSILON);
+
+	widget = GTK_WIDGET (canvas);
+
+	center_x = widget->allocation.width / 2;
+	center_y = widget->allocation.height / 2;
+
+	/* Find the coordinates of the screen center in units. */
+	cx = (canvas->layout.hadjustment->value + center_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+	cy = (canvas->layout.vadjustment->value + center_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+
+	/* Now calculate the new offset of the upper left corner. (round not truncate) */
+	x1 = ((cx - canvas->scroll_x1) * n) - center_x + .5;
+	y1 = ((cy - canvas->scroll_y1) * n) - center_y + .5;
+
+	canvas->pixels_per_unit = n;
+
+	if (!(canvas->root->object.flags & FOO_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+		canvas->root->object.flags |= FOO_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		foo_canvas_request_update (canvas);
+	}
+
+	/* Map a background None window over the bin_window to avoid
+	 * scrolling the window scroll causing exposes.
+	 */
+	window = NULL;
+	if (GTK_WIDGET_MAPPED (widget)) {
+		attributes.window_type = GDK_WINDOW_CHILD;
+		attributes.x = widget->allocation.x;
+		attributes.y = widget->allocation.y;
+		attributes.width = widget->allocation.width;
+		attributes.height = widget->allocation.height;
+		attributes.wclass = GDK_INPUT_OUTPUT;
+		attributes.visual = gtk_widget_get_visual (widget);
+		attributes.colormap = gtk_widget_get_colormap (widget);
+		attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+		
+		attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+		
+		window = gdk_window_new (gtk_widget_get_parent_window (widget),
+					 &attributes, attributes_mask);
+		gdk_window_set_back_pixmap (window, NULL, FALSE);
+		gdk_window_set_user_data (window, widget);
+		
+		gdk_window_show (window);
+	}
+
+	scroll_to (canvas, x1, y1);
+
+	/* If we created a an overlapping background None window, remove it how.
+	 *
+	 * TODO: We would like to temporarily set the bin_window background to
+	 * None to avoid clearing the bin_window to the background, but gdk doesn't
+	 * expose enought to let us do this, so we get a flash-effect here. At least
+	 * it looks better than scroll + expose.
+	 */
+	if (window != NULL) {
+		gdk_window_hide (window);
+		gdk_window_set_user_data (window, NULL);
+		gdk_window_destroy (window);
+	}
+
+	canvas->need_repick = TRUE;
+}
+
+/**
+ * foo_canvas_scroll_to:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset in canvas pixel units.
+ * @cy: Vertical scrolling offset in canvas pixel units.
+ *
+ * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
+ * The canvas will adjust the view so that it is not outside the scrolling
+ * region.  This function is typically not used, as it is better to hook
+ * scrollbars to the canvas layout's scrolling adjusments.
+ **/
+void
+foo_canvas_scroll_to (FooCanvas *canvas, int cx, int cy)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	scroll_to (canvas, cx, cy);
+}
+
+/**
+ * foo_canvas_get_scroll_offsets:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset (return value).
+ * @cy: Vertical scrolling offset (return value).
+ *
+ * Queries the scrolling offsets of a canvas.  The values are returned in canvas
+ * pixel units.
+ **/
+void
+foo_canvas_get_scroll_offsets (FooCanvas *canvas, int *cx, int *cy)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	if (cx)
+		*cx = canvas->layout.hadjustment->value;
+
+	if (cy)
+		*cy = canvas->layout.vadjustment->value;
+}
+
+/**
+ * foo_canvas_update_now:
+ * @canvas: A canvas.
+ *
+ * Forces an immediate update and redraw of a canvas.  If the canvas does not
+ * have any pending update or redraw requests, then no action is taken.  This is
+ * typically only used by applications that need explicit control of when the
+ * display is updated, like games.  It is not needed by normal applications.
+ */
+void
+foo_canvas_update_now (FooCanvas *canvas)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	if (!(canvas->need_update || canvas->need_redraw))
+		return;
+	remove_idle (canvas);
+	do_update (canvas);
+}
+
+/**
+ * foo_canvas_get_item_at:
+ * @canvas: A canvas.
+ * @x: X position in world coordinates.
+ * @y: Y position in world coordinates.
+ *
+ * Looks for the item that is under the specified position, which must be
+ * specified in world coordinates.
+ *
+ * Return value: The sought item, or NULL if no item is at the specified
+ * coordinates.
+ **/
+FooCanvasItem *
+foo_canvas_get_item_at (FooCanvas *canvas, double x, double y)
+{
+	FooCanvasItem *item;
+	double dist;
+	int cx, cy;
+
+	g_return_val_if_fail (FOO_IS_CANVAS (canvas), NULL);
+
+	foo_canvas_w2c (canvas, x, y, &cx, &cy);
+
+	dist = foo_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
+	if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
+		return item;
+	else
+		return NULL;
+}
+
+/* Queues an update of the canvas */
+static void
+foo_canvas_request_update (FooCanvas *canvas)
+{
+	FOO_CANVAS_GET_CLASS (canvas)->request_update (canvas);
+}
+
+static void
+foo_canvas_request_update_real (FooCanvas *canvas)
+{
+	canvas->need_update = TRUE;
+	add_idle (canvas);
+}
+
+/**
+ * foo_canvas_request_redraw:
+ * @canvas: A canvas.
+ * @x1: Leftmost coordinate of the rectangle to be redrawn.
+ * @y1: Upper coordinate of the rectangle to be redrawn.
+ * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
+ * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
+ *
+ * Convenience function that informs a canvas that the specified rectangle needs
+ * to be repainted.  The rectangle includes @x1 and @y1, but not @x2 and @y2.
+ * To be used only by item implementations.
+ **/
+void
+foo_canvas_request_redraw (FooCanvas *canvas, int x1, int y1, int x2, int y2)
+{
+	GdkRectangle bbox;
+
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	if (!GTK_WIDGET_DRAWABLE (canvas) || (x1 >= x2) || (y1 >= y2)) return;
+
+	bbox.x = x1;
+	bbox.y = y1;
+	bbox.width = x2 - x1;
+	bbox.height = y2 - y1;
+
+	gdk_window_invalidate_rect (canvas->layout.bin_window,
+				    &bbox, FALSE);
+}
+
+/**
+ * foo_canvas_w2c:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.
+ **/
+void
+foo_canvas_w2c (FooCanvas *canvas, double wx, double wy, int *cx, int *cy)
+{
+	double zoom;
+
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+	
+	zoom = canvas->pixels_per_unit;
+	
+	if (cx)
+		*cx = floor ((wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs + 0.5);
+	if (cy)
+		*cy = floor ((wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs + 0.5);
+}
+
+/**
+ * foo_canvas_w2c:
+ * @canvas: A canvas.
+ * @world: rectangle in world coordinates.
+ * @canvas: rectangle in canvase coordinates.
+ *
+ * Converts rectangles in world coordinates into canvas pixel coordinates.
+ **/
+void
+foo_canvas_w2c_rect_d (FooCanvas *canvas,
+			 double *x1, double *y1,
+			 double *x2, double *y2)
+{
+	foo_canvas_w2c_d (canvas,
+			    *x1, *y1,
+			    x1, y1);
+	foo_canvas_w2c_d (canvas,
+			    *x2, *y2,
+			    x2, y2);
+}
+
+
+
+/**
+ * foo_canvas_w2c_d:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.  This version
+ * produces coordinates in floating point coordinates, for greater precision.
+ **/
+void
+foo_canvas_w2c_d (FooCanvas *canvas, double wx, double wy, double *cx, double *cy)
+{
+	double zoom;
+
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	zoom = canvas->pixels_per_unit;
+	
+	if (cx)
+		*cx = (wx - canvas->scroll_x1)*zoom + canvas->zoom_xofs;
+	if (cy)
+		*cy = (wy - canvas->scroll_y1)*zoom + canvas->zoom_yofs;
+}
+
+
+/**
+ * foo_canvas_c2w:
+ * @canvas: A canvas.
+ * @cx: Canvas pixel X coordinate.
+ * @cy: Canvas pixel Y coordinate.
+ * @wx: X world coordinate (return value).
+ * @wy: Y world coordinate (return value).
+ *
+ * Converts canvas pixel coordinates to world coordinates.
+ **/
+void
+foo_canvas_c2w (FooCanvas *canvas, int cx, int cy, double *wx, double *wy)
+{
+	double zoom;
+
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	zoom = canvas->pixels_per_unit;
+	
+	if (wx)
+		*wx = (cx - canvas->zoom_xofs)/zoom + canvas->scroll_x1;
+	if (wy)
+		*wy = (cy - canvas->zoom_yofs)/zoom + canvas->scroll_y1;
+}
+
+
+/**
+ * foo_canvas_window_to_world:
+ * @canvas: A canvas.
+ * @winx: Window-relative X coordinate.
+ * @winy: Window-relative Y coordinate.
+ * @worldx: X world coordinate (return value).
+ * @worldy: Y world coordinate (return value).
+ *
+ * Converts window-relative coordinates into world coordinates.  You can use
+ * this when you need to convert mouse coordinates into world coordinates, for
+ * example.
+ * Window coordinates are really the same as canvas coordinates now, but this
+ * function is here for backwards compatibility reasons.
+ **/
+void
+foo_canvas_window_to_world (FooCanvas *canvas, double winx, double winy,
+			      double *worldx, double *worldy)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	if (worldx)
+		*worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
+					       / canvas->pixels_per_unit);
+
+	if (worldy)
+		*worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
+					       / canvas->pixels_per_unit);
+}
+
+
+/**
+ * foo_canvas_world_to_window:
+ * @canvas: A canvas.
+ * @worldx: World X coordinate.
+ * @worldy: World Y coordinate.
+ * @winx: X window-relative coordinate.
+ * @winy: Y window-relative coordinate.
+ *
+ * Converts world coordinates into window-relative coordinates.
+ * Window coordinates are really the same as canvas coordinates now, but this
+ * function is here for backwards compatibility reasons.
+ **/
+void
+foo_canvas_world_to_window (FooCanvas *canvas, double worldx, double worldy,
+			    double *winx, double *winy)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+
+	if (winx)
+		*winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
+
+	if (winy)
+		*winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
+}
+
+
+
+/**
+ * foo_canvas_get_color:
+ * @canvas: A canvas.
+ * @spec: X color specification, or NULL for "transparent".
+ * @color: Returns the allocated color.
+ *
+ * Allocates a color based on the specified X color specification.  As a
+ * convenience to item implementations, it returns TRUE if the color was
+ * allocated, or FALSE if the specification was NULL.  A NULL color
+ * specification is considered as "transparent" by the canvas.
+ *
+ * Return value: TRUE if @spec is non-NULL and the color is allocated.  If @spec
+ * is NULL, then returns FALSE.
+ **/
+int
+foo_canvas_get_color (FooCanvas *canvas, const char *spec, GdkColor *color)
+{
+	GdkColormap *colormap;
+
+	g_return_val_if_fail (FOO_IS_CANVAS (canvas), FALSE);
+	g_return_val_if_fail (color != NULL, FALSE);
+
+	if (!spec) {
+		color->pixel = 0;
+		color->red = 0;
+		color->green = 0;
+		color->blue = 0;
+		return FALSE;
+	}
+
+	gdk_color_parse (spec, color);
+
+	colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+	gdk_rgb_find_color (colormap, color);
+
+	return TRUE;
+}
+
+/**
+ * foo_canvas_get_color_pixel:
+ * @canvas: A canvas.
+ * @rgba: RGBA color specification.
+ *
+ * Allocates a color from the RGBA value passed into this function.  The alpha
+ * opacity value is discarded, since normal X colors do not support it.
+ *
+ * Return value: Allocated pixel value corresponding to the specified color.
+ **/
+gulong
+foo_canvas_get_color_pixel (FooCanvas *canvas, guint rgba)
+{
+	GdkColormap *colormap;
+	GdkColor color;
+
+	g_return_val_if_fail (FOO_IS_CANVAS (canvas), 0);
+
+	color.red = ((rgba & 0xff000000) >> 16) + ((rgba & 0xff000000) >> 24);
+	color.green = ((rgba & 0x00ff0000) >> 8) + ((rgba & 0x00ff0000) >> 16);
+	color.blue = (rgba & 0x0000ff00) + ((rgba & 0x0000ff00) >> 8);
+	color.pixel = 0;
+
+	colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+	gdk_rgb_find_color (colormap, &color);
+
+	return color.pixel;
+}
+
+
+/* FIXME: This function is not useful anymore */
+/**
+ * foo_canvas_set_stipple_origin:
+ * @canvas: A canvas.
+ * @gc: GC on which to set the stipple origin.
+ *
+ * Sets the stipple origin of the specified GC as is appropriate for the canvas,
+ * so that it will be aligned with other stipple patterns used by canvas items.
+ * This is typically only needed by item implementations.
+ **/
+void
+foo_canvas_set_stipple_origin (FooCanvas *canvas, GdkGC *gc)
+{
+	g_return_if_fail (FOO_IS_CANVAS (canvas));
+	g_return_if_fail (GDK_IS_GC (gc));
+
+	gdk_gc_set_ts_origin (gc, 0, 0);
+}
+
+static gboolean
+boolean_handled_accumulator (GSignalInvocationHint *ihint,
+			     GValue                *return_accu,
+			     const GValue          *handler_return,
+			     gpointer               dummy)
+{
+	gboolean continue_emission;
+	gboolean signal_handled;
+	
+	signal_handled = g_value_get_boolean (handler_return);
+	g_value_set_boolean (return_accu, signal_handled);
+	continue_emission = !signal_handled;
+	
+	return continue_emission;
+}
+
+static guint
+foo_canvas_item_accessible_add_focus_handler (AtkComponent    *component,
+                                              AtkFocusHandler handler)
+{
+ 	GSignalMatchType match_type;
+	guint signal_id;
+
+	match_type = G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC;
+	signal_id = g_signal_lookup ("focus-event", ATK_TYPE_OBJECT);
+
+	if (!g_signal_handler_find (component, match_type, signal_id, 0, NULL,
+                                    (gpointer) handler, NULL)) {
+		return g_signal_connect_closure_by_id (component,
+                                                       signal_id, 0,
+                                                       g_cclosure_new (
+                                                       G_CALLBACK (handler), NULL,
+                                                       (GClosureNotify) NULL),
+                                                       FALSE);
+	} 
+	return 0;
+}
+
+static void
+foo_canvas_item_accessible_get_item_extents (FooCanvasItem *item,
+                                             GdkRectangle  *rect)
+{
+ 	double bx1, bx2, by1, by2;
+	gint scroll_x, scroll_y;
+	gint x1, x2, y1, y2;
+
+	foo_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
+	foo_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
+	foo_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
+	x1 = floor (bx1 + .5);
+	y1 = floor (by1 + .5);
+	x2 = floor (bx2 + .5);
+	y2 = floor (by2 + .5);
+	rect->x = x1 - scroll_x;
+	rect->y = y1 - scroll_y;
+	rect->width = x2 - x1;
+	rect->height = y2 - y1;
+}
+
+static gboolean
+foo_canvas_item_accessible_is_item_in_window (FooCanvasItem *item,
+                                              GdkRectangle  *rect)
+{
+ 	GtkWidget *widget;
+	gboolean retval;
+
+	widget = GTK_WIDGET (item->canvas);
+	if (widget->window) {
+		int window_width, window_height;
+
+		gdk_window_get_geometry (widget->window, NULL, NULL,
+                                         &window_width, &window_height, NULL);
+		/*
+                 * Check whether rectangles intersect
+		 */
+                if (rect->x + rect->width < 0 ||
+                    rect->y + rect->height < 0 ||
+                    rect->x > window_width  ||
+                    rect->y > window_height) {
+			retval = FALSE;
+		} else {
+                        retval = TRUE;
+		}
+	} else {
+                retval = FALSE;
+	}
+        return retval;
+}
+
+
+static void
+foo_canvas_item_accessible_get_extents (AtkComponent *component,
+                                        gint		*x,
+                                        gint		*y,
+                                        gint		*width,
+                                        gint		*height,
+                                        AtkCoordType coord_type)
+{
+ 	AtkGObjectAccessible *atk_gobj;
+	GObject *obj;
+	FooCanvasItem *item;
+	gint window_x, window_y;
+	gint toplevel_x, toplevel_y;
+	GdkRectangle rect;
+	GdkWindow *window;
+	GtkWidget *canvas;
+
+	atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+	obj = atk_gobject_accessible_get_object (atk_gobj);
+
+	if (obj == NULL) {
+		/* item is defunct */
+		return;
+	}
+
+        /* Get the CanvasItem */
+	item = FOO_CANVAS_ITEM (obj);
+
+	/* If this item has no parent canvas, something's broken */
+	g_return_if_fail (GTK_IS_WIDGET (item->canvas));
+
+	foo_canvas_item_accessible_get_item_extents (item, &rect);
+	*width = rect.width;
+	*height = rect.height;
+	if (!foo_canvas_item_accessible_is_item_in_window (item, &rect)) {
+		*x = G_MININT;
+		*y = G_MININT;
+		return;
+	}
+
+        canvas = GTK_WIDGET (item->canvas);
+	window = gtk_widget_get_parent_window (canvas);
+	gdk_window_get_origin (window, &window_x, &window_y);
+	*x = rect.x + window_x;
+	*y = rect.y + window_y;
+	if (coord_type == ATK_XY_WINDOW) {
+		window = gdk_window_get_toplevel (canvas->window);
+		gdk_window_get_origin (window, &toplevel_x, &toplevel_y);
+		*x -= toplevel_x;
+		*y -= toplevel_y;
+	}
+        return;
+}
+
+static gint
+foo_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
+{
+	AtkGObjectAccessible *atk_gobj;
+	GObject *g_obj;
+	FooCanvasItem *item;
+
+	atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+	g_obj = atk_gobject_accessible_get_object (atk_gobj);
+	if (g_obj == NULL) {
+		/* Object is defunct */
+		return -1;
+	}
+
+	item = FOO_CANVAS_ITEM (g_obj);
+	if (item->parent) {
+       		return g_list_index (FOO_CANVAS_GROUP (item->parent)->item_list, item);
+	} else {
+		g_return_val_if_fail (item->canvas->root == item, -1);
+		return 0;
+	}
+}
+
+static gboolean
+foo_canvas_item_accessible_grab_focus (AtkComponent *component)
+{
+ 	AtkGObjectAccessible *atk_gobj;
+	GObject *obj;
+	FooCanvasItem *item;
+	GtkWidget *toplevel;
+
+	atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+	obj = atk_gobject_accessible_get_object (atk_gobj);
+
+	item = FOO_CANVAS_ITEM (obj);
+	if (item == NULL) {
+		/* item is defunct */
+		return FALSE;
+	}
+
+        foo_canvas_item_grab_focus (item);
+	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (item->canvas));
+	if (GTK_WIDGET_TOPLEVEL (toplevel)) {
+		gtk_window_present (GTK_WINDOW (toplevel));
+	}
+
+	return TRUE;
+}
+
+static void
+foo_canvas_item_accessible_remove_focus_handler (AtkComponent *component,
+                                                 guint		handler_id)
+{
+ 	g_signal_handler_disconnect (component, handler_id);
+}
+
+static void
+foo_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
+{
+	g_return_if_fail (iface != NULL);
+
+	iface->add_focus_handler = foo_canvas_item_accessible_add_focus_handler;
+	iface->get_extents = foo_canvas_item_accessible_get_extents;
+	iface->get_mdi_zorder = foo_canvas_item_accessible_get_mdi_zorder;
+	iface->grab_focus = foo_canvas_item_accessible_grab_focus;
+      	iface->remove_focus_handler = foo_canvas_item_accessible_remove_focus_handler;
+}
+
+static gboolean
+foo_canvas_item_accessible_is_item_on_screen (FooCanvasItem *item)
+{
+	GdkRectangle rect;
+
+	foo_canvas_item_accessible_get_item_extents (item, &rect);
+	return foo_canvas_item_accessible_is_item_in_window (item, &rect);
+}
+
+static void
+foo_canvas_item_accessible_initialize (AtkObject *obj, gpointer data)
+{
+	if (ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize != NULL)
+		ATK_OBJECT_CLASS (accessible_item_parent_class)->initialize (obj, data);
+	g_object_set_data (G_OBJECT (obj), "atk-component-layer",
+			   GINT_TO_POINTER (ATK_LAYER_MDI));
+}
+
+static AtkStateSet*
+foo_canvas_item_accessible_ref_state_set (AtkObject *accessible)
+{
+ 	AtkGObjectAccessible *atk_gobj;
+	GObject *obj;
+ 	FooCanvasItem *item;
+	AtkStateSet *state_set;
+
+	state_set = ATK_OBJECT_CLASS (accessible_item_parent_class)->ref_state_set (accessible);
+	atk_gobj = ATK_GOBJECT_ACCESSIBLE (accessible);
+	obj = atk_gobject_accessible_get_object (atk_gobj);
+
+	item = FOO_CANVAS_ITEM (obj);
+	if (item == NULL) {
+		atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+	} else {
+                if (item->object.flags & FOO_CANVAS_ITEM_VISIBLE) {
+			atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
+			
+			if (foo_canvas_item_accessible_is_item_on_screen (item)) {
+  				atk_state_set_add_state (state_set, ATK_STATE_SHOWING);
+       			}
+		}
+        	if (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas))) {
+			atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE);
+
+			if (item->canvas->focused_item == item) {
+				atk_state_set_add_state (state_set, ATK_STATE_FOCUSED);
+			}
+		}
+	}
+
+        return state_set;
+}
+
+static void
+foo_canvas_item_accessible_class_init (AtkObjectClass *klass)
+{
+ 	accessible_item_parent_class = g_type_class_peek_parent (klass);
+
+	klass->initialize = foo_canvas_item_accessible_initialize;
+	klass->ref_state_set = foo_canvas_item_accessible_ref_state_set;
+}
+
+static GType
+foo_canvas_item_accessible_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static const GInterfaceInfo atk_component_info = {
+			(GInterfaceInitFunc) foo_canvas_item_accessible_component_interface_init,
+                 	(GInterfaceFinalizeFunc) NULL,
+			NULL
+		};
+		AtkObjectFactory *factory;
+		GType parent_atk_type;
+		GTypeQuery query;
+		GTypeInfo tinfo = { 0 };
+
+		factory = atk_registry_get_factory (atk_get_default_registry(),
+						    GTK_TYPE_OBJECT);
+		if (!factory) {
+			return G_TYPE_INVALID;
+		}
+		parent_atk_type = atk_object_factory_get_accessible_type (factory);
+		if (!parent_atk_type) {
+			return G_TYPE_INVALID;
+		}
+		g_type_query (parent_atk_type, &query);
+		tinfo.class_init = (GClassInitFunc) foo_canvas_item_accessible_class_init;
+		tinfo.class_size = query.class_size;
+		tinfo.instance_size = query.instance_size;
+		type = g_type_register_static (parent_atk_type,
+					       "FooCanvasItemAccessibility",
+					       &tinfo, 0);
+
+		g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
+					     &atk_component_info);
+
+	}
+
+	return type;
+}
+
+static AtkObject *
+foo_canvas_item_accessible_create (GObject *for_object)
+{
+	GType type;
+	AtkObject *accessible;
+	FooCanvasItem *item;
+
+	item = FOO_CANVAS_ITEM (for_object);
+	g_return_val_if_fail (item != NULL, NULL);
+
+	type = foo_canvas_item_accessible_get_type ();
+	if (type == G_TYPE_INVALID) {
+		return atk_no_op_object_new (for_object);
+	}
+
+        accessible = g_object_new (type, NULL);
+	atk_object_initialize (accessible, for_object);
+	return accessible;
+}
+
+static GType
+foo_canvas_item_accessible_factory_get_accessible_type (void)
+{
+	return foo_canvas_item_accessible_get_type ();
+}
+
+static AtkObject*
+foo_canvas_item_accessible_factory_create_accessible (GObject *obj)
+{
+	AtkObject *accessible;
+
+	g_return_val_if_fail (G_IS_OBJECT (obj), NULL);
+
+	accessible = foo_canvas_item_accessible_create (obj);
+
+	return accessible;
+}
+
+static void
+foo_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+	klass->create_accessible = foo_canvas_item_accessible_factory_create_accessible;
+	klass->get_accessible_type = foo_canvas_item_accessible_factory_get_accessible_type;
+}
+ 
+static GType
+foo_canvas_item_accessible_factory_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		static const GTypeInfo tinfo = {
+			sizeof (AtkObjectFactoryClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) foo_canvas_item_accessible_factory_class_init,
+			NULL,		/* class_finalize */
+			NULL,		/* class_data */
+			sizeof (AtkObjectFactory),
+			0,		/* n_preallocs */
+			NULL
+		};
+		type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY,
+					       "FooCanvasItemAccessibilityFactory",
+					       &tinfo, 0);
+	}
+
+	return type;
+}
+
+/* Class initialization function for FooCanvasItemClass */
+static void
+foo_canvas_item_class_init (FooCanvasItemClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass *) klass;
+
+	item_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = foo_canvas_item_set_property;
+	gobject_class->get_property = foo_canvas_item_get_property;
+	gobject_class->dispose = foo_canvas_item_dispose;
+
+	g_object_class_install_property
+		(gobject_class, ITEM_PROP_PARENT,
+		 g_param_spec_object ("parent", NULL, NULL,
+				      FOO_TYPE_CANVAS_ITEM,
+				      G_PARAM_READWRITE));
+	
+	g_object_class_install_property
+		(gobject_class, ITEM_PROP_VISIBLE,
+		 g_param_spec_boolean ("visible", NULL, NULL,
+				      TRUE,
+				      G_PARAM_READWRITE));
+
+	item_signals[ITEM_EVENT] =
+		g_signal_new ("event",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (FooCanvasItemClass, event),
+			      boolean_handled_accumulator, NULL,
+			      foo_canvas_marshal_BOOLEAN__BOXED,
+			      G_TYPE_BOOLEAN, 1,
+			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+	klass->realize = foo_canvas_item_realize;
+	klass->unrealize = foo_canvas_item_unrealize;
+	klass->map = foo_canvas_item_map;
+	klass->unmap = foo_canvas_item_unmap;
+	klass->update = foo_canvas_item_update;
+
+	atk_registry_set_factory_type (atk_get_default_registry (),
+                                       FOO_TYPE_CANVAS_ITEM,
+                                       foo_canvas_item_accessible_factory_get_type ());
+}
diff --git a/libfoocanvas/foo-canvas.h b/libfoocanvas/foo-canvas.h
new file mode 100644
index 0000000..401ba63
--- /dev/null
+++ b/libfoocanvas/foo-canvas.h
@@ -0,0 +1,530 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* FooCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * FooCanvas is basically a port of the Tk toolkit's most excellent canvas
+ * widget.  Tk is copyrighted by the Regents of the University of California,
+ * Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph gimp org>
+ */
+
+#ifndef FOO_CANVAS_H
+#define FOO_CANVAS_H
+
+#include <gtk/gtklayout.h>
+#include <gdk/gdkevents.h>
+#include <stdarg.h>
+
+G_BEGIN_DECLS
+
+
+/* "Small" value used by canvas stuff */
+#define FOO_CANVAS_EPSILON 1e-10
+
+
+/* Macros for building colors that fit in a 32-bit integer.  The values are in
+ * [0, 255].
+ */
+
+#define FOO_CANVAS_COLOR(r, g, b) ((((int) (r) & 0xff) << 24)	\
+				     | (((int) (g) & 0xff) << 16)	\
+				     | (((int) (b) & 0xff) << 8)	\
+				     | 0xff)
+
+#define FOO_CANVAS_COLOR_A(r, g, b, a) ((((int) (r) & 0xff) << 24)	\
+					  | (((int) (g) & 0xff) << 16)	\
+					  | (((int) (b) & 0xff) << 8)	\
+					  | ((int) (a) & 0xff))
+
+
+typedef struct _FooCanvas           FooCanvas;
+typedef struct _FooCanvasClass      FooCanvasClass;
+typedef struct _FooCanvasItem       FooCanvasItem;
+typedef struct _FooCanvasItemClass  FooCanvasItemClass;
+typedef struct _FooCanvasGroup      FooCanvasGroup;
+typedef struct _FooCanvasGroupClass FooCanvasGroupClass;
+
+
+/* FooCanvasItem - base item class for canvas items
+ *
+ * All canvas items are derived from FooCanvasItem.  The only information a
+ * FooCanvasItem contains is its parent canvas, its parent canvas item group,
+ * and its bounding box in world coordinates.
+ *
+ * Items inside a canvas are organized in a tree of FooCanvasItemGroup nodes
+ * and FooCanvasItem leaves.  Each canvas has a single root group, which can
+ * be obtained with the foo_canvas_get_root() function.
+ *
+ * The abstract FooCanvasItem class does not have any configurable or
+ * queryable attributes.
+ */
+
+/* Object flags for items */
+enum {
+	FOO_CANVAS_ITEM_REALIZED         = 1 << 4,
+	FOO_CANVAS_ITEM_MAPPED           = 1 << 5,
+	FOO_CANVAS_ITEM_ALWAYS_REDRAW    = 1 << 6,
+	FOO_CANVAS_ITEM_VISIBLE          = 1 << 7,
+	FOO_CANVAS_ITEM_NEED_UPDATE      = 1 << 8,
+	FOO_CANVAS_ITEM_NEED_DEEP_UPDATE = 1 << 9
+};
+
+/* Update flags for items */
+enum {
+	FOO_CANVAS_UPDATE_REQUESTED  = 1 << 0,
+	FOO_CANVAS_UPDATE_DEEP       = 1 << 1
+};
+
+#define FOO_TYPE_CANVAS_ITEM            (foo_canvas_item_get_type ())
+#define FOO_CANVAS_ITEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_ITEM, FooCanvasItem))
+#define FOO_CANVAS_ITEM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_ITEM, FooCanvasItemClass))
+#define FOO_IS_CANVAS_ITEM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_ITEM))
+#define FOO_IS_CANVAS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_ITEM))
+#define FOO_CANVAS_ITEM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_ITEM, FooCanvasItemClass))
+
+
+struct _FooCanvasItem {
+	GtkObject object;
+
+	/* Parent canvas for this item */
+	FooCanvas *canvas;
+
+	/* Parent canvas group for this item (a FooCanvasGroup) */
+	FooCanvasItem *parent;
+
+	/* Bounding box for this item (in canvas coordinates) */
+	double x1, y1, x2, y2;
+};
+
+struct _FooCanvasItemClass {
+	GtkObjectClass parent_class;
+
+	/* Tell the item to update itself.  The flags are from the update flags
+	 * defined above.  The item should update its internal state from its
+	 * queued state, and recompute and request its repaint area. The
+	 * update method also recomputes the bounding box of the item.
+	 */
+	void (* update) (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+
+	/* Realize an item -- create GCs, etc. */
+	void (* realize) (FooCanvasItem *item);
+
+	/* Unrealize an item */
+	void (* unrealize) (FooCanvasItem *item);
+
+	/* Map an item - normally only need by items with their own GdkWindows */
+	void (* map) (FooCanvasItem *item);
+
+	/* Unmap an item */
+	void (* unmap) (FooCanvasItem *item);
+
+	/* Draw an item of this type.  (x, y) are the upper-left canvas pixel
+	 * coordinates of the drawable, a temporary pixmap, where things get
+	 * drawn.  (width, height) are the dimensions of the drawable.
+	 */
+	void (* draw) (FooCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+
+	/* Calculate the distance from an item to the specified point.  It also
+         * returns a canvas item which is the item itself in the case of the
+         * object being an actual leaf item, or a child in case of the object
+         * being a canvas group.  (cx, cy) are the canvas pixel coordinates that
+         * correspond to the item-relative coordinates (x, y).
+	 */
+	double (* point) (FooCanvasItem *item, double x, double y, int cx, int cy,
+			  FooCanvasItem **actual_item);
+
+	void (* translate) (FooCanvasItem *item, double dx, double dy);
+	
+	/* Fetch the item's bounding box (need not be exactly tight).  This
+	 * should be in item-relative coordinates.
+	 */
+	void (* bounds) (FooCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+	/* Signal: an event ocurred for an item of this type.  The (x, y)
+	 * coordinates are in the canvas world coordinate system.
+	 */
+	gboolean (* event)                (FooCanvasItem *item, GdkEvent *event);
+
+	/* Reserved for future expansion */
+	gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_item_get_type (void) G_GNUC_CONST;
+
+/* Create a canvas item using the standard Gtk argument mechanism.  The item is
+ * automatically inserted at the top of the specified canvas group.  The last
+ * argument must be a NULL pointer.
+ */
+FooCanvasItem *foo_canvas_item_new (FooCanvasGroup *parent, GType type,
+				    const gchar *first_arg_name, ...);
+
+/* Constructors for use in derived classes and language wrappers */
+void foo_canvas_item_construct (FooCanvasItem *item, FooCanvasGroup *parent,
+				const gchar *first_arg_name, va_list args);
+
+/* Configure an item using the standard Gtk argument mechanism.  The last
+ * argument must be a NULL pointer.
+ */
+void foo_canvas_item_set (FooCanvasItem *item, const gchar *first_arg_name, ...);
+
+/* Used only for language wrappers and the like */
+void foo_canvas_item_set_valist (FooCanvasItem *item,
+				 const gchar *first_arg_name, va_list args);
+
+/* Move an item by the specified amount */
+void foo_canvas_item_move (FooCanvasItem *item, double dx, double dy);
+
+/* Raise an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void foo_canvas_item_raise (FooCanvasItem *item, int positions);
+
+/* Lower an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void foo_canvas_item_lower (FooCanvasItem *item, int positions);
+
+/* Raise an item to the top of its parent group's z-order. */
+void foo_canvas_item_raise_to_top (FooCanvasItem *item);
+
+/* Lower an item to the bottom of its parent group's z-order */
+void foo_canvas_item_lower_to_bottom (FooCanvasItem *item);
+
+/* Send an item behind another item */
+void foo_canvas_item_send_behind (FooCanvasItem *item,
+				  FooCanvasItem *behind_item);
+
+
+/* Show an item (make it visible).  If the item is already shown, it has no
+ * effect.
+ */
+void foo_canvas_item_show (FooCanvasItem *item);
+
+/* Hide an item (make it invisible).  If the item is already invisible, it has
+ * no effect.
+ */
+void foo_canvas_item_hide (FooCanvasItem *item);
+
+/* Grab the mouse for the specified item.  Only the events in event_mask will be
+ * reported.  If cursor is non-NULL, it will be used during the duration of the
+ * grab.  Time is a proper X event time parameter.  Returns the same values as
+ * XGrabPointer().
+ */
+int foo_canvas_item_grab (FooCanvasItem *item, unsigned int event_mask,
+			  GdkCursor *cursor, guint32 etime);
+
+/* Ungrabs the mouse -- the specified item must be the same that was passed to
+ * foo_canvas_item_grab().  Time is a proper X event time parameter.
+ */
+void foo_canvas_item_ungrab (FooCanvasItem *item, guint32 etime);
+
+/* These functions convert from a coordinate system to another.  "w" is world
+ * coordinates and "i" is item coordinates.
+ */
+void foo_canvas_item_w2i (FooCanvasItem *item, double *x, double *y);
+void foo_canvas_item_i2w (FooCanvasItem *item, double *x, double *y);
+
+/* Remove the item from its parent group and make the new group its parent.  The
+ * item will be put on top of all the items in the new group.  The item's
+ * coordinates relative to its new parent to *not* change -- this means that the
+ * item could potentially move on the screen.
+ * 
+ * The item and the group must be in the same canvas.  An item cannot be
+ * reparented to a group that is the item itself or that is an inferior of the
+ * item.
+ */
+void foo_canvas_item_reparent (FooCanvasItem *item, FooCanvasGroup *new_group);
+
+/* Used to send all of the keystroke events to a specific item as well as
+ * GDK_FOCUS_CHANGE events.
+ */
+void foo_canvas_item_grab_focus (FooCanvasItem *item);
+
+/* Fetch the bounding box of the item.  The bounding box may not be exactly
+ * tight, but the canvas items will do the best they can.  The returned bounding
+ * box is in the coordinate system of the item's parent.
+ */
+void foo_canvas_item_get_bounds (FooCanvasItem *item,
+				 double *x1, double *y1, double *x2, double *y2);
+
+/* Request that the update method eventually get called.  This should be used
+ * only by item implementations.
+ */
+void foo_canvas_item_request_update (FooCanvasItem *item);
+
+/* Request a redraw of the bounding box of the canvas item */
+void foo_canvas_item_request_redraw (FooCanvasItem *item);
+
+/* FooCanvasGroup - a group of canvas items
+ *
+ * A group is a node in the hierarchical tree of groups/items inside a canvas.
+ * Groups serve to give a logical structure to the items.
+ *
+ * Consider a circuit editor application that uses the canvas for its schematic
+ * display.  Hierarchically, there would be canvas groups that contain all the
+ * components needed for an "adder", for example -- this includes some logic
+ * gates as well as wires.  You can move stuff around in a convenient way by
+ * doing a foo_canvas_item_move() of the hierarchical groups -- to move an
+ * adder, simply move the group that represents the adder.
+ *
+ * The following arguments are available:
+ *
+ * name		type		read/write	description
+ * --------------------------------------------------------------------------------
+ * x		double		RW		X coordinate of group's origin
+ * y		double		RW		Y coordinate of group's origin
+ */
+
+
+#define FOO_TYPE_CANVAS_GROUP            (foo_canvas_group_get_type ())
+#define FOO_CANVAS_GROUP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS_GROUP, FooCanvasGroup))
+#define FOO_CANVAS_GROUP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS_GROUP, FooCanvasGroupClass))
+#define FOO_IS_CANVAS_GROUP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS_GROUP))
+#define FOO_IS_CANVAS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS_GROUP))
+#define FOO_CANVAS_GROUP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS_GROUP, FooCanvasGroupClass))
+
+
+struct _FooCanvasGroup {
+	FooCanvasItem item;
+
+	double xpos, ypos;
+	
+	/* Children of the group */
+	GList *item_list;
+	GList *item_list_end;
+};
+
+struct _FooCanvasGroupClass {
+	FooCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_group_get_type (void) G_GNUC_CONST;
+
+
+/*** FooCanvas ***/
+
+
+#define FOO_TYPE_CANVAS            (foo_canvas_get_type ())
+#define FOO_CANVAS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE_CANVAS, FooCanvas))
+#define FOO_CANVAS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE_CANVAS, FooCanvasClass))
+#define FOO_IS_CANVAS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE_CANVAS))
+#define FOO_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE_CANVAS))
+#define FOO_CANVAS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE_CANVAS, FooCanvasClass))
+
+
+struct _FooCanvas {
+	GtkLayout layout;
+
+	/* Root canvas group */
+	FooCanvasItem *root;
+
+	/* The item containing the mouse pointer, or NULL if none */
+	FooCanvasItem *current_item;
+
+	/* Item that is about to become current (used to track deletions and such) */
+	FooCanvasItem *new_current_item;
+
+	/* Item that holds a pointer grab, or NULL if none */
+	FooCanvasItem *grabbed_item;
+
+	/* If non-NULL, the currently focused item */
+	FooCanvasItem *focused_item;
+
+	/* GC for temporary draw pixmap */
+	GdkGC *pixmap_gc;
+
+	/* Event on which selection of current item is based */
+	GdkEvent pick_event;
+
+	/* Scrolling region */
+	double scroll_x1, scroll_y1;
+	double scroll_x2, scroll_y2;
+
+	/* Scaling factor to be used for display */
+	double pixels_per_unit;
+
+	/* Idle handler ID */
+	guint idle_id;
+
+	/* Signal handler ID for destruction of the root item */
+	guint root_destroy_id;
+
+	/* Internal pixel offsets when zoomed out */
+	int zoom_xofs, zoom_yofs;
+
+	/* Last known modifier state, for deferred repick when a button is down */
+	int state;
+
+	/* Event mask specified when grabbing an item */
+	guint grabbed_event_mask;
+
+	/* Tolerance distance for picking items */
+	int close_enough;
+
+	/* Whether the canvas should center the canvas in the middle of
+	 * the window if the scroll region is smaller than the window */
+	unsigned int center_scroll_region : 1;
+
+	/* Whether items need update at next idle loop iteration */
+	unsigned int need_update : 1;
+
+	/* Are we in the midst of an update */
+	unsigned int doing_update : 1;
+
+	/* Whether the canvas needs redrawing at the next idle loop iteration */
+	unsigned int need_redraw : 1;
+
+	/* Whether current item will be repicked at next idle loop iteration */
+	unsigned int need_repick : 1;
+
+	/* For use by internal pick_current_item() function */
+	unsigned int left_grabbed_item : 1;
+
+	/* For use by internal pick_current_item() function */
+	unsigned int in_repick : 1;
+};
+
+struct _FooCanvasClass {
+	GtkLayoutClass parent_class;
+
+	/* Draw the background for the area given.
+	 */
+	void (* draw_background) (FooCanvas *canvas,
+				  int x, int y, int width, int height);
+
+	/* Private Virtual methods for groping the canvas inside bonobo */
+	void (* request_update) (FooCanvas *canvas);
+
+	/* Reserved for future expansion */
+	gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType foo_canvas_get_type (void) G_GNUC_CONST;
+
+/* Creates a new canvas.  You should check that the canvas is created with the
+ * proper visual and colormap.  Any visual will do unless you intend to insert
+ * gdk_imlib images into it, in which case you should use the gdk_imlib visual.
+ *
+ * You should call foo_canvas_set_scroll_region() soon after calling this
+ * function to set the desired scrolling limits for the canvas.
+ */
+GtkWidget *foo_canvas_new (void);
+
+/* Returns the root canvas item group of the canvas */
+FooCanvasGroup *foo_canvas_root (FooCanvas *canvas);
+
+/* Sets the limits of the scrolling region, in world coordinates */
+void foo_canvas_set_scroll_region (FooCanvas *canvas,
+				   double x1, double y1, double x2, double y2);
+
+/* Gets the limits of the scrolling region, in world coordinates */
+void foo_canvas_get_scroll_region (FooCanvas *canvas,
+				   double *x1, double *y1, double *x2, double *y2);
+
+/* Sets the number of pixels that correspond to one unit in world coordinates */
+void foo_canvas_set_pixels_per_unit (FooCanvas *canvas, double n);
+
+/* Wether the canvas centers the scroll region if it is smaller than the window  */
+void foo_canvas_set_center_scroll_region (FooCanvas *canvas, gboolean center_scroll_region);
+
+/* Scrolls the canvas to the specified offsets, given in canvas pixel coordinates */
+void foo_canvas_scroll_to (FooCanvas *canvas, int cx, int cy);
+
+/* Returns the scroll offsets of the canvas in canvas pixel coordinates.  You
+ * can specify NULL for any of the values, in which case that value will not be
+ * queried.
+ */
+void foo_canvas_get_scroll_offsets (FooCanvas *canvas, int *cx, int *cy);
+
+/* Requests that the canvas be repainted immediately instead of in the idle
+ * loop.
+ */
+void foo_canvas_update_now (FooCanvas *canvas);
+
+/* Returns the item that is at the specified position in world coordinates, or
+ * NULL if no item is there.
+ */
+FooCanvasItem *foo_canvas_get_item_at (FooCanvas *canvas, double x, double y);
+
+/* For use only by item type implementations.  Request that the canvas
+ * eventually redraw the specified region, specified in canvas pixel
+ * coordinates.  The region contains (x1, y1) but not (x2, y2).
+ */
+void foo_canvas_request_redraw (FooCanvas *canvas, int x1, int y1, int x2, int y2);
+
+/* These functions convert from a coordinate system to another.  "w" is world
+ * coordinates, "c" is canvas pixel coordinates (pixel coordinates that are
+ * (0,0) for the upper-left scrolling limit and something else for the
+ * lower-left scrolling limit).
+ */
+void foo_canvas_w2c_rect_d (FooCanvas *canvas,
+			    double *x1, double *y1,
+			    double *x2, double *y2);
+void foo_canvas_w2c (FooCanvas *canvas, double wx, double wy, int *cx, int *cy);
+void foo_canvas_w2c_d (FooCanvas *canvas, double wx, double wy, double *cx, double *cy);
+void foo_canvas_c2w (FooCanvas *canvas, int cx, int cy, double *wx, double *wy);
+
+/* This function takes in coordinates relative to the GTK_LAYOUT
+ * (canvas)->bin_window and converts them to world coordinates.
+ * These days canvas coordinates and window coordinates are the same, but
+ * these are left for backwards compat reasons.
+ */
+void foo_canvas_window_to_world (FooCanvas *canvas,
+				 double winx, double winy, double *worldx, double *worldy);
+
+/* This is the inverse of foo_canvas_window_to_world() */
+void foo_canvas_world_to_window (FooCanvas *canvas,
+				 double worldx, double worldy, double *winx, double *winy);
+
+/* Takes a string specification for a color and allocates it into the specified
+ * GdkColor.  If the string is null, then it returns FALSE. Otherwise, it
+ * returns TRUE.
+ */
+int foo_canvas_get_color (FooCanvas *canvas, const char *spec, GdkColor *color);
+
+/* Allocates a color from the RGB value passed into this function. */
+gulong foo_canvas_get_color_pixel (FooCanvas *canvas,
+				   guint        rgba);
+     
+
+/* Sets the stipple origin of the specified gc so that it will be aligned with
+ * all the stipples used in the specified canvas.  This is intended for use only
+ * by canvas item implementations.
+ */
+void foo_canvas_set_stipple_origin (FooCanvas *canvas, GdkGC *gc);
+
+G_END_DECLS
+
+#endif
diff --git a/libfoocanvas/libfoocanvas.h b/libfoocanvas/libfoocanvas.h
new file mode 100644
index 0000000..adce512
--- /dev/null
+++ b/libfoocanvas/libfoocanvas.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+#ifndef LIBFOOCANVAS_H
+#define LIBFOOCANVAS_H
+
+#include <libfoocanvas/foo-canvas.h>
+#include <libfoocanvas/foo-canvas-line.h>
+#include <libfoocanvas/foo-canvas-text.h>
+#include <libfoocanvas/foo-canvas-polygon.h>
+#include <libfoocanvas/foo-canvas-pixbuf.h>
+#include <libfoocanvas/foo-canvas-widget.h>
+#include <libfoocanvas/foo-canvas-rect-ellipse.h>
+#include <libfoocanvas/foo-canvas-util.h>
+
+G_BEGIN_DECLS
+
+GType foo_canvas_points_get_type (void);
+#define FOO_TYPE_CANVAS_POINTS foo_canvas_points_get_type()
+
+G_END_DECLS
+
+#endif /* LIBFOOCANVAS_H */
diff --git a/libfoocanvas/libfoocanvastypes.c b/libfoocanvas/libfoocanvastypes.c
new file mode 100644
index 0000000..bdf98dc
--- /dev/null
+++ b/libfoocanvas/libfoocanvastypes.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 1999, 2000 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+#include <config.h>
+#include <glib-object.h>
+
+#include <libfoocanvas/libfoocanvas.h>
+
+GType
+foo_canvas_points_get_type (void)
+{
+    static GType type_canvas_points = 0;
+
+    if (!type_canvas_points)
+	type_canvas_points = g_boxed_type_register_static
+	    ("FooCanvasPoints", 
+	     (GBoxedCopyFunc) foo_canvas_points_ref,
+	     (GBoxedFreeFunc) foo_canvas_points_unref);
+
+    return type_canvas_points;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]