X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/8b7938e71a910d0017b2ab013513e5d13716f1ef..4e1f57ba31665f1d681ecd245bdc74b8c6942cf4:/windows.c diff --git a/windows.c b/windows.c index 80a929d..ae93983 100644 --- a/windows.c +++ b/windows.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "puzzles.h" @@ -18,12 +19,21 @@ #define IDM_RESTART 0x0020 #define IDM_UNDO 0x0030 #define IDM_REDO 0x0040 -#define IDM_QUIT 0x0050 -#define IDM_CONFIG 0x0060 -#define IDM_SEED 0x0070 +#define IDM_COPY 0x0050 +#define IDM_SOLVE 0x0060 +#define IDM_QUIT 0x0070 +#define IDM_CONFIG 0x0080 +#define IDM_DESC 0x0090 +#define IDM_SEED 0x00A0 +#define IDM_HELPC 0x00B0 +#define IDM_GAMEHELP 0x00C0 +#define IDM_ABOUT 0x00D0 #define IDM_PRESETS 0x0100 -#ifdef DEBUG +#define HELP_FILE_NAME "puzzles.hlp" +#define HELP_CNT_NAME "puzzles.cnt" + +#ifdef DEBUGGING static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; static int debug_got_console = 0; @@ -59,13 +69,6 @@ void debug_printf(char *fmt, ...) dputs(buf); va_end(ap); } - -#define debug(x) (debug_printf x) - -#else - -#define debug(x) - #endif struct font { @@ -78,6 +81,12 @@ struct cfg_aux { int ctlid; }; +struct blitter { + HBITMAP bitmap; + frontend *fe; + int x, y, w, h; +}; + struct frontend { midend_data *me; HWND hwnd, statusbar, cfgbox; @@ -96,8 +105,11 @@ struct frontend { int nfonts, fontsize; config_item *cfg; struct cfg_aux *cfgaux; - int cfg_which, cfg_done; + int cfg_which, dlg_done; HFONT cfgfont; + char *help_path; + int help_has_contents; + char *laststatus; }; void fatal(char *fmt, ...) @@ -114,9 +126,100 @@ void fatal(char *fmt, ...) exit(1); } +void get_random_seed(void **randseed, int *randseedsize) +{ + time_t *tp = snew(time_t); + time(tp); + *randseed = (void *)tp; + *randseedsize = sizeof(time_t); +} + void status_bar(frontend *fe, char *text) { - SetWindowText(fe->statusbar, text); + char *rewritten = midend_rewrite_statusbar(fe->me, text); + if (!fe->laststatus || strcmp(rewritten, fe->laststatus)) { + SetWindowText(fe->statusbar, rewritten); + sfree(fe->laststatus); + fe->laststatus = rewritten; + } else { + sfree(rewritten); + } +} + +blitter *blitter_new(int w, int h) +{ + blitter *bl = snew(blitter); + + memset(bl, 0, sizeof(blitter)); + bl->w = w; + bl->h = h; + bl->bitmap = 0; + + return bl; +} + +void blitter_free(blitter *bl) +{ + if (bl->bitmap) DeleteObject(bl->bitmap); + sfree(bl); +} + +static void blitter_mkbitmap(frontend *fe, blitter *bl) +{ + HDC hdc = GetDC(fe->hwnd); + bl->bitmap = CreateCompatibleBitmap(hdc, bl->w, bl->h); + ReleaseDC(fe->hwnd, hdc); +} + +/* BitBlt(dstDC, dstX, dstY, dstW, dstH, srcDC, srcX, srcY, dType) */ + +void blitter_save(frontend *fe, blitter *bl, int x, int y) +{ + HDC hdc_win, hdc_blit; + HBITMAP prev_blit; + + if (!bl->bitmap) blitter_mkbitmap(fe, bl); + + bl->x = x; bl->y = y; + + hdc_win = GetDC(fe->hwnd); + hdc_blit = CreateCompatibleDC(hdc_win); + if (!hdc_blit) fatal("hdc_blit failed: 0x%x", GetLastError()); + + prev_blit = SelectObject(hdc_blit, bl->bitmap); + if (prev_blit == NULL || prev_blit == HGDI_ERROR) + fatal("SelectObject for hdc_main failed: 0x%x", GetLastError()); + + if (!BitBlt(hdc_blit, 0, 0, bl->w, bl->h, + fe->hdc_bm, x, y, SRCCOPY)) + fatal("BitBlt failed: 0x%x", GetLastError()); + + SelectObject(hdc_blit, prev_blit); + DeleteDC(hdc_blit); + ReleaseDC(fe->hwnd, hdc_win); +} + +void blitter_load(frontend *fe, blitter *bl, int x, int y) +{ + HDC hdc_win, hdc_blit; + HBITMAP prev_blit; + + assert(bl->bitmap); /* we should always have saved before loading */ + + if (x == BLITTER_FROMSAVED) x = bl->x; + if (y == BLITTER_FROMSAVED) y = bl->y; + + hdc_win = GetDC(fe->hwnd); + hdc_blit = CreateCompatibleDC(hdc_win); + + prev_blit = SelectObject(hdc_blit, bl->bitmap); + + BitBlt(fe->hdc_bm, x, y, bl->w, bl->h, + hdc_blit, 0, 0, SRCCOPY); + + SelectObject(hdc_blit, prev_blit); + DeleteDC(hdc_blit); + ReleaseDC(fe->hwnd, hdc_win); } void frontend_default_colour(frontend *fe, float *output) @@ -130,18 +233,12 @@ void frontend_default_colour(frontend *fe, float *output) void clip(frontend *fe, int x, int y, int w, int h) { - if (!fe->clip) { - fe->clip = CreateRectRgn(0, 0, 1, 1); - GetClipRgn(fe->hdc_bm, fe->clip); - } - IntersectClipRect(fe->hdc_bm, x, y, x+w, y+h); } void unclip(frontend *fe) { - assert(fe->clip); - SelectClipRgn(fe->hdc_bm, fe->clip); + SelectClipRgn(fe->hdc_bm, NULL); } void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, @@ -167,11 +264,7 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, fe->fonts[i].type = fonttype; fe->fonts[i].size = fontsize; - /* - * FIXME: Really I should make at least _some_ effort to - * pick the correct font. - */ - fe->fonts[i].font = CreateFont(-fontsize, 0, 0, 0, 0, + fe->fonts[i].font = CreateFont(-fontsize, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, @@ -203,6 +296,7 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, x -= size.cx; } SetBkMode(fe->hdc_bm, TRANSPARENT); + SetTextColor(fe->hdc_bm, fe->colours[colour]); TextOut(fe->hdc_bm, x, y, text, strlen(text)); SelectObject(fe->hdc_bm, oldfont); } @@ -236,6 +330,25 @@ void draw_line(frontend *fe, int x1, int y1, int x2, int y2, int colour) SelectObject(fe->hdc_bm, oldpen); } +void draw_circle(frontend *fe, int cx, int cy, int radius, + int fill, int colour) +{ + if (fill) { + HBRUSH oldbrush = SelectObject(fe->hdc_bm, fe->brushes[colour]); + HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); + Ellipse(fe->hdc_bm, cx - radius, cy - radius, + cx + radius + 1, cy + radius + 1); + SelectObject(fe->hdc_bm, oldbrush); + SelectObject(fe->hdc_bm, oldpen); + } else { + HPEN oldpen = SelectObject(fe->hdc_bm, fe->pens[colour]); + Arc(fe->hdc_bm, cx - radius, cy - radius, + cx + radius + 1, cy + radius + 1, + cx - radius, cy, cx - radius, cy); + SelectObject(fe->hdc_bm, oldpen); + } +} + void draw_polygon(frontend *fe, int *coords, int npoints, int fill, int colour) { @@ -310,21 +423,141 @@ void activate_timer(frontend *fe) } } +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) + return; + lock = GlobalLock(clipdata); + if (!lock) + 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); +} + +/* + * See if we can find a help file. + */ +static void find_help_file(frontend *fe) +{ + char b[2048], *p, *q, *r; + FILE *fp; + if (!fe->help_path) { + 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; + strcpy(r, HELP_FILE_NAME); + if ( (fp = fopen(b, "r")) != NULL) { + fe->help_path = dupstr(b); + fclose(fp); + } else + fe->help_path = NULL; + strcpy(r, HELP_CNT_NAME); + if ( (fp = fopen(b, "r")) != NULL) { + fe->help_has_contents = TRUE; + fclose(fp); + } else + fe->help_has_contents = FALSE; + } +} + +static void check_window_size(frontend *fe, int *px, int *py) +{ + RECT r; + int x, y, sy; + + if (fe->statusbar) { + RECT sr; + GetWindowRect(fe->statusbar, &sr); + sy = sr.bottom - sr.top; + } else { + sy = 0; + } + + /* + * See if we actually got the window size we wanted, and adjust + * the puzzle size if not. + */ + GetClientRect(fe->hwnd, &r); + x = r.right - r.left; + y = r.bottom - r.top - sy; + midend_size(fe->me, &x, &y, FALSE); + if (x != r.right - r.left || y != r.bottom - r.top) { + /* + * 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, WS_OVERLAPPEDWINDOW &~ + (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), + TRUE, 0); + SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top, + SWP_NOMOVE | SWP_NOZORDER); + } + + if (fe->statusbar) { + GetClientRect(fe->hwnd, &r); + SetWindowPos(fe->statusbar, NULL, 0, r.bottom-r.top-sy, r.right-r.left, + sy, SWP_NOZORDER); + } + + *px = x; + *py = y; +} + static frontend *new_window(HINSTANCE inst, char *game_id, char **error) { frontend *fe; int x, y; - RECT r, sr; + RECT r; HDC hdc; - time_t t; fe = snew(frontend); - time(&t); - fe->me = midend_new(fe, &t, sizeof(t)); + fe->me = midend_new(fe, &thegame); if (game_id) { - *error = midend_game_id(fe->me, game_id, FALSE); + *error = midend_game_id(fe->me, game_id); if (*error) { midend_free(fe->me); sfree(fe); @@ -332,15 +565,19 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) } } + fe->help_path = NULL; + find_help_file(fe); + fe->inst = inst; midend_new_game(fe->me); - midend_size(fe->me, &x, &y); fe->timer = 0; fe->fonts = NULL; fe->nfonts = fe->fontsize = 0; + fe->laststatus = NULL; + { int i, ncolours; float *colours; @@ -360,6 +597,9 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) } } + x = y = INT_MAX; /* find puzzle's preferred size */ + midend_size(fe->me, &x, &y, FALSE); + r.left = r.top = 0; r.right = x; r.bottom = y; @@ -367,13 +607,31 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) (WS_THICKFRAME | WS_MAXIMIZEBOX | WS_OVERLAPPED), TRUE, 0); - fe->hwnd = CreateWindowEx(0, game_name, game_name, + fe->hwnd = CreateWindowEx(0, thegame.name, thegame.name, WS_OVERLAPPEDWINDOW &~ (WS_THICKFRAME | WS_MAXIMIZEBOX), CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, NULL, NULL, inst, NULL); + if (midend_wants_statusbar(fe->me)) { + RECT sr; + fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, "ooh", + WS_CHILD | WS_VISIBLE, + 0, 0, 0, 0, /* status bar does these */ + fe->hwnd, NULL, inst, NULL); + /* + * Now resize the window to take account of the status bar. + */ + GetWindowRect(fe->statusbar, &sr); + GetWindowRect(fe->hwnd, &r); + SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, + r.bottom - r.top + sr.bottom - sr.top, + SWP_NOMOVE | SWP_NOZORDER); + } else { + fe->statusbar = NULL; + } + { HMENU bar = CreateMenu(); HMENU menu = CreateMenu(); @@ -381,10 +639,11 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Game"); AppendMenu(menu, MF_ENABLED, IDM_NEW, "New"); AppendMenu(menu, MF_ENABLED, IDM_RESTART, "Restart"); - AppendMenu(menu, MF_ENABLED, IDM_SEED, "Specific..."); + AppendMenu(menu, MF_ENABLED, IDM_DESC, "Specific..."); + AppendMenu(menu, MF_ENABLED, IDM_SEED, "Random Seed..."); if ((fe->npresets = midend_num_presets(fe->me)) > 0 || - game_can_configure) { + thegame.can_configure) { HMENU sub = CreateMenu(); int i; @@ -405,7 +664,7 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name); } - if (game_can_configure) { + if (thegame.can_configure) { AppendMenu(sub, MF_ENABLED, IDM_CONFIG, "Custom..."); } } @@ -413,25 +672,35 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo"); AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo"); + if (thegame.can_format_as_text) { + AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_COPY, "Copy"); + } + if (thegame.can_solve) { + AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_SOLVE, "Solve"); + } AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit"); + menu = CreateMenu(); + AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Help"); + AppendMenu(menu, MF_ENABLED, IDM_ABOUT, "About"); + if (fe->help_path) { + AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_HELPC, "Contents"); + if (thegame.winhelp_topic) { + char *item; + assert(thegame.name); + item = snewn(9+strlen(thegame.name), char); /*ick*/ + sprintf(item, "Help on %s", thegame.name); + AppendMenu(menu, MF_ENABLED, IDM_GAMEHELP, item); + sfree(item); + } + } SetMenu(fe->hwnd, bar); } - if (midend_wants_statusbar(fe->me)) { - fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, "ooh", - WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, /* status bar does these */ - fe->hwnd, NULL, inst, NULL); - GetWindowRect(fe->statusbar, &sr); - SetWindowPos(fe->hwnd, NULL, 0, 0, - r.right - r.left, r.bottom - r.top + sr.bottom - sr.top, - SWP_NOMOVE | SWP_NOZORDER); - SetWindowPos(fe->statusbar, NULL, 0, y, x, sr.bottom - sr.top, - SWP_NOZORDER); - } else { - fe->statusbar = NULL; - } + check_window_size(fe, &x, &y); hdc = GetDC(fe->hwnd); fe->bitmap = CreateCompatibleBitmap(hdc, x, y); @@ -447,6 +716,30 @@ static frontend *new_window(HINSTANCE inst, char *game_id, char **error) return fe; } +static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + frontend *fe = (frontend *)GetWindowLong(hwnd, GWL_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + return 0; + + case WM_COMMAND: + if ((HIWORD(wParam) == BN_CLICKED || + HIWORD(wParam) == BN_DOUBLECLICKED) && + LOWORD(wParam) == IDOK) + fe->dlg_done = 1; + return 0; + + case WM_CLOSE: + fe->dlg_done = 1; + return 0; + } + + return 0; +} + static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -472,10 +765,10 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, MessageBox(hwnd, err, "Validation error", MB_ICONERROR | MB_OK); } else { - fe->cfg_done = 2; + fe->dlg_done = 2; } } else { - fe->cfg_done = 1; + fe->dlg_done = 1; } return 0; } @@ -509,7 +802,7 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, return 0; case WM_CLOSE: - fe->cfg_done = 1; + fe->dlg_done = 1; return 0; } @@ -518,7 +811,7 @@ static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg, HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2, char *wclass, int wstyle, - int exstyle, char *wtext, int wid) + int exstyle, const char *wtext, int wid) { HWND ret; ret = CreateWindowEx(exstyle, wclass, wtext, @@ -528,6 +821,158 @@ HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2, return ret; } +static void about(frontend *fe) +{ + int i; + WNDCLASS wc; + MSG msg; + TEXTMETRIC tm; + HDC hdc; + HFONT oldfont; + SIZE size; + int gm, id; + int winwidth, winheight, y; + int height, width, maxwid; + const char *strings[16]; + int lengths[16]; + int nstrings = 0; + char titlebuf[512]; + + sprintf(titlebuf, "About %.250s", thegame.name); + + strings[nstrings++] = thegame.name; + strings[nstrings++] = "from Simon Tatham's Portable Puzzle Collection"; + strings[nstrings++] = ver; + + wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW; + wc.lpfnWndProc = DefDlgProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA + 8; + wc.hInstance = fe->inst; + wc.hIcon = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1); + wc.lpszMenuName = NULL; + wc.lpszClassName = "GameAboutBox"; + RegisterClass(&wc); + + hdc = GetDC(fe->hwnd); + SetMapMode(hdc, MM_TEXT); + + fe->dlg_done = FALSE; + + fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), + 0, 0, 0, 0, + FALSE, FALSE, FALSE, DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FF_SWISS, + "MS Shell Dlg"); + + oldfont = SelectObject(hdc, fe->cfgfont); + if (GetTextMetrics(hdc, &tm)) { + height = tm.tmAscent + tm.tmDescent; + width = tm.tmAveCharWidth; + } else { + height = width = 30; + } + + /* + * Figure out the layout of the About box by measuring the + * length of each piece of text. + */ + maxwid = 0; + winheight = height/2; + + for (i = 0; i < nstrings; i++) { + if (GetTextExtentPoint32(hdc, strings[i], strlen(strings[i]), &size)) + lengths[i] = size.cx; + else + lengths[i] = 0; /* *shrug* */ + if (maxwid < lengths[i]) + maxwid = lengths[i]; + winheight += height * 3 / 2 + (height / 2); + } + + winheight += height + height * 7 / 4; /* OK button */ + winwidth = maxwid + 4*width; + + SelectObject(hdc, oldfont); + ReleaseDC(fe->hwnd, hdc); + + /* + * Create the dialog, now that we know its size. + */ + { + RECT r, r2; + + r.left = r.top = 0; + r.right = winwidth; + r.bottom = winheight; + + AdjustWindowRectEx(&r, (WS_OVERLAPPEDWINDOW /*| + DS_MODALFRAME | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU*/) &~ + (WS_MAXIMIZEBOX | WS_OVERLAPPED), + FALSE, 0); + + /* + * Centre the dialog on its parent window. + */ + r.right -= r.left; + r.bottom -= r.top; + GetWindowRect(fe->hwnd, &r2); + r.left = (r2.left + r2.right - r.right) / 2; + r.top = (r2.top + r2.bottom - r.bottom) / 2; + r.right += r.left; + r.bottom += r.top; + + fe->cfgbox = CreateWindowEx(0, wc.lpszClassName, titlebuf, + DS_MODALFRAME | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU, + r.left, r.top, + r.right-r.left, r.bottom-r.top, + fe->hwnd, NULL, fe->inst, NULL); + } + + SendMessage(fe->cfgbox, WM_SETFONT, (WPARAM)fe->cfgfont, FALSE); + + SetWindowLong(fe->cfgbox, GWL_USERDATA, (LONG)fe); + SetWindowLong(fe->cfgbox, DWL_DLGPROC, (LONG)AboutDlgProc); + + id = 1000; + y = height/2; + for (i = 0; i < nstrings; i++) { + int border = width*2 + (maxwid - lengths[i]) / 2; + mkctrl(fe, border, border+lengths[i], y+height*1/8, y+height*9/8, + "Static", 0, 0, strings[i], id++); + y += height*3/2; + + assert(y < winheight); + y += height/2; + } + + y += height/2; /* extra space before OK */ + mkctrl(fe, width*2, maxwid+width*2, y, y+height*7/4, "BUTTON", + BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP | BS_DEFPUSHBUTTON, 0, + "OK", IDOK); + + SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0); + + EnableWindow(fe->hwnd, FALSE); + ShowWindow(fe->cfgbox, SW_NORMAL); + while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { + if (!IsDialogMessage(fe->cfgbox, &msg)) + DispatchMessage(&msg); + if (fe->dlg_done) + break; + } + EnableWindow(fe->hwnd, TRUE); + SetForegroundWindow(fe->hwnd); + DestroyWindow(fe->cfgbox); + DeleteObject(fe->cfgfont); +} + static int get_config(frontend *fe, int which) { config_item *i; @@ -559,7 +1004,7 @@ static int get_config(frontend *fe, int which) hdc = GetDC(fe->hwnd); SetMapMode(hdc, MM_TEXT); - fe->cfg_done = FALSE; + fe->dlg_done = FALSE; fe->cfgfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, 0, @@ -623,6 +1068,7 @@ static int get_config(frontend *fe, int which) col2r = col1l+2*height+maxcheckbox; winwidth = col2r + 2*width; + SelectObject(hdc, oldfont); ReleaseDC(fe->hwnd, hdc); /* @@ -754,7 +1200,7 @@ static int get_config(frontend *fe, int which) while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { if (!IsDialogMessage(fe->cfgbox, &msg)) DispatchMessage(&msg); - if (fe->cfg_done) + if (fe->dlg_done) break; } EnableWindow(fe->hwnd, TRUE); @@ -765,7 +1211,7 @@ static int get_config(frontend *fe, int which) free_cfg(fe->cfg); sfree(fe->cfgaux); - return (fe->cfg_done == 2); + return (fe->dlg_done == 2); } static void new_game_type(frontend *fe) @@ -775,7 +1221,8 @@ static void new_game_type(frontend *fe) int x, y; midend_new_game(fe->me); - midend_size(fe->me, &x, &y); + x = y = INT_MAX; + midend_size(fe->me, &x, &y, FALSE); r.left = r.top = 0; r.right = x; @@ -794,6 +1241,9 @@ static void new_game_type(frontend *fe) r.right - r.left, r.bottom - r.top + sr.bottom - sr.top, SWP_NOMOVE | SWP_NOZORDER); + + check_window_size(fe, &x, &y); + if (fe->statusbar != NULL) SetWindowPos(fe->statusbar, NULL, 0, y, x, sr.bottom - sr.top, SWP_NOZORDER); @@ -807,6 +1257,19 @@ static void new_game_type(frontend *fe) midend_redraw(fe->me); } +static int is_alt_pressed(void) +{ + BYTE keystate[256]; + int r = GetKeyboardState(keystate); + if (!r) + return FALSE; + if (keystate[VK_MENU] & 0x80) + return TRUE; + if (keystate[VK_RMENU] & 0x80) + return TRUE; + return FALSE; +} + static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -823,8 +1286,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, PostQuitMessage(0); break; case IDM_RESTART: - if (!midend_process_key(fe->me, 0, 0, 'r')) - PostQuitMessage(0); + midend_restart_game(fe->me); break; case IDM_UNDO: if (!midend_process_key(fe->me, 0, 0, 'u')) @@ -834,6 +1296,24 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (!midend_process_key(fe->me, 0, 0, '\x12')) PostQuitMessage(0); break; + case IDM_COPY: + { + char *text = midend_text_format(fe->me); + if (text) + write_clip(hwnd, text); + else + MessageBeep(MB_ICONWARNING); + sfree(text); + } + break; + case IDM_SOLVE: + { + char *msg = midend_solve(fe->me); + if (msg) + MessageBox(hwnd, msg, "Unable to solve", + MB_ICONERROR | MB_OK); + } + break; case IDM_QUIT: if (!midend_process_key(fe->me, 0, 0, 'q')) PostQuitMessage(0); @@ -846,6 +1326,28 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (get_config(fe, CFG_SEED)) new_game_type(fe); break; + case IDM_DESC: + if (get_config(fe, CFG_DESC)) + new_game_type(fe); + break; + case IDM_ABOUT: + about(fe); + break; + case IDM_HELPC: + assert(fe->help_path); + WinHelp(hwnd, fe->help_path, + fe->help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0); + break; + case IDM_GAMEHELP: + assert(fe->help_path); + assert(thegame.winhelp_topic); + { + char *cmd = snewn(10+strlen(thegame.winhelp_topic), char); + sprintf(cmd, "JI(`',`%s')", thegame.winhelp_topic); + WinHelp(hwnd, fe->help_path, HELP_COMMAND, (DWORD)cmd); + sfree(cmd); + } + break; default: { int p = ((wParam &~ 0xF) - IDM_PRESETS) / 0x10; @@ -885,38 +1387,70 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_KEYDOWN: { int key = -1; + BYTE keystate[256]; + int r = GetKeyboardState(keystate); + int shift = (r && (keystate[VK_SHIFT] & 0x80)) ? MOD_SHFT : 0; + int ctrl = (r && (keystate[VK_CONTROL] & 0x80)) ? MOD_CTRL : 0; switch (wParam) { - case VK_LEFT: key = CURSOR_LEFT; break; - case VK_RIGHT: key = CURSOR_RIGHT; break; - case VK_UP: key = CURSOR_UP; break; - case VK_DOWN: key = CURSOR_DOWN; break; + case VK_LEFT: + if (!(lParam & 0x01000000)) + key = MOD_NUM_KEYPAD | '4'; + else + key = shift | ctrl | CURSOR_LEFT; + break; + case VK_RIGHT: + if (!(lParam & 0x01000000)) + key = MOD_NUM_KEYPAD | '6'; + else + key = shift | ctrl | CURSOR_RIGHT; + break; + case VK_UP: + if (!(lParam & 0x01000000)) + key = MOD_NUM_KEYPAD | '8'; + else + key = shift | ctrl | CURSOR_UP; + break; + case VK_DOWN: + if (!(lParam & 0x01000000)) + key = MOD_NUM_KEYPAD | '2'; + else + key = shift | ctrl | CURSOR_DOWN; + break; /* * Diagonal keys on the numeric keypad. */ case VK_PRIOR: - if (!(lParam & 0x01000000)) key = CURSOR_UP_RIGHT; + if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '9'; break; case VK_NEXT: - if (!(lParam & 0x01000000)) key = CURSOR_DOWN_RIGHT; + if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '3'; break; case VK_HOME: - if (!(lParam & 0x01000000)) key = CURSOR_UP_LEFT; + if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '7'; break; case VK_END: - if (!(lParam & 0x01000000)) key = CURSOR_DOWN_LEFT; + if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '1'; + break; + case VK_INSERT: + if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '0'; + break; + case VK_CLEAR: + if (!(lParam & 0x01000000)) key = MOD_NUM_KEYPAD | '5'; break; /* * Numeric keypad keys with Num Lock on. */ - case VK_NUMPAD4: key = CURSOR_LEFT; break; - case VK_NUMPAD6: key = CURSOR_RIGHT; break; - case VK_NUMPAD8: key = CURSOR_UP; break; - case VK_NUMPAD2: key = CURSOR_DOWN; break; - case VK_NUMPAD9: key = CURSOR_UP_RIGHT; break; - case VK_NUMPAD3: key = CURSOR_DOWN_RIGHT; break; - case VK_NUMPAD7: key = CURSOR_UP_LEFT; break; - case VK_NUMPAD1: key = CURSOR_DOWN_LEFT; break; + case VK_NUMPAD4: key = MOD_NUM_KEYPAD | '4'; break; + case VK_NUMPAD6: key = MOD_NUM_KEYPAD | '6'; break; + case VK_NUMPAD8: key = MOD_NUM_KEYPAD | '8'; break; + case VK_NUMPAD2: key = MOD_NUM_KEYPAD | '2'; break; + case VK_NUMPAD5: key = MOD_NUM_KEYPAD | '5'; break; + case VK_NUMPAD9: key = MOD_NUM_KEYPAD | '9'; break; + case VK_NUMPAD3: key = MOD_NUM_KEYPAD | '3'; break; + case VK_NUMPAD7: key = MOD_NUM_KEYPAD | '7'; break; + case VK_NUMPAD1: key = MOD_NUM_KEYPAD | '1'; break; + case VK_NUMPAD0: key = MOD_NUM_KEYPAD | '0'; break; } if (key != -1) { @@ -945,13 +1479,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, */ if (message == WM_MBUTTONDOWN || (wParam & MK_SHIFT)) button = MIDDLE_BUTTON; - else if (message == WM_LBUTTONDOWN) - button = LEFT_BUTTON; - else + else if (message == WM_RBUTTONDOWN || is_alt_pressed()) button = RIGHT_BUTTON; + else + button = LEFT_BUTTON; - if (!midend_process_key(fe->me, LOWORD(lParam), - HIWORD(lParam), button)) + if (!midend_process_key(fe->me, (signed short)LOWORD(lParam), + (signed short)HIWORD(lParam), button)) PostQuitMessage(0); SetCapture(hwnd); @@ -970,13 +1504,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, */ if (message == WM_MBUTTONUP || (wParam & MK_SHIFT)) button = MIDDLE_RELEASE; - else if (message == WM_LBUTTONUP) - button = LEFT_RELEASE; - else + else if (message == WM_RBUTTONUP || is_alt_pressed()) button = RIGHT_RELEASE; + else + button = LEFT_RELEASE; - if (!midend_process_key(fe->me, LOWORD(lParam), - HIWORD(lParam), button)) + if (!midend_process_key(fe->me, (signed short)LOWORD(lParam), + (signed short)HIWORD(lParam), button)) PostQuitMessage(0); ReleaseCapture(); @@ -988,13 +1522,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, if (wParam & (MK_MBUTTON | MK_SHIFT)) button = MIDDLE_DRAG; - else if (wParam & MK_LBUTTON) - button = LEFT_DRAG; - else + else if (wParam & MK_RBUTTON || is_alt_pressed()) button = RIGHT_DRAG; + else + button = LEFT_DRAG; - if (!midend_process_key(fe->me, LOWORD(lParam), - HIWORD(lParam), button)) + if (!midend_process_key(fe->me, (signed short)LOWORD(lParam), + (signed short)HIWORD(lParam), button)) PostQuitMessage(0); } break; @@ -1034,7 +1568,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; - wndclass.lpszClassName = game_name; + wndclass.lpszClassName = thegame.name; RegisterClass(&wndclass); } @@ -1044,7 +1578,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (!new_window(inst, *cmdline ? cmdline : NULL, &error)) { char buf[128]; - sprintf(buf, "%.100s Error", game_name); + sprintf(buf, "%.100s Error", thegame.name); MessageBox(NULL, error, buf, MB_OK|MB_ICONERROR); return 1; }