[goobox] ported to modern libraries, removed use of deprecated functions



commit 9b623aff84019c6bb227b3e3edabb0807c00d073
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Fri Nov 13 23:27:40 2009 +0100

    ported to modern libraries, removed use of deprecated functions
    
    huge commit that ports goobox in the modern age.

 AUTHORS                            |    2 +-
 ChangeLog                          | 1357 -----------
 MAINTAINERS                        |    2 +-
 Makefile.am                        |   13 +-
 autogen.sh                         |    4 +-
 configure.ac                       |  199 ++
 configure.in                       |  218 --
 copy-n-paste/Makefile.am           |   16 +
 copy-n-paste/eggdesktopfile.c      | 1477 ++++++++++++
 copy-n-paste/eggdesktopfile.h      |  159 ++
 copy-n-paste/eggsmclient-private.h |   53 +
 copy-n-paste/eggsmclient-xsmp.c    | 1370 +++++++++++
 copy-n-paste/eggsmclient.c         |  589 +++++
 copy-n-paste/eggsmclient.h         |  117 +
 data/GNOME_Goobox.server.in        |   15 -
 data/Makefile.am                   |   19 +-
 data/glade/Makefile.am             |   13 -
 data/glade/cover_chooser.glade     |  237 --
 data/glade/extract_dialog.glade    |  241 --
 data/glade/format_dialog.glade     |  288 ---
 data/glade/preferences.glade       |  719 ------
 data/glade/properties.glade        |  657 ------
 data/glade/ripper_dialog.glade     |  151 --
 data/goobox.applications           |    6 -
 data/goobox.schemas.in             |   13 -
 data/{glade => ui}/.cvsignore      |    0
 data/ui/Makefile.am                |   12 +
 data/ui/cover-chooser.ui           |  187 ++
 data/ui/extract.ui                 |  186 ++
 data/ui/format-options.ui          |  178 ++
 data/ui/preferences.ui             |  454 ++++
 data/ui/properties.ui              |  485 ++++
 data/ui/ripper.ui                  |  140 ++
 po/POTFILES.in                     |   61 +-
 po/update-potfiles.sh              |   13 +
 src/GNOME_Goobox.idl               |   24 -
 src/Makefile.am                    |  139 +-
 src/actions.c                      |   86 +-
 src/album-info.c                   |   20 +-
 src/bacon-cd-selection.c           |  534 -----
 src/bacon-cd-selection.h           |   57 -
 src/cd-drive.c                     | 1455 ------------
 src/cd-drive.h                     |   89 -
 src/dlg-cover-chooser.c            |  998 ++++-----
 src/dlg-extract.c                  |  190 +--
 src/dlg-preferences.c              |  309 +--
 src/dlg-properties.c               |  199 +--
 src/dlg-ripper.c                   |  394 ++--
 src/dlg-ripper.h                   |    1 -
 src/dvd_plus_rw_utils.cpp          |  511 -----
 src/eggtrayicon.c                  |  559 -----
 src/eggtrayicon.h                  |   77 -
 src/file-utils.c                   | 1583 -------------
 src/file-utils.h                   |  103 -
 src/gconf-utils.c                  |  162 +-
 src/gconf-utils.h                  |   47 +-
 src/gio-utils.c                    |  458 ++++
 src/gio-utils.h                    |   75 +
 src/glib-utils.c                   | 2192 +++++++++++++++++-
 src/glib-utils.h                   |  250 ++-
 src/gnome-desktop-thumbnail.c      | 1329 +++++++++++
 src/gnome-desktop-thumbnail.h      |  114 +
 src/gnome-thumbnail-pixbuf-utils.c |  172 ++
 src/goo-application.c              |  219 --
 src/goo-application.h              |   54 -
 src/goo-cdrom-bsd.c                |  290 ---
 src/goo-cdrom-bsd.h                |   52 -
 src/goo-cdrom-linux.c              |  290 ---
 src/goo-cdrom-linux.h              |   52 -
 src/goo-cdrom-solaris.c            |  291 ---
 src/goo-cdrom-solaris.h            |   52 -
 src/goo-cdrom.c                    |  389 ----
 src/goo-cdrom.h                    |  106 -
 src/goo-marshal.list               |    1 +
 src/goo-player-info.c              |  155 +-
 src/goo-player-info.h              |    3 +-
 src/goo-player.c                   |  645 +++----
 src/goo-player.h                   |   27 +-
 src/goo-stock.c                    |    2 +-
 src/goo-volume-tool-button.c       |  207 +--
 src/goo-volume-tool-button.h       |    2 -
 src/goo-window.c                   | 1354 ++++++------
 src/goo-window.h                   |  102 +-
 src/gth-image-list.c               | 4439 ------------------------------------
 src/gth-image-list.h               |  321 ---
 src/gth-user-dir.c                 |  109 +
 src/gth-user-dir.h                 |   45 +
 src/gth-window.c                   |  394 ++++
 src/gth-window.h                   |   89 +
 src/gthumb-marshal.list            |   14 -
 src/gthumb-slide.c                 |  833 -------
 src/gthumb-slide.h                 |   86 -
 src/gtk-file-chooser-preview.c     |  140 +-
 src/gtk-utils.c                    |  870 ++++++--
 src/gtk-utils.h                    |  201 +-
 src/main.c                         |  815 +++----
 src/main.h                         |   15 +-
 src/preferences.c                  |   15 +-
 src/preferences.h                  |    1 -
 src/track-info.c                   |    2 +-
 src/transport.hxx                  | 1102 ---------
 src/typedefs.h                     |    7 +
 src/ui.h                           |    3 +-
 103 files changed, 14088 insertions(+), 21484 deletions(-)
---
diff --git a/AUTHORS b/AUTHORS
index d2db847..ece6749 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1 @@
-Paolo Bacchilega <paobac cvs gnome org>
+Paolo Bacchilega <paobac src gnome org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 7f62799..c14f939 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1,3 +1,3 @@
 Paolo Bacchilega 
-E-mail: paobac svn gnome org 
+E-mail: paobac src gnome org 
 Userid: paobac
diff --git a/Makefile.am b/Makefile.am
index c1f58cd..aed33ff 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,14 +1,5 @@
-## Process this file with automake to produce Makefile.in.
-SUBDIRS =		\
-	data		\
-	help		\
-	src		\
-	po
-
-
-distcleancheck_listfiles = find . -type f -print | grep -v 'omf\.out'
-
-distuninstallcheck_listfiles = find . -type f -print | grep -v '^\./var/scrollkeeper' | grep -v 'omf' | grep -v 'figures'
+SUBDIRS = copy-n-paste data help src po
+ACLOCAL_AMFLAGS = -I m4
 
 
 EXTRA_DIST = 			\
diff --git a/autogen.sh b/autogen.sh
index 617d873..eded612 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -6,9 +6,7 @@ test -z "$srcdir" && srcdir=.
 
 PKG_NAME="Goobox"
 
-(test -f $srcdir/configure.in \
-  && test -f $srcdir/ChangeLog \
-  && test -d $srcdir/src) || {
+(test -f $srcdir/configure.ac && test -d $srcdir/src) || {
     echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
     echo " top-level $PKG_NAME directory"
     exit 1
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..35e5d6a
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,199 @@
+AC_INIT([goobox], [2.1.0], [http://bugzilla.gnome.org/enter_bug.cgi?product=goobox])
+
+GNOME_COMMON_INIT
+
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_SRCDIR([configure.ac])
+
+AM_INIT_AUTOMAKE([1.9 foreign])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
+
+AC_PROG_CXX
+AM_PROG_LIBTOOL
+GNOME_DOC_INIT
+GNOME_COMPILE_WARNINGS([maximum])
+GNOME_MAINTAINER_MODE_DEFINES
+
+GLIB_REQUIRED=2.22
+GTK_REQUIRED=2.18
+GSTREAMER_REQUIRED=0.10.12
+LIBNOTIFY_REQUIRED=0.4.3
+LIBMUSICBRAINZ_REQUIRED=2.1.0
+
+dnl ===========================================================================
+
+PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= $GTK_REQUIRED])
+AC_SUBST([GTK_CFLAGS])
+AC_SUBST([GTK_LIBS])
+
+PKG_CHECK_MODULES(GOO, [
+	glib-2.0 >= $GLIB_REQUIRED
+	gthread-2.0				
+	gtk+-2.0 >= $GTK_REQUIRED
+	gstreamer-0.10 >= $GSTREAMER_REQUIRED
+	libbrasero-media	
+	libmusicbrainz >= $LIBMUSICBRAINZ_REQUIRED 
+	gconf-2.0
+	unique-1.0
+])
+AC_SUBST(GOO_CFLAGS)
+AC_SUBST(GOO_LIBS)
+
+dnl ===========================================================================
+
+AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
+AC_PATH_PROG(GLIB_MKENUMS, glib-mkenums)
+
+AC_ARG_ENABLE(run_in_place,
+	      AS_HELP_STRING([--enable-run-in-place],[load ui data and extensions from the source tree]),
+	      [case "${enableval}" in
+	      yes) enable_run_in_place=yes ;;
+	      no) enable_run_in_place=no ;;
+	      *) AC_MSG_ERROR([bad value ${enableval} for --enable-run-in-place]) ;;
+	      esac],
+	      [enable_run_in_place=no])
+AM_CONDITIONAL(RUN_IN_PLACE, test "x$enable_run_in_place" != xno)
+
+if test x$enable_run_in_place = xyes; then
+	AC_DEFINE(RUN_IN_PLACE, 1, [load ui data and extensions from the source tree])
+fi
+
+dnl ===========================================================================
+dnl
+dnl libnotify
+dnl
+
+AC_ARG_ENABLE(notification,[AC_HELP_STRING([--enable-notification],[enable current track notification if libnotify is available [default=yes]])],, [enable_notification="yes"])
+AM_CONDITIONAL(ENABLE_NOTIFICATION, test "x$enable_notification" = xyes)
+
+LIBNOTIFY_LIBS=""
+LIBNOTIFY_CFLAGS=""
+if test x"$enable_notification" = xyes; then
+   AC_MSG_CHECKING(for notification support)
+
+   if pkg-config --atleast-version=$LIBNOTIFY_REQUIRED libnotify; then
+      LIBNOTIFY_LIBS=`pkg-config --libs libnotify`
+      LIBNOTIFY_CFLAGS=`pkg-config --cflags libnotify`
+      AC_DEFINE(ENABLE_NOTIFICATION, 1, [Have libnotify])
+   else
+      enable_notification=no
+   fi
+fi
+AC_MSG_RESULT($enable_notification)
+AC_SUBST(LIBNOTIFY_LIBS)
+AC_SUBST(LIBNOTIFY_CFLAGS)
+
+dnl ===========================================================================
+
+AC_ARG_ENABLE(media-keys,[AC_HELP_STRING([--enable-media-keys],[enable use of the keyboard media keys [default=yes]])],, [enable_media_keys="yes"])
+AM_CONDITIONAL(ENABLE_MEDIA_KEYS, test "x$enable_media_keys" = xyes)
+
+DBUS_LIBS=""
+DBUS_CFLAGS=""
+if test x"$enable_media_keys" = xyes; then
+   AC_MSG_CHECKING(for media keys support)
+
+   if pkg-config dbus-glib-1; then
+      DBUS_LIBS=`pkg-config --libs dbus-glib-1`
+      DBUS_CFLAGS=`pkg-config --cflags dbus-glib-1`
+      AC_DEFINE(ENABLE_MEDIA_KEYS, 1, [enable media keys support])
+   else
+      enable_media_keys=no
+   fi
+fi
+AC_MSG_RESULT($enable_media_keys)
+AC_SUBST(DBUS_LIBS)
+AC_SUBST(DBUS_CFLAGS)
+
+dnl ===========================================================================
+
+SYSTEM_LIBS=""
+host=`uname -s`
+case "$host" in
+*FreeBSD*)
+	DEFAULT_DEVICE="/dev/acd0c"
+	AC_DEFINE(HAVE_BSD, 1, [Define to 1 if this is a BSD system])
+	SYSTEM_LIBS="-lcam"
+	;;
+*NetBSD*)
+	DEFAULT_DEVICE="/dev/rcd0d"
+	AC_DEFINE(HAVE_BSD, 1, [Define to 1 if this is a BSD system])
+	;;
+*Linux*)
+	DEFAULT_DEVICE="/dev/cdrom"
+	AC_DEFINE(HAVE_LINUX, 1, [Define to 1 if this is a Linux system])
+	;;
+*SunOS*)
+	DEFAULT_DEVICE="/vol/dev/aliases/cdrom0"
+	AC_DEFINE(HAVE_SOLARIS, 1, [Define to 1 if this is a Solaris system])
+	;;
+esac
+
+AC_DEFINE_UNQUOTED(DEFAULT_DEVICE, "$DEFAULT_DEVICE", [Default CD device])
+AC_SUBST(SYSTEM_LIBS)
+
+dnl ===========================================================================
+
+IT_PROG_INTLTOOL([0.35.0])
+GETTEXT_PACKAGE=goobox
+AC_SUBST([GETTEXT_PACKAGE])
+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [GetText Package])
+AM_GLIB_GNU_GETTEXT
+
+if test "x${prefix}" = "xNONE"; then
+	AC_DEFINE_UNQUOTED(LOCALEDIR, "${ac_default_prefix}/${DATADIRNAME}/locale", [Locale directory])
+else
+	AC_DEFINE_UNQUOTED(LOCALEDIR, "${prefix}/${DATADIRNAME}/locale", [Locale directory])
+fi
+
+dnl ===========================================================================
+
+AC_ARG_ENABLE(deprecations,
+	      [AS_HELP_STRING([--enable-deprecations],[warn about deprecated usages])])
+if test "x$enable_deprecations" = "xyes"; then
+	DISABLE_DEPRECATED="-DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED"
+fi
+AC_SUBST(DISABLE_DEPRECATED)
+
+dnl ===========================================================================
+
+AC_ARG_ENABLE(schemas-install,[AC_HELP_STRING([--disable-schemas-install],
+					      [Disable installation of the gconf schemas])])
+AM_CONDITIONAL(SCHEMAS_INSTALL, test x$enable_schemas_install != xno)
+
+AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
+if test x"$GCONFTOOL" = xno; then
+	AC_MSG_ERROR([gconftool-2 executable not found in your path - should be installed with GConf])
+fi
+AM_GCONF_SOURCE_2
+
+dnl ===========================================================================
+
+AC_CONFIG_FILES([
+Makefile
+copy-n-paste/Makefile
+data/Makefile
+data/goobox.desktop.in
+data/ui/Makefile
+data/icons/Makefile
+data/icons/48x48/Makefile
+data/icons/48x48/apps/Makefile
+help/Makefile
+src/Makefile
+src/icons/Makefile
+po/Makefile.in
+])
+
+AC_OUTPUT
+
+echo "
+
+Configuration:
+
+	Source code location:   ${srcdir}
+	Compiler:               ${CC}
+	Run in place:           ${enable_run_in_place}
+	Enable notification:    ${enable_notification}
+	Enable media keys:      ${enable_media_keys}
+"
diff --git a/copy-n-paste/Makefile.am b/copy-n-paste/Makefile.am
new file mode 100644
index 0000000..3846c85
--- /dev/null
+++ b/copy-n-paste/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES = -DGTK_DISABLE_DEPRECATED \
+	   -DGDK_DISABLE_DEPRECATED \
+	   -DG_DISABLE_DEPRECATED 
+
+noinst_LTLIBRARIES = libeggsmclient.la
+
+libeggsmclient_la_LIBADD = $(GTK_LIBS)
+libeggsmclient_la_CFLAGS = $(GTK_CFLAGS)
+libeggsmclient_la_SOURCES = eggdesktopfile.h \
+			    eggdesktopfile.c \
+			    eggsmclient.h \
+			    eggsmclient.c \
+			    eggsmclient-private.h \
+			    eggsmclient-xsmp.c
+
+-include $(top_srcdir)/git.mk
diff --git a/copy-n-paste/eggdesktopfile.c b/copy-n-paste/eggdesktopfile.c
new file mode 100644
index 0000000..357e548
--- /dev/null
+++ b/copy-n-paste/eggdesktopfile.c
@@ -0,0 +1,1477 @@
+/* eggdesktopfile.c - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Based on gnome-desktop-item.c
+ * Copyright (C) 1999, 2000 Red Hat Inc.
+ * Copyright (C) 2001 George Lebl
+ *
+ * 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; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place -
+ * Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "eggdesktopfile.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+struct EggDesktopFile {
+  GKeyFile           *key_file;
+  char               *source;
+
+  char               *name, *icon;
+  EggDesktopFileType  type;
+  char                document_code;
+};
+
+/**
+ * egg_desktop_file_new:
+ * @desktop_file_path: path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @desktop_file.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new (const char *desktop_file_path, GError **error)
+{
+  GKeyFile *key_file;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  return egg_desktop_file_new_from_key_file (key_file, desktop_file_path,
+					     error);
+}
+
+/**
+ * egg_desktop_file_new_from_data_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_data_dirs (const char  *desktop_file_path,
+				     GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  GKeyFile *key_file;
+  char *full_path;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path,
+				       &full_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  desktop_file = egg_desktop_file_new_from_key_file (key_file,
+						     full_path,
+						     error);
+  g_free (full_path);
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_new_from_dirs:
+ * @desktop_file_path: relative path to a Freedesktop-style Desktop file
+ * @search_dirs: NULL-terminated array of directories to search
+ * @error: error pointer
+ *
+ * Looks for @desktop_file_path in the paths returned from
+ * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
+ * a new #EggDesktopFile from it.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_dirs (const char  *desktop_file_path,
+				const char **search_dirs,
+				GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  GKeyFile *key_file;
+  char *full_path;
+
+  key_file = g_key_file_new ();
+  if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs,
+				  &full_path, 0, error))
+    {
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  desktop_file = egg_desktop_file_new_from_key_file (key_file,
+						     full_path,
+						     error);
+  g_free (full_path);
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_new_from_key_file:
+ * @key_file: a #GKeyFile representing a desktop file
+ * @source: the path or URI that @key_file was loaded from, or %NULL
+ * @error: error pointer
+ *
+ * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
+ * @key_file (on success or failure); you should consider @key_file to
+ * be freed after calling this function.
+ *
+ * Return value: the new #EggDesktopFile, or %NULL on error.
+ **/
+EggDesktopFile *
+egg_desktop_file_new_from_key_file (GKeyFile    *key_file,
+				    const char  *source,
+				    GError     **error)
+{
+  EggDesktopFile *desktop_file;
+  char *version, *type;
+
+  if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP))
+    {
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_INVALID,
+		   _("File is not a valid .desktop file"));
+      g_key_file_free (key_file);
+      return NULL;
+    }
+
+  version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP,
+				  EGG_DESKTOP_FILE_KEY_VERSION,
+				  NULL);
+  if (version)
+    {
+      double version_num;
+      char *end;
+
+      version_num = g_ascii_strtod (version, &end);
+      if (*end)
+	{
+	  g_warning ("Invalid Version string '%s' in %s",
+		     version, source ? source : "(unknown)");
+	}
+      else if (version_num > 1.0)
+	{
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_INVALID,
+		       _("Unrecognized desktop file Version '%s'"), version);
+	  g_free (version);
+	  g_key_file_free (key_file);
+	  return NULL;
+	}
+      g_free (version);
+    }
+
+  desktop_file = g_new0 (EggDesktopFile, 1);
+  desktop_file->key_file = key_file;
+
+  if (g_path_is_absolute (source))
+    desktop_file->source = g_filename_to_uri (source, NULL, NULL);
+  else
+    desktop_file->source = g_strdup (source);
+
+  desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+					      EGG_DESKTOP_FILE_KEY_NAME, error);
+  if (!desktop_file->name)
+    {
+      egg_desktop_file_free (desktop_file);
+      return NULL;
+    }
+
+  type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_TYPE, error);
+  if (!type)
+    {
+      egg_desktop_file_free (desktop_file);
+      return NULL;
+    }
+
+  if (!strcmp (type, "Application"))
+    {
+      char *exec, *p;
+
+      desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION;
+
+      exec = g_key_file_get_string (key_file,
+				    EGG_DESKTOP_FILE_GROUP,
+				    EGG_DESKTOP_FILE_KEY_EXEC,
+				    error);
+      if (!exec)
+	{
+	  egg_desktop_file_free (desktop_file);
+	  g_free (type);
+	  return NULL;
+	}
+
+      /* See if it takes paths or URIs or neither */
+      for (p = exec; *p; p++)
+	{
+	  if (*p == '%')
+	    {
+	      if (p[1] == '\0' || strchr ("FfUu", p[1]))
+		{
+		  desktop_file->document_code = p[1];
+		  break;
+		}
+	      p++;
+	    }
+	}
+
+      g_free (exec);
+    }
+  else if (!strcmp (type, "Link"))
+    {
+      char *url;
+
+      desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK;
+
+      url = g_key_file_get_string (key_file,
+				   EGG_DESKTOP_FILE_GROUP,
+				   EGG_DESKTOP_FILE_KEY_URL,
+				   error);
+      if (!url)
+	{
+	  egg_desktop_file_free (desktop_file);
+	  g_free (type);
+	  return NULL;
+	}
+      g_free (url);
+    }
+  else if (!strcmp (type, "Directory"))
+    desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY;
+  else
+    desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
+
+  g_free (type);
+
+  /* Check the Icon key */
+  desktop_file->icon = g_key_file_get_string (key_file,
+					      EGG_DESKTOP_FILE_GROUP,
+					      EGG_DESKTOP_FILE_KEY_ICON,
+					      NULL);
+  if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon))
+    {
+      char *ext;
+
+      /* Lots of .desktop files still get this wrong */
+      ext = strrchr (desktop_file->icon, '.');
+      if (ext && (!strcmp (ext, ".png") ||
+		  !strcmp (ext, ".xpm") ||
+		  !strcmp (ext, ".svg")))
+	{
+	  g_warning ("Desktop file '%s' has malformed Icon key '%s'"
+		     "(should not include extension)",
+		     source ? source : "(unknown)",
+		     desktop_file->icon);
+	  *ext = '\0';
+	}
+    }
+
+  return desktop_file;
+}
+
+/**
+ * egg_desktop_file_free:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Frees @desktop_file.
+ **/
+void
+egg_desktop_file_free (EggDesktopFile *desktop_file)
+{
+  g_key_file_free (desktop_file->key_file);
+  g_free (desktop_file->source);
+  g_free (desktop_file->name);
+  g_free (desktop_file->icon);
+  g_free (desktop_file);
+}
+
+/**
+ * egg_desktop_file_get_source:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the URI that @desktop_file was loaded from.
+ *
+ * Return value: @desktop_file's source URI
+ **/
+const char *
+egg_desktop_file_get_source (EggDesktopFile *desktop_file)
+{
+  return desktop_file->source;
+}
+
+/**
+ * egg_desktop_file_get_desktop_file_type:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the desktop file type of @desktop_file.
+ *
+ * Return value: @desktop_file's type
+ **/
+EggDesktopFileType
+egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file)
+{
+  return desktop_file->type;
+}
+
+/**
+ * egg_desktop_file_get_name:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the (localized) value of @desktop_file's "Name" key.
+ *
+ * Return value: the application/link name
+ **/
+const char *
+egg_desktop_file_get_name (EggDesktopFile *desktop_file)
+{
+  return desktop_file->name;
+}
+
+/**
+ * egg_desktop_file_get_icon:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Gets the value of @desktop_file's "Icon" key.
+ *
+ * If the icon string is a full path (that is, if g_path_is_absolute()
+ * returns %TRUE when called on it), it points to a file containing an
+ * unthemed icon. If the icon string is not a full path, it is the
+ * name of a themed icon, which can be looked up with %GtkIconTheme,
+ * or passed directly to a theme-aware widget like %GtkImage or
+ * %GtkCellRendererPixbuf.
+ *
+ * Return value: the icon path or name
+ **/
+const char *
+egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
+{
+  return desktop_file->icon;
+}
+
+gboolean
+egg_desktop_file_has_key (EggDesktopFile  *desktop_file,
+			  const char      *key,
+			  GError         **error)
+{
+  return g_key_file_has_key (desktop_file->key_file,
+			     EGG_DESKTOP_FILE_GROUP, key,
+			     error);
+}
+
+char *
+egg_desktop_file_get_string (EggDesktopFile  *desktop_file,
+			     const char      *key,
+			     GError         **error)
+{
+  return g_key_file_get_string (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+}
+
+char *
+egg_desktop_file_get_locale_string (EggDesktopFile  *desktop_file,
+				    const char      *key,
+				    const char      *locale,
+				    GError         **error)
+{
+  return g_key_file_get_locale_string (desktop_file->key_file,
+				       EGG_DESKTOP_FILE_GROUP, key, locale,
+				       error);
+}
+
+gboolean
+egg_desktop_file_get_boolean (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+  return g_key_file_get_boolean (desktop_file->key_file,
+				 EGG_DESKTOP_FILE_GROUP, key,
+				 error);
+}
+
+double
+egg_desktop_file_get_numeric (EggDesktopFile  *desktop_file,
+			      const char      *key,
+			      GError         **error)
+{
+  return g_key_file_get_double (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP, key,
+				error);
+}
+
+char **
+egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file,
+				  const char      *key,
+				  gsize           *length,
+				  GError         **error)
+{
+  return g_key_file_get_string_list (desktop_file->key_file,
+				     EGG_DESKTOP_FILE_GROUP, key, length,
+				     error);
+}
+
+char **
+egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
+					 const char      *key,
+					 const char      *locale,
+					 gsize           *length,
+					 GError         **error)
+{
+  return g_key_file_get_locale_string_list (desktop_file->key_file,
+					    EGG_DESKTOP_FILE_GROUP, key,
+					    locale, length,
+					    error);
+}
+
+/**
+ * egg_desktop_file_can_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @desktop_environment: the name of the running desktop environment,
+ * or %NULL
+ *
+ * Tests if @desktop_file can/should be launched in the current
+ * environment. If @desktop_environment is non-%NULL, @desktop_file's
+ * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
+ * this desktop_file is appropriate for the named environment.
+ *
+ * Furthermore, if @desktop_file has type
+ * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
+ * also checked, to make sure the binary it points to exists.
+ *
+ * egg_desktop_file_can_launch() does NOT check the value of the
+ * "Hidden" key.
+ *
+ * Return value: %TRUE if @desktop_file can be launched
+ **/
+gboolean
+egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
+			     const char     *desktop_environment)
+{
+  char *try_exec, *found_program;
+  char **only_show_in, **not_show_in;
+  gboolean found;
+  int i;
+
+  if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
+      desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
+    return FALSE;
+
+  if (desktop_environment)
+    {
+      only_show_in = g_key_file_get_string_list (desktop_file->key_file,
+						 EGG_DESKTOP_FILE_GROUP,
+						 EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
+						 NULL, NULL);
+      if (only_show_in)
+	{
+	  for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
+	    {
+	      if (!strcmp (only_show_in[i], desktop_environment))
+		found = TRUE;
+	    }
+
+	  g_strfreev (only_show_in);
+
+	  if (!found)
+	    return FALSE;
+	}
+
+      not_show_in = g_key_file_get_string_list (desktop_file->key_file,
+						EGG_DESKTOP_FILE_GROUP,
+						EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
+						NULL, NULL);
+      if (not_show_in)
+	{
+	  for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
+	    {
+	      if (!strcmp (not_show_in[i], desktop_environment))
+		found = TRUE;
+	    }
+
+	  g_strfreev (not_show_in);
+
+	  if (found)
+	    return FALSE;
+	}
+    }
+
+  if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
+    {
+      try_exec = g_key_file_get_string (desktop_file->key_file,
+					EGG_DESKTOP_FILE_GROUP,
+					EGG_DESKTOP_FILE_KEY_TRY_EXEC,
+					NULL);
+      if (try_exec)
+	{
+	  found_program = g_find_program_in_path (try_exec);
+	  g_free (try_exec);
+
+	  if (!found_program)
+	    return FALSE;
+	  g_free (found_program);
+	}
+    }
+
+  return TRUE;
+}
+
+/**
+ * egg_desktop_file_accepts_documents:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file represents an application that can accept
+ * documents on the command line.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
+{
+  return desktop_file->document_code != 0;
+}
+
+/**
+ * egg_desktop_file_accepts_multiple:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept multiple documents at once.
+ *
+ * If this returns %FALSE, you can still pass multiple documents to
+ * egg_desktop_file_launch(), but that will result in multiple copies
+ * of the application being launched. See egg_desktop_file_launch()
+ * for more details.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
+{
+  return (desktop_file->document_code == 'F' ||
+	  desktop_file->document_code == 'U');
+}
+
+/**
+ * egg_desktop_file_accepts_uris:
+ * @desktop_file: an #EggDesktopFile
+ *
+ * Tests if @desktop_file can accept (non-"file:") URIs as documents to
+ * open.
+ *
+ * Return value: %TRUE or %FALSE
+ **/
+gboolean
+egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
+{
+  return (desktop_file->document_code == 'U' ||
+	  desktop_file->document_code == 'u');
+}
+
+static void
+append_quoted_word (GString    *str,
+		    const char *s,
+		    gboolean    in_single_quotes,
+		    gboolean    in_double_quotes)
+{
+  const char *p;
+
+  if (!in_single_quotes && !in_double_quotes)
+    g_string_append_c (str, '\'');
+  else if (!in_single_quotes && in_double_quotes)
+    g_string_append (str, "\"'");
+
+  if (!strchr (s, '\''))
+    g_string_append (str, s);
+  else
+    {
+      for (p = s; *p != '\0'; p++)
+	{
+	  if (*p == '\'')
+	    g_string_append (str, "'\\''");
+	  else
+	    g_string_append_c (str, *p);
+	}
+    }
+
+  if (!in_single_quotes && !in_double_quotes)
+    g_string_append_c (str, '\'');
+  else if (!in_single_quotes && in_double_quotes)
+    g_string_append (str, "'\"");
+}
+
+static void
+do_percent_subst (EggDesktopFile *desktop_file,
+		  char            code,
+		  GString        *str,
+		  GSList        **documents,
+		  gboolean        in_single_quotes,
+		  gboolean        in_double_quotes)
+{
+  GSList *d;
+  char *doc;
+
+  switch (code)
+    {
+    case '%':
+      g_string_append_c (str, '%');
+      break;
+
+    case 'F':
+    case 'U':
+      for (d = *documents; d; d = d->next)
+	{
+	  doc = d->data;
+	  g_string_append (str, " ");
+	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+	}
+      *documents = NULL;
+      break;
+
+    case 'f':
+    case 'u':
+      if (*documents)
+	{
+	  doc = (*documents)->data;
+	  g_string_append (str, " ");
+	  append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
+	  *documents = (*documents)->next;
+	}
+      break;
+
+    case 'i':
+      if (desktop_file->icon)
+	{
+	  g_string_append (str, "--icon ");
+	  append_quoted_word (str, desktop_file->icon,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'c':
+      if (desktop_file->name)
+	{
+	  append_quoted_word (str, desktop_file->name,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'k':
+      if (desktop_file->source)
+	{
+	  append_quoted_word (str, desktop_file->source,
+			      in_single_quotes, in_double_quotes);
+	}
+      break;
+
+    case 'D':
+    case 'N':
+    case 'd':
+    case 'n':
+    case 'v':
+    case 'm':
+      /* Deprecated; skip */
+      break;
+
+    default:
+      g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
+      break;
+    }
+}
+
+static char *
+parse_exec (EggDesktopFile  *desktop_file,
+	    GSList         **documents,
+	    GError         **error)
+{
+  char *exec, *p, *command;
+  gboolean escape, single_quot, double_quot;
+  GString *gs;
+
+  exec = g_key_file_get_string (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_EXEC,
+				error);
+  if (!exec)
+    return NULL;
+
+  /* Build the command */
+  gs = g_string_new (NULL);
+  escape = single_quot = double_quot = FALSE;
+
+  for (p = exec; *p != '\0'; p++)
+    {
+      if (escape)
+	{
+	  escape = FALSE;
+	  g_string_append_c (gs, *p);
+	}
+      else if (*p == '\\')
+	{
+	  if (!single_quot)
+	    escape = TRUE;
+	  g_string_append_c (gs, *p);
+	}
+      else if (*p == '\'')
+	{
+	  g_string_append_c (gs, *p);
+	  if (!single_quot && !double_quot)
+	    single_quot = TRUE;
+	  else if (single_quot)
+	    single_quot = FALSE;
+	}
+      else if (*p == '"')
+	{
+	  g_string_append_c (gs, *p);
+	  if (!single_quot && !double_quot)
+	    double_quot = TRUE;
+	  else if (double_quot)
+	    double_quot = FALSE;
+	}
+      else if (*p == '%' && p[1])
+	{
+	  do_percent_subst (desktop_file, p[1], gs, documents,
+			    single_quot, double_quot);
+	  p++;
+	}
+      else
+	g_string_append_c (gs, *p);
+    }
+
+  g_free (exec);
+  command = g_string_free (gs, FALSE);
+
+  /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
+  if (g_key_file_has_key (desktop_file->key_file,
+			  EGG_DESKTOP_FILE_GROUP,
+			  EGG_DESKTOP_FILE_KEY_TERMINAL,
+			  NULL))
+    {
+      GError *terminal_error = NULL;
+      gboolean use_terminal =
+	g_key_file_get_boolean (desktop_file->key_file,
+				EGG_DESKTOP_FILE_GROUP,
+				EGG_DESKTOP_FILE_KEY_TERMINAL,
+				&terminal_error);
+      if (terminal_error)
+	{
+	  g_free (command);
+	  g_propagate_error (error, terminal_error);
+	  return NULL;
+	}
+
+      if (use_terminal)
+	{
+	  gs = g_string_new ("xdg-terminal ");
+	  append_quoted_word (gs, command, FALSE, FALSE);
+	  g_free (command);
+	  command = g_string_free (gs, FALSE);
+	}
+    }
+
+  return command;
+}
+
+static GSList *
+translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
+{
+  gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
+  GSList *ret, *d;
+
+  for (d = documents, ret = NULL; d; d = d->next)
+    {
+      const char *document = d->data;
+      gboolean is_uri = !g_path_is_absolute (document);
+      char *translated;
+
+      if (accepts_uris)
+	{
+	  if (is_uri)
+	    translated = g_strdup (document);
+	  else
+	    translated = g_filename_to_uri (document, NULL, NULL);
+	}
+      else
+	{
+	  if (is_uri)
+	    translated = g_filename_from_uri (document, NULL, NULL);
+	  else
+	    translated = g_strdup (document);
+	}
+
+      if (translated)
+	ret = g_slist_prepend (ret, translated);
+    }
+
+  return g_slist_reverse (ret);
+}
+
+static void
+free_document_list (GSList *documents)
+{
+  GSList *d;
+
+  for (d = documents; d; d = d->next)
+    g_free (d->data);
+  g_slist_free (documents);
+}
+
+/**
+ * egg_desktop_file_parse_exec:
+ * @desktop_file: a #EggDesktopFile
+ * @documents: a list of document paths or URIs
+ * @error: error pointer
+ *
+ * Parses @desktop_file's Exec key, inserting @documents into it, and
+ * returns the result.
+ *
+ * If @documents contains non-file: URIs and @desktop_file does not
+ * accept URIs, those URIs will be ignored. Likewise, if @documents
+ * contains more elements than @desktop_file accepts, the extra
+ * documents will be ignored.
+ *
+ * Return value: the parsed Exec string
+ **/
+char *
+egg_desktop_file_parse_exec (EggDesktopFile  *desktop_file,
+			     GSList          *documents,
+			     GError         **error)
+{
+  GSList *translated, *docs;
+  char *command;
+
+  docs = translated = translate_document_list (desktop_file, documents);
+  command = parse_exec (desktop_file, &docs, error);
+  free_document_list (translated);
+
+  return command;
+}
+
+static gboolean
+parse_link (EggDesktopFile  *desktop_file,
+	    EggDesktopFile **app_desktop_file,
+	    GSList         **documents,
+	    GError         **error)
+{
+  char *url;
+  GKeyFile *key_file;
+
+  url = g_key_file_get_string (desktop_file->key_file,
+			       EGG_DESKTOP_FILE_GROUP,
+			       EGG_DESKTOP_FILE_KEY_URL,
+			       error);
+  if (!url)
+    return FALSE;
+  *documents = g_slist_prepend (NULL, url);
+
+  /* FIXME: use gvfs */
+  key_file = g_key_file_new ();
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_NAME,
+			 "xdg-open");
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_TYPE,
+			 "Application");
+  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
+			 EGG_DESKTOP_FILE_KEY_EXEC,
+			 "xdg-open %u");
+  *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
+  return TRUE;
+}
+
+#if GTK_CHECK_VERSION (2, 12, 0)
+static char *
+start_startup_notification (GdkDisplay     *display,
+			    EggDesktopFile *desktop_file,
+			    const char     *argv0,
+			    int             screen,
+			    int             workspace,
+			    guint32         launch_time)
+{
+  static int sequence = 0;
+  char *startup_id;
+  char *description, *wmclass;
+  char *screen_str, *workspace_str;
+
+  if (g_key_file_has_key (desktop_file->key_file,
+			  EGG_DESKTOP_FILE_GROUP,
+			  EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
+			  NULL))
+    {
+      if (!g_key_file_get_boolean (desktop_file->key_file,
+				   EGG_DESKTOP_FILE_GROUP,
+				   EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
+				   NULL))
+	return NULL;
+      wmclass = NULL;
+    }
+  else
+    {
+      wmclass = g_key_file_get_string (desktop_file->key_file,
+				       EGG_DESKTOP_FILE_GROUP,
+				       EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS,
+				       NULL);
+      if (!wmclass)
+	return NULL;
+    }
+
+  if (launch_time == (guint32)-1)
+    launch_time = gdk_x11_display_get_user_time (display);
+  startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
+				g_get_prgname (),
+				(unsigned long)getpid (),
+				g_get_host_name (),
+				argv0,
+				sequence++,
+				(unsigned long)launch_time);
+
+  description = g_strdup_printf (_("Starting %s"), desktop_file->name);
+  screen_str = g_strdup_printf ("%d", screen);
+  workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace);
+
+  gdk_x11_display_broadcast_startup_message (display, "new",
+					     "ID", startup_id,
+					     "NAME", desktop_file->name,
+					     "SCREEN", screen_str,
+					     "BIN", argv0,
+					     "ICON", desktop_file->icon,
+					     "DESKTOP", workspace_str,
+					     "DESCRIPTION", description,
+					     "WMCLASS", wmclass,
+					     NULL);
+
+  g_free (description);
+  g_free (wmclass);
+  g_free (screen_str);
+  g_free (workspace_str);
+
+  return startup_id;
+}
+
+static void
+end_startup_notification (GdkDisplay *display,
+			  const char *startup_id)
+{
+  gdk_x11_display_broadcast_startup_message (display, "remove",
+					     "ID", startup_id,
+					     NULL);
+}
+
+#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */)
+
+typedef struct {
+  GdkDisplay *display;
+  char *startup_id;
+} StartupNotificationData;
+
+static gboolean
+startup_notification_timeout (gpointer data)
+{
+  StartupNotificationData *sn_data = data;
+
+  end_startup_notification (sn_data->display, sn_data->startup_id);
+  g_object_unref (sn_data->display);
+  g_free (sn_data->startup_id);
+  g_free (sn_data);
+
+  return FALSE;
+}
+
+static void
+set_startup_notification_timeout (GdkDisplay *display,
+				  const char *startup_id)
+{
+  StartupNotificationData *sn_data;
+
+  sn_data = g_new (StartupNotificationData, 1);
+  sn_data->display = g_object_ref (display);
+  sn_data->startup_id = g_strdup (startup_id);
+
+  g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
+			 startup_notification_timeout, sn_data);
+}
+#endif /* GTK 2.12 */
+
+static GPtrArray *
+array_putenv (GPtrArray *env, char *variable)
+{
+  guint i, keylen;
+
+  if (!env)
+    {
+      char **envp;
+
+      env = g_ptr_array_new ();
+
+      envp = g_listenv ();
+      for (i = 0; envp[i]; i++)
+        {
+          const char *value;
+
+          value = g_getenv (envp[i]);
+          g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i],
+                                                 value ? value : ""));
+        }
+      g_strfreev (envp);
+    }
+
+  keylen = strcspn (variable, "=");
+
+  /* Remove old value of key */
+  for (i = 0; i < env->len; i++)
+    {
+      char *envvar = env->pdata[i];
+
+      if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=')
+	{
+	  g_free (envvar);
+	  g_ptr_array_remove_index_fast (env, i);
+	  break;
+	}
+    }
+
+  /* Add new value */
+  g_ptr_array_add (env, g_strdup (variable));
+
+  return env;
+}
+
+static gboolean
+egg_desktop_file_launchv (EggDesktopFile *desktop_file,
+			  GSList *documents, va_list args,
+			  GError **error)
+{
+  EggDesktopFileLaunchOption option;
+  GSList *translated_documents = NULL, *docs = NULL;
+  char *command, **argv;
+  int argc, i, screen_num;
+  gboolean success, current_success;
+  GdkDisplay *display;
+  char *startup_id;
+
+  GPtrArray   *env = NULL;
+  char       **variables = NULL;
+  GdkScreen   *screen = NULL;
+  int          workspace = -1;
+  const char  *directory = NULL;
+  guint32      launch_time = (guint32)-1;
+  GSpawnFlags  flags = G_SPAWN_SEARCH_PATH;
+  GSpawnChildSetupFunc setup_func = NULL;
+  gpointer     setup_data = NULL;
+
+  GPid        *ret_pid = NULL;
+  int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
+  char       **ret_startup_id = NULL;
+
+  if (documents && desktop_file->document_code == 0)
+    {
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		   _("Application does not accept documents on command line"));
+      return FALSE;
+    }
+
+  /* Read the options: technically it's incorrect for the caller to
+   * NULL-terminate the list of options (rather than 0-terminating
+   * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
+   * it's more consistent with other glib/gtk methods, and it will
+   * work as long as sizeof (int) <= sizeof (NULL), and NULL is
+   * represented as 0. (Which is true everywhere we care about.)
+   */
+  while ((option = va_arg (args, EggDesktopFileLaunchOption)))
+    {
+      switch (option)
+	{
+	case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
+	  if (env)
+	    g_ptr_array_free (env, TRUE);
+	  env = g_ptr_array_new ();
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
+	  variables = va_arg (args, char **);
+	  for (i = 0; variables[i]; i++)
+	    env = array_putenv (env, variables[i]);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
+	  screen = va_arg (args, GdkScreen *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
+	  workspace = va_arg (args, int);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
+	  directory = va_arg (args, const char *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_TIME:
+	  launch_time = va_arg (args, guint32);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
+	  flags |= va_arg (args, GSpawnFlags);
+	  /* Make sure they didn't set any flags that don't make sense. */
+	  flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
+	  setup_func = va_arg (args, GSpawnChildSetupFunc);
+	  setup_data = va_arg (args, gpointer);
+	  break;
+
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
+	  ret_pid = va_arg (args, GPid *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
+	  ret_stdin = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
+	  ret_stdout = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
+	  ret_stderr = va_arg (args, int *);
+	  break;
+	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
+	  ret_startup_id = va_arg (args, char **);
+	  break;
+
+	default:
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
+		       _("Unrecognized launch option: %d"),
+		       GPOINTER_TO_INT (option));
+	  success = FALSE;
+	  goto out;
+	}
+    }
+
+  if (screen)
+    {
+      char *display_name = gdk_screen_make_display_name (screen);
+      char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
+      env = array_putenv (env, display_env);
+      g_free (display_name);
+      g_free (display_env);
+
+      display = gdk_screen_get_display (screen);
+    }
+  else
+    {
+      display = gdk_display_get_default ();
+      screen = gdk_display_get_default_screen (display);
+    }
+  screen_num = gdk_screen_get_number (screen);
+
+  translated_documents = translate_document_list (desktop_file, documents);
+  docs = translated_documents;
+
+  success = FALSE;
+
+  do
+    {
+      command = parse_exec (desktop_file, &docs, error);
+      if (!command)
+	goto out;
+
+      if (!g_shell_parse_argv (command, &argc, &argv, error))
+	{
+	  g_free (command);
+	  goto out;
+	}
+      g_free (command);
+
+#if GTK_CHECK_VERSION (2, 12, 0)
+      startup_id = start_startup_notification (display, desktop_file,
+					       argv[0], screen_num,
+					       workspace, launch_time);
+      if (startup_id)
+	{
+	  char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
+						  startup_id);
+	  env = array_putenv (env, startup_id_env);
+	  g_free (startup_id_env);
+	}
+#else
+      startup_id = NULL;
+#endif /* GTK 2.12 */
+
+      if (env != NULL)
+	g_ptr_array_add (env, NULL);
+
+      current_success =
+	g_spawn_async_with_pipes (directory,
+				  argv,
+				  env ? (char **)(env->pdata) : NULL,
+				  flags,
+				  setup_func, setup_data,
+				  ret_pid,
+				  ret_stdin, ret_stdout, ret_stderr,
+				  error);
+      g_strfreev (argv);
+
+      if (startup_id)
+	{
+#if GTK_CHECK_VERSION (2, 12, 0)
+	  if (current_success)
+	    {
+	      set_startup_notification_timeout (display, startup_id);
+
+	      if (ret_startup_id)
+		*ret_startup_id = startup_id;
+	      else
+		g_free (startup_id);
+	    }
+	  else
+#endif /* GTK 2.12 */
+	    g_free (startup_id);
+	}
+      else if (ret_startup_id)
+	*ret_startup_id = NULL;
+
+      if (current_success)
+	{
+	  /* If we successfully launch any instances of the app, make
+	   * sure we return TRUE and don't set @error.
+	   */
+	  success = TRUE;
+	  error = NULL;
+
+	  /* Also, only set the output params on the first one */
+	  ret_pid = NULL;
+	  ret_stdin = ret_stdout = ret_stderr = NULL;
+	  ret_startup_id = NULL;
+	}
+    }
+  while (docs && current_success);
+
+ out:
+  if (env)
+    {
+      g_strfreev ((char **)env->pdata);
+      g_ptr_array_free (env, FALSE);
+    }
+  free_document_list (translated_documents);
+
+  return success;
+}
+
+/**
+ * egg_desktop_file_launch:
+ * @desktop_file: an #EggDesktopFile
+ * @documents: a list of URIs or paths to documents to open
+ * @error: error pointer
+ * @...: additional options
+ *
+ * Launches @desktop_file with the given arguments. Additional options
+ * can be specified as follows:
+ *
+ *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
+ *       clears the environment in the child process
+ *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
+ *       adds the NAME=VALUE strings in the given %NULL-terminated
+ *       array to the child process's environment
+ *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
+ *       causes the application to be launched on the given screen
+ *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
+ *       causes the application to be launched on the given workspace
+ *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
+ *       causes the application to be launched in the given directory
+ *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
+ *       sets the "launch time" for the application. If the user
+ *       interacts with another window after @launch_time but before
+ *       the launched application creates its first window, the window
+ *       manager may choose to not give focus to the new application.
+ *       Passing 0 for @launch_time will explicitly request that the
+ *       application not receive focus.
+ *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
+ *       Sets additional #GSpawnFlags to use. See g_spawn_async() for
+ *       more details.
+ *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
+ *       Sets the child setup callback and the data to pass to it.
+ *       (See g_spawn_async() for more details.)
+ *
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
+ *       On a successful launch, sets * pid to the PID of the launched
+ *       application.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
+ *       On a successful launch, sets * startup_id to the Startup
+ *       Notification "startup id" of the launched application.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stdin.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stdout.
+ *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
+ *       On a successful launch, sets * fd to the file descriptor of
+ *       a pipe connected to the application's stderr.
+ *
+ * The options should be terminated with a single %NULL.
+ *
+ * If @documents contains multiple documents, but
+ * egg_desktop_file_accepts_multiple() returns %FALSE for
+ * @desktop_file, then egg_desktop_file_launch() will actually launch
+ * multiple instances of the application. In that case, the return
+ * value (as well as any values passed via
+ * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
+ * first instance of the application that was launched (but the
+ * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
+ * instance).
+ *
+ * Return value: %TRUE if the application was successfully launched.
+ **/
+gboolean
+egg_desktop_file_launch (EggDesktopFile *desktop_file,
+			 GSList *documents, GError **error,
+			 ...)
+{
+  va_list args;
+  gboolean success;
+  EggDesktopFile *app_desktop_file;
+
+  switch (desktop_file->type)
+    {
+    case EGG_DESKTOP_FILE_TYPE_APPLICATION:
+      va_start (args, error);
+      success = egg_desktop_file_launchv (desktop_file, documents,
+					  args, error);
+      va_end (args);
+      break;
+
+    case EGG_DESKTOP_FILE_TYPE_LINK:
+      if (documents)
+	{
+	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		       EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		       _("Can't pass document URIs to a 'Type=Link' desktop entry"));
+	  return FALSE;
+	}	  
+
+      if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
+	return FALSE;
+
+      va_start (args, error);
+      success = egg_desktop_file_launchv (app_desktop_file, documents,
+					  args, error);
+      va_end (args);
+
+      egg_desktop_file_free (app_desktop_file);
+      free_document_list (documents);
+      break;
+
+    default:
+      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
+		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+		   _("Not a launchable item"));
+      success = FALSE;
+      break;
+    }
+
+  return success;
+}
+
+
+GQuark
+egg_desktop_file_error_quark (void)
+{
+  return g_quark_from_static_string ("egg-desktop_file-error-quark");
+}
+
+
+G_LOCK_DEFINE_STATIC (egg_desktop_file);
+static EggDesktopFile *egg_desktop_file;
+
+/**
+ * egg_set_desktop_file:
+ * @desktop_file_path: path to the application's desktop file
+ *
+ * Creates an #EggDesktopFile for the application from the data at
+ * @desktop_file_path. This will also call g_set_application_name()
+ * with the localized application name from the desktop file, and
+ * gtk_window_set_default_icon_name() or
+ * gtk_window_set_default_icon_from_file() with the application's
+ * icon. Other code may use additional information from the desktop
+ * file.
+ *
+ * Note that for thread safety reasons, this function can only
+ * be called once.
+ **/
+void
+egg_set_desktop_file (const char *desktop_file_path)
+{
+  GError *error = NULL;
+
+  G_LOCK (egg_desktop_file);
+  if (egg_desktop_file)
+    egg_desktop_file_free (egg_desktop_file);
+
+  egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error);
+  if (error)
+    {
+      g_warning ("Could not load desktop file '%s': %s",
+		 desktop_file_path, error->message);
+      g_error_free (error);
+    }
+
+  if (egg_desktop_file) {
+    /* Set localized application name and default window icon */
+    if (egg_desktop_file->name)
+      g_set_application_name (egg_desktop_file->name);
+    if (egg_desktop_file->icon)
+      {
+        if (g_path_is_absolute (egg_desktop_file->icon))
+          gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
+        else
+          gtk_window_set_default_icon_name (egg_desktop_file->icon);
+      }
+  }
+
+  G_UNLOCK (egg_desktop_file);
+}
+
+/**
+ * egg_get_desktop_file:
+ * 
+ * Gets the application's #EggDesktopFile, as set by
+ * egg_set_desktop_file().
+ * 
+ * Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
+ **/
+EggDesktopFile *
+egg_get_desktop_file (void)
+{
+  EggDesktopFile *retval;
+
+  G_LOCK (egg_desktop_file);
+  retval = egg_desktop_file;
+  G_UNLOCK (egg_desktop_file);
+
+  return retval;
+}
diff --git a/copy-n-paste/eggdesktopfile.h b/copy-n-paste/eggdesktopfile.h
new file mode 100644
index 0000000..f8a3d3e
--- /dev/null
+++ b/copy-n-paste/eggdesktopfile.h
@@ -0,0 +1,159 @@
+/* eggdesktopfile.h - Freedesktop.Org Desktop Files
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * 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; 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 __EGG_DESKTOP_FILE_H__
+#define __EGG_DESKTOP_FILE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct EggDesktopFile EggDesktopFile;
+
+typedef enum {
+	EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED,
+
+	EGG_DESKTOP_FILE_TYPE_APPLICATION,
+	EGG_DESKTOP_FILE_TYPE_LINK,
+	EGG_DESKTOP_FILE_TYPE_DIRECTORY
+} EggDesktopFileType;
+
+EggDesktopFile     *egg_desktop_file_new                (const char   *desktop_file_path,
+							 GError      **error);
+
+EggDesktopFile     *egg_desktop_file_new_from_data_dirs (const char   *desktop_file_path,
+							 GError      **error);
+EggDesktopFile     *egg_desktop_file_new_from_dirs      (const char   *desktop_file_path,
+							 const char  **search_dirs,
+							 GError      **error);
+EggDesktopFile     *egg_desktop_file_new_from_key_file  (GKeyFile     *key_file,
+							 const char   *source,
+							 GError      **error);
+
+void                egg_desktop_file_free               (EggDesktopFile  *desktop_file);
+
+const char         *egg_desktop_file_get_source         (EggDesktopFile  *desktop_file);
+
+EggDesktopFileType  egg_desktop_file_get_desktop_file_type (EggDesktopFile  *desktop_file);
+
+const char         *egg_desktop_file_get_name           (EggDesktopFile  *desktop_file);
+const char         *egg_desktop_file_get_icon           (EggDesktopFile  *desktop_file);
+
+gboolean            egg_desktop_file_can_launch         (EggDesktopFile  *desktop_file,
+							 const char      *desktop_environment);
+
+gboolean            egg_desktop_file_accepts_documents  (EggDesktopFile  *desktop_file);
+gboolean            egg_desktop_file_accepts_multiple   (EggDesktopFile  *desktop_file);
+gboolean            egg_desktop_file_accepts_uris       (EggDesktopFile  *desktop_file);
+
+char               *egg_desktop_file_parse_exec         (EggDesktopFile  *desktop_file,
+							 GSList          *documents,
+							 GError         **error);
+
+gboolean            egg_desktop_file_launch             (EggDesktopFile  *desktop_file,
+							 GSList          *documents,
+							 GError         **error,
+							 ...) G_GNUC_NULL_TERMINATED;
+
+typedef enum {
+	EGG_DESKTOP_FILE_LAUNCH_CLEARENV = 1,
+	EGG_DESKTOP_FILE_LAUNCH_PUTENV,
+	EGG_DESKTOP_FILE_LAUNCH_SCREEN,
+	EGG_DESKTOP_FILE_LAUNCH_WORKSPACE,
+	EGG_DESKTOP_FILE_LAUNCH_DIRECTORY,
+	EGG_DESKTOP_FILE_LAUNCH_TIME,
+	EGG_DESKTOP_FILE_LAUNCH_FLAGS,
+	EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_PID,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE,
+	EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID
+} EggDesktopFileLaunchOption;
+
+/* Standard Keys */
+#define EGG_DESKTOP_FILE_GROUP			"Desktop Entry"
+
+#define EGG_DESKTOP_FILE_KEY_TYPE		"Type"
+#define EGG_DESKTOP_FILE_KEY_VERSION		"Version"
+#define EGG_DESKTOP_FILE_KEY_NAME		"Name"
+#define EGG_DESKTOP_FILE_KEY_GENERIC_NAME	"GenericName"
+#define EGG_DESKTOP_FILE_KEY_NO_DISPLAY		"NoDisplay"
+#define EGG_DESKTOP_FILE_KEY_COMMENT		"Comment"
+#define EGG_DESKTOP_FILE_KEY_ICON		"Icon"
+#define EGG_DESKTOP_FILE_KEY_HIDDEN		"Hidden"
+#define EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN	"OnlyShowIn"
+#define EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN	"NotShowIn"
+#define EGG_DESKTOP_FILE_KEY_TRY_EXEC		"TryExec"
+#define EGG_DESKTOP_FILE_KEY_EXEC		"Exec"
+#define EGG_DESKTOP_FILE_KEY_PATH		"Path"
+#define EGG_DESKTOP_FILE_KEY_TERMINAL		"Terminal"
+#define EGG_DESKTOP_FILE_KEY_MIME_TYPE		"MimeType"
+#define EGG_DESKTOP_FILE_KEY_CATEGORIES		"Categories"
+#define EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY	"StartupNotify"
+#define EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS	"StartupWMClass"
+#define EGG_DESKTOP_FILE_KEY_URL		"URL"
+
+/* Accessors */
+gboolean  egg_desktop_file_has_key                (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error);
+char     *egg_desktop_file_get_string             (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error) G_GNUC_MALLOC;
+char     *egg_desktop_file_get_locale_string      (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   const char      *locale,
+						   GError         **error) G_GNUC_MALLOC;
+gboolean  egg_desktop_file_get_boolean            (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error);
+double    egg_desktop_file_get_numeric            (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   GError         **error);
+char    **egg_desktop_file_get_string_list        (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   gsize           *length,
+						   GError         **error) G_GNUC_MALLOC;
+char    **egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
+						   const char      *key,
+						   const char      *locale,
+						   gsize           *length,
+						   GError         **error) G_GNUC_MALLOC;
+
+
+/* Errors */
+#define EGG_DESKTOP_FILE_ERROR egg_desktop_file_error_quark()
+
+GQuark egg_desktop_file_error_quark (void);
+
+typedef enum {
+	EGG_DESKTOP_FILE_ERROR_INVALID,
+	EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
+	EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION
+} EggDesktopFileError;
+
+/* Global application desktop file */
+void            egg_set_desktop_file (const char *desktop_file_path);
+EggDesktopFile *egg_get_desktop_file (void);
+
+
+G_END_DECLS
+
+#endif /* __EGG_DESKTOP_FILE_H__ */
diff --git a/copy-n-paste/eggsmclient-private.h b/copy-n-paste/eggsmclient-private.h
new file mode 100644
index 0000000..ccb10bf
--- /dev/null
+++ b/copy-n-paste/eggsmclient-private.h
@@ -0,0 +1,53 @@
+/* eggsmclient-private.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SM_CLIENT_PRIVATE_H__
+#define __EGG_SM_CLIENT_PRIVATE_H__
+
+#include <gdkconfig.h>
+#include "eggsmclient.h"
+
+G_BEGIN_DECLS
+
+GKeyFile *egg_sm_client_save_state     (EggSMClient *client);
+void      egg_sm_client_quit_requested (EggSMClient *client);
+void      egg_sm_client_quit_cancelled (EggSMClient *client);
+void      egg_sm_client_quit           (EggSMClient *client);
+
+#if defined (GDK_WINDOWING_X11)
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+GType        egg_sm_client_xsmp_get_type (void);
+EggSMClient *egg_sm_client_xsmp_new      (void);
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+GType        egg_sm_client_dbus_get_type (void);
+EggSMClient *egg_sm_client_dbus_new      (void);
+# endif
+#elif defined (GDK_WINDOWING_WIN32)
+GType        egg_sm_client_win32_get_type (void);
+EggSMClient *egg_sm_client_win32_new      (void);
+#elif defined (GDK_WINDOWING_QUARTZ)
+GType        egg_sm_client_osx_get_type (void);
+EggSMClient *egg_sm_client_osx_new      (void);
+#endif
+
+G_END_DECLS
+
+
+#endif /* __EGG_SM_CLIENT_PRIVATE_H__ */
diff --git a/copy-n-paste/eggsmclient-xsmp.c b/copy-n-paste/eggsmclient-xsmp.c
new file mode 100644
index 0000000..1a56156
--- /dev/null
+++ b/copy-n-paste/eggsmclient-xsmp.c
@@ -0,0 +1,1370 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * Inspired by various other pieces of code including GsmClient (C)
+ * 2001 Havoc Pennington, GnomeClient (C) 1998 Carsten Schaar, and twm
+ * session code (C) 1998 The Open Group.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+#include "eggdesktopfile.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <X11/SM/SMlib.h>
+
+#include <gdk/gdk.h>
+
+#define EGG_TYPE_SM_CLIENT_XSMP            (egg_sm_client_xsmp_get_type ())
+#define EGG_SM_CLIENT_XSMP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMP))
+#define EGG_SM_CLIENT_XSMP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
+#define EGG_IS_SM_CLIENT_XSMP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT_XSMP))
+#define EGG_IS_SM_CLIENT_XSMP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT_XSMP))
+#define EGG_SM_CLIENT_XSMP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT_XSMP, EggSMClientXSMPClass))
+
+typedef struct _EggSMClientXSMP        EggSMClientXSMP;
+typedef struct _EggSMClientXSMPClass   EggSMClientXSMPClass;
+
+/* These mostly correspond to the similarly-named states in section
+ * 9.1 of the XSMP spec. Some of the states there aren't represented
+ * here, because we don't need them. SHUTDOWN_CANCELLED is slightly
+ * different from the spec; we use it when the client is IDLE after a
+ * ShutdownCancelled message, but the application is still interacting
+ * and doesn't know the shutdown has been cancelled yet.
+ */
+typedef enum
+{
+  XSMP_STATE_IDLE,
+  XSMP_STATE_SAVE_YOURSELF,
+  XSMP_STATE_INTERACT_REQUEST,
+  XSMP_STATE_INTERACT,
+  XSMP_STATE_SAVE_YOURSELF_DONE,
+  XSMP_STATE_SHUTDOWN_CANCELLED,
+  XSMP_STATE_CONNECTION_CLOSED
+} EggSMClientXSMPState;
+
+static const char *state_names[] = {
+  "idle",
+  "save-yourself",
+  "interact-request",
+  "interact",
+  "save-yourself-done",
+  "shutdown-cancelled",
+  "connection-closed"
+};
+
+#define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state])
+
+struct _EggSMClientXSMP
+{
+  EggSMClient parent;
+
+  SmcConn connection;
+  char *client_id;
+
+  EggSMClientXSMPState state;
+  char **restart_command;
+  gboolean set_restart_command;
+  int restart_style;
+
+  guint idle;
+
+  /* Current SaveYourself state */
+  guint expecting_initial_save_yourself : 1;
+  guint need_save_state : 1;
+  guint need_quit_requested : 1;
+  guint interact_errors : 1;
+  guint shutting_down : 1;
+
+  /* Todo list */
+  guint waiting_to_set_initial_properties : 1;
+  guint waiting_to_emit_quit : 1;
+  guint waiting_to_emit_quit_cancelled : 1;
+  guint waiting_to_save_myself : 1;
+
+};
+
+struct _EggSMClientXSMPClass
+{
+  EggSMClientClass parent_class;
+
+};
+
+static void     sm_client_xsmp_startup (EggSMClient *client,
+					const char  *client_id);
+static void     sm_client_xsmp_set_restart_command (EggSMClient  *client,
+						    int           argc,
+						    const char  **argv);
+static void     sm_client_xsmp_will_quit (EggSMClient *client,
+					  gboolean     will_quit);
+static gboolean sm_client_xsmp_end_session (EggSMClient         *client,
+					    EggSMClientEndStyle  style,
+					    gboolean  request_confirmation);
+
+static void xsmp_save_yourself      (SmcConn   smc_conn,
+				     SmPointer client_data,
+				     int       save_style,
+				     Bool      shutdown,
+				     int       interact_style,
+				     Bool      fast);
+static void xsmp_die                (SmcConn   smc_conn,
+				     SmPointer client_data);
+static void xsmp_save_complete      (SmcConn   smc_conn,
+				     SmPointer client_data);
+static void xsmp_shutdown_cancelled (SmcConn   smc_conn,
+				     SmPointer client_data);
+static void xsmp_interact           (SmcConn   smc_conn,
+				     SmPointer client_data);
+
+static SmProp *array_prop        (const char    *name,
+				  ...);
+static SmProp *ptrarray_prop     (const char    *name,
+				  GPtrArray     *values);
+static SmProp *string_prop       (const char    *name,
+				  const char    *value);
+static SmProp *card8_prop        (const char    *name,
+				  unsigned char  value);
+
+static void set_properties         (EggSMClientXSMP *xsmp, ...);
+static void delete_properties      (EggSMClientXSMP *xsmp, ...);
+
+static GPtrArray *generate_command (char       **restart_command,
+				    const char  *client_id,
+				    const char  *state_file);
+
+static void save_state            (EggSMClientXSMP *xsmp);
+static void do_save_yourself      (EggSMClientXSMP *xsmp);
+static void update_pending_events (EggSMClientXSMP *xsmp);
+
+static void     ice_init             (void);
+static gboolean process_ice_messages (IceConn       ice_conn);
+static void     smc_error_handler    (SmcConn       smc_conn,
+				      Bool          swap,
+				      int           offending_minor_opcode,
+				      unsigned long offending_sequence,
+				      int           error_class,
+				      int           severity,
+				      SmPointer     values);
+
+G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT)
+
+static void
+egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp)
+{
+  xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+  xsmp->connection = NULL;
+  xsmp->restart_style = SmRestartIfRunning;
+}
+
+static void
+egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass)
+{
+  EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass);
+
+  sm_client_class->startup             = sm_client_xsmp_startup;
+  sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command;
+  sm_client_class->will_quit           = sm_client_xsmp_will_quit;
+  sm_client_class->end_session         = sm_client_xsmp_end_session;
+}
+
+EggSMClient *
+egg_sm_client_xsmp_new (void)
+{
+  if (!g_getenv ("SESSION_MANAGER"))
+    return NULL;
+
+  return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL);
+}
+
+static gboolean
+sm_client_xsmp_set_initial_properties (gpointer user_data)
+{
+  EggSMClientXSMP *xsmp = user_data;
+  EggDesktopFile *desktop_file;
+  GPtrArray *clone, *restart;
+  char pid_str[64];
+
+  if (xsmp->idle)
+    {
+      g_source_remove (xsmp->idle);
+      xsmp->idle = 0;
+    }
+  xsmp->waiting_to_set_initial_properties = FALSE;
+
+  if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART)
+    xsmp->restart_style = SmRestartNever;
+
+  /* Parse info out of desktop file */
+  desktop_file = egg_get_desktop_file ();
+  if (desktop_file)
+    {
+      GError *err = NULL;
+      char *cmdline, **argv;
+      int argc;
+
+      if (xsmp->restart_style == SmRestartIfRunning)
+	{
+	  if (egg_desktop_file_get_boolean (desktop_file, 
+					    "X-GNOME-AutoRestart", NULL))
+	    xsmp->restart_style = SmRestartImmediately;
+	}
+
+      if (!xsmp->set_restart_command)
+	{
+	  cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err);
+	  if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err))
+	    {
+	      egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp),
+						 argc, (const char **)argv);
+	      g_strfreev (argv);
+	    }
+	  else
+	    {
+	      g_warning ("Could not parse Exec line in desktop file: %s",
+			 err->message);
+	      g_error_free (err);
+	    }
+	  g_free (cmdline);
+	}
+    }
+
+  if (!xsmp->set_restart_command)
+    xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1);
+
+  clone = generate_command (xsmp->restart_command, NULL, NULL);
+  restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+
+  g_debug ("Setting initial properties");
+
+  /* Program, CloneCommand, RestartCommand, and UserID are required.
+   * ProcessID isn't required, but the SM may be able to do something
+   * useful with it.
+   */
+  g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ());
+  set_properties (xsmp,
+		  string_prop   (SmProgram, g_get_prgname ()),
+		  ptrarray_prop (SmCloneCommand, clone),
+		  ptrarray_prop (SmRestartCommand, restart),
+		  string_prop   (SmUserID, g_get_user_name ()),
+		  string_prop   (SmProcessID, pid_str),
+		  card8_prop    (SmRestartStyleHint, xsmp->restart_style),
+		  NULL);
+  g_ptr_array_free (clone, TRUE);
+  g_ptr_array_free (restart, TRUE);
+
+  if (desktop_file)
+    {
+      set_properties (xsmp,
+		      string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)),
+		      NULL);
+    }
+
+  update_pending_events (xsmp);
+  return FALSE;
+}
+
+/* This gets called from two different places: xsmp_die() (when the
+ * server asks us to disconnect) and process_ice_messages() (when the
+ * server disconnects unexpectedly).
+ */
+static void
+sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp)
+{
+  SmcConn connection;
+
+  if (!xsmp->connection)
+    return;
+
+  g_debug ("Disconnecting");
+
+  connection = xsmp->connection;
+  xsmp->connection = NULL;
+  SmcCloseConnection (connection, 0, NULL);
+  xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+
+  xsmp->waiting_to_save_myself = FALSE;
+  update_pending_events (xsmp);
+}
+
+static void
+sm_client_xsmp_startup (EggSMClient *client,
+			const char  *client_id)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+  SmcCallbacks callbacks;
+  char *ret_client_id;
+  char error_string_ret[256];
+
+  xsmp->client_id = g_strdup (client_id);
+
+  ice_init ();
+  SmcSetErrorHandler (smc_error_handler);
+
+  callbacks.save_yourself.callback      = xsmp_save_yourself;
+  callbacks.die.callback                = xsmp_die;
+  callbacks.save_complete.callback      = xsmp_save_complete;
+  callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled;
+
+  callbacks.save_yourself.client_data      = xsmp;
+  callbacks.die.client_data                = xsmp;
+  callbacks.save_complete.client_data      = xsmp;
+  callbacks.shutdown_cancelled.client_data = xsmp;
+
+  client_id = NULL;
+  error_string_ret[0] = '\0';
+  xsmp->connection =
+    SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor,
+		       SmcSaveYourselfProcMask | SmcDieProcMask |
+		       SmcSaveCompleteProcMask |
+		       SmcShutdownCancelledProcMask,
+		       &callbacks,
+		       xsmp->client_id, &ret_client_id,
+		       sizeof (error_string_ret), error_string_ret);
+
+  if (!xsmp->connection)
+    {
+      g_warning ("Failed to connect to the session manager: %s\n",
+		 error_string_ret[0] ?
+		 error_string_ret : "no error message given");
+      xsmp->state = XSMP_STATE_CONNECTION_CLOSED;
+      return;
+    }
+
+  /* We expect a pointless initial SaveYourself if either (a) we
+   * didn't have an initial client ID, or (b) we DID have an initial
+   * client ID, but the server rejected it and gave us a new one.
+   */
+  if (!xsmp->client_id ||
+      (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0))
+    xsmp->expecting_initial_save_yourself = TRUE;
+
+  if (ret_client_id)
+    {
+      g_free (xsmp->client_id);
+      xsmp->client_id = g_strdup (ret_client_id);
+      free (ret_client_id);
+
+      gdk_threads_enter ();
+      gdk_set_sm_client_id (xsmp->client_id);
+      gdk_threads_leave ();
+
+      g_debug ("Got client ID \"%s\"", xsmp->client_id);
+    }
+
+  xsmp->state = XSMP_STATE_IDLE;
+
+  /* Do not set the initial properties until we reach the main loop,
+   * so that the application has a chance to call
+   * egg_set_desktop_file(). (This may also help the session manager
+   * have a better idea of when the application is fully up and
+   * running.)
+   */
+  xsmp->waiting_to_set_initial_properties = TRUE;
+  xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client);
+}
+
+static void
+sm_client_xsmp_set_restart_command (EggSMClient  *client,
+				    int           argc,
+				    const char  **argv)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+  int i;
+
+  g_strfreev (xsmp->restart_command);
+
+  xsmp->restart_command = g_new (char *, argc + 1);
+  for (i = 0; i < argc; i++)
+    xsmp->restart_command[i] = g_strdup (argv[i]);
+  xsmp->restart_command[i] = NULL;
+
+  xsmp->set_restart_command = TRUE;
+}
+
+static void
+sm_client_xsmp_will_quit (EggSMClient *client,
+			  gboolean     will_quit)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+
+  if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED)
+    {
+      /* The session manager has already exited! Schedule a quit
+       * signal.
+       */
+      xsmp->waiting_to_emit_quit = TRUE;
+      update_pending_events (xsmp);
+      return;
+    }
+  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      /* We received a ShutdownCancelled message while the application
+       * was interacting; Schedule a quit_cancelled signal.
+       */
+      xsmp->waiting_to_emit_quit_cancelled = TRUE;
+      update_pending_events (xsmp);
+      return;
+    }
+
+  g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT);
+
+  g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True");
+  SmcInteractDone (xsmp->connection, !will_quit);
+
+  if (will_quit && xsmp->need_save_state)
+    save_state (xsmp);
+
+  g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False");
+  SmcSaveYourselfDone (xsmp->connection, will_quit);
+  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+}
+
+static gboolean
+sm_client_xsmp_end_session (EggSMClient         *client,
+			    EggSMClientEndStyle  style,
+			    gboolean             request_confirmation)
+{
+  EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client;
+  int save_type;
+
+  /* To end the session via XSMP, we have to send a
+   * SaveYourselfRequest. We aren't allowed to do that if anything
+   * else is going on, but we don't want to expose this fact to the
+   * application. So we do our best to patch things up here...
+   *
+   * In the worst case, this method might block for some length of
+   * time in process_ice_messages, but the only time that code path is
+   * honestly likely to get hit is if the application tries to end the
+   * session as the very first thing it does, in which case it
+   * probably won't actually block anyway. It's not worth gunking up
+   * the API to try to deal nicely with the other 0.01% of cases where
+   * this happens.
+   */
+
+  while (xsmp->state != XSMP_STATE_IDLE ||
+	 xsmp->expecting_initial_save_yourself)
+    {
+      /* If we're already shutting down, we don't need to do anything. */
+      if (xsmp->shutting_down)
+	return TRUE;
+
+      switch (xsmp->state)
+	{
+	case XSMP_STATE_CONNECTION_CLOSED:
+	  return FALSE;
+
+	case XSMP_STATE_SAVE_YOURSELF:
+	  /* Trying to log out from the save_state callback? Whatever.
+	   * Abort the save_state.
+	   */
+	  SmcSaveYourselfDone (xsmp->connection, FALSE);
+	  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+	  break;
+
+	case XSMP_STATE_INTERACT_REQUEST:
+	case XSMP_STATE_INTERACT:
+	case XSMP_STATE_SHUTDOWN_CANCELLED:
+	  /* Already in a shutdown-related state, just ignore
+	   * the new shutdown request...
+	   */
+	  return TRUE;
+
+	case XSMP_STATE_IDLE:
+	  if (xsmp->waiting_to_set_initial_properties)
+	    sm_client_xsmp_set_initial_properties (xsmp);
+
+	  if (!xsmp->expecting_initial_save_yourself)
+	    break;
+	  /* else fall through */
+
+	case XSMP_STATE_SAVE_YOURSELF_DONE:
+	  /* We need to wait for some response from the server.*/
+	  process_ice_messages (SmcGetIceConnection (xsmp->connection));
+	  break;
+
+	default:
+	  /* Hm... shouldn't happen */
+	  return FALSE;
+	}
+    }
+
+  /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and
+   * the user chooses to save the session. But gnome-session will do
+   * the wrong thing if we pass SmSaveBoth and the user chooses NOT to
+   * save the session... Sigh.
+   */
+  if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session"))
+    save_type = SmSaveBoth;
+  else
+    save_type = SmSaveGlobal;
+
+  g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : "");
+  SmcRequestSaveYourself (xsmp->connection,
+			  save_type,
+			  True, /* shutdown */
+			  SmInteractStyleAny,
+			  !request_confirmation, /* fast */
+			  True /* global */);
+  return TRUE;
+}
+
+static gboolean
+idle_do_pending_events (gpointer data)
+{
+  EggSMClientXSMP *xsmp = data;
+  EggSMClient *client = data;
+
+  gdk_threads_enter ();
+
+  xsmp->idle = 0;
+
+  if (xsmp->waiting_to_emit_quit)
+    {
+      xsmp->waiting_to_emit_quit = FALSE;
+      egg_sm_client_quit (client);
+      goto out;
+    }
+
+  if (xsmp->waiting_to_emit_quit_cancelled)
+    {
+      xsmp->waiting_to_emit_quit_cancelled = FALSE;
+      egg_sm_client_quit_cancelled (client);
+      xsmp->state = XSMP_STATE_IDLE;
+    }
+
+  if (xsmp->waiting_to_save_myself)
+    {
+      xsmp->waiting_to_save_myself = FALSE;
+      do_save_yourself (xsmp);
+    }
+
+ out:
+  gdk_threads_leave ();
+  return FALSE;
+}
+
+static void
+update_pending_events (EggSMClientXSMP *xsmp)
+{
+  gboolean want_idle =
+    xsmp->waiting_to_emit_quit ||
+    xsmp->waiting_to_emit_quit_cancelled ||
+    xsmp->waiting_to_save_myself;
+
+  if (want_idle)
+    {
+      if (xsmp->idle == 0)
+	xsmp->idle = g_idle_add (idle_do_pending_events, xsmp);
+    }
+  else
+    {
+      if (xsmp->idle != 0)
+	g_source_remove (xsmp->idle);
+      xsmp->idle = 0;
+    }
+}
+
+static void
+fix_broken_state (EggSMClientXSMP *xsmp, const char *message,
+		  gboolean send_interact_done,
+		  gboolean send_save_yourself_done)
+{
+  g_warning ("Received XSMP %s message in state %s: client or server error",
+	     message, EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  /* Forget any pending SaveYourself plans we had */
+  xsmp->waiting_to_save_myself = FALSE;
+  update_pending_events (xsmp);
+
+  if (send_interact_done)
+    SmcInteractDone (xsmp->connection, False);
+  if (send_save_yourself_done)
+    SmcSaveYourselfDone (xsmp->connection, True);
+
+  xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE;
+}
+
+/* SM callbacks */
+
+static void
+xsmp_save_yourself (SmcConn   smc_conn,
+		    SmPointer client_data,
+		    int       save_type,
+		    Bool      shutdown,
+		    int       interact_style,
+		    Bool      fast)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  gboolean wants_quit_requested;
+
+  g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s",
+	   save_type == SmSaveLocal ? "SmSaveLocal" :
+	   save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth",
+	   shutdown ? "Shutdown" : "!Shutdown",
+	   interact_style == SmInteractStyleAny ? "SmInteractStyleAny" :
+	   interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" :
+	   "SmInteractStyleNone", fast ? "Fast" : "!Fast",
+	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  if (xsmp->state != XSMP_STATE_IDLE &&
+      xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE);
+      return;
+    }
+
+  if (xsmp->waiting_to_set_initial_properties)
+    sm_client_xsmp_set_initial_properties (xsmp);
+
+  /* If this is the initial SaveYourself, ignore it; we've already set
+   * properties and there's no reason to actually save state too.
+   */
+  if (xsmp->expecting_initial_save_yourself)
+    {
+      xsmp->expecting_initial_save_yourself = FALSE;
+
+      if (save_type == SmSaveLocal &&
+	  interact_style == SmInteractStyleNone &&
+	  !shutdown && !fast)
+	{
+	  g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself");
+	  SmcSaveYourselfDone (xsmp->connection, True);
+	  /* As explained in the comment at the end of
+	   * do_save_yourself(), SAVE_YOURSELF_DONE is the correct
+	   * state here, not IDLE.
+	   */
+	  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+	  return;
+	}
+      else
+	g_warning ("First SaveYourself was not the expected one!");
+    }
+
+  /* Even ignoring the "fast" flag completely, there are still 18
+   * different combinations of save_type, shutdown and interact_style.
+   * We interpret them as follows:
+   *
+   *   Type  Shutdown  Interact	 Interpretation
+   *     G      F       A/E/N  	 do nothing (1)
+   *     G      T         N    	 do nothing (1)*
+   *     G      T        A/E   	 quit_requested (2)
+   *    L/B     F       A/E/N  	 save_state (3)
+   *    L/B     T         N    	 save_state (3)*
+   *    L/B     T        A/E   	 quit_requested, then save_state (4)
+   *
+   *   1. Do nothing, because the SM asked us to do something
+   *      uninteresting (save open files, but then don't quit
+   *      afterward) or rude (save open files without asking the user
+   *      for confirmation).
+   *
+   *   2. Request interaction and then emit ::quit_requested. This
+   *      perhaps isn't quite correct for the SmInteractStyleErrors
+   *      case, but we don't care.
+   *
+   *   3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these
+   *      rows essentially get demoted to SmSaveLocal, because their
+   *      Global halves correspond to "do nothing".
+   *
+   *   4. Request interaction, emit ::quit_requested, and then emit
+   *      ::save_state after interacting. This is the SmSaveBoth
+   *      equivalent of #2, but we also promote SmSaveLocal shutdown
+   *      SaveYourselfs to SmSaveBoth here, because we want to give
+   *      the user a chance to save open files before quitting.
+   *
+   * (* It would be nice if we could do something useful when the
+   * session manager sends a SaveYourself with shutdown True and
+   * SmInteractStyleNone. But we can't, so we just pretend it didn't
+   * even tell us it was shutting down. The docs for ::quit mention
+   * that it might not always be preceded by ::quit_requested.)
+   */
+
+  /* As an optimization, we don't actually request interaction and
+   * emit ::quit_requested if the application isn't listening to the
+   * signal.
+   */
+  wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE);
+
+  xsmp->need_save_state     = (save_type != SmSaveGlobal);
+  xsmp->need_quit_requested = (shutdown && wants_quit_requested &&
+			       interact_style != SmInteractStyleNone);
+  xsmp->interact_errors     = (interact_style == SmInteractStyleErrors);
+
+  xsmp->shutting_down       = shutdown;
+
+  do_save_yourself (xsmp);
+}
+
+static void
+do_save_yourself (EggSMClientXSMP *xsmp)
+{
+  if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      /* The SM cancelled a previous SaveYourself, but we haven't yet
+       * had a chance to tell the application, so we can't start
+       * processing this SaveYourself yet.
+       */
+      xsmp->waiting_to_save_myself = TRUE;
+      update_pending_events (xsmp);
+      return;
+    }
+
+  if (xsmp->need_quit_requested)
+    {
+      xsmp->state = XSMP_STATE_INTERACT_REQUEST;
+
+      g_debug ("Sending InteractRequest(%s)",
+	       xsmp->interact_errors ? "Error" : "Normal");
+      SmcInteractRequest (xsmp->connection,
+			  xsmp->interact_errors ? SmDialogError : SmDialogNormal,
+			  xsmp_interact,
+			  xsmp);
+      return;
+    }
+
+  if (xsmp->need_save_state)
+    {
+      save_state (xsmp);
+
+      /* Though unlikely, the client could have been disconnected
+       * while the application was saving its state.
+       */
+      if (!xsmp->connection)
+	 return;
+    }
+
+  g_debug ("Sending SaveYourselfDone(True)");
+  SmcSaveYourselfDone (xsmp->connection, True);
+
+  /* The client state diagram in the XSMP spec says that after a
+   * non-shutdown SaveYourself, we go directly back to "idle". But
+   * everything else in both the XSMP spec and the libSM docs
+   * disagrees.
+   */
+  xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE;
+}
+
+static void
+save_state (EggSMClientXSMP *xsmp)
+{
+  GKeyFile *state_file;
+  char *state_file_path, *data;
+  EggDesktopFile *desktop_file;
+  GPtrArray *restart;
+  int offset, fd;
+
+  /* We set xsmp->state before emitting save_state, but our caller is
+   * responsible for setting it back afterward.
+   */
+  xsmp->state = XSMP_STATE_SAVE_YOURSELF;
+
+  state_file = egg_sm_client_save_state ((EggSMClient *)xsmp);
+  if (!state_file)
+    {
+      restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL);
+      set_properties (xsmp,
+		      ptrarray_prop (SmRestartCommand, restart),
+		      NULL);
+      g_ptr_array_free (restart, TRUE);
+      delete_properties (xsmp, SmDiscardCommand, NULL);
+      return;
+    }
+
+  desktop_file = egg_get_desktop_file ();
+  if (desktop_file)
+    {
+      GKeyFile *merged_file;
+      char *desktop_file_path;
+
+      merged_file = g_key_file_new ();
+      desktop_file_path =
+	g_filename_from_uri (egg_desktop_file_get_source (desktop_file),
+			     NULL, NULL);
+      if (desktop_file_path &&
+	  g_key_file_load_from_file (merged_file, desktop_file_path,
+				     G_KEY_FILE_KEEP_COMMENTS |
+				     G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+	{
+	  guint g, k, i;
+	  char **groups, **keys, *value, *exec;
+
+	  groups = g_key_file_get_groups (state_file, NULL);
+	  for (g = 0; groups[g]; g++)
+	    {
+	      keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL);
+	      for (k = 0; keys[k]; k++)
+		{
+		  value = g_key_file_get_value (state_file, groups[g],
+						keys[k], NULL);
+		  if (value)
+		    {
+		      g_key_file_set_value (merged_file, groups[g],
+					    keys[k], value);
+		      g_free (value);
+		    }
+		}
+	      g_strfreev (keys);
+	    }
+	  g_strfreev (groups);
+
+	  g_key_file_free (state_file);
+	  state_file = merged_file;
+
+	  /* Update Exec key using "--sm-client-state-file %k" */
+	  restart = generate_command (xsmp->restart_command,
+				      NULL, "%k");
+	  for (i = 0; i < restart->len; i++)
+	    restart->pdata[i] = g_shell_quote (restart->pdata[i]);
+	  g_ptr_array_add (restart, NULL);
+	  exec = g_strjoinv (" ", (char **)restart->pdata);
+	  g_strfreev ((char **)restart->pdata);
+	  g_ptr_array_free (restart, FALSE);
+
+	  g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP,
+				 EGG_DESKTOP_FILE_KEY_EXEC,
+				 exec);
+	  g_free (exec);
+	}
+      else
+	desktop_file = NULL;
+
+      g_free (desktop_file_path);
+    }
+
+  /* Now write state_file to disk. (We can't use mktemp(), because
+   * that requires the filename to end with "XXXXXX", and we want
+   * it to end with ".desktop".)
+   */
+
+  data = g_key_file_to_data (state_file, NULL, NULL);
+  g_key_file_free (state_file);
+
+  offset = 0;
+  while (1)
+    {
+      state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s",
+					 g_get_user_config_dir (),
+					 G_DIR_SEPARATOR, G_DIR_SEPARATOR,
+					 g_get_prgname (),
+					 (long)time (NULL) + offset,
+					 desktop_file ? "desktop" : "state");
+
+      fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644);
+      if (fd == -1)
+	{
+	  if (errno == EEXIST)
+	    {
+	      offset++;
+	      g_free (state_file_path);
+	      continue;
+	    }
+	  else if (errno == ENOTDIR || errno == ENOENT)
+	    {
+	      char *sep = strrchr (state_file_path, G_DIR_SEPARATOR);
+
+	      *sep = '\0';
+	      if (g_mkdir_with_parents (state_file_path, 0755) != 0)
+		{
+		  g_warning ("Could not create directory '%s'",
+			     state_file_path);
+		  g_free (state_file_path);
+		  state_file_path = NULL;
+		  break;
+		}
+
+	      continue;
+	    }
+
+	  g_warning ("Could not create file '%s': %s",
+		     state_file_path, g_strerror (errno));
+	  g_free (state_file_path);
+	  state_file_path = NULL;
+	  break;
+	}
+
+      close (fd);
+      g_file_set_contents (state_file_path, data, -1, NULL);
+      break;
+    }
+  g_free (data);
+
+  restart = generate_command (xsmp->restart_command, xsmp->client_id,
+			      state_file_path);
+  set_properties (xsmp,
+		  ptrarray_prop (SmRestartCommand, restart),
+		  NULL);
+  g_ptr_array_free (restart, TRUE);
+
+  if (state_file_path)
+    {
+      set_properties (xsmp,
+		      array_prop (SmDiscardCommand,
+				  "/bin/rm", "-rf", state_file_path,
+				  NULL),
+		      NULL);
+      g_free (state_file_path);
+    }
+}
+
+static void
+xsmp_interact (SmcConn   smc_conn,
+	       SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  EggSMClient *client = client_data;
+
+  g_debug ("Received Interact message in state %s",
+	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  if (xsmp->state != XSMP_STATE_INTERACT_REQUEST)
+    {
+      fix_broken_state (xsmp, "Interact", TRUE, TRUE);
+      return;
+    }
+
+  xsmp->state = XSMP_STATE_INTERACT;
+  egg_sm_client_quit_requested (client);
+}
+
+static void
+xsmp_die (SmcConn   smc_conn,
+	  SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  EggSMClient *client = client_data;
+
+  g_debug ("Received Die message in state %s",
+	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  sm_client_xsmp_disconnect (xsmp);
+  egg_sm_client_quit (client);
+}
+
+static void
+xsmp_save_complete (SmcConn   smc_conn,
+		    SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+
+  g_debug ("Received SaveComplete message in state %s",
+	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+    xsmp->state = XSMP_STATE_IDLE;
+  else
+    fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE);
+}
+
+static void
+xsmp_shutdown_cancelled (SmcConn   smc_conn,
+			 SmPointer client_data)
+{
+  EggSMClientXSMP *xsmp = client_data;
+  EggSMClient *client = client_data;
+
+  g_debug ("Received ShutdownCancelled message in state %s",
+	   EGG_SM_CLIENT_XSMP_STATE (xsmp));
+
+  xsmp->shutting_down = FALSE;
+
+  if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE)
+    {
+      /* We've finished interacting and now the SM has agreed to
+       * cancel the shutdown.
+       */
+      xsmp->state = XSMP_STATE_IDLE;
+      egg_sm_client_quit_cancelled (client);
+    }
+  else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED)
+    {
+      /* Hm... ok, so we got a shutdown SaveYourself, which got
+       * cancelled, but the application was still interacting, so we
+       * didn't tell it yet, and then *another* SaveYourself arrived,
+       * which we must still be waiting to tell the app about, except
+       * that now that SaveYourself has been cancelled too! Dizzy yet?
+       */
+      xsmp->waiting_to_save_myself = FALSE;
+      update_pending_events (xsmp);
+    }
+  else
+    {
+      g_debug ("Sending SaveYourselfDone(False)");
+      SmcSaveYourselfDone (xsmp->connection, False);
+
+      if (xsmp->state == XSMP_STATE_INTERACT)
+	{
+	  /* The application is currently interacting, so we can't
+	   * tell it about the cancellation yet; we will wait until
+	   * after it calls egg_sm_client_will_quit().
+	   */
+	  xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED;
+	}
+      else
+	{
+	  /* The shutdown was cancelled before the application got a
+	   * chance to interact.
+	   */
+	  xsmp->state = XSMP_STATE_IDLE;
+	}
+    }
+}
+
+/* Utilities */
+
+/* Create a restart/clone/Exec command based on @restart_command.
+ * If @client_id is non-%NULL, add "--sm-client-id @client_id".
+ * If @state_file is non-%NULL, add "--sm-client-state-file @state_file".
+ *
+ * None of the input strings are g_strdup()ed; the caller must keep
+ * them around until it is done with the returned GPtrArray, and must
+ * then free the array, but not its contents.
+ */
+static GPtrArray *
+generate_command (char **restart_command, const char *client_id,
+		  const char *state_file)
+{
+  GPtrArray *cmd;
+  int i;
+
+  cmd = g_ptr_array_new ();
+  g_ptr_array_add (cmd, restart_command[0]);
+
+  if (client_id)
+    {
+      g_ptr_array_add (cmd, "--sm-client-id");
+      g_ptr_array_add (cmd, (char *)client_id);
+    }
+
+  if (state_file)
+    {
+      g_ptr_array_add (cmd, "--sm-client-state-file");
+      g_ptr_array_add (cmd, (char *)state_file);
+    }
+
+  for (i = 1; restart_command[i]; i++)
+    g_ptr_array_add (cmd, restart_command[i]);
+
+  return cmd;
+}
+
+/* Takes a NULL-terminated list of SmProp * values, created by
+ * array_prop, ptrarray_prop, string_prop, card8_prop, sets them, and
+ * frees them.
+ */
+static void
+set_properties (EggSMClientXSMP *xsmp, ...)
+{
+  GPtrArray *props;
+  SmProp *prop;
+  va_list ap;
+  guint i;
+
+  props = g_ptr_array_new ();
+
+  va_start (ap, xsmp);
+  while ((prop = va_arg (ap, SmProp *)))
+    g_ptr_array_add (props, prop);
+  va_end (ap);
+
+  if (xsmp->connection)
+    {
+      SmcSetProperties (xsmp->connection, props->len,
+			(SmProp **)props->pdata);
+    }
+
+  for (i = 0; i < props->len; i++)
+    {
+      prop = props->pdata[i];
+      g_free (prop->vals);
+      g_free (prop);
+    }
+  g_ptr_array_free (props, TRUE);
+}
+
+/* Takes a NULL-terminated list of property names and deletes them. */
+static void
+delete_properties (EggSMClientXSMP *xsmp, ...)
+{
+  GPtrArray *props;
+  char *prop;
+  va_list ap;
+
+  if (!xsmp->connection)
+    return;
+
+  props = g_ptr_array_new ();
+
+  va_start (ap, xsmp);
+  while ((prop = va_arg (ap, char *)))
+    g_ptr_array_add (props, prop);
+  va_end (ap);
+
+  SmcDeleteProperties (xsmp->connection, props->len,
+		       (char **)props->pdata);
+
+  g_ptr_array_free (props, TRUE);
+}
+
+/* Takes an array of strings and creates a LISTofARRAY8 property. The
+ * strings are neither dupped nor freed; they need to remain valid
+ * until you're done with the SmProp.
+ */
+static SmProp *
+array_prop (const char *name, ...) 
+{
+  SmProp *prop;
+  SmPropValue pv;
+  GArray *vals;
+  char *value;
+  va_list ap;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = SmLISTofARRAY8;
+
+  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+  va_start (ap, name);
+  while ((value = va_arg (ap, char *)))
+    {
+      pv.length = strlen (value);
+      pv.value = value;
+      g_array_append_val (vals, pv);
+    }
+
+  prop->num_vals = vals->len;
+  prop->vals = (SmPropValue *)vals->data;
+
+  g_array_free (vals, FALSE);
+
+  return prop;
+}
+
+/* Takes a GPtrArray of strings and creates a LISTofARRAY8 property.
+ * The array contents are neither dupped nor freed; they need to
+ * remain valid until you're done with the SmProp.
+ */
+static SmProp *
+ptrarray_prop (const char *name, GPtrArray *values)
+{
+  SmProp *prop;
+  SmPropValue pv;
+  GArray *vals;
+  guint i;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = SmLISTofARRAY8;
+
+  vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue));
+
+  for (i = 0; i < values->len; i++)
+    {
+      pv.length = strlen (values->pdata[i]);
+      pv.value = values->pdata[i];
+      g_array_append_val (vals, pv);
+    }
+
+  prop->num_vals = vals->len;
+  prop->vals = (SmPropValue *)vals->data;
+
+  g_array_free (vals, FALSE);
+
+  return prop;
+}
+
+/* Takes a string and creates an ARRAY8 property. The string is
+ * neither dupped nor freed; it needs to remain valid until you're
+ * done with the SmProp.
+ */
+static SmProp *
+string_prop (const char *name, const char *value)
+{
+  SmProp *prop;
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = SmARRAY8;
+
+  prop->num_vals = 1;
+  prop->vals = g_new (SmPropValue, 1);
+
+  prop->vals[0].length = strlen (value);
+  prop->vals[0].value = (char *)value;
+
+  return prop;
+}
+
+/* Takes a char and creates a CARD8 property. */
+static SmProp *
+card8_prop (const char *name, unsigned char value)
+{
+  SmProp *prop;
+  char *card8val;
+
+  /* To avoid having to allocate and free prop->vals[0], we cheat and
+   * make vals a 2-element-long array and then use the second element
+   * to store value.
+   */
+
+  prop = g_new (SmProp, 1);
+  prop->name = (char *)name;
+  prop->type = SmCARD8;
+
+  prop->num_vals = 1;
+  prop->vals = g_new (SmPropValue, 2);
+  card8val = (char *)(&prop->vals[1]);
+  card8val[0] = value;
+
+  prop->vals[0].length = 1;
+  prop->vals[0].value = card8val;
+
+  return prop;
+}
+
+/* ICE code. This makes no effort to play nice with anyone else trying
+ * to use libICE. Fortunately, no one uses libICE for anything other
+ * than SM. (DCOP uses ICE, but it has its own private copy of
+ * libICE.)
+ *
+ * When this moves to gtk, it will need to be cleverer, to avoid
+ * tripping over old apps that use GnomeClient or that use libSM
+ * directly.
+ */
+
+#include <X11/ICE/ICElib.h>
+#include <fcntl.h>
+
+static void        ice_error_handler    (IceConn        ice_conn,
+					 Bool           swap,
+					 int            offending_minor_opcode,
+					 unsigned long  offending_sequence,
+					 int            error_class,
+					 int            severity,
+					 IcePointer     values);
+static void        ice_io_error_handler (IceConn        ice_conn);
+static void        ice_connection_watch (IceConn        ice_conn,
+					 IcePointer     client_data,
+					 Bool           opening,
+					 IcePointer    *watch_data);
+
+static void
+ice_init (void)
+{
+  IceSetIOErrorHandler (ice_io_error_handler);
+  IceSetErrorHandler (ice_error_handler);
+  IceAddConnectionWatch (ice_connection_watch, NULL);
+}
+
+static gboolean
+process_ice_messages (IceConn ice_conn)
+{
+  IceProcessMessagesStatus status;
+
+  gdk_threads_enter ();
+  status = IceProcessMessages (ice_conn, NULL, NULL);
+  gdk_threads_leave ();
+
+  switch (status)
+    {
+    case IceProcessMessagesSuccess:
+      return TRUE;
+
+    case IceProcessMessagesIOError:
+      sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn));
+      return FALSE;
+
+    case IceProcessMessagesConnectionClosed:
+      return FALSE;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static gboolean
+ice_iochannel_watch (GIOChannel   *channel,
+		     GIOCondition  condition,
+		     gpointer      client_data)
+{
+  return process_ice_messages (client_data);
+}
+
+static void
+ice_connection_watch (IceConn     ice_conn,
+		      IcePointer  client_data,
+		      Bool        opening,
+		      IcePointer *watch_data)
+{
+  guint watch_id;
+
+  if (opening)
+    {
+      GIOChannel *channel;
+      int fd = IceConnectionNumber (ice_conn);
+
+      fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC);
+      channel = g_io_channel_unix_new (fd);
+      watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR,
+				 ice_iochannel_watch, ice_conn);
+      g_io_channel_unref (channel);
+
+      *watch_data = GUINT_TO_POINTER (watch_id);
+    }
+  else
+    {
+      watch_id = GPOINTER_TO_UINT (*watch_data);
+      g_source_remove (watch_id);
+    }
+}
+
+static void
+ice_error_handler (IceConn       ice_conn,
+		   Bool          swap,
+		   int           offending_minor_opcode,
+		   unsigned long offending_sequence,
+		   int           error_class,
+		   int           severity,
+		   IcePointer    values)
+{
+  /* Do nothing */
+} 
+
+static void
+ice_io_error_handler (IceConn ice_conn)
+{
+  /* Do nothing */
+} 
+
+static void
+smc_error_handler (SmcConn       smc_conn,
+                   Bool          swap,
+                   int           offending_minor_opcode,
+                   unsigned long offending_sequence,
+                   int           error_class,
+                   int           severity,
+                   SmPointer     values)
+{
+  /* Do nothing */
+}
diff --git a/copy-n-paste/eggsmclient.c b/copy-n-paste/eggsmclient.c
new file mode 100644
index 0000000..efa901d
--- /dev/null
+++ b/copy-n-paste/eggsmclient.c
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "eggsmclient.h"
+#include "eggsmclient-private.h"
+
+static void egg_sm_client_debug_handler (const char *log_domain,
+					 GLogLevelFlags log_level,
+					 const char *message,
+					 gpointer user_data);
+
+enum {
+  SAVE_STATE,
+  QUIT_REQUESTED,
+  QUIT_CANCELLED,
+  QUIT,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct _EggSMClientPrivate {
+  GKeyFile *state_file;
+};
+
+#define EGG_SM_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_SM_CLIENT, EggSMClientPrivate))
+
+G_DEFINE_TYPE (EggSMClient, egg_sm_client, G_TYPE_OBJECT)
+
+static EggSMClient *global_client;
+static EggSMClientMode global_client_mode = EGG_SM_CLIENT_MODE_NORMAL;
+
+static void
+egg_sm_client_init (EggSMClient *client)
+{
+  ;
+}
+
+static void
+egg_sm_client_class_init (EggSMClientClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (EggSMClientPrivate));
+
+  /**
+   * EggSMClient::save_state:
+   * @client: the client
+   * @state_file: a #GKeyFile to save state information into
+   *
+   * Emitted when the session manager has requested that the
+   * application save information about its current state. The
+   * application should save its state into @state_file, and then the
+   * session manager may then restart the application in a future
+   * session and tell it to initialize itself from that state.
+   *
+   * You should not save any data into @state_file's "start group"
+   * (ie, the %NULL group). Instead, applications should save their
+   * data into groups with names that start with the application name,
+   * and libraries that connect to this signal should save their data
+   * into groups with names that start with the library name.
+   *
+   * Alternatively, rather than (or in addition to) using @state_file,
+   * the application can save its state by calling
+   * egg_sm_client_set_restart_command() during the processing of this
+   * signal (eg, to include a list of files to open).
+   **/
+  signals[SAVE_STATE] =
+    g_signal_new ("save_state",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, save_state),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__POINTER,
+                  G_TYPE_NONE,
+                  1, G_TYPE_POINTER);
+
+  /**
+   * EggSMClient::quit_requested:
+   * @client: the client
+   *
+   * Emitted when the session manager requests that the application
+   * exit (generally because the user is logging out). The application
+   * should decide whether or not it is willing to quit (perhaps after
+   * asking the user what to do with documents that have unsaved
+   * changes) and then call egg_sm_client_will_quit(), passing %TRUE
+   * or %FALSE to give its answer to the session manager. (It does not
+   * need to give an answer before returning from the signal handler;
+   * it can interact with the user asynchronously and then give its
+   * answer later on.) If the application does not connect to this
+   * signal, then #EggSMClient will automatically return %TRUE on its
+   * behalf.
+   *
+   * The application should not save its session state as part of
+   * handling this signal; if the user has requested that the session
+   * be saved when logging out, then ::save_state will be emitted
+   * separately.
+   * 
+   * If the application agrees to quit, it should then wait for either
+   * the ::quit_cancelled or ::quit signals to be emitted.
+   **/
+  signals[QUIT_REQUESTED] =
+    g_signal_new ("quit_requested",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit_requested),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
+  /**
+   * EggSMClient::quit_cancelled:
+   * @client: the client
+   *
+   * Emitted when the session manager decides to cancel a logout after
+   * the application has already agreed to quit. After receiving this
+   * signal, the application can go back to what it was doing before
+   * receiving the ::quit_requested signal.
+   **/
+  signals[QUIT_CANCELLED] =
+    g_signal_new ("quit_cancelled",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit_cancelled),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+
+  /**
+   * EggSMClient::quit:
+   * @client: the client
+   *
+   * Emitted when the session manager wants the application to quit
+   * (generally because the user is logging out). The application
+   * should exit as soon as possible after receiving this signal; if
+   * it does not, the session manager may choose to forcibly kill it.
+   *
+   * Normally a GUI application would only be sent a ::quit if it
+   * agreed to quit in response to a ::quit_requested signal. However,
+   * this is not guaranteed; in some situations the session manager
+   * may decide to end the session without giving applications a
+   * chance to object.
+   **/
+  signals[QUIT] =
+    g_signal_new ("quit",
+                  G_OBJECT_CLASS_TYPE (object_class),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (EggSMClientClass, quit),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE,
+                  0);
+}
+
+static gboolean sm_client_disable = FALSE;
+static char *sm_client_state_file = NULL;
+static char *sm_client_id = NULL;
+static char *sm_config_prefix = NULL;
+
+static gboolean
+sm_client_post_parse_func (GOptionContext  *context,
+			   GOptionGroup    *group,
+			   gpointer         data,
+			   GError         **error)
+{
+  EggSMClient *client = egg_sm_client_get ();
+
+  if (sm_client_id == NULL)
+    {
+      const gchar *desktop_autostart_id;
+
+      desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+
+      if (desktop_autostart_id != NULL)
+        sm_client_id = g_strdup (desktop_autostart_id);
+    }
+
+  /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
+   * use the same client id. */
+  g_unsetenv ("DESKTOP_AUTOSTART_ID");
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->startup)
+    EGG_SM_CLIENT_GET_CLASS (client)->startup (client, sm_client_id);
+  return TRUE;
+}
+
+/**
+ * egg_sm_client_get_option_group:
+ *
+ * Creates a %GOptionGroup containing the session-management-related
+ * options. You should add this group to the application's
+ * %GOptionContext if you want to use #EggSMClient.
+ *
+ * Return value: the %GOptionGroup
+ **/
+GOptionGroup *
+egg_sm_client_get_option_group (void)
+{
+  const GOptionEntry entries[] = {
+    { "sm-client-disable", 0, 0,
+      G_OPTION_ARG_NONE, &sm_client_disable,
+      N_("Disable connection to session manager"), NULL },
+    { "sm-client-state-file", 0, 0,
+      G_OPTION_ARG_FILENAME, &sm_client_state_file,
+      N_("Specify file containing saved configuration"), N_("FILE") },
+    { "sm-client-id", 0, 0,
+      G_OPTION_ARG_STRING, &sm_client_id,
+      N_("Specify session management ID"), N_("ID") },
+    /* GnomeClient compatibility option */
+    { "sm-disable", 0, G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_NONE, &sm_client_disable,
+      NULL, NULL },
+    /* GnomeClient compatibility option. This is a dummy option that only
+     * exists so that sessions saved by apps with GnomeClient can be restored
+     * later when they've switched to EggSMClient. See bug #575308.
+     */
+    { "sm-config-prefix", 0, G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_STRING, &sm_config_prefix,
+      NULL, NULL },
+    { NULL }
+  };
+  GOptionGroup *group;
+
+  /* Use our own debug handler for the "EggSMClient" domain. */
+  g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
+		     egg_sm_client_debug_handler, NULL);
+
+  group = g_option_group_new ("sm-client",
+			      _("Session management options:"),
+			      _("Show session management options"),
+			      NULL, NULL);
+  g_option_group_add_entries (group, entries);
+  g_option_group_set_parse_hooks (group, NULL, sm_client_post_parse_func);
+
+  return group;
+}
+
+/**
+ * egg_sm_client_set_mode:
+ * @mode: an #EggSMClient mode
+ *
+ * Sets the "mode" of #EggSMClient as follows:
+ *
+ *    %EGG_SM_CLIENT_MODE_DISABLED: Session management is completely
+ *    disabled. The application will not even connect to the session
+ *    manager. (egg_sm_client_get() will still return an #EggSMClient,
+ *    but it will just be a dummy object.)
+ *
+ *    %EGG_SM_CLIENT_MODE_NO_RESTART: The application will connect to
+ *    the session manager (and thus will receive notification when the
+ *    user is logging out, etc), but will request to not be
+ *    automatically restarted with saved state in future sessions.
+ *
+ *    %EGG_SM_CLIENT_MODE_NORMAL: The default. #EggSMCLient will
+ *    function normally.
+ *
+ * This must be called before the application's main loop begins.
+ **/
+void
+egg_sm_client_set_mode (EggSMClientMode mode)
+{
+  global_client_mode = mode;
+}
+
+/**
+ * egg_sm_client_get_mode:
+ *
+ * Gets the global #EggSMClientMode. See egg_sm_client_set_mode()
+ * for details.
+ *
+ * Return value: the global #EggSMClientMode
+ **/
+EggSMClientMode
+egg_sm_client_get_mode (void)
+{
+  return global_client_mode;
+}
+
+/**
+ * egg_sm_client_get:
+ *
+ * Returns the master #EggSMClient for the application.
+ *
+ * On platforms that support saved sessions (ie, POSIX/X11), the
+ * application will only request to be restarted by the session
+ * manager if you call egg_set_desktop_file() to set an application
+ * desktop file. In particular, if the desktop file contains the key
+ * "X
+ *
+ * Return value: the master #EggSMClient.
+ **/
+EggSMClient *
+egg_sm_client_get (void)
+{
+  if (!global_client)
+    {
+      if (global_client_mode != EGG_SM_CLIENT_MODE_DISABLED &&
+	  !sm_client_disable)
+	{
+#if defined (GDK_WINDOWING_WIN32)
+	  global_client = egg_sm_client_win32_new ();
+#elif defined (GDK_WINDOWING_QUARTZ)
+	  global_client = egg_sm_client_osx_new ();
+#else
+	  /* If both D-Bus and XSMP are compiled in, try XSMP first
+	   * (since it supports state saving) and fall back to D-Bus
+	   * if XSMP isn't available.
+	   */
+# ifdef EGG_SM_CLIENT_BACKEND_XSMP
+	  global_client = egg_sm_client_xsmp_new ();
+# endif
+# ifdef EGG_SM_CLIENT_BACKEND_DBUS
+	  if (!global_client)
+	    global_client = egg_sm_client_dbus_new ();
+# endif
+#endif
+	}
+
+      /* Fallback: create a dummy client, so that callers don't have
+       * to worry about a %NULL return value.
+       */
+      if (!global_client)
+	global_client = g_object_new (EGG_TYPE_SM_CLIENT, NULL);
+    }
+
+  return global_client;
+}
+
+/**
+ * egg_sm_client_is_resumed:
+ * @client: the client
+ *
+ * Checks whether or not the current session has been resumed from
+ * a previous saved session. If so, the application should call
+ * egg_sm_client_get_state_file() and restore its state from the
+ * returned #GKeyFile.
+ *
+ * Return value: %TRUE if the session has been resumed
+ **/
+gboolean
+egg_sm_client_is_resumed (EggSMClient *client)
+{
+  g_return_val_if_fail (client == global_client, FALSE);
+
+  return sm_client_state_file != NULL;
+}
+
+/**
+ * egg_sm_client_get_state_file:
+ * @client: the client
+ *
+ * If the application was resumed by the session manager, this will
+ * return the #GKeyFile containing its state from the previous
+ * session.
+ *
+ * Note that other libraries and #EggSMClient itself may also store
+ * state in the key file, so if you call egg_sm_client_get_groups(),
+ * on it, the return value will likely include groups that you did not
+ * put there yourself. (It is also not guaranteed that the first
+ * group created by the application will still be the "start group"
+ * when it is resumed.)
+ *
+ * Return value: the #GKeyFile containing the application's earlier
+ * state, or %NULL on error. You should not free this key file; it
+ * is owned by @client.
+ **/
+GKeyFile *
+egg_sm_client_get_state_file (EggSMClient *client)
+{
+  EggSMClientPrivate *priv = EGG_SM_CLIENT_GET_PRIVATE (client);
+  char *state_file_path;
+  GError *err = NULL;
+
+  g_return_val_if_fail (client == global_client, NULL);
+
+  if (!sm_client_state_file)
+    return NULL;
+  if (priv->state_file)
+    return priv->state_file;
+
+  if (!strncmp (sm_client_state_file, "file://", 7))
+    state_file_path = g_filename_from_uri (sm_client_state_file, NULL, NULL);
+  else
+    state_file_path = g_strdup (sm_client_state_file);
+
+  priv->state_file = g_key_file_new ();
+  if (!g_key_file_load_from_file (priv->state_file, state_file_path, 0, &err))
+    {
+      g_warning ("Could not load SM state file '%s': %s",
+		 sm_client_state_file, err->message);
+      g_clear_error (&err);
+      g_key_file_free (priv->state_file);
+      priv->state_file = NULL;
+    }
+
+  g_free (state_file_path);
+  return priv->state_file;
+}
+
+/**
+ * egg_sm_client_set_restart_command:
+ * @client: the client
+ * @argc: the length of @argv
+ * @argv: argument vector
+ *
+ * Sets the command used to restart @client if it does not have a
+ * .desktop file that can be used to find its restart command.
+ *
+ * This can also be used when handling the ::save_state signal, to
+ * save the current state via an updated command line. (Eg, providing
+ * a list of filenames to open when the application is resumed.)
+ **/
+void
+egg_sm_client_set_restart_command (EggSMClient  *client,
+				   int           argc,
+				   const char  **argv)
+{
+  g_return_if_fail (EGG_IS_SM_CLIENT (client));
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command)
+    EGG_SM_CLIENT_GET_CLASS (client)->set_restart_command (client, argc, argv);
+}
+
+/**
+ * egg_sm_client_will_quit:
+ * @client: the client
+ * @will_quit: whether or not the application is willing to quit
+ *
+ * This MUST be called in response to the ::quit_requested signal, to
+ * indicate whether or not the application is willing to quit. The
+ * application may call it either directly from the signal handler, or
+ * at some later point (eg, after asynchronously interacting with the
+ * user).
+ *
+ * If the application does not connect to ::quit_requested,
+ * #EggSMClient will call this method on its behalf (passing %TRUE
+ * for @will_quit).
+ *
+ * After calling this method, the application should wait to receive
+ * either ::quit_cancelled or ::quit.
+ **/
+void
+egg_sm_client_will_quit (EggSMClient *client,
+			 gboolean     will_quit)
+{
+  g_return_if_fail (EGG_IS_SM_CLIENT (client));
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->will_quit)
+    EGG_SM_CLIENT_GET_CLASS (client)->will_quit (client, will_quit);
+}
+
+/**
+ * egg_sm_client_end_session:
+ * @style: a hint at how to end the session
+ * @request_confirmation: whether or not the user should get a chance
+ * to confirm the action
+ *
+ * Requests that the session manager end the current session. @style
+ * indicates how the session should be ended, and
+ * @request_confirmation indicates whether or not the user should be
+ * given a chance to confirm the logout/reboot/shutdown. Both of these
+ * flags are merely hints though; the session manager may choose to
+ * ignore them.
+ *
+ * Return value: %TRUE if the request was sent; %FALSE if it could not
+ * be (eg, because it could not connect to the session manager).
+ **/
+gboolean
+egg_sm_client_end_session (EggSMClientEndStyle  style,
+			   gboolean             request_confirmation)
+{
+  EggSMClient *client = egg_sm_client_get ();
+
+  g_return_val_if_fail (EGG_IS_SM_CLIENT (client), FALSE);
+
+  if (EGG_SM_CLIENT_GET_CLASS (client)->end_session)
+    {
+      return EGG_SM_CLIENT_GET_CLASS (client)->end_session (client, style,
+							    request_confirmation);
+    }
+  else
+    return FALSE;
+}
+
+/* Signal-emitting callbacks from platform-specific code */
+
+GKeyFile *
+egg_sm_client_save_state (EggSMClient *client)
+{
+  GKeyFile *state_file;
+  char *group;
+
+  g_return_val_if_fail (client == global_client, NULL);
+
+  state_file = g_key_file_new ();
+
+  g_debug ("Emitting save_state");
+  g_signal_emit (client, signals[SAVE_STATE], 0, state_file);
+  g_debug ("Done emitting save_state");
+
+  group = g_key_file_get_start_group (state_file);
+  if (group)
+    {
+      g_free (group);
+      return state_file;
+    }
+  else
+    {
+      g_key_file_free (state_file);
+      return NULL;
+    }
+}
+
+void
+egg_sm_client_quit_requested (EggSMClient *client)
+{
+  g_return_if_fail (client == global_client);
+
+  if (!g_signal_has_handler_pending (client, signals[QUIT_REQUESTED], 0, FALSE))
+    {
+      g_debug ("Not emitting quit_requested because no one is listening");
+      egg_sm_client_will_quit (client, TRUE);
+      return;
+    }
+
+  g_debug ("Emitting quit_requested");
+  g_signal_emit (client, signals[QUIT_REQUESTED], 0);
+  g_debug ("Done emitting quit_requested");
+}
+
+void
+egg_sm_client_quit_cancelled (EggSMClient *client)
+{
+  g_return_if_fail (client == global_client);
+
+  g_debug ("Emitting quit_cancelled");
+  g_signal_emit (client, signals[QUIT_CANCELLED], 0);
+  g_debug ("Done emitting quit_cancelled");
+}
+
+void
+egg_sm_client_quit (EggSMClient *client)
+{
+  g_return_if_fail (client == global_client);
+
+  g_debug ("Emitting quit");
+  g_signal_emit (client, signals[QUIT], 0);
+  g_debug ("Done emitting quit");
+
+  /* FIXME: should we just call gtk_main_quit() here? */
+}
+
+static void
+egg_sm_client_debug_handler (const char *log_domain,
+			     GLogLevelFlags log_level,
+			     const char *message,
+			     gpointer user_data)
+{
+  static int debug = -1;
+
+  if (debug < 0)
+    debug = (g_getenv ("EGG_SM_CLIENT_DEBUG") != NULL);
+
+  if (debug)
+    g_log_default_handler (log_domain, log_level, message, NULL);
+}
diff --git a/copy-n-paste/eggsmclient.h b/copy-n-paste/eggsmclient.h
new file mode 100644
index 0000000..e620b75
--- /dev/null
+++ b/copy-n-paste/eggsmclient.h
@@ -0,0 +1,117 @@
+/* eggsmclient.h
+ * Copyright (C) 2007 Novell, Inc.
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SM_CLIENT_H__
+#define __EGG_SM_CLIENT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_SM_CLIENT            (egg_sm_client_get_type ())
+#define EGG_SM_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SM_CLIENT, EggSMClient))
+#define EGG_SM_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SM_CLIENT, EggSMClientClass))
+#define EGG_IS_SM_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SM_CLIENT))
+#define EGG_IS_SM_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SM_CLIENT))
+#define EGG_SM_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SM_CLIENT, EggSMClientClass))
+
+typedef struct _EggSMClient        EggSMClient;
+typedef struct _EggSMClientClass   EggSMClientClass;
+typedef struct _EggSMClientPrivate EggSMClientPrivate;
+
+typedef enum {
+  EGG_SM_CLIENT_END_SESSION_DEFAULT,
+  EGG_SM_CLIENT_LOGOUT,
+  EGG_SM_CLIENT_REBOOT,
+  EGG_SM_CLIENT_SHUTDOWN
+} EggSMClientEndStyle;
+
+typedef enum {
+  EGG_SM_CLIENT_MODE_DISABLED,
+  EGG_SM_CLIENT_MODE_NO_RESTART,
+  EGG_SM_CLIENT_MODE_NORMAL
+} EggSMClientMode;
+
+struct _EggSMClient
+{
+  GObject parent;
+
+};
+
+struct _EggSMClientClass
+{
+  GObjectClass parent_class;
+
+  /* signals */
+  void (*save_state)       (EggSMClient *client,
+			    GKeyFile    *state_file);
+
+  void (*quit_requested)   (EggSMClient *client);
+  void (*quit_cancelled)   (EggSMClient *client);
+  void (*quit)             (EggSMClient *client);
+
+  /* virtual methods */
+  void	   (*startup)             (EggSMClient          *client,
+				   const char           *client_id);
+  void	   (*set_restart_command) (EggSMClient          *client,
+				   int                   argc,
+				   const char          **argv);
+  void	   (*will_quit)           (EggSMClient          *client,
+				   gboolean              will_quit);
+  gboolean (*end_session)         (EggSMClient          *client,
+				   EggSMClientEndStyle   style,
+				   gboolean              request_confirmation);
+
+  /* Padding for future expansion */
+  void (*_egg_reserved1) (void);
+  void (*_egg_reserved2) (void);
+  void (*_egg_reserved3) (void);
+  void (*_egg_reserved4) (void);
+};
+
+GType            egg_sm_client_get_type            (void) G_GNUC_CONST;
+
+GOptionGroup    *egg_sm_client_get_option_group    (void);
+
+/* Initialization */
+void             egg_sm_client_set_mode            (EggSMClientMode mode);
+EggSMClientMode  egg_sm_client_get_mode            (void);
+EggSMClient     *egg_sm_client_get                 (void);
+
+/* Resuming a saved session */
+gboolean         egg_sm_client_is_resumed          (EggSMClient *client);
+GKeyFile        *egg_sm_client_get_state_file      (EggSMClient *client);
+
+/* Alternate means of saving state */
+void             egg_sm_client_set_restart_command (EggSMClient  *client,
+						    int           argc,
+						    const char  **argv);
+
+/* Handling "quit_requested" signal */
+void             egg_sm_client_will_quit           (EggSMClient *client,
+						    gboolean     will_quit);
+
+/* Initiate a logout/reboot/shutdown */
+gboolean         egg_sm_client_end_session         (EggSMClientEndStyle  style,
+						    gboolean             request_confirmation);
+
+G_END_DECLS
+
+
+#endif /* __EGG_SM_CLIENT_H__ */
diff --git a/data/Makefile.am b/data/Makefile.am
index ebbf010..2613b75 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,7 +1,4 @@
-SUBDIRS = glade icons
-
-applicationsdir = $(datadir)/application-registry
-applications_DATA = goobox.applications
+SUBDIRS = ui icons
 
 desktop_in_in_files = goobox.desktop.in.in
 desktop_in_files = $(desktop_in_in_files:.desktop.in.in=.desktop.in) 
@@ -21,28 +18,18 @@ install-data-local:
 	GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(top_builddir)/data/$(schema_DATA)
 endif
 
-bonobodir       = $(libdir)/bonobo
-serverdir       = $(libdir)/bonobo/servers
-server_in_files = GNOME_Goobox.server.in
-server_DATA     = $(server_in_files:.server.in=.server)
-
- INTLTOOL_SERVER_RULE@
-
 EXTRA_DIST = 			\
 	goobox.schemas		\
 	goobox.schemas.in	\
-	$(applications_DATA)	\
 	$(desktop_in_in_files)	\
 	$(desktop_in_files)	\
 	$(desktop_DATA)		\
-	$(icon_DATA)		\
-	$(server_in_files)
+	$(icon_DATA)
 
 
 DISTCLEANFILES =                \
 	$(schema_DATA)		\
 	$(desktop_in_files)     \
-	$(desktop_DATA)		\
-	$(server_DATA)
+	$(desktop_DATA)
 
 -include $(top_srcdir)/git.mk
diff --git a/data/goobox.schemas.in b/data/goobox.schemas.in
index 8e9c250..049eaab 100644
--- a/data/goobox.schemas.in
+++ b/data/goobox.schemas.in
@@ -256,19 +256,6 @@
 	</locale>
       </schema>
 
-      <schema>
-	<key>/schemas/apps/goobox/extract/first_time</key>
-	<applyto>/apps/goobox/extract/first_time</applyto>
-	<owner>goobox</owner>
-	<type>bool</type>
-	<default>true</default>
-	<locale name="C">
-	  <short></short>
-	  <long>
-	  </long>
-	</locale>
-      </schema>
-
       <!-- RIPPER -->
 
       <schema>
diff --git a/data/glade/.cvsignore b/data/ui/.cvsignore
similarity index 100%
rename from data/glade/.cvsignore
rename to data/ui/.cvsignore
diff --git a/data/ui/Makefile.am b/data/ui/Makefile.am
new file mode 100644
index 0000000..d8399e5
--- /dev/null
+++ b/data/ui/Makefile.am
@@ -0,0 +1,12 @@
+gladedir = $(datadir)/goobox/ui
+glade_DATA = \
+	cover-chooser.ui	\
+	extract.ui		\
+	format-options.ui	\
+	preferences.ui		\
+	properties.ui		\
+	ripper.ui
+
+EXTRA_DIST = $(glade_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/data/ui/cover-chooser.ui b/data/ui/cover-chooser.ui
new file mode 100644
index 0000000..c8cd1bd
--- /dev/null
+++ b/data/ui/cover-chooser.ui
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkDialog" id="cover_chooser_dialog">
+    <property name="visible">True</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Choose a CD Cover</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkVBox" id="image_list_box">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Found images:</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="progress_label">
+                        <property name="visible">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="cancel_search_button">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="relief">none</property>
+                        <child>
+                          <object class="GtkImage" id="image1">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-cancel</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="pack_type">end</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="icon_view_scrolledwindow">
+                    <property name="width_request">554</property>
+                    <property name="height_request">390</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="help_button">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="revert_button">
+                <property name="label">gtk-undo</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="ok_button">
+                <property name="label">gtk-apply</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-11">help_button</action-widget>
+      <action-widget response="-7">revert_button</action-widget>
+      <action-widget response="-10">ok_button</action-widget>
+      <action-widget response="-7">cancel_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/data/ui/extract.ui b/data/ui/extract.ui
new file mode 100644
index 0000000..4f5fbee
--- /dev/null
+++ b/data/ui/extract.ui
@@ -0,0 +1,186 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkDialog" id="extract_dialog">
+    <property name="border_width">6</property>
+    <property name="title" translatable="yes">Extract Tracks</property>
+    <property name="resizable">False</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkVBox" id="vbox3">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkVBox" id="vbox9">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="label7">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Extract&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox5">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="label8">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">    </property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVBox" id="vbox10">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkVBox" id="vbox11">
+                            <property name="visible">True</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkRadioButton" id="all_tracks_radiobutton">
+                                <property name="label" translatable="yes">_All tracks</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkRadioButton" id="selected_tracks_radiobutton">
+                                <property name="label" translatable="yes">_Selected tracks</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                                <property name="group">all_tracks_radiobutton</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="ok_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="help_button">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel_button</action-widget>
+      <action-widget response="-5">ok_button</action-widget>
+      <action-widget response="-11">help_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/data/ui/format-options.ui b/data/ui/format-options.ui
new file mode 100644
index 0000000..9732e71
--- /dev/null
+++ b/data/ui/format-options.ui
@@ -0,0 +1,178 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="upper">10</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">1</property>
+    <property name="page_size">1</property>
+  </object>
+  <object class="GtkDialog" id="format_dialog">
+    <property name="title" translatable="yes">Format Properties</property>
+    <property name="resizable">False</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox4">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkVBox" id="vbox168">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkFrame" id="frame1">
+                <property name="visible">True</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment1">
+                    <property name="visible">True</property>
+                    <property name="top_padding">12</property>
+                    <property name="left_padding">6</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox1">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">12</property>
+                        <child>
+                          <object class="GtkLabel" id="description_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="wrap">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox18">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="quality_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkVBox" id="vbox169">
+                                <property name="visible">True</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkHScale" id="quality_scale">
+                                    <property name="width_request">250</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="adjustment">adjustment1</property>
+                                    <property name="digits">0</property>
+                                  </object>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkHBox" id="hbox17">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">24</property>
+                                    <child>
+                                      <object class="GtkLabel" id="smaller_value_label">
+                                        <property name="visible">True</property>
+                                        <property name="xalign">0</property>
+                                        <property name="use_markup">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="bigger_value_label">
+                                        <property name="visible">True</property>
+                                        <property name="xalign">1</property>
+                                        <property name="use_markup">True</property>
+                                      </object>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="title_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area4">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="ok_button">
+                <property name="label">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-7">ok_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/data/ui/preferences.ui b/data/ui/preferences.ui
new file mode 100644
index 0000000..a0922fb
--- /dev/null
+++ b/data/ui/preferences.ui
@@ -0,0 +1,454 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkDialog" id="preferences_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">CD Player Preferences</property>
+    <property name="resizable">False</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">5</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkNotebook" id="notebook">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <child>
+                  <object class="GtkVBox" id="vbox167">
+                    <property name="visible">True</property>
+                    <property name="border_width">12</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkVBox" id="general_vbox">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label2">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">&lt;b&gt;CD Drive&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkAlignment" id="alignment1">
+                            <property name="visible">True</property>
+                            <property name="left_padding">12</property>
+                            <child>
+                              <object class="GtkVBox" id="drive_selector_box">
+                                <property name="visible">True</property>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="autoplay_checkbutton">
+                        <property name="label" translatable="yes">Automatically play newly inserted discs</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+                <child type="tab">
+                  <object class="GtkLabel" id="label37">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">General</property>
+                  </object>
+                  <packing>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox156">
+                    <property name="visible">True</property>
+                    <property name="border_width">12</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox157">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label28">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">&lt;b&gt;Destination folder&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox15">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label29">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">    </property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkVBox" id="vbox158">
+                                <property name="visible">True</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkFileChooserButton" id="destination_filechooserbutton">
+                                    <property name="visible">True</property>
+                                    <property name="action">select-folder</property>
+                                    <property name="local_only">False</property>
+                                    <property name="title" translatable="yes">Choose destination folder</property>
+                                  </object>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVBox" id="vbox159">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label30">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">&lt;b&gt;Output format&lt;/b&gt;</property>
+                            <property name="use_markup">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox16">
+                            <property name="visible">True</property>
+                            <child>
+                              <object class="GtkLabel" id="label31">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">    </property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkVBox" id="vbox160">
+                                <property name="visible">True</property>
+                                <property name="orientation">vertical</property>
+                                <property name="spacing">24</property>
+                                <child>
+                                  <object class="GtkVBox" id="vbox166">
+                                    <property name="visible">True</property>
+                                    <property name="orientation">vertical</property>
+                                    <property name="spacing">6</property>
+                                    <child>
+                                      <object class="GtkHBox" id="filetype_combobox_box">
+                                        <property name="visible">True</property>
+                                        <property name="spacing">6</property>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <object class="GtkButton" id="filetype_properties_button">
+                                            <property name="label">gtk-properties</property>
+                                            <property name="visible">True</property>
+                                            <property name="can_focus">True</property>
+                                            <property name="receives_default">False</property>
+                                            <property name="use_stock">True</property>
+                                          </object>
+                                          <packing>
+                                            <property name="expand">False</property>
+                                            <property name="fill">False</property>
+                                            <property name="pack_type">end</property>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkNotebook" id="encoding_notebook">
+                                        <property name="visible">True</property>
+                                        <property name="show_tabs">False</property>
+                                        <property name="show_border">False</property>
+                                        <child>
+                                          <object class="GtkVBox" id="vbox146">
+                                            <property name="visible">True</property>
+                                            <property name="orientation">vertical</property>
+                                            <property name="spacing">24</property>
+                                            <child>
+                                              <object class="GtkLabel" id="ogg_description_label">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="label" translatable="yes">&lt;small&gt;&lt;i&gt;Vorbis is an open source, lossy audio codec with high quality output at a lower file size than MP3.&lt;/i&gt;&lt;/small&gt;</property>
+                                                <property name="use_markup">True</property>
+                                                <property name="wrap">True</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">False</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                        </child>
+                                        <child type="tab">
+                                          <object class="GtkLabel" id="label32">
+                                            <property name="visible">True</property>
+                                          </object>
+                                          <packing>
+                                            <property name="tab_fill">False</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkVBox" id="vbox149">
+                                            <property name="visible">True</property>
+                                            <property name="orientation">vertical</property>
+                                            <property name="spacing">24</property>
+                                            <child>
+                                              <object class="GtkLabel" id="flac_description_label">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="label" translatable="yes">&lt;small&gt;&lt;i&gt;Free Lossless Audio Codec (FLAC) is an open source codec that compresses but does not degrade audio quality.&lt;/i&gt;&lt;/small&gt;</property>
+                                                <property name="use_markup">True</property>
+                                                <property name="wrap">True</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">False</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                          </packing>
+                                        </child>
+                                        <child type="tab">
+                                          <object class="GtkLabel" id="label33">
+                                            <property name="visible">True</property>
+                                          </object>
+                                          <packing>
+                                            <property name="position">1</property>
+                                            <property name="tab_fill">False</property>
+                                          </packing>
+                                        </child>
+                                        <child>
+                                          <object class="GtkVBox" id="vbox161">
+                                            <property name="visible">True</property>
+                                            <property name="orientation">vertical</property>
+                                            <child>
+                                              <object class="GtkLabel" id="wave_description_label">
+                                                <property name="visible">True</property>
+                                                <property name="xalign">0</property>
+                                                <property name="label" translatable="yes">&lt;small&gt;&lt;i&gt;WAV+PCM is a lossless format that holds uncompressed, raw pulse-code modulated (PCM) audio.&lt;/i&gt;&lt;/small&gt;</property>
+                                                <property name="use_markup">True</property>
+                                                <property name="wrap">True</property>
+                                              </object>
+                                              <packing>
+                                                <property name="expand">False</property>
+                                                <property name="fill">False</property>
+                                                <property name="position">0</property>
+                                              </packing>
+                                            </child>
+                                          </object>
+                                          <packing>
+                                            <property name="position">2</property>
+                                          </packing>
+                                        </child>
+                                        <child type="tab">
+                                          <object class="GtkLabel" id="label36">
+                                            <property name="visible">True</property>
+                                          </object>
+                                          <packing>
+                                            <property name="position">2</property>
+                                            <property name="tab_fill">False</property>
+                                          </packing>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkCheckButton" id="save_playlist_checkbutton">
+                        <property name="label" translatable="yes">_Save playlist</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child type="tab">
+                  <object class="GtkLabel" id="label27">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Extraction</property>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                    <property name="tab_fill">False</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="help_button">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="close_button">
+                <property name="label">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-11">help_button</action-widget>
+      <action-widget response="-7">close_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/data/ui/properties.ui b/data/ui/properties.ui
new file mode 100644
index 0000000..8d674d7
--- /dev/null
+++ b/data/ui/properties.ui
@@ -0,0 +1,485 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="upper">9999</property>
+    <property name="value">2000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkListStore" id="model1">
+    <columns>
+      <!-- column-name gchararray -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">Single artist</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Various artists</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkDialog" id="properties_dialog">
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">Properties</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">5</property>
+        <child>
+          <object class="GtkVBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkVBox" id="vbox2">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkTable" id="table1">
+                    <property name="visible">True</property>
+                    <property name="n_rows">5</property>
+                    <property name="n_columns">2</property>
+                    <property name="column_spacing">6</property>
+                    <property name="row_spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="label2">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Title:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">title_entry</property>
+                      </object>
+                      <packing>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label8">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="yalign">0</property>
+                        <property name="label" translatable="yes">T_racks:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">track_treeview</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label1">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Artist:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">artist_entry</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox8">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkComboBox" id="artist_combobox">
+                            <property name="visible">True</property>
+                            <property name="model">model1</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="renderer1"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="artist_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="y_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label3">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Year:</property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox3">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkCheckButton" id="year_checkbutton">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <property name="focus_on_click">False</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="year_spinbutton">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="adjustment">adjustment1</property>
+                            <property name="climb_rate">1</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">4</property>
+                        <property name="bottom_attach">5</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox9">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkEntry" id="title_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="search_button">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="tooltip_text" translatable="yes">Search for the remaining data</property>
+                            <property name="relief">none</property>
+                            <child>
+                              <object class="GtkImage" id="image5">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-find</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="y_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox1">
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkVBox" id="vbox4">
+                            <property name="visible">True</property>
+                            <property name="orientation">vertical</property>
+                            <child>
+                              <object class="GtkHBox" id="info_box">
+                                <child>
+                                  <object class="GtkHBox" id="hbox5">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">6</property>
+                                    <child>
+                                      <object class="GtkImage" id="image2">
+                                        <property name="visible">True</property>
+                                        <property name="stock">gtk-dialog-warning</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="info_label">
+                                        <property name="visible">True</property>
+                                        <property name="label" translatable="yes">No album found.</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkHBox" id="navigation_box">
+                                <child>
+                                  <object class="GtkHBox" id="hbox6">
+                                    <property name="visible">True</property>
+                                    <property name="spacing">12</property>
+                                    <child>
+                                      <object class="GtkButton" id="prev_album_button">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="relief">none</property>
+                                        <child>
+                                          <object class="GtkImage" id="image3">
+                                            <property name="visible">True</property>
+                                            <property name="stock">gtk-go-back</property>
+                                          </object>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">0</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkLabel" id="album_label">
+                                        <property name="visible">True</property>
+                                        <property name="label">Album %d of %d</property>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">1</property>
+                                      </packing>
+                                    </child>
+                                    <child>
+                                      <object class="GtkButton" id="next_album_button">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">True</property>
+                                        <property name="receives_default">False</property>
+                                        <property name="relief">none</property>
+                                        <child>
+                                          <object class="GtkImage" id="image4">
+                                            <property name="visible">True</property>
+                                            <property name="stock">gtk-go-forward</property>
+                                          </object>
+                                        </child>
+                                      </object>
+                                      <packing>
+                                        <property name="expand">False</property>
+                                        <property name="fill">False</property>
+                                        <property name="position">2</property>
+                                      </packing>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="pack_type">end</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="fill">False</property>
+                            <property name="pack_type">end</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="bottom_attach">2</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options">GTK_FILL</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow1">
+                        <property name="width_request">450</property>
+                        <property name="height_request">250</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTreeView" id="track_treeview">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="enable_search">False</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="right_attach">2</property>
+                        <property name="top_attach">3</property>
+                        <property name="bottom_attach">4</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="help_button">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="undo_button">
+                <property name="label">gtk-undo</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="ok_button">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-11">help_button</action-widget>
+      <action-widget response="-6">undo_button</action-widget>
+      <action-widget response="-6">cancel_button</action-widget>
+      <action-widget response="-5">ok_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/data/ui/ripper.ui b/data/ui/ripper.ui
new file mode 100644
index 0000000..21df062
--- /dev/null
+++ b/data/ui/ripper.ui
@@ -0,0 +1,140 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy toplevel-contextual -->
+  <object class="GtkDialog" id="ripper_dialog">
+    <property name="border_width">6</property>
+    <property name="title" translatable="yes">Extracting Tracks</property>
+    <property name="resizable">False</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox3">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkVBox" id="vbox1">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkImage" id="image1">
+                    <property name="visible">True</property>
+                    <property name="pixel_size">64</property>
+                    <property name="icon_name">drive-optical</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="padding">12</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox12">
+                <property name="visible">True</property>
+                <property name="border_width">5</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkLabel" id="title_label">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">&lt;big&gt;&lt;b&gt;Extracting tracks&lt;/b&gt;&lt;/big&gt;</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="padding">6</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox170">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">2</property>
+                    <child>
+                      <object class="GtkProgressBar" id="progress_progressbar">
+                        <property name="width_request">350</property>
+                        <property name="visible">True</property>
+                        <property name="pulse_step">0.10000000149</property>
+                        <property name="ellipsize">start</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="track_label">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="use_markup">True</property>
+                        <property name="ellipsize">end</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area3">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 92d9aec..3f95a61 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,24 +1,24 @@
-# List of source files containing translatable strings.
-# Please keep this file sorted alphabetically.
+# DO NOT EDIT.  This file is automatically generated.
+# List of source files which contain translatable strings.
 [encoding: UTF-8]
-data/glade/cover_chooser.glade
-data/glade/extract_dialog.glade
-data/glade/format_dialog.glade
-data/glade/preferences.glade
-data/glade/properties.glade
-data/glade/ripper_dialog.glade
-data/glade/properties.glade
-data/GNOME_Goobox.server.in
+copy-n-paste/eggdesktopfile.c
+copy-n-paste/eggdesktopfile.h
+copy-n-paste/eggsmclient.c
+copy-n-paste/eggsmclient.h
+copy-n-paste/eggsmclient-private.h
+copy-n-paste/eggsmclient-xsmp.c
 data/goobox.desktop.in.in
 data/goobox.schemas.in
+[type: gettext/glade]data/ui/cover-chooser.ui
+[type: gettext/glade]data/ui/extract.ui
+[type: gettext/glade]data/ui/format-options.ui
+[type: gettext/glade]data/ui/preferences.ui
+[type: gettext/glade]data/ui/properties.ui
+[type: gettext/glade]data/ui/ripper.ui
 src/actions.c
 src/actions.h
 src/album-info.c
 src/album-info.h
-src/bacon-cd-selection.c
-src/bacon-cd-selection.h
-src/cd-drive.c
-src/cd-drive.h
 src/dlg-cover-chooser.c
 src/dlg-cover-chooser.h
 src/dlg-extract.c
@@ -29,22 +29,15 @@ src/dlg-properties.c
 src/dlg-properties.h
 src/dlg-ripper.c
 src/dlg-ripper.h
-src/eggtrayicon.c
-src/eggtrayicon.h
-src/file-utils.c
-src/file-utils.h
 src/gconf-utils.c
 src/gconf-utils.h
+src/gio-utils.c
+src/gio-utils.h
 src/glib-utils.c
 src/glib-utils.h
-src/goo-application.c
-src/goo-application.h
-src/goo-cdrom-bsd.c
-src/goo-cdrom.c
-src/goo-cdrom.h
-src/goo-cdrom-linux.c
-src/goo-cdrom-linux.h
-src/goo-cdrom-solaris.c
+src/gnome-desktop-thumbnail.c
+src/gnome-desktop-thumbnail.h
+src/gnome-thumbnail-pixbuf-utils.c
 src/goo-error.c
 src/goo-error.h
 src/goo-player.c
@@ -53,19 +46,23 @@ src/goo-player-info.c
 src/goo-player-info.h
 src/goo-stock.c
 src/goo-stock.h
-src/goo-window.c
-src/goo-window.h
 src/goo-volume-tool-button.c
 src/goo-volume-tool-button.h
-src/gth-image-list.c
-src/gth-image-list.h
-src/gthumb-slide.c
-src/gthumb-slide.h
+src/goo-window.c
+src/goo-window.h
+src/gth-user-dir.c
+src/gth-user-dir.h
+src/gth-window.c
+src/gth-window.h
 src/gtk-file-chooser-preview.c
+src/gtk-file-chooser-preview.h
 src/gtk-utils.c
 src/gtk-utils.h
+src/icons/pixbufs.h
 src/main.c
 src/main.h
+src/metadata.c
+src/metadata.h
 src/preferences.c
 src/preferences.h
 src/track-info.c
diff --git a/po/update-potfiles.sh b/po/update-potfiles.sh
new file mode 100644
index 0000000..e3ffc5d
--- /dev/null
+++ b/po/update-potfiles.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+echo "# DO NOT EDIT.  This file is automatically generated."
+echo "# List of source files which contain translatable strings."
+echo "[encoding: UTF-8]"
+files=`find .. \( -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.ui' -o -name '*.schemas.in' -o -name '*.desktop.in.in' -o -name '*.extension.in.in' \) -printf "%P\n" | sort`
+for f in $files; do
+	case $f in
+        build/*) ;;
+	*.ui) echo "[type: gettext/glade]$f" ;;
+	*) echo $f
+	esac
+done
diff --git a/src/Makefile.am b/src/Makefile.am
index b9cf997..f5663e1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,77 +1,33 @@
 SUBDIRS=icons
-DIST_SUBDIRS=icons
-
-gladedir = $(datadir)/goobox/glade
 
 bin_PROGRAMS = goobox
 
-if ENABLE_DEPRECATIONS
-DISABLE_DEPRECATED = -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED -DGNOME_VFS_DISABLE_DEPRECATED -DGNOME_DISABLE_DEPRECATED -DBONOBO_DISABLE_DEPRECATED
+if RUN_IN_PLACE
+uidir = $(abs_top_srcdir)/data/ui
 else
-DISABLE_DEPRECATED =
+uidir = $(datadir)/goobox/ui
 endif
 
 INCLUDES =						\
 	-I$(top_srcdir)					\
 	-I$(top_builddir)				\
+	-I$(top_srcdir)/copy-n-paste/ 			\
 	-DGOO_PREFIX=\"$(prefix)\"           		\
         -DGOO_SYSCONFDIR=\"$(sysconfdir)\"   		\
         -DGOO_DATADIR=\"$(datadir)\"     	    	\
         -DGOO_LIBDIR=\"$(libdir)\"  	         	\
-	-DGLADEDIR=\""$(gladedir)"\" 			\
-	-DGOO_GLADEDIR=\""$(gladedir)"\"     		\
+	-DGOO_UIDIR=\""$(uidir)"\"     			\
 	$(DISABLE_DEPRECATED)				\
+	$(DBUS_CFLAGS)					\
 	$(GOO_CFLAGS)					\
-	$(LIBNOTIFY_CFLAGS)				\
-	$(HAL_CFLAGS)
-
-gnome_goobox_idl_sources =           	\
-	GNOME_Goobox-stubs.c         	\
-	GNOME_Goobox-skels.c         	\
-	GNOME_Goobox.h         		\
-	GNOME_Goobox-common.c
-
-$(gnome_goobox_idl_sources): gnome_goobox_idl_stamp
-gnome_goobox_idl_stamp: GNOME_Goobox.idl $(ORBIT_IDL)
-	$(ORBIT_IDL) $(IDL_INCLUDES) $<
-	touch $@
+	$(LIBNOTIFY_CFLAGS)
 
 BUILT_SOURCES =				\
 	goo-marshal.c			\
 	goo-marshal.h
 
-GTH_MARSHALLERS =			\
-	gthumb-marshal.c		\
-	gthumb-marshal.h
-
-GTH_ENUMS =				\
-	gthumb-enum-types.h		\
-	gthumb-enum-types.c
-
-GTH_HEADER_FILES =			\
-	gth-image-list.h		
-
-GTH_SOURCES = 				\
-	$(GTH_HEADER_FILES)		\
-	$(GTH_MARSHALLERS)		\
-	$(GTH_ENUMS)			\
-	gth-image-list.c		\
-	gthumb-slide.c			\
-	gthumb-slide.h
-
-BACON_SOURCES = 			\
-	cd-drive.c			\
-	cd-drive.h			\
-	bacon-cd-selection.c		\
-	bacon-cd-selection.h		\
-	dvd_plus_rw_utils.cpp		\
-	transport.hxx
-
 goobox_SOURCES = 				\
 	$(BUILT_SOURCES)			\
-	$(BACON_SOURCES)			\
-	$(GTH_SOURCES)				\
-	$(gnome_goobox_idl_sources)		\
 	actions.c				\
 	actions.h				\
 	album-info.c				\
@@ -86,36 +42,31 @@ goobox_SOURCES = 				\
 	dlg-properties.h			\
 	dlg-ripper.c				\
 	dlg-ripper.h				\
-	eggtrayicon.c				\
-	eggtrayicon.h				\
-	file-utils.c				\
-	file-utils.h				\
 	gconf-utils.c				\
 	gconf-utils.h				\
+	gio-utils.c				\
+	gio-utils.h				\
 	glib-utils.c				\
 	glib-utils.h				\
-	goo-application.c			\
-	goo-application.h			\
-	goo-cdrom.c				\
-	goo-cdrom.h				\
-	goo-cdrom-bsd.c				\
-	goo-cdrom-bsd.h				\
-	goo-cdrom-linux.c			\
-	goo-cdrom-linux.h			\
-	goo-cdrom-solaris.c			\
-	goo-cdrom-solaris.h			\
+	gnome-desktop-thumbnail.c		\
+	gnome-desktop-thumbnail.h		\
+	gnome-thumbnail-pixbuf-utils.c		\
 	goo-error.c				\
 	goo-error.h				\
 	goo-player.c				\
 	goo-player.h				\
 	goo-player-info.c			\
 	goo-player-info.h			\
+	goo-stock.c				\
+	goo-stock.h				\
 	goo-volume-tool-button.c		\
 	goo-volume-tool-button.h		\
 	goo-window.c				\
 	goo-window.h				\
-	goo-stock.c				\
-	goo-stock.h				\
+	gth-user-dir.c				\
+	gth-user-dir.h				\
+	gth-window.c				\
+	gth-window.h				\
 	gtk-file-chooser-preview.c		\
 	gtk-file-chooser-preview.h		\
 	gtk-utils.c				\
@@ -138,50 +89,20 @@ goo-marshal.c: goo-marshal.list $(GLIB_GENMARSHAL)
 	echo "#include \"goo-marshal.h\"" > $@ && \
 	$(GLIB_GENMARSHAL) $< --body --prefix=goo_marshal >> $@
 
-gthumb-marshal.h: gthumb-marshal.list $(GLIB_GENMARSHAL)
-	$(GLIB_GENMARSHAL) $< --header --prefix=gthumb_marshal > $@
-
-gthumb-marshal.c: gthumb-marshal.list gthumb-marshal.h  $(GLIB_GENMARSHAL)
-	echo "#include \"gthumb-marshal.h\"" > $@ && \
-	$(GLIB_GENMARSHAL) $< --body --prefix=gthumb_marshal >> $@
-
-gthumb-enum-types.h: $(GTH_HEADER_FILES) $(GLIB_MKENUMS)
-	$(GLIB_MKENUMS) \
-		--fhead "#ifndef GTHUMB_ENUM__TYPES_H\n#define GTHUMB_ENUM_TYPES_H\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
-		--fprod "/* enumerations from \"@filename \" */\n" \
-		--vhead "GType @enum_name _get_type (void);\n#define GTH_TYPE_ ENUMSHORT@ (@enum_name _get_type())\n" \
-		--ftail "G_END_DECLS\n\n#endif /* GTHUMB_ENUM_TYPES_H */" \
-		$^> xgen-$(@F) \
-	&& (cmp -s xgen-$(@F) gthumb-enum-types.h || cp xgen-$(@F) gthumb-enum-types.h ) \
-        && rm -f xgen-$(@F)
-
-gthumb-enum-types.c: $(GTH_HEADER_FILES) gthumb-enum-types.h
-	$(GLIB_MKENUMS) \
-		--fhead "#include <glib-object.h>\n" \
-		--fprod "\n/* enumerations from \"@filename \" */\n#include \"@filename \"" \
-		--vhead "GType\n enum_name@_get_type (void)\n{\n  static GType etype = 0;\n  if (etype == 0) {\n    static const G Type@Value values[] = {" \
-		--vprod "      { @VALUENAME@, \"@VALUENAME \", \"@valuenick \" }," \
-		--vtail "      { 0, NULL, NULL }\n    };\n    etype = g_ type@_register_static (\"@EnumName \", values);\n  }\n  return etype;\n}\n" \
-		$^> xgen-$(@F) \
-	&& (cmp -s xgen-$(@F) gthumb-enum-types.c || cp xgen-$(@F) gthumb-enum-types.c ) \
-	&& rm -f xgen-$(@F)
-
-goobox_LDADD = 					\
-	$(GOO_LIBS) 				\
-	$(LIBNOTIFY_LIBS)			\
-	$(HAL_LIBS)				\
+goobox_LDADD = 				\
+	$(top_builddir)/copy-n-paste/libeggsmclient.la  \
+	$(DBUS_LIBS)			\
+	$(GOO_LIBS) 			\
+	$(LIBNOTIFY_LIBS)		\
 	$(SYSTEM_LIBS)
 
-EXTRA_DIST = 					\
-	goo-marshal.list 			\
-	gthumb-marshal.list 			\
-	GNOME_Goobox.idl
+EXTRA_DIST = 				\
+	goo-marshal.list 		\
+	gthumb-marshal.list
+
+CLEANFILES = $(BUILT_SOURCES)
 
-CLEANFILES = 					\
-	$(BUILT_SOURCES) 			\
-	$(GTH_MARSHALLERS) 			\
-	$(GTH_ENUMS)				\
-	$(gnome_goobox_idl_sources) 		\
-	gnome_goobox_idl_stamp
+dist-hook:
+	cd $(distdir); rm -f $(CLEANFILES)
 
 -include $(top_srcdir)/git.mk
diff --git a/src/actions.c b/src/actions.c
index 0d3eb84..2a4ba2d 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -21,32 +21,26 @@
  */
 
 #include <config.h>
-#include <gnome.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <gnome.h>
-#include <libbonobo.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
 #include "actions.h"
-#include "main.h"
+#include "dlg-extract.h"
+#include "dlg-preferences.h"
+#include "dlg-properties.h"
 #include "gtk-utils.h"
 #include "goo-window.h"
-#include "file-utils.h"
 #include "gconf-utils.h"
+#include "main.h"
 #include "preferences.h"
-#include "dlg-extract.h"
-#include "dlg-preferences.h"
-#include "dlg-properties.h"
 
 
 void
 activate_action_play (GtkAction *action, 
 		      gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_play (window);
+	goo_window_play (GOO_WINDOW (data));
 }
 
 
@@ -54,8 +48,7 @@ void
 activate_action_play_selected (GtkAction *action, 
 			       gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_play_selected (window);
+	goo_window_play_selected (GOO_WINDOW (data));
 }
 
 
@@ -63,8 +56,7 @@ void
 activate_action_pause (GtkAction *action,
 		       gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_pause (window);
+	goo_window_pause (GOO_WINDOW (data));
 }
 
 
@@ -72,8 +64,7 @@ void
 activate_action_toggle_play (GtkAction *action, 
 			     gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_toggle_play (window);
+	goo_window_toggle_play (GOO_WINDOW (data));
 }
 
 
@@ -81,8 +72,7 @@ void
 activate_action_stop (GtkAction *action, 
 		      gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_stop (window);
+	goo_window_stop (GOO_WINDOW (data));
 }
 
 
@@ -90,8 +80,7 @@ void
 activate_action_next (GtkAction *action, 
 		      gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_next (window);
+	goo_window_next (GOO_WINDOW (data));
 }
 
 
@@ -99,8 +88,7 @@ void
 activate_action_prev (GtkAction *action, 
 		      gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_prev (window);
+	goo_window_prev (GOO_WINDOW (data));
 }
 
 
@@ -108,8 +96,7 @@ void
 activate_action_eject (GtkAction *action, 
 		       gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_eject (window);
+	goo_window_eject (GOO_WINDOW (data));
 }
 
 
@@ -117,41 +104,7 @@ void
 activate_action_reload (GtkAction *action, 
 			gpointer   data)
 {
-	GooWindow *window = data;
-	goo_window_update (window);
-}
-
-
-
-static void
-show_help (GooWindow  *window,
-	   const char *section)
-{
-	GError *err = NULL;  
-
-        gnome_help_display ("goobox", section, &err);
-        
-        if (err != NULL) {
-                GtkWidget *dialog;
-                
-                dialog = _gtk_message_dialog_new (GTK_WINDOW (window),
-						  GTK_DIALOG_DESTROY_WITH_PARENT, 
-						  GTK_STOCK_DIALOG_ERROR,
-						  _("Could not display help"),
-						  err->message,
-						  GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
-						  NULL);
-                gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
-                g_signal_connect (G_OBJECT (dialog), "response",
-                                  G_CALLBACK (gtk_widget_destroy),
-                                  NULL);
-                
-                gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-                
-                gtk_widget_show (dialog);
-                
-                g_error_free (err);
-        }
+	goo_window_update (GOO_WINDOW (data));
 }
 
 
@@ -159,8 +112,7 @@ void
 activate_action_manual (GtkAction *action, 
 			gpointer   data)
 {
-	GooWindow *window = data;
-	show_help (window, NULL);
+	show_help_dialog (GTK_WINDOW (data), NULL);
 }
 
 
@@ -168,8 +120,7 @@ void
 activate_action_shortcuts (GtkAction *action, 
 			   gpointer   data)
 {
-	GooWindow *window = data;
-	show_help (window, "goobox-shortcuts");
+	show_help_dialog (GTK_WINDOW (data), "goobox-shortcuts");
 }
 
 
@@ -275,10 +226,8 @@ external_app_watch_func (GPid     pid,
 	       	         gint     status,
 	                 gpointer data)
 {
-	GooWindow *window = data;
-	
 	g_spawn_close_pid (pid);
-	goo_window_set_hibernate (window, FALSE);
+	goo_window_set_hibernate (GOO_WINDOW (data), FALSE);
 }
 
 
@@ -321,7 +270,7 @@ activate_action_copy_disc (GtkAction *action,
 	char      *command;
 	GError    *error = NULL;
 
-	command = g_strconcat ("nautilus-cd-burner --source-device=", 
+	command = g_strconcat ("brasero --copy=",
 			       goo_player_get_device (goo_window_get_player (window)), 
 			       NULL);
 
@@ -336,6 +285,7 @@ activate_action_copy_disc (GtkAction *action,
 						   &error);
 		goo_window_set_hibernate (window, FALSE);
 	}
+
 	g_free (command);
 }
 
diff --git a/src/album-info.c b/src/album-info.c
index 2b9be01..a0e7c5d 100644
--- a/src/album-info.c
+++ b/src/album-info.c
@@ -21,11 +21,12 @@
  */
 
 #include <config.h>
-#include <gnome.h>
+#include <glib/gi18n.h>
 #include <gst/gst.h>
 #include "album-info.h"
 #include "glib-utils.h"
-#include "file-utils.h"
+#include "gth-user-dir.h"
+
 
 #define MBI_VARIOUS_ARTIST_ID  "89ad4ac3-39f7-470e-963a-56509c546377"
 
@@ -276,7 +277,8 @@ album_info_copy_metadata (AlbumInfo *to_album,
 
 	for (scan_to = to_album->tracks, scan_from = from_album->tracks; 
 	     scan_to && scan_from; 
-	     scan_to = scan_to->next, scan_from = scan_from->next) {
+	     scan_to = scan_to->next, scan_from = scan_from->next)
+	{
 		TrackInfo *to_track = scan_to->data;
 		TrackInfo *from_track = scan_from->data;
 		
@@ -289,18 +291,10 @@ static char *
 get_cache_path (AlbumInfo  *album,
 		const char *disc_id)
 {
-	char *path = NULL;
-	char *dir;
-	
 	if (disc_id == NULL)
 		return NULL;
-		
-	dir = g_build_filename (g_get_home_dir (), ".gnome2", "goobox.d", "albums", NULL);
-	if (ensure_dir_exists (dir, 0700) == GNOME_VFS_OK) 
-		path = g_build_filename (dir, disc_id, NULL);
-	g_free (dir);
-	
-	return path;
+	else
+		return gth_user_dir_get_file (GTH_DIR_DATA, "goobox", "albums", disc_id, NULL);
 }
 
 
diff --git a/src/dlg-cover-chooser.c b/src/dlg-cover-chooser.c
index afa6ab1..cebbb2c 100644
--- a/src/dlg-cover-chooser.c
+++ b/src/dlg-cover-chooser.c
@@ -22,338 +22,67 @@
 
 
 #include <config.h>
-
 #include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
 #include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
-#include <libgnomevfs/gnome-vfs-async-ops.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
-#include "typedefs.h"
-#include "main.h"
-#include "file-utils.h"
+#include "gio-utils.h"
 #include "glib-utils.h"
 #include "gtk-utils.h"
 #include "goo-window.h"
 #include "goo-stock.h"
-#include "gth-image-list.h"
-#include "preferences.h"
-#include "gconf-utils.h"
 
-#define GLADE_CHOOSER_FILE "cover_chooser.glade"
+#define BUFFER_SIZE 4096
+#define COVER_BACKUP_FILENAME "original_cover.png"
 #define MAX_IMAGES 20
-#define READ_TIMEOUT 20
-#define BUFFER_SIZE 612
-#define IMG_PERM 0600
-#define DIR_PERM 0700
-#define THUMB_SIZE 100
-#define THUMB_BORDER 14
-#define QUERY_RESULT "result.html"
-#define ORIGINAL_COVER "original_cover.png"
-
-typedef struct _DialogData DialogData;
-
-typedef void (*FileSavedFunc) (DialogData *data, const char *filename, gboolean success);
-
-struct _DialogData {
-	GooWindow           *window;
-	char                *artist;
-	char                *album;
-	GList               *url_list;
-	GList               *current;
-	guint                load_id, urls, url;
-	char                *tmpdir;
-	GList               *tmpfiles;
-	char                *cover_backup;
-	int                  max_images;
-	gboolean             autofetching;
-
-	FileSavedFunc        file_saved_func;
-	char                *source;
-	char                *dest;
-
-	GnomeVFSAsyncHandle *vfs_handle;
-	GnomeVFSResult       vfs_result;
-
-	GladeXML            *gui;
-
-	GtkWidget           *dialog;
-	GtkWidget           *image_list;
-	GtkWidget           *progress_label;
-	GtkWidget           *ok_button;
-	GtkWidget           *revert_button;
-	GtkWidget           *cc_cancel_search_button;
-};
-
-
-static void
-cancel_search (DialogData *data)
-{
-	if (data->vfs_handle != NULL) {
-		gnome_vfs_async_cancel (data->vfs_handle);
-		data->vfs_handle = NULL;
-	}
-
-	if (data->load_id != 0) {
-		g_source_remove (data->load_id);
-		data->load_id = 0;
-	}
-}
-
-
-static void
-destroy_cb (GtkWidget  *widget, 
-	    DialogData *data)
-{
-	cancel_search (data);
-	
-	if (data->tmpdir != NULL) {
-		g_list_foreach (data->tmpfiles, (GFunc) gnome_vfs_unlink, NULL);
-		path_list_free (data->tmpfiles);
-
-		gnome_vfs_remove_directory (data->tmpdir);
-		g_free (data->tmpdir);
-	}
-
-	g_free (data->dest);
-	g_free (data->source);
-	g_free (data->album);
-	g_free (data->artist);
-	path_list_free (data->url_list);
-	if (data->gui != NULL)
-		g_object_unref (data->gui);
-	g_free (data);
-}
-
-
-/* -- copy_file_from_url() -- */
-
-
-static int
-copy_progress_update_cb (GnomeVFSAsyncHandle      *handle,
-			 GnomeVFSXferProgressInfo *info,
-			 gpointer                  callback_data)
-{
-	DialogData *data = callback_data;
-
-	if (info->status != GNOME_VFS_XFER_PROGRESS_STATUS_OK) {
-		data->vfs_result = info->status;
-		return FALSE;
-	} 
-	if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) {
-		debug (DEBUG_INFO, "COMPLETED");
-	
-		if (data->file_saved_func != NULL)
-			(*data->file_saved_func) (data, data->dest, (data->vfs_result == GNOME_VFS_OK));
-	}
-
-	return TRUE;
-}
-
-
-static void
-copy_file_from_url (DialogData    *data,
-		    const char    *uri_source,
-		    const char    *uri_dest,
-		    FileSavedFunc  file_saved_func)
-{
-	GList                     *src_uri_list, *dest_uri_list;
-	GnomeVFSXferOptions        xfer_options;
-	GnomeVFSXferErrorMode      xfer_error_mode;
-	GnomeVFSXferOverwriteMode  overwrite_mode;
-	GnomeVFSResult             result;
-
-	data->file_saved_func = file_saved_func;
-	
-	g_free (data->dest);
-	data->dest = g_strdup (uri_dest);
-
-	src_uri_list = g_list_prepend (NULL, gnome_vfs_uri_new (uri_source));
-	dest_uri_list = g_list_prepend (NULL, gnome_vfs_uri_new (uri_dest));
-
-	xfer_options    = GNOME_VFS_XFER_DEFAULT;
-	xfer_error_mode = GNOME_VFS_XFER_ERROR_MODE_ABORT;
-	overwrite_mode  = GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE;
-
-	data->vfs_result = GNOME_VFS_OK;
-
-	result = gnome_vfs_async_xfer (&data->vfs_handle,
-				       src_uri_list,
-				       dest_uri_list,
-				       xfer_options,
-				       xfer_error_mode,
-				       overwrite_mode,
-				       GNOME_VFS_PRIORITY_DEFAULT,
-				       copy_progress_update_cb,
-				       data,
-				       NULL,
-				       NULL);
-
-	g_list_foreach (src_uri_list, (GFunc) gnome_vfs_uri_unref, NULL);
-	g_list_free (src_uri_list);
-
-	g_list_foreach (dest_uri_list, (GFunc) gnome_vfs_uri_unref, NULL);
-	g_list_free (dest_uri_list);
-
-	/**/
-
-	if (result != GNOME_VFS_OK) {
-		if (file_saved_func != NULL)
-			(*file_saved_func) (data, uri_dest, FALSE);
-	}
-}
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
 
 
-static void load_current_url (DialogData *data);
-
-
-static gboolean
-load_next_url (gpointer callback_data)
-{
-	DialogData *data = callback_data;
-
-	g_source_remove (data->load_id);
-	data->load_id = 0;
-
-	data->current = data->current->next;
-	load_current_url (data);
-
-	return FALSE;
-}
-
-
-static void
-append_image (DialogData *data,
-	      const char *filename)
-{
-	GdkPixbuf *image;
-	int        pos;
-
-	image = gdk_pixbuf_new_from_file_at_size (filename, 
-						  THUMB_SIZE, THUMB_SIZE, 
-						  NULL);
-	if (image == NULL)
-		return;
-
-	pos = gth_image_list_append (GTH_IMAGE_LIST (data->image_list),
-				     image,
-				     filename,
-				     NULL);
-	gth_image_list_set_image_data_full (GTH_IMAGE_LIST (data->image_list),
-					    pos,
-					    g_strdup (filename),
-					    g_free);
-	g_object_unref (image);
-}
-
-
-static void
-image_saved_cb (DialogData *data,
-		const char *filename,
-		gboolean    success)
-{
-	if (success) {
-		char *tmpfile = g_strdup (filename);
-
-		debug (DEBUG_INFO, "LOAD IMAGE: %s\n", tmpfile);
-
-		data->tmpfiles = g_list_prepend (data->tmpfiles, tmpfile);
-		append_image (data, tmpfile);
-	}
-
-	data->load_id = g_idle_add (load_next_url, data);
-}
-
-
-static void
-update_progress_label (DialogData *data)
-{
-	char *text;
-
-	if (data->url < data->urls)
-		text = g_strdup_printf (_("%u, loading image: %u"), 
-					data->urls, 
-					data->url + 1);
-	else
-		text = g_strdup_printf ("%u", data->urls);
-	gtk_label_set_text (GTK_LABEL (data->progress_label), text);
-	g_free (text);
-}
-
-
-static void
-search_completed (DialogData *data)
-{
-	char *text;
-	
-	gtk_widget_set_sensitive (data->cc_cancel_search_button, FALSE);
-	
-	text = g_strdup_printf ("%u", data->urls);
-	gtk_label_set_text (GTK_LABEL (data->progress_label), text);
-	g_free (text);
-}
-
-
-static void
-load_current_url (DialogData *data)
-{
-	char *url, *dest;
-	char *filename;
-
-	update_progress_label (data);
-
-	if ((data->current == NULL) || (data->url >= data->max_images)) {
-		search_completed (data);
-		return;
-	}
-
-	url = data->current->data;
-
-	debug (DEBUG_INFO, "LOAD %s\n", url);
-
-	filename = g_strdup_printf ("%d.png", data->url);
-	dest = g_build_filename (data->tmpdir, filename, NULL);
-	g_free (filename);
-
-	copy_file_from_url (data, url, dest, image_saved_cb);
-	data->url++;
-}
-
-
-static void
-start_loading_images (DialogData *data)
-{
-	gth_image_list_set_no_image_text (GTH_IMAGE_LIST (data->image_list),
-					  _("Loading images"));
-
-	data->current = data->url_list;
-	load_current_url (data);
-}
+enum {
+	URL_COLUMN,
+	IMAGE_COLUMN,
+	N_COLUMNS
+};
 
 
-static gboolean
-make_file_list_from_search_result (DialogData *data,
-				   const char *filename)
+typedef struct {
+	GooWindow    *window;
+	char         *artist;
+	char         *album;
+	GtkBuilder   *builder;
+	GtkWidget    *dialog;
+	GtkWidget    *icon_view;
+	GdkPixbuf    *cover_backup;
+	GList        *file_list;
+	int           total_files;
+	GList        *current_file;
+	int           loaded_files;
+	GCancellable *cancellable;
+	gboolean      searching;
+	gboolean      destroy;
+} DialogData;
+
+
+static GList *
+make_file_list_from_search_result (void  *buffer,
+				   gsize  count,
+				   int    max_files)
 {
-	int       fd, n;
-	char      buf[BUFFER_SIZE];
-	int       buf_offset = 0;
-	GString  *partial_url;
-	gboolean  done = FALSE;
-	int       urls = 0;
-
-	fd = open (filename, O_RDONLY);
-	if (fd == 0) 
-		return FALSE;
-	
+	GList        *list = NULL;
+	int           n_files = 0;
+	gboolean      done = FALSE;
+	GInputStream *stream;
+	gssize        n;
+	char          buf[BUFFER_SIZE];
+	int           buf_offset = 0;
+	GString      *partial_url;
+
+	stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
 	partial_url = NULL;
-	while ((n = read (fd, buf + buf_offset, BUFFER_SIZE - buf_offset - 1)) > 0) {
+	while ((n = g_input_stream_read (stream,
+					 buf + buf_offset,
+					 BUFFER_SIZE - buf_offset - 1,
+					 NULL,
+					 NULL)) > 0)
+	{
 		const char *prefix = "/images?q=tbn:";
 		int         prefix_len = strlen (prefix);
 		char       *url_start;
@@ -361,20 +90,20 @@ make_file_list_from_search_result (DialogData *data,
 
 		buf[buf_offset+n] = 0;
 
-		if (partial_url == NULL) 
+		if (partial_url == NULL)
 			url_start = strstr (buf, prefix);
 		else
 			url_start = buf;
 
 		while (url_start != NULL) {
 			char *url_end;
-			
+
 			url_end = strstr (url_start, " ");
-		
+
 			if (url_end == NULL) {
 				if (partial_url == NULL)
 					partial_url = g_string_new (url_start);
-				else 
+				else
 					g_string_append (partial_url, url_start);
 				url_start = NULL;
 				copy_tail = FALSE;
@@ -383,24 +112,23 @@ make_file_list_from_search_result (DialogData *data,
 				char *url_tail = g_strndup (url_start, url_end - url_start);
 				char *url;
 				char *complete_url;
-				
+
 				if (partial_url != NULL) {
 					g_string_append (partial_url, url_tail);
 					g_free (url_tail);
 					url = partial_url->str;
 					g_string_free (partial_url, FALSE);
 					partial_url = NULL;
-				} 
-				else 
+				}
+				else
 					url = url_tail;
-					
+
 				complete_url = g_strconcat ("http://images.google.com";, url, NULL);
 				g_free (url);
 
-				data->url_list = g_list_prepend (data->url_list, complete_url);
-				urls++;
-				
-				if (urls >= data->max_images) {
+				list = g_list_prepend (list, complete_url);
+				n_files++;
+				if (n_files >= max_files) {
 					done = TRUE;
 					break;
 				}
@@ -414,219 +142,318 @@ make_file_list_from_search_result (DialogData *data,
 
 		if (copy_tail) {
 			prefix_len = MIN (prefix_len, buf_offset + n);
-			strncpy (buf, 
-				 buf + buf_offset + n - prefix_len, 
+			strncpy (buf,
+				 buf + buf_offset + n - prefix_len,
 				 prefix_len);
 			buf_offset = prefix_len;
-		} 
-		else 
+		}
+		else
 			buf_offset = 0;
 	}
 
 	if (partial_url != NULL)
 		g_string_free (partial_url, TRUE);
 
-	close (fd);
-	data->tmpfiles = g_list_prepend (data->tmpfiles, g_strdup (filename));
-
-	data->url_list = g_list_reverse (data->url_list);
-	data->urls = urls;
-	data->url = 0;
+	g_object_unref (stream);
 
-	return (data->url_list != NULL);
+	return g_list_reverse (list);
 }
 
 
-static void
-search_result_saved_cb (DialogData *data,
-			const char *filename,
-			gboolean    success)
-{
-	if (! success) {
-		_gtk_error_dialog_run (GTK_WINDOW (data->dialog),
-				       _("Could not search for a cover on Internet"),
-				       "%s",
-				       gnome_vfs_result_to_string (data->vfs_result));
-		return;
-	}
-
-	if (! make_file_list_from_search_result (data, filename)) {
-		gth_image_list_set_no_image_text (GTH_IMAGE_LIST (data->image_list),
-						  _("No image found"));
-		return;
-	}
-
-	start_loading_images (data);
-}
-
-
-static char*
-get_query (DialogData *data)
+static char *
+get_query (const char *album,
+	   const char *artist)
 {
 	char *s, *e, *q;
 
-	s = g_strdup_printf ("%s %s", data->album, data->artist);
-	e = gnome_vfs_escape_string (s);
+	s = g_strdup_printf ("%s %s", album, artist);
+	e = g_uri_escape_string (s, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
 	q = g_strconcat ("http://images.google.com/images?q=";, e, NULL);
+
 	g_free (e);
 	g_free (s);
-	
+
 	return q;
 }
 
 
-static gboolean
-start_searching (gpointer callback_data)
+/* -- dlg_cover_chooser -- */
+
+
+static void
+destroy_cb (GtkWidget  *widget, 
+	    DialogData *data)
 {
-	DialogData *data = callback_data;
-	char       *query;
-	char       *dest;
+	if (data->searching) {
+		data->destroy = TRUE;
+		g_cancellable_cancel (data->cancellable);
+		return;
+	}
+	
+	g_object_unref (data->cancellable);
+	_g_string_list_free (data->file_list);
+	_g_object_unref (data->cover_backup);
+	g_object_unref (data->builder);
+	g_free (data->album);
+	g_free (data->artist);
+	g_free (data);
+}
 
-	g_source_remove (data->load_id);
-	data->load_id = 0;
 
-	query = get_query (data);
-	dest = g_build_filename (data->tmpdir, QUERY_RESULT, NULL);
-	copy_file_from_url (data, query, dest, search_result_saved_cb);
+static void
+search_completed (DialogData *data)
+{
+	char *text;
+	
+	data->searching = FALSE;
+	gtk_widget_set_sensitive (GET_WIDGET ("cancel_search_button"), FALSE);
+	text = g_strdup_printf ("%u", data->total_files);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("progress_label")), text);
 
-	g_free (dest);
-	g_free (query);
+	g_free (text);
 
-	return FALSE;
+	if (data->destroy)
+		destroy_cb (NULL, data);
 }
 
 
-/* callbacks */
+static void load_current_file (DialogData *data);
 
 
-/* called when the "help" button is clicked. */
 static void
-help_cb (GtkWidget  *widget, 
-	 DialogData *data)
+search_image_data_ready_cb (void     *buffer,
+			    gsize     count,
+			    GError   *error,
+			    gpointer  user_data)
 {
-	GError *err;
+	DialogData *data = user_data;
 
-	err = NULL;  
-	gnome_help_display ("goobox", "search_cover_on_internet", &err);
-	
-	if (err != NULL) {
-		GtkWidget *dialog;
-		
-		dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
-						 0,
-						 GTK_MESSAGE_ERROR,
-						 GTK_BUTTONS_CLOSE,
-						 _("Could not display help: %s"),
-						 err->message);
-		
-		g_signal_connect (G_OBJECT (dialog), "response",
-				  G_CALLBACK (gtk_widget_destroy),
-				  NULL);
-		
-		gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-		
-		gtk_widget_show (dialog);
-		
-		g_error_free (err);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		search_completed (data);
+		return;
 	}
+
+	if (error == NULL) {
+		GInputStream *stream;
+		GdkPixbuf    *image;
+
+		stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
+		image = gdk_pixbuf_new_from_stream (stream, NULL, &error);
+		if (image != NULL) {
+			GtkTreeModel *model;
+			GtkTreeIter   iter;
+			char         *url;
+
+			model = gtk_icon_view_get_model (GTK_ICON_VIEW (data->icon_view));
+			gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+			url = (char *) data->current_file->data;
+			gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+					    URL_COLUMN, url,
+					    IMAGE_COLUMN, image,
+					    -1);
+
+			g_object_unref (image);
+		}
+	}
+
+	data->loaded_files++;
+	data->current_file = data->current_file->next;
+	load_current_file (data);
+}
+
+
+static void
+update_progress_label (DialogData *data)
+{
+	char *text;
+
+	if (data->loaded_files < data->total_files)
+		text = g_strdup_printf (_("%u, loading image: %u"),
+					data->total_files,
+					data->loaded_files + 1);
+	else
+		text = g_strdup_printf ("%u", data->total_files);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("progress_label")), text);
+
+	g_free (text);
 }
 
 
 static void
-revert_cb (GtkWidget  *widget, 
-	   DialogData *data)
+load_current_file (DialogData *data)
 {
-	char *original_cover;
+	char  *url;
+	GFile *source;
+
+	update_progress_label (data);
 
-	if (data->cover_backup == NULL) 
+	if (data->current_file == NULL) {
+		search_completed (data);
 		return;
+	}
+
+	url = data->current_file->data;
+
+	debug (DEBUG_INFO, "LOADING %s\n", url);
 
-	original_cover = goo_window_get_cover_filename (data->window);
-	file_copy (data->cover_backup, original_cover);
-	goo_window_update_cover (data->window);
+	source = g_file_new_for_uri (url);
+	g_load_file_async (source,
+			   G_PRIORITY_DEFAULT,
+			   data->cancellable,
+			   search_image_data_ready_cb,
+			   data);
+
+	g_object_unref (source);
 }
 
 
-/* called when the "ok" button is clicked. */
 static void
-ok_cb (GtkWidget  *widget, 
-       DialogData *data)
+start_loading_files (DialogData *data)
 {
-	GthImageList *list = GTH_IMAGE_LIST (data->image_list);
-	GList        *selection;
+	gtk_list_store_clear (GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (data->icon_view))));
+	data->total_files = g_list_length (data->file_list);
+	data->current_file = data->file_list;
+	data->loaded_files = 0;
+	load_current_file (data);
+}
 
-	selection =  gth_image_list_get_selection (list);
-	if (selection != NULL) {
-		char *src = selection->data;
 
-		debug (DEBUG_INFO, "SET COVER: %s\n", src);
+static void
+search_query_ready_cb (void     *buffer,
+		       gsize     count,
+		       GError   *error,
+		       gpointer  user_data)
+{
+	DialogData *data = user_data;
 
-		if (! data->autofetching 
-		    || goo_window_get_current_cd_autofetch (data->window))
-			goo_window_set_cover_image (data->window, src);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		search_completed (data);
+		return;
+	}
 
-		g_list_free (selection);
+	if (error != NULL) {
+		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog),
+						    _("Could not search for a cover on Internet"),
+						    &error);
+		search_completed (data);
+		return;
 	}
+
+	data->file_list = make_file_list_from_search_result (buffer, count, MAX_IMAGES);
+	start_loading_files (data);
 }
 
 
 static void
-image_list_selection_changed_cb (GthImageList *list,
-				 DialogData   *data)
+start_searching (DialogData *data)
 {
-	GList *selection;
+	char  *query;
+	GFile *file;
+
+	data->searching = TRUE;
+	g_cancellable_reset (data->cancellable);
+	gtk_widget_set_sensitive (GET_WIDGET ("cancel_search_button"), TRUE);
+
+	query = get_query (data->album, data->artist);
+	file = g_file_new_for_uri (query);
+	g_load_file_async (file,
+			   G_PRIORITY_DEFAULT,
+			   data->cancellable,
+			   search_query_ready_cb,
+			   data);
+
+	g_object_unref (file);
+	g_free (query);
+}
 
-	selection = gth_image_list_get_selection (list);
-	gtk_widget_set_sensitive (data->ok_button, selection != NULL);
-	g_list_free (selection);
+
+static void
+help_button_clicked_cb (GtkWidget  *widget,
+			DialogData *data)
+{
+	show_help_dialog (GTK_WINDOW (data->window), "search_cover_on_internet");
 }
 
 
 static void
-image_list_item_activated_cb (GthImageList *list,
-			      int           pos,
-			      DialogData   *data)
+revert_button_clicked_cb (GtkWidget  *widget,
+			  DialogData *data)
 {
-	ok_cb (NULL, data);
+	goo_window_set_cover_image_from_pixbuf (data->window, data->cover_backup);
 }
 
 
 static void
-backup_cover_image (DialogData   *data)
+ok_button_clicked_cb (GtkWidget  *widget,
+		      DialogData *data)
 {
-	GnomeVFSURI *uri;
-	char        *original_cover;
-	char        *cover_backup;
+	GList *list;
 
-	original_cover = goo_window_get_cover_filename (data->window);
-	if (original_cover == NULL) {	
-		gtk_widget_set_sensitive (data->revert_button, FALSE);
-		return;
-	}
+	list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (data->icon_view));
+	if (list != NULL) {
+		GtkTreePath  *path;
+		GtkTreeModel *model;
+		GtkTreeIter   iter;
 
-	uri = new_uri_from_path (original_cover);
-	if (uri == NULL) {
-		gtk_widget_set_sensitive (data->revert_button, FALSE);
-		return;
+		path = list->data;
+		model = gtk_icon_view_get_model (GTK_ICON_VIEW (data->icon_view));
+		if (gtk_tree_model_get_iter (model, &iter, path)) {
+			GdkPixbuf *image;
+
+			gtk_tree_model_get (model, &iter, IMAGE_COLUMN, &image, -1);
+			goo_window_set_cover_image_from_pixbuf (data->window, image);
+
+			g_object_unref (image);
+		}
+
+		g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+		g_list_free (list);
 	}
-	gtk_widget_set_sensitive (data->revert_button, gnome_vfs_uri_exists (uri));
-	gnome_vfs_uri_unref (uri);
+}
+
+
+static void
+icon_view_selection_changed_cb (GtkIconView *icon_view,
+				DialogData  *data)
+{
+	GList *list;
 
-	cover_backup = g_build_filename (data->tmpdir, ORIGINAL_COVER, NULL);
-	file_copy (original_cover, cover_backup);
-	g_free (original_cover);
+	list = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (data->icon_view));
+	gtk_widget_set_sensitive (GET_WIDGET ("ok_button"), list != NULL);
 
-	data->tmpfiles = g_list_prepend (data->tmpfiles, cover_backup);
-	data->cover_backup = cover_backup;
+	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (list);
 }
 
 
 static void
-cancel_search_button_clicked_cb (GtkWidget  *widget, 
+icon_view_item_activated_cb (GtkIconView *icon_view,
+			     GtkTreePath *path,
+			     DialogData  *data)
+{
+	ok_button_clicked_cb (NULL, data);
+}
+
+
+static void
+cancel_search_button_clicked_cb (GtkWidget  *widget,
        				 DialogData *data)
 {
-	cancel_search (data);
-	search_completed (data);	
+	g_cancellable_cancel (data->cancellable);
+}
+
+
+static void
+backup_cover_image (DialogData *data)
+{
+	char *cover_filename;
+
+	cover_filename = goo_window_get_cover_filename (data->window);
+	gtk_widget_set_sensitive (GET_WIDGET ("revert_button"), cover_filename != NULL);
+	if (cover_filename != NULL)
+		data->cover_backup = gdk_pixbuf_new_from_file (cover_filename, NULL);
+
+	g_free (cover_filename);
 }
 
 
@@ -635,50 +462,49 @@ dlg_cover_chooser (GooWindow  *window,
 		   const char *album,
 		   const char *artist)
 {
-	DialogData *data;
-	GtkWidget  *scrolled_window;
-	GtkWidget  *btn_cancel;
-	GtkWidget  *btn_help;
-	GtkWidget  *image;
+	DialogData      *data;
+	GtkListStore    *model;
+	GtkCellRenderer *renderer;
+	GtkWidget       *image;
 
 	data = g_new0 (DialogData, 1);
 	data->window = window;
-	data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_CHOOSER_FILE, NULL, NULL);
-        if (!data->gui) {
-		g_warning ("Could not find " GLADE_CHOOSER_FILE "\n");
-		g_free (data);
-                return;
-        }
-
+	data->builder = _gtk_builder_new_from_file ("cover-chooser.ui", "");
 	data->album = g_strdup (album);
 	data->artist = g_strdup (artist);
-	data->max_images = MAX_IMAGES;
+	data->cancellable = g_cancellable_new ();
 
 	/* Get the widgets. */
 
-	data->dialog = glade_xml_get_widget (data->gui, "cover_chooser_dialog");
-	scrolled_window = glade_xml_get_widget (data->gui, "image_list_scrolledwindow");
-	data->progress_label = glade_xml_get_widget (data->gui, "progress_label");
-	data->ok_button = glade_xml_get_widget (data->gui, "cc_okbutton");
-	data->revert_button = glade_xml_get_widget (data->gui, "cc_revertbutton");
-	btn_cancel = glade_xml_get_widget (data->gui, "cc_cancelbutton");
-	btn_help = glade_xml_get_widget (data->gui, "cc_helpbutton");
+	data->dialog = GET_WIDGET ("cover_chooser_dialog");
+
+	model = gtk_list_store_new (N_COLUMNS,
+				    G_TYPE_STRING,
+				    GDK_TYPE_PIXBUF);
+	data->icon_view = gtk_icon_view_new_with_model (GTK_TREE_MODEL (model));
+	g_object_unref (model);
 
-	data->cc_cancel_search_button = glade_xml_get_widget (data->gui, "cc_cancel_search_button");
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	g_object_set (renderer, "follow-state", TRUE, NULL);
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->icon_view),
+				    renderer,
+				    TRUE);
+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (data->icon_view),
+					renderer,
+					"pixbuf", IMAGE_COLUMN,
+					NULL);
 
-	data->image_list = gth_image_list_new (THUMB_SIZE + THUMB_BORDER);
-	gth_image_list_set_view_mode (GTH_IMAGE_LIST (data->image_list),
-				      GTH_VIEW_MODE_VOID);
-	gth_image_list_set_selection_mode (GTH_IMAGE_LIST (data->image_list),
-					   GTK_SELECTION_SINGLE);
-	gtk_container_add (GTK_CONTAINER (scrolled_window), data->image_list);
+	gtk_widget_show (data->icon_view);
+	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("icon_view_scrolledwindow")), data->icon_view);
 
 	/* Set widgets data. */
 
-	gtk_widget_set_sensitive (data->ok_button, FALSE);
+	backup_cover_image (data);
+
+	gtk_widget_set_sensitive (GET_WIDGET ("ok_button"), FALSE);
 
 	image = gtk_image_new_from_stock (GOO_STOCK_RESET, GTK_ICON_SIZE_BUTTON);
-	g_object_set (data->revert_button, 
+	g_object_set (GET_WIDGET ("revert_button"),
 		      "use_stock", TRUE,
 		      "label", GOO_STOCK_RESET,
 		      "image", image,
@@ -690,31 +516,31 @@ dlg_cover_chooser (GooWindow  *window,
 			  "destroy",
 			  G_CALLBACK (destroy_cb),
 			  data);
-	g_signal_connect_swapped (G_OBJECT (btn_cancel), 
+	g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
 				  "clicked",
 				  G_CALLBACK (gtk_widget_destroy),
 				  G_OBJECT (data->dialog));
-	g_signal_connect (G_OBJECT (btn_help), 
+	g_signal_connect (GET_WIDGET ("help_button"),
 			  "clicked",
-			  G_CALLBACK (help_cb),
+			  G_CALLBACK (help_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->ok_button), 
+	g_signal_connect (GET_WIDGET ("ok_button"),
 			  "clicked",
-			  G_CALLBACK (ok_cb),
+			  G_CALLBACK (ok_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->revert_button), 
+	g_signal_connect (GET_WIDGET ("revert_button"),
 			  "clicked",
-			  G_CALLBACK (revert_cb),
+			  G_CALLBACK (revert_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->image_list), 
-			  "selection_changed",
-			  G_CALLBACK (image_list_selection_changed_cb),
+	g_signal_connect (G_OBJECT (data->icon_view),
+			  "selection-changed",
+			  G_CALLBACK (icon_view_selection_changed_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->image_list), 
-			  "item_activated",
-			  G_CALLBACK (image_list_item_activated_cb),
+	g_signal_connect (G_OBJECT (data->icon_view),
+			  "item-activated",
+			  G_CALLBACK (icon_view_item_activated_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->cc_cancel_search_button), 
+	g_signal_connect (GET_WIDGET ("cancel_search_button"),
 			  "clicked",
 			  G_CALLBACK (cancel_search_button_clicked_cb),
 			  data);
@@ -723,83 +549,72 @@ dlg_cover_chooser (GooWindow  *window,
 
 	gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (window));
 	gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
-	gtk_widget_show_all (data->dialog);
+	gtk_widget_show (data->dialog);
 
-	/**/
+	start_searching (data);
+}
 
-	data->tmpdir = g_strdup (get_temp_work_dir ());
-	ensure_dir_exists (data->tmpdir, DIR_PERM);
-	backup_cover_image (data);
 
-	gth_image_list_set_no_image_text (GTH_IMAGE_LIST (data->image_list),
-					  _("Searching images..."));
+/* -- auto fetch functions -- */
 
-	data->load_id = g_idle_add (start_searching, data);
-}
+
+typedef struct {
+	GooWindow *window;
+} FetchData;
 
 
 static void
-auto_fetch__image_saved_cb (DialogData *data,
-			    const char *filename,
-			    gboolean    success)
+fetch_data_free (FetchData *data)
 {
-	if (success) {
-		char *tmpfile = g_strdup (filename);
-		
-		debug (DEBUG_INFO, "LOAD IMAGE: %s\n", tmpfile);
-		data->tmpfiles = g_list_prepend (data->tmpfiles, tmpfile);
-
-		goo_window_set_cover_image (data->window, get_path_from_uri (filename));
-	}
-
-	destroy_cb (NULL, data);
+	g_free (data);
 }
 
 
 static void
-auto_fetch__search_result_saved_cb (DialogData *data,
-				    const char *filename,
-				    gboolean    success)
+fetch_image_data_ready_cb (void     *buffer,
+			   gsize     count,
+			   GError   *error,
+			   gpointer  user_data)
 {
-	if (! success || ! make_file_list_from_search_result (data, filename))
-		return;
-
-	if (data->url_list != NULL) {
-		char *filename, *url, *dest;
+	FetchData *data = user_data;
 
-		url = (char*) data->url_list->data;
-
-		filename = g_strdup_printf ("%d.png", data->url);
-		dest = g_build_filename (data->tmpdir, filename, NULL);
-		g_free (filename);
-
-		copy_file_from_url (data, url, dest, auto_fetch__image_saved_cb)
-;
-		g_free (dest);
-	} 
-	else
-		destroy_cb (NULL, data);
+	if (error == NULL)
+		goo_window_set_cover_image_from_data (data->window, buffer, count);
+	fetch_data_free (data);
 }
 
 
-static gboolean
-auto_fetch_from_name__start_searching (gpointer callback_data)
+static void
+query_ready_cb (void     *buffer,
+		gsize     count,
+		GError   *error,
+		gpointer  user_data)
 {
-	DialogData *data = callback_data;
-	char       *query;
-	char       *dest;
+	FetchData *data = user_data;
+	GList     *list;
 
-	g_source_remove (data->load_id);
-	data->load_id = 0;
+	if (error != NULL) {
+		fetch_data_free (data);
+		return;
+	}
 
-	query = get_query (data);
-	dest = g_build_filename (data->tmpdir, QUERY_RESULT, NULL);
-	copy_file_from_url (data, query, dest, auto_fetch__search_result_saved_cb);
+	list = make_file_list_from_search_result (buffer, count, 1);
+	if (list != NULL) {
+		GFile *file;
 
-	g_free (dest);
-	g_free (query);
+		file = g_file_new_for_uri ((char *) list->data);
+		g_load_file_async (file,
+				   G_PRIORITY_DEFAULT,
+				   NULL,
+				   fetch_image_data_ready_cb,
+				   data);
 
-	return FALSE;
+		g_object_unref (file);
+	}
+	else
+		fetch_data_free (data);
+
+	_g_string_list_free (list);
 }
 
 
@@ -808,36 +623,23 @@ fetch_cover_image_from_name (GooWindow  *window,
 		             const char *album,
 		             const char *artist)
 {
-	DialogData *data;
+	FetchData *data;
+	char      *url;
+	GFile     *file;
 	
-	data = g_new0 (DialogData, 1);
+	data = g_new0 (FetchData, 1);
 	data->window = window;
-	data->album = g_strdup (album);
-	data->artist = g_strdup (artist);
-	data->max_images = 1;
-	data->autofetching = TRUE;
-
-	data->tmpdir = get_temp_work_dir ();
-	ensure_dir_exists (data->tmpdir, DIR_PERM);
-
-	data->load_id = g_idle_add (auto_fetch_from_name__start_searching, data);
-}
-
-
-static gboolean
-auto_fetch_from_asin__start_searching (gpointer callback_data)
-{
-	DialogData *data = callback_data;
-	char       *dest;
 
-	g_source_remove (data->load_id);
-	data->load_id = 0;
+	url = get_query (album, artist);
+	file = g_file_new_for_uri (url);
+	g_load_file_async (file,
+			   G_PRIORITY_DEFAULT,
+			   NULL,
+			   query_ready_cb,
+			   data);
 
-	dest = g_strconcat ("file://", data->tmpdir, "/1.jpg", NULL);
-	copy_file_from_url (data, data->source, dest, auto_fetch__image_saved_cb);
-	g_free (dest);
-
-	return FALSE;
+	g_object_unref (file);
+	g_free (url);
 }
 
 
@@ -845,16 +647,20 @@ void
 fetch_cover_image_from_asin (GooWindow  *window,
 		             const char *asin)
 {
-	DialogData *data;
+	FetchData *data;
+	char      *url;
+	GFile     *file;
 	
-	data = g_new0 (DialogData, 1);
+	data = g_new0 (FetchData, 1);
 	data->window = window;
-	data->max_images = 1;
-	data->autofetching = TRUE;
-
-	data->tmpdir = g_strdup (get_temp_work_dir ());
-	ensure_dir_exists (data->tmpdir, DIR_PERM);
-
-	data->source = g_strdup_printf ("http://images.amazon.com/images/P/%s.01._SCLZZZZZZZ_.jpg";, asin);
-	data->load_id = g_idle_add (auto_fetch_from_asin__start_searching, data);
+	url = g_strdup_printf ("http://images.amazon.com/images/P/%s.01._SCLZZZZZZZ_.jpg";, asin);
+	file = g_file_new_for_uri (url);
+	g_load_file_async (file,
+			   G_PRIORITY_DEFAULT,
+			   NULL,
+			   fetch_image_data_ready_cb,
+			   data);
+
+	g_object_unref (file);
+	g_free (url);
 }
diff --git a/src/dlg-extract.c b/src/dlg-extract.c
index 9635a76..1ea4f15 100644
--- a/src/dlg-extract.c
+++ b/src/dlg-extract.c
@@ -21,141 +21,81 @@
  */
 
 #include <config.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
 #include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <libgnomeui/gnome-dialog.h>
-#include <libgnomeui/gnome-dialog-util.h>
-#include <libgnomeui/gnome-propertybox.h>
-#include <libgnomeui/gnome-pixmap.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
 #include <gst/gst.h>
-#include "typedefs.h"
-#include "main.h"
+#include "dlg-extract.h"
+#include "dlg-ripper.h"
 #include "gconf-utils.h"
-#include "gtk-utils.h"
-#include "typedefs.h"
+#include "goo-player.h"
 #include "goo-stock.h"
-#include "goo-window.h"
-#include "preferences.h"
+#include "gtk-utils.h"
 #include "track-info.h"
-#include "goo-player.h"
-#include "dlg-ripper.h"
-#include "file-utils.h"
+#include "typedefs.h"
+
+
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
 
-#define GLADE_EXTRACT_FILE "extract_dialog.glade"
 
 typedef struct {
 	GooWindow   *window;
-	GooPlayer   *player_cd;
+	GooPlayer   *player;
 	GList       *tracks;
 	GList       *selected_tracks;
-
-	GladeXML    *gui;
-
+	GtkBuilder  *builder;
 	GtkWidget   *dialog;
-	GtkWidget   *e_alltrack_radiobutton;
-	GtkWidget   *e_selected_radiobutton;
 } DialogData;
 
 
-/* called when the main dialog is closed. */
 static void
-destroy_cb (GtkWidget  *widget, 
-	    DialogData *data)
+dialog_destroy_cb (GtkWidget  *widget,
+		   DialogData *data)
 {
 	track_list_free (data->selected_tracks);
 	track_list_free (data->tracks);
-	g_object_unref (data->player_cd);
-	g_object_unref (data->gui);
+	g_object_unref (data->player);
+	g_object_unref (data->builder);
 	g_free (data);
 }
 
 
-static gboolean
-dlg_extract__start_ripping (gpointer callback_data)
+static void
+ok_button_clicked_cb (GtkWidget  *widget,
+		      DialogData *data)
 {
-	GtkWidget  *dialog = callback_data;
-	DialogData *data;
-	GList      *tracks_to_rip;
+	GList *tracks_to_rip;
 
-	data = g_object_get_data (G_OBJECT (dialog), "dialog_data");
+	gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
+	gtk_widget_hide (data->dialog);
 
-	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton)))
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("selected_tracks_radiobutton"))))
 		tracks_to_rip = track_list_dup (data->selected_tracks);
 	else
-		tracks_to_rip = track_list_dup (goo_player_get_album (data->player_cd)->tracks);
+		tracks_to_rip = track_list_dup (goo_player_get_album (data->player)->tracks);
 
 	dlg_ripper (data->window, tracks_to_rip);
 	
 	track_list_free (tracks_to_rip);
 	gtk_widget_destroy (data->dialog);
-	
-	return FALSE;
-}
-
-
-/* called when the "ok" button is clicked. */
-static void
-ok_cb (GtkWidget  *widget, 
-       DialogData *data)
-{
-	gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
-	gtk_widget_hide (data->dialog); 
-
-	g_idle_add (dlg_extract__start_ripping, data->dialog);		
 }
 
 
-/* called when the "help" button is clicked. */
 static void
-help_cb (GtkWidget  *widget, 
-	 DialogData *data)
+help_button_clicked_cb (GtkWidget  *widget,
+			DialogData *data)
 {
-	GError *err;
-
-	err = NULL;  
-	gnome_help_display ("goobox", "extract", &err);
-	
-	if (err != NULL) {
-		GtkWidget *dialog;
-		
-		dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
-						 GTK_DIALOG_MODAL,
-						 GTK_MESSAGE_ERROR,
-						 GTK_BUTTONS_CLOSE,
-						 _("Could not display help: %s"),
-						 err->message);
-		
-		g_signal_connect (G_OBJECT (dialog), "response",
-				  G_CALLBACK (gtk_widget_destroy),
-				  NULL);
-		
-		gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-		gtk_widget_show (dialog);
-		
-		g_error_free (err);
-	}
+	show_help_dialog (GTK_WINDOW (data->window), "extract");
 }
 
 
-/* create the main dialog. */
 void
 dlg_extract_ask (GooWindow *window)
 {
-	GstElement       *encoder;
-	gboolean          ogg_encoder, flac_encoder, wave_encoder;
-	DialogData       *data;
-	GtkWidget        *btn_ok;
-	GtkWidget        *btn_cancel;
-	GtkWidget        *btn_help;
-	int               selected;
+	GstElement *encoder;
+	gboolean    ogg_encoder;
+	gboolean    flac_encoder;
+	gboolean    wave_encoder;
+	DialogData *data;
+	int         selected;
 
 	encoder = gst_element_factory_make (OGG_ENCODER, "encoder");
 	ogg_encoder = encoder != NULL;
@@ -172,7 +112,7 @@ dlg_extract_ask (GooWindow *window)
 	if (encoder != NULL) 
 		gst_object_unref (GST_OBJECT (encoder));
 
-	if (!ogg_encoder && !flac_encoder && !wave_encoder) {
+	if (! ogg_encoder && ! flac_encoder && ! wave_encoder) {
 		GtkWidget *d;
 		char      *msg;
 		
@@ -205,66 +145,45 @@ dlg_extract_ask (GooWindow *window)
 
 	data = g_new0 (DialogData, 1);
 	data->window = window;
-	data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_EXTRACT_FILE, NULL, NULL);
-        if (!data->gui) {
-		g_warning ("Could not find " GLADE_EXTRACT_FILE "\n");
-		g_free (data);
-                return;
-        }
-
+	data->builder = _gtk_builder_new_from_file ("extract.ui", "");
 	data->tracks = goo_window_get_tracks (window, FALSE);
 	data->selected_tracks = goo_window_get_tracks (window, TRUE);
-	data->player_cd = goo_window_get_player (window);
-	g_object_ref (data->player_cd);
+	data->player = g_object_ref (goo_window_get_player (window));
 
 	eel_gconf_preload_cache ("/apps/goobox/dialogs/extract", GCONF_CLIENT_PRELOAD_ONELEVEL);
 
 	/* Get the widgets. */
 
-	data->dialog = glade_xml_get_widget (data->gui, "extract_dialog");
-	data->e_alltrack_radiobutton = glade_xml_get_widget (data->gui, "e_alltrack_radiobutton");
-	data->e_selected_radiobutton = glade_xml_get_widget (data->gui, "e_selected_radiobutton");
-
-	btn_ok = glade_xml_get_widget (data->gui, "e_okbutton");
-	btn_cancel = glade_xml_get_widget (data->gui, "e_cancelbutton");
-	btn_help = glade_xml_get_widget (data->gui, "e_helpbutton");
-
-	gtk_button_set_use_stock (GTK_BUTTON (btn_ok), TRUE);
-	gtk_button_set_label (GTK_BUTTON (btn_ok), GOO_STOCK_EXTRACT);
+	data->dialog = GET_WIDGET ("extract_dialog");
+	gtk_button_set_use_stock (GTK_BUTTON (GET_WIDGET ("ok_button")), TRUE);
+	gtk_button_set_label (GTK_BUTTON (GET_WIDGET ("ok_button")), GOO_STOCK_EXTRACT);
 
 	/* Set widgets data. */
 
-	g_object_set_data (G_OBJECT (data->dialog), "dialog_data", data);
-
-	/* FIXME: use this property or delete it. */
-	if (eel_gconf_get_boolean (PREF_EXTRACT_FIRST_TIME, TRUE)) {
-		eel_gconf_set_boolean (PREF_EXTRACT_FIRST_TIME, FALSE);
-	} 
-
 	selected = g_list_length (data->selected_tracks);
-	gtk_widget_set_sensitive (data->e_selected_radiobutton, selected > 0);
+	gtk_widget_set_sensitive (GET_WIDGET ("selected_tracks_radiobutton"), selected > 0);
 	if (selected <= 1)
-		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_alltrack_radiobutton), TRUE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("all_tracks_radiobutton")), TRUE);
 	else
-		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton), TRUE);
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("selected_tracks_radiobutton")), TRUE);
 
 	/* Set the signals handlers. */
 
-	g_signal_connect (G_OBJECT (data->dialog), 
+	g_signal_connect (data->dialog,
 			  "destroy",
-			  G_CALLBACK (destroy_cb),
+			  G_CALLBACK (dialog_destroy_cb),
 			  data);
-	g_signal_connect_swapped (G_OBJECT (btn_cancel), 
+	g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
 				  "clicked",
 				  G_CALLBACK (gtk_widget_destroy),
 				  G_OBJECT (data->dialog));
-	g_signal_connect (G_OBJECT (btn_help), 
+	g_signal_connect (GET_WIDGET ("help_button"),
 			  "clicked",
-			  G_CALLBACK (help_cb),
+			  G_CALLBACK (help_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (btn_ok), 
+	g_signal_connect (GET_WIDGET ("ok_button"),
 			  "clicked",
-			  G_CALLBACK (ok_cb),
+			  G_CALLBACK (ok_button_clicked_cb),
 			  data);
 
 	/* run dialog. */
@@ -282,6 +201,7 @@ dlg_extract_selected (GooWindow *window)
 	
 	tracks_to_rip = goo_window_get_tracks (window, TRUE);
 	dlg_ripper (window, tracks_to_rip);
+
 	track_list_free (tracks_to_rip);
 }
 
@@ -289,17 +209,13 @@ dlg_extract_selected (GooWindow *window)
 void
 dlg_extract (GooWindow *window)
 {
-	/* FIXME
 	GList *selected_tracks;
-	int    n_selected_tracks;
 	
 	selected_tracks = goo_window_get_tracks (window, TRUE);
-	n_selected_tracks = g_list_length (selected_tracks);
-	track_list_free (selected_tracks);
-	
-	if (n_selected_tracks <= 1)
+	if (g_list_length (selected_tracks) < 1)
 		dlg_ripper (window, NULL);
-	else*/
-	
-	dlg_extract_ask (window);
+	else
+		dlg_extract_ask (window);
+
+	track_list_free (selected_tracks);
 }
diff --git a/src/dlg-preferences.c b/src/dlg-preferences.c
index d0f58de..d993b7e 100644
--- a/src/dlg-preferences.c
+++ b/src/dlg-preferences.c
@@ -3,7 +3,7 @@
 /*
  *  Goo
  *
- *  Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2001-2009 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,144 +21,100 @@
  */
 
 #include <config.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
+#include <brasero/brasero-drive-selection.h>
 #include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <glade/glade.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
 #include <gst/gst.h>
-#include "main.h"
 #include "gconf-utils.h"
-#include "typedefs.h"
 #include "goo-window.h"
+#include "gtk-utils.h"
 #include "preferences.h"
-#include "bacon-cd-selection.h"
-#include "goo-stock.h"
-#include "file-utils.h"
+#include "typedefs.h"
+
+#define N_VALUES 10
+#define DEFAULT_OGG_QUALITY 0.5
+#define DEFAULT_FLAC_COMPRESSION 5
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
 
-#define GLADE_PREF_FILE "preferences.glade"
-#define GLADE_FORMAT_FILE "format_dialog.glade"
 
-enum { TEXT_COLUMN, DATA_COLUMN, PRESENT_COLUMN, N_COLUMNS };
+enum {
+	TEXT_COLUMN,
+	DATA_COLUMN,
+	PRESENT_COLUMN,
+	N_COLUMNS
+};
+
 
-/*static int ogg_rate[] = { 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };*/
 static int flac_compression[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
 
-#define N_VALUES 10
-#define DEFAULT_OGG_QUALITY 0.5
-#define DEFAULT_FLAC_COMPRESSION 5
 
 typedef struct {
 	GooWindow    *window;
-	int           ogg_value;
-	int           flac_value;
-
-	GladeXML     *gui;
-
+	GtkBuilder   *builder;
 	GtkWidget    *dialog;
 	GtkWidget    *drive_selector;
-
-	GtkWidget    *p_destination_filechooserbutton;
-	GtkWidget    *p_autoplay_checkbutton;
-
+	GtkWidget    *filetype_combobox;
 	GtkTreeModel *filetype_model;
-	GtkWidget    *p_filetype_combobox;
-	GtkWidget    *p_encoding_notebook;
-	GtkWidget    *p_filetype_properties_button;
-	GtkWidget    *p_save_playlist_checkbutton;
+	int           ogg_value;
+	int           flac_value;
 } DialogData;
 
 
-/* called when the "apply" button is clicked. */
 static void
-apply_cb (GtkWidget  *widget, 
-	  DialogData *data)
+apply_button_clicked_cb (GtkWidget  *widget,
+			 DialogData *data)
 {
-	const char    *destination;
-	const char    *device;
-	const char    *current_device;
+	const char   *destination;
+	BraseroDrive *br_drive;
+	const char   *device;
 
-	destination = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->p_destination_filechooserbutton));
-	eel_gconf_set_path (PREF_EXTRACT_DESTINATION, destination);
+	destination = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (GET_WIDGET ("destination_filechooserbutton")));
+	eel_gconf_set_uri (PREF_EXTRACT_DESTINATION, destination);
 
-	pref_set_file_format (gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_filetype_combobox)));
-	eel_gconf_set_boolean (PREF_EXTRACT_SAVE_PLAYLIST, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->p_save_playlist_checkbutton)));
-	eel_gconf_set_boolean (PREF_GENERAL_AUTOPLAY, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->p_autoplay_checkbutton)));
+	pref_set_file_format (gtk_combo_box_get_active (GTK_COMBO_BOX (data->filetype_combobox)));
+	eel_gconf_set_boolean (PREF_EXTRACT_SAVE_PLAYLIST, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("save_playlist_checkbutton"))));
+	eel_gconf_set_boolean (PREF_GENERAL_AUTOPLAY, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("autoplay_checkbutton"))));
 	
 	/**/
 
-	device = bacon_cd_selection_get_device (BACON_CD_SELECTION (data->drive_selector));
-	if (device == NULL) 
+	br_drive = brasero_drive_selection_get_active (BRASERO_DRIVE_SELECTION (data->drive_selector));
+	if (br_drive == NULL)
 		return;
-		
-	current_device = goo_player_get_device (goo_window_get_player (data->window));
-	
-	if ((current_device != NULL) && (strcmp (current_device, device) == 0)) 
-		return;
-		
+
+	device = brasero_drive_get_device (br_drive);
 	eel_gconf_set_string (PREF_GENERAL_DEVICE, device);
 	goo_window_set_device (data->window, device);
 	goo_window_update (data->window);
+
+	g_object_unref (br_drive);
 }
 
 
-/* called when the main dialog is closed. */
 static void
-destroy_cb (GtkWidget  *widget, 
-	    DialogData *data)
+dialog_destroy_cb (GtkWidget  *widget,
+		   DialogData *data)
 {
-	apply_cb (widget, data);
+	apply_button_clicked_cb (widget, data);
 	data->window->preferences_dialog = NULL;
-	g_object_unref (G_OBJECT (data->gui));
+
+	g_object_unref (data->builder);
 	g_free (data);
 }
 
 
-/* called when the "close" button is clicked. */
 static void
-close_cb (GtkWidget  *widget, 
-	  DialogData *data)
+close_button_clicked_cb (GtkWidget  *widget,
+			 DialogData *data)
 {
-	apply_cb (widget, data);
+	apply_button_clicked_cb (widget, data);
 	gtk_widget_destroy (data->dialog);
 }
 
 
-/* called when the "help" button is clicked. */
 static void
-help_cb (GtkWidget  *widget, 
-	 DialogData *data)
+help_button_clicked_cb (GtkWidget  *widget,
+			DialogData *data)
 {
-	GError *err;
-
-	err = NULL;  
-	gnome_help_display ("goo", "preferences", &err);
-	
-	if (err != NULL) {
-		GtkWidget *dialog;
-		
-		dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
-						 GTK_DIALOG_MODAL,
-						 GTK_MESSAGE_ERROR,
-						 GTK_BUTTONS_CLOSE,
-						 _("Could not display help: %s"),
-						 err->message);
-		
-		g_signal_connect (G_OBJECT (dialog), "response",
-				  G_CALLBACK (gtk_widget_destroy),
-				  NULL);
-		
-		gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-		
-		gtk_widget_show (dialog);
-		
-		g_error_free (err);
-	}
+	show_help_dialog (GTK_WINDOW (data->window), "preferences");
 }
 
 
@@ -166,13 +122,10 @@ void dlg_format (DialogData *dialog_data, GooFileFormat format);
 
 
 static void
-filetype_properties_clicked_cb	(GtkWidget  *widget, 
-				 DialogData *data)
+filetype_properties_clicked_cb (GtkWidget  *widget,
+			        DialogData *data)
 {
-	int format;
-	
-	format = gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_filetype_combobox));
-	dlg_format (data, format);	
+	dlg_format (data, gtk_combo_box_get_active (GTK_COMBO_BOX (data->filetype_combobox)));
 }
 
 
@@ -181,7 +134,7 @@ drive_selector_device_changed_cb (GtkOptionMenu *option_menu,
 				  const char    *device_path,
 				  DialogData    *data)
 {
-	apply_cb (NULL, data);
+	apply_button_clicked_cb (NULL, data);
 }
 
 
@@ -191,9 +144,9 @@ filetype_combobox_changed_cb (GtkComboBox *widget,
 {
 	int format;
 	
-	format = gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_filetype_combobox));
-	gtk_notebook_set_current_page (GTK_NOTEBOOK (data->p_encoding_notebook), format);
-	gtk_widget_set_sensitive (data->p_filetype_properties_button, format != GOO_FILE_FORMAT_WAVE);
+	format = gtk_combo_box_get_active (GTK_COMBO_BOX (data->filetype_combobox));
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (GET_WIDGET ("encoding_notebook")), format);
+	gtk_widget_set_sensitive (GET_WIDGET ("filetype_properties_button"), format != GOO_FILE_FORMAT_WAVE);
 }
 
 
@@ -202,12 +155,11 @@ set_description_label (DialogData *data,
 		       const char *widget_name, 
 		       const char *label_text)
 {
-	GtkWidget *label;
-	char      *text;
+	char *text;
 	
-	label = glade_xml_get_widget (data->gui, widget_name);
 	text = g_markup_printf_escaped ("<small><i>%s</i></small>", label_text);
-	gtk_label_set_markup (GTK_LABEL (label), text);
+	gtk_label_set_markup (GTK_LABEL (GET_WIDGET (widget_name)), text);
+
 	g_free (text);
 }
 
@@ -216,12 +168,6 @@ void
 dlg_preferences (GooWindow *window)
 {
 	DialogData      *data;
-	GtkWidget       *btn_close;
-	GtkWidget       *btn_help;
-	GtkWidget       *box;
-	GtkWidget       *filetype_combobox_box;
-	GtkWidget       *p_filetype_properties_button;
-	char            *device = NULL;
 	char            *destination = NULL;
 	GooFileFormat    file_format;
 	GstElement      *encoder;
@@ -237,49 +183,29 @@ dlg_preferences (GooWindow *window)
         
 	data = g_new0 (DialogData, 1);
 	data->window = window;
-	data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_PREF_FILE, NULL, NULL);
-        if (!data->gui) {
-                g_warning ("Could not find " GLADE_PREF_FILE "\n");
-		g_free (data);
-                return;
-        }
+	data->builder = _gtk_builder_new_from_file ("preferences.ui", "");
 
 	eel_gconf_preload_cache ("/apps/goobox/general", GCONF_CLIENT_PRELOAD_ONELEVEL);
 
 	/* Get the widgets. */
 
-	data->dialog = glade_xml_get_widget (data->gui, "preferences_dialog");
+	data->dialog = GET_WIDGET ("preferences_dialog");
 	window->preferences_dialog = data->dialog;
 
-	data->p_destination_filechooserbutton = glade_xml_get_widget (data->gui, "p_destination_filechooserbutton");
-	data->p_autoplay_checkbutton = glade_xml_get_widget (data->gui, "p_autoplay_checkbutton");
-	
-	filetype_combobox_box = glade_xml_get_widget (data->gui, "filetype_combobox_box");
-	data->p_encoding_notebook = glade_xml_get_widget (data->gui, "p_encoding_notebook");
-	data->p_filetype_properties_button = glade_xml_get_widget (data->gui, "p_filetype_properties_button");
-	data->p_save_playlist_checkbutton = glade_xml_get_widget (data->gui, "p_save_playlist_checkbutton");
-	p_filetype_properties_button = glade_xml_get_widget (data->gui, "p_filetype_properties_button");
-	
-	box = glade_xml_get_widget (data->gui, "p_drive_selector_box");
-	btn_close = glade_xml_get_widget (data->gui, "p_closebutton");
-	btn_help = glade_xml_get_widget (data->gui, "p_helpbutton");
-
 	/* Set widgets data. */
 
 	if (preferences_get_use_sound_juicer ()) {
 		GtkWidget *notebook;
 		GtkWidget *encoder_page;
-		GtkWidget *vbox;
 
-		notebook = glade_xml_get_widget (data->gui, "p_notebook");
+		notebook = GET_WIDGET ("notebook");
 		gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
 		gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
 
 		encoder_page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 1);
 		gtk_widget_hide (encoder_page);
 
-		vbox = glade_xml_get_widget (data->gui, "general_vbox");
-		gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
+		gtk_container_set_border_width (GTK_CONTAINER (GET_WIDGET ("general_vbox")), 0);
 	}
 
 	/* Extraction */
@@ -288,31 +214,26 @@ dlg_preferences (GooWindow *window)
                                                                    G_TYPE_STRING,
                                                                    G_TYPE_INT,
                                                                    G_TYPE_BOOLEAN));
-	data->p_filetype_combobox = gtk_combo_box_new_with_model (data->filetype_model);
+	data->filetype_combobox = gtk_combo_box_new_with_model (data->filetype_model);
 	
 	renderer = gtk_cell_renderer_text_new ();
-        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->p_filetype_combobox),
+        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (data->filetype_combobox),
                                     renderer,
                                     FALSE);
-        gtk_cell_layout_set_attributes  (GTK_CELL_LAYOUT (data->p_filetype_combobox),
+        gtk_cell_layout_set_attributes  (GTK_CELL_LAYOUT (data->filetype_combobox),
                                          renderer,
                                          "text", TEXT_COLUMN,
                                          "sensitive", PRESENT_COLUMN,
                                          NULL);
-	gtk_widget_show (data->p_filetype_combobox);
-	gtk_box_pack_start (GTK_BOX (filetype_combobox_box), data->p_filetype_combobox, TRUE, TRUE, 0);
+	gtk_widget_show (data->filetype_combobox);
+	gtk_box_pack_start (GTK_BOX (GET_WIDGET ("filetype_combobox_box")), data->filetype_combobox, TRUE, TRUE, 0);
 
 	/**/
 	
-	destination = eel_gconf_get_path (PREF_EXTRACT_DESTINATION, "");
-	if ((destination == NULL) || (strcmp (destination, "") == 0)) { 
-		char *tmp;
-		
-		tmp = xdg_user_dir_lookup ("MUSIC");
-		destination = get_uri_from_local_path (tmp);
-		g_free (tmp);
-	}	
-	gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (data->p_destination_filechooserbutton), destination);
+	destination = eel_gconf_get_uri (PREF_EXTRACT_DESTINATION, "");
+	if ((destination == NULL) || (strcmp (destination, "") == 0))
+		destination = g_filename_to_uri (g_get_user_special_dir (G_USER_DIRECTORY_MUSIC), NULL, NULL);
+	gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (GET_WIDGET ("destination_filechooserbutton")), destination);
 	g_free (destination);
 
 	encoder = gst_element_factory_make (OGG_ENCODER, "encoder");
@@ -366,7 +287,7 @@ dlg_preferences (GooWindow *window)
 			file_format = GOO_FILE_FORMAT_WAVE;
 	}
 	
-	gtk_combo_box_set_active (GTK_COMBO_BOX (data->p_filetype_combobox), file_format);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (data->filetype_combobox), file_format);
 	filetype_combobox_changed_cb (NULL, data);
 
 	/**/
@@ -375,46 +296,38 @@ dlg_preferences (GooWindow *window)
 	set_description_label (data, "flac_description_label", _("Free Lossless Audio Codec (FLAC) is an open source codec that compresses but does not degrade audio quality."));
 	set_description_label (data, "wave_description_label", _("WAV+PCM is a lossless format that holds uncompressed, raw pulse-code modulated (PCM) audio."));
 		
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->p_save_playlist_checkbutton), eel_gconf_get_boolean (PREF_EXTRACT_SAVE_PLAYLIST, TRUE));
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->p_autoplay_checkbutton), eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE));
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("save_playlist_checkbutton")), eel_gconf_get_boolean (PREF_EXTRACT_SAVE_PLAYLIST, TRUE));
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("autoplay_checkbutton")), eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE));
 	
 	/**/
 
-	data->drive_selector = bacon_cd_selection_new (Drives, goo_player_get_drive (goo_window_get_player (window)));
+	data->drive_selector = brasero_drive_selection_new ();
 	gtk_widget_show (data->drive_selector);
-	gtk_box_pack_start (GTK_BOX (box), data->drive_selector, TRUE, TRUE, 0);
-	
-	device = eel_gconf_get_string (PREF_GENERAL_DEVICE, bacon_cd_selection_get_default_device (BACON_CD_SELECTION (data->drive_selector)));
-	bacon_cd_selection_set_device (BACON_CD_SELECTION (data->drive_selector), device);
-	g_free (device);
+	gtk_box_pack_start (GTK_BOX (GET_WIDGET ("drive_selector_box")), data->drive_selector, TRUE, TRUE, 0);
 
 	/* Set the signals handlers. */
 
-	g_signal_connect (G_OBJECT (data->dialog), 
+	g_signal_connect (data->dialog,
 			  "destroy",
-			  G_CALLBACK (destroy_cb),
+			  G_CALLBACK (dialog_destroy_cb),
 			  data);
-
-	g_signal_connect (G_OBJECT (btn_close), 
+	g_signal_connect (GET_WIDGET ("close_button"),
 			  "clicked",
-			  G_CALLBACK (close_cb),
+			  G_CALLBACK (close_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (btn_help), 
+	g_signal_connect (GET_WIDGET ("help_button"),
 			  "clicked",
-			  G_CALLBACK (help_cb),
+			  G_CALLBACK (help_button_clicked_cb),
 			  data);
-
-	g_signal_connect (G_OBJECT (p_filetype_properties_button), 
+	g_signal_connect (GET_WIDGET ("filetype_properties_button"),
 			  "clicked",
 			  G_CALLBACK (filetype_properties_clicked_cb),
 			  data);
-
 	g_signal_connect (G_OBJECT (data->drive_selector), 
-			  "device_changed",
+			  "changed",
 			  G_CALLBACK (drive_selector_device_changed_cb),
 			  data);
-
-	g_signal_connect (G_OBJECT (data->p_filetype_combobox), 
+	g_signal_connect (data->filetype_combobox,
 			  "changed",
 			  G_CALLBACK (filetype_combobox_changed_cb),
 			  data);
@@ -434,7 +347,7 @@ typedef struct {
 	GooFileFormat  format;
 	int            value;
 	
-	GladeXML      *gui;
+	GtkBuilder    *builder;
 	GtkWidget     *dialog;
 	GtkWidget     *f_quality_label;
 	GtkWidget     *f_quality_scale;
@@ -445,7 +358,7 @@ static void
 format_dialog_destroy_cb (GtkWidget        *widget, 
 	    		  FormatDialogData *data)
 {
-	g_object_unref (G_OBJECT (data->gui));
+	g_object_unref (data->builder);
 	g_free (data);
 }
 
@@ -522,56 +435,34 @@ dlg_format (DialogData    *preferences_data,
 	    GooFileFormat  format)
 {
 	FormatDialogData *data;
-	GtkWidget  *btn_ok;
-        GtkWidget  *f_smaller_value_label;
-        GtkWidget  *f_bigger_value_label;
-        GtkWidget  *f_title_label;
-        GtkWidget  *f_description_label;
-        char       *text;
+        char             *text;
         
 	data = g_new0 (FormatDialogData, 1);
 	data->format = format;
-	data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_FORMAT_FILE, NULL, NULL);
-        if (!data->gui) {
-                g_warning ("Could not find " GLADE_FORMAT_FILE "\n");
-		g_free (data);
-                return;
-        }
-
-	/* Get the widgets. */
-
-	data->dialog = glade_xml_get_widget (data->gui, "format_dialog");
-
-	data->f_quality_label = glade_xml_get_widget (data->gui, "f_quality_label");
-	data->f_quality_scale = glade_xml_get_widget (data->gui, "f_quality_scale");
-	f_smaller_value_label = glade_xml_get_widget (data->gui, "f_smaller_value_label");
-	f_bigger_value_label = glade_xml_get_widget (data->gui, "f_bigger_value_label");
-	f_title_label = glade_xml_get_widget (data->gui, "f_title_label");
-	f_description_label = glade_xml_get_widget (data->gui, "f_description_label");
-	
-	btn_ok = glade_xml_get_widget (data->gui, "f_okbutton");
+	data->builder = _gtk_builder_new_from_file ("format-options.ui", "");
+	data->dialog = GET_WIDGET ("format_dialog");
 	
 	/* Set widgets data. */		
 
 	data->value = get_current_value (format);
-	gtk_range_set_value (GTK_RANGE (data->f_quality_scale), scale_value (data->value));
+	gtk_range_set_value (GTK_RANGE (GET_WIDGET ("quality_scale")), scale_value (data->value));
 
 	if (format == GOO_FILE_FORMAT_FLAC) {	
 		text = g_strdup_printf ("<small><i>%s</i></small>", _("Faster compression"));
-		gtk_label_set_markup (GTK_LABEL (f_smaller_value_label), text);
+		gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("smaller_value_label")), text);
 		g_free (text);
 
 		text = g_strdup_printf ("<small><i>%s</i></small>", _("Higher compression"));
-		gtk_label_set_markup (GTK_LABEL (f_bigger_value_label), text);
+		gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("bigger_value_label")), text);
 		g_free (text);
 	}
 	else {
 		text = g_strdup_printf ("<small><i>%s</i></small>", _("Smaller size"));
-		gtk_label_set_markup (GTK_LABEL (f_smaller_value_label), text);
+		gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("smaller_value_label")), text);
 		g_free (text);
 
 		text = g_strdup_printf ("<small><i>%s</i></small>", _("Higher quality"));
-		gtk_label_set_markup (GTK_LABEL (f_bigger_value_label), text);
+		gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("bigger_value_label")), text);
 		g_free (text);
 	}
 
@@ -586,7 +477,7 @@ dlg_format (DialogData    *preferences_data,
 		text = g_strdup ("");
 		break;
 	}
-	gtk_label_set_markup (GTK_LABEL (f_title_label), text);
+	gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("title_label")), text);
 	g_free (text);
 
 	switch (data->format) {
@@ -600,7 +491,7 @@ dlg_format (DialogData    *preferences_data,
 		text = "";
 		break;
 	}
-	gtk_label_set_text (GTK_LABEL (data->f_quality_label), text);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("quality_label")), text);
 
 	switch (data->format) {
 	case GOO_FILE_FORMAT_OGG:
@@ -616,20 +507,20 @@ dlg_format (DialogData    *preferences_data,
 		text = "";
 		break;
 	}
-	gtk_label_set_text (GTK_LABEL (f_description_label), text);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("description_label")), text);
 
 	/* Set the signals handlers. */
 
-	g_signal_connect (G_OBJECT (data->dialog), 
+	g_signal_connect (data->dialog,
 			  "destroy",
 			  G_CALLBACK (format_dialog_destroy_cb),
 			  data);
 
-	g_signal_connect (G_OBJECT (btn_ok), 
+	g_signal_connect (GET_WIDGET ("ok_button"),
 			  "clicked",
 			  G_CALLBACK (format_dialog_ok_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->f_quality_scale), 
+	g_signal_connect (GET_WIDGET ("quality_scale"),
 			  "value_changed",
 			  G_CALLBACK (format_dialog_scale_value_changed_cb),
 			  data);
diff --git a/src/dlg-properties.c b/src/dlg-properties.c
index 8eb2b67..de56ecd 100644
--- a/src/dlg-properties.c
+++ b/src/dlg-properties.c
@@ -3,7 +3,7 @@
 /*
  *  Goo
  *
- *  Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2001-2009 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,47 +21,42 @@
  */
 
 #include <config.h>
-
 #include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <glade/glade.h>
-#include "goo-stock.h"
 #include "dlg-properties.h"
+#include "goo-stock.h"
+#include "gtk-utils.h"
 #include "metadata.h"
 
-#define GLADE_PREF_FILE "properties.glade"
 
-enum { NUMBER_COLUMN, TITLE_COLUMN, ARTIST_COLUMN, DATA_COLUMN, N_COLUMNS };
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
+
+
+enum {
+	NUMBER_COLUMN,
+	TITLE_COLUMN,
+	ARTIST_COLUMN,
+	DATA_COLUMN,
+	N_COLUMNS
+};
+
 
 typedef struct {
 	GooWindow         *window;
-	GladeXML          *gui;
 	GtkWidget         *dialog;
+	GtkBuilder        *builder;
 	GtkListStore      *list_store; 
 	GtkTreeViewColumn *author_column;
-	GtkWidget         *p_title_entry;
-	GtkWidget         *p_artist_entry;
-	GtkWidget         *p_artist_combobox;
-	GtkWidget         *p_year_spinbutton;
-	GtkWidget         *p_year_checkbutton;
-	GtkWidget         *p_info_label;
-	GtkWidget         *p_info_box;
-	GtkWidget         *p_navigation_box;
-	GtkWidget         *p_album_label;
-	GtkWidget         *p_track_treeview;
-	
 	GList             *albums;
 	int                n_albums, current_album;
 } DialogData;
 
 
-/* called when the main dialog is closed. */
 static void
-destroy_cb (GtkWidget  *widget, 
-	    DialogData *data)
+dialog_destroy_cb (GtkWidget  *widget,
+		   DialogData *data)
 {
 	data->window->properties_dialog = NULL;
-	g_object_unref (G_OBJECT (data->gui));
+	g_object_unref (G_OBJECT (data->builder));
 	g_free (data);
 }
 
@@ -74,15 +69,15 @@ set_album_from_data (DialogData *data)
 	GtkTreeIter  iter;
 		
 	album = album_info_copy (goo_window_get_album (data->window));
-	album_info_set_title (album, gtk_entry_get_text (GTK_ENTRY (data->p_title_entry)));
-	album_artist = gtk_entry_get_text (GTK_ENTRY (data->p_artist_entry));
+	album_info_set_title (album, gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("title_entry"))));
+	album_artist = gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("artist_entry")));
 	album_info_set_artist (album, album_artist, "");
-	album->various_artist = gtk_combo_box_get_active (GTK_COMBO_BOX (data->p_artist_combobox)) == 1;
+	album->various_artist = gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("artist_combobox"))) == 1;
 	
-	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->p_year_checkbutton))) {
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("year_checkbutton")))) {
 		GDate *date;
 		
-		date = g_date_new_dmy (1, 1, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (data->p_year_spinbutton)));
+		date = g_date_new_dmy (1, 1, gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (GET_WIDGET ("year_spinbutton"))));
 		album_info_set_release_date (album, date);
 		g_date_free (date);
 	}
@@ -128,8 +123,8 @@ set_album_from_data (DialogData *data)
 
 
 static void
-ok_cb (GtkWidget  *widget, 
-       DialogData *data)
+ok_button_clicked_cb (GtkWidget  *widget,
+		      DialogData *data)
 {
 	set_album_from_data (data);
 	gtk_widget_destroy (data->dialog);
@@ -137,34 +132,10 @@ ok_cb (GtkWidget  *widget,
 
 
 static void
-help_cb (GtkWidget  *widget, 
-	 DialogData *data)
+help_button_clicked_cb (GtkWidget  *widget,
+			DialogData *data)
 {
-	GError *err;
-
-	err = NULL;  
-	gnome_help_display ("goo", "properties", &err);
-	
-	if (err != NULL) {
-		GtkWidget *dialog;
-		
-		dialog = gtk_message_dialog_new (GTK_WINDOW (data->dialog),
-						 GTK_DIALOG_MODAL,
-						 GTK_MESSAGE_ERROR,
-						 GTK_BUTTONS_CLOSE,
-						 _("Could not display help: %s"),
-						 err->message);
-		
-		g_signal_connect (G_OBJECT (dialog), "response",
-				  G_CALLBACK (gtk_widget_destroy),
-				  NULL);
-		
-		gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
-		
-		gtk_widget_show (dialog);
-		
-		g_error_free (err);
-	}
+	show_help_dialog (GTK_WINDOW (data->window), "properties");
 }
 
 
@@ -174,17 +145,17 @@ set_data_from_album (DialogData *data,
 {
 	GList *scan;
 	
-	gtk_combo_box_set_active (GTK_COMBO_BOX (data->p_artist_combobox), album->various_artist ? 1 : 0);
+	gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("artist_combobox")), album->various_artist ? 1 : 0);
 	
 	if (album->title != NULL)
-		gtk_entry_set_text (GTK_ENTRY (data->p_title_entry), album->title);
+		gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("title_entry")), album->title);
 	if (album->artist != NULL)
-		gtk_entry_set_text (GTK_ENTRY (data->p_artist_entry), album->artist);
-	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->p_year_checkbutton), g_date_valid (album->release_date));
+		gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("artist_entry")), album->artist);
+	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("year_checkbutton")), g_date_valid (album->release_date));
 	if (g_date_valid (album->release_date))
-		gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->p_year_spinbutton), g_date_get_year (album->release_date));
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("year_spinbutton")), g_date_get_year (album->release_date));
 	else	
-		gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->p_year_spinbutton), 0);
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("year_spinbutton")), 0);
 
 	gtk_list_store_clear (data->list_store);
 	for (scan = album->tracks; scan; scan = scan->next) {
@@ -212,11 +183,11 @@ show_album (DialogData *data,
 		return;
 	data->current_album = n;
 		
-	gtk_widget_hide (data->p_info_box);
-	gtk_widget_show (data->p_navigation_box);
+	gtk_widget_hide (GET_WIDGET ("info_box"));
+	gtk_widget_show (GET_WIDGET ("navigation_box"));
 		
 	s = g_strdup_printf (_("Album %d of %d"), data->current_album + 1, data->n_albums); 
-	gtk_label_set_text (GTK_LABEL (data->p_album_label), s);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("album_label")), s);
 	g_free (s);
 	
 	set_data_from_album (data, g_list_nth (data->albums, n)->data);
@@ -264,7 +235,7 @@ search_cb (GtkWidget  *widget,
 	mb_SetDepth (mb, 4);
 	/*mb_SetMaxItems(mb, 10);*/
 	
-	mb_args[0] = (char*) gtk_entry_get_text (GTK_ENTRY (data->p_title_entry));
+	mb_args[0] = (char*) gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("title_entry")));
 	mb_args[1] = NULL;
 	if (! mb_QueryWithArgs (mb, MBQ_FindAlbumByName, mb_args)) {
 		char  mb_error[1024];
@@ -272,7 +243,7 @@ search_cb (GtkWidget  *widget,
 		
 	        mb_GetQueryError (mb, mb_error, sizeof (mb_error));
 	        s = g_strdup_printf (_("Search failed: %s\n"), mb_error);
-        	gtk_label_set_text (GTK_LABEL (data->p_info_label), s);
+        	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("info_label")), s);
         	g_free (s);
 	}
 	else {	
@@ -281,9 +252,9 @@ search_cb (GtkWidget  *widget,
 		data->n_albums = g_list_length (data->albums);
 	
 		if (data->n_albums == 0) { 
-			gtk_label_set_text (GTK_LABEL (data->p_info_label), _("No album found"));
-			gtk_widget_show (data->p_info_box);
-			gtk_widget_hide (data->p_navigation_box);
+			gtk_label_set_text (GTK_LABEL (GET_WIDGET ("info_label")), _("No album found"));
+			gtk_widget_show (GET_WIDGET ("info_box"));
+			gtk_widget_hide (GET_WIDGET ("navigation_box"));
 		} 
 		else 
 			show_album (data, 0);
@@ -431,11 +402,11 @@ artist_combobox_changed_cb (GtkComboBox *widget,
 {
 	gboolean single_artist = gtk_combo_box_get_active (widget) == 0;
 	gtk_tree_view_column_set_visible (data->author_column, ! single_artist);
-	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (data->p_track_treeview), ! single_artist);
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (GET_WIDGET ("track_treeview")), ! single_artist);
 	if (single_artist)
-		gtk_widget_show (data->p_artist_entry);
+		gtk_widget_show (GET_WIDGET ("artist_entry"));
 	else
-		gtk_widget_hide (data->p_artist_entry);
+		gtk_widget_hide (GET_WIDGET ("artist_entry"));
 }
 
 
@@ -456,11 +427,11 @@ next_album_button_clicked_cb (GtkButton  *button,
 
 
 static void
-reset_album_button_clicked_cb (GtkButton  *button,
-                               DialogData *data)
+undo_button_clicked_cb (GtkButton  *button,
+                        DialogData *data)
 {
-	gtk_widget_hide (data->p_info_box);
-	gtk_widget_hide (data->p_navigation_box);
+	gtk_widget_hide (GET_WIDGET ("info_box"));
+	gtk_widget_hide (GET_WIDGET ("navigation_box"));
 	set_data_from_album (data, goo_window_get_album (data->window));
 }
 
@@ -469,7 +440,7 @@ static void
 year_checkbutton_toggled_cb (GtkToggleButton *button,
                              DialogData      *data)
 {
-	gtk_widget_set_sensitive (data->p_year_spinbutton, gtk_toggle_button_get_active (button));
+	gtk_widget_set_sensitive (GET_WIDGET ("year_spinbutton"), gtk_toggle_button_get_active (button));
 }
 
 
@@ -477,13 +448,6 @@ void
 dlg_properties (GooWindow *window)
 {
 	DialogData *data;
-	GtkWidget  *btn_cancel;
-	GtkWidget  *btn_ok;
-	GtkWidget  *btn_help;
-	GtkWidget  *btn_search;
-        GtkWidget  *p_prev_album_button;
-        GtkWidget  *p_next_album_button;
-        GtkWidget  *p_reset_album_button;
         GtkWidget  *image;
         
         if (window->properties_dialog != NULL) {
@@ -493,42 +457,17 @@ dlg_properties (GooWindow *window)
         
 	data = g_new0 (DialogData, 1);
 	data->window = window;
-	data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_PREF_FILE, NULL, NULL);
-        if (!data->gui) {
-                g_warning ("Could not find " GLADE_PREF_FILE "\n");
-		g_free (data);
-                return;
-        }
+	data->builder = _gtk_builder_new_from_file ("properties.ui", "");
 
 	/* Get the widgets. */
 
-	data->dialog = glade_xml_get_widget (data->gui, "properties_dialog");
+	data->dialog = GET_WIDGET ("properties_dialog");
 	window->properties_dialog = data->dialog;
 
-	data->p_artist_entry = glade_xml_get_widget (data->gui, "p_artist_entry");
-	data->p_artist_combobox = glade_xml_get_widget (data->gui, "p_artist_combobox");
-	data->p_title_entry = glade_xml_get_widget (data->gui, "p_title_entry");
-	data->p_info_box = glade_xml_get_widget (data->gui, "p_info_box");
-	data->p_info_label = glade_xml_get_widget (data->gui, "p_info_label");
-	data->p_navigation_box = glade_xml_get_widget (data->gui, "p_navigation_box");
-	data->p_album_label = glade_xml_get_widget (data->gui, "p_album_label");
-	data->p_year_spinbutton = glade_xml_get_widget (data->gui, "p_year_spinbutton");
-	data->p_year_checkbutton = glade_xml_get_widget (data->gui, "p_year_checkbutton");
-	
-	btn_ok = glade_xml_get_widget (data->gui, "p_okbutton");
-	btn_cancel = glade_xml_get_widget (data->gui, "p_cancelbutton");
-	btn_help = glade_xml_get_widget (data->gui, "p_helpbutton");
-	btn_search = glade_xml_get_widget (data->gui, "p_search_button");
-	data->p_track_treeview = glade_xml_get_widget (data->gui, "p_track_treeview");
-	
-	p_prev_album_button = glade_xml_get_widget (data->gui, "p_prev_album_button");
-	p_next_album_button = glade_xml_get_widget (data->gui, "p_next_album_button");
-	p_reset_album_button = glade_xml_get_widget (data->gui, "p_undobutton");
-	
-	/**/
+	/* Set widgets data. */
 	
 	image = gtk_image_new_from_stock (GOO_STOCK_RESET, GTK_ICON_SIZE_BUTTON);
-	g_object_set (p_reset_album_button, 
+	g_object_set (GET_WIDGET ("undo_button"),
 		      "use_stock", TRUE,
 		      "label", GOO_STOCK_RESET,
 		      "image", image,
@@ -539,48 +478,48 @@ dlg_properties (GooWindow *window)
 					       G_TYPE_STRING,
 					       G_TYPE_STRING,
 					       G_TYPE_POINTER);
-	gtk_tree_view_set_model (GTK_TREE_VIEW (data->p_track_treeview), GTK_TREE_MODEL (data->list_store));
-	add_columns (data, GTK_TREE_VIEW (data->p_track_treeview));
+	gtk_tree_view_set_model (GTK_TREE_VIEW (GET_WIDGET ("track_treeview")), GTK_TREE_MODEL (data->list_store));
+	add_columns (data, GTK_TREE_VIEW (GET_WIDGET ("track_treeview")));
 	
 	/* Set the signals handlers. */
 
 	g_signal_connect (G_OBJECT (data->dialog), 
 			  "destroy",
-			  G_CALLBACK (destroy_cb),
+			  G_CALLBACK (dialog_destroy_cb),
 			  data);
-	g_signal_connect_swapped (G_OBJECT (btn_cancel), 
+	g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
 			  	  "clicked",
 			  	  G_CALLBACK (gtk_widget_destroy),
 			  	  data->dialog);
-	g_signal_connect (G_OBJECT (btn_ok), 
+	g_signal_connect (GET_WIDGET ("ok_button"),
 			  "clicked",
-			  G_CALLBACK (ok_cb),
+			  G_CALLBACK (ok_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (btn_help), 
+	g_signal_connect (GET_WIDGET ("help_button"),
 			  "clicked",
-			  G_CALLBACK (help_cb),
+			  G_CALLBACK (help_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (btn_search), 
+	g_signal_connect (GET_WIDGET ("search_button"),
 			  "clicked",
 			  G_CALLBACK (search_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->p_artist_combobox), 
+	g_signal_connect (GET_WIDGET ("artist_combobox"),
 			  "changed",
 			  G_CALLBACK (artist_combobox_changed_cb),
 			  data);
-	g_signal_connect (G_OBJECT (p_prev_album_button), 
+	g_signal_connect (GET_WIDGET ("prev_album_button"),
 			  "clicked",
 			  G_CALLBACK (prev_album_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (p_next_album_button), 
+	g_signal_connect (GET_WIDGET ("next_album_button"),
 			  "clicked",
 			  G_CALLBACK (next_album_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (p_reset_album_button), 
+	g_signal_connect (GET_WIDGET ("undo_button"),
 			  "clicked",
-			  G_CALLBACK (reset_album_button_clicked_cb),
+			  G_CALLBACK (undo_button_clicked_cb),
 			  data);
-	g_signal_connect (G_OBJECT (data->p_year_checkbutton), 
+	g_signal_connect (GET_WIDGET ("year_checkbutton"),
 			  "toggled",
                           G_CALLBACK (year_checkbutton_toggled_cb), 
                           data);
diff --git a/src/dlg-ripper.c b/src/dlg-ripper.c
index d22eeaa..413a9e7 100644
--- a/src/dlg-ripper.c
+++ b/src/dlg-ripper.c
@@ -21,34 +21,17 @@
  */
 
 #include <config.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
 #include <math.h>
-
+#include <brasero/brasero-drive.h>
 #include <gtk/gtk.h>
-#include <libgnome/libgnome.h>
-#include <libgnomeui/gnome-dialog.h>
-#include <libgnomeui/gnome-dialog-util.h>
-#include <libgnomeui/gnome-propertybox.h>
-#include <libgnomeui/gnome-pixmap.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
 #include <gst/gst.h>
-#include "typedefs.h"
-#include "main.h"
 #include "gconf-utils.h"
+#include "glib-utils.h"
 #include "gtk-utils.h"
-#include "file-utils.h"
-#include "typedefs.h"
-#include "goo-window.h"
+#include "main.h"
 #include "preferences.h"
-#include "track-info.h"
-#include "goo-player.h"
-#include "goo-cdrom.h"
+#include "typedefs.h"
+
 
 #define TOC_OFFSET 150
 #define GLADE_RIPPER_FILE "ripper_dialog.glade"
@@ -58,10 +41,11 @@
 #define DEFAULT_OGG_QUALITY 0.5
 #define DEFAULT_FLAC_COMPRESSION 5
 #define BUFFER_SIZE 1024
+#define GET_WIDGET(x) _gtk_builder_get_widget (data->builder, (x))
+
 
 typedef struct {
 	GooWindow     *window;
-	char          *device;
 	char          *destination;
 	GooFileFormat  format;
 	GList         *tracks;
@@ -69,7 +53,7 @@ typedef struct {
 	GList         *current_track;
 	int            current_track_n;
 	char          *ext;
-	GooCdrom      *cdrom;
+	BraseroDrive  *drive;
 	AlbumInfo     *album;
 
 	GstElement    *pipeline;
@@ -83,35 +67,31 @@ typedef struct {
 	int            total_sectors;
 	int            current_track_sectors;
 	int            prev_tracks_sectors;
-	char          *current_file;
+	GFile         *current_file;
 	gboolean       ripping;
 	GTimer        *timer;
 	double         prev_remaining_time;
 
-	GladeXML      *gui;
-
+	GtkBuilder    *builder;
 	GtkWidget     *dialog;
-	GtkWidget     *r_progress_progressbar;
-	GtkWidget     *r_track_label;
 } DialogData;
 
 
 /* From lame.h */
 typedef enum vbr_mode_e {
-  vbr_off=0,
-  vbr_mt,               /* obsolete, same as vbr_mtrh */
-  vbr_rh,
-  vbr_abr,
-  vbr_mtrh,
-  vbr_max_indicator,    /* Don't use this! It's used for sanity checks.       */  
-  vbr_default=vbr_rh    /* change this to change the default VBR mode of LAME */
+	vbr_off=0,
+	vbr_mt,               /* obsolete, same as vbr_mtrh */
+	vbr_rh,
+	vbr_abr,
+	vbr_mtrh,
+	vbr_max_indicator,    /* Don't use this! It's used for sanity checks.       */
+	vbr_default=vbr_rh    /* change this to change the default VBR mode of LAME */
 } vbr_mode;
 
 
-/* called when the main dialog is closed. */
 static void
-destroy_cb (GtkWidget  *widget, 
-	    DialogData *data)
+dialog_destroy_cb (GtkWidget  *widget,
+		   DialogData *data)
 {
 	if (data->update_handle != 0) {
 		g_source_remove (data->update_handle);
@@ -119,7 +99,7 @@ destroy_cb (GtkWidget  *widget,
 	}
 
 	if (data->ripping && (data->current_file != NULL))
-		gnome_vfs_unlink (data->current_file);
+		g_file_delete (data->current_file, NULL, NULL);
 
 	if (data->pipeline != NULL) {
 		gst_element_set_state (data->pipeline, GST_STATE_NULL);
@@ -129,15 +109,12 @@ destroy_cb (GtkWidget  *widget,
 	if (data->timer != NULL)
 		g_timer_destroy (data->timer);
 
-	goo_cdrom_unlock_tray (data->cdrom);
-	g_object_unref (data->cdrom);
-	
-	g_free (data->device);
+	brasero_drive_unlock (data->drive);
+	g_object_unref (data->drive);
 	g_free (data->destination);
-	g_free (data->current_file);
+	_g_object_unref (data->current_file);
 	track_list_free (data->tracks);
-
-	g_object_unref (data->gui);
+	g_object_unref (data->builder);
 	g_free (data);
 }
 
@@ -151,7 +128,7 @@ rip_next_track (gpointer callback_data)
 	DialogData *data = callback_data;
 	TrackInfo  *track;
 
-	g_free (data->current_file);
+	_g_object_unref (data->current_file);
 	data->current_file = NULL;
 
 	gst_element_set_state (data->encoder, GST_STATE_NULL);
@@ -178,7 +155,7 @@ update_ui (gpointer callback_data)
 
 	ripped_tracks = data->current_track_sectors + data->prev_tracks_sectors;
 	fraction = (double) ripped_tracks / data->total_sectors;
-	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->r_progress_progressbar), fraction);
+	gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (GET_WIDGET ("progress_progressbar")), fraction);
 
 	/* wait a bit before predicting the remaining time. */
 	if ((fraction < 10e-3) || (ripped_tracks < 250)) {
@@ -206,7 +183,7 @@ update_ui (gpointer callback_data)
 	}
 	
 	msg = g_strdup_printf (_("Extracting track: %d of %d %s"), data->current_track_n, data->n_tracks, (time_left != NULL ? time_left : ""));
-	gtk_progress_bar_set_text  (GTK_PROGRESS_BAR (data->r_progress_progressbar), msg);
+	gtk_progress_bar_set_text  (GTK_PROGRESS_BAR (GET_WIDGET ("progress_progressbar")), msg);
 
 	g_free (msg);
 	g_free (time_left);
@@ -283,10 +260,9 @@ create_pipeline (DialogData *data)
 	
 	data->pipeline = gst_pipeline_new ("pipeline");
 	
-	/*data->source = gst_element_make_from_uri (GST_URI_SRC, "cdda://1", "source");*/
 	data->source = gst_element_factory_make ("cdparanoiasrc", "source");
 	g_object_set (G_OBJECT (data->source), 
-		      "device", data->device, 
+		      "device", brasero_drive_get_device (data->drive),
 		      "read-speed", G_MAXINT,
 		      NULL);
 
@@ -299,19 +275,16 @@ create_pipeline (DialogData *data)
 	switch (data->format) {
 	case GOO_FILE_FORMAT_OGG:	
 		data->ext = "ogg";
-		
 		data->encoder = gst_element_factory_make (OGG_ENCODER, "encoder");
 		ogg_quality = eel_gconf_get_float (PREF_ENCODER_OGG_QUALITY, DEFAULT_OGG_QUALITY);
 		g_object_set (data->encoder,
 			      "quality", ogg_quality,
 			      NULL);
-		
 		data->container = gst_element_factory_make ("oggmux", "container");
 		break;
 
 	case GOO_FILE_FORMAT_FLAC:
 		data->ext = "flac";
-		
 		data->encoder = data->container = gst_element_factory_make (FLAC_ENCODER, "encoder");
 		flac_compression = eel_gconf_get_integer (PREF_ENCODER_FLAC_COMPRESSION, DEFAULT_FLAC_COMPRESSION);
 		g_object_set (data->encoder,
@@ -321,12 +294,11 @@ create_pipeline (DialogData *data)
 
 	case GOO_FILE_FORMAT_WAVE:
 		data->ext = "wav";
-		
 		data->encoder = data->container = gst_element_factory_make (WAVE_ENCODER, "encoder");
 		break;
 	}
 
-	data->sink = gst_element_factory_make ("gnomevfssink", "sink");
+	data->sink = gst_element_factory_make ("giosink", "sink");
 
 	if (data->encoder == data->container) {
 		gst_bin_add_many (GST_BIN (data->pipeline), data->source, queue, 
@@ -362,25 +334,102 @@ create_pipeline (DialogData *data)
 }
 
 
-static char*
-get_destination_folder (DialogData *data)
+static gboolean
+valid_filename_char (char c)
 {
-	char *artist_filename;
-	char *album_filename;
-	char *result;
+	/* "$\'`\"\\!?* ()[]<>&|@#:;" */
+	return strchr ("/\\!?*:;'`\"", c) == NULL;
+}
 
-	artist_filename = tracktitle_to_filename (data->album->artist);
-	album_filename = tracktitle_to_filename (data->album->title);
-	
-	result = g_strconcat (data->destination, G_DIR_SEPARATOR_S,
-			      artist_filename, G_DIR_SEPARATOR_S,
-			      album_filename,
-			      NULL);
 
+/* Remove special characters from a track title in order to make it a
+ * valid filename. */
+char*
+tracktitle_to_filename (const char *trackname,
+			gboolean    escape)
+{
+	char       *filename, *f, *f2;
+	const char *t;
+	gboolean    add_space;
+
+	if (trackname == NULL)
+		return NULL;
+
+	filename = g_new (char, strlen (trackname) + 1);
+
+	/* Substitute invalid characters with spaces. */
+
+	f = filename;
+	t = trackname;
+	while (*t != 0) {
+		gboolean invalid_char = FALSE;
+
+		while ((*t != 0) && ! valid_filename_char (*t)) {
+			invalid_char = TRUE;
+			t++;
+		}
+
+		if (invalid_char)
+			*f++ = ' ';
+
+		*f = *t;
+
+		if (*t != 0) {
+			f++;
+			t++;
+		}
+	}
+	*f = 0;
+
+	/* Remove double spaces. */
+	add_space = FALSE;
+	for (f = f2 = filename; *f != 0; f++)
+		if (*f != ' ') {
+			if (add_space) {
+				*f2++ = ' ';
+				add_space = FALSE;
+			}
+			*f2 = *f;
+			f2++;
+		} else
+			add_space = TRUE;
+	*f2 = 0;
+
+	if (escape) {
+		char *escaped;
+
+		escaped = g_uri_escape_string (filename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+		g_free (filename);
+		filename = escaped;
+	}
+
+	return filename;
+}
+
+
+static GFile *
+get_destination_folder (DialogData *data)
+{
+	char  *artist_filename;
+	char  *album_filename;
+	char  *uri;
+	GFile *folder;
+
+	artist_filename = tracktitle_to_filename (data->album->artist, TRUE);
+	album_filename = tracktitle_to_filename (data->album->title, TRUE);
+	uri = g_strconcat (data->destination,
+			   G_DIR_SEPARATOR_S,
+			   artist_filename,
+			   G_DIR_SEPARATOR_S,
+			   album_filename,
+			   NULL);
+	folder = g_file_new_for_uri (uri);
+
+	g_free (uri);
 	g_free (artist_filename);
 	g_free (album_filename);
 
-	return result;
+	return folder;
 }
 
 
@@ -393,25 +442,18 @@ done_dialog_response_cb (GtkDialog  *dialog,
 
 	gtk_widget_destroy (GTK_WIDGET (dialog));
 	
-	if ((button_number == GTK_RESPONSE_OK)
-	    && eel_gconf_get_boolean (PREF_RIPPER_VIEW_DISTINATION, FALSE)) {
-		GError *error  = NULL;
-		char   *folder = get_destination_folder (data);
-		char   *scheme = NULL;
-		char   *url    = NULL;
+	if ((button_number == GTK_RESPONSE_OK) && eel_gconf_get_boolean (PREF_RIPPER_VIEW_DISTINATION, FALSE)) {
+		GFile  *folder;
+		char   *uri;
+		GError *error = NULL;
 		
-		scheme = gnome_vfs_get_uri_scheme (folder);
-		if ((scheme == NULL) || (strcmp (scheme, "") == 0))
-			url = gnome_vfs_get_uri_from_local_path (folder);
-		else
-			url = g_strdup (folder);
-
-		if (! gnome_url_show (url, &error))
+		folder = get_destination_folder (data);
+		uri = g_file_get_uri (folder);
+		if (! gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)), uri, GDK_CURRENT_TIME, &error))
 			_gtk_error_dialog_from_gerror_run (GTK_WINDOW (data->window), _("Could not display the destination folder"), &error);
 
-		g_free (scheme);
-		g_free (url);
-		g_free (folder);
+		g_free (uri);
+		g_object_unref (folder);
 	}
 
 	gtk_widget_destroy (data->dialog);
@@ -439,8 +481,8 @@ get_track_filename (TrackInfo  *track,
 {
 	char *filename, *track_filename;
 
-	filename = tracktitle_to_filename (track->title);
-	track_filename = g_strdup_printf ("%s.%%20%s.%s", zero_padded (track->number + 1), filename, ext);
+	filename = tracktitle_to_filename (track->title, FALSE);
+	track_filename = g_strdup_printf ("%s. %s.%s", zero_padded (track->number + 1), filename, ext);
 	g_free (filename);
 
 	return track_filename;
@@ -450,46 +492,33 @@ get_track_filename (TrackInfo  *track,
 static void
 save_playlist (DialogData *data)
 {
-	char           *folder;
-	char           *filename, *album_filename;
-	GnomeVFSResult  result;
-	GnomeVFSHandle *handle;
+	GFile         *folder;
+	char          *album_filename;
+	char          *playlist_filename;
+	GFile         *file;
+	GOutputStream *stream;
 
 	folder = get_destination_folder (data);
-	album_filename = tracktitle_to_filename (data->album->title);
-	filename = g_strconcat ("file://", folder, "/", album_filename, ".pls", NULL);
-	g_free (album_filename);
-
-	gnome_vfs_unlink (filename);
+	album_filename = tracktitle_to_filename (data->album->title, FALSE);
+	playlist_filename = g_strconcat (album_filename, ".pls", NULL);
+	file = g_file_get_child (folder, playlist_filename);
 
-	result = gnome_vfs_create (&handle,
-				   filename,
-				   GNOME_VFS_OPEN_WRITE,
-				   TRUE,
-				   PLS_PERMISSIONS);
+	g_file_delete (file, NULL, NULL);
 
-	if (result == GNOME_VFS_OK) {
-		GList *scan;
+	stream = (GOutputStream *) g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL);
+	if (stream != NULL) {
 		char   buffer[BUFFER_SIZE];
+		GList *scan;
 		int    n = 0;
 
 		strcpy (buffer, "[playlist]\n");
-		gnome_vfs_write (handle,
-				 buffer,
-				 strlen (buffer),
-				 NULL);
+		g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
 
 		sprintf (buffer, "NumberOfEntries=%d\n", data->n_tracks);
-		gnome_vfs_write (handle,
-				 buffer,
-				 strlen (buffer),
-				 NULL);
+		g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
 
 		strcpy (buffer, "Version=2\n");
-		gnome_vfs_write (handle,
-				 buffer,
-				 strlen (buffer),
-				 NULL);
+		g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
 		
 		for (scan = data->tracks; scan; scan = scan->next) {
 			TrackInfo *track = scan->data;
@@ -499,47 +528,40 @@ save_playlist (DialogData *data)
 			n++;
 
 			track_filename = get_track_filename (track, data->ext);
-			unescaped = gnome_vfs_unescape_string (track_filename, "");
+			unescaped = g_uri_unescape_string (track_filename, "");
 			
 			sprintf (buffer, "File%d=%s\n", n, unescaped);
-			gnome_vfs_write (handle,
-					 buffer,
-					 strlen (buffer),
-					 NULL);
+			g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
 			
 			g_free (unescaped);		 
 			g_free (track_filename);
 
 			sprintf (buffer, "Title%d=%s - %s\n", n, data->album->artist, track->title);
-			gnome_vfs_write (handle,
-					 buffer,
-					 strlen (buffer),
-					 NULL);
+			g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
 
 			sprintf (buffer, "Length%d=%d\n", n, track->min * 60 + track->sec);
-			gnome_vfs_write (handle,
-					 buffer,
-					 strlen (buffer),
-					 NULL);
+			g_output_stream_write (stream, buffer, strlen (buffer), NULL, NULL);
 		}
 
-		gnome_vfs_close (handle);
+		g_object_unref (stream);
 	}
 
-	g_free (filename);
-	g_free (folder);
+	g_object_unref (file);
+	g_free (playlist_filename);
+	g_free (album_filename);
+	g_object_unref (folder);
 }
 
 
 static void
 rip_current_track (DialogData *data)
 {
-	TrackInfo      *track;
-	char           *msg;
-	char           *filename;
-	char           *folder;
-	GstEvent       *event;
-	GnomeVFSResult  result;
+	TrackInfo *track;
+	char      *msg;
+	GFile     *folder;
+	GError    *error = NULL;
+	char      *filename;
+	GstEvent  *event;
 
 	if (data->current_track == NULL) {
 		GtkWidget *d;
@@ -570,37 +592,33 @@ rip_current_track (DialogData *data)
 	track = data->current_track->data;
 
 	msg = g_markup_printf_escaped (_("<i>Extracting \"%s\"</i>"), track->title); 
-	gtk_label_set_markup (GTK_LABEL (data->r_track_label), msg);
+	gtk_label_set_markup (GTK_LABEL (GET_WIDGET ("track_label")), msg);
 	g_free (msg);
 
-	/* Set the filename */
+	/* Set the destination file */
 
 	folder = get_destination_folder (data);
-	
-	if ((result = ensure_dir_exists (folder, DESTINATION_PERMISSIONS)) != GNOME_VFS_OK) {
-		char *utf8_folder;
-
-		utf8_folder = g_locale_to_utf8 (folder, -1, 0, 0, 0);
-		_gtk_error_dialog_run (GTK_WINDOW (data->window),
-				       _("Could not extract tracks"),
-				       _("Could not create folder \"%s\"\n\n%s"),
-				       utf8_folder,
-				       gnome_vfs_result_to_string (result));
-		g_free (utf8_folder);
-		gtk_widget_destroy (data->dialog);
-		return;
+	if (! g_file_make_directory_with_parents (folder, NULL, &error)) {
+		if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+			_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->window),
+							    _("Could not extract tracks"),
+							    &error);
+			gtk_widget_destroy (data->dialog);
+			return;
+		}
 	}
 
-	g_free (data->current_file);
+	_g_object_unref (data->current_file);
 	filename = get_track_filename (track, data->ext);
-	data->current_file = g_strconcat (folder, G_DIR_SEPARATOR_S, filename, NULL);
+	data->current_file = g_file_get_child (folder, filename);
+
 	g_free (filename);
-	g_free (folder);
+	g_object_unref (folder);
 
-	gnome_vfs_unlink (data->current_file);
+	g_file_delete (data->current_file, NULL, NULL);
 
 	gst_element_set_state (data->sink, GST_STATE_NULL);
-	g_object_set (G_OBJECT (data->sink), "location", data->current_file, NULL);
+	g_object_set (G_OBJECT (data->sink), "file", data->current_file, NULL);
 
 	/* Set track tags. */
 
@@ -659,15 +677,13 @@ start_ripper (DialogData *data)
 	GList *scan;
 
 	data->ripping = TRUE;
+	brasero_drive_lock (data->drive, _("Extracting disc tracks"), NULL);
 
-	goo_cdrom_lock_tray (data->cdrom);
 	create_pipeline (data);
-
 	data->prev_tracks_sectors = 0;
 	data->total_sectors = 0;
 	for (scan = data->tracks; scan; scan = scan->next) {
 		TrackInfo *track = scan->data;
-		
 		data->total_sectors += track->sectors;
 	}
 	data->current_track_n = 1;
@@ -678,67 +694,45 @@ start_ripper (DialogData *data)
 }
 
 
-/* create the main dialog. */
 void
 dlg_ripper (GooWindow *window,
 	    GList     *tracks)
 {
-	DialogData  *data;
-	GtkWidget   *btn_cancel;
-	GooPlayer   *player;
+	GooPlayer  *player;
+	DialogData *data;
 	
-	data = g_new0 (DialogData, 1);
-	data->window = window;
-	data->gui = glade_xml_new (GOO_GLADEDIR "/" GLADE_RIPPER_FILE, NULL, NULL);
-        if (!data->gui) {
-		g_warning ("Could not find " GLADE_RIPPER_FILE "\n");
-		g_free (data);
-                return;
-        }
-
 	goo_window_stop (window);
 	player = goo_window_get_player (window);
 
-	data->destination = eel_gconf_get_path (PREF_EXTRACT_DESTINATION, "");
-	if ((data->destination == NULL) || (strcmp (data->destination, "") == 0)) { 
-		char *tmp;
-		
-		tmp = xdg_user_dir_lookup ("MUSIC");
-		data->destination = get_uri_from_local_path (tmp);
-		g_free (tmp);
-	}
-	data->device = g_strdup (goo_player_get_device (player));
+	data = g_new0 (DialogData, 1);
+	data->window = window;
+	data->builder = _gtk_builder_new_from_file ("ripper.ui", "");
+	data->dialog = GET_WIDGET ("ripper_dialog");
+	data->destination = eel_gconf_get_uri (PREF_EXTRACT_DESTINATION, "");
+	if ((data->destination == NULL) || (strcmp (data->destination, "") == 0))
+		data->destination = g_filename_to_uri (g_get_user_special_dir (G_USER_DIRECTORY_MUSIC), NULL, NULL);
+	data->drive = g_object_ref (goo_player_get_drive (player));
 	data->format = pref_get_file_format ();
 	data->album = goo_player_get_album (player);
-	if (tracks == NULL)
-		data->tracks = track_list_dup (data->album->tracks);
-	else
+	if (tracks != NULL)
 		data->tracks = track_list_dup (tracks);
+	else
+		data->tracks = track_list_dup (data->album->tracks);
 	data->n_tracks = g_list_length (data->tracks);
-	
 	data->update_handle = 0;
 	data->timer = g_timer_new ();
-	data->cdrom = goo_cdrom_new (data->device);
-
-	/* Get the widgets. */
-
-	data->dialog = glade_xml_get_widget (data->gui, "ripper_dialog");
-	data->r_progress_progressbar = glade_xml_get_widget (data->gui, "r_progress_progressbar");
-	data->r_track_label = glade_xml_get_widget (data->gui, "r_track_label");
-	btn_cancel = glade_xml_get_widget (data->gui, "r_cancelbutton");
 
 	/* Set widgets data. */
 
-	gtk_label_set_ellipsize (GTK_LABEL (data->r_track_label),
-				 PANGO_ELLIPSIZE_END);
+	gtk_label_set_ellipsize (GTK_LABEL (GET_WIDGET ("track_label")), PANGO_ELLIPSIZE_END);
 
 	/* Set the signals handlers. */
 
-	g_signal_connect (G_OBJECT (data->dialog), 
+	g_signal_connect (data->dialog,
 			  "destroy",
-			  G_CALLBACK (destroy_cb),
+			  G_CALLBACK (dialog_destroy_cb),
 			  data);
-	g_signal_connect_swapped (G_OBJECT (btn_cancel), 
+	g_signal_connect_swapped (GET_WIDGET ("cancel_button"),
 				  "clicked",
 				  G_CALLBACK (gtk_widget_destroy),
 				  data->dialog);
diff --git a/src/dlg-ripper.h b/src/dlg-ripper.h
index e68b716..74b99f0 100644
--- a/src/dlg-ripper.h
+++ b/src/dlg-ripper.h
@@ -23,7 +23,6 @@
 #ifndef DLG_RIPPER_H
 #define DLG_RIPPER_H
 
-#include "typedefs.h"
 #include "goo-window.h"
 
 void   dlg_ripper (GooWindow *window,
diff --git a/src/gconf-utils.c b/src/gconf-utils.c
index 93f9c83..0ec519a 100644
--- a/src/gconf-utils.c
+++ b/src/gconf-utils.c
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
 /*
- *  Goo
+ *  gThumb
  *
- *  Copyright (C) 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2001, 2002 The Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -46,7 +46,7 @@
 
 #include <config.h>
 #include <string.h>
-#include <gnome.h>
+#include <errno.h>
 #include <gconf/gconf-client.h>
 #include <gconf/gconf.h>
 #include "gconf-utils.h"
@@ -54,6 +54,9 @@
 #include "goo-error.h"
 #include "glib-utils.h"
 
+#define HOME_DIR "~"
+
+
 static GConfClient *global_gconf_client = NULL;
 
 
@@ -106,7 +109,6 @@ eel_gconf_handle_error (GError **error)
 					       "GConf error:\n  %s\n"
 					       "All further errors "
 					       "shown only on terminal",
-					       "%s",
 					       (*error)->message);
 		}
 		g_error_free (*error);
@@ -231,6 +233,37 @@ eel_gconf_get_integer (const char *key,
 }
 
 
+int
+eel_gconf_get_enum (const char *key,
+		    GType       enum_type,
+		    int         def_val)
+{
+	GEnumValue *def_enum_val;
+	char       *value_nick;
+	GEnumValue *value;
+	
+	def_enum_val = _g_enum_type_get_value (enum_type, def_val);
+	value_nick = eel_gconf_get_string (key, def_enum_val->value_nick);
+	value = _g_enum_type_get_value_by_nick (enum_type, value_nick);	
+	g_free (value_nick);
+	
+	return (value != NULL) ? value->value : 0;	
+}
+
+
+void
+eel_gconf_set_enum (const char *key,
+		    GType       enum_type,
+		    int         value)
+{
+	GEnumValue *enum_value;
+	
+	enum_value = _g_enum_type_get_value (enum_type, value);
+	if (enum_value != NULL)
+		eel_gconf_set_string (key, enum_value->value_nick);
+}
+
+
 void
 eel_gconf_set_float (const char *key,
 		     float       float_value)
@@ -316,15 +349,16 @@ eel_gconf_get_string (const char *key,
 	
 	val = gconf_client_get_string (client, key, &error);
 
-	if (val != NULL) {
+	/* Return the default value if the key does not exist,
+	   or if it is empty. */
+	if (val != NULL && strcmp (val, "")) {
 		g_return_val_if_fail (error == NULL, result);
 		g_free (result);
 		result = g_strdup (val);
-		g_free (val);
 
 	} else if (error != NULL)
 		eel_gconf_handle_error (&error);
-	
+
 	return result;
 }
 
@@ -416,7 +450,7 @@ eel_gconf_get_path_list (const char *key)
 	slist = NULL;
 	for (scan = str_slist; scan; scan = scan->next) {
 		char *str = scan->data;
-		char *path = _g_substitute (str, '~', g_get_home_dir ());
+		char *path = _g_replace (str, HOME_DIR, g_get_home_dir ());
 		slist = g_slist_prepend (slist, path);
 	}
 
@@ -427,53 +461,6 @@ eel_gconf_get_path_list (const char *key)
 }
 
 
-static char *
-tilde_compress (const char *path)
-{
-	const char *home_dir = g_get_home_dir();
-	int         home_dir_l = strlen (home_dir);
-	int         ntilde = 0;
-	const char *scan;
-	int         path_l, result_l;
-	char       *result, *scan2;
-
-	if (path == NULL)
-		return NULL;
-
-	path_l = strlen (path);
-	for (scan = path; scan != NULL; scan++) {
-		if (path_l - (scan - path) < home_dir_l)
-			break;
-		if (strncmp (scan, home_dir, home_dir_l) == 0)
-			ntilde++;
-	}
-	
-	if (ntilde == 0)
-		return g_strdup (path);
-	
-	result_l = strlen (path) + ntilde - (ntilde * home_dir_l);
-	result = g_new (char, result_l + 1);
-
-	for (scan = path, scan2 = result; scan != NULL; scan2++) {
-		if (path_l - (scan - path) < home_dir_l) {
-			strcpy (scan2, scan);
-			scan2 += strlen (scan);
-			break;
-		}
-		if (strncmp (scan, home_dir, home_dir_l) == 0) {
-			*scan2 = '~';
-			scan += home_dir_l;
-		} else {
-			*scan2 = *scan;
-			scan++;
-		} 
-	}
-	*scan2 = 0;
-
-	return result;
-}
-
-
 void
 eel_gconf_set_path_list (const char    *key,
 			 const GSList  *string_list_value)
@@ -484,7 +471,7 @@ eel_gconf_set_path_list (const char    *key,
 	path_slist = NULL;
 	for (scan = string_list_value; scan; scan = scan->next) {
 		char *value = scan->data;
-		char *path = tilde_compress (value);
+		char *path = _g_replace (value, g_get_home_dir (), HOME_DIR);
 		path_slist = g_slist_prepend (path_slist, path);
 	}
 	path_slist = g_slist_reverse (path_slist);
@@ -544,26 +531,67 @@ char *
 eel_gconf_get_path (const char *key,
 		    const char *def_val)
 {
+	char *value;
 	char *path;
-	char *no_tilde_path;
-
-	path = eel_gconf_get_string (key, def_val);
-	no_tilde_path = _g_substitute (path, '~', g_get_home_dir ());
-	g_free (path);
+	
+	value = eel_gconf_get_string (key, def_val);
+	path = _g_replace (value, HOME_DIR, g_get_home_dir ());
+	g_free (value);
 
-	return no_tilde_path;
+	return path;
 }
 
 
 void
 eel_gconf_set_path (const char *key,
-		    const char *value)
+		    const char *path)
 {
-	char *tilde_path;
+	char *value;
+
+	value = _g_replace (path, g_get_home_dir (), HOME_DIR);
+	eel_gconf_set_string (key, value);
+	g_free (value);
+}
+
+
+char *
+eel_gconf_get_uri (const char *key,
+	           const char *def_val)
+{
+	GRegex *regex;
+	char   *s;
+	char   *home;
+	char   *uri;
+
+	regex = g_regex_new ("~", 0, 0, NULL);
+	s = eel_gconf_get_string (key, def_val);
+	home = g_uri_escape_string (g_get_home_dir (), G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+	uri = g_regex_replace_literal (regex, s, -1, 0, home, 0, NULL);
+
+	g_free (home);
+	g_free (s);
+	g_regex_unref (regex);
+
+	return uri;
+}
+
+
+void
+eel_gconf_set_uri (const char *key,
+		   const char *value)
+{
+	char   *home;
+	GRegex *regex;
+	char   *s;
+
+	home = g_uri_escape_string (g_get_home_dir (), G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+	regex = g_regex_new (home, 0, 0, NULL);
+	s = g_regex_replace_literal (regex, value, -1, 0, "~", 0, NULL);
+	eel_gconf_set_string (key, s);
 
-	tilde_path = tilde_compress (value);
-	eel_gconf_set_string (key, tilde_path);
-	g_free (tilde_path);
+	g_free (s);
+	g_regex_unref (regex);
+	g_free (home);
 }
 
 
diff --git a/src/gconf-utils.h b/src/gconf-utils.h
index 31ae3ee..853bea1 100644
--- a/src/gconf-utils.h
+++ b/src/gconf-utils.h
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
 /*
- *  Goo
+ *  gThumb
  *
- *  Copyright (C) 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2001, 2002 The Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -56,90 +56,67 @@ G_BEGIN_DECLS
 #define EEL_GCONF_UNDEFINED_CONNECTION 0
 
 GConfClient *eel_gconf_client_get_global     (void);
-
 void         eel_global_client_free          (void);
-
 gboolean     eel_gconf_handle_error          (GError                **error);
-
 gboolean     eel_gconf_get_boolean           (const char             *key,
 					      gboolean                def_val);
-
 void         eel_gconf_set_boolean           (const char             *key,
 					      gboolean                value);
-
 int          eel_gconf_get_integer           (const char             *key,
 					      int                     def_val);
-
 void         eel_gconf_set_integer           (const char             *key,
 					      int                     value);
-
+int          eel_gconf_get_enum              (const char             *key,
+					      GType                   enum_type,
+					      int                     def_val);
+void         eel_gconf_set_enum              (const char             *key,
+					      GType                   enum_type,
+					      int                     value);					      
 float        eel_gconf_get_float             (const char             *key,
 					      float                   def_val);
-
 void         eel_gconf_set_float             (const char             *key,
 					      float                   value);
-
 char *       eel_gconf_get_string            (const char             *key,
 					      const char             *def_val);
-
 void         eel_gconf_set_string            (const char             *key,
 					      const char             *value);
-
 char *       eel_gconf_get_path              (const char             *key,
 					      const char             *def_val);
-
 void         eel_gconf_set_path              (const char             *key,
 					      const char             *value);
-
+char *       eel_gconf_get_uri               (const char             *key,
+					      const char             *def_val);
+void         eel_gconf_set_uri               (const char             *key,
+					      const char             *value);
 char *       eel_gconf_get_locale_string     (const char             *key,
 					      const char             *def_val);
-
 void         eel_gconf_set_locale_string     (const char             *key,
 					      const char             *value);
-
 GSList *     eel_gconf_get_string_list       (const char             *key);
-
 void         eel_gconf_set_string_list       (const char             *key,
 					      const GSList           *string_list_value);
-
 GSList *     eel_gconf_get_path_list         (const char             *key);
-
 void         eel_gconf_set_path_list         (const char             *key,
 					      const GSList           *string_list_value);
-
 GSList *     eel_gconf_get_locale_string_list(const char             *key);
-
 void         eel_gconf_set_locale_string_list(const char             *key,
 					      const GSList           *string_list_value);
-
 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);
 
diff --git a/src/gio-utils.c b/src/gio-utils.c
new file mode 100644
index 0000000..0399ca5
--- /dev/null
+++ b/src/gio-utils.c
@@ -0,0 +1,458 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include "glib-utils.h"
+#include "gio-utils.h"
+
+
+#define BUFFER_SIZE 4096
+
+
+gboolean
+g_load_file_in_buffer (GFile   *file,
+		       void   **buffer,
+		       gsize   *size,
+		       GError **error)
+{
+	GFileInputStream *istream;
+	gboolean          retval;
+	void             *local_buffer;
+	gsize             count;
+	gssize            n;
+	char              tmp_buffer[BUFFER_SIZE];
+
+	istream = g_file_read (file, NULL, error);
+	if (istream == NULL)
+		return FALSE;
+
+	retval = FALSE;
+	local_buffer = NULL;
+	count = 0;
+	for (;;) {
+		n = g_input_stream_read (G_INPUT_STREAM (istream), tmp_buffer, BUFFER_SIZE, NULL, error);
+		if (n < 0) {
+			g_free (local_buffer);
+			retval = FALSE;
+			break;
+		}
+		else if (n == 0) {
+			*buffer = local_buffer;
+			*size = count;
+			retval = TRUE;
+			break;
+		}
+
+		local_buffer = g_realloc (local_buffer, count + n + 1);
+		memcpy (local_buffer + count, tmp_buffer, n);
+		count += n;
+	}
+
+	g_object_unref (istream);
+
+	return retval;
+}
+
+
+typedef struct {
+	int                  io_priority;
+	GCancellable        *cancellable;
+	BufferReadyCallback  callback;
+	gpointer             user_data;
+	GInputStream        *stream;
+	guchar               tmp_buffer[BUFFER_SIZE];
+	void                *buffer;
+	gsize                count;
+} LoadData;
+
+
+static void
+load_data_free (LoadData *load_data)
+{
+	if (load_data->stream != NULL)
+		g_object_unref (load_data->stream);
+	g_free (load_data->buffer);
+	g_free (load_data);
+}
+
+
+static void
+load_file__stream_read_cb (GObject      *source_object,
+			   GAsyncResult *result,
+			   gpointer      user_data)
+{
+	LoadData *load_data = user_data;
+	GError   *error = NULL;
+	gssize    count;
+
+	count = g_input_stream_read_finish (load_data->stream, result, &error);
+	if (count < 0) {
+		load_data->callback (NULL, -1, error, load_data->user_data);
+		load_data_free (load_data);
+		return;
+	}
+	else if (count == 0) {
+		if (load_data->buffer != NULL)
+			((char *)load_data->buffer)[load_data->count] = 0;
+		load_data->callback (load_data->buffer, load_data->count, NULL, load_data->user_data);
+		load_data_free (load_data);
+		return;
+	}
+
+	load_data->buffer = g_realloc (load_data->buffer, load_data->count + count + 1);
+	memcpy (load_data->buffer + load_data->count, load_data->tmp_buffer, count);
+	load_data->count += count;
+
+	g_input_stream_read_async (load_data->stream,
+				   load_data->tmp_buffer,
+				   BUFFER_SIZE,
+				   load_data->io_priority,
+				   load_data->cancellable,
+				   load_file__stream_read_cb,
+				   load_data);
+}
+
+
+static void
+load_file__file_read_cb (GObject      *source_object,
+			 GAsyncResult *result,
+			 gpointer      user_data)
+{
+	LoadData *load_data = user_data;
+	GError   *error = NULL;
+
+	load_data->stream = (GInputStream *) g_file_read_finish (G_FILE (source_object), result, &error);
+	if (load_data->stream == NULL) {
+		load_data->callback (NULL, -1, error, load_data->user_data);
+		load_data_free (load_data);
+		return;
+	}
+
+	load_data->count = 0;
+	g_input_stream_read_async (load_data->stream,
+				   load_data->tmp_buffer,
+				   BUFFER_SIZE,
+				   load_data->io_priority,
+				   load_data->cancellable,
+				   load_file__stream_read_cb,
+				   load_data);
+}
+
+
+void
+g_load_file_async (GFile               *file,
+		   int                  io_priority,
+		   GCancellable        *cancellable,
+		   BufferReadyCallback  callback,
+		   gpointer             user_data)
+{
+	LoadData *load_data;
+
+	load_data = g_new0 (LoadData, 1);
+	load_data->io_priority = io_priority;
+	load_data->cancellable = cancellable;
+	load_data->callback = callback;
+	load_data->user_data = user_data;
+
+	g_file_read_async (file, io_priority, cancellable, load_file__file_read_cb, load_data);
+}
+
+
+/* -- g_write_file_async -- */
+
+
+typedef struct {
+	int                  io_priority;
+	GCancellable        *cancellable;
+	BufferReadyCallback  callback;
+	gpointer             user_data;
+	void                *buffer;
+	gsize                count;
+	gsize                written;
+	GError              *error;
+} WriteData;
+
+
+static void
+write_data_free (WriteData *write_data)
+{
+	g_free (write_data);
+}
+
+
+static void
+write_file__notify (gpointer user_data)
+{
+	WriteData *write_data = user_data;
+
+	write_data->callback (write_data->buffer, write_data->count, write_data->error, write_data->user_data);
+	write_data_free (write_data);
+}
+
+
+static void
+write_file__stream_flush_cb (GObject      *source_object,
+			     GAsyncResult *result,
+			     gpointer      user_data)
+{
+	GOutputStream *stream = (GOutputStream *) source_object;
+	WriteData     *write_data = user_data;
+	GError        *error = NULL;
+
+	g_output_stream_flush_finish (stream, result, &error);
+	write_data->error = error;
+	g_object_unref (stream);
+
+	call_when_idle (write_file__notify, write_data);
+}
+
+
+static void
+write_file__stream_write_ready_cb (GObject      *source_object,
+				   GAsyncResult *result,
+				   gpointer      user_data)
+{
+	GOutputStream *stream = (GOutputStream *) source_object;
+	WriteData     *write_data = user_data;
+	GError        *error = NULL;
+	gssize         count;
+
+	count = g_output_stream_write_finish (stream, result, &error);
+	write_data->written += count;
+
+	if ((count == 0) || (write_data->written == write_data->count)) {
+		g_output_stream_flush_async (stream,
+					     write_data->io_priority,
+					     write_data->cancellable,
+					     write_file__stream_flush_cb,
+					     user_data);
+		return;
+	}
+
+	g_output_stream_write_async (stream,
+				     write_data->buffer + write_data->written,
+				     write_data->count - write_data->written,
+				     write_data->io_priority,
+				     write_data->cancellable,
+				     write_file__stream_write_ready_cb,
+				     write_data);
+}
+
+
+static void
+write_file__replace_ready_cb (GObject      *source_object,
+			      GAsyncResult *result,
+			      gpointer      user_data)
+{
+	WriteData     *write_data = user_data;
+	GOutputStream *stream;
+	GError        *error = NULL;
+
+	stream = (GOutputStream*) g_file_replace_finish ((GFile*) source_object, result, &error);
+	if (stream == NULL) {
+		write_data->callback (write_data->buffer, write_data->count, error, write_data->user_data);
+		write_data_free (write_data);
+		return;
+	}
+
+	write_data->written = 0;
+	g_output_stream_write_async (stream,
+				     write_data->buffer,
+				     write_data->count,
+				     write_data->io_priority,
+				     write_data->cancellable,
+				     write_file__stream_write_ready_cb,
+				     write_data);
+}
+
+
+void
+g_write_file_async (GFile               *file,
+		    void                *buffer,
+		    gsize                count,
+		    int                  io_priority,
+		    GCancellable        *cancellable,
+		    BufferReadyCallback  callback,
+		    gpointer             user_data)
+{
+	WriteData *write_data;
+
+	write_data = g_new0 (WriteData, 1);
+	write_data->buffer = buffer;
+	write_data->count = count;
+	write_data->io_priority = io_priority;
+	write_data->cancellable = cancellable;
+	write_data->callback = callback;
+	write_data->user_data = user_data;
+
+	g_file_replace_async (file, NULL, FALSE, 0, io_priority, cancellable, write_file__replace_ready_cb, write_data);
+}
+
+
+GFile *
+_g_file_create_unique (GFile       *parent,
+		       const char  *display_name,
+		       const char  *suffix,
+		       GError     **error)
+{
+	GFile             *file = NULL;
+	GError            *local_error = NULL;
+	int                n;
+	GFileOutputStream *stream;
+
+	file = g_file_get_child_for_display_name (parent, display_name, &local_error);
+	n = 0;
+	do {
+		char *new_display_name;
+
+		if (file != NULL)
+			g_object_unref (file);
+
+		n++;
+		if (n == 1)
+			new_display_name = g_strdup_printf ("%s%s", display_name, suffix);
+		else
+			new_display_name = g_strdup_printf ("%s %d%s", display_name, n, suffix);
+
+		file = g_file_get_child_for_display_name (parent, new_display_name, &local_error);
+		if (local_error == NULL)
+			stream = g_file_create (file, 0, NULL, &local_error);
+
+		if ((stream == NULL) && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+			g_clear_error (&local_error);
+
+		g_free (new_display_name);
+	}
+	while ((stream == NULL) && (local_error == NULL));
+
+	if (stream == NULL) {
+		g_object_unref (file);
+		file = NULL;
+	}
+	else
+		g_object_unref (stream);
+
+	return file;
+}
+
+
+GFile *
+_g_directory_create_unique (GFile       *parent,
+			    const char  *display_name,
+			    const char  *suffix,
+			    GError     **error)
+{
+	GFile    *file = NULL;
+	gboolean  created = FALSE;
+	GError   *local_error = NULL;
+	int       n;
+
+	file = g_file_get_child_for_display_name (parent, display_name, &local_error);
+	if (file == NULL) {
+		g_propagate_error (error, local_error);
+		return NULL;
+	}
+
+	n = 0;
+	do {
+		char *new_display_name;
+
+		if (file != NULL)
+			g_object_unref (file);
+
+		n++;
+		if (n == 1)
+			new_display_name = g_strdup_printf ("%s%s", display_name, suffix);
+		else
+			new_display_name = g_strdup_printf ("%s %d%s", display_name, n, suffix);
+
+		file = g_file_get_child_for_display_name (parent, new_display_name, &local_error);
+		if (local_error == NULL)
+			created = g_file_make_directory (file, NULL, &local_error);
+
+		if (! created && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+			g_clear_error (&local_error);
+
+		g_free (new_display_name);
+	}
+	while (! created && (local_error == NULL));
+
+	if (local_error != NULL) {
+		g_object_unref (file);
+		file = NULL;
+	}
+
+	if (local_error != NULL)
+		g_propagate_error (error, local_error);
+
+	return file;
+}
+
+
+/* -- g_write_file -- */
+
+
+gboolean
+g_write_file (GFile             *file,
+	      gboolean           make_backup,
+	      GFileCreateFlags   flags,
+	      void              *buffer,
+	      gsize              count,
+	      GCancellable      *cancellable,
+	      GError           **error)
+{
+	gboolean       success;
+	GOutputStream *stream;
+
+	stream = (GOutputStream *) g_file_replace (file, NULL, make_backup, flags, cancellable, error);
+	if (stream != NULL)
+		success = g_output_stream_write_all (stream, buffer, count, NULL, cancellable, error);
+	else
+		success = FALSE;
+
+	_g_object_unref (stream);
+
+	return success;
+}
+
+
+gboolean
+_g_directory_make (GFile    *file,
+		   guint32   unix_mode,
+		   GError  **error)
+{
+	if (! g_file_make_directory (file, NULL, error)) {
+		if (! (*error)->code == G_IO_ERROR_EXISTS)
+			return FALSE;
+		g_clear_error (error);
+	}
+
+	return g_file_set_attribute_uint32 (file,
+					    G_FILE_ATTRIBUTE_UNIX_MODE,
+					    unix_mode,
+					    G_FILE_QUERY_INFO_NONE,
+					    NULL,
+					    error);
+}
diff --git a/src/gio-utils.h b/src/gio-utils.h
new file mode 100644
index 0000000..332b1ae
--- /dev/null
+++ b/src/gio-utils.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GIO_UTILS_H
+#define _GIO_UTILS_H
+
+#include <glib.h>
+#include <gio/gio.h>
+#include "typedefs.h"
+
+G_BEGIN_DECLS
+
+/* callback types */
+
+typedef void (*BufferReadyCallback)  (void        *buffer,
+				      gsize        count,
+				      GError      *error,
+				      gpointer     user_data);
+
+/* -- load/write/create file  -- */
+
+gboolean g_load_file_in_buffer       (GFile                 *file,
+				      void                 **buffer,
+				      gsize                 *size,
+				      GError               **error);
+void     g_load_file_async           (GFile                 *file,
+				      int                    io_priority,
+				      GCancellable          *cancellable,
+				      BufferReadyCallback    callback,
+				      gpointer               user_data);
+gboolean g_write_file                (GFile                 *file,
+				      gboolean               make_backup,
+			              GFileCreateFlags       flags,
+				      void                  *buffer,
+				      gsize                  count,
+				      GCancellable          *cancellable,
+				      GError               **error);
+void     g_write_file_async          (GFile                 *file,
+				      void                  *buffer,
+		    		      gsize                  count,
+				      int                    io_priority,
+				      GCancellable          *cancellable,
+				      BufferReadyCallback    callback,
+				      gpointer               user_data);
+GFile * _g_file_create_unique        (GFile                 *parent,
+				      const char            *display_name,
+				      const char            *suffix,
+				      GError               **error);
+GFile * _g_directory_create_unique   (GFile                 *parent,
+				      const char            *display_name,
+				      const char            *suffix,
+				      GError               **error);
+
+G_END_DECLS
+
+#endif /* _GIO_UTILS_H */
diff --git a/src/glib-utils.c b/src/glib-utils.c
index 965b2b5..dc4e2b1 100644
--- a/src/glib-utils.c
+++ b/src/glib-utils.c
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
 /*
- *  Goo
+ *  GThumb
  *
- *  Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2001-2008 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,12 +20,510 @@
  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
  */
 
-#include <string.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 #include <glib.h>
+#include <glib/gi18n.h>
 #include <glib/gprintf.h>
+#include <gio/gio.h>
 #include "glib-utils.h"
 
+#define MAX_PATTERNS 128
+
+
+/* gobject utils*/
+
+
+gpointer
+_g_object_ref (gpointer object)
+{
+	if (object != NULL)
+		return g_object_ref (object);
+	else
+		return NULL;
+}
+
+
+void
+_g_object_unref (gpointer object)
+{
+	if (object != NULL)
+		g_object_unref (object);
+}
+
+
+void
+_g_object_clear (gpointer  object)
+{
+	gpointer *object_p = (gpointer *) object;
+
+	if ((object_p != NULL) && (*object_p != NULL)) {
+		g_object_unref (*object_p);
+		*object_p = NULL;
+	}
+}
+
+
+GList *
+_g_object_list_ref (GList *list)
+{
+	GList *new_list;
+
+	if (list == NULL)
+		return NULL;
+
+	new_list = g_list_copy (list);
+	g_list_foreach (new_list, (GFunc) g_object_ref, NULL);
+
+	return new_list;
+}
+
+
+void
+_g_object_list_unref (GList *list)
+{
+	g_list_foreach (list, (GFunc) g_object_unref, NULL);
+	g_list_free (list);
+}
+
+
+GType
+g_object_list_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0)
+		type = g_boxed_type_register_static ("GObjectList",
+						     (GBoxedCopyFunc) _g_object_list_ref,
+						     (GBoxedFreeFunc) _g_object_list_unref);
+
+	return type;
+}
+
+
+GEnumValue *
+_g_enum_type_get_value (GType enum_type,
+			int   value)
+{
+	GEnumClass *class;
+	GEnumValue *enum_value;
+
+	class = G_ENUM_CLASS (g_type_class_ref (enum_type));
+	enum_value = g_enum_get_value (class, value);
+	g_type_class_unref (class);
+
+	return enum_value;
+}
+
+
+GEnumValue *
+_g_enum_type_get_value_by_nick (GType       enum_type,
+				const char *nick)
+{
+	GEnumClass *class;
+	GEnumValue *enum_value;
+
+	class = G_ENUM_CLASS (g_type_class_ref (enum_type));
+	enum_value = g_enum_get_value_by_nick (class, nick);
+	g_type_class_unref (class);
+
+	return enum_value;
+}
+
+
+/* idle callback */
+
+
+IdleCall*
+idle_call_new (DataFunc func,
+	       gpointer data)
+{
+	IdleCall *call = g_new0 (IdleCall, 1);
+	call->func = func;
+	call->data = data;
+	return call;
+}
+
+
+void
+idle_call_free (IdleCall *call)
+{
+	g_free (call);
+}
+
+
+static gboolean
+idle_call_exec_cb (gpointer data)
+{
+	IdleCall *call = data;
+	(*call->func) (call->data);
+	return FALSE;
+}
+
+
+guint
+idle_call_exec (IdleCall *call,
+		gboolean  use_idle_cb)
+{
+	if (use_idle_cb)
+		return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+					idle_call_exec_cb,
+					call,
+					(GDestroyNotify) idle_call_free);
+	else {
+		(*call->func) (call->data);
+		idle_call_free (call);
+		return 0;
+	}
+}
+
+
+guint
+call_when_idle (DataFunc  func,
+		gpointer  data)
+{
+	return idle_call_exec (idle_call_new (func, data), TRUE);
+}
+
+
+typedef struct {
+	gpointer       object;
+	ReadyCallback  ready_func;
+	gpointer       user_data;
+	GError        *error;
+	guint          id;
+} ObjectReadyData;
+
+
+static gboolean
+exec_object_ready_func (gpointer user_data)
+{
+	ObjectReadyData *data = user_data;
+
+	g_source_remove (data->id);
+	data->ready_func (G_OBJECT (data->object), data->error, data->user_data);
+	g_free (data);
+
+	return FALSE;
+}
+
+
+void
+object_ready_with_error (gpointer       object,
+			 ReadyCallback  ready_func,
+			 gpointer       user_data,
+			 GError        *error)
+{
+	ObjectReadyData *data;
+
+	data = g_new0 (ObjectReadyData, 1);
+
+	data->object = object;
+	data->ready_func = ready_func;
+	data->user_data = user_data;
+	data->error = error;
+	data->id = g_idle_add (exec_object_ready_func, data);
+}
+
+
+typedef struct {
+	ReadyFunc  ready_func;
+	gpointer   user_data;
+	GError    *error;
+	guint      id;
+} ReadyData;
+
+
+static gboolean
+exec_ready_func (gpointer user_data)
+{
+	ReadyData *data = user_data;
+
+	g_source_remove (data->id);
+	data->ready_func (data->error, data->user_data);
+	g_free (data);
+
+	return FALSE;
+}
+
+
+void
+ready_with_error (ReadyFunc  ready_func,
+		  gpointer   user_data,
+		  GError    *error)
+{
+	ReadyData *data;
+
+	data = g_new0 (ReadyData, 1);
+	data->ready_func = ready_func;
+	data->user_data = user_data;
+	data->error = error;
+	data->id = g_idle_add (exec_ready_func, data);
+}
+
+
+/* debug */
+
+
+void
+debug (const char *file,
+       int         line,
+       const char *function,
+       const char *format,
+       ...)
+{
+#ifdef DEBUG
+	static   gboolean first_time = 0;
+	static   gboolean print_debug_info = 0;
+	va_list  args;
+	char    *str;
+
+	if (! first_time) {
+		first_time = 1;
+		if (g_getenv ("GTHUMB_DEBUG"))
+			print_debug_info = 1;
+	}
+
+	if (! print_debug_info)
+		return;
+
+	g_return_if_fail (format != NULL);
+
+	va_start (args, format);
+	str = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	g_fprintf (stderr, "[%s] %s:%d (%s):\n\t%s\n", g_get_prgname(), file, line, function, str);
+
+	g_free (str);
+#endif
+}
+
+
+void
+performance (const char *file,
+	     int         line,
+	     const char *function,
+	     const char *format,
+	     ...)
+{
+#ifdef DEBUG
+	va_list args;
+	char *formatted, *str, *filename;
+
+	filename = g_path_get_basename (file);
+
+	va_start (args, format);
+	formatted = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	str = g_strdup_printf ("MARK: %s: (%s:%d) [%s] : %s", g_get_prgname(), filename, line, function, formatted);
+	g_free (formatted);
+
+	access (str, F_OK);
+	g_free (str);
+	g_free (filename);
+#endif
+}
+
+
+/* GTimeVal utils */
+
+
+int
+_g_time_val_cmp (GTimeVal *a,
+		 GTimeVal *b)
+{
+	if (a->tv_sec == b->tv_sec) {
+		if (a->tv_usec == b->tv_usec)
+			return 0;
+		else
+			return a->tv_usec > b->tv_usec ? 1 : -1;
+	}
+	else if (a->tv_sec > b->tv_sec)
+		return 1;
+	else
+		return -1;
+}
+
+
+void
+_g_time_val_reset (GTimeVal *time_)
+{
+	time_->tv_sec = 0;
+	time_->tv_usec = 0;
+}
+
+
+gboolean
+_g_time_val_from_exif_date (const char *exif_date,
+			    GTimeVal   *time_)
+{
+	struct tm tm;
+	long   val;
+
+	g_return_val_if_fail (time_ != NULL, FALSE);
+
+	if (exif_date == NULL)
+		return FALSE;
+
+	while (g_ascii_isspace (*exif_date))
+		exif_date++;
+
+	if (*exif_date == '\0')
+		return FALSE;
+
+	if (! g_ascii_isdigit (*exif_date))
+		return FALSE;
+
+	/* YYYY */
+
+	val = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+	tm.tm_year = val - 1900;
+
+	if (*exif_date != ':')
+		return FALSE;
+
+	/* MM */
+
+	exif_date++;
+	tm.tm_mon = g_ascii_strtoull (exif_date, (char **)&exif_date, 10) - 1;
+
+	if (*exif_date != ':')
+		return FALSE;
+
+	/* DD */
+
+	exif_date++;
+	tm.tm_mday = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+
+  	if (*exif_date != ' ')
+		return FALSE;
+
+  	/* hh */
+
+  	val = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+  	tm.tm_hour = val;
+
+  	if (*exif_date != ':')
+		return FALSE;
+
+  	/* mm */
+
+	exif_date++;
+	tm.tm_min = g_ascii_strtoull (exif_date, (char **)&exif_date, 10);
+
+	if (*exif_date != ':')
+		return FALSE;
+
+      	/* ss */
+
+	exif_date++;
+	tm.tm_sec = strtoul (exif_date, (char **)&exif_date, 10);
+
+	time_->tv_sec = mktime (&tm);
+	time_->tv_usec = 0;
+
+	/* usec */
+
+	if ((*exif_date == ',') || (*exif_date == '.')) {
+		glong mul = 100000;
+
+		while (g_ascii_isdigit (*++exif_date)) {
+			time_->tv_usec += (*exif_date - '0') * mul;
+			mul /= 10;
+		}
+	}
+
+	while (g_ascii_isspace (*exif_date))
+		exif_date++;
+
+	return *exif_date == '\0';
+}
+
+
+char *
+_g_time_val_to_exif_date (GTimeVal *time_)
+{
+	char      *retval;
+	struct tm *tm;
+	time_t     secs;
+
+	g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
+
+	secs = time_->tv_sec;
+	tm = localtime (&secs);
+
+	retval = g_strdup_printf ("%4d:%02d:%02d %02d:%02d:%02d",
+				  tm->tm_year + 1900,
+				  tm->tm_mon + 1,
+				  tm->tm_mday,
+				  tm->tm_hour,
+				  tm->tm_min,
+				  tm->tm_sec);
+
+	return retval;
+}
+
+
+/* Bookmark file utils */
+
+
+void
+_g_bookmark_file_clear (GBookmarkFile *bookmark)
+{
+	char **uris;
+	int    i;
+
+	uris = g_bookmark_file_get_uris (bookmark, NULL);
+	for (i = 0; uris[i] != NULL; i++)
+		g_bookmark_file_remove_item (bookmark, uris[i], NULL);
+	g_strfreev (uris);
+}
+
+
+void
+_g_bookmark_file_add_uri (GBookmarkFile *bookmark,
+			  const char    *uri)
+{
+	g_bookmark_file_set_is_private (bookmark, uri, TRUE);
+	g_bookmark_file_add_application (bookmark, uri, NULL, NULL);
+}
+
+
+void
+_g_bookmark_file_set_uris (GBookmarkFile *bookmark,
+			   GList         *uri_list)
+{
+	GList *scan;
+
+	_g_bookmark_file_clear (bookmark);
+	for (scan = uri_list; scan; scan = scan->next)
+		_g_bookmark_file_add_uri (bookmark, scan->data);
+}
+
+
+/* String utils */
+
+
+void
+_g_strset (char       **s,
+	   const char  *value)
+{
+	if (*s == value)
+		return;
+
+	if (*s != NULL) {
+		g_free (*s);
+		*s = NULL;
+	}
+
+	if (value != NULL)
+		*s = g_strdup (value);
+}
+
 
 char *
 _g_strdup_with_max_size (const char *s,
@@ -86,23 +584,23 @@ _g_get_template_from_text (const char *utf8_template)
 		chunk_end = chunk_start;
 
 		ch = g_utf8_get_char (chunk_end);
-		while (reading_sharps 
-		       && (*chunk_end != 0) 
+		while (reading_sharps
+		       && (*chunk_end != 0)
 		       && (ch == '#')) {
 			chunk_end = g_utf8_next_char (chunk_end);
 			ch = g_utf8_get_char (chunk_end);
 			chunk_len++;
 		}
-		
+
 		ch = g_utf8_get_char (chunk_end);
-		while (! reading_sharps 
-		       && (*chunk_end != 0) 
+		while (! reading_sharps
+		       && (*chunk_end != 0)
 		       && (*chunk_end != '#')) {
 			chunk_end = g_utf8_next_char (chunk_end);
 			ch = g_utf8_get_char (chunk_end);
 			chunk_len++;
 		}
-		
+
 		chunk = _g_utf8_strndup (chunk_start, chunk_len);
 		str_list = g_list_prepend (str_list, chunk);
 		n++;
@@ -150,7 +648,7 @@ _g_get_name_from_template (char **utf8_template,
 				g_string_append_c (s, '0');
 				sharps_len--;
 			}
-				
+
 			g_string_append (s, s_n);
 			g_free (s_n);
 		}
@@ -164,38 +662,42 @@ _g_get_name_from_template (char **utf8_template,
 
 
 char *
-_g_substitute (const char *from,
-	       const char  this,
-	       const char *with_this)
+_g_replace (const char *str,
+	    const char *from_str,
+	    const char *to_str)
 {
-	char       *result;
-	GString    *r;
-	const char *s;
+	char    **tokens;
+	int       i;
+	GString  *gstr;
 
-	if ((from == NULL) || (with_this == NULL))
-		return g_strdup ("");
+	if (str == NULL)
+		return NULL;
 
-	if (strchr (from, this) == NULL)
-		return g_strdup (from);
+	if (from_str == NULL)
+		return g_strdup (str);
 
-	r = g_string_new (NULL);
-	for (s = from; *s != 0; s++) 
-		if (*s == this)
-			g_string_append (r, with_this);
-		else
-			g_string_append_c (r, *s);
+	if (strcmp (str, from_str) == 0)
+		return g_strdup (to_str);
 
-	result = r->str;
-	g_string_free (r, FALSE);
+	tokens = g_strsplit (str, from_str, -1);
 
-	return result;
+	gstr = g_string_new (NULL);
+	for (i = 0; tokens[i] != NULL; i++) {
+		g_string_append (gstr, tokens[i]);
+		if ((to_str != NULL) && (tokens[i+1] != NULL))
+			g_string_append (gstr, to_str);
+	}
+
+	g_strfreev (tokens);
+
+	return g_string_free (gstr, FALSE);
 }
 
 
 char *
-_g_substitute_pattern (const char *utf8_text, 
-		       gunichar    pattern, 
-		       const char *value)
+_g_replace_pattern (const char *utf8_text,
+		    gunichar    pattern,
+		    const char *value)
 {
 	const char *s;
 	GString    *r;
@@ -213,15 +715,17 @@ _g_substitute_pattern (const char *utf8_text,
 
 		if (ch == '%') {
 			s = g_utf8_next_char (s);
-			
+
 			if (*s == 0) {
 				g_string_append_unichar (r, ch);
 				break;
 			}
 
 			ch = g_utf8_get_char (s);
-			if (ch == pattern)
-				g_string_append (r, value);
+			if (ch == pattern) {
+				if (value)
+					g_string_append (r, value);
+			}
 			else {
 				g_string_append (r, "%");
 				g_string_append_unichar (r, ch);
@@ -239,6 +743,29 @@ _g_substitute_pattern (const char *utf8_text,
 
 
 char *
+_g_utf8_replace (const char  *string,
+		 const char  *pattern,
+		 const char  *replacement)
+{
+	GRegex *regex;
+	char   *result;
+
+	if (string == NULL)
+		return NULL;
+
+	regex = g_regex_new (pattern, 0, 0, NULL);
+	if (regex == NULL)
+		return NULL;
+
+	result = g_regex_replace_literal (regex, string, -1, 0, replacement, 0, NULL);
+
+	g_regex_unref (regex);
+
+	return result;
+}
+
+
+char *
 _g_utf8_strndup (const char *str,
 		 gsize       n)
 {
@@ -256,42 +783,76 @@ _g_utf8_strndup (const char *str,
 }
 
 
+static const char *
+_g_utf8_strstr (const char *haystack,
+		const char *needle)
+{
+	const char *s;
+	gsize       i;
+	gsize       haystack_len = g_utf8_strlen (haystack, -1);
+	gsize       needle_len = g_utf8_strlen (needle, -1);
+	int         needle_size = strlen (needle);
+
+	s = haystack;
+	for (i = 0; i <= haystack_len - needle_len; i++) {
+		if (strncmp (s, needle, needle_size) == 0)
+			return s;
+		s = g_utf8_next_char(s);
+	}
+
+	return NULL;
+}
+
+
 char **
-_g_utf8_strsplit (const char     *str,
-		  const gunichar  delimiter)
+_g_utf8_strsplit (const char *string,
+		  const char *delimiter,
+		  int         max_tokens)
 {
-	GSList      *slist = NULL, *scan;
+	GSList      *string_list = NULL, *slist;
 	char       **str_array;
-	const char  *s, *t;
+	const char  *s;
 	guint        n = 0;
+	const char  *remainder;
 
-	if (str == NULL)
-		return g_new0 (char *, 1);
-
-	t = s = str;
-	do {
-		gunichar ch = g_utf8_get_char (t);
-		if ((ch == delimiter) || (*t == 0)) {
-			if (t != s) {
-				n++;
-				slist = g_slist_prepend (slist, g_strndup (s, t - s));
-			}
+	g_return_val_if_fail (string != NULL, NULL);
+	g_return_val_if_fail (delimiter != NULL, NULL);
+	g_return_val_if_fail (delimiter[0] != '\0', NULL);
 
-			if (*t != 0)
-				t = s = g_utf8_next_char (t);
-			else
-				break;
-		} else 
-			t = g_utf8_next_char (t);
-	} while (TRUE);
+	if (max_tokens < 1)
+		max_tokens = G_MAXINT;
+
+	remainder = string;
+	s = _g_utf8_strstr (remainder, delimiter);
+	if (s != NULL) {
+		gsize delimiter_size = strlen (delimiter);
+
+		while (--max_tokens && (s != NULL)) {
+			gsize  size = s - remainder;
+			char  *new_string;
+
+			new_string = g_new (char, size + 1);
+			strncpy (new_string, remainder, size);
+			new_string[size] = 0;
+
+			string_list = g_slist_prepend (string_list, new_string);
+			n++;
+			remainder = s + delimiter_size;
+			s = _g_utf8_strstr (remainder, delimiter);
+		}
+	}
+	if (*string) {
+		n++;
+		string_list = g_slist_prepend (string_list, g_strdup (remainder));
+	}
 
 	str_array = g_new (char*, n + 1);
 
 	str_array[n--] = NULL;
-	for (scan = slist; scan; scan = scan->next)
-		str_array[n--] = scan->data;
+	for (slist = string_list; slist; slist = slist->next)
+		str_array[n--] = slist->data;
 
-	g_slist_free (slist);
+	g_slist_free (string_list);
 
 	return str_array;
 }
@@ -300,36 +861,9 @@ _g_utf8_strsplit (const char     *str,
 char *
 _g_utf8_strstrip (const char *str)
 {
-	const char *s;
-	const char *t;
-
 	if (str == NULL)
 		return NULL;
-
-	s = str;
-
-	do {
-		gunichar ch = g_utf8_get_char (s);
-		if (! g_unichar_isspace (ch))
-			break;
-		s = g_utf8_next_char (s);
-	} while (*s != 0);
-
-	if (*s == 0)
-		return NULL;
-
-	/**/
-
-	t = s;
-
-	do {
-		gunichar ch = g_utf8_get_char (t);
-		if (g_unichar_isspace (ch))
-			break;
-		t = g_utf8_next_char (t);
-	} while (*t != 0);
-
-	return g_strndup (s, t - s);
+	return g_strstrip (g_strdup (str));
 }
 
 
@@ -337,7 +871,10 @@ gboolean
 _g_utf8_all_spaces (const char *utf8_string)
 {
 	gunichar c;
-	
+
+	if (utf8_string == NULL)
+		return TRUE;
+
 	c = g_utf8_get_char (utf8_string);
 	while (c != 0) {
 		if (! g_unichar_isspace (c))
@@ -350,35 +887,1472 @@ _g_utf8_all_spaces (const char *utf8_string)
 }
 
 
+char *
+_g_utf8_remove_extension (const char *str)
+{
+	char *p;
+	char *ext;
+	char *dest;
+
+	if ((str == NULL) || ! g_utf8_validate (str, -1, NULL))
+		return NULL;
+
+	p = (char *) str;
+	ext = g_utf8_strrchr (p, -1, g_utf8_get_char ("."));
+	dest = g_strdup (p);
+	g_utf8_strncpy (dest, p, g_utf8_strlen (p, -1) - g_utf8_strlen (ext, -1));
+
+	return dest;
+}
+
+
+GList *
+_g_list_insert_list_before (GList *list1,
+			    GList *sibling,
+			    GList *list2)
+{
+  if (!list2)
+    {
+      return list1;
+    }
+  else if (!list1)
+    {
+      return list2;
+    }
+  else if (sibling)
+    {
+      GList *list2_last = g_list_last (list2);
+      if (sibling->prev)
+	{
+	  sibling->prev->next = list2;
+	  list2->prev = sibling->prev;
+	  sibling->prev = list2_last;
+	  list2_last->next = sibling;
+	  return list1;
+	}
+      else
+	{
+	  sibling->prev = list2_last;
+	  list2_last->next = sibling;
+	  return list2;
+	}
+    }
+  else
+    {
+      return g_list_concat (list1, list2);
+    }
+}
+
+
+GHashTable *static_strings = NULL;
+static GStaticMutex static_strings_mutex = G_STATIC_MUTEX_INIT;
+
+
+const char *
+get_static_string (const char *s)
+{
+	const char *result;
+
+	if (s == NULL)
+		return NULL;
+
+	g_static_mutex_lock (&static_strings_mutex);
+
+	if (static_strings == NULL)
+		static_strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+	if (! g_hash_table_lookup_extended (static_strings, s, (gpointer) &result, NULL)) {
+		result = g_strdup (s);
+		g_hash_table_insert (static_strings,
+				     (gpointer) result,
+				     GINT_TO_POINTER (1));
+	}
+
+	g_static_mutex_unlock (&static_strings_mutex);
+
+	return result;
+}
+
+
+char *
+_g_rand_string (int len)
+{
+	static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+	static int   letters_only = 52;
+	static int   whole_alphabet = 62;
+	char        *s;
+	GRand       *rand_gen;
+	int          i;
+
+	s = g_malloc (sizeof (char) * (len + 1));
+	rand_gen = g_rand_new ();
+	for (i = 0; i < len; i++)
+		s[i] = alphabet[g_rand_int_range (rand_gen, 0, (i == 0) ? letters_only : whole_alphabet)];
+	g_rand_free (rand_gen);
+	s[len] = 0;
+
+	return s;
+}
+
+
+int
+_g_strv_find (char       **v,
+	      const char  *s)
+{
+	int i;
+
+	for (i = 0; v[i] != NULL; i++) {
+		if (strcmp (v[i], s) == 0)
+			return i;
+	}
+
+	return -1;
+}
+
+
+char *
+_g_str_remove_suffix (const char *s,
+		      const char *suffix)
+{
+	int s_len;
+	int suffix_len;
+
+	if (s == NULL)
+		return NULL;
+	if (suffix == NULL)
+		return g_strdup (s);
+
+	s_len = strlen (s);
+	suffix_len = strlen (suffix);
+
+	if (suffix_len >= s_len)
+		return g_strdup ("");
+	else
+		return g_strndup (s, s_len - suffix_len);
+}
+
+
+/* Regexp utils */
+
+static char **
+get_patterns_from_pattern (const char *pattern_string)
+{
+	char **patterns;
+	int    i;
+
+	if (pattern_string == NULL)
+		return NULL;
+
+	patterns = _g_utf8_strsplit (pattern_string, ";", MAX_PATTERNS);
+	for (i = 0; patterns[i] != NULL; i++) {
+		char *p1, *p2;
+
+		p1 = _g_utf8_strstrip (patterns[i]);
+		p2 = _g_replace (p1, ".", "\\.");
+		patterns[i] = _g_replace (p2, "*", ".*");
+
+		g_free (p2);
+		g_free (p1);
+	}
+
+	return patterns;
+}
+
+
+GRegex **
+get_regexps_from_pattern (const char         *pattern_string,
+			  GRegexCompileFlags  compile_options)
+{
+	char   **patterns;
+	GRegex **regexps;
+	int      i;
+
+	patterns = get_patterns_from_pattern (pattern_string);
+	if (patterns == NULL)
+		return NULL;
+
+	regexps = g_new0 (GRegex*, g_strv_length (patterns) + 1);
+	for (i = 0; patterns[i] != NULL; i++)
+		regexps[i] = g_regex_new (patterns[i],
+					  G_REGEX_OPTIMIZE | compile_options,
+					  G_REGEX_MATCH_NOTEMPTY,
+					  NULL);
+	g_strfreev (patterns);
+
+	return regexps;
+}
+
+
+gboolean
+string_matches_regexps (GRegex           **regexps,
+			const char        *string,
+			GRegexMatchFlags   match_options)
+{
+	gboolean matched;
+	int      i;
+
+	if ((regexps == NULL) || (regexps[0] == NULL))
+		return TRUE;
+
+	if (string == NULL)
+		return FALSE;
+
+	matched = FALSE;
+	for (i = 0; regexps[i] != NULL; i++)
+		if (g_regex_match (regexps[i], string, match_options, NULL)) {
+			matched = TRUE;
+			break;
+	}
+
+	return matched;
+}
+
+
 void
-debug (const char *file,
-       int         line,
-       const char *function,
-       const char *format, ...)
+free_regexps (GRegex **regexps)
 {
-#ifdef DEBUG
-	va_list  args;
-	char    *str;
+	int i;
 
-	g_return_if_fail (format != NULL);
-	
-	va_start (args, format);
-	str = g_strdup_vprintf (format, args);
+	if (regexps == NULL)
+		return;
+
+	for (i = 0; regexps[i] != NULL; i++)
+		g_regex_unref (regexps[i]);
+	g_free (regexps);
+}
+
+
+/* URI utils  */
+
+
+const char *
+get_home_uri (void)
+{
+	static char *home_uri = NULL;
+
+	if (home_uri == NULL) {
+		const char *path;
+		char       *uri;
+
+		path = g_get_home_dir ();
+		uri = g_uri_escape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+
+		home_uri = g_strconcat ("file://", uri, NULL);
+
+		g_free (uri);
+	}
+
+	return home_uri;
+}
+
+
+const char *
+get_desktop_uri (void)
+{
+	static char *desktop_uri = NULL;
+
+	if (desktop_uri == NULL) {
+		char *path;
+		char *uri;
+
+		path = xdg_user_dir_lookup ("DESKTOP");
+		uri = g_uri_escape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+
+		desktop_uri = g_strconcat ("file://", uri, NULL);
+
+		g_free (uri);
+		g_free (path);
+	}
+
+	return desktop_uri;
+}
+
+
+char *
+xdg_user_dir_lookup (const char *type)
+{
+	/* This function is used by gthumb to determine the default "PICTURES"
+	   directory for imports. The actually directory name is localized.
+	   Bug 425365. */
+
+	FILE *file;
+	char *home_dir, *config_home, *config_file;
+	char buffer[512];
+	char *user_dir;
+	char *p, *d;
+	int len;
+	int relative;
+
+	home_dir = getenv ("HOME");
+	if (home_dir == NULL)
+		return strdup ("/tmp");
+
+	config_home = getenv ("XDG_CONFIG_HOME");
+	if (config_home == NULL || config_home[0] == 0) {
+		config_file = malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1);
+		strcpy (config_file, home_dir);
+		strcat (config_file, "/.config/user-dirs.dirs");
+	}
+	else {
+		config_file = malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1);
+		strcpy (config_file, config_home);
+		strcat (config_file, "/user-dirs.dirs");
+	}
+
+	file = fopen (config_file, "r");
+	free (config_file);
+	if (file == NULL)
+		goto error;
+
+	user_dir = NULL;
+
+	while (fgets (buffer, sizeof (buffer), file)) {
+		/* Remove newline at end */
+		len = strlen (buffer);
+		if (len > 0 && buffer[len-1] == '\n')
+		buffer[len-1] = 0;
+
+		p = buffer;
+		while (*p == ' ' || *p == '\t')
+			p++;
+
+		if (strncmp (p, "XDG_", 4) != 0)
+			continue;
+		p += 4;
+
+		if (strncmp (p, type, strlen (type)) != 0)
+			continue;
+		p += strlen (type);
+
+		if (strncmp (p, "_DIR", 4) != 0)
+		continue;
+		p += 4;
+
+		while (*p == ' ' || *p == '\t')
+			p++;
+
+		if (*p != '=')
+			continue;
+		p++;
+
+		while (*p == ' ' || *p == '\t')
+			p++;
+
+		if (*p != '"')
+			continue;
+		p++;
+
+		relative = 0;
+		if (strncmp (p, "$HOME/", 6) == 0) {
+			p += 6;
+			relative = 1;
+		} else if (*p != '/')
+			continue;
+
+		if (relative) {
+			user_dir = malloc (strlen (home_dir) + 1 + strlen (p) + 1);
+			strcpy (user_dir, home_dir);
+			strcat (user_dir, "/");
+		} else {
+			user_dir = malloc (strlen (p) + 1);
+			*user_dir = 0;
+		}
+
+		d = user_dir + strlen (user_dir);
+		while (*p && *p != '"')	{
+			if ((*p == '\\') && (*(p+1) != 0))
+				p++;
+			*d++ = *p++;
+		}
+		*d = 0;
+	}
+
+	fclose (file);
+
+	if (user_dir)
+		return user_dir;
+
+error:
+	 /* Special case desktop for historical compatibility */
+	if (strcmp (type, "DESKTOP") == 0) {
+		user_dir = malloc (strlen (home_dir) + strlen ("/Desktop") + 1);
+		strcpy (user_dir, home_dir);
+		strcat (user_dir, "/Desktop");
+		return user_dir;
+	}
+	else
+		return strdup (home_dir);
+}
+
+
+int
+uricmp (const char *uri1,
+	const char *uri2)
+{
+	if (uri1 == NULL) {
+		if (uri2 == NULL)
+			return 0;
+		else
+			return -1;
+	}
+
+	if (uri2 == NULL) {
+		if (uri1 == NULL)
+			return 0;
+		else
+			return 1;
+	}
+
+	return g_strcmp0 (uri1, uri2);
+}
+
+
+gboolean
+same_uri (const char *uri1,
+	  const char *uri2)
+{
+	return uricmp (uri1, uri2) == 0;
+}
+
+
+void
+_g_string_list_free (GList *string_list)
+{
+	if (string_list == NULL)
+		return;
+	g_list_foreach (string_list, (GFunc) g_free, NULL);
+	g_list_free (string_list);
+
+}
+
+
+GList *
+_g_string_list_dup (GList *string_list)
+{
+	GList *new_list = NULL;
+	GList *scan;
+
+	for (scan = string_list; scan; scan = scan->next)
+		new_list = g_list_prepend (new_list, g_strdup (scan->data));
+
+	return g_list_reverse (new_list);
+}
+
+
+GType
+g_string_list_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0)
+		type = g_boxed_type_register_static ("GStringList",
+						     (GBoxedCopyFunc) _g_string_list_dup,
+						     (GBoxedFreeFunc) _g_string_list_free);
+
+	return type;
+}
+
+
+GList *
+get_file_list_from_url_list (char *url_list)
+{
+	GList *list = NULL;
+	int    i;
+	char  *url_start, *url_end;
+
+	url_start = url_list;
+	while (url_start[0] != '\0') {
+		char *url;
+
+		if (strncmp (url_start, "file:", 5) == 0) {
+			url_start += 5;
+			if ((url_start[0] == '/')
+			    && (url_start[1] == '/')) url_start += 2;
+		}
+
+		i = 0;
+		while ((url_start[i] != '\0')
+		       && (url_start[i] != '\r')
+		       && (url_start[i] != '\n')) i++;
+		url_end = url_start + i;
+
+		url = g_strndup (url_start, url_end - url_start);
+		list = g_list_prepend (list, url);
+
+		url_start = url_end;
+		i = 0;
+		while ((url_start[i] != '\0')
+		       && ((url_start[i] == '\r')
+			   || (url_start[i] == '\n'))) i++;
+		url_start += i;
+	}
+
+	return g_list_reverse (list);
+}
+
+
+const char *
+_g_uri_get_basename (const char *uri)
+{
+	register char   *base;
+	register gssize  last_char;
+
+	if (uri == NULL)
+		return NULL;
+
+	if (uri[0] == '\0')
+		return "";
+
+	last_char = strlen (uri) - 1;
+
+	if (uri[last_char] == G_DIR_SEPARATOR)
+		return "";
+
+	base = g_utf8_strrchr (uri, -1, G_DIR_SEPARATOR);
+	if (! base)
+		return uri;
+
+	return base + 1;
+}
+
+
+const char *
+_g_uri_get_file_extension (const char *uri)
+{
+	int         len;
+	int         p;
+	const char *ptr = uri;
+
+	if (uri == NULL)
+		return NULL;
+
+	len = strlen (uri);
+	if (len <= 1)
+		return NULL;
+
+	p = len - 1;
+	while ((p >= 0) && (ptr[p] != '.'))
+		p--;
+
+	if (p < 0)
+		return NULL;
+
+	return uri + p;
+}
+
+
+static gboolean
+uri_is_filetype (const char *uri,
+		 GFileType   file_type)
+{
+	gboolean   result = FALSE;
+	GFile     *file;
+	GFileInfo *info;
+	GError    *error = NULL;
+
+	file = g_file_new_for_uri (uri);
+
+	if (! g_file_query_exists (file, NULL)) {
+		g_object_unref (file);
+		return FALSE;
+	}
+
+	info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, &error);
+	if (error == NULL) {
+		result = (g_file_info_get_file_type (info) == file_type);
+	}
+	else {
+		g_warning ("Failed to get file type for uri %s: %s", uri, error->message);
+		g_error_free (error);
+	}
+
+	g_object_unref (info);
+	g_object_unref (file);
+
+	return result;
+}
+
+
+gboolean
+_g_uri_is_file (const char *uri)
+{
+	return uri_is_filetype (uri, G_FILE_TYPE_REGULAR);
+}
+
+
+gboolean
+_g_uri_is_dir (const char *uri)
+{
+	return uri_is_filetype (uri, G_FILE_TYPE_DIRECTORY);
+}
+
+
+gboolean
+_g_uri_parent_of_uri (const char *dirname,
+		      const char *filename)
+{
+	int dirname_l, filename_l, separator_position;
+
+	if ((dirname == NULL) || (filename == NULL))
+		return FALSE;
+
+	dirname_l = strlen (dirname);
+	filename_l = strlen (filename);
+
+	if ((dirname_l == filename_l + 1)
+	    && (dirname[dirname_l - 1] == '/'))
+		return FALSE;
+
+	if ((filename_l == dirname_l + 1)
+	    && (filename[filename_l - 1] == '/'))
+		return FALSE;
+
+	if (dirname[dirname_l - 1] == '/')
+		separator_position = dirname_l - 1;
+	else
+		separator_position = dirname_l;
+
+	return ((filename_l > dirname_l)
+		&& (strncmp (dirname, filename, dirname_l) == 0)
+		&& (filename[separator_position] == '/'));
+}
+
+
+char *
+_g_uri_get_parent (const char *uri)
+{
+	int         p;
+	const char *ptr = uri;
+	char       *new_uri;
+
+	if (uri == NULL)
+		return NULL;
+
+	p = strlen (uri) - 1;
+	if (p < 0)
+		return NULL;
+
+	while ((p > 0) && (ptr[p] != '/'))
+		p--;
+	if ((p == 0) && (ptr[p] == '/'))
+		p++;
+	new_uri = g_strndup (uri, (guint)p);
+
+	return new_uri;
+}
+
+
+char *
+_g_uri_remove_extension (const char *uri)
+{
+	int   len;
+	int   p;
+	char *new_path;
+
+	if (uri == NULL)
+		return NULL;
+
+	len = strlen (uri);
+	if (len == 1)
+		return g_strdup (uri);
+
+	p = len - 1;
+	while ((p > 0) && (uri[p] != '.'))
+		p--;
+	if (p == 0)
+		p = len;
+	new_path = g_strndup (uri, (guint) p);
+
+	return new_path;
+}
+
+
+char *
+_g_build_uri (const char *base, ...)
+{
+	va_list     args;
+	const char *child;
+	GString    *uri;
+
+	uri = g_string_new (base);
+
+	va_start (args, base);
+	while ((child = va_arg (args, const char *)) != NULL) {
+		if (! g_str_has_suffix (uri->str, "/") && ! g_str_has_prefix (child, "/"))
+			g_string_append (uri, "/");
+		g_string_append (uri, child);
+	}
 	va_end (args);
 
-	g_fprintf (stderr, "[GOO] %s:%d (%s):\n\t%s\n", file, line, function, str);
+	return g_string_free (uri, FALSE);
+}
 
-	g_free (str);
-#else /* ! DEBUG */
-#endif
+
+/* GIO utils */
+
+
+gboolean
+_g_file_equal (GFile *file1,
+	       GFile *file2)
+{
+	if ((file1 == NULL) && (file2 == NULL))
+		return TRUE;
+	if ((file1 == NULL) || (file2 == NULL))
+		return FALSE;
+
+	return g_file_equal (file1, file2);
+}
+
+
+char *
+_g_file_get_display_name (GFile *file)
+{
+	char      *name = NULL;
+	GFileInfo *file_info;
+
+	file_info = g_file_query_info (file,
+				       G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+				       G_FILE_QUERY_INFO_NONE,
+				       NULL,
+				       NULL);
+	if (file_info != NULL)
+		name = g_strdup (g_file_info_get_attribute_string (file_info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME));
+	else
+		name = g_file_get_parse_name (file);
+
+	return name;
+}
+
+
+GFileType
+_g_file_get_standard_type (GFile *file)
+{
+	GFileType  result;
+	GFileInfo *info;
+	GError    *error = NULL;
+
+	info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, &error);
+	if (error == NULL) {
+		result = g_file_info_get_file_type (info);
+	}
+	else {
+		result = G_FILE_ATTRIBUTE_TYPE_INVALID;
+		g_error_free (error);
+	}
+
+	g_object_unref (info);
+
+	return result;
+}
+
+
+GFile *
+_g_file_get_destination (GFile *source,
+		         GFile *source_base,
+		         GFile *destination_folder)
+{
+	char       *source_uri;
+	const char *source_suffix;
+	char       *destination_folder_uri;
+	char       *destination_uri;
+	GFile      *destination;
+
+	source_uri = g_file_get_uri (source);
+	if (source_base != NULL) {
+		char *source_base_uri;
+
+		source_base_uri = g_file_get_uri (source_base);
+		source_suffix = source_uri + strlen (source_base_uri);
+
+		g_free (source_base_uri);
+	}
+	else
+		source_suffix = _g_uri_get_basename (source_uri);
+
+	destination_folder_uri = g_file_get_uri (destination_folder);
+	destination_uri = g_strconcat (destination_folder_uri, "/", source_suffix, NULL);
+	destination = g_file_new_for_uri (destination_uri);
+
+	g_free (destination_uri);
+	g_free (destination_folder_uri);
+	g_free (source_uri);
+
+	return destination;
+}
+
+
+GFile *
+_g_file_get_child (GFile *file,
+		   ...)
+{
+	va_list     args;
+	const char *name;
+	GFile      *child;
+
+	child = g_object_ref (file);
+
+	va_start (args, file);
+	while ((name = va_arg (args, const char *)) != NULL) {
+		GFile *tmp;
+
+		tmp = g_file_get_child (child, name);
+		g_object_unref (child);
+		child = tmp;
+	}
+	va_end (args);
+
+	return child;
+}
+
+
+GIcon *
+_g_file_get_icon (GFile *file)
+{
+	GIcon     *icon = NULL;
+	GFileInfo *file_info;
+
+	file_info = g_file_query_info (file,
+				       G_FILE_ATTRIBUTE_STANDARD_ICON,
+				       G_FILE_QUERY_INFO_NONE,
+				       NULL,
+				       NULL);
+	if (file_info != NULL)
+		icon = (GIcon*) g_file_info_get_attribute_object (file_info, G_FILE_ATTRIBUTE_STANDARD_ICON);
+	else
+		icon = g_themed_icon_new ("file");
+
+	return icon;
+}
+
+
+GList *
+_g_file_list_dup (GList *l)
+{
+	GList *r = NULL, *scan;
+	for (scan = l; scan; scan = scan->next)
+		r = g_list_prepend (r, g_file_dup ((GFile*) scan->data));
+	return g_list_reverse (r);
+}
+
+
+void
+_g_file_list_free (GList *l)
+{
+	GList *scan;
+	for (scan = l; scan; scan = scan->next)
+		g_object_unref (scan->data);
+	g_list_free (l);
+}
+
+
+GList *
+_g_file_list_new_from_uri_list (GList *uris)
+{
+	GList *r = NULL, *scan;
+	for (scan = uris; scan; scan = scan->next)
+		r = g_list_prepend (r, g_file_new_for_uri ((char*)scan->data));
+	return g_list_reverse (r);
+}
+
+
+GList *
+_g_file_list_new_from_uriv (char **uris)
+{
+	GList *r = NULL;
+	int    i;
+
+	if (uris == NULL)
+		return NULL;
+
+	for (i = 0; uris[i] != NULL; i++)
+		r = g_list_prepend (r, g_file_new_for_uri (uris[i]));
+
+	return g_list_reverse (r);
+}
+
+
+GList *
+_g_file_list_find_file (GList *l,
+			GFile *file)
+{
+	GList *scan;
+
+	for (scan = l; scan; scan = scan->next)
+		if (g_file_equal (file, (GFile *) scan->data))
+			return scan;
+	return NULL;
+}
+
+
+const char*
+_g_file_get_mime_type (GFile    *file,
+		       gboolean  fast_file_type)
+{
+	GFileInfo  *info;
+	GError     *err = NULL;
+ 	const char *result = NULL;
+
+	info = g_file_query_info (file,
+				  fast_file_type ?
+				  G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE :
+				  G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+				  0, NULL, &err);
+	if (info == NULL) {
+		char *uri;
+
+		uri = g_file_get_uri (file);
+		g_warning ("could not get content type for %s: %s", uri, err->message);
+		g_free (uri);
+		g_clear_error (&err);
+	}
+	else {
+		result = get_static_string (g_content_type_get_mime_type (g_file_info_get_content_type (info)));
+		g_object_unref (info);
+	}
+
+	return result;
+}
+
+
+void
+_g_file_get_modification_time (GFile    *file,
+			       GTimeVal *result)
+{
+	GFileInfo *info;
+	GError    *err = NULL;
+
+	result->tv_sec = 0;
+	result->tv_usec = 0;
+
+	info = g_file_query_info (file,
+				  G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
+				  0,
+				  NULL,
+				  &err);
+	if (info != NULL) {
+		g_file_info_get_modification_time (info, result);
+		g_object_unref (info);
+	}
+	else {
+		char *uri;
+
+		uri = g_file_get_uri (file);
+		g_warning ("could not get modification time for %s: %s", uri, err->message);
+		g_free (uri);
+		g_clear_error (&err);
+	}
+}
+
+
+time_t
+_g_file_get_mtime (GFile *file)
+{
+	GTimeVal timeval;
+
+	_g_file_get_modification_time (file, &timeval);
+	return (time_t) timeval.tv_sec;
+}
+
+
+int
+_g_file_cmp_uris (GFile *a,
+		  GFile *b)
+{
+	return g_file_equal (a, b) ? 0 : 1;
+}
+
+
+int
+_g_file_cmp_modification_time (GFile *file_a,
+			       GFile *file_b)
+{
+	GTimeVal timeval_a;
+	GTimeVal timeval_b;
+
+	_g_file_get_modification_time (file_a, &timeval_a);
+	_g_file_get_modification_time (file_b, &timeval_b);
+
+	return _g_time_val_cmp (&timeval_a, &timeval_b);
+}
+
+
+goffset
+_g_file_get_size (GFile *file)
+{
+	GFileInfo *info;
+	GError    *err = NULL;
+	goffset    size = 0;
+
+	info = g_file_query_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, 0, NULL, &err);
+	if (info != NULL) {
+		size = g_file_info_get_size (info);
+		g_object_unref (info);
+	}
+	else {
+		char *uri;
+
+		uri = g_file_get_uri (file);
+		g_warning ("could not get size for %s: %s", uri, err->message);
+		g_free (uri);
+		g_clear_error (&err);
+	}
+
+	return size;
+}
+
+
+#define MAX_SYMLINKS 32
+
+
+static GFile *
+resolve_symlinks (GFile   *file,
+		  GError **error,
+		  int      level)
+{
+	GFile *resolved;
+	char  *path;
+	char **names;
+	int    i;
+
+	if (level > MAX_SYMLINKS) {
+		char *uri;
+
+		uri = g_file_get_uri (file);
+		*error = g_error_new (G_IO_ERROR, G_IO_ERROR_TOO_MANY_LINKS, "Too many symbolic links for file: %s.", uri);
+		g_free (uri);
+
+		return NULL;
+	}
+
+	path = g_file_get_path (file);
+	if (path == NULL) {
+		char *uri;
+
+		uri = g_file_get_uri (file);
+		*error = g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "No local pathname for file: %s.", uri);
+		g_free (uri);
+
+		return NULL;
+	}
+
+	resolved = g_file_new_for_path (G_DIR_SEPARATOR_S);
+
+	names = g_strsplit (path, G_DIR_SEPARATOR_S, -1);
+	for (i = 0; names[i] != NULL; i++) {
+		GFile     *child;
+		GFileInfo *info;
+		GFile     *new_resolved;
+
+		if (strcmp (names[i], "") == 0)
+			continue;
+
+		child = g_file_get_child (resolved, names[i]);
+		info = g_file_query_info (child, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET, 0, NULL, error);
+		if (info == NULL) {
+			g_object_unref (child);
+			g_object_unref (resolved);
+			resolved = NULL;
+			break;
+		}
+
+		/* if names[i] isn't a symbolic link add it to the resolved path and continue */
+
+		if (! g_file_info_get_is_symlink (info)) {
+			g_object_unref (info);
+			g_object_unref (resolved);
+			resolved = child;
+			continue;
+		}
+
+		g_object_unref (child);
+
+		/* names[i] is a symbolic link */
+
+		new_resolved = g_file_resolve_relative_path (resolved, g_file_info_get_symlink_target (info));
+
+		g_object_unref (resolved);
+		g_object_unref (info);
+
+		resolved = resolve_symlinks (new_resolved, error, level + 1);
+
+		g_object_unref (new_resolved);
+	}
+
+	g_strfreev (names);
+	g_free (path);
+
+	return resolved;
+}
+
+
+GFile *
+_g_file_resolve_all_symlinks (GFile   *file,
+			      GError **error)
+{
+	return resolve_symlinks (file, error, 0);
+}
+
+
+GFile *
+_g_file_append_prefix (GFile      *file,
+		       const char *prefix)
+{
+	char  *uri;
+	char  *new_uri;
+	GFile *new_file;
+
+	uri = g_file_get_uri (file);
+	new_uri = g_strconcat (prefix, uri, NULL);
+	new_file = g_file_new_for_uri (new_uri);
+
+	g_free (new_uri);
+	g_free (uri);
+
+	return new_file;
+}
+
+
+GFile *
+_g_file_append_path (GFile      *file,
+		     const char *path)
+{
+	char  *uri;
+	char  *escaped;
+	char  *new_uri;
+	GFile *new_file;
+
+	if (path == NULL)
+		return g_file_dup (file);
+
+	uri = g_file_get_uri (file);
+	escaped = g_uri_escape_string (path, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
+	new_uri = _g_build_uri (uri, escaped, NULL);
+	new_file = g_file_new_for_uri (new_uri);
+
+	g_free (new_uri);
+	g_free (escaped);
+	g_free (uri);
+
+	return new_file;
+}
+
+
+static gboolean
+_g_file_attributes_matches_mask (const char *attributes,
+			         const char *mask)
+{
+	gboolean   matches = FALSE;
+	char     **attributes_v;
+	char     **mask_v;
+	int        i;
+
+	attributes_v = g_strsplit (attributes, ",", -1);
+	mask_v = g_strsplit (mask, ",", -1);
+	for (i = 0; ! matches && (mask_v[i] != NULL); i++) {
+		GFileAttributeMatcher *matcher;
+		int                    j;
+
+		matcher = g_file_attribute_matcher_new (mask_v[i]);
+		for (j = 0; ! matches && (attributes_v[j] != NULL); j++)
+			matches = g_file_attribute_matcher_matches (matcher, attributes_v[j]);
+
+		g_file_attribute_matcher_unref (matcher);
+	}
+
+	g_strfreev (mask_v);
+	g_strfreev (attributes_v);
+
+	return matches;
+}
+
+
+gboolean
+_g_file_attributes_matches (const char *attributes,
+			    const char *mask)
+{
+	return _g_file_attributes_matches_mask (attributes, mask) || _g_file_attributes_matches_mask (mask, attributes);
+}
+
+
+/* -- _g_file_info_swap_attributes -- */
+
+
+typedef struct {
+	GFileAttributeType type;
+	union {
+		char      *string;
+		char     **stringv;
+		gboolean   boolean;
+		guint32    uint32;
+		gint32     int32;
+		guint64    uint64;
+		gint64     int64;
+		gpointer   object;
+	} v;
+} _GFileAttributeValue;
+
+
+static void
+_g_file_attribute_value_free (_GFileAttributeValue *attr_value)
+{
+	switch (attr_value->type) {
+	case G_FILE_ATTRIBUTE_TYPE_STRING:
+	case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
+		g_free (attr_value->v.string);
+		break;
+/* FIXME: add if glib >= 2.22
+	case G_FILE_ATTRIBUTE_TYPE_STRINGV:
+		g_strfreev (attr_value->v.stringv);
+		break;
+*/
+	case G_FILE_ATTRIBUTE_TYPE_OBJECT:
+		g_object_unref (attr_value->v.object);
+		break;
+	default:
+		break;
+	}
+
+	g_free (attr_value);
+}
+
+
+static _GFileAttributeValue *
+_g_file_info_get_value (GFileInfo  *info,
+			const char *attr)
+{
+	_GFileAttributeValue  *attr_value;
+	GFileAttributeType     type;
+	gpointer               value;
+	GFileAttributeStatus   status;
+
+	attr_value = g_new (_GFileAttributeValue, 1);
+	attr_value->type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+
+	if (! g_file_info_get_attribute_data (info, attr, &type, &value, &status))
+		return attr_value;
+
+	attr_value->type = type;
+	switch (type) {
+	case G_FILE_ATTRIBUTE_TYPE_INVALID:
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_STRING:
+	case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
+		attr_value->v.string = g_strdup ((char *) value);
+		break;
+/* FIXME: add if glib >= 2.22
+	case G_FILE_ATTRIBUTE_TYPE_STRINGV:
+		attr_value->v.stringv = g_strdupv ((char **) value);
+		break;
+*/
+	case G_FILE_ATTRIBUTE_TYPE_BOOLEAN:
+		attr_value->v.boolean = * ((gboolean *) value);
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_UINT32:
+		attr_value->v.uint32 = * ((guint32 *) value);
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_INT32:
+		attr_value->v.int32 = * ((gint32 *) value);
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_UINT64:
+		attr_value->v.uint64 = * ((guint64 *) value);
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_INT64:
+		attr_value->v.int64 = * ((gint64 *) value);
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_OBJECT:
+		attr_value->v.object = g_object_ref ((GObject *) value);
+		break;
+	default:
+		g_warning ("unknown attribute type: %d", type);
+		break;
+	}
+
+	return attr_value;
+}
+
+
+static void
+_g_file_info_set_value (GFileInfo            *info,
+			const char           *attr,
+			_GFileAttributeValue *attr_value)
+{
+	gpointer value = NULL;
+
+	if (attr_value->type == G_FILE_ATTRIBUTE_TYPE_INVALID)
+		return;
+
+	switch (attr_value->type) {
+	case G_FILE_ATTRIBUTE_TYPE_STRING:
+	case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
+		value = attr_value->v.string;
+		break;
+/* FIXME: add if glib >= 2.22
+	case G_FILE_ATTRIBUTE_TYPE_STRINGV:
+		value = attr_value->v.stringv;
+		break;
+*/
+	case G_FILE_ATTRIBUTE_TYPE_BOOLEAN:
+		value = &attr_value->v.boolean;
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_UINT32:
+		value = &attr_value->v.uint32;
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_INT32:
+		value = &attr_value->v.int32;
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_UINT64:
+		value = &attr_value->v.uint64;
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_INT64:
+		value = &attr_value->v.int64;
+		break;
+	case G_FILE_ATTRIBUTE_TYPE_OBJECT:
+		value = attr_value->v.object;
+		break;
+	default:
+		g_warning ("Unknown attribute type: %d", attr_value->type);
+		break;
+	}
+
+	g_file_info_set_attribute (info, attr, attr_value->type, value);
 }
 
 
 void
-set_time_string (char   *buffer,
-		 gint64  time)
+_g_file_info_swap_attributes (GFileInfo  *info,
+			      const char *attr1,
+			      const char *attr2)
+{
+	_GFileAttributeValue *value1;
+	_GFileAttributeValue *value2;
+
+	value1 = _g_file_info_get_value (info, attr1);
+	value2 = _g_file_info_get_value (info, attr2);
+
+	_g_file_info_set_value (info, attr1, value2);
+	_g_file_info_set_value (info, attr2, value1);
+
+	_g_file_attribute_value_free (value1);
+	_g_file_attribute_value_free (value2);
+}
+
+
+gboolean
+_g_mime_type_is_image (const char *mime_type)
+{
+	g_return_val_if_fail (mime_type != NULL, FALSE);
+
+	/* Valid image mime types:
+		1. All *image* types,
+		2. application/x-crw
+			This is a RAW photo file, which for some reason
+			uses an "application" prefix instead of "image".
+	*/
+
+	return (g_content_type_is_a (mime_type, "image/*")
+		|| (strcmp (mime_type, "application/x-crw") == 0));
+}
+
+
+gboolean
+_g_mime_type_is_video (const char *mime_type)
+{
+	g_return_val_if_fail (mime_type != NULL, FALSE);
+
+	return (g_content_type_is_a (mime_type, "video/*")
+		|| (strcmp (mime_type, "application/ogg") == 0)
+		|| (strcmp (mime_type, "application/vnd.rn-realmedia") == 0));
+}
+
+
+gboolean
+_g_mime_type_is_audio (const char *mime_type)
+{
+	g_return_val_if_fail (mime_type != NULL, FALSE);
+
+	return g_content_type_is_a (mime_type, "audio/*");
+}
+
+
+gboolean
+_g_volume_equal (GVolume *v1,
+		 GVolume *v2)
+{
+	char     *id1;
+	char     *id2;
+	gboolean  result;
+
+	id1 = g_volume_get_identifier (v1, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+	id2 = g_volume_get_identifier (v2, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+	result = g_strcmp0 (id1, id2) == 0;
+
+	g_free (id1);
+	g_free (id2);
+
+	return result;
+}
+
+
+gboolean
+_g_drive_equal (GDrive *d1,
+		GDrive *d2)
+{
+	char     *id1;
+	char     *id2;
+	gboolean  result;
+
+	id1 = g_drive_get_identifier (d1, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+	id2 = g_drive_get_identifier (d2, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+	result = g_strcmp0 (id1, id2) == 0;
+
+	g_free (id1);
+	g_free (id2);
+
+	return result;
+}
+
+
+char *
+_g_make_temp_directory (void)
+{
+	char *template;
+
+	template = g_strconcat (g_get_tmp_dir (), "/goobox-XXXXXX", NULL);
+	g_assert (mkdtemp (template) != NULL);
+
+	return template;
+}
+
+
+/* this is totem_time_to_string renamed, thanks to the authors :) */
+char *
+_g_format_duration_for_display (gint64 msecs)
 {
-	sprintf (buffer, 
-		 "%" G_GINT64_FORMAT ":%02" G_GINT64_FORMAT " ", 
-		 time / 60, time % 60);
+        int sec, min, hour, _time;
+
+        _time = (int) (msecs / 1000);
+        sec = _time % 60;
+        _time = _time - sec;
+        min = (_time % (60*60)) / 60;
+        _time = _time - (min * 60);
+        hour = _time / (60*60);
+
+        if (hour > 0)
+        {
+                /* hour:minutes:seconds */
+                /* Translators: This is a time format, like "9:05:02" for 9
+                 * hours, 5 minutes, and 2 seconds. You may change ":" to
+                 * the separator that your locale uses or use "%Id" instead
+                 * of "%d" if your locale uses localized digits.
+                 */
+                return g_strdup_printf (C_("long time format", "%d:%02d:%02d"), hour, min, sec);
+        }
+
+        /* minutes:seconds */
+        /* Translators: This is a time format, like "5:02" for 5
+         * minutes and 2 seconds. You may change ":" to the
+         * separator that your locale uses or use "%Id" instead of
+         * "%d" if your locale uses localized digits.
+         */
+        return g_strdup_printf (C_("short time format", "%d:%02d"), min, sec);
 }
diff --git a/src/glib-utils.h b/src/glib-utils.h
index d1d61e8..7377420 100644
--- a/src/glib-utils.h
+++ b/src/glib-utils.h
@@ -3,7 +3,7 @@
 /*
  *  GThumb
  *
- *  Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *  Copyright (C) 2001-2008 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -23,60 +23,246 @@
 #ifndef _GLIB_UTILS_H
 #define _GLIB_UTILS_H
 
+#include <glib.h>
 #include <glib-object.h>
+#include <gio/gio.h>
+#include "typedefs.h"
+
+G_BEGIN_DECLS
+
+#define GFILE_NAME_TYPE_ATTRIBUTES "standard::name,standard::type"
+#define GFILE_DISPLAY_ATTRIBUTES "standard::display-name,standard::icon"
+#define GFILE_BASIC_ATTRIBUTES GFILE_DISPLAY_ATTRIBUTES ",standard::name,standard::type"
+
+#define DEFINE_STANDARD_ATTRIBUTES(a) ( \
+	"standard::type," \
+	"standard::is-hidden," \
+	"standard::is-backup," \
+	"standard::name," \
+	"standard::display-name," \
+	"standard::edit-name," \
+	"standard::icon," \
+	"standard::size," \
+	"thumbnail::path" \
+	"time::created," \
+	"time::created-usec," \
+	"time::modified," \
+	"time::modified-usec," \
+	"access::*" \
+	a)
+#define GFILE_STANDARD_ATTRIBUTES (DEFINE_STANDARD_ATTRIBUTES(""))
+#define GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE (DEFINE_STANDARD_ATTRIBUTES(",standard::fast-content-type"))
+#define GFILE_STANDARD_ATTRIBUTES_WITH_CONTENT_TYPE (DEFINE_STANDARD_ATTRIBUTES(",standard::fast-content-type,standard::content-type"))
+
+#define GNOME_COPIED_FILES (gdk_atom_intern_static_string ("x-special/gnome-copied-files"))
+#define IROUND(x) ((int)floor(((double)x) + 0.5))
+#define FLOAT_EQUAL(a,b) (fabs (a - b) < 1e-6)
+#define ID_LENGTH 8
+#define G_TYPE_OBJECT_LIST (g_object_list_get_type ())
+#define G_TYPE_STRING_LIST (g_string_list_get_type ())
+
+/* signals */
 
 #define g_signal_handlers_disconnect_by_data(instance, data) \
     g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, \
-                                          0, 0, NULL, NULL, (data))
+					  0, 0, NULL, NULL, (data))
 #define g_signal_handlers_block_by_data(instance, data) \
     g_signal_handlers_block_matched ((instance), G_SIGNAL_MATCH_DATA, \
-                                     0, 0, NULL, NULL, (data))
+				     0, 0, NULL, NULL, (data))
 #define g_signal_handlers_unblock_by_data(instance, data) \
     g_signal_handlers_unblock_matched ((instance), G_SIGNAL_MATCH_DATA, \
-                                       0, 0, NULL, NULL, (data))
+				       0, 0, NULL, NULL, (data))
+
+/* gobject utils*/
+
+gpointer      _g_object_ref                  (gpointer     object);
+void          _g_object_unref                (gpointer     object);
+void          _g_object_clear                (gpointer     object);
+GList *       _g_object_list_ref             (GList       *list);
+void          _g_object_list_unref           (GList       *list);
+GType         g_object_list_get_type         (void);
+GEnumValue *  _g_enum_type_get_value         (GType        enum_type,
+					      int          value);
+GEnumValue *  _g_enum_type_get_value_by_nick (GType        enum_type,
+					      const char  *nick);
+
+/* idle callback */
+
+typedef struct {
+	DataFunc func;
+	gpointer data;
+} IdleCall;
+
+
+IdleCall* idle_call_new           (DataFunc       func,
+				   gpointer       data);
+void      idle_call_free          (IdleCall      *call);
+guint     idle_call_exec          (IdleCall      *call,
+				   gboolean       use_idle_cb);
+guint     call_when_idle          (DataFunc       func,
+				   gpointer       data);
+void      object_ready_with_error (gpointer       object,
+				   ReadyCallback  ready_func,
+				   gpointer       user_data,
+				   GError        *error);
+void      ready_with_error        (ReadyFunc      ready_func,
+				   gpointer       user_data,
+				   GError        *error);
+
+/* debug */
+
+void debug       (const char *file,
+		  int         line,
+		  const char *function,
+		  const char *format,
+		  ...);
+void performance (const char *file,
+		  int         line,
+		  const char *function,
+		  const char *format,
+		  ...);
+
+#define DEBUG_INFO __FILE__, __LINE__, G_STRFUNC
 
+/* GTimeVal utils */
 
-char *   _g_strdup_with_max_size   (const char *s,
-				    int         max_size);
+int             _g_time_val_cmp                  (GTimeVal   *a,
+	 					  GTimeVal   *b);
+void            _g_time_val_reset                (GTimeVal   *time_);
+gboolean        _g_time_val_from_exif_date       (const char *exif_date,
+						  GTimeVal   *time_);
+char *          _g_time_val_to_exif_date         (GTimeVal   *time_);
 
-char **  _g_get_template_from_text (const char *template);
+/* Bookmark file utils */
 
-char *   _g_get_name_from_template (char      **template,
-				    int         num);
+void            _g_bookmark_file_clear           (GBookmarkFile  *bookmark);
+void            _g_bookmark_file_add_uri         (GBookmarkFile  *bookmark,
+						  const char     *uri);
+void            _g_bookmark_file_set_uris        (GBookmarkFile  *bookmark,
+						  GList          *uri_list);
 
-char *   _g_substitute             (const char *from,
-				    const char  this,
-				    const char *with_this);
+/* String utils */
 
-char *   _g_substitute_pattern     (const char *utf8_text, 
-				    gunichar    pattern, 
-				    const char *value);
+void            _g_strset                        (char       **s,
+						  const char  *value);
+char *          _g_strdup_with_max_size          (const char  *s,
+						  int          max_size);
+char **         _g_get_template_from_text        (const char  *s_template);
+char *          _g_get_name_from_template        (char       **s_template,
+						  int          num);
+char *          _g_replace                       (const char  *str,
+						  const char  *from_str,
+						  const char  *to_str);
+char *          _g_replace_pattern               (const char  *utf8_text,
+						  gunichar     pattern,
+						  const char  *value);
+char *          _g_utf8_replace                  (const char  *string,
+						  const char  *pattern,
+						  const char  *replacement);
+char *          _g_utf8_strndup                  (const char  *str,
+						  gsize        n);
+char **         _g_utf8_strsplit                 (const char *string,
+						  const char *delimiter,
+						  int         max_tokens);
+char *          _g_utf8_strstrip                 (const char  *str);
+gboolean        _g_utf8_all_spaces               (const char  *utf8_string);
+char *          _g_utf8_remove_extension         (const char  *str);
+GList *         _g_list_insert_list_before       (GList       *list1,
+						  GList       *sibling,
+						  GList       *list2);
+const char *    get_static_string                (const char  *s);
+char *          _g_rand_string                   (int          len);
+int             _g_strv_find                     (char        **v,
+						  const char   *s);
+char *          _g_str_remove_suffix             (const char   *s,
+						  const char   *suffix);
 
-char *   _g_utf8_strndup           (const char *str,
-				    gsize       n);
+/* Regexp utils */
 
-char **  _g_utf8_strsplit          (const char *str,
-				    gunichar    delimiter);
+GRegex **       get_regexps_from_pattern         (const char  *pattern_string,
+						  GRegexCompileFlags  compile_options);
+gboolean        string_matches_regexps           (GRegex     **regexps,
+						  const char  *string,
+						  GRegexMatchFlags match_options);
+void            free_regexps                     (GRegex     **regexps);
 
-char *   _g_utf8_strstrip          (const char *str);
 
-gboolean _g_utf8_all_spaces        (const char *utf8_string);
+/* URI utils */
 
-void     set_time_string           (char       *buffer,
-				    gint64      time);
+const char *    get_home_uri                     (void);
+const char *    get_desktop_uri                  (void);
+char *          xdg_user_dir_lookup              (const char *type);
+int             uricmp                           (const char *uri1,
+						  const char *uri2);
+gboolean        same_uri                         (const char *uri1,
+						  const char *uri2);
+void            _g_string_list_free              (GList      *string_list);
+GList *         _g_string_list_dup               (GList      *string_list);
+GType           g_string_list_get_type           (void);
+GList *         get_file_list_from_url_list      (char       *url_list);
+const char *    _g_uri_get_basename              (const char *uri);
+const char *    _g_uri_get_file_extension        (const char *uri);
+gboolean        _g_uri_is_file                   (const char *uri);
+gboolean        _g_uri_is_dir                    (const char *uri);
+gboolean        _g_uri_parent_of_uri             (const char *dir,
+						  const char *file);
+char *          _g_uri_get_parent                (const char *uri);
+char *          _g_uri_remove_extension          (const char *uri);
+char *          _g_build_uri                     (const char *base,
+						  ...);
 
+/* GIO utils */
 
-/**/
+gboolean        _g_file_equal                    (GFile      *file1,
+						  GFile      *file2);
+char *          _g_file_get_display_name         (GFile      *file);
+GFileType 	_g_file_get_standard_type        (GFile      *file);
+GFile *         _g_file_get_destination          (GFile      *source,
+						  GFile      *source_base,
+						  GFile      *destination_folder);
+GFile *         _g_file_get_child                (GFile      *file,
+						  ...);
+GIcon *         _g_file_get_icon                 (GFile      *file);
+GList *         _g_file_list_dup                 (GList      *l);
+void            _g_file_list_free                (GList      *l);
+GList *         _g_file_list_new_from_uri_list   (GList      *uris);
+GList *         _g_file_list_new_from_uriv       (char      **uris);
+GList *         _g_file_list_find_file           (GList      *l,
+						  GFile      *file);
+const char*     _g_file_get_mime_type            (GFile      *file,
+						  gboolean    fast_file_type);
+void            _g_file_get_modification_time    (GFile      *file,
+						  GTimeVal   *result);
+time_t          _g_file_get_mtime                (GFile      *file);
+int             _g_file_cmp_uris                 (GFile      *a,
+						  GFile      *b);
+int             _g_file_cmp_modification_time    (GFile      *a,
+						  GFile      *b);
+goffset         _g_file_get_size                 (GFile      *info);
+GFile *         _g_file_resolve_all_symlinks     (GFile      *file,
+						  GError    **error);
+GFile *         _g_file_append_prefix            (GFile      *file,
+						  const char *prefix);
+GFile *         _g_file_append_path              (GFile      *file,
+						  const char *path);
+gboolean        _g_file_attributes_matches       (const char *attributes,
+						  const char *mask);
+void            _g_file_info_swap_attributes     (GFileInfo  *info,
+						  const char *attr1,
+						  const char *attr2);
+gboolean        _g_mime_type_is_image            (const char *mime_type);
+gboolean        _g_mime_type_is_video            (const char *mime_type);
+gboolean        _g_mime_type_is_audio            (const char *mime_type);
+gboolean        _g_volume_equal                  (GVolume    *v1,
+						  GVolume    *v2);
+gboolean        _g_drive_equal                   (GDrive     *d1,
+						  GDrive     *d2);
+char *          _g_make_temp_directory           (void);
 
-#ifndef __GNUC__
-#define __FUNCTION__ ""
-#endif
+/* Other */
 
-#define DEBUG_INFO __FILE__, __LINE__, __FUNCTION__
+char *          _g_format_duration_for_display   (gint64 msecs);
 
-void     debug                     (const char *file,
-				    int         line,
-				    const char *function,
-				    const char *format, ...);
+G_END_DECLS
 
 #endif /* _GLIB_UTILS_H */
diff --git a/src/gnome-desktop-thumbnail.c b/src/gnome-desktop-thumbnail.c
new file mode 100644
index 0000000..7cdbf95
--- /dev/null
+++ b/src/gnome-desktop-thumbnail.c
@@ -0,0 +1,1329 @@
+/*
+ * gnome-thumbnail.c: Utilities for handling thumbnails
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+#include <glib.h>
+#include <stdio.h>
+
+#define GDK_PIXBUF_ENABLE_BACKEND
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-desktop-thumbnail.h"
+#include <gconf/gconf.h>
+#include <gconf/gconf-client.h>
+#include <glib/gstdio.h>
+
+#define SECONDS_BETWEEN_STATS 10
+
+struct _GnomeDesktopThumbnailFactoryPrivate {
+  char *application;
+  GnomeDesktopThumbnailSize size;
+
+  GMutex *lock;
+
+  GHashTable *scripts_hash;
+  guint thumbnailers_notify;
+  guint reread_scheduled;
+};
+
+static void gnome_desktop_thumbnail_factory_init          (GnomeDesktopThumbnailFactory      *factory);
+static void gnome_desktop_thumbnail_factory_class_init    (GnomeDesktopThumbnailFactoryClass *class);
+
+G_DEFINE_TYPE (GnomeDesktopThumbnailFactory,
+	       gnome_desktop_thumbnail_factory,
+	       G_TYPE_OBJECT)
+#define parent_class gnome_desktop_thumbnail_factory_parent_class
+
+typedef struct {
+    gint width;
+    gint height;
+    gint input_width;
+    gint input_height;
+    gboolean preserve_aspect_ratio;
+} SizePrepareContext;
+
+#define LOAD_BUFFER_SIZE 4096
+
+static void
+size_prepared_cb (GdkPixbufLoader *loader,
+		  int              width,
+		  int              height,
+		  gpointer         data)
+{
+  SizePrepareContext *info = data;
+
+  g_return_if_fail (width > 0 && height > 0);
+
+  info->input_width = width;
+  info->input_height = height;
+
+  if (width < info->width && height < info->height) return;
+
+  if (info->preserve_aspect_ratio &&
+      (info->width > 0 || info->height > 0)) {
+    if (info->width < 0)
+      {
+	width = width * (double)info->height/(double)height;
+	height = info->height;
+      }
+    else if (info->height < 0)
+      {
+	height = height * (double)info->width/(double)width;
+	width = info->width;
+      }
+    else if ((double)height * (double)info->width >
+	     (double)width * (double)info->height) {
+      width = 0.5 + (double)width * (double)info->height / (double)height;
+      height = info->height;
+    } else {
+      height = 0.5 + (double)height * (double)info->width / (double)width;
+      width = info->width;
+    }
+  } else {
+    if (info->width > 0)
+      width = info->width;
+    if (info->height > 0)
+      height = info->height;
+  }
+
+  gdk_pixbuf_loader_set_size (loader, width, height);
+}
+
+static GdkPixbuf *
+_gdk_pixbuf_new_from_uri_at_scale (const char *uri,
+				   gint        width,
+				   gint        height,
+				   gboolean    preserve_aspect_ratio)
+{
+    gboolean result;
+    char buffer[LOAD_BUFFER_SIZE];
+    gsize bytes_read;
+    GdkPixbufLoader *loader;
+    GdkPixbuf *pixbuf;
+    GdkPixbufAnimation *animation;
+    GdkPixbufAnimationIter *iter;
+    gboolean has_frame;
+    SizePrepareContext info;
+    GFile *file;
+    GInputStream *input_stream;
+
+    g_return_val_if_fail (uri != NULL, NULL);
+
+    file = g_file_new_for_uri (uri);
+
+    input_stream = G_INPUT_STREAM (g_file_read (file, NULL, NULL));
+    if (input_stream == NULL) {
+	g_object_unref (file);
+	return NULL;
+    }
+
+    loader = gdk_pixbuf_loader_new ();
+    if (1 <= width || 1 <= height) {
+        info.width = width;
+        info.height = height;
+	info.input_width = info.input_height = 0;
+        info.preserve_aspect_ratio = preserve_aspect_ratio;
+        g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info);
+    }
+
+    has_frame = FALSE;
+
+    result = FALSE;
+    while (!has_frame) {
+
+	bytes_read = g_input_stream_read (input_stream,
+					  buffer,
+					  sizeof (buffer),
+					  NULL,
+					  NULL);
+	if (bytes_read == -1) {
+	    break;
+	}
+	result = TRUE;
+	if (bytes_read == 0) {
+	    break;
+	}
+
+	if (!gdk_pixbuf_loader_write (loader,
+				      (unsigned char *)buffer,
+				      bytes_read,
+				      NULL)) {
+	    result = FALSE;
+	    break;
+	}
+
+	animation = gdk_pixbuf_loader_get_animation (loader);
+	if (animation) {
+		iter = gdk_pixbuf_animation_get_iter (animation, NULL);
+		if (!gdk_pixbuf_animation_iter_on_currently_loading_frame (iter)) {
+			has_frame = TRUE;
+		}
+		g_object_unref (iter);
+	}
+    }
+
+    gdk_pixbuf_loader_close (loader, NULL);
+
+    if (!result) {
+	g_object_unref (G_OBJECT (loader));
+	g_input_stream_close (input_stream, NULL, NULL);
+	g_object_unref (input_stream);
+	g_object_unref (file);
+	return NULL;
+    }
+
+    g_input_stream_close (input_stream, NULL, NULL);
+    g_object_unref (input_stream);
+    g_object_unref (file);
+
+    pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+    if (pixbuf != NULL) {
+	g_object_ref (G_OBJECT (pixbuf));
+	g_object_set_data (G_OBJECT (pixbuf), "gnome-original-width",
+			   GINT_TO_POINTER (info.input_width));
+	g_object_set_data (G_OBJECT (pixbuf), "gnome-original-height",
+			   GINT_TO_POINTER (info.input_height));
+    }
+    g_object_unref (G_OBJECT (loader));
+
+    return pixbuf;
+}
+
+static void
+gnome_desktop_thumbnail_factory_finalize (GObject *object)
+{
+  GnomeDesktopThumbnailFactory *factory;
+  GnomeDesktopThumbnailFactoryPrivate *priv;
+  GConfClient *client;
+
+  factory = GNOME_DESKTOP_THUMBNAIL_FACTORY (object);
+
+  priv = factory->priv;
+
+  g_free (priv->application);
+  priv->application = NULL;
+
+  if (priv->reread_scheduled != 0) {
+    g_source_remove (priv->reread_scheduled);
+    priv->reread_scheduled = 0;
+  }
+
+  if (priv->thumbnailers_notify != 0) {
+    client = gconf_client_get_default ();
+    gconf_client_notify_remove (client, priv->thumbnailers_notify);
+    priv->thumbnailers_notify = 0;
+    g_object_unref (client);
+  }
+
+  if (priv->scripts_hash)
+    {
+      g_hash_table_destroy (priv->scripts_hash);
+      priv->scripts_hash = NULL;
+    }
+
+  if (priv->lock)
+    {
+      g_mutex_free (priv->lock);
+      priv->lock = NULL;
+    }
+
+  g_free (priv);
+  factory->priv = NULL;
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+/* Must be called on main thread */
+static GHashTable *
+read_scripts (void)
+{
+  GHashTable *scripts_hash;
+  GConfClient *client;
+  GSList *subdirs, *l;
+  char *subdir, *enable, *escape, *commandkey, *command, *mimetype;
+
+  client = gconf_client_get_default ();
+
+  if (gconf_client_get_bool (client,
+			     "/desktop/gnome/thumbnailers/disable_all",
+			     NULL))
+    {
+      g_object_unref (G_OBJECT (client));
+      return NULL;
+    }
+
+  scripts_hash = g_hash_table_new_full (g_str_hash,
+					g_str_equal,
+					g_free, g_free);
+
+
+  subdirs = gconf_client_all_dirs (client, "/desktop/gnome/thumbnailers", NULL);
+
+  for (l = subdirs; l != NULL; l = l->next)
+    {
+      subdir = l->data;
+
+      enable = g_strdup_printf ("%s/enable", subdir);
+      if (gconf_client_get_bool (client,
+				 enable,
+				 NULL))
+	{
+	  commandkey = g_strdup_printf ("%s/command", subdir);
+	  command = gconf_client_get_string (client, commandkey, NULL);
+	  g_free (commandkey);
+
+	  if (command != NULL) {
+	    mimetype = strrchr (subdir, '/');
+	    if (mimetype != NULL)
+	      {
+		mimetype++; /* skip past slash */
+
+		/* Convert '@' to slash in mimetype */
+		escape = strchr (mimetype, '@');
+		if (escape != NULL)
+		  *escape = '/';
+
+		/* Convert any remaining '@' to '+' in mimetype */
+		while ((escape = strchr (mimetype, '@')) != NULL)
+                  *escape = '+';
+
+		g_hash_table_insert (scripts_hash,
+				     g_strdup (mimetype), command);
+	      }
+	    else
+	      {
+		g_free (command);
+	      }
+	  }
+	}
+      g_free (enable);
+
+      g_free (subdir);
+    }
+
+  g_slist_free(subdirs);
+
+  g_object_unref (G_OBJECT (client));
+
+  return scripts_hash;
+}
+
+
+/* Must be called on main thread */
+static void
+gnome_desktop_thumbnail_factory_reread_scripts (GnomeDesktopThumbnailFactory *factory)
+{
+  GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+  GHashTable *scripts_hash;
+
+  scripts_hash = read_scripts ();
+
+  g_mutex_lock (priv->lock);
+
+  if (priv->scripts_hash != NULL)
+    g_hash_table_destroy (priv->scripts_hash);
+
+  priv->scripts_hash = scripts_hash;
+
+  g_mutex_unlock (priv->lock);
+}
+
+static gboolean
+reread_idle_callback (gpointer user_data)
+{
+  GnomeDesktopThumbnailFactory *factory = user_data;
+  GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+
+  gnome_desktop_thumbnail_factory_reread_scripts (factory);
+
+  g_mutex_lock (priv->lock);
+  priv->reread_scheduled = 0;
+  g_mutex_unlock (priv->lock);
+
+  return FALSE;
+}
+
+static void
+schedule_reread (GConfClient* client,
+		 guint cnxn_id,
+		 GConfEntry *entry,
+		 gpointer user_data)
+{
+  GnomeDesktopThumbnailFactory *factory = user_data;
+  GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+
+  g_mutex_lock (priv->lock);
+
+  if (priv->reread_scheduled == 0)
+    {
+      priv->reread_scheduled = g_idle_add (reread_idle_callback,
+					   factory);
+    }
+
+  g_mutex_unlock (priv->lock);
+}
+
+
+static void
+gnome_desktop_thumbnail_factory_init (GnomeDesktopThumbnailFactory *factory)
+{
+  GConfClient *client;
+  GnomeDesktopThumbnailFactoryPrivate *priv;
+
+  factory->priv = g_new0 (GnomeDesktopThumbnailFactoryPrivate, 1);
+
+  priv = factory->priv;
+
+  priv->size = GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL;
+  priv->application = g_strdup ("gnome-thumbnail-factory");
+
+  priv->scripts_hash = NULL;
+
+  priv->lock = g_mutex_new ();
+
+  gnome_desktop_thumbnail_factory_reread_scripts (factory);
+
+  client = gconf_client_get_default ();
+  gconf_client_add_dir (client,
+			"/desktop/gnome",
+			GCONF_CLIENT_PRELOAD_NONE, NULL);
+
+  priv->thumbnailers_notify = gconf_client_notify_add (client, "/desktop/gnome/thumbnailers",
+						       schedule_reread, factory, NULL,
+						       NULL);
+
+  g_object_unref (G_OBJECT (client));
+}
+
+static void
+gnome_desktop_thumbnail_factory_class_init (GnomeDesktopThumbnailFactoryClass *class)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = G_OBJECT_CLASS (class);
+
+  gobject_class->finalize = gnome_desktop_thumbnail_factory_finalize;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_new:
+ * @size: The thumbnail size to use
+ *
+ * Creates a new #GnomeDesktopThumbnailFactory.
+ *
+ * This function must be called on the main thread.
+ *
+ * Return value: a new #GnomeDesktopThumbnailFactory
+ *
+ * Since: 2.2
+ **/
+GnomeDesktopThumbnailFactory *
+gnome_desktop_thumbnail_factory_new (GnomeDesktopThumbnailSize size)
+{
+  GnomeDesktopThumbnailFactory *factory;
+
+  factory = g_object_new (GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, NULL);
+
+  factory->priv->size = size;
+
+  return factory;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_lookup:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mtime: the mtime of the file
+ *
+ * Tries to locate an existing thumbnail for the file specified.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: The absolute path of the thumbnail, or %NULL if none exist.
+ *
+ * Since: 2.2
+ **/
+char *
+gnome_desktop_thumbnail_factory_lookup (GnomeDesktopThumbnailFactory *factory,
+					const char            *uri,
+					time_t                 mtime)
+{
+  GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+  char *path, *file;
+  GChecksum *checksum;
+  guint8 digest[16];
+  gsize digest_len = sizeof (digest);
+  GdkPixbuf *pixbuf;
+  gboolean res;
+
+  g_return_val_if_fail (uri != NULL, NULL);
+
+  res = FALSE;
+
+  checksum = g_checksum_new (G_CHECKSUM_MD5);
+  g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+  g_checksum_get_digest (checksum, digest, &digest_len);
+  g_assert (digest_len == 16);
+
+  file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+  path = g_build_filename (g_get_home_dir (),
+			   ".thumbnails",
+			   (priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+			   file,
+			   NULL);
+  g_free (file);
+
+  pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+  if (pixbuf != NULL)
+    {
+      res = gnome_desktop_thumbnail_is_valid (pixbuf, uri, mtime);
+      g_object_unref (pixbuf);
+    }
+
+  g_checksum_free (checksum);
+
+  if (res)
+    return path;
+
+  g_free (path);
+  return FALSE;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mtime: the mtime of the file
+ *
+ * Tries to locate an failed thumbnail for the file specified. Writing
+ * and looking for failed thumbnails is important to avoid to try to
+ * thumbnail e.g. broken images several times.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: TRUE if there is a failed thumbnail for the file.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+							    const char            *uri,
+							    time_t                 mtime)
+{
+  char *path, *file;
+  GdkPixbuf *pixbuf;
+  gboolean res;
+  GChecksum *checksum;
+  guint8 digest[16];
+  gsize digest_len = sizeof (digest);
+
+  checksum = g_checksum_new (G_CHECKSUM_MD5);
+  g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+  g_checksum_get_digest (checksum, digest, &digest_len);
+  g_assert (digest_len == 16);
+
+  res = FALSE;
+
+  file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+  path = g_build_filename (g_get_home_dir (),
+			   ".thumbnails/fail",
+			   factory->priv->application,
+			   file,
+			   NULL);
+  g_free (file);
+
+  pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+  g_free (path);
+
+  if (pixbuf)
+    {
+      res = gnome_desktop_thumbnail_is_valid (pixbuf, uri, mtime);
+      g_object_unref (pixbuf);
+    }
+
+  g_checksum_free (checksum);
+
+  return res;
+}
+
+static gboolean
+mimetype_supported_by_gdk_pixbuf (const char *mime_type)
+{
+	guint i;
+	static GHashTable *formats_hash = NULL;
+
+	if (!formats_hash) {
+		GSList *formats, *list;
+
+		formats_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+		formats = gdk_pixbuf_get_formats ();
+		list = formats;
+
+		while (list) {
+			GdkPixbufFormat *format = list->data;
+			gchar **mime_types;
+
+			mime_types = gdk_pixbuf_format_get_mime_types (format);
+
+			for (i = 0; mime_types[i] != NULL; i++)
+				g_hash_table_insert (formats_hash,
+						     (gpointer) g_strdup (mime_types[i]),
+						     GUINT_TO_POINTER (1));
+
+			g_strfreev (mime_types);
+			list = list->next;
+		}
+		g_slist_free (formats);
+	}
+
+	if (g_hash_table_lookup (formats_hash, mime_type))
+		return TRUE;
+
+	return FALSE;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_can_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mime_type: the mime type of the file
+ * @mtime: the mtime of the file
+ *
+ * Returns TRUE if this GnomeIconFactory can (at least try) to thumbnail
+ * this file. Thumbnails or files with failed thumbnails won't be thumbnailed.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: TRUE if the file can be thumbnailed.
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *factory,
+					       const char            *uri,
+					       const char            *mime_type,
+					       time_t                 mtime)
+{
+  /* Don't thumbnail thumbnails */
+  if (uri &&
+      strncmp (uri, "file:/", 6) == 0 &&
+      strstr (uri, "/.thumbnails/") != NULL)
+    return FALSE;
+
+  if (mime_type != NULL &&
+      (mimetype_supported_by_gdk_pixbuf (mime_type) ||
+       (factory->priv->scripts_hash != NULL &&
+	g_hash_table_lookup (factory->priv->scripts_hash, mime_type))))
+    {
+      return !gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (factory,
+								  uri,
+								  mtime);
+    }
+
+  return FALSE;
+}
+
+static char *
+expand_thumbnailing_script (const char *script,
+			    const int   size,
+			    const char *inuri,
+			    const char *outfile)
+{
+  GString *str;
+  const char *p, *last;
+  char *localfile, *quoted;
+  gboolean got_in;
+
+  str = g_string_new (NULL);
+
+  got_in = FALSE;
+  last = script;
+  while ((p = strchr (last, '%')) != NULL)
+    {
+      g_string_append_len (str, last, p - last);
+      p++;
+
+      switch (*p) {
+      case 'u':
+	quoted = g_shell_quote (inuri);
+	g_string_append (str, quoted);
+	g_free (quoted);
+	got_in = TRUE;
+	p++;
+	break;
+      case 'i':
+	localfile = g_filename_from_uri (inuri, NULL, NULL);
+	if (localfile)
+	  {
+	    quoted = g_shell_quote (localfile);
+	    g_string_append (str, quoted);
+	    got_in = TRUE;
+	    g_free (quoted);
+	    g_free (localfile);
+	  }
+	p++;
+	break;
+      case 'o':
+	quoted = g_shell_quote (outfile);
+	g_string_append (str, quoted);
+	g_free (quoted);
+	p++;
+	break;
+      case 's':
+	g_string_append_printf (str, "%d", size);
+	p++;
+	break;
+      case '%':
+	g_string_append_c (str, '%');
+	p++;
+	break;
+      case 0:
+      default:
+	break;
+      }
+      last = p;
+    }
+  g_string_append (str, last);
+
+  if (got_in)
+    return g_string_free (str, FALSE);
+
+  g_string_free (str, TRUE);
+  return NULL;
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_generate_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mime_type: the mime type of the file
+ *
+ * Tries to generate a thumbnail for the specified file. If it succeeds
+ * it returns a pixbuf that can be used as a thumbnail.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Return value: thumbnail pixbuf if thumbnailing succeeded, %NULL otherwise.
+ *
+ * Since: 2.2
+ **/
+GdkPixbuf *
+gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory *factory,
+						    const char            *uri,
+						    const char            *mime_type)
+{
+  GdkPixbuf *pixbuf, *scaled, *tmp_pixbuf;
+  char *script, *expanded_script;
+  int width, height, size;
+  int original_width = 0;
+  int original_height = 0;
+  char dimension[12];
+  double scale;
+  int exit_status;
+  char *tmpname;
+
+  g_return_val_if_fail (uri != NULL, NULL);
+  g_return_val_if_fail (mime_type != NULL, NULL);
+
+  /* Doesn't access any volatile fields in factory, so it's threadsafe */
+
+  size = 128;
+  if (factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE)
+    size = 256;
+
+  pixbuf = NULL;
+
+  script = NULL;
+  if (factory->priv->scripts_hash != NULL)
+    script = g_hash_table_lookup (factory->priv->scripts_hash, mime_type);
+
+  if (script)
+    {
+      int fd;
+      GError *error = NULL;
+
+      fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", &tmpname, &error);
+
+      if (fd != -1)
+	{
+	  close (fd);
+
+	  expanded_script = expand_thumbnailing_script (script, size, uri, tmpname);
+	  if (expanded_script != NULL &&
+	      g_spawn_command_line_sync (expanded_script,
+					 NULL, NULL, &exit_status, NULL) &&
+	      exit_status == 0)
+	    {
+	      pixbuf = gdk_pixbuf_new_from_file (tmpname, NULL);
+	    }
+
+	  g_free (expanded_script);
+	  g_unlink(tmpname);
+	}
+      g_free (tmpname);
+    }
+
+  /* Fall back to gdk-pixbuf */
+  if (pixbuf == NULL)
+    {
+      pixbuf = _gdk_pixbuf_new_from_uri_at_scale (uri, size, size, TRUE);
+
+      if (pixbuf != NULL)
+        {
+          original_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf),
+                                                               "gnome-original-width"));
+          original_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pixbuf),
+                                                                "gnome-original-height"));
+        }
+    }
+
+  if (pixbuf == NULL)
+    return NULL;
+
+  /* The pixbuf loader may attach an "orientation" option to the pixbuf,
+     if the tiff or exif jpeg file had an orientation tag. Rotate/flip
+     the pixbuf as specified by this tag, if present. */
+  tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+  g_object_unref (pixbuf);
+  pixbuf = tmp_pixbuf;
+
+  width = gdk_pixbuf_get_width (pixbuf);
+  height = gdk_pixbuf_get_height (pixbuf);
+
+  if (width > size || height > size)
+    {
+      const gchar *orig_width, *orig_height;
+      scale = (double)size / MAX (width, height);
+
+      scaled = gnome_desktop_thumbnail_scale_down_pixbuf (pixbuf,
+						  floor (width * scale + 0.5),
+						  floor (height * scale + 0.5));
+
+      orig_width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width");
+      orig_height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height");
+
+      if (orig_width != NULL) {
+	      gdk_pixbuf_set_option (scaled, "tEXt::Thumb::Image::Width", orig_width);
+      }
+      if (orig_height != NULL) {
+	      gdk_pixbuf_set_option (scaled, "tEXt::Thumb::Image::Height", orig_height);
+      }
+
+      g_object_unref (pixbuf);
+      pixbuf = scaled;
+    }
+
+  if (original_width > 0) {
+	  g_snprintf (dimension, sizeof (dimension), "%i", original_width);
+	  gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Width", dimension);
+  }
+  if (original_height > 0) {
+	  g_snprintf (dimension, sizeof (dimension), "%i", original_height);
+	  gdk_pixbuf_set_option (pixbuf, "tEXt::Thumb::Image::Height", dimension);
+  }
+
+  return pixbuf;
+}
+
+
+gboolean
+gnome_desktop_thumbnail_factory_generate_thumbnail_async (GnomeDesktopThumbnailFactory  *factory,
+							  const char                    *uri,
+							  const char                    *mime_type,
+							  GPid                          *pid,
+							  char                         **tmpname,
+							  GError                       **error)
+{
+	gboolean   retval = FALSE;
+	int        size;
+	char      *script;
+	int        fd;
+	char      *expanded_script;
+	int        argc;
+	char     **argv;
+
+	g_return_val_if_fail (uri != NULL, FALSE);
+	g_return_val_if_fail (mime_type != NULL, FALSE);
+
+	size = 128;
+	if (factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE)
+		size = 256;
+
+	script = NULL;
+	if (factory->priv->scripts_hash != NULL)
+		script = g_hash_table_lookup (factory->priv->scripts_hash, mime_type);
+
+	if (script == NULL)
+		return FALSE;
+
+	fd = g_file_open_tmp (".gnome_desktop_thumbnail.XXXXXX", tmpname, error);
+	if (fd == -1)
+		return FALSE;
+	close (fd);
+
+	expanded_script = expand_thumbnailing_script (script, size, uri, *tmpname);
+	if (g_shell_parse_argv (expanded_script, &argc, &argv, error))
+		if (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, pid, error))
+			retval = TRUE;
+
+	g_free (expanded_script);
+
+	return retval;
+}
+
+
+GdkPixbuf *
+gnome_desktop_thumbnail_factory_load_from_tempfile (GnomeDesktopThumbnailFactory  *factory,
+						    char                         **tmpname)
+{
+	GdkPixbuf *pixbuf;
+	GdkPixbuf *tmp_pixbuf;
+
+	pixbuf = gdk_pixbuf_new_from_file (*tmpname, NULL);
+	g_unlink (*tmpname);
+	g_free (*tmpname);
+	*tmpname = NULL;
+
+	if (pixbuf == NULL)
+		return NULL;
+
+	tmp_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+	g_object_unref (pixbuf);
+	pixbuf = tmp_pixbuf;
+
+	return pixbuf;
+}
+
+
+static gboolean
+make_thumbnail_dirs (GnomeDesktopThumbnailFactory *factory)
+{
+  char *thumbnail_dir;
+  char *image_dir;
+  gboolean res;
+
+  res = FALSE;
+
+  thumbnail_dir = g_build_filename (g_get_home_dir (),
+				    ".thumbnails",
+				    NULL);
+  if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR))
+    {
+      g_mkdir (thumbnail_dir, 0700);
+      res = TRUE;
+    }
+
+  image_dir = g_build_filename (thumbnail_dir,
+				(factory->priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+				NULL);
+  if (!g_file_test (image_dir, G_FILE_TEST_IS_DIR))
+    {
+      g_mkdir (image_dir, 0700);
+      res = TRUE;
+    }
+
+  g_free (thumbnail_dir);
+  g_free (image_dir);
+
+  return res;
+}
+
+static gboolean
+make_thumbnail_fail_dirs (GnomeDesktopThumbnailFactory *factory)
+{
+  char *thumbnail_dir;
+  char *fail_dir;
+  char *app_dir;
+  gboolean res;
+
+  res = FALSE;
+
+  thumbnail_dir = g_build_filename (g_get_home_dir (),
+				    ".thumbnails",
+				    NULL);
+  if (!g_file_test (thumbnail_dir, G_FILE_TEST_IS_DIR))
+    {
+      g_mkdir (thumbnail_dir, 0700);
+      res = TRUE;
+    }
+
+  fail_dir = g_build_filename (thumbnail_dir,
+			       "fail",
+			       NULL);
+  if (!g_file_test (fail_dir, G_FILE_TEST_IS_DIR))
+    {
+      g_mkdir (fail_dir, 0700);
+      res = TRUE;
+    }
+
+  app_dir = g_build_filename (fail_dir,
+			      factory->priv->application,
+			      NULL);
+  if (!g_file_test (app_dir, G_FILE_TEST_IS_DIR))
+    {
+      g_mkdir (app_dir, 0700);
+      res = TRUE;
+    }
+
+  g_free (thumbnail_dir);
+  g_free (fail_dir);
+  g_free (app_dir);
+
+  return res;
+}
+
+
+/**
+ * gnome_desktop_thumbnail_factory_save_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @thumbnail: the thumbnail as a pixbuf
+ * @uri: the uri of a file
+ * @original_mtime: the modification time of the original file
+ *
+ * Saves @thumbnail at the right place. If the save fails a
+ * failed thumbnail is written.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Since: 2.2
+ **/
+void
+gnome_desktop_thumbnail_factory_save_thumbnail (GnomeDesktopThumbnailFactory *factory,
+						GdkPixbuf             *thumbnail,
+						const char            *uri,
+						time_t                 original_mtime)
+{
+  GnomeDesktopThumbnailFactoryPrivate *priv = factory->priv;
+  char *path, *file, *dir;
+  char *tmp_path;
+  const char *width, *height;
+  int tmp_fd;
+  char mtime_str[21];
+  gboolean saved_ok;
+  GChecksum *checksum;
+  guint8 digest[16];
+  gsize digest_len = sizeof (digest);
+
+  checksum = g_checksum_new (G_CHECKSUM_MD5);
+  g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+  g_checksum_get_digest (checksum, digest, &digest_len);
+  g_assert (digest_len == 16);
+
+  file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+  dir = g_build_filename (g_get_home_dir (),
+			  ".thumbnails",
+			  (priv->size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+			  NULL);
+
+  path = g_build_filename (dir,
+			   file,
+			   NULL);
+  g_free (file);
+
+  g_checksum_free (checksum);
+
+  tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+
+  tmp_fd = g_mkstemp (tmp_path);
+  if (tmp_fd == -1 &&
+      make_thumbnail_dirs (factory))
+    {
+      g_free (tmp_path);
+      tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+      tmp_fd = g_mkstemp (tmp_path);
+    }
+
+  if (tmp_fd == -1)
+    {
+      gnome_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, original_mtime);
+      g_free (dir);
+      g_free (tmp_path);
+      g_free (path);
+      return;
+    }
+  close (tmp_fd);
+
+  g_snprintf (mtime_str, 21, "%ld",  original_mtime);
+  width = gdk_pixbuf_get_option (thumbnail, "tEXt::Thumb::Image::Width");
+  height = gdk_pixbuf_get_option (thumbnail, "tEXt::Thumb::Image::Height");
+
+  if (width != NULL && height != NULL)
+    saved_ok  = gdk_pixbuf_save (thumbnail,
+				 tmp_path,
+				 "png", NULL,
+				 "tEXt::Thumb::Image::Width", width,
+				 "tEXt::Thumb::Image::Height", height,
+				 "tEXt::Thumb::URI", uri,
+				 "tEXt::Thumb::MTime", mtime_str,
+				 "tEXt::Software", "GNOME::ThumbnailFactory",
+				 NULL);
+  else
+    saved_ok  = gdk_pixbuf_save (thumbnail,
+				 tmp_path,
+				 "png", NULL,
+				 "tEXt::Thumb::URI", uri,
+				 "tEXt::Thumb::MTime", mtime_str,
+				 "tEXt::Software", "GNOME::ThumbnailFactory",
+				 NULL);
+
+
+  if (saved_ok)
+    {
+      g_chmod (tmp_path, 0600);
+      g_rename(tmp_path, path);
+    }
+  else
+    {
+      gnome_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, original_mtime);
+    }
+
+  g_free (dir);
+  g_free (path);
+  g_free (tmp_path);
+}
+
+/**
+ * gnome_desktop_thumbnail_factory_create_failed_thumbnail:
+ * @factory: a #GnomeDesktopThumbnailFactory
+ * @uri: the uri of a file
+ * @mtime: the modification time of the file
+ *
+ * Creates a failed thumbnail for the file so that we don't try
+ * to re-thumbnail the file later.
+ *
+ * Usage of this function is threadsafe.
+ *
+ * Since: 2.2
+ **/
+void
+gnome_desktop_thumbnail_factory_create_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+							 const char            *uri,
+							 time_t                 mtime)
+{
+  char *path, *file, *dir;
+  char *tmp_path;
+  int tmp_fd;
+  char mtime_str[21];
+  gboolean saved_ok;
+  GdkPixbuf *pixbuf;
+  GChecksum *checksum;
+  guint8 digest[16];
+  gsize digest_len = sizeof (digest);
+
+  checksum = g_checksum_new (G_CHECKSUM_MD5);
+  g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
+
+  g_checksum_get_digest (checksum, digest, &digest_len);
+  g_assert (digest_len == 16);
+
+  file = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
+
+  dir = g_build_filename (g_get_home_dir (),
+			  ".thumbnails/fail",
+			  factory->priv->application,
+			  NULL);
+
+  path = g_build_filename (dir,
+			   file,
+			   NULL);
+  g_free (file);
+
+  g_checksum_free (checksum);
+
+  tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+
+  tmp_fd = g_mkstemp (tmp_path);
+  if (tmp_fd == -1 &&
+      make_thumbnail_fail_dirs (factory))
+    {
+      g_free (tmp_path);
+      tmp_path = g_strconcat (path, ".XXXXXX", NULL);
+      tmp_fd = g_mkstemp (tmp_path);
+    }
+
+  if (tmp_fd == -1)
+    {
+      g_free (dir);
+      g_free (tmp_path);
+      g_free (path);
+      return;
+    }
+  close (tmp_fd);
+
+  g_snprintf (mtime_str, 21, "%ld",  mtime);
+  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
+  saved_ok  = gdk_pixbuf_save (pixbuf,
+			       tmp_path,
+			       "png", NULL,
+			       "tEXt::Thumb::URI", uri,
+			       "tEXt::Thumb::MTime", mtime_str,
+			       "tEXt::Software", "GNOME::ThumbnailFactory",
+			       NULL);
+  g_object_unref (pixbuf);
+  if (saved_ok)
+    {
+      g_chmod (tmp_path, 0600);
+      g_rename(tmp_path, path);
+    }
+
+  g_free (dir);
+  g_free (path);
+  g_free (tmp_path);
+}
+
+/**
+ * gnome_desktop_thumbnail_md5:
+ * @uri: an uri
+ *
+ * Calculates the MD5 checksum of the uri. This can be useful
+ * if you want to manually handle thumbnail files.
+ *
+ * Return value: A string with the MD5 digest of the uri string.
+ *
+ * Since: 2.2
+ *
+ * @Deprecated: 2.22: Use #GChecksum instead
+ **/
+char *
+gnome_desktop_thumbnail_md5 (const char *uri)
+{
+  return g_compute_checksum_for_data (G_CHECKSUM_MD5,
+                                      (const guchar *) uri,
+                                      strlen (uri));
+}
+
+/**
+ * gnome_desktop_thumbnail_path_for_uri:
+ * @uri: an uri
+ * @size: a thumbnail size
+ *
+ * Returns the filename that a thumbnail of size @size for @uri would have.
+ *
+ * Return value: an absolute filename
+ *
+ * Since: 2.2
+ **/
+char *
+gnome_desktop_thumbnail_path_for_uri (const char         *uri,
+				      GnomeDesktopThumbnailSize  size)
+{
+  char *md5;
+  char *file;
+  char *path;
+
+  md5 = gnome_desktop_thumbnail_md5 (uri);
+  file = g_strconcat (md5, ".png", NULL);
+  g_free (md5);
+
+  path = g_build_filename (g_get_home_dir (),
+			   ".thumbnails",
+			   (size == GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL)?"normal":"large",
+			   file,
+			   NULL);
+
+  g_free (file);
+
+  return path;
+}
+
+/**
+ * gnome_desktop_thumbnail_has_uri:
+ * @pixbuf: an loaded thumbnail pixbuf
+ * @uri: a uri
+ *
+ * Returns whether the thumbnail has the correct uri embedded in the
+ * Thumb::URI option in the png.
+ *
+ * Return value: TRUE if the thumbnail is for @uri
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_has_uri (GdkPixbuf          *pixbuf,
+				 const char         *uri)
+{
+  const char *thumb_uri;
+
+  thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI");
+  if (!thumb_uri)
+    return FALSE;
+
+  return strcmp (uri, thumb_uri) == 0;
+}
+
+/**
+ * gnome_desktop_thumbnail_is_valid:
+ * @pixbuf: an loaded thumbnail #GdkPixbuf
+ * @uri: a uri
+ * @mtime: the mtime
+ *
+ * Returns whether the thumbnail has the correct uri and mtime embedded in the
+ * png options.
+ *
+ * Return value: TRUE if the thumbnail has the right @uri and @mtime
+ *
+ * Since: 2.2
+ **/
+gboolean
+gnome_desktop_thumbnail_is_valid (GdkPixbuf          *pixbuf,
+				  const char         *uri,
+				  time_t              mtime)
+{
+  const char *thumb_uri, *thumb_mtime_str;
+  time_t thumb_mtime;
+
+  thumb_uri = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::URI");
+  if (!thumb_uri)
+    return FALSE;
+  if (strcmp (uri, thumb_uri) != 0)
+    return FALSE;
+
+  thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime");
+  if (!thumb_mtime_str)
+    return FALSE;
+  thumb_mtime = atol (thumb_mtime_str);
+  if (mtime != thumb_mtime)
+    return FALSE;
+
+  return TRUE;
+}
diff --git a/src/gnome-desktop-thumbnail.h b/src/gnome-desktop-thumbnail.h
new file mode 100644
index 0000000..dfdc1af
--- /dev/null
+++ b/src/gnome-desktop-thumbnail.h
@@ -0,0 +1,114 @@
+/*
+ * gnome-thumbnail.h: Utilities for handling thumbnails
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#ifndef GNOME_DESKTOP_THUMBNAIL_H
+#define GNOME_DESKTOP_THUMBNAIL_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <time.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL,
+  GNOME_DESKTOP_THUMBNAIL_SIZE_LARGE
+} GnomeDesktopThumbnailSize;
+
+#define GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY		(gnome_desktop_thumbnail_factory_get_type ())
+#define GNOME_DESKTOP_THUMBNAIL_FACTORY(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, GnomeDesktopThumbnailFactory))
+#define GNOME_DESKTOP_THUMBNAIL_FACTORY_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY, GnomeDesktopThumbnailFactoryClass))
+#define GNOME_DESKTOP_IS_THUMBNAIL_FACTORY(obj)		(G_TYPE_INSTANCE_CHECK_TYPE ((obj), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY))
+#define GNOME_DESKTOP_IS_THUMBNAIL_FACTORY_CLASS(klass)	(G_TYPE_CLASS_CHECK_CLASS_TYPE ((klass), GNOME_DESKTOP_TYPE_THUMBNAIL_FACTORY))
+
+typedef struct _GnomeDesktopThumbnailFactory        GnomeDesktopThumbnailFactory;
+typedef struct _GnomeDesktopThumbnailFactoryClass   GnomeDesktopThumbnailFactoryClass;
+typedef struct _GnomeDesktopThumbnailFactoryPrivate GnomeDesktopThumbnailFactoryPrivate;
+
+struct _GnomeDesktopThumbnailFactory {
+	GObject parent;
+
+	GnomeDesktopThumbnailFactoryPrivate *priv;
+};
+
+struct _GnomeDesktopThumbnailFactoryClass {
+	GObjectClass parent;
+};
+
+GType                  gnome_desktop_thumbnail_factory_get_type (void);
+GnomeDesktopThumbnailFactory *gnome_desktop_thumbnail_factory_new      (GnomeDesktopThumbnailSize     size);
+
+char *                 gnome_desktop_thumbnail_factory_lookup   (GnomeDesktopThumbnailFactory *factory,
+								 const char            *uri,
+								 time_t                 mtime);
+
+gboolean               gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+										   const char            *uri,
+										   time_t                 mtime);
+gboolean               gnome_desktop_thumbnail_factory_can_thumbnail (GnomeDesktopThumbnailFactory *factory,
+								      const char            *uri,
+								      const char            *mime_type,
+								      time_t                 mtime);
+GdkPixbuf *            gnome_desktop_thumbnail_factory_generate_thumbnail (GnomeDesktopThumbnailFactory *factory,
+									   const char            *uri,
+									   const char            *mime_type);
+gboolean               gnome_desktop_thumbnail_factory_generate_thumbnail_async (GnomeDesktopThumbnailFactory *factory,
+										 const char            *uri,
+										 const char            *mime_type,
+										 GPid                  *pid,
+										 char                 **tmpname,
+										 GError               **error);
+GdkPixbuf *            gnome_desktop_thumbnail_factory_load_from_tempfile (GnomeDesktopThumbnailFactory *factory,
+									   char **tmpname);
+void                   gnome_desktop_thumbnail_factory_save_thumbnail (GnomeDesktopThumbnailFactory *factory,
+								       GdkPixbuf             *thumbnail,
+								       const char            *uri,
+								       time_t                 original_mtime);
+void                   gnome_desktop_thumbnail_factory_create_failed_thumbnail (GnomeDesktopThumbnailFactory *factory,
+										const char            *uri,
+										time_t                 mtime);
+
+
+/* Thumbnailing utils: */
+gboolean   gnome_desktop_thumbnail_has_uri           (GdkPixbuf          *pixbuf,
+						      const char         *uri);
+gboolean   gnome_desktop_thumbnail_is_valid          (GdkPixbuf          *pixbuf,
+						      const char         *uri,
+						      time_t              mtime);
+char *     gnome_desktop_thumbnail_md5               (const char         *uri);
+char *     gnome_desktop_thumbnail_path_for_uri      (const char         *uri,
+						      GnomeDesktopThumbnailSize  size);
+
+
+/* Pixbuf utils */
+
+GdkPixbuf *gnome_desktop_thumbnail_scale_down_pixbuf (GdkPixbuf          *pixbuf,
+						      int                 dest_width,
+						      int                 dest_height);
+
+G_END_DECLS
+
+#endif /* GNOME_DESKTOP_THUMBNAIL_H */
diff --git a/src/gnome-thumbnail-pixbuf-utils.c b/src/gnome-thumbnail-pixbuf-utils.c
new file mode 100644
index 0000000..833242a
--- /dev/null
+++ b/src/gnome-thumbnail-pixbuf-utils.c
@@ -0,0 +1,172 @@
+/*
+ * gnome-thumbnail-pixbuf-utils.c: Utilities for handling pixbufs when thumbnailing
+ *
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * 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.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-desktop-thumbnail.h"
+
+#define LOAD_BUFFER_SIZE 65536
+
+/**
+ * gnome_thumbnail_scale_down_pixbuf:
+ * @pixbuf: a #GdkPixbuf
+ * @dest_width: the desired new width
+ * @dest_height: the desired new height
+ *
+ * Scales the pixbuf to the desired size. This function
+ * is a lot faster than gdk-pixbuf when scaling down by
+ * large amounts.
+ *
+ * Return value: a scaled pixbuf
+ * 
+ * Since: 2.2
+ **/
+GdkPixbuf *
+gnome_desktop_thumbnail_scale_down_pixbuf (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;
+}
diff --git a/src/goo-marshal.list b/src/goo-marshal.list
index dd0cd20..d433cc9 100644
--- a/src/goo-marshal.list
+++ b/src/goo-marshal.list
@@ -3,4 +3,5 @@ VOID:INT,POINTER
 VOID:VOID
 VOID:DOUBLE
 VOID:STRING
+VOID:STRING,STRING
 VOID:BOOL
diff --git a/src/goo-player-info.c b/src/goo-player-info.c
index f75369c..a7bc6dc 100644
--- a/src/goo-player-info.c
+++ b/src/goo-player-info.c
@@ -22,13 +22,12 @@
 
 #include <config.h>
 #include <string.h>
-#include <gnome.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
+#include <glib/gi18n.h>
 #include "goo-player-info.h"
 #include "goo-marshal.h"
-#include "glib-utils.h"
 #include "goo-stock.h"
-#include "file-utils.h"
+#include "goo-window.h"
+#include "glib-utils.h"
 
 #define SPACING 0
 #define TITLE1_FORMAT "<span size='large' weight='bold'>%s</span>"
@@ -60,8 +59,8 @@ struct _GooPlayerInfoPrivateData {
 	GtkWidget   *status_image;
 	GtkWidget   *notebook;
 	GtkTooltips *tips;
-	char         current_time[64];
-	char         total_time[64];
+	char        *current_time;
+	char        *total_time;
 	char         time[64];
 	gint64       track_length;
 	gboolean     dragging;
@@ -216,7 +215,8 @@ time_scale_value_changed_cb (GtkRange      *range,
 	
 	new_value = gtk_range_get_value (range);
 	current_time = info->priv->track_length * (new_value / 100.0);
-	set_time_string (info->priv->current_time, current_time);
+	g_free (info->priv->current_time);
+	info->priv->current_time = _g_format_duration_for_display (current_time * 1000);
 	sprintf (info->priv->time, _("%s / %s"), info->priv->total_time, info->priv->current_time);
 	set_time (info, info->priv->time);
 
@@ -243,7 +243,8 @@ update_time_label_cb (gpointer data)
 	}
 	
 	current_time = priv->track_length * new_value;
-	set_time_string (priv->current_time, current_time);
+	g_free (info->priv->current_time);
+	info->priv->current_time = _g_format_duration_for_display (current_time * 1000);
 	sprintf (priv->time, _("%s / %s"), priv->current_time, priv->total_time);
 	set_time (info, priv->time);
 
@@ -294,82 +295,6 @@ cover_button_clicked_cb (GtkWidget     *button,
 /* -- drag and drop -- */
 
 
-static char *
-get_path_from_url (char *url)
-{
-	GnomeVFSURI *uri;
-	char        *escaped;
-	char        *path;
-
-	if (url == NULL)
-		return NULL;
-
-	uri = gnome_vfs_uri_new (url);
-	escaped = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD);
-	path = gnome_vfs_unescape_string (escaped, NULL);
-
-	gnome_vfs_uri_unref (uri);
-	g_free (escaped);
-
-	return path;
-}
-
-
-static GList *
-get_file_list_from_dnd_data (gchar *url_list)
-{
-	GList    *list = NULL;
-	int       i;
-	char     *url_start, *url_end;
-	gboolean  dnd_valid = FALSE;
-
-	i = 0;
-	url_start = url_list;
-	while (url_list[i] != '\0')	{
-		char *url;
-
-		while ((url_list[i] != '\0')
-		       && (url_list[i] != '\r')
-		       && (url_list[i] != '\n')) i++;
-
-		url_end = url_list + i;
-		if (strncmp (url_start, "file:", 5) == 0) {
-			url_start += 5;
-			if ((url_start[0] == '/') 
-			    && (url_start[1] == '/')) url_start += 2;
-
-			dnd_valid = TRUE;
-
-			url = g_strndup (url_start, url_end - url_start);
-			list = g_list_prepend (list, get_path_from_url (url));
-			g_free (url);
-
-			while ((url_list[i] != '\0')
-			       && ((url_list[i] == '\r') 
-				   || (url_list[i] == '\n'))) i++;
-			url_start = url_list + i;
-
-		} else {
-			while ((url_list[i] != '\0')
-			       && ((url_list[i] == '\r') 
-				   || (url_list[i] == '\n'))) i++;
-			url_start = url_list + i;
-			if (url_list[i] == '\0' && !dnd_valid) {
-				path_list_free (list);
-				return NULL;
-			}
-		}
-	}
-	
-	if (!dnd_valid) {
-		path_list_free (list);
-		return NULL;
-	}
-
-	return g_list_reverse (list);
-}
-
-
 void  
 cover_button_drag_data_received  (GtkWidget          *widget,
 				  GdkDragContext     *context,
@@ -380,9 +305,8 @@ cover_button_drag_data_received  (GtkWidget          *widget,
 				  guint               time,
 				  gpointer            extra_data)
 {
-	GooPlayerInfo *info = extra_data;
-	GList         *list;
-	char          *cover_filename;
+	GooPlayerInfo  *info = extra_data;
+	char          **uris;
 
 	if (! ((data->length >= 0) && (data->format == 8))) {
 		gtk_drag_finish (context, FALSE, FALSE, time);
@@ -391,18 +315,20 @@ cover_button_drag_data_received  (GtkWidget          *widget,
 
 	gtk_drag_finish (context, TRUE, FALSE, time);
 
-	list = get_file_list_from_dnd_data ((char *)data->data);
-	if (list == NULL)
-		return;
+	uris = gtk_selection_data_get_uris (data);
+	if (uris[0] != NULL) {
+		GFile *file;
+		char  *cover_filename;
 
-	cover_filename = list->data;
-	if (cover_filename == NULL)
-		return;
+		file = g_file_new_for_uri (uris[0]);
+		cover_filename = g_file_get_path (file);
+		goo_window_set_cover_image (info->priv->window, cover_filename);
 
-	goo_window_set_cover_image (info->priv->window, cover_filename);
+		g_free (cover_filename);
+		g_object_unref (file);
+	}
 
-	if (list != NULL) 
-		path_list_free (list);
+	g_strfreev (uris);
 }
 
 
@@ -413,6 +339,9 @@ goo_player_info_init (GooPlayerInfo *info)
 }
 
 
+static void goo_player_info_update_state (GooPlayerInfo *info);
+
+
 static void 
 goo_player_info_construct (GooPlayerInfo *info)
 {
@@ -422,8 +351,8 @@ goo_player_info_construct (GooPlayerInfo *info)
 	priv = info->priv;
 
 	priv->dragging = FALSE;
-	priv->current_time[0] = '\0';
-	priv->total_time[0] = '\0';
+	priv->current_time = NULL;
+	priv->total_time = NULL;
 	priv->update_id = 0;
 
 	GTK_WIDGET_UNSET_FLAGS (info, GTK_CAN_FOCUS);
@@ -578,6 +507,8 @@ goo_player_info_construct (GooPlayerInfo *info)
 			  "button_release_event",
 			  G_CALLBACK (time_scale_button_release_cb), 
 			  info);
+
+	goo_player_info_update_state (info);
 }
 
 
@@ -591,6 +522,8 @@ goo_player_info_finalize (GObject *object)
   
 	info = GOO_PLAYER_INFO (object);
 	if (info->priv != NULL) {
+		g_free (info->priv->current_time);
+		g_free (info->priv->total_time);
 		if (info->priv->update_id != 0) {
 			g_source_remove (info->priv->update_id);
 			info->priv->update_id = 0;
@@ -612,7 +545,8 @@ goo_player_info_set_time (GooPlayerInfo  *info,
 	if (priv->dragging) 
 		return;
 
-	set_time_string (priv->current_time, current_time);
+	g_free (priv->current_time);
+	priv->current_time = _g_format_duration_for_display (current_time * 1000);
 	sprintf (priv->time, _("%s / %s"), priv->current_time, priv->total_time);
 	set_time (info, priv->time);
 
@@ -662,7 +596,8 @@ update_subtitle (GooPlayerInfo *info,
 	album = goo_window_get_album (info->priv->window);
 	
 	if ((album->title == NULL) || (album->artist == NULL)) {
-		set_time_string (info->priv->total_time, track->length);
+		g_free (info->priv->total_time);
+		info->priv->total_time = _g_format_duration_for_display (track->length * 1000);
 		set_title2 (info, info->priv->total_time);
 	} 
 	else {
@@ -675,7 +610,7 @@ update_subtitle (GooPlayerInfo *info,
 
 
 static void
-goo_player_info_update_state (GooPlayerInfo  *info)
+goo_player_info_update_state (GooPlayerInfo *info)
 {
 	GooPlayerInfoPrivateData *priv = info->priv;
 	GooPlayerState  state;
@@ -708,16 +643,17 @@ goo_player_info_update_state (GooPlayerInfo  *info)
 	gtk_label_set_selectable (GTK_LABEL (priv->title2_label), FALSE);
 	gtk_label_set_selectable (GTK_LABEL (priv->title3_label), FALSE);
 
-	if ((state == GOO_PLAYER_STATE_ERROR)
-	    || (state == GOO_PLAYER_STATE_NO_DISC)
-	    || (state == GOO_PLAYER_STATE_DATA_DISC)) {
-		GError *error = goo_player_get_error (player);
-		
-		set_title1 (info, error->message);
+	if ((state == GOO_PLAYER_STATE_ERROR) || (state == GOO_PLAYER_STATE_NO_DISC))
+	{
+		set_title1 (info, _("No disc"));
 		set_title2 (info, "");
 		set_title3 (info, "");
-		g_error_free (error);
 	} 
+	else if (state == GOO_PLAYER_STATE_DATA_DISC) {
+		set_title1 (info, _("Data disc"));
+		set_title2 (info, "");
+		set_title3 (info, "");
+	}
 	else {
 		TrackInfo *track;
 		
@@ -816,7 +752,8 @@ goo_player_info_set_total_time (GooPlayerInfo  *info,
 {
 	GooPlayerInfoPrivateData *priv = info->priv;
 
-	set_time_string (priv->total_time, total_time);
+	g_free (priv->total_time);
+	priv->total_time = _g_format_duration_for_display (total_time * 1000);
 	goo_player_info_update_state (info);
 }
 
diff --git a/src/goo-player-info.h b/src/goo-player-info.h
index 5983bfd..30ef316 100644
--- a/src/goo-player-info.h
+++ b/src/goo-player-info.h
@@ -23,8 +23,7 @@
 #ifndef GOO_PLAYER_INFO_H
 #define GOO_PLAYER_INFO_H
 
-#include <glib-object.h>
-#include <gtk/gtkhbox.h>
+#include <gtk/gtk.h>
 #include "goo-window.h"
 
 #define GOO_TYPE_PLAYER_INFO              (goo_player_info_get_type ())
diff --git a/src/goo-player.c b/src/goo-player.c
index 3ec269c..130ed7d 100644
--- a/src/goo-player.c
+++ b/src/goo-player.c
@@ -23,18 +23,14 @@
 #include <config.h>
 #include <math.h>
 #include <string.h>
-#include <gnome.h>
+#include <glib/gi18n.h>
 #include <gst/gst.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
 #include <musicbrainz/queries.h>
 #include <musicbrainz/mb_c.h>
-
 #include "goo-player.h"
 #include "goo-marshal.h"
-#include "goo-cdrom.h"
 #include "glib-utils.h"
-#include "file-utils.h"
+#include "gth-user-dir.h"
 #include "main.h"
 #include "metadata.h"
 
@@ -45,19 +41,21 @@
 #define PROGRESS_DELAY 400
 #define QUEUE_SIZE 16384U /*131072U*/
 
-struct _GooPlayerPrivateData {
+struct _GooPlayerPrivate {
+	BraseroDrive    *drive;
+	gulong           medium_added_event;
+	gulong           medium_removed_event;
+
 	GooPlayerState   state;
 	GooPlayerAction  action;
-	GError          *error;
 	double           volume_value;
 	gboolean         is_busy;
+	gboolean         audio_cd;
 	
-	CDDrive         *drive;
-	GooCdrom        *cdrom;
 	GstElement      *pipeline;
 	GstElement      *source;
 	GstPad          *source_pad;
-	GstElement      *volume;
+	GstElement      *audio_volume;
 	GstFormat        track_format;
 	GstFormat        sector_format;
 	
@@ -91,60 +89,15 @@ static guint goo_player_signals[LAST_SIGNAL] = { 0 };
 static void goo_player_finalize    (GObject *object);
 
 #define GOO_PLAYER_GET_PRIVATE_DATA(object) \
-	(G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_PLAYER, GooPlayerPrivateData))
+	(G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_PLAYER, GooPlayerPrivate))
 
 G_DEFINE_TYPE (GooPlayer, goo_player, G_TYPE_OBJECT)
 
 
 static void
-remove_state_polling (GooPlayer *player)
-{
-	if (player->priv->update_state_id == 0) 
-		return;
-
-	debug (DEBUG_INFO, "REMOVE POLLING\n");
-
-	g_source_remove (player->priv->update_state_id);
-	player->priv->update_state_id = 0;
-}
-
-
-static gboolean
-update_state_cb (gpointer data)
-{
-	GooPlayer *player = data;
-
-	g_source_remove (player->priv->update_state_id);
-	goo_cdrom_update_state (player->priv->cdrom);
-	player->priv->update_state_id = g_timeout_add (POLL_TIMEOUT,
-					               update_state_cb,
-						       player);
-						       
-	return FALSE;
-}
-
-
-static void
-add_state_polling (GooPlayer *player)
-{
-	if (player->priv->update_state_id != 0)
-		return;
-
-	debug (DEBUG_INFO, "ADD POLLING\n");
-
-	player->priv->update_state_id = g_timeout_add (POLL_TIMEOUT,
-						       update_state_cb,
-						       player);
-}
-
-
-static void
 destroy_pipeline (GooPlayer *player,
-		  gboolean     poll)
+		  gboolean   poll)
 {
-	if (player->priv->cdrom != NULL)
-		goo_cdrom_unlock_tray (player->priv->cdrom);
-
 	if (player->priv->pipeline != NULL) {
 		gst_element_set_state (player->priv->pipeline, GST_STATE_NULL);
 		gst_object_unref (GST_OBJECT (player->priv->pipeline));
@@ -158,28 +111,22 @@ destroy_pipeline (GooPlayer *player,
 
 	player->priv->source = NULL;
 	player->priv->source_pad = NULL;
-	player->priv->volume = NULL;
-
-	if (poll)
-		add_state_polling (player);
+	player->priv->audio_volume = NULL;
 }
 
 
 static void
-action_done (GooPlayer       *player, 
+action_done (GooPlayer       *self,
 	     GooPlayerAction  action)
 {
-	g_signal_emit_by_name (G_OBJECT (player), "done", action, NULL);
+	g_signal_emit_by_name (G_OBJECT (self), "done", action, NULL);
 }
 
 
 static gboolean
-player_done_cb (gpointer callback_data)
+player_done_cb (gpointer user_data)
 {
-	GooPlayer *player = callback_data;
-	
-	action_done (player, GOO_PLAYER_ACTION_PLAY);
-	
+	action_done ((GooPlayer *) user_data, GOO_PLAYER_ACTION_PLAY);
 	return FALSE;
 }
 
@@ -187,17 +134,17 @@ player_done_cb (gpointer callback_data)
 static gboolean
 pipeline_bus_message_cb (GstBus     *bus,
 		         GstMessage *message,
-		         gpointer    data)
+		         gpointer    user_data)
 {
-	GooPlayer *player = (GooPlayer *) data;
+	GooPlayer *self = user_data;
 
 	switch (GST_MESSAGE_TYPE (message)) {
 	case GST_MESSAGE_EOS:
-		if (player->priv->update_progress_id != 0) {
-			g_source_remove (player->priv->update_progress_id);
-			player->priv->update_progress_id = 0;
+		if (self->priv->update_progress_id != 0) {
+			g_source_remove (self->priv->update_progress_id);
+			self->priv->update_progress_id = 0;
 		}
-		g_idle_add (player_done_cb, data);	
+		g_idle_add (player_done_cb, user_data);
 		break;
 		
 	default:
@@ -209,58 +156,60 @@ pipeline_bus_message_cb (GstBus     *bus,
 
 
 static gboolean
-update_progress_cb (gpointer callback_data)
+update_progress_cb (gpointer user_data)
 {
-	GooPlayer *player = callback_data;
-	gboolean   ret;
+	GooPlayer *self = user_data;
 	gint64     sector = 0;
 
-	if (player->priv->update_progress_id != 0) {
-		g_source_remove (player->priv->update_progress_id);
-		player->priv->update_progress_id = 0;
+	if (self->priv->update_progress_id != 0) {
+		g_source_remove (self->priv->update_progress_id);
+		self->priv->update_progress_id = 0;
 	}
 	
-	ret = gst_pad_query_position (player->priv->source_pad, &(player->priv->sector_format), &sector);
-	if (!ret)
+	if (! gst_pad_query_position (self->priv->source_pad,
+				      &self->priv->sector_format,
+				      &sector))
+	{
 		return FALSE;
+	}
 
-	g_signal_emit_by_name (G_OBJECT (player), 
+	g_signal_emit_by_name (G_OBJECT (self),
 			       "progress", 
-			       ((double) sector) / (double) player->priv->current_track->sectors,
+			       ((double) sector) / (double) self->priv->current_track->sectors,
 			       NULL);
 
-	player->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, callback_data);
+	self->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, user_data);
 
 	return FALSE;
 }
 
 
 static void
-create_pipeline (GooPlayer *player)
+create_pipeline (GooPlayer *self)
 {
-	const char *device;
 	GstElement *audioconvert;
 	GstElement *audioresample;
 	GstElement *queue;
 	GstElement *sink;
 
-	destroy_pipeline (player, FALSE);
-	remove_state_polling (player);
-	goo_cdrom_lock_tray (player->priv->cdrom);
+	destroy_pipeline (self, FALSE);
 
-	player->priv->pipeline = gst_pipeline_new ("pipeline");
+	/*remove_state_polling (self);
+	 goo_cdrom_lock_tray (self->priv->cdrom); FIXME */
 
-	/*player->priv->source = gst_element_make_from_uri (GST_URI_SRC, "cdda://1", "source");*/
-	player->priv->source = gst_element_factory_make ("cdparanoiasrc", "source");
-	device = goo_cdrom_get_device (player->priv->cdrom);
-	debug (DEBUG_INFO, "DEVICE: %s\n", device);
-	g_object_set (G_OBJECT (player->priv->source), 
-		      "device", device, 
+	self->priv->pipeline = gst_pipeline_new ("pipeline");
+
+	/*self->priv->source = gst_element_make_from_uri (GST_URI_SRC, "cdda://1", "source");*/
+	self->priv->source = gst_element_factory_make ("cdparanoiasrc", "source");
+
+	debug (DEBUG_INFO, "DEVICE: %s\n", brasero_drive_get_device (self->priv->drive));
+	g_object_set (G_OBJECT (self->priv->source),
+		      "device", brasero_drive_get_device (self->priv->drive),
 		      NULL);
 
 	audioconvert = gst_element_factory_make ("audioconvert", "convert");
     	audioresample = gst_element_factory_make ("audioresample", "resample");
-	player->priv->volume = gst_element_factory_make ("volume", "volume");
+	self->priv->audio_volume = gst_element_factory_make ("volume", "volume");
 	
 	queue = gst_element_factory_make ("queue", "queue");
 	g_object_set (queue,
@@ -270,20 +219,29 @@ create_pipeline (GooPlayer *player)
 	
 	sink = gst_element_factory_make ("gconfaudiosink", "sink");
 	
-	gst_bin_add_many (GST_BIN (player->priv->pipeline), 
-			  player->priv->source, queue, audioconvert,
-			  audioresample, player->priv->volume, sink, NULL);
-	gst_element_link_many (player->priv->source, queue,
-			       audioconvert, audioresample, 
-			       player->priv->volume, sink, NULL);
-
-	player->priv->track_format = gst_format_get_by_nick ("track");
-	player->priv->sector_format = gst_format_get_by_nick ("sector");
-	player->priv->source_pad = gst_element_get_pad (player->priv->source, "src");
+	gst_bin_add_many (GST_BIN (self->priv->pipeline),
+			  self->priv->source,
+			  queue,
+			  audioconvert,
+			  audioresample,
+			  self->priv->audio_volume,
+			  sink,
+			  NULL);
+	gst_element_link_many (self->priv->source,
+			       queue,
+			       audioconvert,
+			       audioresample,
+			       self->priv->audio_volume,
+			       sink,
+			       NULL);
+
+	self->priv->track_format = gst_format_get_by_nick ("track");
+	self->priv->sector_format = gst_format_get_by_nick ("sector");
+	self->priv->source_pad = gst_element_get_pad (self->priv->source, "src");
 	
-	gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (player->priv->pipeline)),
+	gst_bus_add_watch (gst_pipeline_get_bus (GST_PIPELINE (self->priv->pipeline)),
 			   pipeline_bus_message_cb, 
-			   player);
+			   self);
 }
 
 
@@ -292,20 +250,25 @@ goo_player_empty_list (GooPlayer *player)
 {
 	album_info_unref (player->priv->album);
 	player->priv->album = album_info_new ();
-		
 	player->priv->current_track = NULL;
 	player->priv->current_track_n = -1;
 }
 
 
 static void
-goo_player_set_state (GooPlayer       *player,
+goo_player_set_state (GooPlayer       *self,
 		      GooPlayerState   state,
 		      gboolean         notify)
 {
-	player->priv->state = state;
+	/* FIXME
+	if (state == GOO_PLAYER_STATE_PLAYING)
+		brasero_drive_lock (self->priv->drive, _("Playing CD"), NULL);
+	else
+		brasero_drive_unlock (self->priv->drive);*/
+
+	self->priv->state = state;
 	if (notify)
-		g_signal_emit (G_OBJECT (player), 
+		g_signal_emit (G_OBJECT (self),
 			       goo_player_signals[STATE_CHANGED], 
 			       0,
 			       NULL);
@@ -315,7 +278,12 @@ goo_player_set_state (GooPlayer       *player,
 static void 
 goo_player_class_init (GooPlayerClass *class)
 {
-	GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
+	GObjectClass *gobject_class;
+
+	gobject_class = G_OBJECT_CLASS (class);
+	gobject_class->finalize = goo_player_finalize;
+
+	g_type_class_add_private (class, sizeof (GooPlayerPrivate));
 
 	goo_player_signals[START] =
                 g_signal_new ("start",
@@ -323,7 +291,7 @@ goo_player_class_init (GooPlayerClass *class)
 			      G_SIGNAL_RUN_LAST,
 			      G_STRUCT_OFFSET (GooPlayerClass, start),
 			      NULL, NULL,
-			      goo_marshal_VOID__INT,
+			      g_cclosure_marshal_VOID__INT,
 			      G_TYPE_NONE, 
 			      1,
 			      G_TYPE_INT);
@@ -343,7 +311,7 @@ goo_player_class_init (GooPlayerClass *class)
 			      G_SIGNAL_RUN_LAST,
 			      G_STRUCT_OFFSET (GooPlayerClass, progress),
 			      NULL, NULL,
-			      goo_marshal_VOID__DOUBLE,
+			      g_cclosure_marshal_VOID__DOUBLE,
 			      G_TYPE_NONE, 1,
 			      G_TYPE_DOUBLE);
 	goo_player_signals[MESSAGE] =
@@ -352,7 +320,7 @@ goo_player_class_init (GooPlayerClass *class)
 			      G_SIGNAL_RUN_LAST,
 			      G_STRUCT_OFFSET (GooPlayerClass, message),
 			      NULL, NULL,
-			      goo_marshal_VOID__STRING,
+			      g_cclosure_marshal_VOID__STRING,
 			      G_TYPE_NONE, 1,
 			      G_TYPE_STRING);
 	goo_player_signals[STATE_CHANGED] =
@@ -361,246 +329,122 @@ goo_player_class_init (GooPlayerClass *class)
 			      G_SIGNAL_RUN_LAST,
 			      G_STRUCT_OFFSET (GooPlayerClass, state_changed),
 			      NULL, NULL,
-			      goo_marshal_VOID__VOID,
+			      g_cclosure_marshal_VOID__VOID,
 			      G_TYPE_NONE, 0);
-
-        gobject_class->finalize = goo_player_finalize;
-
-	g_type_class_add_private (class, sizeof (GooPlayerPrivateData));
 }
 
 
 static void 
-goo_player_init (GooPlayer *player)
+goo_player_init (GooPlayer *self)
 {
-	GooPlayerPrivateData *priv;
-
-	player->priv = GOO_PLAYER_GET_PRIVATE_DATA (player);
-	priv = player->priv;
-
-	priv->state = GOO_PLAYER_STATE_STOPPED;
-	priv->action = GOO_PLAYER_ACTION_NONE;
-	priv->error = NULL;
-	priv->is_busy = FALSE;
-	
-	priv->yes_or_no = g_mutex_new ();
-	priv->check_id = 0;
-	priv->exiting = FALSE,
-
-	priv->discid = NULL;
-	priv->album = album_info_new ();
-	priv->current_track_n = -1;
-	priv->volume_value = 1.0;
-
-	priv->update_progress_id = 0;
+	self->priv = GOO_PLAYER_GET_PRIVATE_DATA (self);
+	self->priv->state = GOO_PLAYER_STATE_LISTING;
+	self->priv->action = GOO_PLAYER_ACTION_NONE;
+	self->priv->is_busy = FALSE;
+	self->priv->yes_or_no = g_mutex_new ();
+	self->priv->check_id = 0;
+	self->priv->exiting = FALSE,
+	self->priv->discid = NULL;
+	self->priv->album = album_info_new ();
+	self->priv->current_track_n = -1;
+	self->priv->volume_value = 1.0;
+	self->priv->update_progress_id = 0;
 }
 
 
 static void 
 goo_player_finalize (GObject *object)
 {
-        GooPlayer *player;
+        GooPlayer *self;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (GOO_IS_PLAYER (object));
   
-	player = GOO_PLAYER (object);
+	self = GOO_PLAYER (object);
 	
-	g_mutex_lock (player->priv->yes_or_no);
-	player->priv->exiting = TRUE;
-        g_mutex_unlock (player->priv->yes_or_no);
-
- 	if (player->priv != NULL) {
-		GooPlayerPrivateData *priv = player->priv;
-		
-		if (priv->check_id != 0) {
-			g_source_remove (priv->check_id);
-			priv->check_id = 0;
-		}
-
-		destroy_pipeline (player, FALSE);
+	g_mutex_lock (self->priv->yes_or_no);
+	self->priv->exiting = TRUE;
+        g_mutex_unlock (self->priv->yes_or_no);
 
-		g_mutex_free (priv->yes_or_no);
+        if (self->priv->medium_added_event != 0)
+		g_signal_handler_disconnect (self->priv->drive, self->priv->medium_added_event);
+	if (self->priv->medium_removed_event != 0)
+		g_signal_handler_disconnect (self->priv->drive, self->priv->medium_removed_event);
+	g_object_unref (self->priv->drive);
 
-		remove_state_polling (player);
-		destroy_pipeline (player, FALSE);
+	if (self->priv->check_id != 0)
+		g_source_remove (self->priv->check_id);
 
-		g_free (priv->discid);
-		album_info_unref (priv->album);
-
-		player->priv = NULL;
-	}
+	destroy_pipeline (self, FALSE);
+	g_mutex_free (self->priv->yes_or_no);
+	destroy_pipeline (self, FALSE);
+	g_free (self->priv->discid);
+	album_info_unref (self->priv->album);
 
 	G_OBJECT_CLASS (goo_player_parent_class)->finalize (object);
 }
 
 
 static void
-goo_player_set_error (GooPlayer  *player,
-		      GError     *error)
+drive_medium_added_cb (BraseroDrive  *drive,
+		       BraseroMedium *medium,
+		       gpointer       user_data)
 {
-     if (player->priv->error != NULL)
-	     g_error_free (player->priv->error);
-     player->priv->error = error;
+	goo_player_update ((GooPlayer *) user_data);
 }
 
 
 static void
-cdrom_state_changed_cb (GooCdrom  *cdrom,
-			GooPlayer *player)
+drive_medium_removed_cb (BraseroDrive  *drive,
+		         BraseroMedium *medium,
+		         gpointer       user_data)
 {
-	GooCdromState  cdrom_state;
-	char          *state;
-	char          *message = "";
-
-	if (goo_player_get_device (player) == NULL)
-		return; 
-
-	cdrom_state = goo_cdrom_get_state (cdrom);
-
-	switch (cdrom_state) {
-	case GOO_CDROM_STATE_ERROR:
-		state = "ERROR";
-		break;
-	case GOO_CDROM_STATE_UNKNOWN:
-		state = "UNKNOWN";
-		break;
-	case GOO_CDROM_STATE_DRIVE_NOT_READY:
-		state = "DRIVE_NOT_READY";
-		message = _("Drive not ready");
-		break;
-	case GOO_CDROM_STATE_TRAY_OPEN:
-		state = "TRAY_OPEN";
-		message = _("Tray open");
-		message = _("No disc");
-		break;
-	case GOO_CDROM_STATE_NO_DISC:
-		state = "NO_DISC";
-		message = _("No disc");
-		break;
-	case GOO_CDROM_STATE_DATA_CD:
-		state = "DATA_CD";
-		message = _("Data Disc");
-		break;
-	case GOO_CDROM_STATE_OK:
-		state = "OK";
-		break;
-	default:
-		break;
-	}
-
-	debug (DEBUG_INFO, "STATE CHANGED [%s]\n", state);
-
-	if (cdrom_state == GOO_CDROM_STATE_UNKNOWN)
-		return;
-
-	else if (cdrom_state == GOO_CDROM_STATE_OK) {
-		goo_player_set_state (player, GOO_PLAYER_STATE_STOPPED, TRUE);
-		goo_player_list (player);
-	} 
-	else if (cdrom_state == GOO_CDROM_STATE_ERROR) {
-		GError *error = goo_cdrom_get_error (player->priv->cdrom);
-		
-		goo_player_set_error (player, error);
-		goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, TRUE);
-		goo_player_empty_list (player);
-		action_done (player, GOO_PLAYER_ACTION_LIST);
-	} 
-	else if (cdrom_state == GOO_CDROM_STATE_NO_DISC) {
-		goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", message));
-		goo_player_set_state (player, GOO_PLAYER_STATE_NO_DISC, TRUE);
-		goo_player_empty_list (player);
-		action_done (player, GOO_PLAYER_ACTION_LIST);
-	}
-	else if (cdrom_state == GOO_CDROM_STATE_DATA_CD) {
-		goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", message));
-		goo_player_set_state (player, GOO_PLAYER_STATE_DATA_DISC, TRUE);
-		goo_player_empty_list (player);
-		action_done (player, GOO_PLAYER_ACTION_LIST);
-	}
-	else {
-		goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", message));
-		goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, TRUE);
-		goo_player_empty_list (player);
-		action_done (player, GOO_PLAYER_ACTION_LIST);
-		if (cdrom_state == GOO_CDROM_STATE_TRAY_OPEN) 
-			add_state_polling (GOO_PLAYER (player));
-	}
-}
-
+	GooPlayer *self = user_data;
 
-static void
-create_cdrom (GooPlayer *player)
-{
-	if (player->priv->cdrom != NULL)
-		return;
-		
-	player->priv->cdrom = goo_cdrom_new (player->priv->drive->device);
-	g_signal_connect (player->priv->cdrom, 
-			  "state_changed",
-			  G_CALLBACK (cdrom_state_changed_cb), 
-			  GOO_PLAYER (player));
+	goo_player_update (self);
 }
 
 
 GooPlayer *
-goo_player_new (const char *device)
-{
-	GooPlayer  *player;
-
-	player = GOO_PLAYER (g_object_new (GOO_TYPE_PLAYER, NULL));
-
-	player->priv->drive = get_drive_from_device (device);
-	if (player->priv->drive != NULL) 
-		create_cdrom (player);
-
-	return player;
-}
-
-
-CDDrive *
-goo_player_get_drive (GooPlayer *player)
+goo_player_new (BraseroDrive *drive)
 {
-	return player->priv->drive;
+	GooPlayer *self;
+
+	g_return_val_if_fail (drive != NULL, NULL);
+
+	self = GOO_PLAYER (g_object_new (GOO_TYPE_PLAYER, NULL));
+	self->priv->drive = g_object_ref (drive);
+	self->priv->medium_added_event =
+			g_signal_connect (self->priv->drive,
+					  "medium-added",
+					  G_CALLBACK (drive_medium_added_cb),
+					  self);
+	self->priv->medium_removed_event =
+			g_signal_connect (self->priv->drive,
+					  "medium-removed",
+					  G_CALLBACK (drive_medium_removed_cb),
+					  self);
+
+	return self;
 }
 
 
 static void
-notify_action_start (GooPlayer *player)
+notify_action_start (GooPlayer *self)
 {
-	g_signal_emit (G_OBJECT (player), 
+	g_signal_emit (G_OBJECT (self),
 		       goo_player_signals[START], 
 		       0,
-		       player->priv->action, 
+		       self->priv->action,
 		       NULL);
 }
 
 
-void
-goo_player_update (GooPlayer *player)
-{
-	if (player->priv->cdrom == NULL) {
-		goo_player_set_error (player, g_error_new (GOO_CDROM_ERROR, 0, "%s", _("Invalid device")));
-		goo_player_set_state (player, GOO_PLAYER_STATE_NO_DISC, TRUE);
-		goo_player_empty_list (player);
-		action_done (player, GOO_PLAYER_ACTION_LIST);		
-		return;
-	}
-	
-	player->priv->action = GOO_PLAYER_ACTION_UPDATE;
-	player->priv->state = GOO_PLAYER_STATE_UPDATING;
-	notify_action_start (player);
-	
-	goo_cdrom_set_state (player->priv->cdrom, GOO_CDROM_STATE_UNKNOWN);
-	goo_cdrom_update_state (player->priv->cdrom);
-}
-
-
 static void
-goo_player_set_is_busy (GooPlayer *player,
+goo_player_set_is_busy (GooPlayer *self,
 			gboolean   is_busy)
 {
-	player->priv->is_busy = is_busy;
+	self->priv->is_busy = is_busy;
 }
 
 
@@ -608,19 +452,19 @@ goo_player_set_is_busy (GooPlayer *player,
 
 
 void
-goo_player_set_album (GooPlayer *player,
+goo_player_set_album (GooPlayer *self,
 		      AlbumInfo *album)
 {
-	if (player->priv->album == NULL)
+	if (self->priv->album == NULL)
 		return;
-	album_info_copy_metadata (player->priv->album, album);
-	album_info_save_to_cache (player->priv->album, player->priv->discid);
-	action_done (player, GOO_PLAYER_ACTION_METADATA);
+	album_info_copy_metadata (self->priv->album, album);
+	album_info_save_to_cache (self->priv->album, self->priv->discid);
+	action_done (self, GOO_PLAYER_ACTION_METADATA);
 }
 
 
 static void
-set_cd_metadata_from_rdf (GooPlayer *player, 
+set_cd_metadata_from_rdf (GooPlayer *self,
 			  char      *rdf)
 {
 	musicbrainz_t  mb;
@@ -640,7 +484,7 @@ set_cd_metadata_from_rdf (GooPlayer *player,
 		/* FIXME: ask the user which album to use if the query 
 		 * returned more than one album. */
 		
-		goo_player_set_album (player, first_album);		
+		goo_player_set_album (self, first_album);
 		album_list_free (albums);
 	}	
 
@@ -649,20 +493,14 @@ set_cd_metadata_from_rdf (GooPlayer *player,
 
 
 static char *
-get_cached_rdf_path (GooPlayer *player)
+get_cached_rdf_path (GooPlayer *self)
 {
-	char *path = NULL;
-	char *dir;
-	
-	if (player->priv->discid == NULL)
+	if (self->priv->discid != NULL) {
+		gth_user_dir_make_dir_for_file (GTH_DIR_CACHE, "goobox", self->priv->discid, NULL);
+		return gth_user_dir_get_file (GTH_DIR_CACHE, "goobox", self->priv->discid, NULL);
+	}
+	else
 		return NULL;
-		
-	dir = g_build_filename (g_get_home_dir (), ".gnome2", "goobox.d", "cache", NULL);
-	if (ensure_dir_exists (dir, 0700) == GNOME_VFS_OK) 
-		path = g_build_filename (dir, player->priv->discid, NULL);
-	g_free (dir);
-	
-	return path;
 }
 
 
@@ -684,7 +522,7 @@ save_rdf_to_cache (GooPlayer  *player,
 		g_free (path);
     		return;
 	}
-  
+
 	if (! g_file_set_contents (path, rdf, strlen (rdf), &error)) {
 		debug (DEBUG_INFO, "%s\n", error->message);
 		g_clear_error (&error);
@@ -695,13 +533,13 @@ save_rdf_to_cache (GooPlayer  *player,
 
 
 static char *
-read_cached_rdf (GooPlayer *player)
+read_cached_rdf (GooPlayer *self)
 {
 	char   *path;
 	char   *rdf = NULL;
 	GError *error = NULL;
 		
-	path = get_cached_rdf_path (player);
+	path = get_cached_rdf_path (self);
 	if (path == NULL)
 		return NULL;
 	
@@ -742,7 +580,7 @@ check_get_cd_metadata (gpointer data)
 		return FALSE;
 	}
 
-	if (!done) {
+	if (! done) {
 		player->priv->check_id = g_timeout_add (REFRESH_RATE, 
 						        check_get_cd_metadata, 
 					 	        player);
@@ -823,7 +661,7 @@ check_get_cd_tracks (gpointer data)
 		return FALSE;
 	}
 
-	if (!done) {
+	if (! done) {
 		player->priv->check_id = g_timeout_add (REFRESH_RATE, 
 							check_get_cd_tracks, 
 							player);
@@ -878,7 +716,7 @@ get_cd_tracks (void *thread_data)
 
 	mb = mb_New ();
 	mb_UseUTF8 (mb, TRUE);
-	mb_SetDevice (mb, (char*) goo_player_get_device (player));
+	mb_SetDevice (mb, (char *) goo_player_get_device (player));
 	if (mb_Query (mb, MBQ_GetCDTOC)) {
 		char data[256];
 		int  n_tracks, i;
@@ -920,25 +758,52 @@ get_cd_tracks (void *thread_data)
 }
 
 
+gboolean
+goo_player_is_audio_cd (GooPlayer *self)
+{
+	return self->priv->audio_cd;
+}
+
+
+void
+goo_player_update (GooPlayer *self)
+{
+	BraseroMedium *medium;
+
+	self->priv->audio_cd = FALSE;
+
+	medium = brasero_drive_get_medium (self->priv->drive);
+	if (medium == NULL) {
+		goo_player_stop (self);
+		goo_player_set_state (self, GOO_PLAYER_STATE_NO_DISC, TRUE);
+		goo_player_empty_list (self);
+		action_done (self, GOO_PLAYER_ACTION_LIST);
+	}
+	else if ((BRASERO_MEDIUM_IS (brasero_medium_get_status (medium), BRASERO_MEDIUM_CD | BRASERO_MEDIUM_HAS_AUDIO))) {
+		self->priv->audio_cd = TRUE;
+		goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
+		goo_player_list (self);
+	}
+	else {
+		goo_player_stop (self);
+		goo_player_set_state (self, GOO_PLAYER_STATE_DATA_DISC, TRUE);
+		goo_player_empty_list (self);
+		action_done (self, GOO_PLAYER_ACTION_LIST);
+	}
+}
+
+
 void
 goo_player_list (GooPlayer *player)
 {
 	if (goo_player_get_is_busy (player))
 		return;
-			
+
 	player->priv->action = GOO_PLAYER_ACTION_LIST;
 	player->priv->state = GOO_PLAYER_STATE_LISTING;
 	notify_action_start (player);
 	
 	goo_player_empty_list (player);
-
-	if (goo_cdrom_get_state (player->priv->cdrom) != GOO_CDROM_STATE_OK) {
-		debug (DEBUG_INFO, "NOT OK\n");
-		goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, TRUE);
-		action_done (player, GOO_PLAYER_ACTION_LIST);
-		return;
-	}
-
 	goo_player_set_is_busy (player, TRUE);
 	create_pipeline (player);
 	
@@ -996,7 +861,7 @@ goo_player_seek_track (GooPlayer *player,
 	track_n = CLAMP (n, 0, player->priv->album->n_tracks - 1);
 	player->priv->current_track_n = track_n;
 		
-	debug (DEBUG_INFO, "seek to track %d\n", track_n); /* FIXME */
+	debug (DEBUG_INFO, "seek to track %d\n", track_n);
 	
 	player->priv->current_track = get_track (player, player->priv->current_track_n);
 	
@@ -1010,7 +875,9 @@ goo_player_seek_track (GooPlayer *player,
 			        track_n, 
 			        GST_SEEK_TYPE_SET,
 			        track_n + 1))
+	{
 		g_warning ("seek failed");
+	}
 	gst_element_get_state (player->priv->pipeline, NULL, NULL, -1);
 	
 	action_done (player, GOO_PLAYER_ACTION_SEEK_SONG);
@@ -1053,6 +920,7 @@ gboolean
 goo_player_set_device (GooPlayer  *player,
 		       const char *device)
 {
+	/* FIXME
 	if (goo_player_get_is_busy (player))
 		return TRUE;
 
@@ -1062,24 +930,31 @@ goo_player_set_device (GooPlayer  *player,
 	remove_state_polling (player);
 
 	player->priv->drive = get_drive_from_device (device);
-	if (player->priv->drive == NULL) 
-		goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, FALSE);
-	else {
+	if (player->priv->drive != NULL) {
 		create_cdrom (player);
-		goo_cdrom_set_device (player->priv->cdrom, player->priv->drive->device);
+		goo_cdrom_set_device (player->priv->cdrom, player->priv->device);
 	}
+	else
+		goo_player_set_state (player, GOO_PLAYER_STATE_ERROR, FALSE);
 		
 	return TRUE;
+	*/
+
+	return TRUE;
+}
+
+
+BraseroDrive *
+goo_player_get_drive (GooPlayer *self)
+{
+	return self->priv->drive;
 }
 
 
 const char *
-goo_player_get_device (GooPlayer *player)
+goo_player_get_device (GooPlayer *self)
 {
-	if (player->priv->drive == NULL)
-		return NULL;
-	else
-		return player->priv->drive->device;
+	return brasero_drive_get_device (self->priv->drive);
 }
 
 
@@ -1102,15 +977,16 @@ goo_player_play (GooPlayer *player)
 	if (! ((player->priv->pipeline != NULL)
 	       && ((goo_player_get_state (player) == GOO_PLAYER_STATE_PAUSED)
 		   || (goo_player_get_state (player) == GOO_PLAYER_STATE_SEEKING))))
+	{
 		create_pipeline (player);
+	}
 
-	g_object_set (G_OBJECT (player->priv->volume), "volume", goo_player_get_volume (player), NULL);
+	g_object_set (G_OBJECT (player->priv->audio_volume), "volume", goo_player_get_audio_volume (player), NULL);
 
 	gst_element_set_state (player->priv->pipeline, GST_STATE_PLAYING);
 	goo_player_set_state (player, GOO_PLAYER_STATE_PLAYING, TRUE);
 
 	player->priv->update_progress_id = g_timeout_add (PROGRESS_DELAY, update_progress_cb, player);
-
 }
 
 
@@ -1148,40 +1024,43 @@ goo_player_stop (GooPlayer *player)
 }
 
 
-gboolean
-goo_player_eject (GooPlayer *player)
+
+static void
+eject_ready_cb (GObject      *source_object,
+                GAsyncResult *result,
+                gpointer      user_data)
 {
-	GooCdromState cdrom_state;
-	gboolean      result;
+	GooPlayer *self = user_data;
+	GError    *error = NULL;
 
-	if (goo_player_get_is_busy (player))
-		return FALSE;
+	if (! g_drive_eject_with_operation_finish (G_DRIVE (source_object), result, &error))
+		g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_EJECT, error);
+	else
+		g_signal_emit_by_name (G_OBJECT (self), "done", GOO_PLAYER_ACTION_EJECT, NULL);
 
-	player->priv->action = GOO_PLAYER_ACTION_EJECT;
-	player->priv->state = GOO_PLAYER_STATE_EJECTING;
-	notify_action_start (player);
+	goo_player_set_state (self, GOO_PLAYER_STATE_STOPPED, TRUE);
+}
 
-	destroy_pipeline (player, TRUE);
 
-	cdrom_state = goo_cdrom_get_state (player->priv->cdrom);
-	result = goo_cdrom_eject (player->priv->cdrom);
+void
+goo_player_eject (GooPlayer *self)
+{
+	GDrive *gdrive;
 
-	if (!result)
-		goo_player_set_error (player, goo_cdrom_get_error (player->priv->cdrom));
+	if (self->priv->drive == NULL)
+		return;
 
-	action_done (player, GOO_PLAYER_ACTION_EJECT);
-	
-	return result;	
-}
+	g_signal_emit_by_name (G_OBJECT (self), "start", GOO_PLAYER_ACTION_EJECT);
 
+	gdrive = brasero_drive_get_gdrive (self->priv->drive);
+	g_drive_eject_with_operation (gdrive,
+				      G_MOUNT_UNMOUNT_NONE,
+				      NULL,
+				      NULL,
+				      eject_ready_cb,
+				      self);
 
-GError *
-goo_player_get_error (GooPlayer *player)
-{
-     if (player->priv->error != NULL)
-	     return g_error_copy (player->priv->error);
-     else
-	     return NULL;
+	g_object_unref (gdrive);
 }
 
 
@@ -1200,22 +1079,22 @@ goo_player_get_state (GooPlayer *player)
 
 
 double
-goo_player_get_volume (GooPlayer *player)
+goo_player_get_audio_volume (GooPlayer *player)
 {
 	return player->priv->volume_value;
 }
 
 
 void
-goo_player_set_volume (GooPlayer *player,
-		       double     vol)
+goo_player_set_audio_volume (GooPlayer *player,
+		             double     vol)
 {
 	if (goo_player_get_is_busy (player))
 		return;
 
 	player->priv->volume_value = vol;
-	if (player->priv->volume != NULL)
-		g_object_set (G_OBJECT (player->priv->volume), "volume", vol, NULL);	
+	if (player->priv->audio_volume != NULL)
+		g_object_set (G_OBJECT (player->priv->audio_volume), "volume", vol, NULL);
 }
 
 
diff --git a/src/goo-player.h b/src/goo-player.h
index f337e23..f6dff79 100644
--- a/src/goo-player.h
+++ b/src/goo-player.h
@@ -3,7 +3,7 @@
 /*
  *  Goo
  *
- *  Copyright (C) 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2004-2009 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -24,7 +24,8 @@
 #define GOO_PLAYER_H
 
 #include <glib.h>
-#include "cd-drive.h"
+#include <gio/gio.h>
+#include <brasero/brasero-drive.h>
 #include "track-info.h"
 #include "album-info.h"
 
@@ -35,9 +36,9 @@
 #define GOO_IS_PLAYER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_PLAYER))
 #define GOO_PLAYER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GOO_TYPE_PLAYER, GooPlayerClass))
 
-typedef struct _GooPlayer            GooPlayer;
-typedef struct _GooPlayerClass       GooPlayerClass;
-typedef struct _GooPlayerPrivateData GooPlayerPrivateData;
+typedef struct _GooPlayer        GooPlayer;
+typedef struct _GooPlayerClass   GooPlayerClass;
+typedef struct _GooPlayerPrivate GooPlayerPrivate;
 
 typedef enum {
 	GOO_PLAYER_ACTION_NONE,
@@ -68,7 +69,7 @@ typedef enum {
 struct _GooPlayer
 {
 	GObject __parent;
-	GooPlayerPrivateData *priv;
+	GooPlayerPrivate *priv;
 };
 
 struct _GooPlayerClass
@@ -90,8 +91,10 @@ struct _GooPlayerClass
 };
 
 GType            goo_player_get_type            (void);
-GooPlayer *      goo_player_new                 (const char      *device);
-CDDrive *        goo_player_get_drive           (GooPlayer       *player);
+GooPlayer *      goo_player_new                 (BraseroDrive    *drive);
+BraseroDrive *   goo_player_get_drive           (GooPlayer       *player);
+const char *     goo_player_get_device          (GooPlayer       *player);
+gboolean         goo_player_is_audio_cd         (GooPlayer       *player);
 void             goo_player_update              (GooPlayer       *player);
 void             goo_player_list                (GooPlayer       *player);
 void             goo_player_seek_track          (GooPlayer       *player,
@@ -102,20 +105,18 @@ void             goo_player_skip_to             (GooPlayer       *player,
 void             goo_player_play                (GooPlayer       *player);
 void             goo_player_pause               (GooPlayer       *player);
 void             goo_player_stop                (GooPlayer       *player);
-gboolean         goo_player_eject               (GooPlayer       *player);
-GError *         goo_player_get_error           (GooPlayer       *player);
+void             goo_player_eject               (GooPlayer       *player);
 GooPlayerAction  goo_player_get_action          (GooPlayer       *player);
 GooPlayerState   goo_player_get_state           (GooPlayer       *player);
 gboolean         goo_player_set_device          (GooPlayer       *player,
 						 const char      *device);
-const char *     goo_player_get_device          (GooPlayer       *player);
 const char *     goo_player_get_discid          (GooPlayer       *player);
 void             goo_player_set_album           (GooPlayer       *player,
 						 AlbumInfo       *album);
 AlbumInfo *      goo_player_get_album           (GooPlayer       *player);
-void             goo_player_set_volume          (GooPlayer       *player,
+void             goo_player_set_audio_volume    (GooPlayer       *player,
 						 double           vol);
-double           goo_player_get_volume          (GooPlayer       *player);
+double           goo_player_get_audio_volume    (GooPlayer       *player);
 gboolean         goo_player_get_is_busy         (GooPlayer       *player);
 
 #endif /* GOO_PLAYER_H */
diff --git a/src/goo-stock.c b/src/goo-stock.c
index 9a5f599..9f45906 100644
--- a/src/goo-stock.c
+++ b/src/goo-stock.c
@@ -21,8 +21,8 @@
  */
 
 #include <config.h>
+#include <glib/gi18n.h>
 #include <gtk/gtk.h>
-#include <gnome.h>
 #include "goo-stock.h"
 #include "icons/pixbufs.h"
 
diff --git a/src/goo-volume-tool-button.c b/src/goo-volume-tool-button.c
index e8d2332..2341fd4 100644
--- a/src/goo-volume-tool-button.c
+++ b/src/goo-volume-tool-button.c
@@ -39,16 +39,14 @@
 
 #include <config.h>
 #include <math.h>
-#include <gnome.h>
 #include <gdk/gdkkeysyms.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
 #include "glib-utils.h"
 #include "goo-volume-tool-button.h"
 #include "goo-marshal.h"
 #include "goo-stock.h"
 
-#define GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_VOLUME_TOOL_BUTTON, GooVolumeToolButtonPrivate))
-#define FLOAT_EQUAL(a,b) (fabs ((a) - (b)) < 1e-6)
-
 #define SCALE_WIDTH 25
 #define SCALE_HEIGHT 100
 #define DEFAULT_VOLUME 1.0
@@ -88,8 +86,10 @@ enum {
 
 static guint goo_volume_tool_button_signals[LAST_SIGNAL] = { 0 };
 
+
 G_DEFINE_TYPE (GooVolumeToolButton, goo_volume_tool_button, GTK_TYPE_TOOL_BUTTON)
 
+
 static gboolean
 goo_volume_tool_button_set_tooltip (GtkToolItem *tool_item,
 				    GtkTooltips *tooltips,
@@ -110,51 +110,48 @@ goo_volume_tool_button_set_tooltip (GtkToolItem *tool_item,
 static void
 goo_volume_tool_button_construct_contents (GooVolumeToolButton *button)
 {
-	GooVolumeToolButtonPrivate *priv;
-	GtkWidget                  *box;
-	GtkOrientation              orientation;
-
-	priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
+	GtkWidget      *box;
+	GtkOrientation  orientation;
 
 	orientation = gtk_tool_item_get_orientation (GTK_TOOL_ITEM (button));
 
 	if (orientation == GTK_ORIENTATION_HORIZONTAL) {
 		box = gtk_hbox_new (FALSE, 0);
-		gtk_arrow_set (GTK_ARROW (priv->arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+		gtk_arrow_set (GTK_ARROW (button->priv->arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
 	} 
 	else {
 		box = gtk_vbox_new (FALSE, 0);
-		gtk_arrow_set (GTK_ARROW (priv->arrow), GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+		gtk_arrow_set (GTK_ARROW (button->priv->arrow), GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
 	}
 
-	if (priv->button && priv->button->parent) {
-		g_object_ref (priv->button);
-		gtk_container_remove (GTK_CONTAINER (priv->button->parent),
-				      priv->button);
-		gtk_container_add (GTK_CONTAINER (box), priv->button);
-		g_object_unref (priv->button);
+	if (button->priv->button && button->priv->button->parent) {
+		g_object_ref (button->priv->button);
+		gtk_container_remove (GTK_CONTAINER (button->priv->button->parent),
+				      button->priv->button);
+		gtk_container_add (GTK_CONTAINER (box), button->priv->button);
+		g_object_unref (button->priv->button);
 	}
 
-	if (priv->arrow_button && priv->arrow_button->parent) {
-		g_object_ref (priv->arrow_button);
-		gtk_container_remove (GTK_CONTAINER (priv->arrow_button->parent),
-				      priv->arrow_button);
-		gtk_box_pack_end (GTK_BOX (box), priv->arrow_button,
+	if (button->priv->arrow_button && button->priv->arrow_button->parent) {
+		g_object_ref (button->priv->arrow_button);
+		gtk_container_remove (GTK_CONTAINER (button->priv->arrow_button->parent),
+				      button->priv->arrow_button);
+		gtk_box_pack_end (GTK_BOX (box), button->priv->arrow_button,
 				  FALSE, FALSE, 0);
-		g_object_unref (priv->arrow_button);
+		g_object_unref (button->priv->arrow_button);
 	}
 
-	if (priv->box) {
+	if (button->priv->box) {
 		/* Note: we are not destroying the button and the arrow_button
 		 * here because they were removed from their container above
 		 */
-		gtk_widget_destroy (priv->box);
+		gtk_widget_destroy (button->priv->box);
 	}
 
-	priv->box = box;
+	button->priv->box = box;
 
-	gtk_container_add (GTK_CONTAINER (button), priv->box);
-	gtk_widget_show_all (priv->box);
+	gtk_container_add (GTK_CONTAINER (button), button->priv->box);
+	gtk_widget_show_all (button->priv->box);
 
 	gtk_widget_queue_resize (GTK_WIDGET (button));
 }
@@ -204,7 +201,7 @@ goo_volume_tool_button_toolbar_reconfigured (GtkToolItem *toolitem)
 static void
 goo_volume_tool_button_init (GooVolumeToolButton *button)
 {
-	button->priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
+	button->priv = G_TYPE_INSTANCE_GET_PRIVATE ((button), GOO_TYPE_VOLUME_TOOL_BUTTON, GooVolumeToolButtonPrivate);
 	button->priv->mute = FALSE;
 	button->priv->mute_value = 0.0;
 	button->priv->value = 0.0;
@@ -253,38 +250,6 @@ goo_volume_tool_button_class_init (GooVolumeToolButtonClass *klass)
 	g_type_class_add_private (object_class, sizeof (GooVolumeToolButtonPrivate));
 }
 
-static void
-button_state_changed_cb (GtkWidget           *widget,
-                         GtkStateType         previous_state,
-                         GooVolumeToolButton *button)
-{
-	GooVolumeToolButtonPrivate *priv;
-	GtkWidget *other;
-	GtkStateType state = GTK_WIDGET_STATE (widget);
-	
-	priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
-
-	other = (widget == priv->arrow_button) ? priv->button : priv->arrow_button;
-
-	g_signal_handlers_block_by_func (other,
-					 G_CALLBACK (button_state_changed_cb),
-					 button);
-	
-	if (state == GTK_STATE_PRELIGHT) {
-		gtk_widget_set_state (other, state);
-	} 
-	else if (state == GTK_STATE_NORMAL) {
-		gtk_widget_set_state (other, state);
-	} 
-	else if (state == GTK_STATE_ACTIVE) {
-		gtk_widget_set_state (other, GTK_STATE_NORMAL);
-	}
-
-	g_signal_handlers_unblock_by_func (other,
-					   G_CALLBACK (button_state_changed_cb),
-					   button);
-}
-
 
 static gboolean
 button_clicked_cb (GtkWidget           *widget,
@@ -313,7 +278,6 @@ arrow_button_press_cb (GtkToggleButton     *toggle_button,
 		       GdkEventButton      *event,
 		       GooVolumeToolButton *button)
 {
-	GooVolumeToolButtonPrivate *priv;
 	GtkWidget      *widget;
 	GtkAdjustment  *adj;
   	int             x, y, m, dx, dy, sx, sy, ystartoff, mouse_y;
@@ -321,43 +285,41 @@ arrow_button_press_cb (GtkToggleButton     *toggle_button,
   	GdkEventButton *e;
 
 	gtk_toggle_button_set_active (toggle_button, TRUE);
-  	
-	priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
 	
 	widget = GTK_WIDGET (toggle_button);
-	adj = gtk_range_get_adjustment (GTK_RANGE (priv->volume_scale));
+	adj = gtk_range_get_adjustment (GTK_RANGE (button->priv->volume_scale));
 	
-	priv->timeout = TRUE;
+	button->priv->timeout = TRUE;
 
-    	gtk_window_set_screen (GTK_WINDOW (priv->popup_win), gtk_widget_get_screen (widget));
+    	gtk_window_set_screen (GTK_WINDOW (button->priv->popup_win), gtk_widget_get_screen (widget));
   
   	gdk_window_get_origin (widget->window, &x, &y);
   	x += widget->allocation.x;
   	y += widget->allocation.y;
   
-	gtk_window_move (GTK_WINDOW (priv->popup_win),
+	gtk_window_move (GTK_WINDOW (button->priv->popup_win),
 			 x, y - (SCALE_HEIGHT / 2));
 	update_volume_label (button);
-	gtk_widget_show_all (priv->popup_win);
+	gtk_widget_show_all (button->priv->popup_win);
 
-	gdk_window_get_origin (priv->popup_win->window, &dx, &dy);
-  	dy += priv->popup_win->allocation.y;
-  	gdk_window_get_origin (priv->volume_scale->window, &sx, &sy);
-  	sy += priv->volume_scale->allocation.y;
+	gdk_window_get_origin (button->priv->popup_win->window, &dx, &dy);
+  	dy += button->priv->popup_win->allocation.y;
+  	gdk_window_get_origin (button->priv->volume_scale->window, &sx, &sy);
+  	sy += button->priv->volume_scale->allocation.y;
   	ystartoff = sy - dy;
   	mouse_y = event->y;
 
-	v = gtk_range_get_value (GTK_RANGE (priv->volume_scale)) / (adj->upper - adj->lower);
-  	x += (widget->allocation.width - priv->popup_win->allocation.width) / 2;
+	v = gtk_range_get_value (GTK_RANGE (button->priv->volume_scale)) / (adj->upper - adj->lower);
+  	x += (widget->allocation.width - button->priv->popup_win->allocation.width) / 2;
   	y -= ystartoff;
-  	y -= GTK_RANGE (priv->volume_scale)->min_slider_size / 2;
-  	m = priv->volume_scale->allocation.height - GTK_RANGE (priv->volume_scale)->min_slider_size;
+  	y -= GTK_RANGE (button->priv->volume_scale)->min_slider_size / 2;
+  	m = button->priv->volume_scale->allocation.height - GTK_RANGE (button->priv->volume_scale)->min_slider_size;
   	y -= m * (1.0 - v);
   	y += mouse_y;
-  	gtk_window_move (GTK_WINDOW (priv->popup_win), x, y);
-  	gdk_window_get_origin (priv->volume_scale->window, &sx, &sy);
+  	gtk_window_move (GTK_WINDOW (button->priv->popup_win), x, y);
+  	gdk_window_get_origin (button->priv->volume_scale->window, &sx, &sy);
 	
-	gdk_pointer_grab (priv->popup_win->window, 
+	gdk_pointer_grab (button->priv->popup_win->window,
 			  TRUE,
 			  (GDK_POINTER_MOTION_MASK 
 			   | GDK_BUTTON_PRESS_MASK 
@@ -365,21 +327,21 @@ arrow_button_press_cb (GtkToggleButton     *toggle_button,
 			  NULL, 
 			  NULL, 
 			  GDK_CURRENT_TIME);
-	gdk_keyboard_grab (priv->popup_win->window, TRUE, GDK_CURRENT_TIME);
-	gtk_widget_grab_focus (priv->volume_scale);
-	gtk_grab_add (priv->popup_win);
+	gdk_keyboard_grab (button->priv->popup_win->window, TRUE, GDK_CURRENT_TIME);
+	gtk_widget_grab_focus (button->priv->volume_scale);
+	gtk_grab_add (button->priv->popup_win);
 
 	/* forward event to the slider */
 	e = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
-	e->window = priv->volume_scale->window;
-	e->x = priv->volume_scale->allocation.width / 2;
-	m = priv->volume_scale->allocation.height - GTK_RANGE (priv->volume_scale)->min_slider_size;
-	e->y = ((1.0 - v) * m) + GTK_RANGE (priv->volume_scale)->min_slider_size / 2 + 1;
-	gtk_widget_event (priv->volume_scale, (GdkEvent *) e);
+	e->window = button->priv->volume_scale->window;
+	e->x = button->priv->volume_scale->allocation.width / 2;
+	m = button->priv->volume_scale->allocation.height - GTK_RANGE (button->priv->volume_scale)->min_slider_size;
+	e->y = ((1.0 - v) * m) + GTK_RANGE (button->priv->volume_scale)->min_slider_size / 2 + 1;
+	gtk_widget_event (button->priv->volume_scale, (GdkEvent *) e);
 	e->window = event->window;
 	gdk_event_free ((GdkEvent *) e);		
 
-	priv->pop_time = event->time;
+	button->priv->pop_time = event->time;
 
 	return TRUE;
 }
@@ -417,16 +379,13 @@ scale_button_release_cb (GtkToggleButton     *toggle_button,
 		         GdkEventButton      *event,
 		         GooVolumeToolButton *button)
 {
-	GooVolumeToolButtonPrivate *priv;
-	priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
-
-	if (priv->timeout) {
+	if (button->priv->timeout) {
 		/* if we did a quick click, leave the window open; else, hide it */
-		if (event->time > priv->pop_time + CLICK_TIMEOUT) {
+		if (event->time > button->priv->pop_time + CLICK_TIMEOUT) {
 			ungrab (button);
 			return FALSE;
 		}
-		priv->timeout = FALSE;
+		button->priv->timeout = FALSE;
 	}
 	
 	return FALSE;
@@ -438,10 +397,7 @@ popup_win_event_cb (GtkWidget           *widget,
 		    GdkEvent            *event, 
 		    GooVolumeToolButton *button)
 {
-	GooVolumeToolButtonPrivate *priv;
-	GtkWidget                  *event_widget;
-
-	priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
+	GtkWidget *event_widget;
 
 	switch (event->type) {
 	case GDK_BUTTON_PRESS:
@@ -455,7 +411,7 @@ popup_win_event_cb (GtkWidget           *widget,
 		else {
 			int x, y, w, h;
 			
-			gdk_window_get_geometry (priv->popup_win->window, 
+			gdk_window_get_geometry (button->priv->popup_win->window,
 						 &x, &y, &w, &h, NULL);
 
 			if ((event->button.x < 0) 
@@ -491,7 +447,6 @@ button_scroll_event_cb (GtkWidget           *widget,
 			GdkEventScroll      *event,
 			GooVolumeToolButton *button)
 {
-	GooVolumeToolButtonPrivate *priv = GOO_VOLUME_TOOL_BUTTON_GET_PRIVATE (button);
 	double direction = 1.0;
 
 	if (event->direction == GDK_SCROLL_UP) 
@@ -502,7 +457,7 @@ button_scroll_event_cb (GtkWidget           *widget,
 		return FALSE;
 
 	goo_volume_tool_button_set_volume (button, 
-					   priv->value + (direction * priv->step),
+					   button->priv->value + (direction * button->priv->step),
 					   TRUE);
 
 	return TRUE;
@@ -680,7 +635,6 @@ up_button_clicked_cb (GtkButton *gtk_button,
 static void 
 goo_volume_button_construct (GooVolumeToolButton *button)
 {
-	struct _GooVolumeToolButtonPrivate *priv = button->priv;
 	GtkWidget *box;
 	GtkWidget *arrow;
 	GtkWidget *arrow_button;
@@ -690,23 +644,23 @@ goo_volume_button_construct (GooVolumeToolButton *button)
 	GtkWidget *up_button;
 	GtkWidget *down_button;
 	
-	priv->tips = gtk_tooltips_new ();
-	gtk_object_ref (GTK_OBJECT (priv->tips));
-	gtk_object_sink (GTK_OBJECT (priv->tips));
+	button->priv->tips = gtk_tooltips_new ();
+	gtk_object_ref (GTK_OBJECT (button->priv->tips));
+	gtk_object_sink (GTK_OBJECT (button->priv->tips));
 
 	/* Create the popup window. */
 
-	priv->popup_win = gtk_window_new (GTK_WINDOW_POPUP);
-	gtk_window_set_wmclass (GTK_WINDOW (priv->popup_win), "", "goo_volume_button");
+	button->priv->popup_win = gtk_window_new (GTK_WINDOW_POPUP);
+	gtk_window_set_wmclass (GTK_WINDOW (button->priv->popup_win), "", "goo_volume_button");
 	
-	g_signal_connect (G_OBJECT (priv->popup_win),
+	g_signal_connect (G_OBJECT (button->priv->popup_win),
 			  "event",
 			  G_CALLBACK (popup_win_event_cb), 
 			  button);
 
 	out_frame = gtk_frame_new (NULL);
 	gtk_frame_set_shadow_type (GTK_FRAME (out_frame), GTK_SHADOW_OUT);
-	gtk_container_add (GTK_CONTAINER (priv->popup_win), out_frame);
+	gtk_container_add (GTK_CONTAINER (button->priv->popup_win), out_frame);
 
 	/**/
 
@@ -738,20 +692,20 @@ goo_volume_button_construct (GooVolumeToolButton *button)
 			  button);
 	gtk_box_pack_start (GTK_BOX (volume_vbox), up_button, FALSE, FALSE, 0);
 
-	priv->volume_scale = gtk_vscale_new_with_range (0.0, 1.0, 0.1);
-	gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
-	gtk_scale_set_draw_value (GTK_SCALE (priv->volume_scale), FALSE);
-	/*gtk_range_set_update_policy (GTK_RANGE (priv->volume_scale), GTK_UPDATE_DELAYED);*/
-	gtk_range_set_increments (GTK_RANGE (priv->volume_scale), 0.1, 0.1);
-	gtk_widget_set_size_request (priv->volume_scale, -1, SCALE_HEIGHT);
+	button->priv->volume_scale = gtk_vscale_new_with_range (0.0, 1.0, 0.1);
+	gtk_range_set_inverted (GTK_RANGE (button->priv->volume_scale), TRUE);
+	gtk_scale_set_draw_value (GTK_SCALE (button->priv->volume_scale), FALSE);
+	/*gtk_range_set_update_policy (GTK_RANGE (button->priv->volume_scale), GTK_UPDATE_DELAYED);*/
+	gtk_range_set_increments (GTK_RANGE (button->priv->volume_scale), 0.1, 0.1);
+	gtk_widget_set_size_request (button->priv->volume_scale, -1, SCALE_HEIGHT);
 	 
-	gtk_box_pack_start (GTK_BOX (volume_vbox), priv->volume_scale, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (volume_vbox), button->priv->volume_scale, TRUE, TRUE, 0);
 
-	g_signal_connect (priv->volume_scale, 
+	g_signal_connect (button->priv->volume_scale,
 			  "value_changed",
 			  G_CALLBACK (volume_scale_value_changed_cb), 
 			  button);
-	g_signal_connect (priv->volume_scale, 
+	g_signal_connect (button->priv->volume_scale,
 			  "button_release_event",
 			  G_CALLBACK (scale_button_release_cb), 
 			  button);
@@ -783,8 +737,8 @@ goo_volume_button_construct (GooVolumeToolButton *button)
 	/**/
 
 	gtk_box_pack_start (GTK_BOX (volume_vbox), gtk_hseparator_new (), FALSE, FALSE, 0);
-	priv->volume_label = gtk_label_new (NULL);
-	gtk_box_pack_start (GTK_BOX (volume_vbox), priv->volume_label, FALSE, FALSE, 3);
+	button->priv->volume_label = gtk_label_new (NULL);
+	gtk_box_pack_start (GTK_BOX (volume_vbox), button->priv->volume_label, FALSE, FALSE, 3);
 
 	/* Construct the toolbar button.  */
 
@@ -805,7 +759,7 @@ goo_volume_button_construct (GooVolumeToolButton *button)
 	gtk_container_add (GTK_CONTAINER (arrow_button), arrow);
 	gtk_box_pack_end (GTK_BOX (box), arrow_button,
 			  FALSE, FALSE, 0);
-	gtk_tooltips_set_tip (priv->tips, arrow_button, _("Change the volume level"), NULL);
+	gtk_tooltips_set_tip (button->priv->tips, arrow_button, _("Change the volume level"), NULL);
 
 	gtk_widget_show_all (box);
 	gtk_widget_hide (button->priv->volume_label);
@@ -818,15 +772,6 @@ goo_volume_button_construct (GooVolumeToolButton *button)
 	button->priv->box = box;
 
 	g_signal_connect (real_button, 
-			  "state_changed",
-			  G_CALLBACK (button_state_changed_cb), 
-			  button);
-	g_signal_connect (arrow_button, 
-			  "state_changed",
-			  G_CALLBACK (button_state_changed_cb), 
-			  button);
-
-	g_signal_connect (real_button, 
 			  "clicked",
 			  G_CALLBACK (button_clicked_cb), 
 			  button);
diff --git a/src/goo-volume-tool-button.h b/src/goo-volume-tool-button.h
index cf49e22..2172ad8 100644
--- a/src/goo-volume-tool-button.h
+++ b/src/goo-volume-tool-button.h
@@ -64,8 +64,6 @@ typedef struct _GooVolumeToolButtonPrivate GooVolumeToolButtonPrivate;
 
 struct _GooVolumeToolButton {
 	GtkToolButton parent;
-	
-	/*< private >*/
 	GooVolumeToolButtonPrivate *priv;
 };
 
diff --git a/src/goo-window.c b/src/goo-window.c
index 564e465..158a384 100644
--- a/src/goo-window.c
+++ b/src/goo-window.c
@@ -3,7 +3,7 @@
 /*
  *  Goo
  *
- *  Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ *  Copyright (C) 2004-2009 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -23,21 +23,23 @@
 #include <config.h>
 #include <math.h>
 #include <string.h>
-#include <gnome.h>
-#include <libbonobo.h>
-#include <libgnomevfs/gnome-vfs-ops.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
 #include <gst/gst.h>
-
+#ifdef ENABLE_MEDIA_KEYS
+#include <dbus/dbus-glib.h>
+#endif /* ENABLE_MEDIA_KEYS */
 #include "actions.h"
+#include "gio-utils.h"
 #include "dlg-cover-chooser.h"
-#include "eggtrayicon.h"
-#include "file-utils.h"
 #include "goo-marshal.h"
 #include "goo-stock.h"
 #include "goo-player.h"
 #include "goo-player-info.h"
 #include "goo-window.h"
 #include "goo-volume-tool-button.h"
+#include "gth-user-dir.h"
 #include "gtk-utils.h"
 #include "gtk-file-chooser-preview.h"
 #include "glib-utils.h"
@@ -46,7 +48,6 @@
 #include "preferences.h"
 #include "typedefs.h"
 #include "ui.h"
-
 #include "icons/pixbufs.h"
 
 #define ICON_GTK_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR
@@ -56,7 +57,6 @@
 #define DEFAULT_WIN_HEIGHT 400
 #define HIDE_TRACK_LIST N_("Hide _tracks")
 #define SHOW_TRACK_LIST N_("Show _tracks")
-#define DEFAULT_DEVICE "/dev/cdrom"
 #define DEFAULT_VOLUME 100
 #define PLAYER_CHECK_RATE 100
 #define COVER_SIZE 80
@@ -67,7 +67,7 @@
 #define TRAY_TOOLTIP_DELAY 500
 #define AUTOPLAY_DELAY 250
 
-struct _GooWindowPrivateData {
+struct _GooWindowPrivate {
 	GtkUIManager      *ui;
 	GtkWidget         *list_view;
 	GtkListStore      *list_store;
@@ -98,7 +98,7 @@ struct _GooWindowPrivateData {
 	guint              list_info_cid;
 	guint              progress_cid;
 
-	guint              first_timeout_handle;
+	guint              first_time_event;
 	guint              next_timeout_handle;
 	gint               activity_ref;              /* when > 0 some activity
                                                        * is present. */
@@ -121,14 +121,19 @@ struct _GooWindowPrivateData {
 	gboolean           hibernate;
 	gboolean           notify_action;
 	gboolean           ejected;
+
+#ifdef ENABLE_MEDIA_KEYS
+	DBusGProxy        *media_keys_proxy;
+	gulong             focus_in_event;
+#endif /* ENABLE_MEDIA_KEYS */
 };
 
 enum {
 	UPDATE_COVER,
         LAST_SIGNAL
 };
-static guint goo_window_signals[LAST_SIGNAL] = { 0 };
 
+static guint goo_window_signals[LAST_SIGNAL] = { 0 };
 static int icon_size = 0;
 GList *window_list = NULL;
 
@@ -144,10 +149,11 @@ enum {
 };
 
 #define GOO_WINDOW_GET_PRIVATE_DATA(object) \
-	(G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_WINDOW, GooWindowPrivateData))
+	(G_TYPE_INSTANCE_GET_PRIVATE ((object), GOO_TYPE_WINDOW, GooWindowPrivate))
+
 
+G_DEFINE_TYPE (GooWindow, goo_window, GTH_TYPE_WINDOW)
 
-G_DEFINE_TYPE (GooWindow, goo_window, GNOME_TYPE_APP)
 
 static void
 set_active (GooWindow  *window,
@@ -176,73 +182,38 @@ set_sensitive (GooWindow  *window,
 static void
 window_update_statusbar_list_info (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-	
 	if (window == NULL)
 		return;
 
-	gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->list_info_cid);
+	gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), window->priv->list_info_cid);
 
-	if (priv->album->n_tracks != 0) {
-		char        time_text[64];
-		char       *tracks_s = NULL;
-		GString    *status;
+	if (window->priv->album->n_tracks != 0) {
+		char    *time_text;
+		char    *tracks_s = NULL;
+		GString *status;
 
-		tracks_s = g_strdup_printf (ngettext ("%d track", "%d tracks", priv->album->n_tracks), priv->album->n_tracks);
-		set_time_string (time_text, priv->album->total_length);
+		tracks_s = g_strdup_printf (ngettext ("%d track", "%d tracks", window->priv->album->n_tracks), window->priv->album->n_tracks);
+		time_text = _g_format_duration_for_display (window->priv->album->total_length * 1000);
 		
 		status = g_string_new (NULL);
 		g_string_append (status, tracks_s);
 		g_string_append (status, ", ");
 		g_string_append (status, time_text);
 
-		gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), 
-				    priv->list_info_cid, 
+		gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar),
+				    window->priv->list_info_cid,
 				    status->str);
 
 		g_string_free (status, TRUE);
+		g_free (time_text);
 		g_free (tracks_s);
 	}
 }
 
 
-/* window_update_list */
-
-
-typedef struct {
-	GooWindow *window;
-	GList     *file_list;
-} UpdateData;
-
-
-static void
-update_data_free (gpointer callback_data)
-{
-	UpdateData *data = callback_data;
-
-	g_return_if_fail (data != NULL);
-
-	window_update_statusbar_list_info (data->window);
-
-	if (data->file_list != NULL) 
-		g_list_free (data->file_list);
-	g_free (data);
-}
-
-
-static char*
-get_time_string (gint64 time)
-{
-	char buffer[1024];
-	set_time_string (buffer, time);
-	return g_strdup (buffer);
-}
-
-
 static void
 window_update_sensitivity (GooWindow *window)
 {
-	GooWindowPrivateData  *priv = window->priv;
 	int            n_selected;
 	gboolean       sel_not_null;
 	gboolean       one_file_selected;
@@ -254,11 +225,11 @@ window_update_sensitivity (GooWindow *window)
 	gboolean       stopped;
 	gboolean       play_all;
 
-	n_selected        = _gtk_count_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view)));
+	n_selected        = _gtk_count_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)));
 	sel_not_null      = n_selected > 0;
 	one_file_selected = n_selected == 1;
-	state             = goo_player_get_state (priv->player);
-	error             = (state == GOO_PLAYER_STATE_ERROR) || priv->hibernate;
+	state             = goo_player_get_state (window->priv->player);
+	error             = ! goo_player_is_audio_cd (window->priv->player) || window->priv->hibernate;
 	playing           = state == GOO_PLAYER_STATE_PLAYING;
 	paused            = state == GOO_PLAYER_STATE_PAUSED;
 	stopped           = state == GOO_PLAYER_STATE_STOPPED;
@@ -273,8 +244,8 @@ window_update_sensitivity (GooWindow *window)
 	set_sensitive (window, "Next", audio_cd);
 	set_sensitive (window, "Prev", audio_cd);
 
-	set_sensitive (window, "Extract", audio_cd && (priv->album->n_tracks > 0));
-	set_sensitive (window, "CopyDisc", audio_cd && (priv->album->n_tracks > 0));
+	set_sensitive (window, "Extract", audio_cd && (window->priv->album->n_tracks > 0));
+	set_sensitive (window, "CopyDisc", audio_cd && (window->priv->album->n_tracks > 0));
 	set_sensitive (window, "Properties", audio_cd);
 	set_sensitive (window, "PickCoverFromDisk", audio_cd);
 	set_sensitive (window, "RemoveCover", audio_cd);
@@ -283,20 +254,20 @@ window_update_sensitivity (GooWindow *window)
 	set_sensitive (window, "Repeat", play_all);
 	set_sensitive (window, "Shuffle", play_all);
 
-	gtk_widget_set_sensitive (priv->list_view, audio_cd);
+	gtk_widget_set_sensitive (window->priv->list_view, audio_cd);
 
-	set_sensitive (window, "Eject", ! priv->hibernate);
-	set_sensitive (window, "EjectToolBar", ! priv->hibernate);
-	set_sensitive (window, "Preferences", ! priv->hibernate);
+	set_sensitive (window, "Eject", ! window->priv->hibernate);
+	set_sensitive (window, "EjectToolBar", ! window->priv->hibernate);
+	set_sensitive (window, "Preferences", ! window->priv->hibernate);
 }
 
 
 static GdkPixbuf *
 create_void_icon (GooWindow *window)
 {
-	GtkSettings  *settings;
-	int           width, height, icon_size;
-	GdkPixbuf    *icon;
+	GtkSettings *settings;
+	int          width, height, icon_size;
+	GdkPixbuf   *icon;
 
 	settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (window)));
 
@@ -317,8 +288,8 @@ get_iter_from_track_number (GooWindow   *window,
 			    int          track_number,
 			    GtkTreeIter *iter)
 {
-	GooWindowPrivateData *priv = window->priv;
-	GtkTreeModel         *model = GTK_TREE_MODEL (priv->list_store);
+
+	GtkTreeModel         *model = GTK_TREE_MODEL (window->priv->list_store);
 
 	if (! gtk_tree_model_get_iter_first (model, iter))
 		return FALSE;
@@ -332,7 +303,8 @@ get_iter_from_track_number (GooWindow   *window,
 			return TRUE;
 		}
 		track_info_unref (track);
-	} while (gtk_tree_model_iter_next (model, iter));
+	}
+	while (gtk_tree_model_iter_next (model, iter));
 
 	return FALSE;
 }
@@ -343,7 +315,7 @@ set_track_icon (GooWindow  *window,
 	       int         track_number,
 	       const char *stock_id)
 {
-	GooWindowPrivateData *priv = window->priv;
+
 	GtkTreeIter           iter;
 	GdkPixbuf            *icon;
 
@@ -357,7 +329,7 @@ set_track_icon (GooWindow  *window,
 					       NULL);
 	else
 		icon = create_void_icon (window);
-	gtk_list_store_set (priv->list_store, &iter,
+	gtk_list_store_set (window->priv->list_store, &iter,
 			    COLUMN_ICON, icon,
 			    -1);
 	g_object_unref (icon);
@@ -373,69 +345,6 @@ set_current_track_icon (GooWindow  *window,
 }
 
 
-static gboolean
-update_list_idle (gpointer callback_data)
-{
-	UpdateData           *data = callback_data;
-	GooWindow            *window = data->window;
-	GooWindowPrivateData *priv = window->priv;
-	GList                *file_list;
-	GList                *scan;
-	int                   i, n = FILES_TO_PROCESS_AT_ONCE;
-	GdkPixbuf            *icon;
-
-	if (priv->update_timeout_handle != 0) {
-		g_source_remove (priv->update_timeout_handle);
-		priv->update_timeout_handle = 0;
-	}
-
-	if (data->file_list == NULL) {
-		window_update_sensitivity (window);
-		update_data_free (data);
-		return FALSE;
-	}
-
-	file_list = data->file_list;
-	for (i = 0, scan = file_list; (i < n) && scan->next; i++) 
-		scan = scan->next;
-
-	data->file_list = scan->next;
-	scan->next = NULL;
-
-	icon = create_void_icon (window);
-
-	for (scan = file_list; scan; scan = scan->next) {
-		TrackInfo    *track = scan->data;
-		GtkTreeIter  iter;
-		char        *time_s;
-
-		gtk_list_store_prepend (priv->list_store, &iter);
-
-		time_s = get_time_string (track->length);
-		gtk_list_store_set (priv->list_store, &iter,
-				    COLUMN_TRACK_INFO, track,
-				    COLUMN_NUMBER, track->number + 1, /*FIXME*/
-				    COLUMN_ICON, icon,
-				    COLUMN_TIME, time_s,
-				    COLUMN_TITLE, track->title,
-				    COLUMN_ARTIST, track->artist,
-				    -1);
-		g_free (time_s);
-	}
-
-	g_object_unref (icon);
-
-	if (gtk_events_pending ())
-		gtk_main_iteration_do (TRUE);
-
-	g_list_free (file_list);
-
-	priv->update_timeout_handle = g_idle_add (update_list_idle, data);
-
-	return FALSE;
-}
-
-
 void
 goo_window_update (GooWindow *window)
 {
@@ -447,34 +356,51 @@ goo_window_update (GooWindow *window)
 static void
 goo_window_update_list (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-	UpdateData *udata;
-	
-	if (GTK_WIDGET_REALIZED (priv->list_view))
-		gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (priv->list_view), 0, 0);
+	GdkPixbuf *icon;
+	GList     *scan;
+
+	if (GTK_WIDGET_REALIZED (window->priv->list_view))
+		gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->priv->list_view), 0, 0);
 
 	/**/
 
-	track_info_unref (priv->current_track);	
-	priv->current_track = NULL;
+	track_info_unref (window->priv->current_track);
+	window->priv->current_track = NULL;
 
-	if (priv->playlist != NULL)
-		g_list_free (priv->playlist);
-	priv->playlist = NULL;
+	if (window->priv->playlist != NULL)
+		g_list_free (window->priv->playlist);
+	window->priv->playlist = NULL;
 
 	/**/
 
-	gtk_expander_set_expanded (GTK_EXPANDER (window->priv->list_expander), (priv->album->tracks != NULL));
+	gtk_expander_set_expanded (GTK_EXPANDER (window->priv->list_expander), (window->priv->album->tracks != NULL));
 		
 	/**/
 
-	gtk_list_store_clear (priv->list_store);
+	icon = create_void_icon (window);
+	gtk_list_store_clear (window->priv->list_store);
+	for (scan = window->priv->album->tracks; scan; scan = scan->next) {
+		TrackInfo   *track = scan->data;
+		GtkTreeIter  iter;
+		char        *time_s;
+
+		gtk_list_store_prepend (window->priv->list_store, &iter);
 
-	udata = g_new0 (UpdateData, 1);
-	udata->window = window;
-	if (priv->album->tracks != NULL)
-		udata->file_list = g_list_copy (priv->album->tracks);
-	update_list_idle (udata);
+		time_s = _g_format_duration_for_display (track->length * 1000);
+		gtk_list_store_set (window->priv->list_store, &iter,
+				    COLUMN_TRACK_INFO, track,
+				    COLUMN_NUMBER, track->number + 1,
+				    COLUMN_ICON, icon,
+				    COLUMN_TIME, time_s,
+				    COLUMN_TITLE, track->title,
+				    COLUMN_ARTIST, track->artist,
+				    -1);
+		g_free (time_s);
+	}
+
+	window_update_sensitivity (window);
+
+	g_object_unref (icon);
 }
 
 
@@ -484,9 +410,8 @@ goo_window_update_list (GooWindow *window)
 static void
 goo_window_update_titles (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-	GtkTreeModel         *model = GTK_TREE_MODEL (priv->list_store);
-	GtkTreeIter           iter;
+	GtkTreeModel *model = GTK_TREE_MODEL (window->priv->list_store);
+	GtkTreeIter   iter;
 
 	if (! gtk_tree_model_get_iter_first (model, &iter))
 		return;
@@ -496,28 +421,28 @@ goo_window_update_titles (GooWindow *window)
 		TrackInfo *new_track;
 		
 		gtk_tree_model_get (model, &iter, COLUMN_TRACK_INFO, &track, -1);
-		new_track = album_info_get_track (priv->album, track->number);
+		new_track = album_info_get_track (window->priv->album, track->number);
 		track_info_unref (track);
 		
 		if (new_track == NULL)
 			continue;
 
-		gtk_list_store_set (priv->list_store, &iter,
+		gtk_list_store_set (window->priv->list_store, &iter,
 				    COLUMN_TRACK_INFO, new_track,
 				    COLUMN_TITLE, new_track->title,
 				    COLUMN_ARTIST, new_track->artist,
 				    -1);
 
 		/* Update the current track info. */
-		if ((priv->current_track != NULL) 
-		    && (new_track->number == priv->current_track->number)) {
-			track_info_unref (priv->current_track);
+		if ((window->priv->current_track != NULL)
+		    && (new_track->number == window->priv->current_track->number)) {
+			track_info_unref (window->priv->current_track);
 			track_info_ref (new_track);
-			priv->current_track = new_track;
+			window->priv->current_track = new_track;
 		}
 		track_info_unref (new_track);
-
-	} while (gtk_tree_model_iter_next (model, &iter));
+	}
+	while (gtk_tree_model_iter_next (model, &iter));
 }
 
 
@@ -534,53 +459,63 @@ goo_window_finalize (GObject *object)
 			eel_gconf_notification_remove (window->priv->cnxn_id[i]);
 
 	if (window->priv != NULL) {
-		GooWindowPrivateData *priv = window->priv;
+#ifdef ENABLE_MEDIA_KEYS
+		if (window->priv->media_keys_proxy != NULL) {
+			dbus_g_proxy_call (window->priv->media_keys_proxy,
+					   "ReleaseMediaPlayerKeys",
+					   NULL,
+					   G_TYPE_STRING, "goobox",
+					   G_TYPE_INVALID,
+					   G_TYPE_INVALID);
+			g_object_unref (window->priv->media_keys_proxy);
+		}
+#endif /* ENABLE_MEDIA_KEYS */
 
 		/* Save preferences */
 		
-		eel_gconf_set_integer (PREF_GENERAL_VOLUME, (int) (goo_player_get_volume (priv->player) * 100.0));
-		
+		eel_gconf_set_integer (PREF_GENERAL_VOLUME, (int) (goo_player_get_audio_volume (window->priv->player) * 100.0));
+
 		/**/
 
-		gtk_object_destroy (GTK_OBJECT (priv->tooltips));
-		g_object_unref (priv->list_store);
+		gtk_object_destroy (GTK_OBJECT (window->priv->tooltips));
+		g_object_unref (window->priv->list_store);
 			
-		if (priv->tray_tooltip_delay != 0) {
-			g_source_remove (priv->tray_tooltip_delay);
-			priv->tray_tooltip_delay = 0;
+		if (window->priv->tray_tooltip_delay != 0) {
+			g_source_remove (window->priv->tray_tooltip_delay);
+			window->priv->tray_tooltip_delay = 0;
 		}
 		
-		if (priv->next_timeout_handle != 0) {
-			g_source_remove (priv->next_timeout_handle);
-			priv->next_timeout_handle = 0;
+		if (window->priv->next_timeout_handle != 0) {
+			g_source_remove (window->priv->next_timeout_handle);
+			window->priv->next_timeout_handle = 0;
 		}
 		
-		if (priv->update_timeout_handle != 0) {
-			g_source_remove (priv->update_timeout_handle);
-			priv->update_timeout_handle = 0;
+		if (window->priv->update_timeout_handle != 0) {
+			g_source_remove (window->priv->update_timeout_handle);
+			window->priv->update_timeout_handle = 0;
 		}
 	
-		if (priv->playlist != NULL)
-			g_list_free (priv->playlist);
+		if (window->priv->playlist != NULL)
+			g_list_free (window->priv->playlist);
 
-		if (priv->file_popup_menu != NULL) {
-			gtk_widget_destroy (priv->file_popup_menu);
-			priv->file_popup_menu = NULL;
+		if (window->priv->file_popup_menu != NULL) {
+			gtk_widget_destroy (window->priv->file_popup_menu);
+			window->priv->file_popup_menu = NULL;
 		}
 
-		if (priv->cover_popup_menu != NULL) {
-			gtk_widget_destroy (priv->cover_popup_menu);
-			priv->cover_popup_menu = NULL;
+		if (window->priv->cover_popup_menu != NULL) {
+			gtk_widget_destroy (window->priv->cover_popup_menu);
+			window->priv->cover_popup_menu = NULL;
 		}
 
-		g_signal_handlers_disconnect_by_data (priv->player, window);
-		g_object_unref (priv->player);
+		g_signal_handlers_disconnect_by_data (window->priv->player, window);
+		g_object_unref (window->priv->player);
 
-		track_info_unref (priv->current_track);
-		album_info_unref (priv->album);
+		track_info_unref (window->priv->current_track);
+		album_info_unref (window->priv->album);
 
-		path_list_free (priv->url_list);
-		priv->url_list = NULL;
+		_g_string_list_free (window->priv->url_list);
+		window->priv->url_list = NULL;
 
 		window->priv = NULL;
 	}
@@ -797,8 +732,8 @@ static int
 get_track_number_from_position (GooWindow   *window,
 				int          pos)
 {
-	GooWindowPrivateData *priv = window->priv;
-	GtkTreeModel         *model = GTK_TREE_MODEL (priv->list_store);
+
+	GtkTreeModel         *model = GTK_TREE_MODEL (window->priv->list_store);
 	GtkTreeIter           iter;
 	int                   i = 0;
 
@@ -814,7 +749,8 @@ get_track_number_from_position (GooWindow   *window,
 		if (i == pos)
 			return n;
 		i++;
-	} while (gtk_tree_model_iter_next (model, &iter));
+	}
+	while (gtk_tree_model_iter_next (model, &iter));
 
 	return -1;
 }
@@ -824,8 +760,8 @@ static int
 get_position_from_track_number (GooWindow   *window,
 				int          track_number)
 {
-	GooWindowPrivateData *priv = window->priv;
-	GtkTreeModel         *model = GTK_TREE_MODEL (priv->list_store);
+
+	GtkTreeModel         *model = GTK_TREE_MODEL (window->priv->list_store);
 	GtkTreeIter           iter;
 	int                   pos = 0;
 
@@ -852,31 +788,31 @@ create_playlist (GooWindow *window,
 		 gboolean   play_all,
 		 gboolean   shuffle)
 {
-	GooWindowPrivateData *priv = window->priv;
+
 	GList *playlist;
 	int    pos = 0, i;
 
 	debug (DEBUG_INFO, "PLAY ALL: %d\n", play_all);
 	debug (DEBUG_INFO, "SHUFFLE: %d\n", shuffle);
 
-	if (priv->playlist != NULL)
-		g_list_free (priv->playlist);
-	priv->playlist = NULL;
+	if (window->priv->playlist != NULL)
+		g_list_free (window->priv->playlist);
+	window->priv->playlist = NULL;
 
 	if (!play_all) 
 		return;
 
 	playlist = NULL;
 
-	if (priv->current_track != NULL)
-		pos = get_position_from_track_number (window, priv->current_track->number);
+	if (window->priv->current_track != NULL)
+		pos = get_position_from_track_number (window, window->priv->current_track->number);
 
-	for (i = 0; i < priv->album->n_tracks; i++, pos = (pos + 1) % priv->album->n_tracks) {
+	for (i = 0; i < window->priv->album->n_tracks; i++, pos = (pos + 1) % window->priv->album->n_tracks) {
 		int track_number;
 		
 		track_number = get_track_number_from_position (window, pos);
-		if ((priv->current_track != NULL) 
-		    && (priv->current_track->number == track_number))
+		if ((window->priv->current_track != NULL)
+		    && (window->priv->current_track->number == track_number))
 			continue;
 		playlist = g_list_prepend (playlist, GINT_TO_POINTER (track_number));
 	}
@@ -901,7 +837,7 @@ create_playlist (GooWindow *window,
 		g_rand_free (grand);
 	} 
 
-	priv->playlist = playlist;
+	window->priv->playlist = playlist;
 }
 
 
@@ -909,11 +845,9 @@ static void
 play_track (GooWindow *window,
 	    int        track_number)
 {
-	GooWindowPrivateData *priv = window->priv;
-
-	if (!GTK_WIDGET_VISIBLE (window))
-		priv->notify_action = TRUE;
-	goo_player_seek_track (priv->player, track_number);
+	if (! GTK_WIDGET_VISIBLE (window))
+		window->priv->notify_action = TRUE;
+	goo_player_seek_track (window->priv->player, track_number);
 }
 
 
@@ -1045,8 +979,9 @@ first_time_idle (gpointer callback_data)
 {
 	GooWindow *window = callback_data;
 
-	g_source_remove (window->priv->first_timeout_handle);
-	
+	g_source_remove (window->priv->first_time_event);
+	window->priv->first_time_event = 0;
+
 	goo_player_update (window->priv->player);
 
 	return FALSE;
@@ -1072,8 +1007,49 @@ goo_window_show (GtkWidget *widget)
 	set_active (window, "ViewStatusbar", view_foobar);
 	goo_window_set_statusbar_visibility (window, view_foobar);
 
-	if (window->priv->first_timeout_handle == 0)
-		window->priv->first_timeout_handle = g_timeout_add (IDLE_TIMEOUT, first_time_idle, window);
+	if (window->priv->first_time_event == 0)
+		window->priv->first_time_event = g_timeout_add (IDLE_TIMEOUT, first_time_idle, window);
+}
+
+
+static gboolean
+check_state_for_closing_cb (gpointer data)
+{
+	GooWindow *self = data;
+
+	g_source_remove (self->priv->check_id);
+	self->priv->check_id = 0;
+
+	if (! goo_player_get_is_busy (self->priv->player)) {
+		gtk_widget_destroy (GTK_WIDGET (self));
+		return FALSE;
+	}
+
+	self->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
+					      check_state_for_closing_cb,
+					      self);
+
+	return FALSE;
+}
+
+
+static void
+goo_window_real_close (GthWindow *base)
+{
+	GooWindow *self = GOO_WINDOW (base);
+
+	self->priv->exiting = TRUE;
+	if (goo_player_get_is_busy (self->priv->player)) {
+		gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE);
+
+		if (self->priv->check_id != 0)
+			g_source_remove (self->priv->check_id);
+		self->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE,
+						      check_state_for_closing_cb,
+						      self);
+	}
+	else
+		gtk_widget_destroy (GTK_WIDGET (self));
 }
 
 
@@ -1082,14 +1058,20 @@ goo_window_class_init (GooWindowClass *class)
 {
 	GObjectClass   *gobject_class;
 	GtkWidgetClass *widget_class;
+	GthWindowClass *window_class;
 
-	widget_class = (GtkWidgetClass*) class;
-	gobject_class = (GObjectClass*) class;
+	g_type_class_add_private (class, sizeof (GooWindowPrivate));
 
+	gobject_class = (GObjectClass*) class;
 	gobject_class->finalize = goo_window_finalize;
+
+	widget_class = (GtkWidgetClass*) class;
 	widget_class->unrealize = goo_window_unrealize;
-	widget_class->show      = goo_window_show;
+	widget_class->show = goo_window_show;
 	
+	window_class = (GthWindowClass *) class;
+	window_class->close = goo_window_real_close;
+
 	goo_window_signals[UPDATE_COVER] =
                 g_signal_new ("update-cover",
 			      G_TYPE_FROM_CLASS (class),
@@ -1099,7 +1081,6 @@ goo_window_class_init (GooWindowClass *class)
 			      goo_marshal_VOID__VOID,
 			      G_TYPE_NONE, 
 			      0);
-	g_type_class_add_private (class, sizeof (GooWindowPrivateData));
 }
 
 
@@ -1266,7 +1247,7 @@ get_action_name (GooPlayerAction action)
 		name = "METADATA";
 		break;
 	default:
-		name = "WHAT?";
+		name = "???";
 		break;
 	}
 
@@ -1283,14 +1264,14 @@ set_action_label_and_icon (GooWindow  *window,
 			   const char *action_prefix,
 			   ...)
 {
-	GooWindowPrivateData *priv = window->priv;
+
 	va_list args;
 
 	va_start (args, action_prefix);
 
 	while (action_prefix != NULL) {
 		char      *path = g_strconcat (action_prefix, action_name, NULL);
-		GtkAction *action = gtk_ui_manager_get_action (priv->ui, path);
+		GtkAction *action = gtk_ui_manager_get_action (window->priv->ui, path);
 		
 		if (action != NULL)
 			g_object_set (G_OBJECT (action), 
@@ -1314,8 +1295,6 @@ player_start_cb (GooPlayer       *player,
 		 GooPlayerAction  action,
 		 GooWindow       *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-
 	debug (DEBUG_INFO, "START [%s]\n", get_action_name (action));
 
 	switch (action) {
@@ -1328,32 +1307,32 @@ player_start_cb (GooPlayer       *player,
 					   "/MenuBar/CDMenu/",
 					   NULL);
 
-#ifdef HAVE_LIBNOTIFY
+#ifdef ENABLE_NOTIFICATION
 
-		if (priv->notify_action) {
+		if (window->priv->notify_action) {
 			GString *info = g_string_new ("");
 
-			if (priv->album->title != NULL) {
-				char *e_album = g_markup_escape_text (priv->album->title, -1);
+			if (window->priv->album->title != NULL) {
+				char *e_album = g_markup_escape_text (window->priv->album->title, -1);
 				
 				g_string_append_printf (info, "<i>%s</i>", e_album);
 				g_free (e_album);
 			}
 			g_string_append (info, "\n");
-			if (priv->album->artist != NULL) {
-				char *e_artist = g_markup_escape_text (priv->album->artist, -1);
+			if (window->priv->album->artist != NULL) {
+				char *e_artist = g_markup_escape_text (window->priv->album->artist, -1);
 				
 				g_string_append_printf (info, "<big>%s</big>", e_artist);
 				g_free (e_artist);
 			}
 			system_notify (window,
-				       priv->current_track->title,
+				       window->priv->current_track->title,
 				       info->str);
 			g_string_free (info, TRUE);
-			priv->notify_action = FALSE;
+			window->priv->notify_action = FALSE;
 		}
 		
-#endif /* HAVE_LIBNOTIFY */
+#endif /* ENABLE_NOTIFICATION */
 
 		break;
 	default:
@@ -1365,15 +1344,14 @@ player_start_cb (GooPlayer       *player,
 static void
 goo_window_select_current_track (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-	GtkTreeSelection     *selection;
-	GtkTreePath          *path;
-	int                   pos;
+	GtkTreeSelection *selection;
+	GtkTreePath      *path;
+	int               pos;
 
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
 	gtk_tree_selection_unselect_all (selection);
 
-	pos = get_position_from_track_number (window, priv->current_track->number);
+	pos = get_position_from_track_number (window, window->priv->current_track->number);
 	if (pos == -1)
 		return;
 
@@ -1387,11 +1365,10 @@ static gboolean
 next_time_idle (gpointer callback_data)
 {
 	GooWindow *window = callback_data;
-	GooWindowPrivateData *priv = window->priv;
 
-	if (priv->next_timeout_handle != 0) {
-		g_source_remove (priv->next_timeout_handle);
-		priv->next_timeout_handle = 0;
+	if (window->priv->next_timeout_handle != 0) {
+		g_source_remove (window->priv->next_timeout_handle);
+		window->priv->next_timeout_handle = 0;
 	}
 
 	play_next_track_in_playlist (window);
@@ -1420,40 +1397,33 @@ goo_window_set_current_track (GooWindow *window,
 static void
 window_update_title (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-	GooPlayerState state;
-	gboolean       playing;
-	gboolean       paused;
-	gboolean       stopped;
-	GString       *title;
+	GooPlayerState  state;
+	gboolean        playing;
+	gboolean        paused;
+	gboolean        stopped;
+	GString        *title;
 
-	state   = goo_player_get_state (priv->player);
+	state   = goo_player_get_state (window->priv->player);
 	playing = state == GOO_PLAYER_STATE_PLAYING;
 	paused  = state == GOO_PLAYER_STATE_PAUSED;
 	stopped = state == GOO_PLAYER_STATE_STOPPED;
 
 	title = g_string_new ("");
 
-	if (priv->current_track != NULL) {
-		g_string_append (title, priv->current_track->title);
-		if (priv->current_track->artist != NULL) {
+	if (window->priv->current_track != NULL) {
+		g_string_append (title, window->priv->current_track->title);
+		if (window->priv->current_track->artist != NULL) {
 			g_string_append (title, " - ");
-			g_string_append (title, priv->current_track->artist);
+			g_string_append (title, window->priv->current_track->artist);
 		}
-		/*g_string_append_printf (title, 
+		g_string_append_printf (title,
 					"  [%d/%d]",
-					priv->current_track->number + 1,
-					priv->album->n_tracks);
+					window->priv->current_track->number + 1,
+					window->priv->album->n_tracks);
 		if (paused)
 			g_string_append_printf (title, 
 						" [%s]",
-						_("Paused"));*/
-	} 
-	else if (state == GOO_PLAYER_STATE_ERROR) {
-		GError *error = goo_player_get_error (priv->player);
-		
-		g_string_append (title, error->message);
-		g_error_free (error);
+						_("Paused"));
 	} 
 	else if (state == GOO_PLAYER_STATE_NO_DISC) {
 		g_string_append (title, _("No Disc"));
@@ -1462,20 +1432,20 @@ window_update_title (GooWindow *window)
 		g_string_append (title, _("Data Disc"));
 	}
 	else {
-		if ((priv->album->title == NULL) || (priv->album->artist == NULL)) 
+		if ((window->priv->album->title == NULL) || (window->priv->album->artist == NULL))
 			g_string_append (title, _("Audio CD"));
 		else {
-			g_string_append (title, priv->album->title);
+			g_string_append (title, window->priv->album->title);
 			g_string_append (title, " - ");
-			g_string_append (title, priv->album->artist);
+			g_string_append (title, window->priv->album->artist);
 		}
 	}
 
 	gtk_window_set_title (GTK_WINDOW (window), title->str);
 
-	/*if (priv->tray_tips != NULL) {
-		gtk_tooltips_set_tip (GTK_TOOLTIPS (priv->tray_tips), 
-				      priv->tray_box, 
+	/* FIXME if (window->priv->tray_tips != NULL) {
+		gtk_tooltips_set_tip (GTK_TOOLTIPS (window->priv->tray_tips),
+				      window->priv->tray_box,
 				      title->str,
 				      NULL);
 	}*/
@@ -1489,24 +1459,19 @@ goo_window_get_cover_filename (GooWindow *window)
 {
 	const char *discid;
 	char       *filename;
-	char       *dir;
+	char       *path;
 	
 	discid = goo_player_get_discid (window->priv->player);
 	if (discid == NULL)
 		return NULL;
 
-	dir = g_build_filename (g_get_home_dir (), 
-				".gnome2", 
-				"goobox.d",
-				"covers", 
-			   	NULL);
-	ensure_dir_exists (dir, 0700);
-	filename = g_strconcat (dir, G_DIR_SEPARATOR_S, 
-				discid, ".png", 
-				NULL);
-	g_free (dir);
+	filename = g_strconcat (discid, ".png", NULL);
+	gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, "goobox", "covers", filename, NULL);
+	path = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "covers", filename, NULL);
+
+	g_free (filename);
 	
-	return filename;
+	return path;
 }
 
 
@@ -1520,61 +1485,29 @@ goo_window_update_cover (GooWindow *window)
 }
 
 
-static char*
-get_config_filename (void)
-{
-	char *config_dirname;
-	char *config_filename;
-
-	config_dirname = g_build_filename (g_get_home_dir (),
-					   ".gnome2",
-					   "goobox.d",
-					   NULL);
-	ensure_dir_exists (config_dirname, 0700);
-	config_filename = g_build_filename (config_dirname,
-					    "config",
-					    NULL);
-	g_free (config_dirname);
-
-	return config_filename;
-}
-
-
 static void
 save_config_file (GKeyFile *kv_file,
 		  char     *config_filename)
 {
-	GnomeVFSURI    *uri;
-	GnomeVFSHandle *handle;
-	GnomeVFSResult  result;
-	GError         *error = NULL;
-	char           *buffer;
-	gsize           buffer_size;
-
-	uri = new_uri_from_path (config_filename);
-	result = gnome_vfs_create_uri (&handle, 
-				       uri,
-				       GNOME_VFS_OPEN_WRITE,
-				       FALSE,
-				       0600);
-	gnome_vfs_uri_unref (uri);
-
-	if (result != GNOME_VFS_OK)
-		return;
+	GFile *file;
+	char  *buffer;
+	gsize  buffer_size;
 
+	file = g_file_new_for_path (config_filename);
 	buffer = g_key_file_to_data (kv_file,
 				     &buffer_size,
-				     &error);
-	if (error == NULL) 
-		gnome_vfs_write (handle,
-				 buffer,
-				 buffer_size,
-				 NULL);
-	else
-		g_error_free (error);
+				     NULL);
+	if (buffer != NULL)
+		g_write_file (file,
+			      FALSE,
+			      G_FILE_CREATE_NONE,
+			      buffer,
+			      buffer_size,
+			      NULL,
+			      NULL);
 
 	g_free (buffer);
-	gnome_vfs_close (handle);
+	g_object_unref (file);
 }
 
 
@@ -1588,7 +1521,7 @@ goo_window_set_current_cd_autofetch (GooWindow *window,
 
 	kv_file = g_key_file_new ();
 
-	config_filename = get_config_filename ();
+	config_filename = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
 	g_key_file_load_from_file (kv_file, 
 				   config_filename, 
 				   G_KEY_FILE_NONE,
@@ -1599,6 +1532,7 @@ goo_window_set_current_cd_autofetch (GooWindow *window,
 				discid,
 				autofetch);
 
+	gth_user_dir_make_dir_for_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
 	save_config_file (kv_file, config_filename);
 
 	g_free (config_filename);
@@ -1609,34 +1543,35 @@ goo_window_set_current_cd_autofetch (GooWindow *window,
 gboolean
 goo_window_get_current_cd_autofetch (GooWindow *window)
 {
-	GKeyFile   *kv_file;
-	char       *config_filename;
-	const char *discid;
-	gboolean    value = FALSE;
-	GError     *error = NULL;
+	GKeyFile *kv_file;
+	char     *config_filename;
+	gboolean  autofetch = FALSE;
 
 	kv_file = g_key_file_new ();
-
-	config_filename = get_config_filename ();
-	g_key_file_load_from_file (kv_file, 
-				   config_filename, 
-				   G_KEY_FILE_NONE,
-				   NULL);
-	discid = goo_player_get_discid (window->priv->player);
-
-	value = g_key_file_get_boolean (kv_file,
-					CONFIG_KEY_AUTOFETCH_GROUP,
-					discid,
-					&error);
-	if (error != NULL) {
-		value = TRUE;
-		g_error_free (error);
+	config_filename = gth_user_dir_get_file (GTH_DIR_CONFIG, "goobox", "config", NULL);
+	if (g_key_file_load_from_file (kv_file,
+				       config_filename,
+				       G_KEY_FILE_NONE,
+				       NULL))
+	{
+		const char *discid;
+		GError     *error = NULL;
+
+		discid = goo_player_get_discid (window->priv->player);
+		autofetch = g_key_file_get_boolean (kv_file,
+					            CONFIG_KEY_AUTOFETCH_GROUP,
+					            discid,
+					            &error);
+		if (error != NULL) {
+			autofetch = TRUE;
+			g_error_free (error);
+		}
 	}
 
 	g_free (config_filename);
 	g_key_file_free (kv_file);
 
-	return value;
+	return autofetch;
 }
 
 
@@ -1668,6 +1603,7 @@ auto_fetch_cover_image (GooWindow *window)
 		return;
 	}
 	g_free (filename);
+
 	if (window->priv->album->asin != NULL) 
 		fetch_cover_image_from_asin (window, window->priv->album->asin);
 	else if ((window->priv->album->title != NULL) && (window->priv->album->artist != NULL))
@@ -1704,8 +1640,6 @@ player_done_cb (GooPlayer       *player,
 		GError          *error,
 		GooWindow       *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-
 	debug (DEBUG_INFO, "DONE [%s]\n", get_action_name (action));
 
 	switch (action) {
@@ -1715,12 +1649,12 @@ player_done_cb (GooPlayer       *player,
 		goo_window_update_cover (window);
 		window_update_title (window);
 		set_current_track_icon (window, NULL);
-		if (AutoPlay || (priv->ejected && eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE))) {
+		if (AutoPlay || (window->priv->ejected && eel_gconf_get_boolean (PREF_GENERAL_AUTOPLAY, TRUE))) {
 			AutoPlay = FALSE;
 			g_timeout_add (AUTOPLAY_DELAY, autoplay_cb, window);
 		}
 		if (goo_player_get_state (player) >= GOO_PLAYER_STATE_NO_DISC)
-			priv->ejected = FALSE;
+			window->priv->ejected = FALSE;
 		break;
 
 	case GOO_PLAYER_ACTION_METADATA:
@@ -1732,7 +1666,7 @@ player_done_cb (GooPlayer       *player,
 		break;
 		
 	case GOO_PLAYER_ACTION_SEEK_SONG:
-		goo_window_set_current_track (window, goo_player_get_current_track (priv->player));
+		goo_window_set_current_track (window, goo_player_get_current_track (window->priv->player));
 		goo_window_select_current_track (window);
 		set_current_track_icon (window, GTK_STOCK_MEDIA_PLAY);
 		break;
@@ -1749,14 +1683,12 @@ player_done_cb (GooPlayer       *player,
 					   NULL);
 		if (action == GOO_PLAYER_ACTION_PLAY) {
 			set_current_track_icon (window, GTK_STOCK_MEDIA_PLAY);
-			priv->next_timeout_handle = g_idle_add (next_time_idle, window);
+			window->priv->next_timeout_handle = g_idle_add (next_time_idle, window);
 		} 
 		else if (action == GOO_PLAYER_ACTION_STOP) 
 			set_current_track_icon (window, GTK_STOCK_MEDIA_STOP);
-		else if (action == GOO_PLAYER_ACTION_EJECT) {
-			priv->ejected = TRUE;
-			goo_player_update (priv->player);
-		}
+		else if (action == GOO_PLAYER_ACTION_EJECT)
+			window->priv->ejected = TRUE;
 		break;
 		
 	case GOO_PLAYER_ACTION_PAUSE:
@@ -1829,10 +1761,9 @@ file_button_press_cb (GtkWidget      *widget,
 		      gpointer        data)
 {
 	GooWindow             *window = data;
-	GooWindowPrivateData  *priv = window->priv;
 	GtkTreeSelection      *selection;
 
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
 	if (selection == NULL)
 		return FALSE;
 
@@ -1843,11 +1774,11 @@ file_button_press_cb (GtkWidget      *widget,
 		GtkTreePath *path;
 		GtkTreeIter iter;
 
-		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->list_view),
+		if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view),
 						   event->x, event->y,
-						   &path, NULL, NULL, NULL)) {
-
-			if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store), &iter, path)) {
+						   &path, NULL, NULL, NULL))
+		{
+			if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), &iter, path)) {
 				gtk_tree_path_free (path);
 				return FALSE;
 			}
@@ -1857,24 +1788,26 @@ file_button_press_cb (GtkWidget      *widget,
 				gtk_tree_selection_unselect_all (selection);
 				gtk_tree_selection_select_iter (selection, &iter);
 			}
-
-		} else
+		}
+		else
 			gtk_tree_selection_unselect_all (selection);
 
-		gtk_menu_popup (GTK_MENU (priv->file_popup_menu),
+		gtk_menu_popup (GTK_MENU (window->priv->file_popup_menu),
 				NULL, NULL, NULL, 
 				window, 
 				event->button,
 				event->time);
 		return TRUE;
-
-	} else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) {
+	}
+	else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) {
 		GtkTreePath *path;
 
-		if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->list_view),
+		if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view),
 						     event->x, event->y,
 						     &path, NULL, NULL, NULL))
+		{
 			gtk_tree_selection_unselect_all (selection);
+		}
 	}
 
 	return FALSE;
@@ -1915,39 +1848,36 @@ static void
 sort_column_changed_cb (GtkTreeSortable *sortable,
 			gpointer         user_data)
 {
-	GooWindow *window = user_data;
-	GooWindowPrivateData  *priv = window->priv;
-	GtkSortType    order;
-	int            column_id;
-	GooPlayerState state;
+	GooWindow      *window = user_data;
+	GtkSortType     order;
+	int             column_id;
+	GooPlayerState  state;
 
 	if (! gtk_tree_sortable_get_sort_column_id (sortable, 
 						    &column_id, 
 						    &order))
+	{
 		return;
+	}
 
-	priv->sort_method = get_sort_method_from_column (column_id);
-	priv->sort_type = order;
-
-	state = goo_player_get_state (priv->player);
+	window->priv->sort_method = get_sort_method_from_column (column_id);
+	window->priv->sort_type = order;
 
 	/* Recreate the playlist if not already playing. */
-	if ((state != GOO_PLAYER_STATE_PLAYING) &&  (state != GOO_PLAYER_STATE_PAUSED)) { 
-		gboolean   play_all;
-		gboolean   shuffle;
-		
-		play_all = eel_gconf_get_boolean (PREF_PLAYLIST_PLAYALL, TRUE);
-		shuffle = eel_gconf_get_boolean (PREF_PLAYLIST_SHUFFLE, FALSE);
-		create_playlist (window, play_all, shuffle);
-	}
+
+	state = goo_player_get_state (window->priv->player);
+	if ((state != GOO_PLAYER_STATE_PLAYING) &&  (state != GOO_PLAYER_STATE_PAUSED))
+		create_playlist (window,
+				 eel_gconf_get_boolean (PREF_PLAYLIST_PLAYALL, TRUE),
+				 eel_gconf_get_boolean (PREF_PLAYLIST_SHUFFLE, FALSE));
 }
 
 
 static void
 update_ui_from_expander_state (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-	GtkExpander          *expander = GTK_EXPANDER (priv->list_expander);
+
+	GtkExpander *expander = GTK_EXPANDER (window->priv->list_expander);
 
 	if (gtk_expander_get_expanded (expander)) {
 		gtk_expander_set_label (expander, _(HIDE_TRACK_LIST));
@@ -1955,16 +1885,16 @@ update_ui_from_expander_state (GooWindow *window)
 			gtk_window_resize (GTK_WINDOW (window), 
 					   eel_gconf_get_integer (PREF_UI_WINDOW_WIDTH, DEFAULT_WIN_WIDTH),
 					   eel_gconf_get_integer (PREF_UI_WINDOW_HEIGHT, DEFAULT_WIN_HEIGHT));
-		gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->statusbar), TRUE);
-		/*gtk_widget_show (priv->list_view->parent);*/
+		gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE);
+		/*gtk_widget_show (window->priv->list_view->parent);*/
 		gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
 	} 
 	else {
 		if (GTK_WIDGET_REALIZED (window))
 			save_window_size (window);
-		gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->statusbar), FALSE); 
+		gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), FALSE);
 		gtk_expander_set_label (expander, _(SHOW_TRACK_LIST));
-		/*gtk_widget_hide (priv->list_view->parent);*/
+		/*gtk_widget_hide (window->priv->list_view->parent);*/
 		gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
 	}
 }
@@ -1979,7 +1909,6 @@ list_expander_expanded_cb (GtkExpander *expander,
 	eel_gconf_set_boolean (PREF_UI_PLAYLIST, gtk_expander_get_expanded (expander));
 }
 
-
 static void
 player_info_skip_to_cb (GooPlayerInfo *info,
 			int            seconds,
@@ -2059,20 +1988,21 @@ window_key_press_cb (GtkWidget   *widget,
 }
 
 
+#if 0
 static void
 tray_object_destroyed (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
 
-	gtk_object_unref (GTK_OBJECT (priv->tray_tips));
-	priv->tray_tips = NULL;
 
-	if (priv->tray_popup_menu != NULL) {
-		gtk_widget_destroy (priv->file_popup_menu);
-		priv->file_popup_menu = NULL;
+	gtk_object_unref (GTK_OBJECT (window->priv->tray_tips));
+	window->priv->tray_tips = NULL;
+
+	if (window->priv->tray_popup_menu != NULL) {
+		gtk_widget_destroy (window->priv->file_popup_menu);
+		window->priv->file_popup_menu = NULL;
 	}
 
-	priv->tray = NULL;
+	window->priv->tray = NULL;
 }
 
 
@@ -2098,10 +2028,12 @@ tray_icon_pressed (GtkWidget   *widget,
 	if ((event->keyval == GDK_space) 
 	    || (event->keyval == GDK_KP_Space) 
 	    || (event->keyval == GDK_Return)
-	    || (event->keyval == GDK_KP_Enter)) {
+	    || (event->keyval == GDK_KP_Enter))
+	{
 		goo_window_toggle_visibility (window);
 		return TRUE;
-	} else 
+	}
+	else
 		return FALSE;
 }
 
@@ -2199,13 +2131,14 @@ tray_icon_expose (GtkWidget      *widget,
 
 	return FALSE;
 }
+#endif
 
 
 static void
 volume_button_changed_cb (GooVolumeToolButton *button,
 			  GooWindow           *window)
 {
-	goo_player_set_volume (window->priv->player, goo_volume_tool_button_get_volume (button));
+	goo_player_set_audio_volume (window->priv->player, goo_volume_tool_button_get_volume (button));
 }
 
 
@@ -2221,23 +2154,122 @@ goo_window_init (GooWindow *window)
 }
 
 
+#ifdef ENABLE_MEDIA_KEYS
+
+
 static void
-goo_window_construct (GooWindow  *window,
-		      const char *device_path)
-{
-	GooWindowPrivateData *priv = window->priv;
-	GtkWidget            *menubar, *toolbar;
-	GtkWidget            *scrolled_window;
-	GtkWidget            *vbox;
-	GtkWidget            *hbox;
-	GtkWidget            *expander;
-	GtkTreeSelection     *selection;
-	int                   i;
-	GtkActionGroup       *actions;
-	GtkUIManager         *ui;
-	GError               *error = NULL;		
-	char                 *device;
-	
+media_player_key_pressed_cb (DBusGProxy *proxy,
+			     const char *application,
+			     const char *key,
+			     GooWindow  *window)
+{
+        if (strcmp ("goobox", application) == 0) {
+                if (strcmp ("Play", key) == 0)
+                	goo_window_toggle_play (window);
+                else if (strcmp ("Previous", key) == 0)
+                	goo_window_prev (window);
+                else if (strcmp ("Next", key) == 0)
+                	goo_window_next (window);
+                else if (strcmp ("Stop", key) == 0)
+                	goo_window_stop (window);
+        }
+}
+
+
+static gboolean
+window_focus_in_event_cb (GtkWidget     *widget,
+			  GdkEventFocus *event,
+			  gpointer       user_data)
+{
+	GooWindow *window = user_data;
+
+	dbus_g_proxy_call (window->priv->media_keys_proxy,
+			   "GrabMediaPlayerKeys",
+			   NULL,
+			   G_TYPE_STRING, "goobox",
+			   G_TYPE_UINT, 0,
+			   G_TYPE_INVALID,
+			   G_TYPE_INVALID);
+
+	return FALSE;
+}
+
+
+static void
+_goo_window_enable_media_keys (GooWindow *window)
+{
+	DBusGConnection *connection;
+
+	connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+	if (connection == NULL)
+		return;
+
+	window->priv->media_keys_proxy = dbus_g_proxy_new_for_name_owner (connection,
+									  "org.gnome.SettingsDaemon",
+									  "/org/gnome/SettingsDaemon/MediaKeys",
+									  "org.gnome.SettingsDaemon.MediaKeys",
+									  NULL);
+	if (window->priv->media_keys_proxy == NULL)
+		window->priv->media_keys_proxy = dbus_g_proxy_new_for_name_owner (connection,
+		                                                                  "org.gnome.SettingsDaemon",
+		                                                                  "/org/gnome/SettingsDaemon",
+		                                                                  "org.gnome.SettingsDaemon",
+		                                                                  NULL);
+	dbus_g_connection_unref (connection);
+
+	if (window->priv->media_keys_proxy == NULL)
+		return;
+
+	dbus_g_proxy_call (window->priv->media_keys_proxy,
+			   "GrabMediaPlayerKeys",
+			   NULL,
+			   G_TYPE_STRING, "goobox",
+			   G_TYPE_UINT, 0,
+			   G_TYPE_INVALID,
+			   G_TYPE_INVALID);
+
+        dbus_g_object_register_marshaller (goo_marshal_VOID__STRING_STRING,
+					   G_TYPE_NONE,
+					   G_TYPE_STRING,
+					   G_TYPE_STRING,
+					   G_TYPE_INVALID);
+        dbus_g_proxy_add_signal (window->priv->media_keys_proxy,
+				 "MediaPlayerKeyPressed",
+				 G_TYPE_STRING,
+				 G_TYPE_STRING,
+				 G_TYPE_INVALID);
+	dbus_g_proxy_connect_signal (window->priv->media_keys_proxy,
+				     "MediaPlayerKeyPressed",
+				     G_CALLBACK (media_player_key_pressed_cb),
+				     window,
+				     NULL);
+
+	window->priv->focus_in_event = g_signal_connect (window,
+							 "focus-in-event",
+							 G_CALLBACK (window_focus_in_event_cb),
+							 window);
+}
+
+
+#endif /* ENABLE_MEDIA_KEYS */
+
+
+static void
+goo_window_construct (GooWindow    *window,
+		      BraseroDrive *drive)
+{
+
+	GtkWidget        *menubar, *toolbar;
+	GtkWidget        *scrolled_window;
+	GtkWidget        *vbox;
+	GtkWidget        *hbox;
+	GtkWidget        *expander;
+	GtkTreeSelection *selection;
+	int               i;
+	GtkActionGroup   *actions;
+	GtkUIManager     *ui;
+	GError           *error = NULL;
+
 	g_signal_connect (G_OBJECT (window), 
 			  "delete_event",
 			  G_CALLBACK (window_delete_event_cb),
@@ -2257,67 +2289,62 @@ goo_window_construct (GooWindow  *window,
 
 	/* Create the data */
 
-	if (device_path == NULL)
-		device = eel_gconf_get_string (PREF_GENERAL_DEVICE, DEFAULT_DEVICE);
-	else
-		device = g_strdup (device_path);
-	priv->player = goo_player_new (device);
-	g_free (device);
+	window->priv->player = goo_player_new (drive);
 
-	g_signal_connect (priv->player, 
+	g_signal_connect (window->priv->player,
 			  "start",
 			  G_CALLBACK (player_start_cb), 
 			  window);
-	g_signal_connect (priv->player, 
+	g_signal_connect (window->priv->player,
 			  "done",
 			  G_CALLBACK (player_done_cb), 
 			  window);
-	g_signal_connect (priv->player, 
+	g_signal_connect (window->priv->player,
 			  "state_changed",
 			  G_CALLBACK (player_state_changed_cb), 
 			  window);
 
-	priv->playlist = NULL;
+	window->priv->playlist = NULL;
 
 	/* Create the widgets. */
 
-	priv->tooltips = gtk_tooltips_new ();
+	window->priv->tooltips = gtk_tooltips_new ();
 
 	/* * File list. */
 
-	priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, 
-					       GOO_TYPE_TRACK_INFO,
-					       G_TYPE_INT,
-					       GDK_TYPE_PIXBUF,
-					       G_TYPE_STRING,
-					       G_TYPE_STRING,
-					       G_TYPE_STRING);
-	priv->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->list_store));
-
-	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (priv->list_view), TRUE);
-	add_columns (window, GTK_TREE_VIEW (priv->list_view));
-	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (priv->list_view), TRUE);
-	gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->list_view), COLUMN_TITLE);
-
-	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->list_store),
+	window->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS,
+						       GOO_TYPE_TRACK_INFO,
+						       G_TYPE_INT,
+						       GDK_TYPE_PIXBUF,
+						       G_TYPE_STRING,
+						       G_TYPE_STRING,
+						       G_TYPE_STRING);
+	window->priv->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->priv->list_store));
+
+	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (window->priv->list_view), TRUE);
+	add_columns (window, GTK_TREE_VIEW (window->priv->list_view));
+	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (window->priv->list_view), TRUE);
+	gtk_tree_view_set_search_column (GTK_TREE_VIEW (window->priv->list_view), COLUMN_TITLE);
+
+	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
 					 COLUMN_TITLE, title_column_sort_func,
 					 NULL, NULL);
-	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->list_store),
+	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
 					 COLUMN_ARTIST, artist_column_sort_func,
 					 NULL, NULL);
-	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->list_store),
+	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store),
 					 COLUMN_TIME, time_column_sort_func,
 					 NULL, NULL);
 
-	priv->sort_method = preferences_get_sort_method ();
-	priv->sort_type = preferences_get_sort_type ();
+	window->priv->sort_method = preferences_get_sort_method ();
+	window->priv->sort_type = preferences_get_sort_type ();
 
-	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->list_store), get_column_from_sort_method (priv->sort_method), priv->sort_type);
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), get_column_from_sort_method (window->priv->sort_method), window->priv->sort_type);
 	
-	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
 
-	g_signal_connect (G_OBJECT (priv->list_view),
+	g_signal_connect (G_OBJECT (window->priv->list_view),
                           "row_activated",
                           G_CALLBACK (row_activated_cb),
                           window);
@@ -2325,48 +2352,26 @@ goo_window_construct (GooWindow  *window,
                           "changed",
                           G_CALLBACK (selection_changed_cb),
                           window);
-	g_signal_connect (G_OBJECT (priv->list_view), 
+	g_signal_connect (G_OBJECT (window->priv->list_view),
 			  "button_press_event",
 			  G_CALLBACK (file_button_press_cb), 
 			  window);
 
-	g_signal_connect (G_OBJECT (priv->list_store), 
+	g_signal_connect (G_OBJECT (window->priv->list_store),
 			  "sort_column_changed",
 			  G_CALLBACK (sort_column_changed_cb), 
 			  window);
 
-	/*
-	egg_tree_multi_drag_add_drag_support (GTK_TREE_VIEW (window->list_view));
-
-        gtk_drag_source_set (window->list_view, 
-			     GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
-			     target_table, n_targets, 
-			     GDK_ACTION_COPY);
-
-	g_signal_connect (G_OBJECT (window->list_view), 
-			  "drag_data_get",
-			  G_CALLBACK (file_list_drag_data_get), 
-			  window);
-	g_signal_connect (G_OBJECT (window->list_view), 
-			  "drag_begin",
-			  G_CALLBACK (file_list_drag_begin), 
-			  window);
-	g_signal_connect (G_OBJECT (window->list_view), 
-			  "drag_end",
-			  G_CALLBACK (file_list_drag_end), 
-			  window);
-	*/
-
 	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
 					GTK_POLICY_AUTOMATIC,
 					GTK_POLICY_AUTOMATIC);
 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
-	gtk_container_add (GTK_CONTAINER (scrolled_window), priv->list_view);
+	gtk_container_add (GTK_CONTAINER (scrolled_window), window->priv->list_view);
 
 	/* Build the menu and the toolbar. */
 
-	priv->actions = actions = gtk_action_group_new ("Actions");
+	window->priv->actions = actions = gtk_action_group_new ("Actions");
 	gtk_action_group_set_translation_domain (actions, NULL);
 	gtk_action_group_add_actions (actions, 
 				      action_entries, 
@@ -2377,7 +2382,7 @@ goo_window_construct (GooWindow  *window,
 					     n_action_toggle_entries, 
 					     window);
 
-	priv->ui = ui = gtk_ui_manager_new ();
+	window->priv->ui = ui = gtk_ui_manager_new ();
 	
 	g_signal_connect (ui, "connect_proxy",
 			  G_CALLBACK (connect_proxy_cb), window);
@@ -2388,34 +2393,18 @@ goo_window_construct (GooWindow  *window,
 	gtk_window_add_accel_group (GTK_WINDOW (window), 
 				    gtk_ui_manager_get_accel_group (ui));
 	
-	if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error)) {
+	if (! gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error)) {
 		g_message ("building menus failed: %s", error->message);
 		g_error_free (error);
 	}
 
 	menubar = gtk_ui_manager_get_widget (ui, "/MenuBar");
 	gtk_widget_show (menubar);
+	gth_window_attach (GTH_WINDOW (window), menubar, GTH_WINDOW_MENUBAR);
 
-	gnome_app_add_docked (GNOME_APP (window),
-			      menubar,
-			      "MenuBar",
-			      (BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL 
-			       | BONOBO_DOCK_ITEM_BEH_EXCLUSIVE 
-			       | (eel_gconf_get_boolean (PREF_DESKTOP_MENUBAR_DETACHABLE, TRUE) ? BONOBO_DOCK_ITEM_BEH_NORMAL : BONOBO_DOCK_ITEM_BEH_LOCKED)),
-			      BONOBO_DOCK_TOP,
-			      1, 1, 0);
-
-	priv->toolbar = toolbar = gtk_ui_manager_get_widget (ui, "/ToolBar");
+	window->priv->toolbar = toolbar = gtk_ui_manager_get_widget (ui, "/ToolBar");
 	gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE);
-
-	gnome_app_add_docked (GNOME_APP (window),
-			      toolbar,
-			      "ToolBar",
-			      (BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL 
-			       | BONOBO_DOCK_ITEM_BEH_EXCLUSIVE 
-			       | (eel_gconf_get_boolean (PREF_DESKTOP_TOOLBAR_DETACHABLE, TRUE) ? BONOBO_DOCK_ITEM_BEH_NORMAL : BONOBO_DOCK_ITEM_BEH_LOCKED)),
-			      BONOBO_DOCK_TOP,
-			      2, 1, 0);
+	gth_window_attach_toolbar (GTH_WINDOW (window), 0, window->priv->toolbar);
 
 	{
 		GtkAction *action;
@@ -2459,8 +2448,8 @@ goo_window_construct (GooWindow  *window,
 		gtk_size_group_add_widget (size_group, pause_button);		
 	}
 
-	priv->file_popup_menu = gtk_ui_manager_get_widget (ui, "/ListPopupMenu");
-	priv->cover_popup_menu = gtk_ui_manager_get_widget (ui, "/CoverPopupMenu");
+	window->priv->file_popup_menu = gtk_ui_manager_get_widget (ui, "/ListPopupMenu");
+	window->priv->cover_popup_menu = gtk_ui_manager_get_widget (ui, "/CoverPopupMenu");
 
 	/* Add the volume button to the toolbar. */
 	
@@ -2469,31 +2458,31 @@ goo_window_construct (GooWindow  *window,
 		
 		sep = gtk_separator_tool_item_new ();
 		gtk_widget_show (GTK_WIDGET (sep));	
-		gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), 
+		gtk_toolbar_insert (GTK_TOOLBAR (window->priv->toolbar),
 				    GTK_TOOL_ITEM (sep),
 				    VOLUME_BUTTON_POSITION);
 	}
 	
-	priv->volume_button = (GtkWidget*) goo_volume_tool_button_new ();
-	g_signal_connect (priv->volume_button, 
+	window->priv->volume_button = (GtkWidget*) goo_volume_tool_button_new ();
+	g_signal_connect (window->priv->volume_button,
 			  "changed",
 			  G_CALLBACK (volume_button_changed_cb), 
 			  window);
-	gtk_widget_show (GTK_WIDGET (priv->volume_button));
-	gtk_tool_item_set_is_important (GTK_TOOL_ITEM (priv->volume_button), FALSE); /*FIXME*/
-	gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), 
-			    GTK_TOOL_ITEM (priv->volume_button), 
+	gtk_widget_show (GTK_WIDGET (window->priv->volume_button));
+	gtk_tool_item_set_is_important (GTK_TOOL_ITEM (window->priv->volume_button), FALSE);
+	gtk_toolbar_insert (GTK_TOOLBAR (window->priv->toolbar),
+			    GTK_TOOL_ITEM (window->priv->volume_button),
 			    VOLUME_BUTTON_POSITION + 1);
 
 	/* Create the statusbar. */
 
-	priv->statusbar = gtk_statusbar_new ();
-	priv->help_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar), "help_message");
-	priv->list_info_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar), "list_info");
-	priv->progress_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (priv->statusbar), "progress");
-	gnome_app_set_statusbar (GNOME_APP (window), priv->statusbar);
-
-	gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->statusbar), TRUE);
+	window->priv->statusbar = gtk_statusbar_new ();
+	gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE);
+	window->priv->help_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "help_message");
+	window->priv->list_info_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "list_info");
+	window->priv->progress_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "progress");
+	gth_window_attach (GTH_WINDOW (window), window->priv->statusbar, GTH_WINDOW_STATUSBAR);
+	gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE);
 
 	/**/
 
@@ -2520,8 +2509,8 @@ goo_window_construct (GooWindow  *window,
 
 	/**/
 
-	priv->list_expander = expander = gtk_expander_new_with_mnemonic (_(HIDE_TRACK_LIST));
-	gtk_container_add (GTK_CONTAINER (priv->list_expander), scrolled_window);
+	window->priv->list_expander = expander = gtk_expander_new_with_mnemonic (_(HIDE_TRACK_LIST));
+	gtk_container_add (GTK_CONTAINER (window->priv->list_expander), scrolled_window);
 	gtk_expander_set_expanded (GTK_EXPANDER (expander), FALSE /*eel_gconf_get_boolean (PREF_UI_PLAYLIST, TRUE)*/);
 	g_signal_connect (expander,
 			  "notify::expanded",
@@ -2532,10 +2521,10 @@ goo_window_construct (GooWindow  *window,
 			    expander, 
 			    TRUE, TRUE, 0);
 
-	gnome_app_set_contents (GNOME_APP (window), vbox);
 	gtk_widget_show_all (vbox);
+	gth_window_attach_content (GTH_WINDOW (window), 0, vbox);
 
-	gtk_widget_grab_focus (priv->list_view);
+	gtk_widget_grab_focus (window->priv->list_view);
 
 	window_sync_ui_with_preferences (window);
 
@@ -2543,17 +2532,19 @@ goo_window_construct (GooWindow  *window,
 				     eel_gconf_get_integer (PREF_UI_WINDOW_WIDTH, DEFAULT_WIN_WIDTH),
 				     eel_gconf_get_integer (PREF_UI_WINDOW_HEIGHT, DEFAULT_WIN_HEIGHT));
 
-	goo_volume_tool_button_set_volume (GOO_VOLUME_TOOL_BUTTON (priv->volume_button), 
+	goo_volume_tool_button_set_volume (GOO_VOLUME_TOOL_BUTTON (window->priv->volume_button),
 					   eel_gconf_get_integer (PREF_GENERAL_VOLUME, DEFAULT_VOLUME) / 100.0,
 					   TRUE);
 
-	/* Create the tray icon. */
+	/* The tray icon. */
 	
-	priv->tray = GTK_WIDGET (egg_tray_icon_new ("Goobox"));
-	priv->tray_box = gtk_event_box_new ();
+#if 0
+	window->priv->tray = GTK_WIDGET (egg_tray_icon_new ("Goobox"));
+	window->priv->tray_box = gtk_event_box_new ();
 	
-	priv->info_popup = gtk_window_new (GTK_WINDOW_POPUP);
+	window->priv->info_popup = gtk_window_new (GTK_WINDOW_POPUP);
 	
+	/* FIXME
 	{
 		GtkWidget *out_frame;
 		GtkWidget *info;
@@ -2561,7 +2552,7 @@ goo_window_construct (GooWindow  *window,
 		out_frame = gtk_frame_new (NULL);
 		gtk_frame_set_shadow_type (GTK_FRAME (out_frame), GTK_SHADOW_OUT);
 		gtk_widget_show (out_frame);
-		gtk_container_add (GTK_CONTAINER (priv->info_popup), out_frame);
+		gtk_container_add (GTK_CONTAINER (window->priv->info_popup), out_frame);
 		
 		info = goo_player_info_new (window, FALSE);
 		gtk_container_set_border_width (GTK_CONTAINER (info), 0);
@@ -2576,140 +2567,124 @@ goo_window_construct (GooWindow  *window,
 				  window);
 		gtk_container_add (GTK_CONTAINER (out_frame), info);
 	}
+	*/
 	
-	g_signal_connect (G_OBJECT (priv->tray_box), 
+	g_signal_connect (G_OBJECT (window->priv->tray_box),
 			  "button_press_event",
 			  G_CALLBACK (tray_icon_clicked), 
 			  window);
-	g_signal_connect (G_OBJECT (priv->tray_box), 
+	g_signal_connect (G_OBJECT (window->priv->tray_box),
 			  "key_press_event",
 			  G_CALLBACK (tray_icon_pressed),
 			  window);
-	g_signal_connect (G_OBJECT (priv->tray_box), 
+	g_signal_connect (G_OBJECT (window->priv->tray_box),
 			  "enter_notify_event",
 			  G_CALLBACK (tray_icon_enter_notify_event_cb),
 			  window);
-	g_signal_connect (G_OBJECT (priv->tray_box), 
+	g_signal_connect (G_OBJECT (window->priv->tray_box),
 			  "leave_notify_event",
 			  G_CALLBACK (tray_icon_leave_notify_event_cb),
 			  window);
 
-	g_object_set_data_full (G_OBJECT (priv->tray), 
+	g_object_set_data_full (G_OBJECT (window->priv->tray),
 				"tray-action-data", 
 				window,
 				(GDestroyNotify) tray_object_destroyed);
 
-	gtk_container_add (GTK_CONTAINER (priv->tray), priv->tray_box);
-	priv->tray_icon = gtk_image_new_from_icon_name ("goobox", GTK_ICON_SIZE_BUTTON);
+	gtk_container_add (GTK_CONTAINER (window->priv->tray), window->priv->tray_box);
+	window->priv->tray_icon = gtk_image_new_from_icon_name ("goobox", GTK_ICON_SIZE_BUTTON);
 
-	g_signal_connect (G_OBJECT (priv->tray_icon), 
+	g_signal_connect (G_OBJECT (window->priv->tray_icon),
 			  "expose_event",
 			  G_CALLBACK (tray_icon_expose), 
 			  window);
 
-	GTK_WIDGET_SET_FLAGS (priv->tray_box, GTK_CAN_FOCUS);
-	atk_object_set_name (gtk_widget_get_accessible (priv->tray_box), _("CD Player"));
+	GTK_WIDGET_SET_FLAGS (window->priv->tray_box, GTK_CAN_FOCUS);
+	atk_object_set_name (gtk_widget_get_accessible (window->priv->tray_box), _("CD Player"));
 	
-	gtk_container_add (GTK_CONTAINER (priv->tray_box), priv->tray_icon);
-	priv->tray_tips = gtk_tooltips_new ();
-	gtk_object_ref (GTK_OBJECT (priv->tray_tips));
-	gtk_object_sink (GTK_OBJECT (priv->tray_tips));
-	/* gtk_tooltips_set_tip (GTK_TOOLTIPS (priv->tray_tips), 
-			      priv->tray_box, 
+	gtk_container_add (GTK_CONTAINER (window->priv->tray_box), window->priv->tray_icon);
+	window->priv->tray_tips = gtk_tooltips_new ();
+	gtk_object_ref (GTK_OBJECT (window->priv->tray_tips));
+	gtk_object_sink (GTK_OBJECT (window->priv->tray_tips));
+	/* gtk_tooltips_set_tip (GTK_TOOLTIPS (window->priv->tray_tips),
+			      window->priv->tray_box,
 			      _("CD Player"), 
 			      NULL); */
 
-	priv->tray_popup_menu = gtk_ui_manager_get_widget (ui, "/TrayPopupMenu");
-	gnome_popup_menu_attach (priv->tray_popup_menu, priv->tray_box, NULL);
-	gtk_widget_show_all (priv->tray);
+	window->priv->tray_popup_menu = gtk_ui_manager_get_widget (ui, "/TrayPopupMenu");
+	gnome_popup_menu_attach (window->priv->tray_popup_menu, window->priv->tray_box, NULL);
+	gtk_widget_show_all (window->priv->tray);
+#endif
 	
 	/* Add notification callbacks. */
 
 	i = 0;
 
-	priv->cnxn_id[i++] = eel_gconf_notification_add (
+	window->priv->cnxn_id[i++] = eel_gconf_notification_add (
 					   PREF_UI_TOOLBAR,
 					   pref_view_toolbar_changed,
 					   window);
-	priv->cnxn_id[i++] = eel_gconf_notification_add (
+	window->priv->cnxn_id[i++] = eel_gconf_notification_add (
 					   PREF_UI_STATUSBAR,
 					   pref_view_statusbar_changed,
 					   window);
-	priv->cnxn_id[i++] = eel_gconf_notification_add (
+	window->priv->cnxn_id[i++] = eel_gconf_notification_add (
 					   PREF_UI_PLAYLIST,
 					   pref_view_playlist_changed,
 					   window);
-	priv->cnxn_id[i++] = eel_gconf_notification_add (
+	window->priv->cnxn_id[i++] = eel_gconf_notification_add (
 					   PREF_PLAYLIST_PLAYALL,
 					   pref_playlist_playall_changed,
 					   window);
-	priv->cnxn_id[i++] = eel_gconf_notification_add (
+	window->priv->cnxn_id[i++] = eel_gconf_notification_add (
 					   PREF_PLAYLIST_SHUFFLE,
 					   pref_playlist_shuffle_changed,
 					   window);
-	priv->cnxn_id[i++] = eel_gconf_notification_add (
+	window->priv->cnxn_id[i++] = eel_gconf_notification_add (
 					   PREF_PLAYLIST_REPEAT,
 					   pref_playlist_repeat_changed,
 					   window);
-}
-
-GtkWindow * 
-goo_window_new (const char *device)
-{
-	GooWindow *window;
 
-	window = (GooWindow*) g_object_new (GOO_TYPE_WINDOW, NULL);
-	goo_window_construct (window, device);
+	/* Media keys*/
 
-	window_list = g_list_prepend (window_list, window);
-
-	return (GtkWindow*) window;
+#ifdef ENABLE_MEDIA_KEYS
+	_goo_window_enable_media_keys (window);
+#endif /* ENABLE_MEDIA_KEYS */
 }
 
 
-static void
-goo_window_destroy (GooWindow *window)
+GtkWidget *
+goo_window_new (BraseroDrive *drive)
 {
-	window_list = g_list_remove (window_list, window);
-	gtk_widget_destroy (GTK_WIDGET (window));
-	if (window_list == NULL)
-		bonobo_main_quit ();
-}
+	GooWindow *window;
 
+	if (drive == NULL) {
+		char *default_device;
 
-static gboolean
-check_player_state_cb (gpointer data)
-{
-	GooWindow *window = data;
+		default_device = eel_gconf_get_string (PREF_GENERAL_DEVICE, NULL);
+		if (default_device != NULL)
+			drive = main_get_drive_for_device (default_device);
+		if (drive == NULL)
+			drive = main_get_most_likely_drive ();
 
-	g_source_remove (window->priv->check_id);
+		g_free (default_device);
+	}
 
-	if (!goo_player_get_is_busy (window->priv->player)) 
-		goo_window_destroy (window);
-	else
-		window->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE, 
-							check_player_state_cb, 
-							window);
+	g_return_val_if_fail (drive != NULL, NULL);
 
-	return FALSE;
+	window = (GooWindow*) g_object_new (GOO_TYPE_WINDOW, NULL);
+	goo_window_construct (window, drive);
+
+	window_list = g_list_prepend (window_list, window);
+
+	return (GtkWidget *) window;
 }
 
 
 void
 goo_window_close (GooWindow *window)
 {
-	window->priv->exiting = TRUE;
-	if (! goo_player_get_is_busy (window->priv->player)) 
-		goo_window_destroy (window);
-	else {
-		gtk_widget_set_sensitive (GTK_WIDGET (window), FALSE);
-
-		if (window->priv->check_id != 0)
-			g_source_remove (window->priv->check_id);
-		window->priv->check_id = g_timeout_add (PLAYER_CHECK_RATE, 
-							check_player_state_cb, 
-							window);
-	}
+	gth_window_close (GTH_WINDOW (window));
 }
 
 
@@ -2742,15 +2717,16 @@ goo_window_set_statusbar_visibility (GooWindow *window,
 void
 goo_window_play (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-
 	if (window->priv->hibernate)
 		return;
 	
-	if (goo_player_get_state (priv->player) == GOO_PLAYER_STATE_PLAYING) 
+	if (! goo_player_is_audio_cd (window->priv->player))
+		return;
+
+	if (goo_player_get_state (window->priv->player) == GOO_PLAYER_STATE_PLAYING)
 		return;
 
-	if (goo_player_get_state (priv->player) != GOO_PLAYER_STATE_PAUSED) {
+	if (goo_player_get_state (window->priv->player) != GOO_PLAYER_STATE_PAUSED) {
 		gboolean  play_all;
 		gboolean  shuffle;
 		
@@ -2758,8 +2734,8 @@ goo_window_play (GooWindow *window)
 		shuffle  = eel_gconf_get_boolean (PREF_PLAYLIST_SHUFFLE, FALSE);
 		create_playlist (window, play_all, shuffle);
 
-		if (priv->current_track != NULL) 
-			play_track (window, priv->current_track->number);
+		if (window->priv->current_track != NULL)
+			play_track (window, window->priv->current_track->number);
 		else if (window->priv->playlist != NULL)
 			play_next_track_in_playlist (window);
 		else
@@ -2767,7 +2743,7 @@ goo_window_play (GooWindow *window)
 	} 
 	else {
 		set_current_track_icon (window, GTK_STOCK_MEDIA_PLAY);
-		goo_player_play (priv->player);
+		goo_player_play (window->priv->player);
 	}
 }
 
@@ -2794,33 +2770,27 @@ goo_window_play_selected (GooWindow *window)
 void
 goo_window_stop (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-
 	if (window->priv->hibernate)
 		return;
-
-	goo_player_stop (priv->player);
+	if (! goo_player_is_audio_cd (window->priv->player))
+		return;
+	goo_player_stop (window->priv->player);
 }
 
 
 void
 goo_window_pause (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-
 	if (window->priv->hibernate)
 		return;
-
-	goo_player_pause (priv->player);
+	goo_player_pause (window->priv->player);
 }
 
 
 void
 goo_window_toggle_play (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-
-	if (goo_player_get_state (priv->player) != GOO_PLAYER_STATE_PLAYING)
+	if (goo_player_get_state (window->priv->player) != GOO_PLAYER_STATE_PLAYING)
 		goo_window_play (window);
 	else
 		goo_window_pause (window);
@@ -2830,21 +2800,22 @@ goo_window_toggle_play (GooWindow *window)
 void
 goo_window_next (GooWindow *window)
 {
-	GooWindowPrivateData *priv = window->priv;
-
 	if (window->priv->album->n_tracks == 0)
 		return;
 
-	if (priv->current_track != NULL) {
+	if (! goo_player_is_audio_cd (window->priv->player))
+		return;
+
+	if (window->priv->current_track != NULL) {
 		if (window->priv->playlist == NULL) {
-			int current_track = priv->current_track->number;
+			int current_track = window->priv->current_track->number;
 			int current_pos;
 			int new_pos, new_track;
 
 			goo_window_stop (window);
 
 			current_pos = get_position_from_track_number (window, current_track);
-			new_pos = MIN (current_pos + 1, priv->album->n_tracks - 1);
+			new_pos = MIN (current_pos + 1, window->priv->album->n_tracks - 1);
 			new_track = get_track_number_from_position (window, new_pos);
 			goo_window_set_current_track (window, new_track);
 
@@ -2866,6 +2837,9 @@ goo_window_prev (GooWindow *window)
 {
 	int new_pos;
 
+	if (! goo_player_is_audio_cd (window->priv->player))
+		return;
+
 	if (window->priv->album->n_tracks == 0)
 		return;
 
@@ -2877,9 +2851,11 @@ goo_window_prev (GooWindow *window)
 		current_track = window->priv->current_track->number;
 		current_pos = get_position_from_track_number (window, current_track);
 		
+		/* FIXME
 		if (goo_player_info_get_progress (GOO_PLAYER_INFO (window->priv->info)) * window->priv->current_track->length > 4)
 			new_pos = current_pos;
 		else
+		*/
 			new_pos = MAX (current_pos - 1, 0);
 	} 
 	else
@@ -2895,27 +2871,16 @@ goo_window_eject (GooWindow *window)
 {
 	if (window->priv->hibernate)
 		return;
-
-	if (! goo_player_eject (window->priv->player)) {
-		GError *e = goo_player_get_error (window->priv->player);
-		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window), 
-						   _("Could not eject the CD"), &e);
-	}
+	goo_player_eject (window->priv->player);
 }
 
 
 void
 goo_window_set_device (GooWindow  *window,
-		       const char *device_path)
+		       const char *device)
 {
-	if (! goo_player_set_device (window->priv->player, device_path)) {
-		GError *e = goo_player_get_error (window->priv->player);
-		
-		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window), 
-						   _("Could not read drive"), &e);
-	}
-
-	if (device_path == NULL)
+	goo_player_set_device (window->priv->player, device);
+	if (device == NULL)
 		window_update_sensitivity (window);
 }
 
@@ -2929,9 +2894,7 @@ add_selected_track (GtkTreeModel *model,
 	GList    **list = data;
 	TrackInfo  *track;
 
-	gtk_tree_model_get (model, iter,
-			    COLUMN_TRACK_INFO, &track,
-			    -1);
+	gtk_tree_model_get (model, iter, COLUMN_TRACK_INFO, &track, -1);
 	*list = g_list_prepend (*list, track);
 }
 
@@ -2947,19 +2910,18 @@ GList *
 goo_window_get_tracks (GooWindow *window,
 		       gboolean   selection)
 {
-	GooWindowPrivateData *priv = window->priv;
 	GtkTreeSelection     *list_selection;
 	GList                *tracks;
 
-	if (priv->album->tracks == NULL)
+	if (window->priv->album->tracks == NULL)
 		return NULL;
 	
 	if (! selection) 
-		return track_list_dup (priv->album->tracks);
+		return track_list_dup (window->priv->album->tracks);
 
 	/* return selected track list */
 
-	list_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->list_view));
+	list_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view));
 	if (list_selection == NULL)
 		return NULL;
 
@@ -2978,34 +2940,23 @@ goo_window_get_player (GooWindow *window)
 
 
 void
-goo_window_set_cover_image (GooWindow  *window,
-			    const char *filename)
+goo_window_set_cover_image_from_pixbuf (GooWindow *window,
+					GdkPixbuf *image)
 {
-	GdkPixbuf *image;
 	GError    *error = NULL;
 	GdkPixbuf *frame;
 	char      *cover_filename;
 
-	if (window->priv->hibernate)
+	if (image == NULL)
 		return;
 
-	goo_window_set_current_cd_autofetch (window, FALSE);
-
-	image = gdk_pixbuf_new_from_file (filename, &error);
-	if (image == NULL) {
-		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
-						   _("Could not load image"),
-						   &error);
-		return;
-	}
-		
 	frame = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (image),
 				gdk_pixbuf_get_has_alpha (image),
 				gdk_pixbuf_get_bits_per_sample (image),
 				gdk_pixbuf_get_width (image) + 2,
 				gdk_pixbuf_get_height (image) + 2);
 	gdk_pixbuf_fill (frame, 0x00000000);
-	gdk_pixbuf_copy_area (image, 
+	gdk_pixbuf_copy_area (image,
 			      0, 0,
 			      gdk_pixbuf_get_width (image),
 			      gdk_pixbuf_get_height (image),
@@ -3014,16 +2965,70 @@ goo_window_set_cover_image (GooWindow  *window,
 
 	cover_filename = goo_window_get_cover_filename (window);
 	debug (DEBUG_INFO, "SAVE IMAGE %s\n", cover_filename);
-		
+
 	if (! gdk_pixbuf_save (frame, cover_filename, "png", &error, NULL))
 		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
 						   _("Could not save cover image"),
 						   &error);
+	else {
+		goo_window_set_current_cd_autofetch (window, FALSE);
+		goo_window_update_cover (window);
+	}
+
 	g_free (cover_filename);
 	g_object_unref (frame);
+}
+
+
+void
+goo_window_set_cover_image (GooWindow  *window,
+			    const char *filename)
+{
+	GdkPixbuf *image;
+	GError    *error = NULL;
+
+	if (window->priv->hibernate)
+		return;
+
+	image = gdk_pixbuf_new_from_file (filename, &error);
+	if (image == NULL) {
+		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
+						   _("Could not load image"),
+						   &error);
+		return;
+	}
+	goo_window_set_cover_image_from_pixbuf (window, image);
+
 	g_object_unref (image);
+}
+
+
+void
+goo_window_set_cover_image_from_data (GooWindow *window,
+				      void      *buffer,
+				      gsize      count)
+{
+	GInputStream *stream;
+	GdkPixbuf    *image;
+	GError       *error = NULL;
+
+	if (window->priv->hibernate)
+		return;
 
-	goo_window_update_cover (window);	
+	stream = g_memory_input_stream_new_from_data (buffer, count, NULL);
+	image = gdk_pixbuf_new_from_stream (stream, NULL, &error);
+	if (image == NULL) {
+		_gtk_error_dialog_from_gerror_run (GTK_WINDOW (window),
+						   _("Could not load image"),
+						   &error);
+		g_object_unref (stream);
+		return;
+	}
+
+	goo_window_set_cover_image_from_pixbuf (window, image);
+
+	g_object_unref (image);
+	g_object_unref (stream);
 }
 
 
@@ -3142,9 +3147,13 @@ goo_window_search_cover_on_internet (GooWindow *window)
 	debug (DEBUG_INFO, "SEARCH ON INTERNET\n");
 
 	if ((window->priv->album->title == NULL) || (window->priv->album->artist == NULL)) {
-		_gtk_error_dialog_run (GTK_WINDOW (window),
-				       _("Could not search for a cover on Internet"),
-				       _("You have to enter the artist and album names in order to find the album cover."));
+		_gtk_message_dialog_new (GTK_WINDOW (window),
+					 GTK_DIALOG_MODAL,
+					 GTK_STOCK_DIALOG_ERROR,
+				         _("Could not search for a cover on Internet"),
+				         _("You have to enter the artist and album names in order to find the album cover."),
+				         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+				         NULL);
 		return;
 	}
 
@@ -3155,7 +3164,8 @@ goo_window_search_cover_on_internet (GooWindow *window)
 void
 goo_window_remove_cover (GooWindow *window)
 {
-	char *cover_filename;
+	char  *cover_filename;
+	GFile *file;
 
 	if (window->priv->hibernate)
 		return;
@@ -3166,7 +3176,9 @@ goo_window_remove_cover (GooWindow *window)
 	if (cover_filename == NULL)
 		return;
 
-	gnome_vfs_unlink (cover_filename);
+	file = g_file_new_for_path (cover_filename);
+	g_file_delete (file, NULL, NULL);
+
 	g_free (cover_filename);
 
 	goo_window_update_cover (window);
@@ -3207,7 +3219,7 @@ goo_window_toggle_visibility (GooWindow *window)
 
 
 double
-goo_window_get_volume (GooWindow   *window)
+goo_window_get_volume (GooWindow *window)
 {
 	GooVolumeToolButton *volume_button;
 
@@ -3217,8 +3229,8 @@ goo_window_get_volume (GooWindow   *window)
 
 
 void
-goo_window_set_volume (GooWindow   *window,
-		       double       value)
+goo_window_set_volume (GooWindow *window,
+		       double     value)
 {
 	GooVolumeToolButton *volume_button;
 
@@ -3231,8 +3243,8 @@ goo_window_set_volume (GooWindow   *window,
 
 
 void
-goo_window_set_hibernate (GooWindow   *window,
-			  gboolean     hibernate)
+goo_window_set_hibernate (GooWindow *window,
+			  gboolean   hibernate)
 {
 	window->priv->hibernate = hibernate;
 
diff --git a/src/goo-window.h b/src/goo-window.h
index f0021af..f45e9c6 100644
--- a/src/goo-window.h
+++ b/src/goo-window.h
@@ -23,8 +23,11 @@
 #ifndef GOO_WINDOW_H
 #define GOO_WINDOW_H
 
-#include <libgnomeui/gnome-app.h>
+#include <gtk/gtk.h>
+#include <brasero/brasero-drive.h>
+#include "album-info.h"
 #include "goo-player.h"
+#include "gth-window.h"
 
 #define GOO_TYPE_WINDOW              (goo_window_get_type ())
 #define GOO_WINDOW(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_WINDOW, GooWindow))
@@ -33,21 +36,21 @@
 #define GOO_IS_WINDOW_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_WINDOW))
 #define GOO_WINDOW_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GOO_TYPE_WINDOW, GooWindowClass))
 
-typedef struct _GooWindow            GooWindow;
-typedef struct _GooWindowClass       GooWindowClass;
-typedef struct _GooWindowPrivateData GooWindowPrivateData;
+typedef struct _GooWindow        GooWindow;
+typedef struct _GooWindowClass   GooWindowClass;
+typedef struct _GooWindowPrivate GooWindowPrivate;
 
 struct _GooWindow
 {
-	GnomeApp              __parent;
-	GtkWidget            *preferences_dialog;
-	GtkWidget            *properties_dialog;
-	GooWindowPrivateData *priv;
+	GthWindow __parent;
+	GtkWidget *preferences_dialog;
+	GtkWidget *properties_dialog;
+	GooWindowPrivate *priv;
 };
 
 struct _GooWindowClass
 {
-	GnomeAppClass __parent_class;
+	GthWindowClass __parent_class;
 	
 	/*<signals>*/
 
@@ -55,43 +58,48 @@ struct _GooWindowClass
 };
 
 GType       goo_window_get_type                  (void);
-GtkWindow * goo_window_new                       (const char  *device);
-void        goo_window_close                     (GooWindow   *window);
-void        goo_window_set_toolbar_visibility    (GooWindow   *window,
-						  gboolean     visible);
-void        goo_window_set_statusbar_visibility  (GooWindow   *window,
-						  gboolean     visible);
-void        goo_window_update                    (GooWindow   *window);
-void        goo_window_play                      (GooWindow   *window);
-void        goo_window_play_selected             (GooWindow   *window);
-void        goo_window_toggle_play               (GooWindow   *window);
-void        goo_window_stop                      (GooWindow   *window);
-void        goo_window_pause                     (GooWindow   *window);
-void        goo_window_prev                      (GooWindow   *window);
-void        goo_window_next                      (GooWindow   *window);
-void        goo_window_eject                     (GooWindow   *window);
-void        goo_window_set_device                (GooWindow   *window,
-						  const char  *device);
-AlbumInfo * goo_window_get_album                 (GooWindow   *window);
-GList *     goo_window_get_tracks                (GooWindow   *window,
-						  gboolean     selection);
-GooPlayer * goo_window_get_player                (GooWindow   *window);
-void        goo_window_update_cover              (GooWindow   *window);
-void        goo_window_set_cover_image           (GooWindow   *window,
-						  const char  *filename);
-char *      goo_window_get_cover_filename        (GooWindow   *window);
-void        goo_window_pick_cover_from_disk      (GooWindow   *window);
-void        goo_window_search_cover_on_internet  (GooWindow   *window);
-void        goo_window_remove_cover              (GooWindow   *window);
-void        goo_window_toggle_visibility         (GooWindow   *window);
-double      goo_window_get_volume                (GooWindow   *window);
-void        goo_window_set_volume                (GooWindow   *window,
-						  double       value);
-void        goo_window_set_hibernate             (GooWindow   *window,
-						  gboolean     hibernate);
-void        goo_window_set_current_cd_autofetch  (GooWindow   *window,
-						  gboolean     autofetch);
-gboolean    goo_window_get_current_cd_autofetch  (GooWindow   *window);
-GtkWidget * goo_window_get_tray_icon             (GooWindow   *window);
+GtkWidget * goo_window_new                       (BraseroDrive *drive);
+void        goo_window_close                     (GooWindow    *window);
+void        goo_window_set_toolbar_visibility    (GooWindow    *window,
+						  gboolean      visible);
+void        goo_window_set_statusbar_visibility  (GooWindow    *window,
+						  gboolean      visible);
+void        goo_window_update                    (GooWindow    *window);
+void        goo_window_play                      (GooWindow    *window);
+void        goo_window_play_selected             (GooWindow    *window);
+void        goo_window_toggle_play               (GooWindow    *window);
+void        goo_window_stop                      (GooWindow    *window);
+void        goo_window_pause                     (GooWindow    *window);
+void        goo_window_prev                      (GooWindow    *window);
+void        goo_window_next                      (GooWindow    *window);
+void        goo_window_eject                     (GooWindow    *window);
+void        goo_window_set_device                (GooWindow    *window,
+						  const char   *device);
+AlbumInfo * goo_window_get_album                 (GooWindow    *window);
+GList *     goo_window_get_tracks                (GooWindow    *window,
+						  gboolean      selection);
+GooPlayer * goo_window_get_player                (GooWindow    *window);
+void        goo_window_update_cover              (GooWindow    *window);
+void        goo_window_set_cover_image_from_pixbuf (GooWindow  *window,
+					          GdkPixbuf    *image);
+void        goo_window_set_cover_image           (GooWindow    *window,
+						  const char   *filename);
+void        goo_window_set_cover_image_from_data (GooWindow    *window,
+						  void         *buffer,
+						  gsize         count);
+char *      goo_window_get_cover_filename        (GooWindow    *window);
+void        goo_window_pick_cover_from_disk      (GooWindow    *window);
+void        goo_window_search_cover_on_internet  (GooWindow    *window);
+void        goo_window_remove_cover              (GooWindow    *window);
+void        goo_window_toggle_visibility         (GooWindow    *window);
+double      goo_window_get_volume                (GooWindow    *window);
+void        goo_window_set_volume                (GooWindow    *window,
+						  double        value);
+void        goo_window_set_hibernate             (GooWindow    *window,
+						  gboolean      hibernate);
+void        goo_window_set_current_cd_autofetch  (GooWindow    *window,
+						  gboolean      autofetch);
+gboolean    goo_window_get_current_cd_autofetch  (GooWindow    *window);
+GtkWidget * goo_window_get_tray_icon             (GooWindow    *window);
 
 #endif /* GOO_WINDOW_H */
diff --git a/src/gth-user-dir.c b/src/gth-user-dir.c
new file mode 100644
index 0000000..ad1e3d3
--- /dev/null
+++ b/src/gth-user-dir.c
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <glib.h>
+#include "gth-user-dir.h"
+
+
+static char *
+gth_user_dir_get_file_va_list (GthDir      dir_type,
+			       const char *first_element,
+                               va_list     var_args)
+{
+	const char *user_dir;
+	char       *config_dir;
+	GString    *path;
+	const char *element;
+	char       *filename;
+
+	switch (dir_type) {
+	case GTH_DIR_CONFIG:
+		user_dir = g_get_user_config_dir ();
+		break;
+	case GTH_DIR_CACHE:
+		user_dir = g_get_user_cache_dir ();
+		break;
+	case GTH_DIR_DATA:
+		user_dir = g_get_user_data_dir ();
+		break;
+	}
+
+	config_dir = g_build_path (G_DIR_SEPARATOR_S,
+				   user_dir,
+				   NULL);
+	path = g_string_new (config_dir);
+
+	element = first_element;
+  	while (element != NULL) {
+  		if (element[0] != '\0') {
+  			g_string_append (path, G_DIR_SEPARATOR_S);
+  			g_string_append (path, element);
+  		}
+		element = va_arg (var_args, const char *);
+	}
+
+	filename = path->str;
+
+	g_string_free (path, FALSE);
+	g_free (config_dir);
+
+	return filename;
+}
+
+
+char *
+gth_user_dir_get_file (GthDir      dir_type,
+		       const char *first_element,
+                       ...)
+{
+	va_list  var_args;
+	char    *filename;
+
+	va_start (var_args, first_element);
+	filename = gth_user_dir_get_file_va_list (dir_type, first_element, var_args);
+	va_end (var_args);
+
+	return filename;
+}
+
+
+void
+gth_user_dir_make_dir_for_file (GthDir      dir_type,
+			        const char *first_element,
+                                ...)
+{
+	va_list  var_args;
+	char    *config_file;
+	char    *config_dir;
+
+	va_start (var_args, first_element);
+	config_file = gth_user_dir_get_file_va_list (dir_type, first_element, var_args);
+	va_end (var_args);
+
+	config_dir = g_path_get_dirname (config_file);
+	g_mkdir_with_parents (config_dir, 0700);
+
+	g_free (config_dir);
+	g_free (config_file);
+}
diff --git a/src/gth-user-dir.h b/src/gth-user-dir.h
new file mode 100644
index 0000000..2308c1b
--- /dev/null
+++ b/src/gth-user-dir.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_USER_DIR_H
+#define GTH_USER_DIR_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	GTH_DIR_CONFIG,
+	GTH_DIR_CACHE,
+	GTH_DIR_DATA
+} GthDir;
+
+char *  gth_user_dir_get_file              (GthDir      dir_type,
+					    const char *first_element,
+				            ...);
+void    gth_user_dir_make_dir_for_file     (GthDir      dir_type,
+					    const char *first_element,
+                                            ...);
+
+G_END_DECLS
+
+#endif /* GTH_USER_DIR_H */
diff --git a/src/gth-window.c b/src/gth-window.c
new file mode 100644
index 0000000..458aac4
--- /dev/null
+++ b/src/gth-window.c
@@ -0,0 +1,394 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2005-2008 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include "gth-window.h"
+#include "gtk-utils.h"
+
+
+#define GTH_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTH_TYPE_WINDOW, GthWindowPrivate))
+
+enum  {
+	GTH_WINDOW_DUMMY_PROPERTY,
+	GTH_WINDOW_N_PAGES
+};
+
+
+static GtkWindowClass *parent_class = NULL;
+static GList *window_list = NULL;
+
+
+struct _GthWindowPrivate {
+	int         n_pages;
+	int         current_page;
+	GtkWidget  *table;
+	GtkWidget  *notebook;
+	GtkWidget  *menubar;
+	GtkWidget  *toolbar;
+	GtkWidget  *statusbar;
+	GtkWidget **toolbars;
+	GtkWidget **contents;
+};
+
+
+static void
+gth_window_set_n_pages (GthWindow *self,
+			int        n_pages)
+{
+	int i;
+
+	self->priv->n_pages = n_pages;
+
+	self->priv->table = gtk_table_new (4, 1, FALSE);
+	gtk_widget_show (self->priv->table);
+	gtk_container_add (GTK_CONTAINER (self), self->priv->table);
+
+	self->priv->notebook = gtk_notebook_new ();
+	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (self->priv->notebook), FALSE);
+	gtk_notebook_set_show_border (GTK_NOTEBOOK (self->priv->notebook), FALSE);
+	gtk_widget_show (self->priv->notebook);
+	gtk_table_attach (GTK_TABLE (self->priv->table),
+			  self->priv->notebook,
+			  0, 1,
+			  2, 3,
+			  GTK_EXPAND | GTK_FILL,
+			  GTK_EXPAND | GTK_FILL,
+			  0, 0);
+
+	self->priv->toolbars = g_new0 (GtkWidget *, n_pages);
+	self->priv->contents = g_new0 (GtkWidget *, n_pages);
+
+	for (i = 0; i < n_pages; i++) {
+		GtkWidget *page;
+
+		page = gtk_vbox_new (FALSE, 0);
+		gtk_widget_show (page);
+		gtk_notebook_append_page (GTK_NOTEBOOK (self->priv->notebook),
+					  page,
+					  NULL);
+
+		self->priv->toolbars[i] = gtk_hbox_new (FALSE, 0);
+		gtk_widget_show (self->priv->toolbars[i]);
+		gtk_box_pack_start (GTK_BOX (page), self->priv->toolbars[i], FALSE, FALSE, 0);
+
+		self->priv->contents[i] = gtk_hbox_new (FALSE, 0);
+		gtk_widget_show (self->priv->contents[i]);
+		gtk_box_pack_start (GTK_BOX (page), self->priv->contents[i], TRUE, TRUE, 0);
+	}
+}
+
+
+static void
+gth_window_set_property (GObject      *object,
+			 guint         property_id,
+			 const GValue *value,
+			 GParamSpec   *pspec)
+{
+	GthWindow *self;
+
+	self = GTH_WINDOW (object);
+
+	switch (property_id) {
+	case GTH_WINDOW_N_PAGES:
+		gth_window_set_n_pages (self, g_value_get_int (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+		break;
+	}
+}
+
+
+static void
+gth_window_finalize (GObject *object)
+{
+	GthWindow *window = GTH_WINDOW (object);
+
+	g_free (window->priv->toolbars);
+	g_free (window->priv->contents);
+
+	window_list = g_list_remove (window_list, window);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+
+	if (window_list == NULL)
+		gtk_main_quit ();
+}
+
+
+static gboolean
+gth_window_delete_event (GtkWidget   *widget,
+			 GdkEventAny *event)
+{
+	gth_window_close ((GthWindow*) widget);
+	return TRUE;
+}
+
+
+static void
+gth_window_real_close (GthWindow *window)
+{
+	/* virtual */
+}
+
+
+static void
+gth_window_real_set_current_page (GthWindow *window,
+				  int        page)
+{
+	if (window->priv->current_page == page)
+		return;
+	window->priv->current_page = page;
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), page);
+}
+
+
+static void
+gth_window_class_init (GthWindowClass *klass)
+{
+	GObjectClass   *gobject_class;
+	GtkWidgetClass *widget_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthWindowPrivate));
+
+	gobject_class = (GObjectClass*) klass;
+	gobject_class->set_property = gth_window_set_property;
+	gobject_class->finalize = gth_window_finalize;
+
+	widget_class = (GtkWidgetClass*) klass;
+	widget_class->delete_event = gth_window_delete_event;
+
+	klass->close = gth_window_real_close;
+	klass->set_current_page = gth_window_real_set_current_page;
+
+	g_object_class_install_property (G_OBJECT_CLASS (klass),
+					 GTH_WINDOW_N_PAGES,
+					 g_param_spec_int ("n-pages",
+							   "n-pages",
+							   "n-pages",
+							   0,
+							   G_MAXINT,
+							   1,
+							   G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+
+static void
+gth_window_init (GthWindow *window)
+{
+	window->priv = GTH_WINDOW_GET_PRIVATE (window);
+	window->priv->table = NULL;
+	window->priv->contents = NULL;
+	window->priv->n_pages = 0;
+	window->priv->current_page = -1;
+	window->priv->menubar = NULL;
+	window->priv->toolbar = NULL;
+	window->priv->statusbar = NULL;
+
+	window_list = g_list_prepend (window_list, window);
+}
+
+
+GType
+gth_window_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo type_info = {
+			sizeof (GthWindowClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_window_class_init,
+			NULL,
+			NULL,
+			sizeof (GthWindow),
+			0,
+			(GInstanceInitFunc) gth_window_init
+		};
+
+		type = g_type_register_static (GTK_TYPE_WINDOW,
+					       "GthWindow",
+					       &type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+void
+gth_window_close (GthWindow *window)
+{
+	GTH_WINDOW_GET_CLASS (window)->close (window);
+}
+
+
+void
+gth_window_attach (GthWindow     *window,
+		   GtkWidget     *child,
+		   GthWindowArea  area)
+{
+	int position;
+
+	g_return_if_fail (window != NULL);
+	g_return_if_fail (GTH_IS_WINDOW (window));
+	g_return_if_fail (child != NULL);
+	g_return_if_fail (GTK_IS_WIDGET (child));
+
+	switch (area) {
+	case GTH_WINDOW_MENUBAR:
+		window->priv->menubar = child;
+		position = 0;
+		break;
+	case GTH_WINDOW_TOOLBAR:
+		window->priv->toolbar = child;
+		position = 1;
+		break;
+	case GTH_WINDOW_STATUSBAR:
+		window->priv->statusbar = child;
+		position = 3;
+		break;
+	default:
+		return;
+	}
+
+	gtk_table_attach (GTK_TABLE (window->priv->table),
+			  child,
+			  0, 1,
+			  position, position + 1,
+			  GTK_EXPAND | GTK_FILL,
+			  GTK_FILL,
+			  0, 0);
+}
+
+
+void
+gth_window_attach_toolbar (GthWindow *window,
+			   int        page,
+			   GtkWidget *child)
+{
+	g_return_if_fail (window != NULL);
+	g_return_if_fail (GTH_IS_WINDOW (window));
+	g_return_if_fail (page >= 0 && page < window->priv->n_pages);
+	g_return_if_fail (child != NULL);
+	g_return_if_fail (GTK_IS_WIDGET (child));
+
+	_gtk_container_remove_children (GTK_CONTAINER (window->priv->toolbars[page]), NULL, NULL);
+	gtk_container_add (GTK_CONTAINER (window->priv->toolbars[page]), child);
+}
+
+
+void
+gth_window_attach_content (GthWindow *window,
+			   int        page,
+			   GtkWidget *child)
+{
+	g_return_if_fail (window != NULL);
+	g_return_if_fail (GTH_IS_WINDOW (window));
+	g_return_if_fail (page >= 0 && page < window->priv->n_pages);
+	g_return_if_fail (child != NULL);
+	g_return_if_fail (GTK_IS_WIDGET (child));
+
+	_gtk_container_remove_children (GTK_CONTAINER (window->priv->contents[page]), NULL, NULL);
+	gtk_container_add (GTK_CONTAINER (window->priv->contents[page]), child);
+}
+
+
+void
+gth_window_set_current_page (GthWindow *window,
+			     int        page)
+{
+	GTH_WINDOW_GET_CLASS (window)->set_current_page (window, page);
+}
+
+
+int
+gth_window_get_current_page (GthWindow *window)
+{
+	return window->priv->current_page;
+}
+
+
+static void
+hide_widget (GtkWidget *widget)
+{
+	if (widget != NULL)
+		gtk_widget_hide (widget);
+}
+
+
+static void
+show_widget (GtkWidget *widget)
+{
+	if (widget != NULL)
+		gtk_widget_show (widget);
+}
+
+
+void
+gth_window_show_only_content (GthWindow *window,
+			      gboolean   only_content)
+{
+	int i;
+
+	if (only_content) {
+		for (i = 0; i < window->priv->n_pages; i++)
+			hide_widget (window->priv->toolbars[i]);
+		hide_widget (window->priv->menubar);
+		hide_widget (window->priv->toolbar);
+		hide_widget (window->priv->statusbar);
+	}
+	else {
+		for (i = 0; i < window->priv->n_pages; i++)
+			show_widget (window->priv->toolbars[i]);
+		show_widget (window->priv->menubar);
+		show_widget (window->priv->toolbar);
+		show_widget (window->priv->statusbar);
+	}
+}
+
+
+int
+gth_window_get_n_windows (void)
+{
+	return g_list_length (window_list);
+}
+
+
+GList *
+gth_window_get_window_list (void)
+{
+	return window_list;
+}
+
+
+GtkWidget *
+gth_window_get_current_window (void)
+{
+	if ((window_list == NULL) || (g_list_length (window_list) > 1))
+		return NULL;
+	else
+		return (GtkWidget *) window_list->data;
+}
diff --git a/src/gth-window.h b/src/gth-window.h
new file mode 100644
index 0000000..24b3839
--- /dev/null
+++ b/src/gth-window.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2005-2008 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_WINDOW_H
+#define GTH_WINDOW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum { /*< skip >*/
+	GTH_WINDOW_MENUBAR,
+	GTH_WINDOW_TOOLBAR,
+	GTH_WINDOW_STATUSBAR,
+} GthWindowArea;
+
+#define GTH_TYPE_WINDOW              (gth_window_get_type ())
+#define GTH_WINDOW(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_WINDOW, GthWindow))
+#define GTH_WINDOW_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_WINDOW, GthWindowClass))
+#define GTH_IS_WINDOW(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_WINDOW))
+#define GTH_IS_WINDOW_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_WINDOW))
+#define GTH_WINDOW_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_WINDOW, GthWindowClass))
+
+typedef struct _GthWindow        GthWindow;
+typedef struct _GthWindowClass   GthWindowClass;
+typedef struct _GthWindowPrivate GthWindowPrivate;
+
+struct _GthWindow
+{
+	GtkWindow __parent;
+	GthWindowPrivate *priv;
+};
+
+struct _GthWindowClass
+{
+	GtkWindowClass __parent_class;
+
+	/*< virtual functions >*/
+
+	void (*set_current_page)  (GthWindow *window,
+				   int        page);
+	void (*close)             (GthWindow *window);
+};
+
+GType          gth_window_get_type           (void);
+void           gth_window_close              (GthWindow     *window);
+void           gth_window_attach             (GthWindow     *window,
+					      GtkWidget     *child,
+					      GthWindowArea  area);
+void           gth_window_attach_toolbar     (GthWindow     *window,
+					      int            page,
+					      GtkWidget     *child);
+void           gth_window_attach_content     (GthWindow     *window,
+					      int            page,
+					      GtkWidget     *child);
+void           gth_window_set_current_page   (GthWindow     *window,
+					      int            page);
+int            gth_window_get_current_page   (GthWindow     *window);
+void           gth_window_show_only_content  (GthWindow     *window,
+					      gboolean       only_content);
+
+/**/
+
+int            gth_window_get_n_windows      (void);
+GList *        gth_window_get_window_list    (void);
+GtkWidget *    gth_window_get_current_window (void);
+
+G_END_DECLS
+
+#endif /* GTH_WINDOW_H */
diff --git a/src/gtk-file-chooser-preview.c b/src/gtk-file-chooser-preview.c
index a49ea44..7a2e8e9 100644
--- a/src/gtk-file-chooser-preview.c
+++ b/src/gtk-file-chooser-preview.c
@@ -23,32 +23,24 @@
  */
 
 #include <config.h>
-#include <libintl.h>
+#include <glib/gi18n.h>
 #include <gtk/gtk.h>
-#include <libgnomeui/gnome-thumbnail.h>
-#include <libgnomevfs/gnome-vfs.h>
-#include <libgnomevfs/gnome-vfs-mime.h>
-#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include "glib-utils.h"
+#include "gnome-desktop-thumbnail.h"
 #include "gtk-file-chooser-preview.h"
 
-#define _(String) gettext (String)
-
-#ifdef gettext_noop
-#    define N_(String) gettext_noop (String)
-#else
-#    define N_(String) (String)
-#endif
 
 #define MIN_WIDTH 150
 #define PREVIEW_SIZE 120
 
 
 struct _GtkFileChooserPreviewPrivateData {
-	char                  *uri;
-	GnomeThumbnailFactory *thumb_factory;
-	GtkWidget             *image;
-	GtkWidget             *image_info;
-	GdkWindow             *bin_window;
+	char       *uri;
+	GnomeDesktopThumbnailFactory
+	           *thumb_factory;
+	GtkWidget  *image;
+	GtkWidget  *image_info;
+	GdkWindow  *bin_window;
 };
 
 
@@ -138,8 +130,8 @@ gtk_file_chooser_preview_init (GtkFileChooserPreview *preview)
 	preview->priv = g_new0 (GtkFileChooserPreviewPrivateData, 1);
 	priv = preview->priv;
 
-	priv->thumb_factory = gnome_thumbnail_factory_new (GNOME_THUMBNAIL_SIZE_NORMAL);
-	priv->uri = NULL;
+	preview->priv->thumb_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
+	preview->priv->uri = NULL;
 }
 
 
@@ -153,13 +145,8 @@ gtk_file_chooser_preview_finalize (GObject *object)
 
 	preview = GTK_FILE_CHOOSER_PREVIEW (object);
 	if (preview->priv != NULL) {
-		GtkFileChooserPreviewPrivateData *priv = preview->priv;
-
-		if (priv->thumb_factory != NULL)
-			g_object_unref (priv->thumb_factory);
-
-		g_free (priv->uri);
-		priv->uri = NULL;
+		_g_object_unref (preview->priv->thumb_factory);
+		g_free (preview->priv->uri);
 
 		g_free (preview->priv);
 		preview->priv = NULL;
@@ -181,7 +168,6 @@ set_void_preview (GtkFileChooserPreview *preview)
 static void
 gtk_file_chooser_preview_construct (GtkFileChooserPreview  *preview)
 {
-	GtkFileChooserPreviewPrivateData *priv = preview->priv;
 	GtkWidget *event_box;
 	GtkWidget *vbox;
 	GtkWidget *button;
@@ -223,15 +209,15 @@ gtk_file_chooser_preview_construct (GtkFileChooserPreview  *preview)
 	gtk_widget_show (vbox2);
 	gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, FALSE, 0);
 
-	priv->image = gtk_image_new ();
-	gtk_widget_show (priv->image);
-	gtk_box_pack_start (GTK_BOX (vbox2), priv->image, FALSE, FALSE, 0);
+	preview->priv->image = gtk_image_new ();
+	gtk_widget_show (preview->priv->image);
+	gtk_box_pack_start (GTK_BOX (vbox2), preview->priv->image, FALSE, FALSE, 0);
 
-	priv->image_info = gtk_label_new (NULL);
-	gtk_label_set_justify (GTK_LABEL (priv->image_info), GTK_JUSTIFY_CENTER);
-	gtk_misc_set_alignment (GTK_MISC (priv->image_info), 0.5, 0.5);
-	gtk_widget_hide (priv->image_info);
-	gtk_box_pack_start (GTK_BOX (vbox2), priv->image_info, FALSE, FALSE, 0);
+	preview->priv->image_info = gtk_label_new (NULL);
+	gtk_label_set_justify (GTK_LABEL (preview->priv->image_info), GTK_JUSTIFY_CENTER);
+	gtk_misc_set_alignment (GTK_MISC (preview->priv->image_info), 0.5, 0.5);
+	gtk_widget_hide (preview->priv->image_info);
+	gtk_box_pack_start (GTK_BOX (vbox2), preview->priv->image_info, FALSE, FALSE, 0);
 
 	set_void_preview (preview);
 }
@@ -250,70 +236,46 @@ gtk_file_chooser_preview_new (void)
 }
 
 
-static GnomeVFSFileInfo*
-vfs_get_file_info (const gchar *uri)
-{
-	GnomeVFSFileInfo *info;
-	GnomeVFSResult    result;
-
-	if (uri == NULL) 
-		return NULL; 
-
-	info = gnome_vfs_file_info_new ();
-	result = gnome_vfs_get_file_info (uri,
-					  info, 
-					  (GNOME_VFS_FILE_INFO_DEFAULT | GNOME_VFS_FILE_INFO_FOLLOW_LINKS));
-	
-	if (result != GNOME_VFS_OK) {
-		gnome_vfs_file_info_unref (info);
-		info = NULL;
-	}
-
-	return info;
-}
-
-
 void
 gtk_file_chooser_preview_set_uri (GtkFileChooserPreview *preview,
 				  const char            *uri)
 {
-	GtkFileChooserPreviewPrivateData *priv = preview->priv;
-	GnomeVFSFileInfo  *info;
-	GdkPixbuf         *pixbuf;
-	char              *thumb_uri;
-
-	g_free (priv->uri);
-	priv->uri = NULL;
-
+	GFile     *file;
+	GFileInfo *info;
+	GTimeVal   timeval;
+	GdkPixbuf *pixbuf;
+	char      *thumb_uri;
+
+	g_free (preview->priv->uri);
+	preview->priv->uri = NULL;
 	if (uri == NULL) {
 		set_void_preview (preview);
 		return;
 	}
 
-	priv->uri = g_strdup (uri);
-	info = vfs_get_file_info (uri);
-
+	preview->priv->uri = g_strdup (uri);
+	file = g_file_new_for_uri (uri);
+	info = g_file_query_info (file, "time::*,standard::content-type,standard::size", G_FILE_QUERY_INFO_NONE, NULL, NULL);
 	if (info == NULL) {
 		set_void_preview (preview);
+		g_object_unref (file);
 		return;
 	}
 
-	thumb_uri = gnome_thumbnail_factory_lookup (priv->thumb_factory,
-						    uri,
-						    info->mtime);
+	g_file_info_get_modification_time (info, &timeval);
+
+	thumb_uri = gnome_desktop_thumbnail_factory_lookup (preview->priv->thumb_factory,
+							    uri,
+							    timeval.tv_sec);
 	if (thumb_uri != NULL) {
 		pixbuf = gdk_pixbuf_new_from_file (thumb_uri, NULL);
-
-	} else {
-		char *mime_type = gnome_vfs_get_mime_type (uri);
-
-		pixbuf = gnome_thumbnail_factory_generate_thumbnail (priv->thumb_factory, uri, mime_type);
+	}
+	else {
+		pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (preview->priv->thumb_factory, uri, g_file_info_get_content_type (info));
 		if (pixbuf != NULL)
-			gnome_thumbnail_factory_save_thumbnail (priv->thumb_factory, pixbuf, uri, info->mtime);
+			gnome_desktop_thumbnail_factory_save_thumbnail (preview->priv->thumb_factory, pixbuf, uri, timeval.tv_sec);
 		else
-			gnome_thumbnail_factory_create_failed_thumbnail (priv->thumb_factory, uri, info->mtime);
-
-		g_free (mime_type);
+			gnome_desktop_thumbnail_factory_create_failed_thumbnail (preview->priv->thumb_factory, uri, timeval.tv_sec);
 	}
 
 	if (pixbuf != NULL) {
@@ -322,28 +284,28 @@ gtk_file_chooser_preview_set_uri (GtkFileChooserPreview *preview,
 		
 		w = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width");
 		h = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height");
-		size_text = gnome_vfs_format_file_size_for_display (info->size); 
+		size_text = g_format_size_for_display (g_file_info_get_size (info));
 		text = g_strconcat (size_text, 
 				    ((w == NULL)?NULL:"\n"),
 				    w, " x ", h, " ", _("pixels"),
 				    NULL);
 		g_free (size_text);
 
-		gtk_label_set_markup (GTK_LABEL (priv->image_info), text);
+		gtk_label_set_markup (GTK_LABEL (preview->priv->image_info), text);
 		g_free (text);
 
-		gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf); 
+		gtk_image_set_from_pixbuf (GTK_IMAGE (preview->priv->image), pixbuf);
 		
 		g_object_unref (pixbuf);
-		gtk_widget_show (priv->image);
-		gtk_widget_show (priv->image_info);
+		gtk_widget_show (preview->priv->image);
+		gtk_widget_show (preview->priv->image_info);
 
 		gtk_widget_set_sensitive (GTK_BIN (preview)->child, TRUE);
-
-	} else 
+	}
+	else
 		set_void_preview (preview);
 
-	gnome_vfs_file_info_unref (info);
+	g_object_unref (info);
 }
 
 
diff --git a/src/gtk-utils.c b/src/gtk-utils.c
index 63465c0..e10a557 100644
--- a/src/gtk-utils.c
+++ b/src/gtk-utils.c
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
 /*
- *  Goo
+ *  File-Roller
  *
- *  Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2001-2008 The Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,45 +21,16 @@
  */
 
 #include <config.h>
-#include <libgnome/libgnome.h>
-#include <gtk/gtk.h>
+#include <math.h>
+#include <string.h>
 #include "gconf-utils.h"
-#include "file-utils.h"
-
+#include "gtk-utils.h"
 
 #define REQUEST_ENTRY_WIDTH 220
 
 
-GtkWidget *
-_gtk_image_new_from_xpm_data (char * xpm_data[])
-{
-	GtkWidget *image;
-	GdkPixbuf *pixbuf;
-
-	pixbuf = gdk_pixbuf_new_from_xpm_data ((const char**) xpm_data);
-	image = gtk_image_new_from_pixbuf (pixbuf);
-	g_object_unref (G_OBJECT (pixbuf));
-
-	return image;
-}
-
-
-GtkWidget *
-_gtk_image_new_from_inline (const guint8 *data)
-{
-	GtkWidget *image;
-	GdkPixbuf *pixbuf;
-
-	pixbuf = gdk_pixbuf_new_from_inline (-1, data, FALSE, NULL);
-	image = gtk_image_new_from_pixbuf (pixbuf);
-	g_object_unref (G_OBJECT (pixbuf));
-
-	return image;
-}
-
-
 static GtkWidget *
-create_button (const char *stock_id, 
+create_button (const char *stock_id,
 	       const char *text)
 {
 	GtkWidget    *button;
@@ -131,8 +102,8 @@ _gtk_message_dialog_new (GtkWindow        *parent,
 
 	gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
 	gtk_container_set_border_width (GTK_CONTAINER (d), 6);
-	gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
-	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 8);
+	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
 
 	/* Add label and image */
 
@@ -144,11 +115,11 @@ _gtk_message_dialog_new (GtkWindow        *parent,
 	escaped_message = g_markup_escape_text (message, -1);
 	if (secondary_message != NULL) {
 		char *escaped_secondary_message = g_markup_escape_text (secondary_message, -1);
-		markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", 
+		markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
 					       escaped_message,
 					       escaped_secondary_message);
 		g_free (escaped_secondary_message);
-	} else 
+	} else
 		markup_text = g_strdup (escaped_message);
 	gtk_label_set_markup (GTK_LABEL (label), markup_text);
 	g_free (markup_text);
@@ -156,22 +127,22 @@ _gtk_message_dialog_new (GtkWindow        *parent,
 
 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
 	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-	
+
 	hbox = gtk_hbox_new (FALSE, 6);
 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), image,
 			    FALSE, FALSE, 0);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), label,
 			    TRUE, TRUE, 0);
-	
-	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
 			    hbox,
 			    FALSE, FALSE, 0);
-	
+
 	gtk_widget_show_all (hbox);
-	
+
 	/* Add buttons */
 
 	if (first_button_text == NULL)
@@ -223,15 +194,15 @@ _gtk_request_dialog_run (GtkWindow        *parent,
 
 	gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
 	gtk_container_set_border_width (GTK_CONTAINER (d), 6);
-	gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
-	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 12);
+	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 12);
 
 	/* Add label and image */
 
 	image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
 	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
 
-	label = gtk_label_new (message);	
+	label = gtk_label_new (message);
 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
 	gtk_label_set_selectable (GTK_LABEL (label), FALSE);
 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
@@ -260,37 +231,41 @@ _gtk_request_dialog_run (GtkWindow        *parent,
 
 	gtk_box_pack_start (GTK_BOX (hbox), vbox,
 			    TRUE, TRUE, 0);
-	
-	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
 			    hbox,
 			    FALSE, FALSE, 0);
-	
+
 	gtk_widget_show_all (hbox);
-	
+
 	/* Add buttons */
 
 	button = create_button (GTK_STOCK_CANCEL, no_button_text);
-	gtk_dialog_add_action_widget (GTK_DIALOG (d), 
-				      button, 
+	gtk_dialog_add_action_widget (GTK_DIALOG (d),
+				      button,
 				      GTK_RESPONSE_CANCEL);
 
 	/**/
 
 	button = create_button (GTK_STOCK_OK, yes_button_text);
-	gtk_dialog_add_action_widget (GTK_DIALOG (d), 
+	gtk_dialog_add_action_widget (GTK_DIALOG (d),
 				      button,
 				      GTK_RESPONSE_YES);
 
 	/**/
 
-	gtk_dialog_set_default_response (GTK_DIALOG (d), 
+	gtk_dialog_set_default_response (GTK_DIALOG (d),
 					 GTK_RESPONSE_YES);
 	gtk_widget_grab_focus (entry);
 
 	/* Run dialog */
 
-	if (gtk_dialog_run (GTK_DIALOG (d)) == GTK_RESPONSE_YES) 
-		result = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+	if ((gtk_dialog_run (GTK_DIALOG (d)) == GTK_RESPONSE_YES) &&
+	    (strlen (gtk_entry_get_text (GTK_ENTRY (entry))) > 0) )
+		/* Normalize unicode text to "NFC" form for consistency. */
+		result = g_utf8_normalize (gtk_entry_get_text (GTK_ENTRY (entry)),
+					   -1,
+					   G_NORMALIZE_NFC);
 	else
 		result = NULL;
 
@@ -319,51 +294,51 @@ _gtk_yesno_dialog_new (GtkWindow        *parent,
 
 	gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
 	gtk_container_set_border_width (GTK_CONTAINER (d), 6);
-	gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
-	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 8);
+	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
 
 	/* Add label and image */
 
 	image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
 	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
 
-	label = gtk_label_new (message);	
+	label = gtk_label_new (message);
 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
 	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-	
+
 	hbox = gtk_hbox_new (FALSE, 12);
 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), image,
 			    FALSE, FALSE, 0);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), label,
 			    TRUE, TRUE, 0);
-	
-	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
 			    hbox,
 			    FALSE, FALSE, 0);
-	
+
 	gtk_widget_show_all (hbox);
 
 	/* Add buttons */
 
 	button = create_button (GTK_STOCK_CANCEL, no_button_text);
-	gtk_dialog_add_action_widget (GTK_DIALOG (d), 
-				      button, 
+	gtk_dialog_add_action_widget (GTK_DIALOG (d),
+				      button,
 				      GTK_RESPONSE_CANCEL);
 
 	/**/
 
 	button = create_button (GTK_STOCK_OK, yes_button_text);
-	gtk_dialog_add_action_widget (GTK_DIALOG (d), 
-				      button, 
+	gtk_dialog_add_action_widget (GTK_DIALOG (d),
+				      button,
 				      GTK_RESPONSE_YES);
 
 	/**/
 
 	gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
-	
+
 	return d;
 }
 
@@ -372,7 +347,7 @@ static void
 yesno__check_button_toggled_cb (GtkToggleButton *button,
 				const char      *gconf_key)
 {
-	eel_gconf_set_boolean (gconf_key, 
+	eel_gconf_set_boolean (gconf_key,
 			       ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
 }
 
@@ -399,8 +374,8 @@ _gtk_yesno_dialog_with_checkbutton_new (GtkWindow        *parent,
 
 	gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
 	gtk_container_set_border_width (GTK_CONTAINER (d), 6);
-	gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (d)->vbox), 6);
-	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (d)->vbox), 8);
+	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
 
 	/* Add label and image */
 
@@ -410,42 +385,42 @@ _gtk_yesno_dialog_with_checkbutton_new (GtkWindow        *parent,
 	label = gtk_label_new (message);
 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
 	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-	
+
 	hbox = gtk_hbox_new (FALSE, 12);
 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), image,
 			    FALSE, FALSE, 0);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), label,
 			    TRUE, TRUE, 0);
-	
-	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
 			    hbox,
 			    FALSE, FALSE, 0);
 
 	/* Add checkbutton */
 
 	check_button = gtk_check_button_new_with_mnemonic (check_button_label);
-	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
 			    check_button,
 			    FALSE, FALSE, 0);
 	gtk_widget_show (check_button);
-	
+
 	gtk_widget_show_all (hbox);
 
 	/* Add buttons */
 
 	button = create_button (GTK_STOCK_CANCEL, no_button_text);
-	gtk_dialog_add_action_widget (GTK_DIALOG (d), 
-				      button, 
+	gtk_dialog_add_action_widget (GTK_DIALOG (d),
+				      button,
 				      GTK_RESPONSE_CANCEL);
 
 	/**/
 
 	button = create_button (GTK_STOCK_OK, yes_button_text);
-	gtk_dialog_add_action_widget (GTK_DIALOG (d), 
-				      button, 
+	gtk_dialog_add_action_widget (GTK_DIALOG (d),
+				      button,
 				      GTK_RESPONSE_YES);
 
 	/**/
@@ -453,10 +428,10 @@ _gtk_yesno_dialog_with_checkbutton_new (GtkWindow        *parent,
 	gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
 
 	g_signal_connect (G_OBJECT (check_button),
-			  "toggled", 
-			  G_CALLBACK (yesno__check_button_toggled_cb), 
+			  "toggled",
+			  G_CALLBACK (yesno__check_button_toggled_cb),
 			  (gpointer) gconf_key);
-	
+
 	return d;
 }
 
@@ -465,7 +440,7 @@ static void
 ok__check_button_toggled_cb (GtkToggleButton *button,
 				const char      *gconf_key)
 {
-	eel_gconf_set_boolean (gconf_key, 
+	eel_gconf_set_boolean (gconf_key,
 			       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
 }
 
@@ -503,23 +478,23 @@ _gtk_ok_dialog_with_checkbutton_new (GtkWindow        *parent,
 	escaped_message = g_markup_escape_text (message, -1);
 	markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>", escaped_message);
 
-	label = gtk_label_new ("");	
+	label = gtk_label_new ("");
 	gtk_label_set_markup (GTK_LABEL (label), markup_text);
 	g_free (markup_text);
 	g_free (escaped_message);
 
 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
 	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-	
+
 	hbox = gtk_hbox_new (FALSE, 12);
 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), image,
 			    FALSE, FALSE, 0);
-	
+
 	gtk_box_pack_start (GTK_BOX (hbox), label,
 			    TRUE, TRUE, 0);
-	
+
 	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (d)->vbox),
 			    hbox,
 			    FALSE, FALSE, 0);
@@ -534,60 +509,199 @@ _gtk_ok_dialog_with_checkbutton_new (GtkWindow        *parent,
 
 	gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (check_button),
 				     eel_gconf_get_boolean (gconf_key, FALSE));
-	
+
 	gtk_widget_show_all (hbox);
 
 	/* Add buttons */
 
 	button = create_button (GTK_STOCK_CANCEL, ok_button_text);
-	gtk_dialog_add_action_widget (GTK_DIALOG (d), 
-				      button, 
+	gtk_dialog_add_action_widget (GTK_DIALOG (d),
+				      button,
 				      GTK_RESPONSE_OK);
-	
+
 	/**/
 
 	gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
 
 	g_signal_connect (G_OBJECT (check_button),
-			  "toggled", 
-			  G_CALLBACK (ok__check_button_toggled_cb), 
+			  "toggled",
+			  G_CALLBACK (ok__check_button_toggled_cb),
+			  (gpointer) gconf_key);
+
+	return d;
+}
+
+
+GtkWidget*
+_gtk_message_dialog_with_checkbutton_new (GtkWindow        *parent,
+			 		  GtkDialogFlags    flags,
+			 		  const char       *stock_id,
+			 		  const char       *message,
+			 		  const char       *secondary_message,
+			 		  const char       *gconf_key,
+			 		  const char       *check_button_label,
+			 		  const char       *first_button_text,
+			 		  ...)
+{
+	GtkWidget  *d;
+	GtkWidget  *label;
+	GtkWidget  *image;
+	GtkWidget  *hbox;
+	GtkWidget  *check_button;
+	va_list     args;
+	const char *text;
+	int         response_id;
+	char       *escaped_message, *markup_text;
+
+	g_return_val_if_fail (message != NULL, NULL);
+
+	if (stock_id == NULL)
+		stock_id = GTK_STOCK_DIALOG_INFO;
+
+	d = gtk_dialog_new_with_buttons ("", parent, flags, NULL);
+	gtk_window_set_resizable (GTK_WINDOW (d), FALSE);
+
+	gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
+	gtk_container_set_border_width (GTK_CONTAINER (d), 6);
+	gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (d))), 6);
+	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))), 8);
+
+	/* Add label and image */
+
+	image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
+	gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+	label = gtk_label_new ("");
+
+	escaped_message = g_markup_escape_text (message, -1);
+	if (secondary_message != NULL) {
+		char *escaped_secondary_message = g_markup_escape_text (secondary_message, -1);
+		markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+					       escaped_message,
+					       escaped_secondary_message);
+		g_free (escaped_secondary_message);
+	} else
+		markup_text = g_strdup (escaped_message);
+	gtk_label_set_markup (GTK_LABEL (label), markup_text);
+	g_free (markup_text);
+	g_free (escaped_message);
+
+	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+	gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+	hbox = gtk_hbox_new (FALSE, 6);
+	gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+
+	gtk_box_pack_start (GTK_BOX (hbox), image,
+			    FALSE, FALSE, 0);
+
+	gtk_box_pack_start (GTK_BOX (hbox), label,
+			    TRUE, TRUE, 0);
+
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
+			    hbox,
+			    FALSE, FALSE, 0);
+
+	gtk_widget_show_all (hbox);
+
+	/* Add checkbutton */
+
+	check_button = gtk_check_button_new_with_mnemonic (check_button_label);
+	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (d))),
+			    check_button,
+			    FALSE, FALSE, 0);
+	gtk_widget_show (check_button);
+
+	/* Add buttons */
+
+	if (first_button_text == NULL)
+		return d;
+
+	va_start (args, first_button_text);
+
+	text = first_button_text;
+	response_id = va_arg (args, gint);
+
+	while (text != NULL) {
+		gtk_dialog_add_button (GTK_DIALOG (d), text, response_id);
+
+		text = va_arg (args, gchar*);
+		if (text == NULL)
+			break;
+		response_id = va_arg (args, int);
+	}
+
+	va_end (args);
+
+	gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
+
+	g_signal_connect (G_OBJECT (check_button),
+			  "toggled",
+			  G_CALLBACK (yesno__check_button_toggled_cb),
 			  (gpointer) gconf_key);
-	
+
 	return d;
 }
 
 
 void
-_gtk_error_dialog_from_gerror_run (GtkWindow        *parent,
-				   const char       *main_message,
-				   GError          **gerror)
+_gtk_error_dialog_from_gerror_run (GtkWindow   *parent,
+				   const char  *title,
+				   GError     **gerror)
 {
 	GtkWidget *d;
 
 	g_return_if_fail (*gerror != NULL);
-	g_return_if_fail ((*gerror)->message != NULL);
-
-	if ((*gerror)->message != NULL) {
-		d = _gtk_message_dialog_new (parent,
-					     GTK_DIALOG_MODAL,
-					     GTK_STOCK_DIALOG_ERROR,
-					     main_message,
-					     (*gerror)->message,
-					     GTK_STOCK_OK, GTK_RESPONSE_CANCEL,
-					     NULL);
-		g_signal_connect (G_OBJECT (d), "response",
-				  G_CALLBACK (gtk_widget_destroy),
-				  NULL);
-		gtk_widget_show (d);
-	}
 
+	d = _gtk_message_dialog_new (parent,
+				     GTK_DIALOG_DESTROY_WITH_PARENT,
+				     GTK_STOCK_DIALOG_ERROR,
+				     title,
+				     (*gerror)->message,
+				     GTK_STOCK_OK, GTK_RESPONSE_OK,
+				     NULL);
+	gtk_dialog_run (GTK_DIALOG (d));
+
+	gtk_widget_destroy (d);
 	g_clear_error (gerror);
 }
 
 
+static void
+error_dialog_response_cb (GtkDialog *dialog,
+			  int        response,
+			  gpointer   user_data)
+{
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+
+void
+_gtk_error_dialog_from_gerror_show (GtkWindow   *parent,
+				    const char  *title,
+				    GError     **gerror)
+{
+	GtkWidget *d;
+
+	d = _gtk_message_dialog_new (parent,
+				     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+				     GTK_STOCK_DIALOG_ERROR,
+				     title,
+				     (gerror != NULL) ? (*gerror)->message : NULL,
+				     GTK_STOCK_OK, GTK_RESPONSE_OK,
+				     NULL);
+	g_signal_connect (d, "response", G_CALLBACK (error_dialog_response_cb), NULL);
+
+	gtk_window_present (GTK_WINDOW (d));
+
+	if (gerror != NULL)
+		g_clear_error (gerror);
+}
+
+
+
 void
 _gtk_error_dialog_run (GtkWindow        *parent,
-		       const gchar      *main_message,
 		       const gchar      *format,
 		       ...)
 {
@@ -602,8 +716,8 @@ _gtk_error_dialog_run (GtkWindow        *parent,
 	d =  _gtk_message_dialog_new (parent,
 				      GTK_DIALOG_MODAL,
 				      GTK_STOCK_DIALOG_ERROR,
-				      main_message,
 				      message,
+				      NULL,
 				      GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
 				      NULL);
 	g_free (message);
@@ -646,179 +760,489 @@ _gtk_info_dialog_run (GtkWindow        *parent,
 }
 
 
-void
-_gtk_entry_set_locale_text (GtkEntry   *entry,
-			    const char *text)
+static GdkPixbuf *
+get_themed_icon_pixbuf (GThemedIcon  *icon,
+			int           size,
+			GtkIconTheme *icon_theme)
+{
+	char        **icon_names;
+	GtkIconInfo  *icon_info;
+	GdkPixbuf    *pixbuf;
+	GError       *error = NULL;
+
+	g_object_get (icon, "names", &icon_names, NULL);
+
+	icon_info = gtk_icon_theme_choose_icon (icon_theme, (const char **)icon_names, size, 0);
+	if (icon_info == NULL)
+		icon_info = gtk_icon_theme_lookup_icon (icon_theme, "text-x-generic", size, GTK_ICON_LOOKUP_USE_BUILTIN);
+
+	pixbuf = gtk_icon_info_load_icon (icon_info, &error);
+	if (pixbuf == NULL) {
+		g_warning ("could not load icon pixbuf: %s\n", error->message);
+		g_clear_error (&error);
+	}
+
+	gtk_icon_info_free (icon_info);
+	g_strfreev (icon_names);
+
+	return pixbuf;
+}
+
+
+static GdkPixbuf *
+get_file_icon_pixbuf (GFileIcon *icon,
+		      int        size)
 {
-	char *utf8_text;
+	GFile     *file;
+	char      *filename;
+	GdkPixbuf *pixbuf;
 
-	if (text == NULL)
-		text = "";
+	file = g_file_icon_get_file (icon);
+	filename = g_file_get_path (file);
+	pixbuf = gdk_pixbuf_new_from_file_at_size (filename, size, -1, NULL);
+	g_free (filename);
+	g_object_unref (file);
 
-	utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
-	gtk_entry_set_text (entry, utf8_text);
-	g_free (utf8_text);
+	return pixbuf;
 }
 
 
-char *
-_gtk_entry_get_locale_text (GtkEntry *entry)
+gboolean
+scale_keeping_ratio_min (int      *width,
+			 int      *height,
+			 int       min_width,
+			 int       min_height,
+			 int       max_width,
+			 int       max_height,
+			 gboolean  allow_upscaling)
 {
-	const char *utf8_text;
-	char       *text;
+	double   w = *width;
+	double   h = *height;
+	double   min_w = min_width;
+	double   min_h = min_height;
+	double   max_w = max_width;
+	double   max_h = max_height;
+	double   factor;
+	int      new_width, new_height;
+	gboolean modified;
+
+	if ((*width < max_width) && (*height < max_height) && ! allow_upscaling)
+		return FALSE;
+
+	if (((*width < min_width) || (*height < min_height)) && ! allow_upscaling)
+		return FALSE;
+
+	factor = MAX (MIN (max_w / w, max_h / h), MAX (min_w / w, min_h / h));
+	new_width  = MAX ((int) floor (w * factor + 0.50), 1);
+	new_height = MAX ((int) floor (h * factor + 0.50), 1);
+
+	modified = (new_width != *width) || (new_height != *height);
 
-	utf8_text = gtk_entry_get_text (entry);
-	if (utf8_text == NULL)
+	*width = new_width;
+	*height = new_height;
+
+	return modified;
+}
+
+
+gboolean
+scale_keeping_ratio (int      *width,
+		     int      *height,
+		     int       max_width,
+		     int       max_height,
+		     gboolean  allow_upscaling)
+{
+	return scale_keeping_ratio_min (width,
+					height,
+					0,
+					0,
+					max_width,
+					max_height,
+					allow_upscaling);
+}
+
+
+GdkPixbuf *
+_g_icon_get_pixbuf (GIcon        *icon,
+		    int           size,
+		    GtkIconTheme *theme)
+{
+	GdkPixbuf *pixbuf;
+	int        w, h;
+
+	if (icon == NULL)
 		return NULL;
 
-	text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+	if (G_IS_THEMED_ICON (icon))
+		pixbuf = get_themed_icon_pixbuf (G_THEMED_ICON (icon), size, theme);
+	if (G_IS_FILE_ICON (icon))
+		pixbuf = get_file_icon_pixbuf (G_FILE_ICON (icon), size);
+
+	if (pixbuf == NULL)
+		return NULL;
 
-	return text;
+	w = gdk_pixbuf_get_width (pixbuf);
+	h = gdk_pixbuf_get_height (pixbuf);
+	if (scale_keeping_ratio (&w, &h, size, size, FALSE)) {
+		GdkPixbuf *scaled;
+
+		scaled = gdk_pixbuf_scale_simple (pixbuf, w, h, GDK_INTERP_BILINEAR);
+		g_object_unref (pixbuf);
+		pixbuf = scaled;
+	}
+
+	return pixbuf;
+}
+
+
+GdkPixbuf *
+get_mime_type_pixbuf (const char   *mime_type,
+		      int           icon_size,
+		      GtkIconTheme *icon_theme)
+{
+	GdkPixbuf *pixbuf = NULL;
+	GIcon     *icon;
+
+	if (icon_theme == NULL)
+		icon_theme = gtk_icon_theme_get_default ();
+
+	icon = g_content_type_get_icon (mime_type);
+	pixbuf = _g_icon_get_pixbuf (icon, icon_size, icon_theme);
+	g_object_unref (icon);
+
+	return pixbuf;
+}
+
+
+int
+_gtk_icon_get_pixel_size (GtkWidget   *widget,
+			  GtkIconSize  size)
+{
+	int icon_width, icon_height;
+
+	gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
+					   size,
+					   &icon_width, &icon_height);
+	return MAX (icon_width, icon_height);
 }
 
 
 void
-_gtk_label_set_locale_text (GtkLabel   *label,
-			    const char *text)
+show_help_dialog (GtkWindow  *parent,
+		  const char *section)
 {
-	char *utf8_text;
+	char   *uri;
+	GError *error = NULL;
+
+	uri = g_strconcat ("ghelp:goobox", section ? "?" : NULL, section, NULL);
+	if (! gtk_show_uri (gtk_window_get_screen (parent), uri, GDK_CURRENT_TIME, &error)) {
+  		GtkWidget *dialog;
+
+		dialog = _gtk_message_dialog_new (parent,
+						  GTK_DIALOG_DESTROY_WITH_PARENT,
+						  GTK_STOCK_DIALOG_ERROR,
+						  _("Could not display help"),
+						  error->message,
+						  GTK_STOCK_OK, GTK_RESPONSE_OK,
+						  NULL);
+		gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+		g_signal_connect (G_OBJECT (dialog), "response",
+				  G_CALLBACK (gtk_widget_destroy),
+				  NULL);
+
+		gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
 
-	if (text == NULL)
-		text = "";
+		gtk_widget_show (dialog);
 
-	utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
-	gtk_label_set_text (label, utf8_text);
-	g_free (utf8_text);
+		g_clear_error (&error);
+	}
+	g_free (uri);
 }
 
 
-char *
-_gtk_label_get_locale_text (GtkLabel *label)
+void
+_gtk_container_remove_children (GtkContainer *container,
+				gpointer      start_after_this,
+				gpointer      stop_before_this)
 {
-	const char *utf8_text;
-	char       *text;
+	GList *children;
+	GList *scan;
 
-	utf8_text = gtk_label_get_text (label);
-	if (utf8_text == NULL)
-		return NULL;
+	children = gtk_container_get_children (container);
+
+	if (start_after_this != NULL) {
+		for (scan = children; scan; scan = scan->next)
+			if (scan->data == start_after_this) {
+				scan = scan->next;
+				break;
+			}
+	}
+	else
+		scan = children;
+
+	for (/* void */; scan && (scan->data != stop_before_this); scan = scan->next)
+		gtk_container_remove (container, scan->data);
+	g_list_free (children);
+}
+
+
+int
+_gtk_container_get_pos (GtkContainer *container,
+			GtkWidget    *child)
+{
+	GList    *children;
+	gboolean  found = FALSE;
+	int       idx;
+	GList    *scan;
+
+	children = gtk_container_get_children (container);
+	for (idx = 0, scan = children; ! found && scan; idx++, scan = scan->next) {
+		if (child == scan->data) {
+			found = TRUE;
+			break;
+		}
+	}
+	g_list_free (children);
+
+	return ! found ? -1 : idx;
+}
+
+
+guint
+_gtk_container_get_n_children (GtkContainer *container)
+{
+	GList *children;
+	guint  len;
+
+	children = gtk_container_get_children (container);
+	len = g_list_length (children);
+	g_list_free (children);
+
+	return len;
+}
+
+
+GtkBuilder *
+_gtk_builder_new_from_file (const char *ui_file,
+			    const char *extension)
+{
+	char       *filename;
+	GtkBuilder *builder;
+	GError     *error = NULL;
+
+	filename = g_build_filename (GOO_UIDIR, ui_file, NULL);
+	builder = gtk_builder_new ();
+	if (! gtk_builder_add_from_file (builder, filename, &error)) {
+		g_warning ("%s\n", error->message);
+		g_clear_error (&error);
+	}
+	g_free (filename);
+
+	return builder;
+}
+
+
+GtkWidget *
+_gtk_builder_get_widget (GtkBuilder *builder,
+			 const char *name)
+{
+	return (GtkWidget *) gtk_builder_get_object (builder, name);
+}
 
-	text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
 
-	return text;
+GtkWidget *
+_gtk_combo_box_new_with_texts (const char *first_text,
+			       ...)
+{
+	GtkWidget  *combo_box;
+	va_list     args;
+	const char *text;
+
+	combo_box = gtk_combo_box_new_text ();
+
+	va_start (args, first_text);
+
+	text = first_text;
+	while (text != NULL) {
+		gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), text);
+		text = va_arg (args, const char *);
+	}
+
+	va_end (args);
+
+	return combo_box;
 }
 
 
 void
-_gtk_entry_set_filename_text (GtkEntry   *entry,
-			      const char *text)
+_gtk_combo_box_append_texts (GtkComboBox *combo_box,
+			     const char  *first_text,
+			     ...)
 {
-	char *utf8_text;
+	va_list     args;
+	const char *text;
 
-	if (text == NULL)
-		text = "";
+	va_start (args, first_text);
 
-	utf8_text = g_filename_to_utf8 (text, -1, NULL, NULL, NULL);
-	gtk_entry_set_text (entry, utf8_text);
-	g_free (utf8_text);
+	text = first_text;
+	while (text != NULL) {
+		gtk_combo_box_append_text (combo_box, text);
+		text = va_arg (args, const char *);
+	}
+
+	va_end (args);
 }
 
 
-char *
-_gtk_entry_get_filename_text (GtkEntry   *entry)
+GtkWidget *
+_gtk_image_new_from_xpm_data (char * xpm_data[])
 {
-	const char *utf8_text;
-	char       *text;
+	GdkPixbuf *pixbuf;
+	GtkWidget *image;
 
-	utf8_text = gtk_entry_get_text (entry);
-	if (utf8_text == NULL)
-		return NULL;
+	pixbuf = gdk_pixbuf_new_from_xpm_data ((const char**) xpm_data);
+	image = gtk_image_new_from_pixbuf (pixbuf);
+	gtk_widget_show (image);
 
-	text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+	g_object_unref (G_OBJECT (pixbuf));
 
-	return text;
+	return image;
+}
+
+
+GtkWidget *
+_gtk_image_new_from_inline (const guint8 *data)
+{
+	GdkPixbuf *pixbuf;
+	GtkWidget *image;
+
+	pixbuf = gdk_pixbuf_new_from_inline (-1, data, FALSE, NULL);
+	image = gtk_image_new_from_pixbuf (pixbuf);
+	gtk_widget_show (image);
+
+	g_object_unref (G_OBJECT (pixbuf));
+
+	return image;
 }
 
 
 void
-_gtk_label_set_filename_text (GtkLabel   *label,
-			      const char *text)
+_gtk_widget_get_screen_size (GtkWidget *widget,
+			     int       *width,
+			     int       *height)
 {
-	char *utf8_text;
+	GdkScreen    *screen;
+	GdkRectangle  screen_geom;
 
-	if (text == NULL)
-		text = "";
+	screen = gtk_widget_get_screen (widget);
+	gdk_screen_get_monitor_geometry (screen,
+					 gdk_screen_get_monitor_at_window (screen, widget->window),
+					 &screen_geom);
 
-	utf8_text = g_filename_to_utf8 (text, -1, NULL, NULL, NULL);
-	gtk_label_set_text (label, utf8_text);
-	g_free (utf8_text);
+	*width = screen_geom.width;
+	*height = screen_geom.height;
 }
 
 
-char *
-_gtk_label_get_filename_text (GtkLabel   *label)
+void
+_gtk_tree_path_list_free (GList *list)
 {
-	const char *utf8_text;
-	char       *text;
+	g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
+	g_list_free (list);
+}
 
-	utf8_text = gtk_label_get_text (label);
-	if (utf8_text == NULL)
-		return NULL;
 
-	text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+int
+_gtk_paned_get_position2 (GtkPaned *paned)
+{
+	GtkRequisition requisition;
+	int            pos;
+	int            size;
+
+	if (! GTK_WIDGET_VISIBLE (paned))
+		return 0;
+
+	pos = gtk_paned_get_position (paned);
 
-	return text;
+	gtk_window_get_size (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (paned))), &(requisition.width), &(requisition.height));
+	if (gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_HORIZONTAL)
+		size = requisition.width;
+	else
+		size = requisition.height;
+
+	return size - pos;
 }
 
 
-gboolean
-exec_command (const char *application, 
-	      GList      *file_list)
+void
+_gtk_paned_set_position2 (GtkPaned *paned,
+			  int       pos)
 {
-        GString *command;
-        GList   *scan;
-	GError  *err = NULL;
-	gboolean error;
+	GtkWidget *top_level;
+	int        size;
+
+	top_level = gtk_widget_get_toplevel (GTK_WIDGET (paned));
+	if (gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_HORIZONTAL)
+		size = top_level->allocation.width;
+	else
+		size = top_level->allocation.height;
+
+	if (pos > 0)
+		gtk_paned_set_position (paned, size - pos);
+}
 
-	command = g_string_new ("");
-	g_string_append (command, application);
-        for (scan = file_list; scan; scan = scan->next) {
-		char *filename = scan->data;
-		char *e_filename;
 
-		g_string_append_c (command, ' ');
-		e_filename = shell_escape (filename);
-		g_string_append (command, e_filename);
+void
+_g_launch_command (GtkWidget  *parent,
+		   const char *command,
+		   const char *name,
+		   GList      *files)
+{
+	GError              *error = NULL;
+	GAppInfo            *app_info;
+	GdkAppLaunchContext *launch_context;
+
+	app_info = g_app_info_create_from_commandline (command, name, G_APP_INFO_CREATE_SUPPORTS_URIS, &error);
+	if (app_info == NULL) {
+		_gtk_error_dialog_from_gerror_show(GTK_WINDOW (parent), _("Could not launch the application"), &error);
+		return;
+	}
 
-		g_free (e_filename);
-        }
+	launch_context = gdk_app_launch_context_new ();
+	gdk_app_launch_context_set_screen (launch_context, gtk_widget_get_screen (parent));
 
-	error = (! g_spawn_command_line_async (command->str, &err) || (err != NULL));
-	if (error)
-		_gtk_error_dialog_from_gerror_run (NULL, _("Could not execute command"), &err);
-	g_string_free (command, TRUE);
+	if (! g_app_info_launch (app_info, files, G_APP_LAUNCH_CONTEXT (launch_context), &error)) {
+		_gtk_error_dialog_from_gerror_show(GTK_WINDOW (parent), _("Could not launch the application"), &error);
+		return;
+	}
 
-	return ! error;
+	g_object_unref (app_info);
 }
 
 
 static void
 count_selected (GtkTreeModel *model,
-                GtkTreePath  *path,
-                GtkTreeIter  *iter,
-                gpointer      data)
+		GtkTreePath  *path,
+		GtkTreeIter  *iter,
+		gpointer      data)
 {
-        int *n = data;
-        *n = *n + 1;
+	int *n = data;
+	*n = *n + 1;
 }
 
 
 int
 _gtk_count_selected (GtkTreeSelection *selection)
 {
-        int n = 0;
+	int n;
+
+	if (selection == NULL)
+		return 0;
 
-        if (selection == NULL)
-                return 0;
-        gtk_tree_selection_selected_foreach (selection, count_selected, &n);
-        return n;
+	n = 0;
+	gtk_tree_selection_selected_foreach (selection, count_selected, &n);
+
+	return n;
 }
+
diff --git a/src/gtk-utils.h b/src/gtk-utils.h
index dfd5e31..1d50de7 100644
--- a/src/gtk-utils.h
+++ b/src/gtk-utils.h
@@ -1,9 +1,9 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
 /*
- *  GThumb
+ *  File-Roller
  *
- *  Copyright (C) 2001 The Free Software Foundation, Inc.
+ *  Copyright (C) 2001-2008 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,87 +20,116 @@
  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
  */
 
-#ifndef _GTK_UTILS_H
-#define _GTK_UTILS_H
-
-#include <gtk/gtkwidget.h>
-
-GtkWidget * _gtk_image_new_from_xpm_data (char             *xpm_data[]);
-GtkWidget * _gtk_image_new_from_inline   (const guint8     *data);
-
-GtkWidget*  _gtk_message_dialog_new      (GtkWindow        *parent,
-					  GtkDialogFlags    flags,
-					  const char       *stock_id,
-					  const char       *message,
-					  const char       *secondary_message,
-					  const char       *first_button_text,
-					  ...);
-
-gchar*      _gtk_request_dialog_run      (GtkWindow        *parent,
-					  GtkDialogFlags    flags,
-					  const char       *message,
-					  const char       *default_value,
-					  int               max_length,
-					  const char       *no_button_text,
-					  const char       *yes_button_text);
-
-GtkWidget*  _gtk_yesno_dialog_new        (GtkWindow        *parent,
-					  GtkDialogFlags    flags,
-					  const char       *message,
-					  const char       *no_button_text,
-					  const char       *yes_button_text);
-
-GtkWidget*  _gtk_yesno_dialog_with_checkbutton_new (GtkWindow        *parent,
-						    GtkDialogFlags    flags,
-						    const char       *message,
-						    const char       *no_button_text,
-						    const char       *yes_button_text,
-						    const char       *check_button_label,
-						    const char       *gconf_key);
-
-GtkWidget*  _gtk_ok_dialog_with_checkbutton_new (GtkWindow        *parent,
-						 GtkDialogFlags    flags,
-						 const char       *message,
-						 const char       *ok_button_text,
-						 const char       *check_button_label,
-						 const char       *gconf_key);
-
-void        _gtk_error_dialog_from_gerror_run  (GtkWindow        *parent,
-						const char       *main_message,
-						GError          **gerror);
-
-void        _gtk_error_dialog_run        (GtkWindow        *parent,
-					  const gchar      *main_message,
-					  const gchar      *format,
-					  ...) G_GNUC_PRINTF (3, 4);
-
-void        _gtk_info_dialog_run         (GtkWindow        *parent,
-					  const gchar      *format,
-					  ...) G_GNUC_PRINTF (2, 3);
-
-void        _gtk_entry_set_locale_text   (GtkEntry   *entry,
-					  const char *text);
-
-char *      _gtk_entry_get_locale_text   (GtkEntry   *entry);
-
-void        _gtk_label_set_locale_text   (GtkLabel   *label,
-					  const char *text);
-
-char *      _gtk_label_get_locale_text   (GtkLabel   *label);
-
-void        _gtk_entry_set_filename_text (GtkEntry   *entry,
-					  const char *text);
-
-char *      _gtk_entry_get_filename_text (GtkEntry   *entry);
-
-void        _gtk_label_set_filename_text (GtkLabel   *label,
-					  const char *text);
-
-char *      _gtk_label_get_filename_text (GtkLabel   *label);
-
-gboolean    exec_command                 (const char *application, 
-					  GList      *file_list);
-
-int         _gtk_count_selected          (GtkTreeSelection *selection);
-
-#endif /* _GTK_UTILS_H */
+#ifndef GTK_UTILS_H
+#define GTK_UTILS_H
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GtkWidget*  _gtk_message_dialog_new        (GtkWindow        *parent,
+					    GtkDialogFlags    flags,
+					    const char       *stock_id,
+					    const char       *message,
+					    const char       *secondary_message,
+					    const char       *first_button_text,
+					    ...);
+GtkWidget*
+_gtk_message_dialog_with_checkbutton_new   (GtkWindow        *parent,
+			 		    GtkDialogFlags    flags,
+			 		    const char       *stock_id,
+			 		    const char       *message,
+			 		    const char       *secondary_message,
+			 		    const char       *gconf_key,
+			 		    const char       *check_button_label,
+			 		    const char       *first_button_text,
+			 		    ...);
+gchar*      _gtk_request_dialog_run        (GtkWindow        *parent,
+					    GtkDialogFlags    flags,
+					    const char       *message,
+					    const char       *default_value,
+					    int               max_length,
+					    const char       *no_button_text,
+					    const char       *yes_button_text);
+GtkWidget*  _gtk_yesno_dialog_new          (GtkWindow        *parent,
+					    GtkDialogFlags    flags,
+					    const char       *message,
+					    const char       *no_button_text,
+					    const char       *yes_button_text);
+GtkWidget*
+_gtk_yesno_dialog_with_checkbutton_new     (GtkWindow        *parent,
+					    GtkDialogFlags    flags,
+					    const char       *message,
+					    const char       *no_button_text,
+					    const char       *yes_button_text,
+					    const char       *check_button_label,
+					    const char       *gconf_key);
+GtkWidget*
+_gtk_ok_dialog_with_checkbutton_new        (GtkWindow        *parent,
+					    GtkDialogFlags    flags,
+					    const char       *message,
+					    const char       *ok_button_text,
+					    const char       *check_button_label,
+					    const char       *gconf_key);
+void
+_gtk_error_dialog_from_gerror_run          (GtkWindow        *parent,
+					    const char       *title,
+					    GError          **gerror);
+void
+_gtk_error_dialog_from_gerror_show         (GtkWindow        *parent,
+					    const char       *title,
+					    GError          **gerror);
+void        _gtk_error_dialog_run          (GtkWindow        *parent,
+					    const gchar      *format,
+					    ...) G_GNUC_PRINTF (2, 3);
+void        _gtk_info_dialog_run           (GtkWindow        *parent,
+					    const gchar      *format,
+					    ...) G_GNUC_PRINTF (2, 3);
+GdkPixbuf * _g_icon_get_pixbuf             (GIcon            *icon,
+		 			    int               size,
+		 			    GtkIconTheme     *icon_theme);
+GdkPixbuf * get_mime_type_pixbuf           (const char       *mime_type,
+					    int               icon_size,
+					    GtkIconTheme     *icon_theme);
+int         _gtk_icon_get_pixel_size       (GtkWidget        *widget,
+					    GtkIconSize       size);
+void        show_help_dialog               (GtkWindow        *parent,
+					    const char       *section);
+void        _gtk_container_remove_children
+					   (GtkContainer     *container,
+					    gpointer          start_after_this,
+			   		    gpointer          stop_before_this);
+int         _gtk_container_get_pos         (GtkContainer     *container,
+					    GtkWidget        *child);
+guint       _gtk_container_get_n_children  (GtkContainer     *container);
+GtkBuilder *
+	    _gtk_builder_new_from_file     (const char       *filename,
+					    const char       *extension);
+GtkWidget *
+	    _gtk_builder_get_widget        (GtkBuilder       *builder,
+			 		    const char       *name);
+GtkWidget * _gtk_combo_box_new_with_texts  (const char       *first_text,
+					    ...);
+void        _gtk_combo_box_append_texts    (GtkComboBox      *combo_box,
+					    const char       *first_text,
+					    ...);
+GtkWidget * _gtk_image_new_from_xpm_data   (char             *xpm_data[]);
+GtkWidget * _gtk_image_new_from_inline     (const guint8     *data);
+void        _gtk_widget_get_screen_size    (GtkWidget        *widget,
+					    int              *width,
+					    int              *height);
+void        _gtk_tree_path_list_free       (GList            *list);
+int         _gtk_paned_get_position2       (GtkPaned         *paned);
+void        _gtk_paned_set_position2       (GtkPaned         *paned,
+					    int               pos);
+void        _g_launch_command              (GtkWidget        *parent,
+					    const char       *command,
+					    const char       *name,
+					    GList            *files);
+int         _gtk_count_selected            (GtkTreeSelection *selection);
+
+G_END_DECLS
+
+#endif
diff --git a/src/main.c b/src/main.c
index d18296d..573c178 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3,7 +3,7 @@
 /*
  *  Goo
  *
- *  Copyright (C) 2004 Free Software Foundation, Inc.
+ *  Copyright (C) 2004-2009 Free Software Foundation, Inc.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -21,39 +21,44 @@
  */
 
 #include <config.h>
-#include <gnome.h>
-#include <libbonobo.h>
-#include <libgnomeui/gnome-window-icon.h>
-#include <libgnomevfs/gnome-vfs-init.h>
-#include <libgnomevfs/gnome-vfs-utils.h>
-#include <glade/glade.h>
+#include <brasero/brasero-medium-monitor.h>
 #include <gst/gst.h>
-#include "file-utils.h"
+#include <glib.h>
+#include <unique/unique.h>
+#include "eggsmclient.h"
 #include "goo-stock.h"
 #include "gconf-utils.h"
 #include "goo-window.h"
 #include "typedefs.h"
 #include "preferences.h"
 #include "main.h"
-#include "goo-application.h"
 #include "gtk-utils.h"
 #include "glib-utils.h"
-#include "cd-drive.h"
-
-#ifdef HAVE_MMKEYS
-#include <X11/Xlib.h>
-#include <X11/XF86keysym.h>
-#include <gdk/gdkx.h>
-#endif /* HAVE_MMKEYS */
-
-#ifdef HAVE_LIBNOTIFY
 
+#ifdef ENABLE_NOTIFICATION
 #include <libnotify/notify.h>
 static NotifyNotification *notification = NULL;
+#endif /* ENABLE_NOTIFICATION */
+
+#define VOLUME_STEP 10 /* FIXME */
+
+enum {
+	COMMAND_UNUSED,
+	COMMAND_PLAY,
+	COMMAND_PLAY_PAUSE,
+	COMMAND_STOP,
+	COMMAND_NEXT_TRACK,
+	COMMAND_PREVIOUS_TRACK,
+	COMMAND_EJECT_DISK,
+	COMMAND_HIDE_SHOW,
+	COMMAND_VOLUME_UP,
+	COMMAND_vOLUME_DOWN,
+	COMMAND_QUIT,
+	COMMAND_PRESENT,
+	COMMAND_SET_DEVICE
+};
 
-#endif /* HAVE_LIBNOTIFY */
-
-GtkWindow *main_window = NULL;
+GtkWidget *main_window = NULL;
 int        AutoPlay = FALSE;
 int        PlayPause = FALSE;
 int        Stop = FALSE;
@@ -64,20 +69,13 @@ int        HideShow = FALSE;
 int        VolumeUp = FALSE;
 int        VolumeDown = FALSE;
 int        Quit = FALSE;
-GList     *Drives = NULL;
-
-static void     prepare_app         (void);
-static void     initialize_data     (void);
-static void     release_data        (void);
 
-static void     init_session        (char **argv);
-static gboolean session_is_restored (void);
-static gboolean load_session        (void);
-static void     init_mmkeys         (void);
+static void release_data (void);
 
-static char           *default_device = NULL;
-static BonoboObject   *goo_application = NULL;
-static GOptionContext *context = NULL;
+static UniqueApp  *application = NULL;
+static const char *program_argv0; /* argv[0] from main(); used as the command to restart the program */
+static char       *default_device = NULL;
+static gboolean    version = FALSE;
 
 static const GOptionEntry options[] = {
 	{ "device", 'd',  0, G_OPTION_ARG_STRING, &default_device, 
@@ -113,238 +111,209 @@ static const GOptionEntry options[] = {
 	{ "quit", '\0', 0, G_OPTION_ARG_NONE, &Quit,
           N_("Quit the application"),
           0 },
-          
+
+          { "version", 'v', 0, G_OPTION_ARG_NONE, &version,
+  	  N_("Show version"), NULL },
+
 	{ NULL }
 };
 
 
-/* -- Main -- */
+/* session management */
 
 
-int main (int    argc, 
-	  char **argv)
+static void
+goo_save_state (EggSMClient *client,
+		GKeyFile    *state,
+		gpointer     user_data)
 {
-	GnomeProgram *program;
-	char         *description;
-	CORBA_Object  factory;
+	const char *argv[2] = { NULL };
 
-	if (! g_thread_supported ()) 
-		g_thread_init (NULL);
+	argv[0] = program_argv0;
+	argv[1] = NULL;
+	egg_sm_client_set_restart_command (client, 1, argv);
 
-	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
-	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-	textdomain (GETTEXT_PACKAGE);
-	
-        description = g_strdup_printf ("- %s", _("Play CDs and save the tracks to disk as files"));
-        context = g_option_context_new (description);
-        g_free (description);
-        g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
-	g_option_context_add_group (context, gst_init_get_option_group ());
-  	g_option_context_set_ignore_unknown_options (context, TRUE);
-  
-	program = gnome_program_init ("goobox", VERSION,
-			              LIBGNOMEUI_MODULE, argc, argv,
-			              GNOME_PARAM_GOPTION_CONTEXT, context,
-			              GNOME_PARAM_HUMAN_READABLE_NAME, _("CD Player"),
-				      GNOME_PARAM_APP_PREFIX, GOO_PREFIX,
-                                      GNOME_PARAM_APP_SYSCONFDIR, GOO_SYSCONFDIR,
-                                      GNOME_PARAM_APP_DATADIR, GOO_DATADIR,
-                                      GNOME_PARAM_APP_LIBDIR, GOO_LIBDIR,
-				      NULL);
-
-        if (! g_thread_supported ()) {
-                g_thread_init (NULL);
-                gdk_threads_init ();
-        }
-
-	if (! gnome_vfs_init ()) 
-                g_error ("Cannot initialize the Virtual File System.");
-	gnome_authentication_manager_init ();
-	glade_gnome_init ();
-	
-#ifdef HAVE_LIBNOTIFY
+	g_key_file_set_string (state, "Session", "/device", goo_player_get_device (goo_window_get_player (GOO_WINDOW (main_window))));
+}
 
-	if (! notify_init ("goobox")) 
-                g_warning ("Cannot initialize notification system.");
-                
-#endif /* HAVE_LIBNOTIFY */
 
-	factory = bonobo_activation_activate_from_id ("OAFIID:GNOME_Goobox_Application_Factory",
-						      Bonobo_ACTIVATION_FLAG_EXISTING_ONLY,
-						      NULL, NULL);
+static void
+goo_session_manager_init (void)
+{
+	EggSMClient *client = NULL;
 
-	if (factory != NULL) {
-		CORBA_Environment        env;
-		GNOME_Goobox_Application app;
-		
-		CORBA_exception_init (&env);
+	client = egg_sm_client_get ();
+	g_signal_connect (client, "save-state", G_CALLBACK (goo_save_state), NULL);
+}
 
-		app = bonobo_activation_activate_from_id ("OAFIID:GNOME_Goobox_Application", 0, NULL, &env);
 
-                if (AutoPlay)
-                	GNOME_Goobox_Application_play (app, &env);
-		else if (PlayPause)
-                	GNOME_Goobox_Application_play_pause (app, &env);
-                else if (Stop)
-                	GNOME_Goobox_Application_stop (app, &env);
-		else if (Next)
-                	GNOME_Goobox_Application_next (app, &env);
-		else if (Prev)
-                	GNOME_Goobox_Application_prev (app, &env);
-		else if (Eject)
-                	GNOME_Goobox_Application_eject (app, &env);
-		else if (HideShow)
-                	GNOME_Goobox_Application_hide_show (app, &env);
-		else if (VolumeUp)
-                	GNOME_Goobox_Application_volume_up (app, &env);
-		else if (VolumeDown)
-                	GNOME_Goobox_Application_volume_down (app, &env);
-		else if (Quit)
-                	GNOME_Goobox_Application_quit (app, &env);
-		else
-			GNOME_Goobox_Application_present (app, &env);
+static void
+goo_restore_session (EggSMClient *client)
+{
+	GKeyFile     *state = NULL;
+	char         *device;
+	BraseroDrive *drive;
 
-		bonobo_object_release_unref (app, &env);
-		CORBA_exception_free (&env);
+	state = egg_sm_client_get_state_file (client);
 
-		gdk_notify_startup_complete ();
+	device = g_key_file_get_string (state, "Session", "device", NULL);
+	drive = main_get_drive_for_device (device);
+	main_window = goo_window_new (drive);
+	gtk_widget_show (main_window);
 
-		exit (0);
-	}
+	g_object_unref (drive);
+	g_free (device);
+}
 
-	goo_stock_init ();
-	init_session (argv);
-	initialize_data ();
-	prepare_app ();
 
-	bonobo_main ();
+static UniqueResponse
+unique_app_message_received_cb (UniqueApp         *unique_app,
+				UniqueCommand      command,
+				UniqueMessageData *message,
+				guint              time_,
+				gpointer           user_data)
+{
+	UniqueResponse  res;
 
-	release_data ();
+	res = UNIQUE_RESPONSE_OK;
 
-	return 0;
-}
+	switch (command) {
+	case UNIQUE_OPEN:
+	case UNIQUE_NEW:
+		/* FIXME */
+		break;
 
+	case COMMAND_PLAY:
+		goo_window_play (GOO_WINDOW (main_window));
+		break;
 
-/* Initialize application data. */
+	case COMMAND_PLAY_PAUSE:
+		goo_window_toggle_play (GOO_WINDOW (main_window));
+		break;
 
+	case COMMAND_STOP:
+		goo_window_stop (GOO_WINDOW (main_window));
+		break;
 
-static void 
-initialize_data (void)
-{
-        g_set_application_name (_("CD Player"));
-        gtk_window_set_default_icon_name ("goobox");
+	case COMMAND_NEXT_TRACK:
+		goo_window_next (GOO_WINDOW (main_window));
+		break;
 
-	eel_gconf_monitor_add ("/apps/goobox");
-	
-	Drives = scan_for_cdroms (FALSE, FALSE);
-}
+	case COMMAND_PREVIOUS_TRACK:
+		goo_window_prev (GOO_WINDOW (main_window));
+		break;
 
+	case COMMAND_EJECT_DISK:
+		goo_window_eject (GOO_WINDOW (main_window));
+		break;
 
-static void 
-release_data (void)
-{
-	if (goo_application != NULL)
-		bonobo_object_unref (goo_application);
-	eel_global_client_free ();
-	
-	if (Drives != NULL) {
-		g_list_foreach (Drives, (GFunc) cd_drive_free, NULL);
-		g_list_free (Drives);
-		Drives = NULL;
-	}
-}
+	case COMMAND_HIDE_SHOW:
+		goo_window_toggle_visibility (GOO_WINDOW (main_window));
+		break;
 
+	case COMMAND_VOLUME_UP:
+		{
+			int volume;
 
-CDDrive * 
-get_drive_from_device (const char *device)
-{
-	CDDrive    *result = NULL;
-	char       *resolved_device = NULL;
-	char       *resolved_real_device = NULL;
-	const char *real_device = NULL;
-	GList      *scan;
-	
-	if (device == NULL)
-		return NULL;
-	
-	if (resolve_all_symlinks (device, &resolved_device) != GNOME_VFS_OK)
-		resolved_device = NULL;
+			volume = goo_window_get_volume (GOO_WINDOW (main_window));
+			goo_window_set_volume (GOO_WINDOW (main_window), volume + VOLUME_STEP);
+		}
+		break;
 
-	if (resolved_device == NULL)
-		return NULL;
-	
-	device = get_path_from_uri (resolved_device);
-	for (scan = Drives; scan; scan = scan->next) {
-		CDDrive *drive = scan->data;
-		
-		if (drive->device == NULL)
-			continue;
-		if (resolve_all_symlinks (drive->device, &resolved_real_device) != GNOME_VFS_OK)
-			continue;
-		real_device = get_path_from_uri (resolved_real_device);
-		if (strcmp (real_device, device) == 0) {
-			result = drive;
-			break;
+	case COMMAND_vOLUME_DOWN:
+		{
+			int volume;
+
+			volume = goo_window_get_volume (GOO_WINDOW (main_window));
+			goo_window_set_volume (GOO_WINDOW (main_window), volume - VOLUME_STEP);
 		}
-	}
-	
-	g_free (resolved_device);
-	
-	return result;
-}
+		break;
 
+	case COMMAND_QUIT:
+		goo_window_close (GOO_WINDOW (main_window));
+		break;
 
-GtkWindow *
-get_window_from_device (const char *device)
-{
-	CDDrive *device_drive;
-	GList   *scan;
-	
-	device_drive = get_drive_from_device (device);
-	if (device_drive == NULL)
-		return NULL;
-		
-	for (scan = window_list; scan; scan = scan->next) {
-		GooWindow *window = scan->data;
+	case COMMAND_PRESENT:
+		if (GTK_WIDGET_VISIBLE (main_window))
+			gtk_window_present (GTK_WINDOW (main_window));
+		else
+			goo_window_toggle_visibility (GOO_WINDOW (main_window));
+		break;
 
-		if (goo_player_get_drive (goo_window_get_player (window)) == device_drive)
-			return (GtkWindow *) window;
-	}
-	
-	return NULL;
-}
+	case COMMAND_SET_DEVICE:
+		{
+			char *device;
 
+			device = unique_message_data_get_text (message);
+			if (*device == '\0')
+				device = NULL;
 
-/* Create the windows. */
+			if (device != NULL) {
+				/* FIXME
+				GooPlayer *player;
+				CDDrive   *current_drive;
+
+				player = goo_window_get_player (GOO_WINDOW (main_window));
+				current_drive = goo_player_get_drive (player);
+
+				if (current_drive == NULL) {
+					main_window = get_window_from_device (device);
+					if (main_window == NULL)
+						main_window = goo_window_new (device);
+				}
+				else
+					goo_window_set_device (GOO_WINDOW (main_window), device);
+				*/
+			}
+		}
+		break;
+
+	default:
+		res = UNIQUE_RESPONSE_PASSTHROUGH;
+		break;
+	}
+
+	return res;
+}
 
 
 static gboolean
-check_plugins (void)
+required_gstreamer_plugins_available (void)
 {
-	char *required_plugins[] = { "cdparanoiasrc", "gnomevfssink", "audioconvert", "volume" };
+	char *required_plugins[] = { "cdparanoiasrc", "audioconvert", "volume", "giosink" };
 	int   i;
 
 	for (i = 0; i < G_N_ELEMENTS (required_plugins); i++) {
 		GstElement *element;
 		gboolean    present;
-		
+
 		element = gst_element_factory_make (required_plugins[i], NULL);
 		present = (element != NULL);
-		if (element != NULL) 
+		if (element != NULL)
 			gst_object_unref (GST_OBJECT (element));
-	
+
 		if (! present)
 			return FALSE;
-	}	
+	}
 
 	return TRUE;
 }
 
 
-static void 
-prepare_app (void)
+static void
+release_data (void)
 {
-	if (! check_plugins ()) {
+	_g_object_unref (application);
+	eel_global_client_free ();
+}
+
+
+static void
+prepare_application (void)
+{
+	EggSMClient *client = NULL;
+
+	if (! required_gstreamer_plugins_available ()) {
 		GtkWidget *d;
 		d = _gtk_message_dialog_new (NULL,
 					     0,
@@ -354,289 +323,241 @@ prepare_app (void)
 					     GTK_STOCK_OK, GTK_RESPONSE_OK,
 					     NULL);
 		g_signal_connect (G_OBJECT (d), "response",
-				  G_CALLBACK (bonobo_main_quit),
+				  G_CALLBACK (gtk_main_quit),
 				  NULL);
 		gtk_widget_show (d);
 
 		return;
 	}
-	
-	if (session_is_restored ()) 
-		load_session ();
-	else 
-		main_window = goo_window_new (default_device);
-	gtk_widget_show (GTK_WIDGET (main_window));
-
-	goo_application = goo_application_new (gdk_screen_get_default ());
-
-#ifdef HAVE_MMKEYS
-	init_mmkeys ();
-#endif /* HAVE_MMKEYS */
-}
-
-
-/* SM support */
-
-
-/* The master client we use for SM */
-static GnomeClient *master_client = NULL;
 
-/* argv[0] from main(); used as the command to restart the program */
-static const char *program_argv0 = NULL;
+	application = unique_app_new_with_commands ("org.gnome.goobox", NULL,
+						    "auto-play", COMMAND_PLAY,
+						    "play-pause", COMMAND_PLAY_PAUSE,
+						    "stop", COMMAND_STOP,
+						    "next", COMMAND_NEXT_TRACK,
+						    "prev", COMMAND_PREVIOUS_TRACK,
+						    "eject", COMMAND_EJECT_DISK,
+						    "hide-show", COMMAND_HIDE_SHOW,
+						    "volume-up", COMMAND_VOLUME_UP,
+						    "volume-down", COMMAND_vOLUME_DOWN,
+						    "quit", COMMAND_QUIT,
+						    "present", COMMAND_PRESENT,
+						    "set-device", COMMAND_SET_DEVICE,
+						    NULL);
+
+	if (unique_app_is_running (application)) {
+		if (default_device != NULL) {
+			UniqueMessageData *data;
+
+			data = unique_message_data_new ();
+			unique_message_data_set_text (data, default_device, -1);
+			unique_app_send_message (application, COMMAND_SET_DEVICE, data);
+
+			unique_message_data_free (data);
+		}
 
+		if (AutoPlay)
+			unique_app_send_message (application, COMMAND_PLAY, NULL);
+		else if (PlayPause)
+			unique_app_send_message (application, COMMAND_PLAY_PAUSE, NULL);
+		else if (Stop)
+			unique_app_send_message (application, COMMAND_STOP, NULL);
+		else if (Next)
+			unique_app_send_message (application, COMMAND_NEXT_TRACK, NULL);
+		else if (Prev)
+			unique_app_send_message (application, COMMAND_PREVIOUS_TRACK, NULL);
+		else if (Eject)
+			unique_app_send_message (application, COMMAND_EJECT_DISK, NULL);
+		else if (HideShow)
+			unique_app_send_message (application, COMMAND_HIDE_SHOW, NULL);
+		else if (VolumeUp)
+			unique_app_send_message (application, COMMAND_VOLUME_UP, NULL);
+		else if (VolumeDown)
+			unique_app_send_message (application, COMMAND_vOLUME_DOWN, NULL);
+		else if (Quit)
+			unique_app_send_message (application, COMMAND_QUIT, NULL);
 
-static void
-save_session (GnomeClient *client)
-{
-	const char  *prefix;
+		return;
+	}
 
-	prefix = gnome_client_get_config_prefix (client);
-	gnome_config_push_prefix (prefix);
+	if (! unique_app_is_running (application)) {
+	        g_set_application_name (_("CD Player"));
+	        gtk_window_set_default_icon_name ("goobox");
+	        goo_stock_init ();
+		eel_gconf_monitor_add ("/apps/goobox");
+		g_signal_connect (application,
+				  "message-received",
+				  G_CALLBACK (unique_app_message_received_cb),
+				  NULL);
+	}
 
-	gnome_config_set_string ("Session/device", goo_player_get_device (goo_window_get_player (GOO_WINDOW (main_window))));
+	client = egg_sm_client_get ();
+	if (egg_sm_client_is_resumed (client)) {
+		goo_restore_session (client);
+		return;
+	}
 
-	gnome_config_pop_prefix ();
-	gnome_config_sync ();
+	gtk_widget_show (goo_window_new (NULL));
 }
 
 
-/* save_yourself handler for the master client */
-static gboolean
-client_save_yourself_cb (GnomeClient *client,
-			 gint phase,
-			 GnomeSaveStyle save_style,
-			 gboolean shutdown,
-			 GnomeInteractStyle interact_style,
-			 gboolean fast,
-			 gpointer data)
+int main (int argc, char **argv)
 {
-	const char *prefix;
-	char       *argv[4] = { NULL };
+	char           *description;
+	GOptionContext *context = NULL;
+	GError         *error = NULL;
 
-	save_session (client);
-
-	prefix = gnome_client_get_config_prefix (client);
-
-	/* Tell the session manager how to discard this save */
-
-	argv[0] = "rm";
-	argv[1] = "-rf";
-	argv[2] = gnome_config_get_real_path (prefix);
-	argv[3] = NULL;
-	gnome_client_set_discard_command (client, 3, argv);
+	program_argv0 = argv[0];
 
-	/* Tell the session manager how to clone or restart this instance */
+	if (! g_thread_supported ()) {
+		g_thread_init (NULL);
+		gdk_threads_init ();
+	}
 
-	argv[0] = (char *) program_argv0;
-	argv[1] = NULL; /* "--debug-session"; */
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
 	
-	gnome_client_set_clone_command (client, 1, argv);
-	gnome_client_set_restart_command (client, 1, argv);
-
-	return TRUE;
-}
-
-/* die handler for the master client */
-static void
-client_die_cb (GnomeClient *client, gpointer data)
-{
-	if (! client->save_yourself_emitted)
-		save_session (client);
-	if (goo_application != NULL)
-		bonobo_object_unref (goo_application);
-	bonobo_main_quit ();
-}
-
-
-static void
-init_session (char **argv)
-{
-	if (master_client != NULL)
-		return;
-
-	program_argv0 = argv[0];
+        description = g_strdup_printf ("- %s", _("Play CDs and save the tracks to disk as files"));
+        context = g_option_context_new (description);
+        g_free (description);
+        g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+        g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+	g_option_context_add_group (context, gtk_get_option_group (TRUE));
+	g_option_context_add_group (context, egg_sm_client_get_option_group ());
+	g_option_context_add_group (context, gst_init_get_option_group ());
+  	g_option_context_set_ignore_unknown_options (context, TRUE);
+	if (! g_option_context_parse (context, &argc, &argv, &error)) {
+		g_critical ("Failed to parse arguments: %s", error->message);
+		g_error_free (error);
+		g_option_context_free (context);
+		return EXIT_FAILURE;
+	}
+	g_option_context_free (context);
 
-	master_client = gnome_master_client ();
+	if (version) {
+		g_print ("%s %s, Copyright (C) 2004-2009 Free Software Foundation, Inc.\n", PACKAGE_NAME, PACKAGE_VERSION);
+		return 0;
+	}
 
-	g_signal_connect (master_client, "save_yourself",
-			  G_CALLBACK (client_save_yourself_cb),
-			  NULL);
+#ifdef ENABLE_NOTIFICATION
+	if (! notify_init ("goobox"))
+                g_warning ("Cannot initialize notification system.");
+#endif /* ENABLE_NOTIFICATION */
+	goo_session_manager_init ();
+	prepare_application ();
+
+	if (! unique_app_is_running (application)) {
+		gdk_threads_enter ();
+		gtk_main ();
+		gdk_threads_leave ();
+	}
 
-	g_signal_connect (master_client, "die",
-			  G_CALLBACK (client_die_cb),
-			  NULL);
+	release_data ();
+	
+	return 0;
 }
 
 
-gboolean
-session_is_restored (void)
+/*
+CDDrive * 
+get_drive_from_device (const char *device)
 {
-	gboolean restored;
+	CDDrive    *result = NULL;
+	char       *resolved_device = NULL;
+	char       *resolved_real_device = NULL;
+	const char *real_device = NULL;
+	GList      *scan;
 	
-	if (! master_client)
-		return FALSE;
-
-	restored = (gnome_client_get_flags (master_client) & GNOME_CLIENT_RESTORED) != 0;
+	if (device == NULL)
+		return NULL;
+	
+	if (resolve_all_symlinks (device, &resolved_device) != GNOME_VFS_OK)
+		resolved_device = NULL;
 
-	return restored;
+	if (resolved_device == NULL)
+		return NULL;
+	
+	device = get_path_from_uri (resolved_device);
+	for (scan = Drives; scan; scan = scan->next) {
+		CDDrive *drive = scan->data;
+		
+		if (drive->device == NULL)
+			continue;
+		if (resolve_all_symlinks (drive->device, &resolved_real_device) != GNOME_VFS_OK)
+			continue;
+		real_device = get_path_from_uri (resolved_real_device);
+		if (strcmp (real_device, device) == 0) {
+			result = drive;
+			break;
+		}
+	}
+	
+	g_free (resolved_device);
+	
+	return result;
 }
 
 
-gboolean
-load_session (void)
+GtkWindow *
+get_window_from_device (const char *device)
 {
-	char *device;
-
-	gnome_config_push_prefix (gnome_client_get_config_prefix (master_client));
-
-	device = gnome_config_get_string ("Session/device");
-	main_window = goo_window_new (device);
-	g_free (device);
-
-	gnome_config_pop_prefix ();
-
-	return TRUE;
-}
-
-
-/* From rhythmbox/shell/rb-shell-player.c
- *
- *  Copyright (C) 2002, 2003 Jorn Baayen <jorn nl linux org>
- *  Copyright (C) 2002,2003 Colin Walters <walters debian org>
- *
- *  Modified by Paolo Bacchilega for Goobox
- *
- *  Copyright (C) 2005 Paolo Bacchilega <paobac cvs gnome org>
- */
-
-#ifdef HAVE_MMKEYS
+	CDDrive *device_drive;
+	GList   *scan;
+	
+	device_drive = get_drive_from_device (device);
+	if (device_drive == NULL)
+		return NULL;
+		
+	for (scan = window_list; scan; scan = scan->next) {
+		GooWindow *window = scan->data;
 
-static void
-grab_mmkey (int        key_code, 
-	    GdkWindow *root)
-{
-	gdk_error_trap_push ();
-
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  0,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  Mod2Mask,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  Mod5Mask,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  LockMask,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  Mod2Mask | Mod5Mask,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  Mod2Mask | LockMask,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  Mod5Mask | LockMask,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
-	XGrabKey (GDK_DISPLAY (), key_code,
-		  Mod2Mask | Mod5Mask | LockMask,
-		  GDK_WINDOW_XID (root), True,
-		  GrabModeAsync, GrabModeAsync);
+		if (goo_player_get_drive (goo_window_get_player (window)) == device_drive)
+			return (GtkWindow *) window;
+	}
 	
-	gdk_flush ();
-        if (gdk_error_trap_pop ()) 
-		debug (DEBUG_INFO, "Error grabbing key");
+	return NULL;
 }
+*/
 
 
-static GdkFilterReturn
-filter_mmkeys (GdkXEvent *xevent, 
-	       GdkEvent  *event, 
-	       gpointer   data)
+BraseroDrive *
+main_get_most_likely_drive (void)
 {
-	XEvent    *xev;
-	XKeyEvent *key;
-
-	xev = (XEvent *) xevent;
-	if (xev->type != KeyPress) 
-		return GDK_FILTER_CONTINUE;
+	BraseroDrive         *result;
+	BraseroMediumMonitor *monitor;
+	GSList               *drivers;
+
+	monitor = brasero_medium_monitor_get_default ();
+	drivers = brasero_medium_monitor_get_drives (monitor, BRASERO_MEDIA_TYPE_AUDIO | BRASERO_MEDIA_TYPE_CD);
+	if (drivers != NULL)
+		result = g_object_ref ((BraseroDrive *) drivers->data);
+	else
+		result = NULL;
 
-	key = (XKeyEvent *) xevent;
+	g_slist_foreach (drivers, (GFunc) g_object_unref, NULL);
+	g_slist_free (drivers);
+	g_object_unref (monitor);
 
-	if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay) == key->keycode) {	
-		goo_window_toggle_play (GOO_WINDOW (main_window));
-		return GDK_FILTER_REMOVE;
-	} 
-	else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause) == key->keycode) {	
-		goo_window_pause (GOO_WINDOW (main_window));
-		return GDK_FILTER_REMOVE;
-	} 
-	else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop) == key->keycode) {
-		goo_window_stop (GOO_WINDOW (main_window));
-		return GDK_FILTER_REMOVE;		
-	} 
-	else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev) == key->keycode) {
-		goo_window_prev (GOO_WINDOW (main_window));
-		return GDK_FILTER_REMOVE;		
-	} 
-	else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext) == key->keycode) {
-		goo_window_next (GOO_WINDOW (main_window));
-		return GDK_FILTER_REMOVE;
-	} 
-	else if (XKeysymToKeycode (GDK_DISPLAY (), XF86XK_Eject) == key->keycode) {
-		goo_window_eject (GOO_WINDOW (main_window));
-		return GDK_FILTER_REMOVE;
-	} 
-	else 
-		return GDK_FILTER_CONTINUE;
+	return result;
 }
 
 
-static void
-init_mmkeys (void)
+BraseroDrive *
+main_get_drive_for_device (const char *device)
 {
-	gint        keycodes[] = {0, 0, 0, 0, 0, 0};
-	GdkDisplay *display;
-	GdkScreen  *screen;
-	GdkWindow  *root;
-	guint       i, j;
-
-	keycodes[0] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPlay);
-	keycodes[1] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioStop);
-	keycodes[2] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPrev);
-	keycodes[3] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioNext);
-	keycodes[4] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_AudioPause);
-	keycodes[5] = XKeysymToKeycode (GDK_DISPLAY (), XF86XK_Eject);
-
-	display = gdk_display_get_default ();
-
-	for (i = 0; i < gdk_display_get_n_screens (display); i++) {
-		screen = gdk_display_get_screen (display, i);
-
-		if (screen != NULL) {
-			root = gdk_screen_get_root_window (screen);
-
-			for (j = 0; j < G_N_ELEMENTS (keycodes) ; j++) {
-				if (keycodes[j] != 0)
-					grab_mmkey (keycodes[j], root);
-			}
+	BraseroDrive         *result = NULL;
+	BraseroMediumMonitor *monitor;
 
-			gdk_window_add_filter (root, filter_mmkeys, NULL);
-		}
-	}
-}
+	monitor = brasero_medium_monitor_get_default ();
+	result = brasero_medium_monitor_get_drive (monitor, device);
+	g_object_unref (monitor);
 
-#endif /* HAVE_MMKEYS */
+	return result;
+}
 
 
-#ifdef HAVE_LIBNOTIFY
+#ifdef ENABLE_NOTIFICATION
 
 
 static gboolean
@@ -673,7 +594,7 @@ notify_action_stop_cb (NotifyNotification *notification,
 }
 
 
-#endif /* HAVE_LIBNOTIFY */
+#endif /* ENABLE_NOTIFICATION */
 
 
 void 
@@ -681,7 +602,7 @@ system_notify (GooWindow  *window,
 	       const char *title,
 	       const char *msg)
 {
-#ifdef HAVE_LIBNOTIFY
+#ifdef ENABLE_NOTIFICATION
         GtkWidget *tray_icon;
 	GdkScreen *screen = NULL;
 	int        x = -1, y = -1;
@@ -706,7 +627,7 @@ system_notify (GooWindow  *window,
 	}
 
 	if (notification == NULL) {
-		caps = notify_get_server_caps();
+		caps = notify_get_server_caps ();
 		if (caps != NULL) {
 			for (c = caps; c != NULL; c = c->next) {
 				if (strcmp ((char*)c->data, "actions") == 0) {
@@ -747,5 +668,5 @@ system_notify (GooWindow  *window,
 
 	notify_notification_show (notification, NULL);
 	
-#endif /* HAVE_LIBNOTIFY */
+#endif /* ENABLE_NOTIFICATION */
 }
diff --git a/src/main.h b/src/main.h
index fc25060..ad8d327 100644
--- a/src/main.h
+++ b/src/main.h
@@ -23,19 +23,22 @@
 #ifndef MAIN_H
 #define MAIN_H
 
+#include <brasero/brasero-drive.h>
 #include "goo-window.h"
-#include "cd-drive.h"
 
-extern GtkWindow *main_window;
+extern GtkWidget *main_window;
 extern GList     *window_list;
 extern int        AutoPlay;
 extern int        HideShow;
-extern GList     *Drives;
 
-void        system_notify          (GooWindow  *window,
-	       			    const char *title,
-	       			    const char *msg);
+/*
 CDDrive *   get_drive_from_device  (const char *device);
 GtkWindow * get_window_from_device (const char *device);
+*/
+BraseroDrive *  main_get_most_likely_drive (void);
+BraseroDrive *  main_get_drive_for_device  (const char *device);
+void            system_notify              (GooWindow  *window,
+	       			            const char *title,
+	       			            const char *msg);
 
 #endif /* MAIN_H */
diff --git a/src/preferences.c b/src/preferences.c
index b3ba6a6..50b96d8 100644
--- a/src/preferences.c
+++ b/src/preferences.c
@@ -21,12 +21,10 @@
  */
 
 #include <string.h>
-#include <libgnome/libgnome.h>
 #include <gconf/gconf-client.h>
 #include "typedefs.h"
 #include "preferences.h"
 #include "main.h"
-#include "file-utils.h"
 #include "gconf-utils.h"
 #include "goo-window.h"
 
@@ -251,5 +249,16 @@ preferences_set_sort_type (GtkSortType i_value)
 gboolean
 preferences_get_use_sound_juicer (void)
 {
-	return eel_gconf_get_boolean (PREF_GENERAL_USE_SJ, FALSE) && is_program_in_path (SOUND_JUICER_EXE);
+	char     *path;
+	gboolean  result;
+
+	if (! eel_gconf_get_boolean (PREF_GENERAL_USE_SJ, FALSE))
+		return FALSE;
+
+	path = g_find_program_in_path (SOUND_JUICER_EXE);
+	result = path != NULL;
+
+	g_free (path);
+
+	return result;
 }
diff --git a/src/preferences.h b/src/preferences.h
index 389be42..6508bc6 100644
--- a/src/preferences.h
+++ b/src/preferences.h
@@ -49,7 +49,6 @@
 #define PREF_EXTRACT_DESTINATION      "/apps/goobox/dialogs/extract/destination"
 #define PREF_EXTRACT_FILETYPE         "/apps/goobox/dialogs/extract/file_type"
 #define PREF_EXTRACT_SAVE_PLAYLIST    "/apps/goobox/dialogs/extract/save_playlist"
-#define PREF_EXTRACT_FIRST_TIME       "/apps/goobox/dialogs/extract/first_time"
 #define PREF_RIPPER_VIEW_DISTINATION  "/apps/goobox/dialogs/ripper/view_destination"
 
 #define PREF_ENCODER_OGG_QUALITY      "/apps/goobox/encoder/ogg_quality"
diff --git a/src/track-info.c b/src/track-info.c
index de83eec..3d1c543 100644
--- a/src/track-info.c
+++ b/src/track-info.c
@@ -21,7 +21,7 @@
  */
 
 #include <config.h>
-#include <gnome.h>
+#include <glib/gi18n.h>
 #include <gst/gst.h>
 #include "track-info.h"
 
diff --git a/src/typedefs.h b/src/typedefs.h
index 56a4f5e..9ff7e4c 100644
--- a/src/typedefs.h
+++ b/src/typedefs.h
@@ -41,5 +41,12 @@ typedef enum {
 } WindowSortMethod;
 
 
+typedef void (*DataFunc)         (gpointer    user_data);
+typedef void (*ReadyFunc)        (GError     *error,
+			 	  gpointer    user_data);
+typedef void (*ReadyCallback)    (GObject    *object,
+				  GError     *error,
+			   	  gpointer    user_data);
+
 #endif /* TYPEDEFS_H */
 
diff --git a/src/ui.h b/src/ui.h
index 9c1fd66..4152cc1 100644
--- a/src/ui.h
+++ b/src/ui.h
@@ -25,7 +25,6 @@
 
 
 #include <config.h>
-#include <gnome.h>
 #include "actions.h"
 #include "goo-stock.h"
 
@@ -37,7 +36,7 @@ static GtkActionEntry action_entries[] = {
 	{ "HelpMenu", NULL, N_("_Help") },
 	{ "CDCoverMenu", NULL, N_("C_over") },
 
-	{ "About", GNOME_STOCK_ABOUT,
+	{ "About", GTK_STOCK_ABOUT,
 	  NULL, NULL,
 	  N_("Information about the program"),
 	  G_CALLBACK (activate_action_about) },



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