ref: 7633a56ff04b4d8ec683b34224c41304e422ca28
dir: /main.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 "a.h" #include "theme.h" enum { Emouse, Eresize, Ekeyboard, }; enum { Tickw = 3, Padding = 4, Scrollwidth = 12, }; Mousectl *mctl; Keyboardctl *kctl; Image *bg; Image *fg; Image *selbg; Image *mfg; Image *tick; Rectangle ir; Rectangle sr; Rectangle lr; int lh; int lcount; int loff; int lsel; int scrollsize; int pmode; int plumbfd; char** lines; int nlines; int maxlines; Match* matches; int nmatches; char input[255] = {0}; int ninput = 0; char pwd[255] = {0}; void* emalloc(ulong size) { void *p; p = malloc(size); if(p == nil) sysfatal("malloc: %r"); return p; } void* erealloc(void *p, ulong size) { void *q; q = realloc(p, size); if(q == nil) sysfatal("realloc: %r"); return q; } int matchcmp(void *a, void *b) { Match m, n; m = *(Match*)a; n = *(Match*)b; return n.score - m.score; } void match(char *pat) { int i, s; nmatches = 0; for(i = 0; i < nlines; i++){ s = fuzzymatch(pat, lines[i]); if(s >= 0){ matches[nmatches].name = lines[i]; matches[nmatches].score = s; nmatches++; } } if(nmatches > 0) qsort(matches, nmatches, sizeof(Match), matchcmp); } void loadlines(void) { Biobuf *bp; char *s; nlines = 0; nmatches = 0; maxlines = 4096; lines = emalloc(maxlines * sizeof *lines); matches = emalloc(maxlines * sizeof *matches); bp = Bfdopen(0, OREAD); for(;;){ s = Brdstr(bp, '\n', 1); if(s == nil) break; lines[nlines++] = s; matches[nmatches++].name = s; if(nlines >= maxlines || nmatches >= maxlines){ maxlines *= 1.5; lines = erealloc(lines, maxlines * sizeof *lines); matches = erealloc(matches, maxlines * sizeof *matches); } } Bterm(bp); } void activate(void) { Match m; m = matches[lsel + loff]; if(pmode){ print("%s", m.name); threadexitsall(nil); } plumbsendtext(plumbfd, argv0, nil, pwd, m.name); } 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) { if(loff + i >= nmatches) return; draw(screen, linerect(i), sel ? selbg : bg, nil, ZP); string(screen, addpt(lr.min, Pt(0, i * lh)), fg, ZP, font, matches[loff + i].name); } void redraw(void) { char b[10] = {0}; Point p; Rectangle r, scrposr; int i, h, y, ye; draw(screen, screen->r, bg, nil, ZP); p = string(screen, addpt(screen->r.min, Pt(Padding, Padding)), fg, ZP, font, "> "); p = stringn(screen, p, fg, ZP, font, input, ninput); r = Rect(p.x, p.y, p.x + Dx(tick->r), p.y + Dy(tick->r)); draw(screen, r, tick, nil, ZP); draw(screen, sr, mfg, nil, ZP); border(screen, sr, 0, fg, ZP); if(nmatches > 0){ h = ((double)lcount / nmatches) * Dy(sr); y = ((double)loff / nmatches) * 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, bg, nil, ZP); for(i = 0; i < lcount; i++) drawline(i, i == lsel); i = snprint(b, sizeof b, "%d/%d", nmatches, nlines); b[i] = 0; p = Pt(screen->r.max.x - Padding - stringwidth(font, b), screen->r.min.y + Padding); string(screen, p, mfg, ZP, font, b); flushimage(display, 1); } int scroll(int lines, int setsel) { if(nmatches <= lcount) return 0; if(lines < 0 && loff == 0) return 0; if(lines > 0 && loff + lcount >= nmatches){ return 0; } loff += lines; if(loff < 0) loff = 0; if(loff + nmatches%lcount >= nmatches) loff = nmatches - nmatches%lcount; if(setsel){ if(lines > 0) lsel = 0; else lsel = lcount - 1; } redraw(); return 1; } void inputchanged(void) { int i; input[ninput] = 0; if(ninput > 0){ match(input); }else{ nmatches = 0; for(i = 0; i < nlines; i++) matches[nmatches++].name = lines[i]; } if(lsel >= (nmatches - 1)) lsel = 0; if(nmatches == 0) lsel = -1; loff = 0; redraw(); } void changesel(int from, int to) { drawline(from, 0); drawline(to, 1); flushimage(display, 1); } void eresize(void) { ir = Rect(Padding, Padding, screen->r.max.x - Padding, Padding + font->height + Padding); ir = rectaddpt(ir, screen->r.min); sr = Rect(screen->r.min.x + Padding, ir.max.y, screen->r.min.x + Padding + Scrollwidth, screen->r.max.y - Padding - 1); lr = Rect(sr.max.x + 2*Padding, ir.max.y, ir.max.x, screen->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) < nmatches){ changesel(lsel, n); lsel = n; } if(m->buttons == 4) if(nmatches > 0 && lsel >= 0) 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) * nmatches / 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, p; switch(k){ 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 < (nmatches - 1)){ if(lsel == lcount - 1) scroll(lcount, 1); else if(loff + lsel < nmatches - 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(-nmatches, 0) == 0) changesel(osel, lsel); break; case Kend: osel = lsel; lsel = nmatches%lcount - 1; if(scroll(nmatches, 0) == 0){ changesel(osel, lsel); } break; case '\n': if(lsel >= 0) activate(); break; case Kesc: if(ninput > 0){ ninput = 0; inputchanged(); }else threadexitsall(nil); break; case Kbs: if(ninput > 0){ ninput--; inputchanged(); } break; case Knack: /* ^U */ if(ninput > 0){ ninput = 0; inputchanged(); } break; case Ketb: /* ^W */ if(ninput > 0){ p = ninput - 1; for(; p >= 0 && !isalnum(input[p]); --p) ; for(; p > 0 && isalnum(input[p-1]); --p) ; ninput = p >= 0 ? p : 0; input[ninput] = '\0'; inputchanged(); } break; default: if(isprint(k) && ninput < (sizeof input - 1)){ input[ninput++] = (char)k; /* XXX */ inputchanged(); } } } Image* createtick(void) { Image *t; t = allocimage(display, Rect(0, 0, Tickw, font->height), screen->chan, 0, DWhite); if(t == nil) return 0; /* background color */ draw(t, t->r, bg, nil, ZP); /* vertical line */ draw(t, Rect(Tickw/2, 0, Tickw/2+1, font->height), fg, nil, ZP); /* box on each end */ draw(t, Rect(0, 0, Tickw, Tickw), fg, nil, ZP); draw(t, Rect(0, font->height-Tickw, Tickw, font->height), fg, nil, ZP); return t; } void initcolors(void) { Theme *theme; theme = loadtheme(); if(theme != nil){ bg = theme->back; fg = theme->text; selbg = theme->border; mfg = theme->border; }else{ bg = display->white; fg = display->black; selbg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xEFEFEFFF); mfg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x999999FF); } tick = createtick(); } void usage(void) { fprint(2, "usage: %s [-p]\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 }, }; pmode = 0; ARGBEGIN{ case 'p': pmode = 1; break; default: usage(); }ARGEND; getwd(pwd, sizeof pwd); if(pmode == 0){ plumbfd = plumbopen("send", OWRITE|OCEXEC); if(plumbfd < 0) sysfatal("plumbopen: %r"); } loadlines(); if(initdraw(nil, nil, "fm") < 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; initcolors(); 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; } } }