detection/#terrain fix for u.uinwater
[aNetHack.git] / win / win32 / mhmain.c
blob2b52f71cf1cd9fb4556c366f6233a59442d859fd
1 /* NetHack 3.6 mhmain.c $NHDT-Date: 1432512811 2015/05/25 00:13:31 $ $NHDT-Branch: master $:$NHDT-Revision: 1.62 $ */
2 /* Copyright (C) 2001 by Alex Kompel */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "winMS.h"
6 #include <commdlg.h>
7 #include "date.h"
8 #include "patchlevel.h"
9 #include "resource.h"
10 #include "mhmsg.h"
11 #include "mhinput.h"
12 #include "mhmain.h"
13 #include "mhmenu.h"
14 #include "mhstatus.h"
15 #include "mhmsgwnd.h"
16 #include "mhmap.h"
18 typedef struct mswin_nethack_main_window {
19 int mapAcsiiModeSave;
20 } NHMainWindow, *PNHMainWindow;
22 extern winid WIN_STATUS;
24 static TCHAR szMainWindowClass[] = TEXT("MSNHMainWndClass");
25 static TCHAR szTitle[MAX_LOADSTRING];
26 extern void mswin_display_splash_window(BOOL);
28 LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
29 LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
30 static LRESULT onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
31 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
32 static void register_main_window_class(void);
33 static int menuid2mapmode(int menuid);
34 static int mapmode2menuid(int map_mode);
35 static void nhlock_windows(BOOL lock);
36 static char *nh_compose_ascii_screenshot();
37 static void mswin_apply_window_style_all();
38 // returns strdup() created pointer - callee assumes the ownership
40 HWND
41 mswin_init_main_window()
43 static int run_once = 0;
44 HWND ret;
45 WINDOWPLACEMENT wp;
47 /* register window class */
48 if (!run_once) {
49 LoadString(GetNHApp()->hApp, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
50 register_main_window_class();
51 run_once = 1;
54 /* create the main window */
55 ret =
56 CreateWindow(szMainWindowClass, /* registered class name */
57 szTitle, /* window name */
58 WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, /* window style */
59 CW_USEDEFAULT, /* horizontal position of window */
60 CW_USEDEFAULT, /* vertical position of window */
61 CW_USEDEFAULT, /* window width */
62 CW_USEDEFAULT, /* window height */
63 NULL, /* handle to parent or owner window */
64 NULL, /* menu handle or child identifier */
65 GetNHApp()->hApp, /* handle to application instance */
66 NULL /* window-creation data */
69 if (!ret)
70 panic("Cannot create main window");
72 if (GetNHApp()->regMainMinX != CW_USEDEFAULT) {
73 wp.length = sizeof(wp);
74 wp.showCmd = GetNHApp()->regMainShowState;
76 wp.ptMinPosition.x = GetNHApp()->regMainMinX;
77 wp.ptMinPosition.y = GetNHApp()->regMainMinY;
79 wp.ptMaxPosition.x = GetNHApp()->regMainMaxX;
80 wp.ptMaxPosition.y = GetNHApp()->regMainMaxY;
82 wp.rcNormalPosition.left = GetNHApp()->regMainLeft;
83 wp.rcNormalPosition.top = GetNHApp()->regMainTop;
84 wp.rcNormalPosition.right = GetNHApp()->regMainRight;
85 wp.rcNormalPosition.bottom = GetNHApp()->regMainBottom;
86 SetWindowPlacement(ret, &wp);
87 } else
88 ShowWindow(ret, SW_SHOWDEFAULT);
90 UpdateWindow(ret);
91 return ret;
94 void
95 register_main_window_class()
97 WNDCLASS wcex;
99 ZeroMemory(&wcex, sizeof(wcex));
100 wcex.style = CS_HREDRAW | CS_VREDRAW;
101 wcex.lpfnWndProc = (WNDPROC) MainWndProc;
102 wcex.cbClsExtra = 0;
103 wcex.cbWndExtra = 0;
104 wcex.hInstance = GetNHApp()->hApp;
105 wcex.hIcon = LoadIcon(GetNHApp()->hApp, (LPCTSTR) IDI_NETHACKW);
106 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
107 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
108 wcex.lpszMenuName = (TCHAR *) IDC_NETHACKW;
109 wcex.lpszClassName = szMainWindowClass;
111 RegisterClass(&wcex);
115 * Keypad keys are translated to the normal values below.
116 * Shifted keypad keys are translated to the
117 * shift values below.
120 enum KEY_INDEXES {
121 KEY_NW,
122 KEY_N,
123 KEY_NE,
124 KEY_MINUS,
125 KEY_W,
126 KEY_GOINTERESTING,
127 KEY_E,
128 KEY_PLUS,
129 KEY_SW,
130 KEY_S,
131 KEY_SE,
132 KEY_INV,
133 KEY_WAITLOOK,
134 KEY_LAST
137 static const unsigned char
138 /* normal, shift, control */
139 keypad[KEY_LAST][3] =
141 { 'y', 'Y', C('y') }, /* 7 */
142 { 'k', 'K', C('k') }, /* 8 */
143 { 'u', 'U', C('u') }, /* 9 */
144 { 'm', C('p'), C('p') }, /* - */
145 { 'h', 'H', C('h') }, /* 4 */
146 { 'g', 'G', 'g' }, /* 5 */
147 { 'l', 'L', C('l') }, /* 6 */
148 { '+', 'P', C('p') }, /* + */
149 { 'b', 'B', C('b') }, /* 1 */
150 { 'j', 'J', C('j') }, /* 2 */
151 { 'n', 'N', C('n') }, /* 3 */
152 { 'i', 'I', C('i') }, /* Ins */
153 { '.', ':', ':' } /* Del */
155 numpad[KEY_LAST][3] = {
156 { '7', M('7'), '7' }, /* 7 */
157 { '8', M('8'), '8' }, /* 8 */
158 { '9', M('9'), '9' }, /* 9 */
159 { 'm', C('p'), C('p') }, /* - */
160 { '4', M('4'), '4' }, /* 4 */
161 { '5', M('5'), '5' }, /* 5 */
162 { '6', M('6'), '6' }, /* 6 */
163 { '+', 'P', C('p') }, /* + */
164 { '1', M('1'), '1' }, /* 1 */
165 { '2', M('2'), '2' }, /* 2 */
166 { '3', M('3'), '3' }, /* 3 */
167 { '0', M('0'), '0' }, /* Ins */
168 { '.', ':', ':' } /* Del */
171 #define STATEON(x) ((GetKeyState(x) & 0xFFFE) != 0)
172 #define KEYTABLE_REGULAR(x) ((iflags.num_pad ? numpad : keypad)[x][0])
173 #define KEYTABLE_SHIFT(x) ((iflags.num_pad ? numpad : keypad)[x][1])
174 #define KEYTABLE(x) \
175 (STATEON(VK_SHIFT) ? KEYTABLE_SHIFT(x) : KEYTABLE_REGULAR(x))
177 static const char *extendedlist = "acdefijlmnopqrstuvw?2";
179 #define SCANLO 0x02
180 static const char scanmap[] = {
181 /* ... */
182 '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0, 0, 0, 0, 'q', 'w',
183 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd',
184 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
185 'b', 'n', 'm', ',', '.', '?' /* ... */
189 // FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
191 // PURPOSE: Processes messages for the main window.
193 LRESULT CALLBACK
194 MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
196 PNHMainWindow data;
198 switch (message) {
199 case WM_CREATE:
200 /* set window data */
201 data = (PNHMainWindow) malloc(sizeof(NHMainWindow));
202 if (!data)
203 panic("out of memory");
204 ZeroMemory(data, sizeof(NHMainWindow));
205 data->mapAcsiiModeSave = MAP_MODE_ASCII12x16;
206 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
208 /* update menu items */
209 CheckMenuItem(
210 GetMenu(hWnd), IDM_SETTING_LOCKWINDOWS,
211 MF_BYCOMMAND
212 | (GetNHApp()->bWindowsLocked ? MF_CHECKED : MF_UNCHECKED));
214 CheckMenuItem(GetMenu(hWnd), IDM_SETTING_AUTOLAYOUT,
215 GetNHApp()->bAutoLayout ? MF_CHECKED : MF_UNCHECKED);
217 /* store handle to the mane menu in the application record */
218 GetNHApp()->hMainWnd = hWnd;
219 break;
221 case WM_MSNH_COMMAND:
222 onMSNHCommand(hWnd, wParam, lParam);
223 break;
225 case WM_KEYDOWN: {
226 data = (PNHMainWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
228 /* translate arrow keys into nethack commands */
229 switch (wParam) {
230 case VK_LEFT:
231 if (STATEON(VK_CONTROL)) {
232 /* scroll map window one line left */
233 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
234 MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
235 } else {
236 NHEVENT_KBD(KEYTABLE(KEY_W));
238 return 0;
240 case VK_RIGHT:
241 if (STATEON(VK_CONTROL)) {
242 /* scroll map window one line right */
243 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
244 MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM) NULL);
245 } else {
246 NHEVENT_KBD(KEYTABLE(KEY_E));
248 return 0;
250 case VK_UP:
251 if (STATEON(VK_CONTROL)) {
252 /* scroll map window one line up */
253 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
254 MAKEWPARAM(SB_LINEUP, 0), (LPARAM) NULL);
255 } else {
256 NHEVENT_KBD(KEYTABLE(KEY_N));
258 return 0;
260 case VK_DOWN:
261 if (STATEON(VK_CONTROL)) {
262 /* scroll map window one line down */
263 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
264 MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM) NULL);
265 } else {
266 NHEVENT_KBD(KEYTABLE(KEY_S));
268 return 0;
270 case VK_HOME:
271 if (STATEON(VK_CONTROL)) {
272 /* scroll map window to upper left corner */
273 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
274 MAKEWPARAM(SB_THUMBTRACK, 0), (LPARAM) NULL);
276 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
277 MAKEWPARAM(SB_THUMBTRACK, 0), (LPARAM) NULL);
278 } else {
279 NHEVENT_KBD(KEYTABLE(KEY_NW));
281 return 0;
283 case VK_END:
284 if (STATEON(VK_CONTROL)) {
285 /* scroll map window to lower right corner */
286 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
287 MAKEWPARAM(SB_THUMBTRACK, ROWNO), (LPARAM) NULL);
289 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_HSCROLL,
290 MAKEWPARAM(SB_THUMBTRACK, COLNO), (LPARAM) NULL);
291 } else {
292 NHEVENT_KBD(KEYTABLE(KEY_SW));
294 return 0;
296 case VK_PRIOR:
297 if (STATEON(VK_CONTROL)) {
298 /* scroll map window one page up */
299 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
300 MAKEWPARAM(SB_PAGEUP, 0), (LPARAM) NULL);
301 } else {
302 NHEVENT_KBD(KEYTABLE(KEY_NE));
304 return 0;
306 case VK_NEXT:
307 if (STATEON(VK_CONTROL)) {
308 /* scroll map window one page down */
309 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_VSCROLL,
310 MAKEWPARAM(SB_PAGEDOWN, 0), (LPARAM) NULL);
311 } else {
312 NHEVENT_KBD(KEYTABLE(KEY_SE));
314 return 0;
316 case VK_DECIMAL:
317 case VK_DELETE:
318 NHEVENT_KBD(KEYTABLE(KEY_WAITLOOK));
319 return 0;
321 case VK_INSERT:
322 NHEVENT_KBD(KEYTABLE(KEY_INV));
323 return 0;
325 case VK_SUBTRACT:
326 NHEVENT_KBD(KEYTABLE(KEY_MINUS));
327 return 0;
329 case VK_ADD:
330 NHEVENT_KBD(KEYTABLE(KEY_PLUS));
331 return 0;
333 case VK_CLEAR: /* This is the '5' key */
334 NHEVENT_KBD(KEYTABLE(KEY_GOINTERESTING));
335 return 0;
337 case VK_F4:
338 if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
339 mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
340 ? data->mapAcsiiModeSave
341 : MAP_MODE_TILES);
342 } else {
343 mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
344 ? MAP_MODE_ASCII_FIT_TO_SCREEN
345 : MAP_MODE_TILES_FIT_TO_SCREEN);
347 return 0;
349 case VK_F5:
350 if (IS_MAP_ASCII(iflags.wc_map_mode)) {
351 if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
352 mswin_select_map_mode(MAP_MODE_TILES_FIT_TO_SCREEN);
353 } else {
354 mswin_select_map_mode(MAP_MODE_TILES);
356 } else {
357 if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
358 mswin_select_map_mode(MAP_MODE_ASCII_FIT_TO_SCREEN);
359 } else {
360 mswin_select_map_mode(data->mapAcsiiModeSave);
363 return 0;
365 default: {
366 WORD c;
367 BYTE kbd_state[256];
369 c = 0;
370 ZeroMemory(kbd_state, sizeof(kbd_state));
371 GetKeyboardState(kbd_state);
373 if (ToAscii(wParam, (lParam >> 16) & 0xFF, kbd_state, &c, 0)) {
374 NHEVENT_KBD(c & 0xFF);
375 return 0;
376 } else {
377 return 1;
381 } /* end switch */
382 } break;
384 case WM_SYSCHAR: /* Alt-char pressed */
387 If not nethackmode, don't handle Alt-keys here.
388 If no Alt-key pressed it can never be an extended command
390 if (GetNHApp()->regNetHackMode && ((lParam & 1 << 29) != 0)) {
391 unsigned char c = (unsigned char) (wParam & 0xFF);
392 unsigned char scancode = (lParam >> 16) & 0xFF;
393 if (index(extendedlist, tolower(c)) != 0) {
394 NHEVENT_KBD(M(tolower(c)));
395 } else if (scancode == (SCANLO + SIZE(scanmap)) - 1) {
396 NHEVENT_KBD(M('?'));
398 return 0;
400 return DefWindowProc(hWnd, message, wParam, lParam);
401 } break;
403 case WM_COMMAND:
404 /* process commands - menu commands mostly */
405 if (onWMCommand(hWnd, wParam, lParam))
406 return DefWindowProc(hWnd, message, wParam, lParam);
407 else
408 return 0;
410 case WM_MOVE:
411 case WM_SIZE: {
412 WINDOWPLACEMENT wp;
414 mswin_layout_main_window(NULL);
416 wp.length = sizeof(wp);
417 if (GetWindowPlacement(hWnd, &wp)) {
418 GetNHApp()->regMainShowState =
419 (wp.showCmd == SW_SHOWMAXIMIZED ? SW_SHOWMAXIMIZED
420 : SW_SHOWNORMAL);
422 GetNHApp()->regMainMinX = wp.ptMinPosition.x;
423 GetNHApp()->regMainMinY = wp.ptMinPosition.y;
425 GetNHApp()->regMainMaxX = wp.ptMaxPosition.x;
426 GetNHApp()->regMainMaxY = wp.ptMaxPosition.y;
428 GetNHApp()->regMainLeft = wp.rcNormalPosition.left;
429 GetNHApp()->regMainTop = wp.rcNormalPosition.top;
430 GetNHApp()->regMainRight = wp.rcNormalPosition.right;
431 GetNHApp()->regMainBottom = wp.rcNormalPosition.bottom;
433 break;
435 case WM_SETFOCUS:
436 /* if there is a menu window out there -
437 transfer input focus to it */
438 if (IsWindow(GetNHApp()->hPopupWnd)) {
439 SetFocus(GetNHApp()->hPopupWnd);
441 break;
443 case WM_CLOSE: {
444 /* exit gracefully */
445 if (program_state.gameover) {
446 /* assume the user really meant this, as the game is already
447 * over... */
448 /* to make sure we still save bones, just set stop printing flag
450 program_state.stopprint++;
451 NHEVENT_KBD(
452 '\033'); /* and send keyboard input as if user pressed ESC */
453 /* additional code for this is done in menu and rip windows */
454 } else if (!program_state.something_worth_saving) {
455 /* User exited before the game started, e.g. during splash display
457 /* Just get out. */
458 bail((char *) 0);
459 } else {
460 /* prompt user for action */
461 switch (NHMessageBox(hWnd, TEXT("Save?"),
462 MB_YESNOCANCEL | MB_ICONQUESTION)) {
463 case IDYES:
464 #ifdef SAFERHANGUP
465 /* destroy popup window - it has its own loop and we need to
466 return control to NetHack core at this point */
467 if (IsWindow(GetNHApp()->hPopupWnd))
468 SendMessage(GetNHApp()->hPopupWnd, WM_COMMAND, IDCANCEL,
471 /* tell NetHack core that "hangup" is requested */
472 hangup(1);
473 #else
474 NHEVENT_KBD('y');
475 dosave();
476 #endif
477 break;
478 case IDNO:
479 NHEVENT_KBD('q');
480 done(QUIT);
481 break;
482 case IDCANCEL:
483 break;
487 return 0;
489 case WM_DESTROY:
490 /* apparently we never get here
491 TODO: work on exit routines - need to send
492 WM_QUIT somehow */
494 /* clean up */
495 free((PNHMainWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA));
496 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
498 // PostQuitMessage(0);
499 exit(1);
500 break;
502 default:
503 return DefWindowProc(hWnd, message, wParam, lParam);
505 return 0;
508 void
509 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
511 UNREFERENCED_PARAMETER(hWnd);
512 UNREFERENCED_PARAMETER(wParam);
513 UNREFERENCED_PARAMETER(lParam);
515 switch (wParam) {
516 /* new window was just added */
517 case MSNH_MSG_ADDWND: {
518 PMSNHMsgAddWnd msg_param = (PMSNHMsgAddWnd) lParam;
519 HWND child;
521 if (GetNHApp()->windowlist[msg_param->wid].type == NHW_MAP)
522 mswin_select_map_mode(iflags.wc_map_mode);
524 child = GetNHApp()->windowlist[msg_param->wid].win;
525 } break;
529 /* adjust windows to fit main window layout
530 ---------------------------
531 | Status |
532 +-------------------------+
535 | MAP |
538 +-------------------------+
539 | Messages |
540 ---------------------------
542 void
543 mswin_layout_main_window(HWND changed_child)
545 winid i;
546 RECT client_rt, wnd_rect;
547 POINT status_org;
548 SIZE status_size;
549 POINT msg_org;
550 SIZE msg_size;
551 POINT map_org;
552 SIZE map_size;
553 SIZE menu_size;
554 HWND wnd_status, wnd_msg;
555 PNHMainWindow data;
557 if (GetNHApp()->bAutoLayout) {
558 GetClientRect(GetNHApp()->hMainWnd, &client_rt);
559 data = (PNHMainWindow) GetWindowLongPtr(GetNHApp()->hMainWnd,
560 GWLP_USERDATA);
562 /* get sizes of child windows */
563 wnd_status = mswin_hwnd_from_winid(WIN_STATUS);
564 if (IsWindow(wnd_status)) {
565 mswin_status_window_size(wnd_status, &status_size);
566 } else {
567 status_size.cx = status_size.cy = 0;
570 wnd_msg = mswin_hwnd_from_winid(WIN_MESSAGE);
571 if (IsWindow(wnd_msg)) {
572 mswin_message_window_size(wnd_msg, &msg_size);
573 } else {
574 msg_size.cx = msg_size.cy = 0;
577 /* find all menu windows and calculate the size */
578 menu_size.cx = menu_size.cy = 0;
579 for (i = 0; i < MAXWINDOWS; i++) {
580 SIZE tmp_size;
581 if (GetNHApp()->windowlist[i].win
582 && !GetNHApp()->windowlist[i].dead
583 && GetNHApp()->windowlist[i].type == NHW_MENU) {
584 mswin_menu_window_size(GetNHApp()->windowlist[i].win,
585 &tmp_size);
586 menu_size.cx = max(menu_size.cx, tmp_size.cx);
587 menu_size.cy = max(menu_size.cy, tmp_size.cy);
591 /* set window positions */
592 SetRect(&wnd_rect, client_rt.left, client_rt.top, client_rt.right,
593 client_rt.bottom);
594 switch (iflags.wc_align_status) {
595 case ALIGN_LEFT:
596 status_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
597 status_size.cy =
598 (wnd_rect.bottom - wnd_rect.top); // that won't look good
599 status_org.x = wnd_rect.left;
600 status_org.y = wnd_rect.top;
601 wnd_rect.left += status_size.cx;
602 break;
604 case ALIGN_RIGHT:
605 status_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
606 status_size.cy =
607 (wnd_rect.bottom - wnd_rect.top); // that won't look good
608 status_org.x = wnd_rect.right - status_size.cx;
609 status_org.y = wnd_rect.top;
610 wnd_rect.right -= status_size.cx;
611 break;
613 case ALIGN_TOP:
614 status_size.cx = (wnd_rect.right - wnd_rect.left);
615 status_org.x = wnd_rect.left;
616 status_org.y = wnd_rect.top;
617 wnd_rect.top += status_size.cy;
618 break;
620 case ALIGN_BOTTOM:
621 default:
622 status_size.cx = (wnd_rect.right - wnd_rect.left);
623 status_org.x = wnd_rect.left;
624 status_org.y = wnd_rect.bottom - status_size.cy;
625 wnd_rect.bottom -= status_size.cy;
626 break;
629 switch (iflags.wc_align_message) {
630 case ALIGN_LEFT:
631 msg_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
632 msg_size.cy = (wnd_rect.bottom - wnd_rect.top);
633 msg_org.x = wnd_rect.left;
634 msg_org.y = wnd_rect.top;
635 wnd_rect.left += msg_size.cx;
636 break;
638 case ALIGN_RIGHT:
639 msg_size.cx = (wnd_rect.right - wnd_rect.left) / 4;
640 msg_size.cy = (wnd_rect.bottom - wnd_rect.top);
641 msg_org.x = wnd_rect.right - msg_size.cx;
642 msg_org.y = wnd_rect.top;
643 wnd_rect.right -= msg_size.cx;
644 break;
646 case ALIGN_TOP:
647 msg_size.cx = (wnd_rect.right - wnd_rect.left);
648 msg_org.x = wnd_rect.left;
649 msg_org.y = wnd_rect.top;
650 wnd_rect.top += msg_size.cy;
651 break;
653 case ALIGN_BOTTOM:
654 default:
655 msg_size.cx = (wnd_rect.right - wnd_rect.left);
656 msg_org.x = wnd_rect.left;
657 msg_org.y = wnd_rect.bottom - msg_size.cy;
658 wnd_rect.bottom -= msg_size.cy;
659 break;
662 /* map window */
663 map_org.x = wnd_rect.left;
664 map_org.y = wnd_rect.top;
665 map_size.cx = wnd_rect.right - wnd_rect.left;
666 map_size.cy = wnd_rect.bottom - wnd_rect.top;
668 GetNHApp()->rtStatusWindow.left = status_org.x;
669 GetNHApp()->rtStatusWindow.top = status_org.y;
670 GetNHApp()->rtStatusWindow.right = status_org.x + status_size.cx;
671 GetNHApp()->rtStatusWindow.bottom = status_org.y + status_size.cy;
673 GetNHApp()->rtTextWindow.left = map_org.x;
674 GetNHApp()->rtTextWindow.top = map_org.y;
675 GetNHApp()->rtTextWindow.right =
676 map_org.x + (wnd_rect.right - wnd_rect.left);
677 GetNHApp()->rtTextWindow.bottom = map_org.y + map_size.cy;
679 GetNHApp()->rtMapWindow.left = map_org.x;
680 GetNHApp()->rtMapWindow.top = map_org.y;
681 GetNHApp()->rtMapWindow.right = map_org.x + map_size.cx;
682 GetNHApp()->rtMapWindow.bottom = map_org.y + map_size.cy;
684 GetNHApp()->rtMsgWindow.left = msg_org.x;
685 GetNHApp()->rtMsgWindow.top = msg_org.y;
686 GetNHApp()->rtMsgWindow.right = msg_org.x + msg_size.cx;
687 GetNHApp()->rtMsgWindow.bottom = msg_org.y + msg_size.cy;
689 /* map_width/4 < menu_width < map_width*2/3 */
690 GetNHApp()->rtMenuWindow.left =
691 GetNHApp()->rtMapWindow.right
692 - min(map_size.cx * 2 / 3, max(map_size.cx / 4, menu_size.cx));
693 GetNHApp()->rtMenuWindow.top = GetNHApp()->rtMapWindow.top;
694 GetNHApp()->rtMenuWindow.right = GetNHApp()->rtMapWindow.right;
695 GetNHApp()->rtMenuWindow.bottom = GetNHApp()->rtMapWindow.bottom;
697 GetNHApp()->rtInvenWindow.left = GetNHApp()->rtMenuWindow.left;
698 GetNHApp()->rtInvenWindow.top = GetNHApp()->rtMenuWindow.top;
699 GetNHApp()->rtInvenWindow.right = GetNHApp()->rtMenuWindow.right;
700 GetNHApp()->rtInvenWindow.bottom = GetNHApp()->rtMenuWindow.bottom;
702 /* adjust map window size only if perm_invent is set */
703 if (flags.perm_invent)
704 GetNHApp()->rtMapWindow.right = GetNHApp()->rtMenuWindow.left;
707 /* go through the windows list and adjust sizes */
708 for (i = 0; i < MAXWINDOWS; i++) {
709 if (GetNHApp()->windowlist[i].win
710 && !GetNHApp()->windowlist[i].dead) {
711 RECT rt;
712 /* kludge - inventory window should have its own type (same as
713 menu-text
714 as a matter of fact) */
715 if (flags.perm_invent && i == WIN_INVEN) {
716 mswin_get_window_placement(NHW_INVEN, &rt);
717 } else {
718 mswin_get_window_placement(GetNHApp()->windowlist[i].type,
719 &rt);
722 MoveWindow(GetNHApp()->windowlist[i].win, rt.left, rt.top,
723 rt.right - rt.left, rt.bottom - rt.top, TRUE);
726 if (IsWindow(changed_child))
727 SetForegroundWindow(changed_child);
730 LRESULT
731 onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
733 int wmId, wmEvent;
734 PNHMainWindow data;
736 UNREFERENCED_PARAMETER(lParam);
738 data = (PNHMainWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
739 wmId = LOWORD(wParam);
740 wmEvent = HIWORD(wParam);
742 // Parse the menu selections:
743 switch (wmId) {
744 case IDM_ABOUT:
745 mswin_display_splash_window(TRUE);
746 break;
748 case IDM_EXIT:
749 done2();
750 break;
752 case IDM_SAVE:
753 if (!program_state.gameover && !program_state.done_hup)
754 dosave();
755 else
756 MessageBeep(0);
757 break;
759 case IDM_MAP_TILES:
760 case IDM_MAP_ASCII4X6:
761 case IDM_MAP_ASCII6X8:
762 case IDM_MAP_ASCII8X8:
763 case IDM_MAP_ASCII16X8:
764 case IDM_MAP_ASCII7X12:
765 case IDM_MAP_ASCII8X12:
766 case IDM_MAP_ASCII12X16:
767 case IDM_MAP_ASCII16X12:
768 case IDM_MAP_ASCII10X18:
769 mswin_select_map_mode(menuid2mapmode(wmId));
770 break;
772 case IDM_MAP_FIT_TO_SCREEN:
773 if (IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
774 mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
775 ? data->mapAcsiiModeSave
776 : MAP_MODE_TILES);
777 } else {
778 mswin_select_map_mode(IS_MAP_ASCII(iflags.wc_map_mode)
779 ? MAP_MODE_ASCII_FIT_TO_SCREEN
780 : MAP_MODE_TILES_FIT_TO_SCREEN);
782 break;
784 case IDM_SETTING_SCREEN_TO_CLIPBOARD: {
785 char *p;
786 size_t len;
787 HANDLE hglbCopy;
788 char *p_copy;
790 p = nh_compose_ascii_screenshot();
791 if (!p)
792 return 0;
793 len = strlen(p);
795 if (!OpenClipboard(hWnd)) {
796 NHMessageBox(hWnd, TEXT("Cannot open clipboard"),
797 MB_OK | MB_ICONERROR);
798 return 0;
801 EmptyClipboard();
803 hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (len + 1) * sizeof(char));
804 if (hglbCopy == NULL) {
805 CloseClipboard();
806 return FALSE;
809 p_copy = (char *) GlobalLock(hglbCopy);
810 strncpy(p_copy, p, len);
811 p_copy[len] = 0; // null character
812 GlobalUnlock(hglbCopy);
814 SetClipboardData(SYMHANDLING(H_IBM) ? CF_OEMTEXT : CF_TEXT, hglbCopy);
816 CloseClipboard();
818 free(p);
819 } break;
821 case IDM_SETTING_SCREEN_TO_FILE: {
822 OPENFILENAME ofn;
823 TCHAR filename[1024];
824 TCHAR whackdir[MAX_PATH];
825 FILE *pFile;
826 char *text;
827 wchar_t *wtext;
828 int tlen = 0;
830 ZeroMemory(filename, sizeof(filename));
831 ZeroMemory(&ofn, sizeof(ofn));
832 ofn.lStructSize = sizeof(OPENFILENAME);
833 ofn.hwndOwner = hWnd;
834 ofn.hInstance = GetNHApp()->hApp;
835 ofn.lpstrFilter = TEXT("Text Files (*.txt)\x0*.txt\x0")
836 TEXT("All Files (*.*)\x0*.*\x0") TEXT("\x0\x0");
837 ofn.lpstrCustomFilter = NULL;
838 ofn.nMaxCustFilter = 0;
839 ofn.nFilterIndex = 1;
840 ofn.lpstrFile = filename;
841 ofn.nMaxFile = SIZE(filename);
842 ofn.lpstrFileTitle = NULL;
843 ofn.nMaxFileTitle = 0;
844 ofn.lpstrInitialDir = NH_A2W(hackdir, whackdir, MAX_PATH);
845 ofn.lpstrTitle = NULL;
846 ofn.Flags = OFN_LONGNAMES | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
847 ofn.nFileOffset = 0;
848 ofn.nFileExtension = 0;
849 ofn.lpstrDefExt = TEXT("txt");
850 ofn.lCustData = 0;
851 ofn.lpfnHook = 0;
852 ofn.lpTemplateName = 0;
854 if (!GetSaveFileName(&ofn))
855 return FALSE;
857 text = nh_compose_ascii_screenshot();
858 if (!text)
859 return FALSE;
861 pFile = _tfopen(filename, TEXT("wt+,ccs=UTF-8"));
862 if (!pFile) {
863 TCHAR buf[4096];
864 _stprintf(buf, TEXT("Cannot open %s for writing!"), filename);
865 NHMessageBox(hWnd, buf, MB_OK | MB_ICONERROR);
866 free(text);
867 return FALSE;
870 tlen = strlen(text);
871 wtext = (wchar_t *) malloc(tlen * sizeof(wchar_t));
872 if (!wtext)
873 panic("out of memory");
874 MultiByteToWideChar(NH_CODEPAGE, 0, text, -1, wtext, tlen);
875 fwrite(wtext, tlen * sizeof(wchar_t), 1, pFile);
876 fclose(pFile);
877 free(text);
878 free(wtext);
879 } break;
881 case IDM_NHMODE: {
882 GetNHApp()->regNetHackMode = GetNHApp()->regNetHackMode ? 0 : 1;
883 mswin_menu_check_intf_mode();
884 mswin_apply_window_style_all();
885 break;
887 case IDM_CLEARSETTINGS: {
888 mswin_destroy_reg();
889 /* Notify the user that windows settings will not be saved this time.
891 NHMessageBox(GetNHApp()->hMainWnd,
892 TEXT("Your Windows Settings will not be stored when you "
893 "exit this time."),
894 MB_OK | MB_ICONINFORMATION);
895 break;
898 case IDM_SETTING_AUTOLAYOUT:
899 GetNHApp()->bAutoLayout = !GetNHApp()->bAutoLayout;
900 mswin_layout_main_window(NULL);
902 /* Update menu item check-mark */
903 CheckMenuItem(GetMenu(GetNHApp()->hMainWnd), IDM_SETTING_AUTOLAYOUT,
904 GetNHApp()->bAutoLayout ? MF_CHECKED : MF_UNCHECKED);
905 break;
907 case IDM_SETTING_LOCKWINDOWS:
908 nhlock_windows(!GetNHApp()->bWindowsLocked);
909 break;
911 case IDM_HELP_LONG:
912 display_file(HELP, TRUE);
913 break;
915 case IDM_HELP_COMMANDS:
916 display_file(SHELP, TRUE);
917 break;
919 case IDM_HELP_HISTORY:
920 (void) dohistory();
921 break;
923 case IDM_HELP_INFO_CHAR:
924 (void) dowhatis();
925 break;
927 case IDM_HELP_INFO_KEY:
928 (void) dowhatdoes();
929 break;
931 case IDM_HELP_OPTIONS:
932 option_help();
933 break;
935 case IDM_HELP_OPTIONS_LONG:
936 display_file(OPTIONFILE, TRUE);
937 break;
939 case IDM_HELP_EXTCMD:
940 (void) doextlist();
941 break;
943 case IDM_HELP_LICENSE:
944 display_file(LICENSE, TRUE);
945 break;
947 case IDM_HELP_PORTHELP:
948 display_file(PORT_HELP, TRUE);
949 break;
951 default:
952 return 1;
954 return 0;
957 // Mesage handler for about box.
958 LRESULT CALLBACK
959 About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
961 char buf[BUFSZ];
962 TCHAR wbuf[BUFSZ];
963 RECT main_rt, dlg_rt;
964 SIZE dlg_sz;
966 UNREFERENCED_PARAMETER(lParam);
968 switch (message) {
969 case WM_INITDIALOG:
970 getversionstring(buf);
971 SetDlgItemText(hDlg, IDC_ABOUT_VERSION,
972 NH_A2W(buf, wbuf, sizeof(wbuf)));
974 SetDlgItemText(hDlg, IDC_ABOUT_COPYRIGHT,
975 NH_A2W(COPYRIGHT_BANNER_A "\n" COPYRIGHT_BANNER_B
976 "\n" COPYRIGHT_BANNER_C
977 "\n" COPYRIGHT_BANNER_D,
978 wbuf, BUFSZ));
980 /* center dialog in the main window */
981 GetWindowRect(GetNHApp()->hMainWnd, &main_rt);
982 GetWindowRect(hDlg, &dlg_rt);
983 dlg_sz.cx = dlg_rt.right - dlg_rt.left;
984 dlg_sz.cy = dlg_rt.bottom - dlg_rt.top;
986 dlg_rt.left = (main_rt.left + main_rt.right - dlg_sz.cx) / 2;
987 dlg_rt.right = dlg_rt.left + dlg_sz.cx;
988 dlg_rt.top = (main_rt.top + main_rt.bottom - dlg_sz.cy) / 2;
989 dlg_rt.bottom = dlg_rt.top + dlg_sz.cy;
990 MoveWindow(hDlg, (main_rt.left + main_rt.right - dlg_sz.cx) / 2,
991 (main_rt.top + main_rt.bottom - dlg_sz.cy) / 2, dlg_sz.cx,
992 dlg_sz.cy, TRUE);
994 return TRUE;
996 case WM_COMMAND:
997 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
998 EndDialog(hDlg, LOWORD(wParam));
999 return TRUE;
1001 break;
1003 return FALSE;
1006 void
1007 mswin_menu_check_intf_mode()
1009 HMENU hMenu = GetMenu(GetNHApp()->hMainWnd);
1011 if (GetNHApp()->regNetHackMode)
1012 CheckMenuItem(hMenu, IDM_NHMODE, MF_CHECKED);
1013 else
1014 CheckMenuItem(hMenu, IDM_NHMODE, MF_UNCHECKED);
1017 void
1018 mswin_select_map_mode(int mode)
1020 PNHMainWindow data;
1021 winid map_id;
1023 map_id = WIN_MAP;
1024 data =
1025 (PNHMainWindow) GetWindowLongPtr(GetNHApp()->hMainWnd, GWLP_USERDATA);
1027 /* override for Rogue level */
1028 if (Is_rogue_level(&u.uz) && !IS_MAP_ASCII(mode))
1029 return;
1031 /* set map mode menu mark */
1032 if (IS_MAP_ASCII(mode)) {
1033 CheckMenuRadioItem(
1034 GetMenu(GetNHApp()->hMainWnd), IDM_MAP_TILES, IDM_MAP_ASCII10X18,
1035 mapmode2menuid(IS_MAP_FIT_TO_SCREEN(mode) ? data->mapAcsiiModeSave
1036 : mode),
1037 MF_BYCOMMAND);
1038 } else {
1039 CheckMenuRadioItem(GetMenu(GetNHApp()->hMainWnd), IDM_MAP_TILES,
1040 IDM_MAP_ASCII10X18, mapmode2menuid(MAP_MODE_TILES),
1041 MF_BYCOMMAND);
1044 /* set fit-to-screen mode mark */
1045 CheckMenuItem(GetMenu(GetNHApp()->hMainWnd), IDM_MAP_FIT_TO_SCREEN,
1046 MF_BYCOMMAND | (IS_MAP_FIT_TO_SCREEN(mode) ? MF_CHECKED
1047 : MF_UNCHECKED));
1049 if (IS_MAP_ASCII(iflags.wc_map_mode)
1050 && !IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) {
1051 data->mapAcsiiModeSave = iflags.wc_map_mode;
1054 iflags.wc_map_mode = mode;
1057 ** first, check if WIN_MAP has been inialized.
1058 ** If not - attempt to retrieve it by type, then check it again
1060 if (map_id == WIN_ERR)
1061 map_id = mswin_winid_from_type(NHW_MAP);
1062 if (map_id != WIN_ERR)
1063 mswin_map_mode(mswin_hwnd_from_winid(map_id), mode);
1066 static struct t_menu2mapmode {
1067 int menuID;
1068 int mapMode;
1069 } _menu2mapmode[] = { { IDM_MAP_TILES, MAP_MODE_TILES },
1070 { IDM_MAP_ASCII4X6, MAP_MODE_ASCII4x6 },
1071 { IDM_MAP_ASCII6X8, MAP_MODE_ASCII6x8 },
1072 { IDM_MAP_ASCII8X8, MAP_MODE_ASCII8x8 },
1073 { IDM_MAP_ASCII16X8, MAP_MODE_ASCII16x8 },
1074 { IDM_MAP_ASCII7X12, MAP_MODE_ASCII7x12 },
1075 { IDM_MAP_ASCII8X12, MAP_MODE_ASCII8x12 },
1076 { IDM_MAP_ASCII12X16, MAP_MODE_ASCII12x16 },
1077 { IDM_MAP_ASCII16X12, MAP_MODE_ASCII16x12 },
1078 { IDM_MAP_ASCII10X18, MAP_MODE_ASCII10x18 },
1079 { IDM_MAP_FIT_TO_SCREEN, MAP_MODE_ASCII_FIT_TO_SCREEN },
1080 { -1, -1 } };
1083 menuid2mapmode(int menuid)
1085 struct t_menu2mapmode *p;
1086 for (p = _menu2mapmode; p->mapMode != -1; p++)
1087 if (p->menuID == menuid)
1088 return p->mapMode;
1089 return -1;
1093 mapmode2menuid(int map_mode)
1095 struct t_menu2mapmode *p;
1096 for (p = _menu2mapmode; p->mapMode != -1; p++)
1097 if (p->mapMode == map_mode)
1098 return p->menuID;
1099 return -1;
1102 void
1103 nhlock_windows(BOOL lock)
1105 /* update menu */
1106 GetNHApp()->bWindowsLocked = lock;
1107 CheckMenuItem(GetMenu(GetNHApp()->hMainWnd), IDM_SETTING_LOCKWINDOWS,
1108 MF_BYCOMMAND | (lock ? MF_CHECKED : MF_UNCHECKED));
1110 /* restyle windows */
1111 mswin_apply_window_style_all();
1114 void
1115 mswin_apply_window_style(HWND hwnd) {
1116 DWORD style = 0, exstyle = 0;
1118 style = GetWindowLong(hwnd, GWL_STYLE);
1119 exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
1121 if( !GetNHApp()->bWindowsLocked ) {
1122 style = WS_CHILD|WS_CLIPSIBLINGS|WS_CAPTION|WS_SIZEBOX|(style & (WS_VISIBLE|WS_VSCROLL|WS_HSCROLL));
1123 exstyle = WS_EX_WINDOWEDGE;
1124 } else if (GetNHApp()->regNetHackMode) {
1125 /* do away borders */
1126 style = WS_CHILD|WS_CLIPSIBLINGS|(style & (WS_VISIBLE|WS_VSCROLL|WS_HSCROLL));
1127 exstyle = 0;
1128 } else {
1129 style = WS_CHILD|WS_CLIPSIBLINGS|WS_THICKFRAME|(style & (WS_VISIBLE|WS_VSCROLL|WS_HSCROLL));
1130 exstyle = WS_EX_WINDOWEDGE;
1133 SetWindowLong(hwnd, GWL_STYLE, style);
1134 SetWindowLong(hwnd, GWL_EXSTYLE, exstyle);
1135 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1136 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOCOPYBITS);
1139 void
1140 mswin_apply_window_style_all() {
1141 int i;
1142 for (i = 0; i < MAXWINDOWS; i++) {
1143 if (IsWindow(GetNHApp()->windowlist[i].win)
1144 && !GetNHApp()->windowlist[i].dead) {
1145 mswin_apply_window_style(GetNHApp()->windowlist[i].win);
1148 mswin_layout_main_window(NULL);
1151 // returns strdup() created pointer - callee assumes the ownership
1152 #define TEXT_BUFFER_SIZE 4096
1153 char *
1154 nh_compose_ascii_screenshot()
1156 char *retval;
1157 PMSNHMsgGetText text;
1159 retval = (char *) malloc(3 * TEXT_BUFFER_SIZE);
1161 text =
1162 (PMSNHMsgGetText) malloc(sizeof(MSNHMsgGetText) + TEXT_BUFFER_SIZE);
1163 text->max_size =
1164 TEXT_BUFFER_SIZE
1165 - 1; /* make sure we always have 0 at the end of the buffer */
1167 ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
1168 SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_MSNH_COMMAND,
1169 (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
1170 strcpy(retval, text->buffer);
1172 ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
1173 SendMessage(mswin_hwnd_from_winid(WIN_MAP), WM_MSNH_COMMAND,
1174 (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
1175 strcat(retval, text->buffer);
1177 ZeroMemory(text->buffer, TEXT_BUFFER_SIZE);
1178 SendMessage(mswin_hwnd_from_winid(WIN_STATUS), WM_MSNH_COMMAND,
1179 (WPARAM) MSNH_MSG_GETTEXT, (LPARAM) text);
1180 strcat(retval, text->buffer);
1182 free(text);
1183 return retval;