summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--doc/print_keys.py14
-rw-r--r--ranger/api/keys.py1
-rw-r--r--ranger/defaults/apps.py4
-rw-r--r--ranger/defaults/commands.py41
-rw-r--r--ranger/defaults/keys.py14
-rw-r--r--ranger/defaults/options.py3
-rw-r--r--ranger/ext/command_parser.py16
-rw-r--r--ranger/ext/shutil_generatorized.py12
-rw-r--r--ranger/gui/ui.py33
-rw-r--r--ranger/shared/settings.py22
10 files changed, 136 insertions, 24 deletions
diff --git a/doc/print_keys.py b/doc/print_keys.py
new file mode 100644
index 00000000..0790acab
--- /dev/null
+++ b/doc/print_keys.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python
+"""
+You can use this tool to find out values of keypresses
+"""
+
+from curses import *
+
+sep = '; '
+
+@wrapper
+def main(w):
+	while True:
+		w.addstr(str(w.getch()) + sep)
+
diff --git a/ranger/api/keys.py b/ranger/api/keys.py
index 5f12e75d..00479b0d 100644
--- a/ranger/api/keys.py
+++ b/ranger/api/keys.py
@@ -75,7 +75,6 @@ def narg(number_, function_, *args_, **keywords_):
 	narg(50, foo, 123) == foo(123, narg=50)
 	"""
 	args, keywords = replace_narg(number_, function_, args_, keywords_)
-	print(args, keywords)
 	return function_(*args, **keywords)
 
 def replace_narg(number, function, args, keywords):
diff --git a/ranger/defaults/apps.py b/ranger/defaults/apps.py
index 9543badb..b3500c0d 100644
--- a/ranger/defaults/apps.py
+++ b/ranger/defaults/apps.py
@@ -58,9 +58,11 @@ class CustomApplications(Applications):
 		f = c.file
 
 		if f.extension is not None:
-			if f.extension in ('pdf'):
+			if f.extension in ('pdf' ):
 				c.flags += 'd'
 				return self.either(c, 'evince', 'zathura', 'apvlv')
+			if f.extension in ('xml', ):
+				return self.app_editor(c)
 			if f.extension in ('html', 'htm', 'xhtml', 'swf'):
 				return self.either(c, 'firefox', 'opera', 'elinks')
 			if f.extension in ('swc', 'smc'):
diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py
index 89b1a3cb..c5c47340 100644
--- a/ranger/defaults/commands.py
+++ b/ranger/defaults/commands.py
@@ -58,7 +58,7 @@ class Command(FileManagerAware):
 		rel_dirname = dirname(rel_dest)
 
 		try:
-			# are we after a directory?
+			# are we at the end of a directory?
 			if rel_dest.endswith('/') or rel_dest == '':
 				_, dirnames, _ = os.walk(abs_dest).next()
 
@@ -107,7 +107,7 @@ class Command(FileManagerAware):
 		rel_dirname = dirname(rel_dest)
 
 		try:
-			# are we after a directory?
+			# are we at the end of a directory?
 			if rel_dest.endswith('/') or rel_dest == '':
 				_, dirnames, filenames = os.walk(abs_dest).next()
 				names = dirnames + filenames
@@ -236,6 +236,43 @@ class find(Command):
 		return self.count == 1
 
 
+class set(Command):
+	"""
+	:set <option name>=<python expression>
+
+	Gives an option a new value.
+	"""
+	def execute(self):
+		line = parse(self.line)
+		name = line.chunk(1)
+		name, value, _ = line.parse_setting_line()
+		if name and value:
+			try:
+				value = eval(value)
+			except:
+				pass
+			self.fm.settings[name] = value
+
+	def tab(self):
+		line = parse(self.line)
+		from ranger import log
+		log(line.parse_setting_line())
+		name, value, name_done = line.parse_setting_line()
+		settings = self.fm.settings
+		if not name:
+			return (line + setting for setting in settings)
+		if not value and not name_done:
+			return (line + setting for setting in settings \
+					if setting.startswith(name))
+		if not value:
+			return line + repr(settings[name])
+		if bool in settings.types_of(name):
+			if 'true'.startswith(value.lower()):
+				return line + 'True'
+			if 'false'.startswith(value.lower()):
+				return line + 'False'
+
+
 class quit(Command):
 	"""
 	:quit
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
index cbdf3c4c..b43744a9 100644
--- a/ranger/defaults/keys.py
+++ b/ranger/defaults/keys.py
@@ -80,8 +80,18 @@ def initialize_commands(map):
 	map(ctrl('d'), 'J', fm.move(down=0.5, pages=True))
 	map(ctrl('u'), 'K', fm.move(up=0.5, pages=True))
 
-	map(']', fm.traverse())
-	map('[', fm.history_go(-1))
+	def move_parent(n):
+		def fnc(arg):
+			arg.fm.move_left()
+			arg.fm.move_pointer(n)
+			if arg.fm.env.cf.is_directory:
+				arg.fm.move_right()
+		return fnc
+
+	map(']', move_parent(1))
+	map('[', move_parent(-1))
+	map('}', fm.traverse())
+	map('{', fm.history_go(-1))
 
 	# --------------------------------------------------------- history
 	map('H', fm.history_go(-1))
diff --git a/ranger/defaults/options.py b/ranger/defaults/options.py
index 6b2a0dc9..d96955b7 100644
--- a/ranger/defaults/options.py
+++ b/ranger/defaults/options.py
@@ -61,6 +61,9 @@ draw_bookmark_borders = True
 # How many columns are there, and what are their relative widths?
 column_ratios = (1, 1, 4, 3)
 
+# Enable the mouse support?
+mouse_enabled = True
+
 # Display the file size in the main column or status bar?
 display_size_in_main_column = True
 display_size_in_status_bar = False
