From f8e96a972786a2448b764cba4be88aa05d708933 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 1 Dec 2009 00:53:06 +0100 Subject: structural improvements --- ranger.py | 55 ++++++--------------------------- ranger/api.py | 21 +++++++++++++ ranger/command.py | 24 ++++++++++----- ranger/conf/keys.py | 9 +++--- ranger/environment.py | 4 +-- ranger/fm.py | 9 +++--- ranger/fsobject.py | 19 +----------- ranger/gui/ui.py | 11 ++++--- ranger/main.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 148 insertions(+), 88 deletions(-) create mode 100644 ranger/main.py diff --git a/ranger.py b/ranger.py index 8a2d5967..9a2e59c1 100755 --- a/ranger.py +++ b/ranger.py @@ -1,5 +1,7 @@ #!/usr/bin/python # coding=utf-8 +# ranger: Browse your files inside the console. + # An embedded shell script. Assuming this file is /usr/bin/ranger, # this hack allows you to use the cd-after-exit feature by typing: @@ -16,53 +18,14 @@ fi return 1 """ -from ranger.fm import FM -from ranger.environment import Environment -from ranger.command import CommandList -from ranger.conf import keys, options -from ranger.gui.defaultui import DefaultUI as UI -from ranger.conf.colorschemes.snow import MyColorScheme - -import sys, os, locale - -try: - assert sys.argv[1] == '--cd-after-exit' - cd_after_exit = True - sys.stderr = sys.stdout - del sys.argv[1] -except: - cd_after_exit = False - -# TODO: Parse arguments - -# TODO: load config - -os.stat_float_times(True) -locale.setlocale(locale.LC_ALL, 'en_US.utf8') - try: - path = os.path.abspath('.') - opt = options.dummy() - - env = Environment(opt) - commandlist = CommandList() - colorscheme = MyColorScheme() - keys.initialize_commands(commandlist) - - my_ui = UI(env, commandlist, colorscheme) - my_fm = FM(env) - my_fm.feed(path, my_ui) - my_fm.run() + from ranger.main import main -finally: - try: - my_ui.exit() - except: - pass +except ImportError as errormessage: + print(errormessage) + print("To run an uninstalled copy of ranger,") + print("launch ranger.py in the top directory.") - if cd_after_exit: - try: - sys.__stderr__.write(env.pwd.path) - except: - pass +else: + main() diff --git a/ranger/api.py b/ranger/api.py index b820e085..5ca40175 100644 --- a/ranger/api.py +++ b/ranger/api.py @@ -8,3 +8,24 @@ def log(txt): f.write("\n") f.close() +ONE_KB = 1024 +UNITS = tuple('BKMGTP') +NINE_THOUSAND = len(UNITS) - 1 + +def human_readable(byte): + import math + + if not byte: + return '0 B' + + its = int(math.log(byte, 2) / 10) + flt = float(byte) / (1 << (10 * its)) + + if its > NINE_THOUSAND: + return '>9000' # off scale + + if int(flt) == flt: + return '%.0f %s' % (flt, UNITS[its]) + + else: + return '%.2f %s' % (flt, UNITS[its]) diff --git a/ranger/command.py b/ranger/command.py index 5e6c74d1..203a0329 100644 --- a/ranger/command.py +++ b/ranger/command.py @@ -9,22 +9,27 @@ class CommandList(): # and when to wait for the rest of the key combination. For "gg" we # will assign "g" to a dummy which tells the program not to do the latter. def rebuild_paths(self): - paths = self.paths - + """ fill the path dictionary with dummie objects """ 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 + if path not in self.paths: + self.paths[path] = self.dummy_object self.dummies_in_paths = True def keypath(self, tup): + """ split a tuple like (a,b,c,d) into [(a,), (a,b), (a,b,c)] """ + length = len(tup) + + if length == 0: + return () + if length == 1: + return (tup, ) + current = [] all = [] @@ -35,6 +40,7 @@ class CommandList(): return all def remove_dummies(self): + """ remove dummie objects in case you have to rebuild a path dictionary which already contains dummie objects. """ for k in tuple(paths.keys()): if paths[k] == self.dummy_object: del paths[k] self.dummies_in_paths = False @@ -49,13 +55,17 @@ class CommandList(): elif isinstance(obj, int): return (obj, ) else: - raise TypeError('need a str or a tuple for str_to_tuple') + raise TypeError('need a str, int or tuple for str_to_tuple') def bind(self, fnc, *keys): + """ create a Command object and assign it to the given key combinations. """ if len(keys) == 0: return + 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 diff --git a/ranger/conf/keys.py b/ranger/conf/keys.py index 3d5fa856..5ad4e510 100644 --- a/ranger/conf/keys.py +++ b/ranger/conf/keys.py @@ -10,9 +10,8 @@ def initialize_commands(cl): # * an integer which represents an ascii code # * a tuple of integers - def move(relative = 0, absolute = None): - return lambda fm: fm.move_pointer( - relative = relative, absolute = absolute) + def move(**keywords): + return lambda fm: fm.move_pointer(**keywords) def move_pages(n): return lambda fm: fm.move_pointer_by_pages(n) @@ -34,8 +33,8 @@ def initialize_commands(cl): cl.bind(FM.edit_file, 'E') # toggle options - cl.bind(toggle_option('show_hidden'), 'th') - cl.bind(toggle_option('preview_files'), 'tp') + cl.bind(toggle_option('show_hidden'), 'th') + cl.bind(toggle_option('preview_files'), 'tp') cl.bind(toggle_option('directories_first'), 'td') # key combinations which change the current directory diff --git a/ranger/environment.py b/ranger/environment.py index e3d9bb9f..a15abba2 100644 --- a/ranger/environment.py +++ b/ranger/environment.py @@ -4,9 +4,9 @@ from ranger.directory import Directory, NoDirectoryGiven class Environment(): # A collection of data which is relevant for more than # one class. - def __init__(self, opt): + def __init__(self, path, opt): + self.path = abspath(expanduser(path)) self.opt = opt - self.path = None self.pathway = () self.directories = {} self.pwd = None # current directory diff --git a/ranger/fm.py b/ranger/fm.py index 02cf1300..e88ff964 100644 --- a/ranger/fm.py +++ b/ranger/fm.py @@ -2,16 +2,15 @@ from os import devnull null = open(devnull, 'a') class FM(): - def __init__(self, environment): + def __init__(self, environment, ui): 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 + + self.env.enter_dir(self.env.path) + while 1: try: self.ui.draw() diff --git a/ranger/fsobject.py b/ranger/fsobject.py index 485f5165..48a6eee2 100644 --- a/ranger/fsobject.py +++ b/ranger/fsobject.py @@ -42,6 +42,7 @@ class FileSystemObject(object): self.loaded = True import os + from ranger.api import human_readable if os.access(self.path, os.F_OK): self.stat = os.stat(self.path) self.islink = os.path.islink(self.path) @@ -92,21 +93,3 @@ class FileSystemObject(object): self.load() return True return False - -ONE_KB = 1024 -UNITS = tuple('BKMGTY') -MAX_I = len(UNITS) - 1 - -def human_readable(byte): - i = 0 - flt = float(byte) - - while flt > ONE_KB and i < MAX_I: - flt /= ONE_KB - i += 1 - - if int(flt) == flt: - return '%.0f %s' % (flt, UNITS[i]) - - else: - return '%.2f %s' % (flt, UNITS[i]) diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 77f88080..34f7c54c 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -5,14 +5,10 @@ class UI(): self.env = env self.commandlist = commandlist self.colorscheme = colorscheme + self.is_set_up = False self.widgets = [] - self.initialize() - - self.setup() - self.resize() - def initialize(self): self.win = curses.initscr() self.win.leaveok(1) @@ -28,6 +24,11 @@ class UI(): avail, old = curses.mousemask(mask) curses.mousemask(avail) + if not self.is_set_up: + self.is_set_up = True + self.setup() + self.resize() + def handle_mouse(self, fm): try: event = MouseEvent(curses.getmouse()) diff --git a/ranger/main.py b/ranger/main.py new file mode 100644 index 00000000..14953682 --- /dev/null +++ b/ranger/main.py @@ -0,0 +1,84 @@ +import sys +import os +import locale +from optparse import OptionParser, SUPPRESS_HELP + +from ranger.fm import FM +from ranger.environment import Environment +from ranger.command import CommandList +from ranger.conf import keys, options +from ranger.gui.defaultui import DefaultUI as UI +from ranger.conf.colorschemes.snow import MyColorScheme + +VERSION = '1.0.0' + +USAGE = '''%s [options] [path/filename]''' + +def main(): + try: + import curses + except ImportError as errormessage: + print(errormessage) + print('ranger requires the python curses module. Aborting.') + sys.exit(1) + + locale.setlocale(locale.LC_ALL, 'en_US.utf8') + os.stat_float_times(True) + + # Parse options + parser = OptionParser( + usage = USAGE, + version = 'ranger ' + VERSION ) + + # Instead of using this directly, use the embedded + # shell script by running ranger with: + # source /path/to/ranger /path/to/ranger + parser.add_option( '--cd-after-exit', + action = 'store_true', + dest = 'cd_after_exit', + help = SUPPRESS_HELP ) + + args, rest = parser.parse_args() + + if args.cd_after_exit: + sys.stderr = sys.__stdout__ + if rest[0] == sys.argv[0]: + del rest[0] + + # Initialize objects + target = ' '.join(rest) + if target: + if not os.access(target, os.F_OK): + print("File or directory doesn't exist: %s" % target) + sys.exit(1) + elif os.path.isfile(target): + FM.execute_file(FM(0, 0), target) + sys.exit(0) + else: + path = target + + else: + path = '.' + + opt = options.dummy() + + env = Environment(path, opt) + commandlist = CommandList() + colorscheme = MyColorScheme() + keys.initialize_commands(commandlist) + + my_ui = UI(env, commandlist, colorscheme) + my_fm = FM(env, my_ui) + + try: + # Run the file manager + my_ui.initialize() + my_fm.run() + + finally: + # Finish, clean up + my_ui.exit() + + if args.cd_after_exit: + try: sys.__stderr__.write(env.pwd.path) + except: pass -- cgit 1.4.1-2-gfad0