diff options
Diffstat (limited to 'ranger/core/actions.py')
-rw-r--r-- | ranger/core/actions.py | 619 |
1 files changed, 343 insertions, 276 deletions
diff --git a/ranger/core/actions.py b/ranger/core/actions.py index e55d65b1..6910871b 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -18,15 +18,288 @@ import shutil from inspect import cleandoc import ranger -from ranger.shared import EnvironmentAware, SettingsAware +from ranger.ext.direction import Direction +from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware from ranger import fsobject from ranger.gui.widgets import console_mode as cmode from ranger.fsobject import File -class Actions(EnvironmentAware, SettingsAware): +class Actions(FileManagerAware, EnvironmentAware, SettingsAware): search_method = 'ctime' search_forward = False + # -------------------------- + # -- Backwards Compatibility + # -------------------------- + # All methods defined here are just for backwards compatibility, + # allowing old configuration files to work with newer versions. + # You can delete them and they should change nothing if you use + # an up-to-date configuration file. + + def dummy(self, *args, **keywords): + """For backwards compatibility only.""" + + handle_mouse = resize = dummy + + def move_left(self, narg=1): + """Enter the parent directory""" + self.move(left=1, narg=narg) + + def move_right(self, narg=None): + """Enter the current directory or execute the current file""" + self.move(right=1, narg=narg) + + def move_pointer(self, relative=0, absolute=None, narg=None): + """Move the pointer down by <relative> or to <absolute>""" + dct = dict(down=relative, narg=narg) + if absolute is not None: + dct['to'] = absolute + self.move(**dct) + + def move_pointer_by_pages(self, relative): + """Move the pointer down by <relative> pages""" + self.move(down=relative, pages=True) + + def move_pointer_by_percentage(self, relative=0, absolute=None, narg=None): + """Move the pointer down to <absolute>%""" + self.move(to=absolute, percentage=True, narg=narg) + + # -------------------------- + # -- Basic Commands + # -------------------------- + + def exit(self): + """Exit the program""" + raise SystemExit() + + def reset(self): + """Reset the filemanager, clearing the directory buffer""" + old_path = self.env.cwd.path + self.env.garbage_collect(-1) + self.enter_dir(old_path) + + def reload_cwd(self): + try: + cwd = self.env.cwd + except: + pass + cwd.unload() + cwd.load_content() + + def notify(self, text, duration=4, bad=False): + if isinstance(text, Exception): + if ranger.arg.debug: + raise + bad = True + text = str(text) + self.log.appendleft(text) + if hasattr(self.ui, 'notify'): + self.ui.notify(text, duration=duration, bad=bad) + + def redraw_window(self): + """Redraw the window""" + self.ui.redraw_window() + + def open_console(self, mode=':', string=''): + """Open the console if the current UI supports that""" + if hasattr(self.ui, 'open_console'): + self.ui.open_console(mode, string) + + def execute_file(self, files, **kw): + """Execute a file. + app is the name of a method in Applications, without the "app_" + flags is a string consisting of runner.ALLOWED_FLAGS + mode is a positive integer. + Both flags and mode specify how the program is run.""" + + if isinstance(files, set): + files = list(files) + elif type(files) not in (list, tuple): + files = [files] + + return self.run(files=list(files), **kw) + + # -------------------------- + # -- Moving Around + # -------------------------- + + def move(self, narg=None, **kw): + """ + A universal movement method. + + Accepts these parameters: + (int) down, (int) up, (int) left, (int) right, (int) to, + (bool) absolute, (bool) relative, (bool) pages, + (bool) percentage + + to=X is translated to down=X, absolute=True + + Example: + self.move(down=4, pages=True) # moves down by 4 pages. + self.move(to=2, pages=True) # moves to page 2. + self.move(to=1, percentage=True) # moves to 80% + """ + direction = Direction(kw) + if 'left' in direction: + steps = direction.left() + if narg is not None: + steps *= narg + try: + directory = os.path.join(*(['..'] * steps)) + except: + return + self.env.enter_dir(directory) + + elif 'right' in direction: + mode = 0 + if narg is not None: + mode = narg + cf = self.env.cf + selection = self.env.get_selection() + if not self.env.enter_dir(cf) and selection: + if self.execute_file(selection, mode=mode) is False: + self.open_console(cmode.OPEN_QUICK) + + elif direction.vertical(): + newpos = direction.move( + direction=direction.down(), + override=narg, + maximum=len(self.env.cwd), + current=self.env.cwd.pointer, + pagesize=self.ui.browser.hei) + self.env.cwd.move(absolute=newpos) + + def history_go(self, relative): + """Move back and forth in the history""" + self.env.history_go(relative) + + def scroll(self, relative): + """Scroll down by <relative> lines""" + if hasattr(self.ui, 'scroll'): + self.ui.scroll(relative) + self.env.cf = self.env.cwd.pointed_obj + + def enter_dir(self, path, remember=False, history=True): + """Enter the directory at the given path""" + if remember: + cwd = self.env.cwd + result = self.env.enter_dir(path, history=history) + self.bookmarks.remember(cwd) + return result + return self.env.enter_dir(path, history=history) + + def cd(self, path, remember=True): + """enter the directory at the given path, remember=True""" + self.enter_dir(path, remember=remember) + + def traverse(self): + cf = self.env.cf + cwd = self.env.cwd + if cf is not None and cf.is_directory: + self.enter_dir(cf.path) + elif cwd.pointer >= len(cwd) - 1: + while True: + self.move(left=1) + cwd = self.env.cwd + if cwd.pointer < len(cwd) - 1: + break + if cwd.path == '/': + break + self.move(down=1) + self.traverse() + else: + self.move(down=1) + self.traverse() + + # -------------------------- + # -- Shortcuts / Wrappers + # -------------------------- + + def execute_command(self, cmd, **kw): + return self.run(cmd, **kw) + + def edit_file(self, file=None): + """Calls execute_file with the current file and app='editor'""" + if file is None: + file = self.env.cf + elif isinstance(file, str): + file = File(os.path.expanduser(file)) + if file is None: + return + self.execute_file(file, app = 'editor') + + def toggle_boolean_option(self, string): + """Toggle a boolean option named <string>""" + if isinstance(self.env.settings[string], bool): + self.env.settings[string] ^= True + + def set_option(self, optname, value): + """Set the value of an option named <optname>""" + self.env.settings[optname] = value + + def sort(self, func=None, reverse=None): + if reverse is not None: + self.env.settings['sort_reverse'] = bool(reverse) + + if func is not None: + self.env.settings['sort'] = str(func) + + def set_filter(self, fltr): + try: + self.env.cwd.filter = fltr + except: + pass + + def mark(self, all=False, toggle=False, val=None, movedown=None, narg=1): + """ + A wrapper for the directory.mark_xyz functions. + + Arguments: + all - change all files of the current directory at once? + toggle - toggle the marked-status? + val - mark or unmark? + """ + + if self.env.cwd is None: + return + + cwd = self.env.cwd + + if not cwd.accessible: + return + + if movedown is None: + movedown = not all + + if val is None and toggle is False: + return + + if all: + if toggle: + cwd.toggle_all_marks() + else: + cwd.mark_all(val) + else: + for i in range(cwd.pointer, min(cwd.pointer + narg, len(cwd))): + item = cwd.files[i] + if item is not None: + if toggle: + cwd.toggle_mark(item) + else: + cwd.mark_item(item, val) + + if movedown: + self.move(down=narg) + + if hasattr(self.ui, 'redraw_main_column'): + self.ui.redraw_main_column() + if hasattr(self.ui, 'status'): + self.ui.status.need_redraw = True + + # -------------------------- + # -- Searching + # -------------------------- + def search(self, order=None, forward=True): original_order = order if self.search_forward: @@ -74,26 +347,11 @@ class Actions(EnvironmentAware, SettingsAware): self.search_method = order self.search_forward = forward - def resize(self): - """Update the size of the UI""" - self.ui.update_size() - - def exit(self): - """Exit the program""" - raise SystemExit() - - def enter_dir(self, path, remember=False): - """Enter the directory at the given path""" - if remember: - cwd = self.env.cwd - result = self.env.enter_dir(path) - self.bookmarks.remember(cwd) - return result - return self.env.enter_dir(path) - - def cd(self, path, remember=True): - """enter the directory at the given path, remember=True""" - self.enter_dir(path, remember) + # -------------------------- + # -- Tags + # -------------------------- + # Tags are saved in ~/.ranger/tagged and simply mark if a + # file is important to you in any context. def tag_toggle(self, movedown=None): try: @@ -107,7 +365,7 @@ class Actions(EnvironmentAware, SettingsAware): if movedown is None: movedown = len(sel) == 1 if movedown: - self.move_pointer(relative=1) + self.move(down=1) if hasattr(self.ui, 'redraw_main_column'): self.ui.redraw_main_column() @@ -124,11 +382,16 @@ class Actions(EnvironmentAware, SettingsAware): if movedown is None: movedown = len(sel) == 1 if movedown: - self.move_pointer(relative=1) + self.move(down=1) if hasattr(self.ui, 'redraw_main_column'): self.ui.redraw_main_column() + # -------------------------- + # -- Bookmarks + # -------------------------- + # Using ranger.container.bookmarks. + def enter_bookmark(self, key): """Enter the bookmark with the name <key>""" try: @@ -148,35 +411,10 @@ class Actions(EnvironmentAware, SettingsAware): """Delete the bookmark with the name <key>""" self.bookmarks.delete(key) - def move_left(self, narg=None): - """Enter the parent directory""" - if narg is None: - narg = 1 - try: - directory = os.path.join(*(['..'] * narg)) - except: - return - self.env.enter_dir(directory) - - def move_right(self, mode=0, narg=None): - """Enter the current directory or execute the current file""" - cf = self.env.cf - sel = self.env.get_selection() - - if isinstance(narg, int): - mode = narg - if not self.env.enter_dir(cf): - if sel: - if self.execute_file(sel, mode=mode) is False: - self.open_console(cmode.OPEN_QUICK) - - def history_go(self, relative): - """Move back and forth in the history""" - self.env.history_go(relative) - - def handle_mouse(self): - """Handle mouse-buttons if one was pressed""" - self.ui.handle_mouse() + # -------------------------- + # -- Pager + # -------------------------- + # These commands open the built-in pager and set specific sources. def display_command_help(self, console_widget): if not hasattr(self.ui, 'open_pager'): @@ -239,229 +477,59 @@ class Actions(EnvironmentAware, SettingsAware): pager = self.ui.open_embedded_pager() pager.set_source(f) - def execute_file(self, files, **kw): - """Execute a file. - app is the name of a method in Applications, without the "app_" - flags is a string consisting of runner.ALLOWED_FLAGS - mode is a positive integer. - Both flags and mode specify how the program is run.""" - - if isinstance(files, set): - files = list(files) - elif type(files) not in (list, tuple): - files = [files] - - return self.run(files=list(files), **kw) - - def execute_command(self, cmd, **kw): - return self.run(cmd, **kw) - - def edit_file(self, file=None): - """Calls execute_file with the current file and app='editor'""" - if file is None: - file = self.env.cf - elif isinstance(file, str): - file = File(os.path.expanduser(file)) - if file is None: - return - self.execute_file(file, app = 'editor') - - def open_console(self, mode=':', string=''): - """Open the console if the current UI supports that""" - if hasattr(self.ui, 'open_console'): - self.ui.open_console(mode, string) - - def move_pointer(self, relative = 0, absolute = None, narg=None): - """Move the pointer down by <relative> or to <absolute>""" - self.env.cwd.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) + # -------------------------- + # -- Tabs + # -------------------------- + # This implementation of tabs is very simple and keeps track of + # directory paths only. + + def tab_open(self, name, path=None): + do_emit_signal = name != self.current_tab + self.current_tab = name + if path or (name in self.tabs): + self.enter_dir(path or self.tabs[name]) 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.cwd.move(relative=int(relative * self.env.termsize[0])) - - def move_pointer_by_percentage(self, relative=0, absolute=None, narg=None): - """Move the pointer down by <relative>% or to <absolute>%""" - try: - factor = len(self.env.cwd) / 100.0 - except: - return - - if narg is not None: - absolute = narg - - if absolute is not None: - absolute = int(absolute * factor) - - self.env.cwd.move( - relative=int(relative * factor), - absolute=absolute) - - def scroll(self, relative): - """Scroll down by <relative> lines""" - if hasattr(self.ui, 'scroll'): - self.ui.scroll(relative) - self.env.cf = self.env.cwd.pointed_obj - - def redraw_window(self): - """Redraw the window""" - self.ui.redraw_window() - - def reset(self): - """Reset the filemanager, clearing the directory buffer""" - old_path = self.env.cwd.path - self.env.directories = {} - self.enter_dir(old_path) - - def toggle_boolean_option(self, string): - """Toggle a boolean option named <string>""" - if isinstance(self.env.settings[string], bool): - self.env.settings[string] ^= True - - def sort(self, func=None, reverse=None): - if reverse is not None: - self.env.settings['reverse'] = bool(reverse) - - if func is not None: - self.env.settings['sort'] = str(func) - - def force_load_preview(self): - cf = self.env.cf - if hasattr(cf, 'unload') and hasattr(cf, 'load_content'): - cf.unload() - cf.load_content() - - def reload_cwd(self): - try: - cwd = self.env.cwd - except: - pass - cwd.unload() - cwd.load_content() - - def traverse(self): - cf = self.env.cf - cwd = self.env.cwd - if cf is not None and cf.is_directory: - self.enter_dir(cf.path) - elif cwd.pointer >= len(cwd) - 1: - while True: - self.enter_dir('..') - cwd = self.env.cwd - if cwd.pointer < len(cwd) - 1: - break - if cwd.path == '/': - break - self.move_pointer(1) - self.traverse() - else: - self.move_pointer(1) - self.traverse() - - def set_filter(self, fltr): - try: - self.env.cwd.filter = fltr - except: - pass - - def notify(self, text, duration=4, bad=False): - if isinstance(text, Exception): - if ranger.arg.debug: - raise - bad = True - text = str(text) - self.log.appendleft(text) - 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. - - Arguments: - all - change all files of the current directory at once? - toggle - toggle the marked-status? - val - mark or unmark? - """ - - if self.env.cwd is None: - return - - cwd = self.env.cwd - - if not cwd.accessible: - return - - if movedown is None: - movedown = not all - - if val is None and toggle is False: - return - - if all: - if toggle: - cwd.toggle_all_marks() - else: - cwd.mark_all(val) - else: - for i in range(cwd.pointer, min(cwd.pointer + narg, len(cwd))): - item = cwd.files[i] - if item is not None: - if toggle: - cwd.toggle_mark(item) - else: - cwd.mark_item(item, val) - - if movedown: - self.move_pointer(relative=narg) - - if hasattr(self.ui, 'redraw_main_column'): - self.ui.redraw_main_column() - if hasattr(self.ui, 'status'): - self.ui.status.need_redraw = True - - # ------------------------------------ filesystem operations + self._update_current_tab() + if do_emit_signal: + self.signal_emit('tab.change') + + def tab_close(self, name=None): + if name is None: + name = self.current_tab + if name == self.current_tab: + direction = -1 if name == self._get_tab_list()[-1] else 1 + previous = self.current_tab + self.tab_move(direction) + if previous == self.current_tab: + return # can't close last tab + if name in self.tabs: + del self.tabs[name] + + def tab_move(self, offset): + assert isinstance(offset, int) + tablist = self._get_tab_list() + current_index = tablist.index(self.current_tab) + newtab = tablist[(current_index + offset) % len(tablist)] + if newtab != self.current_tab: + self.tab_open(newtab) + + def tab_new(self): + for i in range(10): + i = (i + 1) % 10 + if not i in self.tabs: + self.tab_open(i) + break + + def _get_tab_list(self): + assert len(self.tabs) > 0, "There must be >=1 tabs at all times" + return sorted(self.tabs) + + def _update_current_tab(self): + self.tabs[self.current_tab] = self.env.cwd.path + + # -------------------------- + # -- File System Operations + # -------------------------- def copy(self): """Copy the selected items""" @@ -567,7 +635,6 @@ class Actions(EnvironmentAware, SettingsAware): except OSError as err: self.notify(err) - def rename(self, src, dest): if hasattr(src, 'path'): src = src.path |