menu_sys.def revision 1.69
1/* $NetBSD: menu_sys.def,v 1.69 2019/04/12 15:59:34 martin Exp $ */ 2 3/* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35/* menu_sys.defs -- Menu system standard routines. */ 36 37#include <string.h> 38#include <ctype.h> 39 40#define REQ_EXECUTE 1000 41#define REQ_NEXT_ITEM 1001 42#define REQ_PREV_ITEM 1002 43#define REQ_REDISPLAY 1003 44#define REQ_SCROLLDOWN 1004 45#define REQ_SCROLLUP 1005 46#define REQ_HELP 1006 47 48/* Macros */ 49#define MAX(x,y) ((x)>(y)?(x):(y)) 50#define MIN(x,y) ((x)<(y)?(x):(y)) 51 52/* Initialization state. */ 53static int __menu_init = 0; 54static int max_lines = 0, max_cols = 0; 55#ifndef scrolltext 56static const char *scrolltext = " <: page up, >: page down"; 57#endif 58 59#ifdef DYNAMIC_MENUS 60static int num_menus = 0; 61#define DYN_INIT_NUM 32 62static menudesc **menu_list; 63#define MENUS(n) (*(menu_list[n])) 64#else 65#define MENUS(n) (menu_def[n]) 66#endif 67 68/* prototypes for in here! */ 69static void init_menu(menudesc *m); 70static char opt_ch(menudesc *m, int op_no); 71static void draw_menu(menudesc *m, void *arg); 72static void process_help(menudesc *m); 73static void process_req(menudesc *m, void *arg, int req); 74static int menucmd(WINDOW *w); 75 76#ifndef NULL 77#define NULL 0 78#endif 79 80/* menu system processing routines */ 81#define mbeep() (void)fputc('\a', stderr) 82 83static int 84menucmd(WINDOW *w) 85{ 86 int ch; 87 88 while (TRUE) { 89 ch = wgetch(w); 90 91 switch (ch) { 92 case '\n': 93 return REQ_EXECUTE; 94 case '\016': /* Control-P */ 95 case KEY_DOWN: 96 return REQ_NEXT_ITEM; 97 case '\020': /* Control-N */ 98 case KEY_UP: 99 return REQ_PREV_ITEM; 100 case '\014': /* Control-L */ 101 return REQ_REDISPLAY; 102 case '<': 103 case '\010': /* Control-H (backspace) */ 104 case KEY_PPAGE: 105 case KEY_LEFT: 106 return REQ_SCROLLUP; 107 case '\026': /* Control-V */ 108 case '>': 109 case ' ': 110 case KEY_NPAGE: 111 case KEY_RIGHT: 112 return REQ_SCROLLDOWN; 113 case '?': 114 return REQ_HELP; 115 case '\033': /* esc-v is scroll down */ 116 ch = wgetch(w); 117 if (ch == 'v') 118 return REQ_SCROLLUP; 119 else 120 ch = 0; /* zap char so we beep */ 121 } 122 123 if (isalpha(ch)) 124 return ch; 125 126 mbeep(); 127 wrefresh(w); 128 } 129} 130 131static void 132init_menu(menudesc *m) 133{ 134 int wmax; 135 int hadd, wadd, exithadd; 136 int i; 137 int x, y, w; 138 const char *title, *tp, *ep; 139 140 x = m->x; 141 y = m->y; 142 w = m->w; 143 wmax = 0; 144 hadd = ((m->mopt & MC_NOBOX) ? 0 : 145 ((m->mopt & MC_CONTINUOUS) ? 1 : 2)); 146 wadd = ((m->mopt & MC_NOBOX) ? 2 : 4); 147 if (!(m->mopt & MC_NOSHORTCUT)) 148 wadd += 3; 149 150 title = m->title; 151#ifdef MENU_EXPANDS 152 if (m->exp_title) 153 title = m->exp_title; 154#endif 155 if (title) 156 title = MSG_XLAT(title); 157 if (title && title[0] != 0) { 158 /* Allow multiple line titles */ 159 for (tp = title; (ep = strchr(tp, '\n')); tp = ep + 1) { 160 i = ep - tp; 161 wmax = MAX(wmax, i); 162 hadd++; 163 } 164 hadd++; 165 i = strlen(tp); 166 wmax = MAX(wmax, i); 167 if (i != 0) 168 hadd++; 169 } else { 170 m->title = NULL; 171 title = "untitled"; 172 } 173 exithadd = ((m->mopt & MC_NOEXITOPT) ? 0 : 1); 174 175#ifdef MSG_DEFS_H 176 if (y < 0) { 177 /* put menu box below message text */ 178 y = -y; 179 i = msg_row(); 180 if (i > y) 181 y = i; 182 } 183#endif 184 185 /* Calculate h? h == number of visible options. */ 186 if (m->h == 0) 187 m->h = m->numopts + exithadd; 188 189 if (m->h > max_lines - y - hadd) { 190 /* Not enough space for all the options */ 191 if (m->h <= 4 || !(m->mopt & (MC_SCROLL | MC_ALWAYS_SCROLL))) { 192 /* move menu up screen */ 193 y = max_lines - hadd - m->h; 194 if (y < 0) 195 y = 0; 196 } 197 m->h = max_lines - y - hadd; 198 } 199 200 if (m->h < m->numopts + exithadd || m->mopt & MC_ALWAYS_SCROLL) { 201 /* We need to add the scroll text... 202 * The used to be a check for MC_SCROLL here, but it is 203 * fairly pointless - you just don't want the program 204 * to exit on this sort of error. 205 */ 206 if (m->h < 3) { 207 endwin(); 208 (void)fprintf(stderr, 209 "Window too short (m->h %d, m->numopts %d, exithadd %d, y %d, max_lines %d, hadd %d) for menu \"%.30s\"\n", 210 m->h, m->numopts, exithadd, y, max_lines, hadd, 211 title); 212 exit(1); 213 } 214 hadd++; 215 m->h = MIN(m->h, max_lines - y - hadd); 216 i = strlen(scrolltext); 217 wmax = MAX(wmax, i); 218 } 219 220 /* Calculate w? */ 221 if (w == 0) { 222 int l; 223 for (i = 0; i < m->numopts; i++) { 224#ifdef MENU_EXPANDS 225 tp = m->opts[i].opt_exp_name ? 226 m->opts[i].opt_exp_name : 227 MSG_XLAT(m->opts[i].opt_name); 228#else 229 tp = MSG_XLAT(m->opts[i].opt_name); 230#endif 231 if (tp == NULL) 232 continue; 233 l = strlen(tp); 234 wmax = MAX(wmax, l); 235 } 236 w = wmax; 237 } 238 239 /* check and adjust for screen fit */ 240 if (w + wadd > max_cols) { 241 endwin(); 242 (void)fprintf(stderr, 243 "Screen too narrow (%d + %d > %d) for menu \"%s\"\n", 244 w, wadd, max_cols, title); 245 exit(1); 246 247 } 248 249 if (x == -1) 250 x = (max_cols - (w + wadd)) / 2; /* center */ 251 else if (x + w + wadd > max_cols) 252 x = max_cols - (w + wadd); /* right align */ 253 254 if (y == 0) { 255 /* Center - rather than top */ 256 y = (max_lines - hadd - m->h) / 2; 257 } 258 259 /* Get the windows. */ 260 m->mw = newwin(m->h + hadd, w + wadd, y, x); 261 262 if (m->mw == NULL) { 263 endwin(); 264 (void)fprintf(stderr, 265 "Could not create window (%d + %d, %d + %d, %d, %d) for menu \"%s\"\n", 266 m->h, hadd, w, wadd, y, x, title); 267 exit(1); 268 } 269 keypad(m->mw, TRUE); /* enable multi-key assembling for win */ 270 271 /* XXX is it even worth doing this right? */ 272 if (has_colors()) { 273 wbkgd(m->mw, COLOR_PAIR(1)); 274 wattrset(m->mw, COLOR_PAIR(1)); 275 } 276 277 if (m->mopt & MC_SUBMENU) { 278 /* Keep a copy of what is on the screen under the window */ 279 m->sv_mw = newwin(m->h + hadd, w + wadd, y, x); 280 /* 281 * cursrc contains post-doupdate() data, not post-refresh() 282 * data so we must call doupdate to ensure we save the 283 * correct data. Avoids PR 26660. 284 */ 285 doupdate(); 286 if (m->sv_mw) 287 overwrite(curscr, m->sv_mw); 288 } 289} 290 291static char 292opt_ch(menudesc *m, int op_no) 293{ 294 char c; 295 296 if (op_no == m->numopts) 297 return 'x'; 298 299 if (op_no < 25) { 300 c = 'a' + op_no; 301 if (c >= 'x') 302 c++; 303 } else 304 c = 'A' + op_no - 25; 305 return c; 306} 307 308static void 309draw_menu_line(menudesc *m, int opt, int cury, void *arg, const char *text) 310{ 311 int hasbox = m->mopt & MC_NOBOX ? 0 : 1; 312 int noshort = (opt < m->numopts) 313 && (m->opts[opt].opt_flags & OPT_NOSHORT); 314 315 if (m->cursel == opt) { 316 mvwaddstr(m->mw, cury, hasbox, ">"); 317 wstandout(m->mw); 318 } else 319 mvwaddstr(m->mw, cury, hasbox, " "); 320 if (!(m->mopt & MC_NOSHORTCUT) && !noshort) 321 wprintw(m->mw, "%c: ", opt_ch(m, opt)); 322 else if (!(m->mopt & MC_NOSHORTCUT)) 323 wprintw(m->mw, " "); 324 325 if (!text && m->draw_line) 326 m->draw_line(m, opt, arg); 327 else 328 waddstr(m->mw, MSG_XLAT(text)); 329 if (m->cursel == opt) 330 wstandend(m->mw); 331} 332 333static void 334draw_menu(menudesc *m, void *arg) 335{ 336 int opt; 337 int hasbox, cury, maxy; 338 int tadd; 339 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 340 const char *tp, *ep; 341 const char *title; 342 343 hasbox = (m->mopt & MC_NOBOX ? 0 : 1); 344 345 /* Clear the window */ 346 wclear(m->mw); 347 348 tadd = hasbox; 349 title = m->title; 350#ifdef MENU_EXPANDS 351 if (m->exp_title) 352 title = m->exp_title; 353#endif 354 if (title) { 355 for (tp = MSG_XLAT(title); ; tp = ep + 1) { 356 ep = strchr(tp , '\n'); 357 mvwaddnstr(m->mw, tadd++, hasbox + 1, tp, 358 ep ? ep - tp : -1); 359 if (ep == NULL || *ep == 0) 360 break; 361 } 362 if ((m->mopt & MC_CONTINUOUS) == 0) 363 tadd++; 364 } 365 366 cury = tadd; 367 maxy = getmaxy(m->mw) - hasbox; 368 if (m->numopts + hasexit > m->h) 369 /* allow for scroll text */ 370 maxy--; 371 372 if (m->cursel == -1) { 373 m->cursel = m->numopts; 374 if (m->h <= m->numopts) 375 m->topline = m->numopts + 1 - m->h; 376 } 377 378 while (m->cursel >= m->topline + m->h) 379 m->topline = MIN(m->topline + m->h, 380 m->numopts + hasexit - m->h); 381 while (m->cursel < m->topline) 382 m->topline = MAX(0, m->topline - m->h); 383 384 for (opt = m->topline; opt < m->numopts; opt++) { 385 if (cury >= maxy) 386 break; 387 draw_menu_line(m, opt, cury++, arg, 388#ifdef MENU_EXPANDS 389 m->opts[opt].opt_exp_name ? 390 m->opts[opt].opt_exp_name : m->opts[opt].opt_name 391#else 392 m->opts[opt].opt_name 393#endif 394 ); 395 } 396 397 /* Add the exit option. */ 398 if (!(m->mopt & MC_NOEXITOPT)) { 399 if (cury < maxy) 400 draw_menu_line(m, m->numopts, cury++, arg, m->exitstr); 401 else 402 opt = 0; 403 } 404 405 /* Add the scroll line */ 406 if (opt != m->numopts || m->topline != 0) 407 mvwaddstr(m->mw, cury, hasbox, scrolltext); 408 409 /* Add the box. */ 410 if (!(m->mopt & MC_NOBOX)) 411 box(m->mw, 0, 0); 412 413 wmove(m->mw, tadd + m->cursel - m->topline, hasbox); 414 wrefresh(m->mw); 415} 416 417 418static void 419/*ARGSUSED*/ 420process_help(menudesc *m) 421{ 422 const char *help = m->helpstr; 423 WINDOW *sv_curscr; 424 int lineoff = 0; 425 int curoff = 0; 426 int again; 427 int winin; 428 429 /* Is there help? */ 430 if (!help) { 431 mbeep(); 432 return; 433 } 434 sv_curscr = newwin(getmaxy(curscr), getmaxx(curscr), 0, 0); 435 if (!sv_curscr) { 436 mbeep(); 437 return; 438 } 439 /* 440 * Save screen contents so we can restore before returning. 441 * cursrc contains post-doupdate() data, not post-refresh() 442 * data so we must call doupdate to ensure we save the 443 * correct data. Avoids PR 26660. 444 */ 445 doupdate(); 446 overwrite(curscr, sv_curscr); 447 touchwin(stdscr); 448 449 help = MSG_XLAT(help); 450 /* Display the help information. */ 451 do { 452 if (lineoff < curoff) { 453 help = MSG_XLAT(m->helpstr); 454 curoff = 0; 455 } 456 while (*help && curoff < lineoff) { 457 if (*help == '\n') 458 curoff++; 459 help++; 460 } 461 462 wclear(stdscr); 463 mvwaddstr(stdscr, 0, 0, 464 "Help: exit: x, page up: u <, page down: d >"); 465 mvwaddstr(stdscr, 2, 0, help); 466 wmove(stdscr, 1, 0); 467 wrefresh(stdscr); 468 469 do { 470 winin = wgetch(stdscr); 471 if (winin < KEY_MIN) 472 winin = tolower(winin); 473 again = 0; 474 switch (winin) { 475 case '<': 476 case 'u': 477 case KEY_UP: 478 case KEY_LEFT: 479 case KEY_PPAGE: 480 if (lineoff) 481 lineoff -= max_lines - 2; 482 else 483 again = 1; 484 break; 485 case '>': 486 case 'd': 487 case KEY_DOWN: 488 case KEY_RIGHT: 489 case KEY_NPAGE: 490 if (*help) 491 lineoff += max_lines - 2; 492 else 493 again = 1; 494 break; 495 case 'q': 496 break; 497 case 'x': 498 winin = 'q'; 499 break; 500 default: 501 again = 1; 502 } 503 if (again) 504 mbeep(); 505 } while (again); 506 } while (winin != 'q'); 507 508 /* Restore the original screen contents */ 509 touchwin(sv_curscr); 510 wnoutrefresh(sv_curscr); 511 delwin(sv_curscr); 512 513 /* Some code thinks that wrefresh(stdout) is a good idea... */ 514 wclear(stdscr); 515} 516 517#ifdef MENU_EXPANDS 518static void 519free_exp_menu_items(menudesc *m) 520{ 521 int i; 522 523 if (m->exp_title != NULL) { 524 free(__UNCONST(m->exp_title)); 525 m->exp_title = NULL; 526 } 527 for (i = 0; i < m->numopts; i++) { 528 if (m->opts[i].opt_exp_name != NULL) { 529 free(__UNCONST(m->opts[i].opt_exp_name)); 530 m->opts[i].opt_exp_name = NULL; 531 } 532 } 533} 534#endif 535 536static void 537process_req(menudesc *m, void *arg, int req) 538{ 539 int ch; 540 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 541 542 switch(req) { 543 544 case REQ_EXECUTE: 545 return; 546 547 case REQ_NEXT_ITEM: 548 ch = m->cursel; 549 for (;;) { 550 ch++; 551 if (ch >= m->numopts + hasexit) { 552 mbeep(); 553 return; 554 } 555 if (hasexit && ch == m->numopts) 556 break; 557 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 558 break; 559 } 560 m->cursel = ch; 561 if (m->cursel >= m->topline + m->h) 562 m->topline = m->cursel - m->h + 1; 563 break; 564 565 case REQ_PREV_ITEM: 566 ch = m->cursel; 567 for (;;) { 568 if (ch <= 0) { 569 mbeep(); 570 return; 571 } 572 ch--; 573 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 574 break; 575 } 576 m->cursel = ch; 577 if (m->cursel < m->topline) 578 m->topline = m->cursel; 579 break; 580 581 case REQ_HELP: 582 process_help(m); 583 break; 584 585 case REQ_REDISPLAY: 586 endwin(); 587 doupdate(); 588 break; 589 590 case REQ_SCROLLUP: 591 if (m->cursel == 0) { 592 mbeep(); 593 return; 594 } 595 m->topline = MAX(0, m->topline - m->h); 596 m->cursel = MAX(0, m->cursel - m->h); 597 wclear(m->mw); 598 break; 599 600 case REQ_SCROLLDOWN: 601 if (m->cursel >= m->numopts + hasexit - 1) { 602 mbeep(); 603 return; 604 } 605 m->topline = MIN(m->topline + m->h, 606 MAX(m->numopts + hasexit - m->h, 0)); 607 m->cursel = MIN(m->numopts + hasexit - 1, m->cursel + m->h); 608 wclear(m->mw); 609 break; 610 611 default: 612 ch = req; 613 if (ch == 'x' && hasexit) { 614 m->cursel = m->numopts; 615 break; 616 } 617 if (m->mopt & MC_NOSHORTCUT) { 618 mbeep(); 619 return; 620 } 621 if (ch > 'z') 622 ch = 255; 623 if (ch >= 'a') { 624 if (ch > 'x') 625 ch--; 626 ch = ch - 'a'; 627 } else 628 ch = 25 + ch - 'A'; 629 if (ch < 0 || ch >= m->numopts) { 630 mbeep(); 631 return; 632 } 633 if (m->opts[ch].opt_flags & OPT_IGNORE) { 634 mbeep(); 635 return; 636 } 637 m->cursel = ch; 638 } 639 640 draw_menu(m, arg); 641} 642 643int 644menu_init(void) 645{ 646 int i; 647 648 if (__menu_init) 649 return 0; 650 651#ifdef USER_MENU_INIT 652 if (USER_MENU_INIT) 653 return 1; 654#endif 655 656 if (initscr() == NULL) 657 return 1; 658 659 cbreak(); 660 noecho(); 661 662 /* XXX Should be configurable but it almost isn't worth it. */ 663 if (has_colors()) { 664 start_color(); 665 init_pair(1, COLOR_WHITE, COLOR_BLUE); 666 bkgd(COLOR_PAIR(1)); 667 attrset(COLOR_PAIR(1)); 668 } 669 670 max_lines = getmaxy(stdscr); 671 max_cols = getmaxx(stdscr); 672 keypad(stdscr, TRUE); 673#ifdef DYNAMIC_MENUS 674 num_menus = DYN_INIT_NUM; 675 while (num_menus < DYN_MENU_START) 676 num_menus *= 2; 677 menu_list = malloc(sizeof *menu_list * num_menus); 678 if (menu_list == NULL) 679 return 2; 680 (void)memset(menu_list, 0, sizeof *menu_list * num_menus); 681 for (i = 0; i < DYN_MENU_START; i++) 682 menu_list[i] = &menu_def[i]; 683#endif 684 685 __menu_init = 1; 686 return 0; 687} 688 689void 690process_menu(int num, void *arg) 691{ 692 int sel = 0; 693 int req, rv; 694 menu_ent *opt; 695 696 menudesc *m; 697 698 /* Initialize? */ 699 if (menu_init()) { 700 __menu_initerror(); 701 return; 702 } 703 704 if (num < 0 || num >= num_menus) 705 return; 706 m = &MENUS(num); 707 if (m == NULL) 708 return; 709 710 /* Default to select option 0 and display from 0, skip any 711 * disabled options at the top of the menu. */ 712 m->topline = 0; 713 if ((m->mopt & (MC_DFLTEXIT | MC_NOEXITOPT)) == MC_DFLTEXIT) { 714 m->cursel = -1; 715 } else if (m->opts != NULL) { 716 for (m->cursel = 0; m->cursel < m->numopts; m->cursel++) 717 if ((m->opts[m->cursel].opt_flags & OPT_IGNORE) == 0) 718 break; 719 } 720 721 for (;;) { 722 if (isendwin()) 723 /* I'm not sure this is needed with netbsd's curses */ 724 doupdate(); 725#ifdef MENU_EXPANDS 726 /* Expand the menu items, if needed */ 727 if (m->expand_act && m->mw == NULL) 728 (*m->expand_act)(m, arg); 729#endif 730 731 /* Process the display action */ 732 if (m->post_act) 733 (*m->post_act)(m, arg); 734 if (m->mw == NULL) 735 init_menu(m); 736 draw_menu(m, arg); 737 738 while ((req = menucmd(m->mw)) != REQ_EXECUTE) 739 process_req(m, arg, req); 740 741 sel = m->cursel; 742 if (!(m->mopt & MC_NOCLEAR)) { 743 wclear(m->mw); 744 if (m->sv_mw) 745 overwrite(m->sv_mw, m->mw); 746 wnoutrefresh(m->mw); 747 } 748 749 /* Process the items */ 750 if (sel >= m->numopts) 751 /* exit option */ 752 break; 753 754 opt = &m->opts[sel]; 755 if (opt->opt_flags & OPT_IGNORE) 756 continue; 757 if (opt->opt_flags & OPT_ENDWIN) 758 endwin(); 759 if (opt->opt_action) { 760 rv = (*opt->opt_action)(m, arg); 761 if (rv == -1) 762 continue; 763 else if (rv != 0) 764 break; 765 } 766 767 if (opt->opt_menu != -1) { 768 if (!(opt->opt_flags & OPT_SUB)) { 769 num = opt->opt_menu; 770 wclear(m->mw); 771 if (m->sv_mw) { 772 overwrite(m->sv_mw, m->mw); 773 delwin(m->sv_mw); 774 m->sv_mw = NULL; 775 } 776 wnoutrefresh(m->mw); 777 delwin(m->mw); 778 m->mw = NULL; 779 m = &MENUS(num); 780 continue; 781 } 782 process_menu(opt->opt_menu, arg); 783 } 784 if (opt->opt_flags & OPT_EXIT) 785 break; 786 } 787 788 if (m->mopt & MC_NOCLEAR) { 789 wclear(m->mw); 790 if (m->sv_mw) 791 overwrite(m->sv_mw, m->mw); 792 wnoutrefresh(m->mw); 793 } 794 795#ifdef MENU_EXPANDS 796 /* Free expanded menu items, if any */ 797 free_exp_menu_items(m); 798#endif 799 800 /* Process the exit action */ 801 if (m->exit_act) 802 (*m->exit_act)(m, arg); 803 delwin(m->mw); 804 m->mw = NULL; 805 if (m->sv_mw) { 806 delwin(m->sv_mw); 807 m->sv_mw = NULL; 808 } 809} 810 811 812void 813set_menu_numopts(int menu, int numopts) 814{ 815 816 MENUS(menu).numopts = numopts; 817} 818 819menudesc * 820get_menudesc(int menu) 821{ 822 if (menu < 0 || menu >= num_menus) 823 return NULL; 824 825 return &MENUS(menu); 826} 827 828/* Control L is end of standard routines, remaining only for dynamic. */ 829 830/* Beginning of routines for dynamic menus. */ 831 832static int 833double_menus(void) 834{ 835 menudesc **temp; 836 int sz = sizeof *menu_list * num_menus; 837 838 temp = realloc(menu_list, sz * 2); 839 if (temp == NULL) 840 return 0; 841 (void)memset(temp + num_menus, 0, sz); 842 menu_list = temp; 843 num_menus *= 2; 844 845 return 1; 846} 847 848int 849new_menu(const char *title, menu_ent *opts, int numopts, 850 int x, int y, int h, int w, int mopt, 851 void (*post_act)(menudesc *, void *), 852 void (*draw_line)(menudesc *, int, void *), 853 void (*exit_act)(menudesc *, void *), 854 const char *help, const char *exit_str) 855{ 856 int ix; 857 menudesc *m; 858 859 /* Find free menu entry. */ 860 for (ix = DYN_MENU_START; ; ix++) { 861 if (ix >= num_menus && !double_menus()) 862 return -1; 863 m = menu_list[ix]; 864 if (m == NULL) { 865 m = calloc(sizeof *m, 1); 866 if (m == NULL) 867 return -1; 868 menu_list[ix] = m; 869 break; 870 } 871 if (!(m->mopt & MC_VALID)) 872 break; 873 } 874 875 /* Set Entries */ 876 m->title = title; 877#ifdef MENU_EXPANDS 878 m->exp_title = NULL; 879#endif 880 m->opts = opts; 881 m->numopts = numopts; 882 m->x = x; 883 m->y = y; 884 m->h = h; 885 m->w = w; 886 m->mopt = mopt | MC_VALID; 887#ifdef MENU_EXPANDS 888 m->expand_act = NULL; 889#endif 890 m->post_act = post_act; 891 m->draw_line = draw_line; 892 m->exit_act = exit_act; 893 m->helpstr = help; 894 m->exitstr = exit_str ? exit_str : "Exit"; 895 896 return ix; 897} 898 899void 900free_menu(int menu_no) 901{ 902 menudesc *m; 903 904 if (menu_no < 0 || menu_no >= num_menus) 905 return; 906 907 m = menu_list[menu_no]; 908 if (!(m->mopt & MC_VALID)) 909 return; 910 if (m->mw != NULL) 911 delwin(m->mw); 912 memset(m, 0, sizeof *m); 913} 914