1 /* NetHack 3.6 mhmenu.c $NHDT-Date: 1524689398 2018/04/25 20:49:58 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.28 $ */
2 /* Copyright (c) 2009 by Michael Allison */
3 /* NetHack may be freely redistributed. See license for details. */
17 #define NHMENU_STR_SIZE BUFSZ
18 #define MIN_TABSTOP_SIZE 0
20 #define TAB_SEPARATION 10 /* pixels between each tab stop */
22 typedef struct mswin_menu_item {
28 char str[NHMENU_STR_SIZE];
33 } NHMenuItem, *PNHMenuItem;
35 typedef struct mswin_nethack_menu_window {
36 int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */
37 int how; /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */
41 int size; /* number of items in items[] */
42 int allocated; /* number of allocated slots in items[] */
43 PNHMenuItem items; /* menu items */
44 char gacc[QBUFSZ]; /* group accelerators */
45 BOOL counting; /* counting flag */
46 char prompt[QBUFSZ]; /* menu prompt */
47 int tab_stop_size[NUMTABS]; /* tabstops to align option values */
58 HBITMAP bmpCheckedCount;
59 HBITMAP bmpNotChecked;
60 } NHMenuWindow, *PNHMenuWindow;
62 extern short glyph2tile[];
64 static WNDPROC wndProcListViewOrig = NULL;
65 static WNDPROC editControlWndProc = NULL;
67 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj != NULL)
68 #define NHMENU_IS_SELECTED(item) ((item).count != 0)
70 LRESULT CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM);
71 LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM);
72 LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM);
73 static void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam,
75 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
76 static LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
77 static LRESULT 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,
84 static void reset_menu_count(HWND hwndList, PNHMenuWindow data);
85 static LRESULT onListChar(HWND hWnd, HWND hwndList, WORD ch);
86 static char *parse_menu_str(char *dest, const char *src, size_t size);
89 mswin_init_menu_window(int type)
93 ret = CreateDialog(GetNHApp()->hApp, MAKEINTRESOURCE(IDD_MENU),
94 GetNHApp()->hMainWnd, MenuWndProc);
96 panic("Cannot create menu window");
99 SetMenuType(ret, type);
104 mswin_menu_window_select_menu(HWND hWnd, int how, MENU_ITEM_P **_selected)
108 MENU_ITEM_P *selected = NULL;
111 char accell_str[256];
113 assert(_selected != NULL);
117 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
120 SetMenuListType(hWnd, how);
122 /* Ok, now give items a unique accelerators */
123 ZeroMemory(accell_str, sizeof(accell_str));
126 #if defined(WIN_CE_SMARTPHONE)
127 if (data->menu.size > 10) {
128 *ap++ = MENU_FIRST_PAGE;
129 *ap++ = MENU_LAST_PAGE;
130 *ap++ = MENU_NEXT_PAGE;
131 *ap++ = MENU_PREVIOUS_PAGE;
132 if (data->how == PICK_ANY) {
133 *ap++ = MENU_SELECT_ALL;
134 *ap++ = MENU_UNSELECT_ALL;
135 *ap++ = MENU_INVERT_ALL;
136 *ap++ = MENU_SELECT_PAGE;
137 *ap++ = MENU_UNSELECT_PAGE;
138 *ap++ = MENU_INVERT_PAGE;
144 if (data->type == MENU_TYPE_MENU) {
145 char next_char = 'a';
147 for (i = 0; i < data->menu.size; i++) {
148 if (data->menu.items[i].accelerator != 0) {
149 *ap++ = data->menu.items[i].accelerator;
150 next_char = (char) (data->menu.items[i].accelerator + 1);
151 } else if (NHMENU_IS_SELECTABLE(data->menu.items[i])) {
152 if ((next_char >= 'a' && next_char <= 'z')
153 || (next_char >= 'A' && next_char <= 'Z')) {
154 data->menu.items[i].accelerator = next_char;
155 *ap++ = data->menu.items[i].accelerator;
159 else if (next_char > 'Z')
162 data->menu.items[i].accelerator = next_char;
163 *ap++ = data->menu.items[i].accelerator;
170 /* collect group accelerators */
171 data->menu.gacc[0] = '\0';
172 ap = data->menu.gacc;
173 if (data->how != PICK_NONE) {
174 for (i = 0; i < data->menu.size; i++) {
175 if (data->menu.items[i].group_accel
176 && !strchr(data->menu.gacc,
177 data->menu.items[i].group_accel)) {
178 *ap++ = data->menu.items[i].group_accel;
184 reset_menu_count(NULL, data);
187 #if defined(WIN_CE_SMARTPHONE)
188 if (data->type == MENU_TYPE_MENU)
189 NHSPhoneSetKeypadFromString(accell_str);
192 mswin_popup_display(hWnd, &data->done);
195 if (data->result != -1) {
196 if (how == PICK_NONE) {
197 if (data->result >= 0)
201 } else if (how == PICK_ONE || how == PICK_ANY) {
202 /* count selected items */
204 for (i = 0; i < data->menu.size; i++) {
205 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
206 && NHMENU_IS_SELECTED(data->menu.items[i])) {
214 (MENU_ITEM_P *) malloc(ret_val * sizeof(MENU_ITEM_P));
216 panic("out of memory");
219 for (i = 0; i < data->menu.size; i++) {
220 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
221 && NHMENU_IS_SELECTED(data->menu.items[i])) {
222 selected[sel_ind].item =
223 data->menu.items[i].identifier;
224 selected[sel_ind].count = data->menu.items[i].count;
229 *_selected = selected;
234 mswin_popup_destroy(hWnd);
236 #if defined(WIN_CE_SMARTPHONE)
237 if (data->type == MENU_TYPE_MENU)
238 NHSPhoneSetKeypadDefault();
245 MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
249 CheckInputDialog(hWnd, message, wParam, lParam);
251 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
253 case WM_INITDIALOG: {
257 text_control = GetDlgItem(hWnd, IDC_MENU_TEXT);
259 data = (PNHMenuWindow) malloc(sizeof(NHMenuWindow));
260 ZeroMemory(data, sizeof(NHMenuWindow));
261 data->type = MENU_TYPE_TEXT;
262 data->how = PICK_NONE;
266 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL));
267 data->bmpCheckedCount =
268 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT));
269 data->bmpNotChecked =
270 LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL));
271 SetWindowLong(hWnd, GWL_USERDATA, (LONG) data);
273 /* subclass edit control */
275 (WNDPROC) GetWindowLong(text_control, GWL_WNDPROC);
276 SetWindowLong(text_control, GWL_WNDPROC, (LONG) NHMenuTextWndProc);
278 /* set text window font */
279 hDC = GetDC(text_control);
280 SendMessage(text_control, WM_SETFONT,
281 (WPARAM) mswin_get_font(NHW_TEXT, ATR_NONE, hDC, FALSE),
283 ReleaseDC(text_control, hDC);
285 #if defined(WIN_CE_SMARTPHONE)
286 /* special initialization for SmartPhone dialogs */
287 NHSPhoneDialogSetup(hWnd, IDC_SPHONE_DIALOGBAR, FALSE,
288 GetNHApp()->bFullScreen);
292 case WM_MSNH_COMMAND:
293 onMSNHCommand(hWnd, wParam, lParam);
301 switch (LOWORD(wParam)) {
303 if (data->type == MENU_TYPE_MENU
304 && (data->how == PICK_ONE || data->how == PICK_ANY)
305 && data->menu.counting) {
309 /* reset counter if counting is in progress */
310 list = GetMenuControl(hWnd);
311 i = ListView_GetNextItem(list, -1, LVNI_FOCUSED);
313 SelectMenuItem(list, data, i, 0);
330 LPNMHDR lpnmhdr = (LPNMHDR) lParam;
331 switch (LOWORD(wParam)) {
332 case IDC_MENU_LIST: {
333 if (!data || data->type != MENU_TYPE_MENU)
336 switch (lpnmhdr->code) {
337 case LVN_ITEMACTIVATE: {
338 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
339 if (data->how == PICK_ONE) {
340 if (lpnmlv->iItem >= 0 && lpnmlv->iItem < data->menu.size
341 && NHMENU_IS_SELECTABLE(
342 data->menu.items[lpnmlv->iItem])) {
343 SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
349 } else if (data->how == PICK_ANY) {
350 if (lpnmlv->iItem >= 0 && lpnmlv->iItem < data->menu.size
351 && NHMENU_IS_SELECTABLE(
352 data->menu.items[lpnmlv->iItem])) {
353 SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
356 data->menu.items[lpnmlv->iItem])
364 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
365 if (lpnmlv->iItem == -1)
367 if (data->how == PICK_ANY) {
369 lpnmlv->hdr.hwndFrom, data, lpnmlv->iItem,
370 NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])
376 case LVN_ITEMCHANGED: {
377 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
378 if (lpnmlv->iItem == -1)
380 if (!(lpnmlv->uChanged & LVIF_STATE))
383 /* update item that has the focus */
384 data->menu.items[lpnmlv->iItem].has_focus =
385 !!(lpnmlv->uNewState & LVIS_FOCUSED);
386 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
389 /* update count for single-selection menu (follow the listview
391 if (data->how == PICK_ONE) {
392 if (lpnmlv->uNewState & LVIS_SELECTED) {
393 SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
398 /* check item focus */
399 data->menu.items[lpnmlv->iItem].has_focus =
400 !!(lpnmlv->uNewState & LVIS_FOCUSED);
401 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
406 reset_menu_count(lpnmhdr->hwndFrom, data);
414 if (hWnd != GetNHApp()->hPopupWnd) {
415 SetFocus(GetNHApp()->hPopupWnd);
421 if (wParam == IDC_MENU_LIST)
422 return onMeasureItem(hWnd, wParam, lParam);
427 if (wParam == IDC_MENU_LIST)
428 return onDrawItem(hWnd, wParam, lParam);
433 case WM_CTLCOLOREDIT:
434 case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */
435 HDC hdcEdit = (HDC) wParam;
436 HWND hwndEdit = (HWND) lParam;
437 if (hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT)) {
438 SetBkColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_BG));
439 SetTextColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_FG));
440 return (BOOL) mswin_get_brush(NHW_TEXT, MSWIN_COLOR_BG);
447 DeleteObject(data->bmpChecked);
448 DeleteObject(data->bmpCheckedCount);
449 DeleteObject(data->bmpNotChecked);
450 if (data->type == MENU_TYPE_TEXT) {
451 if (data->text.text) {
452 mswin_free_text_buffer(data->text.text);
453 data->text.text = NULL;
457 SetWindowLong(hWnd, GWL_USERDATA, (LONG) 0);
465 CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
467 #if defined(WIN_CE_POCKETPC)
470 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
472 if (!(data && data->type == MENU_TYPE_MENU
473 && (data->how == PICK_ONE || data->how == PICK_ANY)))
478 if (GetNHApp()->bUseSIP)
479 SHSipPreference(hWnd, SIP_UP);
484 if (GetNHApp()->bUseSIP)
485 SHSipPreference(hWnd, SIP_DOWN);
489 LPNMHDR lpnmhdr = (LPNMHDR) lParam;
490 switch (lpnmhdr->code) {
492 if (GetNHApp()->bUseSIP)
493 SHSipPreference(hWnd, SIP_UP);
496 if (GetNHApp()->bUseSIP)
497 SHSipPreference(hWnd, SIP_DOWN);
504 switch (HIWORD(wParam)) {
506 if (GetNHApp()->bUseSIP)
507 SHSipPreference(hWnd, SIP_UP);
510 if (GetNHApp()->bUseSIP)
511 SHSipPreference(hWnd, SIP_DOWN);
521 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
525 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
527 case MSNH_MSG_PUTSTR: {
528 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam;
531 if (data->type != MENU_TYPE_TEXT)
532 SetMenuType(hWnd, MENU_TYPE_TEXT);
534 if (!data->text.text) {
535 data->text.text = mswin_init_text_buffer(
536 program_state.gameover ? FALSE : GetNHApp()->bWrapText);
537 if (!data->text.text)
541 mswin_add_text(data->text.text, msg_data->attr, msg_data->text);
543 text_view = GetDlgItem(hWnd, IDC_MENU_TEXT);
545 panic("cannot get text view window");
546 mswin_render_text(data->text.text, text_view);
549 case MSNH_MSG_STARTMENU: {
552 if (data->type != MENU_TYPE_MENU)
553 SetMenuType(hWnd, MENU_TYPE_MENU);
555 if (data->menu.items)
556 free(data->menu.items);
557 data->how = PICK_NONE;
558 data->menu.items = NULL;
560 data->menu.allocated = 0;
563 for (i = 0; i < NUMTABS; ++i)
564 data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
567 case MSNH_MSG_ADDMENU: {
568 PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu) lParam;
575 if (data->type != MENU_TYPE_MENU)
577 if (strlen(msg_data->str) == 0)
580 if (data->menu.size == data->menu.allocated) {
581 data->menu.allocated += 10;
582 data->menu.items = (PNHMenuItem) realloc(
583 data->menu.items, data->menu.allocated * sizeof(NHMenuItem));
586 new_item = data->menu.size;
587 ZeroMemory(&data->menu.items[new_item],
588 sizeof(data->menu.items[new_item]));
589 data->menu.items[new_item].glyph = msg_data->glyph;
590 data->menu.items[new_item].identifier = *msg_data->identifier;
591 data->menu.items[new_item].accelerator = msg_data->accelerator;
592 data->menu.items[new_item].group_accel = msg_data->group_accel;
593 data->menu.items[new_item].attr = msg_data->attr;
594 parse_menu_str(data->menu.items[new_item].str, msg_data->str,
596 data->menu.items[new_item].presel = msg_data->presel;
598 /* calculate tabstop size */
599 p = strchr(data->menu.items[new_item].str, '\t');
601 data->menu.items[new_item].has_tab = TRUE;
603 saveFont = SelectObject(
604 hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE));
605 p1 = data->menu.items[new_item].str;
610 SetRect(&drawRect, 0, 0, 1, 1);
612 *p = '\0'; /* for time being, view tab field as zstring */
613 DrawText(hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect,
614 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS
616 data->menu.tab_stop_size[column] =
617 max(data->menu.tab_stop_size[column],
618 drawRect.right - drawRect.left);
621 else /* last string so, */
626 p = strchr(p1, '\t');
628 SelectObject(hDC, saveFont);
629 ReleaseDC(hWnd, hDC);
631 data->menu.items[new_item].has_tab = FALSE;
638 case MSNH_MSG_ENDMENU: {
639 PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu) lParam;
640 if (msg_data->text) {
641 strncpy(data->menu.prompt, msg_data->text,
642 sizeof(data->menu.prompt) - 1);
644 ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
652 LayoutMenu(HWND hWnd)
658 POINT pt_elem, pt_ok, pt_cancel;
659 SIZE sz_elem, sz_ok, sz_cancel;
661 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
662 menu_ok = GetDlgItem(hWnd, IDOK);
663 menu_cancel = GetDlgItem(hWnd, IDCANCEL);
665 /* get window coordinates */
666 GetClientRect(hWnd, &clrt);
668 /* set window placements */
669 if (IsWindow(menu_ok)) {
670 GetWindowRect(menu_ok, &rt);
671 sz_ok.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
672 sz_ok.cy = rt.bottom - rt.top;
673 pt_ok.x = clrt.left + MENU_MARGIN;
674 pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy;
675 MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE);
678 pt_ok.y = clrt.bottom;
679 sz_ok.cx = sz_ok.cy = 0;
682 if (IsWindow(menu_cancel)) {
683 GetWindowRect(menu_cancel, &rt);
684 sz_cancel.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
685 sz_cancel.cy = rt.bottom - rt.top;
686 pt_cancel.x = (clrt.left + clrt.right) / 2 + MENU_MARGIN;
687 pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy;
688 MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx,
692 pt_cancel.y = clrt.bottom;
693 sz_cancel.cx = sz_cancel.cy = 0;
696 pt_elem.x = clrt.left + MENU_MARGIN;
697 pt_elem.y = clrt.top + MENU_MARGIN;
698 sz_elem.cx = (clrt.right - clrt.left) - 2 * MENU_MARGIN;
699 sz_elem.cy = min(pt_cancel.y, pt_ok.y) - MENU_MARGIN - pt_elem.y;
700 MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx,
703 /* reformat text for the text menu */
704 if (data && data->type == MENU_TYPE_TEXT && data->text.text)
705 mswin_render_text(data->text.text, GetMenuControl(hWnd));
709 SetMenuType(HWND hWnd, int type)
714 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
718 text = GetDlgItem(hWnd, IDC_MENU_TEXT);
719 list = GetDlgItem(hWnd, IDC_MENU_LIST);
720 if (data->type == MENU_TYPE_TEXT) {
721 ShowWindow(list, SW_HIDE);
722 EnableWindow(list, FALSE);
723 EnableWindow(text, TRUE);
724 ShowWindow(text, SW_SHOW);
727 ShowWindow(text, SW_HIDE);
728 EnableWindow(text, FALSE);
729 EnableWindow(list, TRUE);
730 ShowWindow(list, SW_SHOW);
737 SetMenuListType(HWND hWnd, int how)
751 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
752 if (data->type != MENU_TYPE_MENU)
759 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL
760 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
764 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL
765 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
769 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL
770 | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
774 panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
776 if (strlen(data->menu.prompt) == 0) {
777 dwStyles |= LVS_NOCOLUMNHEADER;
780 GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
781 DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
782 control = CreateWindow(WC_LISTVIEW, NULL, dwStyles, rt.left, rt.top,
783 rt.right - rt.left, rt.bottom - rt.top, hWnd,
784 (HMENU) IDC_MENU_LIST, GetNHApp()->hApp, NULL);
786 panic("cannot create menu control");
788 /* install the hook for the control window procedure */
789 wndProcListViewOrig = (WNDPROC) GetWindowLong(control, GWL_WNDPROC);
790 SetWindowLong(control, GWL_WNDPROC, (LONG) NHMenuListWndProc);
792 /* set control font */
793 fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM) 0, (LPARAM) 0);
794 SendMessage(control, WM_SETFONT, (WPARAM) fnt, (LPARAM) 0);
796 /* set control colors */
797 ListView_SetBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
798 ListView_SetTextBkColor(control,
799 mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
800 ListView_SetTextColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG));
802 /* add column to the list view */
803 mswin_menu_window_size(hWnd, &wnd_size);
805 ZeroMemory(&lvcol, sizeof(lvcol));
806 lvcol.mask = LVCF_WIDTH | LVCF_TEXT;
807 lvcol.cx = max(wnd_size.cx, GetSystemMetrics(SM_CXSCREEN));
808 lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ);
809 ListView_InsertColumn(control, 0, &lvcol);
811 /* add items to the list view */
812 for (i = 0; i < data->menu.size; i++) {
814 ZeroMemory(&lvitem, sizeof(lvitem));
815 sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '),
816 data->menu.items[i].str);
818 lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
821 lvitem.state = data->menu.items[i].presel ? LVIS_SELECTED : 0;
822 lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ);
823 lvitem.lParam = (LPARAM) &data->menu.items[i];
824 nItem = SendMessage(control, LB_ADDSTRING, (WPARAM) 0, (LPARAM) buf);
825 if (ListView_InsertItem(control, &lvitem) == -1) {
826 panic("cannot insert menu item");
833 GetMenuControl(HWND hWnd)
837 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
839 if (data->type == MENU_TYPE_TEXT) {
840 return GetDlgItem(hWnd, IDC_MENU_TEXT);
842 return GetDlgItem(hWnd, IDC_MENU_LIST);
847 onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
849 LPMEASUREITEMSTRUCT lpmis;
856 lpmis = (LPMEASUREITEMSTRUCT) lParam;
857 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
858 GetClientRect(GetMenuControl(hWnd), &list_rect);
860 hdc = GetDC(GetMenuControl(hWnd));
862 SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
863 GetTextMetrics(hdc, &tm);
865 /* Set the height of the list box items. */
866 lpmis->itemHeight = max(tm.tmHeight, TILE_Y) + 2;
867 lpmis->itemWidth = list_rect.right - list_rect.left;
869 SelectObject(hdc, saveFont);
870 ReleaseDC(GetMenuControl(hWnd), hdc);
875 onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
877 LPDRAWITEMSTRUCT lpdis;
888 COLORREF OldBg, OldFg, NewBg;
892 lpdis = (LPDRAWITEMSTRUCT) lParam;
894 /* If there are no list box items, skip this message. */
895 if (lpdis->itemID == -1)
898 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
900 item = &data->menu.items[lpdis->itemID];
902 tileDC = CreateCompatibleDC(lpdis->hDC);
903 saveFont = SelectObject(
904 lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE));
905 NewBg = mswin_get_color(NHW_MENU, MSWIN_COLOR_BG);
906 OldBg = SetBkColor(lpdis->hDC, NewBg);
908 SetTextColor(lpdis->hDC, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG));
910 GetTextMetrics(lpdis->hDC, &tm);
912 x = lpdis->rcItem.left + 1;
914 /* print check mark if it is a "selectable" menu */
915 if (data->how != PICK_NONE) {
916 if (NHMENU_IS_SELECTABLE(*item)) {
921 switch (item->count) {
923 hbrCheckMark = CreatePatternBrush(data->bmpChecked);
926 hbrCheckMark = CreatePatternBrush(data->bmpNotChecked);
929 hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount);
933 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
934 SetBrushOrgEx(lpdis->hDC, x, y, NULL);
935 saveBrush = SelectObject(lpdis->hDC, hbrCheckMark);
936 PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY);
937 SelectObject(lpdis->hDC, saveBrush);
938 DeleteObject(hbrCheckMark);
942 if (item->accelerator != 0) {
943 buf[0] = item->accelerator;
946 SetRect(&drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right,
947 lpdis->rcItem.bottom);
948 DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect,
949 DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
951 x += tm.tmAveCharWidth + tm.tmOverhang + 5;
953 x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10;
957 /* print glyph if present */
958 if (item->glyph != NO_GLYPH) {
961 saveBmp = SelectObject(tileDC, GetNHApp()->bmpTiles);
962 ntile = glyph2tile[item->glyph];
963 t_x = (ntile % TILES_PER_LINE) * TILE_X;
964 t_y = (ntile / TILES_PER_LINE) * TILE_Y;
966 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
968 nhapply_image_transparent(lpdis->hDC, x, y, TILE_X, TILE_Y, tileDC,
969 t_x, t_y, TILE_X, TILE_Y, TILE_BK_COLOR);
970 SelectObject(tileDC, saveBmp);
978 p = strchr(item->str, '\t');
980 SetRect(&drawRect, x, lpdis->rcItem.top,
981 min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
982 lpdis->rcItem.bottom);
986 *p = '\0'; /* for time being, view tab field as zstring */
987 DrawText(lpdis->hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1),
988 &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
991 else /* last string so, */
995 p = strchr(p1, '\t');
996 drawRect.left = drawRect.right + TAB_SEPARATION;
999 min(drawRect.left + data->menu.tab_stop_size[column],
1000 lpdis->rcItem.right);
1004 SetRect(&drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right,
1005 lpdis->rcItem.bottom);
1006 DrawText(lpdis->hDC, NH_A2W(item->str, wbuf, BUFSZ),
1007 strlen(item->str), &drawRect,
1008 DT_LEFT | DT_VCENTER | DT_SINGLELINE);
1011 /* draw focused item */
1012 if (item->has_focus) {
1016 GetClientRect(lpdis->hwndItem, &client_rt);
1017 if (NHMENU_IS_SELECTABLE(*item)
1018 && data->menu.items[lpdis->itemID].count > 0
1019 && item->glyph != NO_GLYPH) {
1020 if (data->menu.items[lpdis->itemID].count == -1) {
1021 _stprintf(wbuf, TEXT("Count: All"));
1023 _stprintf(wbuf, TEXT("Count: %d"),
1024 data->menu.items[lpdis->itemID].count);
1027 SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK,
1028 lpdis->hDC, FALSE));
1030 /* calculate text rectangle */
1031 SetRect(&drawRect, client_rt.left, lpdis->rcItem.top,
1032 client_rt.right, lpdis->rcItem.bottom);
1033 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1034 DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE
1037 /* erase text rectangle */
1039 max(client_rt.left + 1,
1040 client_rt.right - (drawRect.right - drawRect.left) - 10);
1041 drawRect.right = client_rt.right - 1;
1042 drawRect.top = lpdis->rcItem.top;
1043 drawRect.bottom = lpdis->rcItem.bottom;
1044 bkBrush = CreateSolidBrush(GetBkColor(lpdis->hDC));
1045 FillRect(lpdis->hDC, &drawRect, bkBrush);
1046 DeleteObject(bkBrush);
1049 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1050 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
1053 /* draw focus rect */
1054 SetRect(&drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right,
1055 lpdis->rcItem.bottom);
1056 DrawFocusRect(lpdis->hDC, &drawRect);
1059 SetTextColor(lpdis->hDC, OldFg);
1060 SetBkColor(lpdis->hDC, OldBg);
1061 SelectObject(lpdis->hDC, saveFont);
1067 onListChar(HWND hWnd, HWND hwndList, WORD ch)
1071 int curIndex, topIndex, pageSize;
1072 boolean is_accelerator = FALSE;
1074 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
1077 case MENU_FIRST_PAGE:
1079 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1080 ListView_EnsureVisible(hwndList, i, FALSE);
1083 case MENU_LAST_PAGE:
1084 i = max(0, data->menu.size - 1);
1085 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1086 ListView_EnsureVisible(hwndList, i, FALSE);
1089 case MENU_NEXT_PAGE:
1090 topIndex = ListView_GetTopIndex(hwndList);
1091 pageSize = ListView_GetCountPerPage(hwndList);
1092 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1093 /* Focus down one page */
1094 i = min(curIndex + pageSize, data->menu.size - 1);
1095 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1096 /* Scrollpos down one page */
1097 i = min(topIndex + (2 * pageSize - 1), data->menu.size - 1);
1098 ListView_EnsureVisible(hwndList, i, FALSE);
1101 case MENU_PREVIOUS_PAGE:
1102 topIndex = ListView_GetTopIndex(hwndList);
1103 pageSize = ListView_GetCountPerPage(hwndList);
1104 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1105 /* Focus up one page */
1106 i = max(curIndex - pageSize, 0);
1107 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1108 /* Scrollpos up one page */
1109 i = max(topIndex - pageSize, 0);
1110 ListView_EnsureVisible(hwndList, i, FALSE);
1113 case MENU_SELECT_ALL:
1114 if (data->how == PICK_ANY) {
1115 reset_menu_count(hwndList, data);
1116 for (i = 0; i < data->menu.size; i++) {
1117 SelectMenuItem(hwndList, data, i, -1);
1123 case MENU_UNSELECT_ALL:
1124 if (data->how == PICK_ANY) {
1125 reset_menu_count(hwndList, data);
1126 for (i = 0; i < data->menu.size; i++) {
1127 SelectMenuItem(hwndList, data, i, 0);
1133 case MENU_INVERT_ALL:
1134 if (data->how == PICK_ANY) {
1135 reset_menu_count(hwndList, data);
1136 for (i = 0; i < data->menu.size; i++) {
1137 SelectMenuItem(hwndList, data, i,
1138 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1145 case MENU_SELECT_PAGE:
1146 if (data->how == PICK_ANY) {
1148 reset_menu_count(hwndList, data);
1149 topIndex = ListView_GetTopIndex(hwndList);
1150 pageSize = ListView_GetCountPerPage(hwndList);
1151 from = max(0, topIndex);
1152 to = min(data->menu.size, from + pageSize);
1153 for (i = from; i < to; i++) {
1154 SelectMenuItem(hwndList, data, i, -1);
1160 case MENU_UNSELECT_PAGE:
1161 if (data->how == PICK_ANY) {
1163 reset_menu_count(hwndList, data);
1164 topIndex = ListView_GetTopIndex(hwndList);
1165 pageSize = ListView_GetCountPerPage(hwndList);
1166 from = max(0, topIndex);
1167 to = min(data->menu.size, from + pageSize);
1168 for (i = from; i < to; i++) {
1169 SelectMenuItem(hwndList, data, i, 0);
1175 case MENU_INVERT_PAGE:
1176 if (data->how == PICK_ANY) {
1178 reset_menu_count(hwndList, data);
1179 topIndex = ListView_GetTopIndex(hwndList);
1180 pageSize = ListView_GetCountPerPage(hwndList);
1181 from = max(0, topIndex);
1182 to = min(data->menu.size, from + pageSize);
1183 for (i = from; i < to; i++) {
1184 SelectMenuItem(hwndList, data, i,
1185 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1193 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1197 reset_menu_count(hwndList, data);
1198 mswin_getlin("Search for:", buf);
1199 if (!*buf || *buf == '\033')
1202 for (i = 0; i < data->menu.size; i++) {
1203 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1204 && strstr(data->menu.items[i].str, buf)) {
1205 if (data->how == PICK_ANY) {
1208 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0 : -1);
1209 /* save the first item - we will move focus to it */
1210 if (selected_item == -1)
1212 } else if (data->how == PICK_ONE) {
1213 SelectMenuItem(hwndList, data, i, -1);
1220 if (selected_item > 0) {
1221 ListView_SetItemState(hwndList, selected_item, LVIS_FOCUSED,
1223 ListView_EnsureVisible(hwndList, selected_item, FALSE);
1231 /* ends menu for PICK_ONE/PICK_NONE
1232 select item for PICK_ANY */
1233 if (data->how == PICK_ONE || data->how == PICK_NONE) {
1237 } else if (data->how == PICK_ANY) {
1238 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1240 SelectMenuItem(hwndList, data, i,
1241 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1249 if (strchr(data->menu.gacc, ch)
1250 && !(ch == '0' && data->menu.counting)) {
1251 /* matched a group accelerator */
1252 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1253 reset_menu_count(hwndList, data);
1254 for (i = 0; i < data->menu.size; i++) {
1255 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1256 && data->menu.items[i].group_accel == ch) {
1257 if (data->how == PICK_ANY) {
1260 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1262 } else if (data->how == PICK_ONE) {
1263 SelectMenuItem(hwndList, data, i, -1);
1279 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1281 count = data->menu.items[i].count;
1285 count += (int) (ch - '0');
1286 if (count != 0) /* ignore leading zeros */ {
1287 data->menu.counting = TRUE;
1288 data->menu.items[i].count = min(100000, count);
1289 ListView_RedrawItems(hwndList, i,
1290 i); /* update count mark */
1296 is_accelerator = FALSE;
1297 for (i = 0; i < data->menu.size; i++) {
1298 if (data->menu.items[i].accelerator == ch) {
1299 is_accelerator = TRUE;
1304 if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
1305 || is_accelerator) {
1306 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1307 for (i = 0; i < data->menu.size; i++) {
1308 if (data->menu.items[i].accelerator == ch) {
1309 if (data->how == PICK_ANY) {
1312 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1314 ListView_SetItemState(hwndList, i, LVIS_FOCUSED,
1316 ListView_EnsureVisible(hwndList, i, FALSE);
1318 } else if (data->how == PICK_ONE) {
1319 SelectMenuItem(hwndList, data, i, -1);
1331 reset_menu_count(hwndList, data);
1336 mswin_menu_window_size(HWND hWnd, LPSIZE sz)
1347 GetClientRect(hWnd, &rt);
1348 sz->cx = rt.right - rt.left;
1349 sz->cy = rt.bottom - rt.top;
1351 GetWindowRect(hWnd, &wrt);
1352 extra_cx = (wrt.right - wrt.left) - sz->cx;
1354 data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
1356 control = GetMenuControl(hWnd);
1357 hdc = GetDC(control);
1359 if (data->type == MENU_TYPE_MENU) {
1360 /* Calculate the width of the list box. */
1361 saveFont = SelectObject(
1362 hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE));
1363 GetTextMetrics(hdc, &tm);
1364 for (i = 0; i < data->menu.size; i++) {
1365 LONG menuitemwidth = 0;
1369 p1 = data->menu.items[i].str;
1370 p = strchr(data->menu.items[i].str, '\t');
1375 SetRect(&tabRect, 0, 0, 1, 1);
1377 *p = '\0'; /* for time being, view tab field as
1379 DrawText(hdc, NH_A2W(p1, wbuf, BUFSZ), strlen(p1),
1380 &tabRect, DT_CALCRECT | DT_LEFT | DT_VCENTER
1382 /* it probably isn't necessary to recompute the tab width
1384 * just in case, honoring the previously computed value
1386 menuitemwidth += max(data->menu.tab_stop_size[column],
1387 tabRect.right - tabRect.left);
1390 else /* last string so, */
1392 /* add the separation only when not the last item */
1393 /* in the last item, we break out of the loop, in the
1394 * statement just above */
1395 menuitemwidth += TAB_SEPARATION;
1398 p = strchr(p1, '\t');
1401 sz->cx = max(sz->cx, (LONG)(2 * TILE_X + menuitemwidth
1402 + tm.tmAveCharWidth * 12
1405 SelectObject(hdc, saveFont);
1407 /* do not change size for text output - the text will be formatted
1413 ReleaseDC(control, hdc);
1418 SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1422 if (item < 0 || item >= data->menu.size)
1425 if (data->how == PICK_ONE && count != 0) {
1426 for (i = 0; i < data->menu.size; i++)
1427 if (item != i && data->menu.items[i].count != 0) {
1428 data->menu.items[i].count = 0;
1429 ListView_RedrawItems(hwndList, i, i);
1433 data->menu.items[item].count = count;
1434 ListView_RedrawItems(hwndList, item, item);
1435 reset_menu_count(hwndList, data);
1439 reset_menu_count(HWND hwndList, PNHMenuWindow data)
1442 data->menu.counting = FALSE;
1443 if (IsWindow(hwndList)) {
1444 i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
1446 ListView_RedrawItems(hwndList, i, i);
1450 /* List window Proc */
1452 NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1454 BOOL bUpdateFocusItem = FALSE;
1457 /* filter keyboard input for the control */
1458 #if !defined(WIN_CE_SMARTPHONE)
1463 if (PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
1464 if (onListChar(GetParent(hWnd), hWnd, (char) msg.wParam) == -2) {
1469 if (wParam == VK_LEFT || wParam == VK_RIGHT)
1470 bUpdateFocusItem = TRUE;
1473 /* tell Windows not to process arrow keys */
1475 return DLGC_WANTARROWS;
1477 #else /* defined(WIN_CE_SMARTPHONE) */
1479 if (wParam == VK_TACTION) {
1480 if (onListChar(GetParent(hWnd), hWnd, ' ') == -2) {
1483 } else if (NHSPhoneTranslateKbdMessage(wParam, lParam, TRUE)) {
1485 BOOL processed = FALSE;
1486 if (mswin_have_input()) {
1487 evt = mswin_input_pop();
1488 if (evt->type == NHEVENT_CHAR
1489 && onListChar(GetParent(hWnd), hWnd, evt->kbd.ch) == -2) {
1493 /* eat the rest of the events */
1494 if (mswin_have_input())
1501 if (wParam == VK_LEFT || wParam == VK_RIGHT)
1502 bUpdateFocusItem = TRUE;
1506 /* translate SmartPhone keyboard message */
1507 if (NHSPhoneTranslateKbdMessage(wParam, lParam, FALSE))
1511 /* tell Windows not to process default button on VK_RETURN */
1513 return DLGC_DEFPUSHBUTTON | DLGC_WANTALLKEYS
1514 | (wndProcListViewOrig
1515 ? CallWindowProc(wndProcListViewOrig, hWnd, message,
1522 bUpdateFocusItem = TRUE;
1526 if (bUpdateFocusItem) {
1530 /* invalidate the focus rectangle */
1531 i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);
1533 ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1534 InvalidateRect(hWnd, &rt, TRUE);
1538 if (wndProcListViewOrig)
1539 return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam,
1545 /* Text control window proc - implements close on space */
1547 NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1554 /* close on space */
1555 PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0);
1560 PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0),
1566 PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0),
1572 PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, 0),
1578 PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, 0),
1582 break; /* case WM_KEYUP: */
1585 if (editControlWndProc)
1586 return CallWindowProc(editControlWndProc, hWnd, message, wParam,
1591 /*----------------------------------------------------------------------------*/
1593 parse_menu_str(char *dest, const char *src, size_t size)
1596 if (!dest || size == 0)
1599 strncpy(dest, src, size);
1600 dest[size - 1] = '\x0';
1602 /* replace "[ ]*\[" with "\t\[" */
1603 p1 = p2 = strstr(dest, " [");
1605 while (p1 != dest && *p1 == ' ')
1607 p1++; /* backup to space */
1609 memmove(p1, p2, strlen(p2));
1610 p1[strlen(p2)] = '\x0';