aboutsummaryrefslogtreecommitdiff
path: root/contrib/bsddialog/lib/formbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bsddialog/lib/formbox.c')
-rw-r--r--contrib/bsddialog/lib/formbox.c1194
1 files changed, 790 insertions, 404 deletions
diff --git a/contrib/bsddialog/lib/formbox.c b/contrib/bsddialog/lib/formbox.c
index 564fa99d69a8..5e80471c6974 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-2023 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,890 @@
#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 nkitems */
+ 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].maxvaluelen == 0)
+ RETURN_FMTERROR("item %u [0-%u] maxvaluelen = 0",
+ i, nitems);
+ if (items[i].fieldlen == 0)
+ RETURN_FMTERROR("item %u [0-%u] fieldlen = 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,
+ conf->ascii_lines ? '^' : ACS_UARROW, 5);
+
+ if (f->y + f->viewrows < f->h)
+ mvwhline(f->box, h-1, (w / 2) - 2,
+ conf->ascii_lines ? 'v' : ACS_DARROW, 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_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_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_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);
}