NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / win / win32 / mhmenu.c
blob5742d858f48aa9228f047cda3f67896e133858eb
1 /* aNetHack 0.0.1 mhmenu.c $ANH-Date: 1432512811 2015/05/25 00:13:31 $ $ANH-Branch: master $:$ANH-Revision: 1.48 $ */
2 /* Copyright (c) Alex Kompel, 2002 */
3 /* aNetHack may be freely redistributed. See license for details. */
5 #include "winMS.h"
6 #include <assert.h>
7 #include "resource.h"
8 #include "mhmenu.h"
9 #include "mhmain.h"
10 #include "mhmsg.h"
11 #include "mhfont.h"
12 #include "mhdlg.h"
14 #define MENU_MARGIN 0
15 #define NHMENU_STR_SIZE BUFSZ
16 #define MIN_TABSTOP_SIZE 0
17 #define NUMTABS 15
18 #define TAB_SEPARATION 10 /* pixels between each tab stop */
20 #define DEFAULT_COLOR_BG_TEXT COLOR_WINDOW
21 #define DEFAULT_COLOR_FG_TEXT COLOR_WINDOWTEXT
22 #define DEFAULT_COLOR_BG_MENU COLOR_WINDOW
23 #define DEFAULT_COLOR_FG_MENU COLOR_WINDOWTEXT
25 typedef struct mswin_menu_item {
26 int glyph;
27 ANY_P identifier;
28 CHAR_P accelerator;
29 CHAR_P group_accel;
30 int attr;
31 char str[NHMENU_STR_SIZE];
32 BOOLEAN_P presel;
33 int count;
34 BOOL has_focus;
35 } NHMenuItem, *PNHMenuItem;
37 typedef struct mswin_anethack_menu_window {
38 int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */
39 int how; /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */
41 union {
42 struct menu_list {
43 int size; /* number of items in items[] */
44 int allocated; /* number of allocated slots in items[] */
45 PNHMenuItem items; /* menu items */
46 char gacc[QBUFSZ]; /* group accelerators */
47 BOOL counting; /* counting flag */
48 char prompt[QBUFSZ]; /* menu prompt */
49 int tab_stop_size[NUMTABS]; /* tabstops to align option values */
50 int menu_cx; /* menu width */
51 } menu;
53 struct menu_text {
54 TCHAR *text;
55 SIZE text_box_size;
56 } text;
58 int result;
59 int done;
61 HBITMAP bmpChecked;
62 HBITMAP bmpCheckedCount;
63 HBITMAP bmpNotChecked;
65 BOOL is_active;
66 } NHMenuWindow, *PNHMenuWindow;
68 extern short glyph2tile[];
70 static WNDPROC wndProcListViewOrig = NULL;
71 static WNDPROC editControlWndProc = NULL;
73 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj != NULL)
74 #define NHMENU_IS_SELECTED(item) ((item).count != 0)
75 #define NHMENU_HAS_GLYPH(item) ((item).glyph != NO_GLYPH)
77 INT_PTR CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM);
78 LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM);
79 LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM);
80 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
81 static BOOL onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
82 static BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
83 static void LayoutMenu(HWND hwnd);
84 static void SetMenuType(HWND hwnd, int type);
85 static void SetMenuListType(HWND hwnd, int now);
86 static HWND GetMenuControl(HWND hwnd);
87 static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item,
88 int count);
89 static void reset_menu_count(HWND hwndList, PNHMenuWindow data);
90 static BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch);
92 /*-----------------------------------------------------------------------------*/
93 HWND
94 mswin_init_menu_window(int type)
96 HWND ret;
97 RECT rt;
99 /* get window position */
100 if (GetNHApp()->bAutoLayout) {
101 SetRect(&rt, 0, 0, 0, 0);
102 } else {
103 mswin_get_window_placement(NHW_MENU, &rt);
106 /* create menu window object */
107 ret = CreateDialog(GetNHApp()->hApp, MAKEINTRESOURCE(IDD_MENU),
108 GetNHApp()->hMainWnd, MenuWndProc);
109 if (!ret) {
110 panic("Cannot create menu window");
113 /* move it in the predefined position */
114 if (!GetNHApp()->bAutoLayout) {
115 MoveWindow(ret, rt.left, rt.top, rt.right - rt.left,
116 rt.bottom - rt.top, TRUE);
119 /* Set window caption */
120 SetWindowText(ret, "Menu/Text");
122 mswin_apply_window_style(ret);
124 SetMenuType(ret, type);
125 return ret;
127 /*-----------------------------------------------------------------------------*/
129 mswin_menu_window_select_menu(HWND hWnd, int how, MENU_ITEM_P **_selected,
130 BOOL activate)
132 PNHMenuWindow data;
133 int ret_val;
134 MENU_ITEM_P *selected = NULL;
135 int i;
136 char *ap;
138 assert(_selected != NULL);
139 *_selected = NULL;
140 ret_val = -1;
142 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
144 /* force activate for certain menu types */
145 if (data->type == MENU_TYPE_MENU
146 && (how == PICK_ONE || how == PICK_ANY)) {
147 activate = TRUE;
150 data->is_active = activate && !GetNHApp()->regaNetHackMode;
152 /* set menu type */
153 SetMenuListType(hWnd, how);
155 /* Ok, now give items a unique accelerators */
156 if (data->type == MENU_TYPE_MENU) {
157 char next_char = 'a';
159 data->menu.gacc[0] = '\0';
160 ap = data->menu.gacc;
161 for (i = 0; i < data->menu.size; i++) {
162 if (data->menu.items[i].accelerator != 0) {
163 next_char = (char) (data->menu.items[i].accelerator + 1);
164 } else if (NHMENU_IS_SELECTABLE(data->menu.items[i])) {
165 if ((next_char >= 'a' && next_char <= 'z')
166 || (next_char >= 'A' && next_char <= 'Z')) {
167 data->menu.items[i].accelerator = next_char;
168 } else {
169 if (next_char > 'z')
170 next_char = 'A';
171 else if (next_char > 'Z')
172 next_char = 'a';
174 data->menu.items[i].accelerator = next_char;
177 next_char++;
181 /* collect group accelerators */
182 for (i = 0; i < data->menu.size; i++) {
183 if (data->how != PICK_NONE) {
184 if (data->menu.items[i].group_accel
185 && !strchr(data->menu.gacc,
186 data->menu.items[i].group_accel)) {
187 *ap++ = data->menu.items[i].group_accel;
188 *ap = '\x0';
193 reset_menu_count(NULL, data);
196 LayoutMenu(hWnd); // show dialog buttons
198 if (activate) {
199 mswin_popup_display(hWnd, &data->done);
200 } else {
201 SetFocus(GetNHApp()->hMainWnd);
202 mswin_layout_main_window(hWnd);
205 /* get the result */
206 if (data->result != -1) {
207 if (how == PICK_NONE) {
208 if (data->result >= 0)
209 ret_val = 0;
210 else
211 ret_val = -1;
212 } else if (how == PICK_ONE || how == PICK_ANY) {
213 /* count selected items */
214 ret_val = 0;
215 for (i = 0; i < data->menu.size; i++) {
216 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
217 && NHMENU_IS_SELECTED(data->menu.items[i])) {
218 ret_val++;
221 if (ret_val > 0) {
222 int sel_ind;
224 selected =
225 (MENU_ITEM_P *) malloc(ret_val * sizeof(MENU_ITEM_P));
226 if (!selected)
227 panic("out of memory");
229 sel_ind = 0;
230 for (i = 0; i < data->menu.size; i++) {
231 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
232 && NHMENU_IS_SELECTED(data->menu.items[i])) {
233 selected[sel_ind].item =
234 data->menu.items[i].identifier;
235 selected[sel_ind].count = data->menu.items[i].count;
236 sel_ind++;
239 ret_val = sel_ind;
240 *_selected = selected;
245 if (activate) {
246 data->is_active = FALSE;
247 LayoutMenu(hWnd); // hide dialog buttons
248 mswin_popup_destroy(hWnd);
250 return ret_val;
252 /*-----------------------------------------------------------------------------*/
253 INT_PTR CALLBACK
254 MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
256 PNHMenuWindow data;
257 HWND control;
258 HDC hdc;
259 TCHAR title[MAX_LOADSTRING];
261 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
262 switch (message) {
263 case WM_INITDIALOG:
264 data = (PNHMenuWindow) malloc(sizeof(NHMenuWindow));
265 ZeroMemory(data, sizeof(NHMenuWindow));
266 data->type = MENU_TYPE_TEXT;
267 data->how = PICK_NONE;
268 data->result = 0;
269 data->done = 0;
270 data->bmpChecked =
271 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL));
272 data->bmpCheckedCount =
273 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT));
274 data->bmpNotChecked =
275 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL));
276 data->is_active = FALSE;
277 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
279 /* set font for the text cotrol */
280 control = GetDlgItem(hWnd, IDC_MENU_TEXT);
281 hdc = GetDC(control);
282 SendMessage(control, WM_SETFONT,
283 (WPARAM) mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE),
284 (LPARAM) 0);
285 ReleaseDC(control, hdc);
287 /* subclass edit control */
288 editControlWndProc =
289 (WNDPROC) GetWindowLongPtr(control, GWLP_WNDPROC);
290 SetWindowLongPtr(control, GWLP_WNDPROC, (LONG_PTR) NHMenuTextWndProc);
292 /* Even though the dialog has no caption, you can still set the title
293 which shows on Alt-Tab */
294 LoadString(GetNHApp()->hApp, IDS_APP_TITLE, title, MAX_LOADSTRING);
295 SetWindowText(hWnd, title);
297 /* set focus to text control for now */
298 SetFocus(control);
299 return FALSE;
301 case WM_MSNH_COMMAND:
302 onMSNHCommand(hWnd, wParam, lParam);
303 break;
305 case WM_SIZE: {
306 RECT rt;
307 LayoutMenu(hWnd);
308 GetWindowRect(hWnd, &rt);
309 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
310 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
311 if (flags.perm_invent && mswin_winid_from_handle(hWnd) == WIN_INVEN)
312 mswin_update_window_placement(NHW_INVEN, &rt);
313 else
314 mswin_update_window_placement(NHW_MENU, &rt);
316 return FALSE;
318 case WM_MOVE: {
319 RECT rt;
320 GetWindowRect(hWnd, &rt);
321 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
322 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
323 if (flags.perm_invent && mswin_winid_from_handle(hWnd) == WIN_INVEN)
324 mswin_update_window_placement(NHW_INVEN, &rt);
325 else
326 mswin_update_window_placement(NHW_MENU, &rt);
328 return FALSE;
330 case WM_CLOSE:
331 if (program_state.gameover) {
332 data->result = -1;
333 data->done = 1;
334 program_state.stopprint++;
335 return TRUE;
336 } else
337 return FALSE;
339 case WM_COMMAND: {
340 switch (LOWORD(wParam)) {
341 case IDCANCEL:
342 if (data->type == MENU_TYPE_MENU
343 && (data->how == PICK_ONE || data->how == PICK_ANY)
344 && data->menu.counting) {
345 HWND list;
346 int i;
348 /* reset counter if counting is in progress */
349 list = GetMenuControl(hWnd);
350 i = ListView_GetNextItem(list, -1, LVNI_FOCUSED);
351 if (i >= 0) {
352 SelectMenuItem(list, data, i, 0);
354 return TRUE;
355 } else {
356 data->result = -1;
357 data->done = 1;
359 return TRUE;
361 case IDOK:
362 data->done = 1;
363 data->result = 0;
364 return TRUE;
366 } break;
368 case WM_NOTIFY: {
369 LPNMHDR lpnmhdr = (LPNMHDR) lParam;
370 switch (LOWORD(wParam)) {
371 case IDC_MENU_LIST: {
372 if (!data || data->type != MENU_TYPE_MENU)
373 break;
375 switch (lpnmhdr->code) {
376 case LVN_ITEMACTIVATE: {
377 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
378 if (data->how == PICK_ONE) {
379 if (lpnmlv->iItem >= 0 && lpnmlv->iItem < data->menu.size
380 && NHMENU_IS_SELECTABLE(
381 data->menu.items[lpnmlv->iItem])) {
382 SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
383 lpnmlv->iItem, -1);
384 data->done = 1;
385 data->result = 0;
386 return TRUE;
389 } break;
391 case NM_CLICK: {
392 LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW) lParam;
393 if (lpnmitem->iItem == -1)
394 return 0;
395 if (data->how == PICK_ANY) {
396 SelectMenuItem(
397 lpnmitem->hdr.hwndFrom, data, lpnmitem->iItem,
398 NHMENU_IS_SELECTED(data->menu.items[lpnmitem->iItem])
400 : -1);
402 } break;
404 case LVN_ITEMCHANGED: {
405 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
406 if (lpnmlv->iItem == -1)
407 return 0;
408 if (!(lpnmlv->uChanged & LVIF_STATE))
409 return 0;
411 if (data->how == PICK_ONE || data->how == PICK_ANY) {
412 data->menu.items[lpnmlv->iItem].has_focus =
413 !!(lpnmlv->uNewState & LVIS_FOCUSED);
414 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
415 lpnmlv->iItem);
418 /* update count for single-selection menu (follow the listview
419 * selection) */
420 if (data->how == PICK_ONE) {
421 if (lpnmlv->uNewState & LVIS_SELECTED) {
422 SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
423 lpnmlv->iItem, -1);
427 /* check item focus */
428 if (data->how == PICK_ONE || data->how == PICK_ANY) {
429 data->menu.items[lpnmlv->iItem].has_focus =
430 !!(lpnmlv->uNewState & LVIS_FOCUSED);
431 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
432 lpnmlv->iItem);
434 } break;
436 case NM_KILLFOCUS:
437 reset_menu_count(lpnmhdr->hwndFrom, data);
438 break;
440 } break;
442 } break;
444 case WM_SETFOCUS:
445 if (hWnd != GetNHApp()->hPopupWnd) {
446 SetFocus(GetNHApp()->hMainWnd);
447 } else {
448 if (IsWindow(GetMenuControl(hWnd)))
449 SetFocus(GetMenuControl(hWnd));
451 return FALSE;
453 case WM_MEASUREITEM:
454 if (wParam == IDC_MENU_LIST)
455 return onMeasureItem(hWnd, wParam, lParam);
456 else
457 return FALSE;
459 case WM_DRAWITEM:
460 if (wParam == IDC_MENU_LIST)
461 return onDrawItem(hWnd, wParam, lParam);
462 else
463 return FALSE;
465 case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */
466 HDC hdcEdit = (HDC) wParam;
467 HWND hwndEdit = (HWND) lParam;
468 if (hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT)) {
469 SetBkColor(hdcEdit, text_bg_brush ? text_bg_color
470 : (COLORREF) GetSysColor(
471 DEFAULT_COLOR_BG_TEXT));
472 SetTextColor(hdcEdit, text_fg_brush ? text_fg_color
473 : (COLORREF) GetSysColor(
474 DEFAULT_COLOR_FG_TEXT));
475 return (INT_PTR)(text_bg_brush
476 ? text_bg_brush
477 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
480 return FALSE;
482 case WM_CTLCOLORDLG:
483 return (INT_PTR)(text_bg_brush
484 ? text_bg_brush
485 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
487 case WM_DESTROY:
488 if (data) {
489 DeleteObject(data->bmpChecked);
490 DeleteObject(data->bmpCheckedCount);
491 DeleteObject(data->bmpNotChecked);
492 if (data->type == MENU_TYPE_TEXT) {
493 if (data->text.text)
494 free(data->text.text);
496 free(data);
497 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
499 return TRUE;
501 return FALSE;
503 /*-----------------------------------------------------------------------------*/
504 void
505 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
507 PNHMenuWindow data;
509 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
510 switch (wParam) {
511 case MSNH_MSG_PUTSTR: {
512 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam;
513 HWND text_view;
514 TCHAR wbuf[BUFSZ];
515 size_t text_size;
516 RECT text_rt;
517 HGDIOBJ saveFont;
518 HDC hdc;
520 if (data->type != MENU_TYPE_TEXT)
521 SetMenuType(hWnd, MENU_TYPE_TEXT);
523 if (!data->text.text) {
524 text_size = strlen(msg_data->text) + 4;
525 data->text.text =
526 (TCHAR *) malloc(text_size * sizeof(data->text.text[0]));
527 ZeroMemory(data->text.text,
528 text_size * sizeof(data->text.text[0]));
529 } else {
530 text_size = _tcslen(data->text.text) + strlen(msg_data->text) + 4;
531 data->text.text = (TCHAR *) realloc(
532 data->text.text, text_size * sizeof(data->text.text[0]));
534 if (!data->text.text)
535 break;
537 _tcscat(data->text.text, NH_A2W(msg_data->text, wbuf, BUFSZ));
538 _tcscat(data->text.text, TEXT("\r\n"));
540 text_view = GetDlgItem(hWnd, IDC_MENU_TEXT);
541 if (!text_view)
542 panic("cannot get text view window");
543 SetWindowText(text_view, data->text.text);
545 /* calculate dimensions of the added line of text */
546 hdc = GetDC(text_view);
547 saveFont =
548 SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE));
549 SetRect(&text_rt, 0, 0, 0, 0);
550 DrawText(hdc, msg_data->text, strlen(msg_data->text), &text_rt,
551 DT_CALCRECT | DT_TOP | DT_LEFT | DT_NOPREFIX
552 | DT_SINGLELINE);
553 data->text.text_box_size.cx =
554 max(text_rt.right - text_rt.left, data->text.text_box_size.cx);
555 data->text.text_box_size.cy += text_rt.bottom - text_rt.top;
556 SelectObject(hdc, saveFont);
557 ReleaseDC(text_view, hdc);
558 } break;
560 case MSNH_MSG_STARTMENU: {
561 int i;
562 if (data->type != MENU_TYPE_MENU)
563 SetMenuType(hWnd, MENU_TYPE_MENU);
565 if (data->menu.items)
566 free(data->menu.items);
567 data->how = PICK_NONE;
568 data->menu.items = NULL;
569 data->menu.size = 0;
570 data->menu.allocated = 0;
571 data->done = 0;
572 data->result = 0;
573 for (i = 0; i < NUMTABS; ++i)
574 data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
575 } break;
577 case MSNH_MSG_ADDMENU: {
578 PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu) lParam;
579 char *p, *p1;
580 int new_item;
581 HDC hDC;
582 int column;
583 HFONT saveFont;
584 LONG menuitemwidth = 0;
585 TEXTMETRIC tm;
587 if (data->type != MENU_TYPE_MENU)
588 break;
589 if (strlen(msg_data->str) == 0)
590 break;
592 if (data->menu.size == data->menu.allocated) {
593 data->menu.allocated += 10;
594 data->menu.items = (PNHMenuItem) realloc(
595 data->menu.items, data->menu.allocated * sizeof(NHMenuItem));
598 new_item = data->menu.size;
599 ZeroMemory(&data->menu.items[new_item],
600 sizeof(data->menu.items[new_item]));
601 data->menu.items[new_item].glyph = msg_data->glyph;
602 data->menu.items[new_item].identifier = *msg_data->identifier;
603 data->menu.items[new_item].accelerator = msg_data->accelerator;
604 data->menu.items[new_item].group_accel = msg_data->group_accel;
605 data->menu.items[new_item].attr = msg_data->attr;
606 strncpy(data->menu.items[new_item].str, msg_data->str,
607 NHMENU_STR_SIZE);
608 data->menu.items[new_item].presel = msg_data->presel;
610 /* calculate tabstop size */
611 hDC = GetDC(hWnd);
612 saveFont = SelectObject(
613 hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE));
614 GetTextMetrics(hDC, &tm);
615 p1 = data->menu.items[new_item].str;
616 p = strchr(data->menu.items[new_item].str, '\t');
617 column = 0;
618 for (;;) {
619 TCHAR wbuf[BUFSZ];
620 RECT drawRect;
621 SetRect(&drawRect, 0, 0, 1, 1);
622 if (p != NULL)
623 *p = '\0'; /* for time being, view tab field as zstring */
624 DrawText(hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect,
625 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS
626 | DT_SINGLELINE);
627 data->menu.tab_stop_size[column] =
628 max(data->menu.tab_stop_size[column],
629 drawRect.right - drawRect.left);
631 menuitemwidth += data->menu.tab_stop_size[column];
633 if (p != NULL)
634 *p = '\t';
635 else /* last string so, */
636 break;
638 /* add the separation only when not the last item */
639 /* in the last item, we break out of the loop, in the statement
640 * just above */
641 menuitemwidth += TAB_SEPARATION;
643 ++column;
644 p1 = p + 1;
645 p = strchr(p1, '\t');
647 SelectObject(hDC, saveFont);
648 ReleaseDC(hWnd, hDC);
650 /* calculate new menu width */
651 data->menu.menu_cx =
652 max(data->menu.menu_cx,
653 2 * TILE_X + menuitemwidth
654 + (tm.tmAveCharWidth + tm.tmOverhang) * 12);
656 /* increment size */
657 data->menu.size++;
658 } break;
660 case MSNH_MSG_ENDMENU: {
661 PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu) lParam;
662 if (msg_data->text) {
663 strncpy(data->menu.prompt, msg_data->text,
664 sizeof(data->menu.prompt) - 1);
665 } else {
666 ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
668 } break;
671 /*-----------------------------------------------------------------------------*/
672 void
673 LayoutMenu(HWND hWnd)
675 PNHMenuWindow data;
676 HWND menu_ok;
677 HWND menu_cancel;
678 RECT clrt, rt;
679 POINT pt_elem, pt_ok, pt_cancel;
680 SIZE sz_elem, sz_ok, sz_cancel;
682 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
683 menu_ok = GetDlgItem(hWnd, IDOK);
684 menu_cancel = GetDlgItem(hWnd, IDCANCEL);
686 /* get window coordinates */
687 GetClientRect(hWnd, &clrt);
689 // OK button
690 if (data->is_active) {
691 GetWindowRect(menu_ok, &rt);
692 if (data->type == MENU_TYPE_TEXT
693 || (data->type == MENU_TYPE_MENU && data->how == PICK_NONE)) {
694 sz_ok.cx = (clrt.right - clrt.left) - 2 * MENU_MARGIN;
695 } else {
696 sz_ok.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
698 sz_ok.cy = rt.bottom - rt.top;
699 pt_ok.x = clrt.left + MENU_MARGIN;
700 pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy;
701 ShowWindow(menu_ok, SW_SHOW);
702 MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE);
703 } else {
704 sz_ok.cx = sz_ok.cy = 0;
705 pt_ok.x = pt_ok.y = 0;
706 ShowWindow(menu_ok, SW_HIDE);
709 // CANCEL button
710 if (data->is_active
711 && !(data->type == MENU_TYPE_TEXT
712 || (data->type == MENU_TYPE_MENU && data->how == PICK_NONE))) {
713 GetWindowRect(menu_ok, &rt);
714 sz_cancel.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
715 pt_cancel.x = (clrt.left + clrt.right) / 2 + MENU_MARGIN;
716 sz_cancel.cy = rt.bottom - rt.top;
717 pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy;
718 ShowWindow(menu_cancel, SW_SHOW);
719 MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx,
720 sz_cancel.cy, TRUE);
721 } else {
722 sz_cancel.cx = sz_cancel.cy = 0;
723 pt_cancel.x = pt_cancel.y = 0;
724 ShowWindow(menu_cancel, SW_HIDE);
727 // main menu control
728 pt_elem.x = clrt.left + MENU_MARGIN;
729 pt_elem.y = clrt.top + MENU_MARGIN;
730 sz_elem.cx = (clrt.right - clrt.left) - 2 * MENU_MARGIN;
731 if (data->is_active) {
732 sz_elem.cy = (clrt.bottom - clrt.top) - max(sz_ok.cy, sz_cancel.cy)
733 - 3 * MENU_MARGIN;
734 } else {
735 sz_elem.cy = (clrt.bottom - clrt.top) - 2 * MENU_MARGIN;
738 if (data->type == MENU_TYPE_MENU) {
739 ListView_SetColumnWidth(
740 GetMenuControl(hWnd), 0,
741 max(clrt.right - clrt.left - GetSystemMetrics(SM_CXVSCROLL),
742 data->menu.menu_cx));
745 MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx,
746 sz_elem.cy, TRUE);
748 /*-----------------------------------------------------------------------------*/
749 void
750 SetMenuType(HWND hWnd, int type)
752 PNHMenuWindow data;
753 HWND list, text;
755 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
757 data->type = type;
759 text = GetDlgItem(hWnd, IDC_MENU_TEXT);
760 list = GetDlgItem(hWnd, IDC_MENU_LIST);
761 if (data->type == MENU_TYPE_TEXT) {
762 ShowWindow(list, SW_HIDE);
763 EnableWindow(list, FALSE);
764 EnableWindow(text, TRUE);
765 ShowWindow(text, SW_SHOW);
766 if (data->is_active)
767 SetFocus(text);
768 } else {
769 ShowWindow(text, SW_HIDE);
770 EnableWindow(text, FALSE);
771 EnableWindow(list, TRUE);
772 ShowWindow(list, SW_SHOW);
773 if (data->is_active)
774 SetFocus(list);
776 LayoutMenu(hWnd);
778 /*-----------------------------------------------------------------------------*/
779 void
780 SetMenuListType(HWND hWnd, int how)
782 PNHMenuWindow data;
783 RECT rt;
784 DWORD dwStyles;
785 char buf[BUFSZ];
786 TCHAR wbuf[BUFSZ];
787 int nItem;
788 int i;
789 HWND control;
790 LVCOLUMN lvcol;
791 LRESULT fnt;
793 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
794 if (data->type != MENU_TYPE_MENU)
795 return;
797 data->how = how;
799 switch (how) {
800 case PICK_NONE:
801 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_VSCROLL
802 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
803 | LVS_SINGLESEL;
804 break;
805 case PICK_ONE:
806 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_VSCROLL
807 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
808 | LVS_SINGLESEL;
809 break;
810 case PICK_ANY:
811 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_VSCROLL
812 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
813 | LVS_SINGLESEL;
814 break;
815 default:
816 panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
819 if (strlen(data->menu.prompt) == 0) {
820 dwStyles |= LVS_NOCOLUMNHEADER;
823 GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
824 DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
825 control = CreateWindow(WC_LISTVIEW, NULL, dwStyles, rt.left, rt.top,
826 rt.right - rt.left, rt.bottom - rt.top, hWnd,
827 (HMENU) IDC_MENU_LIST, GetNHApp()->hApp, NULL);
828 if (!control)
829 panic("cannot create menu control");
831 /* install the hook for the control window procedure */
832 wndProcListViewOrig = (WNDPROC) GetWindowLongPtr(control, GWLP_WNDPROC);
833 SetWindowLongPtr(control, GWLP_WNDPROC, (LONG_PTR) NHMenuListWndProc);
835 /* set control colors */
836 ListView_SetBkColor(control, menu_bg_brush ? menu_bg_color
837 : (COLORREF) GetSysColor(
838 DEFAULT_COLOR_BG_MENU));
839 ListView_SetTextBkColor(
840 control, menu_bg_brush ? menu_bg_color : (COLORREF) GetSysColor(
841 DEFAULT_COLOR_BG_MENU));
842 ListView_SetTextColor(
843 control, menu_fg_brush ? menu_fg_color : (COLORREF) GetSysColor(
844 DEFAULT_COLOR_FG_MENU));
846 /* set control font */
847 fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM) 0, (LPARAM) 0);
848 SendMessage(control, WM_SETFONT, (WPARAM) fnt, (LPARAM) 0);
850 /* add column to the list view */
851 ZeroMemory(&lvcol, sizeof(lvcol));
852 lvcol.mask = LVCF_WIDTH | LVCF_TEXT;
853 lvcol.cx = GetSystemMetrics(SM_CXFULLSCREEN);
854 lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ);
855 ListView_InsertColumn(control, 0, &lvcol);
857 /* add items to the list view */
858 for (i = 0; i < data->menu.size; i++) {
859 LVITEM lvitem;
860 ZeroMemory(&lvitem, sizeof(lvitem));
861 sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '),
862 data->menu.items[i].str);
864 lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
865 lvitem.iItem = i;
866 lvitem.iSubItem = 0;
867 lvitem.state = data->menu.items[i].presel ? LVIS_SELECTED : 0;
868 lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ);
869 lvitem.lParam = (LPARAM) &data->menu.items[i];
870 nItem = (int) SendMessage(control, LB_ADDSTRING, (WPARAM) 0,
871 (LPARAM) buf);
872 if (ListView_InsertItem(control, &lvitem) == -1) {
873 panic("cannot insert menu item");
876 if (data->is_active)
877 SetFocus(control);
879 /*-----------------------------------------------------------------------------*/
880 HWND
881 GetMenuControl(HWND hWnd)
883 PNHMenuWindow data;
885 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
887 if (data->type == MENU_TYPE_TEXT) {
888 return GetDlgItem(hWnd, IDC_MENU_TEXT);
889 } else {
890 return GetDlgItem(hWnd, IDC_MENU_LIST);
893 /*-----------------------------------------------------------------------------*/
894 BOOL
895 onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
897 LPMEASUREITEMSTRUCT lpmis;
898 TEXTMETRIC tm;
899 HGDIOBJ saveFont;
900 HDC hdc;
901 PNHMenuWindow data;
902 RECT list_rect;
903 int i;
905 UNREFERENCED_PARAMETER(wParam);
907 lpmis = (LPMEASUREITEMSTRUCT) lParam;
908 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
909 GetClientRect(GetMenuControl(hWnd), &list_rect);
911 hdc = GetDC(GetMenuControl(hWnd));
912 saveFont =
913 SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
914 GetTextMetrics(hdc, &tm);
916 /* Set the height of the list box items to max height of the individual
917 * items */
918 for (i = 0; i < data->menu.size; i++) {
919 if (NHMENU_HAS_GLYPH(data->menu.items[i])
920 && !IS_MAP_ASCII(iflags.wc_map_mode)) {
921 lpmis->itemHeight =
922 max(lpmis->itemHeight,
923 (UINT) max(tm.tmHeight, GetNHApp()->mapTile_Y) + 2);
924 } else {
925 lpmis->itemHeight =
926 max(lpmis->itemHeight, (UINT) max(tm.tmHeight, TILE_Y) + 2);
930 /* set width to the window width */
931 lpmis->itemWidth = list_rect.right - list_rect.left;
933 SelectObject(hdc, saveFont);
934 ReleaseDC(GetMenuControl(hWnd), hdc);
935 return TRUE;
937 /*-----------------------------------------------------------------------------*/
938 BOOL
939 onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
941 LPDRAWITEMSTRUCT lpdis;
942 PNHMenuItem item;
943 PNHMenuWindow data;
944 TEXTMETRIC tm;
945 HGDIOBJ saveFont;
946 HDC tileDC;
947 short ntile;
948 int t_x, t_y;
949 int x, y;
950 TCHAR wbuf[BUFSZ];
951 RECT drawRect;
952 COLORREF OldBg, OldFg, NewBg;
953 char *p, *p1;
954 int column;
955 int spacing = 0;
957 int color = NO_COLOR, attr;
958 boolean menucolr = FALSE;
960 UNREFERENCED_PARAMETER(wParam);
962 lpdis = (LPDRAWITEMSTRUCT) lParam;
964 /* If there are no list box items, skip this message. */
965 if (lpdis->itemID == -1)
966 return FALSE;
968 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
970 item = &data->menu.items[lpdis->itemID];
972 tileDC = CreateCompatibleDC(lpdis->hDC);
973 saveFont = SelectObject(
974 lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE));
975 NewBg = menu_bg_brush ? menu_bg_color
976 : (COLORREF) GetSysColor(DEFAULT_COLOR_BG_MENU);
977 OldBg = SetBkColor(lpdis->hDC, NewBg);
978 OldFg = SetTextColor(lpdis->hDC,
979 menu_fg_brush
980 ? menu_fg_color
981 : (COLORREF) GetSysColor(DEFAULT_COLOR_FG_MENU));
983 GetTextMetrics(lpdis->hDC, &tm);
984 spacing = tm.tmAveCharWidth;
986 /* set initial offset */
987 x = lpdis->rcItem.left + 1;
989 /* print check mark and letter */
990 if (NHMENU_IS_SELECTABLE(*item)) {
991 char buf[2];
992 if (data->how != PICK_NONE) {
993 HGDIOBJ saveBrush;
994 HBRUSH hbrCheckMark;
996 switch (item->count) {
997 case -1:
998 hbrCheckMark = CreatePatternBrush(data->bmpChecked);
999 break;
1000 case 0:
1001 hbrCheckMark = CreatePatternBrush(data->bmpNotChecked);
1002 break;
1003 default:
1004 hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount);
1005 break;
1008 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
1009 SetBrushOrgEx(lpdis->hDC, x, y, NULL);
1010 saveBrush = SelectObject(lpdis->hDC, hbrCheckMark);
1011 PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY);
1012 SelectObject(lpdis->hDC, saveBrush);
1013 DeleteObject(hbrCheckMark);
1016 x += TILE_X + spacing;
1018 if (item->accelerator != 0) {
1019 buf[0] = item->accelerator;
1020 buf[1] = '\x0';
1022 if (iflags.use_menu_color
1023 && (menucolr = get_menu_coloring(item->str, &color, &attr))) {
1024 SelectObject(lpdis->hDC,
1025 mswin_get_font(NHW_MENU, attr, lpdis->hDC, FALSE));
1026 if (color != NO_COLOR)
1027 SetTextColor(lpdis->hDC, nhcolor_to_RGB(color));
1030 SetRect(&drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right,
1031 lpdis->rcItem.bottom);
1032 DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect,
1033 DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
1035 x += tm.tmAveCharWidth + tm.tmOverhang + spacing;
1036 } else {
1037 x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 2 * spacing;
1040 /* print glyph if present */
1041 if (NHMENU_HAS_GLYPH(*item)) {
1042 if (!IS_MAP_ASCII(iflags.wc_map_mode)) {
1043 HGDIOBJ saveBmp;
1045 saveBmp = SelectObject(tileDC, GetNHApp()->bmpMapTiles);
1046 ntile = glyph2tile[item->glyph];
1047 t_x =
1048 (ntile % GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_X;
1049 t_y =
1050 (ntile / GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_Y;
1052 y = (lpdis->rcItem.bottom + lpdis->rcItem.top
1053 - GetNHApp()->mapTile_Y) / 2;
1055 if (GetNHApp()->bmpMapTiles == GetNHApp()->bmpTiles) {
1056 /* using original anethack tiles - apply image transparently */
1057 (*GetNHApp()->lpfnTransparentBlt)(lpdis->hDC, x, y, TILE_X, TILE_Y,
1058 tileDC, t_x, t_y, TILE_X, TILE_Y,
1059 TILE_BK_COLOR);
1060 } else {
1061 /* using custom tiles - simple blt */
1062 BitBlt(lpdis->hDC, x, y, GetNHApp()->mapTile_X,
1063 GetNHApp()->mapTile_Y, tileDC, t_x, t_y, SRCCOPY);
1065 SelectObject(tileDC, saveBmp);
1066 x += GetNHApp()->mapTile_X;
1067 } else {
1068 const char *sel_ind;
1069 switch (item->count) {
1070 case -1:
1071 sel_ind = "+";
1072 break;
1073 case 0:
1074 sel_ind = "-";
1075 break;
1076 default:
1077 sel_ind = "#";
1078 break;
1081 SetRect(&drawRect, x, lpdis->rcItem.top,
1082 min(x + tm.tmAveCharWidth, lpdis->rcItem.right),
1083 lpdis->rcItem.bottom);
1084 DrawText(lpdis->hDC, NH_A2W(sel_ind, wbuf, BUFSZ), 1, &drawRect,
1085 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
1086 x += tm.tmAveCharWidth;
1088 } else {
1089 /* no glyph - need to adjust so help window won't look to cramped */
1090 x += TILE_X;
1093 x += spacing;
1095 /* draw item text */
1096 p1 = item->str;
1097 p = strchr(item->str, '\t');
1098 column = 0;
1099 SetRect(&drawRect, x, lpdis->rcItem.top,
1100 min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
1101 lpdis->rcItem.bottom);
1102 for (;;) {
1103 TCHAR wbuf[BUFSZ];
1104 if (p != NULL)
1105 *p = '\0'; /* for time being, view tab field as zstring */
1106 DrawText(lpdis->hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect,
1107 DT_LEFT | DT_VCENTER | DT_SINGLELINE);
1108 if (p != NULL)
1109 *p = '\t';
1110 else /* last string so, */
1111 break;
1113 p1 = p + 1;
1114 p = strchr(p1, '\t');
1115 drawRect.left = drawRect.right + TAB_SEPARATION;
1116 ++column;
1117 drawRect.right = min(drawRect.left + data->menu.tab_stop_size[column],
1118 lpdis->rcItem.right);
1121 /* draw focused item */
1122 if (item->has_focus || (NHMENU_IS_SELECTABLE(*item)
1123 && data->menu.items[lpdis->itemID].count != -1)) {
1124 RECT client_rt;
1126 GetClientRect(lpdis->hwndItem, &client_rt);
1127 client_rt.right = min(client_rt.right, lpdis->rcItem.right);
1128 if (NHMENU_IS_SELECTABLE(*item)
1129 && data->menu.items[lpdis->itemID].count != 0
1130 && item->glyph != NO_GLYPH) {
1131 if (data->menu.items[lpdis->itemID].count == -1) {
1132 _stprintf(wbuf, TEXT("Count: All"));
1133 } else {
1134 _stprintf(wbuf, TEXT("Count: %d"),
1135 data->menu.items[lpdis->itemID].count);
1138 SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK,
1139 lpdis->hDC, FALSE));
1141 /* calculate text rectangle */
1142 SetRect(&drawRect, client_rt.left, lpdis->rcItem.top,
1143 client_rt.right, lpdis->rcItem.bottom);
1144 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1145 DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE
1146 | DT_NOPREFIX);
1148 /* erase text rectangle */
1149 drawRect.left =
1150 max(client_rt.left + 1,
1151 client_rt.right - (drawRect.right - drawRect.left) - 10);
1152 drawRect.right = client_rt.right - 1;
1153 drawRect.top = lpdis->rcItem.top;
1154 drawRect.bottom = lpdis->rcItem.bottom;
1155 FillRect(lpdis->hDC, &drawRect,
1156 menu_bg_brush ? menu_bg_brush
1157 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_MENU));
1159 /* draw text */
1160 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1161 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
1164 if (item->has_focus) {
1165 /* draw focus rect */
1166 RECT client_rt;
1168 GetClientRect(lpdis->hwndItem, &client_rt);
1169 SetRect(&drawRect, client_rt.left, lpdis->rcItem.top,
1170 client_rt.left + ListView_GetColumnWidth(lpdis->hwndItem, 0),
1171 lpdis->rcItem.bottom);
1172 DrawFocusRect(lpdis->hDC, &drawRect);
1175 SetTextColor(lpdis->hDC, OldFg);
1176 SetBkColor(lpdis->hDC, OldBg);
1177 SelectObject(lpdis->hDC, saveFont);
1178 DeleteDC(tileDC);
1179 return TRUE;
1181 /*-----------------------------------------------------------------------------*/
1182 BOOL
1183 onListChar(HWND hWnd, HWND hwndList, WORD ch)
1185 int i = 0;
1186 PNHMenuWindow data;
1187 int curIndex, topIndex, pageSize;
1188 boolean is_accelerator = FALSE;
1190 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1192 switch (ch) {
1193 case MENU_FIRST_PAGE:
1194 i = 0;
1195 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1196 ListView_EnsureVisible(hwndList, i, FALSE);
1197 return -2;
1199 case MENU_LAST_PAGE:
1200 i = max(0, data->menu.size - 1);
1201 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1202 ListView_EnsureVisible(hwndList, i, FALSE);
1203 return -2;
1205 case MENU_NEXT_PAGE:
1206 topIndex = ListView_GetTopIndex(hwndList);
1207 pageSize = ListView_GetCountPerPage(hwndList);
1208 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1209 /* Focus down one page */
1210 i = min(curIndex + pageSize, data->menu.size - 1);
1211 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1212 /* Scrollpos down one page */
1213 i = min(topIndex + (2 * pageSize - 1), data->menu.size - 1);
1214 ListView_EnsureVisible(hwndList, i, FALSE);
1215 return -2;
1217 case MENU_PREVIOUS_PAGE:
1218 topIndex = ListView_GetTopIndex(hwndList);
1219 pageSize = ListView_GetCountPerPage(hwndList);
1220 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1221 /* Focus up one page */
1222 i = max(curIndex - pageSize, 0);
1223 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1224 /* Scrollpos up one page */
1225 i = max(topIndex - pageSize, 0);
1226 ListView_EnsureVisible(hwndList, i, FALSE);
1227 break;
1229 case MENU_SELECT_ALL:
1230 if (data->how == PICK_ANY) {
1231 reset_menu_count(hwndList, data);
1232 for (i = 0; i < data->menu.size; i++) {
1233 SelectMenuItem(hwndList, data, i, -1);
1235 return -2;
1237 break;
1239 case MENU_UNSELECT_ALL:
1240 if (data->how == PICK_ANY) {
1241 reset_menu_count(hwndList, data);
1242 for (i = 0; i < data->menu.size; i++) {
1243 SelectMenuItem(hwndList, data, i, 0);
1245 return -2;
1247 break;
1249 case MENU_INVERT_ALL:
1250 if (data->how == PICK_ANY) {
1251 reset_menu_count(hwndList, data);
1252 for (i = 0; i < data->menu.size; i++) {
1253 SelectMenuItem(hwndList, data, i,
1254 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1255 : -1);
1257 return -2;
1259 break;
1261 case MENU_SELECT_PAGE:
1262 if (data->how == PICK_ANY) {
1263 int from, to;
1264 reset_menu_count(hwndList, data);
1265 topIndex = ListView_GetTopIndex(hwndList);
1266 pageSize = ListView_GetCountPerPage(hwndList);
1267 from = max(0, topIndex);
1268 to = min(data->menu.size, from + pageSize);
1269 for (i = from; i < to; i++) {
1270 SelectMenuItem(hwndList, data, i, -1);
1272 return -2;
1274 break;
1276 case MENU_UNSELECT_PAGE:
1277 if (data->how == PICK_ANY) {
1278 int from, to;
1279 reset_menu_count(hwndList, data);
1280 topIndex = ListView_GetTopIndex(hwndList);
1281 pageSize = ListView_GetCountPerPage(hwndList);
1282 from = max(0, topIndex);
1283 to = min(data->menu.size, from + pageSize);
1284 for (i = from; i < to; i++) {
1285 SelectMenuItem(hwndList, data, i, 0);
1287 return -2;
1289 break;
1291 case MENU_INVERT_PAGE:
1292 if (data->how == PICK_ANY) {
1293 int from, to;
1294 reset_menu_count(hwndList, data);
1295 topIndex = ListView_GetTopIndex(hwndList);
1296 pageSize = ListView_GetCountPerPage(hwndList);
1297 from = max(0, topIndex);
1298 to = min(data->menu.size, from + pageSize);
1299 for (i = from; i < to; i++) {
1300 SelectMenuItem(hwndList, data, i,
1301 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1302 : -1);
1304 return -2;
1306 break;
1308 case MENU_SEARCH:
1309 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1310 char buf[BUFSZ];
1312 reset_menu_count(hwndList, data);
1313 if (mswin_getlin_window("Search for:", buf, BUFSZ) == IDCANCEL) {
1314 strcpy(buf, "\033");
1316 if (data->is_active)
1317 SetFocus(hwndList); // set focus back to the list control
1318 if (!*buf || *buf == '\033')
1319 return -2;
1320 for (i = 0; i < data->menu.size; i++) {
1321 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1322 && strstr(data->menu.items[i].str, buf)) {
1323 if (data->how == PICK_ANY) {
1324 SelectMenuItem(
1325 hwndList, data, i,
1326 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0 : -1);
1327 } else if (data->how == PICK_ONE) {
1328 SelectMenuItem(hwndList, data, i, -1);
1329 ListView_SetItemState(hwndList, i, LVIS_FOCUSED,
1330 LVIS_FOCUSED);
1331 ListView_EnsureVisible(hwndList, i, FALSE);
1332 break;
1336 } else {
1337 mswin_nhbell();
1339 return -2;
1341 case ' ': {
1342 if (GetNHApp()->regaNetHackMode) {
1343 /* aNetHack mode: Scroll down one page,
1344 ends menu when on last page. */
1345 SCROLLINFO si;
1347 si.cbSize = sizeof(SCROLLINFO);
1348 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1349 GetScrollInfo(hwndList, SB_VERT, &si);
1350 if ((si.nPos + (int) si.nPage) > (si.nMax - si.nMin)) {
1351 /* We're at the bottom: dismiss. */
1352 data->done = 1;
1353 data->result = 0;
1354 return -2;
1356 /* We're not at the bottom: page down. */
1357 topIndex = ListView_GetTopIndex(hwndList);
1358 pageSize = ListView_GetCountPerPage(hwndList);
1359 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1360 /* Focus down one page */
1361 i = min(curIndex + pageSize, data->menu.size - 1);
1362 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1363 /* Scrollpos down one page */
1364 i = min(topIndex + (2 * pageSize - 1), data->menu.size - 1);
1365 ListView_EnsureVisible(hwndList, i, FALSE);
1367 return -2;
1368 } else {
1369 /* Windows mode: ends menu for PICK_ONE/PICK_NONE
1370 select item for PICK_ANY */
1371 if (data->how == PICK_ONE || data->how == PICK_NONE) {
1372 data->done = 1;
1373 data->result = 0;
1374 return -2;
1375 } else if (data->how == PICK_ANY) {
1376 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1377 if (i >= 0) {
1378 SelectMenuItem(
1379 hwndList, data, i,
1380 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0 : -1);
1384 } break;
1386 default:
1387 if (strchr(data->menu.gacc, ch)
1388 && !(ch == '0' && data->menu.counting)) {
1389 /* matched a group accelerator */
1390 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1391 reset_menu_count(hwndList, data);
1392 for (i = 0; i < data->menu.size; i++) {
1393 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1394 && data->menu.items[i].group_accel == ch) {
1395 if (data->how == PICK_ANY) {
1396 SelectMenuItem(
1397 hwndList, data, i,
1398 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1399 : -1);
1400 } else if (data->how == PICK_ONE) {
1401 SelectMenuItem(hwndList, data, i, -1);
1402 data->result = 0;
1403 data->done = 1;
1404 return -2;
1408 return -2;
1409 } else {
1410 mswin_nhbell();
1411 return -2;
1415 if (isdigit((uchar) ch)) {
1416 int count;
1417 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1418 if (i >= 0) {
1419 count = data->menu.items[i].count;
1420 if (count == -1)
1421 count = 0;
1422 count *= 10L;
1423 count += (int) (ch - '0');
1424 if (count != 0) /* ignore leading zeros */ {
1425 data->menu.counting = TRUE;
1426 data->menu.items[i].count = min(100000, count);
1427 ListView_RedrawItems(hwndList, i,
1428 i); /* update count mark */
1431 return -2;
1434 is_accelerator = FALSE;
1435 for (i = 0; i < data->menu.size; i++) {
1436 if (data->menu.items[i].accelerator == ch) {
1437 is_accelerator = TRUE;
1438 break;
1442 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
1443 || is_accelerator) {
1444 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1445 topIndex = ListView_GetTopIndex(hwndList);
1446 if( topIndex < 0 || topIndex > data->menu.size ) break; // impossible?
1447 int iter = topIndex;
1448 do {
1449 i = iter % data->menu.size;
1450 if (data->menu.items[i].accelerator == ch) {
1451 if (data->how == PICK_ANY) {
1452 SelectMenuItem(
1453 hwndList, data, i,
1454 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1455 : -1);
1456 ListView_SetItemState(hwndList, i, LVIS_FOCUSED,
1457 LVIS_FOCUSED);
1458 ListView_EnsureVisible(hwndList, i, FALSE);
1459 return -2;
1460 } else if (data->how == PICK_ONE) {
1461 SelectMenuItem(hwndList, data, i, -1);
1462 data->result = 0;
1463 data->done = 1;
1464 return -2;
1467 } while( (++iter % data->menu.size) != topIndex );
1470 break;
1473 reset_menu_count(hwndList, data);
1474 return -1;
1476 /*-----------------------------------------------------------------------------*/
1477 void
1478 mswin_menu_window_size(HWND hWnd, LPSIZE sz)
1480 HWND control;
1481 PNHMenuWindow data;
1482 RECT rt, wrt;
1483 int extra_cx;
1485 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1486 if (data) {
1487 control = GetMenuControl(hWnd);
1489 /* get the control size */
1490 GetClientRect(control, &rt);
1491 sz->cx = rt.right - rt.left;
1492 sz->cy = rt.bottom - rt.top;
1494 /* calculate "extra" space around the control */
1495 GetWindowRect(hWnd, &wrt);
1496 extra_cx = (wrt.right - wrt.left) - sz->cx;
1498 if (data->type == MENU_TYPE_MENU) {
1499 sz->cx = data->menu.menu_cx + GetSystemMetrics(SM_CXVSCROLL);
1500 } else {
1501 /* Use the width of the text box */
1502 sz->cx = data->text.text_box_size.cx
1503 + 2 * GetSystemMetrics(SM_CXVSCROLL);
1505 sz->cx += extra_cx;
1506 } else {
1507 /* uninitilized window */
1508 GetClientRect(hWnd, &rt);
1509 sz->cx = rt.right - rt.left;
1510 sz->cy = rt.bottom - rt.top;
1513 /*-----------------------------------------------------------------------------*/
1514 void
1515 SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1517 int i;
1519 if (item < 0 || item >= data->menu.size)
1520 return;
1522 if (data->how == PICK_ONE && count != 0) {
1523 for (i = 0; i < data->menu.size; i++)
1524 if (item != i && data->menu.items[i].count != 0) {
1525 data->menu.items[i].count = 0;
1526 ListView_RedrawItems(hwndList, i, i);
1530 data->menu.items[item].count = count;
1531 ListView_RedrawItems(hwndList, item, item);
1532 reset_menu_count(hwndList, data);
1534 /*-----------------------------------------------------------------------------*/
1535 void
1536 reset_menu_count(HWND hwndList, PNHMenuWindow data)
1538 int i;
1539 data->menu.counting = FALSE;
1540 if (IsWindow(hwndList)) {
1541 i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
1542 if (i >= 0)
1543 ListView_RedrawItems(hwndList, i, i);
1546 /*-----------------------------------------------------------------------------*/
1547 /* List window Proc */
1548 LRESULT CALLBACK
1549 NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1551 BOOL bUpdateFocusItem;
1553 /* we will redraw focused item whenever horizontal scrolling occurs
1554 since "Count: XXX" indicator is garbled by scrolling */
1555 bUpdateFocusItem = FALSE;
1557 switch (message) {
1558 case WM_KEYDOWN:
1559 if (wParam == VK_LEFT || wParam == VK_RIGHT)
1560 bUpdateFocusItem = TRUE;
1561 break;
1563 case WM_CHAR: /* filter keyboard input for the control */
1564 if (wParam > 0 && wParam < 256
1565 && onListChar(GetParent(hWnd), hWnd, (char) wParam) == -2) {
1566 return 0;
1567 } else {
1568 return 1;
1570 break;
1572 case WM_SIZE:
1573 case WM_HSCROLL:
1574 bUpdateFocusItem = TRUE;
1575 break;
1577 case WM_SETFOCUS:
1578 if (GetParent(hWnd) != GetNHApp()->hPopupWnd) {
1579 SetFocus(GetNHApp()->hMainWnd);
1581 return FALSE;
1584 /* update focused item */
1585 if (bUpdateFocusItem) {
1586 int i;
1587 RECT rt;
1589 /* invalidate the focus rectangle */
1590 i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);
1591 if (i != -1) {
1592 ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1593 InvalidateRect(hWnd, &rt, TRUE);
1597 /* call ListView control window proc */
1598 if (wndProcListViewOrig)
1599 return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam,
1600 lParam);
1601 else
1602 return 0;
1604 /*-----------------------------------------------------------------------------*/
1605 /* Text control window proc - implements scrolling without a cursor */
1606 LRESULT CALLBACK
1607 NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1609 HDC hDC;
1610 RECT rc;
1612 switch (message) {
1613 case WM_ERASEBKGND:
1614 hDC = (HDC) wParam;
1615 GetClientRect(hWnd, &rc);
1616 FillRect(hDC, &rc, text_bg_brush
1617 ? text_bg_brush
1618 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
1619 return 0;
1621 case WM_KEYDOWN:
1622 switch (wParam) {
1623 /* close on space in Windows mode
1624 page down on space in aNetHack mode */
1625 case VK_SPACE: {
1626 SCROLLINFO si;
1628 si.cbSize = sizeof(SCROLLINFO);
1629 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1630 GetScrollInfo(hWnd, SB_VERT, &si);
1631 /* If anethackmode and not at the end of the list */
1632 if (GetNHApp()->regaNetHackMode
1633 && (si.nPos + (int) si.nPage) <= (si.nMax - si.nMin))
1634 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1635 else
1636 PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0),
1638 return 0;
1640 case VK_NEXT:
1641 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1642 return 0;
1643 case VK_PRIOR:
1644 SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0);
1645 return 0;
1646 case VK_UP:
1647 SendMessage(hWnd, EM_SCROLL, SB_LINEUP, 0);
1648 return 0;
1649 case VK_DOWN:
1650 SendMessage(hWnd, EM_SCROLL, SB_LINEDOWN, 0);
1651 return 0;
1653 break;
1655 case WM_CHAR:
1656 switch(wParam) {
1657 case MENU_FIRST_PAGE:
1658 SendMessage(hWnd, EM_SCROLL, SB_TOP, 0);
1659 return 0;
1660 case MENU_LAST_PAGE:
1661 SendMessage(hWnd, EM_SCROLL, SB_BOTTOM, 0);
1662 return 0;
1663 case MENU_NEXT_PAGE:
1664 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1665 return 0;
1666 case MENU_PREVIOUS_PAGE:
1667 SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0);
1668 return 0;
1670 break;
1672 /* edit control needs to know nothing of its focus */
1673 case WM_SETFOCUS:
1674 HideCaret(hWnd);
1675 return 0;
1678 if (editControlWndProc)
1679 return CallWindowProc(editControlWndProc, hWnd, message, wParam,
1680 lParam);
1681 else
1682 return 0;
1684 /*-----------------------------------------------------------------------------*/