nautilus r14815 - in trunk: . eel libnautilus-private po



Author: alexl
Date: Mon Dec 15 15:56:41 2008
New Revision: 14815
URL: http://svn.gnome.org/viewvc/nautilus?rev=14815&view=rev

Log:
2008-12-15  Alexander Larsson  <alexl redhat com>

        * Makefile.am:
        * acconfig.h:
        * configure.in:
	* eel/
        * libnautilus-private/Makefile.am:
	Import eel into nautilus.



Added:
   trunk/eel/
   trunk/eel/ChangeLog
   trunk/eel/Makefile.am
   trunk/eel/README
   trunk/eel/README.canvas
   trunk/eel/check-eel   (contents, props changed)
   trunk/eel/check-program.c
   trunk/eel/eel-accessibility.c
   trunk/eel/eel-accessibility.h
   trunk/eel/eel-alert-dialog.c
   trunk/eel/eel-alert-dialog.h
   trunk/eel/eel-art-extensions.c
   trunk/eel/eel-art-extensions.h
   trunk/eel/eel-art-gtk-extensions.c
   trunk/eel/eel-art-gtk-extensions.h
   trunk/eel/eel-background-box.c
   trunk/eel/eel-background-box.h
   trunk/eel/eel-background.c
   trunk/eel/eel-background.h
   trunk/eel/eel-canvas-rect-ellipse.c
   trunk/eel/eel-canvas-rect-ellipse.h
   trunk/eel/eel-canvas-util.c
   trunk/eel/eel-canvas-util.h
   trunk/eel/eel-canvas.c
   trunk/eel/eel-canvas.h
   trunk/eel/eel-debug-drawing.c
   trunk/eel/eel-debug-drawing.h
   trunk/eel/eel-debug.c
   trunk/eel/eel-debug.h
   trunk/eel/eel-editable-label.c
   trunk/eel/eel-editable-label.h
   trunk/eel/eel-enumeration.c
   trunk/eel/eel-enumeration.h
   trunk/eel/eel-gconf-extensions.c
   trunk/eel/eel-gconf-extensions.h
   trunk/eel/eel-gdk-extensions.c
   trunk/eel/eel-gdk-extensions.h
   trunk/eel/eel-gdk-pixbuf-extensions.c
   trunk/eel/eel-gdk-pixbuf-extensions.h
   trunk/eel/eel-glib-extensions.c
   trunk/eel/eel-glib-extensions.h
   trunk/eel/eel-gnome-extensions.c
   trunk/eel/eel-gnome-extensions.h
   trunk/eel/eel-graphic-effects.c
   trunk/eel/eel-graphic-effects.h
   trunk/eel/eel-gtk-container.c
   trunk/eel/eel-gtk-container.h
   trunk/eel/eel-gtk-extensions.c
   trunk/eel/eel-gtk-extensions.h
   trunk/eel/eel-gtk-macros.h
   trunk/eel/eel-i18n.c
   trunk/eel/eel-i18n.h
   trunk/eel/eel-image-table.c
   trunk/eel/eel-image-table.h
   trunk/eel/eel-labeled-image.c
   trunk/eel/eel-labeled-image.h
   trunk/eel/eel-lib-self-check-functions.c
   trunk/eel/eel-lib-self-check-functions.h
   trunk/eel/eel-pango-extensions.c
   trunk/eel/eel-pango-extensions.h
   trunk/eel/eel-preferences-builder.c
   trunk/eel/eel-preferences.c
   trunk/eel/eel-preferences.h
   trunk/eel/eel-self-checks.c
   trunk/eel/eel-self-checks.h
   trunk/eel/eel-stock-dialogs.c
   trunk/eel/eel-stock-dialogs.h
   trunk/eel/eel-string.c
   trunk/eel/eel-string.h
   trunk/eel/eel-types.c
   trunk/eel/eel-types.h
   trunk/eel/eel-vfs-extensions.c
   trunk/eel/eel-vfs-extensions.h
   trunk/eel/eel-wrap-table.c
   trunk/eel/eel-wrap-table.h
   trunk/eel/eel-xml-extensions.c
   trunk/eel/eel-xml-extensions.h
   trunk/eel/eel.h
   trunk/eel/eelmarshal.list
   trunk/eel/makeenums.pl   (contents, props changed)
   trunk/eel/maketypes.awk   (contents, props changed)
   trunk/eel/update-from-egg.sh   (contents, props changed)
Modified:
   trunk/ChangeLog
   trunk/Makefile.am
   trunk/acconfig.h
   trunk/configure.in
   trunk/libnautilus-private/Makefile.am
   trunk/po/ChangeLog
   trunk/po/POTFILES.in
   trunk/po/am.po
   trunk/po/ar.po
   trunk/po/as.po
   trunk/po/az.po
   trunk/po/be.po
   trunk/po/bg.po
   trunk/po/bn.po
   trunk/po/bn_IN.po
   trunk/po/br.po
   trunk/po/bs.po
   trunk/po/ca.po
   trunk/po/cs.po
   trunk/po/cy.po
   trunk/po/da.po
   trunk/po/de.po
   trunk/po/dz.po
   trunk/po/el.po
   trunk/po/en_CA.po
   trunk/po/en_GB.po
   trunk/po/es.po
   trunk/po/et.po
   trunk/po/eu.po
   trunk/po/fa.po
   trunk/po/fi.po
   trunk/po/fr.po
   trunk/po/ga.po
   trunk/po/gl.po
   trunk/po/gu.po
   trunk/po/he.po
   trunk/po/hi.po
   trunk/po/hr.po
   trunk/po/hu.po
   trunk/po/id.po
   trunk/po/is.po
   trunk/po/it.po
   trunk/po/ja.po
   trunk/po/ka.po
   trunk/po/kn.po
   trunk/po/ko.po
   trunk/po/ku.po
   trunk/po/li.po
   trunk/po/lt.po
   trunk/po/lv.po
   trunk/po/mk.po
   trunk/po/ml.po
   trunk/po/mn.po
   trunk/po/mr.po
   trunk/po/ms.po
   trunk/po/nb.po
   trunk/po/ne.po
   trunk/po/nl.po
   trunk/po/nn.po
   trunk/po/oc.po
   trunk/po/or.po
   trunk/po/pa.po
   trunk/po/pl.po
   trunk/po/ps.po
   trunk/po/pt.po
   trunk/po/pt_BR.po
   trunk/po/ro.po
   trunk/po/ru.po
   trunk/po/rw.po
   trunk/po/si.po
   trunk/po/sk.po
   trunk/po/sl.po
   trunk/po/sq.po
   trunk/po/sr.po
   trunk/po/sr latin po
   trunk/po/sv.po
   trunk/po/ta.po
   trunk/po/th.po
   trunk/po/tr.po
   trunk/po/uk.po
   trunk/po/vi.po
   trunk/po/wa.po
   trunk/po/xh.po
   trunk/po/zh_CN.po
   trunk/po/zh_HK.po
   trunk/po/zh_TW.po

Modified: trunk/Makefile.am
==============================================================================
--- trunk/Makefile.am	(original)
+++ trunk/Makefile.am	Mon Dec 15 15:56:41 2008
@@ -14,6 +14,7 @@
 	nautilus-autorun-software.desktop.in
 
 SUBDIRS =			\
+	eel			\
 	libnautilus-extension	\
 	cut-n-paste-code 	\
 	libnautilus-private 	\

Modified: trunk/acconfig.h
==============================================================================
--- trunk/acconfig.h	(original)
+++ trunk/acconfig.h	Mon Dec 15 15:56:41 2008
@@ -24,3 +24,4 @@
 #undef HAVE_STARTUP_NOTIFICATION
 #undef HAVE_EXIF
 #undef HAVE_OLD_EXIF
+#undef HAVE_RENDER

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Mon Dec 15 15:56:41 2008
@@ -1,8 +1,7 @@
-AC_PREREQ(2.54)
+oAC_PREREQ(2.54)
 
 dnl ===========================================================================
 
-m4_define(eel_minver,                  2.25.1)
 m4_define(glib_minver,                 2.19.0)
 m4_define(gnome_desktop_minver,        2.9.91)
 m4_define(pango_minver,                1.1.2)
@@ -13,6 +12,8 @@
 m4_define(tracker_minver,              0.0.1)
 m4_define(exempi_minver,               1.99.2)
 m4_define(exempi_minver_newapi,        1.99.5)
+m4_define(gail_minver,                 0.16)
+
 
 dnl 1. If the library code has changed at all since last release, then increment revision.
 dnl 2. If any interfaces have been added, then increment current and set revision to 0.
@@ -32,12 +33,12 @@
 AM_MAINTAINER_MODE
 AC_SUBST([ACLOCAL_AMFLAGS], ["\${ACLOCAL_FLAGS}"])
 
-AC_SUBST(EEL_REQUIRED, [eel_minver])
 AC_SUBST(GLIB_REQUIRED, [glib_minver])
 AC_SUBST(GNOME_DESKTOP_REQUIRED, [gnome_desktop_minver])
 AC_SUBST(PANGO_REQUIRED, [pango_minver])
 AC_SUBST(GTK_REQUIRED, [gtk_minver])
 AC_SUBST(XML_REQUIRED, [xml_minver])
+AC_SUBST(GAIL_REQUIRED)
 
 dnl We need to decrement current by one in the calculation of the age because
 dnl the library was started with version "1:0:0" instead of "0:0:0"
@@ -56,7 +57,6 @@
 AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
 
 PKG_CHECK_MODULES(ALL, [
-	eel-2.0			>= eel_minver
 	glib-2.0		>= glib_minver
 	gnome-desktop-2.0	>= gnome_desktop_minver
 	gio-unix-2.0		
@@ -64,6 +64,7 @@
 	pango			>= pango_minver
 	gtk+-2.0		>= gtk_minver
 	libxml-2.0		>= xml_minver
+	gail			>= gail_minver
 	unique-1.0
 	dbus-glib-1
 ])
@@ -104,6 +105,7 @@
 
 dnl ==========================================================================
 
+AC_CHECK_PROGS(PERL, perl5 perl)
 AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
 
 dnl ==========================================================================
@@ -370,6 +372,45 @@
 	AC_MSG_RESULT(no)
 fi
 
+dnl ===========================================================================
+
+dnl strftime checks
+
+AC_TRY_RUN([#include <time.h>
+                int main ()
+                {
+                  char buf[100];
+                  struct tm tm = {0};
+                  tm.tm_year = 99;
+                  if (strftime(buf, 100, "%EY", &tm) == 4 &&
+		      strcmp (buf, "1999")==0)
+                    return 0;
+                  return 1;
+                }
+            ],
+	    AC_DEFINE(HAVE_STRFTIME_EXTENSION, 1, [Define if strftime supports %E and %O modifiers.])
+            )
+
+dnl ===========================================================================
+
+#
+# Checks for Xft/XRender
+#
+have_render=false
+RENDER_LIBS=""
+
+AC_CHECK_LIB(Xrender, XRenderFindFormat, 
+    have_render=true,:,-lXext)
+
+if $have_render ; then
+   RENDER_LIBS="-lXrender -lXext"
+   AC_DEFINE(HAVE_RENDER)
+fi
+
+AC_SUBST(RENDER_LIBS)
+
+
+	    
 dnl ==========================================================================
 	
 dnl libegg
@@ -386,7 +427,7 @@
 AC_SUBST(LIBNAUTILUS_EXTENSION_LIBS)
 
 dnl core nautilus
-CORE_MODULES="glib-2.0 eel-2.0 gnome-desktop-2.0 gio-2.0 gio-unix-2.0 unique-1.0 dbus-glib-1 $EXTRA_CORE_MODULES"
+CORE_MODULES="glib-2.0 gnome-desktop-2.0 gio-2.0 gio-unix-2.0 unique-1.0 dbus-glib-1 gail gconf-2.0 libxml-2.0 $EXTRA_CORE_MODULES"
 CORE_CFLAGS="`$PKG_CONFIG --cflags $CORE_MODULES` $x_cflags $WARNING_CFLAGS"
 AC_SUBST(CORE_CFLAGS)
 CORE_LIBS="`$PKG_CONFIG --libs $CORE_MODULES` $x_libs"
@@ -454,6 +495,8 @@
 docs/reference/Makefile
 docs/reference/libnautilus-extension/Makefile
 docs/reference/libnautilus-extension/version.xml
+eel/Makefile
+eel/eel-features.h
 icons/Makefile
 libnautilus-private/Makefile
 libnautilus-extension/Makefile

Added: trunk/eel/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/eel/Makefile.am	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,188 @@
+NULL=
+
+noinst_LTLIBRARIES=libeel-2.la
+
+INCLUDES =						\
+	-DG_LOG_DOMAIN=\"Eel\"				\
+	-I$(top_srcdir)					\
+	$(CORE_CFLAGS)					\
+	-DDATADIR=\""$(datadir)"\"			\
+	-DSOURCE_DATADIR=\""$(top_srcdir)/data"\"	\
+	-DGNOMELOCALEDIR=\""$(prefix)/${DATADIRNAME}/locale"\"	\
+	-DG_DISABLE_DEPRECATED				\
+	-DGDK_DISABLE_DEPRECATED			\
+	-DGDK_PIXBUF_DISABLE_DEPRECATED			\
+	-DGMENU_I_KNOW_THIS_IS_UNSTABLE			\
+	$(NULL)
+
+libeel_2_la_LDFLAGS =				\
+	-version-info @EEL_VERSION_INFO@	\
+	-no-undefined				\
+	$(CORE_LIBS)				\
+	$(RENDER_LIBS)				\
+	$(X_LIBS)				\
+	$(NULL)
+
+libeel_2_la_SOURCES =				\
+	eel-accessibility.c			\
+	eel-alert-dialog.c			\
+	eel-art-extensions.c			\
+	eel-art-gtk-extensions.c		\
+	eel-background.c			\
+	eel-background-box.c                    \
+	eel-canvas.c				\
+	eel-canvas-util.c			\
+	eel-canvas-rect-ellipse.c		\
+	eel-debug-drawing.c			\
+	eel-debug.c				\
+	eel-editable-label.c			\
+	eel-enumeration.c			\
+	eel-gconf-extensions.c			\
+	eel-gdk-extensions.c			\
+	eel-gdk-pixbuf-extensions.c		\
+	eel-glib-extensions.c			\
+	eel-gnome-extensions.c			\
+	eel-graphic-effects.c			\
+	eel-gtk-container.c			\
+	eel-gtk-extensions.c			\
+	eel-i18n.c				\
+	eel-image-table.c			\
+	eel-labeled-image.c			\
+	eel-lib-self-check-functions.c		\
+	eel-pango-extensions.c			\
+	eel-preferences-builder.c		\
+	eel-preferences.c			\
+	eel-self-checks.c			\
+	eel-stock-dialogs.c			\
+	eel-string.c				\
+	eel-types.c				\
+	eel-vfs-extensions.c			\
+	eel-wrap-table.c			\
+	eel-xml-extensions.c			\
+	eel-lib-self-check-functions.h		\
+	$(NULL)
+
+libeelincludedir = $(includedir)/eel-2/eel
+
+eel_headers =					\
+	eel-accessibility.h			\
+	eel-alert-dialog.h			\
+	eel-art-extensions.h			\
+	eel-art-gtk-extensions.h		\
+	eel-background.h			\
+	eel-background-box.h                    \
+	eel-canvas.h				\
+	eel-canvas-util.h			\
+	eel-canvas-rect-ellipse.h		\
+	eel-debug-drawing.h			\
+	eel-debug.h				\
+	eel-editable-label.h			\
+	eel-enumeration.h			\
+	eel-gconf-extensions.h			\
+	eel-gdk-extensions.h			\
+	eel-gdk-pixbuf-extensions.h		\
+	eel-glib-extensions.h			\
+	eel-gnome-extensions.h			\
+	eel-graphic-effects.h			\
+	eel-gtk-container.h			\
+	eel-gtk-extensions.h			\
+	eel-gtk-macros.h			\
+	eel-i18n.h				\
+	eel-image-table.h			\
+	eel-labeled-image.h			\
+	eel-pango-extensions.h			\
+	eel-preferences.h			\
+	eel-self-checks.h			\
+	eel-stock-dialogs.h			\
+	eel-string.h				\
+	eel-types.h				\
+	eel-vfs-extensions.h			\
+	eel-wrap-table.h			\
+	eel-xml-extensions.h			\
+	eel.h					\
+	$(NULL)
+
+libeelinclude_HEADERS =				\
+	$(eel_headers)				\
+	eel-type-builtins.h			\
+	eel-marshal.h				\
+	$(NULL)
+
+marshal_sources = 				\
+	eel-marshal.h				\
+	eel-marshal.c				\
+	$(NULL)
+
+eel-marshal.h: eelmarshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --header --prefix=eel_marshal > $@
+eel-marshal.c: eelmarshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --body --prefix=eel_marshal > $@
+
+stamp_sources =					\
+	eel-enums.defs				\
+	eel-type-builtins-evals.c		\
+	$(NULL)
+
+stamps =					\
+	eel-makeenums-stamp			\
+	eel-stamp				\
+	$(NULL)
+
+eel-makeenums-stamp: makeenums.pl $(eel_headers)
+	$(PERL) $< defs $(filter-out $<,$^) > xgen-eed \
+	&& (cmp -s xgen-eed eel-enums.defs || mv -f xgen-eed eel-enums.defs) \
+	&& rm -f xgen-eed \
+	&& $(PERL) $< arrays $(filter-out $<,$^) > xgen-etbe \
+	&& (cmp -s xgen-etbe eel-type-builtins-evals.c || mv -f xgen-etbe eel-type-builtins-evals.c) \
+	&& rm -f xgen-etbe \
+	&& echo timestamp > $@
+
+maketypes_sources =				\
+	eel-type-builtins.h			\
+	eel-type-builtins-ids.c			\
+	eel-type-builtins-vars.c		\
+	$(NULL)
+
+eel-stamp: eel-makeenums-stamp $(maketypes_sources)
+	echo timestamp > $@
+
+eel-type-builtins.h: eel-enums.defs maketypes.awk eel-makeenums-stamp
+	LC_ALL=C $(AWK) -f $(srcdir)/maketypes.awk $< macros > $@
+eel-type-builtins-vars.c: eel-enums.defs maketypes.awk eel-makeenums-stamp
+	LC_ALL=C $(AWK) -f $(srcdir)/maketypes.awk $< variables > $@
+eel-type-builtins-ids.c: eel-enums.defs maketypes.awk eel-makeenums-stamp
+	LC_ALL=C $(AWK) -f $(srcdir)/maketypes.awk $< entries > $@
+
+noinst_PROGRAMS = check-program
+
+check_program_SOURCES = check-program.c
+check_program_DEPENDENCIES = libeel-2.la
+check_program_LDADD = $(EEL_LIBS)
+check_program_LDFLAGS =	$(check_program_DEPENDENCIES) -lm
+
+TESTS = check-eel
+
+EXTRA_DIST =					\
+	check-eel				\
+	eelmarshal.list				\
+	makeenums.pl				\
+	maketypes.awk				\
+	$(NULL)
+
+$(libeel_2_la_OBJECTS): $(marshal_sources)
+
+# This trick causes the stamp file to be built first.
+Makefile: eel-stamp
+
+# This trick causes the generated files to be built the first time.
+$(stamp_sources): # never add any dependencies
+	test -f $@ || touch $@
+
+built_sources =	$(stamps) $(stamp_sources) $(maketypes_sources) $(marshal_sources)
+CLEANFILES = $(built_sources)
+DONT_DIST_FILES = $(built_sources)
+
+dist-hook:
+	for file in $(DONT_DIST_FILES) ; do \
+	    rm -f $(distdir)/$$file ; \
+	done

Added: trunk/eel/README
==============================================================================
--- (empty file)
+++ trunk/eel/README	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,4 @@
+README for eel/eel
+
+Writeme
+

Added: trunk/eel/README.canvas
==============================================================================
--- (empty file)
+++ trunk/eel/README.canvas	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,25 @@
+eel-canvas is a cut-and-pasted version of some parts foo-canvas.
+Please do not change the cut and pasted parts. If you need to
+upgrade to a new foo-canvas, use this:
+	
+FOODIR=../../foocanvas/libfoocanvas
+
+cp $FOODIR/foo-canvas.c eel-canvas.c
+cp $FOODIR/foo-canvas.h eel-canvas.h
+cp $FOODIR/foo-canvas-util.c eel-canvas-util.c
+cp $FOODIR/foo-canvas-util.h eel-canvas-util.h
+cp $FOODIR/foo-canvas-rect-ellipse.h eel-canvas-rect-ellipse.h
+cp $FOODIR/foo-canvas-rect-ellipse.c eel-canvas-rect-ellipse.c
+	
+perl -pi -e 's/foo_canvas_marshal_/eel_marshal_/s' eel-canvas.c
+perl -pi -e 's/foo-canvas-marshal\.h/eel-marshal\.h/s' eel-canvas.c
+perl -pi -e 's/#include\ \"foo-canvas-marshal\.c\"//s' eel-canvas.c
+perl -pi -e 's/foo-canvas-i18n\.h/eel-i18n\.h/s' eel-canvas.c
+
+perl -pi -e 's/foo_canvas_/eel_canvas_/g' eel-canvas*.[ch]
+perl -pi -e 's/FOO_CANVAS/EEL_CANVAS/g' eel-canvas*.[ch]
+perl -pi -e 's/FooCanvas/EelCanvas/g' eel-canvas*.[ch]
+perl -pi -e 's/foo-canvas/eel-canvas/g' eel-canvas*.[ch]
+perl -pi -e 's/libfoocanvas/eel/g' eel-canvas*.[ch]
+perl -pi -e 's/FOO_TYPE_/EEL_TYPE_/g' eel-canvas*.[ch]
+perl -pi -e 's/FOO_IS_/EEL_IS_/g' eel-canvas*.[ch]

Added: trunk/eel/check-eel
==============================================================================
--- (empty file)
+++ trunk/eel/check-eel	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,3 @@
+#!/bin/sh
+./check-program --g-fatal-warnings --sm-disable
+

Added: trunk/eel/check-program.c
==============================================================================
--- (empty file)
+++ trunk/eel/check-program.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,60 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* check-program.c: A simple driver for eel self checks.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+
+#include <eel/eel-debug.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-lib-self-check-functions.h>
+#include <eel/eel-self-checks.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <libxml/parser.h>
+#include <stdlib.h>
+
+int
+main (int argc, char *argv[])
+{
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+	eel_make_warnings_and_criticals_stop_in_debugger ();
+	
+
+	LIBXML_TEST_VERSION
+	gtk_init (&argc, &argv);
+
+	/* Run the checks for eel twice. */
+
+	eel_run_lib_self_checks ();
+	eel_exit_if_self_checks_failed ();
+
+	eel_run_lib_self_checks ();
+	eel_exit_if_self_checks_failed ();
+
+	eel_debug_shut_down ();
+
+#endif /* !EEL_OMIT_SELF_CHECK */
+	
+	return EXIT_SUCCESS;
+}

Added: trunk/eel/eel-accessibility.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-accessibility.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,413 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* eel-accessibility.h - Utility functions for accessibility
+
+   Copyright (C) 2002 Anders Carlsson, Sun Microsystems, Inc.
+
+   The Eel 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 Eel 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 Eel 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.
+
+   Authors:
+	Anders Carlsson <andersca gnu org>
+	Michael Meeks   <michael ximian com>
+*/
+#include <config.h>
+#include <gtk/gtk.h>
+#include <atk/atkrelationset.h>
+#include <eel/eel-accessibility.h>
+
+void
+eel_accessibility_set_up_label_widget_relation (GtkWidget *label, GtkWidget *widget)
+{
+	AtkObject *atk_widget, *atk_label;
+
+	atk_label = gtk_widget_get_accessible (label);
+	atk_widget = gtk_widget_get_accessible (widget);
+
+	/* Create the label -> widget relation */
+	atk_object_add_relationship (atk_label, ATK_RELATION_LABEL_FOR, atk_widget);
+
+	/* Create the widget -> label relation */
+	atk_object_add_relationship (atk_widget, ATK_RELATION_LABELLED_BY, atk_label);
+}
+
+/*
+ * Hacks to make re-using gail somewhat easier.
+ */
+
+/**
+ * eel_accessibility_create_derived_type:
+ * @type_name: the name for the new accessible type eg. NautilusIconCanvasItemAccessible
+ * @existing_gobject_with_proxy: the GType of an object that has a registered factory that
+ *      manufactures the type we want to inherit from. ie. to inherit from a GailCanvasItem
+ *      we need to pass GNOME_TYPE_CANVAS_ITEM - since GailCanvasItem is registered against
+ *      that type.
+ * @opt_gail_parent_class: the name of the Gail class to derive from eg. GailCanvasItem
+ * @class_init: the init function to run for this class
+ * 
+ * This should be run to register the type, it can subsequently be run with
+ * the same name and will not re-register it, but simply return it.
+ *
+ * NB. to do instance init, you prolly want to override AtkObject::initialize
+ * 
+ * Return value: the registered type, or 0 on failure.
+ **/
+GType
+eel_accessibility_create_derived_type (const char *type_name,
+				       GType existing_gobject_with_proxy,
+				       EelAccessibilityClassInitFn class_init)
+{
+	GType type;
+	GType parent_atk_type;
+	GTypeInfo tinfo = { 0 };
+	GTypeQuery query;
+	AtkObjectFactory *factory;
+
+	if ((type = g_type_from_name (type_name))) {
+		return type;
+	}
+
+	factory = atk_registry_get_factory
+		(atk_get_default_registry (),
+		 existing_gobject_with_proxy);
+	if (!factory) {
+		return G_TYPE_INVALID;
+	}
+	
+	parent_atk_type = atk_object_factory_get_accessible_type (factory);
+	if (!parent_atk_type) {
+		return G_TYPE_INVALID;
+	}
+
+	/*
+	 * Figure out the size of the class and instance 
+	 * we are deriving from
+	 */
+	g_type_query (parent_atk_type, &query);
+
+	if (class_init) {
+		tinfo.class_init = (GClassInitFunc) class_init;
+	}
+
+	tinfo.class_size    = query.class_size;
+	tinfo.instance_size = query.instance_size;
+
+	/* Register the type */
+	type = g_type_register_static (
+		parent_atk_type, type_name, &tinfo, 0);
+
+	return type;
+}
+
+
+static GQuark
+get_quark_accessible (void)
+{
+	static GQuark quark_accessible_object = 0;
+
+	if (!quark_accessible_object) {
+		quark_accessible_object = g_quark_from_static_string
+			("accessible-object");
+	}
+
+	return quark_accessible_object;
+}
+
+static GQuark
+get_quark_gobject (void)
+{
+	static GQuark quark_accessible_gobject = 0;
+
+	if (!quark_accessible_gobject) {
+		quark_accessible_gobject = g_quark_from_static_string
+			("object-for-accessible");
+	}
+
+	return quark_accessible_gobject;
+}
+
+/**
+ * eel_accessibility_get_atk_object:
+ * @object: a GObject of some sort
+ * 
+ * gets an AtkObject associated with a GObject
+ * 
+ * Return value: the associated accessible if one exists or NULL
+ **/
+AtkObject *
+eel_accessibility_get_atk_object (gpointer object)
+{
+	return g_object_get_qdata (object, get_quark_accessible ());
+}
+
+/**
+ * eel_accessibilty_for_object:
+ * @object: a GObject of some sort
+ * 
+ * gets an AtkObject associated with a GObject and if it doesn't
+ * exist creates a suitable accessible object.
+ * 
+ * Return value: an associated accessible.
+ **/
+AtkObject *
+eel_accessibility_for_object (gpointer object)
+{
+	if (GTK_IS_WIDGET (object))
+		return gtk_widget_get_accessible (object);
+
+	return atk_gobject_accessible_for_object (object);
+}
+
+/**
+ * eel_accessibility_get_gobject:
+ * @object: an AtkObject
+ * 
+ * gets the GObject associated with the AtkObject, for which
+ * @object provides accessibility support.
+ * 
+ * Return value: the accessible's associated GObject
+ **/
+gpointer
+eel_accessibility_get_gobject (AtkObject *object)
+{
+	return g_object_get_qdata (G_OBJECT (object), get_quark_gobject ());
+}
+
+static void
+eel_accessibility_destroy (gpointer data,
+			   GObject *where_the_object_was)
+{
+	atk_object_notify_state_change
+		(ATK_OBJECT (data), ATK_STATE_DEFUNCT, TRUE); 
+}
+
+/**
+ * eel_accessibility_set_atk_object_return:
+ * @object: a GObject
+ * @atk_object: it's AtkObject
+ * 
+ * used to register and return a new accessible object for something
+ * 
+ * Return value: @atk_object.
+ **/
+AtkObject *
+eel_accessibility_set_atk_object_return (gpointer   object,
+					 AtkObject *atk_object)
+{
+	atk_object_initialize (atk_object, object);
+
+	if (!ATK_IS_GOBJECT_ACCESSIBLE (atk_object)) {
+		g_object_set_qdata_full
+			(object, get_quark_accessible (), atk_object,
+			 (GDestroyNotify)eel_accessibility_destroy);
+		g_object_set_qdata
+			(G_OBJECT (atk_object), get_quark_gobject (), object);
+	}
+
+	return atk_object;
+}
+
+static GailTextUtil *
+get_simple_text (gpointer object)
+{
+	GObject *gobject;
+	EelAccessibleTextIface *aif;
+
+	if (GTK_IS_ACCESSIBLE (object)) {
+		gobject = G_OBJECT (GTK_ACCESSIBLE (object)->widget);
+	} else {
+		gobject = eel_accessibility_get_gobject (object);
+	}
+
+	if (!gobject) {
+		return NULL;
+	}
+
+	aif = EEL_ACCESSIBLE_TEXT_GET_IFACE (gobject);
+	if (!aif) {
+		g_warning ("No accessible text inferface on '%s'",
+			   g_type_name_from_instance ((gpointer) gobject));
+
+	} else if (aif->get_text) {
+		return aif->get_text (gobject);
+	}
+
+	return NULL;
+}
+
+char *
+eel_accessibility_text_get_text (AtkText *text,
+				 gint     start_pos,
+				 gint     end_pos)
+{
+	GailTextUtil *util = get_simple_text (text);
+	g_return_val_if_fail (util != NULL, NULL);
+
+	return gail_text_util_get_substring (util, start_pos, end_pos);
+}
+
+gunichar 
+eel_accessibility_text_get_character_at_offset (AtkText *text,
+						gint     offset)
+{
+	char *txt, *index;
+	gint sucks1 = 0, sucks2 = -1;
+	gunichar c;
+	GailTextUtil *util = get_simple_text (text);
+	g_return_val_if_fail (util != NULL, 0);
+
+	txt = gail_text_util_get_substring (util, sucks1, sucks2);
+
+	index = g_utf8_offset_to_pointer (txt, offset);
+	c = g_utf8_get_char (index);
+	g_free (txt);
+
+	return c;
+}
+
+char *
+eel_accessibility_text_get_text_before_offset (AtkText	      *text,
+					       gint            offset,
+					       AtkTextBoundary boundary_type,
+					       gint           *start_offset,
+					       gint           *end_offset)
+{
+	GailTextUtil *util = get_simple_text (text);
+	g_return_val_if_fail (util != NULL, NULL);
+
+	return gail_text_util_get_text (
+		util, NULL, GAIL_BEFORE_OFFSET, 
+		boundary_type, offset, start_offset, end_offset);
+}
+
+char *
+eel_accessibility_text_get_text_at_offset (AtkText        *text,
+					   gint            offset,
+					   AtkTextBoundary boundary_type,
+					   gint           *start_offset,
+					   gint           *end_offset)
+{
+	GailTextUtil *util = get_simple_text (text);
+	g_return_val_if_fail (util != NULL, NULL);
+
+	return gail_text_util_get_text (
+		util, NULL, GAIL_AT_OFFSET,
+		boundary_type, offset, start_offset, end_offset);
+}
+
+gchar*
+eel_accessibility_text_get_text_after_offset  (AtkText	      *text,
+					       gint            offset,
+					       AtkTextBoundary boundary_type,
+					       gint           *start_offset,
+					       gint           *end_offset)
+{
+	GailTextUtil *util = get_simple_text (text);
+	g_return_val_if_fail (util != NULL, NULL);
+
+	return gail_text_util_get_text (
+		util, NULL, GAIL_AFTER_OFFSET, 
+		boundary_type, offset, start_offset, end_offset);
+}
+
+gint
+eel_accessibility_text_get_character_count (AtkText *text)
+{
+	GailTextUtil *util = get_simple_text (text);
+	g_return_val_if_fail (util != NULL, -1);
+
+	return gtk_text_buffer_get_char_count (util->buffer);
+}
+
+static void
+eel_accessibility_simple_text_interface_init (AtkTextIface *iface)
+{
+	iface->get_text                = eel_accessibility_text_get_text;
+	iface->get_character_at_offset = eel_accessibility_text_get_character_at_offset;
+	iface->get_text_before_offset  = eel_accessibility_text_get_text_before_offset;
+	iface->get_text_at_offset      = eel_accessibility_text_get_text_at_offset;
+	iface->get_text_after_offset   = eel_accessibility_text_get_text_after_offset;
+	iface->get_character_count     = eel_accessibility_text_get_character_count;
+
+/*	iface->get_caret_offset = eel_accessibility_text_get_caret_offset;
+	iface->set_caret_offset = eel_accessibility_text_set_caret_offset;
+	iface->get_selection = eel_accessibility_text_get_selection;
+	iface->get_n_selections = eel_accessibility_text_get_n_selections;
+	iface->add_selection = eel_accessibility_text_add_selection;
+	iface->remove_selection = eel_accessibility_text_remove_selection;
+	iface->set_selection = eel_accessibility_text_set_selection;
+	iface->get_run_attributes = eel_accessibility_text_get_run_attributes;
+	iface->get_default_attributes = eel_accessibility_text_get_default_attributes;
+	iface->get_character_extents = eel_accessibility_text_get_character_extents;
+	iface->get_offset_at_point = eel_accessibility_text_get_offset_at_point; */
+}
+
+void
+eel_accessibility_add_simple_text (GType type)
+{
+	const GInterfaceInfo simple_text_info = {
+		(GInterfaceInitFunc)
+		eel_accessibility_simple_text_interface_init,
+		(GInterfaceFinalizeFunc) NULL,
+		NULL
+	};
+
+	g_return_if_fail (type != G_TYPE_INVALID);
+
+	g_type_add_interface_static (
+		type, ATK_TYPE_TEXT, &simple_text_info);
+}
+
+GType
+eel_accessible_text_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		const GTypeInfo tinfo = {
+			sizeof (AtkTextIface),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) NULL,
+			(GClassFinalizeFunc) NULL
+		};
+
+		type = g_type_register_static (
+			G_TYPE_INTERFACE, "EelAccessibleText", &tinfo, 0);
+	}
+
+	return type;
+}
+
+void
+eel_accessibility_set_name (gpointer    object,
+			    const char *name)
+{
+	AtkObject *atk_object = eel_accessibility_for_object (object);
+
+	if (atk_object) {
+		atk_object_set_name (atk_object, name);
+	}
+}
+
+void
+eel_accessibility_set_description (gpointer    object,
+				   const char *description)
+{
+	AtkObject *atk_object = eel_accessibility_for_object (object);
+
+	if (atk_object) {
+		atk_object_set_description (atk_object, description);
+	}
+}

Added: trunk/eel/eel-accessibility.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-accessibility.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,152 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* eel-accessibility.h - Utility functions for accessibility
+
+   Copyright (C) 2002 Anders Carlsson
+
+   The Eel 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 Eel 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 Eel 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.
+
+   Authors: Anders Carlsson <andersca gnu org>
+*/
+
+#ifndef EEL_ACCESSIBILITY_H
+#define EEL_ACCESSIBILITY_H
+
+#include <glib-object.h>
+#include <atk/atkobject.h>
+#include <atk/atkregistry.h>
+#include <atk/atkobjectfactory.h>
+#include <gtk/gtk.h>
+#include <libgail-util/gailtextutil.h>
+
+void eel_accessibility_set_up_label_widget_relation (GtkWidget *label, GtkWidget *widget);
+
+typedef void     (*EelAccessibilityClassInitFn)    (AtkObjectClass *klass);
+
+AtkObject    *eel_accessibility_get_atk_object        (gpointer              object);
+AtkObject    *eel_accessibility_for_object            (gpointer              object);
+gpointer      eel_accessibility_get_gobject           (AtkObject            *object);
+AtkObject    *eel_accessibility_set_atk_object_return (gpointer              object,
+						       AtkObject            *atk_object);
+GType         eel_accessibility_create_derived_type   (const char           *type_name,
+						       GType                 existing_gobject_with_proxy,
+						       EelAccessibilityClassInitFn class_init);
+void          eel_accessibility_set_name              (gpointer              object,
+						       const char           *name);
+void          eel_accessibility_set_description       (gpointer              object,
+						       const char           *description);
+
+char*         eel_accessibility_text_get_text         (AtkText              *text,
+                                                       gint                 start_pos,
+                                                       gint                 end_pos);
+gunichar      eel_accessibility_text_get_character_at_offset
+                                                      (AtkText              *text,
+                                                       gint                 offset);
+char*         eel_accessibility_text_get_text_before_offset
+                                                      (AtkText              *text,
+                                                       gint                 offset,
+                                                       AtkTextBoundary      boundary_type,
+                                                       gint                 *start_offset,
+                                                       gint                 *end_offset);
+char*         eel_accessibility_text_get_text_at_offset
+                                                      (AtkText              *text,
+                                                       gint                 offset,
+                                                       AtkTextBoundary      boundary_type,
+                                                       gint                 *start_offset,
+                                                       gint                 *end_offset);
+char*         eel_accessibility_text_get_text_after_offset
+                                                      (AtkText              *text,
+                                                       gint                 offset,
+                                                       AtkTextBoundary      boundary_type,
+                                                       gint                 *start_offset,
+                                                       gint                 *end_offset);
+gint          eel_accessibility_text_get_character_count
+                                                      (AtkText              *text);
+
+                     
+#define EEL_TYPE_ACCESSIBLE_TEXT           (eel_accessible_text_get_type ())
+#define EEL_IS_ACCESSIBLE_TEXT(obj)        G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEL_TYPE_ACCESSIBLE_TEXT)
+#define EEL_ACCESSIBLE_TEXT(obj)           G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_ACCESSIBLE_TEXT, EelAccessibleText)
+#define EEL_ACCESSIBLE_TEXT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EEL_TYPE_ACCESSIBLE_TEXT, EelAccessibleTextIface))
+
+/* Instead of implementing the AtkText interface, implement this */
+typedef struct _EelAccessibleText EelAccessibleText;
+
+typedef struct {
+	GTypeInterface parent;
+	
+	GailTextUtil *(*get_text)   (GObject *text);
+	PangoLayout  *(*get_layout) (GObject *text);
+} EelAccessibleTextIface;
+
+GType eel_accessible_text_get_type      (void);
+void  eel_accessibility_add_simple_text (GType type);
+
+/* From gail - should be unneccessary when AtkObjectFactory is fixed */
+#define EEL_ACCESSIBLE_FACTORY(type, factory_name, type_as_function, opt_create_accessible)	\
+										\
+static GType									\
+type_as_function ## _factory_get_accessible_type (void)				\
+{										\
+  return type;									\
+}										\
+										\
+static AtkObject*								\
+type_as_function ## _factory_create_accessible (GObject *obj)			\
+{										\
+  AtkObject *accessible;							\
+										\
+  g_assert (G_IS_OBJECT (obj));  						\
+										\
+  accessible = opt_create_accessible (obj);					\
+										\
+  return accessible;								\
+}										\
+										\
+static void									\
+type_as_function ## _factory_class_init (AtkObjectFactoryClass *klass)		\
+{										\
+  klass->create_accessible   = type_as_function ## _factory_create_accessible;	\
+  klass->get_accessible_type = type_as_function ## _factory_get_accessible_type;\
+}										\
+										\
+static GType									\
+type_as_function ## _factory_get_type (void)					\
+{										\
+  static GType t = 0;								\
+										\
+  if (!t)									\
+  {										\
+    static const GTypeInfo tinfo =						\
+    {										\
+      sizeof (AtkObjectFactoryClass),					\
+      NULL, NULL, (GClassInitFunc) type_as_function ## _factory_class_init,			\
+      NULL, NULL, sizeof (AtkObjectFactory), 0, NULL, NULL			\
+    };										\
+										\
+    t = g_type_register_static (						\
+	    ATK_TYPE_OBJECT_FACTORY, factory_name, &tinfo, 0);			\
+  }										\
+										\
+  return t;									\
+}
+
+#define EEL_OBJECT_SET_FACTORY(object_type, type_as_function)			\
+	atk_registry_set_factory_type (atk_get_default_registry (),		\
+				       object_type,				\
+				       type_as_function ## _factory_get_type ())
+
+
+#endif /* EEL_ACCESSIBILITY_H */

Added: trunk/eel/eel-alert-dialog.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-alert-dialog.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,467 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-alert-dialog.c: An HIG compliant alert dialog.
+
+   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.
+
+*/
+#include <config.h>
+
+#include "eel-alert-dialog.h"
+#include "eel-i18n.h"
+#include "eel-gtk-macros.h"
+#include <gtk/gtk.h>
+#include <string.h>
+
+enum {
+  PROP_0,
+  PROP_ALERT_TYPE,
+  PROP_BUTTONS
+};
+
+struct _EelAlertDialogDetails {
+	GtkWidget *image;
+	GtkWidget *primary_label;
+	GtkWidget *secondary_label;
+	GtkWidget *details_expander;
+	GtkWidget *details_label;
+	GtkMessageType type;
+};
+
+
+static gpointer parent_class;
+
+static void eel_alert_dialog_finalize     (GObject             *object);
+static void eel_alert_dialog_class_init   (EelAlertDialogClass *klass);
+static void eel_alert_dialog_init         (EelAlertDialog      *dialog);
+static void eel_alert_dialog_style_set    (GtkWidget           *widget,
+					   GtkStyle            *prev_style);
+static void eel_alert_dialog_set_property (GObject             *object,
+					   guint                prop_id,
+					   const GValue        *value,
+					   GParamSpec          *pspec);
+static void eel_alert_dialog_get_property (GObject             *object,
+					   guint                prop_id,
+					   GValue              *value,
+					   GParamSpec          *pspec);
+static void eel_alert_dialog_add_buttons  (EelAlertDialog      *alert_dialog,
+					   GtkButtonsType       buttons);
+
+GType
+eel_alert_dialog_get_type (void)
+{
+	static GType dialog_type = 0;
+
+	if (!dialog_type) {
+	
+		const GTypeInfo dialog_info =
+		{
+			sizeof (EelAlertDialogClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) eel_alert_dialog_class_init,
+			NULL,
+			NULL,
+			sizeof (EelAlertDialog),
+			0,
+			(GInstanceInitFunc) eel_alert_dialog_init,
+		};
+	
+		dialog_type = g_type_register_static (GTK_TYPE_DIALOG, "EelAlertDialog",
+	                                              &dialog_info, 0);
+	}
+	return dialog_type;
+}
+
+static void
+eel_alert_dialog_class_init (EelAlertDialogClass *class)
+{
+	GtkWidgetClass *widget_class;
+	GObjectClass   *gobject_class;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	gobject_class = G_OBJECT_CLASS (class);
+
+	parent_class = g_type_class_peek_parent (class);
+
+	G_OBJECT_CLASS (class)->finalize = eel_alert_dialog_finalize;
+	
+	widget_class->style_set = eel_alert_dialog_style_set;
+
+	gobject_class->set_property = eel_alert_dialog_set_property;
+	gobject_class->get_property = eel_alert_dialog_get_property;
+  
+	gtk_widget_class_install_style_property (widget_class,
+	                                         g_param_spec_int ("alert_border",
+	                                         _("Image/label border"),
+	                                         _("Width of border around the label and image in the alert dialog"),
+	                                         0,
+	                                         G_MAXINT,
+	                                         5,
+	                                         G_PARAM_READABLE));
+  
+	g_object_class_install_property (gobject_class,
+	                                 PROP_ALERT_TYPE,
+	                                 g_param_spec_enum ("alert_type",
+	                                 _("Alert Type"),
+	                                 _("The type of alert"),
+	                                 GTK_TYPE_MESSAGE_TYPE,
+	                                 GTK_MESSAGE_INFO,
+	                                 G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT));
+  
+	g_object_class_install_property (gobject_class,
+	                                 PROP_BUTTONS,
+	                                 g_param_spec_enum ("buttons",
+	                                 _("Alert Buttons"),
+	                                 _("The buttons shown in the alert dialog"),
+	                                 GTK_TYPE_BUTTONS_TYPE,
+	                                 GTK_BUTTONS_NONE,
+	                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+eel_alert_dialog_finalize (GObject *object)
+{
+	EelAlertDialog *dialog;
+
+	dialog = EEL_ALERT_DIALOG (object);
+
+	g_free (dialog->details);
+
+	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+
+static void
+eel_alert_dialog_init (EelAlertDialog *dialog)
+{
+	GtkWidget *hbox;
+	GtkWidget *vbox;
+	GtkWidget *expander;
+	
+	dialog->details = g_new0 (EelAlertDialogDetails, 1);
+	
+	dialog->details->primary_label = gtk_label_new (NULL);
+	dialog->details->secondary_label = gtk_label_new (NULL);
+	dialog->details->details_label = gtk_label_new (NULL);
+	dialog->details->image = gtk_image_new_from_stock (NULL, GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (dialog->details->image), 0.5, 0.0);
+
+	gtk_label_set_line_wrap (GTK_LABEL (dialog->details->primary_label), TRUE);
+	gtk_label_set_selectable (GTK_LABEL (dialog->details->primary_label), TRUE);
+	gtk_label_set_use_markup (GTK_LABEL (dialog->details->primary_label), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (dialog->details->primary_label), 0.0, 0.5);
+
+	gtk_label_set_line_wrap (GTK_LABEL (dialog->details->secondary_label), TRUE);
+	gtk_label_set_selectable (GTK_LABEL (dialog->details->secondary_label), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (dialog->details->secondary_label), 0.0, 0.5);
+
+	gtk_label_set_line_wrap (GTK_LABEL (dialog->details->details_label), TRUE);
+	gtk_label_set_selectable (GTK_LABEL (dialog->details->details_label), TRUE);
+	gtk_misc_set_alignment (GTK_MISC (dialog->details->details_label), 0.0, 0.5);
+
+	hbox = gtk_hbox_new (FALSE, 12);
+	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+
+	gtk_box_pack_start (GTK_BOX (hbox), dialog->details->image,
+	                    FALSE, FALSE, 0);
+
+	vbox = gtk_vbox_new (FALSE, 12);
+	
+	gtk_box_pack_start (GTK_BOX (hbox), vbox,
+	                    FALSE, FALSE, 0);
+
+	gtk_box_pack_start (GTK_BOX (vbox), dialog->details->primary_label,
+	                    FALSE, FALSE, 0);
+		      
+	gtk_box_pack_start (GTK_BOX (vbox), dialog->details->secondary_label,
+	                    FALSE, FALSE, 0);
+
+	expander = gtk_expander_new_with_mnemonic (_("Show more _details"));
+	dialog->details->details_expander = expander;
+	gtk_expander_set_spacing (GTK_EXPANDER (expander), 6);
+	gtk_container_add (GTK_CONTAINER (expander), dialog->details->details_label);
+	
+	gtk_box_pack_start (GTK_BOX (vbox), expander, 
+			    FALSE, FALSE, 0);
+	
+
+	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, 
+	                    FALSE, FALSE, 0);
+
+	gtk_widget_show_all (hbox);
+	gtk_widget_hide (expander);
+	
+}
+
+static void
+setup_type (EelAlertDialog *dialog,
+	    GtkMessageType  type)
+{
+	const gchar *stock_id = NULL;
+	GtkStockItem item;
+  
+	switch (type) {
+		case GTK_MESSAGE_INFO:
+			stock_id = GTK_STOCK_DIALOG_INFO;
+			break;
+		case GTK_MESSAGE_QUESTION:
+			stock_id = GTK_STOCK_DIALOG_QUESTION;
+			break;
+		case GTK_MESSAGE_WARNING:
+			stock_id = GTK_STOCK_DIALOG_WARNING;
+			break;
+		case GTK_MESSAGE_ERROR:
+			stock_id = GTK_STOCK_DIALOG_ERROR;
+			break;
+		default:
+			g_warning ("Unknown GtkMessageType %d", type);
+			break;
+	}
+
+	if (stock_id == NULL) {
+		stock_id = GTK_STOCK_DIALOG_INFO;
+  	}
+	
+	if (gtk_stock_lookup (stock_id, &item)) {
+		gtk_image_set_from_stock (GTK_IMAGE (dialog->details->image), stock_id,
+		                          GTK_ICON_SIZE_DIALOG);
+	} else {
+		g_warning ("Stock dialog ID doesn't exist?");
+	}
+}
+
+static void 
+eel_alert_dialog_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+	EelAlertDialog *dialog;
+  
+	dialog = EEL_ALERT_DIALOG (object);
+  
+	switch (prop_id) {
+		case PROP_ALERT_TYPE:
+			dialog->details->type = g_value_get_enum (value);
+			setup_type (dialog, dialog->details->type);
+			break;
+		case PROP_BUTTONS:
+			eel_alert_dialog_add_buttons (dialog, g_value_get_enum (value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void 
+eel_alert_dialog_get_property (GObject     *object,
+                               guint        prop_id,
+                               GValue      *value,
+                               GParamSpec  *pspec)
+{
+	EelAlertDialog *dialog;
+  
+	dialog = EEL_ALERT_DIALOG (object);
+  
+	switch (prop_id) {
+		case PROP_ALERT_TYPE:
+			g_value_set_enum (value, dialog->details->type);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+void
+eel_alert_dialog_set_primary_label (EelAlertDialog *dialog,
+                                    const gchar *message)
+{
+	gchar *markup_str;
+	char *escaped_message;
+
+	if (message != NULL) {
+		escaped_message = g_markup_escape_text (message, -1);
+		markup_str = g_strconcat ("<span weight=\"bold\" size=\"larger\">", escaped_message, "</span>", NULL);
+		gtk_label_set_markup (GTK_LABEL (EEL_ALERT_DIALOG (dialog)->details->primary_label),
+		                      markup_str);
+		g_free (markup_str);
+		g_free (escaped_message);
+	}
+}
+
+void
+eel_alert_dialog_set_secondary_label (EelAlertDialog *dialog,
+                                      const gchar *message)
+{
+	if (message != NULL) {
+		gtk_label_set_text (GTK_LABEL (EEL_ALERT_DIALOG (dialog)->details->secondary_label),
+		                    message);
+	} else {
+		gtk_widget_hide (EEL_ALERT_DIALOG (dialog)->details->secondary_label);
+	}
+}
+
+void
+eel_alert_dialog_set_details_label (EelAlertDialog *dialog,
+				    const gchar    *message)
+{
+	if (message != NULL) {
+		gtk_widget_show (dialog->details->details_expander);
+		gtk_label_set_text (GTK_LABEL (dialog->details->details_label), message);
+	} else {
+		gtk_widget_hide (dialog->details->details_expander);
+	}
+}
+
+
+GtkWidget*
+eel_alert_dialog_new (GtkWindow     *parent,
+                      GtkDialogFlags flags,
+                      GtkMessageType type,
+                      GtkButtonsType buttons,
+                      const gchar   *primary_message,
+                      const gchar   *secondary_message)
+{
+	GtkWidget *widget;
+	GtkDialog *dialog;
+
+	g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
+
+	widget = g_object_new (EEL_TYPE_ALERT_DIALOG,
+	                       "alert_type", type,
+	                       "buttons", buttons,
+	                       NULL);
+	atk_object_set_role (gtk_widget_get_accessible (widget), ATK_ROLE_ALERT);
+
+	dialog = GTK_DIALOG (widget);
+	
+	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);		
+	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+	gtk_dialog_set_has_separator (dialog, FALSE);
+
+	/* Make sure we don't get a window title. 
+         * HIG says that alert dialogs should not have window title
+         */
+	gtk_window_set_title (GTK_WINDOW (dialog), "");
+	gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), TRUE);
+  	
+	eel_alert_dialog_set_primary_label (EEL_ALERT_DIALOG (dialog),
+	                                    primary_message);
+		       
+	eel_alert_dialog_set_secondary_label (EEL_ALERT_DIALOG (dialog),
+	                                      secondary_message);
+
+	if (parent != NULL) {
+		gtk_window_set_transient_for (GTK_WINDOW (widget),
+		                              GTK_WINDOW (parent));
+	}
+
+	if (flags & GTK_DIALOG_MODAL) {
+		gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+	}
+
+	if (flags & GTK_DIALOG_DESTROY_WITH_PARENT) {
+		gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
+	}
+	return widget;
+}
+
+static void
+eel_alert_dialog_add_buttons (EelAlertDialog* alert_dialog,
+                              GtkButtonsType  buttons)
+{
+	GtkDialog* dialog;
+	
+	dialog = GTK_DIALOG (alert_dialog);
+
+	switch (buttons) {
+		case GTK_BUTTONS_NONE:
+			break;
+		case GTK_BUTTONS_OK:
+			gtk_dialog_add_button (dialog,
+		                               GTK_STOCK_OK,
+			                       GTK_RESPONSE_OK);
+			gtk_dialog_set_default_response (dialog, 
+			                                GTK_RESPONSE_OK);
+			break;
+		case GTK_BUTTONS_CLOSE:
+			gtk_dialog_add_button (dialog,
+		                               GTK_STOCK_CLOSE,
+			                      GTK_RESPONSE_CLOSE);
+			gtk_dialog_set_default_response (dialog, 
+			                                 GTK_RESPONSE_CLOSE);
+			break;
+		case GTK_BUTTONS_CANCEL:
+			gtk_dialog_add_button (dialog,
+			                       GTK_STOCK_CANCEL,
+			                       GTK_RESPONSE_CANCEL);
+			gtk_dialog_set_default_response (dialog, 
+			                                 GTK_RESPONSE_CANCEL);
+			break;
+		case GTK_BUTTONS_YES_NO:
+			gtk_dialog_add_button (dialog,
+			                       GTK_STOCK_NO,
+			                       GTK_RESPONSE_NO);
+			gtk_dialog_add_button (dialog,
+			                       GTK_STOCK_YES,
+			                       GTK_RESPONSE_YES);
+			gtk_dialog_set_default_response (dialog, 
+			                                 GTK_RESPONSE_YES);
+			break;
+		case GTK_BUTTONS_OK_CANCEL:
+			gtk_dialog_add_button (dialog,
+			                       GTK_STOCK_CANCEL,
+			                       GTK_RESPONSE_CANCEL);
+			gtk_dialog_add_button (dialog,
+			                       GTK_STOCK_OK,
+			                       GTK_RESPONSE_OK);
+			gtk_dialog_set_default_response (dialog, 
+			                                 GTK_RESPONSE_OK);
+			break;
+		default:
+			g_warning ("Unknown GtkButtonsType");
+ 			break;
+	} 
+	g_object_notify (G_OBJECT (alert_dialog), "buttons");
+}
+
+static void
+eel_alert_dialog_style_set (GtkWidget *widget,
+                            GtkStyle  *prev_style)
+{
+	GtkWidget *parent;
+	gint border_width;
+	
+	border_width = 0;
+
+	parent = GTK_WIDGET (EEL_ALERT_DIALOG (widget)->details->image->parent);
+
+	if (parent != NULL) {
+		gtk_widget_style_get (widget, "alert_border",
+		                      &border_width, NULL);
+
+		gtk_container_set_border_width (GTK_CONTAINER (parent),
+		                                border_width);
+	}
+
+	if (GTK_WIDGET_CLASS (parent_class)->style_set) {
+		(GTK_WIDGET_CLASS (parent_class)->style_set) (widget, prev_style);
+	}
+}

Added: trunk/eel/eel-alert-dialog.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-alert-dialog.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,60 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-alert-dialog.h: An HIG compliant alert dialog.
+
+   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.
+
+*/
+
+#ifndef EEL_ALERT_DIALOG_H
+#define EEL_ALERT_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#define EEL_TYPE_ALERT_DIALOG        (eel_alert_dialog_get_type ())
+#define EEL_ALERT_DIALOG(obj)        (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEL_TYPE_ALERT_DIALOG, EelAlertDialog))
+
+typedef struct _EelAlertDialog        EelAlertDialog;
+typedef struct _EelAlertDialogClass   EelAlertDialogClass;
+typedef struct _EelAlertDialogDetails EelAlertDialogDetails;
+
+struct _EelAlertDialog
+{
+	GtkDialog parent_instance;
+	EelAlertDialogDetails *details;
+};
+
+struct _EelAlertDialogClass
+{
+	GtkDialogClass parent_class;
+};
+
+GType      eel_alert_dialog_get_type (void);
+
+GtkWidget* eel_alert_dialog_new                 (GtkWindow      *parent,
+                                                 GtkDialogFlags  flags,
+                                                 GtkMessageType  type,
+                                                 GtkButtonsType  buttons,
+                                                 const gchar    *primary_message,
+                                                 const gchar    *secondary_message);
+void       eel_alert_dialog_set_primary_label   (EelAlertDialog *dialog,
+		                                 const gchar    *message);
+void       eel_alert_dialog_set_secondary_label (EelAlertDialog *dialog,
+		                                 const gchar    *message);
+void       eel_alert_dialog_set_details_label   (EelAlertDialog *dialog,
+						 const gchar    *message);
+
+#endif /* EEL_ALERT_DIALOG_H */

Added: trunk/eel/eel-art-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-art-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,308 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-art-extensions.c - implementation of libart extension functions.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+
+#include "eel-art-extensions.h"
+#include "eel-lib-self-check-functions.h"
+#include <math.h>
+
+const EelDRect eel_drect_empty = { 0.0, 0.0, 0.0, 0.0 };
+const EelIRect eel_irect_empty = { 0, 0, 0, 0 };
+const EelIPoint eel_ipoint_max = { G_MAXINT, G_MAXINT };
+const EelIPoint eel_ipoint_min = { G_MININT, G_MININT };
+const EelIPoint eel_ipoint_zero = { 0, 0 };
+const EelDimensions eel_dimensions_empty = { 0, 0 };
+
+void
+eel_irect_copy (EelIRect *dest, const EelIRect *src)
+{
+	dest->x0 = src->x0;
+	dest->y0 = src->y0;
+	dest->x1 = src->x1;
+	dest->y1 = src->y1;
+}
+
+void
+eel_irect_union (EelIRect *dest,
+		  const EelIRect *src1,
+		  const EelIRect *src2) {
+	if (eel_irect_is_empty (src1)) {
+		eel_irect_copy (dest, src2);
+	} else if (eel_irect_is_empty (src2)) {
+		eel_irect_copy (dest, src1);
+	} else {
+		dest->x0 = MIN (src1->x0, src2->x0);
+		dest->y0 = MIN (src1->y0, src2->y0);
+		dest->x1 = MAX (src1->x1, src2->x1);
+		dest->y1 = MAX (src1->y1, src2->y1);
+	}
+}
+
+void
+eel_irect_intersect (EelIRect *dest,
+		     const EelIRect *src1,
+		     const EelIRect *src2)
+{
+	dest->x0 = MAX (src1->x0, src2->x0);
+	dest->y0 = MAX (src1->y0, src2->y0);
+	dest->x1 = MIN (src1->x1, src2->x1);
+	dest->y1 = MIN (src1->y1, src2->y1);
+}
+
+gboolean
+eel_irect_is_empty (const EelIRect *src)
+{
+	return (src->x1 <= src->x0 ||
+		src->y1 <= src->y0);
+}
+
+EelIRect
+eel_irect_assign (int x,
+		      int y,
+		      int width,
+		      int height)
+{
+	EelIRect rectangle;
+
+	rectangle.x0 = x;
+	rectangle.y0 = y;
+	rectangle.x1 = rectangle.x0 + width;
+	rectangle.y1 = rectangle.y0 + height;
+
+	return rectangle;
+}
+
+/**
+ * eel_irect_assign_dimensions:
+ * 
+ * @x: X coodinate for resulting rectangle.
+ * @y: Y coodinate for resulting rectangle.
+ * @dimensions: A EelDimensions structure for the rect's width and height.
+ *
+ * Returns: An EelIRect with the given coordinates and dimensions.
+ */
+EelIRect
+eel_irect_assign_dimensions (int x,
+				 int y,
+				 EelDimensions dimensions)
+{
+	EelIRect rectangle;
+
+	rectangle.x0 = x;
+	rectangle.y0 = y;
+	rectangle.x1 = rectangle.x0 + dimensions.width;
+	rectangle.y1 = rectangle.y0 + dimensions.height;
+
+	return rectangle;
+}
+
+/**
+ * eel_irect_get_width:
+ * 
+ * @rectangle: An EelIRect.
+ *
+ * Returns: The width of the rectangle.
+ * 
+ */
+int
+eel_irect_get_width (EelIRect rectangle)
+{
+	return rectangle.x1 - rectangle.x0;
+}
+
+/**
+ * eel_irect_get_height:
+ * 
+ * @rectangle: An EelIRect.
+ *
+ * Returns: The height of the rectangle.
+ * 
+ */
+int
+eel_irect_get_height (EelIRect rectangle)
+{
+	return rectangle.y1 - rectangle.y0;
+}
+
+
+static void
+eel_drect_copy (EelDRect *dest,
+		const EelDRect *src)
+{
+	dest->x0 = src->x0;
+	dest->y0 = src->y0;
+	dest->x1 = src->x1;
+	dest->y1 = src->y1;
+}
+
+static gboolean
+eel_drect_is_empty (const EelDRect *src)
+{
+	return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+void
+eel_drect_union (EelDRect *dest,
+		 const EelDRect *src1,
+		 const EelDRect *src2)
+{
+	if (eel_drect_is_empty (src1)) {
+		eel_drect_copy (dest, src2);
+	} else if (eel_drect_is_empty (src2)) {
+		eel_drect_copy (dest, src1);
+	} else {
+		dest->x0 = MIN (src1->x0, src2->x0);
+		dest->y0 = MIN (src1->y0, src2->y0);
+		dest->x1 = MAX (src1->x1, src2->x1);
+		dest->y1 = MAX (src1->y1, src2->y1);
+	}
+}
+
+
+/**
+ * eel_irect_contains_point:
+ * 
+ * @rectangle: An EelIRect.
+ * @x: X coordinate to test.
+ * @y: Y coordinate to test.
+ *
+ * Returns: A boolean value indicating whether the rectangle 
+ *          contains the x,y coordinate.
+ * 
+ */
+gboolean
+eel_irect_contains_point (EelIRect rectangle,
+			  int x,
+			  int y)
+{
+	return x >= rectangle.x0
+		&& x <= rectangle.x1
+		&& y >= rectangle.y0
+		&& y <= rectangle.y1;
+}
+
+gboolean
+eel_irect_hits_irect (EelIRect rectangle_a,
+			  EelIRect rectangle_b)
+{
+	EelIRect intersection;
+	eel_irect_intersect (&intersection, &rectangle_a, &rectangle_b);
+	return !eel_irect_is_empty (&intersection);
+}
+
+gboolean
+eel_irect_equal (EelIRect rectangle_a,
+		     EelIRect rectangle_b)
+{
+	return rectangle_a.x0 == rectangle_b.x0
+		&& rectangle_a.y0 == rectangle_b.y0
+		&& rectangle_a.x1 == rectangle_b.x1
+		&& rectangle_a.y1 == rectangle_b.y1;
+}
+
+/**
+ * eel_irect_align:
+ * 
+ * @container: The rectangle that is to contain the aligned rectangle.
+ * @aligned_width: Width of rectangle being algined.
+ * @aligned_height: Height of rectangle being algined.
+ * @x_alignment: X alignment.
+ * @y_alignment: Y alignment.
+ *
+ * Returns: A rectangle aligned within a container rectangle
+ *          using the given alignment parameters.
+ */
+EelIRect
+eel_irect_align (EelIRect container,
+		     int aligned_width,
+		     int aligned_height,
+		     float x_alignment,
+		     float y_alignment)
+{
+	EelIRect aligned;
+	int available_width;
+	int available_height;
+
+	if (eel_irect_is_empty (&container)) {
+		return eel_irect_empty;
+	}
+
+	if (aligned_width == 0 || aligned_height == 0) {
+		return eel_irect_empty;
+	}
+
+	/* Make sure the aligment parameters are within range */
+	x_alignment = MAX (0, x_alignment);
+	x_alignment = MIN (1.0, x_alignment);
+	y_alignment = MAX (0, y_alignment);
+	y_alignment = MIN (1.0, y_alignment);
+
+	available_width = eel_irect_get_width (container) - aligned_width;
+	available_height = eel_irect_get_height (container) - aligned_height;
+
+	aligned.x0 = floor (container.x0 + (available_width * x_alignment) + 0.5);
+	aligned.y0 = floor (container.y0 + (available_height * y_alignment) + 0.5);
+	aligned.x1 = aligned.x0 + aligned_width;
+	aligned.y1 = aligned.y0 + aligned_height;
+
+	return aligned;
+}
+
+
+/**
+ * eel_dimensions_are_empty:
+ * 
+ * @dimensions: A EelDimensions structure.
+ *
+ * Returns: Whether the dimensions are empty.
+ */
+gboolean
+eel_dimensions_are_empty (EelDimensions dimensions)
+{
+	return dimensions.width <= 0 || dimensions.height <= 0;
+}
+
+EelIRect 
+eel_irect_offset_by (EelIRect rectangle, int x, int y)
+{
+	rectangle.x0 += x;
+	rectangle.x1 += x;
+	rectangle.y0 += y;
+	rectangle.y1 += y;
+	
+	return rectangle;
+}
+
+EelIRect 
+eel_irect_scale_by (EelIRect rectangle, double scale)
+{
+	rectangle.x0 *= scale;
+	rectangle.x1 *= scale;
+	rectangle.y0 *= scale;
+	rectangle.y1 *= scale;
+	
+	return rectangle;
+}

Added: trunk/eel/eel-art-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-art-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,111 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-art-extensions.h - interface of libart extension functions.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_ART_EXTENSIONS_H
+#define EEL_ART_EXTENSIONS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+  double x, y;
+} EelDPoint;
+
+typedef struct {
+	int x;
+	int y;
+} EelIPoint;
+
+typedef struct  {
+  double x0, y0, x1, y1;
+} EelDRect;
+
+typedef struct  {
+  /*< public >*/
+  int x0, y0, x1, y1;
+} EelIRect;
+
+typedef struct {
+	int width;
+	int height;
+} EelDimensions;
+
+extern const EelDRect eel_drect_empty;
+extern const EelIRect eel_irect_empty;
+extern const EelIPoint eel_ipoint_max;
+extern const EelIPoint eel_ipoint_min;
+extern const EelIPoint eel_ipoint_zero;
+extern const EelDimensions eel_dimensions_empty;
+
+void     eel_irect_copy              (EelIRect       *dest,
+				      const EelIRect *src);
+void     eel_irect_union             (EelIRect       *dest,
+				      const EelIRect *src1,
+				      const EelIRect *src2);
+void     eel_irect_intersect         (EelIRect       *dest,
+				      const EelIRect *src1,
+				      const EelIRect *src2);
+gboolean eel_irect_equal             (EelIRect        rectangle_a,
+				      EelIRect        rectangle_b);
+gboolean eel_irect_hits_irect        (EelIRect        rectangle_a,
+				      EelIRect        rectangle_b);
+EelIRect eel_irect_offset_by         (EelIRect        rectangle,
+				      int             x,
+				      int             y);
+EelIRect eel_irect_scale_by          (EelIRect        rectangle,
+				      double          scale);
+gboolean eel_irect_is_empty          (const EelIRect *rectangle);
+gboolean eel_irect_contains_point    (EelIRect        outer_rectangle,
+				      int             x,
+				      int             y);
+EelIRect eel_irect_assign            (int             x,
+				      int             y,
+				      int             width,
+				      int             height);
+EelIRect eel_irect_assign_dimensions (int             x,
+				      int             y,
+				      EelDimensions   dimensions);
+int      eel_irect_get_width         (EelIRect        rectangle);
+int      eel_irect_get_height        (EelIRect        rectangle);
+EelIRect eel_irect_align             (EelIRect        container,
+				      int             aligned_width,
+				      int             aligned_height,
+				      float           x_alignment,
+				      float           y_alignment);
+
+
+void eel_drect_union (EelDRect       *dest,
+		      const EelDRect *src1,
+		      const EelDRect *src2);
+
+
+/* EelDimensions functions. */
+gboolean      eel_dimensions_are_empty        (EelDimensions dimensions);
+
+
+G_END_DECLS
+
+#endif /* EEL_ART_EXTENSIONS_H */

Added: trunk/eel/eel-art-gtk-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-art-gtk-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,313 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-eel-gtk-extensions.c - Access gtk/gdk attributes as libeel rectangles.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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 PEELICULAR 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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+
+#include "eel-art-gtk-extensions.h"
+#include <gdk/gdkx.h>
+
+/**
+ * eel_gdk_rectangle_to_eel_irect:
+ * @gdk_rectangle: The source GdkRectangle.
+ *
+ * Return value: An EelIRect representation of the GdkRectangle.
+ *
+ * This is a very simple conversion of rectangles from the Gdk to the Libeel
+ * universe.  This is useful in code that does clipping (or other operations)
+ * using libeel and has a GdkRectangle to work with - for example expose_event()
+ * in GtkWidget's. 
+ */
+EelIRect
+eel_gdk_rectangle_to_eel_irect (GdkRectangle gdk_rectangle)
+{
+	EelIRect rectangle;
+
+	rectangle.x0 = gdk_rectangle.x;
+	rectangle.y0 = gdk_rectangle.y;
+	rectangle.x1 = rectangle.x0 + (int) gdk_rectangle.width;
+	rectangle.y1 = rectangle.y0 + (int) gdk_rectangle.height;
+
+	return rectangle;
+}
+
+/**
+ * eel_screen_get_dimensions:
+ *
+ * Return value: The screen dimensions.
+ *
+ */
+EelDimensions
+eel_screen_get_dimensions (void)
+{
+	EelDimensions screen_dimensions;
+
+	screen_dimensions.width = gdk_screen_width ();
+	screen_dimensions.height = gdk_screen_height ();
+	
+	g_assert (screen_dimensions.width > 0);
+	g_assert (screen_dimensions.height > 0);
+
+	return screen_dimensions;
+}
+
+/**
+ * eel_gdk_window_get_bounds:
+ * @gdk_window: The source GdkWindow.
+ *
+ * Return value: An EelIRect representation of the given GdkWindow's geometry
+ * relative to its parent in the Gdk window hierarchy.
+ *
+ */
+EelIRect
+eel_gdk_window_get_bounds (GdkWindow *gdk_window)
+{
+	EelIRect bounds;
+	int width;
+	int height;
+
+	g_return_val_if_fail (gdk_window != NULL, eel_irect_empty);
+
+	gdk_window_get_position (gdk_window, &bounds.x0, &bounds.y0);
+	gdk_drawable_get_size (gdk_window, &width, &height);
+
+	bounds.x1 = bounds.x0 + width;
+	bounds.y1 = bounds.y0 + height;
+
+	return bounds;
+}
+
+/**
+ * eel_gdk_window_get_bounds:
+ * @gdk_window: The source GdkWindow.
+ *
+ * Return value: An EelIRect representation of the given GdkWindow's geometry
+ * relative to the screen.
+ *
+ */
+EelIRect
+eel_gdk_window_get_screen_relative_bounds (GdkWindow *gdk_window)
+{
+	EelIRect screen_bounds;
+	int width;
+	int height;
+	
+	g_return_val_if_fail (gdk_window != NULL, eel_irect_empty);
+
+	if (!gdk_window_get_origin (gdk_window,
+				    &screen_bounds.x0,
+				    &screen_bounds.y0)) {
+		return eel_irect_empty;
+	}
+	
+	gdk_drawable_get_size (gdk_window, &width, &height);
+	
+	screen_bounds.x1 = screen_bounds.x0 + width;
+	screen_bounds.y1 = screen_bounds.y0 + height;
+	
+	return screen_bounds;
+}
+
+/**
+ * eel_gtk_widget_get_bounds:
+ * @gtk_widget: The source GtkWidget.
+ *
+ * Return value: An EelIRect representation of the given GtkWidget's geometry
+ * relative to its parent.  In the Gtk universe this is known as "allocation."
+ *
+ */
+EelIRect
+eel_gtk_widget_get_bounds (GtkWidget *gtk_widget)
+{
+	g_return_val_if_fail (GTK_IS_WIDGET (gtk_widget), eel_irect_empty);
+	
+	return eel_irect_assign (gtk_widget->allocation.x,
+				     gtk_widget->allocation.y,
+				     (int) gtk_widget->allocation.width,
+				     (int) gtk_widget->allocation.height);
+}
+
+/**
+ * eel_gtk_widget_get_dimensions:
+ * @gtk_widget: The source GtkWidget.
+ *
+ * Return value: The widget's dimensions.  The returned dimensions are only valid
+ *               after the widget's geometry has been "allocated" by its container.
+ */
+EelDimensions
+eel_gtk_widget_get_dimensions (GtkWidget *gtk_widget)
+{
+	EelDimensions dimensions;
+	
+	g_return_val_if_fail (GTK_IS_WIDGET (gtk_widget), eel_dimensions_empty);
+	
+	dimensions.width = (int) gtk_widget->allocation.width;
+	dimensions.height = (int) gtk_widget->allocation.height;
+	
+	return dimensions;
+}
+
+/**
+ * eel_gtk_widget_get_preferred_dimensions:
+ * @gtk_widget: The source GtkWidget.
+ *
+ * Return value: The widget's preferred dimensions.  The preferred dimensions are
+ *               computed by calling the widget's 'size_request' method and thus
+ *               could potentially be expensive for complicated widgets.
+ */
+EelDimensions
+eel_gtk_widget_get_preferred_dimensions (GtkWidget *gtk_widget)
+{
+	GtkRequisition requisition;
+	EelDimensions preferred_dimensions;
+
+	g_return_val_if_fail (GTK_IS_WIDGET (gtk_widget), eel_dimensions_empty);
+
+	gtk_widget_size_request (gtk_widget, &requisition);
+
+	preferred_dimensions.width = (int) requisition.width;
+	preferred_dimensions.height = (int) requisition.height;
+
+	return preferred_dimensions;
+}
+
+/**
+ * eel_gdk_window_clip_dirty_area_to_screen:
+ * @gdk_window: The GdkWindow that the damage occured on.
+ * @dirty_area: The dirty area as an EelIRect.
+ *
+ * Return value: An EelIRect of the dirty area clipped to the screen.
+ *
+ * This function is useful to do less work in expose_event() GtkWidget methods.
+ * It also ensures that any drawing that the widget does is actually onscreen.
+ */
+EelIRect
+eel_gdk_window_clip_dirty_area_to_screen (GdkWindow *gdk_window,
+					  EelIRect dirty_area)
+{
+	EelIRect clipped;
+	EelDimensions screen_dimensions;
+	EelIRect screen_relative_bounds;
+	int dirty_width;
+	int dirty_height;
+
+	g_return_val_if_fail (gdk_window != NULL, eel_irect_empty);
+
+	dirty_width = dirty_area.x1 - dirty_area.x0;
+	dirty_height = dirty_area.y1 - dirty_area.y0;
+
+	g_return_val_if_fail (dirty_width > 0, eel_irect_empty);
+	g_return_val_if_fail (dirty_height > 0, eel_irect_empty);
+
+	screen_dimensions = eel_screen_get_dimensions ();
+	screen_relative_bounds = eel_gdk_window_get_screen_relative_bounds (gdk_window);
+	
+	/* Window is obscured by left edge of screen */
+	if ((screen_relative_bounds.x0 + dirty_area.x0) < 0) {
+		int clipped_width = screen_relative_bounds.x0 + dirty_area.x0 + dirty_width;
+		clipped.x0 = dirty_area.x0 + dirty_width - clipped_width;
+		clipped.x1 = clipped.x0 + clipped_width;
+	} else {
+		clipped.x0 = dirty_area.x0;
+		clipped.x1 = clipped.x0 + dirty_width;
+	}
+	
+	/* Window is obscured by right edge of screen */
+	if (screen_relative_bounds.x1 > screen_dimensions.width) {
+ 		int obscured_width;
+		
+		obscured_width = 
+			screen_relative_bounds.x0 + dirty_area.x0 + dirty_width - screen_dimensions.width;
+		
+		if (obscured_width > 0) {
+			clipped.x1 -= obscured_width;
+		}
+	}
+
+	/* Window is obscured by top edge of screen */
+	if ((screen_relative_bounds.y0 + dirty_area.y0) < 0) {
+		int clipped_height = screen_relative_bounds.y0 + dirty_area.y0 + dirty_height;
+		clipped.y0 = dirty_area.y0 + dirty_height - clipped_height;
+		clipped.y1 = clipped.y0 + clipped_height;
+	} else {
+		clipped.y0 = dirty_area.y0;
+		clipped.y1 = clipped.y0 + dirty_height;
+	}
+	
+	/* Window is obscured by bottom edge of screen */
+	if (screen_relative_bounds.y1 > screen_dimensions.height) {
+ 		int obscured_height;
+		
+		obscured_height = 
+			screen_relative_bounds.y0 + dirty_area.y0 + dirty_height - screen_dimensions.height;
+		
+		if (obscured_height > 0) {
+			clipped.y1 -= obscured_height;
+		}
+	}
+
+	if (eel_irect_is_empty (&clipped)) {
+		clipped = eel_irect_empty;
+	}
+
+	return clipped;
+}
+
+GdkRectangle
+eel_irect_to_gdk_rectangle (EelIRect rectangle)
+{
+	GdkRectangle gdk_rect;
+
+	gdk_rect.x = rectangle.x0;
+	gdk_rect.y = rectangle.y0;
+	gdk_rect.width = eel_irect_get_width (rectangle);
+	gdk_rect.height = eel_irect_get_height (rectangle);
+
+	return gdk_rect;
+}
+
+EelDimensions
+eel_gdk_window_get_dimensions (GdkWindow *gdk_window)
+{
+	EelDimensions dimensions;
+	
+	g_return_val_if_fail (gdk_window != NULL, eel_dimensions_empty);
+
+	gdk_drawable_get_size (gdk_window, &dimensions.width, &dimensions.height);
+
+	return dimensions;
+}
+
+EelIPoint
+eel_gdk_get_pointer_position (void)
+{
+
+	EelIPoint position;
+
+	gdk_window_get_pointer (gdk_get_default_root_window (),
+				&position.x,
+				&position.y,
+				NULL);
+	
+	return position;
+}

Added: trunk/eel/eel-art-gtk-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-art-gtk-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-art-gtk-extensions.h - Access gtk/gdk attributes as libart rectangles.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+/* The following functions accept gtk/gdk structures and
+ * return their bounds and dimensions, where:
+ *
+ * bounds: The (x,y) and (width, height) of something.
+ * dimensions: The (width, height) of something.
+ *
+ * These are very useful in code that uses libart functions
+ * to do operations on ArtIRects (such as intersection)
+ */
+
+#ifndef EEL_ART_GTK_EXTENSIONS_H
+#define EEL_ART_GTK_EXTENSIONS_H
+
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-art-extensions.h>
+
+G_BEGIN_DECLS
+
+/* Convert between GdkRectangle and EelIRect and back */
+GdkRectangle  eel_irect_to_gdk_rectangle            (EelIRect      rectangle);
+EelIRect      eel_gdk_rectangle_to_eel_irect            (GdkRectangle  gdk_rectangle);
+EelDimensions eel_screen_get_dimensions                 (void);
+
+/* GdkWindow parent-relative bounds */
+EelIRect      eel_gdk_window_get_bounds                 (GdkWindow    *window);
+
+/* GdkWindow dimensions */
+EelDimensions eel_gdk_window_get_dimensions             (GdkWindow    *window);
+
+/* GdkWindow screen parent-relative bounds */
+EelIRect      eel_gdk_window_get_screen_relative_bounds (GdkWindow    *window);
+
+/* Clip a dirty area (from exposures) to the on screen parts of a GdkWindow */
+EelIRect      eel_gdk_window_clip_dirty_area_to_screen  (GdkWindow    *window,
+							 EelIRect      dirty_area);
+
+/* GtkWidget bounds and dimensions */
+EelIRect      eel_gtk_widget_get_bounds                 (GtkWidget    *widget);
+EelDimensions eel_gtk_widget_get_dimensions             (GtkWidget    *widget);
+EelDimensions eel_gtk_widget_get_preferred_dimensions   (GtkWidget    *widget);
+EelIPoint     eel_gdk_get_pointer_position              (void);
+
+G_END_DECLS
+
+#endif /* EEL_ART_GTK_EXTENSIONS_H */

Added: trunk/eel/eel-background-box.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-background-box.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-background-box.c - an event box that renders an eel background
+
+   Copyright (C) 2002 Sun Microsystems, Inc.
+
+   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.
+
+   Author: Dave Camp <dave ximian com>
+*/
+
+#include <config.h>
+#include "eel-background-box.h"
+
+#include "eel-gtk-macros.h"
+#include "eel-background.h"
+
+static void eel_background_box_class_init   (EelBackgroundBoxClass *background_box_class);
+static void eel_background_box_init         (EelBackgroundBox      *background);
+
+EEL_CLASS_BOILERPLATE (EelBackgroundBox, eel_background_box, GTK_TYPE_EVENT_BOX)
+
+static gboolean
+eel_background_box_expose_event (GtkWidget *widget,
+				 GdkEventExpose *event)
+{
+	eel_background_expose (widget, event);
+	
+	gtk_container_propagate_expose (GTK_CONTAINER (widget), 
+					GTK_BIN (widget)->child,
+					event);
+	
+	return TRUE;
+}
+
+static void
+eel_background_box_class_init (EelBackgroundBoxClass *klass)
+{
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+	
+ 	widget_class->expose_event = eel_background_box_expose_event;
+}
+
+static void
+eel_background_box_init (EelBackgroundBox *box)
+{
+}
+
+GtkWidget*
+eel_background_box_new (void)
+{
+	EelBackgroundBox *background_box;
+
+	background_box = EEL_BACKGROUND_BOX (gtk_widget_new (eel_background_box_get_type (), NULL));
+	
+	return GTK_WIDGET (background_box);
+}

Added: trunk/eel/eel-background-box.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-background-box.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,61 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-background-box.c - an event box that renders an eel background
+
+   Copyright (C) 2002 Sun Microsystems, Inc.
+
+   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.
+
+   Author: Dave Camp <dave ximian com>
+*/
+
+#ifndef EEL_BACKGROUND_BOX_H
+#define EEL_BACKGROUND_BOX_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EEL_TYPE_BACKGROUND_BOX            (eel_background_box_get_type ())
+#define EEL_BACKGROUND_BOX(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_BACKGROUND_BOX, EelBackgroundBox))
+#define EEL_BACKGROUND_BOX_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_BACKGROUND_BOX, EelBackgroundBoxClass))
+#define EEL_IS_BACKGROUND_BOX(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_BACKGROUND_BOX))
+#define EEL_IS_BACKGROUND_BOX_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_BACKGROUND_BOX))
+
+typedef struct EelBackgroundBox	       EelBackgroundBox;
+typedef struct EelBackgroundBoxClass       EelBackgroundBoxClass;
+typedef struct EelBackgroundBoxDetails     EelBackgroundBoxDetails;
+
+struct EelBackgroundBox
+{
+	/* Superclass */
+	GtkEventBox event_box;
+};
+
+struct EelBackgroundBoxClass 
+{
+	GtkEventBoxClass parent_class;
+};
+
+GtkType    eel_background_box_get_type (void);
+GtkWidget *eel_background_box_new      (void);
+
+G_END_DECLS
+
+#endif /* EEL_BACKGROUND_TABLE_H */
+
+

Added: trunk/eel/eel-background.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-background.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,933 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-background.c: Object for the background of a widget.
+ 
+   Copyright (C) 2000 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+#include "eel-background.h"
+#include "eel-gdk-extensions.h"
+#include "eel-gdk-pixbuf-extensions.h"
+#include "eel-glib-extensions.h"
+#include "eel-gnome-extensions.h"
+#include "eel-gtk-macros.h"
+#include "eel-lib-self-check-functions.h"
+#include "eel-string.h"
+#include "eel-marshal.h"
+#include "eel-types.h"
+#include "eel-type-builtins.h"
+#include <gtk/gtk.h>
+#include <eel/eel-canvas.h>
+#include <eel/eel-canvas-util.h>
+#include <gio/gio.h>
+#include <math.h>
+#include <stdio.h>
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnomeui/gnome-bg.h>
+
+static void       eel_background_class_init                (gpointer       klass);
+static void       eel_background_init                      (gpointer       object,
+							    gpointer       klass);
+static void       eel_background_finalize                  (GObject       *object);
+static GdkPixmap *eel_background_get_pixmap_and_color      (EelBackground *background,
+							    GdkWindow     *window,
+							    GdkColor      *color,
+							    gboolean      *changes_with_size);
+static void set_image_properties (EelBackground *background);
+
+EEL_CLASS_BOILERPLATE (EelBackground, eel_background, GTK_TYPE_OBJECT)
+
+enum {
+	APPEARANCE_CHANGED,
+	SETTINGS_CHANGED,
+	RESET,
+	LAST_SIGNAL
+};
+
+/* This is the size of the GdkRGB dither matrix, in order to avoid
+ * bad dithering when tiling the gradient
+ */
+#define GRADIENT_PIXMAP_TILE_SIZE 128
+
+static guint signals[LAST_SIGNAL];
+
+struct EelBackgroundDetails {
+	char *color;
+	
+	GnomeBG *bg;
+
+	/* Realized data: */
+	gboolean background_changes_with_size;
+	GdkPixmap *background_pixmap;
+	int background_entire_width;
+	int background_entire_height;
+	GdkColor default_color;
+
+	gboolean use_base;
+	
+	/* Is this background attached to desktop window */
+	gboolean is_desktop;
+	/* Desktop screen size watcher */
+	gulong screen_size_handler;
+	/* Can we use common pixmap for root window and desktop window */
+	gboolean use_common_pixmap;
+};
+
+static void
+eel_background_class_init (gpointer klass)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+
+	eel_type_init ();
+
+	signals[APPEARANCE_CHANGED] =
+		g_signal_new ("appearance_changed",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+			      G_STRUCT_OFFSET (EelBackgroundClass,
+					       appearance_changed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	signals[SETTINGS_CHANGED] =
+		g_signal_new ("settings_changed",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+			      G_STRUCT_OFFSET (EelBackgroundClass,
+					       settings_changed),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__INT,
+			      G_TYPE_NONE,
+			      1, G_TYPE_INT);
+	signals[RESET] =
+		g_signal_new ("reset",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+			      G_STRUCT_OFFSET (EelBackgroundClass,
+					       reset),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+
+	object_class->finalize = eel_background_finalize;
+}
+
+static void
+on_bg_changed (GnomeBG *bg, EelBackground *background)
+{
+	g_signal_emit (G_OBJECT (background),
+		       signals[APPEARANCE_CHANGED], 0);
+}
+
+static void
+eel_background_init (gpointer object, gpointer klass)
+{
+	EelBackground *background;
+
+	background = EEL_BACKGROUND (object);
+
+	background->details = g_new0 (EelBackgroundDetails, 1);
+	background->details->default_color.red = 0xffff;
+	background->details->default_color.green = 0xffff;
+	background->details->default_color.blue = 0xffff;
+	background->details->bg = gnome_bg_new ();
+
+	g_signal_connect (background->details->bg, "changed",
+			  G_CALLBACK (on_bg_changed), background);
+	
+}
+
+/* The safe way to clear an image from a background is:
+ * 		eel_background_set_image_uri (NULL);
+ * This fn is a private utility - it does NOT clear
+ * the details->bg_uri setting.
+ */
+static void
+eel_background_remove_current_image (EelBackground *background)
+{
+	if (background->details->bg != NULL) {
+		g_object_unref (G_OBJECT (background->details->bg));
+		background->details->bg = NULL;
+	}
+}
+
+static void
+eel_background_finalize (GObject *object)
+{
+	EelBackground *background;
+
+	background = EEL_BACKGROUND (object);
+
+	g_free (background->details->color);
+	eel_background_remove_current_image (background);
+
+	if (background->details->background_pixmap != NULL) {
+		g_object_unref (background->details->background_pixmap);
+		background->details->background_pixmap = NULL;
+	}
+
+	g_free (background->details);
+
+	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static EelBackgroundImagePlacement
+placement_gnome_to_eel (GnomeBGPlacement p)
+{
+	switch (p) {
+	case GNOME_BG_PLACEMENT_CENTERED:
+		return EEL_BACKGROUND_CENTERED;
+	case GNOME_BG_PLACEMENT_FILL_SCREEN:
+		return EEL_BACKGROUND_SCALED;
+	case GNOME_BG_PLACEMENT_SCALED:
+		return EEL_BACKGROUND_SCALED_ASPECT;
+	case GNOME_BG_PLACEMENT_ZOOMED:
+		return EEL_BACKGROUND_ZOOM;
+	case GNOME_BG_PLACEMENT_TILED:
+		return EEL_BACKGROUND_TILED;
+	}
+
+	return EEL_BACKGROUND_TILED;
+}
+
+static GnomeBGPlacement
+placement_eel_to_gnome (EelBackgroundImagePlacement p)
+{
+	switch (p) {
+	case EEL_BACKGROUND_CENTERED:
+		return GNOME_BG_PLACEMENT_CENTERED;
+	case EEL_BACKGROUND_SCALED:
+		return GNOME_BG_PLACEMENT_FILL_SCREEN;
+	case EEL_BACKGROUND_SCALED_ASPECT:
+		return GNOME_BG_PLACEMENT_SCALED;
+	case EEL_BACKGROUND_ZOOM:
+		return GNOME_BG_PLACEMENT_ZOOMED;
+	case EEL_BACKGROUND_TILED:
+		return GNOME_BG_PLACEMENT_TILED;
+	}
+
+	return GNOME_BG_PLACEMENT_TILED;
+}
+
+EelBackgroundImagePlacement
+eel_background_get_image_placement (EelBackground *background)
+{
+	g_return_val_if_fail (EEL_IS_BACKGROUND (background), EEL_BACKGROUND_TILED);
+
+	return placement_gnome_to_eel (gnome_bg_get_placement (background->details->bg));
+}
+
+void
+eel_background_set_image_placement (EelBackground              *background,
+				    EelBackgroundImagePlacement new_placement)
+{
+	g_return_if_fail (EEL_IS_BACKGROUND (background));
+
+	gnome_bg_set_placement (background->details->bg,
+				placement_eel_to_gnome (new_placement));
+}
+
+EelBackground *
+eel_background_new (void)
+{
+	return EEL_BACKGROUND (g_object_new (EEL_TYPE_BACKGROUND, NULL));
+}
+
+static void
+eel_background_unrealize (EelBackground *background)
+{
+	if (background->details->background_pixmap != NULL) {
+		g_object_unref (background->details->background_pixmap);
+		background->details->background_pixmap = NULL;
+	}
+	background->details->background_entire_width = 0;
+	background->details->background_entire_height = 0;
+	background->details->default_color.red = 0xffff;
+	background->details->default_color.green = 0xffff;
+	background->details->default_color.blue = 0xffff;
+}
+
+static void
+drawable_get_adjusted_size (EelBackground *background,
+			    GdkDrawable   *drawable,
+			    int		  *width,
+			    int	          *height)
+{
+	GdkScreen *screen;
+	
+	/* 
+	 * Screen resolution change makes root drawable have incorrect size.
+	 */    
+	gdk_drawable_get_size (drawable, width, height);
+
+	if (background->details->is_desktop) {
+		screen = gdk_drawable_get_screen (drawable);
+		*width = gdk_screen_get_width (screen);
+		*height = gdk_screen_get_height (screen);
+	}
+}
+
+static gboolean
+eel_background_ensure_realized (EelBackground *background, GdkWindow *window)
+{
+	gpointer data;
+	GtkWidget *widget;
+	GtkStyle *style;
+	gboolean changed;
+	int entire_width;
+	int entire_height;
+	
+	drawable_get_adjusted_size (background, window, &entire_width, &entire_height);
+	
+	/* Set the default color */
+	
+	/* Get the widget to which the window belongs and its style as well */
+	gdk_window_get_user_data (window, &data);
+	widget = GTK_WIDGET (data);
+	if (widget != NULL) {
+		style = gtk_widget_get_style (widget);
+		if (background->details->use_base) {
+			background->details->default_color = style->base[GTK_STATE_NORMAL];
+		} else {
+			background->details->default_color = style->bg[GTK_STATE_NORMAL];
+		}
+		
+		gdk_rgb_find_color (style->colormap, &(background->details->default_color));
+	}
+
+	/* If the pixmap doesn't change with the window size, never update
+	 * it again.
+	 */
+	if (background->details->background_pixmap != NULL &&
+	    !background->details->background_changes_with_size) {
+		return FALSE;
+	}
+
+	/* If the window size is the same as last time, don't update */
+	if (entire_width == background->details->background_entire_width &&
+	    entire_height == background->details->background_entire_height) {
+		return FALSE;
+	}
+
+	if (background->details->background_pixmap != NULL) {
+		g_object_unref (background->details->background_pixmap);
+		background->details->background_pixmap = NULL;
+	}
+
+	changed = FALSE;
+
+	set_image_properties (background);
+
+	background->details->background_changes_with_size = gnome_bg_changes_with_size (background->details->bg);
+	background->details->background_pixmap = gnome_bg_create_pixmap (background->details->bg,
+									 window,
+									 entire_width, entire_height,
+									 background->details->is_desktop);
+
+	changed = TRUE;
+	
+	
+	background->details->background_entire_width = entire_width;
+	background->details->background_entire_height = entire_height;
+	
+	return changed;
+}
+
+static GdkPixmap *
+eel_background_get_pixmap_and_color (EelBackground *background,
+				     GdkWindow     *window,
+				     GdkColor      *color,
+				     gboolean      *changes_with_size)
+{
+	int entire_width;
+	int entire_height;
+
+	drawable_get_adjusted_size (background, window, &entire_width, &entire_height);
+
+	eel_background_ensure_realized (background, window);
+	
+	*color = background->details->default_color;
+	*changes_with_size = background->details->background_changes_with_size;
+	
+	if (background->details->background_pixmap != NULL) {
+		return g_object_ref (background->details->background_pixmap);
+	} 
+	return NULL;
+}
+
+void
+eel_background_expose (GtkWidget                   *widget,
+		       GdkEventExpose              *event)
+{
+	GdkColor color;
+	int window_width;
+	int window_height;
+	gboolean changes_with_size;
+	GdkPixmap *pixmap;
+	GdkGC *gc;
+	GdkGCValues gc_values;
+	GdkGCValuesMask value_mask;
+
+	EelBackground *background;
+	
+	if (event->window != widget->window) {
+		return;
+	}
+
+	background = eel_get_widget_background (widget);
+
+	drawable_get_adjusted_size (background, widget->window, &window_width, &window_height);
+	
+	pixmap = eel_background_get_pixmap_and_color (background,
+						      widget->window,
+						      &color,
+						      &changes_with_size);
+
+        if (!changes_with_size) {
+                /* The background was already drawn by X, since we set
+                 * the GdkWindow background/back_pixmap.
+                 * No need to draw it again. */
+                if (pixmap) {
+                        g_object_unref (pixmap);
+                }
+                return;
+        }
+ 
+	if (pixmap) {
+		gc_values.tile = pixmap;
+		gc_values.ts_x_origin = 0;
+		gc_values.ts_y_origin = 0;
+		gc_values.fill = GDK_TILED;
+		value_mask = GDK_GC_FILL | GDK_GC_TILE | GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN;
+	} else {
+		gdk_rgb_find_color (gtk_widget_get_colormap (widget), &color);
+		gc_values.foreground = color;
+		gc_values.fill = GDK_SOLID;
+		value_mask = GDK_GC_FILL | GDK_GC_FOREGROUND;
+	}
+	
+	gc = gdk_gc_new_with_values (widget->window, &gc_values, value_mask);
+	
+	gdk_gc_set_clip_rectangle (gc, &event->area);
+
+	gdk_draw_rectangle (widget->window, gc, TRUE, 0, 0, window_width, window_height);
+	
+	g_object_unref (gc);
+	
+	if (pixmap) {
+		g_object_unref (pixmap);
+	}
+}
+
+static void
+set_image_properties (EelBackground *background)
+{
+	if (!background->details->color) {
+		gnome_bg_set_color (background->details->bg, GNOME_BG_COLOR_SOLID,
+				 &background->details->default_color, NULL);
+	} else if (!eel_gradient_is_gradient (background->details->color)) {
+		GdkColor c;
+
+		eel_gdk_color_parse_with_white_default (background->details->color, &c);
+	
+		gnome_bg_set_color (background->details->bg, GNOME_BG_COLOR_SOLID, &c, NULL);
+	} else {
+		GdkColor c1;
+		GdkColor c2;
+		char *spec;
+
+		spec = eel_gradient_get_start_color_spec (background->details->color);
+		eel_gdk_color_parse_with_white_default (spec, &c1);
+		g_free (spec);
+
+		spec = eel_gradient_get_end_color_spec (background->details->color);
+		eel_gdk_color_parse_with_white_default (spec, &c2);
+		g_free (spec);
+
+		if (eel_gradient_is_horizontal (background->details->color))
+			gnome_bg_set_color (background->details->bg, GNOME_BG_COLOR_H_GRADIENT, &c1, &c2);
+		else
+			gnome_bg_set_color (background->details->bg, GNOME_BG_COLOR_V_GRADIENT, &c1, &c2);
+
+	}
+}
+
+char *
+eel_background_get_color (EelBackground *background)
+{
+	g_return_val_if_fail (EEL_IS_BACKGROUND (background), NULL);
+
+	return g_strdup (background->details->color);
+}
+
+char *
+eel_background_get_image_uri (EelBackground *background)
+{
+	const char *filename;
+	
+	g_return_val_if_fail (EEL_IS_BACKGROUND (background), NULL);
+
+	filename = gnome_bg_get_filename (background->details->bg);
+	
+	return g_filename_to_uri (filename, NULL, NULL);
+}
+
+/* Use style->base as the default color instead of bg */
+void
+eel_background_set_use_base (EelBackground *background,
+			     gboolean use_base)
+{
+	background->details->use_base = use_base;
+}
+
+void
+eel_background_set_color (EelBackground *background,
+			  const char *color)
+{
+	if (eel_strcmp (background->details->color, color) != 0) {
+		g_free (background->details->color);
+		background->details->color = g_strdup (color);
+		
+		set_image_properties (background);
+	}
+}
+
+static gboolean
+eel_background_set_image_uri_helper (EelBackground *background,
+				     const char *image_uri,
+				     gboolean emit_signal)
+{
+	char *filename;
+
+	if (image_uri != NULL) {
+		filename = g_filename_from_uri (image_uri, NULL, NULL);
+	}
+	else {
+		filename = NULL;
+	}
+	
+	gnome_bg_set_filename (background->details->bg, filename);
+
+	if (emit_signal) {
+		g_signal_emit (GTK_OBJECT (background), signals[SETTINGS_CHANGED], 0, GDK_ACTION_COPY);
+	}
+
+	set_image_properties (background);
+	
+	g_free (filename);
+	
+	return TRUE;
+}
+
+void
+eel_background_set_image_uri (EelBackground *background, const char *image_uri)
+{
+	
+	
+	eel_background_set_image_uri_helper (background, image_uri, TRUE);
+}
+
+/* Use this fn to set both the image and color and avoid flash. The color isn't
+ * changed till after the image is done loading, that way if an update occurs
+ * before then, it will use the old color and image.
+ */
+static void
+eel_background_set_image_uri_and_color (EelBackground *background, GdkDragAction action,
+					const char *image_uri, const char *color)
+{
+	eel_background_set_image_uri_helper (background, image_uri, FALSE);
+	eel_background_set_color (background, color);
+
+	/* We always emit, even if the color didn't change, because the image change
+	 * relies on us doing it here.
+	 */
+
+	g_signal_emit (background, signals[SETTINGS_CHANGED], 0, action);
+}
+
+void
+eel_background_receive_dropped_background_image (EelBackground *background,
+						 GdkDragAction action,
+						 const char *image_uri)
+{
+	/* Currently, we only support tiled images. So we set the placement.
+	 * We rely on eel_background_set_image_uri_and_color to emit
+	 * the SETTINGS_CHANGED & APPEARANCE_CHANGE signals.
+	 */
+	eel_background_set_image_placement (background, EEL_BACKGROUND_TILED);
+	
+	eel_background_set_image_uri_and_color (background, action, image_uri, NULL);
+}
+
+/**
+ * eel_background_is_set:
+ * 
+ * Check whether the background's color or image has been set.
+ */
+gboolean
+eel_background_is_set (EelBackground *background)
+{
+	g_assert (EEL_IS_BACKGROUND (background));
+
+	return background->details->color != NULL
+		|| gnome_bg_get_filename (background->details->bg) != NULL;
+}
+
+/**
+ * eel_background_reset:
+ *
+ * Emit the reset signal to forget any color or image that has been
+ * set previously.
+ */
+void
+eel_background_reset (EelBackground *background)
+{
+	g_return_if_fail (EEL_IS_BACKGROUND (background));
+
+	g_signal_emit (GTK_OBJECT (background), signals[RESET], 0);
+}
+
+static void
+eel_background_set_up_widget (EelBackground *background, GtkWidget *widget)
+{
+	GtkStyle *style;
+	GdkPixmap *pixmap;
+	GdkPixmap *root_pixmap;
+	GdkColor color;
+	
+	int window_width;
+	int window_height;
+	
+	GdkWindow *window;
+	gboolean changes_with_size;
+
+	if (!GTK_WIDGET_REALIZED (widget)) {
+		return;
+	}
+
+	drawable_get_adjusted_size (background, widget->window, &window_width, &window_height);
+	
+	pixmap = eel_background_get_pixmap_and_color (background,
+						      widget->window,
+						      &color, 
+						      &changes_with_size);
+
+	style = gtk_widget_get_style (widget);
+	
+	gdk_rgb_find_color (style->colormap, &color);
+
+	if (EEL_IS_CANVAS (widget)) {
+		window = GTK_LAYOUT (widget)->bin_window;
+	} else {
+		window = widget->window;
+	}
+
+	if (!changes_with_size || background->details->is_desktop) {
+		gdk_window_set_back_pixmap (window, pixmap, FALSE);
+	} else {
+		gdk_window_set_back_pixmap (window, NULL, FALSE);
+		gdk_window_set_background (window, &color);
+	}
+	
+
+	background->details->background_changes_with_size =
+		gnome_bg_changes_with_size (background->details->bg);
+	
+	if (background->details->is_desktop) {
+
+		root_pixmap = NULL;
+
+		if (background->details->use_common_pixmap) {
+			root_pixmap = g_object_ref (pixmap);
+		} else {
+			root_pixmap = gnome_bg_create_pixmap (background->details->bg, window,
+							      window_width, window_height, TRUE);
+		}
+
+		gnome_bg_set_pixmap_as_root (gdk_drawable_get_screen (window), root_pixmap);
+		g_object_unref (root_pixmap);
+	}
+	
+	if (pixmap) {
+		g_object_unref (pixmap);
+	}
+}
+
+static void
+eel_widget_background_changed (GtkWidget *widget, EelBackground *background)
+{
+	eel_background_unrealize (background);
+	eel_background_set_up_widget (background, widget);
+
+	gtk_widget_queue_draw (widget);
+}
+
+/* Callback used when the style of a widget changes.  We have to regenerate its
+ * EelBackgroundStyle so that it will match the chosen GTK+ theme.
+ */
+static void
+widget_style_set_cb (GtkWidget *widget, GtkStyle *previous_style, gpointer data)
+{
+	EelBackground *background;
+	
+	background = EEL_BACKGROUND (data);
+	
+	eel_widget_background_changed (widget, background);
+}
+
+static void
+screen_size_changed (GdkScreen *screen, EelBackground *background)
+{
+	g_signal_emit (background, signals[APPEARANCE_CHANGED], 0);
+}
+
+
+static void
+widget_realized_setup (GtkWidget *widget, gpointer data)
+{
+	EelBackground *background;
+	
+	background = EEL_BACKGROUND (data);
+	
+        if (background->details->is_desktop) {
+		GdkWindow *root_window;	
+		GdkScreen *screen;
+		
+		screen = gtk_widget_get_screen (widget);
+
+		if (background->details->screen_size_handler > 0) {
+		        g_signal_handler_disconnect (screen,
+				                     background->details->screen_size_handler);
+		}
+	
+		background->details->screen_size_handler = 
+			g_signal_connect (screen, "size_changed",
+            				  G_CALLBACK (screen_size_changed), background);
+
+		root_window = gdk_screen_get_root_window(screen);			
+		
+		if (gdk_drawable_get_visual (root_window) == gtk_widget_get_visual (widget)) {
+			background->details->use_common_pixmap = TRUE;
+		} else {
+			background->details->use_common_pixmap = FALSE;
+		}
+	}
+}
+
+static void
+widget_realize_cb (GtkWidget *widget, gpointer data)
+{
+	EelBackground *background;
+	
+	background = EEL_BACKGROUND (data);
+
+	widget_realized_setup (widget, data);
+		
+	eel_background_set_up_widget (background, widget);
+}
+
+static void
+widget_unrealize_cb (GtkWidget *widget, gpointer data)
+{
+	EelBackground *background;
+	
+	background = EEL_BACKGROUND (data);
+
+	if (background->details->screen_size_handler > 0) {
+		        g_signal_handler_disconnect (gtk_widget_get_screen (GTK_WIDGET (widget)),
+				                     background->details->screen_size_handler);
+			background->details->screen_size_handler = 0;
+	}
+	background->details->use_common_pixmap = FALSE;
+}
+
+void
+eel_background_set_desktop (EelBackground *background, GtkWidget *widget, gboolean is_desktop)
+{
+	background->details->is_desktop = is_desktop;
+
+	if (GTK_WIDGET_REALIZED(widget) && background->details->is_desktop) {
+		widget_realized_setup (widget, background);
+	}
+	
+}
+
+gboolean
+eel_background_is_desktop (EelBackground *background)
+{
+	return background->details->is_desktop;
+}
+
+/* Gets the background attached to a widget.
+
+   If the widget doesn't already have a EelBackground object,
+   this will create one. To change the widget's background, you can
+   just call eel_background methods on the widget.
+
+   If the widget is a canvas, nothing more needs to be done.  For
+   normal widgets, you need to call eel_background_expose() from your
+   expose handler to draw the background.
+
+   Later, we might want a call to find out if we already have a background,
+   or a way to share the same background among multiple widgets; both would
+   be straightforward.
+*/
+EelBackground *
+eel_get_widget_background (GtkWidget *widget)
+{
+	gpointer data;
+	EelBackground *background;
+
+	g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+	/* Check for an existing background. */
+	data = g_object_get_data (G_OBJECT (widget), "eel_background");
+	if (data != NULL) {
+		g_assert (EEL_IS_BACKGROUND (data));
+		return data;
+	}
+
+	/* Store the background in the widget's data. */
+	background = eel_background_new ();
+	g_object_ref (background);
+	gtk_object_sink (GTK_OBJECT (background));
+	g_object_set_data_full (G_OBJECT (widget), "eel_background",
+				background, g_object_unref);
+
+	/* Arrange to get the signal whenever the background changes. */
+	g_signal_connect_object (background, "appearance_changed",
+				 G_CALLBACK (eel_widget_background_changed), widget, G_CONNECT_SWAPPED);
+	eel_widget_background_changed (widget, background);
+
+	g_signal_connect_object (widget, "style_set",
+				 G_CALLBACK (widget_style_set_cb),
+				 background,
+				 0);
+	g_signal_connect_object (widget, "realize",
+				 G_CALLBACK (widget_realize_cb),
+				 background,
+				 0);
+	g_signal_connect_object (widget, "unrealize",
+				 G_CALLBACK (widget_unrealize_cb),
+				 background,
+				 0);
+
+	return background;
+}
+
+/* determine if a background is darker or lighter than average, to help clients know what
+   colors to draw on top with */
+gboolean
+eel_background_is_dark (EelBackground *background)
+{
+	return gnome_bg_is_dark (background->details->bg);
+}
+   
+/* handle dropped colors */
+void
+eel_background_receive_dropped_color (EelBackground *background,
+				      GtkWidget *widget,
+				      GdkDragAction action,
+				      int drop_location_x,
+				      int drop_location_y,
+				      const GtkSelectionData *selection_data)
+{
+	guint16 *channels;
+	char *color_spec;
+	char *new_gradient_spec;
+	int left_border, right_border, top_border, bottom_border;
+
+	g_return_if_fail (EEL_IS_BACKGROUND (background));
+	g_return_if_fail (GTK_IS_WIDGET (widget));
+	g_return_if_fail (selection_data != NULL);
+
+	/* Convert the selection data into a color spec. */
+	if (selection_data->length != 8 || selection_data->format != 16) {
+		g_warning ("received invalid color data");
+		return;
+	}
+	channels = (guint16 *) selection_data->data;
+	color_spec = g_strdup_printf ("#%02X%02X%02X",
+				      channels[0] >> 8,
+				      channels[1] >> 8,
+				      channels[2] >> 8);
+
+	/* Figure out if the color was dropped close enough to an edge to create a gradient.
+	   For the moment, this is hard-wired, but later the widget will have to have some
+	   say in where the borders are.
+	*/
+	left_border = 32;
+	right_border = widget->allocation.width - 32;
+	top_border = 32;
+	bottom_border = widget->allocation.height - 32;
+	if (drop_location_x < left_border && drop_location_x <= right_border) {
+		new_gradient_spec = eel_gradient_set_left_color_spec (background->details->color, color_spec);
+	} else if (drop_location_x >= left_border && drop_location_x > right_border) {
+		new_gradient_spec = eel_gradient_set_right_color_spec (background->details->color, color_spec);
+	} else if (drop_location_y < top_border && drop_location_y <= bottom_border) {
+		new_gradient_spec = eel_gradient_set_top_color_spec (background->details->color, color_spec);
+	} else if (drop_location_y >= top_border && drop_location_y > bottom_border) {
+		new_gradient_spec = eel_gradient_set_bottom_color_spec (background->details->color, color_spec);
+	} else {
+		new_gradient_spec = g_strdup (color_spec);
+	}
+	
+	g_free (color_spec);
+
+	eel_background_set_image_uri_and_color (background, action, NULL, new_gradient_spec);
+
+	g_free (new_gradient_spec);
+}
+
+void
+eel_background_save_to_gconf (EelBackground *background)
+{
+	GConfClient *client = gconf_client_get_default ();
+
+	if (background->details->bg)
+		gnome_bg_save_to_preferences (background->details->bg, client);
+}
+
+/* self check code */
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+void
+eel_self_check_background (void)
+{
+	EelBackground *background;
+
+	background = eel_background_new ();
+
+	eel_background_set_color (background, NULL);
+	eel_background_set_color (background, "");
+	eel_background_set_color (background, "red");
+	eel_background_set_color (background, "red-blue");
+	eel_background_set_color (background, "red-blue:h");
+
+	gtk_object_sink (GTK_OBJECT (background));
+}
+
+#endif

Added: trunk/eel/eel-background.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-background.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,155 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-background.h: Object for the background of a widget.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#ifndef EEL_BACKGROUND_H
+#define EEL_BACKGROUND_H
+
+/* Windows for Eel can contain backgrounds that are either tiled
+   with an image, a solid color, or a color gradient. This class manages
+   the process of loading the image if necessary and parsing the string
+   that specifies either a color or color gradient.
+
+   The color or gradient is always present, even if there's a tiled image
+   on top of it. This is used when the tiled image can't be loaded for
+   some reason (or just has not been loaded yet).
+
+   The EelBackground object is easier to modify than a GtkStyle.
+   You can just call eel_get_window_background and modify the
+   returned background directly, unlike a style, which must be copied,
+   modified and then set.
+*/
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+typedef struct EelBackground EelBackground;
+typedef struct EelBackgroundClass EelBackgroundClass;
+
+#define EEL_TYPE_BACKGROUND \
+	(eel_background_get_type ())
+#define EEL_BACKGROUND(obj) \
+	(GTK_CHECK_CAST ((obj), EEL_TYPE_BACKGROUND, EelBackground))
+#define EEL_BACKGROUND_CLASS(klass) \
+	(GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_BACKGROUND, EelBackgroundClass))
+#define EEL_IS_BACKGROUND(obj) \
+	(GTK_CHECK_TYPE ((obj), EEL_TYPE_BACKGROUND))
+#define EEL_IS_BACKGROUND_CLASS(klass) \
+	(GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_BACKGROUND))
+
+typedef enum {
+	EEL_BACKGROUND_TILED = 0, /* zero makes this the default placement */
+	EEL_BACKGROUND_CENTERED,
+	EEL_BACKGROUND_SCALED,
+	EEL_BACKGROUND_SCALED_ASPECT,
+	EEL_BACKGROUND_ZOOM
+} EelBackgroundImagePlacement;
+
+GtkType                     eel_background_get_type                         (void);
+EelBackground *             eel_background_new                              (void);
+
+
+/* Calls to change a background. */
+void                        eel_background_set_use_base                     (EelBackground               *background,
+									     gboolean                     use_base);
+void                        eel_background_set_color                        (EelBackground               *background,
+									     const char                  *color_or_gradient);
+void                        eel_background_set_image_uri                    (EelBackground               *background,
+									     const char                  *image_uri);
+
+void                        eel_background_reset                            (EelBackground               *background);
+void                        eel_background_set_image_placement              (EelBackground               *background,
+									     EelBackgroundImagePlacement  placement);
+
+/* Should be TRUE for desktop background */
+void			    eel_background_set_desktop 			    (EelBackground              *background,
+									     GtkWidget *widget, 
+									     gboolean is_desktop);
+gboolean		    eel_background_is_desktop 			    (EelBackground              *background);
+
+/* Calls to interrogate the current state of a background. */
+char *                      eel_background_get_color                        (EelBackground               *background);
+char *                      eel_background_get_image_uri                    (EelBackground               *background);
+EelBackgroundImagePlacement eel_background_get_image_placement              (EelBackground               *background);
+gboolean                    eel_background_is_dark                          (EelBackground               *background);
+gboolean                    eel_background_is_set                           (EelBackground               *background);
+
+/* Helper function for widgets using EelBackground */
+void                        eel_background_expose                           (GtkWidget                   *widget,
+									     GdkEventExpose              *event);
+
+/* Handles a dragged color being dropped on a widget to change the background color. */
+void                        eel_background_receive_dropped_color            (EelBackground               *background,
+									     GtkWidget                   *widget,
+									     GdkDragAction                action,
+									     int                          drop_location_x,
+									     int                          drop_location_y,
+									     const GtkSelectionData      *dropped_color);
+
+/* Handles a special-case image name that means "reset to default background" too. */
+void                        eel_background_receive_dropped_background_image (EelBackground               *background,
+									     GdkDragAction                action,
+									     const char                  *image_uri);
+
+/* Gets or creates a background so that it's attached to a widget. */
+EelBackground *             eel_get_widget_background                       (GtkWidget                   *widget);
+void			    eel_background_save_to_gconf                    (EelBackground               *background);
+
+typedef struct EelBackgroundDetails EelBackgroundDetails;
+
+struct EelBackground
+{
+	GtkObject object;
+	EelBackgroundDetails *details;
+};
+
+struct EelBackgroundClass
+{
+	GtkObjectClass parent_class;
+
+	/* This signal is emitted whenever the background settings are
+	 * changed.
+	 */
+	void (* settings_changed) (EelBackground *);
+
+	/* This signal is emitted whenever the appearance of the
+	 * background has changed, like when the background settings are
+	 * altered or when an image is loaded.
+	 */
+	void (* appearance_changed) (EelBackground *);
+
+	/* This signal is emitted when image loading is over - whether it
+	 * was successfully loaded or not.
+	 */
+	void (* image_loading_done) (EelBackground *background, gboolean successful_load);
+
+	/* This signal is emitted when the background is reset by receiving
+	   the reset property from a drag
+	 */
+	void (* reset) (EelBackground *);
+
+};
+
+#endif /* EEL_BACKGROUND_H */

Added: trunk/eel/eel-canvas-rect-ellipse.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-canvas-rect-ellipse.c	Mon Dec 15 15:56:41 2008
@@ -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 EelCanvas widget
+ *
+ * EelCanvas 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 "eel-canvas-rect-ellipse.h"
+#include "eel-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 eel_canvas_re_class_init (EelCanvasREClass *class);
+static void eel_canvas_re_init       (EelCanvasRE      *re);
+static void eel_canvas_re_destroy    (GtkObject          *object);
+static void eel_canvas_re_set_property (GObject              *object,
+					  guint                 param_id,
+					  const GValue         *value,
+					  GParamSpec           *pspec);
+static void eel_canvas_re_get_property (GObject              *object,
+					  guint                 param_id,
+					  GValue               *value,
+					  GParamSpec           *pspec);
+
+static void eel_canvas_re_update_shared (EelCanvasItem *item,
+					   double i2w_dx, double i2w_dy, int flags);
+static void eel_canvas_re_realize     (EelCanvasItem *item);
+static void eel_canvas_re_unrealize   (EelCanvasItem *item);
+static void eel_canvas_re_bounds      (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+static void eel_canvas_re_translate   (EelCanvasItem *item, double dx, double dy);
+static void eel_canvas_rect_update      (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+static void eel_canvas_ellipse_update      (EelCanvasItem *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 EelCanvasItemClass *re_parent_class;
+static EelCanvasREClass *rect_parent_class;
+
+
+GType
+eel_canvas_re_get_type (void)
+{
+	static GType re_type = 0;
+
+	if (!re_type) {
+		GTypeInfo re_info = {
+		  sizeof (EelCanvasREClass),
+		  (GBaseInitFunc) NULL,
+		  (GBaseFinalizeFunc) NULL,
+		  (GClassInitFunc) eel_canvas_re_class_init,
+		  NULL,           /* class_finalize */
+		  NULL,           /* class_data */
+		  sizeof (EelCanvasRE),
+		  0,              /* n_preallocs */
+		  (GInstanceInitFunc) eel_canvas_re_init
+		};
+
+		re_type = g_type_register_static (eel_canvas_item_get_type (),
+						  "EelCanvasRE",
+						  &re_info,
+						  0);
+	}
+
+	return re_type;
+}
+
+static void
+eel_canvas_re_class_init (EelCanvasREClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	EelCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (EelCanvasItemClass *) class;
+
+	re_parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = eel_canvas_re_set_property;
+	gobject_class->get_property = eel_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_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y1,
+                 g_param_spec_double ("y1", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X2,
+                 g_param_spec_double ("x2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y2,
+                 g_param_spec_double ("y2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_string ("fill_color", NULL, NULL,
+                                      NULL,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        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_READABLE | G_PARAM_WRITABLE)));
+        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_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_STIPPLE,
+                 g_param_spec_object ("fill_stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_COLOR,
+                 g_param_spec_string ("outline_color", NULL, NULL,
+                                      NULL,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        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_READABLE | G_PARAM_WRITABLE)));
+        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_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_OUTLINE_STIPPLE,
+                 g_param_spec_object ("outline_stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_PIXELS,
+                 g_param_spec_uint ("width_pixels", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        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_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = eel_canvas_re_destroy;
+
+	item_class->realize = eel_canvas_re_realize;
+	item_class->unrealize = eel_canvas_re_unrealize;
+	item_class->translate = eel_canvas_re_translate;
+	item_class->bounds = eel_canvas_re_bounds;
+}
+
+static void
+eel_canvas_re_init (EelCanvasRE *re)
+{
+	re->x1 = 0.0;
+	re->y1 = 0.0;
+	re->x2 = 0.0;
+	re->y2 = 0.0;
+	re->width = 0.0;
+}
+
+static void
+eel_canvas_re_destroy (GtkObject *object)
+{
+	EelCanvasRE *re;
+
+	g_assert (object != NULL);
+	g_assert (EEL_IS_CANVAS_RE (object));
+
+	re = EEL_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 (EelCanvasRE *re, double *px1, double *py1, double *px2, double *py2)
+{
+	EelCanvasItem *item;
+	double x1, y1, x2, y2;
+	int cx1, cy1, cx2, cy2;
+	double hwidth;
+
+#ifdef VERBOSE
+	g_print ("re get_bounds\n");
+#endif
+	item = EEL_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;
+
+	eel_canvas_item_i2w (item, &x1, &y1);
+	eel_canvas_item_i2w (item, &x2, &y2);
+	eel_canvas_w2c (item->canvas, x1 - hwidth, y1 - hwidth, &cx1, &cy1);
+	eel_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 (EelCanvasRE *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
+eel_canvas_re_set_fill (EelCanvasRE *re, gboolean fill_set)
+{
+	if (re->fill_set != fill_set) {
+		re->fill_set = fill_set;
+		eel_canvas_item_request_update (EEL_CANVAS_ITEM (re));
+	}
+}
+
+static void
+eel_canvas_re_set_outline (EelCanvasRE *re, gboolean outline_set)
+{
+	if (re->outline_set != outline_set) {
+		re->outline_set = outline_set;
+		eel_canvas_item_request_update (EEL_CANVAS_ITEM (re));
+	}
+}
+
+static void
+eel_canvas_re_set_property (GObject              *object,
+			      guint                 param_id,
+			      const GValue         *value,
+			      GParamSpec           *pspec)
+{
+	EelCanvasItem *item;
+	EelCanvasRE *re;
+	GdkColor color = { 0, 0, 0, 0, };
+	GdkColor *pcolor;
+	int have_pixel;
+
+	g_assert (object != NULL);
+	g_assert (EEL_IS_CANVAS_RE (object));
+
+	item = EEL_CANVAS_ITEM (object);
+	re = EEL_CANVAS_RE (object);
+	have_pixel = FALSE;
+
+	switch (param_id) {
+	case PROP_X1:
+		re->x1 = g_value_get_double (value);
+
+		eel_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y1:
+		re->y1 = g_value_get_double (value);
+
+		eel_canvas_item_request_update (item);
+		break;
+
+	case PROP_X2:
+		re->x2 = g_value_get_double (value);
+
+		eel_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y2:
+		re->y2 = g_value_get_double (value);
+
+		eel_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))
+				eel_canvas_re_set_fill (re, TRUE);
+			else
+				eel_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);
+			eel_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:
+			eel_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 = eel_canvas_get_color_pixel (item->canvas, re->fill_color);
+
+		set_gc_foreground (re->fill_gc, re->fill_pixel);
+
+		eel_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))
+				eel_canvas_re_set_outline (re, TRUE);
+			else
+				eel_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);
+			eel_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:
+			eel_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 = eel_canvas_get_color_pixel (item->canvas,
+									  re->outline_color);
+
+		set_gc_foreground (re->outline_gc, re->outline_pixel);
+
+		eel_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);
+
+		eel_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);
+
+		eel_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 (EelCanvasRE *re, gulong pixel, GValue *value)
+{
+	GdkColor color;
+	EelCanvasItem *item = (EelCanvasItem *) 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
+eel_canvas_re_get_property (GObject              *object,
+			      guint                 param_id,
+			      GValue               *value,
+			      GParamSpec           *pspec)
+{
+	EelCanvasRE *re;
+
+	g_assert (object != NULL);
+	g_assert (EEL_IS_CANVAS_RE (object));
+
+	re = EEL_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 (EelCanvasRE *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
+eel_canvas_re_update_shared (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	EelCanvasRE *re;
+
+#ifdef VERBOSE
+	g_print ("eel_canvas_re_update_shared\n");
+#endif
+	re = EEL_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
+eel_canvas_re_realize (EelCanvasItem *item)
+{
+	EelCanvasRE *re;
+
+#ifdef VERBOSE
+	g_print ("eel_canvas_re_realize\n");
+#endif
+	re = EEL_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 = eel_canvas_get_color_pixel (item->canvas, re->fill_color);
+	re->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+	re->outline_pixel = eel_canvas_get_color_pixel (item->canvas, re->outline_color);
+	set_colors_and_stipples (re);
+
+#ifdef OLD_XFORM
+	(* EEL_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+eel_canvas_re_unrealize (EelCanvasItem *item)
+{
+	EelCanvasRE *re;
+
+	re = EEL_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
+eel_canvas_re_translate (EelCanvasItem *item, double dx, double dy)
+{
+	EelCanvasRE *re;
+
+#ifdef VERBOSE
+	g_print ("eel_canvas_re_translate\n");
+#endif
+	re = EEL_CANVAS_RE (item);
+
+	re->x1 += dx;
+	re->y1 += dy;
+	re->x2 += dx;
+	re->y2 += dy;
+}
+
+
+static void
+eel_canvas_re_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	EelCanvasRE *re;
+	double hwidth;
+
+#ifdef VERBOSE
+	g_print ("eel_canvas_re_bounds\n");
+#endif
+	re = EEL_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 eel_canvas_rect_class_init (EelCanvasRectClass *class);
+static void eel_canvas_rect_init (EelCanvasRect *rect);
+static void eel_canvas_rect_finalize (GObject *object);
+static void eel_canvas_rect_realize  (EelCanvasItem *item);
+
+static void   eel_canvas_rect_draw   (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+static double eel_canvas_rect_point  (EelCanvasItem *item, double x, double y, int cx, int cy,
+				        EelCanvasItem **actual_item);
+
+struct _EelCanvasRectPrivate {
+	Rect last_update_rect;
+	Rect last_outline_update_rect;
+	int last_outline_update_width;
+
+#ifdef HAVE_RENDER
+	gboolean use_render;
+	XRenderPictFormat *format;
+#endif
+};
+
+GType
+eel_canvas_rect_get_type (void)
+{
+	static GType rect_type = 0;
+
+	if (!rect_type) {
+		GTypeInfo rect_info = {
+		  sizeof (EelCanvasRectClass),
+		  (GBaseInitFunc) NULL,
+		  (GBaseFinalizeFunc) NULL,
+		  (GClassInitFunc) eel_canvas_rect_class_init,
+		  NULL,           /* class_finalize */
+		  NULL,           /* class_data */
+		  sizeof (EelCanvasRect),
+		  0,              /* n_preallocs */
+		  (GInstanceInitFunc) eel_canvas_rect_init
+		};
+
+		rect_type = g_type_register_static (eel_canvas_re_get_type (),
+						    "EelCanvasRect",
+						    &rect_info,
+						    0);
+	}
+
+	return rect_type;
+}
+
+static void
+eel_canvas_rect_class_init (EelCanvasRectClass *class)
+{
+	EelCanvasItemClass *item_class;
+
+	rect_parent_class = g_type_class_peek_parent (class);
+
+	item_class = (EelCanvasItemClass *) class;
+
+	item_class->draw = eel_canvas_rect_draw;
+	item_class->point = eel_canvas_rect_point;
+	item_class->update = eel_canvas_rect_update;
+	item_class->realize = eel_canvas_rect_realize;
+
+	G_OBJECT_CLASS (class)->finalize = eel_canvas_rect_finalize;
+	
+}
+
+static void
+eel_canvas_rect_init (EelCanvasRect *rect)
+{
+	rect->priv = g_new0 (EelCanvasRectPrivate, 1);
+}
+
+static void
+eel_canvas_rect_finalize (GObject *object)
+{
+	EelCanvasRect *rect = EEL_CANVAS_RECT (object);
+
+	if (rect->priv) {
+		g_free (rect->priv);
+	}
+
+	G_OBJECT_CLASS (rect_parent_class)->finalize (object);
+}
+
+static void
+eel_canvas_rect_realize  (EelCanvasItem *item)
+{
+#ifdef HAVE_RENDER
+	EelCanvasRectPrivate *priv;
+	int event_base, error_base;
+
+	priv = EEL_CANVAS_RECT (item)->priv;
+
+	priv->use_render = XRenderQueryExtension (gdk_display, &event_base, &error_base);
+
+	if (priv->use_render) {
+		Display *dpy;
+		GdkVisual *gdk_visual;
+		Visual *visual;
+
+		dpy = gdk_x11_drawable_get_xdisplay (GTK_WIDGET (item->canvas)->window);
+		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 (EEL_CANVAS_ITEM_CLASS (rect_parent_class)->realize) {
+		(* EEL_CANVAS_ITEM_CLASS (rect_parent_class)->realize) (item);
+	}
+}
+
+
+static void
+render_rect_alpha (EelCanvasRect *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;
+	EelCanvasRectPrivate *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
+eel_canvas_rect_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose)
+{
+	EelCanvasRE *re;
+	double x1, y1, x2, y2;
+	int cx1, cy1, cx2, cy2;
+	double i2w_dx, i2w_dy;
+
+	re = EEL_CANVAS_RE (item);
+
+	/* Get canvas pixel coordinates */
+	i2w_dx = 0.0;
+	i2w_dy = 0.0;
+	eel_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;
+
+	eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+	eel_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 (EEL_CANVAS_RECT (item),
+							   drawable,
+							   part.x, part.y,
+							   part.width, part.height,
+							   re->fill_color);
+				}
+			}
+			
+			g_free (rectangles);
+		} else {
+			if (re->fill_stipple)
+				eel_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)
+			eel_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
+eel_canvas_rect_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item)
+{
+	EelCanvasRE *re;
+	double x1, y1, x2, y2;
+	double hwidth;
+	double dx, dy;
+	double tmp;
+
+#ifdef VERBOSE
+	g_print ("eel_canvas_rect_point\n");
+#endif
+	re = EEL_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 (EelCanvas *canvas,
+			Rect     *update_rect,
+			int     width)
+{
+	eel_canvas_request_redraw (canvas,
+				   update_rect->x0, update_rect->y0,
+				   update_rect->x1, update_rect->y0 + width);
+	eel_canvas_request_redraw (canvas,
+				   update_rect->x0, update_rect->y1-width,
+				   update_rect->x1, update_rect->y1);
+	eel_canvas_request_redraw (canvas,
+				   update_rect->x0,       update_rect->y0,
+				   update_rect->x0+width, update_rect->y1);
+	eel_canvas_request_redraw (canvas,
+				   update_rect->x1-width, update_rect->y0,
+				   update_rect->x1,       update_rect->y1);
+}
+
+
+static void
+eel_canvas_rect_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, gint flags)
+{
+	EelCanvasRE *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];
+	EelCanvasRectPrivate *priv;
+
+	eel_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags);
+
+	re = EEL_CANVAS_RE (item);
+	priv = EEL_CANVAS_RECT (item)->priv;
+	
+	x1 = re->x1 + i2w_dx;
+	y1 = re->y1 + i2w_dy;
+	x2 = re->x2 + i2w_dx;
+	y2 = re->y2 + i2w_dy;
+
+	eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1);
+	eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2);
+
+	update_rect = make_rect (cx1, cy1, cx2+1, cy2+1);
+#if 0
+	eel_canvas_request_redraw (item->canvas,
+				   update_rect.x0, update_rect.y0,
+				   update_rect.x1, update_rect.y1);
+	eel_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++) {
+		eel_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 eel_canvas_ellipse_class_init (EelCanvasEllipseClass *class);
+
+static void   eel_canvas_ellipse_draw   (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose);
+static double eel_canvas_ellipse_point  (EelCanvasItem *item, double x, double y, int cx, int cy,
+					   EelCanvasItem **actual_item);
+
+
+GType
+eel_canvas_ellipse_get_type (void)
+{
+	static GType ellipse_type = 0;
+
+	if (!ellipse_type) {
+		GTypeInfo ellipse_info = {
+		  sizeof (EelCanvasEllipseClass),
+		  (GBaseInitFunc) NULL,
+		  (GBaseFinalizeFunc) NULL,
+		  (GClassInitFunc) eel_canvas_ellipse_class_init,
+		  NULL,           /* class_finalize */
+		  NULL,           /* class_data */
+		  sizeof (EelCanvasEllipse),
+		  0,              /* n_preallocs */
+		  (GInstanceInitFunc) NULL
+
+		};
+
+		ellipse_type = g_type_register_static (eel_canvas_re_get_type (),
+						       "EelCanvasEllipse",
+						       &ellipse_info,
+						       0);
+	}
+
+	return ellipse_type;
+}
+
+static void
+eel_canvas_ellipse_class_init (EelCanvasEllipseClass *class)
+{
+	EelCanvasItemClass *item_class;
+
+	item_class = (EelCanvasItemClass *) class;
+
+	item_class->draw = eel_canvas_ellipse_draw;
+	item_class->point = eel_canvas_ellipse_point;
+	item_class->update = eel_canvas_ellipse_update;
+}
+
+static void
+eel_canvas_ellipse_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose)
+{
+	EelCanvasRE *re;
+	int x1, y1, x2, y2;
+	double i2w_dx, i2w_dy;
+
+	re = EEL_CANVAS_RE (item);
+
+	/* Get canvas pixel coordinates */
+
+	i2w_dx = 0.0;
+	i2w_dy = 0.0;
+	eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy);
+	
+	eel_canvas_w2c (item->canvas,
+			  re->x1 + i2w_dx,
+			  re->y1 + i2w_dy,
+			  &x1, &y1);
+	eel_canvas_w2c (item->canvas,
+			  re->x2 + i2w_dx,
+			  re->y2 + i2w_dy,
+			  &x2, &y2);
+
+	if (re->fill_set) {
+		if (re->fill_stipple)
+			eel_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)
+			eel_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
+eel_canvas_ellipse_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item)
+{
+	EelCanvasRE *re;
+	double dx, dy;
+	double scaled_dist;
+	double outline_dist;
+	double center_dist;
+	double width;
+	double a, b;
+	double diamx, diamy;
+
+	re = EEL_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 > EEL_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
+eel_canvas_ellipse_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, gint flags)
+{
+	EelCanvasRE *re;
+	double x0, y0, x1, y1;
+
+#ifdef VERBOSE
+	g_print ("eel_canvas_sllipse_update item %x\n", item);
+#endif
+
+	eel_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags);
+	re = EEL_CANVAS_RE (item);
+
+	get_bounds (re, &x0, &y0, &x1, &y1);
+	eel_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;
+		}
+	}
+}

Added: trunk/eel/eel-canvas-rect-ellipse.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-canvas-rect-ellipse.h	Mon Dec 15 15:56:41 2008
@@ -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 EelCanvas widget
+ *
+ * EelCanvas 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 EEL_CANVAS_RECT_ELLIPSE_H
+#define EEL_CANVAS_RECT_ELLIPSE_H
+
+
+#include <eel/eel-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 EEL_TYPE_CANVAS_RE            (eel_canvas_re_get_type ())
+#define EEL_CANVAS_RE(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_CANVAS_RE, EelCanvasRE))
+#define EEL_CANVAS_RE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS_RE, EelCanvasREClass))
+#define EEL_IS_CANVAS_RE(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_CANVAS_RE))
+#define EEL_IS_CANVAS_RE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS_RE))
+#define EEL_CANVAS_RE_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), EEL_TYPE_CANVAS_RE, EelCanvasREClass))
+
+
+typedef struct _EelCanvasRE      EelCanvasRE;
+typedef struct _EelCanvasREClass EelCanvasREClass;
+
+struct _EelCanvasRE {
+	EelCanvasItem 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 _EelCanvasREClass {
+	EelCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType eel_canvas_re_get_type (void) G_GNUC_CONST;
+
+
+/* Rectangle item.  No configurable or queryable arguments are available (use those in
+ * EelCanvasRE).
+ */
+
+
+#define EEL_TYPE_CANVAS_RECT            (eel_canvas_rect_get_type ())
+#define EEL_CANVAS_RECT(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_CANVAS_RECT, EelCanvasRect))
+#define EEL_CANVAS_RECT_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS_RECT, EelCanvasRectClass))
+#define EEL_IS_CANVAS_RECT(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_CANVAS_RECT))
+#define EEL_IS_CANVAS_RECT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS_RECT))
+#define EEL_CANVAS_RECT_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), EEL_TYPE_CANVAS_RECT, EelCanvasRectClass))
+
+
+typedef struct _EelCanvasRect EelCanvasRect;
+typedef struct _EelCanvasRectPrivate EelCanvasRectPrivate;
+typedef struct _EelCanvasRectClass EelCanvasRectClass;
+
+struct _EelCanvasRect {
+	EelCanvasRE re;
+	EelCanvasRectPrivate *priv;
+};
+
+struct _EelCanvasRectClass {
+	EelCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType eel_canvas_rect_get_type (void) G_GNUC_CONST;
+
+
+/* Ellipse item.  No configurable or queryable arguments are available (use those in
+ * EelCanvasRE).
+ */
+
+
+#define EEL_TYPE_CANVAS_ELLIPSE            (eel_canvas_ellipse_get_type ())
+#define EEL_CANVAS_ELLIPSE(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_CANVAS_ELLIPSE, EelCanvasEllipse))
+#define EEL_CANVAS_ELLIPSE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS_ELLIPSE, EelCanvasEllipseClass))
+#define EEL_IS_CANVAS_ELLIPSE(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_CANVAS_ELLIPSE))
+#define EEL_IS_CANVAS_ELLIPSE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS_ELLIPSE))
+#define EEL_CANVAS_ELLIPSE_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), EEL_TYPE_CANVAS_ELLIPSE, EelCanvasEllipseClass))
+
+
+typedef struct _EelCanvasEllipse EelCanvasEllipse;
+typedef struct _EelCanvasEllipseClass EelCanvasEllipseClass;
+
+struct _EelCanvasEllipse {
+	EelCanvasRE re;
+};
+
+struct _EelCanvasEllipseClass {
+	EelCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType eel_canvas_ellipse_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif

Added: trunk/eel/eel-canvas-util.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-canvas-util.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,396 @@
+/*
+ * 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 EelCanvas widget
+ *
+ * EelCanvas 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 "eel-canvas.h"
+#include "eel-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
+
+/**
+ * eel_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.
+ **/
+EelCanvasPoints *
+eel_canvas_points_new (int num_points)
+{
+	EelCanvasPoints *points;
+
+	g_return_val_if_fail (num_points > 1, NULL);
+
+	points = g_new (EelCanvasPoints, 1);
+	points->num_points = num_points;
+	points->coords = g_new (double, 2 * num_points);
+	points->ref_count = 1;
+
+	return points;
+}
+
+/**
+ * eel_canvas_points_ref:
+ * @points: A canvas points structure.
+ * 
+ * Increases the reference count of the specified points structure.
+ * 
+ * Return value: The canvas points structure itself.
+ **/
+EelCanvasPoints *
+eel_canvas_points_ref (EelCanvasPoints *points)
+{
+	g_return_val_if_fail (points != NULL, NULL);
+
+	points->ref_count += 1;
+	return points;
+}
+
+/**
+ * eel_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
+eel_canvas_points_free (EelCanvasPoints *points)
+{
+	g_return_if_fail (points != NULL);
+
+	points->ref_count -= 1;
+	if (points->ref_count == 0) {
+		g_free (points->coords);
+		g_free (points);
+	}
+}
+
+/**
+ * eel_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
+eel_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;
+}
+
+/**
+ * eel_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
+eel_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 < EEL_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;
+		}
+	}
+}
+
+/**
+ * eel_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
+eel_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;
+	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;
+}
+
+/**
+ * eel_canvas_item_reset_bounds:
+ * @item: A canvas item
+ * 
+ * Resets the bounding box of a canvas item to an empty rectangle.
+ **/
+void
+eel_canvas_item_reset_bounds (EelCanvasItem *item)
+{
+	item->x1 = 0.0;
+	item->y1 = 0.0;
+	item->x2 = 0.0;
+	item->y2 = 0.0;
+}
+
+/**
+ * eel_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
+eel_canvas_update_bbox (EelCanvasItem *item, int x1, int y1, int x2, int y2)
+{
+	eel_canvas_item_request_redraw (item);
+	item->x1 = x1;
+	item->y1 = y1;
+	item->x2 = x2;
+	item->y2 = y2;
+	eel_canvas_item_request_redraw (item);
+}
+

Added: trunk/eel/eel-canvas-util.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-canvas-util.h	Mon Dec 15 15:56:41 2008
@@ -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 EelCanvas widget
+ *
+ * EelCanvas 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 EEL_CANVAS_UTIL_H
+#define EEL_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;
+} EelCanvasPoints;
+
+
+/* Allocate a new EelCanvasPoints structure with enough space for the specified number of points */
+EelCanvasPoints *eel_canvas_points_new (int num_points);
+
+/* Increate ref count */
+EelCanvasPoints *eel_canvas_points_ref (EelCanvasPoints *points);
+#define eel_canvas_points_unref eel_canvas_points_free
+
+/* Decrease ref count and free structure if it has reached zero */
+void eel_canvas_points_free (EelCanvasPoints *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 eel_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 eel_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 eel_canvas_polygon_to_point (double *poly, int num_points, double x, double y);
+
+
+void eel_canvas_item_reset_bounds (EelCanvasItem *item);
+
+/* Sets the bbox to the new value, requesting full repaint. */
+void eel_canvas_update_bbox (EelCanvasItem *item, int x1, int y1, int x2, int y2);
+
+G_END_DECLS
+
+#endif

Added: trunk/eel/eel-canvas.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-canvas.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,3979 @@
+/* -*- 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@
+ */
+/*
+ * EelCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * EelCanvas 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 EelCanvasImage sizes are in units or pixels (scale or don't scale).
+ *
+ * - Implement a flag for eel_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 *eel_canvas_gimme_all_items_contained_in_this_area (EelCanvas *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 EelCanvasEllipse).
+ *
+ * - 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 "eel-canvas.h"
+#include "eel-i18n.h"
+
+#include "eel-marshal.h"
+
+static void eel_canvas_request_update (EelCanvas      *canvas);
+static void group_add                   (EelCanvasGroup *group,
+					 EelCanvasItem  *item);
+static void group_remove                (EelCanvasGroup *group,
+					 EelCanvasItem  *item);
+static void redraw_and_repick_if_mapped (EelCanvasItem *item);
+
+/*** EelCanvasItem ***/
+
+/* Some convenience stuff */
+#define GCI_UPDATE_MASK (EEL_CANVAS_UPDATE_REQUESTED | EEL_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 eel_canvas_item_class_init     (EelCanvasItemClass *class);
+static void eel_canvas_item_init           (EelCanvasItem      *item);
+static int  emit_event                       (EelCanvas *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;
+
+
+/**
+ * eel_canvas_item_get_type:
+ *
+ * Registers the &EelCanvasItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &EelCanvasItem class.
+ **/
+GType
+eel_canvas_item_get_type (void)
+{
+	static GType canvas_item_type = 0;
+
+	if (!canvas_item_type) {
+		const GTypeInfo canvas_item_info = {
+			sizeof (EelCanvasItemClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_canvas_item_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (EelCanvasItem),
+			0,              /* n_preallocs */
+			(GInstanceInitFunc) eel_canvas_item_init
+		};
+
+		canvas_item_type = g_type_register_static (gtk_object_get_type (),
+							   "EelCanvasItem",
+							   &canvas_item_info,
+							   0);
+	}
+
+	return canvas_item_type;
+}
+
+/* Object initialization function for EelCanvasItem */
+static void
+eel_canvas_item_init (EelCanvasItem *item)
+{
+	item->object.flags |= EEL_CANVAS_ITEM_VISIBLE;
+}
+
+/**
+ * eel_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
+ * eel_canvas_rect_get_type().  The list of object arguments/value pairs is
+ * used to configure the item.
+ *
+ * Return value: The newly-created item.
+ **/
+EelCanvasItem *
+eel_canvas_item_new (EelCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
+{
+	EelCanvasItem *item;
+	va_list args;
+
+	g_return_val_if_fail (EEL_IS_CANVAS_GROUP (parent), NULL);
+	g_return_val_if_fail (g_type_is_a (type, eel_canvas_item_get_type ()), NULL);
+
+	item = EEL_CANVAS_ITEM (g_object_new (type, NULL));
+
+	va_start (args, first_arg_name);
+	eel_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 (EelCanvasItem *item)
+{
+	group_add (EEL_CANVAS_GROUP (item->parent), item);
+
+	redraw_and_repick_if_mapped (item);
+}
+
+/* Set_property handler for canvas items */
+static void
+eel_canvas_item_set_property (GObject *gobject, guint param_id,
+			      const GValue *value, GParamSpec *pspec)
+{
+	EelCanvasItem *item;
+
+	g_assert (EEL_IS_CANVAS_ITEM (gobject));
+
+	item = EEL_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 = EEL_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)) {
+			eel_canvas_item_show (item);
+		} else {
+			eel_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
+eel_canvas_item_get_property (GObject *gobject, guint param_id,
+			      GValue *value, GParamSpec *pspec)
+{
+	EelCanvasItem *item;
+
+	g_assert (EEL_IS_CANVAS_ITEM (gobject));
+
+	item = EEL_CANVAS_ITEM (gobject);
+
+	switch (param_id) {
+	case ITEM_PROP_VISIBLE:
+		g_value_set_boolean (value, item->object.flags & EEL_CANVAS_ITEM_VISIBLE);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/**
+ * eel_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
+eel_canvas_item_construct (EelCanvasItem *item, EelCanvasGroup *parent,
+			   const gchar *first_arg_name, va_list args)
+{
+	g_return_if_fail (EEL_IS_CANVAS_GROUP (parent));
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+	item->parent = EEL_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 (EelCanvasItem *item)
+{
+	if (item->object.flags & EEL_CANVAS_ITEM_MAPPED) {
+		eel_canvas_item_request_redraw (item);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+
+/* Standard object dispose function for canvas items */
+static void
+eel_canvas_item_dispose (GObject *object)
+{
+	EelCanvasItem *item;
+
+	g_assert (EEL_IS_CANVAS_ITEM (object));
+
+	item = EEL_CANVAS_ITEM (object);
+
+	eel_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 & EEL_CANVAS_ITEM_MAPPED)
+		(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+	if (item->object.flags & EEL_CANVAS_ITEM_REALIZED)
+		(* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+	if (item->parent)
+		group_remove (EEL_CANVAS_GROUP (item->parent), item);
+
+	G_OBJECT_CLASS (item_parent_class)->dispose (object);
+}
+
+/* Realize handler for canvas items */
+static void
+eel_canvas_item_realize (EelCanvasItem *item)
+{
+	if (item->parent && !(item->parent->object.flags & EEL_CANVAS_ITEM_REALIZED))
+		(* EEL_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, EEL_CANVAS_ITEM_REALIZED);
+
+	eel_canvas_item_request_update (item);
+}
+
+/* Unrealize handler for canvas items */
+static void
+eel_canvas_item_unrealize (EelCanvasItem *item)
+{
+	if (item->object.flags & EEL_CANVAS_ITEM_MAPPED)
+		(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+	
+	GTK_OBJECT_UNSET_FLAGS (item, EEL_CANVAS_ITEM_REALIZED);
+}
+
+/* Map handler for canvas items */
+static void
+eel_canvas_item_map (EelCanvasItem *item)
+{
+	GTK_OBJECT_SET_FLAGS (item, EEL_CANVAS_ITEM_MAPPED);
+}
+
+/* Unmap handler for canvas items */
+static void
+eel_canvas_item_unmap (EelCanvasItem *item)
+{
+	GTK_OBJECT_UNSET_FLAGS (item, EEL_CANVAS_ITEM_MAPPED);
+}
+
+/* Update handler for canvas items */
+static void
+eel_canvas_item_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	GTK_OBJECT_UNSET_FLAGS (item, EEL_CANVAS_ITEM_NEED_UPDATE);
+	GTK_OBJECT_UNSET_FLAGS (item, EEL_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
+eel_canvas_item_invoke_update (EelCanvasItem *item,
+			       double i2w_dx,
+			       double i2w_dy,
+			       int flags)
+{
+	int child_flags;
+
+	child_flags = flags;
+
+	/* apply object flags to child flags */
+	child_flags &= ~EEL_CANVAS_UPDATE_REQUESTED;
+
+	if (item->object.flags & EEL_CANVAS_ITEM_NEED_UPDATE)
+		child_flags |= EEL_CANVAS_UPDATE_REQUESTED;
+
+	if (item->object.flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)
+		child_flags |= EEL_CANVAS_UPDATE_DEEP;
+
+	if (child_flags & GCI_UPDATE_MASK) {
+		if (EEL_CANVAS_ITEM_GET_CLASS (item)->update)
+			EEL_CANVAS_ITEM_GET_CLASS (item)->update (item, i2w_dx, i2w_dy, child_flags);
+	}
+ 
+	/* If this fail you probably forgot to chain up to
+	 * EelCanvasItem::update from a derived class */
+ 	g_assert (!(item->object.flags & EEL_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
+eel_canvas_item_invoke_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item)
+{
+	/* Calculate x & y in item local coordinates */
+
+	if (EEL_CANVAS_ITEM_GET_CLASS (item)->point)
+		return EEL_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
+
+	return 1e18;
+}
+
+/**
+ * eel_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
+eel_canvas_item_set (EelCanvasItem *item, const gchar *first_arg_name, ...)
+{
+	va_list args;
+
+	va_start (args, first_arg_name);
+	eel_canvas_item_set_valist (item, first_arg_name, args);
+	va_end (args);
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_set_valist (EelCanvasItem *item, const gchar *first_arg_name, va_list args)
+{
+	g_return_if_fail (EEL_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 */
+	eel_canvas_item_request_redraw (item);
+#endif
+
+	item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_move (EelCanvasItem *item, double dx, double dy)
+{
+        g_return_if_fail (item != NULL);
+        g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+        if (!EEL_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;
+        }
+
+        (* EEL_CANVAS_ITEM_GET_CLASS (item)->translate) (item, dx, dy);
+
+	if (item->object.flags & EEL_CANVAS_ITEM_MAPPED) 
+		item->canvas->need_repick = TRUE;
+
+	if (!(item->object.flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+		item->object.flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		if (item->parent != NULL)
+			eel_canvas_item_request_update (item->parent);
+		else
+			eel_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)
+{
+	EelCanvasGroup *parent;
+
+	if (link == before)
+		return FALSE;
+
+	parent = EEL_CANVAS_GROUP (EEL_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;
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_raise (EelCanvasItem *item, int positions)
+{
+	GList *link, *before;
+	EelCanvasGroup *parent;
+
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+	g_return_if_fail (positions >= 0);
+
+	if (!item->parent || positions == 0)
+		return;
+
+	parent = EEL_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);
+	}
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_lower (EelCanvasItem *item, int positions)
+{
+	GList *link, *before;
+	EelCanvasGroup *parent;
+
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+	g_return_if_fail (positions >= 1);
+
+	if (!item->parent || positions == 0)
+		return;
+
+	parent = EEL_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);
+	}
+}
+
+
+/**
+ * eel_canvas_item_raise_to_top:
+ * @item: A canvas item.
+ *
+ * Raises an item to the top of its parent's stack.
+ **/
+void
+eel_canvas_item_raise_to_top (EelCanvasItem *item)
+{
+	GList *link;
+	EelCanvasGroup *parent;
+
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+	if (!item->parent)
+		return;
+
+	parent = EEL_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);
+	}
+}
+
+
+/**
+ * eel_canvas_item_lower_to_bottom:
+ * @item: A canvas item.
+ *
+ * Lowers an item to the bottom of its parent's stack.
+ **/
+void
+eel_canvas_item_lower_to_bottom (EelCanvasItem *item)
+{
+	GList *link;
+	EelCanvasGroup *parent;
+
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+	if (!item->parent)
+		return;
+
+	parent = EEL_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);
+	}
+}
+
+/**
+ * eel_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
+eel_canvas_item_send_behind (EelCanvasItem *item,
+			     EelCanvasItem *behind_item)
+{
+	GList *item_list;
+	int item_position, behind_position;
+
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+	if (behind_item == NULL) {
+		eel_canvas_item_raise_to_top (item);
+		return;
+	}
+
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (behind_item));
+	g_return_if_fail (item->parent == behind_item->parent);
+
+	item_list = EEL_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) {
+		eel_canvas_item_raise (item, (behind_position - 1) - item_position);
+	} else {
+		eel_canvas_item_lower (item, item_position - behind_position);
+	}
+}
+
+/**
+ * eel_canvas_item_show:
+ * @item: A canvas item.
+ *
+ * Shows a canvas item.  If the item was already shown, then no action is taken.
+ **/
+void
+eel_canvas_item_show (EelCanvasItem *item)
+{
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+	if (!(item->object.flags & EEL_CANVAS_ITEM_VISIBLE)) {
+		item->object.flags |= EEL_CANVAS_ITEM_VISIBLE;
+		
+		if (!(item->object.flags & EEL_CANVAS_ITEM_REALIZED))
+			(* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+
+		if (item->parent != NULL) {
+			if (!(item->object.flags & EEL_CANVAS_ITEM_MAPPED) &&
+			    item->parent->object.flags & EEL_CANVAS_ITEM_MAPPED)
+				(* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+		} else {
+			if (!(item->object.flags & EEL_CANVAS_ITEM_MAPPED) &&
+			    GTK_WIDGET_MAPPED (GTK_WIDGET (item->canvas)))
+				(* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+		}
+
+		redraw_and_repick_if_mapped (item);
+	}
+}
+
+
+/**
+ * eel_canvas_item_hide:
+ * @item: A canvas item.
+ *
+ * Hides a canvas item.  If the item was already hidden, then no action is
+ * taken.
+ **/
+void
+eel_canvas_item_hide (EelCanvasItem *item)
+{
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+	if (item->object.flags & EEL_CANVAS_ITEM_VISIBLE) {
+		item->object.flags &= ~EEL_CANVAS_ITEM_VISIBLE;
+
+		redraw_and_repick_if_mapped (item);
+		
+		if (item->object.flags & EEL_CANVAS_ITEM_MAPPED)
+			(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+		/* No need to unrealize when we just want to hide */
+	}
+}
+
+
+/**
+ * eel_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 eel_canvas_item_hide(), then it
+ * returns %GDK_GRAB_NOT_VIEWABLE.  Else, it returns the result of calling
+ * gdk_pointer_grab().
+ **/
+int
+eel_canvas_item_grab (EelCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
+{
+	int retval;
+
+	g_return_val_if_fail (EEL_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 & EEL_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;
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_ungrab (EelCanvasItem *item, guint32 etime)
+{
+	GdkDisplay *display;
+
+	g_return_if_fail (EEL_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);
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_w2i (EelCanvasItem *item, double *x, double *y)
+{
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+	g_return_if_fail (x != NULL);
+	g_return_if_fail (y != NULL);
+
+	item = item->parent;
+	while (item) {
+		if (EEL_IS_CANVAS_GROUP (item)) {
+			*x -= EEL_CANVAS_GROUP (item)->xpos;
+			*y -= EEL_CANVAS_GROUP (item)->ypos;
+		}
+
+		item = item->parent;
+	}
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_i2w (EelCanvasItem *item, double *x, double *y)
+{
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+	g_return_if_fail (x != NULL);
+	g_return_if_fail (y != NULL);
+
+	item = item->parent;
+	while (item) {
+		if (EEL_IS_CANVAS_GROUP (item)) {
+			*x += EEL_CANVAS_GROUP (item)->xpos;
+			*y += EEL_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 (EelCanvasItem *item, EelCanvasItem *parent)
+{
+	for (; item; item = item->parent)
+		if (item == parent)
+			return TRUE;
+
+	return FALSE;
+}
+
+/**
+ * eel_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
+eel_canvas_item_reparent (EelCanvasItem *item, EelCanvasGroup *new_group)
+{
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+	g_return_if_fail (EEL_IS_CANVAS_GROUP (new_group));
+
+	/* Both items need to be in the same canvas */
+	g_return_if_fail (item->canvas == EEL_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 (EEL_CANVAS_ITEM (new_group), item));
+
+	/* Everything is ok, now actually reparent the item */
+
+	g_object_ref (GTK_OBJECT (item)); /* protect it from the unref in group_remove */
+
+	eel_canvas_item_request_redraw (item);
+
+	group_remove (EEL_CANVAS_GROUP (item->parent), item);
+	item->parent = EEL_CANVAS_ITEM (new_group);
+	group_add (new_group, item);
+
+	/* Redraw and repick */
+
+	redraw_and_repick_if_mapped (item);
+
+	g_object_unref (GTK_OBJECT (item));
+}
+
+/**
+ * eel_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
+eel_canvas_item_grab_focus (EelCanvasItem *item)
+{
+	EelCanvasItem *focused_item;
+	GdkEvent ev;
+
+	g_return_if_fail (EEL_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);                          
+	}                               
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_get_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	double tx1, ty1, tx2, ty2;
+
+	g_return_if_fail (EEL_IS_CANVAS_ITEM (item));
+
+	tx1 = ty1 = tx2 = ty2 = 0.0;
+
+	/* Get the item's bounds in its coordinate system */
+
+	if (EEL_CANVAS_ITEM_GET_CLASS (item)->bounds)
+		(* EEL_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;
+}
+
+
+/**
+ * eel_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
+eel_canvas_item_request_update (EelCanvasItem *item)
+{
+	g_return_if_fail (!item->canvas->doing_update);
+
+	if (item->object.flags & EEL_CANVAS_ITEM_NEED_UPDATE)
+		return;
+
+	item->object.flags |= EEL_CANVAS_ITEM_NEED_UPDATE;
+
+	if (item->parent != NULL) {
+		/* Recurse up the tree */
+		eel_canvas_item_request_update (item->parent);
+	} else {
+		/* Have reached the top of the tree, make sure the update call gets scheduled. */
+		eel_canvas_request_update (item->canvas);
+	}
+}
+
+/**
+ * eel_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
+eel_canvas_item_request_redraw (EelCanvasItem *item)
+{
+	if (item->object.flags & EEL_CANVAS_ITEM_MAPPED)
+		eel_canvas_request_redraw (item->canvas,
+					   item->x1, item->y1,
+					   item->x2 + 1, item->y2 + 1);
+}
+
+
+
+/*** EelCanvasGroup ***/
+
+
+enum {
+	GROUP_PROP_0,
+	GROUP_PROP_X,
+	GROUP_PROP_Y
+};
+
+
+static void eel_canvas_group_class_init  (EelCanvasGroupClass *class);
+static void eel_canvas_group_init        (EelCanvasGroup      *group);
+static void eel_canvas_group_set_property(GObject               *object, 
+					    guint                  param_id,
+					    const GValue          *value,
+					    GParamSpec            *pspec);
+static void eel_canvas_group_get_property(GObject               *object,
+					    guint                  param_id,
+					    GValue                *value,
+					    GParamSpec            *pspec);
+
+static void eel_canvas_group_destroy     (GtkObject             *object);
+
+static void   eel_canvas_group_update      (EelCanvasItem *item,
+					      double           i2w_dx,
+					      double           i2w_dy,
+					      int              flags);
+static void   eel_canvas_group_unrealize   (EelCanvasItem *item);
+static void   eel_canvas_group_map         (EelCanvasItem *item);
+static void   eel_canvas_group_unmap       (EelCanvasItem *item);
+static void   eel_canvas_group_draw        (EelCanvasItem *item, GdkDrawable *drawable,
+					      GdkEventExpose *expose);
+static double eel_canvas_group_point       (EelCanvasItem *item, double x, double y,
+					      int cx, int cy,
+					      EelCanvasItem **actual_item);
+static void   eel_canvas_group_translate   (EelCanvasItem *item, double dx, double dy);
+static void   eel_canvas_group_bounds      (EelCanvasItem *item, double *x1, double *y1,
+					      double *x2, double *y2);
+
+
+static EelCanvasItemClass *group_parent_class;
+
+
+/**
+ * eel_canvas_group_get_type:
+ *
+ * Registers the &EelCanvasGroup class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &EelCanvasGroup class.
+ **/
+GType
+eel_canvas_group_get_type (void)
+{
+	static GType group_type = 0;
+
+	if (!group_type) {
+		const GTypeInfo group_info = {
+			sizeof (EelCanvasGroupClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_canvas_group_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (EelCanvasGroup),
+			0,              /* n_preallocs */
+			(GInstanceInitFunc) eel_canvas_group_init
+
+	
+		};
+
+		group_type = g_type_register_static (eel_canvas_item_get_type (),
+						     "EelCanvasGroup",
+						     &group_info,
+						     0);
+	}
+
+	return group_type;
+}
+
+/* Class initialization function for EelCanvasGroupClass */
+static void
+eel_canvas_group_class_init (EelCanvasGroupClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	EelCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (EelCanvasItemClass *) class;
+
+	group_parent_class = gtk_type_class (eel_canvas_item_get_type ());
+
+	gobject_class->set_property = eel_canvas_group_set_property;
+	gobject_class->get_property = eel_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_READABLE | G_PARAM_WRITABLE)));
+	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_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = eel_canvas_group_destroy;
+
+	item_class->update = eel_canvas_group_update;
+	item_class->unrealize = eel_canvas_group_unrealize;
+	item_class->map = eel_canvas_group_map;
+	item_class->unmap = eel_canvas_group_unmap;
+	item_class->draw = eel_canvas_group_draw;
+	item_class->point = eel_canvas_group_point;
+	item_class->translate = eel_canvas_group_translate;
+	item_class->bounds = eel_canvas_group_bounds;
+}
+
+/* Object initialization function for EelCanvasGroup */
+static void
+eel_canvas_group_init (EelCanvasGroup *group)
+{
+	group->xpos = 0.0;
+	group->ypos = 0.0;
+}
+
+/* Set_property handler for canvas groups */
+static void
+eel_canvas_group_set_property (GObject *gobject, guint param_id,
+			       const GValue *value, GParamSpec *pspec)
+{
+	EelCanvasItem *item;
+	EelCanvasGroup *group;
+	double old;
+	gboolean moved;
+
+	g_assert (EEL_IS_CANVAS_GROUP (gobject));
+
+	item = EEL_CANVAS_ITEM (gobject);
+	group = EEL_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 |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		if (item->parent != NULL)
+			eel_canvas_item_request_update (item->parent);
+		else
+			eel_canvas_request_update (item->canvas);
+	}
+}
+
+/* Get_property handler for canvas groups */
+static void
+eel_canvas_group_get_property (GObject *gobject, guint param_id,
+				 GValue *value, GParamSpec *pspec)
+{
+	EelCanvasGroup *group;
+
+	g_assert (EEL_IS_CANVAS_GROUP (gobject));
+
+	group = EEL_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
+eel_canvas_group_destroy (GtkObject *object)
+{
+	EelCanvasGroup *group;
+	EelCanvasItem *child;
+	GList *list;
+
+	g_assert (EEL_IS_CANVAS_GROUP (object));
+
+	group = EEL_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
+eel_canvas_group_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
+{
+	EelCanvasGroup *group;
+	GList *list;
+	EelCanvasItem *i;
+	double bbox_x0, bbox_y0, bbox_x1, bbox_y1;
+	gboolean first = TRUE;
+
+	group = EEL_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;
+
+		eel_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
+eel_canvas_group_unrealize (EelCanvasItem *item)
+{
+	EelCanvasGroup *group;
+	GList *list;
+	EelCanvasItem *i;
+
+	group = EEL_CANVAS_GROUP (item);
+
+	/* Unmap group before children to avoid flash */
+	if (item->object.flags & EEL_CANVAS_ITEM_MAPPED)
+		(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & EEL_CANVAS_ITEM_REALIZED)
+			(* EEL_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
+	}
+
+	(* group_parent_class->unrealize) (item);
+}
+
+/* Map handler for canvas groups */
+static void
+eel_canvas_group_map (EelCanvasItem *item)
+{
+	EelCanvasGroup *group;
+	GList *list;
+	EelCanvasItem *i;
+
+	group = EEL_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & EEL_CANVAS_ITEM_VISIBLE &&
+		    !(i->object.flags & EEL_CANVAS_ITEM_MAPPED)) {
+			if (!(i->object.flags & EEL_CANVAS_ITEM_REALIZED))
+				(* EEL_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
+				
+			(* EEL_CANVAS_ITEM_GET_CLASS (i)->map) (i);
+		}
+	}
+
+	(* group_parent_class->map) (item);
+}
+
+/* Unmap handler for canvas groups */
+static void
+eel_canvas_group_unmap (EelCanvasItem *item)
+{
+	EelCanvasGroup *group;
+	GList *list;
+	EelCanvasItem *i;
+
+	group = EEL_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & EEL_CANVAS_ITEM_MAPPED)
+			(* EEL_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
+	}
+
+	(* group_parent_class->unmap) (item);
+}
+
+/* Draw handler for canvas groups */
+static void
+eel_canvas_group_draw (EelCanvasItem *item, GdkDrawable *drawable,
+			 GdkEventExpose *expose)
+{
+	EelCanvasGroup *group;
+	GList *list;
+	EelCanvasItem *child = NULL;
+
+	group = EEL_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if ((child->object.flags & EEL_CANVAS_ITEM_MAPPED) &&
+		    (EEL_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)
+				(* EEL_CANVAS_ITEM_GET_CLASS (child)->draw) (child, drawable, expose);
+		}
+	}
+}
+
+/* Point handler for canvas groups */
+static double
+eel_canvas_group_point (EelCanvasItem *item, double x, double y, int cx, int cy,
+			EelCanvasItem **actual_item)
+{
+	EelCanvasGroup *group;
+	GList *list;
+	EelCanvasItem *child, *point_item;
+	int x1, y1, x2, y2;
+	double gx, gy;
+	double dist, best;
+	int has_point;
+
+	group = EEL_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 & EEL_CANVAS_ITEM_MAPPED)
+		    && EEL_CANVAS_ITEM_GET_CLASS (child)->point) {
+			dist = eel_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;
+}
+
+static void
+eel_canvas_group_translate (EelCanvasItem *item, double dx, double dy)
+{
+        EelCanvasGroup *group;
+
+        group = EEL_CANVAS_GROUP (item);
+
+        group->xpos += dx;
+        group->ypos += dy;
+}
+
+/* Bounds handler for canvas groups */
+static void
+eel_canvas_group_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	EelCanvasGroup *group;
+	EelCanvasItem *child;
+	GList *list;
+	double tx1, ty1, tx2, ty2;
+	double minx, miny, maxx, maxy;
+	int set;
+
+	group = EEL_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 & EEL_CANVAS_ITEM_MAPPED) {
+			set = TRUE;
+			eel_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 & EEL_CANVAS_ITEM_MAPPED))
+			continue;
+
+		eel_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 (EelCanvasGroup *group, EelCanvasItem *item)
+{
+	g_object_ref (GTK_OBJECT (item));
+	gtk_object_sink (GTK_OBJECT (item));
+
+	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 & EEL_CANVAS_ITEM_VISIBLE &&
+	    group->item.object.flags & EEL_CANVAS_ITEM_MAPPED) {
+		if (!(item->object.flags & EEL_CANVAS_ITEM_REALIZED))
+			(* EEL_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+		
+		if (!(item->object.flags & EEL_CANVAS_ITEM_MAPPED))
+			(* EEL_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+	}
+}
+
+/* Removes an item from a group */
+static void
+group_remove (EelCanvasGroup *group, EelCanvasItem *item)
+{
+	GList *children;
+
+	g_assert (EEL_IS_CANVAS_GROUP (group));
+	g_assert (EEL_IS_CANVAS_ITEM (item));
+
+	for (children = group->item_list; children; children = children->next)
+		if (children->data == item) {
+			if (item->object.flags & EEL_CANVAS_ITEM_MAPPED)
+				(* EEL_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+			if (item->object.flags & EEL_CANVAS_ITEM_REALIZED)
+				(* EEL_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+			/* Unparent the child */
+
+			item->parent = NULL;
+			g_object_unref (GTK_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;
+		}
+}
+
+
+/*** EelCanvas ***/
+
+
+enum {
+	DRAW_BACKGROUND,
+	LAST_SIGNAL
+};
+
+static void eel_canvas_class_init          (EelCanvasClass *class);
+static void eel_canvas_init                (EelCanvas      *canvas);
+static void eel_canvas_destroy             (GtkObject        *object);
+static void eel_canvas_map                 (GtkWidget        *widget);
+static void eel_canvas_unmap               (GtkWidget        *widget);
+static void eel_canvas_realize             (GtkWidget        *widget);
+static void eel_canvas_unrealize           (GtkWidget        *widget);
+static void eel_canvas_size_allocate       (GtkWidget        *widget,
+					      GtkAllocation    *allocation);
+static gint eel_canvas_button              (GtkWidget        *widget,
+					      GdkEventButton   *event);
+static gint eel_canvas_motion              (GtkWidget        *widget,
+					      GdkEventMotion   *event);
+static gint eel_canvas_expose              (GtkWidget        *widget,
+					      GdkEventExpose   *event);
+static gint eel_canvas_key                 (GtkWidget        *widget,
+					      GdkEventKey      *event);
+static gint eel_canvas_crossing            (GtkWidget        *widget,
+					      GdkEventCrossing *event);
+static gint eel_canvas_focus_in            (GtkWidget        *widget,
+					      GdkEventFocus    *event);
+static gint eel_canvas_focus_out           (GtkWidget        *widget,
+					      GdkEventFocus    *event);
+static void eel_canvas_request_update_real (EelCanvas      *canvas);
+static void eel_canvas_draw_background     (EelCanvas      *canvas,
+					      int               x,
+					      int               y,
+					      int               width,
+					      int               height);
+
+
+static GtkLayoutClass *canvas_parent_class;
+
+static guint canvas_signals[LAST_SIGNAL];
+
+/**
+ * eel_canvas_get_type:
+ *
+ * Registers the &EelCanvas class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &EelCanvas class.
+ **/
+GType
+eel_canvas_get_type (void)
+{
+	static GType canvas_type = 0;
+
+	if (!canvas_type) {
+		const GTypeInfo canvas_info = {
+			sizeof (EelCanvasClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_canvas_class_init,
+			NULL,           /* class_finalize */
+			NULL,           /* class_data */
+			sizeof (EelCanvas),
+			0,              /* n_preallocs */
+			(GInstanceInitFunc) eel_canvas_init
+		};
+
+		canvas_type = g_type_register_static (gtk_layout_get_type (),
+						      "EelCanvas",
+						      &canvas_info,
+						      0);
+	}
+
+	return canvas_type;
+}
+
+static void
+eel_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
+eel_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
+eel_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
+eel_canvas_accessible_initialize (AtkObject *obj, 
+				  gpointer   data)
+{
+	EelCanvas *canvas;
+
+	if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize != NULL) 
+		ATK_OBJECT_CLASS (accessible_parent_class)->initialize (obj, data);
+
+	canvas = EEL_CANVAS (data);
+	g_signal_connect (canvas->layout.hadjustment,
+			  "value_changed",
+			  G_CALLBACK (eel_canvas_accessible_adjustment_changed),
+			  obj);
+	g_signal_connect (canvas->layout.vadjustment,
+			  "value_changed",
+			  G_CALLBACK (eel_canvas_accessible_adjustment_changed),
+			  obj);
+	
+	obj->role = ATK_ROLE_LAYERED_PANE;
+}
+
+static gint
+eel_canvas_accessible_get_n_children (AtkObject* obj)
+{
+ 	GtkAccessible *accessible;
+	GtkWidget *widget;
+	EelCanvas *canvas;
+	EelCanvasGroup *root_group;
+
+	accessible = GTK_ACCESSIBLE (obj);
+	widget = accessible->widget;
+	if (widget == NULL) {
+		/* State is defunct */
+		return 0;
+	}
+
+	g_assert (EEL_IS_CANVAS (widget));
+
+	canvas = EEL_CANVAS (widget);
+	root_group = eel_canvas_root (canvas);
+	g_assert (root_group != NULL);
+	return 1;
+}
+
+static AtkObject*
+eel_canvas_accessible_ref_child (AtkObject *obj,
+                                 gint       i)
+{
+	GtkAccessible *accessible;
+	GtkWidget *widget;
+	EelCanvas *canvas;
+	EelCanvasGroup *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 = EEL_CANVAS (widget);
+	root_group = eel_canvas_root (canvas);
+	g_assert (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
+eel_canvas_accessible_class_init (AtkObjectClass *klass)
+{
+ 	accessible_parent_class = g_type_class_peek_parent (klass);
+
+	klass->initialize = eel_canvas_accessible_initialize;
+	klass->get_n_children = eel_canvas_accessible_get_n_children;
+	klass->ref_child = eel_canvas_accessible_ref_child;
+}
+
+static GType
+eel_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) eel_canvas_accessible_class_init;
+		tinfo.class_size = query.class_size;
+		tinfo.instance_size = query.instance_size;
+		type = g_type_register_static (parent_atk_type,
+					       "EelCanvasAccessibility",
+					       &tinfo, 0);
+	}
+	return type;
+}
+
+static AtkObject *
+eel_canvas_accessible_create (GObject *for_object)
+{
+	GType type;
+	AtkObject *accessible;
+	EelCanvas *canvas;
+
+	canvas = EEL_CANVAS (for_object);
+	g_assert (canvas != NULL);
+
+	type = eel_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
+eel_canvas_accessible_factory_get_accessible_type (void)
+{
+	return eel_canvas_accessible_get_type ();
+}
+
+static AtkObject*
+eel_canvas_accessible_factory_create_accessible (GObject *obj)
+{
+	AtkObject *accessible;
+
+	g_assert (G_IS_OBJECT (obj));
+
+	accessible = eel_canvas_accessible_create (obj);
+
+	return accessible;
+}
+
+static void
+eel_canvas_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+	klass->create_accessible = eel_canvas_accessible_factory_create_accessible;
+	klass->get_accessible_type = eel_canvas_accessible_factory_get_accessible_type;
+}
+ 
+static GType
+eel_canvas_accessible_factory_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		const GTypeInfo tinfo = {
+			sizeof (AtkObjectFactoryClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_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,
+					       "EelCanvasAccessibilityFactory",
+					       &tinfo, 0);
+	}
+
+	return type;
+}
+
+
+/* Class initialization function for EelCanvasClass */
+static void
+eel_canvas_class_init (EelCanvasClass *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 = gtk_type_class (gtk_layout_get_type ());
+
+	gobject_class->set_property = eel_canvas_set_property;
+	gobject_class->get_property = eel_canvas_get_property;
+
+	object_class->destroy = eel_canvas_destroy;
+
+	widget_class->map = eel_canvas_map;
+	widget_class->unmap = eel_canvas_unmap;
+	widget_class->realize = eel_canvas_realize;
+	widget_class->unrealize = eel_canvas_unrealize;
+	widget_class->size_allocate = eel_canvas_size_allocate;
+	widget_class->button_press_event = eel_canvas_button;
+	widget_class->button_release_event = eel_canvas_button;
+	widget_class->motion_notify_event = eel_canvas_motion;
+	widget_class->expose_event = eel_canvas_expose;
+	widget_class->key_press_event = eel_canvas_key;
+	widget_class->key_release_event = eel_canvas_key;
+	widget_class->enter_notify_event = eel_canvas_crossing;
+	widget_class->leave_notify_event = eel_canvas_crossing;
+	widget_class->focus_in_event = eel_canvas_focus_in;
+	widget_class->focus_out_event = eel_canvas_focus_out;
+
+	klass->draw_background = eel_canvas_draw_background;
+	klass->request_update = eel_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 (EelCanvasClass, draw_background),
+			      NULL, NULL,
+			      eel_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 (),
+				       EEL_TYPE_CANVAS,
+				       eel_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 EelCanvas */
+static void
+eel_canvas_init (EelCanvas *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 = EEL_CANVAS_ITEM (g_object_new (eel_canvas_group_get_type (), NULL));
+	canvas->root->canvas = canvas;
+
+	g_object_ref (GTK_OBJECT (canvas->root));
+	gtk_object_sink (GTK_OBJECT (canvas->root));
+
+	canvas->root_destroy_id = g_signal_connect (GTK_OBJECT (canvas->root), "destroy",
+						    (GtkSignalFunc) 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 (EelCanvas *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 (EelCanvas *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 EelCanvas */
+static void
+eel_canvas_destroy (GtkObject *object)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (object));
+
+	/* remember, destroy can be run multiple times! */
+
+	canvas = EEL_CANVAS (object);
+
+	if (canvas->root_destroy_id) {
+		g_signal_handler_disconnect (GTK_OBJECT (canvas->root), canvas->root_destroy_id);
+		canvas->root_destroy_id = 0;
+	}
+	if (canvas->root) {
+		g_object_unref (GTK_OBJECT (canvas->root));
+		canvas->root = NULL;
+	}
+
+	shutdown_transients (canvas);
+
+	if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
+}
+
+/**
+ * eel_canvas_new:
+ * @void:
+ *
+ * Creates a new empty canvas.  If you wish to use the
+ * &EelCanvasImage 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 *
+eel_canvas_new (void)
+{
+	return GTK_WIDGET (g_object_new (eel_canvas_get_type (), NULL));
+}
+
+/* Map handler for the canvas */
+static void
+eel_canvas_map (GtkWidget *widget)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (widget));
+
+	/* Normal widget mapping stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
+
+	canvas = EEL_CANVAS (widget);
+
+	/* Map items */
+
+	if (canvas->root->object.flags & EEL_CANVAS_ITEM_VISIBLE &&
+	    !(canvas->root->object.flags & EEL_CANVAS_ITEM_MAPPED) &&
+	    EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
+		(* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
+}
+
+/* Unmap handler for the canvas */
+static void
+eel_canvas_unmap (GtkWidget *widget)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (widget));
+
+	canvas = EEL_CANVAS (widget);
+
+	shutdown_transients (canvas);
+
+	/* Unmap items */
+
+	if (EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
+		(* EEL_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
+eel_canvas_realize (GtkWidget *widget)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (widget));
+
+	/* Normal widget realization stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
+
+	canvas = EEL_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);
+
+	(* EEL_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
+}
+
+/* Unrealize handler for the canvas */
+static void
+eel_canvas_unrealize (GtkWidget *widget)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (widget));
+
+	canvas = EEL_CANVAS (widget);
+
+	shutdown_transients (canvas);
+
+	/* Unrealize items and parent widget */
+
+	(* EEL_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 (EelCanvas *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 & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+			canvas->root->object.flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+			eel_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 (GTK_OBJECT (canvas->layout.hadjustment), "value_changed");
+	if (changed_y)
+		g_signal_emit_by_name (GTK_OBJECT (canvas->layout.vadjustment), "value_changed");
+}
+
+/* Size allocation handler for the canvas */
+static void
+eel_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (widget));
+	g_assert (allocation != NULL);
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
+
+	canvas = EEL_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 (GTK_OBJECT (canvas->layout.hadjustment), "changed");
+	g_signal_emit_by_name (GTK_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 (EelCanvas *canvas, GdkEvent *event)
+{
+	GdkEvent ev;
+	gint finished;
+	EelCanvasItem *item;
+	EelCanvasItem *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:
+		eel_canvas_window_to_world (canvas,
+					      ev.crossing.x, ev.crossing.y,
+					      &ev.crossing.x, &ev.crossing.y);
+		break;
+
+	case GDK_MOTION_NOTIFY:
+                eel_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:
+                eel_canvas_window_to_world (canvas,
+                                              ev.motion.x, ev.motion.y,
+                                              &ev.motion.x, &ev.motion.y);
+                break;
+
+	case GDK_BUTTON_RELEASE:
+		eel_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 (
+		       GTK_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 (EelCanvas *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 */
+		eel_canvas_c2w (canvas, cx, cy, &x, &y);
+
+		/* find the closest item */
+		if (canvas->root->object.flags & EEL_CANVAS_ITEM_MAPPED)
+			eel_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;
+
+		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
+eel_canvas_button (GtkWidget *widget, GdkEventButton *event)
+{
+	EelCanvas *canvas;
+	int mask;
+	int retval;
+
+	g_assert (EEL_IS_CANVAS (widget));
+	g_assert (event != NULL);
+
+	retval = FALSE;
+
+	canvas = EEL_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
+eel_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (widget));
+	g_assert (event != NULL);
+
+	canvas = EEL_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
+eel_canvas_key (GtkWidget *widget, GdkEventKey *event)
+{
+	EelCanvas *canvas;
+	
+	g_assert (EEL_IS_CANVAS (widget));
+	g_assert (event != NULL);
+
+	canvas = EEL_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
+eel_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
+{
+	EelCanvas *canvas;
+
+	g_assert (EEL_IS_CANVAS (widget));
+	g_assert (event != NULL);
+
+	canvas = EEL_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
+eel_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+	EelCanvas *canvas;
+
+	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	canvas = EEL_CANVAS (widget);
+
+	if (canvas->focused_item)
+		return emit_event (canvas, (GdkEvent *) event);
+	else
+		return FALSE;
+}
+
+/* Focus out handler for the canvas */
+static gint
+eel_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+	EelCanvas *canvas;
+
+	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	canvas = EEL_CANVAS (widget);
+
+	if (canvas->focused_item)
+		return emit_event (canvas, (GdkEvent *) event);
+	else
+		return FALSE;
+}
+
+/* Expose handler for the canvas */
+static gint
+eel_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+	EelCanvas *canvas;
+
+	canvas = EEL_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_assert (!canvas->doing_update);
+
+		canvas->doing_update = TRUE;
+		eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+		g_assert (canvas->doing_update);
+
+		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 & EEL_CANVAS_ITEM_MAPPED)
+		(* EEL_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
+eel_canvas_draw_background (EelCanvas *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 (EelCanvas *canvas)
+{
+	/* Cause the update if necessary */
+
+update_again:
+	if (canvas->need_update) {
+		g_assert (!canvas->doing_update);
+
+		canvas->doing_update = TRUE;
+		eel_canvas_item_invoke_update (canvas->root, 0, 0, 0);
+
+		g_assert (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)
+{
+	EelCanvas *canvas;
+
+	GDK_THREADS_ENTER ();
+
+	canvas = EEL_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 (EelCanvas *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);
+	}
+}
+
+/**
+ * eel_canvas_root:
+ * @canvas: A canvas.
+ *
+ * Queries the root group of a canvas.
+ *
+ * Return value: The root group of the specified canvas.
+ **/
+EelCanvasGroup *
+eel_canvas_root (EelCanvas *canvas)
+{
+	g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
+
+	return EEL_CANVAS_GROUP (canvas->root);
+}
+
+
+/**
+ * eel_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
+eel_canvas_set_scroll_region (EelCanvas *canvas, double x1, double y1, double x2, double y2)
+{
+	double wxofs, wyofs;
+	int xofs, yofs;
+
+	g_return_if_fail (EEL_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.
+	 */
+
+	eel_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;
+
+	eel_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
+
+	scroll_to (canvas, xofs, yofs);
+
+	canvas->need_repick = TRUE;
+
+	if (!(canvas->root->object.flags & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+		canvas->root->object.flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		eel_canvas_request_update (canvas);
+	}
+}
+
+
+/**
+ * eel_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
+eel_canvas_get_scroll_region (EelCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
+{
+	g_return_if_fail (EEL_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
+eel_canvas_set_center_scroll_region (EelCanvas *canvas,
+				     gboolean center_scroll_region)
+{
+	g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+	canvas->center_scroll_region = center_scroll_region != 0;
+
+	scroll_to (canvas,
+		   canvas->layout.hadjustment->value,
+		   canvas->layout.vadjustment->value);
+}
+
+
+/**
+ * eel_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
+eel_canvas_set_pixels_per_unit (EelCanvas *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 (EEL_IS_CANVAS (canvas));
+	g_return_if_fail (n > EEL_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 & EEL_CANVAS_ITEM_NEED_DEEP_UPDATE)) {
+		canvas->root->object.flags |= EEL_CANVAS_ITEM_NEED_DEEP_UPDATE;
+		eel_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;
+}
+
+/**
+ * eel_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
+eel_canvas_scroll_to (EelCanvas *canvas, int cx, int cy)
+{
+	g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+	scroll_to (canvas, cx, cy);
+}
+
+/**
+ * eel_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
+eel_canvas_get_scroll_offsets (EelCanvas *canvas, int *cx, int *cy)
+{
+	g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+	if (cx)
+		*cx = canvas->layout.hadjustment->value;
+
+	if (cy)
+		*cy = canvas->layout.vadjustment->value;
+}
+
+/**
+ * eel_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
+eel_canvas_update_now (EelCanvas *canvas)
+{
+	g_return_if_fail (EEL_IS_CANVAS (canvas));
+
+	if (!(canvas->need_update || canvas->need_redraw))
+		return;
+	remove_idle (canvas);
+	do_update (canvas);
+}
+
+/**
+ * eel_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.
+ **/
+EelCanvasItem *
+eel_canvas_get_item_at (EelCanvas *canvas, double x, double y)
+{
+	EelCanvasItem *item;
+	double dist;
+	int cx, cy;
+
+	g_return_val_if_fail (EEL_IS_CANVAS (canvas), NULL);
+
+	eel_canvas_w2c (canvas, x, y, &cx, &cy);
+
+	dist = eel_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
+eel_canvas_request_update (EelCanvas *canvas)
+{
+	EEL_CANVAS_GET_CLASS (canvas)->request_update (canvas);
+}
+
+static void
+eel_canvas_request_update_real (EelCanvas *canvas)
+{
+	canvas->need_update = TRUE;
+	add_idle (canvas);
+}
+
+/**
+ * eel_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
+eel_canvas_request_redraw (EelCanvas *canvas, int x1, int y1, int x2, int y2)
+{
+	GdkRectangle bbox;
+
+	g_return_if_fail (EEL_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);
+}
+
+/**
+ * eel_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
+eel_canvas_w2c (EelCanvas *canvas, double wx, double wy, int *cx, int *cy)
+{
+	double zoom;
+
+	g_return_if_fail (EEL_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);
+}
+
+/**
+ * eel_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
+eel_canvas_w2c_rect_d (EelCanvas *canvas,
+			 double *x1, double *y1,
+			 double *x2, double *y2)
+{
+	eel_canvas_w2c_d (canvas,
+			    *x1, *y1,
+			    x1, y1);
+	eel_canvas_w2c_d (canvas,
+			    *x2, *y2,
+			    x2, y2);
+}
+
+
+
+/**
+ * eel_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
+ * returns coordinates in floating point coordinates, for greater precision.
+ **/
+void
+eel_canvas_w2c_d (EelCanvas *canvas, double wx, double wy, double *cx, double *cy)
+{
+	double zoom;
+
+	g_return_if_fail (EEL_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;
+}
+
+
+/**
+ * eel_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
+eel_canvas_c2w (EelCanvas *canvas, int cx, int cy, double *wx, double *wy)
+{
+	double zoom;
+
+	g_return_if_fail (EEL_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;
+}
+
+
+/**
+ * eel_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
+eel_canvas_window_to_world (EelCanvas *canvas, double winx, double winy,
+			      double *worldx, double *worldy)
+{
+	g_return_if_fail (EEL_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);
+}
+
+
+/**
+ * eel_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
+eel_canvas_world_to_window (EelCanvas *canvas, double worldx, double worldy,
+			    double *winx, double *winy)
+{
+	g_return_if_fail (EEL_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;
+}
+
+
+
+/**
+ * eel_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
+eel_canvas_get_color (EelCanvas *canvas, const char *spec, GdkColor *color)
+{
+	GdkColormap *colormap;
+
+	g_return_val_if_fail (EEL_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;
+}
+
+/**
+ * eel_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
+eel_canvas_get_color_pixel (EelCanvas *canvas, guint rgba)
+{
+	GdkColormap *colormap;
+	GdkColor color;
+
+	g_return_val_if_fail (EEL_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 */
+/**
+ * eel_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
+eel_canvas_set_stipple_origin (EelCanvas *canvas, GdkGC *gc)
+{
+	g_return_if_fail (EEL_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
+eel_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
+eel_canvas_item_accessible_get_item_extents (EelCanvasItem *item,
+                                             GdkRectangle  *rect)
+{
+ 	double bx1, bx2, by1, by2;
+	gint scroll_x, scroll_y;
+	gint x1, x2, y1, y2;
+
+	eel_canvas_item_get_bounds (item, &bx1, &by1, &bx2, &by2);
+	eel_canvas_w2c_rect_d (item->canvas, &bx1, &by1, &bx2, &by2);
+	eel_canvas_get_scroll_offsets (item->canvas, &scroll_x, &scroll_y);
+	x1 = floor (bx1);
+	y1 = floor (by1);
+	x2 = ceil (bx2);
+	y2 = ceil (by2);
+	rect->x = x1 - scroll_x;
+	rect->y = y1 - scroll_y;
+	rect->width = x2 - x1;
+	rect->height = y2 - y1;
+}
+
+static gboolean
+eel_canvas_item_accessible_is_item_in_window (EelCanvasItem *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
+eel_canvas_item_accessible_get_extents (AtkComponent *component,
+                                        gint		*x,
+                                        gint		*y,
+                                        gint		*width,
+                                        gint		*height,
+                                        AtkCoordType coord_type)
+{
+ 	AtkGObjectAccessible *atk_gobj;
+	GObject *obj;
+	EelCanvasItem *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 = EEL_CANVAS_ITEM (obj);
+
+	/* If this item has no parent canvas, something's broken */
+	g_assert (GTK_IS_WIDGET (item->canvas));
+
+	eel_canvas_item_accessible_get_item_extents (item, &rect);
+	*width = rect.width;
+	*height = rect.height;
+	if (!eel_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
+eel_canvas_item_accessible_get_mdi_zorder (AtkComponent *component)
+{
+	AtkGObjectAccessible *atk_gobj;
+	GObject *g_obj;
+	EelCanvasItem *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 = EEL_CANVAS_ITEM (g_obj);
+	if (item->parent) {
+       		return g_list_index (EEL_CANVAS_GROUP (item->parent)->item_list, item);
+	} else {
+		g_assert (item->canvas->root == item);
+		return 0;
+	}
+}
+
+static gboolean
+eel_canvas_item_accessible_grab_focus (AtkComponent *component)
+{
+ 	AtkGObjectAccessible *atk_gobj;
+	GObject *obj;
+	EelCanvasItem *item;
+	GtkWidget *toplevel;
+
+	atk_gobj = ATK_GOBJECT_ACCESSIBLE (component);
+	obj = atk_gobject_accessible_get_object (atk_gobj);
+
+	item = EEL_CANVAS_ITEM (obj);
+	if (item == NULL) {
+		/* item is defunct */
+		return FALSE;
+	}
+
+        eel_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
+eel_canvas_item_accessible_remove_focus_handler (AtkComponent *component,
+                                                 guint		handler_id)
+{
+ 	g_signal_handler_disconnect (component, handler_id);
+}
+
+static void
+eel_canvas_item_accessible_component_interface_init (AtkComponentIface *iface)
+{
+	g_assert (iface != NULL);
+
+	iface->add_focus_handler = eel_canvas_item_accessible_add_focus_handler;
+	iface->get_extents = eel_canvas_item_accessible_get_extents;
+	iface->get_mdi_zorder = eel_canvas_item_accessible_get_mdi_zorder;
+	iface->grab_focus = eel_canvas_item_accessible_grab_focus;
+      	iface->remove_focus_handler = eel_canvas_item_accessible_remove_focus_handler;
+}
+
+static gboolean
+eel_canvas_item_accessible_is_item_on_screen (EelCanvasItem *item)
+{
+	GdkRectangle rect;
+
+	eel_canvas_item_accessible_get_item_extents (item, &rect);
+	return eel_canvas_item_accessible_is_item_in_window (item, &rect);
+}
+
+static void
+eel_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*
+eel_canvas_item_accessible_ref_state_set (AtkObject *accessible)
+{
+ 	AtkGObjectAccessible *atk_gobj;
+	GObject *obj;
+ 	EelCanvasItem *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 = EEL_CANVAS_ITEM (obj);
+	if (item == NULL) {
+		atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT);
+	} else {
+                if (item->object.flags & EEL_CANVAS_ITEM_VISIBLE) {
+			atk_state_set_add_state (state_set, ATK_STATE_VISIBLE);
+			
+			if (eel_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
+eel_canvas_item_accessible_class_init (AtkObjectClass *klass)
+{
+ 	accessible_item_parent_class = g_type_class_peek_parent (klass);
+
+	klass->initialize = eel_canvas_item_accessible_initialize;
+	klass->ref_state_set = eel_canvas_item_accessible_ref_state_set;
+}
+
+static GType
+eel_canvas_item_accessible_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		const GInterfaceInfo atk_component_info = {
+			(GInterfaceInitFunc) eel_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) eel_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,
+					       "EelCanvasItemAccessibility",
+					       &tinfo, 0);
+
+		g_type_add_interface_static (type, ATK_TYPE_COMPONENT,
+					     &atk_component_info);
+
+	}
+
+	return type;
+}
+
+static AtkObject *
+eel_canvas_item_accessible_create (GObject *for_object)
+{
+	GType type;
+	AtkObject *accessible;
+	EelCanvasItem *item;
+
+	item = EEL_CANVAS_ITEM (for_object);
+	g_assert (item != NULL);
+
+	type = eel_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
+eel_canvas_item_accessible_factory_get_accessible_type (void)
+{
+	return eel_canvas_item_accessible_get_type ();
+}
+
+static AtkObject*
+eel_canvas_item_accessible_factory_create_accessible (GObject *obj)
+{
+	AtkObject *accessible;
+
+	g_assert (G_IS_OBJECT (obj));
+
+	accessible = eel_canvas_item_accessible_create (obj);
+
+	return accessible;
+}
+
+static void
+eel_canvas_item_accessible_factory_class_init (AtkObjectFactoryClass *klass)
+{
+	klass->create_accessible = eel_canvas_item_accessible_factory_create_accessible;
+	klass->get_accessible_type = eel_canvas_item_accessible_factory_get_accessible_type;
+}
+ 
+static GType
+eel_canvas_item_accessible_factory_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		const GTypeInfo tinfo = {
+			sizeof (AtkObjectFactoryClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_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,
+					       "EelCanvasItemAccessibilityFactory",
+					       &tinfo, 0);
+	}
+
+	return type;
+}
+
+/* Class initialization function for EelCanvasItemClass */
+static void
+eel_canvas_item_class_init (EelCanvasItemClass *class)
+{
+	GObjectClass *gobject_class;
+
+	gobject_class = (GObjectClass *) class;
+
+	item_parent_class = gtk_type_class (gtk_object_get_type ());
+
+	gobject_class->set_property = eel_canvas_item_set_property;
+	gobject_class->get_property = eel_canvas_item_get_property;
+
+	g_object_class_install_property
+		(gobject_class, ITEM_PROP_PARENT,
+		 g_param_spec_object ("parent", NULL, NULL,
+				      EEL_TYPE_CANVAS_ITEM,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+	
+	g_object_class_install_property
+		(gobject_class, ITEM_PROP_VISIBLE,
+		 g_param_spec_boolean ("visible", NULL, NULL,
+				      TRUE,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	item_signals[ITEM_EVENT] =
+		g_signal_new ("event",
+			      G_TYPE_FROM_CLASS (class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (EelCanvasItemClass, event),
+			      boolean_handled_accumulator, NULL,
+			      eel_marshal_BOOLEAN__BOXED,
+			      G_TYPE_BOOLEAN, 1,
+			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+	gobject_class->dispose = eel_canvas_item_dispose;
+
+	class->realize = eel_canvas_item_realize;
+	class->unrealize = eel_canvas_item_unrealize;
+	class->map = eel_canvas_item_map;
+	class->unmap = eel_canvas_item_unmap;
+	class->update = eel_canvas_item_update;
+
+	atk_registry_set_factory_type (atk_get_default_registry (),
+                                       EEL_TYPE_CANVAS_ITEM,
+                                       eel_canvas_item_accessible_factory_get_type ());
+}

Added: trunk/eel/eel-canvas.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-canvas.h	Mon Dec 15 15:56:41 2008
@@ -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@
+ */
+/* EelCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * EelCanvas 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 EEL_CANVAS_H
+#define EEL_CANVAS_H
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <stdarg.h>
+
+G_BEGIN_DECLS
+
+
+/* "Small" value used by canvas stuff */
+#define EEL_CANVAS_EPSILON 1e-10
+
+
+/* Macros for building colors that fit in a 32-bit integer.  The values are in
+ * [0, 255].
+ */
+
+#define EEL_CANVAS_COLOR(r, g, b) ((((int) (r) & 0xff) << 24)	\
+				     | (((int) (g) & 0xff) << 16)	\
+				     | (((int) (b) & 0xff) << 8)	\
+				     | 0xff)
+
+#define EEL_CANVAS_COLOR_A(r, g, b, a) ((((int) (r) & 0xff) << 24)	\
+					  | (((int) (g) & 0xff) << 16)	\
+					  | (((int) (b) & 0xff) << 8)	\
+					  | ((int) (a) & 0xff))
+
+
+typedef struct _EelCanvas           EelCanvas;
+typedef struct _EelCanvasClass      EelCanvasClass;
+typedef struct _EelCanvasItem       EelCanvasItem;
+typedef struct _EelCanvasItemClass  EelCanvasItemClass;
+typedef struct _EelCanvasGroup      EelCanvasGroup;
+typedef struct _EelCanvasGroupClass EelCanvasGroupClass;
+
+
+/* EelCanvasItem - base item class for canvas items
+ *
+ * All canvas items are derived from EelCanvasItem.  The only information a
+ * EelCanvasItem 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 EelCanvasItemGroup nodes
+ * and EelCanvasItem leaves.  Each canvas has a single root group, which can
+ * be obtained with the eel_canvas_get_root() function.
+ *
+ * The abstract EelCanvasItem class does not have any configurable or
+ * queryable attributes.
+ */
+
+/* Object flags for items */
+enum {
+	EEL_CANVAS_ITEM_REALIZED         = 1 << 4,
+	EEL_CANVAS_ITEM_MAPPED           = 1 << 5,
+	EEL_CANVAS_ITEM_ALWAYS_REDRAW    = 1 << 6,
+	EEL_CANVAS_ITEM_VISIBLE          = 1 << 7,
+	EEL_CANVAS_ITEM_NEED_UPDATE      = 1 << 8,
+	EEL_CANVAS_ITEM_NEED_DEEP_UPDATE = 1 << 9
+};
+
+/* Update flags for items */
+enum {
+	EEL_CANVAS_UPDATE_REQUESTED  = 1 << 0,
+	EEL_CANVAS_UPDATE_DEEP       = 1 << 1
+};
+
+#define EEL_TYPE_CANVAS_ITEM            (eel_canvas_item_get_type ())
+#define EEL_CANVAS_ITEM(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_CANVAS_ITEM, EelCanvasItem))
+#define EEL_CANVAS_ITEM_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS_ITEM, EelCanvasItemClass))
+#define EEL_IS_CANVAS_ITEM(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_CANVAS_ITEM))
+#define EEL_IS_CANVAS_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS_ITEM))
+#define EEL_CANVAS_ITEM_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), EEL_TYPE_CANVAS_ITEM, EelCanvasItemClass))
+
+
+struct _EelCanvasItem {
+	GtkObject object;
+
+	/* Parent canvas for this item */
+	EelCanvas *canvas;
+
+	/* Parent canvas group for this item (a EelCanvasGroup) */
+	EelCanvasItem *parent;
+
+	/* Bounding box for this item (in canvas coordinates) */
+	double x1, y1, x2, y2;
+};
+
+struct _EelCanvasItemClass {
+	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) (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
+
+	/* Realize an item -- create GCs, etc. */
+	void (* realize) (EelCanvasItem *item);
+
+	/* Unrealize an item */
+	void (* unrealize) (EelCanvasItem *item);
+
+	/* Map an item - normally only need by items with their own GdkWindows */
+	void (* map) (EelCanvasItem *item);
+
+	/* Unmap an item */
+	void (* unmap) (EelCanvasItem *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) (EelCanvasItem *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) (EelCanvasItem *item, double x, double y, int cx, int cy,
+			  EelCanvasItem **actual_item);
+
+	void (* translate) (EelCanvasItem *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) (EelCanvasItem *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)                (EelCanvasItem *item, GdkEvent *event);
+
+	/* Reserved for future expansion */
+	gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType eel_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.
+ */
+EelCanvasItem *eel_canvas_item_new (EelCanvasGroup *parent, GType type,
+				    const gchar *first_arg_name, ...);
+
+/* Constructors for use in derived classes and language wrappers */
+void eel_canvas_item_construct (EelCanvasItem *item, EelCanvasGroup *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 eel_canvas_item_set (EelCanvasItem *item, const gchar *first_arg_name, ...);
+
+/* Used only for language wrappers and the like */
+void eel_canvas_item_set_valist (EelCanvasItem *item,
+				 const gchar *first_arg_name, va_list args);
+
+/* Move an item by the specified amount */
+void eel_canvas_item_move (EelCanvasItem *item, double dx, double dy);
+
+/* Raise an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void eel_canvas_item_raise (EelCanvasItem *item, int positions);
+
+/* Lower an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void eel_canvas_item_lower (EelCanvasItem *item, int positions);
+
+/* Raise an item to the top of its parent group's z-order. */
+void eel_canvas_item_raise_to_top (EelCanvasItem *item);
+
+/* Lower an item to the bottom of its parent group's z-order */
+void eel_canvas_item_lower_to_bottom (EelCanvasItem *item);
+
+/* Send an item behind another item */
+void eel_canvas_item_send_behind (EelCanvasItem *item,
+				  EelCanvasItem *behind_item);
+
+
+/* Show an item (make it visible).  If the item is already shown, it has no
+ * effect.
+ */
+void eel_canvas_item_show (EelCanvasItem *item);
+
+/* Hide an item (make it invisible).  If the item is already invisible, it has
+ * no effect.
+ */
+void eel_canvas_item_hide (EelCanvasItem *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 eel_canvas_item_grab (EelCanvasItem *item, unsigned int event_mask,
+			  GdkCursor *cursor, guint32 etime);
+
+/* Ungrabs the mouse -- the specified item must be the same that was passed to
+ * eel_canvas_item_grab().  Time is a proper X event time parameter.
+ */
+void eel_canvas_item_ungrab (EelCanvasItem *item, guint32 etime);
+
+/* These functions convert from a coordinate system to another.  "w" is world
+ * coordinates and "i" is item coordinates.
+ */
+void eel_canvas_item_w2i (EelCanvasItem *item, double *x, double *y);
+void eel_canvas_item_i2w (EelCanvasItem *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 eel_canvas_item_reparent (EelCanvasItem *item, EelCanvasGroup *new_group);
+
+/* Used to send all of the keystroke events to a specific item as well as
+ * GDK_FOCUS_CHANGE events.
+ */
+void eel_canvas_item_grab_focus (EelCanvasItem *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 eel_canvas_item_get_bounds (EelCanvasItem *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 eel_canvas_item_request_update (EelCanvasItem *item);
+
+/* Request a redraw of the bounding box of the canvas item */
+void eel_canvas_item_request_redraw (EelCanvasItem *item);
+
+/* EelCanvasGroup - 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 eel_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 EEL_TYPE_CANVAS_GROUP            (eel_canvas_group_get_type ())
+#define EEL_CANVAS_GROUP(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_CANVAS_GROUP, EelCanvasGroup))
+#define EEL_CANVAS_GROUP_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS_GROUP, EelCanvasGroupClass))
+#define EEL_IS_CANVAS_GROUP(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_CANVAS_GROUP))
+#define EEL_IS_CANVAS_GROUP_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS_GROUP))
+#define EEL_CANVAS_GROUP_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), EEL_TYPE_CANVAS_GROUP, EelCanvasGroupClass))
+
+
+struct _EelCanvasGroup {
+	EelCanvasItem item;
+
+	double xpos, ypos;
+	
+	/* Children of the group */
+	GList *item_list;
+	GList *item_list_end;
+};
+
+struct _EelCanvasGroupClass {
+	EelCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType eel_canvas_group_get_type (void) G_GNUC_CONST;
+
+
+/*** EelCanvas ***/
+
+
+#define EEL_TYPE_CANVAS            (eel_canvas_get_type ())
+#define EEL_CANVAS(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_CANVAS, EelCanvas))
+#define EEL_CANVAS_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_CANVAS, EelCanvasClass))
+#define EEL_IS_CANVAS(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_CANVAS))
+#define EEL_IS_CANVAS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_CANVAS))
+#define EEL_CANVAS_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), EEL_TYPE_CANVAS, EelCanvasClass))
+
+
+struct _EelCanvas {
+	GtkLayout layout;
+
+	/* Root canvas group */
+	EelCanvasItem *root;
+
+	/* The item containing the mouse pointer, or NULL if none */
+	EelCanvasItem *current_item;
+
+	/* Item that is about to become current (used to track deletions and such) */
+	EelCanvasItem *new_current_item;
+
+	/* Item that holds a pointer grab, or NULL if none */
+	EelCanvasItem *grabbed_item;
+
+	/* If non-NULL, the currently focused item */
+	EelCanvasItem *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 _EelCanvasClass {
+	GtkLayoutClass parent_class;
+
+	/* Draw the background for the area given.
+	 */
+	void (* draw_background) (EelCanvas *canvas,
+				  int x, int y, int width, int height);
+
+	/* Private Virtual methods for groping the canvas inside bonobo */
+	void (* request_update) (EelCanvas *canvas);
+
+	/* Reserved for future expansion */
+	gpointer spare_vmethods [4];
+};
+
+
+/* Standard Gtk function */
+GType eel_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 eel_canvas_set_scroll_region() soon after calling this
+ * function to set the desired scrolling limits for the canvas.
+ */
+GtkWidget *eel_canvas_new (void);
+
+/* Returns the root canvas item group of the canvas */
+EelCanvasGroup *eel_canvas_root (EelCanvas *canvas);
+
+/* Sets the limits of the scrolling region, in world coordinates */
+void eel_canvas_set_scroll_region (EelCanvas *canvas,
+				   double x1, double y1, double x2, double y2);
+
+/* Gets the limits of the scrolling region, in world coordinates */
+void eel_canvas_get_scroll_region (EelCanvas *canvas,
+				   double *x1, double *y1, double *x2, double *y2);
+
+/* Sets the number of pixels that correspond to one unit in world coordinates */
+void eel_canvas_set_pixels_per_unit (EelCanvas *canvas, double n);
+
+/* Wether the canvas centers the scroll region if it is smaller than the window  */
+void eel_canvas_set_center_scroll_region (EelCanvas *canvas, gboolean center_scroll_region);
+
+/* Scrolls the canvas to the specified offsets, given in canvas pixel coordinates */
+void eel_canvas_scroll_to (EelCanvas *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 eel_canvas_get_scroll_offsets (EelCanvas *canvas, int *cx, int *cy);
+
+/* Requests that the canvas be repainted immediately instead of in the idle
+ * loop.
+ */
+void eel_canvas_update_now (EelCanvas *canvas);
+
+/* Returns the item that is at the specified position in world coordinates, or
+ * NULL if no item is there.
+ */
+EelCanvasItem *eel_canvas_get_item_at (EelCanvas *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 eel_canvas_request_redraw (EelCanvas *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 eel_canvas_w2c_rect_d (EelCanvas *canvas,
+			    double *x1, double *y1,
+			    double *x2, double *y2);
+void eel_canvas_w2c (EelCanvas *canvas, double wx, double wy, int *cx, int *cy);
+void eel_canvas_w2c_d (EelCanvas *canvas, double wx, double wy, double *cx, double *cy);
+void eel_canvas_c2w (EelCanvas *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 eel_canvas_window_to_world (EelCanvas *canvas,
+				 double winx, double winy, double *worldx, double *worldy);
+
+/* This is the inverse of eel_canvas_window_to_world() */
+void eel_canvas_world_to_window (EelCanvas *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 eel_canvas_get_color (EelCanvas *canvas, const char *spec, GdkColor *color);
+
+/* Allocates a color from the RGB value passed into this function. */
+gulong eel_canvas_get_color_pixel (EelCanvas *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 eel_canvas_set_stipple_origin (EelCanvas *canvas, GdkGC *gc);
+
+G_END_DECLS
+
+#endif

Added: trunk/eel/eel-debug-drawing.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-debug-drawing.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,527 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-debug-drawing.c: Eel drawing debugging aids.
+ 
+   Copyright (C) 2000 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-debug-drawing.h"
+
+#include "eel-art-gtk-extensions.h"
+#include "eel-debug.h"
+#include "eel-gdk-extensions.h"
+#include "eel-gdk-extensions.h"
+#include "eel-gdk-pixbuf-extensions.h"
+#include "eel-gtk-extensions.h"
+#include "eel-gtk-extensions.h"
+#include "eel-gtk-macros.h"
+
+#include <gtk/gtk.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/*
+ * PixbufViewer is a very simple "private" widget that displays
+ * a GdkPixbuf.  It is used by eel_debug_show_pixbuf() to
+ * display a pixbuf in an in process pixbuf debugging window.
+ *
+ * We cant use EelImage for this because part of the reason
+ * for pixbuf debugging is to debug EelImage itself.
+ */
+#define DEBUG_TYPE_PIXBUF_VIEWER (debug_pixbuf_viewer_get_type ())
+#define DEBUG_PIXBUF_VIEWER(obj) (GTK_CHECK_CAST ((obj), DEBUG_TYPE_PIXBUF_VIEWER, DebugPixbufViewer))
+#define DEBUG_IS_PIXBUF_VIEWER(obj) (GTK_CHECK_TYPE ((obj), DEBUG_TYPE_PIXBUF_VIEWER))
+
+typedef struct DebugPixbufViewer DebugPixbufViewer;
+typedef struct DebugPixbufViewerClass DebugPixbufViewerClass;
+
+static GtkType debug_pixbuf_viewer_get_type   (void);
+static void    debug_pixbuf_viewer_set_pixbuf (DebugPixbufViewer *viewer,
+					       GdkPixbuf         *pixbuf);
+
+struct DebugPixbufViewer
+{
+	GtkWidget widget;
+	GdkPixbuf *pixbuf;
+};
+
+struct DebugPixbufViewerClass
+{
+	GtkWidgetClass parent_class;
+};
+
+/* GtkObjectClass methods */
+static void debug_pixbuf_viewer_class_init (DebugPixbufViewerClass *pixbuf_viewer_class);
+static void debug_pixbuf_viewer_init       (DebugPixbufViewer      *pixbuf_viewer);
+static void debug_pixbuf_viewer_finalize         (GObject                *object);
+
+/* GtkWidgetClass methods */
+static void debug_pixbuf_viewer_size_request     (GtkWidget              *widget,
+						  GtkRequisition         *requisition);
+static int  debug_pixbuf_viewer_expose_event     (GtkWidget              *widget,
+						  GdkEventExpose         *event);
+
+EEL_CLASS_BOILERPLATE (DebugPixbufViewer, debug_pixbuf_viewer, GTK_TYPE_WIDGET)
+
+static void
+debug_pixbuf_viewer_class_init (DebugPixbufViewerClass *pixbuf_viewer_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (pixbuf_viewer_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (pixbuf_viewer_class);
+
+	object_class->finalize = debug_pixbuf_viewer_finalize;
+	widget_class->size_request = debug_pixbuf_viewer_size_request;
+	widget_class->expose_event = debug_pixbuf_viewer_expose_event;
+}
+
+static void
+debug_pixbuf_viewer_init (DebugPixbufViewer *viewer)
+{
+	GTK_WIDGET_UNSET_FLAGS (viewer, GTK_CAN_FOCUS);
+	GTK_WIDGET_SET_FLAGS (viewer, GTK_NO_WINDOW);
+}
+
+static void
+debug_pixbuf_viewer_finalize (GObject *object)
+{
+ 	DebugPixbufViewer *viewer;
+
+	viewer = DEBUG_PIXBUF_VIEWER (object);
+	eel_gdk_pixbuf_unref_if_not_null (viewer->pixbuf);
+	viewer->pixbuf = NULL;
+
+	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static void
+debug_pixbuf_viewer_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+	DebugPixbufViewer *viewer;
+	EelDimensions dimensions;
+
+	g_assert (DEBUG_IS_PIXBUF_VIEWER (widget));
+	g_assert (requisition != NULL);
+	
+	viewer = DEBUG_PIXBUF_VIEWER (widget);
+
+	if (viewer->pixbuf != NULL) {
+		dimensions = eel_gdk_pixbuf_get_dimensions (viewer->pixbuf);
+	} else {
+		dimensions = eel_dimensions_empty;
+	}
+	
+   	requisition->width = MAX (2, dimensions.width);
+   	requisition->height = MAX (2, dimensions.height);
+}
+
+static int
+debug_pixbuf_viewer_expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ 	DebugPixbufViewer *viewer;
+	EelIRect clipped_dirty_area;
+	EelIRect dirty_area;
+	EelIRect bounds;
+
+	g_assert (DEBUG_IS_PIXBUF_VIEWER (widget));
+	g_assert (event != NULL);
+	g_assert (event->window == widget->window);
+	g_assert (GTK_WIDGET_REALIZED (widget));
+	
+ 	viewer = DEBUG_PIXBUF_VIEWER (widget);
+
+	if (viewer->pixbuf == NULL) {
+		return TRUE;
+	}
+	
+	bounds.x0 = widget->allocation.x + (widget->allocation.width - gdk_pixbuf_get_width (viewer->pixbuf)) / 2;
+	bounds.y0 = widget->allocation.y + (widget->allocation.height - gdk_pixbuf_get_height (viewer->pixbuf)) / 2;
+	bounds.x1 = bounds.x0 + gdk_pixbuf_get_width (viewer->pixbuf);
+	bounds.y1 = bounds.y0 + gdk_pixbuf_get_height (viewer->pixbuf);
+	
+	/* Clip the dirty area to the screen; bail if no work to do */
+	dirty_area = eel_gdk_rectangle_to_eel_irect (event->area);
+	clipped_dirty_area = eel_gdk_window_clip_dirty_area_to_screen (event->window,
+								       dirty_area);
+	if (!eel_irect_is_empty (&clipped_dirty_area)) {
+		EelIRect clipped_bounds;
+
+		eel_irect_intersect (&clipped_bounds, &bounds, &clipped_dirty_area);
+
+		if (!eel_irect_is_empty (&clipped_bounds)) {
+			g_assert (clipped_bounds.x0 >= bounds.x0);
+			g_assert (clipped_bounds.y0 >= bounds.y0);
+			
+			eel_gdk_pixbuf_draw_to_drawable (viewer->pixbuf,
+							 event->window,
+							 widget->style->white_gc,
+							 clipped_bounds.x0 - bounds.x0,
+							 clipped_bounds.y0 - bounds.y0,
+							 clipped_bounds,
+							 GDK_RGB_DITHER_NONE,
+							 GDK_PIXBUF_ALPHA_BILEVEL,
+							 EEL_STANDARD_ALPHA_THRESHHOLD);
+		}
+	}
+
+	bounds.x0 -= 1;
+	bounds.y0 -= 1;
+	bounds.x1 += 1;
+	bounds.y1 += 1;
+
+	return TRUE;
+}
+
+static void
+debug_pixbuf_viewer_set_pixbuf (DebugPixbufViewer *viewer, GdkPixbuf *pixbuf)
+{
+	g_assert (DEBUG_IS_PIXBUF_VIEWER (viewer));
+	
+	if (pixbuf != viewer->pixbuf) {
+		eel_gdk_pixbuf_unref_if_not_null (viewer->pixbuf);
+		eel_gdk_pixbuf_ref_if_not_null (pixbuf);
+		viewer->pixbuf = pixbuf;
+		gtk_widget_queue_resize (GTK_WIDGET (viewer));
+	}
+}
+
+/**
+ * eel_debug_draw_rectangle_and_cross:
+ * @rectangle: Rectangle bounding the rectangle.
+ * @color: Color to use for the rectangle and cross.
+ *
+ * Draw a rectangle and cross.  Useful for debugging exposure events.
+ */
+void
+eel_debug_draw_rectangle_and_cross (GdkDrawable *drawable,
+				    EelIRect rectangle,
+				    guint32 color,
+				    gboolean draw_cross)
+{
+	GdkGC *gc;
+	GdkColor color_gdk = { 0 };
+
+	int width;
+	int height;
+
+	g_return_if_fail (drawable != NULL);
+	g_return_if_fail (!eel_irect_is_empty (&rectangle));
+
+	width = rectangle.x1 - rectangle.x0;
+	height = rectangle.y1 - rectangle.y0;
+
+ 	gc = gdk_gc_new (drawable);
+ 	gdk_gc_set_function (gc, GDK_COPY);
+	
+	color_gdk.red   = ((color >> 16) & 0xff) << 8;
+	color_gdk.green = ((color >>  8) & 0xff) << 8;
+	color_gdk.blue  = ((color      ) & 0xff) << 8;
+	gdk_colormap_alloc_color (
+		gdk_drawable_get_colormap (drawable),
+		&color_gdk, FALSE, FALSE);
+	gdk_gc_set_rgb_fg_color (gc, &color_gdk);
+	
+	gdk_draw_rectangle (drawable,
+			    gc,
+			    FALSE,
+			    rectangle.x0,
+			    rectangle.y0,
+			    width - 1,
+			    height - 1);
+
+	if (draw_cross) {
+		gdk_draw_line (drawable,
+			       gc,
+			       rectangle.x0,
+			       rectangle.y0,
+			       rectangle.x0 + width - 1,
+			       rectangle.y0 + height - 1);
+		
+		gdk_draw_line (drawable,
+			       gc,
+			       rectangle.x0 + width - 1,
+			       rectangle.y0,
+			       rectangle.x0,
+			       rectangle.y0 + height - 1);
+	}
+
+	g_object_unref (gc);
+}
+
+/**
+ * eel_debug_show_pixbuf_in_external_viewer:
+ * @pixbuf: Pixbuf to show.
+ * @viewer_name: Viewer name.
+ *
+ * Show the given pixbuf in an external out of process viewer.
+ * This is very useful for debugging pixbuf stuff.
+ *
+ * Perhaps this function should be #ifdef DEBUG or something like that.
+ */
+void
+eel_debug_show_pixbuf_in_external_viewer (const GdkPixbuf *pixbuf,
+					  const char *viewer_name)
+{
+	char *command;
+	char *file_name;
+	gboolean save_result;
+	int ignore;
+
+	g_return_if_fail (pixbuf != NULL);
+	g_return_if_fail (viewer_name != NULL);
+
+	file_name = g_strdup ("/tmp/eel-debug-png-file-XXXXXX");
+
+	if (mktemp (file_name) != file_name) {
+		g_free (file_name);
+		file_name = g_strdup_printf ("/tmp/isis-debug-png-file-%d", getpid ());
+	}
+
+	save_result = eel_gdk_pixbuf_save_to_file (pixbuf, file_name);
+
+	if (save_result == FALSE) {
+		g_warning ("Failed to save '%s'", file_name);
+		g_free (file_name);
+		return;
+	}
+	
+	command = g_strdup_printf ("%s %s", viewer_name, file_name);
+
+	ignore = system (command);
+	g_free (command);
+	remove (file_name);
+	g_free (file_name);
+}
+
+static GtkWidget *debug_window = NULL;
+static GtkWidget *debug_image = NULL;
+
+static void
+debug_delete_event (GtkWidget *widget, GdkEvent *event, gpointer callback_data)
+{
+	gtk_widget_hide (widget);
+}
+
+static void
+destroy_debug_window (void)
+{
+	if (debug_window != NULL) {
+		gtk_widget_destroy (debug_window);
+		debug_window = NULL;
+	}
+}
+
+/**
+ * eel_debug_show_pixbuf_in:
+ * @pixbuf: Pixbuf to show.
+ *
+ * Show the given pixbuf in an in process window.
+ */
+void
+eel_debug_show_pixbuf (GdkPixbuf *pixbuf)
+{
+	if (debug_window == NULL) {
+		GtkWidget *vbox;
+		
+		debug_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+		vbox = gtk_vbox_new (FALSE, 0);
+		gtk_container_add (GTK_CONTAINER (debug_window), vbox);
+		gtk_window_set_title (GTK_WINDOW (debug_window), "Pixbuf debugging");
+		gtk_window_set_resizable (GTK_WINDOW (debug_window), TRUE);
+		gtk_container_set_border_width (GTK_CONTAINER (debug_window), 10);
+		g_signal_connect (debug_window, "delete_event", G_CALLBACK (debug_delete_event), NULL);
+		
+		debug_image = gtk_widget_new (debug_pixbuf_viewer_get_type (), NULL);
+		
+		gtk_box_pack_start (GTK_BOX (vbox), debug_image, TRUE, TRUE, 0);
+
+		eel_gtk_widget_set_background_color (debug_window, "white");
+
+		eel_debug_call_at_shutdown (destroy_debug_window);
+		
+		gtk_widget_show (debug_image);
+		gtk_widget_show (vbox);
+	}
+
+	gtk_widget_show (debug_window);
+	debug_pixbuf_viewer_set_pixbuf (DEBUG_PIXBUF_VIEWER (debug_image), pixbuf);
+
+	gdk_window_clear_area_e (debug_window->window, 0, 0, -1, -1);
+}
+
+void
+eel_debug_pixbuf_draw_point (GdkPixbuf *pixbuf,
+			     int x,
+			     int y,
+			     guint32 color,
+			     int opacity)
+{
+	EelDimensions dimensions;
+	guchar *pixels;
+	gboolean has_alpha;
+	guint pixel_offset;
+	guint rowstride;
+	guchar red;
+	guchar green;
+	guchar blue;
+	guchar alpha;
+	guchar *offset;
+
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf));
+	g_return_if_fail (opacity >= EEL_OPACITY_FULLY_TRANSPARENT);
+	g_return_if_fail (opacity <= EEL_OPACITY_FULLY_OPAQUE);
+
+	dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf);
+
+	g_return_if_fail (x >= 0 && x < dimensions.width);
+	g_return_if_fail (y >= 0 && y < dimensions.height);
+
+	pixels = gdk_pixbuf_get_pixels (pixbuf);
+	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+	pixel_offset = has_alpha ? 4 : 3;
+
+	red = EEL_RGBA_COLOR_GET_R (color);
+	green = EEL_RGBA_COLOR_GET_G (color);
+	blue = EEL_RGBA_COLOR_GET_B (color);
+	alpha = (guchar) opacity;
+
+	offset = pixels + y * rowstride + x * pixel_offset;
+
+	*(offset + 0) = red;
+	*(offset + 1) = green;
+	*(offset + 2) = blue;
+	
+	if (has_alpha) {
+		*(offset + 3) = alpha;
+	}
+}
+
+void
+eel_debug_pixbuf_draw_rectangle (GdkPixbuf *pixbuf,
+				 gboolean filled,
+				 int x0,
+				 int y0,
+				 int x1,
+				 int y1,
+				 guint32 color,
+				 int opacity)
+{
+	EelDimensions dimensions;
+	int x;
+	int y;
+
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf));
+	g_return_if_fail (opacity >= EEL_OPACITY_FULLY_TRANSPARENT);
+	g_return_if_fail (opacity <= EEL_OPACITY_FULLY_OPAQUE);
+
+	dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf);
+
+	if (x0 == -1) {
+		x0 = 0;
+	}
+
+	if (y0 == -1) {
+		y0 = 0;
+	}
+
+	if (x1 == -1) {
+		x1 = dimensions.width - 1;
+	}
+
+	if (y1 == -1) {
+		y1 = dimensions.height - 1;
+	}
+
+	g_return_if_fail (x1 > x0);
+	g_return_if_fail (y1 > y0);
+	g_return_if_fail (x0 >= 0 && x0 < dimensions.width);
+	g_return_if_fail (y0 >= 0 && y0 < dimensions.height);
+	g_return_if_fail (x1 >= 0 && x1 < dimensions.width);
+	g_return_if_fail (y1 >= 0 && y1 < dimensions.height);
+
+	if (filled) {
+		for (y = y0; y <= y1; y++) {
+			for (x = x0; x <= x1; x++) {
+				eel_debug_pixbuf_draw_point (pixbuf, x, y, color, opacity);
+			}
+		}
+	} else {
+		/* Top / Bottom */
+		for (x = x0; x <= x1; x++) {
+			eel_debug_pixbuf_draw_point (pixbuf, x, y0, color, opacity);
+			eel_debug_pixbuf_draw_point (pixbuf, x, y1, color, opacity);
+		}
+		
+		/* Left / Right */
+		for (y = y0; y <= y1; y++) {
+			eel_debug_pixbuf_draw_point (pixbuf, x0, y, color, opacity);
+			eel_debug_pixbuf_draw_point (pixbuf, x1, y, color, opacity);
+		}
+	}
+}
+
+void
+eel_debug_pixbuf_draw_rectangle_inset (GdkPixbuf *pixbuf,
+				       gboolean filled,
+				       int x0,
+				       int y0,
+				       int x1,
+				       int y1,
+				       guint32 color,
+				       int opacity,
+				       int inset)
+{
+	EelDimensions dimensions;
+	
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf));
+	g_return_if_fail (opacity >= EEL_OPACITY_FULLY_TRANSPARENT);
+	g_return_if_fail (opacity <= EEL_OPACITY_FULLY_OPAQUE);
+
+	dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf);
+
+	if (x0 == -1) {
+		x0 = 0;
+	}
+
+	if (y0 == -1) {
+		y0 = 0;
+	}
+
+	if (x1 == -1) {
+		x1 = dimensions.width - 1;
+	}
+
+	if (y1 == -1) {
+		y1 = dimensions.height - 1;
+	}
+
+	x0 += inset;
+	y0 += inset;
+	x1 -= inset;
+	y1 -= inset;
+
+	g_return_if_fail (x1 > x0);
+	g_return_if_fail (y1 > y0);
+
+	eel_debug_pixbuf_draw_rectangle (pixbuf, filled, x0, y0, x1, y1, color, opacity);
+}

Added: trunk/eel/eel-debug-drawing.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-debug-drawing.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,72 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-debug-drawing.h: Eel drawing debugging aids.
+ 
+   Copyright (C) 2000 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_DEBUG_DRAWING_H
+#define EEL_DEBUG_DRAWING_H
+
+#include <eel/eel-gdk-pixbuf-extensions.h>
+
+/* Draw a rectangle and cross on the given window */
+void eel_debug_draw_rectangle_and_cross       (GdkDrawable     *drawable,
+					       EelIRect         rectangle,
+					       guint32          color,
+					       gboolean         draw_cross);
+
+/* Show the given pixbuf in an external out of process viewer */
+void eel_debug_show_pixbuf_in_external_viewer (const GdkPixbuf *pixbuf,
+					       const char      *viewer_name);
+
+/* Show the given pixbuf in an in process window */
+void eel_debug_show_pixbuf                    (GdkPixbuf *pixbuf);
+
+/* Draw a point in a pixbuf */
+void eel_debug_pixbuf_draw_point              (GdkPixbuf       *pixbuf,
+					       int              x,
+					       int              y,
+					       guint32          color,
+					       int              opacity);
+/* Draw a rectangle in a pixbuf.  The coordinates (-1,-1( (-1,-1) will use
+ * the whole pixbuf. */
+void eel_debug_pixbuf_draw_rectangle          (GdkPixbuf       *pixbuf,
+					       gboolean         filled,
+					       int              x0,
+					       int              y0,
+					       int              x1,
+					       int              y1,
+					       guint32          color,
+					       int              opacity);
+/* Draw an inset rectangle in a pixbuf.  Positive inset make the rectangle
+ * smaller.  Negative inset makes it larger.
+ */
+void eel_debug_pixbuf_draw_rectangle_inset    (GdkPixbuf       *pixbuf,
+					       gboolean         filled,
+					       int              x0,
+					       int              y0,
+					       int              x1,
+					       int              y1,
+					       guint32          color,
+					       int              opacity,
+					       int              inset);
+
+#endif /* EEL_DEBUG_DRAWING_H */

Added: trunk/eel/eel-debug.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-debug.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,129 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-debug.c: Eel debugging aids.
+ 
+   Copyright (C) 2000, 2001 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+#include "eel-debug.h"
+
+#include <glib.h>
+#include <signal.h>
+#include <stdio.h>
+
+typedef struct {
+	gpointer data;
+	GFreeFunc function;
+} ShutdownFunction;
+
+static GList *shutdown_functions;
+
+/* Raise a SIGINT signal to get the attention of the debugger.
+ * When not running under the debugger, we don't want to stop,
+ * so we ignore the signal for just the moment that we raise it.
+ */
+void
+eel_stop_in_debugger (void)
+{
+	void (* saved_handler) (int);
+
+	saved_handler = signal (SIGINT, SIG_IGN);
+	raise (SIGINT);
+	signal (SIGINT, saved_handler);
+}
+
+/* Stop in the debugger after running the default log handler.
+ * This makes certain kinds of messages stop in the debugger
+ * without making them fatal (you can continue).
+ */
+static void
+log_handler (const char *domain,
+	     GLogLevelFlags level,
+	     const char *message,
+	     gpointer data)
+{
+	g_log_default_handler (domain, level, message, data);
+	if ((level & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) != 0) {
+		eel_stop_in_debugger ();
+	}
+}
+
+void
+eel_make_warnings_and_criticals_stop_in_debugger (void)
+{
+	g_log_set_default_handler (log_handler, NULL);
+}
+
+int 
+eel_get_available_file_descriptor_count (void)
+{
+	int count;
+	GList *list;
+	GList *p;
+	FILE *file;
+
+	list = NULL;
+	for (count = 0; ; count++) {
+		file = fopen ("/dev/null", "r");
+		if (file == NULL) {
+			break;
+		}
+		list = g_list_prepend (list, file);
+	}
+
+	for (p = list; p != NULL; p = p->next) {
+		fclose (p->data);
+	}
+	g_list_free (list);
+
+	return count;
+}
+
+void
+eel_debug_shut_down (void)
+{
+	ShutdownFunction *f;
+
+	while (shutdown_functions != NULL) {
+		f = shutdown_functions->data;
+		shutdown_functions = g_list_remove (shutdown_functions, f);
+		
+		f->function (f->data);
+		g_free (f);
+	}
+}
+
+void
+eel_debug_call_at_shutdown (EelFunction function)
+{
+	eel_debug_call_at_shutdown_with_data ((GFreeFunc) function, NULL);
+}
+
+void
+eel_debug_call_at_shutdown_with_data (GFreeFunc function, gpointer data)
+{
+	ShutdownFunction *f;
+
+	f = g_new (ShutdownFunction, 1);
+	f->data = data;
+	f->function = function;
+	shutdown_functions = g_list_prepend (shutdown_functions, f);
+}

Added: trunk/eel/eel-debug.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-debug.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,48 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-debug.h: Eel debugging aids.
+ 
+   Copyright (C) 2000, 2001 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Darin Adler <darin eazel com>
+*/
+
+#ifndef EEL_DEBUG_H
+#define EEL_DEBUG_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef void (* EelFunction) (void);
+
+void eel_stop_in_debugger                             (void);
+void eel_make_warnings_and_criticals_stop_in_debugger (void);
+int  eel_get_available_file_descriptor_count          (void);
+
+/* A way to do cleanup at exit for compatibility with shutdown tools
+ * like the ones in Bonobo.
+ */
+void eel_debug_shut_down                              (void);
+void eel_debug_call_at_shutdown                       (EelFunction function);
+void eel_debug_call_at_shutdown_with_data             (GFreeFunc   function,
+						       gpointer    data);
+
+G_END_DECLS
+
+#endif /* EEL_DEBUG_H */

Added: trunk/eel/eel-editable-label.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-editable-label.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,4410 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+
+#include "eel-editable-label.h"
+#include "eel-i18n.h"
+#include "eel-marshal.h"
+#include "eel-accessibility.h"
+#include <libgail-util/gailmisc.h>
+
+#include <pango/pango.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+enum {
+  MOVE_CURSOR,
+  POPULATE_POPUP,
+  DELETE_FROM_CURSOR,
+  CUT_CLIPBOARD,
+  COPY_CLIPBOARD,
+  PASTE_CLIPBOARD,
+  TOGGLE_OVERWRITE,
+  LAST_SIGNAL
+};
+
+enum {
+  PROP_0,
+  PROP_TEXT,
+  PROP_JUSTIFY,
+  PROP_WRAP,
+  PROP_CURSOR_POSITION,
+  PROP_SELECTION_BOUND
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void     eel_editable_label_editable_init           (GtkEditableClass      *iface);
+static void     eel_editable_label_class_init              (EelEditableLabelClass *klass);
+static void     eel_editable_label_init                    (EelEditableLabel      *label);
+static void     eel_editable_label_set_property            (GObject               *object,
+							    guint                  prop_id,
+							    const GValue          *value,
+							    GParamSpec            *pspec);
+static void     eel_editable_label_get_property            (GObject               *object,
+							    guint                  prop_id,
+							    GValue                *value,
+							    GParamSpec            *pspec);
+static void     eel_editable_label_finalize                (GObject               *object);
+static void     eel_editable_label_size_request            (GtkWidget             *widget,
+							    GtkRequisition        *requisition);
+static void     eel_editable_label_size_allocate           (GtkWidget             *widget,
+							    GtkAllocation         *allocation);
+static void     eel_editable_label_state_changed           (GtkWidget             *widget,
+							    GtkStateType           state);
+static void     eel_editable_label_style_set               (GtkWidget             *widget,
+							    GtkStyle              *previous_style);
+static void     eel_editable_label_direction_changed       (GtkWidget             *widget,
+							    GtkTextDirection       previous_dir);
+static gint     eel_editable_label_expose                  (GtkWidget             *widget,
+							    GdkEventExpose        *event);
+static void     eel_editable_label_realize                 (GtkWidget             *widget);
+static void     eel_editable_label_unrealize               (GtkWidget             *widget);
+static void     eel_editable_label_map                     (GtkWidget             *widget);
+static void     eel_editable_label_unmap                   (GtkWidget             *widget);
+static gint     eel_editable_label_button_press            (GtkWidget             *widget,
+							    GdkEventButton        *event);
+static gint     eel_editable_label_button_release          (GtkWidget             *widget,
+							    GdkEventButton        *event);
+static gint     eel_editable_label_motion                  (GtkWidget             *widget,
+							    GdkEventMotion        *event);
+static gint     eel_editable_label_key_press               (GtkWidget             *widget,
+							    GdkEventKey           *event);
+static gint     eel_editable_label_key_release             (GtkWidget             *widget,
+							    GdkEventKey           *event);
+static gint     eel_editable_label_focus_in                (GtkWidget             *widget,
+							    GdkEventFocus         *event);
+static gint     eel_editable_label_focus_out               (GtkWidget             *widget,
+							    GdkEventFocus         *event);
+static AtkObject *eel_editable_label_get_accessible        (GtkWidget             *widget);
+static void     eel_editable_label_commit_cb               (GtkIMContext          *context,
+							    const gchar           *str,
+							    EelEditableLabel      *label);
+static void     eel_editable_label_preedit_changed_cb      (GtkIMContext          *context,
+							    EelEditableLabel      *label);
+static gboolean eel_editable_label_retrieve_surrounding_cb (GtkIMContext          *context,
+							    EelEditableLabel      *label);
+static gboolean eel_editable_label_delete_surrounding_cb   (GtkIMContext          *slave,
+							    gint                   offset,
+							    gint                   n_chars,
+							    EelEditableLabel      *label);
+static void     eel_editable_label_clear_layout            (EelEditableLabel      *label);
+static void     eel_editable_label_recompute               (EelEditableLabel      *label);
+static void     eel_editable_label_ensure_layout           (EelEditableLabel      *label,
+							    gboolean               include_preedit);
+static void     eel_editable_label_select_region_index     (EelEditableLabel      *label,
+							    gint                   anchor_index,
+							    gint                   end_index);
+static gboolean eel_editable_label_focus                   (GtkWidget             *widget,
+							    GtkDirectionType       direction);
+static void     eel_editable_label_move_cursor             (EelEditableLabel      *label,
+							    GtkMovementStep        step,
+							    gint                   count,
+							    gboolean               extend_selection);
+static void     eel_editable_label_delete_from_cursor      (EelEditableLabel      *label,
+							    GtkDeleteType          type,
+							    gint                   count);
+static void     eel_editable_label_copy_clipboard          (EelEditableLabel      *label);
+static void     eel_editable_label_cut_clipboard           (EelEditableLabel      *label);
+static void     eel_editable_label_paste                   (EelEditableLabel      *label,
+							    GdkAtom                selection);
+static void     eel_editable_label_paste_clipboard         (EelEditableLabel      *label);
+static void     eel_editable_label_select_all              (EelEditableLabel      *label);
+static void     eel_editable_label_do_popup                (EelEditableLabel      *label,
+							    GdkEventButton        *event);
+static void     eel_editable_label_toggle_overwrite        (EelEditableLabel      *label);
+static gint     eel_editable_label_move_forward_word       (EelEditableLabel      *label,
+							    gint                   start);
+static gint     eel_editable_label_move_backward_word      (EelEditableLabel      *label,
+							    gint                   start);
+static void     eel_editable_label_reset_im_context        (EelEditableLabel      *label);
+static void     eel_editable_label_check_cursor_blink      (EelEditableLabel      *label);
+static void     eel_editable_label_pend_cursor_blink       (EelEditableLabel      *label);
+
+/* Editable implementation: */
+static void     editable_insert_text_emit     (GtkEditable *editable,
+					       const gchar *new_text,
+					       gint         new_text_length,
+					       gint        *position);
+static void     editable_delete_text_emit     (GtkEditable *editable,
+					       gint         start_pos,
+					       gint         end_pos);
+static void     editable_insert_text          (GtkEditable *editable,
+					       const gchar *new_text,
+					       gint         new_text_length,
+					       gint        *position);
+static void     editable_delete_text          (GtkEditable *editable,
+					       gint         start_pos,
+					       gint         end_pos);
+static gchar *  editable_get_chars            (GtkEditable *editable,
+					       gint         start_pos,
+					       gint         end_pos);
+static void     editable_set_selection_bounds (GtkEditable *editable,
+					       gint         start,
+					       gint         end);
+static gboolean editable_get_selection_bounds (GtkEditable *editable,
+					       gint        *start,
+					       gint        *end);
+static void     editable_real_set_position    (GtkEditable *editable,
+					       gint         position);
+static gint     editable_get_position         (GtkEditable *editable);
+
+static GdkGC *  make_cursor_gc                (GtkWidget   *widget,
+					       const gchar *property_name,
+					       GdkColor    *fallback);
+
+
+
+static GtkMiscClass *parent_class = NULL;
+
+GType
+eel_editable_label_get_type (void)
+{
+  static GType label_type = 0;
+  
+  if (!label_type)
+    {
+      const GTypeInfo label_info =
+      {
+	sizeof (EelEditableLabelClass),
+	NULL,           /* base_init */
+	NULL,           /* base_finalize */
+	(GClassInitFunc) eel_editable_label_class_init,
+	NULL,           /* class_finalize */
+	NULL,           /* class_data */
+	sizeof (EelEditableLabel),
+	32,             /* n_preallocs */
+	(GInstanceInitFunc) eel_editable_label_init,
+      };
+
+      const GInterfaceInfo editable_info =
+      {
+	(GInterfaceInitFunc) eel_editable_label_editable_init,	/* interface_init */
+	NULL,							/* interface_finalize */
+	NULL							/* interface_data */
+      };
+
+      
+      label_type = g_type_register_static (GTK_TYPE_MISC, "EelEditableLabel", &label_info, 0);
+      g_type_add_interface_static (label_type,
+				   GTK_TYPE_EDITABLE,
+				   &editable_info);
+    }
+  
+  return label_type;
+}
+
+static void
+add_move_binding (GtkBindingSet  *binding_set,
+		  guint           keyval,
+		  guint           modmask,
+		  GtkMovementStep step,
+		  gint            count)
+{
+  g_assert ((modmask & GDK_SHIFT_MASK) == 0);
+  
+  gtk_binding_entry_add_signal (binding_set, keyval, modmask,
+				"move_cursor", 3,
+				G_TYPE_ENUM, step,
+				G_TYPE_INT, count,
+                                G_TYPE_BOOLEAN, FALSE);
+
+  /* Selection-extending version */
+  gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
+				"move_cursor", 3,
+				G_TYPE_ENUM, step,
+				G_TYPE_INT, count,
+                                G_TYPE_BOOLEAN, TRUE);
+}
+
+static void
+eel_editable_label_class_init (EelEditableLabelClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+  GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  GtkBindingSet *binding_set;
+
+  parent_class = gtk_type_class (GTK_TYPE_MISC);
+  
+  gobject_class->set_property = eel_editable_label_set_property;
+  gobject_class->get_property = eel_editable_label_get_property;
+  gobject_class->finalize = eel_editable_label_finalize;
+
+  widget_class->size_request = eel_editable_label_size_request;
+  widget_class->size_allocate = eel_editable_label_size_allocate;
+  widget_class->state_changed = eel_editable_label_state_changed;
+  widget_class->style_set = eel_editable_label_style_set;
+  widget_class->direction_changed = eel_editable_label_direction_changed;
+  widget_class->expose_event = eel_editable_label_expose;
+  widget_class->realize = eel_editable_label_realize;
+  widget_class->unrealize = eel_editable_label_unrealize;
+  widget_class->map = eel_editable_label_map;
+  widget_class->unmap = eel_editable_label_unmap;
+  widget_class->button_press_event = eel_editable_label_button_press;
+  widget_class->button_release_event = eel_editable_label_button_release;
+  widget_class->motion_notify_event = eel_editable_label_motion;
+  widget_class->focus = eel_editable_label_focus;
+  widget_class->key_press_event = eel_editable_label_key_press;
+  widget_class->key_release_event = eel_editable_label_key_release;
+  widget_class->focus_in_event = eel_editable_label_focus_in;
+  widget_class->focus_out_event = eel_editable_label_focus_out;
+  widget_class->get_accessible = eel_editable_label_get_accessible;
+
+  class->move_cursor = eel_editable_label_move_cursor;
+  class->delete_from_cursor = eel_editable_label_delete_from_cursor;
+  class->copy_clipboard = eel_editable_label_copy_clipboard;
+  class->cut_clipboard = eel_editable_label_cut_clipboard;
+  class->paste_clipboard = eel_editable_label_paste_clipboard;
+  class->toggle_overwrite = eel_editable_label_toggle_overwrite;
+  
+  signals[MOVE_CURSOR] = 
+    g_signal_new ("move_cursor",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (EelEditableLabelClass, move_cursor),
+		  NULL, NULL,
+		  eel_marshal_VOID__ENUM_INT_BOOLEAN,
+		  G_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT, G_TYPE_BOOLEAN);
+  
+  signals[COPY_CLIPBOARD] =
+    g_signal_new ("copy_clipboard",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET  (EelEditableLabelClass, copy_clipboard),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+  
+  signals[POPULATE_POPUP] =
+    g_signal_new ("populate_popup",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (EelEditableLabelClass, populate_popup),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__OBJECT,
+		  G_TYPE_NONE, 1, GTK_TYPE_MENU);
+
+  signals[DELETE_FROM_CURSOR] = 
+    g_signal_new ("delete_from_cursor",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (EelEditableLabelClass, delete_from_cursor),
+		  NULL, NULL, 
+		  eel_marshal_VOID__ENUM_INT,
+		  G_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, G_TYPE_INT);
+  
+  signals[CUT_CLIPBOARD] =
+    g_signal_new ("cut_clipboard",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (EelEditableLabelClass, cut_clipboard),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
+  signals[PASTE_CLIPBOARD] =
+    g_signal_new ("paste_clipboard",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (EelEditableLabelClass, paste_clipboard),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
+  signals[TOGGLE_OVERWRITE] =
+    g_signal_new ("toggle_overwrite",
+		  G_TYPE_FROM_CLASS (object_class),
+		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		  G_STRUCT_OFFSET (EelEditableLabelClass, toggle_overwrite),
+		  NULL, NULL, 
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE, 0);
+
+  
+  g_object_class_install_property (G_OBJECT_CLASS(object_class),
+                                   PROP_TEXT,
+                                   g_param_spec_string ("text",
+                                                        _("Text"),
+                                                        _("The text of the label."),
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+				   PROP_JUSTIFY,
+                                   g_param_spec_enum ("justify",
+                                                      _("Justification"),
+                                                      _("The alignment of the lines in the text of the label relative to each other. This does NOT affect the alignment of the label within its allocation. See GtkMisc::xalign for that."),
+						      GTK_TYPE_JUSTIFICATION,
+						      GTK_JUSTIFY_LEFT,
+                                                      G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_WRAP,
+                                   g_param_spec_boolean ("wrap",
+                                                        _("Line wrap"),
+                                                        _("If set, wrap lines if the text becomes too wide."),
+                                                        FALSE,
+                                                        G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_CURSOR_POSITION,
+                                   g_param_spec_int ("cursor_position",
+                                                     _("Cursor Position"),
+                                                     _("The current position of the insertion cursor in chars."),
+                                                     0,
+                                                     G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READABLE));
+  
+  g_object_class_install_property (gobject_class,
+                                   PROP_SELECTION_BOUND,
+                                   g_param_spec_int ("selection_bound",
+                                                     _("Selection Bound"),
+                                                     _("The position of the opposite end of the selection from the cursor in chars."),
+                                                     0,
+                                                     G_MAXINT,
+                                                     0,
+                                                     G_PARAM_READABLE));
+  
+  /*
+   * Key bindings
+   */
+
+  binding_set = gtk_binding_set_by_class (class);
+
+  /* Moving the insertion point */
+  add_move_binding (binding_set, GDK_Right, 0,
+		    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+  
+  add_move_binding (binding_set, GDK_Left, 0,
+		    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+
+  add_move_binding (binding_set, GDK_KP_Right, 0,
+		    GTK_MOVEMENT_VISUAL_POSITIONS, 1);
+  
+  add_move_binding (binding_set, GDK_KP_Left, 0,
+		    GTK_MOVEMENT_VISUAL_POSITIONS, -1);
+  
+  add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_LOGICAL_POSITIONS, 1);
+  
+  add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_LOGICAL_POSITIONS, -1);
+  
+  add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_WORDS, 1);
+
+  add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_WORDS, -1);
+
+  add_move_binding (binding_set, GDK_KP_Right, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_WORDS, 1);
+
+  add_move_binding (binding_set, GDK_KP_Left, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_WORDS, -1);
+  
+  add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
+
+  add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
+
+  add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
+		    GTK_MOVEMENT_WORDS, 1);
+
+  add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
+		    GTK_MOVEMENT_WORDS, -1);
+
+  add_move_binding (binding_set, GDK_Home, 0,
+		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+
+  add_move_binding (binding_set, GDK_End, 0,
+		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+
+  add_move_binding (binding_set, GDK_KP_Home, 0,
+		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
+
+  add_move_binding (binding_set, GDK_KP_End, 0,
+		    GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+  
+  add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_BUFFER_ENDS, -1);
+
+  add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+  add_move_binding (binding_set, GDK_KP_Home, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_BUFFER_ENDS, -1);
+
+  add_move_binding (binding_set, GDK_KP_End, GDK_CONTROL_MASK,
+		    GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+  add_move_binding (binding_set, GDK_Up, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, -1);
+
+  add_move_binding (binding_set, GDK_KP_Up, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, -1);
+  
+  add_move_binding (binding_set, GDK_Down, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, 1);
+
+  add_move_binding (binding_set, GDK_KP_Down, 0,
+                    GTK_MOVEMENT_DISPLAY_LINES, 1);
+  
+  /* Select all
+   */
+  gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
+                                "move_cursor", 3,
+                                GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
+                                G_TYPE_INT, -1,
+				G_TYPE_BOOLEAN, FALSE);
+  gtk_binding_entry_add_signal (binding_set, GDK_a, GDK_CONTROL_MASK,
+                                "move_cursor", 3,
+                                GTK_TYPE_MOVEMENT_STEP, GTK_MOVEMENT_BUFFER_ENDS,
+                                G_TYPE_INT, 1,
+				G_TYPE_BOOLEAN, TRUE);
+  
+  /* Deleting text */
+  gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_CHARS,
+				G_TYPE_INT, 1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_CHARS,
+				G_TYPE_INT, 1);
+  
+  gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_CHARS,
+				G_TYPE_INT, -1);
+
+  /* Make this do the same as Backspace, to help with mis-typing */
+  gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_SHIFT_MASK,
+                                "delete_from_cursor", 2,
+                                G_TYPE_ENUM, GTK_DELETE_CHARS,
+                                G_TYPE_INT, -1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+				G_TYPE_INT, 1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_CONTROL_MASK,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+				G_TYPE_INT, 1);
+  
+  gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
+				"delete_from_cursor", 2,
+				G_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+				G_TYPE_INT, -1);
+
+  /* Cut/copy/paste */
+
+  gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
+				"cut_clipboard", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
+				"copy_clipboard", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
+				"paste_clipboard", 0);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_SHIFT_MASK,
+				"cut_clipboard", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_CONTROL_MASK,
+				"copy_clipboard", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_Insert, GDK_SHIFT_MASK,
+				"paste_clipboard", 0);
+
+  /* Overwrite */
+  gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
+				"toggle_overwrite", 0);
+  gtk_binding_entry_add_signal (binding_set, GDK_KP_Insert, 0,
+				"toggle_overwrite", 0);
+}
+
+static void
+eel_editable_label_editable_init (GtkEditableClass *iface)
+{
+  iface->do_insert_text = editable_insert_text_emit;
+  iface->do_delete_text = editable_delete_text_emit;
+  iface->insert_text = editable_insert_text;
+  iface->delete_text = editable_delete_text;
+  iface->get_chars = editable_get_chars;
+  iface->set_selection_bounds = editable_set_selection_bounds;
+  iface->get_selection_bounds = editable_get_selection_bounds;
+  iface->set_position = editable_real_set_position;
+  iface->get_position = editable_get_position;
+}
+
+
+static void 
+eel_editable_label_set_property (GObject      *object,
+				 guint         prop_id,
+				 const GValue *value,
+				 GParamSpec   *pspec)
+{
+  EelEditableLabel *label;
+
+  label = EEL_EDITABLE_LABEL (object);
+  
+  switch (prop_id)
+    {
+    case PROP_TEXT:
+      eel_editable_label_set_text (label, g_value_get_string (value));
+      break;
+    case PROP_JUSTIFY:
+      eel_editable_label_set_justify (label, g_value_get_enum (value));
+      break;
+    case PROP_WRAP:
+      eel_editable_label_set_line_wrap (label, g_value_get_boolean (value));
+      break;	  
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void 
+eel_editable_label_get_property (GObject     *object,
+				 guint        prop_id,
+				 GValue      *value,
+				 GParamSpec  *pspec)
+{
+  EelEditableLabel *label;
+  gint offset;
+  
+  label = EEL_EDITABLE_LABEL (object);
+  
+  switch (prop_id)
+    {
+    case PROP_TEXT:
+      g_value_set_string (value, label->text);
+      break;
+    case PROP_JUSTIFY:
+      g_value_set_enum (value, label->jtype);
+      break;
+    case PROP_WRAP:
+      g_value_set_boolean (value, label->wrap);
+      break;
+    case PROP_CURSOR_POSITION:
+      offset = g_utf8_pointer_to_offset (label->text,
+					 label->text + label->selection_end);
+      g_value_set_int (value, offset);
+      break;
+    case PROP_SELECTION_BOUND:
+      offset = g_utf8_pointer_to_offset (label->text,
+					 label->text + label->selection_anchor);
+      g_value_set_int (value, offset);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+eel_editable_label_init (EelEditableLabel *label)
+{
+  label->jtype = GTK_JUSTIFY_LEFT;
+  label->wrap = FALSE;
+  label->wrap_mode = PANGO_WRAP_WORD;
+
+  label->layout = NULL;
+  label->text_size = 1;
+  label->text = g_malloc (label->text_size);
+  label->text[0] = '\0';
+  label->n_bytes = 0;
+  
+  GTK_WIDGET_SET_FLAGS (label, GTK_CAN_FOCUS);
+
+    /* This object is completely private. No external entity can gain a reference
+   * to it; so we create it here and destroy it in finalize().
+   */
+  label->im_context = gtk_im_multicontext_new ();
+
+  g_signal_connect (G_OBJECT (label->im_context), "commit",
+		    G_CALLBACK (eel_editable_label_commit_cb), label);
+  g_signal_connect (G_OBJECT (label->im_context), "preedit_changed",
+		    G_CALLBACK (eel_editable_label_preedit_changed_cb), label);
+  g_signal_connect (G_OBJECT (label->im_context), "retrieve_surrounding",
+		    G_CALLBACK (eel_editable_label_retrieve_surrounding_cb), label);
+  g_signal_connect (G_OBJECT (label->im_context), "delete_surrounding",
+		    G_CALLBACK (eel_editable_label_delete_surrounding_cb), label);
+}
+
+/**
+ * eel_editable_label_new:
+ * @str: The text of the label
+ *
+ * Creates a new label with the given text inside it. You can
+ * pass %NULL to get an empty label widget.
+ *
+ * Return value: the new #EelEditableLabel
+ **/
+GtkWidget*
+eel_editable_label_new (const gchar *str)
+{
+  EelEditableLabel *label;
+  
+  label = g_object_new (EEL_TYPE_EDITABLE_LABEL, NULL);
+
+  if (str && *str)
+    eel_editable_label_set_text (label, str);
+  
+  return GTK_WIDGET (label);
+}
+
+/**
+ * eel_editable_label_set_text:
+ * @label: a #EelEditableLabel
+ * @str: The text you want to set.
+ *
+ * Sets the text within the #EelEditableLabel widget.  It overwrites any text that
+ * was there before.  
+ *
+ * This will also clear any previously set mnemonic accelerators.
+ **/
+void
+eel_editable_label_set_text (EelEditableLabel *label,
+			     const gchar *str)
+{
+  GtkEditable *editable;
+  int tmp_pos;
+  
+  g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
+  g_return_if_fail (str != NULL);
+  
+  if (strcmp (label->text, str) == 0)
+    return;
+
+  editable = GTK_EDITABLE (label);
+  gtk_editable_delete_text (editable, 0, -1);
+  tmp_pos = 0;
+  gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
+}
+
+/**
+ * eel_editable_label_get_text:
+ * @label: a #EelEditableLabel
+ * 
+ * Fetches the text from a label widget, as displayed on the
+ * screen. This does not include any embedded underlines
+ * indicating mnemonics or Pango markup. (See eel_editable_label_get_label())
+ * 
+ * Return value: the text in the label widget. This is the internal
+ *   string used by the label, and must not be modified.
+ **/
+G_CONST_RETURN gchar *
+eel_editable_label_get_text (EelEditableLabel *label)
+{
+  g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), NULL);
+
+  return label->text;
+}
+
+/**
+ * eel_editable_label_set_justify:
+ * @label: a #EelEditableLabel
+ * @jtype: a #GtkJustification
+ *
+ * Sets the alignment of the lines in the text of the label relative to
+ * each other.  %GTK_JUSTIFY_LEFT is the default value when the
+ * widget is first created with eel_editable_label_new(). If you instead want
+ * to set the alignment of the label as a whole, use
+ * gtk_misc_set_alignment() instead. eel_editable_label_set_justify() has no
+ * effect on labels containing only a single line.
+ **/
+void
+eel_editable_label_set_justify (EelEditableLabel        *label,
+				GtkJustification jtype)
+{
+  g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
+  g_return_if_fail (jtype >= GTK_JUSTIFY_LEFT && jtype <= GTK_JUSTIFY_FILL);
+  
+  if ((GtkJustification) label->jtype != jtype)
+    {
+      label->jtype = jtype;
+
+      /* No real need to be this drastic, but easier than duplicating the code */
+      eel_editable_label_recompute (label);
+      
+      g_object_notify (G_OBJECT (label), "justify");
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+    }
+}
+
+/**
+ * eel_editable_label_get_justify:
+ * @label: a #EelEditableLabel
+ *
+ * Returns the justification of the label. See eel_editable_label_set_justify ().
+ *
+ * Return value: #GtkJustification
+ **/
+GtkJustification
+eel_editable_label_get_justify (EelEditableLabel *label)
+{
+  g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), 0);
+
+  return label->jtype;
+}
+
+void
+eel_editable_label_set_draw_outline (EelEditableLabel *label,
+				     gboolean          draw_outline)
+{
+    draw_outline = draw_outline != FALSE;
+
+    if (label->draw_outline != draw_outline)
+    {
+      label->draw_outline = draw_outline;
+      
+      gtk_widget_queue_draw (GTK_WIDGET (label));
+    }
+
+}
+
+
+/**
+ * eel_editable_label_set_line_wrap:
+ * @label: a #EelEditableLabel
+ * @wrap: the setting
+ *
+ * Toggles line wrapping within the #EelEditableLabel widget.  %TRUE makes it break
+ * lines if text exceeds the widget's size.  %FALSE lets the text get cut off
+ * by the edge of the widget if it exceeds the widget size.
+ **/
+void
+eel_editable_label_set_line_wrap (EelEditableLabel *label,
+				  gboolean  wrap)
+{
+  g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
+  
+  wrap = wrap != FALSE;
+  
+  if (label->wrap != wrap)
+    {
+      label->wrap = wrap;
+      g_object_notify (G_OBJECT (label), "wrap");
+      
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+    }
+}
+
+
+void
+eel_editable_label_set_line_wrap_mode (EelEditableLabel *label,
+				       PangoWrapMode     mode)
+{
+  g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
+  
+  if (label->wrap_mode != mode)
+    {
+      label->wrap_mode = mode;
+      
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+    }
+  
+}
+
+
+/**
+ * eel_editable_label_get_line_wrap:
+ * @label: a #EelEditableLabel
+ *
+ * Returns whether lines in the label are automatically wrapped. See eel_editable_label_set_line_wrap ().
+ *
+ * Return value: %TRUE if the lines of the label are automatically wrapped.
+ */
+gboolean
+eel_editable_label_get_line_wrap (EelEditableLabel *label)
+{
+  g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), FALSE);
+
+  return label->wrap;
+}
+
+PangoFontDescription *
+eel_editable_label_get_font_description (EelEditableLabel *label)
+{
+  if (label->font_desc)
+    return pango_font_description_copy (label->font_desc);
+
+  return NULL;
+}
+
+void
+eel_editable_label_set_font_description (EelEditableLabel *label,
+					 const PangoFontDescription *desc)
+{
+  if (label->font_desc)
+    pango_font_description_free (label->font_desc);
+
+  if (desc)
+    label->font_desc = pango_font_description_copy (desc);
+  else
+    label->font_desc = NULL;
+
+  eel_editable_label_clear_layout (label);
+}
+
+static void
+eel_editable_label_finalize (GObject *object)
+{
+  EelEditableLabel *label;
+  
+  g_assert (EEL_IS_EDITABLE_LABEL (object));
+  
+  label = EEL_EDITABLE_LABEL (object);
+
+  if (label->font_desc)
+    {
+      pango_font_description_free (label->font_desc);
+      label->font_desc = NULL;
+    }
+  
+  g_object_unref (G_OBJECT (label->im_context));
+  label->im_context = NULL;
+  
+  g_free (label->text);
+  label->text = NULL;
+
+  if (label->layout)
+    {
+      g_object_unref (G_OBJECT (label->layout));
+      label->layout = NULL;
+    }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+eel_editable_label_clear_layout (EelEditableLabel *label)
+{
+  if (label->layout)
+    {
+      g_object_unref (G_OBJECT (label->layout));
+      label->layout = NULL;
+    }
+}
+
+static void
+eel_editable_label_recompute (EelEditableLabel *label)
+{
+  eel_editable_label_clear_layout (label);
+  eel_editable_label_check_cursor_blink (label);
+}
+
+typedef struct _LabelWrapWidth LabelWrapWidth;
+struct _LabelWrapWidth
+{
+  gint width;
+  PangoFontDescription *font_desc;
+};
+
+static void
+label_wrap_width_free (gpointer data)
+{
+  LabelWrapWidth *wrap_width = data;
+  pango_font_description_free (wrap_width->font_desc);
+  g_free (wrap_width);
+}
+
+static gint
+get_label_wrap_width (EelEditableLabel *label)
+{
+  PangoLayout *layout;
+  GtkStyle *style = GTK_WIDGET (label)->style;
+
+  LabelWrapWidth *wrap_width = g_object_get_data (G_OBJECT (style), "gtk-label-wrap-width");
+  if (!wrap_width)
+    {
+      wrap_width = g_new0 (LabelWrapWidth, 1);
+      g_object_set_data_full (G_OBJECT (style), "gtk-label-wrap-width",
+			      wrap_width, label_wrap_width_free);
+    }
+
+  if (wrap_width->font_desc && pango_font_description_equal (wrap_width->font_desc, style->font_desc))
+    return wrap_width->width;
+
+  if (wrap_width->font_desc)
+    pango_font_description_free (wrap_width->font_desc);
+
+  wrap_width->font_desc = pango_font_description_copy (style->font_desc);
+
+  layout = gtk_widget_create_pango_layout (GTK_WIDGET (label), 
+					   "This long string gives a good enough length for any line to have.");
+  pango_layout_get_size (layout, &wrap_width->width, NULL);
+  g_object_unref (layout);
+
+  return wrap_width->width;
+}
+
+static void
+eel_editable_label_ensure_layout (EelEditableLabel *label,
+				  gboolean        include_preedit)
+{
+  GtkWidget *widget;
+  PangoRectangle logical_rect;
+
+  /* Normalize for comparisons */
+  include_preedit = include_preedit != 0;
+
+  if (label->preedit_length > 0 &&
+      include_preedit != label->layout_includes_preedit)
+    eel_editable_label_clear_layout (label);
+  
+  widget = GTK_WIDGET (label);
+
+  if (label->layout == NULL)
+    {
+      gchar *preedit_string = NULL;
+      gint preedit_length = 0;
+      PangoAttrList *preedit_attrs = NULL;
+      PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
+      PangoAttrList *tmp_attrs = pango_attr_list_new ();
+
+      if (include_preedit)
+	{
+	  gtk_im_context_get_preedit_string (label->im_context,
+					     &preedit_string, &preedit_attrs, NULL);
+	  preedit_length = label->preedit_length;
+	}
+
+      if (preedit_length)
+	{
+	  GString *tmp_string = g_string_new (NULL);
+	  
+	  g_string_prepend_len (tmp_string, label->text, label->n_bytes);
+	  g_string_insert (tmp_string, label->selection_anchor, preedit_string);
+      
+	  label->layout = gtk_widget_create_pango_layout (widget, tmp_string->str);
+      
+	  pango_attr_list_splice (tmp_attrs, preedit_attrs,
+				  label->selection_anchor, preedit_length);
+	  
+	  g_string_free (tmp_string, TRUE);
+	}
+      else
+	{
+	  label->layout = gtk_widget_create_pango_layout (widget, label->text);
+	}
+      label->layout_includes_preedit = include_preedit;
+
+      if (label->font_desc != NULL)
+	pango_layout_set_font_description (label->layout, label->font_desc);
+      
+      pango_layout_set_attributes (label->layout, tmp_attrs);
+      
+      if (preedit_string)
+	g_free (preedit_string);
+      if (preedit_attrs)
+	pango_attr_list_unref (preedit_attrs);
+      pango_attr_list_unref (tmp_attrs);
+
+      switch (label->jtype)
+	{
+	case GTK_JUSTIFY_LEFT:
+	  align = PANGO_ALIGN_LEFT;
+	  break;
+	case GTK_JUSTIFY_RIGHT:
+	  align = PANGO_ALIGN_RIGHT;
+	  break;
+	case GTK_JUSTIFY_CENTER:
+	  align = PANGO_ALIGN_CENTER;
+	  break;
+	case GTK_JUSTIFY_FILL:
+	  /* FIXME: This just doesn't work to do this */
+	  align = PANGO_ALIGN_LEFT;
+	  pango_layout_set_justify (label->layout, TRUE);
+	  break;
+	default:
+	  g_assert_not_reached();
+	}
+
+      pango_layout_set_alignment (label->layout, align);
+
+      if (label->wrap)
+	{
+	  gint longest_paragraph;
+	  gint width, height;
+	  gint set_width;
+
+	  gtk_widget_get_size_request (widget, &set_width, NULL);
+	  if (set_width > 0)
+	    pango_layout_set_width (label->layout, set_width * PANGO_SCALE);
+	  else
+	    {
+	      gint wrap_width;
+	      
+	      pango_layout_set_width (label->layout, -1);
+	      pango_layout_get_extents (label->layout, NULL, &logical_rect);
+
+	      width = logical_rect.width;
+	      
+	      /* Try to guess a reasonable maximum width */
+	      longest_paragraph = width;
+
+	      wrap_width = get_label_wrap_width (label);
+	      width = MIN (width, wrap_width);
+	      width = MIN (width,
+			   PANGO_SCALE * (gdk_screen_width () + 1) / 2);
+	      
+	      pango_layout_set_width (label->layout, width);
+	      pango_layout_get_extents (label->layout, NULL, &logical_rect);
+	      width = logical_rect.width;
+	      height = logical_rect.height;
+	      
+	      /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
+	       * so we try short search for a narrower width that leaves us with the same height
+	       */
+	      if (longest_paragraph > 0)
+		{
+		  gint nlines, perfect_width;
+		  
+		  nlines = pango_layout_get_line_count (label->layout);
+		  perfect_width = (longest_paragraph + nlines - 1) / nlines;
+		  
+		  if (perfect_width < width)
+		    {
+		      pango_layout_set_width (label->layout, perfect_width);
+		      pango_layout_get_extents (label->layout, NULL, &logical_rect);
+		      
+		      if (logical_rect.height <= height)
+			width = logical_rect.width;
+		      else
+			{
+			  gint mid_width = (perfect_width + width) / 2;
+			  
+			  if (mid_width > perfect_width)
+			    {
+			      pango_layout_set_width (label->layout, mid_width);
+			      pango_layout_get_extents (label->layout, NULL, &logical_rect);
+			      
+			      if (logical_rect.height <= height)
+				width = logical_rect.width;
+			    }
+			}
+		    }
+		}
+	      pango_layout_set_width (label->layout, width);
+	    }
+	  pango_layout_set_wrap (label->layout, label->wrap_mode);
+	}
+      else		/* !label->wrap */
+	pango_layout_set_width (label->layout, -1);
+    }
+}
+
+static void
+eel_editable_label_size_request (GtkWidget      *widget,
+				 GtkRequisition *requisition)
+{
+  EelEditableLabel *label;
+  gint width, height;
+  PangoRectangle logical_rect;
+  gint set_width;
+
+  g_assert (EEL_IS_EDITABLE_LABEL (widget));
+  g_assert (requisition != NULL);
+  
+  label = EEL_EDITABLE_LABEL (widget);
+
+  /*  
+   * If word wrapping is on, then the height requisition can depend
+   * on:
+   *
+   *   - Any width set on the widget via gtk_widget_set_usize().
+   *   - The padding of the widget (xpad, set by gtk_misc_set_padding)
+   *
+   * Instead of trying to detect changes to these quantities, if we
+   * are wrapping, we just rewrap for each size request. Since
+   * size requisitions are cached by the GTK+ core, this is not
+   * expensive.
+   */
+
+  if (label->wrap)
+    eel_editable_label_recompute (label);
+
+  eel_editable_label_ensure_layout (label, TRUE);
+
+  width = label->misc.xpad * 2;
+  height = label->misc.ypad * 2;
+
+  pango_layout_get_extents (label->layout, NULL, &logical_rect);
+  
+  gtk_widget_get_size_request (widget, &set_width, NULL);
+  if (label->wrap && set_width > 0)
+    width += set_width;
+  else 
+    width += PANGO_PIXELS (logical_rect.width);
+  
+  height += PANGO_PIXELS (logical_rect.height);
+
+  requisition->width = width;
+  requisition->height = height;
+}
+
+static void
+eel_editable_label_size_allocate (GtkWidget     *widget,
+				  GtkAllocation *allocation)
+{
+  (* GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation);
+}
+
+static void
+eel_editable_label_state_changed (GtkWidget   *widget,
+				  GtkStateType prev_state)
+{
+  EelEditableLabel *label;
+  
+  label = EEL_EDITABLE_LABEL (widget);
+
+  eel_editable_label_select_region (label, 0, 0);
+
+  if (GTK_WIDGET_CLASS (parent_class)->state_changed)
+    GTK_WIDGET_CLASS (parent_class)->state_changed (widget, prev_state);
+}
+
+static void 
+eel_editable_label_style_set (GtkWidget *widget,
+			      GtkStyle  *previous_style)
+{
+  EelEditableLabel *label;
+  static GdkColor gray = { 0, 0x8888, 0x8888, 0x8888 };
+  
+  g_assert (EEL_IS_EDITABLE_LABEL (widget));
+  
+  label = EEL_EDITABLE_LABEL (widget);
+
+  /* We have to clear the layout, fonts etc. may have changed */
+  eel_editable_label_recompute (label);
+
+  /* Set the background, foreground and cursor colors based on 
+   * the new theme selected.
+   */
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+	gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+
+	if (label->primary_cursor_gc != NULL) 
+	  {
+		gtk_gc_release (label->primary_cursor_gc);
+		label->primary_cursor_gc = NULL;
+	  }
+
+	if (label->secondary_cursor_gc != NULL) 
+	  {
+		gtk_gc_release (label->secondary_cursor_gc);
+		label->secondary_cursor_gc = NULL;
+	  }
+
+	label->primary_cursor_gc = make_cursor_gc (widget,
+						   "cursor-color",
+						   &widget->style->black);
+
+	label->secondary_cursor_gc = make_cursor_gc (widget,
+						     "secondary-cursor-color",
+						     &gray);
+    }
+}
+
+static void 
+eel_editable_label_direction_changed (GtkWidget        *widget,
+				      GtkTextDirection previous_dir)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
+
+  if (label->layout)
+    pango_layout_context_changed (label->layout);
+
+  GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
+}
+
+static void
+get_layout_location (EelEditableLabel  *label,
+                     gint      *xp,
+                     gint      *yp)
+{
+  GtkMisc *misc;
+  GtkWidget *widget;
+  gfloat xalign;
+  GtkRequisition req;
+  gint x, y;
+  
+  misc = GTK_MISC (label);
+  widget = GTK_WIDGET (label);
+  
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+    xalign = misc->xalign;
+  else
+    xalign = 1.0 - misc->xalign;
+
+  gtk_widget_get_child_requisition (widget, &req);
+  
+  x = floor ((gint)misc->xpad
+             + ((widget->allocation.width - req.width) * xalign)
+             + 0.5);
+  
+  y = floor ((gint)misc->ypad 
+             + ((widget->allocation.height - req.height) * misc->yalign)
+             + 0.5);
+
+  if (xp)
+    *xp = x;
+
+  if (yp)
+    *yp = y;
+}
+
+static void
+eel_editable_label_get_cursor_pos (EelEditableLabel  *label,
+				   PangoRectangle *strong_pos,
+				   PangoRectangle *weak_pos)
+{
+  const gchar *text;
+  const gchar *preedit_text;
+  gint index;
+  
+  eel_editable_label_ensure_layout (label, TRUE);
+  
+  text = pango_layout_get_text (label->layout);
+  preedit_text = text + label->selection_anchor;
+  index = label->selection_anchor +
+    g_utf8_offset_to_pointer (preedit_text, label->preedit_cursor) - preedit_text;
+      
+  pango_layout_get_cursor_pos (label->layout, index, strong_pos, weak_pos);
+}
+
+/* Copied from gtkutil private function */
+static gboolean
+eel_editable_label_get_block_cursor_location (EelEditableLabel  *label,
+					      gint *index,
+					      PangoRectangle *pos,
+					      gboolean *at_line_end)
+{
+  const gchar *text;
+  const gchar *preedit_text;
+  PangoLayoutLine *layout_line;
+  PangoRectangle strong_pos, weak_pos;
+  gint line_no;
+  gboolean rtl;
+  PangoContext *context;
+  PangoFontMetrics *metrics;
+  const PangoFontDescription *font_desc;
+  
+  eel_editable_label_ensure_layout (label, TRUE);
+  
+  text = pango_layout_get_text (label->layout);
+  preedit_text = text + label->selection_anchor;
+  text = g_utf8_offset_to_pointer (preedit_text, label->preedit_cursor);
+  index[0] = label->selection_anchor + text - preedit_text;
+
+  pango_layout_index_to_pos (label->layout, index[0], pos);
+
+  index[1] = label->selection_anchor + g_utf8_next_char (text) - preedit_text;
+
+  if (pos->width != 0)
+    {
+      if (at_line_end)
+	*at_line_end = FALSE;
+      if (pos->width < 0) /* RTL char, shift x value back to top left of rect */
+	{
+	  pos->x += pos->width;
+	  pos->width = -pos->width;
+	}
+      return TRUE;
+    }
+
+  pango_layout_index_to_line_x (label->layout, index[0], FALSE, &line_no, NULL);
+  layout_line = pango_layout_get_line_readonly (label->layout, line_no);
+  if (layout_line == NULL)
+    return FALSE;
+
+    text = pango_layout_get_text (label->layout);
+  if (index[0] < layout_line->start_index + layout_line->length)
+    {
+      /* this may be a zero-width character in the middle of the line,
+       * or it could be a character where line is wrapped, we do want
+       * block cursor in latter case */
+      if (g_utf8_next_char (text + index[0]) - text !=
+	  layout_line->start_index + layout_line->length)
+	{
+	  /* zero-width character in the middle of the line, do not
+	   * bother with block cursor */
+	  return FALSE;
+	}
+    }
+
+  /* Cursor is at the line end. It may be an empty line, or it could
+   * be on the left or on the right depending on text direction, or it
+   * even could be in the middle of visual layout in bidi text. */
+
+  pango_layout_get_cursor_pos (label->layout, index[0], &strong_pos, &weak_pos);
+
+  if (strong_pos.x != weak_pos.x)
+    {
+      /* do not show block cursor in this case, since the character typed
+       * in may or may not appear at the cursor position */
+      return FALSE;
+    }
+
+  context = pango_layout_get_context (label->layout);
+
+  /* In case when index points to the end of line, pos->x is always most right
+   * pixel of the layout line, so we need to correct it for RTL text. */
+  if (layout_line->length)
+    {
+      if (layout_line->resolved_dir == PANGO_DIRECTION_RTL)
+	{
+	  PangoLayoutIter *iter;
+	  PangoRectangle line_rect;
+	  gint i;
+	  gint left, right;
+	  const gchar *p;
+
+	  p = g_utf8_prev_char (text + index[0]);
+
+	  pango_layout_line_index_to_x (layout_line, p - text, FALSE, &left);
+	  pango_layout_line_index_to_x (layout_line, p - text, TRUE, &right);
+	  pos->x = MIN (left, right);
+
+	  iter = pango_layout_get_iter (label->layout);
+	  for (i = 0; i < line_no; i++)
+	    pango_layout_iter_next_line (iter);
+	  pango_layout_iter_get_line_extents (iter, NULL, &line_rect);
+	  pango_layout_iter_free (iter);
+
+          rtl = TRUE;
+	  pos->x += line_rect.x;
+	}
+      else
+	rtl = FALSE;
+    }
+  else
+    {
+      rtl = pango_context_get_base_dir (context) == PANGO_DIRECTION_RTL;
+    }
+  
+  font_desc = pango_layout_get_font_description (label->layout);
+  if (!font_desc)
+    font_desc = pango_context_get_font_description (context);
+
+  metrics = pango_context_get_metrics (context, font_desc, NULL);
+  pos->width = pango_font_metrics_get_approximate_char_width (metrics);
+  pango_font_metrics_unref (metrics);
+
+  if (rtl)
+    pos->x -= pos->width - 1;
+
+  if (at_line_end)
+    *at_line_end = TRUE;
+
+  return pos->width != 0;
+}
+
+
+/* These functions are copies from gtk+, as they are not exported from gtk+ */
+
+static GdkGC *
+make_cursor_gc (GtkWidget   *widget,
+		const gchar *property_name,
+		GdkColor    *fallback)
+{
+  GdkGCValues gc_values;
+  GdkGCValuesMask gc_values_mask;
+  GdkColor *cursor_color;
+
+  gtk_widget_style_get (widget, property_name, &cursor_color, NULL);
+  
+  gc_values_mask = GDK_GC_FOREGROUND;
+  if (cursor_color)
+    {
+      gc_values.foreground = *cursor_color;
+      gdk_color_free (cursor_color);
+    }
+  else
+    gc_values.foreground = *fallback;
+  
+  gdk_rgb_find_color (widget->style->colormap, &gc_values.foreground);
+  return gtk_gc_get (widget->style->depth, widget->style->colormap, &gc_values, gc_values_mask);
+}
+
+static void
+_eel_draw_insertion_cursor (GtkWidget        *widget,
+			    GdkDrawable      *drawable,
+			    GdkGC            *gc,
+                            GdkRectangle     *location,
+                            GtkTextDirection  direction,
+                            gboolean          draw_arrow)
+{
+  gint stem_width;
+  gint arrow_width;
+  gint x, y;
+  gint i;
+  gfloat cursor_aspect_ratio;
+  gint offset;
+  
+  g_assert (direction != GTK_TEXT_DIR_NONE);
+  
+  gtk_widget_style_get (widget, "cursor-aspect-ratio", &cursor_aspect_ratio, NULL);
+  
+  stem_width = location->height * cursor_aspect_ratio + 1;
+  arrow_width = stem_width + 1;
+
+  /* put (stem_width % 2) on the proper side of the cursor */
+  if (direction == GTK_TEXT_DIR_LTR)
+    offset = stem_width / 2;
+  else
+    offset = stem_width - stem_width / 2;
+  
+  for (i = 0; i < stem_width; i++)
+    gdk_draw_line (drawable, gc,
+		   location->x + i - offset, location->y,
+		   location->x + i - offset, location->y + location->height - 1);
+
+  if (draw_arrow)
+    {
+      if (direction == GTK_TEXT_DIR_RTL)
+        {
+          x = location->x - offset - 1;
+          y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
+  
+          for (i = 0; i < arrow_width; i++)
+            {
+              gdk_draw_line (drawable, gc,
+                             x, y + i + 1,
+                             x, y + 2 * arrow_width - i - 1);
+              x --;
+            }
+        }
+      else if (direction == GTK_TEXT_DIR_LTR)
+        {
+          x = location->x + stem_width - offset;
+          y = location->y + location->height - arrow_width * 2 - arrow_width + 1;
+  
+          for (i = 0; i < arrow_width; i++) 
+            {
+              gdk_draw_line (drawable, gc,
+                             x, y + i + 1,
+                             x, y + 2 * arrow_width - i - 1);
+              x++;
+            }
+        }
+    }
+}
+
+static void
+eel_editable_label_draw_cursor (EelEditableLabel  *label, gint xoffset, gint yoffset)
+{
+  if (GTK_WIDGET_DRAWABLE (label))
+    {
+      GtkWidget *widget = GTK_WIDGET (label);
+
+      GtkTextDirection keymap_direction;
+      GtkTextDirection widget_direction;
+      gboolean split_cursor;
+      gboolean block;
+      gboolean block_at_line_end;
+      gint range[2];
+      PangoRectangle strong_pos, weak_pos;
+      PangoRectangle *cursor1 = NULL;
+      PangoRectangle *cursor2 = NULL;
+      GdkRectangle cursor_location;
+      GtkTextDirection dir1 = GTK_TEXT_DIR_NONE;
+      GtkTextDirection dir2 = GTK_TEXT_DIR_NONE;
+
+      keymap_direction =
+	(gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
+	GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
+
+      widget_direction = gtk_widget_get_direction (widget);
+
+      if (label->overwrite_mode &&
+	  eel_editable_label_get_block_cursor_location (label, range,
+							&strong_pos,
+							&block_at_line_end))
+	block = TRUE;
+      else
+	block = FALSE;
+
+      if (!block)
+	{ 
+          eel_editable_label_get_cursor_pos (label, &strong_pos, &weak_pos);
+
+          g_object_get (gtk_widget_get_settings (widget),
+			"gtk-split-cursor", &split_cursor,
+			NULL);
+
+          dir1 = widget_direction;
+      
+	  if (split_cursor)
+	    {
+	      cursor1 = &strong_pos;
+
+	      if (strong_pos.x != weak_pos.x ||
+		  strong_pos.y != weak_pos.y)
+		{
+		  dir2 = (widget_direction == GTK_TEXT_DIR_LTR) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
+		  cursor2 = &weak_pos;
+		}
+	    }
+	  else
+	    {
+	      if (keymap_direction == widget_direction)
+		  cursor1 = &strong_pos;
+	      else
+		  cursor1 = &weak_pos;
+	    }
+      
+          cursor_location.x = xoffset + PANGO_PIXELS (cursor1->x);
+	  cursor_location.y = yoffset + PANGO_PIXELS (cursor1->y);
+	  cursor_location.width = 0;
+	  cursor_location.height = PANGO_PIXELS (cursor1->height);
+
+	  _eel_draw_insertion_cursor (widget, widget->window,
+				      label->primary_cursor_gc,
+				      &cursor_location, dir1,
+				      dir2 != GTK_TEXT_DIR_NONE);
+
+	  if (dir2 != GTK_TEXT_DIR_NONE)
+	    {
+	      cursor_location.x = xoffset + PANGO_PIXELS (cursor2->x);
+	      cursor_location.y = yoffset + PANGO_PIXELS (cursor2->y);
+	      cursor_location.width = 0;
+	      cursor_location.height = PANGO_PIXELS (cursor2->height);
+
+	      _eel_draw_insertion_cursor (widget, widget->window,
+					  label->secondary_cursor_gc,
+					  &cursor_location, dir2, TRUE);
+	    }
+	}
+      else /* Block cursor */
+	{
+          GdkRegion *clip;
+
+	  gdk_draw_rectangle (widget->window, label->primary_cursor_gc, TRUE,
+			      xoffset + PANGO_PIXELS (strong_pos.x),
+			      yoffset + PANGO_PIXELS (strong_pos.y),
+			      PANGO_PIXELS (strong_pos.width),
+			      PANGO_PIXELS (strong_pos.height));
+
+	  if (!block_at_line_end)
+	    {
+	      clip = gdk_pango_layout_get_clip_region (label->layout,
+						       xoffset, yoffset,
+						       range, 1);
+
+	      /* FIXME should use gtk_paint, but it can't use a clip
+	       * region
+	       */
+
+	      gdk_gc_set_clip_region (label->primary_cursor_gc, clip);
+
+	      gdk_draw_layout_with_colors (widget->window,
+		                           label->primary_cursor_gc,
+					   xoffset, yoffset,
+					   label->layout,
+					   &widget->style->base[GTK_STATE_NORMAL],
+					   NULL);
+
+	      gdk_gc_set_clip_region (label->primary_cursor_gc, NULL);
+	      gdk_region_destroy (clip);
+	    }
+	}	  
+    }
+}
+
+
+static gint
+eel_editable_label_expose (GtkWidget      *widget,
+			   GdkEventExpose *event)
+{
+  EelEditableLabel *label;
+  gint x, y;
+  
+  g_assert (EEL_IS_EDITABLE_LABEL (widget));
+  g_assert (event != NULL);
+  
+  label = EEL_EDITABLE_LABEL (widget);
+
+  eel_editable_label_ensure_layout (label, TRUE);
+  
+  if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_MAPPED (widget) &&
+      label->text)
+    {
+      get_layout_location (label, &x, &y);
+      
+      gtk_paint_layout (widget->style,
+                        widget->window,
+                        GTK_WIDGET_STATE (widget),
+			FALSE,
+                        &event->area,
+                        widget,
+                        "label",
+                        x, y,
+                        label->layout);
+      
+      if (label->selection_anchor != label->selection_end)
+        {
+          gint range[2];
+	  const char *text;
+          GdkRegion *clip;
+	  GtkStateType state;
+	  
+          range[0] = label->selection_anchor;
+          range[1] = label->selection_end;
+
+	  /* Handle possible preedit string */
+	  if (label->preedit_length > 0 &&
+	      range[1] > label->selection_anchor)
+	    {
+	      text = pango_layout_get_text (label->layout) + label->selection_anchor;
+	      range[1] += g_utf8_offset_to_pointer (text, label->preedit_length) - text;
+	    }
+	  
+          if (range[0] > range[1])
+            {
+              gint tmp = range[0];
+              range[0] = range[1];
+              range[1] = tmp;
+            }
+
+          clip = gdk_pango_layout_get_clip_region (label->layout,
+                                                   x, y,
+                                                   range,
+                                                   1);
+
+          /* FIXME should use gtk_paint, but it can't use a clip
+           * region
+           */
+
+          gdk_gc_set_clip_region (widget->style->black_gc, clip);
+
+
+	  state = GTK_STATE_SELECTED;
+	  if (!GTK_WIDGET_HAS_FOCUS (widget))
+	    state = GTK_STATE_ACTIVE;
+	      
+          gdk_draw_layout_with_colors (widget->window,
+                                       widget->style->black_gc,
+                                       x, y,
+                                       label->layout,
+                                       &widget->style->text[state],
+                                       &widget->style->base[state]);
+
+          gdk_gc_set_clip_region (widget->style->black_gc, NULL);
+          gdk_region_destroy (clip);
+        }
+      else if (GTK_WIDGET_HAS_FOCUS (widget))
+	eel_editable_label_draw_cursor (label, x, y);
+
+      if (label->draw_outline)
+	gdk_draw_rectangle (widget->window,
+			    widget->style->fg_gc [GTK_WIDGET_STATE (widget)],
+			    FALSE,
+			    0, 0,
+			    widget->allocation.width - 1,
+			    widget->allocation.height - 1);
+    }
+
+  return FALSE;
+}
+
+static void
+eel_editable_label_realize (GtkWidget *widget)
+{
+  EelEditableLabel *label;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+  static GdkColor gray = { 0, 0x8888, 0x8888, 0x8888 };
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+  label = EEL_EDITABLE_LABEL (widget);
+
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  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.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.cursor = gdk_cursor_new (GDK_XTERM);
+  attributes.event_mask = gtk_widget_get_events (widget) |
+    (GDK_EXPOSURE_MASK |
+     GDK_BUTTON_PRESS_MASK |
+     GDK_BUTTON_RELEASE_MASK |
+     GDK_BUTTON1_MOTION_MASK |
+     GDK_BUTTON3_MOTION_MASK |
+     GDK_POINTER_MOTION_HINT_MASK |
+     GDK_POINTER_MOTION_MASK |
+     GDK_ENTER_NOTIFY_MASK |
+     GDK_LEAVE_NOTIFY_MASK);
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y  | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
+
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+				   &attributes, attributes_mask);
+  gdk_window_set_user_data (widget->window, widget);
+
+  gdk_cursor_unref (attributes.cursor);
+
+  widget->style = gtk_style_attach (widget->style, widget->window);
+  
+  gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+
+  gtk_im_context_set_client_window (label->im_context, widget->window);
+
+  label->primary_cursor_gc = make_cursor_gc (widget,
+					     "cursor-color",
+					     &widget->style->black);
+      
+  label->secondary_cursor_gc = make_cursor_gc (widget,
+					       "secondary-cursor-color",
+					       &gray);
+}
+
+static void
+eel_editable_label_unrealize (GtkWidget *widget)
+{
+  EelEditableLabel *label;
+
+  label = EEL_EDITABLE_LABEL (widget);
+
+  gtk_gc_release (label->primary_cursor_gc);
+  label->primary_cursor_gc = NULL;
+  
+  gtk_gc_release (label->secondary_cursor_gc);
+  label->secondary_cursor_gc = NULL;
+
+  /* Strange. Copied from GtkEntry, should be NULL? */
+  gtk_im_context_set_client_window (label->im_context, NULL);
+  
+  (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+eel_editable_label_map (GtkWidget *widget)
+{
+  (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+}
+
+static void
+eel_editable_label_unmap (GtkWidget *widget)
+{
+  (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
+}
+
+static void
+window_to_layout_coords (EelEditableLabel *label,
+                         gint     *x,
+                         gint     *y)
+{
+  gint lx, ly;
+
+  /* get layout location in widget->window coords */
+  get_layout_location (label, &lx, &ly);
+  
+  if (x)
+    *x -= lx;                   /* go to layout */
+
+  if (y)
+    *y -= ly;                   /* go to layout */
+}
+
+static void
+get_layout_index (EelEditableLabel *label,
+                  gint      x,
+                  gint      y,
+                  gint     *index)
+{
+  gint trailing = 0;
+  const gchar *cluster;
+  const gchar *cluster_end;
+
+  *index = 0;
+  
+  eel_editable_label_ensure_layout (label, TRUE);
+  
+  window_to_layout_coords (label, &x, &y);
+
+  x *= PANGO_SCALE;
+  y *= PANGO_SCALE;
+  
+  pango_layout_xy_to_index (label->layout,
+                            x, y,
+                            index, &trailing);
+
+  if (*index >= label->selection_anchor && label->preedit_length)
+    {
+      if (*index >= label->selection_anchor + label->preedit_length)
+	*index -= label->preedit_length;
+      else
+	{
+	  *index = label->selection_anchor;
+	  trailing = 0;
+	}
+    }
+  
+  cluster = label->text + *index;
+  cluster_end = cluster;
+  while (trailing)
+    {
+      cluster_end = g_utf8_next_char (cluster_end);
+      --trailing;
+    }
+
+  *index += (cluster_end - cluster);
+}
+
+static void
+eel_editable_label_select_word (EelEditableLabel *label)
+{
+  gint min, max;
+  
+  gint start_index = eel_editable_label_move_backward_word (label, label->selection_end);
+  gint end_index = eel_editable_label_move_forward_word (label, label->selection_end);
+
+  min = MIN (label->selection_anchor,
+	     label->selection_end);
+  max = MAX (label->selection_anchor,
+	     label->selection_end);
+
+  min = MIN (min, start_index);
+  max = MAX (max, end_index);
+
+  eel_editable_label_select_region_index (label, min, max);
+}
+
+static gint
+eel_editable_label_button_press (GtkWidget      *widget,
+				 GdkEventButton *event)
+{
+  EelEditableLabel *label;
+  gint index = 0;
+  
+  label = EEL_EDITABLE_LABEL (widget);
+
+  if (event->button == 1)
+    {
+      if (!GTK_WIDGET_HAS_FOCUS (widget))
+	gtk_widget_grab_focus (widget);
+
+      if (event->type == GDK_3BUTTON_PRESS)
+	{
+	  eel_editable_label_select_region_index (label, 0, strlen (label->text));
+	  return TRUE;
+	}
+      
+      if (event->type == GDK_2BUTTON_PRESS)
+	{
+	  eel_editable_label_select_word (label);
+	  return TRUE;
+	}
+      
+      get_layout_index (label, event->x, event->y, &index);
+      
+      if ((label->selection_anchor !=
+	   label->selection_end) &&
+	  (event->state & GDK_SHIFT_MASK))
+	{
+	  gint min, max;
+	  
+	  /* extend (same as motion) */
+	  min = MIN (label->selection_anchor,
+		     label->selection_end);
+	  max = MAX (label->selection_anchor,
+		     label->selection_end);
+	  
+	  min = MIN (min, index);
+	  max = MAX (max, index);
+	  
+	  /* ensure the anchor is opposite index */
+	  if (index == min)
+	    {
+	      gint tmp = min;
+	      min = max;
+	      max = tmp;
+	    }
+	  
+	  eel_editable_label_select_region_index (label, min, max);
+	}
+      else
+	{
+	  if (event->type == GDK_3BUTTON_PRESS)
+	      eel_editable_label_select_region_index (label, 0, strlen (label->text));
+	  else if (event->type == GDK_2BUTTON_PRESS)
+	      eel_editable_label_select_word (label);
+	  else 
+	    /* start a replacement */
+	    eel_editable_label_select_region_index (label, index, index);
+	}
+  
+      return TRUE;
+    }
+  else if (event->button == 2 && event->type == GDK_BUTTON_PRESS)
+    {
+      get_layout_index (label, event->x, event->y, &index);
+      
+      eel_editable_label_select_region_index (label, index, index);
+      eel_editable_label_paste (label, GDK_SELECTION_PRIMARY);
+      
+      return TRUE;
+    }
+  else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+    {
+      eel_editable_label_do_popup (label, event);
+
+      return TRUE;
+      
+    }
+  return FALSE;
+}
+
+static gint
+eel_editable_label_button_release (GtkWidget      *widget,
+				   GdkEventButton *event)
+
+{
+  if (event->button != 1)
+    return FALSE;
+  
+  /* The goal here is to return TRUE iff we ate the
+   * button press to start selecting.
+   */
+  
+  return TRUE;
+}
+
+static gint
+eel_editable_label_motion (GtkWidget      *widget,
+			   GdkEventMotion *event)
+{
+  EelEditableLabel *label;
+  gint index;
+  gint x, y;
+  
+  label = EEL_EDITABLE_LABEL (widget);
+  
+  if ((event->state & GDK_BUTTON1_MASK) == 0)
+    return FALSE;
+
+  gdk_window_get_pointer (widget->window,
+                          &x, &y, NULL);
+  
+  get_layout_index (label, x, y, &index);
+
+  eel_editable_label_select_region_index (label,
+					  label->selection_anchor,
+					  index);
+  
+  return TRUE;
+}
+
+static void
+get_text_callback (GtkClipboard     *clipboard,
+                   GtkSelectionData *selection_data,
+                   guint             info,
+                   gpointer          user_data_or_owner)
+{
+  EelEditableLabel *label;
+  
+  label = EEL_EDITABLE_LABEL (user_data_or_owner);
+  
+  if ((label->selection_anchor != label->selection_end) &&
+      label->text)
+    {
+      gint start, end;
+      gint len;
+      
+      start = MIN (label->selection_anchor,
+                   label->selection_end);
+      end = MAX (label->selection_anchor,
+                 label->selection_end);
+
+      len = strlen (label->text);
+
+      if (end > len)
+        end = len;
+
+      if (start > len)
+        start = len;
+
+      gtk_selection_data_set_text (selection_data,
+				   label->text + start,
+				   end - start);
+    }
+}
+
+static void
+clear_text_callback (GtkClipboard     *clipboard,
+                     gpointer          user_data_or_owner)
+{
+  EelEditableLabel *label;
+
+  label = EEL_EDITABLE_LABEL (user_data_or_owner);
+
+  label->selection_anchor = label->selection_end;
+      
+  gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+static void
+eel_editable_label_select_region_index (EelEditableLabel *label,
+					gint      anchor_index,
+					gint      end_index)
+{
+  GtkClipboard *clipboard;
+
+  g_assert (EEL_IS_EDITABLE_LABEL (label));
+  
+
+  if (label->selection_anchor == anchor_index &&
+      label->selection_end == end_index)
+    return;
+
+  eel_editable_label_reset_im_context (label);
+
+  label->selection_anchor = anchor_index;
+  label->selection_end = end_index;
+
+  clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);      
+      
+  if (anchor_index != end_index)
+    {
+      GtkTargetList *list;
+      GtkTargetEntry *targets;
+      guint n_targets;
+
+      list = gtk_target_list_new (NULL, 0);
+      gtk_target_list_add_text_targets (list, 0);
+      targets = gtk_target_table_new_from_list (list, &n_targets);
+
+      gtk_clipboard_set_with_owner (clipboard,
+				    targets, n_targets,
+				    get_text_callback,
+				    clear_text_callback,
+				    G_OBJECT (label));
+
+      gtk_clipboard_set_can_store (clipboard, NULL, 0);
+      gtk_target_table_free (targets, n_targets);
+      gtk_target_list_unref (list);
+    }
+  else
+    {
+      if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (label))
+	gtk_clipboard_clear (clipboard);
+    }
+  
+  gtk_widget_queue_draw (GTK_WIDGET (label));
+  
+  g_object_freeze_notify (G_OBJECT (label));
+  g_object_notify (G_OBJECT (label), "cursor_position");
+  g_object_notify (G_OBJECT (label), "selection_bound");
+  g_object_thaw_notify (G_OBJECT (label));
+}
+
+/**
+ * eel_editable_label_select_region:
+ * @label: a #EelEditableLabel
+ * @start_offset: start offset (in characters not bytes)
+ * @end_offset: end offset (in characters not bytes)
+ *
+ * Selects a range of characters in the label, if the label is selectable.
+ * See eel_editable_label_set_selectable(). If the label is not selectable,
+ * this function has no effect. If @start_offset or
+ * @end_offset are -1, then the end of the label will be substituted.
+ * 
+ **/
+void
+eel_editable_label_select_region  (EelEditableLabel *label,
+				   gint      start_offset,
+				   gint      end_offset)
+{
+  g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
+  
+  if (label->text)
+    {
+      if (start_offset < 0)
+        start_offset = g_utf8_strlen (label->text, -1);
+      
+      if (end_offset < 0)
+        end_offset = g_utf8_strlen (label->text, -1);
+      
+      eel_editable_label_select_region_index (label,
+					      g_utf8_offset_to_pointer (label->text, start_offset) - label->text,
+					      g_utf8_offset_to_pointer (label->text, end_offset) - label->text);
+    }
+}
+
+/**
+ * eel_editable_label_get_selection_bounds:
+ * @label: a #EelEditableLabel
+ * @start: return location for start of selection, as a character offset
+ * @end: return location for end of selection, as a character offset
+ * 
+ * Gets the selected range of characters in the label, returning %TRUE
+ * if there's a selection.
+ * 
+ * Return value: %TRUE if selection is non-empty
+ **/
+gboolean
+eel_editable_label_get_selection_bounds (EelEditableLabel  *label,
+					 gint      *start,
+					 gint      *end)
+{
+  gint start_index, end_index;
+  gint start_offset, end_offset;
+  gint len;
+  
+  g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), FALSE);
+
+      
+  start_index = MIN (label->selection_anchor,
+		     label->selection_end);
+  end_index = MAX (label->selection_anchor,
+		   label->selection_end);
+
+  len = strlen (label->text);
+  
+  if (end_index > len)
+    end_index = len;
+  
+  if (start_index > len)
+    start_index = len;
+  
+  start_offset = g_utf8_strlen (label->text, start_index);
+  end_offset = g_utf8_strlen (label->text, end_index);
+  
+  if (start_offset > end_offset)
+    {
+      gint tmp = start_offset;
+      start_offset = end_offset;
+      end_offset = tmp;
+    }
+  
+  if (start)
+    *start = start_offset;
+  
+  if (end)
+    *end = end_offset;
+  
+  return start_offset != end_offset;
+}
+
+
+/**
+ * eel_editable_label_get_layout:
+ * @label: a #EelEditableLabel
+ * 
+ * Gets the #PangoLayout used to display the label.
+ * The layout is useful to e.g. convert text positions to
+ * pixel positions, in combination with eel_editable_label_get_layout_offsets().
+ * The returned layout is owned by the label so need not be
+ * freed by the caller.
+ * 
+ * Return value: the #PangoLayout for this label
+ **/
+PangoLayout*
+eel_editable_label_get_layout (EelEditableLabel *label)
+{
+  g_return_val_if_fail (EEL_IS_EDITABLE_LABEL (label), NULL);
+
+  eel_editable_label_ensure_layout (label, TRUE);
+
+  return label->layout;
+}
+
+/**
+ * eel_editable_label_get_layout_offsets:
+ * @label: a #EelEditableLabel
+ * @x: location to store X offset of layout, or %NULL
+ * @y: location to store Y offset of layout, or %NULL
+ *
+ * Obtains the coordinates where the label will draw the #PangoLayout
+ * representing the text in the label; useful to convert mouse events
+ * into coordinates inside the #PangoLayout, e.g. to take some action
+ * if some part of the label is clicked. Of course you will need to
+ * create a #GtkEventBox to receive the events, and pack the label
+ * inside it, since labels are a #GTK_NO_WINDOW widget. Remember
+ * when using the #PangoLayout functions you need to convert to
+ * and from pixels using PANGO_PIXELS() or #PANGO_SCALE.
+ * 
+ **/
+void
+eel_editable_label_get_layout_offsets (EelEditableLabel *label,
+				       gint     *x,
+				       gint     *y)
+{
+  g_return_if_fail (EEL_IS_EDITABLE_LABEL (label));
+  
+  get_layout_location (label, x, y);
+}
+
+static void
+eel_editable_label_pend_cursor_blink (EelEditableLabel *label)
+{
+  /* TODO */
+}
+
+static void
+eel_editable_label_check_cursor_blink (EelEditableLabel *label)
+{
+  /* TODO */
+}
+
+static gint
+eel_editable_label_key_press (GtkWidget   *widget,
+			      GdkEventKey *event)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
+
+  eel_editable_label_pend_cursor_blink (label);
+
+  if (gtk_im_context_filter_keypress (label->im_context, event))
+    {
+      /*TODO eel_editable_label_obscure_mouse_cursor (label);*/
+      label->need_im_reset = TRUE;
+      return TRUE;
+    }
+
+  if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+    /* Activate key bindings
+     */
+    return TRUE;
+
+  return FALSE;
+}
+
+static gint
+eel_editable_label_key_release (GtkWidget   *widget,
+				GdkEventKey *event)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
+
+  if (gtk_im_context_filter_keypress (label->im_context, event))
+    {
+      label->need_im_reset = TRUE;
+      return TRUE;
+    }
+
+  return GTK_WIDGET_CLASS (parent_class)->key_release_event (widget, event);
+}
+
+static void
+eel_editable_label_keymap_direction_changed (GdkKeymap *keymap,
+					     EelEditableLabel  *label)
+{
+  gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+static gint
+eel_editable_label_focus_in (GtkWidget     *widget,
+			     GdkEventFocus *event)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
+  
+  gtk_widget_queue_draw (widget);
+  
+  label->need_im_reset = TRUE;
+  gtk_im_context_focus_in (label->im_context);
+
+  g_signal_connect (gdk_keymap_get_default (),
+		    "direction_changed",
+		    G_CALLBACK (eel_editable_label_keymap_direction_changed), label);
+
+  eel_editable_label_check_cursor_blink (label);
+
+  return FALSE;
+}
+
+static gint
+eel_editable_label_focus_out (GtkWidget     *widget,
+			      GdkEventFocus *event)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (widget);
+  
+  gtk_widget_queue_draw (widget);
+
+  label->need_im_reset = TRUE;
+  gtk_im_context_focus_out (label->im_context);
+
+  eel_editable_label_check_cursor_blink (label);
+  
+  g_signal_handlers_disconnect_by_func (gdk_keymap_get_default (),
+                                        (gpointer) eel_editable_label_keymap_direction_changed,
+                                        label);
+  
+  return FALSE;
+}
+
+static void
+eel_editable_label_delete_text (EelEditableLabel *label,
+				int start_pos,
+				int end_pos)
+{
+  int anchor, end;
+  
+  if (start_pos < 0)
+    start_pos = 0;
+  if (end_pos < 0 || end_pos > label->n_bytes)
+    end_pos = label->n_bytes;
+  
+  if (start_pos < end_pos)
+    {
+      g_memmove (label->text + start_pos, label->text + end_pos, label->n_bytes + 1 - end_pos);
+      label->n_bytes -= (end_pos - start_pos);
+
+      anchor = label->selection_anchor;
+      if (anchor > start_pos)
+	anchor -= MIN (anchor, end_pos) - start_pos;
+
+      end = label->selection_end;
+      if (end > start_pos)
+	end -= MIN (end, end_pos) - start_pos;
+      
+      /* We might have changed the selection */
+      eel_editable_label_select_region_index (label, anchor, end);
+      
+      eel_editable_label_recompute (label);  
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+      
+      g_object_notify (G_OBJECT (label), "text");
+      g_signal_emit_by_name (GTK_EDITABLE (label), "changed");
+    }
+}
+
+static void
+eel_editable_label_insert_text (EelEditableLabel *label,
+				const gchar *new_text,
+				gint         new_text_length,
+				gint        *index)
+{
+  if (new_text_length + label->n_bytes + 1 > label->text_size)
+    {
+      while (new_text_length + label->n_bytes + 1 > label->text_size)
+	{
+	  if (label->text_size == 0)
+	    label->text_size = 16;
+	  else
+	    label->text_size *= 2;
+	}
+
+      label->text = g_realloc (label->text, label->text_size);
+    }
+
+  g_object_freeze_notify (G_OBJECT (label));
+
+  g_memmove (label->text + *index + new_text_length, label->text + *index, label->n_bytes - *index);
+  memcpy (label->text + *index, new_text, new_text_length);
+  
+  label->n_bytes += new_text_length;
+
+  /* NUL terminate for safety and convenience */
+  label->text[label->n_bytes] = '\0';
+
+  g_object_notify (G_OBJECT (label), "text");
+
+  if (label->selection_anchor > *index)
+    {
+      g_object_notify (G_OBJECT (label), "cursor_position");
+      g_object_notify (G_OBJECT (label), "selection_bound");
+      label->selection_anchor += new_text_length;
+    }
+  
+  if (label->selection_end > *index)
+    {
+      label->selection_end += new_text_length;
+      g_object_notify (G_OBJECT (label), "selection_bound");
+    }
+
+  *index += new_text_length;
+
+  eel_editable_label_recompute (label);  
+  gtk_widget_queue_resize (GTK_WIDGET (label));
+
+  g_object_thaw_notify (G_OBJECT (label));
+  g_signal_emit_by_name (GTK_EDITABLE (label), "changed");
+}
+
+/* Used for im_commit_cb and inserting Unicode chars */
+static void
+eel_editable_label_enter_text (EelEditableLabel *label,
+			       const gchar    *str)
+{
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint tmp_pos;
+  gboolean old_need_im_reset;
+
+  /* Never reset the im while commiting, as that resets possible im state */
+  old_need_im_reset = label->need_im_reset;
+  label->need_im_reset = FALSE;
+
+  if (label->selection_end != label->selection_anchor)
+    gtk_editable_delete_selection (editable);
+  else
+    {
+      if (label->overwrite_mode)
+        eel_editable_label_delete_from_cursor (label, GTK_DELETE_CHARS, 1);
+    }
+  
+  tmp_pos = g_utf8_pointer_to_offset (label->text,
+                                      label->text + label->selection_anchor);
+  gtk_editable_insert_text (GTK_EDITABLE (label), str, strlen (str), &tmp_pos);
+  tmp_pos = g_utf8_offset_to_pointer (label->text, tmp_pos) - label->text;
+  eel_editable_label_select_region_index (label, tmp_pos, tmp_pos);
+
+  label->need_im_reset = old_need_im_reset;
+}
+
+/* IM Context Callbacks
+ */
+
+static void
+eel_editable_label_commit_cb (GtkIMContext *context,
+			      const gchar  *str,
+			      EelEditableLabel  *label)
+{
+  eel_editable_label_enter_text (label, str);
+}
+
+static void 
+eel_editable_label_preedit_changed_cb (GtkIMContext *context,
+				       EelEditableLabel  *label)
+{
+  gchar *preedit_string;
+  gint cursor_pos;
+  
+  gtk_im_context_get_preedit_string (label->im_context,
+				     &preedit_string, NULL,
+				     &cursor_pos);
+  label->preedit_length = strlen (preedit_string);
+  cursor_pos = CLAMP (cursor_pos, 0, g_utf8_strlen (preedit_string, -1));
+  label->preedit_cursor = cursor_pos;
+  g_free (preedit_string);
+
+  eel_editable_label_recompute (label);  
+  gtk_widget_queue_resize (GTK_WIDGET (label));
+}
+
+static gboolean
+eel_editable_label_retrieve_surrounding_cb (GtkIMContext *context,
+					    EelEditableLabel  *label)
+{
+  gtk_im_context_set_surrounding (context,
+				  label->text,
+				  strlen (label->text) + 1,
+				  label->selection_end);
+
+  return TRUE;
+}
+
+static gboolean
+eel_editable_label_delete_surrounding_cb (GtkIMContext *slave,
+					  gint          offset,
+					  gint          n_chars,
+					  EelEditableLabel  *label)
+{
+  gint current_pos;
+
+  current_pos = g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
+  gtk_editable_delete_text (GTK_EDITABLE (label), 
+			    current_pos + offset, 
+			    current_pos + offset + n_chars);
+
+  return TRUE;
+}
+
+static gboolean
+eel_editable_label_focus (GtkWidget         *widget,
+			  GtkDirectionType   direction)
+{
+  /* We never want to be in the tab chain */
+  return FALSE;
+}
+
+/* Compute the X position for an offset that corresponds to the "more important
+ * cursor position for that offset. We use this when trying to guess to which
+ * end of the selection we should go to when the user hits the left or
+ * right arrow key.
+ */
+static void
+get_better_cursor (EelEditableLabel *label,
+		   gint      index,
+		   gint      *x,
+		   gint      *y)
+{
+  GtkTextDirection keymap_direction =
+    (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
+    GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
+  GtkTextDirection widget_direction = gtk_widget_get_direction (GTK_WIDGET (label));
+  gboolean split_cursor;
+  PangoRectangle strong_pos, weak_pos;
+  
+  g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
+		"gtk-split-cursor", &split_cursor,
+		NULL);
+
+  eel_editable_label_get_cursor_pos (label, &strong_pos, &weak_pos);
+
+  if (split_cursor)
+    {
+      *x = strong_pos.x / PANGO_SCALE;
+      *y = strong_pos.y / PANGO_SCALE;
+    }
+  else
+    {
+      if (keymap_direction == widget_direction)
+	{
+	  *x = strong_pos.x / PANGO_SCALE;
+	  *y = strong_pos.y / PANGO_SCALE;
+	}
+      else
+	{
+	  *x = weak_pos.x / PANGO_SCALE;
+	  *y = weak_pos.y / PANGO_SCALE;
+	}
+    }
+}
+
+
+static gint
+eel_editable_label_move_logically (EelEditableLabel *label,
+				   gint      start,
+				   gint      count)
+{
+  gint offset = g_utf8_pointer_to_offset (label->text,
+					  label->text + start);
+
+  if (label->text)
+    {
+      PangoLogAttr *log_attrs;
+      gint n_attrs;
+      gint length;
+
+      eel_editable_label_ensure_layout (label, FALSE);
+      
+      length = g_utf8_strlen (label->text, -1);
+
+      pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
+
+      while (count > 0 && offset < length)
+	{
+	  do
+	    offset++;
+	  while (offset < length && !log_attrs[offset].is_cursor_position);
+	  
+	  count--;
+	}
+      while (count < 0 && offset > 0)
+	{
+	  do
+	    offset--;
+	  while (offset > 0 && !log_attrs[offset].is_cursor_position);
+	  
+	  count++;
+	}
+      
+      g_free (log_attrs);
+    }
+
+  return g_utf8_offset_to_pointer (label->text, offset) - label->text;
+}
+
+static gint
+eel_editable_label_move_visually (EelEditableLabel *label,
+				  gint      start,
+				  gint      count)
+{
+  gint index;
+
+  index = start;
+  
+  while (count != 0)
+    {
+      int new_index, new_trailing;
+      gboolean split_cursor;
+      gboolean strong;
+
+      eel_editable_label_ensure_layout (label, FALSE);
+
+      g_object_get (gtk_widget_get_settings (GTK_WIDGET (label)),
+		    "gtk-split-cursor", &split_cursor,
+		    NULL);
+
+      if (split_cursor)
+	strong = TRUE;
+      else
+	{
+	  GtkTextDirection keymap_direction =
+	    (gdk_keymap_get_direction (gdk_keymap_get_default ()) == PANGO_DIRECTION_LTR) ?
+	    GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
+
+	  strong = keymap_direction == gtk_widget_get_direction (GTK_WIDGET (label));
+	}
+      
+      if (count > 0)
+	{
+	  pango_layout_move_cursor_visually (label->layout, strong, index, 0, 1, &new_index, &new_trailing);
+	  count--;
+	}
+      else
+	{
+	  pango_layout_move_cursor_visually (label->layout, strong, index, 0, -1, &new_index, &new_trailing);
+	  count++;
+	}
+
+      if (new_index < 0 || new_index == G_MAXINT)
+	break;
+
+      index = new_index;
+      
+      while (new_trailing--)
+	index = g_utf8_next_char (label->text + new_index) - label->text;
+    }
+  
+  return index;
+}
+
+static gint
+eel_editable_label_move_line (EelEditableLabel *label,
+			      gint      start,
+			      gint      count)
+{
+  int n_lines, i;
+  int x;
+  PangoLayoutLine *line;
+  int index;
+  
+  eel_editable_label_ensure_layout (label, FALSE);
+
+  n_lines = pango_layout_get_line_count (label->layout);
+
+  for (i = 0; i < n_lines; i++)
+    {
+      line = pango_layout_get_line (label->layout, i);
+      if (start >= line->start_index &&
+	  start <= line->start_index + line->length)
+	{
+	  pango_layout_line_index_to_x (line, start, FALSE, &x);
+	  break;
+	}
+    }
+  if (i == n_lines)
+    i = n_lines - 1;
+  
+  i += count;
+  i = CLAMP (i, 0, n_lines - 1);
+
+  line = pango_layout_get_line (label->layout, i);
+  if (pango_layout_line_x_to_index (line,
+				    x,
+				    &index, NULL))
+    return index;
+  else
+    {
+      if (i == n_lines - 1)
+	return line->start_index + line->length;
+      else
+	return line->start_index + line->length - 1;
+    }
+}
+
+static gint
+eel_editable_label_move_forward_word (EelEditableLabel *label,
+				      gint      start)
+{
+  gint new_pos = g_utf8_pointer_to_offset (label->text,
+					   label->text + start);
+  gint length;
+
+  length = g_utf8_strlen (label->text, -1);
+  if (new_pos < length)
+    {
+      PangoLogAttr *log_attrs;
+      gint n_attrs;
+
+      eel_editable_label_ensure_layout (label, FALSE);
+      
+      pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
+      
+      /* Find the next word end */
+      new_pos++;
+      while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end)
+	new_pos++;
+
+      g_free (log_attrs);
+    }
+
+  return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
+}
+
+
+static gint
+eel_editable_label_move_backward_word (EelEditableLabel *label,
+				       gint      start)
+{
+  gint new_pos = g_utf8_pointer_to_offset (label->text,
+					   label->text + start);
+  
+  if (new_pos > 0)
+    {
+      PangoLogAttr *log_attrs;
+      gint n_attrs;
+
+      eel_editable_label_ensure_layout (label, FALSE);
+      
+      pango_layout_get_log_attrs (label->layout, &log_attrs, &n_attrs);
+      
+      new_pos -= 1;
+
+      /* Find the previous word beginning */
+      while (new_pos > 0 && !log_attrs[new_pos].is_word_start)
+	new_pos--;
+
+      g_free (log_attrs);
+    }
+
+  return g_utf8_offset_to_pointer (label->text, new_pos) - label->text;
+}
+
+static void
+eel_editable_label_move_cursor (EelEditableLabel    *label,
+				GtkMovementStep      step,
+				gint                 count,
+				gboolean             extend_selection)
+{
+  gint new_pos;
+  
+  new_pos = label->selection_end;
+
+  if (label->selection_end != label->selection_anchor &&
+      !extend_selection)
+    {
+      /* If we have a current selection and aren't extending it, move to the
+       * start/or end of the selection as appropriate
+       */
+      switch (step)
+	{
+	case GTK_MOVEMENT_DISPLAY_LINES:
+	case GTK_MOVEMENT_VISUAL_POSITIONS:
+	  {
+	    gint end_x, end_y;
+	    gint anchor_x, anchor_y;
+	    gboolean end_is_left;
+	    
+	    get_better_cursor (label, label->selection_end, &end_x, &end_y);
+	    get_better_cursor (label, label->selection_anchor, &anchor_x, &anchor_y);
+
+	    end_is_left = (end_y < anchor_y) || (end_y == anchor_y && end_x < anchor_x);
+	    
+	    if (count < 0)
+	      new_pos = end_is_left ? label->selection_end : label->selection_anchor;
+	    else
+	      new_pos = !end_is_left ? label->selection_end : label->selection_anchor;
+
+	    break;
+	  }
+	case GTK_MOVEMENT_LOGICAL_POSITIONS:
+	case GTK_MOVEMENT_WORDS:
+	  if (count < 0)
+	    new_pos = MIN (label->selection_end, label->selection_anchor);
+	  else
+	    new_pos = MAX (label->selection_end, label->selection_anchor);
+	  break;
+	case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+	case GTK_MOVEMENT_PARAGRAPH_ENDS:
+	case GTK_MOVEMENT_BUFFER_ENDS:
+	  /* FIXME: Can do better here */
+	  new_pos = count < 0 ? 0 : strlen (label->text);
+	  break;
+	case GTK_MOVEMENT_PARAGRAPHS:
+	case GTK_MOVEMENT_PAGES:
+	  break;
+	default:
+	  g_assert_not_reached ();
+	  break;
+	}
+    }
+  else
+    {
+      switch (step)
+	{
+	case GTK_MOVEMENT_LOGICAL_POSITIONS:
+	  new_pos = eel_editable_label_move_logically (label, new_pos, count);
+	  break;
+	case GTK_MOVEMENT_VISUAL_POSITIONS:
+	  new_pos = eel_editable_label_move_visually (label, new_pos, count);
+	  break;
+	case GTK_MOVEMENT_WORDS:
+	  while (count > 0)
+	    {
+	      new_pos = eel_editable_label_move_forward_word (label, new_pos);
+	      count--;
+	    }
+	  while (count < 0)
+	    {
+	      new_pos = eel_editable_label_move_backward_word (label, new_pos);
+	      count++;
+	    }
+	  break;
+	case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+	case GTK_MOVEMENT_PARAGRAPH_ENDS:
+	case GTK_MOVEMENT_BUFFER_ENDS:
+	  /* FIXME: Can do better here */
+	  new_pos = count < 0 ? 0 : strlen (label->text);
+	  break;
+	case GTK_MOVEMENT_DISPLAY_LINES:
+	  new_pos = eel_editable_label_move_line (label, new_pos, count);
+	  break;
+	  break;
+	case GTK_MOVEMENT_PARAGRAPHS:
+	case GTK_MOVEMENT_PAGES:
+	  break;
+	default:
+	  g_assert_not_reached ();
+	  break;
+	}
+    }
+
+  if (extend_selection)
+    eel_editable_label_select_region_index (label,
+					    label->selection_anchor,
+					    new_pos);
+  else
+    eel_editable_label_select_region_index (label, new_pos, new_pos);
+}
+
+static void
+eel_editable_label_reset_im_context (EelEditableLabel  *label)
+{
+  if (label->need_im_reset)
+    {
+      label->need_im_reset = 0;
+      gtk_im_context_reset (label->im_context);
+    }
+}
+
+
+static void
+eel_editable_label_delete_from_cursor (EelEditableLabel *label,
+				       GtkDeleteType     type,
+				       gint              count)
+{
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint start_pos = label->selection_anchor;
+  gint end_pos = label->selection_anchor;
+  
+  eel_editable_label_reset_im_context (label);
+
+  if (label->selection_anchor != label->selection_end)
+    {
+      gtk_editable_delete_selection (editable);
+      return;
+    }
+  
+  switch (type)
+    {
+    case GTK_DELETE_CHARS:
+      end_pos = eel_editable_label_move_logically (label, start_pos, count);
+      start_pos = g_utf8_pointer_to_offset (label->text, label->text + start_pos);
+      end_pos = g_utf8_pointer_to_offset (label->text, label->text + end_pos);
+      gtk_editable_delete_text (GTK_EDITABLE (label), MIN (start_pos, end_pos), MAX (start_pos, end_pos));
+      break;
+    case GTK_DELETE_WORDS:
+      if (count < 0)
+	{
+	  /* Move to end of current word, or if not on a word, end of previous word */
+	  end_pos = eel_editable_label_move_backward_word (label, end_pos);
+	  end_pos = eel_editable_label_move_forward_word (label, end_pos);
+	}
+      else if (count > 0)
+	{
+	  /* Move to beginning of current word, or if not on a word, begining of next word */
+	  start_pos = eel_editable_label_move_forward_word (label, start_pos);
+	  start_pos = eel_editable_label_move_backward_word (label, start_pos);
+	}
+	
+      /* Fall through */
+    case GTK_DELETE_WORD_ENDS:
+      while (count < 0)
+	{
+	  start_pos = eel_editable_label_move_backward_word (label, start_pos);
+	  count++;
+	}
+      while (count > 0)
+	{
+	  end_pos = eel_editable_label_move_forward_word (label, end_pos);
+	  count--;
+	}
+      start_pos = g_utf8_pointer_to_offset (label->text, label->text + start_pos);
+      end_pos = g_utf8_pointer_to_offset (label->text, label->text + end_pos);
+ 
+      gtk_editable_delete_text (GTK_EDITABLE (label), start_pos, end_pos);
+      break;
+    case GTK_DELETE_DISPLAY_LINE_ENDS:
+    case GTK_DELETE_PARAGRAPH_ENDS:
+      end_pos = g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
+      if (count < 0)
+	gtk_editable_delete_text (GTK_EDITABLE (label), 0, end_pos);
+      else
+	gtk_editable_delete_text (GTK_EDITABLE (label), end_pos, -1);
+      break;
+    case GTK_DELETE_DISPLAY_LINES:
+    case GTK_DELETE_PARAGRAPHS:
+      gtk_editable_delete_text (GTK_EDITABLE (label), 0, -1);  
+      break;
+    case GTK_DELETE_WHITESPACE:
+      /* TODO eel_editable_label_delete_whitespace (label); */
+      break;
+    }
+  
+  eel_editable_label_pend_cursor_blink (label);
+}
+
+
+static void
+eel_editable_label_copy_clipboard (EelEditableLabel *label)
+{
+  if (label->text)
+    {
+      gint start, end;
+      gint len;
+      
+      start = MIN (label->selection_anchor,
+                   label->selection_end);
+      end = MAX (label->selection_anchor,
+                 label->selection_end);
+
+      len = strlen (label->text);
+
+      if (end > len)
+        end = len;
+
+      if (start > len)
+        start = len;
+
+      if (start != end)
+	gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+				label->text + start, end - start);
+    }
+}
+
+static void
+eel_editable_label_cut_clipboard (EelEditableLabel *label)
+{
+  if (label->text)
+    {
+      gint start, end;
+      gint len;
+      
+      start = MIN (label->selection_anchor,
+                   label->selection_end);
+      end = MAX (label->selection_anchor,
+                 label->selection_end);
+
+      len = strlen (label->text);
+
+      if (end > len)
+        end = len;
+
+      if (start > len)
+        start = len;
+
+      if (start != end)
+	{
+	  gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+				  label->text + start, end - start);
+          start = g_utf8_pointer_to_offset (label->text, label->text + start);
+          end = g_utf8_pointer_to_offset (label->text, label->text + end);
+	  gtk_editable_delete_text (GTK_EDITABLE (label), start, end);
+	}
+    }
+}
+
+static void
+paste_received (GtkClipboard *clipboard,
+		const gchar  *text,
+		gpointer      data)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (data);
+  GtkEditable *editable = GTK_EDITABLE (label);
+  gint tmp_pos;
+      
+  if (text)
+    {
+      if (label->selection_end != label->selection_anchor)
+	gtk_editable_delete_selection (editable);
+
+      tmp_pos = g_utf8_pointer_to_offset (label->text,
+                                      label->text + label->selection_anchor);
+      gtk_editable_insert_text (GTK_EDITABLE (label), text, strlen (text), &tmp_pos);
+      tmp_pos = g_utf8_offset_to_pointer (label->text, tmp_pos) - label->text;
+      eel_editable_label_select_region_index (label, tmp_pos, tmp_pos);
+    }
+
+  g_object_unref (G_OBJECT (label));
+}
+
+static void
+eel_editable_label_paste (EelEditableLabel *label,
+			  GdkAtom   selection)
+{
+  g_object_ref (G_OBJECT (label));
+  gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (label), selection),
+			      paste_received, label);
+}
+
+static void
+eel_editable_label_paste_clipboard (EelEditableLabel *label)
+{
+  eel_editable_label_paste (label, GDK_NONE);
+}
+
+static void
+eel_editable_label_select_all (EelEditableLabel *label)
+{
+  eel_editable_label_select_region_index (label, 0, strlen (label->text));
+}
+
+/* Quick hack of a popup menu
+ */
+static void
+activate_cb (GtkWidget *menuitem,
+	     EelEditableLabel  *label)
+{
+  const gchar *signal = g_object_get_data (G_OBJECT (menuitem), "gtk-signal");
+  g_signal_emit_by_name (GTK_OBJECT (label), signal);
+}
+
+static void
+append_action_signal (EelEditableLabel     *label,
+		      GtkWidget    *menu,
+		      const gchar  *stock_id,
+		      const gchar  *signal,
+                      gboolean      sensitive)
+{
+  GtkWidget *menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
+
+  g_object_set_data (G_OBJECT (menuitem), "gtk-signal", (char *)signal);
+  g_signal_connect (GTK_OBJECT (menuitem), "activate",
+		    GTK_SIGNAL_FUNC (activate_cb), label);
+
+  gtk_widget_set_sensitive (menuitem, sensitive);
+  
+  gtk_widget_show (menuitem);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+}
+
+static void
+popup_menu_detach (GtkWidget *attach_widget,
+		   GtkMenu   *menu)
+{
+  EelEditableLabel *label;
+  label = EEL_EDITABLE_LABEL (attach_widget);
+
+  label->popup_menu = NULL;
+}
+
+static void
+popup_position_func (GtkMenu   *menu,
+                     gint      *x,
+                     gint      *y,
+                     gboolean  *push_in,
+                     gpointer	user_data)
+{
+  EelEditableLabel *label;
+  GtkWidget *widget;
+  GtkRequisition req;
+  
+  label = EEL_EDITABLE_LABEL (user_data);  
+  widget = GTK_WIDGET (label);
+
+  g_assert (GTK_WIDGET_REALIZED (label));
+
+  gdk_window_get_origin (widget->window, x, y);      
+
+  /*gtk_widget_size_request (label->popup_menu, &req);*/
+  req = widget->requisition;
+  
+  *x += widget->allocation.width / 2;
+  *y += widget->allocation.height;
+
+  *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
+  *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
+}
+
+static void
+eel_editable_label_toggle_overwrite (EelEditableLabel *label)
+{
+  label->overwrite_mode = !label->overwrite_mode;
+  gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+typedef struct
+{
+  EelEditableLabel *label;
+  gint button;
+  guint time;
+} PopupInfo;
+
+static void
+popup_targets_received (GtkClipboard     *clipboard,
+			GtkSelectionData *data,
+			gpointer          user_data)
+{
+  GtkWidget *menuitem, *submenu;
+  gboolean have_selection;
+  gboolean clipboard_contains_text;
+  PopupInfo *info;
+  EelEditableLabel *label;
+
+  info = user_data;
+  label = info->label;
+
+  if (GTK_WIDGET_REALIZED (label))
+    {
+      if (label->popup_menu)
+	gtk_widget_destroy (label->popup_menu);
+  
+      label->popup_menu = gtk_menu_new ();
+
+      gtk_menu_attach_to_widget (GTK_MENU (label->popup_menu),
+				 GTK_WIDGET (label),
+				 popup_menu_detach);
+
+      have_selection =
+	label->selection_anchor != label->selection_end;
+  
+      clipboard_contains_text = gtk_selection_data_targets_include_text (data);
+
+      append_action_signal (label, label->popup_menu, GTK_STOCK_CUT, "cut_clipboard",
+			    have_selection);
+      append_action_signal (label, label->popup_menu, GTK_STOCK_COPY, "copy_clipboard",
+			    have_selection);
+      append_action_signal (label, label->popup_menu, GTK_STOCK_PASTE, "paste_clipboard",
+			    clipboard_contains_text);
+  
+      menuitem = gtk_menu_item_new_with_label (_("Select All"));
+      g_signal_connect_object (GTK_OBJECT (menuitem), "activate",
+			       GTK_SIGNAL_FUNC (eel_editable_label_select_all), label,
+			       G_CONNECT_SWAPPED);
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (label->popup_menu), menuitem);
+
+      menuitem = gtk_separator_menu_item_new ();
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (label->popup_menu), menuitem);
+      
+      menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
+      gtk_widget_show (menuitem);
+      submenu = gtk_menu_new ();
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
+      
+      gtk_menu_shell_append (GTK_MENU_SHELL (label->popup_menu), menuitem);
+  
+      gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (label->im_context),
+					    GTK_MENU_SHELL (submenu));
+
+      g_signal_emit (GTK_OBJECT (label),
+		     signals[POPULATE_POPUP], 0,
+		     label->popup_menu);
+
+      if (info->button)
+	gtk_menu_popup (GTK_MENU (label->popup_menu), NULL, NULL,
+			NULL, NULL,
+			info->button, info->time);
+      else
+	{
+	  gtk_menu_popup (GTK_MENU (label->popup_menu), NULL, NULL,
+			  popup_position_func, label,
+			  info->button, info->time);
+	  gtk_menu_shell_select_first (GTK_MENU_SHELL (label->popup_menu), FALSE);
+	}
+    }
+
+  g_object_unref (label);
+  g_free (info);
+}
+
+static void
+eel_editable_label_do_popup (EelEditableLabel *label,
+			     GdkEventButton   *event)
+{
+  PopupInfo *info = g_new (PopupInfo, 1);
+
+  /* In order to know what entries we should make sensitive, we
+   * ask for the current targets of the clipboard, and when
+   * we get them, then we actually pop up the menu.
+   */
+  info->label = g_object_ref (label);
+  
+  if (event)
+    {
+      info->button = event->button;
+      info->time = event->time;
+    }
+  else
+    {
+      info->button = 0;
+      info->time = gtk_get_current_event_time ();
+    }
+
+  gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (label), GDK_SELECTION_CLIPBOARD),
+				  gdk_atom_intern ("TARGETS", FALSE),
+				  popup_targets_received,
+				  info);
+}
+
+/************ Editable implementation ****************/
+
+static void
+editable_insert_text_emit  (GtkEditable *editable,
+			    const gchar *new_text,
+			    gint         new_text_length,
+			    gint        *position)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  gchar buf[64];
+  gchar *text;
+  int text_length;
+
+  text_length = g_utf8_strlen (label->text, -1);
+
+  if (*position < 0 || *position > text_length)
+    *position = text_length;
+  
+  g_object_ref (G_OBJECT (editable));
+  
+  if (new_text_length <= 63)
+    text = buf;
+  else
+    text = g_new (gchar, new_text_length + 1);
+
+  text[new_text_length] = '\0';
+  strncpy (text, new_text, new_text_length);
+  
+  g_signal_emit_by_name (editable, "insert_text", text, new_text_length, position);
+
+  if (new_text_length > 63)
+    g_free (text);
+
+  g_object_unref (G_OBJECT (editable));
+}
+
+static void
+editable_delete_text_emit (GtkEditable *editable,
+			   gint         start_pos,
+			   gint         end_pos)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  int text_length;
+
+  text_length = g_utf8_strlen (label->text, -1);
+
+  if (end_pos < 0 || end_pos > text_length)
+    end_pos = text_length;
+  if (start_pos < 0)
+    start_pos = 0;
+  if (start_pos > end_pos)
+    start_pos = end_pos;
+  
+  g_object_ref (G_OBJECT (editable));
+
+  g_signal_emit_by_name (editable, "delete_text", start_pos, end_pos);
+
+  g_object_unref (G_OBJECT (editable));
+}
+
+static void
+editable_insert_text (GtkEditable *editable,
+		      const gchar *new_text,
+		      gint         new_text_length,
+		      gint        *position)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  gint index;
+
+  if (new_text_length < 0)
+    new_text_length = strlen (new_text);
+
+  index = g_utf8_offset_to_pointer (label->text, *position) - label->text;
+
+  eel_editable_label_insert_text (label,
+				  new_text,
+				  new_text_length,
+				  &index);
+  
+  *position = g_utf8_pointer_to_offset (label->text, label->text + index);
+}
+
+static void
+editable_delete_text (GtkEditable *editable,
+		      gint         start_pos,
+		      gint         end_pos)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  int text_length;
+  gint start_index, end_index;
+
+  text_length = g_utf8_strlen (label->text, -1);
+
+  if (end_pos < 0 || end_pos > text_length)
+    end_pos = text_length;
+  if (start_pos < 0)
+    start_pos = 0;
+  if (start_pos > end_pos)
+    start_pos = end_pos;
+
+  start_index = g_utf8_offset_to_pointer (label->text, start_pos) - label->text;
+  end_index = g_utf8_offset_to_pointer (label->text, end_pos) - label->text;
+  
+  eel_editable_label_delete_text (label, start_index, end_index);
+}
+
+static gchar *    
+editable_get_chars (GtkEditable   *editable,
+		    gint           start_pos,
+		    gint           end_pos)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  int text_length;
+  gint start_index, end_index;
+  
+  text_length = g_utf8_strlen (label->text, -1);
+
+  if (end_pos < 0 || end_pos > text_length)
+    end_pos = text_length;
+  if (start_pos < 0)
+    start_pos = 0;
+  if (start_pos > end_pos)
+    start_pos = end_pos;
+
+  start_index = g_utf8_offset_to_pointer (label->text, start_pos) - label->text;
+  end_index = g_utf8_offset_to_pointer (label->text, end_pos) - label->text;
+
+  return g_strndup (label->text + start_index, end_index - start_index);
+}
+
+static void
+editable_set_selection_bounds (GtkEditable *editable,
+			       gint         start,
+			       gint         end)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  int text_length;
+  gint start_index, end_index;
+  
+  text_length = g_utf8_strlen (label->text, -1);
+
+  if (end < 0 || end > text_length)
+    end = text_length;
+  if (start < 0)
+    start = text_length;
+  if (start > text_length)
+    start = text_length;
+  
+  eel_editable_label_reset_im_context (label);
+
+  start_index = g_utf8_offset_to_pointer (label->text, start) - label->text;
+  end_index = g_utf8_offset_to_pointer (label->text, end) - label->text;
+
+  eel_editable_label_select_region_index (label, start_index, end_index);
+}
+
+static gboolean
+editable_get_selection_bounds (GtkEditable *editable,
+			       gint        *start,
+			       gint        *end)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+
+  *start = g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
+  *end = g_utf8_pointer_to_offset (label->text, label->text + label->selection_end);
+
+  return (label->selection_anchor != label->selection_end);
+}
+
+static void
+editable_real_set_position (GtkEditable *editable,
+			    gint         position)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  int text_length;
+  int index;
+  
+  text_length = g_utf8_strlen (label->text, -1);
+  
+  if (position < 0 || position > text_length)
+    position = text_length;
+
+  index = g_utf8_offset_to_pointer (label->text, position) - label->text;
+  
+  if (index != label->selection_anchor ||
+      index != label->selection_end)
+    {
+      eel_editable_label_select_region_index (label, index, index);
+    }
+}
+
+static gint
+editable_get_position (GtkEditable *editable)
+{
+  EelEditableLabel *label = EEL_EDITABLE_LABEL (editable);
+  
+  return g_utf8_pointer_to_offset (label->text, label->text + label->selection_anchor);
+}
+
+
+static AtkObjectClass *a11y_parent_class = NULL;
+
+static const char* eel_editable_label_accessible_data = "eel-editable-label-accessible-data";
+
+/************ Accessible implementation ****************/
+
+typedef struct {
+	GailTextUtil *textutil;
+        gint         selection_anchor;
+        gint         selection_end;
+        gchar        *signal_name;
+        gint         position;
+        gint         length;
+} EelEditableLabelAccessiblePrivate;
+
+typedef struct
+{
+  EelEditableLabel* label;
+  gint position;
+} EelEditableLabelAccessiblePaste;
+
+
+static gchar*
+eel_editable_label_accessible_get_text (AtkText *text,
+					gint    start_pos,
+					gint    end_pos)
+{
+  GtkWidget *widget;
+  EelEditableLabelAccessiblePrivate *priv;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+  /* State is defunct */
+    return NULL;
+
+  priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
+  return gail_text_util_get_substring (priv->textutil, start_pos, end_pos);
+}
+
+static gunichar 
+eel_editable_label_accessible_get_character_at_offset (AtkText *text,
+						       gint     offset)
+{
+  GtkWidget *widget;
+  EelEditableLabelAccessiblePrivate *priv;
+  gchar *string;
+  gchar *index;
+  gunichar unichar;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return '\0';
+
+  priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
+  string = gail_text_util_get_substring (priv->textutil, 0, -1);
+  if (offset >= g_utf8_strlen (string, -1))
+    {
+      unichar = '\0';
+    }
+  else
+    {
+      index = g_utf8_offset_to_pointer (string, offset);
+
+      unichar = g_utf8_get_char(index);
+    }
+
+  g_free(string);
+  return unichar;
+}
+
+static gchar*
+eel_editable_label_accessible_get_text_before_offset (AtkText         *text,
+						      gint            offset,
+						      AtkTextBoundary boundary_type,
+						      gint            *start_offset,
+						      gint            *end_offset)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  EelEditableLabelAccessiblePrivate *priv;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+  /* State is defunct */
+    return NULL;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
+
+  return gail_text_util_get_text (priv->textutil,
+                                  eel_editable_label_get_layout (label),
+                                  GAIL_BEFORE_OFFSET, 
+                                  boundary_type, offset,
+                                  start_offset, end_offset);
+}
+
+static gchar*
+eel_editable_label_accessible_get_text_at_offset (AtkText          *text,
+						  gint             offset,
+						  AtkTextBoundary  boundary_type,
+						  gint             *start_offset,
+    						  gint             *end_offset)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  EelEditableLabelAccessiblePrivate *priv;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return NULL;
+
+
+  label = EEL_EDITABLE_LABEL (widget);
+  priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
+  return gail_text_util_get_text (priv->textutil,
+                                  eel_editable_label_get_layout (label),
+                                  GAIL_AT_OFFSET, 
+                                  boundary_type, offset,
+                                  start_offset, end_offset);
+}
+
+static gchar*
+eel_editable_label_accessible_get_text_after_offset  (AtkText          *text,
+						      gint             offset,
+						      AtkTextBoundary  boundary_type,
+						      gint             *start_offset,
+						      gint             *end_offset)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  EelEditableLabelAccessiblePrivate *priv;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return NULL;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  priv = g_object_get_data (G_OBJECT (text), eel_editable_label_accessible_data);
+  return gail_text_util_get_text (priv->textutil,
+                                  eel_editable_label_get_layout (label),
+                                  GAIL_AFTER_OFFSET, 
+                                  boundary_type, offset,
+                                  start_offset, end_offset);
+}
+
+static gint
+eel_editable_label_accessible_get_caret_offset (AtkText *text)
+{
+  GtkWidget *widget;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return 0;
+
+  return gtk_editable_get_position (GTK_EDITABLE (widget));
+}
+
+static gboolean
+eel_editable_label_accessible_set_caret_offset (AtkText *text, gint offset)
+{
+  GtkWidget *widget;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return FALSE;
+
+  gtk_editable_set_position (GTK_EDITABLE (widget), offset);
+  return TRUE;
+}
+
+static gint
+eel_editable_label_accessible_get_character_count (AtkText *text)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return 0;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  return g_utf8_strlen (eel_editable_label_get_text (label), -1);
+}
+
+static gint
+eel_editable_label_accessible_get_n_selections (AtkText *text)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  gint select_start, select_end;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return -1;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start, 
+                                     &select_end);
+
+  if (select_start != select_end)
+    return 1;
+  else
+    return 0;
+}
+
+static gchar*
+eel_editable_label_accessible_get_selection (AtkText *text,
+					    gint    selection_num,
+					    gint    *start_pos,
+					    gint    *end_pos)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return NULL;
+
+ /* Only let the user get the selection if one is set, and if the
+  * selection_num is 0.
+  */
+  if (selection_num != 0)
+     return NULL;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (label), start_pos, end_pos);
+
+  if (*start_pos != *end_pos)
+     return gtk_editable_get_chars (GTK_EDITABLE (label), *start_pos, *end_pos);
+  else
+     return NULL;
+}
+
+static gboolean
+eel_editable_label_accessible_add_selection (AtkText *text,
+					     gint    start_pos,
+					     gint    end_pos)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  gint select_start, select_end;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return FALSE;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start, 
+                                     &select_end);
+
+ /* If there is already a selection, then don't allow another to be added,
+  * since EelEditableLabel only supports one selected region.
+  */
+  if (select_start == select_end)
+    {
+       gtk_editable_select_region (GTK_EDITABLE (label), start_pos, end_pos);
+       return TRUE;
+    }
+  else
+   return FALSE;
+}
+
+static gboolean
+eel_editable_label_accessible_remove_selection (AtkText *text,
+						gint    selection_num)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  gint select_start, select_end, caret_pos;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return FALSE;
+
+  if (selection_num != 0)
+     return FALSE;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start, 
+                                     &select_end);
+
+  if (select_start != select_end)
+    {
+     /* Setting the start & end of the selected region to the caret position
+      * turns off the selection.
+      */
+      caret_pos = gtk_editable_get_position (GTK_EDITABLE (label));
+      gtk_editable_select_region (GTK_EDITABLE (label), caret_pos, caret_pos);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+eel_editable_label_accessible_set_selection (AtkText *text,
+					     gint    selection_num,
+					     gint    start_pos,
+					     gint    end_pos)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  gint select_start, select_end;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return FALSE;
+
+ /* Only let the user move the selection if one is set, and if the
+  * selection_num is 0
+  */
+  if (selection_num != 0)
+     return FALSE;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (label), &select_start, 
+                                     &select_end);
+
+  if (select_start != select_end)
+    {
+      gtk_editable_select_region (GTK_EDITABLE (label), start_pos, end_pos);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static AtkAttributeSet*
+eel_editable_label_accessible_get_run_attributes (AtkText *text,
+						  gint    offset,
+						  gint    *start_offset,
+						  gint    *end_offset)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  AtkAttributeSet *at_set = NULL;
+  GtkTextDirection dir;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return NULL;
+
+  label = EEL_EDITABLE_LABEL (widget);
+ 
+  dir = gtk_widget_get_direction (widget);
+  if (dir == GTK_TEXT_DIR_RTL)
+    {
+      at_set = gail_misc_add_attribute (at_set,
+                                        ATK_TEXT_ATTR_DIRECTION,
+       g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir)));
+    }
+
+  at_set = gail_misc_layout_get_run_attributes (at_set,
+                                                eel_editable_label_get_layout (label),
+                                                label->text,
+                                                offset,
+                                                start_offset,
+                                                end_offset);
+  return at_set;
+}
+
+static AtkAttributeSet*
+eel_editable_label_accessible_get_default_attributes (AtkText *text)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  AtkAttributeSet *at_set = NULL;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return NULL;
+
+  label = EEL_EDITABLE_LABEL (widget);
+
+  at_set = gail_misc_get_default_attributes (at_set,
+                                             eel_editable_label_get_layout (label),
+                                             widget);
+  return at_set;
+}
+
+static void
+eel_editable_label_accessible_get_character_extents (AtkText      *text,
+						     gint         offset,
+						     gint         *x,
+						     gint         *y,
+						     gint         *width,
+						     gint         *height,
+						     AtkCoordType coords)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  PangoRectangle char_rect;
+  gint index, cursor_index, x_layout, y_layout;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  eel_editable_label_get_layout_offsets (label, &x_layout, &y_layout);
+  index = g_utf8_offset_to_pointer (label->text, offset) - label->text;
+  cursor_index = label->selection_anchor;
+  if (index > cursor_index)
+    index += label->preedit_length;
+  pango_layout_index_to_pos (eel_editable_label_get_layout(label), index, &char_rect);
+ 
+  gail_misc_get_extents_from_pango_rectangle (widget, &char_rect, 
+                        x_layout, y_layout, x, y, width, height, coords);
+} 
+
+static gint 
+eel_editable_label_accessible_get_offset_at_point (AtkText      *text,
+						   gint         x,
+						   gint         y,
+						   AtkCoordType coords)
+{ 
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  gint index, cursor_index, x_layout, y_layout;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return -1;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  
+  eel_editable_label_get_layout_offsets (label, &x_layout, &y_layout);
+  
+  index = gail_misc_get_index_at_point_in_layout (widget, 
+               eel_editable_label_get_layout(label), x_layout, y_layout, x, y, coords);
+  if (index == -1)
+    {
+      if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW)
+        return g_utf8_strlen (label->text, -1);
+
+      return index;  
+    }
+  else
+    {
+      cursor_index = label->selection_anchor;
+      if (index >= cursor_index && label->preedit_length)
+        {
+          if (index >= cursor_index + label->preedit_length)
+            index -= label->preedit_length;
+          else
+            index = cursor_index;
+        }
+      return g_utf8_pointer_to_offset (label->text, label->text + index);
+    }
+}
+
+static void
+atk_text_interface_init (AtkTextIface *iface)
+{
+  g_assert (iface != NULL);
+
+  iface->get_text = eel_editable_label_accessible_get_text;
+  iface->get_character_at_offset = eel_editable_label_accessible_get_character_at_offset;
+  iface->get_text_before_offset = eel_editable_label_accessible_get_text_before_offset;
+  iface->get_text_at_offset = eel_editable_label_accessible_get_text_at_offset;
+  iface->get_text_after_offset = eel_editable_label_accessible_get_text_after_offset;
+  iface->get_caret_offset = eel_editable_label_accessible_get_caret_offset;
+  iface->set_caret_offset = eel_editable_label_accessible_set_caret_offset;
+  iface->get_character_count = eel_editable_label_accessible_get_character_count;
+  iface->get_n_selections = eel_editable_label_accessible_get_n_selections;
+  iface->get_selection = eel_editable_label_accessible_get_selection;
+  iface->add_selection = eel_editable_label_accessible_add_selection;
+  iface->remove_selection = eel_editable_label_accessible_remove_selection;
+  iface->set_selection = eel_editable_label_accessible_set_selection;
+  iface->get_run_attributes = eel_editable_label_accessible_get_run_attributes;
+  iface->get_default_attributes = eel_editable_label_accessible_get_default_attributes;
+  iface->get_character_extents = eel_editable_label_accessible_get_character_extents;
+  iface->get_offset_at_point = eel_editable_label_accessible_get_offset_at_point;
+}
+
+static void
+eel_editable_label_accessible_set_text_contents (AtkEditableText *text,
+						 const gchar     *string)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return;
+
+  label = EEL_EDITABLE_LABEL (widget);
+
+  eel_editable_label_set_text (label, string);
+}
+
+static void
+eel_editable_label_accessible_insert_text (AtkEditableText *text,
+					   const gchar     *string,
+					   gint            length,
+					   gint            *position)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  GtkEditable *editable;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  editable = GTK_EDITABLE (label);
+
+  gtk_editable_insert_text (editable, string, length, position);
+}
+
+static void
+eel_editable_label_accessible_copy_text   (AtkEditableText *text,
+					   gint            start_pos,
+					   gint            end_pos)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  GtkEditable *editable;
+  gchar *str;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  editable = GTK_EDITABLE (label);
+  str = gtk_editable_get_chars (editable, start_pos, end_pos);
+  gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
+}
+
+static void
+eel_editable_label_accessible_cut_text (AtkEditableText *text,
+					gint            start_pos,
+					gint            end_pos)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  GtkEditable *editable;
+  gchar *str;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  editable = GTK_EDITABLE (label);
+  str = gtk_editable_get_chars (editable, start_pos, end_pos);
+  gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1);
+  gtk_editable_delete_text (editable, start_pos, end_pos);
+}
+
+static void
+eel_editable_label_accessible_delete_text (AtkEditableText *text,
+					   gint            start_pos,
+					   gint            end_pos)
+{
+  GtkWidget *widget;
+  EelEditableLabel *label;
+  GtkEditable *editable;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return;
+
+  label = EEL_EDITABLE_LABEL (widget);
+  editable = GTK_EDITABLE (label);
+
+  gtk_editable_delete_text (editable, start_pos, end_pos);
+}
+
+static void
+eel_editable_label_accessible_paste_received (GtkClipboard *clipboard,
+					      const gchar  *text,
+					      gpointer     data)
+{
+  EelEditableLabelAccessiblePaste* paste_struct = (EelEditableLabelAccessiblePaste *)data;
+
+  if (text)
+    gtk_editable_insert_text (GTK_EDITABLE (paste_struct->label), text, -1,
+       &(paste_struct->position));
+
+  g_object_unref (paste_struct->label);
+}
+
+static void
+eel_editable_label_accessible_paste_text (AtkEditableText *text,
+					  gint            position)
+{
+  GtkWidget *widget;
+  GtkEditable *editable;
+  EelEditableLabelAccessiblePaste paste_struct;
+
+  widget = GTK_ACCESSIBLE (text)->widget;
+  if (widget == NULL)
+    /* State is defunct */
+    return;
+
+  editable = GTK_EDITABLE (widget);
+  if (!gtk_editable_get_editable (editable))
+    return;
+  paste_struct.label = EEL_EDITABLE_LABEL (widget);
+  paste_struct.position = position;
+
+  g_object_ref (paste_struct.label);
+  gtk_clipboard_request_text (gtk_clipboard_get (GDK_NONE),
+    eel_editable_label_accessible_paste_received, &paste_struct);
+}
+
+static void
+atk_editable_text_interface_init (AtkEditableTextIface *iface)
+{
+  g_assert (iface != NULL);
+
+  iface->set_text_contents = eel_editable_label_accessible_set_text_contents;
+  iface->insert_text = eel_editable_label_accessible_insert_text;
+  iface->copy_text = eel_editable_label_accessible_copy_text;
+  iface->cut_text = eel_editable_label_accessible_cut_text;
+  iface->delete_text = eel_editable_label_accessible_delete_text;
+  iface->paste_text = eel_editable_label_accessible_paste_text;
+}
+
+static void
+eel_editable_label_accessible_notify_insert (AtkObject *accessible)
+{
+  EelEditableLabelAccessiblePrivate *priv;
+
+  priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
+  if (priv->signal_name)
+    {
+      g_signal_emit_by_name (accessible, 
+                             priv->signal_name,
+                             priv->position,
+                             priv->length);
+      priv->signal_name = NULL;
+    }
+}
+
+static gboolean
+eel_editable_label_accessible_idle_notify_insert (gpointer data)
+{
+  eel_editable_label_accessible_notify_insert (data);
+  return FALSE;
+}
+
+/* Note arg1 returns the character at the start of the insert.
+ * arg2 returns the number of characters inserted.
+ */
+static void 
+eel_editable_label_accessible_insert_text_cb (EelEditableLabel *label, 
+                                              gchar            *arg1, 
+                                              gint             arg2,
+                                              gpointer         arg3)
+{
+  AtkObject *accessible;
+  EelEditableLabelAccessiblePrivate *priv;
+  gint *position = (gint *) arg3;
+
+  accessible = gtk_widget_get_accessible (GTK_WIDGET (label));
+  priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
+  if (!priv->signal_name)
+    {
+      priv->signal_name = "text_changed::insert";
+      priv->position = *position;
+      priv->length = arg2;
+    }
+  /*
+   * The signal will be emitted when the cursor position is updated.
+   * or in an idle handler if it not updated.
+   */
+   g_idle_add (eel_editable_label_accessible_idle_notify_insert, accessible);
+}
+
+/* Note arg1 returns the start of the delete range, arg2 returns the
+ * end of the delete range if multiple characters are deleted.	
+ */
+static void 
+eel_editable_label_accessible_delete_text_cb (EelEditableLabel *label,
+					      gint             arg1, 
+					      gint             arg2)
+{
+  AtkObject *accessible;
+
+  accessible = gtk_widget_get_accessible (GTK_WIDGET (label));
+
+  /*
+   * Zero length text deleted so ignore
+   */
+  if (arg2 - arg1 == 0)
+    return;
+
+  g_signal_emit_by_name (accessible, "text_changed::delete", arg1, arg2 - arg1);
+}
+
+static void
+eel_editable_label_accessible_changed_cb (EelEditableLabel *label)
+{
+  AtkObject *accessible;
+  EelEditableLabelAccessiblePrivate *priv;
+
+  accessible = gtk_widget_get_accessible (GTK_WIDGET (label));
+  priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
+  gail_text_util_text_setup (priv->textutil, eel_editable_label_get_text (label));
+}
+
+static gboolean 
+check_for_selection_change (AtkObject   *accessible,
+                            GtkWidget   *widget)
+{
+  EelEditableLabelAccessiblePrivate *priv;
+  EelEditableLabel *label;
+  gboolean ret_val = FALSE;
+
+  priv = g_object_get_data (G_OBJECT (accessible), eel_editable_label_accessible_data);
+  label = EEL_EDITABLE_LABEL (widget);
+ 
+  if (label->selection_anchor != label->selection_end)
+    {
+      if (label->selection_anchor != priv->selection_anchor ||
+          label->selection_end != priv->selection_end)
+        /*
+         * This check is here as this function can be called
+         * for notification of selection_end and selection_anchor.
+         * The values of selection_anchor and selection_end may be the same 
+         * for both notifications and we only want to generate one
+         * text_selection_changed signal.
+         */
+        ret_val = TRUE;
+    }
+  else 
+    {
+      /* We had a selection */
+      ret_val = (priv->selection_anchor != priv->selection_end);
+    }
+  priv->selection_anchor = label->selection_anchor;
+  priv->selection_end = label->selection_end;
+
+  return ret_val;
+}
+
+static void
+eel_editable_label_accessible_notify_gtk (GObject    *obj,
+                                          GParamSpec *pspec)
+{
+  GtkWidget *widget;
+  AtkObject *accessible;
+  EelEditableLabel *label;
+
+  widget = GTK_WIDGET (obj);
+  label = EEL_EDITABLE_LABEL (widget);
+  accessible = gtk_widget_get_accessible (widget);
+
+  if (strcmp (pspec->name, "cursor-position") == 0)
+    {
+      eel_editable_label_accessible_notify_insert (accessible);
+      if (check_for_selection_change (accessible, widget))
+        g_signal_emit_by_name (accessible, "text_selection_changed");
+      /*
+       * The label cursor position has moved so generate the signal.
+       */
+      g_signal_emit_by_name (accessible, "text_caret_moved",
+                             g_utf8_pointer_to_offset (label->text,
+					 label->text + label->selection_anchor));
+    }
+  else if (strcmp (pspec->name, "selection-bound") == 0)
+    {
+      eel_editable_label_accessible_notify_insert (accessible);
+
+      if (check_for_selection_change (accessible, widget))
+        g_signal_emit_by_name (accessible, "text_selection_changed");
+    }
+}
+
+static void
+eel_editable_label_accessible_initialize (AtkObject *accessible,
+                                          gpointer   widget)
+{
+  EelEditableLabelAccessiblePrivate *priv;
+  EelEditableLabel *label;
+
+  a11y_parent_class->initialize (accessible, widget);
+
+  label = EEL_EDITABLE_LABEL (widget);
+  priv = g_new0 (EelEditableLabelAccessiblePrivate, 1);
+  priv->textutil = gail_text_util_new ();
+  gail_text_util_text_setup (priv->textutil, eel_editable_label_get_text (EEL_EDITABLE_LABEL (widget)));
+  priv->selection_anchor = label->selection_anchor;
+  priv->selection_end = label->selection_end;
+  g_object_set_data (G_OBJECT (accessible), eel_editable_label_accessible_data, priv);
+  g_signal_connect (widget, "insert-text",
+        G_CALLBACK (eel_editable_label_accessible_insert_text_cb), NULL);
+  g_signal_connect (widget, "delete-text",
+        G_CALLBACK (eel_editable_label_accessible_delete_text_cb), NULL);
+  g_signal_connect (widget, "changed",
+        G_CALLBACK (eel_editable_label_accessible_changed_cb), NULL);
+
+  g_signal_connect (widget,
+                    "notify",
+                    G_CALLBACK (eel_editable_label_accessible_notify_gtk),
+                    NULL);
+  atk_object_set_role (accessible, ATK_ROLE_TEXT);
+}
+
+static G_CONST_RETURN gchar*
+eel_editable_label_accessible_get_name (AtkObject *accessible)
+{
+  if (accessible->name != NULL)
+    return accessible->name;
+  else
+    {
+      GtkWidget *widget;
+
+      widget = GTK_ACCESSIBLE (accessible)->widget;
+      if (widget == NULL)
+      /* State is defunct */
+        return NULL;
+
+      g_assert (EEL_IS_EDITABLE_LABEL (widget));
+      return eel_editable_label_get_text (EEL_EDITABLE_LABEL (widget));
+    }
+}
+
+static AtkStateSet*
+eel_editable_label_accessible_ref_state_set (AtkObject *accessible)
+{
+  AtkStateSet *state_set;
+  GtkWidget *widget;
+
+  state_set = a11y_parent_class->ref_state_set (accessible);
+  widget = GTK_ACCESSIBLE (accessible)->widget;
+ 
+  if (widget == NULL)
+    return state_set;
+
+  atk_state_set_add_state (state_set, ATK_STATE_EDITABLE);
+  atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
+  return state_set;
+}
+
+static void 
+eel_editable_label_accessible_finalize (GObject *object)
+{
+  EelEditableLabelAccessiblePrivate *priv;
+
+  priv = g_object_get_data (object, eel_editable_label_accessible_data);
+  g_object_unref (priv->textutil);
+  g_free (priv);
+  G_OBJECT_CLASS (a11y_parent_class)->finalize (object);
+}
+
+static void
+eel_editable_label_accessible_class_init (AtkObjectClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  a11y_parent_class = g_type_class_peek_parent (klass);
+
+  klass->initialize = eel_editable_label_accessible_initialize;
+  klass->get_name = eel_editable_label_accessible_get_name;
+  klass->ref_state_set = eel_editable_label_accessible_ref_state_set;
+  gobject_class->finalize = eel_editable_label_accessible_finalize;
+}
+
+static AtkObject *
+eel_editable_label_get_accessible (GtkWidget *widget)
+{
+  static GType type = 0;
+  AtkObject *accessible;
+
+  if ((accessible = eel_accessibility_get_atk_object (widget)))
+    return accessible;
+
+  if (!type)
+    {
+      const GInterfaceInfo atk_editable_text_info = 
+      {
+        (GInterfaceInitFunc) atk_editable_text_interface_init,
+        (GInterfaceFinalizeFunc) NULL,
+        NULL
+      };
+      const GInterfaceInfo atk_text_info =
+      {
+        (GInterfaceInitFunc) atk_text_interface_init,
+        (GInterfaceFinalizeFunc) NULL,
+        NULL
+      };
+
+      type = eel_accessibility_create_derived_type ("EelEditableLabelAccessible",
+		       G_TYPE_FROM_INSTANCE (widget),
+		       eel_editable_label_accessible_class_init);
+
+      if (!type)
+        return NULL;
+
+      g_type_add_interface_static (type, ATK_TYPE_EDITABLE_TEXT, &atk_editable_text_info);
+      g_type_add_interface_static (type, ATK_TYPE_TEXT, &atk_text_info);
+    }
+
+  accessible = g_object_new (type, NULL);
+
+  return eel_accessibility_set_atk_object_return (widget, accessible);
+}
+

Added: trunk/eel/eel-editable-label.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-editable-label.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,142 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GTK+ Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#ifndef __EEL_EDITABLE_LABEL_H__
+#define __EEL_EDITABLE_LABEL_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define EEL_TYPE_EDITABLE_LABEL		  (eel_editable_label_get_type ())
+#define EEL_EDITABLE_LABEL(obj)		  (GTK_CHECK_CAST ((obj), EEL_TYPE_EDITABLE_LABEL, EelEditableLabel))
+#define EEL_EDITABLE_LABEL_CLASS(klass)	  (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_EDITABLE_LABEL, EelEditableLabelClass))
+#define EEL_IS_EDITABLE_LABEL(obj)	  (GTK_CHECK_TYPE ((obj), EEL_TYPE_EDITABLE_LABEL))
+#define EEL_IS_EDITABLE_LABEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_EDITABLE_LABEL))
+#define EEL_EDITABLE_LABEL_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), EEL_TYPE_EDITABLE_LABEL, EelEditableLabelClass))
+       
+
+typedef struct _EelEditableLabel       EelEditableLabel;
+typedef struct _EelEditableLabelClass  EelEditableLabelClass;
+
+typedef struct _EelEditableLabelSelectionInfo EelEditableLabelSelectionInfo;
+
+struct _EelEditableLabel
+{
+  GtkMisc misc;
+
+  /*< private >*/
+  guint   jtype : 2;
+  guint   wrap : 1;
+  guint   overwrite_mode : 1;
+  guint   draw_outline : 1;
+  PangoWrapMode  wrap_mode;
+  
+  gchar  *text;
+  int text_size; /* allocated size, in bytes */
+  int n_bytes;	 /* length in use (not including terminating zero), in bytes */
+
+  PangoLayout *layout;
+  guint        layout_includes_preedit : 1;
+
+  int selection_anchor; /* cursor pos, byte index */
+  int selection_end; /* byte index */
+  
+  GtkWidget *popup_menu;
+
+  GtkIMContext *im_context;
+  gboolean need_im_reset;
+  int preedit_length;	/* length of preedit string, in bytes */
+  int preedit_cursor;	/* offset of cursor within preedit string, in chars */
+
+  GdkGC *primary_cursor_gc;
+  GdkGC *secondary_cursor_gc;
+  
+  PangoFontDescription *font_desc;
+};
+
+struct _EelEditableLabelClass
+{
+  GtkMiscClass parent_class;
+
+  void (* move_cursor)        (EelEditableLabel  *label,
+			       GtkMovementStep    step,
+			       gint               count,
+			       gboolean           extend_selection);
+  void (* insert_at_cursor)   (EelEditableLabel  *label,
+			       const gchar       *str);
+  void (* delete_from_cursor) (EelEditableLabel  *label,
+			       GtkDeleteType      type,
+			       gint               count);
+  void (* cut_clipboard)      (EelEditableLabel  *label);
+  void (* copy_clipboard)     (EelEditableLabel  *label);
+  void (* paste_clipboard)    (EelEditableLabel  *label);
+  void (* toggle_overwrite)   (EelEditableLabel  *label);
+  
+  /* Hook to customize right-click popup for selectable labels */
+  void (* populate_popup)   (EelEditableLabel  *label,
+                             GtkMenu           *menu);
+};
+
+GType                 eel_editable_label_get_type          (void) G_GNUC_CONST;
+GtkWidget*            eel_editable_label_new                  (const char       *str);
+void                  eel_editable_label_set_text             (EelEditableLabel *label,
+							       const char       *str);
+G_CONST_RETURN gchar* eel_editable_label_get_text             (EelEditableLabel *label);
+void                  eel_editable_label_set_justify          (EelEditableLabel *label,
+							       GtkJustification  jtype);
+GtkJustification      eel_editable_label_get_justify          (EelEditableLabel *label);
+void                  eel_editable_label_set_line_wrap        (EelEditableLabel *label,
+							       gboolean          wrap);
+void                  eel_editable_label_set_line_wrap_mode   (EelEditableLabel *label,
+							       PangoWrapMode     mode);
+gboolean              eel_editable_label_get_line_wrap        (EelEditableLabel *label);
+void                  eel_editable_label_set_draw_outline     (EelEditableLabel *label,
+							       gboolean          wrap);
+void                  eel_editable_label_select_region        (EelEditableLabel *label,
+							       gint              start_offset,
+							       gint              end_offset);
+gboolean              eel_editable_label_get_selection_bounds (EelEditableLabel *label,
+							       gint             *start,
+							       gint             *end);
+PangoLayout *         eel_editable_label_get_layout           (EelEditableLabel *label);
+void                  eel_editable_label_get_layout_offsets   (EelEditableLabel *label,
+							       gint             *x,
+							       gint             *y);
+PangoFontDescription *eel_editable_label_get_font_description (EelEditableLabel *label);
+void                  eel_editable_label_set_font_description (EelEditableLabel *label,
+							       const PangoFontDescription *desc);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __EEL_EDITABLE_LABEL_H__ */

Added: trunk/eel/eel-enumeration.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-enumeration.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,533 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-enumeration.c: Enumeration data structure.
+
+   Copyright (C) 2000 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-enumeration.h"
+
+#include "eel-debug.h"
+#include "eel-glib-extensions.h"
+#include "eel-lib-self-check-functions.h"
+#include "eel-string.h"
+#include "eel-i18n.h"
+
+static gboolean suppress_duplicate_registration_warning;
+
+struct EelEnumeration
+{
+	char *id;
+	GPtrArray *entries; /* array of EelEnumerationEntry */
+};
+
+static EelEnumeration *
+eel_enumeration_new (const char *id)
+{
+	EelEnumeration *enumeration;
+
+	g_assert (id != NULL);
+	g_assert (id[0] != '\0');
+	
+	enumeration = g_new0 (EelEnumeration, 1);
+
+	enumeration->id = g_strdup (id);
+	enumeration->entries = g_ptr_array_new ();
+
+	return enumeration;
+}
+
+static void
+free_entry (EelEnumerationEntry *entry)
+{
+	g_free (entry->name);
+	g_free (entry->description);
+	g_free (entry);
+}
+
+static void
+eel_enumeration_free (EelEnumeration *enumeration)
+{
+	if (enumeration == NULL) {
+		return;
+	}
+
+	g_free (enumeration->id);
+	g_ptr_array_foreach (enumeration->entries, (GFunc) free_entry, NULL);
+	g_ptr_array_free (enumeration->entries, TRUE);
+	g_free (enumeration);
+}
+
+char *
+eel_enumeration_get_id (const EelEnumeration *enumeration)
+{
+	g_return_val_if_fail (enumeration != NULL, NULL);
+
+	return g_strdup (enumeration->id);
+}
+
+guint
+eel_enumeration_get_length (const EelEnumeration *enumeration)
+{
+	g_return_val_if_fail (enumeration != NULL, 0);
+
+	return enumeration->entries->len;
+}
+
+const EelEnumerationEntry *
+eel_enumeration_get_nth_entry (const EelEnumeration *enumeration,
+			       guint n)
+{
+	g_return_val_if_fail (enumeration != NULL, NULL);
+	g_return_val_if_fail (n < enumeration->entries->len, NULL);
+
+	return (EelEnumerationEntry *) g_ptr_array_index (enumeration->entries, n);
+}
+
+int
+eel_enumeration_get_name_position (const EelEnumeration *enumeration,
+				   const char *name)
+{
+	int i;
+
+	g_return_val_if_fail (enumeration != NULL, -1);
+	g_return_val_if_fail (name != NULL, -1);
+
+	for (i = 0; i < enumeration->entries->len; ++i) {
+		EelEnumerationEntry *entry = enumeration->entries->pdata[i];
+		if (strcmp (name, entry->name) == 0) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+gboolean
+eel_enumeration_contains_name (const EelEnumeration *enumeration,
+			       const char *name)
+{
+	g_return_val_if_fail (enumeration != NULL, FALSE);
+	g_return_val_if_fail (name != NULL, FALSE);
+
+	return eel_enumeration_get_name_position (enumeration, name) != -1;
+}
+
+guint
+eel_enumeration_get_value_for_name (const EelEnumeration *enumeration,
+				    const char           *name)
+{
+	int i;
+
+	g_return_val_if_fail (enumeration != NULL, 0);
+	g_return_val_if_fail (name != NULL, 0);
+
+	for (i = 0; i < enumeration->entries->len; ++i) {
+		EelEnumerationEntry *entry = enumeration->entries->pdata[i];
+		if (strcmp (name, entry->name) == 0) {
+			return entry->value;
+		}
+	}
+
+	g_warning ("No name '%s' in enumeration '%s'", name, enumeration->id);
+
+	return 0;
+}
+
+const char *
+eel_enumeration_get_name_for_value (const EelEnumeration *enumeration,
+				    int                   value)
+{
+	int i;
+
+	g_return_val_if_fail (enumeration != NULL, 0);
+
+	for (i = 0; i < enumeration->entries->len; ++i) {
+		EelEnumerationEntry *entry = enumeration->entries->pdata[i];
+		if (value == entry->value) {
+			return entry->name;
+		}
+	}
+
+	g_warning ("No value '%d' in enumeration '%s'", value, enumeration->id);
+
+	return NULL;
+}
+
+char **
+eel_enumeration_get_names (const EelEnumeration *enumeration)
+{
+	GPtrArray *names;
+	int i;
+
+	g_return_val_if_fail (enumeration != NULL, NULL);
+
+	if (enumeration->entries->len == 0) {
+		return NULL;
+	}
+
+	names = g_ptr_array_sized_new (enumeration->entries->len + 1);
+	for (i = 0; i < enumeration->entries->len; ++i) {
+		EelEnumerationEntry *entry = enumeration->entries->pdata[i];
+		g_ptr_array_add (names, g_strdup (entry->name));
+	}
+	g_ptr_array_add (names, NULL);
+
+	return (char **) g_ptr_array_free (names, FALSE);
+}
+
+static EelEnumeration *
+eel_enumeration_new_from_tokens (const char *id,
+				 const char *names,
+				 const char *descriptions,
+				 const char *values,
+				 const char *delimiter)
+{
+	EelEnumeration *enumeration;
+	char **namev;
+	char **descriptionv;
+	char **valuev;
+	int length;
+	guint i;
+
+	g_return_val_if_fail (id != NULL, NULL);
+	g_return_val_if_fail (id[0] != '\0', NULL);
+	g_return_val_if_fail (names != NULL, NULL);
+	g_return_val_if_fail (names[0] != '\0', NULL);
+	g_return_val_if_fail (values != NULL, NULL);
+	g_return_val_if_fail (values[0] != '\0', NULL);
+	g_return_val_if_fail (delimiter != NULL, NULL);
+	g_return_val_if_fail (delimiter[0] != '\0', NULL);
+
+	enumeration = eel_enumeration_new (id);
+
+	namev = g_strsplit (names, delimiter, -1);
+	valuev = g_strsplit (values, delimiter, -1);
+
+	length = g_strv_length (namev);
+	if (g_strv_length (valuev) != length) {
+		g_warning ("names and values have different lengths.");
+		g_strfreev (namev);
+		g_strfreev (valuev);
+		return NULL;
+	}
+
+	descriptionv = descriptions != NULL ? 
+		       g_strsplit (descriptions, delimiter, -1) : NULL;
+
+	if (descriptionv != NULL) {
+		if (g_strv_length (descriptionv) != length) {
+			g_warning ("names and descriptions have different lengths.");
+			g_strfreev (namev);
+			g_strfreev (descriptionv);
+			g_strfreev (valuev);
+			return NULL;
+		}
+	}
+
+	for (i = 0; i < length; i++) {
+		EelEnumerationEntry *entry;
+		int value;
+
+		if (!eel_str_to_int (valuev[i], &value)) {
+			g_warning ("Could not convert value '%d' to an integer.  Using 0.", i);
+			value = 0;
+		}
+
+		entry = g_new0 (EelEnumerationEntry, 1);
+		entry->name = namev[i];
+		entry->description = descriptionv ? descriptionv[i] : NULL;
+		entry->value = value;
+
+		g_ptr_array_add (enumeration->entries, entry);
+	}
+
+	return enumeration;
+}
+
+static EelEnumerationEntry *
+dup_entry (const EelEnumerationEntry *entry)
+{
+	EelEnumerationEntry *res;
+
+	res = g_new0 (EelEnumerationEntry, 1);
+	res->name = g_strdup (entry->name);
+	res->description = g_strdup (entry->description);
+	res->value = entry->value;
+
+	return res;
+}
+
+static EelEnumeration *
+eel_enumeration_new_from_entries (const char *id,
+				  const EelEnumerationEntry entries[],
+				  guint n_entries)
+{
+	EelEnumeration *enumeration;
+	guint i;
+
+	g_assert (id != NULL);
+	g_assert (id[0] != '\0');
+	g_assert (entries != NULL);
+
+	enumeration = eel_enumeration_new (id);
+
+	for (i = 0; i < n_entries; i++) {
+		g_ptr_array_add (enumeration->entries, dup_entry (&entries[i]));
+	}
+
+	return enumeration;
+}
+
+static GHashTable *enumeration_table = NULL;
+
+static void
+enumeration_table_free (void)
+{
+	if (enumeration_table != NULL) {
+		g_hash_table_destroy (enumeration_table);
+		enumeration_table = NULL;
+	}
+}
+
+static GHashTable *
+enumeration_table_get (void)
+{
+	if (enumeration_table != NULL) {
+		return enumeration_table;
+	}
+
+	enumeration_table = g_hash_table_new_full (g_str_hash,
+						   g_str_equal,
+						   (GDestroyNotify) g_free,
+						   (GDestroyNotify) eel_enumeration_free);
+
+	eel_debug_call_at_shutdown (enumeration_table_free);
+
+	return enumeration_table;
+}
+
+const EelEnumeration *
+eel_enumeration_lookup (const char *id)
+{
+	GHashTable *table;
+
+	g_return_val_if_fail (id != NULL, NULL);
+	g_return_val_if_fail (id[0] != '\0', NULL);
+
+	table = enumeration_table_get ();
+	g_return_val_if_fail (table != NULL, NULL);
+
+	return g_hash_table_lookup (table, id);
+}
+
+void
+eel_enumeration_register (const char                *id,
+			  const EelEnumerationEntry  entries[],
+			  guint                      n_entries)
+{
+	GHashTable *table;
+	EelEnumeration *enumeration;
+
+	g_return_if_fail (id != NULL);
+	g_return_if_fail (id[0] != '\0');
+	g_return_if_fail (entries != NULL);
+
+	table = enumeration_table_get ();
+	g_return_if_fail (table != NULL);
+
+	if (eel_enumeration_lookup (id) != NULL) {
+		if (!suppress_duplicate_registration_warning) {
+			g_warning ("Trying to register duplicate enumeration '%s'.", id);
+		}
+
+		return;
+	}
+
+	enumeration = eel_enumeration_new_from_entries (id, entries, n_entries);
+
+	g_hash_table_insert (table, g_strdup (id), enumeration);
+}
+
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+#define CHECK_ENUMERATION_ENTRY(enumeration, i, name, description, value) \
+	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_name_position (enumeration, name), i); \
+	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_value_for_name (enumeration, name), value); \
+	EEL_CHECK_STRING_RESULT (g_strdup (eel_enumeration_get_name_for_value (enumeration, value)), name);
+
+static EelEnumerationEntry speed_tradeoff_enum_entries[] = {
+	{ "always",	    "Always",		10 },
+	{ "local_only",	    "Local Files Only",	20 },
+	{ "never",	    "Never",		30 }
+};
+
+static EelEnumerationEntry standard_zoom_levels_enum_entries[] = {
+	{ "smallest",	    "25%",	25 },
+	{ "smaller",	    "50%",	50 },
+	{ "small",	    "75%",	75 },
+	{ "standard",	    "100%",	100 },
+	{ "large",	    "150%",	150 },
+	{ "larger",	    "200%",	200 },
+	{ "largest",	    "400%",	400 }
+};
+
+static EelEnumerationEntry file_size_enum_entries[] = {
+	{ "102400",	    "100 K",	102400 },
+	{ "512000",	    "500 K",	512000 },
+	{ "1048576",	    "1 MB",	1048576 },
+	{ "3145728",	    "3 MB",	3145728 },
+	{ "5242880",	    "5 MB",	5242880 },
+	{ "10485760",	    "10 MB",	10485760 },
+	{ "104857600",	    "100 MB",	104857600 }
+};
+
+#define CHECK_REGISTERED_ENUMERATION(enumname) \
+G_STMT_START { \
+	const EelEnumeration *e; \
+	int i; \
+	e = eel_enumeration_lookup (#enumname); \
+	g_return_if_fail (e != NULL); \
+	for (i = 0; i < G_N_ELEMENTS (enumname##_enum_entries); i++) { \
+		CHECK_ENUMERATION_ENTRY (e, \
+					 i, \
+				 	 enumname##_enum_entries[i].name, \
+					 enumname##_enum_entries[i].description, \
+					 enumname##_enum_entries[i].value); \
+	} \
+	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_length (e), i); \
+} G_STMT_END
+
+void
+eel_self_check_enumeration (void)
+{
+	EelEnumeration *e;
+	char **names;
+
+	/***/
+	e = eel_enumeration_new_from_tokens ("id",
+					     "single",
+					     NULL,
+					     "1",
+					     ",");
+	
+	CHECK_ENUMERATION_ENTRY (e, 0, "single", "", 1);
+ 	EEL_CHECK_STRING_RESULT (eel_enumeration_get_id (e), "id");
+ 	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_length (e), 1);
+	eel_enumeration_free (e);
+
+	/***/
+	e = eel_enumeration_new_from_tokens ("id",
+					     "apple,orange,banana",
+					     NULL,
+					     "1,2,3",
+					     ",");
+	
+	CHECK_ENUMERATION_ENTRY (e, 0, "apple", "", 1);
+	CHECK_ENUMERATION_ENTRY (e, 1, "orange", "", 2);
+	CHECK_ENUMERATION_ENTRY (e, 2, "banana", "", 3);
+ 	EEL_CHECK_STRING_RESULT (eel_enumeration_get_id (e), "id");
+ 	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_length (e), 3);
+	eel_enumeration_free (e);
+
+	/***/
+	e = eel_enumeration_new_from_tokens ("id",
+					     "foo",
+					     NULL,
+					     "666",
+					     ",");
+	CHECK_ENUMERATION_ENTRY (e, 0, "foo", "", 666);
+ 	EEL_CHECK_STRING_RESULT (eel_enumeration_get_id (e), "id");
+ 	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_length (e), 1);
+	eel_enumeration_free (e);
+
+	/***/
+	e = eel_enumeration_new_from_tokens ("id",
+					     "one,two,---,three",
+					     "One,Two,---,Three",
+					     "1,2,0,3",
+					     ",");
+	CHECK_ENUMERATION_ENTRY (e, 0, "one", "One", 1);
+	CHECK_ENUMERATION_ENTRY (e, 1, "two", "Two", 2);
+	CHECK_ENUMERATION_ENTRY (e, 2, "---", "---", 0);
+	CHECK_ENUMERATION_ENTRY (e, 3, "three", "Three", 3);
+ 	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_length (e), 4);
+	eel_enumeration_free (e);
+
+	/***/
+	e = eel_enumeration_new_from_tokens ("id",
+					     "red,green,blue",
+					     "Red Desc,Green Desc,Blue Desc",
+					     "10,20,30",
+					     ",");
+	
+	CHECK_ENUMERATION_ENTRY (e, 0, "red", "Red Desc", 10);
+	CHECK_ENUMERATION_ENTRY (e, 1, "green", "Green Desc", 20);
+	CHECK_ENUMERATION_ENTRY (e, 2, "blue", "Blue Desc", 30);
+ 	EEL_CHECK_STRING_RESULT (eel_enumeration_get_id (e), "id");
+ 	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_length (e), 3);
+	
+ 	EEL_CHECK_BOOLEAN_RESULT (eel_enumeration_contains_name (e, "red"), TRUE);
+ 	EEL_CHECK_BOOLEAN_RESULT (eel_enumeration_contains_name (e, "green"), TRUE);
+ 	EEL_CHECK_BOOLEAN_RESULT (eel_enumeration_contains_name (e, "blue"), TRUE);
+ 	EEL_CHECK_BOOLEAN_RESULT (eel_enumeration_contains_name (e, "pink"), FALSE);
+	
+	eel_enumeration_free (e);
+
+	/***/
+	e = eel_enumeration_new_from_tokens ("id",
+					     "red,foo:green,bar:blue,baz",
+					     "Red,Desc:Green,Desc:Blue,Desc",
+					     "10:20:30",
+					     ":");
+
+	CHECK_ENUMERATION_ENTRY (e, 0, "red,foo", "Red,Desc", 10);
+	CHECK_ENUMERATION_ENTRY (e, 1, "green,bar", "Green,Desc", 20);
+	CHECK_ENUMERATION_ENTRY (e, 2, "blue,baz", "Blue,Desc", 30);
+ 	EEL_CHECK_STRING_RESULT (eel_enumeration_get_id (e), "id");
+ 	EEL_CHECK_INTEGER_RESULT (eel_enumeration_get_length (e), 3);
+ 	EEL_CHECK_BOOLEAN_RESULT (eel_enumeration_contains_name (e, "black"), FALSE);
+
+	names = eel_enumeration_get_names (e);
+	EEL_CHECK_INTEGER_RESULT (strcmp(names[2], "blue,baz"), 0);
+	g_strfreev (names);
+	eel_enumeration_free (e);
+
+	/***/
+	suppress_duplicate_registration_warning = TRUE;
+	eel_enumeration_register ("speed_tradeoff",
+				  speed_tradeoff_enum_entries,
+				  G_N_ELEMENTS (speed_tradeoff_enum_entries));
+	eel_enumeration_register ("standard_zoom_levels",
+				  standard_zoom_levels_enum_entries,
+				  G_N_ELEMENTS (standard_zoom_levels_enum_entries));
+	eel_enumeration_register ("file_size",
+				  file_size_enum_entries,
+				  G_N_ELEMENTS (file_size_enum_entries));
+	suppress_duplicate_registration_warning = FALSE;
+
+	CHECK_REGISTERED_ENUMERATION(speed_tradeoff);
+	CHECK_REGISTERED_ENUMERATION(standard_zoom_levels);
+	CHECK_REGISTERED_ENUMERATION(file_size);
+}
+
+#endif /* !EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-enumeration.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-enumeration.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,63 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-enumeration.h: Enumeration data structure.
+ 
+   Copyright (C) 2000 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_ENUMERATION_H
+#define EEL_ENUMERATION_H
+
+#include <glib.h>
+
+/* Opaque EelEnumeration declaration. */
+typedef struct EelEnumeration EelEnumeration;
+
+typedef struct
+{
+	char *name;
+	char *description;
+	guint value;
+} EelEnumerationEntry;
+
+char *          eel_enumeration_get_id                            (const EelEnumeration      *enumeration);
+
+guint           eel_enumeration_get_length                        (const EelEnumeration      *enumeration);
+const EelEnumerationEntry *
+                eel_enumeration_get_nth_entry                     (const EelEnumeration      *enumeration,
+								   guint                      n);
+int             eel_enumeration_get_name_position                 (const EelEnumeration      *enumeration,
+								   const char                *name);
+gboolean        eel_enumeration_contains_name                     (const EelEnumeration      *enumeration,
+								   const char                *name);
+guint           eel_enumeration_get_value_for_name                (const EelEnumeration      *enumeration,
+								   const char                *name);
+const char *    eel_enumeration_get_name_for_value                (const EelEnumeration      *enumeration,
+								   int                        value);
+char **         eel_enumeration_get_names                         (const EelEnumeration      *enumeration);
+
+void            eel_enumeration_register                          (const char                *id,
+								   const EelEnumerationEntry  entries[],
+								   guint                      n_entries);
+const EelEnumeration *
+                eel_enumeration_lookup                            (const char                *id);
+
+#endif /* EEL_ENUMERATION_H */
+

Added: trunk/eel/eel-gconf-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gconf-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,647 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gconf-extensions.c - Stuff to make GConf easier to use.
+
+   Copyright (C) 2000, 2001 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-gconf-extensions.h"
+
+#include "eel-debug.h"
+#include "eel-glib-extensions.h"
+#include "eel-stock-dialogs.h"
+#include "eel-string.h"
+#include "eel-i18n.h"
+
+#include <gconf/gconf-client.h>
+#include <gconf/gconf.h>
+
+static GConfClient *global_gconf_client = NULL;
+
+static void
+global_client_free (void)
+{
+	if (global_gconf_client == NULL) {
+		return;
+	}
+	
+	g_object_unref (global_gconf_client);
+	global_gconf_client = NULL;
+}
+
+/* Public */
+GConfClient *
+eel_gconf_client_get_global (void)
+{
+	/* Initialize gconf if needed */
+	if (!gconf_is_initialized ()) {
+		char *argv[] = { "eel-preferences", NULL };
+		GError *error = NULL;
+		
+		if (!gconf_init (1, argv, &error)) {
+			if (eel_gconf_handle_error (&error)) {
+				return NULL;
+			}
+		}
+	}
+	
+	if (global_gconf_client == NULL) {
+		global_gconf_client = gconf_client_get_default ();
+		eel_debug_call_at_shutdown (global_client_free);
+	}
+	
+	return global_gconf_client;
+}
+
+gboolean
+eel_gconf_handle_error (GError **error)
+{
+	char *message;
+	static gboolean shown_dialog = FALSE;
+	
+	g_return_val_if_fail (error != NULL, FALSE);
+
+	if (*error != NULL) {
+		g_warning (_("GConf error:\n  %s"), (*error)->message);
+		if (! shown_dialog) {
+			shown_dialog = TRUE;
+
+			message = g_strdup_printf (_("GConf error: %s"),
+						   (*error)->message);
+			eel_show_error_dialog (message, 
+			                       _("All further errors shown "
+			                       "only on terminal."),
+			                       NULL);
+			g_free (message);
+		}
+		g_error_free (*error);
+		*error = NULL;
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+void
+eel_gconf_set_boolean (const char *key,
+			    gboolean boolean_value)
+{
+	GConfClient *client;
+	GError *error = NULL;
+	
+	g_return_if_fail (key != NULL);
+
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+	
+	gconf_client_set_bool (client, key, boolean_value, &error);
+	eel_gconf_handle_error (&error);
+}
+
+gboolean
+eel_gconf_get_boolean (const char *key)
+{
+	gboolean result;
+	GConfClient *client;
+	GError *error = NULL;
+	
+	g_return_val_if_fail (key != NULL, FALSE);
+	
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, FALSE);
+	
+	result = gconf_client_get_bool (client, key, &error);
+	
+	if (eel_gconf_handle_error (&error)) {
+		result = FALSE;
+	}
+	
+	return result;
+}
+
+void
+eel_gconf_set_integer (const char *key,
+			    int int_value)
+{
+	GConfClient *client;
+	GError *error = NULL;
+
+	g_return_if_fail (key != NULL);
+
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_set_int (client, key, int_value, &error);
+	eel_gconf_handle_error (&error);
+}
+
+int
+eel_gconf_get_integer (const char *key)
+{
+	int result;
+	GConfClient *client;
+	GError *error = NULL;
+	
+	g_return_val_if_fail (key != NULL, 0);
+	
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, 0);
+	
+	result = gconf_client_get_int (client, key, &error);
+
+	if (eel_gconf_handle_error (&error)) {
+		result = 0;
+	}
+
+	return result;
+}
+
+void
+eel_gconf_set_string (const char *key,
+			   const char *string_value)
+{
+	GConfClient *client;
+	GError *error = NULL;
+
+	g_return_if_fail (key != NULL);
+
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+	
+	gconf_client_set_string (client, key, string_value, &error);
+	eel_gconf_handle_error (&error);
+}
+
+char *
+eel_gconf_get_string (const char *key)
+{
+	char *result;
+	GConfClient *client;
+	GError *error = NULL;
+	
+	g_return_val_if_fail (key != NULL, NULL);
+	
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, NULL);
+	
+	result = gconf_client_get_string (client, key, &error);
+	
+	if (eel_gconf_handle_error (&error)) {
+		result = g_strdup ("");
+	}
+	
+	return result;
+}
+
+void
+eel_gconf_set_string_list (const char *key,
+				const GSList *slist)
+{
+	GConfClient *client;
+	GError *error;
+
+	g_return_if_fail (key != NULL);
+
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	error = NULL;
+	gconf_client_set_list (client, key, GCONF_VALUE_STRING,
+			       /* Need cast cause of GConf api bug */
+			       (GSList *) slist,
+			       &error);
+	eel_gconf_handle_error (&error);
+}
+
+GSList *
+eel_gconf_get_string_list (const char *key)
+{
+	GSList *slist;
+	GConfClient *client;
+	GError *error;
+	
+	g_return_val_if_fail (key != NULL, NULL);
+	
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, NULL);
+	
+	error = NULL;
+	slist = gconf_client_get_list (client, key, GCONF_VALUE_STRING, &error);
+	if (eel_gconf_handle_error (&error)) {
+		slist = NULL;
+	}
+
+	return slist;
+}
+
+void
+eel_gconf_unset (const char *key)
+{
+	GConfClient *client;
+	GError *error;
+	
+	g_return_if_fail (key != NULL);
+	
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+	
+	error = NULL;
+	gconf_client_unset (client, key, &error);
+	eel_gconf_handle_error (&error);
+}
+
+gboolean
+eel_gconf_is_default (const char *key)
+{
+	gboolean result;
+	GConfValue *value;
+	GError *error = NULL;
+	
+	g_return_val_if_fail (key != NULL, FALSE);
+	
+	value = gconf_client_get_without_default  (eel_gconf_client_get_global (), key, &error);
+
+	if (eel_gconf_handle_error (&error)) {
+		if (value != NULL) {
+			gconf_value_free (value);
+		}
+		return FALSE;
+	}
+
+	result = (value == NULL);
+	eel_gconf_value_free (value);
+	return result;
+}
+
+gboolean
+eel_gconf_key_is_writable (const char *key)
+{
+	gboolean result;
+	GError *error = NULL;
+	
+	g_return_val_if_fail (key != NULL, FALSE);
+	
+	result = gconf_client_key_is_writable  (eel_gconf_client_get_global (), key, &error);
+
+	if (eel_gconf_handle_error (&error)) {
+		return result;
+	}
+
+	return result;
+}
+
+gboolean
+eel_gconf_monitor_add (const char *directory)
+{
+	GError *error = NULL;
+	GConfClient *client;
+
+	g_return_val_if_fail (directory != NULL, FALSE);
+
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, FALSE);
+
+	gconf_client_add_dir (client,
+			      directory,
+			      GCONF_CLIENT_PRELOAD_NONE,
+			      &error);
+	
+	if (eel_gconf_handle_error (&error)) {
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+eel_gconf_monitor_remove (const char *directory)
+{
+	GError *error = NULL;
+	GConfClient *client;
+
+	if (directory == NULL) {
+		return FALSE;
+	}
+
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, FALSE);
+	
+	gconf_client_remove_dir (client,
+				 directory,
+				 &error);
+	
+	if (eel_gconf_handle_error (&error)) {
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+void
+eel_gconf_preload_cache (const char             *directory,
+			 GConfClientPreloadType  preload_type)
+{
+	GError *error = NULL;
+	GConfClient *client;
+
+	if (directory == NULL) {
+		return;
+	}
+
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+	
+	gconf_client_preload (client,
+			      directory,
+			      preload_type,
+			      &error);
+	
+	eel_gconf_handle_error (&error);
+}
+
+void
+eel_gconf_suggest_sync (void)
+{
+	GConfClient *client;
+	GError *error = NULL;
+
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+	
+	gconf_client_suggest_sync (client, &error);
+	eel_gconf_handle_error (&error);
+}
+
+GConfValue*
+eel_gconf_get_value (const char *key)
+{
+	GConfValue *value = NULL;
+	GConfClient *client;
+	GError *error = NULL;
+
+	g_return_val_if_fail (key != NULL, NULL);
+
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, NULL);
+
+	value = gconf_client_get (client, key, &error);
+	
+	if (eel_gconf_handle_error (&error)) {
+		if (value != NULL) {
+			gconf_value_free (value);
+			value = NULL;
+		}
+	}
+
+	return value;
+}
+
+GConfValue*
+eel_gconf_get_default_value (const char *key)
+{
+	GConfValue *value = NULL;
+	GConfClient *client;
+	GError *error = NULL;
+	
+	g_return_val_if_fail (key != NULL, NULL);
+
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, NULL);
+
+	value = gconf_client_get_default_from_schema (client, key, &error);
+	
+	if (eel_gconf_handle_error (&error)) {
+		if (value != NULL) {
+			gconf_value_free (value);
+			value = NULL;
+		}
+	}
+
+	return value;
+}
+
+static gboolean
+simple_value_is_equal (const GConfValue *a,
+		       const GConfValue *b)
+{
+	g_assert (a != NULL);
+	g_assert (b != NULL);
+
+	switch (a->type) {
+	case GCONF_VALUE_STRING:
+		return eel_str_is_equal (gconf_value_get_string (a),
+					 gconf_value_get_string (b));
+
+	case GCONF_VALUE_INT:
+		return gconf_value_get_int (a) ==
+			gconf_value_get_int (b);
+
+	case GCONF_VALUE_FLOAT:
+		return gconf_value_get_float (a) ==
+			gconf_value_get_float (b);
+
+	case GCONF_VALUE_BOOL:
+		return gconf_value_get_bool (a) ==
+			gconf_value_get_bool (b);
+	default:
+		g_assert_not_reached ();
+	}
+	
+	return FALSE;
+}
+
+gboolean
+eel_gconf_value_is_equal (const GConfValue *a,
+			       const GConfValue *b)
+{
+	GSList *node_a;
+	GSList *node_b;
+
+	if (a == NULL && b == NULL) {
+		return TRUE;
+	}
+
+	if (a == NULL || b == NULL) {
+		return FALSE;
+	}
+
+	if (a->type != b->type) {
+		return FALSE;
+	}
+
+	switch (a->type) {
+	case GCONF_VALUE_STRING:
+	case GCONF_VALUE_INT:
+	case GCONF_VALUE_FLOAT:
+	case GCONF_VALUE_BOOL:
+		return simple_value_is_equal (a, b);
+		break;
+		
+	case GCONF_VALUE_LIST:
+		if (gconf_value_get_list_type (a) !=
+		    gconf_value_get_list_type (b)) {
+			return FALSE;
+		}
+
+		node_a = gconf_value_get_list (a);
+		node_b = gconf_value_get_list (b);
+		
+		if (node_a == NULL && node_b == NULL) {
+			return TRUE;
+		}
+
+		if (g_slist_length (node_a) !=
+		    g_slist_length (node_b)) {
+			return FALSE;
+		}
+		
+		for (;
+		     node_a != NULL && node_b != NULL;
+		     node_a = node_a->next, node_b = node_b->next) {
+			g_assert (node_a->data != NULL);
+			g_assert (node_b->data != NULL);
+			if (!simple_value_is_equal (node_a->data, node_b->data)) {
+				return FALSE;
+			}
+		}
+		
+		return TRUE;
+	default:
+		/* FIXME: pair ? */
+		g_assert (0);
+	}
+	
+	g_assert_not_reached ();
+	return FALSE;
+}
+
+void
+eel_gconf_value_free (GConfValue *value)
+{
+	if (value == NULL) {
+		return;
+	}
+	
+	gconf_value_free (value);
+}
+
+guint
+eel_gconf_notification_add (const char *key,
+				 GConfClientNotifyFunc notification_callback,
+				 gpointer callback_data)
+{
+	guint notification_id;
+	GConfClient *client;
+	GError *error = NULL;
+	
+	g_return_val_if_fail (key != NULL, EEL_GCONF_UNDEFINED_CONNECTION);
+	g_return_val_if_fail (notification_callback != NULL, EEL_GCONF_UNDEFINED_CONNECTION);
+
+	client = eel_gconf_client_get_global ();
+	g_return_val_if_fail (client != NULL, EEL_GCONF_UNDEFINED_CONNECTION);
+	
+	notification_id = gconf_client_notify_add (client,
+						   key,
+						   notification_callback,
+						   callback_data,
+						   NULL,
+						   &error);
+	
+	if (eel_gconf_handle_error (&error)) {
+		if (notification_id != EEL_GCONF_UNDEFINED_CONNECTION) {
+			gconf_client_notify_remove (client, notification_id);
+			notification_id = EEL_GCONF_UNDEFINED_CONNECTION;
+		}
+	}
+	
+	return notification_id;
+}
+
+void
+eel_gconf_notification_remove (guint notification_id)
+{
+	GConfClient *client;
+
+	if (notification_id == EEL_GCONF_UNDEFINED_CONNECTION) {
+		return;
+	}
+	
+	client = eel_gconf_client_get_global ();
+	g_return_if_fail (client != NULL);
+
+	gconf_client_notify_remove (client, notification_id);
+}
+
+GSList *
+eel_gconf_value_get_string_list (const GConfValue *value)
+{
+ 	GSList *result;
+ 	const GSList *slist;
+ 	const GSList *node;
+	const char *string;
+	const GConfValue *next_value;
+
+	if (value == NULL) {
+		return NULL;
+	}
+
+	g_return_val_if_fail (value->type == GCONF_VALUE_LIST, NULL);
+	g_return_val_if_fail (gconf_value_get_list_type (value) == GCONF_VALUE_STRING, NULL);
+
+	slist = gconf_value_get_list (value);
+	result = NULL;
+	for (node = slist; node != NULL; node = node->next) {
+		next_value = node->data;
+		g_return_val_if_fail (next_value != NULL, NULL);
+		g_return_val_if_fail (next_value->type == GCONF_VALUE_STRING, NULL);
+		string = gconf_value_get_string (next_value);
+		result = g_slist_prepend (result, g_strdup (string));
+	}
+	return g_slist_reverse (result);
+}
+
+void
+eel_gconf_value_set_string_list (GConfValue *value,
+				 const GSList *string_list)
+{
+ 	const GSList *node;
+	GConfValue *next_value;
+ 	GSList *value_list;
+
+	g_return_if_fail (value->type == GCONF_VALUE_LIST);
+	g_return_if_fail (gconf_value_get_list_type (value) == GCONF_VALUE_STRING);
+
+	value_list = NULL;
+	for (node = string_list; node != NULL; node = node->next) {
+		next_value = gconf_value_new (GCONF_VALUE_STRING);
+		gconf_value_set_string (next_value, node->data);
+		value_list = g_slist_append (value_list, next_value);
+	}
+
+	gconf_value_set_list (value, value_list);
+
+	for (node = value_list; node != NULL; node = node->next) {
+		gconf_value_free (node->data);
+	}
+	g_slist_free (value_list);
+}
+

Added: trunk/eel/eel-gconf-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gconf-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,73 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gconf-extensions.h - Stuff to make GConf easier to use.
+
+   Copyright (C) 2000, 2001 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_GCONF_EXTENSIONS_H
+#define EEL_GCONF_EXTENSIONS_H
+
+#include <glib.h>
+#include <gconf/gconf.h>
+#include <gconf/gconf-client.h>
+
+G_BEGIN_DECLS
+
+#define EEL_GCONF_UNDEFINED_CONNECTION 0
+
+GConfClient *eel_gconf_client_get_global     (void);
+gboolean     eel_gconf_handle_error          (GError                **error);
+void         eel_gconf_set_boolean           (const char             *key,
+					      gboolean                boolean_value);
+gboolean     eel_gconf_get_boolean           (const char             *key);
+int          eel_gconf_get_integer           (const char             *key);
+void         eel_gconf_set_integer           (const char             *key,
+					      int                     int_value);
+char *       eel_gconf_get_string            (const char             *key);
+void         eel_gconf_set_string            (const char             *key,
+					      const char             *string_value);
+GSList *     eel_gconf_get_string_list       (const char             *key);
+void         eel_gconf_set_string_list       (const char             *key,
+					      const GSList           *string_list_value);
+void         eel_gconf_unset                 (const char             *key);
+gboolean     eel_gconf_key_is_writable       (const char             *key);
+gboolean     eel_gconf_is_default            (const char             *key);
+gboolean     eel_gconf_monitor_add           (const char             *directory);
+gboolean     eel_gconf_monitor_remove        (const char             *directory);
+void         eel_gconf_preload_cache         (const char             *directory,
+					      GConfClientPreloadType  preload_type);
+void         eel_gconf_suggest_sync          (void);
+GConfValue*  eel_gconf_get_value             (const char             *key);
+GConfValue*  eel_gconf_get_default_value     (const char             *key);
+gboolean     eel_gconf_value_is_equal        (const GConfValue       *a,
+					      const GConfValue       *b);
+void         eel_gconf_value_free            (GConfValue             *value);
+guint        eel_gconf_notification_add      (const char             *key,
+					      GConfClientNotifyFunc   notification_callback,
+					      gpointer                callback_data);
+void         eel_gconf_notification_remove   (guint                   notification_id);
+GSList *     eel_gconf_value_get_string_list (const GConfValue       *value);
+void         eel_gconf_value_set_string_list (GConfValue             *value,
+					      const GSList           *string_list);
+
+G_END_DECLS
+
+#endif /* EEL_GCONF_EXTENSIONS_H */

Added: trunk/eel/eel-gdk-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gdk-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,947 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gdk-extensions.c: Graphics routines to augment what's in gdk.
+
+   Copyright (C) 1999, 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>, 
+            Pavel Cisler <pavel eazel com>,
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-gdk-extensions.h"
+
+#include "eel-glib-extensions.h"
+#include "eel-lib-self-check-functions.h"
+#include "eel-string.h"
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <stdlib.h>
+#include <pango/pango.h>
+
+#define GRADIENT_BAND_SIZE 4
+
+/**
+ * eel_gdk_rectangle_contains_rectangle:
+ * @outer: Rectangle possibly containing another rectangle.
+ * @inner: Rectangle that might be inside.
+ *
+ * Retun TRUE if inner rectangle is contained inside outer rectangle
+ */
+gboolean
+eel_gdk_rectangle_contains_rectangle (GdkRectangle outer, GdkRectangle inner)
+{
+	return outer.x <= inner.x && outer.x + outer.width >= inner.x + inner.width
+		&& outer.y <= inner.y && outer.y + outer.height >= inner.y + inner.height;
+}
+
+/**
+ * eel_interpolate_color:
+ * @ratio: Place on line between colors to interpolate.
+ * @start_color: Color for one end.
+ * @end_color: Color for the other end
+ * @interpolated_color: Result.
+ *
+ * Compute a color between @start_color and @end_color in color space.
+ * Currently, the color space used is RGB, but a future version could
+ * instead do the interpolation in the best color space for expressing
+ * human perception.
+ */
+guint32
+eel_interpolate_color (gdouble ratio,
+		       guint32 start_rgb,
+		       guint32 end_rgb)
+{
+	guchar red, green, blue;
+
+	g_return_val_if_fail (ratio >= 0.0, 0);
+	g_return_val_if_fail (ratio <= 1.0, 0);
+
+	red = ((start_rgb >> 16) & 0xFF) * (1.0 - ratio) + ((end_rgb >> 16) & 0xFF) * ratio;
+	green = ((start_rgb >> 8) & 0xFF) * (1.0 - ratio) + ((end_rgb >> 8) & 0xFF) * ratio;
+	blue = (start_rgb & 0xFF) * (1.0 - ratio) + (end_rgb & 0xFF) * ratio;
+	return (((red << 8) | green) << 8) | blue;
+}
+
+/**
+ * eel_gradient_new
+ * @start_color: Color for the top or left.
+ * @end_color: Color for the bottom or right.
+ * @is_horizontal: Direction of the gradient.
+ *
+ * Create a string that combines the start and end colors along
+ * with the direction of the gradient in a standard format.
+ */
+char *
+eel_gradient_new (const char *start_color,
+		       const char *end_color,
+		       gboolean is_horizontal)
+{
+	/* Handle the special case where the start and end colors are identical.
+	   Handle the special case where the end color is an empty string.
+	*/
+	if (eel_strcmp(start_color, end_color) == 0 || end_color == NULL || end_color[0] == '\0') {
+		return g_strdup (start_color);
+	}
+
+	/* Handle the special case where the start color is an empty string. */
+	if (start_color == NULL || start_color[0] == '\0') {
+		return g_strdup (end_color);
+	}
+	
+	/* Handle the general case. */
+	return g_strconcat (start_color, "-", end_color, is_horizontal ? ":h" : NULL, NULL);
+}
+
+/**
+ * eel_gradient_is_gradient
+ * @gradient_spec: A gradient spec. string.
+ *
+ * Return true if the spec. specifies a gradient instead of a solid color.
+ */
+gboolean
+eel_gradient_is_gradient (const char *gradient_spec)
+{
+	return eel_strchr (gradient_spec, '-') != NULL;
+}
+
+/**
+ * eel_gradient_is_horizontal
+ * @gradient_spec: A gradient spec. string.
+ *
+ * Return true if the spec. specifies a horizontal gradient.
+ */
+gboolean
+eel_gradient_is_horizontal (const char *gradient_spec)
+{
+	size_t length;
+
+	length = eel_strlen (gradient_spec);
+	return length >= 2 && gradient_spec[length - 2] == ':' && gradient_spec[length - 1] == 'h';
+}
+
+static char *
+eel_gradient_strip_trailing_direction_if_any (const char *gradient_spec)
+{
+	size_t length;
+
+	length = eel_strlen (gradient_spec);
+	if (length >= 2 && gradient_spec[length - 2] == ':'
+	    && (gradient_spec[length - 1] == 'v' || gradient_spec[length - 1] == 'h')) {
+		length -= 2;
+	}
+
+	return g_strndup (gradient_spec, length);
+}
+
+/* For parsing n-point gradients. Successive calls should pass the next_spec value
+ * set by the previous call as their first argument - to continue parsing where the
+ * previous call left off.
+ */
+char *
+eel_gradient_parse_one_color_spec (const char *spec, int *percent, const char **next_spec)
+{
+	char *result;
+	const char *rgb_end_ptr;
+	const char *percent_ptr;
+	const char *separator_ptr;
+	
+	percent_ptr   = eel_strchr (spec, '%');
+	separator_ptr = eel_strchr (spec, '-');
+
+	if (percent_ptr != NULL && (separator_ptr == NULL || percent_ptr < separator_ptr)) {
+		if (percent != NULL) {
+			*percent = (int) strtol (percent_ptr + 1, NULL, 10);
+		}
+		rgb_end_ptr = percent_ptr;
+	} else {
+		if (percent != NULL) {
+			*percent = 100;
+		}
+		rgb_end_ptr = separator_ptr;
+	}
+		
+	if (rgb_end_ptr != NULL) {
+		result = g_strndup (spec, rgb_end_ptr - spec);
+	} else {
+		result = eel_gradient_strip_trailing_direction_if_any (spec);
+	}
+
+	/* It's important not to use spec after setting *next_spec because it's
+	 * likely that *next_spec == spec. 
+	 */
+	if (next_spec != NULL) {
+		*next_spec = (separator_ptr != NULL) ? separator_ptr + 1 : NULL;
+	}
+
+	return result;
+}
+
+/* FIXME bugzilla.eazel.com 5076:
+ * anyone using eel_gradient_get_start_color_spec or
+ * eel_gradient_get_end_color_spec is assuming the gradient
+ * is 2 colors which is questionable.
+ * 
+ * Callers should be rewritten and these fns eliminated.
+ */
+ 
+/**
+ * eel_gradient_get_start_color_spec
+ * @gradient_spec: A gradient spec. string.
+ *
+ * Return the start color.
+ * This may be the entire gradient_spec if it's a solid color.
+ */
+char *
+eel_gradient_get_start_color_spec (const char *gradient_spec)
+{
+	return eel_gradient_parse_one_color_spec (gradient_spec, NULL, NULL);
+}
+
+/**
+ * eel_gradient_get_end_color_spec
+ * @gradient_spec: A gradient spec. string.
+ *
+ * Return the end color.
+ * This may be the entire gradient_spec if it's a solid color.
+ */
+char *
+eel_gradient_get_end_color_spec (const char *gradient_spec)
+{
+	char* color = NULL;
+
+	do {
+		g_free (color);
+		color = eel_gradient_parse_one_color_spec (gradient_spec, NULL, &gradient_spec);
+	} while (gradient_spec != NULL);
+
+	return color;
+}
+
+/* Do the work shared by all the set_color_spec functions below. */
+static char *
+eel_gradient_set_edge_color (const char *gradient_spec,
+				  const char *edge_color,
+				  gboolean is_horizontal,
+				  gboolean change_end)
+{
+	char *opposite_color;
+	char *result;
+
+	g_assert (edge_color != NULL);
+
+	/* Get the color from the existing gradient spec. for the opposite
+	   edge. This will parse away all the stuff we don't want from the
+	   old gradient spec.
+	*/
+	opposite_color = change_end
+		? eel_gradient_get_start_color_spec (gradient_spec)
+		: eel_gradient_get_end_color_spec (gradient_spec);
+
+	/* Create a new gradient spec. The eel_gradient_new function handles
+	   some special cases, so we don't have to bother with them here.
+	*/
+	result = eel_gradient_new (change_end ? opposite_color : edge_color,
+					change_end ? edge_color : opposite_color,
+					is_horizontal);
+
+	g_free (opposite_color);
+
+	return result;
+}
+
+/**
+ * eel_gradient_set_left_color_spec
+ * @gradient_spec: A gradient spec. string.
+ * @left_color: Color spec. to replace left color with.
+ *
+ * Changes the left color to what's passed in.
+ * This creates a horizontal gradient.
+ */
+char *
+eel_gradient_set_left_color_spec (const char *gradient_spec,
+				       const char *left_color)
+{
+	g_return_val_if_fail (gradient_spec != NULL, NULL);
+	g_return_val_if_fail (left_color != NULL, NULL);
+
+	return eel_gradient_set_edge_color (gradient_spec, left_color, TRUE, FALSE);
+}
+
+/**
+ * eel_gradient_set_top_color_spec
+ * @gradient_spec: A gradient spec. string.
+ * @top_color: Color spec. to replace top color with.
+ *
+ * Changes the top color to what's passed in.
+ * This creates a vertical gradient.
+ */
+char *
+eel_gradient_set_top_color_spec (const char *gradient_spec,
+				      const char *top_color)
+{
+	g_return_val_if_fail (gradient_spec != NULL, NULL);
+	g_return_val_if_fail (top_color != NULL, NULL);
+
+	return eel_gradient_set_edge_color (gradient_spec, top_color, FALSE, FALSE);
+}
+
+/**
+ * eel_gradient_set_right_color_spec
+ * @gradient_spec: A gradient spec. string.
+ * @right_color: Color spec. to replace right color with.
+ *
+ * Changes the right color to what's passed in.
+ * This creates a horizontal gradient.
+ */
+char *
+eel_gradient_set_right_color_spec (const char *gradient_spec,
+					const char *right_color)
+{
+	g_return_val_if_fail (gradient_spec != NULL, NULL);
+	g_return_val_if_fail (right_color != NULL, NULL);
+
+	return eel_gradient_set_edge_color (gradient_spec, right_color, TRUE, TRUE);
+}
+
+/**
+ * eel_gradient_set_bottom_color_spec
+ * @gradient_spec: A gradient spec. string.
+ * @bottom_color: Color spec. to replace bottom color with.
+ *
+ * Changes the bottom color to what's passed in.
+ * This creates a vertical gradient.
+ */
+char *
+eel_gradient_set_bottom_color_spec (const char *gradient_spec,
+					 const char *bottom_color)
+{
+	g_return_val_if_fail (gradient_spec != NULL, NULL);
+	g_return_val_if_fail (bottom_color != NULL, NULL);
+
+	return eel_gradient_set_edge_color (gradient_spec, bottom_color, FALSE, TRUE);
+}
+
+/**
+ * eel_gdk_color_parse_with_white_default
+ * @color_spec: A color spec, or NULL.
+ * @color: Pointer to place to put resulting color.
+ *
+ * The same as gdk_color_parse, except sets the color to white if
+ * the spec. can't be parsed, instead of returning a boolean flag.
+ */
+void
+eel_gdk_color_parse_with_white_default (const char *color_spec,
+					GdkColor *color)
+{
+	gboolean got_color;
+
+	g_return_if_fail (color != NULL);
+
+	got_color = FALSE;
+	if (color_spec != NULL) {
+		if (gdk_color_parse (color_spec, color)) {
+			got_color = TRUE;
+		}
+	}
+
+	if (!got_color) {
+		color->red = 0xFFFF;
+		color->green = 0xFFFF;
+		color->blue = 0xFFFF;
+	}
+}
+
+/**
+ * eel_parse_rgb_with_white_default
+ * @color_spec: A color spec, or NULL.
+ * Returns: An rgb value.
+ *
+ * The same as gdk_color_parse, except sets the color to white if
+ * the spec. can't be parsed instead of returning a boolean flag
+ * and returns a guint32 rgb value instead of a GdkColor.
+ */
+guint32
+eel_parse_rgb_with_white_default (const char *color_spec)
+{
+	GdkColor color;
+
+	eel_gdk_color_parse_with_white_default (color_spec, &color);
+	return ((color.red << 8) & EEL_RGB_COLOR_RED)
+		| (color.green & EEL_RGB_COLOR_GREEN)
+		| ((color.blue >> 8) & EEL_RGB_COLOR_BLUE);
+}
+
+guint32
+eel_rgb16_to_rgb (gushort r, gushort g, gushort b)
+{
+	guint32 result;
+
+	result = (0xff0000 | (r & 0xff00));
+	result <<= 8;
+	result |= ((g & 0xff00) | (b >> 8));
+
+	return result;
+}
+
+guint32
+eel_rgb8_to_rgb (guchar r, guchar g, guchar b)
+{
+	return eel_rgb16_to_rgb (r << 8, g << 8, b << 8);
+}
+
+/**
+ * eel_gdk_color_to_rgb
+ * @color: A GdkColor style color.
+ * Returns: An rgb value.
+ *
+ * Converts from a GdkColor stlye color to a gdk_rgb one.
+ * Alpha gets set to fully opaque
+ */
+guint32
+eel_gdk_color_to_rgb (const GdkColor *color)
+{
+	return eel_rgb16_to_rgb (color->red, color->green, color->blue);
+}
+
+/**
+ * eel_gdk_rgb_to_color
+ * @color: a gdk_rgb style value.
+ *
+ * Converts from a gdk_rgb value style to a GdkColor one.
+ * The gdk_rgb color alpha channel is ignored.
+ * 
+ * Return value: A GdkColor structure version of the given RGB color.
+ */
+GdkColor
+eel_gdk_rgb_to_color (guint32 color)
+{
+	GdkColor result;
+
+	result.red = ((color >> 16) & 0xFF) * 0x101;
+	result.green = ((color >> 8) & 0xFF) * 0x101;
+	result.blue = (color & 0xff) * 0x101;
+	result.pixel = 0;
+
+	return result;
+}
+
+/**
+ * eel_gdk_rgb_to_color_spec
+ * @color: a gdk_rgb style value.
+ *
+ * Converts from a gdk_rgb value style to a string color spec.
+ * The gdk_rgb color alpha channel is ignored.
+ * 
+ * Return value: a newly allocated color spec.
+ */
+char *
+eel_gdk_rgb_to_color_spec (const guint32 color)
+{
+	return g_strdup_printf ("#%06X", (guint) (color & 0xFFFFFF));
+}
+
+static guint32
+eel_shift_color_component (guchar component, float shift_by)
+{
+	guint32 result;
+	if (shift_by > 1.0) {
+		result = component * (2 - shift_by);
+	} else {
+		result = 0xff - shift_by * (0xff - component);
+	}
+
+	return result & 0xff;
+}
+
+/**
+ * eel_rgb_shift_color
+ * @color: A color.
+ * @shift_by: darken or lighten factor.
+ * Returns: An darkened or lightened rgb value.
+ *
+ * Darkens (@shift_by > 1) or lightens (@shift_by < 1)
+ * @color.
+ */
+guint32
+eel_rgb_shift_color (guint32 color, float shift_by)
+{
+	guint32 result;
+
+	/* shift red by shift_by */
+	result = eel_shift_color_component((color & 0x00ff0000) >> 16, shift_by);
+	result <<= 8;
+	/* shift green by shift_by */
+	result |=  eel_shift_color_component((color & 0x0000ff00) >> 8, shift_by);
+	result <<= 8;
+	/* shift blue by shift_by */
+	result |=  eel_shift_color_component((color & 0x000000ff), shift_by);
+
+	/* alpha doesn't change */
+	result |= (0xff000000 & color);
+
+	return result;
+}
+
+/**
+ * eel_gdk_color_is_dark:
+ * 
+ * Return true if the given color is `dark'
+ */
+gboolean
+eel_gdk_color_is_dark (GdkColor *color)
+{
+	int intensity;
+
+	intensity = (((color->red >> 8) * 77)
+		     + ((color->green >> 8) * 150)
+		     + ((color->blue >> 8) * 28)) >> 8;
+
+	return intensity < 128;
+}
+
+/**
+ * eel_stipple_bitmap_for_screen:
+ * 
+ * Get pointer to 50% stippled bitmap suitable for use
+ * on @screen. This is a global object; do not free.
+ */
+GdkBitmap *
+eel_stipple_bitmap_for_screen (GdkScreen *screen)
+{
+	static char       stipple_bits[] = { 0x02, 0x01 };
+	static GPtrArray *stipples = NULL;
+	int screen_num, n_screens, i;
+
+	if (stipples == NULL) {
+		n_screens = gdk_display_get_n_screens (
+					gdk_screen_get_display (screen));
+		stipples = g_ptr_array_sized_new (n_screens);
+
+		for (i = 0; i < n_screens; i++) {
+			g_ptr_array_index (stipples, i) = NULL;
+		}
+	}
+
+	screen_num = gdk_screen_get_number (screen);
+
+	if (g_ptr_array_index (stipples, screen_num) == NULL) {
+		g_ptr_array_index (stipples, screen_num) =
+			gdk_bitmap_create_from_data (
+				gdk_screen_get_root_window (screen),
+				stipple_bits, 2, 2);
+	}
+
+	return g_ptr_array_index (stipples, screen_num);
+}
+
+/**
+ * eel_stipple_bitmap:
+ *
+ * Get pointer to 50% stippled bitmap suitable for use
+ * on the default screen. This is a global object; do
+ * not free.
+ *
+ * This method is not multiscreen safe. Do not use it.
+ */
+GdkBitmap *
+eel_stipple_bitmap (void)
+{
+	return eel_stipple_bitmap_for_screen (gdk_screen_get_default ());
+}
+
+/**
+ * eel_gdk_window_bring_to_front:
+ * 
+ * Raise window and give it focus.
+ */
+void 
+eel_gdk_window_bring_to_front (GdkWindow *window)
+{
+	/* This takes care of un-iconifying the window and
+	 * raising it if needed.
+	 */
+	gdk_window_show (window);
+
+	/* If the window was already showing, it would not have
+	 * the focus at this point. Do a little X trickery to
+	 * ensure it is focused.
+	 */
+	eel_gdk_window_focus (window, GDK_CURRENT_TIME);
+}
+
+void
+eel_gdk_window_focus (GdkWindow *window, guint32 timestamp)
+{
+	gdk_error_trap_push ();
+	XSetInputFocus (GDK_DISPLAY (),
+			GDK_WINDOW_XWINDOW (window),
+			RevertToParent,
+			timestamp);
+	gdk_flush();
+	gdk_error_trap_pop ();
+}
+
+void
+eel_gdk_window_set_wm_protocols (GdkWindow *window,
+				 GdkAtom *protocols,
+				 int nprotocols)
+{
+	Atom *atoms;
+	int i;
+
+	atoms = g_new (Atom, nprotocols);
+	for (i = 0; i < nprotocols; i++) {
+		atoms[i] = gdk_x11_atom_to_xatom (protocols[i]);
+	}
+
+	XSetWMProtocols (GDK_WINDOW_XDISPLAY (window),
+			 GDK_WINDOW_XWINDOW (window),
+			 atoms, nprotocols);
+
+	g_free (atoms);
+}
+
+/**
+ * eel_gdk_window_set_wm_hints_input:
+ * 
+ * Set the WM_HINTS.input flag to the passed in value
+ */
+void
+eel_gdk_window_set_wm_hints_input (GdkWindow *window, gboolean status)
+{
+	Display *dpy;
+	Window id;
+	XWMHints *wm_hints;
+
+	g_return_if_fail (window != NULL);
+
+	dpy = GDK_WINDOW_XDISPLAY (window);
+	id = GDK_WINDOW_XWINDOW (window);
+
+	wm_hints = XGetWMHints (dpy, id);
+	if (wm_hints == 0) {
+		wm_hints = XAllocWMHints ();
+	}
+
+	wm_hints->flags |= InputHint;
+	wm_hints->input = (status == FALSE) ? False : True;
+
+	XSetWMHints (dpy, id, wm_hints);
+	XFree (wm_hints);
+}
+
+void
+eel_gdk_window_set_invisible_cursor (GdkWindow *window)
+{
+	GdkBitmap *empty_bitmap;
+	GdkCursor *cursor;
+	GdkColor useless;
+	char invisible_cursor_bits[] = { 0x0 };	
+	
+	useless.red = useless.green = useless.blue = 0;
+	useless.pixel = 0;
+	
+	empty_bitmap = gdk_bitmap_create_from_data (window,
+						    invisible_cursor_bits,
+						    1, 1);
+	
+	cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
+					     empty_bitmap,
+					     &useless,
+					     &useless, 0, 0);
+
+	gdk_window_set_cursor (window, cursor);
+
+	gdk_cursor_unref (cursor);
+
+	g_object_unref (empty_bitmap);
+}
+
+EelGdkGeometryFlags
+eel_gdk_parse_geometry (const char *string, int *x_return, int *y_return,
+			     guint *width_return, guint *height_return)
+{
+	int x11_flags;
+	EelGdkGeometryFlags gdk_flags;
+
+	g_return_val_if_fail (string != NULL, EEL_GDK_NO_VALUE);
+	g_return_val_if_fail (x_return != NULL, EEL_GDK_NO_VALUE);
+	g_return_val_if_fail (y_return != NULL, EEL_GDK_NO_VALUE);
+	g_return_val_if_fail (width_return != NULL, EEL_GDK_NO_VALUE);
+	g_return_val_if_fail (height_return != NULL, EEL_GDK_NO_VALUE);
+
+	x11_flags = XParseGeometry (string, x_return, y_return,
+				    width_return, height_return);
+
+	gdk_flags = EEL_GDK_NO_VALUE;
+	if (x11_flags & XValue) {
+		gdk_flags |= EEL_GDK_X_VALUE;
+	}
+	if (x11_flags & YValue) {
+		gdk_flags |= EEL_GDK_Y_VALUE;
+	}
+	if (x11_flags & WidthValue) {
+		gdk_flags |= EEL_GDK_WIDTH_VALUE;
+	}
+	if (x11_flags & HeightValue) {
+		gdk_flags |= EEL_GDK_HEIGHT_VALUE;
+	}
+	if (x11_flags & XNegative) {
+		gdk_flags |= EEL_GDK_X_NEGATIVE;
+	}
+	if (x11_flags & YNegative) {
+		gdk_flags |= EEL_GDK_Y_NEGATIVE;
+	}
+
+	return gdk_flags;
+}
+
+void
+eel_gdk_draw_layout_with_drop_shadow (GdkDrawable         *drawable,
+				      GdkGC               *gc,
+				      GdkColor            *text_color,
+				      GdkColor            *shadow_color,
+				      int                  x,
+				      int                  y,
+				      PangoLayout         *layout)
+{
+	gdk_draw_layout_with_colors (drawable, gc,
+				     x+1, y+1,
+				     layout,
+				     shadow_color, NULL);
+	
+	gdk_draw_layout_with_colors (drawable, gc,
+				     x, y,
+				     layout,
+				     text_color, NULL);
+}
+
+#if ! defined (EEL_OMIT_SELF_CHECK)
+
+static char *
+eel_gdk_color_as_hex_string (GdkColor color)
+{
+	return g_strdup_printf ("%04X%04X%04X",
+				color.red, color.green, color.blue);
+}
+
+static char *
+eel_self_check_parse (const char *color_spec)
+{
+	GdkColor color;
+
+	eel_gdk_color_parse_with_white_default (color_spec, &color);
+	return eel_gdk_color_as_hex_string (color);
+}
+
+static char *
+eel_self_check_gdk_rgb_to_color (guint32 color)
+{
+	GdkColor result;
+
+	result = eel_gdk_rgb_to_color (color);
+
+	return eel_gdk_color_as_hex_string (result);
+}
+
+void
+eel_self_check_gdk_extensions (void)
+{
+	/* eel_interpolate_color */
+	EEL_CHECK_INTEGER_RESULT (eel_interpolate_color (0.0, 0, 0), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_interpolate_color (0.0, 0, 0xFFFFFF), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_interpolate_color (0.5, 0, 0xFFFFFF), 0x7F7F7F);
+	EEL_CHECK_INTEGER_RESULT (eel_interpolate_color (1.0, 0, 0xFFFFFF), 0xFFFFFF);
+
+	/* eel_fill_rectangle */
+	/* Make a GdkImage and fill it, maybe? */
+
+	/* eel_fill_rectangle_with_color */
+
+	/* eel_fill_rectangle_with_gradient */
+
+	/* eel_gradient_new */
+	EEL_CHECK_STRING_RESULT (eel_gradient_new ("", "", FALSE), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_new ("a", "b", FALSE), "a-b");
+	EEL_CHECK_STRING_RESULT (eel_gradient_new ("a", "b", TRUE), "a-b:h");
+	EEL_CHECK_STRING_RESULT (eel_gradient_new ("a", "a", FALSE), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_new ("a", "a", TRUE), "a");
+
+	/* eel_gradient_is_gradient */
+	EEL_CHECK_BOOLEAN_RESULT (eel_gradient_is_gradient (""), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_gradient_is_gradient ("-"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_gradient_is_gradient ("a"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_gradient_is_gradient ("a-b"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_gradient_is_gradient ("a-b:h"), TRUE);
+
+	/* eel_gradient_get_start_color_spec */
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec (""), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("-"), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a-b"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a-"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("-b"), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a:h"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a:v"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a:c"), "a:c");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a:-b"), "a:");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_start_color_spec ("a:-b:v"), "a:");
+
+	/* eel_gradient_get_end_color_spec */
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec (""), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("-"), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a-b"), "b");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a-"), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("-b"), "b");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a:h"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a:v"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a:c"), "a:c");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a:-b"), "b");
+	EEL_CHECK_STRING_RESULT (eel_gradient_get_end_color_spec ("a:-b:v"), "b");
+
+	/* eel_gradient_set_left_color_spec */
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("", ""), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("a", ""), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("a", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("a", "b"), "b-a:h");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("a-c:v", "b"), "b-c:h");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("a-c:v", "c"), "c");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_left_color_spec ("a:-b:v", "d"), "d-b:h");
+
+	/* eel_gradient_set_top_color_spec */
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("", ""), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("a", ""), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("a", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("a", "b"), "b-a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("a-c:v", "b"), "b-c");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("a-c:v", "c"), "c");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_top_color_spec ("a:-b:h", "d"), "d-b");
+
+	/* eel_gradient_set_right_color_spec */
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("", ""), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("a", ""), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("a", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("a", "b"), "a-b:h");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("a-c:v", "b"), "a-b:h");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("a-c:v", "c"), "a-c:h");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_right_color_spec ("a:-b:v", "d"), "a:-d:h");
+
+	/* eel_gradient_set_bottom_color_spec */
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("", ""), "");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("a", ""), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("a", "a"), "a");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("a", "b"), "a-b");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("a-c:v", "b"), "a-b");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("a-c:v", "c"), "a-c");
+	EEL_CHECK_STRING_RESULT (eel_gradient_set_bottom_color_spec ("a:-b:h", "d"), "a:-d");
+
+	/* eel_gdk_color_parse_with_white_default */
+	EEL_CHECK_STRING_RESULT (eel_self_check_parse (""), "FFFFFFFFFFFF");
+	EEL_CHECK_STRING_RESULT (eel_self_check_parse ("a"), "FFFFFFFFFFFF");
+	EEL_CHECK_STRING_RESULT (eel_self_check_parse ("white"), "FFFFFFFFFFFF");
+	EEL_CHECK_STRING_RESULT (eel_self_check_parse ("black"), "000000000000");
+	EEL_CHECK_STRING_RESULT (eel_self_check_parse ("red"), "FFFF00000000");
+	EEL_CHECK_STRING_RESULT (eel_self_check_parse ("#012345"), "010123234545");
+	/* EEL_CHECK_STRING_RESULT (eel_self_check_parse ("rgb:0123/4567/89AB"), "#014589"); */
+
+	/* eel_gdk_rgb_to_color */
+	EEL_CHECK_STRING_RESULT (eel_self_check_gdk_rgb_to_color (EEL_RGB_COLOR_RED), "FFFF00000000");
+	EEL_CHECK_STRING_RESULT (eel_self_check_gdk_rgb_to_color (EEL_RGB_COLOR_BLACK), "000000000000");
+	EEL_CHECK_STRING_RESULT (eel_self_check_gdk_rgb_to_color (EEL_RGB_COLOR_WHITE), "FFFFFFFFFFFF");
+	EEL_CHECK_STRING_RESULT (eel_self_check_gdk_rgb_to_color (EEL_RGB_COLOR_PACK (0x01, 0x23, 0x45)), "010123234545");
+	
+	/* EEL_RGBA_COLOR_PACK */
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0xFF, 0x00, 0x00, 00), EEL_RGB_COLOR_RED);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0x00, 0xFF, 0x00, 00), EEL_RGB_COLOR_GREEN);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0x00, 0x00, 0xFF, 00), EEL_RGB_COLOR_BLUE);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0xFF, 0xFF, 0xFF, 00), EEL_RGB_COLOR_WHITE);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0x00, 0x00, 0x00, 00), EEL_RGB_COLOR_BLACK);
+
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0xFF, 0x00, 0x00, 0xFF), EEL_RGBA_COLOR_OPAQUE_RED);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0x00, 0xFF, 0x00, 0xFF), EEL_RGBA_COLOR_OPAQUE_GREEN);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0x00, 0x00, 0xFF, 0xFF), EEL_RGBA_COLOR_OPAQUE_BLUE);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0xFF, 0xFF, 0xFF, 0xFF), EEL_RGBA_COLOR_OPAQUE_WHITE);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_PACK (0x00, 0x00, 0x00, 0xFF), EEL_RGBA_COLOR_OPAQUE_BLACK);
+
+	/* EEL_RGB_COLOR_PACK */
+	EEL_CHECK_INTEGER_RESULT (EEL_RGB_COLOR_PACK (0xFF, 0x00, 0x00), EEL_RGBA_COLOR_OPAQUE_RED);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGB_COLOR_PACK (0x00, 0xFF, 0x00), EEL_RGBA_COLOR_OPAQUE_GREEN);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGB_COLOR_PACK (0x00, 0x00, 0xFF), EEL_RGBA_COLOR_OPAQUE_BLUE);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGB_COLOR_PACK (0xFF, 0xFF, 0xFF), EEL_RGBA_COLOR_OPAQUE_WHITE);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGB_COLOR_PACK (0x00, 0x00, 0x00), EEL_RGBA_COLOR_OPAQUE_BLACK);
+
+	/* EEL_RGBA_COLOR_GET_R */
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGBA_COLOR_OPAQUE_RED), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGBA_COLOR_OPAQUE_GREEN), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGBA_COLOR_OPAQUE_BLUE), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGBA_COLOR_OPAQUE_WHITE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGBA_COLOR_OPAQUE_BLACK), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGB_COLOR_RED), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGB_COLOR_GREEN), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGB_COLOR_BLUE), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGB_COLOR_WHITE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_R (EEL_RGB_COLOR_BLACK), 0x00);
+
+	/* EEL_RGBA_COLOR_GET_G */
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGBA_COLOR_OPAQUE_RED), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGBA_COLOR_OPAQUE_GREEN), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGBA_COLOR_OPAQUE_BLUE), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGBA_COLOR_OPAQUE_WHITE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGBA_COLOR_OPAQUE_BLACK), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGB_COLOR_RED), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGB_COLOR_GREEN), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGB_COLOR_BLUE), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGB_COLOR_WHITE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_G (EEL_RGB_COLOR_BLACK), 0x00);
+
+	/* EEL_RGBA_COLOR_GET_B */
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGBA_COLOR_OPAQUE_RED), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGBA_COLOR_OPAQUE_GREEN), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGBA_COLOR_OPAQUE_BLUE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGBA_COLOR_OPAQUE_WHITE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGBA_COLOR_OPAQUE_BLACK), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGB_COLOR_RED), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGB_COLOR_GREEN), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGB_COLOR_BLUE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGB_COLOR_WHITE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_B (EEL_RGB_COLOR_BLACK), 0x00);
+
+	/* EEL_RGBA_COLOR_GET_A */
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGBA_COLOR_OPAQUE_RED), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGBA_COLOR_OPAQUE_GREEN), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGBA_COLOR_OPAQUE_BLUE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGBA_COLOR_OPAQUE_WHITE), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGBA_COLOR_OPAQUE_BLACK), 0xFF);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGB_COLOR_RED), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGB_COLOR_GREEN), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGB_COLOR_BLUE), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGB_COLOR_WHITE), 0x00);
+	EEL_CHECK_INTEGER_RESULT (EEL_RGBA_COLOR_GET_A (EEL_RGB_COLOR_BLACK), 0x00);
+
+}
+
+#endif /* ! EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-gdk-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gdk-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,163 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gdk-extensions.h: Graphics routines to augment what's in gdk.
+
+   Copyright (C) 1999, 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>,
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_GDK_EXTENSIONS_H
+#define EEL_GDK_EXTENSIONS_H
+
+#include <gdk/gdk.h>
+
+#define EEL_RGB_COLOR_RED	0xFF0000
+#define EEL_RGB_COLOR_GREEN	0x00FF00
+#define EEL_RGB_COLOR_BLUE	0x0000FF
+#define EEL_RGB_COLOR_WHITE	0xFFFFFF
+#define EEL_RGB_COLOR_BLACK	0x000000
+
+#define EEL_RGBA_COLOR_OPAQUE_RED	0xFFFF0000
+#define EEL_RGBA_COLOR_OPAQUE_GREEN	0xFF00FF00
+#define EEL_RGBA_COLOR_OPAQUE_BLUE	0xFF0000FF
+#define EEL_RGBA_COLOR_OPAQUE_WHITE	0xFFFFFFFF
+#define EEL_RGBA_COLOR_OPAQUE_BLACK	0xFF000000
+
+/* Pack RGBA values into 32 bits */
+#define EEL_RGBA_COLOR_PACK(r, g, b, a)		\
+( (((guint32)a) << 24) |			\
+  (((guint32)r) << 16) |			\
+  (((guint32)g) <<  8) |			\
+  (((guint32)b) <<  0) )
+
+/* Pack opaque RGBA values into 32 bits */
+#define EEL_RGB_COLOR_PACK(r, g, b)		\
+EEL_RGBA_COLOR_PACK((r), (g), (b), 0xFF)
+
+/* Access the individual RGBA components */
+#define EEL_RGBA_COLOR_GET_R(color) (((color) >> 16) & 0xff)
+#define EEL_RGBA_COLOR_GET_G(color) (((color) >> 8) & 0xff)
+#define EEL_RGBA_COLOR_GET_B(color) (((color) >> 0) & 0xff)
+#define EEL_RGBA_COLOR_GET_A(color) (((color) >> 24) & 0xff)
+
+/* Bits returned by eel_gdk_parse_geometry */
+typedef enum {
+	EEL_GDK_NO_VALUE     = 0x00,
+	EEL_GDK_X_VALUE      = 0x01,
+	EEL_GDK_Y_VALUE      = 0x02,
+	EEL_GDK_WIDTH_VALUE  = 0x04,
+	EEL_GDK_HEIGHT_VALUE = 0x08,
+	EEL_GDK_ALL_VALUES   = 0x0f,
+	EEL_GDK_X_NEGATIVE   = 0x10,
+	EEL_GDK_Y_NEGATIVE   = 0x20
+} EelGdkGeometryFlags;
+
+/* A gradient spec. is a string that contains a specifier for either a
+   color or a gradient. If the string has a "-" in it, then it's a gradient.
+   The gradient is vertical by default and the spec. can end with ":v" to indicate that.
+   If the gradient ends with ":h", the gradient is horizontal.
+*/
+char *              eel_gradient_new                       (const char          *start_color,
+							    const char          *end_color,
+							    gboolean             is_horizontal);
+char *              eel_gradient_parse_one_color_spec      (const char          *spec,
+							    int                 *percent,
+							    const char         **next_spec);
+gboolean            eel_gradient_is_gradient               (const char          *gradient_spec);
+char *              eel_gradient_get_start_color_spec      (const char          *gradient_spec);
+char *              eel_gradient_get_end_color_spec        (const char          *gradient_spec);
+gboolean            eel_gradient_is_horizontal             (const char          *gradient_spec);
+char *              eel_gradient_set_left_color_spec       (const char          *gradient_spec,
+							    const char          *left_color);
+char *              eel_gradient_set_top_color_spec        (const char          *gradient_spec,
+							    const char          *top_color);
+char *              eel_gradient_set_right_color_spec      (const char          *gradient_spec,
+							    const char          *right_color);
+char *              eel_gradient_set_bottom_color_spec     (const char          *gradient_spec,
+							    const char          *bottom_color);
+
+
+/* A version of parse_color that substitutes a default color instead of returning
+   a boolean to indicate it cannot be parsed.
+*/
+void                eel_gdk_color_parse_with_default       (const char          *color_spec,
+							    const GdkColor      *default_color,
+							    GdkColor            *parsed_color);
+void                eel_gdk_color_parse_with_white_default (const char          *color_spec,
+							    GdkColor            *parsed_color);
+guint32             eel_parse_rgb_with_default             (const char          *color_spec,
+							    guint32              default_rgb);
+guint32             eel_parse_rgb_with_white_default       (const char          *color_spec);
+guint32             eel_rgb_shift_color                    (guint32              color,
+							    float                shift_by);
+guint32             eel_rgb16_to_rgb                       (gushort              r,
+							    gushort              g,
+							    gushort              b);
+guint32             eel_rgb8_to_rgb                        (guchar               r,
+							    guchar               g,
+							    guchar               b);
+guint32             eel_gdk_color_to_rgb                   (const GdkColor      *color);
+GdkColor            eel_gdk_rgb_to_color                   (guint32              color);
+char *              eel_gdk_rgb_to_color_spec              (guint32              color);
+
+gboolean            eel_gdk_color_is_dark                  (GdkColor            *color);
+
+/* A routine to get a 50% gray stippled bitmap for use in some types of highlighting. */
+GdkBitmap *         eel_stipple_bitmap_for_screen          (GdkScreen *screen);
+GdkBitmap *         eel_stipple_bitmap                     (void);
+
+
+/* Misc GdkRectangle helper functions */
+gboolean            eel_gdk_rectangle_contains_rectangle   (GdkRectangle         outer,
+							    GdkRectangle         inner);
+
+
+/* A basic operation we use for drawing gradients is interpolating two colors.*/
+guint32             eel_interpolate_color                  (gdouble              ratio,
+							    guint32              start_rgb,
+							    guint32              end_rgb);
+
+/* Misc GdkWindow helper functions */
+void                eel_gdk_window_bring_to_front          (GdkWindow           *window);
+void                eel_gdk_window_set_invisible_cursor    (GdkWindow           *window);
+void                eel_gdk_window_focus                   (GdkWindow           *window,
+							    guint32              timestamp);
+void                eel_gdk_window_set_wm_protocols        (GdkWindow           *window,
+							    GdkAtom             *protocols,
+							    int                  nprotocols);
+
+
+void                eel_gdk_window_set_wm_hints_input      (GdkWindow           *w,
+							    gboolean             status);
+
+/* Wrapper for XParseGeometry */
+EelGdkGeometryFlags eel_gdk_parse_geometry                 (const char          *string,
+							    int                 *x_return,
+							    int                 *y_return,
+							    guint               *width_return,
+							    guint               *height_return);
+void                eel_gdk_draw_layout_with_drop_shadow   (GdkDrawable         *drawable,
+							    GdkGC               *gc,
+							    GdkColor            *text_color,
+							    GdkColor            *shadow_color,
+							    int                  x,
+							    int                  y,
+							    PangoLayout         *layout);
+#endif /* EEL_GDK_EXTENSIONS_H */

Added: trunk/eel/eel-gdk-pixbuf-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gdk-pixbuf-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,1305 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gdk-pixbuf-extensions.c: Routines to augment what's in gdk-pixbuf.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-gdk-pixbuf-extensions.h"
+
+#include "eel-art-gtk-extensions.h"
+#include "eel-debug-drawing.h"
+#include "eel-debug.h"
+#include "eel-gdk-extensions.h"
+#include "eel-glib-extensions.h"
+#include "eel-lib-self-check-functions.h"
+#include "eel-string.h"
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+#include <gio/gio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define LOAD_BUFFER_SIZE 65536
+
+const EelIRect eel_gdk_pixbuf_whole_pixbuf = { G_MININT, G_MININT, G_MAXINT, G_MAXINT };
+
+struct EelPixbufLoadHandle {
+	GCancellable *cancellable;
+	GInputStream *stream;
+	EelPixbufLoadCallback callback;
+	gpointer callback_data;
+	GdkPixbufLoader *loader;
+	char buffer[LOAD_BUFFER_SIZE];
+};
+
+/**
+ * eel_gdk_pixbuf_list_ref
+ * @pixbuf_list: A list of GdkPixbuf objects.
+ *
+ * Refs all the pixbufs.
+ **/
+void
+eel_gdk_pixbuf_list_ref (GList *pixbuf_list)
+{
+	g_list_foreach (pixbuf_list, (GFunc) g_object_ref, NULL);
+}
+
+/**
+ * eel_gdk_pixbuf_list_free
+ * @pixbuf_list: A list of GdkPixbuf objects.
+ *
+ * Unrefs all the pixbufs, then frees the list.
+ **/
+void
+eel_gdk_pixbuf_list_free (GList *pixbuf_list)
+{
+	eel_g_list_free_deep_custom (pixbuf_list, (GFunc) g_object_unref, NULL);
+}
+
+GdkPixbuf *
+eel_gdk_pixbuf_load (const char *uri)
+{
+	GdkPixbuf *pixbuf;
+	GFile *file;
+	GFileInputStream *stream;
+
+	g_return_val_if_fail (uri != NULL, NULL);
+
+	file = g_file_new_for_uri (uri);
+
+	stream = g_file_read (file, NULL, NULL);
+	
+	g_object_unref (file);
+	
+	if (stream == NULL) {
+		return NULL;
+	}
+
+	pixbuf = eel_gdk_pixbuf_load_from_stream (G_INPUT_STREAM (stream));
+
+	g_object_unref (stream);
+		
+	return pixbuf;
+}
+
+GdkPixbuf *
+eel_gdk_pixbuf_load_from_stream (GInputStream  *stream)
+{
+	return eel_gdk_pixbuf_load_from_stream_at_size (stream, -1);
+}
+
+static void
+pixbuf_loader_size_prepared (GdkPixbufLoader *loader,
+			     int              width,
+			     int              height,
+			     gpointer         desired_size_ptr)
+{
+	int size, desired_size;
+	float scale;
+
+	size = MAX (width, height);
+	desired_size = GPOINTER_TO_INT (desired_size_ptr);
+
+	if (size != desired_size) {
+		scale = (float) desired_size / size;
+		gdk_pixbuf_loader_set_size (loader,
+					    floor (scale * width + 0.5),
+					    floor (scale * height + 0.5));
+	}
+}
+
+GdkPixbuf *
+eel_gdk_pixbuf_load_from_stream_at_size (GInputStream  *stream,
+					 int            size)
+{
+	char buffer[LOAD_BUFFER_SIZE];
+	gssize bytes_read;
+	GdkPixbufLoader *loader;
+	GdkPixbuf *pixbuf;
+	gboolean got_eos;
+	
+
+	g_return_val_if_fail (stream != NULL, NULL);
+
+	got_eos = FALSE;
+	loader = gdk_pixbuf_loader_new ();
+
+	if (size > 0) {
+		g_signal_connect (loader, "size-prepared",
+				  G_CALLBACK (pixbuf_loader_size_prepared),
+				  GINT_TO_POINTER (size));
+	}
+
+	while (1) {
+		bytes_read = g_input_stream_read (stream, buffer, sizeof (buffer),
+						  NULL, NULL);
+		
+		if (bytes_read < 0) {
+			break;
+		}
+		if (bytes_read == 0) {
+			got_eos = TRUE;
+			break;
+		}
+		if (!gdk_pixbuf_loader_write (loader,
+					      buffer,
+					      bytes_read,
+					      NULL)) {
+			break;
+		}
+	}
+
+	g_input_stream_close (stream, NULL, NULL);
+	gdk_pixbuf_loader_close (loader, NULL);
+
+	pixbuf = NULL;
+	if (got_eos) {
+		pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+		if (pixbuf != NULL) {
+			g_object_ref (pixbuf);
+		}
+	}
+
+	g_object_unref (loader);
+
+	return pixbuf;
+}
+
+static void
+free_pixbuf_load_handle (EelPixbufLoadHandle *handle)
+{
+	g_object_unref (handle->cancellable);
+	if (handle->loader != NULL) {
+		g_object_unref (handle->loader);
+	}
+	if (handle->stream) {
+		g_input_stream_close_async (handle->stream, 0, NULL, NULL, NULL);
+		g_object_unref (handle->stream);
+	}
+	g_free (handle);
+}
+
+static void
+load_done (EelPixbufLoadHandle *handle, GError *error, gboolean get_pixbuf)
+{
+	GdkPixbuf *pixbuf;
+
+	if (handle->loader != NULL) {
+		gdk_pixbuf_loader_close (handle->loader, NULL);
+	}
+
+	pixbuf = get_pixbuf ? gdk_pixbuf_loader_get_pixbuf (handle->loader) : NULL;
+	
+	handle->callback (error, pixbuf, handle->callback_data);
+
+	free_pixbuf_load_handle (handle);
+}
+
+static void
+file_read_callback (GObject *source_object,
+		    GAsyncResult *res,
+		    gpointer user_data)
+{
+	EelPixbufLoadHandle *handle;
+	gssize bytes_read;
+	GError *error;
+
+	handle = user_data;
+
+	if (g_cancellable_is_cancelled (handle->cancellable)) {
+		free_pixbuf_load_handle (handle);
+		return;
+	}
+	
+	error = NULL;
+	bytes_read = g_input_stream_read_finish  (G_INPUT_STREAM (source_object),
+						  res, &error);
+	
+	if (bytes_read > 0) {
+		if (!gdk_pixbuf_loader_write (handle->loader,
+					      handle->buffer,
+					      bytes_read,
+					      &error)) {
+			bytes_read = -1;
+		} else {
+			g_input_stream_read_async (handle->stream,
+						   handle->buffer,
+						   sizeof (handle->buffer),
+						   0,
+						   handle->cancellable,
+						   file_read_callback, handle);
+			return;
+		}
+	}
+
+	load_done (handle, error, bytes_read == 0);
+	
+	if (error != NULL) {
+		g_error_free (error);
+	}
+}
+
+static void
+file_opened_callback (GObject *source_object,
+		      GAsyncResult *res,
+		      gpointer user_data)
+{
+	EelPixbufLoadHandle *handle;
+	GFileInputStream *stream;
+	GError *error;
+
+	handle = user_data;
+
+	if (g_cancellable_is_cancelled (handle->cancellable)) {
+		free_pixbuf_load_handle (handle);
+		return;
+	}
+
+	error = NULL;
+	stream = g_file_read_finish (G_FILE (source_object), res, &error);
+
+	if (stream == NULL) {
+		load_done (handle, error, FALSE);
+		g_error_free (error);
+		return;
+	}
+
+	handle->stream = G_INPUT_STREAM (stream);
+	handle->loader = gdk_pixbuf_loader_new ();
+
+	
+	g_input_stream_read_async (handle->stream,
+				   handle->buffer,
+				   sizeof (handle->buffer),
+				   0,
+				   handle->cancellable,
+				   file_read_callback, handle);
+}
+
+EelPixbufLoadHandle *
+eel_gdk_pixbuf_load_async (const char *uri,
+			   int priority,
+			   EelPixbufLoadCallback callback,
+			   gpointer callback_data)
+{
+	EelPixbufLoadHandle *handle;
+	GFile *file;
+
+	handle = g_new0 (EelPixbufLoadHandle, 1);
+	handle->cancellable = g_cancellable_new ();
+	handle->callback = callback;
+	handle->callback_data = callback_data;
+
+	file = g_file_new_for_uri (uri);
+	
+	g_file_read_async (file, priority, handle->cancellable,
+			   file_opened_callback, handle);
+
+	return handle;
+}
+
+void
+eel_cancel_gdk_pixbuf_load (EelPixbufLoadHandle *handle)
+{
+	if (handle == NULL) {
+		return;
+	}
+	
+	g_cancellable_cancel (handle->cancellable);
+}
+
+/* return the average value of each component */
+guint32
+eel_gdk_pixbuf_average_value (GdkPixbuf *pixbuf)
+{
+	guint64 a_total, r_total, g_total, b_total;
+	guint row, column;
+	int row_stride;
+	const guchar *pixels, *p;
+	int r, g, b, a;
+	guint64 dividend;
+	guint width, height;
+
+	width = gdk_pixbuf_get_width (pixbuf);
+	height = gdk_pixbuf_get_height (pixbuf);
+	row_stride = gdk_pixbuf_get_rowstride (pixbuf);
+	pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+	/* iterate through the pixbuf, counting up each component */
+	a_total = 0;
+	r_total = 0;
+	g_total = 0;
+	b_total = 0;
+
+	if (gdk_pixbuf_get_has_alpha (pixbuf)) {
+		for (row = 0; row < height; row++) {
+			p = pixels + (row * row_stride);
+			for (column = 0; column < width; column++) {
+				r = *p++;
+				g = *p++;
+				b = *p++;
+				a = *p++;
+				
+				a_total += a;
+				r_total += r * a;
+				g_total += g * a;
+				b_total += b * a;
+			}
+		}
+		dividend = height * width * 0xFF;
+		a_total *= 0xFF;
+	} else {
+		for (row = 0; row < height; row++) {
+			p = pixels + (row * row_stride);
+			for (column = 0; column < width; column++) {
+				r = *p++;
+				g = *p++;
+				b = *p++;
+				
+				r_total += r;
+				g_total += g;
+				b_total += b;
+			}
+		}
+		dividend = height * width;
+		a_total = dividend * 0xFF;
+	}
+
+	return ((a_total + dividend / 2) / dividend) << 24
+		| ((r_total + dividend / 2) / dividend) << 16
+		| ((g_total + dividend / 2) / dividend) << 8
+		| ((b_total + dividend / 2) / dividend);
+}
+
+double
+eel_gdk_scale_to_fit_factor (int width, int height,
+			     int max_width, int max_height,
+			     int *scaled_width, int *scaled_height)
+{
+	double scale_factor;
+	
+	scale_factor = MIN (max_width  / (double) width, max_height / (double) height);
+
+	*scaled_width  = floor (width * scale_factor + .5);
+	*scaled_height = floor (height * scale_factor + .5);
+
+	return scale_factor;
+}
+
+/* Returns a scaled copy of pixbuf, preserving aspect ratio. The copy will
+ * be scaled as large as possible without exceeding the specified width and height.
+ */
+GdkPixbuf *
+eel_gdk_pixbuf_scale_to_fit (GdkPixbuf *pixbuf, int max_width, int max_height)
+{
+	int scaled_width;
+	int scaled_height;
+
+	eel_gdk_scale_to_fit_factor (gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
+				     max_width, max_height,
+				     &scaled_width, &scaled_height);
+
+	return gdk_pixbuf_scale_simple (pixbuf, scaled_width, scaled_height, GDK_INTERP_BILINEAR);	
+}
+
+/* Returns a copy of pixbuf scaled down, preserving aspect ratio, to fit
+ * within the specified width and height. If it already fits, a copy of
+ * the original, without scaling, is returned.
+ */
+GdkPixbuf *
+eel_gdk_pixbuf_scale_down_to_fit (GdkPixbuf *pixbuf, int max_width, int max_height)
+{
+	int scaled_width;
+	int scaled_height;
+	
+	double scale_factor;
+
+	scale_factor = eel_gdk_scale_to_fit_factor (gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
+						    max_width, max_height,
+						    &scaled_width, &scaled_height);
+
+	if (scale_factor >= 1.0) {
+		return gdk_pixbuf_copy (pixbuf);
+	} else {				
+		return eel_gdk_pixbuf_scale_down (pixbuf, scaled_width, scaled_height);	
+	}
+}
+
+double
+eel_gdk_scale_to_min_factor (int width, int height,
+			     int min_width, int min_height,
+			     int *scaled_width, int *scaled_height)
+{
+	double scale_factor;
+
+	scale_factor = MAX (min_width / (double) width, min_height / (double) height);
+
+	*scaled_width  = floor (width * scale_factor + .5);
+	*scaled_height = floor (height * scale_factor + .5);
+
+	return scale_factor;
+}
+
+/* Returns a scaled copy of pixbuf, preserving aspect ratio. The copy will
+ * be scaled as small as possible without going under the specified width and height.
+ */
+GdkPixbuf *
+eel_gdk_pixbuf_scale_to_min (GdkPixbuf *pixbuf, int min_width, int min_height)
+{
+	int scaled_width;
+	int scaled_height;
+
+	eel_gdk_scale_to_min_factor (gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf),
+				     min_width, min_height,
+				     &scaled_width, &scaled_height);
+
+	return gdk_pixbuf_scale_simple (pixbuf, scaled_width, scaled_height, GDK_INTERP_BILINEAR);
+}
+
+/**
+ * eel_gdk_pixbuf_is_valid:
+ * @pixbuf: A GdkPixbuf
+ *
+ * Return value: A boolean indicating whether the given pixbuf is valid.
+ *
+ * A pixbuf is valid if:
+ * 
+ *   1. It is non NULL
+ *   2. It is has non NULL pixel data.
+ *   3. It has width and height greater than 0.
+ */
+gboolean
+eel_gdk_pixbuf_is_valid (const GdkPixbuf *pixbuf)
+{
+	return ((pixbuf != NULL)
+		&& (gdk_pixbuf_get_pixels (pixbuf) != NULL)
+		&& (gdk_pixbuf_get_width (pixbuf) > 0)
+		&& (gdk_pixbuf_get_height (pixbuf) > 0));
+}
+
+/**
+ * eel_gdk_pixbuf_get_dimensions:
+ * @pixbuf: A GdkPixbuf
+ *
+ * Return value: The dimensions of the pixbuf as a EelDimensions.
+ *
+ * This function is useful in code that uses libart rect 
+ * intersection routines.
+ */
+EelDimensions
+eel_gdk_pixbuf_get_dimensions (const GdkPixbuf *pixbuf)
+{
+	EelDimensions dimensions;
+
+	g_return_val_if_fail (eel_gdk_pixbuf_is_valid (pixbuf), eel_dimensions_empty);
+
+	dimensions.width = gdk_pixbuf_get_width (pixbuf);
+	dimensions.height = gdk_pixbuf_get_height (pixbuf);
+
+	return dimensions;
+}
+
+/**
+ * eel_gdk_pixbuf_fill_rectangle_with_color:
+ * @pixbuf: Target pixbuf to fill into.
+ * @area: Rectangle to fill.
+ * @color: The color to use.
+ *
+ * Fill the rectangle with the the given color.
+ */
+void
+eel_gdk_pixbuf_fill_rectangle_with_color (GdkPixbuf *pixbuf,
+					  EelIRect area,
+					  guint32 color)
+{
+	EelIRect target;
+	guchar red;
+	guchar green;
+	guchar blue;
+	guchar alpha;
+	guchar *pixels;
+	gboolean has_alpha;
+	guint pixel_offset;
+	guint rowstride;
+	guchar *row_offset;
+	int x;
+	int y;
+
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf));
+	
+	target = eel_gdk_pixbuf_intersect (pixbuf, 0, 0, area);
+	if (eel_irect_is_empty (&target)) {
+		return;
+	}
+
+	pixels = gdk_pixbuf_get_pixels (pixbuf);
+	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+	pixel_offset = has_alpha ? 4 : 3;
+	red = EEL_RGBA_COLOR_GET_R (color);
+	green = EEL_RGBA_COLOR_GET_G (color);
+	blue = EEL_RGBA_COLOR_GET_B (color);
+	alpha = EEL_RGBA_COLOR_GET_A (color);
+
+	row_offset = pixels + target.y0 * rowstride;
+
+	for (y = target.y0; y < target.y1; y++) {
+		guchar *offset = row_offset + (target.x0 * pixel_offset);
+		
+		for (x = target.x0; x < target.x1; x++) {
+			*(offset++) = red;
+			*(offset++) = green;
+			*(offset++) = blue;
+			
+			if (has_alpha) {
+				*(offset++) = alpha;
+			}
+			
+		}
+
+		row_offset += rowstride;
+	}
+}
+
+gboolean
+eel_gdk_pixbuf_save_to_file (const GdkPixbuf *pixbuf,
+			     const char *file_name)
+{
+	return gdk_pixbuf_save ((GdkPixbuf *) pixbuf,
+				file_name, "png", NULL, NULL);
+}
+
+void
+eel_gdk_pixbuf_ref_if_not_null (GdkPixbuf *pixbuf_or_null)
+{
+	if (pixbuf_or_null != NULL) {
+		g_object_ref (pixbuf_or_null);
+	}
+}
+
+void
+eel_gdk_pixbuf_unref_if_not_null (GdkPixbuf *pixbuf_or_null)
+{
+	if (pixbuf_or_null != NULL) {
+		g_object_unref (pixbuf_or_null);
+	}
+}
+
+void
+eel_gdk_pixbuf_draw_to_drawable (const GdkPixbuf *pixbuf,
+				 GdkDrawable *drawable,
+				 GdkGC *gc,
+				 int source_x,
+				 int source_y,
+				 EelIRect destination_area,
+				 GdkRgbDither dither,
+				 GdkPixbufAlphaMode alpha_compositing_mode,
+				 int alpha_threshold)
+{
+	EelDimensions dimensions;
+	EelIRect target;
+	EelIRect source;
+	int target_width;
+	int target_height;
+	int source_width;
+	int source_height;
+
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf));
+	g_return_if_fail (drawable != NULL);
+	g_return_if_fail (gc != NULL);
+	g_return_if_fail (!eel_irect_is_empty (&destination_area));
+ 	g_return_if_fail (alpha_threshold > EEL_OPACITY_FULLY_TRANSPARENT);
+ 	g_return_if_fail (alpha_threshold <= EEL_OPACITY_FULLY_OPAQUE);
+ 	g_return_if_fail (alpha_compositing_mode >= GDK_PIXBUF_ALPHA_BILEVEL);
+ 	g_return_if_fail (alpha_compositing_mode <= GDK_PIXBUF_ALPHA_FULL);
+
+	dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf);
+	
+	g_return_if_fail (source_x >= 0);
+	g_return_if_fail (source_y >= 0);
+	g_return_if_fail (source_x < dimensions.width);
+	g_return_if_fail (source_y < dimensions.height);
+
+	/* Clip the destination area to the pixbuf dimensions; bail if no work */
+	target = eel_gdk_pixbuf_intersect (pixbuf,
+					   destination_area.x0,
+					   destination_area.y0,
+					   destination_area);
+	if (eel_irect_is_empty (&target)) {
+		return;
+	}
+
+	/* Assign the source area */
+	source = eel_irect_assign (source_x,
+				       source_y,
+				       dimensions.width - source_x,
+				       dimensions.height - source_y);
+
+	/* Adjust the target width if the source area is smaller than the
+	 * source pixbuf dimensions */
+	target_width = target.x1 - target.x0;
+	target_height = target.y1 - target.y0;
+	source_width = source.x1 - source.x0;
+	source_height = source.y1 - source.y0;
+
+	target.x1 = target.x0 + MIN (target_width, source_width);
+	target.y1 = target.y0 + MIN (target_height, source_height);
+
+	gdk_draw_pixbuf (drawable, gc, (GdkPixbuf *) pixbuf,
+			 source.x0,
+			 source.y0,
+			 target.x0,
+			 target.y0,
+			 target.x1 - target.x0,
+			 target.y1 - target.y0,
+			 dither,
+			 0,
+			 0);
+}
+
+/**
+ * eel_gdk_pixbuf_draw_to_pixbuf:
+ * @pixbuf: The source pixbuf to draw.
+ * @destination_pixbuf: The destination pixbuf.
+ * @source_x: The source pixbuf x coordiate to composite from.
+ * @source_y: The source pixbuf y coordiate to composite from.
+ * @destination_area: The destination area within the destination pixbuf.
+ *                    This area will be clipped if invalid in any way.
+ *
+ * Copy one pixbuf onto another another..  This function has some advantages
+ * over plain gdk_pixbuf_copy_area():
+ *
+ *   Composition paramters (source coordinate, destination area) are
+ *   given in a way that is consistent with the rest of the extensions
+ *   in this file.  That is, it matches the declaration of
+ *   eel_gdk_pixbuf_draw_to_pixbuf_alpha() and 
+ *   eel_gdk_pixbuf_draw_to_drawable() very closely.
+ *
+ *   All values are clipped to make sure they are valid.
+ *
+ */
+void
+eel_gdk_pixbuf_draw_to_pixbuf (const GdkPixbuf *pixbuf,
+			       GdkPixbuf *destination_pixbuf,
+			       int source_x,
+			       int source_y,
+			       EelIRect destination_area)
+{
+	EelDimensions dimensions;
+	EelIRect target;
+	EelIRect source;
+	int target_width;
+	int target_height;
+	int source_width;
+	int source_height;
+	
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf));
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (destination_pixbuf));
+	g_return_if_fail (!eel_irect_is_empty (&destination_area));
+
+	dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf);
+
+	g_return_if_fail (source_x >= 0);
+	g_return_if_fail (source_y >= 0);
+	g_return_if_fail (source_x < dimensions.width);
+	g_return_if_fail (source_y < dimensions.height);
+
+	/* Clip the destination area to the pixbuf dimensions; bail if no work */
+	target = eel_gdk_pixbuf_intersect (destination_pixbuf, 0, 0, destination_area);
+	if (eel_irect_is_empty (&target)) {
+ 		return;
+ 	}
+
+	/* Assign the source area */
+	source = eel_irect_assign (source_x,
+				       source_y,
+				       dimensions.width - source_x,
+				       dimensions.height - source_y);
+
+	/* Adjust the target width if the source area is smaller than the
+	 * source pixbuf dimensions */
+	target_width = target.x1 - target.x0;
+	target_height = target.y1 - target.y0;
+	source_width = source.x1 - source.x0;
+	source_height = source.y1 - source.y0;
+
+	target.x1 = target.x0 + MIN (target_width, source_width);
+	target.y1 = target.y0 + MIN (target_height, source_height);
+
+	gdk_pixbuf_copy_area (pixbuf,
+			      source.x0,
+			      source.y0,
+			      target.x1 - target.x0,
+			      target.y1 - target.y0,
+			      destination_pixbuf,
+			      target.x0,
+			      target.y0);
+}
+
+/**
+ * eel_gdk_pixbuf_draw_to_pixbuf_alpha:
+ * @pixbuf: The source pixbuf to draw.
+ * @destination_pixbuf: The destination pixbuf.
+ * @source_x: The source pixbuf x coordiate to composite from.
+ * @source_y: The source pixbuf y coordiate to composite from.
+ * @destination_area: The destination area within the destination pixbuf.
+ *                    This area will be clipped if invalid in any way.
+ * @opacity: The opacity of the drawn tiles where 0 <= opacity <= 255.
+ * @interpolation_mode: The interpolation mode.  See <gdk-pixbuf.h>
+ *
+ * Composite one pixbuf over another.  This function has some advantages
+ * over plain gdk_pixbuf_composite():
+ *
+ *   Composition paramters (source coordinate, destination area) are
+ *   given in a way that is consistent with the rest of the extensions
+ *   in this file.  That is, it matches the declaration of
+ *   eel_gdk_pixbuf_draw_to_pixbuf() and 
+ *   eel_gdk_pixbuf_draw_to_drawable() very closely.
+ *
+ *   All values are clipped to make sure they are valid.
+ *
+ *   Workaround a limitation in gdk_pixbuf_composite() that does not allow
+ *   the source (x,y) to be greater than (0,0)
+ * 
+ */
+void
+eel_gdk_pixbuf_draw_to_pixbuf_alpha (const GdkPixbuf *pixbuf,
+				     GdkPixbuf *destination_pixbuf,
+				     int source_x,
+				     int source_y,
+				     EelIRect destination_area,
+				     int opacity,
+				     GdkInterpType interpolation_mode)
+{
+	EelDimensions dimensions;
+	EelIRect target;
+	EelIRect source;
+	int target_width;
+	int target_height;
+	int source_width;
+	int source_height;
+
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (pixbuf));
+	g_return_if_fail (eel_gdk_pixbuf_is_valid (destination_pixbuf));
+	g_return_if_fail (!eel_irect_is_empty (&destination_area));
+	g_return_if_fail (opacity >= EEL_OPACITY_FULLY_TRANSPARENT);
+	g_return_if_fail (opacity <= EEL_OPACITY_FULLY_OPAQUE);
+	g_return_if_fail (interpolation_mode >= GDK_INTERP_NEAREST);
+	g_return_if_fail (interpolation_mode <= GDK_INTERP_HYPER);
+	
+	dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf);
+
+	g_return_if_fail (source_x >= 0);
+	g_return_if_fail (source_y >= 0);
+	g_return_if_fail (source_x < dimensions.width);
+	g_return_if_fail (source_y < dimensions.height);
+
+	/* Clip the destination area to the pixbuf dimensions; bail if no work */
+	target = eel_gdk_pixbuf_intersect (destination_pixbuf, 0, 0, destination_area);
+	if (eel_irect_is_empty (&target)) {
+ 		return;
+ 	}
+
+	/* Assign the source area */
+	source = eel_irect_assign (source_x,
+				       source_y,
+				       dimensions.width - source_x,
+				       dimensions.height - source_y);
+	
+	/* Adjust the target width if the source area is smaller than the
+	 * source pixbuf dimensions */
+	target_width = target.x1 - target.x0;
+	target_height = target.y1 - target.y0;
+	source_width = source.x1 - source.x0;
+	source_height = source.y1 - source.y0;
+
+	target.x1 = target.x0 + MIN (target_width, source_width);
+	target.y1 = target.y0 + MIN (target_height, source_height);
+	
+	/* If the source point is not (0,0), then we need to create a sub pixbuf
+	 * with only the source area.  This is needed to work around a limitation
+	 * in gdk_pixbuf_composite() that requires the source area to be (0,0). */
+	if (source.x0 != 0 || source.y0 != 0) {
+		EelIRect area;
+		int width;
+		int height;
+
+		width = dimensions.width - source.x0;
+		height = dimensions.height - source.y0;
+		
+		area.x0 = source.x0;
+		area.y0 = source.y0;
+		area.x1 = area.x0 + width;
+		area.y1 = area.y0 + height;
+		
+		pixbuf = eel_gdk_pixbuf_new_from_pixbuf_sub_area ((GdkPixbuf *) pixbuf, area);
+	} else {
+		g_object_ref (G_OBJECT (pixbuf));
+	}
+	
+	gdk_pixbuf_composite (pixbuf,
+			      destination_pixbuf,
+			      target.x0,
+			      target.y0,
+			      target.x1 - target.x0,
+			      target.y1 - target.y0,
+			      target.x0,
+			      target.y0,
+			      1.0,
+			      1.0,
+			      interpolation_mode,
+			      opacity);
+
+	g_object_unref (G_OBJECT (pixbuf));
+}
+
+static void
+pixbuf_destroy_callback (guchar  *pixels,
+			 gpointer callback_data)
+{
+	g_assert (pixels != NULL);
+	g_assert (callback_data != NULL);
+
+	g_object_unref (callback_data);
+}
+
+/**
+ * eel_gdk_pixbuf_new_from_pixbuf_sub_area:
+ * @pixbuf: The source pixbuf.
+ * @area: The area within the source pixbuf to use for the sub pixbuf.
+ *        This area needs to be contained within the bounds of the 
+ *        source pixbuf, otherwise it will be clipped to that. 
+ *
+ * Return value: A newly allocated pixbuf that shares the pixel data
+ *               of the source pixbuf in order to represent a sub area.
+ *
+ * Create a pixbuf from a sub area of another pixbuf.  The resulting pixbuf
+ * will share the pixel data of the source pixbuf.  Memory bookeeping is
+ * all taken care for the caller.  All you need to do is gdk_pixbuf_unref()
+ * the resulting pixbuf to properly free resources.
+ */
+GdkPixbuf *
+eel_gdk_pixbuf_new_from_pixbuf_sub_area (GdkPixbuf *pixbuf,
+					 EelIRect area)
+{
+	GdkPixbuf *sub_pixbuf;
+	EelIRect target;
+	guchar *pixels;
+	
+	g_return_val_if_fail (eel_gdk_pixbuf_is_valid (pixbuf), NULL);
+	g_return_val_if_fail (!eel_irect_is_empty (&area), NULL);
+
+	/* Clip the pixbuf by the given area; bail if no work */
+	target = eel_gdk_pixbuf_intersect (pixbuf, 0, 0, area);
+	if (eel_irect_is_empty (&target)) {
+ 		return NULL;
+ 	}
+
+	/* Since we are going to be sharing the given pixbuf's data, we need 
+	 * to ref it.  It will be unreffed in the destroy function above */
+	g_object_ref (pixbuf);
+
+	/* Compute the offset into the pixel data */
+	pixels = 
+		gdk_pixbuf_get_pixels (pixbuf)
+		+ (target.y0 * gdk_pixbuf_get_rowstride (pixbuf))
+		+ (target.x0 * (gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3));
+	
+	/* Make a pixbuf pretending its real estate is the sub area */
+	sub_pixbuf = gdk_pixbuf_new_from_data (pixels,
+					       GDK_COLORSPACE_RGB,
+					       gdk_pixbuf_get_has_alpha (pixbuf),
+					       8,
+					       eel_irect_get_width (target),
+					       eel_irect_get_height (target),
+					       gdk_pixbuf_get_rowstride (pixbuf),
+					       pixbuf_destroy_callback,
+					       pixbuf);
+
+	return sub_pixbuf;
+}
+
+/**
+ * eel_gdk_pixbuf_new_from_existing_buffer:
+ * @buffer: The existing buffer.
+ * @buffer_rowstride: The existing buffer's rowstride.
+ * @buffer_has_alpha: A boolean value indicating whether the buffer has alpha.
+ * @area: The area within the existing buffer to use for the pixbuf.
+ *        This area needs to be contained within the bounds of the 
+ *        buffer, otherwise memory will be trashed.
+ *
+ * Return value: A newly allocated pixbuf that uses the existing buffer
+ *               for its pixel data.
+ *
+ * Create a pixbuf from an existing buffer.
+ *
+ * The resulting pixbuf is only valid for as long as &buffer is valid.  It is
+ * up to the caller to make sure they both exist in the same scope.
+ * Also, it is up to the caller to make sure that the given area is fully 
+ * contained in the buffer, otherwise memory trashing will happen.
+ */
+GdkPixbuf *
+eel_gdk_pixbuf_new_from_existing_buffer (guchar *buffer,
+					 int buffer_rowstride,
+					 gboolean buffer_has_alpha,
+					 EelIRect area)
+{
+	GdkPixbuf *pixbuf;
+	guchar *pixels;
+	
+	g_return_val_if_fail (buffer != NULL, NULL);
+	g_return_val_if_fail (buffer_rowstride > 0, NULL);
+	g_return_val_if_fail (!eel_irect_is_empty (&area), NULL);
+	
+	/* Compute the offset into the buffer */
+	pixels = 
+		buffer
+		+ (area.y0 * buffer_rowstride)
+		+ (area.x0 * (buffer_has_alpha ? 4 : 3));
+	
+	pixbuf = gdk_pixbuf_new_from_data (pixels,
+					   GDK_COLORSPACE_RGB,
+					   buffer_has_alpha,
+					   8,
+					   eel_irect_get_width (area),
+					   eel_irect_get_height (area),
+					   buffer_rowstride,
+					   NULL,
+					   NULL);
+
+	return pixbuf;
+}
+
+/**
+ * eel_gdk_pixbuf_intersect:
+ * @pixbuf: A GdkPixbuf.
+ * @pixbuf_x: X coordinate of pixbuf.
+ * @pixbuf_y: Y coordinate of pixbuf.
+ * @rectangle: An EelIRect.
+ *
+ * Return value: The intersection of the pixbuf and the given rectangle.
+ *
+ */
+EelIRect
+eel_gdk_pixbuf_intersect (const GdkPixbuf *pixbuf,
+			  int pixbuf_x,
+			  int pixbuf_y,
+			  EelIRect rectangle)
+{
+	EelIRect intersection;
+	EelIRect bounds;
+	EelDimensions dimensions;
+
+	g_return_val_if_fail (eel_gdk_pixbuf_is_valid (pixbuf), eel_irect_empty);
+
+	dimensions = eel_gdk_pixbuf_get_dimensions (pixbuf);
+	bounds = eel_irect_assign_dimensions (pixbuf_x, pixbuf_y, dimensions);
+
+	eel_irect_intersect (&intersection, &rectangle, &bounds);
+
+	/* In theory, this is not needed because a rectangle is empty
+	 * regardless of how MUCH negative the dimensions are.  
+	 * However, to make debugging and self checks simpler, we
+	 * consistenly return a standard empty rectangle.
+	 */
+	if (eel_irect_is_empty (&intersection)) {
+		return eel_irect_empty;
+	}
+
+	return intersection;
+}
+
+GdkPixbuf *
+eel_gdk_pixbuf_scale_down (GdkPixbuf *pixbuf,
+			   int dest_width,
+			   int dest_height)
+{
+	int source_width, source_height;
+	int s_x1, s_y1, s_x2, s_y2;
+	int s_xfrac, s_yfrac;
+	int dx, dx_frac, dy, dy_frac;
+	div_t ddx, ddy;
+	int x, y;
+	int r, g, b, a;
+	int n_pixels;
+	gboolean has_alpha;
+	guchar *dest, *src, *xsrc, *src_pixels;
+	GdkPixbuf *dest_pixbuf;
+	int pixel_stride;
+	int source_rowstride, dest_rowstride;
+
+	if (dest_width == 0 || dest_height == 0) {
+		return NULL;
+	}
+	
+	source_width = gdk_pixbuf_get_width (pixbuf);
+	source_height = gdk_pixbuf_get_height (pixbuf);
+
+	g_assert (source_width >= dest_width);
+	g_assert (source_height >= dest_height);
+
+	ddx = div (source_width, dest_width);
+	dx = ddx.quot;
+	dx_frac = ddx.rem;
+	
+	ddy = div (source_height, dest_height);
+	dy = ddy.quot;
+	dy_frac = ddy.rem;
+
+	has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+	source_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	src_pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+	dest_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8,
+				      dest_width, dest_height);
+	dest = gdk_pixbuf_get_pixels (dest_pixbuf);
+	dest_rowstride = gdk_pixbuf_get_rowstride (dest_pixbuf);
+
+	pixel_stride = (has_alpha)?4:3;
+	
+	s_y1 = 0;
+	s_yfrac = -dest_height/2;
+	while (s_y1 < source_height) {
+		s_y2 = s_y1 + dy;
+		s_yfrac += dy_frac;
+		if (s_yfrac > 0) {
+			s_y2++;
+			s_yfrac -= dest_height;
+		}
+
+		s_x1 = 0;
+		s_xfrac = -dest_width/2;
+		while (s_x1 < source_width) {
+			s_x2 = s_x1 + dx;
+			s_xfrac += dx_frac;
+			if (s_xfrac > 0) {
+				s_x2++;
+				s_xfrac -= dest_width;
+			}
+
+			/* Average block of [x1,x2[ x [y1,y2[ and store in dest */
+			r = g = b = a = 0;
+			n_pixels = 0;
+
+			src = src_pixels + s_y1 * source_rowstride + s_x1 * pixel_stride;
+			for (y = s_y1; y < s_y2; y++) {
+				xsrc = src;
+				if (has_alpha) {
+					for (x = 0; x < s_x2-s_x1; x++) {
+						n_pixels++;
+						
+						r += xsrc[3] * xsrc[0];
+						g += xsrc[3] * xsrc[1];
+						b += xsrc[3] * xsrc[2];
+						a += xsrc[3];
+						xsrc += 4;
+					}
+				} else {
+					for (x = 0; x < s_x2-s_x1; x++) {
+						n_pixels++;
+						r += *xsrc++;
+						g += *xsrc++;
+						b += *xsrc++;
+					}
+				}
+				src += source_rowstride;
+			}
+			
+			if (has_alpha) {
+				if (a != 0) {
+					*dest++ = r / a;
+					*dest++ = g / a;
+					*dest++ = b / a;
+					*dest++ = a / n_pixels;
+				} else {
+					*dest++ = 0;
+					*dest++ = 0;
+					*dest++ = 0;
+					*dest++ = 0;
+				}
+			} else {
+				*dest++ = r / n_pixels;
+				*dest++ = g / n_pixels;
+				*dest++ = b / n_pixels;
+			}
+			
+			s_x1 = s_x2;
+		}
+		s_y1 = s_y2;
+		dest += dest_rowstride - dest_width * pixel_stride;
+	}
+	
+	return dest_pixbuf;
+}
+
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+static char *
+check_average_value (int width, int height, const char* fill)
+{
+	char c;
+	guint r, g, b, a;
+	gboolean alpha, gray;
+	int gray_tweak;
+	GdkPixbuf *pixbuf;
+	int x, y, rowstride, n_channels;
+	guchar *pixels;
+	guint32 average;
+	guchar v;
+
+	r = g = b = a = 0;
+	alpha = FALSE;
+	gray = FALSE;
+	gray_tweak = 0;
+	if (sscanf (fill, " %x,%x,%x,%x %c", &r, &g, &b, &a, &c) == 4) {
+		alpha = TRUE;
+	} else if (sscanf (fill, " %x,%x,%x %c", &r, &g, &b, &c) == 3) {
+	} else if (sscanf (fill, " gray%d %c", &gray_tweak, &c) == 1) {
+		gray = TRUE;
+	} else {
+		return g_strdup ("bad fill string format");
+	}
+
+	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, alpha, 8, width, height);
+
+	pixels = gdk_pixbuf_get_pixels (pixbuf);
+	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+	if (!gray) {
+		for (y = 0; y < height; y++) {
+			for (x = 0; x < width; x++) {
+				pixels [y * rowstride + x * n_channels + 0] = r;
+				pixels [y * rowstride + x * n_channels + 1] = g;
+				pixels [y * rowstride + x * n_channels + 2] = b;
+				if (alpha) {
+					pixels [y * rowstride + x * n_channels + 3] = a;
+				}
+			}
+		}
+	} else {
+		for (y = 0; y < height; y++) {
+			for (x = 0; x < width; x++) {
+				v = ((x + y) & 1) ? 0x80 : 0x7F;
+				if (((x + y) & 0xFF) == 0)
+					v += gray_tweak;
+				pixels [y * rowstride + x * n_channels + 0] = v;
+				pixels [y * rowstride + x * n_channels + 1] = v;
+				pixels [y * rowstride + x * n_channels + 2] = v;
+			}
+		}
+		pixels [0] += gray_tweak;
+		pixels [1] += gray_tweak;
+		pixels [2] += gray_tweak;
+	}
+
+	average = eel_gdk_pixbuf_average_value (pixbuf);
+	g_object_unref (pixbuf);
+
+	return g_strdup_printf ("%02X,%02X,%02X,%02X",
+				(average >> 16) & 0xFF,
+				(average >> 8) & 0xFF,
+				average & 0xFF,
+				average >> 24);
+}
+
+void
+eel_self_check_gdk_pixbuf_extensions (void)
+{
+	GdkPixbuf *pixbuf;
+	EelIRect clip_area;
+
+	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 100, 100);
+
+	EEL_CHECK_BOOLEAN_RESULT (eel_gdk_pixbuf_is_valid (pixbuf), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_gdk_pixbuf_is_valid (NULL), FALSE);
+
+	EEL_CHECK_DIMENSIONS_RESULT (eel_gdk_pixbuf_get_dimensions (pixbuf), 100, 100);
+
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, eel_gdk_pixbuf_whole_pixbuf), 0, 0, 100, 100);
+
+	clip_area = eel_irect_assign (0, 0, 0, 0);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 0, 0);
+
+	clip_area = eel_irect_assign (0, 0, 0, 0);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 0, 0);
+
+	clip_area = eel_irect_assign (0, 0, 100, 100);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 100, 100);
+
+	clip_area = eel_irect_assign (-10, -10, 100, 100);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 90, 90);
+
+	clip_area = eel_irect_assign (-10, -10, 110, 110);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 100, 100);
+
+	clip_area = eel_irect_assign (0, 0, 99, 99);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 99, 99);
+
+	clip_area = eel_irect_assign (0, 0, 1, 1);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 1, 1);
+
+	clip_area = eel_irect_assign (-1, -1, 1, 1);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 0, 0);
+
+	clip_area = eel_irect_assign (-1, -1, 2, 2);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 1, 1);
+
+	clip_area = eel_irect_assign (100, 100, 1, 1);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 0, 0);
+
+	clip_area = eel_irect_assign (101, 101, 1, 1);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 0, 0, 0, 0);
+
+	clip_area = eel_irect_assign (80, 0, 100, 100);
+	EEL_CHECK_RECTANGLE_RESULT (eel_gdk_pixbuf_intersect (pixbuf, 0, 0, clip_area), 80, 0, 100, 100);
+
+	g_object_unref (pixbuf);
+
+	/* No checks for empty pixbufs because GdkPixbuf doesn't seem to allow them. */
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "00,00,00"), "00,00,00,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "00,00,00,00"), "00,00,00,00");
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "00,00,00,FF"), "00,00,00,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "01,01,01"), "01,01,01,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "FE,FE,FE"), "FE,FE,FE,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "FF,FF,FF"), "FF,FF,FF,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "FF,FF,FF,00"), "00,00,00,00");
+	EEL_CHECK_STRING_RESULT (check_average_value (1, 1, "11,22,33"), "11,22,33,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "00,00,00"), "00,00,00,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "00,00,00,00"), "00,00,00,00");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "00,00,00,FF"), "00,00,00,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "01,01,01"), "01,01,01,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "FE,FE,FE"), "FE,FE,FE,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "FF,FF,FF"), "FF,FF,FF,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "FF,FF,FF,00"), "00,00,00,00");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "11,22,33"), "11,22,33,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "gray -1"), "7F,7F,7F,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "gray 0"), "80,80,80,FF");
+	EEL_CHECK_STRING_RESULT (check_average_value (1000, 1000, "gray 1"), "80,80,80,FF");
+}
+
+#endif /* !EEL_OMIT_SELF_CHECK */
+

Added: trunk/eel/eel-gdk-pixbuf-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gdk-pixbuf-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,155 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gdk-pixbuf-extensions.h: Routines to augment what's in gdk-pixbuf.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_GDK_PIXBUF_EXTENSIONS_H
+#define EEL_GDK_PIXBUF_EXTENSIONS_H
+
+#include <eel/eel-art-extensions.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk/gdk.h>
+#include <gio/gio.h>
+
+#define EEL_STANDARD_ALPHA_THRESHHOLD 128
+#define EEL_OPACITY_FULLY_TRANSPARENT 0
+#define EEL_OPACITY_FULLY_OPAQUE      255
+
+extern const EelIRect eel_gdk_pixbuf_whole_pixbuf;
+
+typedef struct EelPixbufLoadHandle EelPixbufLoadHandle;
+typedef void (* EelPixbufLoadCallback) (GError         *error,
+					GdkPixbuf      *pixbuf,
+					gpointer        callback_data);
+
+/* Convenience functions for lists of GdkPixbuf objects. */
+void                 eel_gdk_pixbuf_list_ref                  (GList                 *pixbuf_list);
+void                 eel_gdk_pixbuf_list_unref                (GList                 *pixbuf_list);
+void                 eel_gdk_pixbuf_list_free                 (GList                 *pixbuf_list);
+
+
+/* Loading a GdkPixbuf with a URI. */
+GdkPixbuf *          eel_gdk_pixbuf_load                      (const char            *uri);
+GdkPixbuf *          eel_gdk_pixbuf_load_from_stream          (GInputStream          *stream);
+GdkPixbuf *          eel_gdk_pixbuf_load_from_stream_at_size  (GInputStream          *stream,
+							       int                    size);
+
+
+/* Same thing async. */
+EelPixbufLoadHandle *eel_gdk_pixbuf_load_async                (const char            *uri,
+							       int                    priority,
+							       EelPixbufLoadCallback  callback,
+							       gpointer               callback_data);
+void                 eel_cancel_gdk_pixbuf_load               (EelPixbufLoadHandle   *handle);
+GdkPixbuf *          eel_gdk_pixbuf_scale_down_to_fit         (GdkPixbuf             *pixbuf,
+							       int                    max_width,
+							       int                    max_height);
+GdkPixbuf *          eel_gdk_pixbuf_scale_to_fit              (GdkPixbuf             *pixbuf,
+							       int                    max_width,
+							       int                    max_height);
+double               eel_gdk_scale_to_fit_factor              (int                    width,
+							       int                    height,
+							       int                    max_width,
+							       int                    max_height,
+							       int                   *scaled_width,
+							       int                   *scaled_height);
+GdkPixbuf *          eel_gdk_pixbuf_scale_to_min              (GdkPixbuf             *pixbuf,
+							       int                    min_width,
+							       int                    min_height);
+double              eel_gdk_scale_to_min_factor               (int                   width,
+                                                               int                   height,
+							       int                   min_width,
+							       int                   min_height,
+							       int                   *scaled_width,
+							       int                   *scaled_height);
+
+/* return average color values for each component (argb) */
+guint32              eel_gdk_pixbuf_average_value             (GdkPixbuf             *pixbuf);
+void                 eel_gdk_pixbuf_fill_rectangle_with_color (GdkPixbuf             *pixbuf,
+							       EelIRect               area,
+							       guint32                color);
+
+
+/* Save a pixbuf to a png file.  Return value indicates succss/TRUE or failure/FALSE */
+gboolean             eel_gdk_pixbuf_save_to_file              (const GdkPixbuf       *pixbuf,
+							       const char            *file_name);
+void                 eel_gdk_pixbuf_ref_if_not_null           (GdkPixbuf             *pixbuf_or_null);
+void                 eel_gdk_pixbuf_unref_if_not_null         (GdkPixbuf             *pixbuf_or_null);
+
+
+/* Copy a pixbuf to an area of a GdkDrawable */
+void                 eel_gdk_pixbuf_draw_to_drawable          (const GdkPixbuf       *pixbuf,
+							       GdkDrawable           *drawable,
+							       GdkGC                 *gc,
+							       int                    source_x,
+							       int                    source_y,
+							       EelIRect               destination_area,
+							       GdkRgbDither           dither,
+							       GdkPixbufAlphaMode     alpha_compositing_mode,
+							       int                    alpha_threshold);
+
+/* Copy a pixbuf to an area of another pixbuf */
+void                 eel_gdk_pixbuf_draw_to_pixbuf            (const GdkPixbuf       *pixbuf,
+							       GdkPixbuf             *destination_pixbuf,
+							       int                    source_x,
+							       int                    source_y,
+							       EelIRect               destination_area);
+
+
+/* Composite one pixbuf over another with the given opacity */
+void                 eel_gdk_pixbuf_draw_to_pixbuf_alpha      (const GdkPixbuf       *pixbuf,
+							       GdkPixbuf             *destination_pixbuf,
+							       int                    source_x,
+							       int                    source_y,
+							       EelIRect               destination_area,
+							       int                    opacity,
+							       GdkInterpType          interpolation_mode);
+
+
+/* Create a pixbuf from a sub area of another pixbuf */
+GdkPixbuf *          eel_gdk_pixbuf_new_from_pixbuf_sub_area  (GdkPixbuf             *pixbuf,
+							       EelIRect               area);
+/* Create a pixbuf from an existing buffer. */
+GdkPixbuf *          eel_gdk_pixbuf_new_from_existing_buffer  (guchar                *buffer,
+							       int                    buffer_rowstride,
+							       gboolean               buffer_has_alpha,
+							       EelIRect               area);
+
+/* Determine whether a pixbuf is valid or not */
+gboolean             eel_gdk_pixbuf_is_valid                  (const GdkPixbuf       *pixbuf);
+
+/* Access the dimensions of a pixbuf. */
+EelDimensions        eel_gdk_pixbuf_get_dimensions            (const GdkPixbuf       *pixbuf);
+
+/* Return the intersection of the pixbuf with the given rectangle. */
+EelIRect             eel_gdk_pixbuf_intersect                 (const GdkPixbuf       *pixbuf,
+							       int                    pixbuf_x,
+							       int                    pixbuf_y,
+							       EelIRect               rectangle);
+
+/* Scales large pixbufs down fast */
+GdkPixbuf *          eel_gdk_pixbuf_scale_down                (GdkPixbuf *pixbuf,
+							       int dest_width,
+							       int dest_height);
+
+#endif /* EEL_GDK_PIXBUF_EXTENSIONS_H */

Added: trunk/eel/eel-glib-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-glib-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,1159 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-glib-extensions.c - implementation of new functions that conceptually
+                                belong in glib. Perhaps some of these will be
+                                actually rolled into glib someday.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: John Sullivan <sullivan eazel com>
+*/
+
+#include <config.h>
+#include "eel-glib-extensions.h"
+
+#include "eel-debug.h"
+#include "eel-lib-self-check-functions.h"
+#include "eel-string.h"
+#include "eel-i18n.h"
+#include <glib-object.h>
+#include <math.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <locale.h>
+
+/* Legal conversion specifiers, as specified in the C standard. */
+#define C_STANDARD_STRFTIME_CHARACTERS "aAbBcdHIjmMpSUwWxXyYZ"
+#define C_STANDARD_NUMERIC_STRFTIME_CHARACTERS "dHIjmMSUwWyY"
+#define SUS_EXTENDED_STRFTIME_MODIFIERS "EO"
+
+#define SAFE_SHELL_CHARACTERS "-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+typedef struct {
+	GHashTable *hash_table;
+	char *display_name;
+	gboolean keys_known_to_be_strings;
+} HashTableToFree;
+
+static GList *hash_tables_to_free_at_exit;
+
+/**
+ * eel_g_date_new_tm:
+ * 
+ * Get a new GDate * for the date represented by a tm struct. 
+ * The caller is responsible for g_free-ing the result.
+ * @time_pieces: Pointer to a tm struct representing the date to be converted.
+ * 
+ * Returns: Newly allocated date.
+ * 
+ **/
+GDate *
+eel_g_date_new_tm (struct tm *time_pieces)
+{
+	/* tm uses 0-based months; GDate uses 1-based months.
+	 * tm_year needs 1900 added to get the full year.
+	 */
+	return g_date_new_dmy (time_pieces->tm_mday,
+			       time_pieces->tm_mon + 1,
+			       time_pieces->tm_year + 1900);
+}
+
+/**
+ * eel_strdup_strftime:
+ *
+ * Cover for standard date-and-time-formatting routine strftime that returns
+ * a newly-allocated string of the correct size. The caller is responsible
+ * for g_free-ing the returned string.
+ *
+ * Besides the buffer management, there are two differences between this
+ * and the library strftime:
+ *
+ *   1) The modifiers "-" and "_" between a "%" and a numeric directive
+ *      are defined as for the GNU version of strftime. "-" means "do not
+ *      pad the field" and "_" means "pad with spaces instead of zeroes".
+ *   2) Non-ANSI extensions to strftime are flagged at runtime with a
+ *      warning, so it's easy to notice use of the extensions without
+ *      testing with multiple versions of the library.
+ *
+ * @format: format string to pass to strftime. See strftime documentation
+ * for details.
+ * @time_pieces: date/time, in struct format.
+ * 
+ * Return value: Newly allocated string containing the formatted time.
+ **/
+char *
+eel_strdup_strftime (const char *format, struct tm *time_pieces)
+{
+	GString *string;
+	const char *remainder, *percent;
+	char code[4], buffer[512];
+	char *piece, *result, *converted;
+	size_t string_length;
+	gboolean strip_leading_zeros, turn_leading_zeros_to_spaces;
+	char modifier;
+	int i;
+
+	/* Format could be translated, and contain UTF-8 chars,
+	 * so convert to locale encoding which strftime uses */
+	converted = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
+	g_return_val_if_fail (converted != NULL, NULL);
+	
+	string = g_string_new ("");
+	remainder = converted;
+
+	/* Walk from % character to % character. */
+	for (;;) {
+		percent = strchr (remainder, '%');
+		if (percent == NULL) {
+			g_string_append (string, remainder);
+			break;
+		}
+		g_string_append_len (string, remainder,
+				     percent - remainder);
+
+		/* Handle the "%" character. */
+		remainder = percent + 1;
+		switch (*remainder) {
+		case '-':
+			strip_leading_zeros = TRUE;
+			turn_leading_zeros_to_spaces = FALSE;
+			remainder++;
+			break;
+		case '_':
+			strip_leading_zeros = FALSE;
+			turn_leading_zeros_to_spaces = TRUE;
+			remainder++;
+			break;
+		case '%':
+			g_string_append_c (string, '%');
+			remainder++;
+			continue;
+		case '\0':
+			g_warning ("Trailing %% passed to eel_strdup_strftime");
+			g_string_append_c (string, '%');
+			continue;
+		default:
+			strip_leading_zeros = FALSE;
+			turn_leading_zeros_to_spaces = FALSE;
+			break;
+		}
+
+		modifier = 0;
+		if (strchr (SUS_EXTENDED_STRFTIME_MODIFIERS, *remainder) != NULL) {
+			modifier = *remainder;
+			remainder++;
+
+			if (*remainder == 0) {
+				g_warning ("Unfinished %%%c modifier passed to eel_strdup_strftime", modifier);
+				break;
+			}
+		} 
+		
+		if (strchr (C_STANDARD_STRFTIME_CHARACTERS, *remainder) == NULL) {
+			g_warning ("eel_strdup_strftime does not support "
+				   "non-standard escape code %%%c",
+				   *remainder);
+		}
+
+		/* Convert code to strftime format. We have a fixed
+		 * limit here that each code can expand to a maximum
+		 * of 512 bytes, which is probably OK. There's no
+		 * limit on the total size of the result string.
+		 */
+		i = 0;
+		code[i++] = '%';
+		if (modifier != 0) {
+#ifdef HAVE_STRFTIME_EXTENSION
+			code[i++] = modifier;
+#endif
+		}
+		code[i++] = *remainder;
+		code[i++] = '\0';
+		string_length = strftime (buffer, sizeof (buffer),
+					  code, time_pieces);
+		if (string_length == 0) {
+			/* We could put a warning here, but there's no
+			 * way to tell a successful conversion to
+			 * empty string from a failure.
+			 */
+			buffer[0] = '\0';
+		}
+
+		/* Strip leading zeros if requested. */
+		piece = buffer;
+		if (strip_leading_zeros || turn_leading_zeros_to_spaces) {
+			if (strchr (C_STANDARD_NUMERIC_STRFTIME_CHARACTERS, *remainder) == NULL) {
+				g_warning ("eel_strdup_strftime does not support "
+					   "modifier for non-numeric escape code %%%c%c",
+					   remainder[-1],
+					   *remainder);
+			}
+			if (*piece == '0') {
+				do {
+					piece++;
+				} while (*piece == '0');
+				if (!g_ascii_isdigit (*piece)) {
+				    piece--;
+				}
+			}
+			if (turn_leading_zeros_to_spaces) {
+				memset (buffer, ' ', piece - buffer);
+				piece = buffer;
+			}
+		}
+		remainder++;
+
+		/* Add this piece. */
+		g_string_append (string, piece);
+	}
+	
+	/* Convert the string back into utf-8. */
+	result = g_locale_to_utf8 (string->str, -1, NULL, NULL, NULL);
+
+	g_string_free (string, TRUE);
+	g_free (converted);
+
+	return result;
+}
+
+/**
+ * eel_g_list_exactly_one_item
+ *
+ * Like g_list_length (list) == 1, only O(1) instead of O(n).
+ * @list: List.
+ *
+ * Return value: TRUE if the list has exactly one item.
+ **/
+gboolean
+eel_g_list_exactly_one_item (GList *list)
+{
+	return list != NULL && list->next == NULL;
+}
+
+/**
+ * eel_g_list_more_than_one_item
+ *
+ * Like g_list_length (list) > 1, only O(1) instead of O(n).
+ * @list: List.
+ *
+ * Return value: TRUE if the list has more than one item.
+ **/
+gboolean
+eel_g_list_more_than_one_item (GList *list)
+{
+	return list != NULL && list->next != NULL;
+}
+
+/**
+ * eel_g_list_equal
+ *
+ * Compares two lists to see if they are equal.
+ * @list_a: First list.
+ * @list_b: Second list.
+ *
+ * Return value: TRUE if the lists are the same length with the same elements.
+ **/
+gboolean
+eel_g_list_equal (GList *list_a, GList *list_b)
+{
+	GList *p, *q;
+
+	for (p = list_a, q = list_b; p != NULL && q != NULL; p = p->next, q = q->next) {
+		if (p->data != q->data) {
+			return FALSE;
+		}
+	}
+	return p == NULL && q == NULL;
+}
+
+/**
+ * eel_g_str_list_equal
+ *
+ * Compares two lists of C strings to see if they are equal.
+ * @list_a: First list.
+ * @list_b: Second list.
+ *
+ * Return value: TRUE if the lists contain the same strings.
+ **/
+gboolean
+eel_g_str_list_equal (GList *list_a, GList *list_b)
+{
+	GList *p, *q;
+
+	for (p = list_a, q = list_b; p != NULL && q != NULL; p = p->next, q = q->next) {
+		if (eel_strcmp (p->data, q->data) != 0) {
+			return FALSE;
+		}
+	}
+	return p == NULL && q == NULL;
+}
+
+/**
+ * eel_g_str_list_copy
+ *
+ * @list: List of strings and/or NULLs to copy.
+ * Return value: Deep copy of @list.
+ **/
+GList *
+eel_g_str_list_copy (GList *list)
+{
+	GList *node, *result;
+
+	result = NULL;
+	
+	for (node = g_list_last (list); node != NULL; node = node->prev) {
+		result = g_list_prepend (result, g_strdup (node->data));
+	}
+	return result;
+}
+
+/**
+ * eel_g_str_list_alphabetize
+ *
+ * Sort a list of strings using locale-sensitive rules.
+ *
+ * @list: List of strings and/or NULLs.
+ * 
+ * Return value: @list, sorted.
+ **/
+GList *
+eel_g_str_list_alphabetize (GList *list)
+{
+	return g_list_sort (list, (GCompareFunc) g_utf8_collate);
+}
+
+int
+eel_g_str_list_index (GList *str_list,
+		      const char *str)
+{
+	int i;
+	GList *l;
+	for (i = 0, l = str_list; l != NULL; l = l->next, i++) {
+		if (!strcmp (str, (const char*)l->data)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+/**
+ * eel_g_list_free_deep_custom
+ *
+ * Frees the elements of a list and then the list, using a custom free function.
+ *
+ * @list: List of elements that can be freed with the provided free function.
+ * @element_free_func: function to call with the data pointer and user_data to free it.
+ * @user_data: User data to pass to element_free_func
+ **/
+void
+eel_g_list_free_deep_custom (GList *list, GFunc element_free_func, gpointer user_data)
+{
+	g_list_foreach (list, element_free_func, user_data);
+	g_list_free (list);
+}
+
+/**
+ * eel_g_list_free_deep
+ *
+ * Frees the elements of a list and then the list.
+ * @list: List of elements that can be freed with g_free.
+ **/
+void
+eel_g_list_free_deep (GList *list)
+{
+	eel_g_list_free_deep_custom (list, (GFunc) g_free, NULL);
+}
+
+/**
+ * eel_g_list_free_deep_custom
+ *
+ * Frees the elements of a list and then the list, using a custom free function.
+ *
+ * @list: List of elements that can be freed with the provided free function.
+ * @element_free_func: function to call with the data pointer and user_data to free it.
+ * @user_data: User data to pass to element_free_func
+ **/
+void
+eel_g_slist_free_deep_custom (GSList *list, GFunc element_free_func, gpointer user_data)
+{
+	g_slist_foreach (list, element_free_func, user_data);
+	g_slist_free (list);
+}
+
+/**
+ * eel_g_slist_free_deep
+ *
+ * Frees the elements of a list and then the list.
+ * @list: List of elements that can be freed with g_free.
+ **/
+void
+eel_g_slist_free_deep (GSList *list)
+{
+	eel_g_slist_free_deep_custom (list, (GFunc) g_free, NULL);
+}
+
+
+/**
+ * eel_g_strv_find
+ * 
+ * Get index of string in array of strings.
+ * 
+ * @strv: NULL-terminated array of strings.
+ * @find_me: string to search for.
+ * 
+ * Return value: index of array entry in @strv that
+ * matches @find_me, or -1 if no matching entry.
+ */
+int
+eel_g_strv_find (char **strv, const char *find_me)
+{
+	int index;
+
+	g_return_val_if_fail (find_me != NULL, -1);
+	
+	for (index = 0; strv[index] != NULL; ++index) {
+		if (strcmp (strv[index], find_me) == 0) {
+			return index;
+		}
+	}
+
+	return -1;
+}
+
+static int
+compare_pointers (gconstpointer pointer_1, gconstpointer pointer_2)
+{
+	if ((const char *) pointer_1 < (const char *) pointer_2) {
+		return -1;
+	}
+	if ((const char *) pointer_1 > (const char *) pointer_2) {
+		return +1;
+	}
+	return 0;
+}
+
+gboolean
+eel_g_lists_sort_and_check_for_intersection (GList **list_1,
+					     GList **list_2) 
+
+{
+	GList *node_1, *node_2;
+	int compare_result;
+	
+	*list_1 = g_list_sort (*list_1, compare_pointers);
+	*list_2 = g_list_sort (*list_2, compare_pointers);
+
+	node_1 = *list_1;
+	node_2 = *list_2;
+
+	while (node_1 != NULL && node_2 != NULL) {
+		compare_result = compare_pointers (node_1->data, node_2->data);
+		if (compare_result == 0) {
+			return TRUE;
+		}
+		if (compare_result <= 0) {
+			node_1 = node_1->next;
+		}
+		if (compare_result >= 0) {
+			node_2 = node_2->next;
+		}
+	}
+
+	return FALSE;
+}
+
+
+/**
+ * eel_g_list_partition
+ * 
+ * Parition a list into two parts depending on whether the data
+ * elements satisfy a provided predicate. Order is preserved in both
+ * of the resulting lists, and the original list is consumed. A list
+ * of the items that satisfy the predicate is returned, and the list
+ * of items not satisfying the predicate is returned via the failed
+ * out argument.
+ * 
+ * @list: List to partition.
+ * @predicate: Function to call on each element.
+ * @user_data: Data to pass to function.  
+ * @failed: The GList * variable pointed to by this argument will be
+ * set to the list of elements for which the predicate returned
+ * false. */
+
+GList *
+eel_g_list_partition (GList *list,
+			   EelPredicateFunction  predicate,
+			   gpointer user_data,
+			   GList **failed)
+{
+	GList *predicate_true;
+	GList *predicate_false;
+	GList *reverse;
+	GList *p;
+	GList *next;
+
+	predicate_true = NULL;
+	predicate_false = NULL;
+
+	reverse = g_list_reverse (list);
+
+	for (p = reverse; p != NULL; p = next) {
+		next = p->next;
+		
+		if (next != NULL) {
+			next->prev = NULL;
+		}
+
+		if (predicate (p->data, user_data)) {
+			p->next = predicate_true;
+ 			if (predicate_true != NULL) {
+				predicate_true->prev = p;
+			}
+			predicate_true = p;
+		} else {
+			p->next = predicate_false;
+ 			if (predicate_false != NULL) {
+				predicate_false->prev = p;
+			}
+			predicate_false = p;
+		}
+	}
+
+	*failed = predicate_false;
+	return predicate_true;
+}
+
+/**
+ * eel_get_system_time
+ * 
+ * Return value: number of microseconds since the machine was turned on
+ */
+gint64
+eel_get_system_time (void)
+{
+	struct timeval tmp;
+
+	gettimeofday (&tmp, NULL);
+	return (gint64)tmp.tv_usec + (gint64)tmp.tv_sec * G_GINT64_CONSTANT (1000000);
+}
+
+static void
+print_key_string (gpointer key, gpointer value, gpointer callback_data)
+{
+	g_assert (callback_data == NULL);
+
+	g_print ("--> %s\n", (char *) key);
+}
+
+static void
+free_hash_tables_at_exit (void)
+{
+	GList *p;
+	HashTableToFree *hash_table_to_free;
+	guint size;
+
+	for (p = hash_tables_to_free_at_exit; p != NULL; p = p->next) {
+		hash_table_to_free = p->data;
+
+		size = g_hash_table_size (hash_table_to_free->hash_table);
+		if (size != 0) {
+			if (hash_table_to_free->keys_known_to_be_strings) {
+				g_print ("\n--- Hash table keys for warning below:\n");
+				g_hash_table_foreach (hash_table_to_free->hash_table,
+						      print_key_string,
+						      NULL);
+			}
+			g_warning ("\"%s\" hash table still has %u element%s at quit time%s",
+				   hash_table_to_free->display_name, size,
+				   size == 1 ? "" : "s",
+				   hash_table_to_free->keys_known_to_be_strings
+				   ? " (keys above)" : "");
+		}
+
+		g_hash_table_destroy (hash_table_to_free->hash_table);
+		g_free (hash_table_to_free->display_name);
+		g_free (hash_table_to_free);
+	}
+	g_list_free (hash_tables_to_free_at_exit);
+	hash_tables_to_free_at_exit = NULL;
+}
+
+GHashTable *
+eel_g_hash_table_new_free_at_exit (GHashFunc hash_func,
+				   GCompareFunc key_compare_func,
+				   const char *display_name)
+{
+	GHashTable *hash_table;
+	HashTableToFree *hash_table_to_free;
+
+	/* FIXME: We can take out the NAUTILUS_DEBUG check once we
+	 * have fixed more of the leaks. For now, it's a bit too noisy
+	 * for the general public.
+	 */
+	if (hash_tables_to_free_at_exit == NULL) {
+		eel_debug_call_at_shutdown (free_hash_tables_at_exit);
+	}
+
+	hash_table = g_hash_table_new (hash_func, key_compare_func);
+
+	hash_table_to_free = g_new (HashTableToFree, 1);
+	hash_table_to_free->hash_table = hash_table;
+	hash_table_to_free->display_name = g_strdup (display_name);
+	hash_table_to_free->keys_known_to_be_strings =
+		hash_func == g_str_hash;
+
+	hash_tables_to_free_at_exit = g_list_prepend
+		(hash_tables_to_free_at_exit, hash_table_to_free);
+
+	return hash_table;
+}
+
+typedef struct {
+	GList *keys;
+	GList *values;
+} FlattenedHashTable;
+
+static void
+flatten_hash_table_element (gpointer key, gpointer value, gpointer callback_data)
+{
+	FlattenedHashTable *flattened_table;
+
+	flattened_table = callback_data;
+	flattened_table->keys = g_list_prepend
+		(flattened_table->keys, key);
+	flattened_table->values = g_list_prepend
+		(flattened_table->values, value);
+}
+
+void
+eel_g_hash_table_safe_for_each (GHashTable *hash_table,
+				GHFunc callback,
+				gpointer callback_data)
+{
+	FlattenedHashTable flattened;
+	GList *p, *q;
+
+	flattened.keys = NULL;
+	flattened.values = NULL;
+
+	g_hash_table_foreach (hash_table,
+			      flatten_hash_table_element,
+			      &flattened);
+
+	for (p = flattened.keys, q = flattened.values;
+	     p != NULL;
+	     p = p->next, q = q->next) {
+		(* callback) (p->data, q->data, callback_data);
+	}
+
+	g_list_free (flattened.keys);
+	g_list_free (flattened.values);
+}
+
+int
+eel_round (double d)
+{
+	double val;
+
+	val = floor (d + .5);
+
+	/* The tests are needed because the result of floating-point to integral
+	 * conversion is undefined if the floating point value is not representable
+	 * in the new type. E.g. the magnititude is too large or a negative
+	 * floating-point value being converted to an unsigned.
+	 */
+	g_return_val_if_fail (val <= INT_MAX, INT_MAX);
+	g_return_val_if_fail (val >= INT_MIN, INT_MIN);
+
+	return val;
+}
+
+GList *
+eel_g_list_from_g_slist (GSList *slist)
+{
+	GList *list;
+	GSList *node;
+
+	list = NULL;
+	for (node = slist; node != NULL; node = node->next) {
+		list = g_list_prepend (list, node->data);
+	}
+	return g_list_reverse (list);
+}
+
+GSList *
+eel_g_slist_from_g_list (GList *list)
+{
+	GSList *slist;
+	GList *node;
+
+	slist = NULL;
+	for (node = list; node != NULL; node = node->next) {
+		slist = g_slist_prepend (slist, node->data);
+	}
+	return g_slist_reverse (slist);
+}
+
+/* Return the operating system name: Linux, Solaris, etc. */
+char *
+eel_get_operating_system_name (void)
+{
+	struct utsname buffer;
+
+	if (uname (&buffer) != -1) {
+		/* Check for special sysnames for which there is 
+		 * more accepted names.
+		 */
+		if (eel_str_is_equal (buffer.sysname, "SunOS")) {
+			return g_strdup ("Solaris");
+		}
+
+		return g_strdup (buffer.sysname);
+	}
+
+	return g_strdup ("Unix");
+}
+
+int
+eel_compare_integer (gconstpointer a,
+			  gconstpointer b)
+{
+	int int_a;
+	int int_b;
+
+	int_a = GPOINTER_TO_INT (a);
+	int_b = GPOINTER_TO_INT (b);
+
+	if (int_a == int_b) {
+		return 0;
+	}
+
+	return int_a < int_b ? -1 : 1;
+}
+
+/**
+ * eel_g_object_list_ref
+ *
+ * Ref all the objects in a list.
+ * @list: GList of objects.
+ **/
+GList *
+eel_g_object_list_ref (GList *list)
+{
+	g_list_foreach (list, (GFunc) g_object_ref, NULL);
+	return list;
+}
+
+/**
+ * eel_g_object_list_unref
+ *
+ * Unref all the objects in a list.
+ * @list: GList of objects.
+ **/
+void
+eel_g_object_list_unref (GList *list)
+{
+	g_list_foreach (list, (GFunc) g_object_unref, NULL);
+}
+
+/**
+ * eel_g_object_list_free
+ *
+ * Free a list of objects after unrefing them.
+ * @list: GList of objects.
+ **/
+void
+eel_g_object_list_free (GList *list)
+{
+	eel_g_object_list_unref (list);
+	g_list_free (list);
+}
+
+/**
+ * eel_g_object_list_copy
+ *
+ * Copy the list of objects, ref'ing each one.
+ * @list: GList of objects.
+ **/
+GList *
+eel_g_object_list_copy (GList *list)
+{
+	return g_list_copy (eel_g_object_list_ref (list));
+}
+
+/**
+ * eel_add_weak_pointer
+ *
+ * Nulls out a saved reference to an object when the object gets destroyed.
+ *
+ * @pointer_location: Address of the saved pointer.
+ **/
+void 
+eel_add_weak_pointer (gpointer pointer_location)
+{
+	gpointer *object_location;
+
+	g_return_if_fail (pointer_location != NULL);
+
+	object_location = (gpointer *) pointer_location;
+	if (*object_location == NULL) {
+		/* The reference is NULL, nothing to do. */
+		return;
+	}
+
+	g_return_if_fail (G_IS_OBJECT (*object_location));
+
+	g_object_add_weak_pointer (G_OBJECT (*object_location),
+				   object_location);
+}
+
+/**
+ * eel_remove_weak_pointer
+ *
+ * Removes the weak pointer that was added by eel_add_weak_pointer.
+ * Also nulls out the pointer.
+ *
+ * @pointer_location: Pointer that was passed to eel_add_weak_pointer.
+ **/
+void 
+eel_remove_weak_pointer (gpointer pointer_location)
+{
+	gpointer *object_location;
+
+	g_return_if_fail (pointer_location != NULL);
+
+	object_location = (gpointer *) pointer_location;	
+	if (*object_location == NULL) {
+		/* The object was already destroyed and the reference
+		 * nulled out, nothing to do.
+		 */
+		return;
+	}
+
+	g_return_if_fail (G_IS_OBJECT (*object_location));
+
+	g_object_remove_weak_pointer (G_OBJECT (*object_location),
+				      object_location);
+	
+	*object_location = NULL;
+}
+
+/* Get the filename encoding, returns TRUE if utf8 */
+
+typedef struct _EelFilenameCharsetCache EelFilenameCharsetCache;
+
+struct _EelFilenameCharsetCache {
+  gboolean is_utf8;
+  gchar *charset;
+  gchar *filename_charset;
+};
+
+static void
+filename_charset_cache_free (gpointer data)
+{
+  EelFilenameCharsetCache *cache = data;
+  g_free (cache->charset);
+  g_free (cache->filename_charset);
+  g_free (cache);
+}
+
+/*
+ * eel_get_filename_charset:
+ * @charset: return location for the name of the filename encoding 
+ *
+ * Determines the character set used for filenames by consulting the 
+ * environment variables G_FILENAME_ENCODING and G_BROKEN_FILENAMES. 
+ *
+ * G_FILENAME_ENCODING may be set to a comma-separated list of character 
+ * set names. The special token "@locale" is taken to mean the character set 
+ * for the current locale. The first character set from the list is taken 
+ * as the filename encoding. 
+ * If G_FILENAME_ENCODING is not set, but G_BROKEN_FILENAMES is, the
+ * character set of the current locale is taken as the filename encoding.
+ *
+ * The returned @charset belongs to Eel and must not be freed.
+ * 
+ * Return value: %TRUE if the charset used for filename is UTF-8.
+ */
+gboolean
+eel_get_filename_charset (const gchar **filename_charset)
+{
+  static GStaticPrivate cache_private = G_STATIC_PRIVATE_INIT;
+  EelFilenameCharsetCache *cache = g_static_private_get (&cache_private);
+  const gchar *charset;
+  
+  if (!cache)
+    {
+      cache = g_new0 (EelFilenameCharsetCache, 1);
+      g_static_private_set (&cache_private, cache, filename_charset_cache_free);
+    }
+
+  g_get_charset (&charset);
+
+  if (!(cache->charset && strcmp (cache->charset, charset) == 0))
+    {
+      const gchar *new_charset;
+      gchar *p, *q;
+
+      g_free (cache->charset);
+      g_free (cache->filename_charset);
+      cache->charset = g_strdup (charset);
+      
+      p = getenv ("G_FILENAME_ENCODING");
+      if (p != NULL) 
+	{
+	  q = strchr (p, ',');
+	  if (!q) 
+	    q = p + strlen (p);
+
+	  if (strncmp ("@locale", p, q - p) == 0)
+	    {
+	      cache->is_utf8 = g_get_charset (&new_charset);
+	      cache->filename_charset = g_strdup (new_charset);
+	    }
+	  else
+	    {
+	      cache->filename_charset = g_strndup (p, q - p);
+	      cache->is_utf8 = (strcmp (cache->filename_charset, "UTF-8") == 0);
+	    }
+	}
+      else if (getenv ("G_BROKEN_FILENAMES") != NULL)
+	{
+	  cache->is_utf8 = g_get_charset (&new_charset);
+	  cache->filename_charset = g_strdup (new_charset);
+	}
+      else 
+	{
+	  cache->filename_charset = g_strdup ("UTF-8");
+	  cache->is_utf8 = TRUE;
+	}
+    }
+
+  if (filename_charset)
+    *filename_charset = cache->filename_charset;
+
+  return cache->is_utf8;
+}
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+static void 
+check_tm_to_g_date (time_t time)
+{
+	struct tm *before_conversion;
+	struct tm after_conversion;
+	GDate *date;
+
+	before_conversion = localtime (&time);
+	date = eel_g_date_new_tm (before_conversion);
+
+	g_date_to_struct_tm (date, &after_conversion);
+
+	g_date_free (date);
+
+	EEL_CHECK_INTEGER_RESULT (after_conversion.tm_mday,
+				       before_conversion->tm_mday);
+	EEL_CHECK_INTEGER_RESULT (after_conversion.tm_mon,
+				       before_conversion->tm_mon);
+	EEL_CHECK_INTEGER_RESULT (after_conversion.tm_year,
+				       before_conversion->tm_year);
+}
+
+static gboolean
+eel_test_predicate (gpointer data,
+		    gpointer callback_data)
+{
+	return g_ascii_strcasecmp (data, callback_data) <= 0;
+}
+
+static char *
+test_strftime (const char *format,
+	       int year,
+	       int month,
+	       int day,
+	       int hour,
+	       int minute,
+	       int second)
+{
+	struct tm time_pieces;
+
+	time_pieces.tm_sec = second;
+	time_pieces.tm_min = minute;
+	time_pieces.tm_hour = hour;
+	time_pieces.tm_mday = day;
+	time_pieces.tm_mon = month - 1;
+	time_pieces.tm_year = year - 1900;
+	time_pieces.tm_isdst = -1;
+	mktime (&time_pieces);
+
+	return eel_strdup_strftime (format, &time_pieces);
+}
+
+void
+eel_self_check_glib_extensions (void)
+{
+	char **strv;
+	GList *compare_list_1;
+	GList *compare_list_2;
+	GList *compare_list_3;
+	GList *compare_list_4;
+	GList *compare_list_5;
+	gint64 time1, time2;
+	GList *list_to_partition;
+	GList *expected_passed;
+	GList *expected_failed;
+	GList *actual_passed;
+	GList *actual_failed;
+	char *huge_string;
+	
+	check_tm_to_g_date (0);			/* lower limit */
+	check_tm_to_g_date ((time_t) -1);	/* upper limit */
+	check_tm_to_g_date (time (NULL));	/* current time */
+
+	strv = g_strsplit ("zero|one|two|three|four", "|", 0);
+	EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "zero"), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "one"), 1);
+	EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "four"), 4);
+	EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "five"), -1);
+	EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, ""), -1);
+	EEL_CHECK_INTEGER_RESULT (eel_g_strv_find (strv, "o"), -1);
+	g_strfreev (strv);
+
+	/* eel_get_system_time */
+	time1 = eel_get_system_time ();
+	time2 = eel_get_system_time ();
+	EEL_CHECK_BOOLEAN_RESULT (time1 - time2 > -1000, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (time1 - time2 <= 0, TRUE);
+
+	/* eel_g_str_list_equal */
+
+	/* We g_strdup because identical string constants can be shared. */
+
+	compare_list_1 = NULL;
+	compare_list_1 = g_list_append (compare_list_1, g_strdup ("Apple"));
+	compare_list_1 = g_list_append (compare_list_1, g_strdup ("zebra"));
+	compare_list_1 = g_list_append (compare_list_1, g_strdup ("! #!@$# $!"));
+
+	compare_list_2 = NULL;
+	compare_list_2 = g_list_append (compare_list_2, g_strdup ("Apple"));
+	compare_list_2 = g_list_append (compare_list_2, g_strdup ("zebra"));
+	compare_list_2 = g_list_append (compare_list_2, g_strdup ("! #!@$# $!"));
+
+	compare_list_3 = NULL;
+	compare_list_3 = g_list_append (compare_list_3, g_strdup ("Apple"));
+	compare_list_3 = g_list_append (compare_list_3, g_strdup ("zebra"));
+
+	compare_list_4 = NULL;
+	compare_list_4 = g_list_append (compare_list_4, g_strdup ("Apple"));
+	compare_list_4 = g_list_append (compare_list_4, g_strdup ("zebra"));
+	compare_list_4 = g_list_append (compare_list_4, g_strdup ("! #!@$# $!"));
+	compare_list_4 = g_list_append (compare_list_4, g_strdup ("foobar"));
+
+	compare_list_5 = NULL;
+	compare_list_5 = g_list_append (compare_list_5, g_strdup ("Apple"));
+	compare_list_5 = g_list_append (compare_list_5, g_strdup ("zzzzzebraaaaaa"));
+	compare_list_5 = g_list_append (compare_list_5, g_strdup ("! #!@$# $!"));
+
+	EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_2), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_3), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_4), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (compare_list_1, compare_list_5), FALSE);
+
+	eel_g_list_free_deep (compare_list_1);
+	eel_g_list_free_deep (compare_list_2);
+	eel_g_list_free_deep (compare_list_3);
+	eel_g_list_free_deep (compare_list_4);
+	eel_g_list_free_deep (compare_list_5);
+
+	/* eel_g_list_partition */
+
+	list_to_partition = NULL;
+	list_to_partition = g_list_append (list_to_partition, "Cadillac");
+	list_to_partition = g_list_append (list_to_partition, "Pontiac");
+	list_to_partition = g_list_append (list_to_partition, "Ford");
+	list_to_partition = g_list_append (list_to_partition, "Range Rover");
+	
+	expected_passed = NULL;
+	expected_passed = g_list_append (expected_passed, "Cadillac");
+	expected_passed = g_list_append (expected_passed, "Ford");
+	
+	expected_failed = NULL;
+	expected_failed = g_list_append (expected_failed, "Pontiac");
+	expected_failed = g_list_append (expected_failed, "Range Rover");
+	
+	actual_passed = eel_g_list_partition (list_to_partition, 
+						   eel_test_predicate,
+						   "m",
+						   &actual_failed);
+	
+	EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (expected_passed, actual_passed), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_g_str_list_equal (expected_failed, actual_failed), TRUE);
+	
+	/* Don't free "list_to_partition", since it is consumed
+	 * by eel_g_list_partition.
+	 */
+	
+	g_list_free (expected_passed);
+	g_list_free (actual_passed);
+	g_list_free (expected_failed);
+	g_list_free (actual_failed);
+
+	/* eel_strdup_strftime */
+	huge_string = g_new (char, 10000+1);
+	memset (huge_string, 'a', 10000);
+	huge_string[10000] = '\0';
+
+	setlocale (LC_TIME, "C");
+
+	EEL_CHECK_STRING_RESULT (test_strftime ("", 2000, 1, 1, 0, 0, 0), "");
+	EEL_CHECK_STRING_RESULT (test_strftime (huge_string, 2000, 1, 1, 0, 0, 0), huge_string);
+	EEL_CHECK_STRING_RESULT (test_strftime ("%%", 2000, 1, 1, 1, 0, 0), "%");
+	EEL_CHECK_STRING_RESULT (test_strftime ("%%%%", 2000, 1, 1, 1, 0, 0), "%%");
+	EEL_CHECK_STRING_RESULT (test_strftime ("%m/%d/%y, %I:%M %p", 2000, 1, 1, 1, 0, 0), "01/01/00, 01:00 AM");
+	EEL_CHECK_STRING_RESULT (test_strftime ("%-m/%-d/%y, %-I:%M %p", 2000, 1, 1, 1, 0, 0), "1/1/00, 1:00 AM");
+	EEL_CHECK_STRING_RESULT (test_strftime ("%_m/%_d/%y, %_I:%M %p", 2000, 1, 1, 1, 0, 0), " 1/ 1/00,  1:00 AM");
+
+	setlocale (LC_TIME, "");
+
+	g_free (huge_string);
+
+	/* eel_shell_quote */
+	EEL_CHECK_STRING_RESULT (g_shell_quote (""), "''");
+	EEL_CHECK_STRING_RESULT (g_shell_quote ("a"), "'a'");
+	EEL_CHECK_STRING_RESULT (g_shell_quote ("("), "'('");
+	EEL_CHECK_STRING_RESULT (g_shell_quote ("'"), "''\\'''");
+	EEL_CHECK_STRING_RESULT (g_shell_quote ("'a"), "''\\''a'");
+	EEL_CHECK_STRING_RESULT (g_shell_quote ("a'"), "'a'\\'''");
+	EEL_CHECK_STRING_RESULT (g_shell_quote ("a'a"), "'a'\\''a'");
+
+	/* eel_compare_integer */
+	EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (0), GINT_TO_POINTER (0)), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (0), GINT_TO_POINTER (1)), -1);
+	EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (1), GINT_TO_POINTER (0)), 1);
+	EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (-1), GINT_TO_POINTER (0)), -1);
+	EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (0), GINT_TO_POINTER (-1)), 1);
+	EEL_CHECK_INTEGER_RESULT (eel_compare_integer (GINT_TO_POINTER (-1), GINT_TO_POINTER (-1)), 0);
+
+#ifdef __linux__
+	EEL_CHECK_STRING_RESULT (eel_get_operating_system_name (), "Linux");
+#endif
+}
+
+#endif /* !EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-glib-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-glib-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,126 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-glib-extensions.h - interface for new functions that conceptually
+                                belong in glib. Perhaps some of these will be
+                                actually rolled into glib someday.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: John Sullivan <sullivan eazel com>
+*/
+
+#ifndef EEL_GLIB_EXTENSIONS_H
+#define EEL_GLIB_EXTENSIONS_H
+
+#include <glib.h>
+
+/* A gboolean variant for bit fields. */
+typedef guint eel_boolean_bit;
+
+/* Callback functions that have user data. */
+typedef int      (* EelCompareFunction)   (gconstpointer a,
+					   gconstpointer b,
+					   gpointer callback_data);
+typedef int      (* EelSearchFunction)    (gconstpointer item,
+					   gpointer callback_data);
+
+/* Predicate. */
+typedef gboolean (* EelPredicateFunction) (gpointer data,
+					   gpointer callback_data);
+
+/* Date & time functions. */
+GDate *     eel_g_date_new_tm                           (struct tm             *time_pieces);
+char *      eel_strdup_strftime                         (const char            *format,
+							 struct tm             *time_pieces);
+
+/* GList functions. */
+gboolean    eel_g_list_exactly_one_item                 (GList                 *list);
+gboolean    eel_g_list_more_than_one_item               (GList                 *list);
+gboolean    eel_g_list_equal                            (GList                 *list_a,
+							 GList                 *list_b);
+gboolean    eel_g_lists_sort_and_check_for_intersection (GList                **list_a,
+							 GList                **list_b);
+GList *     eel_g_list_partition                        (GList                 *list,
+							 EelPredicateFunction   predicate,
+							 gpointer               user_data,
+							 GList                **removed);
+
+/* List functions for lists of g_free'able objects. */
+void        eel_g_list_free_deep                        (GList                 *list);
+void        eel_g_list_free_deep_custom                 (GList                 *list,
+							 GFunc                  element_free_func,
+							 gpointer               user_data);
+
+/* GSList functions. */
+GList *     eel_g_list_from_g_slist                     (GSList                *list);
+GSList *    eel_g_slist_from_g_list                     (GList                 *list);
+
+/* List functions for slists of g_free'able objects. */
+void        eel_g_slist_free_deep                       (GSList                *list);
+void        eel_g_slist_free_deep_custom                (GSList                *list,
+							 GFunc                  element_free_func,
+							 gpointer               user_data);
+
+/* List functions for lists of C strings. */
+gboolean    eel_g_str_list_equal                        (GList                 *str_list_a,
+							 GList                 *str_list_b);
+GList *     eel_g_str_list_copy                         (GList                 *str_list);
+GList *     eel_g_str_list_alphabetize                  (GList                 *str_list);
+int         eel_g_str_list_index                        (GList                 *str_list,
+							 const char            *str);
+
+/* List functions for lists of objects */
+GList *     eel_g_object_list_ref                       (GList                 *list);
+void        eel_g_object_list_unref                     (GList                 *list);
+void        eel_g_object_list_free                      (GList                 *list);
+GList *     eel_g_object_list_copy                      (GList                 *list);
+
+/* GHashTable functions */
+GHashTable *eel_g_hash_table_new_free_at_exit           (GHashFunc              hash_function,
+							 GCompareFunc           key_compare_function,
+							 const char            *display_name);
+void        eel_g_hash_table_safe_for_each              (GHashTable            *hash_table,
+							 GHFunc                 callback,
+							 gpointer               callback_data);
+
+/* NULL terminated string arrays (strv). */
+int         eel_g_strv_find                             (char                 **strv,
+							 const char            *find_me);
+
+/* return the time in microseconds since the machine was started */
+gint64      eel_get_system_time                         (void);
+
+/* math */
+int         eel_round                                   (double                 d);
+
+/* A GCompareFunc for integers */
+int         eel_compare_integer                         (gconstpointer          a,
+							 gconstpointer          b);
+
+/* Return the operating system name: Linux, Solaris, etc. */
+char *      eel_get_operating_system_name               (void);
+
+/* Better weak pointer functions */
+void        eel_add_weak_pointer                        (gpointer               pointer_location);
+void        eel_remove_weak_pointer                     (gpointer               pointer_location);
+
+/* Get the filename encoding, returns TRUE if utf8 */
+gboolean    eel_get_filename_charset                    (const gchar **filename_charset);
+
+
+#endif /* EEL_GLIB_EXTENSIONS_H */

Added: trunk/eel/eel-gnome-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gnome-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,203 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gnome-extensions.c - implementation of new functions that operate on
+                            gnome classes. Perhaps some of these should be
+  			    rolled into gnome someday.
+
+   Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+
+#include "eel-gnome-extensions.h"
+#include "eel-art-extensions.h"
+#include "eel-gdk-extensions.h"
+#include "eel-glib-extensions.h"
+#include "eel-gtk-extensions.h"
+#include "eel-stock-dialogs.h"
+#include "eel-i18n.h"
+#include <X11/Xatom.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <libgnome/gnome-desktop-utils.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* Return a command string containing the path to a terminal on this system. */
+
+static char *
+try_terminal_command (const char *program,
+		      const char *args)
+{
+	char *program_in_path, *quoted, *result;
+
+	if (program == NULL) {
+		return NULL;
+	}
+
+	program_in_path = g_find_program_in_path (program);
+	if (program_in_path == NULL) {
+		return NULL;
+	}
+
+	quoted = g_shell_quote (program_in_path);
+	if (args == NULL || args[0] == '\0') {
+		return quoted;
+	}
+	result = g_strconcat (quoted, " ", args, NULL);
+	g_free (quoted);
+	return result;
+}
+
+static char *
+try_terminal_command_argv (int argc,
+			   char **argv)
+{
+	GString *string;
+	int i;
+	char *quoted, *result;
+
+	if (argc == 0) {
+		return NULL;
+	}
+
+	if (argc == 1) {
+		return try_terminal_command (argv[0], NULL);
+	}
+	
+	string = g_string_new (argv[1]);
+	for (i = 2; i < argc; i++) {
+		quoted = g_shell_quote (argv[i]);
+		g_string_append_c (string, ' ');
+		g_string_append (string, quoted);
+		g_free (quoted);
+	}
+	result = try_terminal_command (argv[0], string->str);
+	g_string_free (string, TRUE);
+
+	return result;
+}
+
+static char *
+get_terminal_command_prefix (gboolean for_command)
+{
+	int argc;
+	char **argv;
+	char *command;
+	guint i;
+	static const char *const commands[][3] = {
+		{ "gnome-terminal", "-x",                                      "" },
+		{ "dtterm",         "-e",                                      "-ls" },
+		{ "nxterm",         "-e",                                      "-ls" },
+		{ "color-xterm",    "-e",                                      "-ls" },
+		{ "rxvt",           "-e",                                      "-ls" },
+		{ "xterm",          "-e",                                      "-ls" },
+	};
+
+	/* Try the terminal from preferences. Use without any
+	 * arguments if we are just doing a standalone terminal.
+	 */
+	argc = 0;
+	argv = g_new0 (char *, 1);
+	gnome_desktop_prepend_terminal_to_vector (&argc, &argv);
+
+	command = NULL;
+	if (argc != 0) {
+		if (for_command) {
+			command = try_terminal_command_argv (argc, argv);
+		} else {
+			/* Strip off the arguments in a lame attempt
+			 * to make it be an interactive shell.
+			 */
+			command = try_terminal_command (argv[0], NULL);
+		}
+	}
+
+	while (argc != 0) {
+		g_free (argv[--argc]);
+	}
+	g_free (argv);
+
+	if (command != NULL) {
+		return command;
+	}
+
+	/* Try well-known terminal applications in same order that gmc did. */
+	for (i = 0; i < G_N_ELEMENTS (commands); i++) {
+		command = try_terminal_command (commands[i][0],
+						commands[i][for_command ? 1 : 2]);
+		if (command != NULL) {
+			break;
+		}
+	}
+	
+	return command;
+}
+
+char *
+eel_gnome_make_terminal_command (const char *command)
+{
+	char *prefix, *quoted, *terminal_command;
+
+	if (command == NULL) {
+		return get_terminal_command_prefix (FALSE);
+	}
+	prefix = get_terminal_command_prefix (TRUE);
+	quoted = g_shell_quote (command);
+	terminal_command = g_strconcat (prefix, " /bin/sh -c ", quoted, NULL);
+	g_free (prefix);
+	g_free (quoted);
+	return terminal_command;
+}
+
+void
+eel_gnome_open_terminal_on_screen (const char *command,
+				   GdkScreen  *screen)
+{
+	char *command_line;
+
+	if (screen == NULL) {
+		screen = gdk_screen_get_default ();
+	}
+	
+	command_line = eel_gnome_make_terminal_command (command);
+	if (command_line == NULL) {
+		g_message ("Could not start a terminal");
+		return;
+	}
+	gdk_spawn_command_line_on_screen (screen, command_line, NULL);
+	g_free (command_line);
+}
+
+void
+eel_gnome_open_terminal (const char *command)
+{
+	eel_gnome_open_terminal_on_screen (command, NULL);
+}

Added: trunk/eel/eel-gnome-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gnome-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,44 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gnome-extensions.h - interface for new functions that operate on
+                                 gnome classes. Perhaps some of these should be
+  			         rolled into gnome someday.
+
+   Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#ifndef EEL_GNOME_EXTENSIONS_H
+#define EEL_GNOME_EXTENSIONS_H
+
+#include <gtk/gtk.h>
+
+/* icon selection callback function. */
+typedef void (* EelIconSelectionFunction) (const char *icon_path, gpointer callback_data);
+
+
+/* Return a command string containing the path to a terminal on this system. */
+char *        eel_gnome_make_terminal_command                         (const char               *command);
+
+/* Open up a new terminal, optionally passing in a command to execute */
+void          eel_gnome_open_terminal                                 (const char               *command);
+void          eel_gnome_open_terminal_on_screen                       (const char               *command,
+								       GdkScreen                *screen);
+								 
+#endif /* EEL_GNOME_EXTENSIONS_H */

Added: trunk/eel/eel-graphic-effects.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-graphic-effects.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,399 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* Eel - pixbuf manipulation routines for graphical effects.
+ *
+ * Copyright (C) 2000 Eazel, Inc
+ *
+ * Author: Andy Hertzfeld <andy eazel com>
+ *
+ * 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.
+ */
+
+/* This file contains pixbuf manipulation routines used for graphical effects like pre-lighting
+   and selection hilighting */
+
+#include <config.h>
+#include "eel-graphic-effects.h"
+#include <string.h>
+
+/* shared utility to create a new pixbuf from the passed-in one */
+
+static GdkPixbuf *
+create_new_pixbuf (GdkPixbuf *src)
+{
+	g_assert (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB);
+	g_assert ((!gdk_pixbuf_get_has_alpha (src)
+			       && gdk_pixbuf_get_n_channels (src) == 3)
+			      || (gdk_pixbuf_get_has_alpha (src)
+				  && gdk_pixbuf_get_n_channels (src) == 4));
+
+	return gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
+			       gdk_pixbuf_get_has_alpha (src),
+			       gdk_pixbuf_get_bits_per_sample (src),
+			       gdk_pixbuf_get_width (src),
+			       gdk_pixbuf_get_height (src));
+}
+
+static GdkPixbuf *
+create_new_pixbuf_with_alpha (GdkPixbuf *src)
+{
+	g_assert (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB);
+	g_assert ((!gdk_pixbuf_get_has_alpha (src)
+			       && gdk_pixbuf_get_n_channels (src) == 3)
+			      || (gdk_pixbuf_get_has_alpha (src)
+				  && gdk_pixbuf_get_n_channels (src) == 4));
+
+	return gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
+			       TRUE,
+			       gdk_pixbuf_get_bits_per_sample (src),
+			       gdk_pixbuf_get_width (src),
+			       gdk_pixbuf_get_height (src));
+}
+
+/* utility routine to bump the level of a color component with pinning */
+
+static guchar
+lighten_component (guchar cur_value)
+{
+	int new_value = cur_value;
+	new_value += 24 + (new_value >> 3);
+	if (new_value > 255) {
+		new_value = 255;
+	}
+	return (guchar) new_value;
+}
+
+GdkPixbuf *
+eel_create_spotlight_pixbuf (GdkPixbuf* src)
+{
+	GdkPixbuf *dest;
+	int i, j;
+	int width, height, has_alpha, src_row_stride, dst_row_stride;
+	guchar *target_pixels, *original_pixels;
+	guchar *pixsrc, *pixdest;
+
+	g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
+	g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
+			       && gdk_pixbuf_get_n_channels (src) == 3)
+			      || (gdk_pixbuf_get_has_alpha (src)
+				  && gdk_pixbuf_get_n_channels (src) == 4), NULL);
+	g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL);
+
+	dest = create_new_pixbuf (src);
+	
+	has_alpha = gdk_pixbuf_get_has_alpha (src);
+	width = gdk_pixbuf_get_width (src);
+	height = gdk_pixbuf_get_height (src);
+	dst_row_stride = gdk_pixbuf_get_rowstride (dest);
+	src_row_stride = gdk_pixbuf_get_rowstride (src);
+	target_pixels = gdk_pixbuf_get_pixels (dest);
+	original_pixels = gdk_pixbuf_get_pixels (src);
+
+	for (i = 0; i < height; i++) {
+		pixdest = target_pixels + i * dst_row_stride;
+		pixsrc = original_pixels + i * src_row_stride;
+		for (j = 0; j < width; j++) {		
+			*pixdest++ = lighten_component (*pixsrc++);
+			*pixdest++ = lighten_component (*pixsrc++);
+			*pixdest++ = lighten_component (*pixsrc++);
+			if (has_alpha) {
+				*pixdest++ = *pixsrc++;
+			}
+		}
+	}
+	return dest;
+}
+
+
+/* the following routine was stolen from the panel to darken a pixbuf, by manipulating the saturation */
+
+/* saturation is 0-255, darken is 0-255 */
+
+GdkPixbuf *
+eel_create_darkened_pixbuf (GdkPixbuf *src, int saturation, int darken)
+{
+	gint i, j;
+	gint width, height, src_row_stride, dest_row_stride;
+	gboolean has_alpha;
+	guchar *target_pixels, *original_pixels;
+	guchar *pixsrc, *pixdest;
+	guchar intensity;
+	guchar alpha;
+	guchar negalpha;
+	guchar r, g, b;
+	GdkPixbuf *dest;
+
+	g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
+	g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
+			       && gdk_pixbuf_get_n_channels (src) == 3)
+			      || (gdk_pixbuf_get_has_alpha (src)
+				  && gdk_pixbuf_get_n_channels (src) == 4), NULL);
+	g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL);
+
+	dest = create_new_pixbuf (src);
+
+	has_alpha = gdk_pixbuf_get_has_alpha (src);
+	width = gdk_pixbuf_get_width (src);
+	height = gdk_pixbuf_get_height (src);
+	dest_row_stride = gdk_pixbuf_get_rowstride (dest);
+	src_row_stride = gdk_pixbuf_get_rowstride (src);
+	target_pixels = gdk_pixbuf_get_pixels (dest);
+	original_pixels = gdk_pixbuf_get_pixels (src);
+
+	for (i = 0; i < height; i++) {
+		pixdest = target_pixels + i * dest_row_stride;
+		pixsrc = original_pixels + i * src_row_stride;
+		for (j = 0; j < width; j++) {
+			r = *pixsrc++;
+			g = *pixsrc++;
+			b = *pixsrc++;
+			intensity = (r * 77 + g * 150 + b * 28) >> 8;
+			negalpha = ((255 - saturation) * darken) >> 8;
+			alpha = (saturation * darken) >> 8;
+			*pixdest++ = (negalpha * intensity + alpha * r) >> 8;
+			*pixdest++ = (negalpha * intensity + alpha * g) >> 8;
+			*pixdest++ = (negalpha * intensity + alpha * b) >> 8;
+			if (has_alpha) {
+				*pixdest++ = *pixsrc++;
+			}
+		}
+	}
+	return dest;
+}
+
+/* this routine colorizes the passed-in pixbuf by multiplying each pixel with the passed in color */
+
+GdkPixbuf *
+eel_create_colorized_pixbuf (GdkPixbuf *src,
+				  int red_value,
+				  int green_value,
+				  int blue_value)
+{
+	int i, j;
+	int width, height, has_alpha, src_row_stride, dst_row_stride;
+	guchar *target_pixels;
+	guchar *original_pixels;
+	guchar *pixsrc;
+	guchar *pixdest;
+	GdkPixbuf *dest;
+	
+	g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
+	g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
+			       && gdk_pixbuf_get_n_channels (src) == 3)
+			      || (gdk_pixbuf_get_has_alpha (src)
+				  && gdk_pixbuf_get_n_channels (src) == 4), NULL);
+	g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL);
+
+	dest = create_new_pixbuf (src);
+	
+	has_alpha = gdk_pixbuf_get_has_alpha (src);
+	width = gdk_pixbuf_get_width (src);
+	height = gdk_pixbuf_get_height (src);
+	src_row_stride = gdk_pixbuf_get_rowstride (src);
+	dst_row_stride = gdk_pixbuf_get_rowstride (dest);
+	target_pixels = gdk_pixbuf_get_pixels (dest);
+	original_pixels = gdk_pixbuf_get_pixels (src);
+
+	for (i = 0; i < height; i++) {
+		pixdest = target_pixels + i*dst_row_stride;
+		pixsrc = original_pixels + i*src_row_stride;
+		for (j = 0; j < width; j++) {		
+			*pixdest++ = (*pixsrc++ * red_value) >> 8;
+			*pixdest++ = (*pixsrc++ * green_value) >> 8;
+			*pixdest++ = (*pixsrc++ * blue_value) >> 8;
+			if (has_alpha) {
+				*pixdest++ = *pixsrc++;
+			}
+		}
+	}
+	return dest;
+}
+
+/* utility to stretch a frame to the desired size */
+
+static void
+draw_frame_row (GdkPixbuf *frame_image, int target_width, int source_width, int source_v_position, int dest_v_position, GdkPixbuf *result_pixbuf, int left_offset, int height)
+{
+	int remaining_width, h_offset, slab_width;
+	
+	remaining_width = target_width;
+	h_offset = 0;
+	while (remaining_width > 0) {	
+		slab_width = remaining_width > source_width ? source_width : remaining_width;
+		gdk_pixbuf_copy_area (frame_image, left_offset, source_v_position, slab_width, height, result_pixbuf, left_offset + h_offset, dest_v_position);
+		remaining_width -= slab_width;
+		h_offset += slab_width; 
+	}
+}
+
+/* utility to draw the middle section of the frame in a loop */
+static void
+draw_frame_column (GdkPixbuf *frame_image, int target_height, int source_height, int source_h_position, int dest_h_position, GdkPixbuf *result_pixbuf, int top_offset, int width)
+{
+	int remaining_height, v_offset, slab_height;
+	
+	remaining_height = target_height;
+	v_offset = 0;
+	while (remaining_height > 0) {	
+		slab_height = remaining_height > source_height ? source_height : remaining_height;
+		gdk_pixbuf_copy_area (frame_image, source_h_position, top_offset, width, slab_height, result_pixbuf, dest_h_position, top_offset + v_offset);
+		remaining_height -= slab_height;
+		v_offset += slab_height; 
+	}
+}
+
+GdkPixbuf *
+eel_stretch_frame_image (GdkPixbuf *frame_image, int left_offset, int top_offset, int right_offset, int bottom_offset,
+			 int dest_width, int dest_height, gboolean fill_flag)
+{
+	GdkPixbuf *result_pixbuf;
+	guchar *pixels_ptr;
+	int frame_width, frame_height;
+	int y, row_stride;
+	int target_width, target_frame_width;
+	int target_height, target_frame_height;
+	
+	frame_width  = gdk_pixbuf_get_width  (frame_image);
+	frame_height = gdk_pixbuf_get_height (frame_image );
+	
+	if (fill_flag) {
+		result_pixbuf = gdk_pixbuf_scale_simple (frame_image, dest_width, dest_height, GDK_INTERP_NEAREST);
+	} else {
+		result_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, dest_width, dest_height);
+	}
+	row_stride = gdk_pixbuf_get_rowstride (result_pixbuf);
+	pixels_ptr = gdk_pixbuf_get_pixels (result_pixbuf);
+	
+	/* clear the new pixbuf */
+	if (!fill_flag) {
+		for (y = 0; y < dest_height; y++) {
+			memset (pixels_ptr, 255, row_stride);
+			pixels_ptr += row_stride; 
+		}
+	}
+	
+	target_width  = dest_width - left_offset - right_offset;
+	target_frame_width = frame_width - left_offset - right_offset;
+	
+	target_height  = dest_height - top_offset - bottom_offset;
+	target_frame_height = frame_height - top_offset - bottom_offset;
+	
+	/* draw the left top corner  and top row */
+	gdk_pixbuf_copy_area (frame_image, 0, 0, left_offset, top_offset, result_pixbuf, 0,  0);
+	draw_frame_row (frame_image, target_width, target_frame_width, 0, 0, result_pixbuf, left_offset, top_offset);
+	
+	/* draw the right top corner and left column */
+	gdk_pixbuf_copy_area (frame_image, frame_width - right_offset, 0, right_offset, top_offset, result_pixbuf, dest_width - right_offset,  0);
+	draw_frame_column (frame_image, target_height, target_frame_height, 0, 0, result_pixbuf, top_offset, left_offset);
+
+	/* draw the bottom right corner and bottom row */
+	gdk_pixbuf_copy_area (frame_image, frame_width - right_offset, frame_height - bottom_offset, right_offset, bottom_offset, result_pixbuf, dest_width - right_offset,  dest_height - bottom_offset);
+	draw_frame_row (frame_image, target_width, target_frame_width, frame_height - bottom_offset, dest_height - bottom_offset, result_pixbuf, left_offset, bottom_offset);
+		
+	/* draw the bottom left corner and the right column */
+	gdk_pixbuf_copy_area (frame_image, 0, frame_height - bottom_offset, left_offset, bottom_offset, result_pixbuf, 0,  dest_height - bottom_offset);
+	draw_frame_column (frame_image, target_height, target_frame_height, frame_width - right_offset, dest_width - right_offset, result_pixbuf, top_offset, right_offset);
+	
+	return result_pixbuf;
+}
+
+
+/* draw an arbitrary frame around an image, with the result passed back in a newly allocated pixbuf */
+GdkPixbuf *
+eel_embed_image_in_frame (GdkPixbuf *source_image, GdkPixbuf *frame_image, int left_offset, int top_offset, int right_offset, int bottom_offset)
+{
+	GdkPixbuf *result_pixbuf;
+	int source_width, source_height;
+	int dest_width, dest_height;
+	
+	source_width  = gdk_pixbuf_get_width  (source_image);
+	source_height = gdk_pixbuf_get_height (source_image);
+
+	dest_width  = source_width  + left_offset + right_offset;
+	dest_height = source_height + top_offset  + bottom_offset;
+	
+	result_pixbuf = eel_stretch_frame_image (frame_image, left_offset, top_offset, right_offset, bottom_offset, 
+						      dest_width, dest_height, FALSE);
+		
+	/* Finally, copy the source image into the framed area */
+	gdk_pixbuf_copy_area (source_image, 0, 0, source_width, source_height, result_pixbuf, left_offset,  top_offset);
+
+	return result_pixbuf;
+}
+
+
+/* this routine takes the source pixbuf and returns a new one that's semi-transparent, by
+   clearing every other pixel's alpha value in a checkerboard grip.  We have to do the
+   checkerboard instead of reducing the alpha since it will be turned into an alpha-less
+   gdkpixmap and mask for the actual dragging */
+
+GdkPixbuf *
+eel_make_semi_transparent (GdkPixbuf *src)
+{
+	gint i, j, temp_alpha;
+	gint width, height, has_alpha, src_row_stride, dst_row_stride;
+	guchar *target_pixels, *original_pixels;
+	guchar *pixsrc, *pixdest;
+	guchar alpha_value;
+	GdkPixbuf *dest_pixbuf;
+	guchar start_alpha_value;
+	
+	g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
+	g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
+			       && gdk_pixbuf_get_n_channels (src) == 3)
+			      || (gdk_pixbuf_get_has_alpha (src)
+				  && gdk_pixbuf_get_n_channels (src) == 4), NULL);
+	g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL);
+
+	dest_pixbuf = create_new_pixbuf_with_alpha (src);
+
+	has_alpha = gdk_pixbuf_get_has_alpha (src);
+	width = gdk_pixbuf_get_width (src);
+	height = gdk_pixbuf_get_height (src);
+	src_row_stride = gdk_pixbuf_get_rowstride (src);
+	dst_row_stride = gdk_pixbuf_get_rowstride (dest_pixbuf);
+	
+	/* set up pointers to the actual pixels */
+	target_pixels = gdk_pixbuf_get_pixels (dest_pixbuf);
+	original_pixels = gdk_pixbuf_get_pixels (src);
+
+	/* loop through the pixels to do the actual work, copying from the source to the destination */
+	start_alpha_value = ~0;
+	for (i = 0; i < height; i++) {
+		pixdest = target_pixels + i * dst_row_stride;
+		pixsrc = original_pixels + i * src_row_stride;
+		alpha_value = start_alpha_value;
+		for (j = 0; j < width; j++) {
+			*pixdest++ = *pixsrc++; /* red */
+			*pixdest++ = *pixsrc++; /* green */
+			*pixdest++ = *pixsrc++; /* blue */
+			
+			if (has_alpha) {
+				temp_alpha = *pixsrc++;
+			} else {
+				temp_alpha = ~0;
+			}
+			*pixdest++ = temp_alpha & alpha_value;
+			
+			alpha_value = ~alpha_value;
+		}
+		
+		start_alpha_value = ~start_alpha_value;
+	}
+	
+	return dest_pixbuf;
+}
+

Added: trunk/eel/eel-graphic-effects.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-graphic-effects.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,66 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-graphic-effects.h: Pixmap manipulation routines for graphical effects.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+ 
+   Authors: Andy Hertzfeld <andy eazel com>
+ */
+
+#ifndef EEL_GRAPHIC_EFFECTS_H
+#define EEL_GRAPHIC_EFFECTS_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+/* return a lightened pixbuf for pre-lighting */
+GdkPixbuf *eel_create_spotlight_pixbuf (GdkPixbuf *source_pixbuf);
+
+/* return a darkened pixbuf for selection hiliting */
+GdkPixbuf *eel_create_darkened_pixbuf  (GdkPixbuf *source_pixbuf,
+					int        saturation,
+					int        darken);
+
+/* return a pixbuf colorized with the color specified by the parameters */
+GdkPixbuf* eel_create_colorized_pixbuf (GdkPixbuf *source_pixbuf,
+					int        red_value,
+					int        green_value,
+					int        blue_value);
+
+/* stretch a image frame */
+GdkPixbuf *eel_stretch_frame_image     (GdkPixbuf *frame_image,
+					int        left_offset,
+					int        top_offset,
+					int        right_offset,
+					int        bottom_offset,
+					int        dest_width,
+					int        dest_height,
+					gboolean   fill_flag);
+
+/* embed in image in a frame */
+GdkPixbuf *eel_embed_image_in_frame    (GdkPixbuf *source_image,
+					GdkPixbuf *frame_image,
+					int        left_offset,
+					int        top_offset,
+					int        right_offset,
+					int        bottom_offset);
+
+/* return a semi-transparent pixbuf from the source pixbuf using a checkboard
+   stipple in the alpha channel (so it can be converted to an alpha-less pixmap) */
+GdkPixbuf *eel_make_semi_transparent   (GdkPixbuf *source_pixbuf);
+
+#endif /* EEL_GRAPHIC_EFFECTS_H */

Added: trunk/eel/eel-gtk-container.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gtk-container.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,209 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gtk-container.c - Functions to simplify the implementations of 
+  			 GtkContainer widgets.
+
+   Copyright (C) 2001 Ramiro Estrugo.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-gtk-container.h"
+#include "eel-art-extensions.h"
+
+/**
+ * eel_gtk_container_child_expose_event:
+ * 
+ * @container: A GtkContainer widget.
+ * @child: A child of @container or NULL;
+ * @event: The expose event.
+ *
+ * Forward an expose event to a child if needed.  It is valid to give a NULL @child.
+ * In that case this function is a noop.  Proper clipping is done to ensure that the @child
+ * does indeed need to be forwarded the exposure event.  Finally, the forwarding 
+ * only occurs if the child is a NO_WINDOW widget.  Of course, it is valid to feed
+ * non NO_WINDOW widgets to this function, in which case this function is a noop.
+ */
+void
+eel_gtk_container_child_expose_event (GtkContainer *container,
+				      GtkWidget *child,
+				      GdkEventExpose *event)
+{
+	g_return_if_fail (GTK_IS_CONTAINER (container));
+
+	if (child == NULL) {
+		return;
+	}
+
+	g_return_if_fail (GTK_IS_WIDGET (child));
+
+	gtk_container_propagate_expose (container, child, event);
+}
+
+/**
+ * eel_gtk_container_child_map:
+ * 
+ * @container: A GtkContainer widget.
+ * @child: A child of @container or NULL;
+ *
+ * Map a child if needed.  This is usually called from the "GtkWidget::map"
+ * method of the @container widget.  If @child is NULL, then this function is a noop.
+ */
+void
+eel_gtk_container_child_map (GtkContainer *container,
+			     GtkWidget *child)
+{
+	g_return_if_fail (GTK_IS_CONTAINER (container));
+
+	if (child == NULL) {
+		return;
+	}
+
+	g_return_if_fail (child->parent == GTK_WIDGET (container));
+
+	if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child)) {
+		gtk_widget_map (child);
+	}
+}
+
+/**
+ * eel_gtk_container_child_unmap:
+ * 
+ * @container: A GtkContainer widget.
+ * @child: A child of @container or NULL;
+ *
+ * Unmap a child if needed.  This is usually called from the "GtkWidget::unmap"
+ * method of the @container widget.  If @child is NULL, then this function is a noop.
+ */
+void
+eel_gtk_container_child_unmap (GtkContainer *container,
+			       GtkWidget *child)
+{
+	g_return_if_fail (GTK_IS_CONTAINER (container));
+
+	if (child == NULL) {
+		return;
+	}
+
+	g_return_if_fail (child->parent == GTK_WIDGET (container));
+	
+	if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_MAPPED (child)) {
+		gtk_widget_unmap (child);
+	}
+}
+
+/**
+ * eel_gtk_container_child_add:
+ * 
+ * @container: A GtkContainer widget.
+ * @child: A non NULL unparented child.
+ *
+ * Add a @child to a @container.  The @child is realized, mapped
+ * and resized if needed.  This is usually called from the "GtkContainer::add"
+ * method of the @container.  The @child cannot be NULL.
+ */
+void
+eel_gtk_container_child_add (GtkContainer *container,
+			     GtkWidget *child)
+{
+	g_return_if_fail (GTK_IS_CONTAINER (container));
+	g_return_if_fail (GTK_IS_WIDGET (child));
+	
+	gtk_widget_set_parent (child, GTK_WIDGET (container));
+
+	if (GTK_WIDGET_REALIZED (container)) {
+		gtk_widget_realize (child);
+	}
+	
+	if (GTK_WIDGET_VISIBLE (container) && GTK_WIDGET_VISIBLE (child)) {
+		if (GTK_WIDGET_MAPPED (container)) {
+			gtk_widget_map (child);
+		}
+		
+		gtk_widget_queue_resize (child);
+	}
+}
+
+/**
+ * eel_gtk_container_child_remove:
+ * 
+ * @container: A GtkContainer widget.
+ * @child: A non NULL child of @container.
+ *
+ * Remove @child from @container.  The @container is resized if needed.
+ * This is usually called from the "GtkContainer::remove" method of the
+ * @container.  The child cannot be NULL.
+ */
+void
+eel_gtk_container_child_remove (GtkContainer *container,
+				GtkWidget *child)
+{
+	gboolean child_was_visible;
+
+	g_return_if_fail (GTK_IS_CONTAINER (container));
+	g_return_if_fail (GTK_IS_WIDGET (child));
+	g_return_if_fail (child->parent == GTK_WIDGET (container));
+	
+	child_was_visible = GTK_WIDGET_VISIBLE (child);
+	
+	gtk_widget_unparent (child);
+
+	if (child_was_visible) {
+		gtk_widget_queue_resize (GTK_WIDGET (container));
+	}
+}
+
+/**
+ * eel_gtk_container_child_size_allocate:
+ * 
+ * @container: A GtkContainer widget.
+ * @child: A child of @container or NULL;
+ *
+ * Invoke the "GtkWidget::size_allocate" method of @child.  
+ * This function is usually called from the "GtkWidget::size_allocate"
+ * method of @container.  The child can be NULL, in which case this 
+ * function is a noop.
+ */
+void
+eel_gtk_container_child_size_allocate (GtkContainer *container,
+				       GtkWidget *child,
+				       EelIRect child_geometry)
+{
+	GtkAllocation child_allocation;
+
+	g_return_if_fail (GTK_IS_CONTAINER (container));
+
+	if (child == NULL) {
+		return;
+	}
+
+	g_return_if_fail (GTK_IS_WIDGET (child));
+	g_return_if_fail (child->parent == GTK_WIDGET (container));
+
+ 	if (eel_irect_is_empty (&child_geometry)) {
+		return;
+	}
+
+	child_allocation.x = child_geometry.x0;
+	child_allocation.y = child_geometry.y0;
+	child_allocation.width = eel_irect_get_width (child_geometry);
+	child_allocation.height = eel_irect_get_height (child_geometry);
+	
+	gtk_widget_size_allocate (child, &child_allocation);
+}

Added: trunk/eel/eel-gtk-container.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gtk-container.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,47 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gtk-container.h - Functions to simplify the implementations of 
+  			 GtkContainer widgets.
+
+   Copyright (C) 2001 Ramiro Estrugo.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_GTK_CONTAINER_H
+#define EEL_GTK_CONTAINER_H
+
+#include <gtk/gtk.h>
+#include <eel/eel-art-extensions.h>
+
+void eel_gtk_container_child_expose_event (GtkContainer   *container,
+					   GtkWidget      *child,
+					   GdkEventExpose *event);
+void eel_gtk_container_child_map          (GtkContainer   *container,
+					   GtkWidget      *child);
+void eel_gtk_container_child_unmap        (GtkContainer   *container,
+					   GtkWidget      *child);
+void eel_gtk_container_child_add          (GtkContainer   *container,
+					   GtkWidget      *child);
+void eel_gtk_container_child_remove       (GtkContainer   *container,
+					   GtkWidget      *child);
+void eel_gtk_container_child_size_allocate (GtkContainer *container,
+					    GtkWidget *child,
+					    EelIRect child_geometry);
+
+#endif /* EEL_GTK_CONTAINER_H */

Added: trunk/eel/eel-gtk-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gtk-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,1174 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gtk-extensions.c - implementation of new functions that operate on
+  			  gtk classes. Perhaps some of these should be
+  			  rolled into gtk someday.
+
+   Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+   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.
+
+   Authors: John Sullivan <sullivan eazel com>
+            Ramiro Estrugo <ramiro eazel com>
+	    Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+#include "eel-gtk-extensions.h"
+
+#include "eel-gdk-pixbuf-extensions.h"
+#include "eel-glib-extensions.h"
+#include "eel-gnome-extensions.h"
+#include "eel-pango-extensions.h"
+#include "eel-string.h"
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkprivate.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <math.h>
+#include "eel-marshal.h"
+#include "eel-marshal.c"
+
+/* This number is fairly arbitrary. Long enough to show a pretty long
+ * menu title, but not so long to make a menu grotesquely wide.
+ */
+#define MAXIMUM_MENU_TITLE_LENGTH	48
+
+/* Used for window position & size sanity-checking. The sizes are big enough to prevent
+ * at least normal-sized gnome panels from obscuring the window at the screen edges. 
+ */
+#define MINIMUM_ON_SCREEN_WIDTH		100
+#define MINIMUM_ON_SCREEN_HEIGHT	100
+
+
+/**
+ * eel_gtk_window_get_geometry_string:
+ * @window: a #GtkWindow
+ * 
+ * Obtains the geometry string for this window, suitable for
+ * set_geometry_string(); assumes the window has NorthWest gravity
+ * 
+ * Return value: geometry string, must be freed
+ **/
+char*
+eel_gtk_window_get_geometry_string (GtkWindow *window)
+{
+	char *str;
+	int w, h, x, y;
+	
+	g_return_val_if_fail (GTK_IS_WINDOW (window), NULL);
+	g_return_val_if_fail (gtk_window_get_gravity (window) ==
+			      GDK_GRAVITY_NORTH_WEST, NULL);
+
+	gtk_window_get_position (window, &x, &y);
+	gtk_window_get_size (window, &w, &h);
+	
+	str = g_strdup_printf ("%dx%d+%d+%d", w, h, x, y);
+
+	return str;
+}
+
+static void
+send_delete_event (GtkWindow *window)
+{
+	/* Synthesize delete_event to close window. */
+	
+	GdkEvent event;
+	GtkWidget *widget;
+	
+	widget = GTK_WIDGET (window);
+	
+	event.any.type = GDK_DELETE;
+	event.any.window = widget->window;
+	event.any.send_event = TRUE;
+	
+	g_object_ref (event.any.window);
+	gtk_main_do_event (&event);
+	g_object_unref (event.any.window);
+}
+
+static int
+handle_standard_close_accelerator (GtkWindow *window, 
+				   GdkEventKey *event, 
+				   gpointer user_data)
+{
+	g_assert (GTK_IS_WINDOW (window));
+	g_assert (event != NULL);
+	g_assert (user_data == NULL);
+
+	if (eel_gtk_window_event_is_close_accelerator (window, event)) {
+		send_delete_event (window);
+		g_signal_stop_emission_by_name (
+			G_OBJECT (window), "key_press_event");
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+/**
+ * eel_gtk_window_event_is_close_accelerator:
+ * 
+ * Tests whether a key event is a standard window close accelerator.
+ * Not needed for clients that use eel_gtk_window_set_up_close_accelerator;
+ * use only if you must set up your own key_event handler for your own reasons.
+ **/
+gboolean
+eel_gtk_window_event_is_close_accelerator (GtkWindow *window, GdkEventKey *event)
+{
+	g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	if (event->state & GDK_CONTROL_MASK) {
+		/* Note: menu item equivalents are case-sensitive, so we will
+		 * be case-sensitive here too.
+		 */		
+		if (event->keyval == EEL_STANDARD_CLOSE_WINDOW_CONTROL_KEY) {
+			return TRUE;
+		}
+	}
+
+
+	return FALSE;	
+}
+
+/**
+ * eel_gtk_window_set_up_close_accelerator:
+ * 
+ * Sets up the standard keyboard equivalent to close the window.
+ * Call this for windows that don't set up a keyboard equivalent to
+ * close the window some other way, e.g. via a menu item accelerator.
+ *
+ * NOTE: do not use for GtkDialog, it already sets up the right
+ * stuff here.
+ * 
+ * @window: The GtkWindow that should be hidden when the standard
+ * keyboard equivalent is typed.
+ **/
+void
+eel_gtk_window_set_up_close_accelerator (GtkWindow *window)
+{
+	g_return_if_fail (GTK_IS_WINDOW (window));
+
+	if (GTK_IS_DIALOG (window)) {
+		g_warning ("eel_gtk_window_set_up_close_accelerator: Should not mess with close accelerator on GtkDialogs");
+		return;
+	}
+	
+	g_signal_connect (window,
+			    "key_press_event",
+			    G_CALLBACK (handle_standard_close_accelerator),
+			    NULL);
+}
+
+static void
+sanity_check_window_position (int *left, int *top)
+{
+	g_assert (left != NULL);
+	g_assert (top != NULL);
+
+	/* Make sure the top of the window is on screen, for
+	 * draggability (might not be necessary with all window managers,
+	 * but seems reasonable anyway). Make sure the top of the window
+	 * isn't off the bottom of the screen, or so close to the bottom
+	 * that it might be obscured by the panel.
+	 */
+	*top = CLAMP (*top, 0, gdk_screen_height() - MINIMUM_ON_SCREEN_HEIGHT);
+	
+	/* FIXME bugzilla.eazel.com 669: 
+	 * If window has negative left coordinate, set_uposition sends it
+	 * somewhere else entirely. Not sure what level contains this bug (XWindows?).
+	 * Hacked around by pinning the left edge to zero, which just means you
+	 * can't set a window to be partly off the left of the screen using
+	 * this routine.
+	 */
+	/* Make sure the left edge of the window isn't off the right edge of
+	 * the screen, or so close to the right edge that it might be
+	 * obscured by the panel.
+	 */
+	*left = CLAMP (*left, 0, gdk_screen_width() - MINIMUM_ON_SCREEN_WIDTH);
+}
+
+static void
+sanity_check_window_dimensions (guint *width, guint *height)
+{
+	g_assert (width != NULL);
+	g_assert (height != NULL);
+
+	/* Pin the size of the window to the screen, so we don't end up in
+	 * a state where the window is so big essential parts of it can't
+	 * be reached (might not be necessary with all window managers,
+	 * but seems reasonable anyway).
+	 */
+	*width = MIN (*width, gdk_screen_width());
+	*height = MIN (*height, gdk_screen_height());
+}
+
+/**
+ * eel_gtk_window_set_initial_geometry:
+ * 
+ * Sets the position and size of a GtkWindow before the
+ * GtkWindow is shown. It is an error to call this on a window that
+ * is already on-screen. Takes into account screen size, and does
+ * some sanity-checking on the passed-in values.
+ * 
+ * @window: A non-visible GtkWindow
+ * @geometry_flags: A EelGdkGeometryFlags value defining which of
+ * the following parameters have defined values
+ * @left: pixel coordinate for left of window
+ * @top: pixel coordinate for top of window
+ * @width: width of window in pixels
+ * @height: height of window in pixels
+ */
+void
+eel_gtk_window_set_initial_geometry (GtkWindow *window, 
+					  EelGdkGeometryFlags geometry_flags,
+					  int left,
+					  int top,
+					  guint width,
+					  guint height)
+{
+	GdkScreen *screen;
+	int real_left, real_top;
+	int screen_width, screen_height;
+
+	g_return_if_fail (GTK_IS_WINDOW (window));
+
+	/* Setting the default size doesn't work when the window is already showing.
+	 * Someday we could make this move an already-showing window, but we don't
+	 * need that functionality yet. 
+	 */
+	g_return_if_fail (!GTK_WIDGET_VISIBLE (window));
+
+	if ((geometry_flags & EEL_GDK_X_VALUE) && (geometry_flags & EEL_GDK_Y_VALUE)) {
+		real_left = left;
+		real_top = top;
+
+		screen = gtk_window_get_screen (window);
+		screen_width  = gdk_screen_get_width  (screen);
+		screen_height = gdk_screen_get_height (screen);
+
+		/* This is sub-optimal. GDK doesn't allow us to set win_gravity
+		 * to South/East types, which should be done if using negative
+		 * positions (so that the right or bottom edge of the window
+		 * appears at the specified position, not the left or top).
+		 * However it does seem to be consistent with other GNOME apps.
+		 */
+		if (geometry_flags & EEL_GDK_X_NEGATIVE) {
+			real_left = screen_width - real_left;
+		}
+		if (geometry_flags & EEL_GDK_Y_NEGATIVE) {
+			real_top = screen_height - real_top;
+		}
+
+		sanity_check_window_position (&real_left, &real_top);
+		gtk_window_move (window, real_left, real_top);
+	}
+
+	if ((geometry_flags & EEL_GDK_WIDTH_VALUE) && (geometry_flags & EEL_GDK_HEIGHT_VALUE)) {
+		sanity_check_window_dimensions (&width, &height);
+		gtk_window_set_default_size (GTK_WINDOW (window), (int)width, (int)height);
+	}
+}
+
+/**
+ * eel_gtk_window_set_initial_geometry_from_string:
+ * 
+ * Sets the position and size of a GtkWindow before the
+ * GtkWindow is shown. The geometry is passed in as a string. 
+ * It is an error to call this on a window that
+ * is already on-screen. Takes into account screen size, and does
+ * some sanity-checking on the passed-in values.
+ * 
+ * @window: A non-visible GtkWindow
+ * @geometry_string: A string suitable for use with eel_gdk_parse_geometry
+ * @minimum_width: If the width from the string is smaller than this,
+ * use this for the width.
+ * @minimum_height: If the height from the string is smaller than this,
+ * use this for the height.
+ * @ignore_position: If true position data from string will be ignored.
+ */
+void
+eel_gtk_window_set_initial_geometry_from_string (GtkWindow *window, 
+						 const char *geometry_string,
+						 guint minimum_width,
+						 guint minimum_height,
+						 gboolean ignore_position)
+{
+	int left, top;
+	guint width, height;
+	EelGdkGeometryFlags geometry_flags;
+
+	g_return_if_fail (GTK_IS_WINDOW (window));
+	g_return_if_fail (geometry_string != NULL);
+
+	/* Setting the default size doesn't work when the window is already showing.
+	 * Someday we could make this move an already-showing window, but we don't
+	 * need that functionality yet. 
+	 */
+	g_return_if_fail (!GTK_WIDGET_VISIBLE (window));
+
+	geometry_flags = eel_gdk_parse_geometry (geometry_string, &left, &top, &width, &height);
+
+	/* Make sure the window isn't smaller than makes sense for this window.
+	 * Other sanity checks are performed in set_initial_geometry.
+	 */
+	if (geometry_flags & EEL_GDK_WIDTH_VALUE) {
+		width = MAX (width, minimum_width);
+	}
+	if (geometry_flags & EEL_GDK_HEIGHT_VALUE) {
+		height = MAX (height, minimum_height);
+	}
+	
+	/* Ignore saved window position if requested. */
+	if (ignore_position) {
+		geometry_flags &= ~(EEL_GDK_X_VALUE | EEL_GDK_Y_VALUE);
+	}
+
+	eel_gtk_window_set_initial_geometry (window, geometry_flags, left, top, width, height);
+}
+
+/**
+ * eel_pop_up_context_menu:
+ * 
+ * Pop up a context menu under the mouse.
+ * The menu is sunk after use, so it will be destroyed unless the 
+ * caller first ref'ed it.
+ * 
+ * This function is more of a helper function than a gtk extension,
+ * so perhaps it belongs in a different file.
+ * 
+ * @menu: The menu to pop up under the mouse.
+ * @offset_x: Number of pixels to displace the popup menu vertically
+ * @offset_y: Number of pixels to displace the popup menu horizontally
+ * @event: The event that invoked this popup menu.
+ **/
+void 
+eel_pop_up_context_menu (GtkMenu	     *menu,
+			      gint16	      offset_x,
+			      gint16	      offset_y,
+			      GdkEventButton *event)
+{
+	GdkPoint offset;
+	int button;
+
+	g_return_if_fail (GTK_IS_MENU (menu));
+
+	offset.x = offset_x;
+	offset.y = offset_y;
+
+	/* The event button needs to be 0 if we're popping up this menu from
+	 * a button release, else a 2nd click outside the menu with any button
+	 * other than the one that invoked the menu will be ignored (instead
+	 * of dismissing the menu). This is a subtle fragility of the GTK menu code.
+	 */
+
+	if (event) {
+		button = event->type == GDK_BUTTON_RELEASE
+			? 0
+			: event->button;
+	} else {
+		button = 0;
+	}
+	
+	gtk_menu_popup (menu,					/* menu */
+			NULL,					/* parent_menu_shell */
+			NULL,					/* parent_menu_item */
+			NULL,
+			&offset,			        /* data */
+			button,					/* button */
+			event ? event->time : GDK_CURRENT_TIME); /* activate_time */
+
+	gtk_object_sink (GTK_OBJECT (menu));
+}
+
+GtkMenuItem *
+eel_gtk_menu_append_separator (GtkMenu *menu)
+{
+	return eel_gtk_menu_insert_separator (menu, -1);
+}
+
+GtkMenuItem *
+eel_gtk_menu_insert_separator (GtkMenu *menu, int index)
+{
+	GtkWidget *menu_item;
+
+	menu_item = gtk_separator_menu_item_new ();
+	gtk_widget_show (menu_item);
+	gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menu_item, index);
+
+	return GTK_MENU_ITEM (menu_item);
+}
+
+void
+eel_gtk_menu_set_item_visibility (GtkMenu *menu, int index, gboolean visible)
+{
+	GList *children;
+	GtkWidget *menu_item;
+
+	g_return_if_fail (GTK_IS_MENU (menu));
+
+	children = gtk_container_get_children (GTK_CONTAINER (menu));
+	g_return_if_fail (index >= 0 && index < (int) g_list_length (children));
+
+	menu_item = GTK_WIDGET (g_list_nth_data (children, index));
+	if (visible) {
+		gtk_widget_show (menu_item);
+	} else {
+		gtk_widget_hide (menu_item);
+	}
+
+	g_list_free (children);
+}
+
+gboolean
+eel_point_in_allocation (const GtkAllocation *allocation,
+			      int x, int y)
+{
+	g_return_val_if_fail (allocation != NULL, FALSE);
+	return x >= allocation->x
+		&& y >= allocation->y
+		&& x < allocation->x + allocation->width 
+		&& y < allocation->y + allocation->height;
+}
+
+/* FIXME this function is dangerous, because widget->window coords (or
+ * other window-belonging-to-widget coords) do not need to be in the
+ * same coordinate system as widget->allocation.
+ * If you use this function, be aware of that. Someone should probably
+ * audit all uses, too.
+ */
+gboolean
+eel_point_in_widget (GtkWidget *widget,
+			  int x, int y)
+{
+	if (widget == NULL) {
+		return FALSE;
+	}
+	g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+	return eel_point_in_allocation (&widget->allocation, x, y);
+}
+
+/**
+ * eel_gtk_widget_set_shown
+ *
+ * Show or hide a widget.
+ * @widget: The widget.
+ * @shown: Boolean value indicating whether the widget should be shown or hidden.
+ **/
+void
+eel_gtk_widget_set_shown (GtkWidget *widget, gboolean shown)
+{
+	g_return_if_fail (GTK_IS_WIDGET (widget));
+
+	if (shown) {
+		gtk_widget_show (widget);
+	} else {
+		gtk_widget_hide (widget);
+	}
+}
+
+/* This stuff is stolen from Gtk. */
+
+typedef struct DisconnectInfo {
+	GtkObject *object1;
+	guint disconnect_handler1;
+	guint signal_handler;
+	GtkObject *object2;
+	guint disconnect_handler2;
+} DisconnectInfo;
+
+static void
+alive_disconnecter (GtkObject *object, DisconnectInfo *info)
+{
+	g_assert (info != NULL);
+	g_assert (GTK_IS_OBJECT (info->object1));
+	g_assert (info->disconnect_handler1 != 0);
+	g_assert (info->signal_handler != 0);
+	g_assert (GTK_IS_OBJECT (info->object2));
+	g_assert (info->disconnect_handler2 != 0);
+	g_assert (object == info->object1 || object == info->object2);
+	
+	g_signal_handler_disconnect (info->object1, info->disconnect_handler1);
+	g_signal_handler_disconnect (info->object1, info->signal_handler);
+	g_signal_handler_disconnect (info->object2, info->disconnect_handler2);
+	
+	g_free (info);
+}
+
+/**
+ * eel_gtk_signal_connect_full_while_alive
+ *
+ * Like gtk_signal_connect_while_alive, but works with full parameters.
+ **/
+void
+eel_gtk_signal_connect_full_while_alive (GtkObject *object,
+					 const gchar *name,
+					 GtkSignalFunc func,
+					 GtkCallbackMarshal marshal,
+					 gpointer data,
+					 GtkDestroyNotify destroy_func,
+					 gboolean object_signal,
+					 gboolean after,
+					 GtkObject *alive_object)
+{
+	DisconnectInfo *info;
+	
+	g_return_if_fail (GTK_IS_OBJECT (object));
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (func != NULL || marshal != NULL);
+	g_return_if_fail (object_signal == FALSE || object_signal == TRUE);
+	g_return_if_fail (after == FALSE || after == TRUE);
+	g_return_if_fail (GTK_IS_OBJECT (alive_object));
+	
+	info = g_new (DisconnectInfo, 1);
+	info->object1 = object;
+	info->object2 = alive_object;
+	
+
+	info->signal_handler = g_signal_connect_closure (
+		object, name,
+		(object_signal
+		 ? g_cclosure_new_swap
+		 : g_cclosure_new) (func, data, (GClosureNotify) destroy_func),
+		after);
+
+	info->disconnect_handler1 = g_signal_connect (G_OBJECT (object),
+						      "destroy",
+						      G_CALLBACK (alive_disconnecter),
+						      info);
+	info->disconnect_handler2 = g_signal_connect (G_OBJECT (alive_object),
+						      "destroy",
+						      G_CALLBACK (alive_disconnecter),
+						      info);
+}
+
+typedef struct
+{
+	GtkObject *object;
+	guint object_destroy_handler;
+	
+	GtkWidget *realized_widget;
+	guint realized_widget_destroy_handler;
+	guint realized_widget_unrealized_handler;
+
+	guint signal_handler;
+} RealizeDisconnectInfo;
+
+static void
+while_realized_disconnecter (GtkObject *object,
+			     RealizeDisconnectInfo *info)
+{
+	g_assert (GTK_IS_OBJECT (object));
+	g_assert (info != NULL);
+	g_assert (GTK_IS_OBJECT (info->object));
+	g_assert (info->object_destroy_handler != 0);
+	g_assert (info->object_destroy_handler != 0);
+	g_assert (info->realized_widget_destroy_handler != 0);
+	g_assert (info->realized_widget_unrealized_handler != 0);
+
+ 	g_signal_handler_disconnect (info->object, info->object_destroy_handler);
+ 	g_signal_handler_disconnect (info->object, info->signal_handler);
+ 	g_signal_handler_disconnect (info->realized_widget, info->realized_widget_destroy_handler);
+ 	g_signal_handler_disconnect (info->realized_widget, info->realized_widget_unrealized_handler);
+	g_free (info);
+}
+
+/**
+ * eel_gtk_signal_connect_while_realized:
+ *
+ * @object: Object to connect to.
+ * @name: Name of signal to connect to.
+ * @callback: Caller's callback.
+ * @callback_data: Caller's callback_data.
+ * @realized_widget: Widget to monitor for realized state.  Signal is connected
+ *                   while this wigget is realized.
+ *
+ * Connect to a signal of an object while another widget is realized.  This is 
+ * useful for non windowed widgets that need to monitor events in their ancestored
+ * windowed widget.  The signal is automatically disconnected when &widget is
+ * unrealized.  Also, the signal is automatically disconnected when either &object
+ * or &widget are destroyed.
+ **/
+void
+eel_gtk_signal_connect_while_realized (GtkObject *object,
+					    const char *name,
+					    GtkSignalFunc callback,
+					    gpointer callback_data,
+					    GtkWidget *realized_widget)
+{
+	RealizeDisconnectInfo *info;
+
+	g_return_if_fail (GTK_IS_OBJECT (object));
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (name[0] != '\0');
+	g_return_if_fail (callback != NULL);
+	g_return_if_fail (GTK_IS_WIDGET (realized_widget));
+	g_return_if_fail (GTK_WIDGET_REALIZED (realized_widget));
+
+	info = g_new0 (RealizeDisconnectInfo, 1);
+	
+	info->object = object;
+	info->object_destroy_handler = 
+		g_signal_connect (G_OBJECT (info->object),
+				  "destroy",
+				  G_CALLBACK (while_realized_disconnecter),
+				  info);
+	
+	info->realized_widget = realized_widget;
+	info->realized_widget_destroy_handler = 
+		g_signal_connect (G_OBJECT (info->realized_widget),
+				  "destroy",
+				  G_CALLBACK (while_realized_disconnecter),
+				  info);
+	info->realized_widget_unrealized_handler = 
+		g_signal_connect_after (G_OBJECT (info->realized_widget),
+					"unrealize",
+					G_CALLBACK (while_realized_disconnecter),
+					info);
+
+	info->signal_handler = g_signal_connect (G_OBJECT (info->object),
+						 name, callback, callback_data);
+}
+
+/**
+ * eel_gtk_container_get_first_child.
+ *
+ * Returns the first child of a container.
+ * @container: The container.
+ **/
+
+static void
+get_first_callback (GtkWidget *widget, gpointer callback_data)
+{
+	GtkWidget **first_child_slot;
+
+	g_assert (GTK_IS_WIDGET (widget));
+	g_assert (callback_data != NULL);
+	
+	first_child_slot = callback_data;
+
+	if (*first_child_slot == NULL) {
+		*first_child_slot = widget;
+		/* We'd stop the iterating now if we could. */
+	} else {
+		g_assert (GTK_IS_WIDGET (*first_child_slot));
+	}
+}
+
+GtkWidget *
+eel_gtk_container_get_first_child (GtkContainer *container)
+{
+	GtkWidget *first_child;
+
+	g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
+	
+	first_child = NULL;
+	gtk_container_foreach (container, get_first_callback, &first_child);
+	g_assert (first_child == NULL || GTK_IS_WIDGET (first_child));
+	return first_child;
+}
+
+typedef struct {
+	GtkCallback   callback;
+	gpointer      callback_data;
+} container_foreach_deep_callback_data;
+
+static void
+container_foreach_deep_callback (GtkWidget *child, gpointer data)
+{
+	container_foreach_deep_callback_data *deep_data;
+
+	deep_data = (container_foreach_deep_callback_data *) data;
+
+	deep_data->callback (child, deep_data->callback_data);
+
+	if (GTK_IS_CONTAINER (child)) {
+		gtk_container_foreach (GTK_CONTAINER (child), container_foreach_deep_callback, data);
+	}
+}
+
+void
+eel_gtk_container_foreach_deep (GtkContainer *container,
+				     GtkCallback callback,
+				     gpointer callback_data)
+{
+	container_foreach_deep_callback_data deep_data;
+	deep_data.callback = callback;
+	deep_data.callback_data = callback_data;
+	gtk_container_foreach (container, container_foreach_deep_callback, &deep_data);
+}
+
+/* The standard gtk_adjustment_set_value ignores page size, which
+ * disagrees with the logic used by scroll bars, for example.
+ */
+void
+eel_gtk_adjustment_set_value (GtkAdjustment *adjustment,
+				   float value)
+{
+	float upper_page_start, clamped_value;
+
+	g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+	
+	upper_page_start = MAX (adjustment->upper - adjustment->page_size, adjustment->lower);
+	clamped_value = CLAMP (value, adjustment->lower, upper_page_start);
+	if (clamped_value != adjustment->value) {
+		adjustment->value = clamped_value;
+		gtk_adjustment_value_changed (adjustment);
+	}
+}
+
+/* Clamp a value if the minimum or maximum has changed. */
+void
+eel_gtk_adjustment_clamp_value (GtkAdjustment *adjustment)
+{
+	g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
+	
+	eel_gtk_adjustment_set_value (adjustment, adjustment->value);
+}
+
+/**
+ * eel_gtk_label_make_bold.
+ *
+ * Switches the font of label to a bold equivalent.
+ * @label: The label.
+ **/
+void
+eel_gtk_label_make_bold (GtkLabel *label)
+{
+	PangoFontDescription *font_desc;
+
+	font_desc = pango_font_description_new ();
+
+	pango_font_description_set_weight (font_desc,
+					   PANGO_WEIGHT_BOLD);
+
+	/* This will only affect the weight of the font, the rest is
+	 * from the current state of the widget, which comes from the
+	 * theme or user prefs, since the font desc only has the
+	 * weight flag turned on.
+	 */
+	gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
+
+	pango_font_description_free (font_desc);
+}
+
+/**
+ * eel_gtk_label_set_scale:
+ * @label: 
+ * @num_steps: 
+ *
+ * Function is broken, see eel_gtk_label_make_larger() for explanation
+ * 
+ **/
+void
+eel_gtk_label_set_scale (GtkLabel *label,
+			 double scale_factor)
+{
+	PangoAttrList *old_attr_list;
+	PangoAttrList *attr_list;
+	
+	g_return_if_fail (GTK_IS_LABEL (label));
+	g_return_if_fail (scale_factor > 0);
+
+	old_attr_list = gtk_label_get_attributes (label);
+	attr_list = eel_pango_attr_list_apply_global_attribute (old_attr_list,
+								pango_attr_scale_new (scale_factor));
+	gtk_label_set_attributes (label, attr_list);
+	pango_attr_list_unref (attr_list);
+}
+
+static void
+get_layout_location (GtkLabel  *label,
+                     gint      *xp,
+                     gint      *yp)
+{
+  GtkMisc *misc;
+  GtkWidget *widget;
+  float xalign;
+  int x, y;
+  int shadow_offset;
+  
+  shadow_offset = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (label),
+						      "eel-label-shadow-offset"));
+  
+  misc = GTK_MISC (label);
+  widget = GTK_WIDGET (label);
+  
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+    xalign = misc->xalign;
+  else
+    xalign = 1.0 - misc->xalign;
+  
+  x = floor (widget->allocation.x + (int)misc->xpad
+             + ((widget->allocation.width - widget->requisition.width - shadow_offset) * xalign)
+             + 0.5);
+  
+  y = floor (widget->allocation.y + (int)misc->ypad 
+             + ((widget->allocation.height - widget->requisition.height - shadow_offset) * misc->yalign)
+             + 0.5);
+  
+
+  if (xp)
+    *xp = x;
+
+  if (yp)
+    *yp = y;
+}
+
+static gboolean
+eel_gtk_label_expose_event (GtkLabel *label, GdkEventExpose *event, gpointer user_data)
+{
+	int x, y;
+	GdkColor color;
+	GtkWidget *widget;
+	GdkGC *gc;
+	guint32 shadow_color;
+	int shadow_offset;
+	
+	shadow_color = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (label),
+							   "eel-label-shadow-color"));
+	shadow_offset = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (label),
+							    "eel-label-shadow-offset"));
+
+	color = eel_gdk_rgb_to_color (shadow_color);
+	
+	get_layout_location (label, &x, &y);
+
+	widget = GTK_WIDGET (label);
+	if (shadow_offset > 0) {
+		gc = gdk_gc_new (widget->window);
+		gdk_gc_set_rgb_fg_color (gc, &color);
+		gdk_gc_set_clip_rectangle (gc, &event->area);
+		
+		gdk_draw_layout (widget->window,
+				 gc,
+				 x + shadow_offset, y + shadow_offset,
+				 label->layout);
+		g_object_unref (gc);
+	}
+	
+	gtk_paint_layout (widget->style,
+			  widget->window,
+			  GTK_WIDGET_STATE (widget),
+			  FALSE,
+			  &event->area,
+			  widget,
+			  "label",
+			  x, y,
+			  label->layout);
+
+	return TRUE;
+}
+
+static void
+eel_gtk_label_size_request (GtkLabel *label, GtkRequisition *requisition, gpointer user_data)
+{
+	gint shadow_offset;
+
+	shadow_offset = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (label),
+							    "eel-label-shadow-offset"));
+
+	requisition->width += shadow_offset;
+	requisition->height += shadow_offset;
+}
+
+static void
+set_up_label (GtkLabel *label)
+{
+
+	if (g_object_get_data (G_OBJECT (label), "eel-label-set-up") != NULL) {
+		return;
+	}
+
+	g_signal_connect (label, "expose_event",
+			  G_CALLBACK (eel_gtk_label_expose_event), NULL);
+	g_signal_connect_after (label, "size_request",
+				G_CALLBACK (eel_gtk_label_size_request), NULL);
+
+	g_object_set_data (G_OBJECT (label), "eel-label-set-up", "eel-label-set-up");
+}
+
+void
+eel_gtk_label_set_drop_shadow_color (GtkLabel *label,
+				     guint32 color)
+{
+	set_up_label (label);
+
+	g_object_set_data (G_OBJECT (label), "eel-label-shadow-color",
+			   GINT_TO_POINTER (color));
+		
+	gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+void
+eel_gtk_label_set_drop_shadow_offset (GtkLabel *label,
+				      gint offset)
+{
+	set_up_label (label);
+
+	g_object_set_data (G_OBJECT (label), "eel-label-shadow-offset",
+			   GINT_TO_POINTER (offset));
+	
+	gtk_widget_queue_draw (GTK_WIDGET (label));
+}
+
+void
+eel_gtk_widget_set_background_color (GtkWidget *widget,
+				     const char *color_spec)
+{
+	GdkColor color;
+
+	g_return_if_fail (GTK_IS_WIDGET (widget));
+
+	eel_gdk_color_parse_with_white_default (color_spec, &color);
+
+	gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, &color);
+	gtk_widget_modify_base (widget, GTK_STATE_NORMAL, &color);
+	gtk_widget_modify_bg (widget, GTK_STATE_ACTIVE, &color);
+	gtk_widget_modify_base (widget, GTK_STATE_ACTIVE, &color);
+}
+
+void
+eel_gtk_widget_set_foreground_color (GtkWidget *widget,
+				     const char *color_spec)
+{
+	GdkColor color;
+
+	g_return_if_fail (GTK_IS_WIDGET (widget));
+
+	eel_gdk_color_parse_with_white_default (color_spec, &color);
+
+	gtk_widget_modify_fg (widget, GTK_STATE_NORMAL, &color);
+	gtk_widget_modify_text (widget, GTK_STATE_NORMAL, &color);
+	gtk_widget_modify_fg (widget, GTK_STATE_ACTIVE, &color);
+	gtk_widget_modify_text (widget, GTK_STATE_ACTIVE, &color);
+}
+
+GtkWidget *
+eel_gtk_widget_find_windowed_ancestor (GtkWidget *widget)
+{
+	g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+	while (widget && GTK_WIDGET_NO_WINDOW (widget)) {
+		widget = widget->parent;
+	}
+
+	return widget;
+}
+
+/* eel_gtk_get_system_font:
+ *
+ * Return the system font as selected in the control center. Need to 
+ * g_object_unref() the result when done with it.
+ *
+ * Perhaps there is a better way to figure out what that font is, but
+ * the following is simple enough and it works.
+ */
+PangoFontDescription *
+eel_gtk_get_system_font (void)
+{
+	GtkWidget *label;
+	PangoFontDescription *font;
+
+	label = gtk_label_new ("");
+	
+	gtk_widget_ensure_style (label);
+
+	font = pango_font_description_copy (label->style->font_desc);
+
+ 	gtk_object_sink (GTK_OBJECT (label));
+
+	return font;
+}
+
+void
+eel_gtk_widget_get_button_event_location (GtkWidget *widget,
+					  const GdkEventButton *event,
+					  int *x,
+					  int *y)
+{
+	int window_x, window_y;
+
+	g_return_if_fail (GTK_IS_WIDGET (widget));
+	g_return_if_fail (event != NULL);
+
+	gdk_window_get_position (event->window, &window_x, &window_y);
+	if (x != NULL) {
+		*x = event->x + window_x - widget->allocation.x;
+	}
+	if (y != NULL) {
+		*y = event->y + window_y - widget->allocation.y;
+	}
+}
+
+void
+eel_gtk_widget_get_motion_event_location (GtkWidget *widget,
+					  const GdkEventMotion *event,
+					  int *x,
+					  int *y)
+{
+	eel_gtk_widget_get_button_event_location (widget, (const GdkEventButton *) event, x, y);
+}
+
+gboolean
+eel_gtk_tree_view_cell_is_completely_visible (GtkTreeView          *tree_view,
+					      GtkTreePath          *path,
+					      GtkTreeViewColumn    *column)
+{
+	GdkRectangle cell_rect, visible_rect;
+
+	gtk_tree_view_get_background_area (tree_view, path, column, &cell_rect);
+	gtk_tree_view_widget_to_tree_coords (tree_view, cell_rect.x, cell_rect.y,
+					     &cell_rect.x, &cell_rect.y);
+	gtk_tree_view_get_visible_rect (tree_view, &visible_rect);
+	return eel_gdk_rectangle_contains_rectangle (visible_rect, cell_rect);
+}
+
+static gboolean 
+tree_view_button_press_callback (GtkWidget *tree_view,
+				 GdkEventButton *event,
+				 gpointer data)
+{
+	GtkTreePath *path;
+	GtkTreeViewColumn *column;
+
+	if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
+		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view),
+						   event->x, event->y,
+						   &path,
+						   &column,
+						   NULL, 
+						   NULL)) {
+			gtk_tree_view_row_activated
+				(GTK_TREE_VIEW (tree_view), path, column);
+		}
+	}
+
+	return FALSE;
+}
+
+void
+eel_gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view,
+						gboolean should_activate)
+{
+	guint button_press_id;
+
+	button_press_id = GPOINTER_TO_UINT 
+		(g_object_get_data (G_OBJECT (tree_view), 
+				    "eel-tree-view-activate"));
+
+	if (button_press_id && !should_activate) {
+		g_signal_handler_disconnect (tree_view, button_press_id);
+		g_object_set_data (G_OBJECT (tree_view), 
+				   "eel-tree-view-activate", 
+				   NULL);
+	} else if (!button_press_id && should_activate) {
+		button_press_id = g_signal_connect 
+			(tree_view,
+			 "button_press_event",
+			 G_CALLBACK  (tree_view_button_press_callback),
+			 NULL);
+		g_object_set_data (G_OBJECT (tree_view), 
+				   "eel-tree-view-activate", 
+				   GUINT_TO_POINTER (button_press_id));
+	}
+}
+
+gboolean
+eel_gtk_viewport_get_visible_rect (GtkViewport  *viewport, 
+				   GdkRectangle *rect)
+{
+	GdkRectangle viewport_rect;
+	GdkRectangle child_rect;
+	gboolean return_val;
+
+	g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), FALSE);
+	g_return_val_if_fail (rect != NULL, FALSE);
+	
+	if (GTK_WIDGET_REALIZED (viewport)) {
+		viewport_rect.x = 0;
+		viewport_rect.y = 0;
+		gdk_drawable_get_size (viewport->view_window, 
+				       &viewport_rect.width, 
+				       &viewport_rect.height);
+		
+		gdk_window_get_position (viewport->bin_window,
+					 &child_rect.x,
+					 &child_rect.y);
+		gdk_drawable_get_size (viewport->bin_window,
+				       &child_rect.width,
+				       &child_rect.height);
+
+		return_val = gdk_rectangle_intersect (&viewport_rect, 
+						      &child_rect,
+						      rect);
+		rect->x -= child_rect.x;
+		rect->y -= child_rect.y;
+		
+		return return_val;
+	}
+	
+	rect->x = rect->y = rect->width = rect->height = 0;
+	return FALSE;
+}
+
+void
+eel_gtk_viewport_scroll_to_rect (GtkViewport  *viewport, 
+				 GdkRectangle *rect)
+{
+	GdkRectangle visible_rect;
+	int scroll_x;
+	int scroll_y;
+	GtkAdjustment *adjustment;
+
+	g_return_if_fail (GTK_IS_VIEWPORT (viewport));
+	g_return_if_fail (rect != NULL);
+
+	if (eel_gtk_viewport_get_visible_rect (viewport, &visible_rect)) {
+		scroll_x = -1;
+		scroll_y = -1;
+
+		if (rect->x + rect->width > visible_rect.x + visible_rect.width) {
+			scroll_x = rect->x - (visible_rect.width - rect->width);
+		}
+		if (rect->y + rect->height > visible_rect.y + visible_rect.height) {
+			scroll_y = rect->y - (visible_rect.height - rect->height);
+		}
+
+		if (rect->x < visible_rect.x) {
+			scroll_x = rect->x;
+		}
+
+		if (rect->y < visible_rect.y) {
+			scroll_y = rect->y;
+		}
+
+		adjustment = gtk_viewport_get_hadjustment (viewport);
+		if (adjustment && scroll_x != -1) {
+			eel_gtk_adjustment_set_value (adjustment,
+						      (double)scroll_x);
+		}
+
+		adjustment = gtk_viewport_get_vadjustment (viewport);
+		if (adjustment && scroll_y != -1) {
+			eel_gtk_adjustment_set_value (adjustment,
+						      (double)scroll_y);
+		}
+	}
+}

Added: trunk/eel/eel-gtk-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gtk-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,141 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-gtk-extensions.h - interface for new functions that operate on
+  			       gtk classes. Perhaps some of these should be
+  			       rolled into gtk someday.
+
+   Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+   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.
+
+   Authors: John Sullivan <sullivan eazel com>
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_GTK_EXTENSIONS_H
+#define EEL_GTK_EXTENSIONS_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <eel/eel-gdk-extensions.h>
+
+#define EEL_DEFAULT_POPUP_MENU_DISPLACEMENT 	2
+#define EEL_STANDARD_CLOSE_WINDOW_CONTROL_KEY 	'w'
+
+/* signals */
+void                  eel_gtk_signal_connect_full_while_alive         (GtkObject            *object,
+								       const gchar          *name,
+								       GtkSignalFunc         func,
+								       GtkCallbackMarshal    marshal,
+								       gpointer              data,
+								       GtkDestroyNotify      destroy_func,
+								       gboolean              object_signal,
+								       gboolean              after,
+								       GtkObject            *alive_object);
+void                  eel_gtk_signal_connect_while_realized           (GtkObject            *object,
+								       const char           *name,
+								       GtkSignalFunc         callback,
+								       gpointer              callback_data,
+								       GtkWidget            *realized_widget);
+
+/* GtkWidget */
+void                  eel_gtk_widget_set_shown                        (GtkWidget            *widget,
+								       gboolean              shown);
+gboolean              eel_point_in_allocation                         (const GtkAllocation  *allocation,
+								       int                   x,
+								       int                   y);
+gboolean              eel_point_in_widget                             (GtkWidget            *widget,
+								       int                   x,
+								       int                   y);
+void                  eel_gtk_widget_set_background_color             (GtkWidget            *widget,
+								       const char           *color_spec);
+void                  eel_gtk_widget_set_foreground_color             (GtkWidget            *widget,
+								       const char           *color_spec);
+GtkWidget *           eel_gtk_widget_find_windowed_ancestor           (GtkWidget            *widget);
+PangoFontDescription *eel_gtk_get_system_font                         (void);
+void                  eel_gtk_widget_get_button_event_location        (GtkWidget            *widget,
+								       const GdkEventButton *event,
+								       int                  *x,
+								       int                  *y);
+void                  eel_gtk_widget_get_motion_event_location        (GtkWidget            *widget,
+								       const GdkEventMotion *event,
+								       int                  *x,
+								       int                  *y);
+
+/* GtkContainer */
+GtkWidget *           eel_gtk_container_get_first_child               (GtkContainer         *container);
+void                  eel_gtk_container_foreach_deep                  (GtkContainer         *container,
+								       GtkCallback           callback,
+								       gpointer              callback_data);
+
+/* GtkWindow */
+void                  eel_gtk_window_set_initial_geometry             (GtkWindow            *window,
+								       EelGdkGeometryFlags   geometry_flags,
+								       int                   left,
+								       int                   top,
+								       guint                 width,
+								       guint                 height);
+void                  eel_gtk_window_set_initial_geometry_from_string (GtkWindow            *window,
+								       const char           *geometry_string,
+								       guint                 minimum_width,
+								       guint                 minimum_height,
+								       gboolean		     ignore_position);
+void                  eel_gtk_window_set_up_close_accelerator         (GtkWindow            *window);
+gboolean              eel_gtk_window_event_is_close_accelerator       (GtkWindow            *window,
+								       GdkEventKey          *event);
+char *                eel_gtk_window_get_geometry_string              (GtkWindow            *window);
+
+
+/* GtkMenu and GtkMenuItem */
+void                  eel_pop_up_context_menu                         (GtkMenu              *menu,
+								       gint16                offset_x,
+								       gint16                offset_y,
+								       GdkEventButton       *event);
+GtkMenuItem *         eel_gtk_menu_append_separator                   (GtkMenu              *menu);
+GtkMenuItem *         eel_gtk_menu_insert_separator                   (GtkMenu              *menu,
+								       int                   index);
+void                  eel_gtk_menu_set_item_visibility                (GtkMenu              *menu,
+								       int                   index,
+								       gboolean              visible);
+
+/* GtkLabel */
+void                  eel_gtk_label_make_bold                         (GtkLabel             *label);
+void                  eel_gtk_label_set_scale                         (GtkLabel             *label,
+								       double                scale_factor);
+void                  eel_gtk_label_set_drop_shadow_color             (GtkLabel             *label,
+								       guint32               color);
+void                  eel_gtk_label_set_drop_shadow_offset            (GtkLabel             *label,
+								       gint                  offset); 
+/* GtkAdjustment */
+void                  eel_gtk_adjustment_set_value                    (GtkAdjustment        *adjustment,
+								       float                 value);
+void                  eel_gtk_adjustment_clamp_value                  (GtkAdjustment        *adjustment);
+
+/* GtkTreeView */
+gboolean              eel_gtk_tree_view_cell_is_completely_visible    (GtkTreeView          *tree_view,
+								       GtkTreePath          *path,
+								       GtkTreeViewColumn    *column);
+void                  eel_gtk_tree_view_set_activate_on_single_click  (GtkTreeView               *tree_view,
+								       gboolean                   should_activate);
+
+/* GtkViewport */
+gboolean              eel_gtk_viewport_get_visible_rect               (GtkViewport          *viewport, 
+								       GdkRectangle         *rect);
+
+void                  eel_gtk_viewport_scroll_to_rect                 (GtkViewport          *viewport,
+								       GdkRectangle         *rect);
+
+#endif /* EEL_GTK_EXTENSIONS_H */

Added: trunk/eel/eel-gtk-macros.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-gtk-macros.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,178 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-gtk-macros.h: Macros to reduce boilerplate when using GTK.
+ 
+   Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Authors: Darin Adler <darin bentspoon com>
+            Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_GTK_MACROS_H
+#define EEL_GTK_MACROS_H
+
+#ifndef EEL_DISABLE_DEPRECATED
+
+/* Define a parent_class global and a get_type function for a GTK class.
+   Since this is boilerplate, it's better not to repeat it over and over again.
+   Called like this:
+
+       EEL_CLASS_BOILERPLATE (EelBookmark, eel_bookmark, GTK_TYPE_OBJECT)
+
+   The parent_class_type parameter is guaranteed to be evaluated only once
+   so it can be an expression, even an expression that contains a function call.
+*/
+
+#define EEL_CLASS_BOILERPLATE(class_name, prefix, parent_class_type)          \
+	EEL_BOILERPLATE (class_name, class_name, prefix, parent_class_type,   \
+                         EEL_REGISTER_TYPE)
+#define EEL_REGISTER_TYPE(class_name, corba_name)                             \
+	g_type_register_static (parent_type, #class_name, &info, 0)
+
+#define EEL_BOILERPLATE(class_name, corba_name, prefix, parent_class_type,    \
+                        register_type)                                        \
+                                                                              \
+static gpointer parent_class;                                                 \
+                                                                              \
+GtkType                                                                       \
+prefix##_get_type (void)                                                      \
+{                                                                             \
+	GtkType parent_type;                                                  \
+	static GtkType type;                                                  \
+                                                                              \
+	if (type == 0) {                                                      \
+		static GTypeInfo info = {                                     \
+			sizeof (class_name##Class),                           \
+                        NULL, NULL,                                           \
+			(GClassInitFunc) prefix##_class_init,                 \
+                        NULL, NULL,                                           \
+			sizeof (class_name), 0,                               \
+			(GInstanceInitFunc) prefix##_init,                    \
+			NULL                                                  \
+		};                                                            \
+                                                                              \
+		parent_type = (parent_class_type);                            \
+		type = register_type (class_name, corba_name);                \
+		parent_class = g_type_class_ref (parent_type);                \
+	}                                                                     \
+                                                                              \
+	return type;                                                          \
+}
+
+/* Call a parent class version of a virtual function (or default
+ * signal handler since that's the same thing). Nice because it
+ * documents what it's doing and there is less chance for a
+ * typo. Depends on the parent class pointer having the conventional
+ * name "parent_class" as the boilerplate macro above does it.
+ */
+#define EEL_CALL_PARENT(parent_class_cast_macro, signal, parameters)          \
+                                                                              \
+G_STMT_START {                                                                \
+	if (parent_class_cast_macro (parent_class)->signal != NULL) {         \
+		(* parent_class_cast_macro (parent_class)->signal) parameters;\
+        }                                                                     \
+} G_STMT_END
+
+/* Same thing, for functions with a return value. */
+#define EEL_CALL_PARENT_WITH_RETURN_VALUE(parent_class_cast_macro, signal,    \
+                                          parameters)                         \
+                                                                              \
+(parent_class_cast_macro (parent_class)->signal == NULL)                      \
+	? 0                                                                   \
+	: ((* parent_class_cast_macro (parent_class)->signal) parameters)
+
+#endif /* EEL_DISABLE_DEPRECATED */
+
+/* Call a virtual function. Useful when the virtual function is not a
+ * signal, otherwise you want to gtk_signal emit. Nice because it
+ * documents what it's doing and there is less chance for a typo.
+ */
+#define EEL_CALL_METHOD(class_cast_macro, object, signal, parameters)         \
+                                                                              \
+G_STMT_START {                                                                \
+	if (class_cast_macro (G_OBJECT_GET_CLASS (object))->signal != NULL) { \
+		(* class_cast_macro (G_OBJECT_GET_CLASS (object))->signal)    \
+                parameters;                                                   \
+	}                                                                     \
+} G_STMT_END
+
+/* Same thing, for functions with a return value. */
+#define EEL_CALL_METHOD_WITH_RETURN_VALUE(class_cast_macro, object, signal,   \
+                                          parameters)                         \
+                                                                              \
+(class_cast_macro (G_OBJECT_GET_CLASS (object))->signal == NULL)              \
+	? 0                                                                   \
+	: ((* class_cast_macro (G_OBJECT_GET_CLASS (object))->signal)         \
+           parameters)                                                        \
+
+#ifndef G_DISABLE_ASSERT
+
+/* Define a signal that is not implemented by this class but must be 
+ * implemented by subclasses. This macro should be used inside the
+ * class initialization function. The companion macro EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL
+ * must be used earlier in the file. Called like this:
+ * 
+ * EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass,
+ *					 fm_directory_view,
+ *					 clear); 
+ */
+#define EEL_ASSIGN_MUST_OVERRIDE_SIGNAL(class_pointer, prefix, signal)        \
+                                                                              \
+* (void (**)(void)) & (class_pointer)->signal = prefix##_unimplemented_##signal
+
+/* Provide a debug-only implementation of a signal that must be implemented
+ * by subclasses. The debug-only implementation fires a warning if it is called.
+ * This macro should be placed as if it were a function, earlier in the file
+ * than the class initialization function. Called like this:
+ * 
+ * EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, clear);
+ */
+#define EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL(prefix, signal)                    \
+                                                                              \
+static void                                                                   \
+prefix##_unimplemented_##signal (void)                                        \
+{                                                                             \
+	g_warning ("failed to override signal " #prefix "->" #signal);        \
+}
+
+#else /* G_DISABLE_ASSERT */
+
+#define EEL_DEFINE_MUST_OVERRIDE_SIGNAL(class_cast_macro, class_pointer, prefix, signal)
+#define EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL(prefix, signal)
+#define EEL_ASSIGN_MUST_OVERRIDE_SIGNAL(class_pointer, prefix, signal)
+
+#endif /* G_DISABLE_ASSERT */
+
+/* Access a method. */
+#define EEL_ACCESS_METHOD(class_cast_macro, object, method)                   \
+(class_cast_macro (G_OBJECT_GET_CLASS (object))->method)
+
+/* Invoke a method for a given object. */
+#define EEL_INVOKE_METHOD(class_cast_macro, object, method, parameters)       \
+((* EEL_ACCESS_METHOD (class_cast_macro, object, method)) parameters)
+
+/* Assert the non-nullness of a method for a given object. */
+#define EEL_ASSERT_METHOD(class_cast_macro, object, method)                   \
+g_assert (EEL_ACCESS_METHOD (class_cast_macro, object, method) != NULL)
+
+/* Invoke a method if it ain't null. */
+#define EEL_INVOKE_METHOD_IF(class_cast_macro, object, method, parameters)    \
+(EEL_ACCESS_METHOD (class_cast_macro, object, method) ? 0 :                   \
+	EEL_INVOKE_METHOD (class_cast_macro, object, method, parameters))
+
+#endif /* EEL_GTK_MACROS_H */

Added: trunk/eel/eel-i18n.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-i18n.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,51 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-i18n.c:  I18n stuff for Eel.
+
+   Copyright (C) 2002 MandrakeSoft.
+
+   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.
+
+   Authors: Frederic Crozat <fcrozat mandrakesoft com>
+*/
+
+#include <config.h>
+#include <glib.h>
+#include "eel-i18n.h"
+
+#ifdef ENABLE_NLS
+
+#include <libintl.h>
+
+
+
+G_CONST_RETURN char *
+_eel_gettext (const char *str)
+{
+	static gboolean _eel_gettext_initialized = FALSE;
+
+	if (!_eel_gettext_initialized) {
+		bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+#    ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+		bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#    endif
+		_eel_gettext_initialized = TRUE;
+	}
+
+	return dgettext (GETTEXT_PACKAGE, str);
+}
+
+#endif /* ENABLE_NLS */

Added: trunk/eel/eel-i18n.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-i18n.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,53 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-i18n.h: I18n stuff for Eel.
+
+   Copyright (C) 2002 MandrakeSoft
+
+   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.
+
+   Authors: Frederic Crozat <fcrozat mandrakesoft com>
+*/
+
+#ifndef EEL_I18N_H
+#define EEL_I18N_H
+
+#ifdef ENABLE_NLS
+#include <glib.h>
+
+G_CONST_RETURN char *
+_eel_gettext (const char *str) G_GNUC_FORMAT (1);
+
+#include <libintl.h>
+#define _(String) _eel_gettext(String)
+
+#ifdef gettext_noop
+#define N_(String) gettext_noop(String)
+#else
+#define N_(String) (String)
+#endif
+#else /* NLS is disabled */
+#define _(String) (String)
+#define N_(String) (String)
+#define textdomain(String) (String)
+#define gettext(String) (String)
+#define dgettext(Domain,String) (String)
+#define dcgettext(Domain,String,Type) (String)
+#define bindtextdomain(Domain,Directory) (Domain)
+#endif
+
+
+#endif /* EEL_I18N_H */

Added: trunk/eel/eel-image-table.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-image-table.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,579 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-image-table.c - An image table.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-image-table.h"
+
+#include "eel-art-extensions.h"
+#include "eel-art-gtk-extensions.h"
+#include "eel-debug-drawing.h"
+#include "eel-gtk-extensions.h"
+#include "eel-gtk-macros.h"
+#include "eel-labeled-image.h"
+#include "eel-marshal.h"
+#include <gtk/gtk.h>
+
+/* Arguments */
+enum
+{
+	ARG_0,
+	ARG_CHILD_UNDER_POINTER
+};
+
+/* Detail member struct */
+struct EelImageTableDetails
+{
+	GtkWidget *child_under_pointer;
+	GtkWidget *child_being_pressed;
+	GdkGC     *clear_gc;
+};
+
+/* Signals */
+typedef enum
+{
+	CHILD_ENTER,
+	CHILD_LEAVE,
+	CHILD_PRESSED,
+	CHILD_RELEASED,
+	CHILD_CLICKED,
+	LAST_SIGNAL
+} ImageTableSignals;
+
+/* Signals */
+static guint image_table_signals[LAST_SIGNAL] = { 0 };
+
+static void    eel_image_table_class_init     (EelImageTableClass *image_table_class);
+static void    eel_image_table_init           (EelImageTable      *image);
+
+/* GObjectClass methods */
+static void    eel_image_table_finalize             (GObject            *object);
+
+/* GtkWidgetClass methods */
+static void    eel_image_table_realize              (GtkWidget          *widget);
+static void    eel_image_table_unrealize            (GtkWidget          *widget);
+
+/* GtkContainerClass methods */
+static void    eel_image_table_remove               (GtkContainer       *container,
+						     GtkWidget          *widget);
+static GtkType eel_image_table_child_type           (GtkContainer       *container);
+
+/* Private EelImageTable methods */
+static void    image_table_emit_signal              (EelImageTable      *image_table,
+						     GtkWidget          *child,
+						     guint               signal_index,
+						     int                 x,
+						     int                 y,
+						     int                 button,
+						     guint               state,
+						     GdkEvent           *event);
+
+/* Ancestor callbacks */
+static int     ancestor_enter_notify_event          (GtkWidget          *widget,
+						     GdkEventCrossing   *event,
+						     gpointer            event_data);
+static int     ancestor_leave_notify_event          (GtkWidget          *widget,
+						     GdkEventCrossing   *event,
+						     gpointer            event_data);
+static int     ancestor_motion_notify_event         (GtkWidget          *widget,
+						     GdkEventMotion     *event,
+						     gpointer            event_data);
+static int     ancestor_button_press_event          (GtkWidget          *widget,
+						     GdkEventButton     *event,
+						     gpointer            event_data);
+static int     ancestor_button_release_event        (GtkWidget          *widget,
+						     GdkEventButton     *event,
+						     gpointer            event_data);
+
+EEL_CLASS_BOILERPLATE (EelImageTable, eel_image_table, EEL_TYPE_WRAP_TABLE)
+
+static void
+eel_image_table_class_init (EelImageTableClass *image_table_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (image_table_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (image_table_class);
+	GtkContainerClass *container_class = GTK_CONTAINER_CLASS (image_table_class);
+
+	/* GObjectClass */
+	object_class->finalize = eel_image_table_finalize;
+	
+ 	/* GtkWidgetClass */
+ 	widget_class->realize = eel_image_table_realize;
+ 	widget_class->unrealize = eel_image_table_unrealize;
+
+ 	/* GtkContainerClass */
+	container_class->remove = eel_image_table_remove;
+	container_class->child_type = eel_image_table_child_type;
+
+	/* Signals */
+	image_table_signals[CHILD_ENTER] = g_signal_new ("child_enter",
+							 G_TYPE_FROM_CLASS (object_class),
+							 G_SIGNAL_RUN_LAST,
+							 G_STRUCT_OFFSET (EelImageTableClass, child_enter),
+							 NULL, NULL,
+							 eel_marshal_VOID__OBJECT_POINTER,
+							 G_TYPE_NONE,
+							 2,
+							 GTK_TYPE_WIDGET,
+							 G_TYPE_POINTER);
+	image_table_signals[CHILD_LEAVE] = g_signal_new ("child_leave",
+							 G_TYPE_FROM_CLASS (object_class),
+							 G_SIGNAL_RUN_LAST,
+							 G_STRUCT_OFFSET (EelImageTableClass, child_leave),
+							 NULL, NULL,
+							 eel_marshal_VOID__OBJECT_POINTER,
+							 G_TYPE_NONE,
+							 2,
+							 GTK_TYPE_WIDGET,
+							 G_TYPE_POINTER);
+	image_table_signals[CHILD_PRESSED] = g_signal_new ("child_pressed",
+							   G_TYPE_FROM_CLASS (object_class),
+							   G_SIGNAL_RUN_LAST,
+							   G_STRUCT_OFFSET (EelImageTableClass, child_pressed),
+							   NULL, NULL,
+							   eel_marshal_VOID__OBJECT_POINTER,
+							   G_TYPE_NONE,
+							   2,
+							   GTK_TYPE_WIDGET,
+							   G_TYPE_POINTER);
+	image_table_signals[CHILD_RELEASED] = g_signal_new ("child_released",
+							    G_TYPE_FROM_CLASS (object_class),
+							    G_SIGNAL_RUN_LAST,
+							    G_STRUCT_OFFSET (EelImageTableClass, child_released),
+							    NULL, NULL,
+							    eel_marshal_VOID__OBJECT_POINTER,
+							    G_TYPE_NONE,
+							    2,
+							    GTK_TYPE_WIDGET,
+							    G_TYPE_POINTER);
+	image_table_signals[CHILD_CLICKED] = g_signal_new ("child_clicked",
+							   G_TYPE_FROM_CLASS (object_class),
+							   G_SIGNAL_RUN_LAST,
+							   G_STRUCT_OFFSET (EelImageTableClass, child_clicked),
+							   NULL, NULL,
+							   eel_marshal_VOID__OBJECT_POINTER,
+							   G_TYPE_NONE,
+							   2,
+							   GTK_TYPE_WIDGET,
+							   G_TYPE_POINTER);
+}
+
+static void
+eel_image_table_init (EelImageTable *image_table)
+{
+	GTK_WIDGET_SET_FLAGS (image_table, GTK_NO_WINDOW);
+
+	image_table->details = g_new0 (EelImageTableDetails, 1);
+}
+
+/* GObjectClass methods */
+static void
+eel_image_table_finalize (GObject *object)
+{
+ 	EelImageTable *image_table;
+	
+	image_table = EEL_IMAGE_TABLE (object);
+
+	g_free (image_table->details);
+
+	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static void
+eel_image_table_realize (GtkWidget *widget)
+{
+	GtkWidget *windowed_ancestor;
+
+	g_assert (EEL_IS_IMAGE_TABLE (widget));
+	
+	/* Chain realize */
+	EEL_CALL_PARENT (GTK_WIDGET_CLASS, realize, (widget));
+
+	windowed_ancestor = eel_gtk_widget_find_windowed_ancestor (widget);
+	g_assert (GTK_IS_WIDGET (windowed_ancestor));
+	
+	gtk_widget_add_events (windowed_ancestor,
+			       GDK_BUTTON_PRESS_MASK
+			       | GDK_BUTTON_RELEASE_MASK
+			       | GDK_BUTTON_MOTION_MASK
+			       | GDK_ENTER_NOTIFY_MASK
+			       | GDK_LEAVE_NOTIFY_MASK
+			       | GDK_POINTER_MOTION_MASK);
+
+	eel_gtk_signal_connect_while_realized (GTK_OBJECT (windowed_ancestor),
+					       "enter_notify_event",
+					       G_CALLBACK (ancestor_enter_notify_event),
+					       widget,
+					       widget);
+	
+	eel_gtk_signal_connect_while_realized (GTK_OBJECT (windowed_ancestor),
+					       "leave_notify_event",
+					       G_CALLBACK (ancestor_leave_notify_event),
+					       widget,
+					       widget);
+	
+	eel_gtk_signal_connect_while_realized (GTK_OBJECT (windowed_ancestor),
+					       "motion_notify_event",
+					       G_CALLBACK (ancestor_motion_notify_event),
+					       widget,
+					       widget);
+	
+	eel_gtk_signal_connect_while_realized (GTK_OBJECT (windowed_ancestor),
+					       "button_press_event",
+					       G_CALLBACK (ancestor_button_press_event),
+					       widget,
+					       widget);
+	
+	eel_gtk_signal_connect_while_realized (GTK_OBJECT (windowed_ancestor),
+					       "button_release_event",
+					       G_CALLBACK (ancestor_button_release_event),
+					       widget,
+					       widget);
+}
+
+static void
+eel_image_table_unrealize (GtkWidget *widget)
+{
+	EelImageTable *image_table;
+
+	g_assert (EEL_IS_IMAGE_TABLE (widget));
+
+	image_table = EEL_IMAGE_TABLE (widget);
+
+	if (image_table->details->clear_gc != NULL) {
+		g_object_unref (image_table->details->clear_gc);
+		image_table->details->clear_gc = NULL;
+	}
+
+	/* Chain unrealize */
+	EEL_CALL_PARENT (GTK_WIDGET_CLASS, unrealize, (widget));
+}
+
+/* GtkContainerClass methods */
+static void
+eel_image_table_remove (GtkContainer *container,
+			GtkWidget *child)
+{
+	EelImageTable *image_table;
+	
+	g_assert (EEL_IS_IMAGE_TABLE (container));
+	g_assert (EEL_IS_LABELED_IMAGE (child));
+	
+	image_table = EEL_IMAGE_TABLE (container);
+
+	if (child == image_table->details->child_under_pointer) {
+		image_table->details->child_under_pointer = NULL;
+	}
+
+	if (child == image_table->details->child_being_pressed) {
+		image_table->details->child_being_pressed = NULL;
+	}
+
+	EEL_CALL_PARENT (GTK_CONTAINER_CLASS, remove, (container, child));
+}
+
+static GtkType
+eel_image_table_child_type (GtkContainer *container)
+{
+	return EEL_TYPE_LABELED_IMAGE;
+}
+
+/* Private EelImageTable methods */
+
+static void
+image_table_emit_signal (EelImageTable *image_table,
+			 GtkWidget *child,
+			 guint signal_index,
+			 int x,
+			 int y,
+			 int button,
+			 guint state,
+			 GdkEvent *gdk_event)
+{
+	EelImageTableEvent event;
+
+	g_assert (EEL_IS_IMAGE_TABLE (image_table));
+	g_assert (GTK_IS_WIDGET (child));
+	g_assert (signal_index < LAST_SIGNAL);
+
+	event.x = x;
+	event.y = y;
+	event.button = button;
+	event.state = state;
+	event.event = gdk_event;
+	
+	g_signal_emit (image_table, 
+		       image_table_signals[signal_index],
+		       0,
+		       child,
+		       &event);
+}
+
+static void
+image_table_handle_motion (EelImageTable *image_table,
+			   int x,
+			   int y,
+			   GdkEvent *event)
+{
+	GtkWidget *child;
+	GtkWidget *leave_emit_child = NULL;
+	GtkWidget *enter_emit_child = NULL;
+
+	g_assert (EEL_IS_IMAGE_TABLE (image_table));
+
+	child = eel_wrap_table_find_child_at_event_point (EEL_WRAP_TABLE (image_table), x, y);
+
+	if (child && !GTK_WIDGET_SENSITIVE (child)) {
+		return;
+	}
+
+	if (child == image_table->details->child_under_pointer) {
+		return;
+	}
+
+	if (child != NULL) {
+		if (image_table->details->child_under_pointer != NULL) {
+			leave_emit_child = image_table->details->child_under_pointer;
+		}
+
+		image_table->details->child_under_pointer = child;
+		enter_emit_child = image_table->details->child_under_pointer;
+	} else {
+		if (image_table->details->child_under_pointer != NULL) {
+			leave_emit_child = image_table->details->child_under_pointer;
+		}
+
+		image_table->details->child_under_pointer = NULL;
+	}
+
+	if (leave_emit_child != NULL) {
+		image_table_emit_signal (image_table,
+					 leave_emit_child,
+					 CHILD_LEAVE,
+					 x,
+					 y,
+					 0,
+					 0,
+					 (GdkEvent *)event);
+	}
+
+	if (enter_emit_child != NULL) {
+		image_table_emit_signal (image_table,
+					 enter_emit_child,
+					 CHILD_ENTER,
+					 x,
+					 y,
+					 0,
+					 0,
+					 (GdkEvent *)event);
+	}
+}
+
+static int
+ancestor_enter_notify_event (GtkWidget *widget,
+			     GdkEventCrossing *event,
+			     gpointer event_data)
+{
+	g_assert (GTK_IS_WIDGET (widget));
+	g_assert (EEL_IS_IMAGE_TABLE (event_data));
+	g_assert (event != NULL);
+
+	image_table_handle_motion (EEL_IMAGE_TABLE (event_data), event->x, event->y, (GdkEvent *) event);
+
+	return FALSE;
+}
+
+static int
+ancestor_leave_notify_event (GtkWidget *widget,
+			     GdkEventCrossing *event,
+			     gpointer event_data)
+{
+	EelIRect bounds;
+	int x = -1;
+	int y = -1;
+	
+	g_assert (GTK_IS_WIDGET (widget));
+	g_assert (EEL_IS_IMAGE_TABLE (event_data));
+	g_assert (event != NULL);
+
+	bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (event_data));
+	
+	if (eel_irect_contains_point (bounds, event->x, event->y)) {
+		x = event->x;
+		y = event->y;
+	}
+
+	image_table_handle_motion (EEL_IMAGE_TABLE (event_data), x, y, (GdkEvent *) event);
+	
+	return FALSE;
+}
+
+static int
+ancestor_motion_notify_event (GtkWidget *widget,
+			      GdkEventMotion *event,
+			      gpointer event_data)
+{
+	g_assert (GTK_IS_WIDGET (widget));
+	g_assert (EEL_IS_IMAGE_TABLE (event_data));
+	g_assert (event != NULL);
+
+	image_table_handle_motion (EEL_IMAGE_TABLE (event_data), (int) event->x, (int) event->y, (GdkEvent *) event);
+
+	return FALSE;
+}
+
+static int
+ancestor_button_press_event (GtkWidget *widget,
+			     GdkEventButton *event,
+			     gpointer event_data)
+{
+  	EelImageTable *image_table;
+	GtkWidget *child;
+
+	g_assert (GTK_IS_WIDGET (widget));
+	g_assert (EEL_IS_IMAGE_TABLE (event_data));
+	g_assert (event != NULL);
+
+ 	image_table = EEL_IMAGE_TABLE (event_data);
+
+	child = eel_wrap_table_find_child_at_event_point (EEL_WRAP_TABLE (image_table), event->x, event->y);
+
+	if (child && !GTK_WIDGET_SENSITIVE (child)) {
+		return FALSE;
+	}
+
+	if (child != NULL) {
+		if (child == image_table->details->child_under_pointer) {
+			image_table->details->child_being_pressed = child;
+			image_table_emit_signal (image_table,
+						 child,
+						 CHILD_PRESSED,
+						 event->x,
+						 event->y,
+						 event->button,
+						 event->state,
+						 (GdkEvent *)event);
+		}
+	}
+
+	return FALSE;
+}
+
+static int
+ancestor_button_release_event (GtkWidget *widget,
+			       GdkEventButton *event,
+			       gpointer event_data)
+{
+  	EelImageTable *image_table;
+	GtkWidget *child;
+	GtkWidget *released_emit_child = NULL;
+	GtkWidget *clicked_emit_child = NULL;
+
+	g_assert (GTK_IS_WIDGET (widget));
+	g_assert (EEL_IS_IMAGE_TABLE (event_data));
+	g_assert (event != NULL);
+
+ 	image_table = EEL_IMAGE_TABLE (event_data);
+
+	child = eel_wrap_table_find_child_at_event_point (EEL_WRAP_TABLE (image_table), event->x, event->y);
+
+	if (child && !GTK_WIDGET_SENSITIVE (child)) {
+		return FALSE;
+	}
+
+	if (image_table->details->child_being_pressed != NULL) {
+		released_emit_child = image_table->details->child_being_pressed;
+	}
+
+	if (child != NULL) {
+		if (child == image_table->details->child_being_pressed) {
+			clicked_emit_child = child;
+		}
+	}
+	
+	image_table->details->child_being_pressed = NULL;
+
+	if (released_emit_child != NULL) {
+		image_table_emit_signal (image_table,
+					 released_emit_child,
+					 CHILD_RELEASED,
+					 event->x,
+					 event->y,
+					 event->button,
+					 event->state,
+					 (GdkEvent *)event);
+	}
+	
+	if (clicked_emit_child != NULL) {
+
+		image_table_emit_signal (image_table,
+					 clicked_emit_child,
+					 CHILD_CLICKED,
+					 event->x,
+					 event->y,
+					 event->button,
+					 event->state,
+					 (GdkEvent *)event);
+	}
+	
+	return FALSE;
+}
+
+/**
+ * eel_image_table_new:
+ */
+GtkWidget*
+eel_image_table_new (gboolean homogeneous)
+{
+	EelImageTable *image_table;
+
+	image_table = EEL_IMAGE_TABLE (gtk_widget_new (eel_image_table_get_type (), NULL));
+
+	eel_wrap_table_set_homogeneous (EEL_WRAP_TABLE (image_table), homogeneous);
+	
+	return GTK_WIDGET (image_table);
+}
+
+/**
+ * eel_image_table_add_empty_child:
+ * @image_table: A EelImageTable.
+ *
+ * Add a "empty" child to the table.  Useful when you want to have
+ * empty space between 2 children.
+ *
+ * Returns: The empty child - A EelLabeledImage widget with no label
+ *          or pixbuf.
+ */
+GtkWidget *
+eel_image_table_add_empty_image (EelImageTable *image_table)
+{
+	GtkWidget *empty;
+
+	g_return_val_if_fail (EEL_IS_IMAGE_TABLE (image_table), NULL);
+
+	empty = eel_labeled_image_new (NULL, NULL);
+	gtk_container_add (GTK_CONTAINER (image_table), empty);
+	gtk_widget_set_sensitive (empty, FALSE);
+
+	return empty;
+}

Added: trunk/eel/eel-image-table.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-image-table.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,89 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-image-table.h - An image table.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_IMAGE_TABLE_H
+#define EEL_IMAGE_TABLE_H
+
+#include <eel/eel-wrap-table.h>
+
+G_BEGIN_DECLS
+
+#define EEL_TYPE_IMAGE_TABLE            (eel_image_table_get_type ())
+#define EEL_IMAGE_TABLE(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_IMAGE_TABLE, EelImageTable))
+#define EEL_IMAGE_TABLE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_IMAGE_TABLE, EelImageTableClass))
+#define EEL_IS_IMAGE_TABLE(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_IMAGE_TABLE))
+#define EEL_IS_IMAGE_TABLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_IMAGE_TABLE))
+
+typedef struct EelImageTable		EelImageTable;
+typedef struct EelImageTableClass	EelImageTableClass;
+typedef struct EelImageTableDetails	EelImageTableDetails;
+
+typedef struct
+{
+	int x;
+	int y;
+	int button;
+	guint state;
+	GdkEvent *event;
+} EelImageTableEvent;
+
+struct EelImageTable
+{
+	/* Superclass */
+	EelWrapTable wrap_table;
+
+	/* Private things */
+	EelImageTableDetails *details;
+};
+
+struct EelImageTableClass
+{
+	EelWrapTableClass parent_class;
+
+	/* Signals */
+	void (* child_enter) (EelImageTable *image_table,
+			      GtkWidget *child,
+			      const EelImageTableEvent *event);
+	void (* child_leave) (EelImageTable *image_table,
+			      GtkWidget *child,
+			      const EelImageTableEvent *event);
+	void (* child_pressed) (EelImageTable *image_table,
+				GtkWidget *child,
+				const EelImageTableEvent *event);
+	void (* child_released) (EelImageTable *image_table,
+				 GtkWidget *child,
+				 const EelImageTableEvent *event);
+	void (* child_clicked) (EelImageTable *image_table,
+				GtkWidget *child,
+				const EelImageTableEvent *event);
+};
+
+/* Public GtkImageTable methods */
+GtkType    eel_image_table_get_type         (void);
+GtkWidget *eel_image_table_new              (gboolean       homogeneous);
+GtkWidget *eel_image_table_add_empty_image  (EelImageTable *image_table);
+
+G_END_DECLS
+
+#endif /* EEL_IMAGE_TABLE_H */

Added: trunk/eel/eel-labeled-image.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-labeled-image.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,2376 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-labeled-image.c - A labeled image.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-labeled-image.h"
+
+#include "eel-art-extensions.h"
+#include "eel-art-gtk-extensions.h"
+#include "eel-debug-drawing.h"
+#include "eel-gtk-container.h"
+#include "eel-gtk-extensions.h"
+#include "eel-gtk-macros.h"
+#include "eel-accessibility.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <atk/atkimage.h>
+
+#define DEFAULT_SPACING 0
+#define DEFAULT_X_PADDING 0
+#define DEFAULT_Y_PADDING 0
+#define DEFAULT_X_ALIGNMENT 0.5
+#define DEFAULT_Y_ALIGNMENT 0.5
+
+/* Signals */
+enum
+{
+	ACTIVATE,
+	LAST_SIGNAL
+};
+
+/* Arguments */
+enum
+{
+	PROP_0,
+	PROP_FILL,
+	PROP_LABEL,
+	PROP_LABEL_POSITION,
+	PROP_PIXBUF,
+	PROP_SHOW_IMAGE,
+	PROP_SHOW_LABEL,
+	PROP_SPACING,
+	PROP_X_ALIGNMENT,
+	PROP_X_PADDING,
+	PROP_Y_ALIGNMENT,
+	PROP_Y_PADDING
+};
+
+/* Detail member struct */
+struct EelLabeledImageDetails
+{
+	GtkWidget *image;
+	GtkWidget *label;
+	GtkPositionType label_position;
+	gboolean show_label;
+	gboolean show_image;
+	guint spacing;
+	float x_alignment;
+	float y_alignment;
+	int x_padding;
+	int y_padding;
+	int fixed_image_height;
+	gboolean fill;
+};
+
+/* derived types so we can add our accessibility interfaces */
+static GType         eel_labeled_image_button_get_type        (void);
+static GType         eel_labeled_image_check_button_get_type  (void);
+static GType         eel_labeled_image_radio_button_get_type  (void);
+static GType         eel_labeled_image_toggle_button_get_type (void);
+
+
+static void          eel_labeled_image_class_init         (EelLabeledImageClass  *labeled_image_class);
+static void          eel_labeled_image_init               (EelLabeledImage       *image);
+static void          eel_labeled_image_finalize           (GObject               *object);
+
+
+
+/* GObjectClass methods */
+static void          eel_labeled_image_set_property       (GObject               *object,
+							   guint                  property_id,
+							   const GValue          *value,
+							   GParamSpec            *pspec);
+static void          eel_labeled_image_get_property       (GObject               *object,
+							   guint                  property_id,
+							   GValue                *value,
+							   GParamSpec            *pspec);
+
+/* GtkObjectClass methods */
+static void          eel_labeled_image_destroy            (GtkObject             *object);
+
+/* GtkWidgetClass methods */
+static void          eel_labeled_image_size_request       (GtkWidget             *widget,
+							   GtkRequisition        *requisition);
+static int           eel_labeled_image_expose_event       (GtkWidget             *widget,
+							   GdkEventExpose        *event);
+static void          eel_labeled_image_size_allocate      (GtkWidget             *widget,
+							   GtkAllocation         *allocation);
+static void          eel_labeled_image_map                (GtkWidget             *widget);
+static void          eel_labeled_image_unmap              (GtkWidget             *widget);
+static AtkObject    *eel_labeled_image_get_accessible     (GtkWidget             *widget);
+
+/* GtkContainerClass methods */
+static void          eel_labeled_image_add                (GtkContainer          *container,
+							   GtkWidget             *widget);
+static void          eel_labeled_image_remove             (GtkContainer          *container,
+							   GtkWidget             *widget);
+static void          eel_labeled_image_forall             (GtkContainer          *container,
+							   gboolean               include_internals,
+							   GtkCallback            callback,
+							   gpointer               callback_data);
+
+/* Private EelLabeledImage methods */
+static EelDimensions labeled_image_get_image_dimensions   (const EelLabeledImage *labeled_image);
+static EelDimensions labeled_image_get_label_dimensions   (const EelLabeledImage *labeled_image);
+static void          labeled_image_ensure_label           (EelLabeledImage       *labeled_image);
+static void          labeled_image_ensure_image           (EelLabeledImage       *labeled_image);
+static EelIRect      labeled_image_get_content_bounds     (const EelLabeledImage *labeled_image);
+static EelDimensions labeled_image_get_content_dimensions (const EelLabeledImage *labeled_image);
+static void          labeled_image_update_alignments      (EelLabeledImage       *labeled_image);
+static gboolean      labeled_image_show_label             (const EelLabeledImage *labeled_image);
+static gboolean      labeled_image_show_image             (const EelLabeledImage *labeled_image);
+
+static guint labeled_image_signals[LAST_SIGNAL] = { 0 };
+
+EEL_CLASS_BOILERPLATE (EelLabeledImage, eel_labeled_image, GTK_TYPE_CONTAINER)
+
+/* Class init methods */
+static void
+eel_labeled_image_class_init (EelLabeledImageClass *labeled_image_class)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (labeled_image_class);
+	GtkObjectClass *object_class = GTK_OBJECT_CLASS (labeled_image_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (labeled_image_class);
+	GtkContainerClass *container_class = GTK_CONTAINER_CLASS (labeled_image_class);
+	GtkBindingSet *binding_set;
+
+	gobject_class->finalize = eel_labeled_image_finalize;
+
+	/* GObjectClass */
+	gobject_class->set_property = eel_labeled_image_set_property;
+	gobject_class->get_property = eel_labeled_image_get_property;
+
+	/* GtkObjectClass */
+	object_class->destroy = eel_labeled_image_destroy;
+
+ 	/* GtkWidgetClass */
+ 	widget_class->size_request = eel_labeled_image_size_request;
+	widget_class->size_allocate = eel_labeled_image_size_allocate;
+ 	widget_class->expose_event = eel_labeled_image_expose_event;
+	widget_class->map = eel_labeled_image_map;
+	widget_class->unmap = eel_labeled_image_unmap;
+	widget_class->get_accessible = eel_labeled_image_get_accessible;
+
+ 	/* GtkContainerClass */
+	container_class->add = eel_labeled_image_add;
+	container_class->remove = eel_labeled_image_remove;
+	container_class->forall = eel_labeled_image_forall;
+
+	labeled_image_signals[ACTIVATE] =
+		g_signal_new ("activate",
+			      G_TYPE_FROM_CLASS (labeled_image_class),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+			      G_STRUCT_OFFSET (EelLabeledImageClass,
+					       activate),
+			      NULL, NULL, 
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE, 0);	
+	widget_class->activate_signal = labeled_image_signals[ACTIVATE];
+
+	binding_set = gtk_binding_set_by_class (gobject_class);
+	
+	gtk_binding_entry_add_signal (binding_set, 
+				      GDK_Return, 0,
+				      "activate", 0);
+	gtk_binding_entry_add_signal (binding_set, 
+				      GDK_KP_Enter, 0,
+				      "activate", 0);
+	gtk_binding_entry_add_signal (binding_set, 
+				      GDK_space, 0,
+				      "activate", 0);
+
+
+	/* Properties */
+        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_LABEL,
+		g_param_spec_string ("label", NULL, NULL,
+				     "", G_PARAM_READWRITE));
+
+
+        g_object_class_install_property (
+		gobject_class,
+		PROP_LABEL_POSITION,
+		g_param_spec_enum ("label_position", NULL, NULL,
+				   GTK_TYPE_POSITION_TYPE,
+				   GTK_POS_BOTTOM,
+				   G_PARAM_READWRITE));
+
+        g_object_class_install_property (
+		gobject_class,
+		PROP_SHOW_LABEL,
+		g_param_spec_boolean ("show_label", NULL, NULL,
+				      TRUE, G_PARAM_READWRITE)); 
+				   
+        g_object_class_install_property (
+		gobject_class,
+		PROP_SHOW_IMAGE,
+		g_param_spec_boolean ("show_image", NULL, NULL,
+				      TRUE, G_PARAM_READWRITE)); 
+				   
+
+	g_object_class_install_property (
+		gobject_class,
+		PROP_SPACING,
+		g_param_spec_uint ("spacing", NULL, NULL,
+				   0,
+				   G_MAXINT,
+				   DEFAULT_SPACING,
+				   G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		gobject_class,
+		PROP_X_PADDING,
+		g_param_spec_int ("x_padding", NULL, NULL,
+				  0,
+				  G_MAXINT,
+				  DEFAULT_X_PADDING,
+				  G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		gobject_class,
+		PROP_Y_PADDING,
+		g_param_spec_int ("y_padding", NULL, NULL,
+				  0,
+				  G_MAXINT,
+				  DEFAULT_Y_PADDING,
+				  G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		gobject_class,
+		PROP_X_ALIGNMENT,
+		g_param_spec_float ("x_alignment", NULL, NULL,
+				    0.0,
+				    1.0,
+				    DEFAULT_X_ALIGNMENT,
+				    G_PARAM_READWRITE));
+   
+	g_object_class_install_property (
+		gobject_class,
+		PROP_Y_ALIGNMENT,
+		g_param_spec_float ("y_alignment", NULL, NULL,
+				    0.0,
+				    1.0,
+				    DEFAULT_Y_ALIGNMENT,
+				    G_PARAM_READWRITE));
+
+	g_object_class_install_property (
+		gobject_class,
+		PROP_FILL,
+		g_param_spec_boolean ("fill", NULL, NULL,
+				      FALSE,
+				      G_PARAM_READWRITE));
+}
+
+static void
+eel_labeled_image_init (EelLabeledImage *labeled_image)
+{
+	GTK_WIDGET_SET_FLAGS (labeled_image, GTK_NO_WINDOW);
+
+	labeled_image->details = g_new0 (EelLabeledImageDetails, 1);
+	labeled_image->details->show_label = TRUE;
+	labeled_image->details->show_image = TRUE;
+	labeled_image->details->label_position = GTK_POS_BOTTOM;
+	labeled_image->details->spacing = DEFAULT_SPACING;
+	labeled_image->details->x_padding = DEFAULT_X_PADDING;
+	labeled_image->details->y_padding = DEFAULT_Y_PADDING;
+	labeled_image->details->x_alignment = DEFAULT_X_ALIGNMENT;
+	labeled_image->details->y_alignment = DEFAULT_Y_ALIGNMENT;
+	labeled_image->details->fixed_image_height = 0;
+
+	eel_labeled_image_set_fill (labeled_image, FALSE);
+}
+
+static void
+eel_labeled_image_finalize (GObject *object)
+{
+ 	EelLabeledImage *labeled_image;
+	
+	labeled_image = EEL_LABELED_IMAGE (object);
+
+	g_free (labeled_image->details);
+
+	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+
+static void
+eel_labeled_image_destroy (GtkObject *object)
+{
+ 	EelLabeledImage *labeled_image;
+	
+	labeled_image = EEL_LABELED_IMAGE (object);
+
+	if (labeled_image->details->image != NULL) {
+		gtk_widget_destroy (labeled_image->details->image);
+	}
+
+	if (labeled_image->details->label != NULL) {
+		gtk_widget_destroy (labeled_image->details->label);
+	}
+
+	EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+/* GObjectClass methods */
+static void
+eel_labeled_image_set_property (GObject      *object,
+				guint         property_id,
+				const GValue *value,
+				GParamSpec   *pspec)
+{
+	EelLabeledImage *labeled_image;
+	
+	g_assert (EEL_IS_LABELED_IMAGE (object));
+
+ 	labeled_image = EEL_LABELED_IMAGE (object);
+
+ 	switch (property_id)
+	{
+	case PROP_PIXBUF:
+		eel_labeled_image_set_pixbuf (labeled_image,
+					      g_value_get_object (value));
+		break;
+
+	case PROP_LABEL:
+		eel_labeled_image_set_text (labeled_image, g_value_get_string (value));
+		break;
+
+	case PROP_LABEL_POSITION:
+		eel_labeled_image_set_label_position (labeled_image,
+						      g_value_get_enum (value));
+		break;
+
+	case PROP_SHOW_LABEL:
+		eel_labeled_image_set_show_label (labeled_image,
+						  g_value_get_boolean (value));
+		break;
+
+	case PROP_SHOW_IMAGE:
+		eel_labeled_image_set_show_image (labeled_image,
+						  g_value_get_boolean (value));
+		break;
+
+	case PROP_SPACING:
+		eel_labeled_image_set_spacing (labeled_image,
+					       g_value_get_uint (value));
+		break;
+
+	case PROP_X_PADDING:
+		eel_labeled_image_set_x_padding (labeled_image,
+						 g_value_get_int (value));
+		break;
+
+	case PROP_Y_PADDING:
+		eel_labeled_image_set_y_padding (labeled_image,
+						 g_value_get_int (value));
+		break;
+
+	case PROP_X_ALIGNMENT:
+		eel_labeled_image_set_x_alignment (labeled_image,
+						   g_value_get_float (value));
+		break;
+
+	case PROP_Y_ALIGNMENT:
+		eel_labeled_image_set_y_alignment (labeled_image,
+						   g_value_get_float (value));
+		break;
+
+	case PROP_FILL:
+		eel_labeled_image_set_fill (labeled_image,
+					    g_value_get_boolean (value));
+		break;
+ 	default:
+		g_assert_not_reached ();
+	}
+}
+
+static void
+eel_labeled_image_get_property (GObject    *object,
+				guint       property_id,
+				GValue     *value,
+				GParamSpec *pspec)
+{
+	EelLabeledImage *labeled_image;
+
+	g_assert (EEL_IS_LABELED_IMAGE (object));
+	
+	labeled_image = EEL_LABELED_IMAGE (object);
+
+ 	switch (property_id)
+	{
+	case PROP_LABEL:
+		if (labeled_image->details->label == NULL) {
+			g_value_set_string (value, NULL);
+		} else {
+			g_value_set_string (value, 
+					    gtk_label_get_text (GTK_LABEL (
+						    labeled_image->details->label)));
+		}
+		break;
+
+	case PROP_LABEL_POSITION:
+		g_value_set_enum (value, eel_labeled_image_get_label_position (labeled_image));
+		break;
+
+	case PROP_SHOW_LABEL:
+		g_value_set_boolean (value, eel_labeled_image_get_show_label (labeled_image));
+		break;
+
+	case PROP_SHOW_IMAGE:
+		g_value_set_boolean (value, eel_labeled_image_get_show_image (labeled_image));
+		break;
+
+	case PROP_SPACING:
+		g_value_set_uint (value, eel_labeled_image_get_spacing (labeled_image));
+		break;
+
+	case PROP_X_PADDING:
+		g_value_set_int (value, eel_labeled_image_get_x_padding (labeled_image));
+		break;
+
+	case PROP_Y_PADDING:
+		g_value_set_int (value, eel_labeled_image_get_y_padding (labeled_image));
+		break;
+
+	case PROP_X_ALIGNMENT:
+		g_value_set_float (value, eel_labeled_image_get_x_alignment (labeled_image));
+		break;
+
+	case PROP_Y_ALIGNMENT:
+		g_value_set_float (value, eel_labeled_image_get_y_alignment (labeled_image));
+		break;
+
+	case PROP_FILL:
+		g_value_set_boolean (value, eel_labeled_image_get_fill (labeled_image));
+		break;
+
+ 	default:
+		g_assert_not_reached ();
+	}
+}
+
+/* GtkWidgetClass methods */
+static void
+eel_labeled_image_size_request (GtkWidget *widget,
+				GtkRequisition *requisition)
+{
+	EelLabeledImage *labeled_image;
+ 	EelDimensions content_dimensions;
+
+ 	g_assert (EEL_IS_LABELED_IMAGE (widget));
+ 	g_assert (requisition != NULL);
+
+  	labeled_image = EEL_LABELED_IMAGE (widget);
+	
+ 	content_dimensions = labeled_image_get_content_dimensions (labeled_image);
+
+	requisition->width = 
+		MAX (1, content_dimensions.width) +
+		2 * labeled_image->details->x_padding;
+
+	requisition->height = 
+		MAX (1, content_dimensions.height) +
+		2 * labeled_image->details->y_padding;
+}
+
+static void
+eel_labeled_image_size_allocate (GtkWidget *widget,
+				 GtkAllocation *allocation)
+{
+	EelLabeledImage *labeled_image;
+ 	EelIRect image_bounds;
+	EelIRect label_bounds;
+
+ 	g_assert (EEL_IS_LABELED_IMAGE (widget));
+ 	g_assert (allocation != NULL);
+
+  	labeled_image = EEL_LABELED_IMAGE (widget);
+
+	widget->allocation = *allocation;
+	
+ 	label_bounds = eel_labeled_image_get_label_bounds (labeled_image);
+	eel_gtk_container_child_size_allocate (GTK_CONTAINER (widget),
+					       labeled_image->details->label,
+					       label_bounds);
+
+ 	image_bounds = eel_labeled_image_get_image_bounds (labeled_image);
+	eel_gtk_container_child_size_allocate (GTK_CONTAINER (widget),
+					       labeled_image->details->image,
+					       image_bounds);
+}
+
+static int
+eel_labeled_image_expose_event (GtkWidget *widget,
+				GdkEventExpose *event)
+{
+	EelLabeledImage *labeled_image;
+	EelIRect label_bounds;
+
+	g_assert (EEL_IS_LABELED_IMAGE (widget));
+	g_assert (GTK_WIDGET_REALIZED (widget));
+	g_assert (event != NULL);
+
+  	labeled_image = EEL_LABELED_IMAGE (widget);
+
+	if (GTK_WIDGET_STATE (widget) == GTK_STATE_SELECTED || 
+	    GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE) {
+		label_bounds = eel_labeled_image_get_label_bounds (EEL_LABELED_IMAGE (widget));
+
+		gtk_paint_flat_box (widget->style,
+				    widget->window,
+				    GTK_WIDGET_STATE (widget),
+				    GTK_SHADOW_NONE,
+				    &event->area,
+				    widget,
+				    "eel-labeled-image",
+				    label_bounds.x0, label_bounds.y0,
+				    label_bounds.x1 - label_bounds.x0, 
+				    label_bounds.y1 - label_bounds.y0);
+	}
+
+	if (labeled_image_show_label (labeled_image)) {
+		eel_gtk_container_child_expose_event (GTK_CONTAINER (widget),
+						      labeled_image->details->label,
+						      event);
+	}
+	
+	if (labeled_image_show_image (labeled_image)) {
+		eel_gtk_container_child_expose_event (GTK_CONTAINER (widget),
+						      labeled_image->details->image,
+						      event);
+	}
+
+	if (GTK_WIDGET_HAS_FOCUS (widget)) {
+		label_bounds = eel_labeled_image_get_image_bounds (EEL_LABELED_IMAGE (widget));
+		gtk_paint_focus (widget->style, widget->window,
+				 GTK_STATE_NORMAL,
+				 &event->area, widget,
+				 "eel-focusable-labeled-image",
+				 label_bounds.x0, label_bounds.y0,
+				 label_bounds.x1 - label_bounds.x0, 
+				 label_bounds.y1 - label_bounds.y0);
+	}		
+
+	return FALSE;
+}
+
+static void
+eel_labeled_image_map (GtkWidget *widget)
+{
+	EelLabeledImage *labeled_image;
+
+	g_assert (EEL_IS_LABELED_IMAGE (widget));
+	
+	labeled_image = EEL_LABELED_IMAGE (widget);
+
+ 	GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+	
+	if (labeled_image_show_label (labeled_image)) {
+		eel_gtk_container_child_map (GTK_CONTAINER (widget), labeled_image->details->label);
+	}
+
+	if (labeled_image_show_image (labeled_image)) {
+		eel_gtk_container_child_map (GTK_CONTAINER (widget), labeled_image->details->image);
+	}
+}
+
+static void
+eel_labeled_image_unmap (GtkWidget *widget)
+{
+	EelLabeledImage *labeled_image;
+
+	g_assert (EEL_IS_LABELED_IMAGE (widget));
+	
+	labeled_image = EEL_LABELED_IMAGE (widget);
+
+	GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
+	
+	eel_gtk_container_child_unmap (GTK_CONTAINER (widget), labeled_image->details->label);
+	eel_gtk_container_child_unmap (GTK_CONTAINER (widget), labeled_image->details->image);
+}
+
+/* GtkContainerClass methods */
+static void
+eel_labeled_image_add (GtkContainer *container,
+		       GtkWidget *child)
+{
+	g_assert (GTK_IS_LABEL (child) || GTK_IS_IMAGE (child));
+
+	eel_gtk_container_child_add (container, child);
+}
+
+static void
+eel_labeled_image_remove (GtkContainer *container,
+			  GtkWidget *child)
+{
+	EelLabeledImage *labeled_image;
+	
+	g_assert (GTK_IS_LABEL (child) || GTK_IS_IMAGE (child));
+	
+	labeled_image = EEL_LABELED_IMAGE (container);;
+
+	g_assert (child == labeled_image->details->image || child == labeled_image->details->label);
+
+	eel_gtk_container_child_remove (container, child);
+
+	if (labeled_image->details->image == child) {
+		labeled_image->details->image = NULL;
+	}
+
+	if (labeled_image->details->label == child) {
+		labeled_image->details->label = NULL;
+	}
+}
+
+static void
+eel_labeled_image_forall (GtkContainer *container,
+			  gboolean include_internals,
+			  GtkCallback callback,
+			  gpointer callback_data)
+{
+	EelLabeledImage *labeled_image;
+	
+	g_assert (EEL_IS_LABELED_IMAGE (container));
+	g_assert (callback != NULL);
+	
+	labeled_image = EEL_LABELED_IMAGE (container);
+
+	if (include_internals) {
+		if (labeled_image->details->image != NULL) {
+			(* callback) (labeled_image->details->image, callback_data);
+		}
+		
+		if (labeled_image->details->label != NULL) {
+			(* callback) (labeled_image->details->label, callback_data);
+		}
+	}
+}
+
+/* Private EelLabeledImage methods */
+static gboolean
+is_fixed_height (const EelLabeledImage *labeled_image)
+{
+	return labeled_image->details->fixed_image_height > 0;
+}
+
+static EelDimensions
+labeled_image_get_image_dimensions (const EelLabeledImage *labeled_image)
+{
+	EelDimensions image_dimensions;
+	GtkRequisition image_requisition;	
+
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	if (!labeled_image_show_image (labeled_image)) {
+		return eel_dimensions_empty;
+	}
+	
+	gtk_widget_size_request (labeled_image->details->image, &image_requisition);
+
+	image_dimensions.width = (int) image_requisition.width;
+	image_dimensions.height = (int) image_requisition.height;
+
+	if (is_fixed_height (labeled_image)) {
+		image_dimensions.height = labeled_image->details->fixed_image_height;
+	}
+
+	return image_dimensions;
+}
+
+static EelDimensions
+labeled_image_get_label_dimensions (const EelLabeledImage *labeled_image)
+{
+	EelDimensions label_dimensions;
+	GtkRequisition label_requisition;	
+
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	if (!labeled_image_show_label (labeled_image)) {
+		return eel_dimensions_empty;
+	}
+	
+	gtk_widget_size_request (labeled_image->details->label, &label_requisition);
+	
+	label_dimensions.width = (int) label_requisition.width;
+	label_dimensions.height = (int) label_requisition.height;
+
+	return label_dimensions;
+}
+
+static EelIRect
+labeled_image_get_image_bounds_fill (const EelLabeledImage *labeled_image)
+{
+	EelIRect image_bounds;
+	EelDimensions image_dimensions;
+	EelIRect content_bounds;
+	EelIRect bounds;
+
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	image_dimensions = labeled_image_get_image_dimensions (labeled_image);
+
+	if (eel_dimensions_are_empty (image_dimensions)) {
+		return eel_irect_empty;
+	}
+
+	content_bounds = labeled_image_get_content_bounds (labeled_image);
+	bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (labeled_image));
+	
+	if (!labeled_image_show_label (labeled_image)) {
+		image_bounds = bounds;
+	} else {
+		switch (labeled_image->details->label_position) {
+		case GTK_POS_LEFT:
+			image_bounds.y0 = bounds.y0;
+			image_bounds.x0 = content_bounds.x1 - image_dimensions.width;
+			image_bounds.y1 = bounds.y1;
+			image_bounds.x1 = bounds.x1;
+			break;
+
+		case GTK_POS_RIGHT:
+			image_bounds.y0 = bounds.y0;
+			image_bounds.x0 = bounds.x0;
+			image_bounds.y1 = bounds.y1;
+			image_bounds.x1 = content_bounds.x0 + image_dimensions.width;
+			break;
+
+		case GTK_POS_TOP:
+			image_bounds.x0 = bounds.x0;
+			image_bounds.y0 = content_bounds.y1 - image_dimensions.height;
+			image_bounds.x1 = bounds.x1;
+			image_bounds.y1 = bounds.y1;
+			break;
+
+		case GTK_POS_BOTTOM:
+			image_bounds.x0 = bounds.x0;
+			image_bounds.y0 = bounds.y0;
+			image_bounds.x1 = bounds.x1;
+			image_bounds.y1 = content_bounds.y0 + image_dimensions.height;
+			break;
+
+		default:
+			image_bounds.x0 = 0;
+			image_bounds.y0 = 0;
+			image_bounds.x1 = 0;
+			image_bounds.y1 = 0;
+			g_assert_not_reached ();
+		}
+	}
+
+	return image_bounds;
+}
+
+EelIRect
+eel_labeled_image_get_image_bounds (const EelLabeledImage *labeled_image)
+{
+	EelDimensions image_dimensions;
+	EelDimensions label_dimensions;
+	GtkRequisition image_requisition;
+	EelIRect image_bounds;
+	EelIRect content_bounds;
+
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), eel_irect_empty);
+
+	if (labeled_image->details->fill) {
+		return labeled_image_get_image_bounds_fill (labeled_image);
+	}
+
+	/* get true real dimensions if we're in fixed height mode */
+	if (is_fixed_height (labeled_image) && labeled_image_show_image (labeled_image)) {
+		gtk_widget_size_request (labeled_image->details->image, &image_requisition);
+		image_dimensions.width = (int) image_requisition.width;
+		image_dimensions.height = (int) image_requisition.height;
+	} else {
+		image_dimensions = labeled_image_get_image_dimensions (labeled_image);
+	}
+	
+	label_dimensions = labeled_image_get_label_dimensions (labeled_image);
+
+	if (eel_dimensions_are_empty (image_dimensions)) {
+		return eel_irect_empty;
+	}
+
+	content_bounds = labeled_image_get_content_bounds (labeled_image);
+	
+	if (!labeled_image_show_label (labeled_image)) {
+		image_bounds.x0 = 
+			content_bounds.x0 +
+			(eel_irect_get_width (content_bounds) - image_dimensions.width) / 2;
+		image_bounds.y0 = 
+			content_bounds.y0 +
+			(eel_irect_get_height (content_bounds) - image_dimensions.height) / 2;
+	} else {
+		switch (labeled_image->details->label_position) {
+		case GTK_POS_LEFT:
+			image_bounds.x0 = content_bounds.x1 - image_dimensions.width;
+			image_bounds.y0 = 
+				content_bounds.y0 +
+				(eel_irect_get_height (content_bounds) - image_dimensions.height) / 2;
+			break;
+
+		case GTK_POS_RIGHT:
+			image_bounds.x0 = content_bounds.x0;
+			image_bounds.y0 = 
+				content_bounds.y0 +
+				(eel_irect_get_height (content_bounds) - image_dimensions.height) / 2;
+			break;
+
+		case GTK_POS_TOP:
+			image_bounds.x0 = 
+				content_bounds.x0 +
+				(eel_irect_get_width (content_bounds) - image_dimensions.width) / 2;
+			image_bounds.y0 = content_bounds.y1 - image_dimensions.height;
+			break;
+
+		case GTK_POS_BOTTOM:
+			image_bounds.x0 = 
+				content_bounds.x0 +
+				(eel_irect_get_width (content_bounds) - image_dimensions.width) / 2;
+				
+			if (is_fixed_height (labeled_image)) {	
+				image_bounds.y0 = content_bounds.y0 + eel_irect_get_height (content_bounds)
+					- image_dimensions.height
+					- label_dimensions.height
+					- labeled_image->details->spacing;
+			} else {
+				image_bounds.y0 = content_bounds.y0;
+			}	
+
+			break;
+
+		default:
+			image_bounds.x0 = 0;
+			image_bounds.y0 = 0;
+			g_assert_not_reached ();
+		}
+	}
+	
+	image_bounds.x1 = image_bounds.x0 + image_dimensions.width;
+	image_bounds.y1 = image_bounds.y0 + image_dimensions.height;
+
+	return image_bounds;
+}
+
+static EelIRect
+labeled_image_get_label_bounds_fill (const EelLabeledImage *labeled_image)
+{
+	EelIRect label_bounds;
+	EelDimensions label_dimensions;
+	EelIRect content_bounds;
+	EelIRect bounds;
+
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	label_dimensions = labeled_image_get_label_dimensions (labeled_image);
+
+	if (eel_dimensions_are_empty (label_dimensions)) {
+		return eel_irect_empty;
+	}
+
+	content_bounds = labeled_image_get_content_bounds (labeled_image);
+	bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (labeled_image));
+
+	/* Only the label is shown */
+	if (!labeled_image_show_image (labeled_image)) {
+		label_bounds = bounds;
+		/* Both label and image are shown */
+	} else {
+		switch (labeled_image->details->label_position) {
+		case GTK_POS_LEFT:
+			label_bounds.y0 = bounds.y0;
+			label_bounds.x0 = bounds.x0;
+			label_bounds.y1 = bounds.y1;
+			label_bounds.x1 = content_bounds.x0 + label_dimensions.width;
+			break;
+
+		case GTK_POS_RIGHT:
+			label_bounds.y0 = bounds.y0;
+			label_bounds.x0 = content_bounds.x1 - label_dimensions.width;
+			label_bounds.y1 = bounds.y1;
+			label_bounds.x1 = bounds.x1;
+			break;
+
+		case GTK_POS_TOP:
+			label_bounds.x0 = bounds.x0;
+			label_bounds.y0 = bounds.y0;
+			label_bounds.x1 = bounds.x1;
+			label_bounds.y1 = content_bounds.y0 + label_dimensions.height;
+			break;
+
+		case GTK_POS_BOTTOM:
+			label_bounds.x0 = bounds.x0;
+			label_bounds.y0 = content_bounds.y1 - label_dimensions.height;
+			label_bounds.x1 = bounds.x1;
+			label_bounds.y1 = bounds.y1;
+			break;
+
+		default:
+			label_bounds.x0 = 0;
+			label_bounds.y0 = 0;
+			label_bounds.x1 = 0;
+			label_bounds.y1 = 0;
+			g_assert_not_reached ();
+		}
+	}
+
+	return label_bounds;
+}
+
+EelIRect
+eel_labeled_image_get_label_bounds (const EelLabeledImage *labeled_image)
+{
+	EelIRect label_bounds;
+	EelDimensions label_dimensions;
+	EelIRect content_bounds;
+
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), eel_irect_empty);
+
+	if (labeled_image->details->fill) {
+		return labeled_image_get_label_bounds_fill (labeled_image);
+	}
+
+	label_dimensions = labeled_image_get_label_dimensions (labeled_image);
+
+	if (eel_dimensions_are_empty (label_dimensions)) {
+		return eel_irect_empty;
+	}
+
+	content_bounds = labeled_image_get_content_bounds (labeled_image);
+
+	/* Only the label is shown */
+	if (!labeled_image_show_image (labeled_image)) {
+		label_bounds.x0 = 
+			content_bounds.x0 +
+			(eel_irect_get_width (content_bounds) - label_dimensions.width) / 2;
+		label_bounds.y0 = 
+			content_bounds.y0 +
+			(eel_irect_get_height (content_bounds) - label_dimensions.height) / 2;
+		/* Both label and image are shown */
+	} else {
+		switch (labeled_image->details->label_position) {
+		case GTK_POS_LEFT:
+			label_bounds.x0 = content_bounds.x0;
+			label_bounds.y0 = 
+				content_bounds.y0 +
+				(eel_irect_get_height (content_bounds) - label_dimensions.height) / 2;
+			break;
+
+		case GTK_POS_RIGHT:
+			label_bounds.x0 = content_bounds.x1 - label_dimensions.width;
+			label_bounds.y0 = 
+				content_bounds.y0 +
+				(eel_irect_get_height (content_bounds) - label_dimensions.height) / 2;
+			break;
+
+		case GTK_POS_TOP:
+			label_bounds.x0 = 
+				content_bounds.x0 +
+				(eel_irect_get_width (content_bounds) - label_dimensions.width) / 2;
+			label_bounds.y0 = content_bounds.y0;
+			break;
+
+		case GTK_POS_BOTTOM:
+			label_bounds.x0 = 
+				content_bounds.x0 +
+				(eel_irect_get_width (content_bounds) - label_dimensions.width) / 2;
+			label_bounds.y0 = content_bounds.y1 - label_dimensions.height;
+			break;
+
+		default:
+			label_bounds.x0 = 0;
+			label_bounds.y0 = 0;
+			g_assert_not_reached ();
+		}
+	}
+	
+	label_bounds.x1 = label_bounds.x0 + label_dimensions.width;
+	label_bounds.y1 = label_bounds.y0 + label_dimensions.height;
+
+	return label_bounds;
+}
+
+static void
+labeled_image_update_alignments (EelLabeledImage *labeled_image)
+{
+
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	if (labeled_image->details->label != NULL) {
+		float x_alignment;
+		float y_alignment;
+		
+		if (labeled_image->details->fill) {	
+			x_alignment = GTK_MISC (labeled_image->details->label)->xalign;
+			y_alignment = GTK_MISC (labeled_image->details->label)->yalign;
+			
+			/* Only the label is shown */
+			if (!labeled_image_show_image (labeled_image)) {
+				x_alignment = 0.5;
+				y_alignment = 0.5;
+				/* Both label and image are shown */
+			} else {
+				switch (labeled_image->details->label_position) {
+				case GTK_POS_LEFT:
+					x_alignment = 1.0;
+					y_alignment = 0.5;
+					break;
+					
+				case GTK_POS_RIGHT:
+					x_alignment = 0.0;
+					y_alignment = 0.5;
+					break;
+					
+				case GTK_POS_TOP:
+					x_alignment = 0.5;
+					y_alignment = 1.0;
+					break;
+					
+				case GTK_POS_BOTTOM:
+					x_alignment = 0.5;
+					y_alignment = 0.0;
+					break;
+				}
+				
+			}
+
+			gtk_misc_set_alignment (GTK_MISC (labeled_image->details->label),
+						x_alignment,
+						y_alignment);
+		}
+	}
+
+	if (labeled_image->details->image != NULL) {
+		float x_alignment;
+		float y_alignment;
+		
+		if (labeled_image->details->fill) {	
+			x_alignment = GTK_MISC (labeled_image->details->image)->xalign;
+			y_alignment = GTK_MISC (labeled_image->details->image)->yalign;
+			
+			/* Only the image is shown */
+			if (!labeled_image_show_label (labeled_image)) {
+				x_alignment = 0.5;
+				y_alignment = 0.5;
+				/* Both label and image are shown */
+			} else {
+				switch (labeled_image->details->label_position) {
+				case GTK_POS_LEFT:
+					x_alignment = 0.0;
+					y_alignment = 0.5;
+					break;
+
+				case GTK_POS_RIGHT:
+					x_alignment = 1.0;
+					y_alignment = 0.5;
+					break;
+					
+				case GTK_POS_TOP:
+					x_alignment = 0.5;
+					y_alignment = 0.0;
+					break;
+					
+				case GTK_POS_BOTTOM:
+					x_alignment = 0.5;
+					y_alignment = 1.0;
+					break;
+				}
+			}
+			
+			gtk_misc_set_alignment (GTK_MISC (labeled_image->details->image),
+						x_alignment,
+						y_alignment);
+		}
+	}
+}
+
+static EelDimensions
+labeled_image_get_content_dimensions (const EelLabeledImage *labeled_image)
+{
+	EelDimensions image_dimensions;
+	EelDimensions label_dimensions;
+	EelDimensions content_dimensions;
+
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	image_dimensions = labeled_image_get_image_dimensions (labeled_image);
+	label_dimensions = labeled_image_get_label_dimensions (labeled_image);
+
+	content_dimensions = eel_dimensions_empty;
+
+	/* Both shown */
+	if (!eel_dimensions_are_empty (image_dimensions) && !eel_dimensions_are_empty (label_dimensions)) {
+		content_dimensions.width = 
+			image_dimensions.width + labeled_image->details->spacing + label_dimensions.width;
+		switch (labeled_image->details->label_position) {
+		case GTK_POS_LEFT:
+		case GTK_POS_RIGHT:
+			content_dimensions.width = 
+				image_dimensions.width + labeled_image->details->spacing + label_dimensions.width;
+			content_dimensions.height = MAX (image_dimensions.height, label_dimensions.height);
+			break;
+
+		case GTK_POS_TOP:
+		case GTK_POS_BOTTOM:
+			content_dimensions.width = MAX (image_dimensions.width, label_dimensions.width);
+			content_dimensions.height = 
+				image_dimensions.height + labeled_image->details->spacing + label_dimensions.height;
+			break;
+		}
+		/* Only image shown */
+	} else if (!eel_dimensions_are_empty (image_dimensions)) {
+		content_dimensions.width = image_dimensions.width;
+		content_dimensions.height = image_dimensions.height;
+		/* Only label shown */
+	} else {
+		content_dimensions.width = label_dimensions.width;
+		content_dimensions.height = label_dimensions.height;
+	}
+
+	return content_dimensions;
+}
+
+static EelIRect
+labeled_image_get_content_bounds (const EelLabeledImage *labeled_image)
+{
+	EelDimensions content_dimensions;
+	EelIRect content_bounds;
+	EelIRect bounds;
+
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (labeled_image));
+
+	content_dimensions = labeled_image_get_content_dimensions (labeled_image);
+	content_bounds = eel_irect_align (bounds,
+					      content_dimensions.width,
+					      content_dimensions.height,
+					      labeled_image->details->x_alignment,
+					      labeled_image->details->y_alignment);
+
+	return content_bounds;
+}
+
+static void
+labeled_image_ensure_label (EelLabeledImage *labeled_image)
+{
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	if (labeled_image->details->label != NULL) {
+		return;
+	}
+
+ 	labeled_image->details->label = gtk_label_new (NULL);
+	gtk_container_add (GTK_CONTAINER (labeled_image), labeled_image->details->label);
+	gtk_widget_show (labeled_image->details->label);
+}
+
+static void
+labeled_image_ensure_image (EelLabeledImage *labeled_image)
+{
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	if (labeled_image->details->image != NULL) {
+		return;
+	}
+
+ 	labeled_image->details->image = gtk_image_new ();
+	gtk_container_add (GTK_CONTAINER (labeled_image), labeled_image->details->image);
+	gtk_widget_show (labeled_image->details->image);
+}
+
+static gboolean
+labeled_image_show_image (const EelLabeledImage *labeled_image)
+{
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	return labeled_image->details->image != NULL && labeled_image->details->show_image;
+}
+
+static gboolean
+labeled_image_show_label (const EelLabeledImage *labeled_image)
+{
+	g_assert (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	return labeled_image->details->label != NULL && labeled_image->details->show_label;
+}
+
+/**
+ * eel_labeled_image_new:
+ * @text: Text to use for label or NULL.
+ * @pixbuf: Pixbuf to use for image or NULL.
+ *
+ * Returns A newly allocated EelLabeledImage.  If the &text parameter is not
+ * NULL then the LabeledImage will show a label.  If the &pixbuf parameter is not
+ * NULL then the LabeledImage will show a pixbuf.  Either of these can be NULL at
+ * creation time.  
+ *
+ * Later in the lifetime of the widget you can invoke methods that affect the 
+ * label and/or the image.  If at creation time these were NULL, then they will
+ * be created as neeeded.
+ *
+ * Thus, using this widget in place of EelImage or EelLabel is "free" with
+ * only the GtkObject and function call overhead.
+ *
+ */
+GtkWidget*
+eel_labeled_image_new (const char *text,
+		       GdkPixbuf *pixbuf)
+{
+	EelLabeledImage *labeled_image;
+
+	labeled_image = EEL_LABELED_IMAGE (gtk_widget_new (eel_labeled_image_get_type (), NULL));
+	
+	if (text != NULL) {
+		eel_labeled_image_set_text (labeled_image, text);
+	}
+
+	if (pixbuf != NULL) {
+		eel_labeled_image_set_pixbuf (labeled_image, pixbuf);
+	}
+
+	labeled_image_update_alignments (labeled_image);
+
+	return GTK_WIDGET (labeled_image);
+}
+
+/**
+ * eel_labeled_image_new_from_file_name:
+ * @text: Text to use for label or NULL.
+ * @file_name: File name of picture to use for pixbuf.  Cannot be NULL.
+ *
+ * Returns A newly allocated EelLabeledImage.  If the &text parameter is not
+ * NULL then the LabeledImage will show a label.
+ *
+ */
+GtkWidget*
+eel_labeled_image_new_from_file_name (const char *text,
+				      const char *pixbuf_file_name)
+{
+	EelLabeledImage *labeled_image;
+
+	g_return_val_if_fail (pixbuf_file_name != NULL, NULL);
+
+	labeled_image = EEL_LABELED_IMAGE (eel_labeled_image_new (text, NULL));
+	eel_labeled_image_set_pixbuf_from_file_name (labeled_image, pixbuf_file_name);
+	return GTK_WIDGET (labeled_image);
+}
+
+/**
+ * eel_labeled_image_set_label_position:
+ * @labeled_image: A EelLabeledImage.
+ * @label_position: The position of the label with respect to the image.
+ *
+ * Set the position of the label with respect to the image as follows:
+ *
+ * GTK_POS_LEFT:
+ *   [ <label> <image> ]
+ *
+ * GTK_POS_RIGHT:
+ *   [ <image> <label> ]
+ *
+ * GTK_POS_TOP:
+ *   [ <label> ]
+ *   [ <image> ]
+ *
+ * GTK_POS_BOTTOM:
+ *   [ <image> ]
+ *   [ <label> ]
+ *
+ */
+void
+eel_labeled_image_set_label_position (EelLabeledImage *labeled_image,
+				      GtkPositionType label_position)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+	g_return_if_fail (label_position >= GTK_POS_LEFT);
+	g_return_if_fail (label_position <= GTK_POS_BOTTOM);
+	
+	if (labeled_image->details->label_position == label_position) {
+		return;
+	}
+
+	labeled_image->details->label_position = label_position;
+
+	labeled_image_update_alignments (labeled_image);
+
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_label_postiion:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns an enumeration indicating the position of the label with respect to the image.
+ */
+GtkPositionType
+eel_labeled_image_get_label_position (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+	
+	return labeled_image->details->label_position;
+}
+
+/**
+ * eel_labeled_image_set_show_label:
+ * @labeled_image: A EelLabeledImage.
+ * @show_image: A boolean value indicating whether the label should be shown.
+ *
+ * Update the labeled image to either show or hide the internal label widget.
+ * This function doesnt have any effect if the LabeledImage doesnt already
+ * contain an label.
+ */
+void
+eel_labeled_image_set_show_label (EelLabeledImage *labeled_image,
+				  gboolean show_label)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	if (labeled_image->details->show_label == show_label) {
+		return;
+	}
+
+	labeled_image->details->show_label = show_label;
+
+	if (labeled_image->details->label != NULL) {
+		if (labeled_image->details->show_label) {
+			gtk_widget_show (labeled_image->details->label);
+		} else {
+			gtk_widget_hide (labeled_image->details->label);
+		}
+	}
+
+	labeled_image_update_alignments (labeled_image);
+
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_show_label:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns a boolean value indicating whether the internal label is shown.
+ */
+gboolean
+eel_labeled_image_get_show_label (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+	
+	return labeled_image->details->show_label;
+}
+
+/**
+ * eel_labeled_image_set_show_image:
+ * @labeled_image: A EelLabeledImage.
+ * @show_image: A boolean value indicating whether the image should be shown.
+ *
+ * Update the labeled image to either show or hide the internal image widget.
+ * This function doesnt have any effect if the LabeledImage doesnt already
+ * contain an image.
+ */
+void
+eel_labeled_image_set_show_image (EelLabeledImage *labeled_image,
+				  gboolean show_image)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	if (labeled_image->details->show_image == show_image) {
+		return;
+	}
+
+	labeled_image->details->show_image = show_image;
+
+	if (labeled_image->details->image != NULL) {
+		if (labeled_image->details->show_image) {
+			gtk_widget_show (labeled_image->details->image);
+		} else {
+			gtk_widget_hide (labeled_image->details->image);
+		}
+	}
+
+	labeled_image_update_alignments (labeled_image);
+
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_show_image:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns a boolean value indicating whether the internal image is shown.
+ */
+gboolean
+eel_labeled_image_get_show_image (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+	
+	return labeled_image->details->show_image;
+}
+
+
+/**
+ * eel_labeled_image_set_fixed_image_height:
+ * @labeled_image: A EelLabeledImage.
+ * @fixed_image_height: The new fixed image height.
+ *
+ * Normally, we measure the height of images, but it's sometimes useful
+ * to use a fixed height for all the images.  This routine sets the
+ * image height to the passed in value
+ *
+ */
+void
+eel_labeled_image_set_fixed_image_height (EelLabeledImage *labeled_image,
+					  int new_height)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	if (labeled_image->details->fixed_image_height == new_height) {
+		return;
+	}
+	
+	labeled_image->details->fixed_image_height = new_height;
+
+	labeled_image_update_alignments (labeled_image);
+	
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_set_selected:
+ * @labeled_image: A EelLabeledImage.
+ * @selected: A boolean value indicating whether the labeled image
+ * should be selected.
+ *
+ * Selects or deselects the labeled image.
+ *
+ */
+void            
+eel_labeled_image_set_selected (EelLabeledImage *labeled_image,
+				gboolean selected)
+{
+	GtkStateType state;
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	state = selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL;
+
+	gtk_widget_set_state (GTK_WIDGET (labeled_image), state);
+	gtk_widget_set_state (labeled_image->details->image, state);
+	gtk_widget_set_state (labeled_image->details->label, state);
+}
+
+/**
+ * eel_labeled_image_get_selected:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns the selected state of the labeled image.
+ *
+ */
+gboolean        
+eel_labeled_image_get_selected (EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), FALSE);
+
+	return GTK_WIDGET (labeled_image)->state == GTK_STATE_SELECTED;
+}
+
+/**
+ * eel_labeled_image_set_spacing:
+ * @labeled_image: A EelLabeledImage.
+ * @spacing: The new spacing between label and image.
+ *
+ * Set the spacing between label and image.  This will only affect
+ * the geometry of the widget if both a label and image are currently
+ * visible.
+ *
+ */
+void
+eel_labeled_image_set_spacing (EelLabeledImage *labeled_image,
+			       guint spacing)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	if (labeled_image->details->spacing == spacing) {
+		return;
+	}
+	
+	labeled_image->details->spacing = spacing;
+
+	labeled_image_update_alignments (labeled_image);
+	
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_spacing:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns: The spacing between the label and image.
+ */
+guint
+eel_labeled_image_get_spacing (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+	
+	return labeled_image->details->spacing;
+}
+
+/**
+ * eel_labeled_image_set_x_padding:
+ * @labeled_image: A EelLabeledImage.
+ * @x_padding: The new horizontal padding.
+ *
+ * Set horizontal padding for the EelLabeledImage.  The padding
+ * attribute work just like that in GtkMisc.
+ */
+void
+eel_labeled_image_set_x_padding (EelLabeledImage *labeled_image,
+				 int x_padding)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	x_padding = MAX (0, x_padding);
+	
+	if (labeled_image->details->x_padding == x_padding) {
+		return;
+	}
+	
+	labeled_image->details->x_padding = x_padding;
+	labeled_image_update_alignments (labeled_image);
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_x_padding:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns: The horizontal padding for the LabeledImage's content.
+ */
+int
+eel_labeled_image_get_x_padding (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+
+	return labeled_image->details->x_padding;
+}
+
+/**
+ * eel_labeled_image_set_y_padding:
+ * @labeled_image: A EelLabeledImage.
+ * @x_padding: The new vertical padding.
+ *
+ * Set vertical padding for the EelLabeledImage.  The padding
+ * attribute work just like that in GtkMisc.
+ */
+void
+eel_labeled_image_set_y_padding (EelLabeledImage *labeled_image,
+				 int y_padding)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	y_padding = MAX (0, y_padding);
+	
+	if (labeled_image->details->y_padding == y_padding) {
+		return;
+	}
+	
+	labeled_image->details->y_padding = y_padding;
+	labeled_image_update_alignments (labeled_image);
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_x_padding:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns: The vertical padding for the LabeledImage's content.
+ */
+int
+eel_labeled_image_get_y_padding (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+
+	return labeled_image->details->y_padding;
+}
+
+/**
+ * eel_labeled_image_set_x_alignment:
+ * @labeled_image: A EelLabeledImage.
+ * @x_alignment: The new horizontal alignment.
+ *
+ * Set horizontal alignment for the EelLabeledImage's content.
+ * The 'content' is the union of the image and label.  The alignment
+ * attribute work just like that in GtkMisc.
+ */
+void
+eel_labeled_image_set_x_alignment (EelLabeledImage *labeled_image,
+				   float x_alignment)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	x_alignment = MAX (0, x_alignment);
+	x_alignment = MIN (1.0, x_alignment);
+	
+	if (labeled_image->details->x_alignment == x_alignment) {
+		return;
+	}
+	
+	labeled_image->details->x_alignment = x_alignment;
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_x_alignment:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns: The horizontal alignment for the LabeledImage's content.
+ */
+float
+eel_labeled_image_get_x_alignment (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+
+	return labeled_image->details->x_alignment;
+}
+
+/**
+ * eel_labeled_image_set_y_alignment:
+ * @labeled_image: A EelLabeledImage.
+ * @y_alignment: The new vertical alignment.
+ *
+ * Set vertical alignment for the EelLabeledImage's content.
+ * The 'content' is the union of the image and label.  The alignment
+ * attribute work just like that in GtkMisc.
+ */
+void
+eel_labeled_image_set_y_alignment (EelLabeledImage *labeled_image,
+				   float y_alignment)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	y_alignment = MAX (0, y_alignment);
+	y_alignment = MIN (1.0, y_alignment);
+	
+	if (labeled_image->details->y_alignment == y_alignment) {
+		return;
+	}
+	
+	labeled_image->details->y_alignment = y_alignment;
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_y_alignment:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Returns: The vertical alignment for the LabeledImage's content.
+ */
+float
+eel_labeled_image_get_y_alignment (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+
+	return labeled_image->details->y_alignment;
+}
+
+/**
+ * eel_labeled_image_set_fill:
+ * @labeled_image: A EelLabeledImage.
+ * @fill: A boolean value indicating whether the internal image and label
+ * widgets should fill all the available allocation.
+ *
+ * By default the internal image and label wigets are sized to their natural
+ * preferred geometry.  You can use the 'fill' attribute of LabeledImage
+ * to have the internal widgets fill as much of the LabeledImage allocation
+ * as is available.
+ */
+void
+eel_labeled_image_set_fill (EelLabeledImage *labeled_image,
+			    gboolean fill)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+	
+	if (labeled_image->details->fill == fill) {
+		return;
+	}
+
+	labeled_image->details->fill = fill;
+
+	labeled_image_update_alignments (labeled_image);
+	
+	gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+}
+
+/**
+ * eel_labeled_image_get_fill:
+ * @labeled_image: A EelLabeledImage.
+ *
+ * Retruns a boolean value indicating whether the internal widgets fill
+ * all the available allocation.
+ */
+gboolean
+eel_labeled_image_get_fill (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), 0);
+	
+	return labeled_image->details->fill;
+}
+
+static void
+eel_labled_set_mnemonic_widget (GtkWidget *image_widget,
+				GtkWidget *mnemonic_widget)
+{
+	EelLabeledImage *image;
+
+	g_assert (EEL_IS_LABELED_IMAGE (image_widget));
+
+	image = EEL_LABELED_IMAGE (image_widget);
+
+	if (image->details->label)
+		gtk_label_set_mnemonic_widget
+			(GTK_LABEL (image->details->label), mnemonic_widget);
+}
+
+/**
+ * eel_labeled_image_button_new:
+ * @text: Text to use for label or NULL.
+ * @pixbuf: Pixbuf to use for image or NULL.
+ *
+ * Create a stock GtkButton with a EelLabeledImage child.
+ *
+ */
+GtkWidget *
+eel_labeled_image_button_new (const char *text,
+			      GdkPixbuf *pixbuf)
+{
+	GtkWidget *button;
+	GtkWidget *labeled_image;
+	
+	button = g_object_new (eel_labeled_image_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new (text, pixbuf);
+	gtk_container_add (GTK_CONTAINER (button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, button);
+	gtk_widget_show (labeled_image);
+	
+	return button;
+}
+
+/**
+ * eel_labeled_image_button_new_from_file_name:
+ * @text: Text to use for label or NULL.
+ * @pixbuf_file_name: Name of pixbuf to use for image.  Cannot be NULL.
+ *
+ * Create a stock GtkToggleButton with a EelLabeledImage child.
+ *
+ */
+GtkWidget *
+eel_labeled_image_button_new_from_file_name (const char *text,
+					     const char *pixbuf_file_name)
+{
+	GtkWidget *button;
+	GtkWidget *labeled_image;
+
+	g_return_val_if_fail (pixbuf_file_name != NULL, NULL);
+	
+	button = g_object_new (eel_labeled_image_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name);
+	gtk_container_add (GTK_CONTAINER (button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, button);
+	gtk_widget_show (labeled_image);
+	
+	return button;
+}
+
+/**
+ * eel_labeled_image_toggle_button_new:
+ * @text: Text to use for label or NULL.
+ * @pixbuf: Pixbuf to use for image or NULL.
+ *
+ * Create a stock GtkToggleButton with a EelLabeledImage child.
+ *
+ */
+GtkWidget *
+eel_labeled_image_toggle_button_new (const char *text,
+				     GdkPixbuf *pixbuf)
+{
+	GtkWidget *toggle_button;
+	GtkWidget *labeled_image;
+	
+	toggle_button = g_object_new (eel_labeled_image_toggle_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new (text, pixbuf);
+	gtk_container_add (GTK_CONTAINER (toggle_button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, toggle_button);
+	gtk_widget_show (labeled_image);
+	
+	return toggle_button;
+}
+
+/**
+ * eel_labeled_image_toggle_button_new_from_file_name:
+ * @text: Text to use for label or NULL.
+ * @pixbuf_file_name: Name of pixbuf to use for image.  Cannot be NULL.
+ *
+ * Create a stock GtkToggleButton with a EelLabeledImage child.
+ *
+ */
+GtkWidget *
+eel_labeled_image_toggle_button_new_from_file_name (const char *text,
+						    const char *pixbuf_file_name)
+{
+	GtkWidget *toggle_button;
+	GtkWidget *labeled_image;
+
+	g_return_val_if_fail (pixbuf_file_name != NULL, NULL);
+	
+	toggle_button = g_object_new (eel_labeled_image_toggle_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name);
+	gtk_container_add (GTK_CONTAINER (toggle_button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, toggle_button);
+	gtk_widget_show (labeled_image);
+	
+	return toggle_button;
+}
+
+/**
+ * eel_labeled_image_toggle_button_new:
+ * @text: Text to use for label or NULL.
+ * @pixbuf: Pixbuf to use for image or NULL.
+ *
+ * Create a stock GtkToggleButton with a EelLabeledImage child.
+ *
+ * Returns: the new radio button.
+ */
+GtkWidget *
+eel_labeled_image_radio_button_new (const char *text,
+				    GdkPixbuf  *pixbuf)
+{
+	GtkWidget *radio_button;
+	GtkWidget *labeled_image;
+	
+	radio_button = g_object_new (eel_labeled_image_radio_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new (text, pixbuf);
+	gtk_container_add (GTK_CONTAINER (radio_button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, radio_button);
+	gtk_widget_show (labeled_image);
+	
+	return radio_button;
+}
+
+/**
+ * eel_labeled_image_radio_button_new_from_file_name:
+ * @text: Text to use for label or NULL.
+ * @pixbuf_file_name: Name of pixbuf to use for image.  Cannot be NULL.
+ *
+ * Create a stock GtkRadioButton with a EelLabeledImage child.
+ *
+ * Returns: the new radio button.
+ */
+GtkWidget *
+eel_labeled_image_radio_button_new_from_file_name (const char *text,
+						   const char *pixbuf_file_name)
+{
+	GtkWidget *radio_button;
+	GtkWidget *labeled_image;
+
+	g_return_val_if_fail (pixbuf_file_name != NULL, NULL);
+	
+	radio_button = g_object_new (eel_labeled_image_radio_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name);
+	gtk_container_add (GTK_CONTAINER (radio_button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, radio_button);
+	gtk_widget_show (labeled_image);
+	
+	return radio_button;
+}
+
+/*
+ * Workaround some bugs in GtkCheckButton where the widget 
+ * does not redraw properly after leave or focus out events
+ * 
+ * The workaround is to draw a little bit more than the 
+ * widget itself - 4 pixels worth.  For some reason the
+ * widget does not properly redraw its edges.
+ */
+static void
+button_leave_callback (GtkWidget *widget,
+		       gpointer callback_data)
+{
+	g_assert (GTK_IS_WIDGET (widget));
+
+	if (GTK_WIDGET_DRAWABLE (widget)) {
+		const int fudge = 4;
+		EelIRect bounds;
+
+		bounds = eel_gtk_widget_get_bounds (widget);
+		
+		bounds.x0 -= fudge;
+		bounds.y0 -= fudge;
+		bounds.x1 += fudge;
+		bounds.y1 += fudge;
+		
+		gtk_widget_queue_draw_area (widget->parent,
+					    bounds.x0,
+					    bounds.y0,
+					    eel_irect_get_width (bounds),
+					    eel_irect_get_height (bounds));
+	}
+}
+
+static gint
+button_focus_out_event_callback (GtkWidget *widget,
+				 GdkEventFocus *event,
+				 gpointer callback_data)
+{
+	g_assert (GTK_IS_WIDGET (widget));
+
+	button_leave_callback (widget, callback_data);
+
+	return FALSE;
+}
+
+/**
+ * eel_labeled_image_check_button_new:
+ * @text: Text to use for label or NULL.
+ * @pixbuf: Pixbuf to use for image or NULL.
+ *
+ * Create a stock GtkCheckButton with a EelLabeledImage child.
+ *
+ */
+GtkWidget *
+eel_labeled_image_check_button_new (const char *text,
+				    GdkPixbuf *pixbuf)
+{
+	GtkWidget *check_button;
+	GtkWidget *labeled_image;
+	
+	check_button = g_object_new (eel_labeled_image_check_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new (text, pixbuf);
+	gtk_container_add (GTK_CONTAINER (check_button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, check_button);
+	gtk_widget_show (labeled_image);
+	
+	/*
+	 * Workaround some bugs in GtkCheckButton where the widget 
+	 * does not redraw properly after leave or focus out events
+	 */
+	g_signal_connect (check_button, "leave",
+			  G_CALLBACK (button_leave_callback), NULL);
+	g_signal_connect (check_button, "focus_out_event",
+			  G_CALLBACK (button_focus_out_event_callback), NULL);
+	
+	return check_button;
+}
+
+/**
+ * eel_labeled_image_check_button_new_from_file_name:
+ * @text: Text to use for label or NULL.
+ * @pixbuf_file_name: Name of pixbuf to use for image.  Cannot be NULL.
+ *
+ * Create a stock GtkCheckButton with a EelLabeledImage child.
+ *
+ */
+GtkWidget *
+eel_labeled_image_check_button_new_from_file_name (const char *text,
+						   const char *pixbuf_file_name)
+{
+	GtkWidget *check_button;
+	GtkWidget *labeled_image;
+
+	g_return_val_if_fail (pixbuf_file_name != NULL, NULL);
+	
+	check_button = g_object_new (eel_labeled_image_check_button_get_type (), NULL);
+	labeled_image = eel_labeled_image_new_from_file_name (text, pixbuf_file_name);
+	gtk_container_add (GTK_CONTAINER (check_button), labeled_image);
+	eel_labled_set_mnemonic_widget (labeled_image, check_button);
+	gtk_widget_show (labeled_image);
+	
+	return check_button;
+}
+
+/*
+ * The rest of the methods are proxies for those in EelImage and 
+ * EelLabel.  We have all these so that we dont have to expose 
+ * our internal widgets at all.  Probably more of these will be added
+ * as they are needed.
+ */
+
+/**
+ * eel_labeled_image_set_pixbuf:
+ * @labaled_image: A EelLabeledImage.
+ * @pixbuf: New pixbuf to use or NULL.
+ *
+ * Change the pixbuf displayed by the LabeledImage.  Note that the widget display
+ * is only updated if the show_image attribute is TRUE.
+ *
+ * If no internal image widget exists as of yet, a new one will be created.
+ *
+ * A NULL &pixbuf will cause the internal image widget (if alive) to be destroyed.
+ */
+void
+eel_labeled_image_set_pixbuf (EelLabeledImage *labeled_image,
+			      GdkPixbuf *pixbuf)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	if (pixbuf == NULL) {
+		if (labeled_image->details->image != NULL) {
+			gtk_widget_destroy (labeled_image->details->image);
+			labeled_image->details->image = NULL;
+		}
+		
+		gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+	} else {
+		labeled_image_ensure_image (labeled_image);
+		gtk_image_set_from_pixbuf (GTK_IMAGE (labeled_image->details->image), pixbuf);
+	}
+}
+
+void
+eel_labeled_image_set_pixbuf_from_file_name (EelLabeledImage *labeled_image,
+					     const char *pixbuf_file_name)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	labeled_image_ensure_image (labeled_image);
+	gtk_image_set_from_file (GTK_IMAGE (labeled_image->details->image), pixbuf_file_name);
+}
+
+/**
+ * eel_labeled_image_set_text:
+ * @labaled_image: A EelLabeledImage.
+ * @text: New text (with mnemnonic) to use or NULL.
+ *
+ * Change the text displayed by the LabeledImage.  Note that the widget display
+ * is only updated if the show_label attribute is TRUE.
+ *
+ * If no internal label widget exists as of yet, a new one will be created.
+ *
+ * A NULL &text will cause the internal label widget (if alive) to be destroyed.
+ */
+void
+eel_labeled_image_set_text (EelLabeledImage *labeled_image,
+			    const char *text)
+{
+	g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+	if (text == NULL) {
+		if (labeled_image->details->label) {
+			gtk_widget_destroy (labeled_image->details->label);
+			labeled_image->details->label = NULL;
+		}
+		
+		gtk_widget_queue_resize (GTK_WIDGET (labeled_image));
+	} else {
+		labeled_image_ensure_label (labeled_image);
+		gtk_label_set_text_with_mnemonic
+			(GTK_LABEL (labeled_image->details->label), text);
+	}
+}
+
+char *
+eel_labeled_image_get_text (const EelLabeledImage *labeled_image)
+{
+	g_return_val_if_fail (EEL_IS_LABELED_IMAGE (labeled_image), NULL);
+	
+	if (labeled_image->details->label == NULL) {
+		return NULL;
+	}
+
+	return g_strdup (gtk_label_get_text (GTK_LABEL (labeled_image->details->label)));
+}
+
+void
+eel_labeled_image_set_can_focus (EelLabeledImage *labeled_image,
+				 gboolean         can_focus)
+{
+	if (can_focus) {
+		GTK_WIDGET_SET_FLAGS
+			(GTK_WIDGET (labeled_image), GTK_CAN_FOCUS);
+		
+	} else {
+		GTK_WIDGET_UNSET_FLAGS
+			(GTK_WIDGET (labeled_image), GTK_CAN_FOCUS);
+	}
+}
+
+static AtkObjectClass *a11y_parent_class = NULL;
+
+static void
+eel_labeled_image_accessible_initialize (AtkObject *accessible,
+					 gpointer   widget)
+{
+	a11y_parent_class->initialize (accessible, widget);
+}
+
+static EelLabeledImage *
+get_image (gpointer object)
+{
+	GtkWidget *widget;
+
+	if (!(widget = GTK_ACCESSIBLE (object)->widget)) {
+		return NULL;
+	}
+	
+	if (GTK_IS_BUTTON (widget))
+		widget = GTK_BIN (widget)->child;
+
+	return EEL_LABELED_IMAGE (widget);
+}
+
+static G_CONST_RETURN gchar *
+eel_labeled_image_accessible_get_name (AtkObject *accessible)
+{
+	EelLabeledImage *labeled_image;
+
+	labeled_image = get_image (accessible);
+
+	if (labeled_image && labeled_image->details &&
+	    labeled_image->details->label)
+		return gtk_label_get_text
+			(GTK_LABEL (labeled_image->details->label));
+
+	g_warning ("no label on '%p'", labeled_image);
+
+	return NULL;
+}
+
+static void
+eel_labeled_image_accessible_image_get_size (AtkImage *image,
+					     gint     *width,
+					     gint     *height)
+{
+	EelLabeledImage *labeled_image;
+
+	labeled_image = get_image (image);
+
+	if (!labeled_image || !labeled_image->details->image) {
+		*width = *height = 0;
+		return;
+	}
+
+	*width = labeled_image->details->image->allocation.width;
+	*height = labeled_image->details->image->allocation.height;
+}
+
+static void
+eel_labeled_image_accessible_image_interface_init (AtkImageIface *iface)
+{
+	iface->get_image_size = eel_labeled_image_accessible_image_get_size;
+}
+
+static void
+eel_labeled_image_accessible_class_init (AtkObjectClass *klass)
+{
+	a11y_parent_class = g_type_class_peek_parent (klass);
+
+	klass->get_name = eel_labeled_image_accessible_get_name;
+	klass->initialize = eel_labeled_image_accessible_initialize;
+}
+
+enum {
+	BUTTON,
+	CHECK,
+	TOGGLE,
+	RADIO,
+	PLAIN,
+	LAST_ONE
+};
+
+static AtkObject *
+eel_labeled_image_get_accessible (GtkWidget *widget)
+{
+	int i;
+	static GType types[LAST_ONE] = { 0 };
+	const char *tname;
+	AtkRole role;
+	AtkObject *accessible;
+
+	if ((accessible = eel_accessibility_get_atk_object (widget)))
+		return accessible;
+
+	if (GTK_IS_CHECK_BUTTON (widget)) {
+		i = BUTTON;
+		role = ATK_ROLE_CHECK_BOX;
+		tname = "EelLabeledImageCheckButtonAccessible";
+
+	} else if (GTK_IS_TOGGLE_BUTTON (widget)) {
+		i = CHECK;
+		role = ATK_ROLE_TOGGLE_BUTTON;
+		tname = "EelLabeledImageToggleButtonAccessible";
+
+	} else if (GTK_IS_RADIO_BUTTON (widget)) {
+		i = RADIO;
+		role = ATK_ROLE_RADIO_BUTTON;
+		tname = "EelLabeledImageRadioButtonAccessible";
+
+	} else if (GTK_IS_BUTTON (widget)) {
+		i = TOGGLE;
+		role = ATK_ROLE_PUSH_BUTTON;
+		tname = "EelLabeledImagePushButtonAccessible";
+
+	} else { /* plain */
+		i = PLAIN;
+		role = ATK_ROLE_IMAGE;
+		tname = "EelLabeledImagePlainAccessible";
+	}
+
+	if (!types [i]) {
+		const GInterfaceInfo atk_image_info = {
+			(GInterfaceInitFunc) eel_labeled_image_accessible_image_interface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL
+		};
+
+		types [i] = eel_accessibility_create_derived_type
+			(tname, G_TYPE_FROM_INSTANCE (widget),
+			 eel_labeled_image_accessible_class_init);
+
+		if (!types [i])
+			return NULL;
+
+		g_type_add_interface_static (
+			types [i], ATK_TYPE_IMAGE, &atk_image_info);
+	}
+
+	accessible = g_object_new (types [i], NULL);
+	atk_object_set_role (accessible, role);
+
+	return eel_accessibility_set_atk_object_return (widget, accessible);
+}
+
+static void
+eel_labeled_image_button_class_init (GtkWidgetClass *klass)
+{
+	klass->get_accessible = eel_labeled_image_get_accessible;
+}
+
+static GType
+eel_labeled_image_button_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		GTypeInfo info = {
+			sizeof (GtkButtonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_labeled_image_button_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GtkButton),
+			0, /* n_preallocs */
+			(GInstanceInitFunc) NULL
+		};
+		
+		type = g_type_register_static
+			(GTK_TYPE_BUTTON,
+			 "EelLabeledImageButton", &info, 0);
+	}
+
+	return type;
+}
+
+static GType
+eel_labeled_image_check_button_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		GTypeInfo info = {
+			sizeof (GtkCheckButtonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_labeled_image_button_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GtkCheckButton),
+			0, /* n_preallocs */
+			(GInstanceInitFunc) NULL
+		};
+		
+		type = g_type_register_static
+			(GTK_TYPE_CHECK_BUTTON,
+			 "EelLabeledImageCheckButton", &info, 0);
+	}
+
+	return type;
+}
+
+static GType
+eel_labeled_image_toggle_button_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		GTypeInfo info = {
+			sizeof (GtkToggleButtonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_labeled_image_button_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GtkToggleButton),
+			0, /* n_preallocs */
+			(GInstanceInitFunc) NULL
+		};
+		
+		type = g_type_register_static
+			(GTK_TYPE_TOGGLE_BUTTON,
+			 "EelLabeledImageToggleButton", &info, 0);
+	}
+
+	return type;
+}
+
+
+static GType
+eel_labeled_image_radio_button_get_type (void)
+{
+	static GType type = 0;
+
+	if (!type) {
+		GTypeInfo info = {
+			sizeof (GtkRadioButtonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) eel_labeled_image_button_class_init,
+			NULL, /* class_finalize */
+			NULL, /* class_data */
+			sizeof (GtkRadioButton),
+			0, /* n_preallocs */
+			(GInstanceInitFunc) NULL
+		};
+		
+		type = g_type_register_static
+			(GTK_TYPE_RADIO_BUTTON,
+			 "EelLabeledImageRadioButton", &info, 0);
+	}
+
+	return type;
+}

Added: trunk/eel/eel-labeled-image.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-labeled-image.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,156 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-labeled-image.h - A labeled image.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+/* EelLabeledImage is a container widget.  It can only contain internal 
+ * widgets.  These internal widgets are can be a EelLabel and/or a 
+ * EelImage.  These internal widgets are created as needed.  That means
+ * that EelLabeledImage can always be used for "free" instead of a 
+ * EelLabel or EelImage.  The only overhead is that of the GtkObject
+ * machinery.
+ *
+ * The position of the label with respect to the image is controlled by the
+ * 'label_positon' attribute.
+ *
+ * By default the internal image and label widgets are sized to their natural
+ * preferred geometry.  You can use the 'fill' attribute of LabeledImage
+ * to have the internal widgets fill as much of the LabeledImage allocation
+ * as is available.
+ *
+ * LabeledImage also has x_padding/y_padding and x_alignment/y_alignment 
+ * attributes that behave exaclty as those in the GtkMisc class.
+ *
+ * Note that the alignments are ignored if the fill attribute is TRUE.
+ */
+
+#ifndef EEL_LABELED_IMAGE_H
+#define EEL_LABELED_IMAGE_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <eel/eel-art-extensions.h>
+
+G_BEGIN_DECLS
+
+#define EEL_TYPE_LABELED_IMAGE            (eel_labeled_image_get_type ())
+#define EEL_LABELED_IMAGE(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_LABELED_IMAGE, EelLabeledImage))
+#define EEL_LABELED_IMAGE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_LABELED_IMAGE, EelLabeledImageClass))
+#define EEL_IS_LABELED_IMAGE(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_LABELED_IMAGE))
+#define EEL_IS_LABELED_IMAGE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_LABELED_IMAGE))
+
+typedef struct EelLabeledImage		  EelLabeledImage;
+typedef struct EelLabeledImageClass	  EelLabeledImageClass;
+typedef struct EelLabeledImageDetails	  EelLabeledImageDetails;
+
+struct EelLabeledImage
+{
+	/* Superclass */
+	GtkContainer container;
+
+	/* Private things */
+	EelLabeledImageDetails *details;
+};
+
+struct EelLabeledImageClass
+{
+	GtkContainerClass parent_class;
+
+	void (*activate) (EelLabeledImage *image);
+};
+
+/* Public GtkLabeledImage methods */
+GtkType         eel_labeled_image_get_type                         (void);
+GtkWidget *     eel_labeled_image_new                              (const char              *text,
+								    GdkPixbuf               *pixbuf);
+GtkWidget *     eel_labeled_image_new_from_file_name               (const char              *text,
+								    const char              *pixbuf_file_name);
+void            eel_labeled_image_set_label_position               (EelLabeledImage         *labeled_image,
+								    GtkPositionType          label_position);
+GtkPositionType eel_labeled_image_get_label_position               (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_show_label                   (EelLabeledImage         *labeled_image,
+								    gboolean                 show_label);
+gboolean        eel_labeled_image_get_show_label                   (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_show_image                   (EelLabeledImage         *labeled_image,
+								    gboolean                 show_image);
+gboolean        eel_labeled_image_get_show_image                   (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_can_focus                    (EelLabeledImage         *labeled_image,
+								    gboolean                 can_focus);
+void            eel_labeled_image_set_spacing                      (EelLabeledImage         *labeled_image,
+								    guint                    spacing);
+guint           eel_labeled_image_get_spacing                      (const EelLabeledImage   *labeled_image);
+int             eel_labeled_image_get_x_padding                    (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_x_padding                    (EelLabeledImage         *labeled_image,
+								    int                      x_padding);
+int             eel_labeled_image_get_y_padding                    (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_y_padding                    (EelLabeledImage         *labeled_image,
+								    int                      y_padding);
+float           eel_labeled_image_get_x_alignment                  (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_x_alignment                  (EelLabeledImage         *labeled_image,
+								    float                    x_alignment);
+float           eel_labeled_image_get_y_alignment                  (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_y_alignment                  (EelLabeledImage         *labeled_image,
+								    float                    y_alignment);
+void            eel_labeled_image_set_fill                         (EelLabeledImage         *labeled_image,
+								    gboolean                 fill);
+gboolean        eel_labeled_image_get_fill                         (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_fixed_image_height           (EelLabeledImage         *labeled_image,
+								    int                      fixed_image_height);
+void            eel_labeled_image_set_selected                     (EelLabeledImage         *labeled_image,
+								    gboolean                 selected);
+gboolean        eel_labeled_image_get_selected                     (EelLabeledImage         *labeled_image);
+
+/* Functions for creating stock GtkButtons with a labeled image child */
+GtkWidget *     eel_labeled_image_button_new                       (const char              *text,
+								    GdkPixbuf               *pixbuf);
+GtkWidget *     eel_labeled_image_button_new_from_file_name        (const char              *text,
+								    const char              *pixbuf_file_name);
+GtkWidget *     eel_labeled_image_toggle_button_new                (const char              *text,
+								    GdkPixbuf               *pixbuf);
+GtkWidget *     eel_labeled_image_toggle_button_new_from_file_name (const char              *text,
+								    const char              *pixbuf_file_name);
+GtkWidget *     eel_labeled_image_check_button_new                 (const char              *text,
+								    GdkPixbuf               *pixbuf);
+GtkWidget *     eel_labeled_image_check_button_new_from_file_name  (const char              *text,
+								    const char              *pixbuf_file_name);
+GtkWidget *     eel_labeled_image_radio_button_new                 (const char              *text,
+								    GdkPixbuf               *pixbuf);
+GtkWidget *     eel_labeled_image_radio_button_new_from_file_name  (const char              *text,
+								    const char              *pixbuf_file_name);
+
+/* These are proxies for methods in EelImage and EelLabel */
+void            eel_labeled_image_set_pixbuf                       (EelLabeledImage         *labeled_image,
+								    GdkPixbuf               *pixbuf);
+void            eel_labeled_image_set_pixbuf_from_file_name        (EelLabeledImage         *labeled_image,
+								    const char              *pixbuf_file_name);
+GdkPixbuf*      eel_labeled_image_get_pixbuf                       (const EelLabeledImage   *labeled_image);
+void            eel_labeled_image_set_text                         (EelLabeledImage         *labeled_image,
+								    const char              *text);
+char*           eel_labeled_image_get_text                         (const EelLabeledImage   *labeled_image);
+EelIRect        eel_labeled_image_get_image_bounds                 (const EelLabeledImage   *labeled_image);
+EelIRect        eel_labeled_image_get_label_bounds                 (const EelLabeledImage   *labeled_image);
+
+G_END_DECLS
+
+#endif /* EEL_LABELED_IMAGE_H */
+
+

Added: trunk/eel/eel-lib-self-check-functions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-lib-self-check-functions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,38 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+   
+   eel-lib-self-check-functions.c: Wrapper for all self check functions
+   in Eel proper.
+ 
+   Copyright (C) 2000 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+
+#if ! defined (EEL_OMIT_SELF_CHECK)
+
+#include "eel-lib-self-check-functions.h"
+
+void
+eel_run_lib_self_checks (void)
+{
+	EEL_LIB_FOR_EACH_SELF_CHECK_FUNCTION (EEL_CALL_SELF_CHECK_FUNCTION)
+}
+
+#endif /* ! EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-lib-self-check-functions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-lib-self-check-functions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,53 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-lib-self-check-functions.h: Wrapper and prototypes for all
+   self-check functions in libeel.
+ 
+   Copyright (C) 2000 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Darin Adler <darin eazel com>
+*/
+
+#include "eel-self-checks.h"
+
+void eel_run_lib_self_checks (void);
+
+/* Putting the prototypes for these self-check functions in each
+   header file for the files they are defined in would make compiling
+   the self-check framework take way too long (since one file would
+   have to include everything).
+
+   So we put the list of functions here instead.
+
+   Instead of just putting prototypes here, we put this macro that
+   can be used to do operations on the whole list of functions.
+*/
+
+#define EEL_LIB_FOR_EACH_SELF_CHECK_FUNCTION(macro) \
+	macro (eel_self_check_background) \
+	macro (eel_self_check_enumeration) \
+	macro (eel_self_check_gdk_extensions) \
+	macro (eel_self_check_gdk_pixbuf_extensions) \
+	macro (eel_self_check_glib_extensions) \
+        macro (eel_self_check_pango_extensions) \
+	macro (eel_self_check_preferences) \
+	macro (eel_self_check_string) \
+/* Add new self-check functions to the list above this line. */
+
+/* Generate prototypes for all the functions. */
+EEL_LIB_FOR_EACH_SELF_CHECK_FUNCTION (EEL_SELF_CHECK_FUNCTION_PROTOTYPE)

Added: trunk/eel/eel-pango-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-pango-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,586 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-pango-extensions.h - interface for new functions that conceptually
+                            belong in pango. Perhaps some of these will be
+                            actually rolled into pango someday.
+
+   Copyright (C) 2001 Anders Carlsson
+
+   The Eel 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 Eel 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 Eel 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.
+
+   Authors: Anders Carlsson <andersca gnu org>
+*/
+
+#include <config.h>
+#include "eel-pango-extensions.h"
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+#include "eel-lib-self-check-functions.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <pango/pango.h>
+#include <string.h>
+
+PangoAttrList *
+eel_pango_attr_list_copy_or_create (PangoAttrList *attr_list)
+{
+	if (attr_list != NULL) {
+		return pango_attr_list_copy (attr_list);
+	}
+	return pango_attr_list_new ();
+}
+
+PangoAttrList *
+eel_pango_attr_list_apply_global_attribute (PangoAttrList *attr_list,
+					    PangoAttribute *attr)
+{
+	PangoAttrList *new_attr_list;
+
+	g_return_val_if_fail (attr != NULL, NULL);
+
+	attr->start_index = 0;
+	attr->end_index = G_MAXINT;
+	
+	new_attr_list = eel_pango_attr_list_copy_or_create (attr_list);
+	pango_attr_list_change (new_attr_list, attr);
+	return new_attr_list;
+}
+
+#define ELLIPSIS "..."
+
+/* Caution: this is an _expensive_ function */
+static int
+measure_string_width (const char  *string,
+		      PangoLayout *layout)
+{
+	int width;
+	
+	pango_layout_set_text (layout, string, -1);
+	pango_layout_get_pixel_size (layout, &width, NULL);
+
+	return width;
+}
+
+/* this is also plenty slow */
+static void
+compute_character_widths (const char    *string,
+			  PangoLayout   *layout,
+			  int           *char_len_return,
+			  int          **widths_return,
+			  int          **cuts_return)
+{
+	int *widths;
+	int *offsets;
+	int *cuts;
+	int char_len;
+	int byte_len;
+	const char *p;
+	int i;
+	PangoLayoutIter *iter;
+	PangoLogAttr *attrs;
+	
+#define BEGINS_UTF8_CHAR(x) (((x) & 0xc0) != 0x80)
+	
+	char_len = g_utf8_strlen (string, -1);
+	byte_len = strlen (string);
+	
+	widths = g_new (int, char_len);
+	offsets = g_new (int, byte_len);
+
+	/* Create a translation table from byte index to char offset */
+	p = string;
+	i = 0;
+	while (*p) {
+		int byte_index = p - string;
+		
+		if (BEGINS_UTF8_CHAR (*p)) {
+			offsets[byte_index] = i;
+			++i;
+		} else {
+			offsets[byte_index] = G_MAXINT; /* segv if we try to use this */
+		}
+		
+		++p;
+	}
+
+	/* Now fill in the widths array */
+	pango_layout_set_text (layout, string, -1);
+	
+	iter = pango_layout_get_iter (layout);
+
+	do {
+		PangoRectangle extents;
+		int byte_index;
+
+		byte_index = pango_layout_iter_get_index (iter);
+
+		if (byte_index < byte_len) {
+			pango_layout_iter_get_char_extents (iter, &extents);
+			
+			g_assert (BEGINS_UTF8_CHAR (string[byte_index]));
+			g_assert (offsets[byte_index] < char_len);
+			
+			widths[offsets[byte_index]] = PANGO_PIXELS (extents.width);
+		}
+		
+	} while (pango_layout_iter_next_char (iter));
+
+	pango_layout_iter_free (iter);
+
+	g_free (offsets);
+	
+	*widths_return = widths;
+
+	/* Now compute character offsets that are legitimate places to
+	 * chop the string
+	 */
+	attrs = g_new (PangoLogAttr, char_len + 1);
+	
+	pango_get_log_attrs (string, byte_len, -1,
+			     pango_context_get_language (
+				     pango_layout_get_context (layout)),
+			     attrs,
+			     char_len + 1);
+
+	cuts = g_new (int, char_len);
+	i = 0;
+	while (i < char_len) {
+		cuts[i] = attrs[i].is_cursor_position;
+
+		++i;
+	}
+
+	g_free (attrs);
+
+	*cuts_return = cuts;
+
+	*char_len_return = char_len;
+}
+
+
+static char *
+eel_string_ellipsize_start (const char *string, PangoLayout *layout, int width)
+{
+	int resulting_width;
+	int *cuts;
+	int *widths;
+	int char_len;
+	const char *p;
+	int truncate_offset;
+
+	/* Zero-length string can't get shorter - catch this here to
+	 * avoid expensive calculations
+	 */
+	if (*string == '\0')
+		return g_strdup ("");
+
+	/* I'm not sure if this short-circuit is a net win; it might be better
+	 * to just dump this, and always do the compute_character_widths() etc.
+	 * down below.
+	 */
+	resulting_width = measure_string_width (string, layout);
+
+	if (resulting_width <= width) {
+		/* String is already short enough. */
+		return g_strdup (string);
+	}
+
+	/* Remove width of an ellipsis */
+	width -= measure_string_width (ELLIPSIS, layout);
+
+	if (width < 0) {
+		/* No room even for an ellipsis. */
+		return g_strdup ("");
+	}
+
+	/* Our algorithm involves removing enough chars from the string to bring
+	 * the width to the required small size. However, due to ligatures,
+	 * combining characters, etc., it's not guaranteed that the algorithm
+	 * always works 100%. It's sort of a heuristic thing. It should work
+	 * nearly all the time... but I wouldn't put in
+	 * g_assert (width of resulting string < width).
+	 *
+	 * Hmm, another thing that this breaks with is explicit line breaks
+	 * in "string"
+	 */
+
+	compute_character_widths (string, layout, &char_len, &widths, &cuts);
+
+        for (truncate_offset = 1; truncate_offset < char_len; truncate_offset++) {
+
+        	resulting_width -= widths[truncate_offset];
+
+        	if (resulting_width <= width &&
+		    cuts[truncate_offset]) {
+			break;
+        	}
+        }
+
+	g_free (cuts);
+	g_free (widths);
+	
+	p = g_utf8_offset_to_pointer (string, truncate_offset);
+	
+	return g_strconcat (ELLIPSIS, p, NULL);
+}
+
+static char *
+eel_string_ellipsize_end (const char *string, PangoLayout *layout, int width)
+{
+	int resulting_width;
+	int *cuts;
+	int *widths;
+	int char_len;
+	const char *p;
+	int truncate_offset;
+	char *result;
+	
+	/* See explanatory comments in ellipsize_start */
+	
+	if (*string == '\0')
+		return g_strdup ("");
+
+	resulting_width = measure_string_width (string, layout);
+	
+	if (resulting_width <= width) {
+		return g_strdup (string);
+	}
+
+	width -= measure_string_width (ELLIPSIS, layout);
+
+	if (width < 0) {
+		return g_strdup ("");
+	}
+	
+	compute_character_widths (string, layout, &char_len, &widths, &cuts);
+	
+        for (truncate_offset = char_len - 1; truncate_offset > 0; truncate_offset--) {
+        	resulting_width -= widths[truncate_offset];
+        	if (resulting_width <= width &&
+		    cuts[truncate_offset]) {
+			break;
+        	}
+        }
+
+	g_free (cuts);
+	g_free (widths);
+
+	p = g_utf8_offset_to_pointer (string, truncate_offset);
+	
+	result = g_malloc ((p - string) + strlen (ELLIPSIS) + 1);
+	memcpy (result, string, (p - string));
+	strcpy (result + (p - string), ELLIPSIS);
+
+	return result;
+}
+
+static char *
+eel_string_ellipsize_middle (const char *string, PangoLayout *layout, int width)
+{
+	int resulting_width;
+	int *cuts;
+	int *widths;
+	int char_len;
+	int starting_fragment_byte_len;
+	int ending_fragment_byte_index;
+	int starting_fragment_length;
+	int ending_fragment_offset;
+	char *result;
+	
+	/* See explanatory comments in ellipsize_start */
+	
+	if (*string == '\0')
+		return g_strdup ("");
+
+	resulting_width = measure_string_width (string, layout);
+	
+	if (resulting_width <= width) {
+		return g_strdup (string);
+	}
+
+	width -= measure_string_width (ELLIPSIS, layout);
+
+	if (width < 0) {
+		return g_strdup ("");
+	}
+	
+	compute_character_widths (string, layout, &char_len, &widths, &cuts);
+	
+	starting_fragment_length = char_len / 2;
+	ending_fragment_offset = starting_fragment_length + 1;
+	
+	/* Shave off a character at a time from the first and the second half
+	 * until we can fit
+	 */
+	resulting_width -= widths[ending_fragment_offset - 1];
+	
+	/* depending on whether the original string length is odd or even, start by
+	 * shaving off the characters from the starting or ending fragment
+	 */
+	if (char_len % 2) {
+		goto shave_end;
+	}
+
+	while (starting_fragment_length > 0 || ending_fragment_offset < char_len) {
+		if (resulting_width <= width &&
+		    cuts[ending_fragment_offset] &&
+		    cuts[starting_fragment_length]) {
+			break;
+		}
+
+		if (starting_fragment_length > 0) {
+			resulting_width -= widths[starting_fragment_length];
+			starting_fragment_length--;
+		}
+
+	shave_end:
+		if (resulting_width <= width &&
+		    cuts[ending_fragment_offset] &&
+		    cuts[starting_fragment_length]) {
+			break;
+		}
+
+		if (ending_fragment_offset < char_len) {
+			resulting_width -= widths[ending_fragment_offset];
+			ending_fragment_offset++;
+		}
+	}
+
+	g_free (cuts);
+	g_free (widths);	
+	
+	/* patch the two fragments together with an ellipsis */
+	result = g_malloc (strlen (string) + strlen (ELLIPSIS) + 1); /* a bit wasteful, no biggie */
+
+	starting_fragment_byte_len = g_utf8_offset_to_pointer (string, starting_fragment_length) - string;
+	ending_fragment_byte_index = g_utf8_offset_to_pointer (string, ending_fragment_offset) - string;
+	
+	memcpy (result, string, starting_fragment_byte_len);
+	strcpy (result + starting_fragment_byte_len, ELLIPSIS);
+	strcpy (result + starting_fragment_byte_len + strlen (ELLIPSIS), string + ending_fragment_byte_index);
+
+	return result;
+}
+
+
+/**
+ * eel_pango_layout_set_text_ellipsized
+ *
+ * @layout: a pango layout
+ * @string: A a string to be ellipsized.
+ * @width: Desired maximum width in points.
+ * @mode: The desired ellipsizing mode.
+ * 
+ * Truncates a string if required to fit in @width and sets it on the
+ * layout. Truncation involves removing characters from the start, middle or end
+ * respectively and replacing them with "...". Algorithm is a bit
+ * fuzzy, won't work 100%.
+ * 
+ */
+void
+eel_pango_layout_set_text_ellipsized (PangoLayout  *layout,
+				      const char   *string,
+				      int           width,
+				      EelEllipsizeMode mode)
+{
+	char *s;
+
+	g_return_if_fail (PANGO_IS_LAYOUT (layout));
+	g_return_if_fail (string != NULL);
+	g_return_if_fail (width >= 0);
+	
+	switch (mode) {
+	case EEL_ELLIPSIZE_START:
+		s = eel_string_ellipsize_start (string, layout, width);
+		break;
+	case EEL_ELLIPSIZE_MIDDLE:
+		s = eel_string_ellipsize_middle (string, layout, width);
+		break;
+	case EEL_ELLIPSIZE_END:
+		s = eel_string_ellipsize_end (string, layout, width);
+		break;
+	default:
+		g_return_if_reached ();
+	}
+	
+	pango_layout_set_text (layout, s, -1);
+	
+	g_free (s);
+}
+
+
+int
+eel_pango_font_description_get_largest_fitting_font_size (const PangoFontDescription *font_desc,
+							  PangoContext *context,
+							  const char   *text,
+							  int           available_width,
+							  int           minimum_acceptable_font_size,
+							  int           maximum_acceptable_font_size)
+{
+	int i;
+	int width;
+	PangoLayout *layout;
+	PangoFontDescription *font;
+
+	g_return_val_if_fail (text != NULL, 0);
+	g_return_val_if_fail (text[0] != '\0', 0);
+	g_return_val_if_fail (available_width > 0, 0);
+	g_return_val_if_fail (minimum_acceptable_font_size > 0, 0);
+	g_return_val_if_fail (maximum_acceptable_font_size > 0, 0);
+	g_return_val_if_fail (maximum_acceptable_font_size > minimum_acceptable_font_size, 0);
+
+	layout = pango_layout_new (context);
+	pango_layout_set_text (layout, text, -1);
+	pango_layout_set_font_description (layout, font_desc);
+	
+	font = pango_font_description_new ();
+
+	for (i = maximum_acceptable_font_size; i >= minimum_acceptable_font_size; i--) {
+
+		pango_font_description_set_size (font, i * PANGO_SCALE);
+		pango_layout_set_font_description (layout, font);
+		pango_layout_get_pixel_size (layout, &width, NULL);
+
+		if (width <= available_width) {
+			pango_font_description_free (font);
+			g_object_unref (layout);
+			return i;
+		}
+	}
+
+	pango_font_description_free (font);
+	g_object_unref (layout);
+	return i;
+}
+
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+static PangoContext *
+eel_create_bogus_test_pango_context (void)
+{
+	PangoContext *context;
+	
+	context = gdk_pango_context_get ();
+	pango_context_set_language (context, gtk_get_default_language ());
+	
+	return context;
+}
+
+/* Testing string truncation is tough because we do not know what font/
+ * font metrics to expect on a given system. To work around this we use
+ * a substring of the original, measure it's length using the given font, 
+ * add the length of the "..." string and use that for truncation.
+ * The result should then be the substring prepended with a "..."
+ */
+static char *
+eel_self_check_ellipsize (const char *string, const char *truncate_to_length_string, EelEllipsizeMode mode)
+{
+	PangoContext *context;
+	int truncation_length;
+	char *result;
+	PangoLayout *layout;
+	
+	context = eel_create_bogus_test_pango_context ();
+	layout = pango_layout_new (context);
+	
+	/* measure the length we want to truncate to */
+	truncation_length = measure_string_width (truncate_to_length_string, layout);
+
+	eel_pango_layout_set_text_ellipsized (layout, string, truncation_length, mode);
+
+	result = g_strdup (pango_layout_get_text (layout));
+	
+	g_object_unref (G_OBJECT (context));
+	g_object_unref (G_OBJECT (layout));
+
+	return result;
+}
+
+static char *
+eel_self_check_ellipsize_start (const char *string, const char *truncate_to_length_string)
+{
+	return eel_self_check_ellipsize (string, truncate_to_length_string, EEL_ELLIPSIZE_START);
+}
+
+static char *
+eel_self_check_ellipsize_middle (const char *string, const char *truncate_to_length_string)
+{
+	return eel_self_check_ellipsize (string, truncate_to_length_string, EEL_ELLIPSIZE_MIDDLE);
+}
+
+static char *
+eel_self_check_ellipsize_end (const char *string, const char *truncate_to_length_string)
+{
+	return eel_self_check_ellipsize (string, truncate_to_length_string, EEL_ELLIPSIZE_END);
+}
+
+void
+eel_self_check_pango_extensions (void)
+{
+	PangoContext *context;
+
+	/* used to test ellipsize routines */
+	context = eel_create_bogus_test_pango_context ();
+
+	/* Turned off these tests because they are failing for me and I
+	 * want the release to be able to pass "make check". We'll have
+	 * to revisit this at some point. The failures started because
+	 * I changed my default font and enabled Xft support. I presume
+	 * this is simply the "fuzziness" Havoc mentions in his comments
+	 * above.
+	 *                                                - Darin
+	 */
+
+    if (0) {
+	/* eel_string_ellipsize_start */
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_start ("012345678", "0012345678"), "012345678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_start ("012345678", "012345678"), "012345678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_start ("012345678", "...45678"), "...45678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_start ("012345678", "...5678"), "...5678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_start ("012345678", "...678"), "...678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_start ("012345678", "...78"), "...78");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_start ("012345678", "...8"), "...8");
+
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("012345678", "0123456789"), "012345678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("012345678", "012345678"), "012345678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("012345678", "012...78"), "012...78");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("012345678", "01...78"), "01...78");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("012345678", "01...8"), "01...8");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("012345678", "0...8"), "0...8");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("012345678", "0..."), "0...");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("0123456789", "0123456789"), "0123456789");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("0123456789", "012...789"), "012...789");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("0123456789", "012...89"), "012...89");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("0123456789", "01...89"), "01...89");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("0123456789", "01...9"), "01...9");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("0123456789", "0...9"), "0...9");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_middle ("0123456789", "0..."), "0...");
+
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_end ("012345678", "0123456789"), "012345678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_end ("012345678", "012345678"), "012345678");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_end ("012345678", "01234..."), "01234...");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_end ("012345678", "0123..."), "0123...");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_end ("012345678", "012..."), "012...");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_end ("012345678", "01..."), "01...");
+	EEL_CHECK_STRING_RESULT (eel_self_check_ellipsize_end ("012345678", "0..."), "0...");
+    }
+
+	g_object_unref (context);
+}
+
+#endif /* !EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-pango-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-pango-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,54 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-pango-extensions.h - interface for new functions that conceptually
+                            belong in pango. Perhaps some of these will be
+                            actually rolled into pango someday.
+
+   Copyright (C) 2001 Anders Carlsson
+
+   The Eel 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 Eel 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 Eel 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.
+
+   Authors: Anders Carlsson <andersca gnu org>
+*/
+
+#ifndef EEL_PANGO_EXTENSIONS_H
+#define EEL_PANGO_EXTENSIONS_H
+
+#include <pango/pango.h>
+
+typedef enum {
+	EEL_ELLIPSIZE_START,
+	EEL_ELLIPSIZE_MIDDLE,
+	EEL_ELLIPSIZE_END
+} EelEllipsizeMode;
+
+PangoAttrList *eel_pango_attr_list_copy_or_create         (PangoAttrList    *attr_list);
+PangoAttrList *eel_pango_attr_list_apply_global_attribute (PangoAttrList    *attr_list,
+							   PangoAttribute   *attr);
+int eel_pango_font_description_get_largest_fitting_font_size (const PangoFontDescription *font_desc,
+							      PangoContext               *context,
+							      const char                 *text,
+							      int                         available_width,
+							      int                         minimum_acceptable_font_size,
+							      int                         maximum_acceptable_font_size);
+
+/* caution: this function is expensive. */
+void           eel_pango_layout_set_text_ellipsized       (PangoLayout      *layout,
+							   const char       *string,
+							   int               width,
+							   EelEllipsizeMode  mode);
+
+#endif /* EEL_PANGO_EXTENSIONS_H */

Added: trunk/eel/eel-preferences-builder.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-preferences-builder.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,564 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-preferences-glade.c - Some functions to connect a Glade-file to gconf keys.
+
+   Copyright (C) 2002 Jan Arne Petersen
+
+   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.
+
+   Authors: Jan Arne Petersen <jpetersen uni-bonn de>
+*/
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "eel-preferences.h"
+
+#define EEL_PREFERENCES_BUILDER_DATA_KEY "eel_preferences_builder_data_key"
+#define EEL_PREFERENCES_BUILDER_DATA_VALUE "eel_preferences_builder_data_value"
+#define EEL_PREFERENCES_BUILDER_DATA_MAP "eel_preferences_builder_data_map"
+#define EEL_PREFERENCES_BUILDER_DATA_WIDGETS "eel_preferences_builder_data_widgets"
+
+/* helper */
+
+static void
+eel_preferences_builder_combo_box_update (GtkComboBox *combo_box,
+					gpointer value,
+					GCallback change_callback)
+{
+	GHashTable *map;
+	int active;
+	gpointer key;
+
+	map = (GHashTable *) g_object_get_data (G_OBJECT (combo_box),
+						EEL_PREFERENCES_BUILDER_DATA_MAP);
+	active = GPOINTER_TO_INT (g_hash_table_lookup (map, value));
+
+	if (active == -1) {
+		return;
+	}
+
+	key = g_object_get_data (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_KEY);
+
+	g_signal_handlers_block_by_func (combo_box, change_callback, key);
+	gtk_combo_box_set_active (combo_box, active);
+	g_signal_handlers_unblock_by_func (combo_box, change_callback, key);
+}
+
+static void
+eel_preference_glade_never_sensitive (GtkWidget *widget, GtkStateType state)
+{
+	gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE);
+}
+
+static void
+eel_preferences_builder_set_never_sensitive (GtkWidget *widget)
+{
+	gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE);
+	g_signal_connect (G_OBJECT (widget), "state_changed",
+			  G_CALLBACK (eel_preference_glade_never_sensitive),
+			  NULL);
+}
+
+/* bool preference */
+
+static void
+eel_preferences_builder_bool_toggled (GtkToggleButton *toggle_button,
+				    char *key)
+{
+	eel_preferences_set_boolean (key, gtk_toggle_button_get_active (toggle_button));
+}
+
+static void
+eel_preferences_builder_bool_update (GtkToggleButton *toggle_button)
+{
+	gboolean value;
+	gpointer key;
+
+	key = g_object_get_data (G_OBJECT (toggle_button), EEL_PREFERENCES_BUILDER_DATA_KEY);
+
+	value = eel_preferences_get_boolean (key);
+	g_signal_handlers_block_by_func (toggle_button, eel_preferences_builder_bool_toggled, key);
+	gtk_toggle_button_set_active (toggle_button, value);
+	g_signal_handlers_unblock_by_func (toggle_button, eel_preferences_builder_bool_toggled, key);
+}
+
+void
+eel_preferences_builder_connect_bool (GtkBuilder *builder,
+				      const char *component,
+				      const char *key)
+{
+	GtkToggleButton *toggle_button;
+
+	g_return_if_fail (builder != NULL);
+	g_return_if_fail (component != NULL);
+	g_return_if_fail (key != NULL);
+
+	toggle_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, component));
+	g_object_set_data_full (G_OBJECT (toggle_button), EEL_PREFERENCES_BUILDER_DATA_KEY,
+				g_strdup (key), (GDestroyNotify) g_free);
+
+	eel_preferences_add_callback_while_alive (key,
+				      		  (EelPreferencesCallback) eel_preferences_builder_bool_update,
+				      		  toggle_button, G_OBJECT (toggle_button));
+
+	if (!eel_preferences_key_is_writable (key)) {
+		eel_preferences_builder_set_never_sensitive (GTK_WIDGET (toggle_button));
+	}
+	
+	g_signal_connect (G_OBJECT (toggle_button), "toggled",
+			  G_CALLBACK (eel_preferences_builder_bool_toggled),
+			  g_object_get_data (G_OBJECT (toggle_button),
+				  		       EEL_PREFERENCES_BUILDER_DATA_KEY));
+
+	eel_preferences_builder_bool_update (toggle_button);
+}
+
+void
+eel_preferences_builder_connect_bool_slave (GtkBuilder *builder,
+					    const char *component,
+					    const char *key)
+{
+	GtkToggleButton *toggle_button;
+
+	g_return_if_fail (builder != NULL);
+	g_return_if_fail (component != NULL);
+	g_return_if_fail (key != NULL);
+
+	toggle_button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (builder, component));
+
+	if (!eel_preferences_key_is_writable (key)) {
+		eel_preferences_builder_set_never_sensitive (GTK_WIDGET (toggle_button));
+	}
+	
+	g_signal_connect_data (G_OBJECT (toggle_button), "toggled",
+			       G_CALLBACK (eel_preferences_builder_bool_toggled),
+			       g_strdup (key), (GClosureNotify) g_free, 0);
+}
+
+/* string enum (ComboBox) preference */
+
+static void
+eel_preferences_builder_string_enum_combo_box_changed (GtkComboBox *combo_box,
+						     char *key)
+{
+	int active;
+	char **values;
+	int i;
+
+	active = gtk_combo_box_get_active  (combo_box);
+	values = g_object_get_data (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_VALUE);
+
+	i = 0;
+	while (i < active && values[i] != NULL) {
+		i++;
+	}
+
+	if (values[i] == NULL) {
+		return;
+	}
+
+	eel_preferences_set (key, values[i]);
+}
+
+static void
+eel_preferences_builder_string_enum_combo_box_update (GtkComboBox *combo_box)
+{
+	char *value;
+
+	value = eel_preferences_get (g_object_get_data (G_OBJECT (combo_box),
+							EEL_PREFERENCES_BUILDER_DATA_KEY));
+
+	eel_preferences_builder_combo_box_update (combo_box, value,
+						  G_CALLBACK (eel_preferences_builder_string_enum_combo_box_changed));
+
+	g_free (value);
+}
+
+void
+eel_preferences_builder_connect_string_enum_combo_box (GtkBuilder *builder,
+						       const char *component,
+						       const char *key,
+						       const char **values)
+{
+	GtkWidget *combo_box;
+	GHashTable *map;
+	int i;
+
+	g_return_if_fail (builder != NULL);
+	g_return_if_fail (component != NULL);
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (values != NULL);
+	
+	combo_box = GTK_WIDGET (gtk_builder_get_object (builder, component));
+
+	map = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+
+	for (i = 0; values[i] != NULL; i++) {
+		g_hash_table_insert (map, g_strdup (values[i]), GINT_TO_POINTER (i));
+	}
+
+	g_object_set_data_full (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_MAP, map,
+				(GDestroyNotify) g_hash_table_destroy);
+	g_object_set_data (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_VALUE, values);
+	g_object_set_data_full (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_KEY,
+				g_strdup (key), (GDestroyNotify) g_free);
+
+	eel_preferences_add_callback_while_alive (key,
+				 		  (EelPreferencesCallback) eel_preferences_builder_string_enum_combo_box_update,
+						  combo_box, G_OBJECT (combo_box));
+
+	if (!eel_preferences_key_is_writable (key)) {
+		eel_preferences_builder_set_never_sensitive (GTK_WIDGET (combo_box));
+	}
+
+	g_signal_connect (G_OBJECT (combo_box), "changed",
+			  G_CALLBACK (eel_preferences_builder_string_enum_combo_box_changed),
+			  g_object_get_data (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_KEY));
+
+	eel_preferences_builder_string_enum_combo_box_update (GTK_COMBO_BOX (combo_box));
+}
+
+void
+eel_preferences_builder_connect_string_enum_combo_box_slave (GtkBuilder *builder,
+							     const char *component,
+							     const char *key)
+{
+	GtkWidget *combo_box;
+
+	g_return_if_fail (builder != NULL);
+	g_return_if_fail (component != NULL);
+	g_return_if_fail (key != NULL);
+	
+	combo_box = GTK_WIDGET (gtk_builder_get_object (builder, component));
+
+	g_assert (g_object_get_data (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_MAP) != NULL);
+
+	if (!eel_preferences_key_is_writable (key)) {
+		eel_preferences_builder_set_never_sensitive (GTK_WIDGET (combo_box));
+	}
+
+	g_signal_connect_data (G_OBJECT (combo_box), "changed",
+			       G_CALLBACK (eel_preferences_builder_string_enum_combo_box_changed),
+			       g_strdup (key), (GClosureNotify) g_free, 0);
+}
+
+
+/* int enum preference */
+
+static void
+eel_preferences_builder_uint_enum_changed (GtkComboBox *combo_box,
+					   char *key)
+{
+	int active;
+	GSList *value_list;
+	int i;
+
+	active = gtk_combo_box_get_active (combo_box);
+	value_list = (GSList *) g_object_get_data (G_OBJECT (combo_box),
+						   EEL_PREFERENCES_BUILDER_DATA_VALUE);
+
+	i = 0;
+	while (i < active && value_list->next != NULL) {
+		i++;
+		value_list = value_list->next;
+	}
+
+	eel_preferences_set_uint (key, GPOINTER_TO_UINT (value_list->data));
+}
+
+static void
+eel_preferences_builder_uint_enum_update (GtkComboBox *combo_box)
+{
+	guint value;
+
+	value = eel_preferences_get_uint (g_object_get_data (G_OBJECT (combo_box),
+							     EEL_PREFERENCES_BUILDER_DATA_KEY));
+
+	eel_preferences_builder_combo_box_update (combo_box, GUINT_TO_POINTER (value),
+						  G_CALLBACK (eel_preferences_builder_uint_enum_changed));
+}
+
+void
+eel_preferences_builder_connect_uint_enum (GtkBuilder  *builder,
+					   const char  *component,
+					   const char  *key,
+					   const guint *values,
+					   int          num_values)
+{
+	GHashTable *map;
+	int i;
+	guint value;
+	GtkComboBox *combo_box;
+	GSList *value_list;
+
+	g_return_if_fail (builder != NULL);
+	g_return_if_fail (component != NULL);
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (values != NULL);
+	
+	combo_box = GTK_COMBO_BOX (gtk_builder_get_object (builder, component));
+
+	map = g_hash_table_new (g_direct_hash, g_direct_equal);
+	value_list = NULL;
+
+	for (i = 0; i < num_values; i++) {
+		value = values[i];
+		value_list = g_slist_append (value_list, GUINT_TO_POINTER (value));
+		g_hash_table_insert (map, GUINT_TO_POINTER (value), GINT_TO_POINTER (i));
+	}
+
+	g_object_set_data_full (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_MAP, map,
+				(GDestroyNotify) g_hash_table_destroy);
+	g_object_set_data_full (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_VALUE, value_list,
+				(GDestroyNotify) g_slist_free);
+	g_object_set_data_full (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_KEY,
+				g_strdup (key), (GDestroyNotify) g_free);
+
+	if (!eel_preferences_key_is_writable (key)) {
+		eel_preferences_builder_set_never_sensitive (GTK_WIDGET (combo_box));
+	}
+
+	g_signal_connect (G_OBJECT (combo_box), "changed",
+			  G_CALLBACK (eel_preferences_builder_uint_enum_changed),
+			  g_object_get_data (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_KEY));
+
+	eel_preferences_add_callback_while_alive (key,
+				 		  (EelPreferencesCallback) eel_preferences_builder_uint_enum_update,
+						  combo_box, G_OBJECT (combo_box));
+
+	eel_preferences_builder_uint_enum_update (combo_box);
+}
+
+
+/* String Enum (RadioButton) preference */
+
+static void
+eel_preferences_builder_string_enum_radio_button_toggled (GtkToggleButton *toggle_button,
+							char *key)
+{
+	if (gtk_toggle_button_get_active (toggle_button) == FALSE) {
+		return;
+	}
+
+	eel_preferences_set (key,
+			     g_object_get_data (G_OBJECT (toggle_button),
+				     		EEL_PREFERENCES_BUILDER_DATA_VALUE));
+}
+
+static void
+eel_preferences_builder_string_enum_radio_button_update (GtkWidget *widget)
+{
+	gpointer key;
+	char *value;
+	GHashTable *map;
+	gpointer object;
+
+	key = g_object_get_data (G_OBJECT (widget), EEL_PREFERENCES_BUILDER_DATA_KEY);
+	value = eel_preferences_get (key);
+	map = g_object_get_data (G_OBJECT (widget), EEL_PREFERENCES_BUILDER_DATA_MAP);
+	object = g_hash_table_lookup (map, value);
+	g_free (value);
+	if (object == NULL) {
+		return;
+	}
+
+	g_signal_handlers_block_by_func (widget,
+					 eel_preferences_builder_string_enum_radio_button_toggled,
+					 key);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (object), TRUE);
+	g_signal_handlers_unblock_by_func (widget,
+					   eel_preferences_builder_string_enum_radio_button_toggled,
+					   key);
+}
+
+void
+eel_preferences_builder_connect_string_enum_radio_button (GtkBuilder *builder,
+							  const char **components,
+							  const char *key,
+							  const char **values)
+{
+	GHashTable *map;
+	int i;
+	GtkWidget *widget;
+	gboolean writable;
+
+	g_return_if_fail (builder != NULL);
+	g_return_if_fail (components != NULL);
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (values != NULL);
+
+	map = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+
+	writable = eel_preferences_key_is_writable (key);
+
+	widget = NULL;
+	for (i = 0; components[i] != NULL && values[i] != NULL; i++) {
+		widget = GTK_WIDGET (gtk_builder_get_object (builder, components[i]));
+		g_hash_table_insert (map, g_strdup (values[i]), widget);
+		if (i == 0) {
+			g_object_set_data_full (G_OBJECT (widget),
+						EEL_PREFERENCES_BUILDER_DATA_MAP, map,
+					        (GDestroyNotify) g_hash_table_destroy);
+		} else {
+			g_object_set_data (G_OBJECT (widget),
+					   EEL_PREFERENCES_BUILDER_DATA_MAP, map);
+		}
+		g_object_set_data_full (G_OBJECT (widget),
+					EEL_PREFERENCES_BUILDER_DATA_VALUE, g_strdup (values[i]),
+					(GDestroyNotify) g_free);
+		g_object_set_data_full (G_OBJECT (widget),
+					EEL_PREFERENCES_BUILDER_DATA_KEY, g_strdup (key),
+					(GDestroyNotify) g_free);
+
+		if (!writable) {
+			eel_preferences_builder_set_never_sensitive (widget);
+		}
+
+		g_signal_connect (G_OBJECT (widget), "toggled",
+				  G_CALLBACK (eel_preferences_builder_string_enum_radio_button_toggled),
+				  g_object_get_data (G_OBJECT (widget),
+					  	     EEL_PREFERENCES_BUILDER_DATA_KEY));
+	}
+
+	eel_preferences_add_callback_while_alive (key,
+						  (EelPreferencesCallback) eel_preferences_builder_string_enum_radio_button_update,
+						  widget, G_OBJECT (widget));
+
+	eel_preferences_builder_string_enum_radio_button_update (widget);
+}
+
+
+/* list enum preference */
+
+static void
+eel_preferences_builder_list_enum_changed (GtkComboBox *combo_box,
+					 char *key)
+{
+	GSList *widgets;
+	int active;
+	char **values;
+	int i;
+	GPtrArray *v;
+
+	widgets = g_object_get_data (G_OBJECT (combo_box), EEL_PREFERENCES_BUILDER_DATA_WIDGETS);
+
+	v = g_ptr_array_new ();
+	for (; widgets != NULL; widgets = widgets->next) {
+		active = gtk_combo_box_get_active (GTK_COMBO_BOX (widgets->data));
+		values = g_object_get_data (G_OBJECT (combo_box),
+					    EEL_PREFERENCES_BUILDER_DATA_VALUE);
+
+		i = 0;
+		while (i < active && values[i] != NULL) {
+			i++;
+		}
+
+		if (values[i] != NULL) {
+			g_ptr_array_add (v, values[i]);
+		}
+	}
+	g_ptr_array_add (v, NULL);
+
+	eel_preferences_set_string_array (key, (char **) v->pdata);
+	g_ptr_array_free (v, TRUE);
+}
+
+static void
+eel_preferences_builder_list_enum_update (GtkWidget *widget)
+{
+	char **values;
+	GSList *components;
+	int i;
+
+	values = eel_preferences_get_string_array (g_object_get_data (G_OBJECT (widget),
+								      EEL_PREFERENCES_BUILDER_DATA_KEY));
+	components = g_object_get_data (G_OBJECT (widget), EEL_PREFERENCES_BUILDER_DATA_WIDGETS);
+	for (i = 0; values[i] != NULL && components != NULL; i++, components = components->next) {
+		eel_preferences_builder_combo_box_update (GTK_COMBO_BOX (components->data), 
+							values[i],
+							G_CALLBACK (eel_preferences_builder_list_enum_changed));
+	}
+
+	g_strfreev (values);
+}
+
+void 
+eel_preferences_builder_connect_list_enum (GtkBuilder *builder,
+					   const char **components,
+					   const char *key,
+					   const char **values)
+{
+	GtkWidget *combo_box;
+	GHashTable *map;
+	int i;
+	GSList *widgets;
+	gboolean writable;
+
+ 	g_return_if_fail (builder != NULL);
+	g_return_if_fail (components != NULL);
+	g_return_if_fail (key != NULL);
+	g_return_if_fail (values != NULL);
+	
+	map = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+
+	for (i = 0; values[i] != NULL; i++) {
+		g_hash_table_insert (map, g_strdup (values[i]), GINT_TO_POINTER (i));
+	}
+
+	writable = eel_preferences_key_is_writable (key);
+
+	combo_box = NULL;
+	widgets = NULL;
+	for (i = 0; components[i] != NULL; i++) {
+		combo_box = GTK_WIDGET (gtk_builder_get_object (builder, components[i]));
+		widgets = g_slist_append (widgets, combo_box);
+		if (i == 0) {
+			g_object_set_data_full (G_OBJECT (combo_box),
+						EEL_PREFERENCES_BUILDER_DATA_MAP, map,
+						(GDestroyNotify) g_hash_table_destroy);
+			g_object_set_data_full (G_OBJECT (combo_box),
+						EEL_PREFERENCES_BUILDER_DATA_WIDGETS,
+						widgets, (GDestroyNotify) g_slist_free);
+		} else {
+			g_object_set_data (G_OBJECT (combo_box),
+					   EEL_PREFERENCES_BUILDER_DATA_MAP, map);
+			g_object_set_data (G_OBJECT (combo_box),
+					   EEL_PREFERENCES_BUILDER_DATA_WIDGETS, widgets);
+		}
+		g_object_set_data (G_OBJECT (combo_box),
+				   EEL_PREFERENCES_BUILDER_DATA_VALUE, values);
+		g_object_set_data_full (G_OBJECT (combo_box),
+				        EEL_PREFERENCES_BUILDER_DATA_KEY, g_strdup (key),
+					(GDestroyNotify) g_free);
+
+		if (!writable) {
+			eel_preferences_builder_set_never_sensitive (combo_box);
+		}
+
+		g_signal_connect (G_OBJECT (combo_box), "changed",
+			  	  G_CALLBACK (eel_preferences_builder_list_enum_changed),
+				  g_object_get_data (G_OBJECT (combo_box),
+					  	     EEL_PREFERENCES_BUILDER_DATA_KEY));
+	}
+
+	eel_preferences_add_callback_while_alive (key,
+						  (EelPreferencesCallback) eel_preferences_builder_list_enum_update,
+						  combo_box, G_OBJECT (combo_box));
+	
+	eel_preferences_builder_list_enum_update (combo_box);
+}
+

Added: trunk/eel/eel-preferences.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-preferences.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,1701 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-preferences.c - Preference peek/poke/notify implementation.
+
+   Copyright (C) 1999, 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-preferences.h"
+
+#include "eel-debug.h"
+#include "eel-gconf-extensions.h"
+#include "eel-lib-self-check-functions.h"
+#include "eel-enumeration.h"
+#include "eel-glib-extensions.h"
+#include "eel-string.h"
+#include <gconf/gconf-client.h>
+#include <gconf/gconf.h>
+#include <gtk/gtk.h>
+
+/* An enumeration used for updating auto-storage variables in a type-specific way. 
+ * FIXME: there is another enumeration like this in eel-global-preferences.c,
+ * used for different purposes but in a related way. Should we combine them?
+ */
+typedef enum {
+	PREFERENCE_BOOLEAN = 1,
+	PREFERENCE_INTEGER,
+	PREFERENCE_STRING,
+	PREFERENCE_STRING_ARRAY,
+	PREFERENCE_STRING_ARRAY_AS_QUARKS
+} PreferenceType;
+
+/*
+ * PreferencesEntry:
+ *
+ * A structure to manage preference hash table nodes.
+ * Preferences are hash tables.  The hash key is the preference name
+ * (a string).  The  hash value is a pointer of the following struct:
+ */
+typedef struct {
+	char *name;
+	char *description;
+	PreferenceType type;
+	gboolean invisible;
+	GList *callback_list;
+	GList *auto_storage_list;
+	int gconf_connection_id;
+	char *enumeration_id;
+	GConfValue *fallback;
+} PreferencesEntry;
+
+/*
+ * PreferencesCallbackEntry:
+ *
+ * A structure to manage callback lists.  A callback list is a GList.
+ * The callback_data in each list node is a pointer to the following 
+ * struct:
+ */
+typedef struct {
+	EelPreferencesCallback callback;
+	gpointer callback_data;
+} PreferencesCallbackEntry;
+
+static GHashTable *global_table = NULL;
+static char *storage_path = NULL;
+static gboolean initialized = FALSE;
+
+static void              preferences_global_table_free             (void);
+static char *            preferences_key_make                      (const char               *name);
+static void              preferences_callback_entry_free           (PreferencesCallbackEntry *callback_entry);
+static void              preferences_entry_update_auto_storage     (PreferencesEntry         *entry);
+static PreferencesEntry *preferences_global_table_lookup_or_insert (const char               *name);
+
+static int
+preferences_gconf_value_get_int (const GConfValue *value)
+{
+	if (value == NULL) {
+		return 0;
+	}
+	g_assert (value->type == GCONF_VALUE_INT);
+	return gconf_value_get_int (value);
+}
+
+static gboolean
+preferences_gconf_value_get_bool (const GConfValue *value)
+{
+	if (value == NULL) {
+		return FALSE;
+	}
+	g_assert (value->type == GCONF_VALUE_BOOL);
+	return gconf_value_get_bool (value);
+}
+
+static char *
+preferences_gconf_value_get_string (const GConfValue *value)
+{
+	if (value == NULL) {
+		return g_strdup ("");
+	}
+	g_assert (value->type == GCONF_VALUE_STRING);
+	return g_strdup (gconf_value_get_string (value));
+}
+
+static char **
+preferences_gconf_value_get_string_array (const GConfValue *value)
+{
+	GSList *slist, *l;
+	GPtrArray *result;
+
+	if (value == NULL) {
+		return NULL;
+	}
+
+	g_assert (value->type == GCONF_VALUE_LIST);
+	g_assert (gconf_value_get_list_type (value) == GCONF_VALUE_STRING);
+
+	slist = eel_gconf_value_get_string_list (value);
+
+	result = g_ptr_array_new ();
+	for (l = slist; l != NULL; l = l->next) {
+		g_ptr_array_add (result, l->data);
+	}
+	g_slist_free (slist);
+	g_ptr_array_add (result, NULL);
+
+ 	return (char **) g_ptr_array_free (result, FALSE);
+}
+
+static const char *
+preferences_peek_storage_path (void)
+{
+	g_assert (storage_path != NULL);
+
+	return storage_path;
+}
+
+static void
+preferences_set_storage_path (const char *new_storage_path)
+{
+	g_assert (eel_strlen (new_storage_path) > 0);
+
+	/* Make sure the path is indeed different */
+	if (eel_str_is_equal (new_storage_path, storage_path)) {
+		return;
+	}
+
+	/* Free the preference hash table */
+	preferences_global_table_free ();
+
+	/* Stop monitoring the old path */
+	eel_gconf_monitor_remove (storage_path);
+
+	g_free (storage_path);
+	storage_path = g_strdup (new_storage_path);
+
+	/* Start monitoring the new path */
+	eel_gconf_monitor_add (storage_path);
+}
+
+static gboolean
+preferences_is_initialized (void)
+{
+	return initialized;
+}
+
+static GConfValue *
+preferences_get_value (const char *name)
+{
+	GConfValue *result;
+	char *key;
+	PreferencesEntry *entry;
+	
+	g_assert (name != NULL);
+	g_assert (preferences_is_initialized ());
+
+	key = preferences_key_make (name);
+	result = eel_gconf_get_value (key);
+	g_free (key);
+
+	if (result == NULL) {		
+		entry = preferences_global_table_lookup_or_insert (name);
+
+		if (entry->fallback)
+			result = gconf_value_copy (entry->fallback);
+	}
+	
+	return result;
+}
+
+/* If the preference name begind with a "/", we interpret 
+ * it as a straight gconf key. */
+static gboolean
+preferences_preference_is_gconf_key (const char *name)
+{
+	g_assert (name != NULL);
+	
+	if (eel_str_has_prefix (name, "/")) {
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+static char *
+preferences_key_make (const char *name)
+{
+	g_assert (name != NULL);
+	
+	if (!preferences_preference_is_gconf_key (name)) {
+		return g_strdup (name);
+	}
+
+	/* Otherwise, we prefix it with the path */
+	return g_strconcat (preferences_peek_storage_path (), "/",
+			    name, NULL);
+}
+
+/* Get default from schema or emergency fallback */
+static GConfValue *
+preferences_get_default_value (const char *name)
+{
+	GConfValue *result;
+	PreferencesEntry *entry;
+	char *key;
+
+	g_assert (name != NULL);
+
+	key = preferences_key_make (name);
+
+	result = eel_gconf_get_default_value (key);
+
+	g_free (key);
+
+	if (result == NULL) {
+		entry = preferences_global_table_lookup_or_insert (name);
+		if (entry && entry->fallback)
+			result = gconf_value_copy (entry->fallback);
+	}
+	
+        return result;
+}
+
+static int
+preferences_callback_entry_compare (gconstpointer a,
+				    gconstpointer b)
+{
+	const PreferencesCallbackEntry *a_entry = a;
+	const PreferencesCallbackEntry *b_entry = b;
+
+	if (a_entry->callback < b_entry->callback) {
+		return -1;
+	}
+
+	if (a_entry->callback > b_entry->callback) {
+		return +1;
+	}
+
+	if (a_entry->callback_data < b_entry->callback_data) {
+		return -1;
+	}
+
+	if (a_entry->callback_data > b_entry->callback_data) {
+		return +1;
+	}
+
+	return 0;
+}
+
+/* Public preferences functions */
+
+gboolean
+eel_preferences_get_is_invisible (const char *name)
+{
+	g_assert (name != NULL);
+	g_assert (preferences_is_initialized ());
+
+	return preferences_global_table_lookup_or_insert (name)->invisible;
+}
+
+void
+eel_preferences_set_is_invisible (const char *name,
+				  gboolean is_invisible)
+{
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	preferences_global_table_lookup_or_insert (name)->invisible = is_invisible;
+}
+
+void
+eel_preferences_set_boolean (const char *name,
+			     gboolean boolean_value)
+{
+	char *key;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+	
+	key = preferences_key_make (name);
+	eel_gconf_set_boolean (key, boolean_value);
+	g_free (key);
+
+	eel_gconf_suggest_sync ();
+}
+
+gboolean
+eel_preferences_get_boolean (const char *name)
+{
+ 	gboolean result;
+	GConfValue *value;
+
+	g_return_val_if_fail (name != NULL, 0);
+	g_return_val_if_fail (preferences_is_initialized (), 0);
+
+	value = preferences_get_value (name);
+	result = preferences_gconf_value_get_bool (value);
+	eel_gconf_value_free (value);
+	
+	return result;
+}
+
+void
+eel_preferences_set_integer (const char *name,
+			     int int_value)
+{
+	char *key;
+	int old_value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+	
+	key = preferences_key_make (name);
+	old_value = eel_preferences_get_integer (name);
+	
+	if (int_value != old_value) {
+		eel_gconf_set_integer (key, int_value);
+	}
+	g_free (key);
+}
+
+int
+eel_preferences_get_integer (const char *name)
+{
+ 	int result;
+	GConfValue *value;
+
+	g_return_val_if_fail (name != NULL, 0);
+	g_return_val_if_fail (preferences_is_initialized (), 0);
+
+	value = preferences_get_value (name);
+	result = preferences_gconf_value_get_int (value);
+	eel_gconf_value_free (value);
+
+	return result;
+}
+
+/* GConf has no unsigned store, save as signed and cast */
+guint
+eel_preferences_get_uint (const char *name)
+{
+	return (guint)eel_preferences_get_integer (name);
+}
+void
+eel_preferences_set_uint (const char              *name,
+			  guint                    uint_value)
+{
+	eel_preferences_set_integer (name, (int)uint_value);
+}
+
+guint
+eel_preferences_get_enum (const char *name)
+{
+	guint             ret_val;
+	char             *str_value;
+	GConfValue       *value;
+	const EelEnumeration   *enumeration;
+	PreferencesEntry *entry;
+
+	g_return_val_if_fail (name != NULL, 0);
+	g_return_val_if_fail (preferences_is_initialized (), 0);
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_return_val_if_fail (entry != NULL, 0);
+
+	enumeration = eel_enumeration_lookup (entry->enumeration_id);
+
+	if (!enumeration) {
+		g_warning ("No enum entry for '%s' (%s)",
+			   name, entry->enumeration_id);
+		return 0;
+	}
+
+	value = preferences_get_value (name);
+	if (value->type == GCONF_VALUE_INT) { /* compatibility path */
+		ret_val = (guint)preferences_gconf_value_get_int (value);
+		eel_gconf_value_free (value);
+		return ret_val;
+	}
+
+	str_value = preferences_gconf_value_get_string (value);
+	eel_gconf_value_free (value);
+
+	if (str_value == NULL) {
+		g_warning ("No key for '%s' at %s", str_value, name);
+		return 0;
+	}
+
+	ret_val = eel_enumeration_get_value_for_name (enumeration, str_value);
+
+	g_free (str_value);
+
+	return ret_val;
+}
+
+void
+eel_preferences_set_enum (const char *name,
+			  guint       int_value)
+{
+	const char       *str_value;
+	const EelEnumeration   *enumeration;
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_return_if_fail (entry != NULL);
+
+	enumeration = eel_enumeration_lookup (entry->enumeration_id);
+
+	if (!enumeration) {
+		g_warning ("No enum entry for '%s' (%s)",
+			   name, entry->enumeration_id);
+		return;
+	}
+
+	str_value = eel_enumeration_get_name_for_value (enumeration, int_value);
+
+	if (str_value == NULL) {
+		g_warning ("No enum match for '%d'", int_value);
+		return;
+	}
+
+	eel_preferences_set (name, str_value);
+}
+
+void
+eel_preferences_set (const char *name,
+		     const char *string_value)
+{
+	char *key;
+	char *old_value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	key = preferences_key_make (name);
+	old_value = eel_preferences_get (name);
+
+	if (strcmp (string_value, old_value) != 0) {
+		eel_gconf_set_string (key, string_value);
+	}
+	g_free (key);
+	g_free (old_value);
+}
+
+char *
+eel_preferences_get (const char *name)
+{
+ 	char *result;
+	GConfValue *value;
+
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (preferences_is_initialized (), NULL);
+
+	value = preferences_get_value (name);
+	result = preferences_gconf_value_get_string (value);
+	eel_gconf_value_free (value);
+	
+	return result;
+}
+
+void
+eel_preferences_set_string_array (const char  *name,
+				  char       **strv_value)
+{
+	GSList *slist;
+	int i;
+	char *key;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	slist = NULL;
+	if (strv_value != NULL) {
+		for (i = 0; strv_value[i] != NULL; i++) {
+			slist = g_slist_prepend (slist, strv_value[i]);
+		}
+		slist = g_slist_reverse (slist);
+	}
+
+	key = preferences_key_make (name);
+	eel_gconf_set_string_list (key, slist);
+	g_free (key);
+
+	g_slist_free (slist);
+}
+
+static gboolean
+string_array_is_valid (char **strv, const char *enumeration_id)
+{
+	guint i;
+	gboolean res;
+
+	g_assert (strv != NULL);
+	g_assert (enumeration_id != NULL);
+
+	res = TRUE;
+	for (i = 0; strv[i] != NULL; i++) {
+		const EelEnumeration *enumeration;
+
+		enumeration = eel_enumeration_lookup (enumeration_id);
+		if (!enumeration) {
+			res = FALSE;
+			break;
+		}
+
+		if (!eel_enumeration_contains_name (enumeration, strv[i])) {
+			res = FALSE;
+			break;
+		}
+	}
+
+	return res;
+}
+
+char **
+eel_preferences_get_string_array (const char *name)
+{
+ 	char **result;
+	GConfValue *value;
+	PreferencesEntry *entry;
+	GConfValue *default_value;
+
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (preferences_is_initialized (), NULL);
+
+	value = preferences_get_value (name);
+	result = preferences_gconf_value_get_string_array (value);
+	eel_gconf_value_free (value);
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	/* No enumeration_id so we're done */
+	if (entry->enumeration_id == NULL) {
+		return result;
+	}
+
+	/* Do a sanity check on the validity of the values */
+	if (string_array_is_valid (result, entry->enumeration_id)) {
+		return result;
+	}
+
+	/* Forget the bad value and use the default instead */
+	g_strfreev (result);
+
+	default_value = preferences_get_default_value (name);
+	if (default_value) {		
+		result = preferences_gconf_value_get_string_array (default_value);
+		gconf_value_free (default_value);
+	}
+
+	return result;
+}
+
+void
+eel_preferences_unset (const char *name)
+{
+	char *key;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	key = preferences_key_make (name);
+	
+	eel_gconf_unset (key);
+
+	g_free (key);
+}
+
+gboolean
+eel_preferences_key_is_writable (const char *name)
+{
+	gboolean result;
+	char *key;
+	
+	g_return_val_if_fail (name != NULL, 0);
+	g_return_val_if_fail (preferences_is_initialized (), 0);
+
+	key = preferences_key_make (name);
+	result = eel_gconf_key_is_writable (key);
+	g_free (key);
+
+	return result;
+}
+
+/**
+ * preferences_callback_entry_invoke_function
+ *
+ * A function that invokes a callback from the given struct.  It is meant to be fed to 
+ * g_list_foreach ()
+ * @data: The list data privately maintained by the GList.
+ * @callback_data: The callback_data privately maintained by the GList.
+ **/
+static void
+preferences_callback_entry_invoke_function (gpointer data,
+					    gpointer callback_data)
+{
+	PreferencesCallbackEntry *callback_entry;
+	
+	g_assert (data != NULL);
+	
+	callback_entry = data;
+
+ 	(* callback_entry->callback) (callback_entry->callback_data);
+}
+
+static void
+preferences_entry_invoke_callbacks (PreferencesEntry *entry)
+{
+	g_assert (entry != NULL);
+
+	/* Update the auto storage preferences */
+	if (entry->auto_storage_list != NULL) {
+		preferences_entry_update_auto_storage (entry);			
+	}
+	
+	/* Invoke callbacks for this entry if any */
+	if (entry->callback_list != NULL) {
+		g_list_foreach (entry->callback_list,
+				preferences_callback_entry_invoke_function,
+				NULL);
+	}
+}
+
+static void
+update_auto_string (gpointer data, gpointer callback_data)
+{
+	char **storage;
+	const char *value;
+
+	g_assert (data != NULL);
+	g_assert (callback_data != NULL);
+
+	storage = (char **)data;
+	value = (const char *)callback_data;
+
+	g_free (*storage);
+	*(char **)storage = g_strdup (value);
+}
+
+static void
+update_auto_string_array (gpointer data, gpointer callback_data)
+{
+	char ***storage;
+	char **value;
+	
+	g_assert (data != NULL);
+	g_assert (callback_data != NULL);
+
+	storage = (char ***)data;
+	value = (char **)callback_data;
+
+	g_strfreev (*storage);
+	*(char ***)storage = value ? g_strdupv (value) : NULL;
+}
+
+static void
+update_auto_string_array_as_quarks (gpointer data, gpointer callback_data)
+{
+	GQuark **storage;
+	char **value;
+	int i = 0;
+	
+	g_assert (data != NULL);
+	g_assert (callback_data != NULL);
+
+	storage = (GQuark **)data;
+	value = (char **)callback_data;
+
+	g_free (*storage);
+	*storage = g_new (GQuark, g_strv_length (value) + 1);
+
+	if (value != NULL) {
+		for (i = 0; value[i] != NULL; ++i) {
+			(*storage)[i] = g_quark_from_string (value[i]);
+		}
+	}
+	(*storage)[i] = 0;
+}
+
+static void
+update_auto_integer_or_boolean (gpointer data, gpointer callback_data)
+{
+	g_assert (data != NULL);
+
+	*(int *)data = GPOINTER_TO_INT (callback_data);
+}
+
+static void
+preferences_entry_update_auto_storage (PreferencesEntry *entry)
+{
+	char *new_string_value;
+	char **new_string_array_value;
+	int new_int_value;
+	guint new_uint_value;
+	gboolean new_boolean_value;
+
+	switch (entry->type) {
+	case PREFERENCE_STRING:
+		if (entry->enumeration_id != NULL) {
+			new_uint_value = eel_preferences_get_enum (entry->name);
+			g_list_foreach (entry->auto_storage_list,
+					update_auto_integer_or_boolean,
+					GINT_TO_POINTER (new_uint_value));
+		} else {
+			new_string_value = eel_preferences_get (entry->name);
+			g_list_foreach (entry->auto_storage_list,
+					update_auto_string,
+					new_string_value);
+			g_free (new_string_value);
+		}
+		break;
+	case PREFERENCE_STRING_ARRAY:
+		new_string_array_value = eel_preferences_get_string_array (entry->name);
+		g_list_foreach (entry->auto_storage_list,
+				update_auto_string_array,
+				new_string_array_value);
+		g_strfreev (new_string_array_value);
+		break;
+	case PREFERENCE_STRING_ARRAY_AS_QUARKS:
+		new_string_array_value = eel_preferences_get_string_array (entry->name);
+		g_list_foreach (entry->auto_storage_list,
+				update_auto_string_array_as_quarks,
+				new_string_array_value);
+		g_strfreev (new_string_array_value);
+		break;
+	case PREFERENCE_INTEGER:
+		new_int_value = eel_preferences_get_integer (entry->name);
+		g_list_foreach (entry->auto_storage_list,
+				update_auto_integer_or_boolean,
+				GINT_TO_POINTER (new_int_value));
+		break;
+	case PREFERENCE_BOOLEAN:
+		new_boolean_value = eel_preferences_get_boolean (entry->name);
+		g_list_foreach (entry->auto_storage_list,
+				update_auto_integer_or_boolean,
+				GINT_TO_POINTER (new_boolean_value));
+		break;
+	default:
+		g_warning ("unexpected preferences type %d in preferences_entry_update_auto_storage", entry->type);
+	}
+}
+
+static void
+preferences_something_changed_notice (GConfClient *client, 
+				      guint connection_id, 
+				      GConfEntry *entry, 
+				      gpointer notice_data)
+{
+	g_assert (entry != NULL);
+	g_assert (entry->key != NULL);
+	g_assert (notice_data != NULL);
+
+	preferences_entry_invoke_callbacks (notice_data);
+}
+
+static void
+preferences_entry_ensure_gconf_connection (PreferencesEntry *entry)
+{
+	char *key;
+	
+	/*
+	 * We install only one gconf notification for each preference entry.
+	 * Otherwise, we would invoke the installed callbacks more than once
+	 * per registered callback.
+	 */
+	if (entry->gconf_connection_id != EEL_GCONF_UNDEFINED_CONNECTION) {
+		return;
+	}
+		
+	g_assert (entry->name != NULL);
+
+	key = preferences_key_make (entry->name);
+
+	entry->gconf_connection_id = eel_gconf_notification_add (key,
+								 preferences_something_changed_notice,
+								 entry);
+	g_free (key);
+
+	g_assert (entry->gconf_connection_id != EEL_GCONF_UNDEFINED_CONNECTION);
+}
+
+/**
+ * preferences_entry_add_callback
+ *
+ * Add a callback to a pref node.  Callbacks are fired whenever
+ * the pref value changes.
+ * @preferences_entry: The hash node.
+ * @callback: The user-supplied callback.
+ * @callback_data: The user-supplied closure.
+ **/
+static void
+preferences_entry_add_callback (PreferencesEntry *entry,
+				EelPreferencesCallback callback,
+				gpointer callback_data)
+{
+	PreferencesCallbackEntry *callback_entry;
+	GList *l;
+
+	g_assert (entry != NULL);
+	g_assert (callback != NULL);
+
+	callback_entry = g_new0 (PreferencesCallbackEntry, 1);
+	callback_entry->callback = callback;
+	callback_entry->callback_data = callback_data;
+
+	l = g_list_find_custom (entry->callback_list, callback_entry, preferences_callback_entry_compare);
+	if (l == NULL) {
+		entry->callback_list = g_list_append (entry->callback_list, callback_entry);
+		preferences_entry_ensure_gconf_connection (entry);
+	} else {
+		g_warning ("Trying to add a callback for %s that already exists.", entry->name);
+	}
+}
+
+/**
+ * preferences_entry_add_auto_storage
+ *
+ * Add an auto-storage variable to a pref node.  The variable will
+ * be updated to match the pref value whenever the pref 
+ * the pref value changes.
+ * @preferences_entry: The hash node.
+ * @storage: The user-supplied location at which to store the value.
+ * @type: Which type of variable this is.
+ **/
+static void
+preferences_entry_add_auto_storage (PreferencesEntry *entry,
+				    gpointer storage,
+				    PreferenceType type)
+{
+	g_assert (entry != NULL);
+	g_assert (storage != NULL);
+	g_assert (entry->type == 0 || entry->type == type);
+	if (g_list_find (entry->auto_storage_list, storage) != NULL) {
+		g_warning ("Trying to add an auto storage for %s that already exists.", entry->name);
+		return;
+	}
+
+	entry->type = type;
+	
+	entry->auto_storage_list = g_list_append (entry->auto_storage_list, storage);
+
+	preferences_entry_ensure_gconf_connection (entry);
+}
+
+static void
+preferences_entry_check_remove_connection (PreferencesEntry *entry)
+{
+	/*
+	 * If there are no callbacks or auto-storage variables left in the entry, 
+	 * remove the gconf notification.
+	 */
+	if (entry->callback_list != NULL || entry->auto_storage_list != NULL) {
+		return;
+	}
+
+	eel_gconf_notification_remove (entry->gconf_connection_id);
+	entry->gconf_connection_id = EEL_GCONF_UNDEFINED_CONNECTION;
+}
+
+/**
+ * preferences_entry_remove_callback
+ *
+ * remove a callback from a pref entry.  Both the callback and the callback_data must
+ * match in order for a callback to be removed from the entry.
+ * @preferences_entry: The hash entry.
+ * @callback: The user-supplied callback.
+ * @callback_data: The user-supplied closure.
+ **/
+static void
+preferences_entry_remove_callback (PreferencesEntry *entry,
+				   EelPreferencesCallback callback,
+				   gpointer callback_data)
+{
+	PreferencesCallbackEntry cb_entry;
+	GList *l;
+
+	g_assert (entry != NULL);
+	g_assert (callback != NULL);
+
+	cb_entry.callback = callback;
+	cb_entry.callback_data = callback_data;
+
+	l = g_list_find_custom (entry->callback_list, &cb_entry, preferences_callback_entry_compare);
+	if (l != NULL) {
+		preferences_callback_entry_free (l->data);
+		entry->callback_list = g_list_delete_link (entry->callback_list, l);
+		preferences_entry_check_remove_connection (entry);
+	} else {
+		g_warning ("Trying to remove a callback for %s without adding it first.", entry->name);
+	}
+
+	g_assert (g_list_find_custom (entry->callback_list, &cb_entry, preferences_callback_entry_compare) == NULL);
+}
+
+/**
+ * preferences_entry_remove_auto_storage
+ *
+ * remove an auto-storage variable from a pref entry.
+ * @preferences_entry: The hash entry.
+ * @storage: The user-supplied location.
+ **/
+static void
+preferences_entry_remove_auto_storage (PreferencesEntry *entry,
+				       gpointer storage)
+{
+	GList *new_list;
+	const GList *node;
+	gpointer storage_in_entry;
+
+	g_assert (entry != NULL);
+	g_assert (storage != NULL);
+	g_assert (entry->auto_storage_list != NULL);
+	
+	new_list = g_list_copy (entry->auto_storage_list);
+	
+	for (node = new_list; node != NULL; node = node->next) {
+		storage_in_entry = node->data;
+		
+		g_assert (storage_in_entry != NULL);
+		
+		if (storage_in_entry == storage) {
+			entry->auto_storage_list = g_list_remove (entry->auto_storage_list, 
+							          storage);
+
+			switch (entry->type) {
+			case PREFERENCE_STRING:
+				update_auto_string (storage, NULL);
+				break;
+			case PREFERENCE_STRING_ARRAY:
+				update_auto_string_array (storage, NULL);
+				break;
+			case PREFERENCE_STRING_ARRAY_AS_QUARKS:
+				update_auto_string_array_as_quarks (storage, NULL);
+				break;
+			case PREFERENCE_BOOLEAN:
+			case PREFERENCE_INTEGER:
+				update_auto_integer_or_boolean (storage, NULL);
+				break;
+			default:
+				g_warning ("unexpected preference type %d in preferences_entry_remove_auto_storage", entry->type);
+			}
+		}
+	}
+
+	g_list_free (new_list);
+
+	preferences_entry_check_remove_connection (entry);
+}
+
+/**
+ * preferences_callback_entry_free
+ *
+ * Free a callback info struct.
+ * @preferences_callback_entry: The struct to free.
+ **/
+static void
+preferences_callback_entry_free (PreferencesCallbackEntry *callback_entry)
+{
+	g_assert (callback_entry != NULL);
+
+	callback_entry->callback = NULL;
+	callback_entry->callback_data = NULL;
+
+	g_free (callback_entry);
+}
+
+/**
+ * preferences_callback_entry_free_func
+ *
+ * A function that frees a callback info struct.  It is meant to be fed to 
+ * g_list_foreach ()
+ * @data: The list data privately maintained by the GList.
+ * @callback_data: The callback_data privately maintained by the GList.
+ **/
+static void
+preferences_callback_entry_free_func (gpointer	data,
+				      gpointer	callback_data)
+{
+	g_assert (data != NULL);
+	
+	preferences_callback_entry_free (data);
+}
+
+/**
+ * preferences_entry_free
+ *
+ * Free a preference hash node's members along with the node itself.
+ * @preferences_hash_node: The node to free.
+ **/
+static void
+preferences_entry_free (PreferencesEntry *entry)
+{
+	g_assert (entry != NULL);
+
+	eel_gconf_notification_remove (entry->gconf_connection_id);
+	entry->gconf_connection_id = EEL_GCONF_UNDEFINED_CONNECTION;
+
+	g_list_free (entry->auto_storage_list);
+	eel_g_list_free_deep_custom (entry->callback_list,
+				     preferences_callback_entry_free_func,
+				     NULL);
+	
+	entry->auto_storage_list = NULL;
+	entry->callback_list = NULL;
+
+	g_free (entry->name);
+	g_free (entry->description);
+	g_free (entry->enumeration_id);
+
+	eel_gconf_value_free (entry->fallback);
+
+	g_free (entry);
+}
+
+/**
+ * preferences_entry_free_func
+ *
+ * A function that frees a pref hash node.  It is meant to be fed to 
+ * g_hash_table_foreach ()
+ * @key: The hash key privately maintained by the GHashTable.
+ * @value: The hash value privately maintained by the GHashTable.
+ * @callback_data: The callback_data privately maintained by the GHashTable.
+ **/
+static void
+preferences_entry_free_func (gpointer key,
+			     gpointer value,
+			     gpointer callback_data)
+{
+	g_assert (value != NULL);
+
+	preferences_entry_free (value);
+}
+
+static void
+preferences_global_table_free (void)
+{
+	if (global_table == NULL) {
+		return;
+	}
+	
+	g_hash_table_foreach (global_table, preferences_entry_free_func, NULL);
+	g_hash_table_destroy (global_table);
+	global_table = NULL;
+
+	g_free (storage_path);
+	storage_path = NULL;
+}
+
+static void
+preferences_uninitialize (void)
+{
+	initialized = FALSE;
+}
+
+static GHashTable *
+preferences_global_table_get_global (void)
+{
+	static gboolean at_exit_handler_added = FALSE;
+
+	if (global_table == NULL) {
+		global_table = g_hash_table_new (g_str_hash, g_str_equal);
+
+		if (!at_exit_handler_added) {
+			at_exit_handler_added = TRUE;
+			eel_debug_call_at_shutdown (preferences_global_table_free);
+			/* ensure that we catch calls to preferences functions after eel shutdown */
+			eel_debug_call_at_shutdown (preferences_uninitialize);
+		}
+	}
+	
+	return global_table;
+}
+
+static PreferencesEntry *
+preferences_global_table_lookup (const char *name)
+{
+	g_assert (name != NULL);
+	g_assert (preferences_global_table_get_global () != NULL);
+	
+	return g_hash_table_lookup (preferences_global_table_get_global (), name);
+}
+
+static PreferencesEntry *
+preferences_global_table_insert (const char *name)
+{
+	PreferencesEntry *entry;
+
+	g_assert (name != NULL);
+	g_assert (preferences_global_table_get_global () != NULL);
+	g_assert (preferences_global_table_lookup (name) == NULL);
+	
+	entry = g_new0 (PreferencesEntry, 1);
+	entry->name = g_strdup (name);
+
+	g_hash_table_insert (preferences_global_table_get_global (), entry->name, entry);
+
+	g_assert (entry == preferences_global_table_lookup (name));
+
+	return entry;
+}
+
+static PreferencesEntry *
+preferences_global_table_lookup_or_insert (const char *name)
+{
+	PreferencesEntry *entry;
+
+	g_assert (name != NULL);
+	
+	entry = preferences_global_table_lookup (name);
+
+	if (entry != NULL) {
+		return entry;
+	}
+
+	entry = preferences_global_table_insert (name);
+	g_assert (entry != NULL);
+
+	return entry;
+}
+
+void
+eel_preferences_add_callback (const char *name,
+			      EelPreferencesCallback callback,
+			      gpointer callback_data)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (callback != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	preferences_entry_add_callback (entry, callback, callback_data);
+}
+
+void
+eel_preferences_add_auto_string (const char *name,
+				 const char **storage)
+{
+	PreferencesEntry *entry;
+	char *value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING);
+
+	value = eel_preferences_get (entry->name);
+	update_auto_string (storage, value);
+	g_free (value);
+}
+
+void
+eel_preferences_add_auto_string_array (const char   *name,
+				       char       ***storage)
+{
+	PreferencesEntry *entry;
+	char **value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING_ARRAY);
+
+	value = eel_preferences_get_string_array (entry->name);
+	update_auto_string_array (storage, value);
+	g_strfreev (value);
+}
+
+void
+eel_preferences_add_auto_string_array_as_quarks (const char *name,
+						 GQuark **storage)
+{
+	PreferencesEntry *entry;
+	char **value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING_ARRAY_AS_QUARKS);
+
+	value = eel_preferences_get_string_array (entry->name);
+	update_auto_string_array_as_quarks (storage, value);
+	g_strfreev (value);
+}
+
+void
+eel_preferences_add_auto_integer (const char *name,
+				  int *storage)
+{
+	PreferencesEntry *entry;
+	int value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	preferences_entry_add_auto_storage (entry, storage, PREFERENCE_INTEGER);
+
+	value = eel_preferences_get_integer (entry->name);
+	update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value));
+}
+
+
+void
+eel_preferences_add_auto_enum (const char *name,
+			       guint *storage)
+{
+	PreferencesEntry *entry;
+	guint value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+	g_assert (entry->enumeration_id != NULL);
+
+	preferences_entry_add_auto_storage (entry, storage, PREFERENCE_STRING);
+
+	value = eel_preferences_get_enum (entry->name);
+	update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value));
+}
+
+void
+eel_preferences_add_auto_boolean (const char *name,
+				  gboolean *storage)
+{
+	PreferencesEntry *entry;
+	gboolean value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	preferences_entry_add_auto_storage (entry, storage, PREFERENCE_BOOLEAN);
+
+	value = eel_preferences_get_boolean (entry->name);
+	update_auto_integer_or_boolean (storage, GINT_TO_POINTER (value));
+}
+
+void
+eel_preferences_remove_auto_string (const char *name,
+				    const char **storage)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup (name);
+	if (entry == NULL) {
+		g_warning ("Trying to remove auto-string for %s without adding it first.", name);
+		return;
+	}
+
+	preferences_entry_remove_auto_storage (entry, storage);
+}
+
+void
+eel_preferences_remove_auto_string_array (const char *name,
+					  char ***storage)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup (name);
+	if (entry == NULL) {
+		g_warning ("Trying to remove auto-string for %s without adding it first.", name);
+		return;
+	}
+
+	preferences_entry_remove_auto_storage (entry, storage);
+}
+
+void
+eel_preferences_remove_auto_integer (const char *name,
+				     int *storage)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup (name);
+	if (entry == NULL) {
+		g_warning ("Trying to remove auto-integer for %s without adding it first.", name);
+		return;
+	}
+
+	preferences_entry_remove_auto_storage (entry, storage);
+}
+
+void
+eel_preferences_remove_auto_boolean (const char *name,
+				     gboolean *storage)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (storage != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup (name);
+	if (entry == NULL) {
+		g_warning ("Trying to remove auto-boolean for %s without adding it first.", name);
+		return;
+	}
+
+	preferences_entry_remove_auto_storage (entry, storage);
+}
+
+typedef struct
+{
+	char *name;
+	EelPreferencesCallback callback;
+	gpointer callback_data;
+} WhileAliveData;
+
+static void
+preferences_while_alive_disconnector (gpointer callback_data, GObject *where_object_was)
+{
+	WhileAliveData *data;
+
+	g_assert (callback_data != NULL);
+
+	data = callback_data;
+
+	/* we might have survived an eel shutdown, which
+	 * already cleared all the callbacks */
+	if (preferences_is_initialized ()) {
+		eel_preferences_remove_callback (data->name,
+						 data->callback,
+						 data->callback_data);
+	}
+
+	g_free (data->name);
+	g_free (data);
+}
+
+void
+eel_preferences_add_callback_while_alive (const char *name,
+					  EelPreferencesCallback callback,
+					  gpointer callback_data,
+					  GObject *alive_object)
+{
+	WhileAliveData *data;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (callback != NULL);
+	g_return_if_fail (G_IS_OBJECT (alive_object));
+	g_return_if_fail (preferences_is_initialized ());
+
+	data = g_new (WhileAliveData, 1);
+	data->name = g_strdup (name);
+	data->callback = callback;
+	data->callback_data = callback_data;
+
+	eel_preferences_add_callback (name, callback, callback_data);
+
+	g_object_weak_ref (alive_object,
+			   preferences_while_alive_disconnector,
+			   data);
+}
+
+void
+eel_preferences_remove_callback (const char *name,
+				 EelPreferencesCallback callback,
+				 gpointer callback_data)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (callback != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup (name);
+
+	if (entry == NULL) {
+		g_warning ("Trying to remove a callback for %s without adding it first.", name);
+		return;
+	}
+	
+	preferences_entry_remove_callback (entry, callback, callback_data);
+}
+
+void
+eel_preferences_set_description (const char *name,
+				 const char *description)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (description != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	g_free (entry->description);
+	entry->description = g_strdup (description);
+}
+
+char *
+eel_preferences_get_description (const char *name)
+{
+	PreferencesEntry *entry;
+
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (preferences_is_initialized (), NULL);
+
+	entry = preferences_global_table_lookup_or_insert (name);
+
+	return g_strdup (entry->description ? entry->description : "");
+}
+
+void
+eel_preferences_set_enumeration_id (const char *name,
+				    const char *enumeration_id)
+{
+	PreferencesEntry *entry;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (enumeration_id != NULL);
+	g_return_if_fail (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	g_free (entry->enumeration_id);
+	entry->enumeration_id = g_strdup (enumeration_id);
+}
+
+char *
+eel_preferences_get_enumeration_id (const char *name)
+{
+	PreferencesEntry *entry;
+
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (preferences_is_initialized (), NULL);
+
+	entry = preferences_global_table_lookup_or_insert (name);
+
+	return g_strdup (entry->enumeration_id);
+}
+
+static void
+preferences_set_emergency_fallback_stealing_value (const char *name,
+						   GConfValue *value)
+{
+	PreferencesEntry *entry;
+
+	g_assert (name != NULL);
+	g_assert (preferences_is_initialized ());
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	g_assert (entry != NULL);
+
+	if (entry->fallback)
+		gconf_value_free (entry->fallback);
+	entry->fallback = value; /* steal ownership of value */
+}
+
+void
+eel_preferences_set_emergency_fallback_string (const char *name,
+					       const char *value)
+{
+	GConfValue *gconf_value;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (value != NULL);
+	
+	gconf_value = gconf_value_new (GCONF_VALUE_STRING);
+
+	gconf_value_set_string (gconf_value, value);
+
+	preferences_set_emergency_fallback_stealing_value (name, gconf_value);
+}
+
+void
+eel_preferences_set_emergency_fallback_integer (const char *name,
+						int         value)
+{
+	GConfValue *gconf_value;
+
+	g_return_if_fail (name != NULL);
+	
+	gconf_value = gconf_value_new (GCONF_VALUE_INT);
+
+	gconf_value_set_int (gconf_value, value);
+
+	preferences_set_emergency_fallback_stealing_value (name, gconf_value);
+}
+
+void
+eel_preferences_set_emergency_fallback_boolean (const char *name,
+						gboolean    value)
+{
+	GConfValue *gconf_value;
+
+	g_return_if_fail (name != NULL);
+	
+	gconf_value = gconf_value_new (GCONF_VALUE_BOOL);
+
+	gconf_value_set_bool (gconf_value, value);
+
+	preferences_set_emergency_fallback_stealing_value (name, gconf_value);
+}
+
+
+void
+eel_preferences_set_emergency_fallback_string_array (const char  *name,
+						     char       **value)
+{
+	GConfValue *gconf_value;
+	GSList *list;
+	int i;
+
+	g_return_if_fail (name != NULL);
+	g_return_if_fail (value != NULL);
+
+	gconf_value = gconf_value_new (GCONF_VALUE_LIST);
+	gconf_value_set_list_type (gconf_value, GCONF_VALUE_STRING);
+
+	list = NULL;
+	for (i = 0; value[i] != NULL; ++i)
+	{
+		GConfValue *v;
+
+		v = gconf_value_new (GCONF_VALUE_STRING);
+		gconf_value_set_string (v, value[i]);
+
+		list = g_slist_prepend (list, v);
+	}
+
+	gconf_value_set_list_nocopy (gconf_value, g_slist_reverse (list));
+
+	preferences_set_emergency_fallback_stealing_value (name, gconf_value);
+}
+
+GConfValue*
+eel_preferences_get_emergency_fallback (const char *name)
+{
+	PreferencesEntry *entry;
+
+	g_return_val_if_fail (name != NULL, NULL);
+	g_return_val_if_fail (preferences_is_initialized (), NULL);
+
+	entry = preferences_global_table_lookup_or_insert (name);
+	
+	return entry->fallback ? gconf_value_copy (entry->fallback) : NULL;
+}
+
+gboolean
+eel_preferences_monitor_directory (const char *directory)
+{
+	g_return_val_if_fail (preferences_is_initialized (), FALSE);
+
+	return eel_gconf_monitor_add (directory);
+}
+
+gboolean
+eel_preferences_is_visible (const char *name)
+{
+	g_return_val_if_fail (name != NULL, FALSE);
+	g_return_val_if_fail (preferences_is_initialized (), FALSE);
+
+	return !preferences_global_table_lookup_or_insert (name)->invisible;
+}
+
+void
+eel_preferences_init (const char *path)
+{
+	g_return_if_fail (eel_strlen (path) > 0);
+	
+	if (initialized) {
+		return;
+	}
+
+	initialized = TRUE;
+
+	preferences_set_storage_path (path);
+}
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+#define CHECK_BOOLEAN(name__, value__)								\
+G_STMT_START {											\
+	eel_preferences_set_boolean ((name__), (value__));					\
+	EEL_CHECK_BOOLEAN_RESULT (eel_preferences_get_boolean (name__), (value__));	\
+} G_STMT_END
+
+#define CHECK_INTEGER(name__, value__)								\
+G_STMT_START {											\
+	eel_preferences_set_integer ((name__), (value__));					\
+	EEL_CHECK_INTEGER_RESULT (eel_preferences_get_integer (name__), (value__));	\
+} G_STMT_END
+
+#define CHECK_STRING(name__, value__)							\
+G_STMT_START {										\
+	eel_preferences_set ((name__), (value__));					\
+	EEL_CHECK_STRING_RESULT (eel_preferences_get (name__), (value__));		\
+} G_STMT_END
+
+void
+eel_self_check_preferences (void)
+{
+	/* Disabled until I can debug why these seemingly harmless tests 
+	 * dont work. -re
+	 */
+#if 0
+	int original_user_level;
+
+	original_user_level = eel_preferences_get_user_level ();
+
+ 	EEL_CHECK_INTEGER_RESULT (eel_preferences_get_integer ("self-check/neverset/i"), 0);
+ 	EEL_CHECK_STRING_RESULT (eel_preferences_get ("self-check/neverset/s"), "");
+ 	EEL_CHECK_BOOLEAN_RESULT (eel_preferences_get_boolean ("self-check/neverset/b"), FALSE);
+
+	eel_preferences_set_user_level (0);
+
+	/* FIXME: Fails if you add the commented-out lines. */
+	/* CHECK_INTEGER ("self-check/i", 0); */
+	CHECK_INTEGER ("self-check/i", 666);
+	/* CHECK_BOOLEAN ("self-check/b", FALSE); */
+	CHECK_BOOLEAN ("self-check/b", TRUE);
+	/* CHECK_STRING ("self-check/s", ""); */
+	CHECK_STRING ("self-check/s", "foo");
+
+	/* Restore the original user level */
+	eel_preferences_set_user_level (original_user_level);
+#endif
+}
+
+#endif /* !EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-preferences.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-preferences.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,157 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-preferences.c - Preference peek/poke/notify interface.
+
+   Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_PREFERENCES_H
+#define EEL_PREFERENCES_H
+
+#include <glib.h>
+#include <eel/eel-gconf-extensions.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/*
+ * A callback which you can register to to be notified when a particular
+ * preference changes.
+ */
+typedef void (*EelPreferencesCallback) (gpointer callback_data);
+
+/* Preferences getters and setters */
+gboolean       eel_preferences_get_boolean                     (const char              *name);
+void           eel_preferences_set_boolean                     (const char              *name,
+								gboolean                 boolean_value);
+int            eel_preferences_get_integer                     (const char              *name);
+void           eel_preferences_set_integer                     (const char              *name,
+								int                      int_value);
+guint          eel_preferences_get_uint                        (const char              *name);
+void           eel_preferences_set_uint                        (const char              *name,
+								guint                    uint_value);
+guint          eel_preferences_get_enum                        (const char              *name);
+void           eel_preferences_set_enum                        (const char              *name,
+								guint                   int_value);
+char *         eel_preferences_get                             (const char              *name);
+void           eel_preferences_set                             (const char              *name,
+								const char              *string_value);
+char **        eel_preferences_get_string_array                 (const char              *name);
+void           eel_preferences_set_string_array                 (const char              *name,
+								 char                   **strv_value);
+
+void           eel_preferences_unset                            (const char              *name);
+
+/* Writability of a key */
+gboolean       eel_preferences_key_is_writable                 (const char              *name);
+
+/* Callbacks */
+void           eel_preferences_add_callback                    (const char              *name,
+								EelPreferencesCallback   callback,
+								gpointer                 callback_data);
+void           eel_preferences_add_callback_while_alive        (const char              *name,
+								EelPreferencesCallback   callback,
+								gpointer                 callback_data,
+								GObject                 *alive_object);
+void           eel_preferences_remove_callback                 (const char              *name,
+								EelPreferencesCallback   callback,
+								gpointer                 callback_data);
+
+/* Variables that are automatically updated (lightweight "callbacks") */
+void           eel_preferences_add_auto_string                 (const char              *name,
+								const char             **storage);
+void           eel_preferences_add_auto_string_array           (const char              *name,
+								char                  ***storage);
+void           eel_preferences_add_auto_string_array_as_quarks (const char              *name,
+								GQuark                 **storage);
+void           eel_preferences_add_auto_integer                (const char              *name,
+								int                     *storage);
+void           eel_preferences_add_auto_enum                   (const char              *name,
+								guint                   *storage);
+void           eel_preferences_add_auto_boolean                (const char              *name,
+								gboolean                *storage);
+void           eel_preferences_remove_auto_string              (const char              *name,
+								const char             **storage);
+void           eel_preferences_remove_auto_string_array        (const char              *name,
+								char                  ***storage);
+void           eel_preferences_remove_auto_integer             (const char              *name,
+								int                     *storage);
+void           eel_preferences_remove_auto_boolean             (const char              *name,
+								int                     *storage);
+
+/* Preferences attributes */
+
+gboolean       eel_preferences_get_is_invisible                (const char              *name);
+void           eel_preferences_set_is_invisible                (const char              *name,
+								gboolean                 invisible);
+char *         eel_preferences_get_description                 (const char              *name);
+void           eel_preferences_set_description                 (const char              *name,
+								const char              *description);
+char *         eel_preferences_get_enumeration_id              (const char              *name);
+void           eel_preferences_set_enumeration_id              (const char              *name,
+								const char              *enumeration_id);
+
+void        eel_preferences_set_emergency_fallback_string      (const char    *name,
+								const char    *value);
+void        eel_preferences_set_emergency_fallback_integer     (const char    *name,
+								int            value);
+void        eel_preferences_set_emergency_fallback_boolean     (const char    *name,
+								gboolean       value);
+void        eel_preferences_set_emergency_fallback_string_array(const char    *name,
+								char         **value);
+GConfValue *eel_preferences_get_emergency_fallback             (const char    *name);
+
+
+gboolean       eel_preferences_monitor_directory               (const char              *directory);
+gboolean       eel_preferences_is_visible                      (const char              *name);
+void           eel_preferences_init                      (const char              *storage_path);
+
+void eel_preferences_builder_connect_bool		         (GtkBuilder *builder,
+								  const char  *component,
+								  const char  *key);
+void eel_preferences_builder_connect_bool_slave			 (GtkBuilder *builder,
+								  const char  *component,
+								  const char  *key);
+void eel_preferences_builder_connect_string_enum_combo_box	 (GtkBuilder *builder,
+								  const char  *component,
+								  const char  *key,
+								  const char **values);
+void eel_preferences_builder_connect_string_enum_combo_box_slave (GtkBuilder *builder,
+								  const char  *component,
+								  const char  *key);
+
+void eel_preferences_builder_connect_uint_enum			 (GtkBuilder *builder,
+								  const char  *component,
+								  const char  *key,
+								  const guint *values,
+								  int          num_values);
+void eel_preferences_builder_connect_string_enum_radio_button	 (GtkBuilder *builder,
+								  const char **components,
+								  const char  *key,
+								  const char **values);
+void eel_preferences_builder_connect_list_enum			 (GtkBuilder *builder,
+						   		  const char **components,
+								  const char  *key,
+								  const char **values);
+
+
+G_END_DECLS
+
+#endif /* EEL_PREFERENCES_H */

Added: trunk/eel/eel-self-checks.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-self-checks.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,221 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+   
+   eel-self-checks.c: The self-check framework.
+ 
+   Copyright (C) 1999 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+
+#if ! defined (EEL_OMIT_SELF_CHECK)
+
+#include "eel-self-checks.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static gboolean failed;
+
+static const char *current_expression;
+static const char *current_file_name;
+static int current_line_number;
+
+void
+eel_exit_if_self_checks_failed (void)
+{
+	if (!failed) {
+		return;
+	}
+
+	printf ("\n");
+
+	exit (EXIT_FAILURE);
+}
+
+void
+eel_report_check_failure (char *result, char *expected)
+{
+	if (!failed) {
+		fprintf (stderr, "\n");
+	}
+
+	fprintf (stderr, "FAIL: check failed in %s, line %d\n", current_file_name, current_line_number);
+	fprintf (stderr, "      evaluated: %s\n", current_expression);
+	fprintf (stderr, "       expected: %s\n", expected == NULL ? "NULL" : expected);
+	fprintf (stderr, "            got: %s\n", result == NULL ? "NULL" : result);
+	
+	failed = TRUE;
+
+	g_free (result);
+	g_free (expected);
+}
+
+static char *
+eel_strdup_boolean (gboolean boolean)
+{
+	if (boolean == FALSE) {
+		return g_strdup ("FALSE");
+	}
+	if (boolean == TRUE) {
+		return g_strdup ("TRUE");
+	}
+	return g_strdup_printf ("gboolean(%d)", boolean);
+}
+
+void
+eel_before_check (const char *expression,
+		  const char *file_name,
+		  int line_number)
+{
+	current_expression = expression;
+	current_file_name = file_name;
+	current_line_number = line_number;
+}
+
+void
+eel_after_check (void)
+{
+	/* It would be good to check here if there was a memory leak. */
+}
+
+void
+eel_check_boolean_result (gboolean result, gboolean expected)
+{
+	if (result != expected) {
+		eel_report_check_failure (eel_strdup_boolean (result),
+					  eel_strdup_boolean (expected));
+	}
+	eel_after_check ();
+}
+
+void
+eel_check_rectangle_result (EelIRect result,
+			    int expected_x0,
+			    int expected_y0,
+			    int expected_x1,
+			    int expected_y1)
+{
+	if (result.x0 != expected_x0
+	    || result.y0 != expected_y0
+	    || result.x1 != expected_x1
+	    || result.y1 != expected_y1) {
+		eel_report_check_failure (g_strdup_printf ("x0=%d, y0=%d, x1=%d, y1=%d",
+							   result.x0,
+							   result.y0,
+							   result.x1,
+							   result.y1),
+					  g_strdup_printf ("x0=%d, y0=%d, x1=%d, y1=%d",
+							   expected_x0,
+							   expected_y0,
+							   expected_x1,
+							   expected_y1));
+	}
+	eel_after_check ();
+}
+
+void
+eel_check_dimensions_result (EelDimensions result,
+			     int expected_width,
+			     int expected_height)
+{
+	if (result.width != expected_width
+	    || result.height != expected_height) {
+		eel_report_check_failure (g_strdup_printf ("width=%d, height=%d",
+							   result.width,
+							   result.height),
+					  g_strdup_printf ("width=%d, height=%d",
+							   expected_width,
+							   expected_height));
+	}
+	eel_after_check ();
+}
+
+void
+eel_check_point_result (EelIPoint result,
+			int expected_x,
+			int expected_y)
+{
+	if (result.x != expected_x
+	    || result.y != expected_y) {
+		eel_report_check_failure (g_strdup_printf ("x=%d, y=%d",
+							   result.x,
+							   result.y),
+					  g_strdup_printf ("x=%d, y=%d",
+							   expected_x,
+							   expected_y));
+	}
+	eel_after_check ();
+}
+
+void
+eel_check_integer_result (long result, long expected)
+{
+	if (result != expected) {
+		eel_report_check_failure (g_strdup_printf ("%ld", result),
+					  g_strdup_printf ("%ld", expected));
+	}
+	eel_after_check ();
+}
+
+void
+eel_check_double_result (double result, double expected)
+{
+	if (result != expected) {
+		eel_report_check_failure (g_strdup_printf ("%f", result),
+					  g_strdup_printf ("%f", expected));
+	}
+	eel_after_check ();
+}
+
+void
+eel_check_string_result (char *result, const char *expected)
+{
+	gboolean match;
+	
+	/* Stricter than eel_strcmp.
+	 * NULL does not match "" in this test.
+	 */
+	if (expected == NULL) {
+		match = result == NULL;
+	} else {
+		match = result != NULL && strcmp (result, expected) == 0;
+	}
+
+	if (!match) {
+		eel_report_check_failure (result, g_strdup (expected));
+	} else {
+		g_free (result);
+	}
+	eel_after_check ();
+}
+
+void
+eel_before_check_function (const char *name)
+{
+	fprintf (stderr, "running %s\n", name);
+}
+
+void
+eel_after_check_function (void)
+{
+}
+
+#endif /* ! EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-self-checks.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-self-checks.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,100 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+   
+   eel-self-checks.h: The self-check framework.
+ 
+   Copyright (C) 1999 Eazel, Inc.
+  
+   This program 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 program 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 program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Darin Adler <darin eazel com>
+*/
+
+#ifndef EEL_SELF_CHECKS_H
+#define EEL_SELF_CHECKS_H
+
+#include <glib.h>
+#include <eel/eel-art-extensions.h>
+
+#define EEL_CHECK_RESULT(type, expression, expected_value) \
+G_STMT_START { \
+	eel_before_check (#expression, __FILE__, __LINE__); \
+	eel_check_##type##_result (expression, expected_value); \
+} G_STMT_END
+
+#define EEL_CHECK_BOOLEAN_RESULT(expression, expected_value) \
+	EEL_CHECK_RESULT(boolean, expression, expected_value)
+#define EEL_CHECK_INTEGER_RESULT(expression, expected_value) \
+	EEL_CHECK_RESULT(integer, expression, expected_value)
+#define EEL_CHECK_DOUBLE_RESULT(expression, expected_value) \
+	EEL_CHECK_RESULT(double, expression, expected_value)
+#define EEL_CHECK_STRING_RESULT(expression, expected_value) \
+	EEL_CHECK_RESULT(string, expression, expected_value)
+#define EEL_CHECK_RECTANGLE_RESULT(expression, expected_x0, expected_y0, expected_x1, expected_y1) \
+G_STMT_START { \
+	eel_before_check (#expression, __FILE__, __LINE__); \
+	eel_check_rectangle_result (expression, expected_x0, expected_y0, expected_x1, expected_y1); \
+} G_STMT_END
+#define EEL_CHECK_DIMENSIONS_RESULT(expression, expected_width, expected_height) \
+G_STMT_START { \
+	eel_before_check (#expression, __FILE__, __LINE__); \
+	eel_check_dimensions_result (expression, expected_width, expected_height); \
+} G_STMT_END
+#define EEL_CHECK_POINT_RESULT(expression, expected_x, expected_y) \
+G_STMT_START { \
+	eel_before_check (#expression, __FILE__, __LINE__); \
+	eel_check_point_result (expression, expected_x, expected_y); \
+} G_STMT_END
+
+void eel_exit_if_self_checks_failed (void);
+void eel_before_check_function      (const char    *name);
+void eel_after_check_function       (void);
+void eel_before_check               (const char    *expression,
+				     const char    *file_name,
+				     int            line_number);
+void eel_after_check                (void);
+
+/* Both 'result' and 'expected' get freed with g_free */
+void eel_report_check_failure       (char          *result,
+				     char          *expected);
+void eel_check_boolean_result       (gboolean       result,
+				     gboolean       expected_value);
+void eel_check_integer_result       (long           result,
+				     long           expected_value);
+void eel_check_double_result        (double         result,
+				     double         expected_value);
+void eel_check_rectangle_result     (EelIRect       result,
+				     int            expected_x0,
+				     int            expected_y0,
+				     int            expected_x1,
+				     int            expected_y1);
+void eel_check_dimensions_result    (EelDimensions  result,
+				     int            expected_width,
+				     int            expected_height);
+void eel_check_point_result         (EelIPoint      result,
+				     int            expected_x,
+				     int            expected_y);
+void eel_check_string_result        (char          *result,
+				     const char    *expected_value);
+
+#define EEL_SELF_CHECK_FUNCTION_PROTOTYPE(function) \
+	void function (void);
+
+#define EEL_CALL_SELF_CHECK_FUNCTION(function) \
+	eel_before_check_function (#function); \
+	function (); \
+	eel_after_check_function ();
+
+#endif /* EEL_SELF_CHECKS_H */

Added: trunk/eel/eel-stock-dialogs.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-stock-dialogs.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,571 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-stock-dialogs.c: Various standard dialogs for Eel.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+#include "eel-stock-dialogs.h"
+
+#include "eel-alert-dialog.h"
+#include "eel-glib-extensions.h"
+#include "eel-gnome-extensions.h"
+#include "eel-string.h"
+#include "eel-i18n.h"
+#include <gtk/gtk.h>
+
+#define TIMED_WAIT_STANDARD_DURATION 2000
+#define TIMED_WAIT_MIN_TIME_UP 3000
+
+#define TIMED_WAIT_MINIMUM_DIALOG_WIDTH 300
+
+#define RESPONSE_DETAILS 1000
+
+typedef struct {
+	EelCancelCallback cancel_callback;
+	gpointer callback_data;
+
+	/* Parameters for creation of the window. */
+	char *wait_message;
+	GtkWindow *parent_window;
+
+	/* Timer to determine when we need to create the window. */
+	guint timeout_handler_id;
+	
+	/* Window, once it's created. */
+	GtkDialog *dialog;
+	
+	/* system time (microseconds) when dialog was created */
+	gint64 dialog_creation_time;
+
+} TimedWait;
+
+static GHashTable *timed_wait_hash_table;
+
+static void timed_wait_dialog_destroy_callback (GtkObject *object, gpointer callback_data);
+
+static guint
+timed_wait_hash (gconstpointer value)
+{
+	const TimedWait *wait;
+
+	wait = value;
+
+	return GPOINTER_TO_UINT (wait->cancel_callback)
+		^ GPOINTER_TO_UINT (wait->callback_data);
+}
+
+static gboolean
+timed_wait_hash_equal (gconstpointer value1, gconstpointer value2)
+{
+	const TimedWait *wait1, *wait2;
+
+	wait1 = value1;
+	wait2 = value2;
+
+	return wait1->cancel_callback == wait2->cancel_callback
+		&& wait1->callback_data == wait2->callback_data;
+}
+
+static void
+timed_wait_delayed_close_destroy_dialog_callback (GtkObject *object, gpointer callback_data)
+{
+	g_source_remove (GPOINTER_TO_UINT (callback_data));
+}
+
+static gboolean
+timed_wait_delayed_close_timeout_callback (gpointer callback_data)
+{
+	guint handler_id;
+
+	handler_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (callback_data),
+							  "eel-stock-dialogs/delayed_close_handler_timeout_id"));
+	
+	g_signal_handlers_disconnect_by_func (G_OBJECT (callback_data),
+					      G_CALLBACK (timed_wait_delayed_close_destroy_dialog_callback),
+					      GUINT_TO_POINTER (handler_id));
+	
+	gtk_object_destroy (GTK_OBJECT (callback_data));
+
+	return FALSE;
+}
+
+static void
+timed_wait_free (TimedWait *wait)
+{
+	guint delayed_close_handler_id;
+	guint64 time_up;
+
+	g_assert (g_hash_table_lookup (timed_wait_hash_table, wait) != NULL);
+
+	g_hash_table_remove (timed_wait_hash_table, wait);
+
+	g_free (wait->wait_message);
+	if (wait->parent_window != NULL) {
+		gtk_widget_unref (GTK_WIDGET (wait->parent_window));
+	}
+	if (wait->timeout_handler_id != 0) {
+		g_source_remove (wait->timeout_handler_id);
+	}
+	if (wait->dialog != NULL) {
+		/* Make sure to detach from the "destroy" signal, or we'll
+		 * double-free.
+		 */
+		g_signal_handlers_disconnect_by_func (G_OBJECT (wait->dialog),
+						      G_CALLBACK (timed_wait_dialog_destroy_callback),
+						      wait);
+
+		/* compute time up in milliseconds */
+		time_up = (eel_get_system_time () - wait->dialog_creation_time) / 1000;
+		
+		if (time_up < TIMED_WAIT_MIN_TIME_UP) {
+			delayed_close_handler_id = g_timeout_add (TIMED_WAIT_MIN_TIME_UP - time_up,
+			                                            timed_wait_delayed_close_timeout_callback,
+			                                            wait->dialog);
+			g_object_set_data (G_OBJECT (wait->dialog),
+					     "eel-stock-dialogs/delayed_close_handler_timeout_id",
+					     GUINT_TO_POINTER (delayed_close_handler_id));
+			g_signal_connect (wait->dialog, "destroy",
+					    G_CALLBACK (timed_wait_delayed_close_destroy_dialog_callback),
+					    GUINT_TO_POINTER (delayed_close_handler_id));
+		} else {
+			gtk_object_destroy (GTK_OBJECT (wait->dialog));
+		}
+	}
+
+	/* And the wait object itself. */
+	g_free (wait);
+}
+
+static void
+timed_wait_dialog_destroy_callback (GtkObject *object, gpointer callback_data)
+{
+	TimedWait *wait;
+
+	wait = callback_data;
+
+	g_assert (GTK_DIALOG (object) == wait->dialog);
+
+	wait->dialog = NULL;
+	
+	/* When there's no cancel_callback, the originator will/must
+	 * call eel_timed_wait_stop which will call timed_wait_free.
+	 */
+
+	if (wait->cancel_callback != NULL) {
+		(* wait->cancel_callback) (wait->callback_data);
+		timed_wait_free (wait);
+	}
+}
+
+static void
+trash_dialog_response_callback (GtkDialog *dialog,
+				int response_id,
+				TimedWait *wait)
+{
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static gboolean
+timed_wait_callback (gpointer callback_data)
+{
+	TimedWait *wait;
+	GtkDialog *dialog;
+	const char *button;
+
+	wait = callback_data;
+
+	/* Put up the timed wait window. */
+	button = wait->cancel_callback != NULL ? GTK_STOCK_CANCEL : GTK_STOCK_OK;
+	dialog = GTK_DIALOG (eel_alert_dialog_new (NULL,
+		                                   0,
+		                                   GTK_MESSAGE_INFO,
+		                                   GTK_BUTTONS_NONE,
+		                                   wait->wait_message,
+		                                   _("You can stop this operation by clicking cancel.")));
+							
+	gtk_dialog_add_button (GTK_DIALOG (dialog), button, GTK_RESPONSE_OK);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+	/* The contents are often very small, causing tiny little
+	 * dialogs with their titles clipped if you just let gtk
+	 * sizing do its thing. This enforces a minimum width to
+	 * make it more likely that the title won't be clipped.
+	 */
+	gtk_window_set_default_size (GTK_WINDOW (dialog),
+				     TIMED_WAIT_MINIMUM_DIALOG_WIDTH,
+				     -1);
+	wait->dialog_creation_time = eel_get_system_time ();
+	gtk_widget_show (GTK_WIDGET (dialog));
+
+	/* FIXME bugzilla.eazel.com 2441: 
+	 * Could parent here, but it's complicated because we
+	 * don't want this window to go away just because the parent
+	 * would go away first.
+	 */
+
+	/* Make the dialog cancel the timed wait when it goes away.
+	 * Connect to "destroy" instead of "response" since we want
+	 * to be called no matter how the dialog goes away.
+	 */
+	g_signal_connect (dialog, "destroy",
+			  G_CALLBACK (timed_wait_dialog_destroy_callback),
+			  wait);
+	g_signal_connect (dialog, "response",
+			  G_CALLBACK (trash_dialog_response_callback),
+			  wait);
+
+	wait->timeout_handler_id = 0;
+	wait->dialog = dialog;
+	
+	return FALSE;
+}
+
+void
+eel_timed_wait_start_with_duration (int duration,
+					 EelCancelCallback cancel_callback,
+					 gpointer callback_data,
+					 const char *wait_message,
+					 GtkWindow *parent_window)
+{
+	TimedWait *wait;
+	
+	g_return_if_fail (callback_data != NULL);
+	g_return_if_fail (wait_message != NULL);
+	g_return_if_fail (parent_window == NULL || GTK_IS_WINDOW (parent_window));
+
+	/* Create the timed wait record. */
+	wait = g_new0 (TimedWait, 1);
+	wait->wait_message = g_strdup (wait_message);
+	wait->cancel_callback = cancel_callback;
+	wait->callback_data = callback_data;
+	wait->parent_window = parent_window;
+	
+	if (parent_window != NULL) {
+		gtk_widget_ref (GTK_WIDGET (parent_window));
+	}
+
+	/* Start the timer. */
+	wait->timeout_handler_id = g_timeout_add (duration, timed_wait_callback, wait);
+
+	/* Put in the hash table so we can find it later. */
+	if (timed_wait_hash_table == NULL) {
+		timed_wait_hash_table = eel_g_hash_table_new_free_at_exit
+			(timed_wait_hash, timed_wait_hash_equal, __FILE__ ": timed wait");
+	}
+	g_assert (g_hash_table_lookup (timed_wait_hash_table, wait) == NULL);
+	g_hash_table_insert (timed_wait_hash_table, wait, wait);
+	g_assert (g_hash_table_lookup (timed_wait_hash_table, wait) == wait);
+}
+
+void
+eel_timed_wait_start (EelCancelCallback cancel_callback,
+			   gpointer callback_data,
+			   const char *wait_message,
+			   GtkWindow *parent_window)
+{
+	eel_timed_wait_start_with_duration
+		(TIMED_WAIT_STANDARD_DURATION,
+		 cancel_callback, callback_data,
+		 wait_message, parent_window);
+}
+
+void
+eel_timed_wait_stop (EelCancelCallback cancel_callback,
+			  gpointer callback_data)
+{
+	TimedWait key;
+	TimedWait *wait;
+
+	g_return_if_fail (callback_data != NULL);
+	
+	key.cancel_callback = cancel_callback;
+	key.callback_data = callback_data;
+	wait = g_hash_table_lookup (timed_wait_hash_table, &key);
+
+	g_return_if_fail (wait != NULL);
+
+	timed_wait_free (wait);
+}
+
+int
+eel_run_simple_dialog (GtkWidget *parent, gboolean ignore_close_box,
+		       GtkMessageType message_type, const char *primary_text,
+		       const char *secondary_text, ...)
+{
+	va_list button_title_args;
+	const char *button_title;
+        GtkWidget *dialog;
+        GtkWidget *top_widget, *chosen_parent;
+	int result;
+	int response_id;
+
+	/* Parent it if asked to. */
+	chosen_parent = NULL;
+        if (parent != NULL) {
+		top_widget = gtk_widget_get_toplevel (parent);
+		if (GTK_IS_WINDOW (top_widget)) {
+			chosen_parent = top_widget;
+		}
+	}
+	
+	/* Create the dialog. */
+	dialog = eel_alert_dialog_new (GTK_WINDOW (chosen_parent), 
+	                               0,
+	                               message_type,
+	                               GTK_BUTTONS_NONE,
+	                               primary_text,
+	                               secondary_text);
+	
+	va_start (button_title_args, secondary_text);
+	response_id = 0;
+	while (1) {
+		button_title = va_arg (button_title_args, const char *);
+		if (button_title == NULL) {
+			break;
+		}
+		gtk_dialog_add_button (GTK_DIALOG (dialog), button_title, response_id);
+		gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id);
+		response_id++;
+	}
+	va_end (button_title_args);
+
+	/* Run it. */
+        gtk_widget_show (dialog);
+        result = gtk_dialog_run (GTK_DIALOG (dialog));
+	while ((result == GTK_RESPONSE_NONE || result == GTK_RESPONSE_DELETE_EVENT) && ignore_close_box) {
+		gtk_widget_show (GTK_WIDGET (dialog));
+		result = gtk_dialog_run (GTK_DIALOG (dialog));
+	}
+	gtk_object_destroy (GTK_OBJECT (dialog));
+
+	return result;
+}
+
+static GtkDialog *
+create_message_dialog (const char *primary_text,
+		       const char *secondary_text,
+		       GtkMessageType type,
+		       GtkButtonsType buttons_type,
+		       GtkWindow *parent)
+{  
+	GtkWidget *dialog;
+
+	dialog = eel_alert_dialog_new (parent,
+	                               0,
+	                               type,
+	                               buttons_type,
+	                               primary_text,
+	                               secondary_text);
+	return GTK_DIALOG (dialog);
+}
+
+static GtkDialog *
+show_message_dialog (const char *primary_text,
+		     const char *secondary_text,
+		     GtkMessageType type,
+		     GtkButtonsType buttons_type,
+		     const char *details_text,
+		     GtkWindow *parent)
+{
+	GtkDialog *dialog;
+
+	dialog = create_message_dialog (primary_text, secondary_text, type, 
+					buttons_type, parent);
+	if (details_text != NULL) {
+		eel_alert_dialog_set_details_label (EEL_ALERT_DIALOG (dialog), details_text);
+	}
+	gtk_widget_show (GTK_WIDGET (dialog));
+
+	g_signal_connect (dialog, "response",
+			  G_CALLBACK (gtk_object_destroy), NULL);
+
+	return dialog;
+}
+
+static GtkDialog *
+show_ok_dialog (const char *primary_text,
+		const char *secondary_text,
+		GtkMessageType type,
+		GtkWindow *parent)
+{  
+	GtkDialog *dialog;
+
+	dialog = show_message_dialog (primary_text, secondary_text, type,
+				      GTK_BUTTONS_OK, NULL, parent);
+	gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+	
+	return dialog;
+}
+
+GtkDialog *
+eel_create_info_dialog (const char *primary_text,
+			const char *secondary_text,
+			GtkWindow *parent)
+{
+	return create_message_dialog (primary_text, secondary_text,
+				      GTK_MESSAGE_INFO,
+				      GTK_BUTTONS_OK,
+				      parent);
+}
+
+GtkDialog *
+eel_show_info_dialog (const char *primary_text,
+		      const char *secondary_text,
+		      GtkWindow *parent)
+{
+	return show_ok_dialog (primary_text, 
+			    secondary_text,
+			    GTK_MESSAGE_INFO, parent);
+}
+
+GtkDialog *
+eel_show_info_dialog_with_details (const char *primary_text,
+				   const char *secondary_text,
+				   const char *detailed_info,
+				   GtkWindow *parent)
+{
+	GtkDialog *dialog;
+
+	if (detailed_info == NULL
+	    || strcmp (primary_text, detailed_info) == 0) {
+		return eel_show_info_dialog (primary_text, secondary_text, parent);
+	}
+
+	dialog = show_message_dialog (primary_text,
+				      secondary_text,
+				      GTK_MESSAGE_INFO, 
+				      GTK_BUTTONS_OK,
+				      detailed_info,
+				      parent);
+
+	return dialog;
+
+}
+
+
+GtkDialog *
+eel_show_warning_dialog (const char *primary_text,
+			 const char *secondary_text,
+			 GtkWindow *parent)
+{
+	return show_ok_dialog (primary_text, 
+			       secondary_text,
+			       GTK_MESSAGE_WARNING, parent);
+}
+
+
+GtkDialog *
+eel_show_error_dialog (const char *primary_text,
+		       const char *secondary_text,
+		       GtkWindow *parent)
+{
+	return show_ok_dialog (primary_text,
+			       secondary_text,
+			       GTK_MESSAGE_ERROR, parent);
+}
+
+GtkDialog *
+eel_show_error_dialog_with_details (const char *primary_text,
+				    const char *secondary_text,
+				    const char *detailed_error_message,
+				    GtkWindow *parent)
+{
+	GtkDialog *dialog;
+
+	g_return_val_if_fail (primary_text != NULL, NULL);
+	g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
+
+	if (detailed_error_message == NULL
+	    || strcmp (primary_text, detailed_error_message) == 0) {
+		return eel_show_error_dialog (primary_text, secondary_text, parent);
+	}
+	
+	dialog = show_message_dialog (primary_text, 
+				      secondary_text,
+				      GTK_MESSAGE_ERROR,
+				      GTK_BUTTONS_OK, detailed_error_message,
+				      parent);
+	return dialog;
+}
+
+/**
+ * eel_show_yes_no_dialog:
+ * 
+ * Create and show a dialog asking a question with two choices.
+ * The caller needs to set up any necessary callbacks 
+ * for the buttons. Use eel_create_question_dialog instead
+ * if any visual changes need to be made, to avoid flashiness.
+ * @question: The text of the question.
+ * @yes_label: The label of the "yes" button.
+ * @no_label: The label of the "no" button.
+ * @parent: The parent window for this dialog.
+ */
+GtkDialog *
+eel_show_yes_no_dialog (const char *primary_text, 
+			const char *secondary_text,
+			const char *yes_label,
+			const char *no_label,
+			GtkWindow *parent)
+{
+	GtkDialog *dialog = NULL;
+	dialog = eel_create_question_dialog (primary_text,
+					     secondary_text,
+					     no_label, GTK_RESPONSE_CANCEL,
+					     yes_label, GTK_RESPONSE_YES,
+					     GTK_WINDOW (parent));
+	gtk_widget_show (GTK_WIDGET (dialog));
+	return dialog;
+}
+
+/**
+ * eel_create_question_dialog:
+ * 
+ * Create a dialog asking a question with at least two choices.
+ * The caller needs to set up any necessary callbacks 
+ * for the buttons. The dialog is not yet shown, so that the
+ * caller can add additional buttons or make other visual changes
+ * without causing flashiness.
+ * @question: The text of the question.
+ * @answer_0: The label of the leftmost button (index 0)
+ * @answer_1: The label of the 2nd-to-leftmost button (index 1)
+ * @parent: The parent window for this dialog.
+ */
+GtkDialog *
+eel_create_question_dialog (const char *primary_text,
+			    const char *secondary_text,
+			    const char *answer_1,
+			    int response_1,
+			    const char *answer_2,
+			    int response_2,
+			    GtkWindow *parent)
+{
+	GtkDialog *dialog;
+	
+	dialog = create_message_dialog (primary_text,
+					secondary_text,
+					GTK_MESSAGE_QUESTION,
+					GTK_BUTTONS_NONE,
+					parent);
+	gtk_dialog_add_buttons (dialog, answer_1, response_1, answer_2, response_2, NULL);
+	gtk_dialog_set_default_response (dialog, response_2);
+	return dialog;
+}

Added: trunk/eel/eel-stock-dialogs.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-stock-dialogs.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,91 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-stock-dialogs.h: Various standard dialogs for Eel.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#ifndef EEL_STOCK_DIALOGS_H
+#define EEL_STOCK_DIALOGS_H
+
+#include <gtk/gtk.h>
+
+typedef void (* EelCancelCallback) (gpointer callback_data);
+
+/* Dialog for cancelling something that normally is fast enough not to need a dialog. */
+void       eel_timed_wait_start               (EelCancelCallback  cancel_callback,
+					       gpointer           callback_data,
+					       const char        *wait_message,
+					       GtkWindow         *parent_window);
+void       eel_timed_wait_start_with_duration (int                duration,
+					       EelCancelCallback  cancel_callback,
+					       gpointer           callback_data,
+					       const char        *wait_message,
+					       GtkWindow         *parent_window);
+void       eel_timed_wait_stop                (EelCancelCallback  cancel_callback,
+					       gpointer           callback_data);
+
+/* Basic dialog with buttons. */
+int        eel_run_simple_dialog              (GtkWidget         *parent,
+					       gboolean           ignore_close_box,
+					       GtkMessageType     message_type,
+					       const char        *primary_text,
+					       const char        *secondary_text,
+					       ...);
+
+/* Variations on gnome stock dialogs; these do line wrapping, we don't
+ * bother with non-parented versions, we allow setting the title,
+ * primary, and secondary messages, and we return GtkDialog pointers 
+ * instead of GtkWidget pointers.
+ */
+GtkDialog *eel_show_info_dialog               (const char        *primary_text,
+					       const char        *secondary_text,
+					       GtkWindow         *parent);
+GtkDialog *eel_show_info_dialog_with_details  (const char        *primary_text,
+					       const char        *secondary_text,
+					       const char        *detailed_informative_message,
+					       GtkWindow         *parent);
+GtkDialog *eel_show_warning_dialog            (const char        *primary_text,
+					       const char        *secondary_text,
+					       GtkWindow         *parent);
+GtkDialog *eel_show_error_dialog              (const char        *primary_text,
+					       const char        *secondary_text,
+					       GtkWindow         *parent);
+GtkDialog *eel_show_error_dialog_with_details (const char        *primary_text,
+					       const char        *secondary_text,
+					       const char        *detailed_error_message,
+					       GtkWindow         *parent);
+GtkDialog *eel_show_yes_no_dialog             (const char        *primary_text,
+					       const char        *secondary_text,
+					       const char        *yes_label,
+					       const char        *no_label,
+					       GtkWindow         *parent);
+GtkDialog *eel_create_question_dialog         (const char        *primary_text,
+					       const char        *secondary_text,
+					       const char        *answer_one,
+					       int                response_one,
+					       const char        *answer_two,
+					       int                response_two,
+					       GtkWindow         *parent);
+GtkDialog *eel_create_info_dialog             (const char        *primary_text,
+					       const char        *secondary_text,
+					       GtkWindow         *parent);
+
+#endif /* EEL_STOCK_DIALOGS_H */

Added: trunk/eel/eel-string.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-string.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,1178 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-string.c: String routines to augment <string.h>.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+#include "eel-string.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+#include "eel-lib-self-check-functions.h"
+#endif
+
+size_t
+eel_strlen (const char *string)
+{
+	return string == NULL ? 0 : strlen (string);
+}
+
+char *
+eel_strchr (const char *haystack, char needle)
+{
+	return haystack == NULL ? NULL : strchr (haystack, needle);
+}
+
+int
+eel_strcmp (const char *string_a, const char *string_b)
+{
+	/* FIXME bugzilla.eazel.com 5450: Maybe we need to make this
+	 * treat 'NULL < ""', or have a flavor that does that. If we
+	 * didn't have code that already relies on 'NULL == ""', I
+	 * would change it right now.
+	 */
+	return strcmp (string_a == NULL ? "" : string_a,
+		       string_b == NULL ? "" : string_b);
+}
+
+int
+eel_strcasecmp (const char *string_a, const char *string_b)
+{
+	/* FIXME bugzilla.eazel.com 5450: Maybe we need to make this
+	 * treat 'NULL < ""', or have a flavor that does that. If we
+	 * didn't have code that already relies on 'NULL == ""', I
+	 * would change it right now.
+	 */
+	return g_ascii_strcasecmp (string_a == NULL ? "" : string_a,
+				   string_b == NULL ? "" : string_b);
+}
+
+int
+eel_strcmp_case_breaks_ties (const char *string_a, const char *string_b)
+{
+	int casecmp_result;
+
+	/* FIXME bugzilla.eazel.com 5450: Maybe we need to make this
+	 * treat 'NULL < ""', or have a flavor that does that. If we
+	 * didn't have code that already relies on 'NULL == ""', I
+	 * would change it right now.
+	 */
+	casecmp_result = eel_strcasecmp (string_a, string_b);
+	if (casecmp_result != 0) {
+		return casecmp_result;
+	}
+	return eel_strcmp (string_a, string_b);
+}
+
+gboolean
+eel_str_is_empty (const char *string_or_null)
+{
+	return eel_strcmp (string_or_null, NULL) == 0;
+}
+
+gboolean
+eel_str_is_equal (const char *string_a, const char *string_b)
+{
+	/* FIXME bugzilla.eazel.com 5450: Maybe we need to make this
+	 * treat 'NULL != ""', or have a flavor that does that. If we
+	 * didn't have code that already relies on 'NULL == ""', I
+	 * would change it right now.
+	 */
+	return eel_strcmp (string_a, string_b) == 0;
+}
+
+gboolean
+eel_istr_is_equal (const char *string_a, const char *string_b)
+{
+	/* FIXME bugzilla.eazel.com 5450: Maybe we need to make this
+	 * treat 'NULL != ""', or have a flavor that does that. If we
+	 * didn't have code that already relies on 'NULL == ""', I
+	 * would change it right now.
+	 */
+	return eel_strcasecmp (string_a, string_b) == 0;
+}
+
+gboolean
+eel_str_has_prefix (const char *haystack, const char *needle)
+{
+	return g_str_has_prefix (haystack == NULL ? "" : haystack,
+				 needle == NULL ? "" : needle);
+}
+
+gboolean
+eel_str_has_suffix (const char *haystack, const char *needle)
+{
+	if (needle == NULL) {
+		return TRUE;
+	}
+	if (haystack == NULL) {
+		return needle[0] == '\0';
+	}
+		
+	return g_str_has_suffix (haystack, needle);
+}
+
+gboolean
+eel_istr_has_prefix (const char *haystack, const char *needle)
+{
+	const char *h, *n;
+	char hc, nc;
+
+	/* Eat one character at a time. */
+	h = haystack == NULL ? "" : haystack;
+	n = needle == NULL ? "" : needle;
+	do {
+		if (*n == '\0') {
+			return TRUE;
+		}
+		if (*h == '\0') {
+			return FALSE;
+		}
+		hc = *h++;
+		nc = *n++;
+		hc = g_ascii_tolower (hc);
+		nc = g_ascii_tolower (nc);
+	} while (hc == nc);
+	return FALSE;
+}
+
+gboolean
+eel_istr_has_suffix (const char *haystack, const char *needle)
+{
+	const char *h, *n;
+	char hc, nc;
+
+	if (needle == NULL) {
+		return TRUE;
+	}
+	if (haystack == NULL) {
+		return needle[0] == '\0';
+	}
+		
+	/* Eat one character at a time. */
+	h = haystack + strlen (haystack);
+	n = needle + strlen (needle);
+	do {
+		if (n == needle) {
+			return TRUE;
+		}
+		if (h == haystack) {
+			return FALSE;
+		}
+		hc = *--h;
+		nc = *--n;
+		hc = g_ascii_tolower (hc);
+		nc = g_ascii_tolower (nc);
+	} while (hc == nc);
+	return FALSE;
+}
+
+/**
+ * eel_str_get_prefix:
+ * Get a new string containing the first part of an existing string.
+ * 
+ * @source: The string whose prefix should be extracted.
+ * @delimiter: The string that marks the end of the prefix.
+ * 
+ * Return value: A newly-allocated string that that matches the first part
+ * of @source, up to but not including the first occurrence of
+ * @delimiter. If @source is NULL, returns NULL. If 
+ * @delimiter is NULL, returns a copy of @source.
+ * If @delimiter does not occur in @source, returns
+ * a copy of @source.
+ **/
+char *
+eel_str_get_prefix (const char *source, 
+			 const char *delimiter)
+{
+	char *prefix_start;
+
+	if (source == NULL) {
+		return NULL;
+	}
+
+	if (delimiter == NULL) {
+		return g_strdup (source);
+	}
+
+	prefix_start = strstr (source, delimiter);
+
+	if (prefix_start == NULL) {
+		return g_strdup ("");
+	}
+
+	return g_strndup (source, prefix_start - source);
+}
+
+gboolean
+eel_str_to_int (const char *string, int *integer)
+{
+	long result;
+	char *parse_end;
+
+	/* Check for the case of an empty string. */
+	if (string == NULL || *string == '\0') {
+		return FALSE;
+	}
+	
+	/* Call the standard library routine to do the conversion. */
+	errno = 0;
+	result = strtol (string, &parse_end, 0);
+
+	/* Check that the result is in range. */
+	if ((result == G_MINLONG || result == G_MAXLONG) && errno == ERANGE) {
+		return FALSE;
+	}
+	if (result < G_MININT || result > G_MAXINT) {
+		return FALSE;
+	}
+
+	/* Check that all the trailing characters are spaces. */
+	while (*parse_end != '\0') {
+		if (!g_ascii_isspace (*parse_end++)) {
+			return FALSE;
+		}
+	}
+
+	/* Return the result. */
+	*integer = result;
+	return TRUE;
+}
+
+char *
+eel_str_double_underscores (const char *string)
+{
+	int underscores;
+	const char *p;
+	char *q;
+	char *escaped;
+	
+	if (string == NULL) {
+		return NULL;
+	}
+	
+	underscores = 0;
+	for (p = string; *p != '\0'; p++) {
+		underscores += (*p == '_');
+	}
+	
+	if (underscores == 0) {
+		return g_strdup (string);
+	}
+
+	escaped = g_new (char, strlen (string) + underscores + 1);
+	for (p = string, q = escaped; *p != '\0'; p++, q++) {
+		/* Add an extra underscore. */
+		if (*p == '_') {
+			*q++ = '_';
+		}
+		*q = *p;
+	}
+	*q = '\0';
+	
+	return escaped;
+}
+
+char *
+eel_str_capitalize (const char *string)
+{
+	char *capitalized;
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	capitalized = g_strdup (string);
+
+	capitalized[0] = g_ascii_toupper (capitalized[0]);
+
+	return capitalized;
+}
+
+/* Note: eel_string_ellipsize_* that use a length in pixels
+ * rather than characters can be found in eel_gdk_extensions.h
+ * 
+ * FIXME bugzilla.eazel.com 5089: 
+ * we should coordinate the names of eel_string_ellipsize_*
+ * and eel_str_*_truncate so that they match better and reflect
+ * their different behavior.
+ */
+char *
+eel_str_middle_truncate (const char *string,
+			 guint truncate_length)
+{
+	char *truncated;
+	guint length;
+	guint num_left_chars;
+	guint num_right_chars;
+
+	const char delimter[] = "...";
+	const guint delimter_length = strlen (delimter);
+	const guint min_truncate_length = delimter_length + 2;
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	/* It doesnt make sense to truncate strings to less than
+	 * the size of the delimiter plus 2 characters (one on each
+	 * side)
+	 */
+	if (truncate_length < min_truncate_length) {
+		return g_strdup (string);
+	}
+
+	length = g_utf8_strlen (string, -1);
+
+	/* Make sure the string is not already small enough. */
+	if (length <= truncate_length) {
+		return g_strdup (string);
+	}
+
+	/* Find the 'middle' where the truncation will occur. */
+	num_left_chars = (truncate_length - delimter_length) / 2;
+	num_right_chars = truncate_length - num_left_chars - delimter_length;
+
+	truncated = g_new (char, strlen (string) + 1);
+
+	g_utf8_strncpy (truncated, string, num_left_chars);
+	strcat (truncated, delimter);
+	strcat (truncated, g_utf8_offset_to_pointer  (string, length - num_right_chars));
+	
+	return truncated;
+}
+
+char *
+eel_str_strip_substring_and_after (const char *string,
+					const char *substring)
+{
+	const char *substring_position;
+
+	g_return_val_if_fail (substring != NULL, g_strdup (string));
+	g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	substring_position = strstr (string, substring);
+	if (substring_position == NULL) {
+		return g_strdup (string);
+	}
+
+	return g_strndup (string,
+			  substring_position - string);
+}
+
+char *
+eel_str_replace_substring (const char *string,
+				const char *substring,
+				const char *replacement)
+{
+	int substring_length, replacement_length, result_length, remaining_length;
+	const char *p, *substring_position;
+	char *result, *result_position;
+
+	g_return_val_if_fail (substring != NULL, g_strdup (string));
+	g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
+
+	if (string == NULL) {
+		return NULL;
+	}
+
+	substring_length = strlen (substring);
+	replacement_length = eel_strlen (replacement);
+
+	result_length = strlen (string);
+	for (p = string; ; p = substring_position + substring_length) {
+		substring_position = strstr (p, substring);
+		if (substring_position == NULL) {
+			break;
+		}
+		result_length += replacement_length - substring_length;
+	}
+
+	result = g_malloc (result_length + 1);
+
+	result_position = result;
+	for (p = string; ; p = substring_position + substring_length) {
+		substring_position = strstr (p, substring);
+		if (substring_position == NULL) {
+			remaining_length = strlen (p);
+			memcpy (result_position, p, remaining_length);
+			result_position += remaining_length;
+			break;
+		}
+		memcpy (result_position, p, substring_position - p);
+		result_position += substring_position - p;
+		memcpy (result_position, replacement, replacement_length);
+		result_position += replacement_length;
+	}
+	g_assert (result_position - result == result_length);
+	result_position[0] = '\0';
+
+	return result;
+}
+
+/**************** Custom printf ***********/
+
+typedef struct {
+	const char *start;
+	const char *end;
+	GString *format;
+	int arg_pos;
+	int width_pos;
+	int width_format_index;
+	int precision_pos;  
+	int precision_format_index;
+} ConversionInfo;
+
+enum {
+	ARG_TYPE_INVALID,
+	ARG_TYPE_INT,
+	ARG_TYPE_LONG,
+	ARG_TYPE_LONG_LONG,
+	ARG_TYPE_SIZE,
+	ARG_TYPE_LONG_DOUBLE,
+	ARG_TYPE_DOUBLE,
+	ARG_TYPE_POINTER
+};
+
+typedef int ArgType; /* An int, because custom are < 0 */
+
+
+static const char *
+get_position (const char *format, int *i)
+{
+	const char *p;
+	
+	p = format;
+	
+	if (g_ascii_isdigit (*p)) {
+		p++;
+		
+		while (g_ascii_isdigit (*p)) {
+			p++;
+		}
+		
+		if (*p == '$') {
+			if (i != NULL) {
+				*i = atoi (format) - 1;
+			}
+			return p + 1;
+		}
+	}
+	
+	return format;
+}
+
+static gboolean
+is_flag (char c)
+{
+	return strchr ("#0- +'I", c) != NULL;
+}
+
+static gboolean
+is_length_modifier (char c)
+{
+	return strchr ("hlLjzt", c) != NULL;
+}
+
+
+static ArgType
+get_arg_type_from_format (EelPrintfHandler *custom_handlers,
+			  const char *format,
+			  int len)
+{
+	int i;
+	char c;
+	
+	c = format[len-1];
+	
+	if (custom_handlers != NULL) {
+		for (i = 0; custom_handlers[i].character != 0; i++) {
+			if (custom_handlers[i].character == c) {
+				return -(i + 1);
+			}
+		}
+	}
+	
+	switch (c) {
+	case 'd':
+	case 'i':
+	case 'o':
+	case 'u':
+	case 'x':
+	case 'X':
+		if (g_str_has_prefix (format, "ll")) {
+			return ARG_TYPE_LONG_LONG;
+		}
+		if (g_str_has_prefix (format, "l")) {
+			return ARG_TYPE_LONG;
+		}
+		if (g_str_has_prefix (format, "l")) {
+			return ARG_TYPE_LONG;
+		}
+		if (g_str_has_prefix (format, "z")) {
+			return ARG_TYPE_SIZE;
+		}
+		return ARG_TYPE_INT;
+	case 'e':
+	case 'E':
+	case 'f':
+	case 'F':
+	case 'g':
+	case 'G':
+	case 'a':
+	case 'A':
+		if (g_str_has_prefix (format, "L")) {
+			return ARG_TYPE_LONG_DOUBLE;
+		}
+		return ARG_TYPE_DOUBLE;
+	case 'c':
+		return ARG_TYPE_INT;
+	case 's':
+	case 'p':
+	case 'n':
+		return ARG_TYPE_POINTER;
+	}
+	return ARG_TYPE_INVALID;
+}
+
+static void
+skip_argv (va_list *va,
+	   ArgType type,
+	   EelPrintfHandler *custom_handlers)
+{
+	if (type < 0) {
+		custom_handlers[-type - 1].skip (va);
+		return;
+	}
+	
+	switch (type) {
+	default:
+	case ARG_TYPE_INVALID:
+		return;
+		
+	case ARG_TYPE_INT:
+		(void) va_arg (*va, int);
+		break;
+	case ARG_TYPE_LONG:
+		(void) va_arg (*va, long int);
+		break;
+	case ARG_TYPE_LONG_LONG:
+		(void) va_arg (*va, long long int);
+		break;
+	case ARG_TYPE_SIZE:
+		(void) va_arg (*va, gsize);
+		break;
+	case ARG_TYPE_LONG_DOUBLE:
+		(void) va_arg (*va, long double);
+		break;
+	case ARG_TYPE_DOUBLE:
+		(void) va_arg (*va, double);
+		break;
+	case ARG_TYPE_POINTER:
+		(void) va_arg (*va, void *);
+		break;
+	}
+}
+
+static void
+skip_to_arg (va_list *va,
+	     ArgType *types,
+	     EelPrintfHandler *custom_handlers,
+	     int n)
+{
+	int i;
+	for (i = 0; i < n; i++) {
+		skip_argv (va, types[i], custom_handlers);
+	}
+}
+
+char *
+eel_strdup_vprintf_with_custom (EelPrintfHandler *custom,
+				const char *format,
+				va_list va_orig)
+{
+	va_list va;
+	const char *p;
+	int num_args, i, j;
+	ArgType *args;
+	ArgType type;
+	ConversionInfo *conversions;
+	GString *f, *str;
+	const char *flags, *width, *prec, *mod, *pos;
+	char *s;
+	
+	num_args = 0;
+	for (p = format; *p != 0; p++) {
+		if (*p == '%') {
+			p++;
+			if (*p != '%') {
+				num_args++;
+			}
+		}
+	}
+	
+	args = g_new0 (ArgType, num_args * 3 + 1);
+	conversions = g_new0 (ConversionInfo, num_args);
+	
+	/* i indexes conversions, j indexes args */
+	i = 0; j = 0;
+	p = format;
+	while (*p != 0) {
+		if (*p != '%') {
+			p++;
+			continue;
+		}
+		p++;
+		if (*p == '%') {
+			p++;
+			continue;
+		}
+		
+		/* We got a real conversion: */
+		f = g_string_new ("%");
+		conversions[i].start = p - 1;
+		
+		/* First comes the positional arg */
+		
+		pos = p;
+		p = get_position (p, NULL);
+		
+		/* Then flags */
+		flags = p;
+		while (is_flag (*p)) {
+			p++;
+		}
+		g_string_append_len (f, flags, p - flags);
+		
+		/* Field width */
+		
+		if (*p == '*') {
+			p++;
+			p = get_position (p, &j);
+			args[j] = ARG_TYPE_INT;
+			conversions[i].width_pos = j++;
+			conversions[i].width_format_index = f->len;
+		} else {
+			conversions[i].width_pos = -1;
+			conversions[i].width_format_index = -1;
+			width = p;
+			while (g_ascii_isdigit (*p)) {
+				p++;
+			}      
+			g_string_append_len (f, width, p - width);
+		}
+		
+		/* Precision */
+		conversions[i].precision_pos = -1;
+		conversions[i].precision_format_index = -1;
+		if (*p == '.') {
+			g_string_append_c (f, '.');
+			p++;
+			
+			if (*p == '*') {
+				p++;
+				p = get_position (p, &j);
+				args[j] = ARG_TYPE_INT;
+				conversions[i].precision_pos = j++;
+				conversions[i].precision_format_index = f->len;
+			} else {
+				prec = p;
+				while (g_ascii_isdigit (*p) || *p == '-') {
+					p++;
+				}      
+				g_string_append_len (f, prec, p - prec);
+			}
+		}
+		
+		/* length modifier */
+		
+		mod = p;
+		
+		while (is_length_modifier (*p)) {
+			p++;
+		}
+		
+		/* conversion specifier */
+		if (*p != 0)
+			p++;
+		
+		g_string_append_len (f, mod, p - mod);
+		
+		get_position (pos, &j);
+		args[j] = get_arg_type_from_format (custom, mod, p - mod);
+		conversions[i].arg_pos = j++;
+		conversions[i].format = f;
+		conversions[i].end = p;
+		
+		i++;
+	}
+	
+	g_assert (i == num_args);
+	
+	str = g_string_new ("");
+	
+	p = format;
+	for (i = 0; i < num_args; i++) {
+		g_string_append_len (str, p, conversions[i].start - p);
+		p = conversions[i].end;
+		
+		if (conversions[i].precision_pos != -1) {
+			char *val;
+			
+			G_VA_COPY(va, va_orig);
+			skip_to_arg (&va, args, custom, conversions[i].precision_pos);
+			val = g_strdup_vprintf ("%d", va);
+			va_end (va);
+			
+			g_string_insert (conversions[i].format,
+					 conversions[i].precision_format_index,
+					 val);
+			
+			g_free (val);
+		}
+		
+		if (conversions[i].width_pos != -1) {
+			char *val;
+			
+			G_VA_COPY(va, va_orig);
+			skip_to_arg (&va, args, custom, conversions[i].width_pos);
+			val = g_strdup_vprintf ("%d", va);
+			va_end (va);
+			
+			g_string_insert (conversions[i].format,
+					 conversions[i].width_format_index,
+					 val);
+			
+			g_free (val);
+		}
+		
+		G_VA_COPY(va, va_orig);
+		skip_to_arg (&va, args, custom, conversions[i].arg_pos);
+		type = args[conversions[i].arg_pos];
+		if (type < 0) {
+			s = custom[-type - 1].to_string (conversions[i].format->str, va);
+			g_string_append (str, s);
+			g_free (s);
+		} else{
+			g_string_append_vprintf (str, conversions[i].format->str, va);
+		}
+		va_end (va);
+		
+		g_string_free (conversions[i].format, TRUE);
+	}
+	g_string_append (str, p);
+	
+	g_free (args);
+	g_free (conversions);
+	
+	return g_string_free (str, FALSE);
+}
+
+char *
+eel_strdup_printf_with_custom (EelPrintfHandler *handlers,
+			       const char *format,
+			       ...)
+{
+	va_list va;
+	char *res;
+	
+	va_start (va, format);
+	res = eel_strdup_vprintf_with_custom (handlers, format, va);
+	va_end (va);
+	
+	return res;
+}
+
+/*********** refcounted strings ****************/
+
+G_LOCK_DEFINE_STATIC (unique_ref_strs);
+static GHashTable *unique_ref_strs = NULL;
+
+static eel_ref_str
+eel_ref_str_new_internal (const char *string, int start_count)
+{
+	char *res;
+	volatile gint *count;
+	gsize len;
+
+	len = strlen (string);
+	res = g_malloc (sizeof (gint) + len + 1);
+	count = (volatile gint *)res;
+	*count = start_count;
+	res += sizeof(gint);
+	memcpy (res, string, len + 1);
+	return res;
+}
+
+eel_ref_str
+eel_ref_str_new (const char *string)
+{
+	if (string == NULL) {
+		return NULL;
+	}
+	
+	return eel_ref_str_new_internal (string, 1);
+}
+
+eel_ref_str
+eel_ref_str_get_unique (const char *string)
+{
+	eel_ref_str res;
+
+	if (string == NULL) {
+		return NULL;
+	}
+	
+	G_LOCK (unique_ref_strs);
+	if (unique_ref_strs == NULL) {
+		unique_ref_strs = g_hash_table_new (g_str_hash, g_str_equal);
+	}
+
+	res = g_hash_table_lookup (unique_ref_strs, string);
+	if (res != NULL) {
+		eel_ref_str_ref (res);
+	} else {
+		res = eel_ref_str_new_internal (string, 0x80000001);
+		g_hash_table_insert (unique_ref_strs, res, res);
+	}
+	
+	G_UNLOCK (unique_ref_strs);
+
+	return res;
+}
+
+eel_ref_str
+eel_ref_str_ref (eel_ref_str str)
+{
+	volatile gint *count;
+
+	count = (volatile gint *)((char *)str - sizeof (gint));
+	g_atomic_int_add (count, 1);
+
+	return str;
+}
+
+void
+eel_ref_str_unref (eel_ref_str str)
+{
+	volatile gint *count;
+	gint old_ref;
+
+	if (str == NULL)
+		return;
+	
+	count = (volatile gint *)((char *)str - sizeof (gint));
+
+	old_ref = g_atomic_int_get (count);
+	if (old_ref == 1) {
+		g_free ((char *)count);
+	} else if (old_ref == 0x80000001) {
+		G_LOCK (unique_ref_strs);
+		/* Need to recheck after taking lock to avoid races with _get_unique() */
+		if (g_atomic_int_exchange_and_add (count, -1) == 0x80000001) {
+			g_hash_table_remove (unique_ref_strs, (char *)str);
+			g_free ((char *)count);
+		} 
+		G_UNLOCK (unique_ref_strs);
+	} else {
+		g_atomic_int_exchange_and_add (count, -1);
+	}
+}
+
+
+#if !defined (EEL_OMIT_SELF_CHECK)
+
+static int
+call_str_to_int (const char *string)
+{
+	int integer;
+
+	integer = 9999;
+	eel_str_to_int (string, &integer);
+	return integer;
+}
+
+static void
+verify_printf (const char *format, ...)
+{
+	va_list va;
+	char *orig, *new;
+	
+	va_start (va, format);
+	orig = g_strdup_vprintf (format, va);
+	va_end (va);
+	
+	va_start (va, format);
+	new = eel_strdup_vprintf_with_custom (NULL, format, va);
+	va_end (va);
+	
+	EEL_CHECK_STRING_RESULT (new, orig);
+	
+	g_free (orig);
+}
+
+static char *
+custom1_to_string (char *format, va_list va)
+{
+	int i;
+	
+	i = va_arg (va, int);
+	
+	return g_strdup_printf ("c1-%d-", i);
+}
+
+static void
+custom1_skip (va_list *va)
+{
+	(void) va_arg (*va, int);
+}
+
+static char *
+custom2_to_string (char *format, va_list va)
+{
+	char *s;
+	
+	s = va_arg (va, char *);
+	
+	return g_strdup_printf ("c2-%s-", s);
+}
+
+static void
+custom2_skip (va_list *va)
+{
+	(void) va_arg (*va, char *);
+}
+
+static EelPrintfHandler handlers[] = {
+	{ 'N', custom1_to_string, custom1_skip },
+	{ 'Y', custom2_to_string, custom2_skip },
+	{ 0 }
+};
+
+static void
+verify_custom (const char *orig, const char *format, ...)
+{
+	char *new;
+	va_list va;
+	
+	va_start (va, format);
+	new = eel_strdup_vprintf_with_custom (handlers, format, va);
+	va_end (va);
+	
+	EEL_CHECK_STRING_RESULT (new, orig);
+}
+
+void
+eel_self_check_string (void)
+{
+	int integer;
+
+	EEL_CHECK_INTEGER_RESULT (eel_strlen (NULL), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_strlen (""), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_strlen ("abc"), 3);
+
+	EEL_CHECK_INTEGER_RESULT (eel_strcmp (NULL, NULL), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_strcmp (NULL, ""), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_strcmp ("", NULL), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_strcmp ("a", "a"), 0);
+	EEL_CHECK_INTEGER_RESULT (eel_strcmp ("aaab", "aaab"), 0);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp (NULL, "a") < 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("a", NULL) > 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("", "a") < 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("a", "") > 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("a", "b") < 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("a", "ab") < 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("ab", "a") > 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("aaa", "aaab") < 0, TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_strcmp ("aaab", "aaa") > 0, TRUE);
+
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix (NULL, NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix (NULL, ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("", NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("a", "a"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("aaab", "aaab"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix (NULL, "a"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("a", NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("", "a"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("a", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("a", "b"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("a", "ab"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("ab", "a"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("aaa", "aaab"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_prefix ("aaab", "aaa"), TRUE);
+
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix (NULL, NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix (NULL, ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("", NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("a", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("", "a"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("a", "a"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("aaab", "aaab"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix (NULL, "a"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("a", NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("", "a"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("a", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("a", "b"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("a", "ab"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("ab", "a"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("ab", "b"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("aaa", "baaa"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_has_suffix ("baaa", "aaa"), TRUE);
+
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix (NULL, NULL), NULL);
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix (NULL, "foo"), NULL);
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("foo", NULL), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("", ""), "");
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("", "foo"), "");
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("foo", ""), "");
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("foo", "foo"), "");
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("foo:", ":"), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("foo:bar", ":"), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_get_prefix ("footle:bar", "tle:"), "foo");	
+
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores (NULL), NULL);
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores (""), "");
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_"), "__");
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo"), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar"), "foo__bar");
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar_2"), "foo__bar__2");
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_foo"), "__foo");
+	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_"), "foo__");
+
+	EEL_CHECK_STRING_RESULT (eel_str_capitalize (NULL), NULL);
+	EEL_CHECK_STRING_RESULT (eel_str_capitalize (""), "");
+	EEL_CHECK_STRING_RESULT (eel_str_capitalize ("foo"), "Foo");
+	EEL_CHECK_STRING_RESULT (eel_str_capitalize ("Foo"), "Foo");
+
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 0), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 1), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 3), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 4), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 5), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 6), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 7), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 0), "a_much_longer_foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 1), "a_much_longer_foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 2), "a_much_longer_foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 3), "a_much_longer_foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 4), "a_much_longer_foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 5), "a...o");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 6), "a...oo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 7), "a_...oo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 8), "a_...foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 9), "a_m...foo");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 8), "so...ven");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 8), "so...odd");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 9), "som...ven");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 9), "som...odd");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 10), "som...even");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 10), "som..._odd");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 11), "some...even");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 11), "some..._odd");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 12), "some..._even");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 12), "some...g_odd");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 13), "somet..._even");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 14), "something_even");
+	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
+
+	#define TEST_INTEGER_CONVERSION_FUNCTIONS(string, boolean_result, integer_result) \
+		EEL_CHECK_BOOLEAN_RESULT (eel_str_to_int (string, &integer), boolean_result); \
+		EEL_CHECK_INTEGER_RESULT (call_str_to_int (string), integer_result);
+
+	TEST_INTEGER_CONVERSION_FUNCTIONS (NULL, FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("a", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS (".", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("0", TRUE, 0)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("1", TRUE, 1)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("+1", TRUE, 1)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("-1", TRUE, -1)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("2147483647", TRUE, 2147483647)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("2147483648", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("+2147483647", TRUE, 2147483647)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("+2147483648", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("-2147483648", TRUE, INT_MIN)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("-2147483649", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("1a", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("0.0", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("1e1", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("21474836470", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("+21474836470", FALSE, 9999)
+	TEST_INTEGER_CONVERSION_FUNCTIONS ("-21474836480", FALSE, 9999)
+
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_is_equal (NULL, NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_is_equal (NULL, ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_is_equal ("", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_is_equal ("", NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_is_equal ("", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_is_equal ("foo", "foo"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_str_is_equal ("foo", "bar"), FALSE);
+
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal (NULL, NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal (NULL, ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal ("", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal ("", NULL), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal ("", ""), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal ("foo", "foo"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal ("foo", "bar"), FALSE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal ("Foo", "foo"), TRUE);
+	EEL_CHECK_BOOLEAN_RESULT (eel_istr_is_equal ("foo", "Foo"), TRUE);
+
+	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after (NULL, "bar"), NULL);
+	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("", "bar"), "");
+	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo", "bar"), "foo");
+	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar", "bar"), "foo ");
+	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar xxx", "bar"), "foo ");
+	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("bar", "bar"), "");
+
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", NULL), NULL);
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", "bar"), NULL);
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", NULL), "bar");
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", ""), "");
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", "bar"), "");
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", ""), "bar");
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("xxx", "x", "foo"), "foofoofoo");
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("fff", "f", "foo"), "foofoofoo");
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "foo", "f"), "fff");
+	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "f", ""), "oooooo");
+
+	verify_printf ("%.*s", 2, "foo");
+	verify_printf ("%*.*s", 2, 4, "foo");
+	verify_printf ("before %5$*1$.*2$s between %6$*3$.*4$d after",
+		       4, 5, 6, 7, "foo", G_PI);
+	verify_custom ("c1-42- c2-foo-","%N %Y", 42 ,"foo");
+	verify_custom ("c1-42- bar c2-foo-","%N %s %Y", 42, "bar" ,"foo");
+	verify_custom ("c1-42- bar c2-foo-","%3$N %2$s %1$Y","foo", "bar", 42);
+
+}
+
+#endif /* !EEL_OMIT_SELF_CHECK */

Added: trunk/eel/eel-string.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-string.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,120 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   eel-string.h: String routines to augment <string.h>.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#ifndef EEL_STRING_H
+#define EEL_STRING_H
+
+#include <glib.h>
+#include <string.h>
+#include <stdarg.h>
+
+/* We use the "str" abbrevation to mean char * string, since
+ * "string" usually means g_string instead. We use the "istr"
+ * abbreviation to mean a case-insensitive char *.
+ */
+
+
+/* NULL is allowed for all the str parameters to these functions. */
+
+/* Versions of basic string functions that allow NULL, and handle
+ * cases that the standard ones get a bit wrong for our purposes.
+ */
+size_t   eel_strlen                        (const char    *str);
+char *   eel_strchr                        (const char    *haystack,
+					    char           needle);
+int      eel_strcmp                        (const char    *str_a,
+					    const char    *str_b);
+int      eel_strcasecmp                    (const char    *str_a,
+					    const char    *str_b);
+int      eel_strcmp_case_breaks_ties       (const char    *str_a,
+					    const char    *str_b);
+
+/* Other basic string operations. */
+gboolean eel_str_is_empty                  (const char    *str_or_null);
+gboolean eel_str_is_equal                  (const char    *str_a,
+					    const char    *str_b);
+gboolean eel_istr_is_equal                 (const char    *str_a,
+					    const char    *str_b);
+gboolean eel_str_has_prefix                (const char    *target,
+					    const char    *prefix);
+char *   eel_str_get_prefix                (const char    *source,
+					    const char    *delimiter);
+gboolean eel_istr_has_prefix               (const char    *target,
+					    const char    *prefix);
+gboolean eel_str_has_suffix                (const char    *target,
+					    const char    *suffix);
+gboolean eel_istr_has_suffix               (const char    *target,
+					    const char    *suffix);
+
+/* Conversions to and from strings. */
+gboolean eel_str_to_int                    (const char    *str,
+					    int           *integer);
+
+/* Escape function for '_' character. */
+char *   eel_str_double_underscores        (const char    *str);
+
+/* Capitalize a string */
+char *   eel_str_capitalize                (const char    *str);
+
+/* Middle truncate a string to a maximum of truncate_length characters.
+ * The resulting string will be truncated in the middle with a "..."
+ * delimiter.
+ */
+char *   eel_str_middle_truncate           (const char    *str,
+					    guint          truncate_length);
+
+
+/* Remove all characters after the passed-in substring. */
+char *   eel_str_strip_substring_and_after (const char    *str,
+					    const char    *substring);
+
+/* Replace all occurrences of substring with replacement. */
+char *   eel_str_replace_substring         (const char    *str,
+					    const char    *substring,
+					    const char    *replacement);
+
+typedef char * eel_ref_str;
+
+eel_ref_str eel_ref_str_new        (const char  *string);
+eel_ref_str eel_ref_str_get_unique (const char  *string);
+eel_ref_str eel_ref_str_ref        (eel_ref_str  str);
+void        eel_ref_str_unref      (eel_ref_str  str);
+
+#define eel_ref_str_peek(__str) ((const char *)(__str))
+
+
+typedef struct {
+  char character;
+  char *(*to_string) (char *format, va_list va);
+  void (*skip) (va_list *va);
+} EelPrintfHandler;
+
+char *eel_strdup_printf_with_custom (EelPrintfHandler *handlers,
+				     const char *format,
+				     ...);
+char *eel_strdup_vprintf_with_custom (EelPrintfHandler *custom,
+				      const char *format,
+				      va_list va);
+
+#endif /* EEL_STRING_H */

Added: trunk/eel/eel-types.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-types.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,73 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* eel-types.h - 
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Maciej Stachowiak <mjs eazel com>
+*/
+
+
+#include <config.h>
+#include <gtk/gtk.h>
+
+#define EEL_COMPILATION 1
+#include <eel/eel.h>
+
+#include "eel-type-builtins-vars.c"
+#include "eel-type-builtins-evals.c"
+
+void
+eel_type_init (void)
+{
+	int i;
+	GtkType type_id;
+	static gboolean initialized = FALSE;
+	
+	static struct {
+		const gchar *type_name;
+		GtkType *type_id;
+		GtkType parent;
+		gconstpointer pointer1;
+		gpointer pointer2;
+	} builtin_info[EEL_TYPE_N_BUILTINS] = {
+#include "eel-type-builtins-ids.c"
+	};
+
+	if (initialized) {
+		return;
+	}
+	initialized = TRUE;
+	
+	for (i = 0; i < EEL_TYPE_N_BUILTINS; i++) {
+		type_id = G_TYPE_INVALID;
+
+		if (builtin_info[i].parent == G_TYPE_ENUM) {
+			type_id = g_enum_register_static (builtin_info[i].type_name, 
+							  builtin_info[i].pointer1);
+		} else if (builtin_info[i].parent == G_TYPE_FLAGS) {
+			type_id = g_flags_register_static (builtin_info[i].type_name, 
+							   builtin_info[i].pointer1);
+		} else {
+			g_assert_not_reached ();
+		}
+
+		g_assert (type_id != G_TYPE_INVALID);
+		*builtin_info[i].type_id = type_id;
+	}
+}
+

Added: trunk/eel/eel-types.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-types.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,32 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* eel-types.h - 
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Maciej Stachowiak <mjs eazel com>
+*/
+
+#ifndef EEL_TYPES_H
+#define EEL_TYPES_H
+
+#include <eel/eel-type-builtins.h>
+
+void eel_type_init (void);
+
+#endif /* EEL_TYPES_H */
+

Added: trunk/eel/eel-vfs-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-vfs-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,178 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-vfs-extensions.c - gnome-vfs extensions.  Its likely some of these will 
+                          be part of gnome-vfs in the future.
+
+   Copyright (C) 1999, 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+	    Pavel Cisler <pavel eazel com>
+	    Mike Fleming  <mfleming eazel com>
+            John Sullivan <sullivan eazel com>
+*/
+
+#include <config.h>
+#include "eel-i18n.h"
+#include "eel-vfs-extensions.h"
+#include "eel-glib-extensions.h"
+#include "eel-lib-self-check-functions.h"
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "eel-string.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+gboolean
+eel_uri_is_trash (const char *uri)
+{
+	return eel_istr_has_prefix (uri, "trash:");
+}
+
+gboolean
+eel_uri_is_search (const char *uri)
+{
+	return eel_istr_has_prefix (uri, EEL_SEARCH_URI);
+}
+
+gboolean
+eel_uri_is_desktop (const char *uri)
+{
+	return eel_istr_has_prefix (uri, EEL_DESKTOP_URI);
+}
+
+char *
+eel_make_valid_utf8 (const char *name)
+{
+	GString *string;
+	const char *remainder, *invalid;
+	int remaining_bytes, valid_bytes;
+
+	string = NULL;
+	remainder = name;
+	remaining_bytes = strlen (name);
+
+	while (remaining_bytes != 0) {
+		if (g_utf8_validate (remainder, remaining_bytes, &invalid)) {
+			break;
+		}
+		valid_bytes = invalid - remainder;
+
+		if (string == NULL) {
+			string = g_string_sized_new (remaining_bytes);
+		}
+		g_string_append_len (string, remainder, valid_bytes);
+		g_string_append_c (string, '?');
+
+		remaining_bytes -= valid_bytes + 1;
+		remainder = invalid + 1;
+	}
+
+	if (string == NULL) {
+		return g_strdup (name);
+	}
+
+	g_string_append (string, remainder);
+	g_string_append (string, _(" (invalid Unicode)"));
+	g_assert (g_utf8_validate (string->str, -1, NULL));
+
+	return g_string_free (string, FALSE);
+}
+
+/**
+ * eel_format_uri_for_display:
+ *
+ * Filter, modify, unescape and change URIs to make them appropriate
+ * to display to users. The conversion is done such that the roundtrip
+ * to UTf8 is reversible.
+ * 
+ * Rules:
+ * 	file: URI's without fragments should appear as local paths
+ * 	file: URI's with fragments should appear as file: URI's
+ * 	All other URI's appear as expected
+ *
+ * @uri: a URI
+ *
+ * returns a g_malloc'd UTF8 string
+ **/
+char *
+eel_format_uri_for_display (const char *uri) 
+{
+	GFile *file;
+	char *res;
+
+	file = g_file_new_for_uri (uri);
+	res = g_file_get_parse_name (file);
+	g_object_unref (file);
+	return res;
+}
+
+char *
+eel_filename_strip_extension (const char * filename_with_extension)
+{
+	char *filename, *end, *end2;
+
+	if (filename_with_extension == NULL) {
+		return NULL;
+	}
+	
+	filename = g_strdup (filename_with_extension);
+
+	end = strrchr (filename, '.');
+
+	if (end && end != filename) {
+		if (strcmp (end, ".gz") == 0 ||
+		    strcmp (end, ".bz2") == 0 ||
+		    strcmp (end, ".sit") == 0 ||
+		    strcmp (end, ".Z") == 0) {
+			end2 = end - 1;
+			while (end2 > filename &&
+			       *end2 != '.') {
+				end2--;
+			}
+			if (end2 != filename) {
+				end = end2;
+			}
+		}
+		*end = '\0';
+	}
+
+	return filename;
+}
+
+void
+eel_filename_get_rename_region (const char           *filename,
+				int                  *start_offset,
+				int                  *end_offset)
+{
+	char *filename_without_extension;
+
+	g_return_if_fail (start_offset != NULL);
+	g_return_if_fail (end_offset != NULL);
+
+	*start_offset = 0;
+	*end_offset = 0;
+
+	g_return_if_fail (filename != NULL);
+
+	filename_without_extension = eel_filename_strip_extension (filename);
+	*end_offset = g_utf8_strlen (filename_without_extension, -1);
+
+	g_free (filename_without_extension);
+}

Added: trunk/eel/eel-vfs-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-vfs-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,57 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-vfs-extensions.h - gnome-vfs extensions.  Its likely some of these will 
+                          be part of gnome-vfs in the future.
+
+   Copyright (C) 1999, 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+	    Pavel Cisler <pavel eazel com>
+	    Mike Fleming  <mfleming eazel com>
+            John Sullivan <sullivan eazel com>
+*/
+
+#ifndef EEL_VFS_EXTENSIONS_H
+#define EEL_VFS_EXTENSIONS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define	EEL_TRASH_URI "trash:"
+#define EEL_DESKTOP_URI "x-nautilus-desktop:"
+#define EEL_SEARCH_URI "x-nautilus-search:"
+
+gboolean           eel_uri_is_trash                      (const char           *uri);
+gboolean           eel_uri_is_trash_folder               (const char           *uri);
+gboolean           eel_uri_is_in_trash                   (const char           *uri);
+gboolean           eel_uri_is_desktop                    (const char           *uri);
+gboolean           eel_uri_is_search                     (const char           *uri);
+
+
+char *             eel_format_uri_for_display            (const char           *uri);
+char *             eel_make_valid_utf8                   (const char           *name);
+
+char *             eel_filename_strip_extension          (const char           *filename);
+void               eel_filename_get_rename_region        (const char           *filename,
+							  int                  *start_offset,
+							  int                  *end_offset);
+
+G_END_DECLS
+
+#endif /* EEL_VFS_EXTENSIONS_H */

Added: trunk/eel/eel-wrap-table.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-wrap-table.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,1053 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-wrap-box.c - A table that can wrap its contents as needed.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#include <config.h>
+#include "eel-wrap-table.h"
+
+#include "eel-art-extensions.h"
+#include "eel-art-gtk-extensions.h"
+#include "eel-gtk-extensions.h"
+#include "eel-gtk-macros.h"
+#include "eel-types.h"
+#include <gtk/gtk.h>
+
+/* Arguments */
+enum
+{
+	PROP_0,
+	PROP_X_SPACING,
+	PROP_Y_SPACING,
+	PROP_X_JUSTIFICATION,
+	PROP_Y_JUSTIFICATION,
+	PROP_HOMOGENEOUS
+};
+
+/* Detail member struct */
+struct EelWrapTableDetails
+{
+	guint x_spacing;
+	guint y_spacing;
+	EelJustification x_justification;
+	EelJustification y_justification;
+	gboolean homogeneous;
+	GList *children;
+
+	guint is_scrolled : 1;
+	guint cols;
+};
+
+static void          eel_wrap_table_class_init           (EelWrapTableClass   *wrap_table_class);
+static void          eel_wrap_table_init                 (EelWrapTable        *wrap);
+/* GObjectClass methods */
+static void          eel_wrap_table_finalize             (GObject             *object);
+static void          eel_wrap_table_set_property         (GObject             *object,
+							  guint                property_id,
+							  const GValue        *value,
+							  GParamSpec          *pspec);
+static void          eel_wrap_table_get_property         (GObject             *object,
+							  guint                property_id,
+							  GValue              *value,
+							  GParamSpec          *pspec);
+/* GtkWidgetClass methods */
+static void          eel_wrap_table_size_request         (GtkWidget           *widget,
+							  GtkRequisition      *requisition);
+static int           eel_wrap_table_expose_event         (GtkWidget           *widget,
+							  GdkEventExpose      *event);
+static void          eel_wrap_table_size_allocate        (GtkWidget           *widget,
+							  GtkAllocation       *allocation);
+static void          eel_wrap_table_map                  (GtkWidget           *widget);
+static void          eel_wrap_table_unmap                (GtkWidget           *widget);
+static void          eel_wrap_table_realize              (GtkWidget           *widget);
+
+/* GtkContainerClass methods */
+static void          eel_wrap_table_add                  (GtkContainer        *container,
+							  GtkWidget           *widget);
+static void          eel_wrap_table_remove               (GtkContainer        *container,
+							  GtkWidget           *widget);
+static void          eel_wrap_table_forall               (GtkContainer        *container,
+							  gboolean             include_internals,
+							  GtkCallback          callback,
+							  gpointer             callback_data);
+static GtkType       eel_wrap_table_child_type           (GtkContainer        *container);
+
+
+/* Private EelWrapTable methods */
+static EelDimensions wrap_table_irect_max_dimensions     (const EelDimensions *one,
+							  const EelDimensions *two);
+static EelDimensions wrap_table_get_max_child_dimensions (const EelWrapTable  *wrap_table);
+static EelDimensions wrap_table_get_content_dimensions   (const EelWrapTable  *wrap_table);
+static EelIRect      wrap_table_get_content_bounds       (const EelWrapTable  *wrap_table);
+static gboolean      wrap_table_child_focus_in           (GtkWidget           *widget,
+							  GdkEventFocus       *event,
+							  gpointer             data);
+static void          wrap_table_layout                   (EelWrapTable        *wrap_table);
+
+
+EEL_CLASS_BOILERPLATE (EelWrapTable, eel_wrap_table, GTK_TYPE_CONTAINER)
+
+/* Class init methods */
+static void
+eel_wrap_table_class_init (EelWrapTableClass *wrap_table_class)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (wrap_table_class);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (wrap_table_class);
+	GtkContainerClass *container_class = GTK_CONTAINER_CLASS (wrap_table_class);
+
+	/* GObjectClass */
+	gobject_class->finalize = eel_wrap_table_finalize;
+	gobject_class->set_property = eel_wrap_table_set_property;
+	gobject_class->get_property = eel_wrap_table_get_property;
+	
+ 	/* GtkWidgetClass */
+ 	widget_class->size_request = eel_wrap_table_size_request;
+	widget_class->size_allocate = eel_wrap_table_size_allocate;
+ 	widget_class->expose_event = eel_wrap_table_expose_event;
+	widget_class->map = eel_wrap_table_map;
+	widget_class->unmap = eel_wrap_table_unmap;
+	widget_class->realize = eel_wrap_table_realize;
+
+ 	/* GtkContainerClass */
+	container_class->add = eel_wrap_table_add;
+	container_class->remove = eel_wrap_table_remove;
+	container_class->forall = eel_wrap_table_forall;
+	container_class->child_type = eel_wrap_table_child_type;
+
+	/* Register some the enum types we need */
+	eel_type_init ();
+
+	/* Arguments */
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_X_SPACING,
+		 g_param_spec_uint ("x_spacing", NULL, NULL,
+                                    0, G_MAXINT, 0, G_PARAM_READWRITE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_Y_SPACING,
+		 g_param_spec_uint ("y_spacing", NULL, NULL,
+				    0, G_MAXINT, 0, G_PARAM_READWRITE));
+	
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_X_JUSTIFICATION,
+		 g_param_spec_enum ("x_justification", NULL, NULL,
+				    EEL_TYPE_JUSTIFICATION,
+				    EEL_JUSTIFICATION_BEGINNING,
+				    G_PARAM_READWRITE));
+				    
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_Y_JUSTIFICATION,
+		 g_param_spec_enum ("y_justification", NULL, NULL,
+				    EEL_TYPE_JUSTIFICATION,
+				    EEL_JUSTIFICATION_BEGINNING,
+				    G_PARAM_READWRITE));
+				    
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_HOMOGENEOUS,
+		 g_param_spec_boolean ("homogenous", NULL, NULL,
+				       FALSE, G_PARAM_READWRITE));
+}
+
+static void
+eel_wrap_table_init (EelWrapTable *wrap_table)
+{
+	GTK_WIDGET_SET_FLAGS (wrap_table, GTK_NO_WINDOW);
+
+	wrap_table->details = g_new0 (EelWrapTableDetails, 1);
+	wrap_table->details->x_justification = EEL_JUSTIFICATION_BEGINNING;
+	wrap_table->details->y_justification = EEL_JUSTIFICATION_END;
+	wrap_table->details->cols = 1;
+}
+
+static void
+eel_wrap_table_finalize (GObject *object)
+{
+ 	EelWrapTable *wrap_table;
+	
+	wrap_table = EEL_WRAP_TABLE (object);
+
+	g_list_free (wrap_table->details->children);
+	g_free (wrap_table->details);
+
+	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+/* GObjectClass methods */
+
+static void
+eel_wrap_table_set_property (GObject      *object,
+			     guint         property_id,
+			     const GValue *value,
+			     GParamSpec   *pspec)
+{
+	EelWrapTable *wrap_table;
+	
+	g_assert (EEL_IS_WRAP_TABLE (object));
+
+ 	wrap_table = EEL_WRAP_TABLE (object);
+
+ 	switch (property_id)
+	{
+	case PROP_X_SPACING:
+		eel_wrap_table_set_x_spacing (wrap_table, g_value_get_uint (value));
+		break;
+
+	case PROP_Y_SPACING:
+		eel_wrap_table_set_y_spacing (wrap_table, g_value_get_uint (value));
+		break;
+
+	case PROP_X_JUSTIFICATION:
+		eel_wrap_table_set_x_justification (wrap_table, g_value_get_enum (value));
+		break;
+
+	case PROP_Y_JUSTIFICATION:
+		eel_wrap_table_set_y_justification (wrap_table, g_value_get_enum (value));
+		break;
+
+	case PROP_HOMOGENEOUS:
+		eel_wrap_table_set_homogeneous (wrap_table, g_value_get_boolean (value));
+		break;
+
+ 	default:
+		g_assert_not_reached ();
+	}
+}
+
+static void
+eel_wrap_table_get_property (GObject    *object,
+			     guint       property_id,
+			     GValue     *value,
+			     GParamSpec *pspec)
+{
+	EelWrapTable *wrap_table;
+
+	g_assert (EEL_IS_WRAP_TABLE (object));
+	
+	wrap_table = EEL_WRAP_TABLE (object);
+
+ 	switch (property_id)
+	{
+	case PROP_X_SPACING:
+		g_value_set_uint (value, eel_wrap_table_get_x_spacing (wrap_table));
+		break;
+
+	case PROP_Y_SPACING:
+		g_value_set_uint (value, eel_wrap_table_get_y_spacing (wrap_table));
+		break;
+
+	case PROP_X_JUSTIFICATION:
+		g_value_set_enum (value, eel_wrap_table_get_x_justification (wrap_table));
+		break;
+
+	case PROP_Y_JUSTIFICATION:
+		g_value_set_enum (value, eel_wrap_table_get_y_justification (wrap_table));
+		break;
+
+	case PROP_HOMOGENEOUS:
+		g_value_set_boolean (value, eel_wrap_table_get_homogeneous (wrap_table));
+		break;
+
+ 	default:
+		g_assert_not_reached ();
+	}
+}
+
+/* GtkWidgetClass methods */
+static void
+eel_wrap_table_size_request (GtkWidget *widget,
+				  GtkRequisition *requisition)
+{
+	EelWrapTable *wrap_table;
+	EelDimensions content_dimensions;
+
+ 	g_assert (EEL_IS_WRAP_TABLE (widget));
+ 	g_assert (requisition != NULL);
+
+  	wrap_table = EEL_WRAP_TABLE (widget);
+
+	content_dimensions = wrap_table_get_content_dimensions (wrap_table);
+
+	/* The -1 tells Satan to use as much space as is available */
+	requisition->width = -1;
+	requisition->height = content_dimensions.height + GTK_CONTAINER (widget)->border_width * 2;
+}
+
+static void
+eel_wrap_table_size_allocate (GtkWidget *widget,
+				   GtkAllocation *allocation)
+{
+	EelWrapTable *wrap_table;
+
+ 	g_assert (EEL_IS_WRAP_TABLE (widget));
+ 	g_assert (allocation != NULL);
+	
+  	wrap_table = EEL_WRAP_TABLE (widget);
+	
+	widget->allocation = *allocation;
+
+	wrap_table_layout (wrap_table);
+}
+
+static int
+eel_wrap_table_expose_event (GtkWidget *widget,
+			     GdkEventExpose *event)
+{
+	EelWrapTable *wrap_table;
+	GList *iterator;
+	
+	g_assert (EEL_IS_WRAP_TABLE (widget));
+	g_assert (GTK_WIDGET_REALIZED (widget));
+	g_assert (event != NULL);
+
+  	wrap_table = EEL_WRAP_TABLE (widget);
+
+	for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) {
+		g_assert (GTK_IS_WIDGET (iterator->data));
+		gtk_container_propagate_expose (GTK_CONTAINER (widget),
+						GTK_WIDGET (iterator->data),
+						event);
+	}
+
+	return FALSE;
+}
+
+static void
+eel_wrap_table_map (GtkWidget *widget)
+{
+	EelWrapTable *wrap_table;
+	GList *iterator;
+
+	g_assert (EEL_IS_WRAP_TABLE (widget));
+	
+	wrap_table = EEL_WRAP_TABLE (widget);
+
+ 	GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+	
+	for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) {
+		GtkWidget *item;
+
+		item = iterator->data;
+
+		if (GTK_WIDGET_VISIBLE (item) && !GTK_WIDGET_MAPPED (item)) {
+			gtk_widget_map (item);
+		}
+	}
+}
+
+static void
+eel_wrap_table_unmap (GtkWidget *widget)
+{
+	EelWrapTable *wrap_table;
+	GList *iterator;
+
+	g_assert (EEL_IS_WRAP_TABLE (widget));
+	
+	wrap_table = EEL_WRAP_TABLE (widget);
+
+	GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
+
+	for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) {
+		GtkWidget *item;
+
+		item = iterator->data;
+
+		if (GTK_WIDGET_VISIBLE (item) && GTK_WIDGET_MAPPED (item)) {
+			gtk_widget_unmap (item);
+		}
+	}
+}
+
+static void
+eel_wrap_table_realize (GtkWidget *widget)
+{
+	g_assert (EEL_IS_WRAP_TABLE (widget));
+
+	GTK_WIDGET_CLASS (parent_class)->realize (widget);
+
+	gtk_widget_queue_resize (widget);
+}
+
+/* GtkContainerClass methods */
+static void
+eel_wrap_table_add (GtkContainer *container,
+			GtkWidget *child)
+{
+	EelWrapTable *wrap_table;
+
+	g_assert (container != NULL);
+	g_assert (EEL_IS_WRAP_TABLE (container));
+	g_assert (GTK_IS_WIDGET (child));
+
+	wrap_table = EEL_WRAP_TABLE (container);
+
+	gtk_widget_set_parent (child, GTK_WIDGET (container));
+
+	wrap_table->details->children = g_list_append (wrap_table->details->children, child);
+
+	if (GTK_WIDGET_REALIZED (container)) {
+		gtk_widget_realize (child);
+	}
+
+	if (GTK_WIDGET_VISIBLE (container) && GTK_WIDGET_VISIBLE (child)) {
+		if (GTK_WIDGET_MAPPED (container)) {
+			gtk_widget_map (child);
+		}
+		
+		gtk_widget_queue_resize (child);
+	}
+
+	if (wrap_table->details->is_scrolled) {
+		g_signal_connect (child, "focus_in_event", 
+				  G_CALLBACK (wrap_table_child_focus_in), 
+				  wrap_table);
+	}
+}
+
+static void
+eel_wrap_table_remove (GtkContainer *container,
+			    GtkWidget *child)
+{
+	EelWrapTable *wrap_table;
+	gboolean child_was_visible;
+	
+	g_assert (EEL_IS_WRAP_TABLE (container));
+	g_assert (GTK_IS_WIDGET (child));
+	
+	wrap_table = EEL_WRAP_TABLE (container);;
+
+	child_was_visible = GTK_WIDGET_VISIBLE (child);
+	gtk_widget_unparent (child);
+	wrap_table->details->children = g_list_remove (wrap_table->details->children, child);
+
+	if (child_was_visible) {
+		gtk_widget_queue_resize (GTK_WIDGET (container));
+	}
+
+	if (wrap_table->details->is_scrolled) {
+		g_signal_handlers_disconnect_by_func (
+			child,
+			G_CALLBACK (wrap_table_child_focus_in), 
+			wrap_table);
+	}
+}
+
+static void
+eel_wrap_table_forall (GtkContainer *container,
+			    gboolean include_internals,
+			    GtkCallback callback,
+			    gpointer callback_data)
+{
+	EelWrapTable *wrap_table;
+	GList *node;
+	GList *next;
+	
+	g_assert (EEL_IS_WRAP_TABLE (container));
+	g_assert (callback != NULL);
+	
+	wrap_table = EEL_WRAP_TABLE (container);;
+	
+	for (node = wrap_table->details->children; node != NULL; node = next) {
+		g_assert (GTK_IS_WIDGET (node->data));
+		next = node->next;
+		(* callback) (GTK_WIDGET (node->data), callback_data);	
+	}
+}
+
+static GtkType
+eel_wrap_table_child_type (GtkContainer   *container)
+{
+	return GTK_TYPE_WIDGET;
+}
+
+/* Private EelWrapTable methods */
+static int
+wrap_table_get_num_fitting (int available,
+			    int spacing,
+			    int max_child_size)
+{
+	int num;
+
+  	g_assert (max_child_size > 0);
+  	g_assert (spacing >= 0);
+
+	available = MAX (available, 0);
+	
+	num = (available + spacing) / (max_child_size + spacing);
+	num = MAX (num, 1);
+
+	return num;
+}
+
+static void
+wrap_table_layout (EelWrapTable *wrap_table)
+{
+	GList *iterator;
+	EelIPoint pos;
+	EelDimensions max_child_dimensions;
+	EelIRect content_bounds;
+	guint num_cols;
+
+ 	g_assert (EEL_IS_WRAP_TABLE (wrap_table));
+
+	max_child_dimensions = wrap_table_get_max_child_dimensions (wrap_table);
+	content_bounds = wrap_table_get_content_bounds (wrap_table);
+	pos.x = content_bounds.x0;
+	pos.y = content_bounds.y0;
+
+	num_cols = wrap_table_get_num_fitting (GTK_WIDGET (wrap_table)->allocation.width -
+					       GTK_CONTAINER (wrap_table)->border_width * 2,
+					       wrap_table->details->x_spacing,
+					       max_child_dimensions.width);
+	if (num_cols != wrap_table->details->cols) {
+		wrap_table->details->cols = num_cols;
+		gtk_widget_queue_resize (GTK_WIDGET (wrap_table));
+		return;
+	}
+
+	for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) {
+		GtkWidget *item;
+
+		item = iterator->data;
+		
+		if (GTK_WIDGET_VISIBLE (item)) {
+			GtkAllocation item_allocation;
+
+			if (wrap_table->details->homogeneous) {
+				item_allocation.x = pos.x;
+				item_allocation.y = pos.y;
+				item_allocation.width = max_child_dimensions.width;
+				item_allocation.height = max_child_dimensions.height;
+				
+				if ((pos.x + max_child_dimensions.width) > content_bounds.x1) {
+					pos.x = content_bounds.x0 + wrap_table->details->x_spacing + max_child_dimensions.width;
+					pos.y += (max_child_dimensions.height + wrap_table->details->y_spacing);
+					item_allocation.x = content_bounds.x0;
+					item_allocation.y = pos.y;
+				} else {
+					pos.x += (wrap_table->details->x_spacing + max_child_dimensions.width);
+				}
+			} else {
+				GtkRequisition item_requisition;
+				
+				gtk_widget_size_request (item, &item_requisition);
+				
+				item_allocation.x = pos.x;
+				item_allocation.y = pos.y;
+				item_allocation.width = item_requisition.width;
+				item_allocation.height = item_requisition.height;
+				
+				g_assert (item_allocation.width <= max_child_dimensions.width);
+				g_assert (item_allocation.height <= max_child_dimensions.height);
+				
+				if ((pos.x + max_child_dimensions.width) > content_bounds.x1) {
+					pos.x = content_bounds.x0 + wrap_table->details->x_spacing + max_child_dimensions.width;
+					pos.y += (max_child_dimensions.height + wrap_table->details->y_spacing);
+					item_allocation.x = content_bounds.x0;
+					item_allocation.y = pos.y;
+				} else {
+					pos.x += (wrap_table->details->x_spacing + max_child_dimensions.width);
+				}
+				
+				switch (wrap_table->details->x_justification) {
+				case EEL_JUSTIFICATION_MIDDLE:
+					item_allocation.x += (max_child_dimensions.width - (int) item_allocation.width) / 2;
+					break;
+				case EEL_JUSTIFICATION_END:
+					item_allocation.x += (max_child_dimensions.width - (int) item_allocation.width);
+					break;
+				default:
+					break;
+				}
+				
+				switch (wrap_table->details->y_justification) {
+				case EEL_JUSTIFICATION_MIDDLE:
+					item_allocation.y += (max_child_dimensions.height - (int) item_allocation.height) / 2;
+					break;
+				case EEL_JUSTIFICATION_END:
+					item_allocation.y += (max_child_dimensions.height - (int) item_allocation.height);
+					break;
+				default:
+					break;
+				}
+			}
+			
+			gtk_widget_size_allocate (item, &item_allocation);
+		}
+	}
+}
+
+static EelDimensions
+wrap_table_irect_max_dimensions (const EelDimensions *one,
+				     const EelDimensions *two)
+{
+	EelDimensions max_dimensions;
+
+	g_assert (one != NULL);
+	g_assert (two != NULL);
+
+	max_dimensions.width = MAX (one->width, two->width);
+	max_dimensions.height = MAX (one->height, two->height);
+
+	return max_dimensions;
+}
+
+static EelDimensions
+wrap_table_get_max_child_dimensions (const EelWrapTable *wrap_table)
+{
+	EelDimensions max_dimensions;
+	GList *iterator;
+
+  	g_assert (EEL_IS_WRAP_TABLE (wrap_table));
+
+	max_dimensions = eel_dimensions_empty;
+
+	for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) {
+		GtkWidget *child;
+		
+		child = iterator->data;
+		
+		if (GTK_WIDGET_VISIBLE (child)) {
+ 			GtkRequisition child_requisition;
+			EelDimensions child_dimensions;
+
+ 			gtk_widget_size_request (child, &child_requisition);
+
+			child_dimensions.width = (int) child_requisition.width;
+			child_dimensions.height = (int) child_requisition.height;
+
+			max_dimensions = wrap_table_irect_max_dimensions (&child_dimensions, &max_dimensions);
+		}
+	}
+
+	return max_dimensions;
+}
+
+static EelDimensions
+wrap_table_get_content_dimensions (const EelWrapTable *wrap_table)
+{
+	EelDimensions content_dimensions;
+	guint num_children;
+
+  	g_assert (EEL_IS_WRAP_TABLE (wrap_table));
+
+	content_dimensions = eel_dimensions_empty;
+	
+	num_children = g_list_length (wrap_table->details->children);
+
+	if (num_children > 0) {
+		EelDimensions max_child_dimensions;
+		EelDimensions dimensions;
+		int num_cols;
+		int num_rows;
+
+		dimensions = eel_gtk_widget_get_dimensions (GTK_WIDGET (wrap_table));
+		max_child_dimensions = wrap_table_get_max_child_dimensions (wrap_table);
+
+		max_child_dimensions.width = MAX (max_child_dimensions.width, 1);
+		max_child_dimensions.height = MAX (max_child_dimensions.height, 1);
+
+		num_cols = wrap_table_get_num_fitting (dimensions.width -
+						       GTK_CONTAINER (wrap_table)->border_width * 2,
+						       wrap_table->details->x_spacing,
+						       max_child_dimensions.width);
+		num_rows = num_children / num_cols;
+		num_rows = MAX (num_rows, 1);
+		
+		if ((num_children % num_rows) > 0) {
+			num_rows++;
+		}
+		
+		content_dimensions.width = dimensions.width;
+		content_dimensions.height = num_rows * max_child_dimensions.height;
+
+		content_dimensions.width += (num_cols - 1) * wrap_table->details->x_spacing;
+		content_dimensions.height += (num_rows - 1) * wrap_table->details->y_spacing;
+	}
+
+	return content_dimensions;
+}
+
+static EelIRect
+wrap_table_get_content_bounds (const EelWrapTable *wrap_table)
+{
+	EelIRect content_bounds;
+
+  	g_assert (EEL_IS_WRAP_TABLE (wrap_table));
+
+	content_bounds = eel_gtk_widget_get_bounds (GTK_WIDGET (wrap_table));
+
+	content_bounds.x0 += GTK_CONTAINER (wrap_table)->border_width;
+	content_bounds.y0 += GTK_CONTAINER (wrap_table)->border_width;
+	content_bounds.x1 -= GTK_CONTAINER (wrap_table)->border_width;
+	content_bounds.y1 -= GTK_CONTAINER (wrap_table)->border_width;
+
+	return content_bounds;
+}
+
+static gboolean
+wrap_table_child_focus_in (GtkWidget *widget, 
+			   GdkEventFocus *event, 
+			   gpointer data)
+{
+	g_assert (widget->parent && widget->parent->parent);
+	g_assert (GTK_IS_VIEWPORT (widget->parent->parent));
+
+	eel_gtk_viewport_scroll_to_rect (GTK_VIEWPORT (widget->parent->parent), 
+					 &widget->allocation);
+	
+	return FALSE;
+}
+
+/**
+ * eel_wrap_table_new:
+ *
+ */
+GtkWidget*
+eel_wrap_table_new (gboolean homogeneous)
+{
+	EelWrapTable *wrap_table;
+
+	wrap_table = EEL_WRAP_TABLE (gtk_widget_new (eel_wrap_table_get_type (), NULL));
+
+	eel_wrap_table_set_homogeneous (wrap_table, homogeneous);
+	
+	return GTK_WIDGET (wrap_table);
+}
+
+/**
+ * eel_wrap_table_set_x_spacing:
+ * @wrap_table: A EelWrapTable.
+ * @x_spacing: The new horizontal spacing between wraps.
+ *
+ */
+void
+eel_wrap_table_set_x_spacing (EelWrapTable *wrap_table,
+				   guint x_spacing)
+{
+	g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table));
+	
+	if (wrap_table->details->x_spacing == x_spacing) {
+		return;
+	}
+	
+	wrap_table->details->x_spacing = x_spacing;
+
+	gtk_widget_queue_resize (GTK_WIDGET (wrap_table));
+}
+
+/**
+ * eel_wrap_table_get_item_spacing:
+ * @wrap_table: A EelWrapTable.
+ *
+ * Returns: The horizontal spacing between wraps.
+ */
+guint
+eel_wrap_table_get_x_spacing (const EelWrapTable *wrap_table)
+{
+	g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0);
+	
+	return wrap_table->details->x_spacing;
+}
+
+/**
+ * eel_wrap_table_set_y_spacing:
+ * @wrap_table: A EelWrapTable.
+ * @y_spacing: The new horizontal spacing between wraps.
+ *
+ */
+void
+eel_wrap_table_set_y_spacing (EelWrapTable *wrap_table,
+				   guint y_spacing)
+{
+	g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table));
+	
+	if (wrap_table->details->y_spacing == y_spacing) {
+		return;
+	}
+	
+	wrap_table->details->y_spacing = y_spacing;
+
+	gtk_widget_queue_resize (GTK_WIDGET (wrap_table));
+}
+
+/**
+ * eel_wrap_table_get_item_spacing:
+ * @wrap_table: A EelWrapTable.
+ *
+ * Returns: The horizontal spacing between wraps.
+ */
+guint
+eel_wrap_table_get_y_spacing (const EelWrapTable *wrap_table)
+{
+	g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0);
+	
+	return wrap_table->details->y_spacing;
+}
+
+
+/**
+ * eel_wrap_table_find_child_at_event_point:
+ * @wrap_table: A EelWrapTable.
+ * @x: Event x;
+ * @y: Event y;
+ *
+ * Returns: Child found at given coordinates or NULL of no child is found.
+ */
+GtkWidget *
+eel_wrap_table_find_child_at_event_point (const EelWrapTable *wrap_table,
+					  int x,
+					  int y)
+{
+	GList *iterator;
+	
+  	g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), NULL);
+
+	for (iterator = wrap_table->details->children; iterator; iterator = iterator->next) {
+		GtkWidget *child;
+		
+		child = iterator->data;
+		
+		if (GTK_WIDGET_VISIBLE (child)) {
+			EelIRect child_bounds;
+
+			child_bounds = eel_gtk_widget_get_bounds (child);
+ 			
+			if (eel_irect_contains_point (child_bounds, x, y)) {
+				return child;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * eel_wrap_table_set_x_justification:
+ * @wrap_table: A EelWrapTable.
+ * @x_justification: The new horizontal justification between wraps.
+ *
+ */
+void
+eel_wrap_table_set_x_justification (EelWrapTable *wrap_table,
+					 EelJustification x_justification)
+{
+	g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table));
+	g_return_if_fail (x_justification >= EEL_JUSTIFICATION_BEGINNING);
+	g_return_if_fail (x_justification <= EEL_JUSTIFICATION_END);
+	
+	if (wrap_table->details->x_justification == x_justification) {
+		return;
+	}
+	
+	wrap_table->details->x_justification = x_justification;
+	gtk_widget_queue_resize (GTK_WIDGET (wrap_table));
+}
+
+/**
+ * eel_wrap_table_get_item_justification:
+ * @wrap_table: A EelWrapTable.
+ *
+ * Returns: The horizontal justification between wraps.
+ */
+EelJustification 
+eel_wrap_table_get_x_justification (const EelWrapTable *wrap_table)
+{
+	g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0);
+	
+	return wrap_table->details->x_justification;
+}
+
+/**
+ * eel_wrap_table_set_y_justification:
+ * @wrap_table: A EelWrapTable.
+ * @y_justification: The new horizontal justification between wraps.
+ *
+ */
+void
+eel_wrap_table_set_y_justification (EelWrapTable *wrap_table,
+					 EelJustification y_justification)
+{
+	g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table));
+	g_return_if_fail (y_justification >= EEL_JUSTIFICATION_BEGINNING);
+	g_return_if_fail (y_justification <= EEL_JUSTIFICATION_END);
+	
+	if (wrap_table->details->y_justification == y_justification) {
+		return;
+	}
+	
+	wrap_table->details->y_justification = y_justification;
+	gtk_widget_queue_resize (GTK_WIDGET (wrap_table));
+}
+
+/**
+ * eel_wrap_table_get_item_justification:
+ * @wrap_table: A EelWrapTable.
+ *
+ * Returns: The horizontal justification between wraps.
+ */
+EelJustification 
+eel_wrap_table_get_y_justification (const EelWrapTable *wrap_table)
+{
+	g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0);
+	
+	return wrap_table->details->y_justification;
+}
+
+/**
+ * eel_wrap_table_set_homogeneous:
+ * @wrap_table: A EelWrapTable.
+ * @homogeneous: The new horizontal spacing between wraps.
+ *
+ */
+void
+eel_wrap_table_set_homogeneous (EelWrapTable *wrap_table,
+				     gboolean homogeneous)
+{
+	g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table));
+	
+	if (wrap_table->details->homogeneous == homogeneous) {
+		return;
+	}
+	
+	wrap_table->details->homogeneous = homogeneous;
+
+	gtk_widget_queue_resize (GTK_WIDGET (wrap_table));
+}
+
+/**
+ * eel_wrap_table_get_item_spacing:
+ * @wrap_table: A EelWrapTable.
+ *
+ * Returns: The horizontal spacing between wraps.
+ */
+gboolean
+eel_wrap_table_get_homogeneous (const EelWrapTable *wrap_table)
+{
+	g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), FALSE);
+	
+	return wrap_table->details->homogeneous;
+}
+
+/**
+ * eel_wrap_table_reorder_child:
+ * @wrap_table: A EelWrapTable.
+ * @child: Child to reorder.
+ * @position: New position to put child at.
+ *
+ * Reorder the given chilren into the given position.
+ *
+ * Position is interpreted as follows:
+ *
+ *  0 - Place child at start of table.
+ * -1 - Place child at end of table.
+ *  n - Place child at nth position.  Count starts at 0.
+ */
+void
+eel_wrap_table_reorder_child (EelWrapTable *wrap_table,
+				   GtkWidget *child,
+				   int position)
+{
+	GList *node;
+	gboolean found_child = FALSE;
+
+	g_return_if_fail (EEL_IS_WRAP_TABLE (wrap_table));
+	g_return_if_fail (g_list_length (wrap_table->details->children) > 0);
+
+	if (position == -1) {
+		position = g_list_length (wrap_table->details->children) - 1;
+	}
+
+	g_return_if_fail (position >= 0);
+	g_return_if_fail ((guint) position < g_list_length (wrap_table->details->children));
+
+	for (node = wrap_table->details->children; node != NULL; node = node->next) {
+		GtkWidget *next_child;
+		next_child = node->data;
+		
+		if (next_child == child) {
+			g_assert (found_child == FALSE);
+			found_child = TRUE;
+		}
+	}
+
+	g_return_if_fail (found_child);
+
+	wrap_table->details->children = g_list_remove (wrap_table->details->children, child);
+	wrap_table->details->children = g_list_insert (wrap_table->details->children, child, position);
+
+	gtk_widget_queue_resize (GTK_WIDGET (wrap_table));
+}
+
+/**
+ * eel_wrap_table_get_num_children:
+ * @wrap_table: A EelWrapTable.
+ *
+ * Returns: The number of children being managed by the wrap table.
+ */
+guint
+eel_wrap_table_get_num_children (const EelWrapTable *wrap_table)
+{
+	g_return_val_if_fail (EEL_IS_WRAP_TABLE (wrap_table), 0);
+	
+	return g_list_length (wrap_table->details->children);
+}
+
+GtkWidget *
+eel_scrolled_wrap_table_new (gboolean homogenous,
+			     GtkWidget **wrap_table_out)
+{
+	GtkWidget *scrolled_window;
+	GtkWidget *wrap_table;
+	GtkWidget *viewport;
+	
+	g_return_val_if_fail (wrap_table_out != NULL, NULL);
+	
+	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+					GTK_POLICY_NEVER,
+					GTK_POLICY_AUTOMATIC);
+
+	viewport = gtk_viewport_new (gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (scrolled_window)),
+				     gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_window)));
+	gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport),
+				      GTK_SHADOW_NONE);
+	
+	gtk_container_add (GTK_CONTAINER (scrolled_window),
+			   viewport);
+	
+	wrap_table = eel_wrap_table_new (homogenous);
+	gtk_container_add (GTK_CONTAINER (viewport),
+			   wrap_table);
+
+	gtk_widget_show (wrap_table);
+	gtk_widget_show (viewport);
+
+	EEL_WRAP_TABLE (wrap_table)->details->is_scrolled = 1;
+
+	*wrap_table_out = wrap_table;
+	return scrolled_window;
+}

Added: trunk/eel/eel-wrap-table.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-wrap-table.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,97 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-wrap-table.h - A table that can wrap its contents as needed.
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Ramiro Estrugo <ramiro eazel com>
+*/
+
+#ifndef EEL_WRAP_TABLE_H
+#define EEL_WRAP_TABLE_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EEL_TYPE_WRAP_TABLE            (eel_wrap_table_get_type ())
+#define EEL_WRAP_TABLE(obj)            (GTK_CHECK_CAST ((obj), EEL_TYPE_WRAP_TABLE, EelWrapTable))
+#define EEL_WRAP_TABLE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), EEL_TYPE_WRAP_TABLE, EelWrapTableClass))
+#define EEL_IS_WRAP_TABLE(obj)         (GTK_CHECK_TYPE ((obj), EEL_TYPE_WRAP_TABLE))
+#define EEL_IS_WRAP_TABLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), EEL_TYPE_WRAP_TABLE))
+
+typedef struct EelWrapTable	       EelWrapTable;
+typedef struct EelWrapTableClass       EelWrapTableClass;
+typedef struct EelWrapTableDetails     EelWrapTableDetails;
+
+struct EelWrapTable
+{
+	/* Superclass */
+	GtkContainer container;
+
+	/* Private things */
+	EelWrapTableDetails *details;
+};
+
+struct EelWrapTableClass
+{
+	GtkContainerClass parent_class;
+};
+
+typedef enum
+{
+	EEL_JUSTIFICATION_BEGINNING,
+	EEL_JUSTIFICATION_MIDDLE,
+	EEL_JUSTIFICATION_END
+} EelJustification;
+
+/* Public GtkWrapTable methods */
+GtkType          eel_wrap_table_get_type                  (void);
+GtkWidget *      eel_wrap_table_new                       (gboolean            homogeneous);
+void             eel_wrap_table_set_x_spacing             (EelWrapTable       *wrap_table,
+							   guint               x_spacing);
+guint            eel_wrap_table_get_x_spacing             (const EelWrapTable *wrap_table);
+void             eel_wrap_table_set_y_spacing             (EelWrapTable       *wrap_table,
+							   guint               y_spacing);
+guint            eel_wrap_table_get_y_spacing             (const EelWrapTable *wrap_table);
+GtkWidget *      eel_wrap_table_find_child_at_event_point (const EelWrapTable *wrap_table,
+							   int                 x,
+							   int                 y);
+void             eel_wrap_table_set_x_justification       (EelWrapTable       *wrap_table,
+							   EelJustification    justification);
+EelJustification eel_wrap_table_get_x_justification       (const EelWrapTable *wrap_table);
+void             eel_wrap_table_set_y_justification       (EelWrapTable       *wrap_table,
+							   EelJustification    justification);
+EelJustification eel_wrap_table_get_y_justification       (const EelWrapTable *wrap_table);
+void             eel_wrap_table_set_homogeneous           (EelWrapTable       *wrap_table,
+							   gboolean            homogeneous);
+gboolean         eel_wrap_table_get_homogeneous           (const EelWrapTable *wrap_table);
+void             eel_wrap_table_reorder_child             (EelWrapTable       *wrap_table,
+							   GtkWidget          *child,
+							   int                 position);
+guint            eel_wrap_table_get_num_children          (const EelWrapTable *wrap_table);
+
+GtkWidget *      eel_scrolled_wrap_table_new              (gboolean            homogenous, 
+							   GtkWidget         **wrap_table_out);
+
+G_END_DECLS
+
+#endif /* EEL_WRAP_TABLE_H */
+
+

Added: trunk/eel/eel-xml-extensions.c
==============================================================================
--- (empty file)
+++ trunk/eel/eel-xml-extensions.c	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,184 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-xml-extensions.c - functions that extend gnome-xml
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#include <config.h>
+#include "eel-xml-extensions.h"
+
+#include "eel-string.h"
+#include "eel-i18n.h"
+#include <glib.h>
+#include <libxml/parser.h>
+#include <stdlib.h>
+
+xmlNodePtr
+eel_xml_get_children (xmlNodePtr parent)
+{
+	if (parent == NULL) {
+		return NULL;
+	}
+	return parent->children;
+}
+
+xmlNodePtr
+eel_xml_get_root_children (xmlDocPtr document)
+{
+	return eel_xml_get_children (xmlDocGetRootElement (document));
+}
+
+xmlNodePtr
+eel_xml_get_child_by_name_and_property (xmlNodePtr parent,
+					     const char *child_name,
+					     const char *property_name,
+					     const char *property_value)
+{
+	xmlNodePtr child;
+	xmlChar *property;
+	gboolean match;
+
+	if (parent == NULL) {
+		return NULL;
+	}
+	for (child = eel_xml_get_children (parent); child != NULL; child = child->next) {
+		if (strcmp (child->name, child_name) == 0) {
+			property = xmlGetProp (child, property_name);
+			match = eel_strcmp (property, property_value) == 0;
+			xmlFree (property);
+			if (match) {
+				return child;
+			}
+		}
+	}
+	return NULL;
+}
+
+/* return a child of the passed-in node with a matching name */
+
+xmlNodePtr
+eel_xml_get_child_by_name (xmlNodePtr parent,
+					     const char *child_name)
+{
+	xmlNodePtr child;
+
+	if (parent == NULL) {
+		return NULL;
+	}
+	for (child = eel_xml_get_children (parent); child != NULL; child = child->next) {
+		if (strcmp (child->name, child_name) == 0) {
+			return child;
+		}
+	}
+	return NULL;
+}
+
+
+xmlNodePtr
+eel_xml_get_root_child_by_name_and_property (xmlDocPtr document,
+						  const char *child_name,
+						  const char *property_name,
+						  const char *property_value)
+{
+	return eel_xml_get_child_by_name_and_property
+		(xmlDocGetRootElement (document),
+		 child_name,
+		 property_name,
+		 property_value);
+}
+
+/**
+ * eel_xml_get_property_for_children
+ * 
+ * Returns a list of the values for the specified property for all
+ * children of the node that have the specified name.
+ *
+ * @parent:     xmlNodePtr representing the node in question.
+ * @child_name: child element name to look for
+ * @property:   name of propety to reutnr for matching children that have the property
+ * 
+ * Returns: A list of keywords.
+ * 
+ **/
+GList *
+eel_xml_get_property_for_children (xmlNodePtr parent,
+					const char *child_name,
+					const char *property_name)
+{
+	GList *properties;
+	xmlNode *child;
+	xmlChar *property;
+	
+	properties = NULL;
+	
+	for (child = eel_xml_get_children (parent);
+	     child != NULL;
+	     child = child->next) {
+		if (strcmp (child->name, child_name) == 0) {
+			property = xmlGetProp (child, property_name);
+			if (property != NULL) {
+				properties = g_list_prepend (properties,
+							     g_strdup (property));
+				xmlFree (property);
+			}
+		}
+	}
+
+	/* Reverse so you get them in the same order as the XML file. */
+	return g_list_reverse (properties);
+}
+
+xmlChar *
+eel_xml_get_property_translated (xmlNodePtr parent,
+				      const char *property_name)
+{
+	xmlChar *property, *untranslated_property;
+	char *untranslated_property_name;
+	const char *translated_property;
+
+	/* Try for the already-translated version. */
+	property = xmlGetProp (parent, property_name);
+	if (property != NULL) {
+		return property;
+	}
+
+	/* Try for the untranslated version. */
+	untranslated_property_name = g_strconcat ("_", property_name, NULL);
+	untranslated_property = xmlGetProp (parent, untranslated_property_name);
+	g_free (untranslated_property_name);
+	if (untranslated_property == NULL) {
+		return NULL;
+	}
+
+	/* Try to translate. */
+	translated_property = gettext (untranslated_property);
+
+	/* If not translation is found, return untranslated property as-is. */
+	if (translated_property == (char *) untranslated_property) {
+		return untranslated_property;
+	}
+	
+	/* If a translation happened, make a copy to match the normal
+	 * behavior of this function (returning a string you xmlFree).
+	 */
+	xmlFree (untranslated_property);
+	return xmlStrdup (translated_property);
+}

Added: trunk/eel/eel-xml-extensions.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel-xml-extensions.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,49 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel-xml-extensions.h - functions that extend gnome-xml
+
+   Copyright (C) 2000 Eazel, Inc.
+
+   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.
+
+   Authors: Darin Adler <darin eazel com>
+*/
+
+#ifndef EEL_XML_EXTENSIONS_H
+#define EEL_XML_EXTENSIONS_H
+
+#include <libxml/tree.h>
+#include <glib.h>
+
+xmlNodePtr eel_xml_get_children                        (xmlNodePtr  parent);
+xmlNodePtr eel_xml_get_root_children                   (xmlDocPtr   document);
+xmlNodePtr eel_xml_get_child_by_name                   (xmlNodePtr  parent,
+							const char *child_name);
+xmlNodePtr eel_xml_get_child_by_name_and_property      (xmlNodePtr  parent,
+							const char *child_name,
+							const char *property_name,
+							const char *property_value);
+xmlNodePtr eel_xml_get_root_child_by_name_and_property (xmlDocPtr   document,
+							const char *child_name,
+							const char *property_name,
+							const char *property_value);
+GList     *eel_xml_get_property_for_children           (xmlNodePtr  parent,
+							const char *child_name,
+							const char *property_name);
+xmlChar   *eel_xml_get_property_translated             (xmlNodePtr  parent,
+							const char *property_name);
+
+#endif /* EEL_XML_EXTENSIONS_H */

Added: trunk/eel/eel.h
==============================================================================
--- (empty file)
+++ trunk/eel/eel.h	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,53 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* eel.h
+
+   Copyright (C) 2001 Eazel, Inc.
+
+   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.
+
+   Authors: Maciej Stachowiak <mjs eazel com>
+*/
+
+#ifndef EEL_H
+#define EEL_H
+
+#include <eel/eel-art-extensions.h>
+#include <eel/eel-art-gtk-extensions.h>
+#include <eel/eel-background.h>
+#include <eel/eel-enumeration.h>
+#include <eel/eel-gconf-extensions.h>
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-gnome-extensions.h>
+#include <eel/eel-graphic-effects.h>
+#include <eel/eel-gtk-container.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-image-table.h>
+#include <eel/eel-labeled-image.h>
+#include <eel/eel-pango-extensions.h>
+#include <eel/eel-preferences.h>
+#include <eel/eel-self-checks.h>
+#include <eel/eel-stock-dialogs.h>
+#include <eel/eel-string.h>
+#include <eel/eel-types.h>
+#include <eel/eel-vfs-extensions.h>
+#include <eel/eel-wrap-table.h>
+#include <eel/eel-xml-extensions.h>
+
+#endif /* EEL_H */

Added: trunk/eel/eelmarshal.list
==============================================================================
--- (empty file)
+++ trunk/eel/eelmarshal.list	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,13 @@
+BOOLEAN:BOOLEAN
+BOOLEAN:BOXED
+BOOLEAN:POINTER,POINTER
+BOOLEAN:VOID
+ENUM:INT,INT
+INT:INT
+INT:POINTER,STRING
+STRING:POINTER
+STRING:VOID
+VOID:ENUM,INT
+VOID:ENUM,INT,BOOLEAN
+VOID:INT,INT,INT,INT
+VOID:OBJECT,POINTER

Added: trunk/eel/makeenums.pl
==============================================================================
--- (empty file)
+++ trunk/eel/makeenums.pl	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,220 @@
+#!/usr/bin/perl -w
+
+# This script snarfs the enums from header files and writes them out into
+# a .defs file (gnome.defs, for example). From there, the sister script
+# maketypes.awk converts the defs into a *typebuiltins.h, as well as
+# *typebuiltins_vals.c, *typebuiltins_ids.c and *typebuiltins_evals.c.
+
+# Information about the current enumeration
+
+my $flags;			# Is enumeration a bitmask
+my $seenbitshift;			# Have we seen bitshift operators?
+my $prefix;			# Prefix for this enumeration
+my $enumname;			# Name for this enumeration
+my $firstenum = 1;		# Is this the first enumeration in file?
+my @entries;			# [ $name, $val ] for each entry
+
+sub parse_options {
+    my $opts = shift;
+    my @opts;
+
+    for $opt (split /\s*,\s*/, $opts) {
+	my ($key,$val) = $opt =~ /\s*(\w+)(?:=(\S+))?/;
+	defined $val or $val = 1;
+	push @opts, $key, $val;
+    }
+    @opts;
+}
+sub parse_entries {
+    my $file = shift;
+
+    while (<$file>) {
+	# Read lines until we have no open comments
+	while (m@/\*
+	       ([^*]|\*(?!/))*$
+	       @x) {
+	    my $new;
+	    defined ($new = <$file>) || die "Unmatched comment";
+	    $_ .= $new;
+	}
+	# Now strip comments
+	s@/\*(?!<)
+	    ([^*]+|\*(?!/))*
+	   \*/@@gx;
+	
+	s \n@ @;
+	
+	next if m ^\s*$@;
+
+	# Handle include files
+	if (/^\#include\s*<([^>]*)>/ ) {
+            my $file= "../$1";
+	    open NEWFILE, $file or die "Cannot open include file $file: $!\n";
+	    
+	    if (parse_entries (\*NEWFILE)) {
+		return 1;
+	    } else {
+		next;
+	    }
+	}
+	
+	if (/^\s*\}\s*(\w+)/) {
+	    $enumname = $1;
+	    return 1;
+	}
+
+	if (m ^\s*
+              (\w+)\s*		         # name
+              (?:=(                      # value
+                   (?:[^,/]|/(?!\*))*
+                  ))?,?\s*
+              (?:/\*<		         # options 
+                (([^*]|\*(?!/))*)
+               >\*/)?
+              \s*$
+             @x) {
+	    my ($name, $value, $options) = ($1,$2,$3);
+
+	    if (!defined $flags && defined $value && $value =~ /<</) {
+		$seenbitshift = 1;
+	    }
+	    if (defined $options) {
+		my %options = parse_options($options);
+		if (!defined $options{skip}) {
+		    push @entries, [ $name, $options{nick} ];
+		}
+	    } else {
+		push @entries, [ $name ];
+	    }
+	} else {
+	    print STDERR "Can't understand: $_\n";
+	}
+    }
+    return 0;
+}
+
+
+my $gen_arrays = 0;
+my $gen_defs = 0;
+
+# Parse arguments
+
+if (@ARGV) {
+    if ($ARGV[0] eq "arrays") {
+	shift @ARGV;
+	$gen_arrays = 1;
+    } elsif ($ARGV[0] eq "defs") {
+	shift @ARGV;
+	$gen_defs = 1;
+    } else {
+	$gen_defs = 1;
+    }
+    
+}
+
+if ($gen_defs) {
+    print ";; generated by makeenums.pl  ; -*- scheme -*-\n\n";
+} else {
+    print "/* Generated by makeenums.pl */\n\n";
+}
+
+ENUMERATION:
+while (<>) {
+    if (eof) {
+	close (ARGV);		# reset line numbering
+	$firstenum = 1;		# Flag to print filename at next enum
+    }
+
+    if (m ^\s*typedef\s+enum\s*
+           ({)?\s*
+           (?:/\*<
+             (([^*]|\*(?!/))*)
+            >\*/)?
+         @x) {
+	if (defined $2) {
+	    my %options = parse_options($2);
+	    $prefix = $options{prefix};
+	    $flags = $options{flags};
+	} else {
+	    $prefix = undef;
+	    $flags = undef;
+	}
+	# Didn't have trailing '{' look on next lines
+	if (!defined $1) {
+	    while (<>) {
+		if (s/^\s*\{//) {
+		    last;
+		}
+	    }
+	}
+
+	$seenbitshift = 0;
+	@entries = ();
+
+	# Now parse the entries
+	parse_entries (\*ARGV);
+
+	# figure out if this was a flags or enums enumeration
+
+	if (!defined $flags) {
+	    $flags = $seenbitshift;
+	}
+
+	# Autogenerate a prefix
+
+	if (!defined $prefix) {
+	    for (@entries) {
+		my $name = $_->[0];
+		if (defined $prefix) {
+		    my $tmp = ~ ($name ^ $prefix);
+		    ($tmp) = $tmp =~ /(^\xff*)/;
+		    $prefix = $prefix & $tmp;
+		} else {
+		    $prefix = $name;
+		}
+	    }
+	    # Trim so that it ends in an underscore
+	    $prefix =~ s/_[^_]*$/_/;
+	}
+	
+	for $entry (@entries) {
+	    my ($name,$nick) = @{$entry};
+            if (!defined $nick) {
+ 	        ($nick = $name) =~ s/^$prefix//;
+	        $nick =~ tr/_/-/;
+	        $nick = lc($nick);
+	        @{$entry} = ($name, $nick);
+            }
+	}
+
+	# Spit out the output
+
+	if ($gen_defs) {
+	    if ($firstenum) {
+		print qq(\n; enumerations from "$ARGV"\n);
+		$firstenum = 0;
+	    }
+	    
+	    print "\n(define-".($flags ? "flags" : "enum")." $enumname";
+
+	    for (@entries) {
+		my ($name,$nick) = @{$_};
+		print "\n   ($nick $name)";
+	    }
+	    print ")\n";
+
+	} else {
+	    ($valuename = $enumname) =~ s/([A-Z][a-z])/_$1/g;
+	    $valuename =~ s/([a-z])([A-Z])/$1_$2/g;
+	    $valuename = lc($valuename);
+
+	    print "static const GEnumValue $ {valuename}_values[] = {\n";
+	    for (@entries) {
+		my ($name,$nick) = @{$_};
+		print qq(  { $name, "$name", "$nick" },\n);
+	    }
+	    print "  { 0, NULL, NULL }\n";
+	    print "};\n";
+	}
+    }
+}

Added: trunk/eel/maketypes.awk
==============================================================================
--- (empty file)
+++ trunk/eel/maketypes.awk	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,155 @@
+
+BEGIN {
+  type_name = "";	# GtkEnumType
+  type_macro = "";	# GTK_TYPE_ENUM_TYPE
+  type_ident = "";	# _gtk_enum_type
+  type_counter = 0;
+  gen_macros = 0;
+  gen_entries = 0;
+  gen_vars = 0;
+  boxed_copy = "";
+  boxed_free = "";
+  
+  for (i = 2; i < ARGC; i++)
+    {
+      if (ARGV[i] == "macros")
+	gen_macros = 1;
+      else if (ARGV[i] == "entries")
+	gen_entries = 1;
+      else if (ARGV[i] == "variables")
+	gen_vars = 1;
+      ARGV[i] = "";
+    }
+  
+  if (gen_macros)
+    {
+      printf ("/* type macros, generated by maketypes.awk */\n");
+      printf ("\n");
+      printf ("#ifdef G_OS_WIN32\n");
+      printf ("#  ifdef EEL_COMPILATION\n");
+      printf ("#    define EELTYPEBUILTINS_VAR __declspec(dllexport)\n");
+      printf ("#  else\n");
+      printf ("#    define EELTYPEBUILTINS_VAR extern __declspec(dllimport)\n");
+      printf ("#  endif\n");
+      printf ("#else\n");
+      printf ("#  ifdef EEL_COMPILATION\n");
+      printf ("#    define EELTYPEBUILTINS_VAR\n");
+      printf ("#  else\n");
+      printf ("#    define EELTYPEBUILTINS_VAR extern\n");
+      printf ("#  endif\n");
+      printf ("#endif\n");
+      printf ("\n");
+    }
+  else if (gen_entries)
+    printf ("/* type entries, generated by maketypes.awk */\n\n");
+  else if (gen_vars)
+    printf ("/* type variables, generated by maketypes.awk */\n\n");
+  else
+    {
+      printf ("hm? what do you want me to do?\n") > "/dev/stderr";
+      exit 1;
+    }
+}
+
+function set_type (set_type_1)
+{
+  type_counter += 1;
+  type_name = set_type_1;
+  type_macro = "EEL_TYPE_";
+  
+  tmp = type_name;
+# OK, the following is ridiculous, and sed s///g would be far easier
+  gsub ("[A-Z]", "@&", tmp);
+  gsub ("[^A-Z]@", "&_", tmp);
+  gsub ("@", "", tmp);
+  gsub ("[A-Z][A-Z][A-Z][0-9a-z]", "@&", tmp);
+  gsub ("@..", "&_", tmp);
+  gsub ("@", "", tmp);
+  type_macro = type_macro toupper (tmp);
+  type_ident = "_" tolower (tmp);
+
+  sub ("^EEL_TYPE_EEL_", "EEL_TYPE_", type_macro);
+}
+
+function generate (generate_what)
+{
+  if (gen_macros)
+    {
+      printf ("EELTYPEBUILTINS_VAR GType %s;\n", type_macro);
+    }
+  if (gen_entries)
+    {
+      printf ("  { \"%s\", &%s,\n", type_name, type_macro);
+      if (generate_what == "BOXED")
+	printf ("    G_TYPE_%s, %s, %s, },\n", generate_what, boxed_copy, boxed_free);
+      else
+	printf ("    G_TYPE_%s, %s_values },\n", generate_what, type_ident);
+    }
+  if (gen_vars)
+    {
+      printf ("EELTYPEBUILTINS_VAR GType %s = 0;\n", type_macro);
+    }
+}
+
+# skip scheme comments
+";" {
+  sub (";.*", "");
+}
+
+# parse keywords
+
+/\(define-enum/ {
+  if ($2 == "")
+    printf ("huh? define-enum keyword without arg?\n") > "/dev/stderr";
+  else
+    {
+      set_type($2);
+      generate("ENUM");
+    }
+}
+
+/\(define-flags/ {
+  if ($2 == "")
+    printf ("huh? define-flags keyword without arg?\n") > "/dev/stderr";
+  else
+    {
+      set_type($2);
+      generate("FLAGS");
+    }
+}
+
+/\(define-boxed/ {
+  if ($2 == "")
+    printf ("huh? define-boxed keyword without arg?\n") > "/dev/stderr";
+  else
+      {
+	  boxed_copy = "NULL";
+	  boxed_free = "NULL";
+	  set_type($2);
+	  do {
+	      getline;
+	      sub (";.*", "", $0);
+	  } while ($0 ~ /^[ \t]*$/);
+	  tmp_var1 = $1;
+	  if ($0 ~ /\)/) { generate("BOXED"); next; }
+	  do {
+	      getline;
+	      sub (";.*", "", $0);
+	  } while ($0 ~ /^[ \t]*$/);
+	  tmp_var2 = $1;
+	  sub (/\).*/, "", tmp_var2);
+	  if (tmp_var1 ~ /^[_A-Za-z][_A-Za-z0-9]*$/ &&
+	      tmp_var2 ~ /^[_A-Za-z][_A-Za-z0-9]*$/)
+	      {
+		  boxed_copy = tmp_var1;
+		  boxed_free = tmp_var2;
+		  # printf ("read boxed funcs: %s %s\n", boxed_copy, boxed_free) > "/dev/stderr";
+	      }
+	  generate("BOXED");
+      }
+}
+
+END {
+  if (gen_macros)
+    printf("\n#define\tEEL_TYPE_N_BUILTINS\t(%u)\n", type_counter);
+}

Added: trunk/eel/update-from-egg.sh
==============================================================================
--- (empty file)
+++ trunk/eel/update-from-egg.sh	Mon Dec 15 15:56:41 2008
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+function die() {
+  echo $*
+  exit 1
+}
+
+if test -z "$EGGDIR"; then
+   echo "Must set EGGDIR"
+   exit 1
+fi
+
+if test -z "$EGGFILES"; then
+   echo "Must set EGGFILES"
+   exit 1
+fi
+
+for FILE in $EGGFILES; do
+  if cmp -s $EGGDIR/$FILE $FILE; then
+     echo "File $FILE is unchanged"
+  else
+     cp $EGGDIR/$FILE $FILE || die "Could not move $EGGDIR/$FILE to $FILE"
+     echo "Updated $FILE"
+  fi
+done

Modified: trunk/libnautilus-private/Makefile.am
==============================================================================
--- trunk/libnautilus-private/Makefile.am	(original)
+++ trunk/libnautilus-private/Makefile.am	Mon Dec 15 15:56:41 2008
@@ -30,6 +30,7 @@
 	$(SELINUX_LIBS)			\
 	$(BEAGLE_LIBS)			\
 	$(TRACKER_LIBS)			\
+	$(top_builddir)/eel/libeel-2.la \
 	$(top_builddir)/libnautilus-extension/libnautilus-extension.la \
 	$(CORE_LIBS)			\
 	$(NULL)

Modified: trunk/po/POTFILES.in
==============================================================================
--- trunk/po/POTFILES.in	(original)
+++ trunk/po/POTFILES.in	Mon Dec 15 15:56:41 2008
@@ -5,6 +5,14 @@
 cut-n-paste-code/libegg/eggsmclient.c
 data/browser.xml
 data/nautilus.xml.in
+eel/eel-alert-dialog.c
+eel/eel-canvas.c
+eel/eel-editable-label.c
+eel/eel-gconf-extensions.c
+eel/eel-glib-extensions.c
+eel/eel-gnome-extensions.c
+eel/eel-stock-dialogs.c
+eel/eel-vfs-extensions.c
 libnautilus-extension/nautilus-column.c
 libnautilus-extension/nautilus-menu-item.c
 libnautilus-extension/nautilus-property-page.c



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