#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
-#if GTK_CHECK_VERSION(2,0,0) && !defined HAVE_SENSIBLE_ABSOLUTE_SIZE_FUNCTION
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
-#endif
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
#include "puzzles.h"
config_item *cfg;
int cfg_which, cfgret;
GtkWidget *cfgbox;
+ char *paste_data;
+ int paste_data_len;
};
void get_random_seed(void **randseed, int *randseedsize)
/* `Monospace' and `Sans' are meta-families guaranteed to exist */
pango_font_description_set_family(fd, fonttype == FONT_FIXED ?
"Monospace" : "Sans");
+ pango_font_description_set_weight(fd, PANGO_WEIGHT_BOLD);
/*
* I found some online Pango documentation which
* described a function called
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)
+ if (event->keyval == GDK_Up)
keyval = CURSOR_UP;
- else if (event->keyval == GDK_Down || event->keyval == GDK_KP_Down ||
- event->keyval == GDK_KP_2)
+ else if (event->keyval == GDK_KP_Up || event->keyval == GDK_KP_8)
+ keyval = MOD_NUM_KEYPAD | '8';
+ else if (event->keyval == GDK_Down)
keyval = CURSOR_DOWN;
- else if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left ||
- event->keyval == GDK_KP_4)
+ else if (event->keyval == GDK_KP_Down || event->keyval == GDK_KP_2)
+ keyval = MOD_NUM_KEYPAD | '2';
+ else if (event->keyval == GDK_Left)
keyval = CURSOR_LEFT;
- else if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right ||
- event->keyval == GDK_KP_6)
+ else if (event->keyval == GDK_KP_Left || event->keyval == GDK_KP_4)
+ keyval = MOD_NUM_KEYPAD | '4';
+ else if (event->keyval == GDK_Right)
keyval = 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;
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;
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),
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;
fe->h = y;
}
+GdkAtom compound_text_atom, utf8_string_atom;
+int paste_initialised = FALSE;
+
+void init_paste()
+{
+ if (paste_initialised)
+ return;
+
+ if (!compound_text_atom)
+ compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
+ if (!utf8_string_atom)
+ utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
+
+ /*
+ * Ensure that all the cut buffers exist - according to the
+ * 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);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, "", 0);
+ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+ XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, "", 0);
+}
+
+/* Store data in a cut-buffer. */
+void store_cutbuffer(char *ptr, int len)
+{
+ /* ICCCM says we must rotate the buffers before storing to buffer 0. */
+ XRotateBuffers(GDK_DISPLAY(), 1);
+ XStoreBytes(GDK_DISPLAY(), ptr, len);
+}
+
+void write_clip(frontend *fe, char *data)
+{
+ init_paste();
+
+ if (fe->paste_data)
+ sfree(fe->paste_data);
+
+ /*
+ * For this simple application we can safely assume that the
+ * data passed to this function is pure ASCII, which means we
+ * can return precisely the same stuff for types STRING,
+ * COMPOUND_TEXT or UTF8_STRING.
+ */
+
+ fe->paste_data = data;
+ fe->paste_data_len = strlen(data);
+
+ store_cutbuffer(fe->paste_data, fe->paste_data_len);
+
+ if (gtk_selection_owner_set(fe->area, GDK_SELECTION_PRIMARY,
+ CurrentTime)) {
+ gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
+ GDK_SELECTION_TYPE_STRING, 1);
+ gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
+ compound_text_atom, 1);
+ gtk_selection_add_target(fe->area, GDK_SELECTION_PRIMARY,
+ utf8_string_atom, 1);
+ }
+}
+
+void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
+ guint info, guint time_stamp, gpointer data)
+{
+ frontend *fe = (frontend *)data;
+ gtk_selection_data_set(seldata, seldata->target, 8,
+ fe->paste_data, fe->paste_data_len);
+}
+
+gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
+ gpointer data)
+{
+ frontend *fe = (frontend *)data;
+
+ if (fe->paste_data)
+ sfree(fe->paste_data);
+ fe->paste_data = NULL;
+ fe->paste_data_len = 0;
+ return TRUE;
+}
+
+static void menu_copy_event(GtkMenuItem *menuitem, gpointer data)
+{
+ frontend *fe = (frontend *)data;
+ char *text;
+
+ text = midend_text_format(fe->me);
+
+ if (text) {
+ write_clip(fe, text);
+ } else {
+ gdk_beep();
+ }
+}
+
+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_config_event(GtkMenuItem *menuitem, gpointer data)
{
frontend *fe = (frontend *)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)
{
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');
+ if (thegame.can_format_as_text) {
+ add_menu_separator(GTK_CONTAINER(menu));
+ menuitem = gtk_menu_item_new_with_label("Copy");
+ gtk_container_add(GTK_CONTAINER(menu), menuitem);
+ gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
+ 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;
fe->timer_active = FALSE;
+ fe->paste_data = NULL;
+ fe->paste_data_len = 0;
+
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(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), "selection_get",
+ GTK_SIGNAL_FUNC(selection_get), fe);
+ gtk_signal_connect(GTK_OBJECT(fe->area), "selection_clear_event",
+ GTK_SIGNAL_FUNC(selection_clear), 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",
parstr = thegame.encode_params(par);
while (n-- > 0) {
- char *seed = thegame.new_seed(par, rs);
+ game_aux_info *aux = NULL;
+ char *seed = thegame.new_seed(par, rs, &aux);
printf("%s:%s\n", parstr, seed);
sfree(seed);
+ if (aux)
+ thegame.free_aux_info(aux);
}
return 0;