about summary refs log tree commit diff stats
path: root/ranger/gui
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2013-02-10 03:28:06 +0100
committerhut <hut@lavabit.com>2013-02-10 03:35:27 +0100
commitd1a1173ddc315f21a3d468f43ac55aa43d31883d (patch)
tree10d728b37294856eb21e6e962089ac38507d868c /ranger/gui
parent184e84284d2f4e48631a4b94b87ba76126470206 (diff)
downloadranger-d1a1173ddc315f21a3d468f43ac55aa43d31883d.tar.gz
replaced tabs with 4 spaces in all python files
PEP 8 (Style Guide for Python Code) suggests the use of 4 spaces:
http://www.python.org/dev/peps/pep-0008/#indentation

If you need to use tools like "git blame", you can use the -w option to
ignore this commit entirely.  Patches will continue to work if you
substitute tabs with 4 spaces everywhere except in the Makefile.
Diffstat (limited to 'ranger/gui')
-rw-r--r--ranger/gui/ansi.py282
-rw-r--r--ranger/gui/bar.py242
-rw-r--r--ranger/gui/color.py26
-rw-r--r--ranger/gui/colorscheme.py196
-rw-r--r--ranger/gui/context.py40
-rw-r--r--ranger/gui/curses_shortcuts.py110
-rw-r--r--ranger/gui/displayable.py612
-rw-r--r--ranger/gui/mouse_event.py100
-rw-r--r--ranger/gui/ui.py710
-rw-r--r--ranger/gui/widgets/__init__.py8
-rw-r--r--ranger/gui/widgets/browsercolumn.py758
-rw-r--r--ranger/gui/widgets/browserview.py662
-rw-r--r--ranger/gui/widgets/console.py838
-rw-r--r--ranger/gui/widgets/pager.py398
-rw-r--r--ranger/gui/widgets/statusbar.py546
-rw-r--r--ranger/gui/widgets/taskview.py168
-rw-r--r--ranger/gui/widgets/titlebar.py278
17 files changed, 2987 insertions, 2987 deletions
diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py
index 1b693c7c..7019c6fb 100644
--- a/ranger/gui/ansi.py
+++ b/ranger/gui/ansi.py
@@ -14,154 +14,154 @@ codesplit_re = re.compile('38;5;(\d+);|48;5;(\d+);|(\d*);')
 reset = '\x1b[0m'
 
 def split_ansi_from_text(ansi_text):
-	return ansi_re.split(ansi_text)
+    return ansi_re.split(ansi_text)
 
 # For information on the ANSI codes see
 # githttp://en.wikipedia.org/wiki/ANSI_escape_code
 def text_with_fg_bg_attr(ansi_text):
-	fg, bg, attr = -1, -1, 0
-	for chunk in split_ansi_from_text(ansi_text):
-		if chunk and chunk[0] == '\x1b':
-			if chunk[-1] != 'm':
-				continue
-			match = re.match(r'^.\[(.*).$', chunk)
-			if not match:
-				# XXX I have no test case to determine what should happen here
-				continue
-			attr_args = match.group(1)
-
-			# Convert arguments to attributes/colors
-			for x256fg, x256bg, arg in codesplit_re.findall(attr_args + ';'):
-				# first handle xterm256 codes
-				try:
-					if len(x256fg) > 0:           # xterm256 foreground
-						fg = int(x256fg)
-						continue
-					elif len(x256bg) > 0:         # xterm256 background
-						bg = int(x256bg)
-						continue
-					elif len(arg) > 0:            # usual ansi code
-						n = int(arg)
-					else:                         # empty code means reset
-						n = 0
-				except:
-					continue
-
-				if n == 0:                        # reset colors and attributes
-					fg, bg, attr = -1, -1, 0
-
-				elif n == 1:                      # enable attribute
-					attr |= color.bold
-				elif n == 4:
-					attr |= color.underline
-				elif n == 5:
-					attr |= color.blink
-				elif n == 7:
-					attr |= color.reverse
-				elif n == 8:
-					attr |= color.invisible
-
-				elif n == 22:                     # disable attribute
-					attr &= not color.bold
-				elif n == 24:
-					attr &= not color.underline
-				elif n == 25:
-					attr &= not color.blink
-				elif n == 27:
-					attr &= not color.reverse
-				elif n == 28:
-					attr &= not color.invisible
-
-				elif n >= 30 and n <= 37:         # 8 ansi foreground and background colors
-					fg = n - 30
-				elif n == 39:
-					fg = -1
-				elif n >= 40 and n <= 47:
-					bg = n - 40
-				elif n == 49:
-					bg = -1
-
-				elif n >= 90 and n <= 97:         # 8 aixterm high intensity colors (light but not bold)
-					fg = n - 90 + 8
-				elif n == 99:
-					fg = -1
-				elif n >= 100 and n <= 107:
-					bg = n - 100 + 8
-				elif n == 109:
-					bg = -1
-
-			yield (fg, bg, attr)
-
-		else:
-			yield chunk
+    fg, bg, attr = -1, -1, 0
+    for chunk in split_ansi_from_text(ansi_text):
+        if chunk and chunk[0] == '\x1b':
+            if chunk[-1] != 'm':
+                continue
+            match = re.match(r'^.\[(.*).$', chunk)
+            if not match:
+                # XXX I have no test case to determine what should happen here
+                continue
+            attr_args = match.group(1)
+
+            # Convert arguments to attributes/colors
+            for x256fg, x256bg, arg in codesplit_re.findall(attr_args + ';'):
+                # first handle xterm256 codes
+                try:
+                    if len(x256fg) > 0:           # xterm256 foreground
+                        fg = int(x256fg)
+                        continue
+                    elif len(x256bg) > 0:         # xterm256 background
+                        bg = int(x256bg)
+                        continue
+                    elif len(arg) > 0:            # usual ansi code
+                        n = int(arg)
+                    else:                         # empty code means reset
+                        n = 0
+                except:
+                    continue
+
+                if n == 0:                        # reset colors and attributes
+                    fg, bg, attr = -1, -1, 0
+
+                elif n == 1:                      # enable attribute
+                    attr |= color.bold
+                elif n == 4:
+                    attr |= color.underline
+                elif n == 5:
+                    attr |= color.blink
+                elif n == 7:
+                    attr |= color.reverse
+                elif n == 8:
+                    attr |= color.invisible
+
+                elif n == 22:                     # disable attribute
+                    attr &= not color.bold
+                elif n == 24:
+                    attr &= not color.underline
+                elif n == 25:
+                    attr &= not color.blink
+                elif n == 27:
+                    attr &= not color.reverse
+                elif n == 28:
+                    attr &= not color.invisible
+
+                elif n >= 30 and n <= 37:         # 8 ansi foreground and background colors
+                    fg = n - 30
+                elif n == 39:
+                    fg = -1
+                elif n >= 40 and n <= 47:
+                    bg = n - 40
+                elif n == 49:
+                    bg = -1
+
+                elif n >= 90 and n <= 97:         # 8 aixterm high intensity colors (light but not bold)
+                    fg = n - 90 + 8
+                elif n == 99:
+                    fg = -1
+                elif n >= 100 and n <= 107:
+                    bg = n - 100 + 8
+                elif n == 109:
+                    bg = -1
+
+            yield (fg, bg, attr)
+
+        else:
+            yield chunk
 
 def char_len(ansi_text):
-	"""
-	Count the number of visible characters.
-
-	>>> char_len("\x1b[0;30;40mX\x1b[0m")
-	1
-	>>> char_len("\x1b[0;30;40mXY\x1b[0m")
-	2
-	>>> char_len("\x1b[0;30;40mX\x1b[0mY")
-	2
-	>>> char_len("hello")
-	5
-	>>> char_len("")
-	0
-	"""
-	return len(ansi_re.sub('', ansi_text))
+    """
+    Count the number of visible characters.
+
+    >>> char_len("\x1b[0;30;40mX\x1b[0m")
+    1
+    >>> char_len("\x1b[0;30;40mXY\x1b[0m")
+    2
+    >>> char_len("\x1b[0;30;40mX\x1b[0mY")
+    2
+    >>> char_len("hello")
+    5
+    >>> char_len("")
+    0
+    """
+    return len(ansi_re.sub('', ansi_text))
 
 def char_slice(ansi_text, start, length):
-	"""
-	Slices a string with respect to ansi code sequences
-
-	Acts as if the ansi codes aren't there, slices the text from the
-	given start point to the given length and adds the codes back in.
-
-	>>> test_string = "abcde\x1b[30mfoo\x1b[31mbar\x1b[0mnormal"
-	>>> split_ansi_from_text(test_string)
-	['abcde', '\\x1b[30m', 'foo', '\\x1b[31m', 'bar', '\\x1b[0m', 'normal']
-	>>> char_slice(test_string, 1, 3)
-	'bcd'
-	>>> char_slice(test_string, 5, 6)
-	'\\x1b[30mfoo\\x1b[31mbar'
-	>>> char_slice(test_string, 0, 8)
-	'abcde\\x1b[30mfoo'
-	>>> char_slice(test_string, 4, 4)
-	'e\\x1b[30mfoo'
-	>>> char_slice(test_string, 11, 100)
-	'\\x1b[0mnormal'
-	>>> char_slice(test_string, 9, 100)
-	'\\x1b[31mar\\x1b[0mnormal'
-	>>> char_slice(test_string, 9, 4)
-	'\\x1b[31mar\\x1b[0mno'
-	"""
-	chunks = []
-	last_color = ""
-	pos = old_pos = 0
-	for i, chunk in enumerate(split_ansi_from_text(ansi_text)):
-		if i % 2 == 1:
-			last_color = chunk
-			continue
-
-		old_pos = pos
-		pos += len(chunk)
-		if pos <= start:
-			pass # seek
-		elif old_pos < start and pos >= start:
-			chunks.append(last_color)
-			chunks.append(chunk[start-old_pos:start-old_pos+length])
-		elif pos > length + start:
-			chunks.append(last_color)
-			chunks.append(chunk[:start-old_pos+length])
-		else:
-			chunks.append(last_color)
-			chunks.append(chunk)
-		if pos - start >= length:
-			break
-	return ''.join(chunks)
+    """
+    Slices a string with respect to ansi code sequences
+
+    Acts as if the ansi codes aren't there, slices the text from the
+    given start point to the given length and adds the codes back in.
+
+    >>> test_string = "abcde\x1b[30mfoo\x1b[31mbar\x1b[0mnormal"
+    >>> split_ansi_from_text(test_string)
+    ['abcde', '\\x1b[30m', 'foo', '\\x1b[31m', 'bar', '\\x1b[0m', 'normal']
+    >>> char_slice(test_string, 1, 3)
+    'bcd'
+    >>> char_slice(test_string, 5, 6)
+    '\\x1b[30mfoo\\x1b[31mbar'
+    >>> char_slice(test_string, 0, 8)
+    'abcde\\x1b[30mfoo'
+    >>> char_slice(test_string, 4, 4)
+    'e\\x1b[30mfoo'
+    >>> char_slice(test_string, 11, 100)
+    '\\x1b[0mnormal'
+    >>> char_slice(test_string, 9, 100)
+    '\\x1b[31mar\\x1b[0mnormal'
+    >>> char_slice(test_string, 9, 4)
+    '\\x1b[31mar\\x1b[0mno'
+    """
+    chunks = []
+    last_color = ""
+    pos = old_pos = 0
+    for i, chunk in enumerate(split_ansi_from_text(ansi_text)):
+        if i % 2 == 1:
+            last_color = chunk
+            continue
+
+        old_pos = pos
+        pos += len(chunk)
+        if pos <= start:
+            pass # seek
+        elif old_pos < start and pos >= start:
+            chunks.append(last_color)
+            chunks.append(chunk[start-old_pos:start-old_pos+length])
+        elif pos > length + start:
+            chunks.append(last_color)
+            chunks.append(chunk[:start-old_pos+length])
+        else:
+            chunks.append(last_color)
+            chunks.append(chunk)
+        if pos - start >= length:
+            break
+    return ''.join(chunks)
 
 if __name__ == '__main__':
-	import doctest
-	doctest.testmod()
+    import doctest
+    doctest.testmod()
diff --git a/ranger/gui/bar.py b/ranger/gui/bar.py
index ae07dd35..a6596bf5 100644
--- a/ranger/gui/bar.py
+++ b/ranger/gui/bar.py
@@ -6,134 +6,134 @@ import sys
 PY3 = sys.version > '3'
 
 class Bar(object):
-	left = None
-	right = None
-	gap = None
-
-	def __init__(self, base_color_tag):
-		self.left = BarSide(base_color_tag)
-		self.right = BarSide(base_color_tag)
-		self.gap = BarSide(base_color_tag)
-
-	def add(self, *a, **kw):
-		self.left.add(*a, **kw)
-
-	def addright(self, *a, **kw):
-		self.right.add(*a, **kw)
-
-	def sumsize(self):
-		return self.left.sumsize() + self.right.sumsize()
-
-	def fixedsize(self):
-		return self.left.fixedsize() + self.right.fixedsize()
-
-	def shrink_by_removing(self, wid):
-		leftsize = self.left.sumsize()
-		rightsize = self.right.sumsize()
-		sumsize = leftsize + rightsize
-
-		# remove elemets from the left until it fits
-		if sumsize > wid:
-			while len(self.left) > 0:
-				leftsize -= len(self.left.pop(-1))
-				if leftsize + rightsize <= wid:
-					break
-			sumsize = leftsize + rightsize
-
-			# remove elemets from the right until it fits
-			if sumsize > wid:
-				while len(self.right) > 0:
-					rightsize -= len(self.right.pop(0))
-					if leftsize + rightsize <= wid:
-						break
-				sumsize = leftsize + rightsize
-
-		if sumsize < wid:
-			self.fill_gap(' ', (wid - sumsize), gapwidth=True)
-
-	def shrink_from_the_left(self, wid):
-		fixedsize = self.fixedsize()
-		if wid < fixedsize:
-			raise ValueError("Cannot shrink down to that size by cutting")
-		leftsize = self.left.sumsize()
-		rightsize = self.right.sumsize()
-		oversize = leftsize + rightsize - wid
-		if oversize <= 0:
-			return self.fill_gap(' ', wid, gapwidth=False)
-
-		# Shrink items to a minimum size until there is enough room.
-		for item in self.left:
-			if not item.fixed:
-				itemlen = len(item)
-				if oversize > itemlen - item.min_size:
-					item.cut_off_to(item.min_size)
-					oversize -= (itemlen - item.min_size)
-				else:
-					item.cut_off(oversize)
-					break
-
-	def fill_gap(self, char, wid, gapwidth=False):
-		del self.gap[:]
-
-		if not gapwidth:
-			wid = wid - self.sumsize()
-
-		if wid > 0:
-			self.gap.add(char * wid, 'space')
-
-	def combine(self):
-		return self.left + self.gap + self.right
+    left = None
+    right = None
+    gap = None
+
+    def __init__(self, base_color_tag):
+        self.left = BarSide(base_color_tag)
+        self.right = BarSide(base_color_tag)
+        self.gap = BarSide(base_color_tag)
+
+    def add(self, *a, **kw):
+        self.left.add(*a, **kw)
+
+    def addright(self, *a, **kw):
+        self.right.add(*a, **kw)
+
+    def sumsize(self):
+        return self.left.sumsize() + self.right.sumsize()
+
+    def fixedsize(self):
+        return self.left.fixedsize() + self.right.fixedsize()
+
+    def shrink_by_removing(self, wid):
+        leftsize = self.left.sumsize()
+        rightsize = self.right.sumsize()
+        sumsize = leftsize + rightsize
+
+        # remove elemets from the left until it fits
+        if sumsize > wid:
+            while len(self.left) > 0:
+                leftsize -= len(self.left.pop(-1))
+                if leftsize + rightsize <= wid:
+                    break
+            sumsize = leftsize + rightsize
+
+            # remove elemets from the right until it fits
+            if sumsize > wid:
+                while len(self.right) > 0:
+                    rightsize -= len(self.right.pop(0))
+                    if leftsize + rightsize <= wid:
+                        break
+                sumsize = leftsize + rightsize
+
+        if sumsize < wid:
+            self.fill_gap(' ', (wid - sumsize), gapwidth=True)
+
+    def shrink_from_the_left(self, wid):
+        fixedsize = self.fixedsize()
+        if wid < fixedsize:
+            raise ValueError("Cannot shrink down to that size by cutting")
+        leftsize = self.left.sumsize()
+        rightsize = self.right.sumsize()
+        oversize = leftsize + rightsize - wid
+        if oversize <= 0:
+            return self.fill_gap(' ', wid, gapwidth=False)
+
+        # Shrink items to a minimum size until there is enough room.
+        for item in self.left:
+            if not item.fixed:
+                itemlen = len(item)
+                if oversize > itemlen - item.min_size:
+                    item.cut_off_to(item.min_size)
+                    oversize -= (itemlen - item.min_size)
+                else:
+                    item.cut_off(oversize)
+                    break
+
+    def fill_gap(self, char, wid, gapwidth=False):
+        del self.gap[:]
+
+        if not gapwidth:
+            wid = wid - self.sumsize()
+
+        if wid > 0:
+            self.gap.add(char * wid, 'space')
+
+    def combine(self):
+        return self.left + self.gap + self.right
 
 
 class BarSide(list):
-	def __init__(self, base_color_tag):
-		self.base_color_tag = base_color_tag
+    def __init__(self, base_color_tag):
+        self.base_color_tag = base_color_tag
 
-	def add(self, string, *lst, **kw):
-		cs = ColoredString(string, self.base_color_tag, *lst)
-		cs.__dict__.update(kw)
-		self.append(cs)
+    def add(self, string, *lst, **kw):
+        cs = ColoredString(string, self.base_color_tag, *lst)
+        cs.__dict__.update(kw)
+        self.append(cs)
 
-	def add_space(self, n=1):
-		self.add(' ' * n, 'space')
+    def add_space(self, n=1):
+        self.add(' ' * n, 'space')
 
-	def sumsize(self):
-		return sum(len(item) for item in self)
+    def sumsize(self):
+        return sum(len(item) for item in self)
 
-	def fixedsize(self):
-		n = 0
-		for item in self:
-			if item.fixed:
-				n += len(item)
-			else:
-				n += item.min_size
-		return n
+    def fixedsize(self):
+        n = 0
+        for item in self:
+            if item.fixed:
+                n += len(item)
+            else:
+                n += item.min_size
+        return n
 
 
 class ColoredString(object):
-	def __init__(self, string, *lst):
-		self.string = WideString(string)
-		self.lst = lst
-		self.fixed = False
-		if not len(string):
-			self.min_size = 0
-		elif PY3:
-			self.min_size = utf_char_width(string[0])
-		else:
-			self.min_size = utf_char_width(self.string.chars[0].decode('utf-8'))
-
-	def cut_off(self, n):
-		if n >= 1:
-			self.string = self.string[:-n]
-
-	def cut_off_to(self, n):
-		if n < self.min_size:
-			self.string = self.string[:self.min_size]
-		elif n < len(self.string):
-			self.string = self.string[:n]
-
-	def __len__(self):
-		return len(self.string)
-
-	def __str__(self):
-		return str(self.string)
+    def __init__(self, string, *lst):
+        self.string = WideString(string)
+        self.lst = lst
+        self.fixed = False
+        if not len(string):
+            self.min_size = 0
+        elif PY3:
+            self.min_size = utf_char_width(string[0])
+        else:
+            self.min_size = utf_char_width(self.string.chars[0].decode('utf-8'))
+
+    def cut_off(self, n):
+        if n >= 1:
+            self.string = self.string[:-n]
+
+    def cut_off_to(self, n):
+        if n < self.min_size:
+            self.string = self.string[:self.min_size]
+        elif n < len(self.string):
+            self.string = self.string[:n]
+
+    def __len__(self):
+        return len(self.string)
+
+    def __str__(self):
+        return str(self.string)
diff --git a/ranger/gui/color.py b/ranger/gui/color.py
index 037ff5c1..ab986b55 100644
--- a/ranger/gui/color.py
+++ b/ranger/gui/color.py
@@ -19,18 +19,18 @@ import curses
 COLOR_PAIRS = {10: 0}
 
 def get_color(fg, bg):
-	"""
-	Returns the curses color pair for the given fg/bg combination.
-	"""
+    """
+    Returns the curses color pair for the given fg/bg combination.
+    """
 
-	c = bg+2 + 9*(fg + 2)
+    c = bg+2 + 9*(fg + 2)
 
-	if c not in COLOR_PAIRS:
-		size = len(COLOR_PAIRS)
-		curses.init_pair(size, fg, bg)
-		COLOR_PAIRS[c] = size
+    if c not in COLOR_PAIRS:
+        size = len(COLOR_PAIRS)
+        curses.init_pair(size, fg, bg)
+        COLOR_PAIRS[c] = size
 
-	return COLOR_PAIRS[c]
+    return COLOR_PAIRS[c]
 
 black   = curses.COLOR_BLACK
 blue    = curses.COLOR_BLUE
@@ -52,7 +52,7 @@ invisible  = curses.A_INVIS
 default_colors = (default, default, normal)
 
 def remove_attr(integer, attribute):
-	"""Remove an attribute from an integer"""
-	if integer & attribute:
-		return integer ^ attribute
-	return integer
+    """Remove an attribute from an integer"""
+    if integer & attribute:
+        return integer ^ attribute
+    return integer
diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py
index 34e59f55..0a142d52 100644
--- a/ranger/gui/colorscheme.py
+++ b/ranger/gui/colorscheme.py
@@ -37,103 +37,103 @@ from ranger.ext.cached_function import cached_function
 from ranger.ext.iter_tools import flatten
 
 class ColorScheme(object):
-	"""
-	This is the class that colorschemes must inherit from.
-
-	it defines the get() method, which returns the color tuple
-	which fits to the given keys.
-	"""
-
-	@cached_function
-	def get(self, *keys):
-		"""
-		Returns the (fg, bg, attr) for the given keys.
-
-		Using this function rather than use() will cache all
-		colors for faster access.
-		"""
-		context = Context(keys)
-		color = self.use(context)
-		if len(color) != 3 or not all(isinstance(value, int) \
-				for value in color):
-			raise ValueError("Bad Value from colorscheme.  Need "
-				"a tuple of (foreground_color, background_color, attribute).")
-		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(*flatten(keys))
-		return attr | color_pair(get_color(fg, bg))
-
-	def use(self, context):
-		"""
-		Use the colorscheme to determine the (fg, bg, attr) tuple.
-
-		Override this method in your own colorscheme.
-		"""
-		return (-1, -1, 0)
+    """
+    This is the class that colorschemes must inherit from.
+
+    it defines the get() method, which returns the color tuple
+    which fits to the given keys.
+    """
+
+    @cached_function
+    def get(self, *keys):
+        """
+        Returns the (fg, bg, attr) for the given keys.
+
+        Using this function rather than use() will cache all
+        colors for faster access.
+        """
+        context = Context(keys)
+        color = self.use(context)
+        if len(color) != 3 or not all(isinstance(value, int) \
+                for value in color):
+            raise ValueError("Bad Value from colorscheme.  Need "
+                "a tuple of (foreground_color, background_color, attribute).")
+        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(*flatten(keys))
+        return attr | color_pair(get_color(fg, bg))
+
+    def use(self, context):
+        """
+        Use the colorscheme to determine the (fg, bg, attr) tuple.
+
+        Override this method in your own colorscheme.
+        """
+        return (-1, -1, 0)
 
 def _colorscheme_name_to_class(signal):
