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