16 Image *textcols[NCOL];
17 static Rune Ldot[] = { '.', 0 };
20 TABDIR = 3 /* width of tabs in directory windows */
24 textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
29 t->scrollr.max.x = r.min.x+Scrollwid;
31 r.min.x += Scrollwid+Scrollgap;
36 memmove(t->fr.cols, cols, sizeof t->fr.cols);
37 textredraw(t, r, rf->f, screen, -1);
41 textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx)
46 frinit(&t->fr, r, f, b, t->fr.cols);
48 rr.min.x -= Scrollwid; /* back fill to scroll bar */
49 draw(t->fr.b, rr, t->fr.cols[BACK], nil, ZP);
50 /* use no wider than 3-space tabs in a directory */
54 maxt = min(TABDIR, maxtab);
58 t->fr.maxtab = maxt*stringwidth(f, "0");
59 if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){
60 if(t->fr.maxlines > 0){
62 textcolumnate(t, t->w->dlp, t->w->ndl);
67 textsetselect(t, t->q0, t->q1);
72 textresize(Text *t, Rectangle r)
77 r.max.y -= Dy(r)%t->fr.font->height;
83 t->scrollr.max.x = r.min.x+Scrollwid;
85 r.min.x += Scrollwid+Scrollgap;
87 textredraw(t, r, t->fr.font, screen, odx);
96 filedeltext(t->file, t);
112 dircmp(const void *a, const void *b)
119 n = min(da->nr, db->nr);
120 i = memcmp(da->r, db->r, n*sizeof(Rune));
123 return da->nr - db->nr;
127 textcolumnate(Text *t, Dirlist **dlp, int ndl)
129 int i, j, w, colw, mint, maxt, ncol, nrow;
132 static Rune Lnl[] = { '\n', 0 };
133 static Rune Ltab[] = { '\t', 0 };
135 if(t->file->ntext > 1)
137 mint = stringwidth(t->fr.font, "0");
138 /* go for narrower tabs if set more than 3 wide */
139 t->fr.maxtab = min(maxtab, TABDIR)*mint;
142 for(i=0; i<ndl; i++){
145 if(maxt-w%maxt < mint || w%maxt==0)
155 ncol = max(1, Dx(t->fr.r)/colw);
156 nrow = (ndl+ncol-1)/ncol;
159 for(i=0; i<nrow; i++){
160 for(j=i; j<ndl; j+=nrow){
162 fileinsert(t->file, q1, dl->r, dl->nr);
167 if(maxt-w%maxt < mint){
168 fileinsert(t->file, q1, Ltab, 1);
173 fileinsert(t->file, q1, Ltab, 1);
178 fileinsert(t->file, q1, Lnl, 1);
184 textload(Text *t, uint q0, char *file, int setqid)
188 int fd, i, j, n, ndl, nulls;
194 if(t->ncache!=0 || t->file->b.nc || t->w==nil || t!=&t->w->body || (t->w->isdir && t->file->nname==0))
196 fd = open(file, OREAD);
198 warning(nil, "can't open %s: %r\n", file);
203 warning(nil, "can't fstat %s: %r\n", file);
207 if(d->qid.type & QTDIR){
208 /* this is checked in get() but it's possible the file changed underfoot */
209 if(t->file->ntext > 1){
210 warning(nil, "%s is a directory; can't read with multiple windows on it\n", file);
214 t->w->filemenu = FALSE;
215 if(t->file->name[t->file->nname-1] != '/'){
216 rp = runemalloc(t->file->nname+1);
217 runemove(rp, t->file->name, t->file->nname);
218 rp[t->file->nname] = '/';
219 winsetname(t->w, rp, t->file->nname+1);
225 while((n=dirread(fd, &dbuf)) > 0){
227 if(nodotfiles && dbuf[i].name[0] == '.')
229 dl = emalloc(sizeof(Dirlist));
230 j = strlen(dbuf[i].name);
231 tmp = emalloc(j+1+1);
232 memmove(tmp, dbuf[i].name, j);
233 if(dbuf[i].qid.type & QTDIR)
236 dl->r = bytetorune(tmp, &dl->nr);
237 dl->wid = stringwidth(t->fr.font, tmp);
240 dlp = realloc(dlp, ndl*sizeof(Dirlist*));
245 qsort(dlp, ndl, sizeof(Dirlist*), dircmp);
248 textcolumnate(t, dlp, ndl);
252 t->w->filemenu = TRUE;
253 q1 = q0 + fileload(t->file, q0, fd, &nulls);
256 t->file->dev = d->dev;
257 t->file->mtime = d->mtime;
258 t->file->qidpath = d->qid.path;
262 for(q=q0; q<q1; q+=n){
266 bufread(&t->file->b, q, rp, n);
269 else if(q <= t->org+t->fr.nchars)
270 frinsert(&t->fr, rp, rp+n, q-t->org);
271 if(t->fr.lastlinefull)
275 for(i=0; i<t->file->ntext; i++){
276 u = t->file->text[i];
278 if(u->org > u->file->b.nc) /* will be 0 because of reset(), but safety first */
280 textresize(u, u->all);
281 textbacknl(u, u->org, 0); /* go to beginning of line */
283 textsetselect(u, q0, q0);
286 warning(nil, "%s: NUL bytes elided\n", file);
296 textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp)
301 if(t->what == Tag){ /* can't happen but safety first: mustn't backspace over file name */
303 textinsert(t, q0, r, n, tofile);
329 textdelete(t, q0, q0+initial, tofile);
332 textinsert(t, q0, tp, n, tofile);
341 textinsert(Text *t, uint q0, Rune *r, uint n, int tofile)
346 if(tofile && t->ncache != 0)
347 error("text.insert");
351 fileinsert(t->file, q0, r, n);
354 t->w->utflastqid = -1;
356 if(t->file->ntext > 1)
357 for(i=0; i<t->file->ntext; i++){
358 u = t->file->text[i];
360 u->w->dirty = TRUE; /* always a body */
361 textinsert(u, q0, r, n, FALSE);
362 textsetselect(u, u->q0, u->q1);
374 else if(q0 <= t->org+t->fr.nchars)
375 frinsert(&t->fr, r, r+n, q0-t->org);
381 winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r);
383 winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n);
394 if(t->fr.lastlinefull || t->nofill)
404 n = t->file->b.nc-(t->org+t->fr.nchars);
407 if(n > 2000) /* educated guess at reasonable amount */
409 bufread(&t->file->b, t->org+t->fr.nchars, rp, n);
411 * it's expensive to frinsert more than we need, so
414 nl = t->fr.maxlines-t->fr.nlines;
423 frinsert(&t->fr, rp, rp+i, t->fr.nchars);
424 }while(t->fr.lastlinefull == FALSE);
429 textdelete(Text *t, uint q0, uint q1, int tofile)
435 if(tofile && t->ncache != 0)
436 error("text.delete");
441 filedelete(t->file, q0, q1);
444 t->w->utflastqid = -1;
446 if(t->file->ntext > 1)
447 for(i=0; i<t->file->ntext; i++){
448 u = t->file->text[i];
450 u->w->dirty = TRUE; /* always a body */
451 textdelete(u, q0, q1, FALSE);
452 textsetselect(u, u->q0, u->q1);
458 t->q0 -= min(n, t->q0-q0);
460 t->q1 -= min(n, t->q1-q0);
463 else if(q0 < t->org+t->fr.nchars){
465 if(p1 > t->fr.nchars)
472 frdelete(&t->fr, p0, p1);
479 winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);
484 textconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1)
486 *p0 = min(q0, t->file->b.nc);
487 *p1 = min(q1, t->file->b.nc);
491 textreadc(Text *t, uint q)
495 if(t->cq0<=q && q<t->cq0+t->ncache)
496 r = t->cache[q-t->cq0];
498 bufread(&t->file->b, q, &r, 1);
503 textbswidth(Text *t, Rune c)
509 /* there is known to be at least one character to erase */
510 if(c == 0x08) /* ^H: erase character */
515 r = textreadc(t, q-1);
516 if(r == '\n'){ /* eat at most one more character */
517 if(q == t->q0) /* eat the newline */
523 if(eq && skipping) /* found one; stop skipping */
525 else if(!eq && !skipping)
534 textfilewidth(Text *t, uint q0, int oneelement)
541 r = textreadc(t, q-1);
544 if(oneelement && r=='/')
552 textcomplete(Text *t)
563 /* control-f: filename completion; works back to white space or / */
564 if(t->q0<t->file->b.nc && textreadc(t, t->q0)>' ') /* must be at end of word */
566 nstr = textfilewidth(t, t->q0, TRUE);
567 str = runemalloc(nstr);
568 npath = textfilewidth(t, t->q0-nstr, FALSE);
569 path = runemalloc(npath);
576 for(i=0; i<nstr; i++)
577 str[i] = textreadc(t, q++);
578 q = t->q0-nstr-npath;
579 for(i=0; i<npath; i++)
580 path[i] = textreadc(t, q++);
581 /* is path rooted? if not, we need to make it relative to window path */
582 if(npath>0 && path[0]=='/')
583 dir = runestr(path, npath);
585 dir = dirname(t, nil, 0);
586 if(dir.nr + 1 + npath > nelem(tmp)){
592 dir.r = runestrdup(Ldot);
594 runemove(tmp, dir.r, dir.nr);
596 runemove(tmp+dir.nr+1, path, npath);
600 dir = cleanrname(dir);
603 s = smprint("%.*S", nstr, str);
604 dirs = smprint("%.*S", dir.nr, dir.r);
605 c = complete(dirs, s);
608 warning(nil, "error attempting completion: %r\n");
613 warning(nil, "%.*S%s%.*S*%s\n",
615 dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "",
617 c->nmatch ? "" : ": no matches in:");
618 for(i=0; i<c->nfile; i++)
619 warning(nil, " %s\n", c->filename[i]);
623 rp = runesmprint("%s", c->string);
635 texttype(Text *t, Rune r)
643 if(t->what!=Body && r=='\n')
651 textshow(t, t->q0-1, t->q0-1, TRUE);
655 if(t->q1 < t->file->b.nc){
657 textshow(t, t->q1+1, t->q1+1, TRUE);
661 n = t->fr.maxlines/3;
664 n = 2*t->fr.maxlines/3;
666 q0 = t->org+frcharofpt(&t->fr, Pt(t->fr.r.min.x, t->fr.r.min.y+n*t->fr.font->height));
667 textsetorigin(t, q0, FALSE);
670 n = t->fr.maxlines/3;
673 n = 2*t->fr.maxlines/3;
675 q0 = textbacknl(t, t->org, n);
676 textsetorigin(t, q0, FALSE);
679 textshow(t, 0, 0, FALSE);
686 textshow(t, t->file->b.nc, t->file->b.nc, FALSE);
696 cut(t, t, nil, TRUE, TRUE, nil, 0);
699 textshow(t, t->q0, t->q0, 1);
701 case 0x06: /* ^F: complete */
703 rp = textcomplete(t);
707 break; /* fall through to normal insertion case */
710 textsetselect(t, t->eq0, t->q0);
718 case 0x08: /* ^H: erase character */
719 case 0x15: /* ^U: erase line */
720 case 0x17: /* ^W: erase word */
721 if(t->q0 == 0) /* nothing to erase */
723 nnb = textbswidth(t, r);
726 /* if selection is at beginning of window, avoid deleting invisible text */
733 for(i=0; i<t->file->ntext; i++){
734 u = t->file->text[i];
740 error("text.type backspace");
744 textdelete(u, q1-n, q1, FALSE);
747 if(u->eq0==q1 || u->eq0==~0)
750 textdelete(u, q0, q0+nb, TRUE);
752 textsetselect(u, u->q0, u->q1);
754 textsetselect(t, q0, q0);
757 for(i=0; i<t->file->ntext; i++)
758 textfill(t->file->text[i]);
761 if(t->w->autoindent){
762 /* find beginning of previous line using backspace code */
763 nnb = textbswidth(t, 0x15); /* ^U case */
764 rp = runemalloc(nnb + 1);
767 for(i=0; i<nnb; i++){
768 r = textreadc(t, t->q0-nnb+i);
769 if(r != ' ' && r != '\t')
774 break; /* fall through to normal code */
776 /* otherwise ordinary character; just insert, typically in caches of all texts */
777 for(i=0; i<t->file->ntext; i++){
778 u = t->file->text[i];
783 else if(t->q0 != u->cq0+u->ncache)
784 error("text.type cq1");
785 textinsert(u, t->q0, rp, nr, FALSE);
787 textsetselect(u, u->q0, u->q1);
788 if(u->ncache+nr > u->ncachealloc){
789 u->ncachealloc += 10 + nr;
790 u->cache = runerealloc(u->cache, u->ncachealloc);
792 runemove(u->cache+u->ncache, rp, nr);
797 textsetselect(t, t->q0+nr, t->q0+nr);
798 if(r=='\n' && t->w!=nil)
803 textcommit(Text *t, int tofile)
808 fileinsert(t->file, t->cq0, t->cache, t->ncache);
811 t->w->utflastqid = -1;
816 static Text *clicktext;
817 static uint clickmsec;
818 static Text *selecttext;
822 * called from frame library
825 framescroll(Frame *f, int dl)
827 if(f != &selecttext->fr)
828 error("frameselect not right frame");
829 textframescroll(selecttext, dl);
833 textframescroll(Text *t, int dl)
842 q0 = textbacknl(t, t->org, -dl);
843 if(selectq > t->org+t->fr.p0)
844 textsetselect(t, t->org+t->fr.p0, selectq);
846 textsetselect(t, selectq, t->org+t->fr.p0);
848 if(t->org+t->fr.nchars == t->file->b.nc)
850 q0 = t->org+frcharofpt(&t->fr, Pt(t->fr.r.min.x, t->fr.r.min.y+dl*t->fr.font->height));
851 if(selectq > t->org+t->fr.p1)
852 textsetselect(t, t->org+t->fr.p1, selectq);
854 textsetselect(t, selectq, t->org+t->fr.p1);
856 textsetorigin(t, q0, TRUE);
869 * To have double-clicking and chording, we double-click
870 * immediately if it might make sense.
875 selectq = t->org+frcharofpt(&t->fr, mouse->xy);
876 if(clicktext==t && mouse->msec-clickmsec<500)
877 if(q0==q1 && selectq==q0){
878 textdoubleclick(t, &q0, &q1);
879 textsetselect(t, q0, q1);
880 flushimage(display, 1);
883 /* stay here until something interesting happens */
886 while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
887 mouse->xy.x = x; /* in case we're calling frselect */
889 q0 = t->q0; /* may have changed */
893 if(mouse->buttons == b){
894 t->fr.scroll = framescroll;
895 frselect(&t->fr, mousectl);
896 /* horrible botch: while asleep, may have lost selection altogether */
897 if(selectq > t->file->b.nc)
898 selectq = t->org + t->fr.p0;
903 q0 = t->org + t->fr.p0;
904 if(selectq > t->org+t->fr.nchars)
907 q1 = t->org+t->fr.p1;
910 if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){
911 textdoubleclick(t, &q0, &q1);
915 clickmsec = mouse->msec;
919 textsetselect(t, q0, q1);
920 flushimage(display, 1);
921 state = op = 0; /* undo when possible; +1 for cut, -1 for paste */
922 while(mouse->buttons){
926 if(state==0 && op==0 && t->what==Body){
928 filemark(t->w->body.file);
931 if(state==-1 && t->what==Body){
933 textsetselect(t, q0, t->q0);
935 }else if(state != 1 && op != -1){
936 cut(t, t, nil, TRUE, TRUE, nil, 0);
940 if(state==1 && t->what==Body){
942 textsetselect(t, q0, t->q1);
944 }else if(state != -1 && op != 1){
945 paste(t, t, nil, TRUE, FALSE, nil, 0);
952 flushimage(display, 1);
953 while(mouse->buttons == b)
960 textshow(Text *t, uint q0, uint q1, int doselect)
968 if(t->w!=nil && t->fr.maxlines==0)
969 colgrow(t->col, t->w, 1);
971 textsetselect(t, q0, q1);
972 qe = t->org+t->fr.nchars;
973 if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->file->b.nc+t->ncache)))
976 if(t->w->nopen[QWevent] > 0)
977 nl = 3*t->fr.maxlines/4;
979 nl = t->fr.maxlines/4;
980 q = textbacknl(t, q0, nl);
981 /* avoid going backwards if trying to go forwards - long lines! */
982 if(!(q0>t->org && q<t->org))
983 textsetorigin(t, q, TRUE);
984 while(q0 > t->org+t->fr.nchars)
985 textsetorigin(t, t->org+1, FALSE);
1001 selrestore(Frame *f, Point pt0, uint p0, uint p1)
1003 if(p1<=f->p0 || p0>=f->p1){
1005 frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);
1008 if(p0>=f->p0 && p1<=f->p1){
1009 /* entirely inside */
1010 frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
1014 /* they now are known to overlap */
1016 /* before selection */
1018 frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);
1020 pt0 = frptofchar(f, p0);
1022 /* after selection */
1024 frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]);
1027 /* inside selection */
1028 frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
1032 textsetselect(Text *t, uint q0, uint q1)
1036 /* t->fr.p0 and t->fr.p1 are always right; t->q0 and t->q1 may be off */
1039 /* compute desired p0,p1 from q0,q1 */
1046 if(p0 > t->fr.nchars)
1048 if(p1 > t->fr.nchars)
1050 if(p0==t->fr.p0 && p1==t->fr.p1)
1052 /* screen disagrees with desired selection */
1053 if(t->fr.p1<=p0 || p1<=t->fr.p0 || p0==p1 || t->fr.p1==t->fr.p0){
1054 /* no overlap or too easy to bother trying */
1055 frdrawsel(&t->fr, frptofchar(&t->fr, t->fr.p0), t->fr.p0, t->fr.p1, 0);
1056 frdrawsel(&t->fr, frptofchar(&t->fr, p0), p0, p1, 1);
1059 /* overlap; avoid unnecessary painting */
1061 /* extend selection backwards */
1062 frdrawsel(&t->fr, frptofchar(&t->fr, p0), p0, t->fr.p0, 1);
1063 }else if(p0 > t->fr.p0){
1064 /* trim first part of selection */
1065 frdrawsel(&t->fr, frptofchar(&t->fr, t->fr.p0), t->fr.p0, p0, 0);
1068 /* extend selection forwards */
1069 frdrawsel(&t->fr, frptofchar(&t->fr, t->fr.p1), t->fr.p1, p1, 1);
1070 }else if(p1 < t->fr.p1){
1071 /* trim last part of selection */
1072 frdrawsel(&t->fr, frptofchar(&t->fr, p1), p1, t->fr.p1, 0);
1081 * Release the button in less than DELAY ms and it's considered a null selection
1082 * if the mouse hardly moved, regardless of whether it crossed a char boundary.
1090 xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p) /* when called, button is down */
1092 uint p0, p1, q, tmp;
1094 Point mp, pt0, pt1, qt;
1103 frtick(f, frptofchar(f, f->p0), 0);
1104 p0 = p1 = frcharofpt(f, mp);
1105 pt0 = frptofchar(f, p0);
1106 pt1 = frptofchar(f, p1);
1110 q = frcharofpt(f, mc->m.xy);
1114 if(reg != region(q, p0)){ /* crossed starting point; reset */
1116 selrestore(f, pt0, p0, p1);
1118 selrestore(f, pt1, p1, p0);
1121 reg = region(q, p0);
1123 frdrawsel0(f, pt0, p0, p1, col, display->white);
1125 qt = frptofchar(f, q);
1128 frdrawsel0(f, pt1, p1, q, col, display->white);
1131 selrestore(f, qt, q, p1);
1134 selrestore(f, pt1, p1, q);
1136 frdrawsel0(f, qt, q, p1, col, display->white);
1143 flushimage(f->display, 1);
1145 }while(mc->m.buttons == b);
1146 if(mc->m.msec-msec < DELAY && p0!=p1
1147 && abs(mp.x-mc->m.xy.x)<MINMOVE
1148 && abs(mp.y-mc->m.xy.y)<MINMOVE) {
1150 selrestore(f, pt0, p0, p1);
1152 selrestore(f, pt1, p1, p0);
1160 pt0 = frptofchar(f, p0);
1163 selrestore(f, pt0, p0, p1);
1166 frtick(f, frptofchar(f, f->p0), 1);
1167 flushimage(f->display, 1);
1173 textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask)
1178 p0 = xselect(&t->fr, mousectl, high, &p1);
1179 buts = mousectl->m.buttons;
1180 if((buts & mask) == 0){
1185 while(mousectl->m.buttons)
1186 readmouse(mousectl);
1191 textselect2(Text *t, uint *q0, uint *q1, Text **tp)
1196 buts = textselect23(t, q0, q1, but2col, 4);
1199 if(buts & 1){ /* pick up argument */
1207 textselect3(Text *t, uint *q0, uint *q1)
1211 h = (textselect23(t, q0, q1, but3col, 1|2) == 0);
1215 static Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
1216 static Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
1217 static Rune left2[] = { '\n', 0 };
1218 static Rune left3[] = { '\'', '"', '`', 0 };
1236 textdoubleclick(Text *t, uint *q0, uint *q1)
1242 for(i=0; left[i]!=nil; i++){
1246 /* try matching character to left, looking right */
1250 c = textreadc(t, q-1);
1251 p = runestrchr(l, c);
1253 if(textclickmatch(t, c, r[p-l], 1, &q))
1257 /* try matching character to right, looking left */
1258 if(q == t->file->b.nc)
1261 c = textreadc(t, q);
1262 p = runestrchr(r, c);
1264 if(textclickmatch(t, c, l[p-r], -1, &q)){
1265 *q1 = *q0+(*q0<t->file->b.nc && c=='\n');
1267 if(c!='\n' || q!=0 || textreadc(t, 0)=='\n')
1273 /* try filling out word to right */
1274 while(*q1<t->file->b.nc && isalnum(textreadc(t, *q1)))
1276 /* try filling out word to left */
1277 while(*q0>0 && isalnum(textreadc(t, *q0-1)))
1282 textclickmatch(Text *t, int cl, int cr, int dir, uint *q)
1290 if(*q == t->file->b.nc)
1292 c = textreadc(t, *q);
1298 c = textreadc(t, *q);
1306 return cl=='\n' && nest==1;
1310 textbacknl(Text *t, uint p, uint n)
1314 /* look for start of this line if n==0 */
1315 if(n==0 && p>0 && textreadc(t, p-1)!='\n')
1318 while(i-->0 && p>0){
1319 --p; /* it's at a newline now; back over it */
1322 /* at 128 chars, call it a line anyway */
1323 for(j=128; --j>0 && p>0; p--)
1324 if(textreadc(t, p-1)=='\n')
1331 textsetorigin(Text *t, uint org, int exact)
1337 if(org>0 && !exact){
1338 /* org is an estimate of the char posn; find a newline */
1339 /* don't try harder than 256 chars */
1340 for(i=0; i<256 && org<t->file->b.nc; i++){
1341 if(textreadc(t, org) == '\n'){
1350 if(a>=0 && a<t->fr.nchars){
1351 frdelete(&t->fr, 0, a);
1352 fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
1354 else if(a<0 && -a<t->fr.nchars){
1357 bufread(&t->file->b, org, r, n);
1358 frinsert(&t->fr, r, r+n, 0);
1361 frdelete(&t->fr, 0, t->fr.nchars);
1365 textsetselect(t, t->q0, t->q1);
1366 if(fixup && t->fr.p1 > t->fr.p0)
1367 frdrawsel(&t->fr, frptofchar(&t->fr, t->fr.p1-1), t->fr.p1-1, t->fr.p1, 1);
1375 /* do t->delete(0, t->nc, TRUE) without building backup stuff */
1376 textsetselect(t, t->org, t->org);
1377 frdelete(&t->fr, 0, t->fr.nchars);
1382 bufreset(&t->file->b);