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
textinit(Text * t,Image * b,Rectangle r,Font * f,Image * cols[NCOL])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
textredraw(Text * t,Rectangle r,Font * f,Image * b)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
textresize(Text * t,Image * b,Rectangle r)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
textclose(Text * t)67 textclose(Text *t)
68 {
69 closerunestr(&t->rs);
70 frclear(t, 1);
71 }
72
73 void
textinsert(Text * t,uint q0,Rune * r,uint n)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
textfill(Text * t)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
textdelete(Text * t,uint q0,uint q1)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
textbswidth(Text * t,Rune c)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
texttype(Text * t,Rune r)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
framescroll(Frame * f,int dl)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
textframescroll(Text * t,int dl)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
textselect(Text * t)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
textshow(Text * t,uint q0,uint q1,int doselect)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
region(int a,int b)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
selrestore(Frame * f,Point pt0,uint p0,uint p1)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
textsetselect(Text * t,uint q0,uint q1)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
xselect(Frame * f,Mousectl * mc,Image * col,uint * p1p)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
textselect23(Text * t,uint * q0,uint * q1,Image * high,int mask)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
textselect2(Text * t,uint * q0,uint * q1,Text ** tp)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
textselect3(Text * t,uint * q0,uint * q1)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
textdoubleclick(Text * t,uint * q0,uint * q1)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
textclickmatch(Text * t,int cl,int cr,int dir,uint * q)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
textbacknl(Text * t,uint p,uint n)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
textsetorigin(Text * t,uint org,int exact)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
textset(Text * t,Rune * r,int n)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
textmouse(Text * t,Point xy,int but)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 }
881