[atomix] Remove libgnome and bonobo usage.
- From: Thomas Hindoe Paaboel Andersen <thomashpa src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [atomix] Remove libgnome and bonobo usage.
- Date: Mon, 22 Mar 2010 21:27:05 +0000 (UTC)
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]