ref: ea620d4de1d42f79af3ceae871d69258e292444b
author: telephil9 <[email protected]>
date: Sun Mar 22 14:27:33 EDT 2020
Initial import
--- /dev/null
+++ b/README.md
@@ -1,0 +1,28 @@
+vdir
+=====
+A minimalistic visual directory browser for Plan9.
+![vdir](vdir.png)
+
+Actions are performed using the right mouse button:
+- Home: go to current user's home directory
+- Up: go to the parent directory
+- Cd: present an entry to jump to a given directory
+
+Right-clicking on folders, opens them.
+Right-clicking on files send them to the plumber.
+
+Keyboard shortcuts are:
+- Page up / Page down to scroll
+- Home to go to $home
+- Up arrow to go to parent directory
+- q / Del to quit
+
+Usage:
+------
+Install with the usual ``mk install``
+Run ``vdir [directory]``
+
+Disclaimer:
+-----------
+This was a quick and dirty hack that sure contains some bugs.
+Enjoy anyway :)
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,7 @@
+</$objtype/mkfile
+
+BIN=$home/bin/$objtype
+TARG=vdir
+OFILES=vdir.$O
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/vdir.c
@@ -1,0 +1,351 @@
+#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)
+{
+ folder = allocimage(display, Rect(0, 0, 12, 12), screen->chan, 0, DWhite);
+ line(folder, Pt( 0, 1), Pt( 5, 1), 0, 0, 0, display->black, ZP);
+ line(folder, Pt( 5, 1), Pt( 7, 4), 0, 0, 0, display->black, ZP);
+ line(folder, Pt( 7, 4), Pt(10, 4), 0, 0, 0, display->black, ZP);
+ line(folder, Pt(10, 4), Pt(10, 10), 0, 0, 0, display->black, ZP);
+ line(folder, Pt(10, 10), Pt( 0, 10), 0, 0, 0, display->black, ZP);
+ line(folder, Pt( 0, 10), Pt( 0, 1), 0, 0, 0, display->black, ZP);
+ file = allocimage(display, Rect(0, 0, 12, 12), screen->chan, 0, DWhite);
+ line(file, Pt( 1, 1), Pt( 7, 1), 0, 0, 0, display->black, ZP);
+ line(file, Pt( 7, 1), Pt( 7, 4), 0, 0, 0, display->black, ZP);
+ line(file, Pt( 7, 4), Pt(10, 4), 0, 0, 0, display->black, ZP);
+ line(file, Pt(10, 4), Pt(10, 11), 0, 0, 0, display->black, ZP);
+ line(file, Pt(10, 11), Pt( 1, 11), 0, 0, 0, display->black, ZP);
+ line(file, Pt( 1, 11), Pt( 1, 1), 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");
+ upr = drawbutton(&p, "Up");
+ cdr = drawbutton(&p, "Cd");
+ 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;
+ 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;
+ }
+ }
+}
binary files /dev/null b/vdir.png differ