[atomix] Remove libgnome and bonobo usage.



commit ad936b3e91b370863c721e5d646fec2adcb7d314
Author: Thomas Hindoe Paaboel Andersen <phomes gmail com>
Date:   Mon Mar 22 22:17:05 2010 +0100

    Remove libgnome and bonobo usage.
    
    Highscores still need a bit more love.

 configure.in                      |    5 +-
 src/Makefile.am                   |   17 +-
 src/atomix-ui.xml                 |   70 -----
 src/games-debug.c                 |   53 ++++
 src/games-debug.h                 |   84 ++++++
 src/games-gtk-compat.h            |   44 +++
 src/games-profile.c               |   64 ++++
 src/games-profile.h               |   54 ++++
 src/games-runtime.c               |  592 +++++++++++++++++++++++++++++++++++++
 src/games-runtime.h               |   89 ++++++
 src/games-score.c                 |  104 +++++++
 src/games-score.h                 |   57 ++++
 src/games-scores-backend.c        |  336 +++++++++++++++++++++
 src/games-scores-backend.h        |   68 +++++
 src/games-scores-dialog-private.h |   53 ++++
 src/games-scores-dialog.c         |  568 +++++++++++++++++++++++++++++++++++
 src/games-scores-dialog.h         |   79 +++++
 src/games-scores.c                |  502 +++++++++++++++++++++++++++++++
 src/games-scores.h                |  101 +++++++
 src/games-show.c                  |  159 ++++++++++
 src/games-show.h                  |   39 +++
 src/main.c                        |  230 ++++++++-------
 src/main.h                        |   12 +-
 23 files changed, 3193 insertions(+), 187 deletions(-)
---
diff --git a/configure.in b/configure.in
index 068ff9f..e9d244d 100644
--- a/configure.in
+++ b/configure.in
@@ -20,19 +20,16 @@ GNOME_MAINTAINER_MODE_DEFINES
 
 dnl ================= Requirements =======================
 LIBGTK_REQUIRED=2.12.0
-LIBGNOME_REQUIRED=2.0.1
-LIBGNOMEUI_REQUIRED=2.0.1
 LIBXML_REQUIRED=2.4.23
 GDK_PIXBUF_REQUIRED=2.0.5
 #LIBGLADE_REQUIRED=2.0.0
 LIBGNOMECANVAS_REQUIRED=2.0.1
-LIBBONOBOUI_REQUIRED=2.0.0
 
 dnl ******************************
 dnl pkg-config checks
 dnl ******************************
 
-ATOMIX_MODULES="gtk+-2.0 >= $LIBGTK_REQUIRED libgnome-2.0 >= $LIBGNOME_REQUIRED libgnomeui-2.0 >= $LIBGNOMEUI_REQUIRED libxml-2.0 >= $LIBXML_REQUIRED gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED libgnomecanvas-2.0 >= $LIBGNOMECANVAS_REQUIRED libbonoboui-2.0 >= $LIBBONOBOUI_REQUIRED"
+ATOMIX_MODULES="gtk+-2.0 >= $LIBGTK_REQUIRED libxml-2.0 >= $LIBXML_REQUIRED gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED libgnomecanvas-2.0 >= $LIBGNOMECANVAS_REQUIRED"
 #libglade-2.0 >= $LIBGLADE_REQUIRED
 PKG_CHECK_MODULES(ATOMIX, $ATOMIX_MODULES)
 AC_SUBST(ATOMIX_CFLAGS)
diff --git a/src/Makefile.am b/src/Makefile.am
index 2851447..c3d83b7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,10 +2,10 @@
 scoredir = $(localstatedir)/games
 uidir = $(datadir)/gnome-2.0/ui
 
-INCLUDES = -Wall \
-	-I$(top_srcdir) \
+INCLUDES = -I$(top_srcdir) \
 	$(ATOMIX_CFLAGS) \
 	$(WARN_CFLAGS) \
+	-DPKGDATADIR="\"$(pkgdatadir)\"" \
 	-DDATADIR=\""$(datadir)"\" \
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
 	-DSCORESDIR=\""$(scoredir)"\"
@@ -20,7 +20,17 @@ atomix_SOURCES = \
 	undo.c undo.h \
 	goal.c goal.h \
 	goal-view.c goal-view.h \
-	clock.c clock.h
+	clock.c clock.h \
+	games-debug.c games-debug.h \
+	games-gtk-compat.h \
+	games-profile.c games-profile.h \
+	games-runtime.c games-runtime.h \
+	games-score.c games-score.h \
+	games-scores.c games-scores.h \
+	games-scores-backend.c games-scores-backend.h \
+	games-scores-dialog.c games-scores-dialog.h \
+	games-scores-dialog-private.h \
+	games-show.c games-show.h
 
 atomix_DEPENDENCIES = libatomix.a
 
@@ -60,6 +70,5 @@ level_convert_LDADD = \
 	-L$(top_builddir)/src \
 	$(ATOMIX_LIBS)
 
-ui_DATA = atomix-ui.xml
 EXTRA_DIST = $(ui_DATA)
 
