xref: /inferno-os/appl/wm/brutus/mod.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Brutusext;
2
3# <Extension mod file>
4# For module descriptions (in book)
5
6Name:	con "Brutus mod";
7
8include "sys.m";
9	sys: Sys;
10
11include "draw.m";
12	draw: Draw;
13	Context, Font: import draw;
14
15include	"bufio.m";
16	bufio: Bufio;
17	Iobuf: import bufio;
18
19include "tk.m";
20	tk: Tk;
21
22include "tkclient.m";
23	tkclient: Tkclient;
24
25include "string.m";
26	S : String;
27
28include	"brutus.m";
29	Size8, Index, Roman, Italic, Bold, Type, NFONT, NSIZE: import Brutus;
30
31include	"brutusext.m";
32
33Mstring: adt
34{
35	s: string;
36	style: int;
37	indexed: int;
38	width: int;
39	next: cyclic ref Mstring;
40};
41
42fontname :=  array[NFONT] of {
43	"/fonts/lucidasans/unicode.7.font",
44	"/fonts/lucidasans/italiclatin1.7.font",
45	"/fonts/lucidasans/boldlatin1.7.font",
46	"/fonts/lucidasans/typelatin1.7.font",
47	};
48
49fontswitch :=  array[NFONT] of {
50	"\\fontseries{m}\\rmfamily ",
51	"\\itshape ",
52	"\\fontseries{b}\\rmfamily ",
53	"\\fontseries{mc}\\ttfamily ",
54	};
55
56fontref := array[NFONT] of ref Font;
57
58LEFTCHARS: con 45;
59LEFTPIX: con LEFTCHARS*7;	# 7 is width of lucidasans/typelatin1.7 chars
60
61init(s: Sys, d: Draw, b: Bufio, t: Tk, w: Tkclient)
62{
63	sys = s;
64	draw = d;
65	bufio = b;
66	tk = t;
67	tkclient = w;
68	S = load String String->PATH;
69}
70
71create(parent: string, t: ref Tk->Toplevel, name, args: string): string
72{
73	(spec, err) := getspec(parent, args);
74	if(err != nil)
75		return err;
76	n := len spec;
77	if(n == 0)
78		return "empty spec";
79	d := t.image.display;
80	for(i:=0; i < NFONT; i++) {
81		if(i == Bold || fontref[i] != nil)
82			continue;
83		fontref[i] = Font.open(d, fontname[i]);
84		if(fontref[i] == nil)
85			return sys->sprint("can't open font %s: %r\n", fontname[i]);
86	}
87	(nil, nil, rw, nil) := measure(spec, 1);
88	lw := LEFTPIX;
89	wd := lw + rw;
90	fnt := fontref[Roman];
91	ht := n * fnt.height;
92	err = tk->cmd(t, "canvas " + name + " -width " + string wd
93			+ " -height " + string ht
94			+ " -font " + fontname[Type]);
95	if(len err > 0 && err[0] == '!')
96		return "problem creating canvas";
97	y := 0;
98	xl := 0;
99	xr := lw;
100	for(l := spec; l != nil; l = tl l) {
101		(lm, rm) := hd l;
102		canvmstring(t, name, lm, xl, y);
103		canvmstring(t, name, rm, xr, y);
104		y += fnt.height;
105	}
106	tk->cmd(t, "update");
107	return "";
108}
109
110canvmstring(t: ref Tk->Toplevel, canv: string, m: ref Mstring, x, y: int)
111{
112	# assume fonts all have same ascent
113	while(m != nil) {
114		pos := string x + " " + string y;
115		font := "";
116		if(m.style != Type)
117			font = " -font " + fontname[m.style];
118		e := tk->cmd(t, canv + " create text " + pos + " -anchor nw "
119			+ font + " -text '" + m.s);
120		x += m.width;
121		m = m.next;
122	}
123}
124
125getspec(parent, args: string) : (list of (ref Mstring, ref Mstring), string)
126{
127	(n, argl) := sys->tokenize(args, " ");
128	if(n != 1)
129		return (nil, "usage: " + Name + " file");
130	b := bufio->open(fullname(parent, hd argl), Sys->OREAD);
131	if(b == nil)
132		return (nil, sys->sprint("can't open %s, the error was: %r", hd argl));
133	mm : list of (ref Mstring, ref Mstring) = nil;
134	for(;;) {
135		s := b.gets('\n');
136		if(s == "")
137			break;
138		(nf, fl) := sys->tokenize(s, "	");
139		if(nf == 0)
140			mm = (nil, nil) :: mm;
141		else {
142			sleft := "";
143			sright := "";
144			if(nf == 1) {
145				f := hd fl;
146				if(s[0] == '\t')
147					sright = f;
148				else
149					sleft = f;
150			}
151			else {
152				sleft = hd fl;
153				sright = hd tl fl;
154			}
155			mm = (tom(sleft, Type, Roman, 1), tom(sright, Italic, Type, 0)) :: mm;
156		}
157	}
158	ans : list of (ref Mstring, ref Mstring) = nil;
159	while(mm != nil) {
160		ans = hd mm :: ans;
161		mm = tl mm;
162	}
163	return (ans, "");
164}
165
166tom(str: string, defstyle, altstyle, doindex: int) : ref Mstring
167{
168	if(str == "")
169		return nil;
170	if(str[len str - 1] == '\n')
171		str = str[0: len str - 1];
172	if(str == "")
173		return nil;
174	style := defstyle;
175	if(str[0] == '|')
176		style = altstyle;
177	(nil, l) := sys->tokenize(str, "|");
178	dummy := ref Mstring;
179	last := dummy;
180	if(doindex && l != nil && S->prefix("  ", hd l))
181		doindex = 0;	# continuation line
182	while(l != nil) {
183		s := hd l;
184		m : ref Mstring;
185		if(doindex && style == defstyle) {
186			# index 'words' in defstyle, but not past : or (
187			(sl,sr) := S->splitl(s, ":(");
188			while(sl != nil) {
189				a : string;
190				(a,sl) = S->splitl(sl, "a-zA-Z");
191				if(a != "") {
192					m = ref Mstring(a, style, 0, 0, nil);
193					last.next = m;
194					last = m;
195				}
196				if(sl != "") {
197					b : string;
198					(b,sl) = S->splitl(sl, "^a-zA-Z0-9_");
199					if(b != "") {
200						m = ref Mstring(b, style, 1, 0, nil);
201						last.next = m;
202						last = m;
203					}
204				}
205			}
206			if(sr != "") {
207				m = ref Mstring(sr, style, 0, 0, nil);
208				last.next = m;
209				last = m;
210				doindex = 0;
211			}
212		}
213		else {
214			m = ref Mstring(s, style, 0, 0, nil);
215			last.next = m;
216			last = m;
217		}
218		l = tl l;
219		if(style == defstyle)
220			style = altstyle;
221		else
222			style = defstyle;
223	}
224	return dummy.next;
225}
226
227measure(spec: list of (ref Mstring, ref Mstring), pixels: int) : (int, ref Mstring,  int, ref Mstring)
228{
229	maxl := 0;
230	maxr := 0;
231	maxlm : ref Mstring = nil;
232	maxrm : ref Mstring = nil;
233	while(spec != nil) {
234		(lm, rm) := hd spec;
235		spec = tl spec;
236		(maxl, maxlm) = measuremax(lm, maxl, maxlm, pixels);
237		(maxr, maxrm) = measuremax(rm, maxr, maxrm, pixels);
238	}
239	return (maxl, maxlm, maxr, maxrm);
240}
241
242measuremax(m: ref Mstring, maxw: int, maxm: ref Mstring, pixels: int) : (int, ref Mstring)
243{
244	w := 0;
245	for(mm := m; mm != nil; mm = mm.next) {
246		if(pixels)
247			mm.width = fontref[mm.style].width(mm.s);
248		else
249			mm.width = len mm.s;
250		w += mm.width;
251	}
252	if(w > maxw) {
253		maxw = w;
254		maxm = m;
255	}
256	return (maxw, maxm);
257}
258
259cook(parent: string, nil: int, args: string): (ref Celem, string)
260{
261	(spec, err) := getspec(parent, args);
262	if(err != nil)
263		return (nil, err);
264	(nil, maxlm, nil, nil) := measure(spec, 0);
265	ans := fontce(Roman);
266	tail := specialce("\\begin{tabbing}\\hspace{3in}\\=\\kill\n");
267	tail = add(ans, nil, tail);
268	for(l := spec; l != nil; l = tl l) {
269		(lm, rm) := hd l;
270		tail = cookmstring(ans, tail, lm, 1);
271		tail = add(ans, tail, specialce("\\>"));
272		tail = cookmstring(ans, tail, rm, 0);
273		tail = add(ans, tail, specialce("\\\\\n"));
274	}
275	add(ans, tail, specialce("\\end{tabbing}"));
276	return (ans, "");
277}
278
279cookmstring(par, tail: ref Celem, m: ref Mstring, doindex: int) : ref Celem
280{
281	s := "";
282	if(m == nil)
283		return tail;
284	while(m != nil) {
285		e := fontce(m.style);
286		te := textce(m.s);
287		add(e, nil, te);
288		if(doindex && m.indexed) {
289			ie := ref Celem(Index, nil, nil, nil, nil, nil);
290			add(ie, nil, e);
291			e = ie;
292		}
293		tail = add(par, tail, e);
294		m = m.next;
295	}
296	return tail;
297}
298
299specialce(s: string) : ref Celem
300{
301	return ref Celem(Special, s, nil, nil, nil, nil);
302}
303
304textce(s: string) : ref Celem
305{
306	return ref Celem(Text, s, nil, nil, nil, nil);
307}
308
309fontce(sty: int) : ref Celem
310{
311	return ref Celem(sty*NSIZE+Size8, nil, nil, nil, nil, nil);
312}
313
314add(par, tail: ref Celem, e: ref Celem) : ref Celem
315{
316	if(tail == nil) {
317		par.contents = e;
318		e.parent = par;
319	}
320	else
321		tail.next = e;
322	e.prev = tail;
323	return e;
324}
325
326fullname(parent, file: string): string
327{
328	if(len parent==0 || (len file>0 && (file[0]=='/' || file[0]=='#')))
329		return file;
330
331	for(i:=len parent-1; i>=0; i--)
332		if(parent[i] == '/')
333			return parent[0:i+1] + file;
334	return file;
335}
336