[comp.sources.misc] v18i080: menubar - C Menubar function, Part01/01

jek5036@ultb.isc.rit.edu (J.E. King) (04/23/91)

Submitted-by: J.E. King <jek5036@ultb.isc.rit.edu>
Posting-number: Volume 18, Issue 80
Archive-name: menubar/part01
Supersedes: menubar: Volume 17, Issue 62

This is a demonstration package on how to use two new curses routines:

menubar  - sets up a menu on the screen using a menu-bar format,
curgets  - get a string using curses in a box on the screen,
termlock - a terminal lock program complete with compile
           definable timeout, and an option to logout at 
           timeout (safelock).

Jim King <jek5036@ultb.isc.rit.edu>

-- cut here -- cut here -- cut here -- cut here -- cut here -- cut here --
#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -s ./Makefile`
then
echo "writing ./Makefile"
cat > ./Makefile << '\End\Of\Shar\'
#
# Makefile for termlock, curses implemented
# by Jim King (jek5036@ultb.isc.rit.edu)
#
# Define TIMELOCK is you want the program to timeout
# Timeout means if the terminal is idle (nobody touches it) for so many
# seconds, the program will quit.. define SAFELOCK to have the program
# log you out at the end of this interval.  Useful for computer rooms
# where terminals are fought over..
#
# If you define TIMELOCK or SAFELOCK, make sure you define DEFTIME.
# DEFTIME is the amount of idle seconds the terminal can sit before timeout.
#
# CHECKAT is the amount of seconds the program will 'sleep' before
# checking if timeout time has occured.
#
# Define SYSV for a SYSV make
#
# CFLAGS = -O -DSAFELOCK -DCHECKAT=30 -DDEFTIME=600 -DSYSV

CFLAGS = -O -DTIMELOCK -DCHECKAT=15 -DDEFTIME=300 # 5 minutes

all: termlock

termlock: menubar.o termlock.o curgets.o
	cc termlock.o menubar.o curgets.o -o termlock -O -lcurses -ltermcap

termlock.o: termlock.c /usr/include/curses.h /usr/include/signal.h
menubar.o: menubar.c /usr/include/curses.h
curgets.o: curgets.c /usr/include/curses.h
\End\Of\Shar\
else
  echo "will not over write ./Makefile"
fi
if [ `wc -c ./Makefile | awk '{printf $1}'` -ne 1083 ]
then
echo `wc -c ./Makefile | awk '{print "Got " $1 ", Expected " 1083}'`
fi
if `test ! -s ./README`
then
echo "writing ./README"
cat > ./README << '\End\Of\Shar\'
This is a demonstration package on how to use two new curses routines:

menubar - sets up a menu on the screen using a menu-bar format
curgets - get a string using curses in a box on the screen

termlock itself is a terminal lock program complete with compile
definable timeout, and an option to logout at timeout (safelock).

