[gnome-games] gnotravex: Tidy up the code



commit 361007edae4ee2da3dc52f71629e333c383ef65c
Author: Robert Ancell <robert ancell gmail com>
Date:   Mon Jul 12 11:26:05 2010 +1000

    gnotravex: Tidy up the code

 gnotravex/gnotravex.c | 2110 +++++++++++++++++++++++--------------------------
 1 files changed, 1007 insertions(+), 1103 deletions(-)
---
diff --git a/gnotravex/gnotravex.c b/gnotravex/gnotravex.c
index 48196d1..a4a53ab 100644
--- a/gnotravex/gnotravex.c
+++ b/gnotravex/gnotravex.c
@@ -1,9 +1,9 @@
 /* -*- mode:C; indent-tabs-mode: nil; tab-width: 8; c-basic-offset: 2; -*- */
 
-/* 
+/*
  *   Gnome Tetravex: Tetravex clone
  *   Written by Lars Rydlinge <lars rydlinge hig se>
- * 
+ *
  *   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
@@ -20,16 +20,13 @@
  */
 
 #include <config.h>
-
 #include <string.h>
 #include <math.h>
 #include <time.h>
 #include <stdlib.h>
-
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
-
 #include <libgames-support/games-clock.h>
 #include <libgames-support/games-conf.h>
 #include <libgames-support/games-gtk-compat.h>
@@ -48,88 +45,83 @@
 #define APPNAME "gnotravex"
 #define APPNAME_LONG N_("Tetravex")
 
-/* This is based on the point where the numbers become unreadable on my
- * screen at 3x3. - Callum */
-#define MINIMUM_TILE_SIZE 40
-
 #define RELEASE 4
 #define PRESS 3
 #define MOVING 2
 #define UNUSED 1
 #define USED 0
 
+#define LONG_COUNT 15
+#define SHORT_COUNT 5
+#define DELAY 10
+
 #define KEY_GRID_SIZE     "grid_size"
 #define KEY_CLICK_MOVE    "click_to_move"
 
 #define DEFAULT_WIDTH 320
 #define DEFAULT_HEIGHT 240
 
-static const char *translatable_number[10] = {
-  /* Translators: in-game numbers, replaceable with single-character local ideograms */
-  NC_("number", "0"),
-  NC_("number", "1"),
-  NC_("number", "2"),
-  NC_("number", "3"),
-  NC_("number", "4"),
-  NC_("number", "5"),
-  NC_("number", "6"),
-  NC_("number", "7"),
-  NC_("number", "8"),
-  NC_("number", "9")
-};
-
-static GtkWidget *window;
-static GtkWidget *statusbar;
-static GtkWidget *space;
-static GtkWidget *timer;
-static GdkGC *bg_gc;
-
-static const GamesScoresCategory scorecats[] = {
-  { "2x2", N_("2\303\2272") },
-  { "3x3", N_("3\303\2273") },
-  { "4x4", N_("4\303\2274") },
-  { "5x5", N_("5\303\2275") },
-  { "6x6", N_("6\303\2276") }
-};
-
-static GamesScores *highscores;
-
-static int xborder;
-static int yborder;
-static int gap;
+/* The sector of a tile to mark quads with */
+#define NORTH 0
+#define SOUTH 1
+#define EAST  2
+#define WEST  3
 
-static GdkPixmap *buffer = NULL;
+#define HIGHLIGHT 0
+#define BASE      1
+#define SHADOW    2
+#define TEXT      3
 
-typedef struct _tile {
+typedef struct
+{
   gint n, w, e, s;
   gint status;
-} tile;
-
-static tile tiles[9][18];
-static tile orig_tiles[9][9];
+} Tile;
 
