From b2b58e7f15e5700a1f8248fb1bf32386b4b45c57 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 22 Oct 2011 04:23:36 +0200 Subject: New key "dc" to get cumulative size of selected directories --- ranger/core/actions.py | 5 +++++ ranger/defaults/rc.conf | 2 ++ ranger/fsobject/directory.py | 38 ++++++++++++++++++++++++++++++++++++-- ranger/fsobject/fsobject.py | 3 +++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 0fd60b9a..5966789a 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -87,6 +87,11 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.notify("Aborting: " + item.get_description()) self.loader.remove(index=0) + def get_cumulative_size(self): + for f in self.env.get_selection() or (): + f.look_up_cumulative_size() + self.ui.redraw_main_column() + def redraw_window(self): """Redraw the window""" self.ui.redraw_window() diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf index a9e64622..b75c188e 100644 --- a/ranger/defaults/rc.conf +++ b/ranger/defaults/rc.conf @@ -216,6 +216,8 @@ map oC chain set sort=ctime; set sort_reverse=True map oA chain set sort=atime; set sort_reverse=True map oT chain set sort="type"; set sort_reverse=True +map dc get_cumulative_size + # Settings map zc toggle_option collapse_preview map zd toggle_option sort_directories_first diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index b3a35a0a..04ed35e0 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -25,6 +25,7 @@ from ranger.fsobject import File, FileSystemObject from ranger.core.shared import SettingsAware from ranger.ext.accumulator import Accumulator from ranger.ext.lazy_property import lazy_property +from ranger.ext.human_readable import human_readable def sort_by_basename(path): """returns path.basename (for sorting)""" @@ -79,6 +80,8 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): content_outdated = False content_loaded = False + _cumulative_size_calculated = False + sort_dict = { 'basename': sort_by_basename, 'natural': sort_naturally, @@ -185,8 +188,14 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): hidden_filter = not self.settings.show_hidden \ and self.settings.hidden_filter filelist = os.listdir(mypath) - self.size = len(filelist) - self.infostring = ' %d' % self.size + if not self._cumulative_size_calculated \ + or self.content_loaded: + self.size = len(filelist) + self.infostring = ' %d' % self.size + if self._cumulative_size_calculated: + self._cumulative_size_calculated = False + if self.is_link: + self.infostring = '->' + self.infostring filenames = [mypath + (mypath == '/' and fname or '/' + fname)\ for fname in filelist if accept_file( fname, mypath, hidden_filter, self.filter)] @@ -327,6 +336,31 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): else: self.correct_pointer() + def _get_cumulative_size(self): + if self.size == 0: + return 0 + cum = 0 + for dirpath, dirnames, filenames in os.walk(self.path, + onerror=lambda _: None): + for file in filenames: + try: + if dirpath == self.path: + stat = os.stat(os.path.realpath(dirpath + "/" + file)) + else: + stat = os.stat(dirpath + "/" + file) + cum += stat.st_size + except: + pass + return cum + + def look_up_cumulative_size(self): + if not self._cumulative_size_calculated: + self._cumulative_size_calculated = True + cum = self._get_cumulative_size() + self.size = cum + self.infostring = ('-> ' if self.is_link else ' ') + \ + human_readable(cum) + @lazy_property def size(self): try: diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index d74b21c1..51e49808 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -117,6 +117,9 @@ class FileSystemObject(FileManagerAware): def use(self): """Used in garbage-collecting. Override in Directory""" + def look_up_cumulative_size(self): + pass # normal files have no cumulative size + def set_mimetype(self): """assign attributes such as self.video according to the mimetype""" basename = self.basename -- cgit 1.4.1-2-gfad0 From 63c7a6803f9d3f80f559c891bb131caad0271588 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 22 Oct 2011 04:41:31 +0200 Subject: fsobject.directory: Add a "?" when unsure about the size --- ranger/fsobject/directory.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index 04ed35e0..fd4679ce 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -81,6 +81,7 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): content_loaded = False _cumulative_size_calculated = False + _cumulative_size_needs_update = False sort_dict = { 'basename': sort_by_basename, @@ -188,14 +189,19 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): hidden_filter = not self.settings.show_hidden \ and self.settings.hidden_filter filelist = os.listdir(mypath) - if not self._cumulative_size_calculated \ - or self.content_loaded: + + if self._cumulative_size_calculated: + if self.content_loaded: + self._cumulative_size_needs_update = True + self.infostring = '%s' % (human_readable( + self.size, seperator=\ + ('? ' if self._cumulative_size_needs_update else ' '))) + else: self.size = len(filelist) self.infostring = ' %d' % self.size - if self._cumulative_size_calculated: - self._cumulative_size_calculated = False if self.is_link: self.infostring = '->' + self.infostring + filenames = [mypath + (mypath == '/' and fname or '/' + fname)\ for fname in filelist if accept_file( fname, mypath, hidden_filter, self.filter)] @@ -354,12 +360,13 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): return cum def look_up_cumulative_size(self): - if not self._cumulative_size_calculated: + if self._cumulative_size_needs_update or \ + not self._cumulative_size_calculated: self._cumulative_size_calculated = True - cum = self._get_cumulative_size() - self.size = cum + self._cumulative_size_needs_update = False + self.size = self._get_cumulative_size() self.infostring = ('-> ' if self.is_link else ' ') + \ - human_readable(cum) + human_readable(self.size) @lazy_property def size(self): -- cgit 1.4.1-2-gfad0 From be642580391f41ebdf0811766fee69405b932a42 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 22 Oct 2011 04:42:45 +0200 Subject: fsobject.directory: Improved cumulative_size stuff --- ranger/fsobject/directory.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index fd4679ce..e307e20a 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -81,7 +81,6 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): content_loaded = False _cumulative_size_calculated = False - _cumulative_size_needs_update = False sort_dict = { 'basename': sort_by_basename, @@ -191,11 +190,11 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): filelist = os.listdir(mypath) if self._cumulative_size_calculated: - if self.content_loaded: - self._cumulative_size_needs_update = True - self.infostring = '%s' % (human_readable( - self.size, seperator=\ - ('? ' if self._cumulative_size_needs_update else ' '))) + # If self.content_loaded is true, this is not the first + # time loading. So I can't really be sure if the + # size has changed and I'll add a "?". + self.infostring = ' %s' % (human_readable(self.size, + seperator=('? ' if self.content_loaded else ' '))) else: self.size = len(filelist) self.infostring = ' %d' % self.size @@ -346,27 +345,25 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): if self.size == 0: return 0 cum = 0 + realpath = os.path.realpath for dirpath, dirnames, filenames in os.walk(self.path, onerror=lambda _: None): for file in filenames: try: if dirpath == self.path: - stat = os.stat(os.path.realpath(dirpath + "/" + file)) + stat = os_stat(realpath(dirpath + "/" + file)) else: - stat = os.stat(dirpath + "/" + file) + stat = os_stat(dirpath + "/" + file) cum += stat.st_size except: pass return cum def look_up_cumulative_size(self): - if self._cumulative_size_needs_update or \ - not self._cumulative_size_calculated: - self._cumulative_size_calculated = True - self._cumulative_size_needs_update = False - self.size = self._get_cumulative_size() - self.infostring = ('-> ' if self.is_link else ' ') + \ - human_readable(self.size) + self._cumulative_size_calculated = True + self.size = self._get_cumulative_size() + self.infostring = ('-> ' if self.is_link else ' ') + \ + human_readable(self.size) @lazy_property def size(self): -- cgit 1.4.1-2-gfad0 From 2970f6158daa8783302ce7005fe5b30d3b93d39e Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 22 Oct 2011 04:53:58 +0200 Subject: ext.human_readable: Fixed spelling error (seperator->separator) --- ranger/ext/human_readable.py | 24 ++++++++++++------------ ranger/fsobject/directory.py | 2 +- ranger/gui/widgets/statusbar.py | 8 ++++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ranger/ext/human_readable.py b/ranger/ext/human_readable.py index 9cdce409..c5bd2aac 100644 --- a/ranger/ext/human_readable.py +++ b/ranger/ext/human_readable.py @@ -13,7 +13,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -def human_readable(byte, seperator=' '): +def human_readable(byte, separator=' '): """ Convert a large number of bytes to an easily readable format. @@ -27,27 +27,27 @@ def human_readable(byte, seperator=' '): if byte <= 0: return '0' if byte < 2**10: - return '%d%sB' % (byte, seperator) + return '%d%sB' % (byte, separator) if byte < 2**10 * 999: - return '%.3g%sK' % (byte / 2**10.0, seperator) + return '%.3g%sK' % (byte / 2**10.0, separator) if byte < 2**20: - return '%.4g%sK' % (byte / 2**10.0, seperator) + return '%.4g%sK' % (byte / 2**10.0, separator) if byte < 2**20 * 999: - return '%.3g%sM' % (byte / 2**20.0, seperator) + return '%.3g%sM' % (byte / 2**20.0, separator) if byte < 2**30: - return '%.4g%sM' % (byte / 2**20.0, seperator) + return '%.4g%sM' % (byte / 2**20.0, separator) if byte < 2**30 * 999: - return '%.3g%sG' % (byte / 2**30.0, seperator) + return '%.3g%sG' % (byte / 2**30.0, separator) if byte < 2**40: - return '%.4g%sG' % (byte / 2**30.0, seperator) + return '%.4g%sG' % (byte / 2**30.0, separator) if byte < 2**40 * 999: - return '%.3g%sT' % (byte / 2**40.0, seperator) + return '%.3g%sT' % (byte / 2**40.0, separator) if byte < 2**50: - return '%.4g%sT' % (byte / 2**40.0, seperator) + return '%.4g%sT' % (byte / 2**40.0, separator) if byte < 2**50 * 999: - return '%.3g%sP' % (byte / 2**50.0, seperator) + return '%.3g%sP' % (byte / 2**50.0, separator) if byte < 2**60: - return '%.4g%sP' % (byte / 2**50.0, seperator) + return '%.4g%sP' % (byte / 2**50.0, separator) return '>9000' if __name__ == '__main__': diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index e307e20a..ce6be623 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -194,7 +194,7 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): # time loading. So I can't really be sure if the # size has changed and I'll add a "?". self.infostring = ' %s' % (human_readable(self.size, - seperator=('? ' if self.content_loaded else ' '))) + separator=('? ' if self.content_loaded else ' '))) else: self.size = len(filelist) self.infostring = ' %d' % self.size diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index 1e2e2520..b7a238da 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -231,17 +231,17 @@ class StatusBar(Widget): if target.marked_items: if len(target.marked_items) == len(target.files): - right.add(human_readable(target.disk_usage, seperator='')) + right.add(human_readable(target.disk_usage, separator='')) else: right.add(human_readable(sum(f.size \ for f in target.marked_items \ - if f.is_file), seperator='')) + if f.is_file), separator='')) right.add("/" + str(len(target.marked_items))) else: - right.add(human_readable(target.disk_usage, seperator='') + + right.add(human_readable(target.disk_usage, separator='') + " sum, ") right.add(human_readable(self.env.get_free_space( \ - target.mount_path), seperator='') + " free") + target.mount_path), separator='') + " free") right.add(" ", "space") if target.marked_items: -- cgit 1.4.1-2-gfad0 From 3c0fd67fd5581bb1aaa84c957b568abdb5dc1a54 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 22 Oct 2011 05:04:29 +0200 Subject: Added option "autoupdate_cumulative_size" --- ranger/container/settingobject.py | 1 + ranger/defaults/options.py | 6 ++++++ ranger/defaults/rc.conf | 1 + ranger/fsobject/directory.py | 10 ++++++++-- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ranger/container/settingobject.py b/ranger/container/settingobject.py index 5c24d663..51c12c6e 100644 --- a/ranger/container/settingobject.py +++ b/ranger/container/settingobject.py @@ -19,6 +19,7 @@ from ranger.core.shared import FileManagerAware ALLOWED_SETTINGS = { 'autosave_bookmarks': bool, + 'autoupdate_cumulative_size': bool, 'collapse_preview': bool, 'colorscheme_overlay': (type(None), type(lambda:0)), 'colorscheme': str, diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 3b44d4f6..92b9ada9 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -104,6 +104,12 @@ padding_right = True # When false, bookmarks are saved when ranger is exited. autosave_bookmarks = True +# You can display the "real" cumulative size of directories by using the +# command :get_cumulative_size or typing "dc". The size is expensive to +# calculate and will not be updated automatically. You can choose +# to update it automatically though by turning on this option: +autoupdate_cumulative_size = False + # Makes sense for screen readers: show_cursor = False diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf index b75c188e..b8106663 100644 --- a/ranger/defaults/rc.conf +++ b/ranger/defaults/rc.conf @@ -228,6 +228,7 @@ map zm toggle_option mouse_enabled map zp toggle_option preview_files map zP toggle_option preview_directories map zs toggle_option sort_case_insensitive +map zu toggle_option autoupdate_cumulative_size map zv toggle_option use_preview_script map zf console filter diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index ce6be623..bc09b080 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -193,8 +193,14 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): # If self.content_loaded is true, this is not the first # time loading. So I can't really be sure if the # size has changed and I'll add a "?". - self.infostring = ' %s' % (human_readable(self.size, - separator=('? ' if self.content_loaded else ' '))) + if self.content_loaded: + if self.fm.settings.autoupdate_cumulative_size: + self.look_up_cumulative_size() + else: + self.infostring = ' %s' % human_readable( + self.size, separator='? ') + else: + self.infostring = ' %s' % human_readable(self.size) else: self.size = len(filelist) self.infostring = ' %d' % self.size -- cgit 1.4.1-2-gfad0 From 97e724a3f590210a819414de3c21278d2f3c03dc Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 23 Oct 2011 19:55:42 +0200 Subject: gui.curses_shortcuts: Fix unicode bug with surrogates --- ranger/gui/curses_shortcuts.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py index a977beda..4a3bb4b9 100644 --- a/ranger/gui/curses_shortcuts.py +++ b/ranger/gui/curses_shortcuts.py @@ -21,6 +21,10 @@ from ranger.ext.iter_tools import flatten from ranger.gui.color import get_color from ranger.core.shared import SettingsAware +def _fix_surrogates(args): + return [isinstance(arg, str) and arg.encode('utf-8', 'surrogateescape') + .decode('utf-8', 'replace') or arg for arg in args] + class CursesShortcuts(SettingsAware): """ This class defines shortcuts to faciliate operations with curses. @@ -35,13 +39,19 @@ class CursesShortcuts(SettingsAware): try: self.win.addstr(*args) except: - pass + try: + self.win.addstr(*_fix_surrogates(args)) + except: + pass def addnstr(self, *args): try: self.win.addnstr(*args) except: - pass + try: + self.win.addnstr(*_fix_surrogates(args)) + except: + pass def addch(self, *args): try: -- cgit 1.4.1-2-gfad0 From 6d212a856e9efbfafd5b1540bcb2852199c1787f Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 23 Oct 2011 22:58:38 +0200 Subject: defaults/apps.py: added new handlers --- ranger/defaults/apps.py | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 3ec6bff2..a53e2c8b 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -97,23 +97,35 @@ class CustomApplications(Applications): if f.extension is not None: if f.extension in ('pdf', ): - return self.either(c, 'evince', 'zathura', 'apvlv') + return self.either(c, 'evince', 'zathura', 'apvlv', 'okular') if f.extension == 'djvu': return self.either(c, 'evince') - if f.extension in ('xml', ): + if f.extension in ('xml', 'csv'): return self.either(c, 'editor') + if f.extension in ('html', 'htm', 'xhtml') or f.extension == 'swf': + c.flags += 'd' + handler = self.either(c, + 'luakit', 'uzbl', 'vimprobable', 'vimprobable2', 'jumanji', + 'firefox', 'seamonkey', 'iceweasel', 'opera', + 'surf', 'midori', 'epiphany', 'konqueror') + # Only return if some program was found: + if handler: + return handler if f.extension in ('html', 'htm', 'xhtml'): - return self.either(c, 'firefox', 'opera', 'jumanji', - 'luakit', 'elinks', 'lynx') - if f.extension == 'swf': - return self.either(c, 'firefox', 'opera', 'jumanji', 'luakit') + # These browsers can't handle flash, so they're not called above. + c.flags += 'D' + return self.either(c, 'elinks', 'links', 'links2', 'lynx', 'w3m') if f.extension == 'nes': return self.either(c, 'fceux') if f.extension in ('swc', 'smc', 'sfc'): return self.either(c, 'zsnes') - if f.extension in ('odt', 'ods', 'odp', 'odf', 'odg', - 'doc', 'xls'): - return self.either(c, 'libreoffice', 'soffice', 'ooffice') + if f.extension == 'doc': + return self.either(c, 'abiword', 'libreoffice', + 'soffice', 'ooffice') + if f.extension in ('odt', 'ods', 'odp', 'odf', 'odg', 'sxc', + 'stc', 'xls', 'xlsx', 'xlt', 'xlw', 'gnm', 'gnumeric'): + return self.either(c, 'gnumeric', 'kspread', + 'libreoffice', 'soffice', 'ooffice') if f.mimetype is not None: if INTERPRETED_LANGUAGES.match(f.mimetype): @@ -125,8 +137,8 @@ class CustomApplications(Applications): if f.video or f.audio: if f.video: c.flags += 'd' - return self.either(c, 'mplayer2', 'mplayer', 'smplayer', 'vlc', - 'totem') + return self.either(c, 'smplayer', 'gmplayer', 'mplayer2', + 'mplayer', 'vlc', 'totem') if f.image: if c.mode in (11, 12, 13, 14): @@ -281,8 +293,14 @@ class CustomApplications(Applications): CustomApplications.generic('fceux', 'wine', 'zsnes', deps=['X']) # Add those which should only run in X AND should be detached/forked here: -CustomApplications.generic('opera', 'firefox', 'apvlv', 'evince', - 'zathura', 'gimp', 'mirage', 'eog', 'jumanji', +CustomApplications.generic( + 'luakit', 'uzbl', 'vimprobable', 'vimprobable2', 'jumanji', + 'firefox', 'seamonkey', 'iceweasel', 'opera', + 'surf', 'midori', 'epiphany', 'konqueror', + 'evince', 'zathura', 'apvlv', 'okular', + 'eog', 'mirage', 'gimp', + 'libreoffice', 'soffice', 'ooffice', 'gnumeric', 'kspread', 'abiword', + 'gmplayer', 'smplayer', 'vlc', flags='d', deps=['X']) # What filetypes are recognized as scripts for interpreted languages? -- cgit 1.4.1-2-gfad0 From 25c9aa011cedfd58a5b18bc45698b192d9411ea4 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 23 Oct 2011 23:11:40 +0200 Subject: Added constant ranger.STABLE to indicate stable versions --- ranger/__init__.py | 1 + ranger/core/helper.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ranger/__init__.py b/ranger/__init__.py index f321ee4f..ae467b30 100644 --- a/ranger/__init__.py +++ b/ranger/__init__.py @@ -36,6 +36,7 @@ TIME_BEFORE_FILE_BECOMES_GARBAGE = 1200 MACRO_DELIMITER = '%' LOGFILE = '/tmp/ranger_errorlog' USAGE = '%prog [options] [path/filename]' +STABLE = False # If the environment variable XDG_CONFIG_HOME is non-empty, CONFDIR is ignored # and the configuration directory will be $XDG_CONFIG_HOME/ranger instead. diff --git a/ranger/core/helper.py b/ranger/core/helper.py index 46d35f7a..c22a52b8 100644 --- a/ranger/core/helper.py +++ b/ranger/core/helper.py @@ -32,7 +32,8 @@ def parse_arguments(): else: default_confdir = CONFDIR - parser = OptionParser(usage=USAGE, version='ranger '+__version__) + parser = OptionParser(usage=USAGE, version='ranger %s%s' \ + % (__version__, " (stable)" if STABLE else "")) parser.add_option('-d', '--debug', action='store_true', help="activate debug mode") -- cgit 1.4.1-2-gfad0 From 2f7e770983ba9219b892f3d739b55e0bc724205c Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 24 Oct 2011 00:37:14 +0200 Subject: core.actions: Added comment about possible bug --- ranger/core/actions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 5966789a..5364f391 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -691,6 +691,9 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): # self.previews['/tmp/foo.jpg']['loading'] = False # A -1 in tuples means "any"; (80, -1) = wid. of 80 and any hei. # The key 'foundpreview' is added later. Values in (True, False) + # XXX: Previews can break when collapse_preview is on and the + # preview column is popping out as you move the cursor on e.g. a + # PDF file. try: data = self.previews[path] except: -- cgit 1.4.1-2-gfad0 From 8a2d5352480629aeb7a983ea4ddadf7005223907 Mon Sep 17 00:00:00 2001 From: David Pugnasse Date: Mon, 24 Oct 2011 00:58:33 +0200 Subject: core.main: added --selectfile option --- doc/ranger.1 | 3 +++ doc/ranger.pod | 4 ++++ ranger/core/actions.py | 8 ++++++-- ranger/core/helper.py | 2 ++ ranger/core/main.py | 6 ++++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/doc/ranger.1 b/doc/ranger.1 index e9f00501..c1a5b381 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -178,6 +178,9 @@ selected files into \fItargetfile\fR, adding one newline after each filename. .IX Item "--choosedir=targetfile" Allows you to pick a directory with ranger. When you exit ranger, it will write the last visited directory into \fItargetfile\fR. +.IP "\fB\-\-selectfile\fR=\fItargetfile\fR" 14 +.IX Item "--selectfile=targetfile" +Open ranger with \fItargetfile\fR selected. .IP "\fB\-\-copy\-config\fR=\fIfile\fR" 14 .IX Item "--copy-config=file" Create copies of the default configuration files in your local configuration diff --git a/doc/ranger.pod b/doc/ranger.pod index 069b9de1..8943b476 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -67,6 +67,10 @@ selected files into I, adding one newline after each filename. Allows you to pick a directory with ranger. When you exit ranger, it will write the last visited directory into I. +=item B<--selectfile>=I + +Open ranger with I selected. + =item B<--copy-config>=I Create copies of the default configuration files in your local configuration diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 5364f391..3300a534 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -344,8 +344,12 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def select_file(self, path): path = path.strip() - if self.enter_dir(os.path.dirname(path)): - self.env.cwd.move_to_obj(path) + dirname = os.path.dirname(path) + if dirname: + if self.enter_dir(dirname): + self.env.cwd.move_to_obj(path) + else: + self.env.cwd.move_to_obj(join(self.env.cwd.path, path)) def history_go(self, relative): """Move back and forth in the history""" diff --git a/ranger/core/helper.py b/ranger/core/helper.py index c22a52b8..88072e13 100644 --- a/ranger/core/helper.py +++ b/ranger/core/helper.py @@ -66,6 +66,8 @@ def parse_arguments(): ", it will write the name of the last visited directory to TARGET") parser.add_option('--list-unused-keys', action='store_true', help="List common keys which are not bound to any action.") + parser.add_option('--selectfile', type='string', metavar='filepath', + help="Open ranger with supplied file selected.") options, positional = parser.parse_args() arg = OpenStruct(options.__dict__, targets=positional) diff --git a/ranger/core/main.py b/ranger/core/main.py index c87a4660..49513125 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -49,6 +49,9 @@ def main(): SettingsAware._setup(clean=arg.clean) + if arg.selectfile: + arg.targets.insert(0, os.path.dirname(arg.selectfile)) + targets = arg.targets or ['.'] target = targets[0] if arg.targets: @@ -97,6 +100,9 @@ def main(): from ranger.ext import curses_interrupt_handler curses_interrupt_handler.install_interrupt_handler() + if arg.selectfile: + fm.select_file(arg.selectfile) + # Run the file manager fm.initialize() fm.ui.initialize() -- cgit 1.4.1-2-gfad0 From 9ab9f65217df8dd75e171702bee2943ca295e956 Mon Sep 17 00:00:00 2001 From: David Pugnasse Date: Tue, 25 Oct 2011 02:15:53 +0200 Subject: core.main: fix --selectfile with relative paths --- ranger/core/actions.py | 8 ++------ ranger/core/main.py | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 3300a534..5364f391 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -344,12 +344,8 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def select_file(self, path): path = path.strip() - dirname = os.path.dirname(path) - if dirname: - if self.enter_dir(dirname): - self.env.cwd.move_to_obj(path) - else: - self.env.cwd.move_to_obj(join(self.env.cwd.path, path)) + if self.enter_dir(os.path.dirname(path)): + self.env.cwd.move_to_obj(path) def history_go(self, relative): """Move back and forth in the history""" diff --git a/ranger/core/main.py b/ranger/core/main.py index 49513125..14e4b1f6 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -50,6 +50,7 @@ def main(): SettingsAware._setup(clean=arg.clean) if arg.selectfile: + arg.selectfile = os.path.abspath(arg.selectfile) arg.targets.insert(0, os.path.dirname(arg.selectfile)) targets = arg.targets or ['.'] -- cgit 1.4.1-2-gfad0 From 4b61759c89d22daa35ab6b6f3c7f48af34af6241 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 27 Oct 2011 23:43:43 +0200 Subject: Makefile: Fixed "PHONY" stuff --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8adae6a0..3e5e7cc1 100644 --- a/Makefile +++ b/Makefile @@ -90,4 +90,4 @@ snapshot: todo: @grep --color -Ion '\(TODO\|XXX\).*' -r ranger -.PHONY: default options compile clean doc cleandoc snapshot install man todo +.PHONY: clean cleandoc compile default doc help install man manhtml options snapshot test todo -- cgit 1.4.1-2-gfad0 From dd06c0e65ecc829281d69206ac7827b83d0da97a Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 27 Oct 2011 23:46:24 +0200 Subject: Fixed tab completion of "open_with" command --- ranger/defaults/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index d564d4f6..6cfeff17 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -307,12 +307,12 @@ class open_with(Command): return app, flags, int(mode) - def _get_tab(self): + def tab(self): data = self.rest(1) if ' ' not in data: all_apps = self.fm.apps.all() if all_apps: - return (app for app in all_apps if app.startswith(data)) + return (self.firstpart + app for app in all_apps if app.startswith(data)) return None -- cgit 1.4.1-2-gfad0 From 6304301bc16b00dfcedfafb762762f47b9df8bd2 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 28 Oct 2011 14:58:53 +0200 Subject: Makefile: Added info about DESTDIR in "make options" --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 3e5e7cc1..f13de38d 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ options: help @echo 'PYTHON = $(PYTHON)' @echo 'PYOPTIMIZE = $(PYOPTIMIZE)' @echo 'DOCDIR = $(DOCDIR)' + @echo 'DESTDIR = $(DESTDIR)' help: @echo 'make: Test and compile ranger.' -- cgit 1.4.1-2-gfad0 From 0439e68883ae73b713761243a2611f3615ac3b5d Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 29 Oct 2011 21:19:17 +0200 Subject: api.apps: Fix handling of single-string return values --- ranger/api/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/api/apps.py b/ranger/api/apps.py index 1af3167b..56f8afd2 100644 --- a/ranger/api/apps.py +++ b/ranger/api/apps.py @@ -109,7 +109,7 @@ class Applications(FileManagerAware): arguments = handler(context) # flatten if isinstance(arguments, str): - return (arguments, ) + return arguments if arguments is None: return None result = [] -- cgit 1.4.1-2-gfad0 From fdd390452b1937db7ba3ba7f30d0a6ece44ca02f Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 30 Oct 2011 01:20:39 +0200 Subject: core.runner: added "fm" attribute to Context, as documented --- ranger/core/fm.py | 2 +- ranger/core/runner.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 59eb4e18..7ba07484 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -85,7 +85,7 @@ class FM(Actions, SignalDispatcher): def mylogfunc(text): self.notify(text, bad=True) self.run = Runner(ui=self.ui, apps=self.apps, - logfunc=mylogfunc) + logfunc=mylogfunc, fm=self) self.env.signal_bind('cd', self._update_current_tab) diff --git a/ranger/core/runner.py b/ranger/core/runner.py index 940f410e..8adf01d7 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -94,8 +94,9 @@ class Context(object): class Runner(object): - def __init__(self, ui=None, logfunc=None, apps=None): + def __init__(self, ui=None, logfunc=None, apps=None, fm=None): self.ui = ui + self.fm = fm self.logfunc = logfunc self.apps = apps self.zombies = set() @@ -132,7 +133,7 @@ class Runner(object): # creating a Context object and passing it to # an Application object. - context = Context(app=app, files=files, mode=mode, + context = Context(app=app, files=files, mode=mode, fm=self.fm, flags=flags, wait=wait, popen_kws=popen_kws, file=files and files[0] or None) -- cgit 1.4.1-2-gfad0 From 353d765c2a14ac23ccea337f4cac2bc73b2b0693 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 30 Oct 2011 01:21:40 +0200 Subject: Added signals runner.execute.{before,after} --- ranger/core/runner.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ranger/core/runner.py b/ranger/core/runner.py index 8adf01d7..5d97489d 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -195,10 +195,14 @@ class Runner(object): if toggle_ui: self._activate_ui(False) try: + error = None process = None + self.fm.signal_emit('runner.execute.before', + popen_kws=popen_kws, context=context) try: process = Popen(**popen_kws) except Exception as e: + error = e self._log("Failed to run: %s\n%s" % (str(action), str(e))) else: if context.wait: @@ -208,6 +212,8 @@ class Runner(object): if wait_for_enter: press_enter() finally: + self.fm.signal_emit('runner.execute.after', + popen_kws=popen_kws, context=context, error=error) if devnull: devnull.close() if toggle_ui: -- cgit 1.4.1-2-gfad0 From 6b3a30d788bdb186a66a05e6e910c37d66164ace Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 30 Oct 2011 01:33:17 +0200 Subject: Added option "init_function" --- ranger/container/settingobject.py | 1 + ranger/core/fm.py | 3 +++ ranger/defaults/options.py | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/ranger/container/settingobject.py b/ranger/container/settingobject.py index 51c12c6e..e7ded15e 100644 --- a/ranger/container/settingobject.py +++ b/ranger/container/settingobject.py @@ -32,6 +32,7 @@ ALLOWED_SETTINGS = { 'draw_borders': bool, 'flushinput': bool, 'hidden_filter': lambda x: isinstance(x, str) or hasattr(x, 'match'), + 'init_function': (type(None), type(lambda:0)), 'load_default_rc': (bool, type(None)), 'max_console_history_size': (int, type(None)), 'max_history_size': (int, type(None)), diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 7ba07484..9b5cc036 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -89,6 +89,9 @@ class FM(Actions, SignalDispatcher): self.env.signal_bind('cd', self._update_current_tab) + if self.settings.init_function: + self.settings.init_function(self) + def destroy(self): debug = ranger.arg.debug if self.ui: diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 92b9ada9..548dc0fe 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -123,6 +123,16 @@ sort_directories_first = True # (Especially on xterm) xterm_alt_key = False +# A function that is called when the user interface is being set up. +#init_function = None + +# You can use it to initialize some custom functionality or bind singals +#def init_function(fm): +# fm.notify("Hello :)") +# def on_tab_change(signal): +# signal.origin.notify("Changing tab! Yay!") +# fm.signal_bind("tab.change", on_tab_change) + # The color scheme overlay. Explained below. colorscheme_overlay = None -- cgit 1.4.1-2-gfad0 From 2081cbe1f64017100ccfccd34a75d7731ff77dae Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 30 Oct 2011 01:40:59 +0200 Subject: define "init_function" in defaults --- ranger/defaults/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 548dc0fe..46bca607 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -124,7 +124,7 @@ sort_directories_first = True xterm_alt_key = False # A function that is called when the user interface is being set up. -#init_function = None +init_function = None # You can use it to initialize some custom functionality or bind singals #def init_function(fm): -- cgit 1.4.1-2-gfad0 From ec6093297e8851802ff2184cd2949bb9cfbad1d7 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 30 Oct 2011 02:23:12 +0200 Subject: core.actions: paste_symlink() doesn't fail if file exists --- ranger/core/actions.py | 10 +++++++--- ranger/ext/next_available_filename.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 ranger/ext/next_available_filename.py diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 5364f391..00fd2179 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -28,6 +28,7 @@ from ranger.ext.direction import Direction from ranger.ext.relative_symlink import relative_symlink from ranger.ext.keybinding_parser import key_to_string, construct_keybinding from ranger.ext.shell_escape import shell_quote +from ranger.ext.next_available_filename import next_available_filename from ranger.core.shared import FileManagerAware, EnvironmentAware, \ SettingsAware from ranger.fsobject import File @@ -918,18 +919,21 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def paste_symlink(self, relative=False): copied_files = self.env.copy for f in copied_files: + self.notify(next_available_filename(f.basename)) try: + new_name = next_available_filename(f.basename) if relative: - relative_symlink(f.path, join(getcwd(), f.basename)) + relative_symlink(f.path, join(getcwd(), new_name)) else: - symlink(f.path, join(getcwd(), f.basename)) + symlink(f.path, join(getcwd(), new_name)) except Exception as x: self.notify(x) def paste_hardlink(self): for f in self.env.copy: try: - link(f.path, join(getcwd(), f.basename)) + new_name = next_available_filename(f.basename) + link(f.path, join(getcwd(), new_name)) except Exception as x: self.notify(x) diff --git a/ranger/ext/next_available_filename.py b/ranger/ext/next_available_filename.py new file mode 100644 index 00000000..696063cf --- /dev/null +++ b/ranger/ext/next_available_filename.py @@ -0,0 +1,30 @@ +# Copyright (C) 2011 Roman Zimbelmann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import os.path + +def next_available_filename(fname, directory="."): + existing_files = os.listdir(directory) + + if fname not in existing_files: + return fname + if not fname.endswith("_"): + fname += "_" + if fname not in existing_files: + return fname + + for i in range(1, len(existing_files) + 1): + if fname + str(i) not in existing_files: + return fname + str(i) -- cgit 1.4.1-2-gfad0 From 975be325388b00cca3c0aa2bb0a70ef0a57d2f93 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 30 Oct 2011 02:32:55 +0200 Subject: api.commands: added a TODO entry --- ranger/api/commands.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ranger/api/commands.py b/ranger/api/commands.py index ae3bdc94..ea0df16b 100644 --- a/ranger/api/commands.py +++ b/ranger/api/commands.py @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +# TODO: Add an optional "!" to all commands and set a flag if it's there + import os import ranger import re -- cgit 1.4.1-2-gfad0 From 83c8d324492263c9aadc3098e1ebccc8d2dd1d91 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 31 Oct 2011 20:33:40 +0100 Subject: core.actions: implement visual mode --- ranger/core/actions.py | 77 ++++++++++++++++++++++++++++++++++++----- ranger/defaults/rc.conf | 4 ++- ranger/gui/widgets/statusbar.py | 5 ++- 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 00fd2179..77f064f2 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -42,6 +42,11 @@ class _MacroTemplate(string.Template): class Actions(FileManagerAware, EnvironmentAware, SettingsAware): search_method = 'ctime' + mode = 'normal' # either 'normal' or 'visual'. + _visual_reverse = False + _visual_start = None + _visual_start_pos = None + _previous_selection = None # -------------------------- # -- Basic Commands @@ -57,6 +62,32 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.previews = {} self.env.garbage_collect(-1, self.tabs) self.enter_dir(old_path) + self.change_mode('normal') + + def change_mode(self, mode): + if mode == self.mode: + return + if mode == 'visual': + self._visual_start = self.env.cwd.pointed_obj + self._visual_start_pos = self.env.cwd.pointer + self._previous_selection = set(self.env.cwd.marked_items) + self.mark_files(val=not self._visual_reverse, movedown=False) + elif mode == 'normal': + if self.mode == 'visual': + self._visual_start = None + self._visual_start_pos = None + self._previous_selection = None + else: + return + self.mode = mode + self.ui.status.request_redraw() + + def toggle_visual_mode(self, reverse=False): + if self.mode == 'normal': + self._visual_reverse = reverse + self.change_mode('visual') + else: + self.change_mode('normal') def reload_cwd(self): try: @@ -312,6 +343,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): except: return self.env.enter_dir(directory) + self.change_mode('normal') if cwd and cwd.accessible and cwd.content_loaded: if 'right' in direction: mode = 0 @@ -330,8 +362,34 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): current=cwd.pointer, pagesize=self.ui.browser.hei) cwd.move(to=newpos) + if self.mode == 'visual': + try: + startpos = cwd.index(self._visual_start) + except: + self._visual_start = None + startpos = min(self._visual_start_pos, len(cwd)) + # The files between here and _visual_start_pos + targets = set(cwd.files[min(startpos, newpos):\ + max(startpos, newpos) + 1]) + # The selection before activating visual mode + old = self._previous_selection + # The current selection + current = set(cwd.marked_items) + + # Set theory anyone? + if not self._visual_reverse: + for f in targets - current: + cwd.mark_item(f, True) + for f in current - old - targets: + cwd.mark_item(f, False) + else: + for f in targets & current: + cwd.mark_item(f, False) + for f in old - current - targets: + cwd.mark_item(f, True) def move_parent(self, n, narg=None): + self.change_mode('normal') if narg is not None: n *= narg parent = self.env.at_level(-1) @@ -360,18 +418,20 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): 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) + cwd = self.env.cwd + result = self.env.enter_dir(path, history=history) + if cwd != self.env.cwd: + if remember: + self.bookmarks.remember(cwd) + self.change_mode('normal') + return result def cd(self, path, remember=True): """enter the directory at the given path, remember=True""" self.enter_dir(path, remember=remember) def traverse(self): + self.change_mode('normal') cf = self.env.cf cwd = self.env.cwd if cf is not None and cf.is_directory: @@ -768,13 +828,14 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): # directory paths only. def tab_open(self, name, path=None): - do_emit_signal = name != self.current_tab + tab_has_changed = name != self.current_tab self.current_tab = name if path or (name in self.tabs): self.enter_dir(path or self.tabs[name]) else: self._update_current_tab() - if do_emit_signal: + if tab_has_changed: + self.change_mode('normal') self.signal_emit('tab.change') def tab_close(self, name=None): diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf index b8106663..1ecbc398 100644 --- a/ranger/defaults/rc.conf +++ b/ranger/defaults/rc.conf @@ -39,6 +39,7 @@ map R reload_cwd map reset map redraw_window map abort +map change_mode normal map i display_file map ? help @@ -62,8 +63,9 @@ map T tag_remove map " tag_toggle tag=%any map mark_files toggle=True map v mark_files all=True toggle=True -map V mark_files all=True val=False map uv mark_files all=True val=False +map V toggle_visual_mode +map uV toggle_visual_mode reverse=True # For the nostalgics: Midnight Commander bindings map help diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index b7a238da..a7d3fc73 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -159,7 +159,10 @@ class StatusBar(Widget): if stat is None: return - perms = target.get_permission_string() + if self.fm.mode != 'normal': + perms = '--%s--' % self.fm.mode.upper() + else: + perms = target.get_permission_string() how = getuid() == stat.st_uid and 'good' or 'bad' left.add(perms, 'permissions', how) left.add_space() -- cgit 1.4.1-2-gfad0 From 1ea86f6281b2ea4eaf65dc65757977cd06d7beef Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 31 Oct 2011 22:46:05 +0100 Subject: widgets.titlebar: Fix drawing of broken filenames --- ranger/gui/widgets/titlebar.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py index 6b92ccfa..3ca3f80d 100644 --- a/ranger/gui/widgets/titlebar.py +++ b/ranger/gui/widgets/titlebar.py @@ -160,5 +160,6 @@ class TitleBar(Widget): self.win.move(0, 0) for part in result: self.color(*part.lst) - self.addstr(str(part)) + y, x = self.win.getyx() + self.addstr(y, x, str(part)) self.color_reset() -- cgit 1.4.1-2-gfad0 From 7b33b517e5105303b78b05a8970d9718a5b5135c Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 9 Nov 2011 16:01:11 +0100 Subject: corrected README --- README | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README b/README index 84bf30a3..9b0ed735 100644 --- a/README +++ b/README @@ -89,7 +89,7 @@ to open a file or type Q to quit. The third column shows a preview of the current file. The second is the main column and the first shows the parent directory. -Ranger will automatically copy simple configuration files to ~/.config/ranger. -If you mess them up, just delete them and ranger will copy them again. Run -ranger with --dont-copy-config to disable this. Also check ranger/defaults/ -for the default configuration. +Ranger can automatically copy default configuration files to ~/.config/ranger +if you run it with the switch --copy-config. (see ranger --help for a +description of that switch.) Also check ranger/defaults/ for the default +configuration. -- cgit 1.4.1-2-gfad0 From df95694991dd2387f02638df048805985c5b3461 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 9 Nov 2011 16:04:44 +0100 Subject: Disable visual mode when typing "v" --- ranger/core/actions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 77f064f2..d3f74e5c 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -536,6 +536,8 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): cwd.toggle_all_marks() else: cwd.mark_all(val) + if self.mode == 'visual': + self.change_mode('normal') else: for i in range(cwd.pointer, min(cwd.pointer + narg, len(cwd))): item = cwd.files[i] -- cgit 1.4.1-2-gfad0 From be25337b3ab0ab5841346ddc7973e8a3fc57ce7d Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 15 Nov 2011 20:55:44 +0100 Subject: defaults/apps.py: Add epdfview to default apps.py --- ranger/defaults/apps.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index a53e2c8b..fcc264f6 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -97,7 +97,8 @@ class CustomApplications(Applications): if f.extension is not None: if f.extension in ('pdf', ): - return self.either(c, 'evince', 'zathura', 'apvlv', 'okular') + return self.either(c, 'evince', 'zathura', 'apvlv', 'okular', + 'epdfview') if f.extension == 'djvu': return self.either(c, 'evince') if f.extension in ('xml', 'csv'): -- cgit 1.4.1-2-gfad0 From bd7d4f042e387250f3c164a2d7e154a5bf3ff602 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 15 Nov 2011 20:56:34 +0100 Subject: core.actions: Disable visual mode when opening console --- ranger/core/actions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index d3f74e5c..dbfba502 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -129,7 +129,8 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): self.ui.redraw_window() def open_console(self, string='', prompt=None, position=None): - """Open the console if the current UI supports that""" + """Open the console""" + self.change_mode('normal') self.ui.open_console(string, prompt=prompt, position=position) def execute_console(self, string='', wildcards=[], quantifier=None): -- cgit 1.4.1-2-gfad0 From a514859b91df995fb983bbae866653ee7b83e795 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 15 Nov 2011 21:08:49 +0100 Subject: defaults/apps.py: Added instructions to overriding app_defaults --- ranger/defaults/apps.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index fcc264f6..01acd551 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -14,9 +14,17 @@ # in your ~/.config/ranger/apps.py, you should subclass the class defined # here like this: # -# from ranger.defaults.apps import CustomApplications as DefaultApps -# class CustomApplications(DeafultApps): -# +# from ranger.defaults.apps import CustomApplications as DefaultApps +# class CustomApplications(DeafultApps): +# +# +# To override app_defaults, you can write something like: +# +# def app_defaults(self, c): +# f = c.file +# if f.extension == 'lol': +# return "lolopener", c +# return DefaultApps.app_default(self, c) # # =================================================================== # This system is based on things called MODES and FLAGS. You can read -- cgit 1.4.1-2-gfad0 From bd156f5c939505c9a7ec2fc4f48242719887de6f Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 15 Nov 2011 21:24:13 +0100 Subject: data/scope.sh: shorter way of getting file extension --- ranger/data/scope.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh index aeb47a13..ed4f01e1 100755 --- a/ranger/data/scope.sh +++ b/ranger/data/scope.sh @@ -26,7 +26,7 @@ maxln=200 # Stop after $maxln lines. Can be used like ls | head -n $maxln # Find out something about the file: mimetype=$(file --mime-type -Lb "$path") -extension=$(echo "$path" | grep '\.' | grep -o '[^.]\+$') +extension=${path##*.} # Functions: # "have $1" succeeds if $1 is an existing command/installed program -- cgit 1.4.1-2-gfad0 From 1b2d7d7a1966310f40cd1793b6a8d303067390b9 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 15 Nov 2011 23:25:51 +0100 Subject: defaults/apps.py: Added mupdf and llpp, reordered. --- ranger/defaults/apps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 01acd551..4654f592 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -105,8 +105,8 @@ class CustomApplications(Applications): if f.extension is not None: if f.extension in ('pdf', ): - return self.either(c, 'evince', 'zathura', 'apvlv', 'okular', - 'epdfview') + return self.either(c, 'llpp', 'zathura', 'mupdf', 'apvlv', + 'evince', 'okular', 'epdfview') if f.extension == 'djvu': return self.either(c, 'evince') if f.extension in ('xml', 'csv'): -- cgit 1.4.1-2-gfad0 From e854efe9b2bf955b55b9e4175e67035742c01e2d Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 16 Nov 2011 23:14:47 +0100 Subject: defaults/apps.py: detach epdfview, mupdf and llpp. --- ranger/defaults/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 4654f592..0a68d006 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -306,7 +306,7 @@ CustomApplications.generic( 'luakit', 'uzbl', 'vimprobable', 'vimprobable2', 'jumanji', 'firefox', 'seamonkey', 'iceweasel', 'opera', 'surf', 'midori', 'epiphany', 'konqueror', - 'evince', 'zathura', 'apvlv', 'okular', + 'evince', 'zathura', 'apvlv', 'okular', 'epdfview', 'mupdf', 'llpp', 'eog', 'mirage', 'gimp', 'libreoffice', 'soffice', 'ooffice', 'gnumeric', 'kspread', 'abiword', 'gmplayer', 'smplayer', 'vlc', -- cgit 1.4.1-2-gfad0 From 2b02078dc0ec9e939e1db3cbbe5f1d3bf4490846 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 23 Nov 2011 12:58:34 +0100 Subject: core.actions: only choosefile when pressing l/enter --- ranger/core/actions.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index dbfba502..1fe99ada 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -285,15 +285,16 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): Both flags and mode specify how the program is run.""" # ranger can act as a file chooser when running with --choosefile=... - if ranger.arg.choosefile: - open(ranger.arg.choosefile, 'w').write(self.fm.env.cf.path) + if ('mode' not in kw or kw['mode'] == 0) and 'app' not in kw: + if ranger.arg.choosefile: + open(ranger.arg.choosefile, 'w').write(self.fm.env.cf.path) - if ranger.arg.choosefiles: - open(ranger.arg.choosefiles, 'w').write("".join( - f.path + "\n" for f in self.fm.env.get_selection())) + if ranger.arg.choosefiles: + open(ranger.arg.choosefiles, 'w').write("".join( + f.path + "\n" for f in self.fm.env.get_selection())) - if ranger.arg.choosefile or ranger.arg.choosefiles: - raise SystemExit() + if ranger.arg.choosefile or ranger.arg.choosefiles: + raise SystemExit() if isinstance(files, set): files = list(files) -- cgit 1.4.1-2-gfad0 From 4038295758a9786c7f88405f330d2264b912b059 Mon Sep 17 00:00:00 2001 From: M Rawash Date: Mon, 5 Dec 2011 21:06:59 +0200 Subject: Added new flags 'a', 'r' and 't' --- ranger/core/runner.py | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/ranger/core/runner.py b/ranger/core/runner.py index 5d97489d..cf7b7b41 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -30,15 +30,19 @@ d: detach the process. p: redirect output to the pager c: run only the current file (not handled here) w: wait for enter-press afterwards +a: use alternative application +r: run application with root privilege (requires sudo) +t: run application in a new terminal window (An uppercase key negates the respective lower case flag) """ import os import sys from subprocess import Popen, PIPE +from ranger.ext.get_executables import get_executables -ALLOWED_FLAGS = 'sdpwcSDPWC' +ALLOWED_FLAGS = 'sdpwcartSDPWCART' def press_enter(): @@ -119,7 +123,7 @@ class Runner(object): def __call__(self, action=None, try_app_first=False, app='default', files=None, mode=0, - flags='', wait=True, **popen_kws): + flags='', alt=None, wait=True, **popen_kws): """ Run the application in the way specified by the options. @@ -134,7 +138,7 @@ class Runner(object): # an Application object. context = Context(app=app, files=files, mode=mode, fm=self.fm, - flags=flags, wait=wait, popen_kws=popen_kws, + flags=flags, alt=alt, wait=wait, popen_kws=popen_kws, file=files and files[0] or None) if self.apps: @@ -160,7 +164,6 @@ class Runner(object): wait_for_enter = False devnull = None - popen_kws['args'] = action if 'shell' not in popen_kws: popen_kws['shell'] = isinstance(action, str) if 'stdout' not in popen_kws: @@ -189,7 +192,35 @@ class Runner(object): if 'w' in context.flags: if not pipe_output and context.wait: # <-- sanity check wait_for_enter = True + if 'a' in context.flags and context.alt: + if isinstance(action, str): + import re + action = re.sub('\w+', context.alt, action) + else: + action[0] = context.alt + if 'r' in context.flags: + if 'sudo' not in get_executables(): + return self._log("Can not run with 'r' flag, sudo is not installed!") + if isinstance(action, str): + action = 'sudo '+action + else: + action = ['sudo']+action + toggle_ui = True + context.wait = True + if 't' in context.flags: + if 'DISPLAY' not in os.environ: + return self._log("Can not run with 't' flag, no display found!") + term = os.environ['TERMCMD'] if 'TERMCMD' in os.environ else os.environ['TERM'] + if term not in get_executables(): + term = 'xterm' + if isinstance(action, str): + action = term+' -e '+action + else: + action = [term,'-e']+action + toggle_ui = False + context.wait = False + popen_kws['args'] = action # Finally, run it if toggle_ui: -- cgit 1.4.1-2-gfad0 From f206876a66deb57eb27058c35e69c411e5171f01 Mon Sep 17 00:00:00 2001 From: M Rawash Date: Wed, 7 Dec 2011 00:39:41 +0200 Subject: core/runner.py: got rid of the 'a' flag --- ranger/core/runner.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/ranger/core/runner.py b/ranger/core/runner.py index cf7b7b41..7d433652 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -30,7 +30,6 @@ d: detach the process. p: redirect output to the pager c: run only the current file (not handled here) w: wait for enter-press afterwards -a: use alternative application r: run application with root privilege (requires sudo) t: run application in a new terminal window (An uppercase key negates the respective lower case flag) @@ -123,7 +122,7 @@ class Runner(object): def __call__(self, action=None, try_app_first=False, app='default', files=None, mode=0, - flags='', alt=None, wait=True, **popen_kws): + flags='', wait=True, **popen_kws): """ Run the application in the way specified by the options. @@ -138,7 +137,7 @@ class Runner(object): # an Application object. context = Context(app=app, files=files, mode=mode, fm=self.fm, - flags=flags, alt=alt, wait=wait, popen_kws=popen_kws, + flags=flags, wait=wait, popen_kws=popen_kws, file=files and files[0] or None) if self.apps: @@ -192,12 +191,6 @@ class Runner(object): if 'w' in context.flags: if not pipe_output and context.wait: # <-- sanity check wait_for_enter = True - if 'a' in context.flags and context.alt: - if isinstance(action, str): - import re - action = re.sub('\w+', context.alt, action) - else: - action[0] = context.alt if 'r' in context.flags: if 'sudo' not in get_executables(): return self._log("Can not run with 'r' flag, sudo is not installed!") -- cgit 1.4.1-2-gfad0 From 703b35122c4db45b601547a5f341fd7dbc6f0052 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 10 Dec 2011 01:24:01 +0100 Subject: core.loader: fix bug with chardet module --- ranger/core/loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/core/loader.py b/ranger/core/loader.py index e1262718..59d3e6c0 100644 --- a/ranger/core/loader.py +++ b/ranger/core/loader.py @@ -146,7 +146,7 @@ def safeDecode(string): return string.decode("utf-8") except (UnicodeDecodeError): if HAVE_CHARDET: - return string.decode(chardet.detect(str)["encoding"]) + return string.decode(chardet.detect(string)["encoding"]) else: return "" -- cgit 1.4.1-2-gfad0 From d6e61b1d3004ce58fc58a9d6035498b13e6f2089 Mon Sep 17 00:00:00 2001 From: M Rawash Date: Sun, 11 Dec 2011 21:58:02 +0200 Subject: Added documentation for new flags --- doc/ranger.1 | 7 ++++++- doc/ranger.pod | 5 +++++ ranger/defaults/apps.py | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/ranger.1 b/doc/ranger.1 index c1a5b381..94d5387d 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -282,18 +282,23 @@ Note: The bookmarks ' (Apostrophe) and ` (Backtick) are the same. Flags give you a way to modify the behavior of the spawned process. They are used in the commands :open_with (key \*(L"r\*(R") and :shell (key \*(L"!\*(R"). .PP -.Vb 5 +.Vb 7 \& s Silent mode. Output will be discarded. \& d Detach the process. (Run in background) \& p Redirect output to the pager \& w Wait for an Enter\-press when the process is done \& c Run the current file only, instead of the selection +\& r Run application with root privilege (requires sudo) +\& t Run application in a new terminal window .Ve .PP By default, all the flags are off unless specified otherwise in the \fIapps.py\fR configuration file. You can specify as many flags as you want. An uppercase flag negates the effect: \*(L"ddcccDs\*(R" is equivalent to \*(L"cs\*(R". .PP +The \*(L"t\*(R" flag looks for the environment variable \s-1TERMCMD\s0, and uses it as the +terminal command, if it's not set it'll use xterm. +.PP Examples: \f(CW\*(C`:open_with p\*(C'\fR will pipe the output of that process into the pager. \f(CW\*(C`:shell \-w df\*(C'\fR will run \*(L"df\*(R" and wait for you to press Enter before switching back to ranger. diff --git a/doc/ranger.pod b/doc/ranger.pod index 8943b476..e261ca60 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -190,11 +190,16 @@ used in the commands :open_with (key "r") and :shell (key "!"). p Redirect output to the pager w Wait for an Enter-press when the process is done c Run the current file only, instead of the selection + r Run application with root privilege (requires sudo) + t Run application in a new terminal window By default, all the flags are off unless specified otherwise in the F configuration file. You can specify as many flags as you want. An uppercase flag negates the effect: "ddcccDs" is equivalent to "cs". +The "t" flag looks for the environment variable TERMCMD, and uses it as the +terminal command, if it's not set it'll use xterm. + Examples: C<:open_with p> will pipe the output of that process into the pager. C<:shell -w df> will run "df" and wait for you to press Enter before switching back to ranger. diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 0a68d006..77c66c7b 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -35,6 +35,8 @@ # p Redirect output to the pager # w Wait for an Enter-press when the process is done # c Run the current file only, instead of the selection +# r Run application with root privilege +# t Run application in a new terminal window # # To implement flags in this file, you could do this: # context.flags += "d" -- cgit 1.4.1-2-gfad0 From d99291d8395042ade1337d025a69eaccd191bb54 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 3 Dec 2011 00:27:10 +0100 Subject: fsobject.fsobject: Properly escape control characters --- ranger/fsobject/fsobject.py | 11 +++++++++++ ranger/gui/widgets/browsercolumn.py | 2 +- ranger/gui/widgets/titlebar.py | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index 51e49808..1d1f202d 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -26,6 +26,12 @@ from ranger.ext.spawn import spawn from ranger.ext.lazy_property import lazy_property from ranger.ext.human_readable import human_readable +if hasattr(str, 'maketrans'): + maketrans = str.maketrans +else: + from string import maketrans +unsafe_chars = '\n' + ''.join(map(chr, range(32))) + ''.join(map(chr, range(128, 256))) +safe_string_table = maketrans(unsafe_chars, '?' * len(unsafe_chars)) _extract_number_re = re.compile(r'([^0-9]?)(\d*)') class FileSystemObject(FileManagerAware): @@ -106,6 +112,11 @@ class FileSystemObject(FileManagerAware): return [c if i % 3 == 1 else (int(c) if c else 0) for i, c in \ enumerate(_extract_number_re.split(self.basename_lower))] + @lazy_property + def safe_basename(self): + return self.basename.translate(safe_string_table) + + for attr in ('video', 'audio', 'image', 'media', 'document', 'container'): exec("%s = lazy_property(" "lambda self: self.set_mimetype() or self.%s)" % (attr, attr)) diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index b6b745aa..91633d4e 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -220,7 +220,7 @@ class BrowserColumn(Pager): bad_info_color = None this_color = base_color + list(drawn.mimetype_tuple) - text = drawn.basename + text = drawn.safe_basename tagged = self.fm.tags and drawn.realpath in self.fm.tags if tagged: diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py index 3ca3f80d..ddb27f2c 100644 --- a/ranger/gui/widgets/titlebar.py +++ b/ranger/gui/widgets/titlebar.py @@ -124,11 +124,11 @@ class TitleBar(Widget): else: clr = 'directory' - bar.add(path.basename, clr, directory=path) + bar.add(path.safe_basename, clr, directory=path) bar.add('/', clr, fixed=True, directory=path) if self.env.cf is not None: - bar.add(self.env.cf.basename, 'file') + bar.add(self.env.cf.safe_basename, 'file') def _get_right_part(self, bar): # TODO: fix that pressed keys are cut off when chaining CTRL keys -- cgit 1.4.1-2-gfad0 From fcc47bf5261f328fb38c2df03f62c0847e562552 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 17 Dec 2011 22:40:46 +0100 Subject: gui.ui: Only display printable chars in the window title --- ranger/fsobject/fsobject.py | 9 ++++++--- ranger/gui/ui.py | 6 ++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index 1d1f202d..943c8aa4 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -30,10 +30,13 @@ if hasattr(str, 'maketrans'): maketrans = str.maketrans else: from string import maketrans -unsafe_chars = '\n' + ''.join(map(chr, range(32))) + ''.join(map(chr, range(128, 256))) -safe_string_table = maketrans(unsafe_chars, '?' * len(unsafe_chars)) +_unsafe_chars = '\n' + ''.join(map(chr, range(32))) + ''.join(map(chr, range(128, 256))) +_safe_string_table = maketrans(_unsafe_chars, '?' * len(_unsafe_chars)) _extract_number_re = re.compile(r'([^0-9]?)(\d*)') +def safe_path(path): + return path.translate(_safe_string_table) + class FileSystemObject(FileManagerAware): (basename, basename_lower, @@ -114,7 +117,7 @@ class FileSystemObject(FileManagerAware): @lazy_property def safe_basename(self): - return self.basename.translate(safe_string_table) + return self.basename.translate(_safe_string_table) for attr in ('video', 'audio', 'image', 'media', 'document', 'container'): diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 91d0d774..064d4a16 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -21,6 +21,7 @@ import _curses from .displayable import DisplayableContainer from .mouse_event import MouseEvent from ranger.ext.keybinding_parser import ALT_KEY +from ranger.fsobject.fsobject import safe_path TERMINALS_WITH_TITLE = ("xterm", "xterm-256color", "rxvt", "rxvt-256color", "rxvt-unicode", "aterm", "Eterm", @@ -293,10 +294,7 @@ class UI(DisplayableContainer): split = cwd.rsplit(os.sep, self.settings.shorten_title) if os.sep in split[0]: cwd = os.sep.join(split[1:]) - try: - sys.stdout.write("\033]2;ranger:" + cwd + "\007") - except UnicodeEncodeError: - sys.stdout.write("\033]2;ranger:" + ascii_only(cwd) + "\007") + sys.stdout.write("\033]2;ranger:" + safe_path(cwd) + "\007") self.win.refresh() def finalize(self): -- cgit 1.4.1-2-gfad0 From fc17126a6a0ab436c757c545fa55e87ccd89635c Mon Sep 17 00:00:00 2001 From: M Rawash Date: Mon, 19 Dec 2011 08:44:46 +0200 Subject: core/runner.py: allow combination of 'r' and 'd' flags --- ranger/core/runner.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ranger/core/runner.py b/ranger/core/runner.py index 7d433652..baec1595 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -194,10 +194,11 @@ class Runner(object): if 'r' in context.flags: if 'sudo' not in get_executables(): return self._log("Can not run with 'r' flag, sudo is not installed!") + dflag = ('d' in context.flags) if isinstance(action, str): - action = 'sudo '+action + action = 'sudo ' + (dflag and '-b ' or '') + action else: - action = ['sudo']+action + action = ['sudo'] + (dflag and ['-b'] or []) + action toggle_ui = True context.wait = True if 't' in context.flags: -- cgit 1.4.1-2-gfad0 From a87bf3e07a77ca167d33435abefdf22b10a13656 Mon Sep 17 00:00:00 2001 From: M Rawash Date: Mon, 19 Dec 2011 08:47:05 +0200 Subject: core/runner.py: removed the 'a' flag from ALLOWED_FLAGS --- ranger/core/runner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ranger/core/runner.py b/ranger/core/runner.py index baec1595..ba6d82a8 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -41,7 +41,7 @@ from subprocess import Popen, PIPE from ranger.ext.get_executables import get_executables -ALLOWED_FLAGS = 'sdpwcartSDPWCART' +ALLOWED_FLAGS = 'sdpwcrtSDPWCRT' def press_enter(): @@ -208,9 +208,9 @@ class Runner(object): if term not in get_executables(): term = 'xterm' if isinstance(action, str): - action = term+' -e '+action + action = term + ' -e ' + action else: - action = [term,'-e']+action + action = [term, '-e'] + action toggle_ui = False context.wait = False -- cgit 1.4.1-2-gfad0 From 5efbecb2ec32805541923a72462d10fe58cd32cf Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 19 Dec 2011 13:40:18 +0100 Subject: Revert the part of d99291d8 that broke unicode --- ranger/gui/widgets/browsercolumn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index 91633d4e..b6b745aa 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -220,7 +220,7 @@ class BrowserColumn(Pager): bad_info_color = None this_color = base_color + list(drawn.mimetype_tuple) - text = drawn.safe_basename + text = drawn.basename tagged = self.fm.tags and drawn.realpath in self.fm.tags if tagged: -- cgit 1.4.1-2-gfad0 From e681682273412e6b26f85bc0dff325f605abb305 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 19 Dec 2011 14:15:25 +0100 Subject: core.fm: Added todo-entry and some fail-safe stuff --- ranger/core/fm.py | 2 ++ ranger/core/main.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ranger/core/fm.py b/ranger/core/fm.py index 9b5cc036..20327a71 100644 --- a/ranger/core/fm.py +++ b/ranger/core/fm.py @@ -212,6 +212,8 @@ class FM(Actions, SignalDispatcher): finally: if ranger.arg.choosedir and self.env.cwd and self.env.cwd.path: + # XXX: UnicodeEncodeError: 'utf-8' codec can't encode character + # '\udcf6' in position 42: surrogates not allowed open(ranger.arg.choosedir, 'w').write(self.env.cwd.path) self.bookmarks.remember(env.cwd) self.bookmarks.save() diff --git a/ranger/core/main.py b/ranger/core/main.py index 14e4b1f6..b69e3c6d 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -127,7 +127,10 @@ def main(): print("ranger version: %s, executed with python %s" % (ranger.__version__, sys.version.split()[0])) print("Locale: %s" % '.'.join(str(s) for s in locale.getlocale())) - print("Current file: %s" % filepath) + try: + print("Current file: %s" % filepath) + except: + pass print(crash_traceback) print("ranger crashed. " \ "Please report this traceback at:") -- cgit 1.4.1-2-gfad0 From dd7e3aea6df8d3b85dd6ada555198f4a2209c78f Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 19 Dec 2011 14:18:48 +0100 Subject: widgets.titlebar: TODO entry, fix unicode chars in titlebar --- ranger/gui/widgets/titlebar.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py index ddb27f2c..2b5e836b 100644 --- a/ranger/gui/widgets/titlebar.py +++ b/ranger/gui/widgets/titlebar.py @@ -102,6 +102,7 @@ class TitleBar(Widget): self.result = bar.combine() def _get_left_part(self, bar): + # TODO: Properly escape non-printable chars without breaking unicode if self.env.username == 'root': clr = 'bad' else: @@ -124,11 +125,11 @@ class TitleBar(Widget): else: clr = 'directory' - bar.add(path.safe_basename, clr, directory=path) + bar.add(path.basename, clr, directory=path) bar.add('/', clr, fixed=True, directory=path) if self.env.cf is not None: - bar.add(self.env.cf.safe_basename, 'file') + bar.add(self.env.cf.basename, 'file') def _get_right_part(self, bar): # TODO: fix that pressed keys are cut off when chaining CTRL keys -- cgit 1.4.1-2-gfad0 From 8d0782c20ce033ae69fa6f134f68af0e9862acdc Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 4 Jan 2012 22:21:39 +0100 Subject: widgets.statusbar: Include marked directory sizes in statusbar summary --- ranger/core/actions.py | 1 + ranger/gui/widgets/statusbar.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 1fe99ada..8106a89b 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -122,6 +122,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): def get_cumulative_size(self): for f in self.env.get_selection() or (): f.look_up_cumulative_size() + self.ui.status.request_redraw() self.ui.redraw_main_column() def redraw_window(self): diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index a7d3fc73..7948b201 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -236,9 +236,9 @@ class StatusBar(Widget): if len(target.marked_items) == len(target.files): right.add(human_readable(target.disk_usage, separator='')) else: - right.add(human_readable(sum(f.size \ - for f in target.marked_items \ - if f.is_file), separator='')) + sumsize = sum(f.size for f in target.marked_items if not + f.is_directory or f._cumulative_size_calculated) + right.add(human_readable(sumsize, separator='')) right.add("/" + str(len(target.marked_items))) else: right.add(human_readable(target.disk_usage, separator='') + -- cgit 1.4.1-2-gfad0 From 8c917a95c27e5293f762b018edc0751e95cf9726 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 11 Jan 2012 10:31:53 +0100 Subject: defaults.commands: on in !shell put the filename instead of %s --- ranger/defaults/commands.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index 6cfeff17..087d9b14 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -218,7 +218,11 @@ class shell(Command): return (start + program + ' ' for program \ in get_executables() if program.startswith(command)) if position_of_last_space == len(command) - 1: - return self.line + '%s ' + selection = self.fm.env.get_selection() + if len(selection) == 1: + return self.line + selection[0].shell_escaped_basename + ' ' + else: + return self.line + '%s ' else: before_word, start_of_word = self.line.rsplit(' ', 1) return (before_word + ' ' + file.shell_escaped_basename \ -- cgit 1.4.1-2-gfad0 From 999a9268aa3d8104d864131d7ad4ee6d6b2a643b Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 11 Jan 2012 11:03:27 +0100 Subject: ext.shell_escape: Fixed escaping of tabs and unprintables --- ranger/ext/shell_escape.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ranger/ext/shell_escape.py b/ranger/ext/shell_escape.py index 28a502bf..b68afc33 100644 --- a/ranger/ext/shell_escape.py +++ b/ranger/ext/shell_escape.py @@ -18,17 +18,20 @@ Functions to escape metacharacters of arguments for shell commands. """ META_CHARS = (' ', "'", '"', '`', '&', '|', ';', - '$', '!', '(', ')', '[', ']', '<', '>') + '$', '!', '(', ')', '[', ']', '<', '>', '\t') +UNESCAPABLE = set(map(chr, list(range(9)) + list(range(10, 32)) \ + + list(range(127, 256)))) META_DICT = dict([(mc, '\\' + mc) for mc in META_CHARS]) def shell_quote(string): """Escapes by quoting""" return "'" + str(string).replace("'", "'\\''") + "'" - def shell_escape(arg): """Escapes by adding backslashes""" arg = str(arg) + if UNESCAPABLE & set(arg): + return shell_quote(arg) arg = arg.replace('\\', '\\\\') # make sure this comes at the start for k, v in META_DICT.items(): arg = arg.replace(k, v) -- cgit 1.4.1-2-gfad0 From 907e3fde740f69b4ef9bc8fa4a7090512583baf7 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 11 Jan 2012 11:49:34 +0100 Subject: added --profile option --- ranger/core/helper.py | 5 +++++ ranger/core/main.py | 28 +++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/ranger/core/helper.py b/ranger/core/helper.py index 88072e13..446d9328 100644 --- a/ranger/core/helper.py +++ b/ranger/core/helper.py @@ -68,6 +68,11 @@ def parse_arguments(): help="List common keys which are not bound to any action.") parser.add_option('--selectfile', type='string', metavar='filepath', help="Open ranger with supplied file selected.") + parser.add_option('--list-tagged-files', type='string', default=None, + metavar='tag', + help="List all files which are tagged with the given tag, default: *") + parser.add_option('--profile', action='store_true', + help="Print statistics of CPU usage on exit.") options, positional = parser.parse_args() arg = OpenStruct(options.__dict__, targets=positional) diff --git a/ranger/core/main.py b/ranger/core/main.py index b69e3c6d..1c2686bb 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -46,6 +46,20 @@ def main(): fm = FM() fm.copy_config_files(arg.copy_config) return 1 if arg.fail_unless_cd else 0 + if arg.list_tagged_files: + fm = FM() + try: + f = open(fm.confpath('tagged'), 'r') + except: + pass + else: + for line in f.readlines(): + if len(line) > 2 and line[1] == ':': + if line[0] in arg.list_tagged_files: + sys.stdout.write(line[2:]) + elif len(line) > 0 and '*' in arg.list_tagged_files: + sys.stdout.write(line) + return 1 if arg.fail_unless_cd else 0 SettingsAware._setup(clean=arg.clean) @@ -107,7 +121,15 @@ def main(): # Run the file manager fm.initialize() fm.ui.initialize() - fm.loop() + if ranger.arg.profile: + import cProfile + import pstats + profile = None + ranger.__fm = fm + cProfile.run('ranger.__fm.loop()', '/tmp/ranger_profile') + profile = pstats.Stats('/tmp/ranger_profile') + else: + fm.loop() except Exception: import traceback crash_traceback = traceback.format_exc() @@ -123,6 +145,10 @@ def main(): fm.ui.destroy() except (AttributeError, NameError): pass + if ranger.arg.profile and profile: + stdout, sys.stdout = sys.stdout, sys.stderr + profile.strip_dirs().sort_stats('cumulative').print_callees(100) + sys.stdout = stdout if crash_traceback: print("ranger version: %s, executed with python %s" % (ranger.__version__, sys.version.split()[0])) -- cgit 1.4.1-2-gfad0 From 4904e06e6aa2278f96d9fd007c6ac3261f127f25 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 11 Jan 2012 14:58:00 +0100 Subject: core.main: pipe the profiler stream to stderr --- ranger/core/main.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ranger/core/main.py b/ranger/core/main.py index 1c2686bb..8458f928 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -127,7 +127,7 @@ def main(): profile = None ranger.__fm = fm cProfile.run('ranger.__fm.loop()', '/tmp/ranger_profile') - profile = pstats.Stats('/tmp/ranger_profile') + profile = pstats.Stats('/tmp/ranger_profile', stream=sys.stderr) else: fm.loop() except Exception: @@ -146,9 +146,7 @@ def main(): except (AttributeError, NameError): pass if ranger.arg.profile and profile: - stdout, sys.stdout = sys.stdout, sys.stderr - profile.strip_dirs().sort_stats('cumulative').print_callees(100) - sys.stdout = stdout + profile.strip_dirs().sort_stats('cumulative').print_callees() if crash_traceback: print("ranger version: %s, executed with python %s" % (ranger.__version__, sys.version.split()[0])) -- cgit 1.4.1-2-gfad0 From 0374f709574d3db79887b86ca4486d65f5eb1c01 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 11 Jan 2012 14:58:14 +0100 Subject: ext.signals: no unnecessary sorting on signal_bind --- ranger/ext/signals.py | 20 ++++++++++++++++++-- ranger/fsobject/directory.py | 4 ++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ranger/ext/signals.py b/ranger/ext/signals.py index ecb48de3..0df39fe0 100644 --- a/ranger/ext/signals.py +++ b/ranger/ext/signals.py @@ -126,7 +126,7 @@ class SignalDispatcher(object): handler._function = None self._signals = dict() - def signal_bind(self, signal_name, function, priority=0.5, weak=False): + def signal_bind(self, signal_name, function, priority=0.5, weak=False, autosort=True): """ Bind a function to the signal. @@ -162,9 +162,25 @@ class SignalDispatcher(object): handler = SignalHandler(signal_name, function, priority, nargs > 0) handlers.append(handler) - handlers.sort(key=lambda handler: -handler._priority) + if autosort: + handlers.sort(key=lambda handler: -handler._priority) return handler + def signal_force_sort(self, signal_name=None): + """ + Forces a sorting of signal handlers by priority. + + This is only necessary if you used signal_bind with autosort=False + after finishing to bind many signals at once. + """ + if signal_name is None: + for handlers in self._signals.values(): + handlers.sort(key=lambda handler: -handler._priority) + elif signal_name in self._signals: + self._signals[signal_name].sort(key=lambda handler: -handler._priority) + else: + return False + def signal_unbind(self, signal_handler): """ Removes a signal binding. diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py index bc09b080..81e50ed9 100644 --- a/ranger/fsobject/directory.py +++ b/ranger/fsobject/directory.py @@ -103,11 +103,11 @@ class Directory(FileSystemObject, Accumulator, Loadable, SettingsAware): for opt in ('sort_directories_first', 'sort', 'sort_reverse', 'sort_case_insensitive'): self.settings.signal_bind('setopt.' + opt, - self.request_resort, weak=True) + self.request_resort, weak=True, autosort=False) for opt in ('hidden_filter', 'show_hidden'): self.settings.signal_bind('setopt.' + opt, - self.request_reload, weak=True) + self.request_reload, weak=True, autosort=False) self.use() def request_resort(self): -- cgit 1.4.1-2-gfad0 From 98bca305d5e053ccd624073e2c2ecac50640106f Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 11 Jan 2012 16:16:29 +0100 Subject: gui.colorschemes: more efficient color_at --- ranger/ext/cached_function.py | 27 ++++++++++++++++++++++++ ranger/gui/colorscheme.py | 42 ++++++++++++++++--------------------- ranger/gui/curses_shortcuts.py | 3 --- ranger/gui/widgets/browsercolumn.py | 12 +++++------ ranger/gui/widgets/taskview.py | 12 +++++------ 5 files changed, 56 insertions(+), 40 deletions(-) create mode 100644 ranger/ext/cached_function.py diff --git a/ranger/ext/cached_function.py b/ranger/ext/cached_function.py new file mode 100644 index 00000000..00068583 --- /dev/null +++ b/ranger/ext/cached_function.py @@ -0,0 +1,27 @@ +# Copyright (C) 2012 Roman Zimbelmann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +def cached_function(fnc): + cache = {} + def inner_cached_function(self, *args): + try: + return cache[args] + except: + value = fnc(self, *args) + cache[args] = value + return value + inner_cached_function._cache = cache + return inner_cached_function + diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py index bc5a67a5..cc72d6a8 100644 --- a/ranger/gui/colorscheme.py +++ b/ranger/gui/colorscheme.py @@ -46,6 +46,8 @@ from ranger.gui.color import get_color from ranger.gui.context import Context from ranger.core.helper import allow_access_to_confdir from ranger.core.shared import SettingsAware +from ranger.ext.cached_function import cached_function +from ranger.ext.iter_tools import flatten # ColorScheme is not SettingsAware but it will gain access # to the settings during the initialization. We can't import @@ -60,9 +62,6 @@ class ColorScheme(SettingsAware): which fits to the given keys. """ - def __init__(self): - self.cache = {} - def get(self, *keys): """ Returns the (fg, bg, attr) for the given keys. @@ -70,33 +69,28 @@ class ColorScheme(SettingsAware): Using this function rather than use() will cache all colors for faster access. """ - keys = frozenset(keys) - try: - return self.cache[keys] - - except KeyError: - context = Context(keys) - - # add custom error messages for broken colorschemes - color = self.use(context) - if self.settings.colorscheme_overlay: - result = self.settings.colorscheme_overlay(context, *color) - assert isinstance(result, (tuple, list)), \ - "Your colorscheme overlay doesn't return a tuple!" - assert all(isinstance(val, int) for val in result), \ - "Your colorscheme overlay doesn't return a tuple"\ - " containing 3 integers!" - color = result - self.cache[keys] = color - return color - + context = Context(keys) + + # add custom error messages for broken colorschemes + color = self.use(context) + if self.settings.colorscheme_overlay: + result = self.settings.colorscheme_overlay(context, *color) + assert isinstance(result, (tuple, list)), \ + "Your colorscheme overlay doesn't return a tuple!" + assert all(isinstance(val, int) for val in result), \ + "Your colorscheme overlay doesn't return a tuple"\ + " containing 3 integers!" + color = result + return color + + @cached_function def get_attr(self, *keys): """ Returns the curses attribute for the specified keys Ready to use for curses.setattr() """ - fg, bg, attr = self.get(*keys) + fg, bg, attr = self.get(*flatten(keys)) return attr | color_pair(get_color(fg, bg)) def use(self, context): diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py index 10a159a1..a383ab4c 100644 --- a/ranger/gui/curses_shortcuts.py +++ b/ranger/gui/curses_shortcuts.py @@ -17,7 +17,6 @@ import curses import _curses -from ranger.ext.iter_tools import flatten from ranger.gui.color import get_color from ranger.core.shared import SettingsAware @@ -63,7 +62,6 @@ class CursesShortcuts(SettingsAware): def color(self, *keys): """Change the colors from now on.""" - keys = flatten(keys) attr = self.settings.colorscheme.get_attr(*keys) try: self.win.attrset(attr) @@ -72,7 +70,6 @@ class CursesShortcuts(SettingsAware): def color_at(self, y, x, wid, *keys): """Change the colors at the specified position""" - keys = flatten(keys) attr = self.settings.colorscheme.get_attr(*keys) try: self.win.chgat(y, x, wid, attr) diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py index b6b745aa..e9c08439 100644 --- a/ranger/gui/widgets/browsercolumn.py +++ b/ranger/gui/widgets/browsercolumn.py @@ -178,7 +178,7 @@ class BrowserColumn(Pager): self.win.move(0, 0) if not self.target.content_loaded: - self.color(base_color) + self.color(tuple(base_color)) self.addnstr("...", self.wid) self.color_reset() return @@ -187,13 +187,13 @@ class BrowserColumn(Pager): base_color.append('main_column') if not self.target.accessible: - self.color(base_color, 'error') + self.color(tuple(base_color + ['error'])) self.addnstr("not accessible", self.wid) self.color_reset() return if self.target.empty(): - self.color(base_color, 'empty') + self.color(tuple(base_color + ['empty'])) self.addnstr("empty", self.wid) self.color_reset() return @@ -289,15 +289,15 @@ class BrowserColumn(Pager): if x > 0: self.addstr(line, x, infostring) - self.color_at(line, 0, self.wid, this_color) + self.color_at(line, 0, self.wid, tuple(this_color)) if bad_info_color: start, wid = bad_info_color - self.color_at(line, start, wid, this_color, 'badinfo') + self.color_at(line, start, wid, tuple(this_color), 'badinfo') if (self.main_column or self.settings.display_tags_in_all_columns) \ and tagged and self.wid > 2: this_color.append('tag_marker') - self.color_at(line, 0, len(tagged_marker), this_color) + self.color_at(line, 0, len(tagged_marker), tuple(this_color)) self.color_reset() diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py index c4476b9c..a3f8e314 100644 --- a/ranger/gui/widgets/taskview.py +++ b/ranger/gui/widgets/taskview.py @@ -17,8 +17,6 @@ The TaskView allows you to modify what the loader is doing. """ -from collections import deque - from . import Widget from ranger.ext.accumulator import Accumulator @@ -31,7 +29,7 @@ class TaskView(Widget, Accumulator): self.scroll_begin = 0 def draw(self): - base_clr = deque() + base_clr = [] base_clr.append('in_taskview') lst = self.get_list() @@ -48,7 +46,7 @@ class TaskView(Widget, Accumulator): return self.addstr(0, 0, "Task View") - self.color_at(0, 0, self.wid, base_clr, 'title') + self.color_at(0, 0, self.wid, tuple(base_clr), 'title') if lst: for i in range(self.hei - 1): @@ -59,19 +57,19 @@ class TaskView(Widget, Accumulator): break y = i + 1 - clr = deque(base_clr) + clr = list(base_clr) if self.pointer == i: clr.append('selected') descr = obj.get_description() self.addstr(y, 0, descr, self.wid) - self.color_at(y, 0, self.wid, clr) + self.color_at(y, 0, self.wid, tuple(clr)) else: if self.hei > 1: self.addstr(1, 0, "No task in the queue.") - self.color_at(1, 0, self.wid, base_clr, 'error') + self.color_at(1, 0, self.wid, tuple(base_clr), 'error') self.color_reset() -- cgit 1.4.1-2-gfad0 From 7fb3df7ce886f1f3085bd8561669cef714d077e3 Mon Sep 17 00:00:00 2001 From: hut Date: Sat, 14 Jan 2012 23:58:42 +0100 Subject: defaults/options: case insensitive sort by default --- ranger/defaults/options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py index 46bca607..d076a96d 100644 --- a/ranger/defaults/options.py +++ b/ranger/defaults/options.py @@ -116,7 +116,7 @@ show_cursor = False # One of: size, basename, mtime, type sort = 'natural' sort_reverse = False -sort_case_insensitive = False +sort_case_insensitive = True sort_directories_first = True # Enable this if key combinations with the Alt Key don't work for you. -- cgit 1.4.1-2-gfad0 From a110af2a538104dd909e4766dd1c0044b804dbdc Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 15 Jan 2012 00:08:54 +0100 Subject: defaults.commands.bulkrename: support filenames starting with - --- ranger/defaults/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index 087d9b14..da0c6735 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -801,11 +801,11 @@ class bulkrename(Command): cmdfile.write(b"# This file will be executed when you close the editor.\n") cmdfile.write(b"# Please double-check everything, clear the file to abort.\n") if py3: - cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \ + cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \ for old, new in zip(filenames, new_filenames) \ if old != new).encode("utf-8")) else: - cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \ + cmdfile.write("\n".join("mv -vi -- " + esc(old) + " " + esc(new) \ for old, new in zip(filenames, new_filenames) if old != new)) cmdfile.flush() self.fm.execute_file([File(cmdfile.name)], app='editor') -- cgit 1.4.1-2-gfad0 From 1cc4c0e4c2a022d64dac2d380729ffacb2cfa31c Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 15 Jan 2012 03:34:31 +0100 Subject: gui.ui: Fix title drawing. Since when is this broken anyway?! --- ranger/gui/ui.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 064d4a16..5599190d 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -21,7 +21,6 @@ import _curses from .displayable import DisplayableContainer from .mouse_event import MouseEvent from ranger.ext.keybinding_parser import ALT_KEY -from ranger.fsobject.fsobject import safe_path TERMINALS_WITH_TITLE = ("xterm", "xterm-256color", "rxvt", "rxvt-256color", "rxvt-unicode", "aterm", "Eterm", @@ -294,7 +293,10 @@ class UI(DisplayableContainer): split = cwd.rsplit(os.sep, self.settings.shorten_title) if os.sep in split[0]: cwd = os.sep.join(split[1:]) - sys.stdout.write("\033]2;ranger:" + safe_path(cwd) + "\007") + fixed_cwd = cwd.encode('utf-8', 'surrogateescape'). \ + decode('utf-8', 'replace') + sys.stdout.write("\033]2;ranger:" + fixed_cwd + "\007") + sys.stdout.flush() self.win.refresh() def finalize(self): -- cgit 1.4.1-2-gfad0 From c2b71f721ba4fec22e6b8135508e07cb56df3320 Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 18 Jan 2012 16:21:20 +0100 Subject: Added doc for TERMCMD and integrated it in commands.py --- doc/ranger.pod | 7 +++++++ ranger/core/runner.py | 4 +++- ranger/defaults/commands.py | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/ranger.pod b/doc/ranger.pod index e261ca60..65615a8e 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -956,6 +956,13 @@ program out of "vim", "emacs" and "nano". Defines the shell that ranger is going to use with the :shell command and the "S" key. Defaults to "bash". +=item TERMCMD + +Defines the terminal emulator command that ranger is going to use with the +:terminal command and the "t" run flag. Defaults to "x-terminal-emulator" or +"xterm" + + =item XDG_CONFIG_HOME Specifies the directory for configuration files. Defaults to F<$HOME/.config>. diff --git a/ranger/core/runner.py b/ranger/core/runner.py index ba6d82a8..17cdcca5 100644 --- a/ranger/core/runner.py +++ b/ranger/core/runner.py @@ -204,7 +204,9 @@ class Runner(object): if 't' in context.flags: if 'DISPLAY' not in os.environ: return self._log("Can not run with 't' flag, no display found!") - term = os.environ['TERMCMD'] if 'TERMCMD' in os.environ else os.environ['TERM'] + term = os.environ.get('TERMCMD', os.environ.get('TERM')) + if term not in get_executables(): + term = 'x-terminal-emulator' if term not in get_executables(): term = 'xterm' if isinstance(action, str): diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index da0c6735..ba9030c5 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -459,6 +459,11 @@ class terminal(Command): Spawns an "x-terminal-emulator" starting in the current directory. """ def execute(self): + command = os.environ.get('TERMCMD', os.environ.get('TERM')) + if command not in get_executables(): + command = 'x-terminal-emulator' + if command not in get_executables(): + command = 'xterm' self.fm.run('x-terminal-emulator', flags='d') -- cgit 1.4.1-2-gfad0 From 5db94646ffdd492103a40a2e33758b1827032661 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 20 Jan 2012 20:46:10 +0100 Subject: ext.cached_function: Fixed --- ranger/ext/cached_function.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/ext/cached_function.py b/ranger/ext/cached_function.py index 00068583..4d9ded18 100644 --- a/ranger/ext/cached_function.py +++ b/ranger/ext/cached_function.py @@ -15,11 +15,11 @@ def cached_function(fnc): cache = {} - def inner_cached_function(self, *args): + def inner_cached_function(*args): try: return cache[args] except: - value = fnc(self, *args) + value = fnc(*args) cache[args] = value return value inner_cached_function._cache = cache -- cgit 1.4.1-2-gfad0 From 9e146c8272e25b98d0f196bc34ac1725f894863a Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 24 Jan 2012 10:54:30 +0100 Subject: defaults/commands: fixed TERMCMD in :terminal --- ranger/defaults/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index ba9030c5..c9729999 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -464,7 +464,7 @@ class terminal(Command): command = 'x-terminal-emulator' if command not in get_executables(): command = 'xterm' - self.fm.run('x-terminal-emulator', flags='d') + self.fm.run(command, flags='d') class delete(Command): -- cgit 1.4.1-2-gfad0 From 30f17d752f75f5950315e328b21bff50eac86ab4 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 26 Jan 2012 22:34:48 +0100 Subject: api.commands: Fix slashes in _tab_directory_content() Previously, when the tab completion result is unique, a slash was always added, even if it's a file, not a directory. Thanks to gwash for pointing out this bug. --- ranger/api/commands.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ranger/api/commands.py b/ranger/api/commands.py index ea0df16b..a2501c7f 100644 --- a/ranger/api/commands.py +++ b/ranger/api/commands.py @@ -266,9 +266,11 @@ class Command(FileManagerAware): if len(names) == 0: return - # one result. since it must be a directory, append a slash. + # one result. append a slash if it's a directory if len(names) == 1: - return self.start(1) + join(rel_dirname, names[0]) + '/' + path = join(rel_dirname, names[0]) + slash = '/' if os.path.isdir(path) else '' + return self.start(1) + path + slash # more than one result. append no slash, so the user can # manually type in the slash to advance into that directory -- cgit 1.4.1-2-gfad0 From 47c2cf6920324da597cf969329c0abb59591c9ca Mon Sep 17 00:00:00 2001 From: M Rawash Date: Fri, 27 Jan 2012 00:14:55 +0200 Subject: new :relink command --- doc/ranger.1 | 8 ++++++++ doc/ranger.pod | 4 ++++ ranger/defaults/commands.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/doc/ranger.1 b/doc/ranger.1 index 94d5387d..06b08d50 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -741,6 +741,9 @@ it by typing `` or '' the next time you start ranger. .IX Item "rename newname" Rename the current file. If a file with that name already exists, the renaming will fail. Also try the key binding A for appending something to a file name. +.IP "relink \fInewpath\fR" 2 +.IX Item "relink newpath" +Change the link destination of the current symlink file to . First will load the original link. .IP "save_copy_buffer" 2 .IX Item "save_copy_buffer" Save the copy buffer from \fI~/.config/ranger/copy_buffer\fR. This can be used to @@ -869,6 +872,11 @@ program out of \*(L"vim\*(R", \*(L"emacs\*(R" and \*(L"nano\*(R". .IX Item "SHELL" Defines the shell that ranger is going to use with the :shell command and the \*(L"S\*(R" key. Defaults to \*(L"bash\*(R". +.IP "\s-1TERMCMD\s0" 8 +.IX Item "TERMCMD" +Defines the terminal emulator command that ranger is going to use with the +:terminal command and the \*(L"t\*(R" run flag. Defaults to \*(L"x\-terminal-emulator\*(R" or +\&\*(L"xterm\*(R" .IP "\s-1XDG_CONFIG_HOME\s0" 8 .IX Item "XDG_CONFIG_HOME" Specifies the directory for configuration files. Defaults to \fI\f(CI$HOME\fI/.config\fR. diff --git a/doc/ranger.pod b/doc/ranger.pod index 65615a8e..93b69999 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -787,6 +787,10 @@ it by typing `` or '' the next time you start ranger. Rename the current file. If a file with that name already exists, the renaming will fail. Also try the key binding A for appending something to a file name. +=item relink I + +Change the link destination of the current symlink file to . First will load the original link. + =item save_copy_buffer Save the copy buffer from I<~/.config/ranger/copy_buffer>. This can be used to diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index c9729999..d1f4cf16 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -818,6 +818,45 @@ class bulkrename(Command): cmdfile.close() +class relink(Command): + """ + :relink + + Changes the linked path of the currently highlighted symlink to + """ + + def execute(self): + from ranger.fsobject import File + + new_path = self.rest(1) + cf = self.fm.env.cf + + if not new_path: + return self.fm.notify('Syntax: relink ', bad=True) + + if not cf.is_link: + return self.fm.notify('%s is not a symlink!' % cf.basename, bad=True) + + if new_path == os.readlink(cf.path): + return + + try: + os.remove(cf.path) + os.symlink(new_path, cf.path) + except OSError as err: + self.fm.notify(err) + + self.fm.reset() + self.fm.env.cwd.pointed_obj = cf + self.fm.env.cf = cf + + def tab(self): + if not self.rest(1): + return self.line+os.readlink(self.fm.env.cf.path) + else: + return self._tab_directory_content() + + class help_(Command): """ :help -- cgit 1.4.1-2-gfad0 From 731a5a1eb66d7ff58258dbda30a8c8d9141d129f Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 9 Feb 2012 16:04:02 +0100 Subject: widgets/statusbar: Display "All" when all files are shown (fix) --- ranger/gui/widgets/statusbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index 7948b201..1a3b356e 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -254,7 +254,7 @@ class StatusBar(Widget): elif len(target.files): right.add(str(target.pointer + 1) + '/' + str(len(target.files)) + ' ', base) - if max_pos == 0: + if max_pos <= 0: right.add('All', base, 'all') elif pos == 0: right.add('Top', base, 'top') -- cgit 1.4.1-2-gfad0 From f8b6ef7e55f20dbf1db9aea5e7b40444be413d69 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 9 Feb 2012 16:04:39 +0100 Subject: defaults/apps: Add wildmidi --- ranger/defaults/apps.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py index 77c66c7b..fbcc83c0 100644 --- a/ranger/defaults/apps.py +++ b/ranger/defaults/apps.py @@ -113,6 +113,8 @@ class CustomApplications(Applications): return self.either(c, 'evince') if f.extension in ('xml', 'csv'): return self.either(c, 'editor') + if f.extension == 'mid': + return self.either(c, 'wildmidi') if f.extension in ('html', 'htm', 'xhtml') or f.extension == 'swf': c.flags += 'd' handler = self.either(c, -- cgit 1.4.1-2-gfad0 From 5eed34cb880330cae45c6cfedad596a4c74700e5 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 9 Feb 2012 22:25:26 +0100 Subject: widgets/statusbar: Catch exceptions in env.get_free_space --- ranger/gui/widgets/statusbar.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py index 1a3b356e..1ffb9fa3 100644 --- a/ranger/gui/widgets/statusbar.py +++ b/ranger/gui/widgets/statusbar.py @@ -241,10 +241,14 @@ class StatusBar(Widget): right.add(human_readable(sumsize, separator='')) right.add("/" + str(len(target.marked_items))) else: - right.add(human_readable(target.disk_usage, separator='') + - " sum, ") - right.add(human_readable(self.env.get_free_space( \ - target.mount_path), separator='') + " free") + right.add(human_readable(target.disk_usage, separator='') + " sum") + try: + free = self.env.get_free_space(target.mount_path) + except OSError: + pass + else: + right.add(", ", "space") + right.add(human_readable(free, separator='') + " free") right.add(" ", "space") if target.marked_items: -- cgit 1.4.1-2-gfad0 From a0cff8b77b3a81c84661e96a1efdd5bd5d6e6eac Mon Sep 17 00:00:00 2001 From: David Bengoa Date: Sun, 12 Feb 2012 16:41:48 +0100 Subject: Add --cmd=COMMAND command line option --- ranger/core/helper.py | 2 ++ ranger/core/main.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/ranger/core/helper.py b/ranger/core/helper.py index 446d9328..3f93e79c 100644 --- a/ranger/core/helper.py +++ b/ranger/core/helper.py @@ -73,6 +73,8 @@ def parse_arguments(): help="List all files which are tagged with the given tag, default: *") parser.add_option('--profile', action='store_true', help="Print statistics of CPU usage on exit.") + parser.add_option('--cmd', type='string', metavar='COMMAND', + help="COMMAND will be executed after ranger has initialized") options, positional = parser.parse_args() arg = OpenStruct(options.__dict__, targets=positional) diff --git a/ranger/core/main.py b/ranger/core/main.py index 8458f928..f80ee2d6 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -121,6 +121,10 @@ def main(): # Run the file manager fm.initialize() fm.ui.initialize() + + if arg.cmd: + fm.execute_console(arg.cmd) + if ranger.arg.profile: import cProfile import pstats -- cgit 1.4.1-2-gfad0 From e748d5056c907c5f9e46f6824442d47231a1ad32 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 12 Feb 2012 17:12:28 +0100 Subject: core.main: Allow multiple --cmd's, add man page entry for --cmd --- doc/ranger.1 | 6 +++++- doc/ranger.pod | 5 +++++ ranger/core/helper.py | 5 +++-- ranger/core/main.py | 3 ++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/doc/ranger.1 b/doc/ranger.1 index 06b08d50..e58008e0 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.5.2" "10/24/2011" "ranger manual" +.TH RANGER 1 "ranger-1.5.2" "02/12/2012" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -204,6 +204,10 @@ the execution of this file type is explicitly handled in the configuration. When a filename is supplied, run it with the given \fIflags\fR to modify behavior. The execution of this file type is explicitly handled in the configuration. +.IP "\fB\-\-cmd\fR=\fIcommand\fR" 14 +.IX Item "--cmd=command" +Execute the command after the configuration has been read. Use this option +multiple times to run multiple commands. .IP "\fB\-\-version\fR" 14 .IX Item "--version" Print the version and exit. diff --git a/doc/ranger.pod b/doc/ranger.pod index 93b69999..0799f35d 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -99,6 +99,11 @@ When a filename is supplied, run it with the given I to modify behavior. The execution of this file type is explicitly handled in the configuration. +=item B<--cmd>=I + +Execute the command after the configuration has been read. Use this option +multiple times to run multiple commands. + =item B<--version> Print the version and exit. diff --git a/ranger/core/helper.py b/ranger/core/helper.py index 3f93e79c..c556b9bd 100644 --- a/ranger/core/helper.py +++ b/ranger/core/helper.py @@ -73,8 +73,9 @@ def parse_arguments(): help="List all files which are tagged with the given tag, default: *") parser.add_option('--profile', action='store_true', help="Print statistics of CPU usage on exit.") - parser.add_option('--cmd', type='string', metavar='COMMAND', - help="COMMAND will be executed after ranger has initialized") + parser.add_option('--cmd', action='append', type='string', metavar='COMMAND', + help="Execute COMMAND after the configuration has been read. " + "Use this option multiple times to run multiple commands.") options, positional = parser.parse_args() arg = OpenStruct(options.__dict__, targets=positional) diff --git a/ranger/core/main.py b/ranger/core/main.py index f80ee2d6..a17c6863 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -123,7 +123,8 @@ def main(): fm.ui.initialize() if arg.cmd: - fm.execute_console(arg.cmd) + for command in arg.cmd: + fm.execute_console(command) if ranger.arg.profile: import cProfile -- cgit 1.4.1-2-gfad0 From ac6df7b5e5b60b789d9262a92942a72c6192a265 Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 12 Feb 2012 17:18:00 +0100 Subject: core.main: fixed crash when using ranger as file opener --- ranger/core/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ranger/core/main.py b/ranger/core/main.py index a17c6863..4bb4c48a 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -80,7 +80,8 @@ def main(): print(string) from ranger.core.runner import Runner from ranger.fsobject import File - runner = Runner(logfunc=print_function) + fm = FM() + runner = Runner(logfunc=print_function, fm=fm) load_apps(runner, arg.clean) runner(files=[File(target)], mode=arg.mode, flags=arg.flags) return 1 if arg.fail_unless_cd else 0 -- cgit 1.4.1-2-gfad0 From 824104ae627f276eee8ec88522c873a6b76c5ecc Mon Sep 17 00:00:00 2001 From: hut Date: Sun, 12 Feb 2012 17:21:47 +0100 Subject: ranger.1: Added entry for --list-tagged-files --- doc/ranger.1 | 4 ++++ doc/ranger.pod | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/doc/ranger.1 b/doc/ranger.1 index e58008e0..60731bbc 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -191,6 +191,10 @@ directory. Existing ones will not be overwritten. Possible values: \fIall\fR, List common keys which are not bound to any action in the \*(L"browser\*(R" context. This list is not complete, you can bind any key that is supported by curses: use the key code returned by \f(CW\*(C`getch()\*(C'\fR. +.IP "\fB\-\-list\-tagged\-files\fR=\fItag\fR" 14 +.IX Item "--list-tagged-files=tag" +List all files which are tagged with the given tag. Note: Tags are single +characters. The default tag is \*(L"*\*(R" .IP "\fB\-\-fail\-unless\-cd\fR" 14 .IX Item "--fail-unless-cd" Return the exit code 1 if ranger is used to run a file instead of used for file diff --git a/doc/ranger.pod b/doc/ranger.pod index 0799f35d..926a8de0 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -83,6 +83,11 @@ List common keys which are not bound to any action in the "browser" context. This list is not complete, you can bind any key that is supported by curses: use the key code returned by C. +=item B<--list-tagged-files>=I + +List all files which are tagged with the given tag. Note: Tags are single +characters. The default tag is "*" + =item B<--fail-unless-cd> Return the exit code 1 if ranger is used to run a file instead of used for file -- cgit 1.4.1-2-gfad0 From ec18cfafc2f85e4dbc4ab2d61588395a59352443 Mon Sep 17 00:00:00 2001 From: Serge Broslavsky Date: Tue, 14 Feb 2012 11:55:18 +0200 Subject: Feature: pasting hardlinked trees This feature is useful when there is a need to create a directory tree, which is similar to an existing one while keeping all the files in this new tree hardlinked to those existing ones. Such functionality might be useful when rearranging a huge directory tree (or a set of directory trees) into some new layout. This operation is done synchronously (i.e. it doesn't use CommandLoader to put the ongoing operation into the task queue). Target end of the operation is handled the following way: 1. If target directory with the same name exists - it gets used. 2. If target folder does not exist - it is created with the same mode bits as corresponding source one. 3. If target file with the same name exists and is a hardlink of the source file already - it is getting left as it is. If it's not a hardlink of the source file - a new hardlink is created in a form of _, _1, ..., etc. 4. If target file does not exist - a hardlink is created. --- ranger/core/actions.py | 26 ++++++++++++++++++++++++-- ranger/defaults/rc.conf | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/ranger/core/actions.py b/ranger/core/actions.py index 8106a89b..4e72de77 100644 --- a/ranger/core/actions.py +++ b/ranger/core/actions.py @@ -19,8 +19,8 @@ import re import shutil import string import tempfile -from os.path import join, isdir, realpath -from os import link, symlink, getcwd +from os.path import join, isdir, realpath, exists +from os import link, symlink, getcwd, listdir, stat from inspect import cleandoc import ranger @@ -1003,6 +1003,28 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware): except Exception as x: self.notify(x) + def paste_hardlinked_subtree(self): + for f in self.env.copy: + try: + target_path = join(getcwd(), f.basename) + self._recurse_hardlinked_tree(f.path, target_path) + except Exception as x: + self.notify(x) + + def _recurse_hardlinked_tree(self, source_path, target_path): + if isdir(source_path): + if not exists(target_path): + os.mkdir(target_path, stat(source_path).st_mode) + for item in listdir(source_path): + self._recurse_hardlinked_tree( + join(source_path, item), + join(target_path, item)) + else: + if not exists(target_path) \ + or stat(source_path).st_ino != stat(target_path).st_ino: + link(source_path, + next_available_filename(target_path)) + def paste(self, overwrite=False): """Paste the selected items into the current directory""" copied_files = tuple(self.env.copy) diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf index 1ecbc398..77ffa5c3 100644 --- a/ranger/defaults/rc.conf +++ b/ranger/defaults/rc.conf @@ -147,6 +147,7 @@ map po paste overwrite=True map pl paste_symlink relative=False map pL paste_symlink relative=True map phl paste_hardlink +map pht paste_hardlinked_subtree map dd cut map ud uncut -- cgit 1.4.1-2-gfad0 From 9ff49bf6b1004126b528b73c1d7822ce99581410 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 21 Feb 2012 14:48:00 +0100 Subject: ranger.1: fix vim script "RangerChooser" when no file is opened https://savannah.nongnu.org/bugs/?35556 --- doc/ranger.1 | 4 ++-- doc/ranger.pod | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ranger.1 b/doc/ranger.1 index 60731bbc..fe2d2d1e 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.5.2" "02/12/2012" "ranger manual" +.TH RANGER 1 "ranger-1.5.2" "02/21/2012" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -906,7 +906,7 @@ opening in your current vim session. .PP .Vb 9 \& fun! RangerChooser() -\& silent !ranger \-\-choosefile=/tmp/chosenfile \`[ \-z \*(Aq%\*(Aq ] && echo \-n . || dirname %\` +\& exec "silent !ranger \-\-choosefile=/tmp/chosenfile " . expand("%:p:h") \& if filereadable(\*(Aq/tmp/chosenfile\*(Aq) \& exec \*(Aqedit \*(Aq . system(\*(Aqcat /tmp/chosenfile\*(Aq) \& call system(\*(Aqrm /tmp/chosenfile\*(Aq) diff --git a/doc/ranger.pod b/doc/ranger.pod index 926a8de0..f438cae7 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -1004,7 +1004,7 @@ This is a vim function which allows you to use ranger to select a file for opening in your current vim session. fun! RangerChooser() - silent !ranger --choosefile=/tmp/chosenfile `[ -z '%' ] && echo -n . || dirname %` + exec "silent !ranger --choosefile=/tmp/chosenfile " . expand("%:p:h") if filereadable('/tmp/chosenfile') exec 'edit ' . system('cat /tmp/chosenfile') call system('rm /tmp/chosenfile') -- cgit 1.4.1-2-gfad0 From 9400aa042cee4230690155f61bc1f6de11bb9c06 Mon Sep 17 00:00:00 2001 From: hut Date: Fri, 24 Feb 2012 02:03:25 +0100 Subject: gui.ui: Added "rxvt-unicode-256color" to TERMINALS_WITH_TITLE --- ranger/gui/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py index 5599190d..e6c7d065 100644 --- a/ranger/gui/ui.py +++ b/ranger/gui/ui.py @@ -23,8 +23,8 @@ from .mouse_event import MouseEvent from ranger.ext.keybinding_parser import ALT_KEY TERMINALS_WITH_TITLE = ("xterm", "xterm-256color", "rxvt", - "rxvt-256color", "rxvt-unicode", "aterm", "Eterm", - "screen", "screen-256color") + "rxvt-256color", "rxvt-unicode", "rxvt-unicode-256color", + "aterm", "Eterm", "screen", "screen-256color") MOUSEMASK = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION -- cgit 1.4.1-2-gfad0 From f16bbc20854a051ddedf6ab43cffd89ddbcef27f Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 5 Mar 2012 11:11:30 +0100 Subject: Removed unnecessary spaces --- doc/ranger.1 | 4 ++-- doc/ranger.pod | 2 +- ranger/defaults/commands.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/ranger.1 b/doc/ranger.1 index fe2d2d1e..823b31c7 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "RANGER 1" -.TH RANGER 1 "ranger-1.5.2" "02/21/2012" "ranger manual" +.TH RANGER 1 "ranger-1.5.2" "03/05/2012" "ranger manual" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -296,7 +296,7 @@ used in the commands :open_with (key \*(L"r\*(R") and :shell (key \*(L"!\*(R"). \& p Redirect output to the pager \& w Wait for an Enter\-press when the process is done \& c Run the current file only, instead of the selection -\& r Run application with root privilege (requires sudo) +\& r Run application with root privilege (requires sudo) \& t Run application in a new terminal window .Ve .PP diff --git a/doc/ranger.pod b/doc/ranger.pod index f438cae7..e8c45826 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -200,7 +200,7 @@ used in the commands :open_with (key "r") and :shell (key "!"). p Redirect output to the pager w Wait for an Enter-press when the process is done c Run the current file only, instead of the selection - r Run application with root privilege (requires sudo) + r Run application with root privilege (requires sudo) t Run application in a new terminal window By default, all the flags are off unless specified otherwise in the F diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py index d1f4cf16..a89dd0f7 100644 --- a/ranger/defaults/commands.py +++ b/ranger/defaults/commands.py @@ -836,10 +836,10 @@ class relink(Command): if not cf.is_link: return self.fm.notify('%s is not a symlink!' % cf.basename, bad=True) - + if new_path == os.readlink(cf.path): return - + try: os.remove(cf.path) os.symlink(new_path, cf.path) -- cgit 1.4.1-2-gfad0 From a3d5f44b1c1a8b7ef7e56c33f03ce91cc38117a6 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 5 Mar 2012 11:12:44 +0100 Subject: Added "sudo" optional dependency to README --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index 9b0ed735..bc67c16b 100644 --- a/README +++ b/README @@ -56,6 +56,7 @@ Dependencies Optional: * The "file" program for determining file types * The python module "chardet", in case of encoding detection problems +* "sudo" to use the "run as root"-feature Optional, for enhanced file previews (with "scope.sh"): * img2txt (from caca-utils) for previewing images -- cgit 1.4.1-2-gfad0 From 6b1666df4664482644efb270f4e0215b469c8ffc Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 5 Mar 2012 11:12:58 +0100 Subject: Catch a trivial ncurses exception --- ranger/gui/widgets/browserview.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py index d386d389..ea04c1e0 100644 --- a/ranger/gui/widgets/browserview.py +++ b/ranger/gui/widgets/browserview.py @@ -215,7 +215,10 @@ class BrowserView(Widget, DisplayableContainer): ystart = self.hei - hei self.addnstr(ystart - 1, 0, "key command".ljust(self.wid), self.wid) - self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE) + try: + self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE) + except: + pass whitespace = " " * self.wid i = ystart for key, cmd in hints: -- cgit 1.4.1-2-gfad0 From 23b7f16961a7b679dec16f6ee91401a6b8f6ca82 Mon Sep 17 00:00:00 2001 From: hut Date: Mon, 5 Mar 2012 11:27:16 +0100 Subject: Added $RANGER_LEVEL environment variable (see man page) --- doc/ranger.1 | 13 +++++++++++++ doc/ranger.pod | 13 +++++++++++++ ranger/core/main.py | 7 +++++++ 3 files changed, 33 insertions(+) diff --git a/doc/ranger.1 b/doc/ranger.1 index 823b31c7..433bb8a7 100644 --- a/doc/ranger.1 +++ b/doc/ranger.1 @@ -872,6 +872,11 @@ with T. To assign a named tag, type ". .SH "ENVIRONMENT" .IX Header "ENVIRONMENT" These environment variables have an effect on ranger: +.IP "\s-1RANGER_LEVEL\s0" 8 +.IX Item "RANGER_LEVEL" +Ranger sets this environment variable to \*(L"1\*(R" or increments it if it already +exists. External programs can determine whether they were spawned from ranger +by checking for this variable. .IP "\s-1EDITOR\s0" 8 .IX Item "EDITOR" Defines the editor to be used for the \*(L"E\*(R" key. Defaults to the first installed @@ -899,6 +904,14 @@ Using PYTHONOPTIMIZE=2 (like python \-OO) will additionally discard any docstrings. Using this will disable the key on commands. .SH "EXAMPLES" .IX Header "EXAMPLES" +.SS "\s-1BASH:\s0 Display that the shell spawned from ranger:" +.IX Subsection "BASH: Display that the shell spawned from ranger:" +By putting this in ~/.bashrc, \*(L"(in ranger) \*(R" will be displayed next to your +prompt to notify you that the shell spawned from ranger. +.PP +.Vb 1 +\& [ \-n "$RANGER_LEVEL" ] && PS1="$PS1"\*(Aq(in ranger) \*(Aq +.Ve .SS "\s-1VIM:\s0 File Chooser" .IX Subsection "VIM: File Chooser" This is a vim function which allows you to use ranger to select a file for diff --git a/doc/ranger.pod b/doc/ranger.pod index e8c45826..9f8b4f04 100644 --- a/doc/ranger.pod +++ b/doc/ranger.pod @@ -960,6 +960,12 @@ These environment variables have an effect on ranger: =over 8 +=item RANGER_LEVEL + +Ranger sets this environment variable to "1" or increments it if it already +exists. External programs can determine whether they were spawned from ranger +by checking for this variable. + =item EDITOR Defines the editor to be used for the "E" key. Defaults to the first installed @@ -998,6 +1004,13 @@ docstrings. Using this will disable the key on commands. =head1 EXAMPLES +=head2 BASH: Display that the shell spawned from ranger: + +By putting this in ~/.bashrc, "(in ranger) " will be displayed next to your +prompt to notify you that the shell spawned from ranger. + + [ -n "$RANGER_LEVEL" ] && PS1="$PS1"'(in ranger) ' + =head2 VIM: File Chooser This is a vim function which allows you to use ranger to select a file for diff --git a/ranger/core/main.py b/ranger/core/main.py index 4bb4c48a..b4629801 100644 --- a/ranger/core/main.py +++ b/ranger/core/main.py @@ -38,6 +38,13 @@ def main(): except: print("Warning: Unable to set locale. Expect encoding problems.") + # so that programs can know that ranger spawned them: + level = 'RANGER_LEVEL' + if level in os.environ and os.environ[level].isdigit(): + os.environ[level] = str(int(os.environ[level]) + 1) + else: + os.environ[level] = '1' + if not 'SHELL' in os.environ: os.environ['SHELL'] = 'bash' -- cgit 1.4.1-2-gfad0