2 * $Id: util.c,v 1.260 2014/09/01 17:01:01 tom Exp $
4 * util.c -- miscellaneous utilities for dialog
6 * Copyright 2000-2013,2014 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
);
274 #ifdef NCURSES_VERSION
279 int fd
= fileno(dialog_state
.screen_output
);
281 buffer
[0] = (char) ch
;
282 return (int) write(fd
, buffer
, (size_t) 1);
287 * Do some initialization for dialog.
289 * 'input' is the real tty input of dialog. Usually it is stdin, but if
290 * --input-fd option is used, it may be anything.
292 * 'output' is where dialog will send its result. Usually it is stderr, but
293 * if --stdout or --output-fd is used, it may be anything. We are concerned
294 * mainly with the case where it happens to be the same as stdout.
297 init_dialog(FILE *input
, FILE *output
)
302 setlocale(LC_ALL
, "");
304 dialog_state
.output
= output
;
305 dialog_state
.tab_len
= TAB_LEN
;
306 dialog_state
.aspect_ratio
= DEFAULT_ASPECT_RATIO
;
308 dialog_state
.use_colors
= USE_COLORS
; /* use colors by default? */
309 dialog_state
.use_shadow
= USE_SHADOW
; /* shadow dialog boxes by default? */
313 if (dlg_parse_rc() == -1) /* Read the configuration file */
314 dlg_exiterr("init_dialog: dlg_parse_rc");
318 * Some widgets (such as gauge) may read from the standard input. Pipes
319 * only connect stdout/stdin, so there is not much choice. But reading a
320 * pipe would get in the way of curses' normal reading stdin for getch.
322 * As in the --stdout (see below), reopening the terminal does not always
323 * work properly. dialog provides a --pipe-fd option for this purpose. We
324 * test that case first (differing fileno's for input/stdin). If the
325 * fileno's are equal, but we're not reading from a tty, see if we can open
328 dialog_state
.pipe_input
= stdin
;
329 if (fileno(input
) != fileno(stdin
)) {
330 if ((fd1
= dup(fileno(input
))) >= 0
331 && (fd2
= dup(fileno(stdin
))) >= 0) {
332 (void) dup2(fileno(input
), fileno(stdin
));
333 dialog_state
.pipe_input
= fdopen(fd2
, "r");
334 if (fileno(stdin
) != 0) /* some functions may read fd #0 */
335 (void) dup2(fileno(stdin
), 0);
337 dlg_exiterr("cannot open tty-input");
340 } else if (!isatty(fileno(stdin
))) {
341 if ((fd1
= open_terminal(&device
, O_RDONLY
)) >= 0) {
342 if ((fd2
= dup(fileno(stdin
))) >= 0) {
343 dialog_state
.pipe_input
= fdopen(fd2
, "r");
344 if (freopen(device
, "r", stdin
) == 0)
345 dlg_exiterr("cannot open tty-input");
346 if (fileno(stdin
) != 0) /* some functions may read fd #0 */
347 (void) dup2(fileno(stdin
), 0);
355 * If stdout is not a tty and dialog is called with the --stdout option, we
356 * have to provide for a way to write to the screen.
358 * The curses library normally writes its output to stdout, leaving stderr
359 * free for scripting. Scripts are simpler when stdout is redirected. The
360 * newterm function is useful; it allows us to specify where the output
361 * goes. Reopening the terminal is not portable since several
362 * configurations do not allow this to work properly:
364 * a) some getty implementations (and possibly broken tty drivers, e.g., on
365 * HPUX 10 and 11) cause stdin to act as if it is still in cooked mode
366 * even though results from ioctl's state that it is successfully
367 * altered to raw mode. Broken is the proper term.
369 * b) the user may not have permissions on the device, e.g., if one su's
370 * from the login user to another non-privileged user.
372 if (!isatty(fileno(stdout
))
373 && (fileno(stdout
) == fileno(output
) || dialog_tty())) {
374 if ((fd1
= open_terminal(&device
, O_WRONLY
)) >= 0
375 && (dialog_state
.screen_output
= fdopen(fd1
, "w")) != 0) {
376 if (newterm(NULL
, dialog_state
.screen_output
, stdin
) == 0) {
377 dlg_exiterr("cannot initialize curses");
381 dlg_exiterr("cannot open tty-output");
384 dialog_state
.screen_output
= stdout
;
387 #ifdef NCURSES_VERSION
389 * Cancel xterm's alternate-screen mode.
391 if (!dialog_vars
.keep_tite
392 && (fileno(dialog_state
.screen_output
) != fileno(stdout
)
393 || isatty(fileno(dialog_state
.screen_output
)))
394 && key_mouse
!= 0 /* xterm and kindred */
395 && isprivate(enter_ca_mode
)
396 && isprivate(exit_ca_mode
)) {
398 * initscr() or newterm() already wrote enter_ca_mode as a side
399 * effect of initializing the screen. It would be nice to not even
400 * do that, but we do not really have access to the correct copy of
401 * the terminfo description until those functions have been invoked.
404 (void) tputs(exit_ca_mode
, 0, my_putc
);
405 (void) tputs(clear_screen
, 0, my_putc
);
407 * Prevent ncurses from switching "back" to the normal screen when
408 * exiting from dialog. That would move the cursor to the original
409 * location saved in xterm. Normally curses sets the cursor position
410 * to the first line after the display, but the alternate screen
411 * switching is done after that point.
413 * Cancelling the strings altogether also works around the buggy
414 * implementation of alternate-screen in rxvt, etc., which clear
415 * more of the display than they should.
424 (void) keypad(stdscr
, TRUE
);
428 if (!dialog_state
.no_mouse
) {
432 dialog_state
.screen_initialized
= TRUE
;
435 if (dialog_state
.use_colors
|| dialog_state
.use_shadow
)
436 dlg_color_setup(); /* Set up colors */
439 /* Set screen to screen attribute */
444 static int defined_colors
= 1; /* pair-0 is reserved */
446 * Setup for color display
449 dlg_color_setup(void)
453 if (has_colors()) { /* Terminal supports color? */
454 (void) start_color();
456 #if defined(HAVE_USE_DEFAULT_COLORS)
457 use_default_colors();
460 #if defined(__NetBSD__) && defined(_CURSES_)
461 #define C_ATTR(x,y) (((x) != 0 ? A_BOLD : 0) | COLOR_PAIR((y)))
462 /* work around bug in NetBSD curses */
463 for (i
= 0; i
< sizeof(dlg_color_table
) /
464 sizeof(dlg_color_table
[0]); i
++) {
466 /* Initialize color pairs */
467 (void) init_pair(i
+ 1,
468 dlg_color_table
[i
].fg
,
469 dlg_color_table
[i
].bg
);
471 /* Setup color attributes */
472 dlg_color_table
[i
].atr
= C_ATTR(dlg_color_table
[i
].hilite
, i
+ 1);
474 defined_colors
= i
+ 1;
476 for (i
= 0; i
< sizeof(dlg_color_table
) /
477 sizeof(dlg_color_table
[0]); i
++) {
479 /* Initialize color pairs */
480 chtype color
= dlg_color_pair(dlg_color_table
[i
].fg
,
481 dlg_color_table
[i
].bg
);
483 /* Setup color attributes */
484 dlg_color_table
[i
].atr
= ((dlg_color_table
[i
].hilite
491 dialog_state
.use_colors
= FALSE
;
492 dialog_state
.use_shadow
= FALSE
;
497 dlg_color_count(void)
499 return sizeof(dlg_color_table
) / sizeof(dlg_color_table
[0]);
503 * Wrapper for getattrs(), or the more cumbersome X/Open wattr_get().
506 dlg_get_attrs(WINDOW
*win
)
510 result
= (chtype
) getattrs(win
);
514 wattr_get(win
, &my_result
, &my_pair
, NULL
);
521 * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
522 * have (or can) define a pair with the given color as foreground on the
523 * window's defined background.
526 dlg_color_pair(int foreground
, int background
)
533 for (pair
= 1; pair
< defined_colors
; ++pair
) {
534 if (pair_content((short) pair
, &fg
, &bg
) != ERR
536 && bg
== background
) {
537 result
= (chtype
) COLOR_PAIR(pair
);
542 if (!found
&& (defined_colors
+ 1) < COLOR_PAIRS
) {
543 pair
= defined_colors
++;
544 (void) init_pair((short) pair
, (short) foreground
, (short) background
);
545 result
= (chtype
) COLOR_PAIR(pair
);
551 * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
552 * have (or can) define a pair with the given color as foreground on the
553 * window's defined background.
556 define_color(WINDOW
*win
, int foreground
)
558 chtype attrs
= dlg_get_attrs(win
);
560 short fg
, bg
, background
;
562 if ((pair
= PAIR_NUMBER(attrs
)) != 0
563 && pair_content((short) pair
, &fg
, &bg
) != ERR
) {
566 background
= COLOR_BLACK
;
568 return dlg_color_pair(foreground
, background
);
573 * End using dialog functions.
578 if (dialog_state
.screen_initialized
) {
579 dialog_state
.screen_initialized
= FALSE
;
582 (void) fflush(stdout
);
587 #define isOurEscape(p) (((p)[0] == '\\') && ((p)[1] == 'Z') && ((p)[2] != 0))
590 dlg_count_real_columns(const char *text
)
594 result
= dlg_count_columns(text
);
595 if (result
&& dialog_vars
.colors
) {
598 if (dialog_vars
.colors
&& isOurEscape(text
)) {
599 hidden
+= ESCAPE_LEN
;
612 centered(int width
, const char *string
)
614 int need
= dlg_count_real_columns(string
);
617 left
= (width
- need
) / 2 - 1;
623 #ifdef USE_WIDE_CURSES
625 is_combining(const char *txt
, int *combined
)
629 if (*combined
== 0) {
630 if (UCH(*txt
) >= 128) {
633 size_t given
= strlen(txt
);
636 memset(&state
, 0, sizeof(state
));
637 len
= mbrtowc(&wch
, txt
, given
, &state
);
638 if ((int) len
> 0 && wcwidth(wch
) == 0) {
639 *combined
= (int) len
- 1;
652 * Print the name (tag) or text from a DIALOG_LISTITEM, highlighting the
653 * first character if selected.
656 dlg_print_listitem(WINDOW
*win
,
662 chtype attr
= A_NORMAL
;
671 const int *indx
= dlg_index_wchars(text
);
672 attrs
[3] = tag_key_selected_attr
;
673 attrs
[2] = tag_key_attr
;
674 attrs
[1] = tag_selected_attr
;
677 (void) wattrset(win
, selected
? attrs
[3] : attrs
[2]);
678 (void) waddnstr(win
, text
, indx
[1]);
680 if ((int) strlen(text
) > indx
[1]) {
681 limit
= dlg_limit_columns(text
, climit
, 1);
683 (void) wattrset(win
, selected
? attrs
[1] : attrs
[0]);
686 indx
[limit
] - indx
[1]);
690 attrs
[1] = item_selected_attr
;
691 attrs
[0] = item_attr
;
693 cols
= dlg_index_columns(text
);
694 limit
= dlg_limit_columns(text
, climit
, 0);
697 (void) wattrset(win
, selected
? attrs
[1] : attrs
[0]);
698 dlg_print_text(win
, text
, cols
[limit
], &attr
);
704 * Print up to 'cols' columns from 'text', optionally rendering our escape
705 * sequence for attributes and color.
708 dlg_print_text(WINDOW
*win
, const char *txt
, int cols
, chtype
*attr
)
710 int y_origin
, x_origin
;
711 int y_before
, x_before
= 0;
712 int y_after
, x_after
;
717 #ifdef USE_WIDE_CURSES
721 getyx(win
, y_origin
, x_origin
);
722 while (cols
> 0 && (*txt
!= '\0')) {
723 if (dialog_vars
.colors
) {
724 while (isOurEscape(txt
)) {
728 switch (code
= CharOf(*txt
)) {
739 *attr
|= define_color(win
, code
- '0');
755 *attr
&= ~A_UNDERLINE
;
758 *attr
|= A_UNDERLINE
;
767 if (ended
|| *txt
== '\n' || *txt
== '\0')
769 useattr
= (*attr
) & A_ATTRIBUTES
;
772 * Prevent this from making text invisible when the foreground and
773 * background colors happen to be the same, and there's no bold
776 if ((useattr
& A_COLOR
) != 0 && (useattr
& A_BOLD
) == 0) {
777 short pair
= (short) PAIR_NUMBER(useattr
);
779 if (pair_content(pair
, &fg
, &bg
) != ERR
782 useattr
|= dlg_color_pair(fg
, ((bg
== COLOR_BLACK
)
789 * Write the character, using curses to tell exactly how wide it
790 * is. If it is a tab, discount that, since the caller thinks
791 * tabs are nonprinting, and curses will expand tabs to one or
794 thisTab
= (CharOf(*txt
) == TAB
);
796 getyx(win
, y_before
, x_before
);
799 (void) waddch(win
, CharOf(*txt
++) | useattr
);
800 getyx(win
, y_after
, x_after
);
801 if (thisTab
&& (y_after
== y_origin
))
802 tabbed
+= (x_after
- x_before
);
803 if ((y_after
!= y_origin
) ||
804 (x_after
>= (cols
+ tabbed
+ x_origin
)
805 #ifdef USE_WIDE_CURSES
806 && !is_combining(txt
, &combined
)
815 * Print one line of the prompt in the window within the limits of the
816 * specified right margin. The line will end on a word boundary and a pointer
817 * to the start of the next line is returned, or a NULL pointer if the end of
818 * *prompt is reached.
821 dlg_print_line(WINDOW
*win
,
824 int lm
, int rm
, int *x
)
826 const char *wrap_ptr
;
827 const char *test_ptr
;
828 const char *hide_ptr
= 0;
829 const int *cols
= dlg_index_columns(prompt
);
830 const int *indx
= dlg_index_wchars(prompt
);
835 int limit
= dlg_count_wchars(prompt
);
842 * Set *test_ptr to the end of the line or the right margin (rm), whichever
843 * is less, and set wrap_ptr to the end of the last word in the line.
845 for (n
= 0; n
< limit
; ++n
) {
846 test_ptr
= prompt
+ indx
[test_inx
];
847 if (*test_ptr
== '\n' || *test_ptr
== '\0' || cur_x
>= (rm
+ hidden
))
849 if (*test_ptr
== TAB
&& n
== 0) {
850 tabbed
= 8; /* workaround for leading tabs */
851 } else if (*test_ptr
== ' ' && n
!= 0 && prompt
[indx
[n
- 1]] != ' ') {
854 } else if (dialog_vars
.colors
&& isOurEscape(test_ptr
)) {
856 hidden
+= ESCAPE_LEN
;
857 n
+= (ESCAPE_LEN
- 1);
859 cur_x
= lm
+ tabbed
+ cols
[n
+ 1];
860 if (cur_x
> (rm
+ hidden
))
866 * If the line doesn't reach the right margin in the middle of a word, then
867 * we don't have to wrap it at the end of the previous word.
869 test_ptr
= prompt
+ indx
[test_inx
];
870 if (*test_ptr
== '\n' || *test_ptr
== ' ' || *test_ptr
== '\0') {
872 while (wrap_inx
> 0 && prompt
[indx
[wrap_inx
- 1]] == ' ') {
875 *x
= lm
+ indx
[wrap_inx
];
876 } else if (*x
== 1 && cur_x
>= rm
) {
878 * If the line has no spaces, then wrap it anyway at the right margin
883 wrap_ptr
= prompt
+ indx
[wrap_inx
];
884 #ifdef USE_WIDE_CURSES
885 if (UCH(*wrap_ptr
) >= 128) {
887 while (is_combining(wrap_ptr
, &combined
)) {
894 * If we found hidden text past the last point that we will display,
895 * discount that from the displayed length.
897 if ((hide_ptr
!= 0) && (hide_ptr
>= wrap_ptr
)) {
898 hidden
-= ESCAPE_LEN
;
900 while (test_ptr
< wrap_ptr
) {
901 if (dialog_vars
.colors
&& isOurEscape(test_ptr
)) {
902 hidden
-= ESCAPE_LEN
;
903 test_ptr
+= ESCAPE_LEN
;
911 * Print the line if we have a window pointer. Otherwise this routine
912 * is just being called for sizing the window.
915 dlg_print_text(win
, prompt
, (cols
[wrap_inx
] - hidden
), attr
);
918 /* *x tells the calling function how long the line was */
924 /* Find the start of the next line and return a pointer to it */
926 while (*test_ptr
== ' ')
928 if (*test_ptr
== '\n')
930 dlg_finish_string(prompt
);
935 justify_text(WINDOW
*win
,
939 int *high
, int *wide
)
941 chtype attr
= A_NORMAL
;
942 int x
= (2 * MARGIN
);
945 int lm
= (2 * MARGIN
); /* left margin (box-border plus a space) */
946 int rm
= limit_x
; /* right margin */
947 int bm
= limit_y
; /* bottom margin */
948 int last_y
= 0, last_x
= 0;
958 getyx(win
, last_y
, last_x
);
959 while (y
<= bm
&& *prompt
) {
962 if (*prompt
== '\n') {
963 while (*prompt
== '\n' && y
< bm
) {
964 if (*(prompt
+ 1) != '\0') {
967 (void) wmove(win
, y
, lm
);
972 (void) wmove(win
, y
, lm
);
975 prompt
= dlg_print_line(win
, &attr
, prompt
, lm
, rm
, &x
);
977 getyx(win
, last_y
, last_x
);
982 (void) wmove(win
, y
, lm
);
984 max_x
= MAX(max_x
, x
);
986 /* Move back to the last position after drawing prompt, for msgbox. */
988 (void) wmove(win
, last_y
, last_x
);
990 /* Set the final height and width for the calling function */
998 * Print a string of text in a window, automatically wrap around to the next
999 * line if the string is too long to fit on one line. Note that the string may
1000 * contain embedded newlines.
1003 dlg_print_autowrap(WINDOW
*win
, const char *prompt
, int height
, int width
)
1005 justify_text(win
, prompt
,
1008 (int *) 0, (int *) 0);
1012 * Display the message in a scrollable window. Actually the way it works is
1013 * that we create a "tall" window of the proper width, let the text wrap within
1014 * that, and copy a slice of the result to the dialog.
1016 * It works for ncurses. Other curses implementations show only blanks (Tru64)
1017 * or garbage (NetBSD).
1020 dlg_print_scrolled(WINDOW
*win
,
1030 (void) pauseopt
; /* used only for ncurses */
1032 getyx(win
, oldy
, oldx
);
1033 #ifdef NCURSES_VERSION
1035 int wide
= width
- (2 * MARGIN
);
1043 #if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20040417
1045 * If we're not limited by the screensize, allow text to possibly be
1046 * one character per line.
1048 if ((len
= dlg_count_columns(prompt
)) > high
)
1051 dummy
= newwin(high
, width
, 0, 0);
1053 (void) wattrset(win
, dialog_attr
);
1054 dlg_print_autowrap(win
, prompt
, height
+ 1 + (3 * MARGIN
), width
);
1057 wbkgdset(dummy
, dialog_attr
| ' ');
1058 (void) wattrset(dummy
, dialog_attr
);
1060 dlg_print_autowrap(dummy
, prompt
, high
, width
);
1064 copywin(dummy
, /* srcwin */
1066 offset
+ MARGIN
, /* sminrow */
1067 MARGIN
, /* smincol */
1068 MARGIN
, /* dminrow */
1069 MARGIN
, /* dmincol */
1070 height
, /* dmaxrow */
1076 /* if the text is incomplete, or we have scrolled, show the percentage */
1077 if (y
> 0 && wide
> 4) {
1078 percent
= (int) ((height
+ offset
) * 100.0 / y
);
1083 if (offset
!= 0 || percent
!= 100) {
1084 (void) wattrset(win
, position_indicator_attr
);
1085 (void) wmove(win
, MARGIN
+ height
, wide
- 4);
1086 (void) sprintf(buffer
, "%d%%", percent
);
1087 (void) waddstr(win
, buffer
);
1088 if ((len
= (int) strlen(buffer
)) < 4) {
1089 (void) wattrset(win
, border_attr
);
1090 whline(win
, dlg_boxchar(ACS_HLINE
), 4 - len
);
1094 last
= (y
- height
);
1100 (void) wattrset(win
, dialog_attr
);
1101 dlg_print_autowrap(win
, prompt
, height
+ 1 + (3 * MARGIN
), width
);
1104 wmove(win
, oldy
, oldx
);
1109 dlg_check_scrolled(int key
, int last
, int page
, bool * show
, int *offset
)
1116 case DLGK_PAGE_FIRST
:
1122 case DLGK_PAGE_LAST
:
1123 if (*offset
< last
) {
1134 case DLGK_GRID_DOWN
:
1135 if (*offset
< last
) {
1140 case DLGK_PAGE_PREV
:
1148 case DLGK_PAGE_NEXT
:
1149 if (*offset
< last
) {
1164 * Calculate the window size for preformatted text. This will calculate box
1165 * dimensions that are at or close to the specified aspect ratio for the prompt
1166 * string with all spaces and newlines preserved and additional newlines added
1170 auto_size_preformatted(const char *prompt
, int *height
, int *width
)
1172 int high
= 0, wide
= 0;
1173 float car
; /* Calculated Aspect Ratio */
1175 int max_y
= SLINES
- 1;
1176 int max_x
= SCOLS
- 2;
1177 int max_width
= max_x
;
1178 int ar
= dialog_state
.aspect_ratio
;
1180 /* Get the initial dimensions */
1181 justify_text((WINDOW
*) 0, prompt
, max_y
, max_x
, &high
, &wide
);
1182 car
= (float) (wide
/ high
);
1185 * If the aspect ratio is greater than it should be, then decrease the
1186 * width proportionately.
1189 diff
= car
/ (float) ar
;
1190 max_x
= (int) ((float) wide
/ diff
+ 4);
1191 justify_text((WINDOW
*) 0, prompt
, max_y
, max_x
, &high
, &wide
);
1192 car
= (float) wide
/ (float) high
;
1196 * If the aspect ratio is too small after decreasing the width, then
1197 * incrementally increase the width until the aspect ratio is equal to or
1198 * greater than the specified aspect ratio.
1200 while (car
< ar
&& max_x
< max_width
) {
1202 justify_text((WINDOW
*) 0, prompt
, max_y
, max_x
, &high
, &wide
);
1203 car
= (float) (wide
/ high
);
1211 * Find the length of the longest "word" in the given string. By setting the
1212 * widget width at least this long, we can avoid splitting a word on the
1216 longest_word(const char *string
)
1218 int length
, result
= 0;
1220 while (*string
!= '\0') {
1222 while (*string
!= '\0' && !isspace(UCH(*string
))) {
1226 result
= MAX(result
, length
);
1227 if (*string
!= '\0')
1234 * if (height or width == -1) Maximize()
1235 * if (height or width == 0), justify and return actual limits.
1238 real_auto_size(const char *title
,
1240 int *height
, int *width
,
1241 int boxlines
, int mincols
)
1243 int x
= (dialog_vars
.begin_set
? dialog_vars
.begin_x
: 2);
1244 int y
= (dialog_vars
.begin_set
? dialog_vars
.begin_y
: 1);
1245 int title_length
= title
? dlg_count_columns(title
) : 0;
1249 int save_high
= *height
;
1250 int save_wide
= *width
;
1267 wide
= MAX(title_length
, mincols
);
1268 if (strchr(prompt
, '\n') == 0) {
1269 double val
= (dialog_state
.aspect_ratio
*
1270 dlg_count_real_columns(prompt
));
1271 double xxx
= sqrt(val
);
1272 int tmp
= (int) xxx
;
1273 wide
= MAX(wide
, tmp
);
1274 wide
= MAX(wide
, longest_word(prompt
));
1275 justify_text((WINDOW
*) 0, prompt
, high
, wide
, height
, width
);
1277 auto_size_preformatted(prompt
, height
, width
);
1281 justify_text((WINDOW
*) 0, prompt
, high
, wide
, height
, width
);
1285 if (*width
< title_length
) {
1286 justify_text((WINDOW
*) 0, prompt
, high
, title_length
, height
, width
);
1287 *width
= title_length
;
1290 if (*width
< mincols
&& save_wide
== 0)
1294 *height
+= boxlines
+ 2;
1297 *height
= save_high
;
1302 /* End of real_auto_size() */
1305 dlg_auto_size(const char *title
,
1312 real_auto_size(title
, prompt
, height
, width
, boxlines
, mincols
);
1314 if (*width
> SCOLS
) {
1319 if (*height
> SLINES
)
1324 * if (height or width == -1) Maximize()
1325 * if (height or width == 0)
1326 * height=MIN(SLINES, num.lines in fd+n);
1327 * width=MIN(SCOLS, MAX(longer line+n, mincols));
1330 dlg_auto_sizefile(const char *title
,
1338 int len
= title
? dlg_count_columns(title
) : 0;
1345 /* Open input file for reading */
1346 if ((fd
= fopen(file
, "rb")) == NULL
)
1347 dlg_exiterr("dlg_auto_sizefile: Cannot open input file %s", file
);
1349 if ((*height
== -1) || (*width
== -1)) {
1350 *height
= SLINES
- (dialog_vars
.begin_set
? dialog_vars
.begin_y
: 0);
1351 *width
= SCOLS
- (dialog_vars
.begin_set
? dialog_vars
.begin_x
: 0);
1353 if ((*height
!= 0) && (*width
!= 0)) {
1357 if (*height
> SLINES
)
1364 while (((ch
= getc(fd
)) != '\n') && !feof(fd
))
1365 if ((ch
== TAB
) && (dialog_vars
.tab_correct
))
1366 offset
+= dialog_state
.tab_len
- (offset
% dialog_state
.tab_len
);
1376 /* now 'count' has the number of lines of fd and 'len' the max length */
1378 *height
= MIN(SLINES
, count
+ numlines
+ boxlines
);
1379 *width
= MIN(SCOLS
, MAX((len
+ nc
), mincols
));
1380 /* here width and height can be maximized if > SCOLS|SLINES because
1381 textbox-like widgets don't put all <file> on the screen.
1382 Msgbox-like widget instead have to put all <text> correctly. */
1388 dlg_get_cell_attrs(WINDOW
*win
)
1391 #ifdef USE_WIDE_CURSES
1396 if (win_wch(win
, &wch
) == OK
1397 && getcchar(&wch
, &cc
, &attrs
, &pair
, NULL
) == OK
) {
1403 result
= winch(win
) & (A_ATTRIBUTES
& ~A_COLOR
);
1409 * Draw a rectangular box with line drawing characters.
1411 * borderchar is used to color the upper/left edges.
1413 * boxchar is used to color the right/lower edges. It also is fill-color used
1414 * for the box contents.
1416 * Normally, if you are drawing a scrollable box, use menubox_border_attr for
1417 * boxchar, and menubox_attr for borderchar since the scroll-arrows are drawn
1418 * with menubox_attr at the top, and menubox_border_attr at the bottom. That
1419 * also (given the default color choices) produces a recessed effect.
1421 * If you want a raised effect (and are not going to use the scroll-arrows),
1422 * reverse this choice.
1425 dlg_draw_box2(WINDOW
*win
, int y
, int x
, int height
, int width
,
1426 chtype boxchar
, chtype borderchar
, chtype borderchar2
)
1429 chtype save
= dlg_get_attrs(win
);
1431 (void) wattrset(win
, 0);
1432 for (i
= 0; i
< height
; i
++) {
1433 (void) wmove(win
, y
+ i
, x
);
1434 for (j
= 0; j
< width
; j
++)
1436 (void) waddch(win
, borderchar
| dlg_boxchar(ACS_ULCORNER
));
1437 else if (i
== height
- 1 && !j
)
1438 (void) waddch(win
, borderchar
| dlg_boxchar(ACS_LLCORNER
));
1439 else if (!i
&& j
== width
- 1)
1440 (void) waddch(win
, borderchar2
| dlg_boxchar(ACS_URCORNER
));
1441 else if (i
== height
- 1 && j
== width
- 1)
1442 (void) waddch(win
, borderchar2
| dlg_boxchar(ACS_LRCORNER
));
1444 (void) waddch(win
, borderchar
| dlg_boxchar(ACS_HLINE
));
1445 else if (i
== height
- 1)
1446 (void) waddch(win
, borderchar2
| dlg_boxchar(ACS_HLINE
));
1448 (void) waddch(win
, borderchar
| dlg_boxchar(ACS_VLINE
));
1449 else if (j
== width
- 1)
1450 (void) waddch(win
, borderchar2
| dlg_boxchar(ACS_VLINE
));
1452 (void) waddch(win
, boxchar
| ' ');
1454 (void) wattrset(win
, save
);
1458 dlg_draw_box(WINDOW
*win
, int y
, int x
, int height
, int width
,
1459 chtype boxchar
, chtype borderchar
)
1461 dlg_draw_box2(win
, y
, x
, height
, width
, boxchar
, borderchar
, boxchar
);
1464 static DIALOG_WINDOWS
*
1465 find_window(WINDOW
*win
)
1467 DIALOG_WINDOWS
*result
= 0;
1470 for (p
= dialog_state
.all_windows
; p
!= 0; p
= p
->next
) {
1471 if (p
->normal
== win
) {
1481 * If we have wchgat(), use that for updating shadow attributes, to work with
1482 * wide-character data.
1486 * Check if the given point is "in" the given window. If so, return the window
1487 * pointer, otherwise null.
1490 in_window(WINDOW
*win
, int y
, int x
)
1493 int y_base
= getbegy(win
);
1494 int x_base
= getbegx(win
);
1495 int y_last
= getmaxy(win
) + y_base
;
1496 int x_last
= getmaxx(win
) + x_base
;
1498 if (y
>= y_base
&& y
<= y_last
&& x
>= x_base
&& x
<= x_last
)
1504 window_at_cell(DIALOG_WINDOWS
* dw
, int y
, int x
)
1508 int y_want
= y
+ getbegy(dw
->shadow
);
1509 int x_want
= x
+ getbegx(dw
->shadow
);
1511 for (p
= dialog_state
.all_windows
; p
!= 0; p
= p
->next
) {
1512 if (dw
->normal
!= p
->normal
1513 && dw
->shadow
!= p
->normal
1514 && (result
= in_window(p
->normal
, y_want
, x_want
)) != 0) {
1525 in_shadow(WINDOW
*normal
, WINDOW
*shadow
, int y
, int x
)
1527 bool result
= FALSE
;
1528 int ybase
= getbegy(normal
);
1529 int ylast
= getmaxy(normal
) + ybase
;
1530 int xbase
= getbegx(normal
);
1531 int xlast
= getmaxx(normal
) + xbase
;
1533 y
+= getbegy(shadow
);
1534 x
+= getbegx(shadow
);
1536 if (y
>= ybase
+ SHADOW_ROWS
1537 && y
< ylast
+ SHADOW_ROWS
1539 && x
< xlast
+ SHADOW_COLS
) {
1540 /* in the right-side */
1542 } else if (y
>= ylast
1543 && y
< ylast
+ SHADOW_ROWS
1544 && x
>= ybase
+ SHADOW_COLS
1545 && x
< ylast
+ SHADOW_COLS
) {
1546 /* check the bottom */
1554 * When erasing a shadow, check each cell to make sure that it is not part of
1555 * another box's shadow. This is a little complicated since most shadows are
1556 * merged onto stdscr.
1559 last_shadow(DIALOG_WINDOWS
* dw
, int y
, int x
)
1564 for (p
= dialog_state
.all_windows
; p
!= 0; p
= p
->next
) {
1565 if (p
->normal
!= dw
->normal
1566 && in_shadow(p
->normal
, dw
->shadow
, y
, x
)) {
1575 repaint_cell(DIALOG_WINDOWS
* dw
, bool draw
, int y
, int x
)
1577 WINDOW
*win
= dw
->shadow
;
1581 if ((cellwin
= window_at_cell(dw
, y
, x
)) != 0
1582 && (draw
|| last_shadow(dw
, y
, x
))
1583 && (y2
= (y
+ getbegy(win
) - getbegy(cellwin
))) >= 0
1584 && (x2
= (x
+ getbegx(win
) - getbegx(cellwin
))) >= 0
1585 && wmove(cellwin
, y2
, x2
) != ERR
) {
1586 chtype the_cell
= dlg_get_attrs(cellwin
);
1587 chtype the_attr
= (draw
? shadow_attr
: the_cell
);
1589 if (dlg_get_cell_attrs(cellwin
) & A_ALTCHARSET
) {
1590 the_attr
|= A_ALTCHARSET
;
1594 the_attr
& (chtype
) (~A_COLOR
),
1595 (short) PAIR_NUMBER(the_attr
),
1599 chtype the_char
= ((winch(cellwin
) & A_CHARTEXT
) | the_attr
);
1600 (void) waddch(cellwin
, the_char
);
1603 wnoutrefresh(cellwin
);
1607 #define RepaintCell(dw, draw, y, x) repaint_cell(dw, draw, y, x)
1610 repaint_shadow(DIALOG_WINDOWS
* dw
, bool draw
, int y
, int x
, int height
, int width
)
1614 if (UseShadow(dw
)) {
1616 chtype save
= dlg_get_attrs(dw
->shadow
);
1617 (void) wattrset(dw
->shadow
, draw
? shadow_attr
: screen_attr
);
1619 for (i
= 0; i
< SHADOW_ROWS
; ++i
) {
1620 for (j
= 0; j
< width
; ++j
) {
1621 RepaintCell(dw
, draw
, i
+ y
+ height
, j
+ x
+ SHADOW_COLS
);
1624 for (i
= 0; i
< height
; i
++) {
1625 for (j
= 0; j
< SHADOW_COLS
; ++j
) {
1626 RepaintCell(dw
, draw
, i
+ y
+ SHADOW_ROWS
, j
+ x
+ width
);
1629 (void) wnoutrefresh(dw
->shadow
);
1631 (void) wattrset(dw
->shadow
, save
);
1637 * Draw a shadow on the parent window corresponding to the right- and
1638 * bottom-edge of the child window, to give a 3-dimensional look.
1641 draw_childs_shadow(DIALOG_WINDOWS
* dw
)
1643 if (UseShadow(dw
)) {
1646 getbegy(dw
->normal
) - getbegy(dw
->shadow
),
1647 getbegx(dw
->normal
) - getbegx(dw
->shadow
),
1648 getmaxy(dw
->normal
),
1649 getmaxx(dw
->normal
));
1654 * Erase a shadow on the parent window corresponding to the right- and
1655 * bottom-edge of the child window.
1658 erase_childs_shadow(DIALOG_WINDOWS
* dw
)
1660 if (UseShadow(dw
)) {
1663 getbegy(dw
->normal
) - getbegy(dw
->shadow
),
1664 getbegx(dw
->normal
) - getbegx(dw
->shadow
),
1665 getmaxy(dw
->normal
),
1666 getmaxx(dw
->normal
));
1671 * Draw shadows along the right and bottom edge to give a more 3D look
1675 dlg_draw_shadow(WINDOW
*win
, int y
, int x
, int height
, int width
)
1677 repaint_shadow(find_window(win
), TRUE
, y
, x
, height
, width
);
1679 #endif /* HAVE_COLOR */
1682 * Allow shell scripts to remap the exit codes so they can distinguish ESC
1689 static const struct {
1693 { DLG_EXIT_CANCEL
, "DIALOG_CANCEL" },
1694 { DLG_EXIT_ERROR
, "DIALOG_ERROR" },
1695 { DLG_EXIT_ESC
, "DIALOG_ESC" },
1696 { DLG_EXIT_EXTRA
, "DIALOG_EXTRA" },
1697 { DLG_EXIT_HELP
, "DIALOG_HELP" },
1698 { DLG_EXIT_OK
, "DIALOG_OK" },
1699 { DLG_EXIT_ITEM_HELP
, "DIALOG_ITEM_HELP" },
1707 bool overridden
= FALSE
;
1710 for (n
= 0; n
< sizeof(table
) / sizeof(table
[0]); n
++) {
1711 if (table
[n
].code
== code
) {
1712 if ((name
= getenv(table
[n
].name
)) != 0) {
1713 value
= strtol(name
, &temp
, 0);
1714 if (temp
!= 0 && temp
!= name
&& *temp
== '\0') {
1724 * Prior to 2004/12/19, a widget using --item-help would exit with "OK"
1725 * if the help button were selected. Now we want to exit with "HELP",
1726 * but allow the environment variable to override.
1728 if (code
== DLG_EXIT_ITEM_HELP
&& !overridden
) {
1729 code
= DLG_EXIT_HELP
;
1732 #ifdef HAVE_DLG_TRACE
1733 dlg_trace((const char *) 0); /* close it */
1737 _dlg_inputstr_leaks();
1738 #if defined(NCURSES_VERSION) && defined(HAVE__NC_FREE_AND_EXIT)
1739 _nc_free_and_exit(code
);
1743 if (dialog_state
.input
== stdin
) {
1747 * Just in case of using --input-fd option, do not
1748 * call atexit functions of ncurses which may hang.
1750 if (dialog_state
.input
) {
1751 fclose(dialog_state
.input
);
1752 dialog_state
.input
= 0;
1754 if (dialog_state
.pipe_input
) {
1755 if (dialog_state
.pipe_input
!= stdin
) {
1756 fclose(dialog_state
.pipe_input
);
1757 dialog_state
.pipe_input
= 0;
1764 /* quit program killing all tailbg */
1766 dlg_exiterr(const char *fmt
,...)
1773 (void) fputc('\n', stderr
);
1775 (void) vfprintf(stderr
, fmt
, ap
);
1777 (void) fputc('\n', stderr
);
1779 dlg_killall_bg(&retval
);
1781 (void) fflush(stderr
);
1782 (void) fflush(stdout
);
1783 dlg_exit(DLG_EXIT_ERROR
);
1789 if (dialog_vars
.beep_signal
) {
1791 dialog_vars
.beep_signal
= 0;
1796 dlg_print_size(int height
, int width
)
1798 if (dialog_vars
.print_siz
)
1799 fprintf(dialog_state
.output
, "Size: %d, %d\n", height
, width
);
1803 dlg_ctl_size(int height
, int width
)
1805 if (dialog_vars
.size_err
) {
1806 if ((width
> COLS
) || (height
> LINES
)) {
1807 dlg_exiterr("Window too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
1808 height
, width
, LINES
, COLS
);
1811 else if ((dialog_state
.use_shadow
)
1812 && ((width
> SCOLS
|| height
> SLINES
))) {
1813 if ((width
<= COLS
) && (height
<= LINES
)) {
1814 /* try again, without shadows */
1815 dialog_state
.use_shadow
= 0;
1817 dlg_exiterr("Window+Shadow too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
1818 height
, width
, SLINES
, SCOLS
);
1826 * If the --tab-correct was not selected, convert tabs to single spaces.
1829 dlg_tab_correct_str(char *prompt
)
1833 if (dialog_vars
.tab_correct
) {
1834 while ((ptr
= strchr(prompt
, TAB
)) != NULL
) {
1842 dlg_calc_listh(int *height
, int *list_height
, int item_no
)
1844 /* calculate new height and list_height */
1845 int rows
= SLINES
- (dialog_vars
.begin_set
? dialog_vars
.begin_y
: 0);
1846 if (rows
- (*height
) > 0) {
1847 if (rows
- (*height
) > item_no
)
1848 *list_height
= item_no
;
1850 *list_height
= rows
- (*height
);
1852 (*height
) += (*list_height
);
1857 dlg_calc_listw(int item_no
, char **items
, int group
)
1859 int n
, i
, len1
= 0, len2
= 0;
1860 for (i
= 0; i
< (item_no
* group
); i
+= group
) {
1861 if ((n
= dlg_count_columns(items
[i
])) > len1
)
1863 if ((n
= dlg_count_columns(items
[i
+ 1])) > len2
)
1870 dlg_calc_list_width(int item_no
, DIALOG_LISTITEM
* items
)
1872 int n
, i
, len1
= 0, len2
= 0;
1873 int bits
= ((dialog_vars
.no_tags
? 1 : 0)
1874 + (dialog_vars
.no_items
? 2 : 0));
1876 for (i
= 0; i
< item_no
; ++i
) {
1881 if ((n
= dlg_count_columns(items
[i
].name
)) > len1
)
1883 if ((n
= dlg_count_columns(items
[i
].text
)) > len2
)
1889 if ((n
= dlg_count_columns(items
[i
].name
)) > len1
)
1900 static char empty
[] = "";
1905 dlg_strclone(const char *cprompt
)
1909 prompt
= dlg_malloc(char, strlen(cprompt
) + 1);
1910 assert_ptr(prompt
, "dlg_strclone");
1911 strcpy(prompt
, cprompt
);
1917 dlg_asciibox(chtype ch
)
1921 if (ch
== ACS_ULCORNER
)
1923 else if (ch
== ACS_LLCORNER
)
1925 else if (ch
== ACS_URCORNER
)
1927 else if (ch
== ACS_LRCORNER
)
1929 else if (ch
== ACS_HLINE
)
1931 else if (ch
== ACS_VLINE
)
1933 else if (ch
== ACS_LTEE
)
1935 else if (ch
== ACS_RTEE
)
1937 else if (ch
== ACS_UARROW
)
1939 else if (ch
== ACS_DARROW
)
1946 dlg_boxchar(chtype ch
)
1948 chtype result
= dlg_asciibox(ch
);
1951 if (dialog_vars
.ascii_lines
)
1953 else if (dialog_vars
.no_lines
)
1960 dlg_box_x_ordinate(int width
)
1964 if (dialog_vars
.begin_set
== 1) {
1965 x
= dialog_vars
.begin_x
;
1967 /* center dialog box on screen unless --begin-set */
1968 x
= (SCOLS
- width
) / 2;
1974 dlg_box_y_ordinate(int height
)
1978 if (dialog_vars
.begin_set
== 1) {
1979 y
= dialog_vars
.begin_y
;
1981 /* center dialog box on screen unless --begin-set */
1982 y
= (SLINES
- height
) / 2;
1988 dlg_draw_title(WINDOW
*win
, const char *title
)
1990 if (title
!= NULL
) {
1991 chtype attr
= A_NORMAL
;
1992 chtype save
= dlg_get_attrs(win
);
1993 int x
= centered(getmaxx(win
), title
);
1995 (void) wattrset(win
, title_attr
);
1997 dlg_print_text(win
, title
, getmaxx(win
) - x
, &attr
);
1998 (void) wattrset(win
, save
);
1999 dlg_finish_string(title
);
2004 dlg_draw_bottom_box2(WINDOW
*win
, chtype on_left
, chtype on_right
, chtype on_inside
)
2006 int width
= getmaxx(win
);
2007 int height
= getmaxy(win
);
2010 (void) wattrset(win
, on_left
);
2011 (void) wmove(win
, height
- 3, 0);
2012 (void) waddch(win
, dlg_boxchar(ACS_LTEE
));
2013 for (i
= 0; i
< width
- 2; i
++)
2014 (void) waddch(win
, dlg_boxchar(ACS_HLINE
));
2015 (void) wattrset(win
, on_right
);
2016 (void) waddch(win
, dlg_boxchar(ACS_RTEE
));
2017 (void) wattrset(win
, on_inside
);
2018 (void) wmove(win
, height
- 2, 1);
2019 for (i
= 0; i
< width
- 2; i
++)
2020 (void) waddch(win
, ' ');
2024 dlg_draw_bottom_box(WINDOW
*win
)
2026 dlg_draw_bottom_box2(win
, border_attr
, dialog_attr
, dialog_attr
);
2030 * Remove a window, repainting everything else. This would be simpler if we
2031 * used the panel library, but that is not _always_ available.
2034 dlg_del_window(WINDOW
*win
)
2036 DIALOG_WINDOWS
*p
, *q
, *r
;
2039 * If --keep-window was set, do not delete/repaint the windows.
2041 if (dialog_vars
.keep_window
)
2044 /* Leave the main window untouched if there are no background windows.
2045 * We do this so the current window will not be cleared on exit, allowing
2046 * things like the infobox demo to run without flicker.
2048 if (dialog_state
.getc_callbacks
!= 0) {
2050 wnoutrefresh(stdscr
);
2053 for (p
= dialog_state
.all_windows
, q
= r
= 0; p
!= 0; r
= p
, p
= p
->next
) {
2054 if (p
->normal
== win
) {
2055 q
= p
; /* found a match - should be only one */
2057 dialog_state
.all_windows
= p
->next
;
2062 if (p
->shadow
!= 0) {
2063 touchwin(p
->shadow
);
2064 wnoutrefresh(p
->shadow
);
2066 touchwin(p
->normal
);
2067 wnoutrefresh(p
->normal
);
2072 if (dialog_state
.all_windows
!= 0)
2073 erase_childs_shadow(q
);
2074 del_subwindows(q
->normal
);
2075 dlg_unregister_window(q
->normal
);
2083 * Create a window, optionally with a shadow.
2086 dlg_new_window(int height
, int width
, int y
, int x
)
2088 return dlg_new_modal_window(stdscr
, height
, width
, y
, x
);
2092 * "Modal" windows differ from normal ones by having a shadow in a window
2093 * separate from the standard screen.
2096 dlg_new_modal_window(WINDOW
*parent
, int height
, int width
, int y
, int x
)
2099 DIALOG_WINDOWS
*p
= dlg_calloc(DIALOG_WINDOWS
, 1);
2103 || (win
= newwin(height
, width
, y
, x
)) == 0) {
2104 dlg_exiterr("Can't make new window at (%d,%d), size (%d,%d).\n",
2105 y
, x
, height
, width
);
2107 p
->next
= dialog_state
.all_windows
;
2109 dialog_state
.all_windows
= p
;
2111 if (dialog_state
.use_shadow
) {
2113 draw_childs_shadow(p
);
2117 (void) keypad(win
, TRUE
);
2122 * Move/Resize a window, optionally with a shadow.
2126 dlg_move_window(WINDOW
*win
, int height
, int width
, int y
, int x
)
2131 dlg_ctl_size(height
, width
);
2133 if ((p
= find_window(win
)) != 0) {
2134 (void) wresize(win
, height
, width
);
2135 (void) mvwin(win
, y
, x
);
2137 if (p
->shadow
!= 0) {
2138 if (dialog_state
.use_shadow
) {
2139 (void) mvwin(p
->shadow
, y
+ SHADOW_ROWS
, x
+ SHADOW_COLS
);
2148 draw_childs_shadow(p
);
2153 #endif /* KEY_RESIZE */
2156 dlg_sub_window(WINDOW
*parent
, int height
, int width
, int y
, int x
)
2160 if ((win
= subwin(parent
, height
, width
, y
, x
)) == 0) {
2161 dlg_exiterr("Can't make sub-window at (%d,%d), size (%d,%d).\n",
2162 y
, x
, height
, width
);
2165 add_subwindow(parent
, win
);
2166 (void) keypad(win
, TRUE
);
2172 dlg_default_item(char **items
, int llen
)
2176 if (dialog_vars
.default_item
!= 0) {
2178 while (*items
!= 0) {
2179 if (!strcmp(dialog_vars
.default_item
, *items
)) {
2191 dlg_default_listitem(DIALOG_LISTITEM
* items
)
2195 if (dialog_vars
.default_item
!= 0) {
2197 while (items
->name
!= 0) {
2198 if (!strcmp(dialog_vars
.default_item
, items
->name
)) {
2210 * Draw the string for item_help
2213 dlg_item_help(const char *txt
)
2215 if (USE_ITEM_HELP(txt
)) {
2216 chtype attr
= A_NORMAL
;
2219 (void) wattrset(stdscr
, itemhelp_attr
);
2220 (void) wmove(stdscr
, LINES
- 1, 0);
2221 (void) wclrtoeol(stdscr
);
2223 dlg_print_text(stdscr
, txt
, COLS
- 1, &attr
);
2224 if (itemhelp_attr
& A_COLOR
) {
2225 /* fill the remainder of the line with the window's attributes */
2226 getyx(stdscr
, y
, x
);
2233 (void) wnoutrefresh(stdscr
);
2237 #ifndef HAVE_STRCASECMP
2239 dlg_strcmp(const char *a
, const char *b
)
2246 if (isalpha(ac
) && islower(ac
))
2248 if (isalpha(bc
) && islower(bc
))
2251 if (ac
== 0 || bc
== 0 || cmp
!= 0)
2259 * Returns true if 'dst' points to a blank which follows another blank which
2260 * is not a leading blank on a line.
2263 trim_blank(char *base
, char *dst
)
2267 while (dst
-- != base
) {
2270 } else if (*dst
!= ' ') {
2280 * Change embedded "\n" substrings to '\n' characters and tabs to single
2281 * spaces. If there are no "\n"s, it will strip all extra spaces, for
2282 * justification. If it has "\n"'s, it will preserve extra spaces. If cr_wrap
2283 * is set, it will preserve '\n's.
2286 dlg_trim_string(char *s
)
2291 int has_newlines
= !dialog_vars
.no_nl_expand
&& (strstr(s
, "\\n") != 0);
2293 while (*p
!= '\0') {
2294 if (*p
== TAB
&& !dialog_vars
.nocollapse
)
2297 if (has_newlines
) { /* If prompt contains "\n" strings */
2298 if (*p
== '\\' && *(p
+ 1) == 'n') {
2303 * Handle end of lines intelligently. If '\n' follows "\n"
2304 * then ignore the '\n'. This eliminates the need to escape
2305 * the '\n' character (no need to use "\n\").
2311 } else if (*p
== '\n') {
2312 if (dialog_vars
.cr_wrap
)
2315 /* Replace the '\n' with a space if cr_wrap is not set */
2316 if (!trim_blank(base
, s
))
2320 } else /* If *p != '\n' */
2322 } else if (dialog_vars
.trim_whitespace
) {
2324 if (*(s
- 1) != ' ') {
2329 } else if (*p
== '\n') {
2330 if (dialog_vars
.cr_wrap
)
2332 else if (*(s
- 1) != ' ') {
2333 /* Strip '\n's if cr_wrap is not set. */
2340 } else { /* If there are no "\n" strings */
2341 if (*p
== ' ' && !dialog_vars
.nocollapse
) {
2342 if (!trim_blank(base
, s
))
2354 dlg_set_focus(WINDOW
*parent
, WINDOW
*win
)
2357 (void) wmove(parent
,
2358 getpary(win
) + getcury(win
),
2359 getparx(win
) + getcurx(win
));
2360 (void) wnoutrefresh(win
);
2366 * Returns the nominal maximum buffer size.
2369 dlg_max_input(int max_len
)
2371 if (dialog_vars
.max_input
!= 0 && dialog_vars
.max_input
< MAX_LEN
)
2372 max_len
= dialog_vars
.max_input
;
2378 * Free storage used for the result buffer.
2381 dlg_clr_result(void)
2383 if (dialog_vars
.input_length
) {
2384 dialog_vars
.input_length
= 0;
2385 if (dialog_vars
.input_result
)
2386 free(dialog_vars
.input_result
);
2388 dialog_vars
.input_result
= 0;
2392 * Setup a fixed-buffer for the result.
2395 dlg_set_result(const char *string
)
2397 unsigned need
= string
? (unsigned) strlen(string
) + 1 : 0;
2399 /* inputstr.c needs a fixed buffer */
2404 * If the buffer is not big enough, allocate a new one.
2406 if (dialog_vars
.input_length
!= 0
2407 || dialog_vars
.input_result
== 0
2408 || need
> MAX_LEN
) {
2412 dialog_vars
.input_length
= need
;
2413 dialog_vars
.input_result
= dlg_malloc(char, need
);
2414 assert_ptr(dialog_vars
.input_result
, "dlg_set_result");
2417 strcpy(dialog_vars
.input_result
, string
? string
: "");
2419 return dialog_vars
.input_result
;
2423 * Accumulate results in dynamically allocated buffer.
2424 * If input_length is zero, it is a MAX_LEN buffer belonging to the caller.
2427 dlg_add_result(const char *string
)
2429 unsigned have
= (dialog_vars
.input_result
2430 ? (unsigned) strlen(dialog_vars
.input_result
)
2432 unsigned want
= (unsigned) strlen(string
) + 1 + have
;
2434 if ((want
>= MAX_LEN
)
2435 || (dialog_vars
.input_length
!= 0)
2436 || (dialog_vars
.input_result
== 0)) {
2438 if (dialog_vars
.input_length
== 0
2439 || dialog_vars
.input_result
== 0) {
2441 char *save_result
= dialog_vars
.input_result
;
2443 dialog_vars
.input_length
= want
* 2;
2444 dialog_vars
.input_result
= dlg_malloc(char, dialog_vars
.input_length
);
2445 assert_ptr(dialog_vars
.input_result
, "dlg_add_result malloc");
2446 dialog_vars
.input_result
[0] = '\0';
2447 if (save_result
!= 0)
2448 strcpy(dialog_vars
.input_result
, save_result
);
2449 } else if (want
>= dialog_vars
.input_length
) {
2450 dialog_vars
.input_length
= want
* 2;
2451 dialog_vars
.input_result
= dlg_realloc(char,
2452 dialog_vars
.input_length
,
2453 dialog_vars
.input_result
);
2454 assert_ptr(dialog_vars
.input_result
, "dlg_add_result realloc");
2457 strcat(dialog_vars
.input_result
, string
);
2461 * These are characters that (aside from the quote-delimiter) will have to
2462 * be escaped in a single- or double-quoted string.
2464 #define FIX_SINGLE "\n\\"
2465 #define FIX_DOUBLE FIX_SINGLE "[]{}?*;`~#$^&()|<>"
2468 * Returns the quote-delimiter.
2471 quote_delimiter(void)
2473 return dialog_vars
.single_quoted
? "'" : "\"";
2477 * Returns true if we should quote the given string.
2480 must_quote(char *string
)
2484 if (*string
!= '\0') {
2485 size_t len
= strlen(string
);
2486 if (strcspn(string
, quote_delimiter()) != len
)
2488 else if (strcspn(string
, "\n\t ") != len
)
2491 code
= (strcspn(string
, FIX_DOUBLE
) != len
);
2500 * Add a quoted string to the result buffer.
2503 dlg_add_quoted(char *string
)
2506 const char *my_quote
= quote_delimiter();
2507 const char *must_fix
= (dialog_vars
.single_quoted
2511 if (must_quote(string
)) {
2513 dlg_add_result(my_quote
);
2514 while (*string
!= '\0') {
2515 temp
[0] = *string
++;
2516 if ((strchr
) (my_quote
, *temp
) || (strchr
) (must_fix
, *temp
))
2517 dlg_add_result("\\");
2518 dlg_add_result(temp
);
2520 dlg_add_result(my_quote
);
2522 dlg_add_result(string
);
2527 * When adding a result, make that depend on whether "--quoted" is used.
2530 dlg_add_string(char *string
)
2532 if (dialog_vars
.quoted
) {
2533 dlg_add_quoted(string
);
2535 dlg_add_result(string
);
2540 dlg_need_separator(void)
2542 bool result
= FALSE
;
2544 if (dialog_vars
.output_separator
) {
2546 } else if (dialog_vars
.input_result
&& *(dialog_vars
.input_result
)) {
2553 dlg_add_separator(void)
2555 const char *separator
= (dialog_vars
.separate_output
) ? "\n" : " ";
2557 if (dialog_vars
.output_separator
)
2558 separator
= dialog_vars
.output_separator
;
2560 dlg_add_result(separator
);
2563 #define HELP_PREFIX "HELP "
2566 dlg_add_help_listitem(int *result
, char **tag
, DIALOG_LISTITEM
* item
)
2568 dlg_add_result(HELP_PREFIX
);
2569 if (USE_ITEM_HELP(item
->help
)) {
2570 *tag
= dialog_vars
.help_tags
? item
->name
: item
->help
;
2571 *result
= DLG_EXIT_ITEM_HELP
;
2578 dlg_add_help_formitem(int *result
, char **tag
, DIALOG_FORMITEM
* item
)
2580 dlg_add_result(HELP_PREFIX
);
2581 if (USE_ITEM_HELP(item
->help
)) {
2582 *tag
= dialog_vars
.help_tags
? item
->name
: item
->help
;
2583 *result
= DLG_EXIT_ITEM_HELP
;
2590 * Some widgets support only one value of a given variable - save/restore the
2591 * global dialog_vars so we can override it consistently.
2594 dlg_save_vars(DIALOG_VARS
* vars
)
2596 *vars
= dialog_vars
;
2600 * Most of the data in DIALOG_VARS is normally set by command-line options.
2601 * The input_result member is an exception; it is normally set by the dialog
2602 * library to return result values.
2605 dlg_restore_vars(DIALOG_VARS
* vars
)
2607 char *save_result
= dialog_vars
.input_result
;
2608 unsigned save_length
= dialog_vars
.input_length
;
2610 dialog_vars
= *vars
;
2611 dialog_vars
.input_result
= save_result
;
2612 dialog_vars
.input_length
= save_length
;
2616 * Called each time a widget is invoked which may do output, increment a count.
2619 dlg_does_output(void)
2621 dialog_state
.output_count
+= 1;
2625 * Compatibility for different versions of curses.
2627 #if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY))
2629 dlg_getbegx(WINDOW
*win
)
2632 getbegyx(win
, y
, x
);
2636 dlg_getbegy(WINDOW
*win
)
2639 getbegyx(win
, y
, x
);
2644 #if !(defined(HAVE_GETCURX) && defined(HAVE_GETCURY))
2646 dlg_getcurx(WINDOW
*win
)
2653 dlg_getcury(WINDOW
*win
)
2661 #if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY))
2663 dlg_getmaxx(WINDOW
*win
)
2666 getmaxyx(win
, y
, x
);
2670 dlg_getmaxy(WINDOW
*win
)
2673 getmaxyx(win
, y
, x
);
2678 #if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY))
2680 dlg_getparx(WINDOW
*win
)
2683 getparyx(win
, y
, x
);
2687 dlg_getpary(WINDOW
*win
)
2690 getparyx(win
, y
, x
);
2695 #ifdef NEED_WGETPARENT
2697 dlg_wgetparent(WINDOW
*win
)
2703 for (p
= dialog_state
.all_subwindows
; p
!= 0; p
= p
->next
) {
2704 if (p
->shadow
== win
) {