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