commit f9999dc4de71128e720cc6033402cf925f6245dd
parent 34b341144e9e63e1ad92d5937c32cd45669d9471
Author: rpa <rpa@laika>
Date: Sun, 18 Dec 2022 15:13:06 +0000
tabul: rework internal data structure
Diffstat:
M | src/tabul/tabul.c | | | 296 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
1 file changed, 175 insertions(+), 121 deletions(-)
diff --git a/src/tabul/tabul.c b/src/tabul/tabul.c
@@ -2,9 +2,6 @@
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
*/
@@ -18,23 +15,29 @@
#include <keyboard.h>
#include <mouse.h>
-typedef struct Table Table;
-struct Table{
- Rectangle r;
- char **s;
+typedef struct Array Array;
+struct Array {
+ long n;
+ void **p;
+ void (*freep)(void *);
+ void *aux;
};
-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);
+
+Array * createarray(void (*freep)(void *));
+void freearray(void *);
+void astore(Array *, int, void *);
+void * afetch(Array *, int);
+void atrim(Array *ar);
+
+Array *view;
+
+Array * readtable(int);
+int writetable(Array *, int);
+char * tfetch(Array *, Point);
+void tstore(Array *, Point, char *);
+void cleartable(Array *, Rectangle);
+Array * duptable(Array *, Rectangle);
+void pastetable(Array *, Array *, Point);
Image *bord, *text, *bg, *curbg;
Rectangle cell, blank;
@@ -78,10 +81,10 @@ loadfile(char *file)
fprint(2, "loadfile: %r\n");
return;
}
- Table *t = readtable(fd);
+ Array *t = readtable(fd);
close(fd);
if (t != nil) {
- freetable(view);
+ freearray(view);
view = t;
}
}
@@ -106,9 +109,9 @@ snarfsel(void)
fprint(2, "snarfsel: %r");
return;
}
- Table *t = duptable(view, cells.sel);
+ Array *t = duptable(view, cells.sel);
writetable(t, fd);
- freetable(t);
+ freearray(t);
close(fd);
}
@@ -120,7 +123,7 @@ pastesel(void)
fprint(2, "pastesel: %r");
return;
}
- Table *t = readtable(fd);
+ Array *t = readtable(fd);
pastetable(view, t, cells.cur);
close(fd);
}
@@ -132,7 +135,7 @@ _cell(Image *screen, Rectangle r, Image *brd, Image *bg, Image *fg, int t, Font
draw(screen, r, brd, nil, ZP);
draw(screen, r2, bg, nil, ZP);
if (text != nil) {
- int n = strlen(text);
+ int n = 16;//strlen(text);
while (stringnwidth(font, text, n) > Dx(r2)) n--;
stringn(screen, addpt(r2.min, Pt(1,(Dy(r2) - font->height)/2)), fg, ZP, font, text, n);
}
@@ -150,8 +153,8 @@ 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 = tfetch(view, xy);
- if ((v != nil) && (*v != nil)) s = *v;
+ char *v = tfetch(view, xy);
+ if (v != nil) s = v;
_cell(screen, r, bord, cellbg, text, t, font, s);
}
}
@@ -225,10 +228,15 @@ redraw(void)
void
colors(void)
{
- bord = display->black;
- text = display->black;
- bg = display->white;
- curbg = allocimage(display, Rect(0,0,1,1), XRGB32, 1, 0xddddddff);
+ Image *black, *white, *gray;
+ black = display->black;
+ white = display->white;
+ gray = allocimage(display, Rect(0,0,1,1), XRGB32, 1, 0xddddddff);
+
+ bord = black;
+ text = black;
+ bg = white;
+ curbg = gray;
}
void
@@ -355,12 +363,16 @@ kbd(Rune r)
// TODO: make utf8-complaint, make thread safe?
if (edit.str->ptr > edit.str->base) edit.str->ptr--;
s_terminate(edit.str);
+ flushedit();
+ drawcell(cells.cur);
drawedit();
flushimage(display, 1);
break;
default:
s_putc(edit.str, r);
s_terminate(edit.str);
+ flushedit();
+ drawcell(cells.cur);
drawedit();
flushimage(display, 1);
}
@@ -400,7 +412,7 @@ threadmain(int argc, char **argv)
if (argc == 1) {
loadfile(argv[0]);
strncat(file, argv[0], 256);
- } else view = alloctable(Rect(0, 0, 1, 1));
+ } else view = createarray(freearray);
init();
Alt alts[] = {
{mctl->c, &mv, CHANRCV},
@@ -430,15 +442,15 @@ void
flushedit(void)
{
if (strlen(s_to_c(edit.str)) == 0) tstore(view, cells.cur, nil);
- else tstore(view, cells.cur, strdup(s_to_c(edit.str)));
+ else tstore(view, cells.cur, s_to_c(edit.str));
}
void
setedit(void)
{
- char **v = tfetch(view, cells.cur);
+ char *v = tfetch(view, cells.cur);
s_reset(edit.str);
- if ((v != nil) && (*v != nil)) s_append(edit.str, *v);
+ if (v != nil) s_append(edit.str, v);
}
void
@@ -452,112 +464,79 @@ 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
-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 = 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)
+Array *
+duptable(Array *t, Rectangle r)
{
- Table *n = alloctable(r);
+ assert(t != nil);
+ Array *n = createarray(freearray);
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);
+ for (x = r.min.x; x < r.max.x; x++)
+ for (y = r.min.y; y < r.max.y; y++) {
+ char *s = tfetch(t, Pt(x, y));
+ tstore(n, subpt(Pt(x, y), r.min), s);
}
return n;
}
void
-cleartable(Table *t, Rectangle r)
+cleartable(Array *t, Rectangle r)
{
+ assert(t != nil);
int x, y;
- for (x = r.min.x; x < r.max.x; x++)
- for (y = r.min.x; y < r.max.y; y++) {
+ for (x = r.min.x; x < r.max.x; x++)
+ for (y = r.min.y; y < r.max.y; y++) {
tstore(t, Pt(x, y), nil);
}
}
void
-pastetable(Table *to, Table *fr, Point sp)
+pastetable(Array *to, Array *fr, Point sp)
{
+ assert(to != nil);
+ assert(fr != nil);
+ Rectangle r;
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);
+ r.min = ZP;
+ r.max.x = 0;
+ r.max.y = fr->n;
+ for (y = 0; y < fr->n; y++) {
+ Array *row = afetch(fr, y);
+ if ((row != nil) && (row->n > r.max.x)) {
+ r.max.x = row->n;
+ }
+ }
+ r = rectaddpt(r, sp);
+ for (x = r.min.x; x < r.max.x; x++)
+ for (y = r.min.y; y < r.max.y; y++) {
+ char *s = tfetch(fr, subpt(Pt(x, y), r.min));
+ tstore(to, Pt(x, y), s);
}
}
-char **
-tfetch(Table *t, Point xy)
+char *
+tfetch(Array *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)]);
+ Array *row = afetch(t, xy.y);
+ if (row == nil) return nil;
+ return afetch(row, xy.x);
}
void
-tstore(Table *t, Point xy, char *s)
-{
- char **v = tfetch(t, xy);
- if ((s == nil) && ((v == nil) || (*v == nil))) return;
- Rectangle new = {
- (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, t->r) == 0) {
- resizetable(t, new);
- }
- v = tfetch(t, xy);
- free(*v);
- *v = s;
+tstore(Array *t, Point xy, char *s)
+{
+ Array *row = afetch(t, xy.y);
+ if (row == nil) row = createarray(free);
+ char *col = afetch(row, xy.x);
+ free(col);
+ if (s != nil) s = strdup(s);
+ astore(row, xy.x, s);
+ astore(t, xy.y, row);
}
-Table *
+Array *
readtable(int fd)
{
- Table *t = mallocz(sizeof(Table), 1);
+ Array *t = mallocz(sizeof(Array), 1);
char *s, *args[64];
int n, x, y = 0;
fd = dup(fd, -1);
@@ -569,7 +548,7 @@ readtable(int fd)
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]));
+ if (strlen(args[x]) != 0) tstore(t, Pt(x, y), args[x]);
};
free(s);
y++;
@@ -579,19 +558,94 @@ readtable(int fd)
}
int
-writetable(Table *t, int fd)
+writetable(Array *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);
+ for (y = 0; y < t->n; y++) {
+ Array *row = afetch(t, y);
+ if (row != nil) {
+ atrim(row);
+ if (row->n == 0) {
+ freearray(row);
+ astore(t, y, nil);
+ }
+ }
+ }
+ atrim(t);
+
+ for (y = 0; y < t->n; y++) {
+ Array *row = afetch(t, y);
+ if (row == nil) continue;
+ for (x = 0; x < row->n; x++) {
+ char *s = afetch(row, x);
+ if (x != 0) Bprint(b, "\t");
+ if (s != nil) Bprint(b, "%s", s);
}
Bprint(b, "\n");
}
Bterm(b);
return 0;
}
+
+Array *
+createarray(void (*freep)(void *))
+{
+ Array *new = mallocz(sizeof(Array), 1);
+ new->freep = freep;
+ return new;
+}
+
+void
+freearray(void *v)
+{
+ if (v == nil) return;
+ Array *ar = v;
+ if (ar->freep != nil) {
+ int i;
+ for (i = 0; i < ar->n; i++) ar->freep(ar->p[i]);
+ }
+ free(ar);
+}
+
+void
+astore(Array *ar, int n, void *p)
+{
+ assert(ar != nil);
+ assert(n >= 0);
+ if (n >= ar->n) {
+ ar->p = realloc(ar->p, sizeof(void *) * (n + 1));
+ int i;
+ for (i = ar->n; i < n; i++) ar->p[i] = nil;
+ ar->n = n + 1;
+ }
+ ar->p[n] = p;
+}
+
+void *
+afetch(Array *ar, int n)
+{
+ assert(ar != nil);
+ assert(n >= 0);
+ if (n < ar->n) {
+ return ar->p[n];
+ }
+ else return nil;
+}
+
+void
+atrim(Array *ar)
+{
+ assert(ar != nil);
+ int n;
+ for (n = ar->n - 1; n >= 0; n--) {
+ if (ar->p[n] != nil) {
+ ar->n = n + 1;
+ return;
+ }
+ }
+ ar->n = 0;
+ free(ar->p);
+ ar->p = nil;
+}