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 + 1 >= maxwords) { 447 maxwords += 1024; 448 words = realloc(words, maxwords * sizeof(int)); 449 if (words == NULL) 450 RETURN_ERROR("Cannot realloc memory for text " 451 "autosize"); 452 } 453 454 if (text[i] == '\0') { 455 words[nword] = wordlen; 456 maxwordlen = MAX(wordlen, maxwordlen); 457 break; 458 } 459 460 if (strchr("\t\n ", text[i]) != NULL) { 461 maxwordlen = MAX(wordlen, maxwordlen); 462 463 if (wordlen != 0) { 464 words[nword] = wordlen; 465 nword++; 466 wordlen = 0; 467 } 468 469 if (text[i] == '\t') { 470 for (j = 0; j < tablen; j++) 471 words[nword + j] = 1; 472 nword += tablen; 473 } else { 474 words[nword] = text[i] == '\n' ? NL : WS; 475 nword++; 476 } 477 } 478 else 479 wordlen++; 480 481 i++; 482 } 483 484 if (increasecols) { 485 mincols = MAX(mincols, maxwordlen); 486 mincols = MAX(mincols, 487 (int)conf->auto_minwidth - HBORDERS - TEXTHMARGINS); 488 mincols = MIN(mincols, maxwidth); 489 } 490 491 while (true) { 492 x = 0; 493 y = 1; 494 line=0; 495 for (i = 0; i <= nword; i++) { 496 if (words[i] == NL) { 497 y++; 498 x = 0; 499 } 500 else if (words[i] == WS) { 501 x++; 502 if (x >= mincols) { 503 x = 0; 504 y++; 505 } 506 } 507 else { 508 if (words[i] + x <= mincols) 509 x += words[i]; 510 else { 511 for (z = words[i]; z > 0; ) { 512 y++; 513 x = MIN(mincols, z); 514 z -= x; 515 } 516 } 517 } 518 line = MAX(line, x); 519 } 520 521 if (increasecols == false) 522 break; 523 if (y <= maxrows || mincols >= maxwidth) 524 break; 525 mincols++; 526 } 527 528 *h = (nword == 0 && words[0] == 0) ? 0 : y; 529 *w = MIN(mincols, line); /* wtext can be less than mincols */ 530 531 free(words); 532 533 return (0); 534 } 535 536 int 537 text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text, 538 struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext) 539 { 540 int wbuttons, maxhtext; 541 bool changewtext; 542 543 wbuttons = 0; 544 if (bs != NULL) 545 wbuttons = buttons_width(*bs); 546 547 if (cols == BSDDIALOG_AUTOSIZE) { 548 startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS); 549 changewtext = true; 550 } else if (cols == BSDDIALOG_FULLSCREEN) { 551 startwtext = widget_max_width(conf) - VBORDERS - TEXTHMARGINS; 552 changewtext = false; 553 } else { /* fixed */ 554 startwtext = cols - VBORDERS - TEXTHMARGINS; 555 changewtext = false; 556 } 557 558 if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { 559 maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext; 560 if (bs != NULL) 561 maxhtext -= 2; 562 } else { /* fixed */ 563 maxhtext = rows - VBORDERS - rowsnotext; 564 if (bs != NULL) 565 maxhtext -= 2; 566 } 567 568 if (startwtext <= 0 && changewtext) 569 startwtext = 1; 570 if (maxhtext <= 0 || startwtext <= 0) { 571 *htext = *wtext = 0; 572 return (0); 573 } 574 575 if (text_autosize(conf, text, maxhtext, startwtext, changewtext, 576 htext, wtext) != 0) 577 return (BSDDIALOG_ERROR); 578 579 return (0); 580 } 581 582 int widget_max_height(struct bsddialog_conf *conf) 583 { 584 int maxheight; 585 586 maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.h : SCREENLINES; 587 if (maxheight <= 0) 588 RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); 589 590 if (conf->y > 0) { 591 maxheight -= conf->y; 592 if (maxheight <= 0) 593 RETURN_ERROR("Terminal too small, screen lines - " 594 "shadow - y <= 0"); 595 } 596 597 return (maxheight); 598 } 599 600 int widget_max_width(struct bsddialog_conf *conf) 601 { 602 int maxwidth; 603 604 maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.w : SCREENCOLS; 605 if (maxwidth <= 0) 606 RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); 607 608 if (conf->x > 0) { 609 maxwidth -= conf->x; 610 if (maxwidth <= 0) 611 RETURN_ERROR("Terminal too small, screen cols - shadow " 612 "- x <= 0"); 613 } 614 615 return (maxwidth); 616 } 617 618 int 619 widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget, 620 bool withbuttons) 621 { 622 int min; 623 624 min = 0; 625 626 /* buttons */ 627 if (withbuttons) 628 min += 2; /* buttons and border */ 629 630 /* text */ 631 min += htext; 632 633 /* specific widget min height */ 634 min += minwidget; 635 636 /* dialog borders */ 637 min += HBORDERS; 638 /* conf.auto_minheight */ 639 min = MAX(min, (int)conf->auto_minheight); 640 /* avoid terminal overflow */ 641 min = MIN(min, widget_max_height(conf)); 642 643 return (min); 644 } 645 646 int 647 widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, 648 struct buttons *bs) 649 650 { 651 int min, delimtitle; 652 653 min = 0; 654 655 /* buttons */ 656 if (bs != NULL) 657 min += buttons_width(*bs); 658 659 /* text */ 660 if (wtext > 0) 661 min = MAX(min, wtext + TEXTHMARGINS); 662 663 /* specific widget min width */ 664 min = MAX(min, minwidget); 665 666 /* title */ 667 if (conf->title != NULL) { 668 delimtitle = t.dialog.delimtitle ? 2 : 0; 669 min = MAX(min, (int)strlen(conf->title) + 2 + delimtitle); 670 } 671 672 /* bottom title */ 673 if (conf->bottomtitle != NULL) 674 min = MAX(min, (int)strlen(conf->bottomtitle) + 4); 675 676 /* dialog borders */ 677 min += VBORDERS; 678 /* conf.auto_minwidth */ 679 min = MAX(min, (int)conf->auto_minwidth); 680 /* avoid terminal overflow */ 681 min = MIN(min, widget_max_width(conf)); 682 683 return (min); 684 } 685 686 int 687 set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) 688 { 689 int maxheight, maxwidth; 690 691 if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR) 692 return (BSDDIALOG_ERROR); 693 694 if (rows == BSDDIALOG_FULLSCREEN) 695 *h = maxheight; 696 else if (rows < BSDDIALOG_FULLSCREEN) 697 RETURN_ERROR("Negative (less than -1) height"); 698 else if (rows > BSDDIALOG_AUTOSIZE) { 699 if ((*h = rows) > maxheight) 700 RETURN_ERROR("Height too big (> terminal height - " 701 "shadow)"); 702 } 703 /* rows == AUTOSIZE: each widget has to set its size */ 704 705 if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR) 706 return (BSDDIALOG_ERROR); 707 708 if (cols == BSDDIALOG_FULLSCREEN) 709 *w = maxwidth; 710 else if (cols < BSDDIALOG_FULLSCREEN) 711 RETURN_ERROR("Negative (less than -1) width"); 712 else if (cols > BSDDIALOG_AUTOSIZE) { 713 if ((*w = cols) > maxwidth) 714 RETURN_ERROR("Width too big (> terminal width - " 715 "shadow)"); 716 } 717 /* cols == AUTOSIZE: each widget has to set its size */ 718 719 return (0); 720 } 721 722 int 723 set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) 724 { 725 if (conf->y == BSDDIALOG_CENTER) 726 *y = SCREENLINES/2 - (h + t.shadow.h)/2; 727 else if (conf->y < BSDDIALOG_CENTER) 728 RETURN_ERROR("Negative begin y (less than -1)"); 729 else if (conf->y >= SCREENLINES) 730 RETURN_ERROR("Begin Y under the terminal"); 731 else 732 *y = conf->y; 733 734 if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > SCREENLINES) 735 RETURN_ERROR("The lower of the box under the terminal " 736 "(begin Y + height (+ shadow) > terminal lines)"); 737 738 739 if (conf->x == BSDDIALOG_CENTER) 740 *x = SCREENCOLS/2 - (w + t.shadow.w)/2; 741 else if (conf->x < BSDDIALOG_CENTER) 742 RETURN_ERROR("Negative begin x (less than -1)"); 743 else if (conf->x >= SCREENCOLS) 744 RETURN_ERROR("Begin X over the right of the terminal"); 745 else 746 *x = conf->x; 747 748 if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > SCREENCOLS) 749 RETURN_ERROR("The right of the box over the terminal " 750 "(begin X + width (+ shadow) > terminal cols)"); 751 752 return (0); 753 } 754 755 /* Widgets builders */ 756 void 757 draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, 758 enum elevation elev) 759 { 760 int leftcolor, rightcolor; 761 int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee; 762 763 if (conf->no_lines) 764 return; 765 766 if (conf->ascii_lines) { 767 ls = rs = '|'; 768 ts = bs = '-'; 769 tl = tr = bl = br = ltee = rtee = '+'; 770 } else { 771 ls = rs = ACS_VLINE; 772 ts = bs = ACS_HLINE; 773 tl = ACS_ULCORNER; 774 tr = ACS_URCORNER; 775 bl = ACS_LLCORNER; 776 br = ACS_LRCORNER; 777 ltee = ACS_LTEE; 778 rtee = ACS_RTEE; 779 } 780 781 leftcolor = elev == RAISED ? 782 t.dialog.lineraisecolor : t.dialog.linelowercolor; 783 rightcolor = elev == RAISED ? 784 t.dialog.linelowercolor : t.dialog.lineraisecolor; 785 wattron(win, leftcolor); 786 wborder(win, ls, rs, ts, bs, tl, tr, bl, br); 787 wattroff(win, leftcolor); 788 789 wattron(win, rightcolor); 790 mvwaddch(win, 0, cols-1, tr); 791 mvwvline(win, 1, cols-1, rs, rows-2); 792 mvwaddch(win, rows-1, cols-1, br); 793 mvwhline(win, rows-1, 1, bs, cols-2); 794 wattroff(win, rightcolor); 795 } 796 797 WINDOW * 798 new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, 799 enum elevation elev) 800 { 801 WINDOW *win; 802 803 if ((win = newwin(rows, cols, y, x)) == NULL) { 804 set_error_string("Cannot build boxed window"); 805 return (NULL); 806 } 807 808 wbkgd(win, t.dialog.color); 809 810 draw_borders(conf, win, rows, cols, elev); 811 812 return (win); 813 } 814 815 static int 816 draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, 817 WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons) 818 { 819 int h, w, ts, ltee, rtee; 820 821 ts = conf->ascii_lines ? '-' : ACS_HLINE; 822 ltee = conf->ascii_lines ? '+' : ACS_LTEE; 823 rtee = conf->ascii_lines ? '+' : ACS_RTEE; 824 825 getmaxyx(widget, h, w); 826 827 if (conf->shadow) 828 wnoutrefresh(shadow); 829 830 draw_borders(conf, widget, h, w, RAISED); 831 832 if (conf->title != NULL) { 833 if (t.dialog.delimtitle && conf->no_lines == false) { 834 wattron(widget, t.dialog.lineraisecolor); 835 mvwaddch(widget, 0, w/2-strlen(conf->title)/2-1, rtee); 836 wattroff(widget, t.dialog.lineraisecolor); 837 } 838 wattron(widget, t.dialog.titlecolor); 839 mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title); 840 wattroff(widget, t.dialog.titlecolor); 841 if (t.dialog.delimtitle && conf->no_lines == false) { 842 wattron(widget, t.dialog.lineraisecolor); 843 waddch(widget, ltee); 844 wattroff(widget, t.dialog.lineraisecolor); 845 } 846 } 847 848 if (bs != NULL) { 849 if (conf->no_lines == false) { 850 wattron(widget, t.dialog.lineraisecolor); 851 mvwaddch(widget, h-3, 0, ltee); 852 mvwhline(widget, h-3, 1, ts, w-2); 853 wattroff(widget, t.dialog.lineraisecolor); 854 855 wattron(widget, t.dialog.linelowercolor); 856 mvwaddch(widget, h-3, w-1, rtee); 857 wattroff(widget, t.dialog.linelowercolor); 858 } 859 draw_buttons(widget, *bs, shortcutbuttons); 860 } 861 862 if (conf->bottomtitle != NULL) { 863 wattron(widget, t.dialog.bottomtitlecolor); 864 wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1); 865 waddch(widget, ' '); 866 waddstr(widget, conf->bottomtitle); 867 waddch(widget, ' '); 868 wattroff(widget, t.dialog.bottomtitlecolor); 869 } 870 871 wnoutrefresh(widget); 872 873 if (textpad != NULL && text != NULL) /* textbox */ 874 if (print_textpad(conf, textpad, text) !=0) 875 return (BSDDIALOG_ERROR); 876 877 return (0); 878 } 879 880 int 881 update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, 882 int y, int x, int h, int w, WINDOW *textpad, const char *text, 883 struct buttons *bs, bool shortcutbuttons) 884 { 885 int error; 886 887 if (conf->shadow) { 888 wclear(shadow); 889 mvwin(shadow, y + t.shadow.h, x + t.shadow.w); 890 wresize(shadow, h, w); 891 } 892 893 wclear(widget); 894 mvwin(widget, y, x); 895 wresize(widget, h, w); 896 897 if (textpad != NULL) { 898 wclear(textpad); 899 wresize(textpad, 1, w - HBORDERS - TEXTHMARGINS); 900 } 901 902 error = draw_dialog(conf, shadow, widget, textpad, text, bs, 903 shortcutbuttons); 904 905 return (error); 906 } 907 908 int 909 new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y, 910 int x, int h, int w, WINDOW **textpad, const char *text, struct buttons *bs, 911 bool shortcutbuttons) 912 { 913 int error; 914 915 if (conf->shadow) { 916 *shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w); 917 if (*shadow == NULL) 918 RETURN_ERROR("Cannot build shadow"); 919 wbkgd(*shadow, t.shadow.color); 920 } 921 922 if ((*widget = new_boxed_window(conf, y, x, h, w, RAISED)) == NULL) { 923 if (conf->shadow) 924 delwin(*shadow); 925 return (BSDDIALOG_ERROR); 926 } 927 928 if (textpad != NULL && text != NULL) { /* textbox */ 929 *textpad = newpad(1, w - HBORDERS - TEXTHMARGINS); 930 if (*textpad == NULL) { 931 delwin(*widget); 932 if (conf->shadow) 933 delwin(*shadow); 934 RETURN_ERROR("Cannot build the pad window for text"); 935 } 936 wbkgd(*textpad, t.dialog.color); 937 } 938 939 error = draw_dialog(conf, *shadow, *widget, 940 textpad == NULL ? NULL : *textpad, text, bs, shortcutbuttons); 941 942 return (error); 943 } 944 945 void 946 end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, 947 WINDOW *textpad) 948 { 949 int y, x, h, w; 950 951 getbegyx(widget, y, x); 952 getmaxyx(widget, h, w); 953 954 if (conf->sleep > 0) 955 sleep(conf->sleep); 956 957 if (textpad != NULL) 958 delwin(textpad); 959 960 delwin(widget); 961 962 if (conf->shadow) 963 delwin(shadow); 964 965 if (conf->clear) 966 hide_widget(y, x, h, w, conf->shadow); 967 968 if (conf->get_height != NULL) 969 *conf->get_height = h; 970 if (conf->get_width != NULL) 971 *conf->get_width = w; 972 } 973