/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021-2022 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" #define ERRBUFLEN 1024 /* Error buffer len */ /* Error */ static char errorbuffer[ERRBUFLEN]; const char *get_error_string(void) { return (errorbuffer); } void set_error_string(const char *str) { strncpy(errorbuffer, str, ERRBUFLEN-1); } /* Unicode */ wchar_t* alloc_mbstows(const char *mbstring) { size_t charlen, nchar; mbstate_t mbs; const char *pmbstring; wchar_t *wstring; nchar = 1; pmbstring = mbstring; memset(&mbs, 0, sizeof(mbs)); while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 && charlen != (size_t)-1 && charlen != (size_t)-2) { pmbstring += charlen; nchar++; } if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL) return (NULL); mbstowcs(wstring, mbstring, nchar); return (wstring); } void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch) { wchar_t ws[2]; ws[0] = wch; ws[1] = L'\0'; mvwaddwstr(w, y, x, ws); } int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col) { bool multicol; int w; unsigned int ncol; size_t charlen, mb_cur_max; wchar_t wch; mbstate_t mbs; multicol = false; mb_cur_max = MB_CUR_MAX; ncol = 0; memset(&mbs, 0, sizeof(mbs)); while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 && charlen != (size_t)-1 && charlen != (size_t)-2) { if (mbtowc(&wch, mbstring, mb_cur_max) < 0) return (-1); w = (wch == L'\t') ? TABSIZE : wcwidth(wch); ncol += (w < 0) ? 0 : w; if (w > 1 && wch != L'\t') multicol = true; mbstring += charlen; } if (cols != NULL) *cols = ncol; if (has_multi_col != NULL) *has_multi_col = multicol; return (0); } unsigned int strcols(const char *mbstring) { int w; unsigned int ncol; size_t charlen, mb_cur_max; wchar_t wch; mbstate_t mbs; mb_cur_max = MB_CUR_MAX; ncol = 0; memset(&mbs, 0, sizeof(mbs)); while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 && charlen != (size_t)-1 && charlen != (size_t)-2) { if (mbtowc(&wch, mbstring, mb_cur_max) < 0) return (0); w = (wch == L'\t') ? TABSIZE : wcwidth(wch); ncol += (w < 0) ? 0 : w; mbstring += charlen; } return (ncol); } /* Clear */ int hide_widget(int y, int x, int h, int w, bool withshadow) { WINDOW *clear; if ((clear = newwin(h, w, y + t.shadow.y, x + t.shadow.x)) == NULL) RETURN_ERROR("Cannot hide the widget"); wbkgd(clear, t.screen.color); if (withshadow) wrefresh(clear); mvwin(clear, y, x); wrefresh(clear); delwin(clear); return (0); } /* F1 help */ int f1help(struct bsddialog_conf *conf) { int output; struct bsddialog_conf hconf; bsddialog_initconf(&hconf); hconf.title = "HELP"; hconf.button.ok_label = "EXIT"; hconf.clear = true; hconf.ascii_lines = conf->ascii_lines; hconf.no_lines = conf->no_lines; hconf.shadow = conf->shadow; hconf.text.highlight = conf->text.highlight; output = BSDDIALOG_OK; if (conf->key.f1_message != NULL) output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0); if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL) output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0); return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); } /* Buttons */ static void draw_button(WINDOW *window, int y, int x, int size, const char *text, wchar_t first, bool selected, bool shortcut) { int i, color_arrows, color_shortkey, color_button; if (selected) { color_arrows = t.button.f_delimcolor; color_shortkey = t.button.f_shortcutcolor; color_button = t.button.f_color; } else { color_arrows = t.button.delimcolor; color_shortkey = t.button.shortcutcolor; color_button = t.button.color; } wattron(window, color_arrows); mvwaddch(window, y, x, t.button.leftdelim); wattroff(window, color_arrows); wattron(window, color_button); for (i = 1; i < size - 1; i++) waddch(window, ' '); wattroff(window, color_button); wattron(window, color_arrows); mvwaddch(window, y, x + i, t.button.rightdelim); wattroff(window, color_arrows); x = x + 1 + ((size - 2 - strcols(text))/2); wattron(window, color_button); mvwaddstr(window, y, x, text); wattroff(window, color_button); if (shortcut) { wattron(window, color_shortkey); mvwaddwch(window, y, x, first); wattroff(window, color_shortkey); } } void draw_buttons(WINDOW *window, struct buttons bs, bool shortcut) { int i, x, startx, y, rows, cols; unsigned int newmargin, margin, wbuttons; getmaxyx(window, rows, cols); y = rows - 2; newmargin = cols - VBORDERS - (bs.nbuttons * bs.sizebutton); newmargin /= (bs.nbuttons + 1); newmargin = MIN(newmargin, t.button.maxmargin); if (newmargin == 0) { margin = t.button.minmargin; wbuttons = buttons_min_width(bs); } else { margin = newmargin; wbuttons = bs.nbuttons * bs.sizebutton; wbuttons += (bs.nbuttons + 1) * margin; } startx = (cols)/2 - wbuttons/2 + newmargin; for (i = 0; i < (int)bs.nbuttons; i++) { x = i * (bs.sizebutton + margin); draw_button(window, y, startx + x, bs.sizebutton, bs.label[i], bs.first[i], i == bs.curr, shortcut); } } void get_buttons(struct bsddialog_conf *conf, struct buttons *bs, const char *yesoklabel, const char *nocancellabel) { int i; #define SIZEBUTTON 8 #define DEFAULT_BUTTON_LABEL BUTTON_OK_LABEL #define DEFAULT_BUTTON_VALUE BSDDIALOG_OK wchar_t first; bs->nbuttons = 0; bs->curr = 0; bs->sizebutton = 0; if (yesoklabel != NULL && conf->button.without_ok == false) { bs->label[0] = conf->button.ok_label != NULL ? conf->button.ok_label : yesoklabel; bs->value[0] = BSDDIALOG_OK; bs->nbuttons += 1; } if (conf->button.with_extra) { bs->label[bs->nbuttons] = conf->button.extra_label != NULL ? conf->button.extra_label : "Extra"; bs->value[bs->nbuttons] = BSDDIALOG_EXTRA; bs->nbuttons += 1; } if (nocancellabel != NULL && conf->button.without_cancel == false) { bs->label[bs->nbuttons] = conf->button.cancel_label ? conf->button.cancel_label : nocancellabel; bs->value[bs->nbuttons] = BSDDIALOG_CANCEL; if (conf->button.default_cancel) bs->curr = bs->nbuttons; bs->nbuttons += 1; } if (conf->button.with_help) { bs->label[bs->nbuttons] = conf->button.help_label != NULL ? conf->button.help_label : "Help"; bs->value[bs->nbuttons] = BSDDIALOG_HELP; bs->nbuttons += 1; } if (conf->button.generic1_label != NULL) { bs->label[bs->nbuttons] = conf->button.generic1_label; bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1; bs->nbuttons += 1; } if (conf->button.generic2_label != NULL) { bs->label[bs->nbuttons] = conf->button.generic2_label; bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2; bs->nbuttons += 1; } if (bs->nbuttons == 0) { bs->label[0] = DEFAULT_BUTTON_LABEL; bs->value[0] = DEFAULT_BUTTON_VALUE; bs->nbuttons = 1; } for (i = 0; i < (int)bs->nbuttons; i++) { mbtowc(&first, bs->label[i], MB_CUR_MAX); bs->first[i] = first; } if (conf->button.default_label != NULL) { for (i = 0; i < (int)bs->nbuttons; i++) { if (strcmp(conf->button.default_label, bs->label[i]) == 0) bs->curr = i; } } bs->sizebutton = MAX(SIZEBUTTON - 2, strcols(bs->label[0])); for (i = 1; i < (int)bs->nbuttons; i++) bs->sizebutton = MAX(bs->sizebutton, strcols(bs->label[i])); bs->sizebutton += 2; } int buttons_min_width(struct buttons bs) { unsigned int width; width = bs.nbuttons * bs.sizebutton; if (bs.nbuttons > 0) width += (bs.nbuttons - 1) * t.button.minmargin; return (width); } bool shortcut_buttons(wint_t key, struct buttons *bs) { bool match; unsigned int i; match = false; for (i = 0; i < bs->nbuttons; i++) { if (towlower(key) == towlower(bs->first[i])) { bs->curr = i; match = true; break; } } return (match); } /* Text */ static bool is_wtext_attr(const wchar_t *wtext) { if (wcsnlen(wtext, 3) < 3) return (false); if (wtext[0] != L'\\' || wtext[1] != L'Z') return (false); return (wcschr(L"nbBrRuU01234567", wtext[2]) == NULL ? false : true); } static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext) { enum bsddialog_color bg; if (is_wtext_attr(wtext) == false) return (false); if ((wtext[2] - L'0') >= 0 && (wtext[2] - L'0') < 8) { bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL); wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0)); return (true); } switch (wtext[2]) { case L'n': wattron(win, t.dialog.color); wattrset(win, A_NORMAL); break; case L'b': wattron(win, A_BOLD); break; case L'B': wattroff(win, A_BOLD); break; case L'r': wattron(win, A_REVERSE); break; case L'R': wattroff(win, A_REVERSE); break; case L'u': wattron(win, A_UNDERLINE); break; case L'U': wattroff(win, A_UNDERLINE); break; } return (true); } /* Word Wrapping */ static void print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str, bool color) { int i, j, len, reallen, wc; wchar_t ws[2]; ws[1] = L'\0'; len = wcslen(str); if (color) { reallen = 0; i=0; while (i < len) { if (is_wtext_attr(str+i) == false) reallen += wcwidth(str[i]); i++; } } else reallen = wcswidth(str, len); i = 0; while (i < len) { if (*x + reallen > cols) { *y = (*x != 0 ? *y+1 : *y); if (*y >= *rows) { *rows = *y + 1; wresize(win, *rows, cols); } *x = 0; } j = *x; while (j < cols && i < len) { if (color && check_set_wtext_attr(win, str+i)) { i += 3; } else if (j + wcwidth(str[i]) > cols) { break; } else { /* inline mvwaddwch() for efficiency */ ws[0] = str[i]; mvwaddwstr(win, *y, j, ws); wc = wcwidth(str[i]);; reallen -= wc; j += wc; i++; *x = j; } } } } static int print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) { bool loop; int i, j, z, rows, cols, x, y, tablen; wchar_t *wtext, *string; if ((wtext = alloc_mbstows(text)) == NULL) RETURN_ERROR("Cannot allocate/print text in wchar_t*"); if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL) RETURN_ERROR("Cannot build (analyze) text"); getmaxyx(pad, rows, cols); tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; i = j = x = y = 0; loop = true; while (loop) { string[j] = wtext[i]; if (wcschr(L"\n\t ", string[j]) != NULL || string[j] == L'\0') { string[j] = L'\0'; print_string(pad, &rows, cols, &y, &x, string, conf->text.highlight); } switch (wtext[i]) { case L'\0': loop = false; break; case L'\n': x = 0; y++; j = -1; break; case L'\t': for (z = 0; z < tablen; z++) { if (x >= cols) { x = 0; y++; } x++; } j = -1; break; case L' ': x++; if (x >= cols) { x = 0; y++; } j = -1; } if (y >= rows) { rows = y + 1; wresize(pad, rows, cols); } j++; i++; } free(wtext); free(string); return (0); } /* Text Autosize */ #define NL -1 #define WS -2 #define TB -3 struct textproperties { int nword; int *words; uint8_t *wletters; int maxwordcols; int maxline; bool hasnewline; }; static int text_properties(struct bsddialog_conf *conf, const char *text, struct textproperties *tp) { int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols; wchar_t *wtext; tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; maxwords = 1024; if ((tp->words = calloc(maxwords, sizeof(int))) == NULL) RETURN_ERROR("Cannot alloc memory for text autosize"); if ((wtext = alloc_mbstows(text)) == NULL) RETURN_ERROR("Cannot allocate/autosize text in wchar_t*"); wtextlen = wcslen(wtext); if ((tp->wletters = calloc(wtextlen, sizeof(uint8_t))) == NULL) RETURN_ERROR("Cannot allocate wletters for text autosizing"); tp->nword = 0; tp->maxline = 0; tp->maxwordcols = 0; tp->hasnewline = false; currlinecols = 0; wordcols = 0; l = 0; for (i = 0; i < wtextlen; i++) { if (conf->text.highlight && is_wtext_attr(wtext + i)) { i += 2; /* +1 for update statement */ continue; } if (tp->nword + 1 >= maxwords) { maxwords += 1024; tp->words = realloc(tp->words, maxwords * sizeof(int)); if (tp->words == NULL) RETURN_ERROR("Cannot realloc memory for text " "autosize"); } if (wcschr(L"\t\n ", wtext[i]) != NULL) { tp->maxwordcols = MAX(wordcols, tp->maxwordcols); if (wordcols != 0) { /* line */ currlinecols += wordcols; /* word */ tp->words[tp->nword] = wordcols; tp->nword += 1; wordcols = 0; } switch (wtext[i]) { case L'\t': /* line */ currlinecols += tablen; /* word */ tp->words[tp->nword] = TB; break; case L'\n': /* line */ tp->hasnewline = true; tp->maxline = MAX(tp->maxline, currlinecols); currlinecols = 0; /* word */ tp->words[tp->nword] = NL; break; case L' ': /* line */ currlinecols += 1; /* word */ tp->words[tp->nword] = WS; break; } tp->nword += 1; } else { tp->wletters[l] = wcwidth(wtext[i]); wordcols += tp->wletters[l]; l++; } } /* word */ if (wordcols != 0) { tp->words[tp->nword] = wordcols; tp->nword += 1; tp->maxwordcols = MAX(wordcols, tp->maxwordcols); } /* line */ tp->maxline = MAX(tp->maxline, currlinecols); free(wtext); return (0); } static int text_autosize(struct bsddialog_conf *conf, struct textproperties *tp, int maxrows, int mincols, bool increasecols, int *h, int *w) { int i, j, x, y, z, l, line, maxwidth, tablen; maxwidth = widget_max_width(conf) - HBORDERS - TEXTHMARGINS; tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; if (increasecols) { mincols = MAX(mincols, tp->maxwordcols); mincols = MAX(mincols, (int)conf->auto_minwidth - HBORDERS - TEXTHMARGINS); mincols = MIN(mincols, maxwidth); } while (true) { x = 0; y = 1; line=0; l = 0; for (i = 0; i < tp->nword; i++) { switch (tp->words[i]) { case TB: for (j = 0; j < tablen; j++) { if (x >= mincols) { x = 0; y++; } x++; } break; case NL: y++; x = 0; break; case WS: x++; if (x >= mincols) { x = 0; y++; } break; default: if (tp->words[i] + x <= mincols) { x += tp->words[i]; for (z = 0 ; z != tp->words[i]; l++ ) z += tp->wletters[l]; } else if (tp->words[i] <= mincols) { y++; x = tp->words[i]; for (z = 0 ; z != tp->words[i]; l++ ) z += tp->wletters[l]; } else { for (j = tp->words[i]; j > 0; ) { y = (x == 0) ? y : y + 1; z = 0; while (z != j && z < mincols) { z += tp->wletters[l]; l++; } x = z; line = MAX(line, x); j -= z; } } } line = MAX(line, x); } if (increasecols == false) break; if (mincols >= maxwidth) break; if (line >= y * (int)conf->text.cols_per_row && y <= maxrows) break; mincols++; } *h = (tp->nword == 0) ? 0 : y; *w = MIN(mincols, line); /* wtext can be less than mincols */ return (0); } int text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text, struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext) { bool changewtext; int wbuttons, maxhtext; struct textproperties tp; wbuttons = 0; if (bs != NULL) wbuttons = buttons_min_width(*bs); /* Rows */ if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext; } else { /* fixed */ maxhtext = rows - VBORDERS - rowsnotext; } if (bs != NULL) maxhtext -= 2; if (maxhtext <= 0) maxhtext = 1; /* text_autosize() computes always htext */ /* Cols */ if (cols == BSDDIALOG_AUTOSIZE) { startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS); changewtext = true; } else if (cols == BSDDIALOG_FULLSCREEN) { startwtext = widget_max_width(conf) - VBORDERS - TEXTHMARGINS; changewtext = false; } else { /* fixed */ startwtext = cols - VBORDERS - TEXTHMARGINS; changewtext = false; } if (startwtext <= 0 && changewtext) startwtext = 1; if (startwtext <= 0) RETURN_ERROR("Fullscreen or fixed cols to print text <=0"); /* Sizing calculation */ if (text_properties(conf, text, &tp) != 0) return (BSDDIALOG_ERROR); if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext, wtext) != 0) return (BSDDIALOG_ERROR); free(tp.words); free(tp.wletters); return (0); } /* Widget size and position */ int widget_max_height(struct bsddialog_conf *conf) { int maxheight; maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES; if (maxheight <= 0) RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); if (conf->y != BSDDIALOG_CENTER && conf->auto_topmargin > 0) RETURN_ERROR("conf.y > 0 and conf->auto_topmargin > 0"); else if (conf->y == BSDDIALOG_CENTER) { maxheight -= conf->auto_topmargin; if (maxheight <= 0) RETURN_ERROR("Terminal too small, screen lines - top " "margins <= 0"); } else if (conf->y > 0) { maxheight -= conf->y; if (maxheight <= 0) RETURN_ERROR("Terminal too small, screen lines - " "shadow - y <= 0"); } maxheight -= conf->auto_downmargin; if (maxheight <= 0) RETURN_ERROR("Terminal too small, screen lines - Down margins " "<= 0"); return (maxheight); } int widget_max_width(struct bsddialog_conf *conf) { int maxwidth; maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS; if (maxwidth <= 0) RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); if (conf->x > 0) { maxwidth -= conf->x; if (maxwidth <= 0) RETURN_ERROR("Terminal too small, screen cols - shadow " "- x <= 0"); } return (maxwidth); } int widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget, bool withbuttons) { int min; min = 0; /* buttons */ if (withbuttons) min += 2; /* buttons and border */ /* text */ min += htext; /* specific widget min height */ min += minwidget; /* dialog borders */ min += HBORDERS; /* conf.auto_minheight */ min = MAX(min, (int)conf->auto_minheight); /* avoid terminal overflow */ min = MIN(min, widget_max_height(conf)); return (min); } int widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, struct buttons *bs) { int min, delimtitle, wbottomtitle, wtitle; min = 0; /* buttons */ if (bs != NULL) min += buttons_min_width(*bs); /* text */ if (wtext > 0) min = MAX(min, wtext + TEXTHMARGINS); /* specific widget min width */ min = MAX(min, minwidget); /* title */ if (conf->title != NULL) { delimtitle = t.dialog.delimtitle ? 2 : 0; wtitle = strcols(conf->title); min = MAX(min, wtitle + 2 + delimtitle); } /* bottom title */ if (conf->bottomtitle != NULL) { wbottomtitle = strcols(conf->bottomtitle); min = MAX(min, wbottomtitle + 4); } /* dialog borders */ min += VBORDERS; /* conf.auto_minwidth */ min = MAX(min, (int)conf->auto_minwidth); /* avoid terminal overflow */ min = MIN(min, widget_max_width(conf)); return (min); } int set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) { int maxheight, maxwidth; if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR) return (BSDDIALOG_ERROR); if (rows == BSDDIALOG_FULLSCREEN) *h = maxheight; else if (rows < BSDDIALOG_FULLSCREEN) RETURN_ERROR("Negative (less than -1) height"); else if (rows > BSDDIALOG_AUTOSIZE) /* fixed rows */ *h = MIN(rows, maxheight); /* rows is at most maxheight */ /* rows == AUTOSIZE: each widget has to set its size */ if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR) return (BSDDIALOG_ERROR); if (cols == BSDDIALOG_FULLSCREEN) *w = maxwidth; else if (cols < BSDDIALOG_FULLSCREEN) RETURN_ERROR("Negative (less than -1) width"); else if (cols > BSDDIALOG_AUTOSIZE) /* fixed cols */ *w = MIN(cols, maxwidth); /* cols is at most maxwidth */ /* cols == AUTOSIZE: each widget has to set its size */ return (0); } int set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) { int hshadow = conf->shadow ? (int)t.shadow.y : 0; int wshadow = conf->shadow ? (int)t.shadow.x : 0; if (conf->y == BSDDIALOG_CENTER) { *y = SCREENLINES/2 - (h + hshadow)/2; if (*y < (int)conf->auto_topmargin) *y = conf->auto_topmargin; if (*y + h + hshadow > SCREENLINES - (int)conf->auto_downmargin) *y = SCREENLINES - h - hshadow - conf->auto_downmargin; } else if (conf->y < BSDDIALOG_CENTER) RETURN_ERROR("Negative begin y (less than -1)"); else if (conf->y >= SCREENLINES) RETURN_ERROR("Begin Y under the terminal"); else *y = conf->y; if (*y + h + hshadow > SCREENLINES) RETURN_ERROR("The lower of the box under the terminal " "(begin Y + height (+ shadow) > terminal lines)"); if (conf->x == BSDDIALOG_CENTER) *x = SCREENCOLS/2 - (w + wshadow)/2; else if (conf->x < BSDDIALOG_CENTER) RETURN_ERROR("Negative begin x (less than -1)"); else if (conf->x >= SCREENCOLS) RETURN_ERROR("Begin X over the right of the terminal"); else *x = conf->x; if ((*x + w + wshadow) > SCREENCOLS) RETURN_ERROR("The right of the box over the terminal " "(begin X + width (+ shadow) > terminal cols)"); return (0); } /* Widgets build, update, destroy */ void draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, enum elevation elev) { int leftcolor, rightcolor; int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee; if (conf->no_lines) return; if (conf->ascii_lines) { ls = rs = '|'; ts = bs = '-'; tl = tr = bl = br = ltee = rtee = '+'; } else { ls = rs = ACS_VLINE; ts = bs = ACS_HLINE; tl = ACS_ULCORNER; tr = ACS_URCORNER; bl = ACS_LLCORNER; br = ACS_LRCORNER; ltee = ACS_LTEE; rtee = ACS_RTEE; } leftcolor = elev == RAISED ? t.dialog.lineraisecolor : t.dialog.linelowercolor; rightcolor = elev == RAISED ? t.dialog.linelowercolor : t.dialog.lineraisecolor; wattron(win, leftcolor); wborder(win, ls, rs, ts, bs, tl, tr, bl, br); wattroff(win, leftcolor); wattron(win, rightcolor); mvwaddch(win, 0, cols-1, tr); mvwvline(win, 1, cols-1, rs, rows-2); mvwaddch(win, rows-1, cols-1, br); mvwhline(win, rows-1, 1, bs, cols-2); wattroff(win, rightcolor); } WINDOW * new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, enum elevation elev) { WINDOW *win; if ((win = newwin(rows, cols, y, x)) == NULL) { set_error_string("Cannot build boxed window"); return (NULL); } wbkgd(win, t.dialog.color); draw_borders(conf, win, rows, cols, elev); return (win); } static int draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons) { int h, w, wtitle, wbottomtitle, ts, ltee, rtee; ts = conf->ascii_lines ? '-' : ACS_HLINE; ltee = conf->ascii_lines ? '+' : ACS_LTEE; rtee = conf->ascii_lines ? '+' : ACS_RTEE; getmaxyx(widget, h, w); if (conf->shadow) wnoutrefresh(shadow); draw_borders(conf, widget, h, w, RAISED); if (conf->title != NULL) { if ((wtitle = strcols(conf->title)) < 0) return (BSDDIALOG_ERROR); if (t.dialog.delimtitle && conf->no_lines == false) { wattron(widget, t.dialog.lineraisecolor); mvwaddch(widget, 0, w/2 - wtitle/2 -1, rtee); wattroff(widget, t.dialog.lineraisecolor); } wattron(widget, t.dialog.titlecolor); mvwaddstr(widget, 0, w/2 - wtitle/2, conf->title); wattroff(widget, t.dialog.titlecolor); if (t.dialog.delimtitle && conf->no_lines == false) { wattron(widget, t.dialog.lineraisecolor); waddch(widget, ltee); wattroff(widget, t.dialog.lineraisecolor); } } if (bs != NULL) { if (conf->no_lines == false) { wattron(widget, t.dialog.lineraisecolor); mvwaddch(widget, h-3, 0, ltee); mvwhline(widget, h-3, 1, ts, w-2); wattroff(widget, t.dialog.lineraisecolor); wattron(widget, t.dialog.linelowercolor); mvwaddch(widget, h-3, w-1, rtee); wattroff(widget, t.dialog.linelowercolor); } draw_buttons(widget, *bs, shortcutbuttons); } if (conf->bottomtitle != NULL) { if ((wbottomtitle = strcols(conf->bottomtitle)) < 0) return (BSDDIALOG_ERROR); wattron(widget, t.dialog.bottomtitlecolor); wmove(widget, h - 1, w/2 - wbottomtitle/2 - 1); waddch(widget, ' '); waddstr(widget, conf->bottomtitle); waddch(widget, ' '); wattroff(widget, t.dialog.bottomtitlecolor); } wnoutrefresh(widget); if (textpad != NULL && text != NULL) /* textbox */ if (print_textpad(conf, textpad, text) !=0) return (BSDDIALOG_ERROR); return (0); } int update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, int y, int x, int h, int w, WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons) { int error; if (conf->shadow) { wclear(shadow); mvwin(shadow, y + t.shadow.y, x + t.shadow.x); wresize(shadow, h, w); } wclear(widget); mvwin(widget, y, x); wresize(widget, h, w); if (textpad != NULL) { wclear(textpad); wresize(textpad, 1, w - HBORDERS - TEXTHMARGINS); } error = draw_dialog(conf, shadow, widget, textpad, text, bs, shortcutbuttons); return (error); } int new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y, int x, int h, int w, WINDOW **textpad, const char *text, struct buttons *bs, bool shortcutbuttons) { int error; if (conf->shadow) { *shadow = newwin(h, w, y + t.shadow.y, x + t.shadow.x); if (*shadow == NULL) RETURN_ERROR("Cannot build shadow"); wbkgd(*shadow, t.shadow.color); } if ((*widget = new_boxed_window(conf, y, x, h, w, RAISED)) == NULL) { if (conf->shadow) delwin(*shadow); return (BSDDIALOG_ERROR); } if (textpad != NULL && text != NULL) { /* textbox */ *textpad = newpad(1, w - HBORDERS - TEXTHMARGINS); if (*textpad == NULL) { delwin(*widget); if (conf->shadow) delwin(*shadow); RETURN_ERROR("Cannot build the pad window for text"); } wbkgd(*textpad, t.dialog.color); } error = draw_dialog(conf, *shadow, *widget, textpad == NULL ? NULL : *textpad, text, bs, shortcutbuttons); return (error); } void end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, WINDOW *textpad) { int y, x, h, w; getbegyx(widget, y, x); getmaxyx(widget, h, w); if (conf->sleep > 0) sleep(conf->sleep); if (textpad != NULL) delwin(textpad); delwin(widget); if (conf->shadow) delwin(shadow); if (conf->clear) hide_widget(y, x, h, w, conf->shadow); if (conf->get_height != NULL) *conf->get_height = h; if (conf->get_width != NULL) *conf->get_width = w; }