/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <draw.h>#include <memdraw.h>#include <thread.h>#include <cursor.h>#include <mouse.h>#include <keyboard.h>#include <frame.h>#include <plumb.h>#include <html.h>#include "dat.h"#include "fns.h" Image *tagcols[NCOL];Image *textcols[NCOL]; voidtextinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOL]){ t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollsize; t->lastsr = ZR; r.min.x += Scrollsize+Scrollgap; t->rs.nr = 0; memmove(t->Frame.cols, cols, sizeof t->Frame.cols); textredraw(t, r, f, b);} voidtextredraw(Text *t, Rectangle r, Font *f, Image *b){ Rectangle r1; frinit(t, r, f, b, t->Frame.cols); r1 = t->r; r1.min.x -= Scrollsize+Scrollgap; /* back fill to scroll bar */ draw(t->b, r1, t->cols[BACK], nil, ZP); t->maxtab = Maxtab*stringwidth(f, "0"); textfill(t); textsetselect(t, t->q0, t->q1);} inttextresize(Text *t, Image *b, Rectangle r){ if(Dy(r) > 0) r.max.y -= Dy(r)%t->font->height; else r.max.y = r.min.y; t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollsize; t->lastsr = ZR; r.min.x += Scrollsize+Scrollgap; frclear(t, 0); textredraw(t, r, t->font, b); if(t->what == Textarea) textscrdraw(t); return r.max.y;} voidtextclose(Text *t){ closerunestr(&t->rs); frclear(t, 1);} voidtextinsert(Text *t, uint q0, Rune *r, uint n){ if(n == 0) return; t->rs.r = runerealloc(t->rs.r, t->rs.nr+n); runemove(t->rs.r+q0+n, t->rs.r+q0, t->rs.nr-q0); runemove(t->rs.r+q0, r, n); t->rs.nr += n; if(q0 < t->q1) t->q1 += n; if(q0 < t->q0) t->q0 += n; if(q0 < t->org) t->org += n; else if(q0 <= t->org+t->nchars) frinsert(t, r, r+n, q0-t->org);} voidtextfill(Text *t){ Rune *rp; int i, n, m, nl; if(t->lastlinefull) return; rp = runemalloc(BUFSIZE*8); do{ n = t->rs.nr-(t->org+t->nchars); if(n == 0) break; if(n > 2000) /* educated guess at reasonable amount */ n = 2000; runemove(rp, t->rs.r+(t->org+t->nchars), n); /* * it's expensive to frinsert more than we need, so * count newlines. */ nl = t->maxlines-t->nlines; m = 0; for(i=0; i<n; ){ if(rp[i++] == '\n'){ m++; if(m >= nl) break; } } frinsert(t, rp, rp+i, t->nchars); }while(t->lastlinefull == FALSE); free(rp);} voidtextdelete(Text *t, uint q0, uint q1){ uint n, p0, p1; n = q1-q0; if(n == 0) return; runemove(t->rs.r+q0, t->rs.r+q1, t->rs.nr-q1); t->rs.nr -= n; if(q0 < t->q0) t->q0 -= min(n, t->q0-q0); if(q0 < t->q1) t->q1 -= min(n, t->q1-q0); if(q1 <= t->org) t->org -= n; else if(q0 < t->org+t->nchars){ p1 = q1 - t->org; if(p1 > t->nchars) p1 = t->nchars; if(q0 < t->org){ t->org = q0; p0 = 0; }else p0 = q0 - t->org; frdelete(t, p0, p1); textfill(t); } t->rs.r[t->rs.nr] = L'\0';} inttextbswidth(Text *t, Rune c){ uint q, eq; Rune r; int skipping; /* there is known to be at least one character to erase */ if(c == 0x08) /* ^H: erase character */ return 1; q = t->q0; skipping = TRUE; while(q > 0){ r = t->rs.r[q-1]; if(r == '\n'){ /* eat at most one more character */ if(q == t->q0) /* eat the newline */ --q; break; } if(c == 0x17){ eq = isalnum(r); if(eq && skipping) /* found one; stop skipping */ skipping = FALSE; else if(!eq && !skipping) break; } --q; } return t->q0-q;} voidtexttype(Text *t, Rune r){ uint q0, q1; int nb, n; int nr; Rune *rp; if(t->what!=Textarea && r=='\n'){ if(t->what==Urltag) get(t, t, XXX, XXX, nil, 0); return; } if(t->what==Tag && (r==Kscrollonedown || r==Kscrolloneup)) return; nr = 1; rp = &r; switch(r){ case Kleft: if(t->q0 > 0) textshow(t, t->q0-1, t->q0-1, TRUE); return; case Kright: if(t->q1 < t->rs.nr) 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: textshow(t, 0, 0, FALSE); return; case Kend: textshow(t, t->rs.nr, t->rs.nr, FALSE); return; case 0x01: /* ^A: beginning of line */ /* go to where ^U would erase, if not already at BOL */ nb = 0; if(t->q0>0 && t->rs.r[t->q0-1]!='\n') nb = textbswidth(t, 0x15); textshow(t, t->q0-nb, t->q0-nb, TRUE); return; case 0x05: /* ^E: end of line */ q0 = t->q0; while(q0<t->rs.nr && t->rs.r[q0]!='\n') q0++; textshow(t, q0, q0, TRUE); return; } if(t->q1 > t->q0) cut(t, t, TRUE, TRUE, nil, 0); textshow(t, t->q0, t->q0, TRUE); switch(r){ case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nb = textbswidth(t, r); q1 = t->q0; q0 = q1-nb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nb = q1-q0; } if(nb > 0){ textdelete(t, q0, q0+nb); textsetselect(t, q0, q0); } return; } /* otherwise ordinary character; just insert */ textinsert(t, t->q0, &r, 1); if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(t->what == Textarea) textscrdraw(t);} static Text *clicktext;static uint clickmsec;static Text *selecttext;static uint selectq; /* * called from frame library */voidframescroll(Frame *f, int dl){ if(f != &selecttext->Frame) error("frameselect not right frame"); textframescroll(selecttext, dl);} voidtextframescroll(Text *t, int dl){ uint q0; if(dl == 0){ scrsleep(100); return; } if(dl < 0){ q0 = textbacknl(t, t->org, -dl); if(selectq > t->org+t->p0) textsetselect(t, t->org+t->p0, selectq); else textsetselect(t, selectq, t->org+t->p0); }else{ if(t->org+t->nchars == t->rs.nr) return; q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height)); if(selectq > t->org+t->p1) textsetselect(t, t->org+t->p1, selectq); else textsetselect(t, selectq, t->org+t->p1); } textsetorigin(t, q0, TRUE);} voidtextselect(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->rs.nr) 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(b & 2){ if(state==-1 && t->what==Textarea){ textsetselect(t, q0, t->q0); state = 0; }else if(state != 1){ cut(t, t, TRUE, TRUE, nil, 0); state = 1; } }else{ if(state==1 && t->what==Textarea){ textsetselect(t, q0, t->q1); state = 0; }else if(state != -1){ paste(t, t, TRUE, FALSE, nil, 0); state = -1; } } textscrdraw(t); } flushimage(display, 1); while(mouse->buttons == b) readmouse(mousectl); clicktext = nil; }} voidtextshow(Text *t, uint q0, uint q1, int doselect){ int qe; int nl; uint q; if(t->what != Textarea){ if(doselect) textsetselect(t, q0, q1); return; } if(doselect) textsetselect(t, q0, q1); qe = t->org+t->nchars; if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->rs.nr))) textscrdraw(t); else{ nl = t->maxlines/4; q = textbacknl(t, q0, nl); /* avoid going backwards if trying to go forwards - long lines! */ if(!(q0>t->org && q<t->org)) textsetorigin(t, q, TRUE); while(q0 > t->org+t->nchars) textsetorigin(t, t->org+1, FALSE); }} staticintregion(int a, int b){ if(a < b) return -1; if(a == b) return 0; return 1;} voidselrestore(Frame *f, Point pt0, uint p0, uint p1){ if(p1<=f->p0 || p0>=f->p1){ /* no overlap */ frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]); return; } if(p0>=f->p0 && p1<=f->p1){ /* entirely inside */ frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]); return; } /* they now are known to overlap */ /* before selection */ if(p0 < f->p0){ frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]); p0 = f->p0; pt0 = frptofchar(f, p0); }Lines 1–500 of 889. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h> #include <libc.h> #include <draw.h> #include <memdraw.h> #include <thread.h> #include <cursor.h> #include <mouse.h> #include <keyboard.h> #include <frame.h> #include <plumb.h> #include <html.h> #include "dat.h" #include "fns.h" Image *tagcols[NCOL]; Image *textcols[NCOL]; void textinit(Text *t, Image *b, Rectangle r, Font *f, Image *cols[NCOL]) { t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollsize; t->lastsr = ZR; r.min.x += Scrollsize+Scrollgap; t->rs.nr = 0; memmove(t->Frame.cols, cols, sizeof t->Frame.cols); textredraw(t, r, f, b); } void textredraw(Text *t, Rectangle r, Font *f, Image *b) { Rectangle r1; frinit(t, r, f, b, t->Frame.cols); r1 = t->r; r1.min.x -= Scrollsize+Scrollgap; /* back fill to scroll bar */ draw(t->b, r1, t->cols[BACK], nil, ZP); t->maxtab = Maxtab*stringwidth(f, "0"); textfill(t); textsetselect(t, t->q0, t->q1); } int textresize(Text *t, Image *b, Rectangle r) { if(Dy(r) > 0) r.max.y -= Dy(r)%t->font->height; else r.max.y = r.min.y; t->all = r; t->scrollr = r; t->scrollr.max.x = r.min.x+Scrollsize; t->lastsr = ZR; r.min.x += Scrollsize+Scrollgap; frclear(t, 0); textredraw(t, r, t->font, b); if(t->what == Textarea) textscrdraw(t); return r.max.y; } void textclose(Text *t) { closerunestr(&t->rs); frclear(t, 1); } void textinsert(Text *t, uint q0, Rune *r, uint n) { if(n == 0) return; t->rs.r = runerealloc(t->rs.r, t->rs.nr+n); runemove(t->rs.r+q0+n, t->rs.r+q0, t->rs.nr-q0); runemove(t->rs.r+q0, r, n); t->rs.nr += n; if(q0 < t->q1) t->q1 += n; if(q0 < t->q0) t->q0 += n; if(q0 < t->org) t->org += n; else if(q0 <= t->org+t->nchars) frinsert(t, r, r+n, q0-t->org); } void textfill(Text *t) { Rune *rp; int i, n, m, nl; if(t->lastlinefull) return; rp = runemalloc(BUFSIZE*8); do{ n = t->rs.nr-(t->org+t->nchars); if(n == 0) break; if(n > 2000) /* educated guess at reasonable amount */ n = 2000; runemove(rp, t->rs.r+(t->org+t->nchars), n); /* * it's expensive to frinsert more than we need, so * count newlines. */ nl = t->maxlines-t->nlines; m = 0; for(i=0; i<n; ){ if(rp[i++] == '\n'){ m++; if(m >= nl) break; } } frinsert(t, rp, rp+i, t->nchars); }while(t->lastlinefull == FALSE); free(rp); } void textdelete(Text *t, uint q0, uint q1) { uint n, p0, p1; n = q1-q0; if(n == 0) return; runemove(t->rs.r+q0, t->rs.r+q1, t->rs.nr-q1); t->rs.nr -= n; if(q0 < t->q0) t->q0 -= min(n, t->q0-q0); if(q0 < t->q1) t->q1 -= min(n, t->q1-q0); if(q1 <= t->org) t->org -= n; else if(q0 < t->org+t->nchars){ p1 = q1 - t->org; if(p1 > t->nchars) p1 = t->nchars; if(q0 < t->org){ t->org = q0; p0 = 0; }else p0 = q0 - t->org; frdelete(t, p0, p1); textfill(t); } t->rs.r[t->rs.nr] = L'\0'; } int textbswidth(Text *t, Rune c) { uint q, eq; Rune r; int skipping; /* there is known to be at least one character to erase */ if(c == 0x08) /* ^H: erase character */ return 1; q = t->q0; skipping = TRUE; while(q > 0){ r = t->rs.r[q-1]; if(r == '\n'){ /* eat at most one more character */ if(q == t->q0) /* eat the newline */ --q; break; } if(c == 0x17){ eq = isalnum(r); if(eq && skipping) /* found one; stop skipping */ skipping = FALSE; else if(!eq && !skipping) break; } --q; } return t->q0-q; } void texttype(Text *t, Rune r) { uint q0, q1; int nb, n; int nr; Rune *rp; if(t->what!=Textarea && r=='\n'){ if(t->what==Urltag) get(t, t, XXX, XXX, nil, 0); return; } if(t->what==Tag && (r==Kscrollonedown || r==Kscrolloneup)) return; nr = 1; rp = &r; switch(r){ case Kleft: if(t->q0 > 0) textshow(t, t->q0-1, t->q0-1, TRUE); return; case Kright: if(t->q1 < t->rs.nr) 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: textshow(t, 0, 0, FALSE); return; case Kend: textshow(t, t->rs.nr, t->rs.nr, FALSE); return; case 0x01: /* ^A: beginning of line */ /* go to where ^U would erase, if not already at BOL */ nb = 0; if(t->q0>0 && t->rs.r[t->q0-1]!='\n') nb = textbswidth(t, 0x15); textshow(t, t->q0-nb, t->q0-nb, TRUE); return; case 0x05: /* ^E: end of line */ q0 = t->q0; while(q0<t->rs.nr && t->rs.r[q0]!='\n') q0++; textshow(t, q0, q0, TRUE); return; } if(t->q1 > t->q0) cut(t, t, TRUE, TRUE, nil, 0); textshow(t, t->q0, t->q0, TRUE); switch(r){ case 0x08: /* ^H: erase character */ case 0x15: /* ^U: erase line */ case 0x17: /* ^W: erase word */ if(t->q0 == 0) /* nothing to erase */ return; nb = textbswidth(t, r); q1 = t->q0; q0 = q1-nb; /* if selection is at beginning of window, avoid deleting invisible text */ if(q0 < t->org){ q0 = t->org; nb = q1-q0; } if(nb > 0){ textdelete(t, q0, q0+nb); textsetselect(t, q0, q0); } return; } /* otherwise ordinary character; just insert */ textinsert(t, t->q0, &r, 1); if(rp != &r) free(rp); textsetselect(t, t->q0+nr, t->q0+nr); if(t->what == Textarea) textscrdraw(t); } static Text *clicktext; static uint clickmsec; static Text *selecttext; static uint selectq; /* * called from frame library */ void framescroll(Frame *f, int dl) { if(f != &selecttext->Frame) error("frameselect not right frame"); textframescroll(selecttext, dl); } void textframescroll(Text *t, int dl) { uint q0; if(dl == 0){ scrsleep(100); return; } if(dl < 0){ q0 = textbacknl(t, t->org, -dl); if(selectq > t->org+t->p0) textsetselect(t, t->org+t->p0, selectq); else textsetselect(t, selectq, t->org+t->p0); }else{ if(t->org+t->nchars == t->rs.nr) return; q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height)); if(selectq > t->org+t->p1) textsetselect(t, t->org+t->p1, selectq); else textsetselect(t, selectq, t->org+t->p1); } textsetorigin(t, q0, TRUE); } 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->rs.nr) 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(b & 2){ if(state==-1 && t->what==Textarea){ textsetselect(t, q0, t->q0); state = 0; }else if(state != 1){ cut(t, t, TRUE, TRUE, nil, 0); state = 1; } }else{ if(state==1 && t->what==Textarea){ textsetselect(t, q0, t->q1); state = 0; }else if(state != -1){ paste(t, t, TRUE, FALSE, nil, 0); state = -1; } } textscrdraw(t); } flushimage(display, 1); while(mouse->buttons == b) readmouse(mousectl); clicktext = nil; } } void textshow(Text *t, uint q0, uint q1, int doselect) { int qe; int nl; uint q; if(t->what != Textarea){ if(doselect) textsetselect(t, q0, q1); return; } if(doselect) textsetselect(t, q0, q1); qe = t->org+t->nchars; if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->rs.nr))) textscrdraw(t); else{ nl = t->maxlines/4; q = textbacknl(t, q0, nl); /* avoid going backwards if trying to go forwards - long lines! */ if(!(q0>t->org && q<t->org)) textsetorigin(t, q, TRUE); while(q0 > t->org+t->nchars) textsetorigin(t, t->org+1, FALSE); } } static int region(int a, int b) { if(a < b) return -1; if(a == b) return 0; return 1; } void selrestore(Frame *f, Point pt0, uint p0, uint p1) { if(p1<=f->p0 || p0>=f->p1){ /* no overlap */ frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]); return; } if(p0>=f->p0 && p1<=f->p1){ /* entirely inside */ frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]); return; } /* they now are known to overlap */ /* before selection */ if(p0 < f->p0){ frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]); p0 = f->p0; pt0 = frptofchar(f, p0); }