summaryrefslogtreecommitdiff
path: root/clients/bokftui.c
diff options
context:
space:
mode:
authorAnders Betts <anders.betts@gmail.com>2026-09-19 21:58:58 +0200
committerAnders Betts <anders.betts@gmail.com>2026-09-19 21:58:58 +0200
commit4329ffb98d7eeffe2b3874d2b31cb0857d33d944 (patch)
tree16e777dc90fa334ce3f436a3b5d3a7a9efdbd8b9 /clients/bokftui.c
parent34fc2b1dce788fbb093e70c2124630167e7980ef (diff)
downloadbokf-4329ffb98d7eeffe2b3874d2b31cb0857d33d944.tar.gz
bokf-4329ffb98d7eeffe2b3874d2b31cb0857d33d944.zip
tui: shared widget layer (fields, prompts, lists, forms, pager) + testsv0.1.36
Diffstat (limited to 'clients/bokftui.c')
-rw-r--r--clients/bokftui.c1918
1 files changed, 444 insertions, 1474 deletions
diff --git a/clients/bokftui.c b/clients/bokftui.c
index 9ee5859..0967f51 100644
--- a/clients/bokftui.c
+++ b/clients/bokftui.c
@@ -13,6 +13,7 @@
#include <unistd.h>
#include "client.h"
+#include "tui.h"
#include "formula.h"
#include "util.h"
#include "version.h"
@@ -46,6 +47,11 @@ static int ui_getch(void)
return ch;
}
+static void request_quit(void)
+{
+ g_quit = 1;
+}
+
struct app {
struct client_conn conn;
char socket[256];
@@ -139,10 +145,8 @@ static int jbool_val(const char *resp, const char *path, int def)
/* ui primitives */
/* ------------------------------------------------------------------ */
-static char g_status[512];
static char g_server_version[32];
-static int disp_width(const char *s);
static void tui_log(const char *fmt, ...)
__attribute__((format(printf, 1, 2)));
@@ -151,759 +155,12 @@ static void bokslut_screen(struct app *a);
static void arsredovisning_screen(struct app *a);
static char *rpc_dry(struct app *a, const char *cmd, const char *args);
static int64_t vouchers_new(struct app *a);
-static void acct_cache_free(void);
-static void acct_cache_load(struct app *a);
-static const char *acct_name(struct app *a, const char *number);
-static int acct_exists(struct app *a, const char *number);
-
-/* ---- shared line editor used by every form and prompt ---- */
-struct ledit {
- char *buf;
- size_t cap;
- size_t len;
- size_t pos; /* caret position in bytes */
-};
-
-static size_t disp_width_n(const char *s, size_t n)
-{
- size_t w = 0;
- for (size_t i = 0; i < n && s[i]; i++)
- if (((unsigned char)s[i] & 0xC0) != 0x80)
- w++;
- return w;
-}
-
-static void le_init(struct ledit *e, char *buf, size_t cap)
-{
- e->buf = buf;
- e->cap = cap;
- e->len = strlen(buf);
- e->pos = e->len;
-}
-
-static void le_clear(struct ledit *e)
-{
- e->len = 0;
- e->pos = 0;
- e->buf[0] = '\0';
-}
-
-static void le_insert(struct ledit *e, unsigned char ch)
-{
- if (e->len + 1 >= e->cap)
- return;
- memmove(e->buf + e->pos + 1, e->buf + e->pos, e->len - e->pos + 1);
- e->buf[e->pos] = (char)ch;
- e->pos++;
- e->len++;
-}
-
-static void le_backspace(struct ledit *e)
-{
- if (e->pos == 0)
- return;
- memmove(e->buf + e->pos - 1, e->buf + e->pos, e->len - e->pos + 1);
- e->pos--;
- e->len--;
-}
-
-static void le_delete(struct ledit *e)
-{
- if (e->pos >= e->len)
- return;
- memmove(e->buf + e->pos, e->buf + e->pos + 1, e->len - e->pos);
- e->len--;
-}
-
-/* Consumes the key when it is an editing key; returns 1 then. */
-static int le_key(struct ledit *e, int ch)
-{
- if (ch == KEY_LEFT) {
- if (e->pos > 0) {
- e->pos--;
- while (e->pos > 0 && ((unsigned char)e->buf[e->pos] & 0xC0) == 0x80)
- e->pos--;
- }
- return 1;
- }
- if (ch == KEY_RIGHT) {
- if (e->pos < e->len) {
- e->pos++;
- while (e->pos < e->len &&
- ((unsigned char)e->buf[e->pos] & 0xC0) == 0x80)
- e->pos++;
- }
- return 1;
- }
- if (ch == KEY_HOME || ch == 1) { /* Ctrl+A */
- e->pos = 0;
- return 1;
- }
- if (ch == KEY_END || ch == 5) { /* Ctrl+E */
- e->pos = e->len;
- return 1;
- }
- if (ch == KEY_DC) {
- le_delete(e);
- return 1;
- }
- if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {
- le_backspace(e);
- return 1;
- }
- if (ch == 21) { /* ^U */
- le_clear(e);
- return 1;
- }
- if (ch >= 32 && ch < 256) {
- le_insert(e, (unsigned char)ch);
- return 1;
- }
- return 0;
-}
-
-/* Date field: only digits are accepted, dashes are inserted automatically
- ("20260215" -> "2026-02-15"). The caret is tracked in digit space and
- reported back as a byte offset so forms render it like any field. */
-static int date_field_edit(char *buf, size_t cap, size_t *pos, int ch,
- int *fresh)
-{
- char d[9];
- size_t n = 0;
- for (const char *q = buf; *q && n < 8; q++)
- if (*q >= '0' && *q <= '9')
- d[n++] = *q;
- size_t ci = n; /* caret in digit space; byte pos -> digit index */
- if (*pos != (size_t)-1) {
- size_t seen = 0;
- for (size_t i = 0; i < *pos && buf[i]; i++)
- if (buf[i] >= '0' && buf[i] <= '9')
- seen++;
- ci = seen;
- }
-
- if (*fresh) {
- int editing = ch == KEY_BACKSPACE || ch == 127 || ch == 8 ||
- ch == KEY_DC || (ch >= '0' && ch <= '9');
- int moving = ch == KEY_LEFT || ch == KEY_RIGHT || ch == KEY_HOME ||
- ch == KEY_END || ch == 1 || ch == 5;
- if (editing || moving) {
- if (editing) {
- n = 0;
- ci = 0;
- }
- *fresh = 0; /* movement keeps the date and moves the caret */
- }
- }
-
- if (ch >= '0' && ch <= '9') {
- if (ci > n)
- ci = n;
- if (n < 8) {
- for (size_t i = n; i > ci; i--)
- d[i] = d[i - 1];
- d[ci] = (char)ch;
- n++;
- } else {
- /* full field: overwrite the digit at the caret */
- if (ci > 7)
- ci = 7;
- d[ci] = (char)ch;
- }
- ci++;
- } else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {
- if (ci > 0) {
- for (size_t i = ci - 1; i + 1 < n; i++)
- d[i] = d[i + 1];
- n--;
- ci--;
- }
- } else if (ch == KEY_DC) {
- if (ci < n) {
- for (size_t i = ci; i + 1 < n; i++)
- d[i] = d[i + 1];
- n--;
- }
- } else if (ch == KEY_LEFT) {
- if (ci > 0)
- ci--;
- } else if (ch == KEY_RIGHT) {
- if (ci < n)
- ci++;
- } else if (ch == KEY_HOME || ch == 1) {
- ci = 0;
- } else if (ch == KEY_END || ch == 5) {
- ci = n;
- } else if (ch == 21) {
- n = 0;
- ci = 0;
- *fresh = 0;
- } else {
- return 0; /* not a date editing key */
- }
-
- /* rebuild "YYYY-MM-DD" (partial while typing) */
- char out[16];
- size_t o = 0;
- for (size_t i = 0; i < n; i++) {
- if (i == 4 || i == 6)
- out[o++] = '-';
- out[o++] = d[i];
- }
- out[o] = '\0';
- snprintf(buf, cap, "%s", out);
-
- size_t byte = ci;
- if (n > 4 && ci >= 4)
- byte++;
- if (n > 6 && ci >= 6)
- byte++;
- if (byte > strlen(buf))
- byte = strlen(buf);
- *pos = byte;
- return 1;
-}
-
-/* Prompt for a date with the date editor. Returns a static buffer or NULL
- on Esc. */
-static char *date_prompt(const char *label, const char *def)
-{
- static char buf[16];
- snprintf(buf, sizeof buf, "%s", def ? def : "");
- int y = LINES - 3;
- move(y, 2);
- clrtoeol();
- attron(A_BOLD);
- mvaddstr(y, 2, label);
- attroff(A_BOLD);
- size_t pos = (size_t)-1;
- int fresh = 1;
- int prev = curs_set(1);
- for (;;) {
- move(y, (int)strlen(label) + 3);
- clrtoeol();
- addnstr(buf, (int)sizeof buf);
- clrtoeol();
- size_t slen = strlen(buf);
- size_t cp = pos == (size_t)-1 || pos > slen ? slen : pos;
- move(y, (int)strlen(label) + 3 + (int)disp_width_n(buf, cp));
- refresh();
- int ch = ui_getch();
- if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) {
- curs_set(prev == ERR ? 0 : prev);
- return buf;
- }
- if (ch == 27) {
- curs_set(prev == ERR ? 0 : prev);
- return NULL;
- }
- date_field_edit(buf, sizeof buf, &pos, ch, &fresh);
- }
-}
-
-/* Applies a key to a form field. *pos is the caret, (size_t)-1 = at the
- end. When *fresh is set, the first editing key replaces the content. */
-static int field_edit(char *buf, size_t cap, size_t *pos, int ch, int *fresh)
-{
- struct ledit e;
- le_init(&e, buf, cap);
- if (*pos != (size_t)-1 && *pos <= e.len)
- e.pos = *pos;
- if (*fresh) {
- int editing = ch == KEY_BACKSPACE || ch == 127 || ch == 8 ||
- ch == KEY_DC || (ch >= 32 && ch < 256);
- int moving = ch == KEY_LEFT || ch == KEY_RIGHT || ch == KEY_HOME ||
- ch == KEY_END || ch == 1 || ch == 5;
- if (editing || moving) {
- if (editing)
- le_clear(&e);
- *fresh = 0; /* movement keeps the content and moves the caret */
- }
- }
- int handled = le_key(&e, ch);
- *pos = e.pos;
- return handled;
-}
-
-static void frame(const char *title)
-{
- erase();
- attron(A_BOLD);
- box(stdscr, 0, 0);
- mvaddstr(0, 2, title);
- attroff(A_BOLD);
- if (g_status[0]) {
- attron(A_DIM);
- mvaddnstr(1, 2, g_status, COLS - 4);
- char ver[96];
- if (g_server_version[0])
- snprintf(ver, sizeof ver, "tui %s srv %s", BOKF_VERSION,
- g_server_version);
- else
- snprintf(ver, sizeof ver, "tui %s", BOKF_VERSION);
- int x = COLS - 3 - disp_width(ver);
- if (x > disp_width(g_status) + 6)
- mvaddstr(1, x, ver);
- attroff(A_DIM);
- }
-}
-
-static void hints(const char *s)
-{
- char line[512];
- snprintf(line, sizeof line, "%s ^R = ladda om", s);
- attron(A_DIM);
- mvaddstr(LINES - 1, 2, line);
- attroff(A_DIM);
- clrtoeol();
-}
-
-static void message(const char *title, const char *fmt, ...)
- __attribute__((format(printf, 2, 3)));
-
-static void message(const char *title, const char *fmt, ...)
-{
- char text[1024];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(text, sizeof text, fmt, ap);
- va_end(ap);
- int h = 5, w = (int)strlen(text) + 4;
- if (w > COLS - 4)
- w = COLS - 4;
- if (w < 30)
- w = 30;
- WINDOW *win = newwin(h, w, (LINES - h) / 2, (COLS - w) / 2);
- box(win, 0, 0);
- wattron(win, A_BOLD);
- mvwaddnstr(win, 0, 2, title, w - 4);
- wattroff(win, A_BOLD);
- mvwaddnstr(win, 2, 2, text, w - 4);
- mvwaddstr(win, h - 1, w - 12, " tryck Enter");
- wrefresh(win);
- nodelay(stdscr, FALSE);
- timeout(-1);
- for (;;) {
- int ch = wgetch(win);
- if (ch == 3) {
- g_quit = 1;
- break;
- }
- if (ch == '\n' || ch == '\r' || ch == ' ' || ch == 27 || ch == KEY_ENTER)
- break;
- }
- delwin(win);
- touchwin(stdscr);
- refresh();
-}
-
-/* Line editor inside the current window at (y,x) with fixed label already
- drawn. Returns 1 on Enter, 2 on Tab, 3 on Shift-Tab, 0 on Esc. */
-static int edit_field(int y, int x, char *buf, size_t cap, int mask)
-{
- size_t pos = (size_t)-1;
- int fresh = 1;
- for (;;) {
- move(y, x);
- if (mask) {
- size_t len = strlen(buf);
- for (size_t i = 0; i < len; i++)
- addch('*');
- } else {
- addnstr(buf, (int)cap);
- }
- clrtoeol();
- size_t len = strlen(buf);
- size_t cp = pos == (size_t)-1 || pos > len ? len : pos;
- move(y, x + (mask ? (int)cp : (int)disp_width_n(buf, cp)));
- refresh();
- int ch = ui_getch();
- if (ch == '\n' || ch == '\r' || ch == KEY_ENTER)
- return 1;
- if (ch == 27)
- return 0;
- if (ch == '\t')
- return 2;
- if (ch == KEY_BTAB)
- return 3;
- field_edit(buf, cap, &pos, ch, &fresh);
- }
-}
-
-static char *prompt(const char *label, const char *def, int mask)
-{
- static char buf[512];
- snprintf(buf, sizeof buf, "%s", def ? def : "");
- int y = LINES - 3;
- move(y, 2);
- clrtoeol();
- attron(A_BOLD);
- mvaddstr(y, 2, label);
- attroff(A_BOLD);
- refresh();
- int prev = curs_set(1);
- int rc = edit_field(y, (int)strlen(label) + 3, buf, sizeof buf, mask);
- curs_set(prev == ERR ? 0 : prev);
- if (rc == 0)
- return NULL;
- return buf;
-}
-
-static int text_view(const char *title, const char *text,
- const char *action_hint)
-{
- int ret = 0;
- int nlines = 0;
- for (const char *p = text; *p; p++)
- if (*p == '\n')
- nlines++;
- nlines += 1;
- char **lines = xcalloc(nlines ? nlines : 1, sizeof(char *));
- int n = 0;
- char *copy = xstrdup(text);
- for (char *p = copy;;) {
- char *nl = strchr(p, '\n');
- if (nl)
- *nl = '\0';
- lines[n++] = p;
- if (!nl)
- break;
- p = nl + 1;
- }
- int top = 0;
- int view = LINES - 4;
- for (;;) {
- frame(title);
- for (int i = 0; i < view; i++) {
- move(2 + i, 2);
- clrtoeol();
- if (top + i < n)
- mvaddnstr(2 + i, 2, lines[top + i], COLS - 4);
- }
- char hint[256];
- if (action_hint)
- snprintf(hint, sizeof hint,
- "piltangenter/PgUp/PgDn rullar F5 = uppdatera %s "
- "q = tillbaka",
- action_hint);
- else
- snprintf(hint, sizeof hint,
- "piltangenter/PgUp/PgDn rullar F5 = uppdatera "
- "q = tillbaka");
- hints(hint);
- refresh();
- int ch = ui_getch();
- if (ch == 'q' || ch == 27)
- break;
- if (ch == KEY_F(5)) {
- ret = 1;
- break;
- }
- if (ch == 's' && action_hint) {
- ret = 2;
- break;
- }
- if (ch == KEY_DOWN && top + view < n)
- top++;
- else if (ch == KEY_UP && top > 0)
- top--;
- else if (ch == KEY_NPAGE)
- top += view;
- else if (ch == KEY_PPAGE)
- top -= view;
- else if (ch == KEY_HOME)
- top = 0;
- else if (ch == KEY_END)
- top = n > view ? n - view : 0;
- if (top + view > n)
- top = n > view ? n - view : 0;
- if (top < 0)
- top = 0;
- }
- free(copy);
- free(lines);
- return ret;
-}
-
-static int menu(const char *title, const char *const *items, int n,
- int allow_new, int *cursor)
-{
- int sel = (cursor && *cursor >= 0 && *cursor < n) ? *cursor : 0;
- int top = 0;
- char gotobuf[12] = "";
- int goto_active = 0;
- for (;;) {
- if (cursor)
- *cursor = sel;
- int view = (LINES - 4) / 2;
- if (view < 1)
- view = 1;
- if (sel < top)
- top = sel;
- if (sel >= top + view)
- top = sel - view + 1;
- frame(title);
- for (int i = 0; i < view && top + i < n; i++) {
- int idx = top + i;
- char line[512];
- snprintf(line, sizeof line, "%2d. %s", idx + 1, items[idx]);
- if (idx == sel)
- attron(A_REVERSE);
- mvaddnstr(3 + i * 2, 4, line, COLS - 6);
- if (idx == sel)
- attroff(A_REVERSE);
- }
- if (goto_active) {
- move(LINES - 2, 2);
- attron(A_BOLD);
- printw("Gå till nummer: %s", gotobuf);
- attroff(A_BOLD);
- clrtoeol();
- } else {
- move(LINES - 2, 2);
- clrtoeol();
- }
- hints(allow_new
- ? "upp/ned, 1-9 = snabbval, g = gå till, PgUp/PgDn,"
- " Home/End, Enter ^N = ny Esc/q = tillbaka"
- " ^C = avsluta"
- : "upp/ned, 1-9 = snabbval, g = gå till, PgUp/PgDn,"
- " Home/End, Enter Esc/q = tillbaka ^C = avsluta");
- refresh();
- int ch = ui_getch();
- if (goto_active) {
- if (ch >= '0' && ch <= '9' && strlen(gotobuf) < 9) {
- size_t gl = strlen(gotobuf);
- gotobuf[gl] = (char)ch;
- gotobuf[gl + 1] = '\0';
- int v = atoi(gotobuf);
- if (v >= 1 && v <= n)
- sel = v - 1;
- } else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {
- size_t gl = strlen(gotobuf);
- if (gl)
- gotobuf[gl - 1] = '\0';
- if (gotobuf[0]) {
- int v = atoi(gotobuf);
- if (v >= 1 && v <= n)
- sel = v - 1;
- }
- } else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER ||
- ch == 27 || ch == 'g' || ch == 'G') {
- goto_active = 0;
- gotobuf[0] = '\0';
- }
- continue;
- }
- if (ch == KEY_UP && sel > 0)
- sel--;
- else if (ch == KEY_DOWN && sel < n - 1)
- sel++;
- else if (ch == KEY_NPAGE)
- sel = sel + view < n ? sel + view : n - 1;
- else if (ch == KEY_PPAGE)
- sel = sel - view > 0 ? sel - view : 0;
- else if (ch == KEY_HOME)
- sel = 0;
- else if (ch == KEY_END)
- sel = n - 1;
- else if (ch >= '1' && ch <= '9' && ch - '1' < n) {
- sel = ch - '1';
- if (cursor)
- *cursor = sel;
- return sel;
- }
- else if (ch == 'g' || ch == 'G') {
- goto_active = 1;
- gotobuf[0] = '\0';
- } else if (ch == KEY_CTRL_N && allow_new)
- return -4;
- else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER)
- return sel;
- else if (ch == 27 || ch == 'q')
- return g_quit ? -5 : -1;
- }
-}
-
-static int select_list(const char *title, char **items, int n, int start,
- int allow_refresh, int *cursor, int allow_new,
- const char *archive_action, int allow_toggle)
-{
- int sel = start;
- if (sel < 0)
- sel = 0;
- if (n > 0 && sel >= n)
- sel = n - 1;
- int top = 0;
- int view = LINES - 4;
- char gotobuf[12] = "";
- int goto_active = 0;
- for (;;) {
- if (cursor)
- *cursor = sel;
- frame(title);
- if (sel < top)
- top = sel;
- if (sel >= top + view)
- top = sel - view + 1;
- int numw = 1;
- for (int tmp = n; tmp >= 10; tmp /= 10)
- numw++;
- if (numw > 9)
- numw = 9;
- for (int i = 0; i < view && top + i < n; i++) {
- char line[1024];
- snprintf(line, sizeof line, "%*d. %s", numw, top + i + 1,
- items[top + i]);
- if (top + i == sel)
- attron(A_REVERSE);
- mvaddnstr(2 + i, 2, line, COLS - 4);
- if (top + i == sel)
- attroff(A_REVERSE);
- }
- if (goto_active) {
- move(LINES - 2, 2);
- attron(A_BOLD);
- printw("Gå till rad: %s", gotobuf);
- attroff(A_BOLD);
- clrtoeol();
- } else {
- move(LINES - 2, 2);
- clrtoeol();
- }
- {
- char hint[256], dbuf[48] = "";
- if (archive_action)
- snprintf(dbuf, sizeof dbuf, " d = %s", archive_action);
- snprintf(hint, sizeof hint,
- "upp/ned, 1-9 = hoppa, g = gå till, PgUp/PgDn, Home/End,"
- " Enter%s%s%s%s Esc/q = tillbaka",
- allow_refresh ? " F5 = uppdatera" : "",
- allow_new ? " ^N = ny" : "", dbuf,
- allow_toggle ? " ^A = öppna/stäng" : "");
- snprintf(hint + strlen(hint), sizeof hint - strlen(hint),
- " ^C = avsluta");
- hints(hint);
- }
- refresh();
- int ch = ui_getch();
- if (goto_active) {
- if (ch >= '0' && ch <= '9' && strlen(gotobuf) < 9) {
- size_t gl = strlen(gotobuf);
- gotobuf[gl] = (char)ch;
- gotobuf[gl + 1] = '\0';
- int v = atoi(gotobuf);
- if (v >= 1 && v <= n)
- sel = v - 1;
- } else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) {
- size_t gl = strlen(gotobuf);
- if (gl)
- gotobuf[gl - 1] = '\0';
- if (gotobuf[0]) {
- int v = atoi(gotobuf);
- if (v >= 1 && v <= n)
- sel = v - 1;
- }
- } else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER ||
- ch == 27 || ch == 'g' || ch == 'G') {
- goto_active = 0;
- gotobuf[0] = '\0';
- }
- continue;
- }
- if (ch == KEY_UP && sel > 0)
- sel--;
- else if (ch == KEY_DOWN && sel < n - 1)
- sel++;
- else if (ch == KEY_NPAGE)
- sel = sel + view < n ? sel + view : n - 1;
- else if (ch == KEY_PPAGE)
- sel = sel - view > 0 ? sel - view : 0;
- else if (ch == KEY_HOME)
- sel = 0;
- else if (ch == KEY_END)
- sel = n - 1;
- else if (ch >= '1' && ch <= '9') {
- int idx = ch - '1';
- if (idx < n)
- sel = idx; /* goto row, Enter opens */
- } else if (ch == 'g' || ch == 'G') {
- goto_active = 1;
- gotobuf[0] = '\0';
- } else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER)
- return sel;
- else if (allow_refresh && ch == KEY_F(5))
- return -2;
- else if (allow_new && ch == KEY_CTRL_N)
- return -4;
- else if (archive_action && (ch == 'd' || ch == 'D') && n > 0)
- return -6;
- else if (allow_toggle && ch == 1 && n > 0)
- return -7;
- else if (ch == 27 || ch == 'q')
- return g_quit ? -5 : -1;
- }
-}
-
-static int parse_kr(const char *s, int64_t *out)
-{
- if (!s || !*s) {
- *out = 0;
- return 0;
- }
- int64_t whole = 0, frac = 0;
- int fd = 0, seen_dot = 0;
- for (const char *p = s; *p; p++) {
- if (*p == ' ' || *p == '\t')
- continue;
- if (*p == '.' || *p == ',') {
- if (seen_dot)
- return -1;
- seen_dot = 1;
- continue;
- }
- if (*p < '0' || *p > '9')
- return -1;
- if (!seen_dot) {
- whole = whole * 10 + (*p - '0');
- } else {
- if (fd >= 2)
- return -1;
- frac = frac * 10 + (*p - '0');
- fd++;
- }
- }
- if (fd == 1)
- frac *= 10;
- *out = whole * 100 + frac;
- return 0;
-}
-
-static void kr_format(int64_t ore, char *buf, size_t n)
-{
- long long v = (long long)ore;
- int neg = v < 0;
- if (neg)
- v = -v;
- char digits[32];
- snprintf(digits, sizeof digits, "%lld", v / 100);
- char out[28];
- size_t o = 0;
- size_t len = strlen(digits);
- for (size_t i = 0; i < len && o + 2 < sizeof out; i++) {
- if (i > 0 && (len - i) % 3 == 0)
- out[o++] = ' ';
- out[o++] = digits[i];
- }
- out[o] = '\0';
- snprintf(buf, n, "%s%s,%02lld", neg ? "-" : "", out, v % 100);
-}
/* Whole kronor with space thousands, öre truncated like the blankett. */
static void kr_whole(int64_t ore, char *buf, size_t n)
{
char tmp[48];
- kr_format((ore / 100) * 100, tmp, sizeof tmp);
+ tui_kr_format((ore / 100) * 100, tmp, sizeof tmp);
char *comma = strchr(tmp, ',');
if (comma)
*comma = '\0';
@@ -951,40 +208,7 @@ static void replace_x_into(const char *src, double x, char *dst, size_t cap)
/* Display width in terminal columns, counting UTF-8 lead bytes. Good enough
for Swedish text; combining marks are ignored. */
-static int disp_width(const char *s)
-{
- int w = 0;
- for (const unsigned char *p = (const unsigned char *)s; *p; p++)
- if ((*p & 0xC0) != 0x80)
- w++;
- return w;
-}
-static void pad_field(char *buf, size_t cap, int width)
-{
- size_t slen = strlen(buf);
- int w = 0;
- size_t i = 0;
- while (i < slen && buf[i]) {
- unsigned char c = (unsigned char)buf[i];
- size_t len = 1;
- if ((c & 0xE0) == 0xC0)
- len = 2;
- else if ((c & 0xF0) == 0xE0)
- len = 3;
- else if ((c & 0xF8) == 0xF0)
- len = 4;
- if (i + len > slen || w + 1 > width)
- break;
- w++;
- i += len;
- }
- buf[i] = '\0';
- while (w < width && strlen(buf) + 1 < cap) {
- strcat(buf, " ");
- w++;
- }
-}
/* ------------------------------------------------------------------ */
/* app helpers */
@@ -1075,8 +299,15 @@ static void app_refresh_context(struct app *a)
static void update_status(struct app *a)
{
- snprintf(g_status, sizeof g_status, "%s | %s - %s | %s | %s",
- a->org_name, a->fy_start, a->fy_end, a->role, a->username);
+ char st[512], right[160];
+ snprintf(st, sizeof st, "%s | %s - %s | %s | %s", a->org_name,
+ a->fy_start, a->fy_end, a->role, a->username);
+ if (g_server_version[0])
+ snprintf(right, sizeof right, "tui %s srv %s", BOKF_VERSION,
+ g_server_version);
+ else
+ snprintf(right, sizeof right, "tui %s", BOKF_VERSION);
+ tui_set_status(st, right);
}
@@ -1120,107 +351,63 @@ static int64_t fy_new_form(struct app *a)
else
snprintf(label, sizeof label, "%d/%d", sy, ey);
- int field = 0;
- int field_fresh = 1;
- size_t field_pos = (size_t)-1;
- curs_set(1);
+ struct tui_form_field ff[3];
for (;;) {
- frame("Nytt räkenskapsår");
- int caret_y = -1, caret_x = -1;
- attron(A_BOLD);
- mvaddstr(4, 4, "Etikett");
- mvaddstr(6, 4, "Startdatum");
- mvaddstr(8, 4, "Slutdatum");
- attroff(A_BOLD);
- {
- const char *vals[3] = { label, start, end };
- int ys[3] = { 4, 6, 8 };
- for (int k = 0; k < 3; k++) {
- char padded[64];
- snprintf(padded, sizeof padded, "%-24s", vals[k]);
- if (field == k) {
- attron(A_REVERSE);
- caret_y = ys[k];
- size_t slen = strlen(vals[k]);
- size_t cp = (field_fresh || field_pos == (size_t)-1 ||
- field_pos > slen)
- ? slen
- : field_pos;
- caret_x = 16 + (int)disp_width_n(vals[k], cp);
- }
- mvaddstr(ys[k], 16, padded);
- if (field == k)
- attroff(A_REVERSE);
- }
- }
- hints("Tab = byta fält F5 = validera ^Enter = skapa Esc = avbryt");
- if (caret_y >= 0)
- move(caret_y, caret_x);
- refresh();
-
- int ch = ui_getch();
- if (ch == 27) {
- curs_set(0);
+ memset(ff, 0, sizeof ff);
+ ff[0].label = "Etikett";
+ ff[0].value = label;
+ ff[0].cap = sizeof label;
+ ff[0].kind = TUI_F_TEXT;
+ ff[1].label = "Startdatum";
+ ff[1].value = start;
+ ff[1].cap = sizeof start;
+ ff[1].kind = TUI_F_DATE;
+ ff[2].label = "Slutdatum";
+ ff[2].value = end;
+ ff[2].cap = sizeof end;
+ ff[2].kind = TUI_F_DATE;
+ int r = tui_form_run("Nytt räkenskapsår", ff, 3, 1);
+ if (r == TUI_FORM_BACK)
return 0;
- }
- if (ch == '\t') {
- field = (field + 1) % 3;
- field_fresh = 1;
+ if (r != TUI_FORM_REFRESH && r != TUI_FORM_SUBMIT)
+ continue; /* a field was edited; keep the form up */
+ if (!label[0]) {
+ tui_message("Räkenskapsår", "Etiketten får inte vara tom.");
continue;
}
- if (ch == KEY_BTAB) {
- field = (field + 2) % 3;
- field_fresh = 1;
+ if (!util_parse_iso_date(start) || !util_parse_iso_date(end)) {
+ tui_message("Räkenskapsår",
+ "Start- och slutdatum måste vara ÅÅÅÅ-MM-DD.");
continue;
}
- if (ch == KEY_F(5) || ch == KEY_CTRL_ENTER || ch == KEY_F(9)) {
- if (!label[0]) {
- message("Räkenskapsår", "Etiketten får inte vara tom.");
- continue;
- }
- if (!util_parse_iso_date(start) || !util_parse_iso_date(end)) {
- message("Räkenskapsår",
- "Start- och slutdatum måste vara ÅÅÅÅ-MM-DD.");
- continue;
- }
- if (strcmp(start, end) >= 0) {
- message("Räkenskapsår",
+ if (strcmp(start, end) >= 0) {
+ tui_message("Räkenskapsår",
"Startdatum måste ligga före slutdatum.");
- continue;
- }
- if (ch == KEY_F(5)) {
- message("Validering OK", "%s %s - %s", label, start, end);
- continue;
- }
- yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
- yyjson_mut_val *o = yyjson_mut_obj(d);
- yyjson_mut_doc_set_root(d, o);
- yyjson_mut_obj_add_strcpy(d, o, "label", label);
- yyjson_mut_obj_add_strcpy(d, o, "start_date", start);
- yyjson_mut_obj_add_strcpy(d, o, "end_date", end);
- char *args = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- resp = client_rpc(&a->conn, "fiscal_year.open", a->session, a->org,
- args);
- free(args);
- if (resp && client_ok(resp)) {
- int64_t id = jint_val(resp, "result.id", 0);
- message("Räkenskapsår", "%s skapat.", label);
- free(resp);
- curs_set(0);
- return id;
- }
- show_error("Kunde inte skapa räkenskapsåret", resp);
- free(resp);
continue;
}
- char *buf = field == 0 ? label : (field == 1 ? start : end);
- size_t cap = field == 0 ? sizeof label
- : (field == 1 ? sizeof start : sizeof end);
- if (field == 0)
- field_edit(buf, cap, &field_pos, ch, &field_fresh);
- else
- date_field_edit(buf, cap, &field_pos, ch, &field_fresh);
+ if (r == TUI_FORM_REFRESH) {
+ tui_message("Validering OK", "%s %s - %s", label, start, end);
+ continue;
+ }
+ yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
+ yyjson_mut_val *o = yyjson_mut_obj(d);
+ yyjson_mut_doc_set_root(d, o);
+ yyjson_mut_obj_add_strcpy(d, o, "label", label);
+ yyjson_mut_obj_add_strcpy(d, o, "start_date", start);
+ yyjson_mut_obj_add_strcpy(d, o, "end_date", end);
+ char *args = yyjson_mut_write(d, 0, NULL);
+ yyjson_mut_doc_free(d);
+ char *oresp = client_rpc(&a->conn, "fiscal_year.open", a->session,
+ a->org, args);
+ free(args);
+ if (oresp && client_ok(oresp)) {
+ int64_t id = jint_val(oresp, "result.id", 0);
+ tui_message("Räkenskapsår", "%s skapat.", label);
+ free(oresp);
+ return id;
+ }
+ show_error("Kunde inte skapa räkenskapsåret", oresp);
+ free(oresp);
}
}
@@ -1273,7 +460,7 @@ static void select_fiscal_year(struct app *a)
closed[i] = strcmp(status ? status : "", "closed") == 0;
char line[256], stbuf[16];
snprintf(stbuf, sizeof stbuf, "%s", closed[i] ? "stängd" : "öppen");
- pad_field(stbuf, sizeof stbuf, 6);
+ tui_pad_field(stbuf, sizeof stbuf, 6);
snprintf(line, sizeof line, "%-25s %s %s", ranges[i], stbuf,
labels[i]);
lines[i] = xstrdup(line);
@@ -1287,7 +474,7 @@ static void select_fiscal_year(struct app *a)
int start = cursor >= 0 ? cursor : sel_default;
if (start >= (int)n)
start = (int)n - 1;
- int sel = select_list("Räkenskapsår", lines, (int)n, start, 0,
+ int sel = tui_select_list("Räkenskapsår", lines, (int)n, start, 0,
&cursor, 1, NULL, 1);
for (size_t i = 0; i < n; i++)
free(lines[i]);
@@ -1309,7 +496,7 @@ static void select_fiscal_year(struct app *a)
snprintf(label, sizeof label, "%s räkenskapsåret %s? (j/n): ",
closed[cursor] ? "Återöppna" : "Avsluta",
labels[cursor]);
- char *ans = prompt(label, "n", 0);
+ char ansbuf[512]; char *ans = tui_prompt_into(ansbuf, sizeof ansbuf, label, "n", 0) ? ansbuf : NULL;
if (ans && (ans[0] == 'j' || ans[0] == 'J')) {
char args[64];
snprintf(args, sizeof args,
@@ -1321,7 +508,7 @@ static void select_fiscal_year(struct app *a)
: "fiscal_year.close",
a->session, a->org, args);
if (r && client_ok(r))
- message("Räkenskapsår", "Räkenskapsåret %s är %s.",
+ tui_message("Räkenskapsår", "Räkenskapsåret %s är %s.",
labels[cursor],
closed[cursor] ? "återöppnat" : "avslutat");
else
@@ -1362,11 +549,11 @@ static void show_error(const char *title, const char *resp)
char *code = jstr_dup(resp, "error.code");
char *msg = jstr_dup(resp, "error.message");
if ((!code || !*code) && (!msg || !*msg)) {
- message(title, "%s", resp && *resp
+ tui_message(title, "%s", resp && *resp
? resp
: "Inget svar från servern (kör daemonen?)");
} else {
- message(title, "%s: %s", code && *code ? code : "fel",
+ tui_message(title, "%s: %s", code && *code ? code : "fel",
msg && *msg ? msg : "okänt fel");
}
free(code);
@@ -1442,7 +629,7 @@ static char *file_browser(struct app *a, const char *start_dir)
for (;;) {
DIR *d = opendir(dir);
if (!d) {
- message("Filväljare", "Kan inte öppna %s", dir);
+ tui_message("Filväljare", "Kan inte öppna %s", dir);
return NULL;
}
struct fentry *ents = NULL;
@@ -1481,7 +668,7 @@ static char *file_browser(struct app *a, const char *start_dir)
}
char title[1200];
snprintf(title, sizeof title, "Välj fil — %.200s", dir);
- int sel = select_list(title, items, (int)n + 1, 0, 1, NULL, 0, NULL, 0);
+ int sel = tui_select_list(title, items, (int)n + 1, 0, 1, NULL, 0, NULL, 0);
for (size_t i = 0; i <= n; i++)
free(items[i]);
free(items);
@@ -1561,7 +748,7 @@ static void attachment_download(struct app *a, int64_t att_id)
unsigned char *data = NULL;
size_t n = 0;
if (!b64 || util_b64_decode(b64, strlen(b64), &data, &n) != 0) {
- message("Underlag", "Kunde inte avkoda innehållet.");
+ tui_message("Underlag", "Kunde inte avkoda innehållet.");
goto done;
}
const char *home = getenv("HOME");
@@ -1571,7 +758,7 @@ static void attachment_download(struct app *a, int64_t att_id)
if (stat(dl, &sb) != 0 || !S_ISDIR(sb.st_mode))
snprintf(dl, sizeof dl, "%s", home && *home ? home : ".");
snprintf(def, sizeof def, "%s/%s", dl, fn && *fn ? fn : "underlag");
- char *path = prompt("Spara underlag: ", def, 0);
+ char pathbuf[512]; char *path = tui_prompt_into(pathbuf, sizeof pathbuf, "Spara underlag: ", def, 0) ? pathbuf : NULL;
if (!path || !*path)
goto done;
char full[600];
@@ -1580,13 +767,13 @@ static void attachment_download(struct app *a, int64_t att_id)
else
snprintf(full, sizeof full, "%s", path);
if (stat(full, &sb) == 0) {
- char *ans = prompt("Filen finns, skriv över? (j/n): ", "n", 0);
+ char ansbuf[512]; char *ans = tui_prompt_into(ansbuf, sizeof ansbuf, "Filen finns, skriv över? (j/n): ", "n", 0) ? ansbuf : NULL;
if (!ans || (ans[0] != 'j' && ans[0] != 'J'))
goto done;
}
FILE *f = fopen(full, "wb");
if (!f || fwrite(data, 1, n, f) != n) {
- message("Underlag", "Kunde inte skriva %s: %s", full, strerror(errno));
+ tui_message("Underlag", "Kunde inte skriva %s: %s", full, strerror(errno));
if (f)
fclose(f);
goto done;
@@ -1597,17 +784,17 @@ static void attachment_download(struct app *a, int64_t att_id)
util_sha256(data, n, raw);
util_hex(raw, sizeof raw, hex);
if (sha && *sha && strcmp(hex, sha) != 0)
- message("Underlag", "VARNING: kontrollsumman stämmer inte.\nSparat: %s",
+ tui_message("Underlag", "VARNING: kontrollsumman stämmer inte.\nSparat: %s",
full);
else
- message("Underlag", "Sparat: %s\n(%zu byte)", full, n);
+ tui_message("Underlag", "Sparat: %s\n(%zu byte)", full, n);
int is_text = (mime && strncmp(mime, "text/", 5) == 0) ||
(n > 0 && bytes_look_text(data, n));
if (is_text && n <= 256 * 1024) {
char *copy = xmalloc(n + 1);
memcpy(copy, data, n);
copy[n] = '\0';
- text_view("Underlag", copy, NULL);
+ tui_pager("Underlag", copy, NULL);
free(copy);
}
done:
@@ -1657,13 +844,13 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
snprintf(path, sizeof path, "result.rows.%zu.credit_ore", i);
int64_t credit = jint_val(resp, path, 0);
char d[32], c[32];
- kr_format(debit, d, sizeof d);
- kr_format(credit, c, sizeof c);
+ tui_kr_format(debit, d, sizeof d);
+ tui_kr_format(credit, c, sizeof c);
char acol[32], ncol[256];
snprintf(acol, sizeof acol, "%s", acc ? acc : "");
- pad_field(acol, sizeof acol, 6);
+ tui_pad_field(acol, sizeof acol, 6);
snprintf(ncol, sizeof ncol, "%s", name ? name : "");
- pad_field(ncol, sizeof ncol, 34);
+ tui_pad_field(ncol, sizeof ncol, 34);
snprintf(line, sizeof line, " %s %s D %12s K %12s\n", acol,
ncol, d, c);
buf_append(&text, line, strlen(line));
@@ -1720,10 +907,10 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
"upp/ned F5 = uppdatera ^N = nytt verifikat c ="
" rätta ^F = bifoga%s Esc/q = tillbaka",
natts ? " f = underlag" : "");
- frame("Verifikat");
+ tui_frame("Verifikat");
for (int i = 0; i < view && top + i < n; i++)
mvaddnstr(2 + i, 2, lines[top + i], COLS - 4);
- hints(hint);
+ tui_hints(hint);
refresh();
int ch = ui_getch();
if (ch == 'q' || ch == 27) {
@@ -1739,7 +926,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
stop = 1;
}
} else if (ch == 'c') {
- char *reason = prompt("Beskriv rättelsen: ", "", 0);
+ char reasonbuf[512]; char *reason = tui_prompt_into(reasonbuf, sizeof reasonbuf, "Beskriv rättelsen: ", "", 0) ? reasonbuf : NULL;
if (reason && *reason) {
yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
yyjson_mut_val *o = yyjson_mut_obj(d);
@@ -1754,7 +941,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
a->org, cargs);
if (r && client_ok(r)) {
int64_t new_id = jint_val(r, "result.id", 0);
- message("Rättat", "Ändringsverifikat %lld skapat",
+ tui_message("Rättat", "Ändringsverifikat %lld skapat",
(long long)new_id);
corrected = 1;
} else {
@@ -1771,14 +958,14 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
struct stat sb;
if (stat(path, &sb) == 0 &&
(long)sb.st_size > a->max_attachment_bytes) {
- message("Bilaga", "Filen är %.1f MB, max %ld MB.",
+ tui_message("Bilaga", "Filen är %.1f MB, max %ld MB.",
(double)sb.st_size / (1024 * 1024),
a->max_attachment_bytes / (1024 * 1024));
free(path);
} else {
char *b64 = read_file_b64(path);
if (!b64) {
- message("Fel", "Kunde inte läsa %s", path);
+ tui_message("Fel", "Kunde inte läsa %s", path);
free(path);
} else {
const char *base = strrchr(path, '/');
@@ -1797,7 +984,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
char *r = client_rpc(&a->conn, "attachment.put",
a->session, a->org, fargs);
if (r && client_ok(r))
- message("Underlag", "Bifogat.");
+ tui_message("Underlag", "Bifogat.");
else
show_error("Kunde inte bifoga", r);
free(r);
@@ -1822,7 +1009,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
free(fn);
}
int acursor = 0;
- int asel = select_list("Underlag", alines, (int)natts, 0, 0,
+ int asel = tui_select_list("Underlag", alines, (int)natts, 0, 0,
&acursor, 0, "ta bort", 0);
if (asel >= 0)
attachment_download(a, aids[asel]);
@@ -1832,7 +1019,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
snprintf(q, sizeof q,
"Ta bort '%s' från verifikatet? (j/n): ",
alines[acursor]);
- char *ans = prompt(q, "n", 0);
+ char ansbuf[512]; char *ans = tui_prompt_into(ansbuf, sizeof ansbuf, q, "n", 0) ? ansbuf : NULL;
if (ans && (ans[0] == 'j' || ans[0] == 'J')) {
char uargs[64];
snprintf(uargs, sizeof uargs,
@@ -1841,7 +1028,7 @@ static int voucher_detail(struct app *a, int64_t id, int64_t *out_new)
char *r = client_rpc(&a->conn, "attachment.unlink",
a->session, a->org, uargs);
if (r && client_ok(r)) {
- message("Underlag", "Länken borttagen.");
+ tui_message("Underlag", "Länken borttagen.");
want_refresh = 1;
stop = 1;
} else {
@@ -2033,7 +1220,7 @@ static int64_t vouchers_new(struct app *a)
text_w = 8;
for (;;) {
- frame("Nytt verifikat");
+ tui_frame("Nytt verifikat");
int caret_y = -1, caret_x = -1;
attron(A_BOLD);
mvaddstr(2, 2, "Datum");
@@ -2050,7 +1237,7 @@ static int64_t vouchers_new(struct app *a)
int w = hw[k] > 1 ? hw[k] : 1;
char padded[512];
snprintf(padded, sizeof padded, "%s", hvals[k]);
- pad_field(padded, sizeof padded, w);
+ tui_pad_field(padded, sizeof padded, w);
if (field == k) {
attron(A_REVERSE);
caret_y = hy[k];
@@ -2059,7 +1246,7 @@ static int64_t vouchers_new(struct app *a)
field_pos > slen)
? slen
: field_pos;
- int cw = (int)disp_width_n(hvals[k], cp);
+ int cw = (int)tui_disp_width_n(hvals[k], cp);
caret_x = hx[k] + (cw > w ? w : cw);
}
mvaddstr(hy[k], hx[k], padded);
@@ -2079,9 +1266,9 @@ static int64_t vouchers_new(struct app *a)
for (int i = 0; i < nrows; i++) {
int y = 7 + i;
int64_t v;
- if (parse_kr(rows[i].debit, &v) == 0)
+ if (tui_parse_kr(rows[i].debit, &v) == 0)
sum_d += v;
- if (parse_kr(rows[i].credit, &v) == 0)
+ if (tui_parse_kr(rows[i].credit, &v) == 0)
sum_c += v;
int sel_ci = -1;
if (field >= 3) {
@@ -2097,7 +1284,7 @@ static int64_t vouchers_new(struct app *a)
int w = ws[k] > 1 ? ws[k] : 1;
char padded[512];
snprintf(padded, sizeof padded, "%s", vals[k]);
- pad_field(padded, sizeof padded, w);
+ tui_pad_field(padded, sizeof padded, w);
if (k == sel_ci) {
attron(A_REVERSE);
caret_y = y;
@@ -2106,7 +1293,7 @@ static int64_t vouchers_new(struct app *a)
field_pos > slen)
? slen
: field_pos;
- int cw = (int)disp_width_n(vals[k], cp);
+ int cw = (int)tui_disp_width_n(vals[k], cp);
caret_x = xs[k] + (cw > w ? w : cw);
}
mvaddstr(y, xs[k], padded);
@@ -2117,16 +1304,16 @@ static int64_t vouchers_new(struct app *a)
char nbuf[512];
const char *nm = acct_name(a, rows[i].account);
snprintf(nbuf, sizeof nbuf, "%s", nm ? nm : "");
- pad_field(nbuf, sizeof nbuf, name_w);
+ tui_pad_field(nbuf, sizeof nbuf, name_w);
attron(A_DIM);
mvaddstr(y, x_name, nbuf);
attroff(A_DIM);
}
}
char d1[32], c1[32], diff[32];
- kr_format(sum_d, d1, sizeof d1);
- kr_format(sum_c, c1, sizeof c1);
- kr_format(sum_d - sum_c, diff, sizeof diff);
+ tui_kr_format(sum_d, d1, sizeof d1);
+ tui_kr_format(sum_c, c1, sizeof c1);
+ tui_kr_format(sum_d - sum_c, diff, sizeof diff);
int balanced = sum_d == sum_c;
move(7 + nrows, 2);
clrtoeol();
@@ -2149,7 +1336,7 @@ static int64_t vouchers_new(struct app *a)
attroff(A_BOLD);
if (!balanced)
mvprintw(7 + nrows + 2, 2, "Differens: %s", diff);
- hints("F4 = mall ^F = bifoga fil Tab = byta fält F5 = validera"
+ tui_hints("F4 = mall ^F = bifoga fil Tab = byta fält F5 = validera"
" ^X = rensa rad ^Enter = bokför Esc = avbryt");
if (caret_y >= 0)
move(caret_y, caret_x);
@@ -2203,7 +1390,7 @@ static int64_t vouchers_new(struct app *a)
}
size_t ln = jarr_size(lresp, "result.items");
if (ln == 0) {
- message("Mallar",
+ tui_message("Mallar",
"Inga mallar ännu. Skapa en under Mallar först.");
free(lresp);
continue;
@@ -2223,7 +1410,7 @@ static int64_t vouchers_new(struct app *a)
char *ds = jstr_dup(lresp, path);
char nmbuf[128];
snprintf(nmbuf, sizeof nmbuf, "%s", nm ? nm : "");
- pad_field(nmbuf, sizeof nmbuf, 24);
+ tui_pad_field(nmbuf, sizeof nmbuf, 24);
snprintf(line, sizeof line, "%s %-3s %2lld rader %s", nmbuf,
ser ? ser : "", (long long)rc, ds ? ds : "");
names[i] = xstrdup(nm ? nm : "");
@@ -2232,7 +1419,7 @@ static int64_t vouchers_new(struct app *a)
free(ser);
free(ds);
}
- int tsel = select_list("Välj mall", lines, (int)ln, 0, 1, NULL, 0, NULL, 0);
+ int tsel = tui_select_list("Välj mall", lines, (int)ln, 0, 1, NULL, 0, NULL, 0);
if (tsel >= 0)
snprintf(tname, sizeof tname, "%s", names[tsel]);
for (size_t i = 0; i < ln; i++) {
@@ -2260,10 +1447,10 @@ static int64_t vouchers_new(struct app *a)
show_error("Mall", resp);
free(resp);
} else {
- char *xs = prompt("Belopp (x, kr): ", "", 0);
+ char xsbuf[512]; char *xs = tui_prompt_into(xsbuf, sizeof xsbuf, "Belopp (x, kr): ", "", 0) ? xsbuf : NULL;
double xv = 0;
if (xs && *xs && !parse_x_double(xs, &xv)) {
- message("Fel", "Ogiltigt belopp.");
+ tui_message("Fel", "Ogiltigt belopp.");
free(resp);
continue;
}
@@ -2294,7 +1481,7 @@ static int64_t vouchers_new(struct app *a)
char ferr[256] = "";
if (formula_resolve_rows(in, n, xv, out, &on, ferr,
sizeof ferr) != 0) {
- message("Mall", "%s",
+ tui_message("Mall", "%s",
ferr[0] ? ferr : "kunde inte lösa mallen");
} else {
memset(rows, 0, sizeof rows);
@@ -2304,11 +1491,11 @@ static int64_t vouchers_new(struct app *a)
"%s", out[i].account);
char tmp[32];
if (out[i].debit_ore) {
- kr_format(out[i].debit_ore, tmp, sizeof tmp);
+ tui_kr_format(out[i].debit_ore, tmp, sizeof tmp);
snprintf(rows[i].debit, sizeof rows[i].debit,
"%s", tmp);
} else {
- kr_format(out[i].credit_ore, tmp,
+ tui_kr_format(out[i].credit_ore, tmp,
sizeof tmp);
snprintf(rows[i].credit,
sizeof rows[i].credit, "%s", tmp);
@@ -2352,7 +1539,7 @@ static int64_t vouchers_new(struct app *a)
struct stat sb;
if (stat(path, &sb) == 0 &&
(long)sb.st_size > a->max_attachment_bytes) {
- message("Bilaga", "Filen är %.1f MB, max %ld MB.",
+ tui_message("Bilaga", "Filen är %.1f MB, max %ld MB.",
(double)sb.st_size / (1024 * 1024),
a->max_attachment_bytes / (1024 * 1024));
free(path);
@@ -2360,9 +1547,9 @@ static int64_t vouchers_new(struct app *a)
}
char *b64 = read_file_b64(path);
if (!b64) {
- message("Bilaga", "Kunde inte läsa %s", path);
+ tui_message("Bilaga", "Kunde inte läsa %s", path);
} else if (natt >= 32) {
- message("Bilaga", "Max 32 bilagor per verifikat.");
+ tui_message("Bilaga", "Max 32 bilagor per verifikat.");
} else {
const char *base = strrchr(path, '/');
base = base ? base + 1 : path;
@@ -2429,9 +1616,9 @@ static int64_t vouchers_new(struct app *a)
if (!rows[i].account[0])
continue;
int64_t dv = 0, cv = 0;
- if (parse_kr(rows[i].debit, &dv) != 0 ||
- parse_kr(rows[i].credit, &cv) != 0) {
- message("Fel", "Ogiltigt belopp på rad %d", i + 1);
+ if (tui_parse_kr(rows[i].debit, &dv) != 0 ||
+ tui_parse_kr(rows[i].credit, &cv) != 0) {
+ tui_message("Fel", "Ogiltigt belopp på rad %d", i + 1);
yyjson_mut_doc_free(d);
goto next_key;
}
@@ -2463,14 +1650,14 @@ static int64_t vouchers_new(struct app *a)
char *r = client_read_line(&a->conn);
if (r && client_ok(r)) {
int64_t num = jint_val(r, "result.number", 0);
- message("Validering OK",
+ tui_message("Validering OK",
"Nummer %lld skulle tilldelas.", (long long)num);
} else {
show_error("Validering misslyckades", r);
}
free(r);
} else {
- message("Fel", "Kunde inte nå servern");
+ tui_message("Fel", "Kunde inte nå servern");
}
free(raw);
} else {
@@ -2481,7 +1668,7 @@ static int64_t vouchers_new(struct app *a)
int64_t num = jint_val(r, "result.number", 0);
char *ser = jstr_dup(r, "result.series");
int replayed = jbool_val(r, "result.replayed", 0);
- message("Bokfört", "%s%lld (id %lld)%s", ser ? ser : "",
+ tui_message("Bokfört", "%s%lld (id %lld)%s", ser ? ser : "",
(long long)num, (long long)id,
replayed ? " — redan bokfört (idempotent)" : "");
free(ser);
@@ -2538,9 +1725,9 @@ static int64_t vouchers_new(struct app *a)
/* first keystroke in a freshly focused field replaces its content
(so a prefilled date can be typed over directly) */
if (field == 0)
- date_field_edit(buf, cap, &field_pos, ch, &field_fresh);
+ tui_date_field_edit(buf, cap, &field_pos, ch, &field_fresh);
else
- field_edit(buf, cap, &field_pos, ch, &field_fresh);
+ tui_field_edit(buf, cap, &field_pos, ch, &field_fresh);
normalize_rows(rows, &nrows, &field);
nfields = 3 + 4 * nrows;
}
@@ -2633,7 +1820,7 @@ static void vouchers_screen(struct app *a)
}
}
int cur = start;
- int sel = select_list("Verifikat", items, (int)total, start, 1, &cur,
+ int sel = tui_select_list("Verifikat", items, (int)total, start, 1, &cur,
1, NULL, 0);
if (cur >= 0 && (size_t)cur < n)
a->voucher_sel = ids[cur];
@@ -2800,9 +1987,9 @@ static void accounts_report(struct app *a)
char vatbuf[32];
char tibuf[32];
snprintf(ncol, sizeof ncol, "%s", name ? name : "");
- pad_field(ncol, sizeof ncol, name_w);
+ tui_pad_field(ncol, sizeof ncol, name_w);
snprintf(tibuf, sizeof tibuf, "%s", type_sv(type));
- pad_field(tibuf, sizeof tibuf, 12);
+ tui_pad_field(tibuf, sizeof tibuf, 12);
snprintf(line, sizeof line, "%-6s %s %s %-5s %-6s %-8s\n",
number ? number : "", ncol, tibuf,
active ? "ja" : "nej", sru ? sru : "",
@@ -2820,7 +2007,7 @@ static void accounts_report(struct app *a)
(long long)n, (long long)active_count);
buf_append(&text, line, strlen(line));
buf_append(&text, "\0", 1);
- int again = text_view("Kontolista", (char *)text.p, NULL);
+ int again = tui_pager("Kontolista", (char *)text.p, NULL);
buf_free(&text);
free(resp);
if (!again)
@@ -2895,7 +2082,7 @@ static int acct_in(const char *number, int lo, int hi)
/* Pad to `width` display columns without truncating (labels may be longer). */
static void pad_min(char *buf, size_t n, int width)
{
- int w = disp_width(buf);
+ int w = tui_disp_width(buf);
size_t len = strlen(buf);
while (w < width && len + 1 < n) {
buf[len++] = ' ';
@@ -2905,25 +2092,6 @@ static void pad_min(char *buf, size_t n, int width)
}
/* Right-align `text` in `width` display columns (headers with å/ä/ö). */
-static void pad_hdr(char *buf, size_t n, int width, const char *text)
-{
- int w = disp_width(text);
- int pad = width > w ? width - w : 0;
- snprintf(buf, n, "%*s%s", pad, "", text);
-}
-
-static void pad_label(char *buf, size_t n, const char *text, int width)
-{
- snprintf(buf, n, "%s", text);
- pad_field(buf, n, width);
-}
-
-static void amt_col(char *buf, size_t n, int width, int64_t ore)
-{
- char a[48];
- kr_format(ore, a, sizeof a);
- snprintf(buf, n, "%*s", width, a);
-}
/* ---------------- balansräkning ---------------- */
@@ -2958,11 +2126,11 @@ static int bs_rows(struct buf *t, yyjson_val *res, int lo, int hi,
int64_t per = ub - ib;
if (t) {
char nm[128], a1[48], a2[48], a3[48], a4[48];
- pad_label(nm, sizeof nm, vstr(row, "name"), 48);
- amt_col(a1, sizeof a1, 14, ib);
- amt_col(a2, sizeof a2, 14, ib);
- amt_col(a3, sizeof a3, 14, per);
- amt_col(a4, sizeof a4, 14, ub);
+ tui_pad_label(nm, sizeof nm, vstr(row, "name"), 48);
+ tui_amt_col(a1, sizeof a1, 14, ib);
+ tui_amt_col(a2, sizeof a2, 14, ib);
+ tui_amt_col(a3, sizeof a3, 14, per);
+ tui_amt_col(a4, sizeof a4, 14, ub);
buf_line(t, " %5s %s %s %s %s %s\n", acc, nm, a1, a2, a3, a4);
}
sum->ib += ib;
@@ -2978,11 +2146,11 @@ static void bs_sum_line(struct buf *t, const char *label,
const struct bs_sum *s)
{
char lbl[64], a1[48], a2[48], a3[48], a4[48];
- pad_label(lbl, sizeof lbl, label, 48);
- amt_col(a1, sizeof a1, 14, s->ib);
- amt_col(a2, sizeof a2, 14, s->ib);
- amt_col(a3, sizeof a3, 14, s->per);
- amt_col(a4, sizeof a4, 14, s->ub);
+ tui_pad_label(lbl, sizeof lbl, label, 48);
+ tui_amt_col(a1, sizeof a1, 14, s->ib);
+ tui_amt_col(a2, sizeof a2, 14, s->ib);
+ tui_amt_col(a3, sizeof a3, 14, s->per);
+ tui_amt_col(a4, sizeof a4, 14, s->ub);
buf_line(t, " %5s %s %s %s %s %s\n", "", lbl, a1, a2, a3, a4);
}
@@ -3066,9 +2234,9 @@ static void fmt_balans(struct buf *t, const struct app *a, yyjson_val *res)
yyjson_val *eq = yyjson_obj_get(res, "equity");
int64_t result = eq ? vint(eq, "result_ore") : 0;
char lbl[64], zero[48], a1[48];
- pad_label(lbl, sizeof lbl, "Beräknat resultat", 48);
- amt_col(zero, sizeof zero, 14, 0);
- amt_col(a1, sizeof a1, 14, result);
+ tui_pad_label(lbl, sizeof lbl, "Beräknat resultat", 48);
+ tui_amt_col(zero, sizeof zero, 14, 0);
+ tui_amt_col(a1, sizeof a1, 14, result);
buf_line(t, "\n");
buf_rule(t, BS_RULE_W);
buf_line(t, " %5s %s %s %s %s %s\n", "", lbl, zero, zero, a1, a1);
@@ -3137,9 +2305,9 @@ static void is_summary(struct buf *t, const char *label, int64_t cur,
char lbl[64], a1[48], a2[48];
if (blank)
buf_line(t, "\n");
- pad_label(lbl, sizeof lbl, label, 48);
- amt_col(a1, sizeof a1, 15, cur);
- amt_col(a2, sizeof a2, 15, prev);
+ tui_pad_label(lbl, sizeof lbl, label, 48);
+ tui_amt_col(a1, sizeof a1, 15, cur);
+ tui_amt_col(a2, sizeof a2, 15, prev);
buf_line(t, " %5s %s %s %s %s\n", "", lbl, a1, a1, a2);
}
@@ -3161,9 +2329,9 @@ static void is_print_group(struct buf *t, yyjson_val *res, yyjson_val *prev,
: -vint(row, "amount_ore");
int64_t p = is_prev_lookup(prev, acc, !gr->rev);
char nm[128], a1[48], a2[48];
- pad_label(nm, sizeof nm, vstr(row, "name"), 48);
- amt_col(a1, sizeof a1, 15, c);
- amt_col(a2, sizeof a2, 15, p);
+ tui_pad_label(nm, sizeof nm, vstr(row, "name"), 48);
+ tui_amt_col(a1, sizeof a1, 15, c);
+ tui_amt_col(a2, sizeof a2, 15, p);
buf_line(t, " %5s %s %s %s %s\n", acc, nm, a1, a1, a2);
sc += c;
sp += p;
@@ -3205,9 +2373,9 @@ static void is_range_rows(struct buf *t, yyjson_val *res, yyjson_val *prev,
: -vint(row, "amount_ore");
int64_t p = is_prev_lookup(prev, acc, k != 0);
char nm[128], a1[48], a2[48];
- pad_label(nm, sizeof nm, vstr(row, "name"), 48);
- amt_col(a1, sizeof a1, 15, c);
- amt_col(a2, sizeof a2, 15, p);
+ tui_pad_label(nm, sizeof nm, vstr(row, "name"), 48);
+ tui_amt_col(a1, sizeof a1, 15, c);
+ tui_amt_col(a2, sizeof a2, 15, p);
buf_line(t, " %5s %s %s %s %s\n", acc, nm, a1, a1, a2);
*sum += c;
*sum_pre += p;
@@ -3233,10 +2401,10 @@ static void fmt_resultat(struct buf *t, const struct app *a, yyjson_val *res,
buf_line(t, "%s\n", gr->section);
} else {
char lbl[80], h1[32], h2[32], h3[32];
- pad_label(lbl, sizeof lbl, gr->section, 48);
- pad_hdr(h1, sizeof h1, 15, "Period");
- pad_hdr(h2, sizeof h2, 15, "Ackumulerat");
- pad_hdr(h3, sizeof h3, 15, "Föreg. per.");
+ tui_pad_label(lbl, sizeof lbl, gr->section, 48);
+ tui_pad_hdr(h1, sizeof h1, 15, "Period");
+ tui_pad_hdr(h2, sizeof h2, 15, "Ackumulerat");
+ tui_pad_hdr(h3, sizeof h3, 15, "Föreg. per.");
buf_line(t, " %5s %s %s %s %s\n", "", lbl, h1, h2, h3);
buf_rule(t, IS_RULE_W);
buf_line(t, "\n");
@@ -3313,11 +2481,11 @@ static void fmt_saldobalans(struct buf *t, const struct app *a,
{
fmt_head(t, a, "Saldobalans", res);
char nmh[64], h1[32], h2[32], h3[32], h4[32];
- pad_label(nmh, sizeof nmh, "Benämning", 48);
- pad_hdr(h1, sizeof h1, 14, "Ingående");
- pad_hdr(h2, sizeof h2, 14, "Debet");
- pad_hdr(h3, sizeof h3, 14, "Kredit");
- pad_hdr(h4, sizeof h4, 14, "Utgående");
+ tui_pad_label(nmh, sizeof nmh, "Benämning", 48);
+ tui_pad_hdr(h1, sizeof h1, 14, "Ingående");
+ tui_pad_hdr(h2, sizeof h2, 14, "Debet");
+ tui_pad_hdr(h3, sizeof h3, 14, "Kredit");
+ tui_pad_hdr(h4, sizeof h4, 14, "Utgående");
buf_line(t, " %5s %s %s %s %s %s\n", "Konto", nmh, h1, h2, h3, h4);
buf_rule(t, BS_RULE_W);
yyjson_val *arr = yyjson_obj_get(res, "accounts");
@@ -3325,21 +2493,21 @@ static void fmt_saldobalans(struct buf *t, const struct app *a,
for (size_t i = 0; i < n; i++) {
yyjson_val *row = yyjson_arr_get(arr, i);
char nm[128], a1[48], a2[48], a3[48], a4[48];
- pad_label(nm, sizeof nm, vstr(row, "name"), 48);
- amt_col(a1, sizeof a1, 14, vint(row, "ib_ore"));
- amt_col(a2, sizeof a2, 14, vint(row, "debit_ore"));
- amt_col(a3, sizeof a3, 14, vint(row, "credit_ore"));
- amt_col(a4, sizeof a4, 14, vint(row, "ub_ore"));
+ tui_pad_label(nm, sizeof nm, vstr(row, "name"), 48);
+ tui_amt_col(a1, sizeof a1, 14, vint(row, "ib_ore"));
+ tui_amt_col(a2, sizeof a2, 14, vint(row, "debit_ore"));
+ tui_amt_col(a3, sizeof a3, 14, vint(row, "credit_ore"));
+ tui_amt_col(a4, sizeof a4, 14, vint(row, "ub_ore"));
buf_line(t, " %5s %s %s %s %s %s\n", vstr(row, "account"), nm, a1,
a2, a3, a4);
}
yyjson_val *tot = yyjson_obj_get(res, "totals");
char lbl[64], a1[48], a2[48], a3[48], a4[48];
- pad_label(lbl, sizeof lbl, "Summa", 48);
- amt_col(a1, sizeof a1, 14, vint(tot, "ib_ore"));
- amt_col(a2, sizeof a2, 14, vint(tot, "debit_ore"));
- amt_col(a3, sizeof a3, 14, vint(tot, "credit_ore"));
- amt_col(a4, sizeof a4, 14, vint(tot, "ub_ore"));
+ tui_pad_label(lbl, sizeof lbl, "Summa", 48);
+ tui_amt_col(a1, sizeof a1, 14, vint(tot, "ib_ore"));
+ tui_amt_col(a2, sizeof a2, 14, vint(tot, "debit_ore"));
+ tui_amt_col(a3, sizeof a3, 14, vint(tot, "credit_ore"));
+ tui_amt_col(a4, sizeof a4, 14, vint(tot, "ub_ore"));
buf_line(t, "\n");
buf_rule(t, BS_RULE_W);
buf_line(t, " %5s %s %s %s %s %s\n", "", lbl, a1, a2, a3, a4);
@@ -3508,11 +2676,11 @@ static void fmt_huvudbok(struct buf *t, const struct app *a, yyjson_val *res)
int64_t c = vint(ao, "credit_ore");
buf_line(t, "\n%s %s\n\n", vstr(ao, "account"), vstr(ao, "name"));
char a1[48];
- amt_col(a1, sizeof a1, 16, ib);
+ tui_amt_col(a1, sizeof a1, 16, ib);
char lbl[96];
- pad_label(lbl, sizeof lbl, "Ingående balans:", 59);
+ tui_pad_label(lbl, sizeof lbl, "Ingående balans:", 59);
buf_line(t, " %s %14s %14s %16s\n", lbl, "", "", a1);
- pad_label(lbl, sizeof lbl, "Ingående saldo:", 59);
+ tui_pad_label(lbl, sizeof lbl, "Ingående saldo:", 59);
buf_line(t, " %s %14s %14s %16s\n", lbl, "", "", a1);
buf_line(t, "\n");
yyjson_val *rows = yyjson_obj_get(ao, "rows");
@@ -3525,23 +2693,23 @@ static void fmt_huvudbok(struct buf *t, const struct app *a, yyjson_val *res)
const char *txt = vstr(ro, "row_description");
if (!*txt)
txt = vstr(ro, "description");
- pad_label(nm, sizeof nm, txt, 40);
- amt_col(dd, sizeof dd, 14, vint(ro, "debit_ore"));
- amt_col(cc, sizeof cc, 14, vint(ro, "credit_ore"));
+ tui_pad_label(nm, sizeof nm, txt, 40);
+ tui_amt_col(dd, sizeof dd, 14, vint(ro, "debit_ore"));
+ tui_amt_col(cc, sizeof cc, 14, vint(ro, "credit_ore"));
snprintf(deb, sizeof deb, "%s", vint(ro, "debit_ore") ? dd : "");
snprintf(kre, sizeof kre, "%s", vint(ro, "credit_ore") ? cc : "");
- amt_col(sal, sizeof sal, 16, vint(ro, "saldo_ore"));
+ tui_amt_col(sal, sizeof sal, 16, vint(ro, "saldo_ore"));
buf_line(t, " %-7s %-10s %s %14s %14s %16s\n", ver,
vstr(ro, "date"), nm, deb, kre, sal);
}
char s1[48], s2[48], s3[48];
- amt_col(s1, sizeof s1, 14, d);
- amt_col(s2, sizeof s2, 14, c);
- amt_col(s3, sizeof s3, 16, d - c);
- pad_label(lbl, sizeof lbl, "Omslutning:", 59);
+ tui_amt_col(s1, sizeof s1, 14, d);
+ tui_amt_col(s2, sizeof s2, 14, c);
+ tui_amt_col(s3, sizeof s3, 16, d - c);
+ tui_pad_label(lbl, sizeof lbl, "Omslutning:", 59);
buf_line(t, " %s %14s %14s %16s\n", lbl, s1, s2, s3);
- amt_col(s3, sizeof s3, 16, vint(ao, "ub_ore"));
- pad_label(lbl, sizeof lbl, "Utgående saldo:", 59);
+ tui_amt_col(s3, sizeof s3, 16, vint(ao, "ub_ore"));
+ tui_pad_label(lbl, sizeof lbl, "Utgående saldo:", 59);
buf_line(t, " %s %14s %14s %16s\n", lbl, s1, s2, s3);
}
}
@@ -3557,7 +2725,7 @@ static void fmt_verifikationslista(struct buf *t, const struct app *a,
buf_line(t, "Senaste ver.: %s%lld\n\n", vstr(lv, "series"),
(long long)vint(lv, "number"));
char nmh[64];
- pad_label(nmh, sizeof nmh, "Benämning", 48);
+ tui_pad_label(nmh, sizeof nmh, "Benämning", 48);
buf_line(t, "%-8s %-11s %s %14s %14s\n", "Ver.", "Datum/Konto", nmh,
"Debet", "Kredit");
buf_rule(t, 98);
@@ -3575,9 +2743,9 @@ static void fmt_verifikationslista(struct buf *t, const struct app *a,
for (size_t k = 0; k < nr; k++) {
yyjson_val *ro = yyjson_arr_get(rows, k);
char nm[256], dd[48], cc[48], deb[48], kre[48];
- pad_label(nm, sizeof nm, vstr(ro, "name"), 48);
- amt_col(dd, sizeof dd, 14, vint(ro, "debit_ore"));
- amt_col(cc, sizeof cc, 14, vint(ro, "credit_ore"));
+ tui_pad_label(nm, sizeof nm, vstr(ro, "name"), 48);
+ tui_amt_col(dd, sizeof dd, 14, vint(ro, "debit_ore"));
+ tui_amt_col(cc, sizeof cc, 14, vint(ro, "credit_ore"));
snprintf(deb, sizeof deb, "%s", vint(ro, "debit_ore") ? dd : "");
snprintf(kre, sizeof kre, "%s", vint(ro, "credit_ore") ? cc : "");
buf_line(t, " %-10s %s %14s %14s\n", vstr(ro, "account"),
@@ -3586,8 +2754,8 @@ static void fmt_verifikationslista(struct buf *t, const struct app *a,
}
yyjson_val *tot = yyjson_obj_get(res, "totals");
char s1[48], s2[48];
- amt_col(s1, sizeof s1, 14, vint(tot, "debit_ore"));
- amt_col(s2, sizeof s2, 14, vint(tot, "credit_ore"));
+ tui_amt_col(s1, sizeof s1, 14, vint(tot, "debit_ore"));
+ tui_amt_col(s2, sizeof s2, 14, vint(tot, "credit_ore"));
buf_line(t, "\n%-69s %14s %14s\n", "Omslutning:", s1, s2);
}
@@ -3598,7 +2766,7 @@ static void eskd_save(struct app *a, const char *from, const char *to)
snprintf(period, sizeof period, "%.4s%.2s", to, to + 5);
snprintf(def, sizeof def, "%s/eskd_%s.xml", home && *home ? home : ".",
period);
- char *path = prompt("Spara eSKD-fil: ", def, 0);
+ char pathbuf[512]; char *path = tui_prompt_into(pathbuf, sizeof pathbuf, "Spara eSKD-fil: ", def, 0) ? pathbuf : NULL;
if (!path || !*path)
return;
if (path[0] == '~' && (path[1] == '/' || path[1] == '\0'))
@@ -3618,16 +2786,16 @@ static void eskd_save(struct app *a, const char *from, const char *to)
unsigned char *data = NULL;
size_t n = 0;
if (!b64 || util_b64_decode(b64, strlen(b64), &data, &n) != 0) {
- message("eSKD", "Kunde inte avkoda filen från servern.");
+ tui_message("eSKD", "Kunde inte avkoda filen från servern.");
} else {
FILE *f = fopen(full, "wb");
if (!f || fwrite(data, 1, n, f) != n) {
- message("eSKD", "Kunde inte skriva %s: %s", full, strerror(errno));
+ tui_message("eSKD", "Kunde inte skriva %s: %s", full, strerror(errno));
if (f)
fclose(f);
} else {
fclose(f);
- message("eSKD", "Sparat: %s\n(%zu byte, ISO-8859-1)", full, n);
+ tui_message("eSKD", "Sparat: %s\n(%zu byte, ISO-8859-1)", full, n);
}
}
free(data);
@@ -3813,8 +2981,8 @@ static void sru_rows(struct buf *t, yyjson_val *arr)
for (size_t i = 0; i < n; i++) {
yyjson_val *row = yyjson_arr_get(arr, i);
char lbl[256], amt[48];
- pad_label(lbl, sizeof lbl, sru_label(vstr(row, "code")), 66);
- kr_format(vint(row, "amount") * 100, amt, sizeof amt);
+ tui_pad_label(lbl, sizeof lbl, sru_label(vstr(row, "code")), 66);
+ tui_kr_format(vint(row, "amount") * 100, amt, sizeof amt);
buf_line(t, "%s %14s\n", lbl, amt);
}
}
@@ -3841,7 +3009,7 @@ static void fmt_ink2(struct buf *t, const struct app *a, yyjson_val *res)
for (size_t i = 0; i < nu; i++) {
yyjson_val *row = yyjson_arr_get(un, i);
char amt[48];
- kr_format(vint(row, "amount_ore"), amt, sizeof amt);
+ tui_kr_format(vint(row, "amount_ore"), amt, sizeof amt);
buf_line(t, " %s %s\n", vstr(row, "account"), amt);
}
}
@@ -3855,7 +3023,7 @@ static void sru_save(struct app *a)
struct stat sb;
if (stat(def, &sb) != 0 || !S_ISDIR(sb.st_mode))
snprintf(def, sizeof def, "%s", home && *home ? home : ".");
- char *dir = prompt("Spara SRU-filer i: ", def, 0);
+ char dirbuf[512]; char *dir = tui_prompt_into(dirbuf, sizeof dirbuf, "Spara SRU-filer i: ", def, 0) ? dirbuf : NULL;
if (!dir || !*dir)
return;
char full[512];
@@ -3892,7 +3060,7 @@ static void sru_save(struct app *a)
} else {
if (f)
fclose(f);
- message("SRU", "Kunde inte skriva %s: %s", saved[i],
+ tui_message("SRU", "Kunde inte skriva %s: %s", saved[i],
strerror(errno));
}
free(data);
@@ -3900,7 +3068,7 @@ static void sru_save(struct app *a)
}
free(resp);
if (ok == 2)
- message("SRU", "Sparat:\n%s\n%s\n\nLadda upp båda i Skatteverkets"
+ tui_message("SRU", "Sparat:\n%s\n%s\n\nLadda upp båda i Skatteverkets"
" filöverföring.",
saved[0], saved[1]);
}
@@ -3922,7 +3090,7 @@ static void reports_screen(struct app *a)
for (;;) {
if (g_quit)
return;
- int sel = menu("Rapporter", report_items, 9, 0, &last_sel);
+ int sel = tui_menu("Rapporter", report_items, 9, 0, &last_sel);
if (sel < 0)
return;
if (sel == 6) {
@@ -3947,7 +3115,7 @@ static void reports_screen(struct app *a)
else
buf_line(&out, "%s", resp);
buf_append(&out, "", 1);
- int sagain = text_view("Rapport", (const char *)out.p,
+ int sagain = tui_pager("Rapport", (const char *)out.p,
"s = spara SRU");
buf_free(&out);
yyjson_doc_free(sd);
@@ -3999,12 +3167,12 @@ static void reports_screen(struct app *a)
break;
case 5: {
cmd = "report.vat";
- char *from = date_prompt("Från (YYYY-MM-DD): ", a->fy_start);
+ char frombuf[16]; char *from = tui_date_prompt_into(frombuf, sizeof frombuf, "Från (YYYY-MM-DD): ", a->fy_start) ? frombuf : NULL;
if (!from)
continue;
char f[16];
snprintf(f, sizeof f, "%s", from);
- char *to = date_prompt("Till (YYYY-MM-DD): ", a->fy_end);
+ char tobuf[16]; char *to = tui_date_prompt_into(tobuf, sizeof tobuf, "Till (YYYY-MM-DD): ", a->fy_end) ? tobuf : NULL;
if (!to)
continue;
snprintf(vat_from, sizeof vat_from, "%s", f);
@@ -4060,7 +3228,7 @@ static void reports_screen(struct app *a)
fmt_moms(&out, a, res);
}
buf_append(&out, "", 1);
- int again = text_view("Rapport", (const char *)out.p,
+ int again = tui_pager("Rapport", (const char *)out.p,
sel == 5 ? "s = spara eSKD" : NULL);
buf_free(&out);
yyjson_doc_free(d);
@@ -4090,7 +3258,7 @@ static int64_t pick_voucher(struct app *a)
}
size_t n = jarr_size(resp, "result.items");
if (n == 0) {
- message("Verifikat", "Inga verifikat i räkenskapsåret.");
+ tui_message("Verifikat", "Inga verifikat i räkenskapsåret.");
free(resp);
return 0;
}
@@ -4117,7 +3285,7 @@ static int64_t pick_voucher(struct app *a)
free(date);
free(desc);
}
- int sel = select_list("Välj verifikat", items, (int)n, 0, 1, NULL, 0,
+ int sel = tui_select_list("Välj verifikat", items, (int)n, 0, 1, NULL, 0,
NULL, 0);
int64_t out = sel >= 0 ? ids[sel] : 0;
for (size_t i = 0; i < n; i++)
@@ -4154,7 +3322,7 @@ static void inbox_screen(struct app *a)
int64_t size = jint_val(resp, path, 0);
char fnbuf[256];
snprintf(fnbuf, sizeof fnbuf, "%s", fn ? fn : "");
- pad_field(fnbuf, sizeof fnbuf, 40);
+ tui_pad_field(fnbuf, sizeof fnbuf, 40);
snprintf(line, sizeof line, "%s %8lld byte", fnbuf,
(long long)size);
items[i] = xstrdup(line);
@@ -4173,7 +3341,7 @@ static void inbox_screen(struct app *a)
top = (int)n - view;
if (top < 0)
top = 0;
- frame("Underlag (inkorg)");
+ tui_frame("Underlag (inkorg)");
for (size_t i = (size_t)top; i < n && (int)(i - (size_t)top) < view;
i++) {
int y = 2 + (int)(i - (size_t)top);
@@ -4185,7 +3353,7 @@ static void inbox_screen(struct app *a)
}
if (n == 0)
mvaddstr(2, 2, "Inga obokförda underlag.");
- hints("Enter = hämta k = koppla till verifikat a/^N = lägg till"
+ tui_hints("Enter = hämta k = koppla till verifikat a/^N = lägg till"
" fil PgUp/PgDn, Home/End rullar F5 = uppdatera Esc/q ="
" tillbaka");
refresh();
@@ -4222,7 +3390,7 @@ static void inbox_screen(struct app *a)
char *r = client_rpc(&a->conn, "attachment.link", a->session,
a->org, kargs);
if (r && client_ok(r))
- message("Underlag", "Kopplat till verifikatet.");
+ tui_message("Underlag", "Kopplat till verifikatet.");
else
show_error("Kunde inte koppla", r);
free(r);
@@ -4234,7 +3402,7 @@ static void inbox_screen(struct app *a)
struct stat sb;
if (stat(path, &sb) == 0 &&
(long)sb.st_size > a->max_attachment_bytes) {
- message("Bilaga", "Filen är %.1f MB, max %ld MB.",
+ tui_message("Bilaga", "Filen är %.1f MB, max %ld MB.",
(double)sb.st_size / (1024 * 1024),
a->max_attachment_bytes / (1024 * 1024));
free(path);
@@ -4242,7 +3410,7 @@ static void inbox_screen(struct app *a)
}
char *b64 = read_file_b64(path);
if (!b64) {
- message("Fel", "Kunde inte läsa %s", path);
+ tui_message("Fel", "Kunde inte läsa %s", path);
continue;
}
const char *base = strrchr(path, '/');
@@ -4267,7 +3435,7 @@ static void inbox_screen(struct app *a)
r = client_read_line(&a->conn);
free(raw);
if (r && client_ok(r))
- message("Underlag", "Sparat.");
+ tui_message("Underlag", "Sparat.");
else
show_error("Kunde inte spara", r);
free(r);
@@ -4283,7 +3451,7 @@ static void audit_screen(struct app *a)
for (;;) {
if (g_quit)
return;
- int sel = menu("Revision", audit_items, 2, 0, &last_sel);
+ int sel = tui_menu("Revision", audit_items, 2, 0, &last_sel);
if (sel < 0)
return;
if (sel == 0) {
@@ -4296,7 +3464,7 @@ static void audit_screen(struct app *a)
}
int64_t checked = jint_val(resp, "result.checked", 0);
int is_ok = jbool_val(resp, "result.ok", 0);
- message("Hashkedja", "%s\n%d händelser kontrollerade.",
+ tui_message("Hashkedja", "%s\n%d händelser kontrollerade.",
is_ok ? "Kedjan är intakt." : "KEDJAN ÄR BRUTEN!",
(int)checked);
free(resp);
@@ -4333,7 +3501,7 @@ static void audit_screen(struct app *a)
free(code);
}
buf_append(&text, "\0", 1);
- int again = text_view("Behandlingshistorik", (char *)text.p, NULL);
+ int again = tui_pager("Behandlingshistorik", (char *)text.p, NULL);
buf_free(&text);
free(resp);
if (!again)
@@ -4464,7 +3632,7 @@ static int template_form(struct app *a, const char *load_name)
text_w = 8;
for (;;) {
- frame(load_name ? "Redigera mall" : "Ny mall");
+ tui_frame(load_name ? "Redigera mall" : "Ny mall");
int caret_y = -1, caret_x = -1;
attron(A_BOLD);
mvaddstr(2, 2, "Namn");
@@ -4480,7 +3648,7 @@ static int template_form(struct app *a, const char *load_name)
int w = hw[k] > 1 ? hw[k] : 1;
char padded[512];
snprintf(padded, sizeof padded, "%s", hvals[k]);
- pad_field(padded, sizeof padded, w);
+ tui_pad_field(padded, sizeof padded, w);
if (field == k) {
attron(A_REVERSE);
caret_y = hy[k];
@@ -4489,7 +3657,7 @@ static int template_form(struct app *a, const char *load_name)
field_pos > slen)
? slen
: field_pos;
- int cw = (int)disp_width_n(hvals[k], cp);
+ int cw = (int)tui_disp_width_n(hvals[k], cp);
caret_x = hx[k] + (cw > w ? w : cw);
}
mvaddstr(hy[k], hx[k], padded);
@@ -4520,7 +3688,7 @@ static int template_form(struct app *a, const char *load_name)
int w = ws[k] > 1 ? ws[k] : 1;
char padded[512];
snprintf(padded, sizeof padded, "%s", vals[k]);
- pad_field(padded, sizeof padded, w);
+ tui_pad_field(padded, sizeof padded, w);
if (k == sel_ci) {
attron(A_REVERSE);
caret_y = y;
@@ -4529,7 +3697,7 @@ static int template_form(struct app *a, const char *load_name)
field_pos > slen)
? slen
: field_pos;
- int cw = (int)disp_width_n(vals[k], cp);
+ int cw = (int)tui_disp_width_n(vals[k], cp);
caret_x = xs[k] + (cw > w ? w : cw);
}
mvaddstr(y, xs[k], padded);
@@ -4540,7 +3708,7 @@ static int template_form(struct app *a, const char *load_name)
char nbuf[512];
const char *nm = acct_name(a, rows[i].account);
snprintf(nbuf, sizeof nbuf, "%s", nm ? nm : "");
- pad_field(nbuf, sizeof nbuf, name_w);
+ tui_pad_field(nbuf, sizeof nbuf, name_w);
attron(A_DIM);
mvaddstr(y, x_name, nbuf);
attroff(A_DIM);
@@ -4553,7 +3721,7 @@ static int template_form(struct app *a, const char *load_name)
"x = belopp i kronor. Positivt blir debet, negativt kredit.",
COLS - 4);
attroff(A_DIM);
- hints("Tab = byta fält F5 = validera ^X = rensa rad ^Enter = spara Esc = avbryt");
+ tui_hints("Tab = byta fält F5 = validera ^X = rensa rad ^Enter = spara Esc = avbryt");
if (caret_y >= 0)
move(caret_y, caret_x);
refresh();
@@ -4638,7 +3806,7 @@ static int template_form(struct app *a, const char *load_name)
if (!problem && used == 0)
problem = "Mallen behöver minst en rad.";
if (problem) {
- message("Mall", "%s", problem);
+ tui_message("Mall", "%s", problem);
continue;
}
@@ -4672,10 +3840,10 @@ static int template_form(struct app *a, const char *load_name)
free(args);
if (resp && client_ok(resp)) {
if (ch == KEY_F(5)) {
- message("Validering OK",
+ tui_message("Validering OK",
"Mallen är korrekt (%d rader).", used);
} else {
- message("Mall", "Mallen '%s' sparad.", tname);
+ tui_message("Mall", "Mallen '%s' sparad.", tname);
free(resp);
curs_set(0);
return 1;
@@ -4719,7 +3887,7 @@ static int template_form(struct app *a, const char *load_name)
}
if (!buf)
continue;
- field_edit(buf, cap, &field_pos, ch, &field_fresh);
+ tui_field_edit(buf, cap, &field_pos, ch, &field_fresh);
trow_normalize(rows, &nrows, &field);
nfields = 3 + 3 * nrows;
}
@@ -4729,7 +3897,7 @@ static void template_archive(struct app *a, const char *name)
{
char label[256];
snprintf(label, sizeof label, "Arkivera '%s'? (j/n): ", name);
- char *ans = prompt(label, "n", 0);
+ char ansbuf[512]; char *ans = tui_prompt_into(ansbuf, sizeof ansbuf, label, "n", 0) ? ansbuf : NULL;
if (!ans || (ans[0] != 'j' && ans[0] != 'J'))
return;
yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
@@ -4742,7 +3910,7 @@ static void template_archive(struct app *a, const char *name)
client_rpc(&a->conn, "template.archive", a->session, a->org, args);
free(args);
if (resp && client_ok(resp))
- message("Mall", "Mallen '%s' arkiverad.", name);
+ tui_message("Mall", "Mallen '%s' arkiverad.", name);
else
show_error("Kunde inte arkivera", resp);
free(resp);
@@ -4849,14 +4017,14 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
{
static struct tui_ibrow rows[64];
if (nold > 63) {
- message("Ingående balans",
+ tui_message("Ingående balans",
"För många konton (%d) för att redigera här.", nold);
return 0;
}
memset(rows, 0, sizeof rows);
for (int i = 0; i < nold && i < 63; i++) {
snprintf(rows[i].account, sizeof rows[i].account, "%s", old_acc[i]);
- kr_format(old_amt[i], rows[i].amount, sizeof rows[i].amount);
+ tui_kr_format(old_amt[i], rows[i].amount, sizeof rows[i].amount);
}
int nrows = nold > 0 ? nold : 1;
int field = 0;
@@ -4875,7 +4043,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
curs_set(1);
for (;;) {
- frame("Ingående balans");
+ tui_frame("Ingående balans");
int caret_y = -1, caret_x = -1;
attron(A_BOLD);
mvaddstr(2, 2,
@@ -4904,7 +4072,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
int w = ws[k] > 1 ? ws[k] : 1;
char padded[512];
snprintf(padded, sizeof padded, "%s", vals[k]);
- pad_field(padded, sizeof padded, w);
+ tui_pad_field(padded, sizeof padded, w);
if (k == sel_ci) {
attron(A_REVERSE);
caret_y = y;
@@ -4913,7 +4081,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
field_pos > slen)
? slen
: field_pos;
- int cw = (int)disp_width_n(vals[k], cp);
+ int cw = (int)tui_disp_width_n(vals[k], cp);
caret_x = xs[k] + (cw > w ? w : cw);
}
mvaddstr(y, xs[k], padded);
@@ -4924,13 +4092,13 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
char nbuf[512];
const char *nm = acct_name(a, rows[i].account);
snprintf(nbuf, sizeof nbuf, "%s", nm ? nm : "");
- pad_field(nbuf, sizeof nbuf, name_w);
+ tui_pad_field(nbuf, sizeof nbuf, name_w);
attron(A_DIM);
mvaddstr(y, x_name, nbuf);
attroff(A_DIM);
}
}
- hints("Tab = byta fält F5 = validera ^X = rensa rad ^Enter = spara Esc = avbryt");
+ tui_hints("Tab = byta fält F5 = validera ^X = rensa rad ^Enter = spara Esc = avbryt");
if (caret_y >= 0)
move(caret_y, caret_x);
refresh();
@@ -5012,12 +4180,12 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
problem = msg;
}
if (problem) {
- message("Ingående balans", "%s", problem);
+ tui_message("Ingående balans", "%s", problem);
continue;
}
if (ch == KEY_F(5)) {
- message("Validering OK", "%d konton, summan är noll.", used);
+ tui_message("Validering OK", "%d konton, summan är noll.", used);
continue;
}
@@ -5079,7 +4247,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
}
if (posted == 0) {
yyjson_mut_doc_free(d);
- message("Ingående balans", "Ingen ändring.");
+ tui_message("Ingående balans", "Ingen ändring.");
continue;
}
yyjson_mut_obj_add_val(d, o, "rows", arr);
@@ -5090,7 +4258,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
free(args);
if (resp && client_ok(resp)) {
int64_t num = jint_val(resp, "result.number", 0);
- message("Ingående balans",
+ tui_message("Ingående balans",
"Sparat som IB %lld (%d justerade rader).",
(long long)num, posted);
free(resp);
@@ -5123,7 +4291,7 @@ static int ib_form(struct app *a, char **old_acc, int64_t *old_amt, int nold)
}
if (!buf)
continue;
- field_edit(buf, cap, &field_pos, ch, &field_fresh);
+ tui_field_edit(buf, cap, &field_pos, ch, &field_fresh);
ibrow_normalize(rows, &nrows, &field);
nfields = 3 * nrows;
}
@@ -5157,7 +4325,7 @@ static void ib_screen(struct app *a)
top = n - view;
if (top < 0)
top = 0;
- frame("Ingående balans");
+ tui_frame("Ingående balans");
attron(A_BOLD);
mvprintw(2, 2, "Räkenskapsår %s ingående balans (effektiv)",
a->fy_label);
@@ -5174,19 +4342,19 @@ static void ib_screen(struct app *a)
for (int i = 0; i < view && top + i < n; i++) {
int idx = top + i;
char amount[32];
- kr_format(amts[idx], amount, sizeof amount);
+ tui_kr_format(amts[idx], amount, sizeof amount);
const char *nm = acct_name(a, accs[idx]);
char nbuf[128];
snprintf(nbuf, sizeof nbuf, "%s", nm ? nm : "");
- pad_field(nbuf, sizeof nbuf, 50);
+ tui_pad_field(nbuf, sizeof nbuf, 50);
mvprintw(6 + i, 2, "%-6s %s %14s", accs[idx], nbuf, amount);
}
char sumbuf[32];
- kr_format(sum, sumbuf, sizeof sumbuf);
+ tui_kr_format(sum, sumbuf, sizeof sumbuf);
attron(A_BOLD);
mvprintw(LINES - 4, 2, "Summa: %s", sumbuf);
attroff(A_BOLD);
- hints("e/^N = redigera PgUp/PgDn, Home/End rullar F5 ="
+ tui_hints("e/^N = redigera PgUp/PgDn, Home/End rullar F5 ="
" uppdatera Esc/q = tillbaka");
refresh();
int ch = ui_getch();
@@ -5295,13 +4463,13 @@ static void board_screen(struct app *a)
}
if (owner)
lines[n] = xstrdup("+ Ny ledamot (^N)");
- int s = select_list("Styrelseledamöter", lines, rows, cursor, 1,
+ int s = tui_select_list("Styrelseledamöter", lines, rows, cursor, 1,
&cursor, owner ? 1 : 0,
owner && n > 0 ? "ta bort" : NULL, 0);
if (s == -6 && cursor >= 0 && (size_t)cursor < n) {
char q[320];
snprintf(q, sizeof q, "Ta bort '%s'? (j/n): ", lines[cursor]);
- char *ans = prompt(q, "n", 0);
+ char ansbuf[512]; char *ans = tui_prompt_into(ansbuf, sizeof ansbuf, q, "n", 0) ? ansbuf : NULL;
if (ans && (ans[0] == 'j' || ans[0] == 'J')) {
char args[64];
snprintf(args, sizeof args, "{\"id\":%lld}",
@@ -5309,64 +4477,66 @@ static void board_screen(struct app *a)
char *r = client_rpc(&a->conn, "board.remove", a->session,
a->org, args);
if (r && client_ok(r)) {
- message("Styrelseledamöter", "Borttagen.");
+ tui_message("Styrelseledamöter", "Borttagen.");
} else {
show_error("Kunde inte ta bort", r);
}
free(r);
}
} else if (s == -4 || (s >= 0 && owner && (size_t)s == n)) {
- char namebuf[256] = "";
- char *name = prompt("Namn: ", "", 0);
- if (name && *name)
- snprintf(namebuf, sizeof namebuf, "%s", name);
- char *title = namebuf[0]
- ? prompt("Titel: ", "Styrelseledamot", 0)
- : NULL;
- if (namebuf[0] && title && *title) {
+ char namebuf[512] = "", titlebuf[512] = "";
+ int ok_name = tui_prompt_into(namebuf, sizeof namebuf, "Namn: ",
+ "", 0);
+ int ok_title = ok_name && namebuf[0]
+ ? tui_prompt_into(titlebuf, sizeof titlebuf,
+ "Titel: ",
+ "Styrelseledamot", 0)
+ : 0;
+ if (ok_name && namebuf[0] && ok_title && titlebuf[0]) {
yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
yyjson_mut_val *o = yyjson_mut_obj(d);
yyjson_mut_doc_set_root(d, o);
yyjson_mut_obj_add_strcpy(d, o, "name", namebuf);
- yyjson_mut_obj_add_strcpy(d, o, "title", title);
+ yyjson_mut_obj_add_strcpy(d, o, "title", titlebuf);
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
char *r = client_rpc(&a->conn, "board.add", a->session,
a->org, args);
free(args);
if (r && client_ok(r))
- message("Styrelseledamöter", "Tillagd.");
+ tui_message("Styrelseledamöter", "Tillagd.");
else
show_error("Kunde inte lägga till", r);
free(r);
}
} else if (s >= 0 && (size_t)s < n) {
if (!owner) {
- message("Styrelseledamöter",
+ tui_message("Styrelseledamöter",
"Endast ägare kan ändra styrelseledamöterna.");
} else {
- char namebuf[256] = "";
- char *name = prompt("Namn: ", names[s] ? names[s] : "", 0);
- if (name && *name)
- snprintf(namebuf, sizeof namebuf, "%s", name);
- char *title = namebuf[0]
- ? prompt("Titel: ",
- titles[s] ? titles[s] : "", 0)
- : NULL;
- if (namebuf[0] && title && *title) {
+ char namebuf[512] = "", titlebuf[512] = "";
+ int ok_name = tui_prompt_into(namebuf, sizeof namebuf,
+ "Namn: ",
+ names[s] ? names[s] : "", 0);
+ int ok_title = ok_name && namebuf[0]
+ ? tui_prompt_into(
+ titlebuf, sizeof titlebuf, "Titel: ",
+ titles[s] ? titles[s] : "", 0)
+ : 0;
+ if (ok_name && namebuf[0] && ok_title && titlebuf[0]) {
yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
yyjson_mut_val *o = yyjson_mut_obj(d);
yyjson_mut_doc_set_root(d, o);
yyjson_mut_obj_add_int(d, o, "id", ids[s]);
yyjson_mut_obj_add_strcpy(d, o, "name", namebuf);
- yyjson_mut_obj_add_strcpy(d, o, "title", title);
+ yyjson_mut_obj_add_strcpy(d, o, "title", titlebuf);
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
char *r = client_rpc(&a->conn, "board.update", a->session,
a->org, args);
free(args);
if (r && client_ok(r))
- message("Styrelseledamöter", "Sparad.");
+ tui_message("Styrelseledamöter", "Sparad.");
else
show_error("Kunde inte spara", r);
free(r);
@@ -5421,107 +4591,52 @@ static void yearinfo_screen(struct app *a)
return;
}
char *vals[6];
- vals[0] = jstr_dup(resp, "result.events");
- vals[1] = jstr_dup(resp, "result.agm_date");
- char divbuf[48];
- kr_format(jint_val(resp, "result.dividend_ore", 0), divbuf,
- sizeof divbuf);
- vals[2] = xstrdup(divbuf);
- vals[3] = jstr_dup(resp, "result.dividend_date");
- vals[4] = jstr_dup(resp, "result.employees");
- vals[5] = jstr_dup(resp, "result.notes");
- for (int i = 0; i < nf; i++)
- if (!vals[i])
- vals[i] = xstrdup("");
- free(resp);
-
- int sel = 0;
- int back = 0;
- for (;;) {
- frame("Information om året");
- attron(A_BOLD);
- mvaddstr(3, 2, "Uppgift");
- mvaddstr(3, 34, "Värde");
- attroff(A_BOLD);
- for (int i = 0; i < nf; i++) {
- char lbl[128], line[640];
- snprintf(lbl, sizeof lbl, "%s", labels[i]);
- pad_field(lbl, sizeof lbl, 31);
- snprintf(line, sizeof line, "%s %s", lbl, vals[i]);
- if (i == sel) {
- attron(A_REVERSE);
- mvaddnstr(5 + i, 2, line, COLS - 4);
- attroff(A_REVERSE);
- } else {
- if (!can_edit)
- attron(A_DIM);
- mvaddnstr(5 + i, 2, line, COLS - 4);
- if (!can_edit)
- attroff(A_DIM);
- }
- }
- hints(can_edit
- ? "upp/ned Enter = ändra F5 = uppdatera"
- " Esc/q = tillbaka ^C = avsluta"
- : "upp/ned F5 = uppdatera Esc/q = tillbaka"
- " ^C = avsluta (endast ägare/redovisare)");
- refresh();
- int ch = ui_getch();
- if (ch == KEY_UP && sel > 0)
- sel--;
- else if (ch == KEY_DOWN && sel < nf - 1)
- sel++;
- else if (ch == KEY_F(5))
- break; /* reload */
- else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) {
- if (!can_edit) {
- message("Information om året",
- "Endast ägare eller redovisare kan ändra.");
- continue;
- }
- char label[160];
- snprintf(label, sizeof label, "%s: ", labels[sel]);
- char *v = prompt(label, vals[sel], 0);
- if (!v)
- continue;
- yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
- yyjson_mut_val *o = yyjson_mut_obj(d);
- yyjson_mut_doc_set_root(d, o);
- yyjson_mut_obj_add_int(d, o, "id", a->fy);
- if (sel == 2) {
- int64_t ore = 0;
- if (parse_kr(v, &ore) != 0) {
- message("Ogiltigt värde", "%s", labels[sel]);
- yyjson_mut_doc_free(d);
- continue;
- }
- yyjson_mut_obj_add_int(d, o, keys[sel], ore);
- } else {
- if ((sel == 1 || sel == 3) && *v &&
- !util_parse_iso_date(v)) {
- message("Ogiltigt datum", "%s", v);
- yyjson_mut_doc_free(d);
- continue;
- }
- yyjson_mut_obj_add_strcpy(d, o, keys[sel], v);
- }
- char *uargs = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- char *r = client_rpc(&a->conn, "fiscal_year.update",
- a->session, a->org, uargs);
- free(uargs);
- if (!r || !client_ok(r))
- show_error("Kunde inte spara", r);
- free(r);
- break; /* reload from the server */
- } else if (ch == 27 || ch == 'q') {
- back = 1;
- break;
+ struct tui_form_field ff[6];
+ int64_t div_ore = jint_val(resp, "result.dividend_ore", 0);
+ memset(ff, 0, sizeof ff);
+ for (int i = 0; i < nf; i++) {
+ vals[i] = xcalloc(512, 1);
+ if (i != 2) {
+ char path[64], *v;
+ snprintf(path, sizeof path, "result.%s", keys[i]);
+ v = jstr_dup(resp, path);
+ snprintf(vals[i], 512, "%s", v ? v : "");
+ free(v);
}
+ ff[i].label = labels[i];
+ ff[i].value = vals[i];
+ ff[i].cap = 512;
+ }
+ free(resp);
+ ff[0].kind = TUI_F_TEXT;
+ ff[1].kind = TUI_F_DATE;
+ ff[2].kind = TUI_F_AMOUNT;
+ ff[2].ore = &div_ore;
+ ff[3].kind = TUI_F_DATE;
+ ff[4].kind = TUI_F_TEXT;
+ ff[5].kind = TUI_F_TEXT;
+ int r = tui_form_run("Information om året", ff, nf, can_edit);
+ if (r >= 0) {
+ yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
+ yyjson_mut_val *o = yyjson_mut_obj(d);
+ yyjson_mut_doc_set_root(d, o);
+ yyjson_mut_obj_add_int(d, o, "id", a->fy);
+ if (r == 2)
+ yyjson_mut_obj_add_int(d, o, "dividend_ore", div_ore);
+ else
+ yyjson_mut_obj_add_strcpy(d, o, keys[r], vals[r]);
+ char *uargs = yyjson_mut_write(d, 0, NULL);
+ yyjson_mut_doc_free(d);
+ char *rr = client_rpc(&a->conn, "fiscal_year.update", a->session,
+ a->org, uargs);
+ free(uargs);
+ if (!rr || !client_ok(rr))
+ show_error("Kunde inte spara", rr);
+ free(rr);
}
for (int i = 0; i < nf; i++)
free(vals[i]);
- if (back)
+ if (r == TUI_FORM_BACK)
return;
}
}
@@ -5539,120 +4654,58 @@ static void company_screen(struct app *a)
free(resp);
return;
}
- char **vals = xcalloc(nf, sizeof(char *));
+ char *vals[16];
+ struct tui_form_field ff[16];
+ memset(ff, 0, sizeof ff);
for (int i = 0; i < nf; i++) {
char path[64];
+ vals[i] = xcalloc(512, 1);
snprintf(path, sizeof path, "result.%s", COMPANY_FIELDS[i].key);
if (COMPANY_FIELDS[i].numeric) {
- char tmp[32];
- snprintf(tmp, sizeof tmp, "%lld",
+ snprintf(vals[i], 512, "%lld",
(long long)jint_val(resp, path, 0));
- vals[i] = xstrdup(tmp);
} else {
char *v = jstr_dup(resp, path);
- vals[i] = v ? v : xstrdup("");
+ snprintf(vals[i], 512, "%s", v ? v : "");
+ free(v);
}
+ ff[i].label = COMPANY_FIELDS[i].label;
+ ff[i].value = vals[i];
+ ff[i].cap = 512;
+ ff[i].kind = TUI_F_TEXT;
}
free(resp);
-
- int sel = 0;
- int back = 0;
- for (;;) {
- frame("Företagsuppgifter");
- attron(A_BOLD);
- mvaddstr(3, 2, "Uppgift");
- mvaddstr(3, 34, "Värde");
- attroff(A_BOLD);
- for (int i = 0; i <= nf; i++) {
- char lbl[128], line[512];
- if (i < nf) {
- snprintf(lbl, sizeof lbl, "%s", COMPANY_FIELDS[i].label);
- pad_field(lbl, sizeof lbl, 31);
- snprintf(line, sizeof line, "%s %s", lbl, vals[i]);
- } else {
- snprintf(lbl, sizeof lbl, "Styrelseledamöter");
- pad_field(lbl, sizeof lbl, 31);
- snprintf(line, sizeof line, "%s (lista)", lbl);
- }
- if (i == sel) {
- attron(A_REVERSE);
- mvaddnstr(5 + i, 2, line, COLS - 4);
- attroff(A_REVERSE);
- } else {
- if (!owner && i < nf)
- attron(A_DIM);
- mvaddnstr(5 + i, 2, line, COLS - 4);
- if (!owner && i < nf)
- attroff(A_DIM);
- }
- }
- hints(owner
- ? "upp/ned Enter = ändra F5 = uppdatera"
- " Esc/q = tillbaka ^C = avsluta"
- : "upp/ned F5 = uppdatera Esc/q = tillbaka"
- " ^C = avsluta (endast ägare kan ändra)");
- refresh();
- int ch = ui_getch();
- if (ch == KEY_UP && sel > 0)
- sel--;
- else if (ch == KEY_DOWN && sel < nf)
- sel++;
- else if (ch == KEY_F(5))
- break; /* reload */
- else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) {
- if (sel == nf) {
- if (!owner) {
- message("Företagsuppgifter",
- "Endast ägare kan ändra"
- " styrelseledamöterna.");
- continue;
- }
- board_screen(a);
- break; /* reload */
- }
- if (!owner) {
- message("Företagsuppgifter",
- "Endast ägare kan ändra företagsuppgifterna.");
- continue;
- }
- char label[160];
- snprintf(label, sizeof label, "%s: ",
- COMPANY_FIELDS[sel].label);
- char *val = prompt(label, vals[sel], 0);
- if (!val)
- continue;
- if (!company_field_valid(&COMPANY_FIELDS[sel], val)) {
- message("Ogiltigt värde", "%s",
- COMPANY_FIELDS[sel].label);
- continue;
- }
+ ff[nf].label = "Styrelseledamöter";
+ ff[nf].kind = TUI_F_ACTION;
+ int r = tui_form_run("Företagsuppgifter", ff, nf + 1, owner);
+ if (r >= 0 && r < nf) {
+ if (!company_field_valid(&COMPANY_FIELDS[r], vals[r])) {
+ tui_message("Ogiltigt värde", "%s", COMPANY_FIELDS[r].label);
+ } else {
yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
yyjson_mut_val *o = yyjson_mut_obj(d);
yyjson_mut_doc_set_root(d, o);
- if (COMPANY_FIELDS[sel].numeric)
- yyjson_mut_obj_add_int(d, o, COMPANY_FIELDS[sel].key,
- strtoll(val, NULL, 10));
+ if (COMPANY_FIELDS[r].numeric)
+ yyjson_mut_obj_add_int(d, o, COMPANY_FIELDS[r].key,
+ strtoll(vals[r], NULL, 10));
else
- yyjson_mut_obj_add_strcpy(d, o, COMPANY_FIELDS[sel].key,
- val);
+ yyjson_mut_obj_add_strcpy(d, o, COMPANY_FIELDS[r].key,
+ vals[r]);
char *args = yyjson_mut_write(d, 0, NULL);
yyjson_mut_doc_free(d);
- char *r = client_rpc(&a->conn, "org.update", a->session,
- a->org, args);
+ char *rr = client_rpc(&a->conn, "org.update", a->session,
+ a->org, args);
free(args);
- if (!r || !client_ok(r))
- show_error("Kunde inte spara", r);
- free(r);
- break; /* reload from the server */
- } else if (ch == 27 || ch == 'q') {
- back = 1;
- break;
+ if (!rr || !client_ok(rr))
+ show_error("Kunde inte spara", rr);
+ free(rr);
}
}
+ if (r == nf)
+ board_screen(a);
for (int i = 0; i < nf; i++)
free(vals[i]);
- free(vals);
- if (back || g_quit)
+ if (r == TUI_FORM_BACK)
return;
}
}
@@ -5675,87 +4728,54 @@ static void settings_screen(struct app *a)
free(resp);
return;
}
- char **vals = xcalloc(nset, sizeof(char *));
+ char *vals[8];
+ struct tui_form_field ff[8];
+ memset(ff, 0, sizeof ff);
for (int i = 0; i < nset; i++) {
- char path[64];
+ char path[64], *v;
+ vals[i] = xcalloc(512, 1);
snprintf(path, sizeof path, "result.%s", SETTING_KEYS[i]);
- char *v = jstr_dup(resp, path);
- vals[i] = v ? v : xstrdup("");
+ v = jstr_dup(resp, path);
+ snprintf(vals[i], 512, "%s", v ? v : "");
+ free(v);
+ ff[i].label = SETTING_LABELS[i];
+ ff[i].value = vals[i];
+ ff[i].cap = 512;
+ ff[i].kind = TUI_F_TEXT;
}
free(resp);
-
- int sel = 0;
- for (;;) {
- frame("Inställningar");
- attron(A_BOLD);
- mvaddstr(4, 2, "Inställning");
- mvaddstr(4, 29, "Värde");
- attroff(A_BOLD);
- for (int i = 0; i < nset; i++) {
- char lbl[128], line[512];
- snprintf(lbl, sizeof lbl, "%s", SETTING_LABELS[i]);
- pad_field(lbl, sizeof lbl, 26);
- snprintf(line, sizeof line, "%s %s", lbl, vals[i]);
- if (i == sel)
- attron(A_REVERSE);
- mvaddnstr(6 + i, 2, line, COLS - 4);
- if (i == sel)
- attroff(A_REVERSE);
- }
- hints("upp/ned Enter = ändra F5 = uppdatera Esc/q = tillbaka"
- " ^C = avsluta");
- refresh();
- int ch = ui_getch();
- if (ch == KEY_UP && sel > 0)
- sel--;
- else if (ch == KEY_DOWN && sel < nset - 1)
- sel++;
- else if (ch == KEY_F(5))
- break; /* reload */
- else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) {
- char label[128];
- snprintf(label, sizeof label, "%s: ", SETTING_LABELS[sel]);
- char *val = prompt(label, vals[sel], 0);
- if (val) {
- yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
- yyjson_mut_val *o = yyjson_mut_obj(d);
- yyjson_mut_doc_set_root(d, o);
- yyjson_mut_obj_add_strcpy(d, o, "key", SETTING_KEYS[sel]);
- yyjson_mut_obj_add_strcpy(d, o, "value", val);
- char *args = yyjson_mut_write(d, 0, NULL);
- yyjson_mut_doc_free(d);
- char *r = client_rpc(&a->conn, "settings.set", a->session,
- a->org, args);
- free(args);
- if (r && client_ok(r)) {
- char *nv = jstr_dup(r, "result.value");
- if (nv) {
- free(vals[sel]);
- vals[sel] = nv;
- if (strcmp(SETTING_KEYS[sel],
- "default_series") == 0)
- snprintf(a->default_series,
- sizeof a->default_series, "%s", nv);
- else if (strcmp(SETTING_KEYS[sel],
- "attachment_dir") == 0)
- snprintf(a->attachment_dir,
- sizeof a->attachment_dir, "%s", nv);
- }
- } else {
- show_error("Kunde inte spara inställningen", r);
- }
- free(r);
+ int r = tui_form_run("Inställningar", ff, nset, 1);
+ if (r >= 0) {
+ yyjson_mut_doc *d = yyjson_mut_doc_new(NULL);
+ yyjson_mut_val *o = yyjson_mut_obj(d);
+ yyjson_mut_doc_set_root(d, o);
+ yyjson_mut_obj_add_strcpy(d, o, "key", SETTING_KEYS[r]);
+ yyjson_mut_obj_add_strcpy(d, o, "value", vals[r]);
+ char *args = yyjson_mut_write(d, 0, NULL);
+ yyjson_mut_doc_free(d);
+ char *rr = client_rpc(&a->conn, "settings.set", a->session,
+ a->org, args);
+ free(args);
+ if (rr && client_ok(rr)) {
+ char *nv = jstr_dup(rr, "result.value");
+ if (nv) {
+ if (strcmp(SETTING_KEYS[r], "default_series") == 0)
+ snprintf(a->default_series, sizeof a->default_series,
+ "%s", nv);
+ else if (strcmp(SETTING_KEYS[r], "attachment_dir") == 0)
+ snprintf(a->attachment_dir,
+ sizeof a->attachment_dir, "%s", nv);
+ free(nv);
}
- } else if (ch == 27 || ch == 'q') {
- for (int i = 0; i < nset; i++)
- free(vals[i]);
- free(vals);
- return;
+ } else {
+ show_error("Kunde inte spara inställningen", rr);
}
+ free(rr);
}
for (int i = 0; i < nset; i++)
free(vals[i]);
- free(vals);
+ if (r == TUI_FORM_BACK)
+ return;
}
}
@@ -5787,7 +4807,7 @@ static void templates_screen(struct app *a)
char *ds = jstr_dup(resp, path);
char nmbuf[128];
snprintf(nmbuf, sizeof nmbuf, "%s", nm ? nm : "");
- pad_field(nmbuf, sizeof nmbuf, 24);
+ tui_pad_field(nmbuf, sizeof nmbuf, 24);
snprintf(line, sizeof line, "%s %-3s %2lld rader %s", nmbuf,
ser ? ser : "", (long long)rc, ds ? ds : "");
names[i] = xstrdup(nm ? nm : "");
@@ -5798,7 +4818,7 @@ static void templates_screen(struct app *a)
}
names[n] = xstrdup("");
lines[n] = xstrdup("+ Ny mall (^N)");
- int s = select_list("Mallar", lines, (int)n + 1, cursor, 1, &cursor, 1,
+ int s = tui_select_list("Mallar", lines, (int)n + 1, cursor, 1, &cursor, 1,
n > 0 ? "arkivera" : NULL, 0);
if (s == -1) {
for (size_t i = 0; i <= n; i++) {
@@ -5883,8 +4903,8 @@ static void fmt_bokslut_plan(struct buf *t, yyjson_val *res)
for (size_t k = 0; k < nr; k++) {
yyjson_val *r = yyjson_arr_get(rows, k);
char a1[48], a2[48];
- amt_col(a1, sizeof a1, 15, vint(r, "debit_ore"));
- amt_col(a2, sizeof a2, 15, vint(r, "credit_ore"));
+ tui_amt_col(a1, sizeof a1, 15, vint(r, "debit_ore"));
+ tui_amt_col(a2, sizeof a2, 15, vint(r, "credit_ore"));
buf_line(t, " %-5s %s %s\n", vstr(r, "account"), a1, a2);
}
}
@@ -5894,12 +4914,12 @@ static void bokslut_plan(struct app *a, const char *fond, const char *rate,
int do_post)
{
int64_t fond_ore = 0, rate_ore = 0;
- if (parse_kr(fond, &fond_ore) != 0) {
- message("Bokslut", "Ogiltigt fondbelopp: %s", fond);
+ if (tui_parse_kr(fond, &fond_ore) != 0) {
+ tui_message("Bokslut", "Ogiltigt fondbelopp: %s", fond);
return;
}
- if (parse_kr(rate, &rate_ore) != 0 || rate_ore < 0 || rate_ore > 10000) {
- message("Bokslut", "Ogiltig skattesats: %s", rate);
+ if (tui_parse_kr(rate, &rate_ore) != 0 || rate_ore < 0 || rate_ore > 10000) {
+ tui_message("Bokslut", "Ogiltig skattesats: %s", rate);
return;
}
char args[160];
@@ -5923,13 +4943,13 @@ static void bokslut_plan(struct app *a, const char *fond, const char *rate,
else
buf_line(&t, "%s", resp);
buf_append(&t, "", 1);
- text_view("Bokslut - plan (torrkörning)", (const char *)t.p, NULL);
+ tui_pager("Bokslut - plan (torrkörning)", (const char *)t.p, NULL);
buf_free(&t);
yyjson_doc_free(d);
free(resp);
if (!do_post)
return;
- char *ans = prompt("Bokför planen? (j/n): ", "n", 0);
+ char ansbuf[512]; char *ans = tui_prompt_into(ansbuf, sizeof ansbuf, "Bokför planen? (j/n): ", "n", 0) ? ansbuf : NULL;
if (!ans || !(ans[0] == 'j' || ans[0] == 'J'))
return;
char *presp = client_rpc(&a->conn, "bokslut.post", a->session, a->org,
@@ -5952,7 +4972,7 @@ static void bokslut_plan(struct app *a, const char *fond, const char *rate,
buf_line(&m, "\n %s%lld %s", vstr(pv, "series"),
(long long)vint(pv, "number"), vstr(v, "description"));
}
- message("Bokslut", "%s", (const char *)m.p);
+ tui_message("Bokslut", "%s", (const char *)m.p);
buf_free(&m);
yyjson_doc_free(pd);
free(presp);
@@ -5960,66 +4980,27 @@ static void bokslut_plan(struct app *a, const char *fond, const char *rate,
static void bokslut_screen(struct app *a)
{
- char fond[32] = "0,00";
- char rate[16] = "20,6";
- int sel = 0;
+ char fond[64] = "0,00", rate[16] = "20,6";
+ struct tui_form_field ff[2];
for (;;) {
if (g_quit)
return;
- frame("Bokslut");
- attron(A_BOLD);
- mvaddstr(4, 2, "Fält");
- mvaddstr(4, 34, "Värde");
- attroff(A_BOLD);
- static const char *const labels[2] = {
- "Periodiseringsfond (kr)", "Skattesats (%)"
- };
- const char *vals[2] = { fond, rate };
- for (int i = 0; i < 2; i++) {
- char lbl[64], line[128];
- snprintf(lbl, sizeof lbl, "%s", labels[i]);
- pad_field(lbl, sizeof lbl, 31);
- snprintf(line, sizeof line, "%s %s", lbl, vals[i]);
- if (i == sel)
- attron(A_REVERSE);
- mvaddnstr(6 + i, 2, line, COLS - 4);
- if (i == sel)
- attroff(A_REVERSE);
- }
- mvaddstr(9, 2, "Fondbelopp i kr, tomt = ingen avsättning.");
- mvaddstr(10, 2, "Skattesatsen tillämpas på resultat efter"
- " bokslutsdispositioner.");
- hints("upp/ned Enter = ändra F5 = visa plan ^Enter = bokför"
- " Esc/q = tillbaka ^C = avsluta");
- refresh();
- int ch = ui_getch();
- if (ch == KEY_UP && sel > 0)
- sel--;
- else if (ch == KEY_DOWN && sel < 1)
- sel++;
- else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) {
- char label[80];
- snprintf(label, sizeof label, "%s: ", labels[sel]);
- char *v = prompt(label, vals[sel], 0);
- if (!v)
- continue;
- int64_t ore = 0;
- if (parse_kr(v, &ore) != 0 ||
- (sel == 1 && (ore < 0 || ore > 10000))) {
- message("Bokslut", "Ogiltigt värde: %s", v);
- continue;
- }
- if (sel == 0)
- snprintf(fond, sizeof fond, "%s", v);
- else
- snprintf(rate, sizeof rate, "%s", v);
- } else if (ch == KEY_F(5)) {
+ memset(ff, 0, sizeof ff);
+ ff[0].label = "Periodiseringsfond (kr)";
+ ff[0].value = fond;
+ ff[0].cap = sizeof fond;
+ ff[0].kind = TUI_F_TEXT;
+ ff[1].label = "Skattesats (%)";
+ ff[1].value = rate;
+ ff[1].cap = sizeof rate;
+ ff[1].kind = TUI_F_TEXT;
+ int r = tui_form_run("Bokslut", ff, 2, 1);
+ if (r == TUI_FORM_REFRESH)
bokslut_plan(a, fond, rate, 0);
- } else if (ch == KEY_CTRL_ENTER || ch == KEY_F(9)) {
+ else if (r == TUI_FORM_SUBMIT)
bokslut_plan(a, fond, rate, 1);
- } else if (ch == 27 || ch == 'q') {
+ else if (r == TUI_FORM_BACK)
return;
- }
}
}
@@ -6028,24 +5009,6 @@ static void bokslut_screen(struct app *a)
/* ------------------------------------------------------------------ */
/* Whole kronor with space thousands; rounded half away from zero. */
-static void kr0_format(int64_t ore, char *buf, size_t n)
-{
- long long v = (long long)(ore >= 0 ? (ore + 50) / 100
- : (ore - 50) / 100);
- int neg = v < 0;
- if (neg)
- v = -v;
- char digits[32], out[40];
- snprintf(digits, sizeof digits, "%lld", v);
- size_t o = 0, len = strlen(digits);
- for (size_t i = 0; i < len && o + 2 < sizeof out; i++) {
- if (i > 0 && (len - i) % 3 == 0)
- out[o++] = ' ';
- out[o++] = digits[i];
- }
- out[o] = '\0';
- snprintf(buf, n, "%s%s", neg ? "-" : "", out);
-}
/* Thousands of kronor for the flerårsöversikt. */
static long long ar_tkr(int64_t ore)
@@ -6170,7 +5133,15 @@ static void ar_load_year(struct ar_year *y, yyjson_val *gl)
static void ar_amt_col(char *buf, size_t n, int width, int64_t ore)
{
char tmp[40];
- kr0_format(ore, tmp, sizeof tmp);
+ if (n == 0)
+ return;
+ tui_kr0_format(ore, tmp, sizeof tmp);
+ if ((size_t)width >= n)
+ width = (int)n - 1;
+ if (width < 1)
+ width = 1;
+ if (strlen(tmp) >= n)
+ tmp[n - 1] = '\0';
snprintf(buf, n, "%*s", width, tmp);
}
@@ -6179,7 +5150,7 @@ static void ar_row(struct buf *t, const char *label, int64_t cur, int64_t prev,
{
char nm[160], padded[200], a1[40], a2[40];
snprintf(nm, sizeof nm, "%s%s", indent ? " " : "", label);
- pad_label(padded, sizeof padded, nm, 48);
+ tui_pad_label(padded, sizeof padded, nm, 48);
ar_amt_col(a1, sizeof a1, 16, cur);
ar_amt_col(a2, sizeof a2, 16, prev);
buf_line(t, "%s %s %s\n", padded, a1, a2);
@@ -6191,7 +5162,7 @@ static void ar_eq_row(struct buf *t, const char *label, int64_t ak,
{
char nm[160], padded[200], c1[32], c2[32], c3[32], c4[32];
snprintf(nm, sizeof nm, "%s%s", indent ? " " : "", label);
- pad_label(padded, sizeof padded, nm, 34);
+ tui_pad_label(padded, sizeof padded, nm, 34);
ar_amt_col(c1, sizeof c1, 12, ak);
ar_amt_col(c2, sizeof c2, 14, br);
ar_amt_col(c3, sizeof c3, 14, ar);
@@ -6369,7 +5340,7 @@ static void fmt_arsredovisning(struct buf *t, const struct app *a,
" vinstutdelningen\n\n");
if (shares > 0) {
char per[48];
- kr_format((dividend + shares / 2) / shares, per, sizeof per);
+ tui_kr_format((dividend + shares / 2) / shares, per, sizeof per);
buf_line(t, "Utdelning per aktie: %s kr. Datum för utdelning är"
" %s.\n\n", per, pay_sv);
} else {
@@ -6534,7 +5505,7 @@ static void arsredovisning_save(struct app *a, const char *text)
struct stat sb;
if (stat(def, &sb) != 0 || !S_ISDIR(sb.st_mode))
snprintf(def, sizeof def, "%s", home && *home ? home : ".");
- char *dir = prompt("Spara årsredovisningen i: ", def, 0);
+ char dirbuf[512]; char *dir = tui_prompt_into(dirbuf, sizeof dirbuf, "Spara årsredovisningen i: ", def, 0) ? dirbuf : NULL;
if (!dir || !*dir)
return;
char full[640];
@@ -6557,12 +5528,12 @@ static void arsredovisning_save(struct app *a, const char *text)
if (!f || fwrite(text, 1, n, f) != n) {
if (f)
fclose(f);
- message("Årsredovisning", "Kunde inte skriva %s: %s", full,
+ tui_message("Årsredovisning", "Kunde inte skriva %s: %s", full,
strerror(errno));
return;
}
fclose(f);
- message("Årsredovisning", "Sparat: %s\n\nFilen är ett utkast i textform"
+ tui_message("Årsredovisning", "Sparat: %s\n\nFilen är ett utkast i textform"
" att redigera vidare (t.ex. i Word).", full);
}
@@ -6681,7 +5652,7 @@ static void arsredovisning_screen(struct app *a)
buf_line(&t, "Kunde inte läsa rapporterna.");
buf_append(&t, "", 1);
int again =
- text_view("Årsredovisning (K2)", (const char *)t.p, "s = spara");
+ tui_pager("Årsredovisning (K2)", (const char *)t.p, "s = spara");
if (again == 2)
arsredovisning_save(a, (const char *)t.p);
buf_free(&t);
@@ -6771,7 +5742,7 @@ static void dashboard(struct app *a)
for (;;) {
if (g_reload)
return;
- int sel = menu("Bokf", MAIN_ITEMS, 12, 1, &last_sel);
+ int sel = tui_menu("Bokf", MAIN_ITEMS, 12, 1, &last_sel);
snprintf(g_scene, sizeof g_scene, "%s", "dashboard");
if (g_quit || g_reload)
return;
@@ -6982,7 +5953,7 @@ static int login_screen(struct app *a)
int field = 3; /* 0 server, 1 user, 2 password, 3 = Logga in button */
for (;;) {
- frame("bokf — inloggning");
+ tui_frame("bokf — inloggning");
attron(A_BOLD);
mvaddstr(4, 4, "Server");
mvaddstr(6, 4, "Användare");
@@ -7001,7 +5972,7 @@ static int login_screen(struct app *a)
}
{
const char *btn = " Logga in ";
- int bw = disp_width(btn);
+ int bw = tui_disp_width(btn);
int bx = (COLS - bw) / 2;
if (bx < 2)
bx = 2;
@@ -7013,7 +5984,7 @@ static int login_screen(struct app *a)
if (field == 3)
attroff(A_REVERSE | A_BOLD);
}
- hints("Tab = byta fält/knapp Enter = logga in ^C = avsluta");
+ tui_hints("Tab = byta fält/knapp Enter = logga in ^C = avsluta");
refresh();
if (field == 3) {
@@ -7040,7 +6011,7 @@ static int login_screen(struct app *a)
/* fall through to the login attempt */
} else {
int y = 4 + field * 2;
- int rc = edit_field(y, 16, field == 0 ? socket_path
+ int rc = tui_edit_field(y, 16, field == 0 ? socket_path
: (field == 1 ? user : pass),
field == 0 ? sizeof socket_path
: (field == 1 ? sizeof user
@@ -7073,7 +6044,7 @@ static int login_screen(struct app *a)
char *err = NULL;
int rc = try_login(a, user, pass, NULL, &err);
if (rc == -2) {
- message("Fel", "Kunde inte ansluta till %s: %s", a->socket,
+ tui_message("Fel", "Kunde inte ansluta till %s: %s", a->socket,
err ? err : "okänt fel");
free(err);
continue;
@@ -7082,12 +6053,12 @@ static int login_screen(struct app *a)
if (err && err[0] == '{') {
char *code = jstr_dup(err, "error.code");
char *msg = jstr_dup(err, "error.message");
- message("Inloggning misslyckades", "%s: %s",
+ tui_message("Inloggning misslyckades", "%s: %s",
code ? code : "fel", msg ? msg : "okänt fel");
free(code);
free(msg);
} else {
- message("Inloggning misslyckades", "%s",
+ tui_message("Inloggning misslyckades", "%s",
err ? err : "transportfel");
}
free(err);
@@ -7109,7 +6080,7 @@ static int select_org(struct app *a)
}
size_t n = jarr_size(resp, "result.items");
if (n == 0) {
- message("Organisationer",
+ tui_message("Organisationer",
"Inga organisationer. Skapa en med bokfctl org.create.");
free(resp);
return -1;
@@ -7124,7 +6095,7 @@ static int select_org(struct app *a)
char *role = jstr_dup(resp, path);
char nbuf[128], line[256];
snprintf(nbuf, sizeof nbuf, "%s", name ? name : "");
- pad_field(nbuf, sizeof nbuf, 30);
+ tui_pad_field(nbuf, sizeof nbuf, 30);
snprintf(line, sizeof line, "%s (%s)", nbuf, role ? role : "");
items[i] = xstrdup(line);
snprintf(path, sizeof path, "result.items.%zu.id", i);
@@ -7132,7 +6103,7 @@ static int select_org(struct app *a)
free(name);
free(role);
}
- int sel = select_list("Välj organisation att representera", items, (int)n,
+ int sel = tui_select_list("Välj organisation att representera", items, (int)n,
0, 0, NULL, 0, NULL, 0);
for (size_t i = 0; i < n; i++)
free(items[i]);
@@ -7220,11 +6191,10 @@ int main(int argc, char **argv)
cbreak();
noecho();
keypad(stdscr, TRUE);
- /* ^Enter has no control code; enable xterm modifyOtherKeys and bind the
- sequence it produces. Terminals without it ignore the request. */
- define_key("\033[27;5;13~", KEY_CTRL_ENTER);
- putp("\033[>4;1m");
- fflush(stdout);
+ tui_keys_setup();
+ tui_set_input(ui_getch);
+ tui_set_screen(tui_frame, tui_hints);
+ tui_set_quit(request_quit);
atexit(reset_modify_keys);
curs_set(1);
@@ -7315,12 +6285,12 @@ int main(int argc, char **argv)
if (auto_err) {
char *code = jstr_dup(auto_err, "error.code");
char *msg = jstr_dup(auto_err, "error.message");
- message("Återanslutning misslyckades", "%s: %s",
+ tui_message("Återanslutning misslyckades", "%s: %s",
code ? code : "fel", msg ? msg : "okänt fel");
free(code);
free(msg);
} else if (stale_session) {
- message("Session", "Sessionen har gått ut, logga in igen.");
+ tui_message("Session", "Sessionen har gått ut, logga in igen.");
}
}
free(auto_err);