diff options
Diffstat (limited to 'contrib/bsddialog/lib')
-rw-r--r-- | contrib/bsddialog/lib/GNUmakefile (renamed from contrib/bsddialog/lib/GNUMakefile) | 15 | ||||
-rw-r--r-- | contrib/bsddialog/lib/Makefile | 49 | ||||
-rw-r--r-- | contrib/bsddialog/lib/barbox.c | 815 | ||||
-rw-r--r-- | contrib/bsddialog/lib/bsddialog.3 | 790 | ||||
-rw-r--r-- | contrib/bsddialog/lib/bsddialog.h | 68 | ||||
-rw-r--r-- | contrib/bsddialog/lib/bsddialog_progressview.h | 8 | ||||
-rw-r--r-- | contrib/bsddialog/lib/bsddialog_theme.h | 45 | ||||
-rw-r--r-- | contrib/bsddialog/lib/datebox.c | 746 | ||||
-rw-r--r-- | contrib/bsddialog/lib/formbox.c | 1195 | ||||
-rw-r--r-- | contrib/bsddialog/lib/infobox.c | 96 | ||||
-rw-r--r-- | contrib/bsddialog/lib/lib_util.c | 1504 | ||||
-rw-r--r-- | contrib/bsddialog/lib/lib_util.h | 169 | ||||
-rw-r--r-- | contrib/bsddialog/lib/libbsddialog.c | 64 | ||||
-rw-r--r-- | contrib/bsddialog/lib/menubox.c | 887 | ||||
-rw-r--r-- | contrib/bsddialog/lib/messagebox.c | 260 | ||||
-rw-r--r-- | contrib/bsddialog/lib/textbox.c | 279 | ||||
-rw-r--r-- | contrib/bsddialog/lib/theme.c | 314 | ||||
-rw-r--r-- | contrib/bsddialog/lib/timebox.c | 534 |
18 files changed, 4605 insertions, 3233 deletions
diff --git a/contrib/bsddialog/lib/GNUMakefile b/contrib/bsddialog/lib/GNUmakefile index 0d724b803be3..7c7a9bc25ee4 100644 --- a/contrib/bsddialog/lib/GNUMakefile +++ b/contrib/bsddialog/lib/GNUmakefile @@ -3,16 +3,19 @@ # # Written in 2021 by Alfonso Sabato Siciliano -VERSION = 0.2 LIBRARY = bsddialog LIBRARY_SO = lib${LIBRARY:=.so} HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h -SOURCES = barbox.c formbox.c infobox.c libbsddialog.c lib_util.c menubox.c \ - messagebox.c textbox.c theme.c timebox.c +SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \ + menubox.c messagebox.c textbox.c theme.c timebox.c OBJECTS = $(SOURCES:.c=.o) -CFLAGS = -D_XOPEN_SOURCE_EXTENDED -Wall -Wextra -Wno-implicit-fallthrough \ - -Werror -fpic -LDFLAGS = -lformw -lncursesw -ltinfo + +ifneq ($(ENABLEDEBUG),) +CFLAGS += -g +endif +CFLAGS += -D_XOPEN_SOURCE_EXTENDED -D_XOPEN_SOURCE -D_GNU_SOURCE \ + -Wall -Wextra -Werror -fpic +LDFLAGS += -lncursesw -ltinfo LIBFLAG = -shared RM = rm -f diff --git a/contrib/bsddialog/lib/Makefile b/contrib/bsddialog/lib/Makefile index 962b059b3e03..252b33f79848 100644 --- a/contrib/bsddialog/lib/Makefile +++ b/contrib/bsddialog/lib/Makefile @@ -3,39 +3,27 @@ # # Written in 2021 by Alfonso Sabato Siciliano -VERSION = 0.2 LIBRARY = bsddialog LIBRARY_SO = lib${LIBRARY:=.so} LIBRARY_A = lib${LIBRARY:=.a} HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h -SOURCES = barbox.c formbox.c infobox.c libbsddialog.c lib_util.c menubox.c \ - messagebox.c textbox.c theme.c timebox.c +SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \ + menubox.c messagebox.c textbox.c theme.c timebox.c OBJECTS = ${SOURCES:.c=.o} -CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -LDFLAGS += -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \ - -Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} \ - -L/usr/lib -lformw -lncursesw -ltinfow .if defined(DEBUG) -# `make -DDEBUG` -CFLAGS = -g -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -.else -CFLAGS += -std=gnu99 -fstack-protector-strong +CFLAGS += -g .endif +CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -std=gnu99 \ + -fstack-protector-strong +LDFLAGS += -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \ + -Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} \ + -L/usr/lib -lncursesw -ltinfow -LOCALBASE = /usr/local LN = ln -s -f RM = rm -f -CP = cp -GZIP = gzip -cn -LDCONFIG = /sbin/ldconfig -m -MAN = ${OUTPUT}.3 -GZIP = gzip -cn -MANDIR = ${LOCALBASE}/share/man/man3 -INSTALL = install -RM = rm -f -all : man ${LIBRARY} +all : ${LIBRARY} ${LIBRARY}: ${LIBRARY_SO} ${LIBRARY_A} @@ -52,24 +40,5 @@ ${LIBRARY_A}: ${OBJECTS} .c.o: ${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} -man: - ${GZIP} ${LIBRARY}.3 > ${LIBRARY}.3.gz - clean: ${RM} ${LIBRARY_SO}* *.o *~ *.gz ${LIBRARY_A} - - -install: - ${INSTALL} -m 644 ${HEADERS} ${LOCALBASE}/include - ${INSTALL} -m 644 -s ${LIBRARY_SO}.${VERSION} ${LOCALBASE}/lib/ - ${INSTALL} -l rs ${LOCALBASE}/lib/${LIBRARY_SO}.${VERSION} ${LOCALBASE}/lib/${LIBRARY_SO} - ${INSTALL} -m 644 ${LIBRARY_A} ${LOCALBASE}/lib - ${LDCONFIG} ${LOCALBASE}/lib - ${INSTALL} -m 644 ${LIBRARY}.3.gz ${MANDIR} - -unistall: - ${RM} ${LOCALBASE}/include/${LIBRARY}*.h - ${RM} ${LOCALBASE}/lib/${LIBRARY_SO} - ${RM} ${LOCALBASE}/lib/${LIBRARY_SO}.${VERSION} - ${LDCONFIG} ${LOCALBASE}/lib - ${RM} ${MANDIR}/${LIBRARY}.3.gz diff --git a/contrib/bsddialog/lib/barbox.c b/contrib/bsddialog/lib/barbox.c index 49aa105c1de3..4feea20c6441 100644 --- a/contrib/bsddialog/lib/barbox.c +++ b/contrib/bsddialog/lib/barbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,9 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> #include <curses.h> #include <stdlib.h> #include <string.h> @@ -39,139 +36,138 @@ #include "bsddialog_theme.h" #include "lib_util.h" -#define BARPADDING 2 -#define MINBARLEN 15 -#define MINBARWIDTH (2 + 2 * BARPADDING + MINBARLEN) -#define MINMGBARLEN 18 -#define MINMGBARWIDTH (2 + 2 * BARPADDING + MINMGBARLEN) +#define BARPADDING 2 /* Dialog border | BARPADDING | box bar */ +#define BOXBORDERS 2 +#define MIN_WBAR 15 +#define MIN_WBOX (BARPADDING + BOXBORDERS + MIN_WBAR + BARPADDING) +#define MIN_WMGBAR 18 /* Mixedgauge main bar */ +#define MIN_WMGBOX (BARPADDING + BOXBORDERS + MIN_WMGBAR + BARPADDING) +#define HBOX 3 +#define WBOX(d) ((d)->w - BORDERS - BARPADDING - BARPADDING) +#define WBAR(d) (WBOX(d) - BOXBORDERS) bool bsddialog_interruptprogview; bool bsddialog_abortprogview; -int bsddialog_total_progview; - -static void -draw_bar(WINDOW *win, int y, int x, int barlen, int perc, bool withlabel, - int label) +long long int bsddialog_total_progview; + +static const char states[12][14] = { + " Succeeded ", /* -1 */ + " Failed ", /* -2 */ + " Passed ", /* -3 */ + " Completed ", /* -4 */ + " Checked ", /* -5 */ + " Done ", /* -6 */ + " Skipped ", /* -7 */ + " In Progress ", /* -8 */ + "(blank) ", /* -9 */ + " N/A ", /* -10 */ + " Pending ", /* -11 */ + " UNKNOWN ", /* < -11, no API */ +}; + +struct bar { + bool toupdate; + WINDOW *win; + int y; /* bar y in win */ + int x; /* bar x in win */ + int w; /* width in win */ + int perc; /* barlen = (w * perc) / 100 */ + const char* fmt; /* format for label */ + int label; /* rangebox and pause perc!=label */ +}; + +static void draw_bar(struct bar *b) { - int i, blue_x, color, stringlen; - char labelstr[128]; - - blue_x = perc > 0 ? (perc * barlen) / 100 : -1; - - wmove(win, y, x); - for (i = 0; i < barlen; i++) { - color = (i <= blue_x) ? t.bar.f_color : t.bar.color; - wattron(win, color); - waddch(win, ' '); - wattroff(win, color); - } - - if (withlabel) - sprintf(labelstr, "%d", label); - else - sprintf(labelstr, "%3d%%", perc); - stringlen = (int)strlen(labelstr); - wmove(win, y, x + barlen/2 - stringlen/2); - for (i = 0; i < stringlen; i++) { - color = (blue_x + 1 <= barlen/2 - stringlen/2 + i ) ? - t.bar.color : t.bar.f_color; - wattron(win, color); - waddch(win, labelstr[i]); - wattroff(win, color); - } + int barlen, xlabel; + chtype ch; + char label[128]; + + barlen = b->perc > 0 ? (b->perc * b->w) / 100 : 0; + + ch = ' ' | t.bar.f_color; + mvwhline(b->win, b->y, b->x, ch, barlen); + ch = ' ' | t.bar.color; + mvwhline(b->win, b->y, b->x + barlen, ch, b->w - barlen); + + sprintf(label, b->fmt, b->label); + xlabel = b->x + b->w/2 - (int)strlen(label)/2; /* 1-byte-char string */ + wattron(b->win, t.bar.color); /* x+barlen < xlabel */ + mvwaddstr(b->win, b->y, xlabel, label); + wattroff(b->win, t.bar.color); + wattron(b->win, t.bar.f_color); /* x+barlen >= xlabel */ + mvwaddnstr(b->win, b->y, xlabel, label, MAX((b->x+barlen) - xlabel, 0)); + wattroff(b->win, t.bar.f_color); + + if (b->toupdate) + wnoutrefresh(b->win); + b->toupdate = false; } -static int -bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - const char *text, struct buttons *bs) +static void update_barbox(struct dialog *d, struct bar *b, bool buttons) { - int htext, wtext; + int y; - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, bs, 3, MINBARWIDTH, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, MINBARWIDTH, bs); - - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 3 /* bar */, bs != NULL); - - return (0); -} - -static int -bar_checksize(int rows, int cols, struct buttons *bs) -{ - int minheight, minwidth; - - minwidth = 0; - if (bs != NULL) /* gauge has not buttons */ - minwidth = buttons_width(*bs); - - minwidth = MAX(minwidth, MINBARWIDTH); - minwidth += VBORDERS; - - if (cols < minwidth) - RETURN_ERROR("Few cols to draw bar and/or buttons"); - - minheight = HBORDERS + 3; - if (bs != NULL) - minheight += 2; - if (rows < minheight) - RETURN_ERROR("Few rows to draw bar"); - - return (0); + y = d->y + d->h - BORDER - HBOX; + if (buttons) + y -= HBUTTONS; + update_box(d->conf, b->win, y, d->x + BORDER + BARPADDING, HBOX, + WBOX(d), RAISED); } int bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int perc, int fd, const char *sep) + int cols, unsigned int perc, int fd, const char *sep, const char *end) { bool mainloop; - int y, x, h, w, fd2; + int fd2; FILE *input; - WINDOW *widget, *textpad, *bar, *shadow; char inputbuf[2048], ntext[2048], *pntext; + struct bar b; + struct dialog d; - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0) - return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, NULL) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, NULL, - false) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%3d%%"; - bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED); - - mainloop = (fd < 0) ? false : true; + input = NULL; + if (fd >= 0) { + CHECK_PTR(sep); + CHECK_PTR(end); - if (mainloop) { fd2 = dup(fd); - input = fdopen(fd2, "r"); - if (input == NULL) - RETURN_ERROR("Cannot build FILE* from fd"); - } else - input = NULL; + if ((input = fdopen(fd2, "r")) == NULL) + RETURN_FMTERROR("Cannot build FILE* from fd %d", fd); + } + perc = MIN(perc, 100); + mainloop = true; while (mainloop) { - wrefresh(widget); - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-4, - x+w-1-TEXTHMARGIN); - draw_borders(conf, bar, 3, w-6, RAISED); - draw_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/); - wrefresh(bar); + if (d.built) { + hide_dialog(&d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(&d, HBOX, MIN_WBOX, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d)) + return (BSDDIALOG_ERROR); + if (d.built) + refresh(); /* fix grey lines expanding screen */ + TEXTPAD(&d, HBOX); + update_barbox(&d, &b, false); + b.w = WBAR(&d); + b.perc = b.label = perc; + b.toupdate = true; + draw_bar(&b); + doupdate(); + if (input == NULL) /* that is fd < 0 */ + break; while (true) { fscanf(input, "%s", inputbuf); - if (strcmp(inputbuf,"EOF") == 0) { + if (strcmp(inputbuf, end) == 0) { mainloop = false; break; } @@ -181,154 +177,173 @@ bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, if (mainloop == false) break; fscanf(input, "%d", &perc); - perc = perc > 100 ? 100 : perc; + perc = MIN(perc, 100); pntext = &ntext[0]; ntext[0] = '\0'; while (true) { fscanf(input, "%s", inputbuf); - if (strcmp(inputbuf,"EOF") == 0) { + if (strcmp(inputbuf, end) == 0) { mainloop = false; break; } if (strcmp(inputbuf, sep) == 0) break; strcpy(pntext, inputbuf); - pntext += strlen(inputbuf); + pntext += strlen(inputbuf); /* end string, no strlen */ pntext[0] = ' '; pntext++; } - if (update_dialog(conf, shadow, widget, y, x, h, w, textpad, - ntext, NULL, false) != 0) - return (BSDDIALOG_ERROR); + pntext[0] = '\0'; + d.text = ntext; } if (input != NULL) fclose(input); - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + delwin(b.win); + end_dialog(&d); return (BSDDIALOG_OK); } /* Mixedgauge */ +static void +mvwaddcstr(WINDOW *win, int y, int x, const char *mbstring, unsigned int cols) +{ + size_t charlen, n, w; + mbstate_t mbs; + const char *pmbstring; + wchar_t wch; + + w = n = 0; + pmbstring = mbstring; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + mbtowc(&wch, pmbstring, charlen); + w += (wch == L'\t') ? TABSIZE : wcwidth(wch); + if (w > cols) + break; + pmbstring += charlen; + n += charlen; + } + mvwaddnstr(win, y, x, mbstring, n); + if(w > cols) + mvwaddstr(win, y, (x + cols) - 3, "..."); +} + +static int +mixedgauge_size_position(struct dialog *d, int nminibars, + const char **minilabels, int *htext) +{ + int i, max_minibarlen; + + max_minibarlen = 0; + for (i = 0; i < (int)nminibars; i++) + max_minibarlen = MAX(max_minibarlen, + (int)strcols(CHECK_STR(minilabels[i]))); + max_minibarlen += 18; /* ' '<max_minibarlen>' ['13'] ' */ + max_minibarlen = MAX(max_minibarlen, MIN_WMGBOX); /* mainbar */ + + 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, nminibars + HBOX, max_minibarlen) != 0) + return (BSDDIALOG_ERROR); + if (widget_checksize(d->h, d->w, &d->bs, nminibars + HBOX, + MIN_WMGBOX) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); +} + static int do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int mainperc, unsigned int nminibars, const char **minilabels, int *minipercs, bool color) { - int i, output, miniperc, y, x, h, w, ypad, max_minbarlen; - int htextpad, htext, wtext; - int colorperc, red, green; - WINDOW *widget, *textpad, *bar, *shadow; - char states[12][14] = { - " Succeeded ", /* -1 */ - " Failed ", /* -2 */ - " Passed ", /* -3 */ - " Completed ", /* -4 */ - " Checked ", /* -5 */ - " Done ", /* -6 */ - " Skipped ", /* -7 */ - " In Progress ", /* -8 */ - "(blank) ", /* -9 */ - " N/A ", /* -10 */ - " Pending ", /* -11 */ - " UNKNOWN ", /* < -11, no API */ - }; + int i, miniperc; + int ystext, htext; + int minicolor, red, green; + struct bar b; + struct dialog d; + + CHECK_ARRAY(nminibars, minilabels); + CHECK_ARRAY(nminibars, minipercs); red = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED, BSDDIALOG_BOLD); green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD); - max_minbarlen = 0; - for (i = 0; i < (int)nminibars; i++) - max_minbarlen = MAX(max_minbarlen, (int)strlen(minilabels[i])); - max_minbarlen += 3 + 16; /* seps + [...] */ - max_minbarlen = MAX(max_minbarlen, MINMGBARWIDTH); /* mainbar */ - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - - /* mixedgauge autosize */ - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, NULL, nminibars + 3, - max_minbarlen, &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - if (cols == BSDDIALOG_AUTOSIZE) - w = widget_min_width(conf, wtext, max_minbarlen, NULL); - if (rows == BSDDIALOG_AUTOSIZE) - h = widget_min_height(conf, htext, nminibars + 3, false); - - /* mixedgauge checksize */ - if (w < max_minbarlen + 2) - RETURN_ERROR("Few cols for this mixedgauge"); - if (h < 5 + (int)nminibars) - RETURN_ERROR("Few rows for this mixedgauge"); - - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (mixedgauge_size_position(&d, nminibars, minilabels, &htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d) != 0) return (BSDDIALOG_ERROR); - - output = new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, - NULL, false); - if (output == BSDDIALOG_ERROR) - return (output); /* mini bars */ + b.win = d.widget; + b.x = 1 + d.w - 2 - 15; + b.w = 13; + b.fmt = "%3d%%"; + b.toupdate = false; for (i = 0; i < (int)nminibars; i++) { miniperc = minipercs[i]; - if (miniperc == BSDDIALOG_MG_BLANK) - continue; /* label */ - if (color && (miniperc >= 0)) - wattron(widget, A_BOLD); - mvwaddstr(widget, i+1, 2, minilabels[i]); - wattroff(widget, A_BOLD); + if (color && miniperc >= 0) + wattron(d.widget, A_BOLD); + mvwaddcstr(d.widget, i+1, 2, CHECK_STR(minilabels[i]), d.w-20); + if (color && miniperc >= 0) + wattroff(d.widget, A_BOLD); /* perc */ - if (miniperc < -11) - mvwaddstr(widget, i+1, w-2-15, states[11]); - else if (miniperc < 0) { - mvwaddstr(widget, i+1, w-2-15, "[ ]"); - colorperc = -1; + if (miniperc == BSDDIALOG_MG_BLANK) + continue; + mvwaddstr(d.widget, i+1, d.w-2-15, "[ ]"); + if (miniperc >= 0) { + b.y = i + 1; + b.perc = b.label = MIN(miniperc, 100); + draw_bar(&b); + } else { /* miniperc < 0 */ + if (miniperc < BSDDIALOG_MG_PENDING) + miniperc = -12; /* UNKNOWN */ + minicolor = t.dialog.color; if (color && miniperc == BSDDIALOG_MG_FAILED) - colorperc = red; - if (color && miniperc == BSDDIALOG_MG_DONE) - colorperc = green; - if (colorperc != -1) - wattron(widget, colorperc); + minicolor = red; + else if (color && miniperc == BSDDIALOG_MG_DONE) + minicolor = green; + wattron(d.widget, minicolor); miniperc = abs(miniperc + 1); - mvwaddstr(widget, i+1, 1+w-2-15, states[miniperc]); - if (colorperc != -1) - wattroff(widget, colorperc); - } - else { /* miniperc >= 0 */ - if (miniperc > 100) - miniperc = 100; - mvwaddstr(widget, i+1, w-2-15, "[ ]"); - draw_bar(widget, i+1, 1+w-2-15, 13, miniperc, false, - -1 /*unused*/); + mvwaddstr(d.widget, i+1, 1+d.w-2-15, states[miniperc]); + wattroff(d.widget, minicolor); } } + wnoutrefresh(d.widget); - wrefresh(widget); - getmaxyx(textpad, htextpad, i /* unused */); - ypad = y + h - 4 - htextpad; - ypad = ypad < y+(int)nminibars ? y+(int)nminibars : ypad; - prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2); + /* text */ + ystext = MAX(d.h - BORDERS - htext - HBOX, (int)nminibars); + rtextpad(&d, 0, 0, ystext, HBOX); /* main bar */ - bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED); - - draw_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/); - - wattron(bar, t.bar.color); - mvwaddstr(bar, 0, 2, "Overall Progress"); - wattroff(bar, t.bar.color); - - wrefresh(bar); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + update_barbox(&d, &b, false); + wattron(b.win, t.bar.color); + mvwaddstr(b.win, 0, 2, "Overall Progress"); + wattroff(b.win, t.bar.color); + + b.y = b.x = 1; + b.w = WBAR(&d); + b.fmt = "%3d%%"; + b.perc = b.label = MIN(mainperc, 100); + b.toupdate = true; + draw_bar(&b); - /* getch(); port ncurses shows nothing */ + doupdate(); + /* getch(); to test with "alternate mode" */ - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + delwin(b.win); + end_dialog(&d); return (BSDDIALOG_OK); } @@ -338,12 +353,12 @@ bsddialog_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int mainperc, unsigned int nminibars, const char **minilabels, int *minipercs) { - int output; + int retval; - output = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars, + retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars, minilabels, minipercs, false); - return (output); + return (retval); } int @@ -352,7 +367,7 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, struct bsddialog_fileminibar *minibar) { bool update; - int perc, output, *minipercs; + int perc, retval, *minipercs; unsigned int i, mainperc, totaltodo; float readforsec; const char **minilabels; @@ -371,7 +386,7 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, } refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1; - output = BSDDIALOG_OK; + retval = BSDDIALOG_OK; i = 0; update = true; time(&told); @@ -384,9 +399,9 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, time(&tnew); if (update || tnew > told + refresh) { - output = do_mixedgauge(conf, text, rows, cols, mainperc, + retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibar, minilabels, minipercs, true); - if (output == BSDDIALOG_ERROR) + if (retval == BSDDIALOG_ERROR) return (BSDDIALOG_ERROR); move(SCREENLINES - 1, 2); @@ -412,7 +427,8 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, minipercs[i] = BSDDIALOG_MG_DONE; update = true; i++; - } else if (minibar[i].status == BSDDIALOG_MG_FAILED || perc < 0) { + } else if (minibar[i].status == BSDDIALOG_MG_FAILED || + perc < 0) { minipercs[i] = BSDDIALOG_MG_FAILED; update = true; } else /* perc >= 0 */ @@ -421,326 +437,279 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, free(minilabels); free(minipercs); - return (output); + return (retval); +} + +static int rangebox_redraw(struct dialog *d, struct bar *b, int *bigchange) +{ + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); + + b->w = WBAR(d); + *bigchange = MAX(1, b->w / 10); + update_barbox(d, b, true); + b->toupdate = true; + + return (0); } int bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, int cols, int min, int max, int *value) { - bool loop, buttupdate, barupdate; - int y, x, h, w; - int input, currvalue, output, sizebar, bigchange, positions; - float perc; - WINDOW *widget, *textpad, *bar, *shadow; - struct buttons bs; - - if (value == NULL) - RETURN_ERROR("*value cannot be NULL"); + bool loop; + int currvalue, retval, bigchange, positions; + wint_t input; + struct bar b; + struct dialog d; + CHECK_PTR(value); if (min >= max) - RETURN_ERROR("min >= max"); + RETURN_FMTERROR("min (%d) >= max (%d)", min, max); + if (*value < min) + RETURN_FMTERROR("value (%d) < min (%d)", *value, min); + if (*value > max) + RETURN_FMTERROR("value (%d) > max (%d)", *value, max); currvalue = *value; positions = max - min + 1; - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%d"; + if (rangebox_redraw(&d, &b, &bigchange) != 0) return (BSDDIALOG_ERROR); - doupdate(); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN); - - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - bigchange = MAX(1, sizebar/10); - - bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3, - sizebar + 2, RAISED); - - loop = buttupdate = barupdate = true; + loop = true; while (loop) { - if (buttupdate) { - draw_buttons(widget, bs, true); - wrefresh(widget); - buttupdate = false; + if (b.toupdate) { + b.perc = ((float)(currvalue - min)*100) / (positions-1); + b.label = currvalue; + draw_bar(&b); } - if (barupdate) { - perc = ((float)(currvalue - min)*100) / (positions-1); - draw_bar(bar, 1, 1, sizebar, perc, true, currvalue); - barupdate = false; - wrefresh(bar); - } - - input = getch(); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; - *value = currvalue; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttupdate = true; + case KEY_CTRL('n'): + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; + case KEY_CTRL('p'): case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_HOME: currvalue = max; - barupdate = true; + b.toupdate = true; break; case KEY_END: currvalue = min; - barupdate = true; + b.toupdate = true; break; case KEY_NPAGE: currvalue -= bigchange; if (currvalue < min) currvalue = min; - barupdate = true; + b.toupdate = true; break; case KEY_PPAGE: currvalue += bigchange; if (currvalue > max) currvalue = max; - barupdate = true; + b.toupdate = true; break; + case '-': case KEY_UP: - if (currvalue < max) { - currvalue++; - barupdate = true; + if (currvalue > min) { + currvalue--; + b.toupdate = true; } break; + case '+': case KEY_DOWN: - if (currvalue > min) { - currvalue--; - barupdate = true; + if (currvalue < max) { + currvalue++; + b.toupdate = true; } break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, - &bs) != 0) + if (rangebox_redraw(&d, &b, &bigchange) != 0) return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget,y, x, h, w, - textpad, text, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (rangebox_redraw(&d, &b, &bigchange) != 0) return (BSDDIALOG_ERROR); - - doupdate(); - - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - bigchange = MAX(1, sizebar/10); - wclear(bar); - mvwin(bar, y + h - 6, x + 1 + BARPADDING); - wresize(bar, 3, sizebar + 2); - draw_borders(conf, bar, 3, sizebar+2, RAISED); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, - x+w-1-TEXTHMARGIN); - - barupdate = true; break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + *value = currvalue; + + delwin(b.win); + end_dialog(&d); - return (output); + return (retval); } -int -bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int sec) +static int pause_redraw(struct dialog *d, struct bar *b) { - bool loop, buttupdate, barupdate; - int output, y, x, h, w, input, tout, sizebar; - float perc; - WINDOW *widget, *textpad, *bar, *shadow; - struct buttons bs; - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) - return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (draw_dialog(d) != 0) return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) - return (BSDDIALOG_ERROR); + b->w = WBAR(d); + update_barbox(d, b, true); + b->toupdate = true; - doupdate(); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN); + return (0); +} - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3, - sizebar + 2, RAISED); +int +bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *seconds) +{ + bool loop; + int retval, tout; + wint_t input; + struct bar b; + struct dialog d; + + CHECK_PTR(seconds); + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%d"; + if (pause_redraw(&d, &b) != 0) + return (BSDDIALOG_ERROR); - tout = sec; + tout = *seconds; nodelay(stdscr, TRUE); timeout(1000); - loop = buttupdate = barupdate = true; + loop = true; while (loop) { - if (barupdate) { - perc = (float)tout * 100 / sec; - draw_bar(bar, 1, 1, sizebar, perc, true, tout); - barupdate = false; - wrefresh(bar); - } - - if (buttupdate) { - draw_buttons(widget, bs, true); - wrefresh(widget); - buttupdate = false; + if (b.toupdate) { + b.perc = (float)tout * 100 / *seconds; + b.label = tout; + draw_bar(&b); } - - input = getch(); - if (input < 0) { /* timeout */ + doupdate(); + if (get_wch(&input) == ERR) { /* timeout */ tout--; if (tout < 0) { - output = BSDDIALOG_TIMEOUT; + retval = BSDDIALOG_TIMEOUT; break; } else { - barupdate = true; + b.toupdate = true; continue; } } switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttupdate = true; + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, - &bs) != 0) - return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (pause_redraw(&d, &b) != 0) return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget,y, x, h, w, - textpad, text, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (pause_redraw(&d, &b) != 0) return (BSDDIALOG_ERROR); - - doupdate(); - - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - wclear(bar); - mvwin(bar, y + h - 6, x + 1 + BARPADDING); - wresize(bar, 3, sizebar + 2); - draw_borders(conf, bar, 3, sizebar+2, LOWERED); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, - x+w-1-TEXTHMARGIN); - - barupdate = true; break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - nodelay(stdscr, FALSE); - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + *seconds = MAX(tout, 0); - return (output); -}
\ No newline at end of file + delwin(b.win); + end_dialog(&d); + + return (retval); +} diff --git a/contrib/bsddialog/lib/bsddialog.3 b/contrib/bsddialog/lib/bsddialog.3 index 38500b4da6ca..cbf1653a2aca 100644 --- a/contrib/bsddialog/lib/bsddialog.3 +++ b/contrib/bsddialog/lib/bsddialog.3 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 2021-2022 Alfonso Sabato Siciliano +.\" Copyright (c) 2021-2024 Alfonso Sabato Siciliano .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -22,13 +22,15 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 9, 2022 +.Dd March 16, 2024 .Dt BSDDIALOG 3 .Os .Sh NAME .Nm bsddialog_backtitle , -.Nm bsddialog_clearterminal , +.Nm bsddialog_calendar , +.Nm bsddialog_clear , .Nm bsddialog_color , +.Nm bsddialog_color_attrs , .Nm bsddialog_checklist , .Nm bsddialog_datebox , .Nm bsddialog_end , @@ -36,9 +38,12 @@ .Nm bsddialog_gauge , .Nm bsddialog_geterror , .Nm bsddialog_get_theme , +.Nm bsddialog_hascolors , .Nm bsddialog_infobox , .Nm bsddialog_init , +.Nm bsddialog_init_notheme , .Nm bsddialog_initconf , +.Nm bsddialog_inmode , .Nm bsddialog_menu , .Nm bsddialog_mixedgauge , .Nm bsddialog_mixedlist , @@ -46,6 +51,7 @@ .Nm bsddialog_pause , .Nm bsddialog_radiolist , .Nm bsddialog_rangebox , +.Nm bsddialog_refresh , .Nm bsddialog_set_theme , .Nm bsddialog_set_default_theme , .Nm bsddialog_textbox , @@ -59,6 +65,16 @@ .Ft int .Fn bsddialog_backtitle "struct bsddialog_conf *conf" "const char *backtitle" .Ft int +.Fo bsddialog_calendar +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int *year" +.Fa "unsigned int *month" +.Fa "unsigned int *day" +.Fc +.Ft int .Fo bsddialog_checklist .Fa "struct bsddialog_conf *conf" .Fa "const char *text" @@ -69,17 +85,17 @@ .Fa "struct bsddialog_menuitem *items" .Fa "int *focusitem" .Fc +.Ft void +.Fn bsddialog_clear "unsigned int y" .Ft int -.Fn bsddialog_clearterminal "void" -.Ft int -.Fo bsddialog_datebox" +.Fo bsddialog_datebox .Fa "struct bsddialog_conf *conf" .Fa "const char *text" .Fa "int rows" .Fa "int cols" -.Fa "unsigned int *yy" -.Fa "unsigned int *mm" -.Fa "unsigned int *dd" +.Fa "unsigned int *year" +.Fa "unsigned int *month" +.Fa "unsigned int *day" .Fc .Ft int .Fn bsddialog_end "void" @@ -92,6 +108,7 @@ .Fa "unsigned int formrows" .Fa "unsigned int nitems" .Fa "struct bsddialog_formitem *items" +.Fa "int *focusitem" .Fc .Ft int .Fo bsddialog_gauge @@ -102,6 +119,7 @@ .Fa "unsigned int perc" .Fa "int fd" .Fa "const char *sep" +.Fa "const char *end" .Fc .Ft const char * .Fn bsddialog_geterror "void" @@ -115,6 +133,10 @@ .Ft int .Fn bsddialog_init "void" .Ft int +.Fn bsddialog_init_notheme "void" +.Ft bool +.Fn bsddialog_inmode "void" +.Ft int .Fn bsddialog_initconf "struct bsddialog_conf *conf" .Ft int .Fo bsddialog_menu @@ -163,7 +185,7 @@ .Fa "const char *text" .Fa "int rows" .Fa "int cols" -.Fa "unsigned int seconds" +.Fa "unsigned int *seconds" .Fc .Ft int .Fo bsddialog_radiolist @@ -186,6 +208,8 @@ .Fa "int max" .Fa "int *value" .Fc +.Ft void +.Fn bsddialog_refresh "void" .Ft int .Fo bsddialog_textbox .Fa "struct bsddialog_conf *conf" @@ -218,7 +242,16 @@ .Fa "unsigned int flags" .Fc .Ft int +.Fo bsddialog_color_attrs +.Fa "int color" +.Fa "enum bsddialog_color *foreground" +.Fa "enum bsddialog_color *background" +.Fa "unsigned int *flags" +.Fc +.Ft int .Fn bsddialog_get_theme "struct bsddialog_theme *theme" +.Ft bool +.Fn bsddialog_hascolors "void" .Ft int .Fn bsddialog_set_default_theme "enum bsddialog_default_theme theme" .Ft int @@ -226,8 +259,7 @@ .Sh DESCRIPTION The .Nm bsddialog -library provides an API to build Text User Interface dialogs and widgets: to -display messages, to get input and to inform about a computation status. +library provides an API to build Text User Interface dialogs and widgets. .Pp .Fn bsddialog_init initializes the library, the only functions that can be called before is @@ -235,53 +267,80 @@ initializes the library, the only functions that can be called before is described later. After the initialization the input and output should be handled via the library API. +.Pp +.Fn bsddialog_init_notheme +is equivalent to +.Fn bsddialog_init +except it does not set the default graphical theme; see +.Sx Theme +subsection to set a theme explicitly. +.Pp .Fn bsddialog_end restores the screen like before -.Fn bsddialog_init , -then it is not possible to use the library functions. +.Fn bsddialog_init . +After the call is not possible to use the library functions. +.Pp +.Fn bsddialog_inmode +returns +.Dv true +after +.Fn bsddialog_init +or +.Fn bsddialog_init_notheme +and before +.Fn bsddialog_end , +.Dv false +otherwise. .Pp -.Fn bsddialog_error -returns a string to describe the last error, it should be called after a -.Dv BSDDIALOG_ERROR -returned value. -.Fn bsddialog_clearterminal -clears the screen. .Fn bsddialog_backtitle prints .Fa backtitle -on the top of the screen, it is possible to set +on the top of the screen. +The function handles .Fa conf.ascii_lines and -.Fa conf.no_lines ; -.Fa conf -is described later. +.Fa conf.no_lines +described later. .Pp -Each -.Fa char* -argument has to be a well terminated string, can be empty -.Pq Dq -but not -.Dv NULL . +.Fn bsddialog_error +returns a string to describe the last error. +The function should be called after a +.Dv BSDDIALOG_ERROR +returned value. +.Pp +.Fn bsddialog_clear +clears the screen from +.Fa y . +.Pp +.Fn bsddialog_refresh +useful to refresh the screen after a terminal mode change, see +.Xr terminfo 5 . .Ss Dialogs The dialogs have common arguments. .Fa text is a string printed inside the dialog. +Each +.Fa char* +parameter can be a multibyte character string depending on current locale, see +.Xr setlocale 3 . .Fa rows and .Fa cols -are height and width, their value can be between 2 and the screen size, +are height and width, their value can be a fixed size, .Dv BSDDIALOG_AUTOSIZE or .Dv BSDDIALOG_FULLSCREEN . .Fa conf -is a struct to customize the dialog, it does not set global properties to the -library. +is a struct to customize the current dialog, it does not set global properties +to the library. .Pp .Bd -literal -offset indent -compact struct bsddialog_conf { bool ascii_lines; unsigned int auto_minheight; unsigned int auto_minwidth; + unsigned int auto_topmargin; + unsigned int auto_downmargin; const char *bottomtitle; bool clear; int *get_height; @@ -298,22 +357,29 @@ struct bsddialog_conf { const char *f1_message; } key; struct { - bool highlight; + unsigned int cols_per_row; + bool escape; unsigned int tablen; } text; struct { bool align_left; bool no_desc; bool no_name; - bool on_without_ok; bool shortcut_buttons; } menu; struct { - bool enable_wchar; - int securech; - bool value_without_ok; + char securech; + char *securembch; + bool value_wchar; } form; struct { + const char *format; + } date; + struct { + bool always_active; + const char *left1_label; + const char *left2_label; + const char *left3_label; bool without_ok; const char *ok_label; bool with_extra; @@ -323,8 +389,9 @@ struct bsddialog_conf { bool default_cancel; bool with_help; const char *help_label; - const char *generic1_label; - const char *generic2_label; + const char *right1_label; + const char *right2_label; + const char *right3_label; const char *default_label; } button; }; @@ -343,8 +410,25 @@ minimum width if .Fa cols is .Dv BSDDIALOG_AUTOSIZE . +.It Fa conf.auto_topmargin +top margin if +.Fa rows +is +.Dv BSDDIALOG_AUTOSIZE +or +.Dv BSDDIALOG_FULLSCREEN , +.Fa conf.y +has to be +.Dv BSDDIALOG_CENTER . +.It Fa conf.auto_downmargin +down margin if +.Fa rows +is +.Dv BSDDIALOG_AUTOSIZE +or +.Dv BSDDIALOG_FULLSCREEN . .It Fa conf.bottomtitle -subtitle at the dialog bottom side. +dialog subtitle. .It Fa conf.clear hide the dialog at exit. .It Fa conf.get_height @@ -362,31 +446,35 @@ draw shadow. .It Fa conf.sleep wait before to return, the value is in seconds. .It Fa conf.title -title at the top dialog side. +dialog title. .It Fa conf.y -vertical position, 0 is top screen size, can be +dialog vertical position, 0 is top screen, can be .Dv BSDDIALOG_CENTER . .It Fa conf.x -horizontal position, 0 is left screen side, can be +dialog horizontal position, 0 is left screen, can be .Dv BSDDIALOG_CENTER . .El .Pp .Bl -column -compact .It Fa conf.key.enable_esc -enables +enable .Dv ESC key to close the dialog. .It Fa conf.key.f1_file -file to open if F1 is pressed. +open a file in a textbox if F1 is pressed. .It Fa conf.key.f1_message -message to display if F1 is pressed. +build a msgbox with message if F1 is pressed. .El .Pp -.Fa conf.text.highlight -enables highlights for -.Fa text , -properly the following sequences are considered escapes: .Bl -column -compact +.It Fa conf.text.cols_per_row +Try to set the number of columns for a row of +.Fa text +with autosizing, default +.Dv 10 . +.It Fa conf.text.escape +enable escapes in +.Fa text : .It Dq \eZ0 black. .It Dq \eZ1 @@ -403,25 +491,55 @@ magenta. cyan. .It Dq \eZ7 white. -.It Dq \eZr -reverse colors between foreground and background. -.It Dq \eZR -disable reverse. .It Dq \eZb bold. .It Dq \eZB disable bold. +.It Dq \eZd +Half bright. +.It Dq \eZD +disable half bright. +.It Dq \eZk +Blink. +.It Dq \eZK +disable blinking. +.It Dq \eZr +reverse foreground and background. +.It Dq \eZR +disable reverse. +.It Dq \eZs +Highlight. +.It Dq \eZS +disable highlighting. .It Dq \eZu underline. .It Dq \eZU disable underline. .It Dq \eZn disable each customization. +.It Fa conf.text.tablen +tab length for +.Fa text +argument and +.Fn bsddialog_textbox +function. .El -.Fa conf.text.tablen -tab length. .Pp .Bl -column -compact +.It Fa conf.button.always_active +buttons always active, avoiding focus switch between buttons and input fields or +input boxes in +.Fn bsddialog_form , +.Fn bsddialog_datebox , +.Fn bsddialog_calendar +and +.Fn bsddialog_timebox . +.It Fa conf.button.left1_label +add a button with the specified label. +.It Fa conf.button.left2_label +add a button with the specified label. +.It Fa conf.button.left3_label +add a button with the specified label. .It Fa conf.button.without_ok disable OK button. .It Fa conf.button.ok_label @@ -440,9 +558,11 @@ on startup focus on the Cancel button. add Help button. .It Fa conf.button.help_label set a label for Help button. -.It Fa conf.button.generic1_label +.It Fa conf.button.right1_label +add a button with the specified label. +.It Fa conf.button.right2_label add a button with the specified label. -.It Fa conf.button.generic2_label +.It Fa conf.button.right3_label add a button with the specified label. .It Fa conf.button.default_label focus on the button with the specified label. @@ -458,148 +578,58 @@ to true, and .Fa conf.x to -.Dv BSDDIALOG_CENTER . -.Pp -.Fn bsddialog_infobox -builds a dialog without buttons and returns instantly. -.Fn bsddialog_msgbox -builds a dialog with OK button. -.Fn bsddialog_yesno -provides a dialog for a -.Dq Yes-No Question , -the labels on buttons are Yes and No. -.Pp -.Fn bsddialog_pause -builds a dialog waiting until the timeout in -.Fa seconds -expires or a button is pressed. +.Dv BSDDIALOG_CENTER , +.Fa conf.text.cols_per_row +to +.Dv 10 . .Pp -.Fn bsddialog_datebox -builds a dialog to select a date, -.Fa yy , -.Fa mm , +.Fn bsddialog_calendar +builds a dialog to select a date. +.Fa year , +.Fa month , and -.Fa dd +.Fa day are default values on startup, selected date at exit. -.Fn bsddialog_timebox -builds a dialog to choose a time, -.Fa hh , -.Fa mm , -and -.Fa ss -are default values on startup, selected time at exit. -.Pp -.Fn bsddialog_checklist , -.Fn bsddialog_menu -and -.Fn bsddialog_radiolist -build dialogs to select some item from a list via the SPACE key, an item is -defined like: -.Pp -.Bd -literal -offset indent -compact -struct bsddialog_menuitem { - const char *prefix; - bool on; - unsigned int depth; - const char *name; - const char *desc; - const char *bottomdesc; -}; -.Ed -.Pp -.Fa prefix , -.Fa name -and -.Fa desc -are strings to describe the item and are printed on its row, -.Fa bottomdesc -is printed on the bottom side of the screen, -.Fa depth -is a margin between the -.Fa prefix -and -.Fa name -useful to implement a -.Dq treeview, -.Fa on -is set to -.Dv true -if the item is selected, -.Dv false -otherwise. -.Fa items -is an array of items of -.Fa nitem -elements, -.Fa menurows -specifies the graphical fixed height of the list, if -.Fa cols -is set to -.Dv BSDDIALOG_AUTOSIZE -.Fa menurows -specifies a maximum value. -Finally, if not -.Dv NULL , -.Fa focusitem -specifies the default item on startup and the last focused item at exit, could -be a negative value if no item is focused. .Pp -.Fn bsddialog_mixedlist -builds a dialog with collections of checklists, radiolists and separators. -A collection is a set defined like: +.Fn bsddialog_checklist +builds dialogs to select some item from a list via the SPACE key, can be +customized by +.Fa conf.menu.* . +See +.Fn bsddialog_menu . .Pp -.Bd -literal -offset indent -compact -enum bsddialog_grouptype { - BSDDIALOG_CHECKLIST, - BSDDIALOG_RADIOLIST, - BSDDIALOG_SEPARATOR, -}; - -struct bsddialog_menugroup { - enum bsddialog_grouptype type; - unsigned int nitems; - struct bsddialog_menuitem *items; -}; -.Ed -.Pp -.Fa groups -is an array of sets of -.Fa ngroups -elements. -.Fa menurows -is the graphical height size for the list. -If not -.Dv NULL , -.Fa focuslist -and -.Fa focusitem -specify the default item on startup and the last focused item at exit, could be -a negative value if no item is focused. -.Pp -.Fn bsddialog_checklist , -.Fn bsddialog_menu , -.Fn bsddialog_mixedlist +.Fn bsddialog_datebox +builds a dialog to select a date. +.Fa year , +.Fa month , and -.Fn bsddialog_radiolist -can be costomizated by: +.Fa day +are default values on startup, selected date at exit. +The function can be customized by: .Bl -column -compact -.It Fa conf.menu.align_left -aligns items to left, default center. -.It Fa conf.menu.no_desc -hide description. -.It Fa conf.menu.no_name -hide names. -.It Fa conf.menu.on_without_ok -set items -.Fa on -also if the OK button is not pressed. -.It Fa conf.menu.shortcut_buttons -enable shortcut keys on buttons, default on items. +.It Fa conf.date.format +date format user interface, possible values: +.Dq d/m/y , +.Dq m/d/y , +.Dq y/m/d . .El .Pp .Fn bsddialog_form -builds a dialog to display a list of items to get strings in input, an item is -defined like: +builds a dialog to display an array of +.Fa items +of +.Fa nitems +elements to get input strings. +.Fa formrows +is the graphical height for the items inside the dialog, +.Dv 0 +for autosizing. +If not +.Dv NULL +.Fa focusitem +is the default item index on startup and the last focused item at exit, a +negative value if no item is focused. +An item is defined like: .Pp .Bd -literal -offset indent -compact struct bsddialog_formitem { @@ -621,7 +651,7 @@ struct bsddialog_formitem { .Ed .Pp .Fa label -describes the request, it is printed at the position +is a string to describe the request at the position .Fa ylabel and .Fa xlabel . @@ -632,77 +662,142 @@ and .Fa fieldlen is its graphical width, while .Fa maxvalelen -is the maximum length of the input string, +is the maximum number of characters of the input string. .Fa init -is the default value. -If the OK button is pressed +is the default field value. +If no error occurs .Fa value -is the allocated memory with the current field string. +is the allocated memory with the current field string at exit, its size depends +on the current locale. .Fa flags -is an OR value to set the -.Dv BSDDIALOG_FIELDHIDDEN -and -.Dv BSDDIALOG_FIELDREADONLY -flags for the field. +is an OR value to set the field: +.Dv BSDDIALOG_FIELDHIDDEN , +.Dv BSDDIALOG_FIELDREADONLY , +.Dv BSDDIALOG_FIELDNOCOLOR , +.Dv BSDDIALOG_FIELDCURSOREND , +.Dv BSDDIALOG_FIELDEXTEND , +.Dv BSDDIALOG_FIELDSINGLEBYTE . .Fa bottomdesc -is printed on the bottom side of the screen if the item is focused. -.Fa items -is an array of items of -.Fa nitems -elements, -.Fa formrows -specifies the graphical fixed height for the items list; -.Fa ylabel -and -.Fa yfield -have to be between 1 and -.Fa formrows . +is printed at bottom screen if the item is focused. .Pp .Fn bsddialog_form can be customized by: .Bl -column -compact -.It Fa conf.form.enable_wchar -enables characters greater than 127 in the field, -.Fa value -is a pointer to allocated memory for a -.Em wchar_t -string. .It Fa conf.form.securech -charachter to hide the input -with +charachter to hide the input with .Dv BSDDIALOG_FIELDHIDDEN . -.It Fa conf.form.value_without_ok -allocate memory and set +.It Fa conf.form.securembch +multibyte charachter to hide the input with +.Dv BSDDIALOG_FIELDHIDDEN , +.Fa conf.form.securech +is ignored. +.It Fa conf.form.value_wchar +the allocated .Fa value -also if the OK button is not pressed. +is a +.Em wchar_t* +string. .El .Pp .Fn bsddialog_gauge -builds a dialog with a bar to shows -.Fa perc , -if the file descriptor +builds a dialog with a bar to show +.Fa perc . +If the file descriptor .Fa fd is greater or equal to 0 the dialog waits to read -.Fa separator +.Fa sep from it, then the first string replaces .Fa perc and the following strings replace .Fa text until the next -.Fa separator , +.Fa sep , the loop ends reading -.Dv EOF . +.Fa end . +.Pp +.Fn bsddialog_infobox +builds a dialog without buttons and returns instantly. +.Pp +.Fn bsddialog_menu +builds a dialog to select an item from a list via SPACE and ENTER. +An item is defined like: +.Pp +.Bd -literal -offset indent -compact +struct bsddialog_menuitem { + const char *prefix; + bool on; + unsigned int depth; + const char *name; + const char *desc; + const char *bottomdesc; +}; +.Ed +.Pp +.Fa prefix , +.Fa name +and +.Fa desc +are printed at the item row. +.Fa bottomdesc +is printed at bottom screen if the item is focused. +.Fa depth +is a margin between +.Fa prefix +and +.Fa name . +At exit +.Fa on +is set to +.Dv true +if the item is selected, +.Dv false +otherwise. +.Fa items +is an array of items of +.Fa nitem +elements. +.Fa menurows +is the graphical height of the list inside the dialog, if +.Fa cols +is +.Dv BSDDIALOG_AUTOSIZE +.Fa menurows +specifies a maximum value. +if not +.Dv NULL +.Fa focusitem +is the default item index on startup and the last focused item at exit, a +negative value if no item is focused. +.Pp +.Fn bsddialog_checklist , +.Fn bsddialog_menu , +.Fn bsddialog_mixedlist +and +.Fn bsddialog_radiolist +can be customized by: +.Bl -column -compact +.It Fa conf.menu.align_left +align items to left, default center. +.It Fa conf.menu.no_desc +hide items description. +.It Fa conf.menu.no_name +hide items name, mutually exclusive with +.Fa conf.menu.no_desc . +.It Fa conf.menu.shortcut_buttons +enable shortcut keys on buttons, default on items. +.El .Pp .Fn bsddialog_mixedgauge -draws a main bar with the +builds a dialog with a main bar with the .Fa mainperc percentage and .Fa nminibars each one with a .Fa minilabel and a +.Fa miniperc . .Fa miniperc -with a value between 0 and 100 or +can be: a positive value to print a bar with a percentace, a negative constant .Dv BSDDIALOG_MG_SUCCEEDED , .Dv BSDDIALOG_MG_FAILED , .Dv BSDDIALOG_MG_PASSED , @@ -711,11 +806,69 @@ with a value between 0 and 100 or .Dv BSDDIALOG_MG_DONE , .Dv BSDDIALOG_MG_SKIPPED , .Dv BSDDIALOG_MG_INPROGRESS , -.Dv BSDDIALOG_MG_BLANK , -.Dv BSDDIALOG_MG_NA -or +.Dv BSDDIALOG_MG_BLANK +to hide +.Fa miniperc , +.Dv BSDDIALOG_MG_NA , .Dv BSDDIALOG_MG_PENDING -to print a descriptive string. +to print a descriptive string, otherwise +.Dq "UNKNOWN" +is printed. +.Pp +.Fn bsddialog_mixedlist +builds a dialog with collections of checklists, radiolists and separators. +A collection is a set defined like: +.Pp +.Bd -literal -offset indent -compact +enum bsddialog_menutype { + BSDDIALOG_CHECKLIST, + BSDDIALOG_RADIOLIST, + BSDDIALOG_SEPARATOR, +}; + +struct bsddialog_menugroup { + enum bsddialog_menutype type; + unsigned int nitems; + struct bsddialog_menuitem *items; + unsigned int min_on; /* unused for now */ +}; +.Ed +.Pp +.Fa groups +is an array of sets of +.Fa ngroups +elements. +.Fa menurows +is the graphical height size for the list. +If not +.Dv NULL , +.Fa focuslist +and +.Fa focusitem +specify the default item on startup and the last focused item at exit, could be +a negative value if no item is focused. +The dialog can be customized by +.Fa conf.menu.* , +see +.Fn bsddialog_menu . +.Pp +.Fn bsddialog_msgbox +builds a dialog with OK button. +.Pp +.Fn bsddialog_pause +builds a dialog waiting until the timeout in +.Fa seconds +expires or a button is pressed. +At exit +.Fa seconds +is set like remaining time. +.Pp +.Fn bsddialog_radiolist +builds dialogs to select at most an item from a list via the SPACE key, can be +customized by +.Fa conf.menu.* . +See +.Fn bsddialog_menu . .Pp .Fn bsddialog_rangebox to select a value between @@ -724,17 +877,49 @@ and .Fa max . .Fa value is the default value on startup and the selected value at exit. -The current value is printed inside a bar, the keys UP, DOWN, HOME, END, PAGEUP -and PAGEDOWN can change it. +The current value is printed inside a bar. .Pp .Fn bsddialog_textbox opens and prints -.Fa file -in a dialog, the UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to -navigate the file. -OK button is renamed EXIT. +.Fa file . +TAB key changes button. +Extra keys 0, h, l, k, j are available to navigate the text. +.Dq OK +button is renamed +.Dq EXIT . +.Pp +.Fn bsddialog_timebox +builds a dialog to choose a time. +.Fa hh , +.Fa mm , +and +.Fa ss +are default values on startup, selected time at exit. +.Pp +.Fn bsddialog_yesno +provides a dialog for a +.Dq Yes-No Question , +the labels on buttons are +.Dq Yes +and +.Dq No . +.Ss Keys +.Bl -tag -width Ds +.It Ctrl-l +Redraw the dialog. +.It F1 +Refer to +.Fa conf.key.f1_file +and +.Fa conf.key.f1_message . +.It SPACE +Select menu item. +.It UP DOWN LEFT RIGHT - + HOME END PAGEUP PAGEDOWN Ctrl-p Ctrl-n TAB +Navigate elements and set value, depending on the dialog. +.El .Ss Theme -The graphical properties are global to the library, they are represented by +The graphical properties are global to the library. +They are represented by .Fa struct bsddialog_theme and can be customized at runtime via the .In bsddialog_theme.h @@ -747,8 +932,8 @@ struct bsddialog_theme { } screen; struct { int color; - unsigned int h; - unsigned int w; + unsigned int y; + unsigned int x; } shadow; struct { int color; @@ -760,56 +945,56 @@ struct bsddialog_theme { int arrowcolor; } dialog; struct { + int f_prefixcolor; + int prefixcolor; int f_selectorcolor; int selectorcolor; int f_namecolor; int namecolor; int f_desccolor; int desccolor; - int namesepcolor; - int descsepcolor; int f_shortcutcolor; int shortcutcolor; + int bottomdesccolor; + int sepnamecolor; + int sepdesccolor; } menu; struct { int f_fieldcolor; int fieldcolor; int readonlycolor; + int bottomdesccolor; } form; struct { int f_color; int color; } bar; struct { - unsigned int hmargin; - int leftdelim; - int rightdelim; - int delimcolor; + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; int f_delimcolor; - int color; + int delimcolor; int f_color; - int shortcutcolor; + int color; int f_shortcutcolor; + int shortcutcolor; } button; }; .Ed .Pp A member with the .Dq f_ -prefix refers to an element with focus. +refers to focus when an element can be in selected or not selected state. .Pp -.Fn bsddialog_get_theme -sets -.Fa theme -like the current theme. -.Pp -A color can be set by the value returned by -.Fn bsddialog_color , -Possible values for -.Fa background -and -.Fa foreground -are: +.Fn bsddialog_color +generates and returns a color to set a +.Fa struct bsddialog_theme +color member. +An +.Fa enum bsddialog_color +can be: .Dv BSDDIALOG_BLACK , .Dv BSDDIALOG_RED , .Dv BSDDIALOG_GREEN , @@ -817,29 +1002,51 @@ are: .Dv BSDDIALOG_BLUE , .Dv BSDDIALOG_MAGENTA , .Dv BSDDIALOG_CYAN , -and -.Dv BSDDIALOG_WHITE , +.Dv BSDDIALOG_WHITE . .Fa flags -specifies OR-flags, possible values: +is an OR value: +.Dv BSDDIALOG_BLINK , .Dv BSDDIALOG_BOLD , -.Dv BSDDIALOG_REVERSE -and +.Dv BSDDIALOG_HALFBRIGHT , +.Dv BSDDIALOG_HIGHLIGHT , +.Dv BSDDIALOG_REVERSE , .Dv BSDDIALOG_UNDERLINE . .Pp +.Fn bsddialog_color_attrs +sets, if not NULL, +.Fa foreground , +.Fa background , +.Fa flags , +like the properties of +.Fa color , +see +.Fn bsddialog_color . +.Pp +.Fn bsddialog_get_theme +sets +.Fa theme +like the current runtime theme. +.Pp +.Fn bsddialog_hascolors +returns +.Dv true +if the terminal provides colors, +.Dv false +otherwise. +.Pp .Fn bsddialog_set_theme sets .Fa theme -like current theme, the changes takes effect only for dialogs built after the -call. +like current runtime theme. +Changes take effect only for dialogs built after +the call. .Pp -The library provides predefined themes: +.Fn bsddialog_set_default_theme +sets a library default theme like current theme, possible values: .Dv BSDDIALOG_THEME_BLACKWHITE , -.Dv BSDDIALOG_THEME_BSDDIALOG , -.Dv BSDDIALOG_THEME_FLAT -and -.Dv BSDDIALOG_THEME_DIALOG , -they can be set via -.Fn bsddialog_set_default_theme . +.Dv BSDDIALOG_THEME_FLAT , +.Dv BSDDIALOG_THEME_3D . +Changes take effect only for dialogs built after the call. .Sh RETURN VALUES The functions return the value .Dv BSDDIALOG_ERROR @@ -850,9 +1057,12 @@ returned: .Dv BSDDIALOG_CANCEL , .Dv BSDDIALOG_HELP , .Dv BSDDIALOG_EXTRA , -.Dv BSDDIALOG_GENERIC1 -or -.Dv BSDDIALOG_GENERIC2 . +.Dv BSDDIALOG_LEFT1 , +.Dv BSDDIALOG_LEFT2 , +.Dv BSDDIALOG_LEFT3 , +.Dv BSDDIALOG_RIGHT1 , +.Dv BSDDIALOG_RIGHT2 , +.Dv BSDDIALOG_RIGHT3 . .Dv BSDDIALOG_YES and .Dv BSDDIALOG_NO @@ -894,10 +1104,11 @@ case BSDDIALOG_YES: printf("Yes\\n"); break; case BSDDIALOG_NO - printf("NO\\n"); + printf("No\\n"); break; case BSDDIALOG_ERROR: printf("Error: %s\\n", bsddialog_geterror()); + break; } .Ed .Pp @@ -938,7 +1149,7 @@ struct bsddialog_menuitem check[2] = { struct bsddialog_menuitem sep[1] = { { "3", true, 0, "Radiolist", "(desc)", "" } }; -struct bsddialog_menuitem radio[5] = { +struct bsddialog_menuitem radio[2] = { { "4", true, 0, "Name 1", "Desc 1", "Radio Bottom Desc 1" }, { "5", false, 0, "Name 2", "Desc 2", "Radio Bottom Desc 2" } }; @@ -975,8 +1186,7 @@ for (i = 0; i < 3; i++) { .Ed .Sh SEE ALSO .Xr bsddialog 1 , -.Xr curses 3 , -.Xr ncurses 3 +.Xr curses 3 .Sh HISTORY The .Nm bsddialog @@ -985,8 +1195,4 @@ library first appeared in .Sh AUTHORS .Nm bsddialog was written by -.An Alfonso Sabato Siciliano Aq Mt alf.siciliano@gmail.com . -.Sh BUGS -.Fn bsddialog_form -does not resize the dialog after a terminal resize and does not provide -scrolling for items.
\ No newline at end of file +.An Alfonso Sabato Siciliano Aq Mt asiciliano@FreeBSD.org . diff --git a/contrib/bsddialog/lib/bsddialog.h b/contrib/bsddialog/lib/bsddialog.h index 37f9899141c0..fd0e2bc02580 100644 --- a/contrib/bsddialog/lib/bsddialog.h +++ b/contrib/bsddialog/lib/bsddialog.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,9 +30,9 @@ #include <stdbool.h> -#define LIBBSDDIALOG_VERSION "0.2" +#define LIBBSDDIALOG_VERSION "1.0.4" -/* Exit status */ +/* Return values */ #define BSDDIALOG_ERROR -1 #define BSDDIALOG_OK 0 #define BSDDIALOG_YES BSDDIALOG_OK @@ -42,8 +42,12 @@ #define BSDDIALOG_EXTRA 3 #define BSDDIALOG_TIMEOUT 4 #define BSDDIALOG_ESC 5 -#define BSDDIALOG_GENERIC1 6 -#define BSDDIALOG_GENERIC2 7 +#define BSDDIALOG_LEFT1 6 +#define BSDDIALOG_LEFT2 7 +#define BSDDIALOG_LEFT3 8 +#define BSDDIALOG_RIGHT1 9 +#define BSDDIALOG_RIGHT2 10 +#define BSDDIALOG_RIGHT3 11 /* Size and position */ #define BSDDIALOG_FULLSCREEN -1 @@ -64,13 +68,19 @@ #define BSDDIALOG_MG_PENDING -11 /* Form */ -#define BSDDIALOG_FIELDHIDDEN 1U -#define BSDDIALOG_FIELDREADONLY 2U +#define BSDDIALOG_FIELDHIDDEN 1U +#define BSDDIALOG_FIELDREADONLY 2U +#define BSDDIALOG_FIELDNOCOLOR 4U +#define BSDDIALOG_FIELDCURSOREND 8U +#define BSDDIALOG_FIELDEXTEND 16U +#define BSDDIALOG_FIELDSINGLEBYTE 32U struct bsddialog_conf { bool ascii_lines; unsigned int auto_minheight; unsigned int auto_minwidth; + unsigned int auto_topmargin; + unsigned int auto_downmargin; const char *bottomtitle; bool clear; int *get_height; @@ -87,22 +97,29 @@ struct bsddialog_conf { const char *f1_message; } key; struct { - bool highlight; + unsigned int cols_per_row; + bool escape; unsigned int tablen; } text; struct { bool align_left; bool no_desc; bool no_name; - bool on_without_ok; bool shortcut_buttons; } menu; struct { - bool enable_wchar; - int securech; - bool value_without_ok; + char securech; + char *securembch; + bool value_wchar; } form; struct { + const char *format; + } date; + struct { + bool always_active; + const char *left1_label; + const char *left2_label; + const char *left3_label; bool without_ok; const char *ok_label; bool with_extra; @@ -112,8 +129,9 @@ struct bsddialog_conf { bool default_cancel; bool with_help; const char *help_label; - const char *generic1_label; - const char *generic2_label; + const char *right1_label; + const char *right2_label; + const char *right3_label; const char *default_label; } button; }; @@ -127,16 +145,17 @@ struct bsddialog_menuitem { const char *bottomdesc; }; -enum bsddialog_grouptype { +enum bsddialog_menutype { BSDDIALOG_CHECKLIST, BSDDIALOG_RADIOLIST, BSDDIALOG_SEPARATOR, }; struct bsddialog_menugroup { - enum bsddialog_grouptype type; + enum bsddialog_menutype type; unsigned int nitems; struct bsddialog_menuitem *items; + unsigned int min_on; /* unused for now */ }; struct bsddialog_formitem { @@ -156,30 +175,37 @@ struct bsddialog_formitem { }; int bsddialog_init(void); +int bsddialog_init_notheme(void); +bool bsddialog_inmode(void); int bsddialog_end(void); int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle); int bsddialog_initconf(struct bsddialog_conf *conf); -int bsddialog_clearterminal(void); +void bsddialog_clear(unsigned int y); +void bsddialog_refresh(void); const char *bsddialog_geterror(void); /* Dialogs */ int +bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day); + +int bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem); int bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd); + int cols, unsigned int *year, unsigned int *month, unsigned int *day); int bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int formheight, unsigned int nitems, - struct bsddialog_formitem *items); + struct bsddialog_formitem *items, int *focusitem); int bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int perc, int fd, const char *sep); + int cols, unsigned int perc, int fd, const char *sep, const char *end); int bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, @@ -206,7 +232,7 @@ bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows, int bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int seconds); + int cols, unsigned int *seconds); int bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows, diff --git a/contrib/bsddialog/lib/bsddialog_progressview.h b/contrib/bsddialog/lib/bsddialog_progressview.h index 0cd9368a1040..5203b798bb07 100644 --- a/contrib/bsddialog/lib/bsddialog_progressview.h +++ b/contrib/bsddialog/lib/bsddialog_progressview.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 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 @@ -36,14 +36,14 @@ extern bool bsddialog_interruptprogview; extern bool bsddialog_abortprogview; -extern int bsddialog_total_progview; +extern long long int bsddialog_total_progview; struct bsddialog_fileminibar { const char *path; const char *label; int status; /* next if BSDDIALOG_MG_DONE or BSDDIALOG_MG_FAILED */ - long long size; - long long read; + long long int size; + long long int read; }; struct bsddialog_progviewconf { diff --git a/contrib/bsddialog/lib/bsddialog_theme.h b/contrib/bsddialog/lib/bsddialog_theme.h index 89381cfe28d5..2071896b61f0 100644 --- a/contrib/bsddialog/lib/bsddialog_theme.h +++ b/contrib/bsddialog/lib/bsddialog_theme.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 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 @@ -29,9 +29,12 @@ #define _LIBBSDDIALOG_THEME_H_ /* color flags */ -#define BSDDIALOG_BOLD 1U -#define BSDDIALOG_REVERSE 2U -#define BSDDIALOG_UNDERLINE 4U +#define BSDDIALOG_BLINK 1U +#define BSDDIALOG_BOLD 2U +#define BSDDIALOG_HALFBRIGHT 4U +#define BSDDIALOG_HIGHLIGHT 8U +#define BSDDIALOG_REVERSE 16U +#define BSDDIALOG_UNDERLINE 32U struct bsddialog_theme { struct { @@ -39,8 +42,8 @@ struct bsddialog_theme { } screen; struct { int color; - unsigned int h; - unsigned int w; + unsigned int y; + unsigned int x; } shadow; struct { int color; @@ -52,44 +55,48 @@ struct bsddialog_theme { int arrowcolor; } dialog; struct { + int f_prefixcolor; + int prefixcolor; int f_selectorcolor; int selectorcolor; int f_namecolor; int namecolor; int f_desccolor; int desccolor; - int namesepcolor; - int descsepcolor; int f_shortcutcolor; int shortcutcolor; + int bottomdesccolor; + int sepnamecolor; + int sepdesccolor; } menu; struct { int f_fieldcolor; int fieldcolor; int readonlycolor; + int bottomdesccolor; } form; struct { int f_color; int color; } bar; struct { - unsigned int hmargin; - int leftdelim; - int rightdelim; - int delimcolor; + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; int f_delimcolor; - int color; + int delimcolor; int f_color; - int shortcutcolor; + int color; int f_shortcutcolor; + int shortcutcolor; } button; }; enum bsddialog_default_theme { + BSDDIALOG_THEME_3D, BSDDIALOG_THEME_BLACKWHITE, - BSDDIALOG_THEME_BSDDIALOG, - BSDDIALOG_THEME_FLAT, - BSDDIALOG_THEME_DIALOG + BSDDIALOG_THEME_FLAT }; enum bsddialog_color { @@ -106,7 +113,11 @@ enum bsddialog_color { int bsddialog_color(enum bsddialog_color foreground, enum bsddialog_color background, unsigned int flags); +int +bsddialog_color_attrs(int color, enum bsddialog_color *foreground, + enum bsddialog_color *background, unsigned int *flags); int bsddialog_get_theme(struct bsddialog_theme *theme); +bool bsddialog_hascolors(void); int bsddialog_set_default_theme(enum bsddialog_default_theme theme); int bsddialog_set_theme(struct bsddialog_theme *theme); diff --git a/contrib/bsddialog/lib/datebox.c b/contrib/bsddialog/lib/datebox.c new file mode 100644 index 000000000000..ee955471799e --- /dev/null +++ b/contrib/bsddialog/lib/datebox.c @@ -0,0 +1,746 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022-2024 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 <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "bsddialog.h" +#include "bsddialog_theme.h" +#include "lib_util.h" + +/* Calendar */ +#define MIN_YEAR_CAL 0 +#define MAX_YEAR_CAL 999999999 +#define MINHCAL 13 +#define MINWCAL 36 /* 34 calendar, 1 + 1 margins */ +/* Datebox */ +#define MIN_YEAR_DATE 0 +#define MAX_YEAR_DATE 9999 +#define MINWDATE 23 /* 3 windows and their borders */ + +#define ISLEAP(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) + +static int minyear; +static int maxyear; + +static const char *m[12] = { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +}; + +enum operation { + UP_DAY, + DOWN_DAY, + LEFT_DAY, + RIGHT_DAY, + UP_MONTH, + DOWN_MONTH, + UP_YEAR, + DOWN_YEAR +}; + +/* private datebox item */ +struct dateitem { + enum operation up; + enum operation down; + WINDOW *win; + int width; + const char *fmt; + int *value; +}; + +static int month_days(int yy, int mm) +{ + int days; + + if (mm == 2) + days = ISLEAP(yy) ? 29 : 28; + else if (mm == 4 || mm == 6 || mm == 9 || mm == 11) + days = 30; + else + days = 31; + + return (days); +} + +static int week_day(int yy, int mm, int dd) +{ + int wd; + + dd += mm < 3 ? yy-- : yy - 2; + wd = 23*mm/9 + dd + 4 + yy/4 - yy/100 + yy/400; + wd %= 7; + + return (wd); +} + +static void +init_date(unsigned int *year, unsigned int *month, unsigned int *day, int *yy, + int *mm, int *dd) +{ + *yy = MIN(*year, (unsigned int)maxyear); + if (*yy < minyear) + *yy = minyear; + *mm = MIN(*month, 12); + if (*mm == 0) + *mm = 1; + *dd = (*day == 0) ? 1 : *day; + if (*dd > month_days(*yy, *mm)) + *dd = month_days(*yy, *mm); +} + +static void datectl(enum operation op, int *yy, int *mm, int *dd) +{ + int ndays; + + ndays = month_days(*yy, *mm); + + switch (op) { + case UP_DAY: + if (*dd > 7) + *dd -= 7; + else { + if (*mm == 1) { + *yy -= 1; + *mm = 12; + } else + *mm -= 1; + ndays = month_days(*yy, *mm); + *dd = ndays - abs(7 - *dd); + } + break; + case DOWN_DAY: + if (*dd + 7 < ndays) + *dd += 7; + else { + if (*mm == 12) { + *yy += 1; + *mm = 1; + } else + *mm += 1; + *dd = *dd + 7 - ndays; + } + break; + case LEFT_DAY: + if (*dd > 1) + *dd -= 1; + else { + if (*mm == 1) { + *yy -= 1; + *mm = 12; + } else + *mm -= 1; + *dd = month_days(*yy, *mm); + } + break; + case RIGHT_DAY: + if (*dd < ndays) + *dd += 1; + else { + if (*mm == 12) { + *yy += 1; + *mm = 1; + } else + *mm += 1; + *dd = 1; + } + break; + case UP_MONTH: + if (*mm == 1) { + *mm = 12; + *yy -= 1; + } else + *mm -= 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case DOWN_MONTH: + if (*mm == 12) { + *mm = 1; + *yy += 1; + } else + *mm += 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case UP_YEAR: + *yy -= 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case DOWN_YEAR: + *yy += 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + } + + if (*yy < minyear) { + *yy = minyear; + *mm = 1; + *dd = 1; + } + if (*yy > maxyear) { + *yy = maxyear; + *mm = 12; + *dd = 31; + } +} + +static void +drawsquare(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev, + const char *fmt, int value, bool focus) +{ + int h, l, w; + + getmaxyx(win, h, w); + draw_borders(conf, win, elev); + if (focus) { + l = 2 + w%2; + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, w/2 - l/2, UARROW(conf), l); + mvwhline(win, h-1, w/2 - l/2, DARROW(conf), l); + wattroff(win, t.dialog.arrowcolor); + } + + if (focus) + wattron(win, t.menu.f_namecolor); + if (strchr(fmt, 's') != NULL) + mvwprintw(win, 1, 1, fmt, m[value - 1]); + else + mvwprintw(win, 1, 1, fmt, value); + if (focus) + wattroff(win, t.menu.f_namecolor); + + wnoutrefresh(win); +} + +static void +print_calendar(struct bsddialog_conf *conf, WINDOW *win, int yy, int mm, int dd, + bool active) +{ + int ndays, i, y, x, wd, h, w; + + getmaxyx(win, h, w); + wclear(win); + draw_borders(conf, win, RAISED); + if (active) { + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, 15, UARROW(conf), 4); + mvwhline(win, h-1, 15, DARROW(conf), 4); + mvwvline(win, 3, 0, LARROW(conf), 3); + mvwvline(win, 3, w-1, RARROW(conf), 3); + wattroff(win, t.dialog.arrowcolor); + } + + mvwaddstr(win, 1, 5, "Sun Mon Tue Wed Thu Fri Sat"); + ndays = month_days(yy, mm); + y = 2; + wd = week_day(yy, mm, 1); + for (i = 1; i <= ndays; i++) { + x = 5 + (4 * wd); /* x has to be 6 with week number */ + wmove(win, y, x); + mvwprintw(win, y, x, "%2d", i); + if (i == dd) { + wattron(win, t.menu.f_namecolor); + mvwprintw(win, y, x, "%2d", i); + wattroff(win, t.menu.f_namecolor); + } + wd++; + if (wd > 6) { + wd = 0; + y++; + } + } + + wnoutrefresh(win); +} + +static int +calendar_redraw(struct dialog *d, WINDOW *yy_win, WINDOW *mm_win, + WINDOW *dd_win) +{ + int ycal, xcal; + + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, MINHCAL, MINWCAL, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, MINHCAL + HBUTTONS); + + ycal = d->y + d->h - 15; + xcal = d->x + d->w/2 - 17; + mvwaddstr(d->widget, d->h - 16, d->w/2 - 17, "Month"); + update_box(d->conf, mm_win, ycal, xcal, 3, 17, RAISED); + mvwaddstr(d->widget, d->h - 16, d->w/2, "Year"); + update_box(d->conf, yy_win, ycal, xcal + 17, 3, 17, RAISED); + update_box(d->conf, dd_win, ycal + 3, xcal, 9, 34, RAISED); + wnoutrefresh(d->widget); + + return (0); +} + +int +bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day) +{ + bool loop, focusbuttons; + int retval, sel, yy, mm, dd; + wint_t input; + WINDOW *yy_win, *mm_win, *dd_win; + struct dialog d; + + CHECK_PTR(year); + CHECK_PTR(month); + CHECK_PTR(day); + minyear = MIN_YEAR_CAL; + maxyear = MAX_YEAR_CAL; + init_date(year, month, day, &yy, &mm, &dd); + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((yy_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for yy"); + wbkgd(yy_win, t.dialog.color); + if ((mm_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for mm"); + wbkgd(mm_win, t.dialog.color); + if ((dd_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for dd"); + wbkgd(dd_win, t.dialog.color); + if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + + sel = -1; + loop = focusbuttons = true; + while (loop) { + drawsquare(conf, mm_win, RAISED, "%15s", mm, sel == 0); + drawsquare(conf, yy_win, RAISED, "%15d", yy, sel == 1); + print_calendar(conf, dd_win, yy, mm, dd, sel == 2); + doupdate(); + + if (get_wch(&input) == ERR) + continue; + switch(input) { + case KEY_ENTER: + case 10: /* Enter */ + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } + break; + case 27: /* Esc */ + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } + break; + case '\t': /* TAB */ + if (focusbuttons) { + d.bs.curr++; + if (d.bs.curr >= (int)d.bs.nbuttons) { + focusbuttons = false; + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel++; + if (sel > 2) { + focusbuttons = true; + sel = -1; + d.bs.curr = 0; + } + } + DRAW_BUTTONS(d); + break; + case KEY_CTRL('n'): + case KEY_RIGHT: + if (focusbuttons) { + d.bs.curr++; + if (d.bs.curr >= (int)d.bs.nbuttons) { + focusbuttons = false; + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else if (sel == 2) { + datectl(RIGHT_DAY, &yy, &mm, &dd); + } else { /* Month or Year*/ + sel++; + } + DRAW_BUTTONS(d); + break; + case KEY_CTRL('p'): + case KEY_LEFT: + if (focusbuttons) { + d.bs.curr--; + if (d.bs.curr < 0) { + focusbuttons = false; + sel = 2; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else if (sel == 2) { + datectl(LEFT_DAY, &yy, &mm, &dd); + } else if (sel == 1) { + sel = 0; + } else { /* sel = 0, Month */ + focusbuttons = true; + sel = -1; + d.bs.curr = 0; + } + DRAW_BUTTONS(d); + break; + case KEY_UP: + if (focusbuttons) { + sel = 2; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); + } else if (sel == 0) { + datectl(UP_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(UP_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(UP_DAY, &yy, &mm, &dd); + } + break; + case KEY_DOWN: + if (focusbuttons) { + break; + } else if (sel == 0) { + datectl(DOWN_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(DOWN_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(DOWN_DAY, &yy, &mm, &dd); + } + break; + case '-': + if (focusbuttons) { + break; + } else if (sel == 0) { + datectl(UP_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(UP_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(LEFT_DAY, &yy, &mm, &dd); + } + break; + case '+': + if (focusbuttons) { + break; + } else if (sel == 0) { + datectl(DOWN_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(DOWN_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(RIGHT_DAY, &yy, &mm, &dd); + } + break; + case KEY_HOME: + datectl(UP_MONTH, &yy, &mm, &dd); + break; + case KEY_END: + datectl(DOWN_MONTH, &yy, &mm, &dd); + break; + case KEY_PPAGE: + datectl(UP_YEAR, &yy, &mm, &dd); + break; + case KEY_NPAGE: + datectl(DOWN_YEAR, &yy, &mm, &dd); + break; + case KEY_F(1): + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) + break; + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (calendar_redraw(&d, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + break; + default: + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; + } + } + } + + *year = yy; + *month = mm; + *day = dd; + + delwin(yy_win); + delwin(mm_win); + delwin(dd_win); + end_dialog(&d); + + return (retval); +} + +static int datebox_redraw(struct dialog *d, struct dateitem *di) +{ + int y, x; + + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, 3 /*windows*/, MINWDATE, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 3 /*windows*/ + HBUTTONS); + + y = d->y + d->h - 6; + x = (d->x + d->w / 2) - 11; + update_box(d->conf, di[0].win, y, x, 3, di[0].width, LOWERED); + mvwaddch(d->widget, d->h - 5, x - d->x + di[0].width, '/'); + x += di[0].width + 1; + update_box(d->conf, di[1].win, y, x , 3, di[1].width, LOWERED); + mvwaddch(d->widget, d->h - 5, x - d->x + di[1].width, '/'); + x += di[1].width + 1; + update_box(d->conf, di[2].win, y, x, 3, di[2].width, LOWERED); + wnoutrefresh(d->widget); + + return (0); +} + +static int +build_dateitem(const char *format, int *yy, int *mm, int *dd, + struct dateitem *dt) +{ + int i; + wchar_t *wformat; + struct dateitem init[3] = { + {UP_YEAR, DOWN_YEAR, NULL, 6, "%4d", yy}, + {UP_MONTH, DOWN_MONTH, NULL, 11, "%9s", mm}, + {LEFT_DAY, RIGHT_DAY, NULL, 4, "%02d", dd}, + }; + + for (i = 0; i < 3; i++) { + if ((init[i].win = newwin(1, 1, 1, 1)) == NULL) + RETURN_FMTERROR("Cannot build WINDOW dateitem[%d]", i); + wbkgd(init[i].win, t.dialog.color); + } + + if ((wformat = alloc_mbstows(CHECK_STR(format))) == NULL) + RETURN_ERROR("Cannot allocate conf.date.format in wchar_t*"); + if (format == NULL || wcscmp(wformat, L"d/m/y") == 0) { + dt[0] = init[2]; + dt[1] = init[1]; + dt[2] = init[0]; + } else if (wcscmp(wformat, L"m/d/y") == 0) { + dt[0] = init[1]; + dt[1] = init[2]; + dt[2] = init[0]; + } else if (wcscmp(wformat, L"y/m/d") == 0) { + dt[0] = init[0]; + dt[1] = init[1]; + dt[2] = init[2]; + } else + RETURN_FMTERROR("Invalid conf.date.format=\"%s\"", format); + free(wformat); + + return (0); +} + +int +bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day) +{ + bool loop, focusbuttons; + int retval, i, sel, yy, mm, dd; + wint_t input; + struct dateitem di[3]; + struct dialog d; + + CHECK_PTR(year); + CHECK_PTR(month); + CHECK_PTR(day); + minyear = MIN_YEAR_DATE; + maxyear = MAX_YEAR_DATE; + init_date(year, month, day, &yy, &mm, &dd); + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if (build_dateitem(conf->date.format, &yy, &mm, &dd, di) != 0) + return (BSDDIALOG_ERROR); + if (datebox_redraw(&d, di) != 0) + return (BSDDIALOG_ERROR); + + sel = -1; + loop = focusbuttons = true; + while (loop) { + for (i = 0; i < 3; i++) + drawsquare(conf, di[i].win, LOWERED, di[i].fmt, + *di[i].value, sel == i); + doupdate(); + + if (get_wch(&input) == ERR) + continue; + switch(input) { + case KEY_ENTER: + case 10: /* Enter */ + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } + break; + case 27: /* Esc */ + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } + break; + case '\t': /* TAB */ + case KEY_CTRL('n'): + case KEY_RIGHT: + if (focusbuttons) { + d.bs.curr++; + focusbuttons = d.bs.curr < (int)d.bs.nbuttons ? + true : false; + if (focusbuttons == false) { + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel++; + focusbuttons = sel > 2 ? true : false; + if (focusbuttons) { + d.bs.curr = 0; + } + } + DRAW_BUTTONS(d); + break; + case KEY_CTRL('p'): + case KEY_LEFT: + if (focusbuttons) { + d.bs.curr--; + focusbuttons = d.bs.curr < 0 ? false : true; + if (focusbuttons == false) { + sel = 2; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel--; + focusbuttons = sel < 0 ? true : false; + if (focusbuttons) + d.bs.curr = (int)d.bs.nbuttons - 1; + } + DRAW_BUTTONS(d); + break; + case '-': + if (focusbuttons == false) + datectl(di[sel].up, &yy, &mm, &dd); + break; + case KEY_UP: + if (focusbuttons) { + sel = 0; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); + } else { + datectl(di[sel].up, &yy, &mm, &dd); + } + break; + case '+': + case KEY_DOWN: + if (focusbuttons) + break; + datectl(di[sel].down, &yy, &mm, &dd); + break; + case KEY_F(1): + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) + break; + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (datebox_redraw(&d, di) != 0) + return (BSDDIALOG_ERROR); + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (datebox_redraw(&d, di) != 0) + return (BSDDIALOG_ERROR); + break; + default: + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; + } + } + } + + *year = yy; + *month = mm; + *day = dd; + + for (i = 0; i < 3 ; i++) + delwin(di[i].win); + end_dialog(&d); + + return (retval); +}
\ No newline at end of file diff --git a/contrib/bsddialog/lib/formbox.c b/contrib/bsddialog/lib/formbox.c index 564fa99d69a8..ca473356e350 100644 --- a/contrib/bsddialog/lib/formbox.c +++ b/contrib/bsddialog/lib/formbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,10 +25,8 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> -#include <form.h> +#include <curses.h> +#include <limits.h> #include <stdlib.h> #include <string.h> @@ -36,502 +34,891 @@ #include "bsddialog_theme.h" #include "lib_util.h" -#define ISFIELDHIDDEN(item) (item.flags & BSDDIALOG_FIELDHIDDEN) -#define ISFIELDREADONLY(item) (item.flags & BSDDIALOG_FIELDREADONLY) -#define REDRAWFORM 19860214 /* magic number */ - -/* field_userptr for private buffer and view options */ -struct myfield { - int buflen; - wchar_t *buf; - int pos; - int maxpos; - bool secure; - int securech; - const char *bottomdesc; +enum field_action { + MOVE_CURSOR_BEGIN, + MOVE_CURSOR_END, + MOVE_CURSOR_RIGHT, + MOVE_CURSOR_LEFT, + DEL_LETTER +}; + +struct privateitem { + const char *label; /* formitem.label */ + unsigned int ylabel; /* formitem.ylabel */ + unsigned int xlabel; /* formitem.xlabel */ + unsigned int yfield; /* formitem.yfield */ + unsigned int xfield; /* formitem.xfield */ + bool secure; /* formitem.flags & BSDDIALOG_FIELDHIDDEN */ + bool readonly; /* formitem.flags & BSDDIALOG_FIELDREADONLY */ + bool fieldnocolor; /* formitem.flags & BSDDIALOG_FIELDNOCOLOR */ + bool extendfield; /* formitem.flags & BSDDIALOG_FIELDEXTEND */ + bool fieldonebyte; /* formitem.flags & BSDDIALOG_FIELDSINGLEBYTE */ + bool cursorend; /* formitem.flags & BSDDIALOG_FIELDCURSOREND */ + bool cursor; /* field cursor visibility */ + const char *bottomdesc; /* formitem.bottomdesc */ + + wchar_t *privwbuf; /* formitem.value */ + wchar_t *pubwbuf; /* string for drawitem() */ + unsigned int maxletters; /* formitem.maxvaluelen, [priv|pub]wbuf size */ + unsigned int nletters; /* letters in privwbuf and pubwbuf */ + unsigned int pos; /* pos in privwbuf and pubwbuf */ + unsigned int fieldcols; /* formitem.fieldlen */ + unsigned int xcursor; /* position in fieldcols [0 - fieldcols-1] */ + unsigned int xposdraw; /* first pubwbuf index to draw */ +}; + +struct privateform { + WINDOW *box; /* window to draw borders */ + WINDOW *pad; + unsigned int h; /* only to create pad */ + unsigned int w; /* only to create pad */ + unsigned int wmin; /* to refresh, w can change for FIELDEXTEND */ + unsigned int ys; /* to refresh */ + unsigned int ye; /* to refresh */ + unsigned int xs; /* to refresh */ + unsigned int xe; /* to refresh */ + unsigned int y; /* changes moving focus around items */ + unsigned int formheight; /* API formheight */ + unsigned int viewrows; /* visible rows, real formheight */ + unsigned int minviewrows; /* min viewrows, ylabel != yfield */ + wchar_t securewch; /* wide char of conf.form.secure[mb]ch */ + unsigned int nitems; /* like API nitems */ + struct privateitem *pritems; + int sel; /* selected item in pritem, can be -1 */ + bool hasbottomdesc; /* some item has bottomdesc */ }; -#define GETMYFIELD(field) ((struct myfield*)field_userptr(field)) -#define GETMYFIELD2(form) ((struct myfield*)field_userptr(current_field(form))) -static void insertch(struct myfield *mf, int ch) +static int +build_privateform(struct bsddialog_conf*conf, unsigned int nitems, + struct bsddialog_formitem *items, struct privateform *f) { - int i; + bool insecurecursor; + int mbchsize; + unsigned int i, j, itemybeg, itemxbeg, tmp; + wchar_t *winit; + struct privateitem *item; + + /* checks */ + CHECK_ARRAY(nitems, items); + for (i = 0; i < nitems; i++) { + if (items[i].fieldlen == 0) + RETURN_FMTERROR("item %u [0-%u] fieldlen = 0", + i, nitems); + if (items[i].maxvaluelen == 0) + RETURN_FMTERROR("item %u [0-%u] maxvaluelen = 0", + i, nitems); + } + f->nitems = nitems; + + /* insecure ch */ + insecurecursor = false; + if (conf->form.securembch != NULL) { + mbchsize = mblen(conf->form.securembch, MB_LEN_MAX); + if (mbtowc(&f->securewch, conf->form.securembch, mbchsize) < 0) + RETURN_ERROR("Cannot convert securembch to wchar_t"); + insecurecursor = true; + } else if (conf->form.securech != '\0') { + f->securewch = btowc(conf->form.securech); + insecurecursor = true; + } else { + f->securewch = L' '; + } + + /* alloc and set private items */ + f->pritems = malloc(f->nitems * sizeof(struct privateitem)); + if (f->pritems == NULL) + RETURN_ERROR("Cannot allocate internal form.pritems"); + f->hasbottomdesc = false; + f->h = f->w = f->minviewrows = 0; + for (i = 0; i < f->nitems; i++) { + item = &f->pritems[i]; + item->label = CHECK_STR(items[i].label); + item->ylabel = items[i].ylabel; + item->xlabel = items[i].xlabel; + item->yfield = items[i].yfield; + item->xfield = items[i].xfield; + item->secure = items[i].flags & BSDDIALOG_FIELDHIDDEN; + item->readonly = items[i].flags & BSDDIALOG_FIELDREADONLY; + item->fieldnocolor = items[i].flags & BSDDIALOG_FIELDNOCOLOR; + item->extendfield = items[i].flags & BSDDIALOG_FIELDEXTEND; + item->fieldonebyte = items[i].flags & + BSDDIALOG_FIELDSINGLEBYTE; + item->cursorend = items[i].flags & BSDDIALOG_FIELDCURSOREND; + item->bottomdesc = CHECK_STR(items[i].bottomdesc); + if (items[i].bottomdesc != NULL) + f->hasbottomdesc = true; + if (item->readonly || (item->secure && !insecurecursor)) + item->cursor = false; + else + item->cursor = true; + + item->maxletters = items[i].maxvaluelen; + item->privwbuf = calloc(item->maxletters + 1, sizeof(wchar_t)); + if (item->privwbuf == NULL) + RETURN_ERROR("Cannot allocate item private buffer"); + memset(item->privwbuf, 0, item->maxletters + 1); + item->pubwbuf = calloc(item->maxletters + 1, sizeof(wchar_t)); + if (item->pubwbuf == NULL) + RETURN_ERROR("Cannot allocate item private buffer"); + memset(item->pubwbuf, 0, item->maxletters + 1); + + if ((winit = alloc_mbstows(CHECK_STR(items[i].init))) == NULL) + RETURN_ERROR("Cannot allocate item.init in wchar_t*"); + wcsncpy(item->privwbuf, winit, item->maxletters); + wcsncpy(item->pubwbuf, winit, item->maxletters); + free(winit); + item->nletters = wcslen(item->pubwbuf); + if (item->secure) { + for (j = 0; j < item->nletters; j++) + item->pubwbuf[j] = f->securewch; + } + + item->fieldcols = items[i].fieldlen; + item->xposdraw = 0; + item->xcursor = 0; + item->pos = 0; + + /* size and position */ + f->h = MAX(f->h, item->ylabel); + f->h = MAX(f->h, item->yfield); + f->w = MAX(f->w, item->xlabel + strcols(item->label)); + f->w = MAX(f->w, item->xfield + item->fieldcols); + if (i == 0) { + itemybeg = MIN(item->ylabel, item->yfield); + itemxbeg = MIN(item->xlabel, item->xfield); + } else { + tmp = MIN(item->ylabel, item->yfield); + itemybeg = MIN(itemybeg, tmp); + tmp = MIN(item->xlabel, item->xfield); + itemxbeg = MIN(itemxbeg, tmp); + } + tmp = abs((int)item->ylabel - (int)item->yfield); + f->minviewrows = MAX(f->minviewrows, tmp); + } + if (f->nitems > 0) { + f->h = f->h + 1 - itemybeg; + f->w -= itemxbeg; + f->minviewrows += 1; + } + f->wmin = f->w; + for (i = 0; i < f->nitems; i++) { + f->pritems[i].ylabel -= itemybeg; + f->pritems[i].yfield -= itemybeg; + f->pritems[i].xlabel -= itemxbeg; + f->pritems[i].xfield -= itemxbeg; + } + + return (0); +} + +static bool fieldctl(struct privateitem *item, enum field_action act) +{ + bool change; + int width, oldwidth, nextwidth, cols; + unsigned int i; + + change = false; + switch (act){ + case MOVE_CURSOR_BEGIN: + if (item->pos == 0 && item->xcursor == 0) + break; + /* here the cursor is changed */ + change = true; + item->pos = 0; + item->xcursor = 0; + item->xposdraw = 0; + break; + case MOVE_CURSOR_END: + while (fieldctl(item, MOVE_CURSOR_RIGHT)) + change = true; + break; + case MOVE_CURSOR_LEFT: + if (item->pos == 0) + break; + /* check redundant by item->pos == 0 because of 'while' below */ + if (item->xcursor == 0 && item->xposdraw == 0) + break; + /* here some letter to left */ + change = true; + item->pos -= 1; + width = wcwidth(item->pubwbuf[item->pos]); + if (((int)item->xcursor) - width < 0) { + item->xcursor = 0; + item->xposdraw -= 1; + } else + item->xcursor -= width; + + while (true) { + if (item->xposdraw == 0) + break; + if (item->xcursor >= item->fieldcols / 2) + break; + if (wcwidth(item->pubwbuf[item->xposdraw - 1]) + + item->xcursor + width > item->fieldcols) + break; - if (mf->buflen > mf->maxpos) - return; + item->xposdraw -= 1; + item->xcursor += + wcwidth(item->pubwbuf[item->xposdraw]); + } + break; + case DEL_LETTER: + if (item->nletters == 0) + break; + if (item->pos == item->nletters) + break; + /* here a letter under the cursor */ + change = true; + for (i = item->pos; i < item->nletters; i++) { + item->privwbuf[i] = item->privwbuf[i+1]; + item->pubwbuf[i] = item->pubwbuf[i+1]; + } + item->nletters -= 1; + item->privwbuf[i] = L'\0'; + item->pubwbuf[i] = L'\0'; + break; + case MOVE_CURSOR_RIGHT: /* used also by "insert", see handler loop */ + if (item->pos + 1 == item->maxletters) + break; + if (item->pos == item->nletters) + break; + /* here a change to right */ + change = true; + oldwidth = wcwidth(item->pubwbuf[item->pos]); + item->pos += 1; + if (item->pos == item->nletters) { /* empty column */ + nextwidth = 1; + } else { /* a letter to right */ + nextwidth = wcwidth(item->pubwbuf[item->pos]); + } + if (item->xcursor + oldwidth + nextwidth - 1 >= item->fieldcols) { + cols = nextwidth; + item->xposdraw = item->pos; + while (item->xposdraw != 0) { + cols += wcwidth(item->pubwbuf[item->xposdraw - 1]); + if (cols > (int)item->fieldcols) + break; + item->xposdraw -= 1; + } + item->xcursor = 0; + for (i = item->xposdraw; i < item->pos ; i++) + item->xcursor += wcwidth(item->pubwbuf[i]); + } + else { + item->xcursor += oldwidth; + } - for (i = mf->buflen; i >= mf->pos; i--) { - mf->buf[i+1] = mf->buf[i]; + break; } - mf->buf[mf->pos] = ch; - mf->pos += 1; - if (mf->pos > mf->maxpos) - mf->pos = mf->maxpos; - mf->buflen += 1; - mf->buf[mf->buflen] = '\0'; + return (change); } -static void shiftleft(struct myfield *mf) +static bool insertch(struct privateitem *item, wchar_t wch, wchar_t securewch) { - int i, last; + int i; + + if (item->nletters >= item->maxletters) + return (false); - for (i = mf->pos; i < mf->buflen -1; i++) { - mf->buf[i] = mf->buf[i+1]; + for (i = (int)item->nletters - 1; i >= (int)item->pos; i--) { + item->privwbuf[i+1] = item->privwbuf[i]; + item->pubwbuf[i+1] = item->pubwbuf[i]; } - last = mf->buflen > 0 ? mf->buflen -1 : 0; - mf->buf[last] = '\0'; - mf->buflen = last; + item->privwbuf[item->pos] = wch; + item->pubwbuf[item->pos] = item->secure ? securewch : wch; + item->nletters += 1; + item->privwbuf[item->nletters] = L'\0'; + item->pubwbuf[item->nletters] = L'\0'; + + return (true); } -static void print_bottomdesc(struct myfield *mf) +static char* alloc_wstomb(wchar_t *wstr) { - move(SCREENLINES - 1, 2); - clrtoeol(); - if (mf->bottomdesc != NULL) { - addstr(mf->bottomdesc); - refresh(); + int len, nbytes, i; + char mbch[MB_LEN_MAX], *mbstr; + + nbytes = MB_LEN_MAX; /* to ensure a null terminated string */ + len = wcslen(wstr); + for (i = 0; i < len; i++) { + wctomb(mbch, wstr[i]); + nbytes += mblen(mbch, MB_LEN_MAX); } + if ((mbstr = malloc(nbytes)) == NULL) + return (NULL); + + wcstombs(mbstr, wstr, nbytes); + + return (mbstr); } -static char *w2c(wchar_t *string) +static int +return_values(struct bsddialog_conf *conf, struct privateform *f, + struct bsddialog_formitem *items) { - int i, len; - char *value; + unsigned int i; - len = wcslen(string); - if ((value = calloc(len + 1, sizeof(char))) == NULL) - return NULL; + for (i = 0; i < f->nitems; i++) { + if (conf->form.value_wchar) + items[i].value = (char*)wcsdup(f->pritems[i].privwbuf); + else + items[i].value = alloc_wstomb(f->pritems[i].privwbuf); - for (i = 0; i < len; i++) - value[i] = string[i]; - value[i] = '\0'; + if (items[i].value == NULL) + RETURN_FMTERROR( + "Cannot allocate memory for item[%d].value", i); + } - return value; + return (0); } -static int -return_values(struct bsddialog_conf *conf, int output, int nitems, - struct bsddialog_formitem *items, FORM *form, FIELD **cfield) +static void set_first_with_default(struct privateform *f, int *focusitem) +{ + unsigned int i; + + f->sel = -1; + if (focusitem != NULL && *focusitem >=0 && *focusitem < (int)f->nitems) + if (f->pritems[*focusitem].readonly == false) { + f->sel = *focusitem; + return; + } + for (i = 0 ; i < f->nitems; i++) + if (f->pritems[i].readonly == false) { + f->sel = i; + break; + } +} + +static unsigned int firstitem(unsigned int nitems, struct privateitem *items) +{ + int i; + + for (i = 0; i < (int)nitems; i++) + if (items[i].readonly == false) + break; + + return (i); +} + +static unsigned int lastitem(unsigned int nitems, struct privateitem *items) +{ + int i; + + for (i = nitems - 1; i >= 0 ; i--) + if (items[i].readonly == false) + break; + + return (i); +} + +static unsigned int +previtem(unsigned int nitems, struct privateitem *items, int curritem) { int i; - struct myfield *mf; - if (output != BSDDIALOG_OK && conf->form.value_without_ok == false) - return (output); + for (i = curritem - 1; i >= 0; i--) + if (items[i].readonly == false) + return(i); - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_PREV_FIELD); - for (i = 0; i < nitems; i++) { - mf = GETMYFIELD(cfield[i]); - if (conf->form.enable_wchar) { - items[i].value = (char*)wcsdup(mf->buf); - } else { - items[i].value = w2c(mf->buf); + for (i = nitems - 1; i > curritem - 1; i--) + if (items[i].readonly == false) + return(i); + + return (curritem); +} + +static unsigned int +nextitem(unsigned int nitems, struct privateitem *items, int curritem) +{ + int i; + + for (i = curritem + 1; i < (int)nitems; i++) + if (items[i].readonly == false) + return(i); + + for (i = 0; i < curritem; i++) + if (items[i].readonly == false) + return(i); + + return (curritem); +} + +static void redrawbuttons(struct dialog *d, bool focus, bool shortcut) +{ + int selected; + + selected = d->bs.curr; + if (focus == false) + d->bs.curr = -1; + d->bs.shortcut = shortcut; + draw_buttons(d); + d->bs.curr = selected; +} + +static void +drawitem(struct privateform *f, int idx, bool focus) +{ + int color; + unsigned int n, cols; + struct privateitem *item; + + item = &f->pritems[idx]; + + /* Label */ + wattron(f->pad, t.dialog.color); + mvwaddstr(f->pad, item->ylabel, item->xlabel, item->label); + wattroff(f->pad, t.dialog.color); + + /* Field */ + if (item->readonly) + color = t.form.readonlycolor; + else if (item->fieldnocolor) + color = t.dialog.color; + else + color = focus ? t.form.f_fieldcolor : t.form.fieldcolor; + wattron(f->pad, color); + mvwhline(f->pad, item->yfield, item->xfield, ' ', item->fieldcols); + n = 0; + cols = wcwidth(item->pubwbuf[item->xposdraw]); + while (cols <= item->fieldcols && + item->xposdraw + n < wcslen(item->pubwbuf)) { + n++; + cols += wcwidth(item->pubwbuf[item->xposdraw + n]); + + } + mvwaddnwstr(f->pad, item->yfield, item->xfield, + &item->pubwbuf[item->xposdraw], n); + wattroff(f->pad, color); + + /* Bottom Desc */ + if (f->hasbottomdesc) { + move(SCREENLINES - 1, 2); + clrtoeol(); + if (item->bottomdesc != NULL && focus) { + attron(t.form.bottomdesccolor); + addstr(item->bottomdesc); + attroff(t.form.bottomdesccolor); + refresh(); } - if (items[i].value == NULL) - RETURN_ERROR("Cannot allocate memory for form value"); } - return (output); + /* Cursor */ + curs_set((focus && item->cursor) ? 1 : 0); + wmove(f->pad, item->yfield, item->xfield + item->xcursor); +} + +/* + * Trick: draw 2 times an item switching focus. + * Problem: curses tries to optimize the rendering but sometimes it misses some + * updates or draws old stuff. libformw has a similar problem fixed by the + * same trick. + * Case 1: KEY_DC and KEY_BACKSPACE, deleted multicolumn letters are drawn + * again. It seems fixed by new items pad and prefresh(), previously WINDOW. + * Case2: some terminal, tmux and ssh does not show the cursor. + */ +#define DRAWITEM_TRICK(f, idx, focus) do { \ + drawitem(f, idx, !focus); \ + prefresh((f)->pad, (f)->y, 0, (f)->ys, (f)->xs, (f)->ye, (f)->xe); \ + drawitem(f, idx, focus); \ + prefresh((f)->pad, (f)->y, 0, (f)->ys, (f)->xs, (f)->ye, (f)->xe); \ +} while (0) + +static void update_formbox(struct bsddialog_conf *conf, struct privateform *f) +{ + int h, w; + + getmaxyx(f->box, h, w); + draw_borders(conf, f->box, LOWERED); + + if (f->viewrows < f->h) { + wattron(f->box, t.dialog.arrowcolor); + if (f->y > 0) + mvwhline(f->box, 0, (w / 2) - 2, UARROW(conf), 5); + + if (f->y + f->viewrows < f->h) + mvwhline(f->box, h-1, (w / 2) - 2, DARROW(conf), 5); + wattroff(f->box, t.dialog.arrowcolor); + } +} + +static void curriteminview(struct privateform *f, struct privateitem *item) +{ + unsigned int yup, ydown; + + yup = MIN(item->ylabel, item->yfield); + ydown = MAX(item->ylabel, item->yfield); + + /* selected item in view */ + if (f->y > yup && f->y > 0) + f->y = yup; + if ((int)(f->y + f->viewrows) - 1 < (int)ydown) + f->y = ydown - f->viewrows + 1; + /* lower pad after a terminal expansion */ + if (f->y > 0 && (f->h - f->y) < f->viewrows) + f->y = f->h - f->viewrows; +} + +static int form_size_position(struct dialog *d, struct privateform *f) +{ + int htext, hform; + + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + + /* autosize */ + hform = (int) f->viewrows; + if (f->viewrows == BSDDIALOG_AUTOSIZE) + hform = MAX(f->h, f->minviewrows); + hform += 2; /* formborders */ + + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, &htext, &d->bs, hform, f->w + 4) != 0) + return (BSDDIALOG_ERROR); + /* formheight: avoid overflow, "at most" and at least minviewrows */ + if (d->h - BORDERS - htext - HBUTTONS < 2 + (int)f->minviewrows) { + f->viewrows = f->minviewrows; /* for widget_checksize() */ + } else if (f->viewrows == BSDDIALOG_AUTOSIZE) { + f->viewrows = MIN(d->h - BORDERS - htext - HBUTTONS, hform) - 2; + f->viewrows = MAX(f->viewrows, f->minviewrows); + } else { + f->viewrows = MIN(d->h - BORDERS - htext - HBUTTONS, hform) - 2; + } + + /* checksize */ + if (f->viewrows < f->minviewrows) + RETURN_FMTERROR("formheight, current: %u needed at least %u", + f->viewrows, f->minviewrows); + if (widget_checksize(d->h, d->w, &d->bs, + 2 /* borders */ + f->minviewrows, f->w + 4) != 0) + return (BSDDIALOG_ERROR); + + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); } static int -form_handler(struct bsddialog_conf *conf, WINDOW *widget, struct buttons bs, - WINDOW *formwin, FORM *form, FIELD **cfield, int nitems, - struct bsddialog_formitem *items) +form_redraw(struct dialog *d, struct privateform *f, bool focusinform) { - bool loop, buttupdate, informwin; - int i, chtype, output; + unsigned int i; + + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + f->viewrows = f->formheight; + f->w = f->wmin; + if (form_size_position(d, f) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 2 /* box borders */ + f->viewrows + HBUTTONS); + + update_box(d->conf, f->box, d->y + d->h - 5 - f->viewrows, d->x + 2, + f->viewrows + 2, d->w - 4, LOWERED); + + for (i = 0; i < f->nitems; i++) { + fieldctl(&f->pritems[i], MOVE_CURSOR_BEGIN); + if (f->pritems[i].extendfield) { + f->w = d->w - 6; + f->pritems[i].fieldcols = f->w - f->pritems[i].xfield; + } + if (f->pritems[i].cursorend) + fieldctl(&f->pritems[i], MOVE_CURSOR_END); + } + + wresize(f->pad, f->h, f->w); + for (i = 0; i < f->nitems; i++) + drawitem(f, i, false); + + f->ys = d->y + d->h - 5 - f->viewrows + 1; + f->ye = d->y + d->h - 5 ; + if ((int)f->w >= d->w - 6) { /* left */ + f->xs = d->x + 3; + f->xe = f->xs + d->w - 7; + } else { /* center */ + f->xs = d->x + 3 + (d->w - 6)/2 - f->w/2; + f->xe = f->xs + d->w - 5; + } + + if (f->sel != -1) { /* at least 1 writable item */ + redrawbuttons(d, + d->conf->button.always_active || !focusinform, + !focusinform); + wnoutrefresh(d->widget); + curriteminview(f, &f->pritems[f->sel]); + update_formbox(d->conf, f); + wnoutrefresh(f->box); + DRAWITEM_TRICK(f, f->sel, focusinform); + } else if (f->sel == -1 && f->nitems > 0) { /* all read only */ + redrawbuttons(d, true, true); + wnoutrefresh(d->widget); + update_formbox(d->conf, f); + wnoutrefresh(f->box); + DRAWITEM_TRICK(f, 0, false); /* to refresh pad*/ + } else { /* no item */ + wnoutrefresh(f->box); + } + + return (0); +} + +/* API */ +int +bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int formheight, unsigned int nitems, + struct bsddialog_formitem *items, int *focusitem) +{ + bool switchfocus, changeitem, focusinform, loop; + int next, retval, wchtype; + unsigned int i; wint_t input; - struct myfield *mf; + struct privateitem *item; + struct privateform form; + struct dialog d; + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + + if (build_privateform(conf, nitems, items, &form) != 0) + return (BSDDIALOG_ERROR); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - pos_form_cursor(form); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf->pos = MIN(mf->buflen, mf->maxpos); - curs_set(1); - informwin = true; + if ((form.box = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW form box"); + wbkgd(form.box, t.dialog.color); + if ((form.pad = newpad(1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW form pad"); + wbkgd(form.pad, t.dialog.color); + + set_first_with_default(&form, focusitem); + if (form.sel != -1) { + focusinform = true; + form.y = 0; + item = &form.pritems[form.sel]; + } else { + item = NULL; + focusinform = false; + } - bs.curr = -1; - buttupdate = true; + form.formheight = formheight; + if (form_redraw(&d, &form, focusinform) != 0) + return (BSDDIALOG_ERROR); + changeitem = switchfocus = false; loop = true; while (loop) { - if (buttupdate) { - draw_buttons(widget, bs, !informwin); - wrefresh(widget); - buttupdate = false; - } - wrefresh(formwin); - chtype = get_wch(&input); - if (chtype != KEY_CODE_YES && input > 127 && - conf->form.enable_wchar == false) + doupdate(); + if ((wchtype = get_wch(&input)) == ERR) continue; switch(input) { - case KEY_HOME: - case KEY_PPAGE: - case KEY_END: - case KEY_NPAGE: - /* disabled keys */ - break; case KEY_ENTER: case 10: /* Enter */ - if (informwin) + if (focusinform && conf->button.always_active == false) break; - output = return_values(conf, bs.value[bs.curr], nitems, - items, form, cfield); + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = return_values(conf, BSDDIALOG_ESC, - nitems, items, form, cfield); + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - if (informwin) { - bs.curr = 0; - informwin = false; - curs_set(0); + if (focusinform) { + switchfocus = true; } else { - bs.curr++; - informwin = bs.curr >= (int)bs.nbuttons ? - true : false; - if (informwin) { - curs_set(1); - pos_form_cursor(form); + if (d.bs.curr + 1 < (int)d.bs.nbuttons) { + d.bs.curr++; + } else { + d.bs.curr = 0; + if (form.sel != -1) { + switchfocus = true; + } } + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); } - buttupdate = true; break; case KEY_LEFT: - if (informwin) { - form_driver_w(form, KEY_CODE_YES, REQ_PREV_CHAR); - mf = GETMYFIELD2(form); - if (mf->pos > 0) - mf->pos -= 1; - } else { - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } + if (focusinform) { + if (fieldctl(item, MOVE_CURSOR_LEFT)) + DRAWITEM_TRICK(&form, form.sel, true); + } else if (d.bs.curr > 0) { + d.bs.curr--; + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); + } else if (form.sel != -1) { + switchfocus = true; } break; case KEY_RIGHT: - if (informwin) { - mf = GETMYFIELD2(form); - if (mf->pos >= mf->buflen) - break; - mf->pos += 1; - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_CHAR); - } else { - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + if (focusinform) { + if (fieldctl(item, MOVE_CURSOR_RIGHT)) + DRAWITEM_TRICK(&form, form.sel, true); + } else if (d.bs.curr < (int) d.bs.nbuttons - 1) { + d.bs.curr++; + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); + } else if (form.sel != -1) { + switchfocus = true; } break; + case KEY_CTRL('p'): case KEY_UP: - if (nitems < 2) - break; - set_field_fore(current_field(form), t.form.fieldcolor); - set_field_back(current_field(form), t.form.fieldcolor); - form_driver_w(form, KEY_CODE_YES, REQ_PREV_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = MIN(mf->buflen, mf->maxpos); - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); + if (focusinform) { + next = previtem(form.nitems, form.pritems, + form.sel); + changeitem = form.sel != next; + } else if (form.sel != -1) { + switchfocus = true; + } break; + case KEY_CTRL('n'): case KEY_DOWN: - if (nitems < 2) + if (focusinform == false) break; - set_field_fore(current_field(form), t.form.fieldcolor); - set_field_back(current_field(form), t.form.fieldcolor); - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = MIN(mf->buflen, mf->maxpos); - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); + if (form.nitems == 1) { + switchfocus = true; + } else { + next = nextitem(form.nitems, form.pritems, + form.sel); + changeitem = form.sel != next; + } + break; + case KEY_PPAGE: + if (focusinform) { + next = firstitem(form.nitems, form.pritems); + changeitem = form.sel != next; + } + break; + case KEY_NPAGE: + if (focusinform) { + next = lastitem(form.nitems, form.pritems); + changeitem = form.sel != next; + } break; case KEY_BACKSPACE: case 127: /* Backspace */ - mf = GETMYFIELD2(form); - if (mf->pos <= 0) + if (focusinform == false) break; - form_driver_w(form, KEY_CODE_YES, REQ_DEL_PREV); - form_driver_w(form, KEY_CODE_YES, REQ_BEG_LINE); - mf->pos = mf->pos - 1; - for (i = 0; i < mf->pos; i++) - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_CHAR); - shiftleft(mf); + if (fieldctl(item, MOVE_CURSOR_LEFT)) + if (fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, form.sel, true); break; case KEY_DC: - form_driver_w(form, KEY_CODE_YES, REQ_DEL_CHAR); - mf = GETMYFIELD2(form); - if (mf->pos < mf->buflen) - shiftleft(mf); + if (focusinform == false) + break; + if (fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, form.sel, true); + break; + case KEY_HOME: + if (focusinform == false) + break; + if (fieldctl(item, MOVE_CURSOR_BEGIN)) + DRAWITEM_TRICK(&form, form.sel, true); + break; + case KEY_END: + if (focusinform == false) + break; + if (fieldctl(item, MOVE_CURSOR_END)) + DRAWITEM_TRICK(&form, form.sel, true); break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) + curs_set(0); + if (f1help_dialog(conf) != 0) { + retval = BSDDIALOG_ERROR; + loop = false; + } + if (form_redraw(&d, &form, focusinform) != 0) return (BSDDIALOG_ERROR); - /* No Break */ + break; + case KEY_CTRL('l'): case KEY_RESIZE: - output = REDRAWFORM; - loop = false; + if (form_redraw(&d, &form, focusinform) != 0) + return (BSDDIALOG_ERROR); break; default: - if (informwin) { - if (chtype == KEY_CODE_YES) + if (wchtype == KEY_CODE_YES) + break; + if (focusinform) { + if (item->fieldonebyte && wctob(input) == EOF) break; - mf = GETMYFIELD2(form); - if (mf->secure) - form_driver_w(form, chtype, mf->securech); - else - form_driver_w(form, chtype, input); - insertch(mf, input); - } - else { - if (shortcut_buttons(input, &bs)) { - output = return_values(conf, - bs.value[bs.curr], nitems, items, - form, cfield); + /* + * MOVE_CURSOR_RIGHT manages new positions + * because the cursor remains on the new letter, + * "if" and "while" update the positions. + */ + if (insertch(item, input, form.securewch)) { + fieldctl(item, MOVE_CURSOR_RIGHT); + /* + * no if (fieldctl), update always + * because it fails with maxletters. + */ + DRAWITEM_TRICK(&form, form.sel, true); + } + } else { + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } break; + } /* end switch get_wch() */ + + if (switchfocus) { + focusinform = !focusinform; + d.bs.curr = 0; + redrawbuttons(&d, + conf->button.always_active || !focusinform, + !focusinform); + wnoutrefresh(d.widget); + DRAWITEM_TRICK(&form, form.sel, focusinform); + switchfocus = false; } - } - - curs_set(0); - return (output); -} - -static int -form_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - const char *text, int linelen, unsigned int *formheight, int nitems, - struct buttons bs) -{ - int htext, wtext, menusize; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, *formheight + 2, - linelen + 2, &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, linelen + 2, &bs); - - if (rows == BSDDIALOG_AUTOSIZE) { - if (*formheight == 0) { - menusize = widget_max_height(conf) - HBORDERS - - 2 /*buttons*/ - htext; - menusize = MIN(menusize, nitems + 2); - *formheight = menusize - 2 < 0 ? 0 : menusize - 2; + if (changeitem) { + DRAWITEM_TRICK(&form, form.sel, false); + form.sel = next; + item = &form.pritems[form.sel]; + curriteminview(&form, item); + update_formbox(conf, &form); + wnoutrefresh(form.box); + DRAWITEM_TRICK(&form, form.sel, true); + changeitem = false; } - else /* h autosize with fixed formheight */ - menusize = *formheight + 2; - - *h = widget_min_height(conf, htext, menusize, true); - } else { - if (*formheight == 0) - *formheight = MIN(rows-6-htext, nitems); - } - - return (0); -} - -static int -form_checksize(int rows, int cols, const char *text, int formheight, int nitems, - unsigned int linelen, struct buttons bs) -{ - int mincols, textrow, formrows; + } /* end while (loop) */ - mincols = VBORDERS; - /* buttons */ - mincols += buttons_width(bs); - mincols = MAX(mincols, (int)linelen + 4); - - if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or " - "forms (label + field)"); - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; - - if (nitems > 0 && formheight == 0) - RETURN_ERROR("fields > 0 but formheight == 0, probably " - "terminal too small"); - - formrows = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + formrows + textrow) - RETURN_ERROR("Few lines for this menus"); - - return (0); -} - -int -bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int formheight, unsigned int nitems, - struct bsddialog_formitem *items) -{ - int i, output, color, y, x, h, w; - unsigned long j, maxline, mybufsize; - struct buttons bs; - struct myfield *myfields; - FIELD **cfield; - FORM *form; - WINDOW *widget, *formwin, *textpad, *shadow; - - /* disable form scrolling */ - if (formheight < nitems) - formheight = nitems; - - for (i = 0; i < (int)nitems; i++) { - if (items[i].maxvaluelen == 0) - RETURN_ERROR("maxvaluelen cannot be zero"); - if (items[i].fieldlen == 0) - RETURN_ERROR("fieldlen cannot be zero"); - if (items[i].fieldlen > items[i].maxvaluelen) - RETURN_ERROR("fieldlen cannot be > maxvaluelen"); - } - - maxline = 0; - myfields = malloc(nitems * sizeof(struct myfield)); - cfield = calloc(nitems + 1, sizeof(FIELD*)); - for (i = 0; i < (int)nitems; i++) { - cfield[i] = new_field(1, items[i].fieldlen, items[i].yfield-1, - items[i].xfield-1, 0, 0); - field_opts_off(cfield[i], O_STATIC); - set_max_field(cfield[i], items[i].maxvaluelen); - /* setlocale() should handle set_field_buffer() */ - set_field_buffer(cfield[i], 0, items[i].init); - - mybufsize = (items[i].maxvaluelen + 1) * sizeof(wchar_t); - myfields[i].buf = malloc(mybufsize); - memset(myfields[i].buf, 0, mybufsize); - for (j = 0; j < items[i].maxvaluelen && j < strlen(items[i].init); - j++) - myfields[i].buf[j] = items[i].init[j]; - - myfields[i].buflen = wcslen(myfields[i].buf); - - myfields[i].maxpos = items[i].maxvaluelen -1; - myfields[i].pos = MIN(myfields[i].buflen, myfields[i].maxpos); - - myfields[i].bottomdesc = items[i].bottomdesc; - set_field_userptr(cfield[i], &myfields[i]); - - field_opts_off(cfield[i], O_AUTOSKIP); - field_opts_off(cfield[i], O_BLANK); - - if (ISFIELDHIDDEN(items[i])) { - myfields[i].secure = true; - myfields[i].securech = ' '; - if (conf->form.securech != '\0') - myfields[i].securech = conf->form.securech; - } - else - myfields[i].secure = false; - - if (ISFIELDREADONLY(items[i])) { - field_opts_off(cfield[i], O_EDIT); - field_opts_off(cfield[i], O_ACTIVE); - color = t.form.readonlycolor; - } else { - color = i == 0 ? t.form.f_fieldcolor : t.form.fieldcolor; - } - set_field_fore(cfield[i], color); - set_field_back(cfield[i], color); - - maxline = MAX(maxline, items[i].xlabel + strlen(items[i].label)); - maxline = MAX(maxline, items[i].xfield + items[i].fieldlen - 1); - } - cfield[i] = NULL; - - /* disable focus with 1 item (inputbox or passwordbox) */ - if (formheight == 1 && nitems == 1 && strlen(items[0].label) == 0 && - items[0].xfield == 1 ) { - set_field_fore(cfield[0], t.dialog.color); - set_field_back(cfield[0], t.dialog.color); - } - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (form_autosize(conf, rows, cols, &h, &w, text, maxline, &formheight, - nitems, bs) != 0) - return (BSDDIALOG_ERROR); - if (form_checksize(h, w, text, formheight, nitems, maxline, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); + curs_set(0); - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + if (return_values(conf, &form, items) == BSDDIALOG_ERROR) return (BSDDIALOG_ERROR); - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - formheight, x + 1 + w - TEXTHMARGIN); - - formwin = new_boxed_window(conf, y + h - 3 - formheight -2, x +1, - formheight+2, w-2, LOWERED); - - form = new_form(cfield); - set_form_win(form, formwin); - /* should be formheight */ - set_form_sub(form, derwin(formwin, nitems, w-4, 1, 1)); - post_form(form); - - for (i = 0; i < (int)nitems; i++) - mvwaddstr(formwin, items[i].ylabel, items[i].xlabel, - items[i].label); - - wrefresh(formwin); - - do { - output = form_handler(conf, widget, bs, formwin, form, cfield, - nitems, items); - - if (update_dialog(conf, shadow, widget, y, x, h, w, textpad, - text, &bs, true) != 0) - return (BSDDIALOG_ERROR); + if (focusitem != NULL) + *focusitem = form.sel; - doupdate(); - wrefresh(widget); - - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - formheight, x + 1 + w - TEXTHMARGIN); - - draw_borders(conf, formwin, formheight+2, w-2, LOWERED); - wrefresh(formwin); - - refresh(); - } while (output == REDRAWFORM); - - unpost_form(form); - free_form(form); - for (i = 0; i < (int)nitems; i++) { - free_field(cfield[i]); - free(myfields[i].buf); + if (form.hasbottomdesc && conf->clear) { + move(SCREENLINES - 1, 2); + clrtoeol(); } - free(cfield); - free(myfields); - - delwin(formwin); - end_dialog(conf, shadow, widget, textpad); + for (i = 0; i < form.nitems; i++) { + free(form.pritems[i].privwbuf); + free(form.pritems[i].pubwbuf); + } + delwin(form.pad); + delwin(form.box); + end_dialog(&d); - return (output); + return (retval); } diff --git a/contrib/bsddialog/lib/infobox.c b/contrib/bsddialog/lib/infobox.c deleted file mode 100644 index 5a6b7c2fd692..000000000000 --- a/contrib/bsddialog/lib/infobox.c +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * 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 <sys/param.h> - -#include <curses.h> - -#include "bsddialog.h" -#include "lib_util.h" - -static int -infobox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, const char *text) -{ - int htext, wtext; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, NULL, 0, SCREENCOLS/2, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, 0, NULL); - - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 0, false); - - return (0); -} - -static int infobox_checksize(int rows, int cols) -{ - if (cols < HBORDERS) - RETURN_ERROR("Few cols, infobox needs at least width 2"); - - if (rows < VBORDERS) - RETURN_ERROR("Infobox needs at least height 2"); - - return (0); -} - -/* API */ -int -bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, - int cols) -{ - int y, x, h, w; - WINDOW *shadow, *widget, *textpad; - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (infobox_autosize(conf, rows, cols, &h, &w, text) != 0) - return (BSDDIALOG_ERROR); - if (infobox_checksize(h, w) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, - NULL, false) != 0) - return (BSDDIALOG_ERROR); - - pnoutrefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-2, - x+w-TEXTHMARGIN); - - doupdate(); - - end_dialog(conf, shadow, widget, textpad); - - return (BSDDIALOG_OK); -}
\ No newline at end of file diff --git a/contrib/bsddialog/lib/lib_util.c b/contrib/bsddialog/lib/lib_util.c index 506003b52c8d..d673a1a74d72 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-2022 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,22 +25,82 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> #include <curses.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <wctype.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -#define TABLEN 4 /* Default tab len */ -#define ERRBUFLEN 1024 /* Error buffer */ +/* + * -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); + */ + +/* + * -1- Error and diagnostic + */ +#define ERRBUFLEN 1024 -/* Error */ static char errorbuffer[ERRBUFLEN]; const char *get_error_string(void) @@ -53,55 +113,122 @@ void set_error_string(const char *str) strncpy(errorbuffer, str, ERRBUFLEN-1); } -/* Clear */ -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; - if ((clear = newwin(h, w, y + t.shadow.h, x + t.shadow.w)) == NULL) - RETURN_ERROR("Cannot hide the widget"); - wbkgd(clear, t.screen.color); + 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); +} + +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); } -/* F1 help */ -int f1help(struct bsddialog_conf *conf) +unsigned int strcols(const char *mbstring) { - int output; - struct bsddialog_conf hconf; + 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; + } - 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; + return (ncol); +} - output = BSDDIALOG_OK; - if (conf->key.f1_message != NULL) - output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0); +/* + * -3- Buttons + */ +static int buttons_min_width(struct buttons *bs) +{ + unsigned int width; - if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL) - output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0); + width = bs->nbuttons * bs->sizebutton; + if (bs->nbuttons > 0) + width += (bs->nbuttons - 1) * t.button.minmargin; - return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); + return (width); } -/* Buttons */ static void draw_button(WINDOW *window, int y, int x, int size, const char *text, - bool selected, bool shortcut) + wchar_t first, bool selected, bool shortcut) { int i, color_arrows, color_shortkey, color_button; @@ -126,129 +253,160 @@ draw_button(WINDOW *window, int y, int x, int size, const char *text, 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 (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, struct buttons bs, bool shortcut) +void draw_buttons(struct dialog *d) { - int i, x, startx, y, rows, cols; + int i, x, startx, y; + unsigned int newmargin, margin, wbuttons; - getmaxyx(window, rows, cols); - y = rows - 2; + y = d->h - 2; - startx = cols/2 - buttons_width(bs)/2; + 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; + } - for (i = 0; i < (int)bs.nbuttons; i++) { - x = i * (bs.sizebutton + t.button.hmargin); - draw_button(window, y, startx + x, bs.sizebutton, bs.label[i], - i == bs.curr, shortcut); + 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, - const char *yesoklabel, const char *nocancellabel) +set_buttons(struct dialog *d, bool shortcut, const char *oklabel, + const char *cancellabel) { int i; #define SIZEBUTTON 8 -#define DEFAULT_BUTTON_LABEL BUTTON_OK_LABEL +#define DEFAULT_BUTTON_LABEL OK_LABEL #define DEFAULT_BUTTON_VALUE BSDDIALOG_OK + wchar_t first; - bs->nbuttons = 0; - bs->curr = 0; - bs->sizebutton = 0; + d->bs.nbuttons = 0; + d->bs.curr = 0; + d->bs.sizebutton = 0; + d->bs.shortcut = shortcut; - 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 (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 (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 (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.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 (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 (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 (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 (conf->button.generic1_label != NULL) { - bs->label[bs->nbuttons] = conf->button.generic1_label; - bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1; - 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.generic2_label != NULL) { - bs->label[bs->nbuttons] = conf->button.generic2_label; - bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2; - bs->nbuttons += 1; + 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; } - if (bs->nbuttons == 0) { - bs->label[0] = DEFAULT_BUTTON_LABEL; - bs->value[0] = DEFAULT_BUTTON_VALUE; - bs->nbuttons = 1; + 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; } - 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 (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; } - 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; -} + 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; + } -int buttons_width(struct buttons bs) -{ - unsigned int width; + 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; + } + + if (d->bs.nbuttons == 0) { + d->bs.label[0] = DEFAULT_BUTTON_LABEL; + d->bs.value[0] = DEFAULT_BUTTON_VALUE; + d->bs.nbuttons = 1; + } - width = bs.nbuttons * bs.sizebutton; - if (bs.nbuttons > 0) - width += (bs.nbuttons - 1) * t.button.hmargin; + for (i = 0; i < (int)d->bs.nbuttons; i++) { + mbtowc(&first, d->bs.label[i], MB_CUR_MAX); + d->bs.first[i] = first; + } - return (width); + 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; + } + } + + 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; } -bool shortcut_buttons(int key, struct buttons *bs) +bool shortcut_buttons(wint_t key, struct buttons *bs) { bool match; unsigned int i; match = false; for (i = 0; i < bs->nbuttons; i++) { - if (tolower(key) == tolower(bs->label[i][0])) { + if (towlower(key) == towlower(bs->first[i])) { bs->curr = i; match = true; break; @@ -258,233 +416,192 @@ bool shortcut_buttons(int key, struct buttons *bs) return (match); } -/* Text */ -static bool is_text_attr(const char *text) +/* + * -4- (Auto) Sizing and (Auto) Position + */ +static int widget_max_height(struct bsddialog_conf *conf) { - if (strnlen(text, 3) < 3) - return (false); - - if (text[0] != '\\' || text[1] != 'Z') - return (false); - - return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true); -} + int maxheight; -static bool check_set_text_attr(WINDOW *win, char *text) -{ - if (is_text_attr(text) == false) - return (false); + maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); - if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) { - wattron(win, bsddialog_color(text[2] - '0', COLOR_WHITE, 0)); - return (true); + 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"); } - switch (text[2]) { - case 'n': - wattron(win, t.dialog.color); - 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; - } + maxheight -= conf->auto_downmargin; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - Down margins " + "<= 0"); - return (true); + return (maxheight); } -static void -print_string(WINDOW *win, int *rows, int cols, int *y, int *x, char *str, - bool color) +static int widget_max_width(struct bsddialog_conf *conf) { - int i, j, len, reallen; + int maxwidth; - len = reallen = strlen(str); - if (color) { - i=0; - while (i < len) { - if (is_text_attr(str+i)) - reallen -= 3; - i++; - } - } + maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); - 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_text_attr(win, str+i)) { - i += 3; - } else { - mvwaddch(win, *y, j, str[i]); - i++; - reallen--; - j++; - *x = j; - } - } + if (conf->x > 0) { + maxwidth -= conf->x; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow " + "- x <= 0"); } + + return (maxwidth); } -static int -print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) +static bool is_wtext_attr(const wchar_t *wtext) { - bool loop; - int i, j, z, rows, cols, x, y, tablen; - char *string; - - if ((string = malloc(strlen(text) + 1)) == NULL) - RETURN_ERROR("Cannot build (analyze) text"); - - getmaxyx(pad, rows, cols); - tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; + bool att; - i = j = x = y = 0; - loop = true; - while (loop) { - string[j] = text[i]; - - if (strchr("\n\t ", string[j]) != NULL || string[j] == '\0') { - string[j] = '\0'; - print_string(pad, &rows, cols, &y, &x, string, - conf->text.highlight); - } - - switch (text[i]) { - case '\0': - loop = false; - break; - case '\n': - x = 0; - y++; - j = -1; - break; - case '\t': - for (z = 0; z < tablen; z++) { - if (x >= cols) { - x = 0; - y++; - } - x++; - } - j = -1; - break; - case ' ': - x++; - if (x >= cols) { - x = 0; - y++; - } - j = -1; - } + if (wcsnlen(wtext, 3) < 3) + return (false); + if (wtext[0] != L'\\' || wtext[1] != L'Z') + return (false); - if (y >= rows) { - rows = y + 1; - wresize(pad, rows, cols); - } + att = wcschr(L"nbBdDkKrRsSuU01234567", wtext[2]) == NULL ? false : true; - j++; - i++; - } + return (att); +} - free(string); +#define NL -1 +#define WS -2 +#define TB -3 - return (0); -} +struct textproperties { + int nword; + int *words; + uint8_t *wletters; + int maxwordcols; + int maxline; + bool hasnewline; +}; -/* Autosize */ static int -text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, - int mincols, bool increasecols, int *h, int *w) +text_properties(struct bsddialog_conf *conf, const char *text, + struct textproperties *tp) { - int i, j, z, x, y; - int tablen, wordlen, maxwordlen, nword, maxwords, line, maxwidth; - int *words; -#define NL -1 -#define WS -2 + int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols; + wchar_t *wtext; + + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; maxwords = 1024; - if ((words = calloc(maxwords, sizeof(int))) == NULL) + if ((tp->words = calloc(maxwords, sizeof(int))) == NULL) RETURN_ERROR("Cannot alloc memory for text autosize"); - tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; - maxwidth = widget_max_width(conf) - HBORDERS - TEXTHMARGINS; - - nword = 0; - wordlen = 0; - maxwordlen = 0; - i=0; - while (true) { - if (conf->text.highlight && is_text_attr(text + i)) { - i += 3; + 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; } - if (nword + tablen + 1 >= maxwords) { + if (tp->nword + 1 >= maxwords) { maxwords += 1024; - words = realloc(words, maxwords * sizeof(int)); - if (words == NULL) + tp->words = realloc(tp->words, maxwords * sizeof(int)); + if (tp->words == NULL) RETURN_ERROR("Cannot realloc memory for text " "autosize"); } - if (text[i] == '\0') { - words[nword] = wordlen; - maxwordlen = MAX(wordlen, maxwordlen); - break; - } - - if (strchr("\t\n ", text[i]) != NULL) { - maxwordlen = MAX(wordlen, maxwordlen); + if (wcschr(L"\t\n ", wtext[i]) != NULL) { + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); - if (wordlen != 0) { - words[nword] = wordlen; - nword++; - wordlen = 0; + if (wordcols != 0) { + /* line */ + currlinecols += wordcols; + /* word */ + tp->words[tp->nword] = wordcols; + tp->nword += 1; + wordcols = 0; } - if (text[i] == '\t') { - for (j = 0; j < tablen; j++) - words[nword + j] = 1; - nword += tablen; - } else { - words[nword] = text[i] == '\n' ? NL : WS; - nword++; + 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++; } - else - wordlen++; - - i++; } + /* 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) - BORDERS - TEXTHMARGINS; + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; if (increasecols) { - mincols = MAX(mincols, maxwordlen); + mincols = MAX(mincols, tp->maxwordcols); mincols = MAX(mincols, - (int)conf->auto_minwidth - HBORDERS - TEXTHMARGINS); + (int)conf->auto_minwidth - BORDERS - TEXTHMARGINS); mincols = MIN(mincols, maxwidth); } @@ -492,26 +609,50 @@ text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, x = 0; y = 1; line=0; - for (i = 0; i <= nword; i++) { - if (words[i] == NL) { + 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; - } - else if (words[i] == WS) { + break; + case WS: x++; if (x >= mincols) { x = 0; y++; } - } - else { - if (words[i] + x <= mincols) - x += words[i]; - else { - for (z = words[i]; z > 0; ) { - y++; - x = MIN(mincols, z); - z -= x; + 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; } } } @@ -520,141 +661,111 @@ text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, if (increasecols == false) break; - if (y <= maxrows || mincols >= maxwidth) + if (mincols >= maxwidth) + break; + if (line >= y * (int)conf->text.cols_per_row && y <= maxrows) break; mincols++; } - *h = (nword == 0 && words[0] == 0) ? 0 : y; + *h = (tp->nword == 0) ? 0 : y; *w = MIN(mincols, line); /* wtext can be less than mincols */ - free(words); - return (0); } -int +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) { - int wbuttons, maxhtext; bool changewtext; + int wbuttons, maxhtext; + struct textproperties tp; wbuttons = 0; - if (bs != NULL) - wbuttons = buttons_width(*bs); + 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) - VBORDERS - TEXTHMARGINS; + startwtext = widget_max_width(conf) - BORDERS - TEXTHMARGINS; changewtext = false; } else { /* fixed */ - startwtext = cols - VBORDERS - TEXTHMARGINS; + startwtext = cols - BORDERS - TEXTHMARGINS; changewtext = false; } - if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { - maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext; - if (bs != NULL) - maxhtext -= 2; - } else { /* fixed */ - maxhtext = rows - VBORDERS - rowsnotext; - if (bs != NULL) - maxhtext -= 2; - } - if (startwtext <= 0 && changewtext) startwtext = 1; - if (maxhtext <= 0 || startwtext <= 0) { - *htext = *wtext = 0; - return (0); - } - if (text_autosize(conf, text, maxhtext, startwtext, changewtext, - htext, wtext) != 0) + /* 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); - return (0); -} - -int widget_max_height(struct bsddialog_conf *conf) -{ - int maxheight; - - maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.h : SCREENLINES; - if (maxheight <= 0) - RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); - - if (conf->y > 0) { - maxheight -= conf->y; - if (maxheight <= 0) - RETURN_ERROR("Terminal too small, screen lines - " - "shadow - y <= 0"); - } - - return (maxheight); -} - -int widget_max_width(struct bsddialog_conf *conf) -{ - int maxwidth; - - maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.w : SCREENCOLS; - if (maxwidth <= 0) - RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); + free(tp.words); + free(tp.wletters); - if (conf->x > 0) { - maxwidth -= conf->x; - if (maxwidth <= 0) - RETURN_ERROR("Terminal too small, screen cols - shadow " - "- x <= 0"); - } - - return (maxwidth); + return (0); } -int -widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget, +static int +widget_min_height(struct bsddialog_conf *conf, int htext, int hnotext, bool withbuttons) { int min; - min = 0; - - /* buttons */ - if (withbuttons) - min += 2; /* buttons and border */ + /* dialog borders */ + min = BORDERS; /* text */ min += htext; - /* specific widget min height */ - min += minwidget; + /* specific widget lines without text */ + min += hnotext; + + /* buttons */ + if (withbuttons) + min += HBUTTONS; /* buttons and their up-border */ - /* 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 +static int widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, struct buttons *bs) { - int min, delimtitle; + int min, delimtitle, wbottomtitle, wtitle; min = 0; /* buttons */ - if (bs != NULL) - min += buttons_width(*bs); + if (bs->nbuttons > 0) + min += buttons_min_width(bs); /* text */ if (wtext > 0) @@ -666,19 +777,20 @@ widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, /* title */ if (conf->title != NULL) { delimtitle = t.dialog.delimtitle ? 2 : 0; - min = MAX(min, (int)strlen(conf->title) + 2 + delimtitle); + wtitle = strcols(conf->title); + min = MAX(min, wtitle + 2 + delimtitle); } /* bottom title */ - if (conf->bottomtitle != NULL) - min = MAX(min, (int)strlen(conf->bottomtitle) + 4); + if (conf->bottomtitle != NULL) { + wbottomtitle = strcols(conf->bottomtitle); + min = MAX(min, wbottomtitle + 4); + } /* dialog borders */ - min += VBORDERS; + min += BORDERS; /* conf.auto_minwidth */ min = MAX(min, (int)conf->auto_minwidth); - /* avoid terminal overflow */ - min = MIN(min, widget_max_width(conf)); return (min); } @@ -695,11 +807,8 @@ set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) *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) @@ -709,21 +818,78 @@ set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) *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); } int +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 (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) { - if (conf->y == BSDDIALOG_CENTER) - *y = SCREENLINES/2 - (h + t.shadow.h)/2; + 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) @@ -731,13 +897,13 @@ set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) else *y = conf->y; - if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > SCREENLINES) + 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 + t.shadow.w)/2; + *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) @@ -745,228 +911,456 @@ set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) else *x = conf->x; - if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > SCREENCOLS) + if ((*x + w + wshadow) > SCREENCOLS) RETURN_ERROR("The right of the box over the terminal " "(begin X + width (+ shadow) > terminal cols)"); 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) +{ + 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); +} + +/* + * -5- Widget components and utilities + */ +int hide_dialog(struct dialog *d) { + WINDOW *clear; + + 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); + + if (d->conf->shadow) { + mvwin(clear, d->y + t.shadow.y, d->x + t.shadow.x); + wrefresh(clear); + } + + delwin(clear); + + return (0); +} + +int f1help_dialog(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.escape = conf->text.escape; + + 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); +} + +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; + cchar_t *ls, *rs, *ts, *bs, *tl, *tr, *bl, *br; + cchar_t hline, vline, corner; if (conf->no_lines) return; if (conf->ascii_lines) { - ls = rs = '|'; - ts = bs = '-'; - tl = tr = bl = br = ltee = rtee = '+'; + setcchar(&hline, L"|", 0, 0, NULL); + ls = rs = &hline; + setcchar(&vline, L"-", 0, 0, NULL); + ts = bs = &vline; + setcchar(&corner, L"+", 0, 0, NULL); + tl = tr = bl = br = &corner; } 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; + ls = rs = WACS_VLINE; + ts = bs = WACS_HLINE; + tl = WACS_ULCORNER; + tr = WACS_URCORNER; + bl = WACS_LLCORNER; + br = WACS_LRCORNER; } - leftcolor = elev == RAISED ? + getmaxyx(win, h, w); + leftcolor = (elev == RAISED) ? t.dialog.lineraisecolor : t.dialog.linelowercolor; - rightcolor = elev == RAISED ? + rightcolor = (elev == RAISED) ? t.dialog.linelowercolor : t.dialog.lineraisecolor; + wattron(win, leftcolor); - wborder(win, ls, rs, ts, bs, tl, tr, bl, br); + wborder_set(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); + mvwadd_wch(win, 0, w-1, tr); + mvwvline_set(win, 1, w-1, rs, h-2); + mvwadd_wch(win, h-1, w-1, br); + mvwhline_set(win, h-1, 1, bs, w-2); wattroff(win, rightcolor); } -WINDOW * -new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, +void +update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w, enum elevation elev) { - WINDOW *win; + wclear(win); + wresize(win, h, w); + mvwin(win, y, x); + draw_borders(conf, win, elev); +} - if ((win = newwin(rows, cols, y, x)) == NULL) { - set_error_string("Cannot build boxed window"); - return (NULL); - } +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); +} - wbkgd(win, t.dialog.color); +/* + * -6- Dialog init/build, update/draw, destroy + */ +void end_dialog(struct dialog *d) +{ + if (d->conf->sleep > 0) + sleep(d->conf->sleep); - draw_borders(conf, win, rows, cols, elev); + delwin(d->textpad); + delwin(d->widget); + if (d->conf->shadow) + delwin(d->shadow); - return (win); + if (d->conf->clear) + hide_dialog(d); + + if (d->conf->get_height != NULL) + *d->conf->get_height = d->h; + if (d->conf->get_width != NULL) + *d->conf->get_width = d->w; } -static int -draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons) +static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext) { - int h, w, ts, ltee, rtee; + enum bsddialog_color bg; + + if (is_wtext_attr(wtext) == false) + return (false); + + 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); + } - ts = conf->ascii_lines ? '-' : ACS_HLINE; - ltee = conf->ascii_lines ? '+' : ACS_LTEE; - rtee = conf->ascii_lines ? '+' : ACS_RTEE; + 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; + } - getmaxyx(widget, h, w); + return (true); +} - if (conf->shadow) - wnoutrefresh(shadow); +static void +print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str, + bool color) +{ + int charwidth, i, j, strlen, strwidth; + wchar_t ws[2]; - draw_borders(conf, widget, h, w, RAISED); + ws[1] = L'\0'; - if (conf->title != NULL) { - if (t.dialog.delimtitle && conf->no_lines == false) { - wattron(widget, t.dialog.lineraisecolor); - mvwaddch(widget, 0, w/2-strlen(conf->title)/2-1, rtee); - wattroff(widget, t.dialog.lineraisecolor); - } - wattron(widget, t.dialog.titlecolor); - mvwaddstr(widget, 0, w/2 - strlen(conf->title)/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); + strlen = wcslen(str); + if (color) { + strwidth = 0; + i=0; + while (i < strlen) { + if (is_wtext_attr(str+i) == false) { + strwidth += wcwidth(str[i]); + i++; + } else { + i += 3; + } } - } + } else + strwidth = wcswidth(str, strlen); - 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); + i = 0; + while (i < strlen) { + if (*x + strwidth > cols) { + if (*x != 0) + *y = *y + 1; + if (*y >= *rows) { + *rows = *y + 1; + wresize(win, *rows, cols); + } + *x = 0; + } + j = *x; + while (i < strlen) { + if (color && check_set_wtext_attr(win, str+i)) { + i += 3; + continue; + } - wattron(widget, t.dialog.linelowercolor); - mvwaddch(widget, h-3, w-1, rtee); - wattroff(widget, t.dialog.linelowercolor); + charwidth = wcwidth(str[i]); + if (j + wcwidth(str[i]) > cols) + break; + /* inline mvwaddwch() for efficiency */ + ws[0] = str[i]; + mvwaddwstr(win, *y, j, ws); + strwidth -= charwidth; + j += charwidth; + *x = j; + i++; } - draw_buttons(widget, *bs, shortcutbuttons); } +} - if (conf->bottomtitle != NULL) { - wattron(widget, t.dialog.bottomtitlecolor); - wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1); - waddch(widget, ' '); - waddstr(widget, conf->bottomtitle); - waddch(widget, ' '); - wattroff(widget, t.dialog.bottomtitlecolor); - } +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; - wnoutrefresh(widget); + if ((wtext = alloc_mbstows(text)) == NULL) + RETURN_ERROR("Cannot allocate/print text in wchar_t*"); - if (textpad != NULL && text != NULL) /* textbox */ - if (print_textpad(conf, textpad, text) !=0) - return (BSDDIALOG_ERROR); + if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL) + RETURN_ERROR("Cannot build (analyze) text"); - return (0); -} + getmaxyx(pad, rows, cols); + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; -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; + i = j = x = y = 0; + loop = true; + while (loop) { + string[j] = wtext[i]; - if (conf->shadow) { - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - } + 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); + } - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); + 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 (textpad != NULL) { - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - TEXTHMARGINS); + if (y >= rows) { + rows = y + 1; + wresize(pad, rows, cols); + } + + j++; + i++; } - error = draw_dialog(conf, shadow, widget, textpad, text, bs, - shortcutbuttons); + free(wtext); + free(string); - return (error); + return (0); } -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 draw_dialog(struct dialog *d) { - int error; + int wtitle, wbottomtitle; + cchar_t ts, ltee, rtee; - if (conf->shadow) { - *shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w); - if (*shadow == NULL) - RETURN_ERROR("Cannot build shadow"); - wbkgd(*shadow, t.shadow.color); + if (d->conf->ascii_lines) { + setcchar(&ts, L"-", 0, 0, NULL); + setcchar(<ee, L"+", 0, 0,NULL); + setcchar(&rtee, L"+", 0, 0, NULL); + } else { + ts = *WACS_HLINE; + ltee = *WACS_LTEE; + rtee = *WACS_RTEE; } - if ((*widget = new_boxed_window(conf, y, x, h, w, RAISED)) == NULL) { - if (conf->shadow) - delwin(*shadow); - return (BSDDIALOG_ERROR); + 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 && 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"); + 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); + mvwadd_wch(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); + wadd_wch(d->widget, <ee); + wattroff(d->widget, t.dialog.lineraisecolor); } - wbkgd(*textpad, t.dialog.color); } - error = draw_dialog(conf, *shadow, *widget, - textpad == NULL ? NULL : *textpad, text, bs, shortcutbuttons); + if (d->bs.nbuttons > 0) { + if (d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + mvwadd_wch(d->widget, d->h-3, 0, <ee); + mvwhline_set(d->widget, d->h-3, 1, &ts, d->w-2); + wattroff(d->widget, t.dialog.lineraisecolor); - return (error); -} + wattron(d->widget, t.dialog.linelowercolor); + mvwadd_wch(d->widget, d->h-3, d->w-1, &rtee); + wattroff(d->widget, t.dialog.linelowercolor); + } + draw_buttons(d); + } -void -end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - WINDOW *textpad) -{ - int y, x, h, w; + 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); + } - getbegyx(widget, y, x); - getmaxyx(widget, h, w); + wnoutrefresh(d->widget); - if (conf->sleep > 0) - sleep(conf->sleep); + 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 (textpad != NULL) - delwin(textpad); + if (print_textpad(d->conf, d->textpad, d->text) != 0) + return (BSDDIALOG_ERROR); - delwin(widget); + d->built = true; - if (conf->shadow) - delwin(shadow); + return (0); +} - if (conf->clear) - hide_widget(y, x, h, w, conf->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); + } - if (conf->get_height != NULL) - *conf->get_height = h; - if (conf->get_width != NULL) - *conf->get_width = w; -} + 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 diff --git a/contrib/bsddialog/lib/lib_util.h b/contrib/bsddialog/lib/lib_util.h index 6ebc73cf1055..526f65b4bfaa 100644 --- a/contrib/bsddialog/lib/lib_util.h +++ b/contrib/bsddialog/lib/lib_util.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,106 +28,141 @@ #ifndef _LIBBSDDIALOG_UTIL_H_ #define _LIBBSDDIALOG_UTIL_H_ -#define HBORDERS 2 -#define VBORDERS 2 +#define BORDER 1 +#define BORDERS (BORDER + BORDER) #define TEXTHMARGIN 1 #define TEXTHMARGINS (TEXTHMARGIN + TEXTHMARGIN) +#define HBUTTONS 2 +#define OK_LABEL "OK" +#define CANCEL_LABEL "Cancel" -/* current theme */ +/* theme util */ extern struct bsddialog_theme t; +extern bool hastermcolors; +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) /* debug */ -#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \ - mvprintw(y, x, fmt, __VA_ARGS__); \ - refresh(); \ +#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \ + mvprintw(y, x, fmt, __VA_ARGS__); \ + refresh(); \ } while (0) - -/* error buffer */ -const char *get_error_string(void); -void set_error_string(const char *string); - -#define RETURN_ERROR(str) do { \ - set_error_string(str); \ - return (BSDDIALOG_ERROR); \ +/* error and diagnostic */ +#define RETURN_ERROR(str) do { \ + set_error_string(str); \ + return (BSDDIALOG_ERROR); \ +} while (0) +#define RETURN_FMTERROR(fmt, ...) do { \ + set_fmt_error_string(fmt, __VA_ARGS__); \ + return (BSDDIALOG_ERROR); \ +} while (0) +/* check ptr */ +#define CHECK_PTR(p) do { \ + if (p == NULL) \ + RETURN_ERROR("*" #p " is NULL"); \ +} while (0) +#define CHECK_ARRAY(nitem, a) do { \ + if (nitem > 0 && a == NULL) \ + RETURN_FMTERROR(#nitem " is %d but *" #a " is NULL", nitem); \ +} while (0) +/* widget utils */ +#define KEY_CTRL(c) (c & 037) +#define TEXTPAD(d, downnotext) rtextpad(d, 0, 0, 0, downnotext) +#define SCREENLINES (getmaxy(stdscr)) +#define SCREENCOLS (getmaxx(stdscr)) +#define CHECK_STR(s) (s == NULL ? "" : s) +#define UARROW(c) (c->ascii_lines ? '^' : ACS_UARROW) +#define DARROW(c) (c->ascii_lines ? 'v' : ACS_DARROW) +#define LARROW(c) (c->ascii_lines ? '<' : ACS_LARROW) +#define RARROW(c) (c->ascii_lines ? '>' : ACS_RARROW) +#define DRAW_BUTTONS(d) do { \ + draw_buttons(&d); \ + wnoutrefresh(d.widget); \ } while (0) -/* buttons */ +/* internal types */ +enum elevation { RAISED, LOWERED }; + struct buttons { unsigned int nbuttons; -#define MAXBUTTONS 6 /* ok + extra + cancel + help + 2 generics */ +#define MAXBUTTONS 10 /* 3left + ok + extra + cancel + help + 3 right */ const char *label[MAXBUTTONS]; + bool shortcut; + wchar_t first[MAXBUTTONS]; int value[MAXBUTTONS]; int curr; +#define BUTTONVALUE(bs) bs.value[bs.curr] unsigned int sizebutton; /* including left and right delimiters */ }; -#define BUTTON_OK_LABEL "OK" -#define BUTTON_CANCEL_LABEL "Cancel" -void -get_buttons(struct bsddialog_conf *conf, struct buttons *bs, - const char *yesoklabel, const char *nocancellabel); - -void -draw_buttons(WINDOW *window, struct buttons bs, bool shortcut); - -int buttons_width(struct buttons bs); -bool shortcut_buttons(int key, struct buttons *bs); +struct dialog { + bool built; /* true after the first draw_dialog() */ + struct bsddialog_conf *conf; /* Checked API conf */ + WINDOW *widget; /* Size and position refer to widget */ + int y, x; /* Current position, API conf.[y|x]: -1, >=0 */ + int rows, cols; /* API rows and cols: -1, 0, >0 */ + int h, w; /* Current height and width */ + const char *text; /* Checked API text, at least "" */ + WINDOW *textpad; /* Fake for textbox */ + struct buttons bs; /* bs.nbuttons = 0 for no buttons */ + WINDOW *shadow; +}; -/* help window with F1 key */ -int f1help(struct bsddialog_conf *conf); +/* error and diagnostic */ +const char *get_error_string(void); +void set_error_string(const char *string); +void set_fmt_error_string(const char *fmt, ...); -/* cleaner */ -int hide_widget(int y, int x, int h, int w, bool withshadow); +/* multicolumn character string */ +unsigned int strcols(const char *mbstring); +int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col); +void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch); +wchar_t* alloc_mbstows(const char *mbstring); -/* (auto) size and (auto) position */ -#define SCREENLINES (getmaxy(stdscr)) -#define SCREENCOLS (getmaxx(stdscr)) +/* buttons */ +void +set_buttons(struct dialog *d, bool shortcut, const char *oklabel, + const char *canclabel); +void draw_buttons(struct dialog *d); +bool shortcut_buttons(wint_t key, struct buttons *bs); -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); +/* widget utils */ +int hide_dialog(struct dialog *d); +int f1help_dialog(struct bsddialog_conf *conf); -int widget_max_height(struct bsddialog_conf *conf); -int widget_max_width(struct bsddialog_conf *conf); +void +draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev); -int -widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget, - bool withbuttons); +void +update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w, + enum elevation elev); -int -widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, - struct buttons *bs); +void +rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext); +/* (auto) sizing and (auto) position */ int set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w); 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); -/* widget builders */ -enum elevation { RAISED, LOWERED }; +int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw); -void -draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, - enum elevation elev); +int +set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w); -WINDOW * -new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, - enum elevation elev); +int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext); -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); +/* dialog */ +void end_dialog(struct dialog *d); +int draw_dialog(struct dialog *d); 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); - -void -end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - WINDOW *textpad); +prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows, + int cols, struct dialog *d); -#endif
\ No newline at end of file +#endif diff --git a/contrib/bsddialog/lib/libbsddialog.c b/contrib/bsddialog/lib/libbsddialog.c index 761bdc3efa77..555d060ebcbd 100644 --- a/contrib/bsddialog/lib/libbsddialog.c +++ b/contrib/bsddialog/lib/libbsddialog.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 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 @@ -26,18 +26,19 @@ */ #include <curses.h> -#include <stdlib.h> #include <string.h> -#include <unistd.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -int bsddialog_init(void) +#define DEFAULT_COLS_PER_ROW 10 /* Default conf.text.columns_per_row */ + +static bool in_bsddialog_mode = false; + +int bsddialog_init_notheme(void) { int i, j, c, error; - enum bsddialog_default_theme theme; set_error_string(""); @@ -54,6 +55,7 @@ int bsddialog_init(void) bsddialog_end(); RETURN_ERROR("Cannot init curses (keypad and cursor)"); } + in_bsddialog_mode = true; c = 1; error += start_color(); @@ -64,13 +66,25 @@ int bsddialog_init(void) } } - if (error == OK && has_colors()) + hastermcolors = (error == OK && has_colors()) ? true : false; + + return (BSDDIALOG_OK); +} + +int bsddialog_init(void) +{ + enum bsddialog_default_theme theme; + + bsddialog_init_notheme(); + + if (bsddialog_hascolors()) theme = BSDDIALOG_THEME_FLAT; else theme = BSDDIALOG_THEME_BLACKWHITE; if (bsddialog_set_default_theme(theme) != 0) { bsddialog_end(); + in_bsddialog_mode = false; return (BSDDIALOG_ERROR); } @@ -81,22 +95,35 @@ int bsddialog_end(void) { if (endwin() != OK) RETURN_ERROR("Cannot end curses (endwin)"); + in_bsddialog_mode = false; return (BSDDIALOG_OK); } int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle) { - mvaddstr(0, 1, backtitle); - if (conf->no_lines != true) - mvhline(1, 1, conf->ascii_lines ? '-' : ACS_HLINE, - SCREENCOLS - 2); + CHECK_PTR(conf); + + move(0, 1); + clrtoeol(); + addstr(CHECK_STR(backtitle)); + if (conf->no_lines != true) { + if (conf->ascii_lines) + mvhline(1, 1, '-', SCREENCOLS - 2); + else + mvhline_set(1, 1, WACS_HLINE, SCREENCOLS - 2); + } refresh(); return (BSDDIALOG_OK); } +bool bsddialog_inmode(void) +{ + return (in_bsddialog_mode); +} + const char *bsddialog_geterror(void) { return (get_error_string()); @@ -104,24 +131,25 @@ const char *bsddialog_geterror(void) int bsddialog_initconf(struct bsddialog_conf *conf) { - if (conf == NULL) - RETURN_ERROR("conf is NULL"); - if (sizeof(*conf) != sizeof(struct bsddialog_conf)) - RETURN_ERROR("Bad conf size"); + CHECK_PTR(conf); memset(conf, 0, sizeof(struct bsddialog_conf)); conf->y = BSDDIALOG_CENTER; conf->x = BSDDIALOG_CENTER; conf->shadow = true; + conf->text.cols_per_row = DEFAULT_COLS_PER_ROW; return (BSDDIALOG_OK); } -int bsddialog_clearterminal(void) +void bsddialog_refresh(void) { - if (clear() != OK) - RETURN_ERROR("Cannot clear the terminal"); refresh(); +} - return (BSDDIALOG_OK); +void bsddialog_clear(unsigned int y) +{ + move(y, 0); + clrtobot(); + refresh(); }
\ No newline at end of file diff --git a/contrib/bsddialog/lib/menubox.c b/contrib/bsddialog/lib/menubox.c index 22ed15e6e7a0..896306b2881d 100644 --- a/contrib/bsddialog/lib/menubox.c +++ b/contrib/bsddialog/lib/menubox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,20 +25,13 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> #include <curses.h> #include <stdlib.h> -#include <string.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -#define DEPTH 2 -#define MIN_HEIGHT VBORDERS + 6 /* 2 buttons 1 text 3 menu */ - enum menumode { CHECKLISTMODE, MENUMODE, @@ -47,47 +40,155 @@ enum menumode { SEPARATORMODE }; -struct lineposition { - unsigned int maxsepstr; - unsigned int maxprefix; - unsigned int xselector; - unsigned int selectorlen; - unsigned int maxdepth; - unsigned int xname; - unsigned int maxname; - unsigned int xdesc; - unsigned int maxdesc; - unsigned int line; -}; - struct privateitem { - bool on; - int group; - int index; + const char *prefix; + bool on; /* menu changes, not API on */ + unsigned int depth; + const char *name; + const char *desc; + const char *bottomdesc; + int group; /* index menu in menugroup */ + int index; /* real item index inside its menu */ enum menumode type; - struct bsddialog_menuitem *item; + wchar_t shortcut; }; -static void -set_on_output(struct bsddialog_conf *conf, int output, int ngroups, - struct bsddialog_menugroup *groups, struct privateitem *pritems) +struct privatemenu { + WINDOW *box; /* only for borders */ + WINDOW *pad; /* pad for the private items */ + int ypad; /* start pad line */ + int ys, ye, xs, xe; /* pad pos */ + unsigned int xselector; /* [] */ + unsigned int xname; /* real x: xname + item.depth */ + unsigned int xdesc; /* real x: xdesc + item.depth */ + unsigned int line; /* wpad: prefix [] depth name desc */ + unsigned int apimenurows; + unsigned int menurows; /* real menurows after menu_size_position() */ + int nitems; /* total nitems (all groups * all items) */ + struct privateitem *pritems; + int sel; /* current focus item, can be -1 */ + bool hasbottomdesc; +}; + +static enum menumode +getmode(enum menumode mode, struct bsddialog_menugroup group) { + if (mode == MIXEDLISTMODE) { + if (group.type == BSDDIALOG_SEPARATOR) + mode = SEPARATORMODE; + else if (group.type == BSDDIALOG_RADIOLIST) + mode = RADIOLISTMODE; + else if (group.type == BSDDIALOG_CHECKLIST) + mode = CHECKLISTMODE; + } + + return (mode); +} + +static int +build_privatemenu(struct bsddialog_conf *conf, struct privatemenu *m, + enum menumode mode, unsigned int ngroups, + struct bsddialog_menugroup *groups) +{ + bool onetrue; int i, j, abs; + unsigned int maxsepstr, maxprefix, selectorlen, maxdepth; + unsigned int maxname, maxdesc; + struct bsddialog_menuitem *item; + struct privateitem *pritem; - if (output != BSDDIALOG_OK && !conf->menu.on_without_ok) - return; + /* nitems and fault checks */ + CHECK_ARRAY(ngroups, groups); + m->nitems = 0; + for (i = 0; i < (int)ngroups; i++) { + CHECK_ARRAY(groups[i].nitems, groups[i].items); + m->nitems += (int)groups[i].nitems; + } - for(i = abs = 0; i < ngroups; i++) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - abs += groups[i].nitems; - continue; - } + /* alloc and set private items */ + m->pritems = calloc(m->nitems, sizeof (struct privateitem)); + if (m->pritems == NULL) + RETURN_ERROR("Cannot allocate memory for internal menu items"); + m->hasbottomdesc = false; + abs = 0; + for (i = 0; i < (int)ngroups; i++) { + onetrue = false; + for (j = 0; j < (int)groups[i].nitems; j++) { + item = &groups[i].items[j]; + pritem = &m->pritems[abs]; + + if (getmode(mode, groups[i]) == MENUMODE) { + m->pritems[abs].on = false; + } else if (getmode(mode, groups[i]) == RADIOLISTMODE) { + m->pritems[abs].on = onetrue ? false : item->on; + if (m->pritems[abs].on) + onetrue = true; + } else { /* CHECKLISTMODE */ + m->pritems[abs].on = item->on; + } + pritem->group = i; + pritem->index = j; + pritem->type = getmode(mode, groups[i]); + + pritem->prefix = CHECK_STR(item->prefix); + pritem->depth = item->depth; + pritem->name = CHECK_STR(item->name); + pritem->desc = CHECK_STR(item->desc); + pritem->bottomdesc = CHECK_STR(item->bottomdesc); + if (item->bottomdesc != NULL) + m->hasbottomdesc = true; + + mbtowc(&pritem->shortcut, conf->menu.no_name ? + pritem->desc : pritem->name, MB_CUR_MAX); - for(j = 0; j < (int)groups[i].nitems; j++) { - groups[i].items[j].on = pritems[abs].on; abs++; } } + + /* positions */ + m->xselector = m->xname = m->xdesc = m->line = 0; + maxsepstr = maxprefix = selectorlen = maxdepth = maxname = maxdesc = 0; + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type == RADIOLISTMODE || + m->pritems[i].type == CHECKLISTMODE) + selectorlen = 4; + + if (m->pritems[i].type == SEPARATORMODE) { + maxsepstr = MAX(maxsepstr, + strcols(m->pritems[i].name) + + strcols(m->pritems[i].desc)); + continue; + } + + maxprefix = MAX(maxprefix, strcols(m->pritems[i].prefix)); + maxdepth = MAX(maxdepth, m->pritems[i].depth); + maxname = MAX(maxname, strcols(m->pritems[i].name)); + maxdesc = MAX(maxdesc, strcols(m->pritems[i].desc)); + } + maxname = conf->menu.no_name ? 0 : maxname; + maxdesc = conf->menu.no_desc ? 0 : maxdesc; + + m->xselector = maxprefix + (maxprefix != 0 ? 1 : 0); + m->xname = m->xselector + selectorlen; + m->xdesc = maxdepth + m->xname + maxname; + m->xdesc += (maxname != 0 ? 1 : 0); + m->line = MAX(maxsepstr + 3, m->xdesc + maxdesc); + + return (0); +} + +static void +set_return_on(struct privatemenu *m, struct bsddialog_menugroup *groups) +{ + int i; + struct privateitem *pritem; + + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type == SEPARATORMODE) + continue; + pritem = &m->pritems[i]; + groups[pritem->group].items[pritem->index].on = pritem->on; + } } static int getprev(struct privateitem *pritems, int abs) @@ -176,25 +277,17 @@ getfastprev(int menurows, struct privateitem *pritems, int abs) } static int -getnextshortcut(struct bsddialog_conf *conf, int npritems, - struct privateitem *pritems, int abs, int key) +getnextshortcut(int npritems, struct privateitem *pritems, int abs, wint_t key) { - int i, ch, next; + int i, next; next = -1; for (i = 0; i < npritems; i++) { if (pritems[i].type == SEPARATORMODE) continue; - - if (conf->menu.no_name) - ch = pritems[i].item->desc[0]; - else - ch = pritems[i].item->name[0]; - - if (ch == key) { + if (pritems[i].shortcut == (wchar_t)key) { if (i > abs) return (i); - if (i < abs && next == -1) next = i; } @@ -203,81 +296,66 @@ getnextshortcut(struct bsddialog_conf *conf, int npritems, return (next != -1 ? next : abs); } -static enum menumode -getmode(enum menumode mode, struct bsddialog_menugroup group) -{ - if (mode == MIXEDLISTMODE) { - if (group.type == BSDDIALOG_SEPARATOR) - mode = SEPARATORMODE; - else if (group.type == BSDDIALOG_RADIOLIST) - mode = RADIOLISTMODE; - else if (group.type == BSDDIALOG_CHECKLIST) - mode = CHECKLISTMODE; - } - - return (mode); -} - -static void -drawseparators(struct bsddialog_conf *conf, WINDOW *pad, int linelen, - int nitems, struct privateitem *pritems) +static void drawseparators(struct bsddialog_conf *conf, struct privatemenu *m) { - int i, linech, labellen; + int i, realw, labellen; const char *desc, *name; - for (i = 0; i < nitems; i++) { - if (pritems[i].type != SEPARATORMODE) + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type != SEPARATORMODE) continue; if (conf->no_lines == false) { - wattron(pad, t.menu.desccolor); - linech = conf->ascii_lines ? '-' : ACS_HLINE; - mvwhline(pad, i, 0, linech, linelen); - wattroff(pad, t.menu.desccolor); + wattron(m->pad, t.menu.desccolor); + if (conf->ascii_lines) + mvwhline(m->pad, i, 0, '-', m->line); + else + mvwhline_set(m->pad, i, 0, WACS_HLINE, m->line); + wattroff(m->pad, t.menu.desccolor); } - name = pritems[i].item->name; - desc = pritems[i].item->desc; - labellen = strlen(name) + strlen(desc) + 1; - wmove(pad, i, labellen < linelen ? linelen/2 - labellen/2 : 0); - wattron(pad, t.menu.namesepcolor); - waddstr(pad, name); - wattroff(pad, t.menu.namesepcolor); - if (strlen(name) > 0 && strlen(desc) > 0) - waddch(pad, ' '); - wattron(pad, t.menu.descsepcolor); - waddstr(pad, desc); - wattroff(pad, t.menu.descsepcolor); + name = m->pritems[i].name; + desc = m->pritems[i].desc; + realw = m->xe - m->xs; + labellen = strcols(name) + strcols(desc) + 1; + wmove(m->pad, i, (labellen < realw) ? realw/2 - labellen/2 : 0); + wattron(m->pad, t.menu.sepnamecolor); + waddstr(m->pad, name); + wattroff(m->pad, t.menu.sepnamecolor); + if (strcols(name) > 0 && strcols(desc) > 0) + waddch(m->pad, ' '); + wattron(m->pad, t.menu.sepdesccolor); + waddstr(m->pad, desc); + wattroff(m->pad, t.menu.sepdesccolor); } } static void -drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, - struct lineposition pos, struct privateitem *pritem, bool focus) +drawitem(struct bsddialog_conf *conf, struct privatemenu *m, int y, bool focus) { int colordesc, colorname, colorshortcut; - const char *shortcut; - struct bsddialog_menuitem *item; + struct privateitem *pritem; - item = pritem->item; + pritem = &m->pritems[y]; /* prefix */ - if (item->prefix != NULL && item->prefix[0] != '\0') - mvwaddstr(pad, y, 0, item->prefix); + wattron(m->pad, focus ? t.menu.f_prefixcolor : t.menu.prefixcolor); + mvwaddstr(m->pad, y, 0, pritem->prefix); + wattroff(m->pad, focus ? t.menu.f_prefixcolor : t.menu.prefixcolor); /* selector */ - wmove(pad, y, pos.xselector); - wattron(pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); + wmove(m->pad, y, m->xselector); + wattron(m->pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); if (pritem->type == CHECKLISTMODE) - wprintw(pad, "[%c]", pritem->on ? 'X' : ' '); + wprintw(m->pad, "[%c]", pritem->on ? 'X' : ' '); if (pritem->type == RADIOLISTMODE) - wprintw(pad, "(%c)", pritem->on ? '*' : ' '); - wattroff(pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); + wprintw(m->pad, "(%c)", pritem->on ? '*' : ' '); + wattroff(m->pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); /* name */ colorname = focus ? t.menu.f_namecolor : t.menu.namecolor; if (conf->menu.no_name == false) { - wattron(pad, colorname); - mvwaddstr(pad, y, pos.xname + item->depth * DEPTH, item->name); - wattroff(pad, colorname); + wattron(m->pad, colorname); + mvwaddstr(m->pad, y, m->xname + pritem->depth, pritem->name); + wattroff(m->pad, colorname); } /* description */ @@ -287,145 +365,137 @@ drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, colordesc = focus ? t.menu.f_desccolor : t.menu.desccolor; if (conf->menu.no_desc == false) { - wattron(pad, colordesc); + wattron(m->pad, colordesc); if (conf->menu.no_name) - mvwaddstr(pad, y, pos.xname + item->depth * DEPTH, - item->desc); + mvwaddstr(m->pad, y, m->xname + pritem->depth, + pritem->desc); else - mvwaddstr(pad, y, pos.xdesc, item->desc); - wattroff(pad, colordesc); + mvwaddstr(m->pad, y, m->xdesc, pritem->desc); + wattroff(m->pad, colordesc); } /* shortcut */ if (conf->menu.shortcut_buttons == false) { colorshortcut = focus ? t.menu.f_shortcutcolor : t.menu.shortcutcolor; - wattron(pad, colorshortcut); - - if (conf->menu.no_name) - shortcut = item->desc; - else - shortcut = item->name; - wmove(pad, y, pos.xname + item->depth * DEPTH); - if (shortcut != NULL && shortcut[0] != '\0') - waddch(pad, shortcut[0]); - wattroff(pad, colorshortcut); + wattron(m->pad, colorshortcut); + mvwaddwch(m->pad, y, m->xname + pritem->depth, pritem->shortcut); + wattroff(m->pad, colorshortcut); } /* bottom description */ - move(SCREENLINES - 1, 2); - clrtoeol(); - if (item->bottomdesc != NULL && focus) { - addstr(item->bottomdesc); - refresh(); + if (m->hasbottomdesc) { + move(SCREENLINES - 1, 2); + clrtoeol(); + if (focus) { + attron(t.menu.bottomdesccolor); + addstr(pritem->bottomdesc); + attroff(t.menu.bottomdesccolor); + refresh(); + } } } -static int -menu_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - const char *text, int linelen, unsigned int *menurows, int nitems, - struct buttons bs) +static void update_menubox(struct bsddialog_conf *conf, struct privatemenu *m) { - int htext, wtext, menusize, notext; - - notext = 2; - if (*menurows == BSDDIALOG_AUTOSIZE) { - /* algo 1): grows vertically */ - /* notext = 1; */ - /* algo 2): grows horizontally, better with little screens */ - notext += nitems; - notext = MIN(notext, widget_max_height(conf) - HBORDERS - 3); - } else - notext += *menurows; - - /* cols autosize, rows autosize, rows fullscreen, menu particularity */ - if (cols == BSDDIALOG_AUTOSIZE || rows <= BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, notext, linelen + 4, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } + int h, w; - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, linelen + 4, &bs); + draw_borders(conf, m->box, LOWERED); + getmaxyx(m->box, h, w); - if (rows == BSDDIALOG_AUTOSIZE) { - if (*menurows == 0) { - menusize = widget_max_height(conf) - HBORDERS - - 2 /*buttons*/ - htext; - menusize = MIN(menusize, nitems + 2); - *menurows = menusize - 2 < 0 ? 0 : menusize - 2; - } - else /* h autosize with fixed menurows */ - menusize = *menurows + 2; - - *h = widget_min_height(conf, htext, menusize, true); - /* - * avoid menurows overflow and - * with rows=AUTOSIZE menurows!=0 becomes max-menurows - */ - *menurows = MIN(*h - 6 - htext, (int)*menurows); - } else { - if (*menurows == 0) - *menurows = MIN(*h-6-htext, nitems); - } + if (m->nitems > (int)m->menurows) { + wattron(m->box, t.dialog.arrowcolor); + if (m->ypad > 0) + mvwhline(m->box, 0, 2, UARROW(conf), 3); - return (0); + if ((m->ypad + (int)m->menurows) < m->nitems) + mvwhline(m->box, h-1, 2, DARROW(conf), 3); + + mvwprintw(m->box, h-1, w-6, "%3d%%", + 100 * (m->ypad + m->menurows) / m->nitems); + wattroff(m->box, t.dialog.arrowcolor); + } } -static int -menu_checksize(int rows, int cols, const char *text, int menurows, int nitems, - struct buttons bs) +static int menu_size_position(struct dialog *d, struct privatemenu *m) { - int mincols, textrow, menusize; + int htext, hmenu; - mincols = VBORDERS; - /* buttons */ - mincols += buttons_width(bs); + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + hmenu = (int)(m->menurows == BSDDIALOG_AUTOSIZE) ? + (int)m->nitems : (int)m->menurows; + hmenu += 2; /* menu borders */ /* - * linelen check, comment to allow some hidden col otherwise portconfig - * could not show big menus like www/apache24 + * algo 1: notext = 1 (grows vertically). + * algo 2: notext = hmenu (grows horizontally, better for little term). */ - /* mincols = MAX(mincols, linelen); */ - - if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or " - "name + descripion of the items"); - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, &htext, &d->bs, hmenu, m->line + 4) != 0) + return (BSDDIALOG_ERROR); + /* avoid menurows overflow and menurows becomes "at most menurows" */ + if (d->h - BORDERS - htext - HBUTTONS <= 2 /* menuborders */) + m->menurows = (m->nitems > 0) ? 1 : 0; /* widget_checksize() */ + else + m->menurows = MIN(d->h - BORDERS - htext - HBUTTONS, hmenu) - 2; - if (nitems > 0 && menurows == 0) - RETURN_ERROR("items > 0 but menurows == 0, probably terminal " - "too small"); + /* + * no minw=linelen to avoid big menu fault, then some col can be + * hidden (example portconfig www/apache24). + */ + if (widget_checksize(d->h, d->w, &d->bs, + 2 /* border box */ + MIN(m->menurows, 1), 0) != 0) + return (BSDDIALOG_ERROR); - menusize = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + menusize + textrow) - RETURN_ERROR("Few lines for this menus"); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); return (0); } -/* the caller has to call prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); */ -static void -update_menuwin(struct bsddialog_conf *conf, WINDOW *menuwin, int h, int w, - int totnitems, unsigned int menurows, int ymenupad) +static int mixedlist_redraw(struct dialog *d, struct privatemenu *m) { - draw_borders(conf, menuwin, h, w, LOWERED); - - if (totnitems > (int)menurows) { - wattron(menuwin, t.dialog.arrowcolor); - - if (ymenupad > 0) - mvwprintw(menuwin, 0, 2, "^^^"); - - if ((ymenupad + (int)menurows) < totnitems) - mvwprintw(menuwin, h-1, 2, "vvv"); - - wattroff(menuwin, t.dialog.arrowcolor); - - mvwprintw(menuwin, h-1, w-10, "%3d%%", - 100 * (ymenupad + menurows) / totnitems); + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ } + m->menurows = m->apimenurows; + if (menu_size_position(d, m) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 2/*bmenu*/ + m->menurows + HBUTTONS); + + /* selected item in view*/ + if (m->ypad > m->sel && m->ypad > 0) + m->ypad = m->sel; + if ((int)(m->ypad + m->menurows) <= m->sel) + m->ypad = m->sel - m->menurows + 1; + /* lower pad after a terminal expansion */ + if (m->ypad > 0 && (m->nitems - m->ypad) < (int)m->menurows) + m->ypad = m->nitems - m->menurows; + + update_box(d->conf, m->box, d->y + d->h - 5 - m->menurows, d->x + 2, + m->menurows+2, d->w-4, LOWERED); + update_menubox(d->conf, m); + wnoutrefresh(m->box); + + m->ys = d->y + d->h - 5 - m->menurows + 1; + m->ye = d->y + d->h - 5 ; + if (d->conf->menu.align_left || (int)m->line > d->w - 6) { + m->xs = d->x + 3; + m->xe = m->xs + d->w - 7; + } else { /* center */ + m->xs = d->x + 3 + (d->w-6)/2 - m->line/2; + m->xe = m->xs + d->w - 5; + } + drawseparators(d->conf, m); /* uses xe - xs */ + pnoutrefresh(m->pad, m->ypad, 0, m->ys, m->xs, m->ye, m->xe); + + return (0); } static int @@ -433,330 +503,192 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, enum menumode mode, unsigned int ngroups, struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) { - bool loop, onetrue, movefocus, automenurows, shortcut_butts; - int i, j, y, x, h, w, output, input; - int ymenupad, ys, ye, xs, xe, abs, next, totnitems; - WINDOW *shadow, *widget, *textpad, *menuwin, *menupad; - struct buttons bs; - struct lineposition pos = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - struct bsddialog_menuitem *item; - struct privateitem *pritems; - - shortcut_butts = conf->menu.shortcut_buttons; - - automenurows = (menurows == BSDDIALOG_AUTOSIZE) ? true : false; - - totnitems = 0; - for (i = 0; i < (int)ngroups; i++) { - if (getmode(mode, groups[i]) == RADIOLISTMODE || - getmode(mode, groups[i]) == CHECKLISTMODE) - pos.selectorlen = 3; - - for (j = 0; j < (int)groups[i].nitems; j++) { - totnitems++; - item = &groups[i].items[j]; - - if (groups[i].type == BSDDIALOG_SEPARATOR) { - pos.maxsepstr = MAX(pos.maxsepstr, - strlen(item->name) + strlen(item->desc)); - continue; - } + bool loop, changeitem; + int i, next, retval; + wint_t input; + struct privatemenu m; + struct dialog d; - pos.maxprefix = MAX(pos.maxprefix,strlen(item->prefix)); - pos.maxdepth = MAX(pos.maxdepth, item->depth); - pos.maxname = MAX(pos.maxname, strlen(item->name)); - pos.maxdesc = MAX(pos.maxdesc, strlen(item->desc)); - } - } - pos.maxname = conf->menu.no_name ? 0 : pos.maxname; - pos.maxdesc = conf->menu.no_desc ? 0 : pos.maxdesc; - pos.maxdepth = DEPTH * pos.maxdepth; - - pos.xselector = pos.maxprefix + (pos.maxprefix != 0 ? 1 : 0); - pos.xname = pos.xselector + pos.selectorlen + - (pos.selectorlen > 0 ? 1 : 0); - pos.xdesc = pos.maxdepth + pos.xname + pos.maxname; - pos.xdesc += (pos.maxname != 0 ? 1 : 0); - pos.line = MAX(pos.maxsepstr + 3, pos.xdesc + pos.maxdesc); - - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (menu_autosize(conf, rows, cols, &h, &w, text, pos.line, &menurows, - totnitems, bs) != 0) - return (BSDDIALOG_ERROR); - if (menu_checksize(h, w, text, menurows, totnitems, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); + set_buttons(&d, conf->menu.shortcut_buttons, OK_LABEL, CANCEL_LABEL); + if (d.conf->menu.no_name && d.conf->menu.no_desc) + RETURN_ERROR("Both conf.menu.no_name and conf.menu.no_desc"); - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - shortcut_butts) != 0) + if (build_privatemenu(conf, &m, mode, ngroups, groups) != 0) return (BSDDIALOG_ERROR); - doupdate(); - - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - menurows, x + 1 + w - TEXTHMARGIN); - - menuwin = new_boxed_window(conf, y + h - 5 - menurows, x + 2, - menurows+2, w-4, LOWERED); - - menupad = newpad(totnitems, pos.line); - wbkgd(menupad, t.dialog.color); - - if ((pritems = calloc(totnitems, sizeof (struct privateitem))) == NULL) - RETURN_ERROR("Cannot allocate memory for internal menu items"); - - abs = 0; - for (i = 0; i < (int)ngroups; i++) { - onetrue = false; - for (j = 0; j < (int)groups[i].nitems; j++) { - item = &groups[i].items[j]; - - if (getmode(mode, groups[i]) == MENUMODE) { - pritems[abs].on = false; - } else if (getmode(mode, groups[i]) == RADIOLISTMODE) { - pritems[abs].on = onetrue ? false : item->on; - if (pritems[abs].on) - onetrue = true; - } else { - pritems[abs].on = item->on; - } - pritems[abs].group = i; - pritems[abs].index = j; - pritems[abs].type = getmode(mode, groups[i]); - pritems[abs].item = item; + if ((m.box = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW box menu"); + wbkgd(m.box, t.dialog.color); + m.pad = newpad(m.nitems, m.line); + wbkgd(m.pad, t.dialog.color); - drawitem(conf, menupad, abs, pos, &pritems[abs], false); - abs++; - } - } - drawseparators(conf, menupad, MIN((int)pos.line, w-6), totnitems, - pritems); - abs = getfirst_with_default(totnitems, pritems, ngroups, groups, + for (i = 0; i < m.nitems; i++) + drawitem(conf, &m, i, false); + m.sel = getfirst_with_default(m.nitems, m.pritems, ngroups, groups, focuslist, focusitem); - if (abs >= 0) - drawitem(conf, menupad, abs, pos, &pritems[abs], true); - - ys = y + h - 5 - menurows + 1; - ye = y + h - 5 ; - if (conf->menu.align_left || (int)pos.line > w - 6) { - xs = x + 3; - xe = xs + w - 7; - } else { /* center */ - xs = x + 3 + (w-6)/2 - pos.line/2; - xe = xs + w - 5; - } - - ymenupad = 0; - if ((int)(ymenupad + menurows) - 1 < abs) - ymenupad = abs - menurows + 1; - update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, menurows, - ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + if (m.sel >= 0) + drawitem(d.conf, &m, m.sel, true); + m.ypad = 0; + m.apimenurows = menurows; + if (mixedlist_redraw(&d, &m) != 0) + return (BSDDIALOG_ERROR); - movefocus = false; + changeitem = false; loop = true; while (loop) { - input = getch(); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; - if (abs >= 0 && pritems[abs].type == MENUMODE) - pritems[abs].on = true; - set_on_output(conf, output, ngroups, groups, pritems); + retval = BUTTONVALUE(d.bs); + if (m.sel >= 0 && m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; - if (abs >= 0 && pritems[abs].type == MENUMODE) - pritems[abs].on = true; - set_on_output(conf, output, ngroups, groups, - pritems); + retval = BSDDIALOG_ESC; + if (m.sel >= 0 && + m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - draw_buttons(widget, bs, shortcut_butts); - wrefresh(widget); + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - draw_buttons(widget, bs, shortcut_butts); - wrefresh(widget); - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - draw_buttons(widget, bs, shortcut_butts); - wrefresh(widget); - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - menurows = automenurows ? 0 : menurows; - if (menu_autosize(conf, rows, cols, &h, &w, text, - pos.line, &menurows, totnitems, bs) != 0) + if (mixedlist_redraw(&d, &m) != 0) return (BSDDIALOG_ERROR); - if (menu_checksize(h, w, text, menurows, totnitems, - bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, shortcut_butts) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (mixedlist_redraw(&d, &m) != 0) return (BSDDIALOG_ERROR); - - doupdate(); - - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - menurows, x + 1 + w - TEXTHMARGIN); - - wclear(menuwin); - mvwin(menuwin, y + h - 5 - menurows, x + 2); - wresize(menuwin,menurows+2, w-4); - update_menuwin(conf, menuwin, menurows+2, w-4, - totnitems, menurows, ymenupad); - wrefresh(menuwin); - - ys = y + h - 5 - menurows + 1; - ye = y + h - 5 ; - if (conf->menu.align_left || (int)pos.line > w - 6) { - xs = x + 3; - xe = xs + w - 7; - } else { /* center */ - xs = x + 3 + (w-6)/2 - pos.line/2; - xe = xs + w - 5; - } - - drawseparators(conf, menupad, MIN((int)pos.line, w-6), - totnitems, pritems); - - if ((int)(ymenupad + menurows) - 1 < abs) - ymenupad = abs - menurows + 1; - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); - - refresh(); - break; } - if (abs < 0) + if (m.sel < 0) continue; switch(input) { case KEY_HOME: - next = getnext(totnitems, pritems, -1); - movefocus = next != abs; + next = getnext(m.nitems, m.pritems, -1); + changeitem = next != m.sel; break; + case '-': + case KEY_CTRL('p'): case KEY_UP: - next = getprev(pritems, abs); - movefocus = next != abs; + next = getprev(m.pritems, m.sel); + changeitem = next != m.sel; break; case KEY_PPAGE: - next = getfastprev(menurows, pritems, abs); - movefocus = next != abs; + next = getfastprev(m.menurows, m.pritems, m.sel); + changeitem = next != m.sel; break; case KEY_END: - next = getprev(pritems, totnitems); - movefocus = next != abs; + next = getprev(m.pritems, m.nitems); + changeitem = next != m.sel; break; + case '+': + case KEY_CTRL('n'): case KEY_DOWN: - next = getnext(totnitems, pritems, abs); - movefocus = next != abs; + next = getnext(m.nitems, m.pritems, m.sel); + changeitem = next != m.sel; break; case KEY_NPAGE: - next = getfastnext(menurows, totnitems, pritems, abs); - movefocus = next != abs; + next = getfastnext(m.menurows, m.nitems, m.pritems, m.sel); + changeitem = next != m.sel; break; case ' ': /* Space */ - if (pritems[abs].type == MENUMODE) - break; - else if (pritems[abs].type == CHECKLISTMODE) - pritems[abs].on = !pritems[abs].on; - else { /* RADIOLISTMODE */ - for (i = abs - pritems[abs].index; - i < totnitems && - pritems[i].group == pritems[abs].group; + if (m.pritems[m.sel].type == MENUMODE) { + retval = BUTTONVALUE(d.bs); + m.pritems[m.sel].on = true; + loop = false; + } else if (m.pritems[m.sel].type == CHECKLISTMODE) { + m.pritems[m.sel].on = !m.pritems[m.sel].on; + } else { /* RADIOLISTMODE */ + for (i = m.sel - m.pritems[m.sel].index; + i < m.nitems && + m.pritems[i].group == m.pritems[m.sel].group; i++) { - if (i != abs && pritems[i].on) { - pritems[i].on = false; - drawitem(conf, menupad, i, pos, - &pritems[i], false); + if (i != m.sel && m.pritems[i].on) { + m.pritems[i].on = false; + drawitem(conf, &m, i, false); } } - pritems[abs].on = !pritems[abs].on; + m.pritems[m.sel].on = !m.pritems[m.sel].on; } - drawitem(conf, menupad, abs, pos, &pritems[abs], true); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + drawitem(conf, &m, m.sel, true); + pnoutrefresh(m.pad, m.ypad, 0, m.ys, m.xs, m.ye, m.xe); break; default: - if (shortcut_butts) { - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; - if (pritems[abs].type == MENUMODE) - pritems[abs].on = true; - set_on_output(conf, output, ngroups, - groups, pritems); + if (conf->menu.shortcut_buttons) { + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + if (m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; loop = false; } break; } /* shourtcut items */ - next = getnextshortcut(conf, totnitems, pritems, abs, + next = getnextshortcut(m.nitems, m.pritems, m.sel, input); - movefocus = next != abs; + changeitem = next != m.sel; + } /* end switch get_wch() */ + + if (changeitem) { + drawitem(conf, &m, m.sel, false); + m.sel = next; + drawitem(conf, &m, m.sel, true); + if (m.ypad > m.sel && m.ypad > 0) + m.ypad = m.sel; + if ((int)(m.ypad + m.menurows) <= m.sel) + m.ypad = m.sel - m.menurows + 1; + update_menubox(conf, &m); + wnoutrefresh(m.box); + pnoutrefresh(m.pad, m.ypad, 0, m.ys, m.xs, m.ye, m.xe); + changeitem = false; } + } /* end while (loop) */ - if (movefocus) { - drawitem(conf, menupad, abs, pos, &pritems[abs], false); - abs = next; - drawitem(conf, menupad, abs, pos, &pritems[abs], true); - if (ymenupad > abs && ymenupad > 0) - ymenupad = abs; - if ((int)(ymenupad + menurows) <= abs) - ymenupad = abs - menurows + 1; - update_menuwin(conf, menuwin, menurows+2, w-4, - totnitems, menurows, ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); - movefocus = false; - } - } + set_return_on(&m, groups); if (focuslist != NULL) - *focuslist = abs < 0 ? -1 : pritems[abs].group; + *focuslist = m.sel < 0 ? -1 : m.pritems[m.sel].group; if (focusitem !=NULL) - *focusitem = abs < 0 ? -1 : pritems[abs].index; + *focusitem = m.sel < 0 ? -1 : m.pritems[m.sel].index; - delwin(menupad); - delwin(menuwin); - end_dialog(conf, shadow, widget, textpad); - free(pritems); + if (m.hasbottomdesc && conf->clear) { + move(SCREENLINES - 1, 2); + clrtoeol(); + } + delwin(m.pad); + delwin(m.box); + end_dialog(&d); + free(m.pritems); - return (output); + return (retval); } /* API */ @@ -765,12 +697,12 @@ bsddialog_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int ngroups, struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) { - int output; + int retval; - output = do_mixedlist(conf, text, rows, cols, menurows, MIXEDLISTMODE, + retval = do_mixedlist(conf, text, rows, cols, menurows, MIXEDLISTMODE, ngroups, groups, focuslist, focusitem); - return (output); + return (retval); } int @@ -778,14 +710,15 @@ bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem) { - int output, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_CHECKLIST /* unused */, nitems, items}; + BSDDIALOG_CHECKLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } int @@ -793,14 +726,15 @@ bsddialog_menu(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem) { - int output, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_CHECKLIST /* unused */, nitems, items}; + BSDDIALOG_CHECKLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } int @@ -808,12 +742,13 @@ bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem) { - int output, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_RADIOLIST /* unused */, nitems, items}; + BSDDIALOG_RADIOLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } diff --git a/contrib/bsddialog/lib/messagebox.c b/contrib/bsddialog/lib/messagebox.c index 24b34ccbce97..5132b1b089b8 100644 --- a/contrib/bsddialog/lib/messagebox.c +++ b/contrib/bsddialog/lib/messagebox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,184 +25,172 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - #include <curses.h> -#include <string.h> #include "bsddialog.h" +#include "bsddialog_theme.h" #include "lib_util.h" -static int -message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, const char *text, struct buttons bs) -{ - int htext, wtext; +struct scroll { + int ypad; /* y scrollable pad */ + int htext; /* real h text to draw, to use with htextpad */ + int htextpad; /* h textpad, draw_dialog() set at least 1 */ + int printrows; /* h - BORDER - HBUTTONS - BORDER */ +}; - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, 0, SCREENCOLS/2, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); +static void textupdate(struct dialog *d, struct scroll *s) +{ + if (s->htext > 0 && s->htextpad > s->printrows) { + wattron(d->widget, t.dialog.arrowcolor); + mvwprintw(d->widget, d->h - HBUTTONS - BORDER, + d->w - 4 - TEXTHMARGIN - BORDER, + "%3d%%", 100 * (s->ypad + s->printrows) / s->htextpad); + wattroff(d->widget, t.dialog.arrowcolor); + wnoutrefresh(d->widget); } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, 0, &bs); - - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 0, true); - - return (0); + rtextpad(d, s->ypad, 0, 0, HBUTTONS); } -static int message_checksize(int rows, int cols, struct buttons bs) +static int message_size_position(struct dialog *d, int *htext) { - int mincols; + int minw; - mincols = VBORDERS; - mincols += buttons_width(bs); - - if (cols < mincols) - RETURN_ERROR("Few cols, Msgbox and Yesno need at least width " - "for borders, buttons and spaces between buttons"); - - if (rows < HBORDERS + 2 /*buttons*/) - RETURN_ERROR("Msgbox and Yesno need at least height 4"); + 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 < 0) ? htext : NULL, &d->bs, 0, 0) != 0) + return (BSDDIALOG_ERROR); + minw = (*htext > 0) ? 1 + TEXTHMARGINS : 0 ; + if (widget_checksize(d->h, d->w, &d->bs, MIN(*htext, 1), 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); } -static void -textupdate(WINDOW *widget, WINDOW *textpad, int htextpad, int ytextpad) +static int message_draw(struct dialog *d, struct scroll *s) { - int y, x, h, w; + int unused; - getbegyx(widget, y, x); - getmaxyx(widget, h, w); - - if (htextpad > h - 4) { - mvwprintw(widget, h-3, w-6, "%3d%%", - 100 * (ytextpad+h-4)/ htextpad); - wnoutrefresh(widget); + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ } + if (message_size_position(d, &s->htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + + s->printrows = d->h - BORDER - HBUTTONS - BORDER; + s->ypad = 0; + getmaxyx(d->textpad, s->htextpad, unused); + (void)unused; /* fix unused error */ - pnoutrefresh(textpad, ytextpad, 0, y+1, x+2, y+h-4, x+w-2); + return (0); } static int do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, - struct buttons bs) + const char *oklabel, const char *cancellabel) { bool loop; - int y, x, h, w, input, output, ytextpad, htextpad, unused; - WINDOW *widget, *textpad, *shadow; + int retval; + wint_t input; + struct scroll s; + struct dialog d; - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - if (message_checksize(h, w, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + set_buttons(&d, true, oklabel, cancellabel); + s.htext = -1; + if (message_draw(&d, &s) != 0) return (BSDDIALOG_ERROR); - ytextpad = 0; - getmaxyx(textpad, htextpad, unused); - unused++; /* fix unused error */ - textupdate(widget, textpad, htextpad, ytextpad); loop = true; while (loop) { + textupdate(&d, &s); doupdate(); - input = getch(); + if (get_wch(&input) == ERR) + continue; switch (input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ - if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + if (d.conf->key.enable_esc) { + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - draw_buttons(widget, bs, true); - wnoutrefresh(widget); + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - draw_buttons(widget, bs, true); - wnoutrefresh(widget); - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; - case KEY_RIGHT: - if (bs.curr < (int)bs.nbuttons - 1) { - bs.curr++; - draw_buttons(widget, bs, true); - wnoutrefresh(widget); - } + case '-': + case KEY_CTRL('p'): + case KEY_UP: + if (s.ypad > 0) + s.ypad--; + break; + case '+': + case KEY_CTRL('n'): + case KEY_DOWN: + if (s.ypad + s.printrows < s.htextpad) + s.ypad++; + break; + case KEY_HOME: + s.ypad = 0; + break; + case KEY_END: + s.ypad = MAX(s.htextpad - s.printrows, 0); + break; + case KEY_PPAGE: + s.ypad = MAX(s.ypad - s.printrows, 0); + break; + case KEY_NPAGE: + s.ypad += s.printrows; + if (s.ypad + s.printrows > s.htextpad) + s.ypad = s.htextpad - s.printrows; break; case KEY_F(1): - if (conf->key.f1_file == NULL && - conf->key.f1_message == NULL) + if (d.conf->key.f1_file == NULL && + d.conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (message_autosize(conf, rows, cols, &h, &w, text, - bs) != 0) - return (BSDDIALOG_ERROR); - if (message_checksize(h, w, bs) != 0) + if (f1help_dialog(d.conf) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (message_draw(&d, &s) != 0) return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, true) != 0) - return (BSDDIALOG_ERROR); - - getmaxyx(textpad, htextpad, unused); - textupdate(widget, textpad, htextpad, ytextpad); - - /* Important to fix grey lines expanding screen */ - refresh(); break; - case KEY_UP: - if (ytextpad == 0) - break; - ytextpad--; - textupdate(widget, textpad, htextpad, ytextpad); - break; - case KEY_DOWN: - if (ytextpad + h - 4 >= htextpad) - break; - ytextpad++; - textupdate(widget, textpad, htextpad, ytextpad); + case KEY_CTRL('l'): + case KEY_RESIZE: + if (message_draw(&d, &s) != 0) + return (BSDDIALOG_ERROR); break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - end_dialog(conf, shadow, widget, textpad); + end_dialog(&d); - return (output); + return (retval); } /* API */ @@ -210,20 +198,34 @@ int bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows, int cols) { - struct buttons bs; - - get_buttons(conf, &bs, BUTTON_OK_LABEL, NULL); - - return (do_message(conf, text, rows, cols, bs)); + return (do_message(conf, text, rows, cols, OK_LABEL, NULL)); } int bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows, int cols) { - struct buttons bs; + return (do_message(conf, text, rows, cols, "Yes", "No")); +} - get_buttons(conf, &bs, "Yes", "No"); +int +bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, + int cols) +{ + int htext; + struct dialog d; - return (do_message(conf, text, rows, cols, bs)); -}
\ No newline at end of file + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + htext = -1; + if (message_size_position(&d, &htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d) != 0) + return (BSDDIALOG_ERROR); + TEXTPAD(&d, 0); + doupdate(); + + end_dialog(&d); + + return (BSDDIALOG_OK); +} diff --git a/contrib/bsddialog/lib/textbox.c b/contrib/bsddialog/lib/textbox.c index 69eff7c0bc2e..ca3eb69fff52 100644 --- a/contrib/bsddialog/lib/textbox.c +++ b/contrib/bsddialog/lib/textbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,199 +25,248 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - #include <curses.h> -#include <string.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -static void -textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, int hpad, int wpad, struct buttons bs) +struct scrolltext { + WINDOW *pad; + int ypad; + int xpad; + int ys; + int ye; + int xs; + int xe; + int hpad; + int wpad; + int margin; /* 2 with multicolumn char, 0 otherwise */ + int printrows; /* d.h - BORDERS - HBUTTONS */ +}; + +static void updateborders(struct dialog *d, struct scrolltext *st) { - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, 0, wpad, &bs); + chtype arrowch; + cchar_t borderch; + + if (d->conf->no_lines) + setcchar(&borderch, L" ", 0, 0, NULL); + else if (d->conf->ascii_lines) + setcchar(&borderch, L"|", 0, 0, NULL); + else + borderch = *WACS_VLINE; - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, 0, hpad, true); + if (st->xpad > 0) { + arrowch = LARROW(d->conf) | t.dialog.arrowcolor; + mvwvline(d->widget, (d->h / 2) - 2, 0, arrowch, 4); + } else { + wattron(d->widget, t.dialog.lineraisecolor); + mvwvline_set(d->widget, (d->h / 2) - 2, 0, &borderch, 4); + wattroff(d->widget, t.dialog.lineraisecolor); + } + + if (st->xpad + d->w - 2 - st->margin < st->wpad) { + arrowch = RARROW(d->conf) | t.dialog.arrowcolor; + mvwvline(d->widget, (d->h / 2) - 2, d->w - 1, arrowch, 4); + } else { + wattron(d->widget, t.dialog.linelowercolor); + mvwvline_set(d->widget, (d->h / 2) - 2, d->w - 1, &borderch, 4); + wattroff(d->widget, t.dialog.linelowercolor); + } + + if (st->hpad > d->h - 4) { + wattron(d->widget, t.dialog.arrowcolor); + mvwprintw(d->widget, d->h - 3, d->w - 6, + "%3d%%", 100 * (st->ypad + d->h - 4) / st->hpad); + wattroff(d->widget, t.dialog.arrowcolor); + } } -static int -textbox_checksize(int rows, int cols, int hpad, struct buttons bs) +static int textbox_size_position(struct dialog *d, struct scrolltext *st) { - int mincols; + int minw; - mincols = VBORDERS + bs.sizebutton; + 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, NULL, &d->bs, st->hpad, st->wpad + st->margin) != 0) + return (BSDDIALOG_ERROR); + minw = (st->wpad > 0) ? 2 /*multicolumn char*/ + st->margin : 0 ; + if (widget_checksize(d->h, d->w, &d->bs, MIN(st->hpad, 1), minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); - if (cols < mincols) - RETURN_ERROR("Few cols for the textbox"); + return (0); +} + +static int textbox_draw(struct dialog *d, struct scrolltext *st) +{ + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (textbox_size_position(d, st) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ - if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0)) - RETURN_ERROR("Few rows for the textbox"); + st->ys = d->y + 1; + st->xs = (st->margin == 0) ? d->x + 1 : d->x + 2; + st->ye = st->ys + d->h - 5; + st->xe = st->xs + d->w - 3 - st->margin; + st->ypad = st->xpad = 0; + st->printrows = d->h-4; return (0); } /* API */ int -bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, +bsddialog_textbox(struct bsddialog_conf *conf, const char *file, int rows, int cols) { - bool loop; - int i, output, input; - int y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows; + bool loop, has_multicol_ch; + int i, retval; + unsigned int defaulttablen, linecols; + wint_t input; char buf[BUFSIZ]; FILE *fp; - struct buttons bs; - WINDOW *shadow, *widget, *pad; + struct scrolltext st; + struct dialog d; + if (file == NULL) + RETURN_ERROR("*file is NULL"); if ((fp = fopen(file, "r")) == NULL) - RETURN_ERROR("Cannot open file"); + RETURN_FMTERROR("Cannot open file \"%s\"", file); + + if (prepare_dialog(conf, "" /* fake */, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, "EXIT", NULL); - hpad = 1; - wpad = 1; - pad = newpad(hpad, wpad); - wbkgd(pad, t.dialog.color); + defaulttablen = TABSIZE; + if (conf->text.tablen > 0) + set_tabsize(conf->text.tablen); + st.hpad = 1; + st.wpad = 1; + st.pad = newpad(st.hpad, st.wpad); + wbkgd(st.pad, t.dialog.color); + st.margin = 0; i = 0; while (fgets(buf, BUFSIZ, fp) != NULL) { - if ((int) strlen(buf) > wpad) { - wpad = strlen(buf); - wresize(pad, hpad, wpad); + if (str_props(buf, &linecols, &has_multicol_ch) != 0) + continue; + if ((int)linecols > st.wpad) { + st.wpad = linecols; + wresize(st.pad, st.hpad, st.wpad); } - if (i > hpad-1) { - hpad++; - wresize(pad, hpad, wpad); + if (i > st.hpad-1) { + st.hpad++; + wresize(st.pad, st.hpad, st.wpad); } - mvwaddstr(pad, i, 0, buf); + mvwaddstr(st.pad, i, 0, buf); i++; + if (has_multicol_ch) + st.margin = 2; } fclose(fp); + set_tabsize(defaulttablen); /* reset because it is curses global */ - bs.nbuttons = 1; - bs.label[0] = "EXIT"; - if (conf->button.ok_label != NULL) - bs.label[0] = conf->button.ok_label; - bs.value[0] = BSDDIALOG_OK; - bs.curr = 0; - bs.sizebutton = strlen(bs.label[0]) + 2; - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, bs); - if (textbox_checksize(h, w, hpad, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, NULL, NULL, &bs, - true) != 0) + if (textbox_draw(&d, &st) != 0) return (BSDDIALOG_ERROR); - ys = y + 1; - xs = x + 1; - ye = ys + h - 5; - xe = xs + w - 3; - ypad = xpad = 0; - printrows = h-4; loop = true; while (loop) { - wnoutrefresh(widget); - pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); - doupdate(); - input = getch(); + updateborders(&d, &st); + /* + * Trick, overflow multicolumn charchter right border: + * wnoutrefresh(widget); + * pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); + * doupdate(); + */ + wrefresh(d.widget); + prefresh(st.pad, st.ypad, st.xpad, st.ys, st.xs, st.ye, st.xe); + if (get_wch(&input) == ERR) + continue; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + break; /* loop */ + } switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = BSDDIALOG_OK; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; + case '\t': /* TAB */ + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); + break; case KEY_HOME: - ypad = 0; + st.ypad = 0; break; case KEY_END: - ypad = hpad - printrows; - ypad = ypad < 0 ? 0 : ypad; + st.ypad = MAX(st.hpad - st.printrows, 0); break; case KEY_PPAGE: - ypad -= printrows; - ypad = ypad < 0 ? 0 : ypad; + st.ypad = MAX(st.ypad - st.printrows, 0); break; case KEY_NPAGE: - ypad += printrows; - if (ypad + printrows > hpad) - ypad = hpad - printrows; + st.ypad += st.printrows; + if (st.ypad + st.printrows > st.hpad) + st.ypad = st.hpad - st.printrows; break; case '0': - xpad = 0; + st.xpad = 0; + break; case KEY_LEFT: case 'h': - xpad = xpad > 0 ? xpad - 1 : 0; + st.xpad = MAX(st.xpad - 1, 0); break; case KEY_RIGHT: case 'l': - xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad; + if (st.xpad + d.w - 2 - st.margin < st.wpad) + st.xpad++; break; case KEY_UP: case 'k': - ypad = ypad > 0 ? ypad - 1 : 0; + st.ypad = MAX(st.ypad - 1, 0); break; case KEY_DOWN: case'j': - ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad; + if (st.ypad + st.printrows <= st.hpad -1) + st.ypad++; break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, - bs); - if (textbox_checksize(h, w, hpad, bs) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (textbox_draw(&d, &st) != 0) return (BSDDIALOG_ERROR); - - ys = y + 1; - xs = x + 1; - ye = ys + h - 5; - xe = xs + w - 3; - ypad = xpad = 0; - printrows = h - 4; - - if (update_dialog(conf, shadow, widget, y, x, h, w, - NULL, NULL, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (textbox_draw(&d, &st) != 0) return (BSDDIALOG_ERROR); - - /* Important to fix grey lines expanding screen */ - refresh(); break; - default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; - loop = false; - } } } - end_dialog(conf, shadow, widget, pad); + delwin(st.pad); + end_dialog(&d); - return (output); -}
\ No newline at end of file + return (retval); +} diff --git a/contrib/bsddialog/lib/theme.c b/contrib/bsddialog/lib/theme.c index 20b1e35428dd..04f85b2455fa 100644 --- a/contrib/bsddialog/lib/theme.c +++ b/contrib/bsddialog/lib/theme.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 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 @@ -32,137 +32,117 @@ #include "lib_util.h" #define GET_COLOR(bg, fg) (COLOR_PAIR(bg * 8 + fg +1)) +#define WHITE GET_COLOR(COLOR_WHITE, COLOR_BLACK) +#define BLACK GET_COLOR(COLOR_WHITE, COLOR_BLACK) | A_REVERSE +#define NFLAGS 6 struct bsddialog_theme t; +bool hastermcolors; -static struct bsddialog_theme bsddialogtheme = { -#define bgwidget COLOR_WHITE -#define bgcurr COLOR_YELLOW - .screen.color = GET_COLOR(COLOR_BLACK, COLOR_CYAN), - - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, +struct flag_converter { + unsigned int public; + unsigned int private; +}; - .dialog.delimtitle = true, - .dialog.titlecolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .dialog.lineraisecolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.color = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.arrowcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - - .menu.f_selectorcolor = GET_COLOR(COLOR_BLACK, bgcurr), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.f_desccolor = GET_COLOR(COLOR_WHITE, bgcurr), - .menu.desccolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.f_namecolor = GET_COLOR(COLOR_BLACK, bgcurr), - .menu.namecolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.namesepcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.descsepcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, bgcurr), - .menu.shortcutcolor = GET_COLOR(COLOR_RED, bgwidget), - - .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN), - .form.readonlycolor = GET_COLOR(COLOR_CYAN,COLOR_WHITE), - - .bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .bar.color = GET_COLOR(COLOR_BLUE, COLOR_WHITE), - - .button.hmargin = 3, - .button.leftdelim = '[', - .button.rightdelim = ']', - .button.f_delimcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .button.delimcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .button.f_color = GET_COLOR(COLOR_BLACK, bgcurr) | A_UNDERLINE, - .button.color = GET_COLOR(COLOR_BLACK, bgwidget) | A_UNDERLINE, - .button.f_shortcutcolor = GET_COLOR(COLOR_RED, bgcurr) | A_UNDERLINE, - .button.shortcutcolor = GET_COLOR(COLOR_RED, bgwidget) | A_UNDERLINE +static struct flag_converter flagconv[NFLAGS] = { + {BSDDIALOG_BLINK, A_BLINK}, + {BSDDIALOG_BOLD, A_BOLD}, + {BSDDIALOG_HALFBRIGHT, A_DIM}, + {BSDDIALOG_HIGHLIGHT, A_STANDOUT}, + {BSDDIALOG_REVERSE, A_REVERSE}, + {BSDDIALOG_UNDERLINE, A_UNDERLINE} }; static struct bsddialog_theme blackwhite = { -#define fg COLOR_WHITE -#define bk COLOR_BLACK - .screen.color = GET_COLOR(fg, bk), + .screen.color = WHITE, - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), + .shadow.y = 1, + .shadow.x = 2, .dialog.delimtitle = true, - .dialog.titlecolor = GET_COLOR(fg, bk), - .dialog.lineraisecolor = GET_COLOR(fg, bk), - .dialog.linelowercolor = GET_COLOR(fg, bk), - .dialog.color = GET_COLOR(fg, bk), - .dialog.bottomtitlecolor = GET_COLOR(fg, bk), - .dialog.arrowcolor = GET_COLOR(fg, bk), - - .menu.f_selectorcolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.selectorcolor = GET_COLOR(fg, bk), - .menu.f_desccolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.desccolor = GET_COLOR(fg, bk), - .menu.f_namecolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.namecolor = GET_COLOR(fg, bk), - .menu.namesepcolor = GET_COLOR(fg, bk), - .menu.descsepcolor = GET_COLOR(fg, bk), - .menu.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .menu.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE, - - .form.f_fieldcolor = GET_COLOR(fg, bk) | A_REVERSE, - .form.fieldcolor = GET_COLOR(fg, bk), - .form.readonlycolor = GET_COLOR(fg, bk), - - .bar.f_color = GET_COLOR(fg, bk) | A_REVERSE, - .bar.color = GET_COLOR(fg, bk), - - .button.hmargin = 3, + .dialog.titlecolor = WHITE, + .dialog.lineraisecolor = WHITE, + .dialog.linelowercolor = WHITE, + .dialog.color = WHITE, + .dialog.bottomtitlecolor = WHITE, + .dialog.arrowcolor = WHITE, + + .menu.f_prefixcolor = WHITE, + .menu.prefixcolor = WHITE, + .menu.f_selectorcolor = BLACK, + .menu.selectorcolor = WHITE, + .menu.f_desccolor = BLACK, + .menu.desccolor = WHITE, + .menu.f_namecolor = BLACK, + .menu.namecolor = WHITE, + .menu.f_shortcutcolor = BLACK | A_UNDERLINE, + .menu.shortcutcolor = WHITE | A_UNDERLINE, + .menu.bottomdesccolor = WHITE, + .menu.sepnamecolor = WHITE, + .menu.sepdesccolor = WHITE, + + .form.f_fieldcolor = BLACK, + .form.fieldcolor = WHITE, + .form.readonlycolor = WHITE, + .form.bottomdesccolor = WHITE, + + .bar.f_color = BLACK, + .bar.color = WHITE, + + .button.minmargin = 1, + .button.maxmargin = 5, .button.leftdelim = '[', .button.rightdelim = ']', - .button.f_delimcolor = GET_COLOR(fg, bk), - .button.delimcolor = GET_COLOR(fg, bk), - .button.f_color = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .button.color = GET_COLOR(fg, bk) | A_UNDERLINE, - .button.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .button.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE + .button.f_delimcolor = WHITE, + .button.delimcolor = WHITE, + .button.f_color = BLACK, + .button.color = WHITE, + .button.f_shortcutcolor = BLACK | A_UNDERLINE | A_BOLD, + .button.shortcutcolor = WHITE | A_UNDERLINE | A_BOLD }; -static struct bsddialog_theme dialogtheme = { - .screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD, +static struct bsddialog_theme flat = { + .screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD, - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), + .shadow.y = 1, + .shadow.x = 2, - .dialog.delimtitle = false, + .dialog.delimtitle = true, .dialog.titlecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, - .dialog.lineraisecolor = GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD, - .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, + .dialog.lineraisecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .dialog.color = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, - .dialog.arrowcolor = GET_COLOR(COLOR_GREEN, COLOR_WHITE), - - .menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), - .menu.f_namecolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .menu.namecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), - .menu.namesepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.descsepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_BLUE), - .menu.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - - .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, - .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD, - .form.readonlycolor = GET_COLOR(COLOR_CYAN, COLOR_WHITE)| A_BOLD, + .dialog.arrowcolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), + + .menu.f_prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.selectorcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_namecolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.namecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), + .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_BLUE), + .menu.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.sepnamecolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.sepdesccolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + + .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, + .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD, + .form.readonlycolor = GET_COLOR(COLOR_CYAN, COLOR_WHITE)| A_BOLD, + .form.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), .bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, .bar.color = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, - .button.hmargin = 3, - .button.leftdelim = '<', - .button.rightdelim = '>', + .button.minmargin = 1, + .button.maxmargin = 5, + .button.leftdelim = '[', + .button.rightdelim = ']', .button.f_delimcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, .button.delimcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .button.f_color = GET_COLOR(COLOR_YELLOW, COLOR_BLUE) | A_BOLD, @@ -177,8 +157,8 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) dst->screen.color = src->screen.color; dst->shadow.color = src->shadow.color; - dst->shadow.h = src->shadow.h; - dst->shadow.w = src->shadow.w; + dst->shadow.y = src->shadow.y; + dst->shadow.x = src->shadow.x; dst->dialog.delimtitle = src->dialog.delimtitle; dst->dialog.titlecolor = src->dialog.titlecolor; @@ -188,25 +168,30 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) dst->dialog.bottomtitlecolor = src->dialog.bottomtitlecolor; dst->dialog.arrowcolor = src->dialog.arrowcolor; + dst->menu.f_prefixcolor = src->menu.f_prefixcolor; + dst->menu.prefixcolor = src->menu.prefixcolor; dst->menu.f_selectorcolor = src->menu.f_selectorcolor; dst->menu.selectorcolor = src->menu.selectorcolor; dst->menu.f_desccolor = src->menu.f_desccolor; dst->menu.desccolor = src->menu.desccolor; dst->menu.f_namecolor = src->menu.f_namecolor; dst->menu.namecolor = src->menu.namecolor; - dst->menu.namesepcolor = src->menu.namesepcolor; - dst->menu.descsepcolor = src->menu.descsepcolor; dst->menu.f_shortcutcolor = src->menu.f_shortcutcolor; dst->menu.shortcutcolor = src->menu.shortcutcolor; + dst->menu.bottomdesccolor = src->menu.bottomdesccolor; + dst->menu.sepnamecolor = src->menu.sepnamecolor; + dst->menu.sepdesccolor = src->menu.sepdesccolor; - dst->form.f_fieldcolor = src->form.f_fieldcolor; - dst->form.fieldcolor = src->form.fieldcolor; - dst->form.readonlycolor = src->form.readonlycolor; + dst->form.f_fieldcolor = src->form.f_fieldcolor; + dst->form.fieldcolor = src->form.fieldcolor; + dst->form.readonlycolor = src->form.readonlycolor; + dst->form.bottomdesccolor = src->form.bottomdesccolor; dst->bar.f_color = src->bar.f_color; dst->bar.color = src->bar.color; - dst->button.hmargin = src->button.hmargin; + dst->button.minmargin = src->button.minmargin; + dst->button.maxmargin = src->button.maxmargin; dst->button.leftdelim = src->button.leftdelim; dst->button.rightdelim = src->button.rightdelim; dst->button.f_delimcolor = src->button.f_delimcolor; @@ -217,68 +202,87 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) dst->button.shortcutcolor = src->button.shortcutcolor; bkgd(dst->screen.color); - refresh(); } /* API */ int bsddialog_get_theme(struct bsddialog_theme *theme) { - if (theme == NULL) - RETURN_ERROR("theme is NULL"); - if (sizeof(*theme) != sizeof(struct bsddialog_theme)) - RETURN_ERROR("Bad suze struct bsddialog_theme"); - + CHECK_PTR(theme); set_theme(theme, &t); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_theme(struct bsddialog_theme *theme) { - if (theme == NULL) - RETURN_ERROR("theme is NULL"); - if (sizeof(*theme) != sizeof(struct bsddialog_theme)) - RETURN_ERROR("Bad size struct bsddialog_theme"); - + CHECK_PTR(theme); set_theme(&t, theme); + refresh(); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme) { - - if (newtheme == BSDDIALOG_THEME_FLAT) { - bsddialog_set_theme(&dialogtheme); - t.dialog.lineraisecolor = t.dialog.linelowercolor; - t.dialog.delimtitle = true; - t.button.leftdelim = '['; - t.button.rightdelim = ']'; + if (newtheme == BSDDIALOG_THEME_3D) { + set_theme(&t, &flat); + t.dialog.lineraisecolor = + GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD; + t.dialog.delimtitle = false; + t.dialog.bottomtitlecolor = t.dialog.bottomtitlecolor | A_BOLD; + } else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) { + set_theme(&t, &blackwhite); + } else if (newtheme == BSDDIALOG_THEME_FLAT) { + set_theme(&t, &flat); + } else { + RETURN_FMTERROR("Unknown default theme (%d), " + "to use enum bsddialog_default_theme", + newtheme); } - else if (newtheme == BSDDIALOG_THEME_BSDDIALOG) - bsddialog_set_theme(&bsddialogtheme); - else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) - bsddialog_set_theme(&blackwhite); - else if (newtheme == BSDDIALOG_THEME_DIALOG) - bsddialog_set_theme(&dialogtheme); - else - RETURN_ERROR("Unknow default theme"); - - return (0); + refresh(); + + return (BSDDIALOG_OK); } int bsddialog_color(enum bsddialog_color foreground, enum bsddialog_color background, unsigned int flags) { - unsigned int cursesflags = 0; + unsigned int i, f; + + f = 0; + for (i=0; i < NFLAGS; i++) + if (flags & flagconv[i].public) + f |= flagconv[i].private; + + return (GET_COLOR(foreground, background) | f); +} - if (flags & BSDDIALOG_BOLD) - cursesflags |= A_BOLD; - if (flags & BSDDIALOG_REVERSE) - cursesflags |= A_REVERSE; - if (flags & BSDDIALOG_UNDERLINE) - cursesflags |= A_UNDERLINE; +int +bsddialog_color_attrs(int color, enum bsddialog_color *foreground, + enum bsddialog_color *background, unsigned int *flags) +{ + short fg, bg; + unsigned int i, f; + + if (flags != NULL) { + f = 0; + for (i=0; i < NFLAGS; i++) + if (color & flagconv[i].private) + f |= flagconv[i].public; + *flags = f; + } + if (pair_content(PAIR_NUMBER(color), &fg, &bg) != OK) + RETURN_ERROR("Cannot get color attributes"); + if (foreground != NULL) + *foreground = fg; + if (background != NULL) + *background = bg; + + return (BSDDIALOG_OK); +} - return (GET_COLOR(foreground, background) | cursesflags); +bool bsddialog_hascolors(void) +{ + return (hastermcolors); } diff --git a/contrib/bsddialog/lib/timebox.c b/contrib/bsddialog/lib/timebox.c index 7d69666c32b8..1421cd7d2b81 100644 --- a/contrib/bsddialog/lib/timebox.c +++ b/contrib/bsddialog/lib/timebox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2024 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,512 +25,216 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> #include <curses.h> -#include <string.h> #include "bsddialog.h" +#include "bsddialog_theme.h" #include "lib_util.h" -#define MINWDATE 23 /* 3 windows and their borders */ #define MINWTIME 14 /* 3 windows and their borders */ - -static int -datetime_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, int minw, const char *text, struct buttons bs) +#define HBOX 3 +#define WBOX 4 + +struct clock { + unsigned int max; + unsigned int value; + WINDOW *win; +}; + +static void +drawsquare(struct bsddialog_conf *conf, WINDOW *win, unsigned int value, + bool focus) { - int htext, wtext; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, 3, minw, &htext, - &wtext) != 0) - return (BSDDIALOG_ERROR); + draw_borders(conf, win, LOWERED); + if (focus) { + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, 1, UARROW(conf), 2); + mvwhline(win, 2, 1, DARROW(conf), 2); + wattroff(win, t.dialog.arrowcolor); } - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, htext,minw, &bs); + if (focus) + wattron(win, t.menu.f_namecolor); + mvwprintw(win, 1, 1, "%02u", value); + if (focus) + wattroff(win, t.menu.f_namecolor); - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 3 /* windows */, true); - - return (0); + wnoutrefresh(win); } -static int -datetime_checksize(int rows, int cols, int minw, struct buttons bs) +static int timebox_redraw(struct dialog *d, struct clock *c) { - int mincols; - - mincols = VBORDERS; - mincols += buttons_width(bs); - mincols = MAX(minw, mincols); + int y, x; - if (cols < mincols) - RETURN_ERROR("Few cols for this timebox/datebox"); - - if (rows < 7) /* 2 button + 2 borders + 3 windows */ - RETURN_ERROR("Few rows for this timebox/datebox, at least 7"); + if (d->built) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MINWTIME, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (d->built) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); + + y = d->y + d->h - BORDER - HBUTTONS - HBOX; + x = d->x + d->w/2 - 7; + update_box(d->conf, c[0].win, y, x, HBOX, WBOX, LOWERED); + mvwaddch(d->widget, d->h - 5, d->w/2 - 3, ':'); + update_box(d->conf, c[1].win, y, x += 5, HBOX, WBOX, LOWERED); + mvwaddch(d->widget, d->h - 5, d->w/2 + 2, ':'); + update_box(d->conf, c[2].win, y, x + 5, HBOX, WBOX, LOWERED); + wnoutrefresh(d->widget); /* for mvwaddch(':') */ return (0); } +/* API */ int bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, int cols, unsigned int *hh, unsigned int *mm, unsigned int *ss) { bool loop, focusbuttons; - int i, input, output, y, x, h, w, sel; - WINDOW *widget, *textpad, *shadow; - struct buttons bs; - struct myclockstruct { - unsigned int max; - unsigned int value; - WINDOW *win; - }; - - if (hh == NULL || mm == NULL || ss == NULL) - RETURN_ERROR("hh / mm / ss cannot be NULL"); - - struct myclockstruct c[3] = { + int i, retval, sel; + wint_t input; + struct dialog d; + struct clock c[3] = { {23, *hh, NULL}, {59, *mm, NULL}, {59, *ss, NULL} }; - for (i = 0 ; i < 3; i++) { - if (c[i].value > c[i].max) - c[i].value = c[i].max; - } - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + CHECK_PTR(hh); + CHECK_PTR(mm); + CHECK_PTR(ss); + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, MINWTIME, text, - bs) != 0) - return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWTIME, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + for (i=0; i<3; i++) { + if ((c[i].win = newwin(1, 1, 1, 1)) == NULL) + RETURN_FMTERROR("Cannot build WINDOW for time[%d]", i); + wbkgd(c[i].win, t.dialog.color); + c[i].value = MIN(c[i].value, c[i].max); + } + if (timebox_redraw(&d, c) != 0) return (BSDDIALOG_ERROR); - pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - doupdate(); - - c[0].win = new_boxed_window(conf, y+h-6, x + w/2 - 7, 3, 4, LOWERED); - mvwaddch(widget, h - 5, w/2 - 3, ':'); - c[1].win = new_boxed_window(conf, y+h-6, x + w/2 - 2, 3, 4, LOWERED); - mvwaddch(widget, h - 5, w/2 + 2, ':'); - c[2].win = new_boxed_window(conf, y+h-6, x + w/2 + 3, 3, 4, LOWERED); - - wrefresh(widget); - + sel = -1; loop = focusbuttons = true; while (loop) { - for (i = 0; i < 3; i++) { - mvwprintw(c[i].win, 1, 1, "%2d", c[i].value); - wrefresh(c[i].win); - } - - if (focusbuttons == false) { - wmove(c[sel].win, 1, 2); - wrefresh(c[sel].win); - } - - input = getch(); + for (i = 0; i < 3; i++) + drawsquare(conf, c[i].win, c[i].value, sel == i); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - if (focusbuttons == false) - break; - output = bs.value[bs.curr]; - loop = false; + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; - case KEY_RIGHT: case '\t': /* TAB */ + case KEY_CTRL('n'): + case KEY_RIGHT: if (focusbuttons) { - bs.curr++; - focusbuttons = bs.curr < (int)bs.nbuttons ? + d.bs.curr++; + focusbuttons = d.bs.curr < (int)d.bs.nbuttons ? true : false; if (focusbuttons == false) { - curs_set(1); sel = 0; + d.bs.curr = + conf->button.always_active ? 0 : -1; } } else { sel++; focusbuttons = sel > 2 ? true : false; if (focusbuttons) { - curs_set(0); - bs.curr = 0; + d.bs.curr = 0; } } - draw_buttons(widget, bs, true); - wrefresh(widget); + DRAW_BUTTONS(d); break; + case KEY_CTRL('p'): case KEY_LEFT: if (focusbuttons) { - bs.curr--; - focusbuttons = bs.curr < 0 ? false : true; + d.bs.curr--; + focusbuttons = d.bs.curr < 0 ? false : true; if (focusbuttons == false) { - curs_set(1); sel = 2; + d.bs.curr = + conf->button.always_active ? 0 : -1; } } else { sel--; focusbuttons = sel < 0 ? true : false; - if (focusbuttons) { - curs_set(0); - bs.curr = (int)bs.nbuttons - 1; - } + if (focusbuttons) + d.bs.curr = (int)d.bs.nbuttons - 1; } - draw_buttons(widget, bs, true); - wrefresh(widget); - break; - case KEY_UP: - if (focusbuttons) - break; - c[sel].value = c[sel].value > 0 ? - c[sel].value - 1 : c[sel].max; - break; - case KEY_DOWN: - if (focusbuttons) - break; - c[sel].value = c[sel].value < c[sel].max ? - c[sel].value + 1 : 0; + DRAW_BUTTONS(d); break; - case KEY_F(1): - if (conf->key.f1_file == NULL && - conf->key.f1_message == NULL) - break; - curs_set(0); - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - curs_set(1); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, - MINWTIME, text, bs) != 0) - return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWTIME, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, true) != 0) - return (BSDDIALOG_ERROR); - - doupdate(); - - mvwaddch(widget, h - 5, w/2 - 3, ':'); - mvwaddch(widget, h - 5, w/2 + 2, ':'); - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - wclear(c[0].win); - mvwin(c[0].win, y + h - 6, x + w/2 - 7); - draw_borders(conf, c[0].win, 3, 4, LOWERED); - wrefresh(c[0].win); - - wclear(c[1].win); - mvwin(c[1].win, y + h - 6, x + w/2 - 2); - draw_borders(conf, c[1].win, 3, 4, LOWERED); - wrefresh(c[1].win); - - wclear(c[2].win); - mvwin(c[2].win, y + h - 6, x + w/2 + 3); - draw_borders(conf, c[2].win, 3, 4, LOWERED); - wrefresh(c[2].win); - - /* Important to avoid grey lines expanding screen */ - refresh(); - break; - default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; - loop = false; - } - } - } - - if (output == BSDDIALOG_OK) { - *hh = c[0].value; - *mm = c[1].value; - *ss = c[2].value; - } - - curs_set(0); - - for (i = 0; i < 3; i++) - delwin(c[i].win); - end_dialog(conf, shadow, widget, textpad); - - return (output); -} - -int -bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd) -{ - bool loop, focusbuttons; - int i, input, output, y, x, h, w, sel; - WINDOW *widget, *textpad, *shadow; - struct buttons bs; - struct calendar { - int max; - int value; - WINDOW *win; - unsigned int x; - }; - struct month { - const char *name; - unsigned int days; - }; - - if (yy == NULL || mm == NULL || dd == NULL) - RETURN_ERROR("yy / mm / dd cannot be NULL"); - - struct calendar c[3] = { - {9999, *yy, NULL, 4 }, - {12, *mm, NULL, 9 }, - {31, *dd, NULL, 2 } - }; - - struct month m[12] = { - { "January", 31 }, { "February", 28 }, { "March", 31 }, - { "April", 30 }, { "May", 31 }, { "June", 30 }, - { "July", 31 }, { "August", 31 }, { "September", 30 }, - { "October", 31 }, { "November", 30 }, { "December", 31 } - }; - -#define ISLEAF(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) - - for (i = 0 ; i < 3; i++) { - if (c[i].value > c[i].max) - c[i].value = c[i].max; - if (c[i].value < 1) - c[i].value = 1; - } - c[2].max = m[c[1].value -1].days; - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - if (c[2].value > c[2].max) - c[2].value = c[2].max; - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, MINWDATE, text, - bs) != 0) - return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWDATE, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) - return (BSDDIALOG_ERROR); - - pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - doupdate(); - - c[0].win = new_boxed_window(conf, y+h-6, x + w/2 - 11, 3, 6, LOWERED); - mvwaddch(widget, h - 5, w/2 - 5, '/'); - c[1].win = new_boxed_window(conf, y+h-6, x + w/2 - 4, 3, 11, LOWERED); - mvwaddch(widget, h - 5, w/2 + 7, '/'); - c[2].win = new_boxed_window(conf, y+h-6, x + w/2 + 8, 3, 4, LOWERED); - - wrefresh(widget); - - loop = focusbuttons = true; - while (loop) { - mvwprintw(c[0].win, 1, 1, "%4d", c[0].value); - mvwprintw(c[1].win, 1, 1, "%9s", m[c[1].value-1].name); - mvwprintw(c[2].win, 1, 1, "%2d", c[2].value); - for (i = 0; i < 3; i++) { - wrefresh(c[i].win); - } - if (focusbuttons == false) { - wmove(c[sel].win, 1, c[sel].x); - wrefresh(c[sel].win); - } - - input = getch(); - switch(input) { - case KEY_ENTER: - case 10: /* Enter */ + case '-': if (focusbuttons == false) - break; - output = bs.value[bs.curr]; - loop = false; - break; - case 27: /* Esc */ - if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; - loop = false; - } - break; - case KEY_RIGHT: - case '\t': /* TAB */ - if (focusbuttons) { - bs.curr++; - focusbuttons = bs.curr < (int)bs.nbuttons ? - true : false; - if (focusbuttons == false) { - curs_set(1); - sel = 0; - } - } else { - sel++; - focusbuttons = sel > 2 ? true : false; - if (focusbuttons) { - curs_set(0); - bs.curr = 0; - } - } - draw_buttons(widget, bs, true); - wrefresh(widget); + c[sel].value = c[sel].value > 0 ? + c[sel].value - 1 : c[sel].max; break; - case KEY_LEFT: + case KEY_UP: if (focusbuttons) { - bs.curr--; - focusbuttons = bs.curr < 0 ? false : true; - if (focusbuttons == false) { - curs_set(1); - sel = 2; - } + sel = 0; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); } else { - sel--; - focusbuttons = sel < 0 ? true : false; - if (focusbuttons) { - curs_set(0); - bs.curr = (int)bs.nbuttons - 1; - } + c[sel].value = c[sel].value > 0 ? + c[sel].value - 1 : c[sel].max; } - draw_buttons(widget, bs, true); - wrefresh(widget); - break; - case KEY_UP: - if (focusbuttons) - break; - c[sel].value = c[sel].value > 1 ? - c[sel].value - 1 : c[sel].max ; - /* if mount change */ - c[2].max = m[c[1].value -1].days; - /* if year change */ - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - /* set new day */ - if (c[2].value > c[2].max) - c[2].value = c[2].max; break; + case '+': case KEY_DOWN: if (focusbuttons) break; c[sel].value = c[sel].value < c[sel].max ? - c[sel].value + 1 : 1; - /* if mount change */ - c[2].max = m[c[1].value -1].days; - /* if year change */ - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - /* set new day */ - if (c[2].value > c[2].max) - c[2].value = c[2].max; + c[sel].value + 1 : 0; break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - curs_set(0); - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - curs_set(1); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, - MINWDATE, text, bs) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWDATE, bs) != 0) + if (timebox_redraw(&d, c) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (timebox_redraw(&d, c) != 0) return (BSDDIALOG_ERROR); - doupdate(); - - mvwaddch(widget, h - 5, w/2 - 5, '/'); - mvwaddch(widget, h - 5, w/2 + 7, '/'); - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - wclear(c[0].win); - mvwin(c[0].win, y + h - 6, x + w/2 - 11); - draw_borders(conf, c[0].win, 3, 6, LOWERED); - wrefresh(c[0].win); - - wclear(c[1].win); - mvwin(c[1].win, y + h - 6, x + w/2 - 4); - draw_borders(conf, c[1].win, 3, 11, LOWERED); - wrefresh(c[1].win); - - wclear(c[2].win); - mvwin(c[2].win, y + h - 6, x + w/2 + 8); - draw_borders(conf, c[2].win, 3, 4, LOWERED); - wrefresh(c[2].win); - - /* Important to avoid grey lines expanding screen */ - refresh(); break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - if (output == BSDDIALOG_OK) { - *yy = c[0].value; - *mm = c[1].value; - *dd = c[2].value; - } - - curs_set(0); + *hh = c[0].value; + *mm = c[1].value; + *ss = c[2].value; for (i = 0; i < 3; i++) delwin(c[i].win); - end_dialog(conf, shadow, widget, textpad); + end_dialog(&d); - return (output); + return (retval); } |