acme9k/cols.c

620 lines
14 KiB
C
Raw Normal View History

2019-11-14 23:15:48 +00:00
#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"
2019-11-14 23:15:48 +00:00
#include <fcall.h>
#include <plumb.h>
#include <libsec.h>
#include "dat.h"
#include "fns.h"
#include "config.h"
2019-11-14 23:15:48 +00:00
static Rune Lheader[] = {'N', 'e', 'w', ' ', 'C', 'u', 't', ' ', 'P', 'a',
's', 't', 'e', ' ', 'S', 'n', 'a', 'r', 'f', ' ',
'S', 'o', 'r', 't', ' ', 'Z', 'e', 'r', 'o', 'x',
' ', 'D', 'e', 'l', 'c', 'o', 'l', ' ', 0};
void colinit(Column* c, Rectangle r) {
Rectangle r1;
Text* t;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
c->r = r;
c->w = nil;
c->nw = 0;
t = &c->tag;
t->w = nil;
t->col = c;
r1 = r;
r1.max.y = r1.min.y + font->height;
textinit(t, fileaddtext(nil, t), r1, &reffont, tagcols);
t->what = Columntag;
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(
screen,
r1,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
textinsert(t, 0, Lheader, 38, TRUE);
textsetselect(t, t->file->b.nc, t->file->b.nc);
draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
c->safe = TRUE;
2019-11-14 23:15:48 +00:00
}
Window* coladd(Column* c, Window* w, Window* clone, int y) {
Rectangle r, r1;
Window* v;
int i, j, minht, ymax, buggered;
v = nil;
r = c->r;
r.min.y = c->tag.fr.r.max.y + Border;
if (y < r.min.y && c->nw > 0) { /* steal half of last window by default */
v = c->w[c->nw - 1];
y = v->body.fr.r.min.y + Dy(v->body.fr.r) / 2;
}
/* look for window we'll land on */
for (i = 0; i < c->nw; i++) {
v = c->w[i];
if (y < v->r.max.y)
break;
}
buggered = 0;
if (c->nw > 0) {
if (i < c->nw)
i++; /* new window will go after v */
/*
* if landing window (v) is too small, grow it first.
*/
minht = v->tag.fr.font->height + Border + 1;
j = 0;
while (!c->safe || v->body.fr.maxlines <= 3 || Dy(v->body.all) <= minht) {
if (++j > 10) {
buggered = 1; /* too many windows in column */
break;
}
colgrow(c, v, 1);
}
/*
* figure out where to split v to make room for w
*/
/* new window stops where next window begins */
if (i < c->nw)
ymax = c->w[i]->r.min.y - Border;
else
ymax = c->r.max.y;
/* new window must start after v's tag ends */
y = max(y, v->tagtop.max.y + Border);
/* new window must start early enough to end before ymax */
y = min(y, ymax - minht);
/* if y is too small, too many windows in column */
if (y < v->tagtop.max.y + Border)
buggered = 1;
/*
* resize & redraw v
*/
r = v->r;
r.max.y = ymax;
draw(screen, r, textcols[BACK], nil, ZP);
r1 = r;
y = min(
y,
ymax - (v->tag.fr.font->height * v->taglines + v->body.fr.font->height +
Border + 1));
r1.max.y =
min(y, v->body.fr.r.min.y + v->body.fr.nlines * v->body.fr.font->height);
r1.min.y = winresize(v, r1, FALSE, FALSE);
r1.max.y = r1.min.y + Border;
draw(
screen,
r1,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
/*
* leave r with w's coordinates
*/
r.min.y = r1.max.y;
}
if (w == nil) {
w = emalloc(sizeof(Window));
w->col = c;
draw(screen, r, textcols[BACK], nil, ZP);
wininit(w, clone, r);
} else {
w->col = c;
winresize(w, r, FALSE, TRUE);
}
w->tag.col = c;
w->tag.row = c->row;
w->body.col = c;
w->body.row = c->row;
c->w = realloc(c->w, (c->nw + 1) * sizeof(Window*));
memmove(c->w + i + 1, c->w + i, (c->nw - i) * sizeof(Window*));
c->nw++;
c->w[i] = w;
c->safe = TRUE;
/* if there were too many windows, redraw the whole column */
if (buggered)
colresize(c, c->r);
savemouse(w);
/* near the button, but in the body */
moveto(mousectl, addpt(w->tag.scrollr.max, Pt(3, 3)));
barttext = &w->body;
textsettick(&w->body, c->row);
return w;
2019-11-14 23:15:48 +00:00
}
void colclose(Column* c, Window* w, int dofree) {
Rectangle r;
int i, didmouse, up;
/* w is locked */
if (!c->safe)
colgrow(c, w, 1);
for (i = 0; i < c->nw; i++)
if (c->w[i] == w)
goto Found;
error("can't find window");
Found:
r = w->r;
w->tag.col = nil;
w->body.col = nil;
w->col = nil;
didmouse = restoremouse(w);
if (dofree) {
windelete(w);
winclose(w);
}
c->nw--;
memmove(c->w + i, c->w + i + 1, (c->nw - i) * sizeof(Window*));
c->w = realloc(c->w, c->nw * sizeof(Window*));
if (c->nw == 0) {
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
return;
}
up = 0;
if (i == c->nw) { /* extend last window down */
w = c->w[i - 1];
r.min.y = w->r.min.y;
r.max.y = c->r.max.y;
} else { /* extend next window up */
up = 1;
w = c->w[i];
r.max.y = w->r.max.y;
}
draw(screen, r, textcols[BACK], nil, ZP);
if (c->safe) {
if (!didmouse && up)
w->showdel = TRUE;
winresize(w, r, FALSE, TRUE);
if (!didmouse && up)
movetodel(w);
}
2019-11-14 23:15:48 +00:00
}
void colcloseall(Column* c) {
int i;
Window* w;
if (c == activecol)
activecol = nil;
textclose(&c->tag);
for (i = 0; i < c->nw; i++) {
w = c->w[i];
winclose(w);
}
c->nw = 0;
free(c->w);
free(c);
clearmouse();
2019-11-14 23:15:48 +00:00
}
void colmousebut(Column* c) {
moveto(mousectl, divpt(addpt(c->tag.scrollr.min, c->tag.scrollr.max), 2));
2019-11-14 23:15:48 +00:00
}
void colresize(Column* c, Rectangle r) {
int i;
Rectangle r1, r2;
Window* w;
clearmouse();
r1 = r;
r1.max.y = r1.min.y + c->tag.fr.font->height;
textresize(&c->tag, r1, TRUE);
draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
r1.min.y = r1.max.y;
r1.max.y += Border;
draw(
screen,
r1,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
r1.max.y = r.max.y;
for (i = 0; i < c->nw; i++) {
w = c->w[i];
w->maxlines = 0;
if (i == c->nw - 1)
r1.max.y = r.max.y;
else {
r1.max.y = r1.min.y;
if (Dy(c->r) != 0) {
r1.max.y += (Dy(w->r) + Border) * Dy(r) / Dy(c->r);
}
}
r1.max.y = max(r1.max.y, r1.min.y + Border + font->height);
r2 = r1;
r2.max.y = r2.min.y + Border;
draw(
screen,
r2,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
r1.min.y = r2.max.y;
r1.min.y = winresize(w, r1, FALSE, i == c->nw - 1);
}
c->r = r;
2019-11-14 23:15:48 +00:00
}
static int colcmp(const void* a, const void* b) {
Rune *r1, *r2;
int i, nr1, nr2;
r1 = (*(Window**)a)->body.file->name;
nr1 = (*(Window**)a)->body.file->nname;
r2 = (*(Window**)b)->body.file->name;
nr2 = (*(Window**)b)->body.file->nname;
for (i = 0; i < nr1 && i < nr2; i++) {
if (*r1 != *r2)
return *r1 - *r2;
r1++;
r2++;
}
return nr1 - nr2;
2019-11-14 23:15:48 +00:00
}
void colsort(Column* c) {
int i, y;
Rectangle r, r1, *rp;
Window **wp, *w;
if (c->nw == 0)
return;
clearmouse();
rp = emalloc(c->nw * sizeof(Rectangle));
wp = emalloc(c->nw * sizeof(Window*));
memmove(wp, c->w, c->nw * sizeof(Window*));
qsort(wp, c->nw, sizeof(Window*), colcmp);
for (i = 0; i < c->nw; i++)
rp[i] = wp[i]->r;
r = c->r;
r.min.y = c->tag.fr.r.max.y;
draw(screen, r, textcols[BACK], nil, ZP);
y = r.min.y;
for (i = 0; i < c->nw; i++) {
w = wp[i];
r.min.y = y;
if (i == c->nw - 1)
r.max.y = c->r.max.y;
else
r.max.y = r.min.y + Dy(w->r) + Border;
r1 = r;
r1.max.y = r1.min.y + Border;
draw(
screen,
r1,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
r.min.y = r1.max.y;
y = winresize(w, r, FALSE, i == c->nw - 1);
}
free(rp);
free(c->w);
c->w = wp;
2019-11-14 23:15:48 +00:00
}
void colgrow(Column* c, Window* w, int but) {
Rectangle r, cr;
int i, j, k, l, y1, y2, *nl, *ny, tot, nnl, onl, dnl, h;
Window* v;
for (i = 0; i < c->nw; i++)
if (c->w[i] == w)
goto Found;
error("can't find window");
Found:
cr = c->r;
if (but < 0) { /* make sure window fills its own space properly */
r = w->r;
if (i == c->nw - 1 || c->safe == FALSE)
r.max.y = cr.max.y;
else
r.max.y = c->w[i + 1]->r.min.y - Border;
winresize(w, r, FALSE, TRUE);
return;
}
cr.min.y = c->w[0]->r.min.y;
if (but == 3) { /* full size */
if (i != 0) {
v = c->w[0];
c->w[0] = w;
c->w[i] = v;
}
draw(screen, cr, textcols[BACK], nil, ZP);
winresize(w, cr, FALSE, TRUE);
for (i = 1; i < c->nw; i++)
c->w[i]->body.fr.maxlines = 0;
c->safe = FALSE;
return;
}
/* store old #lines for each window */
onl = w->body.fr.maxlines;
nl = emalloc(c->nw * sizeof(int));
ny = emalloc(c->nw * sizeof(int));
tot = 0;
for (j = 0; j < c->nw; j++) {
l = c->w[j]->taglines - 1 + c->w[j]->body.fr.maxlines;
nl[j] = l;
tot += l;
}
/* approximate new #lines for this window */
if (but == 2) { /* as big as can be */
memset(nl, 0, c->nw * sizeof(int));
goto Pack;
}
nnl = min(onl + max(min(5, w->taglines - 1 + w->maxlines), onl / 2), tot);
if (nnl < w->taglines - 1 + w->maxlines)
nnl = (w->taglines - 1 + w->maxlines + nnl) / 2;
if (nnl == 0)
nnl = 2;
dnl = nnl - onl;
/* compute new #lines for each window */
for (k = 1; k < c->nw; k++) {
/* prune from later window */
j = i + k;
if (j < c->nw && nl[j]) {
l = min(dnl, max(1, nl[j] / 2));
nl[j] -= l;
nl[i] += l;
dnl -= l;
}
/* prune from earlier window */
j = i - k;
if (j >= 0 && nl[j]) {
l = min(dnl, max(1, nl[j] / 2));
nl[j] -= l;
nl[i] += l;
dnl -= l;
}
}
Pack:
/* pack everyone above */
y1 = cr.min.y;
for (j = 0; j < i; j++) {
v = c->w[j];
r = v->r;
r.min.y = y1;
r.max.y = y1 + Dy(v->tagtop);
if (nl[j])
r.max.y += 1 + nl[j] * v->body.fr.font->height;
r.min.y = winresize(v, r, c->safe, FALSE);
r.max.y += Border;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
y1 = r.max.y;
}
/* scan to see new size of everyone below */
y2 = c->r.max.y;
for (j = c->nw - 1; j > i; j--) {
v = c->w[j];
r = v->r;
r.min.y = y2 - Dy(v->tagtop);
if (nl[j])
r.min.y -= 1 + nl[j] * v->body.fr.font->height;
r.min.y -= Border;
ny[j] = r.min.y;
y2 = r.min.y;
}
/* compute new size of window */
r = w->r;
r.min.y = y1;
r.max.y = y2;
h = w->body.fr.font->height;
if (Dy(r) < Dy(w->tagtop) + 1 + h + Border)
r.max.y = r.min.y + Dy(w->tagtop) + 1 + h + Border;
/* draw window */
r.max.y = winresize(w, r, c->safe, TRUE);
if (i < c->nw - 1) {
r.min.y = r.max.y;
r.max.y += Border;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
for (j = i + 1; j < c->nw; j++)
ny[j] -= (y2 - r.max.y);
}
/* pack everyone below */
y1 = r.max.y;
for (j = i + 1; j < c->nw; j++) {
v = c->w[j];
r = v->r;
r.min.y = y1;
r.max.y = y1 + Dy(v->tagtop);
if (nl[j])
r.max.y += 1 + nl[j] * v->body.fr.font->height;
y1 = winresize(v, r, c->safe, j == c->nw - 1);
if (j < c->nw - 1) { /* no border on last window */
r.min.y = y1;
r.max.y += Border;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
y1 = r.max.y;
}
}
free(nl);
free(ny);
c->safe = TRUE;
winmousebut(w);
textsettick(&w->body, c->row);
2019-11-14 23:15:48 +00:00
}
void coldragwin(Column* c, Window* w, int but) {
Rectangle r;
int i, b;
Point p, op;
Window* v;
Column* nc;
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 < c->nw; i++)
if (c->w[i] == w)
goto Found;
error("can't find window");
Found:
if (w->tagexpand) /* force recomputation of window tag size */
w->taglines = 1;
p = mouse->xy;
if (abs(p.x - op.x) < 5 && abs(p.y - op.y) < 5) {
colgrow(c, w, but);
winmousebut(w);
return;
}
/* is it a flick to the right? */
if (abs(p.y - op.y) < 10 && p.x > op.x + 30 && rowwhichcol(c->row, p) == c)
p.x = op.x + Dx(w->r); /* yes: toss to next column */
nc = rowwhichcol(c->row, p);
if (nc != nil && nc != c) {
colclose(c, w, FALSE);
coladd(nc, w, nil, p.y);
winmousebut(w);
return;
}
if (i == 0 && c->nw == 1)
return; /* can't do it */
if (
(i > 0 && p.y < c->w[i - 1]->r.min.y) ||
(i < c->nw - 1 && p.y > w->r.max.y) || (i == 0 && p.y > w->r.max.y)) {
/* shuffle */
colclose(c, w, FALSE);
coladd(c, w, nil, p.y);
winmousebut(w);
return;
}
if (i == 0)
return;
v = c->w[i - 1];
if (p.y < v->tagtop.max.y)
p.y = v->tagtop.max.y;
if (p.y > w->r.max.y - Dy(w->tagtop) - Border)
p.y = w->r.max.y - Dy(w->tagtop) - Border;
r = v->r;
r.max.y = p.y;
if (r.max.y > v->body.fr.r.min.y) {
r.max.y -= (r.max.y - v->body.fr.r.min.y) % v->body.fr.font->height;
if (v->body.fr.r.min.y == v->body.fr.r.max.y)
r.max.y++;
}
r.min.y = winresize(v, r, c->safe, FALSE);
r.max.y = r.min.y + Border;
draw(
screen,
r,
allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, COLOR_EMPTY),
nil,
ZP);
r.min.y = r.max.y;
if (i == c->nw - 1)
r.max.y = c->r.max.y;
else
r.max.y = c->w[i + 1]->r.min.y - Border;
winresize(w, r, c->safe, TRUE);
c->safe = TRUE;
winmousebut(w);
textsettick(&w->body, c->row);
2019-11-14 23:15:48 +00:00
}
Text* colwhich(Column* c, Point p) {
int i;
Window* w;
if (!ptinrect(p, c->r))
return nil;
if (ptinrect(p, c->tag.all))
return &c->tag;
for (i = 0; i < c->nw; i++) {
w = c->w[i];
if (ptinrect(p, w->r)) {
if (ptinrect(p, w->tagtop) || ptinrect(p, w->tag.all))
return &w->tag;
/* exclude partial line at bottom */
if (p.x >= w->body.scrollr.max.x && p.y >= w->body.fr.r.max.y)
return nil;
return &w->body;
}
}
return nil;
2019-11-14 23:15:48 +00:00
}
int colclean(Column* c) {
int i, clean;
2019-11-14 23:15:48 +00:00
clean = TRUE;
for (i = 0; i < c->nw; i++)
clean &= winclean(c->w[i], TRUE);
return clean;
2019-11-14 23:15:48 +00:00
}