list widget selection
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 22 Jan 2023 15:01:16 +0000 (17:01 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sun, 22 Jan 2023 15:01:16 +0000 (17:01 +0200)
src/ftp.c
src/ftp.h
src/input.h
src/main.c
src/tui.c
src/tui.h
src/tui_list.c
src/tuipriv.h
src/unix/input.c
src/unix/tgfx.c

index ee39c42..806c766 100644 (file)
--- 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;
 
index 34013c6..13cc415 100644 (file)
--- 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;
index 313d4f2..ac1a546 100644 (file)
@@ -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
index 4db0dc3..d678219 100644 (file)
@@ -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;
        }
index 2a4d431..1cdad05 100644 (file)
--- 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) {
index 78ec8bd..2a3bfa7 100644 (file)
--- 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);
index 4111d84..728522e 100644 (file)
@@ -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; i<darr_size(wl->entries); 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; i<num; i++) {
-               tg_text(x, ++y, "%s", wl->entries[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]);
+               }
        }
 }
index 27a18b5..45632a8 100644 (file)
@@ -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;
 };
 
 
index 6cb91d8..92c1dcc 100644 (file)
@@ -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;
 }
 
index c807aed..d228227 100644 (file)
@@ -1,17 +1,28 @@
 #include <curses.h>
 #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<ysz; i++) {
                move(y + i, x);
@@ -121,7 +132,7 @@ void tg_rect(const char *label, int x, int y, int xsz, int ysz, unsigned int fla
                tg_text(x + 2, y, "%s", label);
        }
 
-       attroff(COLOR_PAIR(1));
+       attroff(COLOR_PAIR(cur_pair));
 }
 
 static int curses_color(int col)
@@ -140,3 +151,27 @@ static int curses_color(int col)
        }
        return col;
 }
+
+static void upd_color(void)
+{
+       int i;
+
+       for(i=0; i<num_colors; i++) {
+               if(fgcol == colors[i].fg && bgcol == colors[i].bg) {
+                       cur_pair = colors[i].pair;
+                       return;
+               }
+       }
+
+       /* not found, allocate a new color pair */
+       if(num_colors >= 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);
+}