about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/api/apps.py7
-rw-r--r--ranger/colorschemes/default.py3
-rw-r--r--ranger/core/actions.py7
-rw-r--r--ranger/core/fm.py6
-rw-r--r--ranger/defaults/apps.py3
-rw-r--r--ranger/defaults/keys.py1
-rw-r--r--ranger/ext/get_all_modules.py23
-rw-r--r--ranger/ext/get_executables.py33
-rw-r--r--ranger/ext/waitpid_no_intr.py9
-rw-r--r--ranger/fsobject/directory.py5
-rw-r--r--ranger/gui/context.py2
-rw-r--r--ranger/gui/widgets/browsercolumn.py3
-rw-r--r--ranger/gui/widgets/browserview.py2
-rw-r--r--ranger/gui/widgets/console.py9
-rw-r--r--ranger/gui/widgets/statusbar.py11
15 files changed, 68 insertions, 56 deletions
diff --git a/ranger/api/apps.py b/ranger/api/apps.py
index a17a6601..309c0db6 100644
--- a/ranger/api/apps.py
+++ b/ranger/api/apps.py
@@ -20,6 +20,7 @@ This module provides helper functions/classes for ranger.apps.
 import os, sys, re
 from subprocess import Popen, PIPE
 from ranger.ext.iter_tools import flatten
+from ranger.ext.get_executables import get_executables
 from ranger.shared import FileManagerAware
 
 
@@ -68,7 +69,7 @@ class Applications(FileManagerAware):
 			if hasattr(dep, 'dependencies') \
 			and not self._meets_dependencies(dep):
 				return False
-			if dep not in self.fm.executables:
+			if dep not in get_executables():
 				return False
 
 		return True
@@ -78,7 +79,7 @@ class Applications(FileManagerAware):
 			try:
 				application_handler = getattr(self, 'app_' + app)
 			except AttributeError:
-				if app in self.fm.executables:
+				if app in get_executables():
 					return tup(app, *context)
 				continue
 			if self._meets_dependencies(application_handler):
@@ -101,7 +102,7 @@ class Applications(FileManagerAware):
 		try:
 			handler = getattr(self, 'app_' + app)
 		except AttributeError:
-			if app in self.fm.executables:
+			if app in get_executables():
 				return tup(app, *context)  # generic app
 			handler = self.app_default
 		return handler(context)
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index 24f8ab91..ca8456e7 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -59,6 +59,9 @@ class Default(ColorScheme):
 					fg = white
 				else:
 					fg = red
+			if not context.selected and (context.cut or context.copied):
+				fg = black
+				attr |= bold
 			if context.main_column:
 				if context.selected:
 					attr |= bold
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index b0ec289f..a07b4ae0 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -538,16 +538,23 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 	# -- File System Operations
 	# --------------------------
 
+	def uncut(self):
+		self.env.copy = set()
+		self.env.cut = False
+		self.ui.browser.main_column.request_redraw()
+
 	def copy(self):
 		"""Copy the selected items"""
 
 		selected = self.env.get_selection()
 		self.env.copy = set(f for f in selected if f in self.env.cwd.files)
 		self.env.cut = False
+		self.ui.browser.main_column.request_redraw()
 
 	def cut(self):
 		self.copy()
 		self.env.cut = True
+		self.ui.browser.main_column.request_redraw()
 
 	def paste_symlink(self):
 		from os import symlink, getcwd
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index ae815fbf..459620c6 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -50,7 +50,6 @@ class FM(Actions, SignalDispatcher):
 		self.tabs = {}
 		self.current_tab = 1
 		self.loader = Loader()
-		self._executables = None
 		self.apps = self.settings.apps.CustomApplications()
 
 		def mylogfunc(text):
@@ -67,9 +66,8 @@ class FM(Actions, SignalDispatcher):
 
 	@property
 	def executables(self):
-		if self._executables is None:
-			self._executables = sorted(get_executables())
-		return self._executables
+		"""For compatibility. Calls get_executables()"""
+		return get_executables()
 
 	def initialize(self):
 		"""If ui/bookmarks are None, they will be initialized here."""
diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py
index b3500c0d..45f2ace3 100644
--- a/ranger/defaults/apps.py
+++ b/ranger/defaults/apps.py
@@ -46,6 +46,7 @@ This example modifies the behaviour of "feh" and adds a custom media player:
 """
 
 from ranger.api.apps import *
+from ranger.ext.get_executables import get_executables
 
 INTERPRETED_LANGUAGES = re.compile(r'''
 	^(text|application)\/x-(
@@ -103,7 +104,7 @@ class CustomApplications(Applications):
 		else:
 			parts = default_editor.split()
 			exe_name = os.path.basename(parts[0])
-			if exe_name in self.fm.executables:
+			if exe_name in get_executables():
 				return tuple(parts) + tuple(c)
 
 		return self.either(c, 'vim', 'emacs', 'nano')
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
index 81ebfea0..c181857e 100644
--- a/ranger/defaults/keys.py
+++ b/ranger/defaults/keys.py
@@ -133,6 +133,7 @@ map('V', fm.mark(all=True, val=False))
 # ------------------------------------------ file system operations
 map('yy', 'y<dir>', fm.copy())
 map('dd', 'd<dir>', fm.cut())
+map('ud', fm.uncut())
 map('pp', fm.paste())
 map('po', fm.paste(overwrite=True))
 map('pl', fm.paste_symlink())
diff --git a/ranger/ext/get_all_modules.py b/ranger/ext/get_all_modules.py
deleted file mode 100644
index 62c81437..00000000
--- a/ranger/ext/get_all_modules.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-def get_all_modules(dirname):
-	"""returns a list of strings containing the names of modules in a directory"""
-	import os
-	result = []
-	for filename in os.listdir(dirname):
-		if filename.endswith('.py') and not filename.startswith('_'):
-			result.append(filename[0:filename.index('.')])
-	return result
diff --git a/ranger/ext/get_executables.py b/ranger/ext/get_executables.py
index 9eeb3345..22c08eb9 100644
--- a/ranger/ext/get_executables.py
+++ b/ranger/ext/get_executables.py
@@ -13,12 +13,28 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import stat
-import os
-from os.path import isfile, join, exists
+from stat import S_IXOTH, S_IFREG
 from ranger.ext.iter_tools import unique
+from os import listdir, environ, stat
+from os.path import join
 
-def get_executables(*paths):
+
+_cached_executables = None
+
+
+def get_executables():
+	"""
+	Return all executable files in each of the given directories.
+
+	Looks in $PATH by default.
+	"""
+	global _cached_executables
+	if _cached_executables is None:
+		_cached_executables = sorted(get_executables_uncached())
+	return _cached_executables
+
+
+def get_executables_uncached(*paths):
 	"""
 	Return all executable files in each of the given directories.
 
@@ -26,7 +42,7 @@ def get_executables(*paths):
 	"""
 	if not paths:
 		try:
-			pathstring = os.environ['PATH']
+			pathstring = environ['PATH']
 		except KeyError:
 			return ()
 		paths = unique(pathstring.split(':'))
@@ -34,15 +50,16 @@ def get_executables(*paths):
 	executables = set()
 	for path in paths:
 		try:
-			content = os.listdir(path)
+			content = listdir(path)
 		except:
 			continue
 		for item in content:
 			abspath = join(path, item)
 			try:
-				filestat = os.stat(abspath)
+				filestat = stat(abspath)
 			except:
 				continue
-			if filestat.st_mode & (stat.S_IXOTH | stat.S_IFREG):
+			if filestat.st_mode & (S_IXOTH | S_IFREG):
 				executables.add(item)
 	return executables
+
diff --git a/ranger/ext/waitpid_no_intr.py b/ranger/ext/waitpid_no_intr.py
index c14fa5b9..12fbcbce 100644
--- a/ranger/ext/waitpid_no_intr.py
+++ b/ranger/ext/waitpid_no_intr.py
@@ -13,17 +13,18 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+from errno import EINTR
+from os import waitpid
+
 def waitpid_no_intr(pid):
 	"""catch interrupts which occur while using os.waitpid"""
-	import os, errno
-
 	while True:
 		try:
-			return os.waitpid(pid, 0)
+			return waitpid(pid, 0)
 		except KeyboardInterrupt:
 			continue
 		except OSError as e:
-			if e.errno == errno.EINTR:
+			if e.errno == EINTR:
 				continue
 			else:
 				raise
diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py
index bf626004..3574f329 100644
--- a/ranger/fsobject/directory.py
+++ b/ranger/fsobject/directory.py
@@ -237,6 +237,7 @@ class Directory(FileSystemObject, Accumulator, SettingsAware):
 		Loads the contents of the directory. Use this sparingly since
 		it takes rather long.
 		"""
