]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/demo_menus.c
ncurses 5.6 - patch 20080804
[ncurses.git] / test / demo_menus.c
1 /****************************************************************************
2  * Copyright (c) 2005-2007,2008 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.27 2008/08/04 15:37:52 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_userptr                    -
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_sub                        -
53 menu_term                       -
54 menu_userptr                    -
55 set_current_item                -
56 set_item_init                   -
57 set_item_opts                   -
58 set_item_term                   -
59 set_item_userptr                -
60 set_menu_grey                   -
61 set_menu_init                   -
62 set_menu_items                  -
63 set_menu_opts                   -
64 set_menu_pad                    -
65 set_menu_pattern                -
66 set_menu_spacing                -
67 set_menu_term                   -
68 set_menu_userptr                -
69 set_top_row                     -
70 top_row                         -
71 */
72
73 #include <test.priv.h>
74
75 #if USE_LIBMENU
76
77 #include <menu.h>
78
79 #include <sys/types.h>
80 #include <sys/stat.h>
81
82 #ifdef NCURSES_VERSION
83 #ifdef TRACE
84 static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS;
85 extern unsigned _nc_tracing;
86 static MENU *mpTrace;
87 #endif
88 #else
89 #undef TRACE
90 #endif
91
92 typedef enum {
93     eBanner = -1
94     ,eFile
95     ,eSelect
96 #ifdef TRACE
97     ,eTrace
98 #endif
99     ,eMAX
100 } MenuNo;
101
102 #define okMenuNo(n) (((n) > eBanner) && ((n) < eMAX))
103
104 #define MENU_Y  1
105
106 static MENU *mpBanner;
107 static MENU *mpFile;
108 static MENU *mpSelect;
109
110 #if !HAVE_STRDUP
111 #define strdup my_strdup
112 static char *
113 strdup(char *s)
114 {
115     char *p = typeMalloc(char, strlen(s) + 1);
116     if (p)
117         strcpy(p, s);
118     return (p);
119 }
120 #endif /* not HAVE_STRDUP */
121
122 /* Common function to allow ^T to toggle trace-mode in the middle of a test
123  * so that trace-files can be made smaller.
124  */
125 static int
126 wGetchar(WINDOW *win)
127 {
128     int c;
129 #ifdef TRACE
130     while ((c = wgetch(win)) == CTRL('T')) {
131         if (_nc_tracing) {
132             save_trace = _nc_tracing;
133             Trace(("TOGGLE-TRACING OFF"));
134             _nc_tracing = 0;
135         } else {
136             _nc_tracing = save_trace;
137         }
138         trace(_nc_tracing);
139         if (_nc_tracing)
140             Trace(("TOGGLE-TRACING ON"));
141     }
142 #else
143     c = wgetch(win);
144 #endif
145     return c;
146 }
147 #define Getchar() wGetchar(stdscr)
148
149 static int
150 menu_virtualize(int c)
151 {
152     int result;
153
154     if (c == '\n' || c == KEY_EXIT)
155         result = (MAX_COMMAND + 1);
156     else if (c == 'u')
157         result = (REQ_SCR_ULINE);
158     else if (c == 'd')
159         result = (REQ_SCR_DLINE);
160     else if (c == 'b' || c == KEY_NPAGE)
161         result = (REQ_SCR_UPAGE);
162     else if (c == 'f' || c == KEY_PPAGE)
163         result = (REQ_SCR_DPAGE);
164     else if (c == 'l' || c == KEY_LEFT || c == KEY_BTAB)
165         result = (REQ_LEFT_ITEM);
166     else if (c == 'n' || c == KEY_DOWN)
167         result = (REQ_NEXT_ITEM);
168     else if (c == 'p' || c == KEY_UP)
169         result = (REQ_PREV_ITEM);
170     else if (c == 'r' || c == KEY_RIGHT || c == '\t')
171         result = (REQ_RIGHT_ITEM);
172     else if (c == ' ')
173         result = (REQ_TOGGLE_ITEM);
174     else {
175         if (c != KEY_MOUSE)
176             beep();
177         result = (c);
178     }
179     return result;
180 }
181
182 static int
183 menu_getc(MENU * m)
184 {
185     return wGetchar(menu_win(m));
186 }
187
188 static int
189 menu_offset(MenuNo number)
190 {
191     int result = 0;
192
193     if (okMenuNo(number)) {
194         int spc_desc, spc_rows, spc_cols;
195
196 #ifdef NCURSES_VERSION
197         menu_spacing(mpBanner, &spc_desc, &spc_rows, &spc_cols);
198 #else
199         spc_rows = 0;
200 #endif
201
202         /* FIXME: MENU.itemlen seems the only way to get actual width of items */
203         result = (number - (eBanner + 1)) * (mpBanner->itemlen + spc_rows);
204     }
205     return result;
206 }
207
208 static MENU *
209 menu_create(ITEM ** items, int count, int ncols, MenuNo number)
210 {
211     MENU *result;
212     WINDOW *menuwin;
213     int mrows, mcols;
214     int y = okMenuNo(number) ? MENU_Y : 0;
215     int x = menu_offset(number);
216     int margin = (y == MENU_Y) ? 1 : 0;
217     int maxcol = (ncols + x) < COLS ? ncols : (COLS - x - 1);
218     int maxrow = (count + 1) / ncols;
219
220     if ((maxrow + y) >= (LINES - 4))
221         maxrow = LINES - 4 - y;
222
223     result = new_menu(items);
224
225     if (has_colors()) {
226         set_menu_fore(result, COLOR_PAIR(1));
227         set_menu_back(result, COLOR_PAIR(2));
228     }
229
230     set_menu_format(result, maxrow, maxcol);
231     scale_menu(result, &mrows, &mcols);
232
233     if (mcols + (2 * margin + x) >= COLS)
234         mcols = COLS - (2 * margin + x);
235
236 #ifdef TRACE
237     if (number == eTrace)
238         menu_opts_off(result, O_ONEVALUE);
239     else
240         menu_opts_on(result, O_ONEVALUE);
241 #endif
242
243     menuwin = newwin(mrows + (2 * margin), mcols + (2 * margin), y, x);
244     set_menu_win(result, menuwin);
245     keypad(menuwin, TRUE);
246     if (margin)
247         box(menuwin, 0, 0);
248
249     set_menu_sub(result, derwin(menuwin, mrows, mcols, margin, margin));
250
251     post_menu(result);
252
253     return result;
254 }
255
256 static void
257 menu_destroy(MENU * m)
258 {
259     int count;
260
261     Trace(("menu_destroy %p", m));
262     if (m != 0) {
263         ITEM **items = menu_items(m);
264         const char *blob = 0;
265
266         count = item_count(m);
267         Trace(("menu_destroy %p count %d", m, count));
268         if ((count > 0) && (m == mpSelect)) {
269             blob = item_name(*items);
270         }
271
272         unpost_menu(m);
273         free_menu(m);
274
275         /* free the extra data allocated in build_select_menu() */
276         if ((count > 0) && (m == mpSelect)) {
277             if (blob) {
278                 Trace(("freeing blob %p", blob));
279                 free((char *) blob);
280             }
281             free(items);
282         }
283 #ifdef TRACE
284         if ((count > 0) && (m == mpTrace)) {
285             ITEM **ip = items;
286             while (*ip)
287                 free(*ip++);
288         }
289 #endif
290     }
291 }
292
293 /* force the given menu to appear */
294 static void
295 menu_display(MENU * m)
296 {
297     touchwin(menu_win(m));
298     wrefresh(menu_win(m));
299 }
300
301 /*****************************************************************************/
302
303 static void
304 build_file_menu(MenuNo number)
305 {
306     static CONST_MENUS char *labels[] =
307     {
308         "Exit",
309         (char *) 0
310     };
311     static ITEM *items[SIZEOF(labels)];
312
313     ITEM **ip = items;
314     CONST_MENUS char **ap;
315
316     for (ap = labels; *ap; ap++)
317         *ip++ = new_item(*ap, "");
318     *ip = (ITEM *) 0;
319
320     mpFile = menu_create(items, SIZEOF(labels) - 1, 1, number);
321 }
322
323 static int
324 perform_file_menu(int cmd)
325 {
326     return menu_driver(mpFile, cmd);
327 }
328
329 /*****************************************************************************/
330
331 static void
332 build_select_menu(MenuNo number, char *filename)
333 {
334     static CONST_MENUS char *labels[] =
335     {
336         "Lions",
337         "Tigers",
338         "Bears",
339         "(Oh my!)",
340         "Newts",
341         "Platypi",
342         "Lemurs",
343         "(Oh really?!)",
344         "Leopards",
345         "Panthers",
346         "Pumas",
347         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
348         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
349         (char *) 0
350     };
351     static ITEM **items;
352
353     ITEM **ip;
354     CONST_MENUS char **ap = 0;
355     CONST_MENUS char **myList = 0;
356     unsigned count = 0;
357
358     if (filename != 0) {
359         struct stat sb;
360         if (stat(filename, &sb) == 0
361             && (sb.st_mode & S_IFMT) == S_IFREG
362             && sb.st_size != 0) {
363             size_t size = (size_t) sb.st_size;
364             unsigned j, k;
365             char *blob = typeMalloc(char, size + 1);
366             CONST_MENUS char **list = typeCalloc(CONST_MENUS char *, size + 1);
367
368             items = typeCalloc(ITEM *, size + 1);
369             Trace(("build_select_menu blob=%p, items=%p", blob, items));
370             if (blob != 0 && list != 0) {
371                 FILE *fp = fopen(filename, "r");
372                 if (fp != 0) {
373                     if (fread(blob, sizeof(char), size, fp) == size) {
374                         bool mark = TRUE;
375                         for (j = k = 0; j < size; ++j) {
376                             if (mark) {
377                                 list[k++] = blob + j;
378                                 mark = FALSE;
379                             }
380                             if (blob[j] == '\n') {
381                                 blob[j] = '\0';
382                                 if (k > 0 && *list[k - 1] == '\0')
383                                     --k;
384                                 mark = TRUE;
385                             } else if (blob[j] == '\t') {
386                                 blob[j] = ' ';  /* menu items are printable */
387                             }
388                         }
389                         list[k] = 0;
390                         count = k;
391                         ap = myList = list;
392                     }
393                     fclose(fp);
394                 }
395             }
396         }
397     }
398     if (ap == 0) {
399         count = SIZEOF(labels) - 1;
400         items = typeCalloc(ITEM *, count + 1);
401         ap = labels;
402     }
403
404     ip = items;
405     while (*ap != 0)
406         *ip++ = new_item(*ap++, "");
407     *ip = 0;
408
409     mpSelect = menu_create(items, (int) count, 1, number);
410     if (myList != 0)
411         free(myList);
412 }
413
414 static int
415 perform_select_menu(int cmd)
416 {
417     return menu_driver(mpSelect, cmd);
418 }
419
420 /*****************************************************************************/
421
422 #ifdef TRACE
423 #define T_TBL(name) { #name, name }
424 static struct {
425     const char *name;
426     unsigned mask;
427 } t_tbl[] = {
428
429     T_TBL(TRACE_DISABLE),
430         T_TBL(TRACE_TIMES),
431         T_TBL(TRACE_TPUTS),
432         T_TBL(TRACE_UPDATE),
433         T_TBL(TRACE_MOVE),
434         T_TBL(TRACE_CHARPUT),
435         T_TBL(TRACE_ORDINARY),
436         T_TBL(TRACE_CALLS),
437         T_TBL(TRACE_VIRTPUT),
438         T_TBL(TRACE_IEVENT),
439         T_TBL(TRACE_BITS),
440         T_TBL(TRACE_ICALLS),
441         T_TBL(TRACE_CCALLS),
442         T_TBL(TRACE_DATABASE),
443         T_TBL(TRACE_ATTRS),
444         T_TBL(TRACE_MAXIMUM),
445     {
446         (char *) 0, 0
447     }
448 };
449
450 static void
451 build_trace_menu(MenuNo number)
452 {
453     static ITEM *items[SIZEOF(t_tbl)];
454
455     ITEM **ip = items;
456     int n;
457
458     for (n = 0; t_tbl[n].name != 0; n++)
459         *ip++ = new_item(t_tbl[n].name, "");
460     *ip = (ITEM *) 0;
461
462     mpTrace = menu_create(items, SIZEOF(t_tbl) - 1, 2, number);
463 }
464
465 static char *
466 tracetrace(unsigned tlevel)
467 {
468     static char *buf;
469     int n;
470
471     if (buf == 0) {
472         size_t need = 12;
473         for (n = 0; t_tbl[n].name != 0; n++)
474             need += strlen(t_tbl[n].name) + 2;
475         buf = typeMalloc(char, need);
476     }
477     sprintf(buf, "0x%02x = {", tlevel);
478     if (tlevel == 0) {
479         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
480     } else {
481         for (n = 1; t_tbl[n].name != 0; n++)
482             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
483                 strcat(buf, t_tbl[n].name);
484                 strcat(buf, ", ");
485             }
486     }
487     if (buf[strlen(buf) - 2] == ',')
488         buf[strlen(buf) - 2] = '\0';
489     return (strcat(buf, "}"));
490 }
491
492 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
493  * the others
494  */
495 static bool
496 update_trace_menu(MENU * m)
497 {
498     ITEM **items;
499     ITEM *i, **p;
500     bool changed = FALSE;
501
502     items = menu_items(m);
503     i = current_item(m);
504     if (i == items[0]) {
505         if (item_value(i)) {
506             for (p = items + 1; *p != 0; p++)
507                 if (item_value(*p)) {
508                     set_item_value(*p, FALSE);
509                     changed = TRUE;
510                 }
511         }
512     }
513     return changed;
514 }
515
516 static int
517 perform_trace_menu(int cmd)
518 /* interactively set the trace level */
519 {
520     ITEM **ip;
521     unsigned newtrace;
522     int result;
523
524     for (ip = menu_items(mpTrace); *ip; ip++) {
525         unsigned mask = t_tbl[item_index(*ip)].mask;
526         if (mask == 0)
527             set_item_value(*ip, _nc_tracing == 0);
528         else if ((mask & _nc_tracing) == mask)
529             set_item_value(*ip, TRUE);
530     }
531
532     result = menu_driver(mpTrace, cmd);
533
534     if (result == E_OK) {
535         if (update_trace_menu(mpTrace) || cmd == REQ_TOGGLE_ITEM) {
536             newtrace = 0;
537             for (ip = menu_items(mpTrace); *ip; ip++) {
538                 if (item_value(*ip))
539                     newtrace |= t_tbl[item_index(*ip)].mask;
540             }
541             trace(newtrace);
542             Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
543
544             (void) mvprintw(LINES - 2, 0,
545                             "Trace level is %s\n", tracetrace(_nc_tracing));
546             refresh();
547         }
548     }
549     return result;
550 }
551 #endif /* TRACE */
552
553 /*****************************************************************************/
554
555 static int
556 menu_number(void)
557 {
558     return item_index(current_item(mpBanner)) - (eBanner + 1);
559 }
560
561 static MENU *
562 current_menu(void)
563 {
564     MENU *result;
565
566     switch (menu_number()) {
567     case eFile:
568         result = mpFile;
569         break;
570     case eSelect:
571         result = mpSelect;
572         break;
573 #ifdef TRACE
574     case eTrace:
575         result = mpTrace;
576         break;
577 #endif
578     default:
579         result = 0;
580         break;
581     }
582     return result;
583 }
584
585 static void
586 build_menus(char *filename)
587 {
588     static CONST_MENUS char *labels[] =
589     {
590         "File",
591         "Select",
592 #ifdef TRACE
593         "Trace",
594 #endif
595         (char *) 0
596     };
597     static ITEM *items[SIZEOF(labels)];
598
599     ITEM **ip = items;
600     CONST_MENUS char **ap;
601
602     for (ap = labels; *ap; ap++)
603         *ip++ = new_item(*ap, "");
604     *ip = (ITEM *) 0;
605
606     mpBanner = menu_create(items, SIZEOF(labels) - 1, SIZEOF(labels) - 1, eBanner);
607     set_menu_mark(mpBanner, ">");
608
609     build_file_menu(eFile);
610     build_select_menu(eSelect, filename);
611 #ifdef TRACE
612     build_trace_menu(eTrace);
613 #endif
614 }
615
616 static int
617 move_menu(MENU * menu, MENU * current, int by_y, int by_x)
618 {
619     WINDOW *top_win = menu_win(menu);
620     WINDOW *sub_win = menu_sub(menu);
621     int y0, x0;
622     int y1, x1;
623     int result;
624
625     getbegyx(top_win, y0, x0);
626     y0 += by_y;
627     x0 += by_x;
628
629     getbegyx(sub_win, y1, x1);
630     y1 += by_y;
631     x1 += by_x;
632
633     if ((result = mvwin(top_win, y0, x0)) != ERR) {
634 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH < 20060218)
635         sub_win->_begy = y1;
636         sub_win->_begx = x1;
637 #else
638         mvwin(sub_win, y1, x1);
639 #endif
640         if (menu == current) {
641             touchwin(top_win);
642             wnoutrefresh(top_win);
643         }
644     }
645     return result;
646 }
647
648 /*
649  * Move the menus around on the screen, to test mvwin().
650  */
651 static void
652 move_menus(MENU * current, int by_y, int by_x)
653 {
654     if (move_menu(mpBanner, current, by_y, by_x) != ERR) {
655         erase();
656         wnoutrefresh(stdscr);
657         move_menu(mpFile, current, by_y, by_x);
658         move_menu(mpSelect, current, by_y, by_x);
659 #ifdef TRACE
660         move_menu(mpTrace, current, by_y, by_x);
661 #endif
662         doupdate();
663     }
664 }
665
666 static void
667 show_status(int ch, MENU * menu)
668 {
669     move(LINES - 1, 0);
670     printw("key %s, menu %d, mark %s, match %s",
671            keyname(ch),
672            menu_number(),
673            menu_mark(menu),
674            menu_pattern(menu));
675     clrtoeol();
676     refresh();
677 }
678
679 static void
680 perform_menus(void)
681 {
682     MENU *this_menu;
683     MENU *last_menu = mpFile;
684     int code = E_UNKNOWN_COMMAND;
685     int cmd;
686     int ch = ERR;
687
688 #ifdef NCURSES_MOUSE_VERSION
689     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
690 #endif
691
692     menu_display(last_menu);
693
694     for (;;) {
695
696         if (ch != ERR)
697             show_status(ch, last_menu);
698
699         ch = menu_getc(mpBanner);
700
701         /*
702          * Provide for moving the menu around in the screen using shifted
703          * cursor keys.
704          */
705         switch (ch) {
706         case KEY_SF:
707             move_menus(last_menu, 1, 0);
708             continue;
709         case KEY_SR:
710             move_menus(last_menu, -1, 0);
711             continue;
712         case KEY_SLEFT:
713             move_menus(last_menu, 0, -1);
714             continue;
715         case KEY_SRIGHT:
716             move_menus(last_menu, 0, 1);
717             continue;
718         }
719         cmd = menu_virtualize(ch);
720
721         switch (cmd) {
722             /*
723              * The banner menu acts solely to select one of the other menus.
724              * Move between its items, wrapping at the left/right limits.
725              */
726         case REQ_LEFT_ITEM:
727         case REQ_RIGHT_ITEM:
728             code = menu_driver(mpBanner, cmd);
729             if (code == E_REQUEST_DENIED) {
730                 if (menu_number() > 0)
731                     code = menu_driver(mpBanner, REQ_FIRST_ITEM);
732                 else
733                     code = menu_driver(mpBanner, REQ_LAST_ITEM);
734             }
735             break;
736         default:
737             switch (menu_number()) {
738             case eFile:
739                 code = perform_file_menu(cmd);
740                 break;
741             case eSelect:
742                 code = perform_select_menu(cmd);
743                 break;
744 #ifdef TRACE
745             case eTrace:
746                 code = perform_trace_menu(cmd);
747                 break;
748 #endif
749             }
750
751             if ((code == E_REQUEST_DENIED) && (cmd == KEY_MOUSE)) {
752                 code = menu_driver(mpBanner, cmd);
753             }
754
755             break;
756         }
757
758         if (code == E_OK) {
759             this_menu = current_menu();
760             if (this_menu != last_menu) {
761                 move(1, 0);
762                 clrtobot();
763                 box(menu_win(this_menu), 0, 0);
764                 refresh();
765
766                 /* force the current menu to appear */
767                 menu_display(this_menu);
768
769                 last_menu = this_menu;
770             }
771         }
772         wrefresh(menu_win(last_menu));
773         if (code == E_UNKNOWN_COMMAND
774             || code == E_NOT_POSTED) {
775             if (menu_number() == eFile)
776                 break;
777             beep();
778         }
779         if (code == E_REQUEST_DENIED)
780             beep();
781         continue;
782     }
783
784 #ifdef NCURSES_MOUSE_VERSION
785     mousemask(0, (mmask_t *) 0);
786 #endif
787 }
788
789 static void
790 destroy_menus(void)
791 {
792     menu_destroy(mpFile);
793     menu_destroy(mpSelect);
794 #ifdef TRACE
795     menu_destroy(mpTrace);
796 #endif
797     menu_destroy(mpBanner);
798 }
799
800 #if HAVE_RIPOFFLINE
801 static int
802 rip_footer(WINDOW *win, int cols)
803 {
804     wbkgd(win, A_REVERSE);
805     werase(win);
806     wmove(win, 0, 0);
807     wprintw(win, "footer: %d columns", cols);
808     wnoutrefresh(win);
809     return OK;
810 }
811
812 static int
813 rip_header(WINDOW *win, int cols)
814 {
815     wbkgd(win, A_REVERSE);
816     werase(win);
817     wmove(win, 0, 0);
818     wprintw(win, "header: %d columns", cols);
819     wnoutrefresh(win);
820     return OK;
821 }
822 #endif /* HAVE_RIPOFFLINE */
823
824 static void
825 usage(void)
826 {
827     static const char *const tbl[] =
828     {
829         "Usage: demo_menus [options]"
830         ,""
831         ,"Options:"
832 #if HAVE_RIPOFFLINE
833         ,"  -f       rip-off footer line (can repeat)"
834         ,"  -h       rip-off header line (can repeat)"
835 #endif
836 #ifdef TRACE
837         ,"  -t mask  specify default trace-level (may toggle with ^T)"
838 #endif
839     };
840     size_t n;
841     for (n = 0; n < SIZEOF(tbl); n++)
842         fprintf(stderr, "%s\n", tbl[n]);
843     ExitProgram(EXIT_FAILURE);
844 }
845
846 int
847 main(int argc, char *argv[])
848 {
849     int c;
850
851     setlocale(LC_ALL, "");
852
853     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
854         switch (c) {
855 #if HAVE_RIPOFFLINE
856         case 'f':
857             ripoffline(-1, rip_footer);
858             break;
859         case 'h':
860             ripoffline(1, rip_header);
861             break;
862 #endif /* HAVE_RIPOFFLINE */
863 #ifdef TRACE
864         case 't':
865             trace(strtoul(optarg, 0, 0));
866             break;
867 #endif
868         default:
869             usage();
870         }
871     }
872
873     initscr();
874     noraw();
875     cbreak();
876     noecho();
877
878     if (has_colors()) {
879         start_color();
880         init_pair(1, COLOR_RED, COLOR_BLACK);
881         init_pair(2, COLOR_BLUE, COLOR_WHITE);
882     }
883     build_menus(argc > 1 ? argv[1] : 0);
884     perform_menus();
885     destroy_menus();
886
887     endwin();
888     ExitProgram(EXIT_SUCCESS);
889 }
890 #else
891 int
892 main(void)
893 {
894     printf("This program requires the curses menu library\n");
895     ExitProgram(EXIT_FAILURE);
896 }
897 #endif