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);
}