1 /* 2 * $Id: util.c,v 1.255 2013/05/23 22:58:28 tom Exp $ 3 * 4 * util.c -- miscellaneous utilities for dialog 5 * 6 * Copyright 2000-2012,2013 Thomas E. Dickey 7 * 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. 11 * 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. 16 * 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. 22 * 23 * An earlier version of this program lists as authors 24 * Savio Lam (lam836@cs.cuhk.hk) 25 */ 26 27 #include <dialog.h> 28 #include <dlg_keys.h> 29 30 #ifdef HAVE_SETLOCALE 31 #include <locale.h> 32 #endif 33 34 #ifdef NEED_WCHAR_H 35 #include <wchar.h> 36 #endif 37 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> 43 #else 44 #include <term.h> 45 #endif 46 #endif 47 48 #if defined(HAVE_WCHGAT) 49 # if defined(NCURSES_VERSION_PATCH) 50 # if NCURSES_VERSION_PATCH >= 20060715 51 # define USE_WCHGAT 1 52 # else 53 # define USE_WCHGAT 0 54 # endif 55 # else 56 # define USE_WCHGAT 1 57 # endif 58 #else 59 # define USE_WCHGAT 0 60 #endif 61 62 /* globals */ 63 DIALOG_STATE dialog_state; 64 DIALOG_VARS dialog_vars; 65 66 #if !(defined(HAVE_WGETPARENT) && defined(HAVE_WINDOW__PARENT)) 67 #define NEED_WGETPARENT 1 68 #else 69 #undef NEED_WGETPARENT 70 #endif 71 72 #define concat(a,b) a##b 73 74 #ifdef HAVE_RC_FILE 75 #define RC_DATA(name,comment) , #name "_color", comment " color" 76 #else 77 #define RC_DATA(name,comment) /*nothing */ 78 #endif 79 80 #ifdef HAVE_COLOR 81 #include <dlg_colors.h> 82 #define COLOR_DATA(upr) , \ 83 concat(DLGC_FG_,upr), \ 84 concat(DLGC_BG_,upr), \ 85 concat(DLGC_HL_,upr) 86 #else 87 #define COLOR_DATA(upr) /*nothing */ 88 #endif 89 90 #define DATA(atr,upr,lwr,cmt) { atr COLOR_DATA(upr) RC_DATA(lwr,cmt) } 91 92 #define UseShadow(dw) ((dw) != 0 && (dw)->normal != 0 && (dw)->shadow != 0) 93 94 /* 95 * Table of color and attribute values, default is for mono display. 96 * The order matches the DIALOG_ATR() values. 97 */ 98 /* *INDENT-OFF* */ 99 DIALOG_COLORS dlg_color_table[] = 100 { 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") 139 }; 140 /* *INDENT-ON* */ 141 142 /* 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. 145 */ 146 static void 147 add_subwindow(WINDOW *parent, WINDOW *child) 148 { 149 DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1); 150 151 if (p != 0) { 152 p->normal = parent; 153 p->shadow = child; 154 p->next = dialog_state.all_subwindows; 155 dialog_state.all_subwindows = p; 156 } 157 } 158 159 static void 160 del_subwindows(WINDOW *parent) 161 { 162 DIALOG_WINDOWS *p = dialog_state.all_subwindows; 163 DIALOG_WINDOWS *q = 0; 164 DIALOG_WINDOWS *r; 165 166 while (p != 0) { 167 if (p->normal == parent) { 168 delwin(p->shadow); 169 r = p->next; 170 if (q == 0) { 171 dialog_state.all_subwindows = r; 172 } else { 173 q->next = r; 174 } 175 free(p); 176 p = r; 177 } else { 178 q = p; 179 p = p->next; 180 } 181 } 182 } 183 184 /* 185 * Display background title if it exists ... 186 */ 187 void 188 dlg_put_backtitle(void) 189 { 190 int i; 191 192 if (dialog_vars.backtitle != NULL) { 193 chtype attr = A_NORMAL; 194 int backwidth = dlg_count_columns(dialog_vars.backtitle); 195 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)); 204 } 205 206 (void) wnoutrefresh(stdscr); 207 } 208 209 /* 210 * Set window to attribute 'attr'. There are more efficient ways to do this, 211 * but will not work on older/buggy ncurses versions. 212 */ 213 void 214 dlg_attr_clear(WINDOW *win, int height, int width, chtype attr) 215 { 216 int i, j; 217 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, ' '); 223 } 224 (void) touchwin(win); 225 } 226 227 void 228 dlg_clear(void) 229 { 230 dlg_attr_clear(stdscr, LINES, COLS, screen_attr); 231 } 232 233 #define isprivate(s) ((s) != 0 && strstr(s, "\033[?") != 0) 234 235 #define TTY_DEVICE "/dev/tty" 236 237 /* 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. 242 */ 243 static char * 244 dialog_tty(void) 245 { 246 char *result = getenv("DIALOG_TTY"); 247 if (result != 0 && atoi(result) == 0) 248 result = 0; 249 return result; 250 } 251 252 /* 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. 255 */ 256 static int 257 open_terminal(char **result, int mode) 258 { 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) { 266 device = TTY_DEVICE; 267 } 268 } 269 } 270 *result = dlg_strclone(device); 271 return open(device, mode); 272 } 273 274 #ifdef NCURSES_VERSION 275 static int 276 my_putc(int ch) 277 { 278 char buffer[2]; 279 int fd = fileno(dialog_state.screen_output); 280 281 buffer[0] = (char) ch; 282 return (int) write(fd, buffer, (size_t) 1); 283 } 284 #endif 285 286 /* 287 * Do some initialization for dialog. 288 * 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. 291 * 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. 295 */ 296 void 297 init_dialog(FILE *input, FILE *output) 298 { 299 int fd1, fd2; 300 char *device = 0; 301 302 setlocale(LC_ALL, ""); 303 304 dialog_state.output = output; 305 dialog_state.tab_len = TAB_LEN; 306 dialog_state.aspect_ratio = DEFAULT_ASPECT_RATIO; 307 #ifdef HAVE_COLOR 308 dialog_state.use_colors = USE_COLORS; /* use colors by default? */ 309 dialog_state.use_shadow = USE_SHADOW; /* shadow dialog boxes by default? */ 310 #endif 311 312 #ifdef HAVE_RC_FILE 313 if (dlg_parse_rc() == -1) /* Read the configuration file */ 314 dlg_exiterr("init_dialog: dlg_parse_rc"); 315 #endif 316 317 /* 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. 321 * 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 326 * /dev/tty. 327 */ 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); 336 } else { 337 dlg_exiterr("cannot open tty-input"); 338 } 339 close(fd1); 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); 348 } 349 close(fd1); 350 } 351 free(device); 352 } 353 354 /* 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. 357 * 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: 363 * 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. 368 * 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. 371 */ 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"); 378 } 379 free(device); 380 } else { 381 dlg_exiterr("cannot open tty-output"); 382 } 383 } else { 384 dialog_state.screen_output = stdout; 385 (void) initscr(); 386 } 387 #ifdef NCURSES_VERSION 388 /* 389 * Cancel xterm's alternate-screen mode. 390 */ 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)) { 397 /* 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. 402 */ 403 (void) refresh(); 404 (void) tputs(exit_ca_mode, 0, my_putc); 405 (void) tputs(clear_screen, 0, my_putc); 406 /* 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. 412 * 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. 416 */ 417 enter_ca_mode = 0; 418 exit_ca_mode = 0; 419 } 420 #endif 421 #ifdef HAVE_FLUSHINP 422 (void) flushinp(); 423 #endif 424 (void) keypad(stdscr, TRUE); 425 (void) cbreak(); 426 (void) noecho(); 427 428 if (!dialog_state.no_mouse) { 429 mouse_open(); 430 } 431 432 dialog_state.screen_initialized = TRUE; 433 434 #ifdef HAVE_COLOR 435 if (dialog_state.use_colors || dialog_state.use_shadow) 436 dlg_color_setup(); /* Set up colors */ 437 #endif 438 439 /* Set screen to screen attribute */ 440 dlg_clear(); 441 } 442 443 #ifdef HAVE_COLOR 444 static int defined_colors = 1; /* pair-0 is reserved */ 445 /* 446 * Setup for color display 447 */ 448 void 449 dlg_color_setup(void) 450 { 451 unsigned i; 452 453 if (has_colors()) { /* Terminal supports color? */ 454 (void) start_color(); 455 456 #if defined(HAVE_USE_DEFAULT_COLORS) 457 use_default_colors(); 458 #endif 459 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++) { 465 466 /* Initialize color pairs */ 467 (void) init_pair(i + 1, 468 dlg_color_table[i].fg, 469 dlg_color_table[i].bg); 470 471 /* Setup color attributes */ 472 dlg_color_table[i].atr = C_ATTR(dlg_color_table[i].hilite, i + 1); 473 } 474 defined_colors = i + 1; 475 #else 476 for (i = 0; i < sizeof(dlg_color_table) / 477 sizeof(dlg_color_table[0]); i++) { 478 479 /* Initialize color pairs */ 480 chtype color = dlg_color_pair(dlg_color_table[i].fg, 481 dlg_color_table[i].bg); 482 483 /* Setup color attributes */ 484 dlg_color_table[i].atr = ((dlg_color_table[i].hilite 485 ? A_BOLD 486 : 0) 487 | color); 488 } 489 #endif 490 } else { 491 dialog_state.use_colors = FALSE; 492 dialog_state.use_shadow = FALSE; 493 } 494 } 495 496 int 497 dlg_color_count(void) 498 { 499 return sizeof(dlg_color_table) / sizeof(dlg_color_table[0]); 500 } 501 502 /* 503 * Wrapper for getattrs(), or the more cumbersome X/Open wattr_get(). 504 */ 505 chtype 506 dlg_get_attrs(WINDOW *win) 507 { 508 chtype result; 509 #ifdef HAVE_GETATTRS 510 result = (chtype) getattrs(win); 511 #else 512 attr_t my_result; 513 short my_pair; 514 wattr_get(win, &my_result, &my_pair, NULL); 515 result = my_result; 516 #endif 517 return result; 518 } 519 520 /* 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. 524 */ 525 chtype 526 dlg_color_pair(int foreground, int background) 527 { 528 chtype result = 0; 529 int pair; 530 short fg, bg; 531 bool found = FALSE; 532 533 for (pair = 1; pair < defined_colors; ++pair) { 534 if (pair_content((short) pair, &fg, &bg) != ERR 535 && fg == foreground 536 && bg == background) { 537 result = (chtype) COLOR_PAIR(pair); 538 found = TRUE; 539 break; 540 } 541 } 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); 546 } 547 return result; 548 } 549 550 /* 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. 554 */ 555 static chtype 556 define_color(WINDOW *win, int foreground) 557 { 558 chtype attrs = dlg_get_attrs(win); 559 int pair; 560 short fg, bg, background; 561 562 if ((pair = PAIR_NUMBER(attrs)) != 0 563 && pair_content((short) pair, &fg, &bg) != ERR) { 564 background = bg; 565 } else { 566 background = COLOR_BLACK; 567 } 568 return dlg_color_pair(foreground, background); 569 } 570 #endif 571 572 /* 573 * End using dialog functions. 574 */ 575 void 576 end_dialog(void) 577 { 578 if (dialog_state.screen_initialized) { 579 dialog_state.screen_initialized = FALSE; 580 mouse_close(); 581 (void) endwin(); 582 (void) fflush(stdout); 583 } 584 } 585 586 #define ESCAPE_LEN 3 587 #define isOurEscape(p) (((p)[0] == '\\') && ((p)[1] == 'Z') && ((p)[2] != 0)) 588 589 int 590 dlg_count_real_columns(const char *text) 591 { 592 int result = dlg_count_columns(text); 593 if (result && dialog_vars.colors) { 594 int hidden = 0; 595 while (*text) { 596 if (dialog_vars.colors && isOurEscape(text)) { 597 hidden += ESCAPE_LEN; 598 text += ESCAPE_LEN; 599 } else { 600 ++text; 601 } 602 } 603 result -= hidden; 604 } 605 return result; 606 } 607 608 static int 609 centered(int width, const char *string) 610 { 611 int need = dlg_count_real_columns(string); 612 int left; 613 614 left = (width - need) / 2 - 1; 615 if (left < 0) 616 left = 0; 617 return left; 618 } 619 620 #ifdef USE_WIDE_CURSES 621 static bool 622 is_combining(const char *txt, int *combined) 623 { 624 bool result = FALSE; 625 626 if (*combined == 0) { 627 if (UCH(*txt) >= 128) { 628 wchar_t wch; 629 mbstate_t state; 630 size_t given = strlen(txt); 631 size_t len; 632 633 memset(&state, 0, sizeof(state)); 634 len = mbrtowc(&wch, txt, given, &state); 635 if ((int) len > 0 && wcwidth(wch) == 0) { 636 *combined = (int) len - 1; 637 result = TRUE; 638 } 639 } 640 } else { 641 result = TRUE; 642 *combined -= 1; 643 } 644 return result; 645 } 646 #endif 647 648 /* 649 * Print the name (tag) or text from a DIALOG_LISTITEM, highlighting the 650 * first character if selected. 651 */ 652 void 653 dlg_print_listitem(WINDOW *win, 654 const char *text, 655 int climit, 656 bool first, 657 int selected) 658 { 659 chtype attr = A_NORMAL; 660 int limit; 661 const int *cols; 662 chtype attrs[4]; 663 664 if (text == 0) 665 text = ""; 666 667 if (first) { 668 const int *indx = dlg_index_wchars(text); 669 attrs[3] = tag_key_selected_attr; 670 attrs[2] = tag_key_attr; 671 attrs[1] = tag_selected_attr; 672 attrs[0] = tag_attr; 673 674 (void) wattrset(win, selected ? attrs[3] : attrs[2]); 675 (void) waddnstr(win, text, indx[1]); 676 677 if ((int) strlen(text) > indx[1]) { 678 limit = dlg_limit_columns(text, climit, 1); 679 if (limit > 1) { 680 (void) wattrset(win, selected ? attrs[1] : attrs[0]); 681 (void) waddnstr(win, 682 text + indx[1], 683 indx[limit] - indx[1]); 684 } 685 } 686 } else { 687 attrs[1] = item_selected_attr; 688 attrs[0] = item_attr; 689 690 cols = dlg_index_columns(text); 691 limit = dlg_limit_columns(text, climit, 0); 692 693 if (limit > 0) { 694 (void) wattrset(win, selected ? attrs[1] : attrs[0]); 695 dlg_print_text(win, text, cols[limit], &attr); 696 } 697 } 698 } 699 700 /* 701 * Print up to 'cols' columns from 'text', optionally rendering our escape 702 * sequence for attributes and color. 703 */ 704 void 705 dlg_print_text(WINDOW *win, const char *txt, int cols, chtype *attr) 706 { 707 int y_origin, x_origin; 708 int y_before, x_before = 0; 709 int y_after, x_after; 710 int tabbed = 0; 711 bool thisTab; 712 bool ended = FALSE; 713 chtype useattr; 714 #ifdef USE_WIDE_CURSES 715 int combined = 0; 716 #endif 717 718 getyx(win, y_origin, x_origin); 719 while (cols > 0 && (*txt != '\0')) { 720 if (dialog_vars.colors) { 721 while (isOurEscape(txt)) { 722 int code; 723 724 txt += 2; 725 switch (code = CharOf(*txt)) { 726 #ifdef HAVE_COLOR 727 case '0': 728 case '1': 729 case '2': 730 case '3': 731 case '4': 732 case '5': 733 case '6': 734 case '7': 735 *attr &= ~A_COLOR; 736 *attr |= define_color(win, code - '0'); 737 break; 738 #endif 739 case 'B': 740 *attr &= ~A_BOLD; 741 break; 742 case 'b': 743 *attr |= A_BOLD; 744 break; 745 case 'R': 746 *attr &= ~A_REVERSE; 747 break; 748 case 'r': 749 *attr |= A_REVERSE; 750 break; 751 case 'U': 752 *attr &= ~A_UNDERLINE; 753 break; 754 case 'u': 755 *attr |= A_UNDERLINE; 756 break; 757 case 'n': 758 *attr = A_NORMAL; 759 break; 760 } 761 ++txt; 762 } 763 } 764 if (ended || *txt == '\n' || *txt == '\0') 765 break; 766 useattr = (*attr) & A_ATTRIBUTES; 767 #ifdef HAVE_COLOR 768 /* 769 * Prevent this from making text invisible when the foreground and 770 * background colors happen to be the same, and there's no bold 771 * attribute. 772 */ 773 if ((useattr & A_COLOR) != 0 && (useattr & A_BOLD) == 0) { 774 short pair = (short) PAIR_NUMBER(useattr); 775 short fg, bg; 776 if (pair_content(pair, &fg, &bg) != ERR 777 && fg == bg) { 778 useattr &= ~A_COLOR; 779 useattr |= dlg_color_pair(fg, ((bg == COLOR_BLACK) 780 ? COLOR_WHITE 781 : COLOR_BLACK)); 782 } 783 } 784 #endif 785 /* 786 * Write the character, using curses to tell exactly how wide it 787 * is. If it is a tab, discount that, since the caller thinks 788 * tabs are nonprinting, and curses will expand tabs to one or 789 * more blanks. 790 */ 791 thisTab = (CharOf(*txt) == TAB); 792 if (thisTab) { 793 getyx(win, y_before, x_before); 794 (void) y_before; 795 } 796 (void) waddch(win, CharOf(*txt++) | useattr); 797 getyx(win, y_after, x_after); 798 if (thisTab && (y_after == y_origin)) 799 tabbed += (x_after - x_before); 800 if ((y_after != y_origin) || 801 (x_after >= (cols + tabbed + x_origin) 802 #ifdef USE_WIDE_CURSES 803 && !is_combining(txt, &combined) 804 #endif 805 )) { 806 ended = TRUE; 807 } 808 } 809 } 810 811 /* 812 * Print one line of the prompt in the window within the limits of the 813 * specified right margin. The line will end on a word boundary and a pointer 814 * to the start of the next line is returned, or a NULL pointer if the end of 815 * *prompt is reached. 816 */ 817 const char * 818 dlg_print_line(WINDOW *win, 819 chtype *attr, 820 const char *prompt, 821 int lm, int rm, int *x) 822 { 823 const char *wrap_ptr; 824 const char *test_ptr; 825 const char *hide_ptr = 0; 826 const int *cols = dlg_index_columns(prompt); 827 const int *indx = dlg_index_wchars(prompt); 828 int wrap_inx = 0; 829 int test_inx = 0; 830 int cur_x = lm; 831 int hidden = 0; 832 int limit = dlg_count_wchars(prompt); 833 int n; 834 int tabbed = 0; 835 836 *x = 1; 837 838 /* 839 * Set *test_ptr to the end of the line or the right margin (rm), whichever 840 * is less, and set wrap_ptr to the end of the last word in the line. 841 */ 842 for (n = 0; n < limit; ++n) { 843 test_ptr = prompt + indx[test_inx]; 844 if (*test_ptr == '\n' || *test_ptr == '\0' || cur_x >= (rm + hidden)) 845 break; 846 if (*test_ptr == TAB && n == 0) { 847 tabbed = 8; /* workaround for leading tabs */ 848 } else if (*test_ptr == ' ' && n != 0 && prompt[indx[n - 1]] != ' ') { 849 wrap_inx = n; 850 *x = cur_x; 851 } else if (dialog_vars.colors && isOurEscape(test_ptr)) { 852 hide_ptr = test_ptr; 853 hidden += ESCAPE_LEN; 854 n += (ESCAPE_LEN - 1); 855 } 856 cur_x = lm + tabbed + cols[n + 1]; 857 if (cur_x > (rm + hidden)) 858 break; 859 test_inx = n + 1; 860 } 861 862 /* 863 * If the line doesn't reach the right margin in the middle of a word, then 864 * we don't have to wrap it at the end of the previous word. 865 */ 866 test_ptr = prompt + indx[test_inx]; 867 if (*test_ptr == '\n' || *test_ptr == ' ' || *test_ptr == '\0') { 868 wrap_inx = test_inx; 869 while (wrap_inx > 0 && prompt[indx[wrap_inx - 1]] == ' ') { 870 wrap_inx--; 871 } 872 *x = lm + indx[wrap_inx]; 873 } else if (*x == 1 && cur_x >= rm) { 874 /* 875 * If the line has no spaces, then wrap it anyway at the right margin 876 */ 877 *x = rm; 878 wrap_inx = test_inx; 879 } 880 wrap_ptr = prompt + indx[wrap_inx]; 881 #ifdef USE_WIDE_CURSES 882 if (UCH(*wrap_ptr) >= 128) { 883 int combined = 0; 884 while (is_combining(wrap_ptr, &combined)) { 885 ++wrap_ptr; 886 } 887 } 888 #endif 889 890 /* 891 * If we found hidden text past the last point that we will display, 892 * discount that from the displayed length. 893 */ 894 if ((hide_ptr != 0) && (hide_ptr >= wrap_ptr)) { 895 hidden -= ESCAPE_LEN; 896 test_ptr = wrap_ptr; 897 while (test_ptr < wrap_ptr) { 898 if (dialog_vars.colors && isOurEscape(test_ptr)) { 899 hidden -= ESCAPE_LEN; 900 test_ptr += ESCAPE_LEN; 901 } else { 902 ++test_ptr; 903 } 904 } 905 } 906 907 /* 908 * Print the line if we have a window pointer. Otherwise this routine 909 * is just being called for sizing the window. 910 */ 911 if (win) { 912 dlg_print_text(win, prompt, (cols[wrap_inx] - hidden), attr); 913 } 914 915 /* *x tells the calling function how long the line was */ 916 if (*x == 1) 917 *x = rm; 918 919 *x -= hidden; 920 921 /* Find the start of the next line and return a pointer to it */ 922 test_ptr = wrap_ptr; 923 while (*test_ptr == ' ') 924 test_ptr++; 925 if (*test_ptr == '\n') 926 test_ptr++; 927 return (test_ptr); 928 } 929 930 static void 931 justify_text(WINDOW *win, 932 const char *prompt, 933 int limit_y, 934 int limit_x, 935 int *high, int *wide) 936 { 937 chtype attr = A_NORMAL; 938 int x = (2 * MARGIN); 939 int y = MARGIN; 940 int max_x = 2; 941 int lm = (2 * MARGIN); /* left margin (box-border plus a space) */ 942 int rm = limit_x; /* right margin */ 943 int bm = limit_y; /* bottom margin */ 944 int last_y = 0, last_x = 0; 945 946 if (win) { 947 rm -= (2 * MARGIN); 948 bm -= (2 * MARGIN); 949 } 950 if (prompt == 0) 951 prompt = ""; 952 953 if (win != 0) 954 getyx(win, last_y, last_x); 955 while (y <= bm && *prompt) { 956 x = lm; 957 958 if (*prompt == '\n') { 959 while (*prompt == '\n' && y < bm) { 960 if (*(prompt + 1) != '\0') { 961 ++y; 962 if (win != 0) 963 (void) wmove(win, y, lm); 964 } 965 prompt++; 966 } 967 } else if (win != 0) 968 (void) wmove(win, y, lm); 969 970 if (*prompt) { 971 prompt = dlg_print_line(win, &attr, prompt, lm, rm, &x); 972 if (win != 0) 973 getyx(win, last_y, last_x); 974 } 975 if (*prompt) { 976 ++y; 977 if (win != 0) 978 (void) wmove(win, y, lm); 979 } 980 max_x = MAX(max_x, x); 981 } 982 /* Move back to the last position after drawing prompt, for msgbox. */ 983 if (win != 0) 984 (void) wmove(win, last_y, last_x); 985 986 /* Set the final height and width for the calling function */ 987 if (high != 0) 988 *high = y; 989 if (wide != 0) 990 *wide = max_x; 991 } 992 993 /* 994 * Print a string of text in a window, automatically wrap around to the next 995 * line if the string is too long to fit on one line. Note that the string may 996 * contain embedded newlines. 997 */ 998 void 999 dlg_print_autowrap(WINDOW *win, const char *prompt, int height, int width) 1000 { 1001 justify_text(win, prompt, 1002 height, 1003 width, 1004 (int *) 0, (int *) 0); 1005 } 1006 1007 /* 1008 * Display the message in a scrollable window. Actually the way it works is 1009 * that we create a "tall" window of the proper width, let the text wrap within 1010 * that, and copy a slice of the result to the dialog. 1011 * 1012 * It works for ncurses. Other curses implementations show only blanks (Tru64) 1013 * or garbage (NetBSD). 1014 */ 1015 int 1016 dlg_print_scrolled(WINDOW *win, 1017 const char *prompt, 1018 int offset, 1019 int height, 1020 int width, 1021 int pauseopt) 1022 { 1023 int oldy, oldx; 1024 int last = 0; 1025 1026 (void) pauseopt; /* used only for ncurses */ 1027 1028 getyx(win, oldy, oldx); 1029 #ifdef NCURSES_VERSION 1030 if (pauseopt) { 1031 int wide = width - (2 * MARGIN); 1032 int high = LINES; 1033 int y, x; 1034 int len; 1035 int percent; 1036 WINDOW *dummy; 1037 char buffer[5]; 1038 1039 #if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20040417 1040 /* 1041 * If we're not limited by the screensize, allow text to possibly be 1042 * one character per line. 1043 */ 1044 if ((len = dlg_count_columns(prompt)) > high) 1045 high = len; 1046 #endif 1047 dummy = newwin(high, width, 0, 0); 1048 if (dummy == 0) { 1049 (void) wattrset(win, dialog_attr); 1050 dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width); 1051 last = 0; 1052 } else { 1053 wbkgdset(dummy, dialog_attr | ' '); 1054 (void) wattrset(dummy, dialog_attr); 1055 werase(dummy); 1056 dlg_print_autowrap(dummy, prompt, high, width); 1057 getyx(dummy, y, x); 1058 (void) x; 1059 1060 copywin(dummy, /* srcwin */ 1061 win, /* dstwin */ 1062 offset + MARGIN, /* sminrow */ 1063 MARGIN, /* smincol */ 1064 MARGIN, /* dminrow */ 1065 MARGIN, /* dmincol */ 1066 height, /* dmaxrow */ 1067 wide, /* dmaxcol */ 1068 FALSE); 1069 1070 delwin(dummy); 1071 1072 /* if the text is incomplete, or we have scrolled, show the percentage */ 1073 if (y > 0 && wide > 4) { 1074 percent = (int) ((height + offset) * 100.0 / y); 1075 if (percent < 0) 1076 percent = 0; 1077 if (percent > 100) 1078 percent = 100; 1079 if (offset != 0 || percent != 100) { 1080 (void) wattrset(win, position_indicator_attr); 1081 (void) wmove(win, MARGIN + height, wide - 4); 1082 (void) sprintf(buffer, "%d%%", percent); 1083 (void) waddstr(win, buffer); 1084 if ((len = (int) strlen(buffer)) < 4) { 1085 (void) wattrset(win, border_attr); 1086 whline(win, dlg_boxchar(ACS_HLINE), 4 - len); 1087 } 1088 } 1089 } 1090 last = (y - height); 1091 } 1092 } else 1093 #endif 1094 { 1095 (void) offset; 1096 (void) wattrset(win, dialog_attr); 1097 dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width); 1098 last = 0; 1099 } 1100 wmove(win, oldy, oldx); 1101 return last; 1102 } 1103 1104 int 1105 dlg_check_scrolled(int key, int last, int page, bool * show, int *offset) 1106 { 1107 int code = 0; 1108 1109 *show = FALSE; 1110 1111 switch (key) { 1112 case DLGK_PAGE_FIRST: 1113 if (*offset > 0) { 1114 *offset = 0; 1115 *show = TRUE; 1116 } 1117 break; 1118 case DLGK_PAGE_LAST: 1119 if (*offset < last) { 1120 *offset = last; 1121 *show = TRUE; 1122 } 1123 break; 1124 case DLGK_GRID_UP: 1125 if (*offset > 0) { 1126 --(*offset); 1127 *show = TRUE; 1128 } 1129 break; 1130 case DLGK_GRID_DOWN: 1131 if (*offset < last) { 1132 ++(*offset); 1133 *show = TRUE; 1134 } 1135 break; 1136 case DLGK_PAGE_PREV: 1137 if (*offset > 0) { 1138 *offset -= page; 1139 if (*offset < 0) 1140 *offset = 0; 1141 *show = TRUE; 1142 } 1143 break; 1144 case DLGK_PAGE_NEXT: 1145 if (*offset < last) { 1146 *offset += page; 1147 if (*offset > last) 1148 *offset = last; 1149 *show = TRUE; 1150 } 1151 break; 1152 default: 1153 code = -1; 1154 break; 1155 } 1156 return code; 1157 } 1158 1159 /* 1160 * Calculate the window size for preformatted text. This will calculate box 1161 * dimensions that are at or close to the specified aspect ratio for the prompt 1162 * string with all spaces and newlines preserved and additional newlines added 1163 * as necessary. 1164 */ 1165 static void 1166 auto_size_preformatted(const char *prompt, int *height, int *width) 1167 { 1168 int high = 0, wide = 0; 1169 float car; /* Calculated Aspect Ratio */ 1170 float diff; 1171 int max_y = SLINES - 1; 1172 int max_x = SCOLS - 2; 1173 int max_width = max_x; 1174 int ar = dialog_state.aspect_ratio; 1175 1176 /* Get the initial dimensions */ 1177 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide); 1178 car = (float) (wide / high); 1179 1180 /* 1181 * If the aspect ratio is greater than it should be, then decrease the 1182 * width proportionately. 1183 */ 1184 if (car > ar) { 1185 diff = car / (float) ar; 1186 max_x = (int) ((float) wide / diff + 4); 1187 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide); 1188 car = (float) wide / (float) high; 1189 } 1190 1191 /* 1192 * If the aspect ratio is too small after decreasing the width, then 1193 * incrementally increase the width until the aspect ratio is equal to or 1194 * greater than the specified aspect ratio. 1195 */ 1196 while (car < ar && max_x < max_width) { 1197 max_x += 4; 1198 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide); 1199 car = (float) (wide / high); 1200 } 1201 1202 *height = high; 1203 *width = wide; 1204 } 1205 1206 /* 1207 * Find the length of the longest "word" in the given string. By setting the 1208 * widget width at least this long, we can avoid splitting a word on the 1209 * margin. 1210 */ 1211 static int 1212 longest_word(const char *string) 1213 { 1214 int length, result = 0; 1215 1216 while (*string != '\0') { 1217 length = 0; 1218 while (*string != '\0' && !isspace(UCH(*string))) { 1219 length++; 1220 string++; 1221 } 1222 result = MAX(result, length); 1223 if (*string != '\0') 1224 string++; 1225 } 1226 return result; 1227 } 1228 1229 /* 1230 * if (height or width == -1) Maximize() 1231 * if (height or width == 0), justify and return actual limits. 1232 */ 1233 static void 1234 real_auto_size(const char *title, 1235 const char *prompt, 1236 int *height, int *width, 1237 int boxlines, int mincols) 1238 { 1239 int x = (dialog_vars.begin_set ? dialog_vars.begin_x : 2); 1240 int y = (dialog_vars.begin_set ? dialog_vars.begin_y : 1); 1241 int title_length = title ? dlg_count_columns(title) : 0; 1242 int nc = 4; 1243 int high; 1244 int wide; 1245 int save_high = *height; 1246 int save_wide = *width; 1247 1248 if (prompt == 0) { 1249 if (*height == 0) 1250 *height = -1; 1251 if (*width == 0) 1252 *width = -1; 1253 } 1254 1255 if (*height > 0) { 1256 high = *height; 1257 } else { 1258 high = SLINES - y; 1259 } 1260 1261 if (*width <= 0) { 1262 if (prompt != 0) { 1263 wide = MAX(title_length, mincols); 1264 if (strchr(prompt, '\n') == 0) { 1265 double val = (dialog_state.aspect_ratio * 1266 dlg_count_real_columns(prompt)); 1267 double xxx = sqrt(val); 1268 int tmp = (int) xxx; 1269 wide = MAX(wide, tmp); 1270 wide = MAX(wide, longest_word(prompt)); 1271 justify_text((WINDOW *) 0, prompt, high, wide, height, width); 1272 } else { 1273 auto_size_preformatted(prompt, height, width); 1274 } 1275 } else { 1276 wide = SCOLS - x; 1277 justify_text((WINDOW *) 0, prompt, high, wide, height, width); 1278 } 1279 } 1280 1281 if (*width < title_length) { 1282 justify_text((WINDOW *) 0, prompt, high, title_length, height, width); 1283 *width = title_length; 1284 } 1285 1286 if (*width < mincols && save_wide == 0) 1287 *width = mincols; 1288 if (prompt != 0) { 1289 *width += nc; 1290 *height += boxlines + 2; 1291 } 1292 if (save_high > 0) 1293 *height = save_high; 1294 if (save_wide > 0) 1295 *width = save_wide; 1296 } 1297 1298 /* End of real_auto_size() */ 1299 1300 void 1301 dlg_auto_size(const char *title, 1302 const char *prompt, 1303 int *height, 1304 int *width, 1305 int boxlines, 1306 int mincols) 1307 { 1308 real_auto_size(title, prompt, height, width, boxlines, mincols); 1309 1310 if (*width > SCOLS) { 1311 (*height)++; 1312 *width = SCOLS; 1313 } 1314 1315 if (*height > SLINES) 1316 *height = SLINES; 1317 } 1318 1319 /* 1320 * if (height or width == -1) Maximize() 1321 * if (height or width == 0) 1322 * height=MIN(SLINES, num.lines in fd+n); 1323 * width=MIN(SCOLS, MAX(longer line+n, mincols)); 1324 */ 1325 void 1326 dlg_auto_sizefile(const char *title, 1327 const char *file, 1328 int *height, 1329 int *width, 1330 int boxlines, 1331 int mincols) 1332 { 1333 int count = 0; 1334 int len = title ? dlg_count_columns(title) : 0; 1335 int nc = 4; 1336 int numlines = 2; 1337 long offset; 1338 int ch; 1339 FILE *fd; 1340 1341 /* Open input file for reading */ 1342 if ((fd = fopen(file, "rb")) == NULL) 1343 dlg_exiterr("dlg_auto_sizefile: Cannot open input file %s", file); 1344 1345 if ((*height == -1) || (*width == -1)) { 1346 *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0); 1347 *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0); 1348 } 1349 if ((*height != 0) && (*width != 0)) { 1350 (void) fclose(fd); 1351 if (*width > SCOLS) 1352 *width = SCOLS; 1353 if (*height > SLINES) 1354 *height = SLINES; 1355 return; 1356 } 1357 1358 while (!feof(fd)) { 1359 offset = 0; 1360 while (((ch = getc(fd)) != '\n') && !feof(fd)) 1361 if ((ch == TAB) && (dialog_vars.tab_correct)) 1362 offset += dialog_state.tab_len - (offset % dialog_state.tab_len); 1363 else 1364 offset++; 1365 1366 if (offset > len) 1367 len = (int) offset; 1368 1369 count++; 1370 } 1371 1372 /* now 'count' has the number of lines of fd and 'len' the max length */ 1373 1374 *height = MIN(SLINES, count + numlines + boxlines); 1375 *width = MIN(SCOLS, MAX((len + nc), mincols)); 1376 /* here width and height can be maximized if > SCOLS|SLINES because 1377 textbox-like widgets don't put all <file> on the screen. 1378 Msgbox-like widget instead have to put all <text> correctly. */ 1379 1380 (void) fclose(fd); 1381 } 1382 1383 static chtype 1384 dlg_get_cell_attrs(WINDOW *win) 1385 { 1386 chtype result; 1387 #ifdef USE_WIDE_CURSES 1388 cchar_t wch; 1389 wchar_t cc; 1390 attr_t attrs; 1391 short pair; 1392 if (win_wch(win, &wch) == OK 1393 && getcchar(&wch, &cc, &attrs, &pair, NULL) == OK) { 1394 result = attrs; 1395 } else { 1396 result = 0; 1397 } 1398 #else 1399 result = winch(win) & (A_ATTRIBUTES & ~A_COLOR); 1400 #endif 1401 return result; 1402 } 1403 1404 /* 1405 * Draw a rectangular box with line drawing characters. 1406 * 1407 * borderchar is used to color the upper/left edges. 1408 * 1409 * boxchar is used to color the right/lower edges. It also is fill-color used 1410 * for the box contents. 1411 * 1412 * Normally, if you are drawing a scrollable box, use menubox_border_attr for 1413 * boxchar, and menubox_attr for borderchar since the scroll-arrows are drawn 1414 * with menubox_attr at the top, and menubox_border_attr at the bottom. That 1415 * also (given the default color choices) produces a recessed effect. 1416 * 1417 * If you want a raised effect (and are not going to use the scroll-arrows), 1418 * reverse this choice. 1419 */ 1420 void 1421 dlg_draw_box2(WINDOW *win, int y, int x, int height, int width, 1422 chtype boxchar, chtype borderchar, chtype borderchar2) 1423 { 1424 int i, j; 1425 chtype save = dlg_get_attrs(win); 1426 1427 (void) wattrset(win, 0); 1428 for (i = 0; i < height; i++) { 1429 (void) wmove(win, y + i, x); 1430 for (j = 0; j < width; j++) 1431 if (!i && !j) 1432 (void) waddch(win, borderchar | dlg_boxchar(ACS_ULCORNER)); 1433 else if (i == height - 1 && !j) 1434 (void) waddch(win, borderchar | dlg_boxchar(ACS_LLCORNER)); 1435 else if (!i && j == width - 1) 1436 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_URCORNER)); 1437 else if (i == height - 1 && j == width - 1) 1438 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_LRCORNER)); 1439 else if (!i) 1440 (void) waddch(win, borderchar | dlg_boxchar(ACS_HLINE)); 1441 else if (i == height - 1) 1442 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_HLINE)); 1443 else if (!j) 1444 (void) waddch(win, borderchar | dlg_boxchar(ACS_VLINE)); 1445 else if (j == width - 1) 1446 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_VLINE)); 1447 else 1448 (void) waddch(win, boxchar | ' '); 1449 } 1450 (void) wattrset(win, save); 1451 } 1452 1453 void 1454 dlg_draw_box(WINDOW *win, int y, int x, int height, int width, 1455 chtype boxchar, chtype borderchar) 1456 { 1457 dlg_draw_box2(win, y, x, height, width, boxchar, borderchar, boxchar); 1458 } 1459 1460 static DIALOG_WINDOWS * 1461 find_window(WINDOW *win) 1462 { 1463 DIALOG_WINDOWS *result = 0; 1464 DIALOG_WINDOWS *p; 1465 1466 for (p = dialog_state.all_windows; p != 0; p = p->next) { 1467 if (p->normal == win) { 1468 result = p; 1469 break; 1470 } 1471 } 1472 return result; 1473 } 1474 1475 #ifdef HAVE_COLOR 1476 /* 1477 * If we have wchgat(), use that for updating shadow attributes, to work with 1478 * wide-character data. 1479 */ 1480 1481 /* 1482 * Check if the given point is "in" the given window. If so, return the window 1483 * pointer, otherwise null. 1484 */ 1485 static WINDOW * 1486 in_window(WINDOW *win, int y, int x) 1487 { 1488 WINDOW *result = 0; 1489 int y_base = getbegy(win); 1490 int x_base = getbegx(win); 1491 int y_last = getmaxy(win) + y_base; 1492 int x_last = getmaxx(win) + x_base; 1493 1494 if (y >= y_base && y <= y_last && x >= x_base && x <= x_last) 1495 result = win; 1496 return result; 1497 } 1498 1499 static WINDOW * 1500 window_at_cell(DIALOG_WINDOWS * dw, int y, int x) 1501 { 1502 WINDOW *result = 0; 1503 DIALOG_WINDOWS *p; 1504 int y_want = y + getbegy(dw->shadow); 1505 int x_want = x + getbegx(dw->shadow); 1506 1507 for (p = dialog_state.all_windows; p != 0; p = p->next) { 1508 if (dw->normal != p->normal 1509 && dw->shadow != p->normal 1510 && (result = in_window(p->normal, y_want, x_want)) != 0) { 1511 break; 1512 } 1513 } 1514 if (result == 0) { 1515 result = stdscr; 1516 } 1517 return result; 1518 } 1519 1520 static bool 1521 in_shadow(WINDOW *normal, WINDOW *shadow, int y, int x) 1522 { 1523 bool result = FALSE; 1524 int ybase = getbegy(normal); 1525 int ylast = getmaxy(normal) + ybase; 1526 int xbase = getbegx(normal); 1527 int xlast = getmaxx(normal) + xbase; 1528 1529 y += getbegy(shadow); 1530 x += getbegx(shadow); 1531 1532 if (y >= ybase + SHADOW_ROWS 1533 && y < ylast + SHADOW_ROWS 1534 && x >= xlast 1535 && x < xlast + SHADOW_COLS) { 1536 /* in the right-side */ 1537 result = TRUE; 1538 } else if (y >= ylast 1539 && y < ylast + SHADOW_ROWS 1540 && x >= ybase + SHADOW_COLS 1541 && x < ylast + SHADOW_COLS) { 1542 /* check the bottom */ 1543 result = TRUE; 1544 } 1545 1546 return result; 1547 } 1548 1549 /* 1550 * When erasing a shadow, check each cell to make sure that it is not part of 1551 * another box's shadow. This is a little complicated since most shadows are 1552 * merged onto stdscr. 1553 */ 1554 static bool 1555 last_shadow(DIALOG_WINDOWS * dw, int y, int x) 1556 { 1557 DIALOG_WINDOWS *p; 1558 bool result = TRUE; 1559 1560 for (p = dialog_state.all_windows; p != 0; p = p->next) { 1561 if (p->normal != dw->normal 1562 && in_shadow(p->normal, dw->shadow, y, x)) { 1563 result = FALSE; 1564 break; 1565 } 1566 } 1567 return result; 1568 } 1569 1570 static void 1571 repaint_cell(DIALOG_WINDOWS * dw, bool draw, int y, int x) 1572 { 1573 WINDOW *win = dw->shadow; 1574 WINDOW *cellwin; 1575 int y2, x2; 1576 1577 if ((cellwin = window_at_cell(dw, y, x)) != 0 1578 && (draw || last_shadow(dw, y, x)) 1579 && (y2 = (y + getbegy(win) - getbegy(cellwin))) >= 0 1580 && (x2 = (x + getbegx(win) - getbegx(cellwin))) >= 0 1581 && wmove(cellwin, y2, x2) != ERR) { 1582 chtype the_cell = dlg_get_attrs(cellwin); 1583 chtype the_attr = (draw ? shadow_attr : the_cell); 1584 1585 if (dlg_get_cell_attrs(cellwin) & A_ALTCHARSET) { 1586 the_attr |= A_ALTCHARSET; 1587 } 1588 #if USE_WCHGAT 1589 wchgat(cellwin, 1, 1590 the_attr & (chtype) (~A_COLOR), 1591 (short) PAIR_NUMBER(the_attr), 1592 NULL); 1593 #else 1594 { 1595 chtype the_char = ((winch(cellwin) & A_CHARTEXT) | the_attr); 1596 (void) waddch(cellwin, the_char); 1597 } 1598 #endif 1599 wnoutrefresh(cellwin); 1600 } 1601 } 1602 1603 #define RepaintCell(dw, draw, y, x) repaint_cell(dw, draw, y, x) 1604 1605 static void 1606 repaint_shadow(DIALOG_WINDOWS * dw, bool draw, int y, int x, int height, int width) 1607 { 1608 int i, j; 1609 1610 if (UseShadow(dw)) { 1611 #if !USE_WCHGAT 1612 chtype save = dlg_get_attrs(dw->shadow); 1613 (void) wattrset(dw->shadow, draw ? shadow_attr : screen_attr); 1614 #endif 1615 for (i = 0; i < SHADOW_ROWS; ++i) { 1616 for (j = 0; j < width; ++j) { 1617 RepaintCell(dw, draw, i + y + height, j + x + SHADOW_COLS); 1618 } 1619 } 1620 for (i = 0; i < height; i++) { 1621 for (j = 0; j < SHADOW_COLS; ++j) { 1622 RepaintCell(dw, draw, i + y + SHADOW_ROWS, j + x + width); 1623 } 1624 } 1625 (void) wnoutrefresh(dw->shadow); 1626 #if !USE_WCHGAT 1627 (void) wattrset(dw->shadow, save); 1628 #endif 1629 } 1630 } 1631 1632 /* 1633 * Draw a shadow on the parent window corresponding to the right- and 1634 * bottom-edge of the child window, to give a 3-dimensional look. 1635 */ 1636 static void 1637 draw_childs_shadow(DIALOG_WINDOWS * dw) 1638 { 1639 if (UseShadow(dw)) { 1640 repaint_shadow(dw, 1641 TRUE, 1642 getbegy(dw->normal) - getbegy(dw->shadow), 1643 getbegx(dw->normal) - getbegx(dw->shadow), 1644 getmaxy(dw->normal), 1645 getmaxx(dw->normal)); 1646 } 1647 } 1648 1649 /* 1650 * Erase a shadow on the parent window corresponding to the right- and 1651 * bottom-edge of the child window. 1652 */ 1653 static void 1654 erase_childs_shadow(DIALOG_WINDOWS * dw) 1655 { 1656 if (UseShadow(dw)) { 1657 repaint_shadow(dw, 1658 FALSE, 1659 getbegy(dw->normal) - getbegy(dw->shadow), 1660 getbegx(dw->normal) - getbegx(dw->shadow), 1661 getmaxy(dw->normal), 1662 getmaxx(dw->normal)); 1663 } 1664 } 1665 1666 /* 1667 * Draw shadows along the right and bottom edge to give a more 3D look 1668 * to the boxes. 1669 */ 1670 void 1671 dlg_draw_shadow(WINDOW *win, int y, int x, int height, int width) 1672 { 1673 repaint_shadow(find_window(win), TRUE, y, x, height, width); 1674 } 1675 #endif /* HAVE_COLOR */ 1676 1677 /* 1678 * Allow shell scripts to remap the exit codes so they can distinguish ESC 1679 * from ERROR. 1680 */ 1681 void 1682 dlg_exit(int code) 1683 { 1684 /* *INDENT-OFF* */ 1685 static const struct { 1686 int code; 1687 const char *name; 1688 } table[] = { 1689 { DLG_EXIT_CANCEL, "DIALOG_CANCEL" }, 1690 { DLG_EXIT_ERROR, "DIALOG_ERROR" }, 1691 { DLG_EXIT_ESC, "DIALOG_ESC" }, 1692 { DLG_EXIT_EXTRA, "DIALOG_EXTRA" }, 1693 { DLG_EXIT_HELP, "DIALOG_HELP" }, 1694 { DLG_EXIT_OK, "DIALOG_OK" }, 1695 { DLG_EXIT_ITEM_HELP, "DIALOG_ITEM_HELP" }, 1696 }; 1697 /* *INDENT-ON* */ 1698 1699 unsigned n; 1700 char *name; 1701 char *temp; 1702 long value; 1703 bool overridden = FALSE; 1704 1705 retry: 1706 for (n = 0; n < sizeof(table) / sizeof(table[0]); n++) { 1707 if (table[n].code == code) { 1708 if ((name = getenv(table[n].name)) != 0) { 1709 value = strtol(name, &temp, 0); 1710 if (temp != 0 && temp != name && *temp == '\0') { 1711 code = (int) value; 1712 overridden = TRUE; 1713 } 1714 } 1715 break; 1716 } 1717 } 1718 1719 /* 1720 * Prior to 2004/12/19, a widget using --item-help would exit with "OK" 1721 * if the help button were selected. Now we want to exit with "HELP", 1722 * but allow the environment variable to override. 1723 */ 1724 if (code == DLG_EXIT_ITEM_HELP && !overridden) { 1725 code = DLG_EXIT_HELP; 1726 goto retry; 1727 } 1728 #ifdef HAVE_DLG_TRACE 1729 dlg_trace((const char *) 0); /* close it */ 1730 #endif 1731 1732 #ifdef NO_LEAKS 1733 _dlg_inputstr_leaks(); 1734 #if defined(NCURSES_VERSION) && defined(HAVE__NC_FREE_AND_EXIT) 1735 _nc_free_and_exit(code); 1736 #endif 1737 #endif 1738 1739 if (dialog_state.input == stdin) { 1740 exit(code); 1741 } else { 1742 /* 1743 * Just in case of using --input-fd option, do not 1744 * call atexit functions of ncurses which may hang. 1745 */ 1746 if (dialog_state.input) { 1747 fclose(dialog_state.input); 1748 dialog_state.input = 0; 1749 } 1750 if (dialog_state.pipe_input) { 1751 if (dialog_state.pipe_input != stdin) { 1752 fclose(dialog_state.pipe_input); 1753 dialog_state.pipe_input = 0; 1754 } 1755 } 1756 _exit(code); 1757 } 1758 } 1759 1760 /* quit program killing all tailbg */ 1761 void 1762 dlg_exiterr(const char *fmt,...) 1763 { 1764 int retval; 1765 va_list ap; 1766 1767 end_dialog(); 1768 1769 (void) fputc('\n', stderr); 1770 va_start(ap, fmt); 1771 (void) vfprintf(stderr, fmt, ap); 1772 va_end(ap); 1773 (void) fputc('\n', stderr); 1774 1775 dlg_killall_bg(&retval); 1776 1777 (void) fflush(stderr); 1778 (void) fflush(stdout); 1779 dlg_exit(DLG_EXIT_ERROR); 1780 } 1781 1782 void 1783 dlg_beeping(void) 1784 { 1785 if (dialog_vars.beep_signal) { 1786 (void) beep(); 1787 dialog_vars.beep_signal = 0; 1788 } 1789 } 1790 1791 void 1792 dlg_print_size(int height, int width) 1793 { 1794 if (dialog_vars.print_siz) 1795 fprintf(dialog_state.output, "Size: %d, %d\n", height, width); 1796 } 1797 1798 void 1799 dlg_ctl_size(int height, int width) 1800 { 1801 if (dialog_vars.size_err) { 1802 if ((width > COLS) || (height > LINES)) { 1803 dlg_exiterr("Window too big. (height, width) = (%d, %d). Max allowed (%d, %d).", 1804 height, width, LINES, COLS); 1805 } 1806 #ifdef HAVE_COLOR 1807 else if ((dialog_state.use_shadow) 1808 && ((width > SCOLS || height > SLINES))) { 1809 if ((width <= COLS) && (height <= LINES)) { 1810 /* try again, without shadows */ 1811 dialog_state.use_shadow = 0; 1812 } else { 1813 dlg_exiterr("Window+Shadow too big. (height, width) = (%d, %d). Max allowed (%d, %d).", 1814 height, width, SLINES, SCOLS); 1815 } 1816 } 1817 #endif 1818 } 1819 } 1820 1821 /* 1822 * If the --tab-correct was not selected, convert tabs to single spaces. 1823 */ 1824 void 1825 dlg_tab_correct_str(char *prompt) 1826 { 1827 char *ptr; 1828 1829 if (dialog_vars.tab_correct) { 1830 while ((ptr = strchr(prompt, TAB)) != NULL) { 1831 *ptr = ' '; 1832 prompt = ptr; 1833 } 1834 } 1835 } 1836 1837 void 1838 dlg_calc_listh(int *height, int *list_height, int item_no) 1839 { 1840 /* calculate new height and list_height */ 1841 int rows = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0); 1842 if (rows - (*height) > 0) { 1843 if (rows - (*height) > item_no) 1844 *list_height = item_no; 1845 else 1846 *list_height = rows - (*height); 1847 } 1848 (*height) += (*list_height); 1849 } 1850 1851 /* obsolete */ 1852 int 1853 dlg_calc_listw(int item_no, char **items, int group) 1854 { 1855 int n, i, len1 = 0, len2 = 0; 1856 for (i = 0; i < (item_no * group); i += group) { 1857 if ((n = dlg_count_columns(items[i])) > len1) 1858 len1 = n; 1859 if ((n = dlg_count_columns(items[i + 1])) > len2) 1860 len2 = n; 1861 } 1862 return len1 + len2; 1863 } 1864 1865 int 1866 dlg_calc_list_width(int item_no, DIALOG_LISTITEM * items) 1867 { 1868 int n, i, len1 = 0, len2 = 0; 1869 int bits = ((dialog_vars.no_tags ? 1 : 0) 1870 + (dialog_vars.no_items ? 2 : 0)); 1871 1872 for (i = 0; i < item_no; ++i) { 1873 switch (bits) { 1874 case 0: 1875 /* FALLTHRU */ 1876 case 1: 1877 if ((n = dlg_count_columns(items[i].name)) > len1) 1878 len1 = n; 1879 if ((n = dlg_count_columns(items[i].text)) > len2) 1880 len2 = n; 1881 break; 1882 case 2: 1883 /* FALLTHRU */ 1884 case 3: 1885 if ((n = dlg_count_columns(items[i].name)) > len1) 1886 len1 = n; 1887 break; 1888 } 1889 } 1890 return len1 + len2; 1891 } 1892 1893 char * 1894 dlg_strempty(void) 1895 { 1896 static char empty[] = ""; 1897 return empty; 1898 } 1899 1900 char * 1901 dlg_strclone(const char *cprompt) 1902 { 1903 char *prompt = dlg_malloc(char, strlen(cprompt) + 1); 1904 assert_ptr(prompt, "dlg_strclone"); 1905 strcpy(prompt, cprompt); 1906 return prompt; 1907 } 1908 1909 chtype 1910 dlg_asciibox(chtype ch) 1911 { 1912 chtype result = 0; 1913 1914 if (ch == ACS_ULCORNER) 1915 result = '+'; 1916 else if (ch == ACS_LLCORNER) 1917 result = '+'; 1918 else if (ch == ACS_URCORNER) 1919 result = '+'; 1920 else if (ch == ACS_LRCORNER) 1921 result = '+'; 1922 else if (ch == ACS_HLINE) 1923 result = '-'; 1924 else if (ch == ACS_VLINE) 1925 result = '|'; 1926 else if (ch == ACS_LTEE) 1927 result = '+'; 1928 else if (ch == ACS_RTEE) 1929 result = '+'; 1930 else if (ch == ACS_UARROW) 1931 result = '^'; 1932 else if (ch == ACS_DARROW) 1933 result = 'v'; 1934 1935 return result; 1936 } 1937 1938 chtype 1939 dlg_boxchar(chtype ch) 1940 { 1941 chtype result = dlg_asciibox(ch); 1942 1943 if (result != 0) { 1944 if (dialog_vars.ascii_lines) 1945 ch = result; 1946 else if (dialog_vars.no_lines) 1947 ch = ' '; 1948 } 1949 return ch; 1950 } 1951 1952 int 1953 dlg_box_x_ordinate(int width) 1954 { 1955 int x; 1956 1957 if (dialog_vars.begin_set == 1) { 1958 x = dialog_vars.begin_x; 1959 } else { 1960 /* center dialog box on screen unless --begin-set */ 1961 x = (SCOLS - width) / 2; 1962 } 1963 return x; 1964 } 1965 1966 int 1967 dlg_box_y_ordinate(int height) 1968 { 1969 int y; 1970 1971 if (dialog_vars.begin_set == 1) { 1972 y = dialog_vars.begin_y; 1973 } else { 1974 /* center dialog box on screen unless --begin-set */ 1975 y = (SLINES - height) / 2; 1976 } 1977 return y; 1978 } 1979 1980 void 1981 dlg_draw_title(WINDOW *win, const char *title) 1982 { 1983 if (title != NULL) { 1984 chtype attr = A_NORMAL; 1985 chtype save = dlg_get_attrs(win); 1986 int x = centered(getmaxx(win), title); 1987 1988 (void) wattrset(win, title_attr); 1989 wmove(win, 0, x); 1990 dlg_print_text(win, title, getmaxx(win) - x, &attr); 1991 (void) wattrset(win, save); 1992 } 1993 } 1994 1995 void 1996 dlg_draw_bottom_box2(WINDOW *win, chtype on_left, chtype on_right, chtype on_inside) 1997 { 1998 int width = getmaxx(win); 1999 int height = getmaxy(win); 2000 int i; 2001 2002 (void) wattrset(win, on_left); 2003 (void) wmove(win, height - 3, 0); 2004 (void) waddch(win, dlg_boxchar(ACS_LTEE)); 2005 for (i = 0; i < width - 2; i++) 2006 (void) waddch(win, dlg_boxchar(ACS_HLINE)); 2007 (void) wattrset(win, on_right); 2008 (void) waddch(win, dlg_boxchar(ACS_RTEE)); 2009 (void) wattrset(win, on_inside); 2010 (void) wmove(win, height - 2, 1); 2011 for (i = 0; i < width - 2; i++) 2012 (void) waddch(win, ' '); 2013 } 2014 2015 void 2016 dlg_draw_bottom_box(WINDOW *win) 2017 { 2018 dlg_draw_bottom_box2(win, border_attr, dialog_attr, dialog_attr); 2019 } 2020 2021 /* 2022 * Remove a window, repainting everything else. This would be simpler if we 2023 * used the panel library, but that is not _always_ available. 2024 */ 2025 void 2026 dlg_del_window(WINDOW *win) 2027 { 2028 DIALOG_WINDOWS *p, *q, *r; 2029 2030 /* 2031 * If --keep-window was set, do not delete/repaint the windows. 2032 */ 2033 if (dialog_vars.keep_window) 2034 return; 2035 2036 /* Leave the main window untouched if there are no background windows. 2037 * We do this so the current window will not be cleared on exit, allowing 2038 * things like the infobox demo to run without flicker. 2039 */ 2040 if (dialog_state.getc_callbacks != 0) { 2041 touchwin(stdscr); 2042 wnoutrefresh(stdscr); 2043 } 2044 2045 for (p = dialog_state.all_windows, q = r = 0; p != 0; r = p, p = p->next) { 2046 if (p->normal == win) { 2047 q = p; /* found a match - should be only one */ 2048 if (r == 0) { 2049 dialog_state.all_windows = p->next; 2050 } else { 2051 r->next = p->next; 2052 } 2053 } else { 2054 if (p->shadow != 0) { 2055 touchwin(p->shadow); 2056 wnoutrefresh(p->shadow); 2057 } 2058 touchwin(p->normal); 2059 wnoutrefresh(p->normal); 2060 } 2061 } 2062 2063 if (q) { 2064 if (dialog_state.all_windows != 0) 2065 erase_childs_shadow(q); 2066 del_subwindows(q->normal); 2067 dlg_unregister_window(q->normal); 2068 delwin(q->normal); 2069 free(q); 2070 } 2071 doupdate(); 2072 } 2073 2074 /* 2075 * Create a window, optionally with a shadow. 2076 */ 2077 WINDOW * 2078 dlg_new_window(int height, int width, int y, int x) 2079 { 2080 return dlg_new_modal_window(stdscr, height, width, y, x); 2081 } 2082 2083 /* 2084 * "Modal" windows differ from normal ones by having a shadow in a window 2085 * separate from the standard screen. 2086 */ 2087 WINDOW * 2088 dlg_new_modal_window(WINDOW *parent, int height, int width, int y, int x) 2089 { 2090 WINDOW *win; 2091 DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1); 2092 2093 (void) parent; 2094 if (p == 0 2095 || (win = newwin(height, width, y, x)) == 0) { 2096 dlg_exiterr("Can't make new window at (%d,%d), size (%d,%d).\n", 2097 y, x, height, width); 2098 } 2099 p->next = dialog_state.all_windows; 2100 p->normal = win; 2101 dialog_state.all_windows = p; 2102 #ifdef HAVE_COLOR 2103 if (dialog_state.use_shadow) { 2104 p->shadow = parent; 2105 draw_childs_shadow(p); 2106 } 2107 #endif 2108 2109 (void) keypad(win, TRUE); 2110 return win; 2111 } 2112 2113 /* 2114 * Move/Resize a window, optionally with a shadow. 2115 */ 2116 #ifdef KEY_RESIZE 2117 void 2118 dlg_move_window(WINDOW *win, int height, int width, int y, int x) 2119 { 2120 DIALOG_WINDOWS *p; 2121 2122 if (win != 0) { 2123 dlg_ctl_size(height, width); 2124 2125 if ((p = find_window(win)) != 0) { 2126 (void) wresize(win, height, width); 2127 (void) mvwin(win, y, x); 2128 #ifdef HAVE_COLOR 2129 if (p->shadow != 0) { 2130 if (dialog_state.use_shadow) { 2131 (void) mvwin(p->shadow, y + SHADOW_ROWS, x + SHADOW_COLS); 2132 } else { 2133 p->shadow = 0; 2134 } 2135 } 2136 #endif 2137 (void) refresh(); 2138 2139 #ifdef HAVE_COLOR 2140 draw_childs_shadow(p); 2141 #endif 2142 } 2143 } 2144 } 2145 #endif /* KEY_RESIZE */ 2146 2147 WINDOW * 2148 dlg_sub_window(WINDOW *parent, int height, int width, int y, int x) 2149 { 2150 WINDOW *win; 2151 2152 if ((win = subwin(parent, height, width, y, x)) == 0) { 2153 dlg_exiterr("Can't make sub-window at (%d,%d), size (%d,%d).\n", 2154 y, x, height, width); 2155 } 2156 2157 add_subwindow(parent, win); 2158 (void) keypad(win, TRUE); 2159 return win; 2160 } 2161 2162 /* obsolete */ 2163 int 2164 dlg_default_item(char **items, int llen) 2165 { 2166 int result = 0; 2167 2168 if (dialog_vars.default_item != 0) { 2169 int count = 0; 2170 while (*items != 0) { 2171 if (!strcmp(dialog_vars.default_item, *items)) { 2172 result = count; 2173 break; 2174 } 2175 items += llen; 2176 count++; 2177 } 2178 } 2179 return result; 2180 } 2181 2182 int 2183 dlg_default_listitem(DIALOG_LISTITEM * items) 2184 { 2185 int result = 0; 2186 2187 if (dialog_vars.default_item != 0) { 2188 int count = 0; 2189 while (items->name != 0) { 2190 if (!strcmp(dialog_vars.default_item, items->name)) { 2191 result = count; 2192 break; 2193 } 2194 ++items; 2195 count++; 2196 } 2197 } 2198 return result; 2199 } 2200 2201 /* 2202 * Draw the string for item_help 2203 */ 2204 void 2205 dlg_item_help(const char *txt) 2206 { 2207 if (USE_ITEM_HELP(txt)) { 2208 chtype attr = A_NORMAL; 2209 int y, x; 2210 2211 (void) wattrset(stdscr, itemhelp_attr); 2212 (void) wmove(stdscr, LINES - 1, 0); 2213 (void) wclrtoeol(stdscr); 2214 (void) addch(' '); 2215 dlg_print_text(stdscr, txt, COLS - 1, &attr); 2216 if (itemhelp_attr & A_COLOR) { 2217 /* fill the remainder of the line with the window's attributes */ 2218 getyx(stdscr, y, x); 2219 (void) y; 2220 while (x < COLS) { 2221 (void) addch(' '); 2222 ++x; 2223 } 2224 } 2225 (void) wnoutrefresh(stdscr); 2226 } 2227 } 2228 2229 #ifndef HAVE_STRCASECMP 2230 int 2231 dlg_strcmp(const char *a, const char *b) 2232 { 2233 int ac, bc, cmp; 2234 2235 for (;;) { 2236 ac = UCH(*a++); 2237 bc = UCH(*b++); 2238 if (isalpha(ac) && islower(ac)) 2239 ac = _toupper(ac); 2240 if (isalpha(bc) && islower(bc)) 2241 bc = _toupper(bc); 2242 cmp = ac - bc; 2243 if (ac == 0 || bc == 0 || cmp != 0) 2244 break; 2245 } 2246 return cmp; 2247 } 2248 #endif 2249 2250 /* 2251 * Returns true if 'dst' points to a blank which follows another blank which 2252 * is not a leading blank on a line. 2253 */ 2254 static bool 2255 trim_blank(char *base, char *dst) 2256 { 2257 int count = 0; 2258 2259 while (dst-- != base) { 2260 if (*dst == '\n') { 2261 return FALSE; 2262 } else if (*dst != ' ') { 2263 return (count > 1); 2264 } else { 2265 count++; 2266 } 2267 } 2268 return FALSE; 2269 } 2270 2271 /* 2272 * Change embedded "\n" substrings to '\n' characters and tabs to single 2273 * spaces. If there are no "\n"s, it will strip all extra spaces, for 2274 * justification. If it has "\n"'s, it will preserve extra spaces. If cr_wrap 2275 * is set, it will preserve '\n's. 2276 */ 2277 void 2278 dlg_trim_string(char *s) 2279 { 2280 char *base = s; 2281 char *p1; 2282 char *p = s; 2283 int has_newlines = !dialog_vars.no_nl_expand && (strstr(s, "\\n") != 0); 2284 2285 while (*p != '\0') { 2286 if (*p == TAB && !dialog_vars.nocollapse) 2287 *p = ' '; 2288 2289 if (has_newlines) { /* If prompt contains "\n" strings */ 2290 if (*p == '\\' && *(p + 1) == 'n') { 2291 *s++ = '\n'; 2292 p += 2; 2293 p1 = p; 2294 /* 2295 * Handle end of lines intelligently. If '\n' follows "\n" 2296 * then ignore the '\n'. This eliminates the need to escape 2297 * the '\n' character (no need to use "\n\"). 2298 */ 2299 while (*p1 == ' ') 2300 p1++; 2301 if (*p1 == '\n') 2302 p = p1 + 1; 2303 } else if (*p == '\n') { 2304 if (dialog_vars.cr_wrap) 2305 *s++ = *p++; 2306 else { 2307 /* Replace the '\n' with a space if cr_wrap is not set */ 2308 if (!trim_blank(base, s)) 2309 *s++ = ' '; 2310 p++; 2311 } 2312 } else /* If *p != '\n' */ 2313 *s++ = *p++; 2314 } else if (dialog_vars.trim_whitespace) { 2315 if (*p == ' ') { 2316 if (*(s - 1) != ' ') { 2317 *s++ = ' '; 2318 p++; 2319 } else 2320 p++; 2321 } else if (*p == '\n') { 2322 if (dialog_vars.cr_wrap) 2323 *s++ = *p++; 2324 else if (*(s - 1) != ' ') { 2325 /* Strip '\n's if cr_wrap is not set. */ 2326 *s++ = ' '; 2327 p++; 2328 } else 2329 p++; 2330 } else 2331 *s++ = *p++; 2332 } else { /* If there are no "\n" strings */ 2333 if (*p == ' ' && !dialog_vars.nocollapse) { 2334 if (!trim_blank(base, s)) 2335 *s++ = *p; 2336 p++; 2337 } else 2338 *s++ = *p++; 2339 } 2340 } 2341 2342 *s = '\0'; 2343 } 2344 2345 void 2346 dlg_set_focus(WINDOW *parent, WINDOW *win) 2347 { 2348 if (win != 0) { 2349 (void) wmove(parent, 2350 getpary(win) + getcury(win), 2351 getparx(win) + getcurx(win)); 2352 (void) wnoutrefresh(win); 2353 (void) doupdate(); 2354 } 2355 } 2356 2357 /* 2358 * Returns the nominal maximum buffer size. 2359 */ 2360 int 2361 dlg_max_input(int max_len) 2362 { 2363 if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN) 2364 max_len = dialog_vars.max_input; 2365 2366 return max_len; 2367 } 2368 2369 /* 2370 * Free storage used for the result buffer. 2371 */ 2372 void 2373 dlg_clr_result(void) 2374 { 2375 if (dialog_vars.input_length) { 2376 dialog_vars.input_length = 0; 2377 if (dialog_vars.input_result) 2378 free(dialog_vars.input_result); 2379 } 2380 dialog_vars.input_result = 0; 2381 } 2382 2383 /* 2384 * Setup a fixed-buffer for the result. 2385 */ 2386 char * 2387 dlg_set_result(const char *string) 2388 { 2389 unsigned need = string ? (unsigned) strlen(string) + 1 : 0; 2390 2391 /* inputstr.c needs a fixed buffer */ 2392 if (need < MAX_LEN) 2393 need = MAX_LEN; 2394 2395 /* 2396 * If the buffer is not big enough, allocate a new one. 2397 */ 2398 if (dialog_vars.input_length != 0 2399 || dialog_vars.input_result == 0 2400 || need > MAX_LEN) { 2401 2402 dlg_clr_result(); 2403 2404 dialog_vars.input_length = need; 2405 dialog_vars.input_result = dlg_malloc(char, need); 2406 assert_ptr(dialog_vars.input_result, "dlg_set_result"); 2407 } 2408 2409 strcpy(dialog_vars.input_result, string ? string : ""); 2410 2411 return dialog_vars.input_result; 2412 } 2413 2414 /* 2415 * Accumulate results in dynamically allocated buffer. 2416 * If input_length is zero, it is a MAX_LEN buffer belonging to the caller. 2417 */ 2418 void 2419 dlg_add_result(const char *string) 2420 { 2421 unsigned have = (dialog_vars.input_result 2422 ? (unsigned) strlen(dialog_vars.input_result) 2423 : 0); 2424 unsigned want = (unsigned) strlen(string) + 1 + have; 2425 2426 if ((want >= MAX_LEN) 2427 || (dialog_vars.input_length != 0) 2428 || (dialog_vars.input_result == 0)) { 2429 2430 if (dialog_vars.input_length == 0 2431 || dialog_vars.input_result == 0) { 2432 2433 char *save_result = dialog_vars.input_result; 2434 2435 dialog_vars.input_length = want * 2; 2436 dialog_vars.input_result = dlg_malloc(char, dialog_vars.input_length); 2437 assert_ptr(dialog_vars.input_result, "dlg_add_result malloc"); 2438 dialog_vars.input_result[0] = '\0'; 2439 if (save_result != 0) 2440 strcpy(dialog_vars.input_result, save_result); 2441 } else if (want >= dialog_vars.input_length) { 2442 dialog_vars.input_length = want * 2; 2443 dialog_vars.input_result = dlg_realloc(char, 2444 dialog_vars.input_length, 2445 dialog_vars.input_result); 2446 assert_ptr(dialog_vars.input_result, "dlg_add_result realloc"); 2447 } 2448 } 2449 strcat(dialog_vars.input_result, string); 2450 } 2451 2452 /* 2453 * These are characters that (aside from the quote-delimiter) will have to 2454 * be escaped in a single- or double-quoted string. 2455 */ 2456 #define FIX_SINGLE "\n\\" 2457 #define FIX_DOUBLE FIX_SINGLE "[]{}?*;`~#$^&()|<>" 2458 2459 /* 2460 * Returns the quote-delimiter. 2461 */ 2462 static const char * 2463 quote_delimiter(void) 2464 { 2465 return dialog_vars.single_quoted ? "'" : "\""; 2466 } 2467 2468 /* 2469 * Returns true if we should quote the given string. 2470 */ 2471 static bool 2472 must_quote(char *string) 2473 { 2474 bool code = FALSE; 2475 2476 if (*string != '\0') { 2477 size_t len = strlen(string); 2478 if (strcspn(string, quote_delimiter()) != len) 2479 code = TRUE; 2480 else if (strcspn(string, "\n\t ") != len) 2481 code = TRUE; 2482 else 2483 code = (strcspn(string, FIX_DOUBLE) != len); 2484 } else { 2485 code = TRUE; 2486 } 2487 2488 return code; 2489 } 2490 2491 /* 2492 * Add a quoted string to the result buffer. 2493 */ 2494 void 2495 dlg_add_quoted(char *string) 2496 { 2497 char temp[2]; 2498 const char *my_quote = quote_delimiter(); 2499 const char *must_fix = (dialog_vars.single_quoted 2500 ? FIX_SINGLE 2501 : FIX_DOUBLE); 2502 2503 if (must_quote(string)) { 2504 temp[1] = '\0'; 2505 dlg_add_result(my_quote); 2506 while (*string != '\0') { 2507 temp[0] = *string++; 2508 if (strchr(my_quote, *temp) || strchr(must_fix, *temp)) 2509 dlg_add_result("\\"); 2510 dlg_add_result(temp); 2511 } 2512 dlg_add_result(my_quote); 2513 } else { 2514 dlg_add_result(string); 2515 } 2516 } 2517 2518 /* 2519 * When adding a result, make that depend on whether "--quoted" is used. 2520 */ 2521 void 2522 dlg_add_string(char *string) 2523 { 2524 if (dialog_vars.quoted) { 2525 dlg_add_quoted(string); 2526 } else { 2527 dlg_add_result(string); 2528 } 2529 } 2530 2531 bool 2532 dlg_need_separator(void) 2533 { 2534 bool result = FALSE; 2535 2536 if (dialog_vars.output_separator) { 2537 result = TRUE; 2538 } else if (dialog_vars.input_result && *(dialog_vars.input_result)) { 2539 result = TRUE; 2540 } 2541 return result; 2542 } 2543 2544 void 2545 dlg_add_separator(void) 2546 { 2547 const char *separator = (dialog_vars.separate_output) ? "\n" : " "; 2548 2549 if (dialog_vars.output_separator) 2550 separator = dialog_vars.output_separator; 2551 2552 dlg_add_result(separator); 2553 } 2554 2555 /* 2556 * Some widgets support only one value of a given variable - save/restore the 2557 * global dialog_vars so we can override it consistently. 2558 */ 2559 void 2560 dlg_save_vars(DIALOG_VARS * vars) 2561 { 2562 *vars = dialog_vars; 2563 } 2564 2565 /* 2566 * Most of the data in DIALOG_VARS is normally set by command-line options. 2567 * The input_result member is an exception; it is normally set by the dialog 2568 * library to return result values. 2569 */ 2570 void 2571 dlg_restore_vars(DIALOG_VARS * vars) 2572 { 2573 char *save_result = dialog_vars.input_result; 2574 unsigned save_length = dialog_vars.input_length; 2575 2576 dialog_vars = *vars; 2577 dialog_vars.input_result = save_result; 2578 dialog_vars.input_length = save_length; 2579 } 2580 2581 /* 2582 * Called each time a widget is invoked which may do output, increment a count. 2583 */ 2584 void 2585 dlg_does_output(void) 2586 { 2587 dialog_state.output_count += 1; 2588 } 2589 2590 /* 2591 * Compatibility for different versions of curses. 2592 */ 2593 #if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY)) 2594 int 2595 dlg_getbegx(WINDOW *win) 2596 { 2597 int y, x; 2598 getbegyx(win, y, x); 2599 return x; 2600 } 2601 int 2602 dlg_getbegy(WINDOW *win) 2603 { 2604 int y, x; 2605 getbegyx(win, y, x); 2606 return y; 2607 } 2608 #endif 2609 2610 #if !(defined(HAVE_GETCURX) && defined(HAVE_GETCURY)) 2611 int 2612 dlg_getcurx(WINDOW *win) 2613 { 2614 int y, x; 2615 getyx(win, y, x); 2616 return x; 2617 } 2618 int 2619 dlg_getcury(WINDOW *win) 2620 { 2621 int y, x; 2622 getyx(win, y, x); 2623 return y; 2624 } 2625 #endif 2626 2627 #if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY)) 2628 int 2629 dlg_getmaxx(WINDOW *win) 2630 { 2631 int y, x; 2632 getmaxyx(win, y, x); 2633 return x; 2634 } 2635 int 2636 dlg_getmaxy(WINDOW *win) 2637 { 2638 int y, x; 2639 getmaxyx(win, y, x); 2640 return y; 2641 } 2642 #endif 2643 2644 #if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY)) 2645 int 2646 dlg_getparx(WINDOW *win) 2647 { 2648 int y, x; 2649 getparyx(win, y, x); 2650 return x; 2651 } 2652 int 2653 dlg_getpary(WINDOW *win) 2654 { 2655 int y, x; 2656 getparyx(win, y, x); 2657 return y; 2658 } 2659 #endif 2660 2661 #ifdef NEED_WGETPARENT 2662 WINDOW * 2663 dlg_wgetparent(WINDOW *win) 2664 { 2665 #undef wgetparent 2666 WINDOW *result = 0; 2667 DIALOG_WINDOWS *p; 2668 2669 for (p = dialog_state.all_subwindows; p != 0; p = p->next) { 2670 if (p->shadow == win) { 2671 result = p->normal; 2672 break; 2673 } 2674 } 2675 return result; 2676 } 2677 #endif 2678