/* * like fileunsetname, but get the data from arguments */ void logsetname(File *f, String *s) { Undo u; Buffer *delta; if(f->rescuing) return; if(f->unread){ /* This is setting initial file name */ filesetname(f, s); return; } if(f->seq < seq) filemark(f); /* undo a file name change by restoring old name */ delta = &f->epsilon; u.type = Filename; u.mod = TRUE; u.seq = f->seq; u.p0 = 0; /* unused */ u.n = s->n; if(s->n) bufinsert(delta, delta->nc, s->s, s->n); bufinsert(delta, delta->nc, (Rune*)&u, Undosize); if(!f->unread && !f->mod) state(f, Dirty); }
void logdelete(File *f, uint p0, uint p1) { if(f->rescuing) return; if(p0 == p1) return; if(f->seq < seq) filemark(f); if(p0 < f->hiposn) error(Esequence); if(merge.f != f || p0-(merge.p0+merge.n)>Maxmerge /* too far */ || merge.nbuf+(p0-(merge.p0+merge.n))>=RBUFSIZE){ /* too long */ flushmerge(); merge.f = f; merge.p0 = p0; merge.seq = f->seq; } mergeextend(f, p0); /* add to deletion */ merge.n = p1-merge.p0; f->hiposn = p1; if(!f->unread && !f->mod) state(f, Dirty); }
/* * like fileundelete, but get the data from arguments */ void loginsert(File *f, uint p0, Rune *s, uint ns) { if(f->rescuing) return; if(ns == 0) return; if(ns>STRSIZE) panic("loginsert"); if(f->seq < seq) filemark(f); if(p0 < f->hiposn) error(Esequence); if(merge.f != f || p0-(merge.p0+merge.n)>Maxmerge /* too far */ || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE) /* too long */ flushmerge(); if(ns>=RBUFSIZE){ if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil)) panic("loginsert bad merge state"); wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns); }else{ if(merge.f != f){ merge.f = f; merge.p0 = p0; merge.seq = f->seq; } mergeextend(f, p0); /* append string to merge */ runemove(merge.buf+merge.nbuf, s, ns); merge.nbuf += ns; } f->hiposn = p0; if(!f->unread && !f->mod) state(f, Dirty); }
void textselect(Text *t) { uint q0, q1; int b, x, y; int state; selecttext = t; /* * To have double-clicking and chording, we double-click * immediately if it might make sense. */ b = mouse->buttons; q0 = t->q0; q1 = t->q1; selectq = t->org+frcharofpt(t, mouse->xy); if(clicktext==t && mouse->msec-clickmsec<500) if(q0==q1 && selectq==q0){ textdoubleclick(t, &q0, &q1); textsetselect(t, q0, q1); flushimage(display, 1); x = mouse->xy.x; y = mouse->xy.y; /* stay here until something interesting happens */ do readmouse(mousectl); while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3); mouse->xy.x = x; /* in case we're calling frselect */ mouse->xy.y = y; q0 = t->q0; /* may have changed */ q1 = t->q1; selectq = q0; } if(mouse->buttons == b){ t->Frame.scroll = framescroll; frselect(t, mousectl); /* horrible botch: while asleep, may have lost selection altogether */ if(selectq > t->file->nc) selectq = t->org + t->p0; t->Frame.scroll = nil; if(selectq < t->org) q0 = selectq; else q0 = t->org + t->p0; if(selectq > t->org+t->nchars) q1 = selectq; else q1 = t->org+t->p1; } if(q0 == q1){ if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){ textdoubleclick(t, &q0, &q1); clicktext = nil; }else{ clicktext = t; clickmsec = mouse->msec; } }else clicktext = nil; textsetselect(t, q0, q1); flushimage(display, 1); state = 0; /* undo when possible; +1 for cut, -1 for paste */ while(mouse->buttons){ mouse->msec = 0; b = mouse->buttons; if((b&1) && (b&6)){ if(state==0 && t->what==Body){ seq++; filemark(t->w->body.file); } if(b & 2){ if(state==-1 && t->what==Body){ winundo(t->w, TRUE); textsetselect(t, q0, t->q0); state = 0; }else if(state != 1){ cut(t, t, nil, TRUE, TRUE, nil, 0); state = 1; } }else{ if(state==1 && t->what==Body){ winundo(t->w, TRUE); textsetselect(t, q0, t->q1); state = 0; }else if(state != -1){ paste(t, t, nil, TRUE, FALSE, nil, 0); state = -1; } } textscrdraw(t); clearmouse(); } flushimage(display, 1); while(mouse->buttons == b) readmouse(mousectl); clicktext = nil; } }
void texttype(Text *t, Rune r) { uint q0, q1; int nnb, nb, n, i; int nr; Rune *rp; Text *u; if(t->what!=Body && r=='\n') return; nr = 1; rp = &r; switch(r){ case Kleft: if(t->q0 > 0){ typecommit(t); textshow(t, t->q0-1, t->q0-1, TRUE); } return; case Kright: if(t->q1 < t->file->nc){ typecommit(t); textshow(t, t->q1+1, t->q1+1, TRUE); } return; case Kdown: n = t->maxlines/3; goto case_Down; case Kscrollonedown: n = mousescrollsize(t->maxlines); if(n <= 0) n = 1; goto case_Down; case Kpgdown: n = 2*t->maxlines/3; case_Down: q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height)); textsetorigin(t, q0, TRUE); return; case Kup: n = t->maxlines/3; goto case_Up; case Kscrolloneup: n = mousescrollsize(t->maxlines); goto case_Up; case Kpgup: n = 2*t->maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, TRUE); return; case Khome: typecommit(t); textshow(t, 0, 0, FALSE); return; case Kend: typecommit(t); textshow(t, t->file->nc, t->file->nc, FALSE); return; case 0x01: /* ^A: beginning of line */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); textshow(t, t->q0-nnb, t->q0-nnb, TRUE); return; case 0x05: /* ^E: end of line */ typecommit(t); q0 = t->q0; while(q0<t->file->nc && textreadc(t, q0)!='\n') q0++; textshow(t, q0, q0, TRUE); return; } if(t->what == Body){ seq++; filemark(t->file); } if(t->q1 > t->q0){ if(t->ncache != 0) error("text.type"); cut(t, t, nil, TRUE, TRUE, nil, 0); t->eq0 = ~0; } textshow(t, t->q0, t->q0, 1); switch(r){ case 0x06: case Kins: rp = textcomplete(t); if(rp == nil) return; nr = runestrlen(rp); break; /* fall through to normal insertion case */ case 0x1B: if(t->eq0 != ~0) textsetselect(t, t->eq0, t->q0); if(t->ncache > 0) typecommit(t); return; case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nnb = textbswidth(t, r); q1 = t->q0; q0 = q1-nnb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nnb = q1-q0; } if(nnb <= 0) return; for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; u->nofill = TRUE; nb = nnb; n = u->ncache; if(n > 0){ if(q1 != u->cq0+n) error("text.type backspace"); if(n > nb) n = nb; u->ncache -= n; textdelete(u, q1-n, q1, FALSE); nb -= n; } if(u->eq0==q1 || u->eq0==~0) u->eq0 = q0; if(nb && u==t) textdelete(u, q0, q0+nb, TRUE); if(u != t) textsetselect(u, u->q0, u->q1); else textsetselect(t, q0, q0); u->nofill = FALSE; } for(i=0; i<t->file->ntext; i++) textfill(t->file->text[i]); return; case '\n': if(t->w->autoindent){ /* find beginning of previous line using backspace code */ nnb = textbswidth(t, 0x15); /* ^U case */ rp = runemalloc(nnb + 1); nr = 0; rp[nr++] = r; for(i=0; i<nnb; i++){ r = textreadc(t, t->q0-nnb+i); if(r != ' ' && r != '\t') break; rp[nr++] = r; } } break; /* fall through to normal code */ } /* otherwise ordinary character; just insert, typically in caches of all texts */ for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u->eq0 == ~0) u->eq0 = t->q0; if(u->ncache == 0) u->cq0 = t->q0; else if(t->q0 != u->cq0+u->ncache) error("text.type cq1"); textinsert(u, t->q0, rp, nr, FALSE); if(u != t) textsetselect(u, u->q0, u->q1); if(u->ncache+nr > u->ncachealloc){ u->ncachealloc += 10 + nr; u->cache = runerealloc(u->cache, u->ncachealloc); } runemove(u->cache+u->ncache, rp, nr); u->ncache += nr; } if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(r=='\n' && t->w!=nil) wincommit(t->w, t); }
void xfidopen(Xfid *x) { Fcall fc; Window *w; Text *t; char *s; Rune *r; int m, n, q, q0, q1; w = x->f->w; t = &w->body; q = FILE(x->f->qid); if(w){ winlock(w, 'E'); switch(q){ case QWaddr: if(w->nopen[q]++ == 0){ w->addr = range(0, 0); w->limit = range(-1,-1); } break; case QWdata: case QWxdata: w->nopen[q]++; break; case QWevent: if(w->nopen[q]++ == 0){ if(!w->isdir && w->col!=nil){ w->filemenu = FALSE; winsettag(w); } } break; case QWrdsel: /* * Use a temporary file. * A pipe would be the obvious, but we can't afford the * broken pipe notification. Using the code to read QWbody * is n², which should probably also be fixed. Even then, * though, we'd need to squirrel away the data in case it's * modified during the operation, e.g. by |sort */ if(w->rdselfd > 0){ winunlock(w); respond(x, &fc, Einuse); return; } w->rdselfd = tempfile(); if(w->rdselfd < 0){ winunlock(w); respond(x, &fc, "can't create temp file"); return; } w->nopen[q]++; q0 = t->q0; q1 = t->q1; r = fbufalloc(); s = fbufalloc(); while(q0 < q1){ n = q1 - q0; if(n > BUFSIZE/UTFmax) n = BUFSIZE/UTFmax; bufread(&t->file->b, q0, r, n); m = snprint(s, BUFSIZE+1, "%.*S", n, r); if(write(w->rdselfd, s, m) != m){ warning(nil, "can't write temp file for pipe command %r\n"); break; } q0 += n; } fbuffree(s); fbuffree(r); break; case QWwrsel: w->nopen[q]++; seq++; filemark(t->file); cut(t, t, nil, FALSE, TRUE, nil, 0); w->wrselrange = range(t->q1, t->q1); w->nomark = TRUE; break; case QWeditout: if(editing == FALSE){ winunlock(w); respond(x, &fc, Eperm); return; } if(!canqlock(&w->editoutlk)){ winunlock(w); respond(x, &fc, Einuse); return; } w->wrselrange = range(t->q1, t->q1); break; } winunlock(w); } else{ switch(q){ case Qeditout: if(!canqlock(&editoutlk)){ respond(x, &fc, Einuse); return; } break; } } fc.qid = x->f->qid; fc.iounit = messagesize-IOHDRSZ; x->f->open = TRUE; respond(x, &fc, nil); }
void xfidctlwrite(Xfid *x, Window *w) { Fcall fc; int i, m, n, nb, nr, nulls; Rune *r; char *err, *p, *pp, *q, *e; int isfbuf, scrdraw, settag; Text *t; err = nil; e = x->fcall.data+x->fcall.count; scrdraw = FALSE; settag = FALSE; isfbuf = TRUE; if(x->fcall.count < RBUFSIZE) r = fbufalloc(); else{ isfbuf = FALSE; r = emalloc(x->fcall.count*UTFmax+1); } x->fcall.data[x->fcall.count] = 0; textcommit(&w->tag, TRUE); for(n=0; n<x->fcall.count; n+=m){ p = x->fcall.data+n; if(strncmp(p, "lock", 4) == 0){ /* make window exclusive use */ qlock(&w->ctllock); w->ctlfid = x->f->fid; m = 4; }else if(strncmp(p, "unlock", 6) == 0){ /* release exclusive use */ w->ctlfid = ~0; qunlock(&w->ctllock); m = 6; }else if(strncmp(p, "clean", 5) == 0){ /* mark window 'clean', seq=0 */ t = &w->body; t->eq0 = ~0; filereset(t->file); t->file->mod = FALSE; w->dirty = FALSE; settag = TRUE; m = 5; }else if(strncmp(p, "dirty", 5) == 0){ /* mark window 'dirty' */ t = &w->body; /* doesn't change sequence number, so "Put" won't appear. it shouldn't. */ t->file->mod = TRUE; w->dirty = TRUE; settag = TRUE; m = 5; }else if(strncmp(p, "show", 4) == 0){ /* show dot */ t = &w->body; textshow(t, t->q0, t->q1, 1); m = 4; }else if(strncmp(p, "name ", 5) == 0){ /* set file name */ pp = p+5; m = 5; q = memchr(pp, '\n', e-pp); if(q==nil || q==pp){ err = Ebadctl; break; } *q = 0; nulls = FALSE; cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); if(nulls){ err = "nulls in file name"; break; } for(i=0; i<nr; i++) if(r[i] <= ' '){ err = "bad character in file name"; goto out; } out: seq++; filemark(w->body.file); winsetname(w, r, nr); m += (q+1) - pp; }else if(strncmp(p, "dump ", 5) == 0){ /* set dump string */ pp = p+5; m = 5; q = memchr(pp, '\n', e-pp); if(q==nil || q==pp){ err = Ebadctl; break; } *q = 0; nulls = FALSE; cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); if(nulls){ err = "nulls in dump string"; break; } w->dumpstr = runetobyte(r, nr); m += (q+1) - pp; }else if(strncmp(p, "dumpdir ", 8) == 0){ /* set dump directory */ pp = p+8; m = 8; q = memchr(pp, '\n', e-pp); if(q==nil || q==pp){ err = Ebadctl; break; } *q = 0; nulls = FALSE; cvttorunes(pp, q-pp, r, &nb, &nr, &nulls); if(nulls){ err = "nulls in dump directory string"; break; } w->dumpdir = runetobyte(r, nr); m += (q+1) - pp; }else if(strncmp(p, "delete", 6) == 0){ /* delete for sure */ colclose(w->col, w, TRUE); m = 6; }else if(strncmp(p, "del", 3) == 0){ /* delete, but check dirty */ if(!winclean(w, TRUE)){ err = "file dirty"; break; } colclose(w->col, w, TRUE); m = 3; }else if(strncmp(p, "get", 3) == 0){ /* get file */ get(&w->body, nil, nil, FALSE, XXX, nil, 0); m = 3; }else if(strncmp(p, "put", 3) == 0){ /* put file */ put(&w->body, nil, nil, XXX, XXX, nil, 0); m = 3; }else if(strncmp(p, "dot=addr", 8) == 0){ /* set dot */ textcommit(&w->body, TRUE); clampaddr(w); w->body.q0 = w->addr.q0; w->body.q1 = w->addr.q1; textsetselect(&w->body, w->body.q0, w->body.q1); settag = TRUE; m = 8; }else if(strncmp(p, "addr=dot", 8) == 0){ /* set addr */ w->addr.q0 = w->body.q0; w->addr.q1 = w->body.q1; m = 8; }else if(strncmp(p, "limit=addr", 10) == 0){ /* set limit */ textcommit(&w->body, TRUE); clampaddr(w); w->limit.q0 = w->addr.q0; w->limit.q1 = w->addr.q1; m = 10; }else if(strncmp(p, "nomark", 6) == 0){ /* turn off automatic marking */ w->nomark = TRUE; m = 6; }else if(strncmp(p, "mark", 4) == 0){ /* mark file */ seq++; filemark(w->body.file); settag = TRUE; m = 4; }else if(strncmp(p, "nomenu", 6) == 0){ /* turn off automatic menu */ w->filemenu = FALSE; m = 6; }else if(strncmp(p, "menu", 4) == 0){ /* enable automatic menu */ w->filemenu = TRUE; m = 4; }else if(strncmp(p, "noscroll", 8) == 0){ /* turn off automatic scrolling */ w->noscroll = TRUE; m = 8; }else if(strncmp(p, "cleartag", 8) == 0){ /* wipe tag right of bar */ wincleartag(w); settag = TRUE; m = 8; }else if(strncmp(p, "scroll", 6) == 0){ /* turn on automatic scrolling (writes to body only) */ w->noscroll = FALSE; m = 6; }else{ err = Ebadctl; break; } while(p[m] == '\n') m++; } if(isfbuf) fbuffree(r); else free(r); if(err) n = 0; fc.count = n; respond(x, &fc, err); if(settag) winsettag(w); if(scrdraw) textscrdraw(&w->body); }
void xfidwrite(Xfid *x) { Fcall fc; int c, cnt, qid, q, nb, nr, eval; char buf[64], *err; Window *w; Rune *r; Range a; Text *t; uint q0, tq0, tq1; qid = FILE(x->f->qid); w = x->f->w; if(w){ c = 'F'; if(qid==QWtag || qid==QWbody) c = 'E'; winlock(w, c); if(w->col == nil){ winunlock(w); respond(x, &fc, Edel); return; } } x->fcall.data[x->fcall.count] = 0; switch(qid){ case Qcons: w = errorwin(x->f->mntdir, 'X'); t=&w->body; goto BodyTag; case Qlabel: fc.count = x->fcall.count; respond(x, &fc, nil); break; case QWaddr: x->fcall.data[x->fcall.count] = 0; r = bytetorune(x->fcall.data, &nr); t = &w->body; wincommit(w, t); eval = TRUE; a = address(FALSE, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb); free(r); if(nb < nr){ respond(x, &fc, Ebadaddr); break; } if(!eval){ respond(x, &fc, Eaddr); break; } w->addr = a; fc.count = x->fcall.count; respond(x, &fc, nil); break; case Qeditout: case QWeditout: r = bytetorune(x->fcall.data, &nr); if(w) err = edittext(w, w->wrselrange.q1, r, nr); else err = edittext(nil, 0, r, nr); free(r); if(err != nil){ respond(x, &fc, err); break; } fc.count = x->fcall.count; respond(x, &fc, nil); break; case QWerrors: w = errorwinforwin(w); t = &w->body; goto BodyTag; case QWbody: case QWwrsel: t = &w->body; goto BodyTag; case QWctl: xfidctlwrite(x, w); break; case QWdata: a = w->addr; t = &w->body; wincommit(w, t); if(a.q0>t->file->b.nc || a.q1>t->file->b.nc){ respond(x, &fc, Eaddr); break; } r = runemalloc(x->fcall.count); cvttorunes(x->fcall.data, x->fcall.count, r, &nb, &nr, nil); if(w->nomark == FALSE){ seq++; filemark(t->file); } q0 = a.q0; if(a.q1 > q0){ textdelete(t, q0, a.q1, TRUE); w->addr.q1 = q0; } tq0 = t->q0; tq1 = t->q1; textinsert(t, q0, r, nr, TRUE); if(tq0 >= q0) tq0 += nr; if(tq1 >= q0) tq1 += nr; textsetselect(t, tq0, tq1); if(!t->w->noscroll) textshow(t, q0, q0+nr, 0); textscrdraw(t); winsettag(w); free(r); w->addr.q0 += nr; w->addr.q1 = w->addr.q0; fc.count = x->fcall.count; respond(x, &fc, nil); break; case QWevent: xfideventwrite(x, w); break; case QWtag: t = &w->tag; goto BodyTag; BodyTag: q = x->f->nrpart; cnt = x->fcall.count; if(q > 0){ memmove(x->fcall.data+q, x->fcall.data, cnt); /* there's room; see fsysproc */ memmove(x->fcall.data, x->f->rpart, q); cnt += q; x->f->nrpart = 0; } r = runemalloc(cnt); cvttorunes(x->fcall.data, cnt-UTFmax, r, &nb, &nr, nil); /* approach end of buffer */ while(fullrune(x->fcall.data+nb, cnt-nb)){ c = nb; nb += chartorune(&r[nr], x->fcall.data+c); if(r[nr]) nr++; } if(nb < cnt){ memmove(x->f->rpart, x->fcall.data+nb, cnt-nb); x->f->nrpart = cnt-nb; } if(nr > 0){ wincommit(w, t); if(qid == QWwrsel){ q0 = w->wrselrange.q1; if(q0 > t->file->b.nc) q0 = t->file->b.nc; }else q0 = t->file->b.nc; if(qid == QWtag) textinsert(t, q0, r, nr, TRUE); else{ if(w->nomark == FALSE){ seq++; filemark(t->file); } q0 = textbsinsert(t, q0, r, nr, TRUE, &nr); textsetselect(t, t->q0, t->q1); /* insert could leave it somewhere else */ if(qid!=QWwrsel && !t->w->noscroll) textshow(t, q0+nr, q0+nr, 1); textscrdraw(t); } winsettag(w); if(qid == QWwrsel) w->wrselrange.q1 += nr; free(r); } fc.count = x->fcall.count; respond(x, &fc, nil); break; default: sprint(buf, "unknown qid %d in write", qid); respond(x, &fc, buf); break; } if(w) winunlock(w); }
void texttype(Text *t, Rune r) { uint q0, q1; int nnb, nb, n, i; int nr; Rune *rp; Text *u; if(t->what!=Body && t->what!=Tag && r=='\n') return; if(t->what == Tag) t->w->tagsafe = FALSE; nr = 1; rp = &r; switch(r){ case Kleft: typecommit(t); if(t->q0 > 0) textshow(t, t->q0-1, t->q0-1, TRUE); return; case Kright: typecommit(t); if(t->q1 < t->file->b.nc) textshow(t, t->q1+1, t->q1+1, TRUE); return; case Kdown: if(t->what == Tag) goto Tagdown; n = t->fr.maxlines/3; goto case_Down; case Kscrollonedown: if(t->what == Tag) goto Tagdown; n = mousescrollsize(t->fr.maxlines); if(n <= 0) n = 1; goto case_Down; case Kpgdown: n = 2*t->fr.maxlines/3; case_Down: q0 = t->org+frcharofpt(&t->fr, Pt(t->fr.r.min.x, t->fr.r.min.y+n*t->fr.font->height)); textsetorigin(t, q0, TRUE); return; case Kup: if(t->what == Tag) goto Tagup; n = t->fr.maxlines/3; goto case_Up; case Kscrolloneup: if(t->what == Tag) goto Tagup; n = mousescrollsize(t->fr.maxlines); goto case_Up; case Kpgup: n = 2*t->fr.maxlines/3; case_Up: q0 = textbacknl(t, t->org, n); textsetorigin(t, q0, TRUE); return; case Khome: typecommit(t); if(t->org > t->iq1) { q0 = textbacknl(t, t->iq1, 1); textsetorigin(t, q0, TRUE); } else textshow(t, 0, 0, FALSE); return; case Kend: typecommit(t); if(t->iq1 > t->org+t->fr.nchars) { if(t->iq1 > t->file->b.nc) { // should not happen, but does. and it will crash textbacknl. t->iq1 = t->file->b.nc; } q0 = textbacknl(t, t->iq1, 1); textsetorigin(t, q0, TRUE); } else textshow(t, t->file->b.nc, t->file->b.nc, FALSE); return; case 0x01: /* ^A: beginning of line */ typecommit(t); /* go to where ^U would erase, if not already at BOL */ nnb = 0; if(t->q0>0 && textreadc(t, t->q0-1)!='\n') nnb = textbswidth(t, 0x15); textshow(t, t->q0-nnb, t->q0-nnb, TRUE); return; case 0x05: /* ^E: end of line */ typecommit(t); q0 = t->q0; while(q0<t->file->b.nc && textreadc(t, q0)!='\n') q0++; textshow(t, q0, q0, TRUE); return; case Kcmd+'c': /* %C: copy */ typecommit(t); cut(t, t, nil, TRUE, FALSE, nil, 0); return; case Kcmd+'z': /* %Z: undo */ typecommit(t); undo(t, nil, nil, TRUE, 0, nil, 0); return; Tagdown: /* expand tag to show all text */ if(!t->w->tagexpand){ t->w->tagexpand = TRUE; winresize(t->w, t->w->r, FALSE, TRUE); } return; Tagup: /* shrink tag to single line */ if(t->w->tagexpand){ t->w->tagexpand = FALSE; t->w->taglines = 1; winresize(t->w, t->w->r, FALSE, TRUE); } return; } if(t->what == Body){ seq++; filemark(t->file); } /* cut/paste must be done after the seq++/filemark */ switch(r){ case Kcmd+'x': /* %X: cut */ typecommit(t); if(t->what == Body){ seq++; filemark(t->file); } cut(t, t, nil, TRUE, TRUE, nil, 0); textshow(t, t->q0, t->q0, 1); t->iq1 = t->q0; return; case Kcmd+'v': /* %V: paste */ typecommit(t); if(t->what == Body){ seq++; filemark(t->file); } paste(t, t, nil, TRUE, FALSE, nil, 0); textshow(t, t->q0, t->q1, 1); t->iq1 = t->q1; return; } if(t->q1 > t->q0){ if(t->ncache != 0) error("text.type"); cut(t, t, nil, TRUE, TRUE, nil, 0); t->eq0 = ~0; } textshow(t, t->q0, t->q0, 1); switch(r){ case 0x06: /* ^F: complete */ case Kins: typecommit(t); rp = textcomplete(t); if(rp == nil) return; nr = runestrlen(rp); break; /* fall through to normal insertion case */ case 0x1B: if(t->eq0 != ~0) { if(t->eq0 <= t->q0) textsetselect(t, t->eq0, t->q0); else textsetselect(t, t->q0, t->eq0); } if(t->ncache > 0) typecommit(t); t->iq1 = t->q0; return; case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nnb = textbswidth(t, r); q1 = t->q0; q0 = q1-nnb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nnb = q1-q0; } if(nnb <= 0) return; for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; u->nofill = TRUE; nb = nnb; n = u->ncache; if(n > 0){ if(q1 != u->cq0+n) error("text.type backspace"); if(n > nb) n = nb; u->ncache -= n; textdelete(u, q1-n, q1, FALSE); nb -= n; } if(u->eq0==q1 || u->eq0==~0) u->eq0 = q0; if(nb && u==t) textdelete(u, q0, q0+nb, TRUE); if(u != t) textsetselect(u, u->q0, u->q1); else textsetselect(t, q0, q0); u->nofill = FALSE; } for(i=0; i<t->file->ntext; i++) textfill(t->file->text[i]); t->iq1 = t->q0; return; case '\n': if(t->w->autoindent){ /* find beginning of previous line using backspace code */ nnb = textbswidth(t, 0x15); /* ^U case */ rp = runemalloc(nnb + 1); nr = 0; rp[nr++] = r; for(i=0; i<nnb; i++){ r = textreadc(t, t->q0-nnb+i); if(r != ' ' && r != '\t') break; rp[nr++] = r; } } break; /* fall through to normal code */ } /* otherwise ordinary character; just insert, typically in caches of all texts */ for(i=0; i<t->file->ntext; i++){ u = t->file->text[i]; if(u->eq0 == ~0) u->eq0 = t->q0; if(u->ncache == 0) u->cq0 = t->q0; else if(t->q0 != u->cq0+u->ncache) error("text.type cq1"); /* * Change the tag before we add to ncache, * so that if the window body is resized the * commit will not find anything in ncache. */ if(u->what==Body && u->ncache == 0){ u->needundo = TRUE; winsettag(t->w); u->needundo = FALSE; } textinsert(u, t->q0, rp, nr, FALSE); if(u != t) textsetselect(u, u->q0, u->q1); if(u->ncache+nr > u->ncachealloc){ u->ncachealloc += 10 + nr; u->cache = runerealloc(u->cache, u->ncachealloc); } runemove(u->cache+u->ncache, rp, nr); u->ncache += nr; } if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(r=='\n' && t->w!=nil) wincommit(t->w, t); t->iq1 = t->q0; }