1 /* 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 typedef struct mswin_menu_item {
26 char str[NHMENU_STR_SIZE];
31 } NHMenuItem, *PNHMenuItem;
33 typedef struct mswin_nethack_menu_window {
34 int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */
35 int how; /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */
39 int size; /* number of items in items[] */
40 int allocated; /* number of allocated slots in items[] */
41 PNHMenuItem items; /* menu items */
42 char gacc[QBUFSZ]; /* group accelerators */
43 BOOL counting; /* counting flag */
44 char prompt[QBUFSZ]; /* menu prompt */
45 int tab_stop_size[NUMTABS];/* tabstops to align option values */
56 HBITMAP bmpCheckedCount;
57 HBITMAP bmpNotChecked;
58 } NHMenuWindow, *PNHMenuWindow;
60 extern short glyph2tile[];
62 static WNDPROC wndProcListViewOrig = NULL;
63 static WNDPROC editControlWndProc = NULL;
65 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL)
66 #define NHMENU_IS_SELECTED(item) ((item).count!=0)
68 LRESULT CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM);
69 LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM);
70 LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM);
71 static void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
72 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
73 static LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
74 static LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
75 static void LayoutMenu(HWND hwnd);
76 static void SetMenuType(HWND hwnd, int type);
77 static void SetMenuListType(HWND hwnd, int now);
78 static HWND GetMenuControl(HWND hwnd);
79 static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count);
80 static void reset_menu_count(HWND hwndList, PNHMenuWindow data);
81 static LRESULT onListChar(HWND hWnd, HWND hwndList, WORD ch);
82 static char* parse_menu_str(char* dest, const char* src, size_t size);
84 HWND mswin_init_menu_window (int type) {
89 MAKEINTRESOURCE(IDD_MENU),
94 panic("Cannot create menu window");
97 SetMenuType(ret, type);
102 int mswin_menu_window_select_menu (HWND hWnd, int how, MENU_ITEM_P ** _selected)
106 MENU_ITEM_P *selected = NULL;
109 char accell_str[256];
111 assert( _selected!=NULL );
115 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
118 SetMenuListType(hWnd, how);
120 /* Ok, now give items a unique accelerators */
121 ZeroMemory(accell_str, sizeof(accell_str));
124 #if defined(WIN_CE_SMARTPHONE)
125 if( data->menu.size>10 ) {
126 *ap++ = MENU_FIRST_PAGE;
127 *ap++ = MENU_LAST_PAGE;
128 *ap++ = MENU_NEXT_PAGE;
129 *ap++ = MENU_PREVIOUS_PAGE;
130 if( data->how == PICK_ANY ) {
131 *ap++ = MENU_SELECT_ALL;
132 *ap++ = MENU_UNSELECT_ALL;
133 *ap++ = MENU_INVERT_ALL;
134 *ap++ = MENU_SELECT_PAGE;
135 *ap++ = MENU_UNSELECT_PAGE;
136 *ap++ = MENU_INVERT_PAGE;
142 if( data->type == MENU_TYPE_MENU ) {
143 char next_char = 'a';
145 for( i=0; i<data->menu.size; i++) {
146 if( data->menu.items[i].accelerator!=0 ) {
147 *ap++ = data->menu.items[i].accelerator;
148 next_char = (char)(data->menu.items[i].accelerator+1);
149 } else if( NHMENU_IS_SELECTABLE(data->menu.items[i]) ) {
150 if ( (next_char>='a' && next_char<='z') ||
151 (next_char>='A' && next_char<='Z') ) {
152 data->menu.items[i].accelerator = next_char;
153 *ap++ = data->menu.items[i].accelerator;
155 if( next_char > 'z' ) next_char = 'A';
156 else if ( next_char > 'Z' ) break;
158 data->menu.items[i].accelerator = next_char;
159 *ap++ = data->menu.items[i].accelerator;
166 /* collect group accelerators */
167 data->menu.gacc[0] = '\0';
168 ap = data->menu.gacc;
169 if( data->how != PICK_NONE ) {
170 for( i=0; i<data->menu.size; i++) {
171 if( data->menu.items[i].group_accel &&
172 !strchr(data->menu.gacc, data->menu.items[i].group_accel) ) {
173 *ap++ = data->menu.items[i].group_accel;
179 reset_menu_count(NULL, data);
182 #if defined(WIN_CE_SMARTPHONE)
183 if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadFromString( accell_str );
186 mswin_popup_display(hWnd, &data->done);
189 if( data->result != -1 ) {
191 if(data->result>=0) ret_val=0;
193 } else if(how==PICK_ONE || how==PICK_ANY) {
194 /* count selected items */
196 for(i=0; i<data->menu.size; i++ ) {
197 if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
198 NHMENU_IS_SELECTED(data->menu.items[i]) ) {
205 selected = (MENU_ITEM_P*)malloc(ret_val*sizeof(MENU_ITEM_P));
206 if( !selected ) panic("out of memory");
209 for(i=0; i<data->menu.size; i++ ) {
210 if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
211 NHMENU_IS_SELECTED(data->menu.items[i]) ) {
212 selected[sel_ind].item = data->menu.items[i].identifier;
213 selected[sel_ind].count = data->menu.items[i].count;
218 *_selected = selected;
223 mswin_popup_destroy(hWnd);
225 #if defined(WIN_CE_SMARTPHONE)
226 if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadDefault();
232 LRESULT CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
236 CheckInputDialog(hWnd, message, wParam, lParam);
238 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
241 case WM_INITDIALOG: {
245 text_control = GetDlgItem(hWnd, IDC_MENU_TEXT);
247 data = (PNHMenuWindow)malloc(sizeof(NHMenuWindow));
248 ZeroMemory(data, sizeof(NHMenuWindow));
249 data->type = MENU_TYPE_TEXT;
250 data->how = PICK_NONE;
253 data->bmpChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL));
254 data->bmpCheckedCount = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT));
255 data->bmpNotChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL));
256 SetWindowLong(hWnd, GWL_USERDATA, (LONG)data);
258 /* subclass edit control */
259 editControlWndProc = (WNDPROC)GetWindowLong(text_control, GWL_WNDPROC);
260 SetWindowLong(text_control, GWL_WNDPROC, (LONG)NHMenuTextWndProc);
262 /* set text window font */
263 hDC = GetDC(text_control);
267 (WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hDC, FALSE),
270 ReleaseDC(text_control, hDC);
272 #if defined(WIN_CE_SMARTPHONE)
273 /* special initialization for SmartPhone dialogs */
274 NHSPhoneDialogSetup(hWnd, FALSE, GetNHApp()->bFullScreen);
278 case WM_MSNH_COMMAND:
279 onMSNHCommand(hWnd, wParam, lParam);
288 switch (LOWORD(wParam))
291 if( data->type == MENU_TYPE_MENU &&
292 (data->how==PICK_ONE || data->how==PICK_ANY) &&
293 data->menu.counting) {
297 /* reset counter if counting is in progress */
298 list = GetMenuControl(hWnd);
299 i = ListView_GetNextItem(list, -1, LVNI_FOCUSED);
301 SelectMenuItem(list, data, i, 0);
319 LPNMHDR lpnmhdr = (LPNMHDR)lParam;
320 switch (LOWORD(wParam)) {
323 if( !data || data->type!=MENU_TYPE_MENU ) break;
325 switch(lpnmhdr->code) {
326 case LVN_ITEMACTIVATE:
328 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
329 if(data->how==PICK_ONE) {
330 if( lpnmlv->iItem>=0 &&
331 lpnmlv->iItem<data->menu.size &&
332 NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) {
334 lpnmlv->hdr.hwndFrom,
343 } else if( data->how==PICK_ANY ) {
344 if( lpnmlv->iItem>=0 &&
345 lpnmlv->iItem<data->menu.size &&
346 NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) {
348 lpnmlv->hdr.hwndFrom,
351 NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1
358 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
359 if( lpnmlv->iItem==-1 ) return 0;
360 if( data->how==PICK_ANY ) {
362 lpnmlv->hdr.hwndFrom,
365 NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1
370 case LVN_ITEMCHANGED:
372 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
373 if( lpnmlv->iItem==-1 ) return 0;
374 if( !(lpnmlv->uChanged & LVIF_STATE) ) return 0;
376 /* update item that has the focus */
377 data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED);
378 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem);
380 /* update count for single-selection menu (follow the listview selection) */
381 if( data->how==PICK_ONE ) {
382 if( lpnmlv->uNewState & LVIS_SELECTED ) {
384 lpnmlv->hdr.hwndFrom,
392 /* check item focus */
393 data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED);
394 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem);
398 reset_menu_count(lpnmhdr->hwndFrom, data);
407 if( hWnd!=GetNHApp()->hPopupWnd ) {
408 SetFocus(GetNHApp()->hPopupWnd );
414 if( wParam==IDC_MENU_LIST )
415 return onMeasureItem(hWnd, wParam, lParam);
420 if( wParam==IDC_MENU_LIST )
421 return onDrawItem(hWnd, wParam, lParam);
426 case WM_CTLCOLOREDIT:
427 case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */
428 HDC hdcEdit = (HDC) wParam;
429 HWND hwndEdit = (HWND) lParam;
430 if( hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT) ) {
431 SetBkColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_BG));
432 SetTextColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_FG));
433 return (BOOL)mswin_get_brush(NHW_TEXT, MSWIN_COLOR_BG);
439 DeleteObject(data->bmpChecked);
440 DeleteObject(data->bmpCheckedCount);
441 DeleteObject(data->bmpNotChecked);
442 if( data->type == MENU_TYPE_TEXT ) {
443 if( data->text.text ) {
444 mswin_free_text_buffer(data->text.text);
445 data->text.text = NULL;
449 SetWindowLong(hWnd, GWL_USERDATA, (LONG)0);
456 void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
458 #if defined(WIN_CE_POCKETPC)
461 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
464 data->type==MENU_TYPE_MENU &&
465 (data->how==PICK_ONE || data->how==PICK_ANY) ) ) return;
469 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
474 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
479 LPNMHDR lpnmhdr = (LPNMHDR)lParam;
480 switch(lpnmhdr->code) {
482 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
485 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
491 switch(HIWORD(wParam)) {
493 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
496 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
505 void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
509 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
511 case MSNH_MSG_PUTSTR:
513 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam;
516 if( data->type!=MENU_TYPE_TEXT )
517 SetMenuType(hWnd, MENU_TYPE_TEXT);
519 if( !data->text.text ) {
520 data->text.text = mswin_init_text_buffer(
521 program_state.gameover? FALSE : GetNHApp()->bWrapText
523 if( !data->text.text ) break;
526 mswin_add_text(data->text.text, msg_data->attr, msg_data->text);
528 text_view = GetDlgItem(hWnd, IDC_MENU_TEXT);
529 if( !text_view ) panic("cannot get text view window");
530 mswin_render_text(data->text.text, text_view);
533 case MSNH_MSG_STARTMENU:
537 if( data->type!=MENU_TYPE_MENU )
538 SetMenuType(hWnd, MENU_TYPE_MENU);
540 if( data->menu.items ) free(data->menu.items);
541 data->how = PICK_NONE;
542 data->menu.items = NULL;
544 data->menu.allocated = 0;
547 for (i = 0; i < NUMTABS; ++i)
548 data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
551 case MSNH_MSG_ADDMENU:
553 PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam;
560 if( data->type!=MENU_TYPE_MENU ) break;
561 if( strlen(msg_data->str)==0 ) break;
563 if( data->menu.size==data->menu.allocated ) {
564 data->menu.allocated += 10;
565 data->menu.items = (PNHMenuItem)realloc(data->menu.items, data->menu.allocated*sizeof(NHMenuItem));
568 new_item = data->menu.size;
569 ZeroMemory( &data->menu.items[new_item], sizeof(data->menu.items[new_item]));
570 data->menu.items[new_item].glyph = msg_data->glyph;
571 data->menu.items[new_item].identifier = *msg_data->identifier;
572 data->menu.items[new_item].accelerator = msg_data->accelerator;
573 data->menu.items[new_item].group_accel = msg_data->group_accel;
574 data->menu.items[new_item].attr = msg_data->attr;
575 parse_menu_str(data->menu.items[new_item].str, msg_data->str, NHMENU_STR_SIZE);
576 data->menu.items[new_item].presel = msg_data->presel;
578 /* calculate tabstop size */
579 p = strchr(data->menu.items[new_item].str, '\t');
581 data->menu.items[new_item].has_tab = TRUE;
583 saveFont = SelectObject(hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE));
584 p1 = data->menu.items[new_item].str;
589 SetRect ( &drawRect, 0, 0, 1, 1 );
590 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
592 NH_A2W(p1, wbuf, BUFSZ),
595 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE
597 data->menu.tab_stop_size[column] =
598 max( data->menu.tab_stop_size[column], drawRect.right - drawRect.left );
599 if (p != NULL) *p = '\t';
600 else /* last string so, */ break;
604 p = strchr(p1, '\t');
606 SelectObject(hDC, saveFont);
607 ReleaseDC(hWnd, hDC);
609 data->menu.items[new_item].has_tab = FALSE;
616 case MSNH_MSG_ENDMENU:
618 PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu)lParam;
619 if( msg_data->text ) {
620 strncpy( data->menu.prompt, msg_data->text, sizeof(data->menu.prompt)-1 );
622 ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
629 void LayoutMenu(HWND hWnd)
635 POINT pt_elem, pt_ok, pt_cancel;
636 SIZE sz_elem, sz_ok, sz_cancel;
638 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
639 menu_ok = GetDlgItem(hWnd, IDOK);
640 menu_cancel = GetDlgItem(hWnd, IDCANCEL);
642 /* get window coordinates */
643 GetClientRect(hWnd, &clrt );
645 /* set window placements */
646 if( IsWindow(menu_ok) ) {
647 GetWindowRect(menu_ok, &rt);
648 sz_ok.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN;
649 sz_ok.cy = rt.bottom-rt.top;
650 pt_ok.x = clrt.left + MENU_MARGIN;
651 pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy;
652 MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE );
655 pt_ok.y = clrt.bottom;
656 sz_ok.cx = sz_ok.cy = 0;
659 if( IsWindow(menu_cancel) ) {
660 GetWindowRect(menu_cancel, &rt);
661 sz_cancel.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN;
662 sz_cancel.cy = rt.bottom-rt.top;
663 pt_cancel.x = (clrt.left + clrt.right)/2 + MENU_MARGIN;
664 pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy;
665 MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx, sz_cancel.cy, TRUE );
668 pt_cancel.y = clrt.bottom;
669 sz_cancel.cx = sz_cancel.cy = 0;
672 pt_elem.x = clrt.left + MENU_MARGIN;
673 pt_elem.y = clrt.top + MENU_MARGIN;
674 sz_elem.cx = (clrt.right - clrt.left) - 2*MENU_MARGIN;
675 sz_elem.cy = min(pt_cancel.y, pt_ok.y) - MENU_MARGIN - pt_elem.y;
676 MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE );
678 /* reformat text for the text menu */
680 data->type==MENU_TYPE_TEXT &&
682 mswin_render_text(data->text.text, GetMenuControl(hWnd));
685 void SetMenuType(HWND hWnd, int type)
690 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
694 text = GetDlgItem(hWnd, IDC_MENU_TEXT);
695 list = GetDlgItem(hWnd, IDC_MENU_LIST);
696 if(data->type==MENU_TYPE_TEXT) {
697 ShowWindow(list, SW_HIDE);
698 EnableWindow(list, FALSE);
699 EnableWindow(text, TRUE);
700 ShowWindow(text, SW_SHOW);
703 ShowWindow(text, SW_HIDE);
704 EnableWindow(text, FALSE);
705 EnableWindow(list, TRUE);
706 ShowWindow(list, SW_SHOW);
712 void SetMenuListType(HWND hWnd, int how)
726 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
727 if( data->type != MENU_TYPE_MENU ) return;
733 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD
734 | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
735 | LVS_OWNERDRAWFIXED | LVS_SINGLESEL;
738 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD
739 | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
740 | LVS_OWNERDRAWFIXED | LVS_SINGLESEL;
743 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD
744 | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
745 | LVS_OWNERDRAWFIXED | LVS_SINGLESEL;
747 default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
749 if( strlen(data->menu.prompt)==0 ) {
750 dwStyles |= LVS_NOCOLUMNHEADER ;
753 GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
754 DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
755 control = CreateWindow(WC_LISTVIEW, NULL,
762 (HMENU)IDC_MENU_LIST,
765 if( !control ) panic( "cannot create menu control" );
767 /* install the hook for the control window procedure */
768 wndProcListViewOrig = (WNDPROC)GetWindowLong(control, GWL_WNDPROC);
769 SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuListWndProc);
771 /* set control font */
772 fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0);
773 SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0);
775 /* set control colors */
776 ListView_SetBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
777 ListView_SetTextBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
778 ListView_SetTextColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG));
780 /* add column to the list view */
781 mswin_menu_window_size(hWnd, &wnd_size);
783 ZeroMemory(&lvcol, sizeof(lvcol));
784 lvcol.mask = LVCF_WIDTH | LVCF_TEXT;
785 lvcol.cx = max( wnd_size.cx, GetSystemMetrics(SM_CXSCREEN));
786 lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ);
787 ListView_InsertColumn(control, 0, &lvcol);
789 /* add items to the list view */
790 for(i=0; i<data->menu.size; i++ ) {
792 ZeroMemory( &lvitem, sizeof(lvitem) );
793 sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str );
795 lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
798 lvitem.state = data->menu.items[i].presel? LVIS_SELECTED : 0;
799 lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ);
800 lvitem.lParam = (LPARAM)&data->menu.items[i];
801 nItem = SendMessage(control, LB_ADDSTRING, (WPARAM)0, (LPARAM) buf);
802 if( ListView_InsertItem(control, &lvitem)==-1 ) {
803 panic("cannot insert menu item");
810 HWND GetMenuControl(HWND hWnd)
814 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
816 if(data->type==MENU_TYPE_TEXT) {
817 return GetDlgItem(hWnd, IDC_MENU_TEXT);
819 return GetDlgItem(hWnd, IDC_MENU_LIST);
824 LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
826 LPMEASUREITEMSTRUCT lpmis;
833 lpmis = (LPMEASUREITEMSTRUCT) lParam;
834 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
835 GetClientRect(GetMenuControl(hWnd), &list_rect);
837 hdc = GetDC(GetMenuControl(hWnd));
838 saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
839 GetTextMetrics(hdc, &tm);
841 /* Set the height of the list box items. */
842 lpmis->itemHeight = max(tm.tmHeight, TILE_Y)+2;
843 lpmis->itemWidth = list_rect.right - list_rect.left;
845 SelectObject(hdc, saveFont);
846 ReleaseDC(GetMenuControl(hWnd), hdc);
850 LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
852 LPDRAWITEMSTRUCT lpdis;
863 COLORREF OldBg, OldFg, NewBg;
867 lpdis = (LPDRAWITEMSTRUCT) lParam;
869 /* If there are no list box items, skip this message. */
870 if (lpdis->itemID == -1) return FALSE;
872 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
874 item = &data->menu.items[lpdis->itemID];
876 tileDC = CreateCompatibleDC(lpdis->hDC);
877 saveFont = SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE));
878 NewBg = mswin_get_color(NHW_MENU, MSWIN_COLOR_BG);
879 OldBg = SetBkColor(lpdis->hDC, NewBg);
880 OldFg = SetTextColor(lpdis->hDC, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG));
882 GetTextMetrics(lpdis->hDC, &tm);
884 x = lpdis->rcItem.left + 1;
886 /* print check mark if it is a "selectable" menu */
887 if( data->how!=PICK_NONE ) {
888 if( NHMENU_IS_SELECTABLE(*item) ) {
893 switch(item->count) {
894 case -1: hbrCheckMark = CreatePatternBrush(data->bmpChecked); break;
895 case 0: hbrCheckMark = CreatePatternBrush(data->bmpNotChecked); break;
896 default: hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount); break;
899 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
900 SetBrushOrgEx(lpdis->hDC, x, y, NULL);
901 saveBrush = SelectObject(lpdis->hDC, hbrCheckMark);
902 PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY);
903 SelectObject(lpdis->hDC, saveBrush);
904 DeleteObject(hbrCheckMark);
908 if(item->accelerator!=0) {
909 buf[0] = item->accelerator;
912 SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom );
913 DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
915 x += tm.tmAveCharWidth + tm.tmOverhang + 5;
917 x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10;
921 /* print glyph if present */
922 if( item->glyph != NO_GLYPH ) {
925 saveBmp = SelectObject(tileDC, GetNHApp()->bmpTiles);
926 ntile = glyph2tile[ item->glyph ];
927 t_x = (ntile % TILES_PER_LINE)*TILE_X;
928 t_y = (ntile / TILES_PER_LINE)*TILE_Y;
930 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
932 nhapply_image_transparent(
933 lpdis->hDC, x, y, TILE_X, TILE_Y,
934 tileDC, t_x, t_y, TILE_X, TILE_Y, TILE_BK_COLOR );
935 SelectObject(tileDC, saveBmp);
941 if( item->has_tab ) {
943 p = strchr(item->str, '\t');
945 SetRect( &drawRect, x, lpdis->rcItem.top, min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
946 lpdis->rcItem.bottom );
949 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
951 NH_A2W(p1, wbuf, BUFSZ),
954 DT_LEFT | DT_VCENTER | DT_SINGLELINE
956 if (p != NULL) *p = '\t';
957 else /* last string so, */ break;
960 p = strchr(p1, '\t');
961 drawRect.left = drawRect.right + TAB_SEPARATION;
963 drawRect.right = min (drawRect.left + data->menu.tab_stop_size[column], lpdis->rcItem.right);
967 SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom);
969 NH_A2W(item->str, wbuf, BUFSZ),
972 DT_LEFT | DT_VCENTER | DT_SINGLELINE
976 /* draw focused item */
977 if( item->has_focus ) {
981 GetClientRect(lpdis->hwndItem, &client_rt);
982 if( NHMENU_IS_SELECTABLE(*item) &&
983 data->menu.items[lpdis->itemID].count>0 &&
984 item->glyph != NO_GLYPH ) {
985 if( data->menu.items[lpdis->itemID].count==-1 ) {
986 _stprintf(wbuf, TEXT("Count: All") );
988 _stprintf(wbuf, TEXT("Count: %d"), data->menu.items[lpdis->itemID].count );
991 SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE));
993 /* calculate text rectangle */
994 SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom );
995 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
996 DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
998 /* erase text rectangle */
999 drawRect.left = max(client_rt.left+1, client_rt.right - (drawRect.right - drawRect.left) - 10);
1000 drawRect.right = client_rt.right-1;
1001 drawRect.top = lpdis->rcItem.top;
1002 drawRect.bottom = lpdis->rcItem.bottom;
1003 bkBrush = CreateSolidBrush( GetBkColor(lpdis->hDC) );
1004 FillRect(lpdis->hDC, &drawRect, bkBrush );
1005 DeleteObject( bkBrush );
1008 DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1009 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
1012 /* draw focus rect */
1013 SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom );
1014 DrawFocusRect(lpdis->hDC, &drawRect);
1017 SetTextColor (lpdis->hDC, OldFg);
1018 SetBkColor (lpdis->hDC, OldBg);
1019 SelectObject(lpdis->hDC, saveFont);
1024 BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch)
1028 int curIndex, topIndex, pageSize;
1029 boolean is_accelerator = FALSE;
1031 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
1034 case MENU_FIRST_PAGE:
1036 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1037 ListView_EnsureVisible(hwndList, i, FALSE);
1040 case MENU_LAST_PAGE:
1041 i = max(0, data->menu.size-1);
1042 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1043 ListView_EnsureVisible(hwndList, i, FALSE);
1046 case MENU_NEXT_PAGE:
1047 topIndex = ListView_GetTopIndex( hwndList );
1048 pageSize = ListView_GetCountPerPage( hwndList );
1049 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1050 /* Focus down one page */
1051 i = min(curIndex+pageSize, data->menu.size-1);
1052 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1053 /* Scrollpos down one page */
1054 i = min(topIndex+(2*pageSize - 1), data->menu.size-1);
1055 ListView_EnsureVisible(hwndList, i, FALSE);
1058 case MENU_PREVIOUS_PAGE:
1059 topIndex = ListView_GetTopIndex( hwndList );
1060 pageSize = ListView_GetCountPerPage( hwndList );
1061 curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1062 /* Focus up one page */
1063 i = max(curIndex-pageSize, 0);
1064 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1065 /* Scrollpos up one page */
1066 i = max(topIndex-pageSize, 0);
1067 ListView_EnsureVisible(hwndList, i, FALSE);
1070 case MENU_SELECT_ALL:
1071 if( data->how == PICK_ANY ) {
1072 reset_menu_count(hwndList, data);
1073 for(i=0; i<data->menu.size; i++ ) {
1074 SelectMenuItem(hwndList, data, i, -1);
1080 case MENU_UNSELECT_ALL:
1081 if( data->how == PICK_ANY ) {
1082 reset_menu_count(hwndList, data);
1083 for(i=0; i<data->menu.size; i++ ) {
1084 SelectMenuItem(hwndList, data, i, 0);
1090 case MENU_INVERT_ALL:
1091 if( data->how == PICK_ANY ) {
1092 reset_menu_count(hwndList, data);
1093 for(i=0; i<data->menu.size; i++ ) {
1098 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1105 case MENU_SELECT_PAGE:
1106 if( data->how == PICK_ANY ) {
1108 reset_menu_count(hwndList, data);
1109 topIndex = ListView_GetTopIndex( hwndList );
1110 pageSize = ListView_GetCountPerPage( hwndList );
1111 from = max(0, topIndex);
1112 to = min(data->menu.size, from+pageSize);
1113 for(i=from; i<to; i++ ) {
1114 SelectMenuItem(hwndList, data, i, -1);
1120 case MENU_UNSELECT_PAGE:
1121 if( data->how == PICK_ANY ) {
1123 reset_menu_count(hwndList, data);
1124 topIndex = ListView_GetTopIndex( hwndList );
1125 pageSize = ListView_GetCountPerPage( hwndList );
1126 from = max(0, topIndex);
1127 to = min(data->menu.size, from+pageSize);
1128 for(i=from; i<to; i++ ) {
1129 SelectMenuItem(hwndList, data, i, 0);
1135 case MENU_INVERT_PAGE:
1136 if( data->how == PICK_ANY ) {
1138 reset_menu_count(hwndList, data);
1139 topIndex = ListView_GetTopIndex( hwndList );
1140 pageSize = ListView_GetCountPerPage( hwndList );
1141 from = max(0, topIndex);
1142 to = min(data->menu.size, from+pageSize);
1143 for(i=from; i<to; i++ ) {
1148 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1156 if( data->how==PICK_ANY || data->how==PICK_ONE ) {
1160 reset_menu_count(hwndList, data);
1161 mswin_getlin("Search for:", buf);
1162 if (!*buf || *buf == '\033') return -2;
1164 for(i=0; i<data->menu.size; i++ ) {
1165 if( NHMENU_IS_SELECTABLE(data->menu.items[i])
1166 && strstr(data->menu.items[i].str, buf) ) {
1167 if (data->how == PICK_ANY) {
1172 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1174 /* save the first item - we will move focus to it */
1175 if( selected_item == -1 ) selected_item = i;
1176 } else if( data->how == PICK_ONE ) {
1189 if( selected_item>0 ) {
1190 ListView_SetItemState(hwndList, selected_item, LVIS_FOCUSED, LVIS_FOCUSED);
1191 ListView_EnsureVisible(hwndList, selected_item, FALSE);
1199 /* ends menu for PICK_ONE/PICK_NONE
1200 select item for PICK_ANY */
1201 if( data->how==PICK_ONE || data->how==PICK_NONE ) {
1205 } else if( data->how==PICK_ANY ) {
1206 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1212 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1220 if( strchr(data->menu.gacc, ch) &&
1221 !(ch=='0' && data->menu.counting) ) {
1222 /* matched a group accelerator */
1223 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1224 reset_menu_count(hwndList, data);
1225 for(i=0; i<data->menu.size; i++ ) {
1226 if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
1227 data->menu.items[i].group_accel == ch ) {
1228 if( data->how == PICK_ANY ) {
1233 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1235 } else if( data->how == PICK_ONE ) {
1257 i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1259 count = data->menu.items[i].count;
1260 if( count==-1 ) count=0;
1262 count += (int)(ch - '0');
1263 if (count != 0) /* ignore leading zeros */ {
1264 data->menu.counting = TRUE;
1265 data->menu.items[i].count = min(100000, count);
1266 ListView_RedrawItems( hwndList, i, i ); /* update count mark */
1272 is_accelerator = FALSE;
1273 for(i=0; i<data->menu.size; i++) {
1274 if( data->menu.items[i].accelerator == ch ) {
1275 is_accelerator = TRUE;
1280 if( (ch>='a' && ch<='z') ||
1281 (ch>='A' && ch<='Z') || is_accelerator) {
1282 if (data->how == PICK_ANY || data->how == PICK_ONE) {
1283 for(i=0; i<data->menu.size; i++ ) {
1284 if( data->menu.items[i].accelerator == ch ) {
1285 if( data->how == PICK_ANY ) {
1290 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1292 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1293 ListView_EnsureVisible(hwndList, i, FALSE);
1295 } else if( data->how == PICK_ONE ) {
1313 reset_menu_count(hwndList, data);
1317 void mswin_menu_window_size (HWND hWnd, LPSIZE sz)
1328 GetClientRect(hWnd, &rt);
1329 sz->cx = rt.right - rt.left;
1330 sz->cy = rt.bottom - rt.top;
1332 GetWindowRect(hWnd, &wrt);
1333 extra_cx = (wrt.right-wrt.left) - sz->cx;
1335 data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
1337 control = GetMenuControl(hWnd);
1338 hdc = GetDC(control);
1340 if( data->type==MENU_TYPE_MENU ) {
1341 /* Calculate the width of the list box. */
1342 saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE));
1343 GetTextMetrics(hdc, &tm);
1344 for(i=0; i<data->menu.size; i++ ) {
1345 LONG menuitemwidth = 0;
1349 p1 = data->menu.items[i].str;
1350 p = strchr(data->menu.items[i].str, '\t');
1355 SetRect ( &tabRect, 0, 0, 1, 1 );
1356 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
1358 NH_A2W(p1, wbuf, BUFSZ),
1361 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE
1363 /* it probably isn't necessary to recompute the tab width now, but do so
1364 * just in case, honoring the previously computed value
1366 menuitemwidth += max(data->menu.tab_stop_size[column],
1367 tabRect.right - tabRect.left);
1368 if (p != NULL) *p = '\t';
1369 else /* last string so, */ break;
1370 /* add the separation only when not the last item */
1371 /* in the last item, we break out of the loop, in the statement just above */
1372 menuitemwidth += TAB_SEPARATION;
1375 p = strchr(p1, '\t');
1378 sz->cx = max(sz->cx,
1379 (LONG)(2*TILE_X + menuitemwidth + tm.tmAveCharWidth*12 + tm.tmOverhang));
1381 SelectObject(hdc, saveFont);
1383 /* do not change size for text output - the text will be formatted to
1388 ReleaseDC(control, hdc);
1392 void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1396 if( item<0 || item>=data->menu.size ) return;
1398 if( data->how==PICK_ONE && count!=0 ) {
1399 for(i=0; i<data->menu.size; i++)
1400 if( item!=i && data->menu.items[i].count!=0 ) {
1401 data->menu.items[i].count = 0;
1402 ListView_RedrawItems( hwndList, i, i );
1406 data->menu.items[item].count = count;
1407 ListView_RedrawItems( hwndList, item, item );
1408 reset_menu_count(hwndList, data);
1411 void reset_menu_count(HWND hwndList, PNHMenuWindow data)
1414 data->menu.counting = FALSE;
1415 if( IsWindow(hwndList) ) {
1416 i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
1417 if( i>=0 ) ListView_RedrawItems( hwndList, i, i );
1421 /* List window Proc */
1422 LRESULT CALLBACK NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1424 BOOL bUpdateFocusItem = FALSE;
1428 /* filter keyboard input for the control */
1429 #if !defined(WIN_CE_SMARTPHONE)
1434 if( PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) ) {
1435 if( onListChar(GetParent(hWnd), hWnd, (char)msg.wParam)==-2 ) {
1440 if( wParam==VK_LEFT || wParam==VK_RIGHT )
1441 bUpdateFocusItem = TRUE;
1444 #else /* defined(WIN_CE_SMARTPHONE) */
1446 if( wParam==VK_TACTION ) {
1447 if( onListChar(GetParent(hWnd), hWnd, ' ')==-2 ) {
1450 } else if( NHSPhoneTranslateKbdMessage(wParam, lParam, TRUE) ) {
1452 BOOL processed = FALSE;
1453 if( mswin_have_input() ) {
1454 evt = mswin_input_pop();
1455 if( evt->type==NHEVENT_CHAR &&
1456 onListChar(GetParent(hWnd), hWnd, evt->kbd.ch)==-2 ) {
1460 /* eat the rest of the events */
1461 if( mswin_have_input() ) mswin_input_pop();
1463 if( processed ) return 0;
1466 if( wParam==VK_LEFT || wParam==VK_RIGHT )
1467 bUpdateFocusItem = TRUE;
1471 /* translate SmartPhone keyboard message */
1472 if( NHSPhoneTranslateKbdMessage(wParam, lParam, FALSE) )
1476 /* tell Windows not to process default button on VK_RETURN */
1478 return DLGC_DEFPUSHBUTTON | DLGC_WANTALLKEYS |
1479 (wndProcListViewOrig?
1480 CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam)
1486 bUpdateFocusItem = TRUE;
1491 if( bUpdateFocusItem ) {
1495 /* invalidate the focus rectangle */
1496 i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);
1498 ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1499 InvalidateRect(hWnd, &rt, TRUE);
1503 if( wndProcListViewOrig )
1504 return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam);
1509 /* Text control window proc - implements close on space */
1510 LRESULT CALLBACK NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1517 /* close on space */
1518 PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0);
1523 PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL);
1528 PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL);
1533 PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, 0), (LPARAM)NULL);
1538 PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, 0), (LPARAM)NULL);
1541 break; /* case WM_KEYUP: */
1544 if( editControlWndProc )
1545 return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam);
1549 /*----------------------------------------------------------------------------*/
1550 char* parse_menu_str(char* dest, const char* src, size_t size)
1553 if( !dest || size==0 ) return NULL;
1555 strncpy(dest, src, size);
1556 dest[size-1] = '\x0';
1558 /* replace "[ ]*\[" with "\t\[" */
1559 p1 = p2 = strstr(dest, " [");
1561 while( p1!=dest && *p1==' ') p1--;
1562 p1++; /* backup to space */
1564 memmove(p1, p2, strlen(p2));
1565 p1[strlen(p2)] = '\x0';