ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/grid/lib/browser.b/
implement Browser;
#
# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved.
#
include "sys.m";
sys : Sys;
include "draw.m";
draw: Draw;
Rect: import draw;
include "tk.m";
tk: Tk;
include "tkclient.m";
tkclient: Tkclient;
include "./pathreader.m";
include "./browser.m";
entryheight := "";
init()
{
sys = load Sys Sys->PATH;
if (sys == nil)
badmod(Sys->PATH);
draw = load Draw Draw->PATH;
if (draw == nil)
badmod(Draw->PATH);
tk = load Tk Tk->PATH;
if (tk == nil)
badmod(Tk->PATH);
tkclient = load Tkclient Tkclient->PATH;
if (tkclient == nil)
badmod(Tkclient->PATH);
tkclient->init();
}
Browse.new(top: ref Tk->Toplevel, tkchanname, root, rlabel: string, nopanes: int, reader: PathReader): ref Browse
{
b : Browse;
b.top = top;
b.tkchan = tkchanname;
if (nopanes < 1 || nopanes > 2)
return nil;
b.nopanes = 2;
b.bgnorm = bgnorm;
b.bgselect = bgselect;
b.selected = array[2] of { * => Selected (File(nil, nil), nil) };
b.opened = (root, nil) :: nil;
if (root == nil)
return nil;
if (root[len root - 1] != '/')
root[len root] = '/';
b.pane0width = "2 3";
b.root = root;
b.rlabel = rlabel;
b.reader = reader;
b.pane1 = File (nil, "-123");
b.released = 1;
tkcmds(top, pane0scr);
tkcmds(top, pane1scr);
tkcmd(top, "bind .fbrowse.lmov <Button-1> {send "+b.tkchan+" movdiv %X}");
tkcmd(top, "label .fbrowse.l -text { } -anchor w -width 0" +
" -font /fonts/charon/plain.normal.font");
tkcmd(top, ".fbrowse.l configure -height "+tkcmd(top, ".fbrowse.l cget -height"));
tkcmd(top, "grid .fbrowse.l -row 0 -column 0 -sticky ew -pady 2 -columnspan 4");
rb := ref b;
rb.newroot(b.root, b.rlabel);
rb.changeview(nopanes);
setbrowsescrollr(rb);
return rb;
}
Browse.refresh(b: self ref Browse)
{
scrval := tkcmd(b.top, ".fbrowse.sy1 get");
p := isat(scrval, " ");
p1 := b.pane1;
b.newroot(b.root, b.rlabel);
setbrowsescrollr(b);
if (b.nopanes == 2)
popdirpane1(b, p1);
b.selectfile(1,DESELECT, File (nil, nil), nil);
b.selectfile(0,DESELECT, File (nil, nil), nil);
tkcmd(b.top, ".fbrowse.c1 yview moveto "+scrval[:p]+"; update");
}
bgnorm := "white";
bgselect := "#5555FF";
ft := " -font /fonts/charon/plain.normal.font";
fts := " -font /fonts/charon/plain.tiny.font";
ftb := " -font /fonts/charon/bold.normal.font";
Browse.gotoselectfile(b: self ref Browse, file: File): string
{
(dir, tkpath) := b.gotopath(file, 0);
if (tkpath == nil)
return nil;
# Select dir
tkpath += ".l";
if (dir.qid != nil)
tkpath += "Q" + dir.qid;
b.selectfile(0, SELECT, dir, tkpath);
# If it is a file, select the file too
if (!File.eq(file, dir)) {
slaves := tkcmd(b.top, "grid slaves .fbrowse.fl2");
(nil, lst) := sys->tokenize(slaves, " ");
for (; lst != nil; lst = tl lst) {
if (File.eq(file, *b.getpath(hd lst))) {
b.selectfile(1, SELECT, file, hd lst);
tkpath = hd lst;
break;
}
}
pane1see(b);
}
return tkpath;
}
pane1see(b: ref Browse)
{
f := b.selected[1].tkpath;
if (f == "")
return;
x1 := int tkcmd(b.top, f+" cget -actx") - int tkcmd(b.top, ".fbrowse.fl2 cget -actx");
y1 := int tkcmd(b.top, f+" cget -acty") - int tkcmd(b.top, ".fbrowse.fl2 cget -acty");
x2 := x1 + int tkcmd(b.top, f+" cget -actwidth");
y2 := y1 + int tkcmd(b.top, f+" cget -actheight");
tkcmd(b.top, sys->sprint(".fbrowse.c2 see %d %d %d %d", x1,y1,x2,y2));
}
Browse.opendir(b: self ref Browse, file: File, tkpath: string, action: int): int
{
curr := tkcmd(b.top, tkpath+".lp cget -text");
if ((action == OPEN || action == TOGGLE) && curr == "+") {
tkcmd(b.top, tkpath+".lp configure -text {-} -relief sunken");
popdirpane0(b, file, tkpath);
seeframe(b.top, tkpath);
b.addopened(file, 1);
setbrowsescrollr(b);
return 1;
}
else if ((action == CLOSE || action == TOGGLE) && curr == "-") {
tkcmd(b.top, tkpath+".lp configure -text {+} -relief raised");
slaves := tkcmd(b.top, "grid slaves "+tkpath+" -column 1");
p := isat(slaves, " ");
if (p != -1)
tkcmd(b.top, "destroy "+slaves[p:]);
slaves = tkcmd(b.top, "grid slaves "+tkpath+" -column 2");
if (slaves != "")
tkcmd(b.top, "destroy "+slaves);
b.addopened(file, 0);
setbrowsescrollr(b);
return 1;
}
return 0;
}
Browse.addopened(b: self ref Browse, file: File, add: int)
{
tmp : list of File = nil;
for (; b.opened != nil; b.opened = tl b.opened) {
dir := hd b.opened;
if (!File.eq(file, dir))
tmp = dir :: tmp;
}
if (add)
tmp = file :: tmp;
b.opened = tmp;
}
Browse.changeview(b: self ref Browse, nopanes: int)
{
if (b.nopanes == nopanes)
return;
# w := int tkcmd(b.top, ".fbrowse cget -actwidth");
# ws := int tkcmd(b.top, ".fbrowse.sy1 cget -width");
if (nopanes == 1) {
b.pane0width = tkcmd(b.top, ".fbrowse.c1 cget -actwidth") + " " +
tkcmd(b.top, ".fbrowse.c2 cget -actwidth");
tkcmd(b.top, "grid forget .fbrowse.sx2 .fbrowse.c2 .fbrowse.lmov");
tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight 0");
}
else {
(nil, wlist) := sys->tokenize(b.pane0width, " ");
tkcmd(b.top, "grid columnconfigure .fbrowse 1 -weight "+hd wlist);
tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight "+hd tl wlist);
tkcmd(b.top, "grid .fbrowse.sx2 -row 3 -column 3 -sticky ew");
tkcmd(b.top, "grid .fbrowse.c2 -row 2 -column 3 -sticky nsew");
tkcmd(b.top, "grid .fbrowse.lmov -row 2 -column 2 -rowspan 2 -sticky ns");
}
b.nopanes = nopanes;
}
Browse.selectfile(b: self ref Browse, pane, action: int, file: File, tkpath: string)
{
if (action == SELECT && b.selected[pane].tkpath == tkpath)
return;
if (b.selected[pane].tkpath != nil)
tk->cmd(b.top, b.selected[pane].tkpath+" configure -bg "+bgnorm);
if ((action == TOGGLE && b.selected[pane].tkpath == tkpath) || action == DESELECT) {
if (pane == 0)
popdirpane1(b, File (nil,nil));
b.selected[pane] = (File(nil, nil), nil);
return;
}
b.selected[pane] = (file, tkpath);
tkcmd(b.top, tkpath+" configure -bg "+bgselect);
if (pane == 0)
popdirpane1(b, file);
}
Browse.resize(b: self ref Browse)
{
p1 := b.pane1;
b.pane1 = File (nil, nil);
if (p1.path != "")
popdirpane1(b, p1);
if (b.selected[1].tkpath != nil) {
s := b.selected[1];
b.selectfile(1, DESELECT, s.file, s.tkpath);
b.selectfile(1, SELECT, s.file, s.tkpath);
}
}
setbrowsescrollr(b: ref Browse)
{
h := tkcmd(b.top, ".fbrowse.fl cget -height");
w := tkcmd(b.top, ".fbrowse.fl cget -width");
tkcmd(b.top, ".fbrowse.c1 configure -scrollregion {0 0 "+w+" "+h+"}");
if (b.nopanes == 2) {
h = tkcmd(b.top, ".fbrowse.fl2 cget -height");
w = tkcmd(b.top, ".fbrowse.fl2 cget -width");
tkcmd(b.top, ".fbrowse.c2 configure -scrollregion {0 0 "+w+" "+h+"}");
}
}
seeframe(top: ref Tk->Toplevel, frame: string)
{
x := int tkcmd(top, frame+" cget -actx") - int tkcmd(top, ".fbrowse.fl cget -actx");
y := int tkcmd(top, frame+" cget -acty") - int tkcmd(top, ".fbrowse.fl cget -acty");
w := int tkcmd(top, frame+" cget -width");
h := int tkcmd(top, frame+" cget -height");
wc := int tkcmd(top, ".fbrowse.c1 cget -width");
hc := int tkcmd(top, ".fbrowse.c1 cget -height");
if (w > wc)
w = wc;
if (h > hc)
h = hc;
tkcmd(top, sys->sprint(".fbrowse.c1 see %d %d %d %d",x,y,x+w,y+h));
}
# Goes to selected dir OR dir containing selected file
Browse.gotopath(b: self ref Browse, file: File, openfinal: int): (File, string)
{
tkpath := ".fbrowse.fl.f0";
path := b.root;
testqid := "";
testpath := "";
close : list of string;
trackbacklist : list of (string, list of string, list of string) = nil;
trackback := 0;
enddir := "";
if (file.path[len file.path - 1] != '/') {
# i.e. is not a directory
p := isatback(file.path, "/");
enddir = file.path[:p + 1];
}
if (enddir == path) {
if (!dircontainsfile(b, File (path, nil), file))
return (File (nil, nil), nil);
}
else {
for(;;) {
lst : list of string;
if (trackback) {
(path, lst, close) = hd trackbacklist;
trackbacklist = tl trackbacklist;
if (close != nil)
b.opendir(File (hd close, hd tl close), hd tl tl close, CLOSE);
trackback = 0;
}
else {
frames := tkcmd(b.top, "grid slaves "+tkpath+" -column 1");
(nil, lst) = sys->tokenize(frames, " ");
if (lst != nil)
lst = tl lst; # ignore first frame (name of parent dir);
}
found := 0;
hasdups := 1;
for (; lst != nil; lst = tl lst) {
testpath = path;
if (hasdups) {
labels := tkcmd(b.top, "grid slaves "+hd lst+" -row 0");
(nil, lst2) := sys->tokenize(labels, " ");
testpath += tkcmd(b.top, hd tl lst2+" cget -text") + "/";
testqid = getqidfromlabel(hd tl lst2);
if (testqid == nil)
hasdups = 0;
}
else
testpath += tkcmd(b.top, hd lst+".l cget -text") + "/";
if (len testpath <= len file.path && file.path[:len testpath] == testpath) {
opened := 0;
close = nil;
if (openfinal || testpath != file.path)
opened = b.opendir(File(testpath, testqid), hd lst, OPEN);
if (opened)
close = testpath :: testqid :: hd lst :: nil;
if (tl lst != nil && hasdups)
trackbacklist = (path, tl lst, close) :: trackbacklist;
tkpath = hd lst;
path = testpath;
found = 1;
break;
}
}
if (enddir != nil && path == enddir)
if (dircontainsfile(b, File(testpath, testqid), file))
break;
if (!found) {
if (trackbacklist == nil)
return (File (nil, nil), nil);
trackback = 1;
}
else if (testpath == file.path && testqid == file.qid)
break;
}
}
seeframe(b.top, tkpath);
dir := File (path, testqid);
popdirpane1(b, dir);
return (dir, tkpath);
}
dircontainsfile(b: ref Browse, dir, file: File): int
{
(files, hasdups) := b.reader->readpath(dir);
for (j := 0; j < len files; j++) {
if (files[j].name == file.path[len dir.path:] &&
(!hasdups || files[j].qid.path == big file.qid))
return 1;
}
return 0;
}
Browse.getpath(b: self ref Browse, f: string): ref File
{
if (len f < 11 || f[:11] != ".fbrowse.fl")
return nil;
(nil, lst) := sys->tokenize(f, ".");
lst = tl lst;
if (hd lst == "fl2") {
# i.e. is in pane 1
qid := getqidfromlabel(f);
return ref File (b.pane1.path + tk->cmd(b.top, f+" cget -text"), qid);
}
tkpath := ".fbrowse.fl.f0";
path := b.root;
lst = tl tl lst;
# sys->print("getpath: %s %s\n",tkpath, path);
qid := "";
for (; lst != nil; lst = tl lst) {
tkpath += "."+hd lst;
if ((hd lst)[0] == 'l') {
qid = getqidfromlabel(tkpath);
if (qid != nil)
qid = "Q" + qid;
if (len hd lst - len qid > 1)
path += tk->cmd(b.top, tkpath+" cget -text");
}
else if ((hd lst)[0] == 'f') {
qid = getqidfromframe(b,tkpath);
if (qid != nil)
qid = "Q"+qid;
path += tk->cmd(b.top, tkpath+".l"+qid+" cget -text") + "/";
}
# sys->print("getpath: %s %s\n",tkpath, path);
}
# Temporary hack!
if (qid != nil)
qid = qid[1:];
return ref File (path, qid);
}
setroot(b: ref Browse, rlabel, root: string)
{
b.root = root;
b.rlabel = rlabel;
makedir(b, File (root, nil), ".fbrowse.fl.f0", rlabel, "0");
tkcmd(b.top, "grid forget .fbrowse.fl.f0.lp");
}
getqidfromframe(b: ref Browse, frame: string): string
{
tmp := tkcmd(b.top, "grid slaves "+frame+" -row 0");
(nil, lst) := sys->tokenize(tmp, " \t\n");
if (lst == nil)
return nil;
return getqidfromlabel(hd tl lst);
}
getqidfromlabel(label: string): string
{
p := isatback(label, "Q");
if (p != -1)
return label[p+1:];
return nil;
}
popdirpane0(b: ref Browse, dir : File, frame: string)
{
(dirs, hasdups) := b.reader->readpath(dir);
for (i := 0; i < len dirs; i++) {
si := string i;
f : string;
dirqid := string dirs[i].qid.path;
if (!hasdups)
dirqid = nil;
if (dirs[i].mode & sys->DMDIR) {
f = frame + ".f"+si;
makedir(b, File (dir.path+dirs[i].name, dirqid), f, dirs[i].name, string (i+1));
}
else {
if (b.nopanes == 1) {
f = frame + ".l"+si;
makefile(b, f, dirs[i].name, string (i+1), dirqid);
}
}
}
dirs = nil;
}
isopened(b: ref Browse, dir: File): int
{
for (tmp := b.opened; tmp != nil; tmp = tl tmp) {
if (File.eq(hd tmp, dir))
return 1;
}
return 0;
}
makefile(b: ref Browse, f, name, row, qid: string)
{
if (qid != nil)
f += "Q" + qid;
bgcol := bgnorm;
# if (f == selected[0].t1)
# bgcol = bgselect;
p := isat(name, "\0");
if (p != -1) {
tkcmd(b.top, "label "+f+" -text {"+name[:p]+"} -bg "+bgcol+ft);
tkcmd(b.top, "label "+f+"b -text {"+name[p+1:]+"} -bg "+bgcol+ft);
tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -pady 2");
tkcmd(b.top, "grid "+f+"b -row "+row+" -column 2 -sticky w -pady 2");
tkcmd(b.top, "bind "+f+" <Button-2> {send "+b.tkchan+" but2pane1 "+f+"}");
tkcmd(b.top, "bind "+f+" <ButtonRelease-2> {send "+b.tkchan+" release}");
}
else {
tkcmd(b.top, "label "+f+" -text {"+name+"} -bg "+bgcol+ft);
tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -pady 2");
}
tkcmd(b.top, "bind "+f+" <Button-1> {send "+b.tkchan+" but1pane0 "+f+"}");
tkcmd(b.top, "bind "+f+" <ButtonRelease-1> {send "+b.tkchan+" release}");
tkcmd(b.top, "bind "+f+" <Button-2> {send "+b.tkchan+" but2pane0 "+f+"}");
tkcmd(b.top, "bind "+f+" <ButtonRelease-2> {send "+b.tkchan+" release}");
tkcmd(b.top, "bind "+f+" <Button-3> {send "+b.tkchan+" but3pane0 "+f+"}");
tkcmd(b.top, "bind "+f+" <ButtonRelease-3> {send "+b.tkchan+" release}");
}
Browse.defaultaction(b: self ref Browse, lst: list of string, rfile: ref File)
{
tkpath: string;
file: File;
if (len lst > 1) {
tkpath = hd tl lst;
if (len tkpath > 11 && tkpath[:11] == ".fbrowse.fl") {
if (rfile == nil)
file = *b.getpath(tkpath);
else
file = *rfile;
}
}
case hd lst {
"release" =>
b.released = 1;
"open" or "double1pane0" =>
if (file.path == b.root)
break;
if (b.released) {
b.selectfile(0, DESELECT, File(nil, nil), nil);
b.selectfile(1, DESELECT, File(nil, nil), nil);
b.opendir(file, prevframe(tkpath), TOGGLE);
b.selectfile(0, SELECT, file, tkpath);
b.released = 0;
}
"double1pane1" =>
b.gotoselectfile(file);
"but1pane0" =>
if (b.released) {
b.selectfile(1, DESELECT, File(nil, nil), nil);
b.selectfile(0, TOGGLE, file, tkpath);
b.released = 0;
}
"but1pane1" =>
if (b.released) {
b.selectfile(1, TOGGLE, file, tkpath);
b.released = 0;
}
"movdiv" =>
movdiv(b, int hd tl lst);
}
}
prevframe(tkpath: string): string
{
end := len tkpath;
for (;;) {
p := isatback(tkpath[:end], ".");
if (tkpath[p+1] == 'f')
return tkpath[:end];
end = p;
}
return nil;
}
makedir(b: ref Browse, dir: File, f, name, row: string)
{
bgcol := bgnorm;
if (f == ".fbrowse.fl.f0")
dir = File (b.root, nil);
# if (name == "")
# name = path;
if (dir.path[len dir.path - 1] != '/')
dir.path[len dir.path] = '/';
if (File.eq(dir, b.selected[0].file))
bgcol = bgselect;
tkcmd(b.top, "frame "+f+" -bg white");
label := f+".l";
if (dir.qid != nil)
label += "Q" + dir.qid;
tkcmd(b.top, "label "+label+" -text {"+name+"} -bg "+bgcol+ftb);
if (isopened(b, dir)) {
popdirpane0(b, dir, f);
tkcmd(b.top, "label "+f+".lp -text {-} -borderwidth 1 -relief sunken -height 8 -width 8"+fts);
}
else tkcmd(b.top, "label "+f+".lp -text {+} -borderwidth 1 -relief raised -height 8 -width 8"+fts);
tkcmd(b.top, "bind "+label+" <Button-1> {send "+b.tkchan+" but1pane0 "+label+"}");
tkcmd(b.top, "bind "+label+" <Double-Button-1> {send "+b.tkchan+" double1pane0 "+label+"}");
tkcmd(b.top, "bind "+label+" <ButtonRelease-1> {send "+b.tkchan+" release}");
tkcmd(b.top, "bind "+label+" <Button-3> {send "+b.tkchan+" but3pane0 "+label+"}");
tkcmd(b.top, "bind "+label+" <ButtonRelease-3> {send "+b.tkchan+" release}");
tkcmd(b.top, "bind "+label+" <Button-2> {send "+b.tkchan+" but2pane0 "+label+"}");
tkcmd(b.top, "bind "+label+" <ButtonRelease-2> {send "+b.tkchan+" release}");
tkcmd(b.top, "bind "+f+".lp <Button-1> {send "+b.tkchan+" open "+label+"}");
tkcmd(b.top, "bind "+f+".lp <ButtonRelease-1> {send "+b.tkchan+" release}");
tkcmd(b.top, "grid "+f+".lp -row 0 -column 0");
tkcmd(b.top, "grid "+label+" -row 0 -column 1 -sticky w -padx 5 -pady 2 -columnspan 2");
tkcmd(b.top, "grid "+f+" -row "+row+" -column 1 -sticky w -padx 5 -columnspan 2");
}
popdirpane1(b: ref Browse, dir: File)
{
# if (path == b.pane1.path && qid == b.pane1.qid)
# return;
b.pane1 = dir;
labelset(b, ".fbrowse.l", prevpath(dir.path+"/"));
if (b.nopanes == 1)
return;
tkcmd(b.top, "destroy .fbrowse.fl2; frame .fbrowse.fl2 -bg white");
tkcmd(b.top, ".fbrowse.c2 create window 0 0 -window .fbrowse.fl2 -anchor nw");
if (dir.path == nil) {
setbrowsescrollr(b);
return;
}
(dirs, hasdups) := b.reader->readpath(dir);
# if (path[len path - 1] == '/')
# path = path[:len path - 1];
# tkcmd(b.top, "label .fbrowse.fl2.l -text {"+path+"}");
row := 0;
col := 0;
tkcmd(b.top, ".fbrowse.c2 see 0 0");
ni := 0;
n := (int tkcmd(b.top, ".fbrowse.c2 cget -actheight")) / 21;
for (i := 0; i < len dirs; i++) {
f := ".fbrowse.fl2.l"+string ni;
if (hasdups)
f += "Q" + string dirs[i].qid.path;
name := dirs[i].name;
isdir := dirs[i].mode & sys->DMDIR;
if (isdir)
name[len name]= '/';
bgcol := bgnorm;
# Sort this out later
# if (path+"/"+name == selected[1].t0) {
# bgcol = bgselect;
# selected[1].t1 = f;
#}
tkcmd(b.top, "label "+f+" -text {"+name+"} -bg "+bgcol+ft);
tkcmd(b.top, "bind "+f+" <Double-Button-1> {send "+b.tkchan+" double1pane1 "+f+"}");
tkcmd(b.top, "bind "+f+" <Button-1> {send "+b.tkchan+" but1pane1 "+f+"}");
tkcmd(b.top, "bind "+f+" <ButtonRelease-1> {send "+b.tkchan+" release}");
tkcmd(b.top, "bind "+f+" <Button-3> {send "+b.tkchan+" but3pane1 "+f+" %X %Y}");
tkcmd(b.top, "bind "+f+" <ButtonRelease-3> {send "+b.tkchan+" release}");
tkcmd(b.top, "grid "+f+" -row "+string row+" -column "+string col+
" -sticky w -padx 10 -pady 2");
row++;
if (row >= n) {
row = 0;
col++;
}
ni++;
}
dirs = nil;
setbrowsescrollr(b);
}
pane0scr := array[] of {
"frame .fbrowse",
"scrollbar .fbrowse.sy1 -command {.fbrowse.c1 yview}",
"scrollbar .fbrowse.sx1 -command {.fbrowse.c1 xview} -orient horizontal",
"canvas .fbrowse.c1 -yscrollcommand {.fbrowse.sy1 set} -xscrollcommand {.fbrowse.sx1 set} -bg white -width 50 -height 20 -borderwidth 2 -relief sunken -xscrollincrement 10 -yscrollincrement 21",
"grid .fbrowse.sy1 -row 2 -column 0 -sticky ns -rowspan 2",
"grid .fbrowse.sx1 -row 3 -column 1 -sticky ew",
"grid .fbrowse.c1 -row 2 -column 1 -sticky nsew",
"grid rowconfigure .fbrowse 2 -weight 1",
"grid columnconfigure .fbrowse 1 -weight 2",
};
pane1scr := array[] of {
# ".fbrowse.c1 configure -width 146",
"frame .fbrowse.fl2 -bg white",
"label .fbrowse.fl2.l -text {}",
"scrollbar .fbrowse.sx2 -command {.fbrowse.c2 xview} -orient horizontal",
"label .fbrowse.lmov -text { } -relief sunken -borderwidth 2 -width 5",
"canvas .fbrowse.c2 -xscrollcommand {.fbrowse.sx2 set} -bg white -width 50 -height 20 -borderwidth 2 -relief sunken -xscrollincrement 10 -yscrollincrement 21",
".fbrowse.c2 create window 0 0 -window .fbrowse.fl2 -anchor nw",
"grid .fbrowse.sx2 -row 3 -column 3 -sticky ew",
"grid .fbrowse.c2 -row 2 -column 3 -sticky nsew",
"grid .fbrowse.lmov -row 2 -column 2 -rowspan 2 -sticky ns",
"grid columnconfigure .fbrowse 3 -weight 3",
};
Browse.newroot(b: self ref Browse, root, rlabel: string)
{
tk->cmd(b.top, "destroy .fbrowse.fl");
tkcmd(b.top, "frame .fbrowse.fl -bg white");
tkcmd(b.top, ".fbrowse.c1 create window 0 0 -window .fbrowse.fl -anchor nw");
b.pane1 = File (root, nil);
setroot(b, rlabel, root);
setbrowsescrollr(b);
}
Browse.showpath(b: self ref Browse, on: int)
{
if (on == b.showpathlabel)
return;
if (on) {
b.showpathlabel = 1;
if (b.pane1.path != nil)
labelset(b, ".fbrowse.l", prevpath(b.pane1.path+"/"));
}
else {
b.showpathlabel = 0;
tkcmd(b.top, ".fbrowse.l configure -text {}");
}
}
Browse.getselected(b: self ref Browse, pane: int): File
{
return b.selected[pane].file;
}
labelset(b: ref Browse, label, text: string)
{
if (!b.showpathlabel)
return;
if (text != nil) {
tmp := b.rlabel;
if (tmp[len tmp - 1] != '/')
tmp[len tmp] = '/';
text = tmp + text[len b.root:];
}
tkcmd(b.top, label + " configure -text {"+text+"}");
}
movdiv(b: ref Browse, x: int)
{
x1 := int tkcmd(b.top, ".fbrowse.lmov cget -actx");
x2 := x1 + int tkcmd(b.top, ".fbrowse.lmov cget -width");
diff := 0;
if (x < x1)
diff = x - x1;
if (x > x2)
diff = x - x2;
if (abs(diff) > 5) {
w1 := int tkcmd(b.top, ".fbrowse.c1 cget -actwidth");
w2 := int tkcmd(b.top, ".fbrowse.c2 cget -actwidth");
if (w1 + diff < 36)
diff = 36 - w1;
if (w2 - diff < 36)
diff = w2 - 36;
w1 += diff;
w2 -= diff;
# sys->print("w1: %d w2: %d\n",w1,w2);
tkcmd(b.top, "grid columnconfigure .fbrowse 1 -weight "+string w1);
tkcmd(b.top, "grid columnconfigure .fbrowse 3 -weight "+string w2);
}
}
dialog(ctxt: ref draw->Context, oldtop: ref Tk->Toplevel, butlist: list of string, title, msg: string): int
{
(top, titlebar) := tkclient->toplevel(ctxt, "", title, tkclient->Popup);
butchan := chan of string;
tk->namechan(top, butchan, "butchan");
tkcmd(top, "frame .f");
tkcmd(top, "label .f.l -text {"+msg+"} -font /fonts/charon/plain.normal.font");
tkcmd(top, "bind .Wm_t <Button-1> +{focus .}");
tkcmd(top, "bind .Wm_t.title <Button-1> +{focus .}");
l := len butlist;
tkcmd(top, "grid .f.l -row 0 -column 0 -columnspan "+string l+" -sticky w -padx 10 -pady 5");
i := 0;
for(; butlist != nil; butlist = tl butlist) {
si := string i;
tkcmd(top, "button .f.b"+si+" -text {"+hd butlist+"} "+
"-font /fonts/charon/plain.normal.font -command {send butchan "+si+"}");
tkcmd(top, "grid .f.b"+si+" -row 1 -column "+si+" -padx 5 -pady 5");
i++;
}
placement := "";
if (oldtop != nil) {
setcentre(oldtop, top);
placement = "exact";
}
tkcmd(top, "pack .f; update; focus .");
tkclient->onscreen(top, placement);
tkclient->startinput(top, "kbd"::"ptr"::nil);
for (;;) {
alt {
s := <-top.ctxt.kbd =>
tk->keyboard(top, s);
s := <-top.ctxt.ptr =>
tk->pointer(top, *s);
inp := <- butchan =>
tkcmd(oldtop, "focus .");
return int inp;
title = <-top.ctxt.ctl or
title = <-top.wreq or
title = <-titlebar =>
if (title == "exit") {
tkcmd(oldtop, "focus .");
return -1;
}
tkclient->wmctl(top, title);
}
}
}
######################## Select Functions #########################
setselectscrollr(s: ref Select, f: string)
{
h := tkcmd(s.top, f+" cget -height");
w := tkcmd(s.top, f+" cget -width");
tkcmd(s.top, ".fselect.c configure -scrollregion {0 0 "+w+" "+h+"}");
}
Select.setscrollr(s: self ref Select, fname: string)
{
frame := getframe(s, fname);
if (frame != nil)
setselectscrollr(s,frame.path);
}
Select.new(top: ref Tk->Toplevel, tkchanname: string): ref Select
{
s: Select;
s.top = top;
s.tkchan = tkchanname;
s.frames = nil;
s.currfname = nil;
s.currfid = nil;
tkcmds(top, selectscr);
if (entryheight == nil) {
tkcmd(top, "entry .fselect.test");
entryheight = " -height " + tkcmd(top, ".fselect.test cget -height");
tkcmd(top, "destroy .fselect.test");
}
for (i := 1; i < 4; i++)
tkcmd(top, "bind .fselect.c <ButtonRelease-"+string i+"> {send "+s.tkchan+" release}");
return ref s;
}
selectscr := array[] of {
"frame .fselect",
"scrollbar .fselect.sy -command {.fselect.c yview}",
"scrollbar .fselect.sx -command {.fselect.c xview} -orient horizontal",
"canvas .fselect.c -yscrollcommand {.fselect.sy set} -xscrollcommand {.fselect.sx set} -bg white -width 414 -borderwidth 2 -relief sunken -height 180 -xscrollincrement 10 -yscrollincrement 19",
"grid .fselect.sy -row 0 -column 0 -sticky ns -rowspan 2",
"grid .fselect.sx -row 1 -column 1 -sticky ew",
"grid .fselect.c -row 0 -column 1",
};
Select.addframe(s: self ref Select, fname, title: string)
{
if (isat(fname, " ") != -1)
return;
f := ".fselect.f"+fname;
tkcmd(s.top, "frame "+f+" -bg white");
if (title != nil){
tkcmd(s.top, "label "+f+".l -text {"+title+"} -bg white "+
"-font /fonts/charon/bold.normal.font; "+
"grid "+f+".l -row 0 -column 0 -columnspan 3 -sticky w");
}
fr: Frame;
fr.name = fname;
fr.path = f;
fr.selected = nil;
s.frames = ref fr :: s.frames;
}
getframe(s: ref Select, fname: string): ref Frame
{
for (tmp := s.frames; tmp != nil; tmp = tl tmp)
if ((hd tmp).name == fname)
return hd tmp;
return nil;
}
Select.delframe(s: self ref Select, fname: string)
{
if (s.currfname == fname) {
tkcmd(s.top, ".fselect.c delete " + s.currfid);
s.currfid = nil;
s.currfname = nil;
}
f := getframe(s,fname);
if (f != nil) {
tkcmd(s.top, "destroy "+f.path);
tmp: list of ref Frame = nil;
for (;s.frames != nil; s.frames = tl s.frames) {
if ((hd s.frames).name != fname)
tmp = hd s.frames :: tmp;
}
s.frames = tmp;
}
}
Select.showframe(s: self ref Select, fname: string)
{
if (s.currfid != nil)
tkcmd(s.top, ".fselect.c delete " + s.currfid);
f := getframe(s, fname);
if (f != nil) {
s.currfid = tkcmd(s.top, ".fselect.c create window 0 0 "+
"-window "+f.path+" -anchor nw");
s.currfname = fname;
}
}
Select.addselection(s: self ref Select, fname, text: string, lp: list of ref Parameter, allowdups: int): string
{
fr := getframe(s, fname);
if (fr == nil)
return nil;
f := fr.path;
if (!allowdups) {
slv := tkcmd(s.top, "grid slaves "+f+" -column 0");
(nil, slaves) := sys->tokenize(slv, " \t\n");
for (; slaves != nil; slaves = tl slaves) {
if (text == tkcmd(s.top, hd slaves+" cget -text"))
return nil;
}
}
font := " -font /fonts/charon/plain.normal.font";
fontb := " -font /fonts/charon/bold.normal.font";
(id, row) := newselected(s.top, f);
sid := string id;
label := f+".l"+sid;
tkcmd(s.top, "label "+label+" -text {"+text+"} -bg white"+entryheight+font);
gridpack := label+" ";
paramno := 0;
for (; lp != nil; lp = tl lp) {
spn := string paramno;
pframe := f+".f"+sid+"P"+spn;
tkcmd(s.top, "frame "+pframe+" -bg white");
pick p := hd lp {
ArgIn =>
tkp1 := pframe+".lA";
tkp2 := pframe+".eA";
tkcmd(s.top, "label "+tkp1+" -text {"+p.name+"} "+
"-bg white "+entryheight+fontb);
tkcmd(s.top, "entry "+tkp2+" -bg white -width 50 "+
"-borderwidth 1"+entryheight+font);
if (p.initval != nil)
tkcmd(s.top, tkp2+" insert end {"+p.initval+"}");
tkcmd(s.top, "grid "+tkp1+" "+tkp2+" -row 0");
IntIn =>
tkp1 := pframe+".sI";
tkp2 := pframe+".lI";
tkcmd(s.top, "scale "+tkp1+" -showvalue 0 -orient horizontal -height 20"+
" -from "+string p.min+" -to "+string p.max+" -command {send "+
s.tkchan+" scale "+tkp2+"}");
tkcmd(s.top, tkp1+" set "+string p.initval);
tkcmd(s.top, "label "+tkp2+" -text {"+string p.initval+"} "+
"-bg white "+entryheight+fontb);
tkcmd(s.top, "grid "+tkp1+" "+tkp2+" -row 0");
}
gridpack += " "+pframe;
paramno++;
}
tkcmd(s.top, "grid "+gridpack+" -row "+row+" -sticky w");
sendstr := " " + label + " %X %Y}";
tkcmd(s.top, "bind "+label+" <Double-Button-1> {send "+s.tkchan+" double1"+sendstr);
tkcmd(s.top, "bind "+label+" <Button-1> {send "+s.tkchan+" but1"+sendstr);
tkcmd(s.top, "bind "+label+" <ButtonRelease-1> {send "+s.tkchan+" release}");
tkcmd(s.top, "bind "+label+" <Button-2> {send "+s.tkchan+" but2"+sendstr);
tkcmd(s.top, "bind "+label+" <ButtonRelease-2> {send "+s.tkchan+" release}");
tkcmd(s.top, "bind "+label+" <Button-3> {send "+s.tkchan+" but3"+sendstr);
tkcmd(s.top, "bind "+label+" <ButtonRelease-3> {send "+s.tkchan+" release}");
setselectscrollr(s, f);
if (s.currfname == fname) {
y := int tkcmd(s.top, label+" cget -acty") -
int tkcmd(s.top, f+" cget -acty");
h := int tkcmd(s.top, label+" cget -height");
tkcmd(s.top, ".fselect.c see 0 "+string (h+y));
}
return label;
}
newselected(top: ref Tk->Toplevel, frame: string): (int, string)
{
(n, slaves) := sys->tokenize(tkcmd(top, "grid slaves "+frame+" -column 0"), " \t\n");
id := 0;
slaves = tl slaves; # Ignore Title
for (;;) {
if (isin(slaves, frame+".l"+string id))
id++;
else break;
}
return (id, string n);
}
isin(l: list of string, test: string): int
{
for(tmpl := l; tmpl != nil; tmpl = tl tmpl)
if (hd tmpl == test)
return 1;
return 0;
}
Select.delselection(s: self ref Select, fname, tkpath: string)
{
f := getframe(s, fname);
(row, nil) := getrowcol(s.top, tkpath);
slaves := tkcmd(s.top, "grid slaves "+f.path+" -row "+row);
# sys->print("row %s: deleting: %s\n",row,slaves);
tkcmd(s.top, "grid rowdelete "+f.path+" "+row);
tkcmd(s.top, "destroy "+slaves);
# Select the next one if the item deleted was selected
if (f.selected == tkpath) {
f.selected = nil;
for (;;) {
slaves = tkcmd(s.top, "grid slaves "+f.path+" -row "+row);
if (slaves != nil)
break;
r := (int row) - 1;
if (r < 1)
return;
row = string r;
}
(nil, lst) := sys->tokenize(slaves, " ");
if (lst != nil)
s.select(fname, hd lst, SELECT);
}
}
getrowcol(top: ref Tk->Toplevel, s: string): (string, string)
{
row := "";
col := "";
(nil, lst) := sys->tokenize(tkcmd(top, "grid info "+s), " \t\n");
for (; lst != nil; lst = tl lst) {
if (hd lst == "-row")
row = hd tl lst;
else if (hd lst == "-column")
col = hd tl lst;
}
return (row, col);
}
Select.select(s: self ref Select, fname, tkpath: string, action: int)
{
f := getframe(s, fname);
if (action == SELECT && f.selected == tkpath)
return;
if (f.selected != nil)
tkcmd(s.top, f.selected+" configure -bg "+bgnorm);
if ((action == TOGGLE && f.selected == tkpath) || action == DESELECT)
f.selected = nil;
else {
tkcmd(s.top, tkpath+" configure -bg "+bgselect);
f.selected = tkpath;
}
}
Select.defaultaction(s: self ref Select, lst: list of string)
{
case hd lst {
"but1" =>
s.select(s.currfname, hd tl lst, TOGGLE);
"scale" =>
tkcmd(s.top, hd tl lst+" configure -text {"+hd tl tl lst+"}");
}
}
Select.getselected(s: self ref Select, fname: string): string
{
f := getframe(s, fname);
return f.selected;
}
Select.getselection(s: self ref Select, fname: string): list of (string, list of ref Parameter)
{
retlist : list of (string, list of ref Parameter) = nil;
row := 1;
f := getframe(s, fname);
for (;;) {
slaves := tkcmd(s.top, "grid slaves "+f.path+" -row "+string (row++));
# sys->print("slaves: %s\n",slaves);
if (slaves == nil || slaves[0] == '!')
break;
(nil, lst) := sys->tokenize(slaves, " ");
tkpath := hd lst;
lst = tl lst;
lp : list of ref Parameter = nil;
for (; lst != nil; lst = tl lst) {
pslaves := tkcmd(s.top, "grid slaves "+hd lst);
(nil, plist) := sys->tokenize(pslaves, " ");
# sys->print("slaves of %s - hd plist: '%s'\n",hd lst, hd plist);
case (hd plist)[len hd plist - 3:] {
".eA" or ".lA" =>
argname := tkcmd(s.top, hd lst+".lA cget -text");
argval := tkcmd(s.top, hd lst+".eA get");
lp = ref Parameter.ArgOut(argname, argval) :: lp;
".sI" or ".lI" =>
val := int tkcmd(s.top, hd lst+".lI cget -text");
lp = ref Parameter.IntOut(val) :: lp;
}
}
retlist = (tkpath, lp) :: retlist;
}
return retlist;
}
Select.resize(s: self ref Select, width, height: int)
{
ws := int tkcmd(s.top, ".fselect.sy cget -width");
hs := int tkcmd(s.top, ".fselect.sx cget -height");
tkcmd(s.top, ".fselect.c configure -width "+string (width - ws - 8)+
" -height "+string (height - hs - 8));
f := getframe(s, s.currfname);
if (f != nil)
setselectscrollr(s, f.path);
tkcmd(s.top, "update");
}
File.eq(a,b: File): int
{
if (a.path != b.path || a.qid != b.qid)
return 0;
return 1;
}
######################## General Functions ########################
setcentre(top1, top2: ref Tk->Toplevel)
{
x1 := int tkcmd(top1, ". cget -actx");
y1 := int tkcmd(top1, ". cget -acty");
h1 := int tkcmd(top1, ". cget -height");
w1 := int tkcmd(top1, ". cget -width");
h2 := int tkcmd(top2, ".f cget -height");
w2 := int tkcmd(top2, ".f cget -width");
newx := (x1 + (w1 / 2)) - (w2/2);
newy := (y1 + (h1 / 2)) - (h2/2);
tkcmd(top2, ". configure -x "+string newx+" -y "+string newy);
}
abs(x: int): int
{
if (x < 0)
return -x;
return x;
}
prevpath(path: string): string
{
if (path == nil)
return nil;
p := isatback(path[:len path - 1], "/");
if (p == -1)
return nil;
return path[:p+1];
}
isat(s, test: string): int
{
if (len test > len s)
return -1;
for (i := 0; i < (1 + len s - len test); i++)
if (test == s[i:i+len test])
return i;
return -1;
}
isatback(s, test: string): int
{
if (len test > len s)
return -1;
for (i := len s - len test; i >= 0; i--)
if (test == s[i:i+len test])
return i;
return -1;
}
tkcmd(top: ref Tk->Toplevel, cmd: string): string
{
e := tk->cmd(top, cmd);
if (e != "" && e[0] == '!')
sys->print("Tk error: '%s': %s\n",cmd,e);
return e;
}
tkcmds(top: ref Tk->Toplevel, a: array of string)
{
for (j := 0; j < len a; j++)
tkcmd(top, a[j]);
}
badmod(path: string)
{
sys->print("Browser: failed to load: %s\n",path);
exit;
}