+static void win_end_doc(void *handle)
+{
+ frontend *fe = (frontend *)handle;
+
+ assert(fe->drawstatus != DRAWING);
+
+ /*
+ * Free all the fonts created since we began printing.
+ */
+ while (fe->nfonts > fe->fontstart) {
+ fe->nfonts--;
+ DeleteObject(fe->fonts[fe->nfonts].font);
+ }
+ fe->fontstart = 0;
+
+ /*
+ * The MSDN web site sample code doesn't bother to call EndDoc
+ * if an error occurs half way through printing. I expect doing
+ * so would cause the erroneous document to actually be
+ * printed, or something equally undesirable.
+ */
+ if (fe->drawstatus == NOTHING)
+ return;
+
+ if (EndDoc(fe->hdc) <= 0) {
+ char *e = geterrstr();
+ MessageBox(fe->hwnd, e, "Error finishing printing",
+ MB_ICONERROR | MB_OK);
+ sfree(e);
+ fe->drawstatus = NOTHING;
+ }
+}
+
+char *win_text_fallback(void *handle, const char *const *strings, int nstrings)
+{
+ /*
+ * We assume Windows can cope with any UTF-8 likely to be
+ * emitted by a puzzle.
+ */
+ return dupstr(strings[0]);
+}
+
+const struct drawing_api win_drawing = {
+ win_draw_text,
+ win_draw_rect,
+ win_draw_line,
+ win_draw_polygon,
+ win_draw_circle,
+ win_draw_update,
+ win_clip,
+ win_unclip,
+ win_start_draw,
+ win_end_draw,
+ win_status_bar,
+ win_blitter_new,
+ win_blitter_free,
+ win_blitter_save,
+ win_blitter_load,
+ win_begin_doc,
+ win_begin_page,
+ win_begin_puzzle,
+ win_end_puzzle,
+ win_end_page,
+ win_end_doc,
+ win_line_width,
+ win_line_dotted,
+ win_text_fallback,
+};
+
+void print(frontend *fe)
+{
+#ifndef _WIN32_WCE
+ PRINTDLG pd;
+ char doctitle[256];
+ document *doc;
+ midend *nme = NULL; /* non-interactive midend for bulk puzzle generation */
+ int i;
+ char *err = NULL;
+
+ /*
+ * Create our document structure and fill it up with puzzles.
+ */
+ doc = document_new(fe->printw, fe->printh, fe->printscale / 100.0F);
+ for (i = 0; i < fe->printcount; i++) {
+ if (i == 0 && fe->printcurr) {
+ err = midend_print_puzzle(fe->me, doc, fe->printsolns);
+ } else {
+ if (!nme) {
+ game_params *params;
+
+ nme = midend_new(NULL, fe->game, NULL, NULL);
+
+ /*
+ * Set the non-interactive mid-end to have the same
+ * parameters as the standard one.
+ */
+ params = midend_get_params(fe->me);
+ midend_set_params(nme, params);
+ fe->game->free_params(params);
+ }
+
+ midend_new_game(nme);
+ err = midend_print_puzzle(nme, doc, fe->printsolns);
+ }
+ if (err)
+ break;
+ }
+ if (nme)
+ midend_free(nme);
+
+ if (err) {
+ MessageBox(fe->hwnd, err, "Error preparing puzzles for printing",
+ MB_ICONERROR | MB_OK);
+ document_free(doc);
+ return;
+ }
+
+ memset(&pd, 0, sizeof(pd));
+ pd.lStructSize = sizeof(pd);
+ pd.hwndOwner = fe->hwnd;
+ pd.hDevMode = NULL;
+ pd.hDevNames = NULL;
+ pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC |
+ PD_NOPAGENUMS | PD_NOSELECTION;
+ pd.nCopies = 1;
+ pd.nFromPage = pd.nToPage = 0xFFFF;
+ pd.nMinPage = pd.nMaxPage = 1;
+
+ if (!PrintDlg(&pd)) {
+ document_free(doc);
+ return;
+ }
+
+ /*
+ * Now pd.hDC is a device context for the printer.
+ */
+
+ /*
+ * FIXME: IWBNI we put up an Abort box here.
+ */
+
+ memset(&fe->di, 0, sizeof(fe->di));
+ fe->di.cbSize = sizeof(fe->di);
+ sprintf(doctitle, "Printed puzzles from %s (from Simon Tatham's"
+ " Portable Puzzle Collection)", fe->game->name);
+ fe->di.lpszDocName = doctitle;
+ fe->di.lpszOutput = NULL;
+ fe->di.lpszDatatype = NULL;
+ fe->di.fwType = 0;
+
+ fe->drawstatus = PRINTING;
+ fe->hdc = pd.hDC;
+
+ fe->dr = drawing_new(&win_drawing, NULL, fe);
+ document_print(doc, fe->dr);
+ drawing_free(fe->dr);
+ fe->dr = NULL;
+
+ fe->drawstatus = NOTHING;
+
+ DeleteDC(pd.hDC);
+ document_free(doc);
+#endif
+}
+
+void deactivate_timer(frontend *fe)
+{
+ if (!fe)
+ return; /* for non-interactive midend */
+ if (fe->hwnd) KillTimer(fe->hwnd, fe->timer);
+ fe->timer = 0;
+}
+
+void activate_timer(frontend *fe)
+{
+ if (!fe)
+ return; /* for non-interactive midend */
+ if (!fe->timer) {
+ fe->timer = SetTimer(fe->hwnd, 1, 20, NULL);
+ fe->timer_last_tickcount = GetTickCount();
+ }
+}
+
+void write_clip(HWND hwnd, char *data)
+{
+ HGLOBAL clipdata;
+ int len, i, j;
+ char *data2;
+ void *lock;
+
+ /*
+ * Windows expects CRLF in the clipboard, so we must convert
+ * any \n that has come out of the puzzle backend.
+ */
+ len = 0;
+ for (i = 0; data[i]; i++) {
+ if (data[i] == '\n')
+ len++;
+ len++;
+ }
+ data2 = snewn(len+1, char);
+ j = 0;
+ for (i = 0; data[i]; i++) {
+ if (data[i] == '\n')
+ data2[j++] = '\r';
+ data2[j++] = data[i];
+ }
+ assert(j == len);
+ data2[j] = '\0';
+
+ clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
+ if (!clipdata) {
+ sfree(data2);
+ return;
+ }
+ lock = GlobalLock(clipdata);
+ if (!lock) {
+ GlobalFree(clipdata);
+ sfree(data2);
+ return;
+ }
+ memcpy(lock, data2, len);
+ ((unsigned char *) lock)[len] = 0;
+ GlobalUnlock(clipdata);
+
+ if (OpenClipboard(hwnd)) {
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, clipdata);
+ CloseClipboard();
+ } else
+ GlobalFree(clipdata);
+
+ sfree(data2);
+}
+
+/*
+ * Set up Help and see if we can find a help file.
+ */
+static void init_help(void)
+{
+#ifndef _WIN32_WCE
+ char b[2048], *p, *q, *r;
+ FILE *fp;
+
+ /*
+ * Find the executable file path, so we can look alongside
+ * it for help files. Trim the filename off the end.
+ */
+ GetModuleFileName(NULL, b, sizeof(b) - 1);
+ r = b;
+ p = strrchr(b, '\\');
+ if (p && p >= r) r = p+1;
+ q = strrchr(b, ':');
+ if (q && q >= r) r = q+1;
+
+#ifndef NO_HTMLHELP
+ /*
+ * Try HTML Help first.
+ */
+ strcpy(r, CHM_FILE_NAME);
+ if ( (fp = fopen(b, "r")) != NULL) {
+ fclose(fp);
+
+ /*
+ * We have a .CHM. See if we can use it.
+ */
+ hh_dll = LoadLibrary("hhctrl.ocx");
+ if (hh_dll) {
+ htmlhelp = (htmlhelp_t)GetProcAddress(hh_dll, "HtmlHelpA");
+ if (!htmlhelp)
+ FreeLibrary(hh_dll);
+ }
+ if (htmlhelp) {
+ help_path = dupstr(b);
+ help_type = CHM;
+ return;
+ }
+ }
+#endif /* NO_HTMLHELP */
+
+ /*
+ * Now try old-style .HLP.
+ */
+ strcpy(r, HELP_FILE_NAME);
+ if ( (fp = fopen(b, "r")) != NULL) {
+ fclose(fp);
+
+ help_path = dupstr(b);
+ help_type = HLP;
+
+ /*
+ * See if there's a .CNT file alongside it.
+ */
+ strcpy(r, HELP_CNT_NAME);
+ if ( (fp = fopen(b, "r")) != NULL) {
+ fclose(fp);
+ help_has_contents = TRUE;
+ } else
+ help_has_contents = FALSE;
+
+ return;
+ }
+
+ help_type = NONE; /* didn't find any */
+#endif
+}
+
+#ifndef _WIN32_WCE
+
+/*
+ * Start Help.
+ */
+static void start_help(frontend *fe, const char *topic)
+{
+ char *str = NULL;
+ int cmd;
+
+ switch (help_type) {
+ case HLP:
+ assert(help_path);
+ if (topic) {
+ str = snewn(10+strlen(topic), char);
+ sprintf(str, "JI(`',`%s')", topic);
+ cmd = HELP_COMMAND;
+ } else if (help_has_contents) {
+ cmd = HELP_FINDER;
+ } else {
+ cmd = HELP_CONTENTS;
+ }
+ WinHelp(fe->hwnd, help_path, cmd, (DWORD)str);
+ fe->help_running = TRUE;
+ break;
+ case CHM:
+#ifndef NO_HTMLHELP
+ assert(help_path);
+ assert(htmlhelp);
+ if (topic) {
+ str = snewn(20 + strlen(topic) + strlen(help_path), char);
+ sprintf(str, "%s::/%s.html>main", help_path, topic);
+ } else {
+ str = dupstr(help_path);
+ }
+ htmlhelp(fe->hwnd, str, HH_DISPLAY_TOPIC, 0);
+ fe->help_running = TRUE;
+ break;
+#endif /* NO_HTMLHELP */
+ case NONE:
+ assert(!"This shouldn't happen");
+ break;
+ }
+
+ sfree(str);
+}
+
+/*
+ * Stop Help on window cleanup.
+ */
+static void stop_help(frontend *fe)
+{
+ if (fe->help_running) {
+ switch (help_type) {
+ case HLP:
+ WinHelp(fe->hwnd, help_path, HELP_QUIT, 0);
+ break;
+ case CHM:
+#ifndef NO_HTMLHELP
+ assert(htmlhelp);
+ htmlhelp(NULL, NULL, HH_CLOSE_ALL, 0);
+ break;
+#endif /* NO_HTMLHELP */
+ case NONE:
+ assert(!"This shouldn't happen");
+ break;
+ }
+ fe->help_running = FALSE;
+ }
+}
+
+#endif
+
+/*
+ * Terminate Help on process exit.
+ */
+static void cleanup_help(void)
+{
+ /* Nothing to do currently.
+ * (If we were running HTML Help single-threaded, this is where we'd
+ * call HH_UNINITIALIZE.) */
+}
+
+static int get_statusbar_height(frontend *fe)
+{
+ int sy;
+ if (fe->statusbar) {
+ RECT sr;
+ GetWindowRect(fe->statusbar, &sr);
+ sy = sr.bottom - sr.top;
+ } else {
+ sy = 0;
+ }
+ return sy;
+}
+
+static void adjust_statusbar(frontend *fe, RECT *r)
+{
+ int sy;
+
+ if (!fe->statusbar) return;
+
+ sy = get_statusbar_height(fe);
+#ifndef _WIN32_WCE
+ SetWindowPos(fe->statusbar, NULL, 0, r->bottom-r->top-sy, r->right-r->left,
+ sy, SWP_NOZORDER);
+#endif
+}
+
+static void get_menu_size(HWND wh, RECT *r)
+{
+ HMENU bar = GetMenu(wh);
+ RECT rect;
+ int i;
+
+ SetRect(r, 0, 0, 0, 0);
+ for (i = 0; i < GetMenuItemCount(bar); i++) {
+ GetMenuItemRect(wh, bar, i, &rect);
+ UnionRect(r, r, &rect);
+ }
+}
+
+/*
+ * Given a proposed new puzzle size (cx,cy), work out the actual
+ * puzzle size that would be (px,py) and the window size including
+ * furniture (wx,wy).
+ */
+
+static int check_window_resize(frontend *fe, int cx, int cy,
+ int *px, int *py,
+ int *wx, int *wy)
+{
+ RECT r;
+ int x, y, sy = get_statusbar_height(fe), changed = 0;
+
+ /* disallow making window thinner than menu bar */
+ x = max(cx, fe->xmin);
+ y = max(cy - sy, fe->ymin);
+
+ /*
+ * See if we actually got the window size we wanted, and adjust
+ * the puzzle size if not.
+ */
+ midend_size(fe->me, &x, &y, TRUE);
+ if (x != cx || y != cy) {
+ /*
+ * Resize the window, now we know what size we _really_
+ * want it to be.
+ */
+ r.left = r.top = 0;
+ r.right = x;
+ r.bottom = y + sy;
+ AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
+ *wx = r.right - r.left;
+ *wy = r.bottom - r.top;
+ changed = 1;
+ }
+
+ *px = x;
+ *py = y;
+
+ fe->puzz_scale =
+ (float)midend_tilesize(fe->me) / (float)fe->game->preferred_tilesize;
+
+ return changed;
+}
+
+/*
+ * Given the current window size, make sure it's sane for the
+ * current puzzle and resize if necessary.
+ */
+
+static void check_window_size(frontend *fe, int *px, int *py)
+{
+ RECT r;
+ int wx, wy, cx, cy;
+
+ GetClientRect(fe->hwnd, &r);
+ cx = r.right - r.left;
+ cy = r.bottom - r.top;
+
+ if (check_window_resize(fe, cx, cy, px, py, &wx, &wy)) {
+#ifdef _WIN32_WCE
+ SetWindowPos(fe->hwnd, NULL, 0, 0, wx, wy,
+ SWP_NOMOVE | SWP_NOZORDER);
+#endif
+ ;
+ }
+
+ GetClientRect(fe->hwnd, &r);
+ adjust_statusbar(fe, &r);
+}
+
+static void get_max_puzzle_size(frontend *fe, int *x, int *y)
+{
+ RECT r, sr;
+
+ if (SystemParametersInfo(SPI_GETWORKAREA, 0, &sr, FALSE)) {
+ *x = sr.right - sr.left;
+ *y = sr.bottom - sr.top;
+ r.left = 100;
+ r.right = 200;
+ r.top = 100;
+ r.bottom = 200;
+ AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
+ *x -= r.right - r.left - 100;
+ *y -= r.bottom - r.top - 100;
+ } else {
+ *x = *y = INT_MAX;
+ }
+
+ if (fe->statusbar != NULL) {
+ GetWindowRect(fe->statusbar, &sr);
+ *y -= sr.bottom - sr.top;
+ }
+}
+
+#ifdef _WIN32_WCE
+/* Toolbar buttons on the numeric pad */
+static TBBUTTON tbNumpadButtons[] =
+{
+ {0, IDM_KEYEMUL + '1', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {1, IDM_KEYEMUL + '2', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {2, IDM_KEYEMUL + '3', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {3, IDM_KEYEMUL + '4', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {4, IDM_KEYEMUL + '5', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {5, IDM_KEYEMUL + '6', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {6, IDM_KEYEMUL + '7', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {7, IDM_KEYEMUL + '8', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {8, IDM_KEYEMUL + '9', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
+ {9, IDM_KEYEMUL + ' ', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}
+};
+#endif
+
+/*
+ * Allocate a new frontend structure and create its main window.
+ */
+static frontend *frontend_new(HINSTANCE inst)
+{
+ frontend *fe;
+ const char *nogame = "Puzzles (no game selected)";
+
+ fe = snew(frontend);
+
+ fe->inst = inst;
+
+ fe->game = NULL;
+ fe->me = NULL;
+
+ fe->timer = 0;
+ fe->hwnd = NULL;
+
+ fe->help_running = FALSE;
+
+ fe->drawstatus = NOTHING;
+ fe->dr = NULL;
+ fe->fontstart = 0;
+
+ fe->fonts = NULL;
+ fe->nfonts = fe->fontsize = 0;
+
+ fe->colours = NULL;
+ fe->brushes = NULL;
+ fe->pens = NULL;
+
+ fe->puzz_scale = 1.0;
+
+ #ifdef _WIN32_WCE
+ MultiByteToWideChar (CP_ACP, 0, nogame, -1, wGameName, 256);
+ fe->hwnd = CreateWindowEx(0, wClassName, wGameName,
+ WS_VISIBLE,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, inst, NULL);
+
+ {
+ SHMENUBARINFO mbi;
+ RECT rc, rcBar, rcTB, rcClient;
+
+ memset (&mbi, 0, sizeof(SHMENUBARINFO));
+ mbi.cbSize = sizeof(SHMENUBARINFO);
+ mbi.hwndParent = fe->hwnd;
+ mbi.nToolBarId = IDR_MENUBAR1;
+ mbi.hInstRes = inst;
+
+ SHCreateMenuBar(&mbi);
+
+ GetWindowRect(fe->hwnd, &rc);
+ GetWindowRect(mbi.hwndMB, &rcBar);
+ rc.bottom -= rcBar.bottom - rcBar.top;
+ MoveWindow(fe->hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
+
+ fe->numpad = NULL;
+ }
+#else
+ fe->hwnd = CreateWindowEx(0, CLASSNAME, nogame,
+ WS_OVERLAPPEDWINDOW &~
+ (WS_MAXIMIZEBOX),
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, inst, NULL);
+ if (!fe->hwnd) {
+ DWORD lerr = GetLastError();
+ printf("no window: 0x%x\n", lerr);
+ }
+#endif
+
+ fe->gamemenu = NULL;
+ fe->presets = NULL;
+
+ fe->statusbar = NULL;
+ fe->bitmap = NULL;
+
+ SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe);
+
+ return fe;
+}
+
+/*
+ * Populate a frontend structure with a (new) game and midend structure, and
+ * create any window furniture that it needs.
+ *
+ * Previously-allocated memory and window furniture will be freed by this function.
+ */
+static int new_game(frontend *fe, const game *game, char *game_id, char **error)
+{
+ int x, y;
+ RECT r;
+
+ fe->game = game;
+
+ if (fe->me) midend_free(fe->me);
+ fe->me = midend_new(fe, fe->game, &win_drawing, fe);
+
+ if (game_id) {
+ *error = midend_game_id(fe->me, game_id);
+ if (*error) {
+ midend_free(fe->me);
+ sfree(fe);
+ return -1;
+ }
+ }
+
+ midend_new_game(fe->me);
+
+ {
+ int i, ncolours;
+ float *colours;
+
+ colours = midend_colours(fe->me, &ncolours);
+
+ if (fe->colours) sfree(fe->colours);
+ if (fe->brushes) sfree(fe->brushes);
+ if (fe->pens) sfree(fe->pens);
+
+ fe->colours = snewn(ncolours, COLORREF);
+ fe->brushes = snewn(ncolours, HBRUSH);
+ fe->pens = snewn(ncolours, HPEN);
+
+ for (i = 0; i < ncolours; i++) {
+ fe->colours[i] = RGB(255 * colours[i*3+0],
+ 255 * colours[i*3+1],
+ 255 * colours[i*3+2]);
+ fe->brushes[i] = CreateSolidBrush(fe->colours[i]);
+ fe->pens[i] = CreatePen(PS_SOLID, 1, fe->colours[i]);
+ }
+ sfree(colours);
+ }
+
+ if (fe->statusbar)
+ DestroyWindow(fe->statusbar);
+ if (midend_wants_statusbar(fe->me)) {
+ fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"),
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, 0, 0, /* status bar does these */
+ NULL, NULL, fe->inst, NULL);
+ } else
+ fe->statusbar = NULL;
+
+ get_max_puzzle_size(fe, &x, &y);
+ midend_size(fe->me, &x, &y, FALSE);
+
+ r.left = r.top = 0;
+ r.right = x;
+ r.bottom = y;
+ AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
+
+#ifdef _WIN32_WCE
+ if (fe->numpad)
+ DestroyWindow(fe->numpad);
+ if (fe->game->flags & REQUIRE_NUMPAD)
+ {
+ fe->numpad = CreateToolbarEx (fe->hwnd,
+ WS_VISIBLE | WS_CHILD | CCS_NOPARENTALIGN | TBSTYLE_FLAT,
+ 0, 10, fe->inst, IDR_PADTOOLBAR,
+ tbNumpadButtons, sizeof (tbNumpadButtons) / sizeof (TBBUTTON),
+ 0, 0, 14, 15, sizeof (TBBUTTON));
+ GetWindowRect(fe->numpad, &rcTB);
+ GetClientRect(fe->hwnd, &rcClient);
+ MoveWindow(fe->numpad,
+ 0,
+ rcClient.bottom - (rcTB.bottom - rcTB.top) - 1,
+ rcClient.right,
+ rcTB.bottom - rcTB.top,
+ FALSE);
+ SendMessage(fe->numpad, TB_SETINDENT, (rcClient.right - (10 * 21)) / 2, 0);
+ }
+ else {
+ fe->numpad = NULL;
+ }
+ MultiByteToWideChar (CP_ACP, 0, fe->game->name, -1, wGameName, 256);
+ SetWindowText(fe->hwnd, wGameName);
+#else
+ SetWindowText(fe->hwnd, fe->game->name);
+#endif
+
+ if (fe->statusbar)
+ DestroyWindow(fe->statusbar);
+ if (midend_wants_statusbar(fe->me)) {
+ RECT sr;
+ fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"),
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, 0, 0, /* status bar does these */
+ fe->hwnd, NULL, fe->inst, NULL);
+#ifdef _WIN32_WCE
+ /* Flat status bar looks better on the Pocket PC */
+ SendMessage(fe->statusbar, SB_SIMPLE, (WPARAM) TRUE, 0);
+ SendMessage(fe->statusbar, SB_SETTEXT,
+ (WPARAM) 255 | SBT_NOBORDERS,
+ (LPARAM) L"");
+#endif
+
+ /*
+ * Now resize the window to take account of the status bar.
+ */
+ GetWindowRect(fe->statusbar, &sr);
+ GetWindowRect(fe->hwnd, &r);
+#ifndef _WIN32_WCE
+ SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left,
+ r.bottom - r.top + sr.bottom - sr.top,
+ SWP_NOMOVE | SWP_NOZORDER);
+#endif
+ } else {
+ fe->statusbar = NULL;
+ }
+
+ {
+ HMENU oldmenu = GetMenu(fe->hwnd);
+
+#ifndef _WIN32_WCE
+ HMENU bar = CreateMenu();
+ HMENU menu = CreateMenu();
+ RECT menusize;
+
+ AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "&Game");
+#else
+ HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME);
+ DeleteMenu(menu, 0, MF_BYPOSITION);
+#endif
+ fe->gamemenu = menu;
+ AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("&New"));
+ AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("&Restart"));
+#ifndef _WIN32_WCE
+ /* ...here I run out of sensible accelerator characters. */
+ AppendMenu(menu, MF_ENABLED, IDM_DESC, TEXT("Speci&fic..."));
+ AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Rando&m Seed..."));
+#endif
+
+ if (fe->presets)
+ sfree(fe->presets);
+ if ((fe->npresets = midend_num_presets(fe->me)) > 0 ||
+ fe->game->can_configure) {
+ int i;
+#ifndef _WIN32_WCE
+ HMENU sub = CreateMenu();
+
+ AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "&Type");
+#else
+ HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE);
+ DeleteMenu(sub, 0, MF_BYPOSITION);
+#endif
+ fe->presets = snewn(fe->npresets, game_params *);
+
+ for (i = 0; i < fe->npresets; i++) {
+ char *name;
+#ifdef _WIN32_WCE
+ TCHAR wName[255];
+#endif
+
+ midend_fetch_preset(fe->me, i, &name, &fe->presets[i]);
+
+ /*
+ * FIXME: we ought to go through and do something
+ * with ampersands here.