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