Make peg removal accessible from the keyboard.
[sgt/puzzles] / gtk.c
diff --git a/gtk.c b/gtk.c
index 5f016ad..40ee308 100644 (file)
--- a/gtk.c
+++ b/gtk.c
 #define USE_PANGO
 #endif
 
+#ifdef DEBUGGING
+static FILE *debug_fp = NULL;
+
+void dputs(char *buf)
+{
+    if (!debug_fp) {
+        debug_fp = fopen("debug.log", "w");
+    }
+
+    fputs(buf, stderr);
+
+    if (debug_fp) {
+        fputs(buf, debug_fp);
+        fflush(debug_fp);
+    }
+}
+
+void debug_printf(char *fmt, ...)
+{
+    char buf[4096];
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsprintf(buf, fmt, ap);
+    dputs(buf);
+    va_end(ap);
+}
+#endif
+
 /* ----------------------------------------------------------------------
  * Error reporting functions used elsewhere.
  */
@@ -89,6 +118,7 @@ struct frontend {
     char *laststatus;
     int pw, ph;                        /* pixmap size (w, h are area size) */
     int ox, oy;                        /* offset of pixmap in drawing area */
+    char *filesel_name;
 };
 
 void get_random_seed(void **randseed, int *randseedsize)
@@ -334,6 +364,66 @@ void draw_polygon(frontend *fe, int *coords, int npoints,
     sfree(points);
 }
 
+void draw_circle(frontend *fe, int cx, int cy, int radius,
+                 int fill, int colour)
+{
+    gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
+    gdk_draw_arc(fe->pixmap, fe->gc, fill,
+                 cx - radius, cy - radius,
+                 2 * radius, 2 * radius, 0, 360 * 64);
+}
+
+struct blitter {
+    GdkPixmap *pixmap;
+    int w, h, x, y;
+};
+
+blitter *blitter_new(int w, int h)
+{
+    /*
+     * We can't create the pixmap right now, because fe->window
+     * might not yet exist. So we just cache w and h and create it
+     * during the firs call to blitter_save.
+     */
+    blitter *bl = snew(blitter);
+    bl->pixmap = NULL;
+    bl->w = w;
+    bl->h = h;
+    return bl;
+}
+
+void blitter_free(blitter *bl)
+{
+    if (bl->pixmap)
+        gdk_pixmap_unref(bl->pixmap);
+    sfree(bl);
+}
+
+void blitter_save(frontend *fe, blitter *bl, int x, int y)
+{
+    if (!bl->pixmap)
+        bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1);
+    bl->x = x;
+    bl->y = y;
+    gdk_draw_pixmap(bl->pixmap,
+                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
+                    fe->pixmap,
+                    x, y, 0, 0, bl->w, bl->h);
+}
+
+void blitter_load(frontend *fe, blitter *bl, int x, int y)
+{
+    assert(bl->pixmap);
+    if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
+        x = bl->x;
+        y = bl->y;
+    }
+    gdk_draw_pixmap(fe->pixmap,
+                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
+                    bl->pixmap,
+                    0, 0, x, y, bl->w, bl->h);
+}
+
 void draw_update(frontend *fe, int x, int y, int w, int h)
 {
     if (fe->bbox_l > x  ) fe->bbox_l = x  ;
@@ -361,6 +451,7 @@ static void destroy(GtkWidget *widget, gpointer data)
 {
     frontend *fe = (frontend *)data;
     deactivate_timer(fe);
+    midend_free(fe->me);
     gtk_main_quit();
 }
 
@@ -909,7 +1000,11 @@ static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
     fe->w = x;
     fe->h = y;
     gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
-    gtk_window_resize(GTK_WINDOW(fe->window), 1, 1);
+    {
+        GtkRequisition req;
+        gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
+        gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
+    }
 }
 
 GdkAtom compound_text_atom, utf8_string_atom;
@@ -1021,6 +1116,123 @@ static void menu_copy_event(GtkMenuItem *menuitem, gpointer data)
     }
 }
 
