NetBSD/usr.bin/menuc/menu_sys.def
phil 0c35cc0adb Tweeks to option lettering ... to allow 51 options per menu. Also
to generate an error if there are more.  (Checkpoint before furthur
changes to recognize more keypad keys.)
1998-06-25 19:57:10 +00:00

498 lines
9.9 KiB
Modula-2

/* $NetBSD: menu_sys.def,v 1.8 1998/06/25 19:57:10 phil Exp $ */
/*
* Copyright 1997 Piermont Information Systems Inc.
* All rights reserved.
*
* Written by Philip A. Nelson for Piermont Information Systems Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software develooped for the NetBSD Project by
* Piermont Information Systems Inc.
* 4. The name of Piermont Information Systems Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* menu_sys.defs -- Menu system standard routines. */
#include <string.h>
#include <ctype.h>
#define REQ_EXECUTE 1000
#define REQ_NEXT_ITEM 1001
#define REQ_PREV_ITEM 1002
#define REQ_REDISPLAY 1003
#define KEYPAD_DOWN_ARROW 256
#define KEYPAD_UP_ARROW 257
#define MAX(x,y) ((x)>(y)?(x):(y))
#define MIN(x,y) ((x)<(y)?(x):(y))
/* Initialization state. */
static int __menu_init = 0;
int __m_endwin = 0;
static int max_lines = 0;
static char *scrolltext = " <: page up, >: page down";
/* prototypes for in here! */
static void init_menu (struct menudesc *m);
static void post_menu (struct menudesc *m);
static void process_help (struct menudesc *m, int num);
static void process_req (struct menudesc *m, int num, int req);
static void mbeep (void);
static int menucmd (WINDOW *w);
#ifndef NULL
#define NULL (void *)0
#endif
/* menu system processing routines */
static void mbeep (void)
{
fprintf (stderr,"\a");
}
static int mgetch(WINDOW *w)
{
static char buf[20];
static int num = 0;
int i, ret;
for (i=0; i< strlen(KD); i++) {
if (i >= num)
buf[num++] = wgetch(w);
if (buf[i] != KD[i])
break;
}
if (i == strlen(KD)) {
num = 0;
return KEYPAD_DOWN_ARROW;
}
for (i=0; i< strlen(KU); i++) {
if (i >= num)
buf[num++] = wgetch(w);
if (buf[i] != KU[i])
break;
}
if (i == strlen(KU)) {
num = 0;
return KEYPAD_UP_ARROW;
}
ret = buf[0];
for (i = 0; i < strlen(buf); i++)
buf[i] = buf[i+1];
num--;
return ret;
}
static int menucmd (WINDOW *w)
{
int ch;
while (TRUE) {
ch = mgetch(w);
switch (ch) {
case '\n':
return REQ_EXECUTE;
case '\016':
case KEYPAD_DOWN_ARROW:
return REQ_NEXT_ITEM;
case '\020':
case KEYPAD_UP_ARROW:
return REQ_PREV_ITEM;
case '\014':
return REQ_REDISPLAY;
}
if (isalpha(ch) || ch == '?' || ch == '<' || ch == '>')
return (ch);
mbeep();
wrefresh(w);
}
}
static void init_menu (struct menudesc *m)
{
int max;
char **p;
int add, exitadd;
add = ((m->mopt & NOBOX) ? 2 : 4);
exitadd = ((m->mopt & NOEXITOPT) ? 0 : 1);
max = strlen(m->title);
/* Calculate h? h == number of visible options. */
if (m->h == 0) {
m->h = m->numopts + exitadd;
if (m->h + m->y + add >= max_lines && (m->mopt & SCROLL))
m->h = max_lines - m->y - add - 1;
}
/* Check window heights and set scrolling */
if (m->h < m->numopts + exitadd) {
if (m->mopt & SCROLL) {
if (m->h < 3) {
endwin();
(void) fprintf (stderr,
"Window too small for menu \"%s\"\n",
m->title);
exit(1);
}
} else {
endwin();
(void) fprintf (stderr,
"Menu too big for menu \"%s\"\n",
m->title);
exit(1);
}
} else
m->mopt &= ~SCROLL;
/* Calculate w? */
if (m->w == 0) {
p = m->opts;
if (m->mopt & SCROLL)
max = strlen(scrolltext);
while (*p) {
max = MAX(max,strlen(*p));
p++;
}
m->w = max;
}
/* Get the windows. */
m->mw = newwin(m->h+add, m->w+add, m->y, m->x);
if (m->mw == NULL) {
endwin();
(void) fprintf (stderr,
"Could not create window for window with title "
" \"%s\"\n", m->title);
exit(1);
}
}
static void post_menu (struct menudesc *m)
{
int i;
int hasbox, cury, maxy, selrow, lastopt;
int tadd;
if (m->mopt & NOBOX) {
cury = 0;
maxy = m->h;
hasbox = 0;
} else {
cury = 1;
maxy = m->h+1;
hasbox = 1;
}
tadd = strlen(m->title) ? 2 : 0;
if (tadd) {
mvwaddstr(m->mw, cury, cury, m->title);
cury += 2;
maxy += 2;
}
/* Set defaults, calculate lastopt. */
selrow = -1;
if (m->mopt & SCROLL) {
lastopt = MIN(m->numopts, m->topline+m->h-1);
maxy -= 1;
} else
lastopt = m->numopts;
for (i=m->topline; i<lastopt; i++, cury++) {
if (m->cursel == i) {
mvwaddstr (m->mw, cury, hasbox, ">");
wstandout(m->mw);
selrow = cury;
} else
mvwaddstr (m->mw, cury, hasbox, " ");
waddstr (m->mw, m->opts[i]);
if (m->cursel == i)
wstandend(m->mw);
wclrtoeol(m->mw);
}
/* Add the exit option. */
if (!(m->mopt & NOEXITOPT) && cury < maxy) {
if (m->cursel >= m->numopts) {
mvwaddstr (m->mw, cury, hasbox, ">");
wstandout(m->mw);
selrow = cury;
} else
mvwaddstr (m->mw, cury, hasbox, " ");
waddstr (m->mw, "x: Exit");
if (m->cursel >= m->numopts)
wstandend(m->mw);
wclrtoeol(m->mw);
cury++;
}
/* Add the scroll line */
if (m->mopt & SCROLL) {
mvwaddstr (m->mw, cury, hasbox, scrolltext);
if (selrow < 0)
selrow = cury;
}
/* Add the box. */
if (!(m->mopt & NOBOX))
box(m->mw, '*', '*');
wmove(m->mw, selrow, hasbox);
}
static void process_help (struct menudesc *m, int num)
{
char *help = m->helpstr;
int lineoff = 0;
int curoff = 0;
int again;
int winin;
/* Is there help? */
if (!help) {
mbeep();
return;
}
/* Display the help information. */
do {
if (lineoff < curoff) {
help = m->helpstr;
curoff = 0;
}
while (*help && curoff < lineoff) {
if (*help == '\n')
curoff++;
help++;
}
wclear(stdscr);
mvwaddstr (stdscr, 0, 0,
"Help: exit: q, page up: u <, page down: d >");
mvwaddstr (stdscr, 2, 0, help);
wmove (stdscr, 1, 0);
wrefresh(stdscr);
do {
winin = mgetch(stdscr);
winin = tolower(winin);
again = 0;
switch (winin) {
case '<':
case 'u':
case KEYPAD_UP_ARROW:
if (lineoff)
lineoff -= max_lines - 2;
else
again = 1;
break;
case '>':
case 'd':
case KEYPAD_DOWN_ARROW:
if (*help)
lineoff += max_lines - 2;
else
again = 1;
break;
case 'q':
break;
default:
again = 1;
}
} while (again);
} while (winin != 'q');
/* Restore current menu */
wclear(stdscr);
wrefresh(stdscr);
process_item (&num, -2);
}
static void process_req (struct menudesc *m, int num, int req)
{
int ch;
int hasexit = (m->mopt & NOEXITOPT ? 0 : 1 );
int refresh = 0;
if (req == REQ_EXECUTE)
return;
else if (req == REQ_NEXT_ITEM) {
if (m->cursel < m->numopts + hasexit - 1) {
m->cursel++;
refresh = 1;
} else
mbeep();
} else if (req == REQ_PREV_ITEM) {
if (m->cursel > 0) {
m->cursel--;
refresh = 1;
} else
mbeep();
} else if (req == REQ_REDISPLAY) {
wclear(stdscr);
wrefresh(stdscr);
process_item (&num, -2);
refresh = 1;
} else if (req == '?') {
process_help (m, num);
refresh = 1;
} else if (req == '<') {
if (m->topline == 0)
mbeep();
else {
m->topline -= m->h-1;
wclear (m->mw);
refresh = 1;
}
} else if (req == '>') {
if (m->topline + m->h - 1 > m->numopts + hasexit)
mbeep();
else {
m->topline += m->h-1;
wclear (m->mw);
refresh = 1;
}
} else {
ch = tolower (req);
if (ch == 'x' && hasexit) {
m->cursel = m->numopts;
refresh = 1;
} else {
if (ch > 'z')
ch = 255;
if (ch >= 'a') {
if (ch > 'x') ch--;
ch = ch - 'a';
} else
ch = 25 + ch - 'A';
if (ch < 0 || ch >= m->numopts)
mbeep();
else {
m->cursel = ch;
refresh = 1;
}
}
}
if (refresh) {
post_menu (m);
wrefresh (m->mw);
}
}
void process_menu (int num)
{
int sel = 0;
int req, done;
int last_num;
struct menudesc *m = &menus[num];
done = FALSE;
/* Initialize? */
if (!__menu_init) {
if (initscr() == NULL) {
__menu_initerror();
return;
}
cbreak();
noecho();
max_lines = stdscr->maxy;
__menu_init = 1;
}
if (__m_endwin) {
wclear(stdscr);
wrefresh(stdscr);
__m_endwin = 0;
}
if (m->mw == NULL)
init_menu (m);
/* Always preselect option 0 and display from 0! */
m->cursel = 0;
m->topline = 0;
while (!done) {
last_num = num;
if (__m_endwin) {
wclear(stdscr);
wrefresh(stdscr);
__m_endwin = 0;
}
/* Process the display action */
process_item (&num, -2);
post_menu (m);
wrefresh (m->mw);
while ((req = menucmd (m->mw)) != REQ_EXECUTE)
process_req (m, num, req);
sel = m->cursel;
wclear (m->mw);
wrefresh (m->mw);
/* Process the items */
if (sel < m->numopts)
done = process_item (&num, sel);
else
done = TRUE;
/* Reselect m just in case */
if (num != last_num) {
m = &menus[num];
/* Initialize? */
if (m->mw == NULL)
init_menu (m);
process_item (&num, -2);
}
}
/* Process the exit action */
process_item (&num, -1);
}