shithub: purgatorio

ref: bd6c2aad586814b091ce5aca9d41cf2c51adb37b
dir: /appl/acme/col.b/

View raw version
implement Columnm;

include "common.m";

sys : Sys;
utils : Utils;
drawm : Draw;
acme : Acme;
graph : Graph;
gui : Gui;
dat : Dat;
textm : Textm;
rowm : Rowm;
filem : Filem;
windowm : Windowm;

FALSE, TRUE, XXX : import Dat;
Border : import Dat;
mouse, colbutton : import dat;
Point, Rect, Image : import drawm;
draw : import graph;
min, max, abs, error, clearmouse : import utils;
black, white, mainwin : import gui;
Text : import textm;
Row : import rowm;
Window : import windowm;
File : import filem;
Columntag : import Textm;
BACK : import Framem;
tagcols, textcols : import acme;

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

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

	draw(mainwin, r, white, nil, (0, 0));
	c.r = r;
	c.row = nil;
	c.w = nil;
	c.nw = 0;
	c.tag = textm->newtext();
	t = c.tag;
	t.w = nil;
	t.col = c;
	r1 = r;
	r1.max.y = r1.min.y + (graph->font).height;
	t.init(dummy.addtext(t), r1, dat->reffont, tagcols);
	t.what = Columntag;
	r1.min.y = r1.max.y;
	r1.max.y += Border;
	draw(mainwin, r1, black, nil, (0, 0));
	t.insert(0, "New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE, 0);
	t.setselect(t.file.buf.nc, t.file.buf.nc);
	draw(mainwin, t.scrollr, colbutton, nil, colbutton.r.min);
	c.safe = TRUE;
}

Column.add(c : self ref Column, w : ref Window, clone : ref Window, y : int) : ref Window
{
	r, r1 : Rect;
	v : ref Window;
	i, t : int;

	v = nil;
	r = c.r;
	r.min.y = c.tag.frame.r.max.y+Border;
	if(y<r.min.y && c.nw>0){	# steal half of last window by default 
		v = c.w[c.nw-1];
		y = v.body.frame.r.min.y+v.body.frame.r.dy()/2;
	}
	# look for window we'll land on 
	for(i=0; i<c.nw; i++){
		v = c.w[i];
		if(y < v.r.max.y)
			break;
	}
	if(c.nw > 0){
		if(i < c.nw)
			i++;	# new window will go after v 
		#
		# if v's too small, grow it first.
		#
		 
		if(!c.safe || v.body.frame.maxlines<=3){
			c.grow(v, 1, 1);
			y = v.body.frame.r.min.y+v.body.frame.r.dy()/2;
		}
		r = v.r;
		if(i == c.nw)
			t = c.r.max.y;
		else
			t = c.w[i].r.min.y-Border;
		r.max.y = t;
		draw(mainwin, r, textcols[BACK], nil, (0, 0));
		r1 = r;
		y = min(y, t-(v.tag.frame.font.height+v.body.frame.font.height+Border+1));
		r1.max.y = min(y, v.body.frame.r.min.y+v.body.frame.nlines*v.body.frame.font.height);
		r1.min.y = v.reshape(r1, FALSE);
		r1.max.y = r1.min.y+Border;
		draw(mainwin, r1, black, nil, (0, 0));
		r.min.y = r1.max.y;
	}
	if(w == nil){
		w = ref Window;
		draw(mainwin, r, textcols[BACK], nil, (0, 0));
		w.col = c;
		w.init(clone, r);
	}else{
		w.col = c;
		w.reshape(r, FALSE);
	}
	w.tag.col = c;
	w.tag.row = c.row;
	w.body.col = c;
	w.body.row = c.row;
	ocw := c.w;
	c.w = array[c.nw+1] of ref Window;
	c.w[0:] = ocw[0:i];
	c.w[i+1:] = ocw[i:c.nw];
	ocw = nil;
	c.nw++;
	c.w[i] = w;
	utils->savemouse(w);
	# near but not on the button 
	graph->cursorset(w.tag.scrollr.max.add(Point(3, 3)));
	dat->barttext = w.body;
	c.safe = TRUE;
	return w;
}

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

	# w is locked 
	if(!c.safe)
		c.grow(w, 1, 1);
	for(i=0; i<c.nw; i++)
		if(c.w[i] == w)
			break;
	if (i == c.nw)
		error("can't find window");
	r = w.r;
	w.tag.col = nil;
	w.body.col = nil;
	w.col = nil;
	utils->restoremouse(w);
	if(dofree){
		w.delete();
		w.close();
	}
	ocw := c.w;
	c.w = array[c.nw-1] of ref Window;
	c.w[0:] = ocw[0:i];
	c.w[i:] = ocw[i+1:c.nw];
	ocw = nil;
	c.nw--;
	if(c.nw == 0){
		draw(mainwin, r, white, nil, (0, 0));
		return;
	}
	if(i == c.nw){		# extend last window down 
		w = c.w[i-1];
		r.min.y = w.r.min.y;
		r.max.y = c.r.max.y;
	}else{			# extend next window up 
		w = c.w[i];
		r.max.y = w.r.max.y;
	}
	draw(mainwin, r, textcols[BACK], nil, (0, 0));
	if(c.safe)
		w.reshape(r, FALSE);
}

