shithub: vdir

ref: c6ac274f540ba22f511dc95ae70cb7303c7c5087
dir: /vdir.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>
#include <plumb.h>
#include <bio.h>

enum
{
	Toolpadding = 4,
	Padding = 1,
	Scrollwidth = 14,
	Slowscroll = 10,
};

char *home;
char path[256];
Dir* dirs;
long ndirs;
Rectangle toolr;
Rectangle homer;
Rectangle upr;
Rectangle cdr;
Rectangle viewr;
Rectangle scrollr;
Rectangle scrposr;
Image *folder;
Image *file;
Image *toolbg;
Image *toolfg;
Image *viewbg;
Image *viewfg;
Image *scrollbg;
Image *scrollfg;
int lineh;
int nlines;
int offset;


void
readhome(void)
{
	Biobuf *bp;
	
	bp = Bopen("/env/home", OREAD);
	home = Brdstr(bp, 0, 0);
	Bterm(bp);
}

int
dircmp(Dir *a, Dir *b)
{
	if(a->qid.type==b->qid.type)
		return strcmp(a->name, b->name);
	if(a->qid.type&QTDIR)
		return -1;
	return 1;
}

void
loaddirs(void)
{
	int fd;

	fd = open(path, OREAD);
	if(fd<0)
		sysfatal("open: %r");
	ndirs = dirreadall(fd, &dirs);
	qsort(dirs, ndirs, sizeof *dirs, (int(*)(void*,void*))dircmp);
	offset = 0;
	close(fd);
}

void
up(void)
{
	int i, n;

	n = strlen(path);
	if(n==1)
		return;
	for(i = n-1; path[i]; i--){
		if(path[i]=='/'){
			path[i]=0;
			break;
		}
	}
	if(strlen(path)==0)
		sprint(path, "/");
	loaddirs();
}

void
cd(char *dir)
{
	char *sep;

	if(dir == nil)
		sprint(path, home);
	else if(dir[0] == '/')
		snprint(path, sizeof path, dir);
	else{
		sep = strlen(path)==1 ? "" : "/";
		snprint(path, sizeof path, "%s%s%s", path, sep, dir);
	}
	loaddirs();
}

void
plumbfile(char *path, char *name)
{
	int fd;
	char *f;

	f = smprint("%s/%s", path, name);
	fd = plumbopen("send", OWRITE|OCEXEC);
	if(fd<0)
		return;
	plumbsendtext(fd, "vdir", nil, nil, f);
	close(fd);
	free(f);
}	

void
initcolors(void)
{
	toolbg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xEFEFEFFF);
	toolfg = display->black;
	viewbg = display->white;
	viewfg = display->black;
	scrollbg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x999999FF);
	scrollfg = display->white;
}

void
initimages(void)
{
	Point folderpts[] = { 
		Pt(0, 1), Pt(5,1), Pt(7, 4), Pt(10, 4), 
		Pt(10, 10), Pt(0, 10), Pt(0, 1)
	};
	Point filepts[] = {
		Pt(1, 1), Pt(7, 1), Pt(7, 4), Pt(10, 4),
		Pt(10, 11), Pt(1, 11), Pt(1, 1)
	};
	folder = allocimage(display, Rect(0, 0, 12, 12), screen->chan, 0, DWhite);
	poly(folder, folderpts, 7, 0, 0, 0, display->black, ZP);
	file = allocimage(display, Rect(0, 0, 12, 12), screen->chan, 0, DWhite);
	poly(file, filepts, 7, 0, 0, 0, display->black, ZP);	
	line(file, Pt( 7,  1), Pt(10,  4), 0, 0, 0, display->black, ZP);
}

char*
mdate(Dir d)
{
	char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
			   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
	Tm *tm;

	tm = localtime(d.mtime);
	return smprint("%s %02d %02d:%02d", months[tm->mon], tm->mday, tm->hour, tm->min);
}

Rectangle
drawbutton(Point *p, char *s)
{
	Rectangle r;
	int w;

	string(screen, *p, toolfg, ZP, font, s);
	w = stringwidth(font, s);
	r = Rect(p->x, p->y, p->x+w, p->y+lineh);
	p->x += w+Toolpadding;
	line(screen, Pt(p->x, toolr.min.y), Pt(p->x, toolr.max.y-1), 0, 0, 0, scrollbg, ZP);
	p->x += Toolpadding;
	return r;
}

