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/* 8item_description - 9item_init - 10item_opts - 11item_opts_off - 12item_opts_on - 13item_term - 14item_userptr - 15item_visible - 16menu_back - 17menu_fore - 18menu_format - 19menu_grey - 20menu_init - 21menu_mark - 22menu_opts - 23menu_pad - 24menu_pattern - 25menu_request_by_name - 26menu_request_name - 27menu_sub - 28menu_term - 29menu_userptr - 30set_current_item - 31set_item_init - 32set_item_opts - 33set_item_term - 34set_item_userptr - 35set_menu_grey - 36set_menu_init - 37set_menu_items - 38set_menu_opts - 39set_menu_pad - 40set_menu_pattern - 41set_menu_spacing - 42set_menu_term - 43set_menu_userptr - 44set_top_row - 45top_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 59static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS; 60extern unsigned _nc_tracing; 61static MENU *mpTrace; 62#endif 63#else 64#undef TRACE 65#endif 66 67typedef 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 78static MENU *mpBanner; 79static MENU *mpFile; 80static MENU *mpSelect; 81 82#if !HAVE_STRDUP 83#define strdup my_strdup 84static char * 85strdup(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 */ 97static int 98wGetchar(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 121static int 122menu_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 154static int 155menu_getc(MENU * m) 156{ 157 return wGetchar(menu_win(m)); 158} 159 160static int 161menu_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 180static MENU * 181menu_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 228static void 229menu_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 */ 253static void 254menu_display(MENU * m) 255{ 256 touchwin(menu_win(m)); 257 wrefresh(menu_win(m)); 258} 259 260/*****************************************************************************/ 261 262static void 263build_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 282static int 283perform_file_menu(int cmd) 284{ 285 return menu_driver(mpFile, cmd); 286} 287 288/*****************************************************************************/ 289 290static void 291build_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 369static int 370perform_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 } 379static 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 405static void 406build_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 420static char * 421tracetrace(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 */ 450static bool 451update_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 471static int 472perform_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 510static int 511menu_number(void) 512{ 513 return item_index(current_item(mpBanner)); 514} 515 516static MENU * 517current_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 540static void 541build_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 571static void 572perform_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 656static void 657destroy_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 667int 668main(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 690int 691main(void) 692{ 693 printf("This program requires the curses menu library\n"); 694 ExitProgram(EXIT_FAILURE); 695} 696#endif 697