1 /********************************* tui.c ************************************/
3 * 'textual user interface'
5 * Author : P.J. Kunst <kunst@prl.philips.nl>
17 void statusmsg(char *);
21 #if defined(__unix) && !defined(__DJGPP__)
26 # define TITLECOLOR 1 /* color pair indices */
27 # define MAINMENUCOLOR (2 | A_BOLD)
28 # define MAINMENUREVCOLOR (3 | A_BOLD | A_REVERSE)
29 # define SUBMENUCOLOR (4 | A_BOLD)
30 # define SUBMENUREVCOLOR (5 | A_BOLD | A_REVERSE)
32 # define STATUSCOLOR (7 | A_BOLD)
33 # define INPUTBOXCOLOR 8
34 # define EDITBOXCOLOR (9 | A_BOLD | A_REVERSE)
36 # define TITLECOLOR 0 /* color pair indices */
37 # define MAINMENUCOLOR (A_BOLD)
38 # define MAINMENUREVCOLOR (A_BOLD | A_REVERSE)
39 # define SUBMENUCOLOR (A_BOLD)
40 # define SUBMENUREVCOLOR (A_BOLD | A_REVERSE)
42 # define STATUSCOLOR (A_BOLD)
43 # define INPUTBOXCOLOR 0
44 # define EDITBOXCOLOR (A_BOLD | A_REVERSE)
48 #define th 1 /* title window height */
49 #define mh 1 /* main menu height */
50 #define sh 2 /* status window height */
51 #define bh (LINES - th - mh - sh) /* body window height */
52 #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_BLUE
);
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
)
140 int __attribute__((unused
)) maxx
;
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
, " %.4d-%.2d-%.2d %.2d:%.2d:%.2d",
178 tp
->tm_year
+ 1900, tp
->tm_mon
+ 1, tp
->tm_mday
,
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));
246 statusmsg("Use arrow keys and Enter to select (Alt-X to quit)");
248 statusmsg("Use arrow keys and Enter to select");
252 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 void cleanup(void) /* cleanup curses settings */
362 /******************************* EXTERNAL **********************************/
373 return getmaxy(wbody
);
375 int maxy
, __attribute__((unused
)) maxx
;
377 getmaxyx(wbody
, maxy
, maxx
);
382 WINDOW
*bodywin(void)
397 void titlemsg(char *msg
)
399 mvwaddstr(wtitl
, 0, 2, padstr(msg
, bw
- 3));
403 void bodymsg(char *msg
)
409 void errormsg(char *msg
)
412 mvwaddstr(wstat
, 0, 2, padstr(msg
, bw
- 3));
416 void statusmsg(char *msg
)
418 mvwaddstr(wstat
, 1, 2, padstr(msg
, bw
- 3));
422 bool keypressed(void)
435 quit
= (c
== ALT_X
); /* PC only ! */
442 do idle(); while (!keypressed());
446 void DoExit(void) /* terminate program */
451 void domenu(menu
*mp
)
453 int y
, x
, nitems
, barlen
, mheight
, mw
, old
= -1, cur
= 0, cur0
;
459 menudim(mp
, &nitems
, &barlen
);
460 mheight
= nitems
+ 2;
462 wmenu
= newwin(mheight
, mw
, y
, x
);
463 colorbox(wmenu
, SUBMENUCOLOR
, 1);
464 repaintmenu(wmenu
, mp
);
468 while (!stop
&& !quit
)
473 mvwaddstr(wmenu
, old
+ 1, 1,
474 prepad(padstr(mp
[old
].name
, barlen
- 1), 1));
476 setcolor(wmenu
, SUBMENUREVCOLOR
);
477 mvwaddstr(wmenu
, cur
+ 1, 1,
478 prepad(padstr(mp
[cur
].name
, barlen
- 1), 1));
480 setcolor(wmenu
, SUBMENUCOLOR
);
481 statusmsg(mp
[cur
].desc
);
487 switch (key
= ((key
!= ERR
) ? key
: waitforkey()))
489 case '\n': /* menu item selected */
492 setmenupos(y
+ 1, x
+ 1);
497 (mp
[cur
].func
)(); /* perform function */
500 repaintmenu(wmenu
, mp
);
506 cur
= (cur
+ nitems
- 1) % nitems
;
511 cur
= (cur
+ 1) % nitems
;
519 key
= ERR
; /* return to prev submenu */
529 cur
= (cur
+ 1) % nitems
;
531 } while ((cur
!= cur0
) &&
532 (hotkey(mp
[cur
].name
) != toupper((int)key
)));
534 key
= (hotkey(mp
[cur
].name
) == toupper((int)key
)) ? '\n' : ERR
;
545 void startmenu(menu
*mp
, char *mtitle
, FUNC init
)
551 wtitl
= subwin(stdscr
, th
, bw
, 0, 0);
552 wmain
= subwin(stdscr
, mh
, bw
, th
, 0);
553 wbody
= subwin(stdscr
, bh
, bw
, th
+ mh
, 0);
554 wstat
= subwin(stdscr
, sh
, bw
, th
+ mh
+ bh
, 0);
556 colorbox(wtitl
, TITLECOLOR
, 0);
557 colorbox(wmain
, MAINMENUCOLOR
, 0);
558 colorbox(wbody
, BODYCOLOR
, 0);
559 colorbox(wstat
, STATUSCOLOR
, 0);
564 cbreak(); /* direct input (no newline required)... */
565 noecho(); /* ... without echoing */
566 curs_set(0); /* hide cursor (if possible) */
567 nodelay(wbody
, TRUE
); /* don't wait for input... */
568 halfdelay(10); /* ...well, no more than a second, anyway */
569 keypad(wbody
, TRUE
); /* enable cursor keys */
570 scrollok(wbody
, TRUE
); /* enable scrolling in main window */
572 leaveok(stdscr
, TRUE
);
573 leaveok(wtitl
, TRUE
);
574 leaveok(wmain
, TRUE
);
575 leaveok(wstat
, TRUE
);
579 menudim(mp
, &nitems
, &barlen
);
580 repaintmainmenu(barlen
, mp
);
588 static void repainteditbox(WINDOW
*win
, int x
, char *buf
)
591 int __attribute__((unused
)) maxy
;
598 getmaxyx(win
, maxy
, maxx
);
601 mvwprintw(win
, 0, 0, "%s", padstr(buf
, maxx
));
608 weditstr() - edit string
611 The initial value of 'str' with a maximum length of 'field' - 1,
612 which is supplied by the calling routine, is editted. The user's
613 erase (^H), kill (^U) and delete word (^W) chars are interpreted.
614 The PC insert or Tab keys toggle between insert and edit mode.
615 Escape aborts the edit session, leaving 'str' unchanged.
616 Enter, Up or Down Arrow are used to accept the changes to 'str'.
617 NOTE: editstr(), mveditstr(), and mvweditstr() are macros.
620 Returns the input terminating character on success (Escape,
621 Enter, Up or Down Arrow) and ERR on error.
624 It is an error to call this function with a NULL window pointer.
625 The length of the initial 'str' must not exceed 'field' - 1.
629 int weditstr(WINDOW
*win
, char *buf
, int field
)
631 char org
[MAXSTRLEN
], *tp
, *bp
= buf
;
632 bool defdisp
= TRUE
, stop
= FALSE
, insert
= FALSE
;
633 int cury
, curx
, begy
, begx
;
638 if ((field
>= MAXSTRLEN
) || (buf
== NULL
) ||
639 ((int)strlen(buf
) > field
- 1))
642 strcpy(org
, buf
); /* save original */
645 getyx(win
, cury
, curx
);
646 getbegyx(win
, begy
, begx
);
648 wedit
= subwin(win
, 1, field
, begy
+ cury
, begx
+ curx
);
649 oldattr
= getattrs(wedit
);
650 colorbox(wedit
, EDITBOXCOLOR
, 0);
658 repainteditbox(wedit
, bp
- buf
, buf
);
660 switch (c
= wgetch(wedit
))
666 strcpy(buf
, org
); /* restore original */
683 if (bp
- buf
< (int)strlen(buf
))
687 case '\t': /* TAB -- because insert
689 case KEY_IC
: /* enter insert mode */
690 case KEY_EIC
: /* exit insert mode */
694 curs_set(insert
? 2 : 1);
698 if (c
== erasechar()) /* backspace, ^H */
702 memmove((void *)(bp
- 1), (const void *)bp
, strlen(bp
) + 1);
706 else if (c
== killchar()) /* ^U */
711 else if (c
== wordchar()) /* ^W */
715 while ((bp
> buf
) && (*(bp
- 1) == ' '))
717 while ((bp
> buf
) && (*(bp
- 1) != ' '))
720 memmove((void *)bp
, (const void *)tp
, strlen(tp
) + 1);
733 if ((int)strlen(buf
) < field
- 1)
735 memmove((void *)(bp
+ 1), (const void *)bp
,
741 else if (bp
- buf
< field
- 1)
743 /* append new string terminator */
756 wattrset(wedit
, oldattr
);
757 repainteditbox(wedit
, bp
- buf
, buf
);
763 WINDOW
*winputbox(WINDOW
*win
, int nlines
, int ncols
)
766 int cury
, curx
, begy
, begx
;
768 getyx(win
, cury
, curx
);
769 getbegyx(win
, begy
, begx
);
771 winp
= newwin(nlines
, ncols
, begy
+ cury
, begx
+ curx
);
772 colorbox(winp
, INPUTBOXCOLOR
, 1);
777 int getstrings(char *desc
[], char *buf
[], int field
)
780 int oldy
, oldx
, maxy
, maxx
, nlines
, ncols
, i
, n
, l
, mmax
= 0;
784 for (n
= 0; desc
[n
]; n
++)
785 if ((l
= strlen(desc
[n
])) > mmax
)
788 nlines
= n
+ 2; ncols
= mmax
+ field
+ 4;
789 getyx(wbody
, oldy
, oldx
);
790 getmaxyx(wbody
, maxy
, maxx
);
792 winput
= mvwinputbox(wbody
, (maxy
- nlines
) / 2, (maxx
- ncols
) / 2,
795 for (i
= 0; i
< n
; i
++)
796 mvwprintw(winput
, i
+ 1, 2, "%s", desc
[i
]);
802 switch (c
= mvweditstr(winput
, i
+1, mmax
+3, buf
[i
], field
))
816 stop
= TRUE
; /* all passed? */
822 wmove(wbody
, oldy
, oldx
);
828 /* vim: set ts=4 sw=4 et: */