diff --git a/src/games-debug.c b/src/games-debug.c
new file mode 100644
index 0000000..a424e8a
--- /dev/null
+++ b/src/games-debug.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2002,2003 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+
+#include "games-debug.h"
+
+#ifdef GNOME_ENABLE_DEBUG
+GamesDebugFlags _games_debug_flags;
+#endif
+
+void
+_games_debug_init (void)
+{
+#ifdef GNOME_ENABLE_DEBUG
+  const GDebugKey keys[] = {
+    { "card-theme",   GAMES_DEBUG_CARD_THEME   },
+    { "card-cache",   GAMES_DEBUG_CARD_CACHE   },
+    { "blocks-cache", GAMES_DEBUG_BLOCKS_CACHE },
+    { "runtime",      GAMES_DEBUG_RUNTIME      },
+    { "sound",        GAMES_DEBUG_SOUND        },
+    { "window-state", GAMES_DEBUG_WINDOW_STATE }
+  };
+  const char *env;
+
+  env = g_getenv ("GAMES_DEBUG");
+
+#if !GLIB_CHECK_VERSION (2, 16, 0)
+  /* g_parse_debug_string is only NULL-safe since 2.16 */
+  if (env == NULL)
+    return;
+#endif
+
+  _games_debug_flags = g_parse_debug_string (env, keys, G_N_ELEMENTS (keys));
+#endif /* GNOME_ENABLE_DEBUG */
+}
diff --git a/src/games-debug.h b/src/games-debug.h
new file mode 100644
index 0000000..bd1dcce
--- /dev/null
+++ b/src/games-debug.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2002 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* The interfaces in this file are subject to change at any time. */
+
+#ifndef GNOME_DEBUG_H
+#define GNOME_DEBUG_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GAMES_DEBUG_LAST_RESERVED_BIT (8)
+
+typedef enum {
+  GAMES_DEBUG_CARD_THEME    = 1 << 0,
+  GAMES_DEBUG_CARD_CACHE    = 1 << 1,
+  GAMES_DEBUG_BLOCKS_CACHE  = 1 << 2,
+  GAMES_DEBUG_RUNTIME       = 1 << 3,
+  GAMES_DEBUG_SOUND         = 1 << 4,
+  GAMES_DEBUG_WINDOW_STATE  = 1 << 5
+} GamesDebugFlags;
+
+#ifdef GNOME_ENABLE_DEBUG
+extern GamesDebugFlags _games_debug_flags;
+#endif
+
+void _games_debug_init (void);
+
+static inline gboolean _games_debug_on (GamesDebugFlags flags) G_GNUC_CONST G_GNUC_UNUSED;
+
+static inline gboolean
+_games_debug_on (GamesDebugFlags flags)
+{
+#ifdef GNOME_ENABLE_DEBUG
+  return (_games_debug_flags & flags) == flags;
+#else
+  return FALSE;
+#endif
+}
+
+#ifdef GNOME_ENABLE_DEBUG
+#define _GAMES_DEBUG_IF(flags) if (G_UNLIKELY (_games_debug_on (flags)))
+
+#if defined(__GNUC__) && G_HAVE_GNUC_VARARGS
+#define _games_debug_print(flags, fmt, ...) \
+  G_STMT_START { _GAMES_DEBUG_IF(flags) g_printerr(fmt, ##__VA_ARGS__); } G_STMT_END
+#else
+#include <stdarg.h>
+#include <glib/gstdio.h>
+static void _games_debug_print (guint flags, const char *fmt, ...)
+{
+  if (_games_debug_on (flags)) {
+    va_list  ap;
+    va_start (ap, fmt);
+    g_vfprintf (stderr, fmt, ap);
+    va_end (ap);
+  }
+}
+#endif
+
+#else
+#define _GAMES_DEBUG_IF(flags) if (0)
+#define _games_debug_print(...)
+#endif /* GNOME_ENABLE_DEBUG */
+
+G_END_DECLS
+
+#endif /* !GNOME_DEBUG_H */
diff --git a/src/games-gtk-compat.h b/src/games-gtk-compat.h
new file mode 100644
index 0000000..4a2a022
--- /dev/null
+++ b/src/games-gtk-compat.h
@@ -0,0 +1,44 @@
+/*
+ *  Copyright © 2009 Thomas H.P. Andersen <phomes gmail com>
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime 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 Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GAMES_GTK_COMPAT_H
+#define GAMES_GTK_COMPAT_H
+
+G_BEGIN_DECLS
+
+#ifdef GSEAL_ENABLE
+/* Temporary fix from http://live.gnome.org/GnomeGoals/UseGseal */
+#undef GTK_OBJECT_FLAGS
+#define GTK_OBJECT_FLAGS(i) (GTK_OBJECT (i)->GSEAL(flags))
+#endif /* GSEAL_ENABLE */
+
+#if !GTK_CHECK_VERSION (2, 18, 0)
+#define gtk_widget_set_allocation(widget, alloc) ((widget)->allocation=*(alloc))
+#define gtk_widget_get_allocation(widget, alloc) (*(alloc)=(widget)->allocation)
+#define gtk_widget_has_focus(widget) (GTK_WIDGET_HAS_FOCUS (widget))
+#define gtk_widget_get_state(widget) ((widget)->state)
+#define gtk_widget_get_visible(widget) (GTK_WIDGET_VISIBLE (widget))
+#endif /* GTK < 2.18.0 */
+
+#if !GTK_CHECK_VERSION (2, 14, 0)
+#define gtk_widget_get_window(widget) ((widget)->window)
+#endif /* GTK < 2.14.0 */
+
+G_END_DECLS
+
+#endif /* !GAMES_GTK_COMPAT_H */
diff --git a/src/games-profile.c b/src/games-profile.c
new file mode 100644
index 0000000..fb644a0
--- /dev/null
+++ b/src/games-profile.c
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright © 2005 William Jon McCann <mccann jhu edu>
+ *
+ * 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 Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <mccann jhu edu>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "games-profile.h"
+
+void
+_games_profile_log (const char *func,
+                    const char *note,
+                    const char *format,
+                    ...)
+{
+        va_list args;
+        char   *str;
+        char   *formatted;
+
+        if (format == NULL) {
+                formatted = g_strdup ("");
+        } else {
+                va_start (args, format);
+                formatted = g_strdup_vprintf (format, args);
+                va_end (args);
+        }
+
+        if (func != NULL) {
+                str = g_strdup_printf ("MARK: %s %s: %s %s", g_get_prgname(), func, note ? note : "", formatted);
+        } else {
+                str = g_strdup_printf ("MARK: %s: %s %s", g_get_prgname(), note ? note : "", formatted);
+        }
+
+        g_free (formatted);
+
+        g_access (str, F_OK);
+        g_free (str);
+}
diff --git a/src/games-profile.h b/src/games-profile.h
new file mode 100644
index 0000000..fa0bfae
--- /dev/null
+++ b/src/games-profile.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright © 2005 William Jon McCann <mccann jhu edu>
+ *
+ * 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 Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <mccann jhu edu>
+ */
+
+#ifndef GAMES_PROFILE_H
+#define GAMES_PROFILE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#ifdef ENABLE_PROFILING
+#ifdef G_HAVE_ISO_VARARGS
+#define _games_profile_start(...) _games_profile_log (G_STRFUNC, "start", __VA_ARGS__)
+#define _games_profile_end(...)   _games_profile_log (G_STRFUNC, "end", __VA_ARGS__)
+#define _games_profile_msg(...)   _games_profile_log (NULL, NULL, __VA_ARGS__)
+#elif defined(G_HAVE_GNUC_VARARGS)
+#define _games_profile_start(format...) _games_profile_log (G_STRFUNC, "start", format)
+#define _games_profile_end(format...)   _games_profile_log (G_STRFUNC, "end", format)
+#define _games_profile_msg(format...)   _games_profile_log (NULL, NULL, format)
+#else
+#error Need either ISO or GNUC varargs macros!
+#endif
+#else
+#define _games_profile_start(...)
+#define _games_profile_end(...)
+#define _games_profile_msg(...)
+#endif
+
+void _games_profile_log (const char *func,
+                         const char *note,
+                         const char *format,
+                         ...) G_GNUC_PRINTF (3, 4);
+
+G_END_DECLS
+
+#endif /* GAMES_PROFILE_H */
diff --git a/src/games-runtime.c b/src/games-runtime.c
new file mode 100644
index 0000000..1a23239
--- /dev/null
+++ b/src/games-runtime.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright © 2007 Andreas Røsdal <andreasr gnome org>
+ * Copyright © 2007, 2008 Christian Persch
+ * Copyright © 2009 Tor Lillqvist
+ *
+ * This game 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, or (at your option)
+ * any later version.
+ *
+ * This runtime 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 runtime; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ * USA
+ */
+
+#include <config.h>
+
+#include <locale.h>
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#include <conio.h>
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#endif /* G_OS_WIN32 */
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "games-debug.h"
+#include "games-profile.h"
+#include "games-show.h"
+
+#include "games-runtime.h"
+
+#ifdef HAVE_HILDON
+static osso_context_t *osso_context;
+#endif
+
+#if defined(G_OS_WIN32) && !defined(ENABLE_BINRELOC)
+#error binreloc must be enabled on win32
+#endif
+
+#if defined(ENABLE_BINRELOC) && !defined(G_OS_WIN32)
+
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h lai chello nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */
+typedef enum {
+	/** Cannot allocate memory. */
+	GBR_INIT_ERROR_NOMEM,
+	/** Unable to open /proc/self/maps; see errno for details. */
+	GBR_INIT_ERROR_OPEN_MAPS,
+	/** Unable to read from /proc/self/maps; see errno for details. */
+	GBR_INIT_ERROR_READ_MAPS,
+	/** The file format of /proc/self/maps is invalid; kernel bug? */
+	GBR_INIT_ERROR_INVALID_MAPS,
+	/** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
+	GBR_INIT_ERROR_DISABLED
+} GbrInitError;
+
+/** @internal
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occured.
+ */
+static char *
+_br_find_exe (GbrInitError *error)
+{
+	char *path, *path2, *line, *result;
+	size_t buf_size;
+	ssize_t size;
+	struct stat stat_buf;
+	FILE *f;
+
+	/* Read from /proc/self/exe (symlink) */
+	if (sizeof (path) > SSIZE_MAX)
+		buf_size = SSIZE_MAX - 1;
+	else
+		buf_size = PATH_MAX - 1;
+	path = (char *) g_try_malloc (buf_size);
+	if (path == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+	path2 = (char *) g_try_malloc (buf_size);
+	if (path2 == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		g_free (path);
+		return NULL;
+	}
+
+	strncpy (path2, "/proc/self/exe", buf_size - 1);
+
+	while (1) {
+		int i;
+
+		size = readlink (path2, path, buf_size - 1);
+		if (size == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* readlink() success. */
+		path[size] = '\0';
+
+		/* Check whether the symlink's target is also a symlink.
+		 * We want to get the final target. */
+		i = stat (path, &stat_buf);
+		if (i == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* stat() success. */
+		if (!S_ISLNK (stat_buf.st_mode)) {
+			/* path is not a symlink. Done. */
+			g_free (path2);
+			return path;
+		}
+
+		/* path is a symlink. Continue loop and resolve this. */
+		strncpy (path, path2, buf_size - 1);
+	}
+
+
+	/* readlink() or stat() failed; this can happen when the program is
+	 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+	buf_size = PATH_MAX + 128;
+	line = (char *) g_try_realloc (path, buf_size);
+	if (line == NULL) {
+		/* Cannot allocate memory. */
+		g_free (path);
+		if (error)
+			*error = GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+
+	f = fopen ("/proc/self/maps", "r");
+	if (f == NULL) {
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_OPEN_MAPS;
+		return NULL;
+	}
+
+	/* The first entry should be the executable name. */
+	result = fgets (line, (int) buf_size, f);
+	if (result == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_READ_MAPS;
+		return NULL;
+	}
+
+	/* Get rid of newline character. */
+	buf_size = strlen (line);
+	if (buf_size <= 0) {
+		/* Huh? An empty string? */
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+	if (line[buf_size - 1] == 10)
+		line[buf_size - 1] = 0;
+
+	/* Extract the filename; it is always an absolute path. */
+	path = strchr (line, '/');
+
+	/* Sanity check. */
+	if (strstr (line, " r-xp ") == NULL || path == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+
+	path = g_strdup (path);
+	g_free (line);
+	fclose (f);
+	return path;
+}
+
+#endif /* ENABLE_BINRELOC && !G_OS_WIN32 */
+
+static char *app_name;
+static int gpl_version;
+static char *cached_directories[GAMES_RUNTIME_LAST_DIRECTORY];
+
+typedef struct {
+  GamesRuntimeDirectory base_dir;
+  const char *name;
+} DerivedDirectory;
+
+static const DerivedDirectory derived_directories[] = {
+  /* Keep this in the same order as in the GamesRuntimeDirectory enum! */
+#ifdef ENABLE_BINRELOC
+  { GAMES_RUNTIME_MODULE_DIRECTORY,   "share"              }, /* GAMES_RUNTIME_DATA_DIRECTORY              */
+  { GAMES_RUNTIME_DATA_DIRECTORY,     "gnome-games-common" }, /* GAMES_RUNTIME_COMMON_DATA_DIRECTORY       */
+  { GAMES_RUNTIME_DATA_DIRECTORY,     "gnome-games"        }, /* GAMES_RUNTIME_PKG_DATA_DIRECTORY          */
+  { GAMES_RUNTIME_DATA_DIRECTORY,     "scores"             }, /* GAMES_RUNTIME_SCORES_DIRECTORY            */
+#endif /* ENABLE_BINRELOC */
+  { GAMES_RUNTIME_DATA_DIRECTORY,         "locale"         }, /* GAMES_RUNTIME_LOCALE_DIRECTORY            */
+  { GAMES_RUNTIME_COMMON_DATA_DIRECTORY,  "pixmaps"        }, /* GAMES_RUNTIME_COMMON_PIXMAP_DIRECTORY     */
+  { GAMES_RUNTIME_COMMON_DATA_DIRECTORY,  "card-themes"    }, /* GAMES_RUNTIME_PRERENDERED_CARDS_DIRECTORY */
+  { GAMES_RUNTIME_COMMON_DATA_DIRECTORY,  "cards"          }, /* GAMES_RUNTIME_SCALABLE_CARDS_DIRECTORY    */
+  { GAMES_RUNTIME_PKG_DATA_DIRECTORY,     "icons"          }, /* GAMES_RUNTIME_ICON_THEME_DIRECTORY        */
+  { GAMES_RUNTIME_PKG_DATA_DIRECTORY,     "pixmaps"        }, /* GAMES_RUNTIME_PIXMAP_DIRECTORY            */
+  { GAMES_RUNTIME_PKG_DATA_DIRECTORY,     "sounds"         }, /* GAMES_RUNTIME_SOUNDS_DIRECTORY            */
+  { GAMES_RUNTIME_PKG_DATA_DIRECTORY,     NULL             }, /* GAMES_RUNTIME_GAME_DATA_DIRECTORY         */
+  { GAMES_RUNTIME_GAME_DATA_DIRECTORY,    "games"          }, /* GAMES_RUNTIME_GAME_GAMES_DIRECTORY        */
+  { GAMES_RUNTIME_GAME_DATA_DIRECTORY,    "pixmaps"        }, /* GAMES_RUNTIME_GAME_PIXMAP_DIRECTORY       */
+  { GAMES_RUNTIME_GAME_DATA_DIRECTORY,    "themes"         }, /* GAMES_RUNTIME_GAME_THEME_DIRECTORY        */
+  { GAMES_RUNTIME_GAME_DATA_DIRECTORY,    "help"           }, /* GAMES_RUNTIME_GAME_HELP_DIRECTORY         */
+};
+
+typedef int _assertion[G_N_ELEMENTS (derived_directories) + GAMES_RUNTIME_FIRST_DERIVED_DIRECTORY == GAMES_RUNTIME_LAST_DIRECTORY ? 1 : -1];
+
+#if !GTK_CHECK_VERSION (2, 17, 0)
+/* Since version 2.17.0, gtk has default about dialogue hook functions
+ * using gtk_show_uri(). For earlier versions, we need to implement
+ * our own hooks.
+ */
+
+static void
+about_url_hook (GtkAboutDialog *about,
+                const char *uri,
+                gpointer user_data)
+{
+  GdkScreen *screen;
+  GError *error = NULL;
+
+  screen = gtk_widget_get_screen (GTK_WIDGET (about));
+
+  if (!games_show_uri (screen, uri, gtk_get_current_event_time (), &error)) {
+    games_show_error (GTK_WIDGET (about),
+                      error,
+                      "%s", _("Could not show link"));
+    g_error_free (error);
+  }
+}
+
+static void
+about_email_hook (GtkAboutDialog *about,
+                  const char *email_address,
+                  gpointer user_data)
+{
+  char *uri;
+
+#if GLIB_CHECK_VERSION (2, 16, 0)
+  char *escaped_email_address;
+
+  escaped_email_address = g_uri_escape_string (email_address, NULL, FALSE);
+  uri = g_strdup_printf ("mailto:%s";, escaped_email_address);
+  g_free (escaped_email_address);
+#else
+  /* Not really correct, but the best we can do */
+  uri = g_strdup_printf ("mailto:%s";, email_address);
+#endif
+
+  about_url_hook (about, uri, user_data);
+  g_free (uri);
+}
+
+#endif /* GTK < 2.17.0 */
+
+/* public API */
+
+/**
+ * games_runtime_init:
+ *
+ * Initialises the runtime file localisator. This also calls setlocale,
+ * and initialises gettext support and gnome-games debug support.
+ *
+ * NOTE: This must be called before using ANY other glib/gtk/etc function!
+ * 
+ * Returns: %TRUE iff initialisation succeeded
+ */
+gboolean
+games_runtime_init (const char *name)
+{
+  gboolean retval;
+
+  setlocale (LC_ALL, "");
+
+#ifdef G_OS_WIN32
+  /* On Windows, when called from a console, get console output. This works
+   * only with Windows XP or higher; Windows 2000 will not have console
+   * output but it will just work fine.
+   */
+  if (fileno (stdout) != -1 &&
+      _get_osfhandle (fileno (stdout)) != -1) {
+    /* stdout is fine, presumably redirected to a file or pipe.
+     * Make sure stdout goes somewhere, too.
+     */
+    if (_get_osfhandle (fileno (stderr)) == -1) {
+      dup2 (fileno (stdout), fileno (stderr));
+    }
+  } else {
+    typedef BOOL (* WINAPI AttachConsole_t) (DWORD);
+
+    AttachConsole_t p_AttachConsole =
+      (AttachConsole_t) GetProcAddress (GetModuleHandle ("kernel32.dll"), "AttachConsole");
+
+    if (p_AttachConsole != NULL && p_AttachConsole (ATTACH_PARENT_PROCESS)) {
+      freopen ("CONOUT$", "w", stdout);
+      dup2 (fileno (stdout), 1);
+      freopen ("CONOUT$", "w", stderr);
+      dup2 (fileno (stderr), 2);
+    }
+  }
+#endif /* G_OS_WIN32 */
+
+#if defined(HAVE_GNOME) || defined(HAVE_RSVG_GNOMEVFS) || defined(HAVE_CANBERRA_GTK)
+  /* If we're going to use gconf, gnome-vfs, or canberra, we need to
+   * init threads; and this has to be done before calling any other glib functions.
+   */
+#if defined(LIBGAMES_SUPPORT_GI)
+  /* Seed has already called g_thread_init() */
+  g_assert (g_thread_get_initialized());
+#else
+  g_thread_init (NULL);
+#endif
+#endif
+  /* May call any glib function after this point */
+
+  _games_profile_start ("games_runtime_init");
+
+  _games_debug_init ();
+
+  app_name = g_strdup (name);
+
+  bindtextdomain (GETTEXT_PACKAGE, games_runtime_get_directory (GAMES_RUNTIME_LOCALE_DIRECTORY));
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain(GETTEXT_PACKAGE);
+
+#ifdef ENABLE_BINRELOC
+{
+  const char *path;
+
+  /* Now check that we can get the module installation directory */
+  path = games_runtime_get_directory (GAMES_RUNTIME_MODULE_DIRECTORY);
+
+  _games_debug_print (GAMES_DEBUG_RUNTIME,
+                      "Relocation path: %s\n", path ? path : "(null)");
+
+  retval = path != NULL;
+}
+#else /* !ENABLE_BINRELOC */
+  retval = TRUE;
+#endif /* ENABLE_BINRELOC */
+
+  if (strcmp (app_name, "aisleriot") == 0) {
+    gpl_version = 3;
+  } else {
+    gpl_version = 2;
+  }
+
+#if !GTK_CHECK_VERSION (2, 17, 0)
+  gtk_about_dialog_set_url_hook (about_url_hook, NULL, NULL);
+  gtk_about_dialog_set_email_hook (about_email_hook, NULL, NULL);
+#endif
+
+  _games_profile_end ("games_runtime_init");
+
+  return retval;
+}
+
+#ifdef HAVE_HILDON
+
+/**
+ * games_runtime_init_with_osso:
+ *
+ * Like games_runtime_init(), but also initialises the osso context.
+ *
+ * NOTE: This must be called before using ANY other glib/gtk/etc function!
+ * 
+ * Returns: %TRUE iff initialisation succeeded
+ */
+gboolean
+games_runtime_init_with_osso (const char *name,
+                              const char *service_name)
+{
+  if (!games_runtime_init (name))
+    return FALSE;
+
+  osso_context = osso_initialize (service_name, VERSION, FALSE, NULL);
+  if (osso_context == NULL) {
+    g_printerr ("Failed to initialise osso\n");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+ * games_runtime_get_osso_context:
+ *
+ * Returns the osso context. May only be called after
+ * games_runtime_init_with_osso().
+ *
+ * Returns: a #osso_context_t
+ */
+osso_context_t*
+games_runtime_get_osso_context (void)
+{
+  g_assert (osso_context != NULL);
+  return osso_context;
+}
+
+#endif /* HAVE_HILDON */
+
+/**
+ * games_runtime_shutdown:
+ *
+ * Shuts down the runtime file localator.
+ */
+void
+games_runtime_shutdown (void)
+{
+  guint i;
+
+  for (i = 0; i < GAMES_RUNTIME_LAST_DIRECTORY; ++i) {
+    g_free (cached_directories[i]);
+    cached_directories[i] = NULL;
+  }
+
+  g_free (app_name);
+  app_name = NULL;
+
+#ifdef HAVE_HILDON
+  if (osso_context != NULL) {
+    osso_deinitialize (osso_context);
+    osso_context = NULL;
+  }
+#endif /* HAVE_HILDON */
+}
+
+/**
+ * games_runtime_get_directory:
+ * @runtime: the #GamesProgram instance
+ * @directory:
+ *
+ * Returns: the path to use for @directory
+ */
+const char *
+games_runtime_get_directory (GamesRuntimeDirectory directory)
+{
+
+  char *path = NULL;
+
+  g_return_val_if_fail (app_name != NULL, NULL);
+  g_return_val_if_fail (directory < GAMES_RUNTIME_LAST_DIRECTORY, NULL);
+
+  if (cached_directories[directory])
+    return cached_directories[directory];
+
+  switch ((int) directory) {
+#ifdef ENABLE_BINRELOC
+    case GAMES_RUNTIME_MODULE_DIRECTORY:
+#ifdef G_OS_WIN32
+      path = g_win32_get_package_installation_directory_of_module (NULL);
+#else
+      {
+        GbrInitError errv = 0;
+        const char *env;
+
+        if ((env = g_getenv ("GAMES_RELOC_ROOT")) != NULL) {
+          path = g_strdup (env);
+        } else {
+          char *exe, *bindir, *prefix;
+
+          exe = _br_find_exe (&errv);
+          if (exe == NULL) {
+            g_printerr ("Failed to locate the binary relocation prefix (error code %u)\n", errv);
+          } else {
+            bindir = g_path_get_dirname (exe);
+            g_free (exe);
+            prefix = g_path_get_dirname (bindir);
+            g_free (bindir);
+
+            if (prefix != NULL && strcmp (prefix, ".") != 0) {
+              path = prefix;
+            } else {
+              g_free (prefix);
+            }
+          }
+        }
+      }
+#endif /* G_OS_WIN32 */
+      break;
+#else /* !ENABLE_BINRELOC */
+
+    case GAMES_RUNTIME_DATA_DIRECTORY:
+      path = g_strdup (DATADIR);
+      break;
+
+    case GAMES_RUNTIME_COMMON_DATA_DIRECTORY:
+      path = g_build_filename (DATADIR, "gnome-games-common", NULL);
+      break;
+
+    case GAMES_RUNTIME_PKG_DATA_DIRECTORY:
+      path = g_strdup (PKGDATADIR);
+      break;
+
+    case GAMES_RUNTIME_SCORES_DIRECTORY:
+      path = g_strdup (SCORESDIR);
+      break;
+#endif /* ENABLE_BINRELOC */
+
+    default: {
+      const DerivedDirectory *base = &derived_directories[directory - GAMES_RUNTIME_FIRST_DERIVED_DIRECTORY];
+
+      path = g_build_filename (games_runtime_get_directory (base->base_dir),
+                               base->name ? base->name : app_name,
+                               NULL);
+    }
+  }
+
+  cached_directories[directory] = path;
+  return path;
+}
+
+/**
+ * games_runtime_get_file:
+ * @runtime: the #GamesProgram instance
+ * @directory:
+ * @name:
+ *
+ * Returns: a newly allocated string containing the path to the file with name @name
+ *   in the directory specicifed by @directory
+ */
+char *
+games_runtime_get_file (GamesRuntimeDirectory directory,
+                        const char *name)
+{
+  const char *dir;
+
+  g_return_val_if_fail (app_name != NULL, NULL);
+
+  dir = games_runtime_get_directory (directory);
+  if (!dir)
+    return NULL;
+
+  return g_build_filename (dir, name, NULL);
+}
+
+/**
+ * games_runtime_get_gpl_version:
+ *
+ * Returns: the minimum GPL version that the executable is licensed under
+ */
+int
+games_runtime_get_gpl_version (void)
+{
+  return gpl_version;
+}
diff --git a/src/games-runtime.h b/src/games-runtime.h
new file mode 100644
index 0000000..23fd30e
--- /dev/null
+++ b/src/games-runtime.h
@@ -0,0 +1,89 @@
+/*
+ *  Copyright © 2007, 2008 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime 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 Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GAMES_RUNTIME_H
+#define GAMES_RUNTIME_H
+
+#include <glib.h>
+
+#ifdef HAVE_HILDON
+#include <libosso.h>
+#endif
+
+G_BEGIN_DECLS
+
+typedef enum {
+  /* Base directories */
+#ifdef ENABLE_BINRELOC
+  GAMES_RUNTIME_MODULE_DIRECTORY,
+#endif
+
+  GAMES_RUNTIME_DATA_DIRECTORY,
+  GAMES_RUNTIME_COMMON_DATA_DIRECTORY,
+  GAMES_RUNTIME_PKG_DATA_DIRECTORY,
+  GAMES_RUNTIME_SCORES_DIRECTORY,
+
+  /* Derived directories */
+  GAMES_RUNTIME_LOCALE_DIRECTORY,
+
+  GAMES_RUNTIME_COMMON_PIXMAP_DIRECTORY,
+  GAMES_RUNTIME_PRERENDERED_CARDS_DIRECTORY,
+  GAMES_RUNTIME_SCALABLE_CARDS_DIRECTORY,
+
+  GAMES_RUNTIME_ICON_THEME_DIRECTORY,
+  GAMES_RUNTIME_PIXMAP_DIRECTORY,
+  GAMES_RUNTIME_SOUND_DIRECTORY,
+
+  GAMES_RUNTIME_GAME_DATA_DIRECTORY,
+  GAMES_RUNTIME_GAME_GAMES_DIRECTORY,
+  GAMES_RUNTIME_GAME_PIXMAP_DIRECTORY,
+  GAMES_RUNTIME_GAME_THEME_DIRECTORY,
+  /* FIXME On hildon and win32 help is created as html with gnome-doc-tool, and put manually in this directory */
+  GAMES_RUNTIME_GAME_HELP_DIRECTORY,
+
+  GAMES_RUNTIME_LAST_DIRECTORY,
+#ifdef ENABLE_BINRELOC
+  GAMES_RUNTIME_FIRST_DERIVED_DIRECTORY = GAMES_RUNTIME_DATA_DIRECTORY,
+#else
+  GAMES_RUNTIME_FIRST_DERIVED_DIRECTORY = GAMES_RUNTIME_LOCALE_DIRECTORY,
+#endif
+} GamesRuntimeDirectory;
+
+gboolean games_runtime_init (const char *name);
+
+#ifdef HAVE_HILDON
+
+gboolean games_runtime_init_with_osso (const char *name,
+                                       const char *service_name);
+
+osso_context_t* games_runtime_get_osso_context (void);
+
+#endif /* HAVE_HILDON */
+
+void games_runtime_shutdown (void);
+
+const char * games_runtime_get_directory (GamesRuntimeDirectory directory);
+
+char * games_runtime_get_file (GamesRuntimeDirectory directory,
+                               const char *name);
+
+int games_runtime_get_gpl_version (void);
+
+G_END_DECLS
+
+#endif /* !GAMES_RUNTIME_H */
diff --git a/src/games-score.c b/src/games-score.c
new file mode 100644
index 0000000..51d8291
--- /dev/null
+++ b/src/games-score.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2005 Callum McKenzie
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include "games-score.h"
+
+GamesScore *
+games_score_new (void)
+{
+  GamesScore *newscore;
+  const gchar* name;
+
+  newscore = g_slice_new0 (GamesScore);
+  newscore->time = time (NULL);
+  /* FIXME: We don't handle the "Unknown" case. */
+  name = g_get_real_name ();
+  if (name[0] == '\0' || g_utf8_validate (name, -1, NULL) != TRUE) {
+    name = g_get_user_name ();
+    if (g_utf8_validate (name, -1, NULL) != TRUE) {
+      name = "";
+    }
+  }
+  newscore->name = g_strdup (name);
+
+  return newscore;
+}
+
+GamesScore *
+games_score_dup (GamesScore * orig)
+{
+  GamesScore *new;
+
+  new = g_slice_new (GamesScore);
+  *new = *orig;
+  new->name = g_strdup (orig->name);
+
+  return new;
+}
+
+void
+games_score_destroy (GamesScore * score)
+{
+  g_free (score->name);
+  g_slice_free (GamesScore, score);
+}
+
+gint
+games_score_compare_values (GamesScoreStyle style, GamesScoreValue a,
+			    GamesScoreValue b)
+{
+  switch (style) {
+  case GAMES_SCORES_STYLE_PLAIN_DESCENDING:
+    if (a.plain > b.plain)
+      return +1;
+    if (a.plain < b.plain)
+      return -1;
+    return 0;
+  case GAMES_SCORES_STYLE_PLAIN_ASCENDING:
+    if (a.plain > b.plain)
+      return -1;
+    if (a.plain < b.plain)
+      return +1;
+    return 0;
+  case GAMES_SCORES_STYLE_TIME_DESCENDING:
+    if (a.time_double > b.time_double)
+      return +1;
+    if (a.time_double < b.time_double)
+      return -1;
+    return 0;
+  case GAMES_SCORES_STYLE_TIME_ASCENDING:
+    if (a.time_double > b.time_double)
+      return -1;
+    if (a.time_double < b.time_double)
+      return +1;
+    return 0;
+  default:
+    g_warning
+      ("Uknown score style in games_score_compare - treating as equal.");
+    return 0;
+  }
+}
+
+gint
+games_score_compare (GamesScoreStyle style, GamesScore * a, GamesScore * b)
+{
+  return games_score_compare_values (style, a->value, b->value);
+}
diff --git a/src/games-score.h b/src/games-score.h
new file mode 100644
index 0000000..1ef7a5b
--- /dev/null
+++ b/src/games-score.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2005 Callum McKenzie
+ *
+ * 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 GAMES_SCORE_H
+#define GAMES_SCORE_H
+
+#include <glib.h>
+#include <time.h>
+
+G_BEGIN_DECLS
+
+/* GamesScore and GamesScoresStyle should be kept in sync. */
+typedef enum {
+  GAMES_SCORES_STYLE_PLAIN_DESCENDING,
+  GAMES_SCORES_STYLE_PLAIN_ASCENDING,
+  GAMES_SCORES_STYLE_TIME_DESCENDING,
+  GAMES_SCORES_STYLE_TIME_ASCENDING,
+} GamesScoreStyle;
+
+typedef union {
+  guint32 plain;
+  gdouble time_double;		/* minutes.seconds */
+} GamesScoreValue;
+
+typedef struct {
+  GamesScoreValue value;
+  time_t time;
+  gchar *name;
+} GamesScore;
+
+GamesScore *games_score_new (void);
+GamesScore *games_score_dup (GamesScore * orig);
+gint games_score_compare (GamesScoreStyle style, GamesScore * a,
+			  GamesScore * b);
+gint games_score_compare_values (GamesScoreStyle style, GamesScoreValue a,
+				 GamesScoreValue b);
+void games_score_destroy (GamesScore * score);
+
+G_END_DECLS
+
+#endif /* GAMES_SCORE_H */
diff --git a/src/games-scores-backend.c b/src/games-scores-backend.c
new file mode 100644
index 0000000..c93330f
--- /dev/null
+++ b/src/games-scores-backend.c
@@ -0,0 +1,336 @@
+/* games-scores-backend.c 
+ *
+ * Copyright (C) 2005 Callum McKenzie
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "games-score.h"
+#include "games-scores.h"
+#include "games-scores-backend.h"
+#include "games-runtime.h"
+
+#ifdef ENABLE_SETGID
+#include "games-setgid-io.h"
+#endif
+
+struct _GamesScoresBackendPrivate {
+  GamesScoreStyle style;
+  time_t timestamp;
+  gchar *filename;
+  gint fd;
+};
+
+G_DEFINE_TYPE (GamesScoresBackend, games_scores_backend, G_TYPE_OBJECT);
+
+static void
+games_scores_backend_finalize (GObject *object)
+{
+  GamesScoresBackend *backend = GAMES_SCORES_BACKEND (object);
+  GamesScoresBackendPrivate *priv = backend->priv;
+
+  g_free (priv->filename);
+  /* FIXME: more to do? */
+
+  G_OBJECT_CLASS (games_scores_backend_parent_class)->finalize (object);
+}
+
+static void
+games_scores_backend_class_init (GamesScoresBackendClass * klass)
+{
+  GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GamesScoresBackendPrivate));
+  oclass->finalize = games_scores_backend_finalize;
+}
+
+static void
+games_scores_backend_init (GamesScoresBackend * backend)
+{
+  backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend,
+					       GAMES_TYPE_SCORES_BACKEND,
+					       GamesScoresBackendPrivate);
+}
+
+GamesScoresBackend *
+games_scores_backend_new (GamesScoreStyle style,
+			  char *base_name,
+                          char *name)
+{
+  GamesScoresBackend *backend;
+  gchar *fullname;
+
+  backend = GAMES_SCORES_BACKEND (g_object_new (GAMES_TYPE_SCORES_BACKEND,
+						NULL));
+
+  if (name[0] == '\0')		/* Name is "" */
+    fullname = g_strjoin (".", base_name, "scores", NULL);
+  else
+    fullname = g_strjoin (".", base_name, name, "scores", NULL);
+
+  backend->priv->timestamp = 0;
+  backend->priv->style = style;
+  backend->scores_list = NULL;
+  backend->priv->filename = g_build_filename (games_runtime_get_directory (GAMES_RUNTIME_SCORES_DIRECTORY),
+                                              fullname, NULL);
+  g_free (fullname);
+
+  backend->priv->fd = -1;
+
+  return backend;
+}
+
+#ifdef ENABLE_SETGID
+
+/* Get a lock on the scores file. Block until it is available. 
+ * This also supplies the file descriptor we need. The return value
+ * is whether we were succesful or not. */
+static gboolean
+games_scores_backend_get_lock (GamesScoresBackend * self)
+{
+  gint error;
+
+  if (self->priv->fd != -1) {
+    /* Assume we already have the lock and rewind the file to
+     * the beginning. */
+    setgid_io_seek (self->priv->fd, 0, SEEK_SET);
+    return TRUE;		/* Assume we already have the lock. */
+  }
+
+  self->priv->fd = setgid_io_open (self->priv->filename, O_RDWR);
+  if (self->priv->fd == -1) {
+    return FALSE;
+  }
+
+  error = setgid_io_lock (self->priv->fd);
+
+  if (error == -1) {
+    setgid_io_close (self->priv->fd);
+    self->priv->fd = -1;
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Release the lock on the scores file and dispose of the fd. */
+/* We ignore errors, there is nothing we can do about them. */
+static void
+games_scores_backend_release_lock (GamesScoresBackend * self)
+{
+  /* We don't have a lock, ignore this call. */
+  if (self->priv->fd == -1)
+    return;
+
+  setgid_io_unlock (self->priv->fd);
+
+  setgid_io_close (self->priv->fd);
+
+  self->priv->fd = -1;
+}
+
+#endif /* ENABLE_SETGID */
+
+/* You can alter the list returned by this function, but you must
+ * make sure you set it again with the _set_scores method or discard it
+ * with with the _discard_scores method. Otherwise deadlocks will ensue. */
+GList *
+games_scores_backend_get_scores (GamesScoresBackend * self)
+{
+#ifdef ENABLE_SETGID
+  gchar *buffer;
+  gchar *eol;
+  gchar *scorestr;
+  gchar *timestr;
+  gchar *namestr;
+  GamesScore *newscore;
+  struct stat info;
+  int error;
+  ssize_t length, target;
+  GList *t;
+
+  /* Check for a change in the scores file and update if necessary. */
+  error = setgid_io_stat (self->priv->filename, &info);
+
+  /* If an error occurs then we give up on the file and return NULL. */
+  if (error != 0)
+    return NULL;
+
+  if ((info.st_mtime > self->priv->timestamp) || (self->scores_list == NULL)) {
+    self->priv->timestamp = info.st_mtime;
+
+    /* Dump the old list of scores. */
+    t = self->scores_list;
+    while (t != NULL) {
+      games_score_destroy ((GamesScore *) t->data);
+      t = g_list_next (t);
+    }
+    g_list_free (self->scores_list);
+    self->scores_list = NULL;
+
+    /* Lock the file and get the list. */
+    if (!games_scores_backend_get_lock (self))
+      return NULL;
+
+    buffer = g_malloc (info.st_size + 1);
+    if (buffer == NULL) {
+      games_scores_backend_release_lock (self);
+      return NULL;
+    }
+
+    target = info.st_size;
+    length = 0;
+    do {
+      target -= length;
+      length = setgid_io_read (self->priv->fd, buffer, info.st_size);
+      if (length == -1) {
+	games_scores_backend_release_lock (self);
+	g_free (buffer);
+	return NULL;
+      }
+    } while (length < target);
+
+    buffer[info.st_size] = '\0';
+
+    /* FIXME: These details should be in a sub-class. */
+
+    /* Parse the list. We start by breaking it into lines. */
+    /* Since the buffer is null-terminated 
+     * we can do the string stuff reasonably safely. */
+    eol = strchr (buffer, '\n');
+    scorestr = buffer;
+    while (eol != NULL) {
+      *eol++ = '\0';
+      timestr = strchr (scorestr, ' ');
+      if (timestr == NULL)
+	break;
+      *timestr++ = '\0';
+      namestr = strchr (timestr, ' ');
+      if (namestr == NULL)
+	break;
+      *namestr++ = '\0';
+      /* At this point we have three strings, all null terminated. All
+       * part of the original buffer. */
+      newscore = games_score_new ();
+      newscore->name = g_strdup (namestr);
+      newscore->time = g_ascii_strtoull (timestr, NULL, 10);
+      switch (self->priv->style) {
+      case GAMES_SCORES_STYLE_PLAIN_DESCENDING:
+      case GAMES_SCORES_STYLE_PLAIN_ASCENDING:
+	newscore->value.plain = g_ascii_strtod (scorestr, NULL);
+	break;
+      case GAMES_SCORES_STYLE_TIME_DESCENDING:
+      case GAMES_SCORES_STYLE_TIME_ASCENDING:
+	newscore->value.time_double = g_ascii_strtod (scorestr, NULL);
+	break;
+      default:
+        g_assert_not_reached ();
+      }
+      self->scores_list = g_list_append (self->scores_list, newscore);
+      /* Setup again for the next time around. */
+      scorestr = eol;
+      eol = strchr (eol, '\n');
+    }
+
+    g_free (buffer);
+  }
+
+  /* FIXME: Sort the scores! We shouldn't rely on the file being sorted. */
+
+  return self->scores_list;
+#else
+  return NULL;
+#endif /* ENABLE_SETGID */
+}
+
+gboolean
+games_scores_backend_set_scores (GamesScoresBackend * self, GList * list)
+{
+#ifdef ENABLE_SETGID
+  GList *s;
+  GamesScore *d;
+  gchar *buffer;
+  gint output_length = 0;
+  gchar dtostrbuf[G_ASCII_DTOSTR_BUF_SIZE];
+
+  if (!games_scores_backend_get_lock (self))
+    return FALSE;
+
+  self->scores_list = list;
+
+  s = list;
+  while (s != NULL) {
+    gdouble rscore;
+    guint64 rtime;
+    gchar *rname;
+
+    d = (GamesScore *) s->data;
+    rscore = 0.0;
+    switch (self->priv->style) {
+    case GAMES_SCORES_STYLE_PLAIN_DESCENDING:
+    case GAMES_SCORES_STYLE_PLAIN_ASCENDING:
+      rscore = d->value.plain;
+      break;
+    case GAMES_SCORES_STYLE_TIME_DESCENDING:
+    case GAMES_SCORES_STYLE_TIME_ASCENDING:
+      rscore = d->value.time_double;
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+    rtime = d->time;
+    rname = d->name;
+
+    buffer = g_strdup_printf ("%s %lld %s\n",
+			      g_ascii_dtostr (dtostrbuf, sizeof (dtostrbuf),
+					      rscore), rtime, rname);
+    setgid_io_write (self->priv->fd, buffer, strlen (buffer));
+    output_length += strlen (buffer);
+    /* Ignore any errors and blunder on. */
+    g_free (buffer);
+
+    s = g_list_next (s);
+  }
+
+  /* Remove any content in the file that hasn't yet been overwritten. */
+  setgid_io_truncate (self->priv->fd, output_length--);
+
+  /* Update the timestamp so we don't reread the scores unnecessarily. */
+  self->priv->timestamp = time (NULL);
+
+  games_scores_backend_release_lock (self);
+
+  return TRUE;
+#else
+  return FALSE;
+#endif /* ENABLE_SETGID */
+}
+
+void
+games_scores_backend_discard_scores (GamesScoresBackend * self)
+{
+#ifdef ENABLE_SETGID
+  games_scores_backend_release_lock (self);
+#endif
+}
diff --git a/src/games-scores-backend.h b/src/games-scores-backend.h
new file mode 100644
index 0000000..2247cb5
--- /dev/null
+++ b/src/games-scores-backend.h
@@ -0,0 +1,68 @@
+ /* games-scores-backend.c 
+  *
+  * Copyright (C) 2005 Callum McKenzie
+  *
+  * 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 GAMES_SCORES_BACKEND_H
+#define GAMES_SCORES_BACKEND_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <time.h>
+
+#include "games-score.h"
+
+#ifdef ENABLE_SETGID
+#include "games-setgid-io.h"
+#endif
+
+G_BEGIN_DECLS
+
+#define GAMES_TYPE_SCORES_BACKEND (games_scores_backend_get_type ())
+#define GAMES_SCORES_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAMES_TYPE_SCORES_BACKEND, GamesScoresBackend))
+#define GAMES_SCORES_BACKEND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAMES_TYPE_SCORES_BACKEND, GamesScoresBackendClass))
+#define GAMES_IS_SCORES_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAMES_TYPE_SCORES_BACKEND))
+#define GAMES_IS_SCORES_BACKEND_CLASS(kls) (G_TYPE_CHECK_CLASS_TYPE ((kls), GAMES_TYPE_SCORES_BACKEND))
+#define GAMES_GET_SCORES_BACKEND_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAMES_TYPE_SCORES_BACKEND, GamesScoresBackendClass))
+
+typedef struct _GamesScoresBackend        GamesScoresBackend;
+typedef struct _GamesScoresBackendPrivate GamesScoresBackendPrivate;
+typedef struct _GamesScoresBackendClass   GamesScoresBackendClass;
+
+struct _GamesScoresBackend {
+  GObject object;
+  GList *scores_list;
+  GamesScoresBackendPrivate *priv;
+};
+
+struct _GamesScoresBackendClass {
+  GObjectClass parent_class;
+};
+
+GType games_scores_backend_get_type (void);
+GamesScoresBackend *games_scores_backend_new (GamesScoreStyle style,
+					      char *base_name,
+                                              char *name);
+GList *games_scores_backend_get_scores (GamesScoresBackend * self);
+gboolean games_scores_backend_set_scores (GamesScoresBackend * self,
+				          GList * list);
+void games_scores_backend_discard_scores (GamesScoresBackend * self);
+
+G_END_DECLS
+#endif /* GAMES_SCORES_BACKEND_H */
diff --git a/src/games-scores-dialog-private.h b/src/games-scores-dialog-private.h
new file mode 100644
index 0000000..6260ed7
--- /dev/null
+++ b/src/games-scores-dialog-private.h
@@ -0,0 +1,53 @@
+/* -*- mode: C -*-
+
+   games-scores-dialog.h
+   Copyright 2004, 2005, 2006 Callum McKenzie
+
+   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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Authors:   Callum McKenzie <callum physics otago ac nz> */
+
+#ifndef GAMES_SCORES_DIALOG_PRIVATE_H
+#define GAMES_SCORES_DIALOG_PRIVATE_H
+
+G_BEGIN_DECLS
+
+struct _GamesScoresDialogPrivate {
+  GtkWidget *message;
+  GtkWidget *hdiv;
+  GtkWidget *combo;
+  GtkWidget *label;
+  GtkWidget *catbar;
+  GtkListStore *list;
+  GtkTreeView *treeview;
+  GtkCellRenderer *namerenderer;
+  GtkTreeViewColumn *column;
+  GtkTreeViewColumn *namecolumn;
+  GamesScores *scores;
+  GHashTable *categories;
+  GHashTable *catindices;
+  gint catcounter;
+  gint hilight;
+  gint sethilight;
+  gboolean preservehilight;
+  gulong cursor_handler_id;
+
+  /* FIXME: This should be a property. */
+  gint style;
+};
+
+
+G_END_DECLS
+#endif
diff --git a/src/games-scores-dialog.c b/src/games-scores-dialog.c
new file mode 100644
index 0000000..3209090
--- /dev/null
+++ b/src/games-scores-dialog.c
@@ -0,0 +1,568 @@
+/* -*- mode: C -*-
+
+   games-scores-dialog.c
+   Copyright 2004, 2005, 2006 Callum McKenzie
+
+   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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Authors:   Callum McKenzie <callum physics otago ac nz> */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "games-scores-dialog.h"
+#include "games-scores-dialog-private.h"
+#include "games-gtk-compat.h"
+
+G_DEFINE_TYPE (GamesScoresDialog, games_scores_dialog, GTK_TYPE_DIALOG);
+
+static void
+games_scores_dialog_finalize (GObject *o)
+{
+  GamesScoresDialog *dialog = GAMES_SCORES_DIALOG (o);
+
+  if (dialog->_priv->scores)
+    g_object_unref (dialog->_priv->scores);
+
+  if (dialog->_priv->categories)
+    g_hash_table_destroy (dialog->_priv->categories);
+  if (dialog->_priv->catindices)
+    g_hash_table_destroy (dialog->_priv->catindices);
+
+  G_OBJECT_CLASS (games_scores_dialog_parent_class)->finalize (o);
+}
+
+static void
+games_scores_dialog_class_init (GamesScoresDialogClass *klass)
+{
+  g_type_class_add_private (klass, sizeof (GamesScoresDialogPrivate));
+
+  G_OBJECT_CLASS (klass)->finalize = games_scores_dialog_finalize;
+}
+
+/**
+ * add_category:
+ * @self: a pointer to a GamesScoresDialog
+ * @key: an identifier for the category. This should also be a valid
+ * score category for the gnome_score system.
+ * @name: the category name
+ * 
+ * Adds a new category to combo box selector.
+ *
+ **/
+static void games_scores_dialog_add_category (GamesScoresDialog *self, 
+				       const gchar *key, 
+				       const gchar *name)
+{
+  gchar *k;
+
+  k = g_strdup (key);
+
+  g_hash_table_insert (self->_priv->categories, k, 
+			 GINT_TO_POINTER (self->_priv->catcounter));
+  g_hash_table_insert (self->_priv->catindices, 
+			 GINT_TO_POINTER (self->_priv->catcounter),
+			 k);
+  self->_priv->catcounter++;
+  gtk_combo_box_append_text (GTK_COMBO_BOX (self->_priv->combo), name);
+}
+
+/* This is a helper function for loading the initial list of categories
+ * in a _foreach function. If only C had lambda... */
+static void games_scores_dialog_load_categories (GamesScoresCategory *cat, 
+						 GamesScoresDialog *self) 
+{
+    /* category->name is already translated here! */
+    games_scores_dialog_add_category (self, cat->key, cat->name);
+}
+
+/**
+ * set_style:
+ * @self: a pointer to a GamesScoresDialog
+ * @style: the style to use
+ * 
+ * Sets the style of score displayed. e.g. GAMES_SCORES_STYLE_TIME
+ * displays the scores as times. Note that the order of the scores
+ * is determined at the gnome_score layer but their interpretation
+ * is at this layer.
+ *
+ **/
+static void games_scores_dialog_set_style (GamesScoresDialog *self, 
+					   GamesScoreStyle style) 
+{
+  gchar *header;
+
+  self->_priv->style = style;
+  switch (style) {
+  case GAMES_SCORES_STYLE_TIME_DESCENDING:
+  case GAMES_SCORES_STYLE_TIME_ASCENDING:
+    header = _("Time");
+    break;
+  case GAMES_SCORES_STYLE_PLAIN_DESCENDING:
+  case GAMES_SCORES_STYLE_PLAIN_ASCENDING:
+  default:
+    header = _("Score");
+  }
+
+  gtk_tree_view_column_set_title (self->_priv->column, header);
+}
+
+/**
+ * set_category:
+ * @self: a pointer to a GamesScoresDialog
+ * @key: the category to change to
+ * 
+ * Sets the category the scores dialog is displaying.
+ *
+ **/
+static void games_scores_dialog_set_category (GamesScoresDialog *self, 
+					      const gchar *key) 
+{
+  gpointer value;
+  int idx;
+
+  value = g_hash_table_lookup (self->_priv->categories, key);
+  idx = GPOINTER_TO_INT (value);
+
+  self->_priv->preservehilight = TRUE;
+  gtk_combo_box_set_active (GTK_COMBO_BOX (self->_priv->combo), idx);
+}
+
+/**
+ * new:
+ * @domain: the scores domain to use, usually the application name
+ * @title: the title for the dialog
+ * 
+ * Creates a new high scores dialog. Use gtk_dialog_run and 
+ * gtk_widget_destroy to manage it.
+ *
+ * Returns: a new widget
+ *
+ **/
+GtkWidget * games_scores_dialog_new (GtkWindow *parent_window, GamesScores *scores, const gchar *title)
+{
+  GamesScoresDialog *dialog = GAMES_SCORES_DIALOG (g_object_new (GAMES_TYPE_SCORES_DIALOG, NULL));
+
+  dialog->_priv->scores = g_object_ref (scores);
+  games_scores_dialog_set_style (dialog, games_scores_get_style (scores));
+  dialog->_priv->preservehilight = FALSE;
+
+  gtk_window_set_title (GTK_WINDOW (dialog), title);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
+
+  _games_scores_category_foreach (scores, 
+                                  (GamesScoresCategoryForeachFunc) games_scores_dialog_load_categories,
+                                  dialog);
+
+  if (dialog->_priv->catcounter <= 1) {
+    gtk_widget_hide (dialog->_priv->catbar);
+  }
+
+  return (GtkWidget *)dialog;
+}
+
+/* Retrieve the edited name from a new high score. */
+static void games_scores_dialog_name_edited (GtkCellRendererText *cell, 
+					     gchar *path, gchar *new_text, 
+					     GamesScoresDialog *self)
+{
+  GtkTreeIter iter;
+  gchar *old_name = NULL;
+
+  gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (self->_priv->list), 
+				       &iter, path);
+
+  /* Get old name for comparison */
+  gtk_tree_model_get (GTK_TREE_MODEL (self->_priv->list),
+                      &iter, 0, &old_name, -1);
+                           
+  gtk_list_store_set (self->_priv->list, &iter, 0, new_text, -1);
+
+  games_scores_update_score_name (self->_priv->scores, new_text, old_name);
+}
+
+/* Prevent editing of any cell in the high score list but the one we set. */
+static void games_scores_dialog_cursor_changed (GtkTreeView *treeview, 
+						GamesScoresDialog *self)
+{
+  g_object_set (self->_priv->namerenderer, "editable", FALSE, NULL);
+}    
+
+/* These contortions are to ensure that only the single most-recent
+ * entry can be edited. */
+static gboolean games_scores_dialog_set_edit (GamesScoresDialog *self)
+{
+  GtkTreePath *path;
+  GtkTreeSelection *selection;
+
+  /* Just in case we've been closed as soon as we're created. */
+  if (!GTK_WIDGET_REALIZED (self))
+    return FALSE;
+
+  /* Temporarily disable the code that prevents editing when the
+   * cursor changes position. */
+  g_signal_handler_block (self->_priv->treeview, 
+			    self->_priv->cursor_handler_id); 
+  g_object_set (self->_priv->namerenderer, "editable", TRUE, NULL);
+  selection = gtk_tree_view_get_selection (self->_priv->treeview);
+  path = gtk_tree_path_new_from_indices (self->_priv->hilight - 1, -1);
+  gtk_tree_selection_select_path (selection, path);
+  gtk_tree_view_set_cursor (self->_priv->treeview, path, 
+			      self->_priv->namecolumn, TRUE);
+  g_signal_handler_unblock (self->_priv->treeview, 
+			      self->_priv->cursor_handler_id); 
+  gtk_tree_path_free (path);
+
+  return FALSE;
+}
+
+/* Yet another part of the puzzle that lets the correct high-score be
+ * editable. */
+static void games_scores_dialog_set_hilight_private (GamesScoresDialog *self) 
+{
+  if (self->_priv->hilight == 0) {
+    g_object_set (self->_priv->namerenderer, "editable", FALSE, NULL);
+    return;
+  }
+
+  if (self->_priv->hilight == self->_priv->sethilight)
+    return;
+
+  self->_priv->sethilight = self->_priv->hilight;
+
+  /* We can't set the hilight editable immediately in case we are
+   * still in the process of being created and the editing subwindow
+   * gets put in the wrong place. Attaching to the expose signal
+   * doesn't seem to have the desired effect, so instead we just
+   * wait until all other work is done. */
+  g_idle_add ((GSourceFunc)games_scores_dialog_set_edit, self);
+}
+
+/* Load up the list with the current set of scores. */
+static void games_scores_dialog_redraw (GamesScoresDialog *self) {
+  GtkTreeIter iter;
+  gchar *name;
+  gint score;
+  gchar *ss;
+  gdouble dscore;
+  GList *scorelist;
+
+  gtk_list_store_clear (self->_priv->list);
+
+  scorelist = games_scores_get (self->_priv->scores);
+
+  while (scorelist) {
+    name = ((GamesScore *)scorelist->data)->name;
+    switch (self->_priv->style) {
+    case GAMES_SCORES_STYLE_TIME_ASCENDING:
+    case GAMES_SCORES_STYLE_TIME_DESCENDING:
+      dscore = ((GamesScore *)scorelist->data)->value.time_double;
+      score = (int) (100.0 * dscore + 0.5);
+      /* Translators: this is for a minutes, seconds time display. */
+      ss = g_strdup_printf (_("%dm %ds"), score/100, score%100);
+      break; 
+    case GAMES_SCORES_STYLE_PLAIN_ASCENDING:
+    case GAMES_SCORES_STYLE_PLAIN_DESCENDING:
+    default:
+      score = ((GamesScore *)scorelist->data)->value.plain;
+      ss = g_strdup_printf ("%d", score);
+    }
+    gtk_list_store_append (self->_priv->list, &iter);
+    gtk_list_store_set (self->_priv->list, &iter, 0, name, 1, ss, -1);
+    g_free (ss);
+    scorelist = g_list_next (scorelist);
+  }
+    
+  games_scores_dialog_set_hilight_private (self);
+}
+
+/* Change the currently viewed score category. There is a little bit
+ * of silly-buggers here to make sure the change is temporary and
+ * we end up on the right page next time. */
+static void games_scores_dialog_change_category (GtkComboBox *widget, 
+						 GamesScoresDialog *self)
+{
+  gchar *catcopy;
+  gint idx;
+  gchar *newcat;
+  
+  /* This seems like a bit of a hack, but since we're trying to
+   * temporarily change the category it sort of makes sense. */
+
+  catcopy = g_strdup (games_scores_get_category (self->_priv->scores));
+  idx = gtk_combo_box_get_active (widget);
+  newcat = g_hash_table_lookup (self->_priv->catindices,
+				 GINT_TO_POINTER (idx));
+
+  games_scores_set_category (self->_priv->scores, newcat);
+  if (self->_priv->preservehilight) {
+    self->_priv->preservehilight = FALSE;
+  } else {
+    self->_priv->hilight = 0;
+  }
+  games_scores_dialog_redraw (self);
+  games_scores_set_category (self->_priv->scores, catcopy);
+
+  g_free (catcopy);
+}
+
+/* This is to make sure we update ourselves when the window goes through
+ * a hide/show cycle. */
+/* FIXME: We should monitor the high scores list (or get games-scores to
+ * send us a signal. */
+static void games_scores_dialog_show (GamesScoresDialog *self)
+{ 
+  const gchar *cat;
+  
+  cat = games_scores_get_category (self->_priv->scores);
+  if (cat)
+    games_scores_dialog_set_category (self, cat);
+  games_scores_dialog_redraw (self);
+}
+
+/* This is the other half of ensuring the hide/show cycle works properly. */
+static void games_scores_dialog_hide (GamesScoresDialog *self) {
+  self->_priv->hilight = 0;
+  gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (self->_priv->treeview));
+}
+
+/**
+ * set_category_description:
+ * @self: a pointer to a GamesScoresDialog
+ * @description: A description of the categories 
+ * 
+ * Sets the category description label. i.e. the widget to the
+ * left of the category combo box. 
+ *
+ **/
+void games_scores_dialog_set_category_description (GamesScoresDialog *self, 
+						   const gchar *description)
+{
+  gchar *lstr;
+
+  lstr = g_strdup_printf ("<b>%s</b>", description);
+  gtk_label_set_markup (GTK_LABEL (self->_priv->label), lstr);
+  gtk_label_set_use_underline (GTK_LABEL (self->_priv->label), TRUE);
+  g_free(lstr);
+}
+
+/**
+ * set_message:
+ * @self: a pointer to a GamesScoresDialog
+ * @message: the message
+ * 
+ * Sets the message at the top of the dialog. Pango markup is understood.
+ *
+ **/
+void games_scores_dialog_set_message (GamesScoresDialog *self, 
+				      const gchar *message)
+{
+  if ((message == NULL) || (*message == '\0')) {
+    gtk_widget_hide (self->_priv->message);
+    gtk_widget_hide (self->_priv->hdiv);
+  } else {
+    gtk_widget_show (self->_priv->message);
+    gtk_widget_show (self->_priv->hdiv);
+    gtk_label_set_label (GTK_LABEL (self->_priv->message), message);
+  }
+}
+
+/**
+ * set_category_description:
+ * @self: a pointer to a GamesScoresDialog
+ * @pos: the position in the high score list to hilight. Should be in the
+ * range 1 to 10.
+ * 
+ * Hilights an entry in the high score list. This is suitable for indicating
+ * to the player where the game they just played is.
+ *
+ **/
+void games_scores_dialog_set_hilight (GamesScoresDialog *self, guint pos)
+{
+  if ((pos < 1) || (pos > GAMES_SCORES_SIGNIFICANT))
+    return;
+
+  self->_priv->hilight = pos;
+  games_scores_dialog_set_hilight_private (self);
+}
+
+/**
+ * set_buttons:
+ * @self: a pointer to a GamesScoresDialog
+ * @buttons: An or-ed list of GamesScoresButtons
+ * 
+ * Changes the button sets at the buttom of the dialog
+ *
+ **/
+void games_scores_dialog_set_buttons (GamesScoresDialog *self, guint buttons)
+{
+  /* Remove an existing buttons. */
+  gtk_container_foreach (GTK_CONTAINER (gtk_dialog_get_action_area (GTK_DIALOG (self))),
+                         (GtkCallback) (gtk_widget_destroy), NULL);
+
+  /* The default is a single close button, suitable for the scores
+     menu item. */
+  if (buttons == 0)
+	buttons = GAMES_SCORES_CLOSE_BUTTON;
+
+  if (buttons & GAMES_SCORES_QUIT_BUTTON) {
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_QUIT,
+	                       GTK_RESPONSE_REJECT);
+      gtk_dialog_set_default_response (GTK_DIALOG (self), 
+	       			         GTK_RESPONSE_REJECT);
+  }
+
+  if (buttons & GAMES_SCORES_UNDO_BUTTON) {
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_UNDO,
+	                       GTK_RESPONSE_DELETE_EVENT);
+      gtk_dialog_set_default_response (GTK_DIALOG (self), 
+	       			         GTK_RESPONSE_DELETE_EVENT);
+  }
+
+  if (buttons & GAMES_SCORES_NEW_GAME_BUTTON) {
+	gtk_dialog_add_button (GTK_DIALOG (self), _("New Game"),
+	                       GTK_RESPONSE_ACCEPT);
+      gtk_dialog_set_default_response (GTK_DIALOG (self), 
+	       			         GTK_RESPONSE_ACCEPT);
+  }
+
+  if (buttons & GAMES_SCORES_CLOSE_BUTTON) {
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CLOSE,
+	                       GTK_RESPONSE_CLOSE);
+      gtk_dialog_set_default_response (GTK_DIALOG (self), 
+	       			         GTK_RESPONSE_CLOSE);
+  }
+}
+
+static void games_scores_dialog_init (GamesScoresDialog *self) 
+{
+  GtkWidget *vbox;
+  GtkWidget *scroll;
+  GtkWidget *listview;
+  GtkTreeViewColumn *column;
+  GtkCellRenderer *renderer;
+
+  self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GAMES_TYPE_SCORES_DIALOG,
+					     GamesScoresDialogPrivate);
+
+  self->_priv->style = GAMES_SCORES_STYLE_PLAIN_DESCENDING;
+  /* These two hashes are the reverse of each other. As an optimisation 
+   * they share the same set of strings (as keys in the first case and
+   * as data in the second). The first hash is responsible for 
+   * deallocating the memory for the strings. These two are only
+   * valid as a pair. */
+  self->_priv->categories = g_hash_table_new_full (g_str_hash, g_str_equal,
+						     g_free, NULL);
+  self->_priv->catindices = g_hash_table_new (g_direct_hash, g_direct_equal);
+  self->_priv->catcounter = 0;
+  self->_priv->hilight = 0;
+  self->_priv->sethilight = -1;
+
+  gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
+  gtk_container_set_border_width (GTK_CONTAINER (self), 5);
+  gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 2);
+
+  g_signal_connect (G_OBJECT (self), "show", 
+		      G_CALLBACK (games_scores_dialog_show), NULL);
+  g_signal_connect (G_OBJECT (self), "hide", 
+		      G_CALLBACK (games_scores_dialog_hide), NULL);
+
+  vbox = gtk_vbox_new (FALSE, 6);
+  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
+  gtk_box_pack_end (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))),
+                    vbox, TRUE, TRUE, 0);
+
+  scroll = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+				    GTK_POLICY_AUTOMATIC,
+				    GTK_POLICY_AUTOMATIC);
+  gtk_widget_set_size_request (scroll, 250, 265);
+  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
+					 GTK_SHADOW_ETCHED_IN);
+  gtk_box_pack_end (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
+  
+  self->_priv->message = gtk_label_new ("");
+  gtk_label_set_use_markup (GTK_LABEL (self->_priv->message), TRUE);
+  gtk_label_set_justify (GTK_LABEL (self->_priv->message), 
+	                   GTK_JUSTIFY_CENTER);    
+  gtk_box_pack_start (GTK_BOX (vbox), self->_priv->message, FALSE, FALSE, 0);
+
+  self->_priv->hdiv = gtk_hseparator_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), self->_priv->hdiv, FALSE, FALSE, 0);
+
+  self->_priv->catbar = gtk_hbox_new (FALSE, 12);
+  gtk_box_pack_start (GTK_BOX (vbox), self->_priv->catbar, FALSE, FALSE, 0);
+
+  self->_priv->label = gtk_label_new (NULL);
+  gtk_label_set_use_markup (GTK_LABEL (self->_priv->label), TRUE);
+  gtk_box_pack_start (GTK_BOX (self->_priv->catbar), self->_priv->label,
+			FALSE, FALSE, 0);	
+ 
+  self->_priv->combo = gtk_combo_box_new_text ();
+  gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (self->_priv->combo), FALSE);
+  gtk_box_pack_start (GTK_BOX (self->_priv->catbar), 
+			self->_priv->combo, TRUE, TRUE, 0);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (self->_priv->label), self->_priv->combo);
+
+  g_signal_connect (G_OBJECT (self->_priv->combo), "changed", 
+		      G_CALLBACK (games_scores_dialog_change_category), self);
+
+  self->_priv->list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+  listview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->_priv->list));
+  self->_priv->treeview = GTK_TREE_VIEW (listview);
+  self->_priv->cursor_handler_id = 
+    g_signal_connect (G_OBJECT (self->_priv->treeview), 
+			"cursor-changed", 
+			G_CALLBACK (games_scores_dialog_cursor_changed), self);
+  
+  self->_priv->namerenderer = gtk_cell_renderer_text_new ();
+  g_signal_connect (self->_priv->namerenderer, "edited", 
+		      G_CALLBACK (games_scores_dialog_name_edited), self);
+
+  self->_priv->namecolumn = gtk_tree_view_column_new_with_attributes (_("Name"),
+									self->_priv->namerenderer,
+									"text", 0,
+									NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (listview),
+				 GTK_TREE_VIEW_COLUMN (self->_priv->namecolumn));
+  renderer = gtk_cell_renderer_text_new ();
+  /* Note that this assumes the default style is plain. */
+  column = gtk_tree_view_column_new_with_attributes (_("Score"),
+						       renderer,
+						       "text", 1,
+						       NULL);
+  g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (listview),
+				 GTK_TREE_VIEW_COLUMN (column));
+  self->_priv->column = column;
+ 
+  gtk_container_add (GTK_CONTAINER (scroll), listview);
+  
+  games_scores_dialog_set_buttons (self, GAMES_SCORES_CLOSE_BUTTON);
+
+  gtk_window_set_destroy_with_parent (GTK_WINDOW (self), TRUE);
+
+  gtk_widget_grab_focus (self->_priv->combo);
+
+  gtk_widget_show_all (vbox);
+  gtk_widget_hide (self->_priv->hdiv);
+  gtk_widget_hide (self->_priv->message);
+}
+
+/* FIXME: There is basically no range checking. */
+
diff --git a/src/games-scores-dialog.h b/src/games-scores-dialog.h
new file mode 100644
index 0000000..f63333d
--- /dev/null
+++ b/src/games-scores-dialog.h
@@ -0,0 +1,79 @@
+/* -*- mode: C -*-
+
+   games-scores-dialog.h
+   Copyright 2004, 2005, 2006 Callum McKenzie
+
+   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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+/* Authors:   Callum McKenzie <callum physics otago ac nz> */
+
+#ifndef GAMES_SCORES_DIALOG_H
+#define GAMES_SCORES_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#include "games-score.h" /* For GamesScoreStyle. */
+#include "games-scores.h"
+
+G_BEGIN_DECLS
+#define GAMES_TYPE_SCORES_DIALOG (games_scores_dialog_get_type ())
+#define GAMES_SCORES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+                                  GAMES_TYPE_SCORES_DIALOG, \
+                                  GamesScoresDialog))
+#define GAMES_SCORES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+                                          GAMES_TYPE_SCORES_DIALOG, \
+                                          GamesScoresDialogClass))
+#define GAMES_IS_SCORES_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+                                     GAMES_TYPE_SCORES_DIALOG))
+#define GAMES_IS_SCORES_DIALOG_CLASS(kls) (G_TYPE_CHECK_CLASS_TYPE ((kls), \
+                                           GAMES_TYPE_SCORES_DIALOG))
+#define GAMES_GET_SCORES_DIALOG_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+                                            GAMES_TYPE_SCORES_DIALOG, \
+                                            GamesScoresDialogClass))
+
+/* This enumeration is to provide common buttons for the dialog. */
+
+typedef enum {
+  GAMES_SCORES_CLOSE_BUTTON = 1,
+  GAMES_SCORES_NEW_GAME_BUTTON = 2,	
+  GAMES_SCORES_UNDO_BUTTON = 4,
+  GAMES_SCORES_QUIT_BUTTON = 8,
+} GamesScoresButtons;
+
+typedef struct _GamesScoresDialogPrivate GamesScoresDialogPrivate;
+
+typedef struct _GamesScoresDialog {
+  GtkDialog dialog;
+
+  /* <private> */
+  GamesScoresDialogPrivate *_priv;
+} GamesScoresDialog;
+
+typedef struct _GamesScoresDialogClass {
+  GtkDialogClass parent_class;
+} GamesScoresDialogClass;
+
+GType games_scores_dialog_get_type (void);
+
+GtkWidget * games_scores_dialog_new (GtkWindow *parent_window, GamesScores *scores, const gchar *title);
+void games_scores_dialog_set_category_description (GamesScoresDialog *self, 
+						   const gchar *description);
+void games_scores_dialog_set_hilight (GamesScoresDialog *self, guint pos);
+void games_scores_dialog_set_message (GamesScoresDialog *self, 
+				      const gchar *message);
+void games_scores_dialog_set_buttons (GamesScoresDialog *self, guint buttons);
+
+G_END_DECLS
+#endif
diff --git a/src/games-scores.c b/src/games-scores.c
new file mode 100644
index 0000000..8972a09
--- /dev/null
+++ b/src/games-scores.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright 2005 Callum McKenzie
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ * 
+ */
+
+/* Authors:   Callum McKenzie <callum physics otago ac nz> */
+
+/* FIXME: Document */
+
+/* FIXME: Add a finaliser to get rid of some of the strings. */
+
+#include <config.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+
+#include "games-scores-backend.h"
+#include "games-score.h"
+#include "games-scores.h"
+
+/* The local version of the GamesScoresCategory. */
+typedef struct {
+  GamesScoresCategory category;
+  GamesScoresBackend *backend;
+} GamesScoresCategoryInternal;
+
+struct _GamesScoresPrivate {
+  GHashTable *categories;
+  GSList *catsordered;
+  gchar *currentcat;
+  gchar *defcat;
+  gchar *basename;
+  gboolean last_score_significant;
+  gint last_score_position;
+  GamesScoreValue last_score_value;
+  GamesScoreStyle style;
+  GamesScoresCategoryInternal dummycat;
+};
+
+static void
+games_scores_category_free (GamesScoresCategoryInternal *cat)
+{
+  g_free (cat->category.key);
+  g_free (cat->category.name);
+  if (cat->backend)
+    g_object_unref (cat->backend);
+  g_free (cat);
+}
+
+/**
+ * get_current:
+ * @self: A scores object.
+ *
+ * Retrieves the current category and make sure it is in a state to be used.
+ *
+ **/
+static GamesScoresCategoryInternal *
+games_scores_get_current (GamesScores * self)
+{
+  GamesScoresPrivate *priv = self->priv;
+  GamesScoresCategoryInternal *cat;
+
+  if (priv->currentcat == NULL) {
+    /* We have a single, anonymous, category. */
+    cat = &(priv->dummycat);
+  } else {
+    cat = g_hash_table_lookup (priv->categories, priv->currentcat);
+    if (!cat)
+      return NULL;
+  }
+
+  if (cat->backend == NULL) {
+    cat->backend = games_scores_backend_new (priv->style, priv->basename,
+                                             cat->category.key);
+  }
+
+  return cat;
+}
+
+/* FIXME: Static games_score_init function to initialise the setgid stuff. */
+/* FIXME: This is actually an argument for a helper-app since this function
+ * won't know what files we are after until _new is called. */
+
+G_DEFINE_TYPE (GamesScores, games_scores, G_TYPE_OBJECT);
+
+/** 
+ * games_scores_new:
+ * @app_name: the (old) app name (for backward compatibility),
+ *   used as the basename of the category filenames
+ * @categories: the score categories, or %NULL to use an anonymous category
+ * @n_categories: the number of category entries in @categories
+ * @categories_context: the translation context to use for the category names,
+ *   or %NULL to use no translation context
+ * @categories_domain: the translation domain to use for the category names,
+ *   or %NULL to use the default domain
+ * @default_category: the key of the default category, or %NULL
+ * @style: the category style
+ * 
+ *
+ * Returns: a new #GamesScores object
+ */
+GamesScores *
+games_scores_new (const char *app_name,
+                  const GamesScoresCategory *categories,
+                  int n_categories,
+                  const char *categories_context,
+                  const char *categories_domain,
+                  int default_category_index,
+                  GamesScoreStyle style)
+{
+  GamesScores *self;
+  GamesScoresPrivate *priv;
+
+  self = GAMES_SCORES (g_object_new (GAMES_TYPE_SCORES, NULL));
+  priv = self->priv;
+
+  /* FIXME: Input sanity checks. */
+
+  priv->categories = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                            g_free,
+                                            (GDestroyNotify) games_scores_category_free);
+
+  /* catsordered is a record of the ordering of the categories. 
+   * Its data is shared with the hash table. */
+  priv->catsordered = NULL;
+
+  if (n_categories > 0) {
+    int i;
+
+    g_return_val_if_fail (default_category_index >= 0 && default_category_index < n_categories, NULL);
+
+    for (i = 0; i < n_categories; ++i) {
+      const GamesScoresCategory *category = &categories[i];
+      const char *display_name;
+
+      if (categories_context) {
+        display_name = g_dpgettext2 (categories_domain, categories_context, category->name);
+      } else {
+        display_name = dgettext (categories_domain, category->name);
+      }
+
+      games_scores_add_category (self, category->key, display_name);
+    }
+
+    priv->defcat = g_strdup (categories[default_category_index].key);
+    priv->currentcat = g_strdup (priv->defcat);
+  } else {
+    priv->currentcat = NULL;
+    priv->defcat = NULL;
+  }
+
+  priv->basename = g_strdup (app_name);
+  /* FIXME: Do some sanity checks on the default and the like. */
+
+  priv->style = style;
+
+  /* Set up the anonymous category for use when no categories are specified. */
+  priv->dummycat.category.key = (char *) "";
+  priv->dummycat.category.name = (char *) "";
+  priv->dummycat.backend = NULL;
+
+  return self;
+}
+
+/**
+ * games_scores_add_category:
+ * @self:
+ * @key: the key for the new category
+ * @name: the user visible label for the new category
+ *
+ * Add a new category after initialisation. key and name are copied into
+ * internal structures. The scores dialog is not currently updated.
+ *
+ **/
+void
+games_scores_add_category (GamesScores *self,
+                           const char *key,
+                           const char *name)
+{
+  GamesScoresPrivate *priv = self->priv;
+  GamesScoresCategoryInternal *cat;
+
+  cat = g_new (GamesScoresCategoryInternal, 1);
+  cat->category.key = g_strdup (key);
+  cat->category.name = g_strdup (name);
+  cat->backend = NULL;
+
+  g_hash_table_insert (priv->categories, g_strdup (key), cat);
+  priv->catsordered = g_slist_append (priv->catsordered, cat);
+}
+
+/**
+ * set_category:
+ * @scores: A scores object.
+ * @category: A string identifying the category to use (the key in
+ *            the GamesScoresCategory structure).
+ *
+ * This function sets the scores category to use. e.g. whether we are playing
+ * on hard, medium or easy. It should be used at the time that the game
+ * itself switches between difficulty levels. The category determines where
+ * scores are to be stored and read from.
+ *
+ **/
+void
+games_scores_set_category (GamesScores * self, gchar * category)
+{
+  GamesScoresPrivate *priv = self->priv;
+
+  g_return_if_fail (self != NULL);
+
+  if (category == NULL)
+    category = priv->defcat;
+
+  g_free (priv->currentcat);
+  priv->currentcat = g_strdup (category);
+
+  /* FIXME: Check validity of category (Null, the same as current, 
+   * is actually a category) then just set it in the structure. */
+}
+
+/**
+ * add_score:
+ * @self: A scores object.
+ * @score: A GamesScoreValue - it is up to the caller to convert their
+ *         raw value to one of the supported types.
+ *
+ * Add a score to the set of scores. Retention of anything but the
+ * top-ten scores is undefined. It returns either the place in the top ten
+ * or zero if no place was achieved. It can therefore be treated as a
+ * boolean if desired.
+ *
+ **/
+gint
+games_scores_add_score (GamesScores * self, GamesScoreValue score)
+{
+  GamesScoresPrivate *priv = self->priv;
+  GamesScore *fullscore;
+  GamesScoresCategoryInternal *cat;
+  gint place, n;
+  GList *s, *scores_list;
+
+  g_return_val_if_fail (self != NULL, 0);
+
+  fullscore = games_score_new ();
+  fullscore->value = score;
+
+  cat = games_scores_get_current (self);
+
+  scores_list = games_scores_backend_get_scores (cat->backend);
+
+  s = scores_list;
+  place = 0;
+  n = 0;
+
+  while (s != NULL) {
+    GamesScore *oldscore = s->data;
+
+    n++;
+
+    /* If beat someone in the list, add us there. */
+    if (games_score_compare (priv->style, oldscore, fullscore) < 0) {
+      scores_list = g_list_insert_before (scores_list, s,
+					  games_score_dup (fullscore));
+      place = n;
+      break;
+    }
+
+    s = g_list_next (s);
+  }
+
+  /* If we haven't placed anywhere and the list still has 
+   * room to grow, put us on the end. 
+   * This also handles the empty-file case. */
+  if ((place == 0) && (n < GAMES_SCORES_SIGNIFICANT)) {
+    place = n + 1;
+    scores_list = g_list_append (scores_list, games_score_dup (fullscore));
+  }
+
+  if (g_list_length (scores_list) > GAMES_SCORES_SIGNIFICANT) {
+    s = g_list_nth (scores_list, GAMES_SCORES_SIGNIFICANT - 1);
+    /* Note that we are guaranteed to only need to remove one link
+     * and it is also guaranteed not to be the first one. */
+    games_score_destroy ((GamesScore *) (g_list_next (s)->data));
+    g_list_free (g_list_next (s));
+    s->next = NULL;
+  }
+
+  if (games_scores_backend_set_scores (cat->backend, scores_list) == FALSE)
+    place = 0;
+
+  priv->last_score_significant = place > 0;
+  priv->last_score_position = place;
+  priv->last_score_value = score;
+
+  return place;
+}
+
+/**
+ * games_scores_update_score_name:
+ * @self: A scores object.
+ * @new_name: The new name to use.
+ *
+ * By default add_score uses the current user name. This routine updates
+ * that name. There are a few wrinkles: the score may have moved since we
+ * got the original score. Use in normal code is discouraged, it is here 
+ * to be used by GamesScoresDialog.
+ *
+ **/
+void
+games_scores_update_score_name (GamesScores * self, gchar * new_name, gchar * old_name)
+{
+  GamesScoresPrivate *priv = self->priv;
+  GamesScoresCategoryInternal *cat;
+  GList *s, *scores_list;
+  gint n, place;
+  GamesScore *sc;
+  GamesScoreValue score;
+
+  g_return_if_fail (self != NULL);
+
+  place = priv->last_score_position;
+  score = priv->last_score_value;
+
+  if (place == 0)
+    return;
+
+  if (old_name)
+      old_name = g_strdup (old_name); /* Make copy so we can free it later */
+  else
+      old_name = g_strdup (g_get_real_name ());
+
+  cat = games_scores_get_current (self);
+
+  scores_list = games_scores_backend_get_scores (cat->backend);
+
+  s = g_list_last (scores_list);
+  n = g_list_length (scores_list);
+
+  /* We hunt backwards down the list until we find the last entry with
+   * a matching user and score. */
+  /* The check that we haven't gone back before place isn't just a
+   * pointless optimisation. It also catches the case where our score
+   * has been dropped from the high-score list in the meantime. */
+
+  while ((n >= place) && (s != NULL)) {
+    sc = (GamesScore *) (s->data);
+    if ((games_score_compare_values (priv->style, sc->value, score) ==
+	 0) && (g_utf8_collate (old_name, sc->name) == 0)) {
+      g_free (sc->name);
+      sc->name = g_strdup (new_name);
+    }
+
+    s = g_list_previous (s);
+    n--;
+  }
+
+  games_scores_backend_set_scores (cat->backend, scores_list);
+
+  g_free (old_name);
+}
+
+/**
+ * games_scores_update_score:
+ * @self: A scores object.
+ * @new_name: The new name to use.
+ *
+ * By default add_score uses the current user name. This routine updates
+ * that name. There are a few wrinkles: the score may have moved since we
+ * got the original score. Use in normal code is discouraged, it is here 
+ * to be used by GamesScoresDialog.
+ *
+ **/
+void
+games_scores_update_score (GamesScores * self, gchar * new_name)
+{
+    games_scores_update_score_name (self, new_name, NULL);
+}
+
+/**
+ * get:
+ * @self: A scores object.
+ *
+ * Get a list of GamesScore objects for the current category. The list
+ * is still owned by the GamesScores object and is not guaranteed to
+ * be the either the same or accurate after any games_scores call
+ * except games_scores_get. Do not alter the data either.
+ **/
+GList *
+games_scores_get (GamesScores * self)
+{
+  GamesScoresCategoryInternal *cat;
+  GList *scores;
+
+  g_return_val_if_fail (self != NULL, NULL);
+
+  cat = games_scores_get_current (self);
+
+  scores = games_scores_backend_get_scores (cat->backend);
+  /* Tell the backend that we won't be altering the scores so it
+   * can release the lock. */
+  games_scores_backend_discard_scores (cat->backend);
+
+  return scores;
+}
+
+/**
+ * _games_scores_category_foreach:
+ * @self: A scores object.
+ * @func: A function to call.
+ * @userdata: Arbitrary data.
+ *
+ * This function will iterate over the list of categories calling the
+ * supplied function with the category and userdata as arguments.
+ * The ordering of the categories is the order they were added.
+ *
+ **/
+void
+_games_scores_category_foreach (GamesScores * self,
+                                GamesScoresCategoryForeachFunc func,
+                                gpointer userdata)
+{
+  GamesScoresPrivate *priv = self->priv;
+  GSList *l;
+
+  g_return_if_fail (self != NULL);
+
+  for (l = priv->catsordered; l != NULL; l = l->next) {
+    func ((GamesScoresCategory*) l->data, userdata);
+  }
+}
+
+/**
+ * get_style:
+ * @self: A scores object.
+ *
+ * Returns the style of the scores.
+ *
+ **/
+GamesScoreStyle
+games_scores_get_style (GamesScores * self)
+{
+  GamesScoresPrivate *priv = self->priv;
+
+  g_return_val_if_fail (self != NULL, 0);
+
+  return priv->style;
+}
+
+/**
+ * get_category:
+ * @self: A scores object.
+ *
+ * Returns the current category key. It is owned by the GamesScores object and
+ * should not be altered. This will be NULL if no category is current (this
+ * will typically happen if no categories have been added to the GamesScore).
+ *
+ **/
+const gchar *
+games_scores_get_category (GamesScores * self)
+{
+  GamesScoresPrivate *priv = self->priv;
+
+  g_return_val_if_fail (self != NULL, NULL);
+
+  return priv->currentcat;
+}
+
+static void
+games_scores_init (GamesScores * self)
+{
+  GamesScoresPrivate *priv;
+
+  /* Most of the work is done in the _new method. */
+
+  priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GAMES_TYPE_SCORES, GamesScoresPrivate);
+
+  priv->last_score_significant = FALSE;
+  priv->last_score_position = 0;
+  priv->last_score_value.plain = 0;
+}
+
+static void
+games_scores_class_init (GamesScoresClass * klass)
+{
+  g_type_class_add_private (klass, sizeof (GamesScoresPrivate));
+}
diff --git a/src/games-scores.h b/src/games-scores.h
new file mode 100644
index 0000000..1c09d30
--- /dev/null
+++ b/src/games-scores.h
@@ -0,0 +1,101 @@
+/* Games Scores Dialog - Display high scores
+ *
+ * Copyright (c) 2005 by Callum McKenzie
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ */
+
+/* Authors:   Callum McKenzie <callum physics otago ac nz> */
+
+#ifndef GAMES_SCORES_H
+#define GAMES_SCORES_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#include "games-score.h"
+#include "games-scores-backend.h"
+
+/* How many scores get counted as significant. */
+#define GAMES_SCORES_SIGNIFICANT 10
+
+typedef struct {
+  gchar *key;			/* A unique identifier (warning: this is used to generate the
+				 * scores file name, so it should match the old domains) */
+  gchar *name;			/* A human-readable description. */
+} GamesScoresCategory;
+
+typedef void (*GamesScoresCategoryForeachFunc) (GamesScoresCategory * cat,
+						gpointer data);
+
+#define GAMES_TYPE_SCORES (games_scores_get_type())
+#define GAMES_SCORES(obj) G_TYPE_CHECK_INSTANCE_CAST((obj), games_scores_get_type(), GamesScores)
+#define GAMES_SCORES_CONST(obj)	G_TYPE_CHECK_INSTANCE_CAST((obj), games_scores_get_type(), GamesScores const)
+#define GAMES_SCORES_CLASS(klass) G_TYPE_CHECK_CLASS_CAST((klass), games_scores_get_type(), GamesScoresClass)
+#define GAMES_IS_SCORES(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), games_scores_get_type ())
+
+#define GAMES_SCORES_GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), games_scores_get_type(), GamesScoresClass)
+
+typedef struct _GamesScores         GamesScores;
+typedef struct _GamesScoresPrivate  GamesScoresPrivate;
+typedef struct _GamesScoresClass    GamesScoresClass;
+
+struct _GamesScores {
+  GObject parent;
+  GamesScoresPrivate *priv;
+};
+
+struct _GamesScoresClass {
+  GObjectClass parent;
+};
+
+GType games_scores_get_type (void);
+
+GamesScores *games_scores_new (const char *app_name,
+                               const GamesScoresCategory *categories,
+                               int n_categories,
+                               const char *categories_context,
+                               const char *categories_domain,
+                               int default_category_index,
+                               GamesScoreStyle style);
+
+void games_scores_set_category (GamesScores * self, gchar * category);
+
+gint games_scores_add_score (GamesScores * self, GamesScoreValue score);
+
+void games_scores_update_score (GamesScores * self, gchar * new_name);
+
+void games_scores_update_score_name (GamesScores * self, gchar * new_name, gchar * old_name);
+
+GList *games_scores_get (GamesScores * self);
+
+void _games_scores_category_foreach (GamesScores * self,
+                                     GamesScoresCategoryForeachFunc func,
+                                     gpointer userdata);
+
+GamesScoreStyle games_scores_get_style (GamesScores * self);
+
+const gchar *games_scores_get_category (GamesScores * self);
+
+void games_scores_add_category (GamesScores *self,
+                                const char *key,
+				const char *name);
+
+G_END_DECLS
+
+#endif /* GAMES_SCORES_H */
diff --git a/src/games-show.c b/src/games-show.c
new file mode 100644
index 0000000..aa4388d
--- /dev/null
+++ b/src/games-show.c
@@ -0,0 +1,159 @@
+/*
+ *  Copyright © 2008 Thomas H.P. Andersen <phomes gmail com>
+ *  Copyright © 2007, 2008, 2009 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime 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 Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_MAEMO
+#ifdef HAVE_MAEMO_3
+#include <osso-browser-interface.h>
+#else
+#include <tablet-browser-interface.h>
+#endif /* HAVE_MAEMO_3 */
+#endif /* HAVE_MAEMO */
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <io.h>
+#endif /* G_OS_WIN32 */
+
+#include "games-runtime.h"
+
+#include "games-show.h"
+
+/**
+ * games_show_uri:
+ * @screen: screen to show the uri on or %NULL for the default screen
+ * @uri: the uri to show
+ * @timestamp: a timestamp to prevent focus stealing.
+ * @error: a #GError that is returned in case of errors
+ *
+ * This is a convenience function for launching the default application
+ * to show the uri.
+ * Ideally the timestamp is taken from the event triggering
+ * the gtk_show_uri() call, or use gtk_get_current_event_time().
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+games_show_uri (GdkScreen *screen,
+                const char *uri,
+                guint32 timestamp,
+                GError **error)
+{
+#ifdef HAVE_MAEMO
+  osso_rpc_run_with_defaults (games_runtime_get_osso_context (),
+                              "osso_browser",
+                              OSSO_BROWSER_OPEN_NEW_WINDOW_REQ,
+                              NULL,
+                              DBUS_TYPE_STRING, uri,
+                              DBUS_TYPE_INVALID);
+  return TRUE;
+#else
+
+#ifdef G_OS_WIN32
+  ShellExecute (NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
+  return TRUE;
+#else /* !G_OS_WIN32 */
+
+#if GTK_CHECK_VERSION (2, 14, 0)
+  return gtk_show_uri (screen, uri, timestamp, error);
+#else /* GTK+ < 2.14 */
+  char *argv[3] = { (char *) "xdg-open", (char *) uri, NULL };
+ 
+  if (gdk_spawn_on_screen (screen,
+                           NULL /* working directory */,
+                           argv,
+                           NULL /* environment */,
+                           G_SPAWN_SEARCH_PATH,
+                           NULL, NULL,
+                           NULL,
+                           error))
+    return TRUE;
+
+  g_clear_error (error);
+
+  /* Try falling back to gnome-open */
+  argv[0] = (char *) "gnome-open";
+  if (gdk_spawn_on_screen (screen,
+                           NULL /* working directory */,
+                           argv,
+                           NULL /* environment */,
+                           G_SPAWN_SEARCH_PATH,
+                           NULL, NULL,
+                           NULL,
+                           error))
+    return TRUE;
+
+  g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+               "%s", "Failed to show help");
+  return FALSE;
+#endif /* GTK+ >= 2.14 */
+#endif /* G_OS_WIN32 */
+#endif /* HAVE_MAEMO */
+}
+
+/**
+ * games_show_error:
+ * @parent: a transient parent window
+ * @error: a #GError
+ * @primary_text_format:
+ * @...:
+ *
+ * Shows a message dialog with the given primary text, and @error's message
+ * as secondary text. The dialog will be transient to @parent, and modal.
+ * However, this function will *not* block until the dialogue has been dismissed.
+ */
+void
+games_show_error (GtkWidget *window,
+                  GError *error,
+                  const char *primary_text_format,
+                  ...)
+{
+  GtkWidget *dialog;
+  char *primary_text;
+  va_list args;
+
+  va_start (args, primary_text_format);
+  primary_text = g_strdup_vprintf (primary_text_format, args);
+  va_end (args);
+
+  dialog = gtk_message_dialog_new (GTK_WINDOW (window),
+                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+                                   GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
+                                   "%s", primary_text);
+  g_free (primary_text);
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                            "%s", error->message);
+
+#ifdef HAVE_HILDON
+  /* Empty title shows up as "<unnamed>" on maemo */
+  gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
+#else
+  gtk_window_set_title (GTK_WINDOW (dialog), "");
+#endif /* HAVE_HILDON */
+
+  g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+
+  gtk_window_present (GTK_WINDOW (dialog));
+}
diff --git a/src/games-show.h b/src/games-show.h
new file mode 100644
index 0000000..721d442
--- /dev/null
+++ b/src/games-show.h
@@ -0,0 +1,39 @@
+/*
+ *  Copyright © 2008 Thomas H.P. Andersen <phomes gmail com>
+ *  Copyright © 2007, 2008, 2009 Christian Persch
+ *
+ *  This runtime 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.1, or (at your option)
+ *  any later version.
+ *
+ *  This runtime is distributed in the hope runtime 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 Lesser General Public License
+ *  along with this runtime; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GAMES_SHOW_H
+#define GAMES_SHOW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+gboolean games_show_uri (GdkScreen *screen,
+                         const char *uri,
+                         guint32 timestamp,
+                         GError **error);
+
+void games_show_error (GtkWidget *window,
+                       GError *error,
+                       const char *primary_text_format,
+                       ...) G_GNUC_PRINTF (3, 4);
+
+G_END_DECLS
+
+#endif /* !GAMES_SHOW_H */
diff --git a/src/main.c b/src/main.c
index 41548f8..a3a2979 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,9 +19,6 @@
 
 #include "config.h"
 