Column.closeall(c : self ref Column)
{
	i : int;
	w : ref Window;

	if(c == dat->activecol)
		dat->activecol = nil;
	c.tag.close();
	for(i=0; i<c.nw; i++){
		w = c.w[i];
		w.close();
	}
	c.nw = 0;
	c.w = nil;
	c = nil;
	clearmouse();
}

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

Column.reshape(c : self ref Column, r : Rect)
{
	i : int;
	r1, r2 : Rect;
	w : ref Window;

	clearmouse();
	r1 = r;
	r1.max.y = r1.min.y + c.tag.frame.font.height;
	c.tag.reshape(r1);
	draw(mainwin, c.tag.scrollr, colbutton, nil, colbutton.r.min);
	r1.min.y = r1.max.y;
	r1.max.y += Border;
	draw(mainwin, r1, black, nil, (0, 0));
	r1.max.y = r.max.y;
	for(i=0; i<c.nw; i++){
		w = c.w[i];
		w.maxlines = 0;
		if(i == c.nw-1)
			r1.max.y = r.max.y;
		else
			r1.max.y = r1.min.y+(w.r.dy()+Border)*r.dy()/c.r.dy();
		r2 = r1;
		r2.max.y = r2.min.y+Border;
		draw(mainwin, r2, black, nil, (0, 0));
		r1.min.y = r2.max.y;
		r1.min.y = w.reshape(r1, FALSE);
	}
	c.r = r;
}

colcmp(a : ref Window, b : ref Window) : int
{
	r1, r2 : string;

	r1 = a.body.file.name;
	r2 = b.body.file.name;
	if (r1 < r2)
		return -1;
	if (r1 > r2)
		return 1;
	return 0;
}

qsort(a : array of ref Window, n : int)
{
	i, j : int;
	t : ref Window;

	while(n > 1) {
		i = n>>1;
		t = a[0]; a[0] = a[i]; a[i] = t;
		i = 0;
		j = n;
		for(;;) {
			do
				i++;
			while(i < n && colcmp(a[i], a[0]) < 0);
			do
				j--;
			while(j > 0 && colcmp(a[j], a[0]) > 0);
			if(j < i)
				break;
			t = a[i]; a[i] = a[j]; a[j] = t;
		}
		t = a[0]; a[0] = a[j]; a[j] = t;
		n = n-j-1;
		if(j >= n) {
			qsort(a, j);
			a = a[j+1:];
		} else {
			qsort(a[j+1:], n);
			n = j;
		}
	}
}

Column.sort(c : self ref Column)
{
	i, y : int;
	r, r1 : Rect;
	rp : array of Rect;
	w : ref Window;
	wp : array of ref Window;

	if(c.nw == 0)
		return;
	clearmouse();
	rp = array[c.nw] of Rect;
	wp = array[c.nw] of ref Window;
	wp[0:] = c.w[0:c.nw];
	qsort(wp, c.nw);
	for(i=0; i<c.nw; i++)
		rp[i] = wp[i].r;
	r = c.r;
	r.min.y = c.tag.frame.r.max.y;
	draw(mainwin, r, textcols[BACK], nil, (0, 0));
	y = r.min.y;
	for(i=0; i<c.nw; i++){
		w = wp[i];
		r.min.y = y;
		if(i == c.nw-1)
			r.max.y = c.r.max.y;
		else
			r.max.y = r.min.y+w.r.dy()+Border;
		r1 = r;
		r1.max.y = r1.min.y+Border;
		draw(mainwin, r1, black, nil, (0, 0));
		r.min.y = r1.max.y;
		y = w.reshape(r, FALSE);
	}
	rp = nil;
	c.w = wp;
}

