diff options
Diffstat (limited to 'clients/tui.c')
| -rw-r--r-- | clients/tui.c | 192 |
1 files changed, 142 insertions, 50 deletions
diff --git a/clients/tui.c b/clients/tui.c index aa859e2..4bc1d66 100644 --- a/clients/tui.c +++ b/clients/tui.c @@ -991,6 +991,69 @@ static int nav_count(const struct tui_list_nav *v) return c; } +/* Lower-cases ASCII and the two-byte Latin-1 capitals (U+00C0-U+00DE except + U+00D7) into dst. */ +static void fold_copy(char *dst, size_t n, const char *src) +{ + size_t o = 0; + for (const unsigned char *p = (const unsigned char *)src; + *p && o + 1 < n; p++) { + unsigned char c = *p; + if (c >= 'A' && c <= 'Z') { + c = (unsigned char)(c + 32); + } else if (c == 0xC3 && p[1] >= 0x80 && p[1] <= 0x9E && + p[1] != 0x97 && o + 2 < n) { + dst[o++] = (char)c; + c = (unsigned char)(*++p + 0x20); + } + dst[o++] = (char)c; + } + if (n) + dst[o] = '\0'; +} + +int tui_text_match(const char *hay, const char *needle) +{ + if (!needle || !*needle) + return 1; + if (!hay) + return 0; + char h[1024], nd[128]; + fold_copy(h, sizeof h, hay); + fold_copy(nd, sizeof nd, needle); + return strstr(h, nd) != NULL; +} + +/* Next selectable row matching the search, starting at `from` and moving in + dir (wrapping); `from` itself counts. -1 when nothing matches. */ +static int nav_find(const struct tui_list_nav *v, int from, int dir) +{ + if (!v->items || v->n <= 0) + return -1; + for (int k = 0; k < v->n; k++) { + int i = ((from + k * dir) % v->n + v->n) % v->n; + if (nav_selectable(v, i) && tui_text_match(v->items[i], v->gotobuf)) + return i; + } + return -1; +} + +static void nav_search_to(struct tui_list_nav *v, int from, int dir) +{ + int r = nav_find(v, from, dir); + v->goto_miss = r < 0; + if (r >= 0) + v->sel = r; +} + +static void nav_goto_close(struct tui_list_nav *v) +{ + v->goto_active = 0; + v->goto_search = 0; + v->goto_miss = 0; + v->gotobuf[0] = '\0'; +} + /* Keys return the same codes the screens have always used: the selected index, or -1 back, -2 refresh, -4 new, -6 remove, -7 toggle. */ int tui_nav_key(struct tui_list_nav *v, int ch, int allow_new, @@ -1009,26 +1072,51 @@ int tui_nav_key(struct tui_list_nav *v, int ch, int allow_new, return TUI_NAV_NONE; } if (v->goto_active) { - if (ch >= '0' && ch <= '9' && strlen(v->gotobuf) < 9) { - size_t gl = strlen(v->gotobuf); - v->gotobuf[gl] = (char)ch; - v->gotobuf[gl + 1] = '\0'; - int r = nav_row_of(v, atoi(v->gotobuf) - 1); - if (r >= 0) - v->sel = r; + /* the first key decides: digits jump to a row number, anything + else printable searches the row texts */ + size_t gl = strlen(v->gotobuf); + int printable = ch >= 32 && ch < 256 && ch != 127; + if (!v->goto_search && ch >= '0' && ch <= '9') { + if (gl < 9) { + v->gotobuf[gl] = (char)ch; + v->gotobuf[gl + 1] = '\0'; + int r = nav_row_of(v, atoi(v->gotobuf) - 1); + if (r >= 0) + v->sel = r; + } + } else if (printable && v->items && + (v->goto_search || (gl == 0 && ch != ' '))) { + if (gl + 1 < sizeof v->gotobuf) { + v->gotobuf[gl] = (char)ch; + v->gotobuf[gl + 1] = '\0'; + } + v->goto_search = 1; + /* search once the UTF-8 character is complete */ + if ((ch & 0xC0) != 0xC0) + nav_search_to(v, 0, 1); + } else if (v->goto_search && (ch == KEY_DOWN || ch == KEY_UP)) { + int dir = ch == KEY_DOWN ? 1 : -1; + nav_search_to(v, v->sel + dir, dir); } else if (ch == KEY_BACKSPACE || ch == 127 || ch == 8) { - size_t gl = strlen(v->gotobuf); + while (gl > 0 && + ((unsigned char)v->gotobuf[gl - 1] & 0xC0) == 0x80) + gl--; if (gl) - v->gotobuf[gl - 1] = '\0'; - if (v->gotobuf[0]) { + gl--; + v->gotobuf[gl] = '\0'; + if (!gl) { + v->goto_search = 0; + v->goto_miss = 0; + } else if (v->goto_search) { + nav_search_to(v, 0, 1); + } else { int r = nav_row_of(v, atoi(v->gotobuf) - 1); if (r >= 0) v->sel = r; } } else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER || ch == 27 || - ch == 'g' || ch == 'G') { - v->goto_active = 0; - v->gotobuf[0] = '\0'; + (!v->goto_search && (ch == 'g' || ch == 'G'))) { + nav_goto_close(v); } return -3; } @@ -1060,8 +1148,8 @@ int tui_nav_key(struct tui_list_nav *v, int ch, int allow_new, return v->sel; /* menus activate directly */ } } else if (ch == 'g' || ch == 'G') { + nav_goto_close(v); v->goto_active = 1; - v->gotobuf[0] = '\0'; } else if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) return v->sel; else if (ch == KEY_F(5)) @@ -1087,6 +1175,33 @@ int tui_nav_key(struct tui_list_nav *v, int ch, int allow_new, /* list and menu rendering */ /* ------------------------------------------------------------------ */ +/* The goto/search prompt on the line above the hints (blank when closed). */ +static void nav_goto_draw(const struct tui_list_nav *v) +{ + move(LINES - 2, 2); + clrtoeol(); + if (!v->goto_active) + return; + char label[64]; + size_t ll = strlen(v->gotolabel); + if (v->goto_search) + snprintf(label, sizeof label, "Sök: "); + else if (!v->gotobuf[0] && v->items && ll > 2) + snprintf(label, sizeof label, "%.*s eller sök (bokstav): ", + (int)(ll - 2), v->gotolabel); + else + snprintf(label, sizeof label, "%s", v->gotolabel); + tui_style(TUI_ACCENT); + printw("%s%s", label, v->gotobuf); + tui_style_reset(); + tui_style(TUI_DIM); + if (v->goto_miss) + addstr(" (ingen träff)"); + else if (v->goto_search) + addstr(" upp/ned = fler träffar Enter/Esc = stäng"); + tui_style_reset(); +} + static void list_draw(const char *title, char **items, int n, const struct tui_list_nav *v, const char *hint) { @@ -1106,16 +1221,7 @@ static void list_draw(const char *title, char **items, int n, if (idx == v->sel) attroff(A_REVERSE); } - if (v->goto_active) { - move(LINES - 2, 2); - tui_style(TUI_ACCENT); - printw("%s%s", v->gotolabel, v->gotobuf); - tui_style_reset(); - clrtoeol(); - } else { - move(LINES - 2, 2); - clrtoeol(); - } + nav_goto_draw(v); if (g_hints) g_hints(hint); refresh(); @@ -1155,6 +1261,7 @@ int tui_select_list_hook(const char *title, char **items, int n, int start, v.sel = n - 1; v.view = LINES - 4; v.numw = tui_num_width(n); + v.items = (const char *const *)items; snprintf(v.gotolabel, sizeof v.gotolabel, "Gå till rad: "); for (;;) { if (cursor) @@ -1164,7 +1271,7 @@ int tui_select_list_hook(const char *title, char **items, int n, int start, 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," + "upp/ned, 1-9 = hoppa, g = gå till/sök, PgUp/PgDn, Home/End," " Enter%s%s%s%s Esc/q = tillbaka ^C = avsluta", allow_refresh ? " F5 = uppdatera" : "", allow_new ? " ^N = ny" : "", dbuf, @@ -1175,9 +1282,10 @@ int tui_select_list_hook(const char *title, char **items, int n, int start, } list_draw(title, items, n, &v, hint); int ch = in_key(); + int in_goto = v.goto_active; int r = tui_nav_key(&v, ch, allow_new, archive_action != NULL, allow_toggle, 0); - if (r == TUI_NAV_NONE && key) { + if (r == TUI_NAV_NONE && key && !in_goto && !v.goto_active) { int h = key(ud, ch); if (h == TUI_HOOK_BACK) return -1; @@ -1219,6 +1327,7 @@ int tui_menu(const char *title, const char *const *items, int n, if (v.numw < 2) v.numw = 2; v.view = (LINES - 4) / 2; + v.items = items; snprintf(v.gotolabel, sizeof v.gotolabel, "Gå till nummer: "); for (;;) { if (cursor) @@ -1248,22 +1357,13 @@ int tui_menu(const char *title, const char *const *items, int n, if (idx == v.sel) attroff(A_REVERSE); } - if (v.goto_active) { - move(LINES - 2, 2); - tui_style(TUI_ACCENT); - printw("%s%s", v.gotolabel, v.gotobuf); - tui_style_reset(); - clrtoeol(); - } else { - move(LINES - 2, 2); - clrtoeol(); - } + nav_goto_draw(&v); if (g_hints) g_hints(allow_new - ? "upp/ned, 1-9 = snabbval, g = gå till, PgUp/PgDn," + ? "upp/ned, 1-9 = snabbval, g = gå till/sök, PgUp/PgDn," " Home/End, Enter ^N = ny Esc/q = tillbaka" " ^C = avsluta" - : "upp/ned, 1-9 = snabbval, g = gå till, PgUp/PgDn," + : "upp/ned, 1-9 = snabbval, g = gå till/sök, PgUp/PgDn," " Home/End, Enter Esc/q = tillbaka ^C = avsluta"); refresh(); int r = tui_nav_key(&v, in_key(), allow_new, 0, 0, 1); @@ -1723,6 +1823,7 @@ int tui_action_menu(const char *title, const struct tui_action *acts, int n, if (v.numw < 2) v.numw = 2; v.view = LINES - 4; + v.items = (const char *const *)items; snprintf(v.gotolabel, sizeof v.gotolabel, "Gå till nummer: "); for (;;) { int num = 0; @@ -1762,22 +1863,13 @@ int tui_action_menu(const char *title, const struct tui_action *acts, int n, if (acts[idx].enabled != 1) attroff(tui_style_attrs(TUI_DIM, g_colours, 0)); } - if (v.goto_active) { - move(LINES - 2, 2); - tui_style(TUI_ACCENT); - printw("%s%s", v.gotolabel, v.gotobuf); - tui_style_reset(); - clrtoeol(); - } else { - move(LINES - 2, 2); - clrtoeol(); - } + nav_goto_draw(&v); if (g_hints) - g_hints("upp/ned, 1-9 = hoppa, g = gå till, PgUp/PgDn, Home/End," + g_hints("upp/ned, 1-9 = hoppa, g = gå till/sök, PgUp/PgDn, Home/End," " Enter = utför Esc/q = avbryt"); refresh(); int ch = in_key(); - if (ch == '\n' || ch == '\r' || ch == KEY_ENTER) { + if (!v.goto_active && (ch == '\n' || ch == '\r' || ch == KEY_ENTER)) { if (!ok[v.sel]) continue; if (acts[v.sel].enabled != 1) { |
