1 /********************************* tui.c ************************************/
3 * 'textual user interface'
5 * $Id: tui.c,v 1.34 2008/07/14 12:35:23 wmcbrine Exp $
7 * Author : P.J. Kunst <kunst@prl.philips.nl>
19 void statusmsg(char *);
23 #if defined(__unix) && !defined(__DJGPP__)
28 # define TITLECOLOR 1 /* color pair indices */
29 # define MAINMENUCOLOR (2 | A_BOLD)
30 # define MAINMENUREVCOLOR (3 | A_BOLD | A_REVERSE)
31 # define SUBMENUCOLOR (4 | A_BOLD)
32 # define SUBMENUREVCOLOR (5 | A_BOLD | A_REVERSE)
34 # define STATUSCOLOR (7 | A_BOLD)
35 # define INPUTBOXCOLOR 8
36 # define EDITBOXCOLOR (9 | A_BOLD | A_REVERSE)
38 # define TITLECOLOR 0 /* color pair indices */
39 # define MAINMENUCOLOR (A_BOLD)
40 # define MAINMENUREVCOLOR (A_BOLD | A_REVERSE)
41 # define SUBMENUCOLOR (A_BOLD)
42 # define SUBMENUREVCOLOR (A_BOLD | A_REVERSE)
44 # define STATUSCOLOR (A_BOLD)
45 # define INPUTBOXCOLOR 0
46 # define EDITBOXCOLOR (A_BOLD | A_REVERSE)
50 #define th 1 /* title window height */
51 #define mh 1 /* main menu height */
52 #define sh 2 /* status window height */
53 #define bh (LINES - th - mh - sh) /* body window height */
54 #define bw COLS /* body window width */
57 /******************************* STATIC ************************************/
59 static WINDOW *wtitl, *wmain, *wbody, *wstat; /* title, menu, body, status win*/
60 static int nexty, nextx;
61 static int key = ERR, ch = ERR;
62 static bool quit = FALSE;
63 static bool incurses = FALSE;
66 static char wordchar(void)
72 static char *padstr(char *s, int length)
74 static char buf[MAXSTRLEN];
77 sprintf(fmt, (int)strlen(s) > length ? "%%.%ds" : "%%-%ds", length);
83 static char *prepad(char *s, int length)
90 memmove((void *)(s + length), (const void *)s, strlen(s) + 1);
92 for (i = 0; i < length; i++)
99 static void rmline(WINDOW *win, int nr) /* keeps box lines intact */
101 mvwaddstr(win, nr, 1, padstr(" ", bw - 2));
105 static void initcolor(void)
111 /* foreground, background */
113 init_pair(TITLECOLOR & ~A_ATTR, COLOR_BLACK, COLOR_CYAN);
114 init_pair(MAINMENUCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_CYAN);
115 init_pair(MAINMENUREVCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
116 init_pair(SUBMENUCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_CYAN);
117 init_pair(SUBMENUREVCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
118 init_pair(BODYCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_BLUE);
119 init_pair(STATUSCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_CYAN);
120 init_pair(INPUTBOXCOLOR & ~A_ATTR, COLOR_BLACK, COLOR_CYAN);
121 init_pair(EDITBOXCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
125 static void setcolor(WINDOW *win, chtype color)
127 chtype attr = color & A_ATTR; /* extract Bold, Reverse, Blink bits */
130 attr &= ~A_REVERSE; /* ignore reverse, use colors instead! */
131 wattrset(win, COLOR_PAIR(color & A_CHARTEXT) | attr);
133 attr &= ~A_BOLD; /* ignore bold, gives messy display on HP-UX */
138 static void colorbox(WINDOW *win, chtype color, int hasbox)
144 chtype attr = color & A_ATTR; /* extract Bold, Reverse, Blink bits */
146 setcolor(win, color);
150 wbkgd(win, COLOR_PAIR(color & A_CHARTEXT) | (attr & ~A_REVERSE));
160 getmaxyx(win, maxy, maxx);
162 if (hasbox && (maxy > 2))
169 static void idle(void)
176 return; /* time not available */
179 sprintf(buf, " %.2d-%.2d-%.4d %.2d:%.2d:%.2d",
180 tp->tm_mday, tp->tm_mon + 1, tp->tm_year + 1900,
181 tp->tm_hour, tp->tm_min, tp->tm_sec);
183 mvwaddstr(wtitl, 0, bw - strlen(buf) - 2, buf);
187 static void menudim(menu *mp, int *lines, int *columns)
191 for (n=0; mp->func; n++, mp++)
192 if ((l = strlen(mp->name)) > mmax) mmax = l;
198 static void setmenupos(int y, int x)
204 static void getmenupos(int *y, int *x)
210 static int hotkey(const char *s)
212 int c0 = *s; /* if no upper case found, return first char */
215 if (isupper((unsigned char)*s))
221 static void repaintmenu(WINDOW *wmenu, menu *mp)
226 for (i = 0; p->func; i++, p++)
227 mvwaddstr(wmenu, i + 1, 2, p->name);
233 static void repaintmainmenu(int width, menu *mp)
238 for (i = 0; p->func; i++, p++)
239 mvwaddstr(wmain, 0, i * width, prepad(padstr(p->name, width - 1), 1));
245 static void mainhelp(void)
248 statusmsg("Use arrow keys and Enter to select (Alt-X to quit)");
250 statusmsg("Use arrow keys and Enter to select");
254 static void mainmenu(menu *mp)
256 int nitems, barlen, old = -1, cur = 0, c, cur0;
258 menudim(mp, &nitems, &barlen);
259 repaintmainmenu(barlen, mp);
267 mvwaddstr(wmain, 0, old * barlen,
268 prepad(padstr(mp[old].name, barlen - 1), 1));
270 statusmsg(mp[cur].desc);
275 setcolor(wmain, MAINMENUREVCOLOR);
277 mvwaddstr(wmain, 0, cur * barlen,
278 prepad(padstr(mp[cur].name, barlen - 1), 1));
280 setcolor(wmain, MAINMENUCOLOR);
285 switch (c = (key != ERR ? key : waitforkey()))
288 case '\n': /* menu item selected */
292 setmenupos(th + mh, cur * barlen);
294 (mp[cur].func)(); /* perform function */
300 cur = (cur + nitems - 1) % nitems;
305 cur = (cur + 1) % nitems;
313 repaintmainmenu(barlen, mp);
318 cur = (cur + nitems - 1) % nitems;
322 cur = (cur + 1) % nitems;
334 cur = (cur + 1) % nitems;
336 } while ((cur != cur0) && (hotkey(mp[cur].name) != toupper(c)));
338 if (hotkey(mp[cur].name) == toupper(c))
349 static void cleanup(void) /* cleanup curses settings */
364 /******************************* EXTERNAL **********************************/
375 return getmaxy(wbody);
379 getmaxyx(wbody, maxy, maxx);
384 WINDOW *bodywin(void)
399 void titlemsg(char *msg)
401 mvwaddstr(wtitl, 0, 2, padstr(msg, bw - 3));
405 void bodymsg(char *msg)
411 void errormsg(char *msg)
414 mvwaddstr(wstat, 0, 2, padstr(msg, bw - 3));
418 void statusmsg(char *msg)
420 mvwaddstr(wstat, 1, 2, padstr(msg, bw - 3));
424 bool keypressed(void)
437 quit = (c == ALT_X); /* PC only ! */
444 do idle(); while (!keypressed());
448 void DoExit(void) /* terminate program */
453 void domenu(menu *mp)
455 int y, x, nitems, barlen, mheight, mw, old = -1, cur = 0, cur0;
461 menudim(mp, &nitems, &barlen);
462 mheight = nitems + 2;
464 wmenu = newwin(mheight, mw, y, x);
465 colorbox(wmenu, SUBMENUCOLOR, 1);
466 repaintmenu(wmenu, mp);
470 while (!stop && !quit)
475 mvwaddstr(wmenu, old + 1, 1,
476 prepad(padstr(mp[old].name, barlen - 1), 1));
478 setcolor(wmenu, SUBMENUREVCOLOR);
479 mvwaddstr(wmenu, cur + 1, 1,
480 prepad(padstr(mp[cur].name, barlen - 1), 1));
482 setcolor(wmenu, SUBMENUCOLOR);
483 statusmsg(mp[cur].desc);
489 switch (key = ((key != ERR) ? key : waitforkey()))
491 case '\n': /* menu item selected */
494 setmenupos(y + 1, x + 1);
499 (mp[cur].func)(); /* perform function */
502 repaintmenu(wmenu, mp);
508 cur = (cur + nitems - 1) % nitems;
513 cur = (cur + 1) % nitems;
521 key = ERR; /* return to prev submenu */
531 cur = (cur + 1) % nitems;
533 } while ((cur != cur0) &&
534 (hotkey(mp[cur].name) != toupper((int)key)));
536 key = (hotkey(mp[cur].name) == toupper((int)key)) ? '\n' : ERR;
547 void startmenu(menu *mp, char *mtitle)
553 wtitl = subwin(stdscr, th, bw, 0, 0);
554 wmain = subwin(stdscr, mh, bw, th, 0);
555 wbody = subwin(stdscr, bh, bw, th + mh, 0);
556 wstat = subwin(stdscr, sh, bw, th + mh + bh, 0);
558 colorbox(wtitl, TITLECOLOR, 0);
559 colorbox(wmain, MAINMENUCOLOR, 0);
560 colorbox(wbody, BODYCOLOR, 0);
561 colorbox(wstat, STATUSCOLOR, 0);
566 cbreak(); /* direct input (no newline required)... */
567 noecho(); /* ... without echoing */
568 curs_set(0); /* hide cursor (if possible) */
569 nodelay(wbody, TRUE); /* don't wait for input... */
570 halfdelay(10); /* ...well, no more than a second, anyway */
571 keypad(wbody, TRUE); /* enable cursor keys */
572 scrollok(wbody, TRUE); /* enable scrolling in main window */
574 leaveok(stdscr, TRUE);
575 leaveok(wtitl, TRUE);
576 leaveok(wmain, TRUE);
577 leaveok(wstat, TRUE);
584 static void repainteditbox(WINDOW *win, int x, char *buf)
594 getmaxyx(win, maxy, maxx);
597 mvwprintw(win, 0, 0, "%s", padstr(buf, maxx));
604 weditstr() - edit string
607 The initial value of 'str' with a maximum length of 'field' - 1,
608 which is supplied by the calling routine, is editted. The user's
609 erase (^H), kill (^U) and delete word (^W) chars are interpreted.
610 The PC insert or Tab keys toggle between insert and edit mode.
611 Escape aborts the edit session, leaving 'str' unchanged.
612 Enter, Up or Down Arrow are used to accept the changes to 'str'.
613 NOTE: editstr(), mveditstr(), and mvweditstr() are macros.
616 Returns the input terminating character on success (Escape,
617 Enter, Up or Down Arrow) and ERR on error.
620 It is an error to call this function with a NULL window pointer.
621 The length of the initial 'str' must not exceed 'field' - 1.
625 int weditstr(WINDOW *win, char *buf, int field)
627 char org[MAXSTRLEN], *tp, *bp = buf;
628 bool defdisp = TRUE, stop = FALSE, insert = FALSE;
629 int cury, curx, begy, begx, oldattr;
633 if ((field >= MAXSTRLEN) || (buf == NULL) ||
634 ((int)strlen(buf) > field - 1))
637 strcpy(org, buf); /* save original */
640 getyx(win, cury, curx);
641 getbegyx(win, begy, begx);
643 wedit = subwin(win, 1, field, begy + cury, begx + curx);
644 oldattr = wedit->_attrs;
645 colorbox(wedit, EDITBOXCOLOR, 0);
653 repainteditbox(wedit, bp - buf, buf);
655 switch (c = wgetch(wedit))
661 strcpy(buf, org); /* restore original */
678 if (bp - buf < (int)strlen(buf))
682 case '\t': /* TAB -- because insert
684 case KEY_IC: /* enter insert mode */
685 case KEY_EIC: /* exit insert mode */
689 curs_set(insert ? 2 : 1);
693 if (c == erasechar()) /* backspace, ^H */
697 memmove((void *)(bp - 1), (const void *)bp, strlen(bp) + 1);
701 else if (c == killchar()) /* ^U */
706 else if (c == wordchar()) /* ^W */
710 while ((bp > buf) && (*(bp - 1) == ' '))
712 while ((bp > buf) && (*(bp - 1) != ' '))
715 memmove((void *)bp, (const void *)tp, strlen(tp) + 1);
728 if ((int)strlen(buf) < field - 1)
730 memmove((void *)(bp + 1), (const void *)bp,
736 else if (bp - buf < field - 1)
738 /* append new string terminator */
751 wattrset(wedit, oldattr);
752 repainteditbox(wedit, bp - buf, buf);
758 WINDOW *winputbox(WINDOW *win, int nlines, int ncols)
761 int cury, curx, begy, begx;
763 getyx(win, cury, curx);
764 getbegyx(win, begy, begx);
766 winp = newwin(nlines, ncols, begy + cury, begx + curx);
767 colorbox(winp, INPUTBOXCOLOR, 1);
772 int getstrings(char *desc[], char *buf[], int field)
775 int oldy, oldx, maxy, maxx, nlines, ncols, i, n, l, mmax = 0;
779 for (n = 0; desc[n]; n++)
780 if ((l = strlen(desc[n])) > mmax)
783 nlines = n + 2; ncols = mmax + field + 4;
784 getyx(wbody, oldy, oldx);
785 getmaxyx(wbody, maxy, maxx);
787 winput = mvwinputbox(wbody, (maxy - nlines) / 2, (maxx - ncols) / 2,
790 for (i = 0; i < n; i++)
791 mvwprintw(winput, i + 1, 2, "%s", desc[i]);
797 switch (c = mvweditstr(winput, i+1, mmax+3, buf[i], field))
811 stop = TRUE; /* all passed? */
817 wmove(wbody, oldy, oldx);