2 * $Id: util.c,v 1.253 2012/12/23 19:52:54 tom Exp $
4 * util.c -- miscellaneous utilities for dialog
6 * Copyright 2000-2011,2012 Thomas E. Dickey
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License, version 2.1
10 * as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to
19 * Free Software Foundation, Inc.
20 * 51 Franklin St., Fifth Floor
21 * Boston, MA 02110, USA.
23 * An earlier version of this program lists as authors
24 * Savio Lam (lam836@cs.cuhk.hk)
38 #ifdef NCURSES_VERSION
39 #if defined(HAVE_NCURSESW_TERM_H)
40 #include <ncursesw/term.h>
41 #elif defined(HAVE_NCURSES_TERM_H)
42 #include <ncurses/term.h>
48 #if defined(HAVE_WCHGAT)
49 # if defined(NCURSES_VERSION_PATCH)
50 # if NCURSES_VERSION_PATCH >= 20060715
63 DIALOG_STATE dialog_state;
64 DIALOG_VARS dialog_vars;
66 #if !(defined(HAVE_WGETPARENT) && defined(HAVE_WINDOW__PARENT))
67 #define NEED_WGETPARENT 1
69 #undef NEED_WGETPARENT
72 #define concat(a,b) a##b
75 #define RC_DATA(name,comment) , #name "_color", comment " color"
77 #define RC_DATA(name,comment) /*nothing */
81 #include <dlg_colors.h>
82 #define COLOR_DATA(upr) , \
83 concat(DLGC_FG_,upr), \
84 concat(DLGC_BG_,upr), \
87 #define COLOR_DATA(upr) /*nothing */
90 #define DATA(atr,upr,lwr,cmt) { atr COLOR_DATA(upr) RC_DATA(lwr,cmt) }
92 #define UseShadow(dw) ((dw) != 0 && (dw)->normal != 0 && (dw)->shadow != 0)
95 * Table of color and attribute values, default is for mono display.
96 * The order matches the DIALOG_ATR() values.
99 DIALOG_COLORS dlg_color_table[] =
101 DATA(A_NORMAL, SCREEN, screen, "Screen"),
102 DATA(A_NORMAL, SHADOW, shadow, "Shadow"),
103 DATA(A_REVERSE, DIALOG, dialog, "Dialog box"),
104 DATA(A_REVERSE, TITLE, title, "Dialog box title"),
105 DATA(A_REVERSE, BORDER, border, "Dialog box border"),
106 DATA(A_BOLD, BUTTON_ACTIVE, button_active, "Active button"),
107 DATA(A_DIM, BUTTON_INACTIVE, button_inactive, "Inactive button"),
108 DATA(A_UNDERLINE, BUTTON_KEY_ACTIVE, button_key_active, "Active button key"),
109 DATA(A_UNDERLINE, BUTTON_KEY_INACTIVE, button_key_inactive, "Inactive button key"),
110 DATA(A_NORMAL, BUTTON_LABEL_ACTIVE, button_label_active, "Active button label"),
111 DATA(A_NORMAL, BUTTON_LABEL_INACTIVE, button_label_inactive, "Inactive button label"),
112 DATA(A_REVERSE, INPUTBOX, inputbox, "Input box"),
113 DATA(A_REVERSE, INPUTBOX_BORDER, inputbox_border, "Input box border"),
114 DATA(A_REVERSE, SEARCHBOX, searchbox, "Search box"),
115 DATA(A_REVERSE, SEARCHBOX_TITLE, searchbox_title, "Search box title"),
116 DATA(A_REVERSE, SEARCHBOX_BORDER, searchbox_border, "Search box border"),
117 DATA(A_REVERSE, POSITION_INDICATOR, position_indicator, "File position indicator"),
118 DATA(A_REVERSE, MENUBOX, menubox, "Menu box"),
119 DATA(A_REVERSE, MENUBOX_BORDER, menubox_border, "Menu box border"),
120 DATA(A_REVERSE, ITEM, item, "Item"),
121 DATA(A_NORMAL, ITEM_SELECTED, item_selected, "Selected item"),
122 DATA(A_REVERSE, TAG, tag, "Tag"),
123 DATA(A_REVERSE, TAG_SELECTED, tag_selected, "Selected tag"),
124 DATA(A_NORMAL, TAG_KEY, tag_key, "Tag key"),
125 DATA(A_BOLD, TAG_KEY_SELECTED, tag_key_selected, "Selected tag key"),
126 DATA(A_REVERSE, CHECK, check, "Check box"),
127 DATA(A_REVERSE, CHECK_SELECTED, check_selected, "Selected check box"),
128 DATA(A_REVERSE, UARROW, uarrow, "Up arrow"),
129 DATA(A_REVERSE, DARROW, darrow, "Down arrow"),
130 DATA(A_NORMAL, ITEMHELP, itemhelp, "Item help-text"),
131 DATA(A_BOLD, FORM_ACTIVE_TEXT, form_active_text, "Active form text"),
132 DATA(A_REVERSE, FORM_TEXT, form_text, "Form text"),
133 DATA(A_NORMAL, FORM_ITEM_READONLY, form_item_readonly, "Readonly form item"),
134 DATA(A_REVERSE, GAUGE, gauge, "Dialog box gauge"),
135 DATA(A_REVERSE, BORDER2, border2, "Dialog box border2"),
136 DATA(A_REVERSE, INPUTBOX_BORDER2, inputbox_border2, "Input box border2"),
137 DATA(A_REVERSE, SEARCHBOX_BORDER2, searchbox_border2, "Search box border2"),
138 DATA(A_REVERSE, MENUBOX_BORDER2, menubox_border2, "Menu box border2")
143 * Maintain a list of subwindows so that we can delete them to cleanup.
144 * More important, this provides a fallback when wgetparent() is not available.
147 add_subwindow(WINDOW *parent, WINDOW *child)
149 DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1);
154 p->next = dialog_state.all_subwindows;
155 dialog_state.all_subwindows = p;
160 del_subwindows(WINDOW *parent)
162 DIALOG_WINDOWS *p = dialog_state.all_subwindows;
163 DIALOG_WINDOWS *q = 0;
167 if (p->normal == parent) {
171 dialog_state.all_subwindows = r;
185 * Display background title if it exists ...
188 dlg_put_backtitle(void)
192 if (dialog_vars.backtitle != NULL) {
193 chtype attr = A_NORMAL;
194 int backwidth = dlg_count_columns(dialog_vars.backtitle);
196 (void) wattrset(stdscr, screen_attr);
197 (void) wmove(stdscr, 0, 1);
198 dlg_print_text(stdscr, dialog_vars.backtitle, COLS - 2, &attr);
199 for (i = 0; i < COLS - backwidth; i++)
200 (void) waddch(stdscr, ' ');
201 (void) wmove(stdscr, 1, 1);
202 for (i = 0; i < COLS - 2; i++)
203 (void) waddch(stdscr, dlg_boxchar(ACS_HLINE));
206 (void) wnoutrefresh(stdscr);
210 * Set window to attribute 'attr'. There are more efficient ways to do this,
211 * but will not work on older/buggy ncurses versions.
214 dlg_attr_clear(WINDOW *win, int height, int width, chtype attr)
218 (void) wattrset(win, attr);
219 for (i = 0; i < height; i++) {
220 (void) wmove(win, i, 0);
221 for (j = 0; j < width; j++)
222 (void) waddch(win, ' ');
224 (void) touchwin(win);
230 dlg_attr_clear(stdscr, LINES, COLS, screen_attr);
233 #define isprivate(s) ((s) != 0 && strstr(s, "\033[?") != 0)
235 #define TTY_DEVICE "/dev/tty"
238 * If $DIALOG_TTY exists, allow the program to try to open the terminal
239 * directly when stdout is redirected. By default we require the "--stdout"
240 * option to be given, but some scripts were written making use of the
241 * behavior of dialog which tried opening the terminal anyway.
246 char *result = getenv("DIALOG_TTY");
247 if (result != 0 && atoi(result) == 0)
253 * Open the terminal directly. If one of stdin, stdout or stderr really points
254 * to a tty, use it. Otherwise give up and open /dev/tty.
257 open_terminal(char **result, int mode)
259 const char *device = TTY_DEVICE;
260 if (!isatty(fileno(stderr))
261 || (device = ttyname(fileno(stderr))) == 0) {
262 if (!isatty(fileno(stdout))
263 || (device = ttyname(fileno(stdout))) == 0) {
264 if (!isatty(fileno(stdin))
265 || (device = ttyname(fileno(stdin))) == 0) {
270 *result = dlg_strclone(device);
271 return open(device, mode);
275 * Do some initialization for dialog.
277 * 'input' is the real tty input of dialog. Usually it is stdin, but if
278 * --input-fd option is used, it may be anything.
280 * 'output' is where dialog will send its result. Usually it is stderr, but
281 * if --stdout or --output-fd is used, it may be anything. We are concerned
282 * mainly with the case where it happens to be the same as stdout.
285 init_dialog(FILE *input, FILE *output)
290 setlocale(LC_ALL, "");
292 dialog_state.output = output;
293 dialog_state.tab_len = TAB_LEN;
294 dialog_state.aspect_ratio = DEFAULT_ASPECT_RATIO;
296 dialog_state.use_colors = USE_COLORS; /* use colors by default? */
297 dialog_state.use_shadow = USE_SHADOW; /* shadow dialog boxes by default? */
301 if (dlg_parse_rc() == -1) /* Read the configuration file */
302 dlg_exiterr("init_dialog: dlg_parse_rc");
306 * Some widgets (such as gauge) may read from the standard input. Pipes
307 * only connect stdout/stdin, so there is not much choice. But reading a
308 * pipe would get in the way of curses' normal reading stdin for getch.
310 * As in the --stdout (see below), reopening the terminal does not always
311 * work properly. dialog provides a --pipe-fd option for this purpose. We
312 * test that case first (differing fileno's for input/stdin). If the
313 * fileno's are equal, but we're not reading from a tty, see if we can open
316 dialog_state.pipe_input = stdin;
317 if (fileno(input) != fileno(stdin)) {
318 if ((fd1 = dup(fileno(input))) >= 0
319 && (fd2 = dup(fileno(stdin))) >= 0) {
320 (void) dup2(fileno(input), fileno(stdin));
321 dialog_state.pipe_input = fdopen(fd2, "r");
322 if (fileno(stdin) != 0) /* some functions may read fd #0 */
323 (void) dup2(fileno(stdin), 0);
325 dlg_exiterr("cannot open tty-input");
328 } else if (!isatty(fileno(stdin))) {
329 if ((fd1 = open_terminal(&device, O_RDONLY)) >= 0) {
330 if ((fd2 = dup(fileno(stdin))) >= 0) {
331 dialog_state.pipe_input = fdopen(fd2, "r");
332 if (freopen(device, "r", stdin) == 0)
333 dlg_exiterr("cannot open tty-input");
334 if (fileno(stdin) != 0) /* some functions may read fd #0 */
335 (void) dup2(fileno(stdin), 0);
343 * If stdout is not a tty and dialog is called with the --stdout option, we
344 * have to provide for a way to write to the screen.
346 * The curses library normally writes its output to stdout, leaving stderr
347 * free for scripting. Scripts are simpler when stdout is redirected. The
348 * newterm function is useful; it allows us to specify where the output
349 * goes. Reopening the terminal is not portable since several
350 * configurations do not allow this to work properly:
352 * a) some getty implementations (and possibly broken tty drivers, e.g., on
353 * HPUX 10 and 11) cause stdin to act as if it is still in cooked mode
354 * even though results from ioctl's state that it is successfully
355 * altered to raw mode. Broken is the proper term.
357 * b) the user may not have permissions on the device, e.g., if one su's
358 * from the login user to another non-privileged user.
360 if (!isatty(fileno(stdout))
361 && (fileno(stdout) == fileno(output) || dialog_tty())) {
362 if ((fd1 = open_terminal(&device, O_WRONLY)) >= 0
363 && (dialog_state.screen_output = fdopen(fd1, "w")) != 0) {
364 if (newterm(NULL, dialog_state.screen_output, stdin) == 0) {
365 dlg_exiterr("cannot initialize curses");
369 dlg_exiterr("cannot open tty-output");
372 dialog_state.screen_output = stdout;
375 #ifdef NCURSES_VERSION
377 * Cancel xterm's alternate-screen mode.
379 if (!dialog_vars.keep_tite
380 && (dialog_state.screen_output != stdout
381 || isatty(fileno(dialog_state.screen_output)))
382 && key_mouse != 0 /* xterm and kindred */
383 && isprivate(enter_ca_mode)
384 && isprivate(exit_ca_mode)) {
386 * initscr() or newterm() already did putp(enter_ca_mode) as a side
387 * effect of initializing the screen. It would be nice to not even
388 * do that, but we do not really have access to the correct copy of
389 * the terminfo description until those functions have been invoked.
391 (void) putp(exit_ca_mode);
392 (void) putp(clear_screen);
394 * Prevent ncurses from switching "back" to the normal screen when
395 * exiting from dialog. That would move the cursor to the original
396 * location saved in xterm. Normally curses sets the cursor position
397 * to the first line after the display, but the alternate screen
398 * switching is done after that point.
400 * Cancelling the strings altogether also works around the buggy
401 * implementation of alternate-screen in rxvt, etc., which clear
402 * more of the display than they should.
411 (void) keypad(stdscr, TRUE);
415 if (!dialog_state.no_mouse) {
419 dialog_state.screen_initialized = TRUE;
422 if (dialog_state.use_colors || dialog_state.use_shadow)
423 dlg_color_setup(); /* Set up colors */
426 /* Set screen to screen attribute */
431 static int defined_colors = 1; /* pair-0 is reserved */
433 * Setup for color display
436 dlg_color_setup(void)
440 if (has_colors()) { /* Terminal supports color? */
441 (void) start_color();
443 #if defined(HAVE_USE_DEFAULT_COLORS)
444 use_default_colors();
447 #if defined(__NetBSD__) && defined(_CURSES_)
448 #define C_ATTR(x,y) (((x) != 0 ? A_BOLD : 0) | COLOR_PAIR((y)))
449 /* work around bug in NetBSD curses */
450 for (i = 0; i < sizeof(dlg_color_table) /
451 sizeof(dlg_color_table[0]); i++) {
453 /* Initialize color pairs */
454 (void) init_pair(i + 1,
455 dlg_color_table[i].fg,
456 dlg_color_table[i].bg);
458 /* Setup color attributes */
459 dlg_color_table[i].atr = C_ATTR(dlg_color_table[i].hilite, i + 1);
461 defined_colors = i + 1;
463 for (i = 0; i < sizeof(dlg_color_table) /
464 sizeof(dlg_color_table[0]); i++) {
466 /* Initialize color pairs */
467 chtype color = dlg_color_pair(dlg_color_table[i].fg,
468 dlg_color_table[i].bg);
470 /* Setup color attributes */
471 dlg_color_table[i].atr = ((dlg_color_table[i].hilite
478 dialog_state.use_colors = FALSE;
479 dialog_state.use_shadow = FALSE;
484 dlg_color_count(void)
486 return sizeof(dlg_color_table) / sizeof(dlg_color_table[0]);
490 * Wrapper for getattrs(), or the more cumbersome X/Open wattr_get().
493 dlg_get_attrs(WINDOW *win)
497 result = (chtype) getattrs(win);
501 wattr_get(win, &my_result, &my_pair, NULL);
508 * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
509 * have (or can) define a pair with the given color as foreground on the
510 * window's defined background.
513 dlg_color_pair(int foreground, int background)
520 for (pair = 1; pair < defined_colors; ++pair) {
521 if (pair_content((short) pair, &fg, &bg) != ERR
523 && bg == background) {
524 result = (chtype) COLOR_PAIR(pair);
529 if (!found && (defined_colors + 1) < COLOR_PAIRS) {
530 pair = defined_colors++;
531 (void) init_pair((short) pair, (short) foreground, (short) background);
532 result = (chtype) COLOR_PAIR(pair);
538 * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
539 * have (or can) define a pair with the given color as foreground on the
540 * window's defined background.
543 define_color(WINDOW *win, int foreground)
545 chtype attrs = dlg_get_attrs(win);
547 short fg, bg, background;
549 if ((pair = PAIR_NUMBER(attrs)) != 0
550 && pair_content((short) pair, &fg, &bg) != ERR) {
553 background = COLOR_BLACK;
555 return dlg_color_pair(foreground, background);
560 * End using dialog functions.
565 if (dialog_state.screen_initialized) {
566 dialog_state.screen_initialized = FALSE;
569 (void) fflush(stdout);
574 #define isOurEscape(p) (((p)[0] == '\\') && ((p)[1] == 'Z') && ((p)[2] != 0))
577 dlg_count_real_columns(const char *text)
579 int result = dlg_count_columns(text);
580 if (result && dialog_vars.colors) {
583 if (dialog_vars.colors && isOurEscape(text)) {
584 hidden += ESCAPE_LEN;
596 centered(int width, const char *string)
598 int need = dlg_count_real_columns(string);
601 left = (width - need) / 2 - 1;
607 #ifdef USE_WIDE_CURSES
609 is_combining(const char *txt, int *combined)
613 if (*combined == 0) {
614 if (UCH(*txt) >= 128) {
617 size_t given = strlen(txt);
620 memset(&state, 0, sizeof(state));
621 len = mbrtowc(&wch, txt, given, &state);
622 if ((int) len > 0 && wcwidth(wch) == 0) {
623 *combined = (int) len - 1;
636 * Print the name (tag) or text from a DIALOG_LISTITEM, highlighting the
637 * first character if selected.
640 dlg_print_listitem(WINDOW *win,
646 chtype attr = A_NORMAL;
649 const int *indx = dlg_index_wchars(text);
653 attrs[3] = tag_key_selected_attr;
654 attrs[2] = tag_key_attr;
655 attrs[1] = tag_selected_attr;
658 (void) wattrset(win, selected ? attrs[3] : attrs[2]);
659 (void) waddnstr(win, text, indx[1]);
661 if ((int) strlen(text) > indx[1]) {
662 limit = dlg_limit_columns(text, climit, 1);
664 (void) wattrset(win, selected ? attrs[1] : attrs[0]);
667 indx[limit] - indx[1]);
671 attrs[1] = item_selected_attr;
672 attrs[0] = item_attr;
674 cols = dlg_index_columns(text);
675 limit = dlg_limit_columns(text, climit, 0);
678 (void) wattrset(win, selected ? attrs[1] : attrs[0]);
679 dlg_print_text(win, text, cols[limit], &attr);
685 * Print up to 'cols' columns from 'text', optionally rendering our escape
686 * sequence for attributes and color.
689 dlg_print_text(WINDOW *win, const char *txt, int cols, chtype *attr)
691 int y_origin, x_origin;
692 int y_before, x_before = 0;
693 int y_after, x_after;
698 #ifdef USE_WIDE_CURSES
702 getyx(win, y_origin, x_origin);
703 while (cols > 0 && (*txt != '\0')) {
704 if (dialog_vars.colors) {
705 while (isOurEscape(txt)) {
709 switch (code = CharOf(*txt)) {
720 *attr |= define_color(win, code - '0');
736 *attr &= ~A_UNDERLINE;
739 *attr |= A_UNDERLINE;
748 if (ended || *txt == '\n' || *txt == '\0')
750 useattr = (*attr) & A_ATTRIBUTES;
753 * Prevent this from making text invisible when the foreground and
754 * background colors happen to be the same, and there's no bold
757 if ((useattr & A_COLOR) != 0 && (useattr & A_BOLD) == 0) {
758 short pair = (short) PAIR_NUMBER(useattr);
760 if (pair_content(pair, &fg, &bg) != ERR
763 useattr |= dlg_color_pair(fg, ((bg == COLOR_BLACK)
770 * Write the character, using curses to tell exactly how wide it
771 * is. If it is a tab, discount that, since the caller thinks
772 * tabs are nonprinting, and curses will expand tabs to one or
775 thisTab = (CharOf(*txt) == TAB);
777 getyx(win, y_before, x_before);
780 (void) waddch(win, CharOf(*txt++) | useattr);
781 getyx(win, y_after, x_after);
782 if (thisTab && (y_after == y_origin))
783 tabbed += (x_after - x_before);
784 if ((y_after != y_origin) ||
785 (x_after >= (cols + tabbed + x_origin)
786 #ifdef USE_WIDE_CURSES
787 && !is_combining(txt, &combined)
796 * Print one line of the prompt in the window within the limits of the
797 * specified right margin. The line will end on a word boundary and a pointer
798 * to the start of the next line is returned, or a NULL pointer if the end of
799 * *prompt is reached.
802 dlg_print_line(WINDOW *win,
805 int lm, int rm, int *x)
807 const char *wrap_ptr;
808 const char *test_ptr;
809 const char *hide_ptr = 0;
810 const int *cols = dlg_index_columns(prompt);
811 const int *indx = dlg_index_wchars(prompt);
816 int limit = dlg_count_wchars(prompt);
823 * Set *test_ptr to the end of the line or the right margin (rm), whichever
824 * is less, and set wrap_ptr to the end of the last word in the line.
826 for (n = 0; n < limit; ++n) {
827 test_ptr = prompt + indx[test_inx];
828 if (*test_ptr == '\n' || *test_ptr == '\0' || cur_x >= (rm + hidden))
830 if (*test_ptr == TAB && n == 0) {
831 tabbed = 8; /* workaround for leading tabs */
832 } else if (*test_ptr == ' ' && n != 0 && prompt[indx[n - 1]] != ' ') {
835 } else if (dialog_vars.colors && isOurEscape(test_ptr)) {
837 hidden += ESCAPE_LEN;
838 n += (ESCAPE_LEN - 1);
840 cur_x = lm + tabbed + cols[n + 1];
841 if (cur_x > (rm + hidden))
847 * If the line doesn't reach the right margin in the middle of a word, then
848 * we don't have to wrap it at the end of the previous word.
850 test_ptr = prompt + indx[test_inx];
851 if (*test_ptr == '\n' || *test_ptr == ' ' || *test_ptr == '\0') {
853 while (wrap_inx > 0 && prompt[indx[wrap_inx - 1]] == ' ') {
856 *x = lm + indx[wrap_inx];
857 } else if (*x == 1 && cur_x >= rm) {
859 * If the line has no spaces, then wrap it anyway at the right margin
864 wrap_ptr = prompt + indx[wrap_inx];
865 #ifdef USE_WIDE_CURSES
866 if (UCH(*wrap_ptr) >= 128) {
868 while (is_combining(wrap_ptr, &combined)) {
875 * If we found hidden text past the last point that we will display,
876 * discount that from the displayed length.
878 if ((hide_ptr != 0) && (hide_ptr >= wrap_ptr)) {
879 hidden -= ESCAPE_LEN;
881 while (test_ptr < wrap_ptr) {
882 if (dialog_vars.colors && isOurEscape(test_ptr)) {
883 hidden -= ESCAPE_LEN;
884 test_ptr += ESCAPE_LEN;
892 * Print the line if we have a window pointer. Otherwise this routine
893 * is just being called for sizing the window.
896 dlg_print_text(win, prompt, (cols[wrap_inx] - hidden), attr);
899 /* *x tells the calling function how long the line was */
905 /* Find the start of the next line and return a pointer to it */
907 while (*test_ptr == ' ')
909 if (*test_ptr == '\n')
915 justify_text(WINDOW *win,
919 int *high, int *wide)
921 chtype attr = A_NORMAL;
922 int x = (2 * MARGIN);
925 int lm = (2 * MARGIN); /* left margin (box-border plus a space) */
926 int rm = limit_x; /* right margin */
927 int bm = limit_y; /* bottom margin */
928 int last_y = 0, last_x = 0;
938 getyx(win, last_y, last_x);
939 while (y <= bm && *prompt) {
942 if (*prompt == '\n') {
943 while (*prompt == '\n' && y < bm) {
944 if (*(prompt + 1) != '\0') {
947 (void) wmove(win, y, lm);
952 (void) wmove(win, y, lm);
955 prompt = dlg_print_line(win, &attr, prompt, lm, rm, &x);
957 getyx(win, last_y, last_x);
962 (void) wmove(win, y, lm);
964 max_x = MAX(max_x, x);
966 /* Move back to the last position after drawing prompt, for msgbox. */
968 (void) wmove(win, last_y, last_x);
970 /* Set the final height and width for the calling function */
978 * Print a string of text in a window, automatically wrap around to the next
979 * line if the string is too long to fit on one line. Note that the string may
980 * contain embedded newlines.
983 dlg_print_autowrap(WINDOW *win, const char *prompt, int height, int width)
985 justify_text(win, prompt,
988 (int *) 0, (int *) 0);
992 * Display the message in a scrollable window. Actually the way it works is
993 * that we create a "tall" window of the proper width, let the text wrap within
994 * that, and copy a slice of the result to the dialog.
996 * It works for ncurses. Other curses implementations show only blanks (Tru64)
997 * or garbage (NetBSD).
1000 dlg_print_scrolled(WINDOW *win,
1010 (void) pauseopt; /* used only for ncurses */
1012 getyx(win, oldy, oldx);
1013 #ifdef NCURSES_VERSION
1015 int wide = width - (2 * MARGIN);
1023 #if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20040417
1025 * If we're not limited by the screensize, allow text to possibly be
1026 * one character per line.
1028 if ((len = dlg_count_columns(prompt)) > high)
1031 dummy = newwin(high, width, 0, 0);
1033 (void) wattrset(win, dialog_attr);
1034 dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width);
1037 wbkgdset(dummy, dialog_attr | ' ');
1038 (void) wattrset(dummy, dialog_attr);
1040 dlg_print_autowrap(dummy, prompt, high, width);
1044 copywin(dummy, /* srcwin */
1046 offset + MARGIN, /* sminrow */
1047 MARGIN, /* smincol */
1048 MARGIN, /* dminrow */
1049 MARGIN, /* dmincol */
1050 height, /* dmaxrow */
1056 /* if the text is incomplete, or we have scrolled, show the percentage */
1057 if (y > 0 && wide > 4) {
1058 percent = (int) ((height + offset) * 100.0 / y);
1063 if (offset != 0 || percent != 100) {
1064 (void) wattrset(win, position_indicator_attr);
1065 (void) wmove(win, MARGIN + height, wide - 4);
1066 (void) sprintf(buffer, "%d%%", percent);
1067 (void) waddstr(win, buffer);
1068 if ((len = (int) strlen(buffer)) < 4) {
1069 (void) wattrset(win, border_attr);
1070 whline(win, dlg_boxchar(ACS_HLINE), 4 - len);
1074 last = (y - height);
1080 (void) wattrset(win, dialog_attr);
1081 dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width);
1084 wmove(win, oldy, oldx);
1089 dlg_check_scrolled(int key, int last, int page, bool * show, int *offset)
1096 case DLGK_PAGE_FIRST:
1102 case DLGK_PAGE_LAST:
1103 if (*offset < last) {
1114 case DLGK_GRID_DOWN:
1115 if (*offset < last) {
1120 case DLGK_PAGE_PREV:
1128 case DLGK_PAGE_NEXT:
1129 if (*offset < last) {
1144 * Calculate the window size for preformatted text. This will calculate box
1145 * dimensions that are at or close to the specified aspect ratio for the prompt
1146 * string with all spaces and newlines preserved and additional newlines added
1150 auto_size_preformatted(const char *prompt, int *height, int *width)
1152 int high = 0, wide = 0;
1153 float car; /* Calculated Aspect Ratio */
1155 int max_y = SLINES - 1;
1156 int max_x = SCOLS - 2;
1157 int max_width = max_x;
1158 int ar = dialog_state.aspect_ratio;
1160 /* Get the initial dimensions */
1161 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide);
1162 car = (float) (wide / high);
1165 * If the aspect ratio is greater than it should be, then decrease the
1166 * width proportionately.
1169 diff = car / (float) ar;
1170 max_x = (int) ((float) wide / diff + 4);
1171 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide);
1172 car = (float) wide / (float) high;
1176 * If the aspect ratio is too small after decreasing the width, then
1177 * incrementally increase the width until the aspect ratio is equal to or
1178 * greater than the specified aspect ratio.
1180 while (car < ar && max_x < max_width) {
1182 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide);
1183 car = (float) (wide / high);
1191 * Find the length of the longest "word" in the given string. By setting the
1192 * widget width at least this long, we can avoid splitting a word on the
1196 longest_word(const char *string)
1198 int length, result = 0;
1200 while (*string != '\0') {
1202 while (*string != '\0' && !isspace(UCH(*string))) {
1206 result = MAX(result, length);
1207 if (*string != '\0')
1214 * if (height or width == -1) Maximize()
1215 * if (height or width == 0), justify and return actual limits.
1218 real_auto_size(const char *title,
1220 int *height, int *width,
1221 int boxlines, int mincols)
1223 int x = (dialog_vars.begin_set ? dialog_vars.begin_x : 2);
1224 int y = (dialog_vars.begin_set ? dialog_vars.begin_y : 1);
1225 int title_length = title ? dlg_count_columns(title) : 0;
1229 int save_high = *height;
1230 int save_wide = *width;
1247 wide = MAX(title_length, mincols);
1248 if (strchr(prompt, '\n') == 0) {
1249 double val = (dialog_state.aspect_ratio *
1250 dlg_count_real_columns(prompt));
1251 double xxx = sqrt(val);
1252 int tmp = (int) xxx;
1253 wide = MAX(wide, tmp);
1254 wide = MAX(wide, longest_word(prompt));
1255 justify_text((WINDOW *) 0, prompt, high, wide, height, width);
1257 auto_size_preformatted(prompt, height, width);
1261 justify_text((WINDOW *) 0, prompt, high, wide, height, width);
1265 if (*width < title_length) {
1266 justify_text((WINDOW *) 0, prompt, high, title_length, height, width);
1267 *width = title_length;
1270 if (*width < mincols && save_wide == 0)
1274 *height += boxlines + 2;
1277 *height = save_high;
1282 /* End of real_auto_size() */
1285 dlg_auto_size(const char *title,
1292 real_auto_size(title, prompt, height, width, boxlines, mincols);
1294 if (*width > SCOLS) {
1299 if (*height > SLINES)
1304 * if (height or width == -1) Maximize()
1305 * if (height or width == 0)
1306 * height=MIN(SLINES, num.lines in fd+n);
1307 * width=MIN(SCOLS, MAX(longer line+n, mincols));
1310 dlg_auto_sizefile(const char *title,
1318 int len = title ? dlg_count_columns(title) : 0;
1325 /* Open input file for reading */
1326 if ((fd = fopen(file, "rb")) == NULL)
1327 dlg_exiterr("dlg_auto_sizefile: Cannot open input file %s", file);
1329 if ((*height == -1) || (*width == -1)) {
1330 *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
1331 *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0);
1333 if ((*height != 0) && (*width != 0)) {
1337 if (*height > SLINES)
1344 while (((ch = getc(fd)) != '\n') && !feof(fd))
1345 if ((ch == TAB) && (dialog_vars.tab_correct))
1346 offset += dialog_state.tab_len - (offset % dialog_state.tab_len);
1356 /* now 'count' has the number of lines of fd and 'len' the max length */
1358 *height = MIN(SLINES, count + numlines + boxlines);
1359 *width = MIN(SCOLS, MAX((len + nc), mincols));
1360 /* here width and height can be maximized if > SCOLS|SLINES because
1361 textbox-like widgets don't put all <file> on the screen.
1362 Msgbox-like widget instead have to put all <text> correctly. */
1368 dlg_get_cell_attrs(WINDOW *win)
1371 #ifdef USE_WIDE_CURSES
1376 if (win_wch(win, &wch) == OK
1377 && getcchar(&wch, &cc, &attrs, &pair, NULL) == OK) {
1383 result = winch(win) & (A_ATTRIBUTES & ~A_COLOR);
1389 * Draw a rectangular box with line drawing characters.
1391 * borderchar is used to color the upper/left edges.
1393 * boxchar is used to color the right/lower edges. It also is fill-color used
1394 * for the box contents.
1396 * Normally, if you are drawing a scrollable box, use menubox_border_attr for
1397 * boxchar, and menubox_attr for borderchar since the scroll-arrows are drawn
1398 * with menubox_attr at the top, and menubox_border_attr at the bottom. That
1399 * also (given the default color choices) produces a recessed effect.
1401 * If you want a raised effect (and are not going to use the scroll-arrows),
1402 * reverse this choice.
1405 dlg_draw_box2(WINDOW *win, int y, int x, int height, int width,
1406 chtype boxchar, chtype borderchar, chtype borderchar2)
1409 chtype save = dlg_get_attrs(win);
1411 (void) wattrset(win, 0);
1412 for (i = 0; i < height; i++) {
1413 (void) wmove(win, y + i, x);
1414 for (j = 0; j < width; j++)
1416 (void) waddch(win, borderchar | dlg_boxchar(ACS_ULCORNER));
1417 else if (i == height - 1 && !j)
1418 (void) waddch(win, borderchar | dlg_boxchar(ACS_LLCORNER));
1419 else if (!i && j == width - 1)
1420 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_URCORNER));
1421 else if (i == height - 1 && j == width - 1)
1422 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_LRCORNER));
1424 (void) waddch(win, borderchar | dlg_boxchar(ACS_HLINE));
1425 else if (i == height - 1)
1426 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_HLINE));
1428 (void) waddch(win, borderchar | dlg_boxchar(ACS_VLINE));
1429 else if (j == width - 1)
1430 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_VLINE));
1432 (void) waddch(win, boxchar | ' ');
1434 (void) wattrset(win, save);
1438 dlg_draw_box(WINDOW *win, int y, int x, int height, int width,
1439 chtype boxchar, chtype borderchar)
1441 dlg_draw_box2(win, y, x, height, width, boxchar, borderchar, boxchar);
1444 static DIALOG_WINDOWS *
1445 find_window(WINDOW *win)
1447 DIALOG_WINDOWS *result = 0;
1450 for (p = dialog_state.all_windows; p != 0; p = p->next) {
1451 if (p->normal == win) {
1461 * If we have wchgat(), use that for updating shadow attributes, to work with
1462 * wide-character data.
1466 * Check if the given point is "in" the given window. If so, return the window
1467 * pointer, otherwise null.
1470 in_window(WINDOW *win, int y, int x)
1473 int y_base = getbegy(win);
1474 int x_base = getbegx(win);
1475 int y_last = getmaxy(win) + y_base;
1476 int x_last = getmaxx(win) + x_base;
1478 if (y >= y_base && y <= y_last && x >= x_base && x <= x_last)
1484 window_at_cell(DIALOG_WINDOWS * dw, int y, int x)
1488 int y_want = y + getbegy(dw->shadow);
1489 int x_want = x + getbegx(dw->shadow);
1491 for (p = dialog_state.all_windows; p != 0; p = p->next) {
1492 if (dw->normal != p->normal
1493 && dw->shadow != p->normal
1494 && (result = in_window(p->normal, y_want, x_want)) != 0) {
1505 in_shadow(WINDOW *normal, WINDOW *shadow, int y, int x)
1507 bool result = FALSE;
1508 int ybase = getbegy(normal);
1509 int ylast = getmaxy(normal) + ybase;
1510 int xbase = getbegx(normal);
1511 int xlast = getmaxx(normal) + xbase;
1513 y += getbegy(shadow);
1514 x += getbegx(shadow);
1516 if (y >= ybase + SHADOW_ROWS
1517 && y < ylast + SHADOW_ROWS
1519 && x < xlast + SHADOW_COLS) {
1520 /* in the right-side */
1522 } else if (y >= ylast
1523 && y < ylast + SHADOW_ROWS
1524 && x >= ybase + SHADOW_COLS
1525 && x < ylast + SHADOW_COLS) {
1526 /* check the bottom */
1534 * When erasing a shadow, check each cell to make sure that it is not part of
1535 * another box's shadow. This is a little complicated since most shadows are
1536 * merged onto stdscr.
1539 last_shadow(DIALOG_WINDOWS * dw, int y, int x)
1544 for (p = dialog_state.all_windows; p != 0; p = p->next) {
1545 if (p->normal != dw->normal
1546 && in_shadow(p->normal, dw->shadow, y, x)) {
1555 repaint_cell(DIALOG_WINDOWS * dw, bool draw, int y, int x)
1557 WINDOW *win = dw->shadow;
1561 if ((cellwin = window_at_cell(dw, y, x)) != 0
1562 && (draw || last_shadow(dw, y, x))
1563 && (y2 = (y + getbegy(win) - getbegy(cellwin))) >= 0
1564 && (x2 = (x + getbegx(win) - getbegx(cellwin))) >= 0
1565 && wmove(cellwin, y2, x2) != ERR) {
1566 chtype the_cell = dlg_get_attrs(cellwin);
1567 chtype the_attr = (draw ? shadow_attr : the_cell);
1569 if (dlg_get_cell_attrs(cellwin) & A_ALTCHARSET) {
1570 the_attr |= A_ALTCHARSET;
1574 the_attr & (chtype) (~A_COLOR),
1575 (short) PAIR_NUMBER(the_attr),
1579 chtype the_char = ((winch(cellwin) & A_CHARTEXT) | the_attr);
1580 (void) waddch(cellwin, the_char);
1583 wnoutrefresh(cellwin);
1587 #define RepaintCell(dw, draw, y, x) repaint_cell(dw, draw, y, x)
1590 repaint_shadow(DIALOG_WINDOWS * dw, bool draw, int y, int x, int height, int width)
1594 if (UseShadow(dw)) {
1596 chtype save = dlg_get_attrs(dw->shadow);
1597 (void) wattrset(dw->shadow, draw ? shadow_attr : screen_attr);
1599 for (i = 0; i < SHADOW_ROWS; ++i) {
1600 for (j = 0; j < width; ++j) {
1601 RepaintCell(dw, draw, i + y + height, j + x + SHADOW_COLS);
1604 for (i = 0; i < height; i++) {
1605 for (j = 0; j < SHADOW_COLS; ++j) {
1606 RepaintCell(dw, draw, i + y + SHADOW_ROWS, j + x + width);
1609 (void) wnoutrefresh(dw->shadow);
1611 (void) wattrset(dw->shadow, save);
1617 * Draw a shadow on the parent window corresponding to the right- and
1618 * bottom-edge of the child window, to give a 3-dimensional look.
1621 draw_childs_shadow(DIALOG_WINDOWS * dw)
1623 if (UseShadow(dw)) {
1626 getbegy(dw->normal) - getbegy(dw->shadow),
1627 getbegx(dw->normal) - getbegx(dw->shadow),
1628 getmaxy(dw->normal),
1629 getmaxx(dw->normal));
1634 * Erase a shadow on the parent window corresponding to the right- and
1635 * bottom-edge of the child window.
1638 erase_childs_shadow(DIALOG_WINDOWS * dw)
1640 if (UseShadow(dw)) {
1643 getbegy(dw->normal) - getbegy(dw->shadow),
1644 getbegx(dw->normal) - getbegx(dw->shadow),
1645 getmaxy(dw->normal),
1646 getmaxx(dw->normal));
1651 * Draw shadows along the right and bottom edge to give a more 3D look
1655 dlg_draw_shadow(WINDOW *win, int y, int x, int height, int width)
1657 repaint_shadow(find_window(win), TRUE, y, x, height, width);
1659 #endif /* HAVE_COLOR */
1662 * Allow shell scripts to remap the exit codes so they can distinguish ESC
1669 static const struct {
1673 { DLG_EXIT_CANCEL, "DIALOG_CANCEL" },
1674 { DLG_EXIT_ERROR, "DIALOG_ERROR" },
1675 { DLG_EXIT_ESC, "DIALOG_ESC" },
1676 { DLG_EXIT_EXTRA, "DIALOG_EXTRA" },
1677 { DLG_EXIT_HELP, "DIALOG_HELP" },
1678 { DLG_EXIT_OK, "DIALOG_OK" },
1679 { DLG_EXIT_ITEM_HELP, "DIALOG_ITEM_HELP" },
1687 bool overridden = FALSE;
1690 for (n = 0; n < sizeof(table) / sizeof(table[0]); n++) {
1691 if (table[n].code == code) {
1692 if ((name = getenv(table[n].name)) != 0) {
1693 value = strtol(name, &temp, 0);
1694 if (temp != 0 && temp != name && *temp == '\0') {
1704 * Prior to 2004/12/19, a widget using --item-help would exit with "OK"
1705 * if the help button were selected. Now we want to exit with "HELP",
1706 * but allow the environment variable to override.
1708 if (code == DLG_EXIT_ITEM_HELP && !overridden) {
1709 code = DLG_EXIT_HELP;
1712 #ifdef HAVE_DLG_TRACE
1713 dlg_trace((const char *) 0); /* close it */
1717 _dlg_inputstr_leaks();
1718 #if defined(NCURSES_VERSION) && defined(HAVE__NC_FREE_AND_EXIT)
1719 _nc_free_and_exit(code);
1723 if (dialog_state.input == stdin) {
1727 * Just in case of using --input-fd option, do not
1728 * call atexit functions of ncurses which may hang.
1730 if (dialog_state.input) {
1731 fclose(dialog_state.input);
1732 dialog_state.input = 0;
1734 if (dialog_state.pipe_input) {
1735 if (dialog_state.pipe_input != stdin) {
1736 fclose(dialog_state.pipe_input);
1737 dialog_state.pipe_input = 0;
1744 /* quit program killing all tailbg */
1746 dlg_exiterr(const char *fmt,...)
1753 (void) fputc('\n', stderr);
1755 (void) vfprintf(stderr, fmt, ap);
1757 (void) fputc('\n', stderr);
1759 dlg_killall_bg(&retval);
1761 (void) fflush(stderr);
1762 (void) fflush(stdout);
1763 dlg_exit(DLG_EXIT_ERROR);
1769 if (dialog_vars.beep_signal) {
1771 dialog_vars.beep_signal = 0;
1776 dlg_print_size(int height, int width)
1778 if (dialog_vars.print_siz)
1779 fprintf(dialog_state.output, "Size: %d, %d\n", height, width);
1783 dlg_ctl_size(int height, int width)
1785 if (dialog_vars.size_err) {
1786 if ((width > COLS) || (height > LINES)) {
1787 dlg_exiterr("Window too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
1788 height, width, LINES, COLS);
1791 else if ((dialog_state.use_shadow)
1792 && ((width > SCOLS || height > SLINES))) {
1793 if ((width <= COLS) && (height <= LINES)) {
1794 /* try again, without shadows */
1795 dialog_state.use_shadow = 0;
1797 dlg_exiterr("Window+Shadow too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
1798 height, width, SLINES, SCOLS);
1806 * If the --tab-correct was not selected, convert tabs to single spaces.
1809 dlg_tab_correct_str(char *prompt)
1813 if (dialog_vars.tab_correct) {
1814 while ((ptr = strchr(prompt, TAB)) != NULL) {
1822 dlg_calc_listh(int *height, int *list_height, int item_no)
1824 /* calculate new height and list_height */
1825 int rows = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
1826 if (rows - (*height) > 0) {
1827 if (rows - (*height) > item_no)
1828 *list_height = item_no;
1830 *list_height = rows - (*height);
1832 (*height) += (*list_height);
1837 dlg_calc_listw(int item_no, char **items, int group)
1839 int n, i, len1 = 0, len2 = 0;
1840 for (i = 0; i < (item_no * group); i += group) {
1841 if ((n = dlg_count_columns(items[i])) > len1)
1843 if ((n = dlg_count_columns(items[i + 1])) > len2)
1850 dlg_calc_list_width(int item_no, DIALOG_LISTITEM * items)
1852 int n, i, len1 = 0, len2 = 0;
1853 int bits = ((dialog_vars.no_tags ? 1 : 0)
1854 + (dialog_vars.no_items ? 2 : 0));
1856 for (i = 0; i < item_no; ++i) {
1861 if ((n = dlg_count_columns(items[i].name)) > len1)
1863 if ((n = dlg_count_columns(items[i].text)) > len2)
1869 if ((n = dlg_count_columns(items[i].name)) > len1)
1880 static char empty[] = "";
1885 dlg_strclone(const char *cprompt)
1887 char *prompt = dlg_malloc(char, strlen(cprompt) + 1);
1888 assert_ptr(prompt, "dlg_strclone");
1889 strcpy(prompt, cprompt);
1894 dlg_asciibox(chtype ch)
1898 if (ch == ACS_ULCORNER)
1900 else if (ch == ACS_LLCORNER)
1902 else if (ch == ACS_URCORNER)
1904 else if (ch == ACS_LRCORNER)
1906 else if (ch == ACS_HLINE)
1908 else if (ch == ACS_VLINE)
1910 else if (ch == ACS_LTEE)
1912 else if (ch == ACS_RTEE)
1914 else if (ch == ACS_UARROW)
1916 else if (ch == ACS_DARROW)
1923 dlg_boxchar(chtype ch)
1925 chtype result = dlg_asciibox(ch);
1928 if (dialog_vars.ascii_lines)
1930 else if (dialog_vars.no_lines)
1937 dlg_box_x_ordinate(int width)
1941 if (dialog_vars.begin_set == 1) {
1942 x = dialog_vars.begin_x;
1944 /* center dialog box on screen unless --begin-set */
1945 x = (SCOLS - width) / 2;
1951 dlg_box_y_ordinate(int height)
1955 if (dialog_vars.begin_set == 1) {
1956 y = dialog_vars.begin_y;
1958 /* center dialog box on screen unless --begin-set */
1959 y = (SLINES - height) / 2;
1965 dlg_draw_title(WINDOW *win, const char *title)
1967 if (title != NULL) {
1968 chtype attr = A_NORMAL;
1969 chtype save = dlg_get_attrs(win);
1970 int x = centered(getmaxx(win), title);
1972 (void) wattrset(win, title_attr);
1974 dlg_print_text(win, title, getmaxx(win) - x, &attr);
1975 (void) wattrset(win, save);
1980 dlg_draw_bottom_box2(WINDOW *win, chtype on_left, chtype on_right, chtype on_inside)
1982 int width = getmaxx(win);
1983 int height = getmaxy(win);
1986 (void) wattrset(win, on_left);
1987 (void) wmove(win, height - 3, 0);
1988 (void) waddch(win, dlg_boxchar(ACS_LTEE));
1989 for (i = 0; i < width - 2; i++)
1990 (void) waddch(win, dlg_boxchar(ACS_HLINE));
1991 (void) wattrset(win, on_right);
1992 (void) waddch(win, dlg_boxchar(ACS_RTEE));
1993 (void) wattrset(win, on_inside);
1994 (void) wmove(win, height - 2, 1);
1995 for (i = 0; i < width - 2; i++)
1996 (void) waddch(win, ' ');
2000 dlg_draw_bottom_box(WINDOW *win)
2002 dlg_draw_bottom_box2(win, border_attr, dialog_attr, dialog_attr);
2006 * Remove a window, repainting everything else. This would be simpler if we
2007 * used the panel library, but that is not _always_ available.
2010 dlg_del_window(WINDOW *win)
2012 DIALOG_WINDOWS *p, *q, *r;
2015 * If --keep-window was set, do not delete/repaint the windows.
2017 if (dialog_vars.keep_window)
2020 /* Leave the main window untouched if there are no background windows.
2021 * We do this so the current window will not be cleared on exit, allowing
2022 * things like the infobox demo to run without flicker.
2024 if (dialog_state.getc_callbacks != 0) {
2026 wnoutrefresh(stdscr);
2029 for (p = dialog_state.all_windows, q = r = 0; p != 0; r = p, p = p->next) {
2030 if (p->normal == win) {
2031 q = p; /* found a match - should be only one */
2033 dialog_state.all_windows = p->next;
2038 if (p->shadow != 0) {
2039 touchwin(p->shadow);
2040 wnoutrefresh(p->shadow);
2042 touchwin(p->normal);
2043 wnoutrefresh(p->normal);
2048 if (dialog_state.all_windows != 0)
2049 erase_childs_shadow(q);
2050 del_subwindows(q->normal);
2051 dlg_unregister_window(q->normal);
2059 * Create a window, optionally with a shadow.
2062 dlg_new_window(int height, int width, int y, int x)
2064 return dlg_new_modal_window(stdscr, height, width, y, x);
2068 * "Modal" windows differ from normal ones by having a shadow in a window
2069 * separate from the standard screen.
2072 dlg_new_modal_window(WINDOW *parent, int height, int width, int y, int x)
2075 DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1);
2079 || (win = newwin(height, width, y, x)) == 0) {
2080 dlg_exiterr("Can't make new window at (%d,%d), size (%d,%d).\n",
2081 y, x, height, width);
2083 p->next = dialog_state.all_windows;
2085 dialog_state.all_windows = p;
2087 if (dialog_state.use_shadow) {
2089 draw_childs_shadow(p);
2093 (void) keypad(win, TRUE);
2098 * Move/Resize a window, optionally with a shadow.
2102 dlg_move_window(WINDOW *win, int height, int width, int y, int x)
2107 dlg_ctl_size(height, width);
2109 if ((p = find_window(win)) != 0) {
2110 (void) wresize(win, height, width);
2111 (void) mvwin(win, y, x);
2113 if (p->shadow != 0) {
2114 if (dialog_state.use_shadow) {
2115 (void) mvwin(p->shadow, y + SHADOW_ROWS, x + SHADOW_COLS);
2124 draw_childs_shadow(p);
2129 #endif /* KEY_RESIZE */
2132 dlg_sub_window(WINDOW *parent, int height, int width, int y, int x)
2136 if ((win = subwin(parent, height, width, y, x)) == 0) {
2137 dlg_exiterr("Can't make sub-window at (%d,%d), size (%d,%d).\n",
2138 y, x, height, width);
2141 add_subwindow(parent, win);
2142 (void) keypad(win, TRUE);
2148 dlg_default_item(char **items, int llen)
2152 if (dialog_vars.default_item != 0) {
2154 while (*items != 0) {
2155 if (!strcmp(dialog_vars.default_item, *items)) {
2167 dlg_default_listitem(DIALOG_LISTITEM * items)
2171 if (dialog_vars.default_item != 0) {
2173 while (items->name != 0) {
2174 if (!strcmp(dialog_vars.default_item, items->name)) {
2186 * Draw the string for item_help
2189 dlg_item_help(const char *txt)
2191 if (USE_ITEM_HELP(txt)) {
2192 chtype attr = A_NORMAL;
2195 (void) wattrset(stdscr, itemhelp_attr);
2196 (void) wmove(stdscr, LINES - 1, 0);
2197 (void) wclrtoeol(stdscr);
2199 dlg_print_text(stdscr, txt, COLS - 1, &attr);
2200 if (itemhelp_attr & A_COLOR) {
2201 /* fill the remainder of the line with the window's attributes */
2202 getyx(stdscr, y, x);
2209 (void) wnoutrefresh(stdscr);
2213 #ifndef HAVE_STRCASECMP
2215 dlg_strcmp(const char *a, const char *b)
2222 if (isalpha(ac) && islower(ac))
2224 if (isalpha(bc) && islower(bc))
2227 if (ac == 0 || bc == 0 || cmp != 0)
2235 * Returns true if 'dst' points to a blank which follows another blank which
2236 * is not a leading blank on a line.
2239 trim_blank(char *base, char *dst)
2243 while (dst-- != base) {
2246 } else if (*dst != ' ') {
2256 * Change embedded "\n" substrings to '\n' characters and tabs to single
2257 * spaces. If there are no "\n"s, it will strip all extra spaces, for
2258 * justification. If it has "\n"'s, it will preserve extra spaces. If cr_wrap
2259 * is set, it will preserve '\n's.
2262 dlg_trim_string(char *s)
2267 int has_newlines = !dialog_vars.no_nl_expand && (strstr(s, "\\n") != 0);
2269 while (*p != '\0') {
2270 if (*p == TAB && !dialog_vars.nocollapse)
2273 if (has_newlines) { /* If prompt contains "\n" strings */
2274 if (*p == '\\' && *(p + 1) == 'n') {
2279 * Handle end of lines intelligently. If '\n' follows "\n"
2280 * then ignore the '\n'. This eliminates the need to escape
2281 * the '\n' character (no need to use "\n\").
2287 } else if (*p == '\n') {
2288 if (dialog_vars.cr_wrap)
2291 /* Replace the '\n' with a space if cr_wrap is not set */
2292 if (!trim_blank(base, s))
2296 } else /* If *p != '\n' */
2298 } else if (dialog_vars.trim_whitespace) {
2300 if (*(s - 1) != ' ') {
2305 } else if (*p == '\n') {
2306 if (dialog_vars.cr_wrap)
2308 else if (*(s - 1) != ' ') {
2309 /* Strip '\n's if cr_wrap is not set. */
2316 } else { /* If there are no "\n" strings */
2317 if (*p == ' ' && !dialog_vars.nocollapse) {
2318 if (!trim_blank(base, s))
2330 dlg_set_focus(WINDOW *parent, WINDOW *win)
2333 (void) wmove(parent,
2334 getpary(win) + getcury(win),
2335 getparx(win) + getcurx(win));
2336 (void) wnoutrefresh(win);
2342 * Returns the nominal maximum buffer size.
2345 dlg_max_input(int max_len)
2347 if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN)
2348 max_len = dialog_vars.max_input;
2354 * Free storage used for the result buffer.
2357 dlg_clr_result(void)
2359 if (dialog_vars.input_length) {
2360 dialog_vars.input_length = 0;
2361 if (dialog_vars.input_result)
2362 free(dialog_vars.input_result);
2364 dialog_vars.input_result = 0;
2368 * Setup a fixed-buffer for the result.
2371 dlg_set_result(const char *string)
2373 unsigned need = string ? (unsigned) strlen(string) + 1 : 0;
2375 /* inputstr.c needs a fixed buffer */
2380 * If the buffer is not big enough, allocate a new one.
2382 if (dialog_vars.input_length != 0
2383 || dialog_vars.input_result == 0
2384 || need > MAX_LEN) {
2388 dialog_vars.input_length = need;
2389 dialog_vars.input_result = dlg_malloc(char, need);
2390 assert_ptr(dialog_vars.input_result, "dlg_set_result");
2393 strcpy(dialog_vars.input_result, string ? string : "");
2395 return dialog_vars.input_result;
2399 * Accumulate results in dynamically allocated buffer.
2400 * If input_length is zero, it is a MAX_LEN buffer belonging to the caller.
2403 dlg_add_result(const char *string)
2405 unsigned have = (dialog_vars.input_result
2406 ? (unsigned) strlen(dialog_vars.input_result)
2408 unsigned want = (unsigned) strlen(string) + 1 + have;
2410 if ((want >= MAX_LEN)
2411 || (dialog_vars.input_length != 0)
2412 || (dialog_vars.input_result == 0)) {
2414 if (dialog_vars.input_length == 0
2415 || dialog_vars.input_result == 0) {
2417 char *save_result = dialog_vars.input_result;
2419 dialog_vars.input_length = want * 2;
2420 dialog_vars.input_result = dlg_malloc(char, dialog_vars.input_length);
2421 assert_ptr(dialog_vars.input_result, "dlg_add_result malloc");
2422 dialog_vars.input_result[0] = '\0';
2423 if (save_result != 0)
2424 strcpy(dialog_vars.input_result, save_result);
2425 } else if (want >= dialog_vars.input_length) {
2426 dialog_vars.input_length = want * 2;
2427 dialog_vars.input_result = dlg_realloc(char,
2428 dialog_vars.input_length,
2429 dialog_vars.input_result);
2430 assert_ptr(dialog_vars.input_result, "dlg_add_result realloc");
2433 strcat(dialog_vars.input_result, string);
2437 * These are characters that (aside from the quote-delimiter) will have to
2438 * be escaped in a single- or double-quoted string.
2440 #define FIX_SINGLE "\n\\"
2441 #define FIX_DOUBLE FIX_SINGLE "[]{}?*;`~#$^&()|<>"
2444 * Returns the quote-delimiter.
2447 quote_delimiter(void)
2449 return dialog_vars.single_quoted ? "'" : "\"";
2453 * Returns true if we should quote the given string.
2456 must_quote(char *string)
2460 if (*string != '\0') {
2461 size_t len = strlen(string);
2462 if (strcspn(string, quote_delimiter()) != len)
2464 else if (strcspn(string, "\n\t ") != len)
2467 code = (strcspn(string, FIX_DOUBLE) != len);
2476 * Add a quoted string to the result buffer.
2479 dlg_add_quoted(char *string)
2482 const char *my_quote = quote_delimiter();
2483 const char *must_fix = (dialog_vars.single_quoted
2487 if (must_quote(string)) {
2489 dlg_add_result(my_quote);
2490 while (*string != '\0') {
2491 temp[0] = *string++;
2492 if (strchr(my_quote, *temp) || strchr(must_fix, *temp))
2493 dlg_add_result("\\");
2494 dlg_add_result(temp);
2496 dlg_add_result(my_quote);
2498 dlg_add_result(string);
2503 * When adding a result, make that depend on whether "--quoted" is used.
2506 dlg_add_string(char *string)
2508 if (dialog_vars.quoted) {
2509 dlg_add_quoted(string);
2511 dlg_add_result(string);
2516 dlg_need_separator(void)
2518 bool result = FALSE;
2520 if (dialog_vars.output_separator) {
2522 } else if (dialog_vars.input_result && *(dialog_vars.input_result)) {
2529 dlg_add_separator(void)
2531 const char *separator = (dialog_vars.separate_output) ? "\n" : " ";
2533 if (dialog_vars.output_separator)
2534 separator = dialog_vars.output_separator;
2536 dlg_add_result(separator);
2540 * Some widgets support only one value of a given variable - save/restore the
2541 * global dialog_vars so we can override it consistently.
2544 dlg_save_vars(DIALOG_VARS * vars)
2546 *vars = dialog_vars;
2550 * Most of the data in DIALOG_VARS is normally set by command-line options.
2551 * The input_result member is an exception; it is normally set by the dialog
2552 * library to return result values.
2555 dlg_restore_vars(DIALOG_VARS * vars)
2557 char *save_result = dialog_vars.input_result;
2558 unsigned save_length = dialog_vars.input_length;
2560 dialog_vars = *vars;
2561 dialog_vars.input_result = save_result;
2562 dialog_vars.input_length = save_length;
2566 * Called each time a widget is invoked which may do output, increment a count.
2569 dlg_does_output(void)
2571 dialog_state.output_count += 1;
2575 * Compatibility for different versions of curses.
2577 #if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY))
2579 dlg_getbegx(WINDOW *win)
2582 getbegyx(win, y, x);
2586 dlg_getbegy(WINDOW *win)
2589 getbegyx(win, y, x);
2594 #if !(defined(HAVE_GETCURX) && defined(HAVE_GETCURY))
2596 dlg_getcurx(WINDOW *win)
2603 dlg_getcury(WINDOW *win)
2611 #if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY))
2613 dlg_getmaxx(WINDOW *win)
2616 getmaxyx(win, y, x);
2620 dlg_getmaxy(WINDOW *win)
2623 getmaxyx(win, y, x);
2628 #if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY))
2630 dlg_getparx(WINDOW *win)
2633 getparyx(win, y, x);
2637 dlg_getpary(WINDOW *win)
2640 getparyx(win, y, x);
2645 #ifdef NEED_WGETPARENT
2647 dlg_wgetparent(WINDOW *win)
2653 for (p = dialog_state.all_subwindows; p != 0; p = p->next) {
2654 if (p->shadow == win) {