OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / sys / wince / mhmenu.c
1 /* NetHack may be freely redistributed.  See license for details. */
2
3 #include "winMS.h"
4 #include <assert.h>
5 #include "mhmenu.h"
6 #include "mhmain.h"
7 #include "mhmsg.h"
8 #include "mhcmd.h"
9 #include "mhinput.h"
10 #include "mhfont.h"
11 #include "mhcolor.h"
12 #include "mhtxtbuf.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 typedef struct mswin_menu_item {
21         int                             glyph;
22         ANY_P                   identifier;
23         CHAR_P                  accelerator;
24         CHAR_P                  group_accel;
25         int                             attr;
26         char                    str[NHMENU_STR_SIZE];
27         BOOLEAN_P               presel;
28         int                             count;
29         BOOL                    has_focus;
30         BOOL                    has_tab;
31 } NHMenuItem, *PNHMenuItem;
32
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 */
36
37         union {
38                 struct menu_list {
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 */
46                 } menu;
47
48                 struct menu_text {
49                         PNHTextBuffer   text;
50                 } text;
51         };
52         int result;
53         int done;
54
55         HBITMAP bmpChecked;
56         HBITMAP bmpCheckedCount;
57         HBITMAP bmpNotChecked;
58 } NHMenuWindow, *PNHMenuWindow;
59
60 extern short glyph2tile[];
61
62 static WNDPROC wndProcListViewOrig = NULL;
63 static WNDPROC editControlWndProc = NULL;
64
65 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL)
66 #define NHMENU_IS_SELECTED(item) ((item).count!=0)
67
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);
83
84 HWND mswin_init_menu_window (int type) {
85         HWND ret;
86
87         ret = CreateDialog(
88                         GetNHApp()->hApp,
89                         MAKEINTRESOURCE(IDD_MENU),
90                         GetNHApp()->hMainWnd,
91                         MenuWndProc
92         );
93         if( !ret ) {
94                 panic("Cannot create menu window");
95         }
96         
97         SetMenuType(ret, type);
98         return ret;
99 }
100
101
102 int mswin_menu_window_select_menu (HWND hWnd, int how, MENU_ITEM_P ** _selected)
103 {
104         PNHMenuWindow data;
105         int ret_val;
106     MENU_ITEM_P *selected = NULL;
107         int i;
108         char* ap;
109         char accell_str[256];
110
111         assert( _selected!=NULL );
112         *_selected = NULL;
113         ret_val = -1;
114
115         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
116
117         /* set menu type */
118         SetMenuListType(hWnd, how);
119
120         /* Ok, now give items a unique accelerators */
121         ZeroMemory(accell_str, sizeof(accell_str));
122         ap = accell_str;
123
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;
137                 }
138                 *ap++ = MENU_SEARCH;
139         }
140 #endif
141
142         if( data->type == MENU_TYPE_MENU ) {
143                 char next_char = 'a';
144
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;
154                                 } else {
155                                         if( next_char > 'z' ) next_char = 'A';
156                                         else if ( next_char > 'Z' ) break;
157
158                                         data->menu.items[i].accelerator = next_char;
159                                         *ap++ = data->menu.items[i].accelerator;
160                                 }
161
162                                 next_char ++;
163                         }
164                 }
165
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;
174                                         *ap = '\x0';
175                                 }
176                         }
177                 }
178
179                 reset_menu_count(NULL, data);
180         }
181
182 #if defined(WIN_CE_SMARTPHONE)
183         if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadFromString( accell_str );
184 #endif
185
186         mswin_popup_display(hWnd, &data->done);
187
188         /* get the result */
189         if( data->result != -1 ) {
190                 if(how==PICK_NONE) {
191                         if(data->result>=0) ret_val=0;
192                         else                            ret_val=-1;
193                 } else if(how==PICK_ONE || how==PICK_ANY) {
194                         /* count selected items */
195                         ret_val = 0;
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]) ) {
199                                         ret_val++;
200                                 }
201                         }
202                         if( ret_val > 0 ) {
203                                 int sel_ind;
204
205                                 selected = (MENU_ITEM_P*)malloc(ret_val*sizeof(MENU_ITEM_P));
206                                 if( !selected ) panic("out of memory");
207
208                                 sel_ind = 0;
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;
214                                                 sel_ind++;
215                                         }
216                                 }
217                                 ret_val = sel_ind;
218                                 *_selected = selected;
219                         }
220                 }
221         }
222
223         mswin_popup_destroy(hWnd);
224
225 #if defined(WIN_CE_SMARTPHONE)
226         if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadDefault();
227 #endif
228
229         return ret_val;
230 }
231    
232 LRESULT CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
233 {
234         PNHMenuWindow data;
235         
236         CheckInputDialog(hWnd, message, wParam, lParam);        
237
238         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
239         switch (message) 
240         {
241         case WM_INITDIALOG: {
242                 HWND text_control;
243                 HDC hDC;
244
245                 text_control = GetDlgItem(hWnd, IDC_MENU_TEXT);
246
247                 data = (PNHMenuWindow)malloc(sizeof(NHMenuWindow));
248                 ZeroMemory(data, sizeof(NHMenuWindow));
249                 data->type = MENU_TYPE_TEXT;
250                 data->how = PICK_NONE;
251                 data->result = 0;
252                 data->done = 0;
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);
257
258                 /* subclass edit control */
259                 editControlWndProc = (WNDPROC)GetWindowLong(text_control, GWL_WNDPROC);
260                 SetWindowLong(text_control, GWL_WNDPROC, (LONG)NHMenuTextWndProc);
261
262                 /* set text window font */
263                 hDC = GetDC(text_control);
264                 SendMessage(
265                         text_control, 
266                         WM_SETFONT, 
267                         (WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hDC, FALSE),
268                         (LPARAM)0
269                 );
270                 ReleaseDC(text_control, hDC);
271
272 #if defined(WIN_CE_SMARTPHONE)
273                 /* special initialization for SmartPhone dialogs */ 
274                 NHSPhoneDialogSetup(hWnd, FALSE, GetNHApp()->bFullScreen);
275 #endif
276         } break;
277
278         case WM_MSNH_COMMAND:
279                 onMSNHCommand(hWnd, wParam, lParam);
280         break;
281
282         case WM_SIZE:
283                 LayoutMenu(hWnd);
284         return FALSE;
285
286         case WM_COMMAND: 
287         {
288                 switch (LOWORD(wParam)) 
289         { 
290                 case IDCANCEL:
291                         if( data->type == MENU_TYPE_MENU && 
292                             (data->how==PICK_ONE || data->how==PICK_ANY) &&
293                             data->menu.counting) {
294                                 HWND list;
295                                 int i;
296
297                                 /* reset counter if counting is in progress */
298                                 list = GetMenuControl(hWnd);
299                                 i = ListView_GetNextItem(list, -1,      LVNI_FOCUSED);
300                                 if( i>=0 ) {
301                                         SelectMenuItem(list, data, i,  0);
302                                 }
303                                 return FALSE;
304                         } else {
305                                 data->result = -1;
306                                 data->done = 1;
307                         }
308                 return FALSE;
309
310                 case IDOK:
311                         data->done = 1;
312                         data->result = 0;
313                 return FALSE;
314                 }
315         } break;
316
317         case WM_NOTIFY:
318         {
319                 LPNMHDR lpnmhdr = (LPNMHDR)lParam;
320                 switch (LOWORD(wParam)) {
321                 case IDC_MENU_LIST:
322                 {
323                         if( !data || data->type!=MENU_TYPE_MENU ) break;
324
325                         switch(lpnmhdr->code) {
326                         case LVN_ITEMACTIVATE: 
327                         {
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]) ) {
333                                                 SelectMenuItem(
334                                                         lpnmlv->hdr.hwndFrom, 
335                                                         data, 
336                                                         lpnmlv->iItem, 
337                                                         -1
338                                                 );
339                                                 data->done = 1;
340                                                 data->result = 0;
341                                                 return TRUE;
342                                         }
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]) ) {
347                                                 SelectMenuItem(
348                                                         lpnmlv->hdr.hwndFrom, 
349                                                         data, 
350                                                         lpnmlv->iItem, 
351                                                         NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1
352                                                 );
353                                         }
354                                 }
355                         } break;
356
357                         case NM_CLICK: {
358                                 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
359                                 if( lpnmlv->iItem==-1 ) return 0;
360                                 if( data->how==PICK_ANY ) {
361                                         SelectMenuItem(
362                                                 lpnmlv->hdr.hwndFrom, 
363                                                 data, 
364                                                 lpnmlv->iItem, 
365                                                 NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1
366                                         );
367                                 }
368                         } break;
369
370                         case LVN_ITEMCHANGED: 
371                         {
372                                 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
373                                 if( lpnmlv->iItem==-1 ) return 0;
374                                 if( !(lpnmlv->uChanged & LVIF_STATE) ) return 0;
375
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);
379
380                                 /* update count for single-selection menu (follow the listview selection) */
381                                 if( data->how==PICK_ONE ) {
382                                         if( lpnmlv->uNewState & LVIS_SELECTED ) {
383                                                 SelectMenuItem(
384                                                         lpnmlv->hdr.hwndFrom, 
385                                                         data, 
386                                                         lpnmlv->iItem, 
387                                                         -1
388                                                 );
389                                         }
390                                 }
391
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);
395                         } break;
396
397                         case NM_KILLFOCUS:
398                                 reset_menu_count(lpnmhdr->hwndFrom, data);
399                         break;
400
401                         }
402                 } break;
403                 }
404         } break;
405
406         case WM_SETFOCUS:
407                 if( hWnd!=GetNHApp()->hPopupWnd ) {
408                         SetFocus(GetNHApp()->hPopupWnd );
409                         return 0;
410                 }
411         break;
412         
413     case WM_MEASUREITEM: 
414                 if( wParam==IDC_MENU_LIST )
415                         return onMeasureItem(hWnd, wParam, lParam);
416                 else
417                         return FALSE;
418
419     case WM_DRAWITEM:
420                 if( wParam==IDC_MENU_LIST )
421                         return onDrawItem(hWnd, wParam, lParam);
422                 else
423                         return FALSE;
424
425         case WM_CTLCOLORBTN:
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);
434                 }
435         } return FALSE;
436
437         case WM_DESTROY:
438                 if( data ) {
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;
446                                 }
447                         }
448                         free(data);
449                         SetWindowLong(hWnd, GWL_USERDATA, (LONG)0);
450                 }
451                 return TRUE;
452         }
453         return FALSE;
454 }
455
456 void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
457 {
458 #if defined(WIN_CE_POCKETPC)
459         PNHMenuWindow data;
460
461         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
462
463         if( !( data && 
464                    data->type==MENU_TYPE_MENU &&
465                    (data->how==PICK_ONE || data->how==PICK_ANY) ) ) return;
466         
467         switch(message) {
468         case WM_SETFOCUS:
469                 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
470         return;
471
472         case WM_DESTROY:
473         case WM_KILLFOCUS:
474                 if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
475         return;
476
477         case WM_NOTIFY:
478         {
479                 LPNMHDR lpnmhdr = (LPNMHDR)lParam;
480                 switch(lpnmhdr->code) {
481                 case NM_SETFOCUS:
482                         if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
483                 break;
484                 case NM_KILLFOCUS:
485                         if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
486                 break;
487                 }
488         } return;
489
490         case WM_COMMAND:
491                 switch(HIWORD(wParam)) {
492                 case BN_SETFOCUS:
493                         if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
494                 break;
495                 case BN_KILLFOCUS:
496                         if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
497                 break;
498                 }
499         return;
500
501         } /* end switch */
502 #endif
503 }
504
505 void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
506 {
507         PNHMenuWindow data;
508
509         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
510         switch( wParam ) {
511         case MSNH_MSG_PUTSTR: 
512         {
513                 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam;
514                 HWND   text_view;
515
516                 if( data->type!=MENU_TYPE_TEXT )
517                         SetMenuType(hWnd, MENU_TYPE_TEXT);
518
519                 if( !data->text.text ) {
520                         data->text.text = mswin_init_text_buffer(
521                                                 program_state.gameover? FALSE : GetNHApp()->bWrapText
522                                         );
523                         if( !data->text.text ) break;
524                 }
525                 
526                 mswin_add_text(data->text.text, msg_data->attr, msg_data->text); 
527                 
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);
531         } break;
532
533         case MSNH_MSG_STARTMENU:
534         {
535                 int i;
536
537                 if( data->type!=MENU_TYPE_MENU )
538                         SetMenuType(hWnd, MENU_TYPE_MENU);
539
540                 if( data->menu.items ) free(data->menu.items);
541                 data->how = PICK_NONE;
542                 data->menu.items = NULL;
543                 data->menu.size = 0;
544                 data->menu.allocated = 0;
545                 data->done = 0;
546                 data->result = 0;
547                 for (i = 0; i < NUMTABS; ++i)
548                         data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
549         } break;
550
551         case MSNH_MSG_ADDMENU:
552         {
553                 PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam;
554                 char *p, *p1;
555                 int new_item;
556                 HDC hDC;
557                 int column;
558                 HFONT saveFont;
559                 
560                 if( data->type!=MENU_TYPE_MENU ) break;
561                 if( strlen(msg_data->str)==0 ) break;
562
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));
566                 }
567
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;
577
578                 /* calculate tabstop size */
579                 p = strchr(data->menu.items[new_item].str, '\t');
580                 if( p ) {
581                         data->menu.items[new_item].has_tab = TRUE;
582                         hDC = GetDC(hWnd);
583                         saveFont = SelectObject(hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE));
584                         p1 = data->menu.items[new_item].str;
585                         column = 0;
586                         for (;;) {
587                                 TCHAR wbuf[BUFSZ];
588                                 RECT drawRect;
589                                 SetRect ( &drawRect, 0, 0, 1, 1 );
590                                 if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
591                                 DrawText(hDC,
592                                         NH_A2W(p1, wbuf, BUFSZ),
593                                         strlen(p1),
594                                         &drawRect,
595                                         DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE
596                                 );
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;
601
602                                 ++column;
603                                 p1 = p + 1;
604                                 p = strchr(p1, '\t');
605                         }
606                         SelectObject(hDC, saveFont);
607                         ReleaseDC(hWnd, hDC);
608                 } else {
609                         data->menu.items[new_item].has_tab = FALSE;
610                 }
611
612                 /* increment size */
613                 data->menu.size++;
614         } break;
615
616         case MSNH_MSG_ENDMENU:
617         {
618                 PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu)lParam;
619                 if( msg_data->text ) {
620                         strncpy( data->menu.prompt, msg_data->text, sizeof(data->menu.prompt)-1 );
621                 } else {
622                         ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
623                 }
624         } break;
625
626         } /* end switch */
627 }
628
629 void LayoutMenu(HWND hWnd) 
630 {
631         PNHMenuWindow data;
632         HWND  menu_ok;
633         HWND  menu_cancel;
634         RECT  clrt, rt;
635         POINT pt_elem, pt_ok, pt_cancel;
636         SIZE  sz_elem, sz_ok, sz_cancel;
637
638         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
639         menu_ok = GetDlgItem(hWnd, IDOK);
640         menu_cancel = GetDlgItem(hWnd, IDCANCEL);
641
642         /* get window coordinates */
643         GetClientRect(hWnd, &clrt );
644         
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 );
653         } else {
654                 pt_ok.x = 0;
655                 pt_ok.y = clrt.bottom;
656                 sz_ok.cx = sz_ok.cy = 0;
657         }
658         
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 );
666         } else {
667                 pt_cancel.x = 0;
668                 pt_cancel.y = clrt.bottom;
669                 sz_cancel.cx = sz_cancel.cy = 0;
670         }
671
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 );
677
678         /* reformat text for the text menu */
679         if(     data && 
680                 data->type==MENU_TYPE_TEXT &&
681                 data->text.text )
682                 mswin_render_text(data->text.text, GetMenuControl(hWnd));
683 }
684
685 void SetMenuType(HWND hWnd, int type)
686 {
687         PNHMenuWindow data;
688         HWND list, text;
689
690         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
691
692         data->type = type;
693         
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);
701                 SetFocus(text);
702         } else {
703                 ShowWindow(text, SW_HIDE);
704                 EnableWindow(text, FALSE);
705                 EnableWindow(list, TRUE);
706                 ShowWindow(list, SW_SHOW);
707                 SetFocus(list);
708         }
709         LayoutMenu(hWnd);
710 }
711
712 void SetMenuListType(HWND hWnd, int how)
713 {
714         PNHMenuWindow data;
715         RECT rt;
716         DWORD dwStyles;
717         char buf[BUFSZ];
718         TCHAR wbuf[BUFSZ];
719         int nItem;
720         int i;
721         HWND control;
722         LVCOLUMN lvcol;
723         LRESULT fnt;
724         SIZE wnd_size;
725
726         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
727         if( data->type != MENU_TYPE_MENU ) return;
728
729         data->how = how;
730
731         switch(how) {
732         case PICK_NONE: 
733                 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
734                         | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
735                         | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
736                 break;
737         case PICK_ONE: 
738                 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
739                         | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
740                         | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
741                 break;
742         case PICK_ANY: 
743                 dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
744                         | WS_VSCROLL | WS_HSCROLL | LVS_REPORT
745                         | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
746                 break;
747         default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
748         };
749         if( strlen(data->menu.prompt)==0 ) {
750                 dwStyles |= LVS_NOCOLUMNHEADER ;
751         }
752
753         GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
754         DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
755         control = CreateWindow(WC_LISTVIEW, NULL, 
756                 dwStyles,
757                 rt.left,
758                 rt.top,
759                 rt.right - rt.left,
760                 rt.bottom - rt.top,
761                 hWnd,
762                 (HMENU)IDC_MENU_LIST,
763                 GetNHApp()->hApp,
764                 NULL );
765         if( !control ) panic( "cannot create menu control" );
766         
767         /* install the hook for the control window procedure */
768         wndProcListViewOrig = (WNDPROC)GetWindowLong(control, GWL_WNDPROC);
769         SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuListWndProc);
770
771         /* set control font */
772         fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0);
773         SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0);
774
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));
779
780         /* add column to the list view */
781         mswin_menu_window_size(hWnd, &wnd_size);
782
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);
788
789         /* add items to the list view */
790         for(i=0; i<data->menu.size; i++ ) {
791                 LVITEM lvitem;
792                 ZeroMemory( &lvitem, sizeof(lvitem) );
793                 sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str );
794
795                 lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
796                 lvitem.iItem = i;
797                 lvitem.iSubItem = 0;
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");
804                 }
805         }
806         SetFocus(control);
807 }
808
809
810 HWND GetMenuControl(HWND hWnd)
811 {
812         PNHMenuWindow data;
813
814         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
815
816         if(data->type==MENU_TYPE_TEXT) {
817                 return GetDlgItem(hWnd, IDC_MENU_TEXT);
818         } else {
819                 return GetDlgItem(hWnd, IDC_MENU_LIST);
820         }
821 }
822
823
824 LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
825 {
826     LPMEASUREITEMSTRUCT lpmis; 
827     TEXTMETRIC tm;
828         HGDIOBJ saveFont;
829         HDC hdc;
830         PNHMenuWindow data;
831         RECT list_rect;
832
833     lpmis = (LPMEASUREITEMSTRUCT) lParam; 
834         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
835         GetClientRect(GetMenuControl(hWnd), &list_rect);
836
837         hdc = GetDC(GetMenuControl(hWnd));
838         saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
839         GetTextMetrics(hdc, &tm);
840
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;
844
845         SelectObject(hdc, saveFont);
846         ReleaseDC(GetMenuControl(hWnd), hdc);
847         return TRUE;
848 }
849
850 LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
851 {
852     LPDRAWITEMSTRUCT lpdis; 
853         PNHMenuItem item;
854         PNHMenuWindow data;
855     TEXTMETRIC tm;
856         HGDIOBJ saveFont;
857         HDC tileDC;
858         short ntile;
859         int t_x, t_y;
860         int x, y;
861         TCHAR wbuf[BUFSZ];
862         RECT drawRect;
863         COLORREF OldBg, OldFg, NewBg;
864         char *p, *p1;
865         int column;
866
867         lpdis = (LPDRAWITEMSTRUCT) lParam; 
868
869     /* If there are no list box items, skip this message. */
870     if (lpdis->itemID == -1) return FALSE;
871
872         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
873
874     item = &data->menu.items[lpdis->itemID];
875
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)); 
881
882     GetTextMetrics(lpdis->hDC, &tm);
883
884         x = lpdis->rcItem.left + 1;
885
886         /* print check mark if it is a "selectable" menu */
887         if( data->how!=PICK_NONE ) {
888                 if( NHMENU_IS_SELECTABLE(*item) ) {
889                         HGDIOBJ saveBrush;
890                         HBRUSH  hbrCheckMark;
891                         char buf[2];
892
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;
897                         }
898
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);
905
906                         x += TILE_X + 5;
907
908                         if(item->accelerator!=0) {
909                                 buf[0] = item->accelerator;
910                                 buf[1] = '\x0';
911
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);
914                         }
915                         x += tm.tmAveCharWidth + tm.tmOverhang + 5;
916                 } else {
917                         x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10;
918                 }
919         }
920
921         /* print glyph if present */
922         if( item->glyph != NO_GLYPH ) {
923                 HGDIOBJ saveBmp;
924
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;
929
930                 y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; 
931
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);
936         }
937
938         x += TILE_X + 5;
939
940         /* draw item text */
941         if( item->has_tab ) {
942                 p1 = item->str;
943                 p = strchr(item->str, '\t');
944                 column = 0;
945                 SetRect( &drawRect, x, lpdis->rcItem.top, min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
946                         lpdis->rcItem.bottom );
947                 for (;;) {
948                         TCHAR wbuf[BUFSZ];
949                         if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
950                         DrawText(lpdis->hDC,
951                                 NH_A2W(p1, wbuf, BUFSZ),
952                                 strlen(p1),
953                                 &drawRect,
954                                 DT_LEFT | DT_VCENTER | DT_SINGLELINE
955                         );
956                         if (p != NULL) *p = '\t';
957                         else /* last string so, */ break;
958
959                         p1 = p + 1;
960                         p = strchr(p1, '\t');
961                         drawRect.left = drawRect.right + TAB_SEPARATION;
962                         ++column;
963                         drawRect.right = min (drawRect.left + data->menu.tab_stop_size[column], lpdis->rcItem.right);
964                 }
965         } else {
966                 TCHAR wbuf[BUFSZ];
967                 SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom);
968                 DrawText(lpdis->hDC,
969                         NH_A2W(item->str, wbuf, BUFSZ),
970                         strlen(item->str),
971                         &drawRect,
972                         DT_LEFT | DT_VCENTER | DT_SINGLELINE
973                 );
974         }
975
976         /* draw focused item */
977         if( item->has_focus ) {
978                 RECT client_rt;
979                 HBRUSH bkBrush;
980
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") );
987                         } else {
988                                 _stprintf(wbuf, TEXT("Count: %d"), data->menu.items[lpdis->itemID].count );
989                         }
990
991                         SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE));
992
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 );
997                         
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 );
1006
1007                         /* draw text */
1008                         DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, 
1009                                          DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
1010                 }
1011
1012                 /* draw focus rect */
1013                 SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom );
1014                 DrawFocusRect(lpdis->hDC, &drawRect);
1015         }
1016
1017         SetTextColor (lpdis->hDC, OldFg);
1018         SetBkColor (lpdis->hDC, OldBg);
1019         SelectObject(lpdis->hDC, saveFont);
1020         DeleteDC(tileDC);
1021         return TRUE;
1022 }
1023
1024 BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch)
1025 {
1026         int i = 0;
1027         PNHMenuWindow data;
1028         int curIndex, topIndex, pageSize;
1029         boolean is_accelerator = FALSE;
1030
1031         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
1032
1033         switch( ch ) {
1034         case MENU_FIRST_PAGE:
1035                 i = 0;
1036                 ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1037                 ListView_EnsureVisible(hwndList, i, FALSE);
1038         return -2;
1039
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);
1044         return -2;
1045
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);
1056         return -2;
1057
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);
1068         break;
1069
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);
1075                         }
1076                         return -2;
1077                 }
1078         break;
1079
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);
1085                         }
1086                         return -2;
1087                 }
1088         break;
1089
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++ ) {
1094                                 SelectMenuItem(
1095                                         hwndList, 
1096                                         data, 
1097                                         i, 
1098                                         NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1099                                 );
1100                         }
1101                         return -2;
1102                 }
1103         break;
1104
1105         case MENU_SELECT_PAGE:
1106                 if( data->how == PICK_ANY ) {
1107                         int from, to;
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);
1115                         }
1116                         return -2;
1117                 }
1118         break;
1119
1120         case MENU_UNSELECT_PAGE:
1121                 if( data->how == PICK_ANY ) {
1122                         int from, to;
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);
1130                         }
1131                         return -2;
1132                 }
1133         break;
1134
1135         case MENU_INVERT_PAGE:
1136                 if( data->how == PICK_ANY ) {
1137                         int from, to;
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++ ) {
1144                                 SelectMenuItem(
1145                                         hwndList, 
1146                                         data, 
1147                                         i, 
1148                                         NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1149                                 );
1150                         }
1151                         return -2;
1152                 }
1153         break;
1154
1155         case MENU_SEARCH:
1156             if( data->how==PICK_ANY || data->how==PICK_ONE ) {
1157                         char buf[BUFSZ];
1158                         int  selected_item;
1159                         
1160                         reset_menu_count(hwndList, data);
1161                         mswin_getlin("Search for:", buf);
1162                         if (!*buf || *buf == '\033') return -2;
1163                         selected_item = -1;
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) {
1168                                                 SelectMenuItem(
1169                                                         hwndList, 
1170                                                         data, 
1171                                                         i, 
1172                                                         NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1173                                                 );
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 ) {
1177                                                 SelectMenuItem(
1178                                                         hwndList, 
1179                                                         data, 
1180                                                         i, 
1181                                                         -1
1182                                                 );
1183                                                 selected_item = i;
1184                                                 break;
1185                                         }
1186                                 }
1187                         } 
1188
1189                         if( selected_item>0 ) {
1190                                 ListView_SetItemState(hwndList, selected_item, LVIS_FOCUSED, LVIS_FOCUSED);
1191                                 ListView_EnsureVisible(hwndList, selected_item, FALSE);
1192                         }
1193                 } else {
1194                         mswin_nhbell();
1195             }
1196         return -2;
1197
1198         case ' ':
1199                 /* ends menu for PICK_ONE/PICK_NONE
1200                    select item for PICK_ANY */
1201                 if( data->how==PICK_ONE || data->how==PICK_NONE ) {
1202                         data->done = 1;
1203                         data->result = 0;
1204                         return -2;
1205                 } else if( data->how==PICK_ANY ) {
1206                         i = ListView_GetNextItem(hwndList, -1,  LVNI_FOCUSED);
1207                         if( i>=0 ) {
1208                                 SelectMenuItem(
1209                                         hwndList, 
1210                                         data, 
1211                                         i, 
1212                                         NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1213                                 );
1214                         }
1215                         return -2;
1216                 }
1217         break;
1218
1219         default:
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 ) {
1229                                                         SelectMenuItem(
1230                                                                 hwndList, 
1231                                                                 data, 
1232                                                                 i, 
1233                                                                 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1234                                                         );
1235                                                 } else if( data->how == PICK_ONE ) {
1236                                                         SelectMenuItem(
1237                                                                 hwndList, 
1238                                                                 data, 
1239                                                                 i, 
1240                                                                 -1
1241                                                         );
1242                                                         data->result = 0;
1243                                                         data->done = 1;
1244                                                         return -2;
1245                                                 }
1246                                         }
1247                                 }
1248                                 return -2;
1249                         } else {
1250                                 mswin_nhbell();
1251                                 return -2;
1252                         }
1253                 }
1254
1255                 if (isdigit(ch)) {
1256                         int count;
1257                         i = ListView_GetNextItem(hwndList, -1,  LVNI_FOCUSED);
1258                         if( i>=0 ) {
1259                                 count = data->menu.items[i].count;
1260                                 if( count==-1 ) count=0;
1261                                 count *= 10L;
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 */
1267                                 }
1268                         }
1269                         return -2;
1270                 }
1271
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;
1276                                 break;
1277                         }
1278                 }
1279
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 ) {
1286                                                         SelectMenuItem(
1287                                                                 hwndList, 
1288                                                                 data, 
1289                                                                 i, 
1290                                                                 NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
1291                                                         );
1292                                                         ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1293                                                         ListView_EnsureVisible(hwndList, i, FALSE);
1294                                                         return -2;
1295                                                 } else if( data->how == PICK_ONE ) {
1296                                                         SelectMenuItem(
1297                                                                 hwndList, 
1298                                                                 data, 
1299                                                                 i, 
1300                                                                 -1
1301                                                         );
1302                                                         data->result = 0;
1303                                                         data->done = 1;
1304                                                         return -2;
1305                                                 }
1306                                         }
1307                                 }
1308                         }
1309                 }
1310         break;
1311         }
1312
1313         reset_menu_count(hwndList, data);
1314         return -1;
1315 }
1316
1317 void mswin_menu_window_size (HWND hWnd, LPSIZE sz)
1318 {
1319     TEXTMETRIC tm;
1320         HWND control;
1321         HGDIOBJ saveFont;
1322         HDC hdc;
1323         PNHMenuWindow data;
1324         int i;
1325         RECT rt, wrt;
1326         int extra_cx;
1327
1328         GetClientRect(hWnd, &rt);
1329         sz->cx = rt.right - rt.left;
1330         sz->cy = rt.bottom - rt.top;
1331
1332         GetWindowRect(hWnd, &wrt);
1333         extra_cx = (wrt.right-wrt.left) - sz->cx;
1334
1335         data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
1336         if(data) {
1337                 control = GetMenuControl(hWnd);
1338                 hdc = GetDC(control);
1339
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;
1346                                 int column;
1347                                 char *p, *p1;
1348
1349                                 p1 = data->menu.items[i].str;
1350                                 p = strchr(data->menu.items[i].str, '\t');
1351                                 column = 0;
1352                                 for (;;) {
1353                                         TCHAR wbuf[BUFSZ];
1354                                         RECT tabRect;
1355                                         SetRect ( &tabRect, 0, 0, 1, 1 );
1356                                         if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
1357                                         DrawText(hdc,
1358                                                 NH_A2W(p1, wbuf, BUFSZ),
1359                                                 strlen(p1),
1360                                                 &tabRect,
1361                                                 DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE
1362                                         );
1363                                         /* it probably isn't necessary to recompute the tab width now, but do so
1364                                          * just in case, honoring the previously computed value
1365                                          */
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;
1373                                         ++column;
1374                                         p1 = p + 1;
1375                                         p = strchr(p1, '\t');
1376                                 }
1377
1378                                 sz->cx = max(sz->cx, 
1379                                         (LONG)(2*TILE_X + menuitemwidth + tm.tmAveCharWidth*12 + tm.tmOverhang));
1380                         }
1381                         SelectObject(hdc, saveFont);
1382                 } else {
1383                         /* do not change size for text output - the text will be formatted to
1384                            fit any window */
1385                 }
1386                 sz->cx += extra_cx;
1387
1388                 ReleaseDC(control, hdc);
1389         }
1390 }
1391
1392 void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1393 {
1394         int i;
1395
1396         if( item<0 || item>=data->menu.size ) return;
1397
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 );
1403                         };
1404         }
1405
1406         data->menu.items[item].count = count;
1407         ListView_RedrawItems( hwndList, item, item );
1408         reset_menu_count(hwndList, data);
1409 }
1410
1411 void reset_menu_count(HWND hwndList, PNHMenuWindow data) 
1412 {
1413         int i; 
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 ); 
1418         }
1419 }
1420
1421 /* List window Proc */
1422 LRESULT CALLBACK NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1423 {
1424         BOOL bUpdateFocusItem = FALSE;
1425
1426         switch(message) {
1427
1428         /* filter keyboard input for the control */
1429 #if !defined(WIN_CE_SMARTPHONE)
1430         case WM_KEYDOWN:
1431         case WM_KEYUP: {
1432                 MSG msg;
1433
1434                 if( PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) ) {
1435                         if( onListChar(GetParent(hWnd), hWnd, (char)msg.wParam)==-2 ) {
1436                                 return 0;
1437                         }
1438                 }
1439
1440                 if( wParam==VK_LEFT || wParam==VK_RIGHT )
1441                         bUpdateFocusItem = TRUE;
1442         } break;
1443
1444 #else /* defined(WIN_CE_SMARTPHONE) */
1445         case WM_KEYDOWN:
1446                 if( wParam==VK_TACTION ) {
1447                         if( onListChar(GetParent(hWnd), hWnd, ' ')==-2 ) {
1448                                 return 0;
1449                         }
1450                 } else if( NHSPhoneTranslateKbdMessage(wParam, lParam, TRUE) ) {
1451                         PMSNHEvent      evt;
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 ) {
1457                                         processed = TRUE;
1458                                 }
1459
1460                                 /* eat the rest of the events */
1461                                 if( mswin_have_input() ) mswin_input_pop();
1462                         }
1463                         if( processed ) return 0;
1464                 }
1465
1466                 if( wParam==VK_LEFT || wParam==VK_RIGHT )
1467                         bUpdateFocusItem = TRUE;
1468         break;
1469
1470         case WM_KEYUP:
1471                 /* translate SmartPhone keyboard message */
1472                 if( NHSPhoneTranslateKbdMessage(wParam, lParam, FALSE) )
1473                         return 0;
1474         break;
1475
1476         /* tell Windows not to process default button on VK_RETURN */
1477         case WM_GETDLGCODE: 
1478                 return DLGC_DEFPUSHBUTTON | DLGC_WANTALLKEYS |
1479                            (wndProcListViewOrig? 
1480                                                 CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam) 
1481                                                 : 0 );
1482 #endif
1483
1484         case WM_SIZE:
1485         case WM_HSCROLL:
1486                 bUpdateFocusItem = TRUE;
1487         break;
1488
1489         }
1490
1491         if(     bUpdateFocusItem ) {
1492                 int i;
1493                 RECT rt;
1494
1495                 /* invalidate the focus rectangle */
1496                 i = ListView_GetNextItem(hWnd, -1,      LVNI_FOCUSED);
1497                 if( i!=-1 ) {
1498                         ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1499                         InvalidateRect(hWnd, &rt, TRUE);
1500                 }
1501         }
1502
1503         if( wndProcListViewOrig ) 
1504                 return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam);
1505         else 
1506                 return 0;
1507 }
1508
1509 /* Text control window proc - implements close on space */
1510 LRESULT CALLBACK NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1511 {
1512         switch(message) {
1513         case WM_KEYUP:
1514                 switch( wParam ) {
1515                 case VK_SPACE:
1516                 case VK_RETURN:
1517                         /* close on space */
1518                         PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0);
1519                         return 0;
1520                 
1521                 case VK_UP:
1522                         /* scoll up */
1523                         PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL);
1524                         return 0;
1525
1526                 case VK_DOWN:
1527                         /* scoll down */
1528                         PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL);
1529                         return 0;
1530
1531                 case VK_LEFT:
1532                         /* scoll left */
1533                         PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, 0), (LPARAM)NULL);
1534                         return 0;
1535
1536                 case VK_RIGHT:
1537                         /* scoll right */
1538                         PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, 0), (LPARAM)NULL);
1539                         return 0;
1540                 }
1541                 break; /* case WM_KEYUP: */
1542         }
1543
1544         if( editControlWndProc ) 
1545                 return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam);
1546         else 
1547                 return 0;
1548 }
1549 /*----------------------------------------------------------------------------*/
1550 char* parse_menu_str(char* dest, const char* src, size_t size)
1551 {
1552         char *p1, *p2;
1553         if( !dest || size==0 ) return NULL;
1554
1555         strncpy(dest, src, size);
1556         dest[size-1] = '\x0';
1557
1558         /* replace "[ ]*\[" with "\t\[" */
1559         p1 = p2 = strstr(dest, " [");
1560         if( p1 ) {
1561                 while( p1!=dest && *p1==' ') p1--;
1562                 p1++; /* backup to space */
1563                 *p2 = '\t';
1564                 memmove(p1, p2, strlen(p2));
1565                 p1[strlen(p2)] = '\x0';
1566         }
1567         return dest;
1568 }