1implement FBrowse; 2 3# 4# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. 5# 6 7 8include "sys.m"; 9 sys : Sys; 10include "draw.m"; 11 draw: Draw; 12 Rect: import draw; 13include "tk.m"; 14 tk: Tk; 15include "tkclient.m"; 16 tkclient: Tkclient; 17include "readdir.m"; 18 readdir: Readdir; 19include "workdir.m"; 20include "sh.m"; 21 sh: Sh; 22include "grid/pathreader.m"; 23 reader: PathReader; 24include "grid/browser.m"; 25 browser: Browser; 26 Browse, Select, File, Parameter, 27 DESELECT, SELECT, TOGGLE: import browser; 28include "grid/fbrowse.m"; 29 30br: ref Browse; 31 32init(ctxt : ref Draw->Context, title, root, currdir: string): string 33{ 34 sys = load Sys Sys->PATH; 35 if (sys == nil) 36 badmod(Sys->PATH); 37 draw = load Draw Draw->PATH; 38 if (draw == nil) 39 badmod(Draw->PATH); 40 readdir = load Readdir Readdir->PATH; 41 if (readdir == nil) 42 badmod(Readdir->PATH); 43 tk = load Tk Tk->PATH; 44 if (tk == nil) 45 badmod(Tk->PATH); 46 tkclient = load Tkclient Tkclient->PATH; 47 if (tkclient == nil) 48 badmod(Tkclient->PATH); 49 tkclient->init(); 50 workdir := load Workdir Workdir->PATH; 51 if (workdir == nil) 52 badmod(Workdir->PATH); 53 sh = load Sh Sh->PATH; 54 if (sh == nil) 55 badmod(Sh->PATH); 56 browser = load Browser Browser->PATH; 57 if (browser == nil) 58 badmod(Browser->PATH); 59 browser->init(); 60 reader = load PathReader "$self"; 61 if (reader == nil) 62 sys->print("cannot load reader!\n"); 63 sys->pctl(sys->NEWPGRP, nil); 64 if (root == nil) 65 root = "/"; 66 sys->chdir(root); 67 if (currdir == nil) 68 currdir = workdir->init(); 69 if (root[len root - 1] != '/') 70 root[len root] = '/'; 71 if (currdir[len currdir - 1] != '/') 72 currdir[len currdir] = '/'; 73 74 (top, titlebar) := tkclient->toplevel(ctxt,"", title , tkclient->OK | tkclient->Appl); 75 browsechan := chan of string; 76 tk->namechan(top, browsechan, "browsechan"); 77 br = Browse.new(top, "browsechan", root, root, 2, reader); 78 br.addopened(File (root, nil), 1); 79 br.gotoselectfile(File (currdir, nil)); 80 for (ik := 0; ik < len mainscreen; ik++) 81 tkcmd(top,mainscreen[ik]); 82 butchan := chan of string; 83 tk->namechan(top, butchan, "butchan"); 84 85 tkcmd(top, "pack .f -fill both -expand 1; pack propagate . 0"); 86 tkcmd(top, ". configure -height 300 -width 300"); 87 88 tkcmd(top, "update"); 89 released := 1; 90 title = ""; 91 92 tkclient->onscreen(top, nil); 93 resize(top, ctxt.display.image); 94 tkclient->startinput(top, "kbd"::"ptr"::nil); 95 96 path: string; 97 98 main: for (;;) { 99 alt { 100 s := <-top.ctxt.kbd => 101 tk->keyboard(top, s); 102 s := <-top.ctxt.ptr => 103 tk->pointer(top, *s); 104 inp := <-browsechan => 105 (nil, lst) := sys->tokenize(inp, " \n\t"); 106 selected := br.getselected(1); 107 case hd lst { 108 "double1pane1" => 109 tkpath := hd tl lst; 110 file := br.getpath(tkpath); 111 br.defaultaction(lst, file); 112 (n, dir) := sys->stat(file.path); 113 if (n == -1 || dir.mode & sys->DMDIR) 114 break; 115 if ((len dir.name > 4 && dir.name[len dir.name - 4:] == ".dis") || 116 dir.mode & 8r111) 117 spawn send(butchan, "run "+tkpath); 118 else if (dir.mode & 8r222) 119 spawn send(butchan, "write "+tkpath); 120 else if (dir.mode & 8r444) 121 spawn send(butchan, "open "+tkpath); 122 * => 123 br.defaultaction(lst, nil); 124 } 125 if (!File.eq(selected, br.getselected(1))) 126 actionbutton(top, br.selected[1].file.path, br.selected[1].tkpath); 127 tkcmd(top, "update"); 128 inp := <-butchan => 129 (nil, lst) := sys->tokenize(inp, " \n\t"); 130 case hd lst { 131 "refresh" => 132 br.refresh(); 133 "shell" => 134 path = br.getselected(1).path; 135 if (path == nil) 136 sys->chdir(root); 137 else 138 sys->chdir(path); 139 sh->run(ctxt, "/dis/wm/sh.dis" :: nil); 140 141 "run" => 142 spawn run(ctxt, br.getselected(1).path); 143 "read" => 144 wtitle := tkcmd(top, hd tl lst+" cget text"); 145 spawn openfile(ctxt, br.getselected(1).path, wtitle,0); 146 "write" => 147 wtitle := tkcmd(top, hd tl lst+" cget text"); 148 spawn openfile(ctxt, br.getselected(1).path, wtitle,1); 149 } 150 tkcmd(top, "update"); 151 152 title = <-top.ctxt.ctl or 153 title = <-top.wreq or 154 title = <-titlebar => 155 if (title == "exit" || title == "ok") 156 break main; 157 e := tkclient->wmctl(top, title); 158 if (e != nil && e[0] == '!') 159 br.resize(); 160 } 161 } 162 if (title == "ok") 163 return br.getselected(1).path; 164 return ""; 165} 166 167send(chanout: chan of string, s: string) 168{ 169 chanout <-= s; 170} 171 172resize(top: ref Tk->Toplevel, img: ref Draw->Image) 173{ 174 if (img != nil) { 175 scw := img.r.dx(); 176 sch := img.r.dy(); 177 ww := int tkcmd(top, ". cget -width"); 178 wh := int tkcmd(top, ". cget -height"); 179 if (ww > scw) 180 tkcmd(top, ". configure -x 0 -width "+string scw); 181 if (wh > sch) 182 tkcmd(top, ". configure -y 0 -height "+string sch); 183 } 184} 185 186mainscreen := array[] of { 187 "frame .f", 188 "frame .f.ftop", 189 "button .f.ftop.bs -text {Shell} -command {send butchan shell} -font /fonts/charon/bold.normal.font", 190 "button .f.ftop.br -text {Refresh} -command {send butchan refresh} -font /fonts/charon/bold.normal.font", 191 "grid .f.ftop.bs .f.ftop.br -row 0", 192 "grid columnconfigure .f.ftop 2 -minsize 30", 193 "grid .f.ftop -row 0 -column 0 -pady 2 -sticky w", 194 "label .f.l -text { } -height 1 -bg red", 195 "grid .f.l -row 1 -sticky ew", 196 "grid .fbrowse -in .f -row 2 -column 0 -sticky nsew", 197 "grid rowconfigure .f 2 -weight 1", 198 "grid columnconfigure .f 0 -weight 1", 199 200 "bind .Wm_t <Button-1> +{focus .Wm_t}", 201 "bind .Wm_t.title <Button-1> +{focus .Wm_t}", 202 "focus .Wm_t", 203}; 204 205readpath(file: File): (array of ref sys->Dir, int) 206{ 207 (dirs, nil) := readdir->init(file.path, readdir->NAME | readdir->COMPACT); 208 return (dirs, 0); 209} 210 211run(ctxt: ref Draw->Context, file: string) 212{ 213 sys->pctl(sys->FORKNS | sys->NEWPGRP, nil); 214 sys->chdir(browser->prevpath(file)); 215 sh->run(ctxt, file :: nil); 216} 217 218openscr := array[] of { 219 "frame .f", 220 "scrollbar .f.sy -command {.f.t yview}", 221 "text .f.t -yscrollcommand {.f.sy set} -bg white -font /fonts/charon/plain.normal.font", 222 "pack .f.sy -side left -fill y", 223 "pack .f.t -fill both -expand 1", 224 "bind .Wm_t <Button-1> +{focus .Wm_t}", 225 "bind .Wm_t.title <Button-1> +{focus .Wm_t}", 226 "focus .f.t", 227}; 228 229fopensize := ("", ""); 230 231plumbing := array[] of { 232 ("bit", "wm/view"), 233 ("jpg", "wm/view"), 234}; 235 236freader(top: ref Tk->Toplevel, fd: ref sys->FD, sync: chan of int) 237{ 238 sync <-= sys->pctl(0,nil); 239 buf := array[8192] of byte; 240 for (;;) { 241 i := sys->read(fd, buf, len buf); 242 if (i < 1) 243 return; 244 s :=""; 245 for (j := 0; j < i; j++) { 246 c := int buf[j]; 247 if (c == '{' || c == '}') 248 s[len s] = '\\'; 249 s[len s] = c; 250 } 251 tk->cmd(top, ".f.t insert end {"+s+"}; update"); 252 } 253} 254 255openfile(ctxt: ref draw->Context, file, title: string, writeable: int) 256{ 257 ext := getext(file); 258 plumb := getplumb(ext); 259 if (plumb != nil) { 260 sh->run(ctxt, plumb :: file :: nil); 261 return; 262 } 263 button := tkclient->Appl; 264 if (writeable) 265 button = button | tkclient->OK; 266 (top, titlebar) := tkclient->toplevel(ctxt, "", title, button); 267 tkcmds(top, openscr); 268 tkcmd(top,"pack .f -fill both -expand 1"); 269 tkcmd(top,"pack propagate . 0"); 270 (w,h) := fopensize; 271 if (w != "" && h != "") 272 tkcmd(top, ". configure -width "+w+" -height "+h); 273 killpid := -1; 274 fd := sys->open(file, sys->OREAD); 275 if (fd != nil) { 276 sync := chan of int; 277 spawn freader(top, fd, sync); 278 killpid = <-sync; 279 } 280 tkcmd(top, "update"); 281 tkclient->onscreen(top, nil); 282 tkclient->startinput(top, "kbd"::"ptr"::nil); 283 main: for (;;) { 284 alt { 285 s := <-top.ctxt.kbd => 286 tk->keyboard(top, s); 287 s := <-top.ctxt.ptr => 288 tk->pointer(top, *s); 289 290 title = <-top.ctxt.ctl or 291 title = <-top.wreq or 292 title = <-titlebar => 293 if (title == "exit" || title == "ok") 294 break main; 295 tkclient->wmctl(top, title); 296 } 297 } 298 if (killpid != -1) 299 kill(killpid); 300 fopensize = (tkcmd(top, ". cget -width"), tkcmd(top, ". cget -height")); 301 if (title == "ok") { 302 (n, dir) := sys->stat(file); 303 if (n != -1) { 304 fd = sys->create(file, sys->OWRITE, dir.mode); 305 if (fd != nil) { 306 s := tkcmd(top, ".f.t get 1.0 end"); 307 sys->fprint(fd,"%s",s); 308 fd = nil; 309 } 310 } 311 } 312} 313 314badmod(path: string) 315{ 316 sys->print("FBrowse: failed to load: %s\n",path); 317 exit; 318} 319 320tkcmd(top: ref Tk->Toplevel, cmd: string): string 321{ 322 e := tk->cmd(top, cmd); 323 if (e != "" && e[0] == '!') 324 sys->print("Tk error: '%s': %s\n",cmd,e); 325 return e; 326} 327 328tkcmds(top: ref Tk->Toplevel, a: array of string) 329{ 330 for (j := 0; j < len a; j++) 331 tkcmd(top, a[j]); 332} 333 334nactionbuttons := 0; 335actionbutton(top: ref Tk->Toplevel, path, tkpath: string) 336{ 337 (n, dir) := sys->stat(path); 338 for (i := 0; i < nactionbuttons; i++) { 339 tkcmd(top, "grid forget .f.ftop.baction"+string i); 340 tkcmd(top, "destroy .f.ftop.baction"+string i); 341 } 342 if (path == nil || n == -1 || dir.mode & sys->DMDIR) { 343 nactionbuttons = 0; 344 return; 345 } 346 buttons : list of (string,string) = nil; 347 348 if (dir.mode & 8r222) 349 buttons = ("Open", "write "+tkpath) :: buttons; 350 else if (dir.mode & 8r444) 351 buttons = ("Open", "read "+tkpath) :: buttons; 352 if (len dir.name > 4 && dir.name[len dir.name - 4:] == ".dis" || dir.mode & 8r111) 353 buttons = ("Run", "run "+tkpath) :: buttons; 354 355 nactionbuttons = len buttons; 356 for (i = 0; i < nactionbuttons; i++) { 357 name := ".f.ftop.baction"+string i+" "; 358 (text,cmd) := hd buttons; 359 tkcmd(top, "button "+name+"-text {"+text+"} "+ 360 "-font /fonts/charon/bold.normal.font "+ 361 "-command {send butchan "+cmd+"}"); 362 tkcmd(top, "grid "+name+" -row 0 -column "+string (4+i)); 363 buttons = tl buttons; 364 } 365} 366 367getext(file: string): string 368{ 369 (nil, lst) := sys->tokenize(file, "."); 370 for (; tl lst != nil; lst = tl lst) 371 ; 372 return hd lst; 373} 374 375getplumb(ext: string): string 376{ 377 for (i := 0; i < len plumbing; i++) 378 if (ext == plumbing[i].t0) 379 return plumbing[i].t1; 380 return nil; 381} 382 383kill(pid: int) 384{ 385 if ((fd := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE)) != nil) 386 sys->fprint(fd, "kill"); 387} 388