def __init__(self, path, height, width, begy, begx): self.screen = Screen(height, width, begy, begx) self.screen.attr = look.colors["Window"] self.screen.create_window() self.path = util.abspath(path) self.files = [FileStat(os.pardir)] self.mark_files = set() self.mark_size = "0" self.cursor = 0 self.scrolltop = 0 self.maskreg = None self.list = None self.list_title = None self.finder = Finder(self) self.pager = Pager(self) self.history = PathHistory(self)
class Directory(StandardScreen): sort_kind = "Name[^]" sort_updir = False scroll_type = "HalfScroll" statusbar_format = " [{MARK}/{FILE}] {MARKSIZE}bytes {SCROLL}({CURSOR}) {SORT} " def __init__(self, path, height, width, begy, begx): self.screen = Screen(height, width, begy, begx) self.screen.attr = look.colors["Window"] self.screen.create_window() self.path = util.abspath(path) self.files = [FileStat(os.pardir)] self.mark_files = set() self.mark_size = "0" self.cursor = 0 self.scrolltop = 0 self.maskreg = None self.list = None self.list_title = None self.finder = Finder(self) self.pager = Pager(self) self.history = PathHistory(self) @property def file(self): try: return self.files[self.cursor] except IndexError: return self.files[0] def settop(self): self.cursor = 0 def setbottom(self): self.cursor = len(self.files) - 1 def mvcursor(self, x): self.cursor += x def setcursor(self, x): self.cursor = x def mvscroll(self, amount): y, x = self.screen.win.getmaxyx() height = y - 2 bottom = self.scrolltop+height if amount > 0: if bottom >= len(self.files): return for f in self.files[self.scrolltop:self.scrolltop+amount]: f.cache_clear() self.scrolltop += amount if self.cursor < self.scrolltop: self.cursor = self.scrolltop else: if self.scrolltop == 0: return for f in self.files[bottom+amount:bottom]: f.cache_clear() self.scrolltop += amount bottom += amount if self.cursor >= bottom: self.cursor = bottom - 1 def pagedown(self): height = self.screen.win.getmaxyx()[0] - 2 size = len(self.files) if self.scrolltop+height >= size: return for f in self.files[self.scrolltop:self.scrolltop+height]: f.cache_clear() self.scrolltop += height self.cursor += height def pageup(self): if self.scrolltop == 0: return height = self.screen.win.getmaxyx()[0] - 2 for f in self.files[self.scrolltop:self.scrolltop+height]: f.cache_clear() self.scrolltop -= height self.cursor -= height def enter_dir(self): self.chdir(self.file.name) def enter_link(self): if self.file.isdir(): self.enter_dir() def mask(self, regexp): self.maskreg = regexp self.reload() def glob(self, pattern): self.list_title = "Grob:({0})".format(pattern) self.list = list(glob.iglob(pattern)) self.reload() def globdir(self, pattern): def _globdir(dirname): try: files = os.listdir(util.U(dirname)) except OSError: return for f in files: try: path = os.path.join(dirname, f) except UnicodeError: continue if os.path.isdir(path) and not os.path.islink(path): for sub in _globdir(path): yield sub if fnmatch.fnmatch(f, pattern): yield os.path.normpath(path) self.list_title = "Grobdir:({0})".format(pattern) self.list = list(_globdir(os.curdir)) self.reload() def open_listfile(self, path): self.list_title = "File:({0})".format(path) self.list = [] try: with open(path, "r") as f: for line in f: line = line.strip(os.linesep) if os.path.exists(line): line = re.sub("{0}?{1}".format(self.path, os.sep), "", line) self.list.append(line) except Exception as e: message.exception(e) self.reload() def reset(self): if self.ismark(): self.mark_clear() elif self.list is not None: self.list = None self.list_title = None self.reload() elif self.maskreg is not None: self.maskreg = None self.reload() def diskread(self): marks = set(f.name for f in self.mark_files) self.files[:] = [FileStat(os.pardir)] self.mark_files.clear() if self.finder.active: filelist = self.finder.results elif self.list is not None: filelist = self.list else: filelist = os.listdir(self.path) for f in filelist: if not os.path.lexists(f): continue try: fs = FileStat(f) except InvalidEncodingError: continue if self.maskreg: if not (fs.isdir() or self.maskreg.search(fs.name)): continue self.files.append(fs) self.mark_update([f for f in self.files if f.name in marks]) def reload(self): try: os.chdir(self.path) self.diskread() self.sort() except Exception as e: message.exception(e) self.chdir(Workspace.default_path) def chdir(self, path): parpath = util.unix_dirname(self.path) parfile = util.unix_basename(self.path) newpath = util.abspath(util.expanduser(path), self.path) if newpath == self.path: return self.list = None self.list_title = None if self.finder.active: self.finder.finish() self.mark_clear() try: os.chdir(newpath) except Exception as e: return message.exception(e) self.history.update(newpath) self.path = newpath self.diskread() self.sort() if self.path == parpath: self.setcursor(self.get_index(parfile)) else: self.setcursor(0) def get_index(self, fname): for i, f in enumerate(self.files): if fname == f.name: return i return 0 def mark(self, pattern): self.mark_clear() self.mark_update([f for f in self.files if pattern.search(f.name)]) def mark_toggle(self): self.mark_update([self.file], toggle=True) self.mvcursor(+1) def mark_toggle_all(self): self.mark_update(self.files, toggle=True) def mark_below_cursor(self, filetype="all"): self.mark_all(filetype, self.cursor) def mark_all(self, filetype="all", start=0): if filetype == "file": files = [f for f in self.files[start:] if not f.isdir()] elif filetype == "directory": files = [f for f in self.files[start:] if f.isdir()] elif filetype == "symlink": files = [f for f in self.files[start:] if f.islink()] elif filetype == "executable": files = [f for f in self.files[start:] if f.isexec() and not f.isdir() and not f.islink()] elif filetype == "socket": files = [f for f in self.files[start:] if f.issocket()] elif filetype == "fifo": files = [f for f in self.files[start:] if f.isfifo()] elif filetype == "chr": files = [f for f in self.files[start:] if f.ischr()] elif filetype == "block": files = [f for f in self.files[start:] if f.isblock()] else: files = [f for f in self.files[start:]] self.mark_clear() self.mark_update(files) def mark_update(self, fstats, toggle=False): for fs in fstats: if fs.name == os.pardir: continue if toggle: if fs.marktoggle(): self.mark_files.add(fs) else: self.mark_files.discard(fs) else: fs.markon() self.mark_files.add(fs) self.mark_size = self.get_mark_size() def mark_clear(self): for f in self.mark_files: f.markoff() self.mark_files.clear() self.mark_size = "0" def get_mark_size(self): if not self.mark_files: return "0" size = sum(f.stat.st_size for f in self.mark_files if not f.isdir()) return re.sub(r"(\d)(?=(?:\d\d\d)+(?!\d))", r"\1,", str(size)) def get_mark_files(self): return [f.name for f in self.mark_files] or [self.file.name] def ismark(self): return len(self.mark_files) != 0 def sort(self): if self.sort_kind == "Name[^]": self.sort_name(rev=False) elif self.sort_kind == "Name[$]": self.sort_name(rev=True) elif self.sort_kind == "Size[^]": self.sort_size(rev=False) elif self.sort_kind == "Size[$]": self.sort_size(rev=True) elif self.sort_kind == "Time[^]": self.sort_time(rev=False) elif self.sort_kind == "Time[$]": self.sort_time(rev=True) elif self.sort_kind == "Permission[^]": self.sort_permission(rev=False) elif self.sort_kind == "Permission[$]": self.sort_permission(rev=True) elif self.sort_kind == "Link[^]": self.sort_nlink(rev=False) elif self.sort_kind == "Link[$]": self.sort_nlink(rev=True) elif self.sort_kind == "Ext[^]": self.sort_ext(rev=False) elif self.sort_kind == "Ext[$]": self.sort_ext(rev=True) def _sort_default(self, sortfunc, x, y, rev): if x.name == os.pardir: return -1 if y.name == os.pardir: return 1 if self.sort_updir: xdir = x.isdir() ydir = y.isdir() if xdir == ydir: return sortfunc(x, y, rev) else: return 1 if ydir == 1 else -1 else: return sortfunc(x, y, rev) def sort_name(self, rev=False): def _namecompare(x, y, rev): if rev: return util.cmp(y.name, x.name) else: return util.cmp(x.name, y.name) _sort = lambda x, y: self._sort_default(_namecompare, x, y, rev) self.files.sort(key=util.cmp_to_key(_sort)) self.sort_kind = "Name[$]" if rev else "Name[^]" def sort_size(self, rev=False): def _sizecompare(x, y, rev): if rev: ret = util.cmp(y.stat.st_size, x.stat.st_size) else: ret = util.cmp(x.stat.st_size, y.stat.st_size) if ret == 0: return util.cmp(y.name, x.name) if rev else util.cmp(x.name, y.name) else: return ret _sort = lambda x, y: self._sort_default(_sizecompare, x, y, rev) self.files.sort(key=util.cmp_to_key(_sort)) self.sort_kind = "Size[$]" if rev else "Size[^]" def sort_permission(self, rev=False): def _permcompare(x, y, rev): if rev: ret = util.cmp(y.stat.st_mode, x.stat.st_mode) else: ret = util.cmp(x.stat.st_mode, y.stat.st_mode) if ret == 0: return util.cmp(y.name, x.name) if rev else util.cmp(x.name, y.name) else: return ret _sort = lambda x, y: self._sort_default(_permcompare, x, y, rev) self.files.sort(key=util.cmp_to_key(_sort)) self.sort_kind = "Permission[$]" if rev else "Permission[^]" def sort_time(self, rev=False): def _timecompare(x, y, rev): if rev: ret = util.cmp(y.stat.st_mtime, x.stat.st_mtime) else: ret = util.cmp(x.stat.st_mtime, y.stat.st_mtime) if ret == 0: return util.cmp(y.name, x.name) if rev else util.cmp(x.name, y.name) else: return ret _sort = lambda x, y: self._sort_default(_timecompare, x, y, rev) self.files.sort(key=util.cmp_to_key(_sort)) self.sort_kind = "Time[$]" if rev else "Time[^]" def sort_nlink(self, rev=False): def _nlinkcompare(x, y, rev): if rev: ret = util.cmp(y.stat.st_nlink, x.stat.st_nlink) else: ret = util.cmp(x.stat.st_nlink, y.stat.st_nlink) if ret == 0: return util.cmp(y.name, x.name) if rev else util.cmp(x.name, y.name) else: return ret _sort = lambda x, y: self._sort_default(_nlinkcompare, x, y, rev) self.files.sort(key=util.cmp_to_key(_sort)) self.sort_kind = "Link[$]" if rev else "Link[^]" def sort_ext(self, rev=False): def _extcompare(x, y, rev): if rev: ret = util.cmp(util.extname(y.name), util.extname(x.name)) else: ret = util.cmp(util.extname(x.name), util.extname(y.name)) if ret == 0: return util.cmp(y.name, x.name) if rev else util.cmp(x.name, y.name) else: return ret _sort = lambda x, y: self._sort_default(_extcompare, x, y, rev) self.files.sort(key=util.cmp_to_key(_sort)) self.sort_kind = "Ext[$]" if rev else "Ext[^]" def resize(self, height, width, begy, begx): self.screen.resize(height, width, begy, begx) self.screen.attr = look.colors["Window"] self.screen.create_window() self.finder.refresh() self.pager.refresh() def _fix_position(self, size, height): if self.cursor < 0: self.cursor = 0 elif self.cursor >= size: self.cursor = size - 1 if self.cursor >= self.scrolltop+height or self.cursor < self.scrolltop: for f in self.files[self.scrolltop:self.scrolltop+height]: f.cache_clear() if self.scroll_type == "HalfScroll": self.scrolltop = self.cursor - (height//2) elif self.scroll_type == "PageScroll": self.scrolltop = (self.cursor//height) * height elif self.scroll_type == "ContinuousScroll": if self.cursor >= self.scrolltop+height: self.scrolltop = self.cursor - height + 1 elif self.cursor < self.scrolltop: self.scrolltop = self.cursor else: self.scrolltop = self.cursor - (height//2) if self.scrolltop < 0 or size < height: self.scrolltop = 0 elif self.scrolltop >= size: self.scrolltop = (size//height) * height def _draw_titlebar(self, width): title = "" titlewidth = width if not self.path.endswith(os.sep): title += os.sep titlewidth -= len(os.sep) if self.maskreg: title += "{{{0}}}".format(self.maskreg.pattern) titlewidth -= util.termwidth(self.maskreg.pattern) path = util.replhome(self.path) path = util.path_omission(path, titlewidth) self.screen.win.addstr(0, 2, path+title, look.colors["DirectoryPath"]) def _draw_statusbar(self, size, height): try: p = float(self.scrolltop)/float(size-height)*100 except ZeroDivisionError: p = float(self.scrolltop)/float(size)*100 if p == 0: p = "Top" elif p >= 100: p = "Bot" else: p = str(int(p)) + "%" status = self.statusbar_format.format( MARK=len(self.mark_files), FILE=size-1, MARKSIZE=self.mark_size, SCROLL=p, CURSOR=self.cursor, SORT=self.sort_kind) if self.list_title is not None: status += self.list_title y, x = self.screen.win.getmaxyx() if util.termwidth(status) > x-2: status = util.mbs_ljust(status, x-2) self.screen.win.addstr(y-1, 1, status) def draw(self, focus): if self.pager.active: self.pager.draw() return size = len(self.files) win = self.screen.win height = win.getmaxyx()[0] - 2 width = win.getmaxyx()[1] - 3 if self.finder.active: height -= self.finder.panel.y if not height: return win.erase() win.border(*self.borders) self._draw_titlebar(width) self._fix_position(size, height) line = 0 for i in range(self.scrolltop, size): line += 1 if line > height: break f = self.files[i] fstr = f.get_drawn_text(self.path, width) attr = f.get_attr() if self.cursor == i and focus: attr += curses.A_REVERSE if f.marked: win.addstr(line, 1, "*"+fstr, attr) else: win.addstr(line, 1, " "+fstr, attr) self._draw_statusbar(size, height) win.noutrefresh() if self.finder.active: self.finder.draw()