diff options
author | hut <hut@lavabit.com> | 2009-12-27 22:52:14 +0100 |
---|---|---|
committer | hut <hut@lavabit.com> | 2009-12-27 22:52:14 +0100 |
commit | 70aec44f8b5fb168e5f10fbeec332be75f0d67f0 (patch) | |
tree | fcce310cbf8beb786b66561c369b8f10674a4901 | |
parent | f3e9b24a4c14509c023ce14f27f05d5b1e6192e2 (diff) | |
download | ranger-70aec44f8b5fb168e5f10fbeec332be75f0d67f0.tar.gz |
improved key binding implementation
-rw-r--r-- | ranger/container/commandlist.py | 15 | ||||
-rw-r--r-- | ranger/defaults/keys.py | 291 | ||||
-rw-r--r-- | ranger/gui/ui.py | 4 | ||||
-rw-r--r-- | ranger/gui/widgets/console.py | 2 | ||||
-rw-r--r-- | ranger/gui/widgets/process_manager.py | 23 | ||||
-rw-r--r-- | ranger/keyapi.py | 64 |
6 files changed, 220 insertions, 179 deletions
diff --git a/ranger/container/commandlist.py b/ranger/container/commandlist.py index d38116eb..e90f298e 100644 --- a/ranger/container/commandlist.py +++ b/ranger/container/commandlist.py @@ -1,3 +1,15 @@ +class CommandArgument(object): + def __init__(self, fm, displayable, keybuffer): + self.fm = fm + self.wdg = displayable + self.keybuffer = keybuffer + self.n = keybuffer.number + self.keys = str(keybuffer) + +def cmdarg(displayable): + return CommandArgument(displayable.fm, \ + displayable, displayable.env.keybuffer) + class CommandList(object): """ CommandLists are dictionary-like objects which give you a command @@ -112,6 +124,9 @@ class Command(object): def execute(self, *args): """Execute the command""" + def execute_wrap(self, displayable): + self.execute(cmdarg(displayable)) + # def __str__(self): # return 'Cmd({0})'.format(str(self.keys)) diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index c3490c4a..176d8e52 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -1,61 +1,68 @@ -from curses import * -from curses.ascii import * -from ranger import RANGERDIR -from ranger.gui.widgets import console_mode as cmode -from ranger.gui.widgets.console import Console -from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS - -# syntax for binding keys: bind(*keys, fnc) -# fnc is a function which is called with the FM instance, -# keys are one or more key-combinations which are either: -# * a string -# * an integer which represents an ascii code -# * a tuple of integers -# -# in initialize_console_commands, fnc is a function which is -# called with the console widget instance instead. +""" +Syntax for binding keys: bind(*keys, fnc) + +keys are one or more key-combinations which are either: +* a string +* an integer which represents an ascii code +* a tuple of integers + +fnc is a function which is called with the CommandArgument object. + +The CommandArgument object has these methods: +cmdarg.fm: the file manager instance +cmdarg.wdg: the widget or ui instance +cmdarg.n: the number typed before the key combination (if allowed) +cmdarg.keys: the string representation of the used key combination +cmdarg.keybuffer: the keybuffer instance + +Check ranger.keyapi for more information +""" + +from ranger.keyapi import * + +def system_functions(command_list): + """Each commandlist should have those.""" + bind, hint = make_abbreviations(command_list) + + bind(KEY_RESIZE, fm.resize()) + bind(KEY_MOUSE, fm.handle_mouse()) + bind(ctrl('L'), fm.redraw_window()) def initialize_commands(command_list): """Initialize the commands for the main user interface""" - def do(method, *args, **kw): - return lambda fm, n: getattr(fm, method)(*args, **kw) + bind, hint = make_abbreviations(command_list) - def bind(*args): - command_list.bind(args[-1], *args[:-1]) + bind('l', KEY_RIGHT, fm.move_right()) + bind(KEY_ENTER, ctrl('j'), fm.move_right(mode=1)) + bind('H', fm.history_go(-1)) + bind('L', fm.history_go(1)) + bind('J', fm.move_pointer_by_pages(0.5)) + bind('K', fm.move_pointer_by_pages(-0.5)) + bind('E', fm.edit_file()) + bind('i', fm.tag_toggle()) + bind('I', fm.tag_remove()) - bind('l', KEY_RIGHT, do('move_right')) - bind(KEY_ENTER, ctrl('j'), do('move_right', mode=1)) - bind('H', do('history_go', -1)) - bind('L', do('history_go', 1)) - bind('J', do('move_pointer_by_pages', 0.5)) - bind('K', do('move_pointer_by_pages', -0.5)) - bind('E', do('edit_file')) -# bind('o', do('force_load_preview')) - bind('i', do('tag_toggle')) - bind('I', do('tag_remove')) + bind(' ', fm.mark(toggle=True)) + bind('v', fm.mark(all=True, toggle=True)) + bind('V', fm.mark(all=True, val=False)) - bind(' ', do('mark', toggle=True)) - bind('v', do('mark', all=True, toggle=True)) - bind('V', do('mark', all=True, val=False)) + bind('yy', fm.copy()) + bind('dd', fm.cut()) + bind('p', fm.paste()) - bind('yy', do('copy')) - bind('dd', do('cut')) - bind('p', do('paste')) + bind('s', fm.spawn('bash')) - bind('s', do('spawn', 'bash')) - - bind(TAB, do('search', order='tag')) + bind(TAB, fm.search(order='tag')) t_hint = "show_//h//idden //p//review_files //d//irectories_first //a//uto_load_preview //c//ollapse_preview" command_list.hint(t_hint, 't') - bind('th', do('toggle_boolean_option', 'show_hidden')) - bind('tp', do('toggle_boolean_option', 'preview_files')) - bind('td', do('toggle_boolean_option', 'directories_first')) - bind('ta', do('toggle_boolean_option', 'auto_load_preview')) - bind('tc', do('toggle_boolean_option', 'collapse_preview')) + bind('th', fm.toggle_boolean_option('show_hidden')) + bind('tp', fm.toggle_boolean_option('preview_files')) + bind('td', fm.toggle_boolean_option('directories_first')) + bind('ta', fm.toggle_boolean_option('auto_load_preview')) + bind('tc', fm.toggle_boolean_option('collapse_preview')) - sort_hint = "//s//ize //b//ase//n//ame //m//time //t//ype //r//everse" sort_dict = { 's': 'size', 'b': 'basename', @@ -63,14 +70,17 @@ def initialize_commands(command_list): 'm': 'mtime', 't': 'type', } + + # reverse if any of the two letters is capital for key, val in sort_dict.items(): for key, is_upper in ((key.lower(), False), (key.upper(), True)): - # reverse if any of the two letters is capital - bind('o' + key, do('sort', func=val, reverse=is_upper)) - bind('O' + key, do('sort', func=val, reverse=True)) - bind('or', 'Or', 'oR', 'OR', lambda fm, n: \ - fm.sort(reverse=not fm.settings.reverse)) - command_list.hint(sort_hint, 'o', 'O') + bind('o' + key, fm.sort(func=val, reverse=is_upper)) + bind('O' + key, fm.sort(func=val, reverse=True)) + + bind('or', 'Or', 'oR', 'OR', lambda arg: \ + arg.fm.sort(reverse=not arg.fm.settings.reverse)) + + hint('o', 'O', "//s//ize //b//ase//n//ame //m//time //t//ype //r//everse") def edit_name(fm, n): cf = fm.env.cf @@ -78,90 +88,80 @@ def initialize_commands(command_list): fm.open_console(cmode.COMMAND, 'rename ' + cf.basename) bind('A', edit_name) - bind('cw', do('open_console', cmode.COMMAND, 'rename ')) - bind('cd', do('open_console', cmode.COMMAND, 'cd ')) - bind('f', do('open_console', cmode.COMMAND_QUICK, 'find ')) + bind('cw', fm.open_console(cmode.COMMAND, 'rename ')) + bind('cd', fm.open_console(cmode.COMMAND, 'cd ')) + bind('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) - bind('term', do('spawn', 'x-terminal-emulator')) - bind('du', do('runcmd', 'du --max-depth=1 -h | less')) - bind('tf', do('open_console', cmode.COMMAND, 'filter ')) + bind('term', fm.spawn('x-terminal-emulator')) + bind('du', fm.runcmd('du --max-depth=1 -h | less')) + bind('tf', fm.open_console(cmode.COMMAND, 'filter ')) d_hint = 'd//u// (disk usage) d//d// (cut)' command_list.hint(d_hint, 'd') # key combinations which change the current directory - def cd(path): - return lambda fm: fm.enter_dir(path) - - bind('gh', do('cd', '~')) - bind('ge', do('cd', '/etc')) - bind('gu', do('cd', '/usr')) - bind('gr', do('cd', '/')) - bind('gm', do('cd', '/media')) - bind('gn', do('cd', '/mnt')) - bind('gt', do('cd', '~/.trash')) - bind('gs', do('cd', '/srv')) - bind('gR', do('cd', RANGERDIR)) - - bind('n', do('search', forward=True)) - bind('N', do('search', forward=False)) - - bind('cc', do('search', forward=True, order='ctime')) - bind('cm', do('search', forward=True, order='mimetype')) - bind('cs', do('search', forward=True, order='size')) - c_hint = '//c//time //m//imetype //s//ize' - command_list.hint(c_hint, 'c') + bind('gh', fm.enter_dir('~')) + bind('ge', fm.enter_dir('etc')) + bind('gu', fm.enter_dir('/usr')) + bind('gr', fm.enter_dir('/')) + bind('gm', fm.enter_dir('/media')) + bind('gn', fm.enter_dir('/mnt')) + bind('gt', fm.enter_dir('~/.trash')) + bind('gs', fm.enter_dir('/srv')) + bind('gR', fm.enter_dir(RANGERDIR)) + + bind('n', fm.search()) + bind('N', fm.search(forward=False)) + + bind('cc', fm.search(order='ctime')) + bind('cm', fm.search(order='mimetype')) + bind('cs', fm.search(order='mimetype')) + hint('c', '//c//time //m//imetype //s//ize') # bookmarks for key in ALLOWED_BOOKMARK_KEYS: - bind("`" + key, "'" + key, do('enter_bookmark', key)) - bind("m" + key, do('set_bookmark', key)) - bind("um" + key, do('unset_bookmark', key)) + bind("`" + key, "'" + key, fm.enter_bookmark(key)) + bind("m" + key, fm.set_bookmark(key)) + bind("um" + key, fm.unset_bookmark(key)) # system functions - bind(ctrl('D'), 'q', 'ZZ', do('exit')) - bind(ctrl('R'), do('reset')) - bind(ctrl('L'), do('redraw_window')) - bind(ctrl('C'), do('interrupt')) - bind(KEY_RESIZE, do('resize')) - bind(KEY_MOUSE, do('handle_mouse')) - bind(':', do('open_console', cmode.COMMAND)) - bind('>', do('open_console', cmode.COMMAND_QUICK)) - bind('/', do('open_console', cmode.SEARCH)) - bind('?', do('open_console', cmode.SEARCH)) - bind('!', do('open_console', cmode.OPEN)) - bind('r', do('open_console', cmode.OPEN_QUICK)) + system_functions(command_list) + bind(ctrl('D'), 'q', 'ZZ', fm.exit()) + bind(ctrl('R'), fm.reset()) + bind(ctrl('C'), fm.interrupt()) + bind(':', fm.open_console(cmode.COMMAND)) + bind('>', fm.open_console(cmode.COMMAND_QUICK)) + bind('/', fm.open_console(cmode.SEARCH)) + bind('?', fm.open_console(cmode.SEARCH)) + bind('!', fm.open_console(cmode.OPEN)) + bind('r', fm.open_console(cmode.OPEN_QUICK)) # definitions which require their own function: - def test(fm, n): - from ranger import log - log(fm.bookmarks.dct) - bind('x', test) - def ggG(default): # moves to an absolute point, or to a predefined default # if no number is specified. - return lambda fm, n: \ - fm.move_pointer(absolute=(n or default)-1) + return lambda arg: \ + arg.fm.move_pointer(absolute=(arg.n or default)-1) bind('gg', ggG(1)) bind('G', ggG(0)) - bind('%', lambda fm, n: fm.move_pointer_by_percentage(absolute=n or 50)) + bind('%', lambda arg: \ + arg.fm.move_pointer_by_percentage(absolute=arg.n or 50)) def jk(direction): # moves up or down by the specified number or one, in # the predefined direction - return lambda fm, n: \ - fm.move_pointer(relative=(n or 1) * direction) + return lambda arg: \ + arg.fm.move_pointer(relative=(arg.n or 1) * direction) bind('j', KEY_DOWN, jk(1)) bind('k', KEY_UP, jk(-1)) - bind('h', KEY_LEFT, KEY_BACKSPACE, DEL, lambda fm, n: \ - fm.move_left(n or 1)) + bind('h', KEY_LEFT, KEY_BACKSPACE, DEL, lambda arg: \ + arg.fm.move_left(arg.n or 1)) - bind('w', lambda fm, n: fm.ui.open_pman()) + bind('w', lambda arg: arg.fm.ui.open_pman()) command_list.rebuild_paths() @@ -169,70 +169,53 @@ def initialize_commands(command_list): def initialize_console_commands(command_list): """Initialize the commands for the console widget only""" - def bind(*args): - command_list.bind(args[-1], *args[:-1]) - - def do(method, *args, **kw): - return lambda widget: getattr(widget, method)(*args, **kw) - - def do_fm(method, *args, **kw): - return lambda widget: getattr(widget.fm, method)(*args, **kw) + bind, hint = make_abbreviations(command_list) # movement - bind(KEY_UP, do('history_move', -1)) - bind(KEY_DOWN, do('history_move', 1)) - bind(ctrl('b'), KEY_LEFT, do('move', relative = -1)) - bind(ctrl('f'), KEY_RIGHT, do('move', relative = 1)) - bind(ctrl('a'), KEY_HOME, do('move', absolute = 0)) - bind(ctrl('e'), KEY_END, do('move', absolute = -1)) - bind(ctrl('d'), KEY_DC, do('delete', 0)) - bind(ctrl('h'), KEY_BACKSPACE, DEL, do('delete', -1)) - bind(ctrl('w'), do('delete_word')) - bind(ctrl('k'), do('delete_rest', 1)) - bind(ctrl('u'), do('delete_rest', -1)) - bind(ctrl('y'), do('paste')) + bind(KEY_UP, wdg.history_move(-1)) + bind(KEY_DOWN, wdg.history_move(1)) + bind(ctrl('b'), KEY_LEFT, wdg.move(relative = -1)) + bind(ctrl('f'), KEY_RIGHT, wdg.move(relative = 1)) + bind(ctrl('a'), KEY_HOME, wdg.move(absolute = 0)) + bind(ctrl('e'), KEY_END, wdg.move(absolute = -1)) + bind(ctrl('d'), KEY_DC, wdg.delete(0)) + bind(ctrl('h'), KEY_BACKSPACE, DEL, wdg.delete(-1)) + bind(ctrl('w'), wdg.delete_word()) + bind(ctrl('k'), wdg.delete_rest(1)) + bind(ctrl('u'), wdg.delete_rest(-1)) + bind(ctrl('y'), wdg.paste()) # system functions - bind(ctrl('c'), ESC, do('close')) - bind(ctrl('j'), KEY_ENTER, do('execute')) - bind(ctrl('l'), do_fm('redraw')) - bind(TAB, do('tab')) - bind(KEY_BTAB, do('tab', -1)) - bind(KEY_RESIZE, do_fm('resize')) + system_functions(command_list) + bind(ctrl('c'), ESC, wdg.close()) + bind(ctrl('j'), KEY_ENTER, wdg.execute()) + bind(TAB, wdg.tab()) + bind(KEY_BTAB, wdg.tab(-1)) # type keys - def type_key(key): - return lambda con: con.type_key(key) + def type_key(arg): + arg.wdg.type_key(arg.keys) for i in range(ord(' '), ord('~')+1): - bind(i, type_key(i)) + bind(i, type_key) command_list.rebuild_paths() def initialize_process_manager_commands(command_list): """Initialize the commands for the process manager widget""" - from ranger.gui.widgets.process_manager import KeyWrapper as wdg - - def bind(*args): - command_list.bind(args[-1], *args[:-1]) - - def do(method, *args, **kw): - return lambda widget, n: getattr(widget, method)(*args, **kw) - - def do_fm(method, *args, **kw): - return lambda widget, n: getattr(widget.fm, method)(*args, **kw) + system_functions(command_list) + bind, hint = make_abbreviations(command_list) - bind('j', KEY_DOWN, wdg.move(relative=1)) - bind('k', KEY_UP, wdg.move(relative=-1)) - bind('gg', wdg.move(absolute=0)) - bind('G', wdg.move(absolute=-1)) - bind('K', lambda wdg, n: wdg.process_move(0)) - bind('J', lambda wdg, n: wdg.process_move(-1)) + bind('j', KEY_DOWN, nwrap.move(relative=1)) + bind('k', KEY_UP, nwrap.move(relative=-1)) + bind('gg', nwrap.move(absolute=0)) + bind('G', nwrap.move(absolute=-1)) + bind('K', wdg.process_move(0)) + bind('J', wdg.process_move(-1)) - bind('dd', do('process_remove')) + bind('dd', wdg.process_remove()) bind('w', ESC, ctrl('d'), ctrl('c'), - lambda wdg, n: wdg.fm.ui.close_pman()) - bind(KEY_RESIZE, do_fm('resize')) + lambda arg: arg.wdg.fm.ui.close_pman()) command_list.rebuild_paths() diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 974cb9d9..20a263ad 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -129,8 +129,8 @@ class UI(DisplayableContainer): if hasattr(self, 'hint'): self.hint(cmd.text) elif hasattr(cmd, 'execute'): - cmd.execute(self.fm, self.env.keybuffer.number) - self.env.key_clear() + cmd.execute_wrap(self) + self.env.key_clear() def get_next_key(self): """Waits for key input and returns the pressed key""" diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py index 92c57a46..9abc2f7c 100644 --- a/ranger/gui/widgets/console.py +++ b/ranger/gui/widgets/console.py @@ -95,7 +95,7 @@ class Console(Widget): if cmd == self.commandlist.dummy_object: return - cmd.execute(self) + cmd.execute_wrap(self) self.env.key_clear() def type_key(self, key): diff --git a/ranger/gui/widgets/process_manager.py b/ranger/gui/widgets/process_manager.py index 138c70c1..baf4f2ba 100644 --- a/ranger/gui/widgets/process_manager.py +++ b/ranger/gui/widgets/process_manager.py @@ -100,30 +100,9 @@ class ProcessManager(Widget, Accumulator): self.env.key_clear() else: if hasattr(cmd, 'execute'): - cmd.execute(self, self.env.keybuffer.number) + cmd.execute_wrap(self) self.env.key_clear() def get_list(self): return self.fm.loader.queue return self.loader.queue - - -class KeyWrapper(object): - @staticmethod - def move(relative=0, absolute=None): - if absolute is None: - def fnc(wdg, n): - if n is not None: - if relative >= 0: - wdg.move(relative=n) - else: - wdg.move(relative=-n) - else: - wdg.move(relative=relative) - else: - def fnc(wdg, n): - if n is not None: - wdg.move(absolute=n, relative=relative) - else: - wdg.move(absolute=absolute, relative=relative) - return fnc diff --git a/ranger/keyapi.py b/ranger/keyapi.py new file mode 100644 index 00000000..2159660a --- /dev/null +++ b/ranger/keyapi.py @@ -0,0 +1,64 @@ +from curses import * +from curses.ascii import * +from ranger import RANGERDIR +from ranger.gui.widgets import console_mode as cmode +from ranger.container.bookmarks import ALLOWED_KEYS as ALLOWED_BOOKMARK_KEYS +from ranger import log + +def make_abbreviations(command_list): + def bind(*args): + command_list.bind(args[-1], *args[:-1]) + + def hint(*args): + command_list.hint(args[-1], *args[:-1]) + + return bind, hint + +class Wrapper(object): + def __init__(self, firstattr): + self.__firstattr__ = firstattr + + def __getattr__(self, attr): + def wrapper(*args, **keywords): + def bla(command_argument): + obj = getattr(command_argument, self.__firstattr__) + if obj is None: + return + return getattr(obj, attr)(*args, **keywords) + return bla + return wrapper + +fm = Wrapper('fm') +wdg = Wrapper('wdg') + +# fm.enter_dir('~') is translated into lambda arg: arg.fm.enter_dir('~') +# this makes things like this possible: +# bind('gh', fm.enter_dir('~')) +# +# but NOT: (note the 2 dots) +# bind('H', fm.history.go(-1)) +# +# for something like that, use the long version: +# bind('H', lambda arg: arg.fm.history.go(-1)) + + +# Another wrapper for common actions which use a numerical argument: +class nwrap(object): + @staticmethod + def move(relative=0, absolute=None): + if absolute is None: + def fnc(arg): + if arg.n is not None: + if relative >= 0: + arg.wdg.move(relative=arg.n) + else: + arg.wdg.move(relative=-arg.n) + else: + arg.wdg.move(relative=relative) + else: + def fnc(arg): + if arg.n is not None: + arg.wdg.move(absolute=arg.n, relative=relative) + else: + arg.wdg.move(absolute=absolute, relative=relative) + return fnc |