xref: /plan9/sys/src/cmd/abaco/text.c (revision 7ab27030036b6c877a6f81728daeda263d1ca3cf)
1*7ab27030SDavid du Colombier #include <u.h>
2*7ab27030SDavid du Colombier #include <libc.h>
3*7ab27030SDavid du Colombier #include <draw.h>
4*7ab27030SDavid du Colombier #include <memdraw.h>
5*7ab27030SDavid du Colombier #include <thread.h>
6*7ab27030SDavid du Colombier #include <cursor.h>
7*7ab27030SDavid du Colombier #include <mouse.h>
8*7ab27030SDavid du Colombier #include <keyboard.h>
9*7ab27030SDavid du Colombier #include <frame.h>
10*7ab27030SDavid du Colombier #include <plumb.h>
11*7ab27030SDavid du Colombier #include <html.h>
12*7ab27030SDavid du Colombier #include "dat.h"
13*7ab27030SDavid du Colombier #include "fns.h"
14*7ab27030SDavid du Colombier 
15*7ab27030SDavid du Colombier Image	*tagcols[NCOL];
16*7ab27030SDavid du Colombier Image	*textcols[NCOL];
17*7ab27030SDavid du Colombier 
18*7ab27030SDavid du Colombier 
19*7ab27030SDavid du Colombier void
textinit(Text * t,Image * b,Rectangle r,Font * f,Image * cols[NCOL])20*7ab27030SDavid du Colombier textinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOL])
21*7ab27030SDavid du Colombier {
22*7ab27030SDavid du Colombier 	t->all = r;
23*7ab27030SDavid du Colombier 	t->scrollr = r;
24*7ab27030SDavid du Colombier 	t->scrollr.max.x = r.min.x+Scrollsize;
25*7ab27030SDavid du Colombier 	t->lastsr = ZR;
26*7ab27030SDavid du Colombier 	r.min.x += Scrollsize+Scrollgap;
27*7ab27030SDavid du Colombier 	t->rs.nr = 0;
28*7ab27030SDavid du Colombier 	memmove(t->Frame.cols, cols, sizeof t->Frame.cols);
29*7ab27030SDavid du Colombier 	textredraw(t, r, f, b);
30*7ab27030SDavid du Colombier }
31*7ab27030SDavid du Colombier 
32*7ab27030SDavid du Colombier void
textredraw(Text * t,Rectangle r,Font * f,Image * b)33*7ab27030SDavid du Colombier textredraw(Text *t, Rectangle r, Font *f, Image *b)
34*7ab27030SDavid du Colombier {
35*7ab27030SDavid du Colombier 	Rectangle r1;
36*7ab27030SDavid du Colombier 
37*7ab27030SDavid du Colombier 	frinit(t, r, f, b, t->Frame.cols);
38*7ab27030SDavid du Colombier 	r1 = t->r;
39*7ab27030SDavid du Colombier 	r1.min.x -= Scrollsize+Scrollgap;	/* back fill to scroll bar */
40*7ab27030SDavid du Colombier 	draw(t->b, r1, t->cols[BACK], nil, ZP);
41*7ab27030SDavid du Colombier 	t->maxtab = Maxtab*stringwidth(f, "0");
42*7ab27030SDavid du Colombier 	textfill(t);
43*7ab27030SDavid du Colombier 	textsetselect(t, t->q0, t->q1);
44*7ab27030SDavid du Colombier }
45*7ab27030SDavid du Colombier 
46*7ab27030SDavid du Colombier int
textresize(Text * t,Image * b,Rectangle r)47*7ab27030SDavid du Colombier textresize(Text *t, Image *b, Rectangle r)
48*7ab27030SDavid du Colombier {
49*7ab27030SDavid du Colombier 	if(Dy(r) > 0)
50*7ab27030SDavid du Colombier 		r.max.y -= Dy(r)%t->font->height;
51*7ab27030SDavid du Colombier 	else
52*7ab27030SDavid du Colombier 		r.max.y = r.min.y;
53*7ab27030SDavid du Colombier 
54*7ab27030SDavid du Colombier 	t->all = r;
55*7ab27030SDavid du Colombier 	t->scrollr = r;
56*7ab27030SDavid du Colombier 	t->scrollr.max.x = r.min.x+Scrollsize;
57*7ab27030SDavid du Colombier 	t->lastsr = ZR;
58*7ab27030SDavid du Colombier 	r.min.x += Scrollsize+Scrollgap;
59*7ab27030SDavid du Colombier 	frclear(t, 0);
60*7ab27030SDavid du Colombier 	textredraw(t, r, t->font, b);
61*7ab27030SDavid du Colombier 	if(t->what == Textarea)
62*7ab27030SDavid du Colombier 		textscrdraw(t);
63*7ab27030SDavid du Colombier 	return r.max.y;
64*7ab27030SDavid du Colombier }
65*7ab27030SDavid du Colombier 
66*7ab27030SDavid du Colombier void
textclose(Text * t)67*7ab27030SDavid du Colombier textclose(Text *t)
68*7ab27030SDavid du Colombier {
69*7ab27030SDavid du Colombier 	closerunestr(&t->rs);
70*7ab27030SDavid du Colombier 	frclear(t, 1);
71*7ab27030SDavid du Colombier }
72*7ab27030SDavid du Colombier 
73*7ab27030SDavid du Colombier void
textinsert(Text * t,uint q0,Rune * r,uint n)74*7ab27030SDavid du Colombier textinsert(Text *t, uint q0, Rune *r, uint n)
75*7ab27030SDavid du Colombier {
76*7ab27030SDavid du Colombier 	if(n == 0)
77*7ab27030SDavid du Colombier 		return;
78*7ab27030SDavid du Colombier 
79*7ab27030SDavid du Colombier 	t->rs.r = runerealloc(t->rs.r, t->rs.nr+n);
80*7ab27030SDavid du Colombier 	runemove(t->rs.r+q0+n, t->rs.r+q0, t->rs.nr-q0);
81*7ab27030SDavid du Colombier 	runemove(t->rs.r+q0, r, n);
82*7ab27030SDavid du Colombier 	t->rs.nr += n;
83*7ab27030SDavid du Colombier 	if(q0 < t->q1)
84*7ab27030SDavid du Colombier 		t->q1 += n;
85*7ab27030SDavid du Colombier 	if(q0 < t->q0)
86*7ab27030SDavid du Colombier 		t->q0 += n;
87*7ab27030SDavid du Colombier 	if(q0 < t->org)
88*7ab27030SDavid du Colombier 		t->org += n;
89*7ab27030SDavid du Colombier 	else if(q0 <= t->org+t->nchars)
90*7ab27030SDavid du Colombier 		frinsert(t, r, r+n, q0-t->org);
91*7ab27030SDavid du Colombier }
92*7ab27030SDavid du Colombier 
93*7ab27030SDavid du Colombier void
textfill(Text * t)94*7ab27030SDavid du Colombier textfill(Text *t)
95*7ab27030SDavid du Colombier {
96*7ab27030SDavid du Colombier 	Rune *rp;
97*7ab27030SDavid du Colombier 	int i, n, m, nl;
98*7ab27030SDavid du Colombier 
99*7ab27030SDavid du Colombier 	if(t->lastlinefull)
100*7ab27030SDavid du Colombier 		return;
101*7ab27030SDavid du Colombier 	rp = runemalloc(BUFSIZE*8);
102*7ab27030SDavid du Colombier 	do{
103*7ab27030SDavid du Colombier 		n = t->rs.nr-(t->org+t->nchars);
104*7ab27030SDavid du Colombier 		if(n == 0)
105*7ab27030SDavid du Colombier 			break;
106*7ab27030SDavid du Colombier 		if(n > 2000)	/* educated guess at reasonable amount */
107*7ab27030SDavid du Colombier 			n = 2000;
108*7ab27030SDavid du Colombier 		runemove(rp, t->rs.r+(t->org+t->nchars), n);
109*7ab27030SDavid du Colombier 		/*
110*7ab27030SDavid du Colombier 		 * it's expensive to frinsert more than we need, so
111*7ab27030SDavid du Colombier 		 * count newlines.
112*7ab27030SDavid du Colombier 		 */
113*7ab27030SDavid du Colombier 		nl = t->maxlines-t->nlines;
114*7ab27030SDavid du Colombier 		m = 0;
115*7ab27030SDavid du Colombier 		for(i=0; i<n; ){
116*7ab27030SDavid du Colombier 			if(rp[i++] == '\n'){
117*7ab27030SDavid du Colombier 				m++;
118*7ab27030SDavid du Colombier 				if(m >= nl)
119*7ab27030SDavid du Colombier 					break;
120*7ab27030SDavid du Colombier 			}
121*7ab27030SDavid du Colombier 		}
122*7ab27030SDavid du Colombier 		frinsert(t, rp, rp+i, t->nchars);
123*7ab27030SDavid du Colombier 	}while(t->lastlinefull == FALSE);
124*7ab27030SDavid du Colombier 	free(rp);
125*7ab27030SDavid du Colombier }
126*7ab27030SDavid du Colombier 
127*7ab27030SDavid du Colombier void
textdelete(Text * t,uint q0,uint q1)128*7ab27030SDavid du Colombier textdelete(Text *t, uint q0, uint q1)
129*7ab27030SDavid du Colombier {
130*7ab27030SDavid du Colombier 	uint n, p0, p1;
131*7ab27030SDavid du Colombier 
132*7ab27030SDavid du Colombier 	n = q1-q0;
133*7ab27030SDavid du Colombier 	if(n == 0)
134*7ab27030SDavid du Colombier 		return;
135*7ab27030SDavid du Colombier 
136*7ab27030SDavid du Colombier 	runemove(t->rs.r+q0, t->rs.r+q1, t->rs.nr-q1);
137*7ab27030SDavid du Colombier 	t->rs.nr -= n;
138*7ab27030SDavid du Colombier 	if(q0 < t->q0)
139*7ab27030SDavid du Colombier 		t->q0 -= min(n, t->q0-q0);
140*7ab27030SDavid du Colombier 	if(q0 < t->q1)
141*7ab27030SDavid du Colombier 		t->q1 -= min(n, t->q1-q0);
142*7ab27030SDavid du Colombier 	if(q1 <= t->org)
143*7ab27030SDavid du Colombier 		t->org -= n;
144*7ab27030SDavid du Colombier 	else if(q0 < t->org+t->nchars){
145*7ab27030SDavid du Colombier 		p1 = q1 - t->org;
146*7ab27030SDavid du Colombier 		if(p1 > t->nchars)
147*7ab27030SDavid du Colombier 			p1 = t->nchars;
148*7ab27030SDavid du Colombier 		if(q0 < t->org){
149*7ab27030SDavid du Colombier 			t->org = q0;
150*7ab27030SDavid du Colombier 			p0 = 0;
151*7ab27030SDavid du Colombier 		}else
152*7ab27030SDavid du Colombier 			p0 = q0 - t->org;
153*7ab27030SDavid du Colombier 		frdelete(t, p0, p1);
154*7ab27030SDavid du Colombier 		textfill(t);
155*7ab27030SDavid du Colombier 	}
156*7ab27030SDavid du Colombier 	t->rs.r[t->rs.nr] = L'\0';
157*7ab27030SDavid du Colombier }
158*7ab27030SDavid du Colombier 
159*7ab27030SDavid du Colombier int
textbswidth(Text * t,Rune c)160*7ab27030SDavid du Colombier textbswidth(Text *t, Rune c)
161*7ab27030SDavid du Colombier {
162*7ab27030SDavid du Colombier 	uint q, eq;
163*7ab27030SDavid du Colombier 	Rune r;
164*7ab27030SDavid du Colombier 	int skipping;
165*7ab27030SDavid du Colombier 
166*7ab27030SDavid du Colombier 	/* there is known to be at least one character to erase */
167*7ab27030SDavid du Colombier 	if(c == 0x08)	/* ^H: erase character */
168*7ab27030SDavid du Colombier 		return 1;
169*7ab27030SDavid du Colombier 	q = t->q0;
170*7ab27030SDavid du Colombier 	skipping = TRUE;
171*7ab27030SDavid du Colombier 	while(q > 0){
172*7ab27030SDavid du Colombier 		r = t->rs.r[q-1];
173*7ab27030SDavid du Colombier 		if(r == '\n'){		/* eat at most one more character */
174*7ab27030SDavid du Colombier 			if(q == t->q0)	/* eat the newline */
175*7ab27030SDavid du Colombier 				--q;
176*7ab27030SDavid du Colombier 			break;
177*7ab27030SDavid du Colombier 		}
178*7ab27030SDavid du Colombier 		if(c == 0x17){
179*7ab27030SDavid du Colombier 			eq = isalnum(r);
180*7ab27030SDavid du Colombier 			if(eq && skipping)	/* found one; stop skipping */
181*7ab27030SDavid du Colombier 				skipping = FALSE;
182*7ab27030SDavid du Colombier 			else if(!eq && !skipping)
183*7ab27030SDavid du Colombier 				break;
184*7ab27030SDavid du Colombier 		}
185*7ab27030SDavid du Colombier 		--q;
186*7ab27030SDavid du Colombier 	}
187*7ab27030SDavid du Colombier 	return t->q0-q;
188*7ab27030SDavid du Colombier }
189*7ab27030SDavid du Colombier 
190*7ab27030SDavid du Colombier void
texttype(Text * t,Rune r)191*7ab27030SDavid du Colombier texttype(Text *t, Rune r)
192*7ab27030SDavid du Colombier {
193*7ab27030SDavid du Colombier 	uint q0, q1;
194*7ab27030SDavid du Colombier 	int nb, n;
195*7ab27030SDavid du Colombier 	int nr;
196*7ab27030SDavid du Colombier 	Rune *rp;
197*7ab27030SDavid du Colombier 
198*7ab27030SDavid du Colombier 	if(t->what!=Textarea && r=='\n'){
199*7ab27030SDavid du Colombier 		if(t->what==Urltag)
200*7ab27030SDavid du Colombier 			get(t, t, XXX, XXX, nil, 0);
201*7ab27030SDavid du Colombier 		return;
202*7ab27030SDavid du Colombier 	}
203*7ab27030SDavid du Colombier 	if(t->what==Tag && (r==Kscrollonedown || r==Kscrolloneup))
204*7ab27030SDavid du Colombier 		return;
205*7ab27030SDavid du Colombier 
206*7ab27030SDavid du Colombier 	nr = 1;
207*7ab27030SDavid du Colombier 	rp = &r;
208*7ab27030SDavid du Colombier 	switch(r){
209*7ab27030SDavid du Colombier 	case Kleft:
210*7ab27030SDavid du Colombier 		if(t->q0 > 0)
211*7ab27030SDavid du Colombier 			textshow(t, t->q0-1, t->q0-1, TRUE);
212*7ab27030SDavid du Colombier 		return;
213*7ab27030SDavid du Colombier 	case Kright:
214*7ab27030SDavid du Colombier 		if(t->q1 < t->rs.nr)
215*7ab27030SDavid du Colombier 			textshow(t, t->q1+1, t->q1+1, TRUE);
216*7ab27030SDavid du Colombier 		return;
217*7ab27030SDavid du Colombier 	case Kdown:
218*7ab27030SDavid du Colombier 		n = t->maxlines/3;
219*7ab27030SDavid du Colombier 		goto case_Down;
220*7ab27030SDavid du Colombier 	case Kscrollonedown:
221*7ab27030SDavid du Colombier 		n = mousescrollsize(t->maxlines);
222*7ab27030SDavid du Colombier 		if(n <= 0)
223*7ab27030SDavid du Colombier 			n = 1;
224*7ab27030SDavid du Colombier 		goto case_Down;
225*7ab27030SDavid du Colombier 	case Kpgdown:
226*7ab27030SDavid du Colombier 		n = 2*t->maxlines/3;
227*7ab27030SDavid du Colombier 	case_Down:
228*7ab27030SDavid du Colombier 		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));
229*7ab27030SDavid du Colombier 		textsetorigin(t, q0, TRUE);
230*7ab27030SDavid du Colombier 		return;
231*7ab27030SDavid du Colombier 	case Kup:
232*7ab27030SDavid du Colombier 		n = t->maxlines/3;
233*7ab27030SDavid du Colombier 		goto case_Up;
234*7ab27030SDavid du Colombier 	case Kscrolloneup:
235*7ab27030SDavid du Colombier 		n = mousescrollsize(t->maxlines);
236*7ab27030SDavid du Colombier 		goto case_Up;
237*7ab27030SDavid du Colombier 	case Kpgup:
238*7ab27030SDavid du Colombier 		n = 2*t->maxlines/3;
239*7ab27030SDavid du Colombier 	case_Up:
240*7ab27030SDavid du Colombier 		q0 = textbacknl(t, t->org, n);
241*7ab27030SDavid du Colombier 		textsetorigin(t, q0, TRUE);
242*7ab27030SDavid du Colombier 		return;
243*7ab27030SDavid du Colombier 	case Khome:
244*7ab27030SDavid du Colombier 		textshow(t, 0, 0, FALSE);
245*7ab27030SDavid du Colombier 		return;
246*7ab27030SDavid du Colombier 	case Kend:
247*7ab27030SDavid du Colombier 		textshow(t, t->rs.nr, t->rs.nr, FALSE);
248*7ab27030SDavid du Colombier 		return;
249*7ab27030SDavid du Colombier 	case 0x01:	/* ^A: beginning of line */
250*7ab27030SDavid du Colombier 		/* go to where ^U would erase, if not already at BOL */
251*7ab27030SDavid du Colombier 		nb = 0;
252*7ab27030SDavid du Colombier 		if(t->q0>0 && t->rs.r[t->q0-1]!='\n')
253*7ab27030SDavid du Colombier 			nb = textbswidth(t, 0x15);
254*7ab27030SDavid du Colombier 		textshow(t, t->q0-nb, t->q0-nb, TRUE);
255*7ab27030SDavid du Colombier 		return;
256*7ab27030SDavid du Colombier 	case 0x05:	/* ^E: end of line */
257*7ab27030SDavid du Colombier 		q0 = t->q0;
258*7ab27030SDavid du Colombier 		while(q0<t->rs.nr && t->rs.r[q0]!='\n')
259*7ab27030SDavid du Colombier 			q0++;
260*7ab27030SDavid du Colombier 		textshow(t, q0, q0, TRUE);
261*7ab27030SDavid du Colombier 		return;
262*7ab27030SDavid du Colombier 	}
263*7ab27030SDavid du Colombier 	if(t->q1 > t->q0)
264*7ab27030SDavid du Colombier 		cut(t, t, TRUE, TRUE, nil, 0);
265*7ab27030SDavid du Colombier 
266*7ab27030SDavid du Colombier 	textshow(t, t->q0, t->q0, TRUE);
267*7ab27030SDavid du Colombier 	switch(r){
268*7ab27030SDavid du Colombier 	case 0x08:	/* ^H: erase character */
269*7ab27030SDavid du Colombier 	case 0x15:	/* ^U: erase line */
270*7ab27030SDavid du Colombier 	case 0x17:	/* ^W: erase word */
271*7ab27030SDavid du Colombier 		if(t->q0 == 0)	/* nothing to erase */
272*7ab27030SDavid du Colombier 			return;
273*7ab27030SDavid du Colombier 		nb = textbswidth(t, r);
274*7ab27030SDavid du Colombier 		q1 = t->q0;
275*7ab27030SDavid du Colombier 		q0 = q1-nb;
276*7ab27030SDavid du Colombier 		/* if selection is at beginning of window, avoid deleting invisible text */
277*7ab27030SDavid du Colombier 		if(q0 < t->org){
278*7ab27030SDavid du Colombier 			q0 = t->org;
279*7ab27030SDavid du Colombier 			nb = q1-q0;
280*7ab27030SDavid du Colombier 		}
281*7ab27030SDavid du Colombier 		if(nb > 0){
282*7ab27030SDavid du Colombier 			textdelete(t, q0, q0+nb);
283*7ab27030SDavid du Colombier 			textsetselect(t, q0, q0);
284*7ab27030SDavid du Colombier 		}
285*7ab27030SDavid du Colombier 		return;
286*7ab27030SDavid du Colombier 	}
287*7ab27030SDavid du Colombier 	/* otherwise ordinary character; just insert */
288*7ab27030SDavid du Colombier 	textinsert(t, t->q0, &r, 1);
289*7ab27030SDavid du Colombier 	if(rp != &r)
290*7ab27030SDavid du Colombier 		free(rp);
291*7ab27030SDavid du Colombier 	textsetselect(t, t->q0+nr, t->q0+nr);
292*7ab27030SDavid du Colombier 	if(t->what == Textarea)
293*7ab27030SDavid du Colombier 		textscrdraw(t);
294*7ab27030SDavid du Colombier }
295*7ab27030SDavid du Colombier 
296*7ab27030SDavid du Colombier static	Text	*clicktext;
297*7ab27030SDavid du Colombier static	uint	clickmsec;
298*7ab27030SDavid du Colombier static	Text	*selecttext;
299*7ab27030SDavid du Colombier static	uint	selectq;
300*7ab27030SDavid du Colombier 
301*7ab27030SDavid du Colombier /*
302*7ab27030SDavid du Colombier  * called from frame library
303*7ab27030SDavid du Colombier  */
304*7ab27030SDavid du Colombier void
framescroll(Frame * f,int dl)305*7ab27030SDavid du Colombier framescroll(Frame *f, int dl)
306*7ab27030SDavid du Colombier {
307*7ab27030SDavid du Colombier 	if(f != &selecttext->Frame)
308*7ab27030SDavid du Colombier 		error("frameselect not right frame");
309*7ab27030SDavid du Colombier 	textframescroll(selecttext, dl);
310*7ab27030SDavid du Colombier }
311*7ab27030SDavid du Colombier 
312*7ab27030SDavid du Colombier void
textframescroll(Text * t,int dl)313*7ab27030SDavid du Colombier textframescroll(Text *t, int dl)
314*7ab27030SDavid du Colombier {
315*7ab27030SDavid du Colombier 	uint q0;
316*7ab27030SDavid du Colombier 
317*7ab27030SDavid du Colombier 	if(dl == 0){
318*7ab27030SDavid du Colombier 		scrsleep(100);
319*7ab27030SDavid du Colombier 		return;
320*7ab27030SDavid du Colombier 	}
321*7ab27030SDavid du Colombier 	if(dl < 0){
322*7ab27030SDavid du Colombier 		q0 = textbacknl(t, t->org, -dl);
323*7ab27030SDavid du Colombier 		if(selectq > t->org+t->p0)
324*7ab27030SDavid du Colombier 			textsetselect(t, t->org+t->p0, selectq);
325*7ab27030SDavid du Colombier 		else
326*7ab27030SDavid du Colombier 			textsetselect(t, selectq, t->org+t->p0);
327*7ab27030SDavid du Colombier 	}else{
328*7ab27030SDavid du Colombier 		if(t->org+t->nchars == t->rs.nr)
329*7ab27030SDavid du Colombier 			return;
330*7ab27030SDavid du Colombier 		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height));
331*7ab27030SDavid du Colombier 		if(selectq > t->org+t->p1)
332*7ab27030SDavid du Colombier 			textsetselect(t, t->org+t->p1, selectq);
333*7ab27030SDavid du Colombier 		else
334*7ab27030SDavid du Colombier 			textsetselect(t, selectq, t->org+t->p1);
335*7ab27030SDavid du Colombier 	}
336*7ab27030SDavid du Colombier 	textsetorigin(t, q0, TRUE);
337*7ab27030SDavid du Colombier }
338*7ab27030SDavid du Colombier 
339*7ab27030SDavid du Colombier void
textselect(Text * t)340*7ab27030SDavid du Colombier textselect(Text *t)
341*7ab27030SDavid du Colombier {
342*7ab27030SDavid du Colombier 	uint q0, q1;
343*7ab27030SDavid du Colombier 	int b, x, y;
344*7ab27030SDavid du Colombier 	int state;
345*7ab27030SDavid du Colombier 
346*7ab27030SDavid du Colombier 	selecttext = t;
347*7ab27030SDavid du Colombier 	/*
348*7ab27030SDavid du Colombier 	 * To have double-clicking and chording, we double-click
349*7ab27030SDavid du Colombier 	 * immediately if it might make sense.
350*7ab27030SDavid du Colombier 	 */
351*7ab27030SDavid du Colombier 	b = mouse->buttons;
352*7ab27030SDavid du Colombier 	q0 = t->q0;
353*7ab27030SDavid du Colombier 	q1 = t->q1;
354*7ab27030SDavid du Colombier 	selectq = t->org+frcharofpt(t, mouse->xy);
355*7ab27030SDavid du Colombier 	if(clicktext==t && mouse->msec-clickmsec<500)
356*7ab27030SDavid du Colombier 	if(q0==q1 && selectq==q0){
357*7ab27030SDavid du Colombier 		textdoubleclick(t, &q0, &q1);
358*7ab27030SDavid du Colombier 		textsetselect(t, q0, q1);
359*7ab27030SDavid du Colombier 		flushimage(display, 1);
360*7ab27030SDavid du Colombier 		x = mouse->xy.x;
361*7ab27030SDavid du Colombier 		y = mouse->xy.y;
362*7ab27030SDavid du Colombier 		/* stay here until something interesting happens */
363*7ab27030SDavid du Colombier 		do
364*7ab27030SDavid du Colombier 			readmouse(mousectl);
365*7ab27030SDavid du Colombier 		while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
366*7ab27030SDavid du Colombier 		mouse->xy.x = x;	/* in case we're calling frselect */
367*7ab27030SDavid du Colombier 		mouse->xy.y = y;
368*7ab27030SDavid du Colombier 		q0 = t->q0;	/* may have changed */
369*7ab27030SDavid du Colombier 		q1 = t->q1;
370*7ab27030SDavid du Colombier 		selectq = q0;
371*7ab27030SDavid du Colombier 	}
372*7ab27030SDavid du Colombier 	if(mouse->buttons == b){
373*7ab27030SDavid du Colombier 		t->Frame.scroll = framescroll;
374*7ab27030SDavid du Colombier 		frselect(t, mousectl);
375*7ab27030SDavid du Colombier 		/* horrible botch: while asleep, may have lost selection altogether */
376*7ab27030SDavid du Colombier 		if(selectq > t->rs.nr)
377*7ab27030SDavid du Colombier 			selectq = t->org + t->p0;
378*7ab27030SDavid du Colombier 		t->Frame.scroll = nil;
379*7ab27030SDavid du Colombier 		if(selectq < t->org)
380*7ab27030SDavid du Colombier 			q0 = selectq;
381*7ab27030SDavid du Colombier 		else
382*7ab27030SDavid du Colombier 			q0 = t->org + t->p0;
383*7ab27030SDavid du Colombier 		if(selectq > t->org+t->nchars)
384*7ab27030SDavid du Colombier 			q1 = selectq;
385*7ab27030SDavid du Colombier 		else
386*7ab27030SDavid du Colombier 			q1 = t->org+t->p1;
387*7ab27030SDavid du Colombier 	}
388*7ab27030SDavid du Colombier 	if(q0 == q1){
389*7ab27030SDavid du Colombier 		if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){
390*7ab27030SDavid du Colombier 			textdoubleclick(t, &q0, &q1);
391*7ab27030SDavid du Colombier 			clicktext = nil;
392*7ab27030SDavid du Colombier 		}else{
393*7ab27030SDavid du Colombier 			clicktext = t;
394*7ab27030SDavid du Colombier 			clickmsec = mouse->msec;
395*7ab27030SDavid du Colombier 		}
396*7ab27030SDavid du Colombier 	}else
397*7ab27030SDavid du Colombier 		clicktext = nil;
398*7ab27030SDavid du Colombier 	textsetselect(t, q0, q1);
399*7ab27030SDavid du Colombier 	flushimage(display, 1);
400*7ab27030SDavid du Colombier 	state = 0;	/* undo when possible; +1 for cut, -1 for paste */
401*7ab27030SDavid du Colombier 	while(mouse->buttons){
402*7ab27030SDavid du Colombier 		mouse->msec = 0;
403*7ab27030SDavid du Colombier 		b = mouse->buttons;
404*7ab27030SDavid du Colombier 		if((b&1) && (b&6)){
405*7ab27030SDavid du Colombier 			if(b & 2){
406*7ab27030SDavid du Colombier 				if(state==-1 && t->what==Textarea){
407*7ab27030SDavid du Colombier 					textsetselect(t, q0, t->q0);
408*7ab27030SDavid du Colombier 					state = 0;
409*7ab27030SDavid du Colombier 				}else if(state != 1){
410*7ab27030SDavid du Colombier 					cut(t, t, TRUE, TRUE, nil, 0);
411*7ab27030SDavid du Colombier 					state = 1;
412*7ab27030SDavid du Colombier 				}
413*7ab27030SDavid du Colombier 			}else{
414*7ab27030SDavid du Colombier 				if(state==1 && t->what==Textarea){
415*7ab27030SDavid du Colombier 					textsetselect(t, q0, t->q1);
416*7ab27030SDavid du Colombier 					state = 0;
417*7ab27030SDavid du Colombier 				}else if(state != -1){
418*7ab27030SDavid du Colombier 					paste(t, t, TRUE, FALSE, nil, 0);
419*7ab27030SDavid du Colombier 					state = -1;
420*7ab27030SDavid du Colombier 				}
421*7ab27030SDavid du Colombier 			}
422*7ab27030SDavid du Colombier 			textscrdraw(t);
423*7ab27030SDavid du Colombier 		}
424*7ab27030SDavid du Colombier 		flushimage(display, 1);
425*7ab27030SDavid du Colombier 		while(mouse->buttons == b)
426*7ab27030SDavid du Colombier 			readmouse(mousectl);
427*7ab27030SDavid du Colombier 		clicktext = nil;
428*7ab27030SDavid du Colombier 	}
429*7ab27030SDavid du Colombier }
430*7ab27030SDavid du Colombier 
431*7ab27030SDavid du Colombier void
textshow(Text * t,uint q0,uint q1,int doselect)432*7ab27030SDavid du Colombier textshow(Text *t, uint q0, uint q1, int doselect)
433*7ab27030SDavid du Colombier {
434*7ab27030SDavid du Colombier 	int qe;
435*7ab27030SDavid du Colombier 	int nl;
436*7ab27030SDavid du Colombier 	uint q;
437*7ab27030SDavid du Colombier 
438*7ab27030SDavid du Colombier 	if(t->what != Textarea){
439*7ab27030SDavid du Colombier 		if(doselect)
440*7ab27030SDavid du Colombier 			textsetselect(t, q0, q1);
441*7ab27030SDavid du Colombier 		return;
442*7ab27030SDavid du Colombier 	}
443*7ab27030SDavid du Colombier 	if(doselect)
444*7ab27030SDavid du Colombier 		textsetselect(t, q0, q1);
445*7ab27030SDavid du Colombier 	qe = t->org+t->nchars;
446*7ab27030SDavid du Colombier 	if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->rs.nr)))
447*7ab27030SDavid du Colombier 		textscrdraw(t);
448*7ab27030SDavid du Colombier 	else{
449*7ab27030SDavid du Colombier 		nl = t->maxlines/4;
450*7ab27030SDavid du Colombier 		q = textbacknl(t, q0, nl);
451*7ab27030SDavid du Colombier 		/* avoid going backwards if trying to go forwards - long lines! */
452*7ab27030SDavid du Colombier 		if(!(q0>t->org && q<t->org))
453*7ab27030SDavid du Colombier 			textsetorigin(t, q, TRUE);
454*7ab27030SDavid du Colombier 		while(q0 > t->org+t->nchars)
455*7ab27030SDavid du Colombier 			textsetorigin(t, t->org+1, FALSE);
456*7ab27030SDavid du Colombier 	}
457*7ab27030SDavid du Colombier }
458*7ab27030SDavid du Colombier 
459*7ab27030SDavid du Colombier static
460*7ab27030SDavid du Colombier int
region(int a,int b)461*7ab27030SDavid du Colombier region(int a, int b)
462*7ab27030SDavid du Colombier {
463*7ab27030SDavid du Colombier 	if(a < b)
464*7ab27030SDavid du Colombier 		return -1;
465*7ab27030SDavid du Colombier 	if(a == b)
466*7ab27030SDavid du Colombier 		return 0;
467*7ab27030SDavid du Colombier 	return 1;
468*7ab27030SDavid du Colombier }
469*7ab27030SDavid du Colombier 
470*7ab27030SDavid du Colombier void
selrestore(Frame * f,Point pt0,uint p0,uint p1)471*7ab27030SDavid du Colombier selrestore(Frame *f, Point pt0, uint p0, uint p1)
472*7ab27030SDavid du Colombier {
473*7ab27030SDavid du Colombier 	if(p1<=f->p0 || p0>=f->p1){
474*7ab27030SDavid du Colombier 		/* no overlap */
475*7ab27030SDavid du Colombier 		frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);
476*7ab27030SDavid du Colombier 		return;
477*7ab27030SDavid du Colombier 	}
478*7ab27030SDavid du Colombier 	if(p0>=f->p0 && p1<=f->p1){
479*7ab27030SDavid du Colombier 		/* entirely inside */
480*7ab27030SDavid du Colombier 		frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
481*7ab27030SDavid du Colombier 		return;
482*7ab27030SDavid du Colombier 	}
483*7ab27030SDavid du Colombier 
484*7ab27030SDavid du Colombier 	/* they now are known to overlap */
485*7ab27030SDavid du Colombier 
486*7ab27030SDavid du Colombier 	/* before selection */
487*7ab27030SDavid du Colombier 	if(p0 < f->p0){
488*7ab27030SDavid du Colombier 		frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);
489*7ab27030SDavid du Colombier 		p0 = f->p0;
490*7ab27030SDavid du Colombier 		pt0 = frptofchar(f, p0);
491*7ab27030SDavid du Colombier 	}
492*7ab27030SDavid du Colombier 	/* after selection */
493*7ab27030SDavid du Colombier 	if(p1 > f->p1){
494*7ab27030SDavid du Colombier 		frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]);
495*7ab27030SDavid du Colombier 		p1 = f->p1;
496*7ab27030SDavid du Colombier 	}
497*7ab27030SDavid du Colombier 	/* inside selection */
498*7ab27030SDavid du Colombier 	frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
499*7ab27030SDavid du Colombier }
500*7ab27030SDavid du Colombier 
501*7ab27030SDavid du Colombier void
textsetselect(Text * t,uint q0,uint q1)502*7ab27030SDavid du Colombier textsetselect(Text *t, uint q0, uint q1)
503*7ab27030SDavid du Colombier {
504*7ab27030SDavid du Colombier 	int p0, p1;
505*7ab27030SDavid du Colombier 
506*7ab27030SDavid du Colombier 	/* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */
507*7ab27030SDavid du Colombier 	t->q0 = q0;
508*7ab27030SDavid du Colombier 	t->q1 = q1;
509*7ab27030SDavid du Colombier 	/* compute desired p0,p1 from q0,q1 */
510*7ab27030SDavid du Colombier 	p0 = q0-t->org;
511*7ab27030SDavid du Colombier 	p1 = q1-t->org;
512*7ab27030SDavid du Colombier 	if(p0 < 0)
513*7ab27030SDavid du Colombier 		p0 = 0;
514*7ab27030SDavid du Colombier 	if(p1 < 0)
515*7ab27030SDavid du Colombier 		p1 = 0;
516*7ab27030SDavid du Colombier 	if(p0 > t->nchars)
517*7ab27030SDavid du Colombier 		p0 = t->nchars;
518*7ab27030SDavid du Colombier 	if(p1 > t->nchars)
519*7ab27030SDavid du Colombier 		p1 = t->nchars;
520*7ab27030SDavid du Colombier 	if(p0==t->p0 && p1==t->p1)
521*7ab27030SDavid du Colombier 		return;
522*7ab27030SDavid du Colombier 	/* screen disagrees with desired selection */
523*7ab27030SDavid du Colombier 	if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){
524*7ab27030SDavid du Colombier 		/* no overlap or too easy to bother trying */
525*7ab27030SDavid du Colombier 		frdrawsel(t, frptofchar(t, t->p0), t->p0, t->p1, 0);
526*7ab27030SDavid du Colombier 		frdrawsel(t, frptofchar(t, p0), p0, p1, 1);
527*7ab27030SDavid du Colombier 		goto Return;
528*7ab27030SDavid du Colombier 	}
529*7ab27030SDavid du Colombier 	/* overlap; avoid unnecessary painting */
530*7ab27030SDavid du Colombier 	if(p0 < t->p0){
531*7ab27030SDavid du Colombier 		/* extend selection backwards */
532*7ab27030SDavid du Colombier 		frdrawsel(t, frptofchar(t, p0), p0, t->p0, 1);
533*7ab27030SDavid du Colombier 	}else if(p0 > t->p0){
534*7ab27030SDavid du Colombier 		/* trim first part of selection */
535*7ab27030SDavid du Colombier 		frdrawsel(t, frptofchar(t, t->p0), t->p0, p0, 0);
536*7ab27030SDavid du Colombier 	}
537*7ab27030SDavid du Colombier 	if(p1 > t->p1){
538*7ab27030SDavid du Colombier 		/* extend selection forwards */
539*7ab27030SDavid du Colombier 		frdrawsel(t, frptofchar(t, t->p1), t->p1, p1, 1);
540*7ab27030SDavid du Colombier 	}else if(p1 < t->p1){
541*7ab27030SDavid du Colombier 		/* trim last part of selection */
542*7ab27030SDavid du Colombier 		frdrawsel(t, frptofchar(t, p1), p1, t->p1, 0);
543*7ab27030SDavid du Colombier 	}
544*7ab27030SDavid du Colombier 
545*7ab27030SDavid du Colombier     Return:
546*7ab27030SDavid du Colombier 	t->p0 = p0;
547*7ab27030SDavid du Colombier 	t->p1 = p1;
548*7ab27030SDavid du Colombier }
549*7ab27030SDavid du Colombier 
550*7ab27030SDavid du Colombier 
551*7ab27030SDavid du Colombier /*
552*7ab27030SDavid du Colombier  * Release the button in less than DELAY ms and it's considered a null selection
553*7ab27030SDavid du Colombier  * if the mouse hardly moved, regardless of whether it crossed a char boundary.
554*7ab27030SDavid du Colombier  */
555*7ab27030SDavid du Colombier enum {
556*7ab27030SDavid du Colombier 	DELAY = 2,
557*7ab27030SDavid du Colombier 	MINMOVE = 4,
558*7ab27030SDavid du Colombier };
559*7ab27030SDavid du Colombier 
560*7ab27030SDavid du Colombier uint
xselect(Frame * f,Mousectl * mc,Image * col,uint * p1p)561*7ab27030SDavid du Colombier xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p)	/* when called, button is down */
562*7ab27030SDavid du Colombier {
563*7ab27030SDavid du Colombier 	uint p0, p1, q, tmp;
564*7ab27030SDavid du Colombier 	ulong msec;
565*7ab27030SDavid du Colombier 	Point mp, pt0, pt1, qt;
566*7ab27030SDavid du Colombier 	int reg, b;
567*7ab27030SDavid du Colombier 
568*7ab27030SDavid du Colombier 	mp = mc->xy;
569*7ab27030SDavid du Colombier 	b = mc->buttons;
570*7ab27030SDavid du Colombier 	msec = mc->msec;
571*7ab27030SDavid du Colombier 
572*7ab27030SDavid du Colombier 	/* remove tick */
573*7ab27030SDavid du Colombier 	if(f->p0 == f->p1)
574*7ab27030SDavid du Colombier 		frtick(f, frptofchar(f, f->p0), 0);
575*7ab27030SDavid du Colombier 	p0 = p1 = frcharofpt(f, mp);
576*7ab27030SDavid du Colombier 	pt0 = frptofchar(f, p0);
577*7ab27030SDavid du Colombier 	pt1 = frptofchar(f, p1);
578*7ab27030SDavid du Colombier 	reg = 0;
579*7ab27030SDavid du Colombier 	frtick(f, pt0, 1);
580*7ab27030SDavid du Colombier 	do{
581*7ab27030SDavid du Colombier 		q = frcharofpt(f, mc->xy);
582*7ab27030SDavid du Colombier 		if(p1 != q){
583*7ab27030SDavid du Colombier 			if(p0 == p1)
584*7ab27030SDavid du Colombier 				frtick(f, pt0, 0);
585*7ab27030SDavid du Colombier 			if(reg != region(q, p0)){	/* crossed starting point; reset */
586*7ab27030SDavid du Colombier 				if(reg > 0)
587*7ab27030SDavid du Colombier 					selrestore(f, pt0, p0, p1);
588*7ab27030SDavid du Colombier 				else if(reg < 0)
589*7ab27030SDavid du Colombier 					selrestore(f, pt1, p1, p0);
590*7ab27030SDavid du Colombier 				p1 = p0;
591*7ab27030SDavid du Colombier 				pt1 = pt0;
592*7ab27030SDavid du Colombier 				reg = region(q, p0);
593*7ab27030SDavid du Colombier 				if(reg == 0)
594*7ab27030SDavid du Colombier 					frdrawsel0(f, pt0, p0, p1, col, display->white);
595*7ab27030SDavid du Colombier 			}
596*7ab27030SDavid du Colombier 			qt = frptofchar(f, q);
597*7ab27030SDavid du Colombier 			if(reg > 0){
598*7ab27030SDavid du Colombier 				if(q > p1)
599*7ab27030SDavid du Colombier 					frdrawsel0(f, pt1, p1, q, col, display->white);
600*7ab27030SDavid du Colombier 
601*7ab27030SDavid du Colombier 				else if(q < p1)
602*7ab27030SDavid du Colombier 					selrestore(f, qt, q, p1);
603*7ab27030SDavid du Colombier 			}else if(reg < 0){
604*7ab27030SDavid du Colombier 				if(q > p1)
605*7ab27030SDavid du Colombier 					selrestore(f, pt1, p1, q);
606*7ab27030SDavid du Colombier 				else
607*7ab27030SDavid du Colombier 					frdrawsel0(f, qt, q, p1, col, display->white);
608*7ab27030SDavid du Colombier 			}
609*7ab27030SDavid du Colombier 			p1 = q;
610*7ab27030SDavid du Colombier 			pt1 = qt;
611*7ab27030SDavid du Colombier 		}
612*7ab27030SDavid du Colombier 		if(p0 == p1)
613*7ab27030SDavid du Colombier 			frtick(f, pt0, 1);
614*7ab27030SDavid du Colombier 		flushimage(f->display, 1);
615*7ab27030SDavid du Colombier 		readmouse(mc);
616*7ab27030SDavid du Colombier 	}while(mc->buttons == b);
617*7ab27030SDavid du Colombier 	if(mc->msec-msec < DELAY && p0!=p1
618*7ab27030SDavid du Colombier 	&& abs(mp.x-mc->xy.x)<MINMOVE
619*7ab27030SDavid du Colombier 	&& abs(mp.y-mc->xy.y)<MINMOVE) {
620*7ab27030SDavid du Colombier 		if(reg > 0)
621*7ab27030SDavid du Colombier 			selrestore(f, pt0, p0, p1);
622*7ab27030SDavid du Colombier 		else if(reg < 0)
623*7ab27030SDavid du Colombier 			selrestore(f, pt1, p1, p0);
624*7ab27030SDavid du Colombier 		p1 = p0;
625*7ab27030SDavid du Colombier 	}
626*7ab27030SDavid du Colombier 	if(p1 < p0){
627*7ab27030SDavid du Colombier 		tmp = p0;
628*7ab27030SDavid du Colombier 		p0 = p1;
629*7ab27030SDavid du Colombier 		p1 = tmp;
630*7ab27030SDavid du Colombier 	}
631*7ab27030SDavid du Colombier 	pt0 = frptofchar(f, p0);
632*7ab27030SDavid du Colombier 	if(p0 == p1)
633*7ab27030SDavid du Colombier 		frtick(f, pt0, 0);
634*7ab27030SDavid du Colombier 	selrestore(f, pt0, p0, p1);
635*7ab27030SDavid du Colombier 	/* restore tick */
636*7ab27030SDavid du Colombier 	if(f->p0 == f->p1)
637*7ab27030SDavid du Colombier 		frtick(f, frptofchar(f, f->p0), 1);
638*7ab27030SDavid du Colombier 	flushimage(f->display, 1);
639*7ab27030SDavid du Colombier 	*p1p = p1;
640*7ab27030SDavid du Colombier 	return p0;
641*7ab27030SDavid du Colombier }
642*7ab27030SDavid du Colombier 
643*7ab27030SDavid du Colombier int
textselect23(Text * t,uint * q0,uint * q1,Image * high,int mask)644*7ab27030SDavid du Colombier textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask)
645*7ab27030SDavid du Colombier {
646*7ab27030SDavid du Colombier 	uint p0, p1;
647*7ab27030SDavid du Colombier 	int buts;
648*7ab27030SDavid du Colombier 
649*7ab27030SDavid du Colombier 	p0 = xselect(t, mousectl, high, &p1);
650*7ab27030SDavid du Colombier 	buts = mousectl->buttons;
651*7ab27030SDavid du Colombier 	if((buts & mask) == 0){
652*7ab27030SDavid du Colombier 		*q0 = p0+t->org;
653*7ab27030SDavid du Colombier 		*q1 = p1+t->org;
654*7ab27030SDavid du Colombier 	}
655*7ab27030SDavid du Colombier 
656*7ab27030SDavid du Colombier 	while(mousectl->buttons)
657*7ab27030SDavid du Colombier 		readmouse(mousectl);
658*7ab27030SDavid du Colombier 	return buts;
659*7ab27030SDavid du Colombier }
660*7ab27030SDavid du Colombier 
661*7ab27030SDavid du Colombier int
textselect2(Text * t,uint * q0,uint * q1,Text ** tp)662*7ab27030SDavid du Colombier textselect2(Text *t, uint *q0, uint *q1, Text **tp)
663*7ab27030SDavid du Colombier {
664*7ab27030SDavid du Colombier 	int buts;
665*7ab27030SDavid du Colombier 
666*7ab27030SDavid du Colombier 	*tp = nil;
667*7ab27030SDavid du Colombier 	buts = textselect23(t, q0, q1, but2col, 4);
668*7ab27030SDavid du Colombier 	if(buts & 4)
669*7ab27030SDavid du Colombier 		return 0;
670*7ab27030SDavid du Colombier 	if(buts & 1){	/* pick up argument */
671*7ab27030SDavid du Colombier 		*tp = argtext;
672*7ab27030SDavid du Colombier 		return 1;
673*7ab27030SDavid du Colombier 	}
674*7ab27030SDavid du Colombier 	return 1;
675*7ab27030SDavid du Colombier }
676*7ab27030SDavid du Colombier 
677*7ab27030SDavid du Colombier int
textselect3(Text * t,uint * q0,uint * q1)678*7ab27030SDavid du Colombier textselect3(Text *t, uint *q0, uint *q1)
679*7ab27030SDavid du Colombier {
680*7ab27030SDavid du Colombier 	int h;
681*7ab27030SDavid du Colombier 
682*7ab27030SDavid du Colombier 	h = (textselect23(t, q0, q1, but3col, 1|2) == 0);
683*7ab27030SDavid du Colombier 	return h;
684*7ab27030SDavid du Colombier }
685*7ab27030SDavid du Colombier 
686*7ab27030SDavid du Colombier static Rune left1[] =  { L'{', L'[', L'(', L'<', L'«', 0 };
687*7ab27030SDavid du Colombier static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
688*7ab27030SDavid du Colombier static Rune left2[] =  { L'\n', 0 };
689*7ab27030SDavid du Colombier static Rune left3[] =  { L'\'', L'"', L'`', 0 };
690*7ab27030SDavid du Colombier 
691*7ab27030SDavid du Colombier static
692*7ab27030SDavid du Colombier Rune *left[] = {
693*7ab27030SDavid du Colombier 	left1,
694*7ab27030SDavid du Colombier 	left2,
695*7ab27030SDavid du Colombier 	left3,
696*7ab27030SDavid du Colombier 	nil
697*7ab27030SDavid du Colombier };
698*7ab27030SDavid du Colombier static
699*7ab27030SDavid du Colombier Rune *right[] = {
700*7ab27030SDavid du Colombier 	right1,
701*7ab27030SDavid du Colombier 	left2,
702*7ab27030SDavid du Colombier 	left3,
703*7ab27030SDavid du Colombier 	nil
704*7ab27030SDavid du Colombier };
705*7ab27030SDavid du Colombier 
706*7ab27030SDavid du Colombier void
textdoubleclick(Text * t,uint * q0,uint * q1)707*7ab27030SDavid du Colombier textdoubleclick(Text *t, uint *q0, uint *q1)
708*7ab27030SDavid du Colombier {
709*7ab27030SDavid du Colombier 	int c, i;
710*7ab27030SDavid du Colombier 	Rune *r, *l, *p;
711*7ab27030SDavid du Colombier 	uint q;
712*7ab27030SDavid du Colombier 
713*7ab27030SDavid du Colombier 	if(t->rs.nr == 0)
714*7ab27030SDavid du Colombier 		return;
715*7ab27030SDavid du Colombier 
716*7ab27030SDavid du Colombier 	for(i=0; left[i]!=nil; i++){
717*7ab27030SDavid du Colombier 		q = *q0;
718*7ab27030SDavid du Colombier 		l = left[i];
719*7ab27030SDavid du Colombier 		r = right[i];
720*7ab27030SDavid du Colombier 		/* try matching character to left, looking right */
721*7ab27030SDavid du Colombier 		if(q == 0)
722*7ab27030SDavid du Colombier 			c = '\n';
723*7ab27030SDavid du Colombier 		else
724*7ab27030SDavid du Colombier 			c = t->rs.r[q-1];
725*7ab27030SDavid du Colombier 		p = runestrchr(l, c);
726*7ab27030SDavid du Colombier 		if(p != nil){
727*7ab27030SDavid du Colombier 			if(textclickmatch(t, c, t->rs.r[p-l], 1, &q))
728*7ab27030SDavid du Colombier 				*q1 = q-(c!='\n');
729*7ab27030SDavid du Colombier 			return;
730*7ab27030SDavid du Colombier 		}
731*7ab27030SDavid du Colombier 		/* try matching character to right, looking left */
732*7ab27030SDavid du Colombier 		if(q == t->rs.nr)
733*7ab27030SDavid du Colombier 			c = '\n';
734*7ab27030SDavid du Colombier 		else
735*7ab27030SDavid du Colombier 			c = t->rs.r[q];
736*7ab27030SDavid du Colombier 		p = runestrchr(r, c);
737*7ab27030SDavid du Colombier 		if(p != nil){
738*7ab27030SDavid du Colombier 			if(textclickmatch(t, c, l[p-r], -1, &q)){
739*7ab27030SDavid du Colombier 				*q1 = *q0+(*q0<t->rs.nr && c=='\n');
740*7ab27030SDavid du Colombier 				*q0 = q;
741*7ab27030SDavid du Colombier 				if(c!='\n' || q!=0 || t->rs.r[0]=='\n')
742*7ab27030SDavid du Colombier 					(*q0)++;
743*7ab27030SDavid du Colombier 			}
744*7ab27030SDavid du Colombier 			return;
745*7ab27030SDavid du Colombier 		}
746*7ab27030SDavid du Colombier 	}
747*7ab27030SDavid du Colombier 	/* try filling out word to right */
748*7ab27030SDavid du Colombier 	while(*q1<t->rs.nr && isalnum(t->rs.r[*q1]))
749*7ab27030SDavid du Colombier 		(*q1)++;
750*7ab27030SDavid du Colombier 	/* try filling out word to left */
751*7ab27030SDavid du Colombier 	while(*q0>0 && isalnum(t->rs.r[*q0-1]))
752*7ab27030SDavid du Colombier 		(*q0)--;
753*7ab27030SDavid du Colombier }
754*7ab27030SDavid du Colombier 
755*7ab27030SDavid du Colombier int
textclickmatch(Text * t,int cl,int cr,int dir,uint * q)756*7ab27030SDavid du Colombier textclickmatch(Text *t, int cl, int cr, int dir, uint *q)
757*7ab27030SDavid du Colombier {
758*7ab27030SDavid du Colombier 	Rune c;
759*7ab27030SDavid du Colombier 	int nest;
760*7ab27030SDavid du Colombier 
761*7ab27030SDavid du Colombier 	nest = 1;
762*7ab27030SDavid du Colombier 	for(;;){
763*7ab27030SDavid du Colombier 		if(dir > 0){
764*7ab27030SDavid du Colombier 			if(*q == t->rs.nr)
765*7ab27030SDavid du Colombier 				break;
766*7ab27030SDavid du Colombier 			c = t->rs.r[*q];
767*7ab27030SDavid du Colombier 			(*q)++;
768*7ab27030SDavid du Colombier 		}else{
769*7ab27030SDavid du Colombier 			if(*q == 0)
770*7ab27030SDavid du Colombier 				break;
771*7ab27030SDavid du Colombier 			(*q)--;
772*7ab27030SDavid du Colombier 			c = t->rs.r[*q];
773*7ab27030SDavid du Colombier 		}
774*7ab27030SDavid du Colombier 		if(c == cr){
775*7ab27030SDavid du Colombier 			if(--nest==0)
776*7ab27030SDavid du Colombier 				return 1;
777*7ab27030SDavid du Colombier 		}else if(c == cl)
778*7ab27030SDavid du Colombier 			nest++;
779*7ab27030SDavid du Colombier 	}
780*7ab27030SDavid du Colombier 	return cl=='\n' && nest==1;
781*7ab27030SDavid du Colombier }
782*7ab27030SDavid du Colombier 
783*7ab27030SDavid du Colombier uint
textbacknl(Text * t,uint p,uint n)784*7ab27030SDavid du Colombier textbacknl(Text *t, uint p, uint n)
785*7ab27030SDavid du Colombier {
786*7ab27030SDavid du Colombier 	int i, j;
787*7ab27030SDavid du Colombier 
788*7ab27030SDavid du Colombier 	/* look for start of this line if n==0 */
789*7ab27030SDavid du Colombier 	if(n==0 && p>0 && t->rs.r[p-1]!='\n')
790*7ab27030SDavid du Colombier 		n = 1;
791*7ab27030SDavid du Colombier 	i = n;
792*7ab27030SDavid du Colombier 	while(i-->0 && p>0){
793*7ab27030SDavid du Colombier 		--p;	/* it's at a newline now; back over it */
794*7ab27030SDavid du Colombier 		if(p == 0)
795*7ab27030SDavid du Colombier 			break;
796*7ab27030SDavid du Colombier 		/* at 128 chars, call it a line anyway */
797*7ab27030SDavid du Colombier 		for(j=128; --j>0 && p>0; p--)
798*7ab27030SDavid du Colombier 			if(t->rs.r[p-1]=='\n')
799*7ab27030SDavid du Colombier 				break;
800*7ab27030SDavid du Colombier 	}
801*7ab27030SDavid du Colombier 	return p;
802*7ab27030SDavid du Colombier }
803*7ab27030SDavid du Colombier 
804*7ab27030SDavid du Colombier void
textsetorigin(Text * t,uint org,int exact)805*7ab27030SDavid du Colombier textsetorigin(Text *t, uint org, int exact)
806*7ab27030SDavid du Colombier {
807*7ab27030SDavid du Colombier 	int i, a, fixup;
808*7ab27030SDavid du Colombier 	Rune *r;
809*7ab27030SDavid du Colombier 	uint n;
810*7ab27030SDavid du Colombier 
811*7ab27030SDavid du Colombier 	if(org>0 && !exact){
812*7ab27030SDavid du Colombier 		/* org is an estimate of the char posn; find a newline */
813*7ab27030SDavid du Colombier 		/* don't try harder than 256 chars */
814*7ab27030SDavid du Colombier 		for(i=0; i<256 && org<t->rs.nr; i++){
815*7ab27030SDavid du Colombier 			if(t->rs.r[org] == '\n'){
816*7ab27030SDavid du Colombier 				org++;
817*7ab27030SDavid du Colombier 				break;
818*7ab27030SDavid du Colombier 			}
819*7ab27030SDavid du Colombier 			org++;
820*7ab27030SDavid du Colombier 		}
821*7ab27030SDavid du Colombier 	}
822*7ab27030SDavid du Colombier 	a = org-t->org;
823*7ab27030SDavid du Colombier 	fixup = 0;
824*7ab27030SDavid du Colombier 	if(a>=0 && a<t->nchars){
825*7ab27030SDavid du Colombier 		frdelete(t, 0, a);
826*7ab27030SDavid du Colombier 		fixup = 1;	/* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
827*7ab27030SDavid du Colombier 	}else if(a<0 && -a<t->nchars){
828*7ab27030SDavid du Colombier 		n = t->org - org;
829*7ab27030SDavid du Colombier 		r = runemalloc(n);
830*7ab27030SDavid du Colombier 		runemove(r, t->rs.r+org, n);
831*7ab27030SDavid du Colombier 		frinsert(t, r, r+n, 0);
832*7ab27030SDavid du Colombier 		free(r);
833*7ab27030SDavid du Colombier 	}else
834*7ab27030SDavid du Colombier 		frdelete(t, 0, t->nchars);
835*7ab27030SDavid du Colombier 	t->org = org;
836*7ab27030SDavid du Colombier 	textfill(t);
837*7ab27030SDavid du Colombier 	textscrdraw(t);
838*7ab27030SDavid du Colombier 	textsetselect(t, t->q0, t->q1);
839*7ab27030SDavid du Colombier 	if(fixup && t->p1 > t->p0)
840*7ab27030SDavid du Colombier 		frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1);
841*7ab27030SDavid du Colombier }
842*7ab27030SDavid du Colombier 
843*7ab27030SDavid du Colombier void
textset(Text * t,Rune * r,int n)844*7ab27030SDavid du Colombier textset(Text *t, Rune*r, int n)
845*7ab27030SDavid du Colombier {
846*7ab27030SDavid du Colombier 	textdelete(t, 0, t->rs.nr);
847*7ab27030SDavid du Colombier 	textinsert(t, 0, r, n);
848*7ab27030SDavid du Colombier 	textsetselect(t, t->q0, t->q1);
849*7ab27030SDavid du Colombier }
850*7ab27030SDavid du Colombier 
851*7ab27030SDavid du Colombier void
textmouse(Text * t,Point xy,int but)852*7ab27030SDavid du Colombier textmouse(Text *t, Point xy, int but)
853*7ab27030SDavid du Colombier {
854*7ab27030SDavid du Colombier 	Text *argt;
855*7ab27030SDavid du Colombier 	uint q0, q1;
856*7ab27030SDavid du Colombier 
857*7ab27030SDavid du Colombier 	if(ptinrect(xy, t->scrollr)){
858*7ab27030SDavid du Colombier 		if(t->what == Columntag)
859*7ab27030SDavid du Colombier 			rowdragcol(&row, t->col, but);
860*7ab27030SDavid du Colombier 		else if(t->what == Tag)
861*7ab27030SDavid du Colombier 			coldragwin(t->col, t->w, but);
862*7ab27030SDavid du Colombier 		else if(t->what == Textarea)
863*7ab27030SDavid du Colombier 			textscroll(t, but);
864*7ab27030SDavid du Colombier 		if(t->col)
865*7ab27030SDavid du Colombier 			activecol = t->col;
866*7ab27030SDavid du Colombier 		return;
867*7ab27030SDavid du Colombier 	}
868*7ab27030SDavid du Colombier 	if(but == 1){
869*7ab27030SDavid du Colombier 		selpage = nil;
870*7ab27030SDavid du Colombier 		textselect(t);
871*7ab27030SDavid du Colombier 		argtext = t;
872*7ab27030SDavid du Colombier 		seltext = t;
873*7ab27030SDavid du Colombier 	}else if(but == 2){
874*7ab27030SDavid du Colombier 		if(textselect2(t, &q0, &q1, &argt))
875*7ab27030SDavid du Colombier 			execute(t, q0, q1, argt);
876*7ab27030SDavid du Colombier 	}else if(but == 3){
877*7ab27030SDavid du Colombier 		if(textselect3(t, &q0, &q1))
878*7ab27030SDavid du Colombier 			look3(t, q0, q1);
879*7ab27030SDavid du Colombier 	}
880*7ab27030SDavid du Colombier }
881