NetBSD/usr.bin/menuc/menu_sys.def

609 lines
13 KiB
Modula-2

/* $NetBSD: menu_sys.def,v 1.11 1998/07/01 07:46:02 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 REQ_SCROLLDOWN 1004
#define REQ_SCROLLUP 1005
#define REQ_HELP 1006
/* Multiple key support */
#define KEYSEQ_FIRST 256
#define KEYSEQ_DOWN_ARROW 256
#define KEYSEQ_UP_ARROW 257
#define KEYSEQ_LEFT_ARROW 258
#define KEYSEQ_RIGHT_ARROW 259
#define KEYSEQ_PAGE_DOWN 260
#define KEYSEQ_PAGE_UP 261
struct keyseq {
char *termcap_name;
char *chars;
int numchars;
int keyseq_val;
struct keyseq *next;
};
/* keypad and other definitions */
struct keyseq _mc_key_seq[] = {
/* Cludge for xterm ... */
{ NULL, "\033[B", 0, KEYSEQ_DOWN_ARROW, NULL },
{ NULL, "\033[D", 0, KEYSEQ_LEFT_ARROW, NULL },
{ NULL, "\033[C", 0, KEYSEQ_RIGHT_ARROW, NULL },
{ NULL, "\033[A", 0, KEYSEQ_UP_ARROW, NULL },
/* Termcap defined */
{ "kd", NULL, 0, KEYSEQ_DOWN_ARROW, NULL },
{ "kl", NULL, 0, KEYSEQ_LEFT_ARROW, NULL },
{ "kr", NULL, 0, KEYSEQ_RIGHT_ARROW, NULL },
{ "ku", NULL, 0, KEYSEQ_UP_ARROW, NULL },
{ "kf", NULL, 0, KEYSEQ_PAGE_DOWN, NULL }, /* scroll forward */
{ "kN", NULL, 0, KEYSEQ_PAGE_DOWN, NULL }, /* next page */
{ "kP", NULL, 0, KEYSEQ_PAGE_UP, NULL }, /* scroll backward */
{ "kR", NULL, 0, KEYSEQ_PAGE_UP, NULL }, /* prev page */
/* other definitions */
{ NULL, "\033v", 0, KEYSEQ_PAGE_UP, NULL }, /* ESC-v */
{ NULL, "\026", 0, KEYSEQ_PAGE_DOWN, NULL }, /* CTL-v */
};
int _mc_num_key_seq = sizeof(_mc_key_seq) / sizeof(struct keyseq);
struct keyseq *pad_list = NULL;
static char str_area [512];
static char *str_ptr = str_area;
/* Macros */
#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 ins_keyseq (struct keyseq **seq, struct keyseq *ins);
static void init_keyseq (void);
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 void ins_keyseq (struct keyseq **seq, struct keyseq *ins)
{
if (*seq == NULL) {
ins->next = NULL;
*seq = ins;
} else if (ins->numchars <= (*seq)->numchars) {
ins->next = *seq;
*seq = ins;
} else
ins_keyseq (&(*seq)->next, ins);
}
static void init_keyseq (void)
{
int i;
for (i=0; i<_mc_num_key_seq; i++) {
if (_mc_key_seq[i].termcap_name)
_mc_key_seq[i].chars =
tgetstr (_mc_key_seq[i].termcap_name,
&str_ptr);
if (_mc_key_seq[i].chars != NULL &&
(_mc_key_seq[i].numchars = strlen(_mc_key_seq[i].chars))
> 0)
ins_keyseq (&pad_list,&_mc_key_seq[i]);
}
}
static int mgetch(WINDOW *w)
{
static char buf[20];
static int num = 0;
struct keyseq *list = pad_list;
int i, ret;
/* key pad processing */
while (list) {
for (i=0; i< list->numchars; i++) {
if (i >= num)
buf[num++] = wgetch(w);
if (buf[i] != list->chars[i])
break;
}
if (i == list->numchars) {
num = 0;
return list->keyseq_val;
}
list = list->next;
}
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': /* Contnrol-P */
case KEYSEQ_DOWN_ARROW:
return REQ_NEXT_ITEM;
case '\020': /* Control-N */
case KEYSEQ_UP_ARROW:
return REQ_PREV_ITEM;
case '\014': /* Control-L */
return REQ_REDISPLAY;
case '<':
case '\010': /* Control-H (backspace) */
case KEYSEQ_PAGE_UP:
return REQ_SCROLLUP;
case '>':
case ' ':
case KEYSEQ_PAGE_DOWN:
return REQ_SCROLLDOWN;
case '?':
return REQ_HELP;
}
if (isalpha(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 ;
}
/* Check window heights and set scrolling */
if (m->h < m->numopts + exitadd) {
if (!(m->mopt & SCROLL) || m->h < 3) {
endwin();
(void) fprintf (stderr,
"Window too small for menu \"%s\"\n",
m->title);
exit(1);
}
} else
m->mopt &= ~SCROLL;
/* check for screen fit */
if (m->y + m->h + add > max_lines) {
endwin();
(void) fprintf (stderr,
"Screen too small for menu \"%s\"\n", m->title);
exit(1);
}
/* 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;
}
/* Clear the window */
wclear (m->mw);
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);
}
/* 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);
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: x, page up: u <, page down: d >");
mvwaddstr (stdscr, 2, 0, help);
wmove (stdscr, 1, 0);
wrefresh(stdscr);
do {
winin = mgetch(stdscr);
if (winin < KEYSEQ_FIRST)
winin = tolower(winin);
again = 0;
switch (winin) {
case '<':
case 'u':
case KEYSEQ_UP_ARROW:
case KEYSEQ_LEFT_ARROW:
case KEYSEQ_PAGE_UP:
if (lineoff)
lineoff -= max_lines - 2;
else
again = 1;
break;
case '>':
case 'd':
case KEYSEQ_DOWN_ARROW:
case KEYSEQ_RIGHT_ARROW:
case KEYSEQ_PAGE_DOWN:
if (*help)
lineoff += max_lines - 2;
else
again = 1;
break;
case 'q':
break;
case 'x':
winin = 'q';
break;
default:
again = 1;
}
if (again)
mbeep();
} 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;
int scroll_sel = 0;
if (req == REQ_EXECUTE)
return;
else if (req == REQ_NEXT_ITEM) {
if (m->cursel < m->numopts + hasexit - 1) {
m->cursel++;
scroll_sel = 1;
refresh = 1;
if (m->cursel >= m->topline + m->h -1 )
m->topline += 1;
} else
mbeep();
} else if (req == REQ_PREV_ITEM) {
if (m->cursel > 0) {
m->cursel--;
scroll_sel = 1;
refresh = 1;
if (m->cursel < m->topline )
m->topline -= 1;
} else
mbeep();
} else if (req == REQ_REDISPLAY) {
wclear(stdscr);
wrefresh(stdscr);
process_item (&num, -2);
refresh = 1;
} else if (req == REQ_HELP) {
process_help (m, num);
refresh = 1;
} else if (req == REQ_SCROLLUP) {
if (!(m->mopt & SCROLL))
mbeep();
else if (m->topline == 0)
mbeep();
else {
m->topline -= m->h-1;
if (m->topline < 0)
m->topline = 0;
wclear (m->mw);
refresh = 1;
}
} else if (req == REQ_SCROLLDOWN) {
if (!(m->mopt & SCROLL))
mbeep();
else if (m->topline + m->h - 1 > m->numopts + hasexit)
mbeep();
else {
m->topline += m->h-1;
wclear (m->mw);
refresh = 1;
}
} else {
ch = req;
if (ch == 'x' && hasexit) {
m->cursel = m->numopts;
scroll_sel = 1;
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;
scroll_sel = 1;
refresh = 1;
}
}
}
if (m->mopt & SCROLL && scroll_sel) {
while (m->cursel >= m->topline + m->h -1 )
m->topline += m->h -1;
while (m->cursel < m->topline)
m->topline -= m->h -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;
init_keyseq();
__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);
}