-#include <libgnome/gnome-score.h>
-#include <libgnomeui/gnome-scores.h>
-#include <libgnomeui/gnome-ui-init.h>
 #include <sys/types.h>
 #include <dirent.h>
 #include <sys/stat.h>
@@ -29,6 +26,8 @@
 #include <unistd.h>
 #include <errno.h>
 
+#include "games-runtime.h"
+
 #include "board.h"
 #include "playfield.h"
 #include "main.h"
@@ -65,95 +64,85 @@ static void update_statistics (void);
 static void view_congratulations (void);
 static void calculate_score (void);
 static void log_score (void);
+static void show_scores (gint);
 
 /* ===============================================================
       
              Menu callback  functions 
 
 -------------------------------------------------------------- */
-static void verb_GameNew_cb (BonoboUIComponent *uic, gpointer user_data,
-			     const char *cname)
+static void verb_GameNew_cb (GtkAction * action, gpointer data)
 {
   controller_handle_action (GAME_ACTION_NEW);
 }
 
-static void verb_GameEnd_cb (BonoboUIComponent * uic, gpointer user_data,
-			     const char *cname)
+static void verb_GameEnd_cb (GtkAction * action, gpointer data)
 {
   controller_handle_action (GAME_ACTION_END);
 }
 
-static void verb_GameSkip_cb (BonoboUIComponent * uic, gpointer user_data,
-			      const char *cname)
+static void verb_GameSkip_cb (GtkAction * action, gpointer data)
 {
   controller_handle_action (GAME_ACTION_SKIP);
 }
 
