+ i->ival = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),
+ "user-data"));
+}
+
+static int get_config(frontend *fe, int which)
+{
+ GtkWidget *w, *table, *cancel;
+ char *title;
+ config_item *i;
+ int y;
+
+ fe->cfg = midend_get_config(fe->me, which, &title);
+ fe->cfg_which = which;
+ fe->cfgret = FALSE;
+
+ fe->cfgbox = gtk_dialog_new();
+ gtk_window_set_title(GTK_WINDOW(fe->cfgbox), title);
+ sfree(title);
+
+ w = gtk_button_new_with_label("OK");
+ gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area),
+ w, FALSE, FALSE, 0);
+ gtk_widget_show(w);
+ GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
+ gtk_window_set_default(GTK_WINDOW(fe->cfgbox), w);
+ gtk_signal_connect(GTK_OBJECT(w), "clicked",
+ GTK_SIGNAL_FUNC(config_ok_button_clicked), fe);
+
+ w = gtk_button_new_with_label("Cancel");
+ gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->action_area),
+ w, FALSE, FALSE, 0);
+ gtk_widget_show(w);
+ gtk_signal_connect(GTK_OBJECT(w), "clicked",
+ GTK_SIGNAL_FUNC(config_cancel_button_clicked), fe);
+ cancel = w;
+
+ table = gtk_table_new(1, 2, FALSE);
+ y = 0;
+ gtk_box_pack_end(GTK_BOX(GTK_DIALOG(fe->cfgbox)->vbox),
+ table, FALSE, FALSE, 0);
+ gtk_widget_show(table);
+
+ for (i = fe->cfg; i->type != C_END; i++) {
+ gtk_table_resize(GTK_TABLE(table), y+1, 2);
+
+ switch (i->type) {
+ case C_STRING:
+ /*
+ * Edit box with a label beside it.
+ */
+
+ w = gtk_label_new(i->name);
+ gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
+ gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ 3, 3);
+ gtk_widget_show(w);
+
+ w = gtk_entry_new();
+ gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ 3, 3);
+ gtk_entry_set_text(GTK_ENTRY(w), i->sval);
+ gtk_signal_connect(GTK_OBJECT(w), "changed",
+ GTK_SIGNAL_FUNC(editbox_changed), i);
+ gtk_signal_connect(GTK_OBJECT(w), "key_press_event",
+ GTK_SIGNAL_FUNC(editbox_key), NULL);
+ gtk_widget_show(w);
+
+ break;
+
+ case C_BOOLEAN:
+ /*
+ * Simple checkbox.
+ */
+ w = gtk_check_button_new_with_label(i->name);
+ gtk_signal_connect(GTK_OBJECT(w), "toggled",
+ GTK_SIGNAL_FUNC(button_toggled), i);
+ gtk_table_attach(GTK_TABLE(table), w, 0, 2, y, y+1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ 3, 3);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), i->ival);
+ gtk_widget_show(w);
+ break;
+
+ case C_CHOICES:
+ /*
+ * Drop-down list (GtkOptionMenu).
+ */
+
+ w = gtk_label_new(i->name);
+ gtk_misc_set_alignment(GTK_MISC(w), 0.0, 0.5);
+ gtk_table_attach(GTK_TABLE(table), w, 0, 1, y, y+1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ 3, 3);
+ gtk_widget_show(w);
+
+ w = gtk_option_menu_new();
+ gtk_table_attach(GTK_TABLE(table), w, 1, 2, y, y+1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ 3, 3);
+ gtk_widget_show(w);
+
+ {
+ int c, val;
+ char *p, *q, *name;
+ GtkWidget *menuitem;
+ GtkWidget *menu = gtk_menu_new();
+
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(w), menu);
+
+ c = *i->sval;
+ p = i->sval+1;
+ val = 0;
+
+ while (*p) {
+ q = p;
+ while (*q && *q != c)
+ q++;
+
+ name = snewn(q-p+1, char);
+ strncpy(name, p, q-p);
+ name[q-p] = '\0';
+
+ if (*q) q++; /* eat delimiter */
+
+ menuitem = gtk_menu_item_new_with_label(name);
+ gtk_container_add(GTK_CONTAINER(menu), menuitem);
+ gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
+ GINT_TO_POINTER(val));
+ gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+ GTK_SIGNAL_FUNC(droplist_sel), i);
+ gtk_widget_show(menuitem);
+
+ val++;
+
+ p = q;
+ }
+
+ gtk_option_menu_set_history(GTK_OPTION_MENU(w), i->ival);
+ }
+
+ break;
+ }
+
+ y++;
+ }
+
+ gtk_signal_connect(GTK_OBJECT(fe->cfgbox), "destroy",
+ GTK_SIGNAL_FUNC(window_destroy), NULL);
+ gtk_signal_connect(GTK_OBJECT(fe->cfgbox), "key_press_event",
+ GTK_SIGNAL_FUNC(win_key_press), cancel);
+ 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);
+ gtk_widget_show(fe->cfgbox);
+ gtk_main();
+
+ free_cfg(fe->cfg);
+
+ return fe->cfgret;
+}
+
+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);
+ midend_size(fe->me, &x, &y);
+ gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
+ fe->w = x;
+ fe->h = y;
+}
+
+static void menu_config_event(GtkMenuItem *menuitem, gpointer data)
+{
+ frontend *fe = (frontend *)data;
+ int which = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(menuitem),
+ "user-data"));
+ int x, y;
+
+ if (!get_config(fe, which))
+ return;
+
+ midend_new_game(fe->me);
+ 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(char *game_id, char **error)
+{
+ frontend *fe;
+ GtkBox *vbox;
+ GtkWidget *menubar, *menu, *menuitem;
+ int x, y, n;
+
+ fe = snew(frontend);
+
+ fe->me = midend_new(fe, &thegame);
+ if (game_id) {
+ *error = midend_game_id(fe->me, game_id, FALSE);
+ if (*error) {
+ midend_free(fe->me);
+ sfree(fe);
+ return NULL;
+ }
+ }
+ midend_new_game(fe->me);
+
+ fe->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(fe->window), thegame.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');
+
+ menuitem = gtk_menu_item_new_with_label("Specific...");
+ 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",
+ GTK_SIGNAL_FUNC(menu_config_event), fe);
+ gtk_widget_show(menuitem);
+
+ if ((n = midend_num_presets(fe->me)) > 0 || thegame.can_configure) {
+ GtkWidget *submenu;
+ int i;
+
+ menuitem = gtk_menu_item_new_with_label("Type");
+ gtk_container_add(GTK_CONTAINER(menubar), 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, ¶ms);
+
+ 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);
+ }
+
+ if (thegame.can_configure) {
+ menuitem = gtk_menu_item_new_with_label("Custom...");
+ gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
+ GPOINTER_TO_INT(CFG_SETTINGS));
+ gtk_container_add(GTK_CONTAINER(submenu), menuitem);
+ gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+ GTK_SIGNAL_FUNC(menu_config_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;
+ float *colours;
+ gboolean *success;
+
+ fe->colmap = gdk_colormap_get_system();
+ colours = midend_colours(fe->me, &ncolours);
+ fe->ncolours = ncolours;
+ fe->colours = snewn(ncolours, GdkColor);
+ for (i = 0; i < ncolours; i++) {
+ fe->colours[i].red = colours[i*3] * 0xFFFF;
+ fe->colours[i].green = colours[i*3+1] * 0xFFFF;
+ fe->colours[i].blue = colours[i*3+2] * 0xFFFF;
+ }
+ success = snewn(ncolours, gboolean);
+ gdk_colormap_alloc_colors(fe->colmap, fe->colours, ncolours,
+ FALSE, FALSE, success);
+ for (i = 0; i < ncolours; i++) {
+ if (!success[i])
+ g_error("couldn't allocate colour %d (#%02x%02x%02x)\n",
+ i, fe->colours[i].red >> 8,
+ fe->colours[i].green >> 8,
+ fe->colours[i].blue >> 8);
+ }
+ }
+
+ if (midend_wants_statusbar(fe->me)) {
+ GtkWidget *viewport;
+ GtkRequisition req;
+
+ viewport = gtk_viewport_new(NULL, NULL);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
+ fe->statusbar = gtk_statusbar_new();
+ gtk_container_add(GTK_CONTAINER(viewport), fe->statusbar);
+ gtk_widget_show(viewport);
+ gtk_box_pack_end(vbox, viewport, FALSE, FALSE, 0);
+ gtk_widget_show(fe->statusbar);
+ fe->statusctx = gtk_statusbar_get_context_id
+ (GTK_STATUSBAR(fe->statusbar), "game");
+ gtk_statusbar_push(GTK_STATUSBAR(fe->statusbar), fe->statusctx,
+ "test");
+ gtk_widget_size_request(fe->statusbar, &req);
+#if 0
+ /* For GTK 2.0, should we be using gtk_widget_set_size_request? */
+#endif
+ gtk_widget_set_usize(viewport, -1, req.height);
+ } else
+ fe->statusbar = NULL;
+
+ fe->area = gtk_drawing_area_new();
+ midend_size(fe->me, &x, &y);
+ gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y);
+ fe->w = x;
+ fe->h = y;
+
+ gtk_box_pack_end(vbox, fe->area, FALSE, FALSE, 0);
+
+ fe->pixmap = NULL;
+ fe->fonts = NULL;
+ fe->nfonts = fe->fontsize = 0;
+
+ 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",
+ GTK_SIGNAL_FUNC(key_event), fe);
+ gtk_signal_connect(GTK_OBJECT(fe->area), "button_press_event",
+ GTK_SIGNAL_FUNC(button_event), fe);
+ gtk_signal_connect(GTK_OBJECT(fe->area), "button_release_event",
+ GTK_SIGNAL_FUNC(button_event), fe);
+ gtk_signal_connect(GTK_OBJECT(fe->area), "motion_notify_event",
+ GTK_SIGNAL_FUNC(motion_event), fe);
+ gtk_signal_connect(GTK_OBJECT(fe->area), "expose_event",
+ GTK_SIGNAL_FUNC(expose_area), fe);
+ gtk_signal_connect(GTK_OBJECT(fe->window), "map_event",
+ GTK_SIGNAL_FUNC(map_window), fe);
+ gtk_signal_connect(GTK_OBJECT(fe->area), "configure_event",
+ GTK_SIGNAL_FUNC(configure_area), fe);
+
+ gtk_widget_add_events(GTK_WIDGET(fe->area),
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK);
+
+ gtk_widget_show(fe->area);
+ gtk_widget_show(fe->window);
+
+ return fe;