33
45
#define IDM_PRINT 0x0100
34
46
#define IDM_PRESETS 0x0110
48
#define IDM_KEYEMUL 0x0400
36
50
#define HELP_FILE_NAME "puzzles.hlp"
37
51
#define HELP_CNT_NAME "puzzles.cnt"
53
#define CHM_FILE_NAME "puzzles.chm"
54
#endif /* NO_HTMLHELP */
57
typedef HWND (CALLBACK *htmlhelp_t)(HWND, LPCSTR, UINT, DWORD);
58
static htmlhelp_t htmlhelp;
59
static HINSTANCE hh_dll;
60
#endif /* NO_HTMLHELP */
61
enum { NONE, HLP, CHM } help_type;
63
const char *help_topic;
64
int help_has_contents;
67
#define FILENAME_MAX (260)
71
#define HGDI_ERROR ((HANDLE)GDI_ERROR)
77
* Wrapper implementations of functions not supplied by the
81
#define SHGetSubMenu(hWndMB,ID_MENU) (HMENU)SendMessage((hWndMB), SHCMBM_GETSUBMENU, (WPARAM)0, (LPARAM)ID_MENU)
85
int MessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
90
MultiByteToWideChar (CP_ACP, 0, lpText, -1, wText, 2048);
91
MultiByteToWideChar (CP_ACP, 0, lpCaption, -1, wCaption, 2048);
93
return MessageBoxW (hWnd, wText, wCaption, uType);
96
BOOL SetDlgItemTextA(HWND hDlg, int nIDDlgItem, LPCSTR lpString)
100
MultiByteToWideChar (CP_ACP, 0, lpString, -1, wText, 256);
101
return SetDlgItemTextW(hDlg, nIDDlgItem, wText);
104
LPCSTR getenv(LPCSTR buf)
109
BOOL GetKeyboardState(PBYTE pb)
114
static TCHAR wGameName[256];
40
119
static FILE *debug_fp = NULL;
317
422
if (fe->drawstatus == PRINTING) {
320
print_get_colour(fe->dr, colour, &hatch, &r, &g, &b);
425
print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
323
428
br = CreateSolidBrush(RGB(r * 255, g * 255, b * 255));
324
else if (hatch == HATCH_SOLID)
325
br = CreateSolidBrush(RGB(0,0,0));
326
else if (hatch == HATCH_CLEAR)
327
br = CreateSolidBrush(RGB(255,255,255));
432
* This is only ever required during printing, and the
433
* PocketPC port doesn't support printing.
435
fatal("CreateHatchBrush not supported");
329
437
br = CreateHatchBrush(hatch == HATCH_BACKSLASH ? HS_FDIAGONAL :
330
438
hatch == HATCH_SLASH ? HS_BDIAGONAL :
331
439
hatch == HATCH_HORIZ ? HS_HORIZONTAL :
361
471
int width = thin ? 0 : fe->linewidth;
363
print_get_colour(fe->dr, colour, &hatch, &r, &g, &b);
365
pen = CreatePen(PS_SOLID, width,
366
RGB(r * 255, g * 255, b * 255));
367
else if (hatch == HATCH_SOLID)
368
pen = CreatePen(PS_SOLID, width, RGB(0, 0, 0));
369
else if (hatch == HATCH_CLEAR)
370
pen = CreatePen(PS_SOLID, width, RGB(255,255,255));
372
assert(!"This shouldn't happen");
373
pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
473
print_get_colour(fe->dr, colour, fe->printcolour, &hatch, &r, &g, &b);
475
* Stroking in hatched colours is not permitted.
478
pen = CreatePen(PS_SOLID, width, RGB(r * 255, g * 255, b * 255));
376
480
pen = fe->pens[colour];
445
550
fe->fonts[i].type = fonttype;
446
551
fe->fonts[i].size = fontsize;
448
fe->fonts[i].font = CreateFont(-fontsize, 0, 0, 0,
449
fe->drawstatus == PRINTING ? 0 : FW_BOLD,
450
FALSE, FALSE, FALSE, DEFAULT_CHARSET,
451
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
453
(fonttype == FONT_FIXED ?
454
FIXED_PITCH | FF_DONTCARE :
455
VARIABLE_PITCH | FF_SWISS),
553
memset (&lf, 0, sizeof(LOGFONT));
554
lf.lfHeight = -fontsize;
555
lf.lfWeight = (fe->drawstatus == PRINTING ? 0 : FW_BOLD);
556
lf.lfCharSet = DEFAULT_CHARSET;
557
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
558
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
559
lf.lfQuality = DEFAULT_QUALITY;
560
lf.lfPitchAndFamily = (fonttype == FONT_FIXED ?
561
FIXED_PITCH | FF_DONTCARE :
562
VARIABLE_PITCH | FF_SWISS);
564
wcscpy(lf.lfFaceName, TEXT("Tahoma"));
567
fe->fonts[i].font = CreateFontIndirect(&lf);
514
638
static void win_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour)
516
640
frontend *fe = (frontend *)handle;
519
643
if (fe->drawstatus == NOTHING)
522
646
win_set_pen(fe, colour, FALSE);
523
p = win_transform_point(fe, x1, y1);
524
q = win_transform_point(fe, x2, y2);
525
MoveToEx(fe->hdc, p.x, p.y, NULL);
526
LineTo(fe->hdc, q.x, q.y);
647
pp[0] = win_transform_point(fe, x1, y1);
648
pp[1] = win_transform_point(fe, x2, y2);
649
Polyline(fe->hdc, pp, 2);
527
650
if (fe->drawstatus == DRAWING)
528
SetPixel(fe->hdc, q.x, q.y, fe->colours[colour]);
651
SetPixel(fe->hdc, pp[1].x, pp[1].y, fe->colours[colour]);
529
652
win_reset_pen(fe);
533
656
int fillcolour, int outlinecolour)
535
658
frontend *fe = (frontend *)handle;
538
661
assert(outlinecolour >= 0);
540
663
if (fe->drawstatus == NOTHING)
543
if (fillcolour >= 0) {
544
667
win_set_brush(fe, fillcolour);
545
win_set_pen(fe, outlinecolour, FALSE);
546
p = win_transform_point(fe, cx - radius, cy - radius);
547
q = win_transform_point(fe, cx + radius, cy + radius);
548
Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1);
552
win_set_pen(fe, outlinecolour, FALSE);
553
p = win_transform_point(fe, cx - radius, cy - radius);
554
q = win_transform_point(fe, cx + radius, cy + radius);
555
r = win_transform_point(fe, cx - radius, cy);
556
Arc(fe->hdc, p.x, p.y, q.x+1, q.y+1, r.x, r.y, r.x, r.y);
669
fe->oldbr = SelectObject(fe->hdc, GetStockObject(NULL_BRUSH));
671
win_set_pen(fe, outlinecolour, FALSE);
672
p = win_transform_point(fe, cx - radius, cy - radius);
673
q = win_transform_point(fe, cx + radius, cy + radius);
674
Ellipse(fe->hdc, p.x, p.y, q.x+1, q.y+1);
561
679
static void win_draw_polygon(void *handle, int *coords, int npoints,
984
* See if we can find a help file.
1107
* Set up Help and see if we can find a help file.
986
static void find_help_file(frontend *fe)
1109
static void init_help(void)
988
1112
char b[2048], *p, *q, *r;
990
if (!fe->help_path) {
991
GetModuleFileName(NULL, b, sizeof(b) - 1);
993
p = strrchr(b, '\\');
994
if (p && p >= r) r = p+1;
996
if (q && q >= r) r = q+1;
997
strcpy(r, HELP_FILE_NAME);
998
if ( (fp = fopen(b, "r")) != NULL) {
999
fe->help_path = dupstr(b);
1002
fe->help_path = NULL;
1003
strcpy(r, HELP_CNT_NAME);
1004
if ( (fp = fopen(b, "r")) != NULL) {
1005
fe->help_has_contents = TRUE;
1008
fe->help_has_contents = FALSE;
1012
static void check_window_size(frontend *fe, int *px, int *py)
1116
* Find the executable file path, so we can look alongside
1117
* it for help files. Trim the filename off the end.
1119
GetModuleFileName(NULL, b, sizeof(b) - 1);
1121
p = strrchr(b, '\\');
1122
if (p && p >= r) r = p+1;
1123
q = strrchr(b, ':');
1124
if (q && q >= r) r = q+1;
1128
* Try HTML Help first.
1130
strcpy(r, CHM_FILE_NAME);
1131
if ( (fp = fopen(b, "r")) != NULL) {
1135
* We have a .CHM. See if we can use it.
1137
hh_dll = LoadLibrary("hhctrl.ocx");
1139
htmlhelp = (htmlhelp_t)GetProcAddress(hh_dll, "HtmlHelpA");
1141
FreeLibrary(hh_dll);
1144
help_path = dupstr(b);
1146
help_topic = thegame.htmlhelp_topic;
1150
#endif /* NO_HTMLHELP */
1153
* Now try old-style .HLP.
1155
strcpy(r, HELP_FILE_NAME);
1156
if ( (fp = fopen(b, "r")) != NULL) {
1159
help_path = dupstr(b);
1162
help_topic = thegame.winhelp_topic;
1165
* See if there's a .CNT file alongside it.
1167
strcpy(r, HELP_CNT_NAME);
1168
if ( (fp = fopen(b, "r")) != NULL) {
1170
help_has_contents = TRUE;
1172
help_has_contents = FALSE;
1177
help_type = NONE; /* didn't find any */
1186
static void start_help(frontend *fe, const char *topic)
1191
switch (help_type) {
1195
str = snewn(10+strlen(topic), char);
1196
sprintf(str, "JI(`',`%s')", topic);
1198
} else if (help_has_contents) {
1201
cmd = HELP_CONTENTS;
1203
WinHelp(fe->hwnd, help_path, cmd, (DWORD)str);
1204
fe->help_running = TRUE;
1211
str = snewn(20 + strlen(topic) + strlen(help_path), char);
1212
sprintf(str, "%s::/%s.html>main", help_path, topic);
1214
str = dupstr(help_path);
1216
htmlhelp(fe->hwnd, str, HH_DISPLAY_TOPIC, 0);
1217
fe->help_running = TRUE;
1219
#endif /* NO_HTMLHELP */
1221
assert(!"This shouldn't happen");
1229
* Stop Help on window cleanup.
1231
static void stop_help(frontend *fe)
1233
if (fe->help_running) {
1234
switch (help_type) {
1236
WinHelp(fe->hwnd, help_path, HELP_QUIT, 0);
1241
htmlhelp(NULL, NULL, HH_CLOSE_ALL, 0);
1243
#endif /* NO_HTMLHELP */
1245
assert(!"This shouldn't happen");
1248
fe->help_running = FALSE;
1255
* Terminate Help on process exit.
1257
static void cleanup_help(void)
1259
/* Nothing to do currently.
1260
* (If we were running HTML Help single-threaded, this is where we'd
1261
* call HH_UNINITIALIZE.) */
1264
static int get_statusbar_height(frontend *fe)
1017
1267
if (fe->statusbar) {
1019
1269
GetWindowRect(fe->statusbar, &sr);
1277
static void adjust_statusbar(frontend *fe, RECT *r)
1281
if (!fe->statusbar) return;
1283
sy = get_statusbar_height(fe);
1285
SetWindowPos(fe->statusbar, NULL, 0, r->bottom-r->top-sy, r->right-r->left,
1290
static void get_menu_size(HWND wh, RECT *r)
1292
HMENU bar = GetMenu(wh);
1296
SetRect(r, 0, 0, 0, 0);
1297
for (i = 0; i < GetMenuItemCount(bar); i++) {
1298
GetMenuItemRect(wh, bar, i, &rect);
1299
UnionRect(r, r, &rect);
1304
* Given a proposed new puzzle size (cx,cy), work out the actual
1305
* puzzle size that would be (px,py) and the window size including
1306
* furniture (wx,wy).
1309
static int check_window_resize(frontend *fe, int cx, int cy,
1311
int *wx, int *wy, int resize)
1314
int x, y, sy = get_statusbar_height(fe), changed = 0;
1316
/* disallow making window thinner than menu bar */
1317
x = max(cx, fe->xmin);
1318
y = max(cy - sy, fe->ymin);
1026
1321
* See if we actually got the window size we wanted, and adjust
1027
1322
* the puzzle size if not.
1029
GetClientRect(fe->hwnd, &r);
1030
x = r.right - r.left;
1031
y = r.bottom - r.top - sy;
1032
midend_size(fe->me, &x, &y, FALSE);
1033
if (x != r.right - r.left || y != r.bottom - r.top) {
1035
* Resize the window, now we know what size we _really_
1041
AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1042
SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top,
1043
SWP_NOMOVE | SWP_NOZORDER);
1046
if (fe->statusbar) {
1047
GetClientRect(fe->hwnd, &r);
1048
SetWindowPos(fe->statusbar, NULL, 0, r.bottom-r.top-sy, r.right-r.left,
1324
midend_size(fe->me, &x, &y, resize);
1325
if (x != cx || y != cy) {
1327
* Resize the window, now we know what size we _really_
1333
AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1334
*wx = r.right - r.left;
1335
*wy = r.bottom - r.top;
1346
* Given the current window size, make sure it's sane for the
1347
* current puzzle and resize if necessary.
1350
static void check_window_size(frontend *fe, int *px, int *py)
1355
GetClientRect(fe->hwnd, &r);
1356
cx = r.right - r.left;
1357
cy = r.bottom - r.top;
1359
if (check_window_resize(fe, cx, cy, px, py, &wx, &wy, FALSE)) {
1361
SetWindowPos(fe->hwnd, NULL, 0, 0, wx, wy,
1362
SWP_NOMOVE | SWP_NOZORDER);
1367
GetClientRect(fe->hwnd, &r);
1368
adjust_statusbar(fe, &r);
1056
1371
static void get_max_puzzle_size(frontend *fe, int *x, int *y)
1396
/* Toolbar buttons on the numeric pad */
1397
static TBBUTTON tbNumpadButtons[] =
1399
{0, IDM_KEYEMUL + '1', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1400
{1, IDM_KEYEMUL + '2', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1401
{2, IDM_KEYEMUL + '3', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1402
{3, IDM_KEYEMUL + '4', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1403
{4, IDM_KEYEMUL + '5', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1404
{5, IDM_KEYEMUL + '6', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1405
{6, IDM_KEYEMUL + '7', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1406
{7, IDM_KEYEMUL + '8', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1407
{8, IDM_KEYEMUL + '9', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1},
1408
{9, IDM_KEYEMUL + ' ', TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, -1}
1080
1412
static frontend *new_window(HINSTANCE inst, char *game_id, char **error)
1150
1481
AdjustWindowRectEx(&r, WINFLAGS, TRUE, 0);
1484
fe->hwnd = CreateWindowEx(0, wGameName, wGameName,
1486
CW_USEDEFAULT, CW_USEDEFAULT,
1487
CW_USEDEFAULT, CW_USEDEFAULT,
1488
NULL, NULL, inst, NULL);
1492
RECT rc, rcBar, rcTB, rcClient;
1494
memset (&mbi, 0, sizeof(SHMENUBARINFO));
1495
mbi.cbSize = sizeof(SHMENUBARINFO);
1496
mbi.hwndParent = fe->hwnd;
1497
mbi.nToolBarId = IDR_MENUBAR1;
1498
mbi.hInstRes = inst;
1500
SHCreateMenuBar(&mbi);
1502
GetWindowRect(fe->hwnd, &rc);
1503
GetWindowRect(mbi.hwndMB, &rcBar);
1504
rc.bottom -= rcBar.bottom - rcBar.top;
1505
MoveWindow(fe->hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, FALSE);
1507
if (thegame.flags & REQUIRE_NUMPAD)
1509
fe->numpad = CreateToolbarEx (fe->hwnd,
1510
WS_VISIBLE | WS_CHILD | CCS_NOPARENTALIGN | TBSTYLE_FLAT,
1511
0, 10, inst, IDR_PADTOOLBAR,
1512
tbNumpadButtons, sizeof (tbNumpadButtons) / sizeof (TBBUTTON),
1513
0, 0, 14, 15, sizeof (TBBUTTON));
1514
GetWindowRect(fe->numpad, &rcTB);
1515
GetClientRect(fe->hwnd, &rcClient);
1516
MoveWindow(fe->numpad,
1518
rcClient.bottom - (rcTB.bottom - rcTB.top) - 1,
1520
rcTB.bottom - rcTB.top,
1522
SendMessage(fe->numpad, TB_SETINDENT, (rcClient.right - (10 * 21)) / 2, 0);
1152
1528
fe->hwnd = CreateWindowEx(0, thegame.name, thegame.name,
1153
1529
WS_OVERLAPPEDWINDOW &~
1154
(WS_THICKFRAME | WS_MAXIMIZEBOX),
1155
1531
CW_USEDEFAULT, CW_USEDEFAULT,
1156
1532
r.right - r.left, r.bottom - r.top,
1157
1533
NULL, NULL, inst, NULL);
1159
1536
if (midend_wants_statusbar(fe->me)) {
1161
1538
DestroyWindow(fe->statusbar);
1162
fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, "ooh",
1539
fe->statusbar = CreateWindowEx(0, STATUSCLASSNAME, TEXT("ooh"),
1163
1540
WS_CHILD | WS_VISIBLE,
1164
1541
0, 0, 0, 0, /* status bar does these */
1165
1542
fe->hwnd, NULL, inst, NULL);
1544
/* Flat status bar looks better on the Pocket PC */
1545
SendMessage(fe->statusbar, SB_SIMPLE, (WPARAM) TRUE, 0);
1546
SendMessage(fe->statusbar, SB_SETTEXT,
1547
(WPARAM) 255 | SBT_NOBORDERS,
1167
1552
* Now resize the window to take account of the status bar.
1169
1554
GetWindowRect(fe->statusbar, &sr);
1170
1555
GetWindowRect(fe->hwnd, &r);
1171
1557
SetWindowPos(fe->hwnd, NULL, 0, 0, r.right - r.left,
1172
1558
r.bottom - r.top + sr.bottom - sr.top,
1173
1559
SWP_NOMOVE | SWP_NOZORDER);
1175
1562
fe->statusbar = NULL;
1179
1567
HMENU bar = CreateMenu();
1180
1568
HMENU menu = CreateMenu();
1182
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Game");
1183
AppendMenu(menu, MF_ENABLED, IDM_NEW, "New");
1184
AppendMenu(menu, MF_ENABLED, IDM_RESTART, "Restart");
1185
AppendMenu(menu, MF_ENABLED, IDM_DESC, "Specific...");
1186
AppendMenu(menu, MF_ENABLED, IDM_SEED, "Random Seed...");
1571
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "&Game");
1573
HMENU menu = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_GAME);
1574
DeleteMenu(menu, 0, MF_BYPOSITION);
1576
AppendMenu(menu, MF_ENABLED, IDM_NEW, TEXT("&New"));
1577
AppendMenu(menu, MF_ENABLED, IDM_RESTART, TEXT("&Restart"));
1579
/* ...here I run out of sensible accelerator characters. */
1580
AppendMenu(menu, MF_ENABLED, IDM_DESC, TEXT("Speci&fic..."));
1581
AppendMenu(menu, MF_ENABLED, IDM_SEED, TEXT("Rando&m Seed..."));
1188
1584
if ((fe->npresets = midend_num_presets(fe->me)) > 0 ||
1189
1585
thegame.can_configure) {
1190
1588
HMENU sub = CreateMenu();
1193
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "Type");
1590
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)sub, "&Type");
1592
HMENU sub = SHGetSubMenu(SHFindMenuBar(fe->hwnd), ID_TYPE);
1593
DeleteMenu(sub, 0, MF_BYPOSITION);
1195
1595
fe->presets = snewn(fe->npresets, game_params *);
1197
1597
for (i = 0; i < fe->npresets; i++) {
1200
1603
midend_fetch_preset(fe->me, i, &name, &fe->presets[i]);
1204
1607
* with ampersands here.
1207
1611
AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, name);
1613
MultiByteToWideChar (CP_ACP, 0, name, -1, wName, 255);
1614
AppendMenu(sub, MF_ENABLED, IDM_PRESETS + 0x10 * i, wName);
1210
1617
if (thegame.can_configure) {
1211
AppendMenu(sub, MF_ENABLED, IDM_CONFIG, "Custom...");
1618
AppendMenu(sub, MF_ENABLED, IDM_CONFIG, TEXT("&Custom..."));
1623
fe->typemenu = INVALID_HANDLE_VALUE;
1215
1625
AppendMenu(menu, MF_SEPARATOR, 0, 0);
1216
AppendMenu(menu, MF_ENABLED, IDM_LOAD, "Load");
1217
AppendMenu(menu, MF_ENABLED, IDM_SAVE, "Save");
1627
AppendMenu(menu, MF_ENABLED, IDM_LOAD, TEXT("&Load..."));
1628
AppendMenu(menu, MF_ENABLED, IDM_SAVE, TEXT("&Save..."));
1218
1629
AppendMenu(menu, MF_SEPARATOR, 0, 0);
1219
1630
if (thegame.can_print) {
1220
AppendMenu(menu, MF_ENABLED, IDM_PRINT, "Print");
1631
AppendMenu(menu, MF_ENABLED, IDM_PRINT, TEXT("&Print..."));
1221
1632
AppendMenu(menu, MF_SEPARATOR, 0, 0);
1223
AppendMenu(menu, MF_ENABLED, IDM_UNDO, "Undo");
1224
AppendMenu(menu, MF_ENABLED, IDM_REDO, "Redo");
1635
AppendMenu(menu, MF_ENABLED, IDM_UNDO, TEXT("Undo"));
1636
AppendMenu(menu, MF_ENABLED, IDM_REDO, TEXT("Redo"));
1225
1638
if (thegame.can_format_as_text) {
1226
1639
AppendMenu(menu, MF_SEPARATOR, 0, 0);
1227
AppendMenu(menu, MF_ENABLED, IDM_COPY, "Copy");
1640
AppendMenu(menu, MF_ENABLED, IDM_COPY, TEXT("&Copy"));
1229
1643
if (thegame.can_solve) {
1230
1644
AppendMenu(menu, MF_SEPARATOR, 0, 0);
1231
AppendMenu(menu, MF_ENABLED, IDM_SOLVE, "Solve");
1645
AppendMenu(menu, MF_ENABLED, IDM_SOLVE, TEXT("Sol&ve"));
1233
1647
AppendMenu(menu, MF_SEPARATOR, 0, 0);
1234
AppendMenu(menu, MF_ENABLED, IDM_QUIT, "Exit");
1649
AppendMenu(menu, MF_ENABLED, IDM_QUIT, TEXT("E&xit"));
1235
1650
menu = CreateMenu();
1236
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, "Help");
1237
AppendMenu(menu, MF_ENABLED, IDM_ABOUT, "About");
1238
if (fe->help_path) {
1651
AppendMenu(bar, MF_ENABLED|MF_POPUP, (UINT)menu, TEXT("&Help"));
1653
AppendMenu(menu, MF_ENABLED, IDM_ABOUT, TEXT("&About"));
1655
if (help_type != NONE) {
1239
1656
AppendMenu(menu, MF_SEPARATOR, 0, 0);
1240
AppendMenu(menu, MF_ENABLED, IDM_HELPC, "Contents");
1241
if (thegame.winhelp_topic) {
1657
AppendMenu(menu, MF_ENABLED, IDM_HELPC, TEXT("&Contents"));
1243
1660
assert(thegame.name);
1244
item = snewn(9+strlen(thegame.name), char); /*ick*/
1245
sprintf(item, "Help on %s", thegame.name);
1661
item = snewn(10+strlen(thegame.name), char); /*ick*/
1662
sprintf(item, "&Help on %s", thegame.name);
1246
1663
AppendMenu(menu, MF_ENABLED, IDM_GAMEHELP, item);
1250
1667
SetMenu(fe->hwnd, bar);
1668
get_menu_size(fe->hwnd, &menusize);
1669
fe->xmin = (menusize.right - menusize.left) + 25;
1253
1673
fe->bitmap = NULL;
1257
1677
SetWindowLong(fe->hwnd, GWL_USERDATA, (LONG)fe);
1259
ShowWindow(fe->hwnd, SW_NORMAL);
1679
ShowWindow(fe->hwnd, SW_SHOWNORMAL);
1260
1680
SetForegroundWindow(fe->hwnd);
1682
update_type_menu_tick(fe);
1262
1684
midend_redraw(fe->me);
1690
static HFONT dialog_title_font()
1692
static HFONT hf = NULL;
1698
memset (&lf, 0, sizeof(LOGFONT));
1699
lf.lfHeight = -11; /* - ((8 * GetDeviceCaps(hdc, LOGPIXELSY)) / 72) */
1700
lf.lfWeight = FW_BOLD;
1701
wcscpy(lf.lfFaceName, TEXT("Tahoma"));
1703
return hf = CreateFontIndirect(&lf);
1706
static void make_dialog_full_screen(HWND hwnd)
1708
SHINITDLGINFO shidi;
1710
/* Make dialog full screen */
1711
shidi.dwMask = SHIDIM_FLAGS;
1712
shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN |
1715
SHInitDialog(&shidi);
1267
1719
static int CALLBACK AboutDlgProc(HWND hwnd, UINT msg,
1268
1720
WPARAM wParam, LPARAM lParam)
1866
/* Separate version of mkctrl function for the Pocket PC. */
1867
/* Control coordinates should be specified in dialog units. */
1868
HWND mkctrl(frontend *fe, int x1, int x2, int y1, int y2,
1869
LPCTSTR wclass, int wstyle,
1870
int exstyle, const char *wtext, int wid)
1875
/* Convert dialog units into pixels */
1876
rc.left = x1; rc.right = x2;
1877
rc.top = y1; rc.bottom = y2;
1878
MapDialogRect(fe->cfgbox, &rc);
1880
MultiByteToWideChar (CP_ACP, 0, wtext, -1, wwtext, 256);
1882
return CreateWindowEx(exstyle, wclass, wwtext,
1883
wstyle | WS_CHILD | WS_VISIBLE,
1885
rc.right - rc.left, rc.bottom - rc.top,
1886
fe->cfgbox, (HMENU) wid, fe->inst, NULL);
1889
static void create_config_controls(frontend * fe)
1892
int col1l, col1r, col2l, col2r, y;
1897
/* Control placement done in dialog units */
1898
col1l = 4; col1r = 96; /* Label column */
1899
col2l = 100; col2r = 154; /* Input column (edit boxes and combo boxes) */
1902
* Count the controls so we can allocate cfgaux.
1904
for (nctrls = 0, i = fe->cfg; i->type != C_END; i++)
1906
fe->cfgaux = snewn(nctrls, struct cfg_aux);
1909
y = 22; /* Leave some room for the dialog title */
1910
for (i = fe->cfg, j = fe->cfgaux; i->type != C_END; i++, j++) {
1914
* Edit box with a label beside it.
1916
mkctrl(fe, col1l, col1r, y + 1, y + 11,
1917
TEXT("Static"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
1918
mkctrl(fe, col2l, col2r, y, y + 12,
1919
TEXT("EDIT"), WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,
1920
0, "", (j->ctlid = id++));
1921
SetDlgItemTextA(fe->cfgbox, j->ctlid, i->sval);
1928
mkctrl(fe, col1l, col2r, y + 1, y + 11, TEXT("BUTTON"),
1929
BS_NOTIFY | BS_AUTOCHECKBOX | WS_TABSTOP,
1930
0, i->name, (j->ctlid = id++));
1931
CheckDlgButton(fe->cfgbox, j->ctlid, (i->ival != 0));
1936
* Drop-down list with a label beside it.
1938
mkctrl(fe, col1l, col1r, y + 1, y + 11,
1939
TEXT("STATIC"), SS_LEFTNOWORDWRAP, 0, i->name, id++);
1940
ctl = mkctrl(fe, col2l, col2r, y, y + 48,
1941
TEXT("COMBOBOX"), WS_BORDER | WS_TABSTOP |
1942
CBS_DROPDOWNLIST | CBS_HASSTRINGS,
1943
0, "", (j->ctlid = id++));
1945
char c, *p, *q, *str;
1951
while (*q && *q != c) q++;
1952
str = snewn(q-p+1, char);
1953
strncpy(str, p, q-p);
1957
MultiByteToWideChar (CP_ACP, 0, str, -1, ws, 50);
1958
SendMessage(ctl, CB_ADDSTRING, 0, (LPARAM)ws);
1966
SendMessage(ctl, CB_SETCURSEL, i->ival, 0);
1391
1976
static int CALLBACK ConfigDlgProc(HWND hwnd, UINT msg,
1392
1977
WPARAM wParam, LPARAM lParam)
1837
2467
y += height/2; /* extra space before OK and Cancel */
1838
2468
mkctrl(fe, col1l, (col1l+col2r)/2-width, y, y+height*7/4, "BUTTON",
1839
BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
2469
BS_PUSHBUTTON | WS_TABSTOP | BS_DEFPUSHBUTTON, 0,
1841
2471
mkctrl(fe, (col1l+col2r)/2+width, col2r, y, y+height*7/4, "BUTTON",
1842
BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP, 0, "Cancel", IDCANCEL);
2472
BS_PUSHBUTTON | WS_TABSTOP, 0, "Cancel", IDCANCEL);
1844
2474
SendMessage(fe->cfgbox, WM_INITDIALOG, 0, 0);
1846
2476
EnableWindow(fe->hwnd, FALSE);
1847
ShowWindow(fe->cfgbox, SW_NORMAL);
2477
ShowWindow(fe->cfgbox, SW_SHOWNORMAL);
1848
2478
while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) {
1849
2479
if (!IsDialogMessage(fe->cfgbox, &msg))
1850
2480
DispatchMessage(&msg);
1860
2490
sfree(fe->cfgaux);
1862
2492
return (fe->dlg_done == 2);
2497
static void calculate_bitmap_position(frontend *fe, int x, int y)
2499
/* Pocket PC - center the game in the full screen window */
2503
GetClientRect(fe->hwnd, &rcClient);
2504
fe->bitmapPosition.left = (rcClient.right - x) / 2;
2505
yMargin = rcClient.bottom - y;
2507
if (fe->numpad != NULL) {
2509
GetWindowRect(fe->numpad, &rcPad);
2510
yMargin -= rcPad.bottom - rcPad.top;
2513
if (fe->statusbar != NULL) {
2515
GetWindowRect(fe->statusbar, &rcStatus);
2516
yMargin -= rcStatus.bottom - rcStatus.top;
2519
fe->bitmapPosition.top = yMargin / 2;
2521
fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2522
fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2525
static void calculate_bitmap_position(frontend *fe, int x, int y)
2527
/* Plain Windows - position the game in the upper-left corner */
2528
fe->bitmapPosition.left = 0;
2529
fe->bitmapPosition.top = 0;
2530
fe->bitmapPosition.right = fe->bitmapPosition.left + x;
2531
fe->bitmapPosition.bottom = fe->bitmapPosition.top + y;
2535
static void new_bitmap(frontend *fe, int x, int y)
2539
if (fe->bitmap) DeleteObject(fe->bitmap);
2541
hdc = GetDC(fe->hwnd);
2542
fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
2543
calculate_bitmap_position(fe, x, y);
2544
ReleaseDC(fe->hwnd, hdc);
1865
2547
static void new_game_size(frontend *fe)
1871
2552
get_max_puzzle_size(fe, &x, &y);
1872
2553
midend_size(fe->me, &x, &y, FALSE);
2554
fe->ymin = (fe->xmin * y) / x;
1874
2556
r.left = r.top = 0;
1882
2564
sr.left = sr.right = sr.top = sr.bottom = 0;
1884
2567
SetWindowPos(fe->hwnd, NULL, 0, 0,
1885
2568
r.right - r.left,
1886
2569
r.bottom - r.top + sr.bottom - sr.top,
1887
2570
SWP_NOMOVE | SWP_NOZORDER);
1889
2573
check_window_size(fe, &x, &y);
1891
2576
if (fe->statusbar != NULL)
1892
2577
SetWindowPos(fe->statusbar, NULL, 0, y, x,
1893
2578
sr.bottom - sr.top, SWP_NOZORDER);
1895
if (fe->bitmap) DeleteObject(fe->bitmap);
1897
hdc = GetDC(fe->hwnd);
1898
fe->bitmap = CreateCompatibleBitmap(hdc, x, y);
1899
ReleaseDC(fe->hwnd, hdc);
2581
new_bitmap(fe, x, y);
2584
InvalidateRect(fe->hwnd, NULL, TRUE);
1901
2586
midend_redraw(fe->me);
2590
* Given a proposed new window rect, work out the resulting
2591
* difference in client size (from current), and use to try
2592
* and resize the puzzle, returning (wx,wy) as the actual
2596
static void adjust_game_size(frontend *fe, RECT *proposed, int isedge,
2597
int *wx_r, int *wy_r)
2600
int nx, ny, xdiff, ydiff, wx, wy;
2602
/* Work out the current window sizing, and thus the
2603
* difference in size we're asking for. */
2604
GetClientRect(fe->hwnd, &cr);
2606
AdjustWindowRectEx(&wr, WINFLAGS, TRUE, 0);
2608
xdiff = (proposed->right - proposed->left) - (wr.right - wr.left);
2609
ydiff = (proposed->bottom - proposed->top) - (wr.bottom - wr.top);
2612
/* These next four lines work around the fact that midend_size
2613
* is happy to shrink _but not grow_ if you change one dimension
2614
* but not the other. */
2615
if (xdiff > 0 && ydiff == 0)
2616
ydiff = (xdiff * (wr.right - wr.left)) / (wr.bottom - wr.top);
2617
if (xdiff == 0 && ydiff > 0)
2618
xdiff = (ydiff * (wr.bottom - wr.top)) / (wr.right - wr.left);
2621
if (check_window_resize(fe,
2622
(cr.right - cr.left) + xdiff,
2623
(cr.bottom - cr.top) + ydiff,
2624
&nx, &ny, &wx, &wy, TRUE)) {
2625
new_bitmap(fe, nx, ny);
2626
midend_force_redraw(fe->me);
2628
/* reset size to current window size */
2629
wx = wr.right - wr.left;
2630
wy = wr.bottom - wr.top;
2632
/* Re-fetch rectangle; size limits mean we might not have
2633
* taken it quite to the mouse drag positions. */
2634
GetClientRect(fe->hwnd, &cr);
2635
adjust_statusbar(fe, &cr);
2637
*wx_r = wx; *wy_r = wy;
2640
static void update_type_menu_tick(frontend *fe)
2644
if (fe->typemenu == INVALID_HANDLE_VALUE)
2647
total = GetMenuItemCount(fe->typemenu);
2648
n = midend_which_preset(fe->me);
2650
n = total - 1; /* "Custom" item */
2652
for (i = 0; i < total; i++) {
2653
int flag = (i == n ? MF_CHECKED : MF_UNCHECKED);
2654
CheckMenuItem(fe->typemenu, i, MF_BYPOSITION | flag);
2657
DrawMenuBar(fe->hwnd);
1904
2660
static void new_game_type(frontend *fe)
1906
2662
midend_new_game(fe->me);
1907
2663
new_game_size(fe);
2664
update_type_menu_tick(fe);
1910
2667
static int is_alt_pressed(void)
2127
2887
HBITMAP prevbm;
2129
2890
hdc = BeginPaint(hwnd, &p);
2130
2891
hdc2 = CreateCompatibleDC(hdc);
2131
2892
prevbm = SelectObject(hdc2, fe->bitmap);
2894
FillRect(hdc, &(p.rcPaint), (HBRUSH) GetStockObject(WHITE_BRUSH));
2896
IntersectRect(&rcDest, &(fe->bitmapPosition), &(p.rcPaint));
2133
p.rcPaint.left, p.rcPaint.top,
2134
p.rcPaint.right - p.rcPaint.left,
2135
p.rcPaint.bottom - p.rcPaint.top,
2898
rcDest.left, rcDest.top,
2899
rcDest.right - rcDest.left,
2900
rcDest.bottom - rcDest.top,
2137
p.rcPaint.left, p.rcPaint.top,
2902
rcDest.left - fe->bitmapPosition.left,
2903
rcDest.top - fe->bitmapPosition.top,
2139
2905
SelectObject(hdc2, prevbm);
2140
2906
DeleteDC(hdc2);
2301
3093
fe->timer_last_tickcount = now;
3099
RECT *sr = (RECT *)lParam;
3100
int wx, wy, isedge = 0;
3102
if (wParam == WMSZ_TOP ||
3103
wParam == WMSZ_RIGHT ||
3104
wParam == WMSZ_BOTTOM ||
3105
wParam == WMSZ_LEFT) isedge = 1;
3106
adjust_game_size(fe, sr, isedge, &wx, &wy);
3108
/* Given the window size the puzzles constrain
3109
* us to, work out which edge we should be moving. */
3110
if (wParam == WMSZ_TOP ||
3111
wParam == WMSZ_TOPLEFT ||
3112
wParam == WMSZ_TOPRIGHT) {
3113
sr->top = sr->bottom - wy;
3115
sr->bottom = sr->top + wy;
3117
if (wParam == WMSZ_LEFT ||
3118
wParam == WMSZ_TOPLEFT ||
3119
wParam == WMSZ_BOTTOMLEFT) {
3120
sr->left = sr->right - wx;
3122
sr->right = sr->left + wx;
2306
3130
return DefWindowProc(hwnd, message, wParam, lParam);
3134
static int FindPreviousInstance()
3136
/* Check if application is running. If it's running then focus on the window */
3137
HWND hOtherWnd = NULL;
3139
hOtherWnd = FindWindow (wGameName, wGameName);
3142
SetForegroundWindow (hOtherWnd);
2309
3150
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
3156
MultiByteToWideChar (CP_ACP, 0, thegame.name, -1, wGameName, 256);
3157
if (FindPreviousInstance ())
2314
3161
InitCommonControls();