diff options
author | hut <hut@lavabit.com> | 2009-11-27 10:49:48 +0100 |
---|---|---|
committer | hut <hut@lavabit.com> | 2009-11-27 10:49:48 +0100 |
commit | 9506fb8e79f2d04a1ab78039bacdbee7b22109b5 (patch) | |
tree | 3d5c682e9c5032a1c23be6a98c9d3d6e7c8224b5 /ranger | |
parent | 5822dff7d91472bf2fc337c68f144e0ce1de09ae (diff) | |
download | ranger-9506fb8e79f2d04a1ab78039bacdbee7b22109b5.tar.gz |
more VROOM
Diffstat (limited to 'ranger')
-rwxr-xr-x | ranger | 34 | ||||
-rw-r--r-- | ranger/__init__.py | 0 | ||||
-rw-r--r-- | ranger/cli.py | 32 | ||||
-rw-r--r-- | ranger/command.py | 77 | ||||
-rw-r--r-- | ranger/debug.py | 12 | ||||
-rw-r--r-- | ranger/defaultui.py | 39 | ||||
-rw-r--r-- | ranger/directory.py | 74 | ||||
-rw-r--r-- | ranger/directory.rb | 66 | ||||
-rw-r--r-- | ranger/environment.py | 73 | ||||
-rw-r--r-- | ranger/file.py | 6 | ||||
-rw-r--r-- | ranger/fm.py | 42 | ||||
-rw-r--r-- | ranger/fsobject.py | 103 | ||||
-rw-r--r-- | ranger/fstype.py | 5 | ||||
-rw-r--r-- | ranger/keys.py | 15 | ||||
-rw-r--r-- | ranger/options.py | 5 | ||||
-rw-r--r-- | ranger/ui.py | 68 | ||||
-rw-r--r-- | ranger/wdisplay.py | 51 | ||||
-rw-r--r-- | ranger/widget.py | 43 | ||||
-rw-r--r-- | ranger/wstatusbar.py | 12 |
19 files changed, 723 insertions, 34 deletions
diff --git a/ranger b/ranger deleted file mode 100755 index d83e8a3e..00000000 --- a/ranger +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/python -# coding=utf-8 - -# TODO: cd after exit - -from code import debug, fm, defaultui, options, environment - -# TODO: find out the real name of this script and include files relative to here - -# TODO: Parse arguments - -# TODO: load config - -def main(): - import locale, os - os.stat_float_times(True) - locale.setlocale(locale.LC_ALL, 'en_US.utf8') - - try: - path = '/srv/music/compilations/' - opt = options.get() - env = environment.Environment(opt) - - my_ui = defaultui.DefaultUI(env) - my_fm = fm.FM(env) - my_fm.feed(path, my_ui) - my_fm.run() - - except: - my_ui.exit() - raise - - -if __name__ == "__main__": main() diff --git a/ranger/__init__.py b/ranger/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ranger/__init__.py diff --git a/ranger/cli.py b/ranger/cli.py new file mode 100644 index 00000000..7f8fd77f --- /dev/null +++ b/ranger/cli.py @@ -0,0 +1,32 @@ +import curses +import _thread + +class CLIError(): pass + +class CLI(): + def __init__(self): + self.lock = _thread.allocalte_lock() + self.running = False + + def start(self): + with self.lock: + stdscr = curses.initscr() + self.running = True + + def exit(self): + self.stop_unless_running() + with self.lock: + self.running = False + curses.nocbreak() + stdscr.keypad(1) + curses.endwin() + + def stop_unless_running(self): + if not self.running: + raise CLIError("This function needs the cli to be runnig!") + + def print(self, text, x=0, y=0, attr=None): + with self.lock: + + + diff --git a/ranger/command.py b/ranger/command.py new file mode 100644 index 00000000..c57edba0 --- /dev/null +++ b/ranger/command.py @@ -0,0 +1,77 @@ +class CommandDummy(): + pass + +class CommandList(): + def __init__(self): + self.commandlist = [] + self.paths = {} + self.dummies_in_paths = False + self.dummy_object = CommandDummy + + def rebuild_paths(self): + paths = self.paths + + if self.dummies_in_paths: + self.remove_dummies() + + for cmd in self.commandlist: + for key in cmd.keys: + path = [] + for path in self.keypath(key): + try: paths[path] + except KeyError: + paths[path] = self.dummy_object + + self.dummies_in_paths = True + + def keypath(self, tup): + current = [] + all = [] + + for i in range(len(tup) - 1): + current.append(tup[i]) + all.append(tuple(current)) + + return all + + def remove_dummies(self): + for k in tuple(paths.keys()): + if paths[k] == self.dummy_object: del paths[k] + self.dummies_in_paths = False + + + def str_to_tuple(self, obj): + """splits a string into a tuple of integers""" + if type(obj) == tuple: + return obj + elif type(obj) == str: + return tuple(map(ord, list(obj))) + else: + raise TypeError('need a str or a tuple for str_to_tuple') + + def bind(self, fnc, *keys): + keys = tuple(map(self.str_to_tuple, keys)) + cmd = Command(fnc, keys) + cmd.commandlist = self + self.commandlist.append(cmd) + for key in keys: + self.paths[key] = cmd + +class Command(): + def __init__(self, fnc, keys): + self.fnc = fnc + self.keys = keys + self.commandlist = None + +# def rebind(keys): +# self.keys = keys +# self.commandlist.rebuild_paths() + + def execute(self, fm): + self.fnc(fm) + +if __name__ == '__main__': + cl = CommandList() + cl.initialize_commands() + + print(cl.paths) diff --git a/ranger/debug.py b/ranger/debug.py new file mode 100644 index 00000000..12d5d654 --- /dev/null +++ b/ranger/debug.py @@ -0,0 +1,12 @@ +# a module to faciliate debuggin + +LOGFILE = '/tmp/errorlog' + +def log(txt): + f = open(LOGFILE, 'a') + f.write("r1: ") + f.write(str(txt)) + f.write("\n") + f.close() + + diff --git a/ranger/defaultui.py b/ranger/defaultui.py new file mode 100644 index 00000000..70ce75ff --- /dev/null +++ b/ranger/defaultui.py @@ -0,0 +1,39 @@ +import ranger.ui +from ranger.wdisplay import WDisplay +from ranger.wstatusbar import WStatusBar + +class DefaultUI(ranger.ui.UI): + def setup(self): + self.statusbar = WStatusBar(self.win) + self.add_widget(self.statusbar) + + self.displays = [ + WDisplay(self.win, -2), + WDisplay(self.win, -1), + WDisplay(self.win, 0), + WDisplay(self.win, 1) ] + self.displays[2].display_infostring = True + self.displays[2].main_display = True + for disp in self.displays: + self.add_widget(disp) + + RATIO = ( 0.15, 0.15, 0.4, 0.3 ) + + def resize(self): + ranger.ui.UI.resize(self) + y, x = self.win.getmaxyx() + + leftborder = 0 + + i = 0 + for ratio in DefaultUI.RATIO: + wid = int(ratio * x) + try: + self.displays[i].setdim(1, leftborder, y-1, wid - 1) + except KeyError: + pass + leftborder += wid + i += 1 + + self.statusbar.setdim(0, 0, 1, x) + diff --git a/ranger/directory.py b/ranger/directory.py new file mode 100644 index 00000000..392f67bc --- /dev/null +++ b/ranger/directory.py @@ -0,0 +1,74 @@ +import ranger.fsobject +from ranger import file, debug + +class Directory(ranger.fsobject.FSObject): + def __init__(self, path): + ranger.fsobject.FSObject.__init__(self, path) + self.content_loaded = False + self.scheduled = False + self.enterable = False + + self.filenames = None + self.files = None + self.filter = None + self.pointed_index = None + self.pointed_file = None + self.index = None + + def load_content(self): + self.stop_if_frozen() + self.load_if_outdated() + self.content_loaded = True + import os + if self.exists and self.runnable: + basenames = os.listdir(self.path) + mapped = map(lambda name: os.path.join(self.path, name), basenames) + self.filenames = list(mapped) + self.infostring = ' %d' % len(self.filenames) # update the infostring + self.files = [] + for name in self.filenames: + if os.path.isdir(name): + f = Directory(name) + else: + f = file.File(name) + f.load() + self.files.append(f) + else: + self.filenames = None + self.files = None + self.infostring = ranger.fsobject.FSObject.BAD_INFO + + def load_content_once(self): + self.stop_if_frozen() + if not self.content_loaded: + self.load_content() + return True + return False + + def load_content_if_outdated(self): + self.stop_if_frozen() + if self.load_content_once(): return True + + import os + real_mtime = os.stat(self.path).st_mtime + cached_mtime = self.stat.st_mtime + + if real_mtime != cached_mtime: + self.load_content() + return True + return False + + def __len__(self): + if not self.accessible: raise ranger.fsobject.NotLoadedYet() + return len(self.filenames) + + def __getitem__(self, key): + if not self.accessible: raise ranger.fsobject.NotLoadedYet() + return self.files[key] + +if __name__ == '__main__': + d = Directory('.') + d.load_filenames() + print(d.filenames) + print(d[1]) + diff --git a/ranger/directory.rb b/ranger/directory.rb new file mode 100644 index 00000000..5c6e84c1 --- /dev/null +++ b/ranger/directory.rb @@ -0,0 +1,66 @@ +# A Class that contains data about directories +class Directory + class LoadStatus + # @n contains a three bit number: x3x2x1 + # x1: + # 0 = not scheduled + # 1 = scheduled + # x3x2: + # 00 = nothing loaded + # 01 = got the list of files + # 10 = <undefined> + # 11 = got the list of files and entry objects + def initialize(n = 0) + @n = 0 + end + + def got_files? + # is bit 2 nd 3 == 01 + return n & 2 == 2 + end + + def scheduled? + # is the first bit 1? + return n & 1 == 1 + end + + def got_objects? + return n & 4 == 4 + end + attr_accessor :n + end + + def initialize(path) + @path = path + @status = LoadStatus.new(0) + @files = [] + @sort_time = nil + @mtime = nil +# @width = 1000 + @read = false + @free_space = nil + @empty = true + @scheduled = false + end + + # {{{ Trivial + def inspect + return "<Directory: #{path}>" + end + alias to_s inspect + + def size + return @files.size + end + + def not_loaded? + return @level == 0 + end + def file_list_loaded? + return @level >= 1 + end + def ready? + return @level >= 2 + end + # }}} +end diff --git a/ranger/environment.py b/ranger/environment.py new file mode 100644 index 00000000..b5b3888c --- /dev/null +++ b/ranger/environment.py @@ -0,0 +1,73 @@ +import os +from ranger.directory import Directory + +class Vector(): + def __init__(self, x, y): + self.x = x + self.y = y + +class Environment(): + # A collection of data which is relevant for more than + # one class. + def __init__(self, opt): + self.opt = opt + self.path = None + self.pathway = () + self.directories = {} + self.pwd = None # current directory + self.cf = None # current file + self.keybuffer = () + self.copy = None + self.termsize = Vector(80, 24) + + def key_append(self, key): + self.keybuffer += (key, ) + + def key_clear(self): + self.keybuffer = () + + def at_level(self, level): + if level <= 0: + try: + return self.pathway[level - 1] + except IndexError: + return None + else: + return self.cf + + def get_directory(self, path): + import os + path = os.path.abspath(path) + try: + return self.directories[path] + except KeyError: + self.directories[path] = Directory(path) + return self.directories[path] + + def enter_dir(self, path): + # get the absolute path + path = os.path.normpath(os.path.join(self.path, path)) + + self.path = path + self.pwd = self.get_directory(path) + + self.pwd.load_content() + + # build the pathway, a tuple of directory objects which lie + # on the path to the current directory. + if path == '/': + self.pathway = (self.get_directory('/'), ) + else: + pathway = [] + currentpath = '/' + for dir in path.split('/'): + currentpath = os.path.join(currentpath, dir) +# debug.log(currentpath) + pathway.append(self.get_directory(currentpath)) + self.pathway = tuple(pathway) + + # set the current file. + if len(self.pwd) > 0: + self.cf = self.pwd[0] + else: + self.cf = None diff --git a/ranger/file.py b/ranger/file.py new file mode 100644 index 00000000..94c62f9a --- /dev/null +++ b/ranger/file.py @@ -0,0 +1,6 @@ +import ranger.fsobject +class File(ranger.fsobject.FSObject): + pass +# def __init__(self, path): +# fsobject.FSObject.__init__(self, path) + diff --git a/ranger/fm.py b/ranger/fm.py new file mode 100644 index 00000000..d616523e --- /dev/null +++ b/ranger/fm.py @@ -0,0 +1,42 @@ +import sys +#LOGFILE = '/tmp/errorlog' +#f = open(LOGFILE, 'a') +#f.write(str(tuple(sys.path)) + "\n") +#f.close() +#import code.ui, code.debug, code.file, code.directory, code.fstype + + +class FM(): + def __init__(self, environment): + self.env = environment + + def feed(self, path, ui): + self.ui = ui + self.env.path = path + self.env.enter_dir(path) + + def run(self): + import time + while 1: + try: + self.ui.draw() + key = self.ui.get_next_key() + self.ui.press(key, self) + except KeyboardInterrupt: + self.env.key_clear() + time.sleep(0.2) + except: + raise + + def exit(self): + raise SystemExit() + + def move_left(self): + self.env.enter_dir('..') + + def move_right(self): + self.env.enter_dir(self.env.cf.path) + + def move_relative(self): + pass + diff --git a/ranger/fsobject.py b/ranger/fsobject.py new file mode 100644 index 00000000..a21818c0 --- /dev/null +++ b/ranger/fsobject.py @@ -0,0 +1,103 @@ +import ranger.fstype + +class FrozenException(Exception): pass +class NotLoadedYet(Exception): pass + +class FSObject(object): + BAD_INFO = '' + def __init__(self, path): + if type(self) == FSObject: + raise TypeError("FSObject is an abstract class and cannot be initialized.") + from os.path import basename + self.path = path + self.basename = basename(path) + self.exists = False + self.accessible = False + self.marked = False + self.tagged = False + self.frozen = False + self.loaded = False + self.runnable = False + self.islink = False + self.brokenlink = False + self.stat = None + self.infostring = None + self.permissions = None + self.type = ranger.fstype.Unknown + + def __str__(self): + return str(self.path) + + # load() reads useful information about the file from the file system + # and caches it in instance attributes. + def load(self): + self.stop_if_frozen() + self.loaded = True + + import os +# try: + if os.access(self.path, os.F_OK): + self.stat = os.stat(self.path) + self.islink = os.path.islink(self.path) + self.exists = True + self.accessible = True + + if os.path.isdir(self.path): + self.type = ranger.fstype.Directory + try: + self.infostring = ' %d' % len(os.listdir(self.path)) + self.runnable = True + except OSError: + self.infostring = FSObject.BAD_INFO + self.runnable = False + self.accessible = False + elif os.path.isfile(self.path): + self.type = ranger.fstype.File + self.infostring = ' %d' % self.stat.st_size + else: + self.type = ranger.fstype.Unknown + self.infostring = None + + else: +# except OSError: + self.islink = False + self.infostring = None + self.type = ranger.fstype.Nonexistent + self.exists = False + self.runnable = False + self.accessible = False + + def load_once(self): + self.stop_if_frozen() + if not self.loaded: + self.load() + return True + return False + + def load_if_outdated(self): + self.stop_if_frozen() + if self.load_once(): return True + + import os + real_mtime = os.stat(self.path).st_mtime + cached_mtime = self.stat.st_mtime + + if real_mtime != cached_mtime: + self.load() + return True + return False + + def clone(self): + clone = type(self)(self.path) + for key in iter(self.__dict__): + clone.__dict__[key] = self.__dict__[key] + return clone + + def frozen_clone(self): + clone = self.clone() + clone.frozen = True + return clone + + def stop_if_frozen(self): + if self.frozen: raise FrozenException('Cannot modify datastructure while it is frozen') + diff --git a/ranger/fstype.py b/ranger/fstype.py new file mode 100644 index 00000000..4bd0988d --- /dev/null +++ b/ranger/fstype.py @@ -0,0 +1,5 @@ +class File: pass +class Directory: pass +class Nonexistent: pass +class Unknown: pass + diff --git a/ranger/keys.py b/ranger/keys.py new file mode 100644 index 00000000..b710a2aa --- /dev/null +++ b/ranger/keys.py @@ -0,0 +1,15 @@ +def initialize_commands(command_list): + from ranger.fm import FM + + cl = command_list + + # note: the bound function will be called with one parameter, which + # is the FM instance. To use functions with multiple parameters, use: + # lambda fm: myfunction(fm, param1, param2, ...) as the function + + cl.bind(FM.move_left, 'h', 'back') + cl.bind(FM.move_right, 'l', 'forward') + cl.bind(FM.exit, 'q') + + cl.rebuild_paths() + diff --git a/ranger/options.py b/ranger/options.py new file mode 100644 index 00000000..eed79f1e --- /dev/null +++ b/ranger/options.py @@ -0,0 +1,5 @@ +def get(): + return [] + +def dummy(): + return [] diff --git a/ranger/ui.py b/ranger/ui.py new file mode 100644 index 00000000..6bc06b19 --- /dev/null +++ b/ranger/ui.py @@ -0,0 +1,68 @@ +import curses +from ranger.debug import log +class UI(): + def __init__(self, env, commandlist): + self.env = env + self.commandlist = commandlist + + self.widgets = [] + self.win = curses.initscr() + self.win.leaveok(1) + curses.noecho() + curses.halfdelay(3) + + self.setup() + self.resize() + + def setup(self): + pass + + def resize(self): + self.env.termsize = self.win.getmaxyx() + + def add_widget(self, widg): + self.widgets.append(widg) + + def feed_env(self, env): + self.env = env + + def press(self, key, fm): + self.env.key_append(key) + log(self.env.keybuffer) + + try: + cmd = self.commandlist.paths[self.env.keybuffer] + except KeyError: + self.env.key_clear() + return + + if cmd == self.commandlist.dummy_object: + return + + cmd.execute(fm) + self.env.key_clear() + + def exit(self): + curses.nocbreak() + curses.echo() + curses.endwin() + + def draw(self): + from ranger.debug import log + self.win.erase() + for widg in self.widgets: + widg.feed_env(self.env) + widg.draw() + self.win.refresh() + log(tuple(map(str, self.env.pathway))) + +# for i in range(1, len(self.env.pwd)): +# f = self.env.pwd.files[i] +# self.win.addstr(i, 0, f.path) +# if f.infostring: self.win.addstr(i, 50, f.infostring) + + def get_next_key(self): + key = self.win.getch() + curses.flushinp() + return key + diff --git a/ranger/wdisplay.py b/ranger/wdisplay.py new file mode 100644 index 00000000..4bd84044 --- /dev/null +++ b/ranger/wdisplay.py @@ -0,0 +1,51 @@ +import ranger.widget +import curses + +class WDisplay(ranger.widget.Widget): + def __init__(self, win, level): + ranger.widget.Widget.__init__(self,win) + self.level = level + self.main_display = False + self.display_infostring = False + + def feed_env(self, env): + self.target = env.at_level(self.level) + + def draw(self): + from ranger.file import File + from ranger.directory import Directory + + if self.target is None: + pass +# self.win.addnstr(self.y, self.x, "---", self.wid) + elif type(self.target) == File: + self.draw_file() + elif type(self.target) == Directory: + self.draw_directory() + else: + self.win.addnstr(self.y, self.x, "unknown type.", self.wid) + + def draw_file(self): + if not self.target.accessible: + self.win.addnstr(self.y, self.x, "not accessible", self.wid) + return + self.win.addnstr(self.y, self.x, "this is a file.", self.wid) + + def draw_directory(self): + self.target.load_content_once() + if not self.target.accessible: + self.win.addnstr(self.y, self.x, "not accessible", self.wid) + return + for i in range(self.hei): + try: + drawed = self.target[i] + except IndexError: + break + self.win.addnstr(self.y + i, self.x, drawed.basename, self.wid) + if self.display_infostring and drawed.infostring: + info = drawed.infostring + x = self.x + self.wid - 1 - len(info) + if x > self.x: + self.win.addstr(self.y + i, x, str(info) + ' ') + + diff --git a/ranger/widget.py b/ranger/widget.py new file mode 100644 index 00000000..725810ec --- /dev/null +++ b/ranger/widget.py @@ -0,0 +1,43 @@ +import curses + +class OutOfBoundsException(Exception): pass + +class Widget(): + def __init__(self, win): + self.win = win + self.setdim(0, 0, 0, 0) + + def setdim(self, y, x, hei=None, wid=None): + maxy, maxx = self.win.getmaxyx() + wid = wid or maxx - x + hei = hei or maxy - y + if x + wid > maxx and y + hei > maxy: + raise OutOfBoundsException("X and Y out of bounds!") + if x + wid > maxx: + raise OutOfBoundsException("X out of bounds!") + if y + hei > maxy: + raise OutOfBoundsException("Y out of bounds!") + + self.x = x + self.y = y + self.wid = wid + self.hei = hei + + def contains_point(self, y, x): + return (x >= self.x and x < self.x + self.wid) and \ + (y >= self.y and y < self.y + self.hei) + + def feed_env(self): + pass + + def feed(self): + pass + + def click(self): + pass + + def draw(self): + pass + + def destroy(self): + pass diff --git a/ranger/wstatusbar.py b/ranger/wstatusbar.py new file mode 100644 index 00000000..e68fb241 --- /dev/null +++ b/ranger/wstatusbar.py @@ -0,0 +1,12 @@ +import curses +import ranger.widget + +class WStatusBar(ranger.widget.Widget): + def feed_env(self, env): + self.pathway = env.pathway + + def draw(self): + self.win.move(self.y, self.x) + for path in self.pathway: + currentx = self.win.getyx()[1] + self.win.addnstr(path.basename + ' / ', (self.wid - currentx)) |