+static void filesel_ok(GtkButton *button, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+
+    gpointer filesel = gtk_object_get_data(GTK_OBJECT(button), "user-data");
+
+    const char *name =
+        gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
+
+    fe->filesel_name = dupstr(name);
+}
+
+static char *file_selector(frontend *fe, char *title, int save)
+{
+    GtkWidget *filesel =
+        gtk_file_selection_new(title);
+
+    fe->filesel_name = NULL;
+
+    gtk_window_set_modal(GTK_WINDOW(filesel), TRUE);
+    gtk_object_set_data
+        (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data",
+         (gpointer)filesel);
+    gtk_signal_connect
+        (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
+         GTK_SIGNAL_FUNC(filesel_ok), fe);
+    gtk_signal_connect_object
+        (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked",
+         GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);
+    gtk_signal_connect_object
+        (GTK_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked",
+         GTK_SIGNAL_FUNC(gtk_widget_destroy), (gpointer)filesel);
+    gtk_signal_connect(GTK_OBJECT(filesel), "destroy",
+                       GTK_SIGNAL_FUNC(window_destroy), NULL);
+    gtk_widget_show(filesel);
+    gtk_window_set_transient_for(GTK_WINDOW(filesel), GTK_WINDOW(fe->window));
+    gtk_main();
+
+    return fe->filesel_name;
+}
+
+static void savefile_write(void *wctx, void *buf, int len)
+{
+    FILE *fp = (FILE *)wctx;
+    fwrite(buf, 1, len, fp);
+}
+
+static int savefile_read(void *wctx, void *buf, int len)
+{
+    FILE *fp = (FILE *)wctx;
+    int ret;
+
+    ret = fread(buf, 1, len, fp);
+    return (ret == len);
+}
+
+static void menu_save_event(GtkMenuItem *menuitem, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+    char *name;
+
+    name = file_selector(fe, "Enter name of game file to save", TRUE);
+
+    if (name) {
+        FILE *fp = fopen(name, "w");
+        sfree(name);
+
+        if (!fp) {
+            error_box(fe->window, "Unable to open save file");
+            return;
+        }
+
+        midend_serialise(fe->me, savefile_write, fp);
+
+        fclose(fp);
+    }
+}
+
+static void menu_load_event(GtkMenuItem *menuitem, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+    char *name, *err;
+    int x, y;
+
+    name = file_selector(fe, "Enter name of saved game file to load", FALSE);
+
+    if (name) {
+        FILE *fp = fopen(name, "r");
+        sfree(name);
+
+        if (!fp) {
+            error_box(fe->window, "Unable to open saved game file");
+            return;
+        }
+
+        err = midend_deserialise(fe->me, savefile_read, fp);
+
+        fclose(fp);
+
+        if (err) {
+            error_box(fe->window, err);
+            return;
+        }
+
+        get_size(fe, &x, &y);
+        fe->w = x;
+        fe->h = y;
+        gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
+        {
+            GtkRequisition req;
+            gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
+            gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
+        }
+
+    }
+}
+
 static void menu_solve_event(GtkMenuItem *menuitem, gpointer data)
 {
     frontend *fe = (frontend *)data;
@@ -1054,7 +1266,11 @@ static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
     fe->w = x;
     fe->h = y;
     gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
-    gtk_window_resize(GTK_WINDOW(fe->window), 1, 1);
+    {
+        GtkRequisition req;
+        gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
+        gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
+    }
 }
 
 static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
@@ -1198,6 +1414,19 @@ static frontend *new_window(char *game_id, char **error)
        }
     }
 
+    if (getenv("PUZZLES_EXPERIMENTAL_SAVE") != NULL) {
+        add_menu_separator(GTK_CONTAINER(menu));
+        menuitem = gtk_menu_item_new_with_label("Load");
+        gtk_container_add(GTK_CONTAINER(menu), menuitem);
+        gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                           GTK_SIGNAL_FUNC(menu_load_event), fe);
+        gtk_widget_show(menuitem);
+        menuitem = gtk_menu_item_new_with_label("Save");
+        gtk_container_add(GTK_CONTAINER(menu), menuitem);
+        gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                           GTK_SIGNAL_FUNC(menu_save_event), fe);
+        gtk_widget_show(menuitem);
+    }
     add_menu_separator(GTK_CONTAINER(menu));
     add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Undo", 'u');
     add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Redo", '\x12');
@@ -1403,12 +1632,11 @@ int main(int argc, char **argv)
        }
 
        while (n-- > 0) {
-           game_aux_info *aux = NULL;
+           char *aux = NULL;
            char *desc = thegame.new_desc(par, rs, &aux, FALSE);
            printf("%s:%s\n", parstr, desc);
            sfree(desc);
-           if (aux)
-               thegame.free_aux_info(aux);
+            sfree(aux);
        }
 
        return 0;