menu_sys.def revision 1.66
1/* $NetBSD: menu_sys.def,v 1.66 2019/02/16 18:57:21 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 : 2); 145 wadd = ((m->mopt & MC_NOBOX) ? 2 : 4); 146 if (!(m->mopt & MC_NOSHORTCUT)) 147 wadd += 3; 148 149 title = m->title; 150#ifdef MENU_EXPANDS 151 if (m->exp_title) 152 title = m->exp_title; 153#endif 154 if (title) 155 title = MSG_XLAT(title); 156 if (title && title[0] != 0) { 157 /* Allow multiple line titles */ 158 for (tp = title; (ep = strchr(tp, '\n')); tp = ep + 1) { 159 i = ep - tp; 160 wmax = MAX(wmax, i); 161 hadd++; 162 } 163 hadd++; 164 i = strlen(tp); 165 wmax = MAX(wmax, i); 166 if (i != 0) 167 hadd++; 168 } else { 169 m->title = NULL; 170 title = "untitled"; 171 } 172 exithadd = ((m->mopt & MC_NOEXITOPT) ? 0 : 1); 173 174#ifdef MSG_DEFS_H 175 if (y < 0) { 176 /* put menu box below message text */ 177 y = -y; 178 i = msg_row(); 179 if (i > y) 180 y = i; 181 } 182#endif 183 184 /* Calculate h? h == number of visible options. */ 185 if (m->h == 0) 186 m->h = m->numopts + exithadd; 187 188 if (m->h > max_lines - y - hadd) { 189 /* Not enough space for all the options */ 190 if (m->h <= 4 || !(m->mopt & (MC_SCROLL | MC_ALWAYS_SCROLL))) { 191 /* move menu up screen */ 192 y = max_lines - hadd - m->h; 193 if (y < 0) 194 y = 0; 195 } 196 m->h = max_lines - y - hadd; 197 } 198 199 if (m->h < m->numopts + exithadd || m->mopt & MC_ALWAYS_SCROLL) { 200 /* We need to add the scroll text... 201 * The used to be a check for MC_SCROLL here, but it is 202 * fairly pointless - you just don't want the program 203 * to exit on this sort of error. 204 */ 205 if (m->h < 3) { 206 endwin(); 207 (void)fprintf(stderr, 208 "Window too short (m->h %d, m->numopts %d, exithadd %d, y %d, max_lines %d, hadd %d) for menu \"%.30s\"\n", 209 m->h, m->numopts, exithadd, y, max_lines, hadd, 210 title); 211 exit(1); 212 } 213 hadd++; 214 m->h = MIN(m->h, max_lines - y - hadd); 215 i = strlen(scrolltext); 216 wmax = MAX(wmax, i); 217 } 218 219 /* Calculate w? */ 220 if (w == 0) { 221 int l; 222 for (i = 0; i < m->numopts; i++) { 223#ifdef MENU_EXPANDS 224 tp = m->opts[i].opt_exp_name ? 225 m->opts[i].opt_exp_name : 226 MSG_XLAT(m->opts[i].opt_name); 227#else 228 tp = MSG_XLAT(m->opts[i].opt_name); 229#endif 230 if (tp == NULL) 231 continue; 232 l = strlen(tp); 233 wmax = MAX(wmax, l); 234 } 235 w = wmax; 236 } 237 238 /* check and adjust for screen fit */ 239 if (w + wadd > max_cols) { 240 endwin(); 241 (void)fprintf(stderr, 242 "Screen too narrow (%d + %d > %d) for menu \"%s\"\n", 243 w, wadd, max_cols, title); 244 exit(1); 245 246 } 247 248 if (x == -1) 249 x = (max_cols - (w + wadd)) / 2; /* center */ 250 else if (x + w + wadd > max_cols) 251 x = max_cols - (w + wadd); /* right align */ 252 253 if (y == 0) { 254 /* Center - rather than top */ 255 y = (max_lines - hadd - m->h) / 2; 256 } 257 258 /* Get the windows. */ 259 m->mw = newwin(m->h + hadd, w + wadd, y, x); 260 261 if (m->mw == NULL) { 262 endwin(); 263 (void)fprintf(stderr, 264 "Could not create window (%d + %d, %d + %d, %d, %d) for menu \"%s\"\n", 265 m->h, hadd, w, wadd, y, x, title); 266 exit(1); 267 } 268 keypad(m->mw, TRUE); /* enable multi-key assembling for win */ 269 270 /* XXX is it even worth doing this right? */ 271 if (has_colors()) { 272 wbkgd(m->mw, COLOR_PAIR(1)); 273 wattrset(m->mw, COLOR_PAIR(1)); 274 } 275 276 if (m->mopt & MC_SUBMENU) { 277 /* Keep a copy of what is on the screen under the window */ 278 m->sv_mw = newwin(m->h + hadd, w + wadd, y, x); 279 /* 280 * cursrc contains post-doupdate() data, not post-refresh() 281 * data so we must call doupdate to ensure we save the 282 * correct data. Avoids PR 26660. 283 */ 284 doupdate(); 285 if (m->sv_mw) 286 overwrite(curscr, m->sv_mw); 287 } 288} 289 290static char 291opt_ch(menudesc *m, int op_no) 292{ 293 char c; 294 295 if (op_no == m->numopts) 296 return 'x'; 297 298 if (op_no < 25) { 299 c = 'a' + op_no; 300 if (c >= 'x') 301 c++; 302 } else 303 c = 'A' + op_no - 25; 304 return c; 305} 306 307static void 308draw_menu_line(menudesc *m, int opt, int cury, void *arg, const char *text) 309{ 310 int hasbox = m->mopt & MC_NOBOX ? 0 : 1; 311 int noshort = (opt < m->numopts) 312 && (m->opts[opt].opt_flags & OPT_NOSHORT); 313 314 if (m->cursel == opt) { 315 mvwaddstr(m->mw, cury, hasbox, ">"); 316 wstandout(m->mw); 317 } else 318 mvwaddstr(m->mw, cury, hasbox, " "); 319 if (!(m->mopt & MC_NOSHORTCUT) && !noshort) 320 wprintw(m->mw, "%c: ", opt_ch(m, opt)); 321 else if (!(m->mopt & MC_NOSHORTCUT)) 322 wprintw(m->mw, " "); 323 324 if (!text && m->draw_line) 325 m->draw_line(m, opt, arg); 326 else 327 waddstr(m->mw, MSG_XLAT(text)); 328 if (m->cursel == opt) 329 wstandend(m->mw); 330} 331 332static void 333draw_menu(menudesc *m, void *arg) 334{ 335 int opt; 336 int hasbox, cury, maxy; 337 int tadd; 338 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 339 const char *tp, *ep; 340 const char *title; 341 342 hasbox = (m->mopt & MC_NOBOX ? 0 : 1); 343 344 /* Clear the window */ 345 wclear(m->mw); 346 347 tadd = hasbox; 348 title = m->title; 349#ifdef MENU_EXPANDS 350 if (m->exp_title) 351 title = m->exp_title; 352#endif 353 if (title) { 354 for (tp = MSG_XLAT(title); ; tp = ep + 1) { 355 ep = strchr(tp , '\n'); 356 mvwaddnstr(m->mw, tadd++, hasbox + 1, tp, 357 ep ? ep - tp : -1); 358 if (ep == NULL || *ep == 0) 359 break; 360 } 361 tadd++; 362 } 363 364 cury = tadd; 365 maxy = getmaxy(m->mw) - hasbox; 366 if (m->numopts + hasexit > m->h) 367 /* allow for scroll text */ 368 maxy--; 369 370 if (m->cursel == -1) { 371 m->cursel = m->numopts; 372 if (m->h <= m->numopts) 373 m->topline = m->numopts + 1 - m->h; 374 } 375 376 while (m->cursel >= m->topline + m->h) 377 m->topline = MIN(m->topline + m->h, 378 m->numopts + hasexit - m->h); 379 while (m->cursel < m->topline) 380 m->topline = MAX(0, m->topline - m->h); 381 382 for (opt = m->topline; opt < m->numopts; opt++) { 383 if (cury >= maxy) 384 break; 385 draw_menu_line(m, opt, cury++, arg, 386#ifdef MENU_EXPANDS 387 m->opts[opt].opt_exp_name ? 388 m->opts[opt].opt_exp_name : m->opts[opt].opt_name 389#else 390 m->opts[opt].opt_name 391#endif 392 ); 393 } 394 395 /* Add the exit option. */ 396 if (!(m->mopt & MC_NOEXITOPT)) { 397 if (cury < maxy) 398 draw_menu_line(m, m->numopts, cury++, arg, m->exitstr); 399 else 400 opt = 0; 401 } 402 403 /* Add the scroll line */ 404 if (opt != m->numopts || m->topline != 0) 405 mvwaddstr(m->mw, cury, hasbox, scrolltext); 406 407 /* Add the box. */ 408 if (!(m->mopt & MC_NOBOX)) 409 box(m->mw, 0, 0); 410 411 wmove(m->mw, tadd + m->cursel - m->topline, hasbox); 412 wrefresh(m->mw); 413} 414 415 416static void 417/*ARGSUSED*/ 418process_help(menudesc *m) 419{ 420 const char *help = m->helpstr; 421 WINDOW *sv_curscr; 422 int lineoff = 0; 423 int curoff = 0; 424 int again; 425 int winin; 426 427 /* Is there help? */ 428 if (!help) { 429 mbeep(); 430 return; 431 } 432 sv_curscr = newwin(getmaxy(curscr), getmaxx(curscr), 0, 0); 433 if (!sv_curscr) { 434 mbeep(); 435 return; 436 } 437 /* 438 * Save screen contents so we can restore before returning. 439 * cursrc contains post-doupdate() data, not post-refresh() 440 * data so we must call doupdate to ensure we save the 441 * correct data. Avoids PR 26660. 442 */ 443 doupdate(); 444 overwrite(curscr, sv_curscr); 445 touchwin(stdscr); 446 447 help = MSG_XLAT(help); 448 /* Display the help information. */ 449 do { 450 if (lineoff < curoff) { 451 help = MSG_XLAT(m->helpstr); 452 curoff = 0; 453 } 454 while (*help && curoff < lineoff) { 455 if (*help == '\n') 456 curoff++; 457 help++; 458 } 459 460 wclear(stdscr); 461 mvwaddstr(stdscr, 0, 0, 462 "Help: exit: x, page up: u <, page down: d >"); 463 mvwaddstr(stdscr, 2, 0, help); 464 wmove(stdscr, 1, 0); 465 wrefresh(stdscr); 466 467 do { 468 winin = wgetch(stdscr); 469 if (winin < KEY_MIN) 470 winin = tolower(winin); 471 again = 0; 472 switch (winin) { 473 case '<': 474 case 'u': 475 case KEY_UP: 476 case KEY_LEFT: 477 case KEY_PPAGE: 478 if (lineoff) 479 lineoff -= max_lines - 2; 480 else 481 again = 1; 482 break; 483 case '>': 484 case 'd': 485 case KEY_DOWN: 486 case KEY_RIGHT: 487 case KEY_NPAGE: 488 if (*help) 489 lineoff += max_lines - 2; 490 else 491 again = 1; 492 break; 493 case 'q': 494 break; 495 case 'x': 496 winin = 'q'; 497 break; 498 default: 499 again = 1; 500 } 501 if (again) 502 mbeep(); 503 } while (again); 504 } while (winin != 'q'); 505 506 /* Restore the original screen contents */ 507 touchwin(sv_curscr); 508 wnoutrefresh(sv_curscr); 509 delwin(sv_curscr); 510 511 /* Some code thinks that wrefresh(stdout) is a good idea... */ 512 wclear(stdscr); 513} 514 515#ifdef MENU_EXPANDS 516static void 517free_exp_menu_items(menudesc *m) 518{ 519 int i; 520 521 if (m->exp_title != NULL) { 522 free(__UNCONST(m->exp_title)); 523 m->exp_title = NULL; 524 } 525 for (i = 0; i < m->numopts; i++) { 526 if (m->opts[i].opt_exp_name != NULL) { 527 free(__UNCONST(m->opts[i].opt_exp_name)); 528 m->opts[i].opt_exp_name = NULL; 529 } 530 } 531} 532#endif 533 534static void 535process_req(menudesc *m, void *arg, int req) 536{ 537 int ch; 538 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 539 540 switch(req) { 541 542 case REQ_EXECUTE: 543 return; 544 545 case REQ_NEXT_ITEM: 546 ch = m->cursel; 547 for (;;) { 548 ch++; 549 if (ch >= m->numopts + hasexit) { 550 mbeep(); 551 return; 552 } 553 if (hasexit && ch == m->numopts) 554 break; 555 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 556 break; 557 } 558 m->cursel = ch; 559 if (m->cursel >= m->topline + m->h) 560 m->topline = m->cursel - m->h + 1; 561 break; 562 563 case REQ_PREV_ITEM: 564 ch = m->cursel; 565 for (;;) { 566 if (ch <= 0) { 567 mbeep(); 568 return; 569 } 570 ch--; 571 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 572 break; 573 } 574 m->cursel = ch; 575 if (m->cursel < m->topline) 576 m->topline = m->cursel; 577 break; 578 579 case REQ_HELP: 580 process_help(m); 581 break; 582 583 case REQ_REDISPLAY: 584 endwin(); 585 doupdate(); 586 break; 587 588 case REQ_SCROLLUP: 589 if (m->cursel == 0) { 590 mbeep(); 591 return; 592 } 593 m->topline = MAX(0, m->topline - m->h); 594 m->cursel = MAX(0, m->cursel - m->h); 595 wclear(m->mw); 596 break; 597 598 case REQ_SCROLLDOWN: 599 if (m->cursel >= m->numopts + hasexit - 1) { 600 mbeep(); 601 return; 602 } 603 m->topline = MIN(m->topline + m->h, 604 MAX(m->numopts + hasexit - m->h, 0)); 605 m->cursel = MIN(m->numopts + hasexit - 1, m->cursel + m->h); 606 wclear(m->mw); 607 break; 608 609 default: 610 ch = req; 611 if (ch == 'x' && hasexit) { 612 m->cursel = m->numopts; 613 break; 614 } 615 if (m->mopt & MC_NOSHORTCUT) { 616 mbeep(); 617 return; 618 } 619 if (ch > 'z') 620 ch = 255; 621 if (ch >= 'a') { 622 if (ch > 'x') 623 ch--; 624 ch = ch - 'a'; 625 } else 626 ch = 25 + ch - 'A'; 627 if (ch < 0 || ch >= m->numopts) { 628 mbeep(); 629 return; 630 } 631 if (m->opts[ch].opt_flags & OPT_IGNORE) { 632 mbeep(); 633 return; 634 } 635 m->cursel = ch; 636 } 637 638 draw_menu(m, arg); 639} 640 641int 642menu_init(void) 643{ 644 int i; 645 646 if (__menu_init) 647 return 0; 648 649#ifdef USER_MENU_INIT 650 if (USER_MENU_INIT) 651 return 1; 652#endif 653 654 if (initscr() == NULL) 655 return 1; 656 657 cbreak(); 658 noecho(); 659 660 /* XXX Should be configurable but it almost isn't worth it. */ 661 if (has_colors()) { 662 start_color(); 663 init_pair(1, COLOR_WHITE, COLOR_BLUE); 664 bkgd(COLOR_PAIR(1)); 665 attrset(COLOR_PAIR(1)); 666 } 667 668 max_lines = getmaxy(stdscr); 669 max_cols = getmaxx(stdscr); 670 keypad(stdscr, TRUE); 671#ifdef DYNAMIC_MENUS 672 num_menus = DYN_INIT_NUM; 673 while (num_menus < DYN_MENU_START) 674 num_menus *= 2; 675 menu_list = malloc(sizeof *menu_list * num_menus); 676 if (menu_list == NULL) 677 return 2; 678 (void)memset(menu_list, 0, sizeof *menu_list * num_menus); 679 for (i = 0; i < DYN_MENU_START; i++) 680 menu_list[i] = &menu_def[i]; 681#endif 682 683 __menu_init = 1; 684 return 0; 685} 686 687void 688process_menu(int num, void *arg) 689{ 690 int sel = 0; 691 int req, rv; 692 menu_ent *opt; 693 694 menudesc *m; 695 696 /* Initialize? */ 697 if (menu_init()) { 698 __menu_initerror(); 699 return; 700 } 701 702 if (num < 0 || num >= num_menus) 703 return; 704 m = &MENUS(num); 705 if (m == NULL) 706 return; 707 708 /* Default to select option 0 and display from 0, skip any 709 * disabled options at the top of the menu. */ 710 m->topline = 0; 711 if ((m->mopt & (MC_DFLTEXIT | MC_NOEXITOPT)) == MC_DFLTEXIT) { 712 m->cursel = -1; 713 } else if (m->opts != NULL) { 714 for (m->cursel = 0; m->cursel < m->numopts; m->cursel++) 715 if ((m->opts[m->cursel].opt_flags & OPT_IGNORE) == 0) 716 break; 717 } 718 719 for (;;) { 720 if (isendwin()) 721 /* I'm not sure this is needed with netbsd's curses */ 722 doupdate(); 723#ifdef MENU_EXPANDS 724 /* Expand the menu items, if needed */ 725 if (m->expand_act && m->mw == NULL) 726 (*m->expand_act)(m, arg); 727#endif 728 729 /* Process the display action */ 730 if (m->post_act) 731 (*m->post_act)(m, arg); 732 if (m->mw == NULL) 733 init_menu(m); 734 draw_menu(m, arg); 735 736 while ((req = menucmd(m->mw)) != REQ_EXECUTE) 737 process_req(m, arg, req); 738 739 sel = m->cursel; 740 if (!(m->mopt & MC_NOCLEAR)) { 741 wclear(m->mw); 742 if (m->sv_mw) 743 overwrite(m->sv_mw, m->mw); 744 wnoutrefresh(m->mw); 745 } 746 747 /* Process the items */ 748 if (sel >= m->numopts) 749 /* exit option */ 750 break; 751 752 opt = &m->opts[sel]; 753 if (opt->opt_flags & OPT_IGNORE) 754 continue; 755 if (opt->opt_flags & OPT_ENDWIN) 756 endwin(); 757 if (opt->opt_action) { 758 rv = (*opt->opt_action)(m, arg); 759 if (rv == -1) 760 continue; 761 else if (rv != 0) 762 break; 763 } 764 765 if (opt->opt_menu != -1) { 766 if (!(opt->opt_flags & OPT_SUB)) { 767 num = opt->opt_menu; 768 wclear(m->mw); 769 if (m->sv_mw) { 770 overwrite(m->sv_mw, m->mw); 771 delwin(m->sv_mw); 772 m->sv_mw = NULL; 773 } 774 wnoutrefresh(m->mw); 775 delwin(m->mw); 776 m->mw = NULL; 777 m = &MENUS(num); 778 continue; 779 } 780 process_menu(opt->opt_menu, arg); 781 } 782 if (opt->opt_flags & OPT_EXIT) 783 break; 784 } 785 786 if (m->mopt & MC_NOCLEAR) { 787 wclear(m->mw); 788 if (m->sv_mw) 789 overwrite(m->sv_mw, m->mw); 790 wnoutrefresh(m->mw); 791 } 792 793#ifdef MENU_EXPANDS 794 /* Free expanded menu items, if any */ 795 free_exp_menu_items(m); 796#endif 797 798 /* Process the exit action */ 799 if (m->exit_act) 800 (*m->exit_act)(m, arg); 801 delwin(m->mw); 802 m->mw = NULL; 803 if (m->sv_mw) { 804 delwin(m->sv_mw); 805 m->sv_mw = NULL; 806 } 807} 808 809 810void 811set_menu_numopts(int menu, int numopts) 812{ 813 814 MENUS(menu).numopts = numopts; 815} 816 817menudesc * 818get_menudesc(int menu) 819{ 820 if (menu < 0 || menu >= num_menus) 821 return NULL; 822 823 return &MENUS(menu); 824} 825 826/* Control L is end of standard routines, remaining only for dynamic. */ 827 828/* Beginning of routines for dynamic menus. */ 829 830static int 831double_menus(void) 832{ 833 menudesc **temp; 834 int sz = sizeof *menu_list * num_menus; 835 836 temp = realloc(menu_list, sz * 2); 837 if (temp == NULL) 838 return 0; 839 (void)memset(temp + num_menus, 0, sz); 840 menu_list = temp; 841 num_menus *= 2; 842 843 return 1; 844} 845 846int 847new_menu(const char *title, menu_ent *opts, int numopts, 848 int x, int y, int h, int w, int mopt, 849 void (*post_act)(menudesc *, void *), 850 void (*draw_line)(menudesc *, int, void *), 851 void (*exit_act)(menudesc *, void *), 852 const char *help, const char *exit_str) 853{ 854 int ix; 855 menudesc *m; 856 857 /* Find free menu entry. */ 858 for (ix = DYN_MENU_START; ; ix++) { 859 if (ix >= num_menus && !double_menus()) 860 return -1; 861 m = menu_list[ix]; 862 if (m == NULL) { 863 m = calloc(sizeof *m, 1); 864 if (m == NULL) 865 return -1; 866 menu_list[ix] = m; 867 break; 868 } 869 if (!(m->mopt & MC_VALID)) 870 break; 871 } 872 873 /* Set Entries */ 874 m->title = title; 875#ifdef MENU_EXPANDS 876 m->exp_title = NULL; 877#endif 878 m->opts = opts; 879 m->numopts = numopts; 880 m->x = x; 881 m->y = y; 882 m->h = h; 883 m->w = w; 884 m->mopt = mopt | MC_VALID; 885#ifdef MENU_EXPANDS 886 m->expand_act = NULL; 887#endif 888 m->post_act = post_act; 889 m->draw_line = draw_line; 890 m->exit_act = exit_act; 891 m->helpstr = help; 892 m->exitstr = exit_str ? exit_str : "Exit"; 893 894 return ix; 895} 896 897void 898free_menu(int menu_no) 899{ 900 menudesc *m; 901 902 if (menu_no < 0 || menu_no >= num_menus) 903 return; 904 905 m = menu_list[menu_no]; 906 if (!(m->mopt & MC_VALID)) 907 return; 908 if (m->mw != NULL) 909 delwin(m->mw); 910 memset(m, 0, sizeof *m); 911} 912