xref: /plan9-contrib/sys/src/cmd/acme/wind.c (revision 2c2299ce0e1270fbef669a6f1c8a8e80dfa5d69c)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <draw.h>
47dd7cddfSDavid du Colombier #include <thread.h>
59a747e4fSDavid du Colombier #include <cursor.h>
67dd7cddfSDavid du Colombier #include <mouse.h>
77dd7cddfSDavid du Colombier #include <keyboard.h>
87dd7cddfSDavid du Colombier #include <frame.h>
97dd7cddfSDavid du Colombier #include <fcall.h>
107dd7cddfSDavid du Colombier #include <plumb.h>
117dd7cddfSDavid du Colombier #include "dat.h"
127dd7cddfSDavid du Colombier #include "fns.h"
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier int	winid;
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier void
wininit(Window * w,Window * clone,Rectangle r)177dd7cddfSDavid du Colombier wininit(Window *w, Window *clone, Rectangle r)
187dd7cddfSDavid du Colombier {
197dd7cddfSDavid du Colombier 	Rectangle r1, br;
207dd7cddfSDavid du Colombier 	File *f;
217dd7cddfSDavid du Colombier 	Reffont *rf;
227dd7cddfSDavid du Colombier 	Rune *rp;
237dd7cddfSDavid du Colombier 	int nc;
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier 	w->tag.w = w;
26*2c2299ceSDavid du Colombier 	w->taglines = 1;
27*2c2299ceSDavid du Colombier 	w->tagexpand = TRUE;
287dd7cddfSDavid du Colombier 	w->body.w = w;
297dd7cddfSDavid du Colombier 	w->id = ++winid;
307dd7cddfSDavid du Colombier 	incref(w);
3106300895SDavid du Colombier 	if(globalincref)
3206300895SDavid du Colombier 		incref(w);
337dd7cddfSDavid du Colombier 	w->ctlfid = ~0;
347dd7cddfSDavid du Colombier 	w->utflastqid = -1;
357dd7cddfSDavid du Colombier 	r1 = r;
36*2c2299ceSDavid du Colombier 
37*2c2299ceSDavid du Colombier 	w->tagtop = r;
38*2c2299ceSDavid du Colombier 	w->tagtop.max.y = r.min.y + font->height;
39*2c2299ceSDavid du Colombier 	r1.max.y = r1.min.y + w->taglines*font->height;
40*2c2299ceSDavid du Colombier 
417dd7cddfSDavid du Colombier 	incref(&reffont);
427dd7cddfSDavid du Colombier 	f = fileaddtext(nil, &w->tag);
437dd7cddfSDavid du Colombier 	textinit(&w->tag, f, r1, &reffont, tagcols);
447dd7cddfSDavid du Colombier 	w->tag.what = Tag;
457dd7cddfSDavid du Colombier 	/* tag is a copy of the contents, not a tracked image */
467dd7cddfSDavid du Colombier 	if(clone){
477dd7cddfSDavid du Colombier 		textdelete(&w->tag, 0, w->tag.file->nc, TRUE);
487dd7cddfSDavid du Colombier 		nc = clone->tag.file->nc;
497dd7cddfSDavid du Colombier 		rp = runemalloc(nc);
507dd7cddfSDavid du Colombier 		bufread(clone->tag.file, 0, rp, nc);
517dd7cddfSDavid du Colombier 		textinsert(&w->tag, 0, rp, nc, TRUE);
527dd7cddfSDavid du Colombier 		free(rp);
537dd7cddfSDavid du Colombier 		filereset(w->tag.file);
547dd7cddfSDavid du Colombier 		textsetselect(&w->tag, nc, nc);
557dd7cddfSDavid du Colombier 	}
567dd7cddfSDavid du Colombier 	r1 = r;
57*2c2299ceSDavid du Colombier 	r1.min.y += w->taglines*font->height + 1;
587dd7cddfSDavid du Colombier 	if(r1.max.y < r1.min.y)
597dd7cddfSDavid du Colombier 		r1.max.y = r1.min.y;
607dd7cddfSDavid du Colombier 	f = nil;
617dd7cddfSDavid du Colombier 	if(clone){
627dd7cddfSDavid du Colombier 		f = clone->body.file;
637dd7cddfSDavid du Colombier 		w->body.org = clone->body.org;
647dd7cddfSDavid du Colombier 		w->isscratch = clone->isscratch;
657dd7cddfSDavid du Colombier 		rf = rfget(FALSE, FALSE, FALSE, clone->body.reffont->f->name);
667dd7cddfSDavid du Colombier 	}else
677dd7cddfSDavid du Colombier 		rf = rfget(FALSE, FALSE, FALSE, nil);
687dd7cddfSDavid du Colombier 	f = fileaddtext(f, &w->body);
697dd7cddfSDavid du Colombier 	w->body.what = Body;
707dd7cddfSDavid du Colombier 	textinit(&w->body, f, r1, rf, textcols);
717dd7cddfSDavid du Colombier 	r1.min.y -= 1;
727dd7cddfSDavid du Colombier 	r1.max.y = r1.min.y+1;
737dd7cddfSDavid du Colombier 	draw(screen, r1, tagcols[BORD], nil, ZP);
747dd7cddfSDavid du Colombier 	textscrdraw(&w->body);
757dd7cddfSDavid du Colombier 	w->r = r;
767dd7cddfSDavid du Colombier 	br.min = w->tag.scrollr.min;
777dd7cddfSDavid du Colombier 	br.max.x = br.min.x + Dx(button->r);
787dd7cddfSDavid du Colombier 	br.max.y = br.min.y + Dy(button->r);
797dd7cddfSDavid du Colombier 	draw(screen, br, button, nil, button->r.min);
807dd7cddfSDavid du Colombier 	w->filemenu = TRUE;
817dd7cddfSDavid du Colombier 	w->maxlines = w->body.maxlines;
82a30303efSDavid du Colombier 	w->autoindent = globalautoindent;
837dd7cddfSDavid du Colombier 	if(clone){
847dd7cddfSDavid du Colombier 		w->dirty = clone->dirty;
857dd7cddfSDavid du Colombier 		textsetselect(&w->body, clone->body.q0, clone->body.q1);
867dd7cddfSDavid du Colombier 		winsettag(w);
87a30303efSDavid du Colombier 		w->autoindent = clone->autoindent;
887dd7cddfSDavid du Colombier 	}
897dd7cddfSDavid du Colombier }
907dd7cddfSDavid du Colombier 
91*2c2299ceSDavid du Colombier /*
92*2c2299ceSDavid du Colombier  * Draw the appropriate button.
93*2c2299ceSDavid du Colombier  */
94*2c2299ceSDavid du Colombier void
windrawbutton(Window * w)95*2c2299ceSDavid du Colombier windrawbutton(Window *w)
96*2c2299ceSDavid du Colombier {
97*2c2299ceSDavid du Colombier 	Image *b;
98*2c2299ceSDavid du Colombier 	Rectangle br;
99*2c2299ceSDavid du Colombier 
100*2c2299ceSDavid du Colombier 	b = button;
101*2c2299ceSDavid du Colombier 	if(!w->isdir && !w->isscratch && (w->body.file->mod || w->body.ncache))
102*2c2299ceSDavid du Colombier 		b = modbutton;
103*2c2299ceSDavid du Colombier 	br.min = w->tag.scrollr.min;
104*2c2299ceSDavid du Colombier 	br.max.x = br.min.x + Dx(b->r);
105*2c2299ceSDavid du Colombier 	br.max.y = br.min.y + Dy(b->r);
106*2c2299ceSDavid du Colombier 	draw(screen, br, b, nil, b->r.min);
107*2c2299ceSDavid du Colombier }
108*2c2299ceSDavid du Colombier 
1097dd7cddfSDavid du Colombier int
delrunepos(Window * w)110588d0145SDavid du Colombier delrunepos(Window *w)
111588d0145SDavid du Colombier {
112588d0145SDavid du Colombier 	int n;
113588d0145SDavid du Colombier 	Rune rune;
114588d0145SDavid du Colombier 
115588d0145SDavid du Colombier 	for(n=0; n<w->tag.file->nc; n++) {
116588d0145SDavid du Colombier 		bufread(w->tag.file, n, &rune, 1);
117588d0145SDavid du Colombier 		if(rune == ' ')
118588d0145SDavid du Colombier 			break;
119588d0145SDavid du Colombier 	}
120588d0145SDavid du Colombier 	n += 2;
121588d0145SDavid du Colombier 	if(n >= w->tag.file->nc)
122588d0145SDavid du Colombier 		return -1;
123588d0145SDavid du Colombier 	return n;
124588d0145SDavid du Colombier }
125588d0145SDavid du Colombier 
126588d0145SDavid du Colombier void
movetodel(Window * w)127588d0145SDavid du Colombier movetodel(Window *w)
128588d0145SDavid du Colombier {
129588d0145SDavid du Colombier 	int n;
130588d0145SDavid du Colombier 
131588d0145SDavid du Colombier 	n = delrunepos(w);
132588d0145SDavid du Colombier 	if(n < 0)
133588d0145SDavid du Colombier 		return;
134588d0145SDavid du Colombier 	moveto(mousectl, addpt(frptofchar(&w->tag, n), Pt(4, w->tag.font->height-4)));
135588d0145SDavid du Colombier }
136588d0145SDavid du Colombier 
137*2c2299ceSDavid du Colombier /*
138*2c2299ceSDavid du Colombier  * Compute number of tag lines required
139*2c2299ceSDavid du Colombier  * to display entire tag text.
140*2c2299ceSDavid du Colombier  */
141588d0145SDavid du Colombier int
wintaglines(Window * w,Rectangle r)142*2c2299ceSDavid du Colombier wintaglines(Window *w, Rectangle r)
1437dd7cddfSDavid du Colombier {
144*2c2299ceSDavid du Colombier 	int n;
145*2c2299ceSDavid du Colombier 	Rune rune;
146*2c2299ceSDavid du Colombier 	Point p;
147*2c2299ceSDavid du Colombier 
148*2c2299ceSDavid du Colombier 	if(!w->tagexpand && !w->showdel)
149*2c2299ceSDavid du Colombier 		return 1;
150*2c2299ceSDavid du Colombier 	w->showdel = FALSE;
151*2c2299ceSDavid du Colombier 	w->tag.noredraw = 1;
152*2c2299ceSDavid du Colombier 	textresize(&w->tag, r, TRUE);
153*2c2299ceSDavid du Colombier 	w->tag.noredraw = 0;
154*2c2299ceSDavid du Colombier 	w->tagsafe = FALSE;
155*2c2299ceSDavid du Colombier 
156*2c2299ceSDavid du Colombier 	if(!w->tagexpand) {
157*2c2299ceSDavid du Colombier 		/* use just as many lines as needed to show the Del */
158*2c2299ceSDavid du Colombier 		n = delrunepos(w);
159*2c2299ceSDavid du Colombier 		if(n < 0)
160*2c2299ceSDavid du Colombier 			return 1;
161*2c2299ceSDavid du Colombier 		p = subpt(frptofchar(&w->tag, n), w->tag.r.min);
162*2c2299ceSDavid du Colombier 		return 1 + p.y / w->tag.font->height;
163*2c2299ceSDavid du Colombier 	}
164*2c2299ceSDavid du Colombier 
165*2c2299ceSDavid du Colombier 	/* can't use more than we have */
166*2c2299ceSDavid du Colombier 	if(w->tag.nlines >= w->tag.maxlines)
167*2c2299ceSDavid du Colombier 		return w->tag.maxlines;
168*2c2299ceSDavid du Colombier 
169*2c2299ceSDavid du Colombier 	/* if tag ends with \n, include empty line at end for typing */
170*2c2299ceSDavid du Colombier 	n = w->tag.nlines;
171*2c2299ceSDavid du Colombier 	if(w->tag.file->nc > 0){
172*2c2299ceSDavid du Colombier 		bufread(w->tag.file, w->tag.file->nc-1, &rune, 1);
173*2c2299ceSDavid du Colombier 		if(rune == '\n')
174*2c2299ceSDavid du Colombier 			n++;
175*2c2299ceSDavid du Colombier 	}
176*2c2299ceSDavid du Colombier 	if(n == 0)
177*2c2299ceSDavid du Colombier 		n = 1;
178*2c2299ceSDavid du Colombier 	return n;
179*2c2299ceSDavid du Colombier }
180*2c2299ceSDavid du Colombier 
181*2c2299ceSDavid du Colombier int
winresize(Window * w,Rectangle r,int safe,int keepextra)182*2c2299ceSDavid du Colombier winresize(Window *w, Rectangle r, int safe, int keepextra)
183*2c2299ceSDavid du Colombier {
184*2c2299ceSDavid du Colombier 	int oy, y, mouseintag, mouseinbody;
185*2c2299ceSDavid du Colombier 	Point p;
1867dd7cddfSDavid du Colombier 	Rectangle r1;
187*2c2299ceSDavid du Colombier 
188*2c2299ceSDavid du Colombier 	mouseintag = ptinrect(mouse->xy, w->tag.all);
189*2c2299ceSDavid du Colombier 	mouseinbody = ptinrect(mouse->xy, w->body.all);
190*2c2299ceSDavid du Colombier 
191*2c2299ceSDavid du Colombier 	/* tagtop is first line of tag */
192*2c2299ceSDavid du Colombier 	w->tagtop = r;
193*2c2299ceSDavid du Colombier 	w->tagtop.max.y = r.min.y+font->height;
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 	r1 = r;
196*2c2299ceSDavid du Colombier 	r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
197*2c2299ceSDavid du Colombier 
198*2c2299ceSDavid du Colombier 	/* If needed, recompute number of lines in tag. */
199*2c2299ceSDavid du Colombier 	if(!safe || !w->tagsafe || !eqrect(w->tag.all, r1)){
200*2c2299ceSDavid du Colombier 		w->taglines = wintaglines(w, r);
201*2c2299ceSDavid du Colombier 		r1.max.y = min(r.max.y, r1.min.y + w->taglines*font->height);
202*2c2299ceSDavid du Colombier 	}
203*2c2299ceSDavid du Colombier 
204*2c2299ceSDavid du Colombier 	/* If needed, resize & redraw tag. */
2057dd7cddfSDavid du Colombier 	y = r1.max.y;
206*2c2299ceSDavid du Colombier 	if(!safe || !w->tagsafe || !eqrect(w->tag.all, r1)){
207*2c2299ceSDavid du Colombier 		textresize(&w->tag, r1, TRUE);
208*2c2299ceSDavid du Colombier 		y = w->tag.r.max.y;
209*2c2299ceSDavid du Colombier 		windrawbutton(w);
210*2c2299ceSDavid du Colombier 		w->tagsafe = TRUE;
211*2c2299ceSDavid du Colombier 
212*2c2299ceSDavid du Colombier 		/* If mouse is in tag, pull up as tag closes. */
213*2c2299ceSDavid du Colombier 		if(mouseintag && !ptinrect(mouse->xy, w->tag.all)){
214*2c2299ceSDavid du Colombier 			p = mouse->xy;
215*2c2299ceSDavid du Colombier 			p.y = w->tag.all.max.y-3;
216*2c2299ceSDavid du Colombier 			moveto(mousectl, p);
2177dd7cddfSDavid du Colombier 		}
218*2c2299ceSDavid du Colombier 
219*2c2299ceSDavid du Colombier 		/* If mouse is in body, push down as tag expands. */
220*2c2299ceSDavid du Colombier 		if(mouseinbody && ptinrect(mouse->xy, w->tag.all)){
221*2c2299ceSDavid du Colombier 			p = mouse->xy;
222*2c2299ceSDavid du Colombier 			p.y = w->tag.all.max.y+3;
223*2c2299ceSDavid du Colombier 			moveto(mousectl, p);
2247dd7cddfSDavid du Colombier 		}
225*2c2299ceSDavid du Colombier 	}
226*2c2299ceSDavid du Colombier 
227*2c2299ceSDavid du Colombier 	/* If needed, resize & redraw body. */
2287dd7cddfSDavid du Colombier 	r1 = r;
2297dd7cddfSDavid du Colombier 	r1.min.y = y;
230*2c2299ceSDavid du Colombier 	if(!safe || !eqrect(w->body.all, r1)){
231*2c2299ceSDavid du Colombier 		oy = y;
232*2c2299ceSDavid du Colombier 		if(y+1+w->body.font->height <= r.max.y){	/* room for one line */
233*2c2299ceSDavid du Colombier 			r1.min.y = y;
2347dd7cddfSDavid du Colombier 			r1.max.y = y+1;
2357dd7cddfSDavid du Colombier 			draw(screen, r1, tagcols[BORD], nil, ZP);
236*2c2299ceSDavid du Colombier 			y++;
237*2c2299ceSDavid du Colombier 			r1.min.y = min(y, r.max.y);
2387dd7cddfSDavid du Colombier 			r1.max.y = r.max.y;
239*2c2299ceSDavid du Colombier 		}else{
240*2c2299ceSDavid du Colombier 			r1.min.y = y;
241*2c2299ceSDavid du Colombier 			r1.max.y = y;
242*2c2299ceSDavid du Colombier 		}
243*2c2299ceSDavid du Colombier 		y = textresize(&w->body, r1, keepextra);
2447dd7cddfSDavid du Colombier 		w->r = r;
2457dd7cddfSDavid du Colombier 		w->r.max.y = y;
2467dd7cddfSDavid du Colombier 		textscrdraw(&w->body);
247*2c2299ceSDavid du Colombier 		w->body.all.min.y = oy;
2487dd7cddfSDavid du Colombier 	}
2497dd7cddfSDavid du Colombier 	w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
2507dd7cddfSDavid du Colombier 	return w->r.max.y;
2517dd7cddfSDavid du Colombier }
2527dd7cddfSDavid du Colombier 
2537dd7cddfSDavid du Colombier void
winlock1(Window * w,int owner)2547dd7cddfSDavid du Colombier winlock1(Window *w, int owner)
2557dd7cddfSDavid du Colombier {
2567dd7cddfSDavid du Colombier 	incref(w);
2577dd7cddfSDavid du Colombier 	qlock(w);
2587dd7cddfSDavid du Colombier 	w->owner = owner;
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier 
2617dd7cddfSDavid du Colombier void
winlock(Window * w,int owner)2627dd7cddfSDavid du Colombier winlock(Window *w, int owner)
2637dd7cddfSDavid du Colombier {
2647dd7cddfSDavid du Colombier 	int i;
2657dd7cddfSDavid du Colombier 	File *f;
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier 	f = w->body.file;
2687dd7cddfSDavid du Colombier 	for(i=0; i<f->ntext; i++)
2697dd7cddfSDavid du Colombier 		winlock1(f->text[i]->w, owner);
2707dd7cddfSDavid du Colombier }
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier void
winunlock(Window * w)2737dd7cddfSDavid du Colombier winunlock(Window *w)
2747dd7cddfSDavid du Colombier {
2757dd7cddfSDavid du Colombier 	int i;
2767dd7cddfSDavid du Colombier 	File *f;
2777dd7cddfSDavid du Colombier 
278e6f18918SDavid du Colombier 	/*
279e6f18918SDavid du Colombier 	 * subtle: loop runs backwards to avoid tripping over
280e6f18918SDavid du Colombier 	 * winclose indirectly editing f->text and freeing f
281e6f18918SDavid du Colombier 	 * on the last iteration of the loop.
282e6f18918SDavid du Colombier 	 */
2837dd7cddfSDavid du Colombier 	f = w->body.file;
284e6f18918SDavid du Colombier 	for(i=f->ntext-1; i>=0; i--){
2857dd7cddfSDavid du Colombier 		w = f->text[i]->w;
2867dd7cddfSDavid du Colombier 		w->owner = 0;
2877dd7cddfSDavid du Colombier 		qunlock(w);
2887dd7cddfSDavid du Colombier 		winclose(w);
2897dd7cddfSDavid du Colombier 	}
2907dd7cddfSDavid du Colombier }
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier void
winmousebut(Window * w)2937dd7cddfSDavid du Colombier winmousebut(Window *w)
2947dd7cddfSDavid du Colombier {
295*2c2299ceSDavid du Colombier 	moveto(mousectl, addpt(w->tag.scrollr.min,
296*2c2299ceSDavid du Colombier 		divpt(Pt(Dx(w->tag.scrollr), font->height), 2)));
2977dd7cddfSDavid du Colombier }
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier void
windirfree(Window * w)3007dd7cddfSDavid du Colombier windirfree(Window *w)
3017dd7cddfSDavid du Colombier {
3027dd7cddfSDavid du Colombier 	int i;
3037dd7cddfSDavid du Colombier 	Dirlist *dl;
3047dd7cddfSDavid du Colombier 
3057dd7cddfSDavid du Colombier 	if(w->isdir){
3067dd7cddfSDavid du Colombier 		for(i=0; i<w->ndl; i++){
3077dd7cddfSDavid du Colombier 			dl = w->dlp[i];
3087dd7cddfSDavid du Colombier 			free(dl->r);
3097dd7cddfSDavid du Colombier 			free(dl);
3107dd7cddfSDavid du Colombier 		}
3117dd7cddfSDavid du Colombier 		free(w->dlp);
3127dd7cddfSDavid du Colombier 	}
3137dd7cddfSDavid du Colombier 	w->dlp = nil;
3147dd7cddfSDavid du Colombier 	w->ndl = 0;
3157dd7cddfSDavid du Colombier }
3167dd7cddfSDavid du Colombier 
3177dd7cddfSDavid du Colombier void
winclose(Window * w)3187dd7cddfSDavid du Colombier winclose(Window *w)
3197dd7cddfSDavid du Colombier {
3207dd7cddfSDavid du Colombier 	int i;
3217dd7cddfSDavid du Colombier 
3227dd7cddfSDavid du Colombier 	if(decref(w) == 0){
3237dd7cddfSDavid du Colombier 		windirfree(w);
3247dd7cddfSDavid du Colombier 		textclose(&w->tag);
3257dd7cddfSDavid du Colombier 		textclose(&w->body);
3267dd7cddfSDavid du Colombier 		if(activewin == w)
3277dd7cddfSDavid du Colombier 			activewin = nil;
3287dd7cddfSDavid du Colombier 		for(i=0; i<w->nincl; i++)
3297dd7cddfSDavid du Colombier 			free(w->incl[i]);
3307dd7cddfSDavid du Colombier 		free(w->incl);
3317dd7cddfSDavid du Colombier 		free(w->events);
3327dd7cddfSDavid du Colombier 		free(w);
3337dd7cddfSDavid du Colombier 	}
3347dd7cddfSDavid du Colombier }
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier void
windelete(Window * w)3377dd7cddfSDavid du Colombier windelete(Window *w)
3387dd7cddfSDavid du Colombier {
3397dd7cddfSDavid du Colombier 	Xfid *x;
3407dd7cddfSDavid du Colombier 
3417dd7cddfSDavid du Colombier 	x = w->eventx;
3427dd7cddfSDavid du Colombier 	if(x){
3437dd7cddfSDavid du Colombier 		w->nevents = 0;
3447dd7cddfSDavid du Colombier 		free(w->events);
3457dd7cddfSDavid du Colombier 		w->events = nil;
3467dd7cddfSDavid du Colombier 		w->eventx = nil;
3477dd7cddfSDavid du Colombier 		sendp(x->c, nil);	/* wake him up */
3487dd7cddfSDavid du Colombier 	}
3497dd7cddfSDavid du Colombier }
3507dd7cddfSDavid du Colombier 
3517dd7cddfSDavid du Colombier void
winundo(Window * w,int isundo)3527dd7cddfSDavid du Colombier winundo(Window *w, int isundo)
3537dd7cddfSDavid du Colombier {
3547dd7cddfSDavid du Colombier 	Text *body;
3557dd7cddfSDavid du Colombier 	int i;
3567dd7cddfSDavid du Colombier 	File *f;
3577dd7cddfSDavid du Colombier 	Window *v;
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier 	w->utflastqid = -1;
3607dd7cddfSDavid du Colombier 	body = &w->body;
3617dd7cddfSDavid du Colombier 	fileundo(body->file, isundo, &body->q0, &body->q1);
3629a747e4fSDavid du Colombier 	textshow(body, body->q0, body->q1, 1);
3637dd7cddfSDavid du Colombier 	f = body->file;
3647dd7cddfSDavid du Colombier 	for(i=0; i<f->ntext; i++){
3657dd7cddfSDavid du Colombier 		v = f->text[i]->w;
3667dd7cddfSDavid du Colombier 		v->dirty = (f->seq != v->putseq);
3677dd7cddfSDavid du Colombier 		if(v != w){
3687dd7cddfSDavid du Colombier 			v->body.q0 = v->body.p0+v->body.org;
3697dd7cddfSDavid du Colombier 			v->body.q1 = v->body.p1+v->body.org;
3707dd7cddfSDavid du Colombier 		}
3717dd7cddfSDavid du Colombier 	}
3727dd7cddfSDavid du Colombier 	winsettag(w);
3737dd7cddfSDavid du Colombier }
3747dd7cddfSDavid du Colombier 
3757dd7cddfSDavid du Colombier void
winsetname(Window * w,Rune * name,int n)3767dd7cddfSDavid du Colombier winsetname(Window *w, Rune *name, int n)
3777dd7cddfSDavid du Colombier {
3787dd7cddfSDavid du Colombier 	Text *t;
3797dd7cddfSDavid du Colombier 	Window *v;
3807dd7cddfSDavid du Colombier 	int i;
3817dd7cddfSDavid du Colombier 
3827dd7cddfSDavid du Colombier 	t = &w->body;
3837dd7cddfSDavid du Colombier 	if(runeeq(t->file->name, t->file->nname, name, n) == TRUE)
3847dd7cddfSDavid du Colombier 		return;
3857dd7cddfSDavid du Colombier 	w->isscratch = FALSE;
3867dd7cddfSDavid du Colombier 	if(n>=6 && runeeq(L"/guide", 6, name+(n-6), 6))
3877dd7cddfSDavid du Colombier 		w->isscratch = TRUE;
3887dd7cddfSDavid du Colombier 	else if(n>=7 && runeeq(L"+Errors", 7, name+(n-7), 7))
3897dd7cddfSDavid du Colombier 		w->isscratch = TRUE;
3907dd7cddfSDavid du Colombier 	filesetname(t->file, name, n);
3917dd7cddfSDavid du Colombier 	for(i=0; i<t->file->ntext; i++){
3927dd7cddfSDavid du Colombier 		v = t->file->text[i]->w;
3937dd7cddfSDavid du Colombier 		winsettag(v);
3947dd7cddfSDavid du Colombier 		v->isscratch = w->isscratch;
3957dd7cddfSDavid du Colombier 	}
3967dd7cddfSDavid du Colombier }
3977dd7cddfSDavid du Colombier 
3987dd7cddfSDavid du Colombier void
wintype(Window * w,Text * t,Rune r)3997dd7cddfSDavid du Colombier wintype(Window *w, Text *t, Rune r)
4007dd7cddfSDavid du Colombier {
4017dd7cddfSDavid du Colombier 	int i;
4027dd7cddfSDavid du Colombier 
4037dd7cddfSDavid du Colombier 	texttype(t, r);
4047dd7cddfSDavid du Colombier 	if(t->what == Body)
4057dd7cddfSDavid du Colombier 		for(i=0; i<t->file->ntext; i++)
4067dd7cddfSDavid du Colombier 			textscrdraw(t->file->text[i]);
4077dd7cddfSDavid du Colombier 	winsettag(w);
4087dd7cddfSDavid du Colombier }
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier void
wincleartag(Window * w)4117dd7cddfSDavid du Colombier wincleartag(Window *w)
4127dd7cddfSDavid du Colombier {
4137dd7cddfSDavid du Colombier 	int i, n;
4147dd7cddfSDavid du Colombier 	Rune *r;
4157dd7cddfSDavid du Colombier 
4167dd7cddfSDavid du Colombier 	/* w must be committed */
4177dd7cddfSDavid du Colombier 	n = w->tag.file->nc;
4187dd7cddfSDavid du Colombier 	r = runemalloc(n);
4197dd7cddfSDavid du Colombier 	bufread(w->tag.file, 0, r, n);
4207dd7cddfSDavid du Colombier 	for(i=0; i<n; i++)
4217dd7cddfSDavid du Colombier 		if(r[i]==' ' || r[i]=='\t')
4227dd7cddfSDavid du Colombier 			break;
4237dd7cddfSDavid du Colombier 	for(; i<n; i++)
4247dd7cddfSDavid du Colombier 		if(r[i] == '|')
4257dd7cddfSDavid du Colombier 			break;
4267dd7cddfSDavid du Colombier 	if(i == n)
4277dd7cddfSDavid du Colombier 		return;
4287dd7cddfSDavid du Colombier 	i++;
4297dd7cddfSDavid du Colombier 	textdelete(&w->tag, i, n, TRUE);
4307dd7cddfSDavid du Colombier 	free(r);
4317dd7cddfSDavid du Colombier 	w->tag.file->mod = FALSE;
4327dd7cddfSDavid du Colombier 	if(w->tag.q0 > i)
4337dd7cddfSDavid du Colombier 		w->tag.q0 = i;
4347dd7cddfSDavid du Colombier 	if(w->tag.q1 > i)
4357dd7cddfSDavid du Colombier 		w->tag.q1 = i;
4367dd7cddfSDavid du Colombier 	textsetselect(&w->tag, w->tag.q0, w->tag.q1);
4377dd7cddfSDavid du Colombier }
4387dd7cddfSDavid du Colombier 
4397dd7cddfSDavid du Colombier void
winsettag1(Window * w)4407dd7cddfSDavid du Colombier winsettag1(Window *w)
4417dd7cddfSDavid du Colombier {
442*2c2299ceSDavid du Colombier 	int i, j, k, n, bar, dirty, resize;
4437dd7cddfSDavid du Colombier 	Rune *new, *old, *r;
4447dd7cddfSDavid du Colombier 	uint q0, q1;
4457dd7cddfSDavid du Colombier 
4467dd7cddfSDavid du Colombier 	/* there are races that get us here with stuff in the tag cache, so we take extra care to sync it */
4477dd7cddfSDavid du Colombier 	if(w->tag.ncache!=0 || w->tag.file->mod)
4487dd7cddfSDavid du Colombier 		wincommit(w, &w->tag);	/* check file name; also guarantees we can modify tag contents */
4497dd7cddfSDavid du Colombier 	old = runemalloc(w->tag.file->nc+1);
4507dd7cddfSDavid du Colombier 	bufread(w->tag.file, 0, old, w->tag.file->nc);
4517dd7cddfSDavid du Colombier 	old[w->tag.file->nc] = '\0';
4527dd7cddfSDavid du Colombier 	for(i=0; i<w->tag.file->nc; i++)
4537dd7cddfSDavid du Colombier 		if(old[i]==' ' || old[i]=='\t')
4547dd7cddfSDavid du Colombier 			break;
4557dd7cddfSDavid du Colombier 	if(runeeq(old, i, w->body.file->name, w->body.file->nname) == FALSE){
4567dd7cddfSDavid du Colombier 		textdelete(&w->tag, 0, i, TRUE);
4577dd7cddfSDavid du Colombier 		textinsert(&w->tag, 0, w->body.file->name, w->body.file->nname, TRUE);
4587dd7cddfSDavid du Colombier 		free(old);
4597dd7cddfSDavid du Colombier 		old = runemalloc(w->tag.file->nc+1);
4607dd7cddfSDavid du Colombier 		bufread(w->tag.file, 0, old, w->tag.file->nc);
4617dd7cddfSDavid du Colombier 		old[w->tag.file->nc] = '\0';
4627dd7cddfSDavid du Colombier 	}
4637dd7cddfSDavid du Colombier 	new = runemalloc(w->body.file->nname+100);
4647dd7cddfSDavid du Colombier 	i = 0;
4657dd7cddfSDavid du Colombier 	runemove(new+i, w->body.file->name, w->body.file->nname);
4667dd7cddfSDavid du Colombier 	i += w->body.file->nname;
4677dd7cddfSDavid du Colombier 	runemove(new+i, L" Del Snarf", 10);
4687dd7cddfSDavid du Colombier 	i += 10;
4697dd7cddfSDavid du Colombier 	if(w->filemenu){
4707dd7cddfSDavid du Colombier 		if(w->body.file->delta.nc>0 || w->body.ncache){
4717dd7cddfSDavid du Colombier 			runemove(new+i, L" Undo", 5);
4727dd7cddfSDavid du Colombier 			i += 5;
4737dd7cddfSDavid du Colombier 		}
4747dd7cddfSDavid du Colombier 		if(w->body.file->epsilon.nc > 0){
4757dd7cddfSDavid du Colombier 			runemove(new+i, L" Redo", 5);
4767dd7cddfSDavid du Colombier 			i += 5;
4777dd7cddfSDavid du Colombier 		}
4787dd7cddfSDavid du Colombier 		dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq);
4797dd7cddfSDavid du Colombier 		if(!w->isdir && dirty){
4807dd7cddfSDavid du Colombier 			runemove(new+i, L" Put", 4);
4817dd7cddfSDavid du Colombier 			i += 4;
4827dd7cddfSDavid du Colombier 		}
4837dd7cddfSDavid du Colombier 	}
4847dd7cddfSDavid du Colombier 	if(w->isdir){
4857dd7cddfSDavid du Colombier 		runemove(new+i, L" Get", 4);
4867dd7cddfSDavid du Colombier 		i += 4;
4877dd7cddfSDavid du Colombier 	}
4887dd7cddfSDavid du Colombier 	runemove(new+i, L" |", 2);
4897dd7cddfSDavid du Colombier 	i += 2;
49059cc4ca5SDavid du Colombier 	r = runestrchr(old, '|');
4917dd7cddfSDavid du Colombier 	if(r)
4927dd7cddfSDavid du Colombier 		k = r-old+1;
4937dd7cddfSDavid du Colombier 	else{
4947dd7cddfSDavid du Colombier 		k = w->tag.file->nc;
4957dd7cddfSDavid du Colombier 		if(w->body.file->seq == 0){
4967dd7cddfSDavid du Colombier 			runemove(new+i, L" Look ", 6);
4977dd7cddfSDavid du Colombier 			i += 6;
4987dd7cddfSDavid du Colombier 		}
4997dd7cddfSDavid du Colombier 	}
500*2c2299ceSDavid du Colombier 	new[i] = 0;
501*2c2299ceSDavid du Colombier 
502*2c2299ceSDavid du Colombier 	/* replace tag if the new one is different */
503*2c2299ceSDavid du Colombier 	resize = 0;
5047dd7cddfSDavid du Colombier 	if(runeeq(new, i, old, k) == FALSE){
505*2c2299ceSDavid du Colombier 		resize = 1;
5067dd7cddfSDavid du Colombier 		n = k;
5077dd7cddfSDavid du Colombier 		if(n > i)
5087dd7cddfSDavid du Colombier 			n = i;
5097dd7cddfSDavid du Colombier 		for(j=0; j<n; j++)
5107dd7cddfSDavid du Colombier 			if(old[j] != new[j])
5117dd7cddfSDavid du Colombier 				break;
5127dd7cddfSDavid du Colombier 		q0 = w->tag.q0;
5137dd7cddfSDavid du Colombier 		q1 = w->tag.q1;
5147dd7cddfSDavid du Colombier 		textdelete(&w->tag, j, k, TRUE);
5157dd7cddfSDavid du Colombier 		textinsert(&w->tag, j, new+j, i-j, TRUE);
5167dd7cddfSDavid du Colombier 		/* try to preserve user selection */
51759cc4ca5SDavid du Colombier 		r = runestrchr(old, '|');
5187dd7cddfSDavid du Colombier 		if(r){
5197dd7cddfSDavid du Colombier 			bar = r-old;
5207dd7cddfSDavid du Colombier 			if(q0 > bar){
52159cc4ca5SDavid du Colombier 				bar = (runestrchr(new, '|')-new)-bar;
5227dd7cddfSDavid du Colombier 				w->tag.q0 = q0+bar;
5237dd7cddfSDavid du Colombier 				w->tag.q1 = q1+bar;
5247dd7cddfSDavid du Colombier 			}
5257dd7cddfSDavid du Colombier 		}
5267dd7cddfSDavid du Colombier 	}
5277dd7cddfSDavid du Colombier 	free(old);
5287dd7cddfSDavid du Colombier 	free(new);
5297dd7cddfSDavid du Colombier 	w->tag.file->mod = FALSE;
5307dd7cddfSDavid du Colombier 	n = w->tag.file->nc+w->tag.ncache;
5317dd7cddfSDavid du Colombier 	if(w->tag.q0 > n)
5327dd7cddfSDavid du Colombier 		w->tag.q0 = n;
5337dd7cddfSDavid du Colombier 	if(w->tag.q1 > n)
5347dd7cddfSDavid du Colombier 		w->tag.q1 = n;
5357dd7cddfSDavid du Colombier 	textsetselect(&w->tag, w->tag.q0, w->tag.q1);
536*2c2299ceSDavid du Colombier 	windrawbutton(w);
537*2c2299ceSDavid du Colombier 	if(resize){
538*2c2299ceSDavid du Colombier 		w->tagsafe = 0;
539*2c2299ceSDavid du Colombier 		winresize(w, w->r, TRUE, TRUE);
540*2c2299ceSDavid du Colombier 	}
5417dd7cddfSDavid du Colombier }
5427dd7cddfSDavid du Colombier 
5437dd7cddfSDavid du Colombier void
winsettag(Window * w)5447dd7cddfSDavid du Colombier winsettag(Window *w)
5457dd7cddfSDavid du Colombier {
5467dd7cddfSDavid du Colombier 	int i;
5477dd7cddfSDavid du Colombier 	File *f;
5487dd7cddfSDavid du Colombier 	Window *v;
5497dd7cddfSDavid du Colombier 
5507dd7cddfSDavid du Colombier 	f = w->body.file;
5517dd7cddfSDavid du Colombier 	for(i=0; i<f->ntext; i++){
5527dd7cddfSDavid du Colombier 		v = f->text[i]->w;
5537dd7cddfSDavid du Colombier 		if(v->col->safe || v->body.maxlines>0)
5547dd7cddfSDavid du Colombier 			winsettag1(v);
5557dd7cddfSDavid du Colombier 	}
5567dd7cddfSDavid du Colombier }
5577dd7cddfSDavid du Colombier 
5587dd7cddfSDavid du Colombier void
wincommit(Window * w,Text * t)5597dd7cddfSDavid du Colombier wincommit(Window *w, Text *t)
5607dd7cddfSDavid du Colombier {
5617dd7cddfSDavid du Colombier 	Rune *r;
5627dd7cddfSDavid du Colombier 	int i;
5637dd7cddfSDavid du Colombier 	File *f;
5647dd7cddfSDavid du Colombier 
5657dd7cddfSDavid du Colombier 	textcommit(t, TRUE);
5667dd7cddfSDavid du Colombier 	f = t->file;
5677dd7cddfSDavid du Colombier 	if(f->ntext > 1)
5687dd7cddfSDavid du Colombier 		for(i=0; i<f->ntext; i++)
5697dd7cddfSDavid du Colombier 			textcommit(f->text[i], FALSE);	/* no-op for t */
5707dd7cddfSDavid du Colombier 	if(t->what == Body)
5717dd7cddfSDavid du Colombier 		return;
5727dd7cddfSDavid du Colombier 	r = runemalloc(w->tag.file->nc);
5737dd7cddfSDavid du Colombier 	bufread(w->tag.file, 0, r, w->tag.file->nc);
5747dd7cddfSDavid du Colombier 	for(i=0; i<w->tag.file->nc; i++)
5757dd7cddfSDavid du Colombier 		if(r[i]==' ' || r[i]=='\t')
5767dd7cddfSDavid du Colombier 			break;
5777dd7cddfSDavid du Colombier 	if(runeeq(r, i, w->body.file->name, w->body.file->nname) == FALSE){
5787dd7cddfSDavid du Colombier 		seq++;
5797dd7cddfSDavid du Colombier 		filemark(w->body.file);
5807dd7cddfSDavid du Colombier 		w->body.file->mod = TRUE;
5817dd7cddfSDavid du Colombier 		w->dirty = TRUE;
5827dd7cddfSDavid du Colombier 		winsetname(w, r, i);
5837dd7cddfSDavid du Colombier 		winsettag(w);
5847dd7cddfSDavid du Colombier 	}
5857dd7cddfSDavid du Colombier 	free(r);
5867dd7cddfSDavid du Colombier }
5877dd7cddfSDavid du Colombier 
5887dd7cddfSDavid du Colombier void
winaddincl(Window * w,Rune * r,int n)5897dd7cddfSDavid du Colombier winaddincl(Window *w, Rune *r, int n)
5907dd7cddfSDavid du Colombier {
5917dd7cddfSDavid du Colombier 	char *a;
5929a747e4fSDavid du Colombier 	Dir *d;
5937dd7cddfSDavid du Colombier 	Runestr rs;
5947dd7cddfSDavid du Colombier 
5957dd7cddfSDavid du Colombier 	a = runetobyte(r, n);
5969a747e4fSDavid du Colombier 	d = dirstat(a);
5979a747e4fSDavid du Colombier 	if(d == nil){
5987dd7cddfSDavid du Colombier 		if(a[0] == '/')
5997dd7cddfSDavid du Colombier 			goto Rescue;
6007dd7cddfSDavid du Colombier 		rs = dirname(&w->body, r, n);
6017dd7cddfSDavid du Colombier 		r = rs.r;
6027dd7cddfSDavid du Colombier 		n = rs.nr;
6037dd7cddfSDavid du Colombier 		free(a);
6047dd7cddfSDavid du Colombier 		a = runetobyte(r, n);
6059a747e4fSDavid du Colombier 		d = dirstat(a);
6069a747e4fSDavid du Colombier 		if(d == nil)
6077dd7cddfSDavid du Colombier 			goto Rescue;
6087dd7cddfSDavid du Colombier 		r = runerealloc(r, n+1);
6097dd7cddfSDavid du Colombier 		r[n] = 0;
6107dd7cddfSDavid du Colombier 	}
6117dd7cddfSDavid du Colombier 	free(a);
6129a747e4fSDavid du Colombier 	if((d->qid.type&QTDIR) == 0){
6139a747e4fSDavid du Colombier 		free(d);
6147dd7cddfSDavid du Colombier 		warning(nil, "%s: not a directory\n", a);
6157dd7cddfSDavid du Colombier 		free(r);
6167dd7cddfSDavid du Colombier 		return;
6177dd7cddfSDavid du Colombier 	}
6189a747e4fSDavid du Colombier 	free(d);
6197dd7cddfSDavid du Colombier 	w->nincl++;
6207dd7cddfSDavid du Colombier 	w->incl = realloc(w->incl, w->nincl*sizeof(Rune*));
6217dd7cddfSDavid du Colombier 	memmove(w->incl+1, w->incl, (w->nincl-1)*sizeof(Rune*));
6227dd7cddfSDavid du Colombier 	w->incl[0] = runemalloc(n+1);
6237dd7cddfSDavid du Colombier 	runemove(w->incl[0], r, n);
6247dd7cddfSDavid du Colombier 	free(r);
6257dd7cddfSDavid du Colombier 	return;
6267dd7cddfSDavid du Colombier 
6277dd7cddfSDavid du Colombier Rescue:
6287dd7cddfSDavid du Colombier 	warning(nil, "%s: %r\n", a);
6297dd7cddfSDavid du Colombier 	free(r);
6307dd7cddfSDavid du Colombier 	free(a);
6317dd7cddfSDavid du Colombier 	return;
6327dd7cddfSDavid du Colombier }
6337dd7cddfSDavid du Colombier 
6347dd7cddfSDavid du Colombier int
winclean(Window * w,int conservative)6357dd7cddfSDavid du Colombier winclean(Window *w, int conservative)	/* as it stands, conservative is always TRUE */
6367dd7cddfSDavid du Colombier {
6377dd7cddfSDavid du Colombier 	if(w->isscratch || w->isdir)	/* don't whine if it's a guide file, error window, etc. */
6387dd7cddfSDavid du Colombier 		return TRUE;
6397dd7cddfSDavid du Colombier 	if(!conservative && w->nopen[QWevent]>0)
6407dd7cddfSDavid du Colombier 		return TRUE;
6417dd7cddfSDavid du Colombier 	if(w->dirty){
6427dd7cddfSDavid du Colombier 		if(w->body.file->nname)
6437dd7cddfSDavid du Colombier 			warning(nil, "%.*S modified\n", w->body.file->nname, w->body.file->name);
6447dd7cddfSDavid du Colombier 		else{
6457dd7cddfSDavid du Colombier 			if(w->body.file->nc < 100)	/* don't whine if it's too small */
6467dd7cddfSDavid du Colombier 				return TRUE;
6477dd7cddfSDavid du Colombier 			warning(nil, "unnamed file modified\n");
6487dd7cddfSDavid du Colombier 		}
6497dd7cddfSDavid du Colombier 		w->dirty = FALSE;
6507dd7cddfSDavid du Colombier 		return FALSE;
6517dd7cddfSDavid du Colombier 	}
6527dd7cddfSDavid du Colombier 	return TRUE;
6537dd7cddfSDavid du Colombier }
6547dd7cddfSDavid du Colombier 
6558a2c5ad0SDavid du Colombier char*
winctlprint(Window * w,char * buf,int fonts)6569a747e4fSDavid du Colombier winctlprint(Window *w, char *buf, int fonts)
6577dd7cddfSDavid du Colombier {
6588a2c5ad0SDavid du Colombier 	sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->nc,
6597dd7cddfSDavid du Colombier 		w->body.file->nc, w->isdir, w->dirty);
6609a747e4fSDavid du Colombier 	if(fonts)
6618a2c5ad0SDavid du Colombier 		return smprint("%s%11d %q %11d " , buf, Dx(w->body.r),
6628a2c5ad0SDavid du Colombier 			w->body.reffont->f->name, w->body.maxtab);
6638a2c5ad0SDavid du Colombier 	return buf;
6647dd7cddfSDavid du Colombier }
6657dd7cddfSDavid du Colombier 
6667dd7cddfSDavid du Colombier void
winevent(Window * w,char * fmt,...)6677dd7cddfSDavid du Colombier winevent(Window *w, char *fmt, ...)
6687dd7cddfSDavid du Colombier {
6697dd7cddfSDavid du Colombier 	int n;
6707dd7cddfSDavid du Colombier 	char *b;
6717dd7cddfSDavid du Colombier 	Xfid *x;
6727dd7cddfSDavid du Colombier 	va_list arg;
6737dd7cddfSDavid du Colombier 
6747dd7cddfSDavid du Colombier 	if(w->nopen[QWevent] == 0)
6757dd7cddfSDavid du Colombier 		return;
6767dd7cddfSDavid du Colombier 	if(w->owner == 0)
6777dd7cddfSDavid du Colombier 		error("no window owner");
6787dd7cddfSDavid du Colombier 	va_start(arg, fmt);
6799a747e4fSDavid du Colombier 	b = vsmprint(fmt, arg);
6807dd7cddfSDavid du Colombier 	va_end(arg);
6819a747e4fSDavid du Colombier 	if(b == nil)
6829a747e4fSDavid du Colombier 		error("vsmprint failed");
6839a747e4fSDavid du Colombier 	n = strlen(b);
6847dd7cddfSDavid du Colombier 	w->events = realloc(w->events, w->nevents+1+n);
6857dd7cddfSDavid du Colombier 	w->events[w->nevents++] = w->owner;
6867dd7cddfSDavid du Colombier 	memmove(w->events+w->nevents, b, n);
6879a747e4fSDavid du Colombier 	free(b);
6887dd7cddfSDavid du Colombier 	w->nevents += n;
6897dd7cddfSDavid du Colombier 	x = w->eventx;
6907dd7cddfSDavid du Colombier 	if(x){
6917dd7cddfSDavid du Colombier 		w->eventx = nil;
6927dd7cddfSDavid du Colombier 		sendp(x->c, nil);
6937dd7cddfSDavid du Colombier 	}
6947dd7cddfSDavid du Colombier }
695