blob: 62e0d537670948222f8332b0f5fffdb747c9e25c [file] [log] [blame]
Patrick Georgi3b77b722011-07-07 15:41:53 +02001/********************************* tui.c ************************************/
2/*
3 * 'textual user interface'
4 *
5 * $Id: tui.c,v 1.34 2008/07/14 12:35:23 wmcbrine Exp $
6 *
7 * Author : P.J. Kunst <kunst@prl.philips.nl>
8 * Date : 25-02-93
9 */
10
11#include <ctype.h>
12#include <curses.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <time.h>
17#include "tui.h"
18
19void statusmsg(char *);
20int waitforkey(void);
21void rmerror(void);
22
23#if defined(__unix) && !defined(__DJGPP__)
24#include <unistd.h>
25#endif
26
27#ifdef A_COLOR
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)
33# define BODYCOLOR 6
34# define STATUSCOLOR (7 | A_BOLD)
35# define INPUTBOXCOLOR 8
36# define EDITBOXCOLOR (9 | A_BOLD | A_REVERSE)
37#else
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)
43# define BODYCOLOR 0
44# define STATUSCOLOR (A_BOLD)
45# define INPUTBOXCOLOR 0
46# define EDITBOXCOLOR (A_BOLD | A_REVERSE)
47#endif
48
49
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 */
55
56
57/******************************* STATIC ************************************/
58
59static WINDOW *wtitl, *wmain, *wbody, *wstat; /* title, menu, body, status win*/
60static int nexty, nextx;
61static int key = ERR, ch = ERR;
62static bool quit = FALSE;
63static bool incurses = FALSE;
64
65#ifndef PDCURSES
66static char wordchar(void)
67{
Stefan Reinauere11835e2011-10-31 12:54:00 -070068 return 0x17; /* ^W */
Patrick Georgi3b77b722011-07-07 15:41:53 +020069}
70#endif
71
72static char *padstr(char *s, int length)
73{
74 static char buf[MAXSTRLEN];
75 char fmt[10];
76
77 sprintf(fmt, (int)strlen(s) > length ? "%%.%ds" : "%%-%ds", length);
78 sprintf(buf, fmt, s);
79
80 return buf;
81}
82
83static char *prepad(char *s, int length)
84{
85 int i;
86 char *p = s;
87
88 if (length > 0)
89 {
90 memmove((void *)(s + length), (const void *)s, strlen(s) + 1);
91
92 for (i = 0; i < length; i++)
93 *p++ = ' ';
94 }
95
96 return s;
97}
98
99static void rmline(WINDOW *win, int nr) /* keeps box lines intact */
100{
101 mvwaddstr(win, nr, 1, padstr(" ", bw - 2));
102 wrefresh(win);
103}
104
105static void initcolor(void)
106{
107#ifdef A_COLOR
108 if (has_colors())
109 start_color();
110
111 /* foreground, background */
112
Stefan Reinauere11835e2011-10-31 12:54:00 -0700113 init_pair(TITLECOLOR & ~A_ATTR, COLOR_BLACK, COLOR_CYAN);
114 init_pair(MAINMENUCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_CYAN);
Patrick Georgi3b77b722011-07-07 15:41:53 +0200115 init_pair(MAINMENUREVCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
Stefan Reinauere11835e2011-10-31 12:54:00 -0700116 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);
Patrick Georgi3b77b722011-07-07 15:41:53 +0200120 init_pair(INPUTBOXCOLOR & ~A_ATTR, COLOR_BLACK, COLOR_CYAN);
121 init_pair(EDITBOXCOLOR & ~A_ATTR, COLOR_WHITE, COLOR_BLACK);
122#endif
123}
124
125static void setcolor(WINDOW *win, chtype color)
126{
127 chtype attr = color & A_ATTR; /* extract Bold, Reverse, Blink bits */
128
129#ifdef A_COLOR
130 attr &= ~A_REVERSE; /* ignore reverse, use colors instead! */
131 wattrset(win, COLOR_PAIR(color & A_CHARTEXT) | attr);
132#else
133 attr &= ~A_BOLD; /* ignore bold, gives messy display on HP-UX */
134 wattrset(win, attr);
135#endif
136}
137
138static void colorbox(WINDOW *win, chtype color, int hasbox)
139{
140 int maxy;
141#ifndef PDCURSES
142 int maxx;
143#endif
144 chtype attr = color & A_ATTR; /* extract Bold, Reverse, Blink bits */
145
146 setcolor(win, color);
147
148#ifdef A_COLOR
149 if (has_colors())
150 wbkgd(win, COLOR_PAIR(color & A_CHARTEXT) | (attr & ~A_REVERSE));
151 else
152#endif
153 wbkgd(win, attr);
154
Stefan Reinauere11835e2011-10-31 12:54:00 -0700155 werase(win);
Patrick Georgi3b77b722011-07-07 15:41:53 +0200156
157#ifdef PDCURSES
158 maxy = getmaxy(win);
159#else
160 getmaxyx(win, maxy, maxx);
161#endif
162 if (hasbox && (maxy > 2))
163 box(win, 0, 0);
164
165 touchwin(win);
166 wrefresh(win);
167}
168
169static void idle(void)
170{
171 char buf[MAXSTRLEN];
172 time_t t;
173 struct tm *tp;
174
175 if (time (&t) == -1)
176 return; /* time not available */
177
178 tp = localtime(&t);
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);
182
183 mvwaddstr(wtitl, 0, bw - strlen(buf) - 2, buf);
Stefan Reinauere11835e2011-10-31 12:54:00 -0700184 wrefresh(wtitl);
Patrick Georgi3b77b722011-07-07 15:41:53 +0200185}
186
187static void menudim(menu *mp, int *lines, int *columns)
188{
189 int n, l, mmax = 0;
190
191 for (n=0; mp->func; n++, mp++)
192 if ((l = strlen(mp->name)) > mmax) mmax = l;
193
194 *lines = n;
195 *columns = mmax + 2;
196}
197
198static void setmenupos(int y, int x)
199{
200 nexty = y;
201 nextx = x;
202}
203
204static void getmenupos(int *y, int *x)
205{
206 *y = nexty;
207 *x = nextx;
208}
209
210static int hotkey(const char *s)
211{
212 int c0 = *s; /* if no upper case found, return first char */
213
214 for (; *s; s++)
215 if (isupper((unsigned char)*s))
216 break;
217
218 return *s ? *s : c0;
219}
220
221static void repaintmenu(WINDOW *wmenu, menu *mp)
222{
223 int i;
224 menu *p = mp;
225
226 for (i = 0; p->func; i++, p++)
227 mvwaddstr(wmenu, i + 1, 2, p->name);
228
229 touchwin(wmenu);
230 wrefresh(wmenu);
231}
232
233static void repaintmainmenu(int width, menu *mp)
234{
235 int i;
236 menu *p = mp;
237
238 for (i = 0; p->func; i++, p++)
239 mvwaddstr(wmain, 0, i * width, prepad(padstr(p->name, width - 1), 1));
240
241 touchwin(wmain);
242 wrefresh(wmain);
243}
244
245static void mainhelp(void)
246{
247#ifdef ALT_X
248 statusmsg("Use arrow keys and Enter to select (Alt-X to quit)");
249#else
250 statusmsg("Use arrow keys and Enter to select");
251#endif
252}
253
254static void mainmenu(menu *mp)
255{
256 int nitems, barlen, old = -1, cur = 0, c, cur0;
257
258 menudim(mp, &nitems, &barlen);
259 repaintmainmenu(barlen, mp);
260
261 while (!quit)
262 {
263 if (cur != old)
264 {
265 if (old != -1)
266 {
Stefan Reinauere11835e2011-10-31 12:54:00 -0700267 mvwaddstr(wmain, 0, old * barlen,
Patrick Georgi3b77b722011-07-07 15:41:53 +0200268 prepad(padstr(mp[old].name, barlen - 1), 1));
269
270 statusmsg(mp[cur].desc);
271 }
272 else
273 mainhelp();
274
275 setcolor(wmain, MAINMENUREVCOLOR);
276
Stefan Reinauere11835e2011-10-31 12:54:00 -0700277 mvwaddstr(wmain, 0, cur * barlen,
Patrick Georgi3b77b722011-07-07 15:41:53 +0200278 prepad(padstr(mp[cur].name, barlen - 1), 1));
279
280 setcolor(wmain, MAINMENUCOLOR);
281 old = cur;
282 wrefresh(wmain);
283 }
284
285 switch (c = (key != ERR ? key : waitforkey()))
286 {
287 case KEY_DOWN:
288 case '\n': /* menu item selected */
289 touchwin(wbody);
290 wrefresh(wbody);
291 rmerror();
292 setmenupos(th + mh, cur * barlen);
293 curs_set(1);
294 (mp[cur].func)(); /* perform function */
295 curs_set(0);
296
297 switch (key)
298 {
299 case KEY_LEFT:
300 cur = (cur + nitems - 1) % nitems;
301 key = '\n';
302 break;
303
304 case KEY_RIGHT:
305 cur = (cur + 1) % nitems;
306 key = '\n';
307 break;
308
309 default:
310 key = ERR;
311 }
312
313 repaintmainmenu(barlen, mp);
314 old = -1;
315 break;
316
317 case KEY_LEFT:
318 cur = (cur + nitems - 1) % nitems;
319 break;
320
321 case KEY_RIGHT:
322 cur = (cur + 1) % nitems;
323 break;
324
325 case KEY_ESC:
326 mainhelp();
327 break;
328
329 default:
330 cur0 = cur;
331
332 do
333 {
334 cur = (cur + 1) % nitems;
335
336 } while ((cur != cur0) && (hotkey(mp[cur].name) != toupper(c)));
337
338 if (hotkey(mp[cur].name) == toupper(c))
339 key = '\n';
340 }
341
342 }
343
344 rmerror();
345 touchwin(wbody);
346 wrefresh(wbody);
347}
348
349static void cleanup(void) /* cleanup curses settings */
350{
351 if (incurses)
352 {
353 delwin(wtitl);
354 delwin(wmain);
355 delwin(wbody);
356 delwin(wstat);
357 curs_set(1);
358 endwin();
359 incurses = FALSE;
360 }
361}
362
363
364/******************************* EXTERNAL **********************************/
365
366void clsbody(void)
367{
368 werase(wbody);
369 wmove(wbody, 0, 0);
370}
371
372int bodylen(void)
373{
374#ifdef PDCURSES
375 return getmaxy(wbody);
376#else
377 int maxy, maxx;
378
379 getmaxyx(wbody, maxy, maxx);
380 return maxy;
381#endif
382}
383
384WINDOW *bodywin(void)
385{
386 return wbody;
387}
388
389void rmerror(void)
390{
391 rmline(wstat, 0);
392}
393
394void rmstatus(void)
395{
396 rmline(wstat, 1);
397}
398
399void titlemsg(char *msg)
400{
401 mvwaddstr(wtitl, 0, 2, padstr(msg, bw - 3));
402 wrefresh(wtitl);
403}
404
405void bodymsg(char *msg)
406{
407 waddstr(wbody, msg);
408 wrefresh(wbody);
409}
410
411void errormsg(char *msg)
412{
413 beep();
414 mvwaddstr(wstat, 0, 2, padstr(msg, bw - 3));
415 wrefresh(wstat);
416}
417
418void statusmsg(char *msg)
419{
420 mvwaddstr(wstat, 1, 2, padstr(msg, bw - 3));
421 wrefresh(wstat);
422}
423
424bool keypressed(void)
425{
426 ch = wgetch(wbody);
427
428 return ch != ERR;
429}
430
431int getkey(void)
432{
433 int c = ch;
434
435 ch = ERR;
436#ifdef ALT_X
437 quit = (c == ALT_X); /* PC only ! */
438#endif
439 return c;
440}
441
442int waitforkey(void)
443{
444 do idle(); while (!keypressed());
445 return getkey();
446}
447
448void DoExit(void) /* terminate program */
449{
450 quit = TRUE;
451}
452
453void domenu(menu *mp)
454{
455 int y, x, nitems, barlen, mheight, mw, old = -1, cur = 0, cur0;
456 bool stop = FALSE;
457 WINDOW *wmenu;
458
459 curs_set(0);
460 getmenupos(&y, &x);
461 menudim(mp, &nitems, &barlen);
462 mheight = nitems + 2;
463 mw = barlen + 2;
464 wmenu = newwin(mheight, mw, y, x);
465 colorbox(wmenu, SUBMENUCOLOR, 1);
466 repaintmenu(wmenu, mp);
467
468 key = ERR;
469
470 while (!stop && !quit)
471 {
472 if (cur != old)
473 {
474 if (old != -1)
Stefan Reinauere11835e2011-10-31 12:54:00 -0700475 mvwaddstr(wmenu, old + 1, 1,
Patrick Georgi3b77b722011-07-07 15:41:53 +0200476 prepad(padstr(mp[old].name, barlen - 1), 1));
477
478 setcolor(wmenu, SUBMENUREVCOLOR);
479 mvwaddstr(wmenu, cur + 1, 1,
480 prepad(padstr(mp[cur].name, barlen - 1), 1));
481
482 setcolor(wmenu, SUBMENUCOLOR);
483 statusmsg(mp[cur].desc);
484
485 old = cur;
486 wrefresh(wmenu);
487 }
488
489 switch (key = ((key != ERR) ? key : waitforkey()))
490 {
491 case '\n': /* menu item selected */
492 touchwin(wbody);
493 wrefresh(wbody);
494 setmenupos(y + 1, x + 1);
495 rmerror();
496
497 key = ERR;
498 curs_set(1);
499 (mp[cur].func)(); /* perform function */
500 curs_set(0);
501
502 repaintmenu(wmenu, mp);
503
504 old = -1;
505 break;
506
507 case KEY_UP:
508 cur = (cur + nitems - 1) % nitems;
509 key = ERR;
510 break;
511
512 case KEY_DOWN:
513 cur = (cur + 1) % nitems;
514 key = ERR;
515 break;
516
517 case KEY_ESC:
518 case KEY_LEFT:
519 case KEY_RIGHT:
520 if (key == KEY_ESC)
521 key = ERR; /* return to prev submenu */
522
523 stop = TRUE;
524 break;
525
526 default:
527 cur0 = cur;
528
529 do
530 {
531 cur = (cur + 1) % nitems;
532
533 } while ((cur != cur0) &&
534 (hotkey(mp[cur].name) != toupper((int)key)));
535
536 key = (hotkey(mp[cur].name) == toupper((int)key)) ? '\n' : ERR;
537 }
538
539 }
540
541 rmerror();
542 delwin(wmenu);
543 touchwin(wbody);
544 wrefresh(wbody);
545}
546
547void startmenu(menu *mp, char *mtitle)
548{
549 initscr();
550 incurses = TRUE;
551 initcolor();
552
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);
557
558 colorbox(wtitl, TITLECOLOR, 0);
559 colorbox(wmain, MAINMENUCOLOR, 0);
560 colorbox(wbody, BODYCOLOR, 0);
561 colorbox(wstat, STATUSCOLOR, 0);
562
563 if (mtitle)
564 titlemsg(mtitle);
565
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 */
573
574 leaveok(stdscr, TRUE);
575 leaveok(wtitl, TRUE);
576 leaveok(wmain, TRUE);
577 leaveok(wstat, TRUE);
578
579 mainmenu(mp);
580
581 cleanup();
582}
583
584static void repainteditbox(WINDOW *win, int x, char *buf)
585{
586#ifndef PDCURSES
587 int maxy;
588#endif
589 int maxx;
590
591#ifdef PDCURSES
592 maxx = getmaxx(win);
593#else
594 getmaxyx(win, maxy, maxx);
595#endif
596 werase(win);
597 mvwprintw(win, 0, 0, "%s", padstr(buf, maxx));
598 wmove(win, 0, x);
Stefan Reinauere11835e2011-10-31 12:54:00 -0700599 wrefresh(win);
Patrick Georgi3b77b722011-07-07 15:41:53 +0200600}
601
602/*
603
604 weditstr() - edit string
605
606 Description:
607 The initial value of 'str' with a maximum length of 'field' - 1,
Stefan Reinauere11835e2011-10-31 12:54:00 -0700608 which is supplied by the calling routine, is editted. The user's
609 erase (^H), kill (^U) and delete word (^W) chars are interpreted.
Patrick Georgi3b77b722011-07-07 15:41:53 +0200610 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.
614
615 Return Value:
Stefan Reinauere11835e2011-10-31 12:54:00 -0700616 Returns the input terminating character on success (Escape,
Patrick Georgi3b77b722011-07-07 15:41:53 +0200617 Enter, Up or Down Arrow) and ERR on error.
618
619 Errors:
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.
622
623*/
624
625int weditstr(WINDOW *win, char *buf, int field)
626{
627 char org[MAXSTRLEN], *tp, *bp = buf;
628 bool defdisp = TRUE, stop = FALSE, insert = FALSE;
629 int cury, curx, begy, begx, oldattr;
630 WINDOW *wedit;
631 int c = 0;
632
633 if ((field >= MAXSTRLEN) || (buf == NULL) ||
634 ((int)strlen(buf) > field - 1))
635 return ERR;
636
637 strcpy(org, buf); /* save original */
638
639 wrefresh(win);
640 getyx(win, cury, curx);
641 getbegyx(win, begy, begx);
642
643 wedit = subwin(win, 1, field, begy + cury, begx + curx);
644 oldattr = wedit->_attrs;
645 colorbox(wedit, EDITBOXCOLOR, 0);
646
647 keypad(wedit, TRUE);
648 curs_set(1);
649
650 while (!stop)
651 {
652 idle();
653 repainteditbox(wedit, bp - buf, buf);
654
655 switch (c = wgetch(wedit))
656 {
657 case ERR:
658 break;
659
660 case KEY_ESC:
661 strcpy(buf, org); /* restore original */
662 stop = TRUE;
663 break;
664
665 case '\n':
666 case KEY_UP:
667 case KEY_DOWN:
668 stop = TRUE;
669 break;
670
671 case KEY_LEFT:
672 if (bp > buf)
673 bp--;
674 break;
675
676 case KEY_RIGHT:
677 defdisp = FALSE;
678 if (bp - buf < (int)strlen(buf))
679 bp++;
680 break;
681
682 case '\t': /* TAB -- because insert
683 is broken on HPUX */
684 case KEY_IC: /* enter insert mode */
685 case KEY_EIC: /* exit insert mode */
686 defdisp = FALSE;
687 insert = !insert;
688
689 curs_set(insert ? 2 : 1);
690 break;
691
692 default:
693 if (c == erasechar()) /* backspace, ^H */
694 {
695 if (bp > buf)
696 {
697 memmove((void *)(bp - 1), (const void *)bp, strlen(bp) + 1);
698 bp--;
699 }
700 }
701 else if (c == killchar()) /* ^U */
702 {
703 bp = buf;
704 *bp = '\0';
705 }
706 else if (c == wordchar()) /* ^W */
707 {
708 tp = bp;
709
Stefan Reinauere11835e2011-10-31 12:54:00 -0700710 while ((bp > buf) && (*(bp - 1) == ' '))
Patrick Georgi3b77b722011-07-07 15:41:53 +0200711 bp--;
Stefan Reinauere11835e2011-10-31 12:54:00 -0700712 while ((bp > buf) && (*(bp - 1) != ' '))
Patrick Georgi3b77b722011-07-07 15:41:53 +0200713 bp--;
714
715 memmove((void *)bp, (const void *)tp, strlen(tp) + 1);
716 }
717 else if (isprint(c))
718 {
719 if (defdisp)
720 {
721 bp = buf;
722 *bp = '\0';
723 defdisp = FALSE;
724 }
725
726 if (insert)
727 {
728 if ((int)strlen(buf) < field - 1)
729 {
730 memmove((void *)(bp + 1), (const void *)bp,
731 strlen(bp) + 1);
732
733 *bp++ = c;
734 }
735 }
736 else if (bp - buf < field - 1)
737 {
738 /* append new string terminator */
739
740 if (!*bp)
741 bp[1] = '\0';
Stefan Reinauere11835e2011-10-31 12:54:00 -0700742
Patrick Georgi3b77b722011-07-07 15:41:53 +0200743 *bp++ = c;
744 }
745 }
746 }
747 }
748
749 curs_set(0);
750
751 wattrset(wedit, oldattr);
752 repainteditbox(wedit, bp - buf, buf);
753 delwin(wedit);
754
755 return c;
756}
757
758WINDOW *winputbox(WINDOW *win, int nlines, int ncols)
759{
760 WINDOW *winp;
761 int cury, curx, begy, begx;
762
763 getyx(win, cury, curx);
764 getbegyx(win, begy, begx);
765
766 winp = newwin(nlines, ncols, begy + cury, begx + curx);
767 colorbox(winp, INPUTBOXCOLOR, 1);
768
769 return winp;
770}
771
772int getstrings(char *desc[], char *buf[], int field)
773{
774 WINDOW *winput;
775 int oldy, oldx, maxy, maxx, nlines, ncols, i, n, l, mmax = 0;
776 int c = 0;
777 bool stop = FALSE;
778
779 for (n = 0; desc[n]; n++)
780 if ((l = strlen(desc[n])) > mmax)
781 mmax = l;
782
783 nlines = n + 2; ncols = mmax + field + 4;
784 getyx(wbody, oldy, oldx);
785 getmaxyx(wbody, maxy, maxx);
786
Stefan Reinauere11835e2011-10-31 12:54:00 -0700787 winput = mvwinputbox(wbody, (maxy - nlines) / 2, (maxx - ncols) / 2,
Patrick Georgi3b77b722011-07-07 15:41:53 +0200788 nlines, ncols);
789
790 for (i = 0; i < n; i++)
791 mvwprintw(winput, i + 1, 2, "%s", desc[i]);
792
793 i = 0;
794
795 while (!stop)
796 {
797 switch (c = mvweditstr(winput, i+1, mmax+3, buf[i], field))
798 {
799 case KEY_ESC:
800 stop = TRUE;
801 break;
802
803 case KEY_UP:
804 i = (i + n - 1) % n;
805 break;
806
807 case '\n':
808 case '\t':
809 case KEY_DOWN:
810 if (++i == n)
811 stop = TRUE; /* all passed? */
812 }
813 }
814
815 delwin(winput);
816 touchwin(wbody);
817 wmove(wbody, oldy, oldx);
818 wrefresh(wbody);
819
820 return c;
821}