Column.grow(c : self ref Column, w : ref Window, but : int, mv : int)
{
	r, cr : Rect;
	i, j, k, l, y1, y2, tot, nnl, onl, dnl, h : int;
	nl, ny : array of int;
	v : ref Window;

	for(i=0; i<c.nw; i++)
		if(c.w[i] == w)
			break;
	if (i == c.nw)
		error("can't find window");

	cr = c.r;
	if(but < 0){	# make sure window fills its own space properly 
		r = w.r;
		if(i == c.nw-1)
			r.max.y = cr.max.y;
		else
			r.max.y = c.w[i+1].r.min.y;
		w.reshape(r, FALSE);
		return;
	}
	cr.min.y = c.w[0].r.min.y;
	if(but == 3){	# full size 
		if(i != 0){
			v = c.w[0];
			c.w[0] = w;
			c.w[i] = v;
		}
		draw(mainwin, cr, textcols[BACK], nil, (0, 0));
		w.reshape(cr, FALSE);
		for(i=1; i<c.nw; i++)
			c.w[i].body.frame.maxlines = 0;
		c.safe = FALSE;
		return;
	}
	# store old #lines for each window 
	onl = w.body.frame.maxlines;
	nl = array[c.nw] of int;
	ny = array[c.nw] of int;
	tot = 0;
	for(j=0; j<c.nw; j++){
		l = c.w[j].body.frame.maxlines;
		nl[j] = l;
		tot += l;
	}
	# approximate new #lines for this window 
	if(but == 2){	# as big as can be 
		for (j = 0; j < c.nw; j++)
			nl[j] = 0;
		nl[i] = tot;
	}
	else {
		nnl = min(onl + max(min(5, w.maxlines), onl/2), tot);
		if(nnl < w.maxlines)
			nnl = (w.maxlines+nnl)/2;
		if(nnl == 0)
			nnl = 2;
		dnl = nnl - onl;
		# compute new #lines for each window 
		for(k=1; k<c.nw; k++){
			# prune from later window 
			j = i+k;
			if(j<c.nw && nl[j]){
				l = min(dnl, max(1, nl[j]/2));
				nl[j] -= l;
				nl[i] += l;
				dnl -= l;
			}
			# prune from earlier window 
			j = i-k;
			if(j>=0 && nl[j]){
				l = min(dnl, max(1, nl[j]/2));
				nl[j] -= l;
				nl[i] += l;
				dnl -= l;
			}
		}
	}
	# pack everyone above 
	y1 = cr.min.y;
	for(j=0; j<i; j++){
		v = c.w[j];
		r = v.r;
		r.min.y = y1;
		r.max.y = y1+v.tag.all.dy();
		if(nl[j])
			r.max.y += 1 + nl[j]*v.body.frame.font.height;
		if(!c.safe || !v.r.eq(r)){
			draw(mainwin, r, textcols[BACK], nil, (0, 0));
			v.reshape(r, c.safe);
		}
		r.min.y = v.r.max.y;
		r.max.y += Border;
		draw(mainwin, r, black, nil, (0, 0));
		y1 = r.max.y;
	}
	# scan to see new size of everyone below 
	y2 = c.r.max.y;
	for(j=c.nw-1; j>i; j--){
		v = c.w[j];
		r = v.r;
		r.min.y = y2-v.tag.all.dy();
		if(nl[j])
			r.min.y -= 1 + nl[j]*v.body.frame.font.height;
		r.min.y -= Border;
		ny[j] = r.min.y;
		y2 = r.min.y;
	}
	# compute new size of window 
	r = w.r;
	r.min.y = y1;
	r.max.y = r.min.y+w.tag.all.dy();
	h = w.body.frame.font.height;
	if(y2-r.max.y >= 1+h+Border){
		r.max.y += 1;
		r.max.y += h*((y2-r.max.y)/h);
	}
	# draw window 
	if(!c.safe || !w.r.eq(r)){
		draw(mainwin, r, textcols[BACK], nil, (0, 0));
		w.reshape(r, c.safe);
	}
	if(i < c.nw-1){
		r.min.y = r.max.y;
		r.max.y += Border;
		draw(mainwin, r, black, nil, (0, 0));
		for(j=i+1; j<c.nw; j++)
			ny[j] -= (y2-r.max.y);
	}
	# pack everyone below 
	y1 = r.max.y;
	for(j=i+1; j<c.nw; j++){
		v = c.w[j];
		r = v.r;
		r.min.y = y1;
		r.max.y = y1+v.tag.all.dy();
		if(nl[j])
			r.max.y += 1 + nl[j]*v.body.frame.font.height;
		if(!c.safe || !v.r.eq(r)){
			draw(mainwin, r, textcols[BACK], nil, (0, 0));
			v.reshape(r, c.safe);
		}
		if(j < c.nw-1){	# no border on last window 
			r.min.y = v.r.max.y;
			r.max.y += Border;
			draw(mainwin, r, black, nil, (0, 0));
		}
		y1 = r.max.y;
	}
	r = w.r;
	r.min.y = y1;
	r.max.y = c.r.max.y;
	draw(mainwin, r, textcols[BACK], nil, (0, 0));
	nl = nil;
	ny = nil;
	c.safe = TRUE;
	if (mv)
		w.mousebut();
}

