diff options
Diffstat (limited to 'contrib/bsddialog/lib/lib_util.c')
-rw-r--r-- | contrib/bsddialog/lib/lib_util.c | 1763 |
1 files changed, 1061 insertions, 702 deletions
diff --git a/contrib/bsddialog/lib/lib_util.c b/contrib/bsddialog/lib/lib_util.c index a1cdac1169c4..9cfdd6f1a075 100644 --- a/contrib/bsddialog/lib/lib_util.c +++ b/contrib/bsddialog/lib/lib_util.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,972 +25,1331 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - +#include <curses.h> #include <stdlib.h> #include <string.h> #include <unistd.h> - -#ifdef PORTNCURSES -#include <ncurses/curses.h> -#else -#include <curses.h> -#endif +#include <wctype.h> #include "bsddialog.h" -#include "lib_util.h" #include "bsddialog_theme.h" +#include "lib_util.h" -extern struct bsddialog_theme t; +/* + * -1- Error and diagnostic + * + * get_error_string(); + * set_error_string(); + * set_fmt_error_string(); + * + * ---------------------------------------------------- + * -2- (Unicode) Multicolumn character strings + * + * alloc_mbstows(); + * mvwaddwch(); + * str_props(); + * strcols(); + * + * ---------------------------------------------------- + * -3- Buttons + * + * [static] buttons_min_width(); + * [static] draw_button(); + * draw_buttons(); + * set_buttons(); (to call 1 time after prepare_dialog()). + * shortcut_buttons(); + * + * ---------------------------------------------------- + * -4- (Auto) Sizing and (Auto) Position + * + * [static] widget_max_height(conf); + * [static] widget_max_width(struct bsddialog_conf *conf) + * [static] is_wtext_attr(); + * [static] text_properties(); + * [static] text_autosize(); + * [static] text_size(); + * [static] widget_min_height(conf, htext, hnotext, bool buttons); + * [static] widget_min_width(conf, wtext, minw, buttons); + * set_widget_size(); + * set_widget_autosize(); (not for all dialogs). + * widget_checksize(); (not for all dialogs). + * set_widget_position(); + * dialog_size_position(struct dialog); (not for all dialogs). + * + * ---------------------------------------------------- + * -5- (Dialog) Widget components and utils + * + * hide_dialog(struct dialog); + * f1help_dialog(conf); + * draw_borders(conf, win, elev); + * update_box(conf, win, y, x, h, w, elev); + * rtextpad(); (helper for pnoutrefresh(textpad)). + * + * ---------------------------------------------------- + * -6- Dialog init/build, update/draw, destroy + * + * end_dialog(struct dialog); + * [static] check_set_wtext_attr(); + * [static] print_string(); (word wrapping). + * [static] print_textpad(); + * draw_dialog(struct dialog); + * prepare_dialog(struct dialog); + */ -/* Error buffer */ +/* + * -1- Error and diagnostic + */ +#define ERRBUFLEN 1024 -#define ERRBUFLEN 1024 static char errorbuffer[ERRBUFLEN]; const char *get_error_string(void) { - return errorbuffer; + return (errorbuffer); } -void set_error_string(char *str) +void set_error_string(const char *str) { - strncpy(errorbuffer, str, ERRBUFLEN-1); } -/* cleaner */ -int hide_widget(int y, int x, int h, int w, bool withshadow) +void set_fmt_error_string(const char *fmt, ...) { - WINDOW *clear; + va_list arg_ptr; - /* no check: y, x, h and w are checked by the builders */ - if ((clear = newwin(h, w, y + t.shadowrows, x + t.shadowcols)) == NULL) - RETURN_ERROR("Cannot hide the widget"); - wbkgd(clear, t.backgroundcolor); + va_start(arg_ptr, fmt); + vsnprintf(errorbuffer, ERRBUFLEN-1, fmt, arg_ptr); + va_end(arg_ptr); +} - if (withshadow) - wrefresh(clear); +/* + * -2- (Unicode) Multicolumn character strings + */ +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++; + } - mvwin(clear, y, x); - wrefresh(clear); + if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL) + return (NULL); + mbstowcs(wstring, mbstring, nchar); - delwin(clear); + return (wstring); +} - return 0; +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); } -/* F1 help */ -int f1help(struct bsddialog_conf conf) +int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col) { - char *file = conf.hfile; - char *title = conf.title; - int output; + 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); +} - conf.hfile = NULL; - conf.clear = true; - conf.y = BSDDIALOG_CENTER; - conf.x = BSDDIALOG_CENTER; - conf.title = "HELP"; - conf.sleep = 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; + } - output = bsddialog_textbox(conf, file, BSDDIALOG_AUTOSIZE, - BSDDIALOG_AUTOSIZE); - conf.hfile = file; - conf.title = title; + return (ncol); +} - return output; +/* + * -3- Buttons + */ +static 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); } -/* Buttons */ -void -draw_button(WINDOW *window, int y, int x, int size, char *text, bool selected, - bool shortkey) +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.currbuttdelimcolor; - color_shortkey = t.currshortkeycolor; - color_button = t.currbuttoncolor; + color_arrows = t.button.f_delimcolor; + color_shortkey = t.button.f_shortcutcolor; + color_button = t.button.f_color; } else { - color_arrows = t.buttdelimcolor; - color_shortkey = t.shortkeycolor; - color_button = t.buttoncolor; + color_arrows = t.button.delimcolor; + color_shortkey = t.button.shortcutcolor; + color_button = t.button.color; } wattron(window, color_arrows); - mvwaddch(window, y, x, t.buttleftch); + mvwaddch(window, y, x, t.button.leftdelim); wattroff(window, color_arrows); wattron(window, color_button); - for(i = 1; i < size - 1; i++) + for (i = 1; i < size - 1; i++) waddch(window, ' '); wattroff(window, color_button); wattron(window, color_arrows); - mvwaddch(window, y, x + i, t.buttrightchar); + mvwaddch(window, y, x + i, t.button.rightdelim); wattroff(window, color_arrows); - x = x + 1 + ((size - 2 - strlen(text))/2); + x = x + 1 + ((size - 2 - strcols(text))/2); wattron(window, color_button); mvwaddstr(window, y, x, text); wattroff(window, color_button); - if (shortkey) { + if (shortcut) { wattron(window, color_shortkey); - mvwaddch(window, y, x, text[0]); + mvwaddwch(window, y, x, first); wattroff(window, color_shortkey); } } -void -draw_buttons(WINDOW *window, int y, int cols, struct buttons bs, bool shortkey) +void draw_buttons(struct dialog *d) { - int i, x, start_x; + int i, x, startx, y; + unsigned int newmargin, margin, wbuttons; - start_x = bs.sizebutton * bs.nbuttons + (bs.nbuttons - 1) * t.buttonspace; - start_x = cols/2 - start_x/2; + y = d->h - 2; - for (i = 0; i < (int) bs.nbuttons; i++) { - x = i * (bs.sizebutton + t.buttonspace); - draw_button(window, y, start_x + x, bs.sizebutton, bs.label[i], - i == bs.curr, shortkey); + newmargin = d->w - BORDERS - (d->bs.nbuttons * d->bs.sizebutton); + newmargin /= (d->bs.nbuttons + 1); + newmargin = MIN(newmargin, t.button.maxmargin); + if (newmargin == 0) { + margin = t.button.minmargin; + wbuttons = buttons_min_width(&d->bs); + } else { + margin = newmargin; + wbuttons = d->bs.nbuttons * d->bs.sizebutton; + wbuttons += (d->bs.nbuttons + 1) * margin; + } + + startx = d->w/2 - wbuttons/2 + newmargin; + for (i = 0; i < (int)d->bs.nbuttons; i++) { + x = i * (d->bs.sizebutton + margin); + draw_button(d->widget, y, startx + x, d->bs.sizebutton, + d->bs.label[i], d->bs.first[i], i == d->bs.curr, + d->bs.shortcut); } } void -get_buttons(struct bsddialog_conf conf, struct buttons *bs, char *yesoklabel, - char *extralabel, char *nocancellabel, char *helplabel) +set_buttons(struct dialog *d, bool shortcut, const char *oklabel, + const char *cancellabel) { int i; -#define SIZEBUTTON 8 -#define DEFAULT_BUTTON_LABEL LABEL_ok_label -#define DEFAULT_BUTTON_VALUE BSDDIALOG_YESOK - - - bs->nbuttons = 0; - bs->curr = 0; - bs->sizebutton = 0; - - if (yesoklabel != NULL && conf.button.no_ok == false) { - bs->label[0] = yesoklabel; - bs->value[0] = BSDDIALOG_YESOK; - bs->nbuttons += 1; +#define SIZEBUTTON 8 +#define DEFAULT_BUTTON_LABEL OK_LABEL +#define DEFAULT_BUTTON_VALUE BSDDIALOG_OK + wchar_t first; + + d->bs.nbuttons = 0; + d->bs.curr = 0; + d->bs.sizebutton = 0; + d->bs.shortcut = shortcut; + + if (d->conf->button.left1_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left1_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT1; + d->bs.nbuttons += 1; } - if (extralabel != NULL && conf.button.extra_button) { - bs->label[bs->nbuttons] = extralabel; - bs->value[bs->nbuttons] = BSDDIALOG_EXTRA; - bs->nbuttons += 1; + if (d->conf->button.left2_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left2_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT2; + d->bs.nbuttons += 1; } - if (nocancellabel != NULL && conf.button.no_cancel == false) { - bs->label[bs->nbuttons] = nocancellabel; - bs->value[bs->nbuttons] = BSDDIALOG_NOCANCEL; - if (conf.button.defaultno) - bs->curr = bs->nbuttons; - bs->nbuttons += 1; + if (d->conf->button.left3_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left3_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT3; + d->bs.nbuttons += 1; } - if (helplabel != NULL && conf.button.help_button) { - bs->label[bs->nbuttons] = helplabel; - bs->value[bs->nbuttons] = BSDDIALOG_HELP; - bs->nbuttons += 1; + if (oklabel != NULL && d->conf->button.without_ok == false) { + d->bs.label[d->bs.nbuttons] = d->conf->button.ok_label != NULL ? + d->conf->button.ok_label : oklabel; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_OK; + d->bs.nbuttons += 1; } - if (bs->nbuttons == 0) { - bs->label[0] = DEFAULT_BUTTON_LABEL; - bs->value[0] = DEFAULT_BUTTON_VALUE; - bs->nbuttons = 1; + if (d->conf->button.with_extra) { + d->bs.label[d->bs.nbuttons] = d->conf->button.extra_label != NULL ? + d->conf->button.extra_label : "Extra"; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_EXTRA; + d->bs.nbuttons += 1; } - 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; - } + if (cancellabel != NULL && d->conf->button.without_cancel == false) { + d->bs.label[d->bs.nbuttons] = d->conf->button.cancel_label ? + d->conf->button.cancel_label : cancellabel; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_CANCEL; + if (d->conf->button.default_cancel) + d->bs.curr = d->bs.nbuttons; + d->bs.nbuttons += 1; } - bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0])); - for (i=1; i < (int) bs->nbuttons; i++) - bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i])); - bs->sizebutton += 2; -} - -/* Text */ - -// old text, to delete in the future -enum token { TEXT, WS, END }; + if (d->conf->button.with_help) { + d->bs.label[d->bs.nbuttons] = d->conf->button.help_label != NULL ? + d->conf->button.help_label : "Help"; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_HELP; + d->bs.nbuttons += 1; + } -static bool check_set_ncurses_attr(WINDOW *win, char *text) -{ - bool isattr; - int colors[8] = { - COLOR_BLACK, - COLOR_RED, - COLOR_GREEN, - COLOR_YELLOW, - COLOR_BLUE, - COLOR_MAGENTA, - COLOR_CYAN, - COLOR_WHITE - }; + if (d->conf->button.right1_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right1_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT1; + d->bs.nbuttons += 1; + } - if (text[0] == '\0' || text[0] != '\\') - return false; - if (text[1] == '\0' || text[1] != 'Z') - return false; - if (text[2] == '\0') - return false; + if (d->conf->button.right2_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right2_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT2; + d->bs.nbuttons += 1; + } - if ((text[2] - 48) >= 0 && (text[2] - 48) < 8) { - // tocheck: import BSD_COLOR - // tofix color background - wattron(win, COLOR_PAIR(colors[text[2] - 48] * 8 + COLOR_WHITE + 1)); - return true; + if (d->conf->button.right3_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right3_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT3; + d->bs.nbuttons += 1; } - isattr = true; - switch (text[2]) { - case 'n': - wattrset(win, A_NORMAL); - break; - case 'b': - wattron(win, A_BOLD); - break; - case 'B': - wattroff(win, A_BOLD); - break; - case 'r': - wattron(win, A_REVERSE); - break; - case 'R': - wattroff(win, A_REVERSE); - break; - case 'u': - wattron(win, A_UNDERLINE); - break; - case 'U': - wattroff(win, A_UNDERLINE); - break; - default: - isattr = false; + if (d->bs.nbuttons == 0) { + d->bs.label[0] = DEFAULT_BUTTON_LABEL; + d->bs.value[0] = DEFAULT_BUTTON_VALUE; + d->bs.nbuttons = 1; } - return isattr; -} + for (i = 0; i < (int)d->bs.nbuttons; i++) { + mbtowc(&first, d->bs.label[i], MB_CUR_MAX); + d->bs.first[i] = first; + } -static bool isws(int ch) -{ + if (d->conf->button.default_label != NULL) { + for (i = 0; i < (int)d->bs.nbuttons; i++) { + if (strcmp(d->conf->button.default_label, + d->bs.label[i]) == 0) + d->bs.curr = i; + } + } - return (ch == ' ' || ch == '\t' || ch == '\n'); + d->bs.sizebutton = MAX(SIZEBUTTON - 2, strcols(d->bs.label[0])); + for (i = 1; i < (int)d->bs.nbuttons; i++) + d->bs.sizebutton = MAX(d->bs.sizebutton, strcols(d->bs.label[i])); + d->bs.sizebutton += 2; } -static int -next_token(char *text, char *valuestr) +bool shortcut_buttons(wint_t key, struct buttons *bs) { - int i, j; - enum token tok; - - i = j = 0; - - if (text[0] == '\0') - return END; - - while (text[i] != '\0') { - if (isws(text[i])) { - if (i == 0) { - valuestr[0] = text[i]; - valuestr[1] = '\0'; - tok = WS; - } + 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; } - - valuestr[j] = text[i]; - j++; - valuestr[j] = '\0'; - i++; - tok = TEXT; } - return tok; + return (match); } -static void -print_string(WINDOW *win, int *y, int *x, int minx, int maxx, char *str, bool color) +/* + * -4- (Auto) Sizing and (Auto) Position + */ +static int widget_max_height(struct bsddialog_conf *conf) { - int i, j, len, reallen; - - if(strlen(str) == 0) - return; + int maxheight; - len = reallen = strlen(str); - if (color) { - i=0; - while (i < len) { - if (check_set_ncurses_attr(win, str+i)) - reallen -= 3; - i++; - } + 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"); } - i = 0; - while (i < len) { - if (*x + reallen > maxx) { - *y = (*x != minx ? *y+1 : *y); - *x = minx; - } - j = *x; - while (j < maxx && i < len) { - if (color && check_set_ncurses_attr(win, str+i)) { - i += 3; - } else { - mvwaddch(win, *y, j, str[i]); - i++; - reallen--; - j++; - *x = j; - } - } - } + maxheight -= conf->auto_downmargin; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - Down margins " + "<= 0"); + + return (maxheight); } -void -print_text(struct bsddialog_conf conf, WINDOW *pad, int starty, int minx, int maxx, - char *text) +static int widget_max_width(struct bsddialog_conf *conf) { - char *valuestr; - int x, y; - bool loop; - enum token tok; + int maxwidth; - valuestr = malloc(strlen(text) + 1); + maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); - x = minx; - y = starty; - loop = true; - while (loop) { - tok = next_token(text, valuestr); - switch (tok) { - case END: - loop = false; - break; - case WS: - text += strlen(valuestr); - print_string(pad, &y, &x, minx, maxx, valuestr, false /*useless*/); - break; - case TEXT: - text += strlen(valuestr); - print_string(pad, &y, &x, minx, maxx, valuestr, conf.text.colors); - break; - } + if (conf->x > 0) { + maxwidth -= conf->x; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow " + "- x <= 0"); } - free(valuestr); + return (maxwidth); } -// new text funcs - -static bool is_ncurses_attr(char *text) +static bool is_wtext_attr(const wchar_t *wtext) { - bool isattr; - - if (strnlen(text, 3) < 3) - return false; + bool att; - if (text[0] != '\\' || text[1] != 'Z') - return false; + if (wcsnlen(wtext, 3) < 3) + return (false); + if (wtext[0] != L'\\' || wtext[1] != L'Z') + return (false); - if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) - return true; + att = wcschr(L"nbBdDkKrRsSuU01234567", wtext[2]) == NULL ? false : true; - isattr = text[2] == 'n' || text[2] == 'b' || text[2] == 'B' || - text[2] == 'r' || text[2] == 'R' || text[2] == 'u' || - text[2] == 'U'; - - return isattr; + return (att); } -static void -print_str(WINDOW *win, int *rows, int *y, int *x, int cols, char *str, bool color) -{ - int i, j, len, reallen; +#define NL -1 +#define WS -2 +#define TB -3 - if(strlen(str) == 0) - return; +struct textproperties { + int nword; + int *words; + uint8_t *wletters; + int maxwordcols; + int maxline; + bool hasnewline; +}; - len = reallen = strlen(str); - if (color) { - i=0; - while (i < len) { - if (is_ncurses_attr(str+i)) - reallen -= 3; - i++; +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.escape && is_wtext_attr(wtext + i)) { + i += 2; /* +1 for update statement */ + continue; } - } - 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_ncurses_attr(win, str+i)) { - i += 3; - } else { - mvwaddch(win, *y, j, str[i]); - i++; - reallen--; - j++; - *x = j; - } + 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"); } - } -} -static void prepare_text(struct bsddialog_conf conf, char *text, char *buf) -{ - int i, j; + if (wcschr(L"\t\n ", wtext[i]) != NULL) { + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); - i = j = 0; - while (text[i] != '\0') { - switch (text[i]) { - case '\\': - buf[j] = '\\'; - switch (text[i+1]) { - case '\\': - i++; + 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 'n': - if (conf.text.no_nl_expand) { - j++; - buf[j] = 'n'; - } else - buf[j] = '\n'; - i++; + case L'\n': + /* line */ + tp->hasnewline = true; + tp->maxline = MAX(tp->maxline, currlinecols); + currlinecols = 0; + /* word */ + tp->words[tp->nword] = NL; break; - case 't': - if (conf.text.no_collapse) { - j++; - buf[j] = 't'; - } else - buf[j] = '\t'; - i++; + case L' ': + /* line */ + currlinecols += 1; + /* word */ + tp->words[tp->nword] = WS; break; } - break; - case '\n': - buf[j] = conf.text.cr_wrap ? ' ' : '\n'; - break; - case '\t': - buf[j] = conf.text.no_collapse ? '\t' : ' '; - break; - default: - buf[j] = text[i]; + tp->nword += 1; + } else { + tp->wletters[l] = wcwidth(wtext[i]); + wordcols += tp->wletters[l]; + l++; } - i++; - j += (buf[j] == ' ' && conf.text.trim && j > 0 && buf[j-1] == ' ') ? - 0 : 1; } - buf[j] = '\0'; -} - -int -get_text_properties(struct bsddialog_conf conf, char *text, int *maxword, - int *maxline, int *nlines) -{ - char *buf; - int i, buflen, wordlen, linelen; - - if ((buf = malloc(strlen(text) + 1)) == NULL) - RETURN_ERROR("Cannot building a buffer to find the properties "\ - "of the text properties"); - - prepare_text(conf, text, buf); - - buflen = strlen(buf) + 1; - *maxword = 0; - wordlen = 0; - for (i=0; i < buflen; i++) { - if (buf[i] == '\t' || buf[i] == '\n' || buf[i] == ' ' || buf[i] == '\0') - if (wordlen != 0) { - *maxword = MAX(*maxword, wordlen); - wordlen = 0; - continue; - } - if (conf.text.colors && is_ncurses_attr(buf + i)) - i += 3; - else - wordlen++; - } - - *maxline = linelen = 0; - *nlines = 1; - for (i=0; i < buflen; i++) { - switch (buf[i]) { - case '\n': - *nlines = *nlines + 1; - case '\0': - *maxline = MAX(*maxline, linelen); - linelen = 0; - break; - default: - if (conf.text.colors && is_ncurses_attr(buf + i)) - i += 3; - else - linelen++; - } + /* word */ + if (wordcols != 0) { + tp->words[tp->nword] = wordcols; + tp->nword += 1; + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); } - if (*nlines == 1 && *maxline == 0) - *nlines = 0; + /* line */ + tp->maxline = MAX(tp->maxline, currlinecols); - free(buf); + free(wtext); - return 0; + return (0); } static int -print_textpad(struct bsddialog_conf conf, WINDOW *pad, int *rows, int cols, char *text) +text_autosize(struct bsddialog_conf *conf, struct textproperties *tp, + int maxrows, int mincols, bool increasecols, int *h, int *w) { - char *buf, *string; - int i, j, x, y; - bool loop; + int i, j, x, y, z, l, line, maxwidth, tablen; - if ((buf = malloc(strlen(text) + 1)) == NULL) - RETURN_ERROR("Cannot build (analyze) text"); + maxwidth = widget_max_width(conf) - BORDERS - TEXTHMARGINS; + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; - prepare_text(conf, text, buf); - - if ((string = malloc(strlen(text) + 1)) == NULL) { - free(buf); - RETURN_ERROR("Cannot build (analyze) text"); + if (increasecols) { + mincols = MAX(mincols, tp->maxwordcols); + mincols = MAX(mincols, + (int)conf->auto_minwidth - BORDERS - TEXTHMARGINS); + mincols = MIN(mincols, maxwidth); } - i = j = x = y = 0; - loop = true; - while (loop) { - string[j] = buf[i]; - if (string[j] == '\0' || string[j] == '\n' || - string[j] == '\t' || string[j] == ' ') { - if (j != 0) { - string[j] = '\0'; - print_str(pad, rows, &y, &x, cols, string, conf.text.colors); - } - } - - switch (buf[i]) { - case '\0': - loop = false; - break; - case '\n': - j = -1; - x = 0; - y++; - break; - case '\t': - for (j=0; j<4 /*tablen*/; j++) { + 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++; - if (x >= cols) { + } + 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; + } + } } - j = -1; - break; - case ' ': - x++; - if (x >= cols) { - x = 0; - y++; - } - j = -1; - } - - if (y >= *rows) { /* check for whitespaces */ - *rows = y + 1; - wresize(pad, *rows, cols); + line = MAX(line, x); } - j++; - i++; + if (increasecols == false) + break; + if (mincols >= maxwidth) + break; + if (line >= y * (int)conf->text.cols_per_row && y <= maxrows) + break; + mincols++; } - free(string); - free(buf); + *h = (tp->nword == 0) ? 0 : y; + *w = MIN(mincols, line); /* wtext can be less than mincols */ - return 0; + return (0); } -/* autosize */ +static 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->nbuttons > 0) + wbuttons = buttons_min_width(bs); + + /* Rows */ + if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { + maxhtext = widget_max_height(conf) - BORDERS - rowsnotext; + } else { /* fixed */ + maxhtext = rows - BORDERS - rowsnotext; + } + if (bs->nbuttons > 0) + 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) - BORDERS - TEXTHMARGINS; + changewtext = false; + } else { /* fixed */ + startwtext = cols - BORDERS - TEXTHMARGINS; + changewtext = false; + } + + if (startwtext <= 0 && changewtext) + startwtext = 1; -/* - * max y, that is from 0 to LINES - 1 - t.shadowrows, - * could not be max height but avoids problems with checksize - */ -int widget_max_height(struct bsddialog_conf conf) + /* Sizing calculation */ + if (text_properties(conf, text, &tp) != 0) + return (BSDDIALOG_ERROR); + if (tp.nword > 0 && startwtext <= 0) + RETURN_FMTERROR("(fixed cols or fullscreen) " + "needed at least %d cols to draw text", + BORDERS + TEXTHMARGINS + 1); + if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext, + wtext) != 0) + return (BSDDIALOG_ERROR); + + free(tp.words); + free(tp.wletters); + + return (0); +} + +static int +widget_min_height(struct bsddialog_conf *conf, int htext, int hnotext, + bool withbuttons) { - int maxheight; + int min; + + /* dialog borders */ + min = BORDERS; - if ((maxheight = conf.shadow ? LINES - 1 - t.shadowrows : LINES - 1) <= 0) - RETURN_ERROR("Terminal too small, LINES - shadow <= 0"); + /* text */ + min += htext; - if (conf.y > 0) - if ((maxheight -= conf.y) <=0) - RETURN_ERROR("Terminal too small, LINES - shadow - y <= 0"); + /* specific widget lines without text */ + min += hnotext; - return maxheight; + /* buttons */ + if (withbuttons) + min += HBUTTONS; /* buttons and their up-border */ + + /* conf.auto_minheight */ + min = MAX(min, (int)conf->auto_minheight); + + return (min); } -/* - * max x, that is from 0 to COLS - 1 - t.shadowcols, - * * could not be max height but avoids problems with checksize - */ -int widget_max_width(struct bsddialog_conf conf) +static int +widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, + struct buttons *bs) + { - int maxwidth; + int min, delimtitle, wbottomtitle, wtitle; + + min = 0; + + /* buttons */ + if (bs->nbuttons > 0) + min += buttons_min_width(bs); + + /* text */ + if (wtext > 0) + min = MAX(min, wtext + TEXTHMARGINS); - if ((maxwidth = conf.shadow ? COLS - 1 - t.shadowcols : COLS - 1) <= 0) - RETURN_ERROR("Terminal too small, COLS - shadow <= 0"); - if (conf.x > 0) - if ((maxwidth -= conf.x) <=0) - RETURN_ERROR("Terminal too small, COLS - shadow - x <= 0"); + /* specific widget min width */ + min = MAX(min, minwidget); - return maxwidth; + /* 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 += BORDERS; + /* conf.auto_minwidth */ + min = MAX(min, (int)conf->auto_minwidth); + + return (min); } int -set_widget_size(struct bsddialog_conf conf, int rows, int cols, int *h, int *w) +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; + 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) { - if ((*h = rows) > maxheight) - RETURN_ERROR("Height too big (> terminal height - "\ - "shadow"); - } + 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; + 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) { - if ((*w = cols) > maxwidth) - RETURN_ERROR("Width too big (> terminal width - shadow)"); - } + 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; + return (0); } int -set_widget_position(struct bsddialog_conf conf, int *y, int *x, int h, int w) +set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, + int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext, + int minw) { + int htext, wtext; + + if (rows == BSDDIALOG_AUTOSIZE || cols == BSDDIALOG_AUTOSIZE || + rowstext != NULL) { + if (text_size(conf, rows, cols, text, bs, hnotext, minw, + &htext, &wtext) != 0) + return (BSDDIALOG_ERROR); + if (rowstext != NULL) + *rowstext = htext; + } + + if (rows == BSDDIALOG_AUTOSIZE) { + *h = widget_min_height(conf, htext, hnotext, bs->nbuttons > 0); + *h = MIN(*h, widget_max_height(conf)); + } - if (conf.y == BSDDIALOG_CENTER) - *y = LINES/2 - h/2; - else if (conf.y < BSDDIALOG_CENTER) + if (cols == BSDDIALOG_AUTOSIZE) { + *w = widget_min_width(conf, wtext, minw, bs); + *w = MIN(*w, widget_max_width(conf)); + } + + return (0); +} + +int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw) +{ + int minheight, minwidth; + + minheight = BORDERS + hnotext; + if (bs->nbuttons > 0) + minheight += HBUTTONS; + if (h < minheight) + RETURN_FMTERROR("Current rows: %d, needed at least: %d", + h, minheight); + + minwidth = 0; + if (bs->nbuttons > 0) + minwidth = buttons_min_width(bs); + minwidth = MAX(minwidth, minw); + minwidth += BORDERS; + if (w < minwidth) + RETURN_FMTERROR("Current cols: %d, nedeed at least %d", + w, minwidth); + + 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 >= LINES) + else if (conf->y >= SCREENLINES) RETURN_ERROR("Begin Y under the terminal"); else - *y = conf.y; + *y = conf->y; - if ((*y + h + (conf.shadow ? (int) t.shadowrows : 0)) > LINES) - RETURN_ERROR("The lower of the box under the terminal "\ + 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 = COLS/2 - w/2; - else if (conf.x < BSDDIALOG_CENTER) + 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 >= COLS) + else if (conf->x >= SCREENCOLS) RETURN_ERROR("Begin X over the right of the terminal"); else - *x = conf.x; + *x = conf->x; - if ((*x + w + (conf.shadow ? (int) t.shadowcols : 0)) > COLS) - RETURN_ERROR("The right of the box over the terminal "\ + if ((*x + w + wshadow) > SCREENCOLS) + RETURN_ERROR("The right of the box over the terminal " "(begin X + width (+ shadow) > terminal cols)"); - return 0; + return (0); } -/* Widgets builders */ -void -draw_borders(struct bsddialog_conf conf, WINDOW *win, int rows, int cols, - enum elevation elev) +int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext) { - int leftcolor, rightcolor; - int ls, rs, ts, bs, tl, tr, bl, br; - int ltee, rtee; - - 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; - - if (conf.no_lines == false) { - if (conf.ascii_lines) { - ls = rs = '|'; - ts = bs = '-'; - tl = tr = bl = br = ltee = rtee = '+'; - } - leftcolor = elev == RAISED ? t.lineraisecolor : t.linelowercolor; - rightcolor = elev == RAISED ? t.linelowercolor : t.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); - } + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, htext, &d->bs, hnotext, minw) != 0) + return (BSDDIALOG_ERROR); + if (widget_checksize(d->h, d->w, &d->bs, hnotext, minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); } -WINDOW * -new_boxed_window(struct bsddialog_conf conf, int y, int x, int rows, int cols, - enum elevation elev) +/* + * -5- Widget components and utilities + */ +int hide_dialog(struct dialog *d) { - WINDOW *win; + WINDOW *clear; - if ((win = newwin(rows, cols, y, x)) == NULL) { - set_error_string("Cannot build boxed window"); - return NULL; - } + if ((clear = newwin(d->h, d->w, d->y, d->x)) == NULL) + RETURN_ERROR("Cannot hide the widget"); + wbkgd(clear, t.screen.color); + wrefresh(clear); - wbkgd(win, t.widgetcolor); + if (d->conf->shadow) { + mvwin(clear, d->y + t.shadow.y, d->x + t.shadow.x); + wrefresh(clear); + } - draw_borders(conf, win, rows, cols, elev); + delwin(clear); - return win; + return (0); } -/* - * `enum elevation elev` could be useless because it should be always RAISED, - * to check at the end. - */ -static int -draw_widget_withtextpad(struct bsddialog_conf conf, WINDOW *shadow, - WINDOW *widget, int h, int w, enum elevation elev, - WINDOW *textpad, int *htextpad, char *text, bool buttons) +int f1help_dialog(struct bsddialog_conf *conf) { - int ts, ltee, rtee; - int colorsurroundtitle; - - ts = conf.ascii_lines ? '-' : ACS_HLINE; - ltee = conf.ascii_lines ? '+' : ACS_LTEE; - rtee = conf.ascii_lines ? '+' : ACS_RTEE; - colorsurroundtitle = elev == RAISED ? t.lineraisecolor : t.linelowercolor; + int output; + struct bsddialog_conf hconf; - if (shadow != NULL) - wnoutrefresh(shadow); + 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.escape = conf->text.escape; - // move / resize now or the caller? - draw_borders(conf, widget, h, w, elev); + output = BSDDIALOG_OK; + if (conf->key.f1_message != NULL) + output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0); - if (conf.title != NULL) { - if (t.surroundtitle && conf.no_lines == false) { - wattron(widget, colorsurroundtitle); - mvwaddch(widget, 0, w/2 - strlen(conf.title)/2 - 1, rtee); - wattroff(widget, colorsurroundtitle); - } - wattron(widget, t.titlecolor); - mvwaddstr(widget, 0, w/2 - strlen(conf.title)/2, conf.title); - wattroff(widget, t.titlecolor); - if (t.surroundtitle && conf.no_lines == false) { - wattron(widget, colorsurroundtitle); - waddch(widget, ltee); - wattroff(widget, colorsurroundtitle); - } - } + if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL) + output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0); - if (conf.hline != NULL) { - wattron(widget, t.bottomtitlecolor); - wmove(widget, h - 1, w/2 - strlen(conf.hline)/2 - 1); - waddch(widget, '['); - waddstr(widget, conf.hline); - waddch(widget, ']'); - wattroff(widget, t.bottomtitlecolor); - } + return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); +} - if (textpad == NULL && text != NULL) /* no pad, text null for textbox */ - print_text(conf, widget, 1, 2, w-3, text); +void draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev) +{ + int h, w; + int leftcolor, rightcolor; + int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee; - if (buttons && conf.no_lines == false) { - wattron(widget, t.lineraisecolor); - mvwaddch(widget, h-3, 0, ltee); - mvwhline(widget, h-3, 1, ts, w-2); - wattroff(widget, t.lineraisecolor); + if (conf->no_lines) + return; - wattron(widget, t.linelowercolor); - mvwaddch(widget, h-3, w-1, rtee); - wattroff(widget, t.linelowercolor); + 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; } - wnoutrefresh(widget); - - if (textpad == NULL) - return 0; /* widget_init() ends */ + getmaxyx(win, h, w); + 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, w-1, tr); + mvwvline(win, 1, w-1, rs, h-2); + mvwaddch(win, h-1, w-1, br); + mvwhline(win, h-1, 1, bs, w-2); + wattroff(win, rightcolor); +} - if (text != NULL) /* programbox etc */ - if (print_textpad(conf, textpad, htextpad, - w - HBORDERS - t.texthmargin * 2, text) !=0) - return BSDDIALOG_ERROR; +void +update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w, + enum elevation elev) +{ + wclear(win); + wresize(win, h, w); + mvwin(win, y, x); + draw_borders(conf, win, elev); +} - return 0; +void +rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext) +{ + pnoutrefresh(d->textpad, ytext, xtext, + d->y + BORDER + upnotext, + d->x + BORDER + TEXTHMARGIN, + d->y + d->h - 1 - downnotext - BORDER, + d->x + d->w - TEXTHMARGIN - BORDER); } /* - * `enum elevation elev` could be useless because it should be always RAISED, - * to check at the end. + * -6- Dialog init/build, update/draw, destroy */ -int -update_widget_withtextpad(struct bsddialog_conf conf, WINDOW *shadow, - WINDOW *widget, int h, int w, enum elevation elev, - WINDOW *textpad, int *htextpad, char *text, bool buttons) +void end_dialog(struct dialog *d) { - int error; + if (d->conf->sleep > 0) + sleep(d->conf->sleep); - /* nothing for now */ + delwin(d->textpad); + delwin(d->widget); + if (d->conf->shadow) + delwin(d->shadow); - error = draw_widget_withtextpad(conf, shadow, widget, h, w, - elev, textpad, htextpad, text, buttons); + if (d->conf->clear) + hide_dialog(d); - return error; + if (d->conf->get_height != NULL) + *d->conf->get_height = d->h; + if (d->conf->get_width != NULL) + *d->conf->get_width = d->w; } -/* - * `enum elevation elev` could be useless because it should be always RAISED, - * to check at the end. - */ -int -new_widget_withtextpad(struct bsddialog_conf conf, WINDOW **shadow, - WINDOW **widget, int y, int x, int h, int w, enum elevation elev, - WINDOW **textpad, int *htextpad, char *text, bool buttons) +static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext) { - int error; + enum bsddialog_color bg; - if (conf.shadow) { - *shadow = newwin(h, w, y + t.shadowrows, x + t.shadowcols); - if (*shadow == NULL) - RETURN_ERROR("Cannot build shadow"); - wbkgd(*shadow, t.shadowcolor); - } + if (is_wtext_attr(wtext) == false) + return (false); - if ((*widget = new_boxed_window(conf, y, x, h, w, elev)) == NULL) { - if (conf.shadow) - delwin(*shadow); - return BSDDIALOG_ERROR; + if ((wtext[2] >= L'0') && (wtext[2] <= L'7')) { + bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL); + wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0)); + return (true); } - if (textpad == NULL) { /* widget_init() */ - error = draw_widget_withtextpad(conf, *shadow, *widget, h, w, - elev, NULL, NULL, text, buttons); - return error; + 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'd': + wattron(win, A_DIM); + break; + case L'D': + wattroff(win, A_DIM); + break; + case L'k': + wattron(win, A_BLINK); + break; + case L'K': + wattroff(win, A_BLINK); + break; + case L'r': + wattron(win, A_REVERSE); + break; + case L'R': + wattroff(win, A_REVERSE); + break; + case L's': + wattron(win, A_STANDOUT); + break; + case L'S': + wattroff(win, A_STANDOUT); + break; + case L'u': + wattron(win, A_UNDERLINE); + break; + case L'U': + wattroff(win, A_UNDERLINE); + break; } - if (text != NULL) { /* programbox etc */ - *htextpad = 1; - *textpad = newpad(*htextpad, w - HBORDERS - t.texthmargin * 2); - if (*textpad == NULL) { - delwin(*textpad); - if (conf.shadow) - delwin(*shadow); - RETURN_ERROR("Cannot build the pad window for text"); - } - wbkgd(*textpad, t.widgetcolor); - } + return (true); +} - error = draw_widget_withtextpad(conf, *shadow, *widget, h, w, elev, - *textpad, htextpad, text, buttons); +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'; - return error; + 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 { + i +=3 ; + } + } + } 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; + } + } + } } -int -new_widget(struct bsddialog_conf conf, WINDOW **widget, int *y, int *x, - char *text, int *h, int *w, WINDOW **shadow, bool buttons) +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*"); - // to delete (each widget has to check its x,y,h,w) - if (*h <= 0) - ; /* todo */ + if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL) + RETURN_ERROR("Cannot build (analyze) text"); - if (*w <= 0) - ; /* todo */ + getmaxyx(pad, rows, cols); + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; - *y = (conf.y < 0) ? (LINES/2 - *h/2) : conf.y; - *x = (conf.x < 0) ? (COLS/2 - *w/2) : conf.x; + i = j = x = y = 0; + loop = true; + while (loop) { + string[j] = wtext[i]; - if (new_widget_withtextpad(conf, shadow, widget, *y, *x, *h, *w, RAISED, - NULL, NULL, text, buttons) != 0) - return BSDDIALOG_ERROR; + 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.escape); + } - if (conf.shadow) - wrefresh(*shadow); + 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; + } - wrefresh(*widget); + if (y >= rows) { + rows = y + 1; + wresize(pad, rows, cols); + } + + j++; + i++; + } + + free(wtext); + free(string); - return 0; + return (0); } -void -end_widget_withtextpad(struct bsddialog_conf conf, WINDOW *window, int h, int w, - WINDOW *textpad, WINDOW *shadow) +int draw_dialog(struct dialog *d) { - int y, x; + int wtitle, wbottomtitle, ts, ltee, rtee; - getbegyx(window, y, x); /* for clear, add y & x to args? */ + ts = d->conf->ascii_lines ? '-' : ACS_HLINE; + ltee = d->conf->ascii_lines ? '+' : ACS_LTEE; + rtee = d->conf->ascii_lines ? '+' : ACS_RTEE; - if (conf.sleep > 0) - sleep(conf.sleep); + if (d->conf->shadow) { + wclear(d->shadow); + wresize(d->shadow, d->h, d->w); + mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x); + wnoutrefresh(d->shadow); + } - if (textpad != NULL) - delwin(textpad); + wclear(d->widget); + wresize(d->widget, d->h, d->w); + mvwin(d->widget, d->y, d->x); + draw_borders(d->conf, d->widget, RAISED); + + if (d->conf->title != NULL) { + if ((wtitle = strcols(d->conf->title)) < 0) + return (BSDDIALOG_ERROR); + if (t.dialog.delimtitle && d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + mvwaddch(d->widget, 0, d->w/2 - wtitle/2 -1, rtee); + wattroff(d->widget, t.dialog.lineraisecolor); + } + wattron(d->widget, t.dialog.titlecolor); + mvwaddstr(d->widget, 0, d->w/2 - wtitle/2, d->conf->title); + wattroff(d->widget, t.dialog.titlecolor); + if (t.dialog.delimtitle && d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + waddch(d->widget, ltee); + wattroff(d->widget, t.dialog.lineraisecolor); + } + } - delwin(window); + if (d->bs.nbuttons > 0) { + if (d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + mvwaddch(d->widget, d->h-3, 0, ltee); + mvwhline(d->widget, d->h-3, 1, ts, d->w-2); + wattroff(d->widget, t.dialog.lineraisecolor); - if (conf.shadow) - delwin(shadow); + wattron(d->widget, t.dialog.linelowercolor); + mvwaddch(d->widget, d->h-3, d->w-1, rtee); + wattroff(d->widget, t.dialog.linelowercolor); + } + draw_buttons(d); + } + + if (d->conf->bottomtitle != NULL) { + if ((wbottomtitle = strcols(d->conf->bottomtitle)) < 0) + return (BSDDIALOG_ERROR); + wattron(d->widget, t.dialog.bottomtitlecolor); + wmove(d->widget, d->h - 1, d->w/2 - wbottomtitle/2 - 1); + waddch(d->widget, ' '); + waddstr(d->widget, d->conf->bottomtitle); + waddch(d->widget, ' '); + wattroff(d->widget, t.dialog.bottomtitlecolor); + } + + wnoutrefresh(d->widget); + + wclear(d->textpad); + /* `infobox "" 0 2` fails but text is empty and textpad remains 1 1 */ + wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS); + + if (print_textpad(d->conf, d->textpad, d->text) != 0) + return (BSDDIALOG_ERROR); - if (conf.clear) - hide_widget(y, x, h, w, shadow != NULL); + d->built = true; - if (conf.get_height != NULL) - *conf.get_height = h; - if (conf.get_width != NULL) - *conf.get_width = w; + return (0); } -void -end_widget(struct bsddialog_conf conf, WINDOW *window, int h, int w, - WINDOW *shadow) +int +prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows, + int cols, struct dialog *d) { + CHECK_PTR(conf); + + d->built = false; + d->conf = conf; + d->rows = rows; + d->cols = cols; + d->text = CHECK_STR(text); + d->bs.nbuttons = 0; + + if (d->conf->shadow) { + if ((d->shadow = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW shadow"); + wbkgd(d->shadow, t.shadow.color); + } - end_widget_withtextpad(conf, window, h, w, NULL, shadow); -} + if ((d->widget = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW widget"); + wbkgd(d->widget, t.dialog.color); + + /* fake for textpad */ + if ((d->textpad = newpad(1, 1)) == NULL) + RETURN_ERROR("Cannot build the pad WINDOW for text"); + wbkgd(d->textpad, t.dialog.color); + + return (0); +}
\ No newline at end of file |