#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <draw.h>
#include <mouse.h>
#include <ctype.h>
#include <keyboard.h>
#include <frame.h>
#include <9p.h>
#include <bio.h>
#include <b.h>
#include "gui.h"
#include "cook.h"
/* BUG: WIP
*/
static void xinit(Panel*);
static void xterm(Panel*);
static int xctl(Panel* p, char* ctl);
static long xattrs(Panel* p, char* buf, long sz);
static long xread(Panel* p, void* buf, long cnt, vlong off);
static long xwrite(Panel* p, void* buf, long cnt, vlong off);
static void xdraw(Panel* p, int resize);
static void xmouse(Panel* p, Cmouse* m, Channel* mc);
static void xkeyboard(Panel*, Rune);
static void xtruncate(Panel* p);
Pops textops = {
.pref = "text:",
.init = xinit,
.term = xterm,
.ctl = xctl,
.attrs= xattrs,
.read = xread,
.write= xwrite,
.draw = xdraw,
.mouse = xmouse,
.keyboard = xkeyboard,
.truncate = xtruncate,
};
Pops tagops = {
.pref = "tag:",
.init = xinit,
.term = xterm,
.ctl = xctl,
.attrs= xattrs,
.read = xread,
.write= xwrite,
.draw = xdraw,
.mouse = xmouse,
.keyboard = xkeyboard,
.truncate = xtruncate,
};
Pops labelops = {
.pref = "label:",
.init = xinit,
.term = xterm,
.ctl = xctl,
.attrs= xattrs,
.read = xread,
.write= xwrite,
.draw = xdraw,
.mouse = xmouse,
.keyboard = xkeyboard,
.truncate = xtruncate,
};
Pops buttonops = {
.pref = "button:",
.init = xinit,
.term = xterm,
.ctl = xctl,
.attrs= xattrs,
.read = xread,
.write= xwrite,
.draw = xdraw,
.mouse = xmouse,
.keyboard = xkeyboard,
.truncate = xtruncate,
};
static void
xinit(Panel* p)
{
char* s;
char* q;
Rectangle r;
if (!strncmp(p->name, "text:", 5)){
p->flags |= Pedit;
p->wants = Pt(1,1);
} else if (!strncmp(p->name, "tag:", 4)){
p->flags |= Pline|Pedit;
p->wants = Pt(1,0);
} else
p->flags |= Pline;
if (p->flags&Pline)
p->font = fonts[FB];
else
p->font = fonts[FR];
if (p->flags&Pline){
s = strchr(p->name, ':');
q = strchr(s, '.');
if (q)
*q = 0;
p->blks = str2block(s ? s+1 : p->name);
if (q)
*q = '.';
} else
p->blks = str2block("");
assert(p->blks);
settextsize(p);
r = Rpt(ZP, p->minsz);
p->f.nchars = 0;
}
static void
xterm(Panel* p)
{
if (p->blks == nil)
sysfatal("xterm on terminated panel");
blockfree(p->blks);
p->blks = nil;
cleanedits(p);
if (p->loaded)
frclear(&p->f, 1);
p->loaded = 0;
}
static int
dirty(Panel* p, int set, int sendev)
{
int old;
if (p->flags&Pline) // lines do not get dirty
return 0;
old = p->flags;
if (set)
p->flags |= Pdirty;
else
p->flags &= ~Pdirty;
if (old != p->flags){
borders(p->file->parent);
if (sendev)
event(p, set ? "dirty" : "clean" );
return 1;
}
return 0;
}
static int
gotopos(Panel* p, int npos)
{
Tblock* b;
int n;
if (npos == p->froff)
return 0;
b = p->blks;
packblock(b);
if (npos > b->nr)
npos = b->nr;
if (npos > 1){
n = npos - 1; // skip this \n
if (findrln(b, &n)){ // and search for previous one
if (n > 0)
n++;
npos = n;
}
}
if (npos < 0)
npos = 0;
if (p->froff != npos){
p->froff = npos;
return 1;
} else
return 0;
}
static void
mustshow(Panel* p, int npos)
{
int h;
if (npos < p->froff || npos > p->froff + p->f.nchars){
if (gotopos(p, npos) && p->loaded){
h = gettextht(p)/2;
scrollframe(p, -h);
xdraw(p, 0);
}
}
}
static void
xdraw(Panel* p, int )
{
if (!p->file || (p->flags&Pdead))
return;
/* Problem. For buttons, we create the data from the
* file name. But, xinit does not have dfile to set its
* length, and we must do that later, i.e. here.
*/
if (p->dfile && p->dfile->length == 0LL)
p->dfile->length = blocksize(p->blks);
if (hidden(p->file) || Dx(p->rect) < 10 || Dy(p->rect) < p->font->height){
if (p->loaded)
frclear(&p->f, 0);
p->loaded = 0;
return;
}
hidesel(p);
if (p->loaded)
frclear(&p->f, 0);
frinit(&p->f, p->rect, p->font, screen, cols);
p->loaded = 1;
filltext(p);
drawsel(p);
settextsize(p);
if (p->flags&Ptag)
drawtag(p, 0);
}
static void
drawscrollbar(Panel* p, Image* i)
{
static Image* setcol;
Rectangle r;
Rectangle bar;
int y0, dy;
int ysz;
int l;
if (setcol == nil)
setcol = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x5555AAFF);
bar.max.x = r.max.x = i->r.max.x - Inset;
bar.min.x = r.min.x = r.max.x - 15;
r.min.y = i->r.min.y + Inset;
ysz = 90;
r.max.y = r.min.y + ysz;
if (r.max.y > i->r.max.y){
r.max.y = i->r.max.y;
ysz = Dy(r);
}
draw(i, r, cols[BACK], nil, ZP);
l = blocklen(p->blks);
if (l > 0){
y0 = ysz * p->froff / l;
dy = ysz * p->f.nchars / l;
if (dy < 3)
dy = 3;
} else {
y0 = 0;
dy = Dy(r);
}
bar.min.y = r.min.y + y0;
bar.max.y = bar.min.y + dy;
draw(i, bar, setcol, nil, ZP);
border(i, r, 1, cols[BORD], ZP);
}
/* To avoid blinking, we use double buffering during
* scroll operations. This is because we redraw the frame,
* and do not shift rectangles around.
*/
static Image*
scrdraw(Panel* p, Image* i)
{
Rectangle r;
assert(p->loaded);
r = Rpt(ZP, Pt(Dx(p->rect), Dy(p->rect)));
if (i == nil){
i = allocimage(display, r, screen->chan, 0, CBack);
}
draw(i, r, cols[BACK], nil, ZP);
frclear(&p->f, 0);
frinit(&p->f, r, p->font, i, cols);
filltext(p);
drawsel(p);
drawscrollbar(p, i);
draw(screen, p->rect, i, nil, ZP);
return i;
}
static void
scrdone(Panel* p, Image* i)
{
draw(i, Rpt(ZP, Pt(Dx(i->r), Dy(i->r))), cols[BACK], nil, ZP);
frclear(&p->f, 0);
p->loaded = 0;
freeimage(i);
xdraw(p, 0);
}
void
setsel(Panel* p, int ss, int se)
{
if (ss < se){
p->ss = ss;
p->se = se;
} else {
p->ss = se;
p->se = ss;
}
}
double
jumpscale(Panel* p, Point xy)
{
double dy;
double dc;
dy = xy.y - p->rect.min.y;
if (dy > p->rect.max.y - xy.y)
dy = p->rect.max.y - xy.y;
dc = blocklen(p->blks) - p->froff;
if (dc < p->froff)
dc = p->froff;
if (dy < 1)
dy = 1;
return dc / dy;
}
static int
undocmd(Panel* p)
{
int pos;
if (undo(p, &pos)){
dirty(p, hasedits(p), 1);
mustshow(p, pos);
return 1;
} else
return 0;
}
static int
redocmd(Panel* p)
{
int pos;
if (redo(p, &pos)){
dirty(p, hasedits(p), 1);
mustshow(p, pos);
return 1;
} else
return 0;
}
static int
nlines(Rune* r, int n)
{
int i, nl;
int lc;
lc = nl = 0;
for (i = 0; i < n; i++)
if (r[i] == '\n' || ++lc > 80){
nl++;
lc = 0;
}
return nl;
}
int
textins(Panel* p, Rune* r, int nr, int pos)
{
int rc;
rc = frameins(p, r, nr, pos);
p->nlines += nlines(r, nr);
event(p, "ins %11d %11d %.*S", pos, nr, nr, r);
return rc;
}
void
textdel(Panel* p, Rune* r, int nr, int pos)
{
if (nr > 0){
nr = framedel(p, r, nr, pos);
if (nr){
p->nlines -= nlines(r, nr);
event(p, "del %11d %11d", pos, nr);
}
}
}
static void
writepsel(Panel* p)
{
char* s;
if ( !(p->flags&Pline)) {
s = smprint("/devs%s", p->path);
writefstr("/dev/sel", s);
free(s);
}
}
static int
cut(Panel* p, int putsnarf)
{
char* s;
if (p->ss >= p->se)
return 0;
free(p->snarf);
p->nsnarf = p->se - p->ss;
p->snarf = emalloc9p((p->nsnarf+1) * sizeof(Rune));
hidesel(p);
textdel(p, p->snarf, p->nsnarf, p->ss);
p->se = p->s0 = p->ss;
drawsel(p);
filltext(p);
addedit(p, Del, p->snarf, p->nsnarf, p->ss);
if (putsnarf){
p->snarf[p->nsnarf] = 0;
s = smprint("%S", p->snarf);
writefstr("/dev/snarf", s);
free(s);
}
return 1;
}
static void
paste(Panel* p, int pos)
{
Rune* r;
char* s;
if (s = readfstr("/dev/snarf")){
r = utf2runes(s, nil, nil);
free(s);
free(p->snarf);
p->snarf = r;
p->nsnarf = runeslen(r);
if (p->nsnarf){
textins(p, p->snarf, p->nsnarf, pos);
p->s0 = p->ss = pos;
p->se = pos + p->nsnarf;
drawsel(p);
addedit(p, Ins, p->snarf, p->nsnarf, pos);
}
}
}
static void
run(Panel* p, int pos, int onsel)
{
Rune* r;
int s, e;
int n;
char* c;
char* ev;
r = gettextword(p, pos, &s, &e);
c = smprint("%S", r);
if (!wcommand(c)){
if (onsel)
ev = "args";
else
ev = "exec";
n = e - s;
event(p, "%s %11d %S", ev, runenlen(r, n), r);
}
free(c);
free(r);
}
static void
look(Panel* p, int pos)
{
Rune* r;
int s, e;
int n;
if (r = gettextword(p, pos, &s, &e)){
assert(e >= s);
p->ss = s;
p->se = e;
n = p->se - p->ss;
if (n > 0)
event(p, "look %11d %S", runenlen(r, n), r);
free(r);
}
}
static int
findnln(void* a, int , Rune r)
{
int c = (int)r;
int *np = a;
if (c == '\n' && --(*np) < 0)
return 1;
return 0;
}
/* :xx searches for line number. Else, literal match.
*/
static void
search(Panel* p, char* key)
{
Tblock* b;
int pos;
int epos;
int ln;
Rune* r;
Rune* rp;
b = p->blks;
packblock(b);
if (key[0] == ':' && isdigit(key[1])){
ln = strtod(key+1, nil) - 1;
if (ln < 0)
ln = 0;
pos = 0;
while(pos < b->nr && ln > 0 && findln(b, &pos)){
pos++;
ln--;
}
epos = pos + 1;
if (epos < b->nr && findln(b, &epos))
epos++;
} else {
r = utf2runes(key, nil, nil);
if (p->ss != p->se)
pos = p->ss + 1;
if (pos < b->nr)
rp = runestrstr(b->r + pos, r);
else
rp = nil;
if (rp == nil){
pos = 0;
rp = runestrstr(b->r + pos, r);
}
if (rp == nil)
epos = pos = 0;
else {
pos = rp - b->r;
epos = pos + runestrlen(r);
}
free(r);
}
if (pos || epos){
p->ss = pos;
p->se = epos;
mustshow(p, p->ss);
drawsel(p);
}
}
static void
xmouse12(Panel* p, Cmouse* m, Channel* mc)
{
int updated;
updated = 0;
for(;;){
switch(m->buttons){
case 0:
return;
case 3:
if (!updated++)
writepsel(p);
cut(p, 1);
if (p->flags&Pedit)
dirty(p, 1, 1);
else {
undo(p, nil);
p->ss = p->se = 0;
drawsel(p);
}
flushimage(display, 1);
do {
recv(mc, m);
} while (m->buttons == 3);
break;
case 5:
if (p->flags&Pedit)
if (undo(p, nil))
dirty(p, hasedits(p), 0);
flushimage(display, 1);
do {
recv(mc, m);
} while (m->buttons == 5);
break;
default:
recv(mc, m);
break;
}
}
}
static void
xmouse13(Panel* p, Cmouse* m, Channel* mc)
{
int updated;
updated = 0;
for(;;){
switch(m->buttons){
case 0:
return;
case 5:
if (p->flags&Pedit){
cut(p, 0);
paste(p, p->ss);
settextsize(p);
dirty(p, 1, 1);
}
if (!updated++)
writepsel(p);
flushimage(display, 1);
do {
recv(mc, m);
} while (m->buttons == 5);
break;
case 3:
if (p->flags&Pedit)
if (undo(p, nil))
dirty(p, hasedits(p), 0);
flushimage(display, 1);
do {
recv(mc, m);
} while (m->buttons == 3);
break;
default:
recv(mc, m);
break;
}
}
}
static void
xmouse1(Panel* p, Cmouse* m, Channel* mc, Point xy, int pos)
{
Rune* r;
int ss, se;
int updated;
int old;
if (m->flag == CMdouble){
r = gettextword(p, pos, &ss, &se);
if (r){
free(r);
p->ss = ss;
p->se = se;
writepsel(p);
}
} else
p->ss = p->se = pos;
p->s0 = p->ss;
drawsel(p);
flushimage(display, 1);
updated = 0;
old = -1;
for(;;){
recv(mc, m);
xy = m->xy;
panelok(p);
switch(m->buttons){
case 0:
return;
case 1: // slide
pos = p->froff + frcharofpt(&p->f, xy);
if (pos == old){
sleep(10);
} else {
old = pos;
setsel(p, p->s0, pos);
drawsel(p);
if (p->ss != p->se && !updated++)
writepsel(p);
flushimage(display, 1);
}
break;
case 3: // 1-2 chord
xmouse12(p, m, mc);
return;
case 5: // 1-3 chort
xmouse13(p, m, mc);
return;
default:
do {
recv(mc, m);
} while (m->buttons);
}
}
}
static void
xmouse2(Panel* p, Cmouse* m, Channel* mc, Point xy, int pos)
{
int updated;
int old;
updated = 0;
old = pos;
for(;;){
recv(mc, m);
xy = m->xy;
panelok(p);
switch(m->buttons){
case 0:
run(p, updated ? p->ss : pos, 0);
return;
case 3: // 2-1 chord
run(p, pos, 1);
return;
case 2:
if (!updated++)
p->ss = p->se = p->s0 = pos;
pos = p->froff + frcharofpt(&p->f, xy);
if (old == pos)
sleep(10);
else {
setsel(p, p->s0, pos);
drawsel(p);
flushimage(display, 1);
}
break;
}
}
}
static void
xmouse3(Panel* p, Cmouse* m, Channel* mc, Point xy, int pos)
{
double jfactor;
int jpos, jy, old;
Image* i;
recv(mc, m);
xy = m->xy;
switch(m->buttons){
case 0:
look(p, pos);
drawsel(p);
flushimage(display, 1);
break;
case 4: // slide
i = scrdraw(p, nil);
flushimage(display, 1);
jfactor = 0;
jy = xy.y;
old = p->froff;
do {
panelok(p);
if (jfactor == 0){
pos = p->froff;
jfactor = jumpscale(p, xy);
}
jpos = pos + (xy.y - jy) * jfactor;
if (jpos == old || !gotopos(p, jpos))
sleep(10);
else {
old = jpos;
scrdraw(p, i);;
flushimage(display, 1);
}
recv(mc, m);
xy = m->xy;
} while(m->buttons);
scrdone(p, i);
flushimage(display, 1);
break;
case 5: // 3-1 chord
pos = p->froff + frcharofpt(&p->f, xy);
if (pos < p->ss)
p->ss = pos;
else if (pos > p->se)
p->se = pos;
drawsel(p);
flushimage(display, 1);
do {
recv(mc, m);
} while (m->buttons);
break;
}
}
static void
xmouse(Panel* p, Cmouse* m, Channel* mc)
{
int pos;
Point xy;
panelok(p);
if ((p->flags&Pdead) || hidden(p->file)) // paranoia
return;
if (Dx(p->rect) <= 0 || Dy(p->rect) <= 0) // more paranoia
return;
xy = m->xy;
pos = p->froff + frcharofpt(&p->f, xy);
newedit(p);
switch(m->buttons){
case 1:
xmouse1(p, m, mc, xy, pos);
break;
case 2:
xmouse2(p, m, mc, xy, pos);
break;
case 4:
xmouse3(p, m, mc, xy, pos);
break;
}
}
static void
sendkeys(Panel* p, int ss, int se)
{
Tblock* b;
int n;
Rune* r;
if (se - ss <= 0)
return;
b = blockseek(p->blks, &n, ss);
r = emalloc9p((se - ss + 1) * sizeof(Rune));
blockget(b, n, se-ss, r);
event(p, "keys %S", r);
free(r);
}
static void
xkeyboard(Panel* p, Rune r)
{
int k = r;
Rune buf[2];
int pos;
int n;
if ((p->flags&Pdead) || hidden(p->file)) // paranoia
return;
if (Dx(p->rect) <= 10 || Dy(p->rect) <= 10) // more paranoia
return;
pos = p->ss;
switch(k){
case Kbs:
if (!(p->flags&Pedit)){
kbevent:
event(p, "keys %C", k);
return;
}
if (pos <= 0){
fdprint("top\n");
return;
}
if (!cut(p, 1) && pos){
pos--;
mustshow(p, pos);
textdel(p, buf, 1, pos);
p->ss = p->se = pos;
addedit(p, Del, buf, 1, pos);
}
dirty(p, 1, 1);
filltext(p);
break;
case 0x17: // ^W. erase word
case 0x15: // ^U. erase line
if (!(p->flags&Pedit))
goto kbevent;
break;
case Kdel:
event(p, "interrupt");
return;
case Kleft:
undocmd(p);
break;
case Kright:
redocmd(p);
break;
case Kup:
case Kdown:
if (p->flags&Pline)
goto kbevent;
else {
n = p->f.nlines / 3;
if (n < 2)
n = 2;
if (scrollframe(p, (k == Kup) ? -n : n))
xdraw(p, 0);
}
break;
case Kesc:
if (!(p->flags&Pedit))
goto kbevent;
setsel(p, p->s0, pos);
drawsel(p);
break;
case '\n':
if (p->flags&Pline){
setsel(p, p->s0, pos);
drawsel(p);
run(p, (pos>0 ? pos-1 : 0), 0);
break;
}
if (p->flags&Pedit)
event(p, "dirty");
// and fall...
default:
if (!(p->flags&Pedit))
goto kbevent;
cut(p, 1);
mustshow(p, pos);
if (textins(p, &r, 1, pos) && pos >= p->froff+p->f.nchars-2){
p->ss = p->se = pos+1;
scrollframe(p, gettextht(p)/3);
xdraw(p, 0);
} else
p->ss = p->se = pos+1;
addedit(p, Ins, &r, 1, pos);
dirty(p, 1, 1);
if ((p->flags&Pedit) && (r == '\n' || (p->flags&Pline)))
settextsize(p);
}
flushimage(display, 1);
}
static long
xread(Panel* p, void* buf, long cnt, vlong off)
{
char* s;
long l;
/* Would be better to have a real blockread here
* but this suffices.
*/
s = block2str(p->blks, &l);
cnt = genreadbuf(buf, cnt, off, s, l);
free(s);
return cnt;
}
static int
validtextpos(Panel* p, int pos)
{
int l;
l = blocklen(p->blks);
if (pos < 0)
pos = 0;
if (pos > l)
pos = l;
return pos;
}
static long
xwrite(Panel* p, void* buf, long cnt, vlong off)
{
int pos;
int n;
Rune* r;
int nr;
int nout;
Tblock* b;
Tblock* nb;
int nl;
char* pb;
/* Would be better to have a real blockwrite here
* but this suffices.
*/
assert(buf);
pb = buf;
if (off == 0LL)
p->npartial = 0;
if (p->npartial){
if (p->partialoff == off){
pb = emalloc9p(cnt + p->npartial);
memmove(pb, p->partial, p->npartial);
memmove(pb + p->npartial, buf, cnt);
} else {
p->npartial = 0;
p->partialoff = 0;
werrstr("partial rune");
return -1;
}
}
nout = p->npartial + cnt;
r = utf2runes(pb, &nout, &nl);
if (nout != p->npartial + cnt){
n = p->npartial + cnt - nout;
if (n >= UTFmax){ // binary file?
p->npartial = 0;
free(r);
if (pb != buf)
free(pb);
werrstr("binary file?");
return -1;
}
p->npartial = n;
memmove(p->partial, pb + nout, p->npartial);
p->partialoff = off + cnt;
} else {
p->npartial = 0;
p->partialoff = 0;
}
if (pb != buf)
free(pb);
nr = runeslen(r);
bdprint("data: %d runes [%.*S]\n", nr, nr, r);
if (off == 0LL){
pos = 0;
blockfree(p->blks);
p->blks = newblock(r, nr);
p->nlines = nl;
} else {
pos = off2pos(p->blks, (long)off);
b = blockseek(p->blks, &n, pos);
b->nr = n;
nb = newblock(r, nr);
blockcat(b, nb);
p->nlines += nl;
}
p->dfile->length = off+cnt;
assert(pos + nr == blocklen(p->blks));
pos = pos + nr;
p->mark = validtextpos(p, pos);
if (p->flags&Pline)
pos = validtextpos(p, pos + nr - 10);
p->ss = p->se = p->s0 = pos;
settextsize(p);
cleanedits(p);
if (!hidden(p->file)){
if (p->scroll)
mustshow(p, pos);
xdraw(p, 0);
flushimage(display, 1);
}
return cnt;
}
static void
xtruncate(Panel* p)
{
blockfree(p->blks);
p->blks = newblock(nil, 0);
p->nlines = 0;
p->npartial = 0;
p->mark = 0;
p->dfile->length= 0LL;
p->ss = p->se = p->s0 = 0;
if (!hidden(p->file)){
hidesel(p);
xdraw(p, 0);
flushimage(display, 1);
}
}
static int
textctlwr(Panel* p, char* s)
{
char* q;
char* r;
Rune* rs;
int ss, se;
int pos, n;
int nl;
int mustdraw;
mustdraw = 0;
if (!strncmp(s, "ins ", 4)){
pos = strtod(s + 4, &q);
pos = validtextpos(p, pos);
n = 0;
rs = utf2runes(s + 4 + 11 + 1 + 11 + 1, &n, &nl);
if (n>0){
frameins(p, rs, n, pos);
addedit(p, Ins, rs, n, pos);
p->nlines += nl;
}
free(rs);
} else if (!strncmp(s, "del ", 4)){
pos = strtod(s + 4, &q);
pos = validtextpos(p, pos);
n = strtod(q, &r);
if (n>0){
rs = emalloc9p(sizeof(Rune)*(n+1));
framedel(p, rs, n, pos);
addedit(p, Del, rs, n, pos);
p->nlines -= nlines(rs, n);
free(rs);
}
} else if (!strncmp(s, "sel ", 4)){
if (!strncmp(s + 4, "all", 3)){
ss = 0;
se = blocklen(p->blks);
} else {
ss = strtod(s + 4, &q);
se = strtod(q, &r);
ss = off2pos(p->blks, ss);
se = off2pos(p->blks, se);
ss = validtextpos(p, ss);
se = validtextpos(p, se);
}
if (se < ss)
se = ss;
p->s0 = p->ss = ss;
p->se = se;
drawsel(p);
} else if (!strncmp(s, "undo", 4)){
if (!undocmd(p))
return -1;
} else if (!strncmp(s, "redo", 4)){
if (!redocmd(p))
return -1;
} else if (!strncmp(s, "search ", 7)){
search(p, s+7);
} else if (!strcmp(s, "cut")){
if (cut(p, 1)){
settextsize(p);
dirty(p, 1, 1);
}
} else if (!strcmp(s, "paste")){
cut(p, 0);
paste(p, p->ss);
settextsize(p);
dirty(p, 1, 1);
} else if (!strncmp(s, "mark ", 5)){
p->mark = off2pos(p->blks, atoi(s + 5));
p->mark = validtextpos(p, p->mark);
} else if (!strncmp(s, "exec ", 5)){
if (!wcommand(s+5))
event(p, "exec %11d %s", strlen(s+5), s+5);
} else if (!strncmp(s, "look ", 5)){
event(p, "look %11d %s", strlen(s+5), s+5);
} else if (!strncmp(s, "scroll", 6))
p->scroll = 1;
else {
werrstr("bad ctl");
return -1;
}
setctlfilelen(p);
return mustdraw;
}
int
gettextwid(Panel* p)
{
return Dx(p->rect) / stringwidth(p->font, "X");
}
int
gettextht(Panel* p)
{
if (p->f.maxlines && p->loaded)
return p->f.maxlines;
else
return Dy(p->rect) / fontheight(p->font);
}
static long
xattrs(Panel* p, char* str, long l)
{
char sel[100];
seprint(sel, sel+sizeof(sel),
"mark %11ld\nsel %11ld %11ld\nsize %11d %11d\n%s",
pos2off(p->blks, p->mark),
pos2off(p->blks, p->ss), pos2off(p->blks, p->se),
gettextwid(p), gettextht(p),
(p->scroll ? "scroll\n" : ""));
return sprintattrs(p, str, l, sel);
}
static int
xctl(Panel* p, char* ctl)
{
int r;
int odirty;
Font* of;
of = p->font;
odirty = p->flags&Pdirty;
r = genctl(p, ctl);
if (r < 0)
r = textctlwr(p, ctl);
else {
if (odirty && !(p->flags&Pdirty))
setnoedits(p);
if (of != p->font){
if (p->loaded){
frclear(&p->f, 1);
frinit(&p->f, p->rect, p->font, screen, cols);
}
}
}
if (r >= 0 && !hidden(p->file) && (p->flags&Pline) && settextsize(p))
r = 1;
return r;
}
|