- import curses
- import sys
- import os
- import time
- from curses import panel
- from curses.wrapper import wrapper
- from subprocess import Popen, PIPE
- #shamelessly ripped from http://stackoverflow.com/questions/123958/how-to-get-set-logical-directory-path-in-python
- class CwdKeeper(object):
- def __init__(self):
- self._cwd = os.environ.get("PWD")
- if self._cwd is None: # no environment. fall back to calling pwd on shell
- self._cwd = Popen('pwd', stdout=PIPE).communicate()[0].strip()
- self._os_getcwd = os.getcwd
- self._os_chdir = os.chdir
- def chdir(self, path):
- if not self._cwd:
- return self._os_chdir(path)
- p = os.path.normpath(os.path.join(self._cwd, path))
- result = self._os_chdir(p)
- self._cwd = p
- os.environ["PWD"] = p
- return result
- def getcwd(self):
- if not self._cwd:
- return self._os_getcwd()
- return self._cwd
- class Container:
- pass
- def drawbottom(mainwind, width, height, status):
- mainwind.move(height-2, 1)
- mainwind.clrtoeol()
- mainwind.addch(height-2, width-1, curses.ACS_VLINE)
- mainwind.move(height-2, 1)
- mainwind.addstr(status)
- mainwind.refresh()
- def setupdir(mainwind, infowind, width, height, currdir):
- global d
- mainwind.move(1, 1)
- mainwind.clrtoeol()
- mainwind.addch(1, width-1, curses.ACS_VLINE)
- mainwind.move(1, 1)
- mainwind.addstr("Path: " + currdir)
- mainwind.touchwin()
- mainwind.noutrefresh()
- d.l = os.listdir(currdir) #list of the current dir
- if len(d.l) == 0:
- d.l.append("<EMPTY>")
- d.i = {} #file info
- d.p = curses.newpad(len(d.l), width-3)
- d.selected = 0
- d.oldselected = 0
- d.scroll = 0
- d.bar = 0
- #d.oldbar = 0
- idx = 0
- for x in d.l[:]:
- if x[0] == "." and d.invisible:
- d.l.remove(x)
- continue
- d.p.move(idx, 0)
- if os.path.isdir(x):
- d.i[x] = 1
- d.p.addstr(x, curses.color_pair(1))
- else:
- d.i[x] = 0
- d.p.addstr(x)
- idx = idx + 1
- length = len(d.l)
- if length == 0:
- d.l.append("<EMPTY>")
- d.i["<EMPTY>"] = 0
- if d.l[0] == "<EMPTY>" and len(d.l) == 1:
- length = 0
- s = str(length) + " file"
- if length != 1:
- s = s + "s"
- mainwind.addstr(1, width-2-len(s), s)
- mainwind.addch(3, width-2, curses.ACS_UARROW)
- mainwind.addch(height-4, width-2, curses.ACS_DARROW)
- for x in range(4, height-4):
- mainwind.addch(x, width-2, curses.ACS_VLINE)
- drawdir(mainwind, infowind, width, height)
- def drawdir(mainwind, infowind, width, height):
- global d
- d.p.move(d.oldselected, 0)
- if d.i[d.l[d.oldselected]] == 1:
- d.p.addstr(d.l[d.oldselected], curses.color_pair(1))
- else:
- d.p.addstr(d.l[d.oldselected])
- d.p.move(d.selected, 0)
- d.p.addstr(d.l[d.selected], curses.A_REVERSE)
- d.p.noutrefresh(d.scroll, 0, 3, 1, height-4, width-2)
- mainwind.addch(d.bar+4, width-2, curses.ACS_VLINE)
- try:
- d.bar = int((d.selected*1.0/(len(d.l)-1))*(height-9))
- except:
- d.bar = 0
- #raise Exception(d.bar, d.selected, height-8, len(d.l)-1, (d.selected*1.0/(len(d.l)-1)))
- mainwind.addch(d.bar+4, width-2, " ", curses.A_REVERSE)
- mainwind.noutrefresh()
- if d.info:
- infowind.erase()
- infowind.box()
- infowind.hline(2, 0, curses.ACS_HLINE, 50)
- infowind.addch(2, 0, curses.ACS_LTEE)
- infowind.addch(2, 49, curses.ACS_RTEE)
- fname = d.l[d.selected]
- if len(fname) > 42:
- fname = fname[:-(len(fname)-42+3)] + "..."
- infowind.addstr(1, 1, "File: " + fname)
- statcall = os.stat(d.l[d.selected])
- size = statcall.st_size
- suffixes = ["B", "KB", "MB", "GB", "TB"]
- for suffix in suffixes:
- if size > 1024:
- size /= 1024
- else:
- break
- infowind.addstr(3, 1, "File size: " + str(size) + suffix + " (" + str(statcall.st_size) + " bytes)")
- permslist = ["x", "w", "r"]
- tlist = range(9)
- tlist.reverse()
- if os.path.isdir(d.l[d.selected]):
- permstr = "d"
- else:
- permstr = "-"
- for num in tlist:
- if statcall.st_mode & (1 << num) > 0:
- permstr = permstr + permslist[num%3]
- else:
- permstr = permstr + "-"
- infowind.addstr(4, 1, "Permissions: " + permstr)
- infowind.addstr(5, 1, time.strftime("Last modification: %c", time.localtime(statcall.st_mtime)))
- infowind.noutrefresh()
- curses.doupdate()
- def main(mw):
- global d
- mainwind = mw
- mainwind.addstr("Initializing...")
- mainwind.refresh()
- curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
- mainwind.erase()
- height, width = mainwind.getmaxyx()
- if height < 24 or width < 110:
- return 1
- currdir = dir.getcwd()
- #set up pretty view
- mainwind.box()
- mainwind.hline(2, 0, curses.ACS_HLINE, width)
- mainwind.addch(2, 0, curses.ACS_LTEE)
- mainwind.addch(2, width-1, curses.ACS_RTEE)
- mainwind.hline(height-3, 0, curses.ACS_HLINE, width)
- mainwind.addch(height-3, 0, curses.ACS_LTEE)
- mainwind.addch(height-3, width-1, curses.ACS_RTEE)
- mainwind.move(height-2, 1)
- mainwind.addstr("Commands: q: quit, c: chdir, u: up, s: show/hide info window, i: show/hide invisible files, d: delete file")
- infowind = curses.newwin(10, 50, 5, width-54)
- d.info = False
- d.invisible = True
- setupdir(mainwind, infowind, width, height, currdir)
- dirty = False
- while True:
- mainwind.move(0, 0)
- c = mainwind.getch()
- if dirty == True:
- drawbottom(mainwind, width, height, "Commands: q: quit, c: chdir, u: up, s: show/hide info window, i: show/hide invisible files, d: delete file")
- dirty = False
- if c >= 0 and c < 256:
- c = chr(c)
- if c == "q":
- return
- elif c == curses.KEY_UP:
- if d.selected > 0:
- d.oldselected = d.selected
- d.selected = d.selected - 1
- if d.selected < d.scroll:
- d.scroll -= (height-6)/2
- drawdir(mainwind, infowind, width, height)
- elif c == curses.KEY_DOWN:
- if d.selected < len(d.l)-1:
- d.oldselected = d.selected
- d.selected = d.selected + 1
- if d.selected > d.scroll + height - 6:
- d.scroll += (height-6)/2
- if d.scroll > len(d.l)-height+6:
- d.scroll = len(d.l)-height+6
- drawdir(mainwind, infowind, width, height)
- elif c == "u":
- try:
- dir.chdir("..")
- except OSError as e:
- drawbottom(mainwind, width, height, "CHDir error: " + e[1] + " (" + str(e[0]) + ")")
- dirty = True
- continue
- currdir = dir.getcwd()
- setupdir(mainwind, infowind, width, height, currdir)
- elif c == "c":
- if d.i[d.l[d.selected]] != 1:
- drawbottom(mainwind, width, height, "Error: Not a directory!")
- dirty = True
- else:
- try:
- dir.chdir(d.l[d.selected])
- except OSError as e:
- drawbottom(mainwind, width, height, "CHDir error: " + e[1] + " (" + str(e[0]) + ")")
- dirty = True
- continue
- currdir = dir.getcwd()
- setupdir(mainwind, infowind, width, height, currdir)
- elif c == "s":
- if d.info == True:
- d.info = False
- mainwind.touchwin()
- mainwind.refresh()
- drawdir(mainwind, infowind, width, height)
- else:
- d.info = True
- drawdir(mainwind, infowind, width, height)
- elif c == "i":
- d.invisible = not d.invisible
- setupdir(mainwind, infowind, width, height, currdir)
- elif c == "d":
- dirty = True
- if d.i[d.l[d.selected]] != 0:
- drawbottom(mainwind, width, height, "Error: Not a file!")
- continue
- drawbottom(mainwind, width, height, "Are you sure? ")
- yn = mainwind.getch()
- if yn != ord("y"):
- drawbottom(mainwind, width, height, "File deletion aborted!")
- continue
- try:
- os.unlink(d.l[d.selected])
- setupdir(mainwind, infowind, width, height, currdir)
- drawbottom(mainwind, width, height, "File deleted successfully!")
- except OSError as e:
- drawbottom(mainwind, width, height, "Deletion error: " + e[1] + " (" + str(e[0]) + ")")
- dir = CwdKeeper()
- d = Container()
- r = wrapper(main)
- if r == 1:
- print "Your terminal isn't big enough! It needs to be at least 110x24."
