ref: 45e029037e686ea7c844f45443f03cc91cfa870a
dir: /grapple.c/
#include <u.h> #include <libc.h> #include <ctype.h> #include <bio.h> #include <thread.h> #include <draw.h> #include <mouse.h> #include <keyboard.h> #include <plumb.h> #include <regexp.h> typedef struct Line Line; struct Line { char *text; char ptext[255]; }; enum { Emouse, Eresize, Ekeyboard, }; enum { Maxlines = 4096, Padding = 4, Scrollwidth = 12, }; Mousectl *mctl; Keyboardctl *kctl; Image *selbg; Image *mfg; Rectangle ir; Rectangle sr; Rectangle lr; int lh; int lcount; int loff; int lsel; int scrollsize; int qmode; int plumbfd; Line lines[Maxlines]; int nlines; char pwd[255] = {0}; Reprog *re; void loadlines(void) { Biobuf *bp; char *s; Resub sub; char buf[255]; int n; nlines = 0; bp = Bfdopen(0, OREAD); for(;;){ s = Brdstr(bp, '\n', 1); if(s == nil) break; lines[nlines].text = s; memset(&sub, 0, sizeof(sub)); if(regexec(re, s, &sub, 1) != 0) snprint(lines[nlines].ptext, sizeof(lines[nlines].ptext), "%.*s", (int)(sub.ep - sub.sp), sub.sp); else lines[nlines].ptext[0] = 0; nlines += 1; if(nlines >= Maxlines) break; } Bterm(bp); } void activate(void) { Line l; l = lines[lsel + loff]; if(l.ptext[0] == 0) return; plumbsendtext(plumbfd, argv0, nil, pwd, l.ptext); if(qmode) threadexitsall(nil); } int lineat(Point p) { if(ptinrect(p, lr) == 0) return -1; return (p.y - lr.min.y) / lh; } Rectangle linerect(int i) { Rectangle r; r.min.x = 0; r.min.y = i * (font->height + Padding); r.max.x = Dx(lr) - 2*Padding; r.max.y = (i + 1) * (font->height + Padding); r = rectaddpt(r, lr.min); return r; } void drawline(int i, int sel) { Point p; char *t; if(loff + i >= nlines) return; draw(screen, linerect(i), sel ? selbg : display->white, nil, ZP); p = addpt(lr.min, Pt(0, i * lh)); t = lines[loff + i].text; while(*t){ if(*t == '\t') p = string(screen, p, display->black, ZP, font, " "); else p = stringn(screen, p, display->black, ZP, font, t, 1); t++; } } void redraw(void) { Rectangle scrposr; int i, h, y, ye; draw(screen, screen->r, display->white, nil, ZP); draw(screen, sr, mfg, nil, ZP); border(screen, sr, 0, display->black, ZP); if(nlines > 0){ h = ((double)lcount / nlines) * Dy(sr); y = ((double)loff / nlines) * Dy(sr); ye = sr.min.y + y + h - 1; if(ye >= sr.max.y) ye = sr.max.y - 1; scrposr = Rect(sr.min.x + 1, sr.min.y + y + 1, sr.max.x - 1, ye); }else scrposr = insetrect(sr, -1); draw(screen, scrposr, display->white, nil, ZP); for(i = 0; i < lcount; i++) drawline(i, i == lsel); flushimage(display, 1); } int scroll(int lines, int setsel) { if(nlines <= lcount) return 0; if(lines < 0 && loff == 0) return 0; if(lines > 0 && loff + lcount >= nlines){ return 0; } loff += lines; if(loff < 0) loff = 0; if(loff + nlines%lcount >= nlines) loff = nlines - nlines%lcount; if(setsel){ if(lines > 0) lsel = 0; else lsel = lcount - 1; } redraw(); return 1; } void changesel(int from, int to) { drawline(from, 0); drawline(to, 1); flushimage(display, 1); } void eresize(void) { Rectangle r; r = screen->r; sr = Rect(r.min.x + Padding, r.min.y + Padding, r.min.x + Padding + Scrollwidth, r.max.y - Padding - 1); lr = Rect(sr.max.x + Padding, r.min.y + Padding, r.max.x, r.max.y - Padding); lh = font->height + Padding; lcount = Dy(lr) / lh; scrollsize = mousescrollsize(lcount); redraw(); } void emouse(Mouse *m) { int n; if(ptinrect(m->xy, lr)){ if(m->buttons == 1 || m->buttons == 4){ n = lineat(m->xy); if(n != -1 && (loff + n) < nlines){ changesel(lsel, n); lsel = n; } if(m->buttons == 4) activate(); }else if(m->buttons == 8){ scroll(-scrollsize, 1); }else if(m->buttons == 16){ scroll(scrollsize, 1); } }else if(ptinrect(m->xy, sr)){ if(m->buttons == 1){ n = (m->xy.y - sr.min.y) / lh; scroll(-n, 1); }else if(m->buttons == 2){ n = (m->xy.y - sr.min.y) * nlines / Dy(sr); loff = n; redraw(); }else if(m->buttons == 4){ n = (m->xy.y - sr.min.y) / lh; scroll(n, 1); } } } void ekeyboard(Rune k) { int osel; switch(k){ case 'q': case Kesc: case Kdel: threadexitsall(nil); break; case Kup: if(lsel == 0 && loff > 0) scroll(-lcount, 1); else if(lsel > 0) changesel(lsel, --lsel); break; case Kdown: if(lsel < (nlines - 1)){ if(lsel == lcount - 1) scroll(lcount, 1); else if(loff + lsel < nlines - 1) changesel(lsel, ++lsel); } break; case Kpgup: scroll(-lcount, 1); break; case Kpgdown: scroll(lcount, 1); break; case Khome: osel = lsel; lsel = 0; if(scroll(-nlines, 0) == 0) changesel(osel, lsel); break; case Kend: osel = lsel; lsel = nlines%lcount - 1; if(scroll(nlines, 0) == 0){ changesel(osel, lsel); } break; case '\n': if(lsel >= 0) activate(); break; } } void usage(void) { fprint(2, "usage: %s [-q]\n", argv0); exits("usage"); } void threadmain(int argc, char **argv) { Mouse m; Rune k; Alt a[] = { { nil, &m, CHANRCV }, { nil, nil, CHANRCV }, { nil, &k, CHANRCV }, { nil, nil, CHANEND }, }; qmode = 0; ARGBEGIN{ case 'q': qmode = 1; break; default: usage(); }ARGEND; plumbfd = plumbopen("send", OWRITE|OCEXEC); if(plumbfd < 0) sysfatal("plumbopen: %r"); re = regcomp("([.a-zA-Z0-9_/+\\-]+:[0-9]+)"); getwd(pwd, sizeof pwd); loadlines(); if(initdraw(nil, nil, "grapple") < 0) sysfatal("initdraw: %r"); display->locking = 0; if((mctl = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); if((kctl = initkeyboard(nil)) == nil) sysfatal("initkeyboard: %r"); a[Emouse].c = mctl->c; a[Eresize].c = mctl->resizec; a[Ekeyboard].c = kctl->c; selbg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xEFEFEFFF); mfg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x999999FF); loff = 0; lsel = 0; eresize(); for(;;){ switch(alt(a)){ case Emouse: emouse(&m); break; case Eresize: if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); eresize(); break; case Ekeyboard: ekeyboard(k); break; } } }