Have each game declare a name which is used for window titles etc.
[sgt/puzzles] / gtk.c
diff --git a/gtk.c b/gtk.c
index 0d63e58..78eb8e8 100644 (file)
--- a/gtk.c
+++ b/gtk.c
@@ -1,9 +1,5 @@
 /*
  * gtk.c: GTK front end for my puzzle collection.
- * 
- * TODO:
- * 
- *  - Handle resizing, probably just by forbidding it.
  */
 
 #include <stdio.h>
@@ -56,7 +52,7 @@ struct frontend {
     midend_data *me;
     GdkGC *gc;
     int bbox_l, bbox_r, bbox_u, bbox_d;
-    int timer_active;
+    int timer_active, timer_id;
 };
 
 void frontend_default_colour(frontend *fe, float *output)
@@ -143,14 +139,26 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
 
     if (event->string[0] && !event->string[1])
         keyval = (unsigned char)event->string[0];
-    else if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
+    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)
+    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)
+    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)
+    else if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right ||
+             event->keyval == GDK_KP_6)
         keyval = CURSOR_RIGHT;
+    else if (event->keyval == GDK_KP_Home || event->keyval == GDK_KP_7)
+        keyval = CURSOR_UP_LEFT;
+    else if (event->keyval == GDK_KP_End || event->keyval == GDK_KP_1)
+        keyval = CURSOR_DOWN_LEFT;
+    else if (event->keyval == GDK_KP_Page_Up || event->keyval == GDK_KP_9)
+        keyval = CURSOR_UP_RIGHT;
+    else if (event->keyval == GDK_KP_Page_Down || event->keyval == GDK_KP_3)
+        keyval = CURSOR_DOWN_RIGHT;
     else
         keyval = -1;
 
@@ -173,10 +181,10 @@ static gint button_event(GtkWidget *widget, GdkEventButton *event,
     if (event->type != GDK_BUTTON_PRESS)
         return TRUE;
 
-    if (event->button == 1)
-       button = LEFT_BUTTON;
-    else if (event->button == 2)
+    if (event->button == 2 || (event->state & GDK_SHIFT_MASK))
        button = MIDDLE_BUTTON;
+    else if (event->button == 1)
+       button = LEFT_BUTTON;
     else if (event->button == 3)
        button = RIGHT_BUTTON;
     else
@@ -210,6 +218,9 @@ static gint configure_area(GtkWidget *widget,
     frontend *fe = (frontend *)data;
     GdkGC *gc;
 
+    if (fe->pixmap)
+        gdk_pixmap_unref(fe->pixmap);
+
     fe->pixmap = gdk_pixmap_new(widget->window, fe->w, fe->h, -1);
 
     gc = gdk_gc_new(fe->area->window);
@@ -234,19 +245,68 @@ static gint timer_func(gpointer data)
 
 void deactivate_timer(frontend *fe)
 {
+    if (fe->timer_active)
+        gtk_timeout_remove(fe->timer_id);
     fe->timer_active = FALSE;
 }
 
 void activate_timer(frontend *fe)
 {
-    gtk_timeout_add(20, timer_func, fe);
+    if (!fe->timer_active)
+        fe->timer_id = gtk_timeout_add(20, timer_func, fe);
     fe->timer_active = TRUE;
 }
 
+static void menu_key_event(GtkMenuItem *menuitem, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+    int key = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(menuitem),
+                                                  "user-data"));
+    if (!midend_process_key(fe->me, 0, 0, key))
+       gtk_widget_destroy(fe->window);
+}
+
+static void menu_preset_event(GtkMenuItem *menuitem, gpointer data)
+{
+    frontend *fe = (frontend *)data;
+    game_params *params =
+        (game_params *)gtk_object_get_data(GTK_OBJECT(menuitem), "user-data");
+    int x, y;
+
+    midend_set_params(fe->me, params);
+    midend_new_game(fe->me, NULL);
+    midend_size(fe->me, &x, &y);
+    gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
+    fe->w = x;
+    fe->h = y;
+}
+
+static GtkWidget *add_menu_item_with_key(frontend *fe, GtkContainer *cont,
+                                         char *text, int key)
+{
+    GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
+    gtk_container_add(cont, menuitem);
+    gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
+                        GINT_TO_POINTER(key));
+    gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                      GTK_SIGNAL_FUNC(menu_key_event), fe);
+    gtk_widget_show(menuitem);
+    return menuitem;
+}
+
+static void add_menu_separator(GtkContainer *cont)
+{
+    GtkWidget *menuitem = gtk_menu_item_new();
+    gtk_container_add(cont, menuitem);
+    gtk_widget_show(menuitem);
+}
+
 static frontend *new_window(void)
 {
     frontend *fe;
-    int x, y;
+    GtkBox *vbox;
+    GtkWidget *menubar, *menu, *menuitem;
+    int x, y, n;
 
     fe = snew(frontend);
 
@@ -254,6 +314,61 @@ static frontend *new_window(void)
     midend_new_game(fe->me, NULL);
 
     fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(fe->window), game_name);
+#if 0
+    gtk_window_set_resizable(GTK_WINDOW(fe->window), FALSE);
+#else
+    gtk_window_set_policy(GTK_WINDOW(fe->window), FALSE, FALSE, TRUE);
+#endif
+    vbox = GTK_BOX(gtk_vbox_new(FALSE, 0));
+    gtk_container_add(GTK_CONTAINER(fe->window), GTK_WIDGET(vbox));
+    gtk_widget_show(GTK_WIDGET(vbox));
+
+    menubar = gtk_menu_bar_new();
+    gtk_box_pack_start(vbox, menubar, FALSE, FALSE, 0);
+    gtk_widget_show(menubar);
+
+    menuitem = gtk_menu_item_new_with_label("Game");
+    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);
+
+    add_menu_item_with_key(fe, GTK_CONTAINER(menu), "New", 'n');
+    add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Restart", 'r');
+
+    if ((n = midend_num_presets(fe->me)) > 0) {
+        GtkWidget *submenu;
+        int i;
+
+        menuitem = gtk_menu_item_new_with_label("Type");
+        gtk_container_add(GTK_CONTAINER(menu), menuitem);
+        gtk_widget_show(menuitem);
+
+        submenu = gtk_menu_new();
+        gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+
+        for (i = 0; i < n; i++) {
+            char *name;
+            game_params *params;
+
+            midend_fetch_preset(fe->me, i, &name, &params);
+
+            menuitem = gtk_menu_item_new_with_label(name);
+            gtk_container_add(GTK_CONTAINER(submenu), menuitem);
+            gtk_object_set_data(GTK_OBJECT(menuitem), "user-data", params);
+            gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+                               GTK_SIGNAL_FUNC(menu_preset_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');
+    add_menu_separator(GTK_CONTAINER(menu));
+    add_menu_item_with_key(fe, GTK_CONTAINER(menu), "Exit", 'q');
 
     {
         int i, ncolours;
@@ -287,10 +402,12 @@ static frontend *new_window(void)
     fe->w = x;
     fe->h = y;
 
-    gtk_container_add(GTK_CONTAINER(fe->window), fe->area);
+    gtk_box_pack_end(vbox, fe->area, FALSE, FALSE, 0);
 
     fe->pixmap = NULL;
 
+    fe->timer_active = FALSE;
+
     gtk_signal_connect(GTK_OBJECT(fe->window), "destroy",
                       GTK_SIGNAL_FUNC(destroy), fe);
     gtk_signal_connect(GTK_OBJECT(fe->window), "key_press_event",