-static void verb_GameReset_cb (BonoboUIComponent * uic, gpointer user_data,
-			       const char *cname)
+static void verb_GameReset_cb (GtkAction * action, gpointer data)
 {
   controller_handle_action (GAME_ACTION_RESTART);
 }
 
-static void verb_GamePause_cb (BonoboUIComponent * uic, gpointer user_data,
-			       const char *cname)
+static void verb_GamePause_cb (GtkAction * action, gpointer data)
 {
   controller_handle_action (GAME_ACTION_PAUSE);
 }
 
-static void verb_GameContinue_cb (BonoboUIComponent * uic,
-				  gpointer user_data, const char *cname)
+static void verb_GameContinue_cb (GtkAction * action, gpointer data)
 {
   controller_handle_action (GAME_ACTION_CONTINUE);
 }
 
-static void verb_GameUndo_cb (BonoboUIComponent * uic, gpointer user_data,
-			      const char *cname)
+static void verb_GameUndo_cb (GtkAction * action, gpointer data)
 {
   controller_handle_action (GAME_ACTION_UNDO);
 }
 
-static void verb_GameScores_cb (BonoboUIComponent * uic, gpointer user_data,
-				const char *cname)
+static void verb_GameScores_cb (GtkAction * action, gpointer data)
 {
-  struct stat scores_file;
+  show_scores (0);
+}
 
