From e0c59f7f4ddb73390dc9c16569254c0e2a921931 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sun, 22 Jan 2023 17:01:16 +0200 Subject: [PATCH] list widget selection --- src/ftp.c | 31 +++++++---- src/ftp.h | 2 +- src/input.h | 13 +++++ src/main.c | 47 +++++++++++++---- src/tui.c | 6 +++ src/tui.h | 11 ++++ src/tui_list.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/tuipriv.h | 3 ++ src/unix/input.c | 37 ++++++++++++- src/unix/tgfx.c | 51 +++++++++++++++--- 10 files changed, 320 insertions(+), 36 deletions(-) diff --git a/src/ftp.c b/src/ftp.c index ee39c42..806c766 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -20,7 +20,7 @@ #define fcntlsocket fcntl #endif -#define TIMEOUT 5 +#define TIMEOUT 15 static int newconn(struct ftp *ftp); static int sendcmd(struct ftp *ftp, const char *fmt, ...); @@ -187,7 +187,7 @@ int ftp_queue(struct ftp *ftp, int op, const char *arg) { struct ftp_op *fop; - if(!ftp->qhead) { + if(!ftp->busy && !ftp->qhead) { exec_op(ftp, op, arg); return 0; } @@ -195,15 +195,23 @@ int ftp_queue(struct ftp *ftp, int op, const char *arg) if(!(fop = malloc(sizeof *fop))) { return -1; } - if(!(fop->arg = strdup(arg))) { - free(fop); - return -1; + if(arg) { + if(!(fop->arg = strdup(arg))) { + free(fop); + return -1; + } + } else { + fop->arg = 0; } fop->op = op; fop->next = 0; - ftp->qtail->next = fop; - ftp->qtail = fop; + if(ftp->qhead) { + ftp->qtail->next = fop; + ftp->qtail = fop; + } else { + ftp->qhead = ftp->qtail = fop; + } return 0; } @@ -330,6 +338,8 @@ static int sendcmd(struct ftp *ftp, const char *fmt, ...) return -1; } + ftp->busy = 1; + va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); @@ -443,6 +453,7 @@ static void proc_control(struct ftp *ftp, const char *buf) if(ftp->cproc) { if(ftp->cproc(ftp, code, buf, ftp->cproc_cls) <= 0) { ftp->cproc = 0; + ftp->busy = 0; /* execute next operation if there's one queued */ exec_queued(ftp); @@ -468,6 +479,7 @@ static void proc_control(struct ftp *ftp, const char *buf) errmsg("login failed\n"); break; } + ftp->busy = 0; } static int newconn(struct ftp *ftp) @@ -676,7 +688,6 @@ static int parse_dirent(struct ftp_dirent *ent, const char *line) memcpy(ent->name, ptr, len); ent->name[len] = 0; - infomsg("name: %s\n", ent->name); return 0; } @@ -705,8 +716,8 @@ static void dproc_list(struct ftp *ftp, const char *buf, int sz, void *cls) tail = ent; } } - while(*ptr && *ptr != '\n' && *ptr != '\r') ptr++; - while(*ptr && (*ptr == '\r' || *ptr == '\n')) ptr++; + while(ptr < end && *ptr != '\n' && *ptr != '\r') ptr++; + while(ptr < end && (*ptr == '\r' || *ptr == '\n')) ptr++; } ftp->modified |= FTP_MOD_REMDIR; diff --git a/src/ftp.h b/src/ftp.h index 34013c6..13cc415 100644 --- a/src/ftp.h +++ b/src/ftp.h @@ -44,7 +44,7 @@ struct ftp { int ctl, lis, data; /* sockets */ int lis_port; - int status; + int status, busy; char *user, *pass; struct ftp_op *qhead, *qtail; diff --git a/src/input.h b/src/input.h index 313d4f2..ac1a546 100644 --- a/src/input.h +++ b/src/input.h @@ -2,6 +2,19 @@ #define INPUT_H_ enum { + KB_DEL = 128, + KB_INS, + KB_UP, + KB_DOWN, + KB_LEFT, + KB_RIGHT, + KB_HOME, + KB_END, + KB_PGUP, + KB_PGDN +}; + +enum { EV_KEY, EV_MMOVE, EV_MBUTTON diff --git a/src/main.c b/src/main.c index 4db0dc3..d678219 100644 --- a/src/main.c +++ b/src/main.c @@ -48,10 +48,6 @@ int main(int argc, char **argv) tg_clear(); uilist = tui_list("Remote", 0, 0, 40, 23, 0, 0); - tui_add_list_item(uilist, "first item"); - tui_add_list_item(uilist, "second item"); - tui_add_list_item(uilist, "another item"); - tui_add_list_item(uilist, "foo"); tg_setcursor(0, 23); @@ -93,10 +89,7 @@ int main(int argc, char **argv) } } - if(ftp->modified) { - updateui(); - ftp->modified = 0; - } + updateui(); } tg_cleanup(); @@ -106,10 +99,19 @@ int main(int argc, char **argv) return 0; } +static int cmpnames(const void *a, const void *b) +{ + const char *sa = *(const char**)a; + const char *sb = *(const char**)b; + infomsg("cmp(%s, %s)\n", sa, sb); + return strcmp(sa, sb); +} + void updateui(void) { struct ftp_dirent *ent; unsigned int upd = 0; + char buf[128]; if(ftp->curdir_rem && strcmp(tui_get_title(uilist), ftp->curdir_rem) != 0) { tui_set_title(uilist, ftp->curdir_rem); @@ -119,15 +121,25 @@ void updateui(void) if(ftp->modified & FTP_MOD_REMDIR) { tui_clear_list(uilist); - ent = ftp->curdir_rem; + ent = ftp->dent_rem; while(ent) { - tui_add_list_item(uilist, ent->name); + if(ent->type == FTP_DIR) { + sprintf(buf, "%s/", ent->name); + tui_add_list_item(uilist, buf); + } else { + tui_add_list_item(uilist, ent->name); + } ent = ent->next; } + + //tui_sort_list(uilist, cmpnames); + tui_list_select(uilist, 0); + + ftp->modified &= ~FTP_MOD_REMDIR; upd |= 1; } - if(upd & 1) { + if(tui_isdirty(uilist) || upd & 1) { tui_draw(uilist); } } @@ -158,6 +170,19 @@ int keypress(int key) case 'q': return -1; + case KB_UP: + tui_list_sel_prev(uilist); + break; + case KB_DOWN: + tui_list_sel_next(uilist); + break; + case KB_LEFT: + tui_list_sel_start(uilist); + break; + case KB_RIGHT: + tui_list_sel_end(uilist); + break; + default: break; } diff --git a/src/tui.c b/src/tui.c index 2a4d431..1cdad05 100644 --- a/src/tui.c +++ b/src/tui.c @@ -79,6 +79,11 @@ struct tui_widget *tui_parent(struct tui_widget *w) return w->par; } +int tui_isdirty(struct tui_widget *w) +{ + return w->dirty; +} + void tui_draw(struct tui_widget *w) { struct tui_widget *iter; @@ -86,6 +91,7 @@ void tui_draw(struct tui_widget *w) if(w->cbfunc[TUI_DRAW]) { w->cbfunc[TUI_DRAW](w, 0); } + w->dirty = 0; iter = w->child; while(iter) { diff --git a/src/tui.h b/src/tui.h index 78ec8bd..2a3bfa7 100644 --- a/src/tui.h +++ b/src/tui.h @@ -27,6 +27,7 @@ void tui_add_widget(struct tui_widget *par, struct tui_widget *w); void tui_remove_widget(struct tui_widget *par, struct tui_widget *w); struct tui_widget *tui_parent(struct tui_widget *w); +int tui_isdirty(struct tui_widget *w); void tui_draw(struct tui_widget *w); void tui_set_callback(struct tui_widget *w, int type, tui_callback func, void *cls); @@ -40,6 +41,16 @@ struct tui_widget *tui_list(const char *title, int x, int y, int w, int h, tui_c void tui_clear_list(struct tui_widget *w); void tui_add_list_item(struct tui_widget *w, const char *text); +int tui_num_list_items(struct tui_widget *w); + +int tui_list_select(struct tui_widget *w, int idx); +int tui_get_list_sel(struct tui_widget *w); +int tui_list_sel_next(struct tui_widget *w); +int tui_list_sel_prev(struct tui_widget *w); +int tui_list_sel_start(struct tui_widget *w); +int tui_list_sel_end(struct tui_widget *w); + +void tui_sort_list(struct tui_widget *w, int (*cmpfunc)(const void*, const void*)); void tui_wtoscr(struct tui_widget *w, int x, int y, int *retx, int *rety); void tui_scrtow(struct tui_widget *w, int x, int y, int *retx, int *rety); diff --git a/src/tui_list.c b/src/tui_list.c index 4111d84..728522e 100644 --- a/src/tui_list.c +++ b/src/tui_list.c @@ -5,6 +5,7 @@ #include "tui.h" #include "tuipriv.h" #include "tgfx.h" +#include "util.h" static void free_list(struct tui_widget *w, void *cls); static void draw_list(struct tui_widget *w, void *cls); @@ -24,6 +25,8 @@ struct tui_widget *tui_list(const char *title, int x, int y, int width, int heig w->height = height; w->entries = darr_alloc(0, sizeof *w->entries); + w->sel = -1; + w->dirty = 1; if(cbfunc) { tui_set_callback((struct tui_widget*)w, TUI_ONMODIFY, cbfunc, cbdata); @@ -35,32 +38,164 @@ struct tui_widget *tui_list(const char *title, int x, int y, int width, int heig static void free_list(struct tui_widget *w, void *cls) { - darr_free(((struct tui_list*)w)->entries); + struct tui_list *wl = (struct tui_list*)w; + + tui_clear_list(w); + darr_free(wl->entries); } void tui_clear_list(struct tui_widget *w) { + int i; struct tui_list *wl = (struct tui_list*)w; assert(wl->type == TUI_LIST); + + for(i=0; ientries); i++) { + free(wl->entries[i]); + } darr_clear(wl->entries); + wl->dirty = 1; } void tui_add_list_item(struct tui_widget *w, const char *text) { + char *str; + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + str = strdup_nf(text); + darr_push(wl->entries, &str); + wl->dirty = 1; +} + +int tui_num_list_items(struct tui_widget *w) +{ + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + return darr_size(wl->entries); +} + +int tui_list_select(struct tui_widget *w, int idx) +{ + int offs, nelem; + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + + if(idx == wl->sel) { + return 0; /* no change */ + } + + if(idx < 0) { + wl->sel = -1; + return 0; + } + if(idx >= (nelem = darr_size(wl->entries))) { + return -1; + } + wl->sel = idx; + + if(idx < wl->view_offs || idx >= wl->view_offs + wl->height) { + offs = idx - wl->height / 2; + if(offs + wl->height >= nelem) { + offs = nelem - wl->height; + } + if(offs < 0) { + offs = 0; + } + wl->view_offs = offs; + } + + wl->dirty = 1; + return 0; +} + +int tui_get_list_sel(struct tui_widget *w) +{ + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + return wl->sel; +} + +int tui_list_sel_next(struct tui_widget *w) +{ + int nelem; + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + + nelem = darr_size(wl->entries); + + if(wl->sel + 1 >= nelem) { + return -1; + } + + if(++wl->sel - wl->view_offs >= wl->height) { + wl->view_offs = wl->sel - wl->height; + } + wl->dirty = 1; + return 0; +} + +int tui_list_sel_prev(struct tui_widget *w) +{ + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + + if(wl->sel <= 0) { + return -1; + } + if(--wl->sel < wl->view_offs) { + wl->view_offs = wl->sel; + } + wl->dirty = 1; + return 0; +} + +int tui_list_sel_start(struct tui_widget *w) +{ struct tui_list *wl = (struct tui_list*)w; assert(wl->type == TUI_LIST); - darr_push(wl->entries, &text); + + wl->sel = 0; + wl->view_offs = 0; + wl->dirty = 1; + return 0; +} + +int tui_list_sel_end(struct tui_widget *w) +{ + int nelem; + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + + nelem = darr_size(wl->entries); + + wl->sel = nelem - 1; + wl->view_offs = nelem - wl->height; + if(wl->view_offs < 0) wl->view_offs = 0; + wl->dirty = 1; + return 0; +} + +void tui_sort_list(struct tui_widget *w, int (*cmpfunc)(const void*, const void*)) +{ + struct tui_list *wl = (struct tui_list*)w; + assert(wl->type == TUI_LIST); + + if(!cmpfunc) { + cmpfunc = (int (*)(const void*, const void*))strcmp; + } + qsort(wl->entries, darr_size(wl->entries), sizeof *wl->entries, cmpfunc); } static void draw_list(struct tui_widget *w, void *cls) { - int i, x, y, num; + int i, x, y, num, idx; struct tui_list *wl = (struct tui_list*)w; tui_wtoscr(w, 0, 0, &x, &y); tg_bgcolor(TGFX_BLUE); + tg_fgcolor(TGFX_CYAN); tg_rect(wl->title, x, y, wl->width, wl->height, TGFX_FRAME); num = darr_size(wl->entries); @@ -70,6 +205,18 @@ static void draw_list(struct tui_widget *w, void *cls) x++; for(i=0; ientries[i]); + idx = i + wl->view_offs; + if(idx == wl->sel) { + tg_bgcolor(TGFX_CYAN); + tg_fgcolor(TGFX_BLUE); + + tg_rect(0, x, ++y, wl->width-2, 1, 0); + tg_text(x, y, "%s", wl->entries[idx]); + + tg_bgcolor(TGFX_BLUE); + tg_fgcolor(TGFX_CYAN); + } else { + tg_text(x, ++y, "%s", wl->entries[idx]); + } } } diff --git a/src/tuipriv.h b/src/tuipriv.h index 27a18b5..45632a8 100644 --- a/src/tuipriv.h +++ b/src/tuipriv.h @@ -12,6 +12,7 @@ enum { int type; \ char *title; \ int x, y, width, height; \ + int dirty; \ tui_callback cbfunc[TUI_NUM_CALLBACKS]; \ void *cbcls[TUI_NUM_CALLBACKS]; \ struct tui_widget *par, *child, *next @@ -28,6 +29,8 @@ struct tui_list_entry { struct tui_list { WCOMMON; char **entries; /* darr */ + int sel; + int view_offs; }; diff --git a/src/unix/input.c b/src/unix/input.c index 6cb91d8..92c1dcc 100644 --- a/src/unix/input.c +++ b/src/unix/input.c @@ -5,7 +5,6 @@ int init_input(void) { - nodelay(stdscr, TRUE); return 0; } @@ -13,10 +12,44 @@ void cleanup_input(void) { } +static int convkey(int key) +{ + switch(key) { + case KEY_DC: + return KB_DEL; + case KEY_IC: + return KB_INS; + case KEY_UP: + return KB_UP; + case KEY_DOWN: + return KB_DOWN; + case KEY_LEFT: + return KB_LEFT; + case KEY_RIGHT: + return KB_RIGHT; + case KEY_HOME: + return KB_HOME; + case KEY_END: + return KB_END; + case KEY_PPAGE: + return KB_PGUP; + case KEY_NPAGE: + return KB_PGDN; + default: + break; + } + if(key < 128) { + return key; + } + return -1; +} + int poll_input(union event *ev) { ev->type = EV_KEY; - ev->key.key = getch(); + if((ev->key.key = convkey(getch())) == -1) { + return 0; + } return 1; } diff --git a/src/unix/tgfx.c b/src/unix/tgfx.c index c807aed..d228227 100644 --- a/src/unix/tgfx.c +++ b/src/unix/tgfx.c @@ -1,17 +1,28 @@ #include #include "tgfx.h" +#include "util.h" + +struct cpair { + int pair; + int fg, bg; +}; +static struct cpair *colors; +static int num_colors; static int fgcol, bgcol; static int bgchar; +static int cur_pair; static int cur_x, cur_y; static int curses_color(int col); +static void upd_color(void); void tg_init(void) { initscr(); cbreak(); keypad(stdscr, TRUE); + nodelay(stdscr, TRUE); noecho(); start_color(); @@ -19,7 +30,8 @@ void tg_init(void) bgcol = curses_color(TGFX_BLACK); bgchar = ' '; - tg_color(fgcol | (bgcol << 4)); + colors = malloc_nf(COLORS * sizeof *colors); + num_colors = 0; } void tg_cleanup(void) @@ -42,20 +54,17 @@ void tg_clear(void) void tg_fgcolor(int col) { fgcol = curses_color(col); - init_pair(1, fgcol, bgcol); } void tg_bgcolor(int col) { bgcol = curses_color(col); - init_pair(1, fgcol, bgcol); } void tg_color(int col) { fgcol = curses_color(col & 0xf); bgcol = curses_color((col >> 4) & 0xf); - init_pair(1, fgcol, bgcol); } void tg_bgchar(int c) @@ -83,10 +92,11 @@ void tg_text(int x, int y, const char *fmt, ...) void tg_vtext(int x, int y, const char *fmt, va_list ap) { - attron(COLOR_PAIR(1)); + upd_color(); + attron(COLOR_PAIR(cur_pair)); move(y, x); vw_printw(stdscr, fmt, ap); - attroff(COLOR_PAIR(1)); + attroff(COLOR_PAIR(cur_pair)); } @@ -94,7 +104,8 @@ void tg_rect(const char *label, int x, int y, int xsz, int ysz, unsigned int fla { int i; - attron(COLOR_PAIR(1)); + upd_color(); + attron(COLOR_PAIR(cur_pair)); for(i=0; i= COLORS) { + return; + } + i = num_colors++; + cur_pair = num_colors; + + colors[i].fg = fgcol; + colors[i].bg = bgcol; + colors[i].pair = cur_pair; + init_pair(cur_pair, fgcol, bgcol); +} -- 1.7.10.4