Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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