-  stat (SCORESDIR "/atomix.scores", &scores_file);
+static void
+show_scores (gint pos)
+{
+  static GtkWidget *dialog;
 
-  if (scores_file.st_size == 0)
-    {
-      GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (app->mainwin),
-					       GTK_DIALOG_MODAL,
-					       GTK_MESSAGE_INFO,
-					       GTK_BUTTONS_CLOSE,
-					       _("You have not achieved any "
-						 "scores yet. Play a little "
-						 "before coming back!"));
-      gtk_dialog_run (GTK_DIALOG (dlg));
-      gtk_widget_destroy (GTK_WIDGET (dlg));
-
-      return;
-    }
+  if (dialog == NULL) {
+    dialog = games_scores_dialog_new (GTK_WINDOW (app->mainwin),
+                                      app->highscores, _("Atomix"));
+  }
+
+  if (pos > 0) {
+    games_scores_dialog_set_hilight (GAMES_SCORES_DIALOG (dialog), pos);
+  }
 
-  gnome_scores_display ("Atomix", PACKAGE, NULL, 0);
+  gtk_window_present (GTK_WINDOW (dialog));
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_hide (dialog);
 }
 
-static void verb_GameExit_cb (BonoboUIComponent * uic, gpointer user_data,
-			      const char *cname)
+static void verb_GameExit_cb (GtkAction * action, gpointer data)
 {
   atomix_exit ();
 }
 
