xref: /inferno-os/appl/acme/wind.b (revision 546783184a373a2dd59afef3affe029a6aedd701)
1implement Windowm;
2
3include "common.m";
4
5sys : Sys;
6utils : Utils;
7drawm : Draw;
8graph : Graph;
9gui : Gui;
10dat : Dat;
11bufferm : Bufferm;
12textm : Textm;
13filem : Filem;
14look : Look;
15scrl : Scroll;
16acme : Acme;
17
18sprint : import sys;
19FALSE, TRUE, XXX, Astring : import Dat;
20Reffont, reffont, Lock, Ref, button, modbutton : import dat;
21Point, Rect, Image : import drawm;
22min, max, error, warning, stralloc, strfree : import utils;
23font, draw : import graph;
24black, white, mainwin : import gui;
25Buffer : import bufferm;
26Body, Text, Tag : import textm;
27File : import filem;
28Xfid : import Xfidm;
29scrdraw : import scrl;
30tagcols, textcols : import acme;
31BORD : import Framem;
32
33init(mods : ref Dat->Mods)
34{
35	sys = mods.sys;
36	dat = mods.dat;
37	utils = mods.utils;
38	drawm = mods.draw;
39	graph = mods.graph;
40	gui = mods.gui;
41	textm = mods.textm;
42	filem = mods.filem;
43	bufferm = mods.bufferm;
44	look = mods.look;
45	scrl = mods.scroll;
46	acme = mods.acme;
47}
48
49winid : int;
50nullwin : Window;
51
52Window.init(w : self ref Window, clone : ref Window, r : Rect)
53{
54	r1, br : Rect;
55	f : ref File;
56	rf : ref Reffont;
57	rp : ref Astring;
58	nc : int;
59	dummy : ref File = nil;
60
61	c := w.col;
62	*w = nullwin;
63	w.col = c;
64	w.nopen = array[Dat->QMAX] of byte;
65	for (i := 0; i < Dat->QMAX; i++)
66		w.nopen[i] = byte 0;
67	w.qlock = Lock.init();
68	w.ctllock = Lock.init();
69	w.refx = Ref.init();
70	w.tag = textm->newtext();
71	w.tag.w = w;
72	w.body = textm->newtext();
73	w.body.w = w;
74	w.id = ++winid;
75	w.refx.inc();
76	w.ctlfid = ~0;
77	w.utflastqid = -1;
78	r1 = r;
79	r1.max.y = r1.min.y + font.height;
80	reffont.r.inc();
81	f = dummy.addtext(w.tag);
82	w.tag.init(f, r1, reffont, tagcols);
83	w.tag.what = Tag;
84	# tag is a copy of the contents, not a tracked image
85	if(clone != nil){
86		w.tag.delete(0, w.tag.file.buf.nc, TRUE);
87		nc = clone.tag.file.buf.nc;
88		rp = utils->stralloc(nc);
89		clone.tag.file.buf.read(0, rp, 0, nc);
90		w.tag.insert(0, rp.s, nc, TRUE, 0);
91		utils->strfree(rp);
92		rp = nil;
93		w.tag.file.reset();
94		w.tag.setselect(nc, nc);
95	}
96	r1 = r;
97	r1.min.y += font.height + 1;
98	if(r1.max.y < r1.min.y)
99		r1.max.y = r1.min.y;
100	f = nil;
101	if(clone != nil){
102		f = clone.body.file;
103		w.body.org = clone.body.org;
104		w.isscratch = clone.isscratch;
105		rf = Reffont.get(FALSE, FALSE, FALSE, clone.body.reffont.f.name);
106	}else
107		rf = Reffont.get(FALSE, FALSE, FALSE, nil);
108	f = f.addtext(w.body);
109	w.body.what = Body;
110	w.body.init(f, r1, rf, textcols);
111	r1.min.y -= 1;
112	r1.max.y = r1.min.y+1;
113	draw(mainwin, r1, tagcols[BORD], nil, (0, 0));
114	scrdraw(w.body);
115	w.r = r;
116	w.r.max.y = w.body.frame.r.max.y;
117	br.min = w.tag.scrollr.min;
118	br.max.x = br.min.x + button.r.dx();
119	br.max.y = br.min.y + button.r.dy();
120	draw(mainwin, br, button, nil, button.r.min);
121	w.filemenu = TRUE;
122	w.maxlines = w.body.frame.maxlines;
123	if(clone != nil){
124		w.dirty = clone.dirty;
125		w.body.setselect(clone.body.q0, clone.body.q1);
126		w.settag();
127	}
128}
129
130Window.reshape(w : self ref Window, r : Rect, safe : int) : int
131{
132	r1, br : Rect;
133	y : int;
134	b : ref Image;
135
136	r1 = r;
137	r1.max.y = r1.min.y + font.height;
138	y = r1.max.y;
139	if(!safe || !w.tag.frame.r.eq(r1)){
140		y = w.tag.reshape(r1);
141		b = button;
142		if(w.body.file.mod && !w.isdir && !w.isscratch)
143			b = modbutton;
144		br.min = w.tag.scrollr.min;
145		br.max.x = br.min.x + b.r.dx();
146		br.max.y = br.min.y + b.r.dy();
147		draw(mainwin, br, b, nil, b.r.min);
148	}
149	if(!safe || !w.body.frame.r.eq(r1)){
150		if(y+1+font.height > r.max.y){		# no body
151			r1.min.y = y;
152			r1.max.y = y;
153			w.body.reshape(r1);
154			w.r = r;
155			w.r.max.y = y;
156			return y;
157		}
158		r1 = r;
159		r1.min.y = y;
160		r1.max.y = y + 1;
161		draw(mainwin, r1, tagcols[BORD], nil, (0, 0));
162		r1.min.y = y + 1;
163		r1.max.y = r.max.y;
164		y = w.body.reshape(r1);
165		w.r = r;
166		w.r.max.y = y;
167		scrdraw(w.body);
168	}
169	w.maxlines = min(w.body.frame.nlines, max(w.maxlines, w.body.frame.maxlines));
170	return w.r.max.y;
171}
172
173Window.lock1(w : self ref Window, owner : int)
174{
175	w.refx.inc();
176	w.qlock.lock();
177	w.owner = owner;
178}
179
180Window.lock(w : self ref Window, owner : int)
181{
182	i : int;
183	f : ref File;
184
185	f = w.body.file;
186	for(i=0; i<f.ntext; i++)
187		f.text[i].w.lock1(owner);
188}
189
190Window.unlock(w : self ref Window)
191{
192	f := w.body.file;
193	#
194	# subtle: loop runs backwards to avoid tripping over
195	# winclose indirectly editing f.text and freeing f
196	# on the last iteration of the loop
197	#
198	for(i:=f.ntext-1; i>=0; i--){
199		w = f.text[i].w;
200		w.owner = 0;
201		w.qlock.unlock();
202		w.close();
203	}
204}
205
206Window.mousebut(w : self ref Window)
207{
208	graph->cursorset(w.tag.scrollr.min.add(w.tag.scrollr.max).div(2));
209}
210
211Window.dirfree(w : self ref Window)
212{
213	i : int;
214	dl : ref Dat->Dirlist;
215
216	if(w.isdir){
217		for(i=0; i<w.ndl; i++){
218			dl = w.dlp[i];
219			dl.r = nil;
220			dl = nil;
221		}
222	}
223	w.dlp = nil;
224	w.ndl = 0;
225}
226
227Window.close(w : self ref Window)
228{
229	i : int;
230
231	if(w.refx.dec() == 0){
232		w.dirfree();
233		w.tag.close();
234		w.body.close();
235		if(dat->activewin == w)
236			dat->activewin = nil;
237		for(i=0; i<w.nincl; i++)
238			w.incl[i] = nil;
239		w.incl = nil;
240		w.events = nil;
241		w = nil;
242	}
243}
244
245Window.delete(w : self ref Window)
246{
247	x : ref Xfid;
248
249	x = w.eventx;
250	if(x != nil){
251		w.nevents = 0;
252		w.events = nil;
253		w.eventx = nil;
254		x.c <-= Xfidm->Xnil;
255	}
256}
257
258Window.undo(w : self ref Window, isundo : int)
259{
260	body : ref Text;
261	i : int;
262	f : ref File;
263	v : ref Window;
264
265	if(w==nil)
266		return;
267	w.utflastqid = -1;
268	body = w.body;
269	(body.q0, body.q1) = body.file.undo(isundo, body.q0, body.q1);
270	body.show(body.q0, body.q1, TRUE);
271	f = body.file;
272	for(i=0; i<f.ntext; i++){
273		v = f.text[i].w;
274		v.dirty = (f.seq != v.putseq);
275		if(v != w){
276			v.body.q0 = v.body.frame.p0+v.body.org;
277			v.body.q1 = v.body.frame.p1+v.body.org;
278		}
279	}
280	w.settag();
281}
282
283Window.setname(w : self ref Window, name : string, n : int)
284{
285	t : ref Text;
286	v : ref Window;
287	i : int;
288
289	t = w.body;
290	if(t.file.name == name)
291		return;
292	w.isscratch = FALSE;
293	if(n>=6 && name[n-6:n] == "/guide")
294		w.isscratch = TRUE;
295	else if(n>=7 && name[n-7:n] == "+Errors")
296		w.isscratch = TRUE;
297	t.file.setname(name, n);
298	for(i=0; i<t.file.ntext; i++){
299		v = t.file.text[i].w;
300		v.settag();
301		v.isscratch = w.isscratch;
302	}
303}
304
305Window.typex(w : self ref Window, t : ref Text, r : int)
306{
307	i : int;
308
309	t.typex(r, w.echomode);
310	if(t.what == Body)
311		for(i=0; i<t.file.ntext; i++)
312			scrdraw(t.file.text[i]);
313	w.settag();
314}
315
316Window.cleartag(w : self ref Window)
317{
318	i, n : int;
319	r : ref Astring;
320
321	# w must be committed
322	n = w.tag.file.buf.nc;
323	r = utils->stralloc(n);
324	w.tag.file.buf.read(0, r, 0, n);
325	for(i=0; i<n; i++)
326		if(r.s[i]==' ' || r.s[i]=='\t')
327			break;
328	for(; i<n; i++)
329		if(r.s[i] == '|')
330			break;
331	if(i == n)
332		return;
333	i++;
334	w.tag.delete(i, n, TRUE);
335	utils->strfree(r);
336	r = nil;
337	w.tag.file.mod = FALSE;
338	if(w.tag.q0 > i)
339		w.tag.q0 = i;
340	if(w.tag.q1 > i)
341		w.tag.q1 = i;
342	w.tag.setselect(w.tag.q0, w.tag.q1);
343}
344
345Window.settag(w : self ref Window)
346{
347	i : int;
348	f : ref File;
349
350	f = w.body.file;
351	for(i=0; i<f.ntext; i++){
352		v := f.text[i].w;
353		if(v.col.safe || v.body.frame.maxlines>0)
354			v.settag1();
355	}
356}
357
358Window.settag1(w : self ref Window)
359{
360	ii, j, k, n, bar, dirty : int;
361	old : ref Astring;
362	new : string;
363	r : int;
364	b : ref Image;
365	q0, q1 : int;
366	br : Rect;
367
368	if(w.tag.ncache!=0 || w.tag.file.mod)
369		w.commit(w.tag);	# check file name; also can now modify tag
370	old = utils->stralloc(w.tag.file.buf.nc);
371	w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc);
372	for(ii=0; ii<w.tag.file.buf.nc; ii++)
373		if(old.s[ii]==' ' || old.s[ii]=='\t')
374			break;
375	if(old.s[0:ii] != w.body.file.name){
376		w.tag.delete(0, ii, TRUE);
377		w.tag.insert(0, w.body.file.name, len w.body.file.name, TRUE, 0);
378		strfree(old);
379		old = nil;
380		old = utils->stralloc(w.tag.file.buf.nc);
381		w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc);
382	}
383	new = w.body.file.name + " Del Snarf";
384	if(w.filemenu){
385		if(w.body.file.delta.nc>0 || w.body.ncache)
386			new += " Undo";
387		if(w.body.file.epsilon.nc > 0)
388			new += " Redo";
389		dirty = w.body.file.name != nil && (w.body.ncache || w.body.file.seq!=w.putseq);
390		if(!w.isdir && dirty)
391			new += " Put";
392	}
393	if(w.isdir)
394		new += " Get";
395	l := len w.body.file.name;
396	if(l >= 2 && w.body.file.name[l-2: ] == ".b")
397		new += " Limbo";
398	new += " |";
399	r = utils->strchr(old.s, '|');
400	if(r >= 0)
401		k = r+1;
402	else{
403		k = w.tag.file.buf.nc;
404		if(w.body.file.seq == 0)
405			new += " Look ";
406	}
407	if(new != old.s[0:k]){
408		n = k;
409		if(n > len new)
410			n = len new;
411		for(j=0; j<n; j++)
412			if(old.s[j] != new[j])
413				break;
414		q0 = w.tag.q0;
415		q1 = w.tag.q1;
416		w.tag.delete(j, k, TRUE);
417		w.tag.insert(j, new[j:], len new - j, TRUE, 0);
418		# try to preserve user selection
419		r = utils->strchr(old.s, '|');
420		if(r >= 0){
421			bar = r;
422			if(q0 > bar){
423				bar = utils->strchr(new, '|')-bar;
424				w.tag.q0 = q0+bar;
425				w.tag.q1 = q1+bar;
426			}
427		}
428	}
429	strfree(old);
430	old = nil;
431	new = nil;
432	w.tag.file.mod = FALSE;
433	n = w.tag.file.buf.nc+w.tag.ncache;
434	if(w.tag.q0 > n)
435		w.tag.q0 = n;
436	if(w.tag.q1 > n)
437		w.tag.q1 = n;
438	w.tag.setselect(w.tag.q0, w.tag.q1);
439	b = button;
440	if(!w.isdir && !w.isscratch && (w.body.file.mod || w.body.ncache))
441		b = modbutton;
442	br.min = w.tag.scrollr.min;
443	br.max.x = br.min.x + b.r.dx();
444	br.max.y = br.min.y + b.r.dy();
445	draw(mainwin, br, b, nil, b.r.min);
446}
447
448Window.commit(w : self ref Window, t : ref Text)
449{
450	r : ref Astring;
451	i : int;
452	f : ref File;
453
454	t.commit(TRUE);
455	f = t.file;
456	if(f.ntext > 1)
457		for(i=0; i<f.ntext; i++)
458			f.text[i].commit(FALSE);	# no-op for t
459	if(t.what == Body)
460		return;
461	r = utils->stralloc(w.tag.file.buf.nc);
462	w.tag.file.buf.read(0, r, 0, w.tag.file.buf.nc);
463	for(i=0; i<w.tag.file.buf.nc; i++)
464		if(r.s[i]==' ' || r.s[i]=='\t')
465			break;
466	if(r.s[0:i] != w.body.file.name){
467		dat->seq++;
468		w.body.file.mark();
469		w.body.file.mod = TRUE;
470		w.dirty = TRUE;
471		w.setname(r.s, i);
472		w.settag();
473	}
474	utils->strfree(r);
475	r = nil;
476}
477
478Window.addincl(w : self ref Window, r : string, n : int)
479{
480	{
481		(ok, d) := sys->stat(r);
482		if(ok < 0){
483			if(r[0] == '/')
484				raise "e";
485			(r, n) = look->dirname(w.body, r, n);
486			(ok, d) = sys->stat(r);
487			if(ok < 0)
488				raise "e";
489		}
490		if((d.mode&Sys->DMDIR) == 0){
491			warning(nil, sprint("%s: not a directory\n", r));
492			r = nil;
493			return;
494		}
495		w.nincl++;
496		owi := w.incl;
497		w.incl = array[w.nincl] of string;
498		w.incl[1:] = owi[0:w.nincl-1];
499		owi = nil;
500		w.incl[0] = r;
501		r = nil;
502	}
503	exception{
504		* =>
505			warning(nil, sprint("%s: %r\n", r));
506			r = nil;
507	}
508}
509
510Window.clean(w : self ref Window, conservative : int, exiting : int) : int	# as it stands, conservative is always TRUE
511{
512	if(w.isscratch || w.isdir)	# don't whine if it's a guide file, error window, etc.
513		return TRUE;
514	if((!conservative||exiting) && w.nopen[Dat->QWevent]>byte 0)
515		return TRUE;
516	if(w.dirty){
517		if(w.body.file.name != nil)
518			warning(nil, sprint("%s modified\n", w.body.file.name));
519		else{
520			if(w.body.file.buf.nc < 100)	# don't whine if it's too small
521				return TRUE;
522			warning(nil, "unnamed file modified\n");
523		}
524		w.dirty = FALSE;
525		return FALSE;
526	}
527	return TRUE;
528}
529
530Window.ctlprint(w : self ref Window, fonts : int) : string
531{
532	s := sprint("%11d %11d %11d %11d %11d ", w.id, w.tag.file.buf.nc,
533			w.body.file.buf.nc, w.isdir, w.dirty);
534	if(fonts)
535		return sprint("%s%11d %q %11d ", s, w.body.frame.r.dx(),
536			w.body.reffont.f.name, w.body.frame.maxtab);
537	return s;
538}
539
540Window.event(w : self ref Window, fmt : string)
541{
542	n : int;
543	x : ref Xfid;
544
545	if(w.nopen[Dat->QWevent] == byte 0)
546		return;
547	if(w.owner == 0)
548		error("no window owner");
549	n = len fmt;
550	w.events[len w.events] = w.owner;
551	w.events += fmt;
552	w.nevents += n+1;
553	x = w.eventx;
554	if(x != nil){
555		w.eventx = nil;
556		x.c <-= Xfidm->Xnil;
557	}
558}
559