1 /* NetHack 3.6 mhmenu.c $NHDT-Date: 1432512811 2015/05/25 00:13:31 $ $NHDT-Branch: master $:$NHDT-Revision: 1.48 $ */
2 /* Copyright (c) Alex Kompel, 2002 */
3 /* NetHack may be freely redistributed. See license for details. */
16 #define NHMENU_STR_SIZE BUFSZ
17 #define MIN_TABSTOP_SIZE 0
19 #define TAB_SEPARATION 10 /* pixels between each tab stop */
21 #define DEFAULT_COLOR_BG_TEXT COLOR_WINDOW
22 #define DEFAULT_COLOR_FG_TEXT COLOR_WINDOWTEXT
23 #define DEFAULT_COLOR_BG_MENU COLOR_WINDOW
24 #define DEFAULT_COLOR_FG_MENU COLOR_WINDOWTEXT
26 #define CHECK_WIDTH 16
27 #define CHECK_HEIGHT 16
29 typedef struct mswin_menu_item {
35 char str[NHMENU_STR_SIZE];
39 } NHMenuItem, *PNHMenuItem;
41 typedef struct mswin_nethack_menu_window {
42 int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */
43 int how; /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */
47 int size; /* number of items in items[] */
48 int allocated; /* number of allocated slots in items[] */
49 PNHMenuItem items; /* menu items */
50 char gacc[QBUFSZ]; /* group accelerators */
51 BOOL counting; /* counting flag */
52 char prompt[QBUFSZ]; /* menu prompt */
53 int tab_stop_size[NUMTABS]; /* tabstops to align option values */
54 int menu_cx; /* menu width */
66 HBITMAP bmpCheckedCount;
67 HBITMAP bmpNotChecked;
71 } NHMenuWindow, *PNHMenuWindow;
73 extern short glyph2tile[];
75 static WNDPROC wndProcListViewOrig = NULL;
76 static WNDPROC editControlWndProc = NULL;
78 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj != NULL)
79 #define NHMENU_IS_SELECTED(item) ((item).count != 0)
80 #define NHMENU_HAS_GLYPH(item) ((item).glyph != NO_GLYPH)
82 INT_PTR CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM);
83 LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM);
84 LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM);
85 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
86 static BOOL onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
87 static BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
88 static void LayoutMenu(HWND hwnd);
89 static void SetMenuType(HWND hwnd, int type);
90 static void SetMenuListType(HWND hwnd, int now);
91 static HWND GetMenuControl(HWND hwnd);
92 static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item,
94 static void reset_menu_count(HWND hwndList, PNHMenuWindow data);
95 static BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch);
97 /*-----------------------------------------------------------------------------*/
99 mswin_init_menu_window(int type)
104 /* get window position */
105 if (GetNHApp()->bAutoLayout) {
106 SetRect(&rt, 0, 0, 0, 0);
108 mswin_get_window_placement(NHW_MENU, &rt);
111 /* create menu window object */
112 ret = CreateDialog(GetNHApp()->hApp, MAKEINTRESOURCE(IDD_MENU),
113 GetNHApp()->hMainWnd, MenuWndProc);
115 panic("Cannot create menu window");
118 /* move it in the predefined position */
119 if (!GetNHApp()->bAutoLayout) {
120 MoveWindow(ret, rt.left, rt.top, rt.right - rt.left,
121 rt.bottom - rt.top, TRUE);
124 /* Set window caption */
125 SetWindowText(ret, "Menu/Text");
127 mswin_apply_window_style(ret);
129 SetMenuType(ret, type);
132 /*-----------------------------------------------------------------------------*/
134 mswin_menu_window_select_menu(HWND hWnd, int how, MENU_ITEM_P **_selected,
139 MENU_ITEM_P *selected = NULL;
143 assert(_selected != NULL);
147 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
149 /* force activate for certain menu types */
150 if (data->type == MENU_TYPE_MENU
151 && (how == PICK_ONE || how == PICK_ANY)) {
155 data->is_active = activate && !GetNHApp()->regNetHackMode;
158 SetMenuListType(hWnd, how);
160 /* Ok, now give items a unique accelerators */
161 if (data->type == MENU_TYPE_MENU) {
162 char next_char = 'a';
164 data->menu.gacc[0] = '\0';
165 ap = data->menu.gacc;
166 for (i = 0; i < data->menu.size; i++) {
167 if (data->menu.items[i].accelerator != 0) {
168 if (isalpha(data->menu.items[i].accelerator)) {
169 next_char = (char)(data->menu.items[i].accelerator + 1);
171 } else if (NHMENU_IS_SELECTABLE(data->menu.items[i])) {
172 if (isalpha(next_char)) {
173 data->menu.items[i].accelerator = next_char;
177 else if (next_char > 'Z')
180 data->menu.items[i].accelerator = next_char;
187 /* collect group accelerators */
188 for (i = 0; i < data->menu.size; i++) {
189 if (data->how != PICK_NONE) {
190 if (data->menu.items[i].group_accel
191 && !strchr(data->menu.gacc,
192 data->menu.items[i].group_accel)) {
193 *ap++ = data->menu.items[i].group_accel;
199 reset_menu_count(NULL, data);
202 LayoutMenu(hWnd); // show dialog buttons
205 mswin_popup_display(hWnd, &data->done);
207 SetFocus(GetNHApp()->hMainWnd);
208 mswin_layout_main_window(hWnd);
212 if (data->result != -1) {
213 if (how == PICK_NONE) {
214 if (data->result >= 0)
218 } else if (how == PICK_ONE || how == PICK_ANY) {
219 /* count selected items */
221 for (i = 0; i < data->menu.size; i++) {
222 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
223 && NHMENU_IS_SELECTED(data->menu.items[i])) {
231 (MENU_ITEM_P *) malloc(ret_val * sizeof(MENU_ITEM_P));
233 panic("out of memory");
236 for (i = 0; i < data->menu.size; i++) {
237 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
238 && NHMENU_IS_SELECTED(data->menu.items[i])) {
239 selected[sel_ind].item =
240 data->menu.items[i].identifier;
241 selected[sel_ind].count = data->menu.items[i].count;
246 *_selected = selected;
252 data->is_active = FALSE;
253 LayoutMenu(hWnd); // hide dialog buttons
254 mswin_popup_destroy(hWnd);
256 /* If we just used the permanent inventory window to pick something,
257 * set the menu back to its display inventory state.
259 if (iflags.perm_invent && mswin_winid_from_handle(hWnd) == WIN_INVEN
260 && how != PICK_NONE) {
261 data->menu.prompt[0] = '\0';
262 SetMenuListType(hWnd, PICK_NONE);
263 for (i = 0; i < data->menu.size; i++)
264 data->menu.items[i].count = 0;
271 /*-----------------------------------------------------------------------------*/
273 MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
275 PNHMenuWindow data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
276 HWND control = GetDlgItem(hWnd, IDC_MENU_TEXT);
277 TCHAR title[MAX_LOADSTRING];
280 case WM_INITDIALOG: {
282 HDC hdc = GetDC(control);
283 data = (PNHMenuWindow) malloc(sizeof(NHMenuWindow));
284 ZeroMemory(data, sizeof(NHMenuWindow));
285 data->type = MENU_TYPE_TEXT;
286 data->how = PICK_NONE;
290 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL));
291 data->bmpCheckedCount =
292 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT));
293 data->bmpNotChecked =
294 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL));
295 data->bmpDC = CreateCompatibleDC(hdc);
296 data->is_active = FALSE;
297 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data);
299 /* set font for the text cotrol */
300 cached_font * font = mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE);
301 SendMessage(control, WM_SETFONT,
302 (WPARAM) font->hFont,
304 ReleaseDC(control, hdc);
306 /* subclass edit control */
308 (WNDPROC) GetWindowLongPtr(control, GWLP_WNDPROC);
309 SetWindowLongPtr(control, GWLP_WNDPROC, (LONG_PTR) NHMenuTextWndProc);
311 /* Even though the dialog has no caption, you can still set the title
312 which shows on Alt-Tab */
313 LoadString(GetNHApp()->hApp, IDS_APP_TITLE, title, MAX_LOADSTRING);
314 SetWindowText(hWnd, title);
316 /* set focus to text control for now */
321 case WM_MSNH_COMMAND:
322 onMSNHCommand(hWnd, wParam, lParam);
328 GetWindowRect(hWnd, &rt);
329 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
330 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
331 if (iflags.perm_invent && mswin_winid_from_handle(hWnd) == WIN_INVEN)
332 mswin_update_window_placement(NHW_INVEN, &rt);
334 mswin_update_window_placement(NHW_MENU, &rt);
340 GetWindowRect(hWnd, &rt);
341 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
342 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
343 if (iflags.perm_invent && mswin_winid_from_handle(hWnd) == WIN_INVEN)
344 mswin_update_window_placement(NHW_INVEN, &rt);
346 mswin_update_window_placement(NHW_MENU, &rt);
351 if (program_state.gameover) {
354 program_state.stopprint++;
360 switch (LOWORD(wParam)) {
362 if (data->type == MENU_TYPE_MENU
363 && (data->how == PICK_ONE || data->how == PICK_ANY)
364 && data->menu.counting) {
368 /* reset counter if counting is in progress */
369 list = GetMenuControl(hWnd);
370 i = ListView_GetNextItem(list, -1, LVNI_FOCUSED);
372 SelectMenuItem(list, data, i, 0);
389 LPNMHDR lpnmhdr = (LPNMHDR) lParam;
390 switch (LOWORD(wParam)) {
391 case IDC_MENU_LIST: {
392 if (!data || data->type != MENU_TYPE_MENU)
395 switch (lpnmhdr->code) {
396 case LVN_ITEMACTIVATE: {
397 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
398 if (data->how == PICK_ONE) {
399 if (lpnmlv->iItem >= 0 && lpnmlv->iItem < data->menu.size
400 && NHMENU_IS_SELECTABLE(
401 data->menu.items[lpnmlv->iItem])) {
402 SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
412 LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW) lParam;
413 if (lpnmitem->iItem == -1)
415 if (data->how == PICK_ANY) {
417 lpnmitem->hdr.hwndFrom, data, lpnmitem->iItem,
418 NHMENU_IS_SELECTED(data->menu.items[lpnmitem->iItem])
424 case LVN_ITEMCHANGED: {
425 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
426 if (lpnmlv->iItem == -1)
428 if (!(lpnmlv->uChanged & LVIF_STATE))
431 if (data->how == PICK_ONE || data->how == PICK_ANY) {
432 data->menu.items[lpnmlv->iItem].has_focus =
433 !!(lpnmlv->uNewState & LVIS_FOCUSED);
434 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
438 /* update count for single-selection menu (follow the listview
440 if (data->how == PICK_ONE) {
441 if (lpnmlv->uNewState & LVIS_SELECTED) {
442 SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
447 /* check item focus */
448 if (data->how == PICK_ONE || data->how == PICK_ANY) {
449 data->menu.items[lpnmlv->iItem].has_focus =
450 !!(lpnmlv->uNewState & LVIS_FOCUSED);
451 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
457 reset_menu_count(lpnmhdr->hwndFrom, data);
465 if (hWnd != GetNHApp()->hPopupWnd) {
466 SetFocus(GetNHApp()->hMainWnd);
468 if (IsWindow(GetMenuControl(hWnd)))
469 SetFocus(GetMenuControl(hWnd));
474 if (wParam == IDC_MENU_LIST)
475 return onMeasureItem(hWnd, wParam, lParam);
480 if (wParam == IDC_MENU_LIST)
481 return onDrawItem(hWnd, wParam, lParam);
485 case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */
486 HDC hdcEdit = (HDC) wParam;
487 HWND hwndEdit = (HWND) lParam;
488 if (hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT)) {
489 SetBkColor(hdcEdit, text_bg_brush ? text_bg_color
490 : (COLORREF) GetSysColor(
491 DEFAULT_COLOR_BG_TEXT));
492 SetTextColor(hdcEdit, text_fg_brush ? text_fg_color
493 : (COLORREF) GetSysColor(
494 DEFAULT_COLOR_FG_TEXT));
495 return (INT_PTR)(text_bg_brush
497 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
503 return (INT_PTR)(text_bg_brush
505 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
509 DeleteDC(data->bmpDC);
510 DeleteObject(data->bmpChecked);
511 DeleteObject(data->bmpCheckedCount);
512 DeleteObject(data->bmpNotChecked);
513 if (data->type == MENU_TYPE_TEXT) {
515 free(data->text.text);
518 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
524 /*-----------------------------------------------------------------------------*/
526 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
530 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
532 case MSNH_MSG_PUTSTR: {
533 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam;
541 if (data->type != MENU_TYPE_TEXT)
542 SetMenuType(hWnd, MENU_TYPE_TEXT);
544 if (!data->text.text) {
545 text_size = strlen(msg_data->text) + 4;
547 (TCHAR *) malloc(text_size * sizeof(data->text.text[0]));
548 ZeroMemory(data->text.text,
549 text_size * sizeof(data->text.text[0]));
551 text_size = _tcslen(data->text.text) + strlen(msg_data->text) + 4;
552 data->text.text = (TCHAR *) realloc(
553 data->text.text, text_size * sizeof(data->text.text[0]));
555 if (!data->text.text)
558 _tcscat(data->text.text, NH_A2W(msg_data->text, wbuf, BUFSZ));
559 _tcscat(data->text.text, TEXT("\r\n"));
561 text_view = GetDlgItem(hWnd, IDC_MENU_TEXT);
563 panic("cannot get text view window");
564 SetWindowText(text_view, data->text.text);
566 /* calculate dimensions of the added line of text */
567 hdc = GetDC(text_view);
568 cached_font * font = mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE);
569 saveFont = SelectObject(hdc, font->hFont);
570 SetRect(&text_rt, 0, 0, 0, 0);
571 DrawTextA(hdc, msg_data->text, strlen(msg_data->text), &text_rt,
572 DT_CALCRECT | DT_TOP | DT_LEFT | DT_NOPREFIX
574 data->text.text_box_size.cx =
575 max(text_rt.right - text_rt.left, data->text.text_box_size.cx);
576 data->text.text_box_size.cy += text_rt.bottom - text_rt.top;
577 SelectObject(hdc, saveFont);
578 ReleaseDC(text_view, hdc);
581 case MSNH_MSG_STARTMENU: {
583 if (data->type != MENU_TYPE_MENU)
584 SetMenuType(hWnd, MENU_TYPE_MENU);
586 if (data->menu.items)
587 free(data->menu.items);
588 data->how = PICK_NONE;
589 data->menu.items = NULL;
591 data->menu.allocated = 0;
594 for (i = 0; i < NUMTABS; ++i)
595 data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
598 case MSNH_MSG_ADDMENU: {
599 PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu) lParam;
605 LONG menuitemwidth = 0;
608 if (data->type != MENU_TYPE_MENU)
611 if (data->menu.size == data->menu.allocated) {
612 data->menu.allocated += 10;
613 data->menu.items = (PNHMenuItem) realloc(
614 data->menu.items, data->menu.allocated * sizeof(NHMenuItem));
617 new_item = data->menu.size;
618 ZeroMemory(&data->menu.items[new_item],
619 sizeof(data->menu.items[new_item]));
620 data->menu.items[new_item].glyph = msg_data->glyph;
621 data->menu.items[new_item].identifier = *msg_data->identifier;
622 data->menu.items[new_item].accelerator = msg_data->accelerator;
623 data->menu.items[new_item].group_accel = msg_data->group_accel;
624 data->menu.items[new_item].attr = msg_data->attr;
625 strncpy(data->menu.items[new_item].str, msg_data->str,
627 /* prevent & being interpreted as a mnemonic start */
628 strNsubst(data->menu.items[new_item].str, "&", "&&", 0);
629 data->menu.items[new_item].presel = msg_data->presel;
631 /* calculate tabstop size */
633 cached_font * font = mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE);
634 saveFont = SelectObject(hDC, font->hFont);
635 GetTextMetrics(hDC, &tm);
636 p1 = data->menu.items[new_item].str;
637 p = strchr(data->menu.items[new_item].str, '\t');
642 SetRect(&drawRect, 0, 0, 1, 1);
644 *p = '\0'; /* for time being, view tab field as zstring */
645 DrawText(hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect,
646 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS
648 data->menu.tab_stop_size[column] =
649 max(data->menu.tab_stop_size[column],
650 drawRect.right - drawRect.left);
652 menuitemwidth += data->menu.tab_stop_size[column];
656 else /* last string so, */
659 /* add the separation only when not the last item */
660 /* in the last item, we break out of the loop, in the statement
662 menuitemwidth += TAB_SEPARATION;
666 p = strchr(p1, '\t');
668 SelectObject(hDC, saveFont);
669 ReleaseDC(hWnd, hDC);
671 /* calculate new menu width */
673 max(data->menu.menu_cx,
674 2 * TILE_X + menuitemwidth
675 + (tm.tmAveCharWidth + tm.tmOverhang) * 12);
681 case MSNH_MSG_ENDMENU: {
682 PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu) lParam;
683 if (msg_data->text) {
684 strncpy(data->menu.prompt, msg_data->text,
685 sizeof(data->menu.prompt) - 1);
687 ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
691 case MSNH_MSG_RANDOM_INPUT: {
692 PostMessage(GetMenuControl(hWnd),
693 WM_MSNH_COMMAND, MSNH_MSG_RANDOM_INPUT, 0);
698 /*-----------------------------------------------------------------------------*/
700 LayoutMenu(HWND hWnd)
706 POINT pt_elem, pt_ok, pt_cancel;
707 SIZE sz_elem, sz_ok, sz_cancel;
709 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
710 menu_ok = GetDlgItem(hWnd, IDOK);
711 menu_cancel = GetDlgItem(hWnd, IDCANCEL);
713 /* get window coordinates */
714 GetClientRect(hWnd, &clrt);
717 if (data->is_active) {
718 GetWindowRect(menu_ok, &rt);
719 if (data->type == MENU_TYPE_TEXT
720 || (data->type == MENU_TYPE_MENU && data->how == PICK_NONE)) {
721 sz_ok.cx = (clrt.right - clrt.left) - 2 * MENU_MARGIN;
723 sz_ok.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
725 sz_ok.cy = rt.bottom - rt.top;
726 pt_ok.x = clrt.left + MENU_MARGIN;
727 pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy;
728 ShowWindow(menu_ok, SW_SHOW);
729 MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE);
731 sz_ok.cx = sz_ok.cy = 0;
732 pt_ok.x = pt_ok.y = 0;
733 ShowWindow(menu_ok, SW_HIDE);
738 && !(data->type == MENU_TYPE_TEXT
739 || (data->type == MENU_TYPE_MENU && data->how == PICK_NONE))) {
740 GetWindowRect(menu_ok, &rt);
741 sz_cancel.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
742 pt_cancel.x = (clrt.left + clrt.right) / 2 + MENU_MARGIN;
743 sz_cancel.cy = rt.bottom - rt.top;
744 pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy;
745 ShowWindow(menu_cancel, SW_SHOW);
746 MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx,
749 sz_cancel.cx = sz_cancel.cy = 0;
750 pt_cancel.x = pt_cancel.y = 0;
751 ShowWindow(menu_cancel, SW_HIDE);
755 pt_elem.x = clrt.left + MENU_MARGIN;
756 pt_elem.y = clrt.top + MENU_MARGIN;
757 sz_elem.cx = (clrt.right - clrt.left) - 2 * MENU_MARGIN;
758 if (data->is_active) {
759 sz_elem.cy = (clrt.bottom - clrt.top) - max(sz_ok.cy, sz_cancel.cy)
762 sz_elem.cy = (clrt.bottom - clrt.top) - 2 * MENU_MARGIN;
765 if (data->type == MENU_TYPE_MENU) {
766 ListView_SetColumnWidth(
767 GetMenuControl(hWnd), 0,
768 max(clrt.right - clrt.left - GetSystemMetrics(SM_CXVSCROLL),
769 data->menu.menu_cx));
772 MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx,
775 /*-----------------------------------------------------------------------------*/
777 SetMenuType(HWND hWnd, int type)
782 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
786 text = GetDlgItem(hWnd, IDC_MENU_TEXT);
787 list = GetDlgItem(hWnd, IDC_MENU_LIST);
788 if (data->type == MENU_TYPE_TEXT) {
789 ShowWindow(list, SW_HIDE);
790 EnableWindow(list, FALSE);
791 EnableWindow(text, TRUE);
792 ShowWindow(text, SW_SHOW);
796 ShowWindow(text, SW_HIDE);
797 EnableWindow(text, FALSE);
798 EnableWindow(list, TRUE);
799 ShowWindow(list, SW_SHOW);
805 /*-----------------------------------------------------------------------------*/
807 SetMenuListType(HWND hWnd, int how)
820 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
821 if (data->type != MENU_TYPE_MENU)
828 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_VSCROLL
829 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
833 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_VSCROLL
834 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
838 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_CHILD | WS_VSCROLL
839 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
843 panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
846 if (strlen(data->menu.prompt) == 0) {
847 dwStyles |= LVS_NOCOLUMNHEADER;
850 GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
851 DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
852 control = CreateWindow(WC_LISTVIEW, NULL, dwStyles, rt.left, rt.top,
853 rt.right - rt.left, rt.bottom - rt.top, hWnd,
854 (HMENU) IDC_MENU_LIST, GetNHApp()->hApp, NULL);
856 panic("cannot create menu control");
858 /* install the hook for the control window procedure */
859 wndProcListViewOrig = (WNDPROC) GetWindowLongPtr(control, GWLP_WNDPROC);
860 SetWindowLongPtr(control, GWLP_WNDPROC, (LONG_PTR) NHMenuListWndProc);
862 /* set control colors */
863 ListView_SetBkColor(control, menu_bg_brush ? menu_bg_color
864 : (COLORREF) GetSysColor(
865 DEFAULT_COLOR_BG_MENU));
866 ListView_SetTextBkColor(
867 control, menu_bg_brush ? menu_bg_color : (COLORREF) GetSysColor(
868 DEFAULT_COLOR_BG_MENU));
869 ListView_SetTextColor(
870 control, menu_fg_brush ? menu_fg_color : (COLORREF) GetSysColor(
871 DEFAULT_COLOR_FG_MENU));
873 /* set control font */
874 fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM) 0, (LPARAM) 0);
875 SendMessage(control, WM_SETFONT, (WPARAM) fnt, (LPARAM) 0);
877 /* add column to the list view */
878 MonitorInfo monitorInfo;
879 win10_monitor_info(hWnd, &monitorInfo);
880 ZeroMemory(&lvcol, sizeof(lvcol));
881 lvcol.mask = LVCF_WIDTH | LVCF_TEXT;
882 lvcol.cx = monitorInfo.width;
883 lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ);
884 ListView_InsertColumn(control, 0, &lvcol);
886 /* add items to the list view */
887 for (i = 0; i < data->menu.size; i++) {
889 ZeroMemory(&lvitem, sizeof(lvitem));
890 sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '),
891 data->menu.items[i].str);
893 lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
896 lvitem.state = data->menu.items[i].presel ? LVIS_SELECTED : 0;
897 lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ);
898 lvitem.lParam = (LPARAM) &data->menu.items[i];
899 nItem = (int) SendMessage(control, LB_ADDSTRING, (WPARAM) 0,
901 if (ListView_InsertItem(control, &lvitem) == -1) {
902 panic("cannot insert menu item");
908 /*-----------------------------------------------------------------------------*/
910 GetMenuControl(HWND hWnd)
914 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
916 /* We may continue getting window messages after a window's WM_DESTROY is
917 called. We need to handle the case that USERDATA has been freed. */
921 if (data->type == MENU_TYPE_TEXT) {
922 return GetDlgItem(hWnd, IDC_MENU_TEXT);
924 return GetDlgItem(hWnd, IDC_MENU_LIST);
927 /*-----------------------------------------------------------------------------*/
929 onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
931 LPMEASUREITEMSTRUCT lpmis;
939 UNREFERENCED_PARAMETER(wParam);
941 lpmis = (LPMEASUREITEMSTRUCT) lParam;
942 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
943 GetClientRect(GetMenuControl(hWnd), &list_rect);
945 hdc = GetDC(GetMenuControl(hWnd));
946 cached_font * font = mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE);
947 saveFont = SelectObject(hdc, font->hFont);
948 GetTextMetrics(hdc, &tm);
950 /* Set the height of the list box items to max height of the individual
952 for (i = 0; i < data->menu.size; i++) {
953 if (NHMENU_HAS_GLYPH(data->menu.items[i])
954 && !IS_MAP_ASCII(iflags.wc_map_mode)) {
956 max(lpmis->itemHeight,
957 (UINT) max(tm.tmHeight, GetNHApp()->mapTile_Y) + 2);
960 max(lpmis->itemHeight, (UINT) max(tm.tmHeight, TILE_Y) + 2);
964 /* set width to the window width */
965 lpmis->itemWidth = list_rect.right - list_rect.left;
967 SelectObject(hdc, saveFont);
968 ReleaseDC(GetMenuControl(hWnd), hdc);
971 /*-----------------------------------------------------------------------------*/
973 onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
975 LPDRAWITEMSTRUCT lpdis;
986 COLORREF OldBg, OldFg, NewBg;
991 int color = NO_COLOR, attr;
992 boolean menucolr = FALSE;
993 double monitorScale = win10_monitor_scale(hWnd);
994 int tileXScaled = (int) (TILE_X * monitorScale);
995 int tileYScaled = (int) (TILE_Y * monitorScale);
997 UNREFERENCED_PARAMETER(wParam);
999 lpdis = (LPDRAWITEMSTRUCT) lParam;
1001 /* If there are no list box items, skip this message. */
1002 if (lpdis->itemID == -1)
1005 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1007 item = &data->menu.items[lpdis->itemID];
1009 tileDC = CreateCompatibleDC(lpdis->hDC);
1010 cached_font * font = mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE);
1011 saveFont = SelectObject(lpdis->hDC, font->hFont);
1012 NewBg = menu_bg_brush ? menu_bg_color
1013 : (COLORREF) GetSysColor(DEFAULT_COLOR_BG_MENU);
1014 OldBg = SetBkColor(lpdis->hDC, NewBg);
1015 OldFg = SetTextColor(lpdis->hDC,
1018 : (COLORREF) GetSysColor(DEFAULT_COLOR_FG_MENU));
1020 GetTextMetrics(lpdis->hDC, &tm);
1021 spacing = tm.tmAveCharWidth;
1023 /* set initial offset */
1024 x = lpdis->rcItem.left + 1;
1026 /* print check mark and letter */
1027 if (NHMENU_IS_SELECTABLE(*item)) {
1029 if (data->how != PICK_NONE) {
1033 switch (item->count) {
1035 bmpCheck = data->bmpChecked;
1038 bmpCheck = data->bmpNotChecked;
1041 bmpCheck = data->bmpCheckedCount;
1045 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tileYScaled) / 2;
1046 bmpSaved = SelectBitmap(data->bmpDC, bmpCheck);
1047 StretchBlt(lpdis->hDC, x, y, tileXScaled, tileYScaled,
1048 data->bmpDC, 0, 0, CHECK_WIDTH, CHECK_HEIGHT, SRCCOPY);
1049 SelectObject(data->bmpDC, bmpSaved);
1052 x += tileXScaled + spacing;
1054 if (item->accelerator != 0) {
1055 buf[0] = item->accelerator;
1058 if (iflags.use_menu_color
1059 && (menucolr = get_menu_coloring(item->str, &color, &attr))) {
1060 cached_font * menu_font = mswin_get_font(NHW_MENU, attr, lpdis->hDC, FALSE);
1061 SelectObject(lpdis->hDC, menu_font->hFont);
1062 if (color != NO_COLOR)
1063 SetTextColor(lpdis->hDC, nhcolor_to_RGB(color));
1066 SetRect(&drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right,
1067 lpdis->rcItem.bottom);
1068 DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect,
1069 DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
1071 x += tm.tmAveCharWidth + tm.tmOverhang + spacing;
1073 x += tileXScaled + tm.tmAveCharWidth + tm.tmOverhang + 2 * spacing;
1076 /* print glyph if present */
1077 if (NHMENU_HAS_GLYPH(*item)) {
1078 if (!IS_MAP_ASCII(iflags.wc_map_mode)) {
1080 double monitorScale = win10_monitor_scale(hWnd);
1082 saveBmp = SelectObject(tileDC, GetNHApp()->bmpMapTiles);
1083 ntile = glyph2tile[item->glyph];
1085 (ntile % GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_X;
1087 (ntile / GetNHApp()->mapTilesPerLine) * GetNHApp()->mapTile_Y;
1089 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tileYScaled) / 2;
1091 if (GetNHApp()->bmpMapTiles == GetNHApp()->bmpTiles) {
1092 /* using original nethack tiles - apply image transparently */
1093 (*GetNHApp()->lpfnTransparentBlt)(lpdis->hDC, x, y,
1094 tileXScaled, tileYScaled,
1095 tileDC, t_x, t_y, TILE_X, TILE_Y,
1098 /* using custom tiles - simple blt */
1099 StretchBlt(lpdis->hDC, x, y, tileXScaled, tileYScaled,
1100 tileDC, t_x, t_y, GetNHApp()->mapTile_X, GetNHApp()->mapTile_Y, SRCCOPY);
1102 SelectObject(tileDC, saveBmp);
1105 const char *sel_ind;
1106 switch (item->count) {
1118 SetRect(&drawRect, x, lpdis->rcItem.top,
1119 min(x + tm.tmAveCharWidth, lpdis->rcItem.right),
1120 lpdis->rcItem.bottom);
1121 DrawText(lpdis->hDC, NH_A2W(sel_ind, wbuf, BUFSZ), 1, &drawRect,
1122 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
1123 x += tm.tmAveCharWidth;
1126 /* no glyph - need to adjust so help window won't look to cramped */
1132 /* draw item text */
1134 p = strchr(item->str, '\t');
1136 SetRect(&drawRect, x, lpdis->rcItem.top,
1137 min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
1138 lpdis->rcItem.bottom);
1142 *p = '\0'; /* for time being, view tab field as zstring */
1143 DrawText(lpdis->hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect,
1144 DT_LEFT | DT_VCENTER | DT_SINGLELINE);
1147 else /* last string so, */
1151 p = strchr(p1, '\t');
1152 drawRect.left = drawRect.right + TAB_SEPARATION;
1154 drawRect.right = min(drawRect.left + data->menu.tab_stop_size[column],
1155 lpdis->rcItem.right);
1158 /* draw focused item */
1159 if (item->has_focus || (NHMENU_IS_SELECTABLE(*item)
1160 && data->menu.items[lpdis->itemID].count != -1)) {
1163 GetClientRect(lpdis->hwndItem, &client_rt);
1164 client_rt.right = min(client_rt.right, lpdis->rcItem.right);
1165 if (NHMENU_IS_SELECTABLE(*item)
1166 && data->menu.items[lpdis->itemID].count != 0
1167 && item->glyph != NO_GLYPH) {
1168 if (data->menu.items[lpdis->itemID].count == -1) {
1169 _stprintf(wbuf, TEXT("Count: All"));
1171 _stprintf(wbuf, TEXT("Count: %d"),
1172 data->menu.items[lpdis->itemID].count);
1175 /* TOOD: add blinking for blink text */
1177 cached_font * blink_font = mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE);
1178 SelectObject(lpdis->hDC, blink_font->hFont);
1180 /* calculate text rectangle */
1181 SetRect(&drawRect, client_rt.left, lpdis->rcItem.top,
1182 client_rt.right, lpdis->rcItem.bottom);
1183 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1184 DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE
1187 /* erase text rectangle */
1189 max(client_rt.left + 1,
1190 client_rt.right - (drawRect.right - drawRect.left) - 10);
1191 drawRect.right = client_rt.right - 1;
1192 drawRect.top = lpdis->rcItem.top;
1193 drawRect.bottom = lpdis->rcItem.bottom;
1194 FillRect(lpdis->hDC, &drawRect,
1195 menu_bg_brush ? menu_bg_brush
1196 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_MENU));
1199 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1200 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
1203 if (item->has_focus) {
1204 /* draw focus rect */
1207 GetClientRect(lpdis->hwndItem, &client_rt);
1208 SetRect(&drawRect, client_rt.left, lpdis->rcItem.top,
1209 client_rt.left + ListView_GetColumnWidth(lpdis->hwndItem, 0),
1210 lpdis->rcItem.bottom);
1211 DrawFocusRect(lpdis->hDC, &drawRect);
1214 SetTextColor(lpdis->hDC, OldFg);
1215 SetBkColor(lpdis->hDC, OldBg);
1216 SelectObject(lpdis->hDC, saveFont);
1220 /*-----------------------------------------------------------------------------*/
1222 onListChar(HWND hWnd, HWND hwndList, WORD ch)
1226 int curIndex, topIndex, pageSize;
1227 boolean is_accelerator = FALSE;
1229 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1231 is_accelerator = FALSE;
1232 for (i = 0; i < data->menu.size; i++) {
1233 if (data->menu.items[i].accelerator == ch) {
1234 is_accelerator = TRUE;
1239 /* Don't use switch if input matched an accelerator. Sometimes
1240 * accelerators can conflict with menu actions. For example, when
1241 * engraving the extra choice of using fingers matches MENU_UNSELECT_ALL.
1247 case MENU_FIRST_PAGE:
1249 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1250 ListView_EnsureVisible(hwndList, i, FALSE);
1253 case MENU_LAST_PAGE:
1254 i = max(0, data->menu.size - 1);
1255 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1256 ListView_EnsureVisible(hwndList, i, FALSE);
1259 case MENU_NEXT_PAGE:
1260 topIndex = ListView_GetTopIndex(hwndList);
1261 pageSize = ListView_GetCountPerPage(hwndList);
1262 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1263 /* Focus down one page */
1264 i = min(curIndex + pageSize, data->menu.size - 1);
1265 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1266 /* Scrollpos down one page */
1267 i = min(topIndex + (2 * pageSize - 1), data->menu.size - 1);
1268 ListView_EnsureVisible(hwndList, i, FALSE);
1271 case MENU_PREVIOUS_PAGE:
1272 topIndex = ListView_GetTopIndex(hwndList);
1273 pageSize = ListView_GetCountPerPage(hwndList);
1274 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1275 /* Focus up one page */
1276 i = max(curIndex - pageSize, 0);
1277 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1278 /* Scrollpos up one page */
1279 i = max(topIndex - pageSize, 0);
1280 ListView_EnsureVisible(hwndList, i, FALSE);
1283 case MENU_SELECT_ALL:
1284 if (data->how == PICK_ANY) {
1285 reset_menu_count(hwndList, data);
1286 for (i = 0; i < data->menu.size; i++) {
1287 SelectMenuItem(hwndList, data, i, -1);
1293 case MENU_UNSELECT_ALL:
1294 if (data->how == PICK_ANY) {
1295 reset_menu_count(hwndList, data);
1296 for (i = 0; i < data->menu.size; i++) {
1297 SelectMenuItem(hwndList, data, i, 0);
1303 case MENU_INVERT_ALL:
1304 if (data->how == PICK_ANY) {
1305 reset_menu_count(hwndList, data);
1306 for (i = 0; i < data->menu.size; i++) {
1307 SelectMenuItem(hwndList, data, i,
1308 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1315 case MENU_SELECT_PAGE:
1316 if (data->how == PICK_ANY) {
1318 reset_menu_count(hwndList, data);
1319 topIndex = ListView_GetTopIndex(hwndList);
1320 pageSize = ListView_GetCountPerPage(hwndList);
1321 from = max(0, topIndex);
1322 to = min(data->menu.size, from + pageSize);
1323 for (i = from; i < to; i++) {
1324 SelectMenuItem(hwndList, data, i, -1);
1330 case MENU_UNSELECT_PAGE:
1331 if (data->how == PICK_ANY) {
1333 reset_menu_count(hwndList, data);
1334 topIndex = ListView_GetTopIndex(hwndList);
1335 pageSize = ListView_GetCountPerPage(hwndList);
1336 from = max(0, topIndex);
1337 to = min(data->menu.size, from + pageSize);
1338 for (i = from; i < to; i++) {
1339 SelectMenuItem(hwndList, data, i, 0);
1345 case MENU_INVERT_PAGE:
1346 if (data->how == PICK_ANY) {
1348 reset_menu_count(hwndList, data);
1349 topIndex = ListView_GetTopIndex(hwndList);
1350 pageSize = ListView_GetCountPerPage(hwndList);
1351 from = max(0, topIndex);
1352 to = min(data->menu.size, from + pageSize);
1353 for (i = from; i < to; i++) {
1354 SelectMenuItem(hwndList, data, i,
1355 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1363 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1366 reset_menu_count(hwndList, data);
1367 if (mswin_getlin_window("Search for:", buf, BUFSZ) == IDCANCEL) {
1368 strcpy(buf, "\033");
1370 if (data->is_active)
1371 SetFocus(hwndList); // set focus back to the list control
1372 if (!*buf || *buf == '\033')
1374 for (i = 0; i < data->menu.size; i++) {
1375 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1376 && strstr(data->menu.items[i].str, buf)) {
1377 if (data->how == PICK_ANY) {
1380 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0 : -1);
1381 } else if (data->how == PICK_ONE) {
1382 SelectMenuItem(hwndList, data, i, -1);
1383 ListView_SetItemState(hwndList, i, LVIS_FOCUSED,
1385 ListView_EnsureVisible(hwndList, i, FALSE);
1396 if (GetNHApp()->regNetHackMode) {
1397 /* NetHack mode: Scroll down one page,
1398 ends menu when on last page. */
1401 si.cbSize = sizeof(SCROLLINFO);
1402 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1403 GetScrollInfo(hwndList, SB_VERT, &si);
1404 if ((si.nPos + (int) si.nPage) > (si.nMax - si.nMin)) {
1405 /* We're at the bottom: dismiss. */
1410 /* We're not at the bottom: page down. */
1411 topIndex = ListView_GetTopIndex(hwndList);
1412 pageSize = ListView_GetCountPerPage(hwndList);
1413 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1414 /* Focus down one page */
1415 i = min(curIndex + pageSize, data->menu.size - 1);
1416 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1417 /* Scrollpos down one page */
1418 i = min(topIndex + (2 * pageSize - 1), data->menu.size - 1);
1419 ListView_EnsureVisible(hwndList, i, FALSE);
1423 /* Windows mode: ends menu for PICK_ONE/PICK_NONE
1424 select item for PICK_ANY */
1425 if (data->how == PICK_ONE || data->how == PICK_NONE) {
1429 } else if (data->how == PICK_ANY) {
1430 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1434 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0 : -1);
1442 if (strchr(data->menu.gacc, ch)
1443 && !(ch == '0' && data->menu.counting)) {
1444 /* matched a group accelerator */
1445 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1446 reset_menu_count(hwndList, data);
1447 for (i = 0; i < data->menu.size; i++) {
1448 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1449 && data->menu.items[i].group_accel == ch) {
1450 if (data->how == PICK_ANY) {
1453 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1455 } else if (data->how == PICK_ONE) {
1456 SelectMenuItem(hwndList, data, i, -1);
1470 if (isdigit((uchar) ch)) {
1472 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1474 count = data->menu.items[i].count;
1478 count += (int) (ch - '0');
1479 if (count != 0) /* ignore leading zeros */ {
1480 data->menu.counting = TRUE;
1481 data->menu.items[i].count = min(100000, count);
1482 ListView_RedrawItems(hwndList, i,
1483 i); /* update count mark */
1489 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
1490 || is_accelerator) {
1491 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1492 topIndex = ListView_GetTopIndex(hwndList);
1493 if( topIndex < 0 || topIndex > data->menu.size ) break; // impossible?
1494 int iter = topIndex;
1496 i = iter % data->menu.size;
1497 if (data->menu.items[i].accelerator == ch) {
1498 if (data->how == PICK_ANY) {
1501 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1503 ListView_SetItemState(hwndList, i, LVIS_FOCUSED,
1505 ListView_EnsureVisible(hwndList, i, FALSE);
1507 } else if (data->how == PICK_ONE) {
1508 SelectMenuItem(hwndList, data, i, -1);
1514 } while( (++iter % data->menu.size) != topIndex );
1520 reset_menu_count(hwndList, data);
1523 /*-----------------------------------------------------------------------------*/
1525 mswin_menu_window_size(HWND hWnd, LPSIZE sz)
1532 data = (PNHMenuWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
1534 control = GetMenuControl(hWnd);
1536 /* get the control size */
1537 GetClientRect(control, &rt);
1538 sz->cx = rt.right - rt.left;
1539 sz->cy = rt.bottom - rt.top;
1541 /* calculate "extra" space around the control */
1542 GetWindowRect(hWnd, &wrt);
1543 extra_cx = (wrt.right - wrt.left) - sz->cx;
1545 if (data->type == MENU_TYPE_MENU) {
1546 sz->cx = data->menu.menu_cx + GetSystemMetrics(SM_CXVSCROLL);
1548 /* Use the width of the text box */
1549 sz->cx = data->text.text_box_size.cx
1550 + 2 * GetSystemMetrics(SM_CXVSCROLL);
1554 /* uninitilized window */
1555 GetClientRect(hWnd, &rt);
1556 sz->cx = rt.right - rt.left;
1557 sz->cy = rt.bottom - rt.top;
1560 /*-----------------------------------------------------------------------------*/
1562 SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1566 if (item < 0 || item >= data->menu.size)
1569 if (data->how == PICK_ONE && count != 0) {
1570 for (i = 0; i < data->menu.size; i++)
1571 if (item != i && data->menu.items[i].count != 0) {
1572 data->menu.items[i].count = 0;
1573 ListView_RedrawItems(hwndList, i, i);
1577 data->menu.items[item].count = count;
1578 ListView_RedrawItems(hwndList, item, item);
1579 reset_menu_count(hwndList, data);
1581 /*-----------------------------------------------------------------------------*/
1583 reset_menu_count(HWND hwndList, PNHMenuWindow data)
1586 data->menu.counting = FALSE;
1587 if (IsWindow(hwndList)) {
1588 i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
1590 ListView_RedrawItems(hwndList, i, i);
1593 /*-----------------------------------------------------------------------------*/
1594 /* List window Proc */
1596 NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1598 HWND hWndParent = GetParent(hWnd);
1599 BOOL bUpdateFocusItem;
1601 /* we will redraw focused item whenever horizontal scrolling occurs
1602 since "Count: XXX" indicator is garbled by scrolling */
1603 bUpdateFocusItem = FALSE;
1607 if (wParam == VK_LEFT || wParam == VK_RIGHT)
1608 bUpdateFocusItem = TRUE;
1611 case WM_CHAR: /* filter keyboard input for the control */
1612 if (wParam > 0 && wParam < 256
1613 && onListChar(GetParent(hWnd), hWnd, (char) wParam) == -2) {
1622 bUpdateFocusItem = TRUE;
1626 if (GetParent(hWnd) != GetNHApp()->hPopupWnd) {
1627 SetFocus(GetNHApp()->hMainWnd);
1631 case WM_MSNH_COMMAND:
1632 if (wParam == MSNH_MSG_RANDOM_INPUT) {
1633 char c = randomkey();
1635 PostMessage(hWndParent, WM_COMMAND, MAKELONG(IDOK, 0), 0);
1636 else if (c == '\033')
1637 PostMessage(hWndParent, WM_COMMAND, MAKELONG(IDCANCEL, 0), 0);
1639 PostMessage(hWnd, WM_CHAR, c, 0);
1646 /* update focused item */
1647 if (bUpdateFocusItem) {
1651 /* invalidate the focus rectangle */
1652 i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);
1654 ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1655 InvalidateRect(hWnd, &rt, TRUE);
1659 /* call ListView control window proc */
1660 if (wndProcListViewOrig)
1661 return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam,
1666 /*-----------------------------------------------------------------------------*/
1667 /* Text control window proc - implements scrolling without a cursor */
1669 NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1671 HWND hWndParent = GetParent(hWnd);
1678 GetClientRect(hWnd, &rc);
1679 FillRect(hDC, &rc, text_bg_brush
1681 : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
1686 /* close on space in Windows mode
1687 page down on space in NetHack mode */
1691 si.cbSize = sizeof(SCROLLINFO);
1692 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1693 GetScrollInfo(hWnd, SB_VERT, &si);
1694 /* If nethackmode and not at the end of the list */
1695 if (GetNHApp()->regNetHackMode
1696 && (si.nPos + (int) si.nPage) <= (si.nMax - si.nMin))
1697 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1699 PostMessage(hWndParent, WM_COMMAND, MAKELONG(IDOK, 0), 0);
1703 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1706 SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0);
1709 SendMessage(hWnd, EM_SCROLL, SB_LINEUP, 0);
1712 SendMessage(hWnd, EM_SCROLL, SB_LINEDOWN, 0);
1719 case MENU_FIRST_PAGE:
1720 SendMessage(hWnd, EM_SCROLL, SB_TOP, 0);
1722 case MENU_LAST_PAGE:
1723 SendMessage(hWnd, EM_SCROLL, SB_BOTTOM, 0);
1725 case MENU_NEXT_PAGE:
1726 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1728 case MENU_PREVIOUS_PAGE:
1729 SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0);
1734 /* edit control needs to know nothing of its focus */
1739 case WM_MSNH_COMMAND:
1740 if (wParam == MSNH_MSG_RANDOM_INPUT) {
1741 char c = randomkey();
1743 PostMessage(hWndParent, WM_COMMAND, MAKELONG(IDOK, 0), 0);
1744 else if (c == '\033')
1745 PostMessage(hWndParent, WM_COMMAND, MAKELONG(IDCANCEL, 0), 0);
1747 PostMessage(hWnd, WM_CHAR, c, 0);
1754 if (editControlWndProc)
1755 return CallWindowProc(editControlWndProc, hWnd, message, wParam,
1760 /*-----------------------------------------------------------------------------*/