xref: /inferno-os/appl/grid/lib/fbrowse.b (revision e45fa0eb0763b57d6fb0649c064bc3b95ccdea6c)
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