code: purgatorio

ref: e59ffed426f628794d4669f152eff9a6239b99db
dir: /appl/acme/wind.b/

View raw version
implement Windowm;

include "common.m";

sys : Sys;
utils : Utils;
drawm : Draw;
graph : Graph;
gui : Gui;
dat : Dat;
bufferm : Bufferm;
textm : Textm;
filem : Filem;
look : Look;
scrl : Scroll;
acme : Acme;

sprint : import sys;
FALSE, TRUE, XXX, Astring : import Dat;
Reffont, reffont, Lock, Ref, button, modbutton : import dat;
Point, Rect, Image : import drawm;
min, max, error, warning, stralloc, strfree : import utils;
font, draw : import graph;
black, white, mainwin : import gui;
Buffer : import bufferm;
Body, Text, Tag : import textm;
File : import filem;
Xfid : import Xfidm;
scrdraw : import scrl;
tagcols, textcols : import acme;
BORD : import Framem;

init(mods : ref Dat->Mods)
{
	sys = mods.sys;
	dat = mods.dat;
	utils = mods.utils;
	drawm = mods.draw;
	graph = mods.graph;
	gui = mods.gui;
	textm = mods.textm;
	filem = mods.filem;
	bufferm = mods.bufferm;
	look = mods.look;
	scrl = mods.scroll;
	acme = mods.acme;
}

winid : int;
nullwin : Window;

Window.init(w : self ref Window, clone : ref Window, r : Rect)
{
	r1, br : Rect;
	f : ref File;
	rf : ref Reffont;
	rp : ref Astring;
	nc : int;
	dummy : ref File = nil;

	c := w.col;
	*w = nullwin;
	w.col = c;
	w.nopen = array[Dat->QMAX] of byte;
	for (i := 0; i < Dat->QMAX; i++)
		w.nopen[i] = byte 0;
	w.qlock = Lock.init();
	w.ctllock = Lock.init();
	w.refx = Ref.init();
	w.tag = textm->newtext();
	w.tag.w = w;
	w.body = textm->newtext();
	w.body.w = w;
	w.id = ++winid;
	w.refx.inc();
	w.ctlfid = ~0;
	w.utflastqid = -1;
	r1 = r;
	r1.max.y = r1.min.y + font.height;
	reffont.r.inc();
	f = dummy.addtext(w.tag);
	w.tag.init(f, r1, reffont, tagcols);
	w.tag.what = Tag;
	# tag is a copy of the contents, not a tracked image 
	if(clone != nil){
		w.tag.delete(0, w.tag.file.buf.nc, TRUE);
		nc = clone.tag.file.buf.nc;
		rp = utils->stralloc(nc);
		clone.tag.file.buf.read(0, rp, 0, nc);
		w.tag.insert(0, rp.s, nc, TRUE, 0);
		utils->strfree(rp);
		rp = nil;
		w.tag.file.reset();
		w.tag.setselect(nc, nc);
	}
	r1 = r;
	r1.min.y += font.height + 1;
	if(r1.max.y < r1.min.y)
		r1.max.y = r1.min.y;
	f = nil;
	if(clone != nil){
		f = clone.body.file;
		w.body.org = clone.body.org;
		w.isscratch = clone.isscratch;
		rf = Reffont.get(FALSE, FALSE, FALSE, clone.body.reffont.f.name);
	}else
		rf = Reffont.get(FALSE, FALSE, FALSE, nil);
	f = f.addtext(w.body);
	w.body.what = Body;
	w.body.init(f, r1, rf, textcols);
	r1.min.y -= 1;
	r1.max.y = r1.min.y+1;
	draw(mainwin, r1, tagcols[BORD], nil, (0, 0));
	scrdraw(w.body);
	w.r = r;
	w.r.max.y = w.body.frame.r.max.y;
	br.min = w.tag.scrollr.min;
	br.max.x = br.min.x + button.r.dx();
	br.max.y = br.min.y + button.r.dy();
	draw(mainwin, br, button, nil, button.r.min);
	w.filemenu = TRUE;
	w.maxlines = w.body.frame.maxlines;
	if(clone != nil){
		w.dirty = clone.dirty;
		w.body.setselect(clone.body.q0, clone.body.q1);
		w.settag();
	}
}

