1 /****************************************************************************
2  * Copyright 2019-2020,2021 Thomas E. Dickey                                *
3  * Copyright 2003-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 /*
30  * $Id: demo_menus.c,v 1.73 2021/05/08 19:41:08 tom Exp $
31  *
32  * Demonstrate a variety of functions from the menu library.
33  * Thomas Dickey - 2005/4/9
34  */
35 /*
36 item_description		-
37 item_init			-
38 item_opts			-
39 item_opts_off			-
40 item_opts_on			-
41 item_term			-
42 item_visible			-
43 menu_back			-
44 menu_fore			-
45 menu_format			-
46 menu_grey			-
47 menu_init			-
48 menu_opts			-
49 menu_pad			-
50 menu_request_by_name		-
51 menu_request_name		-
52 menu_term			-
53 menu_userptr			-
54 set_current_item		-
55 set_item_opts			-
56 set_menu_grey			-
57 set_menu_items			-
58 set_menu_opts			-
59 set_menu_pad			-
60 set_menu_pattern		-
61 set_menu_spacing		-
62 set_menu_userptr		-
63 set_top_row			-
64 top_row				-
65 */
66 
67 #include <test.priv.h>
68 
69 #if USE_LIBMENU
70 
71 #include <menu.h>
72 
73 #include <sys/types.h>
74 #include <sys/stat.h>
75 
76 #ifdef NCURSES_VERSION
77 #ifdef TRACE
78 static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS;
79 static MENU *mpTrace;
80 #endif
81 #endif
82 
83 typedef enum {
84     eBanner = -1
85     ,eFile
86     ,eSelect
87 #ifdef TRACE
88     ,eTrace
89 #endif
90     ,eMAX
91 } MenuNo;
92 
93 #define okMenuNo(n) (((n) > eBanner) && ((n) < eMAX))
94 
95 #define MENU_Y	1
96 
97 typedef struct {
98     NCURSES_CONST char *name;
99     void (*func) (int);
100     unsigned mask;
101 } MENU_DATA;
102 
103 static void call_files(int);
104 
105 static MENU *mpBanner;
106 static MENU *mpFile;
107 static MENU *mpSelect;
108 
109 static WINDOW *status;
110 
111 static bool loaded_file = FALSE;
112 
113 static char empty[1];
114 
115 #ifdef TRACE
116 static GCC_NORETURN void failed(const char *s);
117 
118 static void
failed(const char * s)119 failed(const char *s)
120 {
121     perror(s);
122     endwin();
123     ExitProgram(EXIT_FAILURE);
124 }
125 #endif
126 
127 /* Common function to allow ^T to toggle trace-mode in the middle of a test
128  * so that trace-files can be made smaller.
129  */
130 static int
wGetchar(WINDOW * win)131 wGetchar(WINDOW *win)
132 {
133     int c;
134 #ifdef TRACE
135     while ((c = wgetch(win)) == CTRL('T')) {
136 	if (_nc_tracing) {
137 	    save_trace = _nc_tracing;
138 	    Trace(("TOGGLE-TRACING OFF"));
139 	    _nc_tracing = 0;
140 	} else {
141 	    _nc_tracing = save_trace;
142 	}
143 	curses_trace(_nc_tracing);
144 	if (_nc_tracing)
145 	    Trace(("TOGGLE-TRACING ON"));
146     }
147 #else
148     c = wgetch(win);
149 #endif
150     return c;
151 }
152 #define Getchar() wGetchar(stdscr)
153 
154 static int
menu_virtualize(int c)155 menu_virtualize(int c)
156 {
157     int result;
158 
159     if (c == '\n' || c == KEY_EXIT)
160 	result = (MAX_COMMAND + 1);
161     else if (c == 'u')
162 	result = (REQ_SCR_ULINE);
163     else if (c == 'd')
164 	result = (REQ_SCR_DLINE);
165     else if (c == 'b' || c == KEY_NPAGE)
166 	result = (REQ_SCR_UPAGE);
167     else if (c == 'f' || c == KEY_PPAGE)
168 	result = (REQ_SCR_DPAGE);
169     else if (c == 'l' || c == KEY_LEFT || c == KEY_BTAB)
170 	result = (REQ_LEFT_ITEM);
171     else if (c == 'n' || c == KEY_DOWN)
172 	result = (REQ_NEXT_ITEM);
173     else if (c == 'p' || c == KEY_UP)
174 	result = (REQ_PREV_ITEM);
175     else if (c == 'r' || c == KEY_RIGHT || c == '\t')
176 	result = (REQ_RIGHT_ITEM);
177     else if (c == ' ')
178 	result = (REQ_TOGGLE_ITEM);
179     else {
180 	if (c != KEY_MOUSE)
181 	    beep();
182 	result = (c);
183     }
184     return result;
185 }
186 
187 static int
menu_getc(MENU * m)188 menu_getc(MENU * m)
189 {
190     return wGetchar(menu_win(m));
191 }
192 
193 static int
menu_offset(MenuNo number)194 menu_offset(MenuNo number)
195 {
196     int result = 0;
197 
198     if (okMenuNo(number)) {
199 	int spc_rows;
200 #ifdef NCURSES_VERSION
201 	int spc_desc, spc_cols;
202 	menu_spacing(mpBanner, &spc_desc, &spc_rows, &spc_cols);
203 #else
204 	spc_rows = 0;
205 #endif
206 
207 	/* FIXME: MENU.itemlen seems the only way to get actual width of items */
208 	result = (number - (eBanner + 1)) * (menu_itemwidth(mpBanner) + spc_rows);
209     }
210     return result;
211 }
212 
213 static void
my_menu_init(MENU * menu)214 my_menu_init(MENU * menu)
215 {
216     Trace(("called MenuHook my_menu_init"));
217     mvwprintw(status, 2, 0, "menu_init %p", (void *) menu);
218     wclrtoeol(status);
219     wrefresh(status);
220 }
221 
222 static void
my_menu_term(MENU * menu)223 my_menu_term(MENU * menu)
224 {
225     Trace(("called MenuHook my_menu_term"));
226     mvwprintw(status, 2, 0, "menu_term %p", (void *) menu);
227     wclrtoeol(status);
228     wrefresh(status);
229 }
230 
231 static void
my_item_init(MENU * menu)232 my_item_init(MENU * menu)
233 {
234     ITEM *item = current_item(menu);
235     const char *name = item_name(item);
236 
237     Trace(("called MenuHook my_item_init (%s)", name));
238     mvwprintw(status, 2, 0, "item_init %s", name);
239     wclrtoeol(status);
240     wrefresh(status);
241 }
242 
243 static void
my_item_term(MENU * menu)244 my_item_term(MENU * menu)
245 {
246     ITEM *item = current_item(menu);
247     const char *name = item_name(item);
248 
249     Trace(("called MenuHook my_item_term (%s)", name));
250     mvwprintw(status, 2, 0, "item_term %s", name);
251     wclrtoeol(status);
252     wrefresh(status);
253 }
254 
255 static MENU *
menu_create(ITEM ** items,int count,int ncols,MenuNo number)256 menu_create(ITEM ** items, int count, int ncols, MenuNo number)
257 {
258     MENU *result;
259     WINDOW *menuwin;
260     int mrows, mcols;
261     int y = okMenuNo(number) ? MENU_Y : 0;
262     int x = menu_offset(number);
263     int margin = (y == MENU_Y) ? 1 : 0;
264     int maxcol = (ncols + x) < COLS ? ncols : (COLS - x - 1);
265     int maxrow = (count + 1) / ncols;
266 
267     if ((maxrow + y) >= (LINES - 4))
268 	maxrow = LINES - 4 - y;
269 
270     result = new_menu(items);
271 
272     if (has_colors()) {
273 	set_menu_fore(result, (chtype) COLOR_PAIR(1));
274 	set_menu_back(result, (chtype) COLOR_PAIR(2));
275     }
276 
277     set_menu_format(result, maxrow, maxcol);
278     scale_menu(result, &mrows, &mcols);
279 
280     if (mcols + (2 * margin + x) >= COLS)
281 	mcols = COLS - (2 * margin + x);
282 
283     menuwin = newwin(mrows + (2 * margin), mcols + (2 * margin), y, x);
284     set_menu_win(result, menuwin);
285     keypad(menuwin, TRUE);
286     if (margin)
287 	box(menuwin, 0, 0);
288 
289     set_menu_sub(result, derwin(menuwin, mrows, mcols, margin, margin));
290 
291 #ifdef TRACE
292     if (number == eTrace)
293 	menu_opts_off(result, O_ONEVALUE);
294     else
295 	menu_opts_on(result, O_ONEVALUE);
296 #endif
297 #if defined(NCURSES_MOUSE_VERSION) && defined(O_MOUSE_MENU)
298     menu_opts_on(result, O_MOUSE_MENU);
299 #endif
300 
301     post_menu(result);
302 
303     set_menu_init(result, my_menu_init);
304     set_menu_term(result, my_menu_term);
305     set_item_init(result, my_item_init);
306     set_item_term(result, my_item_term);
307     return result;
308 }
309 
310 static void
menu_destroy(MENU * m)311 menu_destroy(MENU * m)
312 {
313     Trace(("menu_destroy %p", (void *) m));
314     if (m != 0) {
315 	ITEM **items = menu_items(m);
316 	const char *blob = 0;
317 	int count;
318 
319 	count = item_count(m);
320 	Trace(("menu_destroy %p count %d", (void *) m, count));
321 	if ((count > 0) && (m == mpSelect)) {
322 	    blob = item_name(*items);
323 	}
324 
325 	unpost_menu(m);
326 	free_menu(m);
327 
328 	/* free the extra data allocated in build_select_menu() */
329 	if ((count > 0) && (m == mpSelect)) {
330 	    if (blob && loaded_file) {
331 		Trace(("freeing blob %p", blob));
332 		free((void *) blob);
333 	    }
334 	    free(items);
335 	    items = 0;
336 	}
337 #ifdef TRACE
338 	if ((count > 0) && (m == mpTrace)) {
339 	    ITEM **ip = items;
340 	    if (ip != 0) {
341 		while (*ip)
342 		    free(*ip++);
343 	    }
344 	}
345 #endif
346     }
347 }
348 
349 /* force the given menu to appear */
350 static void
menu_display(MENU * m)351 menu_display(MENU * m)
352 {
353     touchwin(menu_win(m));
354     wrefresh(menu_win(m));
355 }
356 
357 /*****************************************************************************/
358 
359 static void
build_file_menu(MenuNo number)360 build_file_menu(MenuNo number)
361 {
362     static MENU_DATA table[] =
363     {
364 	{"Exit", call_files, 0},
365 	{(char *) 0, 0, 0}
366     };
367     static ITEM *items[SIZEOF(table)];
368 
369     ITEM **ip = items;
370     int n;
371 
372     for (n = 0; table[n].name != 0; ++n) {
373 	*ip = new_item(table[n].name, empty);
374 	set_item_userptr(*ip, (void *) &table[n]);
375 	++ip;
376     }
377     *ip = (ITEM *) 0;
378 
379     mpFile = menu_create(items, SIZEOF(table) - 1, 1, number);
380 }
381 
382 static int
perform_file_menu(int cmd)383 perform_file_menu(int cmd)
384 {
385     return menu_driver(mpFile, cmd);
386 }
387 
388 /*****************************************************************************/
389 
390 static void
call_select(int code)391 call_select(int code)
392 {
393     (void) code;
394     Trace(("Selected item %d", code));
395 }
396 
397 static void
build_select_menu(MenuNo number,char * filename)398 build_select_menu(MenuNo number, char *filename)
399 {
400 #define MY_DATA(name) { name, call_select, 0 }
401     static MENU_DATA table[] =
402     {
403 	MY_DATA("Lions"),
404 	MY_DATA("Tigers"),
405 	MY_DATA("Bears"),
406 	MY_DATA("(Oh my!)"),
407 	MY_DATA("Newts"),
408 	MY_DATA("Platypi"),
409 	MY_DATA("Lemurs"),
410 	MY_DATA("(Oh really?!)"),
411 	MY_DATA("Leopards"),
412 	MY_DATA("Panthers"),
413 	MY_DATA("Pumas"),
414 	MY_DATA("Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs"),
415 	MY_DATA("Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs"),
416 	{(char *) 0, 0, 0}
417     };
418     static ITEM **items;
419 
420     ITEM **ip;
421     MENU_DATA *ap = 0;
422     MENU_DATA *myList = 0;
423     int i;
424     size_t count = 0;
425 
426     if (filename != 0) {
427 	struct stat sb;
428 	if (stat(filename, &sb) == 0
429 	    && (sb.st_mode & S_IFMT) == S_IFREG
430 	    && sb.st_size != 0) {
431 	    size_t size = (size_t) sb.st_size;
432 	    char *blob = typeMalloc(char, size + 1);
433 	    MENU_DATA *list = typeCalloc(MENU_DATA, size + 1);
434 
435 	    items = typeCalloc(ITEM *, size + 1);
436 	    Trace(("build_select_menu blob=%p, items=%p",
437 		   (void *) blob,
438 		   (void *) items));
439 	    if (blob != 0 && list != 0) {
440 		FILE *fp = fopen(filename, "r");
441 		if (fp != 0) {
442 		    if (fread(blob, sizeof(char), size, fp) == size) {
443 			bool mark = TRUE;
444 			unsigned j, k;
445 			for (j = k = 0; j < size; ++j) {
446 			    if (mark) {
447 				list[k++].name = blob + j;
448 				mark = FALSE;
449 			    }
450 			    if (blob[j] == '\n') {
451 				blob[j] = '\0';
452 				if (k > 0 && *list[k - 1].name == '\0')
453 				    --k;
454 				mark = TRUE;
455 			    } else if (blob[j] == '\t') {
456 				blob[j] = ' ';	/* menu items are printable */
457 			    }
458 			}
459 			list[k].name = 0;
460 			count = k;
461 			ap = myList = list;
462 		    }
463 		    fclose(fp);
464 		}
465 		loaded_file = TRUE;
466 	    }
467 	    if (ap == 0)
468 		free(items);
469 	}
470     }
471     if (ap == 0) {
472 	count = SIZEOF(table) - 1;
473 	items = typeCalloc(ITEM *, count + 1);
474 	ap = table;
475     }
476 
477     ip = items;
478     for (i = 0; ap[i].name != 0; ++i) {
479 	ap[i].func = call_select;
480 	ap[i].mask = (unsigned) i;
481 	*ip = new_item(ap[i].name, empty);
482 	set_item_userptr(*ip, (void *) &table[i]);
483 	++ip;
484     }
485     *ip = 0;
486 
487     mpSelect = menu_create(items, (int) count, 1, number);
488     if (myList != 0)
489 	free(myList);
490 }
491 
492 static int
perform_select_menu(int cmd)493 perform_select_menu(int cmd)
494 {
495     return menu_driver(mpSelect, cmd);
496 }
497 
498 /*****************************************************************************/
499 
500 #ifdef TRACE
501 
502 static void
call_trace(int code)503 call_trace(int code)
504 {
505     (void) code;
506     Trace(("Updating trace mask %d", code));
507 }
508 
509 #define T_TBL(name) { #name, call_trace, name }
510 static MENU_DATA t_tbl[] =
511 {
512 
513     T_TBL(TRACE_DISABLE),
514     T_TBL(TRACE_TIMES),
515     T_TBL(TRACE_TPUTS),
516     T_TBL(TRACE_UPDATE),
517     T_TBL(TRACE_MOVE),
518     T_TBL(TRACE_CHARPUT),
519     T_TBL(TRACE_ORDINARY),
520     T_TBL(TRACE_CALLS),
521     T_TBL(TRACE_VIRTPUT),
522     T_TBL(TRACE_IEVENT),
523     T_TBL(TRACE_BITS),
524     T_TBL(TRACE_ICALLS),
525     T_TBL(TRACE_CCALLS),
526     T_TBL(TRACE_DATABASE),
527     T_TBL(TRACE_ATTRS),
528     T_TBL(TRACE_MAXIMUM),
529     {
530 	(char *) 0, 0, 0
531     }
532 };
533 
534 static void
build_trace_menu(MenuNo number)535 build_trace_menu(MenuNo number)
536 {
537     static ITEM *items[SIZEOF(t_tbl)];
538 
539     ITEM **ip = items;
540     int n;
541 
542     for (n = 0; t_tbl[n].name != 0; n++) {
543 	*ip = new_item(t_tbl[n].name, empty);
544 	set_item_userptr(*ip, (void *) &t_tbl[n]);
545 	++ip;
546     }
547     *ip = (ITEM *) 0;
548 
549     mpTrace = menu_create(items, SIZEOF(t_tbl) - 1, 2, number);
550 }
551 
552 static char *
tracetrace(unsigned tlevel)553 tracetrace(unsigned tlevel)
554 {
555     static char *buf;
556     static size_t need = 12;
557     int n;
558 
559     if (buf == 0) {
560 	for (n = 0; t_tbl[n].name != 0; n++)
561 	    need += strlen(t_tbl[n].name) + 2;
562 	buf = typeMalloc(char, need);
563 	if (!buf)
564 	    failed("tracetrace");
565     }
566     _nc_SPRINTF(buf, _nc_SLIMIT(need) "0x%02x = {", tlevel);
567     if (tlevel == 0) {
568 	_nc_STRCAT(buf, t_tbl[0].name ? t_tbl[0].name : "", need);
569 	_nc_STRCAT(buf, ", ", need);
570     } else {
571 	for (n = 1; t_tbl[n].name != 0; n++)
572 	    if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
573 		_nc_STRCAT(buf, t_tbl[n].name, need);
574 		_nc_STRCAT(buf, ", ", need);
575 	    }
576     }
577     if (buf[strlen(buf) - 2] == ',')
578 	buf[strlen(buf) - 2] = '\0';
579     _nc_STRCAT(buf, "}", need);
580     return buf;
581 }
582 
583 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
584  * the others
585  */
586 static bool
update_trace_menu(MENU * m)587 update_trace_menu(MENU * m)
588 {
589     ITEM **items;
590     ITEM *i;
591     bool changed = FALSE;
592 
593     items = menu_items(m);
594     i = current_item(m);
595     if (i == items[0]) {
596 	if (item_value(i)) {
597 	    ITEM **p;
598 	    for (p = items + 1; *p != 0; p++)
599 		if (item_value(*p)) {
600 		    set_item_value(*p, FALSE);
601 		    changed = TRUE;
602 		}
603 	}
604     }
605     return changed;
606 }
607 
608 static int
perform_trace_menu(int cmd)609 perform_trace_menu(int cmd)
610 /* interactively set the trace level */
611 {
612     ITEM **ip;
613     int result;
614 
615     for (ip = menu_items(mpTrace); *ip; ip++) {
616 	MENU_DATA *td = (MENU_DATA *) item_userptr(*ip);
617 	unsigned mask = td->mask;
618 	if (mask == 0)
619 	    set_item_value(*ip, _nc_tracing == 0);
620 	else if ((mask & _nc_tracing) == mask)
621 	    set_item_value(*ip, TRUE);
622     }
623 
624     result = menu_driver(mpTrace, cmd);
625 
626     if (result == E_OK) {
627 	if (update_trace_menu(mpTrace) || cmd == REQ_TOGGLE_ITEM) {
628 	    unsigned newtrace = 0;
629 	    for (ip = menu_items(mpTrace); *ip; ip++) {
630 		if (item_value(*ip)) {
631 		    MENU_DATA *td = (MENU_DATA *) item_userptr(*ip);
632 		    newtrace |= td->mask;
633 		}
634 	    }
635 	    curses_trace(newtrace);
636 	    Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
637 
638 	    MvWPrintw(status, 1, 0,
639 		      "Trace level is %s\n", tracetrace(_nc_tracing));
640 	    wrefresh(status);
641 	}
642     }
643     return result;
644 }
645 #endif /* TRACE */
646 
647 /*****************************************************************************/
648 
649 static int
menu_number(void)650 menu_number(void)
651 {
652     return item_index(current_item(mpBanner)) - (eBanner + 1);
653 }
654 
655 static MENU *
current_menu(void)656 current_menu(void)
657 {
658     MENU *result;
659 
660     switch (menu_number()) {
661     case eFile:
662 	result = mpFile;
663 	break;
664     case eSelect:
665 	result = mpSelect;
666 	break;
667 #ifdef TRACE
668     case eTrace:
669 	result = mpTrace;
670 	break;
671 #endif
672     default:
673 	result = 0;
674 	break;
675     }
676     return result;
677 }
678 
679 static void
call_menus(int code)680 call_menus(int code)
681 {
682     (void) code;
683     Trace(("Activated menu %d\n", code));
684 }
685 
686 static void
build_menus(char * filename)687 build_menus(char *filename)
688 {
689     static MENU_DATA table[] =
690     {
691 	{"File", call_menus, 0},
692 	{"Select", call_menus, 1},
693 #ifdef TRACE
694 	{"Trace", call_menus, 2},
695 #endif
696 	{(char *) 0, 0, 0}
697     };
698     static ITEM *items[SIZEOF(table)];
699 
700     ITEM **ip = items;
701     int n;
702 
703     for (n = 0; table[n].name != 0; ++n) {
704 	*ip = new_item(table[n].name, empty);
705 	set_item_userptr(*ip, (void *) &table[n]);
706 	++ip;
707     }
708     *ip = (ITEM *) 0;
709 
710     mpBanner = menu_create(items, SIZEOF(table) - 1, SIZEOF(table) - 1, eBanner);
711     set_menu_mark(mpBanner, ">");
712 
713     build_file_menu(eFile);
714     build_select_menu(eSelect, filename);
715 #ifdef TRACE
716     build_trace_menu(eTrace);
717 #endif
718 }
719 
720 static int
move_menu(MENU * menu,MENU * current,int by_y,int by_x)721 move_menu(MENU * menu, MENU * current, int by_y, int by_x)
722 {
723     WINDOW *top_win = menu_win(menu);
724     WINDOW *sub_win = menu_sub(menu);
725     int y0, x0;
726     int y1, x1;
727     int result;
728 
729     getbegyx(top_win, y0, x0);
730     y0 += by_y;
731     x0 += by_x;
732 
733     getbegyx(sub_win, y1, x1);
734     y1 += by_y;
735     x1 += by_x;
736 
737     if ((result = mvwin(top_win, y0, x0)) != ERR) {
738 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH < 20060218)
739 	sub_win->_begy = y1;
740 	sub_win->_begx = x1;
741 #else
742 	mvwin(sub_win, y1, x1);
743 #endif
744 	if (menu == current) {
745 	    touchwin(top_win);
746 	    wnoutrefresh(top_win);
747 	}
748     }
749     return result;
750 }
751 
752 /*
753  * Move the menus around on the screen, to test mvwin().
754  */
755 static void
move_menus(MENU * current,int by_y,int by_x)756 move_menus(MENU * current, int by_y, int by_x)
757 {
758     if (move_menu(mpBanner, current, by_y, by_x) != ERR) {
759 	erase();
760 	wnoutrefresh(stdscr);
761 	move_menu(mpFile, current, by_y, by_x);
762 	move_menu(mpSelect, current, by_y, by_x);
763 #ifdef TRACE
764 	move_menu(mpTrace, current, by_y, by_x);
765 #endif
766 	doupdate();
767     }
768 }
769 
770 #if defined(KEY_RESIZE) && NCURSES_EXT_FUNCS
771 static void
resize_menu(MENU ** menu)772 resize_menu(MENU ** menu)
773 {
774 #if 0
775     WINDOW *win = menu_win(*menu);
776     WINDOW *sub = menu_sub(*menu);
777 #endif
778     (void) menu;
779 }
780 
781 static void
resize_menus(MENU * current)782 resize_menus(MENU * current)
783 {
784     (void) current;
785 
786     werase(status);
787     wnoutrefresh(status);
788     wresize(status, 1, COLS);
789     mvwin(status, LINES - 1, 0);
790 
791     resize_menu(&mpBanner);
792     resize_menu(&mpFile);
793     resize_menu(&mpSelect);
794 #ifdef TRACE
795     resize_menu(&mpTrace);
796 #endif
797 }
798 #endif /* defined(KEY_RESIZE) && NCURSES_EXT_FUNCS */
799 
800 static void
show_status(int ch,MENU * menu)801 show_status(int ch, MENU * menu)
802 {
803     wmove(status, 0, 0);
804     wprintw(status, "key %s, menu %d, mark %s, match %s",
805 	    keyname(ch),
806 	    menu_number(),
807 	    menu_mark(menu),
808 	    menu_pattern(menu));
809     wclrtoeol(status);
810     wrefresh(status);
811 }
812 
813 static void
perform_menus(void)814 perform_menus(void)
815 {
816     MENU *this_menu;
817     MENU *last_menu = mpFile;
818     int code = E_UNKNOWN_COMMAND;
819     int cmd;
820     int ch = ERR;
821 
822 #ifdef NCURSES_MOUSE_VERSION
823     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
824 #endif
825 
826     menu_display(last_menu);
827 
828     for (;;) {
829 
830 	if (ch != ERR)
831 	    show_status(ch, last_menu);
832 
833 	ch = menu_getc(mpBanner);
834 
835 	/*
836 	 * Provide for moving the menu around in the screen using shifted
837 	 * cursor keys.
838 	 */
839 	switch (ch) {
840 	case KEY_SF:
841 	    move_menus(last_menu, 1, 0);
842 	    continue;
843 	case KEY_SR:
844 	    move_menus(last_menu, -1, 0);
845 	    continue;
846 	case KEY_SLEFT:
847 	    move_menus(last_menu, 0, -1);
848 	    continue;
849 	case KEY_SRIGHT:
850 	    move_menus(last_menu, 0, 1);
851 	    continue;
852 #if defined(KEY_RESIZE) && NCURSES_EXT_FUNCS
853 	case KEY_RESIZE:
854 	    resize_menus(last_menu);
855 	    continue;
856 #endif
857 	}
858 	cmd = menu_virtualize(ch);
859 
860 	switch (cmd) {
861 	    /*
862 	     * The banner menu acts solely to select one of the other menus.
863 	     * Move between its items, wrapping at the left/right limits.
864 	     */
865 	case REQ_LEFT_ITEM:
866 	case REQ_RIGHT_ITEM:
867 	    code = menu_driver(mpBanner, cmd);
868 	    if (code == E_REQUEST_DENIED) {
869 		if (menu_number() > 0)
870 		    code = menu_driver(mpBanner, REQ_FIRST_ITEM);
871 		else
872 		    code = menu_driver(mpBanner, REQ_LAST_ITEM);
873 	    }
874 	    break;
875 	default:
876 	    switch (menu_number()) {
877 	    case eFile:
878 		code = perform_file_menu(cmd);
879 		break;
880 	    case eSelect:
881 		code = perform_select_menu(cmd);
882 		break;
883 #ifdef TRACE
884 	    case eTrace:
885 		code = perform_trace_menu(cmd);
886 		break;
887 #endif
888 	    }
889 
890 #if defined(NCURSES_MOUSE_VERSION) && defined(O_MOUSE_MENU)
891 	    if ((code == E_REQUEST_DENIED) && (cmd == KEY_MOUSE)) {
892 		(void) menu_getc(mpBanner);
893 		code = menu_driver(mpBanner, cmd);
894 		if (code == E_REQUEST_DENIED) {
895 		    MEVENT event;
896 		    if (menu_getc(mpBanner) == KEY_MOUSE)
897 			getmouse(&event);	/* give up */
898 		}
899 	    }
900 #endif
901 
902 	    break;
903 	}
904 
905 	if (code == E_OK) {
906 	    this_menu = current_menu();
907 	    if (this_menu != last_menu) {
908 		move(1, 0);
909 		clrtobot();
910 		box(menu_win(this_menu), 0, 0);
911 		refresh();
912 
913 		/* force the current menu to appear */
914 		menu_display(this_menu);
915 
916 		last_menu = this_menu;
917 	    }
918 	}
919 	wrefresh(menu_win(last_menu));
920 	if (code == E_UNKNOWN_COMMAND
921 	    || code == E_NOT_POSTED) {
922 	    ITEM *item = current_item(last_menu);
923 	    MENU_DATA *td = (MENU_DATA *) item_userptr(item);
924 	    td->func((int) td->mask);
925 	}
926 	if (code == E_REQUEST_DENIED)
927 	    beep();
928 	continue;
929     }
930 }
931 
932 static void
destroy_menus(void)933 destroy_menus(void)
934 {
935     menu_destroy(mpFile);
936     menu_destroy(mpSelect);
937 #ifdef TRACE
938     menu_destroy(mpTrace);
939 #endif
940     menu_destroy(mpBanner);
941 }
942 
943 #if HAVE_RIPOFFLINE
944 static int
rip_footer(WINDOW * win,int cols)945 rip_footer(WINDOW *win, int cols)
946 {
947     wbkgd(win, A_REVERSE);
948     werase(win);
949     wmove(win, 0, 0);
950     wprintw(win, "footer: %d columns", cols);
951     wnoutrefresh(win);
952     return OK;
953 }
954 
955 static int
rip_header(WINDOW * win,int cols)956 rip_header(WINDOW *win, int cols)
957 {
958     wbkgd(win, A_REVERSE);
959     werase(win);
960     wmove(win, 0, 0);
961     wprintw(win, "header: %d columns", cols);
962     wnoutrefresh(win);
963     return OK;
964 }
965 #endif /* HAVE_RIPOFFLINE */
966 
967 static void
call_files(int code)968 call_files(int code)
969 {
970     switch (code) {
971     case 0:
972 	destroy_menus();
973 	endwin();
974 	printf("DONE!\n");
975 	ExitProgram(EXIT_SUCCESS);
976     }
977 }
978 
979 static void
usage(void)980 usage(void)
981 {
982     static const char *const tbl[] =
983     {
984 	"Usage: demo_menus [options] [menu-file]"
985 	,""
986 	,"Options:"
987 #if HAVE_RIPOFFLINE
988 	,"  -f       rip-off footer line (can repeat)"
989 	,"  -h       rip-off header line (can repeat)"
990 #endif
991 #ifdef TRACE
992 	,"  -t mask  specify default trace-level (may toggle with ^T)"
993 #endif
994     };
995     size_t n;
996     for (n = 0; n < SIZEOF(tbl); n++)
997 	fprintf(stderr, "%s\n", tbl[n]);
998     ExitProgram(EXIT_FAILURE);
999 }
1000 
1001 int
main(int argc,char * argv[])1002 main(int argc, char *argv[])
1003 {
1004     int c;
1005 
1006     setlocale(LC_ALL, "");
1007 
1008     while ((c = getopt(argc, argv, "fht:")) != -1) {
1009 	switch (c) {
1010 #if HAVE_RIPOFFLINE
1011 	case 'f':
1012 	    ripoffline(-1, rip_footer);
1013 	    break;
1014 	case 'h':
1015 	    ripoffline(1, rip_header);
1016 	    break;
1017 #endif /* HAVE_RIPOFFLINE */
1018 #ifdef TRACE
1019 	case 't':
1020 	    curses_trace((unsigned) strtoul(optarg, 0, 0));
1021 	    break;
1022 #endif
1023 	default:
1024 	    usage();
1025 	}
1026     }
1027 
1028     initscr();
1029     noraw();
1030     cbreak();
1031     noecho();
1032 
1033     if (has_colors()) {
1034 	start_color();
1035 	init_pair(1, COLOR_RED, COLOR_BLACK);
1036 	init_pair(2, COLOR_BLUE, COLOR_WHITE);
1037     }
1038     status = newwin(3, COLS, LINES - 3, 0);
1039     build_menus(argc > 1 ? argv[1] : 0);
1040     perform_menus();
1041     destroy_menus();
1042 
1043     endwin();
1044     ExitProgram(EXIT_SUCCESS);
1045 }
1046 #else
1047 int
main(void)1048 main(void)
1049 {
1050     printf("This program requires the curses menu library\n");
1051     ExitProgram(EXIT_FAILURE);
1052 }
1053 #endif
1054