menu_sys.def revision 1.9
1/* $NetBSD: menu_sys.def,v 1.9 1998/06/29 08:46:37 phil 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. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software develooped for the NetBSD Project by 20 * Piermont Information Systems Inc. 21 * 4. The name of Piermont Information Systems Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 35 * THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 */ 38 39/* menu_sys.defs -- Menu system standard routines. */ 40 41#include <string.h> 42#include <ctype.h> 43 44#define REQ_EXECUTE 1000 45#define REQ_NEXT_ITEM 1001 46#define REQ_PREV_ITEM 1002 47#define REQ_REDISPLAY 1003 48#define REQ_SCROLLDOWN 1004 49#define REQ_SCROLLUP 1005 50#define REQ_HELP 1006 51 52/* Multiple key support */ 53#define KEYSEQ_FIRST 256 54#define KEYSEQ_DOWN_ARROW 256 55#define KEYSEQ_UP_ARROW 257 56#define KEYSEQ_LEFT_ARROW 258 57#define KEYSEQ_RIGHT_ARROW 259 58#define KEYSEQ_PAGE_DOWN 260 59#define KEYSEQ_PAGE_UP 261 60 61struct keyseq { 62 char *termcap_name; 63 char *chars; 64 int numchars; 65 int keyseq_val; 66 struct keyseq *next; 67}; 68 69struct keyseq _mc_key_seq[] = { 70 /* keypad definitions */ 71 { "kd", NULL, 0, KEYSEQ_DOWN_ARROW, NULL }, 72 { "kl", NULL, 0, KEYSEQ_LEFT_ARROW, NULL }, 73 { "kr", NULL, 0, KEYSEQ_RIGHT_ARROW, NULL }, 74 { "ku", NULL, 0, KEYSEQ_UP_ARROW, NULL }, 75 { "kf", NULL, 0, KEYSEQ_PAGE_DOWN, NULL }, /* scroll forward */ 76 { "kN", NULL, 0, KEYSEQ_PAGE_DOWN, NULL }, /* next page */ 77 { "kP", NULL, 0, KEYSEQ_PAGE_UP, NULL }, /* scroll backward */ 78 { "kR", NULL, 0, KEYSEQ_PAGE_UP, NULL }, /* prev page */ 79 /* other definitions */ 80 { NULL, "\033v", 0, KEYSEQ_PAGE_UP, NULL }, /* ESC-v */ 81 { NULL, "\026", 0, KEYSEQ_PAGE_DOWN, NULL }, /* CTL-v */ 82}; 83 84int _mc_num_key_seq = sizeof(_mc_key_seq) / sizeof(struct keyseq); 85struct keyseq *pad_list = NULL; 86static char str_area [512]; 87static char *str_ptr = str_area; 88 89/* Macros */ 90#define MAX(x,y) ((x)>(y)?(x):(y)) 91#define MIN(x,y) ((x)<(y)?(x):(y)) 92 93/* Initialization state. */ 94static int __menu_init = 0; 95int __m_endwin = 0; 96static int max_lines = 0; 97static char *scrolltext = " <: page up, >: page down"; 98 99/* prototypes for in here! */ 100static void ins_keyseq (struct keyseq **seq, struct keyseq *ins); 101static void init_keyseq (void); 102static void init_menu (struct menudesc *m); 103static void post_menu (struct menudesc *m); 104static void process_help (struct menudesc *m, int num); 105static void process_req (struct menudesc *m, int num, int req); 106static void mbeep (void); 107static int menucmd (WINDOW *w); 108 109#ifndef NULL 110#define NULL (void *)0 111#endif 112 113/* menu system processing routines */ 114 115static void mbeep (void) 116{ 117 fprintf (stderr,"\a"); 118} 119 120static void ins_keyseq (struct keyseq **seq, struct keyseq *ins) 121{ 122 if (*seq == NULL) { 123 ins->next = NULL; 124 *seq = ins; 125 } else if (ins->numchars <= (*seq)->numchars) { 126 ins->next = *seq; 127 *seq = ins; 128 } else 129 ins_keyseq (&(*seq)->next, ins); 130} 131 132static void init_keyseq (void) 133{ 134 int i; 135 for (i=0; i<_mc_num_key_seq; i++) { 136 if (_mc_key_seq[i].termcap_name) 137 _mc_key_seq[i].chars = 138 tgetstr (_mc_key_seq[i].termcap_name, 139 &str_ptr); 140 if (_mc_key_seq[i].chars != NULL && 141 (_mc_key_seq[i].numchars = strlen(_mc_key_seq[i].chars)) 142 > 0) 143 ins_keyseq (&pad_list,&_mc_key_seq[i]); 144 } 145} 146 147static int mgetch(WINDOW *w) 148{ 149 static char buf[20]; 150 static int num = 0; 151 struct keyseq *list = pad_list; 152 int i, ret; 153 154 /* key pad processing */ 155 while (list) { 156 for (i=0; i< list->numchars; i++) { 157 if (i >= num) 158 buf[num++] = wgetch(w); 159 if (buf[i] != list->chars[i]) 160 break; 161 } 162 if (i == list->numchars) { 163 num = 0; 164 return list->keyseq_val; 165 } 166 list = list->next; 167 } 168 169 ret = buf[0]; 170 for (i = 0; i < strlen(buf); i++) 171 buf[i] = buf[i+1]; 172 num--; 173 return ret; 174} 175 176static int menucmd (WINDOW *w) 177{ 178 int ch; 179 180 while (TRUE) { 181 ch = mgetch(w); 182 183 switch (ch) { 184 case '\n': 185 return REQ_EXECUTE; 186 case '\016': 187 case KEYSEQ_DOWN_ARROW: 188 return REQ_NEXT_ITEM; 189 case '\020': 190 case KEYSEQ_UP_ARROW: 191 return REQ_PREV_ITEM; 192 case '\014': 193 return REQ_REDISPLAY; 194 case '<': 195 case KEYSEQ_PAGE_UP: 196 return REQ_SCROLLUP; 197 case '>': 198 case KEYSEQ_PAGE_DOWN: 199 return REQ_SCROLLDOWN; 200 case '?': 201 return REQ_HELP; 202 } 203 204 if (isalpha(ch)) 205 return (ch); 206 207 mbeep(); 208 wrefresh(w); 209 } 210} 211 212static void init_menu (struct menudesc *m) 213{ 214 int max; 215 char **p; 216 int add, exitadd; 217 218 add = ((m->mopt & NOBOX) ? 2 : 4); 219 exitadd = ((m->mopt & NOEXITOPT) ? 0 : 1); 220 max = strlen(m->title); 221 222 /* Calculate h? h == number of visible options. */ 223 if (m->h == 0) { 224 m->h = m->numopts + exitadd; 225 if (m->h + m->y + add >= max_lines && (m->mopt & SCROLL)) 226 m->h = max_lines - m->y - add ; 227 } 228 229 /* Check window heights and set scrolling */ 230 if (m->h < m->numopts + exitadd) { 231 if (!(m->mopt & SCROLL) || m->h < 3) { 232 endwin(); 233 (void) fprintf (stderr, 234 "Window too small for menu \"%s\"\n", 235 m->title); 236 exit(1); 237 } 238 } else 239 m->mopt &= ~SCROLL; 240 241 /* check for screen fit */ 242 if (m->y + m->h + add > max_lines) { 243 endwin(); 244 (void) fprintf (stderr, 245 "Screen too small for menu \"%s\"\n", m->title); 246 exit(1); 247 248 } 249 250 /* Calculate w? */ 251 if (m->w == 0) { 252 p = m->opts; 253 if (m->mopt & SCROLL) 254 max = strlen(scrolltext); 255 while (*p) { 256 max = MAX(max,strlen(*p)); 257 p++; 258 } 259 m->w = max; 260 } 261 262 /* Get the windows. */ 263 m->mw = newwin(m->h+add, m->w+add, m->y, m->x); 264 265 if (m->mw == NULL) { 266 endwin(); 267 (void) fprintf (stderr, 268 "Could not create window for window with title " 269 " \"%s\"\n", m->title); 270 exit(1); 271 } 272} 273 274static void post_menu (struct menudesc *m) 275{ 276 int i; 277 int hasbox, cury, maxy, selrow, lastopt; 278 int tadd; 279 280 if (m->mopt & NOBOX) { 281 cury = 0; 282 maxy = m->h; 283 hasbox = 0; 284 } else { 285 cury = 1; 286 maxy = m->h+1; 287 hasbox = 1; 288 } 289 290 /* Clear the window */ 291 wclear (m->mw); 292 293 tadd = strlen(m->title) ? 2 : 0; 294 295 if (tadd) { 296 mvwaddstr(m->mw, cury, cury, m->title); 297 cury += 2; 298 maxy += 2; 299 } 300 301 /* Set defaults, calculate lastopt. */ 302 selrow = -1; 303 if (m->mopt & SCROLL) { 304 lastopt = MIN(m->numopts, m->topline+m->h-1); 305 maxy -= 1; 306 } else 307 lastopt = m->numopts; 308 309 for (i=m->topline; i<lastopt; i++, cury++) { 310 if (m->cursel == i) { 311 mvwaddstr (m->mw, cury, hasbox, ">"); 312 wstandout(m->mw); 313 selrow = cury; 314 } else 315 mvwaddstr (m->mw, cury, hasbox, " "); 316 waddstr (m->mw, m->opts[i]); 317 if (m->cursel == i) 318 wstandend(m->mw); 319 } 320 321 /* Add the exit option. */ 322 if (!(m->mopt & NOEXITOPT) && cury < maxy) { 323 if (m->cursel >= m->numopts) { 324 mvwaddstr (m->mw, cury, hasbox, ">"); 325 wstandout(m->mw); 326 selrow = cury; 327 } else 328 mvwaddstr (m->mw, cury, hasbox, " "); 329 waddstr (m->mw, "x: Exit"); 330 if (m->cursel >= m->numopts) 331 wstandend(m->mw); 332 cury++; 333 } 334 335 /* Add the scroll line */ 336 if (m->mopt & SCROLL) { 337 mvwaddstr (m->mw, cury, hasbox, scrolltext); 338 if (selrow < 0) 339 selrow = cury; 340 } 341 342 /* Add the box. */ 343 if (!(m->mopt & NOBOX)) 344 box(m->mw, '*', '*'); 345 346 wmove(m->mw, selrow, hasbox); 347} 348 349static void process_help (struct menudesc *m, int num) 350{ 351 char *help = m->helpstr; 352 int lineoff = 0; 353 int curoff = 0; 354 int again; 355 int winin; 356 357 /* Is there help? */ 358 if (!help) { 359 mbeep(); 360 return; 361 } 362 363 /* Display the help information. */ 364 do { 365 if (lineoff < curoff) { 366 help = m->helpstr; 367 curoff = 0; 368 } 369 while (*help && curoff < lineoff) { 370 if (*help == '\n') 371 curoff++; 372 help++; 373 } 374 375 wclear(stdscr); 376 mvwaddstr (stdscr, 0, 0, 377 "Help: exit: x, page up: u <, page down: d >"); 378 mvwaddstr (stdscr, 2, 0, help); 379 wmove (stdscr, 1, 0); 380 wrefresh(stdscr); 381 382 do { 383 winin = mgetch(stdscr); 384 if (winin < KEYSEQ_FIRST) 385 winin = tolower(winin); 386 again = 0; 387 switch (winin) { 388 case '<': 389 case 'u': 390 case KEYSEQ_UP_ARROW: 391 case KEYSEQ_LEFT_ARROW: 392 case KEYSEQ_PAGE_UP: 393 if (lineoff) 394 lineoff -= max_lines - 2; 395 else 396 again = 1; 397 break; 398 case '>': 399 case 'd': 400 case KEYSEQ_DOWN_ARROW: 401 case KEYSEQ_RIGHT_ARROW: 402 case KEYSEQ_PAGE_DOWN: 403 if (*help) 404 lineoff += max_lines - 2; 405 else 406 again = 1; 407 break; 408 case 'x': 409 case 'q': 410 break; 411 default: 412 again = 1; 413 } 414 if (again) 415 mbeep(); 416 } while (again); 417 } while (winin != 'q'); 418 419 /* Restore current menu */ 420 wclear(stdscr); 421 wrefresh(stdscr); 422 process_item (&num, -2); 423} 424 425static void process_req (struct menudesc *m, int num, int req) 426{ 427 int ch; 428 int hasexit = (m->mopt & NOEXITOPT ? 0 : 1 ); 429 int refresh = 0; 430 int scroll_sel = 0; 431 432 if (req == REQ_EXECUTE) 433 return; 434 435 else if (req == REQ_NEXT_ITEM) { 436 if (m->cursel < m->numopts + hasexit - 1) { 437 m->cursel++; 438 scroll_sel = 1; 439 refresh = 1; 440 } else 441 mbeep(); 442 443 } else if (req == REQ_PREV_ITEM) { 444 if (m->cursel > 0) { 445 m->cursel--; 446 scroll_sel = 1; 447 refresh = 1; 448 } else 449 mbeep(); 450 451 } else if (req == REQ_REDISPLAY) { 452 wclear(stdscr); 453 wrefresh(stdscr); 454 process_item (&num, -2); 455 refresh = 1; 456 457 } else if (req == REQ_HELP) { 458 process_help (m, num); 459 refresh = 1; 460 461 } else if (req == REQ_SCROLLUP) { 462 if (!(m->mopt & SCROLL)) 463 mbeep(); 464 else if (m->topline == 0) 465 mbeep(); 466 else { 467 m->topline -= m->h-1; 468 wclear (m->mw); 469 refresh = 1; 470 } 471 472 } else if (req == REQ_SCROLLDOWN) { 473 if (!(m->mopt & SCROLL)) 474 mbeep(); 475 else if (m->topline + m->h - 1 > m->numopts + hasexit) 476 mbeep(); 477 else { 478 m->topline += m->h-1; 479 wclear (m->mw); 480 refresh = 1; 481 } 482 483 } else { 484 ch = req; 485 if (ch == 'x' && hasexit) { 486 m->cursel = m->numopts; 487 scroll_sel = 1; 488 refresh = 1; 489 } else { 490 if (ch > 'z') 491 ch = 255; 492 if (ch >= 'a') { 493 if (ch > 'x') ch--; 494 ch = ch - 'a'; 495 } else 496 ch = 25 + ch - 'A'; 497 if (ch < 0 || ch >= m->numopts) 498 mbeep(); 499 else { 500 m->cursel = ch; 501 scroll_sel = 1; 502 refresh = 1; 503 } 504 } 505 } 506 507 if (m->mopt & SCROLL && scroll_sel) { 508 while (m->cursel >= m->topline + m->h -1 ) 509 m->topline += m->h -1; 510 while (m->cursel < m->topline) 511 m->topline -= m->h -1; 512 } 513 514 if (refresh) { 515 post_menu (m); 516 wrefresh (m->mw); 517 } 518} 519 520void process_menu (int num) 521{ 522 int sel = 0; 523 int req, done; 524 int last_num; 525 526 struct menudesc *m = &menus[num]; 527 528 done = FALSE; 529 530 /* Initialize? */ 531 if (!__menu_init) { 532 if (initscr() == NULL) { 533 __menu_initerror(); 534 return; 535 } 536 cbreak(); 537 noecho(); 538 max_lines = stdscr->maxy; 539 init_keyseq(); 540 __menu_init = 1; 541 } 542 if (__m_endwin) { 543 wclear(stdscr); 544 wrefresh(stdscr); 545 __m_endwin = 0; 546 } 547 if (m->mw == NULL) 548 init_menu (m); 549 550 /* Always preselect option 0 and display from 0! */ 551 m->cursel = 0; 552 m->topline = 0; 553 554 while (!done) { 555 last_num = num; 556 if (__m_endwin) { 557 wclear(stdscr); 558 wrefresh(stdscr); 559 __m_endwin = 0; 560 } 561 /* Process the display action */ 562 process_item (&num, -2); 563 post_menu (m); 564 wrefresh (m->mw); 565 566 while ((req = menucmd (m->mw)) != REQ_EXECUTE) 567 process_req (m, num, req); 568 569 sel = m->cursel; 570 wclear (m->mw); 571 wrefresh (m->mw); 572 573 /* Process the items */ 574 if (sel < m->numopts) 575 done = process_item (&num, sel); 576 else 577 done = TRUE; 578 579 /* Reselect m just in case */ 580 if (num != last_num) { 581 m = &menus[num]; 582 /* Initialize? */ 583 if (m->mw == NULL) 584 init_menu (m); 585 process_item (&num, -2); 586 } 587 } 588 589 /* Process the exit action */ 590 process_item (&num, -1); 591} 592 593