code: 9ferno

ref: b502a62da2ec6058923db94f87ecc2d29db2fa77
dir: /appl/acme/row.b/

View raw version
implement Rowm;

include "common.m";

sys : Sys;
bufio : Bufio;
utils : Utils;
drawm : Draw;
acme : Acme;
graph : Graph;
gui : Gui;
dat : Dat;
bufferm : Bufferm;
textm : Textm;
filem : Filem;
windowm : Windowm;
columnm : Columnm;
exec : Exec;
look : Look;
edit : Edit;
ecmd : Editcmd;

ALLLOOPER, ALLTOFILE, ALLMATCHFILE, ALLFILECHECK, ALLELOGTERM, ALLEDITINIT, ALLUPDATE: import Edit;
sprint : import sys;
FALSE, TRUE, XXX : import Dat;
Border, BUFSIZE, Astring : import Dat;
Reffont, reffont, Lock, Ref : import dat;
row, home, mouse : import dat;
fontnames : import acme;
font, draw : import graph;
Point, Rect, Image : import drawm;
min, max, abs, error, warning, clearmouse, stralloc, strfree : import utils;
black, white, mainwin : import gui;
Buffer : import bufferm;
Tag, Rowtag, Text : import textm;
Window : import windowm;
File : import filem;
Column : import columnm;
Iobuf : import bufio;

init(mods : ref Dat->Mods)
{
	sys = mods.sys;
	bufio = mods.bufio;
	dat = mods.dat;
	utils = mods.utils;
	drawm = mods.draw;
	acme = mods.acme;
	graph = mods.graph;
	gui = mods.gui;
	bufferm = mods.bufferm;
	textm = mods.textm;
	filem = mods.filem;
	windowm = mods.windowm;
	columnm = mods.columnm;
	exec = mods.exec;
	look = mods.look;
	edit = mods.edit;
	ecmd = mods.editcmd;
}

newrow() : ref Row
{
	r := ref Row;
	r.qlock = Lock.init();
	r.r = ((0, 0), (0, 0));
	r.tag = nil;
	r.col = nil;
	r.ncol = 0;
	return r;
}

Row.init(row : self ref Row, r : Rect)
{
	r1 : Rect;
	t : ref Text;
	dummy : ref File = nil;

	draw(mainwin, r, white, nil, (0, 0));
	row.r = r;
	row.col = nil;
	row.ncol = 0;
	r1 = r;
	r1.max.y = r1.min.y + font.height;
	row.tag = textm->newtext();
	t = row.tag;
	t.init(dummy.addtext(t), r1, Reffont.get(FALSE, FALSE, FALSE, nil), acme->tagcols);
	t.what = Rowtag;
	t.row = row;
	t.w = nil;
	t.col = nil;
	r1.min.y = r1.max.y;
	r1.max.y += Border;
	draw(mainwin, r1, black, nil, (0, 0));
	t.insert(0, "Newcol Kill Putall Dump Exit ", 29, TRUE, 0);
	t.setselect(t.file.buf.nc, t.file.buf.nc);
}

Row.add(row : self ref Row, c : ref Column, x : int) : ref Column
{
	r, r1 : Rect;
	d : ref Column;
	i : int;

	d = nil;
	r = row.r;
	r.min.y = row.tag.frame.r.max.y+Border;
	if(x<r.min.x && row.ncol>0){	#steal 40% of last column by default 
		d = row.col[row.ncol-1];
		x = d.r.min.x + 3*d.r.dx()/5;
	}
	# look for column we'll land on 
	for(i=0; i<row.ncol; i++){
		d = row.col[i];
		if(x < d.r.max.x)
			break;
	}
	if(row.ncol > 0){
		if(i < row.ncol)
			i++;	# new column will go after d 
		r = d.r;
		if(r.dx() < 100)
			return nil;
		draw(mainwin, r, white, nil, (0, 0));
		r1 = r;
		r1.max.x = min(x, r.max.x-50);
		if(r1.dx() < 50)
			r1.max.x = r1.min.x+50;
		d.reshape(r1);
		r1.min.x = r1.max.x;
		r1.max.x = r1.min.x+Border;
		draw(mainwin, r1, black, nil, (0, 0));
		r.min.x = r1.max.x;
	}
	if(c == nil){
		c = ref Column;
		c.init(r);
		reffont.r.inc();
	}else
		c.reshape(r);
	c.row = row;
	c.tag.row = row;
	orc := row.col;
	row.col = array[row.ncol+1] of ref Column;
	row.col[0:] = orc[0:i];
	row.col[i+1:] = orc[i:row.ncol];
	orc = nil;
	row.col[i] = c;
	row.ncol++;
	clearmouse();
	return c;
}

