acme9k/rows.c
Derek Stevens 2908f1ff9b add active tick indication; v9001-a02
window tag/body with keyboard focus will render tick in hilighted text color; doesn't work yet for column/row tags
2022-02-21 22:53:04 -07:00

863 lines
20 KiB
C

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <cursor.h>
#include <mouse.h>
#include <keyboard.h>
#include "libframe/frame.h"
#include <fcall.h>
#include <bio.h>
#include <plumb.h>
#include <libsec.h>
#include "dat.h"
#include "fns.h"
static Rune Lcolhdr[] = {'N', 'e', 'w', 'c', 'o', 'l', ' ', 'K', 'i', 'l',
'l', ' ', 'P', 'u', 't', 'a', 'l', 'l', ' ', 'D',
'u', 'm', 'p', ' ', 'E', 'x', 'i', 't', ' ', 0};
void rowinit(Row* row, Rectangle r) {
Rectangle r1;
Text* t;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
row->r = r;
row->col = nil;
row->ncol = 0;
r1 = r;
r1.max.y = r1.min.y + font->height;
t = &row->tag;
textinit(
t,
fileaddtext(nil, t),
r1,
rfget(FALSE, FALSE, FALSE, nil),
tagcols);
t->what = Rowtag;
t->row = row;
t->w = nil;
t->col = nil;
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(
screen,
r1,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
textinsert(t, 0, Lcolhdr, 29, TRUE);
textsetselect(t, t->file->b.nc, t->file->b.nc);
}
Column* rowadd(Row* row, Column* c, int x) {
Rectangle r, r1;
Column* d;
int i;
d = nil;
r = row->r;
r.min.y = row->tag.fr.r.max.y + Border;
if (x < r.min.x && row->ncol > 0) { /*steal 40% of last column by default */
d = row->col[row->ncol - 1];
x = d->r.min.x + 3 * Dx(d->r) / 5;
}
/* look for column we'll land on */
for (i = 0; i < row->ncol; i++) {
d = row->col[i];
if (x < d->r.max.x)
break;
}
if (row->ncol > 0) {
if (i < row->ncol)
i++; /* new column will go after d */
r = d->r;
if (Dx(r) < 100)
return nil;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
r1 = r;
r1.max.x = min(x - Border, r.max.x - 50);
if (Dx(r1) < 50)
r1.max.x = r1.min.x + 50;
colresize(d, r1);
r1.min.x = r1.max.x;
r1.max.x = r1.min.x + Border;
draw(
screen,
r1,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
r.min.x = r1.max.x;
}
if (c == nil) {
c = emalloc(sizeof(Column));
colinit(c, r);
incref(&reffont.ref);
} else
colresize(c, r);
c->row = row;
c->tag.row = row;
row->col = realloc(row->col, (row->ncol + 1) * sizeof(Column*));
memmove(row->col + i + 1, row->col + i, (row->ncol - i) * sizeof(Column*));
row->col[i] = c;
row->ncol++;
clearmouse();
return c;
}
void rowresize(Row* row, Rectangle r) {
int i, deltax;
Rectangle or, r1, r2;
Column* c;
or = row->r;
deltax = r.min.x - or.min.x;
row->r = r;
r1 = r;
r1.max.y = r1.min.y + font->height;
textresize(&row->tag, r1, TRUE);
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(
screen,
r1,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
r.min.y = r1.max.y;
r1 = r;
r1.max.x = r1.min.x;
for (i = 0; i < row->ncol; i++) {
c = row->col[i];
r1.min.x = r1.max.x;
/* the test should not be necessary, but guarantee we don't lose a pixel */
if (i == row->ncol - 1)
r1.max.x = r.max.x;
else
r1.max.x = (c->r.max.x - or.min.x) * Dx(r) / Dx(or) + deltax;
if (i > 0) {
r2 = r1;
r2.max.x = r2.min.x + Border;
draw(
screen,
r2,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
r1.min.x = r2.max.x;
}
colresize(c, r1);
}
}
void rowdragcol(Row* row, Column* c, int _0) {
Rectangle r;
int i, b, x;
Point p, op;
Column* d;
USED(_0);
clearmouse();
setcursor2(mousectl, &boxcursor, &boxcursor2);
b = mouse->buttons;
op = mouse->xy;
while (mouse->buttons == b)
readmouse(mousectl);
setcursor(mousectl, nil);
if (mouse->buttons) {
while (mouse->buttons)
readmouse(mousectl);
return;
}
for (i = 0; i < row->ncol; i++)
if (row->col[i] == c)
goto Found;
error("can't find column");
Found:
p = mouse->xy;
if ((abs(p.x - op.x) < 5 && abs(p.y - op.y) < 5))
return;
if (
(i > 0 && p.x < row->col[i - 1]->r.min.x) ||
(i < row->ncol - 1 && p.x > c->r.max.x)) {
/* shuffle */
x = c->r.min.x;
rowclose(row, c, FALSE);
if (rowadd(row, c, p.x) == nil) /* whoops! */
if (rowadd(row, c, x) == nil) /* WHOOPS! */
if (rowadd(row, c, -1) == nil) { /* shit! */
rowclose(row, c, TRUE);
return;
}
colmousebut(c);
return;
}
if (i == 0)
return;
d = row->col[i - 1];
if (p.x < d->r.min.x + 80 + Scrollwid)
p.x = d->r.min.x + 80 + Scrollwid;
if (p.x > c->r.max.x - 80 - Scrollwid)
p.x = c->r.max.x - 80 - Scrollwid;
r = d->r;
r.max.x = c->r.max.x;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
r.max.x = p.x;
colresize(d, r);
r = c->r;
r.min.x = p.x;
r.max.x = r.min.x;
r.max.x += Border;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
r.min.x = r.max.x;
r.max.x = c->r.max.x;
colresize(c, r);
colmousebut(c);
}
void rowclose(Row* row, Column* c, int dofree) {
Rectangle r;
int i;
for (i = 0; i < row->ncol; i++)
if (row->col[i] == c)
goto Found;
error("can't find column");
Found:
r = c->r;
if (dofree)
colcloseall(c);
row->ncol--;
memmove(row->col + i, row->col + i + 1, (row->ncol - i) * sizeof(Column*));
row->col = realloc(row->col, row->ncol * sizeof(Column*));
if (row->ncol == 0) {
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
return;
}
if (i == row->ncol) { /* extend last column right */
c = row->col[i - 1];
r.min.x = c->r.min.x;
r.max.x = row->r.max.x;
} else { /* extend next window left */
c = row->col[i];
r.max.x = c->r.max.x;
}
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
colresize(c, r);
}
Column* rowwhichcol(Row* row, Point p) {
int i;
Column* c;
for (i = 0; i < row->ncol; i++) {
c = row->col[i];
if (ptinrect(p, c->r))
return c;
}
return nil;
}
Text* rowwhich(Row* row, Point p) {
Column* c;
if (ptinrect(p, row->tag.all))
return &row->tag;
c = rowwhichcol(row, p);
if (c)
return colwhich(c, p);
return nil;
}
Text* rowtype(Row* row, Rune r, Point p) {
Window* w;
Text* t;
if (r == 0)
r = Runeerror;
clearmouse();
qlock(&row->lk);
if (bartflag)
t = barttext;
else
t = rowwhich(row, p);
if (t != nil && !(t->what == Tag && ptinrect(p, t->scrollr))) {
w = t->w;
if (w == nil)
texttype(t, r);
else {
winlock(w, 'K');
wintype(w, t, r);
/* Expand tag if necessary */
if (t->what == Tag) {
t->w->tagsafe = FALSE;
if (r == '\n')
t->w->tagexpand = TRUE;
winresize(w, w->r, TRUE, TRUE);
}
winunlock(w);
}
}
qunlock(&row->lk);
return t;
}
int rowclean(Row* row) {
int clean;
int i;
clean = TRUE;
for (i = 0; i < row->ncol; i++)
clean &= colclean(row->col[i]);
return clean;
}
void rowdump(Row* row, char* file) {
int i, j, fd, m, n, dumped;
uint q0, q1;
Biobuf* b;
char *buf, *a, *fontname;
Rune* r;
Column* c;
Window *w, *w1;
Text* t;
if (row->ncol == 0)
return;
buf = fbufalloc();
if (file == nil) {
if (home == nil) {
warning(nil, "can't find file for dump: $home not defined\n");
goto Rescue;
}
sprint(buf, "%s/acme.dump", home);
file = buf;
}
fd = create(file, OWRITE, 0600);
if (fd < 0) {
warning(nil, "can't open %s: %r\n", file);
goto Rescue;
}
b = emalloc(sizeof(Biobuf));
Binit(b, fd, OWRITE);
r = fbufalloc();
Bprint(b, "%s\n", wdir);
Bprint(b, "%s\n", fontnames[0]);
Bprint(b, "%s\n", fontnames[1]);
for (i = 0; i < row->ncol; i++) {
c = row->col[i];
Bprint(b, "%11.7f", 100.0 * (c->r.min.x - row->r.min.x) / Dx(row->r));
if (i == row->ncol - 1)
Bputc(b, '\n');
else
Bputc(b, ' ');
}
for (i = 0; i < row->ncol; i++) {
c = row->col[i];
for (j = 0; j < c->nw; j++)
c->w[j]->body.file->dumpid = 0;
}
m = min(RBUFSIZE, row->tag.file->b.nc);
bufread(&row->tag.file->b, 0, r, m);
n = 0;
while (n < m && r[n] != '\n')
n++;
Bprint(b, "w %.*S\n", n, r);
for (i = 0; i < row->ncol; i++) {
c = row->col[i];
m = min(RBUFSIZE, c->tag.file->b.nc);
bufread(&c->tag.file->b, 0, r, m);
n = 0;
while (n < m && r[n] != '\n')
n++;
Bprint(b, "c%11d %.*S\n", i, n, r);
}
for (i = 0; i < row->ncol; i++) {
c = row->col[i];
for (j = 0; j < c->nw; j++) {
w = c->w[j];
wincommit(w, &w->tag);
t = &w->body;
/* windows owned by others get special treatment */
if (w->nopen[QWevent] > 0)
if (w->dumpstr == nil)
continue;
/* zeroxes of external windows are tossed */
if (t->file->ntext > 1)
for (n = 0; n < t->file->ntext; n++) {
w1 = t->file->text[n]->w;
if (w == w1)
continue;
if (w1->nopen[QWevent])
goto Continue2;
}
fontname = "";
if (t->reffont->f != font)
fontname = t->reffont->f->name;
if (t->file->nname)
a = runetobyte(t->file->name, t->file->nname);
else
a = emalloc(1);
if (t->file->dumpid) {
dumped = FALSE;
Bprint(
b,
"x%11d %11d %11d %11d %11.7f %s\n",
i,
t->file->dumpid,
w->body.q0,
w->body.q1,
100.0 * (w->r.min.y - c->r.min.y) / Dy(c->r),
fontname);
} else if (w->dumpstr) {
dumped = FALSE;
Bprint(
b,
"e%11d %11d %11d %11d %11.7f %s\n",
i,
t->file->dumpid,
0,
0,
100.0 * (w->r.min.y - c->r.min.y) / Dy(c->r),
fontname);
} else if ((w->dirty == FALSE && access(a, 0) == 0) || w->isdir) {
dumped = FALSE;
t->file->dumpid = w->id;
Bprint(
b,
"f%11d %11d %11d %11d %11.7f %s\n",
i,
w->id,
w->body.q0,
w->body.q1,
100.0 * (w->r.min.y - c->r.min.y) / Dy(c->r),
fontname);
} else {
dumped = TRUE;
t->file->dumpid = w->id;
Bprint(
b,
"F%11d %11d %11d %11d %11.7f %11d %s\n",
i,
j,
w->body.q0,
w->body.q1,
100.0 * (w->r.min.y - c->r.min.y) / Dy(c->r),
w->body.file->b.nc,
fontname);
}
free(a);
winctlprint(w, buf, 0);
Bwrite(b, buf, strlen(buf));
m = min(RBUFSIZE, w->tag.file->b.nc);
bufread(&w->tag.file->b, 0, r, m);
n = 0;
while (n < m && r[n] != '\n')
n++;
Bprint(b, "%.*S\n", n, r);
if (dumped) {
q0 = 0;
q1 = t->file->b.nc;
while (q0 < q1) {
n = q1 - q0;
if (n > BUFSIZE / UTFmax)
n = BUFSIZE / UTFmax;
bufread(&t->file->b, q0, r, n);
Bprint(b, "%.*S", n, r);
q0 += n;
}
}
if (w->dumpstr) {
if (w->dumpdir)
Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr);
else
Bprint(b, "\n%s\n", w->dumpstr);
}
Continue2:;
}
}
Bterm(b);
close(fd);
free(b);
fbuffree(r);
Rescue:
fbuffree(buf);
}
static char* rdline(Biobuf* b, int* linep) {
char* l;
l = Brdline(b, '\n');
if (l)
(*linep)++;
return l;
}
/*
* Get font names from load file so we don't load fonts we won't use
*/
void rowloadfonts(char* file) {
int i;
Biobuf* b;
char* l;
b = Bopen(file, OREAD);
if (b == nil)
return;
/* current directory */
l = Brdline(b, '\n');
if (l == nil)
goto Return;
/* global fonts */
for (i = 0; i < 2; i++) {
l = Brdline(b, '\n');
if (l == nil)
goto Return;
l[Blinelen(b) - 1] = 0;
if (*l && strcmp(l, fontnames[i]) != 0) {
free(fontnames[i]);
fontnames[i] = estrdup(l);
}
}
Return:
Bterm(b);
}
int rowload(Row* row, char* file, int initing) {
int i, j, line, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd, done;
double percent;
Biobuf *b, *bout;
char *buf, *l, *t, *fontname;
Rune *r, *fontr;
int rune;
Column *c, *c1, *c2;
uint q0, q1;
Rectangle r1, r2;
Window* w;
buf = fbufalloc();
if (file == nil) {
if (home == nil) {
warning(nil, "can't find file for load: $home not defined\n");
goto Rescue1;
}
sprint(buf, "%s/acme.dump", home);
file = buf;
}
b = Bopen(file, OREAD);
if (b == nil) {
warning(nil, "can't open load file %s: %r\n", file);
goto Rescue1;
}
/* current directory */
line = 0;
l = rdline(b, &line);
if (l == nil)
goto Rescue2;
l[Blinelen(b) - 1] = 0;
if (chdir(l) < 0) {
warning(nil, "can't chdir %s\n", l);
goto Rescue2;
}
/* global fonts */
for (i = 0; i < 2; i++) {
l = rdline(b, &line);
if (l == nil)
goto Rescue2;
l[Blinelen(b) - 1] = 0;
if (*l && strcmp(l, fontnames[i]) != 0)
rfget(i, TRUE, i == 0 && initing, l);
}
if (initing && row->ncol == 0)
rowinit(row, screen->clipr);
l = rdline(b, &line);
if (l == nil)
goto Rescue2;
j = Blinelen(b) / 12;
if (j <= 0 || j > 10)
goto Rescue2;
for (i = 0; i < j; i++) {
percent = atof(l + i * 12);
if (percent < 0 || percent >= 100)
goto Rescue2;
x = row->r.min.x + percent * Dx(row->r) / 100 + 0.5;
if (i < row->ncol) {
if (i == 0)
continue;
c1 = row->col[i - 1];
c2 = row->col[i];
r1 = c1->r;
r2 = c2->r;
if (x < Border)
x = Border;
r1.max.x = x - Border;
r2.min.x = x;
if (Dx(r1) < 50 || Dx(r2) < 50)
continue;
draw(
screen,
Rpt(r1.min, r2.max),
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
colresize(c1, r1);
colresize(c2, r2);
r2.min.x = x - Border;
r2.max.x = x;
draw(
screen,
r2,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x222222FF),
nil,
ZP);
}
if (i >= row->ncol)
rowadd(row, nil, x);
}
done = 0;
while (!done) {
l = rdline(b, &line);
if (l == nil)
break;
switch (l[0]) {
case 'c':
l[Blinelen(b) - 1] = 0;
i = atoi(l + 1 + 0 * 12);
r = bytetorune(l + 1 * 12, &nr);
ns = -1;
for (n = 0; n < nr; n++) {
if (r[n] == '/')
ns = n;
if (r[n] == ' ')
break;
}
textdelete(&row->col[i]->tag, 0, row->col[i]->tag.file->b.nc, TRUE);
textinsert(&row->col[i]->tag, 0, r + n + 1, nr - (n + 1), TRUE);
free(r);
break;
case 'w':
l[Blinelen(b) - 1] = 0;
r = bytetorune(l + 2, &nr);
ns = -1;
for (n = 0; n < nr; n++) {
if (r[n] == '/')
ns = n;
if (r[n] == ' ')
break;
}
textdelete(&row->tag, 0, row->tag.file->b.nc, TRUE);
textinsert(&row->tag, 0, r, nr, TRUE);
free(r);
break;
default:
done = 1;
break;
}
}
for (;;) {
if (l == nil)
break;
dumpid = 0;
switch (l[0]) {
case 'e':
if (Blinelen(b) < 1 + 5 * 12 + 1)
goto Rescue2;
l = rdline(b, &line); /* ctl line; ignored */
if (l == nil)
goto Rescue2;
l = rdline(b, &line); /* directory */
if (l == nil)
goto Rescue2;
l[Blinelen(b) - 1] = 0;
if (*l == '\0') {
if (home == nil)
r = bytetorune("./", &nr);
else {
t = emalloc(strlen(home) + 1 + 1);
sprint(t, "%s/", home);
r = bytetorune(t, &nr);
free(t);
}
} else
r = bytetorune(l, &nr);
l = rdline(b, &line); /* command */
if (l == nil)
goto Rescue2;
t = emalloc(Blinelen(b) + 1);
memmove(t, l, Blinelen(b));
run(nil, t, r, nr, TRUE, nil, nil, FALSE);
/* r is freed in run() */
goto Nextline;
case 'f':
if (Blinelen(b) < 1 + 5 * 12 + 1)
goto Rescue2;
fontname = l + 1 + 5 * 12;
ndumped = -1;
break;
case 'F':
if (Blinelen(b) < 1 + 6 * 12 + 1)
goto Rescue2;
fontname = l + 1 + 6 * 12;
ndumped = atoi(l + 1 + 5 * 12 + 1);
break;
case 'x':
if (Blinelen(b) < 1 + 5 * 12 + 1)
goto Rescue2;
fontname = l + 1 + 5 * 12;
ndumped = -1;
dumpid = atoi(l + 1 + 1 * 12);
break;
default:
goto Rescue2;
}
l[Blinelen(b) - 1] = 0;
fontr = nil;
nfontr = 0;
if (*fontname)
fontr = bytetorune(fontname, &nfontr);
i = atoi(l + 1 + 0 * 12);
j = atoi(l + 1 + 1 * 12);
q0 = atoi(l + 1 + 2 * 12);
q1 = atoi(l + 1 + 3 * 12);
percent = atof(l + 1 + 4 * 12);
if (i < 0 || i > 10)
goto Rescue2;
if (i > row->ncol)
i = row->ncol;
c = row->col[i];
y = c->r.min.y + (percent * Dy(c->r)) / 100 + 0.5;
if (y < c->r.min.y || y >= c->r.max.y)
y = -1;
if (dumpid == 0)
w = coladd(c, nil, nil, y);
else
w = coladd(c, nil, lookid(dumpid, TRUE), y);
if (w == nil)
goto Nextline;
w->dumpid = j;
l = rdline(b, &line);
if (l == nil)
goto Rescue2;
l[Blinelen(b) - 1] = 0;
r = bytetorune(l + 5 * 12, &nr);
ns = -1;
for (n = 0; n < nr; n++) {
if (r[n] == '/')
ns = n;
if (r[n] == ' ')
break;
}
if (dumpid == 0)
winsetname(w, r, n);
for (; n < nr; n++)
if (r[n] == '|')
break;
wincleartag(w);
textinsert(&w->tag, w->tag.file->b.nc, r + n + 1, nr - (n + 1), TRUE);
if (ndumped >= 0) {
/* simplest thing is to put it in a file and load that */
sprint(buf, "/tmp/d%d.%.4sacme", getpid(), getuser());
fd = create(buf, OWRITE, 0600);
if (fd < 0) {
free(r);
warning(nil, "can't create temp file: %r\n");
goto Rescue2;
}
bout = emalloc(sizeof(Biobuf));
Binit(bout, fd, OWRITE);
for (n = 0; n < ndumped; n++) {
rune = Bgetrune(b);
if (rune == '\n')
line++;
if (rune == Beof) {
free(r);
Bterm(bout);
free(bout);
close(fd);
remove(buf);
goto Rescue2;
}
Bputrune(bout, rune);
}
Bterm(bout);
free(bout);
textload(&w->body, 0, buf, 1);
remove(buf);
close(fd);
w->body.file->mod = TRUE;
for (n = 0; n < w->body.file->ntext; n++)
w->body.file->text[n]->w->dirty = TRUE;
winsettag(w);
} else if (dumpid == 0 && r[ns + 1] != '+' && r[ns + 1] != '-')
get(&w->body, nil, nil, FALSE, XXX, nil, 0);
if (fontr) {
fontx(&w->body, nil, nil, 0, 0, fontr, nfontr);
free(fontr);
}
free(r);
if (q0 > w->body.file->b.nc || q1 > w->body.file->b.nc || q0 > q1)
q0 = q1 = 0;
textshow(&w->body, q0, q1, 1);
w->maxlines = min(w->body.fr.nlines, max(w->maxlines, w->body.fr.maxlines));
xfidlog(w, "new");
Nextline:
l = rdline(b, &line);
}
Bterm(b);
fbuffree(buf);
return TRUE;
Rescue2:
warning(nil, "bad load file %s:%d\n", file, line);
Bterm(b);
Rescue1:
fbuffree(buf);
return FALSE;
}
void allwindows(void (*f)(Window*, void*), void* arg) {
int i, j;
Column* c;
for (i = 0; i < row.ncol; i++) {
c = row.col[i];
for (j = 0; j < c->nw; j++)
(*f)(c->w[j], arg);
}
}