void
redraw(void)
{
	Point p;
	int i, h, y;
	char buf[255], *t;
	Dir d;
	Image *img;

	draw(screen, screen->r, display->white, nil, ZP);
	p = addpt(screen->r.min, Pt(Toolpadding, Toolpadding));
	draw(screen, toolr, toolbg, nil, ZP);
	line(screen, Pt(toolr.min.x, toolr.max.y), toolr.max, 0, 0, 0, toolfg, ZP);
	homer = drawbutton(&p, "Home");
	cdr = drawbutton(&p, "Cd");
	upr = drawbutton(&p, "Up");
	string(screen, p, toolfg, ZP, font, path);
	draw(screen, scrollr, scrollbg, nil, ZP);
	if(ndirs>0){
		h = ((double)nlines/ndirs)*Dy(scrollr);
		y = ((double)offset/ndirs)*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);
	p = addpt(viewr.min, Pt(Toolpadding, Toolpadding));
	for(i = 0; i<nlines && offset+i<ndirs; i++){
		d = dirs[offset+i];
		t = mdate(d);
		snprint(buf, sizeof buf, "%-32s %12lld  %s", d.name, d.length, t);
		free(t);
		img = (d.qid.type&QTDIR) ? folder : file;
		p.y -= Padding;
		draw(screen, Rect(p.x, p.y+Padding, p.x+12, p.y+Padding+12), img, nil, ZP);
		p.x += 12+4+Padding;
		p.y += Padding;
		string(screen, p, viewfg, ZP, font, buf);
		p.x = viewr.min.x+Toolpadding;
		p.y += lineh;
	}
}

void
scrollup(int off)
{
	if(offset == 0)
		return;
	offset -= off;
	if(offset < 0)
		offset = 0;
	redraw();
}

void
scrolldown(int off)
{
	if(offset+nlines > ndirs)
		return;
	offset += off;
	redraw();
}

void
eresized(int new)
{
	if(new && getwindow(display, Refnone)<0)
		sysfatal("cannot reattach: %r");
	lineh = Padding+font->height+Padding;
	toolr = screen->r;
	toolr.max.y = toolr.min.y+lineh+Toolpadding;
	scrollr = screen->r;
	scrollr.min.y = toolr.max.y+1;
	scrollr.max.x = scrollr.min.x + Scrollwidth;
	scrollr = insetrect(scrollr, 1);
	viewr = screen->r;
	viewr.min.x += Scrollwidth;
	viewr.min.y = toolr.max.y+1;
	nlines = Dy(viewr)/lineh;
	redraw();
}

void
evtkey(Rune k)
{
	switch(k){
	case 'q':
	case Kdel:
		exits(nil);
		break;
	case Kpgup:
		scrollup(nlines);
		break;
	case Kpgdown:
		scrolldown(nlines);
		break;
	case Khome:
		cd(nil);
		redraw();
		break;
	case Kup:
		up();
		redraw();
		break;
	}
}

void
evtmouse(Mouse m)
{
	int n;
	Dir d;
	char buf[256] = {0};

	if(m.buttons&4){
		if(ptinrect(m.xy, homer)){
			cd(nil);
			redraw();
		}else if(ptinrect(m.xy, upr)){
			up();
			redraw();
		}else if(ptinrect(m.xy, cdr)){
			if(eenter("Directory", buf, sizeof buf, &m)>0){
				cd(buf);
				redraw();
			}
		}else if(ptinrect(m.xy, viewr)){
			n = (m.xy.y-viewr.min.y)/lineh;
			if(offset+n>=ndirs)
				return;
			d = dirs[offset+n];
			if(d.qid.type && QTDIR){
				cd(d.name);
				redraw();
			}else
				plumbfile(path, d.name);
		}
	}else if(m.buttons&8)
		scrollup(Slowscroll);
	else if(m.buttons&16)
		scrolldown(Slowscroll);
}


void
main(int argc, char *argv[])
{
	Event e;

	offset = 0;
	if(argc==2)
		snprint(path, sizeof path, argv[1]);
	else
		getwd(path, sizeof path);
	readhome();
	loaddirs();
	if(initdraw(nil, nil, "vdir")<0)
		sysfatal("initdraw: %r");
	initcolors();
	initimages();
	einit(Emouse|Ekeyboard);
	eresized(0);
	for(;;){
		switch(event(&e)){
		case Ekeyboard:
			evtkey(e.kbdc);
			break;
		case Emouse:
			evtmouse(e.mouse);
			break;
		}
	}
}