diff options
Diffstat (limited to 'clients/tui.c')
| -rw-r--r-- | clients/tui.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/clients/tui.c b/clients/tui.c index e8508db..d19bd17 100644 --- a/clients/tui.c +++ b/clients/tui.c @@ -1126,3 +1126,221 @@ int tui_form_run(const char *title, struct tui_form_field *f, int n, } } } + +/* ------------------------------------------------------------------ */ +/* row table (dynamic multi-column editor with a trailing blank row) */ +/* ------------------------------------------------------------------ */ + +void tui_rt_init(struct tui_rt *t, int nrows, int maxrows, int ncols, + const struct tui_rt_col *cols, int y, + void (*cell)(void *, int, int, char **, size_t *), + void (*info)(void *, int, char *, size_t), void *ud) +{ + memset(t, 0, sizeof *t); + t->nrows = nrows > 0 ? nrows : 1; + t->maxrows = maxrows; + t->ncols = ncols; + /* only the editable columns take part in Tab/field navigation */ + for (int i = 0; i < ncols; i++) + if (cols[i].kind != TUI_RT_INFO) + t->neditable++; + t->cols = cols; + t->y = y; + t->cell = cell; + t->info = info; + t->ud = ud; + t->fresh = 1; + t->pos = (size_t)-1; +} + +static void rt_get(struct tui_rt *t, int row, int col, char **buf, size_t *cap) +{ + t->cell(t->ud, row, col, buf, cap); +} + +int tui_rt_blank_row(struct tui_rt *t, int row) +{ + for (int i = 0; i < t->ncols; i++) { + if (t->cols[i].kind == TUI_RT_INFO) + continue; + char *buf = NULL; + size_t cap = 0; + rt_get(t, row, i, &buf, &cap); + if (buf && buf[0]) + return 0; + } + return 1; +} + +int tui_rt_normalize(struct tui_rt *t) +{ + if (t->nrows < 1) + t->nrows = 1; + while (t->nrows > 1 && tui_rt_blank_row(t, t->nrows - 1) && + tui_rt_blank_row(t, t->nrows - 2)) + t->nrows--; + if (!tui_rt_blank_row(t, t->nrows - 1) && t->nrows < t->maxrows) + t->nrows++; + if (t->row >= t->nrows) + t->row = t->nrows - 1; + if (t->col >= t->neditable) + t->col = t->neditable - 1; + if (t->col < 0) + t->col = 0; + return t->nrows; +} + +int tui_rt_move(struct tui_rt *t, int dr, int dc) +{ + int moved = 0; + if (dr < 0 && t->row > 0) { + t->row--; + moved = 1; + } else if (dr > 0 && t->row + 1 < t->nrows) { + t->row++; + moved = 1; + } + if (dc && t->neditable > 0) { + t->col = (t->col + dc + t->neditable) % t->neditable; + moved = 1; + } + t->fresh = 1; + return moved; +} + +int tui_rt_clear(struct tui_rt *t) +{ + if (t->row < 0 || t->row >= t->nrows) + return 0; + for (int i = 0; i < t->ncols; i++) { + if (t->cols[i].kind == TUI_RT_INFO) + continue; + char *buf = NULL; + size_t cap = 0; + rt_get(t, t->row, i, &buf, &cap); + if (buf && cap) + buf[0] = '\0'; + } + tui_rt_normalize(t); + t->fresh = 1; + t->pos = (size_t)-1; + return 1; +} + +/* Returns -1 back, -2 refresh, -3 submit; otherwise an edited key was + consumed. */ +int tui_rt_run(const char *title, struct tui_rt *t, const char *hint) +{ + int top = 0; + for (;;) { + if (g_frame) + g_frame(title); + int view = LINES - 4 - t->y; + if (view < 1) + view = 1; + if (t->row < top) + top = t->row; + if (t->row >= top + view) + top = t->row - view + 1; + attron(A_BOLD); + for (int i = 0; i < t->ncols; i++) { + char hdr[64]; + snprintf(hdr, sizeof hdr, "%s", t->cols[i].header); + tui_pad_field(hdr, sizeof hdr, t->cols[i].width); + mvaddstr(t->y - 1, t->cols[i].x, hdr); + } + attroff(A_BOLD); + int caret_y = -1, caret_x = -1; + int ei = 0; + for (int i = 0; i < t->ncols; i++) { + int focused_col = (t->cols[i].kind != TUI_RT_INFO && + ei == t->col); + if (t->cols[i].kind != TUI_RT_INFO) + ei++; + if (t->cols[i].kind == TUI_RT_INFO) { + continue; + } + for (int v = 0; v < view && top + v < t->nrows; v++) { + int row = top + v; + char *buf = NULL; + size_t cap = 0; + rt_get(t, row, i, &buf, &cap); + char padded[512]; + snprintf(padded, sizeof padded, "%s", buf ? buf : ""); + tui_pad_field(padded, sizeof padded, t->cols[i].width); + int y = t->y + v; + if (focused_col && row == t->row) { + attron(A_REVERSE); + caret_y = y; + size_t slen = strlen(buf ? buf : ""); + size_t cp = (t->fresh || t->pos == (size_t)-1 || + t->pos > slen) + ? slen + : t->pos; + int cw = tui_disp_width_n(buf ? buf : "", cp); + caret_x = t->cols[i].x + + (cw > t->cols[i].width ? t->cols[i].width : cw); + } + mvaddstr(y, t->cols[i].x, padded); + if (focused_col && row == t->row) + attroff(A_REVERSE); + } + } + if (t->info) { + for (int v = 0; v < view && top + v < t->nrows; v++) { + char ibuf[512]; + for (int i = 0; i < t->ncols; i++) { + if (t->cols[i].kind != TUI_RT_INFO) + continue; + t->info(t->ud, top + v, ibuf, sizeof ibuf); + tui_pad_field(ibuf, sizeof ibuf, t->cols[i].width); + attron(A_DIM); + mvaddstr(t->y + v, t->cols[i].x, ibuf); + attroff(A_DIM); + } + } + } + if (g_hints) + g_hints(hint); + if (caret_y >= 0) + move(caret_y, caret_x); + refresh(); + int ch = in_key(); + if (ch == 27 || ch == 'q') + return -1; + if (ch == KEY_F(5)) + return -2; + if (ch == TUI_KEY_CTRL_ENTER || ch == KEY_F(9)) + return -3; + if (ch == '\t') { + tui_rt_move(t, 0, 1); + continue; + } + if (ch == KEY_BTAB) { + tui_rt_move(t, 0, -1); + continue; + } + if (ch == KEY_UP) { + tui_rt_move(t, -1, 0); + continue; + } + if (ch == KEY_DOWN) { + tui_rt_move(t, 1, 0); + continue; + } + if (ch == TUI_KEY_CTRL_X) { + tui_rt_clear(t); + continue; + } + char *buf = NULL; + size_t cap = 0; + rt_get(t, t->row, t->col, &buf, &cap); + if (!buf) + continue; + if (t->cols[t->col].kind == TUI_RT_DATE) + tui_date_field_edit(buf, cap, &t->pos, ch, &t->fresh); + else + tui_field_edit(buf, cap, &t->pos, ch, &t->fresh); + tui_rt_normalize(t); + } +} |
