Fix various departures from C found by `gcc -ansi -pedantic'. I
[sgt/puzzles] / gtk.c
diff --git a/gtk.c b/gtk.c
index 66ee767..7a92d66 100644 (file)
--- a/gtk.c
+++ b/gtk.c
@@ -76,16 +76,17 @@ struct frontend {
     config_item *cfg;
     int cfg_which, cfgret;
     GtkWidget *cfgbox;
-    char *paste_data;
+    void *paste_data;
     int paste_data_len;
+    char *laststatus;
 };
 
 void get_random_seed(void **randseed, int *randseedsize)
 {
-    time_t *tp = snew(time_t);
-    time(tp);
-    *randseed = (void *)tp;
-    *randseedsize = sizeof(time_t);
+    struct timeval *tvp = snew(struct timeval);
+    gettimeofday(tvp, NULL);
+    *randseed = (void *)tvp;
+    *randseedsize = sizeof(struct timeval);
 }
 
 void frontend_default_colour(frontend *fe, float *output)
@@ -98,10 +99,19 @@ void frontend_default_colour(frontend *fe, float *output)
 
 void status_bar(frontend *fe, char *text)
 {
+    char *rewritten;
+
     assert(fe->statusbar);
 
-    gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx);
-    gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, text);
+    rewritten = midend_rewrite_statusbar(fe->me, text);
+    if (!fe->laststatus || strcmp(rewritten, fe->laststatus)) {
+       gtk_statusbar_pop(GTK_STATUSBAR(fe->statusbar), fe->statusctx);
+       gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx, rewritten);
+       sfree(fe->laststatus);
+       fe->laststatus = rewritten;
+    } else {
+       sfree(rewritten);
+    }
 }
 
 void start_draw(frontend *fe)
@@ -317,32 +327,42 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
 {
     frontend *fe = (frontend *)data;
     int keyval;
+    int shift = (event->state & GDK_SHIFT_MASK) ? MOD_SHFT : 0;
+    int ctrl = (event->state & GDK_CONTROL_MASK) ? MOD_CTRL : 0;
 
     if (!fe->pixmap)
         return TRUE;
 
-    if (event->string[0] && !event->string[1])
-        keyval = (unsigned char)event->string[0];
-    else if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
-             event->keyval == GDK_KP_8)
-        keyval = CURSOR_UP;
-    else if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
-             event->keyval == GDK_KP_2)
-        keyval = CURSOR_DOWN;
-    else if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left ||
-             event->keyval == GDK_KP_4)
-        keyval = CURSOR_LEFT;
-    else if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right ||
-             event->keyval == GDK_KP_6)
-        keyval = CURSOR_RIGHT;
+    if (event->keyval == GDK_Up)
+        keyval = shift | ctrl | CURSOR_UP;
+    else if (event->keyval == GDK_KP_Up || event->keyval == GDK_KP_8)
+       keyval = MOD_NUM_KEYPAD | '8';
+    else if (event->keyval == GDK_Down)
+        keyval = shift | ctrl | CURSOR_DOWN;
+    else if (event->keyval == GDK_KP_Down || event->keyval == GDK_KP_2)
+       keyval = MOD_NUM_KEYPAD | '2';
+    else if (event->keyval == GDK_Left)
+        keyval = shift | ctrl | CURSOR_LEFT;
+    else if (event->keyval == GDK_KP_Left || event->keyval == GDK_KP_4)
+       keyval = MOD_NUM_KEYPAD | '4';
+    else if (event->keyval == GDK_Right)
+        keyval = shift | ctrl | CURSOR_RIGHT;
+    else if (event->keyval == GDK_KP_Right || event->keyval == GDK_KP_6)
+       keyval = MOD_NUM_KEYPAD | '6';
     else if (event->keyval == GDK_KP_Home || event->keyval == GDK_KP_7)
-        keyval = CURSOR_UP_LEFT;
+        keyval = MOD_NUM_KEYPAD | '7';
     else if (event->keyval == GDK_KP_End || event->keyval == GDK_KP_1)
-        keyval = CURSOR_DOWN_LEFT;
+        keyval = MOD_NUM_KEYPAD | '1';
     else if (event->keyval == GDK_KP_Page_Up || event->keyval == GDK_KP_9)
-        keyval = CURSOR_UP_RIGHT;
+        keyval = MOD_NUM_KEYPAD | '9';
     else if (event->keyval == GDK_KP_Page_Down || event->keyval == GDK_KP_3)
-        keyval = CURSOR_DOWN_RIGHT;
+        keyval = MOD_NUM_KEYPAD | '3';
+    else if (event->keyval == GDK_KP_Insert || event->keyval == GDK_KP_0)
+        keyval = MOD_NUM_KEYPAD | '0';
+    else if (event->keyval == GDK_KP_Begin || event->keyval == GDK_KP_5)
+        keyval = MOD_NUM_KEYPAD | '5';
+    else if (event->string[0] && !event->string[1])
+        keyval = (unsigned char)event->string[0];
     else
         keyval = -1;
 
@@ -454,7 +474,7 @@ static gint configure_area(GtkWidget *widget,
     gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->w, fe->h);
     gdk_gc_unref(gc);
 
-    midend_redraw(fe->me);
+    midend_force_redraw(fe->me);
 
     return TRUE;
 }
@@ -517,7 +537,7 @@ static int win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
     return FALSE;
 }
 
-void error_box(GtkWidget *parent, char *msg)
+void message_box(GtkWidget *parent, char *title, char *msg, int centre)
 {
     GtkWidget *window, *hbox, *text, *ok;
 
@@ -530,7 +550,7 @@ void error_box(GtkWidget *parent, char *msg)
                        hbox, FALSE, FALSE, 20);
     gtk_widget_show(text);
     gtk_widget_show(hbox);
-    gtk_window_set_title(GTK_WINDOW(window), "Error");
+    gtk_window_set_title(GTK_WINDOW(window), title);
     gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
     ok = gtk_button_new_with_label("OK");
     gtk_box_pack_end(GTK_BOX(GTK_DIALOG(window)->action_area),
@@ -546,11 +566,16 @@ void error_box(GtkWidget *parent, char *msg)
                       GTK_SIGNAL_FUNC(win_key_press), ok);
     gtk_window_set_modal(GTK_WINDOW(window), TRUE);
     gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent));
-    //set_transient_window_pos(parent, window);
+    /* set_transient_window_pos(parent, window); */
     gtk_widget_show(window);
     gtk_main();
 }
 
+void error_box(GtkWidget *parent, char *msg)
+{
+    message_box(parent, "Error", msg, FALSE);
+}
+
 static void config_ok_button_clicked(GtkButton *button, gpointer data)
 {
     frontend *fe = (frontend *)data;
@@ -773,7 +798,7 @@ static int get_config(frontend *fe, int which)
     gtk_window_set_modal(GTK_WINDOW(fe->cfgbox), TRUE);
     gtk_window_set_transient_for(GTK_WINDOW(fe->cfgbox),
                                 GTK_WINDOW(fe->window));
-    //set_transient_window_pos(fe->window, fe->cfgbox);
+    /* set_transient_window_pos(fe->window, fe->cfgbox); */
     gtk_widget_show(fe->cfgbox);
     gtk_main();
 
@@ -811,6 +836,8 @@ int paste_initialised = FALSE;
 
 void init_paste()
 {
+    unsigned char empty[] = { 0 };
+
     if (paste_initialised)
        return;
 
@@ -824,21 +851,21 @@ void init_paste()
      * ICCCM, we must do this before we start using cut buffers.
      */
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0);
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0);
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0);
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0);
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0);
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0);
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0);
     XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
-                   XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, "", 0);
+                   XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0);
 }
 
 /* Store data in a cut-buffer. */
@@ -913,6 +940,24 @@ static void menu_copy_event(GtkMenuItem *menuitem, gpointer data)
     }
 }
 
+static void menu_solve_event(GtkMenuItem *menuitem, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+    char *msg;
+
+    msg = midend_solve(fe->me);
+
+    if (msg)
+       error_box(fe->window, msg);
+}
+
+static void menu_restart_event(GtkMenuItem *menuitem, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+
+    midend_restart_game(fe->me);
+}
+
 static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
 {
     frontend *fe = (frontend *)data;
@@ -930,6 +975,21 @@ static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
     fe->h = y;
 }
 
+static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+    char titlebuf[256];
+    char textbuf[1024];
+
+    sprintf(titlebuf, "About %.200s", thegame.name);
+    sprintf(textbuf,
+           "%.200s\n\n"
+           "from Simon Tatham's Portable Puzzle Collection\n\n"
+           "%.500s", thegame.name, ver);
+
+    message_box(fe->window, titlebuf, textbuf, TRUE);
+}
+
 static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont,
                                          char *text, int key)
 {
@@ -959,9 +1019,13 @@ static frontend *new_window(char *game_id, char **error)
 
     fe = snew(frontend);
 
+    fe->timer_active = FALSE;
+    fe->timer_id = -1;
+
     fe->me = midend_new(fe, &thegame);
+
     if (game_id) {
-        *error = midend_game_id(fe->me, game_id, FALSE);
+        *error = midend_game_id(fe->me, game_id);
         if (*error) {
             midend_free(fe->me);
             sfree(fe);
@@ -993,10 +1057,23 @@ static frontend *new_window(char *game_id, char **error)
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
 
     add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n');
-    add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Restart", 'r');
+
+    menuitem = gtk_menu_item_new_with_label("Restart");
+    gtk_container_add(GTK_CONTAINER(menu), menuitem);
+    gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                      GTK_SIGNAL_FUNC(menu_restart_event), fe);
+    gtk_widget_show(menuitem);
 
     menuitem = gtk_menu_item_new_with_label("Specific...");
     gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
+                       GINT_TO_POINTER(CFG_DESC));
+    gtk_container_add(GTK_CONTAINER(menu), menuitem);
+    gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                      GTK_SIGNAL_FUNC(menu_config_event), fe);
+    gtk_widget_show(menuitem);
+
+    menuitem = gtk_menu_item_new_with_label("Random Seed...");
+    gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
                        GINT_TO_POINTER(CFG_SEED));
     gtk_container_add(GTK_CONTAINER(menu), menuitem);
     gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
@@ -1050,9 +1127,30 @@ static frontend *new_window(char *game_id, char **error)
                           GTK_SIGNAL_FUNC(menu_copy_event), fe);
        gtk_widget_show(menuitem);
     }
+    if (thegame.can_solve) {
+       add_menu_separator(GTK_CONTAINER(menu));
+       menuitem = gtk_menu_item_new_with_label("Solve");
+       gtk_container_add(GTK_CONTAINER(menu), menuitem);
+       gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                          GTK_SIGNAL_FUNC(menu_solve_event), fe);
+       gtk_widget_show(menuitem);
+    }
     add_menu_separator(GTK_CONTAINER(menu));
     add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
 
+    menuitem = gtk_menu_item_new_with_label("Help");
+    gtk_container_add(GTK_CONTAINER(menubar), menuitem);
+    gtk_widget_show(menuitem);
+
+    menu = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
+
+    menuitem = gtk_menu_item_new_with_label("About");
+    gtk_container_add(GTK_CONTAINER(menu), menuitem);
+    gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                      GTK_SIGNAL_FUNC(menu_about_event), fe);
+    gtk_widget_show(menuitem);
+
     {
         int i, ncolours;
         float *colours;
@@ -1114,7 +1212,7 @@ static frontend *new_window(char *game_id, char **error)
     fe->fonts = NULL;
     fe->nfonts = fe->fontsize = 0;
 
-    fe->timer_active = FALSE;
+    fe->laststatus = NULL;
 
     fe->paste_data = NULL;
     fe->paste_data_len = 0;
@@ -1156,6 +1254,12 @@ int main(int argc, char **argv)
     char *pname = argv[0];
     char *error;
 
+    if (argc > 1 && !strcmp(argv[1], "--version")) {
+       printf("%s, from Simon Tatham's Portable Puzzle Collection\n%s\n",
+              thegame.name, ver);
+       return 0;
+    }
+
     /*
      * Special standalone mode for generating puzzle IDs on the
      * command line. Useful for generating puzzles to be printed
@@ -1179,34 +1283,45 @@ int main(int argc, char **argv)
      */
     if (argc > 1 && !strcmp(argv[1], "--generate")) {
        int n = 1;
-       char *params = NULL;
+       char *params = NULL, *seed = NULL;
        game_params *par;
        random_state *rs;
        char *parstr;
 
-       {
-           void *seed;
-           int seedlen;
-           get_random_seed(&seed, &seedlen);
-           rs = random_init(seed, seedlen);
-       }
-
        if (argc > 2)
            n = atoi(argv[2]);
        if (argc > 3)
            params = argv[3];
 
-       if (params)
-           par = thegame.decode_params(params);
-       else
-           par = thegame.default_params();
-       parstr = thegame.encode_params(par);
+        par = thegame.default_params();
+       if (params) {
+            if ( (seed = strchr(params, '#')) != NULL )
+                *seed++ = '\0';
+           thegame.decode_params(par, params);
+        }
+        if ((error = thegame.validate_params(par)) != NULL) {
+           fprintf(stderr, "%s: %s\n", pname, error);
+            return 1;
+        }
+       parstr = thegame.encode_params(par, FALSE);
+
+       {
+           void *seeddata;
+           int seedlen;
+            if (seed) {
+                seeddata = seed;
+                seedlen = strlen(seed);
+            } else {
+                get_random_seed(&seeddata, &seedlen);
+            }
+           rs = random_init(seeddata, seedlen);
+       }
 
        while (n-- > 0) {
            game_aux_info *aux = NULL;
-           char *seed = thegame.new_seed(par, rs, &aux);
-           printf("%s:%s\n", parstr, seed);
-           sfree(seed);
+           char *desc = thegame.new_desc(par, rs, &aux, FALSE);
+           printf("%s:%s\n", parstr, desc);
+           sfree(desc);
            if (aux)
                thegame.free_aux_info(aux);
        }