OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / sys / wince / mhmenu.c
1 /* NetHack 3.6  mhmenu.c       $NHDT-Date: 1524689398 2018/04/25 20:49:58 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.28 $ */
2 /*      Copyright (c) 2009 by Michael Allison              */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "winMS.h"
6 #include <assert.h>
7 #include "mhmenu.h"
8 #include "mhmain.h"
9 #include "mhmsg.h"
10 #include "mhcmd.h"
11 #include "mhinput.h"
12 #include "mhfont.h"
13 #include "mhcolor.h"
14 #include "mhtxtbuf.h"
15
16 #define MENU_MARGIN 0
17 #define NHMENU_STR_SIZE BUFSZ
18 #define MIN_TABSTOP_SIZE 0
19 #define NUMTABS 15
20 #define TAB_SEPARATION 10 /* pixels between each tab stop */
21
22 typedef struct mswin_menu_item {
23     int glyph;
24     ANY_P identifier;
25     CHAR_P accelerator;
26     CHAR_P group_accel;
27     int attr;
28     char str[NHMENU_STR_SIZE];
29     BOOLEAN_P presel;
30     int count;
31     BOOL has_focus;
32     BOOL has_tab;
33 } NHMenuItem, *PNHMenuItem;
34
35 typedef struct mswin_nethack_menu_window {
36     int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */
37     int how;  /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */
38
39     union {
40         struct menu_list {
41             int size;            /* number of items in items[] */
42             int allocated;       /* number of allocated slots in items[] */
43             PNHMenuItem items;   /* menu items */
44             char gacc[QBUFSZ];   /* group accelerators */
45             BOOL counting;       /* counting flag */
46             char prompt[QBUFSZ]; /* menu prompt */
47             int tab_stop_size[NUMTABS]; /* tabstops to align option values */
48         } menu;
49
50         struct menu_text {
51             PNHTextBuffer text;
52         } text;
53     };
54     int result;
55     int done;
56
57     HBITMAP bmpChecked;
58     HBITMAP bmpCheckedCount;
59     HBITMAP bmpNotChecked;
60 } NHMenuWindow, *PNHMenuWindow;
61
62 extern short glyph2tile[];
63
64 static WNDPROC wndProcListViewOrig = NULL;
65 static WNDPROC editControlWndProc = NULL;
66
67 #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj != NULL)
68 #define NHMENU_IS_SELECTED(item) ((item).count != 0)
69
70 LRESULT CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM);
71 LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM);
72 LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM);
73 static void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam,
74                              LPARAM lParam);
75 static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
76 static LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
77 static LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
78 static void LayoutMenu(HWND hwnd);
79 static void SetMenuType(HWND hwnd, int type);
80 static void SetMenuListType(HWND hwnd, int now);
81 static HWND GetMenuControl(HWND hwnd);
82 static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item,
83                            int count);
84 static void reset_menu_count(HWND hwndList, PNHMenuWindow data);
85 static LRESULT onListChar(HWND hWnd, HWND hwndList, WORD ch);
86 static char *parse_menu_str(char *dest, const char *src, size_t size);
87
88 HWND
89 mswin_init_menu_window(int type)
90 {
91     HWND ret;
92
93     ret = CreateDialog(GetNHApp()->hApp, MAKEINTRESOURCE(IDD_MENU),
94                        GetNHApp()->hMainWnd, MenuWndProc);
95     if (!ret) {
96         panic("Cannot create menu window");
97     }
98
99     SetMenuType(ret, type);
100     return ret;
101 }
102
103 int
104 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     char accell_str[256];
112
113     assert(_selected != NULL);
114     *_selected = NULL;
115     ret_val = -1;
116
117     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
118
119     /* set menu type */
120     SetMenuListType(hWnd, how);
121
122     /* Ok, now give items a unique accelerators */
123     ZeroMemory(accell_str, sizeof(accell_str));
124     ap = accell_str;
125
126 #if defined(WIN_CE_SMARTPHONE)
127     if (data->menu.size > 10) {
128         *ap++ = MENU_FIRST_PAGE;
129         *ap++ = MENU_LAST_PAGE;
130         *ap++ = MENU_NEXT_PAGE;
131         *ap++ = MENU_PREVIOUS_PAGE;
132         if (data->how == PICK_ANY) {
133             *ap++ = MENU_SELECT_ALL;
134             *ap++ = MENU_UNSELECT_ALL;
135             *ap++ = MENU_INVERT_ALL;
136             *ap++ = MENU_SELECT_PAGE;
137             *ap++ = MENU_UNSELECT_PAGE;
138             *ap++ = MENU_INVERT_PAGE;
139         }
140         *ap++ = MENU_SEARCH;
141     }
142 #endif
143
144     if (data->type == MENU_TYPE_MENU) {
145         char next_char = 'a';
146
147         for (i = 0; i < data->menu.size; i++) {
148             if (data->menu.items[i].accelerator != 0) {
149                 *ap++ = data->menu.items[i].accelerator;
150                 next_char = (char) (data->menu.items[i].accelerator + 1);
151             } else if (NHMENU_IS_SELECTABLE(data->menu.items[i])) {
152                 if ((next_char >= 'a' && next_char <= 'z')
153                     || (next_char >= 'A' && next_char <= 'Z')) {
154                     data->menu.items[i].accelerator = next_char;
155                     *ap++ = data->menu.items[i].accelerator;
156                 } else {
157                     if (next_char > 'z')
158                         next_char = 'A';
159                     else if (next_char > 'Z')
160                         break;
161
162                     data->menu.items[i].accelerator = next_char;
163                     *ap++ = data->menu.items[i].accelerator;
164                 }
165
166                 next_char++;
167             }
168         }
169
170         /* collect group accelerators */
171         data->menu.gacc[0] = '\0';
172         ap = data->menu.gacc;
173         if (data->how != PICK_NONE) {
174             for (i = 0; i < data->menu.size; i++) {
175                 if (data->menu.items[i].group_accel
176                     && !strchr(data->menu.gacc,
177                                data->menu.items[i].group_accel)) {
178                     *ap++ = data->menu.items[i].group_accel;
179                     *ap = '\x0';
180                 }
181             }
182         }
183
184         reset_menu_count(NULL, data);
185     }
186
187 #if defined(WIN_CE_SMARTPHONE)
188     if (data->type == MENU_TYPE_MENU)
189         NHSPhoneSetKeypadFromString(accell_str);
190 #endif
191
192     mswin_popup_display(hWnd, &data->done);
193
194     /* get the result */
195     if (data->result != -1) {
196         if (how == PICK_NONE) {
197             if (data->result >= 0)
198                 ret_val = 0;
199             else
200                 ret_val = -1;
201         } else if (how == PICK_ONE || how == PICK_ANY) {
202             /* count selected items */
203             ret_val = 0;
204             for (i = 0; i < data->menu.size; i++) {
205                 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
206                     && NHMENU_IS_SELECTED(data->menu.items[i])) {
207                     ret_val++;
208                 }
209             }
210             if (ret_val > 0) {
211                 int sel_ind;
212
213                 selected =
214                     (MENU_ITEM_P *) malloc(ret_val * sizeof(MENU_ITEM_P));
215                 if (!selected)
216                     panic("out of memory");
217
218                 sel_ind = 0;
219                 for (i = 0; i < data->menu.size; i++) {
220                     if (NHMENU_IS_SELECTABLE(data->menu.items[i])
221                         && NHMENU_IS_SELECTED(data->menu.items[i])) {
222                         selected[sel_ind].item =
223                             data->menu.items[i].identifier;
224                         selected[sel_ind].count = data->menu.items[i].count;
225                         sel_ind++;
226                     }
227                 }
228                 ret_val = sel_ind;
229                 *_selected = selected;
230             }
231         }
232     }
233
234     mswin_popup_destroy(hWnd);
235
236 #if defined(WIN_CE_SMARTPHONE)
237     if (data->type == MENU_TYPE_MENU)
238         NHSPhoneSetKeypadDefault();
239 #endif
240
241     return ret_val;
242 }
243
244 LRESULT CALLBACK
245 MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
246 {
247     PNHMenuWindow data;
248
249     CheckInputDialog(hWnd, message, wParam, lParam);
250
251     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
252     switch (message) {
253     case WM_INITDIALOG: {
254         HWND text_control;
255         HDC hDC;
256
257         text_control = GetDlgItem(hWnd, IDC_MENU_TEXT);
258
259         data = (PNHMenuWindow) malloc(sizeof(NHMenuWindow));
260         ZeroMemory(data, sizeof(NHMenuWindow));
261         data->type = MENU_TYPE_TEXT;
262         data->how = PICK_NONE;
263         data->result = 0;
264         data->done = 0;
265         data->bmpChecked =
266             LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL));
267         data->bmpCheckedCount =
268             LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT));
269         data->bmpNotChecked =
270             LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL));
271         SetWindowLong(hWnd, GWL_USERDATA, (LONG) data);
272
273         /* subclass edit control */
274         editControlWndProc =
275             (WNDPROC) GetWindowLong(text_control, GWL_WNDPROC);
276         SetWindowLong(text_control, GWL_WNDPROC, (LONG) NHMenuTextWndProc);
277
278         /* set text window font */
279         hDC = GetDC(text_control);
280         SendMessage(text_control, WM_SETFONT,
281                     (WPARAM) mswin_get_font(NHW_TEXT, ATR_NONE, hDC, FALSE),
282                     (LPARAM) 0);
283         ReleaseDC(text_control, hDC);
284
285 #if defined(WIN_CE_SMARTPHONE)
286         /* special initialization for SmartPhone dialogs */
287         NHSPhoneDialogSetup(hWnd, IDC_SPHONE_DIALOGBAR, FALSE,
288                             GetNHApp()->bFullScreen);
289 #endif
290     } break;
291
292     case WM_MSNH_COMMAND:
293         onMSNHCommand(hWnd, wParam, lParam);
294         break;
295
296     case WM_SIZE:
297         LayoutMenu(hWnd);
298         return FALSE;
299
300     case WM_COMMAND: {
301         switch (LOWORD(wParam)) {
302         case IDCANCEL:
303             if (data->type == MENU_TYPE_MENU
304                 && (data->how == PICK_ONE || data->how == PICK_ANY)
305                 && data->menu.counting) {
306                 HWND list;
307                 int i;
308
309                 /* reset counter if counting is in progress */
310                 list = GetMenuControl(hWnd);
311                 i = ListView_GetNextItem(list, -1, LVNI_FOCUSED);
312                 if (i >= 0) {
313                     SelectMenuItem(list, data, i, 0);
314                 }
315                 return FALSE;
316             } else {
317                 data->result = -1;
318                 data->done = 1;
319             }
320             return FALSE;
321
322         case IDOK:
323             data->done = 1;
324             data->result = 0;
325             return FALSE;
326         }
327     } break;
328
329     case WM_NOTIFY: {
330         LPNMHDR lpnmhdr = (LPNMHDR) lParam;
331         switch (LOWORD(wParam)) {
332         case IDC_MENU_LIST: {
333             if (!data || data->type != MENU_TYPE_MENU)
334                 break;
335
336             switch (lpnmhdr->code) {
337             case LVN_ITEMACTIVATE: {
338                 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
339                 if (data->how == PICK_ONE) {
340                     if (lpnmlv->iItem >= 0 && lpnmlv->iItem < data->menu.size
341                         && NHMENU_IS_SELECTABLE(
342                                data->menu.items[lpnmlv->iItem])) {
343                         SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
344                                        lpnmlv->iItem, -1);
345                         data->done = 1;
346                         data->result = 0;
347                         return TRUE;
348                     }
349                 } else if (data->how == PICK_ANY) {
350                     if (lpnmlv->iItem >= 0 && lpnmlv->iItem < data->menu.size
351                         && NHMENU_IS_SELECTABLE(
352                                data->menu.items[lpnmlv->iItem])) {
353                         SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
354                                        lpnmlv->iItem,
355                                        NHMENU_IS_SELECTED(
356                                            data->menu.items[lpnmlv->iItem])
357                                            ? 0
358                                            : -1);
359                     }
360                 }
361             } break;
362
363             case NM_CLICK: {
364                 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
365                 if (lpnmlv->iItem == -1)
366                     return 0;
367                 if (data->how == PICK_ANY) {
368                     SelectMenuItem(
369                         lpnmlv->hdr.hwndFrom, data, lpnmlv->iItem,
370                         NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])
371                             ? 0
372                             : -1);
373                 }
374             } break;
375
376             case LVN_ITEMCHANGED: {
377                 LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
378                 if (lpnmlv->iItem == -1)
379                     return 0;
380                 if (!(lpnmlv->uChanged & LVIF_STATE))
381                     return 0;
382
383                 /* update item that has the focus */
384                 data->menu.items[lpnmlv->iItem].has_focus =
385                     !!(lpnmlv->uNewState & LVIS_FOCUSED);
386                 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
387                                      lpnmlv->iItem);
388
389                 /* update count for single-selection menu (follow the listview
390                  * selection) */
391                 if (data->how == PICK_ONE) {
392                     if (lpnmlv->uNewState & LVIS_SELECTED) {
393                         SelectMenuItem(lpnmlv->hdr.hwndFrom, data,
394                                        lpnmlv->iItem, -1);
395                     }
396                 }
397
398                 /* check item focus */
399                 data->menu.items[lpnmlv->iItem].has_focus =
400                     !!(lpnmlv->uNewState & LVIS_FOCUSED);
401                 ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem,
402                                      lpnmlv->iItem);
403             } break;
404
405             case NM_KILLFOCUS:
406                 reset_menu_count(lpnmhdr->hwndFrom, data);
407                 break;
408             }
409         } break;
410         }
411     } break;
412
413     case WM_SETFOCUS:
414         if (hWnd != GetNHApp()->hPopupWnd) {
415             SetFocus(GetNHApp()->hPopupWnd);
416             return 0;
417         }
418         break;
419
420     case WM_MEASUREITEM:
421         if (wParam == IDC_MENU_LIST)
422             return onMeasureItem(hWnd, wParam, lParam);
423         else
424             return FALSE;
425
426     case WM_DRAWITEM:
427         if (wParam == IDC_MENU_LIST)
428             return onDrawItem(hWnd, wParam, lParam);
429         else
430             return FALSE;
431
432     case WM_CTLCOLORBTN:
433     case WM_CTLCOLOREDIT:
434     case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */
435         HDC hdcEdit = (HDC) wParam;
436         HWND hwndEdit = (HWND) lParam;
437         if (hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT)) {
438             SetBkColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_BG));
439             SetTextColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_FG));
440             return (BOOL) mswin_get_brush(NHW_TEXT, MSWIN_COLOR_BG);
441         }
442     }
443         return FALSE;
444
445     case WM_DESTROY:
446         if (data) {
447             DeleteObject(data->bmpChecked);
448             DeleteObject(data->bmpCheckedCount);
449             DeleteObject(data->bmpNotChecked);
450             if (data->type == MENU_TYPE_TEXT) {
451                 if (data->text.text) {
452                     mswin_free_text_buffer(data->text.text);
453                     data->text.text = NULL;
454                 }
455             }
456             free(data);
457             SetWindowLong(hWnd, GWL_USERDATA, (LONG) 0);
458         }
459         return TRUE;
460     }
461     return FALSE;
462 }
463
464 void
465 CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
466 {
467 #if defined(WIN_CE_POCKETPC)
468     PNHMenuWindow data;
469
470     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
471
472     if (!(data && data->type == MENU_TYPE_MENU
473           && (data->how == PICK_ONE || data->how == PICK_ANY)))
474         return;
475
476     switch (message) {
477     case WM_SETFOCUS:
478         if (GetNHApp()->bUseSIP)
479             SHSipPreference(hWnd, SIP_UP);
480         return;
481
482     case WM_DESTROY:
483     case WM_KILLFOCUS:
484         if (GetNHApp()->bUseSIP)
485             SHSipPreference(hWnd, SIP_DOWN);
486         return;
487
488     case WM_NOTIFY: {
489         LPNMHDR lpnmhdr = (LPNMHDR) lParam;
490         switch (lpnmhdr->code) {
491         case NM_SETFOCUS:
492             if (GetNHApp()->bUseSIP)
493                 SHSipPreference(hWnd, SIP_UP);
494             break;
495         case NM_KILLFOCUS:
496             if (GetNHApp()->bUseSIP)
497                 SHSipPreference(hWnd, SIP_DOWN);
498             break;
499         }
500     }
501         return;
502
503     case WM_COMMAND:
504         switch (HIWORD(wParam)) {
505         case BN_SETFOCUS:
506             if (GetNHApp()->bUseSIP)
507                 SHSipPreference(hWnd, SIP_UP);
508             break;
509         case BN_KILLFOCUS:
510             if (GetNHApp()->bUseSIP)
511                 SHSipPreference(hWnd, SIP_DOWN);
512             break;
513         }
514         return;
515
516     } /* end switch */
517 #endif
518 }
519
520 void
521 onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
522 {
523     PNHMenuWindow data;
524
525     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
526     switch (wParam) {
527     case MSNH_MSG_PUTSTR: {
528         PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam;
529         HWND text_view;
530
531         if (data->type != MENU_TYPE_TEXT)
532             SetMenuType(hWnd, MENU_TYPE_TEXT);
533
534         if (!data->text.text) {
535             data->text.text = mswin_init_text_buffer(
536                 program_state.gameover ? FALSE : GetNHApp()->bWrapText);
537             if (!data->text.text)
538                 break;
539         }
540
541         mswin_add_text(data->text.text, msg_data->attr, msg_data->text);
542
543         text_view = GetDlgItem(hWnd, IDC_MENU_TEXT);
544         if (!text_view)
545             panic("cannot get text view window");
546         mswin_render_text(data->text.text, text_view);
547     } break;
548
549     case MSNH_MSG_STARTMENU: {
550         int i;
551
552         if (data->type != MENU_TYPE_MENU)
553             SetMenuType(hWnd, MENU_TYPE_MENU);
554
555         if (data->menu.items)
556             free(data->menu.items);
557         data->how = PICK_NONE;
558         data->menu.items = NULL;
559         data->menu.size = 0;
560         data->menu.allocated = 0;
561         data->done = 0;
562         data->result = 0;
563         for (i = 0; i < NUMTABS; ++i)
564             data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
565     } break;
566
567     case MSNH_MSG_ADDMENU: {
568         PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu) lParam;
569         char *p, *p1;
570         int new_item;
571         HDC hDC;
572         int column;
573         HFONT saveFont;
574
575         if (data->type != MENU_TYPE_MENU)
576             break;
577         if (strlen(msg_data->str) == 0)
578             break;
579
580         if (data->menu.size == data->menu.allocated) {
581             data->menu.allocated += 10;
582             data->menu.items = (PNHMenuItem) realloc(
583                 data->menu.items, data->menu.allocated * sizeof(NHMenuItem));
584         }
585
586         new_item = data->menu.size;
587         ZeroMemory(&data->menu.items[new_item],
588                    sizeof(data->menu.items[new_item]));
589         data->menu.items[new_item].glyph = msg_data->glyph;
590         data->menu.items[new_item].identifier = *msg_data->identifier;
591         data->menu.items[new_item].accelerator = msg_data->accelerator;
592         data->menu.items[new_item].group_accel = msg_data->group_accel;
593         data->menu.items[new_item].attr = msg_data->attr;
594         parse_menu_str(data->menu.items[new_item].str, msg_data->str,
595                        NHMENU_STR_SIZE);
596         data->menu.items[new_item].presel = msg_data->presel;
597
598         /* calculate tabstop size */
599         p = strchr(data->menu.items[new_item].str, '\t');
600         if (p) {
601             data->menu.items[new_item].has_tab = TRUE;
602             hDC = GetDC(hWnd);
603             saveFont = SelectObject(
604                 hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE));
605             p1 = data->menu.items[new_item].str;
606             column = 0;
607             for (;;) {
608                 TCHAR wbuf[BUFSZ];
609                 RECT drawRect;
610                 SetRect(&drawRect, 0, 0, 1, 1);
611                 if (p != NULL)
612                     *p = '\0'; /* for time being, view tab field as zstring */
613                 DrawText(hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1), &drawRect,
614                          DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS
615                              | DT_SINGLELINE);
616                 data->menu.tab_stop_size[column] =
617                     max(data->menu.tab_stop_size[column],
618                         drawRect.right - drawRect.left);
619                 if (p != NULL)
620                     *p = '\t';
621                 else /* last string so, */
622                     break;
623
624                 ++column;
625                 p1 = p + 1;
626                 p = strchr(p1, '\t');
627             }
628             SelectObject(hDC, saveFont);
629             ReleaseDC(hWnd, hDC);
630         } else {
631             data->menu.items[new_item].has_tab = FALSE;
632         }
633
634         /* increment size */
635         data->menu.size++;
636     } break;
637
638     case MSNH_MSG_ENDMENU: {
639         PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu) lParam;
640         if (msg_data->text) {
641             strncpy(data->menu.prompt, msg_data->text,
642                     sizeof(data->menu.prompt) - 1);
643         } else {
644             ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
645         }
646     } break;
647
648     } /* end switch */
649 }
650
651 void
652 LayoutMenu(HWND hWnd)
653 {
654     PNHMenuWindow data;
655     HWND menu_ok;
656     HWND menu_cancel;
657     RECT clrt, rt;
658     POINT pt_elem, pt_ok, pt_cancel;
659     SIZE sz_elem, sz_ok, sz_cancel;
660
661     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
662     menu_ok = GetDlgItem(hWnd, IDOK);
663     menu_cancel = GetDlgItem(hWnd, IDCANCEL);
664
665     /* get window coordinates */
666     GetClientRect(hWnd, &clrt);
667
668     /* set window placements */
669     if (IsWindow(menu_ok)) {
670         GetWindowRect(menu_ok, &rt);
671         sz_ok.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
672         sz_ok.cy = rt.bottom - rt.top;
673         pt_ok.x = clrt.left + MENU_MARGIN;
674         pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy;
675         MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE);
676     } else {
677         pt_ok.x = 0;
678         pt_ok.y = clrt.bottom;
679         sz_ok.cx = sz_ok.cy = 0;
680     }
681
682     if (IsWindow(menu_cancel)) {
683         GetWindowRect(menu_cancel, &rt);
684         sz_cancel.cx = (clrt.right - clrt.left) / 2 - 2 * MENU_MARGIN;
685         sz_cancel.cy = rt.bottom - rt.top;
686         pt_cancel.x = (clrt.left + clrt.right) / 2 + MENU_MARGIN;
687         pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy;
688         MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx,
689                    sz_cancel.cy, TRUE);
690     } else {
691         pt_cancel.x = 0;
692         pt_cancel.y = clrt.bottom;
693         sz_cancel.cx = sz_cancel.cy = 0;
694     }
695
696     pt_elem.x = clrt.left + MENU_MARGIN;
697     pt_elem.y = clrt.top + MENU_MARGIN;
698     sz_elem.cx = (clrt.right - clrt.left) - 2 * MENU_MARGIN;
699     sz_elem.cy = min(pt_cancel.y, pt_ok.y) - MENU_MARGIN - pt_elem.y;
700     MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx,
701                sz_elem.cy, TRUE);
702
703     /* reformat text for the text menu */
704     if (data && data->type == MENU_TYPE_TEXT && data->text.text)
705         mswin_render_text(data->text.text, GetMenuControl(hWnd));
706 }
707
708 void
709 SetMenuType(HWND hWnd, int type)
710 {
711     PNHMenuWindow data;
712     HWND list, text;
713
714     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
715
716     data->type = type;
717
718     text = GetDlgItem(hWnd, IDC_MENU_TEXT);
719     list = GetDlgItem(hWnd, IDC_MENU_LIST);
720     if (data->type == MENU_TYPE_TEXT) {
721         ShowWindow(list, SW_HIDE);
722         EnableWindow(list, FALSE);
723         EnableWindow(text, TRUE);
724         ShowWindow(text, SW_SHOW);
725         SetFocus(text);
726     } else {
727         ShowWindow(text, SW_HIDE);
728         EnableWindow(text, FALSE);
729         EnableWindow(list, TRUE);
730         ShowWindow(list, SW_SHOW);
731         SetFocus(list);
732     }
733     LayoutMenu(hWnd);
734 }
735
736 void
737 SetMenuListType(HWND hWnd, int how)
738 {
739     PNHMenuWindow data;
740     RECT rt;
741     DWORD dwStyles;
742     char buf[BUFSZ];
743     TCHAR wbuf[BUFSZ];
744     int nItem;
745     int i;
746     HWND control;
747     LVCOLUMN lvcol;
748     LRESULT fnt;
749     SIZE wnd_size;
750
751     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
752     if (data->type != MENU_TYPE_MENU)
753         return;
754
755     data->how = how;
756
757     switch (how) {
758     case PICK_NONE:
759         dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL
760                    | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
761                    | LVS_SINGLESEL;
762         break;
763     case PICK_ONE:
764         dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL
765                    | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
766                    | LVS_SINGLESEL;
767         break;
768     case PICK_ANY:
769         dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL
770                    | WS_HSCROLL | LVS_REPORT | LVS_OWNERDRAWFIXED
771                    | LVS_SINGLESEL;
772         break;
773     default:
774         panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
775     };
776     if (strlen(data->menu.prompt) == 0) {
777         dwStyles |= LVS_NOCOLUMNHEADER;
778     }
779
780     GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
781     DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
782     control = CreateWindow(WC_LISTVIEW, NULL, dwStyles, rt.left, rt.top,
783                            rt.right - rt.left, rt.bottom - rt.top, hWnd,
784                            (HMENU) IDC_MENU_LIST, GetNHApp()->hApp, NULL);
785     if (!control)
786         panic("cannot create menu control");
787
788     /* install the hook for the control window procedure */
789     wndProcListViewOrig = (WNDPROC) GetWindowLong(control, GWL_WNDPROC);
790     SetWindowLong(control, GWL_WNDPROC, (LONG) NHMenuListWndProc);
791
792     /* set control font */
793     fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM) 0, (LPARAM) 0);
794     SendMessage(control, WM_SETFONT, (WPARAM) fnt, (LPARAM) 0);
795
796     /* set control colors */
797     ListView_SetBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
798     ListView_SetTextBkColor(control,
799                             mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
800     ListView_SetTextColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG));
801
802     /* add column to the list view */
803     mswin_menu_window_size(hWnd, &wnd_size);
804
805     ZeroMemory(&lvcol, sizeof(lvcol));
806     lvcol.mask = LVCF_WIDTH | LVCF_TEXT;
807     lvcol.cx = max(wnd_size.cx, GetSystemMetrics(SM_CXSCREEN));
808     lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ);
809     ListView_InsertColumn(control, 0, &lvcol);
810
811     /* add items to the list view */
812     for (i = 0; i < data->menu.size; i++) {
813         LVITEM lvitem;
814         ZeroMemory(&lvitem, sizeof(lvitem));
815         sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '),
816                 data->menu.items[i].str);
817
818         lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
819         lvitem.iItem = i;
820         lvitem.iSubItem = 0;
821         lvitem.state = data->menu.items[i].presel ? LVIS_SELECTED : 0;
822         lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ);
823         lvitem.lParam = (LPARAM) &data->menu.items[i];
824         nItem = SendMessage(control, LB_ADDSTRING, (WPARAM) 0, (LPARAM) buf);
825         if (ListView_InsertItem(control, &lvitem) == -1) {
826             panic("cannot insert menu item");
827         }
828     }
829     SetFocus(control);
830 }
831
832 HWND
833 GetMenuControl(HWND hWnd)
834 {
835     PNHMenuWindow data;
836
837     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
838
839     if (data->type == MENU_TYPE_TEXT) {
840         return GetDlgItem(hWnd, IDC_MENU_TEXT);
841     } else {
842         return GetDlgItem(hWnd, IDC_MENU_LIST);
843     }
844 }
845
846 LRESULT
847 onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
848 {
849     LPMEASUREITEMSTRUCT lpmis;
850     TEXTMETRIC tm;
851     HGDIOBJ saveFont;
852     HDC hdc;
853     PNHMenuWindow data;
854     RECT list_rect;
855
856     lpmis = (LPMEASUREITEMSTRUCT) lParam;
857     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
858     GetClientRect(GetMenuControl(hWnd), &list_rect);
859
860     hdc = GetDC(GetMenuControl(hWnd));
861     saveFont =
862         SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
863     GetTextMetrics(hdc, &tm);
864
865     /* Set the height of the list box items. */
866     lpmis->itemHeight = max(tm.tmHeight, TILE_Y) + 2;
867     lpmis->itemWidth = list_rect.right - list_rect.left;
868
869     SelectObject(hdc, saveFont);
870     ReleaseDC(GetMenuControl(hWnd), hdc);
871     return TRUE;
872 }
873
874 LRESULT
875 onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
876 {
877     LPDRAWITEMSTRUCT lpdis;
878     PNHMenuItem item;
879     PNHMenuWindow data;
880     TEXTMETRIC tm;
881     HGDIOBJ saveFont;
882     HDC tileDC;
883     short ntile;
884     int t_x, t_y;
885     int x, y;
886     TCHAR wbuf[BUFSZ];
887     RECT drawRect;
888     COLORREF OldBg, OldFg, NewBg;
889     char *p, *p1;
890     int column;
891
892     lpdis = (LPDRAWITEMSTRUCT) lParam;
893
894     /* If there are no list box items, skip this message. */
895     if (lpdis->itemID == -1)
896         return FALSE;
897
898     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
899
900     item = &data->menu.items[lpdis->itemID];
901
902     tileDC = CreateCompatibleDC(lpdis->hDC);
903     saveFont = SelectObject(
904         lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE));
905     NewBg = mswin_get_color(NHW_MENU, MSWIN_COLOR_BG);
906     OldBg = SetBkColor(lpdis->hDC, NewBg);
907     OldFg =
908         SetTextColor(lpdis->hDC, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG));
909
910     GetTextMetrics(lpdis->hDC, &tm);
911
912     x = lpdis->rcItem.left + 1;
913
914     /* print check mark if it is a "selectable" menu */
915     if (data->how != PICK_NONE) {
916         if (NHMENU_IS_SELECTABLE(*item)) {
917             HGDIOBJ saveBrush;
918             HBRUSH hbrCheckMark;
919             char buf[2];
920
921             switch (item->count) {
922             case -1:
923                 hbrCheckMark = CreatePatternBrush(data->bmpChecked);
924                 break;
925             case 0:
926                 hbrCheckMark = CreatePatternBrush(data->bmpNotChecked);
927                 break;
928             default:
929                 hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount);
930                 break;
931             }
932
933             y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
934             SetBrushOrgEx(lpdis->hDC, x, y, NULL);
935             saveBrush = SelectObject(lpdis->hDC, hbrCheckMark);
936             PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY);
937             SelectObject(lpdis->hDC, saveBrush);
938             DeleteObject(hbrCheckMark);
939
940             x += TILE_X + 5;
941
942             if (item->accelerator != 0) {
943                 buf[0] = item->accelerator;
944                 buf[1] = '\x0';
945
946                 SetRect(&drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right,
947                         lpdis->rcItem.bottom);
948                 DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect,
949                          DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
950             }
951             x += tm.tmAveCharWidth + tm.tmOverhang + 5;
952         } else {
953             x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10;
954         }
955     }
956
957     /* print glyph if present */
958     if (item->glyph != NO_GLYPH) {
959         HGDIOBJ saveBmp;
960
961         saveBmp = SelectObject(tileDC, GetNHApp()->bmpTiles);
962         ntile = glyph2tile[item->glyph];
963         t_x = (ntile % TILES_PER_LINE) * TILE_X;
964         t_y = (ntile / TILES_PER_LINE) * TILE_Y;
965
966         y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2;
967
968         nhapply_image_transparent(lpdis->hDC, x, y, TILE_X, TILE_Y, tileDC,
969                                   t_x, t_y, TILE_X, TILE_Y, TILE_BK_COLOR);
970         SelectObject(tileDC, saveBmp);
971     }
972
973     x += TILE_X + 5;
974
975     /* draw item text */
976     if (item->has_tab) {
977         p1 = item->str;
978         p = strchr(item->str, '\t');
979         column = 0;
980         SetRect(&drawRect, x, lpdis->rcItem.top,
981                 min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
982                 lpdis->rcItem.bottom);
983         for (;;) {
984             TCHAR wbuf[BUFSZ];
985             if (p != NULL)
986                 *p = '\0'; /* for time being, view tab field as zstring */
987             DrawText(lpdis->hDC, NH_A2W(p1, wbuf, BUFSZ), strlen(p1),
988                      &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
989             if (p != NULL)
990                 *p = '\t';
991             else /* last string so, */
992                 break;
993
994             p1 = p + 1;
995             p = strchr(p1, '\t');
996             drawRect.left = drawRect.right + TAB_SEPARATION;
997             ++column;
998             drawRect.right =
999                 min(drawRect.left + data->menu.tab_stop_size[column],
1000                     lpdis->rcItem.right);
1001         }
1002     } else {
1003         TCHAR wbuf[BUFSZ];
1004         SetRect(&drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right,
1005                 lpdis->rcItem.bottom);
1006         DrawText(lpdis->hDC, NH_A2W(item->str, wbuf, BUFSZ),
1007                  strlen(item->str), &drawRect,
1008                  DT_LEFT | DT_VCENTER | DT_SINGLELINE);
1009     }
1010
1011     /* draw focused item */
1012     if (item->has_focus) {
1013         RECT client_rt;
1014         HBRUSH bkBrush;
1015
1016         GetClientRect(lpdis->hwndItem, &client_rt);
1017         if (NHMENU_IS_SELECTABLE(*item)
1018             && data->menu.items[lpdis->itemID].count > 0
1019             && item->glyph != NO_GLYPH) {
1020             if (data->menu.items[lpdis->itemID].count == -1) {
1021                 _stprintf(wbuf, TEXT("Count: All"));
1022             } else {
1023                 _stprintf(wbuf, TEXT("Count: %d"),
1024                           data->menu.items[lpdis->itemID].count);
1025             }
1026
1027             SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK,
1028                                                     lpdis->hDC, FALSE));
1029
1030             /* calculate text rectangle */
1031             SetRect(&drawRect, client_rt.left, lpdis->rcItem.top,
1032                     client_rt.right, lpdis->rcItem.bottom);
1033             DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1034                      DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE
1035                          | DT_NOPREFIX);
1036
1037             /* erase text rectangle */
1038             drawRect.left =
1039                 max(client_rt.left + 1,
1040                     client_rt.right - (drawRect.right - drawRect.left) - 10);
1041             drawRect.right = client_rt.right - 1;
1042             drawRect.top = lpdis->rcItem.top;
1043             drawRect.bottom = lpdis->rcItem.bottom;
1044             bkBrush = CreateSolidBrush(GetBkColor(lpdis->hDC));
1045             FillRect(lpdis->hDC, &drawRect, bkBrush);
1046             DeleteObject(bkBrush);
1047
1048             /* draw text */
1049             DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect,
1050                      DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
1051         }
1052
1053         /* draw focus rect */
1054         SetRect(&drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right,
1055                 lpdis->rcItem.bottom);
1056         DrawFocusRect(lpdis->hDC, &drawRect);
1057     }
1058
1059     SetTextColor(lpdis->hDC, OldFg);
1060     SetBkColor(lpdis->hDC, OldBg);
1061     SelectObject(lpdis->hDC, saveFont);
1062     DeleteDC(tileDC);
1063     return TRUE;
1064 }
1065
1066 BOOL
1067 onListChar(HWND hWnd, HWND hwndList, WORD ch)
1068 {
1069     int i = 0;
1070     PNHMenuWindow data;
1071     int curIndex, topIndex, pageSize;
1072     boolean is_accelerator = FALSE;
1073
1074     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
1075
1076     switch (ch) {
1077     case MENU_FIRST_PAGE:
1078         i = 0;
1079         ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1080         ListView_EnsureVisible(hwndList, i, FALSE);
1081         return -2;
1082
1083     case MENU_LAST_PAGE:
1084         i = max(0, data->menu.size - 1);
1085         ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1086         ListView_EnsureVisible(hwndList, i, FALSE);
1087         return -2;
1088
1089     case MENU_NEXT_PAGE:
1090         topIndex = ListView_GetTopIndex(hwndList);
1091         pageSize = ListView_GetCountPerPage(hwndList);
1092         curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1093         /* Focus down one page */
1094         i = min(curIndex + pageSize, data->menu.size - 1);
1095         ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1096         /* Scrollpos down one page */
1097         i = min(topIndex + (2 * pageSize - 1), data->menu.size - 1);
1098         ListView_EnsureVisible(hwndList, i, FALSE);
1099         return -2;
1100
1101     case MENU_PREVIOUS_PAGE:
1102         topIndex = ListView_GetTopIndex(hwndList);
1103         pageSize = ListView_GetCountPerPage(hwndList);
1104         curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1105         /* Focus up one page */
1106         i = max(curIndex - pageSize, 0);
1107         ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
1108         /* Scrollpos up one page */
1109         i = max(topIndex - pageSize, 0);
1110         ListView_EnsureVisible(hwndList, i, FALSE);
1111         break;
1112
1113     case MENU_SELECT_ALL:
1114         if (data->how == PICK_ANY) {
1115             reset_menu_count(hwndList, data);
1116             for (i = 0; i < data->menu.size; i++) {
1117                 SelectMenuItem(hwndList, data, i, -1);
1118             }
1119             return -2;
1120         }
1121         break;
1122
1123     case MENU_UNSELECT_ALL:
1124         if (data->how == PICK_ANY) {
1125             reset_menu_count(hwndList, data);
1126             for (i = 0; i < data->menu.size; i++) {
1127                 SelectMenuItem(hwndList, data, i, 0);
1128             }
1129             return -2;
1130         }
1131         break;
1132
1133     case MENU_INVERT_ALL:
1134         if (data->how == PICK_ANY) {
1135             reset_menu_count(hwndList, data);
1136             for (i = 0; i < data->menu.size; i++) {
1137                 SelectMenuItem(hwndList, data, i,
1138                                NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1139                                                                        : -1);
1140             }
1141             return -2;
1142         }
1143         break;
1144
1145     case MENU_SELECT_PAGE:
1146         if (data->how == PICK_ANY) {
1147             int from, to;
1148             reset_menu_count(hwndList, data);
1149             topIndex = ListView_GetTopIndex(hwndList);
1150             pageSize = ListView_GetCountPerPage(hwndList);
1151             from = max(0, topIndex);
1152             to = min(data->menu.size, from + pageSize);
1153             for (i = from; i < to; i++) {
1154                 SelectMenuItem(hwndList, data, i, -1);
1155             }
1156             return -2;
1157         }
1158         break;
1159
1160     case MENU_UNSELECT_PAGE:
1161         if (data->how == PICK_ANY) {
1162             int from, to;
1163             reset_menu_count(hwndList, data);
1164             topIndex = ListView_GetTopIndex(hwndList);
1165             pageSize = ListView_GetCountPerPage(hwndList);
1166             from = max(0, topIndex);
1167             to = min(data->menu.size, from + pageSize);
1168             for (i = from; i < to; i++) {
1169                 SelectMenuItem(hwndList, data, i, 0);
1170             }
1171             return -2;
1172         }
1173         break;
1174
1175     case MENU_INVERT_PAGE:
1176         if (data->how == PICK_ANY) {
1177             int from, to;
1178             reset_menu_count(hwndList, data);
1179             topIndex = ListView_GetTopIndex(hwndList);
1180             pageSize = ListView_GetCountPerPage(hwndList);
1181             from = max(0, topIndex);
1182             to = min(data->menu.size, from + pageSize);
1183             for (i = from; i < to; i++) {
1184                 SelectMenuItem(hwndList, data, i,
1185                                NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1186                                                                        : -1);
1187             }
1188             return -2;
1189         }
1190         break;
1191
1192     case MENU_SEARCH:
1193         if (data->how == PICK_ANY || data->how == PICK_ONE) {
1194             char buf[BUFSZ];
1195             int selected_item;
1196
1197             reset_menu_count(hwndList, data);
1198             mswin_getlin("Search for:", buf);
1199             if (!*buf || *buf == '\033')
1200                 return -2;
1201             selected_item = -1;
1202             for (i = 0; i < data->menu.size; i++) {
1203                 if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1204                     && strstr(data->menu.items[i].str, buf)) {
1205                     if (data->how == PICK_ANY) {
1206                         SelectMenuItem(
1207                             hwndList, data, i,
1208                             NHMENU_IS_SELECTED(data->menu.items[i]) ? 0 : -1);
1209                         /* save the first item - we will move focus to it */
1210                         if (selected_item == -1)
1211                             selected_item = i;
1212                     } else if (data->how == PICK_ONE) {
1213                         SelectMenuItem(hwndList, data, i, -1);
1214                         selected_item = i;
1215                         break;
1216                     }
1217                 }
1218             }
1219
1220             if (selected_item > 0) {
1221                 ListView_SetItemState(hwndList, selected_item, LVIS_FOCUSED,
1222                                       LVIS_FOCUSED);
1223                 ListView_EnsureVisible(hwndList, selected_item, FALSE);
1224             }
1225         } else {
1226             mswin_nhbell();
1227         }
1228         return -2;
1229
1230     case ' ':
1231         /* ends menu for PICK_ONE/PICK_NONE
1232            select item for PICK_ANY */
1233         if (data->how == PICK_ONE || data->how == PICK_NONE) {
1234             data->done = 1;
1235             data->result = 0;
1236             return -2;
1237         } else if (data->how == PICK_ANY) {
1238             i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1239             if (i >= 0) {
1240                 SelectMenuItem(hwndList, data, i,
1241                                NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1242                                                                        : -1);
1243             }
1244             return -2;
1245         }
1246         break;
1247
1248     default:
1249         if (strchr(data->menu.gacc, ch)
1250             && !(ch == '0' && data->menu.counting)) {
1251             /* matched a group accelerator */
1252             if (data->how == PICK_ANY || data->how == PICK_ONE) {
1253                 reset_menu_count(hwndList, data);
1254                 for (i = 0; i < data->menu.size; i++) {
1255                     if (NHMENU_IS_SELECTABLE(data->menu.items[i])
1256                         && data->menu.items[i].group_accel == ch) {
1257                         if (data->how == PICK_ANY) {
1258                             SelectMenuItem(
1259                                 hwndList, data, i,
1260                                 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1261                                                                         : -1);
1262                         } else if (data->how == PICK_ONE) {
1263                             SelectMenuItem(hwndList, data, i, -1);
1264                             data->result = 0;
1265                             data->done = 1;
1266                             return -2;
1267                         }
1268                     }
1269                 }
1270                 return -2;
1271             } else {
1272                 mswin_nhbell();
1273                 return -2;
1274             }
1275         }
1276
1277         if (isdigit(ch)) {
1278             int count;
1279             i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED);
1280             if (i >= 0) {
1281                 count = data->menu.items[i].count;
1282                 if (count == -1)
1283                     count = 0;
1284                 count *= 10L;
1285                 count += (int) (ch - '0');
1286                 if (count != 0) /* ignore leading zeros */ {
1287                     data->menu.counting = TRUE;
1288                     data->menu.items[i].count = min(100000, count);
1289                     ListView_RedrawItems(hwndList, i,
1290                                          i); /* update count mark */
1291                 }
1292             }
1293             return -2;
1294         }
1295
1296         is_accelerator = FALSE;
1297         for (i = 0; i < data->menu.size; i++) {
1298             if (data->menu.items[i].accelerator == ch) {
1299                 is_accelerator = TRUE;
1300                 break;
1301             }
1302         }
1303
1304         if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
1305             || is_accelerator) {
1306             if (data->how == PICK_ANY || data->how == PICK_ONE) {
1307                 for (i = 0; i < data->menu.size; i++) {
1308                     if (data->menu.items[i].accelerator == ch) {
1309                         if (data->how == PICK_ANY) {
1310                             SelectMenuItem(
1311                                 hwndList, data, i,
1312                                 NHMENU_IS_SELECTED(data->menu.items[i]) ? 0
1313                                                                         : -1);
1314                             ListView_SetItemState(hwndList, i, LVIS_FOCUSED,
1315                                                   LVIS_FOCUSED);
1316                             ListView_EnsureVisible(hwndList, i, FALSE);
1317                             return -2;
1318                         } else if (data->how == PICK_ONE) {
1319                             SelectMenuItem(hwndList, data, i, -1);
1320                             data->result = 0;
1321                             data->done = 1;
1322                             return -2;
1323                         }
1324                     }
1325                 }
1326             }
1327         }
1328         break;
1329     }
1330
1331     reset_menu_count(hwndList, data);
1332     return -1;
1333 }
1334
1335 void
1336 mswin_menu_window_size(HWND hWnd, LPSIZE sz)
1337 {
1338     TEXTMETRIC tm;
1339     HWND control;
1340     HGDIOBJ saveFont;
1341     HDC hdc;
1342     PNHMenuWindow data;
1343     int i;
1344     RECT rt, wrt;
1345     int extra_cx;
1346
1347     GetClientRect(hWnd, &rt);
1348     sz->cx = rt.right - rt.left;
1349     sz->cy = rt.bottom - rt.top;
1350
1351     GetWindowRect(hWnd, &wrt);
1352     extra_cx = (wrt.right - wrt.left) - sz->cx;
1353
1354     data = (PNHMenuWindow) GetWindowLong(hWnd, GWL_USERDATA);
1355     if (data) {
1356         control = GetMenuControl(hWnd);
1357         hdc = GetDC(control);
1358
1359         if (data->type == MENU_TYPE_MENU) {
1360             /* Calculate the width of the list box. */
1361             saveFont = SelectObject(
1362                 hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE));
1363             GetTextMetrics(hdc, &tm);
1364             for (i = 0; i < data->menu.size; i++) {
1365                 LONG menuitemwidth = 0;
1366                 int column;
1367                 char *p, *p1;
1368
1369                 p1 = data->menu.items[i].str;
1370                 p = strchr(data->menu.items[i].str, '\t');
1371                 column = 0;
1372                 for (;;) {
1373                     TCHAR wbuf[BUFSZ];
1374                     RECT tabRect;
1375                     SetRect(&tabRect, 0, 0, 1, 1);
1376                     if (p != NULL)
1377                         *p = '\0'; /* for time being, view tab field as
1378                                       zstring */
1379                     DrawText(hdc, NH_A2W(p1, wbuf, BUFSZ), strlen(p1),
1380                              &tabRect, DT_CALCRECT | DT_LEFT | DT_VCENTER
1381                                            | DT_SINGLELINE);
1382                     /* it probably isn't necessary to recompute the tab width
1383                      * now, but do so
1384                      * just in case, honoring the previously computed value
1385                      */
1386                     menuitemwidth += max(data->menu.tab_stop_size[column],
1387                                          tabRect.right - tabRect.left);
1388                     if (p != NULL)
1389                         *p = '\t';
1390                     else /* last string so, */
1391                         break;
1392                     /* add the separation only when not the last item */
1393                     /* in the last item, we break out of the loop, in the
1394                      * statement just above */
1395                     menuitemwidth += TAB_SEPARATION;
1396                     ++column;
1397                     p1 = p + 1;
1398                     p = strchr(p1, '\t');
1399                 }
1400
1401                 sz->cx = max(sz->cx, (LONG)(2 * TILE_X + menuitemwidth
1402                                             + tm.tmAveCharWidth * 12
1403                                             + tm.tmOverhang));
1404             }
1405             SelectObject(hdc, saveFont);
1406         } else {
1407             /* do not change size for text output - the text will be formatted
1408                to
1409                fit any window */
1410         }
1411         sz->cx += extra_cx;
1412
1413         ReleaseDC(control, hdc);
1414     }
1415 }
1416
1417 void
1418 SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
1419 {
1420     int i;
1421
1422     if (item < 0 || item >= data->menu.size)
1423         return;
1424
1425     if (data->how == PICK_ONE && count != 0) {
1426         for (i = 0; i < data->menu.size; i++)
1427             if (item != i && data->menu.items[i].count != 0) {
1428                 data->menu.items[i].count = 0;
1429                 ListView_RedrawItems(hwndList, i, i);
1430             };
1431     }
1432
1433     data->menu.items[item].count = count;
1434     ListView_RedrawItems(hwndList, item, item);
1435     reset_menu_count(hwndList, data);
1436 }
1437
1438 void
1439 reset_menu_count(HWND hwndList, PNHMenuWindow data)
1440 {
1441     int i;
1442     data->menu.counting = FALSE;
1443     if (IsWindow(hwndList)) {
1444         i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
1445         if (i >= 0)
1446             ListView_RedrawItems(hwndList, i, i);
1447     }
1448 }
1449
1450 /* List window Proc */
1451 LRESULT CALLBACK
1452 NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1453 {
1454     BOOL bUpdateFocusItem = FALSE;
1455
1456     switch (message) {
1457 /* filter keyboard input for the control */
1458 #if !defined(WIN_CE_SMARTPHONE)
1459     case WM_KEYDOWN:
1460     case WM_KEYUP: {
1461         MSG msg;
1462
1463         if (PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE)) {
1464             if (onListChar(GetParent(hWnd), hWnd, (char) msg.wParam) == -2) {
1465                 return 0;
1466             }
1467         }
1468
1469         if (wParam == VK_LEFT || wParam == VK_RIGHT)
1470             bUpdateFocusItem = TRUE;
1471     } break;
1472
1473     /* tell Windows not to process arrow keys */
1474     case WM_GETDLGCODE:
1475         return DLGC_WANTARROWS;
1476
1477 #else /* defined(WIN_CE_SMARTPHONE) */
1478     case WM_KEYDOWN:
1479         if (wParam == VK_TACTION) {
1480             if (onListChar(GetParent(hWnd), hWnd, ' ') == -2) {
1481                 return 0;
1482             }
1483         } else if (NHSPhoneTranslateKbdMessage(wParam, lParam, TRUE)) {
1484             PMSNHEvent evt;
1485             BOOL processed = FALSE;
1486             if (mswin_have_input()) {
1487                 evt = mswin_input_pop();
1488                 if (evt->type == NHEVENT_CHAR
1489                     && onListChar(GetParent(hWnd), hWnd, evt->kbd.ch) == -2) {
1490                     processed = TRUE;
1491                 }
1492
1493                 /* eat the rest of the events */
1494                 if (mswin_have_input())
1495                     mswin_input_pop();
1496             }
1497             if (processed)
1498                 return 0;
1499         }
1500
1501         if (wParam == VK_LEFT || wParam == VK_RIGHT)
1502             bUpdateFocusItem = TRUE;
1503         break;
1504
1505     case WM_KEYUP:
1506         /* translate SmartPhone keyboard message */
1507         if (NHSPhoneTranslateKbdMessage(wParam, lParam, FALSE))
1508             return 0;
1509         break;
1510
1511     /* tell Windows not to process default button on VK_RETURN */
1512     case WM_GETDLGCODE:
1513         return DLGC_DEFPUSHBUTTON | DLGC_WANTALLKEYS
1514                | (wndProcListViewOrig
1515                       ? CallWindowProc(wndProcListViewOrig, hWnd, message,
1516                                        wParam, lParam)
1517                       : 0);
1518 #endif
1519
1520     case WM_SIZE:
1521     case WM_HSCROLL:
1522         bUpdateFocusItem = TRUE;
1523         break;
1524     }
1525
1526     if (bUpdateFocusItem) {
1527         int i;
1528         RECT rt;
1529
1530         /* invalidate the focus rectangle */
1531         i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED);
1532         if (i != -1) {
1533             ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
1534             InvalidateRect(hWnd, &rt, TRUE);
1535         }
1536     }
1537
1538     if (wndProcListViewOrig)
1539         return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam,
1540                               lParam);
1541     else
1542         return 0;
1543 }
1544
1545 /* Text control window proc - implements close on space */
1546 LRESULT CALLBACK
1547 NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
1548 {
1549     switch (message) {
1550     case WM_KEYUP:
1551         switch (wParam) {
1552         case VK_SPACE:
1553         case VK_RETURN:
1554             /* close on space */
1555             PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0);
1556             return 0;
1557
1558         case VK_UP:
1559             /* scoll up */
1560             PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0),
1561                         (LPARAM) NULL);
1562             return 0;
1563
1564         case VK_DOWN:
1565             /* scoll down */
1566             PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0),
1567                         (LPARAM) NULL);
1568             return 0;
1569
1570         case VK_LEFT:
1571             /* scoll left */
1572             PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, 0),
1573                         (LPARAM) NULL);
1574             return 0;
1575
1576         case VK_RIGHT:
1577             /* scoll right */
1578             PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, 0),
1579                         (LPARAM) NULL);
1580             return 0;
1581         }
1582         break; /* case WM_KEYUP: */
1583     }
1584
1585     if (editControlWndProc)
1586         return CallWindowProc(editControlWndProc, hWnd, message, wParam,
1587                               lParam);
1588     else
1589         return 0;
1590 }
1591 /*----------------------------------------------------------------------------*/
1592 char *
1593 parse_menu_str(char *dest, const char *src, size_t size)
1594 {
1595     char *p1, *p2;
1596     if (!dest || size == 0)
1597         return NULL;
1598
1599     strncpy(dest, src, size);
1600     dest[size - 1] = '\x0';
1601
1602     /* replace "[ ]*\[" with "\t\[" */
1603     p1 = p2 = strstr(dest, " [");
1604     if (p1) {
1605         while (p1 != dest && *p1 == ' ')
1606             p1--;
1607         p1++; /* backup to space */
1608         *p2 = '\t';
1609         memmove(p1, p2, strlen(p2));
1610         p1[strlen(p2)] = '\x0';
1611     }
1612     return dest;
1613 }