Window.reshape(w : self ref Window, r : Rect, safe : int) : int
{
	r1, br : Rect;
	y : int;
	b : ref Image;

	r1 = r;
	r1.max.y = r1.min.y + font.height;
	y = r1.max.y;
	if(!safe || !w.tag.frame.r.eq(r1)){
		y = w.tag.reshape(r1);
		b = button;
		if(w.body.file.mod && !w.isdir && !w.isscratch)
			b = modbutton;
		br.min = w.tag.scrollr.min;
		br.max.x = br.min.x + b.r.dx();
		br.max.y = br.min.y + b.r.dy();
		draw(mainwin, br, b, nil, b.r.min);
	}
	if(!safe || !w.body.frame.r.eq(r1)){
		if(y+1+font.height > r.max.y){		# no body 
			r1.min.y = y;
			r1.max.y = y;
			w.body.reshape(r1);
			w.r = r;
			w.r.max.y = y;
			return y;
		}
		r1 = r;
		r1.min.y = y;
		r1.max.y = y + 1;
		draw(mainwin, r1, tagcols[BORD], nil, (0, 0));
		r1.min.y = y + 1;
		r1.max.y = r.max.y;
		y = w.body.reshape(r1);
		w.r = r;
		w.r.max.y = y;
		scrdraw(w.body);
	}
	w.maxlines = min(w.body.frame.nlines, max(w.maxlines, w.body.frame.maxlines));
	return w.r.max.y;
}

Window.lock1(w : self ref Window, owner : int)
{
	w.refx.inc();
	w.qlock.lock();
	w.owner = owner;
}

Window.lock(w : self ref Window, owner : int)
{
	i : int;
	f : ref File;

	f = w.body.file;
	for(i=0; i<f.ntext; i++)
		f.text[i].w.lock1(owner);
}

Window.unlock(w : self ref Window)
{
	f := w.body.file;
	#
	# subtle: loop runs backwards to avoid tripping over
	# winclose indirectly editing f.text and freeing f
	# on the last iteration of the loop
	#
	for(i:=f.ntext-1; i>=0; i--){
		w = f.text[i].w;
		w.owner = 0;
		w.qlock.unlock();
		w.close();
	}
}

Window.mousebut(w : self ref Window)
{
	graph->cursorset(w.tag.scrollr.min.add(w.tag.scrollr.max).div(2));
}

Window.dirfree(w : self ref Window)
{
	i : int;
	dl : ref Dat->Dirlist;

	if(w.isdir){
		for(i=0; i<w.ndl; i++){
			dl = w.dlp[i];
			dl.r = nil;
			dl = nil;
		}
	}
	w.dlp = nil;
	w.ndl = 0;
}

Window.close(w : self ref Window)
{
	i : int;

	if(w.refx.dec() == 0){
		w.dirfree();
		w.tag.close();
		w.body.close();
		if(dat->activewin == w)
			dat->activewin = nil;
		for(i=0; i<w.nincl; i++)
			w.incl[i] = nil;
		w.incl = nil;
		w.events = nil;
		w = nil;
	}
}

Window.delete(w : self ref Window)
{
	x : ref Xfid;

	x = w.eventx;
	if(x != nil){
		w.nevents = 0;
		w.events = nil;
		w.eventx = nil;
		x.c <-= Xfidm->Xnil;
	}
}

Window.undo(w : self ref Window, isundo : int)
{
	body : ref Text;
	i : int;
	f : ref File;
	v : ref Window;

	if(w==nil)
		return;
	w.utflastqid = -1;
	body = w.body;
	(body.q0, body.q1) = body.file.undo(isundo, body.q0, body.q1);
	body.show(body.q0, body.q1, TRUE);
	f = body.file;
	for(i=0; i<f.ntext; i++){
		v = f.text[i].w;
		v.dirty = (f.seq != v.putseq);
		if(v != w){
			v.body.q0 = v.body.frame.p0+v.body.org;
			v.body.q1 = v.body.frame.p1+v.body.org;
		}
	}
	w.settag();
}