Problems to jek5036@ultb.isc.rit.edu (Jim King)
If above address fails (and it won't)
	try pulsar%lsrhs.uucp@xait.xerox.com (same person)

\End\Of\Shar\
else
  echo "will not over write ./README"
fi
if [ `wc -c ./README | awk '{printf $1}'` -ne 466 ]
then
echo `wc -c ./README | awk '{print "Got " $1 ", Expected " 466}'`
fi
if `test ! -s ./curgets.c`
then
echo "writing ./curgets.c"
cat > ./curgets.c << '\End\Of\Shar\'
/*
 * Curses getstring in a box
 * by Jim King (jek5036@ultb.isc.rit.edu)
 */

#include <curses.h>			/* curses include file */

/*
 * curgets is a void type because it does not return anything.
 * curgets arguments:
 *
 * str: address of a character array, passed in like &string
 *      this will contain the string which the user inputs
 * len: the maximum amount of characters to read in (defines box size)
 * y, x: (x, y) coordinates on the screen of the box's upper left-hand corner
 * title: same as str, but it is the title for the box
 */

void curgets(str, len, y, x, title, hide)
char	*str, *title;
int	len, y, x, hide;
{
	WINDOW	*strwin;
	char	c, input[80];
	int	pos, curx;

	strwin = newwin(3, len+2, y, x);
	box(strwin, '|', '-');
	mvwaddch(strwin, 0, 0, '/');
	mvwaddch(strwin, 2, 0, '\\');
	mvwaddch(strwin, 0, len+1, '\\');
	mvwaddch(strwin, 2, len+1, '/');
	wstandout(strwin);
	mvwaddstr(strwin, 0, (len / 2) - (strlen(title) / 2), title);
	wstandend(strwin);
ers:	curx = 1;
	for (pos = 1; pos < len; pos++)
		mvwaddch(strwin, 1, pos, '_');

	for (;;) {
		wmove(strwin, 1, curx);
		wrefresh(strwin);
		noecho(); crmode();
		c = wgetch(strwin);
		switch(c) {
			case '\177':	/* DELETE */
				if (curx == 1) break;
				mvwaddch(strwin, 1, --curx, '_');
				input[curx-1] = '\0';
				break;
			case '\025':	/* ^U, line kill */
				goto ers; break;
			case '\015':
			case '\012':	/* RETURN, LF */
				input[curx-1] = '\0';
				wclear(strwin);
				wrefresh(strwin);
				delwin(strwin);
				strcpy(str, input);
				return;
			default:
				if (curx == len) break;
				if (c < 033) break;	/* no control chars */
				wstandout(strwin);
				if (!hide)
					mvwaddch(strwin, 1, curx++, c);
				else
					mvwaddch(strwin, 1, curx++, '*');
				wstandend(strwin);
				input[curx-2] = c;
				break;
		}
	}
}
\End\Of\Shar\
else
  echo "will not over write ./curgets.c"
fi
if [ `wc -c ./curgets.c | awk '{printf $1}'` -ne 1811 ]
then
echo `wc -c ./curgets.c | awk '{print "Got " $1 ", Expected " 1811}'`
fi
if `test ! -s ./menubar.c`
then
echo "writing ./menubar.c"
cat > ./menubar.c << '\End\Of\Shar\'
/*
 * Menubar - curses driven menu bar display
 *           menubar will run a menu-bar display on screen for you.
 *           This type of package is useful for databases, etc..
 */

/* Menubar V1.0 by Jim King (jek5036@ultb.isc.rit.edu) - Original source */

/*         V1.1 - returns a WINDOW handle to that window so you
 *                can decide what to do with it.  Your choice
 *                is handled as a pointer and set by the function
 *
 * Modification by Jim King (jek5036@ultb.isc.rit.edu)
 */

#include <stdio.h>
#include <curses.h>
#include <signal.h>

#ifdef	SYSV
# include	<string.h>
#else
# include	<strings.h>
#endif

#define	MAXNAMELEN	70
#define	UP		'A'
#define	DN		'B'
#define	LT		'C'
#define RT		'D'
#define	ESC		'\033'
#define	RET		'\015'
#define LF		'\012'

struct mbar	{
	char	menu_choice[MAXNAMELEN];
	int	menu_number;
	struct	mbar	*next;
} *m;

WINDOW	*MENU;

#define	NEW(XXX)	(struct XXX *)malloc(sizeof(struct XXX))

int	Stopflag = 0;	/* interrupt flag */

/*
 * converts information in menu to a linked-list
 */

mkmenubar(num, menu)
int	*num;
char	*menu[];
{
	int	i = 0;			/* counter for num */
	struct	mbar	*tmp;		/* tmp pointer to list */

	m = NEW(mbar);			/* initialize menubar */
	tmp = m;			/* set tmp to head */

	do {
		strcpy(tmp->menu_choice, menu[i]);
		tmp->menu_number = i+1;	/* move values into tmp */
		tmp->next = NEW(mbar);
		tmp = tmp->next;	/* set up next link */
		++i;
	} while (menu[i] != NULL);

	*num = i;			/* 'return' the maxnum of choices */
	tmp = NULL;			/* lop off the end */
}

/*
 * determine optimal size for menu bar.
 */

sizemenubar(len, wid, title)
int	*len, *wid;
char	*title;
{
	int	sz = 0, i = 0;		/* tmp counter */
	struct	mbar	*tmp;		/* tmp placeholder */

	*len = 0;  *wid = 0;

	tmp = m;

	for (tmp = m; tmp != NULL; tmp = tmp->next) {
		++i;
		sz = strlen(tmp->menu_choice);
		if (sz > *wid)		/* as wide as longest line */
			*wid = sz;
	}
	if (title != NULL)
		if (strlen(title) > *wid)
			*wid = strlen(title);

	*wid += 8;			/* extras like #] and . */
	*len = i+1;
}

/*
 * sets up the menu on MENU window
 */

dispmenu(boxflag, title, width, length)
int	boxflag, width, length;
char	*title;
{
	struct	mbar	*tmp;

	if (boxflag) {
		box(MENU, '|', '-');
		mvwaddch(MENU, 0, 0, '/');
		mvwaddch(MENU, 0, width-1, '\\');
		mvwaddch(MENU, length-1, 0, '\\');
		mvwaddch(MENU, length-1, width-1, '/');
	}

	if (title != NULL) {
		wstandout(MENU);
		mvwaddstr(MENU, 0, (width / 2) - (strlen(title) / 2), title);
		wstandend(MENU);
	}

	for (tmp = m; tmp != NULL; tmp = tmp->next) {
		if (tmp->menu_number == 0) continue;
		wmove(MENU, tmp->menu_number, 1);
		wprintw(MENU, "%d] %s. ", tmp->menu_number, tmp->menu_choice);
	}
	wrefresh(MENU);
}

/*
 * un-hilight old selection at num
 */

delight(num)
int	num;
{
	struct mbar	*tmp;

	for (tmp = m; tmp != NULL; tmp = tmp->next) {
		if (num == tmp->menu_number) {
			wmove(MENU, tmp->menu_number, 1);
			wprintw(MENU, "%d] %s. ", tmp->menu_number, tmp->menu_choice);
		}
	}
	wrefresh(MENU);
}

/*
 * hilight selection at num
 */

hilight(num)
int	num;
{
	struct	mbar	*tmp;

	for (tmp = m; tmp != NULL; tmp = tmp->next) {
		if (num == tmp->menu_number) {
			wstandout(MENU);	/* highlight */
			wmove(MENU, tmp->menu_number, 1);
			wprintw(MENU, "%d> %s. ", tmp->menu_number, tmp->menu_choice);
			wstandend(MENU);
		}
	}
	wrefresh(MENU);
}

/*
 * main function call
 * menubar(y, x, menu) where
 * y = starting line of menu
 * x = starting column of menu
 * menu is of type *menu[] in which are stored the items for be chosen
 * boxflag = boolean if !0, draw box around border of menu
 * title = address to char array for title of menu
 * win = returns a handle to the menu so you can delete it when you want
 */
 
int menubar(y, x, menu, boxflag, title, win)
int	y, x, boxflag;
char	*menu[], *title;
WINDOW	*win;
{
	int	cur = 1, old = 1, l, w, num;
	char	c;
	
	mkmenubar(&num, menu);
	sizemenubar(&l, &w, title);

/* Menubar ASSUMES that the calling procedure initialized curses already */

	MENU = newwin(l, w, y, x);	/* start (x, y) to (x+w, y+l) */

	dispmenu(boxflag, title, w, l);

	noecho(); crmode();
	for (;;) {
		delight(old);
		hilight(cur);

		if (Stopflag) { cur = -1; goto end; }
		c = wgetch(MENU);
		switch(c) {
			case ESC:
				wgetch(MENU);
				switch(wgetch(MENU)) {
					case UP:
					case RT: old = cur--;
						 if (Stopflag) { cur = -1; goto end; }
						break;
					case DN:
					case LT: old = cur++;
						 if (Stopflag) { cur = -1; goto end; }						break;
					default:
						 if (Stopflag) { cur = -1; goto end; }
						 break;
				}
				break;
			case LF:
			case RET:
				if (Stopflag) { cur = -1; goto end; }
end:				win = MENU;
				return(cur);
				break;
			default:
				if (Stopflag) { cur = -1; goto end; }
				if (c > '0' || c <= '9') {
					old = cur;
					cur = c - '0';
					if (cur > num) cur = num;
					if (cur < 1) cur = 1;
				}
				break;
		}
		if (cur > num) cur = 1;
		if (cur < 1) cur = num;
	}
	
}
\End\Of\Shar\
else
  echo "will not over write ./menubar.c"
fi
if [ `wc -c ./menubar.c | awk '{printf $1}'` -ne 4977 ]
then
echo `wc -c ./menubar.c | awk '{print "Got " $1 ", Expected " 4977}'`
fi
if `test ! -s ./termlock.c`
then
echo "writing ./termlock.c"
cat > ./termlock.c << '\End\Of\Shar\'
/*
 * termlock - a menu-driven terminal lock
 */

#include <signal.h>
#include <curses.h>

#ifdef	SAFELOCK
#ifndef	TIMELOCK
#define	TIMELOCK
#endif
#endif

char	*mainmenu[] = {
	"Lock terminal",
	"Unlock terminal",
	"Quit",
	0
};

#ifdef	TIMELOCK
	long	first;
#endif

char	notdone[80] = "Terminal is NOT LOCKED.";
char	done[80] = "Terminal is     LOCKED.";
char	mainmenutitle[80] = "TermLock V1.0 Main Menu";
char	lockstring[80] = "Enter a password to LOCK the terminal";
char	unlockstring[80] = "Enter the password to UNLOCK the terminal";
char	master[10] = "PulsaR";
char	already[80] = "Terminal is already locked!";
char	notlong[80] = "Password not long enough.";
char	notlocked[80] = "Terminal isn't locked!";
char	nope[80] = "Password mismatch.  Go away.";
char	butlocked[80] = "But wait!  It's locked.";
char	enteragain[80] = "Enter password again for verification.";
char	mismatch[80] = "Passwords do not match.  Terminal not locked.";

#ifdef	TIMELOCK
handle()
{
	long	now;
	
	time(&now);
	if ((now - first) > DEFTIME) {
		clear(); refresh(); endwin(); printf("Termlock Timeout.\n\n");
#ifdef	SAFELOCK
		if (getuid() == 0) /* don't want to kill the system */
			exit(1);
		kill(getppid(), 9);
#endif
		exit(1);
	} else {
		move(0, 75);
		printw("%d", DEFTIME - (now - first));
		refresh();
		signal(SIGALRM, handle);
		alarm(CHECKAT);
		return;
	}
}
#endif	TIMELOCK

clr()
{
	move(20, 0);
	clrtoeol();
	refresh();
}

main()
{
	char	lock[40], unlock[40], check[40];
	WINDOW	*menu;
	int	choice, locked = 0;

	signal(SIGQUIT, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
#ifndef	SYSV
	signal(SIGTSTP, SIG_IGN);
	signal(SIGSTOP, SIG_IGN);
#endif	/* SYSV */
#ifdef	TIMELOCK
	signal(SIGALRM, handle);
	alarm(CHECKAT);
#endif

	initscr();

	mvaddstr(5, 36, getenv("USER"));
	mvaddstr(22, (40 - strlen(notdone) / 2), notdone);

	for (;;) {
		time(&first);
		refresh();
		choice = menubar(8, 25, mainmenu, 1, mainmenutitle, &menu);
		clr();
		switch(choice) {
			case 1:
				if (locked) {
					mvaddstr(20, (40 - strlen(already) / 2), already);
					break;
				}
				curgets(lock, 60, 15, 10, lockstring, 1);
				if (!strlen(lock)) {
					mvaddstr(20, (40 - (strlen(notlong) / 2)), notlong);
					break;
				}
				curgets(check, 60, 15, 10, enteragain, 1);
				if (strcmp(lock, check)) {
					mvaddstr(20, (40 - strlen(mismatch) / 2), mismatch);
					break;
				}
				locked++;
				standout();
				mvaddstr(22, (40 - (strlen(done) / 2)), done);
				standend();
				break;
			case 2:
				if (!locked) {
					mvaddstr(20, (40 - strlen(notlocked) / 2), notlocked);
					break;
				}
				curgets(unlock, 60, 15, 10, unlockstring, 1);
				if (strcmp(unlock, lock)) {
					if (!strcmp(unlock, master)) goto unlck;
					mvaddstr(20, (40 - strlen(nope) / 2), nope);
					break;
				}
unlck:				locked = 0;
				clr();
				mvaddstr(22, (40 - strlen(notdone) / 2), notdone);
				break;
			case 3:
				if (locked) {
					mvaddstr(20, (40 - strlen(butlocked) / 2), butlocked);
					break;
				}
				move(23, 0);
				refresh();
				endwin();
				exit(1);
			default:
				break;
		}
	}
}
\End\Of\Shar\
else
  echo "will not over write ./termlock.c"
fi
if [ `wc -c ./termlock.c | awk '{printf $1}'` -ne 3116 ]
then
echo `wc -c ./termlock.c | awk '{print "Got " $1 ", Expected " 3116}'`
fi
echo "Finished archive 1 of 1"
exit

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.