1 /* SCCS Id: @(#)mhmenu.c 3.4 2002/03/06 */
2 /* Copyright (c) Alex Kompel, 2002 */
3 /* NetHack may be freely redistributed. See license for details. */
15 #define NHMENU_STR_SIZE BUFSZ
16 #define MIN_TABSTOP_SIZE 0
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 {
31 char str[NHMENU_STR_SIZE];
35 } NHMenuItem, *PNHMenuItem;
37 typedef struct mswin_nethack_menu_window {
38 int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */
39 int how; /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */
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 */
60 HBITMAP bmpCheckedCount;
61 HBITMAP bmpNotChecked;
62 } NHMenuWindow, *PNHMenuWindow;
64 extern short glyph2tile[];
66 static WNDPROC wndProcListViewOrig = NULL;
67 static WNDPROC editControlWndProc = NULL;
69 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL)
70 #define NHMENU_IS_SELECTED(item) ((item).count!=0)
72 BOOL CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM);
73 LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM);
74 LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM);
75 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
76 static BOOL onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
77 static BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
78 static void LayoutMenu(HWND hwnd);
79 static void SetMenuType(HWND hwnd, int type);
80 static void SetMenuListType(HWND hwnd, int now);
81 static HWND GetMenuControl(HWND hwnd);
82 static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count);
83 static void reset_menu_count(HWND hwndList, PNHMenuWindow data);
84 static BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch);
86 /*-----------------------------------------------------------------------------*/
87 HWND mswin_init_menu_window (int type) {
92 MAKEINTRESOURCE(IDD_MENU),
97 panic("Cannot create menu window");
100 SetMenuType(ret, type);
103 /*-----------------------------------------------------------------------------*/
104 int mswin_menu_window_select_menu (HWND hWnd, int how, MENU_ITEM_P ** _selected)
108 MENU_ITEM_P *selected = NULL;
112 assert( _selected!=NULL );
116 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
119 SetMenuListType(hWnd, how);
121 /* Ok, now give items a unique accelerators */
122 if( data->type == MENU_TYPE_MENU ) {
123 char next_char = 'a';
125 data->menu.gacc[0] = '\0';
126 ap = data->menu.gacc;
127 for( i=0; i<data->menu.size; i++) {
128 if( data->menu.items[i].accelerator!=0 ) {
129 next_char = (char)(data->menu.items[i].accelerator+1);
130 } else if( NHMENU_IS_SELECTABLE(data->menu.items[i]) ) {
131 if ( (next_char>='a' && next_char<='z') ||
132 (next_char>='A' && next_char<='Z') ) {
133 data->menu.items[i].accelerator = next_char;
135 if( next_char > 'z' ) next_char = 'A';
136 else if ( next_char > 'Z' ) break;
138 data->menu.items[i].accelerator = next_char;
145 /* collect group accelerators */
146 for( i=0; i<data->menu.size; i++) {
147 if( data->how != PICK_NONE ) {
148 if( data->menu.items[i].group_accel &&
149 !strchr(data->menu.gacc, data->menu.items[i].group_accel) ) {
150 *ap++ = data->menu.items[i].group_accel;
156 reset_menu_count(NULL, data);
159 mswin_popup_display(hWnd, &data->done);
162 if( data->result != -1 ) {
164 if(data->result>=0) ret_val=0;
166 } else if(how==PICK_ONE || how==PICK_ANY) {
167 /* count selected items */
169 for(i=0; i<data->menu.size; i++ ) {
170 if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
171 NHMENU_IS_SELECTED(data->menu.items[i]) ) {
178 selected = (MENU_ITEM_P*)malloc(ret_val*sizeof(MENU_ITEM_P));
179 if( !selected ) panic("out of memory");
182 for(i=0; i<data->menu.size; i++ ) {
183 if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
184 NHMENU_IS_SELECTED(data->menu.items[i]) ) {
185 selected[sel_ind].item = data->menu.items[i].identifier;
186 selected[sel_ind].count = data->menu.items[i].count;
191 *_selected = selected;
196 mswin_popup_destroy(hWnd);
200 /*-----------------------------------------------------------------------------*/
201 BOOL CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
206 TCHAR title[MAX_LOADSTRING];
209 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
213 data = (PNHMenuWindow)malloc(sizeof(NHMenuWindow));
214 ZeroMemory(data, sizeof(NHMenuWindow));
215 data->type = MENU_TYPE_TEXT;
216 data->how = PICK_NONE;
219 data->bmpChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL));
220 data->bmpCheckedCount = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT));
221 data->bmpNotChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL));
222 SetWindowLong(hWnd, GWL_USERDATA, (LONG)data);
224 /* set font for the text cotrol */
225 control = GetDlgItem(hWnd, IDC_MENU_TEXT);
226 hdc = GetDC(control);
227 SendMessage(control, WM_SETFONT, (WPARAM)mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE), (LPARAM)0);
228 ReleaseDC(control, hdc);
230 /* subclass edit control */
231 editControlWndProc = (WNDPROC)GetWindowLong(control, GWL_WNDPROC);
232 SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuTextWndProc);
234 /* Even though the dialog has no caption, you can still set the title
235 which shows on Alt-Tab */
236 LoadString(GetNHApp()->hApp, IDS_APP_TITLE, title, MAX_LOADSTRING);
237 SetWindowText(hWnd, title);
240 case WM_MSNH_COMMAND:
241 onMSNHCommand(hWnd, wParam, lParam);
249 if (program_state.gameover) {
252 program_state.stopprint++;
259 switch (LOWORD(wParam))
262 if( data->type == MENU_TYPE_MENU &&
263 (data->how==PICK_ONE || data->how==PICK_ANY) &&
264 data->menu.counting) {
268 /* reset counter if counting is in progress */
269 list = GetMenuControl(hWnd);
270 i = ListView_GetNextItem(list, -1, LVNI_FOCUSED);
272 SelectMenuItem(list, data, i, 0);
287 switch (HIWORD(wParam))
290 HideCaret((HWND)lParam);
298 LPNMHDR lpnmhdr = (LPNMHDR)lParam;
299 switch (LOWORD(wParam)) {
302 if( !data || data->type!=MENU_TYPE_MENU ) break;
304 switch(lpnmhdr->code) {
305 case LVN_ITEMACTIVATE:
307 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
308 if(data->how==PICK_ONE) {
309 if( lpnmlv->iItem>=0 &&
310 lpnmlv->iItem<data->menu.size &&
311 NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) {
313 lpnmlv->hdr.hwndFrom,
326 LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW) lParam;
327 if( lpnmitem->iItem==-1 ) return 0;
328 if( data->how==PICK_ANY ) {
330 lpnmitem->hdr.hwndFrom,
333 NHMENU_IS_SELECTED(data->menu.items[lpnmitem->iItem])? 0 : -1
338 case LVN_ITEMCHANGED:
340 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
341 if( lpnmlv->iItem==-1 ) return 0;
342 if( !(lpnmlv->uChanged & LVIF_STATE) ) return 0;
344 if( data->how==PICK_ONE || data->how==PICK_ANY ) {
345 data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED);
346 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem);
349 /* update count for single-selection menu (follow the listview selection) */
350 if( data->how==PICK_ONE ) {
351 if( lpnmlv->uNewState & LVIS_SELECTED ) {
353 lpnmlv->hdr.hwndFrom,
361 /* check item focus */
362 if( data->how==PICK_ONE || data->how==PICK_ANY ) {
363 data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED);
364 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem);
369 reset_menu_count(lpnmhdr->hwndFrom, data);
378 if( hWnd!=GetNHApp()->hPopupWnd ) {
379 SetFocus(GetNHApp()->hPopupWnd );
384 if( wParam==IDC_MENU_LIST )
385 return onMeasureItem(hWnd, wParam, lParam);
390 if( wParam==IDC_MENU_LIST )
391 return onDrawItem(hWnd, wParam, lParam);
395 case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */
396 HDC hdcEdit = (HDC) wParam;
397 HWND hwndEdit = (HWND) lParam;
398 if( hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT) ) {
400 text_bg_brush ? text_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_TEXT)
402 SetTextColor(hdcEdit,
403 text_fg_brush ? text_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_TEXT)
405 return (BOOL)(text_bg_brush
406 ? text_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
412 DeleteObject(data->bmpChecked);
413 DeleteObject(data->bmpCheckedCount);
414 DeleteObject(data->bmpNotChecked);
415 if( data->type == MENU_TYPE_TEXT ) {
416 if( data->text.text ) free(data->text.text);
419 SetWindowLong(hWnd, GWL_USERDATA, (LONG)0);
425 /*-----------------------------------------------------------------------------*/
426 void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
430 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
432 case MSNH_MSG_PUTSTR:
434 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam;
439 if( data->type!=MENU_TYPE_TEXT )
440 SetMenuType(hWnd, MENU_TYPE_TEXT);
442 if( !data->text.text ) {
443 text_size = strlen(msg_data->text) + 4;
444 data->text.text = (TCHAR*)malloc(text_size*sizeof(data->text.text[0]));
445 ZeroMemory(data->text.text, text_size*sizeof(data->text.text[0]));
447 text_size = _tcslen(data->text.text) + strlen(msg_data->text) + 4;
448 data->text.text = (TCHAR*)realloc(data->text.text, text_size*sizeof(data->text.text[0]));
450 if( !data->text.text ) break;
452 _tcscat(data->text.text, NH_A2W(msg_data->text, wbuf, BUFSZ));
453 _tcscat(data->text.text, TEXT("\r\n"));
455 text_view = GetDlgItem(hWnd, IDC_MENU_TEXT);
456 if( !text_view ) panic("cannot get text view window");
457 SetWindowText(text_view, data->text.text);
460 case MSNH_MSG_STARTMENU:
463 if( data->type!=MENU_TYPE_MENU )
464 SetMenuType(hWnd, MENU_TYPE_MENU);
466 if( data->menu.items ) free(data->menu.items);
467 data->how = PICK_NONE;
468 data->menu.items = NULL;
470 data->menu.allocated = 0;
473 for (i = 0; i < NUMTABS; ++i)
474 data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
477 case MSNH_MSG_ADDMENU:
479 PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam;
486 if( data->type!=MENU_TYPE_MENU ) break;
487 if( strlen(msg_data->str)==0 ) break;
489 if( data->menu.size==data->menu.allocated ) {
490 data->menu.allocated += 10;
491 data->menu.items = (PNHMenuItem)realloc(data->menu.items, data->menu.allocated*sizeof(NHMenuItem));
494 new_item = data->menu.size;
495 ZeroMemory( &data->menu.items[new_item], sizeof(data->menu.items[new_item]));
496 data->menu.items[new_item].glyph = msg_data->glyph;
497 data->menu.items[new_item].identifier = *msg_data->identifier;
498 data->menu.items[new_item].accelerator = msg_data->accelerator;
499 data->menu.items[new_item].group_accel = msg_data->group_accel;
500 data->menu.items[new_item].attr = msg_data->attr;
501 strncpy(data->menu.items[new_item].str, msg_data->str, NHMENU_STR_SIZE);
502 data->menu.items[new_item].presel = msg_data->presel;
504 /* calculate tabstop size */
506 saveFont = SelectObject(hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE));
507 p1 = data->menu.items[new_item].str;
508 p = strchr(data->menu.items[new_item].str, '\t');
513 SetRect ( &drawRect, 0, 0, 1, 1 );
514 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
516 NH_A2W(p1, wbuf, BUFSZ),
519 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE
521 data->menu.tab_stop_size[column] =
522 max( data->menu.tab_stop_size[column], drawRect.right - drawRect.left );
523 if (p != NULL) *p = '\t';
524 else /* last string so, */ break;
528 p = strchr(p1, '\t');
530 SelectObject(hDC, saveFont);
531 ReleaseDC(hWnd, hDC);
537 case MSNH_MSG_ENDMENU:
539 PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu)lParam;
540 if( msg_data->text ) {
541 strncpy( data->menu.prompt, msg_data->text, sizeof(data->menu.prompt)-1 );
543 ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
549 /*-----------------------------------------------------------------------------*/
550 void LayoutMenu(HWND hWnd)
556 POINT pt_elem, pt_ok, pt_cancel;
557 SIZE sz_elem, sz_ok, sz_cancel;
559 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
560 menu_ok = GetDlgItem(hWnd, IDOK);
561 menu_cancel = GetDlgItem(hWnd, IDCANCEL);
563 /* get window coordinates */
564 GetClientRect(hWnd, &clrt );
566 /* set window placements */
567 GetWindowRect(menu_ok, &rt);
568 sz_ok.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN;
569 sz_ok.cy = rt.bottom-rt.top;
570 pt_ok.x = clrt.left + MENU_MARGIN;
571 pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy;
573 GetWindowRect(menu_cancel, &rt);
574 sz_cancel.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN;
575 sz_cancel.cy = rt.bottom-rt.top;
576 pt_cancel.x = (clrt.left + clrt.right)/2 + MENU_MARGIN;
577 pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy;
579 pt_elem.x = clrt.left + MENU_MARGIN;
580 pt_elem.y = clrt.top + MENU_MARGIN;
581 sz_elem.cx = (clrt.right - clrt.left) - 2*MENU_MARGIN;
582 sz_elem.cy = min(pt_cancel.y, pt_ok.y) - 2*MENU_MARGIN;
584 MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE );
585 MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE );
586 MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx, sz_cancel.cy, TRUE );
588 /*-----------------------------------------------------------------------------*/
589 void SetMenuType(HWND hWnd, int type)
594 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
598 text = GetDlgItem(hWnd, IDC_MENU_TEXT);
599 list = GetDlgItem(hWnd, IDC_MENU_LIST);
600 if(data->type==MENU_TYPE_TEXT) {
601 ShowWindow(list, SW_HIDE);
602 EnableWindow(list, FALSE);
603 EnableWindow(text, TRUE);
604 ShowWindow(text, SW_SHOW);
607 ShowWindow(text, SW_HIDE);
608 EnableWindow(text, FALSE);
609 EnableWindow(list, TRUE);
610 ShowWindow(list, SW_SHOW);
615 /*-----------------------------------------------------------------------------*/
616 void SetMenuListType(HWND hWnd, int how)
629 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
630 if( data->type != MENU_TYPE_MENU ) return;
636 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD
637 | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
638 | LVS_OWNERDRAWFIXED | LVS_SINGLESEL;
641 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD
642 | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
643 | LVS_OWNERDRAWFIXED | LVS_SINGLESEL;
646 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD
647 | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
648 | LVS_OWNERDRAWFIXED | LVS_SINGLESEL;
650 default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
653 if( strlen(data->menu.prompt)==0 ) {
654 dwStyles |= LVS_NOCOLUMNHEADER ;
657 GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
658 DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
659 control = CreateWindow(WC_LISTVIEW, NULL,
666 (HMENU)IDC_MENU_LIST,
669 if( !control ) panic( "cannot create menu control" );
671 /* install the hook for the control window procedure */
672 wndProcListViewOrig = (WNDPROC)GetWindowLong(control, GWL_WNDPROC);
673 SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuListWndProc);
675 /* set control colors */
676 ListView_SetBkColor(control,
677 menu_bg_brush ? menu_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MENU));
678 ListView_SetTextBkColor(control,
679 menu_bg_brush ? menu_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MENU));
680 ListView_SetTextColor(control,
681 menu_fg_brush ? menu_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_MENU));
683 /* set control font */
684 fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0);
685 SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0);
687 /* add column to the list view */
688 ZeroMemory(&lvcol, sizeof(lvcol));
689 lvcol.mask = LVCF_WIDTH | LVCF_TEXT;
690 lvcol.cx = GetSystemMetrics(SM_CXFULLSCREEN);
691 lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ);
692 ListView_InsertColumn(control, 0, &lvcol);
694 /* add items to the list view */
695 for(i=0; i<data->menu.size; i++ ) {
697 ZeroMemory( &lvitem, sizeof(lvitem) );
698 sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str );
700 lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
703 lvitem.state = data->menu.items[i].presel? LVIS_SELECTED : 0;
704 lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ);
705 lvitem.lParam = (LPARAM)&data->menu.items[i];
706 nItem = SendMessage(control, LB_ADDSTRING, (WPARAM)0, (LPARAM) buf);
707 if( ListView_InsertItem(control, &lvitem)==-1 ) {
708 panic("cannot insert menu item");
713 /*-----------------------------------------------------------------------------*/
714 HWND GetMenuControl(HWND hWnd)
718 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
720 if(data->type==MENU_TYPE_TEXT) {
721 return GetDlgItem(hWnd, IDC_MENU_TEXT);
723 return GetDlgItem(hWnd, IDC_MENU_LIST);
726 /*-----------------------------------------------------------------------------*/
727 BOOL onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
729 LPMEASUREITEMSTRUCT lpmis;
736 lpmis = (LPMEASUREITEMSTRUCT) lParam;
737 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
738 GetClientRect(GetMenuControl(hWnd), &list_rect);
740 hdc = GetDC(GetMenuControl(hWnd));
741 saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
742 GetTextMetrics(hdc, &tm);
744 /* Set the height of the list box items. */
745 lpmis->itemHeight = max(tm.tmHeight, TILE_Y)+2;
746 lpmis->itemWidth = list_rect.right - list_rect.left;
748 SelectObject(hdc, saveFont);
749 ReleaseDC(GetMenuControl(hWnd), hdc);
752 /*-----------------------------------------------------------------------------*/
753 BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
755 LPDRAWITEMSTRUCT lpdis;
766 COLORREF OldBg, OldFg, NewBg;
770 lpdis = (LPDRAWITEMSTRUCT) lParam;
772 /* If there are no list box items, skip this message. */
773 if (lpdis->itemID == -1) return FALSE;
775 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
777 item = &data->menu.items[lpdis->itemID];
779 tileDC = CreateCompatibleDC(lpdis->hDC);
780 saveFont = SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE));
781 NewBg = menu_bg_brush ? menu_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MENU);
782 OldBg = SetBkColor(lpdis->hDC, NewBg);
783 OldFg = SetTextColor(lpdis->hDC,
784 menu_fg_brush ? menu_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_MENU));
786 GetTextMetrics(lpdis->hDC, &tm);
788 x = lpdis->rcItem.left + 1;
790 /* print check mark and letter */
791 if( NHMENU_IS_SELECTABLE(*item) ) {
793 if (data->how != PICK_NONE) {
797 switch(item->count) {
798 case -1: hbrCheckMark = CreatePatternBrush(data->bmpChecked); break;
799 case 0: hbrCheckMark = CreatePatternBrush(data->bmpNotChecked); break;
800 default: hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount); break;
803 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
804 SetBrushOrgEx(lpdis->hDC, x, y, NULL);
805 saveBrush = SelectObject(lpdis->hDC, hbrCheckMark);
806 PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY);
807 SelectObject(lpdis->hDC, saveBrush);
808 DeleteObject(hbrCheckMark);
812 if(item->accelerator!=0) {
813 buf[0] = item->accelerator;
816 SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom );
817 DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
819 x += tm.tmAveCharWidth + tm.tmOverhang + 5;
821 x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10;
824 /* print glyph if present */
825 if( item->glyph != NO_GLYPH ) {
828 saveBmp = SelectObject(tileDC, GetNHApp()->bmpTiles);
829 ntile = glyph2tile[ item->glyph ];
830 t_x = (ntile % TILES_PER_LINE)*TILE_X;
831 t_y = (ntile / TILES_PER_LINE)*TILE_Y;
833 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
835 nhapply_image_transparent(
836 lpdis->hDC, x, y, TILE_X, TILE_Y,
837 tileDC, t_x, t_y, TILE_X, TILE_Y, TILE_BK_COLOR );
838 SelectObject(tileDC, saveBmp);
846 p = strchr(item->str, '\t');
848 SetRect( &drawRect, x, lpdis->rcItem.top, min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
849 lpdis->rcItem.bottom );
852 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
854 NH_A2W(p1, wbuf, BUFSZ),
857 DT_LEFT | DT_VCENTER | DT_SINGLELINE
859 if (p != NULL) *p = '\t';
860 else /* last string so, */ break;
863 p = strchr(p1, '\t');
864 drawRect.left = drawRect.right + TAB_SEPARATION;
866 drawRect.right = min (drawRect.left + data->menu.tab_stop_size[column], lpdis->rcItem.right);
869 /* draw focused item */
871 || (NHMENU_IS_SELECTABLE(*item) &&
872 data->menu.items[lpdis->itemID].count!=-1)) {
875 GetClientRect(lpdis->hwndItem, &client_rt);
876 if( NHMENU_IS_SELECTABLE(*item) &&
877 data->menu.items[lpdis->itemID].count!=0 &&
878 item->glyph != NO_GLYPH ) {
879 if( data->menu.items[lpdis->itemID].count==-1 ) {
880 _stprintf(wbuf, TEXT("Count: All") );
882 _stprintf(wbuf, TEXT("Count: %d"), data->menu.items[lpdis->itemID].count );
885 SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE));
887 /* calculate text rectangle */
888 SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom );
889 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
890 DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
892 /* erase text rectangle */
893 drawRect.left = max(client_rt.left+1, client_rt.right - (drawRect.right - drawRect.left) - 10);
894 drawRect.right = client_rt.right-1;
895 drawRect.top = lpdis->rcItem.top;
896 drawRect.bottom = lpdis->rcItem.bottom;
897 FillRect(lpdis->hDC, &drawRect,
898 menu_bg_brush ? menu_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_MENU));
901 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
902 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
905 if (item->has_focus) {
906 /* draw focus rect */
909 GetClientRect(lpdis->hwndItem, &client_rt);
910 SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom );
911 DrawFocusRect(lpdis->hDC, &drawRect);
914 SetTextColor (lpdis->hDC, OldFg);
915 SetBkColor (lpdis->hDC, OldBg);
916 SelectObject(lpdis->hDC, saveFont);
920 /*-----------------------------------------------------------------------------*/
921 BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch)
925 int curIndex, topIndex, pageSize;
926 boolean is_accelerator = FALSE;
928 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
931 case MENU_FIRST_PAGE:
933 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
934 ListView_EnsureVisible(hwndList, i, FALSE);
938 i = max(0, data->menu.size-1);
939 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
940 ListView_EnsureVisible(hwndList, i, FALSE);
944 topIndex = ListView_GetTopIndex( hwndList );
945 pageSize = ListView_GetCountPerPage( hwndList );
946 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
947 /* Focus down one page */
948 i = min(curIndex+pageSize, data->menu.size-1);
949 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
950 /* Scrollpos down one page */
951 i = min(topIndex+(2*pageSize - 1), data->menu.size-1);
952 ListView_EnsureVisible(hwndList, i, FALSE);
955 case MENU_PREVIOUS_PAGE:
956 topIndex = ListView_GetTopIndex( hwndList );
957 pageSize = ListView_GetCountPerPage( hwndList );
958 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
959 /* Focus up one page */
960 i = max(curIndex-pageSize, 0);
961 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
962 /* Scrollpos up one page */
963 i = max(topIndex-pageSize, 0);
964 ListView_EnsureVisible(hwndList, i, FALSE);
967 case MENU_SELECT_ALL:
968 if( data->how == PICK_ANY ) {
969 reset_menu_count(hwndList, data);
970 for(i=0; i<data->menu.size; i++ ) {
971 SelectMenuItem(hwndList, data, i, -1);
977 case MENU_UNSELECT_ALL:
978 if( data->how == PICK_ANY ) {
979 reset_menu_count(hwndList, data);
980 for(i=0; i<data->menu.size; i++ ) {
981 SelectMenuItem(hwndList, data, i, 0);
987 case MENU_INVERT_ALL:
988 if( data->how == PICK_ANY ) {
989 reset_menu_count(hwndList, data);
990 for(i=0; i<data->menu.size; i++ ) {
995 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1002 case MENU_SELECT_PAGE:
1003 if( data->how == PICK_ANY ) {
1005 reset_menu_count(hwndList, data);
1006 topIndex = ListView_GetTopIndex( hwndList );
1007 pageSize = ListView_GetCountPerPage( hwndList );
1008 from = max(0, topIndex);
1009 to = min(data->menu.size, from+pageSize);
1010 for(i=from; i<to; i++ ) {
1011 SelectMenuItem(hwndList, data, i, -1);
1017 case MENU_UNSELECT_PAGE:
1018 if( data->how == PICK_ANY ) {
1020 reset_menu_count(hwndList, data);
1021 topIndex = ListView_GetTopIndex( hwndList );
1022 pageSize = ListView_GetCountPerPage( hwndList );
1023 from = max(0, topIndex);
1024 to = min(data->menu.size, from+pageSize);
1025 for(i=from; i<to; i++ ) {
1026 SelectMenuItem(hwndList, data, i, 0);
1032 case MENU_INVERT_PAGE:
1033 if( data->how == PICK_ANY ) {
1035 reset_menu_count(hwndList, data);
1036 topIndex = ListView_GetTopIndex( hwndList );
1037 pageSize = ListView_GetCountPerPage( hwndList );
1038 from = max(0, topIndex);
1039 to = min(data->menu.size, from+pageSize);
1040 for(i=from; i<to; i++ ) {
1045 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1053 if( data->how==PICK_ANY || data->how==PICK_ONE ) {
1056 reset_menu_count(hwndList, data);
1057 if( mswin_getlin_window("Search for:", buf, BUFSZ)==IDCANCEL ) {
1058 strcpy(buf, "\033");
1060 SetFocus(hwndList); // set focus back to the list control
1061 if (!*buf || *buf == '\033') return -2;
1062 for(i=0; i<data->menu.size; i++ ) {
1063 if( NHMENU_IS_SELECTABLE(data->menu.items[i])
1064 && strstr(data->menu.items[i].str, buf) ) {
1065 if (data->how == PICK_ANY) {
1070 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1072 } else if( data->how == PICK_ONE ) {
1079 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1080 ListView_EnsureVisible(hwndList, i, FALSE);
1092 if (GetNHApp()->regNetHackMode) {
1093 /* NetHack mode: Scroll down one page,
1094 ends menu when on last page. */
1097 si.cbSize = sizeof(SCROLLINFO);
1098 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1099 GetScrollInfo(hwndList, SB_VERT, &si);
1100 if ((si.nPos + (int)si.nPage) > (si.nMax - si.nMin)) {
1101 /* We're at the bottom: dismiss. */
1106 /* We're not at the bottom: page down. */
1107 topIndex = ListView_GetTopIndex( hwndList );
1108 pageSize = ListView_GetCountPerPage( hwndList );
1109 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1110 /* Focus down one page */
1111 i = min(curIndex+pageSize, data->menu.size-1);
1112 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1113 /* Scrollpos down one page */
1114 i = min(topIndex+(2*pageSize - 1), data->menu.size-1);
1115 ListView_EnsureVisible(hwndList, i, FALSE);
1119 /* Windows mode: ends menu for PICK_ONE/PICK_NONE
1120 select item for PICK_ANY */
1121 if( data->how==PICK_ONE || data->how==PICK_NONE ) {
1125 } else if( data->how==PICK_ANY ) {
1126 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1132 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1141 if( strchr(data->menu.gacc, ch) &&
1142 !(ch=='0' && data->menu.counting) ) {
1143 /* matched a group accelerator */
1144 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1145 reset_menu_count(hwndList, data);
1146 for(i=0; i<data->menu.size; i++ ) {
1147 if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
1148 data->menu.items[i].group_accel == ch ) {
1149 if( data->how == PICK_ANY ) {
1154 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1156 } else if( data->how == PICK_ONE ) {
1178 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1180 count = data->menu.items[i].count;
1181 if( count==-1 ) count=0;
1183 count += (int)(ch - '0');
1184 if (count != 0) /* ignore leading zeros */ {
1185 data->menu.counting = TRUE;
1186 data->menu.items[i].count = min(100000, count);
1187 ListView_RedrawItems( hwndList, i, i ); /* update count mark */
1193 is_accelerator = FALSE;
1194 for(i=0; i<data->menu.size; i++) {
1195 if( data->menu.items[i].accelerator == ch ) {
1196 is_accelerator = TRUE;
1201 if( (ch>='a' && ch<='z') ||
1202 (ch>='A' && ch<='Z') || is_accelerator) {
1203 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1204 for(i=0; i<data->menu.size; i++ ) {
1205 if( data->menu.items[i].accelerator == ch ) {
1206 if( data->how == PICK_ANY ) {
1211 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1213 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1214 ListView_EnsureVisible(hwndList, i, FALSE);
1216 } else if( data->how == PICK_ONE ) {
1234 reset_menu_count(hwndList, data);
1237 /*-----------------------------------------------------------------------------*/
1238 void mswin_menu_window_size (HWND hWnd, LPSIZE sz)
1249 GetClientRect(hWnd, &rt);
1250 sz->cx = rt.right - rt.left;
1251 sz->cy = rt.bottom - rt.top;
1253 GetWindowRect(hWnd, &wrt);
1254 extra_cx = (wrt.right-wrt.left) - sz->cx;
1256 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
1258 control = GetMenuControl(hWnd);
1259 hdc = GetDC(control);
1261 if( data->type==MENU_TYPE_MENU ) {
1262 /* Calculate the width of the list box. */
1263 saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE));
1264 GetTextMetrics(hdc, &tm);
1265 for(i=0; i<data->menu.size; i++ ) {
1266 LONG menuitemwidth = 0;
1270 p1 = data->menu.items[i].str;
1271 p = strchr(data->menu.items[i].str, '\t');
1276 SetRect ( &tabRect, 0, 0, 1, 1 );
1277 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
1279 NH_A2W(p1, wbuf, BUFSZ),
1282 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE
1284 /* it probably isn't necessary to recompute the tab width now, but do so
1285 * just in case, honoring the previously computed value
1287 menuitemwidth += max(data->menu.tab_stop_size[column],
1288 tabRect.right - tabRect.left);
1289 if (p != NULL) *p = '\t';
1290 else /* last string so, */ break;
1291 /* add the separation only when not the last item */
1292 /* in the last item, we break out of the loop, in the statement just above */
1293 menuitemwidth += TAB_SEPARATION;
1296 p = strchr(p1, '\t');
1299 sz->cx = max(sz->cx,
1300 (LONG)(2*TILE_X + menuitemwidth + tm.tmAveCharWidth*12 + tm.tmOverhang));
1302 SelectObject(hdc, saveFont);
1304 /* Calculate the width of the text box. */
1306 saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE));
1307 GetTextMetrics(hdc, &tm);
1308 SetRect(&text_rt, 0, 0, sz->cx, sz->cy);
1309 DrawText(hdc, data->text.text, _tcslen(data->text.text), &text_rt, DT_CALCRECT | DT_TOP | DT_LEFT | DT_NOPREFIX);
1310 sz->cx = max(sz->cx, text_rt.right - text_rt.left + 5*tm.tmAveCharWidth + tm.tmOverhang);
1311 SelectObject(hdc, saveFont);
1315 ReleaseDC(control, hdc);
1318 /*-----------------------------------------------------------------------------*/
1319 void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1323 if( item<0 || item>=data->menu.size ) return;
1325 if( data->how==PICK_ONE && count!=0 ) {
1326 for(i=0; i<data->menu.size; i++)
1327 if( item!=i && data->menu.items[i].count!=0 ) {
1328 data->menu.items[i].count = 0;
1329 ListView_RedrawItems( hwndList, i, i );
1333 data->menu.items[item].count = count;
1334 ListView_RedrawItems( hwndList, item, item );
1335 reset_menu_count(hwndList, data);
1337 /*-----------------------------------------------------------------------------*/
1338 void reset_menu_count(HWND hwndList, PNHMenuWindow data)
1341 data->menu.counting = FALSE;
1342 if( IsWindow(hwndList) ) {
1343 i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
1344 if( i>=0 ) ListView_RedrawItems( hwndList, i, i );
1347 /*-----------------------------------------------------------------------------*/
1348 /* List window Proc */
1349 LRESULT CALLBACK NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1351 BOOL bUpdateFocusItem;
1353 bUpdateFocusItem = FALSE;
1357 /* filter keyboard input for the control */
1364 if( PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) ) {
1365 if( onListChar(GetParent(hWnd), hWnd, (char)msg.wParam)==-2 ) {
1369 if( processed ) return 0;
1371 if( wParam==VK_LEFT || wParam==VK_RIGHT )
1372 bUpdateFocusItem = TRUE;
1377 bUpdateFocusItem = TRUE;
1382 if( bUpdateFocusItem ) {
1386 /* invalidate the focus rectangle */
1387 i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);
1389 ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1390 InvalidateRect(hWnd, &rt, TRUE);
1394 if( wndProcListViewOrig )
1395 return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam);
1399 /*-----------------------------------------------------------------------------*/
1400 /* Text control window proc - implements scrolling without a cursor */
1401 LRESULT CALLBACK NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1408 /* close on space in Windows mode
1409 page down on space in NetHack mode */
1414 si.cbSize = sizeof(SCROLLINFO);
1415 si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
1416 GetScrollInfo(hWnd, SB_VERT, &si);
1417 /* If nethackmode and not at the end of the list */
1418 if (GetNHApp()->regNetHackMode &&
1419 (si.nPos + (int)si.nPage) <= (si.nMax - si.nMin))
1420 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1422 PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0);
1426 SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1429 SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0);
1432 SendMessage(hWnd, EM_SCROLL, SB_LINEUP, 0);
1435 SendMessage(hWnd, EM_SCROLL, SB_LINEDOWN, 0);
1443 if( editControlWndProc )
1444 return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam);
1448 /*-----------------------------------------------------------------------------*/