implement Wpanel;
include "mods.m";
mods, debug, win, tree: import dat;
Menu: import menus;
Cpointer, Tagwid, Taght, Inset, setcursor, drawtag,
getfont, readsnarf, cookclick, Arrow, Drag: import gui;
# Panel polymorphism support.
# Panels are only drawn due to user interaction or updates made. All that
# from the o/mero tree. This means that write and update (data/ctl) routines should never draw.
# They just set the Pdraw flag.
# Once the whole tree is updated, we call draw for those that must
# be redrawn.
# Also, mouse and keyboard handlers are called from within the tree, after
# synchronizing, thus it's safe to operate on the panel.
# However:
# Containers do not keep the panel busy in the mean time, thus they may call
# tree operations but may have races if not done with care.
# Atoms keep the panel busy while doing user I/O, thus they may NOT call
# tree operations (directly) but should not have races.
# Doing this right yet not creating one process per panel is hard.
# Perhaps we should have gone for Xfids.
panels: list of Pimpl;
cc: chan of (ref Panel, string);
init(d: Livedat, dir: string)
{
dat = d;
initmods();
fd := open(dir, OREAD);
if (fd == nil)
error(sprint("can't open %s: %r", dir));
(dirs, n) := readdir->readall(fd, Readdir->NONE);
if (n < 0)
error(sprint("reading %s: %r", dir));
panels = nil;
for (i := 0; i < n; i++){
nm := dirs[i].name;
l := len nm;
if (l > 7 && nm[0:3] == "owp" && nm[l-4:] == ".dis"){
path := dir + "/" + nm;
if (debug['P'])
fprint(stderr, "loading %s\n", path);
m := load Pimpl path;
if (m == nil)
fprint(stderr, "%s: %r\n", path);
else if ((e := m->init(d)) != nil)
fprint(stderr, "%s: %s\n", path, e);
else
panels = m :: panels;
}
}
if (debug['d'] || debug['P'])
fprint(stderr, "%d panels loaded\n", len panels);
}
screenname(s: string): int
{
for (i := 0; i < len s; i++)
if (s[i] == ':')
return 0;
return 1;
}
addchild(fp: ref Panel, p: ref Panel)
{
n := len fp.child;
child := array[n + 1] of ref Panel;
child[0:] = fp.child;
child[n] = p;
fp.child = child;
p.parent = fp;
}
nullpanel: Panel;
Panel.new(n: string, fp: ref Panel): ref Panel
{
pname := n;
if (screenname(n))
pname = "row:" + n;
for (l := panels; l != nil; l = tl l){
m := hd l;
for (prefs := m->prefixes; prefs != nil; prefs = tl prefs){
pl := len hd prefs;
if (len pname > pl && pname[0:pl] == hd prefs){
p := ref nullpanel;
p.impl = m;
p.name = n;
if (fp != nil){
addchild(fp, p);
p.path = names->rooted(fp.path, n);
if(fp.flags&Ptag)
p.depth = fp.depth + 1;
else
p.depth = fp.depth;
}
p.rowcol = Qatom;
p.init();
p.flags |= Predraw;
if (debug['P'])
fprint(stderr, "o/live: new %s\n", p.path);
return p;
}
}
}
return nil;
}
Panel.text(p: self ref Panel): string
{
flags := "-------------";
if (p.flags&Phide) flags[0] = 'h';
if (p.flags&Playout) flags[1] = 'l';
if (p.flags&Pedit) flags[2] = 'e';
if (p.flags&Ptag) flags[3] = 't';
if (p.flags&Pmore) flags[4] = 'm';
if (p.flags&Pdirties) flags[5] = 'd';
if (p.flags&Pdirty) flags[6] = 'd';
if (p.flags&Pline) flags[7] = '1';
if (p.flags&Ptbl) flags[8] = 't';
if (p.flags&Psync) flags[9] = 'f';
if (p.flags&Predraw) flags[10] = 'r';
if (p.flags&Pdead) flags[11]='!';
if (p.flags&Pbusy) flags[12] = 'b';
s := sprint("%s row=%d, flags %s %d childs %d shown", p.name, p.rowcol, flags, len p.child, p.nshown);
return s;
}
escape(s: string): string
{
for (i := 0; i < len s -1; i++)
if (s[i] == '\n')
s[i] = 1;
if (s[len s -1] != '\n' )
s[len s] = '\n';
return s;
}
unescape(s: string): string
{
for (i := 0; i < len s; i++)
if (s[i] == 1)
s[i] = '\n';
return s;
}
pfsctl(p: ref Panel, s: string)
{
fname := p.path + "/ctl";
fd := open(fname, OWRITE);
if (fd != nil){
if (debug['E'])
fprint(stderr, "fsctl: %s: %s\n", p.path, s);
seek(fd, big 0, 2);
data := array of byte escape(s);
if (write(fd, data, len data) != len data && debug['E'])
fprint(stderr, "fsctl: write: %r\n");
} else if (debug['E'])
fprint(stderr, "fsctl: open %s/ctl: %r\n", p.path);
}
fsctlproc()
{
for(;;){
(p, s) := <-cc;
pfsctl(p, s);
}
}
Panel.fsctl(p: self ref Panel, s: string, async: int)
{
if (!async){
pfsctl(p, s);
return;
}
if (cc == nil){
cc = chan[16] of (ref Panel, string);
spawn fsctlproc();
}
cc <-= (p, s);
}
Panel.init(p: self ref Panel)
{
if (p.impl == nil)
panic("nil panel implementation");
p.impl->pinit(p);
p.flags |= Predraw;
}
Panel.term(p: self ref Panel)
{
p.flags |= Pdead; # safety
if (debug['P'])
fprint(stderr, "o/live: gone %s\n", p.path);
p.impl->pterm(p);
}
# Process control operation, during update.
# Data is guaranteed to be updated. This is the update for ctl.
Panel.ctl(p: self ref Panel, s: string)
{
p.impl->pctl(p, s);
}
# update panel data, during update
Panel.update(p: self ref Panel, d: array of byte)
{
p.impl->pupdate(p, d);
}
# For panels that require incremental changes.
# Currently text ins/del events only.
Panel.event(p: self ref Panel, s: string)
{
p.impl->pevent(p, s);
}
Panel.draw(p: self ref Panel)
{
if (p.flags&Pshown)
p.impl->pdraw(p);
}
Panel.mouse(p: self ref Panel, m: ref Cpointer, cm: chan of ref Cpointer)
{
p.impl->pmouse(p, m, cm);
}
Panel.kbd(p: self ref Panel, r: int)
{
p.impl->pkbd(p, r);
}
Panel.sync(p: self ref Panel)
{
p.impl->psync(p);
}
intag(p: ref Panel, xy: Point): int
{
ht := Taght;
if (p.flags&Pmore)
ht *= 2;
r := Rect(p.rect.min, (p.rect.min.x+Tagwid, p.rect.min.y+Taght));
return r.contains(xy);
}
nth(l: list of string, n: int): string
{
for (i := 0; l != nil && i < n; i++)
l = tl l;
if (l != nil)
return hd l;
return nil;
}
|