home *** CD-ROM | disk | FTP | other *** search
- /*+
- 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);
- }
- */
-