ref: a920c765f2b4130590fb5971a50690b21664957a
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; }