From 3201b163ffcabd31ab5b82e3192ab43ed5ec006e Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 23 Dec 2009 18:27:51 +0100 Subject: implemented marking, for operations on multiple files --- ranger/actions.py | 57 +++++++++++++++++++++++++----- ranger/colorschemes/default.py | 11 ++++-- ranger/container/environment.py | 12 ++++--- ranger/defaults/keys.py | 3 ++ ranger/fsobject/directory.py | 77 ++++++++++++++++++++++++++++++++++++++--- ranger/fsobject/fsobject.py | 9 ++++- ranger/gui/colorscheme.py | 1 + ranger/gui/widgets/filelist.py | 3 ++ ranger/gui/widgets/statusbar.py | 36 ++++++++++--------- 9 files changed, 172 insertions(+), 37 deletions(-) diff --git a/ranger/actions.py b/ranger/actions.py index 19e0028d..9afb0ade 100644 --- a/ranger/actions.py +++ b/ranger/actions.py @@ -63,12 +63,15 @@ class Actions(EnvironmentAware, SettingsAware): """Enter the parent directory""" self.env.enter_dir('..') - def move_right(self, mode = 0): + def move_right(self, mode=0): """Enter the current directory or execute the current file""" cf = self.env.cf + marked_items = self.env.pwd.marked_items + sel = self.env.get_selection() + if not self.env.enter_dir(cf): - if cf is not None: - if not self.execute_file(cf, mode = mode): + if sel: + if not self.execute_file(sel, mode=mode): self.open_console('@') def history_go(self, relative): @@ -79,18 +82,20 @@ class Actions(EnvironmentAware, SettingsAware): """Handle mouse-buttons if one was pressed""" self.ui.handle_mouse() - def execute_file(self, files, app = '', flags = '', mode = 0): + def execute_file(self, files, app='', flags='', mode=0): """Execute a file. app is the name of a method in Applications, without the "app_" flags is a string consisting of applications.ALLOWED_FLAGS mode is a positive integer. Both flags and mode specify how the program is run.""" - if type(files) not in (list, tuple): + if type(files) not in (list, tuple, set): files = [files] + arbitrary_file = tuple(files)[0] + return self.apps.get(app)( - mainfile = files[0], + mainfile = arbitrary_file, files = files, flags = flags, mode = mode, @@ -159,7 +164,7 @@ class Actions(EnvironmentAware, SettingsAware): def copy(self): """Copy the selected items""" - selected = set([self.env.cf]) + selected = self.env.get_selection() self.env.copy = set(f for f in selected if f in self.env.pwd.files) self.env.cut = False @@ -205,7 +210,7 @@ class Actions(EnvironmentAware, SettingsAware): def delete(self): msg = self.notify("Deleting ...", duration=0) - selected = set([self.env.cf]) + selected = self.env.get_selection() self.env.copy -= selected if selected: for f in selected: @@ -234,6 +239,42 @@ class Actions(EnvironmentAware, SettingsAware): pass else: return method(text, duration=duration, bad=bad) + + def mark(self, all=False, toggle=False, val=None, movedown=None): + """ + A wrapper for the directory.mark_xyz functions. + If all is True, change the marked-status of all files/directories. + If toggle is True, toggle the marked-status. + If val is True, mark the file(s). If False, unmark them. + If movedown is True, move the pointer down finally. + """ + + if self.env.pwd is None: + return + + pwd = self.env.pwd + + if movedown is None: + movedown = not all + + if val is None and toggle is False: + return + + if all: + if toggle: + pwd.toggle_all_marks() + else: + pwd.mark_all(val) + else: + item = self.env.cf + if item is not None: + if toggle: + pwd.toggle_mark(item) + else: + pwd.mark_item(item, val) + + if movedown: + self.move_pointer(relative=1) # aliases: cd = enter_dir diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py index 4f7e5498..13a20db9 100644 --- a/ranger/colorschemes/default.py +++ b/ranger/colorschemes/default.py @@ -40,8 +40,12 @@ class Default(ColorScheme): if context.link: fg = context.good and cyan or magenta - if context.maindisplay and context.selected: - attr |= bold + if context.maindisplay: + if context.selected: + attr |= bold + if context.marked: + attr |= bold + fg = yellow elif context.in_titlebar: attr |= bold @@ -61,6 +65,9 @@ class Default(ColorScheme): fg = cyan elif context.bad: fg = magenta + if context.marked: + attr |= bold | reverse + fg = yellow elif context.in_notify: attr |= reverse diff --git a/ranger/container/environment.py b/ranger/container/environment.py index 1b26d48e..b08b5320 100644 --- a/ranger/container/environment.py +++ b/ranger/container/environment.py @@ -12,7 +12,6 @@ class Environment(SettingsAware): cf = None # current file copy = None cut = None - selection = None termsize = None history = None directories = None @@ -26,7 +25,6 @@ class Environment(SettingsAware): self.pathway = () self.directories = {} self.keybuffer = KeyBuffer() - self.selection = set() self.copy = set() self.history = History(self.settings.max_history_size) @@ -69,14 +67,20 @@ class Environment(SettingsAware): if value.is_older_than(1200): del self.directories[key] + def get_selection(self): + if self.pwd: + return self.pwd.get_selection() + return set() + def get_directory(self, path): """Get the directory object at the given path""" path = abspath(path) try: return self.directories[path] except KeyError: - self.directories[path] = Directory(path) - return self.directories[path] + obj = Directory(path) + self.directories[path] = obj + return obj def assign_correct_cursor_positions(self): """Assign correct cursor positions for subdirectories""" diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py index e3be795d..2fa02285 100644 --- a/ranger/defaults/keys.py +++ b/ranger/defaults/keys.py @@ -34,6 +34,9 @@ def initialize_commands(command_list): bind('E', do('edit_file')) bind('o', do('force_load_preview')) + bind(' ', do('mark', toggle=True)) + bind('v', do('mark', all=True, toggle=True)) + bind('V', do('mark', all=True, val=False)) bind('yy', 'cp', do('copy')) bind('cut', do('cut')) diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index 7fa130ec..6ca25475 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -24,6 +24,7 @@ class Directory(SuperClass, SettingsAware): filenames = None files = None filter = None + marked_items = None pointed_index = None pointed_file = None scroll_begin = 0 @@ -40,10 +41,59 @@ class Directory(SuperClass, SettingsAware): SuperClass.__init__(self, path) + self.marked_items = set() + # to find out if something has changed: self.old_show_hidden = self.settings.show_hidden self.old_directories_first = self.settings.directories_first - self.load_progress = None + + def mark_item(self, item, val): + item._mark(bool(val)) + if val: + if item in self.files: + self.marked_items.add(item) + else: + if item in self.marked_items: + self.marked_items.remove(item) + + def toggle_mark(self, item): + if item.marked: + return self.unmark_item(item) + return self.mark_item(item) + + def toggle_all_marks(self): + for item in self.files: + self.toggle_mark(item) + + def mark_all(self, val): + if val: + for item in self.files: + self.mark_item(item) + else: + for item in self.files: + self.unmark_item(item) + self.marked_items.clear() + self._clear_marked_items() + + def _gc_marked_items(self): + for item in self.marked_items.copy(): + if item.path not in self.filenames: + self.marked_items.remove(item) + + def _clear_marked_items(self): + for item in self.marked_items: + item._mark(False) + self.marked_items.clear() + + def get_selection(self): + """READ ONLY""" + self._gc_marked_items() + if self.marked_items: + return set(self.marked_items) + elif self.pointed_file: + return set([self.pointed_file]) + else: + return set() def load_bit_by_bit(self): """Loads the contents of the directory. Use this sparingly since @@ -71,17 +121,28 @@ class Directory(SuperClass, SettingsAware): self.infostring = ' %d' % len(self.filenames) # update the infostring yield + marked_paths = set(map( \ + lambda obj: obj.path, self.marked_items)) + self._clear_marked_items() + files = [] for name in self.filenames: if isdir(name): - f = Directory(name) + item = Directory(name) else: - f = File(name) - f.load() - files.append(f) + item = File(name) + item.load() + files.append(item) yield self.files = files + + for item in self.files: + if item.path in marked_paths: + self.mark_item(item) + else: + self.unmark_item(item) + self.old_directories_first = None if len(self.files) > 0: @@ -226,6 +287,9 @@ class Directory(SuperClass, SettingsAware): self.pointed_index = i self.pointed_file = self[i] + + if self == self.fm.env.pwd: + self.fm.env.cf = self.pointed_file def load_content_once(self, *a, **k): """Load the contents of the directory if not done yet""" @@ -279,3 +343,6 @@ class Directory(SuperClass, SettingsAware): def __neq__(self, other): """Check for inequality of the directories paths""" return not self.__eq__(other) + + def __hash__(self): + return hash(self.path) diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index 7899e5f5..d968d65f 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -16,7 +16,6 @@ class FileSystemObject(MimeTypeAware, FileManagerAware): accessible = False marked = False tagged = False - frozen = False loaded = False runnable = False islink = False @@ -92,6 +91,14 @@ class FileSystemObject(MimeTypeAware, FileManagerAware): if self.mimetype == '': self.mimetype = None + + def mark(self, boolean): + directory = self.env.get_directory(self.dirname) + directory.mark_item(self) + + def _mark(self, boolean): + """Called by directory.mark_item() and similar functions""" + self.marked = boolean def load(self): """reads useful information about the filesystem-object from the diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py index d8dbc927..386c0f85 100644 --- a/ranger/gui/colorscheme.py +++ b/ranger/gui/colorscheme.py @@ -8,6 +8,7 @@ CONTEXT_KEYS = [ 'reset', 'error', 'good', 'bad', 'space', 'permissions', 'owner', 'group', 'mtime', 'nlink', 'scroll', 'all', 'bot', 'top', 'percentage', + 'marked', 'keybuffer'] # colorscheme specification: diff --git a/ranger/gui/widgets/filelist.py b/ranger/gui/widgets/filelist.py index 2ece3077..8b4443d3 100644 --- a/ranger/gui/widgets/filelist.py +++ b/ranger/gui/widgets/filelist.py @@ -174,6 +174,9 @@ class FileList(Widget): if i == selected_i: this_color.append('selected') + if drawed.marked: + this_color.append('marked') + if isinstance(drawed, Directory): this_color.append('directory') else: diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index b8b418c9..17a5348b 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -94,28 +94,30 @@ class StatusBar(Widget): def _get_right_part(self): part = [] - if self.filelist is not None: - target = self.filelist.target - else: - target = self.env.at_level(0) + if self.filelist is None: + return part + + target = self.filelist.target +# target = self.env.at_level(0) if not target.content_loaded or not target.accessible: return part - if self.filelist is not None: - pos = target.scroll_begin - max_pos = len(target) - self.filelist.hei - - if max_pos > 0: - if pos == 0: - part.append([['scroll', 'top'], 'Top']) - elif pos >= max_pos: - part.append([['scroll', 'bot'], 'Bot']) - else: - part.append([['scroll', 'percentage'], \ - '{0:0>.0f}%'.format(100.0 * pos / max_pos)]) + pos = target.scroll_begin + max_pos = len(target) - self.filelist.hei + + if target.marked_items: + part.append([['scroll', 'marked'], 'Mrk']) + elif max_pos > 0: + if pos == 0: + part.append([['scroll', 'top'], 'Top']) + elif pos >= max_pos: + part.append([['scroll', 'bot'], 'Bot']) else: - part.append([['scroll', 'all'], 'All']) + part.append([['scroll', 'percentage'], \ + '{0:0>.0f}%'.format(100.0 * pos / max_pos)]) + else: + part.append([['scroll', 'all'], 'All']) return part def _combine_parts(self, left, right): -- cgit 1.4.1-2-gfad0