e57a1b0968229ae498d127da29e378f027ef4a1e
[coreboot.git] / payloads / libpayload / curses / PDCurses-3.4 / demos / tui.c
1 /********************************* 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
19 void statusmsg(char *);
20 int waitforkey(void);
21 void 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
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;
64
65 #ifndef PDCURSES
66 static char wordchar(void)
67 {
68     return 0x17;    /* ^W */ 
69 }
70 #endif
71
72 static 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
83 static 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
99 static void rmline(WINDOW *win, int nr)   /* keeps box lines intact */
100 {
101     mvwaddstr(win, nr, 1, padstr(" ", bw - 2));
102     wrefresh(win);
103 }
104
105 static void initcolor(void)
106 {
107 #ifdef A_COLOR
108     if (has_colors())
109         start_color();
110
111     /* foreground, background */
112
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);
122 #endif
123 }
124
125 static 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
138 static 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
155     werase(win); 
156
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
169 static 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);
184     wrefresh(wtitl); 
185 }
186
187 static 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
198 static void setmenupos(int y, int x)
199 {
200     nexty = y;
201     nextx = x;
202 }
203
204 static void getmenupos(int *y, int *x)
205 {
206     *y = nexty;
207     *x = nextx;
208 }
209
210 static 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
221 static 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
233 static 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
245 static 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
254 static 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             {
267                 mvwaddstr(wmain, 0, old * barlen, 
268                           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
277             mvwaddstr(wmain, 0, cur * barlen, 
278                       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
349 static 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
366 void clsbody(void)
367 {
368     werase(wbody);
369     wmove(wbody, 0, 0);
370 }
371
372 int 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
384 WINDOW *bodywin(void)
385 {
386     return wbody;
387 }
388
389 void rmerror(void)
390 {
391     rmline(wstat, 0);
392 }
393
394 void rmstatus(void)
395 {
396     rmline(wstat, 1);
397 }
398
399 void titlemsg(char *msg)
400 {
401     mvwaddstr(wtitl, 0, 2, padstr(msg, bw - 3));
402     wrefresh(wtitl);
403 }
404
405 void bodymsg(char *msg)
406 {
407     waddstr(wbody, msg);
408     wrefresh(wbody);
409 }
410
411 void errormsg(char *msg)
412 {
413     beep();
414     mvwaddstr(wstat, 0, 2, padstr(msg, bw - 3));
415     wrefresh(wstat);
416 }
417
418 void statusmsg(char *msg)
419 {
420     mvwaddstr(wstat, 1, 2, padstr(msg, bw - 3));
421     wrefresh(wstat);
422 }
423
424 bool keypressed(void)
425 {
426     ch = wgetch(wbody);
427
428     return ch != ERR;
429 }
430
431 int 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
442 int waitforkey(void)
443 {
444     do idle(); while (!keypressed());
445     return getkey();
446 }
447
448 void DoExit(void)   /* terminate program */
449 {
450     quit = TRUE;
451 }
452
453 void 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)
475                 mvwaddstr(wmenu, old + 1, 1, 
476                           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
547 void 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
584 static 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);
599     wrefresh(win); 
600 }
601
602 /*
603
604   weditstr()     - edit string
605
606   Description:
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.
614
615   Return Value:
616     Returns the input terminating character on success (Escape, 
617     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
625 int 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
710                 while ((bp > buf) && (*(bp - 1) == ' ')) 
711                     bp--;
712                 while ((bp > buf) && (*(bp - 1) != ' ')) 
713                     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';
742             
743                     *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
758 WINDOW *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
772 int 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
787     winput = mvwinputbox(wbody, (maxy - nlines) / 2, (maxx - ncols) / 2, 
788         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 }