diff options
-rw-r--r-- | ranger/actions.py | 50 | ||||
-rw-r--r-- | ranger/container/keymap.py | 27 | ||||
-rw-r--r-- | ranger/defaults/keys.py | 128 | ||||
-rw-r--r-- | ranger/ext/direction.py | 120 | ||||
-rw-r--r-- | ranger/gui/ui.py | 2 | ||||
-rw-r--r-- | ranger/gui/widgets/browserview.py | 8 |
6 files changed, 307 insertions, 28 deletions
diff --git a/ranger/actions.py b/ranger/actions.py index c4b75aca..754fd857 100644 --- a/ranger/actions.py +++ b/ranger/actions.py @@ -148,8 +148,10 @@ class Actions(EnvironmentAware, SettingsAware): """Delete the bookmark with the name <key>""" self.bookmarks.delete(key) - def move_left(self, narg=1): + def move_left(self, narg=None): """Enter the parent directory""" + if narg is None: + narg = 1 try: directory = os.path.join(*(['..'] * narg)) except: @@ -274,6 +276,44 @@ class Actions(EnvironmentAware, SettingsAware): self.env.pwd.move(relative=relative, absolute=absolute, narg=narg) + def move(self, dir, narg=None): + if narg is not None: + dir = dir * narg + + self.notify(str(dir)) + + if dir.right is not None: + if dir.right >= 0: + if dir.has_explicit_direction: + self.move_right(narg=dir.right) + else: + self.move_right(narg=dir.original_right - 1) + elif dir.right < 0: + self.move_left(narg=dir.left) + else: + if dir.percent: + if dir.absolute: + self.move_pointer_by_percentage( \ + absolute=dir.down, narg=narg) + else: + self.move_pointer_by_percentage( \ + relative=dir.down, narg=narg) + elif dir.pages: + self.move_pointer_by_pages(dir.down) + elif dir.absolute: + if dir.has_explicit_direction: + self.move_pointer(absolute=dir.down) + else: + self.move_pointer(absolute=dir.original_down) + else: + self.move_pointer(relative=dir.down) + + def draw_bookmarks(self): + self.ui.browser.draw_bookmarks = True + + def hide_bookmarks(self): + self.ui.browser.draw_bookmarks = False + def move_pointer_by_pages(self, relative): """Move the pointer down by <relative> pages""" self.env.pwd.move(relative=int(relative * self.env.termsize[0])) @@ -288,9 +328,12 @@ class Actions(EnvironmentAware, SettingsAware): if narg is not None: absolute = narg + if absolute is not None: + absolute = int(absolute * factor) + self.env.pwd.move( relative=int(relative * factor), - absolute=int(absolute * factor)) + absolute=absolute) def scroll(self, relative): """Scroll down by <relative> lines""" @@ -369,6 +412,9 @@ class Actions(EnvironmentAware, SettingsAware): if hasattr(self.ui, 'notify'): self.ui.notify(text, duration=duration, bad=bad) + def hint(self, text): + self.notify(text) + def mark(self, all=False, toggle=False, val=None, movedown=None, narg=1): """ A wrapper for the directory.mark_xyz functions. diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py index f9be5e95..50b70b33 100644 --- a/ranger/container/keymap.py +++ b/ranger/container/keymap.py @@ -16,6 +16,7 @@ import curses from string import ascii_lowercase from inspect import isfunction, getargspec from ranger.ext.tree import Tree +from ranger.ext.direction import Direction MAX_ALIAS_RECURSION = 20 PASSIVE_ACTION = 9003 @@ -26,25 +27,6 @@ DIRECTION = 'direction' DIRARG = 'dir' ALIASARG = 'alias' -class Direction(object): - """An object with a down and right method""" - def __init__(self, down=0, right=0): - self.down = down - self.right = right - - def copy(self): - new = type(self)() - new.__dict__.update(self.__dict__) - return new - - def __mul__(self, other): - copy = self.copy() - if other is not None: - copy.down *= other - copy.right *= other - return copy - __rmul__ = __mul__ - def to_string(i): """convert a ord'd integer to a string""" try: @@ -142,6 +124,7 @@ class KeyBuffer(object): return None assert isinstance(key, int) assert key >= 0 + self.all_keys.append(key) # evaluate quantifiers if self.eval_quantifier and self._do_eval_quantifier(key): @@ -192,7 +175,11 @@ class KeyBuffer(object): self.failure = True return None else: - direction = match.actions['dir'] * self.direction_quant + if self.direction_quant is not None: + direction = match.actions['dir'] * self.direction_quant + direction.has_explicit_direction = True + else: + direction = match.actions['dir'].copy() self.directions.append(direction) self.direction_quant = None self.eval_command = True diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index f2d93c58..7a17c831 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -337,21 +337,34 @@ def _basic_movement(command_list): def base_directions(): + # Direction Keys map = KeyMap() map('<down>', dir=Direction(down=1)) map('<up>', dir=Direction(down=-1)) map('<left>', dir=Direction(right=-1)) map('<right>', dir=Direction(right=1)) + map('<home>', dir=Direction(down=0, absolute=True)) + map('<end>', dir=Direction(down=-1, absolute=True)) + map('<pagedown>', dir=Direction(down=1, pages=True)) + map('<pageup>', dir=Direction(down=-1, pages=True)) + map('%<any>', dir=Direction(down=1, percent=True, absolute=True)) + map('<space>', dir=Direction(down=1, pages=True)) + map('<CR>', dir=Direction(down=1)) return map def vim(): + # Direction Keys map = KeyMap() map.merge(base_directions()) map('j', alias='<down>') map('k', alias='<up>') map('h', alias='<left>') map('l', alias='<right>') + map('gg', alias='<home>') + map('G', alias='<end>') + map('J', dir=Direction(down=20)) + map('K', dir=Direction(down=-20)) return map @@ -370,9 +383,11 @@ def browser_keys(): @map('<dir>') def move(arg): - arg.fm.move_pointer(relative=arg.direction.down) + arg.fm.move(dir=arg.direction, narg=arg.n) map(fm.exit(), 'Q') + map('<cr>', fm.move(dir=Direction(right=1))) + # --------------------------------------------------------- history map('H', fm.history_go(-1)) map('L', fm.history_go(1)) @@ -391,7 +406,7 @@ def browser_keys(): map('pp', fm.paste()) map('po', fm.paste(overwrite=True)) map('pl', fm.paste_symlink()) - map('p<bg>', fm.notify('press //p// once again to confirm pasting' \ + map('p<bg>', fm.hint('press //p// once again to confirm pasting' \ ', or //l// to create symlinks')) # ---------------------------------------------------- run programs @@ -400,7 +415,116 @@ def browser_keys(): map('.term', fm.execute_command('x-terminal-emulator', flags='d')) map('du', fm.execute_command('du --max-depth=1 -h | less')) + # -------------------------------------------------- toggle options + map('b<bg>', fm.hint("bind_//h//idden //p//review_files" \ + "//d//irectories_first //c//ollapse_preview flush//i//nput")) + map('bh', fm.toggle_boolean_option('show_hidden')) + map('bp', fm.toggle_boolean_option('preview_files')) + map('bi', fm.toggle_boolean_option('flushinput')) + map('bd', fm.toggle_boolean_option('directories_first')) + map('bc', fm.toggle_boolean_option('collapse_preview')) + + # ------------------------------------------------------------ sort + map('o<bg>', 'O<bg>', fm.hint("//s//ize //b//ase//n//ame //m//time" \ + " //t//ype //r//everse")) + sort_dict = { + 's': 'size', + 'b': 'basename', + 'n': 'basename', + 'm': 'mtime', + 't': 'type', + } + + for key, val in sort_dict.items(): + for key, is_capital in ((key, False), (key.upper(), True)): + # reverse if any of the two letters is capital + map('o' + key, fm.sort(func=val, reverse=is_capital)) + map('O' + key, fm.sort(func=val, reverse=True)) + + map('or', 'Or', 'oR', 'OR', lambda arg: \ + arg.fm.sort(reverse=not arg.fm.settings.reverse)) + + # ----------------------------------------------- console shortcuts + @map("A") + def append_to_filename(arg): + command = 'rename ' + arg.fm.env.cf.basename + arg.fm.open_console(cmode.COMMAND, command) + + map('cw', fm.open_console(cmode.COMMAND, 'rename ')) + map('cd', fm.open_console(cmode.COMMAND, 'cd ')) + map('f', fm.open_console(cmode.COMMAND_QUICK, 'find ')) + map('bf', fm.open_console(cmode.COMMAND, 'filter ')) + map('d<bg>', fm.hint('d//u// (disk usage) d//d// (cut)')) + + + # --------------------------------------------- jump to directories + map('gh', fm.cd('~')) + map('ge', fm.cd('/etc')) + map('gu', fm.cd('/usr')) + map('gd', fm.cd('/dev')) + map('gl', fm.cd('/lib')) + map('go', fm.cd('/opt')) + map('gv', fm.cd('/var')) + map('gr', 'g/', fm.cd('/')) + map('gm', fm.cd('/media')) + map('gn', fm.cd('/mnt')) + map('gt', fm.cd('/tmp')) + map('gs', fm.cd('/srv')) + map('gR', fm.cd(RANGERDIR)) + + # ------------------------------------------------------- searching + map('/', fm.open_console(cmode.SEARCH)) + + map('n', fm.search()) + map('N', fm.search(forward=False)) + + map(TAB, fm.search(order='tag')) + map('cc', fm.search(order='ctime')) + map('cm', fm.search(order='mimetype')) + map('cs', fm.search(order='size')) + map('c<bg>', fm.hint('//c//time //m//imetype //s//ize')) + + # ------------------------------------------------------- bookmarks + for key in ALLOWED_BOOKMARK_KEYS: + map("`" + key, "'" + key, fm.enter_bookmark(key)) + map("m" + key, fm.set_bookmark(key)) + map("um" + key, fm.unset_bookmark(key)) + map("`<bg>", "'<bg>", "m<bg>", fm.draw_bookmarks()) + + + map(':', ';', fm.open_console(cmode.COMMAND)) + + # ---------------------------------------------------- change views + map('i', fm.display_file()) + map(ctrl('p'), fm.display_log()) + map('?', KEY_F1, fm.display_help()) + map('w', lambda arg: arg.fm.ui.open_taskview()) + + # ---------------------------------------------------------- custom + # This is useful to track watched episode of a series. + @map(']') + def tag_next_and_run(arg): + fm = arg.fm + fm.tag_remove() + fm.tag_remove(movedown=False) + fm.tag_toggle() + fm.move_pointer(relative=-2) + fm.move_right() + fm.move_pointer(relative=1) + + # "enter" = shortcut for "1l" + map('<cr>', fm.move(Direction(right=2))) + + # ------------------------------------------------ system functions + map('ZZ', fm.exit()) + map(ctrl('R'), fm.reset()) + map('R', fm.reload_cwd()) + map(ctrl('C'), fm.exit()) + map(':', ';', fm.open_console(cmode.COMMAND)) + map('>', fm.open_console(cmode.COMMAND_QUICK)) + map('!', fm.open_console(cmode.OPEN)) + map('r', fm.open_console(cmode.OPEN_QUICK)) return map diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py new file mode 100644 index 00000000..28003941 --- /dev/null +++ b/ranger/ext/direction.py @@ -0,0 +1,120 @@ +# Copyright (c) 2009, 2010 hut <hut@lavabit.com> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +class NoDefault(object): + pass + +class Direction(object): + """An object with a down and right method""" + def __init__(self, right=None, down=None, absolute=False, + percent=False, pages=False, **keywords): + self.has_explicit_direction = False + + if 'up' in keywords: + self.down = -keywords['up'] + else: + self.down = down + + if 'left' in keywords: + self.right = -keywords['left'] + else: + self.right = right + + if 'relative' in keywords: + self.absolute = not relative + else: + self.absolute = absolute + + if 'default' in keywords: + self.default = keywords['default'] + else: + self.default = NoDefault + + self.original_down = self.down + self.original_right = self.right + + self.percent = percent + self.pages = pages + + @property + def up(self): + if self.down is None: + return None + return -self.down + + @property + def left(self): + if self.right is None: + return None + return -self.right + + @property + def relative(self): + return not self.absolute + + def down_or_default(self, default): + if self.has_been_modified: + return self.down + return default + + def steps_down(self, page_length=10): + if self.pages: + return self.down * page_length + else: + return self.down + + def steps_right(self, page_length=10): + if self.pages: + return self.right * page_length + else: + return self.right + + def copy(self): + new = type(self)() + new.__dict__.update(self.__dict__) + return new + + def __mul__(self, other): + copy = self.copy() + if self.absolute: + if self.down is not None: + copy.down = other + if self.right is not None: + copy.right = other + else: + if self.down is not None: + copy.down *= other + if self.right is not None: + copy.right *= other + copy.original_down = self.original_down + copy.original_right = self.original_right + return copy + __rmul__ = __mul__ + + def __str__(self): + s = ['<Direction'] + if self.down is not None: + s.append(" down=" + str(self.down)) + if self.right is not None: + s.append(" right=" + str(self.right)) + if self.absolute: + s.append(" absolute") + else: + s.append(" relative") + if self.pages: + s.append(" pages") + if self.percent: + s.append(" percent") + s.append('>') + return ''.join(s) diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 2b406113..05efa639 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -136,6 +136,8 @@ class UI(DisplayableContainer): kbuf = self.env.keybuffer cmd = kbuf.command + self.fm.hide_bookmarks() + if kbuf.failure: kbuf.clear() return diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py index 54f21fa9..080f1be0 100644 --- a/ranger/gui/widgets/browserview.py +++ b/ranger/gui/widgets/browserview.py @@ -23,6 +23,7 @@ class BrowserView(Widget, DisplayableContainer): ratios = None preview = True preview_available = True + draw_bookmarks = False stretch_ratios = None need_clear = False @@ -60,10 +61,9 @@ class BrowserView(Widget, DisplayableContainer): self.add_child(self.pager) def draw(self): - try: - if self.env.cmd.show_obj.draw_bookmarks: - self._draw_bookmarks() - except AttributeError: + if self.draw_bookmarks: + self._draw_bookmarks() + else: if self.need_clear: self.win.erase() self.need_redraw = True |