Column.dragwin(c : self ref Column, w : ref Window, but : int)
{
	r : Rect;
	i, b : int;
	p, op : Point;
	v : ref Window;
	nc : 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<c.nw; i++)
		if(c.w[i] == w)
			break;
	if (i == c.nw)
		error("can't find window");

	p = mouse.xy;
	if(abs(p.x-op.x)<5 && abs(p.y-op.y)<5){
		c.grow(w, but, 1);
		w.mousebut();
		return;
	}
	# is it a flick to the right? 
	if(abs(p.y-op.y)<10 && p.x>op.x+30 && c.row.whichcol(p) == c)
		p.x += w.r.dx();	# yes: toss to next column 
	nc = c.row.whichcol(p);
	if(nc!=nil && nc!=c){
		c.close(w, FALSE);
		nc.add(w, nil, p.y);
		w.mousebut();
		return;
	}
	if(i==0 && c.nw==1)
		return;			# can't do it 
	if((i>0 && p.y<c.w[i-1].r.min.y) || (i<c.nw-1 && p.y>w.r.max.y)
	|| (i==0 && p.y>w.r.max.y)){
		# shuffle 
		c.close(w, FALSE);
		c.add(w, nil, p.y);
		w.mousebut();
		return;
	}
	if(i == 0)
		return;
	v = c.w[i-1];
	if(p.y < v.tag.all.max.y)
		p.y = v.tag.all.max.y;
	if(p.y > w.r.max.y-w.tag.all.dy()-Border)
		p.y = w.r.max.y-w.tag.all.dy()-Border;
	r = v.r;
	r.max.y = p.y;
	if(r.max.y > v.body.frame.r.min.y){
		r.max.y -= (r.max.y-v.body.frame.r.min.y)%v.body.frame.font.height;
		if(v.body.frame.r.min.y == v.body.frame.r.max.y)
			r.max.y++;
	}
	if(!r.eq(v.r)){
		draw(mainwin, r, textcols[BACK], nil, (0, 0));
		v.reshape(r, c.safe);
	}
	r.min.y = v.r.max.y;
	r.max.y = r.min.y+Border;
	draw(mainwin, r, black, nil, (0, 0));
	r.min.y = r.max.y;
	if(i == c.nw-1)
		r.max.y = c.r.max.y;
	else
		r.max.y = c.w[i+1].r.min.y-Border;
	# r.max.y = w.r.max.y;
	if(!r.eq(w.r)){
		draw(mainwin, r, textcols[BACK], nil, (0, 0));
		w.reshape(r, c.safe);
	}
	c.safe = TRUE;
    	w.mousebut();
}

Column.which(c : self ref Column, p : Point) : ref Text
{
	i : int;
	w : ref Window;

	if(!p.in(c.r))
		return nil;
	if(p.in(c.tag.all))
		return c.tag;
	for(i=0; i<c.nw; i++){
		w = c.w[i];
		if(p.in(w.r)){
			if(p.in(w.tag.all))
				return w.tag;
			return w.body;
		}
	}
	return nil;
}

Column.clean(c : self ref Column, exiting : int) : int
{
	clean : int;
	i : int;

	clean = TRUE;
	for(i=0; i<c.nw; i++)
		clean &= c.w[i].clean(TRUE, exiting);
	return clean;
}