OSDN Git Service

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