Row.reshape(row : self ref Row, r : Rect)
{
	i, dx, odx : int;
	r1, r2 : Rect;
	c : ref Column;

	dx = r.dx();
	odx = row.r.dx();
	row.r = r;
	r1 = r;
	r1.max.y = r1.min.y + font.height;
	row.tag.reshape(r1);
	r1.min.y = r1.max.y;
	r1.max.y += Border;
	draw(mainwin, r1, black, nil, (0, 0));
	r.min.y = r1.max.y;
	r1 = r;
	r1.max.x = r1.min.x;
	for(i=0; i<row.ncol; i++){
		c = row.col[i];
		r1.min.x = r1.max.x;
		if(i == row.ncol-1)
			r1.max.x = r.max.x;
		else
			r1.max.x = r1.min.x+c.r.dx()*dx/odx;
		r2 = r1;
		r2.max.x = r2.min.x+Border;
		draw(mainwin, r2, black, nil, (0, 0));
		r1.min.x = r2.max.x;
		c.reshape(r1);
	}
}

Row.dragcol(row : self ref Row, c : ref Column)
{
	r : Rect;
	i, b, x : int;
	p, op : Point;
	d : ref Column;

	clearmouse();
	graph->cursorswitch(dat->boxcursor);
	b = mouse.buttons;
	op = mouse.xy;
	while(mouse.buttons == b)
		acme->frgetmouse();
	graph->cursorswitch(dat->arrowcursor);
	if(mouse.buttons){
		while(mouse.buttons)
			acme->frgetmouse();
		return;
	}

	for(i=0; i<row.ncol; i++)
		if(row.col[i] == c)
			break;
	if (i == row.ncol)
		error("can't find column");

	if(i == 0)
		return;
	p = mouse.xy;
	if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5))
		return;
	if((i>0 && p.x<row.col[i-1].r.min.x) || (i<row.ncol-1 && p.x>c.r.max.x)){
		# shuffle 
		x = c.r.min.x;
		row.close(c, FALSE);
		if(row.add(c, p.x) == nil)	# whoops! 
		if(row.add(c, x) == nil)		# WHOOPS! 
		if(row.add(c, -1)==nil){		# shit! 
			row.close(c, TRUE);
			return;
		}
		c.mousebut();
		return;
	}
	d = row.col[i-1];
	if(p.x < d.r.min.x+80+Dat->Scrollwid)
		p.x = d.r.min.x+80+Dat->Scrollwid;
	if(p.x > c.r.max.x-80-Dat->Scrollwid)
		p.x = c.r.max.x-80-Dat->Scrollwid;
	r = d.r;
	r.max.x = c.r.max.x;
	draw(mainwin, r, white, nil, (0, 0));
	r.max.x = p.x;
	d.reshape(r);
	r = c.r;
	r.min.x = p.x;
	r.max.x = r.min.x;
	r.max.x += Border;
	draw(mainwin, r, black, nil, (0, 0));
	r.min.x = r.max.x;
	r.max.x = c.r.max.x;
	c.reshape(r);
	c.mousebut();
}

Row.close(row : self ref Row, c : ref Column, dofree : int)
{
	r : Rect;
	i : int;

	for(i=0; i<row.ncol; i++)
		if(row.col[i] == c)
			break;
	if (i == row.ncol)
		error("can't find column");

	r = c.r;
	if(dofree)
		c.closeall();
	orc := row.col;
	row.col = array[row.ncol-1] of ref Column;
	row.col[0:] = orc[0:i];
	row.col[i:] = orc[i+1:row.ncol];
	orc = nil;
	row.ncol--;
	if(row.ncol == 0){
		draw(mainwin, r, white, nil, (0, 0));
		return;
	}
	if(i == row.ncol){		# extend last column right 
		c = row.col[i-1];
		r.min.x = c.r.min.x;
		r.max.x = row.r.max.x;
	}else{			# extend next window left 
		c = row.col[i];
		r.max.x = c.r.max.x;
	}
	draw(mainwin, r, white, nil, (0, 0));
	c.reshape(r);
}

Row.whichcol(row : self ref Row, p : Point) : ref Column
{
	i : int;
	c : ref Column;

	for(i=0; i<row.ncol; i++){
		c = row.col[i];
		if(p.in(c.r))
			return c;
	}
	return nil;
}

Row.which(row : self ref Row, p : Point) : ref Text
{
	c : ref Column;

	if(p.in(row.tag.all))
		return row.tag;
	c = row.whichcol(p);
	if(c != nil)
		return c.which(p);
	return nil;
}

