ref: c593a3efa4079f5b79702bf3bb3213e2448c8888
dir: /vdiff.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <event.h> #include <keyboard.h> #include <bio.h> typedef struct Line Line; struct Line { int t; char *s; }; enum { Lfile = 0, Lsep, Ladd, Ldel, Lnone, Ncols, }; enum { Scrollwidth = 12, Scrollgap = 2, Margin = 8, Hpadding = 4, Vpadding = 2, }; Rectangle sr; Rectangle scrollr; Rectangle scrposr; Rectangle listr; Rectangle textr; Image *cols[Ncols]; Image *scrollbg; int lineh; int nlines; int offset; Line **lines; int lsize; int lcount; const char ellipsis[] = "..."; void drawline(Rectangle r, Line *l) { Image *bg; Point p; char *s; bg = cols[l->t]; draw(screen, r, bg, nil, ZP); p = Pt(r.min.x + Hpadding, r.min.y + (Dy(r)-font->height)/2); for(s = l->s; *s; s++){ if(*s == '\t') p = string(screen, p, display->black, ZP, font, " "); else if((p.x+Hpadding+stringwidth(font, " ")+stringwidth(font, ellipsis)>=textr.max.x)){ string(screen, p, display->black, ZP, font, ellipsis); break; }else p = stringn(screen, p, display->black, ZP, font, s, 1); } } void redraw(void) { Rectangle lr; int i, h, y; draw(screen, sr, display->white, nil, ZP); draw(screen, scrollr, scrollbg, nil, ZP); if(lcount>0){ h = ((double)nlines/lcount)*Dy(scrollr); y = ((double)offset/lcount)*Dy(scrollr); scrposr = Rect(scrollr.min.x, scrollr.min.y+y, scrollr.max.x-1, scrollr.min.y+y+h); }else scrposr = Rect(scrollr.min.x, scrollr.min.y, scrollr.max.x-1, scrollr.max.y); draw(screen, scrposr, display->white, nil, ZP); for(i=0; i<nlines && offset+i<lcount; i++){ lr = Rect(textr.min.x, textr.min.y+i*lineh, textr.max.x, textr.min.y+(i+1)*lineh); drawline(lr, lines[offset+i]); } } void scroll(int off) { if(off<0 && offset<=0) return; if(off>0 && offset+nlines>lcount) return; offset += off; if(offset<0) offset = 0; if(offset+nlines>lcount) offset = lcount-nlines+1; redraw(); } void eresized(int new) { if(new && getwindow(display, Refnone)<0) sysfatal("cannot reattach: %r"); sr = screen->r; scrollr = sr; scrollr.max.x = scrollr.min.x+Scrollwidth+Scrollgap; listr = sr; listr.min.x = scrollr.max.x; textr = insetrect(listr, Margin); lineh = Vpadding+font->height+Vpadding; nlines = Dy(textr)/lineh; offset = 0; redraw(); } void initcols(void) { Rectangle cr; cr = Rect(0, 0, 1, 1); cols[Lfile] = allocimage(display, cr, screen->chan, 1, 0xefefefff); cols[Lsep] = allocimage(display, cr, screen->chan, 1, 0xeaffffff); cols[Ladd] = allocimage(display, cr, screen->chan, 1, 0xe6ffedff); cols[Ldel] = allocimage(display, cr, screen->chan, 1, 0xffeef0ff); cols[Lnone] = display->white; scrollbg = allocimage(display, cr, screen->chan, 1, 0x999999ff); } int linetype(char *text) { int type; type = Lnone; if(strncmp(text, "+++", 3)==0) type = Lfile; else if(strncmp(text, "---", 3)==0) type = Lfile; else if(strncmp(text, "@@", 2)==0) type = Lsep; else if(strncmp(text, "+", 1)==0) type = Ladd; else if(strncmp(text, "-", 1)==0) type = Ldel; return type; } Line* parseline(char *s) { Line *l; l = malloc(sizeof *l); if(l==nil) sysfatal("malloc: %r"); l->t = linetype(s); l->s = s; return l; } void parse(int fd) { Biobuf *bp; char *s; lsize = 64; lcount = 0; lines = malloc(lsize * sizeof *lines); if(lines==nil) sysfatal("malloc: %r"); bp = Bfdopen(fd, OREAD); if(bp==nil) sysfatal("Bfdopen: %r"); for(;;){ s = Brdstr(bp, '\n', 1); if(s==nil) break; lines[lcount++] = parseline(s); if(lcount>=lsize){ lsize *= 2; lines = realloc(lines, lsize*sizeof *lines); if(lines==nil) sysfatal("realloc: %r"); } } } void main(void) { Event ev; int e; parse(0); if(initdraw(nil, nil, "vdiff")<0) sysfatal("initdraw: %r"); initcols(); einit(Emouse|Ekeyboard); eresized(0); for(;;){ e = event(&ev); switch(e){ case Emouse: if(ev.mouse.buttons&8) scroll(-10); else if(ev.mouse.buttons&16) scroll(10); break; case Ekeyboard: switch(ev.kbdc){ case Kdel: goto End; break; case Khome: scroll(-1000000); break; case Kend: scroll(1000000); break; case Kpgup: scroll(-nlines); break; case Kpgdown: scroll(nlines); break; } break; } } End: exits(nil); }