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