+		self.content_outdated = False
 
 		if not self.loading:
 			self.load_once()
@@ -370,8 +371,7 @@ class Directory(FileSystemObject, Accumulator, SettingsAware):
 
 		if self.load_content_once(*a, **k): return True
 
-		if self.content_outdated:
-			self.content_outdated = False
+		if self.files is None or self.content_outdated:
 			self.load_content(*a, **k)
 			return True
 
@@ -403,6 +403,7 @@ class Directory(FileSystemObject, Accumulator, SettingsAware):
 		"""The number of containing files"""
 		if not self.accessible or not self.content_loaded:
 			raise ranger.fsobject.NotLoadedYet()
+		assert self.files is not None
 		return len(self.files)
 
 	def __eq__(self, other):
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index 4ea50714..d4c1c94d 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -23,7 +23,7 @@ CONTEXT_KEYS = ['reset', 'error',
 		'good', 'bad',
 		'space', 'permissions', 'owner', 'group', 'mtime', 'nlink',
 		'scroll', 'all', 'bot', 'top', 'percentage',
-		'marked', 'tagged', 'tag_marker',
+		'marked', 'tagged', 'tag_marker', 'cut', 'copied',
 		'help_markup',
 		'seperator', 'key', 'special', 'border',
 		'title', 'text', 'highlight', 'bars', 'quotes', 'tab',
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 4e93ed3e..8cf8990c 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -281,6 +281,9 @@ class BrowserColumn(Pager):
 				if stat.S_ISSOCK(mode):
 					this_color.append('socket')
 
+			if self.env.copy and drawn in self.env.copy:
+				this_color.append('cut' if self.env.cut else 'copied')
+
 			if drawn.islink:
 				this_color.append('link')
 				this_color.append(drawn.exists and 'good' or 'bad')
diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py
index 1995b714..466e23eb 100644
--- a/ranger/gui/widgets/browserview.py
+++ b/ranger/gui/widgets/browserview.py
@@ -264,7 +264,7 @@ class BrowserView(Widget, DisplayableContainer):
 	def poke(self):
 		DisplayableContainer.poke(self)
 		if self.settings.collapse_preview and self.preview:
-			has_preview = self.columns[-2].has_preview()
+			has_preview = self.columns[-1].has_preview()
 			if self.preview_available != has_preview:
 				self.preview_available = has_preview
 				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 0e949d3b..a27e1956 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -27,8 +27,9 @@ from ranger.defaults import commands
 from ranger.gui.widgets.console_mode import is_valid_mode, mode_to_class
 from ranger import log, relpath_conf
 from ranger.ext.shell_escape import shell_quote
-from ranger.ext.direction import Direction
 from ranger.container.keymap import CommandArgs
+from ranger.ext.get_executables import get_executables
+from ranger.ext.direction import Direction
 import ranger
 
 DEFAULT_HISTORY = 0
@@ -443,8 +444,8 @@ class OpenConsole(ConsoleWithTab):
 		try:
 			position_of_last_space = line.rindex(" ")
 		except ValueError:
-			return (start + program + ' ' for program in self.fm.executables \
-					if program.startswith(line))
+			return (start + program + ' ' for program \
+					in get_executables() if program.startswith(line))
 		if position_of_last_space == len(line) - 1:
 			return self.line + '%s '
 		else:
@@ -613,7 +614,7 @@ class QuickOpenConsole(ConsoleWithTab):
 
 	def _is_app(self, arg):
 		return self.fm.apps.has(arg) or \
-			(not self._is_flags(arg) and arg in self.fm.executables)
+			(not self._is_flags(arg) and arg in get_executables())
 
 	def _is_flags(self, arg):
 		from ranger.core.runner import ALLOWED_FLAGS
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index 75fbbe89..78666a3d 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -146,10 +146,9 @@ class StatusBar(Widget):
 		else:
 			target = self.env.at_level(0).pointed_obj
 
-		if target is None:
-			return
-
-		if target.accessible is False:
+		if target is None \
+				or not target.accessible \
+				or (target.is_directory and target.files is None):
 			return
 
 		perms = target.get_permission_string()
@@ -208,7 +207,9 @@ class StatusBar(Widget):
 		if target is None:
 			return
 
-		if not target.content_loaded or not target.accessible:
+		if target is None \
+				or not target.accessible \
+				or (target.is_directory and target.files is None):
 			return
 
 		pos = target.scroll_begin