-	# Find the colorscheme.  First look in ~/.config/ranger/colorschemes,
-	# then at RANGERDIR/colorschemes.  If the file contains a class
-	# named Scheme, it is used.  Otherwise, an arbitrary other class
-	# is picked.
-	if isinstance(signal.value, ColorScheme): return
-
-	if not signal.value:
-		signal.value = 'default'
-
-	scheme_name = signal.value
-	usecustom = not ranger.arg.clean
-
-	def exists(colorscheme):
-		return os.path.exists(colorscheme + '.py')
-
-	def is_scheme(x):
-		try:
-			return issubclass(x, ColorScheme)
-		except:
-			return False
-
-	# create ~/.config/ranger/colorschemes/__init__.py if it doesn't exist
-	if usecustom:
-		if os.path.exists(signal.fm.confpath('colorschemes')):
-			initpy = signal.fm.confpath('colorschemes', '__init__.py')
-			if not os.path.exists(initpy):
-				open(initpy, 'a').close()
-
-	if usecustom and \
-			exists(signal.fm.confpath('colorschemes', scheme_name)):
-		scheme_supermodule = 'colorschemes'
-	elif exists(signal.fm.relpath('colorschemes', scheme_name)):
-		scheme_supermodule = 'ranger.colorschemes'
-		usecustom = False
-	else:
-		scheme_supermodule = None  # found no matching file.
-
-	if scheme_supermodule is None:
-		if signal.previous and isinstance(signal.previous, ColorScheme):
-			signal.value = signal.previous
-		else:
-			signal.value = ColorScheme()
-		raise Exception("Cannot locate colorscheme `%s'" % scheme_name)
-	else:
-		if usecustom: allow_access_to_confdir(ranger.arg.confdir, True)
-		scheme_module = getattr(__import__(scheme_supermodule,
-				globals(), locals(), [scheme_name], 0), scheme_name)
-		if usecustom: allow_access_to_confdir(ranger.arg.confdir, False)
-		if hasattr(scheme_module, 'Scheme') \
-				and is_scheme(scheme_module.Scheme):
-			signal.value = scheme_module.Scheme()
-		else:
-			for var in scheme_module.__dict__.values():
-				if var != ColorScheme and is_scheme(var):
-					signal.value = var()
-					break
-			else:
-				raise Exception("The module contains no valid colorscheme!")
+    # Find the colorscheme.  First look in ~/.config/ranger/colorschemes,
+    # then at RANGERDIR/colorschemes.  If the file contains a class
+    # named Scheme, it is used.  Otherwise, an arbitrary other class
+    # is picked.
+    if isinstance(signal.value, ColorScheme): return
+
+    if not signal.value:
+        signal.value = 'default'
+
+    scheme_name = signal.value
+    usecustom = not ranger.arg.clean
+
+    def exists(colorscheme):
+        return os.path.exists(colorscheme + '.py')
+
+    def is_scheme(x):
+        try:
+            return issubclass(x, ColorScheme)
+        except:
+            return False
+
+    # create ~/.config/ranger/colorschemes/__init__.py if it doesn't exist
+    if usecustom:
+        if os.path.exists(signal.fm.confpath('colorschemes')):
+            initpy = signal.fm.confpath('colorschemes', '__init__.py')
+            if not os.path.exists(initpy):
+                open(initpy, 'a').close()
+
+    if usecustom and \
+            exists(signal.fm.confpath('colorschemes', scheme_name)):
+        scheme_supermodule = 'colorschemes'
+    elif exists(signal.fm.relpath('colorschemes', scheme_name)):
+        scheme_supermodule = 'ranger.colorschemes'
+        usecustom = False
+    else:
+        scheme_supermodule = None  # found no matching file.
+
+    if scheme_supermodule is None:
+        if signal.previous and isinstance(signal.previous, ColorScheme):
+            signal.value = signal.previous
+        else:
+            signal.value = ColorScheme()
+        raise Exception("Cannot locate colorscheme `%s'" % scheme_name)
+    else:
+        if usecustom: allow_access_to_confdir(ranger.arg.confdir, True)
+        scheme_module = getattr(__import__(scheme_supermodule,
+                globals(), locals(), [scheme_name], 0), scheme_name)
+        if usecustom: allow_access_to_confdir(ranger.arg.confdir, False)
+        if hasattr(scheme_module, 'Scheme') \
+                and is_scheme(scheme_module.Scheme):
+            signal.value = scheme_module.Scheme()
+        else:
+            for var in scheme_module.__dict__.values():
+                if var != ColorScheme and is_scheme(var):
+                    signal.value = var()
+                    break
+            else:
+                raise Exception("The module contains no valid colorscheme!")
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index cdccecde..c9e8104e 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -2,28 +2,28 @@
 # This software is distributed under the terms of the GNU GPL version 3.
 
 CONTEXT_KEYS = ['reset', 'error', 'badinfo',
-		'in_browser', 'in_statusbar', 'in_titlebar', 'in_console',
-		'in_pager', 'in_taskview',
-		'directory', 'file', 'hostname',
-		'executable', 'media', 'link', 'fifo', 'socket', 'device',
-		'video', 'audio', 'image', 'media', 'document', 'container',
-		'selected', 'empty', 'main_column', 'message', 'background',
-		'good', 'bad',
-		'space', 'permissions', 'owner', 'group', 'mtime', 'nlink',
-		'scroll', 'all', 'bot', 'top', 'percentage', 'filter',
-		'marked', 'tagged', 'tag_marker', 'cut', 'copied',
-		'help_markup', # COMPAT
-		'seperator', 'key', 'special', 'border', # COMPAT
-		'title', 'text', 'highlight', 'bars', 'quotes', 'tab', 'loaded',
-		'keybuffer']
+        'in_browser', 'in_statusbar', 'in_titlebar', 'in_console',
+        'in_pager', 'in_taskview',
+        'directory', 'file', 'hostname',
+        'executable', 'media', 'link', 'fifo', 'socket', 'device',
+        'video', 'audio', 'image', 'media', 'document', 'container',
+        'selected', 'empty', 'main_column', 'message', 'background',
+        'good', 'bad',
+        'space', 'permissions', 'owner', 'group', 'mtime', 'nlink',
+        'scroll', 'all', 'bot', 'top', 'percentage', 'filter',
+        'marked', 'tagged', 'tag_marker', 'cut', 'copied',
+        'help_markup', # COMPAT
+        'seperator', 'key', 'special', 'border', # COMPAT
+        'title', 'text', 'highlight', 'bars', 'quotes', 'tab', 'loaded',
+        'keybuffer']
 
 class Context(object):
-	def __init__(self, keys):
-		# set all given keys to True
-		d = self.__dict__
-		for key in keys:
-			d[key] = True
+    def __init__(self, keys):
+        # set all given keys to True
+        d = self.__dict__
+        for key in keys:
+            d[key] = True
 
 # set all keys to False
 for key in CONTEXT_KEYS:
