Example #1
0
 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)
Example #2
0
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()