diff options
-rw-r--r-- | ranger/applications.py | 15 | ||||
-rw-r--r-- | ranger/conf/apps.py | 61 | ||||
-rw-r--r-- | ranger/conf/keys.py | 37 | ||||
-rw-r--r-- | ranger/environment.py | 2 | ||||
-rw-r--r-- | ranger/fm.py | 43 | ||||
-rw-r--r-- | ranger/gui/ui.py | 26 | ||||
-rw-r--r-- | ranger/gui/wconsole.py | 134 | ||||
-rw-r--r-- | ranger/helper.py | 52 |
8 files changed, 317 insertions, 53 deletions
diff --git a/ranger/applications.py b/ranger/applications.py new file mode 100644 index 00000000..fd977c43 --- /dev/null +++ b/ranger/applications.py @@ -0,0 +1,15 @@ +ALLOWED_FLAGS = 'sdpSDP' + +class Applications(object): + def get(self, app): + try: + return getattr(self, 'app_' + app) + except AttributeError: + return self.app_default + + def has(self, app): + return hasattr(self, 'app_' + app) + + def all(self): + return [x for x in self.__dict__ if x.startswith('app_')] + diff --git a/ranger/conf/apps.py b/ranger/conf/apps.py new file mode 100644 index 00000000..041b85c8 --- /dev/null +++ b/ranger/conf/apps.py @@ -0,0 +1,61 @@ +from ranger.applications import Applications as SuperClass +from ranger.helper import popen as run + +class CustomApplications(SuperClass): + # How to determine the default application? {{{ + def app_default(self, **kw): + f = kw['mainfile'] + + if f.extension is not None and f.extension in ('pdf'): + return self.app_evince(**kw) + + if f.container: + return self.app_aunpack(**kw) + + if f.video or f.audio: + if f.video: + kw['flags'] += 'd' + return self.app_mplayer(**kw) + + if f.image: + return self.app_feh(**kw) + + if f.document: + return self.app_editor(**kw) + # }}} + + def app_pager(self, **kw): + return run('less', *kw['files'], **kw) + + def app_vim(self, **kw): + return run('vim', *kw['files'], **kw) + + app_editor = app_vim + + def app_mplayer(self, **kw): + if kw['mode'] == 1: + return run('mplayer', *kw['files'], **kw) + + elif kw['mode'] == 2: + return run('mplayer', '-fs', + '-sid', '0', + '-vfm', 'ffmpeg', + '-lavdopts', 'lowres=1:fast:skiploopfilter=all:threads=8', + *kw['files'], **kw) + + else: + return run('mplayer', '-fs', *kw['files'], **kw) + + def app_feh(self, **kw): + return run('feh', *kw['files'], **kw) + + def app_aunpack(self, **kw): + m = kw['mode'] + if m == 0: + kw['flags'] += 'p' + return run('aunpack', '-l', *kw['files'], **kw) + elif m == 1: + return run('aunpack', *kw['files'], **kw) + + def app_evince(self, **kw): + return run('evince', *kw['files'], **kw) diff --git a/ranger/conf/keys.py b/ranger/conf/keys.py index fef36b6e..b9816919 100644 --- a/ranger/conf/keys.py +++ b/ranger/conf/keys.py @@ -12,6 +12,7 @@ def initialize_commands(cl): def curry(fnc, *args, **keywords): return lambda fm: fnc(fm, *args, **keywords) + c=curry def move(**keywords): return lambda fm: fm.move_pointer(**keywords) @@ -20,9 +21,10 @@ def initialize_commands(cl): return lambda fm: fm.move_pointer_by_pages(n) cl.bind(FM.move_left, 'h', curses.KEY_BACKSPACE, 127) - cl.bind(FM.move_right, 'l', curses.KEY_ENTER, ctrl('j')) - cl.bind(curry(FM.history_go, -1), 'H') - cl.bind(curry(FM.history_go, 1), 'L') + cl.bind(FM.move_right, 'l') + cl.bind(c(FM.move_right, mode=1), curses.KEY_ENTER, ctrl('j')) + cl.bind(c(FM.history_go, -1), 'H') + cl.bind(c(FM.history_go, 1), 'L') cl.bind(move( relative = 1 ), 'j') cl.bind(move_pages( 0.5 ), 'J') cl.bind(move( relative = -1 ), 'k') @@ -60,6 +62,9 @@ def initialize_commands(cl): cl.bind(FM.resize, curses.KEY_RESIZE) cl.bind(FM.handle_mouse, curses.KEY_MOUSE) cl.bind(curry(FM.open_console, ':'), ':') + cl.bind(curry(FM.open_console, '/'), '/') + cl.bind(curry(FM.open_console, '!'), '!') + cl.bind(curry(FM.open_console, '@'), 'r') cl.rebuild_paths() @@ -78,22 +83,24 @@ def initialize_console_commands(cl): def curry_fm(fnc, *args, **keywords): return lambda con, fm: fnc(fm, *args, **keywords) + c_fm = curry_fm + c = curry # movement - cl.bind(curry(WConsole.move, relative = -1), curses.KEY_LEFT, ctrl('b')) - cl.bind(curry(WConsole.move, relative = 1), curses.KEY_RIGHT, ctrl('f')) - cl.bind(curry(WConsole.move, absolute = 0), curses.KEY_HOME, ctrl('a')) - cl.bind(curry(WConsole.move, absolute = -1), curses.KEY_END, ctrl('e')) - cl.bind(curry(WConsole.delete, 0), curses.KEY_DC, ctrl('d')) - cl.bind(curry(WConsole.delete, -1), curses.KEY_BACKSPACE, 127, ctrl('h')) - cl.bind(curry(WConsole.delete_rest, -1), ctrl('U')) - cl.bind(curry(WConsole.delete_rest, 1), ctrl('K')) + cl.bind(c(WConsole.move, relative = -1), curses.KEY_LEFT, ctrl('b')) + cl.bind(c(WConsole.move, relative = 1), curses.KEY_RIGHT, ctrl('f')) + cl.bind(c(WConsole.move, absolute = 0), curses.KEY_HOME, ctrl('a')) + cl.bind(c(WConsole.move, absolute = -1), curses.KEY_END, ctrl('e')) + cl.bind(c(WConsole.delete, 0), curses.KEY_DC, ctrl('d')) + cl.bind(c(WConsole.delete, -1), curses.KEY_BACKSPACE, 127, ctrl('h')) + cl.bind(c(WConsole.delete_rest, -1), ctrl('U')) + cl.bind(c(WConsole.delete_rest, 1), ctrl('K')) # system functions - cl.bind(curry(WConsole.close), ESC, ctrl('C')) - cl.bind(curry(WConsole.execute), curses.KEY_ENTER, ctrl('j')) - cl.bind(curry_fm(FM.redraw), ctrl('L')) - cl.bind(curry_fm(FM.resize), curses.KEY_RESIZE) + cl.bind(c(WConsole.close), ESC, ctrl('C')) + cl.bind(WConsole.execute, curses.KEY_ENTER, ctrl('j')) + cl.bind(c_fm(FM.redraw), ctrl('L')) + cl.bind(c_fm(FM.resize), curses.KEY_RESIZE) for i in range(ord(' '), ord('~')): cl.bind(type_key(i), i) diff --git a/ranger/environment.py b/ranger/environment.py index e0ac646a..f5dbe176 100644 --- a/ranger/environment.py +++ b/ranger/environment.py @@ -86,6 +86,8 @@ class Environment(): self.history.pop(0) def enter_dir(self, path, history = True): + path = str(path) + # get the absolute path path = normpath(join(self.path, expanduser(path))) diff --git a/ranger/fm.py b/ranger/fm.py index 05c1e262..95110455 100644 --- a/ranger/fm.py +++ b/ranger/fm.py @@ -1,10 +1,12 @@ from os import devnull +from ranger.conf.apps import CustomApplications as Applications null = open(devnull, 'a') class FM(): def __init__(self, environment, ui): self.env = environment self.ui = ui + self.apps = Applications() def run(self): self.env.enter_dir(self.env.path) @@ -39,13 +41,10 @@ class FM(): def move_left(self): self.env.enter_dir('..') - def move_right(self): - try: - path = self.env.cf.path - if not self.env.enter_dir(path): - self.execute_file(path) - except AttributeError: - pass + def move_right(self, mode = 0): + cf = self.env.cf + if not self.env.enter_dir(cf): + self.execute_file(cf, mode = mode) def history_go(self, relative): self.env.history_go(relative) @@ -53,21 +52,23 @@ class FM(): def handle_mouse(self): self.ui.handle_mouse(self) - def execute_file(self, path): - from subprocess import Popen - Popen(('mplayer', '-fs', path), stdout = null, stderr = null) - + def execute_file(self, files, app = '', flags = '', mode = 0): + if type(files) not in (list, tuple): + files = [files] + + self.apps.get(app)( + mainfile = files[0], + files = files, + flags = flags, + mode = mode, + fm = self, + stdin = None, + apps = self.apps) + def edit_file(self): - from subprocess import Popen - import os - if self.env.cf is None: return - - self.ui.exit() - - p = Popen(('vim', self.env.cf.path)) - os.waitpid(p.pid, 0) - - self.ui.initialize() + if self.env.cf is None: + return + self.execute_file(self.env.cf, app = 'editor') def open_console(self, mode = ':'): if self.ui.can('open_console'): diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 79077cbc..5cc519b1 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -19,22 +19,23 @@ class MouseEvent(): class UI(): def __init__(self, env, commandlist, colorscheme): + import os + os.environ['ESCDELAY'] = '25' # don't know a cleaner way + self.env = env self.commandlist = commandlist self.colorscheme = colorscheme self.is_set_up = False + self.win = curses.initscr() self.widgets = [] def initialize(self): - # dunno if there's a better way for doing this: - import os - os.environ['ESCDELAY'] = '25' - self.win = curses.initscr() self.win.leaveok(0) self.win.keypad(1) + curses.cbreak() curses.noecho() curses.halfdelay(20) curses.curs_set(0) @@ -48,7 +49,17 @@ class UI(): if not self.is_set_up: self.is_set_up = True self.setup() - self.resize() + self.resize() + + def exit(self): + from ranger.helper import log + log("exiting ui!") + self.win.keypad(0) + curses.nocbreak() + curses.echo() + curses.curs_set(1) + curses.mousemask(0) + curses.endwin() def handle_mouse(self, fm): try: @@ -108,11 +119,6 @@ class UI(): cmd.execute(fm) self.env.key_clear() - def exit(self): - curses.nocbreak() - curses.echo() - curses.endwin() - def draw(self): self.win.erase() for widg in self.widgets: diff --git a/ranger/gui/wconsole.py b/ranger/gui/wconsole.py index 4411e83b..fdaa7716 100644 --- a/ranger/gui/wconsole.py +++ b/ranger/gui/wconsole.py @@ -1,7 +1,8 @@ from ranger.gui.widget import Widget as SuperClass import curses -CONSOLE_MODES = tuple(':/?>!') +CONSOLE_MODES = tuple(':@/?>!') +CONSOLE_MODES_DICTIONARY = { '@': 'open with: ' } class WConsole(SuperClass): def __init__(self, win, colorscheme): @@ -14,16 +15,26 @@ class WConsole(SuperClass): initialize_console_commands(self.commandlist) self.last_cursor_mode = 1 self.clear() + self.prompt = None + self.execute_funcs = { + ':': WConsole.execute_command, + '@': WConsole.execute_openwith_quick, + '/': WConsole.execute_search, + '?': WConsole.execute_search, + '>': WConsole.execute_noreturn, + '!': WConsole.execute_openwith } + + def feed_env(self, env): + self.cf = env.cf def draw(self): if self.mode is None: return - - self.win.addstr(self.y, self.x, ":" + self.line) + self.win.addstr(self.y, self.x, self.prompt + self.line) def finalize(self): try: - self.win.move(self.y, self.x + self.pos + 1) + self.win.move(self.y, self.x + self.pos + len(self.prompt)) except: pass @@ -33,6 +44,10 @@ class WConsole(SuperClass): self.last_cursor_mode = curses.curs_set(1) self.mode = mode + try: + self.prompt = CONSOLE_MODES_DICTIONARY[self.mode] + except KeyError: + self.prompt = self.mode self.focused = True self.visible = True return True @@ -50,8 +65,8 @@ class WConsole(SuperClass): def press(self, key, fm, env): from curses.ascii import ctrl, ESC - from ranger.helper import log - log(key) +# from ranger.helper import log +# log(key) try: cmd = self.commandlist.paths[env.keybuffer] @@ -93,13 +108,118 @@ class WConsole(SuperClass): self.pos = 0 def delete(self, mod): + if mod == -1 and len(self.line) == 0: + self.close() pos = self.pos + mod self.line = self.line[0:pos] + self.line[pos+1:] self.move(relative = mod) - def execute(self): + def execute(self, fm): + try: + self.execute_funcs[self.mode] (self, fm) + except KeyError: + pass self.line = '' self.pos = 0 self.close() + def execute_search(self, fm): + pass + + def execute_openwith(self, fm): + line = self.line + if line[0] == '!': + fm.execute_file(tuple(line[1:].split()) + (fm.env.cf.path, )) + else: + fm.execute_file(tuple(line.split()) + (fm.env.cf.path, ), background = True) + + def execute_openwith_quick(self, fm): + split = self.line.split() + app, flags, mode = get_app_flags_mode(self.line, fm) + fm.execute_file( + files = [self.cf], + app = app, + flags = flags, + mode = mode ) + + def execute_noreturn(self, fm): + pass + + def execute_command(self, fm): + pass + +def get_app_flags_mode(line, fm): + app = '' + flags = '' + mode = 0 + split = line.split() + + if len(split) == 0: + pass + + elif len(split) == 1: + part = split[0] + if is_app(part, fm): + app = part + elif is_flags(part): + flags = part + elif is_mode(part): + mode = part + + elif len(split) == 2: + part0 = split[0] + part1 = split[1] + + if is_app(part0, fm): + app = part0 + if is_flags(part1): + flags = part1 + elif is_mode(part1): + mode = part1 + elif is_flags(part0): + flags = part0 + if is_mode(part1): + mode = part1 + elif is_mode(part0): + mode = part0 + if is_flags(part1): + flags = part1 + + elif len(split) >= 3: + part0 = split[0] + part1 = split[1] + part2 = split[2] + + if is_app(part0, fm): + app = part0 + if is_flags(part1): + flags = part1 + if is_mode(part2): + mode = part2 + elif is_mode(part1): + mode = part1 + if is_flags(part2): + flags = part2 + elif is_flags(part0): + flags = part0 + if is_mode(part1): + mode = part1 + elif is_mode(part0): + mode = part0 + if is_flags(part1): + flags = part1 + + return app, flags, int(mode) + +def is_app(arg, fm): + return fm.apps.has(arg) + +def is_flags(arg): + from ranger.applications import ALLOWED_FLAGS + return all(x in ALLOWED_FLAGS for x in arg) + +def is_mode(arg): + return all(x in '0123456789' for x in arg) + + diff --git a/ranger/helper.py b/ranger/helper.py index 5ca40175..150f4f9d 100644 --- a/ranger/helper.py +++ b/ranger/helper.py @@ -29,3 +29,55 @@ def human_readable(byte): else: return '%.2f %s' % (flt, UNITS[its]) + +def waitpid_no_intr(pid): + import os, errno + + while True: + try: + return os.waitpid(pid, 0) + except OSError as e: + if e.errno == errno.EINTR: + continue + else: + raise + +import os +null = open(os.devnull, 'a') + +def popen(*args, **kw): + from subprocess import Popen + from subprocess import PIPE + + flags, fm = kw['flags'], kw['fm'] + for flag in flags: + if ord(flag) <= 90: + bad = flag + flag.lower() + flags = ''.join(c for c in flags if c not in bad) + + args = map(str, args) + popen_kw = {} + + if kw['stdin'] is not None: + popen_kw['stdin'] = kw['stdin'] + + if 's' in flags or 'd' in flags: + popen_kw['stdout'] = popen_kw['stderr'] = popen_kw['stdin'] = null + + if 'p' in flags: + popen_kw['stdout'] = PIPE + p1 = Popen(args, **popen_kw) + kw['stdin'] = p1.stdout + kw['files'] = () + kw['flags'] = ''.join(f for f in kw['flags'] if f in 'd') + p2 = kw['apps'].app_pager(**kw) + return p2 + if 'd' in flags: + p = Popen(args, **popen_kw) + return p + else: + fm.ui.exit() + p = Popen(args, **popen_kw) + waitpid_no_intr(p.pid) + fm.ui.initialize() + return p |