summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS14
-rw-r--r--README.md5
-rw-r--r--doc/ranger.pod3
-rwxr-xr-xranger/config/commands.py3
-rw-r--r--ranger/config/rc.conf2
-rw-r--r--ranger/container/settings.py15
-rw-r--r--ranger/core/main.py12
-rw-r--r--ranger/core/tab.py4
-rwxr-xr-xranger/data/scope.sh3
-rw-r--r--ranger/ext/shell_escape.py2
-rw-r--r--ranger/ext/vcs/bzr.py22
-rw-r--r--ranger/ext/vcs/git.py87
-rw-r--r--ranger/ext/vcs/hg.py10
-rw-r--r--ranger/ext/vcs/svn.py25
-rw-r--r--ranger/ext/vcs/vcs.py11
-rw-r--r--ranger/gui/ansi.py2
-rw-r--r--ranger/gui/displayable.py6
-rw-r--r--ranger/gui/ui.py14
-rw-r--r--ranger/gui/widgets/statusbar.py2
-rw-r--r--ranger/gui/widgets/view_miller.py9
20 files changed, 145 insertions, 106 deletions
diff --git a/AUTHORS b/AUTHORS
index d3789119..75d988f9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -2,27 +2,27 @@ Copyright 2009-2016  Roman Zimbelmann <hut@hut.pm>
 Copyright 2010  David Barnett <davidbarnett2@gmail.com>
 Copyright 2010  Lucas de Vries <lucas@glacicle.org>
 Copyright 2010  Sitaram Chamarty <sitaram@atc.tcs.com>
-Copyright 2011-2012  Abdó Roig-Maranges <abdo.roig@gmail.com>
-Copyright 2011-2012  M Rawash <mrawash@gmail.com>
 Copyright 2011  David Pugnasse <david.pugnasse@gmail.com>
 Copyright 2011  ornicar <thibault.duplessis@gmail.com>
-Copyright 2012  joe <joebodo@gmail.com>
+Copyright 2011-2012  Abdó Roig-Maranges <abdo.roig@gmail.com>
+Copyright 2011-2012  M Rawash <mrawash@gmail.com>
 Copyright 2012  Serge Broslavsky <serge.broslavsky@gmail.com>
-Copyright 2013-2014  GermainZ <germanosz@gmail.com>
+Copyright 2012  joe <joebodo@gmail.com>
 Copyright 2013  Emanuel Guevel
 Copyright 2013  Joseph Tannhuber <sepp.tannhuber@yahoo.de>
+Copyright 2013-2014  GermainZ <germanosz@gmail.com>
 Copyright 2014  Célestin Matte <celestin.matte@gmail.com>
 Copyright 2014  Milan Svoboda <milan.svoboda@centrum.cz>
 Copyright 2014  rukai <rubickent@gmail.com>
 Copyright 2015  Alexander Buddenbrock <a.buddenbrock@ish.de>
-Copyright 2015  anekos <anekos@snca.net>
-Copyright 2015  bastorran
 Copyright 2015  Delisa Mason <iskanamagus@gmail.com>
 Copyright 2015  No Suck <admin@nosuck.org>
 Copyright 2015  Randy Nance <randynobx@gmail.com>
-Copyright 2015  Wojciech Siewierski <wojciech.siewierski@onet.pl>
 Copyright 2015  Ryan Burns <rdburns@gmail.com>
+Copyright 2015  anekos <anekos@snca.net>
+Copyright 2015  bastorran
 Copyright 2015  nfnty <git@nfnty.se>
+Copyright 2015-2016  Wojciech Siewierski <wojciech.siewierski@onet.pl>
 
 Ideally, all contributors of non-trivial code are named here to the extent that
 a name and e-mail address is available.  Please write a mail to hut@hut.pm if
diff --git a/README.md b/README.md
index b5fc03ea..5174f1c1 100644
--- a/README.md
+++ b/README.md
@@ -69,12 +69,13 @@ Optional:
 Optional, for enhanced file previews (with "scope.sh"):
 
 * img2txt (from caca-utils) for ASCII-art image previews
