commit 6ae6525074eb4c0a703c6e560bf013e9cf429773
parent f6511f4cd7bab5cda46c1bf4808b7d0265d056d2
Author: rpa <rpa@laika>
Date: Sun, 11 Dec 2022 23:13:13 +0000
tabul: snarf/paste
Diffstat:
M | src/tabul/tabul.c | | | 297 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
1 file changed, 211 insertions(+), 86 deletions(-)
diff --git a/src/tabul/tabul.c b/src/tabul/tabul.c
@@ -1,5 +1,12 @@
/*
- Primitive editor for tsv files
+ Primitive TSV spreadsheet editor
+
+ TODO:
+ - clear trailing whitespace and newlines when writing tables
+ - switch to another internal data format:
+ instead of rectangle array, store lines (and line lengths) separately
+ - get some kind of refcount system for cell data instead of strdup`ing
+ - look at optimizing drawing routines
*/
#include <u.h>
@@ -16,6 +23,18 @@ struct Table{
Rectangle r;
char **s;
};
+Table *view;
+
+Table * alloctable(Rectangle);
+void freetable(Table *);
+Table * readtable(int);
+int writetable(Table *, int);
+void resizetable(Table *, Rectangle);
+char ** tfetch(Table *, Point);
+void tstore(Table *, Point, char *);
+void cleartable(Table *, Rectangle);
+Table * duptable(Table *, Rectangle);
+void pastetable(Table *, Table *, Point);
Image *bord, *text, *bg, *curbg;
Rectangle cell, blank;
@@ -24,7 +43,8 @@ Keyboardctl *kctl;
Mouse mv;
int rv[2];
Rune kv;
-char *file;
+char file[256];
+char *snarf = "/tmp/tabsnarf";
struct {
Rectangle r;
@@ -46,60 +66,63 @@ struct {
Point cur;
} cells;
-Table view;
-
-void resizeview(Rectangle r);
-void clearview(void);
-void setview(Point xy, char *s);
-char ** getview(Point xy);
-void _cell(Image *screen, Rectangle r, Image *brd, Image *bg, Image *fg, int t, Font *font, char *text);
+void _cell(Image *, Rectangle, Image *, Image *, Image *, int, Font *, char *);
+void snarfsel(void);
+void pastesel(void);
void
-loadfile(char *newfile)
+loadfile(char *file)
{
- char *s, *args[64];
- int n, x, y = 0;
- Biobuf *b = Bopen(newfile, OREAD);
- if (b == nil) {
+ int fd = open(file, OREAD);
+ if (fd < 0) {
fprint(2, "loadfile: %r\n");
return;
}
- free(file);
- file = newfile;
- while( (s = Brdstr(b, '\n', 1)) != nil ) {
- n = getfields(s, args, 64, 0, "\t");
- for (x = 0; x < n; x++) {
- if (strlen(args[x]) != 0) setview(Pt(x, y), strdup(args[x]));
- };
- free(s);
- y++;
+ Table *t = readtable(fd);
+ close(fd);
+ if (t != nil) {
+ freetable(view);
+ view = t;
}
- Bterm(b);
}
void
-savefile(char *newfile)
+savefile(char *file)
{
- // TODO clean up trainling whitespace (tabs and newlines)
- // TODO switch over to bio
- int fd = create(newfile, OWRITE, 0666);
+ int fd = create(file, OWRITE, 0666);
if (fd < 0) {
fprint(2, "savefile: %r\n");
return;
}
- Biobuf *b = Bfdopen(fd, OWRITE);
- free(file);
- file = newfile;
- int x, y;
- for (y = view.r.min.y; y < view.r.max.y; y++) {
- for (x = view.r.min.x; x < view.r.max.x; x++) {
- char **v = getview(Pt(x, y));
- if (x != view.r.min.x) Bprint(b, "\t");
- if ((v != nil) && (*v != nil)) Bprint(b, "%s", *v);
- }
- Bprint(b, "\n");
+ writetable(view, fd);
+ close(fd);
+}
+
+void
+snarfsel(void)
+{
+ int fd = create(snarf, OWRITE, 0666);
+ if (fd < 0) {
+ fprint(2, "snarfsel: %r");
+ return;
}
- Bterm(b);
+ Table *t = duptable(view, cells.sel);
+ writetable(t, fd);
+ freetable(t);
+ close(fd);
+}
+
+void
+pastesel(void)
+{
+ int fd = open(snarf, OREAD);
+ if (fd < 0) {
+ fprint(2, "pastesel: %r");
+ return;
+ }
+ Table *t = readtable(fd);
+ pastetable(view, t, cells.cur);
+ close(fd);
}
void
@@ -127,7 +150,7 @@ drawcell(Point xy)
(xy.x - cells.scroll.x) * cell.max.x,
(xy.y - cells.scroll.y) * cell.max.y), cells.r.min));
if (ptinrect(r.min, cells.r) == 1) {
- char **v = getview(xy);
+ char **v = tfetch(view, xy);
if ((v != nil) && (*v != nil)) s = *v;
_cell(screen, r, bord, cellbg, text, t, font, s);
}
@@ -257,30 +280,42 @@ mouse(Mouse m)
flushimage(display, 1);
}
} else if (m.buttons == 4) {
- static char *items[] = {"load", "save", "exit", nil};
+ static char *items[] = {"cut", "paste", "snarf", "load", "save", "exit", nil};
static Menu menu = {
items,
nil,
0,
};
- char buf[1024];
- buf[0] = '\0';
- if (file != nil) strncat(buf, file, 1024);
switch (menuhit(3, mctl, &menu, nil)) {
- case 0: /* load */
- if (enter("load", buf, 1024, mctl, kctl, nil) > 0) {
- clearview();
- loadfile(strdup(buf));
+ case 0: /* cut */
+ snarfsel();
+ cleartable(view, cells.sel);
+ setedit();
+ redraw();
+ flushimage(display, 1);
+ break;
+ case 1: /* paste */
+ pastesel();
+ setedit();
+ redraw();
+ flushimage(display, 1);
+ break;
+ case 2: /* snarf */
+ snarfsel();
+ break;
+ case 3: /* load */
+ if (enter("load", file, 1024, mctl, kctl, nil) > 0) {
+ loadfile(file);
redraw();
flushimage(display, 1);
}
break;
- case 1: /* save */
- if (enter("save", buf, 1024, mctl, kctl, nil) > 0) {
- savefile(strdup(buf));
+ case 4: /* save */
+ if (enter("save", file, 1024, mctl, kctl, nil) > 0) {
+ savefile(file);
}
break;
- case 2: /* exit */
+ case 5: /* exit */
threadexitsall(nil);
break;
}
@@ -362,7 +397,10 @@ threadmain(int argc, char **argv)
ARGBEGIN {
default: usage();
} ARGEND;
- if (argc == 1) loadfile(strdup(argv[0]));
+ if (argc == 1) {
+ loadfile(argv[0]);
+ strncat(file, argv[0], 256);
+ } else view = alloctable(Rect(0, 0, 1, 1));
init();
Alt alts[] = {
{mctl->c, &mv, CHANRCV},
@@ -391,17 +429,14 @@ threadmain(int argc, char **argv)
void
flushedit(void)
{
- char **v = getview(cells.cur);
- if ((v == nil) || (*v == nil) || (strcmp(s_to_c(edit.str), *v) != 0)) {
- if (strlen(s_to_c(edit.str)) != 0) setview(cells.cur, strdup(s_to_c(edit.str)));
- else setview(cells.cur, nil);
- }
+ if (strlen(s_to_c(edit.str)) == 0) tstore(view, cells.cur, nil);
+ else tstore(view, cells.cur, strdup(s_to_c(edit.str)));
}
void
setedit(void)
{
- char **v = getview(cells.cur);
+ char **v = tfetch(view, cells.cur);
s_reset(edit.str);
if ((v != nil) && (*v != nil)) s_append(edit.str, *v);
}
@@ -417,54 +452,144 @@ drawedit(void)
_cell(screen, edit.r, bord, curbg, text, 1, font, buf);
}
+Table *
+alloctable(Rectangle r)
+{
+ Table *t;
+ t = malloc(sizeof(Table));
+ *t = (Table) {r, mallocz(sizeof(char *) * Dx(r) * Dy(r), 1)};
+ return t;
+}
+
+void
+freetable(Table *t)
+{
+ int x, y;
+ if (t == nil) return;
+ for (x = t->r.min.x; x < t->r.max.x; x++)
+ for (y = t->r.min.y; y < t->r.max.y; y++) {
+ free(t->s[x - t->r.min.x + (y - t->r.min.y) * Dx(t->r)]);
+ };
+ free(t->s);
+ free(t);
+}
+
void
-resizeview(Rectangle r)
+resizetable(Table *t, Rectangle r)
{
char **s = mallocz(sizeof(char *) * Dx(r) * Dy(r), 1);
int x, y;
for (x = r.min.x; x < r.max.x; x++)
for (y = r.min.x; y < r.max.y; y++) {
- int i = x - r.min.x + y * Dx(r);
- char **v = getview(Pt(x, y));
- s[i] = (v == nil) ? nil : *v;
+ int i = x - r.min.x + y * Dx(r);
+ char **v = tfetch(t, Pt(x, y));
+ if ((v != nil) && (*v != nil)) s[i] = strdup(*v);
+ }
+ for (x = t->r.min.x; x < t->r.max.x; x++)
+ for (y = t->r.min.y; y < t->r.max.y; y++) {
+ free(t->s[x - t->r.min.x + (y - t->r.min.y) * Dx(t->r)]);
+ };
+ free(t->s);
+ *t = (Table){r, s};
+}
+
+Table *
+duptable(Table *t, Rectangle r)
+{
+ Table *n = alloctable(r);
+ int x, y;
+ for (x = r.min.x; x < r.max.x; x++)
+ for (y = r.min.x; y < r.max.y; y++) {
+ char **v = tfetch(t, Pt(x, y));
+ if ((v != nil) && (*v != nil)) n->s[x - r.min.x + (y - r.min.y) * Dx(r)] = strdup(*v);
}
- free(view.s);
- view.r = r;
- view.s = s;
+ return n;
}
void
-clearview(void)
+cleartable(Table *t, Rectangle r)
{
- int i;
- if ((view.s == nil) || (Dx(view.r) * Dy(view.r) == 0)) return;
- for (i = 0; i < Dx(view.r) * Dy(view.r); i++) {
- free(view.s[i]);
- view.s[i] = nil;
+ int x, y;
+ for (x = r.min.x; x < r.max.x; x++)
+ for (y = r.min.x; y < r.max.y; y++) {
+ tstore(t, Pt(x, y), nil);
}
}
void
-setview(Point xy, char *s)
+pastetable(Table *to, Table *fr, Point sp)
+{
+ int x, y;
+ for (x = fr->r.min.x; x <= fr->r.max.x; x++)
+ for (y = fr->r.min.x; y <= fr->r.max.y; y++) {
+ char **v = tfetch(fr, Pt(x, y));
+ if ((v != nil) && (*v != nil)) tstore(to, addpt(sp, Pt(x, y)), strdup(*v));
+ else tstore(to, addpt(sp, Pt(x, y)), nil);
+ }
+}
+
+char **
+tfetch(Table *t, Point xy)
+{
+ if ((t->s == nil) || (ptinrect(xy, t->r) == 0)) return nil;
+ return &(t->s[xy.x - t->r.min.x + (xy.y - t->r.min.y) * Dx(t->r)]);
+}
+
+void
+tstore(Table *t, Point xy, char *s)
{
Rectangle new = {
- 0,
- 0,
- (xy.x >= view.r.max.x) ? xy.x + 1 : view.r.max.x,
- (xy.y >= view.r.max.y) ? xy.y + 1 : view.r.max.y,
+ (xy.x < t->r.min.x) ? xy.x : t->r.min.x,
+ (xy.y < t->r.min.y) ? xy.y : t->r.min.y,
+ (xy.x >= t->r.max.x) ? xy.x + 1 : t->r.max.x,
+ (xy.y >= t->r.max.y) ? xy.y + 1 : t->r.max.y,
};
- if (eqrect(new, view.r) == 0) {
- resizeview(new);
+ if (eqrect(new, t->r) == 0) {
+ resizetable(t, new);
}
- char **v = getview(xy);
+ char **v = tfetch(t, xy);
free(*v);
*v = s;
}
-char **
-getview(Point xy)
+Table *
+readtable(int fd)
{
- if ((view.s == nil) || (ptinrect(xy, view.r) == 0)) return nil;
- int i = xy.x + xy.y * Dx(view.r);
- return &view.s[i];
+ Table *t = mallocz(sizeof(Table), 1);
+ char *s, *args[64];
+ int n, x, y = 0;
+ fd = dup(fd, -1);
+ Biobuf *b = Bfdopen(fd, OREAD);
+ if (b == nil) {
+ fprint(2, "readtable: %r\n");
+ return nil;
+ }
+ while( (s = Brdstr(b, '\n', 1)) != nil) {
+ n = getfields(s, args, 64, 0, "\t");
+ for (x = 0; x < n; x++) {
+ if (strlen(args[x]) != 0) tstore(t, Pt(x, y), strdup(args[x]));
+ };
+ free(s);
+ y++;
+ }
+ Bterm(b);
+ return t;
+}
+
+int
+writetable(Table *t, int fd)
+{
+ fd = dup(fd, -1);
+ Biobuf *b = Bfdopen(fd, OWRITE);
+ int x, y;
+ for (y = t->r.min.y; y < t->r.max.y; y++) {
+ for (x = t->r.min.x; x < t->r.max.x; x++) {
+ char **v = tfetch(t, Pt(x, y));
+ if (x != t->r.min.x) Bprint(b, "\t");
+ if ((v != nil) && (*v != nil)) Bprint(b, "%s", *v);
+ }
+ Bprint(b, "\n");
+ }
+ Bterm(b);
+ return 0;
}