OSDN Git Service

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