-* highlight for syntax highlighting of code
-* atool for previews of archives
+* highlight or pygmentize for syntax highlighting of code
+* atool, acat, bsdtar and/or unrar for previews of archives
 * lynx, w3m or elinks for previews of html pages
 * pdftotext for pdf previews
 * transmission-show for viewing bit-torrent information
 * mediainfo or exiftool for viewing information about media files
+* odt2txt for OpenDocument text files (odt, ods, odp and sxw)
 
 
 Installing
diff --git a/doc/ranger.pod b/doc/ranger.pod
index a69dc52d..6b0752bd 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -1063,7 +1063,7 @@ Examples:
 =item filter [I<string>]
 
 Displays only the files which contain the I<string> in their basename.  Running
-this command without any parameter will reset the fitler.
+this command without any parameter will reset the filter.
 
 This command is based on the I<scout> command and supports all of its options.
 
@@ -1220,6 +1220,7 @@ influence its behaviour:
  -m = mark the matching files after pressing enter
  -M = unmark the matching files after pressing enter
  -p = permanent filter: hide non-matching files after pressing enter
+ -r = interpret pattern as a regular expression pattern
  -s = smart case; like -i unless pattern contains upper case letters
  -t = apply filter and search pattern as you type
  -v = inverts the match
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index 4928ba26..18efbfc1 100755
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -195,7 +195,7 @@ class chain(Command):
     Calls multiple commands at once, separated by semicolons.
     """
     def execute(self):
-        for command in self.rest(1).split(";"):
+        for command in [s.strip() for s in self.rest(1).split(";")]:
             self.fm.execute_console(command)
 
 
@@ -1125,6 +1125,7 @@ class scout(Command):
     -m = mark the matching files after pressing enter
     -M = unmark the matching files after pressing enter
     -p = permanent filter: hide non-matching files after pressing enter
+    -r = interpret pattern as a regular expression pattern
     -s = smart case; like -i unless pattern contains upper case letters
     -t = apply filter and search pattern as you type
     -v = inverts the match
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf
index 560fbe4d..cc7f5007 100644
--- a/ranger/config/rc.conf
+++ b/ranger/config/rc.conf
@@ -259,7 +259,7 @@ map !  console shell%space
 map @  console -p6 shell  %%s
 map #  console shell -p%space
 map s  console shell%space
-map r  chain draw_possible_programs; console open_with%space
+map r  chain draw_possible_programs; console open_with%%space
 map f  console find%space
 map cd console cd%space
 
diff --git a/ranger/container/settings.py b/ranger/container/settings.py
index a7911251..a5d71874 100644
--- a/ranger/container/settings.py
+++ b/ranger/container/settings.py
@@ -8,6 +8,15 @@ from ranger.gui.colorscheme import _colorscheme_name_to_class
 import re
 import os.path
 
+# Use these priority constants to trigger events at specific points in time
+# during processing of the signals "setopt" and "setopt.<some_setting_name>"
+SIGNAL_PRIORITY_RAW        = 2.0  # signal.value will be raw
+SIGNAL_PRIORITY_SANITIZE   = 1.0  # (Internal) post-processing signal.value
+SIGNAL_PRIORITY_BETWEEN    = 0.6  # sanitized signal.value, old fm.settings.XYZ
+SIGNAL_PRIORITY_SYNC       = 0.2  # (Internal) updating fm.settings.XYZ
+SIGNAL_PRIORITY_AFTER_SYNC = 0.1  # after fm.settings.XYZ was updated
+
+
 ALLOWED_SETTINGS = {
     'automatically_count_files': bool,
     'autosave_bookmarks': bool,
@@ -97,9 +106,11 @@ class Settings(SignalDispatcher, FileManagerAware):
         self.__dict__['_settings'] = dict()
         for name in ALLOWED_SETTINGS:
             self.signal_bind('setopt.' + name,
-                    self._sanitize, priority=1.0)
+                    self._sanitize,
+                    priority=SIGNAL_PRIORITY_SANITIZE)
             self.signal_bind('setopt.' + name,
-                    self._raw_set_with_signal, priority=0.2)
+                    self._raw_set_with_signal,
+                    priority=SIGNAL_PRIORITY_SYNC)
 
     def _sanitize(self, signal):
         name, value = signal.setting, signal.value
diff --git a/ranger/core/main.py b/ranger/core/main.py
index 000ded68..118a7480 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -303,8 +303,16 @@ def load_settings(fm, clean):
             ranger.fm = fm
             for plugin in sorted(plugins):
                 try:
-                    module = __import__('plugins', fromlist=[plugin])
-                    fm.commands.load_commands_from_module(module)
+                    try:
+                        # importlib does not exist before python2.7.  It's
+                        # required for loading commands from plugins, so you
+                        # can't use that feature in python2.6.
+                        import importlib
+                    except ImportError:
+                        module = __import__('plugins', fromlist=[plugin])
+                    else:
+                        module = importlib.import_module('plugins.' + plugin)
+                        fm.commands.load_commands_from_module(module)
                     fm.log.append("Loaded plugin '%s'." % plugin)
                 except Exception as e:
                     fm.log.append("Error in plugin '%s'" % plugin)
diff --git a/ranger/core/tab.py b/ranger/core/tab.py
index 7b60a5b1..fa40a12b 100644
--- a/ranger/core/tab.py
+++ b/ranger/core/tab.py
@@ -5,6 +5,7 @@ import os
 import sys
 from os.path import abspath, normpath, join, expanduser, isdir
 
+from ranger.container import settings
 from ranger.container.history import History
 from ranger.core.shared import FileManagerAware, SettingsAware
 from ranger.ext.signals import SignalDispatcher
@@ -22,7 +23,8 @@ class Tab(FileManagerAware, SettingsAware):
         # NOTE: in the line below, weak=True works only in python3.  In python2,
         # weak references are not equal to the original object when tested with
         # "==", and this breaks _set_thisfile_from_signal and _on_tab_change.
-        self.fm.signal_bind('move', self._set_thisfile_from_signal, priority=0.1,
+        self.fm.signal_bind('move', self._set_thisfile_from_signal,
+                priority=settings.SIGNAL_PRIORITY_AFTER_SYNC,
                 weak=(sys.version_info[0] >= 3))
         self.fm.signal_bind('tab.change', self._on_tab_change,
                 weak=(sys.version_info[0] >= 3))
diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh
index 669d1e34..44fcec2b 100755
--- a/ranger/data/scope.sh
+++ b/ranger/data/scope.sh
@@ -80,6 +80,9 @@ case "$extension" in
     # BitTorrent Files
     torrent)
         try transmission-show "$path" && { dump | trim; exit 5; } || exit 1;;
+    # ODT Files
+    odt|ods|odp|sxw)
+        try odt2txt "$path" && { dump | trim; exit 5; } || exit 1;;
     # HTML Pages:
     htm|html|xhtml)
         try w3m    -dump "$path" && { dump | trim | fmt -s -w $width; exit 4; }
diff --git a/ranger/ext/shell_escape.py b/ranger/ext/shell_escape.py
index fe542084..d57ff339 100644
--- a/ranger/ext/shell_escape.py
+++ b/ranger/ext/shell_escape.py
@@ -3,7 +3,7 @@
 
 """Functions to escape metacharacters of arguments for shell commands."""
 
-META_CHARS = (' ', "'", '"', '`', '&', '|', ';',
+META_CHARS = (' ', "'", '"', '`', '&', '|', ';', '#',
         '$', '!', '(', ')', '[', ']', '<', '>', '\t')
 UNESCAPABLE = set(map(chr, list(range(9)) + list(range(10, 32))
         + list(range(127, 256))))
diff --git a/ranger/ext/vcs/bzr.py b/ranger/ext/vcs/bzr.py
index 5decc8b1..d0db1faf 100644
--- a/ranger/ext/vcs/bzr.py
+++ b/ranger/ext/vcs/bzr.py
@@ -25,7 +25,7 @@ class Bzr(Vcs):
     def _remote_url(self):
         """Remote url"""
         try:
-            return self._run(['config', 'parent_location']).rstrip('\n') or None
+            return self._run(['config', 'parent_location']) or None
         except VcsError:
             return None
 
@@ -87,29 +87,29 @@ class Bzr(Vcs):
         statuses = set()
 
         # Paths with status
-        output = self._run(['status', '--short', '--no-classify']).rstrip('\n')
-        if not output:
+        lines = self._run(['status', '--short', '--no-classify']).split('\n')
+        if not lines:
             return 'sync'
-        for line in output.split('\n'):
+        for line in lines:
             statuses.add(self._status_translate(line[:2]))
 
         for status in self.DIRSTATUSES:
             if status in statuses:
                 return status
+
         return 'sync'
 
     def data_status_subpaths(self):
         statuses = {}
 
         # Ignored
-        output = self._run(['ls', '--null', '--ignored']).rstrip('\x00')
-        if output:
-            for path in output.split('\x00'):
-                statuses[path] = 'ignored'
+        paths = self._run(['ls', '--null', '--ignored']).split('\0')[:-1]
+        for path in paths:
+            statuses[path] = 'ignored'
 
         # Paths with status
-        output = self._run(['status', '--short', '--no-classify']).rstrip('\n')
-        for line in output.split('\n'):
+        lines = self._run(['status', '--short', '--no-classify']).split('\n')
+        for line in lines:
             statuses[os.path.normpath(line[4:])] = self._status_translate(line[:2])
 
         return statuses
@@ -121,7 +121,7 @@ class Bzr(Vcs):
 
     def data_branch(self):
         try:
-            return self._run(['nick']).rstrip('\n') or None
+            return self._run(['nick']) or None
         except VcsError:
             return None
 
diff --git a/ranger/ext/vcs/git.py b/ranger/ext/vcs/git.py
index 8f4d9ff8..06e066d2 100644
--- a/ranger/ext/vcs/git.py
+++ b/ranger/ext/vcs/git.py
@@ -4,13 +4,19 @@
 """Git module"""
 
 from datetime import datetime
-import json
 import os
 import re
+import unicodedata
 
 from .vcs import Vcs, VcsError
 
 
+def string_control_replace(string, replacement):
+    """Replace all unicode control characters with replacement"""
+    return ''.join(
+        (replacement if unicodedata.category(char)[0] == 'C' else char for char in string))
+
+
 class Git(Vcs):
     """VCS implementation for Git"""
     _status_translations = (
@@ -30,26 +36,17 @@ class Git(Vcs):
 
     def _head_ref(self):
         """Returns HEAD reference"""
-        return self._run(['symbolic-ref', self.HEAD]).rstrip('\n') or None
+        return self._run(['symbolic-ref', self.HEAD]) or None
 
     def _remote_ref(self, ref):
         """Returns remote reference associated to given ref"""
         if ref is None:
             return None
-        return self._run(['for-each-ref', '--format=%(upstream)', ref]).rstrip('\n') or None
+        return self._run(['for-each-ref', '--format=%(upstream)', ref]) or None
 
     def _log(self, refspec=None, maxres=None, filelist=None):
         """Returns an array of dicts containing revision info for refspec"""
-        args = [
-            '--no-pager', 'log',
-            '--pretty={'
-            '%x00short%x00:%x00%h%x00,'
-            '%x00revid%x00:%x00%H%x00,'
-            '%x00author%x00:%x00%an <%ae>%x00,'
-            '%x00date%x00:%ct,'
-            '%x00summary%x00:%x00%s%x00'
-            '}'
-        ]
+        args = ['--no-pager', 'log', '--pretty=%h%x00%H%x00%an <%ae>%x00%ct%x00%s%x00%x00']
         if refspec:
             args += ['-1', refspec]
         elif maxres:
@@ -58,18 +55,22 @@ class Git(Vcs):
             args += ['--'] + filelist
 
         try:
-            output = self._run(args).rstrip('\n')
+            output = self._run(args)
         except VcsError:
             return None
         if not output:
             return None
 
         log = []
-        for line in output\
-                .replace('\\', '\\\\').replace('"', '\\"').replace('\x00', '"').split('\n'):
-            line = json.loads(line)
-            line['date'] = datetime.fromtimestamp(line['date'])
-            log.append(line)
+        for line in output[:-2].split('\0\0\n'):
+            commit_hash_abbrev, commit_hash, author, timestamp, subject = line.split('\0')
+            log.append({
+                'short': commit_hash_abbrev,
+                'revid': commit_hash,
+                'author': string_control_replace(author, ' '),
+                'date': datetime.fromtimestamp(int(timestamp)),
+                'summary': string_control_replace(subject, ' '),
+            })
         return log
 
     def _status_translate(self, code):
@@ -100,10 +101,10 @@ class Git(Vcs):
 
         # Paths with status
         skip = False
-        output = self._run(['status', '--porcelain', '-z']).rstrip('\x00')
-        if not output:
+        lines = self._run(['status', '--porcelain', '-z']).split('\0')[:-1]
+        if not lines:
             return 'sync'
-        for line in output.split('\x00'):
+        for line in lines:
             if skip:
                 skip = False
                 continue
@@ -114,39 +115,37 @@ class Git(Vcs):
         for status in self.DIRSTATUSES:
             if status in statuses:
                 return status
+
         return 'sync'
 
     def data_status_subpaths(self):
         statuses = {}
 
         # Ignored directories
-        output = self._run([
+        paths = self._run([
             'ls-files', '-z', '--others', '--directory', '--ignored', '--exclude-standard'
-        ]).rstrip('\x00')
-        if output:
-            for path in output.split('\x00'):
-                if path.endswith('/'):
-                    statuses[os.path.normpath(path)] = 'ignored'
+        ]).split('\0')[:-1]
+        for path in paths:
+            if path.endswith('/'):
+                statuses[os.path.normpath(path)] = 'ignored'
 
         # Empty directories
-        output = self._run(
-            ['ls-files', '-z', '--others', '--directory', '--exclude-standard']).rstrip('\x00')
-        if output:
-            for path in output.split('\x00'):
-                if path.endswith('/'):
-                    statuses[os.path.normpath(path)] = 'none'
+        paths = self._run(
+            ['ls-files', '-z', '--others', '--directory', '--exclude-standard']).split('\0')[:-1]
+        for path in paths:
+            if path.endswith('/'):
+                statuses[os.path.normpath(path)] = 'none'
 
         # Paths with status
-        output = self._run(['status', '--porcelain', '-z', '--ignored']).rstrip('\x00')
-        if output:
-            skip = False
-            for line in output.split('\x00'):
-                if skip:
-                    skip = False
-                    continue
-                statuses[os.path.normpath(line[3:])] = self._status_translate(line[:2])
-                if line.startswith('R'):
-                    skip = True
+        lines = self._run(['status', '--porcelain', '-z', '--ignored']).split('\0')[:-1]
+        skip = False
+        for line in lines:
+            if skip:
+                skip = False
+                continue
+            statuses[os.path.normpath(line[3:])] = self._status_translate(line[:2])
+            if line.startswith('R'):
+                skip = True
 
         return statuses
 
diff --git a/ranger/ext/vcs/hg.py b/ranger/ext/vcs/hg.py
index cf15e35e..21460975 100644
--- a/ranger/ext/vcs/hg.py
+++ b/ranger/ext/vcs/hg.py
@@ -36,7 +36,7 @@ class Hg(Vcs):
             args += ['--'] + filelist
 
         try:
-            output = self._run(args).rstrip('\n')
+            output = self._run(args)
         except VcsError:
             return None
         if not output:
@@ -56,13 +56,13 @@ class Hg(Vcs):
     def _remote_url(self):
         """Remote url"""
         try:
-            return self._run(['showconfig', 'paths.default']).rstrip('\n') or None
+            return self._run(['showconfig', 'paths.default']) or None
         except VcsError:
             return None
 
     def _status_translate(self, code):
         """Translate status code"""
-        for code_x, status in self._status_translations:  # pylint: disable=invalid-name
+        for code_x, status in self._status_translations:
             if code in code_x:
                 return status
         return 'unknown'
@@ -80,7 +80,7 @@ class Hg(Vcs):
         if filelist:
             args += filelist
         else:
-            args += self.rootvcs.status_subpaths.keys()
+            args += self.rootvcs.status_subpaths.keys()  # pylint: disable=no-member
         self._run(args, catchout=False)
 
     # Data interface
@@ -117,7 +117,7 @@ class Hg(Vcs):
         return 'unknown'
 
     def data_branch(self):
-        return self._run(['branch']).rstrip('\n') or None
+        return self._run(['branch']) or None
 
     def data_info(self, rev=None):
         if rev is None:
diff --git a/ranger/ext/vcs/svn.py b/ranger/ext/vcs/svn.py
index 1813f857..1f09cd52 100644
--- a/ranger/ext/vcs/svn.py
+++ b/ranger/ext/vcs/svn.py
@@ -36,7 +36,7 @@ class SVN(Vcs):
             args += ['--'] + filelist
 
         try:
-            output = self._run(args).rstrip('\n')
+            output = self._run(args)
         except VcsError:
             return None
         if not output:
@@ -66,7 +66,7 @@ class SVN(Vcs):
     def _remote_url(self):
         """Remote url"""
         try:
-            output = self._run(['info', '--xml']).rstrip('\n')
+            output = self._run(['info', '--xml'])
         except VcsError:
             return None
         if not output:
@@ -86,7 +86,7 @@ class SVN(Vcs):
         if filelist:
             args += filelist
         else:
-            args += self.rootvcs.status_subpaths.keys()
+            args += self.rootvcs.status_subpaths.keys()  # pylint: disable=no-member
         self._run(args, catchout=False)
 
     # Data Interface
@@ -95,10 +95,10 @@ class SVN(Vcs):
         statuses = set()
 
         # Paths with status
-        output = self._run(['status']).rstrip('\n')
-        if not output:
+        lines = self._run(['status']).split('\n')
+        if not lines:
             return 'sync'
-        for line in output.split('\n'):
+        for line in lines:
             code = line[0]
             if code == ' ':
                 continue
@@ -113,13 +113,12 @@ class SVN(Vcs):
         statuses = {}
 
         # Paths with status
-        output = self._run(['status']).rstrip('\n')
-        if output:
-            for line in output.split('\n'):
-                code, path = line[0], line[8:]
-                if code == ' ':
-                    continue
-                statuses[os.path.normpath(path)] = self._status_translate(code)
+        lines = self._run(['status']).split('\n')
+        for line in lines:
+            code, path = line[0], line[8:]
+            if code == ' ':
+                continue
+            statuses[os.path.normpath(path)] = self._status_translate(code)
 
         return statuses
 
diff --git a/ranger/ext/vcs/vcs.py b/ranger/ext/vcs/vcs.py
index 487c9405..9c7be653 100644
--- a/ranger/ext/vcs/vcs.py
+++ b/ranger/ext/vcs/vcs.py
@@ -109,7 +109,8 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
 
     # Generic
 
-    def _run(self, args, path=None, catchout=True, retbytes=False):
+    def _run(self, args, path=None,  # pylint: disable=too-many-arguments
+             catchout=True, retbytes=False, rstrip_newline=True):
         """Run a command"""
         cmd = [self.repotype] + args
         if path is None:
@@ -119,7 +120,13 @@ class Vcs(object):  # pylint: disable=too-many-instance-attributes
             try:
                 if catchout:
                     output = subprocess.check_output(cmd, cwd=path, stderr=devnull)
-                    return output if retbytes else output.decode('UTF-8')
+                    if retbytes:
+                        return output
+                    else:
+                        output = output.decode('UTF-8')
+                        if rstrip_newline and output.endswith('\n'):
+                            return output[:-1]
+                        return output
                 else:
                     subprocess.check_call(cmd, cwd=path, stdout=devnull, stderr=devnull)
             except (subprocess.CalledProcessError, FileNotFoundError):
diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py
index e761c379..091406e9 100644
--- a/ranger/gui/ansi.py
+++ b/ranger/gui/ansi.py
@@ -8,7 +8,7 @@ from ranger.gui import color
 import re
 
 ansi_re = re.compile('(\x1b' + r'\[\d*(?:;\d+)*?[a-zA-Z])')
-codesplit_re = re.compile('38;5;(\d+);|48;5;(\d+);|(\d*);')
+codesplit_re = re.compile(r'38;5;(\d+);|48;5;(\d+);|(\d*);')
 reset = '\x1b[0m'
 
 
diff --git a/ranger/gui/displayable.py b/ranger/gui/displayable.py
index 7b5aa954..62eb5300 100644
--- a/ranger/gui/displayable.py
+++ b/ranger/gui/displayable.py
@@ -211,6 +211,7 @@ class DisplayableContainer(Displayable):
     New methods:
 
     add_child(object) -- add the object to the container.
+    replace_child(old_obj, new_obj) -- replaces old object with new object.
     remove_child(object) -- remove the object from the container.
 
     New attributes:
@@ -290,6 +291,11 @@ class DisplayableContainer(Displayable):
         self.container.append(obj)
         obj.parent = self
 
+    def replace_child(self, old_obj, new_obj):
+        """Replace the old object with the new instance in the container."""
+        self.container[self.container.index(old_obj)] = new_obj
+        new_obj.parent = self
+
     def remove_child(self, obj):
         """Remove the object from the container."""
         try:
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 4c302e00..f10bc0f2 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -437,18 +437,18 @@ class UI(DisplayableContainer):
         if value in self.ALLOWED_VIEWMODES:
             if self._viewmode != value:
                 self._viewmode = value
-                resize = False
+                new_browser = self._viewmode_to_class(value)(self.win)
+
                 if hasattr(self, 'browser'):
                     old_size = self.browser.y, self.browser.x, self.browser.hei, self.browser.wid
-                    self.remove_child(self.browser)
+                    self.replace_child(self.browser, new_browser)
                     self.browser.destroy()
-                    resize = True
+                    new_browser.resize(*old_size)
+                else:
+                    self.add_child(new_browser)
 
-                self.browser = self._viewmode_to_class(value)(self.win)
+                self.browser = new_browser
                 self.redraw_window()
-                self.add_child(self.browser)
-                if resize:
-                    self.browser.resize(*old_size)
         else:
             raise ValueError("Attempting to set invalid viewmode `%s`, should "
                     "be one of `%s`." % (value, "`, `".join(self.ALLOWED_VIEWMODES)))
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index 4eb06692..0828c372 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -258,7 +258,7 @@ class StatusBar(Widget):
             right.add("', ", "space")
 
         if target.marked_items:
-            if len(target.marked_items) == len(target.files):
+            if len(target.marked_items) == target.size:
                 right.add(human_readable(target.disk_usage, separator=''))
             else:
                 sumsize = sum(f.size for f in target.marked_items if not
diff --git a/ranger/gui/widgets/view_miller.py b/ranger/gui/widgets/view_miller.py
index 732c6170..42013fe9 100644
--- a/ranger/gui/widgets/view_miller.py
+++ b/ranger/gui/widgets/view_miller.py
@@ -5,6 +5,7 @@
 
 import curses
 import _curses
+from ranger.container import settings
 from ranger.ext.signals import Signal
 from .browsercolumn import BrowserColumn
 from .pager import Pager
@@ -35,7 +36,8 @@ class ViewMiller(ViewBase):
                     self._request_clear_if_has_borders, weak=True)
 
         self.settings.signal_bind('setopt.column_ratios', self.request_clear)
-        self.settings.signal_bind('setopt.column_ratios', self.rebuild)
+        self.settings.signal_bind('setopt.column_ratios', self.rebuild,
+                priority=settings.SIGNAL_PRIORITY_AFTER_SYNC)
 
         self.old_draw_borders = self.settings.draw_borders
 
@@ -254,9 +256,8 @@ class ViewMiller(ViewBase):
 
         # Show the preview column when it has a preview but has
         # been hidden (e.g. because of padding_right = False)
-        if not self.pager.visible and not self.columns[-1].visible and \
-        self.columns[-1].target and self.columns[-1].target.is_directory \
-        or self.columns[-1].has_preview() and not self.pager.visible:
+        if not self.columns[-1].visible and self.columns[-1].has_preview() \
+        and not self.pager.visible:
             self.columns[-1].visible = True
 
         if self.preview and self.is_collapsed != self._collapse():