Window.setname(w : self ref Window, name : string, n : int)
{
	t : ref Text;
	v : ref Window;
	i : int;

	t = w.body;
	if(t.file.name == name)
		return;
	w.isscratch = FALSE;
	if(n>=6 && name[n-6:n] == "/guide")
		w.isscratch = TRUE;
	else if(n>=7 && name[n-7:n] == "+Errors")
		w.isscratch = TRUE;
	t.file.setname(name, n);
	for(i=0; i<t.file.ntext; i++){
		v = t.file.text[i].w;
		v.settag();
		v.isscratch = w.isscratch;
	}
}

Window.typex(w : self ref Window, t : ref Text, r : int)
{
	i : int;

	t.typex(r, w.echomode);
	if(t.what == Body)
		for(i=0; i<t.file.ntext; i++)
			scrdraw(t.file.text[i]);
	w.settag();
}

Window.cleartag(w : self ref Window)
{
	i, n : int;
	r : ref Astring;

	# w must be committed 
	n = w.tag.file.buf.nc;
	r = utils->stralloc(n);
	w.tag.file.buf.read(0, r, 0, n);
	for(i=0; i<n; i++)
		if(r.s[i]==' ' || r.s[i]=='\t')
			break;
	for(; i<n; i++)
		if(r.s[i] == '|')
			break;
	if(i == n)
		return;
	i++;
	w.tag.delete(i, n, TRUE);
	utils->strfree(r);
	r = nil;
	w.tag.file.mod = FALSE;
	if(w.tag.q0 > i)
		w.tag.q0 = i;
	if(w.tag.q1 > i)
		w.tag.q1 = i;
	w.tag.setselect(w.tag.q0, w.tag.q1);
}

Window.settag(w : self ref Window)
{
	i : int;
	f : ref File;

	f = w.body.file;
	for(i=0; i<f.ntext; i++){
		v := f.text[i].w;
		if(v.col.safe || v.body.frame.maxlines>0)
			v.settag1();
	}
}

Window.settag1(w : self ref Window)
{
	ii, j, k, n, bar, dirty : int;
	old : ref Astring;
	new : string;
	r : int;
	b : ref Image;
	q0, q1 : int;
	br : Rect;

	if(w.tag.ncache!=0 || w.tag.file.mod)
		w.commit(w.tag);	# check file name; also can now modify tag
	old = utils->stralloc(w.tag.file.buf.nc);
	w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc);
	for(ii=0; ii<w.tag.file.buf.nc; ii++)
		if(old.s[ii]==' ' || old.s[ii]=='\t')
			break;
	if(old.s[0:ii] != w.body.file.name){
		w.tag.delete(0, ii, TRUE);
		w.tag.insert(0, w.body.file.name, len w.body.file.name, TRUE, 0);
		strfree(old);
		old = nil;
		old = utils->stralloc(w.tag.file.buf.nc);
		w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc);
	}
	new = w.body.file.name + " Del Snarf";
	if(w.filemenu){
		if(w.body.file.delta.nc>0 || w.body.ncache)
			new += " Undo";
		if(w.body.file.epsilon.nc > 0)
			new += " Redo";
		dirty = w.body.file.name != nil && (w.body.ncache || w.body.file.seq!=w.putseq);
		if(!w.isdir && dirty)
			new += " Put";
	}
	if(w.isdir)
		new += " Get";
	l := len w.body.file.name;
	if(l >= 2 && w.body.file.name[l-2: ] == ".b")
		new += " Limbo";
	new += " |";
	r = utils->strchr(old.s, '|');
	if(r >= 0)
		k = r+1;
	else{
		k = w.tag.file.buf.nc;
		if(w.body.file.seq == 0)
			new += " Look ";
	}
	if(new != old.s[0:k]){
		n = k;
		if(n > len new)
			n = len new;
		for(j=0; j<n; j++)
			if(old.s[j] != new[j])
				break;
		q0 = w.tag.q0;
		q1 = w.tag.q1;
		w.tag.delete(j, k, TRUE);
		w.tag.insert(j, new[j:], len new - j, TRUE, 0);
		# try to preserve user selection 
		r = utils->strchr(old.s, '|');
		if(r >= 0){
			bar = r;
			if(q0 > bar){
				bar = utils->strchr(new, '|')-bar;
				w.tag.q0 = q0+bar;
				w.tag.q1 = q1+bar;
			}
		}
	}
	strfree(old);
	old = nil;
	new = nil;
	w.tag.file.mod = FALSE;
	n = w.tag.file.buf.nc+w.tag.ncache;
	if(w.tag.q0 > n)
		w.tag.q0 = n;
	if(w.tag.q1 > n)
		w.tag.q1 = n;
	w.tag.setselect(w.tag.q0, w.tag.q1);
	b = button;
	if(!w.isdir && !w.isscratch && (w.body.file.mod || w.body.ncache))
		b = modbutton;
	br.min = w.tag.scrollr.min;
	br.max.x = br.min.x + b.r.dx();
	br.max.y = br.min.y + b.r.dy();
	draw(mainwin, br, b, nil, b.r.min);
}