-static void verb_EditPreferences_cb (BonoboUIComponent * uic,
-				     gpointer user_data, const char *cname)
+static void verb_EditPreferences_cb (GtkAction * action, gpointer data)
 {
 #if 0
   preferences_show_dialog ();
 #endif
 }
 
-static void verb_HelpAbout_cb (BonoboUIComponent *uic, gpointer user_data,
-			       const char *cname)
+static void verb_HelpAbout_cb (GtkAction * action, gpointer data)
 {
   GtkWidget *dlg;
 
@@ -409,7 +398,6 @@ static void atomix_exit (void)
     g_object_unref (app->tm);
 
   /* quit application */
-  bonobo_object_unref (BONOBO_OBJECT (app->ui_component));
   gtk_widget_destroy (app->mainwin);
 
   gtk_main_quit ();
@@ -483,13 +471,15 @@ static void calculate_score (void)
 
 static void log_score (void)
 {
-  gint position;
+  int pos;
+  GamesScoreValue hiscore;
 
   if (app->score == 0)
     return;
 
-  position = gnome_score_log (app->score, NULL, TRUE);
-  gnome_scores_display (_("Atomix"), PACKAGE, NULL, position);
+  hiscore.plain = app->score;
+  pos = games_scores_add_score (app->highscores, hiscore);
+  show_scores (pos);
 }
 
 static void view_congratulations (void)
@@ -579,7 +569,6 @@ static const CmdEnable not_running[] =
     { "GameUndo",     FALSE },
     { "GamePause",    FALSE },
     { "GameContinue", FALSE },
-    { "EditPreferences", TRUE },
     { NULL, FALSE }
   };
 
