2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021-2023 Alfonso Sabato Siciliano
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
34 #include "bsddialog.h"
35 #include "bsddialog_theme.h"
39 * -1- Error and diagnostic
43 * set_fmt_error_string();
45 * ----------------------------------------------------
46 * -2- (Unicode) Multicolumn character strings
53 * ----------------------------------------------------
56 * [static] buttons_min_width();
57 * [static] draw_button();
59 * set_buttons(); (to call 1 time after prepare_dialog()).
62 * ----------------------------------------------------
63 * -4- (Auto) Sizing and (Auto) Position
65 * [static] widget_max_height(conf);
66 * [static] widget_max_width(struct bsddialog_conf *conf)
67 * [static] is_wtext_attr();
68 * [static] text_properties();
69 * [static] text_autosize();
70 * [static] text_size();
71 * [static] widget_min_height(conf, htext, hnotext, bool buttons);
72 * [static] widget_min_width(conf, wtext, minw, buttons);
74 * set_widget_autosize(); (not for all dialogs).
75 * widget_checksize(); (not for all dialogs).
76 * set_widget_position();
77 * dialog_size_position(struct dialog); (not for all dialogs).
79 * ----------------------------------------------------
80 * -5- (Dialog) Widget components and utils
82 * hide_dialog(struct dialog);
83 * f1help_dialog(conf);
84 * draw_borders(conf, win, elev);
85 * update_box(conf, win, y, x, h, w, elev);
86 * rtextpad(); (helper for pnoutrefresh(textpad)).
88 * ----------------------------------------------------
89 * -6- Dialog init/build, update/draw, destroy
91 * end_dialog(struct dialog);
92 * [static] check_set_wtext_attr();
93 * [static] print_string(); (word wrapping).
94 * [static] print_textpad();
95 * draw_dialog(struct dialog);
96 * prepare_dialog(struct dialog);
100 * -1- Error and diagnostic
102 #define ERRBUFLEN 1024
104 static char errorbuffer[ERRBUFLEN];
106 const char *get_error_string(void)
108 return (errorbuffer);
111 void set_error_string(const char *str)
113 strncpy(errorbuffer, str, ERRBUFLEN-1);
116 void set_fmt_error_string(const char *fmt, ...)
120 va_start(arg_ptr, fmt);
121 vsnprintf(errorbuffer, ERRBUFLEN-1, fmt, arg_ptr);
126 * -2- (Unicode) Multicolumn character strings
128 wchar_t* alloc_mbstows(const char *mbstring)
130 size_t charlen, nchar;
132 const char *pmbstring;
136 pmbstring = mbstring;
137 memset(&mbs, 0, sizeof(mbs));
138 while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 &&
139 charlen != (size_t)-1 && charlen != (size_t)-2) {
140 pmbstring += charlen;
144 if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL)
146 mbstowcs(wstring, mbstring, nchar);
151 void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch)
157 mvwaddwstr(w, y, x, ws);
160 int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col)
165 size_t charlen, mb_cur_max;
170 mb_cur_max = MB_CUR_MAX;
172 memset(&mbs, 0, sizeof(mbs));
173 while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 &&
174 charlen != (size_t)-1 && charlen != (size_t)-2) {
175 if (mbtowc(&wch, mbstring, mb_cur_max) < 0)
177 w = (wch == L'\t') ? TABSIZE : wcwidth(wch);
178 ncol += (w < 0) ? 0 : w;
179 if (w > 1 && wch != L'\t')
186 if (has_multi_col != NULL)
187 *has_multi_col = multicol;
192 unsigned int strcols(const char *mbstring)
196 size_t charlen, mb_cur_max;
200 mb_cur_max = MB_CUR_MAX;
202 memset(&mbs, 0, sizeof(mbs));
203 while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 &&
204 charlen != (size_t)-1 && charlen != (size_t)-2) {
205 if (mbtowc(&wch, mbstring, mb_cur_max) < 0)
207 w = (wch == L'\t') ? TABSIZE : wcwidth(wch);
208 ncol += (w < 0) ? 0 : w;
218 static int buttons_min_width(struct buttons *bs)
222 width = bs->nbuttons * bs->sizebutton;
223 if (bs->nbuttons > 0)
224 width += (bs->nbuttons - 1) * t.button.minmargin;
230 draw_button(WINDOW *window, int y, int x, int size, const char *text,
231 wchar_t first, bool selected, bool shortcut)
233 int i, color_arrows, color_shortkey, color_button;
236 color_arrows = t.button.f_delimcolor;
237 color_shortkey = t.button.f_shortcutcolor;
238 color_button = t.button.f_color;
240 color_arrows = t.button.delimcolor;
241 color_shortkey = t.button.shortcutcolor;
242 color_button = t.button.color;
245 wattron(window, color_arrows);
246 mvwaddch(window, y, x, t.button.leftdelim);
247 wattroff(window, color_arrows);
248 wattron(window, color_button);
249 for (i = 1; i < size - 1; i++)
251 wattroff(window, color_button);
252 wattron(window, color_arrows);
253 mvwaddch(window, y, x + i, t.button.rightdelim);
254 wattroff(window, color_arrows);
256 x = x + 1 + ((size - 2 - strcols(text))/2);
257 wattron(window, color_button);
258 mvwaddstr(window, y, x, text);
259 wattroff(window, color_button);
262 wattron(window, color_shortkey);
263 mvwaddwch(window, y, x, first);
264 wattroff(window, color_shortkey);
268 void draw_buttons(struct dialog *d)
271 unsigned int newmargin, margin, wbuttons;
275 newmargin = d->w - BORDERS - (d->bs.nbuttons * d->bs.sizebutton);
276 newmargin /= (d->bs.nbuttons + 1);
277 newmargin = MIN(newmargin, t.button.maxmargin);
278 if (newmargin == 0) {
279 margin = t.button.minmargin;
280 wbuttons = buttons_min_width(&d->bs);
283 wbuttons = d->bs.nbuttons * d->bs.sizebutton;
284 wbuttons += (d->bs.nbuttons + 1) * margin;
287 startx = d->w/2 - wbuttons/2 + newmargin;
288 for (i = 0; i < (int)d->bs.nbuttons; i++) {
289 x = i * (d->bs.sizebutton + margin);
290 draw_button(d->widget, y, startx + x, d->bs.sizebutton,
291 d->bs.label[i], d->bs.first[i], i == d->bs.curr,
297 set_buttons(struct dialog *d, bool shortcut, const char *oklabel,
298 const char *cancellabel)
302 #define DEFAULT_BUTTON_LABEL OK_LABEL
303 #define DEFAULT_BUTTON_VALUE BSDDIALOG_OK
308 d->bs.sizebutton = 0;
309 d->bs.shortcut = shortcut;
311 if (d->conf->button.left1_label != NULL) {
312 d->bs.label[d->bs.nbuttons] = d->conf->button.left1_label;
313 d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT1;
317 if (d->conf->button.left2_label != NULL) {
318 d->bs.label[d->bs.nbuttons] = d->conf->button.left2_label;
319 d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT2;
323 if (d->conf->button.left3_label != NULL) {
324 d->bs.label[d->bs.nbuttons] = d->conf->button.left3_label;
325 d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT3;
329 if (oklabel != NULL && d->conf->button.without_ok == false) {
330 d->bs.label[d->bs.nbuttons] = d->conf->button.ok_label != NULL ?
331 d->conf->button.ok_label : oklabel;
332 d->bs.value[d->bs.nbuttons] = BSDDIALOG_OK;
336 if (d->conf->button.with_extra) {
337 d->bs.label[d->bs.nbuttons] = d->conf->button.extra_label != NULL ?
338 d->conf->button.extra_label : "Extra";
339 d->bs.value[d->bs.nbuttons] = BSDDIALOG_EXTRA;
343 if (cancellabel != NULL && d->conf->button.without_cancel == false) {
344 d->bs.label[d->bs.nbuttons] = d->conf->button.cancel_label ?
345 d->conf->button.cancel_label : cancellabel;
346 d->bs.value[d->bs.nbuttons] = BSDDIALOG_CANCEL;
347 if (d->conf->button.default_cancel)
348 d->bs.curr = d->bs.nbuttons;
352 if (d->conf->button.with_help) {
353 d->bs.label[d->bs.nbuttons] = d->conf->button.help_label != NULL ?
354 d->conf->button.help_label : "Help";
355 d->bs.value[d->bs.nbuttons] = BSDDIALOG_HELP;
359 if (d->conf->button.right1_label != NULL) {
360 d->bs.label[d->bs.nbuttons] = d->conf->button.right1_label;
361 d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT1;
365 if (d->conf->button.right2_label != NULL) {
366 d->bs.label[d->bs.nbuttons] = d->conf->button.right2_label;
367 d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT2;
371 if (d->conf->button.right3_label != NULL) {
372 d->bs.label[d->bs.nbuttons] = d->conf->button.right3_label;
373 d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT3;
377 if (d->bs.nbuttons == 0) {
378 d->bs.label[0] = DEFAULT_BUTTON_LABEL;
379 d->bs.value[0] = DEFAULT_BUTTON_VALUE;
383 for (i = 0; i < (int)d->bs.nbuttons; i++) {
384 mbtowc(&first, d->bs.label[i], MB_CUR_MAX);
385 d->bs.first[i] = first;
388 if (d->conf->button.default_label != NULL) {
389 for (i = 0; i < (int)d->bs.nbuttons; i++) {
390 if (strcmp(d->conf->button.default_label,
391 d->bs.label[i]) == 0)
396 d->bs.sizebutton = MAX(SIZEBUTTON - 2, strcols(d->bs.label[0]));
397 for (i = 1; i < (int)d->bs.nbuttons; i++)
398 d->bs.sizebutton = MAX(d->bs.sizebutton, strcols(d->bs.label[i]));
399 d->bs.sizebutton += 2;
402 bool shortcut_buttons(wint_t key, struct buttons *bs)
408 for (i = 0; i < bs->nbuttons; i++) {
409 if (towlower(key) == towlower(bs->first[i])) {
420 * -4- (Auto) Sizing and (Auto) Position
422 static int widget_max_height(struct bsddialog_conf *conf)
426 maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES;
428 RETURN_ERROR("Terminal too small, screen lines - shadow <= 0");
430 if (conf->y != BSDDIALOG_CENTER && conf->auto_topmargin > 0)
431 RETURN_ERROR("conf.y > 0 and conf->auto_topmargin > 0");
432 else if (conf->y == BSDDIALOG_CENTER) {
433 maxheight -= conf->auto_topmargin;
435 RETURN_ERROR("Terminal too small, screen lines - top "
437 } else if (conf->y > 0) {
438 maxheight -= conf->y;
440 RETURN_ERROR("Terminal too small, screen lines - "
444 maxheight -= conf->auto_downmargin;
446 RETURN_ERROR("Terminal too small, screen lines - Down margins "
452 static int widget_max_width(struct bsddialog_conf *conf)
456 maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS;
458 RETURN_ERROR("Terminal too small, screen cols - shadow <= 0");
463 RETURN_ERROR("Terminal too small, screen cols - shadow "
470 static bool is_wtext_attr(const wchar_t *wtext)
474 if (wcsnlen(wtext, 3) < 3)
476 if (wtext[0] != L'\\' || wtext[1] != L'Z')
479 att = wcschr(L"nbBdDkKrRsSuU01234567", wtext[2]) == NULL ? false : true;
488 struct textproperties {
498 text_properties(struct bsddialog_conf *conf, const char *text,
499 struct textproperties *tp)
501 int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols;
504 tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
507 if ((tp->words = calloc(maxwords, sizeof(int))) == NULL)
508 RETURN_ERROR("Cannot alloc memory for text autosize");
510 if ((wtext = alloc_mbstows(text)) == NULL)
511 RETURN_ERROR("Cannot allocate/autosize text in wchar_t*");
512 wtextlen = wcslen(wtext);
513 if ((tp->wletters = calloc(wtextlen, sizeof(uint8_t))) == NULL)
514 RETURN_ERROR("Cannot allocate wletters for text autosizing");
519 tp->hasnewline = false;
523 for (i = 0; i < wtextlen; i++) {
524 if (conf->text.escape && is_wtext_attr(wtext + i)) {
525 i += 2; /* +1 for update statement */
529 if (tp->nword + 1 >= maxwords) {
531 tp->words = realloc(tp->words, maxwords * sizeof(int));
532 if (tp->words == NULL)
533 RETURN_ERROR("Cannot realloc memory for text "
537 if (wcschr(L"\t\n ", wtext[i]) != NULL) {
538 tp->maxwordcols = MAX(wordcols, tp->maxwordcols);
542 currlinecols += wordcols;
544 tp->words[tp->nword] = wordcols;
552 currlinecols += tablen;
554 tp->words[tp->nword] = TB;
558 tp->hasnewline = true;
559 tp->maxline = MAX(tp->maxline, currlinecols);
562 tp->words[tp->nword] = NL;
568 tp->words[tp->nword] = WS;
573 tp->wletters[l] = wcwidth(wtext[i]);
574 wordcols += tp->wletters[l];
580 tp->words[tp->nword] = wordcols;
582 tp->maxwordcols = MAX(wordcols, tp->maxwordcols);
585 tp->maxline = MAX(tp->maxline, currlinecols);
593 text_autosize(struct bsddialog_conf *conf, struct textproperties *tp,
594 int maxrows, int mincols, bool increasecols, int *h, int *w)
596 int i, j, x, y, z, l, line, maxwidth, tablen;
598 maxwidth = widget_max_width(conf) - BORDERS - TEXTHMARGINS;
599 tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
602 mincols = MAX(mincols, tp->maxwordcols);
603 mincols = MAX(mincols,
604 (int)conf->auto_minwidth - BORDERS - TEXTHMARGINS);
605 mincols = MIN(mincols, maxwidth);
613 for (i = 0; i < tp->nword; i++) {
614 switch (tp->words[i]) {
616 for (j = 0; j < tablen; j++) {
636 if (tp->words[i] + x <= mincols) {
638 for (z = 0 ; z != tp->words[i]; l++ )
639 z += tp->wletters[l];
640 } else if (tp->words[i] <= mincols) {
643 for (z = 0 ; z != tp->words[i]; l++ )
644 z += tp->wletters[l];
646 for (j = tp->words[i]; j > 0; ) {
647 y = (x == 0) ? y : y + 1;
649 while (z != j && z < mincols) {
650 z += tp->wletters[l];
662 if (increasecols == false)
664 if (mincols >= maxwidth)
666 if (line >= y * (int)conf->text.cols_per_row && y <= maxrows)
671 *h = (tp->nword == 0) ? 0 : y;
672 *w = MIN(mincols, line); /* wtext can be less than mincols */
678 text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text,
679 struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext)
682 int wbuttons, maxhtext;
683 struct textproperties tp;
686 if (bs->nbuttons > 0)
687 wbuttons = buttons_min_width(bs);
690 if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) {
691 maxhtext = widget_max_height(conf) - BORDERS - rowsnotext;
693 maxhtext = rows - BORDERS - rowsnotext;
695 if (bs->nbuttons > 0)
698 maxhtext = 1; /* text_autosize() computes always htext */
701 if (cols == BSDDIALOG_AUTOSIZE) {
702 startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS);
704 } else if (cols == BSDDIALOG_FULLSCREEN) {
705 startwtext = widget_max_width(conf) - BORDERS - TEXTHMARGINS;
708 startwtext = cols - BORDERS - TEXTHMARGINS;
712 if (startwtext <= 0 && changewtext)
715 /* Sizing calculation */
716 if (text_properties(conf, text, &tp) != 0)
717 return (BSDDIALOG_ERROR);
718 if (tp.nword > 0 && startwtext <= 0)
719 RETURN_FMTERROR("(fixed cols or fullscreen) "
720 "needed at least %d cols to draw text",
721 BORDERS + TEXTHMARGINS + 1);
722 if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext,
724 return (BSDDIALOG_ERROR);
733 widget_min_height(struct bsddialog_conf *conf, int htext, int hnotext,
744 /* specific widget lines without text */
749 min += HBUTTONS; /* buttons and their up-border */
751 /* conf.auto_minheight */
752 min = MAX(min, (int)conf->auto_minheight);
758 widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget,
762 int min, delimtitle, wbottomtitle, wtitle;
767 if (bs->nbuttons > 0)
768 min += buttons_min_width(bs);
772 min = MAX(min, wtext + TEXTHMARGINS);
774 /* specific widget min width */
775 min = MAX(min, minwidget);
778 if (conf->title != NULL) {
779 delimtitle = t.dialog.delimtitle ? 2 : 0;
780 wtitle = strcols(conf->title);
781 min = MAX(min, wtitle + 2 + delimtitle);
785 if (conf->bottomtitle != NULL) {
786 wbottomtitle = strcols(conf->bottomtitle);
787 min = MAX(min, wbottomtitle + 4);
792 /* conf.auto_minwidth */
793 min = MAX(min, (int)conf->auto_minwidth);
799 set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w)
801 int maxheight, maxwidth;
803 if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR)
804 return (BSDDIALOG_ERROR);
806 if (rows == BSDDIALOG_FULLSCREEN)
808 else if (rows < BSDDIALOG_FULLSCREEN)
809 RETURN_ERROR("Negative (less than -1) height");
810 else if (rows > BSDDIALOG_AUTOSIZE) /* fixed rows */
811 *h = MIN(rows, maxheight); /* rows is at most maxheight */
812 /* rows == AUTOSIZE: each widget has to set its size */
814 if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR)
815 return (BSDDIALOG_ERROR);
817 if (cols == BSDDIALOG_FULLSCREEN)
819 else if (cols < BSDDIALOG_FULLSCREEN)
820 RETURN_ERROR("Negative (less than -1) width");
821 else if (cols > BSDDIALOG_AUTOSIZE) /* fixed cols */
822 *w = MIN(cols, maxwidth); /* cols is at most maxwidth */
823 /* cols == AUTOSIZE: each widget has to set its size */
829 set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
830 int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext,
835 if (rows == BSDDIALOG_AUTOSIZE || cols == BSDDIALOG_AUTOSIZE ||
837 if (text_size(conf, rows, cols, text, bs, hnotext, minw,
838 &htext, &wtext) != 0)
839 return (BSDDIALOG_ERROR);
840 if (rowstext != NULL)
844 if (rows == BSDDIALOG_AUTOSIZE) {
845 *h = widget_min_height(conf, htext, hnotext, bs->nbuttons > 0);
846 *h = MIN(*h, widget_max_height(conf));
849 if (cols == BSDDIALOG_AUTOSIZE) {
850 *w = widget_min_width(conf, wtext, minw, bs);
851 *w = MIN(*w, widget_max_width(conf));
857 int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw)
859 int minheight, minwidth;
861 minheight = BORDERS + hnotext;
862 if (bs->nbuttons > 0)
863 minheight += HBUTTONS;
865 RETURN_FMTERROR("Current rows: %d, needed at least: %d",
869 if (bs->nbuttons > 0)
870 minwidth = buttons_min_width(bs);
871 minwidth = MAX(minwidth, minw);
874 RETURN_FMTERROR("Current cols: %d, nedeed at least %d",
881 set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w)
883 int hshadow = conf->shadow ? (int)t.shadow.y : 0;
884 int wshadow = conf->shadow ? (int)t.shadow.x : 0;
886 if (conf->y == BSDDIALOG_CENTER) {
887 *y = SCREENLINES/2 - (h + hshadow)/2;
888 if (*y < (int)conf->auto_topmargin)
889 *y = conf->auto_topmargin;
890 if (*y + h + hshadow > SCREENLINES - (int)conf->auto_downmargin)
891 *y = SCREENLINES - h - hshadow - conf->auto_downmargin;
893 else if (conf->y < BSDDIALOG_CENTER)
894 RETURN_ERROR("Negative begin y (less than -1)");
895 else if (conf->y >= SCREENLINES)
896 RETURN_ERROR("Begin Y under the terminal");
900 if (*y + h + hshadow > SCREENLINES)
901 RETURN_ERROR("The lower of the box under the terminal "
902 "(begin Y + height (+ shadow) > terminal lines)");
905 if (conf->x == BSDDIALOG_CENTER)
906 *x = SCREENCOLS/2 - (w + wshadow)/2;
907 else if (conf->x < BSDDIALOG_CENTER)
908 RETURN_ERROR("Negative begin x (less than -1)");
909 else if (conf->x >= SCREENCOLS)
910 RETURN_ERROR("Begin X over the right of the terminal");
914 if ((*x + w + wshadow) > SCREENCOLS)
915 RETURN_ERROR("The right of the box over the terminal "
916 "(begin X + width (+ shadow) > terminal cols)");
921 int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext)
923 if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
924 return (BSDDIALOG_ERROR);
925 if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
926 d->text, htext, &d->bs, hnotext, minw) != 0)
927 return (BSDDIALOG_ERROR);
928 if (widget_checksize(d->h, d->w, &d->bs, hnotext, minw) != 0)
929 return (BSDDIALOG_ERROR);
930 if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
931 return (BSDDIALOG_ERROR);
937 * -5- Widget components and utilities
939 int hide_dialog(struct dialog *d)
943 if ((clear = newwin(d->h, d->w, d->y, d->x)) == NULL)
944 RETURN_ERROR("Cannot hide the widget");
945 wbkgd(clear, t.screen.color);
948 if (d->conf->shadow) {
949 mvwin(clear, d->y + t.shadow.y, d->x + t.shadow.x);
958 int f1help_dialog(struct bsddialog_conf *conf)
961 struct bsddialog_conf hconf;
963 bsddialog_initconf(&hconf);
964 hconf.title = "HELP";
965 hconf.button.ok_label = "EXIT";
967 hconf.ascii_lines = conf->ascii_lines;
968 hconf.no_lines = conf->no_lines;
969 hconf.shadow = conf->shadow;
970 hconf.text.escape = conf->text.escape;
972 output = BSDDIALOG_OK;
973 if (conf->key.f1_message != NULL)
974 output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0);
976 if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL)
977 output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0);
979 return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0);
982 void draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev)
985 int leftcolor, rightcolor;
986 int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee;
991 if (conf->ascii_lines) {
994 tl = tr = bl = br = ltee = rtee = '+';
1006 getmaxyx(win, h, w);
1007 leftcolor = elev == RAISED ?
1008 t.dialog.lineraisecolor : t.dialog.linelowercolor;
1009 rightcolor = elev == RAISED ?
1010 t.dialog.linelowercolor : t.dialog.lineraisecolor;
1012 wattron(win, leftcolor);
1013 wborder(win, ls, rs, ts, bs, tl, tr, bl, br);
1014 wattroff(win, leftcolor);
1016 wattron(win, rightcolor);
1017 mvwaddch(win, 0, w-1, tr);
1018 mvwvline(win, 1, w-1, rs, h-2);
1019 mvwaddch(win, h-1, w-1, br);
1020 mvwhline(win, h-1, 1, bs, w-2);
1021 wattroff(win, rightcolor);
1025 update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w,
1026 enum elevation elev)
1031 draw_borders(conf, win, elev);
1035 rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext)
1037 pnoutrefresh(d->textpad, ytext, xtext,
1038 d->y + BORDER + upnotext,
1039 d->x + BORDER + TEXTHMARGIN,
1040 d->y + d->h - 1 - downnotext - BORDER,
1041 d->x + d->w - TEXTHMARGIN - BORDER);
1045 * -6- Dialog init/build, update/draw, destroy
1047 void end_dialog(struct dialog *d)
1049 if (d->conf->sleep > 0)
1050 sleep(d->conf->sleep);
1054 if (d->conf->shadow)
1060 if (d->conf->get_height != NULL)
1061 *d->conf->get_height = d->h;
1062 if (d->conf->get_width != NULL)
1063 *d->conf->get_width = d->w;
1066 static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext)
1068 enum bsddialog_color bg;
1070 if (is_wtext_attr(wtext) == false)
1073 if ((wtext[2] >= L'0') && (wtext[2] <= L'7')) {
1074 bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL);
1075 wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0));
1081 wattron(win, t.dialog.color);
1082 wattrset(win, A_NORMAL);
1085 wattron(win, A_BOLD);
1088 wattroff(win, A_BOLD);
1091 wattron(win, A_DIM);
1094 wattroff(win, A_DIM);
1097 wattron(win, A_BLINK);
1100 wattroff(win, A_BLINK);
1103 wattron(win, A_REVERSE);
1106 wattroff(win, A_REVERSE);
1109 wattron(win, A_STANDOUT);
1112 wattroff(win, A_STANDOUT);
1115 wattron(win, A_UNDERLINE);
1118 wattroff(win, A_UNDERLINE);
1126 print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str,
1129 int i, j, len, reallen, wc;
1139 if (is_wtext_attr(str+i) == false) {
1140 reallen += wcwidth(str[i]);
1147 reallen = wcswidth(str, len);
1151 if (*x + reallen > cols) {
1152 *y = (*x != 0 ? *y+1 : *y);
1155 wresize(win, *rows, cols);
1160 while (j < cols && i < len) {
1161 if (color && check_set_wtext_attr(win, str+i)) {
1163 } else if (j + wcwidth(str[i]) > cols) {
1166 /* inline mvwaddwch() for efficiency */
1168 mvwaddwstr(win, *y, j, ws);
1169 wc = wcwidth(str[i]);;
1180 print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text)
1183 int i, j, z, rows, cols, x, y, tablen;
1184 wchar_t *wtext, *string;
1186 if ((wtext = alloc_mbstows(text)) == NULL)
1187 RETURN_ERROR("Cannot allocate/print text in wchar_t*");
1189 if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL)
1190 RETURN_ERROR("Cannot build (analyze) text");
1192 getmaxyx(pad, rows, cols);
1193 tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
1198 string[j] = wtext[i];
1200 if (wcschr(L"\n\t ", string[j]) != NULL || string[j] == L'\0') {
1202 print_string(pad, &rows, cols, &y, &x, string,
1216 for (z = 0; z < tablen; z++) {
1236 wresize(pad, rows, cols);
1249 int draw_dialog(struct dialog *d)
1251 int wtitle, wbottomtitle, ts, ltee, rtee;
1253 ts = d->conf->ascii_lines ? '-' : ACS_HLINE;
1254 ltee = d->conf->ascii_lines ? '+' : ACS_LTEE;
1255 rtee = d->conf->ascii_lines ? '+' : ACS_RTEE;
1257 if (d->conf->shadow) {
1259 wresize(d->shadow, d->h, d->w);
1260 mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x);
1261 wnoutrefresh(d->shadow);
1265 wresize(d->widget, d->h, d->w);
1266 mvwin(d->widget, d->y, d->x);
1267 draw_borders(d->conf, d->widget, RAISED);
1269 if (d->conf->title != NULL) {
1270 if ((wtitle = strcols(d->conf->title)) < 0)
1271 return (BSDDIALOG_ERROR);
1272 if (t.dialog.delimtitle && d->conf->no_lines == false) {
1273 wattron(d->widget, t.dialog.lineraisecolor);
1274 mvwaddch(d->widget, 0, d->w/2 - wtitle/2 -1, rtee);
1275 wattroff(d->widget, t.dialog.lineraisecolor);
1277 wattron(d->widget, t.dialog.titlecolor);
1278 mvwaddstr(d->widget, 0, d->w/2 - wtitle/2, d->conf->title);
1279 wattroff(d->widget, t.dialog.titlecolor);
1280 if (t.dialog.delimtitle && d->conf->no_lines == false) {
1281 wattron(d->widget, t.dialog.lineraisecolor);
1282 waddch(d->widget, ltee);
1283 wattroff(d->widget, t.dialog.lineraisecolor);
1287 if (d->bs.nbuttons > 0) {
1288 if (d->conf->no_lines == false) {
1289 wattron(d->widget, t.dialog.lineraisecolor);
1290 mvwaddch(d->widget, d->h-3, 0, ltee);
1291 mvwhline(d->widget, d->h-3, 1, ts, d->w-2);
1292 wattroff(d->widget, t.dialog.lineraisecolor);
1294 wattron(d->widget, t.dialog.linelowercolor);
1295 mvwaddch(d->widget, d->h-3, d->w-1, rtee);
1296 wattroff(d->widget, t.dialog.linelowercolor);
1301 if (d->conf->bottomtitle != NULL) {
1302 if ((wbottomtitle = strcols(d->conf->bottomtitle)) < 0)
1303 return (BSDDIALOG_ERROR);
1304 wattron(d->widget, t.dialog.bottomtitlecolor);
1305 wmove(d->widget, d->h - 1, d->w/2 - wbottomtitle/2 - 1);
1306 waddch(d->widget, ' ');
1307 waddstr(d->widget, d->conf->bottomtitle);
1308 waddch(d->widget, ' ');
1309 wattroff(d->widget, t.dialog.bottomtitlecolor);
1312 wnoutrefresh(d->widget);
1315 /* `infobox "" 0 2` fails but text is empty and textpad remains 1 1 */
1316 wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS);
1318 if (print_textpad(d->conf, d->textpad, d->text) != 0)
1319 return (BSDDIALOG_ERROR);
1327 prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows,
1328 int cols, struct dialog *d)
1336 d->text = CHECK_STR(text);
1339 if (d->conf->shadow) {
1340 if ((d->shadow = newwin(1, 1, 1, 1)) == NULL)
1341 RETURN_ERROR("Cannot build WINDOW shadow");
1342 wbkgd(d->shadow, t.shadow.color);
1345 if ((d->widget = newwin(1, 1, 1, 1)) == NULL)
1346 RETURN_ERROR("Cannot build WINDOW widget");
1347 wbkgd(d->widget, t.dialog.color);
1349 /* fake for textpad */
1350 if ((d->textpad = newpad(1, 1)) == NULL)
1351 RETURN_ERROR("Cannot build the pad WINDOW for text");
1352 wbkgd(d->textpad, t.dialog.color);