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)
49 #define th 1 /* title window height */
50 #define mh 1 /* main menu height */
51 #define sh 2 /* status window height */
52 #define bh (LINES - th - mh - sh) /* body window height */
53 #define bw COLS /* body window width */
55 /******************************* STATIC ************************************/
57 static WINDOW
*wtitl
, *wmain
, *wbody
, *wstat
; /* title, menu, body, status win*/
58 static int nexty
, nextx
;
59 static int key
= ERR
, ch
= ERR
;
60 static bool quit
= FALSE
;
61 static bool incurses
= FALSE
;
64 static char wordchar(void)
70 static char *padstr(char *s
, int length
)
72 static char buf
[MAXSTRLEN
];
75 sprintf(fmt
, (int)strlen(s
) > length
? "%%.%ds" : "%%-%ds", length
);
81 static char *prepad(char *s
, int length
)
88 memmove((void *)(s
+ length
), (const void *)s
, strlen(s
) + 1);
90 for (i
= 0; i
< length
; i
++)
97 static void rmline(WINDOW
*win
, int nr
) /* keeps box lines intact */
99 mvwaddstr(win
, nr
, 1, padstr(" ", bw
- 2));
103 static void initcolor(void)
109 /* foreground, background */
111 init_pair(TITLECOLOR
& ~A_ATTR
, COLOR_BLACK
, COLOR_CYAN
);
112 init_pair(MAINMENUCOLOR
& ~A_ATTR
, COLOR_WHITE
, COLOR_CYAN
);
113 init_pair(MAINMENUREVCOLOR
& ~A_ATTR
, COLOR_WHITE
, COLOR_BLACK
);
114 init_pair(SUBMENUCOLOR
& ~A_ATTR
, COLOR_WHITE
, COLOR_CYAN
);
115 init_pair(SUBMENUREVCOLOR
& ~A_ATTR
, COLOR_WHITE
, COLOR_BLACK
);
116 init_pair(BODYCOLOR
& ~A_ATTR
, COLOR_WHITE
, COLOR_BLUE
);
117 init_pair(STATUSCOLOR
& ~A_ATTR
, COLOR_WHITE
, COLOR_CYAN
);
118 init_pair(INPUTBOXCOLOR
& ~A_ATTR
, COLOR_BLACK
, COLOR_CYAN
);
119 init_pair(EDITBOXCOLOR
& ~A_ATTR
, COLOR_WHITE
, COLOR_BLACK
);
123 static void setcolor(WINDOW
*win
, chtype color
)
125 chtype attr
= color
& A_ATTR
; /* extract Bold, Reverse, Blink bits */
128 attr
&= ~A_REVERSE
; /* ignore reverse, use colors instead! */
129 wattrset(win
, COLOR_PAIR(color
& A_CHARTEXT
) | attr
);
131 attr
&= ~A_BOLD
; /* ignore bold, gives messy display on HP-UX */
136 static void colorbox(WINDOW
*win
, chtype color
, int hasbox
)
142 chtype attr
= color
& A_ATTR
; /* extract Bold, Reverse, Blink bits */
144 setcolor(win
, color
);
148 wbkgd(win
, COLOR_PAIR(color
& A_CHARTEXT
) | (attr
& ~A_REVERSE
));
158 getmaxyx(win
, maxy
, maxx
);
160 if (hasbox
&& (maxy
> 2))
167 static void idle(void)
174 return; /* time not available */
177 sprintf(buf
, " %.2d-%.2d-%.4d %.2d:%.2d:%.2d",
178 tp
->tm_mday
, tp
->tm_mon
+ 1, tp
->tm_year
+ 1900,
179 tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
);
181 mvwaddstr(wtitl
, 0, bw
- strlen(buf
) - 2, buf
);
185 static void menudim(menu
*mp
, int *lines
, int *columns
)
189 for (n
=0; mp
->func
; n
++, mp
++)
190 if ((l
= strlen(mp
->name
)) > mmax
) mmax
= l
;
196 static void setmenupos(int y
, int x
)
202 static void getmenupos(int *y
, int *x
)
208 static int hotkey(const char *s
)
210 int c0
= *s
; /* if no upper case found, return first char */
213 if (isupper((unsigned char)*s
))
219 static void repaintmenu(WINDOW
*wmenu
, menu
*mp
)
224 for (i
= 0; p
->func
; i
++, p
++)
225 mvwaddstr(wmenu
, i
+ 1, 2, p
->name
);
231 static void repaintmainmenu(int width
, menu
*mp
)
236 for (i
= 0; p
->func
; i
++, p
++)
237 mvwaddstr(wmain
, 0, i
* width
, prepad(padstr(p
->name
, width
- 1), 1));
243 static void mainhelp(void)
246 statusmsg("Use arrow keys and Enter to select (Alt-X to quit)");
248 statusmsg("Use arrow keys and Enter to select");
252 static void mainmenu(menu
*mp
)
254 int nitems
, barlen
, old
= -1, cur
= 0, c
, cur0
;
256 menudim(mp
, &nitems
, &barlen
);
257 repaintmainmenu(barlen
, mp
);
265 mvwaddstr(wmain
, 0, old
* barlen
,
266 prepad(padstr(mp
[old
].name
, barlen
- 1), 1));
268 statusmsg(mp
[cur
].desc
);
273 setcolor(wmain
, MAINMENUREVCOLOR
);
275 mvwaddstr(wmain
, 0, cur
* barlen
,
276 prepad(padstr(mp
[cur
].name
, barlen
- 1), 1));
278 setcolor(wmain
, MAINMENUCOLOR
);
283 switch (c
= (key
!= ERR
? key
: waitforkey()))
286 case '\n': /* menu item selected */
290 setmenupos(th
+ mh
, cur
* barlen
);
292 (mp
[cur
].func
)(); /* perform function */
298 cur
= (cur
+ nitems
- 1) % nitems
;
303 cur
= (cur
+ 1) % nitems
;
311 repaintmainmenu(barlen
, mp
);
316 cur
= (cur
+ nitems
- 1) % nitems
;
320 cur
= (cur
+ 1) % nitems
;
332 cur
= (cur
+ 1) % nitems
;
334 } while ((cur
!= cur0
) && (hotkey(mp
[cur
].name
) != toupper(c
)));
336 if (hotkey(mp
[cur
].name
) == toupper(c
))
347 static void cleanup(void) /* cleanup curses settings */
361 /******************************* EXTERNAL **********************************/
372 return getmaxy(wbody
);
376 getmaxyx(wbody
, maxy
, maxx
);
381 WINDOW
*bodywin(void)
396 void titlemsg(char *msg
)
398 mvwaddstr(wtitl
, 0, 2, padstr(msg
, bw
- 3));
402 void bodymsg(char *msg
)
408 void errormsg(char *msg
)
411 mvwaddstr(wstat
, 0, 2, padstr(msg
, bw
- 3));
415 void statusmsg(char *msg
)
417 mvwaddstr(wstat
, 1, 2, padstr(msg
, bw
- 3));
421 bool keypressed(void)
434 quit
= (c
== ALT_X
); /* PC only ! */
441 do idle(); while (!keypressed());
445 void DoExit(void) /* terminate program */
450 void domenu(menu
*mp
)
452 int y
, x
, nitems
, barlen
, mheight
, mw
, old
= -1, cur
= 0, cur0
;
458 menudim(mp
, &nitems
, &barlen
);
459 mheight
= nitems
+ 2;
461 wmenu
= newwin(mheight
, mw
, y
, x
);
462 colorbox(wmenu
, SUBMENUCOLOR
, 1);
463 repaintmenu(wmenu
, mp
);
467 while (!stop
&& !quit
)
472 mvwaddstr(wmenu
, old
+ 1, 1,
473 prepad(padstr(mp
[old
].name
, barlen
- 1), 1));
475 setcolor(wmenu
, SUBMENUREVCOLOR
);
476 mvwaddstr(wmenu
, cur
+ 1, 1,
477 prepad(padstr(mp
[cur
].name
, barlen
- 1), 1));
479 setcolor(wmenu
, SUBMENUCOLOR
);
480 statusmsg(mp
[cur
].desc
);
486 switch (key
= ((key
!= ERR
) ? key
: waitforkey()))
488 case '\n': /* menu item selected */
491 setmenupos(y
+ 1, x
+ 1);
496 (mp
[cur
].func
)(); /* perform function */
499 repaintmenu(wmenu
, mp
);
505 cur
= (cur
+ nitems
- 1) % nitems
;
510 cur
= (cur
+ 1) % nitems
;
518 key
= ERR
; /* return to prev submenu */
528 cur
= (cur
+ 1) % nitems
;
530 } while ((cur
!= cur0
) &&
531 (hotkey(mp
[cur
].name
) != toupper((int)key
)));
533 key
= (hotkey(mp
[cur
].name
) == toupper((int)key
)) ? '\n' : ERR
;
544 void startmenu(menu
*mp
, char *mtitle
)
550 wtitl
= subwin(stdscr
, th
, bw
, 0, 0);
551 wmain
= subwin(stdscr
, mh
, bw
, th
, 0);
552 wbody
= subwin(stdscr
, bh
, bw
, th
+ mh
, 0);
553 wstat
= subwin(stdscr
, sh
, bw
, th
+ mh
+ bh
, 0);
555 colorbox(wtitl
, TITLECOLOR
, 0);
556 colorbox(wmain
, MAINMENUCOLOR
, 0);
557 colorbox(wbody
, BODYCOLOR
, 0);
558 colorbox(wstat
, STATUSCOLOR
, 0);
563 cbreak(); /* direct input (no newline required)... */
564 noecho(); /* ... without echoing */
565 curs_set(0); /* hide cursor (if possible) */
566 nodelay(wbody
, TRUE
); /* don't wait for input... */
567 halfdelay(10); /* ...well, no more than a second, anyway */
568 keypad(wbody
, TRUE
); /* enable cursor keys */
569 scrollok(wbody
, TRUE
); /* enable scrolling in main window */
571 leaveok(stdscr
, TRUE
);
572 leaveok(wtitl
, TRUE
);
573 leaveok(wmain
, TRUE
);
574 leaveok(wstat
, TRUE
);
581 static void repainteditbox(WINDOW
*win
, int x
, char *buf
)
591 getmaxyx(win
, maxy
, maxx
);
594 mvwprintw(win
, 0, 0, "%s", padstr(buf
, maxx
));
601 weditstr() - edit string
604 The initial value of 'str' with a maximum length of 'field' - 1,
605 which is supplied by the calling routine, is editted. The user's
606 erase (^H), kill (^U) and delete word (^W) chars are interpreted.
607 The PC insert or Tab keys toggle between insert and edit mode.
608 Escape aborts the edit session, leaving 'str' unchanged.
609 Enter, Up or Down Arrow are used to accept the changes to 'str'.
610 NOTE: editstr(), mveditstr(), and mvweditstr() are macros.
613 Returns the input terminating character on success (Escape,
614 Enter, Up or Down Arrow) and ERR on error.
617 It is an error to call this function with a NULL window pointer.
618 The length of the initial 'str' must not exceed 'field' - 1.
622 int weditstr(WINDOW
*win
, char *buf
, int field
)
624 char org
[MAXSTRLEN
], *tp
, *bp
= buf
;
625 bool defdisp
= TRUE
, stop
= FALSE
, insert
= FALSE
;
626 int cury
, curx
, begy
, begx
, oldattr
;
630 if ((field
>= MAXSTRLEN
) || (buf
== NULL
) ||
631 ((int)strlen(buf
) > field
- 1))
634 strcpy(org
, buf
); /* save original */
637 getyx(win
, cury
, curx
);
638 getbegyx(win
, begy
, begx
);
640 wedit
= subwin(win
, 1, field
, begy
+ cury
, begx
+ curx
);
641 oldattr
= wedit
->_attrs
;
642 colorbox(wedit
, EDITBOXCOLOR
, 0);
650 repainteditbox(wedit
, bp
- buf
, buf
);
652 switch (c
= wgetch(wedit
))
658 strcpy(buf
, org
); /* restore original */
675 if (bp
- buf
< (int)strlen(buf
))
679 case '\t': /* TAB -- because insert
681 case KEY_IC
: /* enter insert mode */
682 case KEY_EIC
: /* exit insert mode */
686 curs_set(insert
? 2 : 1);
690 if (c
== erasechar()) /* backspace, ^H */
694 memmove((void *)(bp
- 1), (const void *)bp
, strlen(bp
) + 1);
698 else if (c
== killchar()) /* ^U */
703 else if (c
== wordchar()) /* ^W */
707 while ((bp
> buf
) && (*(bp
- 1) == ' '))
709 while ((bp
> buf
) && (*(bp
- 1) != ' '))
712 memmove((void *)bp
, (const void *)tp
, strlen(tp
) + 1);
725 if ((int)strlen(buf
) < field
- 1)
727 memmove((void *)(bp
+ 1), (const void *)bp
,
733 else if (bp
- buf
< field
- 1)
735 /* append new string terminator */
748 wattrset(wedit
, oldattr
);
749 repainteditbox(wedit
, bp
- buf
, buf
);
755 WINDOW
*winputbox(WINDOW
*win
, int nlines
, int ncols
)
758 int cury
, curx
, begy
, begx
;
760 getyx(win
, cury
, curx
);
761 getbegyx(win
, begy
, begx
);
763 winp
= newwin(nlines
, ncols
, begy
+ cury
, begx
+ curx
);
764 colorbox(winp
, INPUTBOXCOLOR
, 1);
769 int getstrings(char *desc
[], char *buf
[], int field
)
772 int oldy
, oldx
, maxy
, maxx
, nlines
, ncols
, i
, n
, l
, mmax
= 0;
776 for (n
= 0; desc
[n
]; n
++)
777 if ((l
= strlen(desc
[n
])) > mmax
)
780 nlines
= n
+ 2; ncols
= mmax
+ field
+ 4;
781 getyx(wbody
, oldy
, oldx
);
782 getmaxyx(wbody
, maxy
, maxx
);
784 winput
= mvwinputbox(wbody
, (maxy
- nlines
) / 2, (maxx
- ncols
) / 2,
787 for (i
= 0; i
< n
; i
++)
788 mvwprintw(winput
, i
+ 1, 2, "%s", desc
[i
]);
794 switch (c
= mvweditstr(winput
, i
+1, mmax
+3, buf
[i
], field
))
808 stop
= TRUE
; /* all passed? */
814 wmove(wbody
, oldy
, oldx
);