@@ -592,7 +581,6 @@ static const CmdEnable running_unmoved[] =
     { "GameUndo",     FALSE },
     { "GamePause",    TRUE  },
     { "GameContinue", FALSE },
-    { "EditPreferences", TRUE },
     { NULL, FALSE }
 };
 
@@ -605,7 +593,6 @@ static const CmdEnable running[] =
     { "GameUndo",     TRUE  },
     { "GamePause",    TRUE  },
     { "GameContinue", FALSE },
-    { "EditPreferences", TRUE },
     { NULL, FALSE }
 };
 
@@ -618,7 +605,6 @@ static const CmdEnable paused[] =
     { "GameUndo",     FALSE },
     { "GamePause",    FALSE },
     { "GameContinue", TRUE  },
-    { "EditPreferences", TRUE },
     { NULL, FALSE }
 };
 
@@ -632,12 +618,13 @@ void update_menu_item_state (void)
   gchar *path;
   gint i;
   const CmdEnable *cmd_list = state_sensitivity[app->state];
+  GtkWidget *widget;
 
   for (i = 0; cmd_list[i].cmd != NULL; i++)
     {
-      path = g_strconcat ("/commands/", cmd_list[i].cmd, NULL);
-      bonobo_ui_component_set_prop (app->ui_component, path, "sensitive",
-				    cmd_list[i].enabled ? "1" : "0", NULL);
+      path = g_strconcat ("/MainMenu/GameMenu/", cmd_list[i].cmd, NULL);
+      widget = gtk_ui_manager_get_widget (app->ui_manager, path);
+      gtk_widget_set_sensitive (widget, cmd_list[i].enabled);
       g_free (path);
     }
 }
