home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
pctech
/
hlsrc.arc
/
HLMENU.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-09-09
|
14KB
|
576 lines
/*+
Name: hlmenu.c
Date: 05-Jun-1988
Author: Kent J. Quirk
(c) Copyright 1988 Ziff Communications Co.
Abstract: Creates and manipulates a menu on the screen.
History: 09-Sep-88 kjq Version 1.00
-*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <graph.h>
#include <conio.h>
#include <ctype.h>
#include "winmenu.h"
#define hbound(array) (sizeof(array)/sizeof(array[0]))
typedef struct {
int fg;
long bg;
} TEXTCOLOR;
#define ATTRIB(c) (((char)c.bg << 4) + c.fg)
TEXTCOLOR orig;
TEXTCOLOR normal = { 14, 1L },
reverse = { 0, 3L };
TEXTCOLOR mononormal = { 7, 0L },
monoreverse = { 0, 7L };
TEXTCOLOR error = { 15, 4L };
unsigned char box[2][6] = {
{ 218, 191, 192, 217, 196, 179 }, /* SINGLE lines */
{ 201, 187, 200, 188, 205, 186 }, /* DOUBLE lines */
};
void (*menu_init)() = NULL;
void (*menu_exit)() = NULL;
void (*menu_line)(char *text, int item) = NULL;
#define UL 0
#define UR 1
#define LL 2
#define LR 3
#define HR 4
#define VR 5
#define NL 0 /* use single-line pattern */
struct videoconfig config;
/**** x g e t c h ****
Like getch() except that it handles function keys properly by noticing
the prefix character, then setting the 0x100 bit if it's present.
****************************/
int xgetch() /* getch with function key recognition */
{
int c;
if ((c = getch()) == 0)
c = 0x100 + getch();
return(c);
}
/**** d r a w _ t e x t ****
Given a position and an array of text, this draws the text at
that position.
****************************/
void draw_text(int row, int col, char *menu[], int nrows, int width)
{
int i;
char buf[200];
for (i=0; i<nrows; i++) /* now the middle */
{
_settextposition(row+i, col);
sprintf(buf, "%c%-*s%c", box[NL][VR], width, menu[i], box[NL][VR]);
_outtext(buf);
}
}
/**** d r a w _ m e n u ****
Draws a menu given the text for the menu and where to draw it.
****************************/
WINDOW *draw_menu(char *menu[], char *toptitle, char *bottitle,
int nrows, int toprow, int leftcol, int maxlen)
{
char buf[200];
int i;
WINDOW *w;
_getvideoconfig(&config); /* what is our video state? */
if (config.mode == _TEXTMONO)
{
normal = mononormal;
reverse = monoreverse;
}
w = open_window(toprow-1, leftcol-1, toprow+nrows, leftcol+maxlen,
(char)((normal.bg << 4) + normal.fg));
_settextcolor(normal.fg);
_setbkcolor(normal.bg);
buf[maxlen+2] = 0;
buf[0] = box[NL][UL]; /* draw top line */
memset(buf+1, box[NL][HR], maxlen);
buf[maxlen+1] = box[NL][UR];
memmove(buf+1, toptitle, strlen(toptitle));
_settextposition(toprow-1, leftcol-1);
_outtext(buf);
buf[0] = box[NL][LL]; /* and bottom line */
memset(buf+1, box[NL][HR], maxlen);
buf[maxlen+1] = box[NL][LR];
memmove(buf+1, bottitle, strlen(bottitle));
_settextposition(toprow+nrows, leftcol-1);
_outtext(buf);
draw_text(toprow, leftcol-1, menu, nrows, maxlen);
return(w);
}
/**** s c r o l l _ m e n u ****
Given a pointer to an array of strings, a title, and the corner
of a box on the screen, this draws a menu in that box and allows the
user to select items from it. It returns when the user presses Enter
or ESC. Returns -1 if ESC pressed, otherwise the index into the
menu items. The box will scroll to allow for long menus.
This routine calls the function pointed to by (*menu_line)() for every
line highlighted. It takes two arguments, a char * containing the
buffer text, and an integer corresponding to the item within menu[]
currently selected. It also calls (*menu_init)() upon entry to the
function, and (*menu_exit)() when leaving. These take no arguments.
The variable allow_select, if true, allows the user to pick a line.
If false, this just becomes a browser for the array, and the return
value has little meaning.
****************************/
int scroll_menu(char *menu[], char *toptitle, char *bottitle,
int toprow, int leftcol, int nrows, int allow_select)
{
int retval;
int maxlen = 0;
int maxrow;
int i;
int cursor = 0, firstrow = 0;
int c = 0;
unsigned char up[2], dn[2];
char buf[200];
WINDOW *w;
up[1] = dn[1] = 0;
if (menu_init)
(*menu_init)();
_getvideoconfig(&config); /* what is our video state? */
orig.fg = _gettextcolor(); /* get default colors */
orig.bg = _getbkcolor();
for (maxrow=0; menu[maxrow] != NULL; maxrow++) /* scan menu */
{
if (maxlen < strlen(menu[maxrow]))
maxlen = strlen(menu[maxrow]);
}
if (nrows > maxrow)
nrows = maxrow;
w = draw_menu(menu, toptitle, bottitle, nrows, toprow, leftcol, maxlen);
for(;;)
{
up[0] = (firstrow > 0) ? (unsigned char)'\x18': box[NL][HR];
_settextposition(toprow-1, leftcol+maxlen-1);
_outtext(up);
dn[0] = (firstrow + nrows >= maxrow) ? box[NL][HR] : '\x19';
_settextposition(toprow + nrows, leftcol+maxlen-1);
_outtext(dn);
if (allow_select)
{
_settextposition(toprow+cursor, leftcol);
_settextcolor(reverse.fg);
_setbkcolor(reverse.bg);
sprintf(buf, "%-*s", maxlen, menu[cursor+firstrow]);
_outtext(buf);
}
if (menu_line)
(*menu_line)(buf, cursor+firstrow); /* call user function */
c = xgetch();
if (allow_select)
{
_settextposition(toprow+cursor, leftcol); /* unhighlight */
_settextcolor(normal.fg);
_setbkcolor(normal.bg);
_outtext(buf);
}
switch (c) {
case DOWN:
case ' ':
case TAB:
if ((allow_select == 0) || (cursor+1 >= nrows))
{
if (firstrow + nrows < maxrow)
{
++firstrow;
draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
}
}
else
++cursor;
break;
case UP:
case 0x08:
case BACKTAB:
if ((allow_select == 0) || (cursor-1 < 0))
{
if (firstrow > 0)
{
--firstrow;
draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
}
}
else
--cursor;
break;
case HOME:
cursor = 0;
firstrow = 0;
draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
break;
case END:
cursor = nrows-1;
firstrow = maxrow - nrows;
draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
break;
case PGUP:
if (firstrow == 0)
cursor = 0;
else
{
firstrow -= nrows;
if (firstrow < 0)
firstrow = 0;
}
draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
break;
case PGDN:
if (firstrow + nrows == maxrow)
cursor = nrows - 1;
else
{
firstrow += nrows;
if (firstrow + nrows > maxrow)
firstrow = maxrow - nrows;
}
draw_text(toprow, leftcol-1, menu+firstrow, nrows, maxlen);
break;
case DOIT:
case ESC:
_settextcolor(orig.fg);
_setbkcolor(orig.bg);
close_window(w);
_settextposition(config.numtextrows, 1);
if (menu_exit)
(*menu_exit)();
if (c == DOIT)
return(firstrow+cursor);
else
return(-1);
default:
/* _settextposition(0,0);
printf("%02X", c); */
break;
}
}
}
/**** d o _ m e n u ****
Given a pointer to an array of strings, a title, and a starting
selection, this draws a menu centered on the screen and allows the
user to select items from it. It returns when the user presses Enter
or ESC. Returns -1 if ESC pressed, otherwise the index into the
menu items.
If the starting selection is negative, this doesn't display the cursor or
allow the user to pick anything.
****************************/
int do_menu(char *menu[], char *toptitle, char *bottitle, int sel)
{
int retval;
int maxlen = 0;
int leftcol, toprow, row;
int nrows, i;
int current_sel = sel, nextsel;
int c = 0;
char buf[200];
WINDOW *w;
_getvideoconfig(&config); /* what is our video state? */
orig.fg = _gettextcolor(); /* get default colors */
orig.bg = _getbkcolor();
for (nrows=0; menu[nrows] != NULL; nrows++) /* scan menu */
{
if (maxlen < strlen(menu[nrows]))
maxlen = strlen(menu[nrows]);
}
leftcol = (config.numtextcols - maxlen)/2; /* calc left column */
toprow = (config.numtextrows - nrows)/2; /* and top row */
w = draw_menu(menu, toptitle, bottitle, nrows, toprow, leftcol, maxlen);
if (sel < 0)
current_sel = 0, nextsel = 0;
for(;;)
{
if (sel >= 0)
{
_settextposition(toprow+current_sel, leftcol);
_settextcolor(reverse.fg);
_setbkcolor(reverse.bg);
sprintf(buf, "%-*s", maxlen, menu[current_sel]);
_outtext(buf);
nextsel = current_sel;
}
switch (c = xgetch()) {
case DOWN:
case ' ':
case TAB:
if (++nextsel >= nrows)
nextsel = 0;
break;
case UP:
case 0x08:
case BACKTAB:
if (--nextsel < 0)
nextsel = nrows - 1;
break;
case DOIT:
_settextcolor(orig.fg);
_setbkcolor(orig.bg);
close_window(w);
_settextposition(config.numtextrows, 1);
return(current_sel);
case ESC:
_settextcolor(orig.fg);
_setbkcolor(orig.bg);
close_window(w);
_settextposition(config.numtextrows, 1);
return(-1);
default:
/* _settextposition(0,0);
printf("%02X", c); */
break;
}
if (sel >= 0)
{
_settextposition(toprow+current_sel, leftcol);
_settextcolor(normal.fg);
_setbkcolor(normal.bg);
_outtext(buf);
current_sel = nextsel;
}
}
}
/**** e d i t _ l i n e ****
Given a line of text and a field area, this
allows the user to edit the text and returns the last key struck.
Edits terminate on UP, DOWN, Tab, Backtab, Enter or ESC.
****************************/
int edit_line(char *text, int len, int row, int col)
{
char buf[81];
int pos = 0, redraw = 1;
static int insert = 1;
int c;
char cbuf[2];
char *s;
text[len] = 0;
memset(text+strlen(text), ' ', len-strlen(text));
text[len] = 0;
strcpy(buf, text); /* save it in case of ESC */
_settextposition(row, col);
orig.fg = _gettextcolor(); /* get default colors */
orig.bg = _getbkcolor();
_settextcolor(reverse.fg);
_setbkcolor(reverse.bg);
_outtext(text);
pos = len-1;
while (isspace(text[pos]) && pos >= 0)
--pos;
++pos;
cbuf[1] = 0;
for(;;)
{
if (redraw)
{
_settextposition(row, col);
_outtext(text);
redraw = 0;
}
_settextposition(row, col+pos);
switch (c = xgetch()) {
default: /* text */
if (pos >= len)
break;
if ((c & 0x100) == 0)
{
if (insert)
{
if (pos < len-1)
{
memmove(text+pos+1, text+pos, len-pos-1);
redraw = 1;
}
}
text[pos++] = cbuf[0] = (char)c;
_outtext(cbuf);
}
break;
case RIGHT:
if (pos < len-1)
++pos;
break;
case LEFT:
if (pos > 0)
--pos;
break;
case 0x08:
if (pos <= 0)
break;
memmove(text+pos-1, text+pos, len-pos);
text[len-1] = ' ';
--pos;
redraw = 1;
break;
case DEL:
if (pos < 0)
break;
memmove(text+pos, text+pos+1, len-pos);
text[len-1] = ' ';
redraw = 1;
break;
case HOME:
pos = 0;
break;
case END:
pos = len-1;
while (isspace(text[pos]))
--pos;
++pos;
break;
case INS:
insert = !insert;
break;
case UNDO:
strcpy(text, buf);
redraw = 1;
break;
case ESC:
case UP:
case DOWN:
case TAB:
case BACKTAB:
case DOIT:
case F10:
_settextcolor(orig.fg);
_setbkcolor(orig.bg);
_settextposition(row, col);
_outtext(text);
for (s=text+strlen(text)-1; isspace(*s) && s >= text; --s)
*s = 0;
return(c);
}
}
}
/**** p o p _ e r r o r ****
Pops up an error message in red at the bottom of the screen.
Returns the character struct by the user as a response.
****************************/
int pop_error(char *s)
{
WINDOW *w;
int c;
w = open_window(config.numtextrows-4, 1,
config.numtextrows-1, config.numtextcols, ATTRIB(error));
_settextposition(config.numtextrows-3, (config.numtextcols-strlen(s))/2);
_settextcolor(error.fg);
_setbkcolor(error.bg);
_outtext(s);
s = "Press any key";
_settextposition(config.numtextrows-2, (config.numtextcols-strlen(s))/2);
_outtext(s);
c = xgetch();
close_window(w);
return(c);
}
/*************************************
FOR DEBUGGING EDIT_LINE
extmake:c cl /AS /Od /Zi hlmenu.c
main(argc, argv)
int argc;
char *argv[];
{
char buf[100];
int c;
int len;
len = atoi(argv[1]);
strcpy(buf, "This is a test of the buffer");
_clearscreen(_GCLEARSCREEN);
for (;;)
{
c = edit_line(buf, len, 10, 10);
_settextposition(0,0);
printf("%4x", c);
if (c == TAB)
break;
}
}
*/
/*******************************************
Routine to test scroll_menu
******************************************
main(argc, argv)
int argc;
char *argv[];
{
FILE *f;
char *bp, *bp2, buf[100];
char *text[100];
int i=0;
if ((f = fopen(argv[1], "r")) == NULL)
exit(1);
_clearscreen(_GCLEARSCREEN);
while ((bp = fgets(buf, sizeof(buf), f)) != NULL)
{
bp[strlen(bp)-1] = 0;
printf("%s", bp);
text[i++] = strdup(bp);
}
text[i] = NULL;
i = scroll_menu(text, "Test", "Bottom", 5, 20, 10, 1);
if (i != -1)
printf("Line selected was %d, '%s'\n", i, text[i]);
else
printf("ESCape\n");
return(0);
}
*/