-	setattr(Context, key, False)
+    setattr(Context, key, False)
diff --git a/ranger/gui/curses_shortcuts.py b/ranger/gui/curses_shortcuts.py
index 43b583a6..82640322 100644
--- a/ranger/gui/curses_shortcuts.py
+++ b/ranger/gui/curses_shortcuts.py
@@ -9,67 +9,67 @@ 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]
+    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.
-	color(*keys) -- sets the color associated with the keys from
-		the current colorscheme.
-	color_at(y, x, wid, *keys) -- sets the color at the given position
-	color_reset() -- resets the color to the default
-	addstr(*args) -- failsafe version of self.win.addstr(*args)
-	"""
+    """
+    This class defines shortcuts to faciliate operations with curses.
+    color(*keys) -- sets the color associated with the keys from
+        the current colorscheme.
+    color_at(y, x, wid, *keys) -- sets the color at the given position
+    color_reset() -- resets the color to the default
+    addstr(*args) -- failsafe version of self.win.addstr(*args)
+    """
 
-	def addstr(self, *args):
-		try:
-			self.win.addstr(*args)
-		except:
-			if len(args) > 1:
-				try:
-					self.win.addstr(*_fix_surrogates(args))
-				except:
-					pass
+    def addstr(self, *args):
+        try:
+            self.win.addstr(*args)
+        except:
+            if len(args) > 1:
+                try:
+                    self.win.addstr(*_fix_surrogates(args))
+                except:
+                    pass
 
-	def addnstr(self, *args):
-		try:
-			self.win.addnstr(*args)
-		except:
-			if len(args) > 2:
-				try:
-					self.win.addnstr(*_fix_surrogates(args))
-				except:
-					pass
+    def addnstr(self, *args):
+        try:
+            self.win.addnstr(*args)
+        except:
+            if len(args) > 2:
+                try:
+                    self.win.addnstr(*_fix_surrogates(args))
+                except:
+                    pass
 
-	def addch(self, *args):
-		try:
-			self.win.addch(*args)
-		except:
-			pass
+    def addch(self, *args):
+        try:
+            self.win.addch(*args)
+        except:
+            pass
 
-	def color(self, *keys):
-		"""Change the colors from now on."""
-		attr = self.settings.colorscheme.get_attr(*keys)
-		try:
-			self.win.attrset(attr)
-		except _curses.error:
-			pass
+    def color(self, *keys):
+        """Change the colors from now on."""
+        attr = self.settings.colorscheme.get_attr(*keys)
+        try:
+            self.win.attrset(attr)
+        except _curses.error:
+            pass
 
-	def color_at(self, y, x, wid, *keys):
-		"""Change the colors at the specified position"""
-		attr = self.settings.colorscheme.get_attr(*keys)
-		try:
-			self.win.chgat(y, x, wid, attr)
-		except _curses.error:
-			pass
+    def color_at(self, y, x, wid, *keys):
+        """Change the colors at the specified position"""
+        attr = self.settings.colorscheme.get_attr(*keys)
+        try:
+            self.win.chgat(y, x, wid, attr)
+        except _curses.error:
+            pass
 
-	def set_fg_bg_attr(self, fg, bg, attr):
-		try:
-			self.win.attrset(curses.color_pair(get_color(fg, bg)) | attr)
-		except _curses.error:
-			pass
+    def set_fg_bg_attr(self, fg, bg, attr):
+        try:
+            self.win.attrset(curses.color_pair(get_color(fg, bg)) | attr)
+        except _curses.error:
+            pass
 
-	def color_reset(self):
-		"""Change the colors to the default colors"""
-		CursesShortcuts.color(self, 'reset')
+    def color_reset(self):
+        """Change the colors to the default colors"""
+        CursesShortcuts.color(self, 'reset')
diff --git a/ranger/gui/displayable.py b/ranger/gui/displayable.py
index 28526d7a..e5431039 100644
--- a/ranger/gui/displayable.py
+++ b/ranger/gui/displayable.py
@@ -5,311 +5,311 @@ from ranger.core.shared import FileManagerAware, EnvironmentAware
 from ranger.gui.curses_shortcuts import CursesShortcuts
 
 class Displayable(EnvironmentAware, FileManagerAware, CursesShortcuts):
-	"""
-	Displayables are objects which are displayed on the screen.
-
-	This is just the abstract class, defining basic operations
-	such as resizing, printing, changing colors.
-	Subclasses of displayable can extend these methods:
-
-	draw() -- draw the object. Is only called if visible.
-	poke() -- is called just before draw(), even if not visible.
-	finalize() -- called after all objects finished drawing.
-	click(event) -- called with a MouseEvent. This is called on all
-		visible objects under the mouse, until one returns True.
-	press(key) -- called after a key press on focused objects.
-	destroy() -- called before destroying the displayable object
-
-	Additionally, there are these methods:
-
-	__contains__(item) -- is the item (y, x) inside the widget?
-
-	These attributes are set:
-
-	Modifiable:
-		focused -- Focused objects receive press() calls.
-		visible -- Visible objects receive draw() and finalize() calls
-		need_redraw -- Should the widget be redrawn? This variable may
-			be set at various places in the script and should eventually be
-			handled (and unset) in the draw() method.
-
-	Read-Only: (i.e. reccomended not to change manually)
-		win -- the own curses window object
-		parent -- the parent (DisplayableContainer) object or None
-		x, y, wid, hei -- absolute coordinates and boundaries
-		settings, fm -- inherited shared variables
-	"""
-
-	def __init__(self, win, env=None, fm=None, settings=None):
-		from ranger.gui.ui import UI
-		if env is not None:
-			self.env = env
-		if fm is not None:
-			self.fm = fm
-		if settings is not None:
-			self.settings = settings
-
-		self.need_redraw = True
-		self.focused = False
-		self.visible = True
-		self.x = 0
-		self.y = 0
-		self.wid = 0
-		self.hei = 0
-		self.paryx = (0, 0)
-		self.parent = None
-
-		self._old_visible = self.visible
-
-		if win is not None:
-			if isinstance(self, UI):
-				self.win = win
-			else:
-				self.win = win.derwin(1, 1, 0, 0)
-
-	def __nonzero__(self):
-		"""Always True"""
-		return True
-	__bool__ = __nonzero__
-
-	def __contains__(self, item):
-		"""
-		Is item inside the boundaries?
-		item can be an iterable like [y, x] or an object with x and y methods.
-		"""
-		try:
-			y, x = item.y, item.x
-		except AttributeError:
-			try:
-				y, x = item
-			except (ValueError, TypeError):
-				return False
-
-		return self.contains_point(y, x)
-
-	def draw(self):
-		"""
-		Draw the object. Called on every main iteration if visible.
-		Containers should call draw() on their contained objects here.
-		Override this!
-		"""
-
-	def destroy(self):
-		"""
-		Called when the object is destroyed.
-		Override this!
-		"""
-
-	def contains_point(self, y, x):
-		"""
-		Test whether the point (with absolute coordinates) lies
-		within the boundaries of this object.
-		"""
-		return (x >= self.x and x < self.x + self.wid) and \
-				(y >= self.y and y < self.y + self.hei)
-
-	def click(self, event):
-		"""
-		Called when a mouse key is pressed and self.focused is True.
-		Override this!
-		"""
-		pass
-
-	def press(self, key):
-		"""
-		Called when a key is pressed and self.focused is True.
-		Override this!
-		"""
-		pass
-
-	def poke(self):
-		"""Called before drawing, even if invisible"""
-		if self._old_visible != self.visible:
-			self._old_visible = self.visible
-			self.need_redraw = True
-
-			if not self.visible:
-				self.win.erase()
-
-	def finalize(self):
-		"""
-		Called after every displayable is done drawing.
-		Override this!
-		"""
-		pass
-
-	def resize(self, y, x, hei=None, wid=None):
-		"""Resize the widget"""
-		do_move = True
-		try:
-			maxy, maxx = self.fm.ui.termsize
-		except TypeError:
-			pass
-		else:
-			if hei is None:
-				hei = maxy - y
-
-			if wid is None:
-				wid = maxx - x
-
-			if x < 0 or y < 0:
-				self.fm.notify("Warning: Subwindow origin below zero for <%s> "
-					"(x = %d, y = %d)" % (self, x, y), bad=True)
-
-			if x + wid > maxx or y + hei > maxy:
-				self.fm.notify("Warning: Subwindow size out of bounds for <%s> "
-					"(x = %d, y = %d, hei = %d, wid = %d)" % (self,
-					x, y, hei, wid), bad=True)
-
-		window_is_cleared = False
-
-		if hei != self.hei or wid != self.wid:
-			#log("resizing " + str(self))
-			self.win.erase()
-			self.need_redraw = True
-			window_is_cleared = True
-			try:
-				self.win.resize(hei, wid)
-			except:
-				# Not enough space for resizing...
-				try:
-					self.win.mvderwin(0, 0)
-					do_move = True
-					self.win.resize(hei, wid)
-				except:
-					pass
-					#raise ValueError("Resizing Failed!")
-
-			self.hei, self.wid = self.win.getmaxyx()
-
-		if do_move or y != self.paryx[0] or x != self.paryx[1]:
-			if not window_is_cleared:
-				self.win.erase()
-				self.need_redraw = True
-			#log("moving " + str(self))
-			try:
-				self.win.mvderwin(y, x)
-			except:
-				pass
-
-			self.paryx = self.win.getparyx()
-			self.y, self.x = self.paryx
-			if self.parent:
-				self.y += self.parent.y
-				self.x += self.parent.x
-
-	def __str__(self):
-		return self.__class__.__name__
+    """
+    Displayables are objects which are displayed on the screen.
+
+    This is just the abstract class, defining basic operations
+    such as resizing, printing, changing colors.
+    Subclasses of displayable can extend these methods:
+
+    draw() -- draw the object. Is only called if visible.
+    poke() -- is called just before draw(), even if not visible.
+    finalize() -- called after all objects finished drawing.
+    click(event) -- called with a MouseEvent. This is called on all
+        visible objects under the mouse, until one returns True.
+    press(key) -- called after a key press on focused objects.
+    destroy() -- called before destroying the displayable object
+
+    Additionally, there are these methods:
+
+    __contains__(item) -- is the item (y, x) inside the widget?
+
+    These attributes are set:
+
+    Modifiable:
+        focused -- Focused objects receive press() calls.
+        visible -- Visible objects receive draw() and finalize() calls
+        need_redraw -- Should the widget be redrawn? This variable may
+            be set at various places in the script and should eventually be
+            handled (and unset) in the draw() method.
+
+    Read-Only: (i.e. reccomended not to change manually)
+        win -- the own curses window object
+        parent -- the parent (DisplayableContainer) object or None
+        x, y, wid, hei -- absolute coordinates and boundaries
+        settings, fm -- inherited shared variables
+    """
+
+    def __init__(self, win, env=None, fm=None, settings=None):
+        from ranger.gui.ui import UI
+        if env is not None:
+            self.env = env
+        if fm is not None:
+            self.fm = fm
+        if settings is not None:
+            self.settings = settings
+
+        self.need_redraw = True
+        self.focused = False
+        self.visible = True
+        self.x = 0
+        self.y = 0
+        self.wid = 0
+        self.hei = 0
+        self.paryx = (0, 0)
+        self.parent = None
+
+        self._old_visible = self.visible
+
+        if win is not None:
+            if isinstance(self, UI):
+                self.win = win
+            else:
+                self.win = win.derwin(1, 1, 0, 0)
+
+    def __nonzero__(self):
+        """Always True"""
+        return True
+    __bool__ = __nonzero__
+
+    def __contains__(self, item):
+        """
+        Is item inside the boundaries?
+        item can be an iterable like [y, x] or an object with x and y methods.
+        """
+        try:
+            y, x = item.y, item.x
+        except AttributeError:
+            try:
+                y, x = item
+            except (ValueError, TypeError):
+                return False
+
+        return self.contains_point(y, x)
+
+    def draw(self):
+        """
+        Draw the object. Called on every main iteration if visible.
+        Containers should call draw() on their contained objects here.
+        Override this!
+        """
+
+    def destroy(self):
+        """
+        Called when the object is destroyed.
+        Override this!
+        """
+
+    def contains_point(self, y, x):
+        """
+        Test whether the point (with absolute coordinates) lies
+        within the boundaries of this object.
+        """
+        return (x >= self.x and x < self.x + self.wid) and \
+                (y >= self.y and y < self.y + self.hei)
+
+    def click(self, event):
+        """
+        Called when a mouse key is pressed and self.focused is True.
+        Override this!
+        """
+        pass
+
+    def press(self, key):
+        """
+        Called when a key is pressed and self.focused is True.
+        Override this!
+        """
+        pass
+
+    def poke(self):
+        """Called before drawing, even if invisible"""
+        if self._old_visible != self.visible:
+            self._old_visible = self.visible
+            self.need_redraw = True
+
+            if not self.visible:
+                self.win.erase()
+
+    def finalize(self):
+        """
+        Called after every displayable is done drawing.
+        Override this!
+        """
+        pass
+
+    def resize(self, y, x, hei=None, wid=None):
+        """Resize the widget"""
+        do_move = True
+        try:
+            maxy, maxx = self.fm.ui.termsize
+        except TypeError:
+            pass
+        else:
+            if hei is None:
+                hei = maxy - y
+
+            if wid is None:
+                wid = maxx - x
+
+            if x < 0 or y < 0:
+                self.fm.notify("Warning: Subwindow origin below zero for <%s> "
+                    "(x = %d, y = %d)" % (self, x, y), bad=True)
+
+            if x + wid > maxx or y + hei > maxy:
+                self.fm.notify("Warning: Subwindow size out of bounds for <%s> "
+                    "(x = %d, y = %d, hei = %d, wid = %d)" % (self,
+                    x, y, hei, wid), bad=True)
+
+        window_is_cleared = False
+
+        if hei != self.hei or wid != self.wid:
+            #log("resizing " + str(self))
+            self.win.erase()
+            self.need_redraw = True
+            window_is_cleared = True
+            try:
+                self.win.resize(hei, wid)
+            except:
+                # Not enough space for resizing...
+                try:
+                    self.win.mvderwin(0, 0)
+                    do_move = True
+                    self.win.resize(hei, wid)
+                except:
+                    pass
+                    #raise ValueError("Resizing Failed!")
+
+            self.hei, self.wid = self.win.getmaxyx()
+
+        if do_move or y != self.paryx[0] or x != self.paryx[1]:
+            if not window_is_cleared:
+                self.win.erase()
+                self.need_redraw = True
+            #log("moving " + str(self))
+            try:
+                self.win.mvderwin(y, x)
+            except:
+                pass
+
+            self.paryx = self.win.getparyx()
+            self.y, self.x = self.paryx
+            if self.parent:
+                self.y += self.parent.y
+                self.x += self.parent.x
+
+    def __str__(self):
+        return self.__class__.__name__
 
 class DisplayableContainer(Displayable):
-	"""
-	DisplayableContainers are Displayables which contain other Displayables.
-
-	This is also an abstract class. The methods draw, poke, finalize,
-	click, press and destroy are extended here and will recursively
-	call the function on all contained objects.
-
-	New methods:
-
-	add_child(object) -- add the object to the container.
-	remove_child(object) -- remove the object from the container.
-
-	New attributes:
-
-	container -- a list with all contained objects (rw)
-	"""
-
-	def __init__(self, win, env=None, fm=None, settings=None):
-		if env is not None:
-			self.env = env
-		if fm is not None:
-			self.fm = fm
-		if settings is not None:
-			self.settings = settings
-
-		self.container = []
-
-		Displayable.__init__(self, win)
-
-	# ------------------------------------ extended or overidden methods
-
-	def poke(self):
-		"""Recursively called on objects in container"""
-		Displayable.poke(self)
-		for displayable in self.container:
-			displayable.poke()
-
-	def draw(self):
-		"""Recursively called on visible objects in container"""
-		for displayable in self.container:
-			if self.need_redraw:
-				displayable.need_redraw = True
-			if displayable.visible:
-				displayable.draw()
-
-		self.need_redraw = False
-
-	def finalize(self):
-		"""Recursively called on visible objects in container"""
-		for displayable in self.container:
-			if displayable.visible:
-				displayable.finalize()
-
-	def press(self, key):
-		"""Recursively called on objects in container"""
-		focused_obj = self._get_focused_obj()
-
-		if focused_obj:
-			focused_obj.press(key)
-			return True
-		return False
-
-	def click(self, event):
-		"""Recursively called on objects in container"""
-		focused_obj = self._get_focused_obj()
-		if focused_obj and focused_obj.click(event):
-			return True
-
-		for displayable in self.container:
-			if displayable.visible and event in displayable:
-				if displayable.click(event):
-					return True
-
-		return False
-
-	def destroy(self):
-		"""Recursively called on objects in container"""
-		for displayable in self.container:
-			displayable.destroy()
-
-	# ----------------------------------------------- new methods
-
-	def add_child(self, obj):
-		"""Add the objects to the container."""
-		if obj.parent:
-			obj.parent.remove_child(obj)
-		self.container.append(obj)
-		obj.parent = self
-
-	def remove_child(self, obj):
-		"""Remove the object from the container."""
-		try:
-			self.container.remove(obj)
-		except ValueError:
-			pass
-		else:
-			obj.parent = None
-
-	def _get_focused_obj(self):
-		# Finds a focused displayable object in the container.
-		for displayable in self.container:
-			if displayable.focused:
-				return displayable
-			try:
-				obj = displayable._get_focused_obj()
-			except AttributeError:
-				pass
-			else:
-				if obj is not None:
-					return obj
-		return None
+    """
+    DisplayableContainers are Displayables which contain other Displayables.
+
+    This is also an abstract class. The methods draw, poke, finalize,
+    click, press and destroy are extended here and will recursively
+    call the function on all contained objects.
+
+    New methods:
+
+    add_child(object) -- add the object to the container.
+    remove_child(object) -- remove the object from the container.
+
+    New attributes:
+
+    container -- a list with all contained objects (rw)
+    """
+
+    def __init__(self, win, env=None, fm=None, settings=None):
+        if env is not None:
+            self.env = env
+        if fm is not None:
+            self.fm = fm
+        if settings is not None:
+            self.settings = settings
+
+        self.container = []
+
+        Displayable.__init__(self, win)
+
+    # ------------------------------------ extended or overidden methods
+
+    def poke(self):
+        """Recursively called on objects in container"""
+        Displayable.poke(self)
+        for displayable in self.container:
+            displayable.poke()
+
+    def draw(self):
+        """Recursively called on visible objects in container"""
+        for displayable in self.container:
+            if self.need_redraw:
+                displayable.need_redraw = True
+            if displayable.visible:
+                displayable.draw()
+
+        self.need_redraw = False
+
+    def finalize(self):
+        """Recursively called on visible objects in container"""
+        for displayable in self.container:
+            if displayable.visible:
+                displayable.finalize()
+
+    def press(self, key):
+        """Recursively called on objects in container"""
+        focused_obj = self._get_focused_obj()
+
+        if focused_obj:
+            focused_obj.press(key)
+            return True
+        return False
+
+    def click(self, event):
+        """Recursively called on objects in container"""
+        focused_obj = self._get_focused_obj()
+        if focused_obj and focused_obj.click(event):
+            return True
+
+        for displayable in self.container:
+            if displayable.visible and event in displayable:
+                if displayable.click(event):
+                    return True
+
+        return False
+
+    def destroy(self):
+        """Recursively called on objects in container"""
+        for displayable in self.container:
+            displayable.destroy()
+
+    # ----------------------------------------------- new methods
+
+    def add_child(self, obj):
+        """Add the objects to the container."""
+        if obj.parent:
+            obj.parent.remove_child(obj)
+        self.container.append(obj)
+        obj.parent = self
+
+    def remove_child(self, obj):
+        """Remove the object from the container."""
+        try:
+            self.container.remove(obj)
+        except ValueError:
+            pass
+        else:
+            obj.parent = None
+
+    def _get_focused_obj(self):
+        # Finds a focused displayable object in the container.
+        for displayable in self.container:
+            if displayable.focused:
+                return displayable
+            try:
+                obj = displayable._get_focused_obj()
+            except AttributeError:
+                pass
+            else:
+                if obj is not None:
+                    return obj
+        return None
diff --git a/ranger/gui/mouse_event.py b/ranger/gui/mouse_event.py
index ed370e54..1f157570 100644
--- a/ranger/gui/mouse_event.py
+++ b/ranger/gui/mouse_event.py
@@ -4,53 +4,53 @@
 import curses
 
 class MouseEvent(object):
-	PRESSED = [ 0,
-			curses.BUTTON1_PRESSED,
-			curses.BUTTON2_PRESSED,
-			curses.BUTTON3_PRESSED,
-			curses.BUTTON4_PRESSED ]
-	CTRL_SCROLLWHEEL_MULTIPLIER = 5
-
-	def __init__(self, getmouse):
-		"""Creates a MouseEvent object from the result of win.getmouse()"""
-		_, self.x, self.y, _, self.bstate = getmouse
-
-		# x-values above ~220 suddenly became negative, apparently
-		# it's sufficient to add 0xFF to fix that error.
-		if self.x < 0:
-			self.x += 0xFF
-
-		if self.y < 0:
-			self.y += 0xFF
-
-	def pressed(self, n):
-		"""Returns whether the mouse key n is pressed"""
-		try:
-			return (self.bstate & MouseEvent.PRESSED[n]) != 0
-		except:
-			return False
-
-	def mouse_wheel_direction(self):
-		"""Returns the direction of the scroll action, 0 if there was none"""
-		# If the bstate > ALL_MOUSE_EVENTS, it's an invalid mouse button.
-		# I interpret invalid buttons as "scroll down" because all tested
-		# systems have a broken curses implementation and this is a workaround.
-		if self.bstate & curses.BUTTON4_PRESSED:
-			return self.ctrl() and -self.CTRL_SCROLLWHEEL_MULTIPLIER or -1
-		elif self.bstate & curses.BUTTON2_PRESSED \
-				or self.bstate > curses.ALL_MOUSE_EVENTS:
-			return self.ctrl() and self.CTRL_SCROLLWHEEL_MULTIPLIER or 1
-		else:
-			return 0
-
-	def ctrl(self):
-		return self.bstate & curses.BUTTON_CTRL
-
-	def alt(self):
-		return self.bstate & curses.BUTTON_ALT
-
-	def shift(self):
-		return self.bstate & curses.BUTTON_SHIFT
-
-	def key_invalid(self):
-		return self.bstate > curses.ALL_MOUSE_EVENTS
+    PRESSED = [ 0,
+            curses.BUTTON1_PRESSED,
+            curses.BUTTON2_PRESSED,
+            curses.BUTTON3_PRESSED,
+            curses.BUTTON4_PRESSED ]
+    CTRL_SCROLLWHEEL_MULTIPLIER = 5
+
+    def __init__(self, getmouse):
+        """Creates a MouseEvent object from the result of win.getmouse()"""
+        _, self.x, self.y, _, self.bstate = getmouse
+
+        # x-values above ~220 suddenly became negative, apparently
+        # it's sufficient to add 0xFF to fix that error.
+        if self.x < 0:
+            self.x += 0xFF
+
+        if self.y < 0:
+            self.y += 0xFF
+
+    def pressed(self, n):
+        """Returns whether the mouse key n is pressed"""
+        try:
+            return (self.bstate & MouseEvent.PRESSED[n]) != 0
+        except:
+            return False
+
+    def mouse_wheel_direction(self):
+        """Returns the direction of the scroll action, 0 if there was none"""
+        # If the bstate > ALL_MOUSE_EVENTS, it's an invalid mouse button.
+        # I interpret invalid buttons as "scroll down" because all tested
+        # systems have a broken curses implementation and this is a workaround.
+        if self.bstate & curses.BUTTON4_PRESSED:
+            return self.ctrl() and -self.CTRL_SCROLLWHEEL_MULTIPLIER or -1
+        elif self.bstate & curses.BUTTON2_PRESSED \
+                or self.bstate > curses.ALL_MOUSE_EVENTS:
+            return self.ctrl() and self.CTRL_SCROLLWHEEL_MULTIPLIER or 1
+        else:
+            return 0
+
+    def ctrl(self):
+        return self.bstate & curses.BUTTON_CTRL
+
+    def alt(self):
+        return self.bstate & curses.BUTTON_ALT
+
+    def shift(self):
+        return self.bstate & curses.BUTTON_SHIFT
+
+    def key_invalid(self):
+        return self.bstate > curses.ALL_MOUSE_EVENTS
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index f01b7b17..f35b11bf 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -14,364 +14,364 @@ MOUSEMASK = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION
 
 _ASCII = ''.join(chr(c) for c in range(32, 127))
 def ascii_only(string):
-	return ''.join(c if c in _ASCII else '?' for c in string)
+    return ''.join(c if c in _ASCII else '?' for c in string)
 
 def _setup_mouse(signal):
-	if signal['value']:
-		curses.mousemask(MOUSEMASK)
-		curses.mouseinterval(0)
-
-		## this line solves this problem:
-		## If an action, following a mouse click, includes the
-		## suspension and re-initializion of the ui (e.g. running a
-		## file by clicking on its preview) and the next key is another
-		## mouse click, the bstate of this mouse event will be invalid.
-		## (atm, invalid bstates are recognized as scroll-down)
-		curses.ungetmouse(0,0,0,0,0)
-	else:
-		curses.mousemask(0)
+    if signal['value']:
+        curses.mousemask(MOUSEMASK)
+        curses.mouseinterval(0)
+
+        ## this line solves this problem:
+        ## If an action, following a mouse click, includes the
+        ## suspension and re-initializion of the ui (e.g. running a
+        ## file by clicking on its preview) and the next key is another
+        ## mouse click, the bstate of this mouse event will be invalid.
+        ## (atm, invalid bstates are recognized as scroll-down)
+        curses.ungetmouse(0,0,0,0,0)
+    else:
+        curses.mousemask(0)
 
 # TODO: progress bar
 # TODO: branch view
 class UI(DisplayableContainer):
-	is_set_up = False
-	load_mode = False
-	is_on = False
-	termsize = None
-
-	def __init__(self, env=None, fm=None):
-		self.keybuffer = KeyBuffer()
-		self.keymaps = KeyMaps(self.keybuffer)
-
-		if fm is not None:
-			self.fm = fm
-
-	def setup_curses(self):
-		os.environ['ESCDELAY'] = '25'   # don't know a cleaner way
-		try:
-			self.win = curses.initscr()
-		except _curses.error as e:
-			if e.args[0] == "setupterm: could not find terminal":
-				os.environ['TERM'] = 'linux'
-				self.win = curses.initscr()
-		self.keymaps.use_keymap('browser')
-		DisplayableContainer.__init__(self, None)
-
-	def initialize(self):
-		"""initialize curses, then call setup (at the first time) and resize."""
-		self.win.leaveok(0)
-		self.win.keypad(1)
-		self.load_mode = False
-
-		curses.cbreak()
-		curses.noecho()
-		curses.halfdelay(20)
-		try:
-			curses.curs_set(int(bool(self.settings.show_cursor)))
-		except:
-			pass
-		curses.start_color()
-		curses.use_default_colors()
-
-		self.settings.signal_bind('setopt.mouse_enabled', _setup_mouse)
-		_setup_mouse(dict(value=self.settings.mouse_enabled))
-
-		if not self.is_set_up:
-			self.is_set_up = True
-			self.setup()
-			self.win.addstr("loading...")
-			self.win.refresh()
-			curses.setupterm()
-			self._draw_title = curses.tigetflag('hs') # has_status_line
-		self.update_size()
-		self.is_on = True
-
-		if self.settings.update_tmux_title:
-			sys.stdout.write("\033kranger\033\\")
-			sys.stdout.flush()
-
-	def suspend(self):
-		"""Turn off curses"""
-		self.win.keypad(0)
-		curses.nocbreak()
-		curses.echo()
-		try:
-			curses.curs_set(1)
-		except:
-			pass
-		if self.settings.mouse_enabled:
-			_setup_mouse(dict(value=False))
-		curses.endwin()
-		self.is_on = False
-
-	def set_load_mode(self, boolean):
-		boolean = bool(boolean)
-		if boolean != self.load_mode:
-			self.load_mode = boolean
-
-			if boolean:
-				# don't wait for key presses in the load mode
-				curses.cbreak()
-				self.win.nodelay(1)
-			else:
-				self.win.nodelay(0)
-				curses.halfdelay(20)
-
-	def destroy(self):
-		"""Destroy all widgets and turn off curses"""
-		self.suspend()
-		DisplayableContainer.destroy(self)
-
-	def handle_mouse(self):
-		"""Handles mouse input"""
-		try:
-			event = MouseEvent(curses.getmouse())
-		except _curses.error:
-			return
-		if not self.console.visible:
-			DisplayableContainer.click(self, event)
-
-	def handle_key(self, key):
-		"""Handles key input"""
-
-		if hasattr(self, 'hint'):
-			self.hint()
-
-		if key < 0:
-			self.keybuffer.clear()
-
-		elif not DisplayableContainer.press(self, key):
-			self.keymaps.use_keymap('browser')
-			self.press(key)
-
-	def press(self, key):
-		keybuffer = self.keybuffer
-		self.status.clear_message()
-
-		keybuffer.add(key)
-		self.fm.hide_bookmarks()
-		self.browser.draw_hints = not keybuffer.finished_parsing \
-				and keybuffer.finished_parsing_quantifier
-
-		if keybuffer.result is not None:
-			try:
-				self.fm.execute_console(keybuffer.result,
-						wildcards=keybuffer.wildcards,
-						quantifier=keybuffer.quantifier)
-			finally:
-				if keybuffer.finished_parsing:
-					keybuffer.clear()
-		elif keybuffer.finished_parsing:
-			keybuffer.clear()
-			return False
-		return True
-
-	def handle_keys(self, *keys):
-		for key in keys:
-			self.handle_key(key)
-
-	def handle_input(self):
-		key = self.win.getch()
-		if key is 27 or key >= 128 and key < 256:
-			# Handle special keys like ALT+X or unicode here:
-			keys = [key]
-			previous_load_mode = self.load_mode
-			self.set_load_mode(True)
-			for n in range(4):
-				getkey = self.win.getch()
-				if getkey is not -1:
-					keys.append(getkey)
-			if len(keys) == 1:
-				keys.append(-1)
-			elif keys[0] == 27:
-				keys[0] = ALT_KEY
-			if self.settings.xterm_alt_key:
-				if len(keys) == 2 and keys[1] in range(127, 256):
-					if keys[0] == 195:
-						keys = [ALT_KEY, keys[1] - 64]
-					elif keys[0] == 194:
-						keys = [ALT_KEY, keys[1] - 128]
-			self.handle_keys(*keys)
-			self.set_load_mode(previous_load_mode)
-			if self.settings.flushinput and not self.console.visible:
-				curses.flushinp()
-		else:
-			# Handle simple key presses, CTRL+X, etc here:
-			if key > 0:
-				if self.settings.flushinput and not self.console.visible:
-					curses.flushinp()
-				if key == curses.KEY_MOUSE:
-					self.handle_mouse()
-				elif key == curses.KEY_RESIZE:
-					self.update_size()
-				else:
-					if not self.fm.input_is_blocked():
-						self.handle_key(key)
-
-	def setup(self):
-		"""Build up the UI by initializing widgets."""
-		from ranger.gui.widgets.browserview import BrowserView
-		from ranger.gui.widgets.titlebar import TitleBar
-		from ranger.gui.widgets.console import Console
-		from ranger.gui.widgets.statusbar import StatusBar
-		from ranger.gui.widgets.taskview import TaskView
-		from ranger.gui.widgets.pager import Pager
-
-		# Create a title bar
-		self.titlebar = TitleBar(self.win)
-		self.add_child(self.titlebar)
-
-		# Create the browser view
-		self.browser = BrowserView(self.win, self.settings.column_ratios)
-		self.settings.signal_bind('setopt.column_ratios',
-				self.browser.change_ratios)
-		self.add_child(self.browser)
-
-		# Create the process manager
-		self.taskview = TaskView(self.win)
-		self.taskview.visible = False
-		self.add_child(self.taskview)
-
-		# Create the status bar
-		self.status = StatusBar(self.win, self.browser.main_column)
-		self.add_child(self.status)
-
-		# Create the console
-		self.console = Console(self.win)
-		self.add_child(self.console)
-		self.console.visible = False
-
-		# Create the pager
-		self.pager = Pager(self.win)
-		self.pager.visible = False
-		self.add_child(self.pager)
-
-	def redraw(self):
-		"""Redraw all widgets"""
-		self.poke()
-
-		# determine which widgets are shown
-		if self.console.wait_for_command_input or self.console.question_queue:
-			self.console.focused = True
-			self.console.visible = True
-			self.status.visible = False
-		else:
-			self.console.focused = False
-			self.console.visible = False
-			self.status.visible = True
-
-		self.draw()
-		self.finalize()
-
-	def redraw_window(self):
-		"""Redraw the window. This only calls self.win.redrawwin()."""
-		self.win.erase()
-		self.win.redrawwin()
-		self.win.refresh()
-		self.win.redrawwin()
-		self.need_redraw = True
-
-	def update_size(self):
-		"""resize all widgets"""
-		self.termsize = self.win.getmaxyx()
-		y, x = self.termsize
-
-		self.browser.resize(self.settings.status_bar_on_top and 2 or 1, 0, y - 2, x)
-		self.taskview.resize(1, 0, y - 2, x)
-		self.pager.resize(1, 0, y - 2, x)
-		self.titlebar.resize(0, 0, 1, x)
-		self.status.resize(self.settings.status_bar_on_top and 1 or y-1, 0, 1, x)
-		self.console.resize(y - 1, 0, 1, x)
-
-	def draw(self):
-		"""Draw all objects in the container"""
-		self.win.touchwin()
-		DisplayableContainer.draw(self)
-		if self._draw_title and self.settings.update_title:
-			cwd = self.fm.thisdir.path
-			if cwd.startswith(self.fm.home_path):
-				cwd = '~' + cwd[len(self.fm.home_path):]
-			if self.settings.shorten_title:
-				split = cwd.rsplit(os.sep, self.settings.shorten_title)
-				if os.sep in split[0]:
-					cwd = os.sep.join(split[1:])
-			try:
-				fixed_cwd = cwd.encode('utf-8', 'surrogateescape'). \
-						decode('utf-8', 'replace')
-				sys.stdout.write("%sranger:%s%s" %
-						(curses.tigetstr('tsl').decode('latin-1'), fixed_cwd,
-						 curses.tigetstr('fsl').decode('latin-1')))
-				sys.stdout.flush()
-			except:
-				pass
-
-		self.win.refresh()
-
-	def finalize(self):
-		"""Finalize every object in container and refresh the window"""
-		DisplayableContainer.finalize(self)
-		self.win.refresh()
-
-	def close_pager(self):
-		if self.console.visible:
-			self.console.focused = True
-		self.pager.close()
-		self.pager.visible = False
-		self.pager.focused = False
-		self.browser.visible = True
-
-	def open_pager(self):
-		if self.console.focused:
-			self.console.focused = False
-		self.pager.open()
-		self.pager.visible = True
-		self.pager.focused = True
-		self.browser.visible = False
-		return self.pager
-
-	def open_embedded_pager(self):
-		self.browser.open_pager()
-		for column in self.browser.columns:
-			if column == self.browser.main_column:
-				break
-			column.level_shift(amount=1)
-		return self.browser.pager
-
-	def close_embedded_pager(self):
-		self.browser.close_pager()
-		for column in self.browser.columns:
-			column.level_restore()
-
-	def open_console(self, string='', prompt=None, position=None):
-		if self.console.open(string, prompt=prompt, position=position):
-			self.status.msg = None
-
-	def close_console(self):
-		self.console.close()
-		self.close_pager()
-
-	def open_taskview(self):
-		self.pager.close()
-		self.pager.visible = False
-		self.pager.focused = False
-		self.console.visible = False
-		self.browser.visible = False
-		self.taskview.visible = True
-		self.taskview.focused = True
-
-	def redraw_main_column(self):
-		self.browser.main_column.need_redraw = True
-
-	def close_taskview(self):
-		self.taskview.visible = False
-		self.browser.visible = True
-		self.taskview.focused = False
-
-	def throbber(self, string='.', remove=False):
-		if remove:
-			self.titlebar.throbber = type(self.titlebar).throbber
-		else:
-			self.titlebar.throbber = string
-
-	def hint(self, text=None):
-		self.status.hint = text
+    is_set_up = False
+    load_mode = False
+    is_on = False
+    termsize = None
+
+    def __init__(self, env=None, fm=None):
+        self.keybuffer = KeyBuffer()
+        self.keymaps = KeyMaps(self.keybuffer)
+
+        if fm is not None:
+            self.fm = fm
+
+    def setup_curses(self):
+        os.environ['ESCDELAY'] = '25'   # don't know a cleaner way
+        try:
+            self.win = curses.initscr()
+        except _curses.error as e:
+            if e.args[0] == "setupterm: could not find terminal":
+                os.environ['TERM'] = 'linux'
+                self.win = curses.initscr()
+        self.keymaps.use_keymap('browser')
+        DisplayableContainer.__init__(self, None)
+
+    def initialize(self):
+        """initialize curses, then call setup (at the first time) and resize."""
+        self.win.leaveok(0)
+        self.win.keypad(1)
+        self.load_mode = False
+
+        curses.cbreak()
+        curses.noecho()
+        curses.halfdelay(20)
+        try:
+            curses.curs_set(int(bool(self.settings.show_cursor)))
+        except:
+            pass
+        curses.start_color()
+        curses.use_default_colors()
+
+        self.settings.signal_bind('setopt.mouse_enabled', _setup_mouse)
+        _setup_mouse(dict(value=self.settings.mouse_enabled))
+
+        if not self.is_set_up:
+            self.is_set_up = True
+            self.setup()
+            self.win.addstr("loading...")
+            self.win.refresh()
+            curses.setupterm()
+            self._draw_title = curses.tigetflag('hs') # has_status_line
+        self.update_size()
+        self.is_on = True
+
+        if self.settings.update_tmux_title:
+            sys.stdout.write("\033kranger\033\\")
+            sys.stdout.flush()
+
+    def suspend(self):
+        """Turn off curses"""
+        self.win.keypad(0)
+        curses.nocbreak()
+        curses.echo()
+        try:
+            curses.curs_set(1)
+        except:
+            pass
+        if self.settings.mouse_enabled:
+            _setup_mouse(dict(value=False))
+        curses.endwin()
+        self.is_on = False
+
+    def set_load_mode(self, boolean):
+        boolean = bool(boolean)
+        if boolean != self.load_mode:
+            self.load_mode = boolean
+
+            if boolean:
+                # don't wait for key presses in the load mode
+                curses.cbreak()
+                self.win.nodelay(1)
+            else:
+                self.win.nodelay(0)
+                curses.halfdelay(20)
+
+    def destroy(self):
+        """Destroy all widgets and turn off curses"""
+        self.suspend()
+        DisplayableContainer.destroy(self)
+
+    def handle_mouse(self):
+        """Handles mouse input"""
+        try:
+            event = MouseEvent(curses.getmouse())
+        except _curses.error:
+            return
+        if not self.console.visible:
+            DisplayableContainer.click(self, event)
+
+    def handle_key(self, key):
+        """Handles key input"""
+
+        if hasattr(self, 'hint'):
+            self.hint()
+
+        if key < 0:
+            self.keybuffer.clear()
+
+        elif not DisplayableContainer.press(self, key):
+            self.keymaps.use_keymap('browser')
+            self.press(key)
+
+    def press(self, key):
+        keybuffer = self.keybuffer
+        self.status.clear_message()
+
+        keybuffer.add(key)
+        self.fm.hide_bookmarks()
+        self.browser.draw_hints = not keybuffer.finished_parsing \
+                and keybuffer.finished_parsing_quantifier
+
+        if keybuffer.result is not None:
+            try:
+                self.fm.execute_console(keybuffer.result,
+                        wildcards=keybuffer.wildcards,
+                        quantifier=keybuffer.quantifier)
+            finally:
+                if keybuffer.finished_parsing:
+                    keybuffer.clear()
+        elif keybuffer.finished_parsing:
+            keybuffer.clear()
+            return False
+        return True
+
+    def handle_keys(self, *keys):
+        for key in keys:
+            self.handle_key(key)
+
+    def handle_input(self):
+        key = self.win.getch()
+        if key is 27 or key >= 128 and key < 256:
+            # Handle special keys like ALT+X or unicode here:
+            keys = [key]
+            previous_load_mode = self.load_mode
+            self.set_load_mode(True)
+            for n in range(4):
+                getkey = self.win.getch()
+                if getkey is not -1:
+                    keys.append(getkey)
+            if len(keys) == 1:
+                keys.append(-1)
+            elif keys[0] == 27:
+                keys[0] = ALT_KEY
+            if self.settings.xterm_alt_key:
+                if len(keys) == 2 and keys[1] in range(127, 256):
+                    if keys[0] == 195:
+                        keys = [ALT_KEY, keys[1] - 64]
+                    elif keys[0] == 194:
+                        keys = [ALT_KEY, keys[1] - 128]
+            self.handle_keys(*keys)
+            self.set_load_mode(previous_load_mode)
+            if self.settings.flushinput and not self.console.visible:
+                curses.flushinp()
+        else:
+            # Handle simple key presses, CTRL+X, etc here:
+            if key > 0:
+                if self.settings.flushinput and not self.console.visible:
+                    curses.flushinp()
+                if key == curses.KEY_MOUSE:
+                    self.handle_mouse()
+                elif key == curses.KEY_RESIZE:
+                    self.update_size()
+                else:
+                    if not self.fm.input_is_blocked():
+                        self.handle_key(key)
+
+    def setup(self):
+        """Build up the UI by initializing widgets."""
+        from ranger.gui.widgets.browserview import BrowserView
+        from ranger.gui.widgets.titlebar import TitleBar
+        from ranger.gui.widgets.console import Console
+        from ranger.gui.widgets.statusbar import StatusBar
+        from ranger.gui.widgets.taskview import TaskView
+        from ranger.gui.widgets.pager import Pager
+
+        # Create a title bar
+        self.titlebar = TitleBar(self.win)
+        self.add_child(self.titlebar)
+
+        # Create the browser view
+        self.browser = BrowserView(self.win, self.settings.column_ratios)
+        self.settings.signal_bind('setopt.column_ratios',
+                self.browser.change_ratios)
+        self.add_child(self.browser)
+
+        # Create the process manager
+        self.taskview = TaskView(self.win)
+        self.taskview.visible = False
+        self.add_child(self.taskview)
+
+        # Create the status bar
+        self.status = StatusBar(self.win, self.browser.main_column)
+        self.add_child(self.status)
+
+        # Create the console
+        self.console = Console(self.win)
+        self.add_child(self.console)
+        self.console.visible = False
+
+        # Create the pager
+        self.pager = Pager(self.win)
+        self.pager.visible = False
+        self.add_child(self.pager)
+
+    def redraw(self):
+        """Redraw all widgets"""
+        self.poke()
+
+        # determine which widgets are shown
+        if self.console.wait_for_command_input or self.console.question_queue:
+            self.console.focused = True
+            self.console.visible = True
+            self.status.visible = False
+        else:
+            self.console.focused = False
+            self.console.visible = False
+            self.status.visible = True
+
+        self.draw()
+        self.finalize()
+
+    def redraw_window(self):
+        """Redraw the window. This only calls self.win.redrawwin()."""
+        self.win.erase()
+        self.win.redrawwin()
+        self.win.refresh()
+        self.win.redrawwin()
+        self.need_redraw = True
+
+    def update_size(self):
+        """resize all widgets"""
+        self.termsize = self.win.getmaxyx()
+        y, x = self.termsize
+
+        self.browser.resize(self.settings.status_bar_on_top and 2 or 1, 0, y - 2, x)
+        self.taskview.resize(1, 0, y - 2, x)
+        self.pager.resize(1, 0, y - 2, x)
+        self.titlebar.resize(0, 0, 1, x)
+        self.status.resize(self.settings.status_bar_on_top and 1 or y-1, 0, 1, x)
+        self.console.resize(y - 1, 0, 1, x)
+
+    def draw(self):
+        """Draw all objects in the container"""
+        self.win.touchwin()
+        DisplayableContainer.draw(self)
+        if self._draw_title and self.settings.update_title:
+            cwd = self.fm.thisdir.path
+            if cwd.startswith(self.fm.home_path):
+                cwd = '~' + cwd[len(self.fm.home_path):]
+            if self.settings.shorten_title:
+                split = cwd.rsplit(os.sep, self.settings.shorten_title)
+                if os.sep in split[0]:
+                    cwd = os.sep.join(split[1:])
+            try:
+                fixed_cwd = cwd.encode('utf-8', 'surrogateescape'). \
+                        decode('utf-8', 'replace')
+                sys.stdout.write("%sranger:%s%s" %
+                        (curses.tigetstr('tsl').decode('latin-1'), fixed_cwd,
+                         curses.tigetstr('fsl').decode('latin-1')))
+                sys.stdout.flush()
+            except:
+                pass
+
+        self.win.refresh()
+
+    def finalize(self):
+        """Finalize every object in container and refresh the window"""
+        DisplayableContainer.finalize(self)
+        self.win.refresh()
+
+    def close_pager(self):
+        if self.console.visible:
+            self.console.focused = True
+        self.pager.close()
+        self.pager.visible = False
+        self.pager.focused = False
+        self.browser.visible = True
+
+    def open_pager(self):
+        if self.console.focused:
+            self.console.focused = False
+        self.pager.open()
+        self.pager.visible = True
+        self.pager.focused = True
+        self.browser.visible = False
+        return self.pager
+
+    def open_embedded_pager(self):
+        self.browser.open_pager()
+        for column in self.browser.columns:
+            if column == self.browser.main_column:
+                break
+            column.level_shift(amount=1)
+        return self.browser.pager
+
+    def close_embedded_pager(self):
+        self.browser.close_pager()
+        for column in self.browser.columns:
+            column.level_restore()
+
+    def open_console(self, string='', prompt=None, position=None):
+        if self.console.open(string, prompt=prompt, position=position):
+            self.status.msg = None
+
+    def close_console(self):
+        self.console.close()
+        self.close_pager()
+
+    def open_taskview(self):
+        self.pager.close()
+        self.pager.visible = False
+        self.pager.focused = False
+        self.console.visible = False
+        self.browser.visible = False
+        self.taskview.visible = True
+        self.taskview.focused = True
+
+    def redraw_main_column(self):
+        self.browser.main_column.need_redraw = True
+
+    def close_taskview(self):
+        self.taskview.visible = False
+        self.browser.visible = True
+        self.taskview.focused = False
+
+    def throbber(self, string='.', remove=False):
+        if remove:
+            self.titlebar.throbber = type(self.titlebar).throbber
+        else:
+            self.titlebar.throbber = string
+
+    def hint(self, text=None):
+        self.status.hint = text
diff --git a/ranger/gui/widgets/__init__.py b/ranger/gui/widgets/__init__.py
index 82e592ee..2a930b6c 100644
--- a/ranger/gui/widgets/__init__.py
+++ b/ranger/gui/widgets/__init__.py
@@ -1,7 +1,7 @@
 from ranger.gui.displayable import Displayable
 
 class Widget(Displayable):
-	"""
-	The Widget class defines no methods and only exists for
-	classification of widgets.
-	"""
+    """
+    The Widget class defines no methods and only exists for
+    classification of widgets.
+    """
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 3324d9a8..a6653070 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -13,382 +13,382 @@ from ranger.fsobject import BAD_INFO
 from ranger.ext.widestring import WideString
 
 class BrowserColumn(Pager):
-	main_column = False
-	display_infostring = False
-	scroll_begin = 0
-	target = None
-	last_redraw_time = -1
-	ellipsis = { False: '~', True: '…' }
-
-	old_dir = None
-	old_thisfile = None
-
-	def __init__(self, win, level):
-		"""
-		win = the curses window object of the BrowserView
-		level = what to display?
-
-		level >0 => previews
-		level 0 => current file/directory
-		level <0 => parent directories
-		"""
-		Pager.__init__(self, win)
-		Widget.__init__(self, win)
-		self.level = level
-		self.original_level = level
-
-		self.settings.signal_bind('setopt.display_size_in_main_column',
-				self.request_redraw, weak=True)
-
-	def request_redraw(self):
-		self.need_redraw = True
-
-	def resize(self, y, x, hei, wid):
-		Widget.resize(self, y, x, hei, wid)
-
-	def click(self, event):
-		"""Handle a MouseEvent"""
-		direction = event.mouse_wheel_direction()
-		if not (event.pressed(1) or event.pressed(3) or direction):
-			return False
-
-		if self.target is None:
-			pass
-
-		elif self.target.is_directory:
-			if self.target.accessible and self.target.content_loaded:
-				index = self.scroll_begin + event.y - self.y
-
-				if direction:
-					if self.level == -1:
-						self.fm.move_parent(direction)
-					else:
-						return False
-				elif event.pressed(1):
-					if not self.main_column:
-						self.fm.enter_dir(self.target.path)
-
-					if index < len(self.target):
-						self.fm.move(to=index)
-				elif event.pressed(3):
-					try:
-						clicked_file = self.target.files[index]
-						if clicked_file.is_directory:
-							self.fm.enter_dir(clicked_file.path)
-						elif self.level == 0:
-							self.fm.thisdir.move_to_obj(clicked_file)
-							self.fm.execute_file(clicked_file)
-					except:
-						pass
-
-		else:
-			if self.level > 0 and not direction:
-				self.fm.move(right=0)
-
-		return True
-
-	def execute_curses_batch(self, line, commands):
-		"""
-		Executes a list of "commands" which can be easily cached.
-
-		"commands" is a list of lists.  Each element contains
-		a text and an attribute.  First, the attribute will be
-		set with attrset, then the text is printed.
-
-		Example:
-		execute_curses_batch(0, [["hello ", 0], ["world", curses.A_BOLD]])
-		"""
-		try:
-			self.win.move(line, 0)
-		except:
-			return
-		for entry in commands:
-			text, attr = entry
-			self.addstr(text, attr)
-
-	def has_preview(self):
-		if self.target is None:
-			return False
-
-		if self.target.is_file:
-			if not self.target.has_preview():
-				return False
-
-		if self.target.is_directory:
-			if self.level > 0 and not self.settings.preview_directories:
-				return False
-
-		return True
-
-	def level_shift(self, amount):
-		self.level = self.original_level + amount
-
-	def level_restore(self):
-		self.level = self.original_level
-
-	def poke(self):
-		Widget.poke(self)
-		self.target = self.fm.thistab.at_level(self.level)
-
-	def draw(self):
-		"""Call either _draw_file() or _draw_directory()"""
-		if self.target != self.old_dir:
-			self.need_redraw = True
-			self.old_dir = self.target
-
-		if self.target:  # don't garbage collect this directory please
-			self.target.use()
-
-		if self.target and self.target.is_directory \
-				and (self.level <= 0 or self.settings.preview_directories):
-			if self.target.pointed_obj != self.old_thisfile:
-				self.need_redraw = True
-				self.old_thisfile = self.target.pointed_obj
-
-			if self.target.load_content_if_outdated() \
-			or self.target.sort_if_outdated() \
-			or self.last_redraw_time < self.target.last_update_time:
-				self.need_redraw = True
-
-		if self.need_redraw:
-			self.win.erase()
-			if self.target is None:
-				pass
-			elif self.target.is_file:
-				Pager.open(self)
-				self._draw_file()
-			elif self.target.is_directory:
-				self._draw_directory()
-				Widget.draw(self)
-			self.need_redraw = False
-			self.last_redraw_time = time()
-
-	def _draw_file(self):
-		"""Draw a preview of the file, if the settings allow it"""
-		self.win.move(0, 0)
-		if not self.target.accessible:
-			self.addnstr("not accessible", self.wid)
-			Pager.close(self)
-			return
-
-		if self.target is None or not self.target.has_preview():
-			Pager.close(self)
-			return
-
-		if self.fm.settings.preview_images and self.target.image:
-			self.set_image(self.target.realpath)
-			Pager.draw(self)
-		else:
-			f = self.target.get_preview_source(self.wid, self.hei)
-			if f is None:
-				Pager.close(self)
-			else:
-				self.set_source(f)
-				Pager.draw(self)
-
-	def _draw_directory(self):
-		"""Draw the contents of a directory"""
-		if self.image:
-			self.image = None
-			self.need_clear_image = True
-			Pager.clear_image(self)
-
-		if self.level > 0 and not self.settings.preview_directories:
-			return
-
-		base_color = ['in_browser']
-
-		self.win.move(0, 0)
-
-		if not self.target.content_loaded:
-			self.color(tuple(base_color))
-			self.addnstr("...", self.wid)
-			self.color_reset()
-			return
-
-		if self.main_column:
-			base_color.append('main_column')
-
-		if not self.target.accessible:
-			self.color(tuple(base_color + ['error']))
-			self.addnstr("not accessible", self.wid)
-			self.color_reset()
-			return
-
-		if self.target.empty():
-			self.color(tuple(base_color + ['empty']))
-			self.addnstr("empty", self.wid)
-			self.color_reset()
-			return
-
-		self._set_scroll_begin()
-
-		copied = [f.path for f in self.fm.copy_buffer]
-		ellipsis = self.ellipsis[self.settings.unicode_ellipsis]
-
-		selected_i = self.target.pointer
-		for line in range(self.hei):
-			i = line + self.scroll_begin
-			if line > self.hei:
-				break
-
-			try:
-				drawn = self.target.files[i]
-			except IndexError:
-				break
-
-			tagged = self.fm.tags and drawn.realpath in self.fm.tags
-			if tagged:
-				tagged_marker = self.fm.tags.marker(drawn.realpath)
-			else:
-				tagged_marker = " "
-
-			key = (self.wid, selected_i == i, drawn.marked, self.main_column,
-					drawn.path in copied, tagged_marker, drawn.infostring,
-					self.fm.do_cut)
-
-			if key in drawn.display_data:
-				self.execute_curses_batch(line, drawn.display_data[key])
-				self.color_reset()
-				continue
-
-			display_data = []
-			drawn.display_data[key] = display_data
-
-			if self.display_infostring and drawn.infostring \
-					and self.settings.display_size_in_main_column:
-				infostring = str(drawn.infostring) + " "
-			else:
-				infostring = ""
-
-			this_color = base_color + list(drawn.mimetype_tuple)
-			text = drawn.basename
-
-			space = self.wid - len(infostring)
-			if self.main_column:
-				space -= 2
-			elif self.settings.display_tags_in_all_columns:
-				space -= 1
-
-			if i == selected_i:
-				this_color.append('selected')
-
-			if drawn.marked:
-				this_color.append('marked')
-				if self.main_column or self.settings.display_tags_in_all_columns:
-					text = " " + text
-
-			if tagged:
-				this_color.append('tagged')
-
-			if drawn.is_directory:
-				this_color.append('directory')
-			else:
-				this_color.append('file')
-
-			if drawn.stat:
-				mode = drawn.stat.st_mode
-				if mode & stat.S_IXUSR:
-					this_color.append('executable')
-				if stat.S_ISFIFO(mode):
-					this_color.append('fifo')
-				if stat.S_ISSOCK(mode):
-					this_color.append('socket')
-				if drawn.is_device:
-					this_color.append('device')
-
-			if drawn.path in copied:
-				this_color.append('cut' if self.fm.do_cut else 'copied')
-
-			if drawn.is_link:
-				this_color.append('link')
-				this_color.append(drawn.exists and 'good' or 'bad')
-
-			attr = self.settings.colorscheme.get_attr(*this_color)
-
-			if (self.main_column or self.settings.display_tags_in_all_columns) \
-					and tagged and self.wid > 2:
-				this_color.append('tag_marker')
-				tag_attr = self.settings.colorscheme.get_attr(*this_color)
-				display_data.append([tagged_marker, tag_attr])
-			else:
-				text = " " + text
-				space += 1
-
-			wtext = WideString(text)
-			if len(wtext) > space:
-				wtext = wtext[:max(0, space - 1)] + ellipsis
-			text = str(wtext)
-
-			display_data.append([text, attr])
-
-			padding = self.wid - len(wtext)
-			if tagged and (self.main_column or \
-					self.settings.display_tags_in_all_columns):
-				padding -= 1
-			if infostring:
-				if len(wtext) + 1 + len(infostring) > self.wid:
-					pass
-				else:
-					padding -= len(infostring)
-					padding = max(0, padding)
-					infostring = (" " * padding) + infostring
-					display_data.append([infostring, attr])
-			else:
-				display_data.append([" " * max(0, padding), attr])
-
-			self.execute_curses_batch(line, display_data)
-			self.color_reset()
-
-	def _get_scroll_begin(self):
-		"""Determines scroll_begin (the position of the first displayed file)"""
-		offset = self.settings.scroll_offset
-		dirsize = len(self.target)
-		winsize = self.hei
-		halfwinsize = winsize // 2
-		index = self.target.pointer or 0
-		original = self.target.scroll_begin
-		projected = index - original
-
-		upper_limit = winsize - 1 - offset
-		lower_limit = offset
-
-		if original < 0:
-			return 0
-
-		if dirsize < winsize:
-			return 0
-
-		if halfwinsize < offset:
-			return min( dirsize - winsize, max( 0, index - halfwinsize ))
-
-		if original > dirsize - winsize:
-			self.target.scroll_begin = dirsize - winsize
-			return self._get_scroll_begin()
-
-		if projected < upper_limit and projected > lower_limit:
-			return original
-
-		if projected > upper_limit:
-			return min( dirsize - winsize,
-					original + (projected - upper_limit))
-
-		if projected < upper_limit:
-			return max( 0,
-					original - (lower_limit - projected))
-
-		return original
-
-	def _set_scroll_begin(self):
-		"""Updates the scroll_begin value"""
-		self.scroll_begin = self._get_scroll_begin()
-		self.target.scroll_begin = self.scroll_begin
-
-	def scroll(self, n):
-		"""scroll down by n lines"""
-		self.need_redraw = True
-		self.target.move(down=n)
-		self.target.scroll_begin += 3 * n
-
-	def __str__(self):
-		return self.__class__.__name__ + ' at level ' + str(self.level)
+    main_column = False
+    display_infostring = False
+    scroll_begin = 0
+    target = None
+    last_redraw_time = -1
+    ellipsis = { False: '~', True: '…' }
+
+    old_dir = None
+    old_thisfile = None
+
+    def __init__(self, win, level):
+        """
+        win = the curses window object of the BrowserView
+        level = what to display?
+
+        level >0 => previews
+        level 0 => current file/directory
+        level <0 => parent directories
+        """
+        Pager.__init__(self, win)
+        Widget.__init__(self, win)
+        self.level = level
+        self.original_level = level
+
+        self.settings.signal_bind('setopt.display_size_in_main_column',
+                self.request_redraw, weak=True)
+
+    def request_redraw(self):
+        self.need_redraw = True
+
+    def resize(self, y, x, hei, wid):
+        Widget.resize(self, y, x, hei, wid)
+
+    def click(self, event):
+        """Handle a MouseEvent"""
+        direction = event.mouse_wheel_direction()
+        if not (event.pressed(1) or event.pressed(3) or direction):
+            return False
+
+        if self.target is None:
+            pass
+
+        elif self.target.is_directory:
+            if self.target.accessible and self.target.content_loaded:
+                index = self.scroll_begin + event.y - self.y
+
+                if direction:
+                    if self.level == -1:
+                        self.fm.move_parent(direction)
+                    else:
+                        return False
+                elif event.pressed(1):
+                    if not self.main_column:
+                        self.fm.enter_dir(self.target.path)
+
+                    if index < len(self.target):
+                        self.fm.move(to=index)
+                elif event.pressed(3):
+                    try:
+                        clicked_file = self.target.files[index]
+                        if clicked_file.is_directory:
+                            self.fm.enter_dir(clicked_file.path)
+                        elif self.level == 0:
+                            self.fm.thisdir.move_to_obj(clicked_file)
+                            self.fm.execute_file(clicked_file)
+                    except:
+                        pass
+
+        else:
+            if self.level > 0 and not direction:
+                self.fm.move(right=0)
+
+        return True
+
+    def execute_curses_batch(self, line, commands):
+        """
+        Executes a list of "commands" which can be easily cached.
+
+        "commands" is a list of lists.  Each element contains
+        a text and an attribute.  First, the attribute will be
+        set with attrset, then the text is printed.
+
+        Example:
+        execute_curses_batch(0, [["hello ", 0], ["world", curses.A_BOLD]])
+        """
+        try:
+            self.win.move(line, 0)
+        except:
+            return
+        for entry in commands:
+            text, attr = entry
+            self.addstr(text, attr)
+
+    def has_preview(self):
+        if self.target is None:
+            return False
+
+        if self.target.is_file:
+            if not self.target.has_preview():
+                return False
+
+        if self.target.is_directory:
+            if self.level > 0 and not self.settings.preview_directories:
+                return False
+
+        return True
+
+    def level_shift(self, amount):
+        self.level = self.original_level + amount
+
+    def level_restore(self):
+        self.level = self.original_level
+
+    def poke(self):
+        Widget.poke(self)
+        self.target = self.fm.thistab.at_level(self.level)
+
+    def draw(self):
+        """Call either _draw_file() or _draw_directory()"""
+        if self.target != self.old_dir:
+            self.need_redraw = True
+            self.old_dir = self.target
+
+        if self.target:  # don't garbage collect this directory please
+            self.target.use()
+
+        if self.target and self.target.is_directory \
+                and (self.level <= 0 or self.settings.preview_directories):
+            if self.target.pointed_obj != self.old_thisfile:
+                self.need_redraw = True
+                self.old_thisfile = self.target.pointed_obj
+
+            if self.target.load_content_if_outdated() \
+            or self.target.sort_if_outdated() \
+            or self.last_redraw_time < self.target.last_update_time:
+                self.need_redraw = True
+
+        if self.need_redraw:
+            self.win.erase()
+            if self.target is None:
+                pass
+            elif self.target.is_file:
+                Pager.open(self)
+                self._draw_file()
+            elif self.target.is_directory:
+                self._draw_directory()
+                Widget.draw(self)
+            self.need_redraw = False
+            self.last_redraw_time = time()
+
+    def _draw_file(self):
+        """Draw a preview of the file, if the settings allow it"""
+        self.win.move(0, 0)
+        if not self.target.accessible:
+            self.addnstr("not accessible", self.wid)
+            Pager.close(self)
+            return
+
+        if self.target is None or not self.target.has_preview():
+            Pager.close(self)
+            return
+
+        if self.fm.settings.preview_images and self.target.image:
+            self.set_image(self.target.realpath)
+            Pager.draw(self)
+        else:
+            f = self.target.get_preview_source(self.wid, self.hei)
+            if f is None:
+                Pager.close(self)
+            else:
+                self.set_source(f)
+                Pager.draw(self)
+
+    def _draw_directory(self):
+        """Draw the contents of a directory"""
+        if self.image:
+            self.image = None
+            self.need_clear_image = True
+            Pager.clear_image(self)
+
+        if self.level > 0 and not self.settings.preview_directories:
+            return
+
+        base_color = ['in_browser']
+
+        self.win.move(0, 0)
+
+        if not self.target.content_loaded:
+            self.color(tuple(base_color))
+            self.addnstr("...", self.wid)
+            self.color_reset()
+            return
+
+        if self.main_column:
+            base_color.append('main_column')
+
+        if not self.target.accessible:
+            self.color(tuple(base_color + ['error']))
+            self.addnstr("not accessible", self.wid)
+            self.color_reset()
+            return
+
+        if self.target.empty():
+            self.color(tuple(base_color + ['empty']))
+            self.addnstr("empty", self.wid)
+            self.color_reset()
+            return
+
+        self._set_scroll_begin()
+
+        copied = [f.path for f in self.fm.copy_buffer]
+        ellipsis = self.ellipsis[self.settings.unicode_ellipsis]
+
+        selected_i = self.target.pointer
+        for line in range(self.hei):
+            i = line + self.scroll_begin
+            if line > self.hei:
+                break
+
+            try:
+                drawn = self.target.files[i]
+            except IndexError:
+                break
+
+            tagged = self.fm.tags and drawn.realpath in self.fm.tags
+            if tagged:
+                tagged_marker = self.fm.tags.marker(drawn.realpath)
+            else:
+                tagged_marker = " "
+
+            key = (self.wid, selected_i == i, drawn.marked, self.main_column,
+                    drawn.path in copied, tagged_marker, drawn.infostring,
+                    self.fm.do_cut)
+
+            if key in drawn.display_data:
+                self.execute_curses_batch(line, drawn.display_data[key])
+                self.color_reset()
+                continue
+
+            display_data = []
+            drawn.display_data[key] = display_data
+
+            if self.display_infostring and drawn.infostring \
+                    and self.settings.display_size_in_main_column:
+                infostring = str(drawn.infostring) + " "
+            else:
+                infostring = ""
+
+            this_color = base_color + list(drawn.mimetype_tuple)
+            text = drawn.basename
+
+            space = self.wid - len(infostring)
+            if self.main_column:
+                space -= 2
+            elif self.settings.display_tags_in_all_columns:
+                space -= 1
+
+            if i == selected_i:
+                this_color.append('selected')
+
+            if drawn.marked:
+                this_color.append('marked')
+                if self.main_column or self.settings.display_tags_in_all_columns:
+                    text = " " + text
+
+            if tagged:
+                this_color.append('tagged')
+
+            if drawn.is_directory:
+                this_color.append('directory')
+            else:
+                this_color.append('file')
+
+            if drawn.stat:
+                mode = drawn.stat.st_mode
+                if mode & stat.S_IXUSR:
+                    this_color.append('executable')
+                if stat.S_ISFIFO(mode):
+                    this_color.append('fifo')
+                if stat.S_ISSOCK(mode):
+                    this_color.append('socket')
+                if drawn.is_device:
+                    this_color.append('device')
+
+            if drawn.path in copied:
+                this_color.append('cut' if self.fm.do_cut else 'copied')
+
+            if drawn.is_link:
+                this_color.append('link')
+                this_color.append(drawn.exists and 'good' or 'bad')
+
+            attr = self.settings.colorscheme.get_attr(*this_color)
+
+            if (self.main_column or self.settings.display_tags_in_all_columns) \
+                    and tagged and self.wid > 2:
+                this_color.append('tag_marker')
+                tag_attr = self.settings.colorscheme.get_attr(*this_color)
+                display_data.append([tagged_marker, tag_attr])
+            else:
+                text = " " + text
+                space += 1
+
+            wtext = WideString(text)
+            if len(wtext) > space:
+                wtext = wtext[:max(0, space - 1)] + ellipsis
+            text = str(wtext)
+
+            display_data.append([text, attr])
+
+            padding = self.wid - len(wtext)
+            if tagged and (self.main_column or \
+                    self.settings.display_tags_in_all_columns):
+                padding -= 1
+            if infostring:
+                if len(wtext) + 1 + len(infostring) > self.wid:
+                    pass
+                else:
+                    padding -= len(infostring)
+                    padding = max(0, padding)
+                    infostring = (" " * padding) + infostring
+                    display_data.append([infostring, attr])
+            else:
+                display_data.append([" " * max(0, padding), attr])
+
+            self.execute_curses_batch(line, display_data)
+            self.color_reset()
+
+    def _get_scroll_begin(self):
+        """Determines scroll_begin (the position of the first displayed file)"""
+        offset = self.settings.scroll_offset
+        dirsize = len(self.target)
+        winsize = self.hei
+        halfwinsize = winsize // 2
+        index = self.target.pointer or 0
+        original = self.target.scroll_begin
+        projected = index - original
+
+        upper_limit = winsize - 1 - offset
+        lower_limit = offset
+
+        if original < 0:
+            return 0
+
+        if dirsize < winsize:
+            return 0
+
+        if halfwinsize < offset:
+            return min( dirsize - winsize, max( 0, index - halfwinsize ))
+
+        if original > dirsize - winsize:
+            self.target.scroll_begin = dirsize - winsize
+            return self._get_scroll_begin()
+
+        if projected < upper_limit and projected > lower_limit:
+            return original
+
+        if projected > upper_limit:
+            return min( dirsize - winsize,
+                    original + (projected - upper_limit))
+
+        if projected < upper_limit:
+            return max( 0,
+                    original - (lower_limit - projected))
+
+        return original
+
+    def _set_scroll_begin(self):
+        """Updates the scroll_begin value"""
+        self.scroll_begin = self._get_scroll_begin()
+        self.target.scroll_begin = self.scroll_begin
+
+    def scroll(self, n):
+        """scroll down by n lines"""
+        self.need_redraw = True
+        self.target.move(down=n)
+        self.target.scroll_begin += 3 * n
+
+    def __str__(self):
+        return self.__class__.__name__ + ' at level ' + str(self.level)
diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py
index e4ab2166..742bd9d4 100644
--- a/ranger/gui/widgets/browserview.py
+++ b/ranger/gui/widgets/browserview.py
@@ -11,334 +11,334 @@ from .pager import Pager
 from ..displayable import DisplayableContainer
 
 class BrowserView(Widget, DisplayableContainer):
-	ratios = None
-	preview = True
-	is_collapsed = False
-	draw_bookmarks = False
-	stretch_ratios = None
-	need_clear = False
-	old_collapse = False
-	draw_hints = False
-	draw_info = False
-
-	def __init__(self, win, ratios, preview = True):
-		DisplayableContainer.__init__(self, win)
-		self.preview = preview
-		self.columns = []
-
-		self.pager = Pager(self.win, embedded=True)
-		self.pager.visible = False
-		self.add_child(self.pager)
-
-		self.change_ratios(ratios)
-
-		for option in ('preview_directories', 'preview_files'):
-			self.settings.signal_bind('setopt.' + option,
-					self._request_clear_if_has_borders, weak=True)
-
-		self.fm.signal_bind('move', self.request_clear)
-		self.settings.signal_bind('setopt.column_ratios', self.request_clear)
-
-	def change_ratios(self, ratios):
-		if isinstance(ratios, Signal):
-			ratios = ratios.value
-
-		for column in self.columns:
-			column.destroy()
-			self.remove_child(column)
-		self.columns = []
-
-		ratio_sum = float(sum(ratios))
-		self.ratios = tuple(x / ratio_sum for x in ratios)
-
-		last = 0.1 if self.settings.padding_right else 0
-		if len(self.ratios) >= 2:
-			self.stretch_ratios = self.ratios[:-2] + \
-					((self.ratios[-2] + self.ratios[-1] * 1.0 - last),
-					(self.ratios[-1] * last))
-
-		offset = 1 - len(ratios)
-		if self.preview: offset += 1
-
-		for level in range(len(ratios)):
-			fl = BrowserColumn(self.win, level + offset)
-			self.add_child(fl)
-			self.columns.append(fl)
-
-		try:
-			self.main_column = self.columns[self.preview and -2 or -1]
-		except IndexError:
-			self.main_column = None
-		else:
-			self.main_column.display_infostring = True
-			self.main_column.main_column = True
-
-		self.resize(self.y, self.x, self.hei, self.wid)
-
-	def _request_clear_if_has_borders(self):
-		if self.settings.draw_borders:
-			self.request_clear()
-
-	def request_clear(self):
-		self.need_clear = True
-
-	def draw(self):
-		if self.need_clear:
-			self.win.erase()
-			self.need_redraw = True
-			self.need_clear = False
-		for tab in self.fm.tabs.values():
-			directory = tab.thisdir
-			if directory:
-				directory.load_content_if_outdated()
-				directory.use()
-		DisplayableContainer.draw(self)
-		if self.settings.draw_borders:
-			self._draw_borders()
-		if self.draw_bookmarks:
-			self._draw_bookmarks()
-		elif self.draw_hints:
-			self._draw_hints()
-		elif self.draw_info:
-			self._draw_info(self.draw_info)
-
-	def finalize(self):
-		if self.pager.visible:
-			try:
-				self.fm.ui.win.move(self.main_column.y, self.main_column.x)
-			except:
-				pass
-			self.pager.draw_image()
-		else:
-			try:
-				x = self.main_column.x
-				y = self.main_column.y + self.main_column.target.pointer\
-						- self.main_column.scroll_begin
-				self.fm.ui.win.move(y, x)
-			except:
-				pass
-			self.columns[-1].draw_image()
-
-	def _draw_borders(self):
-		win = self.win
-		self.color('in_browser', 'border')
-
-		left_start = 0
-		right_end = self.wid - 1
-
-		for child in self.columns:
-			if not child.has_preview():
-				left_start = child.x + child.wid
-			else:
-				break
-		if not self.pager.visible:
-			for child in reversed(self.columns):
-				if not child.has_preview():
-					right_end = child.x - 1
-				else:
-					break
-			if right_end < left_start:
-				right_end = self.wid - 1
-
-		try:
-			win.hline(0, left_start, curses.ACS_HLINE, right_end - left_start)
-			win.hline(self.hei - 1, left_start, curses.ACS_HLINE,
-					right_end - left_start)
-			win.vline(1, left_start, curses.ACS_VLINE, self.hei - 2)
-		except _curses.error:
-			pass
-
-		for child in self.columns:
-			if not child.has_preview():
-				continue
-			if child.main_column and self.pager.visible:
-				win.vline(1, right_end, curses.ACS_VLINE, self.hei - 2)
-				break
-			x = child.x + child.wid
-			y = self.hei - 1
-			try:
-				win.vline(1, x, curses.ACS_VLINE, y - 1)
-				win.addch(0, x, curses.ACS_TTEE, 0)
-				win.addch(y, x, curses.ACS_BTEE, 0)
-			except:
-				# in case it's off the boundaries
-				pass
-
-		self.addch(0, left_start, curses.ACS_ULCORNER)
-		self.addch(self.hei - 1, left_start, curses.ACS_LLCORNER)
-		self.addch(0, right_end, curses.ACS_URCORNER)
-		self.addch(self.hei - 1, right_end, curses.ACS_LRCORNER)
-
-	def _draw_bookmarks(self):
-		self.fm.bookmarks.update_if_outdated()
-		self.color_reset()
-		self.need_clear = True
-
-		sorted_bookmarks = sorted((item for item in self.fm.bookmarks \
-			if self.fm.settings.show_hidden_bookmarks or \
-			'/.' not in item[1].path), key=lambda t: t[0].lower())
-
-		hei = min(self.hei - 1, len(sorted_bookmarks))
-		ystart = self.hei - hei
-
-		maxlen = self.wid
-		self.addnstr(ystart - 1, 0, "mark  path".ljust(self.wid), self.wid)
-
-		whitespace = " " * maxlen
-		for line, items in zip(range(self.hei-1), sorted_bookmarks):
-			key, mark = items
-			string = " " + key + "   " + mark.path
-			self.addstr(ystart + line, 0, whitespace)
-			self.addnstr(ystart + line, 0, string, self.wid)
-
-		self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
-
-	def _draw_info(self, lines):
-		self.need_clear = True
-		hei = min(self.hei - 1, len(lines))
-		ystart = self.hei - hei
-		i = ystart
-		whitespace = " " * self.wid
-		for line in lines:
-			if i >= self.hei:
-				break
-			self.addstr(i, 0, whitespace)
-			self.addnstr(i, 0, line, self.wid)
-			i += 1
-
-	def _draw_hints(self):
-		self.need_clear = True
-		hints = []
-		for k, v in self.fm.ui.keybuffer.pointer.items():
-			k = key_to_string(k)
-			if isinstance(v, dict):
-				text = '...'
-			else:
-				text = v
-			if text.startswith('hint') or text.startswith('chain hint'):
-				continue
-			hints.append((k, text))
-		hints.sort(key=lambda t: t[1])
-
-		hei = min(self.hei - 1, len(hints))
-		ystart = self.hei - hei
-		self.addnstr(ystart - 1, 0, "key          command".ljust(self.wid),
-				self.wid)
-		try:
-			self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
-		except:
-			pass
-		whitespace = " " * self.wid
-		i = ystart
-		for key, cmd in hints:
-			string = " " + key.ljust(11) + " " + cmd
-			self.addstr(i, 0, whitespace)
-			self.addnstr(i, 0, string, self.wid)
-			i += 1
-
-	def _collapse(self):
-		# Should the last column be cut off? (Because there is no preview)
-		if not self.settings.collapse_preview or not self.preview \
-				or not self.stretch_ratios:
-			return False
-		result = not self.columns[-1].has_preview()
-		target = self.columns[-1].target
-		if not result and target and target.is_file:
-			if target.image and self.fm.settings.preview_images:
-				result = False  # don't collapse when drawing images
-			elif self.fm.settings.preview_script and \
-					self.fm.settings.use_preview_script:
-				try:
-					result = not self.fm.previews[target.realpath]['foundpreview']
-				except:
-					return self.old_collapse
-
-		self.old_collapse = result
-		return result
-
-	def resize(self, y, x, hei, wid):
-		"""Resize all the columns according to the given ratio"""
-		DisplayableContainer.resize(self, y, x, hei, wid)
-		borders = self.settings.draw_borders
-		pad = 1 if borders else 0
-		left = pad
-
-		self.is_collapsed = self._collapse()
-		if self.is_collapsed:
-			generator = enumerate(self.stretch_ratios)
-		else:
-			generator = enumerate(self.ratios)
-
-		last_i = len(self.ratios) - 1
-
-		for i, ratio in generator:
-			wid = int(ratio * self.wid)
-
-			cut_off = self.is_collapsed and not self.settings.padding_right
-			if i == last_i:
-				if not cut_off:
-					wid = int(self.wid - left + 1 - pad)
-				else:
-					self.columns[i].resize(pad, left - 1, hei - pad * 2, 1)
-					self.columns[i].visible = False
-					continue
-
-			if i == last_i - 1:
-				self.pager.resize(pad, left, hei - pad * 2, \
-						max(1, self.wid - left - pad))
-
-				if cut_off:
-					self.columns[i].resize(pad, left, hei - pad * 2, \
-							max(1, self.wid - left - pad))
-					continue
-
-			try:
-				self.columns[i].resize(pad, left, hei - pad * 2, \
-						max(1, wid - 1))
-			except KeyError:
-				pass
-
-			left += wid
-
-	def click(self, event):
-		if DisplayableContainer.click(self, event):
-			return True
-		direction = event.mouse_wheel_direction()
-		if direction:
-			self.main_column.scroll(direction)
-		return False
-
-	def open_pager(self):
-		self.pager.visible = True
-		self.pager.focused = True
-		self.need_clear = True
-		self.pager.open()
-		try:
-			self.columns[-1].visible = False
-			self.columns[-2].visible = False
-		except IndexError:
-			pass
-
-	def close_pager(self):
-		self.pager.visible = False
-		self.pager.focused = False
-		self.need_clear = True
-		self.pager.close()
-		try:
-			self.columns[-1].visible = True
-			self.columns[-2].visible = True
-		except IndexError:
-			pass
-
-	def poke(self):
-		DisplayableContainer.poke(self)
-
-		# 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:
-			self.columns[-1].visible = True
-
-		if self.preview and self.is_collapsed != self._collapse():
-			self.resize(self.y, self.x, self.hei, self.wid)
+    ratios = None
+    preview = True
+    is_collapsed = False
+    draw_bookmarks = False
+    stretch_ratios = None
+    need_clear = False
+    old_collapse = False
+    draw_hints = False
+    draw_info = False
+
+    def __init__(self, win, ratios, preview = True):
+        DisplayableContainer.__init__(self, win)
+        self.preview = preview
+        self.columns = []
+
+        self.pager = Pager(self.win, embedded=True)
+        self.pager.visible = False
+        self.add_child(self.pager)
+
+        self.change_ratios(ratios)
+
+        for option in ('preview_directories', 'preview_files'):
+            self.settings.signal_bind('setopt.' + option,
+                    self._request_clear_if_has_borders, weak=True)
+
+        self.fm.signal_bind('move', self.request_clear)
+        self.settings.signal_bind('setopt.column_ratios', self.request_clear)
+
+    def change_ratios(self, ratios):
+        if isinstance(ratios, Signal):
+            ratios = ratios.value
+
+        for column in self.columns:
+            column.destroy()
+            self.remove_child(column)
+        self.columns = []
+
+        ratio_sum = float(sum(ratios))
+        self.ratios = tuple(x / ratio_sum for x in ratios)
+
+        last = 0.1 if self.settings.padding_right else 0
+        if len(self.ratios) >= 2:
+            self.stretch_ratios = self.ratios[:-2] + \
+                    ((self.ratios[-2] + self.ratios[-1] * 1.0 - last),
+                    (self.ratios[-1] * last))
+
+        offset = 1 - len(ratios)
+        if self.preview: offset += 1
+
+        for level in range(len(ratios)):
+            fl = BrowserColumn(self.win, level + offset)
+            self.add_child(fl)
+            self.columns.append(fl)
+
+        try:
+            self.main_column = self.columns[self.preview and -2 or -1]
+        except IndexError:
+            self.main_column = None
+        else:
+            self.main_column.display_infostring = True
+            self.main_column.main_column = True
+
+        self.resize(self.y, self.x, self.hei, self.wid)
+
+    def _request_clear_if_has_borders(self):
+        if self.settings.draw_borders:
+            self.request_clear()
+
+    def request_clear(self):
+        self.need_clear = True
+
+    def draw(self):
+        if self.need_clear:
+            self.win.erase()
+            self.need_redraw = True
+            self.need_clear = False
+        for tab in self.fm.tabs.values():
+            directory = tab.thisdir
+            if directory:
+                directory.load_content_if_outdated()
+                directory.use()
+        DisplayableContainer.draw(self)
+        if self.settings.draw_borders:
+            self._draw_borders()
+        if self.draw_bookmarks:
+            self._draw_bookmarks()
+        elif self.draw_hints:
+            self._draw_hints()
+        elif self.draw_info:
+            self._draw_info(self.draw_info)
+
+    def finalize(self):
+        if self.pager.visible:
+            try:
+                self.fm.ui.win.move(self.main_column.y, self.main_column.x)
+            except:
+                pass
+            self.pager.draw_image()
+        else:
+            try:
+                x = self.main_column.x
+                y = self.main_column.y + self.main_column.target.pointer\
+                        - self.main_column.scroll_begin
+                self.fm.ui.win.move(y, x)
+            except:
+                pass
+            self.columns[-1].draw_image()
+
+    def _draw_borders(self):
+        win = self.win
+        self.color('in_browser', 'border')
+
+        left_start = 0
+        right_end = self.wid - 1
+
+        for child in self.columns:
+            if not child.has_preview():
+                left_start = child.x + child.wid
+            else:
+                break
+        if not self.pager.visible:
+            for child in reversed(self.columns):
+                if not child.has_preview():
+                    right_end = child.x - 1
+                else:
+                    break
+            if right_end < left_start:
+                right_end = self.wid - 1
+
+        try:
+            win.hline(0, left_start, curses.ACS_HLINE, right_end - left_start)
+            win.hline(self.hei - 1, left_start, curses.ACS_HLINE,
+                    right_end - left_start)
+            win.vline(1, left_start, curses.ACS_VLINE, self.hei - 2)
+        except _curses.error:
+            pass
+
+        for child in self.columns:
+            if not child.has_preview():
+                continue
+            if child.main_column and self.pager.visible:
+                win.vline(1, right_end, curses.ACS_VLINE, self.hei - 2)
+                break
+            x = child.x + child.wid
+            y = self.hei - 1
+            try:
+                win.vline(1, x, curses.ACS_VLINE, y - 1)
+                win.addch(0, x, curses.ACS_TTEE, 0)
+                win.addch(y, x, curses.ACS_BTEE, 0)
+            except:
+                # in case it's off the boundaries
+                pass
+
+        self.addch(0, left_start, curses.ACS_ULCORNER)
+        self.addch(self.hei - 1, left_start, curses.ACS_LLCORNER)
+        self.addch(0, right_end, curses.ACS_URCORNER)
+        self.addch(self.hei - 1, right_end, curses.ACS_LRCORNER)
+
+    def _draw_bookmarks(self):
+        self.fm.bookmarks.update_if_outdated()
+        self.color_reset()
+        self.need_clear = True
+
+        sorted_bookmarks = sorted((item for item in self.fm.bookmarks \
+            if self.fm.settings.show_hidden_bookmarks or \
+            '/.' not in item[1].path), key=lambda t: t[0].lower())
+
+        hei = min(self.hei - 1, len(sorted_bookmarks))
+        ystart = self.hei - hei
+
+        maxlen = self.wid
+        self.addnstr(ystart - 1, 0, "mark  path".ljust(self.wid), self.wid)
+
+        whitespace = " " * maxlen
+        for line, items in zip(range(self.hei-1), sorted_bookmarks):
+            key, mark = items
+            string = " " + key + "   " + mark.path
+            self.addstr(ystart + line, 0, whitespace)
+            self.addnstr(ystart + line, 0, string, self.wid)
+
+        self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
+
+    def _draw_info(self, lines):
+        self.need_clear = True
+        hei = min(self.hei - 1, len(lines))
+        ystart = self.hei - hei
+        i = ystart
+        whitespace = " " * self.wid
+        for line in lines:
+            if i >= self.hei:
+                break
+            self.addstr(i, 0, whitespace)
+            self.addnstr(i, 0, line, self.wid)
+            i += 1
+
+    def _draw_hints(self):
+        self.need_clear = True
+        hints = []
+        for k, v in self.fm.ui.keybuffer.pointer.items():
+            k = key_to_string(k)
+            if isinstance(v, dict):
+                text = '...'
+            else:
+                text = v
+            if text.startswith('hint') or text.startswith('chain hint'):
+                continue
+            hints.append((k, text))
+        hints.sort(key=lambda t: t[1])
+
+        hei = min(self.hei - 1, len(hints))
+        ystart = self.hei - hei
+        self.addnstr(ystart - 1, 0, "key          command".ljust(self.wid),
+                self.wid)
+        try:
+            self.win.chgat(ystart - 1, 0, curses.A_UNDERLINE)
+        except:
+            pass
+        whitespace = " " * self.wid
+        i = ystart
+        for key, cmd in hints:
+            string = " " + key.ljust(11) + " " + cmd
+            self.addstr(i, 0, whitespace)
+            self.addnstr(i, 0, string, self.wid)
+            i += 1
+
+    def _collapse(self):
+        # Should the last column be cut off? (Because there is no preview)
+        if not self.settings.collapse_preview or not self.preview \
+                or not self.stretch_ratios:
+            return False
+        result = not self.columns[-1].has_preview()
+        target = self.columns[-1].target
+        if not result and target and target.is_file:
+            if target.image and self.fm.settings.preview_images:
+                result = False  # don't collapse when drawing images
+            elif self.fm.settings.preview_script and \
+                    self.fm.settings.use_preview_script:
+                try:
+                    result = not self.fm.previews[target.realpath]['foundpreview']
+                except:
+                    return self.old_collapse
+
+        self.old_collapse = result
+        return result
+
+    def resize(self, y, x, hei, wid):
+        """Resize all the columns according to the given ratio"""
+        DisplayableContainer.resize(self, y, x, hei, wid)
+        borders = self.settings.draw_borders
+        pad = 1 if borders else 0
+        left = pad
+
+        self.is_collapsed = self._collapse()
+        if self.is_collapsed:
+            generator = enumerate(self.stretch_ratios)
+        else:
+            generator = enumerate(self.ratios)
+
+        last_i = len(self.ratios) - 1
+
+        for i, ratio in generator:
+            wid = int(ratio * self.wid)
+
+            cut_off = self.is_collapsed and not self.settings.padding_right
+            if i == last_i:
+                if not cut_off:
+                    wid = int(self.wid - left + 1 - pad)
+                else:
+                    self.columns[i].resize(pad, left - 1, hei - pad * 2, 1)
+                    self.columns[i].visible = False
+                    continue
+
+            if i == last_i - 1:
+                self.pager.resize(pad, left, hei - pad * 2, \
+                        max(1, self.wid - left - pad))
+
+                if cut_off:
+                    self.columns[i].resize(pad, left, hei - pad * 2, \
+                            max(1, self.wid - left - pad))
+                    continue
+
+            try:
+                self.columns[i].resize(pad, left, hei - pad * 2, \
+                        max(1, wid - 1))
+            except KeyError:
+                pass
+
+            left += wid
+
+    def click(self, event):
+        if DisplayableContainer.click(self, event):
+            return True
+        direction = event.mouse_wheel_direction()
+        if direction:
+            self.main_column.scroll(direction)
+        return False
+
+    def open_pager(self):
+        self.pager.visible = True
+        self.pager.focused = True
+        self.need_clear = True
+        self.pager.open()
+        try:
+            self.columns[-1].visible = False
+            self.columns[-2].visible = False
+        except IndexError:
+            pass
+
+    def close_pager(self):
+        self.pager.visible = False
+        self.pager.focused = False
+        self.need_clear = True
+        self.pager.close()
+        try:
+            self.columns[-1].visible = True
+            self.columns[-2].visible = True
+        except IndexError:
+            pass
+
+    def poke(self):
+        DisplayableContainer.poke(self)
+
+        # 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:
+            self.columns[-1].visible = True
+
+        if self.preview and self.is_collapsed != self._collapse():
+            self.resize(self.y, self.x, self.hei, self.wid)
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index 4d1855f7..f6f433bb 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -17,422 +17,422 @@ from ranger.container.history import History, HistoryEmptyException
 import ranger
 
 class Console(Widget):
-	visible = False
-	last_cursor_mode = None
-	history_search_pattern = None
-	prompt = ':'
-	copy = ''
-	tab_deque = None
-	original_line = None
-	history = None
-	history_backup = None
-	override = None
-	allow_close = False
-	historypath = None
-	wait_for_command_input = False
-	unicode_buffer = ""
-
-	def __init__(self, win):
-		Widget.__init__(self, win)
-		self.clear()
-		self.history = History(self.settings.max_console_history_size)
-		# load history from files
-		if not ranger.arg.clean:
-			self.historypath = self.fm.confpath('history')
-			try:
-				f = open(self.historypath, 'r')
-			except:
-				pass
-			else:
-				for line in f:
-					self.history.add(line[:-1])
-				f.close()
-		self.line = ""
-		self.history_backup = History(self.history)
-
-		# NOTE: the console is considered in the "question mode" when the
-		# question_queue is non-empty.  In that case, the console will draw the
-		# question instead of the regular console, and the input you give is
-		# used to answer the question instead of typing in commands.
-		#
-		# A question is a tuple of (question_string, callback_func,
-		# tuple_of_choices).  callback_func is a function that is called when
-		# the question is answered which gets the answer as an argument.
-		# tuple_of_choices looks like ('y', 'n').  Only one-letter-answers are
-		# currently supported.  Pressing enter uses the first choice whereas
-		# pressing ESC uses the second choice.
-		self.question_queue = []
-
-	def destroy(self):
-		# save history to files
-		if ranger.arg.clean or not self.settings.save_console_history:
-			return
-		if self.historypath:
-			try:
-				f = open(self.historypath, 'w')
-			except:
-				pass
-			else:
-				for entry in self.history_backup:
-					try:
-						f.write(entry + '\n')
-					except UnicodeEncodeError:
-						pass
-				f.close()
-
-	def draw(self):
-		self.win.erase()
-		if self.question_queue:
-			assert isinstance(self.question_queue[0], tuple)
-			assert len(self.question_queue[0]) == 3
-			self.addstr(0, 0, self.question_queue[0][0])
-			return
-
-		self.addstr(0, 0, self.prompt)
-		line = WideString(self.line)
-		overflow = -self.wid + len(self.prompt) + len(line) + 1
-		if overflow > 0: 
-			self.addstr(0, len(self.prompt), str(line[overflow:]))
-		else:
-			self.addstr(0, len(self.prompt), self.line)
-
-	def finalize(self):
-		move = self.fm.ui.win.move
-		if self.question_queue:
-			try:
-				move(self.y, len(self.question_queue[0][0]))
-			except:
-				pass
-		else:
-			try:
-				pos = uwid(self.line[0:self.pos]) + len(self.prompt)
-				move(self.y, self.x + min(self.wid-1, pos))
-			except:
-				pass
-
-	def open(self, string='', prompt=None, position=None):
-		if prompt is not None:
-			assert isinstance(prompt, str)
-			self.prompt = prompt
-		elif 'prompt' in self.__dict__:
-			del self.prompt
-
-		if self.last_cursor_mode is None:
-			try:
-				self.last_cursor_mode = curses.curs_set(1)
-			except:
-				pass
-		self.allow_close = False
-		self.tab_deque = None
-		self.unicode_buffer = ""
-		self.line = string
-		self.history_search_pattern = self.line
-		self.pos = len(string)
-		if position is not None:
-			self.pos = min(self.pos, position)
-		self.history_backup.fast_forward()
-		self.history = History(self.history_backup)
-		self.history.add('')
-		self.wait_for_command_input = True
-		return True
-
-	def close(self, trigger_cancel_function=True):
-		if self.question_queue:
-			question = self.question_queue[0]
-			answers = question[2]
-			if len(answers) >= 2:
-				self._answer_question(answers[1])
-		else:
-			self._close_command_prompt(trigger_cancel_function)
-
-	def _close_command_prompt(self, trigger_cancel_function=True):
-		if trigger_cancel_function:
-			cmd = self._get_cmd(quiet=True)
-			if cmd:
-				try:
-					cmd.cancel()
-				except Exception as error:
-					self.fm.notify(error)
-		if self.last_cursor_mode is not None:
-			try:
-				curses.curs_set(self.last_cursor_mode)
-			except:
-				pass
-			self.last_cursor_mode = None
-		self.fm.hide_console_info()
-		self.add_to_history()
-		self.tab_deque = None
-		self.clear()
-		self.__class__ = Console
-		self.wait_for_command_input = False
-
-	def clear(self):
-		self.pos = 0
-		self.line = ''
-
-	def press(self, key):
-		self.fm.ui.keymaps.use_keymap('console')
-		if not self.fm.ui.press(key):
-			self.type_key(key)
-
-	def _answer_question(self, answer):
-		if not self.question_queue:
-			return False
-		question = self.question_queue[0]
-		text, callback, answers = question
-		if answer in answers:
-			self.question_queue.pop(0)
-			callback(answer)
-			return True
-		return False
-
-	def type_key(self, key):
-		self.tab_deque = None
-
-		line = "" if self.question_queue else self.line
-		result = self._add_character(key, self.unicode_buffer, line, self.pos)
-		if result[1] == line:
-			# line didn't change, so we don't need to do anything, just update
-			# the unicode _buffer.
-			self.unicode_buffer = result[0]
-			return
-
-		if self.question_queue:
-			self.unicode_buffer, answer, self.pos = result
-			self._answer_question(answer)
-		else:
-			self.unicode_buffer, self.line, self.pos = result
-			self.on_line_change()
-
-	def _add_character(self, key, unicode_buffer, line, pos):
-		# Takes the pressed key, a string "unicode_buffer" containing a
-		# potentially incomplete unicode character, the current line and the
-		# position of the cursor inside the line.
-		# This function returns the new unicode buffer, the modified line and
-		# position.
-		if isinstance(key, int):
-			try:
-				key = chr(key)
-			except ValueError:
-				return unicode_buffer, line, pos
-
-		if self.fm.py3:
-			unicode_buffer += key
-			try:
-				decoded = unicode_buffer.encode("latin-1").decode("utf-8")
-			except UnicodeDecodeError:
-				return unicode_buffer, line, pos
-			except UnicodeEncodeError:
-				return unicode_buffer, line, pos
-			else:
-				unicode_buffer = ""
-				if pos == len(line):
-					line += decoded
-				else:
-					line = line[:pos] + decoded + line[pos:]
-				pos += len(decoded)
-		else:
-			if pos == len(line):
-				line += key
-			else:
-				line = line[:pos] + key + line[pos:]
-			pos += len(key)
-		return unicode_buffer, line, pos
-
-	def history_move(self, n):
-		try:
-			current = self.history.current()
-		except HistoryEmptyException:
-			pass
-		else:
-			if self.line != current and self.line != self.history.top():
-				self.history.modify(self.line)
-			if self.history_search_pattern:
-				self.history.search(self.history_search_pattern, n)
-			else:
-				self.history.move(n)
-			current = self.history.current()
-			if self.line != current:
-				self.line = self.history.current()
-				self.pos = len(self.line)
-
-	def add_to_history(self):
-		self.history_backup.fast_forward()
-		self.history_backup.add(self.line)
-		self.history = History(self.history_backup)
-
-	def move(self, **keywords):
-		direction = Direction(keywords)
-		if direction.horizontal():
-			# Ensure that the pointer is moved utf-char-wise
-			if self.fm.py3:
-				self.pos = direction.move(
-						direction=direction.right(),
-						minimum=0,
-						maximum=len(self.line) + 1,
-						current=self.pos)
-			else:
-				if self.fm.py3:
-					uc = list(self.line)
-					upos = len(self.line[:self.pos])
-				else:
-					uc = list(self.line.decode('utf-8', 'ignore'))
-					upos = len(self.line[:self.pos].decode('utf-8', 'ignore'))
-				newupos = direction.move(
-						direction=direction.right(),
-						minimum=0,
-						maximum=len(uc) + 1,
-						current=upos)
-				self.pos = len(''.join(uc[:newupos]).encode('utf-8', 'ignore'))
-
-	def delete_rest(self, direction):
-		self.tab_deque = None
-		if direction > 0:
-			self.copy = self.line[self.pos:]
-			self.line = self.line[:self.pos]
-		else:
-			self.copy = self.line[:self.pos]
-			self.line = self.line[self.pos:]
-			self.pos = 0
-		self.on_line_change()
-
-	def paste(self):
-		if self.pos == len(self.line):
-			self.line += self.copy
-		else:
-			self.line = self.line[:self.pos] + self.copy + self.line[self.pos:]
-		self.pos += len(self.copy)
-		self.on_line_change()
-
-	def delete_word(self, backward=True):
-		if self.line:
-			self.tab_deque = None
-			if backward:
-				right_part = self.line[self.pos:]
-				i = self.pos - 2
-				while i >= 0 and re.match(r'[\w\d]', self.line[i], re.U):
-					i -= 1
-				self.copy = self.line[i + 1:self.pos]
-				self.line = self.line[:i + 1] + right_part
-				self.pos = i + 1
-			else:
-				left_part = self.line[:self.pos]
-				i = self.pos + 1
-				while i < len(self.line) and re.match(r'[\w\d]', self.line[i], re.U):
-					i += 1
-				self.copy = self.line[self.pos:i]
-				if i >= len(self.line):
-					self.line = left_part
-					self.pos = len(self.line)
-				else:
-					self.line = left_part + self.line[i:]
-					self.pos = len(left_part)
-			self.on_line_change()
-
-	def delete(self, mod):
-		self.tab_deque = None
-		if mod == -1 and self.pos == 0:
-			if not self.line:
-				self.close(trigger_cancel_function=False)
-			return
-		# Delete utf-char-wise
-		if self.fm.py3:
-			left_part = self.line[:self.pos + mod]
-			self.pos = len(left_part)
-			self.line = left_part + self.line[self.pos + 1:]
-		else:
-			uc = list(self.line.decode('utf-8', 'ignore'))
-			upos = len(self.line[:self.pos].decode('utf-8', 'ignore')) + mod
-			left_part = ''.join(uc[:upos]).encode('utf-8', 'ignore')
-			self.pos = len(left_part)
-			self.line = left_part + ''.join(uc[upos+1:]).encode('utf-8', 'ignore')
-		self.on_line_change()
-
-	def execute(self, cmd=None):
-		if self.question_queue and cmd is None:
-			question = self.question_queue[0]
-			answers = question[2]
-			if len(answers) >= 1:
-				self._answer_question(answers[0])
-			else:
-				self.question_queue.pop(0)
-			return
-
-		self.allow_close = True
-		self.fm.execute_console(self.line)
-		if self.allow_close:
-			self._close_command_prompt(trigger_cancel_function=False)
-
-	def _get_cmd(self, quiet=False):
-		try:
-			command_class = self._get_cmd_class()
-		except KeyError:
-			if not quiet:
-				error = "Command not found: `%s'" % self.line.split()[0]
-				self.fm.notify(error, bad=True)
-		except:
-			return None
-		else:
-			return command_class(self.line)
-
-	def _get_cmd_class(self):
-		return self.fm.commands.get_command(self.line.split()[0])
-
-	def _get_tab(self):
-		if ' ' in self.line:
-			cmd = self._get_cmd()
-			if cmd:
-				return cmd.tab()
-			else:
-				return None
-
-		return self.fm.commands.command_generator(self.line)
-
-	def tab(self, n=1):
-		if self.tab_deque is None:
-			tab_result = self._get_tab()
-
-			if isinstance(tab_result, str):
-				self.line = tab_result
-				self.pos = len(tab_result)
-				self.on_line_change()
-
-			elif tab_result == None:
-				pass
-
-			elif hasattr(tab_result, '__iter__'):
-				self.tab_deque = deque(tab_result)
-				self.tab_deque.appendleft(self.line)
-
-		if self.tab_deque is not None:
-			self.tab_deque.rotate(-n)
-			self.line = self.tab_deque[0]
-			self.pos = len(self.line)
-			self.on_line_change()
-
-	def on_line_change(self):
-		self.history_search_pattern = self.line
-		try:
-			cls = self._get_cmd_class()
-		except (KeyError, ValueError, IndexError):
-			pass
-		else:
-			cmd = cls(self.line)
-			if cmd and cmd.quick():
-				self.execute(cmd)
-
-	def ask(self, text, callback, choices=['y', 'n']):
-		"""
-		Open a question prompt with predefined choices
-
-		The "text" is displayed as the question text and should include a list
-		of possible keys that the user can type.  The "callback" is a function
-		that is called when the question is answered.  It only gets the answer
-		as an argument.  "choices" is a tuple of one-letter strings that can be
-		typed in by the user.  Every other input gets ignored, except <Enter>
-		and <ESC>.
-
-		The first choice is used when the user presses <Enter>, the second
-		choice is used when the user presses <ESC>.
-		"""
-		self.question_queue.append((text, callback, choices))
+    visible = False
+    last_cursor_mode = None
+    history_search_pattern = None
+    prompt = ':'
+    copy = ''
+    tab_deque = None
+    original_line = None
+    history = None
+    history_backup = None
+    override = None
+    allow_close = False
+    historypath = None
+    wait_for_command_input = False
+    unicode_buffer = ""
+
+    def __init__(self, win):
+        Widget.__init__(self, win)
+        self.clear()
+        self.history = History(self.settings.max_console_history_size)
+        # load history from files
+        if not ranger.arg.clean:
+            self.historypath = self.fm.confpath('history')
+            try:
+                f = open(self.historypath, 'r')
+            except:
+                pass
+            else:
+                for line in f:
+                    self.history.add(line[:-1])
+                f.close()
+        self.line = ""
+        self.history_backup = History(self.history)
+
+        # NOTE: the console is considered in the "question mode" when the
+        # question_queue is non-empty.  In that case, the console will draw the
+        # question instead of the regular console, and the input you give is
+        # used to answer the question instead of typing in commands.
+        #
+        # A question is a tuple of (question_string, callback_func,
+        # tuple_of_choices).  callback_func is a function that is called when
+        # the question is answered which gets the answer as an argument.
+        # tuple_of_choices looks like ('y', 'n').  Only one-letter-answers are
+        # currently supported.  Pressing enter uses the first choice whereas
+        # pressing ESC uses the second choice.
+        self.question_queue = []
+
+    def destroy(self):
+        # save history to files
+        if ranger.arg.clean or not self.settings.save_console_history:
+            return
+        if self.historypath:
+            try:
+                f = open(self.historypath, 'w')
+            except:
+                pass
+            else:
+                for entry in self.history_backup:
+                    try:
+                        f.write(entry + '\n')
+                    except UnicodeEncodeError:
+                        pass
+                f.close()
+
+    def draw(self):
+        self.win.erase()
+        if self.question_queue:
+            assert isinstance(self.question_queue[0], tuple)
+            assert len(self.question_queue[0]) == 3
+            self.addstr(0, 0, self.question_queue[0][0])
+            return
+
+        self.addstr(0, 0, self.prompt)
+        line = WideString(self.line)
+        overflow = -self.wid + len(self.prompt) + len(line) + 1
+        if overflow > 0: 
+            self.addstr(0, len(self.prompt), str(line[overflow:]))
+        else:
+            self.addstr(0, len(self.prompt), self.line)
+
+    def finalize(self):
+        move = self.fm.ui.win.move
+        if self.question_queue:
+            try:
+                move(self.y, len(self.question_queue[0][0]))
+            except:
+                pass
+        else:
+            try:
+                pos = uwid(self.line[0:self.pos]) + len(self.prompt)
+                move(self.y, self.x + min(self.wid-1, pos))
+            except:
+                pass
+
+    def open(self, string='', prompt=None, position=None):
+        if prompt is not None:
+            assert isinstance(prompt, str)
+            self.prompt = prompt
+        elif 'prompt' in self.__dict__:
+            del self.prompt
+
+        if self.last_cursor_mode is None:
+            try:
+                self.last_cursor_mode = curses.curs_set(1)
+            except:
+                pass
+        self.allow_close = False
+        self.tab_deque = None
+        self.unicode_buffer = ""
+        self.line = string
+        self.history_search_pattern = self.line
+        self.pos = len(string)
+        if position is not None:
+            self.pos = min(self.pos, position)
+        self.history_backup.fast_forward()
+        self.history = History(self.history_backup)
+        self.history.add('')
+        self.wait_for_command_input = True
+        return True
+
+    def close(self, trigger_cancel_function=True):
+        if self.question_queue:
+            question = self.question_queue[0]
+            answers = question[2]
+            if len(answers) >= 2:
+                self._answer_question(answers[1])
+        else:
+            self._close_command_prompt(trigger_cancel_function)
+
+    def _close_command_prompt(self, trigger_cancel_function=True):
+        if trigger_cancel_function:
+            cmd = self._get_cmd(quiet=True)
+            if cmd:
+                try:
+                    cmd.cancel()
+                except Exception as error:
+                    self.fm.notify(error)
+        if self.last_cursor_mode is not None:
+            try:
+                curses.curs_set(self.last_cursor_mode)
+            except:
+                pass
+            self.last_cursor_mode = None
+        self.fm.hide_console_info()
+        self.add_to_history()
+        self.tab_deque = None
+        self.clear()
+        self.__class__ = Console
+        self.wait_for_command_input = False
+
+    def clear(self):
+        self.pos = 0
+        self.line = ''
+
+    def press(self, key):
+        self.fm.ui.keymaps.use_keymap('console')
+        if not self.fm.ui.press(key):
+            self.type_key(key)
+
+    def _answer_question(self, answer):
+        if not self.question_queue:
+            return False
+        question = self.question_queue[0]
+        text, callback, answers = question
+        if answer in answers:
+            self.question_queue.pop(0)
+            callback(answer)
+            return True
+        return False
+
+    def type_key(self, key):
+        self.tab_deque = None
+
+        line = "" if self.question_queue else self.line
+        result = self._add_character(key, self.unicode_buffer, line, self.pos)
+        if result[1] == line:
+            # line didn't change, so we don't need to do anything, just update
+            # the unicode _buffer.
+            self.unicode_buffer = result[0]
+            return
+
+        if self.question_queue:
+            self.unicode_buffer, answer, self.pos = result
+            self._answer_question(answer)
+        else:
+            self.unicode_buffer, self.line, self.pos = result
+            self.on_line_change()
+
+    def _add_character(self, key, unicode_buffer, line, pos):
+        # Takes the pressed key, a string "unicode_buffer" containing a
+        # potentially incomplete unicode character, the current line and the
+        # position of the cursor inside the line.
+        # This function returns the new unicode buffer, the modified line and
+        # position.
+        if isinstance(key, int):
+            try:
+                key = chr(key)
+            except ValueError:
+                return unicode_buffer, line, pos
+
+        if self.fm.py3:
+            unicode_buffer += key
+            try:
+                decoded = unicode_buffer.encode("latin-1").decode("utf-8")
+            except UnicodeDecodeError:
+                return unicode_buffer, line, pos
+            except UnicodeEncodeError:
+                return unicode_buffer, line, pos
+            else:
+                unicode_buffer = ""
+                if pos == len(line):
+                    line += decoded
+                else:
+                    line = line[:pos] + decoded + line[pos:]
+                pos += len(decoded)
+        else:
+            if pos == len(line):
+                line += key
+            else:
+                line = line[:pos] + key + line[pos:]
+            pos += len(key)
+        return unicode_buffer, line, pos
+
+    def history_move(self, n):
+        try:
+            current = self.history.current()
+        except HistoryEmptyException:
+            pass
+        else:
+            if self.line != current and self.line != self.history.top():
+                self.history.modify(self.line)
+            if self.history_search_pattern:
+                self.history.search(self.history_search_pattern, n)
+            else:
+                self.history.move(n)
+            current = self.history.current()
+            if self.line != current:
+                self.line = self.history.current()
+                self.pos = len(self.line)
+
+    def add_to_history(self):
+        self.history_backup.fast_forward()
+        self.history_backup.add(self.line)
+        self.history = History(self.history_backup)
+
+    def move(self, **keywords):
+        direction = Direction(keywords)
+        if direction.horizontal():
+            # Ensure that the pointer is moved utf-char-wise
+            if self.fm.py3:
+                self.pos = direction.move(
+                        direction=direction.right(),
+                        minimum=0,
+                        maximum=len(self.line) + 1,
+                        current=self.pos)
+            else:
+                if self.fm.py3:
+                    uc = list(self.line)
+                    upos = len(self.line[:self.pos])
+                else:
+                    uc = list(self.line.decode('utf-8', 'ignore'))
+                    upos = len(self.line[:self.pos].decode('utf-8', 'ignore'))
+                newupos = direction.move(
+                        direction=direction.right(),
+                        minimum=0,
+                        maximum=len(uc) + 1,
+                        current=upos)
+                self.pos = len(''.join(uc[:newupos]).encode('utf-8', 'ignore'))
+
+    def delete_rest(self, direction):
+        self.tab_deque = None
+        if direction > 0:
+            self.copy = self.line[self.pos:]
+            self.line = self.line[:self.pos]
+        else:
+            self.copy = self.line[:self.pos]
+            self.line = self.line[self.pos:]
+            self.pos = 0
+        self.on_line_change()
+
+    def paste(self):
+        if self.pos == len(self.line):
+            self.line += self.copy
+        else:
+            self.line = self.line[:self.pos] + self.copy + self.line[self.pos:]
+        self.pos += len(self.copy)
+        self.on_line_change()
+
+    def delete_word(self, backward=True):
+        if self.line:
+            self.tab_deque = None
+            if backward:
+                right_part = self.line[self.pos:]
+                i = self.pos - 2
+                while i >= 0 and re.match(r'[\w\d]', self.line[i], re.U):
+                    i -= 1
+                self.copy = self.line[i + 1:self.pos]
+                self.line = self.line[:i + 1] + right_part
+                self.pos = i + 1
+            else:
+                left_part = self.line[:self.pos]
+                i = self.pos + 1
+                while i < len(self.line) and re.match(r'[\w\d]', self.line[i], re.U):
+                    i += 1
+                self.copy = self.line[self.pos:i]
+                if i >= len(self.line):
+                    self.line = left_part
+                    self.pos = len(self.line)
+                else:
+                    self.line = left_part + self.line[i:]
+                    self.pos = len(left_part)
+            self.on_line_change()
+
+    def delete(self, mod):
+        self.tab_deque = None
+        if mod == -1 and self.pos == 0:
+            if not self.line:
+                self.close(trigger_cancel_function=False)
+            return
+        # Delete utf-char-wise
+        if self.fm.py3:
+            left_part = self.line[:self.pos + mod]
+            self.pos = len(left_part)
+            self.line = left_part + self.line[self.pos + 1:]
+        else:
+            uc = list(self.line.decode('utf-8', 'ignore'))
+            upos = len(self.line[:self.pos].decode('utf-8', 'ignore')) + mod
+            left_part = ''.join(uc[:upos]).encode('utf-8', 'ignore')
+            self.pos = len(left_part)
+            self.line = left_part + ''.join(uc[upos+1:]).encode('utf-8', 'ignore')
+        self.on_line_change()
+
+    def execute(self, cmd=None):
+        if self.question_queue and cmd is None:
+            question = self.question_queue[0]
+            answers = question[2]
+            if len(answers) >= 1:
+                self._answer_question(answers[0])
+            else:
+                self.question_queue.pop(0)
+            return
+
+        self.allow_close = True
+        self.fm.execute_console(self.line)
+        if self.allow_close:
+            self._close_command_prompt(trigger_cancel_function=False)
+
+    def _get_cmd(self, quiet=False):
+        try:
+            command_class = self._get_cmd_class()
+        except KeyError:
+            if not quiet:
+                error = "Command not found: `%s'" % self.line.split()[0]
+                self.fm.notify(error, bad=True)
+        except:
+            return None
+        else:
+            return command_class(self.line)
+
+    def _get_cmd_class(self):
+        return self.fm.commands.get_command(self.line.split()[0])
+
+    def _get_tab(self):
+        if ' ' in self.line:
+            cmd = self._get_cmd()
+            if cmd:
+                return cmd.tab()
+            else:
+                return None
+
+        return self.fm.commands.command_generator(self.line)
+
+    def tab(self, n=1):
+        if self.tab_deque is None:
+            tab_result = self._get_tab()
+
+            if isinstance(tab_result, str):
+                self.line = tab_result
+                self.pos = len(tab_result)
+                self.on_line_change()
+
+            elif tab_result == None:
+                pass
+
+            elif hasattr(tab_result, '__iter__'):
+                self.tab_deque = deque(tab_result)
+                self.tab_deque.appendleft(self.line)
+
+        if self.tab_deque is not None:
+            self.tab_deque.rotate(-n)
+            self.line = self.tab_deque[0]
+            self.pos = len(self.line)
+            self.on_line_change()
+
+    def on_line_change(self):
+        self.history_search_pattern = self.line
+        try:
+            cls = self._get_cmd_class()
+        except (KeyError, ValueError, IndexError):
+            pass
+        else:
+            cmd = cls(self.line)
+            if cmd and cmd.quick():
+                self.execute(cmd)
+
+    def ask(self, text, callback, choices=['y', 'n']):
+        """
+        Open a question prompt with predefined choices
+
+        The "text" is displayed as the question text and should include a list
+        of possible keys that the user can type.  The "callback" is a function
+        that is called when the question is answered.  It only gets the answer
+        as an argument.  "choices" is a tuple of one-letter strings that can be
+        typed in by the user.  Every other input gets ignored, except <Enter>
+        and <ESC>.
+
+        The first choice is used when the user presses <Enter>, the second
+        choice is used when the user presses <ESC>.
+        """
+        self.question_queue.append((text, callback, choices))
diff --git a/ranger/gui/widgets/pager.py b/ranger/gui/widgets/pager.py
index 6ebde5a9..e9ce8b5c 100644
--- a/ranger/gui/widgets/pager.py
+++ b/ranger/gui/widgets/pager.py
@@ -12,202 +12,202 @@ import ranger.ext.img_display as img_display
 
 # TODO: Scrolling in embedded pager
 class Pager(Widget):
-	source = None
-	source_is_stream = False
-
-	old_source = None
-	old_scroll_begin = 0
-	old_startx = 0
-	need_clear_image = False
-	need_redraw_image = False
-	max_width = None
-	def __init__(self, win, embedded=False):
-		Widget.__init__(self, win)
-		self.embedded = embedded
-		self.scroll_begin = 0
-		self.startx = 0
-		self.markup = None
-		self.lines = []
-		self.image = None
-
-	def open(self):
-		self.scroll_begin = 0
-		self.markup = None
-		self.max_width = 0
-		self.startx = 0
-		self.need_redraw = True
-
-	def clear_image(self):
-		if self.need_clear_image:
-			img_display.clear(self.x, self.y, self.wid, self.hei)
-			self.need_clear_image = False
-
-	def close(self):
-		if self.image:
-			self.need_clear_image = True
-			self.clear_image()
-		if self.source and self.source_is_stream:
-			self.source.close()
-
-	def finalize(self):
-		self.fm.ui.win.move(self.y, self.x)
-
-	def draw(self):
-		if self.need_clear_image:
-			self.need_redraw = True
-
-		if self.old_source != self.source:
-			self.old_source = self.source
-			self.need_redraw = True
-
-		if self.old_scroll_begin != self.scroll_begin or \
-				self.old_startx != self.startx:
-			self.old_startx = self.startx
-			self.old_scroll_begin = self.scroll_begin
-
-		if self.need_redraw:
-			self.need_redraw_image = True
-			self.clear_image()
-
-			if not self.image:
-				line_gen = self._generate_lines(
-						starty=self.scroll_begin, startx=self.startx)
-
-				for line, i in zip(line_gen, range(self.hei)):
-					self._draw_line(i, line)
-
-			self.need_redraw = False
-
-	def draw_image(self):
-		if self.image and self.need_redraw_image:
-			self.source = None
-			self.need_redraw_image = False
-			try:
-				img_display.draw(self.image, self.x, self.y, self.wid, self.hei)
-			except Exception as e:
-				self.fm.notify(e, bad=True)
-
-	def _draw_line(self, i, line):
-		if self.markup is None:
-			self.addstr(i, 0, line)
-		elif self.markup == 'ansi':
-			try:
-				self.win.move(i, 0)
-			except:
-				pass
-			else:
-				for chunk in ansi.text_with_fg_bg_attr(line):
-					if isinstance(chunk, tuple):
-						self.set_fg_bg_attr(*chunk)
-					else:
-						self.addstr(chunk)
-
-	def move(self, narg=None, **kw):
-		direction = Direction(kw)
-		if direction.horizontal():
-			self.startx = direction.move(
-					direction=direction.right(),
-					override=narg,
-					maximum=self.max_width,
-					current=self.startx,
-					pagesize=self.wid,
-					offset=-self.wid + 1)
-		if direction.vertical():
-			if self.source_is_stream:
-				self._get_line(self.scroll_begin + self.hei * 2)
-			self.scroll_begin = direction.move(
-					direction=direction.down(),
-					override=narg,
-					maximum=len(self.lines),
-					current=self.scroll_begin,
-					pagesize=self.hei,
-					offset=-self.hei + 1)
-
-	def press(self, key):
-		self.fm.ui.keymaps.use_keymap('pager')
-		self.fm.ui.press(key)
-
-	def set_image(self, image):
-		if self.image:
-			self.need_clear_image = True
-		self.image = image
-
-		if self.source and self.source_is_stream:
-			self.source.close()
-		self.source = None
-		self.source_is_stream = False
-
-	def set_source(self, source, strip=False):
-		if self.image:
-			self.image = None
-			self.need_clear_image = True
-
-		if self.source and self.source_is_stream:
-			self.source.close()
-
-		self.max_width = 0
-		if isinstance(source, str):
-			self.source_is_stream = False
-			self.lines = source.splitlines()
-			if self.lines:
-				self.max_width = max(len(line) for line in self.lines)
-		elif hasattr(source, '__getitem__'):
-			self.source_is_stream = False
-			self.lines = source
-			if self.lines:
-				self.max_width = max(len(line) for line in source)
-		elif hasattr(source, 'readline'):
-			self.source_is_stream = True
-			self.lines = []
-		else:
-			self.source = None
-			self.source_is_stream = False
-			return False
-		self.markup = 'ansi'
-
-		if not self.source_is_stream and strip:
-			self.lines = map(lambda x: x.strip(), self.lines)
-
-		self.source = source
-		return True
-
-	def click(self, event):
-		n = event.ctrl() and 1 or 3
-		direction = event.mouse_wheel_direction()
-		if direction:
-			self.move(down=direction * n)
-		return True
-
-	def _get_line(self, n, attempt_to_read=True):
-		assert isinstance(n, int), n
-		try:
-			return self.lines[n]
-		except (KeyError, IndexError):
-			if attempt_to_read and self.source_is_stream:
-				try:
-					for l in self.source:
-						if len(l) > self.max_width:
-							self.max_width = len(l)
-						self.lines.append(l)
-						if len(self.lines) > n:
-							break
-				except (UnicodeError, IOError):
-					pass
-				return self._get_line(n, attempt_to_read=False)
-			return ""
-
-	def _generate_lines(self, starty, startx):
-		i = starty
-		if not self.source:
-			raise StopIteration
-		while True:
-			try:
-				line = self._get_line(i).expandtabs(4)
-				if self.markup is 'ansi':
-					line = ansi.char_slice(line, startx, self.wid) + ansi.reset
-				else:
-					line = line[startx:self.wid + startx]
-				yield line.rstrip()
-			except IndexError:
-				raise StopIteration
-			i += 1
+    source = None
+    source_is_stream = False
+
+    old_source = None
+    old_scroll_begin = 0
+    old_startx = 0
+    need_clear_image = False
+    need_redraw_image = False
+    max_width = None
+    def __init__(self, win, embedded=False):
+        Widget.__init__(self, win)
+        self.embedded = embedded
+        self.scroll_begin = 0
+        self.startx = 0
+        self.markup = None
+        self.lines = []
+        self.image = None
+
+    def open(self):
+        self.scroll_begin = 0
+        self.markup = None
+        self.max_width = 0
+        self.startx = 0
+        self.need_redraw = True
+
+    def clear_image(self):
+        if self.need_clear_image:
+            img_display.clear(self.x, self.y, self.wid, self.hei)
+            self.need_clear_image = False
+
+    def close(self):
+        if self.image:
+            self.need_clear_image = True
+            self.clear_image()
+        if self.source and self.source_is_stream:
+            self.source.close()
+
+    def finalize(self):
+        self.fm.ui.win.move(self.y, self.x)
+
+    def draw(self):
+        if self.need_clear_image:
+            self.need_redraw = True
+
+        if self.old_source != self.source:
+            self.old_source = self.source
+            self.need_redraw = True
+
+        if self.old_scroll_begin != self.scroll_begin or \
+                self.old_startx != self.startx:
+            self.old_startx = self.startx
+            self.old_scroll_begin = self.scroll_begin
+
+        if self.need_redraw:
+            self.need_redraw_image = True
+            self.clear_image()
+
+            if not self.image:
+                line_gen = self._generate_lines(
+                        starty=self.scroll_begin, startx=self.startx)
+
+                for line, i in zip(line_gen, range(self.hei)):
+                    self._draw_line(i, line)
+
+            self.need_redraw = False
+
+    def draw_image(self):
+        if self.image and self.need_redraw_image:
+            self.source = None
+            self.need_redraw_image = False
+            try:
+                img_display.draw(self.image, self.x, self.y, self.wid, self.hei)
+            except Exception as e:
+                self.fm.notify(e, bad=True)
+
+    def _draw_line(self, i, line):
+        if self.markup is None:
+            self.addstr(i, 0, line)
+        elif self.markup == 'ansi':
+            try:
+                self.win.move(i, 0)
+            except:
+                pass
+            else:
+                for chunk in ansi.text_with_fg_bg_attr(line):
+                    if isinstance(chunk, tuple):
+                        self.set_fg_bg_attr(*chunk)
+                    else:
+                        self.addstr(chunk)
+
+    def move(self, narg=None, **kw):
+        direction = Direction(kw)
+        if direction.horizontal():
+            self.startx = direction.move(
+                    direction=direction.right(),
+                    override=narg,
+                    maximum=self.max_width,
+                    current=self.startx,
+                    pagesize=self.wid,
+                    offset=-self.wid + 1)
+        if direction.vertical():
+            if self.source_is_stream:
+                self._get_line(self.scroll_begin + self.hei * 2)
+            self.scroll_begin = direction.move(
+                    direction=direction.down(),
+                    override=narg,
+                    maximum=len(self.lines),
+                    current=self.scroll_begin,
+                    pagesize=self.hei,
+                    offset=-self.hei + 1)
+
+    def press(self, key):
+        self.fm.ui.keymaps.use_keymap('pager')
+        self.fm.ui.press(key)
+
+    def set_image(self, image):
+        if self.image:
+            self.need_clear_image = True
+        self.image = image
+
+        if self.source and self.source_is_stream:
+            self.source.close()
+        self.source = None
+        self.source_is_stream = False
+
+    def set_source(self, source, strip=False):
+        if self.image:
+            self.image = None
+            self.need_clear_image = True
+
+        if self.source and self.source_is_stream:
+            self.source.close()
+
+        self.max_width = 0
+        if isinstance(source, str):
+            self.source_is_stream = False
+            self.lines = source.splitlines()
+            if self.lines:
+                self.max_width = max(len(line) for line in self.lines)
+        elif hasattr(source, '__getitem__'):
+            self.source_is_stream = False
+            self.lines = source
+            if self.lines:
+                self.max_width = max(len(line) for line in source)
+        elif hasattr(source, 'readline'):
+            self.source_is_stream = True
+            self.lines = []
+        else:
+            self.source = None
+            self.source_is_stream = False
+            return False
+        self.markup = 'ansi'
+
+        if not self.source_is_stream and strip:
+            self.lines = map(lambda x: x.strip(), self.lines)
+
+        self.source = source
+        return True
+
+    def click(self, event):
+        n = event.ctrl() and 1 or 3
+        direction = event.mouse_wheel_direction()
+        if direction:
+            self.move(down=direction * n)
+        return True
+
+    def _get_line(self, n, attempt_to_read=True):
+        assert isinstance(n, int), n
+        try:
+            return self.lines[n]
+        except (KeyError, IndexError):
+            if attempt_to_read and self.source_is_stream:
+                try:
+                    for l in self.source:
+                        if len(l) > self.max_width:
+                            self.max_width = len(l)
+                        self.lines.append(l)
+                        if len(self.lines) > n:
+                            break
+                except (UnicodeError, IOError):
+                    pass
+                return self._get_line(n, attempt_to_read=False)
+            return ""
+
+    def _generate_lines(self, starty, startx):
+        i = starty
+        if not self.source:
+            raise StopIteration
+        while True:
+            try:
+                line = self._get_line(i).expandtabs(4)
+                if self.markup is 'ansi':
+                    line = ansi.char_slice(line, startx, self.wid) + ansi.reset
+                else:
+                    line = line[startx:self.wid + startx]
+                yield line.rstrip()
+            except IndexError:
+                raise StopIteration
+            i += 1
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index 247ebe62..a2ce3830 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -20,282 +20,282 @@ from . import Widget
 from ranger.gui.bar import Bar
 
 class StatusBar(Widget):
-	__doc__ = __doc__
-	owners = {}
-	groups = {}
-	timeformat = '%Y-%m-%d %H:%M'
-	hint = None
-	msg = None
-
-	old_thisfile = None
-	old_ctime = None
-	old_du = None
-	old_hint = None
-	result = None
-
-	def __init__(self, win, column=None):
-		Widget.__init__(self, win)
-		self.column = column
-		self.settings.signal_bind('setopt.display_size_in_status_bar',
-				self.request_redraw, weak=True)
-
-	def request_redraw(self):
-		self.need_redraw = True
-
-	def notify(self, text, duration=0, bad=False):
-		self.msg = Message(text, duration, bad)
-
-	def clear_message(self):
-		self.msg = None
-
-	def draw(self):
-		"""Draw the statusbar"""
-
-		if self.hint and isinstance(self.hint, str):
-			if self.old_hint != self.hint:
-				self.need_redraw = True
-			if self.need_redraw:
-				self._draw_hint()
-			return
-
-		if self.old_hint and not self.hint:
-			self.old_hint = None
-			self.need_redraw = True
-
-		if self.msg:
-			if self.msg.is_alive():
-				self._draw_message()
-				return
-			else:
-				self.msg = None
-				self.need_redraw = True
-
-		if self.fm.thisfile:
-			self.fm.thisfile.load_if_outdated()
-			try:
-				ctime = self.fm.thisfile.stat.st_ctime
-			except:
-				ctime = -1
-		else:
-			ctime = -1
-
-		if not self.result:
-			self.need_redraw = True
-
-		if self.old_du and not self.fm.thisdir.disk_usage:
-			self.old_du = self.fm.thisdir.disk_usage
-			self.need_redraw = True
-
-		if self.old_thisfile != self.fm.thisfile:
-			self.old_thisfile = self.fm.thisfile
-			self.need_redraw = True
-
-		if self.old_ctime != ctime:
-			self.old_ctime = ctime
-			self.need_redraw = True
-
-		if self.need_redraw:
-			self.need_redraw = False
-
-			self._calc_bar()
-			self._print_result(self.result)
-
-	def _calc_bar(self):
-		bar = Bar('in_statusbar')
-		self._get_left_part(bar)
-		self._get_right_part(bar)
-		bar.shrink_by_removing(self.wid)
-
-		self.result = bar.combine()
-
-	def _draw_message(self):
-		self.win.erase()
-		self.color('in_statusbar', 'message',
-				self.msg.bad and 'bad' or 'good')
-		self.addnstr(0, 0, self.msg.text, self.wid)
-
-	def _draw_hint(self):
-		self.win.erase()
-		highlight = True
-		space_left = self.wid
-		starting_point = self.x
-		for string in self.hint.split('*'):
-			highlight = not highlight
-			if highlight:
-				self.color('in_statusbar', 'text', 'highlight')
-			else:
-				self.color('in_statusbar', 'text')
-
-			try:
-				self.addnstr(0, starting_point, string, space_left)
-			except:
-				break
-			space_left -= len(string)
-			starting_point += len(string)
-
-	def _get_left_part(self, bar):
-		left = bar.left
-
-		if self.column is not None and self.column.target is not None\
-				and self.column.target.is_directory:
-			target = self.column.target.pointed_obj
-		else:
-			directory = self.fm.thistab.at_level(0)
-			if directory:
-				target = directory.pointed_obj
-			else:
-				return
-		try:
-			stat = target.stat
-		except:
-			return
-		if stat is None:
-			return
-
-		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()
-		left.add(str(stat.st_nlink), 'nlink')
-		left.add_space()
-		left.add(self._get_owner(target), 'owner')
-		left.add_space()
-		left.add(self._get_group(target), 'group')
-
-		if target.is_link:
-			how = target.exists and 'good' or 'bad'
-			try:
-				dest = readlink(target.path)
-			except:
-				dest = '?'
-			left.add(' -> ' + dest, 'link', how)
-		else:
-			left.add_space()
-
-			if self.settings.display_size_in_status_bar and target.infostring:
-				left.add(target.infostring.replace(" ", ""))
-
-			left.add_space()
-
-			left.add(strftime(self.timeformat,
-					localtime(stat.st_mtime)), 'mtime')
-
-	def _get_owner(self, target):
-		uid = target.stat.st_uid
-
-		try:
-			return self.owners[uid]
-		except KeyError:
-			try:
-				self.owners[uid] = getpwuid(uid)[0]
-				return self.owners[uid]
-			except KeyError:
-				return str(uid)
-
-	def _get_group(self, target):
-		gid = target.stat.st_gid
-
-		try:
-			return self.groups[gid]
-		except KeyError:
-			try:
-				self.groups[gid] = getgrgid(gid)[0]
-				return self.groups[gid]
-			except KeyError:
-				return str(gid)
-
-	def _get_right_part(self, bar):
-		right = bar.right
-		if self.column is None:
-			return
-
-		target = self.column.target
-		if target is None \
-				or not target.accessible \
-				or (target.is_directory and target.files is None):
-			return
-
-		pos = target.scroll_begin
-		max_pos = len(target) - self.column.hei
-		base = 'scroll'
-
-		if self.fm.thisdir.filter:
-			right.add(" f=", base, 'filter')
-			right.add(repr(self.fm.thisdir.filter), base, 'filter')
-			right.add(", ", "space")
-
-		if target.marked_items:
-			if len(target.marked_items) == len(target.files):
-				right.add(human_readable(target.disk_usage, separator=''))
-			else:
-				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='') + " sum")
-			try:
-				free = 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:
-			# Indicate that there are marked files. Useful if you scroll
-			# away and don't see them anymore.
-			right.add('Mrk', base, 'marked')
-		elif len(target.files):
-			right.add(str(target.pointer + 1) + '/'
-					+ str(len(target.files)) + '  ', base)
-			if max_pos <= 0:
-				right.add('All', base, 'all')
-			elif pos == 0:
-				right.add('Top', base, 'top')
-			elif pos >= max_pos:
-				right.add('Bot', base, 'bot')
-			else:
-				right.add('{0:0>.0f}%'.format(100.0 * pos / max_pos),
-						base, 'percentage')
-		else:
-			right.add('0/0  All', base, 'all')
-
-	def _print_result(self, result):
-		self.win.move(0, 0)
-		for part in result:
-			self.color(*part.lst)
-			self.addstr(str(part))
-
-		if self.settings.draw_progress_bar_in_status_bar:
-			queue = self.fm.loader.queue
-			states = []
-			for item in queue:
-				if item.progressbar_supported:
-					states.append(item.percent)
-			if states:
-				state = sum(states) / len(states)
-				barwidth = state / 100.0 * self.wid
-				self.color_at(0, 0, int(barwidth), ("in_statusbar", "loaded"))
-				self.color_reset()
+    __doc__ = __doc__
+    owners = {}
+    groups = {}
+    timeformat = '%Y-%m-%d %H:%M'
+    hint = None
+    msg = None
+
+    old_thisfile = None
+    old_ctime = None
+    old_du = None
+    old_hint = None
+    result = None
+
+    def __init__(self, win, column=None):
+        Widget.__init__(self, win)
+        self.column = column
+        self.settings.signal_bind('setopt.display_size_in_status_bar',
+                self.request_redraw, weak=True)
+
+    def request_redraw(self):
+        self.need_redraw = True
+
+    def notify(self, text, duration=0, bad=False):
+        self.msg = Message(text, duration, bad)
+
+    def clear_message(self):
+        self.msg = None
+
+    def draw(self):
+        """Draw the statusbar"""
+
+        if self.hint and isinstance(self.hint, str):
+            if self.old_hint != self.hint:
+                self.need_redraw = True
+            if self.need_redraw:
+                self._draw_hint()
+            return
+
+        if self.old_hint and not self.hint:
+            self.old_hint = None
+            self.need_redraw = True
+
+        if self.msg:
+            if self.msg.is_alive():
+                self._draw_message()
+                return
+            else:
+                self.msg = None
+                self.need_redraw = True
+
+        if self.fm.thisfile:
+            self.fm.thisfile.load_if_outdated()
+            try:
+                ctime = self.fm.thisfile.stat.st_ctime
+            except:
+                ctime = -1
+        else:
+            ctime = -1
+
+        if not self.result:
+            self.need_redraw = True
+
+        if self.old_du and not self.fm.thisdir.disk_usage:
+            self.old_du = self.fm.thisdir.disk_usage
+            self.need_redraw = True
+
+        if self.old_thisfile != self.fm.thisfile:
+            self.old_thisfile = self.fm.thisfile
+            self.need_redraw = True
+
+        if self.old_ctime != ctime:
+            self.old_ctime = ctime
+            self.need_redraw = True
+
+        if self.need_redraw:
+            self.need_redraw = False
+
+            self._calc_bar()
+            self._print_result(self.result)
+
+    def _calc_bar(self):
+        bar = Bar('in_statusbar')
+        self._get_left_part(bar)
+        self._get_right_part(bar)
+        bar.shrink_by_removing(self.wid)
+
+        self.result = bar.combine()
+
+    def _draw_message(self):
+        self.win.erase()
+        self.color('in_statusbar', 'message',
+                self.msg.bad and 'bad' or 'good')
+        self.addnstr(0, 0, self.msg.text, self.wid)
+
+    def _draw_hint(self):
+        self.win.erase()
+        highlight = True
+        space_left = self.wid
+        starting_point = self.x
+        for string in self.hint.split('*'):
+            highlight = not highlight
+            if highlight:
+                self.color('in_statusbar', 'text', 'highlight')
+            else:
+                self.color('in_statusbar', 'text')
+
+            try:
+                self.addnstr(0, starting_point, string, space_left)
+            except:
+                break
+            space_left -= len(string)
+            starting_point += len(string)
+
+    def _get_left_part(self, bar):
+        left = bar.left
+
+        if self.column is not None and self.column.target is not None\
+                and self.column.target.is_directory:
+            target = self.column.target.pointed_obj
+        else:
+            directory = self.fm.thistab.at_level(0)
+            if directory:
+                target = directory.pointed_obj
+            else:
+                return
+        try:
+            stat = target.stat
+        except:
+            return
+        if stat is None:
+            return
+
+        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()
+        left.add(str(stat.st_nlink), 'nlink')
+        left.add_space()
+        left.add(self._get_owner(target), 'owner')
+        left.add_space()
+        left.add(self._get_group(target), 'group')
+
+        if target.is_link:
+            how = target.exists and 'good' or 'bad'
+            try:
+                dest = readlink(target.path)
+            except:
+                dest = '?'
+            left.add(' -> ' + dest, 'link', how)
+        else:
+            left.add_space()
+
+            if self.settings.display_size_in_status_bar and target.infostring:
+                left.add(target.infostring.replace(" ", ""))
+
+            left.add_space()
+
+            left.add(strftime(self.timeformat,
+                    localtime(stat.st_mtime)), 'mtime')
+
+    def _get_owner(self, target):
+        uid = target.stat.st_uid
+
+        try:
+            return self.owners[uid]
+        except KeyError:
+            try:
+                self.owners[uid] = getpwuid(uid)[0]
+                return self.owners[uid]
+            except KeyError:
+                return str(uid)
+
+    def _get_group(self, target):
+        gid = target.stat.st_gid
+
+        try:
+            return self.groups[gid]
+        except KeyError:
+            try:
+                self.groups[gid] = getgrgid(gid)[0]
+                return self.groups[gid]
+            except KeyError:
+                return str(gid)
+
+    def _get_right_part(self, bar):
+        right = bar.right
+        if self.column is None:
+            return
+
+        target = self.column.target
+        if target is None \
+                or not target.accessible \
+                or (target.is_directory and target.files is None):
+            return
+
+        pos = target.scroll_begin
+        max_pos = len(target) - self.column.hei
+        base = 'scroll'
+
+        if self.fm.thisdir.filter:
+            right.add(" f=", base, 'filter')
+            right.add(repr(self.fm.thisdir.filter), base, 'filter')
+            right.add(", ", "space")
+
+        if target.marked_items:
+            if len(target.marked_items) == len(target.files):
+                right.add(human_readable(target.disk_usage, separator=''))
+            else:
+                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='') + " sum")
+            try:
+                free = 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:
+            # Indicate that there are marked files. Useful if you scroll
+            # away and don't see them anymore.
+            right.add('Mrk', base, 'marked')
+        elif len(target.files):
+            right.add(str(target.pointer + 1) + '/'
+                    + str(len(target.files)) + '  ', base)
+            if max_pos <= 0:
+                right.add('All', base, 'all')
+            elif pos == 0:
+                right.add('Top', base, 'top')
+            elif pos >= max_pos:
+                right.add('Bot', base, 'bot')
+            else:
+                right.add('{0:0>.0f}%'.format(100.0 * pos / max_pos),
+                        base, 'percentage')
+        else:
+            right.add('0/0  All', base, 'all')
+
+    def _print_result(self, result):
+        self.win.move(0, 0)
+        for part in result:
+            self.color(*part.lst)
+            self.addstr(str(part))
+
+        if self.settings.draw_progress_bar_in_status_bar:
+            queue = self.fm.loader.queue
+            states = []
+            for item in queue:
+                if item.progressbar_supported:
+                    states.append(item.percent)
+            if states:
+                state = sum(states) / len(states)
+                barwidth = state / 100.0 * self.wid
+                self.color_at(0, 0, int(barwidth), ("in_statusbar", "loaded"))
+                self.color_reset()
 
 def get_free_space(path):
-	stat = os.statvfs(path)
-	return stat.f_bavail * stat.f_bsize
+    stat = os.statvfs(path)
+    return stat.f_bavail * stat.f_bsize
 
 class Message(object):
-	elapse = None
-	text = None
-	bad = False
+    elapse = None
+    text = None
+    bad = False
 
-	def __init__(self, text, duration, bad):
-		self.text = text
-		self.bad = bad
-		self.elapse = time() + duration
+    def __init__(self, text, duration, bad):
+        self.text = text
+        self.bad = bad
+        self.elapse = time() + duration
 
-	def is_alive(self):
-		return time() <= self.elapse
+    def is_alive(self):
+        return time() <= self.elapse
diff --git a/ranger/gui/widgets/taskview.py b/ranger/gui/widgets/taskview.py
index e5efc417..3637d0e3 100644
--- a/ranger/gui/widgets/taskview.py
+++ b/ranger/gui/widgets/taskview.py
@@ -9,87 +9,87 @@ from . import Widget
 from ranger.ext.accumulator import Accumulator
 
 class TaskView(Widget, Accumulator):
-	old_lst = None
-
-	def __init__(self, win):
-		Widget.__init__(self, win)
-		Accumulator.__init__(self)
-		self.scroll_begin = 0
-
-	def draw(self):
-		base_clr = []
-		base_clr.append('in_taskview')
-		lst = self.get_list()
-
-		if self.old_lst != lst:
-			self.old_lst = lst
-			self.need_redraw = True
-
-		if self.need_redraw:
-			self.win.erase()
-			if not self.pointer_is_synced():
-				self.sync_index()
-
-			if self.hei <= 0:
-				return
-
-			self.addstr(0, 0, "Task View")
-			self.color_at(0, 0, self.wid, tuple(base_clr), 'title')
-
-			if lst:
-				for i in range(self.hei - 1):
-					i += self.scroll_begin
-					try:
-						obj = lst[i]
-					except IndexError:
-						break
-
-					y = i + 1
-					clr = list(base_clr)
-
-					if self.pointer == i:
-						clr.append('selected')
-
-					descr = obj.get_description()
-					if obj.progressbar_supported and obj.percent >= 0 \
-							and obj.percent <= 100:
-						self.addstr(y, 0, "%3d%% - %s" % \
-								(obj.percent, descr), self.wid)
-						wid = int(self.wid / 100.0 * obj.percent)
-						self.color_at(y, 0, self.wid, tuple(clr))
-						self.color_at(y, 0, wid, tuple(clr), 'loaded')
-					else:
-						self.addstr(y, 0, descr, self.wid)
-						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, tuple(base_clr), 'error')
-
-			self.color_reset()
-
-	def finalize(self):
-		y = self.y + 1 + self.pointer - self.scroll_begin
-		self.fm.ui.win.move(y, self.x)
-
-
-	def task_remove(self, i=None):
-		if i is None:
-			i = self.pointer
-
-		if self.fm.loader.queue:
-			self.fm.loader.remove(index=i)
-
-	def task_move(self, to, i=None):
-		if i is None:
-			i = self.pointer
-
-		self.fm.loader.move(_from=i, to=to)
-
-	def press(self, key):
-		self.fm.ui.keymaps.use_keymap('taskview')
-		self.fm.ui.press(key)
-
-	def get_list(self):
-		return self.fm.loader.queue
+    old_lst = None
+
+    def __init__(self, win):
+        Widget.__init__(self, win)
+        Accumulator.__init__(self)
+        self.scroll_begin = 0
+
+    def draw(self):
+        base_clr = []
+        base_clr.append('in_taskview')
+        lst = self.get_list()
+
+        if self.old_lst != lst:
+            self.old_lst = lst
+            self.need_redraw = True
+
+        if self.need_redraw:
+            self.win.erase()
+            if not self.pointer_is_synced():
+                self.sync_index()
+
+            if self.hei <= 0:
+                return
+
+            self.addstr(0, 0, "Task View")
+            self.color_at(0, 0, self.wid, tuple(base_clr), 'title')
+
+            if lst:
+                for i in range(self.hei - 1):
+                    i += self.scroll_begin
+                    try:
+                        obj = lst[i]
+                    except IndexError:
+                        break
+
+                    y = i + 1
+                    clr = list(base_clr)
+
+                    if self.pointer == i:
+                        clr.append('selected')
+
+                    descr = obj.get_description()
+                    if obj.progressbar_supported and obj.percent >= 0 \
+                            and obj.percent <= 100:
+                        self.addstr(y, 0, "%3d%% - %s" % \
+                                (obj.percent, descr), self.wid)
+                        wid = int(self.wid / 100.0 * obj.percent)
+                        self.color_at(y, 0, self.wid, tuple(clr))
+                        self.color_at(y, 0, wid, tuple(clr), 'loaded')
+                    else:
+                        self.addstr(y, 0, descr, self.wid)
+                        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, tuple(base_clr), 'error')
+
+            self.color_reset()
+
+    def finalize(self):
+        y = self.y + 1 + self.pointer - self.scroll_begin
+        self.fm.ui.win.move(y, self.x)
+
+
+    def task_remove(self, i=None):
+        if i is None:
+            i = self.pointer
+
+        if self.fm.loader.queue:
+            self.fm.loader.remove(index=i)
+
+    def task_move(self, to, i=None):
+        if i is None:
+            i = self.pointer
+
+        self.fm.loader.move(_from=i, to=to)
+
+    def press(self, key):
+        self.fm.ui.keymaps.use_keymap('taskview')
+        self.fm.ui.press(key)
+
+    def get_list(self):
+        return self.fm.loader.queue
diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py
index d37a2fd3..5986ec7a 100644
--- a/ranger/gui/widgets/titlebar.py
+++ b/ranger/gui/widgets/titlebar.py
@@ -13,142 +13,142 @@ from . import Widget
 from ranger.gui.bar import Bar
 
 class TitleBar(Widget):
-	old_thisfile = None
-	old_keybuffer = None
-	old_wid = None
-	result = None
-	throbber = ' '
-	need_redraw = False
-	tab_width = 0
-
-	def __init__(self, *args, **keywords):
-		Widget.__init__(self, *args, **keywords)
-		self.fm.signal_bind('tab.change', self.request_redraw, weak=True)
-
-	def request_redraw(self):
-		self.need_redraw = True
-
-	def draw(self):
-		if self.need_redraw or \
-				self.fm.thisfile != self.old_thisfile or\
-				str(self.fm.ui.keybuffer) != str(self.old_keybuffer) or\
-				self.wid != self.old_wid:
-			self.need_redraw = False
-			self.old_wid = self.wid
-			self.old_thisfile = self.fm.thisfile
-			self._calc_bar()
-		self._print_result(self.result)
-		if self.wid > 2:
-			self.color('in_titlebar', 'throbber')
-			self.addnstr(self.y, self.wid - 2 - self.tab_width,
-					self.throbber, 1)
-
-	def click(self, event):
-		"""Handle a MouseEvent"""
-		direction = event.mouse_wheel_direction()
-		if direction:
-			self.fm.tab_move(direction)
-			self.need_redraw = True
-			return True
-
-		if not event.pressed(1) or not self.result:
-			return False
-
-		pos = self.wid - 1
-		for tabname in reversed(self.fm._get_tab_list()):
-			tabtext = self._get_tab_text(tabname)
-			pos -= len(tabtext)
-			if event.x > pos:
-				self.fm.tab_open(tabname)
-				self.need_redraw = True
-				return True
-
-		pos = 0
-		for i, part in enumerate(self.result):
-			pos += len(part)
-			if event.x < pos:
-				if i < 2:
-					self.fm.enter_dir("~")
-				elif i == 2:
-					self.fm.enter_dir("/")
-				else:
-					try:
-						self.fm.enter_dir(part.directory)
-					except:
-						pass
-				return True
-		return False
-
-	def _calc_bar(self):
-		bar = Bar('in_titlebar')
-		self._get_left_part(bar)
-		self._get_right_part(bar)
-		try:
-			bar.shrink_from_the_left(self.wid)
-		except ValueError:
-			bar.shrink_by_removing(self.wid)
-		self.result = bar.combine()
-
-	def _get_left_part(self, bar):
-		# TODO: Properly escape non-printable chars without breaking unicode
-		if self.fm.username == 'root':
-			clr = 'bad'
-		else:
-			clr = 'good'
-
-		bar.add(self.fm.username, 'hostname', clr, fixed=True)
-		bar.add('@', 'hostname', clr, fixed=True)
-		bar.add(self.fm.hostname, 'hostname', clr, fixed=True)
-		bar.add(':', 'hostname', clr, fixed=True)
-
-		pathway = self.fm.thistab.pathway
-		if self.settings.tilde_in_titlebar and \
-				self.fm.thisdir.path.startswith(self.fm.home_path):
-			pathway = pathway[self.fm.home_path.count('/')+1:]
-			bar.add('~/', 'directory', fixed=True)
-
-		for path in pathway:
-			if path.is_link:
-				clr = 'link'
-			else:
-				clr = 'directory'
-
-			bar.add(path.basename, clr, directory=path)
-			bar.add('/', clr, fixed=True, directory=path)
-
-		if self.fm.thisfile is not None:
-			bar.add(self.fm.thisfile.basename, 'file')
-
-	def _get_right_part(self, bar):
-		# TODO: fix that pressed keys are cut off when chaining CTRL keys
-		kb = str(self.fm.ui.keybuffer)
-		self.old_keybuffer = kb
-		bar.addright(kb, 'keybuffer', fixed=True)
-		bar.addright('  ', 'space', fixed=True)
-		self.tab_width = 0
-		if len(self.fm.tabs) > 1:
-			for tabname in self.fm._get_tab_list():
-				tabtext = self._get_tab_text(tabname)
-				self.tab_width += len(tabtext)
-				clr = 'good' if tabname == self.fm.current_tab else 'bad'
-				bar.addright(tabtext, 'tab', clr, fixed=True)
-
-	def _get_tab_text(self, tabname):
-		result = ' ' + str(tabname)
-		if self.settings.dirname_in_tabs:
-			dirname = basename(self.fm.tabs[tabname].path)
-			if not dirname:
-				result += ":/"
-			elif len(dirname) > 15:
-				result += ":" + dirname[:14] + "~"
-			else:
-				result += ":" + dirname
-		return result
-
-	def _print_result(self, result):
-		self.win.move(0, 0)
-		for part in result:
-			self.color(*part.lst)
-			y, x = self.win.getyx()
-			self.addstr(y, x, str(part))
-		self.color_reset()
+    old_thisfile = None
+    old_keybuffer = None
+    old_wid = None
+    result = None
+    throbber = ' '
+    need_redraw = False
+    tab_width = 0
+
+    def __init__(self, *args, **keywords):
+        Widget.__init__(self, *args, **keywords)
+        self.fm.signal_bind('tab.change', self.request_redraw, weak=True)
+
+    def request_redraw(self):
+        self.need_redraw = True
+
+    def draw(self):
+        if self.need_redraw or \
+                self.fm.thisfile != self.old_thisfile or\
+                str(self.fm.ui.keybuffer) != str(self.old_keybuffer) or\
+                self.wid != self.old_wid:
+            self.need_redraw = False
+            self.old_wid = self.wid
+            self.old_thisfile = self.fm.thisfile
+            self._calc_bar()
+        self._print_result(self.result)
+        if self.wid > 2:
+            self.color('in_titlebar', 'throbber')
+            self.addnstr(self.y, self.wid - 2 - self.tab_width,
+                    self.throbber, 1)
+
+    def click(self, event):
+        """Handle a MouseEvent"""
+        direction = event.mouse_wheel_direction()
+        if direction:
+            self.fm.tab_move(direction)
+            self.need_redraw = True
+            return True
+
+        if not event.pressed(1) or not self.result:
+            return False
+
+        pos = self.wid - 1
+        for tabname in reversed(self.fm._get_tab_list()):
+            tabtext = self._get_tab_text(tabname)
+            pos -= len(tabtext)
+            if event.x > pos:
+                self.fm.tab_open(tabname)
+                self.need_redraw = True
+                return True
+
+        pos = 0
+        for i, part in enumerate(self.result):
+            pos += len(part)
+            if event.x < pos:
+                if i < 2:
+                    self.fm.enter_dir("~")
+                elif i == 2:
+                    self.fm.enter_dir("/")
+                else:
+                    try:
+                        self.fm.enter_dir(part.directory)
+                    except:
+                        pass
+                return True
+        return False
+
+    def _calc_bar(self):
+        bar = Bar('in_titlebar')
+        self._get_left_part(bar)
+        self._get_right_part(bar)
+        try:
+            bar.shrink_from_the_left(self.wid)
+        except ValueError:
+            bar.shrink_by_removing(self.wid)
+        self.result = bar.combine()
+
+    def _get_left_part(self, bar):
+        # TODO: Properly escape non-printable chars without breaking unicode
+        if self.fm.username == 'root':
+            clr = 'bad'
+        else:
+            clr = 'good'
+
+        bar.add(self.fm.username, 'hostname', clr, fixed=True)
+        bar.add('@', 'hostname', clr, fixed=True)
+        bar.add(self.fm.hostname, 'hostname', clr, fixed=True)
+        bar.add(':', 'hostname', clr, fixed=True)
+
+        pathway = self.fm.thistab.pathway
+        if self.settings.tilde_in_titlebar and \
+                self.fm.thisdir.path.startswith(self.fm.home_path):
+            pathway = pathway[self.fm.home_path.count('/')+1:]
+            bar.add('~/', 'directory', fixed=True)
+
+        for path in pathway:
+            if path.is_link:
+                clr = 'link'
+            else:
+                clr = 'directory'
+
+            bar.add(path.basename, clr, directory=path)
+            bar.add('/', clr, fixed=True, directory=path)
+
+        if self.fm.thisfile is not None:
+            bar.add(self.fm.thisfile.basename, 'file')
+
+    def _get_right_part(self, bar):
+        # TODO: fix that pressed keys are cut off when chaining CTRL keys
+        kb = str(self.fm.ui.keybuffer)
+        self.old_keybuffer = kb
+        bar.addright(kb, 'keybuffer', fixed=True)
+        bar.addright('  ', 'space', fixed=True)
+        self.tab_width = 0
+        if len(self.fm.tabs) > 1:
+            for tabname in self.fm._get_tab_list():
+                tabtext = self._get_tab_text(tabname)
+                self.tab_width += len(tabtext)
+                clr = 'good' if tabname == self.fm.current_tab else 'bad'
+                bar.addright(tabtext, 'tab', clr, fixed=True)
+
+    def _get_tab_text(self, tabname):
+        result = ' ' + str(tabname)
+        if self.settings.dirname_in_tabs:
+            dirname = basename(self.fm.tabs[tabname].path)
+            if not dirname:
+                result += ":/"
+            elif len(dirname) > 15:
+                result += ":" + dirname[:14] + "~"
+            else:
+                result += ":" + dirname
+        return result
+
+    def _print_result(self, result):
+        self.win.move(0, 0)
+        for part in result:
+            self.color(*part.lst)
+            y, x = self.win.getyx()
+            self.addstr(y, x, str(part))
+        self.color_reset()