diff --git a/ranger/ext/command_parser.py b/ranger/ext/command_parser.py
index a6971631..3a676e8f 100644
--- a/ranger/ext/command_parser.py
+++ b/ranger/ext/command_parser.py
@@ -13,12 +13,16 @@
 # 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 re
+SETTINGS_RE = re.compile(r'^([^\s]+?)=(.*)$')
+
 class LazyParser(object):
 	"""Parse commands and extract information"""
 	def __init__(self, line):
 		self.line = line
 		self._chunks = None
 		self._rests = None
+		self._setting_line = None
 		self._rests_loaded = 0
 		self._rests_gen_instance = None
 
@@ -62,5 +66,17 @@ class LazyParser(object):
 				lastrest = lastrest[n:]
 				n = 0
 
+	def parse_setting_line(self):
+		if self._setting_line is not None:
+			return self._setting_line
+		match = SETTINGS_RE.match(self.rest(1))
+		if match:
+			self.firstpart += match.group(1) + '='
+			result = [match.group(1), match.group(2), True]
+		else:
+			result = [self.chunk(1), self.rest(2), ' ' in self.rest(1)]
+		self._setting_line = result
+		return result
+
 	def __add__(self, newpart):
 		return self.firstpart + newpart
diff --git a/ranger/ext/shutil_generatorized.py b/ranger/ext/shutil_generatorized.py
index 8bf07ace..ca6be426 100644
--- a/ranger/ext/shutil_generatorized.py
+++ b/ranger/ext/shutil_generatorized.py
@@ -83,18 +83,22 @@ def copymode(src, dst):
     if hasattr(os, 'chmod'):
         st = os.stat(src)
         mode = stat.S_IMODE(st.st_mode)
-        os.chmod(dst, mode)
+        try: os.chmod(dst, mode)
+        except: pass
 
 def copystat(src, dst):
     """Copy all stat info (mode bits, atime, mtime, flags) from src to dst"""
     st = os.stat(src)
     mode = stat.S_IMODE(st.st_mode)
     if hasattr(os, 'utime'):
-        os.utime(dst, (st.st_atime, st.st_mtime))
+        try: os.utime(dst, (st.st_atime, st.st_mtime))
+        except: pass
     if hasattr(os, 'chmod'):
-        os.chmod(dst, mode)
+        try: os.chmod(dst, mode)
+        except: pass
     if hasattr(os, 'chflags') and hasattr(st, 'st_flags'):
-        os.chflags(dst, st.st_flags)
+        try: os.chflags(dst, st.st_flags)
+        except: pass
 
 
 def copy(src, dst, overwrite=False):
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 458cfa5d..6d9c78ad 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -27,9 +27,25 @@ TERMINALS_WITH_TITLE = ("xterm", "xterm-256color", "rxvt",
 		"rxvt-256color", "rxvt-unicode", "aterm", "Eterm",
 		"screen", "screen-256color")
 
+MOUSEMASK = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION
+
+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)
+
 class UI(DisplayableContainer):
 	is_set_up = False
-	mousemask = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION
 	load_mode = False
 	def __init__(self, commandlist=None, env=None, fm=None):
 		self._draw_title = os.environ["TERM"] in TERMINALS_WITH_TITLE
@@ -66,16 +82,8 @@ class UI(DisplayableContainer):
 		curses.start_color()
 		curses.use_default_colors()
 
-		curses.mousemask(self.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)
+		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
@@ -91,7 +99,8 @@ class UI(DisplayableContainer):
 			curses.curs_set(1)
 		except:
 			pass
-		curses.mousemask(0)
+		if self.settings.mouse_enabled:
+			_setup_mouse(dict(value=False))
 		curses.endwin()
 
 	def set_load_mode(self, boolean):
diff --git a/ranger/shared/settings.py b/ranger/shared/settings.py
index a4a58e6e..c5544a47 100644
--- a/ranger/shared/settings.py
+++ b/ranger/shared/settings.py
@@ -39,6 +39,7 @@ ALLOWED_SETTINGS = {
 	'scroll_offset': int,
 	'preview_files': bool,
 	'preview_directories': bool,
+	'mouse_enabled': bool,
 	'flushinput': bool,
 	'colorscheme': str,
 	'colorscheme_overlay': (type(None), type(lambda:0)),
@@ -57,6 +58,9 @@ class SettingObject(SignalDispatcher):
 		SignalDispatcher.__init__(self)
 		self.__dict__['_settings'] = dict()
 		self.__dict__['_setting_sources'] = list()
+		for name in ALLOWED_SETTINGS:
+			self.signal_bind('setopt.'+name,
+					self._raw_set_with_signal, priority=0.2)
 
 	def __setattr__(self, name, value):
 		if name[0] == '_':
@@ -66,8 +70,6 @@ class SettingObject(SignalDispatcher):
 			assert self._check_type(name, value)
 			kws = dict(setting=name, value=value,
 					previous=self._settings[name])
-			self.signal_bind('setopt.'+name,
-					self._raw_set_with_signal, priority=0.2)
 			self.signal_emit('setopt', **kws)
 			self.signal_emit('setopt.'+name, **kws)
 
@@ -89,6 +91,22 @@ class SettingObject(SignalDispatcher):
 			self.__setattr__(name, value)
 			return self._settings[name]
 
+	def __iter__(self):
+		for x in self._settings:
+			yield x
+
+	def types_of(self, name):
+		try:
+			typ = ALLOWED_SETTINGS[name]
+		except KeyError:
+			return tuple()
+		else:
+			if isinstance(typ, tuple):
+				return typ
+			else:
+				return (typ, )
+
+
 	def _check_type(self, name, value):
 		from inspect import isfunction
 		typ = ALLOWED_SETTINGS[name]