@@ -647,24 +634,6 @@ void update_menu_item_state (void)
              GUI creation  functions 
 
 -------------------------------------------------------------- */
-static BonoboUIVerb verbs[] =
-  {
-    BONOBO_UI_VERB ("GameNew", verb_GameNew_cb),
-    BONOBO_UI_VERB ("GameEnd", verb_GameEnd_cb),
-    BONOBO_UI_VERB ("GameSkip", verb_GameSkip_cb),
-    BONOBO_UI_VERB ("GameReset", verb_GameReset_cb),
-    BONOBO_UI_VERB ("GameUndo", verb_GameUndo_cb),
-    BONOBO_UI_VERB ("GamePause", verb_GamePause_cb),
-    BONOBO_UI_VERB ("GameContinue", verb_GameContinue_cb),
-    BONOBO_UI_VERB ("GameScores", verb_GameScores_cb),
-    BONOBO_UI_VERB ("GameExit", verb_GameExit_cb),
-    BONOBO_UI_VERB ("EditPreferences", verb_EditPreferences_cb),
-#if 0
-    BONOBO_UI_VERB ("HelpManual", verb_HelpManual_cb),
-#endif
-    BONOBO_UI_VERB ("HelpAbout", verb_HelpAbout_cb), BONOBO_UI_VERB_END
-  };
-
 static GtkWidget *create_canvas_widget (GtkWidget **canvas)
 {
   GtkWidget *frame;
@@ -752,74 +721,125 @@ static GtkWidget *create_mainwin_content (AtomixApp *app)
   return hbox;
 }
 
-static AtomixApp *create_gui (GnomeProgram *prog)
+static AtomixApp *create_gui (void)
 {
   AtomixApp *app;
-  gchar *ui_file = NULL;
-
+  GtkAccelGroup *accel_group;
+  GtkActionGroup *action_group;
+  GtkWidget *vbox;
+  GtkWidget *menubar;
   GtkWidget *content;
 
+  static const GtkActionEntry actions[] = {
+    {"GameMenu", NULL, N_("_Game")},
+    {"HelpMenu", NULL, N_("_Help")},
+    {"GameNew", NULL, N_("New Game"), NULL, NULL, G_CALLBACK (verb_GameNew_cb)},
+    {"GameEnd", NULL, N_("End Game"), NULL, NULL, G_CALLBACK (verb_GameEnd_cb)},
+    {"GameSkip", NULL, N_("Skip Level"), NULL, NULL, G_CALLBACK (verb_GameSkip_cb)},
+    {"GameReset", NULL, N_("Reset Level"), NULL, NULL, G_CALLBACK (verb_GameReset_cb)},
+    {"GameUndo", "gtk-undo", NULL, NULL, NULL, G_CALLBACK (verb_GameUndo_cb)},
+    {"GamePause", NULL, N_("_Pause Game"), NULL, NULL, G_CALLBACK (verb_GamePause_cb)},
+    {"GameContinue", NULL, N_("_Continue Game"), NULL, NULL, G_CALLBACK (verb_GameContinue_cb)},
+    {"GameScores", NULL, N_("_Scores..."), NULL, NULL, G_CALLBACK (verb_GameScores_cb)},
+    {"GameExit", "gtk-quit", NULL, NULL, NULL, G_CALLBACK (verb_GameExit_cb)},
+    {"HelpAbout", NULL, N_("About"), NULL, NULL, G_CALLBACK (verb_HelpAbout_cb)}
+  };
+
+  const char ui_description[] =
+    "<ui>"
+    "  <menubar name='MainMenu'>"
+    "    <menu action='GameMenu'>"
+    "      <menuitem action='GameNew'/>"
+    "      <menuitem action='GameEnd'/>"
+    "      <separator/>"
+    "      <menuitem action='GameSkip'/>"
+    "      <menuitem action='GameReset'/>"
+    "      <menuitem action='GameUndo'/>"
+    "      <menuitem action='GamePause'/>"
+    "      <menuitem action='GameContinue'/>"
+    "      <separator/>"
+    "      <menuitem action='GameScores'/>"
+    "      <separator/>"
+    "      <menuitem action='GameExit'/>"
+    "    </menu>"
+    "    <menu action='HelpMenu'>"
+    "      <menuitem action='HelpAbout'/>"
+    "    </menu>"
+    "  </menubar>"
+    "</ui>";
+
   app = g_new0 (AtomixApp, 1);
-  app->prog = prog;
   app->level = NULL;
 
-  app->mainwin = bonobo_window_new ("atomix", "Atomix");
+  app->mainwin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title (GTK_WINDOW (app->mainwin), _("Atomix"));
+
   g_signal_connect (G_OBJECT (app->mainwin), "delete_event",
 		    (GCallback) on_app_destroy_event, app);
 
-  app->ui_container =
-    bonobo_ui_engine_get_ui_container (bonobo_window_get_ui_engine
-				       (BONOBO_WINDOW (app->mainwin)));
-
-  app->ui_component = bonobo_ui_component_new ("atomix");
-  bonobo_ui_component_set_container (app->ui_component,
-				     BONOBO_OBJREF (app->ui_container), NULL);
-
-  /* find xml menu description */
-  ui_file = bonobo_ui_util_get_ui_fname (DATADIR, "atomix-ui.xml");
-  if (ui_file && !g_file_test (ui_file, G_FILE_TEST_EXISTS))
-    {
-      g_error (_("Couldn't find file: %s"), ui_file);
-      return NULL;
-    }
+  app->ui_manager = gtk_ui_manager_new ();
 
-  /* set menus */
-  bonobo_ui_util_set_ui (app->ui_component, "", ui_file, "atomix", NULL);
-  g_free (ui_file);
+  action_group = gtk_action_group_new ("MenuActions");
+  gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+  gtk_action_group_add_actions (action_group, actions, G_N_ELEMENTS (actions), NULL);
 
-  bonobo_ui_component_add_verb_list_with_data (app->ui_component, verbs, app);
+  gtk_ui_manager_insert_action_group (app->ui_manager, action_group, 0);
+  gtk_ui_manager_add_ui_from_string (app->ui_manager, ui_description, -1, NULL);
+  accel_group = gtk_ui_manager_get_accel_group (app->ui_manager);
+  gtk_window_add_accel_group (GTK_WINDOW (app->mainwin), accel_group);
 
   /* create window contents */
+  menubar = gtk_ui_manager_get_widget (app->ui_manager, "/MainMenu");
   content = create_mainwin_content (app);
 
   gtk_window_set_default_icon_from_file (g_build_filename (DATADIR,
 							   "pixmaps",
 							   "atomix-icon.png",
 							   NULL),
-					 NULL);
+					 		   NULL);
 
-  gtk_widget_show (GTK_WIDGET (content));
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (app->mainwin), vbox);
+  gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), content, TRUE, TRUE, 0);
 
-  bonobo_window_set_contents (BONOBO_WINDOW (app->mainwin), content);
+  gtk_widget_show_all (GTK_WIDGET (app->mainwin));
 
   return app;
 }
 
 int main (int argc, char *argv[])
 {
-  GnomeProgram *prog;
+  GOptionContext *context;
+  gboolean retval;
+  GError *error = NULL;
 
-  gnome_score_init (PACKAGE);
+  if (!games_runtime_init ("atomix"))
+    return 1;
+
+  context = g_option_context_new (NULL);
+  g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+  g_option_context_add_group (context, gtk_get_option_group (TRUE));
+
+  retval = g_option_context_parse (context, &argc, &argv, &error);
+  g_option_context_free (context);
+  if (!retval) {
+    g_print ("%s", error->message);
+    g_error_free (error);
+    exit (1);
+  }
+
+  g_set_application_name (_("Atomix"));
 
   bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
   textdomain (GETTEXT_PACKAGE);
-
-  prog = gnome_program_init (PACKAGE, VERSION, LIBGNOMEUI_MODULE,
-			     argc, argv, NULL);
-
+  
   /* make a few initalisations here */
-  app = create_gui (prog);
+  app = create_gui ();
+
+  app->highscores = games_scores_new ("Atomix", NULL, 0, NULL, NULL, 0,
+                                       GAMES_SCORES_STYLE_PLAIN_DESCENDING);
 
   game_init ();
 
diff --git a/src/main.h b/src/main.h
index 39c07d3..4922852 100644
--- a/src/main.h
+++ b/src/main.h
@@ -20,10 +20,15 @@
 #ifndef _ATOMIX_MAIN_H_
 #define _ATOMIX_MAIN_H_
 
-#include <bonobo.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libgnomecanvas/libgnomecanvas.h>
 #include "theme-manager.h"
 #include "level-manager.h"
 #include "goal.h"
+#include "games-scores.h"
+#include "games-scores-dialog.h"
 
 typedef enum
 {
@@ -35,10 +40,8 @@ typedef enum
 
 typedef struct
 {
-  GnomeProgram *prog;
   GtkWidget *mainwin;
-  BonoboUIContainer *ui_container;
-  BonoboUIComponent *ui_component;
+  GtkUIManager *ui_manager;
   GtkWidget *ca_matrix;
   GtkWidget *ca_goal;
   GtkWidget *lb_level;
@@ -56,6 +59,7 @@ typedef struct
   Goal *goal;
   gint level_no;
   guint score;
+  GamesScores *highscores;
 } AtomixApp;
 
 void game_level_finished (void);



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