Row.typex(row : self ref Row, r : int, p : Point) : ref Text
{
	w : ref Window;
	t : ref Text;

	clearmouse();
	row.qlock.lock();
	if(dat->bartflag)
		t = dat->barttext;
	else
		t = row.which(p);
	if(t!=nil && !(t.what==Tag && p.in(t.scrollr))){
		w = t.w;
		if(w == nil)
			t.typex(r, 0);
		else{
			w.lock('K');
			w.typex(t, r);
			w.unlock();
		}
	}
	row.qlock.unlock();
	return t;
}

Row.clean(row : self ref Row, exiting : int) : int
{
	clean : int;
	i : int;

	clean = TRUE;
	for(i=0; i<row.ncol; i++)
		clean &= row.col[i].clean(exiting);
	return clean;
}

Row.dump(row : self ref Row, file : string)
{
	i, j, m, n, dumped : int;
	q0, q1 : int;
	b : ref Iobuf;
	buf, fontname, a : string;
	r : ref Astring;
	c : ref Column;
	w, w1 : ref Window;
	t : ref Text;

	if(row.ncol == 0)
		return;
	
	{
		if(file == nil){
			if(home == nil){
				warning(nil, "can't find file for dump: $home not defined\n");
				raise "e";
			}
			buf = sprint("%s/acme.dump", home);
			file = buf;
		}
		b = bufio->create(file, Bufio->OWRITE, 8r600);
		if(b == nil){
			warning(nil, sprint("can't open %s: %r\n", file));
			raise "e";
		}
		r = stralloc(BUFSIZE);
		b.puts(acme->wdir); b.putc('\n');
		b.puts(fontnames[0]); b.putc('\n');
		b.puts(fontnames[1]); b.putc('\n');
		for(i=0; i<row.ncol; i++){
			c = row.col[i];
			b.puts(sprint("%11d", 100*(c.r.min.x-row.r.min.x)/row.r.dx()));
			if(i == row.ncol-1)
				b.putc('\n');
			else
				b.putc(' ');
		}
		for(i=0; i<row.ncol; i++){
			c = row.col[i];
			for(j=0; j<c.nw; j++)
				c.w[j].body.file.dumpid = 0;
		}
		for(i=0; i<row.ncol; i++){
			c = row.col[i];
			for(j=0; j<c.nw; j++){
				w = c.w[j];
				w.commit(w.tag);
				t = w.body;
				# windows owned by others get special treatment 
				if(w.nopen[Dat->QWevent] > byte 0)
					if(w.dumpstr == nil)
						continue;
				# zeroxes of external windows are tossed 
				if(t.file.ntext > 1)
					for(n=0; n<t.file.ntext; n++){
						w1 = t.file.text[n].w;
						if(w == w1)
							continue;
						if(w1.nopen[Dat->QWevent] != byte 0) {
							j = c.nw;
							continue;
						}
					}
				fontname = "";
				if(t.reffont.f != font)
					fontname = t.reffont.f.name;
				a = t.file.name;
				if(t.file.dumpid){
					dumped = FALSE;
					b.puts(sprint("x%11d %11d %11d %11d %11d %s\n", i, t.file.dumpid,
						w.body.q0, w.body.q1,
						100*(w.r.min.y-c.r.min.y)/c.r.dy(),
						fontname));
				}else if(w.dumpstr != nil){
					dumped = FALSE;
					b.puts(sprint("e%11d %11d %11d %11d %11d %s\n", i, t.file.dumpid,
						0, 0,
						100*(w.r.min.y-c.r.min.y)/c.r.dy(),
						fontname));
				}else if(len a == 0){	# don't save unnamed windows 
					continue;
				}else if((!w.dirty && utils->access(a)==0) || w.isdir){
					dumped = FALSE;
					t.file.dumpid = w.id;
					b.puts(sprint("f%11d %11d %11d %11d %11d %s\n", i, w.id,
						w.body.q0, w.body.q1,
						100*(w.r.min.y-c.r.min.y)/c.r.dy(),
						fontname));
				}else{
					dumped = TRUE;
					t.file.dumpid = w.id;
					b.puts(sprint("F%11d %11d %11d %11d %11d %11d %s\n", i, j,
						w.body.q0, w.body.q1,
						100*(w.r.min.y-c.r.min.y)/c.r.dy(),
						w.body.file.buf.nc, fontname));
				}
				a = nil;
				buf = w.ctlprint(0);
				b.puts(buf);
				m = min(BUFSIZE, w.tag.file.buf.nc);
				w.tag.file.buf.read(0, r, 0, m);
				n = 0;
				while(n<m && r.s[n]!='\n')
					n++;
				r.s[n++] = '\n';
				b.puts(r.s[0:n]);
				if(dumped){
					q0 = 0;
					q1 = t.file.buf.nc;
					while(q0 < q1){
						n = q1 - q0;
						if(n > Dat->BUFSIZE)
							n = Dat->BUFSIZE;
						t.file.buf.read(q0, r, 0, n);
						b.puts(r.s[0:n]);
						q0 += n;
					}
				}
				if(w.dumpstr != nil){
					if(w.dumpdir != nil)
						b.puts(sprint("%s\n%s\n", w.dumpdir, w.dumpstr));
					else
						b.puts(sprint("\n%s\n", w.dumpstr));
				}
			}
		}
		b.close();
		b = nil;
		strfree(r);
		r = nil;
	}
	exception{
		* =>
			return;
	}
}

