implement FBrowse; # # 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 "readdir.m"; readdir: Readdir; include "workdir.m"; include "sh.m"; sh: Sh; include "grid/pathreader.m"; reader: PathReader; include "grid/browser.m"; browser: Browser; Browse, Select, File, Parameter, DESELECT, SELECT, TOGGLE: import browser; include "grid/fbrowse.m"; br: ref Browse; init(ctxt : ref Draw->Context, title, root, currdir: string): string { sys = load Sys Sys->PATH; if (sys == nil) badmod(Sys->PATH); draw = load Draw Draw->PATH; if (draw == nil) badmod(Draw->PATH); readdir = load Readdir Readdir->PATH; if (readdir == nil) badmod(Readdir->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(); workdir := load Workdir Workdir->PATH; if (workdir == nil) badmod(Workdir->PATH); sh = load Sh Sh->PATH; if (sh == nil) badmod(Sh->PATH); browser = load Browser Browser->PATH; if (browser == nil) badmod(Browser->PATH); browser->init(); reader = load PathReader "$self"; if (reader == nil) sys->print("cannot load reader!\n"); sys->pctl(sys->NEWPGRP, nil); if (root == nil) root = "/"; sys->chdir(root); if (currdir == nil) currdir = workdir->init(); if (root[len root - 1] != '/') root[len root] = '/'; if (currdir[len currdir - 1] != '/') currdir[len currdir] = '/'; (top, titlebar) := tkclient->toplevel(ctxt,"", title , tkclient->OK | tkclient->Appl); browsechan := chan of string; tk->namechan(top, browsechan, "browsechan"); br = Browse.new(top, "browsechan", root, root, 2, reader); br.addopened(File (root, nil), 1); br.gotoselectfile(File (currdir, nil)); for (ik := 0; ik < len mainscreen; ik++) tkcmd(top,mainscreen[ik]); butchan := chan of string; tk->namechan(top, butchan, "butchan"); tkcmd(top, "pack .f -fill both -expand 1; pack propagate . 0"); tkcmd(top, ". configure -height 300 -width 300"); tkcmd(top, "update"); released := 1; title = ""; tkclient->onscreen(top, nil); resize(top, ctxt.display.image); tkclient->startinput(top, "kbd"::"ptr"::nil); path: string; main: for (;;) { alt { s := <-top.ctxt.kbd => tk->keyboard(top, s); s := <-top.ctxt.ptr => tk->pointer(top, *s); inp := <-browsechan => (nil, lst) := sys->tokenize(inp, " \n\t"); selected := br.getselected(1); case hd lst { "double1pane1" => tkpath := hd tl lst; file := br.getpath(tkpath); br.defaultaction(lst, file); (n, dir) := sys->stat(file.path); if (n == -1 || dir.mode & sys->DMDIR) break; if ((len dir.name > 4 && dir.name[len dir.name - 4:] == ".dis") || dir.mode & 8r111) spawn send(butchan, "run "+tkpath); else if (dir.mode & 8r222) spawn send(butchan, "write "+tkpath); else if (dir.mode & 8r444) spawn send(butchan, "open "+tkpath); * => br.defaultaction(lst, nil); } if (!File.eq(selected, br.getselected(1))) actionbutton(top, br.selected[1].file.path, br.selected[1].tkpath); tkcmd(top, "update"); inp := <-butchan => (nil, lst) := sys->tokenize(inp, " \n\t"); case hd lst { "refresh" => br.refresh(); "shell" => path = br.getselected(1).path; if (path == nil) sys->chdir(root); else sys->chdir(path); sh->run(ctxt, "/dis/wm/sh.dis" :: nil); "run" => spawn run(ctxt, br.getselected(1).path); "read" => wtitle := tkcmd(top, hd tl lst+" cget text"); spawn openfile(ctxt, br.getselected(1).path, wtitle,0); "write" => wtitle := tkcmd(top, hd tl lst+" cget text"); spawn openfile(ctxt, br.getselected(1).path, wtitle,1); } tkcmd(top, "update"); title = <-top.ctxt.ctl or title = <-top.wreq or title = <-titlebar => if (title == "exit" || title == "ok") break main; e := tkclient->wmctl(top, title); if (e != nil && e[0] == '!') br.resize(); } } if (title == "ok") return br.getselected(1).path; return ""; } send(chanout: chan of string, s: string) { chanout <-= s; } resize(top: ref Tk->Toplevel, img: ref Draw->Image) { if (img != nil) { scw := img.r.dx(); sch := img.r.dy(); ww := int tkcmd(top, ". cget -width"); wh := int tkcmd(top, ". cget -height"); if (ww > scw) tkcmd(top, ". configure -x 0 -width "+string scw); if (wh > sch) tkcmd(top, ". configure -y 0 -height "+string sch); } } mainscreen := array[] of { "frame .f", "frame .f.ftop", "button .f.ftop.bs -text {Shell} -command {send butchan shell} -font /fonts/charon/bold.normal.font", "button .f.ftop.br -text {Refresh} -command {send butchan refresh} -font /fonts/charon/bold.normal.font", "grid .f.ftop.bs .f.ftop.br -row 0", "grid columnconfigure .f.ftop 2 -minsize 30", "grid .f.ftop -row 0 -column 0 -pady 2 -sticky w", "label .f.l -text { } -height 1 -bg red", "grid .f.l -row 1 -sticky ew", "grid .fbrowse -in .f -row 2 -column 0 -sticky nsew", "grid rowconfigure .f 2 -weight 1", "grid columnconfigure .f 0 -weight 1", "bind .Wm_t +{focus .Wm_t}", "bind .Wm_t.title +{focus .Wm_t}", "focus .Wm_t", }; readpath(file: File): (array of ref sys->Dir, int) { (dirs, nil) := readdir->init(file.path, readdir->NAME | readdir->COMPACT); return (dirs, 0); } run(ctxt: ref Draw->Context, file: string) { sys->pctl(sys->FORKNS | sys->NEWPGRP, nil); sys->chdir(browser->prevpath(file)); sh->run(ctxt, file :: nil); } openscr := array[] of { "frame .f", "scrollbar .f.sy -command {.f.t yview}", "text .f.t -yscrollcommand {.f.sy set} -bg white -font /fonts/charon/plain.normal.font", "pack .f.sy -side left -fill y", "pack .f.t -fill both -expand 1", "bind .Wm_t +{focus .Wm_t}", "bind .Wm_t.title +{focus .Wm_t}", "focus .f.t", }; fopensize := ("", ""); plumbing := array[] of { ("bit", "wm/view"), ("jpg", "wm/view"), }; freader(top: ref Tk->Toplevel, fd: ref sys->FD, sync: chan of int) { sync <-= sys->pctl(0,nil); buf := array[8192] of byte; for (;;) { i := sys->read(fd, buf, len buf); if (i < 1) return; s :=""; for (j := 0; j < i; j++) { c := int buf[j]; if (c == '{' || c == '}') s[len s] = '\\'; s[len s] = c; } tk->cmd(top, ".f.t insert end {"+s+"}; update"); } } openfile(ctxt: ref draw->Context, file, title: string, writeable: int) { ext := getext(file); plumb := getplumb(ext); if (plumb != nil) { sh->run(ctxt, plumb :: file :: nil); return; } button := tkclient->Appl; if (writeable) button = button | tkclient->OK; (top, titlebar) := tkclient->toplevel(ctxt, "", title, button); tkcmds(top, openscr); tkcmd(top,"pack .f -fill both -expand 1"); tkcmd(top,"pack propagate . 0"); (w,h) := fopensize; if (w != "" && h != "") tkcmd(top, ". configure -width "+w+" -height "+h); killpid := -1; fd := sys->open(file, sys->OREAD); if (fd != nil) { sync := chan of int; spawn freader(top, fd, sync); killpid = <-sync; } tkcmd(top, "update"); tkclient->onscreen(top, nil); tkclient->startinput(top, "kbd"::"ptr"::nil); main: for (;;) { alt { s := <-top.ctxt.kbd => tk->keyboard(top, s); s := <-top.ctxt.ptr => tk->pointer(top, *s); title = <-top.ctxt.ctl or title = <-top.wreq or title = <-titlebar => if (title == "exit" || title == "ok") break main; tkclient->wmctl(top, title); } } if (killpid != -1) kill(killpid); fopensize = (tkcmd(top, ". cget -width"), tkcmd(top, ". cget -height")); if (title == "ok") { (n, dir) := sys->stat(file); if (n != -1) { fd = sys->create(file, sys->OWRITE, dir.mode); if (fd != nil) { s := tkcmd(top, ".f.t get 1.0 end"); sys->fprint(fd,"%s",s); fd = nil; } } } } badmod(path: string) { sys->print("FBrowse: failed to load: %s\n",path); exit; } 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]); } nactionbuttons := 0; actionbutton(top: ref Tk->Toplevel, path, tkpath: string) { (n, dir) := sys->stat(path); for (i := 0; i < nactionbuttons; i++) { tkcmd(top, "grid forget .f.ftop.baction"+string i); tkcmd(top, "destroy .f.ftop.baction"+string i); } if (path == nil || n == -1 || dir.mode & sys->DMDIR) { nactionbuttons = 0; return; } buttons : list of (string,string) = nil; if (dir.mode & 8r222) buttons = ("Open", "write "+tkpath) :: buttons; else if (dir.mode & 8r444) buttons = ("Open", "read "+tkpath) :: buttons; if (len dir.name > 4 && dir.name[len dir.name - 4:] == ".dis" || dir.mode & 8r111) buttons = ("Run", "run "+tkpath) :: buttons; nactionbuttons = len buttons; for (i = 0; i < nactionbuttons; i++) { name := ".f.ftop.baction"+string i+" "; (text,cmd) := hd buttons; tkcmd(top, "button "+name+"-text {"+text+"} "+ "-font /fonts/charon/bold.normal.font "+ "-command {send butchan "+cmd+"}"); tkcmd(top, "grid "+name+" -row 0 -column "+string (4+i)); buttons = tl buttons; } } getext(file: string): string { (nil, lst) := sys->tokenize(file, "."); for (; tl lst != nil; lst = tl lst) ; return hd lst; } getplumb(ext: string): string { for (i := 0; i < len plumbing; i++) if (ext == plumbing[i].t0) return plumbing[i].t1; return nil; } kill(pid: int) { if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil) sys->fprint(fd, "kill"); }