ncurses 5.5
[ncurses.git] / test / demo_menus.c
1 /*
2  * $Id: demo_menus.c,v 1.13 2005/10/01 15:54:31 tom Exp $
3  *
4  * Demonstrate a variety of functions from the menu library.
5  * Thomas Dickey - 2005/4/9
6  */
7 /*
8 item_description                -
9 item_init                       -
10 item_opts                       -
11 item_opts_off                   -
12 item_opts_on                    -
13 item_term                       -
14 item_userptr                    -
15 item_visible                    -
16 menu_back                       -
17 menu_fore                       -
18 menu_format                     -
19 menu_grey                       -
20 menu_init                       -
21 menu_mark                       -
22 menu_opts                       -
23 menu_pad                        -
24 menu_pattern                    -
25 menu_request_by_name            -
26 menu_request_name               -
27 menu_sub                        -
28 menu_term                       -
29 menu_userptr                    -
30 set_current_item                -
31 set_item_init                   -
32 set_item_opts                   -
33 set_item_term                   -
34 set_item_userptr                -
35 set_menu_grey                   -
36 set_menu_init                   -
37 set_menu_items                  -
38 set_menu_opts                   -
39 set_menu_pad                    -
40 set_menu_pattern                -
41 set_menu_spacing                -
42 set_menu_term                   -
43 set_menu_userptr                -
44 set_top_row                     -
45 top_row                         -
46 */
47
48 #include <test.priv.h>
49
50 #if USE_LIBMENU
51
52 #include <menu.h>
53
54 #include <sys/types.h>
55 #include <sys/stat.h>
56
57 #ifdef NCURSES_VERSION
58 #ifdef TRACE
59 static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS;
60 extern unsigned _nc_tracing;
61 static MENU *mpTrace;
62 #endif
63 #else
64 #undef TRACE
65 #endif
66
67 typedef enum {
68     eUnknown = -1
69     ,eFile = 0
70     ,eSelect
71 #ifdef TRACE
72     ,eTrace
73 #endif
74 } MenuNo;
75
76 #define MENU_Y  1
77
78 static MENU *mpBanner;
79 static MENU *mpFile;
80 static MENU *mpSelect;
81
82 #if !HAVE_STRDUP
83 #define strdup my_strdup
84 static char *
85 strdup(char *s)
86 {
87     char *p = (char *) malloc(strlen(s) + 1);
88     if (p)
89         strcpy(p, s);
90     return (p);
91 }
92 #endif /* not HAVE_STRDUP */
93
94 /* Common function to allow ^T to toggle trace-mode in the middle of a test
95  * so that trace-files can be made smaller.
96  */
97 static int
98 wGetchar(WINDOW *win)
99 {
100     int c;
101 #ifdef TRACE
102     while ((c = wgetch(win)) == CTRL('T')) {
103         if (_nc_tracing) {
104             save_trace = _nc_tracing;
105             _tracef("TOGGLE-TRACING OFF");
106             _nc_tracing = 0;
107         } else {
108             _nc_tracing = save_trace;
109         }
110         trace(_nc_tracing);
111         if (_nc_tracing)
112             _tracef("TOGGLE-TRACING ON");
113     }
114 #else
115     c = wgetch(win);
116 #endif
117     return c;
118 }
119 #define Getchar() wGetchar(stdscr)
120
121 static int
122 menu_virtualize(int c)
123 {
124     int result;
125
126     if (c == '\n' || c == KEY_EXIT)
127         result = (MAX_COMMAND + 1);
128     else if (c == 'u')
129         result = (REQ_SCR_ULINE);
130     else if (c == 'd')
131         result = (REQ_SCR_DLINE);
132     else if (c == 'b' || c == KEY_NPAGE)
133         result = (REQ_SCR_UPAGE);
134     else if (c == 'f' || c == KEY_PPAGE)
135         result = (REQ_SCR_DPAGE);
136     else if (c == 'l' || c == KEY_LEFT || c == KEY_BTAB)
137         result = (REQ_LEFT_ITEM);
138     else if (c == 'n' || c == KEY_DOWN)
139         result = (REQ_NEXT_ITEM);
140     else if (c == 'p' || c == KEY_UP)
141         result = (REQ_PREV_ITEM);
142     else if (c == 'r' || c == KEY_RIGHT || c == '\t')
143         result = (REQ_RIGHT_ITEM);
144     else if (c == ' ')
145         result = (REQ_TOGGLE_ITEM);
146     else {
147         if (c != KEY_MOUSE)
148             beep();
149         result = (c);
150     }
151     return result;
152 }
153
154 static int
155 menu_getc(MENU * m)
156 {
157     return wGetchar(menu_win(m));
158 }
159
160 static int
161 menu_offset(MenuNo number)
162 {
163     int result = 0;
164
165     if ((int) number >= 0) {
166         int spc_desc, spc_rows, spc_cols;
167
168 #ifdef NCURSES_VERSION
169         menu_spacing(mpBanner, &spc_desc, &spc_rows, &spc_cols);
170 #else
171         spc_rows = 0;
172 #endif
173
174         /* FIXME: MENU.itemlen seems the only way to get actual width of items */
175         result = number * (mpBanner->itemlen + spc_rows);
176     }
177     return result;
178 }
179
180 static MENU *
181 menu_create(ITEM ** items, int count, int ncols, MenuNo number)
182 {
183     MENU *result;
184     WINDOW *menuwin;
185     int mrows, mcols;
186     int y = ((int) number >= 0) ? MENU_Y : 0;
187     int x = menu_offset(number);
188     int margin = (y == MENU_Y) ? 1 : 0;
189     int maxcol = (ncols + x) < COLS ? ncols : (COLS - x - 1);
190     int maxrow = (count + 1) / ncols;
191
192     if ((maxrow + y) >= (LINES - 4))
193         maxrow = LINES - 4 - y;
194
195     result = new_menu(items);
196
197     if (has_colors()) {
198         set_menu_fore(result, COLOR_PAIR(1));
199         set_menu_back(result, COLOR_PAIR(2));
200     }
201
202     set_menu_format(result, maxrow, maxcol);
203     scale_menu(result, &mrows, &mcols);
204
205     if (mcols + (2 * margin + x) >= COLS)
206         mcols = COLS - (2 * margin + x);
207
208 #ifdef TRACE
209     if (number == eTrace)
210         menu_opts_off(result, O_ONEVALUE);
211     else
212         menu_opts_on(result, O_ONEVALUE);
213 #endif
214
215     menuwin = newwin(mrows + (2 * margin), mcols + (2 * margin), y, x);
216     set_menu_win(result, menuwin);
217     keypad(menuwin, TRUE);
218     if (margin)
219         box(menuwin, 0, 0);
220
221     set_menu_sub(result, derwin(menuwin, mrows, mcols, margin, margin));
222
223     post_menu(result);
224
225     return result;
226 }
227
228 static void
229 menu_destroy(MENU * m)
230 {
231     ITEM **ip;
232     int count;
233
234     if (m != 0) {
235         delwin(menu_win(m));
236
237         ip = menu_items(m);
238         count = item_count(m);
239
240         free_menu(m);
241 #if 0
242         if (count > 0) {
243             while (*ip) {
244                 _tracef("freeing item %d:%d", ip - menu_items(m), count);
245                 free_item(*ip++);
246             }
247         }
248 #endif
249     }
250 }
251
252 /* force the given menu to appear */
253 static void
254 menu_display(MENU * m)
255 {
256     touchwin(menu_win(m));
257     wrefresh(menu_win(m));
258 }
259
260 /*****************************************************************************/
261
262 static void
263 build_file_menu(MenuNo number)
264 {
265     static const char *labels[] =
266     {
267         "Exit",
268         (char *) 0
269     };
270     static ITEM *items[SIZEOF(labels)];
271
272     ITEM **ip = items;
273     const char **ap;
274
275     for (ap = labels; *ap; ap++)
276         *ip++ = new_item(*ap, "");
277     *ip = (ITEM *) 0;
278
279     mpFile = menu_create(items, SIZEOF(labels) - 1, 1, number);
280 }
281
282 static int
283 perform_file_menu(int cmd)
284 {
285     return menu_driver(mpFile, cmd);
286 }
287
288 /*****************************************************************************/
289
290 static void
291 build_select_menu(MenuNo number, char *filename)
292 {
293     static const char *labels[] =
294     {
295         "Lions",
296         "Tigers",
297         "Bears",
298         "(Oh my!)",
299         "Newts",
300         "Platypi",
301         "Lemurs",
302         "(Oh really?!)",
303         "Leopards",
304         "Panthers",
305         "Pumas",
306         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
307         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
308         (char *) 0
309     };
310     static ITEM **items;
311
312     ITEM **ip;
313     const char **ap = 0;
314     unsigned count = 0;
315
316     if (filename != 0) {
317         struct stat sb;
318         if (stat(filename, &sb) == 0
319             && (sb.st_mode & S_IFMT) == S_IFREG
320             && sb.st_size != 0) {
321             unsigned size = sb.st_size;
322             unsigned j, k;
323             char *blob = malloc(size + 1);
324             const char **list = (const char **) calloc(sizeof(*list), size + 1);
325
326             items = (ITEM **) calloc(sizeof(ITEM *), size + 1);
327             if (blob != 0 && list != 0) {
328                 FILE *fp = fopen(filename, "r");
329                 if (fp != 0) {
330                     if (fread(blob, sizeof(char), size, fp) == size) {
331                         bool mark = TRUE;
332                         for (j = k = 0; j < size; ++j) {
333                             if (mark) {
334                                 list[k++] = blob + j;
335                                 mark = FALSE;
336                             }
337                             if (blob[j] == '\n') {
338                                 blob[j] = '\0';
339                                 if (k > 0 && *list[k - 1] == '\0')
340                                     --k;
341                                 mark = TRUE;
342                             } else if (blob[j] == '\t') {
343                                 blob[j] = ' ';  /* menu items are printable */
344                             }
345                         }
346                         list[k] = 0;
347                         count = k;
348                         ap = list;
349                     }
350                     fclose(fp);
351                 }
352             }
353         }
354     }
355     if (ap == 0) {
356         count = SIZEOF(labels) - 1;
357         items = (ITEM **) calloc(count + 1, sizeof(*items));
358         ap = labels;
359     }
360
361     ip = items;
362     while (*ap != 0)
363         *ip++ = new_item(*ap++, "");
364     *ip = 0;
365
366     mpSelect = menu_create(items, (int) count, 1, number);
367 }
368
369 static int
370 perform_select_menu(int cmd)
371 {
372     return menu_driver(mpSelect, cmd);
373 }
374
375 /*****************************************************************************/
376
377 #ifdef TRACE
378 #define T_TBL(name) { #name, name }
379 static struct {
380     const char *name;
381     unsigned mask;
382 } t_tbl[] = {
383
384     T_TBL(TRACE_DISABLE),
385         T_TBL(TRACE_TIMES),
386         T_TBL(TRACE_TPUTS),
387         T_TBL(TRACE_UPDATE),
388         T_TBL(TRACE_MOVE),
389         T_TBL(TRACE_CHARPUT),
390         T_TBL(TRACE_ORDINARY),
391         T_TBL(TRACE_CALLS),
392         T_TBL(TRACE_VIRTPUT),
393         T_TBL(TRACE_IEVENT),
394         T_TBL(TRACE_BITS),
395         T_TBL(TRACE_ICALLS),
396         T_TBL(TRACE_CCALLS),
397         T_TBL(TRACE_DATABASE),
398         T_TBL(TRACE_ATTRS),
399         T_TBL(TRACE_MAXIMUM),
400     {
401         (char *) 0, 0
402     }
403 };
404
405 static void
406 build_trace_menu(MenuNo number)
407 {
408     static ITEM *items[SIZEOF(t_tbl)];
409
410     ITEM **ip = items;
411     int n;
412
413     for (n = 0; t_tbl[n].name != 0; n++)
414         *ip++ = new_item(t_tbl[n].name, "");
415     *ip = (ITEM *) 0;
416
417     mpTrace = menu_create(items, SIZEOF(t_tbl) - 1, 2, number);
418 }
419
420 static char *
421 tracetrace(unsigned tlevel)
422 {
423     static char *buf;
424     int n;
425
426     if (buf == 0) {
427         size_t need = 12;
428         for (n = 0; t_tbl[n].name != 0; n++)
429             need += strlen(t_tbl[n].name) + 2;
430         buf = (char *) malloc(need);
431     }
432     sprintf(buf, "0x%02x = {", tlevel);
433     if (tlevel == 0) {
434         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
435     } else {
436         for (n = 1; t_tbl[n].name != 0; n++)
437             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
438                 strcat(buf, t_tbl[n].name);
439                 strcat(buf, ", ");
440             }
441     }
442     if (buf[strlen(buf) - 2] == ',')
443         buf[strlen(buf) - 2] = '\0';
444     return (strcat(buf, "}"));
445 }
446
447 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
448  * the others
449  */
450 static bool
451 update_trace_menu(MENU * m)
452 {
453     ITEM **items;
454     ITEM *i, **p;
455     bool changed = FALSE;
456
457     items = menu_items(m);
458     i = current_item(m);
459     if (i == items[0]) {
460         if (item_value(i)) {
461             for (p = items + 1; *p != 0; p++)
462                 if (item_value(*p)) {
463                     set_item_value(*p, FALSE);
464                     changed = TRUE;
465                 }
466         }
467     }
468     return changed;
469 }
470
471 static int
472 perform_trace_menu(int cmd)
473 /* interactively set the trace level */
474 {
475     ITEM **ip;
476     unsigned newtrace;
477     int result;
478
479     for (ip = menu_items(mpTrace); *ip; ip++) {
480         unsigned mask = t_tbl[item_index(*ip)].mask;
481         if (mask == 0)
482             set_item_value(*ip, _nc_tracing == 0);
483         else if ((mask & _nc_tracing) == mask)
484             set_item_value(*ip, TRUE);
485     }
486
487     result = menu_driver(mpTrace, cmd);
488
489     if (result == E_OK) {
490         if (update_trace_menu(mpTrace) || cmd == REQ_TOGGLE_ITEM) {
491             newtrace = 0;
492             for (ip = menu_items(mpTrace); *ip; ip++) {
493                 if (item_value(*ip))
494                     newtrace |= t_tbl[item_index(*ip)].mask;
495             }
496             trace(newtrace);
497             _tracef("trace level interactively set to %s", tracetrace(_nc_tracing));
498
499             (void) mvprintw(LINES - 2, 0,
500                             "Trace level is %s\n", tracetrace(_nc_tracing));
501             refresh();
502         }
503     }
504     return result;
505 }
506 #endif /* TRACE */
507
508 /*****************************************************************************/
509
510 static int
511 menu_number(void)
512 {
513     return item_index(current_item(mpBanner));
514 }
515
516 static MENU *
517 current_menu(void)
518 {
519     MENU *result;
520
521     switch (menu_number()) {
522     case eFile:
523         result = mpFile;
524         break;
525     case eSelect:
526         result = mpSelect;
527         break;
528 #ifdef TRACE
529     case eTrace:
530         result = mpTrace;
531         break;
532 #endif
533     default:
534         result = 0;
535         break;
536     }
537     return result;
538 }
539
540 static void
541 build_menus(char *filename)
542 {
543     static const char *labels[] =
544     {
545         "File",
546         "Select",
547 #ifdef TRACE
548         "Trace",
549 #endif
550         (char *) 0
551     };
552     static ITEM *items[SIZEOF(labels)];
553
554     ITEM **ip = items;
555     const char **ap;
556
557     for (ap = labels; *ap; ap++)
558         *ip++ = new_item(*ap, "");
559     *ip = (ITEM *) 0;
560
561     mpBanner = menu_create(items, SIZEOF(labels) - 1, SIZEOF(labels) - 1, eUnknown);
562     set_menu_mark(mpBanner, ">");
563
564     build_file_menu(eFile);
565     build_select_menu(eSelect, filename);
566 #ifdef TRACE
567     build_trace_menu(eTrace);
568 #endif
569 }
570
571 static void
572 perform_menus(void)
573 {
574     MENU *this_menu;
575     MENU *last_menu = mpFile;
576     int code = E_UNKNOWN_COMMAND, cmd, ch;
577
578 #ifdef NCURSES_MOUSE_VERSION
579     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
580 #endif
581
582     menu_display(last_menu);
583
584     for (;;) {
585         ch = menu_getc(mpBanner);
586         cmd = menu_virtualize(ch);
587
588         switch (cmd) {
589             /*
590              * The banner menu acts solely to select one of the other menus.
591              * Move between its items, wrapping at the left/right limits.
592              */
593         case REQ_LEFT_ITEM:
594         case REQ_RIGHT_ITEM:
595             code = menu_driver(mpBanner, cmd);
596             if (code == E_REQUEST_DENIED) {
597                 if (menu_number() > 0)
598                     code = menu_driver(mpBanner, REQ_FIRST_ITEM);
599                 else
600                     code = menu_driver(mpBanner, REQ_LAST_ITEM);
601             }
602             break;
603         default:
604             switch (menu_number()) {
605             case eFile:
606                 code = perform_file_menu(cmd);
607                 break;
608             case eSelect:
609                 code = perform_select_menu(cmd);
610                 break;
611 #ifdef TRACE
612             case eTrace:
613                 code = perform_trace_menu(cmd);
614                 break;
615 #endif
616             }
617
618             if ((code == E_REQUEST_DENIED) && (cmd == KEY_MOUSE)) {
619                 code = menu_driver(mpBanner, cmd);
620             }
621
622             break;
623         }
624
625         if (code == E_OK) {
626             this_menu = current_menu();
627             if (this_menu != last_menu) {
628                 move(1, 0);
629                 clrtobot();
630                 box(menu_win(this_menu), 0, 0);
631                 refresh();
632
633                 /* force the current menu to appear */
634                 menu_display(this_menu);
635
636                 last_menu = this_menu;
637             }
638         }
639         wrefresh(menu_win(last_menu));
640         if (code == E_UNKNOWN_COMMAND
641             || code == E_NOT_POSTED) {
642             if (menu_number() == eFile)
643                 break;
644             beep();
645         }
646         if (code == E_REQUEST_DENIED)
647             beep();
648         continue;
649     }
650
651 #ifdef NCURSES_MOUSE_VERSION
652     mousemask(0, (mmask_t *) 0);
653 #endif
654 }
655
656 static void
657 destroy_menus(void)
658 {
659     menu_destroy(mpFile);
660     menu_destroy(mpSelect);
661 #ifdef TRACE
662     menu_destroy(mpTrace);
663 #endif
664     menu_destroy(mpBanner);
665 }
666
667 int
668 main(int argc, char *argv[])
669 {
670     setlocale(LC_ALL, "");
671
672     initscr();
673     noraw();
674     cbreak();
675     noecho();
676
677     if (has_colors()) {
678         start_color();
679         init_pair(1, COLOR_RED, COLOR_BLACK);
680         init_pair(2, COLOR_BLUE, COLOR_WHITE);
681     }
682     build_menus(argc > 1 ? argv[1] : 0);
683     perform_menus();
684     destroy_menus();
685
686     endwin();
687     return EXIT_SUCCESS;
688 }
689 #else
690 int
691 main(void)
692 {
693     printf("This program requires the curses menu library\n");
694     ExitProgram(EXIT_FAILURE);
695 }
696 #endif