Window.commit(w : self ref Window, t : ref Text)
{
	r : ref Astring;
	i : int;
	f : ref File;

	t.commit(TRUE);
	f = t.file;
	if(f.ntext > 1)
		for(i=0; i<f.ntext; i++)
			f.text[i].commit(FALSE);	# no-op for t 
	if(t.what == Body)
		return;
	r = utils->stralloc(w.tag.file.buf.nc);
	w.tag.file.buf.read(0, r, 0, w.tag.file.buf.nc);
	for(i=0; i<w.tag.file.buf.nc; i++)
		if(r.s[i]==' ' || r.s[i]=='\t')
			break;
	if(r.s[0:i] != w.body.file.name){
		dat->seq++;
		w.body.file.mark();
		w.body.file.mod = TRUE;
		w.dirty = TRUE;
		w.setname(r.s, i);
		w.settag();
	}
	utils->strfree(r);
	r = nil;
}

Window.addincl(w : self ref Window, r : string, n : int)
{
	{
		(ok, d) := sys->stat(r);
		if(ok < 0){
			if(r[0] == '/')
				raise "e";
			(r, n) = look->dirname(w.body, r, n);
			(ok, d) = sys->stat(r);
			if(ok < 0)
				raise "e";
		}
		if((d.mode&Sys->DMDIR) == 0){
			warning(nil, sprint("%s: not a directory\n", r));
			r = nil;
			return;
		}
		w.nincl++;
		owi := w.incl;
		w.incl = array[w.nincl] of string;
		w.incl[1:] = owi[0:w.nincl-1];
		owi = nil;
		w.incl[0] = r;
		r = nil;
	}
	exception{
		* =>
			warning(nil, sprint("%s: %r\n", r));
			r = nil;
	}
}

Window.clean(w : self ref Window, conservative : int, exiting : int) : int	# as it stands, conservative is always TRUE 
{
	if(w.isscratch || w.isdir)	# don't whine if it's a guide file, error window, etc. 
		return TRUE;
	if((!conservative||exiting) && w.nopen[Dat->QWevent]>byte 0)
		return TRUE;
	if(w.dirty){
		if(w.body.file.name != nil)
			warning(nil, sprint("%s modified\n", w.body.file.name));
		else{
			if(w.body.file.buf.nc < 100)	# don't whine if it's too small 
				return TRUE;
			warning(nil, "unnamed file modified\n");
		}
		w.dirty = FALSE;
		return FALSE;
	}
	return TRUE;
}

Window.ctlprint(w : self ref Window, fonts : int) : string
{
	s := sprint("%11d %11d %11d %11d %11d ", w.id, w.tag.file.buf.nc,
			w.body.file.buf.nc, w.isdir, w.dirty);
	if(fonts)
		return sprint("%s%11d %q %11d ", s, w.body.frame.r.dx(),
			w.body.reffont.f.name, w.body.frame.maxtab);
	return s;
}

Window.event(w : self ref Window, fmt : string)
{
	n : int;
	x : ref Xfid;

	if(w.nopen[Dat->QWevent] == byte 0)
		return;
	if(w.owner == 0)
		error("no window owner");
	n = len fmt;
	w.events[len w.events] = w.owner;
	w.events += fmt;
	w.nevents += n+1;
	x = w.eventx;
	if(x != nil){
		w.eventx = nil;
		x.c <-= Xfidm->Xnil;
	}
}