1 /* 2 * $Id: util.c,v 1.300 2021/01/17 22:10:56 tom Exp $ 3 * 4 * util.c -- miscellaneous utilities for dialog 5 * 6 * Copyright 2000-2020,2021 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 #include <dlg_internals.h> 30 31 #include <sys/time.h> 32 33 #ifdef HAVE_SETLOCALE 34 #include <locale.h> 35 #endif 36 37 #ifdef NEED_WCHAR_H 38 #include <wchar.h> 39 #endif 40 41 #ifdef HAVE_SYS_PARAM_H 42 #include <sys/param.h> 43 #endif 44 45 #if defined(NCURSES_VERSION) 46 #define CAN_KEEP_TITE 1 47 #elif defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 800000000) 48 #define CAN_KEEP_TITE 1 49 #else 50 #define CAN_KEEP_TITE 0 51 #endif 52 53 #if CAN_KEEP_TITE 54 #if defined(NCURSES_VERSION) && defined(HAVE_NCURSESW_TERM_H) 55 #include <ncursesw/term.h> 56 #elif defined(NCURSES_VERSION) && defined(HAVE_NCURSES_TERM_H) 57 #include <ncurses/term.h> 58 #else 59 #include <term.h> 60 #endif 61 #endif 62 63 #if defined(HAVE_WCHGAT) 64 # if defined(NCURSES_VERSION_PATCH) 65 # if NCURSES_VERSION_PATCH >= 20060715 66 # define USE_WCHGAT 1 67 # else 68 # define USE_WCHGAT 0 69 # endif 70 # else 71 # define USE_WCHGAT 1 72 # endif 73 #else 74 # define USE_WCHGAT 0 75 #endif 76 77 /* globals */ 78 DIALOG_STATE dialog_state; 79 DIALOG_VARS dialog_vars; 80 81 #if !(defined(HAVE_WGETPARENT) && defined(HAVE_WINDOW__PARENT)) 82 #define NEED_WGETPARENT 1 83 #else 84 #undef NEED_WGETPARENT 85 #endif 86 87 #define concat(a,b) a##b 88 89 #ifdef HAVE_RC_FILE 90 #define RC_DATA(name,comment) , #name "_color", comment " color" 91 #else 92 #define RC_DATA(name,comment) /*nothing */ 93 #endif 94 95 #ifdef HAVE_COLOR 96 #include <dlg_colors.h> 97 #ifdef HAVE_RC_FILE2 98 #define COLOR_DATA(upr) , \ 99 concat(DLGC_FG_,upr), \ 100 concat(DLGC_BG_,upr), \ 101 concat(DLGC_HL_,upr), \ 102 concat(DLGC_UL_,upr), \ 103 concat(DLGC_RV_,upr) 104 #else /* HAVE_RC_FILE2 */ 105 #define COLOR_DATA(upr) , \ 106 concat(DLGC_FG_,upr), \ 107 concat(DLGC_BG_,upr), \ 108 concat(DLGC_HL_,upr) 109 #endif /* HAVE_RC_FILE2 */ 110 #else /* HAVE_COLOR */ 111 #define COLOR_DATA(upr) /*nothing */ 112 #endif /* HAVE_COLOR */ 113 114 #define UseShadow(dw) ((dw) != 0 && (dw)->normal != 0 && (dw)->shadow != 0) 115 116 /* 117 * Table of color and attribute values, default is for mono display. 118 * The order matches the DIALOG_ATR() values. 119 */ 120 #define DATA(atr,upr,lwr,cmt) { atr COLOR_DATA(upr) RC_DATA(lwr,cmt) } 121 /* *INDENT-OFF* */ 122 DIALOG_COLORS dlg_color_table[] = 123 { 124 DATA(A_NORMAL, SCREEN, screen, "Screen"), 125 DATA(A_NORMAL, SHADOW, shadow, "Shadow"), 126 DATA(A_REVERSE, DIALOG, dialog, "Dialog box"), 127 DATA(A_REVERSE, TITLE, title, "Dialog box title"), 128 DATA(A_REVERSE, BORDER, border, "Dialog box border"), 129 DATA(A_BOLD, BUTTON_ACTIVE, button_active, "Active button"), 130 DATA(A_DIM, BUTTON_INACTIVE, button_inactive, "Inactive button"), 131 DATA(A_UNDERLINE, BUTTON_KEY_ACTIVE, button_key_active, "Active button key"), 132 DATA(A_UNDERLINE, BUTTON_KEY_INACTIVE, button_key_inactive, "Inactive button key"), 133 DATA(A_NORMAL, BUTTON_LABEL_ACTIVE, button_label_active, "Active button label"), 134 DATA(A_NORMAL, BUTTON_LABEL_INACTIVE, button_label_inactive, "Inactive button label"), 135 DATA(A_REVERSE, INPUTBOX, inputbox, "Input box"), 136 DATA(A_REVERSE, INPUTBOX_BORDER, inputbox_border, "Input box border"), 137 DATA(A_REVERSE, SEARCHBOX, searchbox, "Search box"), 138 DATA(A_REVERSE, SEARCHBOX_TITLE, searchbox_title, "Search box title"), 139 DATA(A_REVERSE, SEARCHBOX_BORDER, searchbox_border, "Search box border"), 140 DATA(A_REVERSE, POSITION_INDICATOR, position_indicator, "File position indicator"), 141 DATA(A_REVERSE, MENUBOX, menubox, "Menu box"), 142 DATA(A_REVERSE, MENUBOX_BORDER, menubox_border, "Menu box border"), 143 DATA(A_REVERSE, ITEM, item, "Item"), 144 DATA(A_NORMAL, ITEM_SELECTED, item_selected, "Selected item"), 145 DATA(A_REVERSE, TAG, tag, "Tag"), 146 DATA(A_REVERSE, TAG_SELECTED, tag_selected, "Selected tag"), 147 DATA(A_NORMAL, TAG_KEY, tag_key, "Tag key"), 148 DATA(A_BOLD, TAG_KEY_SELECTED, tag_key_selected, "Selected tag key"), 149 DATA(A_REVERSE, CHECK, check, "Check box"), 150 DATA(A_REVERSE, CHECK_SELECTED, check_selected, "Selected check box"), 151 DATA(A_REVERSE, UARROW, uarrow, "Up arrow"), 152 DATA(A_REVERSE, DARROW, darrow, "Down arrow"), 153 DATA(A_NORMAL, ITEMHELP, itemhelp, "Item help-text"), 154 DATA(A_BOLD, FORM_ACTIVE_TEXT, form_active_text, "Active form text"), 155 DATA(A_REVERSE, FORM_TEXT, form_text, "Form text"), 156 DATA(A_NORMAL, FORM_ITEM_READONLY, form_item_readonly, "Readonly form item"), 157 DATA(A_REVERSE, GAUGE, gauge, "Dialog box gauge"), 158 DATA(A_REVERSE, BORDER2, border2, "Dialog box border2"), 159 DATA(A_REVERSE, INPUTBOX_BORDER2, inputbox_border2, "Input box border2"), 160 DATA(A_REVERSE, SEARCHBOX_BORDER2, searchbox_border2, "Search box border2"), 161 DATA(A_REVERSE, MENUBOX_BORDER2, menubox_border2, "Menu box border2") 162 }; 163 #undef DATA 164 /* *INDENT-ON* */ 165 166 /* 167 * Maintain a list of subwindows so that we can delete them to cleanup. 168 * More important, this provides a fallback when wgetparent() is not available. 169 */ 170 static void 171 add_subwindow(WINDOW *parent, WINDOW *child) 172 { 173 DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1); 174 175 if (p != 0) { 176 p->normal = parent; 177 p->shadow = child; 178 p->getc_timeout = WTIMEOUT_OFF; 179 p->next = dialog_state.all_subwindows; 180 dialog_state.all_subwindows = p; 181 } 182 } 183 184 static void 185 del_subwindows(WINDOW *parent) 186 { 187 DIALOG_WINDOWS *p = dialog_state.all_subwindows; 188 DIALOG_WINDOWS *q = 0; 189 DIALOG_WINDOWS *r; 190 191 while (p != 0) { 192 if (p->normal == parent) { 193 delwin(p->shadow); 194 r = p->next; 195 if (q == 0) { 196 dialog_state.all_subwindows = r; 197 } else { 198 q->next = r; 199 } 200 free(p); 201 p = r; 202 } else { 203 q = p; 204 p = p->next; 205 } 206 } 207 } 208 209 /* 210 * Display background title if it exists ... 211 */ 212 void 213 dlg_put_backtitle(void) 214 { 215 216 if (dialog_vars.backtitle != NULL) { 217 chtype attr = A_NORMAL; 218 int backwidth = dlg_count_columns(dialog_vars.backtitle); 219 int i; 220 221 dlg_attrset(stdscr, screen_attr); 222 (void) wmove(stdscr, 0, 1); 223 dlg_print_text(stdscr, dialog_vars.backtitle, COLS - 2, &attr); 224 for (i = 0; i < COLS - backwidth; i++) 225 (void) waddch(stdscr, ' '); 226 (void) wmove(stdscr, 1, 1); 227 for (i = 0; i < COLS - 2; i++) 228 (void) waddch(stdscr, dlg_boxchar(ACS_HLINE)); 229 } 230 231 (void) wnoutrefresh(stdscr); 232 } 233 234 /* 235 * Set window to attribute 'attr'. There are more efficient ways to do this, 236 * but will not work on older/buggy ncurses versions. 237 */ 238 void 239 dlg_attr_clear(WINDOW *win, int height, int width, chtype attr) 240 { 241 int i, j; 242 243 dlg_attrset(win, attr); 244 for (i = 0; i < height; i++) { 245 (void) wmove(win, i, 0); 246 for (j = 0; j < width; j++) 247 (void) waddch(win, ' '); 248 } 249 (void) touchwin(win); 250 } 251 252 void 253 dlg_clear(void) 254 { 255 dlg_attr_clear(stdscr, LINES, COLS, screen_attr); 256 } 257 258 #ifdef KEY_RESIZE 259 void 260 _dlg_resize_cleanup(WINDOW *w) 261 { 262 dlg_clear(); 263 dlg_put_backtitle(); 264 dlg_del_window(w); 265 dlg_mouse_free_regions(); 266 } 267 #endif /* KEY_RESIZE */ 268 269 #define isprivate(s) ((s) != 0 && strstr(s, "\033[?") != 0) 270 271 #define TTY_DEVICE "/dev/tty" 272 273 /* 274 * If $DIALOG_TTY exists, allow the program to try to open the terminal 275 * directly when stdout is redirected. By default we require the "--stdout" 276 * option to be given, but some scripts were written making use of the 277 * behavior of dialog which tried opening the terminal anyway. 278 */ 279 #define dialog_tty() (dlg_getenv_num("DIALOG_TTY", (int *)0) > 0) 280 281 /* 282 * Open the terminal directly. If one of stdin, stdout or stderr really points 283 * to a tty, use it. Otherwise give up and open /dev/tty. 284 */ 285 static int 286 open_terminal(char **result, int mode) 287 { 288 const char *device = TTY_DEVICE; 289 if (!isatty(fileno(stderr)) 290 || (device = ttyname(fileno(stderr))) == 0) { 291 if (!isatty(fileno(stdout)) 292 || (device = ttyname(fileno(stdout))) == 0) { 293 if (!isatty(fileno(stdin)) 294 || (device = ttyname(fileno(stdin))) == 0) { 295 device = TTY_DEVICE; 296 } 297 } 298 } 299 *result = dlg_strclone(device); 300 return open(device, mode); 301 } 302 303 #if CAN_KEEP_TITE 304 static int 305 my_putc(int ch) 306 { 307 char buffer[2]; 308 int fd = fileno(dialog_state.screen_output); 309 310 buffer[0] = (char) ch; 311 return (int) write(fd, buffer, (size_t) 1); 312 } 313 #endif 314 315 /* 316 * Do some initialization for dialog. 317 * 318 * 'input' is the real tty input of dialog. Usually it is stdin, but if 319 * --input-fd option is used, it may be anything. 320 * 321 * 'output' is where dialog will send its result. Usually it is stderr, but 322 * if --stdout or --output-fd is used, it may be anything. We are concerned 323 * mainly with the case where it happens to be the same as stdout. 324 */ 325 void 326 init_dialog(FILE *input, FILE *output) 327 { 328 int fd1, fd2; 329 char *device = 0; 330 331 setlocale(LC_ALL, ""); 332 333 dialog_state.output = output; 334 if (dialog_state.tab_len == 0) 335 dialog_state.tab_len = TAB_LEN; 336 if (dialog_state.aspect_ratio == 0) 337 dialog_state.aspect_ratio = DEFAULT_ASPECT_RATIO; 338 #ifdef HAVE_COLOR 339 dialog_state.use_colors = USE_COLORS; /* use colors by default? */ 340 dialog_state.use_shadow = USE_SHADOW; /* shadow dialog boxes by default? */ 341 #endif 342 343 #ifdef HAVE_RC_FILE 344 if (dlg_parse_rc() == -1) /* Read the configuration file */ 345 dlg_exiterr("init_dialog: dlg_parse_rc"); 346 #endif 347 348 /* 349 * Some widgets (such as gauge) may read from the standard input. Pipes 350 * only connect stdout/stdin, so there is not much choice. But reading a 351 * pipe would get in the way of curses' normal reading stdin for getch. 352 * 353 * As in the --stdout (see below), reopening the terminal does not always 354 * work properly. dialog provides a --pipe-fd option for this purpose. We 355 * test that case first (differing fileno's for input/stdin). If the 356 * fileno's are equal, but we're not reading from a tty, see if we can open 357 * /dev/tty. 358 */ 359 dialog_state.pipe_input = stdin; 360 if (fileno(input) != fileno(stdin)) { 361 if ((fd1 = dup(fileno(input))) >= 0 362 && (fd2 = dup(fileno(stdin))) >= 0) { 363 (void) dup2(fileno(input), fileno(stdin)); 364 dialog_state.pipe_input = fdopen(fd2, "r"); 365 if (fileno(stdin) != 0) /* some functions may read fd #0 */ 366 (void) dup2(fileno(stdin), 0); 367 } else { 368 dlg_exiterr("cannot open tty-input"); 369 } 370 close(fd1); 371 } else if (!isatty(fileno(stdin))) { 372 if ((fd1 = open_terminal(&device, O_RDONLY)) >= 0) { 373 if ((fd2 = dup(fileno(stdin))) >= 0) { 374 dialog_state.pipe_input = fdopen(fd2, "r"); 375 if (freopen(device, "r", stdin) == 0) 376 dlg_exiterr("cannot open tty-input"); 377 if (fileno(stdin) != 0) /* some functions may read fd #0 */ 378 (void) dup2(fileno(stdin), 0); 379 } 380 close(fd1); 381 } 382 free(device); 383 } 384 385 /* 386 * If stdout is not a tty and dialog is called with the --stdout option, we 387 * have to provide for a way to write to the screen. 388 * 389 * The curses library normally writes its output to stdout, leaving stderr 390 * free for scripting. Scripts are simpler when stdout is redirected. The 391 * newterm function is useful; it allows us to specify where the output 392 * goes. Reopening the terminal is not portable since several 393 * configurations do not allow this to work properly: 394 * 395 * a) some getty implementations (and possibly broken tty drivers, e.g., on 396 * HPUX 10 and 11) cause stdin to act as if it is still in cooked mode 397 * even though results from ioctl's state that it is successfully 398 * altered to raw mode. Broken is the proper term. 399 * 400 * b) the user may not have permissions on the device, e.g., if one su's 401 * from the login user to another non-privileged user. 402 */ 403 if (!isatty(fileno(stdout)) 404 && (fileno(stdout) == fileno(output) || dialog_tty())) { 405 if ((fd1 = open_terminal(&device, O_WRONLY)) >= 0 406 && (dialog_state.screen_output = fdopen(fd1, "w")) != 0) { 407 if (newterm(NULL, dialog_state.screen_output, stdin) == 0) { 408 dlg_exiterr("cannot initialize curses"); 409 } 410 free(device); 411 } else { 412 dlg_exiterr("cannot open tty-output"); 413 } 414 } else { 415 dialog_state.screen_output = stdout; 416 (void) initscr(); 417 } 418 dlg_keep_tite(dialog_state.screen_output); 419 #ifdef HAVE_FLUSHINP 420 (void) flushinp(); 421 #endif 422 (void) keypad(stdscr, TRUE); 423 (void) cbreak(); 424 (void) noecho(); 425 426 if (!dialog_state.no_mouse) { 427 mouse_open(); 428 } 429 430 dialog_state.screen_initialized = TRUE; 431 432 #ifdef HAVE_COLOR 433 if (dialog_state.use_colors || dialog_state.use_shadow) 434 dlg_color_setup(); /* Set up colors */ 435 #endif 436 437 /* Set screen to screen attribute */ 438 dlg_clear(); 439 } 440 441 void 442 dlg_keep_tite(FILE *output) 443 { 444 if (!dialog_vars.keep_tite) { 445 #if CAN_KEEP_TITE 446 /* 447 * Cancel xterm's alternate-screen mode. 448 */ 449 if ((fileno(output) != fileno(stdout) 450 || isatty(fileno(output))) 451 && key_mouse != 0 /* xterm and kindred */ 452 && isprivate(enter_ca_mode) 453 && isprivate(exit_ca_mode)) { 454 FILE *save = dialog_state.screen_output; 455 456 /* 457 * initscr() or newterm() already wrote enter_ca_mode as a side 458 * effect of initializing the screen. It would be nice to not even 459 * do that, but we do not really have access to the correct copy of 460 * the terminfo description until those functions have been 461 * invoked. 462 */ 463 (void) refresh(); 464 dialog_state.screen_output = output; 465 (void) tputs(exit_ca_mode, 0, my_putc); 466 (void) tputs(clear_screen, 0, my_putc); 467 dialog_state.screen_output = save; 468 469 /* 470 * Prevent ncurses from switching "back" to the normal screen when 471 * exiting from dialog. That would move the cursor to the original 472 * location saved in xterm. Normally curses sets the cursor 473 * position to the first line after the display, but the alternate 474 * screen switching is done after that point. 475 * 476 * Cancelling the strings altogether also works around the buggy 477 * implementation of alternate-screen in rxvt, etc., which clear 478 * more of the display than they should. 479 */ 480 enter_ca_mode = 0; 481 exit_ca_mode = 0; 482 } 483 #else 484 /* 485 * For other implementations, there are no useful answers: 486 * + SVr4 curses "could" support a similar approach, but the clue about 487 * xterm is absent from its terminal database. 488 * + PDCurses does not provide terminfo. 489 */ 490 (void) output; 491 #endif 492 } 493 } 494 495 #ifdef HAVE_COLOR 496 static int defined_colors = 1; /* pair-0 is reserved */ 497 /* 498 * Setup for color display 499 */ 500 void 501 dlg_color_setup(void) 502 { 503 if (has_colors()) { /* Terminal supports color? */ 504 unsigned i; 505 506 (void) start_color(); 507 508 #if defined(HAVE_USE_DEFAULT_COLORS) 509 use_default_colors(); 510 #endif 511 512 #if defined(__NetBSD__) && defined(_CURSES_) 513 #define C_ATTR(x,y) (((x) != 0 ? A_BOLD : 0) | COLOR_PAIR((y))) 514 /* work around bug in NetBSD curses */ 515 for (i = 0; i < sizeof(dlg_color_table) / 516 sizeof(dlg_color_table[0]); i++) { 517 518 /* Initialize color pairs */ 519 (void) init_pair(i + 1, 520 dlg_color_table[i].fg, 521 dlg_color_table[i].bg); 522 523 /* Setup color attributes */ 524 dlg_color_table[i].atr = C_ATTR(dlg_color_table[i].hilite, i + 1); 525 } 526 defined_colors = i + 1; 527 #else 528 for (i = 0; i < sizeof(dlg_color_table) / 529 sizeof(dlg_color_table[0]); i++) { 530 531 /* Initialize color pairs */ 532 chtype atr = dlg_color_pair(dlg_color_table[i].fg, 533 dlg_color_table[i].bg); 534 535 atr |= (dlg_color_table[i].hilite ? A_BOLD : 0); 536 #ifdef HAVE_RC_FILE2 537 atr |= (dlg_color_table[i].ul ? A_UNDERLINE : 0); 538 atr |= (dlg_color_table[i].rv ? A_REVERSE : 0); 539 #endif /* HAVE_RC_FILE2 */ 540 541 dlg_color_table[i].atr = atr; 542 } 543 #endif 544 } else { 545 dialog_state.use_colors = FALSE; 546 dialog_state.use_shadow = FALSE; 547 } 548 } 549 550 int 551 dlg_color_count(void) 552 { 553 return TableSize(dlg_color_table); 554 } 555 556 /* 557 * Wrapper for getattrs(), or the more cumbersome X/Open wattr_get(). 558 */ 559 chtype 560 dlg_get_attrs(WINDOW *win) 561 { 562 chtype result; 563 #ifdef HAVE_GETATTRS 564 result = (chtype) getattrs(win); 565 #else 566 attr_t my_result; 567 short my_pair; 568 wattr_get(win, &my_result, &my_pair, NULL); 569 result = my_result; 570 #endif 571 return result; 572 } 573 574 /* 575 * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we 576 * have (or can) define a pair with the given color as foreground on the 577 * window's defined background. 578 */ 579 chtype 580 dlg_color_pair(int foreground, int background) 581 { 582 chtype result = 0; 583 int pair; 584 short fg, bg; 585 bool found = FALSE; 586 587 for (pair = 1; pair < defined_colors; ++pair) { 588 if (pair_content((short) pair, &fg, &bg) != ERR 589 && fg == foreground 590 && bg == background) { 591 result = (chtype) COLOR_PAIR(pair); 592 found = TRUE; 593 break; 594 } 595 } 596 if (!found && (defined_colors + 1) < COLOR_PAIRS) { 597 pair = defined_colors++; 598 (void) init_pair((short) pair, (short) foreground, (short) background); 599 result = (chtype) COLOR_PAIR(pair); 600 } 601 return result; 602 } 603 604 /* 605 * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we 606 * have (or can) define a pair with the given color as foreground on the 607 * window's defined background. 608 */ 609 static chtype 610 define_color(WINDOW *win, int foreground) 611 { 612 short fg, bg, background; 613 if (dialog_state.text_only) { 614 background = COLOR_BLACK; 615 } else { 616 chtype attrs = dlg_get_attrs(win); 617 int pair; 618 619 if ((pair = PAIR_NUMBER(attrs)) != 0 620 && pair_content((short) pair, &fg, &bg) != ERR) { 621 background = bg; 622 } else { 623 background = COLOR_BLACK; 624 } 625 } 626 return dlg_color_pair(foreground, background); 627 } 628 #endif 629 630 /* 631 * End using dialog functions. 632 */ 633 void 634 end_dialog(void) 635 { 636 if (dialog_state.screen_initialized) { 637 dialog_state.screen_initialized = FALSE; 638 if (dialog_vars.erase_on_exit) { 639 /* 640 * Clear the screen to the native background color, and leave the 641 * terminal cursor at the lower-left corner of the screen. 642 */ 643 werase(stdscr); 644 wrefresh(stdscr); 645 } 646 mouse_close(); 647 (void) endwin(); 648 (void) fflush(stdout); 649 } 650 } 651 652 #define ESCAPE_LEN 3 653 #define isOurEscape(p) (((p)[0] == '\\') && ((p)[1] == 'Z') && ((p)[2] != 0)) 654 655 int 656 dlg_count_real_columns(const char *text) 657 { 658 int result = 0; 659 if (*text) { 660 result = dlg_count_columns(text); 661 if (result && dialog_vars.colors) { 662 int hidden = 0; 663 while (*text) { 664 if (dialog_vars.colors && isOurEscape(text)) { 665 hidden += ESCAPE_LEN; 666 text += ESCAPE_LEN; 667 } else { 668 ++text; 669 } 670 } 671 result -= hidden; 672 } 673 } 674 return result; 675 } 676 677 static int 678 centered(int width, const char *string) 679 { 680 int need = dlg_count_real_columns(string); 681 int left; 682 683 left = (width - need) / 2 - 1; 684 if (left < 0) 685 left = 0; 686 return left; 687 } 688 689 #ifdef USE_WIDE_CURSES 690 static bool 691 is_combining(const char *txt, int *combined) 692 { 693 bool result = FALSE; 694 695 if (*combined == 0) { 696 if (UCH(*txt) >= 128) { 697 wchar_t wch; 698 mbstate_t state; 699 size_t given = strlen(txt); 700 size_t len; 701 702 memset(&state, 0, sizeof(state)); 703 len = mbrtowc(&wch, txt, given, &state); 704 if ((int) len > 0 && wcwidth(wch) == 0) { 705 *combined = (int) len - 1; 706 result = TRUE; 707 } 708 } 709 } else { 710 result = TRUE; 711 *combined -= 1; 712 } 713 return result; 714 } 715 #endif 716 717 /* 718 * Print the name (tag) or text from a DIALOG_LISTITEM, highlighting the 719 * first character if selected. 720 */ 721 void 722 dlg_print_listitem(WINDOW *win, 723 const char *text, 724 int climit, 725 bool first, 726 int selected) 727 { 728 chtype attr = A_NORMAL; 729 int limit; 730 chtype attrs[4]; 731 732 if (text == 0) 733 text = ""; 734 735 if (first && !dialog_vars.no_hot_list) { 736 const int *indx = dlg_index_wchars(text); 737 attrs[3] = tag_key_selected_attr; 738 attrs[2] = tag_key_attr; 739 attrs[1] = tag_selected_attr; 740 attrs[0] = tag_attr; 741 742 dlg_attrset(win, selected ? attrs[3] : attrs[2]); 743 if (*text != '\0') { 744 (void) waddnstr(win, text, indx[1]); 745 746 if ((int) strlen(text) > indx[1]) { 747 limit = dlg_limit_columns(text, climit, 1); 748 if (limit > 1) { 749 dlg_attrset(win, selected ? attrs[1] : attrs[0]); 750 (void) waddnstr(win, 751 text + indx[1], 752 indx[limit] - indx[1]); 753 } 754 } 755 } 756 } else { 757 const int *cols; 758 759 attrs[1] = item_selected_attr; 760 attrs[0] = item_attr; 761 762 cols = dlg_index_columns(text); 763 limit = dlg_limit_columns(text, climit, 0); 764 765 if (limit > 0) { 766 dlg_attrset(win, selected ? attrs[1] : attrs[0]); 767 dlg_print_text(win, text, cols[limit], &attr); 768 } 769 } 770 } 771 772 /* 773 * Print up to 'cols' columns from 'text', optionally rendering our escape 774 * sequence for attributes and color. 775 */ 776 void 777 dlg_print_text(WINDOW *win, const char *txt, int cols, chtype *attr) 778 { 779 int y_origin, x_origin; 780 int y_before, x_before = 0; 781 int y_after, x_after; 782 int tabbed = 0; 783 bool ended = FALSE; 784 #ifdef USE_WIDE_CURSES 785 int combined = 0; 786 #endif 787 788 if (dialog_state.text_only) { 789 y_origin = y_after = 0; 790 x_origin = x_after = 0; 791 } else { 792 y_after = 0; 793 x_after = 0; 794 getyx(win, y_origin, x_origin); 795 } 796 while (cols > 0 && (*txt != '\0')) { 797 bool thisTab; 798 chtype useattr; 799 800 if (dialog_vars.colors) { 801 while (isOurEscape(txt)) { 802 int code; 803 804 txt += 2; 805 switch (code = CharOf(*txt)) { 806 #ifdef HAVE_COLOR 807 case '0': 808 case '1': 809 case '2': 810 case '3': 811 case '4': 812 case '5': 813 case '6': 814 case '7': 815 *attr &= ~A_COLOR; 816 *attr |= define_color(win, code - '0'); 817 break; 818 #endif 819 case 'B': 820 *attr &= ~A_BOLD; 821 break; 822 case 'b': 823 *attr |= A_BOLD; 824 break; 825 case 'R': 826 *attr &= ~A_REVERSE; 827 break; 828 case 'r': 829 *attr |= A_REVERSE; 830 break; 831 case 'U': 832 *attr &= ~A_UNDERLINE; 833 break; 834 case 'u': 835 *attr |= A_UNDERLINE; 836 break; 837 case 'n': 838 *attr = A_NORMAL; 839 break; 840 default: 841 break; 842 } 843 ++txt; 844 } 845 } 846 if (ended || *txt == '\n' || *txt == '\0') 847 break; 848 useattr = (*attr) & A_ATTRIBUTES; 849 #ifdef HAVE_COLOR 850 /* 851 * Prevent this from making text invisible when the foreground and 852 * background colors happen to be the same, and there's no bold 853 * attribute. 854 */ 855 if ((useattr & A_COLOR) != 0 && (useattr & A_BOLD) == 0) { 856 short pair = (short) PAIR_NUMBER(useattr); 857 short fg, bg; 858 if (pair_content(pair, &fg, &bg) != ERR 859 && fg == bg) { 860 useattr &= ~A_COLOR; 861 useattr |= dlg_color_pair(fg, ((bg == COLOR_BLACK) 862 ? COLOR_WHITE 863 : COLOR_BLACK)); 864 } 865 } 866 #endif 867 /* 868 * Write the character, using curses to tell exactly how wide it 869 * is. If it is a tab, discount that, since the caller thinks 870 * tabs are nonprinting, and curses will expand tabs to one or 871 * more blanks. 872 */ 873 thisTab = (CharOf(*txt) == TAB); 874 if (dialog_state.text_only) { 875 x_before = x_after; 876 } else { 877 if (thisTab) { 878 getyx(win, y_before, x_before); 879 (void) y_before; 880 } 881 } 882 if (dialog_state.text_only) { 883 int ch = CharOf(*txt++); 884 if (thisTab) { 885 while ((x_after++) % 8) { 886 fputc(' ', dialog_state.output); 887 } 888 } else { 889 fputc(ch, dialog_state.output); 890 x_after++; /* FIXME: handle meta per locale */ 891 } 892 } else { 893 (void) waddch(win, CharOf(*txt++) | useattr); 894 getyx(win, y_after, x_after); 895 } 896 if (thisTab && (y_after == y_origin)) 897 tabbed += (x_after - x_before); 898 if ((y_after != y_origin) || 899 (x_after >= (cols + tabbed + x_origin) 900 #ifdef USE_WIDE_CURSES 901 && !is_combining(txt, &combined) 902 #endif 903 )) { 904 ended = TRUE; 905 } 906 } 907 if (dialog_state.text_only) { 908 fputc('\n', dialog_state.output); 909 } 910 } 911 912 /* 913 * Print one line of the prompt in the window within the limits of the 914 * specified right margin. The line will end on a word boundary and a pointer 915 * to the start of the next line is returned, or a NULL pointer if the end of 916 * *prompt is reached. 917 */ 918 const char * 919 dlg_print_line(WINDOW *win, 920 chtype *attr, 921 const char *prompt, 922 int lm, int rm, int *x) 923 { 924 const char *wrap_ptr; 925 const char *test_ptr; 926 const char *hide_ptr = 0; 927 const int *cols = dlg_index_columns(prompt); 928 const int *indx = dlg_index_wchars(prompt); 929 int wrap_inx = 0; 930 int test_inx = 0; 931 int cur_x = lm; 932 int hidden = 0; 933 int limit = dlg_count_wchars(prompt); 934 int n; 935 int tabbed = 0; 936 937 *x = 1; 938 939 /* 940 * Set *test_ptr to the end of the line or the right margin (rm), whichever 941 * is less, and set wrap_ptr to the end of the last word in the line. 942 */ 943 for (n = 0; n < limit; ++n) { 944 int ch = *(test_ptr = prompt + indx[test_inx]); 945 if (ch == '\n' || ch == '\0' || cur_x >= (rm + hidden)) 946 break; 947 if (ch == TAB && n == 0) { 948 tabbed = 8; /* workaround for leading tabs */ 949 } else if (isblank(UCH(ch)) 950 && n != 0 951 && !isblank(UCH(prompt[indx[n - 1]]))) { 952 wrap_inx = n; 953 *x = cur_x; 954 } else if (dialog_vars.colors && isOurEscape(test_ptr)) { 955 hide_ptr = test_ptr; 956 hidden += ESCAPE_LEN; 957 n += (ESCAPE_LEN - 1); 958 } 959 cur_x = lm + tabbed + cols[n + 1]; 960 if (cur_x > (rm + hidden)) 961 break; 962 test_inx = n + 1; 963 } 964 965 /* 966 * If the line doesn't reach the right margin in the middle of a word, then 967 * we don't have to wrap it at the end of the previous word. 968 */ 969 test_ptr = prompt + indx[test_inx]; 970 if (*test_ptr == '\n' || isblank(UCH(*test_ptr)) || *test_ptr == '\0') { 971 wrap_inx = test_inx; 972 while (wrap_inx > 0 && isblank(UCH(prompt[indx[wrap_inx - 1]]))) { 973 wrap_inx--; 974 } 975 *x = lm + indx[wrap_inx]; 976 } else if (*x == 1 && cur_x >= rm) { 977 /* 978 * If the line has no spaces, then wrap it anyway at the right margin 979 */ 980 *x = rm; 981 wrap_inx = test_inx; 982 } 983 wrap_ptr = prompt + indx[wrap_inx]; 984 #ifdef USE_WIDE_CURSES 985 if (UCH(*wrap_ptr) >= 128) { 986 int combined = 0; 987 while (is_combining(wrap_ptr, &combined)) { 988 ++wrap_ptr; 989 } 990 } 991 #endif 992 993 /* 994 * If we found hidden text past the last point that we will display, 995 * discount that from the displayed length. 996 */ 997 if ((hide_ptr != 0) && (hide_ptr >= wrap_ptr)) { 998 hidden -= ESCAPE_LEN; 999 test_ptr = wrap_ptr; 1000 while (test_ptr < wrap_ptr) { 1001 if (dialog_vars.colors && isOurEscape(test_ptr)) { 1002 hidden -= ESCAPE_LEN; 1003 test_ptr += ESCAPE_LEN; 1004 } else { 1005 ++test_ptr; 1006 } 1007 } 1008 } 1009 1010 /* 1011 * Print the line if we have a window pointer. Otherwise this routine 1012 * is just being called for sizing the window. 1013 */ 1014 if (dialog_state.text_only || win) { 1015 dlg_print_text(win, prompt, (cols[wrap_inx] - hidden), attr); 1016 } 1017 1018 /* *x tells the calling function how long the line was */ 1019 if (*x == 1) { 1020 *x = rm; 1021 } 1022 1023 *x -= hidden; 1024 1025 /* Find the start of the next line and return a pointer to it */ 1026 test_ptr = wrap_ptr; 1027 while (isblank(UCH(*test_ptr))) 1028 test_ptr++; 1029 if (*test_ptr == '\n') 1030 test_ptr++; 1031 dlg_finish_string(prompt); 1032 return (test_ptr); 1033 } 1034 1035 static void 1036 justify_text(WINDOW *win, 1037 const char *prompt, 1038 int limit_y, 1039 int limit_x, 1040 int *high, int *wide) 1041 { 1042 chtype attr = A_NORMAL; 1043 int x; 1044 int y = MARGIN; 1045 int max_x = 2; 1046 int lm = (2 * MARGIN); /* left margin (box-border plus a space) */ 1047 int rm = limit_x; /* right margin */ 1048 int bm = limit_y; /* bottom margin */ 1049 int last_y = 0, last_x = 0; 1050 1051 dialog_state.text_height = 0; 1052 dialog_state.text_width = 0; 1053 if (dialog_state.text_only || win) { 1054 rm -= (2 * MARGIN); 1055 bm -= (2 * MARGIN); 1056 } 1057 if (prompt == 0) 1058 prompt = ""; 1059 1060 if (win != 0) 1061 getyx(win, last_y, last_x); 1062 while (y <= bm && *prompt) { 1063 x = lm; 1064 1065 if (*prompt == '\n') { 1066 while (*prompt == '\n' && y < bm) { 1067 if (*(prompt + 1) != '\0') { 1068 ++y; 1069 if (win != 0) 1070 (void) wmove(win, y, lm); 1071 } 1072 prompt++; 1073 } 1074 } else if (win != 0) 1075 (void) wmove(win, y, lm); 1076 1077 if (*prompt) { 1078 prompt = dlg_print_line(win, &attr, prompt, lm, rm, &x); 1079 if (win != 0) 1080 getyx(win, last_y, last_x); 1081 } 1082 if (*prompt) { 1083 ++y; 1084 if (win != 0) 1085 (void) wmove(win, y, lm); 1086 } 1087 max_x = MAX(max_x, x); 1088 } 1089 /* Move back to the last position after drawing prompt, for msgbox. */ 1090 if (win != 0) 1091 (void) wmove(win, last_y, last_x); 1092 1093 /* Set the final height and width for the calling function */ 1094 if (high != 0) 1095 *high = y; 1096 if (wide != 0) 1097 *wide = max_x; 1098 } 1099 1100 /* 1101 * Print a string of text in a window, automatically wrap around to the next 1102 * line if the string is too long to fit on one line. Note that the string may 1103 * contain embedded newlines. 1104 */ 1105 void 1106 dlg_print_autowrap(WINDOW *win, const char *prompt, int height, int width) 1107 { 1108 justify_text(win, prompt, 1109 height, 1110 width, 1111 (int *) 0, (int *) 0); 1112 } 1113 1114 /* 1115 * Display the message in a scrollable window. Actually the way it works is 1116 * that we create a "tall" window of the proper width, let the text wrap within 1117 * that, and copy a slice of the result to the dialog. 1118 * 1119 * It works for ncurses. Other curses implementations show only blanks (Tru64) 1120 * or garbage (NetBSD). 1121 */ 1122 int 1123 dlg_print_scrolled(WINDOW *win, 1124 const char *prompt, 1125 int offset, 1126 int height, 1127 int width, 1128 int pauseopt) 1129 { 1130 int oldy, oldx; 1131 int last = 0; 1132 1133 (void) pauseopt; /* used only for ncurses */ 1134 1135 getyx(win, oldy, oldx); 1136 #ifdef NCURSES_VERSION 1137 if (pauseopt) { 1138 int wide = width - (2 * MARGIN); 1139 int high = LINES; 1140 int len; 1141 WINDOW *dummy; 1142 1143 #if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20040417 1144 /* 1145 * If we're not limited by the screensize, allow text to possibly be 1146 * one character per line. 1147 */ 1148 if ((len = dlg_count_columns(prompt)) > high) 1149 high = len; 1150 #endif 1151 dummy = newwin(high, width, 0, 0); 1152 if (dummy == 0) { 1153 dlg_attrset(win, dialog_attr); 1154 dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width); 1155 last = 0; 1156 } else { 1157 int y, x; 1158 1159 wbkgdset(dummy, dialog_attr | ' '); 1160 dlg_attrset(dummy, dialog_attr); 1161 werase(dummy); 1162 dlg_print_autowrap(dummy, prompt, high, width); 1163 getyx(dummy, y, x); 1164 (void) x; 1165 1166 copywin(dummy, /* srcwin */ 1167 win, /* dstwin */ 1168 offset + MARGIN, /* sminrow */ 1169 MARGIN, /* smincol */ 1170 MARGIN, /* dminrow */ 1171 MARGIN, /* dmincol */ 1172 height, /* dmaxrow */ 1173 wide, /* dmaxcol */ 1174 FALSE); 1175 1176 delwin(dummy); 1177 1178 /* if the text is incomplete, or we have scrolled, show the percentage */ 1179 if (y > 0 && wide > 4) { 1180 int percent = (int) ((height + offset) * 100.0 / y); 1181 1182 if (percent < 0) 1183 percent = 0; 1184 if (percent > 100) 1185 percent = 100; 1186 1187 if (offset != 0 || percent != 100) { 1188 char buffer[5]; 1189 1190 dlg_attrset(win, position_indicator_attr); 1191 (void) wmove(win, MARGIN + height, wide - 4); 1192 (void) sprintf(buffer, "%d%%", percent); 1193 (void) waddstr(win, buffer); 1194 if ((len = (int) strlen(buffer)) < 4) { 1195 dlg_attrset(win, border_attr); 1196 whline(win, dlg_boxchar(ACS_HLINE), 4 - len); 1197 } 1198 } 1199 } 1200 last = (y - height); 1201 } 1202 } else 1203 #endif 1204 { 1205 (void) offset; 1206 dlg_attrset(win, dialog_attr); 1207 dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width); 1208 last = 0; 1209 } 1210 wmove(win, oldy, oldx); 1211 return last; 1212 } 1213 1214 int 1215 dlg_check_scrolled(int key, int last, int page, bool * show, int *offset) 1216 { 1217 int code = 0; 1218 1219 *show = FALSE; 1220 1221 switch (key) { 1222 case DLGK_PAGE_FIRST: 1223 if (*offset > 0) { 1224 *offset = 0; 1225 *show = TRUE; 1226 } 1227 break; 1228 case DLGK_PAGE_LAST: 1229 if (*offset < last) { 1230 *offset = last; 1231 *show = TRUE; 1232 } 1233 break; 1234 case DLGK_GRID_UP: 1235 if (*offset > 0) { 1236 --(*offset); 1237 *show = TRUE; 1238 } 1239 break; 1240 case DLGK_GRID_DOWN: 1241 if (*offset < last) { 1242 ++(*offset); 1243 *show = TRUE; 1244 } 1245 break; 1246 case DLGK_PAGE_PREV: 1247 if (*offset > 0) { 1248 *offset -= page; 1249 if (*offset < 0) 1250 *offset = 0; 1251 *show = TRUE; 1252 } 1253 break; 1254 case DLGK_PAGE_NEXT: 1255 if (*offset < last) { 1256 *offset += page; 1257 if (*offset > last) 1258 *offset = last; 1259 *show = TRUE; 1260 } 1261 break; 1262 default: 1263 code = -1; 1264 break; 1265 } 1266 return code; 1267 } 1268 1269 /* 1270 * Calculate the window size for preformatted text. This will calculate box 1271 * dimensions that are at or close to the specified aspect ratio for the prompt 1272 * string with all spaces and newlines preserved and additional newlines added 1273 * as necessary. 1274 */ 1275 static void 1276 auto_size_preformatted(const char *prompt, int *height, int *width) 1277 { 1278 int high = 0, wide = 0; 1279 float car; /* Calculated Aspect Ratio */ 1280 int max_y = SLINES - 1; 1281 int max_x = SCOLS - 2; 1282 int max_width = max_x; 1283 int ar = dialog_state.aspect_ratio; 1284 1285 /* Get the initial dimensions */ 1286 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide); 1287 car = (float) (wide / high); 1288 1289 /* 1290 * If the aspect ratio is greater than it should be, then decrease the 1291 * width proportionately. 1292 */ 1293 if (car > ar) { 1294 float diff = car / (float) ar; 1295 max_x = (int) ((float) wide / diff + 4); 1296 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide); 1297 car = (float) wide / (float) high; 1298 } 1299 1300 /* 1301 * If the aspect ratio is too small after decreasing the width, then 1302 * incrementally increase the width until the aspect ratio is equal to or 1303 * greater than the specified aspect ratio. 1304 */ 1305 while (car < ar && max_x < max_width) { 1306 max_x += 4; 1307 justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide); 1308 car = (float) (wide / high); 1309 } 1310 1311 *height = high; 1312 *width = wide; 1313 } 1314 1315 /* 1316 * Find the length of the longest "word" in the given string. By setting the 1317 * widget width at least this long, we can avoid splitting a word on the 1318 * margin. 1319 */ 1320 static int 1321 longest_word(const char *string) 1322 { 1323 int result = 0; 1324 1325 while (*string != '\0') { 1326 int length = 0; 1327 while (*string != '\0' && !isspace(UCH(*string))) { 1328 length++; 1329 string++; 1330 } 1331 result = MAX(result, length); 1332 if (*string != '\0') 1333 string++; 1334 } 1335 return result; 1336 } 1337 1338 /* 1339 * if (height or width == -1) Maximize() 1340 * if (height or width == 0), justify and return actual limits. 1341 */ 1342 static void 1343 real_auto_size(const char *title, 1344 const char *prompt, 1345 int *height, int *width, 1346 int boxlines, int mincols) 1347 { 1348 int x = (dialog_vars.begin_set ? dialog_vars.begin_x : 2); 1349 int y = (dialog_vars.begin_set ? dialog_vars.begin_y : 1); 1350 int title_length = title ? dlg_count_columns(title) : 0; 1351 int high; 1352 int save_high = *height; 1353 int save_wide = *width; 1354 int max_high; 1355 int max_wide; 1356 1357 if (prompt == 0) { 1358 if (*height == 0) 1359 *height = -1; 1360 if (*width == 0) 1361 *width = -1; 1362 } 1363 1364 max_high = (*height < 0); 1365 max_wide = (*width < 0); 1366 1367 if (*height > 0) { 1368 high = *height; 1369 } else { 1370 high = SLINES - y; 1371 } 1372 1373 if (*width <= 0) { 1374 int wide; 1375 1376 if (prompt != 0) { 1377 wide = MAX(title_length, mincols); 1378 if (strchr(prompt, '\n') == 0) { 1379 double val = (dialog_state.aspect_ratio * 1380 dlg_count_real_columns(prompt)); 1381 double xxx = sqrt(val); 1382 int tmp = (int) xxx; 1383 wide = MAX(wide, tmp); 1384 wide = MAX(wide, longest_word(prompt)); 1385 justify_text((WINDOW *) 0, prompt, high, wide, height, width); 1386 } else { 1387 auto_size_preformatted(prompt, height, width); 1388 } 1389 } else { 1390 wide = SCOLS - x; 1391 justify_text((WINDOW *) 0, prompt, high, wide, height, width); 1392 } 1393 } 1394 1395 if (*width < title_length) { 1396 justify_text((WINDOW *) 0, prompt, high, title_length, height, width); 1397 *width = title_length; 1398 } 1399 1400 dialog_state.text_height = *height; 1401 dialog_state.text_width = *width; 1402 1403 if (*width < mincols && save_wide == 0) 1404 *width = mincols; 1405 if (prompt != 0) { 1406 *width += ((2 * MARGIN) + SHADOW_COLS); 1407 *height += boxlines + (2 * MARGIN); 1408 } 1409 1410 if (save_high > 0) 1411 *height = save_high; 1412 if (save_wide > 0) 1413 *width = save_wide; 1414 1415 if (max_high) 1416 *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0); 1417 if (max_wide) 1418 *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0); 1419 } 1420 1421 /* End of real_auto_size() */ 1422 1423 void 1424 dlg_auto_size(const char *title, 1425 const char *prompt, 1426 int *height, 1427 int *width, 1428 int boxlines, 1429 int mincols) 1430 { 1431 DLG_TRACE(("# dlg_auto_size(%d,%d) limits %d,%d\n", 1432 *height, *width, 1433 boxlines, mincols)); 1434 1435 real_auto_size(title, prompt, height, width, boxlines, mincols); 1436 1437 if (*width > SCOLS) { 1438 (*height)++; 1439 *width = SCOLS; 1440 } 1441 1442 if (*height > SLINES) { 1443 *height = SLINES; 1444 } 1445 DLG_TRACE(("# ...dlg_auto_size(%d,%d) also %d,%d\n", 1446 *height, *width, 1447 dialog_state.text_height, dialog_state.text_width)); 1448 } 1449 1450 /* 1451 * if (height or width == -1) Maximize() 1452 * if (height or width == 0) 1453 * height=MIN(SLINES, num.lines in fd+n); 1454 * width=MIN(SCOLS, MAX(longer line+n, mincols)); 1455 */ 1456 void 1457 dlg_auto_sizefile(const char *title, 1458 const char *file, 1459 int *height, 1460 int *width, 1461 int boxlines, 1462 int mincols) 1463 { 1464 int count = 0; 1465 int len = title ? dlg_count_columns(title) : 0; 1466 int nc = 4; 1467 int numlines = 2; 1468 FILE *fd; 1469 1470 /* Open input file for reading */ 1471 if ((fd = fopen(file, "rb")) == NULL) 1472 dlg_exiterr("dlg_auto_sizefile: Cannot open input file %s", file); 1473 1474 if ((*height == -1) || (*width == -1)) { 1475 *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0); 1476 *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0); 1477 } 1478 if ((*height != 0) && (*width != 0)) { 1479 (void) fclose(fd); 1480 if (*width > SCOLS) 1481 *width = SCOLS; 1482 if (*height > SLINES) 1483 *height = SLINES; 1484 return; 1485 } 1486 1487 while (!feof(fd)) { 1488 int ch; 1489 long offset; 1490 1491 if (ferror(fd)) 1492 break; 1493 1494 offset = 0; 1495 while (((ch = getc(fd)) != '\n') && !feof(fd)) { 1496 if ((ch == TAB) && (dialog_vars.tab_correct)) { 1497 offset += dialog_state.tab_len - (offset % dialog_state.tab_len); 1498 } else { 1499 offset++; 1500 } 1501 } 1502 1503 if (offset > len) 1504 len = (int) offset; 1505 1506 count++; 1507 } 1508 1509 /* now 'count' has the number of lines of fd and 'len' the max length */ 1510 1511 *height = MIN(SLINES, count + numlines + boxlines); 1512 *width = MIN(SCOLS, MAX((len + nc), mincols)); 1513 /* here width and height can be maximized if > SCOLS|SLINES because 1514 textbox-like widgets don't put all <file> on the screen. 1515 Msgbox-like widget instead have to put all <text> correctly. */ 1516 1517 (void) fclose(fd); 1518 } 1519 1520 /* 1521 * Draw a rectangular box with line drawing characters. 1522 * 1523 * borderchar is used to color the upper/left edges. 1524 * 1525 * boxchar is used to color the right/lower edges. It also is fill-color used 1526 * for the box contents. 1527 * 1528 * Normally, if you are drawing a scrollable box, use menubox_border_attr for 1529 * boxchar, and menubox_attr for borderchar since the scroll-arrows are drawn 1530 * with menubox_attr at the top, and menubox_border_attr at the bottom. That 1531 * also (given the default color choices) produces a recessed effect. 1532 * 1533 * If you want a raised effect (and are not going to use the scroll-arrows), 1534 * reverse this choice. 1535 */ 1536 void 1537 dlg_draw_box2(WINDOW *win, int y, int x, int height, int width, 1538 chtype boxchar, chtype borderchar, chtype borderchar2) 1539 { 1540 int i, j; 1541 chtype save = dlg_get_attrs(win); 1542 1543 dlg_attrset(win, 0); 1544 for (i = 0; i < height; i++) { 1545 (void) wmove(win, y + i, x); 1546 for (j = 0; j < width; j++) 1547 if (!i && !j) 1548 (void) waddch(win, borderchar | dlg_boxchar(ACS_ULCORNER)); 1549 else if (i == height - 1 && !j) 1550 (void) waddch(win, borderchar | dlg_boxchar(ACS_LLCORNER)); 1551 else if (!i && j == width - 1) 1552 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_URCORNER)); 1553 else if (i == height - 1 && j == width - 1) 1554 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_LRCORNER)); 1555 else if (!i) 1556 (void) waddch(win, borderchar | dlg_boxchar(ACS_HLINE)); 1557 else if (i == height - 1) 1558 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_HLINE)); 1559 else if (!j) 1560 (void) waddch(win, borderchar | dlg_boxchar(ACS_VLINE)); 1561 else if (j == width - 1) 1562 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_VLINE)); 1563 else 1564 (void) waddch(win, boxchar | ' '); 1565 } 1566 dlg_attrset(win, save); 1567 } 1568 1569 void 1570 dlg_draw_box(WINDOW *win, int y, int x, int height, int width, 1571 chtype boxchar, chtype borderchar) 1572 { 1573 dlg_draw_box2(win, y, x, height, width, boxchar, borderchar, boxchar); 1574 } 1575 1576 /* 1577 * Search the given 'list' for the given window 'win'. Typically 'win' is an 1578 * input-window, i.e., a window where we might use wgetch. 1579 * 1580 * The all-windows list has normal- and shadow-windows. Since we never use the 1581 * shadow as an input window, normally we just look for the normal-window. 1582 * 1583 * However, the all-subwindows list stores parent/child windows rather than 1584 * normal/shadow windows. When searching that list, we look for the child 1585 * window (in the .shadow field). 1586 */ 1587 static DIALOG_WINDOWS * 1588 find_window(DIALOG_WINDOWS * list, WINDOW *win, bool normal) 1589 { 1590 DIALOG_WINDOWS *result = 0; 1591 DIALOG_WINDOWS *p; 1592 1593 for (p = list; p != 0; p = p->next) { 1594 WINDOW *check = normal ? p->normal : p->shadow; 1595 if (check == win) { 1596 result = p; 1597 break; 1598 } 1599 } 1600 return result; 1601 } 1602 1603 #define SearchTopWindows(win) find_window(dialog_state.all_windows, win, TRUE) 1604 #define SearchSubWindows(win) find_window(dialog_state.all_subwindows, win, FALSE) 1605 1606 /* 1607 * Check for the existence of a window, e.g., when used for input or updating 1608 * the display. This is used in dlg_getc() and related functions, to guard 1609 * against an asynchronous window-deletion that might invalidate the input 1610 * window used in dlg_getc(). 1611 */ 1612 DIALOG_WINDOWS * 1613 _dlg_find_window(WINDOW *win) 1614 { 1615 DIALOG_WINDOWS *result = 0; 1616 1617 if ((result = SearchTopWindows(win)) == NULL) 1618 result = SearchSubWindows(win); 1619 return result; 1620 } 1621 1622 #ifdef HAVE_COLOR 1623 /* 1624 * If we have wchgat(), use that for updating shadow attributes, to work with 1625 * wide-character data. 1626 */ 1627 1628 /* 1629 * Check if the given point is "in" the given window. If so, return the window 1630 * pointer, otherwise null. 1631 */ 1632 static WINDOW * 1633 in_window(WINDOW *win, int y, int x) 1634 { 1635 WINDOW *result = 0; 1636 int y_base = getbegy(win); 1637 int x_base = getbegx(win); 1638 int y_last = getmaxy(win) + y_base; 1639 int x_last = getmaxx(win) + x_base; 1640 1641 if (y >= y_base && y <= y_last && x >= x_base && x <= x_last) 1642 result = win; 1643 return result; 1644 } 1645 1646 static WINDOW * 1647 window_at_cell(DIALOG_WINDOWS * dw, int y, int x) 1648 { 1649 WINDOW *result = 0; 1650 DIALOG_WINDOWS *p; 1651 int y_want = y + getbegy(dw->shadow); 1652 int x_want = x + getbegx(dw->shadow); 1653 1654 for (p = dialog_state.all_windows; p != 0; p = p->next) { 1655 if (dw->normal != p->normal 1656 && dw->shadow != p->normal 1657 && (result = in_window(p->normal, y_want, x_want)) != 0) { 1658 break; 1659 } 1660 } 1661 if (result == 0) { 1662 result = stdscr; 1663 } 1664 return result; 1665 } 1666 1667 static bool 1668 in_shadow(WINDOW *normal, WINDOW *shadow, int y, int x) 1669 { 1670 bool result = FALSE; 1671 int ybase = getbegy(normal); 1672 int ylast = getmaxy(normal) + ybase; 1673 int xbase = getbegx(normal); 1674 int xlast = getmaxx(normal) + xbase; 1675 1676 y += getbegy(shadow); 1677 x += getbegx(shadow); 1678 1679 if (y >= ybase + SHADOW_ROWS 1680 && y < ylast + SHADOW_ROWS 1681 && x >= xlast 1682 && x < xlast + SHADOW_COLS) { 1683 /* in the right-side */ 1684 result = TRUE; 1685 } else if (y >= ylast 1686 && y < ylast + SHADOW_ROWS 1687 && x >= ybase + SHADOW_COLS 1688 && x < ylast + SHADOW_COLS) { 1689 /* check the bottom */ 1690 result = TRUE; 1691 } 1692 1693 return result; 1694 } 1695 1696 /* 1697 * When erasing a shadow, check each cell to make sure that it is not part of 1698 * another box's shadow. This is a little complicated since most shadows are 1699 * merged onto stdscr. 1700 */ 1701 static bool 1702 last_shadow(DIALOG_WINDOWS * dw, int y, int x) 1703 { 1704 DIALOG_WINDOWS *p; 1705 bool result = TRUE; 1706 1707 for (p = dialog_state.all_windows; p != 0; p = p->next) { 1708 if (p->normal != dw->normal 1709 && in_shadow(p->normal, dw->shadow, y, x)) { 1710 result = FALSE; 1711 break; 1712 } 1713 } 1714 return result; 1715 } 1716 1717 static void 1718 repaint_cell(DIALOG_WINDOWS * dw, bool draw, int y, int x) 1719 { 1720 WINDOW *win = dw->shadow; 1721 WINDOW *cellwin; 1722 int y2, x2; 1723 1724 if ((cellwin = window_at_cell(dw, y, x)) != 0 1725 && (draw || last_shadow(dw, y, x)) 1726 && (y2 = (y + getbegy(win) - getbegy(cellwin))) >= 0 1727 && (x2 = (x + getbegx(win) - getbegx(cellwin))) >= 0 1728 && wmove(cellwin, y2, x2) != ERR) { 1729 chtype the_cell = dlg_get_attrs(cellwin); 1730 chtype the_attr = (draw ? shadow_attr : the_cell); 1731 1732 if (winch(cellwin) & A_ALTCHARSET) { 1733 the_attr |= A_ALTCHARSET; 1734 } 1735 #if USE_WCHGAT 1736 wchgat(cellwin, 1, 1737 the_attr & (chtype) (~A_COLOR), 1738 (short) PAIR_NUMBER(the_attr), 1739 NULL); 1740 #else 1741 { 1742 chtype the_char = ((winch(cellwin) & A_CHARTEXT) | the_attr); 1743 (void) waddch(cellwin, the_char); 1744 } 1745 #endif 1746 wnoutrefresh(cellwin); 1747 } 1748 } 1749 1750 #define RepaintCell(dw, draw, y, x) repaint_cell(dw, draw, y, x) 1751 1752 static void 1753 repaint_shadow(DIALOG_WINDOWS * dw, bool draw, int y, int x, int height, int width) 1754 { 1755 if (UseShadow(dw)) { 1756 int i, j; 1757 1758 #if !USE_WCHGAT 1759 chtype save = dlg_get_attrs(dw->shadow); 1760 dlg_attrset(dw->shadow, draw ? shadow_attr : screen_attr); 1761 #endif 1762 for (i = 0; i < SHADOW_ROWS; ++i) { 1763 for (j = 0; j < width; ++j) { 1764 RepaintCell(dw, draw, i + y + height, j + x + SHADOW_COLS); 1765 } 1766 } 1767 for (i = 0; i < height; i++) { 1768 for (j = 0; j < SHADOW_COLS; ++j) { 1769 RepaintCell(dw, draw, i + y + SHADOW_ROWS, j + x + width); 1770 } 1771 } 1772 (void) wnoutrefresh(dw->shadow); 1773 #if !USE_WCHGAT 1774 dlg_attrset(dw->shadow, save); 1775 #endif 1776 } 1777 } 1778 1779 /* 1780 * Draw a shadow on the parent window corresponding to the right- and 1781 * bottom-edge of the child window, to give a 3-dimensional look. 1782 */ 1783 static void 1784 draw_childs_shadow(DIALOG_WINDOWS * dw) 1785 { 1786 if (UseShadow(dw)) { 1787 repaint_shadow(dw, 1788 TRUE, 1789 getbegy(dw->normal) - getbegy(dw->shadow), 1790 getbegx(dw->normal) - getbegx(dw->shadow), 1791 getmaxy(dw->normal), 1792 getmaxx(dw->normal)); 1793 } 1794 } 1795 1796 /* 1797 * Erase a shadow on the parent window corresponding to the right- and 1798 * bottom-edge of the child window. 1799 */ 1800 static void 1801 erase_childs_shadow(DIALOG_WINDOWS * dw) 1802 { 1803 if (UseShadow(dw)) { 1804 repaint_shadow(dw, 1805 FALSE, 1806 getbegy(dw->normal) - getbegy(dw->shadow), 1807 getbegx(dw->normal) - getbegx(dw->shadow), 1808 getmaxy(dw->normal), 1809 getmaxx(dw->normal)); 1810 } 1811 } 1812 1813 /* 1814 * Draw shadows along the right and bottom edge to give a more 3D look 1815 * to the boxes. 1816 */ 1817 void 1818 dlg_draw_shadow(WINDOW *win, int y, int x, int height, int width) 1819 { 1820 repaint_shadow(SearchTopWindows(win), TRUE, y, x, height, width); 1821 } 1822 #endif /* HAVE_COLOR */ 1823 1824 /* 1825 * Allow shell scripts to remap the exit codes so they can distinguish ESC 1826 * from ERROR. 1827 */ 1828 void 1829 dlg_exit(int code) 1830 { 1831 /* *INDENT-OFF* */ 1832 static const struct { 1833 int code; 1834 const char *name; 1835 } table[] = { 1836 { DLG_EXIT_CANCEL, "DIALOG_CANCEL" }, 1837 { DLG_EXIT_ERROR, "DIALOG_ERROR" }, 1838 { DLG_EXIT_ESC, "DIALOG_ESC" }, 1839 { DLG_EXIT_EXTRA, "DIALOG_EXTRA" }, 1840 { DLG_EXIT_HELP, "DIALOG_HELP" }, 1841 { DLG_EXIT_OK, "DIALOG_OK" }, 1842 { DLG_EXIT_ITEM_HELP, "DIALOG_ITEM_HELP" }, 1843 { DLG_EXIT_TIMEOUT, "DIALOG_TIMEOUT" }, 1844 }; 1845 /* *INDENT-ON* */ 1846 1847 unsigned n; 1848 bool overridden = FALSE; 1849 1850 retry: 1851 for (n = 0; n < TableSize(table); n++) { 1852 if (table[n].code == code) { 1853 if (dlg_getenv_num(table[n].name, &code)) { 1854 overridden = TRUE; 1855 } 1856 break; 1857 } 1858 } 1859 1860 /* 1861 * Prior to 2004/12/19, a widget using --item-help would exit with "OK" 1862 * if the help button were selected. Now we want to exit with "HELP", 1863 * but allow the environment variable to override. 1864 */ 1865 if (code == DLG_EXIT_ITEM_HELP && !overridden) { 1866 code = DLG_EXIT_HELP; 1867 goto retry; 1868 } 1869 #ifdef HAVE_DLG_TRACE 1870 dlg_trace((const char *) 0); /* close it */ 1871 #endif 1872 1873 #ifdef NO_LEAKS 1874 _dlg_inputstr_leaks(); 1875 #if defined(NCURSES_VERSION) && (defined(HAVE_CURSES_EXIT) || defined(HAVE__NC_FREE_AND_EXIT)) 1876 curses_exit(code); 1877 #endif 1878 #endif 1879 1880 if (dialog_state.input == stdin) { 1881 exit(code); 1882 } else { 1883 /* 1884 * Just in case of using --input-fd option, do not 1885 * call atexit functions of ncurses which may hang. 1886 */ 1887 if (dialog_state.input) { 1888 fclose(dialog_state.input); 1889 dialog_state.input = 0; 1890 } 1891 if (dialog_state.pipe_input) { 1892 if (dialog_state.pipe_input != stdin) { 1893 fclose(dialog_state.pipe_input); 1894 dialog_state.pipe_input = 0; 1895 } 1896 } 1897 _exit(code); 1898 } 1899 } 1900 1901 #define DATA(name) { DLG_EXIT_ ## name, #name } 1902 /* *INDENT-OFF* */ 1903 static struct { 1904 int code; 1905 const char *name; 1906 } exit_codenames[] = { 1907 DATA(ESC), 1908 DATA(UNKNOWN), 1909 DATA(ERROR), 1910 DATA(OK), 1911 DATA(CANCEL), 1912 DATA(HELP), 1913 DATA(EXTRA), 1914 DATA(ITEM_HELP), 1915 }; 1916 #undef DATA 1917 /* *INDENT-ON* */ 1918 1919 const char * 1920 dlg_exitcode2s(int code) 1921 { 1922 const char *result = "?"; 1923 size_t n; 1924 1925 for (n = 0; n < TableSize(exit_codenames); ++n) { 1926 if (exit_codenames[n].code == code) { 1927 result = exit_codenames[n].name; 1928 break; 1929 } 1930 } 1931 return result; 1932 } 1933 1934 int 1935 dlg_exitname2n(const char *name) 1936 { 1937 int result = DLG_EXIT_UNKNOWN; 1938 size_t n; 1939 1940 for (n = 0; n < TableSize(exit_codenames); ++n) { 1941 if (!dlg_strcmp(exit_codenames[n].name, name)) { 1942 result = exit_codenames[n].code; 1943 break; 1944 } 1945 } 1946 return result; 1947 } 1948 1949 /* quit program killing all tailbg */ 1950 void 1951 dlg_exiterr(const char *fmt, ...) 1952 { 1953 int retval; 1954 va_list ap; 1955 1956 end_dialog(); 1957 1958 (void) fputc('\n', stderr); 1959 va_start(ap, fmt); 1960 (void) vfprintf(stderr, fmt, ap); 1961 va_end(ap); 1962 (void) fputc('\n', stderr); 1963 1964 #ifdef HAVE_DLG_TRACE 1965 va_start(ap, fmt); 1966 dlg_trace_msg("## Error: "); 1967 dlg_trace_va_msg(fmt, ap); 1968 va_end(ap); 1969 #endif 1970 1971 dlg_killall_bg(&retval); 1972 1973 (void) fflush(stderr); 1974 (void) fflush(stdout); 1975 dlg_exit(strcmp(fmt, "timeout") == 0 ? DLG_EXIT_TIMEOUT : DLG_EXIT_ERROR); 1976 } 1977 1978 /* 1979 * Get a string from the environment, rejecting those which are entirely blank. 1980 */ 1981 char * 1982 dlg_getenv_str(const char *name) 1983 { 1984 char *result = getenv(name); 1985 if (result != NULL) { 1986 while (*result != '\0' && isspace(UCH(*result))) 1987 ++result; 1988 if (*result == '\0') 1989 result = NULL; 1990 } 1991 return result; 1992 } 1993 1994 /* 1995 * Get a number from the environment: 1996 * + If the caller provides a pointer in the second parameter, return 1997 * success/failure for the function return, and the actual value via the 1998 * pointer. Use this for decoding arbitrary numbers, e.g., negative or zero. 1999 * + If the caller does not provide a pointer, return the decoded value for 2000 * the function-return. Use this when only values greater than zero are 2001 * useful. 2002 */ 2003 int 2004 dlg_getenv_num(const char *name, int *value) 2005 { 2006 int result = 0; 2007 char *data = getenv(name); 2008 if (data != NULL) { 2009 char *temp = NULL; 2010 long check = strtol(data, &temp, 0); 2011 if (temp != 0 && temp != data && *temp == '\0') { 2012 result = (int) check; 2013 if (value != NULL) { 2014 *value = result; 2015 result = 1; 2016 } 2017 } 2018 } 2019 return result; 2020 } 2021 2022 void 2023 dlg_beeping(void) 2024 { 2025 if (dialog_vars.beep_signal) { 2026 (void) beep(); 2027 dialog_vars.beep_signal = 0; 2028 } 2029 } 2030 2031 void 2032 dlg_print_size(int height, int width) 2033 { 2034 if (dialog_vars.print_siz) { 2035 fprintf(dialog_state.output, "Size: %d, %d\n", height, width); 2036 DLG_TRACE(("# print size: %dx%d\n", height, width)); 2037 } 2038 } 2039 2040 void 2041 dlg_ctl_size(int height, int width) 2042 { 2043 if (dialog_vars.size_err) { 2044 if ((width > COLS) || (height > LINES)) { 2045 dlg_exiterr("Window too big. (height, width) = (%d, %d). Max allowed (%d, %d).", 2046 height, width, LINES, COLS); 2047 } 2048 #ifdef HAVE_COLOR 2049 else if ((dialog_state.use_shadow) 2050 && ((width > SCOLS || height > SLINES))) { 2051 if ((width <= COLS) && (height <= LINES)) { 2052 /* try again, without shadows */ 2053 dialog_state.use_shadow = 0; 2054 } else { 2055 dlg_exiterr("Window+Shadow too big. (height, width) = (%d, %d). Max allowed (%d, %d).", 2056 height, width, SLINES, SCOLS); 2057 } 2058 } 2059 #endif 2060 } 2061 } 2062 2063 /* 2064 * If the --tab-correct was not selected, convert tabs to single spaces. 2065 */ 2066 void 2067 dlg_tab_correct_str(char *prompt) 2068 { 2069 char *ptr; 2070 2071 if (dialog_vars.tab_correct) { 2072 while ((ptr = strchr(prompt, TAB)) != NULL) { 2073 *ptr = ' '; 2074 prompt = ptr; 2075 } 2076 } 2077 } 2078 2079 void 2080 dlg_calc_listh(int *height, int *list_height, int item_no) 2081 { 2082 /* calculate new height and list_height */ 2083 int rows = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0); 2084 if (rows - (*height) > 0) { 2085 if (rows - (*height) > item_no) 2086 *list_height = item_no; 2087 else 2088 *list_height = rows - (*height); 2089 } 2090 (*height) += (*list_height); 2091 } 2092 2093 /* obsolete */ 2094 int 2095 dlg_calc_listw(int item_no, char **items, int group) 2096 { 2097 int i, len1 = 0, len2 = 0; 2098 2099 for (i = 0; i < (item_no * group); i += group) { 2100 int n; 2101 2102 if ((n = dlg_count_columns(items[i])) > len1) 2103 len1 = n; 2104 if ((n = dlg_count_columns(items[i + 1])) > len2) 2105 len2 = n; 2106 } 2107 return len1 + len2; 2108 } 2109 2110 int 2111 dlg_calc_list_width(int item_no, DIALOG_LISTITEM * items) 2112 { 2113 int n, i, len1 = 0, len2 = 0; 2114 int bits = ((dialog_vars.no_tags ? 1 : 0) 2115 + (dialog_vars.no_items ? 2 : 0)); 2116 2117 for (i = 0; i < item_no; ++i) { 2118 switch (bits) { 2119 case 0: 2120 /* FALLTHRU */ 2121 case 1: 2122 if ((n = dlg_count_columns(items[i].name)) > len1) 2123 len1 = n; 2124 if ((n = dlg_count_columns(items[i].text)) > len2) 2125 len2 = n; 2126 break; 2127 case 2: 2128 /* FALLTHRU */ 2129 case 3: 2130 if ((n = dlg_count_columns(items[i].name)) > len1) 2131 len1 = n; 2132 break; 2133 } 2134 } 2135 return len1 + len2; 2136 } 2137 2138 char * 2139 dlg_strempty(void) 2140 { 2141 static char empty[] = ""; 2142 return empty; 2143 } 2144 2145 char * 2146 dlg_strclone(const char *cprompt) 2147 { 2148 char *prompt = 0; 2149 if (cprompt != 0) { 2150 prompt = dlg_malloc(char, strlen(cprompt) + 1); 2151 assert_ptr(prompt, "dlg_strclone"); 2152 strcpy(prompt, cprompt); 2153 } 2154 return prompt; 2155 } 2156 2157 chtype 2158 dlg_asciibox(chtype ch) 2159 { 2160 chtype result = 0; 2161 2162 if (ch == ACS_ULCORNER) 2163 result = '+'; 2164 else if (ch == ACS_LLCORNER) 2165 result = '+'; 2166 else if (ch == ACS_URCORNER) 2167 result = '+'; 2168 else if (ch == ACS_LRCORNER) 2169 result = '+'; 2170 else if (ch == ACS_HLINE) 2171 result = '-'; 2172 else if (ch == ACS_VLINE) 2173 result = '|'; 2174 else if (ch == ACS_LTEE) 2175 result = '+'; 2176 else if (ch == ACS_RTEE) 2177 result = '+'; 2178 else if (ch == ACS_UARROW) 2179 result = '^'; 2180 else if (ch == ACS_DARROW) 2181 result = 'v'; 2182 2183 return result; 2184 } 2185 2186 chtype 2187 dlg_boxchar(chtype ch) 2188 { 2189 chtype result = dlg_asciibox(ch); 2190 2191 if (result != 0) { 2192 if (dialog_vars.ascii_lines) 2193 ch = result; 2194 else if (dialog_vars.no_lines) 2195 ch = ' '; 2196 } 2197 return ch; 2198 } 2199 2200 int 2201 dlg_box_x_ordinate(int width) 2202 { 2203 int x; 2204 2205 if (dialog_vars.begin_set == 1) { 2206 x = dialog_vars.begin_x; 2207 } else { 2208 /* center dialog box on screen unless --begin-set */ 2209 x = (SCOLS - width) / 2; 2210 } 2211 return x; 2212 } 2213 2214 int 2215 dlg_box_y_ordinate(int height) 2216 { 2217 int y; 2218 2219 if (dialog_vars.begin_set == 1) { 2220 y = dialog_vars.begin_y; 2221 } else { 2222 /* center dialog box on screen unless --begin-set */ 2223 y = (SLINES - height) / 2; 2224 } 2225 return y; 2226 } 2227 2228 void 2229 dlg_draw_title(WINDOW *win, const char *title) 2230 { 2231 if (title != NULL) { 2232 chtype attr = A_NORMAL; 2233 chtype save = dlg_get_attrs(win); 2234 int x = centered(getmaxx(win), title); 2235 2236 dlg_attrset(win, title_attr); 2237 wmove(win, 0, x); 2238 dlg_print_text(win, title, getmaxx(win) - x, &attr); 2239 dlg_attrset(win, save); 2240 dlg_finish_string(title); 2241 } 2242 } 2243 2244 void 2245 dlg_draw_bottom_box2(WINDOW *win, chtype on_left, chtype on_right, chtype on_inside) 2246 { 2247 int width = getmaxx(win); 2248 int height = getmaxy(win); 2249 int i; 2250 2251 dlg_attrset(win, on_left); 2252 (void) wmove(win, height - 3, 0); 2253 (void) waddch(win, dlg_boxchar(ACS_LTEE)); 2254 for (i = 0; i < width - 2; i++) 2255 (void) waddch(win, dlg_boxchar(ACS_HLINE)); 2256 dlg_attrset(win, on_right); 2257 (void) waddch(win, dlg_boxchar(ACS_RTEE)); 2258 dlg_attrset(win, on_inside); 2259 (void) wmove(win, height - 2, 1); 2260 for (i = 0; i < width - 2; i++) 2261 (void) waddch(win, ' '); 2262 } 2263 2264 void 2265 dlg_draw_bottom_box(WINDOW *win) 2266 { 2267 dlg_draw_bottom_box2(win, border_attr, dialog_attr, dialog_attr); 2268 } 2269 2270 /* 2271 * Remove a window, repainting everything else. This would be simpler if we 2272 * used the panel library, but that is not _always_ available. 2273 */ 2274 void 2275 dlg_del_window(WINDOW *win) 2276 { 2277 DIALOG_WINDOWS *p, *q, *r; 2278 2279 /* 2280 * If --keep-window was set, do not delete/repaint the windows. 2281 */ 2282 if (dialog_vars.keep_window) 2283 return; 2284 2285 /* Leave the main window untouched if there are no background windows. 2286 * We do this so the current window will not be cleared on exit, allowing 2287 * things like the infobox demo to run without flicker. 2288 */ 2289 if (dialog_state.getc_callbacks != 0) { 2290 touchwin(stdscr); 2291 wnoutrefresh(stdscr); 2292 } 2293 2294 for (p = dialog_state.all_windows, q = r = 0; p != 0; r = p, p = p->next) { 2295 if (p->normal == win) { 2296 q = p; /* found a match - should be only one */ 2297 if (r == 0) { 2298 dialog_state.all_windows = p->next; 2299 } else { 2300 r->next = p->next; 2301 } 2302 } else { 2303 if (p->shadow != 0) { 2304 touchwin(p->shadow); 2305 wnoutrefresh(p->shadow); 2306 } 2307 touchwin(p->normal); 2308 wnoutrefresh(p->normal); 2309 } 2310 } 2311 2312 if (q) { 2313 if (dialog_state.all_windows != 0) 2314 erase_childs_shadow(q); 2315 del_subwindows(q->normal); 2316 dlg_unregister_window(q->normal); 2317 delwin(q->normal); 2318 free(q); 2319 } 2320 doupdate(); 2321 } 2322 2323 /* 2324 * Create a window, optionally with a shadow. 2325 */ 2326 WINDOW * 2327 dlg_new_window(int height, int width, int y, int x) 2328 { 2329 return dlg_new_modal_window(stdscr, height, width, y, x); 2330 } 2331 2332 /* 2333 * "Modal" windows differ from normal ones by having a shadow in a window 2334 * separate from the standard screen. 2335 */ 2336 WINDOW * 2337 dlg_new_modal_window(WINDOW *parent, int height, int width, int y, int x) 2338 { 2339 WINDOW *win; 2340 DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1); 2341 2342 (void) parent; 2343 if (p == 0 2344 || (win = newwin(height, width, y, x)) == 0) { 2345 dlg_exiterr("Can't make new window at (%d,%d), size (%d,%d).\n", 2346 y, x, height, width); 2347 } 2348 p->next = dialog_state.all_windows; 2349 p->normal = win; 2350 p->getc_timeout = WTIMEOUT_OFF; 2351 dialog_state.all_windows = p; 2352 #ifdef HAVE_COLOR 2353 if (dialog_state.use_shadow) { 2354 p->shadow = parent; 2355 draw_childs_shadow(p); 2356 } 2357 #endif 2358 2359 (void) keypad(win, TRUE); 2360 return win; 2361 } 2362 2363 /* 2364 * dlg_getc() uses the return-value to determine how to handle an ERR return 2365 * from a non-blocking read: 2366 * a) if greater than zero, there was an expired timeout (blocking for a short 2367 * time), or 2368 * b) if zero, it was a non-blocking read, or 2369 * c) if negative, an error occurred on a blocking read. 2370 */ 2371 int 2372 dlg_set_timeout(WINDOW *win, bool will_getc) 2373 { 2374 DIALOG_WINDOWS *p; 2375 int result = 0; 2376 2377 if ((p = SearchTopWindows(win)) != NULL) { 2378 int interval = (dialog_vars.timeout_secs * 1000); 2379 2380 if (will_getc || dialog_vars.pause_secs) { 2381 interval = WTIMEOUT_VAL; 2382 } else { 2383 result = interval; 2384 if (interval <= 0) { 2385 interval = WTIMEOUT_OFF; 2386 } 2387 } 2388 wtimeout(win, interval); 2389 p->getc_timeout = interval; 2390 } 2391 return result; 2392 } 2393 2394 void 2395 dlg_reset_timeout(WINDOW *win) 2396 { 2397 DIALOG_WINDOWS *p; 2398 2399 if ((p = SearchTopWindows(win)) != NULL) { 2400 wtimeout(win, p->getc_timeout); 2401 } else { 2402 wtimeout(win, WTIMEOUT_OFF); 2403 } 2404 } 2405 2406 /* 2407 * Move/Resize a window, optionally with a shadow. 2408 */ 2409 #ifdef KEY_RESIZE 2410 void 2411 dlg_move_window(WINDOW *win, int height, int width, int y, int x) 2412 { 2413 if (win != 0) { 2414 DIALOG_WINDOWS *p; 2415 2416 dlg_ctl_size(height, width); 2417 2418 if ((p = SearchTopWindows(win)) != 0) { 2419 (void) wresize(win, height, width); 2420 (void) mvwin(win, y, x); 2421 #ifdef HAVE_COLOR 2422 if (p->shadow != 0) { 2423 if (dialog_state.use_shadow) { 2424 (void) mvwin(p->shadow, y + SHADOW_ROWS, x + SHADOW_COLS); 2425 } else { 2426 p->shadow = 0; 2427 } 2428 } 2429 #endif 2430 (void) refresh(); 2431 2432 #ifdef HAVE_COLOR 2433 draw_childs_shadow(p); 2434 #endif 2435 } 2436 } 2437 } 2438 2439 /* 2440 * Having just received a KEY_RESIZE, wait a short time to ignore followup 2441 * KEY_RESIZE events. 2442 */ 2443 void 2444 dlg_will_resize(WINDOW *win) 2445 { 2446 int n, base; 2447 int caught = 0; 2448 2449 dialog_state.had_resize = TRUE; 2450 dlg_trace_win(win); 2451 wtimeout(win, WTIMEOUT_VAL * 5); 2452 2453 for (n = base = 0; n < base + 10; ++n) { 2454 int ch; 2455 2456 if ((ch = wgetch(win)) != ERR) { 2457 if (ch == KEY_RESIZE) { 2458 base = n; 2459 ++caught; 2460 } else if (ch != ERR) { 2461 ungetch(ch); 2462 break; 2463 } 2464 } 2465 } 2466 dlg_reset_timeout(win); 2467 DLG_TRACE(("# caught %d KEY_RESIZE key%s\n", 2468 1 + caught, 2469 caught == 1 ? "" : "s")); 2470 } 2471 #endif /* KEY_RESIZE */ 2472 2473 WINDOW * 2474 dlg_der_window(WINDOW *parent, int height, int width, int y, int x) 2475 { 2476 WINDOW *win; 2477 2478 /* existing uses of derwin are (almost) guaranteed to succeed, and the 2479 * caller has to allow for failure. 2480 */ 2481 if ((win = derwin(parent, height, width, y, x)) != 0) { 2482 add_subwindow(parent, win); 2483 (void) keypad(win, TRUE); 2484 } 2485 return win; 2486 } 2487 2488 WINDOW * 2489 dlg_sub_window(WINDOW *parent, int height, int width, int y, int x) 2490 { 2491 WINDOW *win; 2492 2493 if ((win = subwin(parent, height, width, y, x)) == 0) { 2494 dlg_exiterr("Can't make sub-window at (%d,%d), size (%d,%d).\n", 2495 y, x, height, width); 2496 } 2497 2498 add_subwindow(parent, win); 2499 (void) keypad(win, TRUE); 2500 return win; 2501 } 2502 2503 /* obsolete */ 2504 int 2505 dlg_default_item(char **items, int llen) 2506 { 2507 int result = 0; 2508 2509 if (dialog_vars.default_item != 0) { 2510 int count = 0; 2511 while (*items != 0) { 2512 if (!strcmp(dialog_vars.default_item, *items)) { 2513 result = count; 2514 break; 2515 } 2516 items += llen; 2517 count++; 2518 } 2519 } 2520 return result; 2521 } 2522 2523 int 2524 dlg_default_listitem(DIALOG_LISTITEM * items) 2525 { 2526 int result = 0; 2527 2528 if (dialog_vars.default_item != 0) { 2529 int count = 0; 2530 while (items->name != 0) { 2531 if (!strcmp(dialog_vars.default_item, items->name)) { 2532 result = count; 2533 break; 2534 } 2535 ++items; 2536 count++; 2537 } 2538 } 2539 return result; 2540 } 2541 2542 /* 2543 * Draw the string for item_help 2544 */ 2545 void 2546 dlg_item_help(const char *txt) 2547 { 2548 if (USE_ITEM_HELP(txt)) { 2549 chtype attr = A_NORMAL; 2550 2551 dlg_attrset(stdscr, itemhelp_attr); 2552 (void) wmove(stdscr, LINES - 1, 0); 2553 (void) wclrtoeol(stdscr); 2554 (void) addch(' '); 2555 dlg_print_text(stdscr, txt, COLS - 1, &attr); 2556 2557 if (itemhelp_attr & A_COLOR) { 2558 int y, x; 2559 /* fill the remainder of the line with the window's attributes */ 2560 getyx(stdscr, y, x); 2561 (void) y; 2562 while (x < COLS) { 2563 (void) addch(' '); 2564 ++x; 2565 } 2566 } 2567 (void) wnoutrefresh(stdscr); 2568 } 2569 } 2570 2571 #ifndef HAVE_STRCASECMP 2572 int 2573 dlg_strcmp(const char *a, const char *b) 2574 { 2575 int ac, bc, cmp; 2576 2577 for (;;) { 2578 ac = UCH(*a++); 2579 bc = UCH(*b++); 2580 if (isalpha(ac) && islower(ac)) 2581 ac = _toupper(ac); 2582 if (isalpha(bc) && islower(bc)) 2583 bc = _toupper(bc); 2584 cmp = ac - bc; 2585 if (ac == 0 || bc == 0 || cmp != 0) 2586 break; 2587 } 2588 return cmp; 2589 } 2590 #endif 2591 2592 /* 2593 * Returns true if 'dst' points to a blank which follows another blank which 2594 * is not a leading blank on a line. 2595 */ 2596 static bool 2597 trim_blank(char *base, char *dst) 2598 { 2599 int count = !!isblank(UCH(*dst)); 2600 2601 while (dst-- != base) { 2602 if (*dst == '\n') { 2603 break; 2604 } else if (isblank(UCH(*dst))) { 2605 count++; 2606 } else { 2607 break; 2608 } 2609 } 2610 return (count > 1); 2611 } 2612 2613 /* 2614 * Change embedded "\n" substrings to '\n' characters and tabs to single 2615 * spaces. If there are no "\n"s, it will strip all extra spaces, for 2616 * justification. If it has "\n"'s, it will preserve extra spaces. If cr_wrap 2617 * is set, it will preserve '\n's. 2618 */ 2619 void 2620 dlg_trim_string(char *s) 2621 { 2622 char *base = s; 2623 char *p1; 2624 char *p = s; 2625 int has_newlines = !dialog_vars.no_nl_expand && (strstr(s, "\\n") != 0); 2626 2627 while (*p != '\0') { 2628 if (*p == TAB && !dialog_vars.nocollapse) 2629 *p = ' '; 2630 2631 if (has_newlines) { /* If prompt contains "\n" strings */ 2632 if (*p == '\\' && *(p + 1) == 'n') { 2633 *s++ = '\n'; 2634 p += 2; 2635 p1 = p; 2636 /* 2637 * Handle end of lines intelligently. If '\n' follows "\n" 2638 * then ignore the '\n'. This eliminates the need to escape 2639 * the '\n' character (no need to use "\n\"). 2640 */ 2641 while (isblank(UCH(*p1))) 2642 p1++; 2643 if (*p1 == '\n') 2644 p = p1 + 1; 2645 } else if (*p == '\n') { 2646 if (dialog_vars.cr_wrap) 2647 *s++ = *p++; 2648 else { 2649 /* Replace the '\n' with a space if cr_wrap is not set */ 2650 if (!trim_blank(base, p)) 2651 *s++ = ' '; 2652 p++; 2653 } 2654 } else /* If *p != '\n' */ 2655 *s++ = *p++; 2656 } else if (dialog_vars.trim_whitespace) { 2657 if (isblank(UCH(*p))) { 2658 if (!isblank(UCH(*(s - 1)))) { 2659 *s++ = ' '; 2660 p++; 2661 } else 2662 p++; 2663 } else if (*p == '\n') { 2664 if (dialog_vars.cr_wrap) 2665 *s++ = *p++; 2666 else if (!isblank(UCH(*(s - 1)))) { 2667 /* Strip '\n's if cr_wrap is not set. */ 2668 *s++ = ' '; 2669 p++; 2670 } else 2671 p++; 2672 } else 2673 *s++ = *p++; 2674 } else { /* If there are no "\n" strings */ 2675 if (isblank(UCH(*p)) && !dialog_vars.nocollapse) { 2676 if (!trim_blank(base, p)) 2677 *s++ = *p; 2678 p++; 2679 } else 2680 *s++ = *p++; 2681 } 2682 } 2683 2684 *s = '\0'; 2685 } 2686 2687 void 2688 dlg_set_focus(WINDOW *parent, WINDOW *win) 2689 { 2690 if (win != 0) { 2691 (void) wmove(parent, 2692 getpary(win) + getcury(win), 2693 getparx(win) + getcurx(win)); 2694 (void) wnoutrefresh(win); 2695 (void) doupdate(); 2696 } 2697 } 2698 2699 /* 2700 * Returns the nominal maximum buffer size. 2701 */ 2702 int 2703 dlg_max_input(int max_len) 2704 { 2705 if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN) 2706 max_len = dialog_vars.max_input; 2707 2708 return max_len; 2709 } 2710 2711 /* 2712 * Free storage used for the result buffer. 2713 */ 2714 void 2715 dlg_clr_result(void) 2716 { 2717 if (dialog_vars.input_length) { 2718 dialog_vars.input_length = 0; 2719 if (dialog_vars.input_result) 2720 free(dialog_vars.input_result); 2721 } 2722 dialog_vars.input_result = 0; 2723 } 2724 2725 /* 2726 * Setup a fixed-buffer for the result. 2727 */ 2728 char * 2729 dlg_set_result(const char *string) 2730 { 2731 unsigned need = string ? (unsigned) strlen(string) + 1 : 0; 2732 2733 /* inputstr.c needs a fixed buffer */ 2734 if (need < MAX_LEN) 2735 need = MAX_LEN; 2736 2737 /* 2738 * If the buffer is not big enough, allocate a new one. 2739 */ 2740 if (dialog_vars.input_length != 0 2741 || dialog_vars.input_result == 0 2742 || need > MAX_LEN) { 2743 2744 dlg_clr_result(); 2745 2746 dialog_vars.input_length = need; 2747 dialog_vars.input_result = dlg_malloc(char, need); 2748 assert_ptr(dialog_vars.input_result, "dlg_set_result"); 2749 } 2750 2751 strcpy(dialog_vars.input_result, string ? string : ""); 2752 2753 return dialog_vars.input_result; 2754 } 2755 2756 /* 2757 * Accumulate results in dynamically allocated buffer. 2758 * If input_length is zero, it is a MAX_LEN buffer belonging to the caller. 2759 */ 2760 void 2761 dlg_add_result(const char *string) 2762 { 2763 unsigned have = (dialog_vars.input_result 2764 ? (unsigned) strlen(dialog_vars.input_result) 2765 : 0); 2766 unsigned want = (unsigned) strlen(string) + 1 + have; 2767 2768 if ((want >= MAX_LEN) 2769 || (dialog_vars.input_length != 0) 2770 || (dialog_vars.input_result == 0)) { 2771 2772 if (dialog_vars.input_length == 0 2773 || dialog_vars.input_result == 0) { 2774 2775 char *save_result = dialog_vars.input_result; 2776 2777 dialog_vars.input_length = want * 2; 2778 dialog_vars.input_result = dlg_malloc(char, dialog_vars.input_length); 2779 assert_ptr(dialog_vars.input_result, "dlg_add_result malloc"); 2780 dialog_vars.input_result[0] = '\0'; 2781 if (save_result != 0) 2782 strcpy(dialog_vars.input_result, save_result); 2783 } else if (want >= dialog_vars.input_length) { 2784 dialog_vars.input_length = want * 2; 2785 dialog_vars.input_result = dlg_realloc(char, 2786 dialog_vars.input_length, 2787 dialog_vars.input_result); 2788 assert_ptr(dialog_vars.input_result, "dlg_add_result realloc"); 2789 } 2790 } 2791 strcat(dialog_vars.input_result, string); 2792 } 2793 2794 /* 2795 * These are characters that (aside from the quote-delimiter) will have to 2796 * be escaped in a single- or double-quoted string. 2797 */ 2798 #define FIX_SINGLE "\n\\" 2799 #define FIX_DOUBLE FIX_SINGLE "[]{}?*;`~#$^&()|<>" 2800 2801 /* 2802 * Returns the quote-delimiter. 2803 */ 2804 static const char * 2805 quote_delimiter(void) 2806 { 2807 return dialog_vars.single_quoted ? "'" : "\""; 2808 } 2809 2810 /* 2811 * Returns true if we should quote the given string. 2812 */ 2813 static bool 2814 must_quote(char *string) 2815 { 2816 bool code = FALSE; 2817 2818 if (*string != '\0') { 2819 size_t len = strlen(string); 2820 if (strcspn(string, quote_delimiter()) != len) 2821 code = TRUE; 2822 else if (strcspn(string, "\n\t ") != len) 2823 code = TRUE; 2824 else 2825 code = (strcspn(string, FIX_DOUBLE) != len); 2826 } else { 2827 code = TRUE; 2828 } 2829 2830 return code; 2831 } 2832 2833 /* 2834 * Add a quoted string to the result buffer. 2835 */ 2836 void 2837 dlg_add_quoted(char *string) 2838 { 2839 char temp[2]; 2840 const char *my_quote = quote_delimiter(); 2841 const char *must_fix = (dialog_vars.single_quoted 2842 ? FIX_SINGLE 2843 : FIX_DOUBLE); 2844 2845 if (must_quote(string)) { 2846 temp[1] = '\0'; 2847 dlg_add_result(my_quote); 2848 while (*string != '\0') { 2849 temp[0] = *string++; 2850 if ((strchr) (my_quote, *temp) || (strchr) (must_fix, *temp)) 2851 dlg_add_result("\\"); 2852 dlg_add_result(temp); 2853 } 2854 dlg_add_result(my_quote); 2855 } else { 2856 dlg_add_result(string); 2857 } 2858 } 2859 2860 /* 2861 * When adding a result, make that depend on whether "--quoted" is used. 2862 */ 2863 void 2864 dlg_add_string(char *string) 2865 { 2866 if (dialog_vars.quoted) { 2867 dlg_add_quoted(string); 2868 } else { 2869 dlg_add_result(string); 2870 } 2871 } 2872 2873 bool 2874 dlg_need_separator(void) 2875 { 2876 bool result = FALSE; 2877 2878 if (dialog_vars.output_separator) { 2879 result = TRUE; 2880 } else if (dialog_vars.input_result && *(dialog_vars.input_result)) { 2881 result = TRUE; 2882 } 2883 return result; 2884 } 2885 2886 void 2887 dlg_add_separator(void) 2888 { 2889 const char *separator = (dialog_vars.separate_output) ? "\n" : " "; 2890 2891 if (dialog_vars.output_separator) 2892 separator = dialog_vars.output_separator; 2893 2894 dlg_add_result(separator); 2895 } 2896 2897 #define HELP_PREFIX "HELP " 2898 2899 void 2900 dlg_add_help_listitem(int *result, char **tag, DIALOG_LISTITEM * item) 2901 { 2902 dlg_add_result(HELP_PREFIX); 2903 if (USE_ITEM_HELP(item->help)) { 2904 *tag = dialog_vars.help_tags ? item->name : item->help; 2905 *result = DLG_EXIT_ITEM_HELP; 2906 } else { 2907 *tag = item->name; 2908 } 2909 } 2910 2911 void 2912 dlg_add_help_formitem(int *result, char **tag, DIALOG_FORMITEM * item) 2913 { 2914 dlg_add_result(HELP_PREFIX); 2915 if (USE_ITEM_HELP(item->help)) { 2916 *tag = dialog_vars.help_tags ? item->name : item->help; 2917 *result = DLG_EXIT_ITEM_HELP; 2918 } else { 2919 *tag = item->name; 2920 } 2921 } 2922 2923 /* 2924 * Some widgets support only one value of a given variable - save/restore the 2925 * global dialog_vars so we can override it consistently. 2926 */ 2927 void 2928 dlg_save_vars(DIALOG_VARS * vars) 2929 { 2930 *vars = dialog_vars; 2931 } 2932 2933 /* 2934 * Most of the data in DIALOG_VARS is normally set by command-line options. 2935 * The input_result member is an exception; it is normally set by the dialog 2936 * library to return result values. 2937 */ 2938 void 2939 dlg_restore_vars(DIALOG_VARS * vars) 2940 { 2941 char *save_result = dialog_vars.input_result; 2942 unsigned save_length = dialog_vars.input_length; 2943 2944 dialog_vars = *vars; 2945 dialog_vars.input_result = save_result; 2946 dialog_vars.input_length = save_length; 2947 } 2948 2949 /* 2950 * Called each time a widget is invoked which may do output, increment a count. 2951 */ 2952 void 2953 dlg_does_output(void) 2954 { 2955 dialog_state.output_count += 1; 2956 } 2957 2958 /* 2959 * Compatibility for different versions of curses. 2960 */ 2961 #if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY)) 2962 int 2963 dlg_getbegx(WINDOW *win) 2964 { 2965 int y, x; 2966 getbegyx(win, y, x); 2967 return x; 2968 } 2969 int 2970 dlg_getbegy(WINDOW *win) 2971 { 2972 int y, x; 2973 getbegyx(win, y, x); 2974 return y; 2975 } 2976 #endif 2977 2978 #if !(defined(HAVE_GETCURX) && defined(HAVE_GETCURY)) 2979 int 2980 dlg_getcurx(WINDOW *win) 2981 { 2982 int y, x; 2983 getyx(win, y, x); 2984 return x; 2985 } 2986 int 2987 dlg_getcury(WINDOW *win) 2988 { 2989 int y, x; 2990 getyx(win, y, x); 2991 return y; 2992 } 2993 #endif 2994 2995 #if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY)) 2996 int 2997 dlg_getmaxx(WINDOW *win) 2998 { 2999 int y, x; 3000 getmaxyx(win, y, x); 3001 return x; 3002 } 3003 int 3004 dlg_getmaxy(WINDOW *win) 3005 { 3006 int y, x; 3007 getmaxyx(win, y, x); 3008 return y; 3009 } 3010 #endif 3011 3012 #if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY)) 3013 int 3014 dlg_getparx(WINDOW *win) 3015 { 3016 int y, x; 3017 getparyx(win, y, x); 3018 return x; 3019 } 3020 int 3021 dlg_getpary(WINDOW *win) 3022 { 3023 int y, x; 3024 getparyx(win, y, x); 3025 return y; 3026 } 3027 #endif 3028 3029 #ifdef NEED_WGETPARENT 3030 WINDOW * 3031 dlg_wgetparent(WINDOW *win) 3032 { 3033 #undef wgetparent 3034 WINDOW *result = 0; 3035 DIALOG_WINDOWS *p; 3036 3037 for (p = dialog_state.all_subwindows; p != 0; p = p->next) { 3038 if (p->shadow == win) { 3039 result = p->normal; 3040 break; 3041 } 3042 } 3043 return result; 3044 } 3045 #endif 3046