1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021-2022 Alfonso Sabato Siciliano 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 30 #include <ctype.h> 31 #include <curses.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "bsddialog.h" 37 #include "bsddialog_theme.h" 38 #include "lib_util.h" 39 40 #define TABLEN 4 /* Default tab len */ 41 #define ERRBUFLEN 1024 /* Error buffer */ 42 43 /* Error */ 44 static char errorbuffer[ERRBUFLEN]; 45 46 const char *get_error_string(void) 47 { 48 return (errorbuffer); 49 } 50 51 void set_error_string(const char *str) 52 { 53 strncpy(errorbuffer, str, ERRBUFLEN-1); 54 } 55 56 /* Clear */ 57 int hide_widget(int y, int x, int h, int w, bool withshadow) 58 { 59 WINDOW *clear; 60 61 if ((clear = newwin(h, w, y + t.shadow.h, x + t.shadow.w)) == NULL) 62 RETURN_ERROR("Cannot hide the widget"); 63 wbkgd(clear, t.screen.color); 64 65 if (withshadow) 66 wrefresh(clear); 67 68 mvwin(clear, y, x); 69 wrefresh(clear); 70 71 delwin(clear); 72 73 return (0); 74 } 75 76 /* F1 help */ 77 int f1help(struct bsddialog_conf *conf) 78 { 79 int output; 80 struct bsddialog_conf hconf; 81 82 bsddialog_initconf(&hconf); 83 hconf.title = "HELP"; 84 hconf.button.ok_label = "EXIT"; 85 hconf.clear = true; 86 hconf.ascii_lines = conf->ascii_lines; 87 hconf.no_lines = conf->no_lines; 88 hconf.shadow = conf->shadow; 89 hconf.text.highlight = conf->text.highlight; 90 91 output = BSDDIALOG_OK; 92 if (conf->key.f1_message != NULL) 93 output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0); 94 95 if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL) 96 output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0); 97 98 return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); 99 } 100 101 /* Buttons */ 102 static void 103 draw_button(WINDOW *window, int y, int x, int size, const char *text, 104 bool selected, bool shortcut) 105 { 106 int i, color_arrows, color_shortkey, color_button; 107 108 if (selected) { 109 color_arrows = t.button.f_delimcolor; 110 color_shortkey = t.button.f_shortcutcolor; 111 color_button = t.button.f_color; 112 } else { 113 color_arrows = t.button.delimcolor; 114 color_shortkey = t.button.shortcutcolor; 115 color_button = t.button.color; 116 } 117 118 wattron(window, color_arrows); 119 mvwaddch(window, y, x, t.button.leftdelim); 120 wattroff(window, color_arrows); 121 wattron(window, color_button); 122 for (i = 1; i < size - 1; i++) 123 waddch(window, ' '); 124 wattroff(window, color_button); 125 wattron(window, color_arrows); 126 mvwaddch(window, y, x + i, t.button.rightdelim); 127 wattroff(window, color_arrows); 128 129 x = x + 1 + ((size - 2 - strlen(text))/2); 130 wattron(window, color_button); 131 mvwaddstr(window, y, x, text); 132 wattroff(window, color_button); 133 134 if (shortcut) { 135 wattron(window, color_shortkey); 136 mvwaddch(window, y, x, text[0]); 137 wattroff(window, color_shortkey); 138 } 139 } 140 141 void 142 draw_buttons(WINDOW *window, struct buttons bs, bool shortcut) 143 { 144 int i, x, startx, y, rows, cols; 145 146 getmaxyx(window, rows, cols); 147 y = rows - 2; 148 149 startx = cols/2 - buttons_width(bs)/2; 150 151 for (i = 0; i < (int)bs.nbuttons; i++) { 152 x = i * (bs.sizebutton + t.button.hmargin); 153 draw_button(window, y, startx + x, bs.sizebutton, bs.label[i], 154 i == bs.curr, shortcut); 155 } 156 } 157 158 void 159 get_buttons(struct bsddialog_conf *conf, struct buttons *bs, 160 const char *yesoklabel, const char *nocancellabel) 161 { 162 int i; 163 #define SIZEBUTTON 8 164 #define DEFAULT_BUTTON_LABEL BUTTON_OK_LABEL 165 #define DEFAULT_BUTTON_VALUE BSDDIALOG_OK 166 167 bs->nbuttons = 0; 168 bs->curr = 0; 169 bs->sizebutton = 0; 170 171 if (yesoklabel != NULL && conf->button.without_ok == false) { 172 bs->label[0] = conf->button.ok_label != NULL ? 173 conf->button.ok_label : yesoklabel; 174 bs->value[0] = BSDDIALOG_OK; 175 bs->nbuttons += 1; 176 } 177 178 if (conf->button.with_extra) { 179 bs->label[bs->nbuttons] = conf->button.extra_label != NULL ? 180 conf->button.extra_label : "Extra"; 181 bs->value[bs->nbuttons] = BSDDIALOG_EXTRA; 182 bs->nbuttons += 1; 183 } 184 185 if (nocancellabel != NULL && conf->button.without_cancel == false) { 186 bs->label[bs->nbuttons] = conf->button.cancel_label ? 187 conf->button.cancel_label : nocancellabel; 188 bs->value[bs->nbuttons] = BSDDIALOG_CANCEL; 189 if (conf->button.default_cancel) 190 bs->curr = bs->nbuttons; 191 bs->nbuttons += 1; 192 } 193 194 if (conf->button.with_help) { 195 bs->label[bs->nbuttons] = conf->button.help_label != NULL ? 196 conf->button.help_label : "Help"; 197 bs->value[bs->nbuttons] = BSDDIALOG_HELP; 198 bs->nbuttons += 1; 199 } 200 201 if (conf->button.generic1_label != NULL) { 202 bs->label[bs->nbuttons] = conf->button.generic1_label; 203 bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1; 204 bs->nbuttons += 1; 205 } 206 207 if (conf->button.generic2_label != NULL) { 208 bs->label[bs->nbuttons] = conf->button.generic2_label; 209 bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2; 210 bs->nbuttons += 1; 211 } 212 213 if (bs->nbuttons == 0) { 214 bs->label[0] = DEFAULT_BUTTON_LABEL; 215 bs->value[0] = DEFAULT_BUTTON_VALUE; 216 bs->nbuttons = 1; 217 } 218 219 if (conf->button.default_label != NULL) { 220 for (i = 0; i < (int)bs->nbuttons; i++) { 221 if (strcmp(conf->button.default_label, 222 bs->label[i]) == 0) 223 bs->curr = i; 224 } 225 } 226 227 bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0])); 228 for (i = 1; i < (int)bs->nbuttons; i++) 229 bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i])); 230 bs->sizebutton += 2; 231 } 232 233 int buttons_width(struct buttons bs) 234 { 235 unsigned int width; 236 237 width = bs.nbuttons * bs.sizebutton; 238 if (bs.nbuttons > 0) 239 width += (bs.nbuttons - 1) * t.button.hmargin; 240 241 return (width); 242 } 243 244 bool shortcut_buttons(int key, struct buttons *bs) 245 { 246 bool match; 247 unsigned int i; 248 249 match = false; 250 for (i = 0; i < bs->nbuttons; i++) { 251 if (tolower(key) == tolower(bs->label[i][0])) { 252 bs->curr = i; 253 match = true; 254 break; 255 } 256 } 257 258 return (match); 259 } 260 261 /* Text */ 262 static bool is_text_attr(const char *text) 263 { 264 if (strnlen(text, 3) < 3) 265 return (false); 266 267 if (text[0] != '\\' || text[1] != 'Z') 268 return (false); 269 270 return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true); 271 } 272 273 static bool check_set_text_attr(WINDOW *win, char *text) 274 { 275 if (is_text_attr(text) == false) 276 return (false); 277 278 if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) { 279 wattron(win, bsddialog_color(text[2] - '0', COLOR_WHITE, 0)); 280 return (true); 281 } 282 283 switch (text[2]) { 284 case 'n': 285 wattron(win, t.dialog.color); 286 wattrset(win, A_NORMAL); 287 break; 288 case 'b': 289 wattron(win, A_BOLD); 290 break; 291 case 'B': 292 wattroff(win, A_BOLD); 293 break; 294 case 'r': 295 wattron(win, A_REVERSE); 296 break; 297 case 'R': 298 wattroff(win, A_REVERSE); 299 break; 300 case 'u': 301 wattron(win, A_UNDERLINE); 302 break; 303 case 'U': 304 wattroff(win, A_UNDERLINE); 305 break; 306 } 307 308 return (true); 309 } 310 311 static void 312 print_string(WINDOW *win, int *rows, int cols, int *y, int *x, char *str, 313 bool color) 314 { 315 int i, j, len, reallen; 316 317 len = reallen = strlen(str); 318 if (color) { 319 i=0; 320 while (i < len) { 321 if (is_text_attr(str+i)) 322 reallen -= 3; 323 i++; 324 } 325 } 326 327 i = 0; 328 while (i < len) { 329 if (*x + reallen > cols) { 330 *y = (*x != 0 ? *y+1 : *y); 331 if (*y >= *rows) { 332 *rows = *y + 1; 333 wresize(win, *rows, cols); 334 } 335 *x = 0; 336 } 337 j = *x; 338 while (j < cols && i < len) { 339 if (color && check_set_text_attr(win, str+i)) { 340 i += 3; 341 } else { 342 mvwaddch(win, *y, j, str[i]); 343 i++; 344 reallen--; 345 j++; 346 *x = j; 347 } 348 } 349 } 350 } 351 352 static int 353 print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) 354 { 355 bool loop; 356 int i, j, z, rows, cols, x, y, tablen; 357 char *string; 358 359 if ((string = malloc(strlen(text) + 1)) == NULL) 360 RETURN_ERROR("Cannot build (analyze) text"); 361 362 getmaxyx(pad, rows, cols); 363 tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; 364 365 i = j = x = y = 0; 366 loop = true; 367 while (loop) { 368 string[j] = text[i]; 369 370 if (strchr("\n\t ", string[j]) != NULL || string[j] == '\0') { 371 string[j] = '\0'; 372 print_string(pad, &rows, cols, &y, &x, string, 373 conf->text.highlight); 374 } 375 376 switch (text[i]) { 377 case '\0': 378 loop = false; 379 break; 380 case '\n': 381 x = 0; 382 y++; 383 j = -1; 384 break; 385 case '\t': 386 for (z = 0; z < tablen; z++) { 387 if (x >= cols) { 388 x = 0; 389 y++; 390 } 391 x++; 392 } 393 j = -1; 394 break; 395 case ' ': 396 x++; 397 if (x >= cols) { 398 x = 0; 399 y++; 400 } 401 j = -1; 402 } 403 404 if (y >= rows) { 405 rows = y + 1; 406 wresize(pad, rows, cols); 407 } 408 409 j++; 410 i++; 411 } 412 413 free(string); 414 415 return (0); 416 } 417 418 /* Autosize */ 419 static int 420 text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, 421 int mincols, bool increasecols, int *h, int *w) 422 { 423 int i, j, z, x, y; 424 int tablen, wordlen, maxwordlen, nword, maxwords, line, maxwidth; 425 int *words; 426 #define NL -1 427 #define WS -2 428 429 maxwords = 1024; 430 if ((words = calloc(maxwords, sizeof(int))) == NULL) 431 RETURN_ERROR("Cannot alloc memory for text autosize"); 432 433 tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; 434 maxwidth = widget_max_width(conf) - HBORDERS - TEXTHMARGINS; 435 436 nword = 0; 437 wordlen = 0; 438 maxwordlen = 0; 439 i=0; 440 while (true) { 441 if (conf->text.highlight && is_text_attr(text + i)) { 442 i += 3; 443 continue; 444 } 445 446 if (nword + tablen >= maxwords) { 447 maxwords += 1024; 448 if (realloc(words, maxwords * sizeof(int)) == NULL) 449 RETURN_ERROR("Cannot realloc memory for text " 450 "autosize"); 451 } 452 453 if (text[i] == '\0') { 454 words[nword] = wordlen; 455 maxwordlen = MAX(wordlen, maxwordlen); 456 break; 457 } 458 459 if (strchr("\t\n ", text[i]) != NULL) { 460 maxwordlen = MAX(wordlen, maxwordlen); 461 462 if (wordlen != 0) { 463 words[nword] = wordlen; 464 nword++; 465 wordlen = 0; 466 } 467 468 if (text[i] == '\t') { 469 for (j = 0; j < tablen; j++) 470 words[nword + j] = 1; 471 nword += tablen; 472 } else { 473 words[nword] = text[i] == '\n' ? NL : WS; 474 nword++; 475 } 476 } 477 else 478 wordlen++; 479 480 i++; 481 } 482 483 if (increasecols) { 484 mincols = MAX(mincols, maxwordlen); 485 mincols = MAX(mincols, 486 (int)conf->auto_minwidth - HBORDERS - TEXTHMARGINS); 487 mincols = MIN(mincols, maxwidth); 488 } 489 490 while (true) { 491 x = 0; 492 y = 1; 493 line=0; 494 for (i = 0; i <= nword; i++) { 495 if (words[i] == NL) { 496 y++; 497 x = 0; 498 } 499 else if (words[i] == WS) { 500 x++; 501 if (x >= mincols) { 502 x = 0; 503 y++; 504 } 505 } 506 else { 507 if (words[i] + x <= mincols) 508 x += words[i]; 509 else { 510 for (z = words[i]; z > 0; ) { 511 y++; 512 x = MIN(mincols, z); 513 z -= x; 514 } 515 } 516 } 517 line = MAX(line, x); 518 } 519 520 if (increasecols == false) 521 break; 522 if (y <= maxrows || mincols >= maxwidth) 523 break; 524 mincols++; 525 } 526 527 *h = (nword == 0 && words[0] == 0) ? 0 : y; 528 *w = MIN(mincols, line); /* wtext can be less than mincols */ 529 530 free(words); 531 532 return (0); 533 } 534 535 int 536 text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text, 537 struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext) 538 { 539 int wbuttons, maxhtext; 540 bool changewtext; 541 542 wbuttons = 0; 543 if (bs != NULL) 544 wbuttons = buttons_width(*bs); 545 546 if (cols == BSDDIALOG_AUTOSIZE) { 547 startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS); 548 changewtext = true; 549 } else if (cols == BSDDIALOG_FULLSCREEN) { 550 startwtext = widget_max_width(conf) - VBORDERS - TEXTHMARGINS; 551 changewtext = false; 552 } else { /* fixed */ 553 startwtext = cols - VBORDERS - TEXTHMARGINS; 554 changewtext = false; 555 } 556 557 if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { 558 maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext; 559 if (bs != NULL) 560 maxhtext -= 2; 561 } else { /* fixed */ 562 maxhtext = rows - VBORDERS - rowsnotext; 563 if (bs != NULL) 564 maxhtext -= 2; 565 } 566 567 if (startwtext <= 0 && changewtext) 568 startwtext = 1; 569 if (maxhtext <= 0 || startwtext <= 0) { 570 *htext = *wtext = 0; 571 return (0); 572 } 573 574 if (text_autosize(conf, text, maxhtext, startwtext, changewtext, 575 htext, wtext) != 0) 576 return (BSDDIALOG_ERROR); 577 578 return (0); 579 } 580 581 int widget_max_height(struct bsddialog_conf *conf) 582 { 583 int maxheight; 584 585 maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.h : SCREENLINES; 586 if (maxheight <= 0) 587 RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); 588 589 if (conf->y > 0) { 590 maxheight -= conf->y; 591 if (maxheight <= 0) 592 RETURN_ERROR("Terminal too small, screen lines - " 593 "shadow - y <= 0"); 594 } 595 596 return (maxheight); 597 } 598 599 int widget_max_width(struct bsddialog_conf *conf) 600 { 601 int maxwidth; 602 603 maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.w : SCREENCOLS; 604 if (maxwidth <= 0) 605 RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); 606 607 if (conf->x > 0) { 608 maxwidth -= conf->x; 609 if (maxwidth <= 0) 610 RETURN_ERROR("Terminal too small, screen cols - shadow " 611 "- x <= 0"); 612 } 613 614 return (maxwidth); 615 } 616 617 int 618 widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget, 619 bool withbuttons) 620 { 621 int min; 622 623 min = 0; 624 625 /* buttons */ 626 if (withbuttons) 627 min += 2; /* buttons and border */ 628 629 /* text */ 630 min += htext; 631 632 /* specific widget min height */ 633 min += minwidget; 634 635 /* dialog borders */ 636 min += HBORDERS; 637 /* conf.auto_minheight */ 638 min = MAX(min, (int)conf->auto_minheight); 639 /* avoid terminal overflow */ 640 min = MIN(min, widget_max_height(conf)); 641 642 return (min); 643 } 644 645 int 646 widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, 647 struct buttons *bs) 648 649 { 650 int min, delimtitle; 651 652 min = 0; 653 654 /* buttons */ 655 if (bs != NULL) 656 min += buttons_width(*bs); 657 658 /* text */ 659 if (wtext > 0) 660 min = MAX(min, wtext + TEXTHMARGINS); 661 662 /* specific widget min width */ 663 min = MAX(min, minwidget); 664 665 /* title */ 666 if (conf->title != NULL) { 667 delimtitle = t.dialog.delimtitle ? 2 : 0; 668 min = MAX(min, (int)strlen(conf->title) + 2 + delimtitle); 669 } 670 671 /* bottom title */ 672 if (conf->bottomtitle != NULL) 673 min = MAX(min, (int)strlen(conf->bottomtitle) + 4); 674 675 /* dialog borders */ 676 min += VBORDERS; 677 /* conf.auto_minwidth */ 678 min = MAX(min, (int)conf->auto_minwidth); 679 /* avoid terminal overflow */ 680 min = MIN(min, widget_max_width(conf)); 681 682 return (min); 683 } 684 685 int 686 set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) 687 { 688 int maxheight, maxwidth; 689 690 if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR) 691 return (BSDDIALOG_ERROR); 692 693 if (rows == BSDDIALOG_FULLSCREEN) 694 *h = maxheight; 695 else if (rows < BSDDIALOG_FULLSCREEN) 696 RETURN_ERROR("Negative (less than -1) height"); 697 else if (rows > BSDDIALOG_AUTOSIZE) { 698 if ((*h = rows) > maxheight) 699 RETURN_ERROR("Height too big (> terminal height - " 700 "shadow)"); 701 } 702 /* rows == AUTOSIZE: each widget has to set its size */ 703 704 if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR) 705 return (BSDDIALOG_ERROR); 706 707 if (cols == BSDDIALOG_FULLSCREEN) 708 *w = maxwidth; 709 else if (cols < BSDDIALOG_FULLSCREEN) 710 RETURN_ERROR("Negative (less than -1) width"); 711 else if (cols > BSDDIALOG_AUTOSIZE) { 712 if ((*w = cols) > maxwidth) 713 RETURN_ERROR("Width too big (> terminal width - " 714 "shadow)"); 715 } 716 /* cols == AUTOSIZE: each widget has to set its size */ 717 718 return (0); 719 } 720 721 int 722 set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) 723 { 724 if (conf->y == BSDDIALOG_CENTER) 725 *y = SCREENLINES/2 - (h + t.shadow.h)/2; 726 else if (conf->y < BSDDIALOG_CENTER) 727 RETURN_ERROR("Negative begin y (less than -1)"); 728 else if (conf->y >= SCREENLINES) 729 RETURN_ERROR("Begin Y under the terminal"); 730 else 731 *y = conf->y; 732 733 if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > SCREENLINES) 734 RETURN_ERROR("The lower of the box under the terminal " 735 "(begin Y + height (+ shadow) > terminal lines)"); 736 737 738 if (conf->x == BSDDIALOG_CENTER) 739 *x = SCREENCOLS/2 - (w + t.shadow.w)/2; 740 else if (conf->x < BSDDIALOG_CENTER) 741 RETURN_ERROR("Negative begin x (less than -1)"); 742 else if (conf->x >= SCREENCOLS) 743 RETURN_ERROR("Begin X over the right of the terminal"); 744 else 745 *x = conf->x; 746 747 if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > SCREENCOLS) 748 RETURN_ERROR("The right of the box over the terminal " 749 "(begin X + width (+ shadow) > terminal cols)"); 750 751 return (0); 752 } 753 754 /* Widgets builders */ 755 void 756 draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, 757 enum elevation elev) 758 { 759 int leftcolor, rightcolor; 760 int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee; 761 762 if (conf->no_lines) 763 return; 764 765 if (conf->ascii_lines) { 766 ls = rs = '|'; 767 ts = bs = '-'; 768 tl = tr = bl = br = ltee = rtee = '+'; 769 } else { 770 ls = rs = ACS_VLINE; 771 ts = bs = ACS_HLINE; 772 tl = ACS_ULCORNER; 773 tr = ACS_URCORNER; 774 bl = ACS_LLCORNER; 775 br = ACS_LRCORNER; 776 ltee = ACS_LTEE; 777 rtee = ACS_RTEE; 778 } 779 780 leftcolor = elev == RAISED ? 781 t.dialog.lineraisecolor : t.dialog.linelowercolor; 782 rightcolor = elev == RAISED ? 783 t.dialog.linelowercolor : t.dialog.lineraisecolor; 784 wattron(win, leftcolor); 785 wborder(win, ls, rs, ts, bs, tl, tr, bl, br); 786 wattroff(win, leftcolor); 787 788 wattron(win, rightcolor); 789 mvwaddch(win, 0, cols-1, tr); 790 mvwvline(win, 1, cols-1, rs, rows-2); 791 mvwaddch(win, rows-1, cols-1, br); 792 mvwhline(win, rows-1, 1, bs, cols-2); 793 wattroff(win, rightcolor); 794 } 795 796 WINDOW * 797 new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, 798 enum elevation elev) 799 { 800 WINDOW *win; 801 802 if ((win = newwin(rows, cols, y, x)) == NULL) { 803 set_error_string("Cannot build boxed window"); 804 return (NULL); 805 } 806 807 wbkgd(win, t.dialog.color); 808 809 draw_borders(conf, win, rows, cols, elev); 810 811 return (win); 812 } 813 814 static int 815 draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, 816 WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons) 817 { 818 int h, w, ts, ltee, rtee; 819 820 ts = conf->ascii_lines ? '-' : ACS_HLINE; 821 ltee = conf->ascii_lines ? '+' : ACS_LTEE; 822 rtee = conf->ascii_lines ? '+' : ACS_RTEE; 823 824 getmaxyx(widget, h, w); 825 826 if (conf->shadow) 827 wnoutrefresh(shadow); 828 829 draw_borders(conf, widget, h, w, RAISED); 830 831 if (conf->title != NULL) { 832 if (t.dialog.delimtitle && conf->no_lines == false) { 833 wattron(widget, t.dialog.lineraisecolor); 834 mvwaddch(widget, 0, w/2-strlen(conf->title)/2-1, rtee); 835 wattroff(widget, t.dialog.lineraisecolor); 836 } 837 wattron(widget, t.dialog.titlecolor); 838 mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title); 839 wattroff(widget, t.dialog.titlecolor); 840 if (t.dialog.delimtitle && conf->no_lines == false) { 841 wattron(widget, t.dialog.lineraisecolor); 842 waddch(widget, ltee); 843 wattroff(widget, t.dialog.lineraisecolor); 844 } 845 } 846 847 if (bs != NULL) { 848 if (conf->no_lines == false) { 849 wattron(widget, t.dialog.lineraisecolor); 850 mvwaddch(widget, h-3, 0, ltee); 851 mvwhline(widget, h-3, 1, ts, w-2); 852 wattroff(widget, t.dialog.lineraisecolor); 853 854 wattron(widget, t.dialog.linelowercolor); 855 mvwaddch(widget, h-3, w-1, rtee); 856 wattroff(widget, t.dialog.linelowercolor); 857 } 858 draw_buttons(widget, *bs, shortcutbuttons); 859 } 860 861 if (conf->bottomtitle != NULL) { 862 wattron(widget, t.dialog.bottomtitlecolor); 863 wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1); 864 waddch(widget, ' '); 865 waddstr(widget, conf->bottomtitle); 866 waddch(widget, ' '); 867 wattroff(widget, t.dialog.bottomtitlecolor); 868 } 869 870 wnoutrefresh(widget); 871 872 if (textpad != NULL && text != NULL) /* textbox */ 873 if (print_textpad(conf, textpad, text) !=0) 874 return (BSDDIALOG_ERROR); 875 876 return (0); 877 } 878 879 int 880 update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, 881 int y, int x, int h, int w, WINDOW *textpad, const char *text, 882 struct buttons *bs, bool shortcutbuttons) 883 { 884 int error; 885 886 if (conf->shadow) { 887 wclear(shadow); 888 mvwin(shadow, y + t.shadow.h, x + t.shadow.w); 889 wresize(shadow, h, w); 890 } 891 892 wclear(widget); 893 mvwin(widget, y, x); 894 wresize(widget, h, w); 895 896 if (textpad != NULL) { 897 wclear(textpad); 898 wresize(textpad, 1, w - HBORDERS - TEXTHMARGINS); 899 } 900 901 error = draw_dialog(conf, shadow, widget, textpad, text, bs, 902 shortcutbuttons); 903 904 return (error); 905 } 906 907 int 908 new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y, 909 int x, int h, int w, WINDOW **textpad, const char *text, struct buttons *bs, 910 bool shortcutbuttons) 911 { 912 int error; 913 914 if (conf->shadow) { 915 *shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w); 916 if (*shadow == NULL) 917 RETURN_ERROR("Cannot build shadow"); 918 wbkgd(*shadow, t.shadow.color); 919 } 920 921 if ((*widget = new_boxed_window(conf, y, x, h, w, RAISED)) == NULL) { 922 if (conf->shadow) 923 delwin(*shadow); 924 return (BSDDIALOG_ERROR); 925 } 926 927 if (textpad != NULL && text != NULL) { /* textbox */ 928 *textpad = newpad(1, w - HBORDERS - TEXTHMARGINS); 929 if (*textpad == NULL) { 930 delwin(*widget); 931 if (conf->shadow) 932 delwin(*shadow); 933 RETURN_ERROR("Cannot build the pad window for text"); 934 } 935 wbkgd(*textpad, t.dialog.color); 936 } 937 938 error = draw_dialog(conf, *shadow, *widget, 939 textpad == NULL ? NULL : *textpad, text, bs, shortcutbuttons); 940 941 return (error); 942 } 943 944 void 945 end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, 946 WINDOW *textpad) 947 { 948 int y, x, h, w; 949 950 getbegyx(widget, y, x); 951 getmaxyx(widget, h, w); 952 953 if (conf->sleep > 0) 954 sleep(conf->sleep); 955 956 if (textpad != NULL) 957 delwin(textpad); 958 959 delwin(widget); 960 961 if (conf->shadow) 962 delwin(shadow); 963 964 if (conf->clear) 965 hide_widget(y, x, h, w, conf->shadow); 966 967 if (conf->get_height != NULL) 968 *conf->get_height = h; 969 if (conf->get_width != NULL) 970 *conf->get_width = w; 971 }