rdline(b : ref Iobuf, line : int) : (int, string)
{
	l : string;

	l = b.gets('\n');
	if(l != nil)
		line++;
	return (line, l);
}

Row.loadx(row : self ref Row, file : string, initing : int)
{
	i, j, line, percent, y, nr, nfontr, n, ns, ndumped, dumpid, x : int;
	b, bout : ref Iobuf;
	fontname : string;
	l, buf, t : string;
	rune : int;
	r, fontr : string;
	c, c1, c2 : ref Column;
	q0, q1 : int;
	r1, r2 : Rect;
	w : ref Window;

	{
		if(file == nil){
			if(home == nil){
				warning(nil, "can't find file for load: $home not defined\n");
				raise "e";
			}
			buf = sprint("%s/acme.dump", home);
			file = buf;
		}
		b = bufio->open(file, Bufio->OREAD);
		if(b == nil){
			warning(nil, sprint("can't open load file %s: %r\n", file));
			raise "e";
		}
		
		{
			# current directory 
			(line, l) = rdline(b, 0);
			if(l == nil)
				raise "e";
			l = l[0:len l - 1];
			if(sys->chdir(l) < 0){
				warning(nil, sprint("can't chdir %s\n", l));
				b.close();
				return;
			}
			# global fonts 
			for(i=0; i<2; i++){
				(line, l) = rdline(b, line);
				if(l == nil)
					raise "e";
				l = l[0:len l -1];
				if(l != nil && l != fontnames[i])
					Reffont.get(i, TRUE, i==0 && initing, l);
			}
			if(initing && row.ncol==0)
				row.init(mainwin.clipr);
			(line, l) = rdline(b, line);
			if(l == nil)
				raise "e";
			j = len l/12;
			if(j<=0 || j>10)
				raise "e";
			for(i=0; i<j; i++){
				percent = int l[12*i:12*i+11];
				if(percent<0 || percent>=100)
					raise "e";
				x = row.r.min.x+percent*row.r.dx()/100;
				if(i < row.ncol){
					if(i == 0)
						continue;
					c1 = row.col[i-1];
					c2 = row.col[i];
					r1 = c1.r;
					r2 = c2.r;
					r1.max.x = x;
					r2.min.x = x+Border;
					if(r1.dx() < 50 || r2.dx() < 50)
						continue;
					draw(mainwin, (r1.min, r2.max), white, nil, (0, 0));
					c1.reshape(r1);
					c2.reshape(r2);
					r2.min.x = x;
					r2.max.x = x+Border;
					draw(mainwin, r2, black, nil, (0, 0));
				}
				if(i >= row.ncol)
					row.add(nil, x);
			}
			for(;;){
				(line, l) = rdline(b, line);
				if(l == nil)
					break;
				dumpid = 0;
				case(l[0]){
				'e' =>
					if(len l < 1+5*12+1)
						raise "e";
					(line, l) = rdline(b, line);	# ctl line; ignored 
					if(l == nil)
						raise "e";
					(line, l) = rdline(b, line);	# directory 
					if(l == nil)
						raise "e";
					l = l[0:len l -1];
					if(len l != 0)
						r = l;
					else{
						if(home == nil)
							r = "./";
						else
							r = home+"/";
					}
					nr = len r;
					(line, l) = rdline(b, line);	# command 
					if(l == nil)
						raise "e";
					t = l[0:len l -1];
					spawn exec->run(nil, t, r, nr, TRUE, nil, nil, FALSE);
					# r is freed in run() 
					continue;
				'f' =>
					if(len l < 1+5*12+1)
						raise "e";
					fontname = l[1+5*12:len l - 1];
					ndumped = -1;
				'F' =>
					if(len l < 1+6*12+1)
						raise "e";
					fontname = l[1+6*12:len l - 1];
					ndumped = int l[1+5*12:1+5*12+11];
				'x' =>
					if(len l < 1+5*12+1)
						raise "e";
					fontname = l[1+5*12: len l - 1];
					ndumped = -1;
					dumpid = int l[1+1*12:1+1*12+11];
				* =>
					raise "e";
				}
				l = l[0:len l -1];
				if(len fontname != 0) {
					fontr = fontname;
					nfontr = len fontname;
				}
				else
					(fontr, nfontr) = (nil, 0);
				i = int l[1+0*12:1+0*12+11];
				j = int l[1+1*12:1+1*12+11];
				q0 = int l[1+2*12:1+2*12+11];
				q1 = int l[1+3*12:1+3*12+11];
				percent = int l[1+4*12:1+4*12+11];
				if(i<0 || i>10)
					raise "e";
				if(i > row.ncol)
					i = row.ncol;
				c = row.col[i];
				y = c.r.min.y+(percent*c.r.dy())/100;
				if(y<c.r.min.y || y>=c.r.max.y)
					y = -1;
				if(dumpid == 0)
					w = c.add(nil, nil, y);
				else
					w = c.add(nil, look->lookid(dumpid, TRUE), y);
				if(w == nil)
					continue;
				w.dumpid = j;
				(line, l) = rdline(b, line);
				if(l == nil)
					raise "e";
				l = l[0:len l - 1];
				r = l[5*12:len l];
				nr = len r;
				ns = -1;
				for(n=0; n<nr; n++){
					if(r[n] == '/')
						ns = n;
					if(r[n] == ' ')
						break;
				}
				if(dumpid == 0)
					w.setname(r, n);
				for(; n<nr; n++)
					if(r[n] == '|')
						break;
				w.cleartag();
				w.tag.insert(w.tag.file.buf.nc, r[n+1:len r], nr-(n+1), TRUE, 0);
				if(ndumped >= 0){
					# simplest thing is to put it in a file and load that 
					buf = sprint("/tmp/d%d.%.4sacme", sys->pctl(0, nil), utils->getuser());
					bout = bufio->create(buf, Bufio->OWRITE, 8r600);
					if(bout == nil){
						warning(nil, "can't create temp file: %r\n");
						b.close();
						return;
					}
					for(n=0; n<ndumped; n++){
						rune = b.getc();
						if(rune == '\n')
							line++;
						if(rune == Bufio->EOF){
							bout.close();
							bout = nil;
							raise "e";
						}
						bout.putc(rune);
					}
					bout.close();
					bout = nil;
					w.body.loadx(0, buf, 1);
					w.body.file.mod = TRUE;
					for(n=0; n<w.body.file.ntext; n++)
						w.body.file.text[n].w.dirty = TRUE;
					w.settag();
					sys->remove(buf);
					buf = nil;
				}else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-')
					exec->get(w.body, nil, nil, FALSE, nil, 0);
				l = r = nil;
				if(fontr != nil){
					exec->fontx(w.body, nil, nil, fontr, nfontr);
					fontr = nil;
				}
				if(q0>w.body.file.buf.nc || q1>w.body.file.buf.nc || q0>q1)
					q0 = q1 = 0;
				w.body.show(q0, q1, TRUE);
				w.maxlines = min(w.body.frame.nlines, max(w.maxlines, w.body.frame.maxlines));
			}
			b.close();
		}
		exception{
			* =>
			 	warning(nil, sprint("bad load file %s:%d\n", file, line));
				b.close();
				raise "e";
		}
	}
	exception{
		* =>
			return;
	}
}

allwindows(o: int, aw: ref  Dat->Allwin)
{
	for(i:=0; i<row.ncol; i++){
		c := row.col[i];
		for(j:=0; j<c.nw; j++){
			w := c.w[j];
			case (o){
			ALLLOOPER =>
				pick k := aw{
					LP => ecmd->alllooper(w, k.lp);
				}
			ALLTOFILE =>
				pick k := aw{
					FF => ecmd->alltofile(w, k.ff);
				}
			ALLMATCHFILE =>
				pick k := aw{
					FF => ecmd->allmatchfile(w, k.ff);
				}
			ALLFILECHECK =>
				pick k := aw{
					FC => ecmd->allfilecheck(w, k.fc);
				}
			ALLELOGTERM =>
				edit->allelogterm(w);
			ALLEDITINIT =>
				edit->alleditinit(w);
			ALLUPDATE =>
				edit->allupdate(w);
			}
		}
	}
}