OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / win / win32 / mhmenu.c
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. */
4
5 #include "winMS.h"
6 #include <assert.h>
7 #include "resource.h"
8 #include "mhmenu.h"
9 #include "mhmain.h"
10 #include "mhmsg.h"
11 #include "mhfont.h"
12 #include "mhdlg.h"
13
14 #define MENU_MARGIN             0
15 #define NHMENU_STR_SIZE         BUFSZ
16 #define MIN_TABSTOP_SIZE        0
17 #define NUMTABS                 15
18 #define TAB_SEPARATION          10 /* pixels between each tab stop */
19
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
24
25 typedef struct mswin_menu_item {
26         int                     glyph;
27         ANY_P                   identifier;
28         CHAR_P          accelerator;
29         CHAR_P          group_accel;
30         int                     attr;
31         char                    str[NHMENU_STR_SIZE];
32         BOOLEAN_P               presel;
33         int                     count;
34         BOOL                    has_focus;
35 } NHMenuItem, *PNHMenuItem;
36
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 */
40
41         union {
42                 struct menu_list {
43                         int                      size;                  /* number of items in items[] */
44                         int                      allocated;                     /* number of allocated slots in items[] */
45                         PNHMenuItem              items;                 /* menu items */
46                         char                     gacc[QBUFSZ];          /* group accelerators */
47                         BOOL                     counting;                      /* counting flag */
48                         char                     prompt[QBUFSZ];                /* menu prompt */
49                         int                      tab_stop_size[NUMTABS];/* tabstops to align option values */
50                 } menu;
51
52                 struct menu_text {
53                         TCHAR*                  text;
54                 } text;
55         };
56         int result;
57         int done;
58
59         HBITMAP bmpChecked;
60         HBITMAP bmpCheckedCount;
61         HBITMAP bmpNotChecked;
62 } NHMenuWindow, *PNHMenuWindow;
63
64 extern short glyph2tile[];
65
66 static WNDPROC wndProcListViewOrig = NULL;
67 static WNDPROC editControlWndProc = NULL;
68
69 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL)
70 #define NHMENU_IS_SELECTED(item) ((item).count!=0)
71
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);
85
86 /*-----------------------------------------------------------------------------*/
87 HWND mswin_init_menu_window (int type) {
88         HWND ret;
89
90         ret = CreateDialog(
91                         GetNHApp()->hApp,
92                         MAKEINTRESOURCE(IDD_MENU),
93                         GetNHApp()->hMainWnd,
94                         MenuWndProc
95         );
96         if( !ret ) {
97                 panic("Cannot create menu window");
98         }
99         
100         SetMenuType(ret, type);
101         return ret;
102 }
103 /*-----------------------------------------------------------------------------*/
104 int mswin_menu_window_select_menu (HWND hWnd, int how, MENU_ITEM_P ** _selected)
105 {
106         PNHMenuWindow data;
107         int ret_val;
108     MENU_ITEM_P *selected = NULL;
109         int i;
110         char* ap;
111
112         assert( _selected!=NULL );
113         *_selected = NULL;
114         ret_val = -1;
115
116         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
117
118         /* set menu type */
119         SetMenuListType(hWnd, how);
120
121         /* Ok, now give items a unique accelerators */
122         if( data->type == MENU_TYPE_MENU ) {
123                 char next_char = 'a';
124
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;
134                                 } else {
135                                         if( next_char > 'z' ) next_char = 'A';
136                                         else if ( next_char > 'Z' ) break;
137
138                                         data->menu.items[i].accelerator = next_char;
139                                 }
140
141                                 next_char ++;
142                         }
143                 }
144
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;
151                                         *ap = '\x0';
152                                 }
153                         }
154                 }
155
156                 reset_menu_count(NULL, data);
157         }
158
159         mswin_popup_display(hWnd, &data->done);
160
161         /* get the result */
162         if( data->result != -1 ) {
163                 if(how==PICK_NONE) {
164                         if(data->result>=0) ret_val=0;
165                         else                            ret_val=-1;
166                 } else if(how==PICK_ONE || how==PICK_ANY) {
167                         /* count selected items */
168                         ret_val = 0;
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]) ) {
172                                         ret_val++;
173                                 }
174                         }
175                         if( ret_val > 0 ) {
176                                 int sel_ind;
177
178                                 selected = (MENU_ITEM_P*)malloc(ret_val*sizeof(MENU_ITEM_P));
179                                 if( !selected ) panic("out of memory");
180
181                                 sel_ind = 0;
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;
187                                                 sel_ind++;
188                                         }
189                                 }
190                                 ret_val = sel_ind;
191                                 *_selected = selected;
192                         }
193                 }
194         }
195
196         mswin_popup_destroy(hWnd);
197
198         return ret_val;
199 }
200 /*-----------------------------------------------------------------------------*/   
201 BOOL CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
202 {
203         PNHMenuWindow data;
204         HWND control;
205         HDC  hdc;
206     TCHAR title[MAX_LOADSTRING];
207
208
209         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
210         switch (message) 
211         {
212         case WM_INITDIALOG:
213                 data = (PNHMenuWindow)malloc(sizeof(NHMenuWindow));
214                 ZeroMemory(data, sizeof(NHMenuWindow));
215                 data->type = MENU_TYPE_TEXT;
216                 data->how = PICK_NONE;
217                 data->result = 0;
218                 data->done = 0;
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);
223
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);
229
230                 /* subclass edit control */
231                 editControlWndProc = (WNDPROC)GetWindowLong(control, GWL_WNDPROC);
232                 SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuTextWndProc);
233
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);
238         break;
239
240         case WM_MSNH_COMMAND:
241                 onMSNHCommand(hWnd, wParam, lParam);
242         break;
243
244         case WM_SIZE:
245                 LayoutMenu(hWnd);
246         return FALSE;
247
248         case WM_CLOSE:
249             if (program_state.gameover) {
250                 data->result = -1;
251                 data->done = 1;
252                 program_state.stopprint++;
253                 return TRUE;
254             } else
255                 return FALSE;
256
257         case WM_COMMAND: 
258         {
259                 switch (LOWORD(wParam)) 
260         { 
261                 case IDCANCEL:
262                         if( data->type == MENU_TYPE_MENU && 
263                             (data->how==PICK_ONE || data->how==PICK_ANY) &&
264                             data->menu.counting) {
265                                 HWND list;
266                                 int i;
267
268                                 /* reset counter if counting is in progress */
269                                 list = GetMenuControl(hWnd);
270                                 i = ListView_GetNextItem(list, -1,      LVNI_FOCUSED);
271                                 if( i>=0 ) {
272                                         SelectMenuItem(list, data, i,  0);
273                                 }
274                                 return TRUE;
275                         } else {
276                                 data->result = -1;
277                                 data->done = 1;
278                         }
279                 return TRUE;
280
281                 case IDOK:
282                         data->done = 1;
283                         data->result = 0;
284                 return TRUE;
285
286         case IDC_MENU_TEXT:
287           switch (HIWORD(wParam))
288           {
289             case EN_SETFOCUS:
290               HideCaret((HWND)lParam);
291               return TRUE;
292           }
293                 }
294         } break;
295
296         case WM_NOTIFY:
297         {
298                 LPNMHDR lpnmhdr = (LPNMHDR)lParam;
299                 switch (LOWORD(wParam)) {
300                 case IDC_MENU_LIST:
301                 {
302                         if( !data || data->type!=MENU_TYPE_MENU ) break;
303
304                         switch(lpnmhdr->code) {
305                         case LVN_ITEMACTIVATE: 
306                         {
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]) ) {
312                                                 SelectMenuItem(
313                                                         lpnmlv->hdr.hwndFrom, 
314                                                         data, 
315                                                         lpnmlv->iItem, 
316                                                         -1
317                                                 );
318                                                 data->done = 1;
319                                                 data->result = 0;
320                                                 return TRUE;
321                                         }
322                                 }
323                         } break;
324
325                         case NM_CLICK: {
326                                 LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW) lParam;
327                                 if( lpnmitem->iItem==-1 ) return 0;
328                                 if( data->how==PICK_ANY ) {
329                                         SelectMenuItem(
330                                                 lpnmitem->hdr.hwndFrom, 
331                                                 data, 
332                                                 lpnmitem->iItem, 
333                                                 NHMENU_IS_SELECTED(data->menu.items[lpnmitem->iItem])? 0 : -1
334                                         );
335                                 }
336                         } break;
337
338                         case LVN_ITEMCHANGED: 
339                         {
340                                 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
341                                 if( lpnmlv->iItem==-1 ) return 0;
342                                 if( !(lpnmlv->uChanged & LVIF_STATE) ) return 0;
343
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);
347                                 }
348
349                                 /* update count for single-selection menu (follow the listview selection) */
350                                 if( data->how==PICK_ONE ) {
351                                         if( lpnmlv->uNewState & LVIS_SELECTED ) {
352                                                 SelectMenuItem(
353                                                         lpnmlv->hdr.hwndFrom, 
354                                                         data, 
355                                                         lpnmlv->iItem, 
356                                                         -1
357                                                 );
358                                         }
359                                 }
360
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);
365                                 }
366                         } break;
367
368                         case NM_KILLFOCUS:
369                                 reset_menu_count(lpnmhdr->hwndFrom, data);
370                         break;
371
372                         }
373                 } break;
374                 }
375         } break;
376
377         case WM_SETFOCUS:
378                 if( hWnd!=GetNHApp()->hPopupWnd ) {
379                         SetFocus(GetNHApp()->hPopupWnd );
380                 }
381         break;
382         
383     case WM_MEASUREITEM: 
384                 if( wParam==IDC_MENU_LIST )
385                         return onMeasureItem(hWnd, wParam, lParam);
386                 else
387                         return FALSE;
388
389     case WM_DRAWITEM:
390                 if( wParam==IDC_MENU_LIST )
391                         return onDrawItem(hWnd, wParam, lParam);
392                 else
393                         return FALSE;
394
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) ) {
399                         SetBkColor(hdcEdit, 
400                                 text_bg_brush ? text_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_TEXT)
401                                 );
402                         SetTextColor(hdcEdit, 
403                                 text_fg_brush ? text_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_TEXT) 
404                                 ); 
405                         return (BOOL)(text_bg_brush 
406                                         ? text_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT));
407                 }
408         } return FALSE;
409
410         case WM_DESTROY:
411                 if( data ) {
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);
417                         }
418                         free(data);
419                         SetWindowLong(hWnd, GWL_USERDATA, (LONG)0);
420                 }
421                 return TRUE;
422         }
423         return FALSE;
424 }
425 /*-----------------------------------------------------------------------------*/
426 void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
427 {
428         PNHMenuWindow data;
429
430         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
431         switch( wParam ) {
432         case MSNH_MSG_PUTSTR: 
433         {
434                 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam;
435                 HWND   text_view;
436                 TCHAR   wbuf[BUFSZ];
437                 size_t text_size;
438
439                 if( data->type!=MENU_TYPE_TEXT )
440                         SetMenuType(hWnd, MENU_TYPE_TEXT);
441
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]));
446                 } else {
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]));
449                 }
450                 if( !data->text.text ) break;
451                 
452                 _tcscat(data->text.text, NH_A2W(msg_data->text, wbuf, BUFSZ)); 
453                 _tcscat(data->text.text, TEXT("\r\n"));
454                 
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);
458         } break;
459
460         case MSNH_MSG_STARTMENU:
461         {
462                 int i;
463                 if( data->type!=MENU_TYPE_MENU )
464                         SetMenuType(hWnd, MENU_TYPE_MENU);
465
466                 if( data->menu.items ) free(data->menu.items);
467                 data->how = PICK_NONE;
468                 data->menu.items = NULL;
469                 data->menu.size = 0;
470                 data->menu.allocated = 0;
471                 data->done = 0;
472                 data->result = 0;
473                 for (i = 0; i < NUMTABS; ++i)
474                         data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
475         } break;
476
477         case MSNH_MSG_ADDMENU:
478         {
479                 PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam;
480                 char *p, *p1;
481                 int new_item;
482                 HDC hDC;
483                 int column;
484                 HFONT saveFont;
485                 
486                 if( data->type!=MENU_TYPE_MENU ) break;
487                 if( strlen(msg_data->str)==0 ) break;
488
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));
492                 }
493
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;
503
504                 /* calculate tabstop size */
505                 hDC = GetDC(hWnd);
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');
509                 column = 0;
510                 for (;;) {
511                         TCHAR wbuf[BUFSZ];
512                         RECT drawRect;
513                         SetRect ( &drawRect, 0, 0, 1, 1 );
514                         if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
515                         DrawText(hDC,
516                                 NH_A2W(p1, wbuf, BUFSZ),
517                                 strlen(p1),
518                                 &drawRect,
519                                 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE
520                         );
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;
525
526                         ++column;
527                         p1 = p + 1;
528                         p = strchr(p1, '\t');
529                 }
530                 SelectObject(hDC, saveFont);
531                 ReleaseDC(hWnd, hDC);
532
533                 /* increment size */
534                 data->menu.size++;
535         } break;
536
537         case MSNH_MSG_ENDMENU:
538         {
539                 PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu)lParam;
540                 if( msg_data->text ) {
541                         strncpy( data->menu.prompt, msg_data->text, sizeof(data->menu.prompt)-1 );
542                 } else {
543                         ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
544                 }
545         } break;
546
547         }
548 }
549 /*-----------------------------------------------------------------------------*/
550 void LayoutMenu(HWND hWnd) 
551 {
552         PNHMenuWindow data;
553         HWND  menu_ok;
554         HWND  menu_cancel;
555         RECT  clrt, rt;
556         POINT pt_elem, pt_ok, pt_cancel;
557         SIZE  sz_elem, sz_ok, sz_cancel;
558
559         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
560         menu_ok = GetDlgItem(hWnd, IDOK);
561         menu_cancel = GetDlgItem(hWnd, IDCANCEL);
562
563         /* get window coordinates */
564         GetClientRect(hWnd, &clrt );
565         
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;
572
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;
578
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;
583
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 );
587 }
588 /*-----------------------------------------------------------------------------*/
589 void SetMenuType(HWND hWnd, int type)
590 {
591         PNHMenuWindow data;
592         HWND list, text;
593
594         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
595
596         data->type = type;
597         
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);
605                 SetFocus(text);
606         } else {
607                 ShowWindow(text, SW_HIDE);
608                 EnableWindow(text, FALSE);
609                 EnableWindow(list, TRUE);
610                 ShowWindow(list, SW_SHOW);
611                 SetFocus(list);
612         }
613         LayoutMenu(hWnd);
614 }
615 /*-----------------------------------------------------------------------------*/
616 void SetMenuListType(HWND hWnd, int how)
617 {
618         PNHMenuWindow data;
619         RECT rt;
620         DWORD dwStyles;
621         char buf[BUFSZ];
622         TCHAR wbuf[BUFSZ];
623         int nItem;
624         int i;
625         HWND control;
626         LVCOLUMN lvcol;
627         LRESULT fnt;
628
629         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
630         if( data->type != MENU_TYPE_MENU ) return;
631
632         data->how = how;
633
634         switch(how) {
635         case PICK_NONE: 
636                 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
637                         | WS_VSCROLL | WS_HSCROLL | LVS_REPORT  
638                         | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
639                 break;
640         case PICK_ONE: 
641                 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
642                         | WS_VSCROLL | WS_HSCROLL | LVS_REPORT  
643                         | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
644                 break;
645         case PICK_ANY: 
646                 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
647                         | WS_VSCROLL | WS_HSCROLL | LVS_REPORT  
648                         | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
649                 break;
650         default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
651         };
652
653         if( strlen(data->menu.prompt)==0 ) {
654                 dwStyles |= LVS_NOCOLUMNHEADER ;
655         }
656
657         GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
658         DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
659         control = CreateWindow(WC_LISTVIEW, NULL, 
660                 dwStyles,
661                 rt.left,
662                 rt.top,
663                 rt.right - rt.left,
664                 rt.bottom - rt.top,
665                 hWnd,
666                 (HMENU)IDC_MENU_LIST,
667                 GetNHApp()->hApp,
668                 NULL );
669         if( !control ) panic( "cannot create menu control" );
670         
671         /* install the hook for the control window procedure */
672         wndProcListViewOrig = (WNDPROC)GetWindowLong(control, GWL_WNDPROC);
673         SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuListWndProc);
674
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));
682
683         /* set control font */
684         fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0);
685         SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0);
686
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);
693
694         /* add items to the list view */
695         for(i=0; i<data->menu.size; i++ ) {
696                 LVITEM lvitem;
697                 ZeroMemory( &lvitem, sizeof(lvitem) );
698                 sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str );
699
700                 lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
701                 lvitem.iItem = i;
702                 lvitem.iSubItem = 0;
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");
709                 }
710         }
711         SetFocus(control);
712 }
713 /*-----------------------------------------------------------------------------*/
714 HWND GetMenuControl(HWND hWnd)
715 {
716         PNHMenuWindow data;
717
718         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
719
720         if(data->type==MENU_TYPE_TEXT) {
721                 return GetDlgItem(hWnd, IDC_MENU_TEXT);
722         } else {
723                 return GetDlgItem(hWnd, IDC_MENU_LIST);
724         }
725 }
726 /*-----------------------------------------------------------------------------*/
727 BOOL onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
728 {
729     LPMEASUREITEMSTRUCT lpmis; 
730     TEXTMETRIC tm;
731         HGDIOBJ saveFont;
732         HDC hdc;
733         PNHMenuWindow data;
734         RECT list_rect;
735
736     lpmis = (LPMEASUREITEMSTRUCT) lParam; 
737         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
738         GetClientRect(GetMenuControl(hWnd), &list_rect);
739
740         hdc = GetDC(GetMenuControl(hWnd));
741         saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
742         GetTextMetrics(hdc, &tm);
743
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;
747
748         SelectObject(hdc, saveFont);
749         ReleaseDC(GetMenuControl(hWnd), hdc);
750         return TRUE;
751 }
752 /*-----------------------------------------------------------------------------*/
753 BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
754 {
755         LPDRAWITEMSTRUCT lpdis;
756         PNHMenuItem item;
757         PNHMenuWindow data;
758         TEXTMETRIC tm;
759         HGDIOBJ saveFont;
760         HDC tileDC;
761         short ntile;
762         int t_x, t_y;
763         int x, y;
764         TCHAR wbuf[BUFSZ];
765         RECT drawRect;
766         COLORREF OldBg, OldFg, NewBg;
767         char *p, *p1;
768         int column;
769
770         lpdis = (LPDRAWITEMSTRUCT) lParam; 
771
772     /* If there are no list box items, skip this message. */
773     if (lpdis->itemID == -1) return FALSE;
774
775         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
776
777     item = &data->menu.items[lpdis->itemID];
778
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)); 
785
786     GetTextMetrics(lpdis->hDC, &tm);
787
788         x = lpdis->rcItem.left + 1;
789
790     /* print check mark and letter */
791         if( NHMENU_IS_SELECTABLE(*item) ) {
792  char buf[2];
793  if (data->how != PICK_NONE) {
794                 HGDIOBJ saveBrush;
795                 HBRUSH  hbrCheckMark;
796
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;
801                 }
802
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);
809
810  }
811                 x += TILE_X + 5;
812                 if(item->accelerator!=0) {
813                         buf[0] = item->accelerator;
814                         buf[1] = '\x0';
815
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);
818                 }
819                 x += tm.tmAveCharWidth + tm.tmOverhang + 5;
820         } else {
821                 x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10;
822         }
823
824         /* print glyph if present */
825         if( item->glyph != NO_GLYPH ) {
826                 HGDIOBJ saveBmp;
827
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;
832
833                 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; 
834
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);
839         }
840
841         x += TILE_X + 5;
842
843         /* draw item text */
844
845         p1 = item->str;
846         p = strchr(item->str, '\t');
847         column = 0;
848         SetRect( &drawRect, x, lpdis->rcItem.top, min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
849             lpdis->rcItem.bottom );
850         for (;;) {
851                 TCHAR wbuf[BUFSZ];
852                 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
853                 DrawText(lpdis->hDC,
854                         NH_A2W(p1, wbuf, BUFSZ),
855                         strlen(p1),
856                         &drawRect,
857                         DT_LEFT | DT_VCENTER | DT_SINGLELINE
858                 );
859                 if (p != NULL) *p = '\t';
860                 else /* last string so, */ break;
861
862                 p1 = p + 1;
863                 p = strchr(p1, '\t');
864                 drawRect.left = drawRect.right + TAB_SEPARATION;
865                 ++column;
866                 drawRect.right = min (drawRect.left + data->menu.tab_stop_size[column], lpdis->rcItem.right);
867         }
868
869         /* draw focused item */
870         if( item->has_focus 
871         || (NHMENU_IS_SELECTABLE(*item) && 
872                         data->menu.items[lpdis->itemID].count!=-1)) {
873                 RECT client_rt;
874
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") );
881                         } else {
882                                 _stprintf(wbuf, TEXT("Count: %d"), data->menu.items[lpdis->itemID].count );
883                         }
884
885                         SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE));
886
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 );
891                         
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));
899
900                         /* draw text */
901                         DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, 
902                                          DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
903                 }
904     }
905     if (item->has_focus) {
906                 /* draw focus rect */
907                 RECT client_rt;
908
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);
912         }
913
914         SetTextColor (lpdis->hDC, OldFg);
915         SetBkColor (lpdis->hDC, OldBg);
916         SelectObject(lpdis->hDC, saveFont);
917         DeleteDC(tileDC);
918         return TRUE;
919 }
920 /*-----------------------------------------------------------------------------*/
921 BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch)
922 {
923         int i = 0;
924         PNHMenuWindow data;
925         int curIndex, topIndex, pageSize;
926         boolean is_accelerator = FALSE;
927
928         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
929
930         switch( ch ) {
931         case MENU_FIRST_PAGE:
932                 i = 0;
933                 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
934                 ListView_EnsureVisible(hwndList, i, FALSE);
935         return -2;
936
937         case MENU_LAST_PAGE:
938                 i = max(0, data->menu.size-1);
939                 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
940                 ListView_EnsureVisible(hwndList, i, FALSE);
941         return -2;
942
943         case MENU_NEXT_PAGE:
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);
953         return -2;
954
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);
965         break;
966
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);
972                         }
973                         return -2;
974                 }
975         break;
976
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);
982                         }
983                         return -2;
984                 }
985         break;
986
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++ ) {
991                                 SelectMenuItem(
992                                         hwndList, 
993                                         data, 
994                                         i, 
995                                         NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
996                                 );
997                         }
998                         return -2;
999                 }
1000         break;
1001
1002         case MENU_SELECT_PAGE:
1003                 if( data->how == PICK_ANY ) {
1004                         int from, to;
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);
1012                         }
1013                         return -2;
1014                 }
1015         break;
1016
1017         case MENU_UNSELECT_PAGE:
1018                 if( data->how == PICK_ANY ) {
1019                         int from, to;
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);
1027                         }
1028                         return -2;
1029                 }
1030         break;
1031
1032         case MENU_INVERT_PAGE:
1033                 if( data->how == PICK_ANY ) {
1034                         int from, to;
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++ ) {
1041                                 SelectMenuItem(
1042                                         hwndList, 
1043                                         data, 
1044                                         i, 
1045                                         NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1046                                 );
1047                         }
1048                         return -2;
1049                 }
1050         break;
1051
1052         case MENU_SEARCH:
1053             if( data->how==PICK_ANY || data->how==PICK_ONE ) {
1054                         char buf[BUFSZ];
1055                         
1056                         reset_menu_count(hwndList, data);
1057                         if( mswin_getlin_window("Search for:", buf, BUFSZ)==IDCANCEL ) {
1058                                 strcpy(buf, "\033");
1059                         }
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) {
1066                                                 SelectMenuItem(
1067                                                         hwndList, 
1068                                                         data, 
1069                                                         i, 
1070                                                         NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1071                                                 );
1072                                         } else if( data->how == PICK_ONE ) {
1073                                                 SelectMenuItem(
1074                                                         hwndList, 
1075                                                         data, 
1076                                                         i, 
1077                                                         -1
1078                                                 );
1079                                                 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1080                                                 ListView_EnsureVisible(hwndList, i, FALSE);
1081                                                 break;
1082                                         }
1083                                 }
1084                         } 
1085                 } else {
1086                         mswin_nhbell();
1087             }
1088         return -2;
1089
1090         case ' ':
1091     {
1092         if (GetNHApp()->regNetHackMode) {
1093             /* NetHack mode: Scroll down one page,
1094                ends menu when on last page. */
1095             SCROLLINFO si;
1096
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. */
1102                 data->done = 1;
1103                             data->result = 0;
1104                 return -2;
1105             }
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);
1116
1117                 return -2;
1118         } else {
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 ) {
1122                             data->done = 1;
1123                             data->result = 0;
1124                             return -2;
1125                     } else if( data->how==PICK_ANY ) {
1126                             i = ListView_GetNextItem(hwndList, -1,      LVNI_FOCUSED);
1127                             if( i>=0 ) {
1128                                     SelectMenuItem(
1129                                             hwndList, 
1130                                             data, 
1131                                             i, 
1132                                             NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1133                                     );
1134                             }
1135                     }
1136         }
1137     }
1138         break;
1139
1140         default:
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 ) {
1150                                                         SelectMenuItem(
1151                                                                 hwndList, 
1152                                                                 data, 
1153                                                                 i, 
1154                                                                 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1155                                                         );
1156                                                 } else if( data->how == PICK_ONE ) {
1157                                                         SelectMenuItem(
1158                                                                 hwndList, 
1159                                                                 data, 
1160                                                                 i, 
1161                                                                 -1
1162                                                         );
1163                                                         data->result = 0;
1164                                                         data->done = 1;
1165                                                         return -2;
1166                                                 }
1167                                         }
1168                                 }
1169                                 return -2;
1170                         } else {
1171                                 mswin_nhbell();
1172                                 return -2;
1173                         }
1174                 }
1175
1176                 if (isdigit(ch)) {
1177                         int count;
1178                         i = ListView_GetNextItem(hwndList, -1,  LVNI_FOCUSED);
1179                         if( i>=0 ) {
1180                                 count = data->menu.items[i].count;
1181                                 if( count==-1 ) count=0;
1182                                 count *= 10L;
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 */
1188                                 }
1189                         }
1190                         return -2;
1191                 }
1192
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;
1197                                 break;
1198                         }
1199                 }
1200
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 ) {
1207                                                         SelectMenuItem(
1208                                                                 hwndList, 
1209                                                                 data, 
1210                                                                 i, 
1211                                                                 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1212                                                         );
1213                                                         ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1214                                                         ListView_EnsureVisible(hwndList, i, FALSE);
1215                                                         return -2;
1216                                                 } else if( data->how == PICK_ONE ) {
1217                                                         SelectMenuItem(
1218                                                                 hwndList, 
1219                                                                 data, 
1220                                                                 i, 
1221                                                                 -1
1222                                                         );
1223                                                         data->result = 0;
1224                                                         data->done = 1;
1225                                                         return -2;
1226                                                 }
1227                                         }
1228                                 }
1229                         }
1230                 }
1231         break;
1232         }
1233
1234         reset_menu_count(hwndList, data);
1235         return -1;
1236 }
1237 /*-----------------------------------------------------------------------------*/
1238 void mswin_menu_window_size (HWND hWnd, LPSIZE sz)
1239 {
1240     TEXTMETRIC tm;
1241         HWND control;
1242         HGDIOBJ saveFont;
1243         HDC hdc;
1244         PNHMenuWindow data;
1245         int i;
1246         RECT rt, wrt;
1247         int extra_cx;
1248
1249         GetClientRect(hWnd, &rt);
1250         sz->cx = rt.right - rt.left;
1251         sz->cy = rt.bottom - rt.top;
1252
1253         GetWindowRect(hWnd, &wrt);
1254         extra_cx = (wrt.right-wrt.left) - sz->cx;
1255
1256         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
1257         if(data) {
1258                 control = GetMenuControl(hWnd);
1259                 hdc = GetDC(control);
1260
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;
1267                                 int column;
1268                                 char *p, *p1;
1269
1270                                 p1 = data->menu.items[i].str;
1271                                 p = strchr(data->menu.items[i].str, '\t');
1272                                 column = 0;
1273                                 for (;;) {
1274                                         TCHAR wbuf[BUFSZ];
1275                                         RECT tabRect;
1276                                         SetRect ( &tabRect, 0, 0, 1, 1 );
1277                                         if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
1278                                         DrawText(hdc,
1279                                                 NH_A2W(p1, wbuf, BUFSZ),
1280                                                 strlen(p1),
1281                                                 &tabRect,
1282                                                 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE
1283                                         );
1284                                         /* it probably isn't necessary to recompute the tab width now, but do so
1285                                          * just in case, honoring the previously computed value
1286                                          */
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;
1294                                         ++column;
1295                                         p1 = p + 1;
1296                                         p = strchr(p1, '\t');
1297                                 }
1298
1299                                 sz->cx = max(sz->cx, 
1300                                         (LONG)(2*TILE_X + menuitemwidth + tm.tmAveCharWidth*12 + tm.tmOverhang));
1301                         }
1302                         SelectObject(hdc, saveFont);
1303                 } else {
1304                         /* Calculate the width of the text box. */
1305                         RECT text_rt;
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);
1312                 }
1313                 sz->cx += extra_cx;
1314
1315                 ReleaseDC(control, hdc);
1316         }
1317 }
1318 /*-----------------------------------------------------------------------------*/
1319 void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1320 {
1321         int i;
1322
1323         if( item<0 || item>=data->menu.size ) return;
1324
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 );
1330                         };
1331         }
1332
1333         data->menu.items[item].count = count;
1334         ListView_RedrawItems( hwndList, item, item );
1335         reset_menu_count(hwndList, data);
1336 }
1337 /*-----------------------------------------------------------------------------*/
1338 void reset_menu_count(HWND hwndList, PNHMenuWindow data) 
1339 {
1340         int i; 
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 ); 
1345         }
1346 }
1347 /*-----------------------------------------------------------------------------*/
1348 /* List window Proc */
1349 LRESULT CALLBACK NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1350 {
1351         BOOL bUpdateFocusItem;
1352
1353         bUpdateFocusItem = FALSE;
1354
1355         switch(message) {
1356
1357         /* filter keyboard input for the control */
1358         case WM_KEYDOWN:
1359         case WM_KEYUP: {
1360                 MSG msg;
1361                 BOOL processed;
1362
1363                 processed = FALSE;
1364                 if( PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) ) {
1365                         if( onListChar(GetParent(hWnd), hWnd, (char)msg.wParam)==-2 ) {
1366                                 processed = TRUE;
1367                         }
1368                 }
1369                 if( processed ) return 0;
1370
1371                 if( wParam==VK_LEFT || wParam==VK_RIGHT )
1372                         bUpdateFocusItem = TRUE;
1373         } break;
1374
1375         case WM_SIZE:
1376         case WM_HSCROLL:
1377                 bUpdateFocusItem = TRUE;
1378         break;
1379
1380         }
1381
1382         if(     bUpdateFocusItem ) {
1383                 int i;
1384                 RECT rt;
1385
1386                 /* invalidate the focus rectangle */
1387                 i = ListView_GetNextItem(hWnd, -1,      LVNI_FOCUSED);
1388                 if( i!=-1 ) {
1389                         ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1390                         InvalidateRect(hWnd, &rt, TRUE);
1391                 }
1392         }
1393
1394         if( wndProcListViewOrig ) 
1395                 return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam);
1396         else 
1397                 return 0;
1398 }
1399 /*-----------------------------------------------------------------------------*/
1400 /* Text control window proc - implements scrolling without a cursor */
1401 LRESULT CALLBACK NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1402 {
1403         switch(message) {
1404
1405         case WM_KEYDOWN:
1406                 switch (wParam)
1407         {
1408         /* close on space in Windows mode
1409            page down on space in NetHack mode */
1410         case VK_SPACE:
1411         {   
1412             SCROLLINFO si;
1413
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);
1421             else
1422                             PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0);
1423             return 0;
1424         }
1425         case VK_NEXT:
1426             SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0);
1427             return 0;
1428         case VK_PRIOR:
1429             SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0);
1430             return 0;
1431         case VK_UP:
1432             SendMessage(hWnd, EM_SCROLL, SB_LINEUP, 0);
1433             return 0;
1434         case VK_DOWN:
1435             SendMessage(hWnd, EM_SCROLL, SB_LINEDOWN, 0);
1436             return 0;
1437
1438                 }
1439         break;
1440
1441         }
1442
1443         if( editControlWndProc ) 
1444                 return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam);
1445         else 
1446                 return 0;
1447 }
1448 /*-----------------------------------------------------------------------------*/