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