menu_sys.def revision 1.62
1/* $NetBSD: menu_sys.def,v 1.62 2019/01/04 15:27:19 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 int noshort = (opt < m->numopts) 305 && (m->opts[opt].opt_flags & OPT_NOSHORT); 306 307 if (m->cursel == opt) { 308 mvwaddstr(m->mw, cury, hasbox, ">"); 309 wstandout(m->mw); 310 } else 311 mvwaddstr(m->mw, cury, hasbox, " "); 312 if (!(m->mopt & MC_NOSHORTCUT) && !noshort) 313 wprintw(m->mw, "%c: ", opt_ch(m, opt)); 314 else if (!(m->mopt & MC_NOSHORTCUT)) 315 wprintw(m->mw, " "); 316 317 if (!text && m->draw_line) 318 m->draw_line(m, opt, arg); 319 else 320 waddstr(m->mw, MSG_XLAT(text)); 321 if (m->cursel == opt) 322 wstandend(m->mw); 323} 324 325static void 326draw_menu(menudesc *m, void *arg) 327{ 328 int opt; 329 int hasbox, cury, maxy; 330 int tadd; 331 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 332 const char *tp, *ep; 333 334 hasbox = (m->mopt & MC_NOBOX ? 0 : 1); 335 336 /* Clear the window */ 337 wclear(m->mw); 338 339 tadd = hasbox; 340 if (m->title) { 341 for (tp = MSG_XLAT(m->title); ; tp = ep + 1) { 342 ep = strchr(tp , '\n'); 343 mvwaddnstr(m->mw, tadd++, hasbox + 1, tp, 344 ep ? ep - tp : -1); 345 if (ep == NULL || *ep == 0) 346 break; 347 } 348 tadd++; 349 } 350 351 cury = tadd; 352 maxy = getmaxy(m->mw) - hasbox; 353 if (m->numopts + hasexit > m->h) 354 /* allow for scroll text */ 355 maxy--; 356 357 if (m->cursel == -1) { 358 m->cursel = m->numopts; 359 if (m->h <= m->numopts) 360 m->topline = m->numopts + 1 - m->h; 361 } 362 363 while (m->cursel >= m->topline + m->h) 364 m->topline = MIN(m->topline + m->h, 365 m->numopts + hasexit - m->h); 366 while (m->cursel < m->topline) 367 m->topline = MAX(0, m->topline - m->h); 368 369 for (opt = m->topline; opt < m->numopts; opt++) { 370 if (cury >= maxy) 371 break; 372 draw_menu_line(m, opt, cury++, arg, 373#ifdef MENU_EXPANDS 374 m->opts[opt].opt_exp_name ? 375 m->opts[opt].opt_exp_name : m->opts[opt].opt_name 376#else 377 m->opts[opt].opt_name 378#endif 379 ); 380 } 381 382 /* Add the exit option. */ 383 if (!(m->mopt & MC_NOEXITOPT)) { 384 if (cury < maxy) 385 draw_menu_line(m, m->numopts, cury++, arg, m->exitstr); 386 else 387 opt = 0; 388 } 389 390 /* Add the scroll line */ 391 if (opt != m->numopts || m->topline != 0) 392 mvwaddstr(m->mw, cury, hasbox, scrolltext); 393 394 /* Add the box. */ 395 if (!(m->mopt & MC_NOBOX)) 396 box(m->mw, 0, 0); 397 398 wmove(m->mw, tadd + m->cursel - m->topline, hasbox); 399 wrefresh(m->mw); 400} 401 402 403static void 404/*ARGSUSED*/ 405process_help(menudesc *m) 406{ 407 const char *help = m->helpstr; 408 WINDOW *sv_curscr; 409 int lineoff = 0; 410 int curoff = 0; 411 int again; 412 int winin; 413 414 /* Is there help? */ 415 if (!help) { 416 mbeep(); 417 return; 418 } 419 sv_curscr = newwin(getmaxy(curscr), getmaxx(curscr), 0, 0); 420 if (!sv_curscr) { 421 mbeep(); 422 return; 423 } 424 /* 425 * Save screen contents so we can restore before returning. 426 * cursrc contains post-doupdate() data, not post-refresh() 427 * data so we must call doupdate to ensure we save the 428 * correct data. Avoids PR 26660. 429 */ 430 doupdate(); 431 overwrite(curscr, sv_curscr); 432 touchwin(stdscr); 433 434 help = MSG_XLAT(help); 435 /* Display the help information. */ 436 do { 437 if (lineoff < curoff) { 438 help = MSG_XLAT(m->helpstr); 439 curoff = 0; 440 } 441 while (*help && curoff < lineoff) { 442 if (*help == '\n') 443 curoff++; 444 help++; 445 } 446 447 wclear(stdscr); 448 mvwaddstr(stdscr, 0, 0, 449 "Help: exit: x, page up: u <, page down: d >"); 450 mvwaddstr(stdscr, 2, 0, help); 451 wmove(stdscr, 1, 0); 452 wrefresh(stdscr); 453 454 do { 455 winin = wgetch(stdscr); 456 if (winin < KEY_MIN) 457 winin = tolower(winin); 458 again = 0; 459 switch (winin) { 460 case '<': 461 case 'u': 462 case KEY_UP: 463 case KEY_LEFT: 464 case KEY_PPAGE: 465 if (lineoff) 466 lineoff -= max_lines - 2; 467 else 468 again = 1; 469 break; 470 case '>': 471 case 'd': 472 case KEY_DOWN: 473 case KEY_RIGHT: 474 case KEY_NPAGE: 475 if (*help) 476 lineoff += max_lines - 2; 477 else 478 again = 1; 479 break; 480 case 'q': 481 break; 482 case 'x': 483 winin = 'q'; 484 break; 485 default: 486 again = 1; 487 } 488 if (again) 489 mbeep(); 490 } while (again); 491 } while (winin != 'q'); 492 493 /* Restore the original screen contents */ 494 touchwin(sv_curscr); 495 wnoutrefresh(sv_curscr); 496 delwin(sv_curscr); 497 498 /* Some code thinks that wrefresh(stdout) is a good idea... */ 499 wclear(stdscr); 500} 501 502#ifdef MENU_EXPANDS 503static void 504free_exp_menu_items(menudesc *m) 505{ 506 int i; 507 508 for (i = 0; i < m->numopts; i++) { 509 if (m->opts[i].opt_exp_name != NULL) { 510 free(__UNCONST(m->opts[i].opt_exp_name)); 511 m->opts[i].opt_exp_name = NULL; 512 } 513 } 514} 515#endif 516 517static void 518process_req(menudesc *m, void *arg, int req) 519{ 520 int ch; 521 int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1); 522 523 switch(req) { 524 525 case REQ_EXECUTE: 526 return; 527 528 case REQ_NEXT_ITEM: 529 ch = m->cursel; 530 for (;;) { 531 ch++; 532 if (ch >= m->numopts + hasexit) { 533 mbeep(); 534 return; 535 } 536 if (hasexit && ch == m->numopts) 537 break; 538 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 539 break; 540 } 541 m->cursel = ch; 542 if (m->cursel >= m->topline + m->h) 543 m->topline = m->cursel - m->h + 1; 544 break; 545 546 case REQ_PREV_ITEM: 547 ch = m->cursel; 548 for (;;) { 549 if (ch <= 0) { 550 mbeep(); 551 return; 552 } 553 ch--; 554 if (!(m->opts[ch].opt_flags & OPT_IGNORE)) 555 break; 556 } 557 m->cursel = ch; 558 if (m->cursel < m->topline) 559 m->topline = m->cursel; 560 break; 561 562 case REQ_HELP: 563 process_help(m); 564 break; 565 566 case REQ_REDISPLAY: 567 endwin(); 568 doupdate(); 569 break; 570 571 case REQ_SCROLLUP: 572 if (m->cursel == 0) { 573 mbeep(); 574 return; 575 } 576 m->topline = MAX(0, m->topline - m->h); 577 m->cursel = MAX(0, m->cursel - m->h); 578 wclear(m->mw); 579 break; 580 581 case REQ_SCROLLDOWN: 582 if (m->cursel >= m->numopts + hasexit - 1) { 583 mbeep(); 584 return; 585 } 586 m->topline = MIN(m->topline + m->h, 587 MAX(m->numopts + hasexit - m->h, 0)); 588 m->cursel = MIN(m->numopts + hasexit - 1, m->cursel + m->h); 589 wclear(m->mw); 590 break; 591 592 default: 593 ch = req; 594 if (ch == 'x' && hasexit) { 595 m->cursel = m->numopts; 596 break; 597 } 598 if (m->mopt & MC_NOSHORTCUT) { 599 mbeep(); 600 return; 601 } 602 if (ch > 'z') 603 ch = 255; 604 if (ch >= 'a') { 605 if (ch > 'x') 606 ch--; 607 ch = ch - 'a'; 608 } else 609 ch = 25 + ch - 'A'; 610 if (ch < 0 || ch >= m->numopts) { 611 mbeep(); 612 return; 613 } 614 if (m->opts[ch].opt_flags & OPT_IGNORE) { 615 mbeep(); 616 return; 617 } 618 m->cursel = ch; 619 } 620 621 draw_menu(m, arg); 622} 623 624int 625menu_init(void) 626{ 627 int i; 628 629 if (__menu_init) 630 return 0; 631 632#ifdef USER_MENU_INIT 633 if (USER_MENU_INIT) 634 return 1; 635#endif 636 637 if (initscr() == NULL) 638 return 1; 639 640 cbreak(); 641 noecho(); 642 643 /* XXX Should be configurable but it almost isn't worth it. */ 644 if (has_colors()) { 645 start_color(); 646 init_pair(1, COLOR_WHITE, COLOR_BLUE); 647 bkgd(COLOR_PAIR(1)); 648 attrset(COLOR_PAIR(1)); 649 } 650 651 max_lines = getmaxy(stdscr); 652 max_cols = getmaxx(stdscr); 653 keypad(stdscr, TRUE); 654#ifdef DYNAMIC_MENUS 655 num_menus = DYN_INIT_NUM; 656 while (num_menus < DYN_MENU_START) 657 num_menus *= 2; 658 menu_list = malloc(sizeof *menu_list * num_menus); 659 if (menu_list == NULL) 660 return 2; 661 (void)memset(menu_list, 0, sizeof *menu_list * num_menus); 662 for (i = 0; i < DYN_MENU_START; i++) 663 menu_list[i] = &menu_def[i]; 664#endif 665 666 __menu_init = 1; 667 return 0; 668} 669 670void 671process_menu(int num, void *arg) 672{ 673 int sel = 0; 674 int req; 675 menu_ent *opt; 676 677 menudesc *m; 678 679 /* Initialize? */ 680 if (menu_init()) { 681 __menu_initerror(); 682 return; 683 } 684 685 if (num < 0 || num >= num_menus) 686 return; 687 m = &MENUS(num); 688 if (m == NULL) 689 return; 690 691 /* Default to select option 0 and display from 0, skip any 692 * disabled options at the top of the menu. */ 693 m->topline = 0; 694 if ((m->mopt & (MC_DFLTEXIT | MC_NOEXITOPT)) == MC_DFLTEXIT) { 695 m->cursel = -1; 696 } else { 697 for (m->cursel = 0; m->cursel < m->numopts; m->cursel++) 698 if ((m->opts[m->cursel].opt_flags & OPT_IGNORE) == 0) 699 break; 700 } 701 702 for (;;) { 703 if (isendwin()) 704 /* I'm not sure this is needed with netbsd's curses */ 705 doupdate(); 706#ifdef MENU_EXPANDS 707 /* Expand the menu items, if needed */ 708 if (m->expand_act && m->mw == NULL) 709 (*m->expand_act)(m, arg); 710#endif 711 712 /* Process the display action */ 713 if (m->post_act) 714 (*m->post_act)(m, arg); 715 if (m->mw == NULL) 716 init_menu(m); 717 draw_menu(m, arg); 718 719 while ((req = menucmd(m->mw)) != REQ_EXECUTE) 720 process_req(m, arg, req); 721 722 sel = m->cursel; 723 if (!(m->mopt & MC_NOCLEAR)) { 724 wclear(m->mw); 725 if (m->sv_mw) 726 overwrite(m->sv_mw, m->mw); 727 wnoutrefresh(m->mw); 728 } 729 730 /* Process the items */ 731 if (sel >= m->numopts) 732 /* exit option */ 733 break; 734 735 opt = &m->opts[sel]; 736 if (opt->opt_flags & OPT_IGNORE) 737 continue; 738 if (opt->opt_flags & OPT_ENDWIN) 739 endwin(); 740 if (opt->opt_action && (*opt->opt_action)(m, arg)) 741 break; 742 743 if (opt->opt_menu != -1) { 744 if (!(opt->opt_flags & OPT_SUB)) { 745 num = opt->opt_menu; 746 wclear(m->mw); 747 if (m->sv_mw) { 748 overwrite(m->sv_mw, m->mw); 749 delwin(m->sv_mw); 750 m->sv_mw = NULL; 751 } 752 wnoutrefresh(m->mw); 753 delwin(m->mw); 754 m->mw = NULL; 755 m = &MENUS(num); 756 continue; 757 } 758 process_menu(opt->opt_menu, arg); 759 } 760 if (opt->opt_flags & OPT_EXIT) 761 break; 762 } 763 764 if (m->mopt & MC_NOCLEAR) { 765 wclear(m->mw); 766 if (m->sv_mw) 767 overwrite(m->sv_mw, m->mw); 768 wnoutrefresh(m->mw); 769 } 770 771#ifdef MENU_EXPANDS 772 /* Free expanded menu items, if any */ 773 free_exp_menu_items(m); 774#endif 775 776 /* Process the exit action */ 777 if (m->exit_act) 778 (*m->exit_act)(m, arg); 779 delwin(m->mw); 780 m->mw = NULL; 781 if (m->sv_mw) { 782 delwin(m->sv_mw); 783 m->sv_mw = NULL; 784 } 785} 786 787 788void 789set_menu_numopts(int menu, int numopts) 790{ 791 792 MENUS(menu).numopts = numopts; 793} 794 795/* Control L is end of standard routines, remaining only for dynamic. */ 796 797/* Beginning of routines for dynamic menus. */ 798 799static int 800double_menus(void) 801{ 802 menudesc **temp; 803 int sz = sizeof *menu_list * num_menus; 804 805 temp = realloc(menu_list, sz * 2); 806 if (temp == NULL) 807 return 0; 808 (void)memset(temp + num_menus, 0, sz); 809 menu_list = temp; 810 num_menus *= 2; 811 812 return 1; 813} 814 815int 816new_menu(const char *title, menu_ent *opts, int numopts, 817 int x, int y, int h, int w, int mopt, 818 void (*post_act)(menudesc *, void *), 819 void (*draw_line)(menudesc *, int, void *), 820 void (*exit_act)(menudesc *, void *), 821 const char *help, const char *exit_str) 822{ 823 int ix; 824 menudesc *m; 825 826 /* Find free menu entry. */ 827 for (ix = DYN_MENU_START; ; ix++) { 828 if (ix >= num_menus && !double_menus()) 829 return -1; 830 m = menu_list[ix]; 831 if (m == NULL) { 832 m = calloc(sizeof *m, 1); 833 if (m == NULL) 834 return -1; 835 menu_list[ix] = m; 836 break; 837 } 838 if (!(m->mopt & MC_VALID)) 839 break; 840 } 841 842 /* Set Entries */ 843 m->title = title; 844 m->opts = opts; 845 m->numopts = numopts; 846 m->x = x; 847 m->y = y; 848 m->h = h; 849 m->w = w; 850 m->mopt = mopt | MC_VALID; 851#ifdef MENU_EXPANDS 852 m->expand_act = NULL; 853#endif 854 m->post_act = post_act; 855 m->draw_line = draw_line; 856 m->exit_act = exit_act; 857 m->helpstr = help; 858 m->exitstr = exit_str ? exit_str : "Exit"; 859 860 return ix; 861} 862 863void 864free_menu(int menu_no) 865{ 866 menudesc *m; 867 868 if (menu_no < 0 || menu_no >= num_menus) 869 return; 870 871 m = menu_list[menu_no]; 872 if (!(m->mopt & MC_VALID)) 873 return; 874 if (m->mw != NULL) 875 delwin(m->mw); 876 memset(m, 0, sizeof *m); 877} 878