-typedef struct _mover {
+typedef struct
+{
   GdkWindow *window;
   GdkPixmap *pixmap;
-  tile heldtile;
+  Tile heldtile;
   gint xstart, ystart;
   gint xend, yend;
   gint xoff, yoff;
   gint x, y;
 } Mover;
 
+typedef enum
+{
+  GAME_OVER,
+  PAUSED,
+  PLAYING,
+} GameState;
+
+static GtkWidget *window = NULL;
+static GtkWidget *statusbar = NULL;
+static GtkWidget *space = NULL;
+static GtkWidget *timer = NULL;
+static GdkGC *bg_gc = NULL;
+static GdkPixmap *background_pixmap = NULL;
+static GamesScores *highscores = NULL;
+static gint xborder = 0;
+static gint yborder = 0;
+static gint gap = 0;
+static GdkPixmap *buffer = NULL;
+static Tile tiles[9][18];
+static Tile orig_tiles[9][9];
 static GdkWindowAttr windowattrib;
 static Mover mousemover;
 static Mover automover;
-
-enum {
-  gameover,
-  paused,
-  playing,
+static const GamesScoresCategory scorecats[] = {
+  { "2x2", N_("2\303\2272") },
+  { "3x3", N_("3\303\2273") },
+  { "4x4", N_("4\303\2274") },
+  { "5x5", N_("5\303\2275") },
+  { "6x6", N_("6\303\2276") }
 };
 
 static gint size = -1;
-static gint game_state = gameover;
+static GameState game_state = GAME_OVER;
 static gint have_been_hinted = 0;
 static gint solve_me = 0;
 static gint moving = 0;
@@ -140,22 +132,16 @@ static gint tile_size = 0;
 static gdouble tile_border_size = 3.0;
 static gdouble arrow_border_size = 1.5;
 static gboolean click_to_move = FALSE;
+static gint button_down = 0;
+
+static gint animcount = 0;
+static gboolean swapanim = FALSE;
+static gint move_src_x = 0, move_src_y = 0, move_dest_x = 0, move_dest_y = 0;
 
 /* The vertices used in the tiles/sockets. These are built using gui_build_vertices() */
 static gdouble vertices[27][2];
 static gboolean rebuild_vertices = TRUE;
 
-/* The sector of a tile to mark quads with */
-#define NORTH 0
-#define SOUTH 1
-#define EAST  2
-#define WEST  3
-
-#define HIGHLIGHT 0
-#define BASE      1
-#define SHADOW    2
-#define TEXT      3
-
 /* The faces used to build a socket */
 static int socket_faces[4][7] = {
   {NORTH, SHADOW,    4, 0, 1, 18, 17},
@@ -209,123 +195,6 @@ static gdouble tile_colours[11][4][4] = {
 /* The colour to use when drawing the sockets */
 #define SOCKET_COLOUR 10
 
-void make_buffer (GtkWidget *);
-void create_window (void);
-GtkWidget *create_menu (GtkUIManager *);
-void init_window_attrib (void);
-GtkWidget *create_statusbar (void);
-GdkPixmap *default_background_pixmap;
-
-gboolean expose_space (GtkWidget *, GdkEventExpose *);
-gint button_press_space (GtkWidget *, GdkEventButton *);
-gint button_release_space (GtkWidget *, GdkEventButton *);
-gint button_motion_space (GtkWidget *, GdkEventButton *);
-
-void gui_build_vertices (void);
-void gui_draw_faces (cairo_t * context, gint xadd, gint yadd, int quads[][7],
-                     int count, guint colours[4], gboolean prelight);
-void gui_draw_arrow (GdkPixmap * target);
-void gui_draw_socket (GdkPixmap * target, GtkStateType state, gint xadd,
-                      gint yadd);
-void gui_draw_number (cairo_t * context, gdouble x, gdouble y, guint number, gdouble *colour);
-void gui_draw_tile (GdkPixmap * target, GtkStateType state, gint xadd,
-                    gint yadd, gint north, gint south, gint east, gint west, gboolean prelight);
-void gui_draw_pixmap (GdkPixmap *, gint, gint, gboolean, Mover*);
-
-void get_pixeltilexy (gint, gint, gint *, gint *);
-void get_tilexy (gint, gint, gint *, gint *);
-void get_offsetxy (gint, gint, gint *, gint *);
-
-void message (gchar *);
-void new_board (gint);
-void redraw_all (void);
-void redraw_left (void);
-gint setup_mover (gint, gint, Mover*);
-void clear_mover(Mover*);
-void release_tile (gint, gint);
-void place_tile (gint, gint);
-void tile_tilexy (gint, gint, gint*, gint*);
-void swap_without_validation (gint, gint, gint, gint);
-gint valid_drop (gint, gint, gint, gint);
-
-void update_tile_size (gint, gint);
-gboolean configure_space (GtkWidget *, GdkEventConfigure *);
-gint compare_tile (tile *, tile *);
-void find_first_tile (gint, gint *, gint *);
-void move_tile (gint, gint, gint, gint);
-void move_column (unsigned char);
-gint game_over (void);
-void game_score (void);
-gint timer_cb (void);
-void timer_start (void);
-void pause_game (void);
-void resume_game (void);
-void pause_cb (void);
-void move_cb (void);
-void clickmove_toggle_cb (GtkToggleAction *, gpointer);
-void hint_move (gint, gint, gint, gint);
-void move_tile_animate (gint, gint, gint, gint, gboolean);
-void move_held_animate (gint, gint, gint, gint);
-gint show_score_dialog (gint, gboolean);
-void new_game (void);
-
-#ifdef WITH_SMCLIENT
-static int save_state_cb (EggSMClient *client, GKeyFile *keyfile, gpointer client_data);
-static int quit_cb (EggSMClient *client, gpointer client_data);
-#endif /* WITH_SMCLIENT */
-static void load_default_background (void);
-
-static GtkAction *new_game_action;
-static GtkAction *pause_action;
-static GtkAction *hint_action;
-static GtkAction *solve_action;
-static GtkAction *scores_action;
-static GtkAction *move_up_action;
-static GtkAction *move_left_action;
-static GtkAction *move_right_action;
-static GtkAction *move_down_action;
-static GtkAction *fullscreen_action;
-
-
-/* ------------------------- MENU ------------------------ */
-void new_game_cb (GtkAction *, gpointer);
-void size_cb (GtkAction *, gpointer);
-void about_cb (GtkAction *, gpointer);
-void score_cb (GtkAction *, gpointer);
-void hint_cb (GtkAction *, gpointer);
-void solve_cb (GtkAction *, gpointer);
-void move_up_cb (GtkAction *, gpointer);
-void move_left_cb (GtkAction *, gpointer);
-void move_right_cb (GtkAction *, gpointer);
-void move_down_cb (GtkAction *, gpointer);
-void help_cb (GtkAction *, gpointer);
-void quit_game_cb (void);
-
-const GtkActionEntry action_entry[] = {
-  {"GameMenu", NULL, N_("_Game")},
-  {"MoveMenu", NULL, N_("_Move")},
-  {"SettingsMenu", NULL, N_("_Settings")},
-  {"SizeMenu", NULL, N_("_Size")},
-  {"HelpMenu", NULL, N_("_Help")},
-  {"NewGame", GAMES_STOCK_NEW_GAME, NULL, NULL, NULL,
-   G_CALLBACK (new_game_cb)},
-  {"Hint", GAMES_STOCK_HINT, NULL, NULL, NULL, G_CALLBACK (hint_cb)},
-  {"Solve", GTK_STOCK_REFRESH, N_("Sol_ve"), NULL, N_("Solve the game"),
-   G_CALLBACK (solve_cb)},
-  {"Scores", GAMES_STOCK_SCORES, NULL, NULL, NULL, G_CALLBACK (score_cb)},
-  {"Quit", GTK_STOCK_QUIT, NULL, NULL, NULL, G_CALLBACK (quit_game_cb)},
-  {"MoveUp", GTK_STOCK_GO_UP, N_("_Up"), "<control>Up",
-   N_("Move the pieces up"), G_CALLBACK (move_up_cb)},
-  {"MoveLeft", GTK_STOCK_GO_BACK, N_("_Left"), "<control>Left",
-   N_("Move the pieces left"), G_CALLBACK (move_left_cb)},
-  {"MoveRight", GTK_STOCK_GO_FORWARD, N_("_Right"), "<control>Right",
-   N_("Move the pieces right"), G_CALLBACK (move_right_cb)},
-  {"MoveDown", GTK_STOCK_GO_DOWN, N_("_Down"), "<control>Down",
-   N_("Move the pieces down"), G_CALLBACK (move_down_cb)},
-  {"Contents", GAMES_STOCK_CONTENTS, NULL, NULL, NULL, G_CALLBACK (help_cb)},
-  {"About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK (about_cb)}
-};
-
 const GtkRadioActionEntry size_action_entry[] = {
   {"Size2x2", NULL, N_("_2\303\2272"), NULL, N_("Play on a 2\303\2272 board"),
    2},
@@ -339,11 +208,16 @@ const GtkRadioActionEntry size_action_entry[] = {
    6}
 };
 
-static const GtkToggleActionEntry toggles[] = {
-  {"ClickToMove", NULL, N_("_Click to Move"), NULL, "Pick up and drop tiles by clicking",
-   G_CALLBACK (clickmove_toggle_cb)}
-};
-
+static GtkAction *new_game_action;
+static GtkAction *pause_action;
+static GtkAction *hint_action;
+static GtkAction *solve_action;
+static GtkAction *scores_action;
+static GtkAction *move_up_action;
+static GtkAction *move_left_action;
+static GtkAction *move_right_action;
+static GtkAction *move_down_action;
+static GtkAction *fullscreen_action;
 static GtkAction *size_action[G_N_ELEMENTS (size_action_entry)];
 
 static const char ui_description[] =
@@ -396,275 +270,7 @@ static const GOptionEntry options[] = {
 
 /* ------------------------------------------------------- */
 
-int
-main (int argc, char **argv)
-{
-  GOptionContext *context;
-  GtkWidget *vbox;
-  GtkWidget *menubar;
-  GtkUIManager *ui_manager;
-  GtkAccelGroup *accel_group;
-  gboolean retval;
-  GError *error = NULL;
-#ifdef WITH_SMCLIENT
-  EggSMClient *sm_client;
-#endif /* WITH_SMCLIENT */
-
-  if (!games_runtime_init ("gnotravex"))
-    return 1;
-
-#ifdef ENABLE_SETGID
-  setgid_io_init ();
-#endif
-
-  context = g_option_context_new (NULL);
-#if GLIB_CHECK_VERSION (2, 12, 0)
-  g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
-#endif /* GLIB_CHECK_VERSION (2, 12, 0) */
-  g_option_context_add_group (context, gtk_get_option_group (TRUE));
-#ifdef WITH_SMCLIENT
-  g_option_context_add_group (context, egg_sm_client_get_option_group ());
-#endif /* WITH_SMCLIENT */
-
-  g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
-  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 (_(APPNAME_LONG));
-
-  games_conf_initialise (APPNAME);
-
-  highscores = games_scores_new ("gnotravex",
-                                 scorecats, G_N_ELEMENTS (scorecats),
-                                 NULL, NULL,
-                                 1 /* default category */,
-                                 GAMES_SCORES_STYLE_TIME_ASCENDING);
-
-  games_stock_init ();
-
-  gtk_window_set_default_icon_name ("gnome-tetravex");
-
-#ifdef WITH_SMCLIENT
-  sm_client = egg_sm_client_get ();
-  g_signal_connect (sm_client, "save-state",
-                    G_CALLBACK (save_state_cb), NULL);
-  g_signal_connect (sm_client, "quit",
-                    G_CALLBACK (quit_cb), NULL);
-#endif /* WITH_SMCLIENT */
-
-  if (size == -1)
-    size = games_conf_get_integer (NULL, KEY_GRID_SIZE, NULL);
-  if (size < 2 || size > 6)
-    size = 3;
-  games_scores_set_category (highscores, scorecats[size - 2].key);
-
-  click_to_move = games_conf_get_boolean (NULL, KEY_CLICK_MOVE, NULL);
-
-  load_default_background ();
-  create_window ();
-
-  space = gtk_drawing_area_new ();
-  gtk_widget_set_events (space,
-                         GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
-                         | GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
-
-  statusbar = create_statusbar ();
-
-  ui_manager = gtk_ui_manager_new ();
-  games_stock_prepare_for_statusbar_tooltips (ui_manager, statusbar);
-
-  menubar = create_menu (ui_manager);
-
-  vbox = gtk_vbox_new (FALSE, 0);
-  gtk_container_add (GTK_CONTAINER (window), vbox);
-
-  gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
-  gtk_box_pack_start (GTK_BOX (vbox), space, TRUE, TRUE, 0);
-  gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 0);
-
-  accel_group = gtk_ui_manager_get_accel_group (ui_manager);
-  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
-
-  gtk_widget_realize (space);
-  bg_gc = gdk_gc_new (gtk_widget_get_window (space));
-  gdk_gc_set_tile (bg_gc, default_background_pixmap);
-  gdk_gc_set_fill (bg_gc, GDK_TILED);
-
-  g_signal_connect (G_OBJECT (space), "expose_event",
-                    G_CALLBACK (expose_space), NULL);
-  g_signal_connect (G_OBJECT (space), "configure_event",
-                    G_CALLBACK (configure_space), NULL);
-  g_signal_connect (G_OBJECT (space), "button_press_event",
-                    G_CALLBACK (button_press_space), NULL);
-  g_signal_connect (G_OBJECT (space), "button_release_event",
-                    G_CALLBACK (button_release_space), NULL);
-  g_signal_connect (G_OBJECT (space), "motion_notify_event",
-                    G_CALLBACK (button_motion_space), NULL);
-  /* We do our own double-buffering. */
-  gtk_widget_set_double_buffered (space, FALSE);
-
-  gtk_widget_show (space);
-
-
-  if (session_xpos >= 0 && session_ypos >= 0)
-    gtk_window_move (GTK_WINDOW (window), session_xpos, session_ypos);
-
-  gtk_widget_show_all (window);
-  init_window_attrib ();
-
-  gtk_action_activate (new_game_action);
-
-  gtk_action_activate (size_action[size - 2]);
-
-  gtk_main ();
-
-  games_conf_shutdown ();
-
-  games_runtime_shutdown ();
-
-  return 0;
-}
-
-/* Enable or disable the game menu items that are only relevant
- * during a game. */
-static
-  void
-set_game_menu_items_sensitive (gboolean state)
-{
-  gtk_action_set_sensitive (pause_action, state);
-  gtk_action_set_sensitive (hint_action, state);
-  gtk_action_set_sensitive (solve_action, state);
-}
-
-/* Show only valid options in the move menu. */
-static
-  void
-update_move_menu_sensitivity (void)
-{
-  int x, y;
-  gboolean clear;
-  gboolean n, w, e, s;
-
-  n = w = e = s = TRUE;
-
-  clear = TRUE;
-  for (x = 0; x < size; x++) {
-    if (tiles[0][x].status == USED)
-      n = FALSE;
-    if (tiles[x][0].status == USED)
-      w = FALSE;
-    if (tiles[x][size - 1].status == USED)
-      e = FALSE;
-    if (tiles[size - 1][x].status == USED)
-      s = FALSE;
-    for (y = 0; y < size; y++)
-      if (tiles[x][y].status == USED)
-        clear = FALSE;
-  }
-
-  if (clear || (game_state == paused))
-    n = w = e = s = FALSE;
-
-  gtk_action_set_sensitive (move_up_action, n);
-  gtk_action_set_sensitive (move_left_action, w);
-  gtk_action_set_sensitive (move_right_action, e);
-  gtk_action_set_sensitive (move_down_action, s);
-}
-
-
-void
-create_window (void)
-{
-  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
-  gtk_window_set_title (GTK_WINDOW (window), _(APPNAME_LONG));
-
-  gtk_window_set_default_size (GTK_WINDOW (window), DEFAULT_WIDTH, DEFAULT_HEIGHT);
-  games_conf_add_window (GTK_WINDOW (window), NULL);
-  gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
-
-  gtk_widget_realize (window);
-  g_signal_connect (G_OBJECT (window), "delete_event",
-                    G_CALLBACK (quit_game_cb), NULL);
-}
-
-gboolean
-expose_space (GtkWidget * widget, GdkEventExpose * event)
-{
-  gdk_draw_drawable (gtk_widget_get_window (widget),
-                     gtk_widget_get_style (widget)->fg_gc[GTK_STATE_NORMAL],
-                     buffer, event->area.x, event->area.y,
-                     event->area.x, event->area.y,
-                     event->area.width, event->area.height);
-  return FALSE;
-}
-
-gint button_down = 0;
-
-gint
-button_press_space (GtkWidget * widget, GdkEventButton * event)
-{
-  if (game_state == paused)
-    gtk_action_activate (pause_action);
-
-  if (game_state != playing)
-    return FALSE;
-   
-  if (event->button != 1)
-    return FALSE;
-   
-  if (click_to_move) 
-  {
-    if (button_down) 
-    {
-      release_tile (event->x,event->y); /* Seen it happened */
-      button_down = 0;
-      return FALSE;
-    }
-    else
-    {
-      if (setup_mover (event->x, event->y, &mousemover))
-        button_down = 1;
-    }
-  }
-  else
-  {
-    if (button_down == 1) 
-    {
-      release_tile (event->x,event->y); /* Seen it happened */
-      button_down = 0;
-      return FALSE;
-    }
-    if (setup_mover (event->x, event->y, &mousemover))
-      button_down = 1;
-  }
-   
-  return FALSE;
-}
-
-gint
-button_release_space (GtkWidget * widget, GdkEventButton * event)
-{
-  /* Ignore when using click to move mode */
-  if (click_to_move)
-    return FALSE;
-
-  if (event->button == 1) {
-    if (button_down == 1) {
-      release_tile (event->x, event->y);
-    }
-    button_down = 0;
-  }
-  return FALSE;
-}
-
-void
+static void
 gui_build_vertices (void)
 {
   gdouble z, midx, midy, offset, far_offset;
@@ -735,7 +341,7 @@ gui_build_vertices (void)
   vertices[19][1] = tile_size - tile_border_size;
   vertices[20][0] = tile_border_size;
   vertices[20][1] = tile_size - tile_border_size;
-   
+
   /* Edges for the arrow */
   w = gap;
   h = size * tile_size;
@@ -747,7 +353,7 @@ gui_build_vertices (void)
   vertices[22][1] = yoffset;
   vertices[23][0] = vertices[22][0];
   vertices[23][1] = h - yoffset;
-   
+
   /* Arrow inner edges */
   dx = w - 2*xoffset;
   dy = (h - 2*yoffset) * 0.5;
@@ -761,7 +367,7 @@ gui_build_vertices (void)
   vertices[26][1] = vertices[21][1] + z2;
 }
 
-void
+static void
 gui_draw_faces (cairo_t * context, gint xadd, gint yadd, int quads[][7],
                 int count, guint colours[4], gboolean prelight)
 {
@@ -795,30 +401,30 @@ gui_draw_faces (cairo_t * context, gint xadd, gint yadd, int quads[][7],
   }
 }
 
-void
+static void
 gui_draw_arrow (GdkPixmap * target)
 {
   cairo_t *context;
   gdouble x, y;
   guint colours[4] = { SOCKET_COLOUR, SOCKET_COLOUR, SOCKET_COLOUR, SOCKET_COLOUR };
-   
+
   context = gdk_cairo_create (GDK_DRAWABLE (buffer));
-     
+
   x = xborder + size * tile_size;
   y = yborder;
   gui_draw_faces (context, x, y, arrow_faces, 3, colours, FALSE);
-     
+
   cairo_destroy (context);
 }
 
-void
+static void
 gui_draw_socket (GdkPixmap * target, GtkStateType state, gint xadd, gint yadd)
 {
   cairo_t *context;
   guint colours[4] = { SOCKET_COLOUR, SOCKET_COLOUR, SOCKET_COLOUR, SOCKET_COLOUR };
   gdouble *colour;
-  
-  gdk_draw_rectangle (GDK_DRAWABLE(target), bg_gc, TRUE, xadd, yadd, 
+
+  gdk_draw_rectangle (GDK_DRAWABLE(target), bg_gc, TRUE, xadd, yadd,
                       tile_size, tile_size);
 
   context = gdk_cairo_create (GDK_DRAWABLE (target));
@@ -844,11 +450,24 @@ gui_draw_socket (GdkPixmap * target, GtkStateType state, gint xadd, gint yadd)
   cairo_destroy (context);
 }
 
-void
+static void
 gui_draw_number (cairo_t * context, gdouble x, gdouble y, guint number, gdouble *colour)
 {
   const gchar *text;
   cairo_text_extents_t extents;
+  static const char *translatable_number[10] = {
+    /* Translators: in-game numbers, replaceable with single-character local ideograms */
+    NC_("number", "0"),
+    NC_("number", "1"),
+    NC_("number", "2"),
+    NC_("number", "3"),
+    NC_("number", "4"),
+    NC_("number", "5"),
+    NC_("number", "6"),
+    NC_("number", "7"),
+    NC_("number", "8"),
+    NC_("number", "9")
+  };
 
   text = g_dpgettext2 (NULL, "number", translatable_number[number]);
 
@@ -860,7 +479,7 @@ gui_draw_number (cairo_t * context, gdouble x, gdouble y, guint number, gdouble
   cairo_show_text (context, text);
 }
 
-void
+static void
 gui_draw_tile (GdkPixmap * target, GtkStateType state, gint xadd, gint yadd,
                gint north, gint south, gint east, gint west, gboolean prelight)
 {
@@ -878,7 +497,7 @@ gui_draw_tile (GdkPixmap * target, GtkStateType state, gint xadd, gint yadd,
   /* Only draw inside the allocated space */
   cairo_rectangle (context, xadd, yadd, tile_size, tile_size);
   cairo_clip (context);
-   
+
   /* Clear background */
   cairo_set_source_rgba (context, 0.0, 0.0, 0.0, 1.0);
   cairo_paint (context);
@@ -910,47 +529,7 @@ gui_draw_tile (GdkPixmap * target, GtkStateType state, gint xadd, gint yadd,
   cairo_destroy (context);
 }
 
-gint
-button_motion_space (GtkWidget * widget, GdkEventButton * event)
-{
-  static int oldx = -1, oldy = -1;
-  gint x, y;
-
-  if (game_state == paused)
-    return FALSE;
-
-  if (button_down == 1) {
-    mousemover.x = event->x;
-    mousemover.y = event->y;
-    x = event->x - mousemover.xoff;
-    y = event->y - mousemover.yoff;
-    gdk_window_move (mousemover.window, x, y);
-    gdk_window_clear (mousemover.window);
-  }
-
-  /* This code hilights pieces as the mouse moves over them
-   * in general imitation of "prelight" in GTK. Need to highlight
-   * differently depending on if we are holding a tile or not */
-  if(mousemover.window == NULL)
-    get_tilexy (event->x, event->y, &x, &y);
-  else
-    tile_tilexy (event->x, event->y, &x, &y);
-
-  if ((x != oldx) || (y != oldy)) {
-    if ((oldx != -1) && (tiles[oldy][oldx].status == USED)) {
-      gui_draw_pixmap (buffer, oldx, oldy, FALSE, NULL);
-    }
-    if ((x != -1) && (tiles[y][x].status == USED)) {
-      gui_draw_pixmap (buffer, x, y, TRUE, NULL);
-    }
-    oldx = x;
-    oldy = y;
-  }
-
-  return FALSE;
-}
-
-void
+static void
 gui_draw_pixmap (GdkPixmap * target, gint x, gint y, gboolean prelight, Mover *mover)
 {
   gint which, xadd = 0, yadd = 0;
@@ -975,8 +554,8 @@ gui_draw_pixmap (GdkPixmap * target, gint x, gint y, gboolean prelight, Mover *m
     state = GTK_STATE_PRELIGHT;
 
   if (which == USED) {
-    if (game_state == paused)
-      gui_draw_tile (buffer, GTK_STATE_NORMAL, xadd, yadd, 0, 0, 0, 0, FALSE);    
+    if (game_state == PAUSED)
+      gui_draw_tile (buffer, GTK_STATE_NORMAL, xadd, yadd, 0, 0, 0, 0, FALSE);
     else
       gui_draw_tile (target, state, xadd, yadd, tiles[y][x].n, tiles[y][x].s,
                      tiles[y][x].e, tiles[y][x].w, state);
@@ -987,7 +566,197 @@ gui_draw_pixmap (GdkPixmap * target, gint x, gint y, gboolean prelight, Mover *m
   gtk_widget_queue_draw_area (space, xadd, yadd, tile_size, tile_size);
 }
 
-void
+static void
+redraw_all (void)
+{
+  guint x, y;
+#if GTK_CHECK_VERSION (2, 90, 5)
+  cairo_region_t *region;
+#else
+  GdkRegion *region;
+#endif
+
+  if (!gtk_widget_get_window (space))
+    return;
+
+  region = gdk_drawable_get_clip_region (GDK_DRAWABLE (gtk_widget_get_window (space)));
+  gdk_window_begin_paint_region (gtk_widget_get_window (space), region);
+
+  gdk_window_clear (gtk_widget_get_window (space));
+  gdk_draw_rectangle (gtk_widget_get_window (space), bg_gc, TRUE, 0, 0, -1, -1);
+  gdk_draw_rectangle (buffer, bg_gc, TRUE, 0, 0, -1, -1);
+  for (y = 0; y < size; y++)
+    for (x = 0; x < size * 2; x++)
+      gui_draw_pixmap (buffer, x, y, FALSE, NULL);
+
+  gui_draw_arrow(buffer);
+
+  gdk_window_end_paint (gtk_widget_get_window (space));
+
+#if GTK_CHECK_VERSION (2, 90, 5)
+  cairo_region_destroy (region);
+#else
+  gdk_region_destroy (region);
+#endif
+}
+
+static void
+redraw_left (void)
+{
+  gint x, y;
+#if GTK_CHECK_VERSION (2, 90, 5)
+  cairo_region_t *region;
+  cairo_rectangle_int_t rect =
+#else
+  GdkRegion *region;
+  GdkRectangle rect =
+#endif
+    { xborder, yborder, tile_size * size, tile_size * size };
+
+#if GTK_CHECK_VERSION (2, 90, 5)
+  region = cairo_region_create_rectangle (&rect);
+#else
+  region = gdk_region_rectangle (&rect);
+#endif
+
+  gdk_window_begin_paint_region (gtk_widget_get_window (space), region);
+
+  for (y = 0; y < size; y++)
+    for (x = 0; x < size; x++)
+      gui_draw_pixmap (buffer, x, y, FALSE, NULL);
+
+  gdk_window_end_paint (gtk_widget_get_window (space));
+
+#if GTK_CHECK_VERSION (2, 90, 5)
+  cairo_region_destroy (region);
+#else
+  gdk_region_destroy (region);
+#endif
+}
+
+/* Enable or disable the game menu items that are only relevant
+ * during a game. */
+static void
+set_game_menu_items_sensitive (gboolean state)
+{
+  gtk_action_set_sensitive (pause_action, state);
+  gtk_action_set_sensitive (hint_action, state);
+  gtk_action_set_sensitive (solve_action, state);
+}
+
+/* Show only valid options in the move menu. */
+static void
+update_move_menu_sensitivity (void)
+{
+  int x, y;
+  gboolean clear;
+  gboolean n, w, e, s;
+
+  n = w = e = s = TRUE;
+
+  clear = TRUE;
+  for (x = 0; x < size; x++) {
+    if (tiles[0][x].status == USED)
+      n = FALSE;
+    if (tiles[x][0].status == USED)
+      w = FALSE;
+    if (tiles[x][size - 1].status == USED)
+      e = FALSE;
+    if (tiles[size - 1][x].status == USED)
+      s = FALSE;
+    for (y = 0; y < size; y++)
+      if (tiles[x][y].status == USED)
+        clear = FALSE;
+  }
+
+  if (clear || (game_state == PAUSED))
+    n = w = e = s = FALSE;
+
+  gtk_action_set_sensitive (move_up_action, n);
+  gtk_action_set_sensitive (move_left_action, w);
+  gtk_action_set_sensitive (move_right_action, e);
+  gtk_action_set_sensitive (move_down_action, s);
+}
+
+static void
+clear_mover(Mover* mover)
+{
+  if(mover->window != NULL)
+    gdk_window_destroy(mover->window);
+  mover->window = NULL;
+  if (mover->pixmap)
+    g_object_unref (mover->pixmap);
+  mover->pixmap = NULL;
+}
+
+static void
+new_board (gint size)
+{
+  static gint myrand = 498;
+  gint x, y, x1, y1, i, j;
+  Tile tmp;
+
+  have_been_hinted = 0;
+  solve_me = 0;
+
+  if (timer_timeout) {
+    g_source_remove (timer_timeout);
+    gtk_widget_set_sensitive (GTK_WIDGET (space), TRUE);
+  }
+
+  if (button_down || moving) {
+    clear_mover(&mousemover);
+    clear_mover(&automover);
+    button_down = 0;
+    moving = 0;
+  }
+
+  g_random_set_seed (time (NULL) + myrand);
+
+  myrand += 17;
+
+  for (y = 0; y < size; y++)
+    for (x = 0; x < size; x++)
+      tiles[y][x].status = UNUSED;
+
+  for (y = 0; y < size; y++)
+    for (x = size; x < size * 2; x++) {
+      tiles[y][x].status = USED;
+      tiles[y][x].n = g_random_int () % 10;
+      tiles[y][x].s = g_random_int () % 10;
+      tiles[y][x].w = g_random_int () % 10;
+      tiles[y][x].e = g_random_int () % 10;
+    }
+
+  /* Sort */
+  for (y = 0; y < size; y++)
+    for (x = size; x < size * 2 - 1; x++)
+      tiles[y][x].e = tiles[y][x + 1].w;
+  for (y = 0; y < size - 1; y++)
+    for (x = size; x < size * 2; x++)
+      tiles[y][x].s = tiles[y + 1][x].n;
+
+  /* Copy tiles to orig_tiles */
+  for (y = 0; y < size; y++)
+    for (x = 0; x < size; x++)
+      orig_tiles[y][x] = tiles[y][x + size];
+
+  /* Unsort */
+  j = 0;
+  do {
+    for (i = 0; i < size * size * size; i++) {
+      x = g_random_int () % size + size;
+      y = g_random_int () % size;
+      x1 = g_random_int () % size + size;
+      y1 = g_random_int () % size;
+      tmp = tiles[y1][x1];
+      tiles[y1][x1] = tiles[y][x];
+      tiles[y][x] = tmp;
+    }
+  } while (tiles[0][size].e == tiles[0][size + 1].w && j++ < 8);
+}
+
+static void
 get_pixeltilexy (gint x, gint y, gint * xx, gint * yy)
 {
   gint sumx = xborder, sumy = yborder;
@@ -1001,30 +770,7 @@ get_pixeltilexy (gint x, gint y, gint * xx, gint * yy)
   *yy = sumy;
 }
 
-/* We use this slightly less strict version when dropping tiles. */
 static void
-get_tilexy_lazy (gint x, gint y, gint * xx, gint * yy)
-{
-  x = x - xborder;
-  y = y - yborder;
-  if (x / tile_size < size)
-    *xx = x / tile_size;
-  else
-    *xx = size + (x - (gap + tile_size * size)) / tile_size;
-  *yy = (y / tile_size);
-
-  /* Bounds checking */
-  if (*xx < 0)
-    *xx = 0;
-  else if (*xx >= size * 2)
-    *xx = size * 2 - 1;
-  if (y < 0)
-    *yy = 0;
-  else if (*yy >= size)
-    *yy = size - 1;
-}
-
-void
 get_tilexy (gint x, gint y, gint * xx, gint * yy)
 {
   /* We return -1, -1 if the location doesn't correspond to a tile. */
@@ -1051,10 +797,9 @@ get_tilexy (gint x, gint y, gint * xx, gint * yy)
   }
 }
 
-void
+static void
 get_offsetxy (gint x, gint y, gint * xoff, gint * yoff)
 {
-
   x = x - xborder;
   y = y - yborder;
   if (x / tile_size < size)
@@ -1064,18 +809,18 @@ get_offsetxy (gint x, gint y, gint * xoff, gint * yoff)
   *yoff = y % tile_size;
 }
 
-gint
+static gboolean
 setup_mover (gint x, gint y, Mover *mover)
 {
   gint xx, yy;
 
   get_tilexy (x, y, &xx, &yy);
   if (xx == -1)
-    return 0; /* No move */
+    return FALSE; /* No move */
   if (tiles[yy][xx].status == UNUSED)
-    return 0; /* No move */
+    return FALSE; /* No move */
   get_offsetxy (x, y, &mover->xoff, &mover->yoff);
-  
+
   mover->heldtile = tiles[yy][xx];
   mover->xstart = xx;
   mover->ystart = yy;
@@ -1093,119 +838,30 @@ setup_mover (gint x, gint y, Mover *mover)
   /* Show held tile on top if swapping */
   if(mover == &automover && mousemover.window != NULL)
     gdk_window_show(mousemover.window);
-  
+
   tiles[yy][xx].status = UNUSED;
   gui_draw_pixmap (buffer, xx, yy, FALSE, NULL);
-  return 1;
-}
-
-void
-clear_mover(Mover* mover)
-{
-  if(mover->window != NULL)
-    gdk_window_destroy(mover->window);
-  mover->window = NULL;
-  if (mover->pixmap)
-    g_object_unref (mover->pixmap);
-  mover->pixmap = NULL;
-}
-
-void
-release_tile(gint x, gint y) {
-  gint xx, yy;
-
-  tile_tilexy (x, y, &xx, &yy);
-  if (xx >= 0 && xx < size * 2 && yy >= 0 && yy < size) {
-    if (tiles[yy][xx].status == UNUSED && valid_drop (mousemover.xstart, mousemover.ystart, xx, yy)) {
-      move_held_animate (x, y, xx, yy);
-      return;
-    } else if (tiles[yy][xx].status == USED) {
-      swap_without_validation(xx, yy, mousemover.xstart, mousemover.ystart);
-      if(valid_drop (xx, yy, xx, yy) && valid_drop (mousemover.xstart, mousemover.ystart, mousemover.xstart, mousemover.ystart)) {
-        swap_without_validation (xx, yy, mousemover.xstart, mousemover.ystart);
-        move_tile_animate (xx, yy, mousemover.xstart, mousemover.ystart, TRUE);
-        return;
-      } else
-        swap_without_validation (xx, yy, mousemover.xstart, mousemover.ystart);
-    }
-  }
-    
-  /* Tile needs to go back to its original position */
-  move_held_animate (x, y, mousemover.xstart, mousemover.ystart);
-}
-
-void
-place_tile (gint x, gint y)
-{
-  tiles[automover.yend][automover.xend] = automover.heldtile;
-  gui_draw_pixmap (buffer, automover.xend, automover.yend, FALSE, NULL);
-
-  clear_mover(&automover);
-  if (game_over () && game_state != gameover) {
-    game_state = gameover;
-    games_clock_stop (GAMES_CLOCK (timer));
-    set_game_menu_items_sensitive (FALSE);
-    if (!have_been_hinted) {
-      message (_("Puzzle solved! Well done!"));
-    } else {
-      message (_("Puzzle solved!"));
-    }
-    game_score ();
-  }
-  update_move_menu_sensitivity ();
-}
-
-void
-tile_tilexy (gint x, gint y, gint *xx, gint *yy)
-{
-  get_tilexy_lazy (x - mousemover.xoff + tile_size / 2,
-                   y - mousemover.yoff + tile_size / 2, xx, yy);
+  return TRUE;
 }
 
-void
+static void
 swap_without_validation (gint x1, gint y1, gint x2, gint y2)
 {
-  tile swp = tiles[y1][x1];
+  Tile swp = tiles[y1][x1];
   tiles[y1][x1] = tiles[y2][x2];
   tiles[y2][x2] = swp;
   tiles[y1][x1].status = USED;
   tiles[y2][x2].status = USED;
 }
 
-gint
-valid_drop (gint sx, gint sy, gint ex, gint ey)
-{
-  if (ex >= size)
-    return 1;
-
-  /* West */
-  if (ex != 0 && tiles[ey][ex - 1].status == USED
-      && tiles[ey][ex - 1].e != tiles[sy][sx].w)
-    return 0;
-  /* East */
-  if (ex != size - 1 && tiles[ey][ex + 1].status == USED
-      && tiles[ey][ex + 1].w != tiles[sy][sx].e)
-    return 0;
-  /* North */
-  if (ey != 0 && tiles[ey - 1][ex].status == USED
-      && tiles[ey - 1][ex].s != tiles[sy][sx].n)
-    return 0;
-  /* South */
-  if (ey != size - 1 && tiles[ey + 1][ex].status == USED
-      && tiles[ey + 1][ex].n != tiles[sy][sx].s)
-    return 0;
-
-  return 1;
-}
-
-void
+static void
 move_tile (gint xx, gint yy, gint x, gint y)
 {
   tiles[yy][xx] = tiles[y][x];
   tiles[y][x].status = UNUSED;
 }
 
-void
+static void
 move_column (unsigned char dir)
 {
   gint x, y;
@@ -1252,19 +908,200 @@ move_column (unsigned char dir)
   update_move_menu_sensitivity ();
 }
 
-gint
+static gboolean
 game_over (void)
 {
   gint x, y;
   for (y = 0; y < size; y++)
     for (x = 0; x < size; x++)
       if (tiles[y][x].status == UNUSED)
-        return 0;
+        return FALSE;
 
-  return 1;
+  return TRUE;
 }
 
-gint
+static void
+message (gchar * message)
+{
+  guint context_id;
+
+  context_id =
+    gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "mesasge");
+  gtk_statusbar_pop (GTK_STATUSBAR (statusbar), context_id);
+  gtk_statusbar_push (GTK_STATUSBAR (statusbar), context_id, message);
+}
+
+static void
+pause_game (void)
+{
+  if (game_state != PAUSED) {
+    game_state = PAUSED;
+    message (_("Game paused"));
+    redraw_all ();
+    update_move_menu_sensitivity ();
+    gtk_action_set_sensitive (hint_action, FALSE);
+    gtk_action_set_sensitive (solve_action, FALSE);
+    games_clock_stop (GAMES_CLOCK (timer));
+  }
+}
+
+static void
+resume_game (void)
+{
+  if (game_state == PAUSED) {
+    game_state = PLAYING;
+    message ("");
+    redraw_all ();
+    update_move_menu_sensitivity ();
+    gtk_action_set_sensitive (hint_action, TRUE);
+    gtk_action_set_sensitive (solve_action, TRUE);
+    games_clock_start (GAMES_CLOCK (timer));
+  }
+}
+
+static void
+make_buffer (GtkWidget * widget)
+{
+  GtkAllocation allocation;
+
+  if (buffer)
+    g_object_unref (buffer);
+
+  gtk_widget_get_allocation (widget, &allocation);
+  buffer = gdk_pixmap_new (gtk_widget_get_window (widget),
+                           allocation.width,
+                           allocation.height, -1);
+}
+
+static void
+timer_start (void)
+{
+  games_clock_stop (GAMES_CLOCK (timer));
+  games_clock_reset (GAMES_CLOCK (timer));
+  games_clock_start (GAMES_CLOCK (timer));
+}
+
+static void
+new_game (void)
+{
+  gchar *str;
+
+  /* Reset pause menu */
+  gtk_action_set_sensitive(pause_action, TRUE);
+
+  game_state = GAME_OVER;
+
+  new_board (size);
+  gtk_widget_freeze_child_notify (space);
+  make_buffer (space);
+  redraw_all ();
+  gtk_widget_thaw_child_notify (space);
+  timer_start ();
+  set_game_menu_items_sensitive (TRUE);
+  update_move_menu_sensitivity ();
+  str = g_strdup_printf (_("Playing %d\303\227%d board"), size, size);
+  message (str);
+  g_free (str);
+
+  game_state = PLAYING;
+}
+
+static void
+new_game_cb (GtkAction * action, gpointer data)
+{
+  new_game ();
+}
+
+static void
+quit_game_cb (void)
+{
+  gtk_main_quit ();
+}
+
+static void
+create_window (void)
+{
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+  gtk_window_set_title (GTK_WINDOW (window), _(APPNAME_LONG));
+
+  gtk_window_set_default_size (GTK_WINDOW (window), DEFAULT_WIDTH, DEFAULT_HEIGHT);
+  games_conf_add_window (GTK_WINDOW (window), NULL);
+  gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+
+  gtk_widget_realize (window);
+  g_signal_connect (G_OBJECT (window), "delete_event",
+                    G_CALLBACK (quit_game_cb), NULL);
+}
+
+static gboolean
+expose_space (GtkWidget * widget, GdkEventExpose * event)
+{
+  gdk_draw_drawable (gtk_widget_get_window (widget),
+                     gtk_widget_get_style (widget)->fg_gc[GTK_STATE_NORMAL],
+                     buffer, event->area.x, event->area.y,
+                     event->area.x, event->area.y,
+                     event->area.width, event->area.height);
+  return FALSE;
+}
+
+/* We use this slightly less strict version when dropping tiles. */
+static void
+get_tilexy_lazy (gint x, gint y, gint * xx, gint * yy)
+{
+  x = x - xborder;
+  y = y - yborder;
+  if (x / tile_size < size)
+    *xx = x / tile_size;
+  else
+    *xx = size + (x - (gap + tile_size * size)) / tile_size;
+  *yy = (y / tile_size);
+
+  /* Bounds checking */
+  if (*xx < 0)
+    *xx = 0;
+  else if (*xx >= size * 2)
+    *xx = size * 2 - 1;
+  if (y < 0)
+    *yy = 0;
+  else if (*yy >= size)
+    *yy = size - 1;
+}
+
+static void
+tile_tilexy (gint x, gint y, gint *xx, gint *yy)
+{
+  get_tilexy_lazy (x - mousemover.xoff + tile_size / 2,
+                   y - mousemover.yoff + tile_size / 2, xx, yy);
+}
+
+static gboolean
+valid_drop (gint sx, gint sy, gint ex, gint ey)
+{
+  if (ex >= size)
+    return TRUE;
+
+  /* West */
+  if (ex != 0 && tiles[ey][ex - 1].status == USED
+      && tiles[ey][ex - 1].e != tiles[sy][sx].w)
+    return FALSE;
+  /* East */
+  if (ex != size - 1 && tiles[ey][ex + 1].status == USED
+      && tiles[ey][ex + 1].w != tiles[sy][sx].e)
+    return FALSE;
+  /* North */
+  if (ey != 0 && tiles[ey - 1][ex].status == USED
+      && tiles[ey - 1][ex].s != tiles[sy][sx].n)
+    return FALSE;
+  /* South */
+  if (ey != size - 1 && tiles[ey + 1][ex].status == USED
+      && tiles[ey + 1][ex].n != tiles[sy][sx].s)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gint
 show_score_dialog (gint pos, gboolean endofgame)
 {
   static GtkWidget *scoresdialog = NULL;
@@ -1304,13 +1141,7 @@ show_score_dialog (gint pos, gboolean endofgame)
   return result;
 }
 
-void
-score_cb (GtkAction * action, gpointer data)
-{
-  show_score_dialog (0, FALSE);
-}
-
-void
+static void
 game_score (void)
 {
   gint pos = 0;
@@ -1330,7 +1161,232 @@ game_score (void)
   }
 }
 
-void
+static void
+place_tile (gint x, gint y)
+{
+  tiles[automover.yend][automover.xend] = automover.heldtile;
+  gui_draw_pixmap (buffer, automover.xend, automover.yend, FALSE, NULL);
+
+  clear_mover(&automover);
+  if (game_over () && game_state != GAME_OVER) {
+    game_state = GAME_OVER;
+    games_clock_stop (GAMES_CLOCK (timer));
+    set_game_menu_items_sensitive (FALSE);
+    if (!have_been_hinted) {
+      message (_("Puzzle solved! Well done!"));
+    } else {
+      message (_("Puzzle solved!"));
+    }
+    game_score ();
+  }
+  update_move_menu_sensitivity ();
+}
+
+static void move_cb (void);
+
+static void
+move_held_animate (gint x, gint y, gint tx, gint ty)
+{
+  /* Need to take over movement from mouse mover to auto mover */
+  gint xx, yy;
+  move_src_x = x - mousemover.xoff;
+  move_src_y = y - mousemover.yoff;
+  get_tilexy (move_src_x, move_src_y, &xx, &yy);
+  get_pixeltilexy (tx, ty, &move_dest_x, &move_dest_y);
+
+  clear_mover(&automover);
+  automover = mousemover;
+  mousemover.window = NULL;
+  mousemover.pixmap = NULL;
+
+  automover.xend = tx;
+  automover.yend = ty;
+  moving = 1;
+  if(xx == tx && yy == ty)
+    animcount = SHORT_COUNT;
+  else
+    animcount = LONG_COUNT;
+  swapanim = FALSE;
+  gtk_widget_set_sensitive (GTK_WIDGET (space), FALSE);
+  timer_timeout = g_timeout_add (DELAY, (GSourceFunc) (move_cb), NULL);
+}
+
+static void
+move_cb (void)
+{
+  float dx, dy;
+  static gint count = 0;
+  dx = (float) (move_src_x - move_dest_x) / animcount;
+  dy = (float) (move_src_y - move_dest_y) / animcount;
+  if (count <= animcount) {
+    gdk_window_move (automover.window, move_src_x - (gint) (count * dx),
+                 (gint) move_src_y - (gint) (count * dy));
+    count++;
+  }
+  if (count > animcount) {
+    count = 0;
+    place_tile (move_dest_x + 1, move_dest_y + 1);
+    moving = 0;
+    g_source_remove (timer_timeout);
+    gtk_widget_set_sensitive (GTK_WIDGET (space), TRUE);
+
+    if (swapanim) {
+      move_held_animate (mousemover.x, mousemover.y, automover.xstart, automover.ystart);
+      return;
+    }
+
+    if (game_state != PLAYING)
+      return;
+    if (solve_me)
+      gtk_action_activate (hint_action);
+  }
+}
+
+static void
+move_tile_animate (gint x1, gint y1, gint x2, gint y2, gboolean sa)
+{
+  get_pixeltilexy (x1, y1, &move_src_x, &move_src_y);
+  get_pixeltilexy (x2, y2, &move_dest_x, &move_dest_y);
+
+  setup_mover (move_src_x, move_src_y, &automover);
+  automover.xend = x2;
+  automover.yend = y2;
+  moving = 1;
+  animcount = LONG_COUNT;
+  swapanim = sa;
+  gtk_widget_set_sensitive (GTK_WIDGET (space), FALSE);
+  timer_timeout = g_timeout_add (DELAY, (GSourceFunc) (move_cb), NULL);
+}
+
+static void
+release_tile(gint x, gint y)
+{
+  gint xx, yy;
+
+  tile_tilexy (x, y, &xx, &yy);
+  if (xx >= 0 && xx < size * 2 && yy >= 0 && yy < size) {
+    if (tiles[yy][xx].status == UNUSED && valid_drop (mousemover.xstart, mousemover.ystart, xx, yy)) {
+      move_held_animate (x, y, xx, yy);
+      return;
+    } else if (tiles[yy][xx].status == USED) {
+      swap_without_validation(xx, yy, mousemover.xstart, mousemover.ystart);
+      if(valid_drop (xx, yy, xx, yy) && valid_drop (mousemover.xstart, mousemover.ystart, mousemover.xstart, mousemover.ystart)) {
+        swap_without_validation (xx, yy, mousemover.xstart, mousemover.ystart);
+        move_tile_animate (xx, yy, mousemover.xstart, mousemover.ystart, TRUE);
+        return;
+      } else
+        swap_without_validation (xx, yy, mousemover.xstart, mousemover.ystart);
+    }
+  }
+
+  /* Tile needs to go back to its original position */
+  move_held_animate (x, y, mousemover.xstart, mousemover.ystart);
+}
+
+static gint
+button_press_space (GtkWidget * widget, GdkEventButton * event)
+{
+  if (game_state == PAUSED)
+    gtk_action_activate (pause_action);
+
+  if (game_state != PLAYING)
+    return FALSE;
+
+  if (event->button != 1)
+    return FALSE;
+
+  if (click_to_move)
+  {
+    if (button_down)
+    {
+      release_tile (event->x,event->y); /* Seen it happened */
+      button_down = 0;
+      return FALSE;
+    }
+    else
+    {
+      if (setup_mover (event->x, event->y, &mousemover))
+        button_down = 1;
+    }
+  }
+  else
+  {
+    if (button_down == 1)
+    {
+      release_tile (event->x,event->y); /* Seen it happened */
+      button_down = 0;
+      return FALSE;
+    }
+    if (setup_mover (event->x, event->y, &mousemover))
+      button_down = 1;
+  }
+
+  return FALSE;
+}
+
+static gint
+button_release_space (GtkWidget * widget, GdkEventButton * event)
+{
+  /* Ignore when using click to move mode */
+  if (click_to_move)
+    return FALSE;
+
+  if (event->button == 1) {
+    if (button_down == 1) {
+      release_tile (event->x, event->y);
+    }
+    button_down = 0;
+  }
+  return FALSE;
+}
+
+static gint
+button_motion_space (GtkWidget * widget, GdkEventButton * event)
+{
+  static int oldx = -1, oldy = -1;
+  gint x, y;
+
+  if (game_state == PAUSED)
+    return FALSE;
+
+  if (button_down == 1) {
+    mousemover.x = event->x;
+    mousemover.y = event->y;
+    x = event->x - mousemover.xoff;
+    y = event->y - mousemover.yoff;
+    gdk_window_move (mousemover.window, x, y);
+    gdk_window_clear (mousemover.window);
+  }
+
+  /* This code hilights pieces as the mouse moves over them
+   * in general imitation of "prelight" in GTK. Need to highlight
+   * differently depending on if we are holding a tile or not */
+  if(mousemover.window == NULL)
+    get_tilexy (event->x, event->y, &x, &y);
+  else
+    tile_tilexy (event->x, event->y, &x, &y);
+
+  if ((x != oldx) || (y != oldy)) {
+    if ((oldx != -1) && (tiles[oldy][oldx].status == USED)) {
+      gui_draw_pixmap (buffer, oldx, oldy, FALSE, NULL);
+    }
+    if ((x != -1) && (tiles[y][x].status == USED)) {
+      gui_draw_pixmap (buffer, x, y, TRUE, NULL);
+    }
+    oldx = x;
+    oldy = y;
+  }
+
+  return FALSE;
+}
+
+static void
+score_cb (GtkAction * action, gpointer data)
+{
+  show_score_dialog (0, FALSE);
+}
+
+static void
 update_tile_size (gint screen_width, gint screen_height)
 {
   gint xt_size, yt_size;
@@ -1353,13 +1409,13 @@ update_tile_size (gint screen_width, gint screen_height)
   /* Make arrow less sunken */
   arrow_border_size = 0.5 * tile_border_size;
   if (arrow_border_size < 1.0)
-    arrow_border_size = 1.0; 
+    arrow_border_size = 1.0;
 
   /* Rebuild the tile/socket vertices when required */
   rebuild_vertices = TRUE;
 }
 
-gboolean
+static gboolean
 configure_space (GtkWidget * widget, GdkEventConfigure * event)
 {
   gtk_widget_freeze_child_notify (widget);
@@ -1371,76 +1427,7 @@ configure_space (GtkWidget * widget, GdkEventConfigure * event)
   return FALSE;
 }
 
-void
-redraw_all (void)
-{
-  guint x, y;
-#if GTK_CHECK_VERSION (2, 90, 5)
-  cairo_region_t *region;
-#else
-  GdkRegion *region;
-#endif
-
-  if (!gtk_widget_get_window (space))
-    return;
-
-  region = gdk_drawable_get_clip_region (GDK_DRAWABLE (gtk_widget_get_window (space)));
-  gdk_window_begin_paint_region (gtk_widget_get_window (space), region);
-
-  gdk_window_clear (gtk_widget_get_window (space));
-  gdk_draw_rectangle (gtk_widget_get_window (space), bg_gc, TRUE, 0, 0, -1, -1);
-  gdk_draw_rectangle (buffer, bg_gc, TRUE, 0, 0, -1, -1);
-  for (y = 0; y < size; y++)
-    for (x = 0; x < size * 2; x++)
-      gui_draw_pixmap (buffer, x, y, FALSE, NULL);
-
-  gui_draw_arrow(buffer);
-
-  gdk_window_end_paint (gtk_widget_get_window (space));
-
-#if GTK_CHECK_VERSION (2, 90, 5)
-  cairo_region_destroy (region);
-#else
-  gdk_region_destroy (region);
-#endif
-}
-
-void
-redraw_left (void)
-{
-  gint x, y;
-#if GTK_CHECK_VERSION (2, 90, 5)
-  cairo_region_t *region;
-  cairo_rectangle_int_t rect =
-#else
-  GdkRegion *region;
-  GdkRectangle rect =
-#endif
-    { xborder, yborder, tile_size * size, tile_size * size };
-
-#if GTK_CHECK_VERSION (2, 90, 5)
-  region = cairo_region_create_rectangle (&rect);
-#else
-  region = gdk_region_rectangle (&rect);
-#endif
-
-  gdk_window_begin_paint_region (gtk_widget_get_window (space), region);
-
-  for (y = 0; y < size; y++)
-    for (x = 0; x < size; x++)
-      gui_draw_pixmap (buffer, x, y, FALSE, NULL);
-
-  gdk_window_end_paint (gtk_widget_get_window (space));
-
-#if GTK_CHECK_VERSION (2, 90, 5)
-  cairo_region_destroy (region);
-#else
-  gdk_region_destroy (region);
-#endif
-}
-
-
-GtkWidget *
+static GtkWidget *
 create_statusbar (void)
 {
   GtkWidget *status_bar, *time_label, *time_box;
@@ -1460,21 +1447,9 @@ create_statusbar (void)
   return status_bar;
 }
 
-void
-message (gchar * message)
-{
-  guint context_id;
-
-  context_id =
-    gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar), "mesasge");
-  gtk_statusbar_pop (GTK_STATUSBAR (statusbar), context_id);
-  gtk_statusbar_push (GTK_STATUSBAR (statusbar), context_id, message);
-}
-
-void
+static void
 init_window_attrib (void)
 {
-
   /* The depth of mover.window must match the depth of gtk_widget_get_window (space). */
   windowattrib.wclass = GDK_INPUT_OUTPUT;
   windowattrib.window_type = GDK_WINDOW_CHILD;
@@ -1485,218 +1460,19 @@ init_window_attrib (void)
   windowattrib.visual = gdk_drawable_get_visual (gtk_widget_get_window (space));
 }
 
-void
-new_board (gint size)
-{
-  static gint myrand = 498;
-  gint x, y, x1, y1, i, j;
-  tile tmp;
-
-  have_been_hinted = 0;
-  solve_me = 0;
-
-  if (timer_timeout) {
-    g_source_remove (timer_timeout);
-    gtk_widget_set_sensitive (GTK_WIDGET (space), TRUE);
-  }
-
-  if (button_down || moving) {
-    clear_mover(&mousemover);
-    clear_mover(&automover);
-    button_down = 0;
-    moving = 0;
-  }
-
-  g_random_set_seed (time (NULL) + myrand);
-
-  myrand += 17;
-
-  for (y = 0; y < size; y++)
-    for (x = 0; x < size; x++)
-      tiles[y][x].status = UNUSED;
-
-  for (y = 0; y < size; y++)
-    for (x = size; x < size * 2; x++) {
-      tiles[y][x].status = USED;
-      tiles[y][x].n = g_random_int () % 10;
-      tiles[y][x].s = g_random_int () % 10;
-      tiles[y][x].w = g_random_int () % 10;
-      tiles[y][x].e = g_random_int () % 10;
-    }
-
-  /* Sort */
-  for (y = 0; y < size; y++)
-    for (x = size; x < size * 2 - 1; x++)
-      tiles[y][x].e = tiles[y][x + 1].w;
-  for (y = 0; y < size - 1; y++)
-    for (x = size; x < size * 2; x++)
-      tiles[y][x].s = tiles[y + 1][x].n;
-
-  /* Copy tiles to orig_tiles */
-  for (y = 0; y < size; y++)
-    for (x = 0; x < size; x++)
-      orig_tiles[y][x] = tiles[y][x + size];
-
-  /* Unsort */
-  j = 0;
-  do {
-    for (i = 0; i < size * size * size; i++) {
-      x = g_random_int () % size + size;
-      y = g_random_int () % size;
-      x1 = g_random_int () % size + size;
-      y1 = g_random_int () % size;
-      tmp = tiles[y1][x1];
-      tiles[y1][x1] = tiles[y][x];
-      tiles[y][x] = tmp;
-    }
-  } while (tiles[0][size].e == tiles[0][size + 1].w && j++ < 8);
-}
-
-void
-pause_game (void)
-{
-  if (game_state != paused) {
-    game_state = paused;
-    message (_("Game paused"));
-    redraw_all ();
-    update_move_menu_sensitivity ();
-    gtk_action_set_sensitive (hint_action, FALSE);
-    gtk_action_set_sensitive (solve_action, FALSE);
-    games_clock_stop (GAMES_CLOCK (timer));
-  }
-}
-
-void
-resume_game (void)
-{
-  if (game_state == paused) {
-    game_state = playing;
-    message ("");
-    redraw_all ();
-    update_move_menu_sensitivity ();
-    gtk_action_set_sensitive (hint_action, TRUE);
-    gtk_action_set_sensitive (solve_action, TRUE);
-    games_clock_start (GAMES_CLOCK (timer));
-  }
-}
-
-void
+static void
 pause_cb (void)
 {
-  if (game_state == gameover)
+  if (game_state == GAME_OVER)
     return;
 
-  if (game_state != paused) {
+  if (game_state != PAUSED) {
     pause_game ();
   } else {
     resume_game ();
   }
 }
 
-void
-timer_start (void)
-{
-  games_clock_stop (GAMES_CLOCK (timer));
-  games_clock_reset (GAMES_CLOCK (timer));
-  games_clock_start (GAMES_CLOCK (timer));
-}
-
-/* --------------------------- MENU --------------------- */
-GtkWidget *
-create_menu (GtkUIManager * ui_manager)
-{
-  gint i;
-  GtkActionGroup *action_group;
-  GtkAction *action;
-
-  action_group = gtk_action_group_new ("actions");
-
-  gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
-  gtk_action_group_add_actions (action_group, action_entry,
-                                G_N_ELEMENTS (action_entry), window);
-  gtk_action_group_add_radio_actions (action_group, size_action_entry,
-                                      G_N_ELEMENTS (size_action_entry), -1,
-                                      G_CALLBACK (size_cb), NULL);
-
-  gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
-  gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, NULL);
-
-  new_game_action = gtk_action_group_get_action (action_group, "NewGame");
-  hint_action = gtk_action_group_get_action (action_group, "Hint");
-  solve_action = gtk_action_group_get_action (action_group, "Solve");
-  scores_action = gtk_action_group_get_action (action_group, "Scores");
-  move_up_action = gtk_action_group_get_action (action_group, "MoveUp");
-  move_left_action = gtk_action_group_get_action (action_group, "MoveLeft");
-  move_right_action = gtk_action_group_get_action (action_group, "MoveRight");
-  move_down_action = gtk_action_group_get_action (action_group, "MoveDown");
-  pause_action = GTK_ACTION (games_pause_action_new ("PauseGame"));
-  g_signal_connect (G_OBJECT (pause_action), "state-changed", G_CALLBACK (pause_cb), NULL);
-  gtk_action_group_add_action_with_accel (action_group, pause_action, NULL);
-  fullscreen_action = GTK_ACTION (games_fullscreen_action_new ("Fullscreen", GTK_WINDOW(window)));
-  gtk_action_group_add_action_with_accel (action_group, fullscreen_action, NULL);
-
-  gtk_action_group_add_toggle_actions (action_group, toggles,
-                                       G_N_ELEMENTS (toggles), NULL);
-  action = gtk_action_group_get_action (action_group, "ClickToMove");
-  gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), click_to_move);
-
-  for (i = 0; i < G_N_ELEMENTS (size_action_entry); i++)
-    size_action[i] =
-      gtk_action_group_get_action (action_group, size_action_entry[i].name);
-
-  return gtk_ui_manager_get_widget (ui_manager, "/MainMenu");
-}
-
-void
-make_buffer (GtkWidget * widget)
-{
-  GtkAllocation allocation;
-
-  if (buffer)
-    g_object_unref (buffer);
-
-  gtk_widget_get_allocation (widget, &allocation);
-  buffer = gdk_pixmap_new (gtk_widget_get_window (widget),
-                           allocation.width,
-                           allocation.height, -1);
-}
-
-void
-new_game (void){
-  gchar *str;
-
-  /* Reset pause menu */
-  gtk_action_set_sensitive(pause_action, TRUE);
-
-  game_state = gameover;
-
-  new_board (size);
-  gtk_widget_freeze_child_notify (space);
-  make_buffer (space);
-  redraw_all ();
-  gtk_widget_thaw_child_notify (space);
-  timer_start ();
-  set_game_menu_items_sensitive (TRUE);
-  update_move_menu_sensitivity ();
-  str = g_strdup_printf (_("Playing %d\303\227%d board"), size, size);
-  message (str);
-  g_free (str);
-    
-  game_state = playing;
-}
-
-void
-new_game_cb (GtkAction * action, gpointer data)
-{
-  new_game ();
-}
-
-void
-quit_game_cb (void)
-{
-  gtk_main_quit ();
-}
-
 #ifdef WITH_SMCLIENT
 static int
 save_state_cb (EggSMClient *client,
@@ -1724,79 +1500,7 @@ save_state_cb (EggSMClient *client,
   return TRUE;
 }
 
-static gint
-quit_cb (EggSMClient *client,
-         gpointer client_data)
-{
-  quit_game_cb ();
-
-  return FALSE;
-}
-
-#endif /* WITH_SMCLIENT */
-
-void
-size_cb (GtkAction * action, gpointer data)
-{
-  gint newsize;
-  gint width, height;
-
-  newsize = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
-
-  gdk_drawable_get_size (gtk_widget_get_window (space), &width, &height);
-
-  if (game_state == paused)
-    gtk_action_activate (pause_action);
-
-  if (size == newsize)
-    return;
-  size = newsize;
-  update_tile_size (width, height);
-  games_scores_set_category (highscores, scorecats[size - 2].key);
-  games_conf_set_integer (NULL, KEY_GRID_SIZE, size);
-  gtk_action_activate (new_game_action);
-}
-
-void
-clickmove_toggle_cb(GtkToggleAction * togglebutton, gpointer data)
-{
-  click_to_move = gtk_toggle_action_get_active (togglebutton);
-  games_conf_set_boolean (NULL, KEY_CLICK_MOVE, click_to_move);
-}
-
-void
-move_up_cb (GtkAction * action, gpointer data)
-{
-  move_column ('n');
-}
-
-void
-move_left_cb (GtkAction * action, gpointer data)
-{
-  move_column ('w');
-}
-
-void
-move_right_cb (GtkAction * action, gpointer data)
-{
-  move_column ('e');
-}
-
-void
-move_down_cb (GtkAction * action, gpointer data)
-{
-  move_column ('s');
-}
-
-gint
-compare_tile (tile * t1, tile * t2)
-{
-  if (t1->e == t2->e && t1->w == t2->w && t1->s == t2->s && t1->n == t2->n)
-    return 0;
-  return 1;
-}
-
-void
+static void
 find_first_tile (gint status, gint * xx, gint * yy)
 {
   gint x, y;
@@ -1809,101 +1513,28 @@ find_first_tile (gint status, gint * xx, gint * yy)
       }
 }
 
-#define LONG_COUNT 15
-#define SHORT_COUNT 5
-#define DELAY 10
-
-gint animcount;
-gboolean swapanim;
-gint move_src_x, move_src_y, move_dest_x, move_dest_y;
-
-void
-move_cb (void)
+static gboolean
+tiles_match (Tile * t1, Tile * t2)
 {
-  float dx, dy;
-  static gint count = 0;
-  dx = (float) (move_src_x - move_dest_x) / animcount;
-  dy = (float) (move_src_y - move_dest_y) / animcount;
-  if (count <= animcount) {
-    gdk_window_move (automover.window, move_src_x - (gint) (count * dx),
-                 (gint) move_src_y - (gint) (count * dy));
-    count++;
-  }
-  if (count > animcount) {
-    count = 0;
-    place_tile (move_dest_x + 1, move_dest_y + 1);
-    moving = 0;
-    g_source_remove (timer_timeout);
-    gtk_widget_set_sensitive (GTK_WIDGET (space), TRUE);
-
-    if(swapanim) {
-      move_held_animate (mousemover.x, mousemover.y, automover.xstart, automover.ystart);
-      return;
-    }
-
-    if (game_state != playing)
-      return;
-    if (solve_me)
-      gtk_action_activate (hint_action);
-  }
+  if (t1->e == t2->e && t1->w == t2->w && t1->s == t2->s && t1->n == t2->n)
+    return FALSE;
+  return TRUE;
 }
 
-void
+static void
 hint_move (gint x1, gint y1, gint x2, gint y2)
 {
   have_been_hinted = 1;
   move_tile_animate (x1, y1, x2, y2, FALSE);
 }
 
-void
-move_tile_animate (gint x1, gint y1, gint x2, gint y2, gboolean sa)
-{
-  get_pixeltilexy (x1, y1, &move_src_x, &move_src_y);
-  get_pixeltilexy (x2, y2, &move_dest_x, &move_dest_y);
-
-  setup_mover (move_src_x, move_src_y, &automover);
-  automover.xend = x2;
-  automover.yend = y2;
-  moving = 1;
-  animcount = LONG_COUNT;
-  swapanim = sa;
-  gtk_widget_set_sensitive (GTK_WIDGET (space), FALSE);
-  timer_timeout = g_timeout_add (DELAY, (GSourceFunc) (move_cb), NULL);
-}
-
-void
-move_held_animate (gint x, gint y, gint tx, gint ty) {
-  /* Need to take over movement from mouse mover to auto mover */
-  gint xx, yy;
-  move_src_x = x - mousemover.xoff;
-  move_src_y = y - mousemover.yoff;
-  get_tilexy (move_src_x, move_src_y, &xx, &yy);
-  get_pixeltilexy (tx, ty, &move_dest_x, &move_dest_y);
-
-  clear_mover(&automover);
-  automover = mousemover;
-  mousemover.window = NULL;
-  mousemover.pixmap = NULL;
-
-  automover.xend = tx;
-  automover.yend = ty;
-  moving = 1;
-  if(xx == tx && yy == ty)
-    animcount = SHORT_COUNT;
-  else
-    animcount = LONG_COUNT;
-  swapanim = FALSE;
-  gtk_widget_set_sensitive (GTK_WIDGET (space), FALSE);
-  timer_timeout = g_timeout_add (DELAY, (GSourceFunc) (move_cb), NULL);
-}
-
-void
+static void
 hint_cb (GtkAction * action, gpointer data)
 {
   gint x1, y1, x2 = 0, y2 = 0, x = 0, y = 0;
-  tile hint_tile;
+  Tile hint_tile;
 
-  if ((game_state != playing) || button_down || moving)
+  if ((game_state != PLAYING) || button_down || moving)
     return;
 
   find_first_tile (USED, &x, &y);
@@ -1914,9 +1545,9 @@ hint_cb (GtkAction * action, gpointer data)
   /* Find position in original map */
   for (y = 0; y < size; y++)
     for (x = 0; x < size; x++)
-      if (compare_tile (&hint_tile, &orig_tiles[y][x]) == 0) {
+      if (!tiles_match (&hint_tile, &orig_tiles[y][x])) {
         if (tiles[y][x].status == USED
-            && compare_tile (&hint_tile, &tiles[y][x]) == 0) {
+            && !tiles_match (&hint_tile, &tiles[y][x])) {
         /* Do Nothing */
         } else {
           x2 = x;
@@ -1968,20 +1599,20 @@ hint_cb (GtkAction * action, gpointer data)
   hint_move (x1, y1, x2, y2);
 }
 
-void
+static void
 solve_cb (GtkAction * action, gpointer data)
 {
   solve_me = 1;
   gtk_action_activate (hint_action);
 }
 
-void
+static void
 help_cb (GtkAction * action, gpointer data)
 {
   games_help_display (window, "gnotravex", NULL);
 }
 
-void
+static void
 about_cb (GtkAction * action, gpointer data)
 {
   const gchar *authors[] = { "Lars Rydlinge", NULL };
@@ -2016,13 +1647,77 @@ about_cb (GtkAction * action, gpointer data)
   g_free (license);
 }
 
+static gint
+quit_cb (EggSMClient *client,
+         gpointer client_data)
+{
+  quit_game_cb ();
+
+  return FALSE;
+}
+
+#endif /* WITH_SMCLIENT */
+
 static void
-load_default_background (void)
+size_cb (GtkAction * action, gpointer data)
+{
+  gint newsize;
+  gint width, height;
+
+  newsize = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action));
+
+  gdk_drawable_get_size (gtk_widget_get_window (space), &width, &height);
+
+  if (game_state == PAUSED)
+    gtk_action_activate (pause_action);
+
+  if (size == newsize)
+    return;
+  size = newsize;
+  update_tile_size (width, height);
+  games_scores_set_category (highscores, scorecats[size - 2].key);
+  games_conf_set_integer (NULL, KEY_GRID_SIZE, size);
+  gtk_action_activate (new_game_action);
+}
+
+static void
+clickmove_toggle_cb(GtkToggleAction * togglebutton, gpointer data)
+{
+  click_to_move = gtk_toggle_action_get_active (togglebutton);
+  games_conf_set_boolean (NULL, KEY_CLICK_MOVE, click_to_move);
+}
+
+static void
+move_up_cb (GtkAction * action, gpointer data)
+{
+  move_column ('n');
+}
+
+static void
+move_left_cb (GtkAction * action, gpointer data)
+{
+  move_column ('w');
+}
+
+static void
+move_right_cb (GtkAction * action, gpointer data)
+{
+  move_column ('e');
+}
+
+static void
+move_down_cb (GtkAction * action, gpointer data)
+{
+  move_column ('s');
+}
+
+static void
+load_background (void)
 {
   GdkPixmap *pm;
   GdkPixbuf *pb;
   char *path;
-  const char * dname; 
+  const char * dname;
   const char * filename = "baize.png";
   GError *error = NULL;
 
@@ -2034,16 +1729,225 @@ load_default_background (void)
     g_error_free (error);
 
     pb = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
-                         FALSE, 
+                         FALSE,
                          8,1,1);
     gdk_pixbuf_fill (pb, 0xffffffff);
   }
   gdk_pixbuf_render_pixmap_and_mask_for_colormap (pb,
-                                                  gdk_colormap_get_system (), 
+                                                  gdk_colormap_get_system (),
                                                   &pm, NULL, 127);
   g_object_unref (pb);
   g_free (path);
 
-  default_background_pixmap = pm; 
+  background_pixmap = pm;
+}
 
+static GtkWidget *
+create_menu (GtkUIManager * ui_manager)
+{
+  gint i;
+  GtkActionGroup *action_group;
+  GtkAction *action;
+  const GtkActionEntry action_entry[] = {
+    {"GameMenu", NULL, N_("_Game")},
+    {"MoveMenu", NULL, N_("_Move")},
+    {"SettingsMenu", NULL, N_("_Settings")},
+    {"SizeMenu", NULL, N_("_Size")},
+    {"HelpMenu", NULL, N_("_Help")},
+    {"NewGame", GAMES_STOCK_NEW_GAME, NULL, NULL, NULL,
+     G_CALLBACK (new_game_cb)},
+    {"Hint", GAMES_STOCK_HINT, NULL, NULL, NULL, G_CALLBACK (hint_cb)},
+    {"Solve", GTK_STOCK_REFRESH, N_("Sol_ve"), NULL, N_("Solve the game"),
+     G_CALLBACK (solve_cb)},
+    {"Scores", GAMES_STOCK_SCORES, NULL, NULL, NULL, G_CALLBACK (score_cb)},
+    {"Quit", GTK_STOCK_QUIT, NULL, NULL, NULL, G_CALLBACK (quit_game_cb)},
+    {"MoveUp", GTK_STOCK_GO_UP, N_("_Up"), "<control>Up",
+     N_("Move the pieces up"), G_CALLBACK (move_up_cb)},
+    {"MoveLeft", GTK_STOCK_GO_BACK, N_("_Left"), "<control>Left",
+     N_("Move the pieces left"), G_CALLBACK (move_left_cb)},
+    {"MoveRight", GTK_STOCK_GO_FORWARD, N_("_Right"), "<control>Right",
+     N_("Move the pieces right"), G_CALLBACK (move_right_cb)},
+    {"MoveDown", GTK_STOCK_GO_DOWN, N_("_Down"), "<control>Down",
+     N_("Move the pieces down"), G_CALLBACK (move_down_cb)},
+    {"Contents", GAMES_STOCK_CONTENTS, NULL, NULL, NULL, G_CALLBACK (help_cb)},
+    {"About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK (about_cb)}
+  };
+  const GtkToggleActionEntry toggles[] = {
+    {"ClickToMove", NULL, N_("_Click to Move"), NULL, "Pick up and drop tiles by clicking",
+     G_CALLBACK (clickmove_toggle_cb)}
+  };
+
+  action_group = gtk_action_group_new ("actions");
+
+  gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+  gtk_action_group_add_actions (action_group, action_entry,
+                                G_N_ELEMENTS (action_entry), window);
+  gtk_action_group_add_radio_actions (action_group, size_action_entry,
+                                      G_N_ELEMENTS (size_action_entry), -1,
+                                      G_CALLBACK (size_cb), NULL);
+
+  gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+  gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, NULL);
+
+  new_game_action = gtk_action_group_get_action (action_group, "NewGame");
+  hint_action = gtk_action_group_get_action (action_group, "Hint");
+  solve_action = gtk_action_group_get_action (action_group, "Solve");
+  scores_action = gtk_action_group_get_action (action_group, "Scores");
+  move_up_action = gtk_action_group_get_action (action_group, "MoveUp");
+  move_left_action = gtk_action_group_get_action (action_group, "MoveLeft");
+  move_right_action = gtk_action_group_get_action (action_group, "MoveRight");
+  move_down_action = gtk_action_group_get_action (action_group, "MoveDown");
+  pause_action = GTK_ACTION (games_pause_action_new ("PauseGame"));
+  g_signal_connect (G_OBJECT (pause_action), "state-changed", G_CALLBACK (pause_cb), NULL);
+  gtk_action_group_add_action_with_accel (action_group, pause_action, NULL);
+  fullscreen_action = GTK_ACTION (games_fullscreen_action_new ("Fullscreen", GTK_WINDOW(window)));
+  gtk_action_group_add_action_with_accel (action_group, fullscreen_action, NULL);
+
+  gtk_action_group_add_toggle_actions (action_group, toggles,
+                                       G_N_ELEMENTS (toggles), NULL);
+  action = gtk_action_group_get_action (action_group, "ClickToMove");
+  gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), click_to_move);
+
+  for (i = 0; i < G_N_ELEMENTS (size_action_entry); i++)
+    size_action[i] =
+      gtk_action_group_get_action (action_group, size_action_entry[i].name);
+
+  return gtk_ui_manager_get_widget (ui_manager, "/MainMenu");
+}
+
+int
+main (int argc, char **argv)
+{
+  GOptionContext *context;
+  GtkWidget *vbox;
+  GtkWidget *menubar;
+  GtkUIManager *ui_manager;
+  GtkAccelGroup *accel_group;
+  gboolean retval;
+  GError *error = NULL;
+#ifdef WITH_SMCLIENT
+  EggSMClient *sm_client;
+#endif /* WITH_SMCLIENT */
+
+  if (!games_runtime_init ("gnotravex"))
+    return 1;
+
+#ifdef ENABLE_SETGID
+  setgid_io_init ();
+#endif
+
+  context = g_option_context_new (NULL);
+#if GLIB_CHECK_VERSION (2, 12, 0)
+  g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+#endif /* GLIB_CHECK_VERSION (2, 12, 0) */
+  g_option_context_add_group (context, gtk_get_option_group (TRUE));
+#ifdef WITH_SMCLIENT
+  g_option_context_add_group (context, egg_sm_client_get_option_group ());
+#endif /* WITH_SMCLIENT */
+
+  g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+  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 (_(APPNAME_LONG));
+
+  games_conf_initialise (APPNAME);
+
+  highscores = games_scores_new ("gnotravex",
+                                 scorecats, G_N_ELEMENTS (scorecats),
+                                 NULL, NULL,
+                                 1 /* default category */,
+                                 GAMES_SCORES_STYLE_TIME_ASCENDING);
+
+  games_stock_init ();
+
+  gtk_window_set_default_icon_name ("gnome-tetravex");
+
+#ifdef WITH_SMCLIENT
+  sm_client = egg_sm_client_get ();
+  g_signal_connect (sm_client, "save-state",
+                    G_CALLBACK (save_state_cb), NULL);
+  g_signal_connect (sm_client, "quit",
+                    G_CALLBACK (quit_cb), NULL);
+#endif /* WITH_SMCLIENT */
+
+  if (size == -1)
+    size = games_conf_get_integer (NULL, KEY_GRID_SIZE, NULL);
+  if (size < 2 || size > 6)
+    size = 3;
+  games_scores_set_category (highscores, scorecats[size - 2].key);
+
+  click_to_move = games_conf_get_boolean (NULL, KEY_CLICK_MOVE, NULL);
+
+  load_background ();
+  create_window ();
+
+  space = gtk_drawing_area_new ();
+  gtk_widget_set_events (space,
+                         GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
+                         | GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
+
+  statusbar = create_statusbar ();
+
+  ui_manager = gtk_ui_manager_new ();
+  games_stock_prepare_for_statusbar_tooltips (ui_manager, statusbar);
+
+  menubar = create_menu (ui_manager);
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), space, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), statusbar, FALSE, FALSE, 0);
+
+  accel_group = gtk_ui_manager_get_accel_group (ui_manager);
+  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
+
+  gtk_widget_realize (space);
+  bg_gc = gdk_gc_new (gtk_widget_get_window (space));
+  if (background_pixmap)
+  {
+    gdk_gc_set_tile (bg_gc, background_pixmap);
+    gdk_gc_set_fill (bg_gc, GDK_TILED);
+  }
+
+  g_signal_connect (G_OBJECT (space), "expose_event",
+                    G_CALLBACK (expose_space), NULL);
+  g_signal_connect (G_OBJECT (space), "configure_event",
+                    G_CALLBACK (configure_space), NULL);
+  g_signal_connect (G_OBJECT (space), "button_press_event",
+                    G_CALLBACK (button_press_space), NULL);
+  g_signal_connect (G_OBJECT (space), "button_release_event",
+                    G_CALLBACK (button_release_space), NULL);
+  g_signal_connect (G_OBJECT (space), "motion_notify_event",
+                    G_CALLBACK (button_motion_space), NULL);
+  /* We do our own double-buffering. */
+  gtk_widget_set_double_buffered (space, FALSE);
+
+  gtk_widget_show (space);
+
+  if (session_xpos >= 0 && session_ypos >= 0)
+    gtk_window_move (GTK_WINDOW (window), session_xpos, session_ypos);
+
+  gtk_widget_show_all (window);
+  init_window_attrib ();
+
+  gtk_action_activate (new_game_action);
+
+  gtk_action_activate (size_action[size - 2]);
+
+  gtk_main ();
+
+  games_conf_shutdown ();
+
+  games_runtime_shutdown ();
+
+  return 0;
 }



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