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