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