summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/applications.py15
-rw-r--r--ranger/conf/apps.py61
-rw-r--r--ranger/conf/keys.py37
-rw-r--r--ranger/environment.py2
-rw-r--r--ranger/fm.py43
-rw-r--r--ranger/gui/ui.py26
-rw-r--r--ranger/gui/wconsole.py134
-rw-r--r--ranger/helper.py52
8 files changed, 317 insertions, 53 deletions
diff --git a/ranger/applications.py b/ranger/applications.py
new file mode 100644
index 00000000..fd977c43
--- /dev/null
+++ b/ranger/applications.py
@@ -0,0 +1,15 @@
+ALLOWED_FLAGS = 'sdpSDP'
+
+class Applications(object):
+	def get(self, app):
+		try:
+			return getattr(self, 'app_' + app)
+		except AttributeError:
+			return self.app_default
+
+	def has(self, app):
+		return hasattr(self, 'app_' + app)
+
+	def all(self):
+		return [x for x in self.__dict__ if x.startswith('app_')]
+
diff --git a/ranger/conf/apps.py b/ranger/conf/apps.py
new file mode 100644
index 00000000..041b85c8
--- /dev/null
+++ b/ranger/conf/apps.py
@@ -0,0 +1,61 @@
+from ranger.applications import Applications as SuperClass
+from ranger.helper import popen as run
+
+class CustomApplications(SuperClass):
+	# How to determine the default application? {{{
+	def app_default(self, **kw):
+		f = kw['mainfile']
+
+		if f.extension is not None and f.extension in ('pdf'):
+			return self.app_evince(**kw)
+
+		if f.container:
+			return self.app_aunpack(**kw)
+
+		if f.video or f.audio:
+			if f.video:
+				kw['flags'] += 'd'
+			return self.app_mplayer(**kw)
+		
+		if f.image:
+			return self.app_feh(**kw)
+
+		if f.document:
+			return self.app_editor(**kw)
+	# }}}
+
+	def app_pager(self, **kw):
+		return run('less', *kw['files'], **kw)
+
+	def app_vim(self, **kw):
+		return run('vim', *kw['files'], **kw)
+
+	app_editor = app_vim
+
+	def app_mplayer(self, **kw):
+		if kw['mode'] == 1:
+			return run('mplayer', *kw['files'], **kw)
+
+		elif kw['mode'] == 2:
+			return run('mplayer', '-fs',
+					'-sid', '0',
+					'-vfm', 'ffmpeg',
+					'-lavdopts', 'lowres=1:fast:skiploopfilter=all:threads=8',
+					*kw['files'], **kw)
+
+		else:
+			return run('mplayer', '-fs', *kw['files'], **kw)
+
+	def app_feh(self, **kw):
+		return run('feh', *kw['files'], **kw)
+
+	def app_aunpack(self, **kw):
+		m = kw['mode']
+		if m == 0:
+			kw['flags'] += 'p'
+			return run('aunpack', '-l', *kw['files'], **kw)
+		elif m == 1:
+			return run('aunpack', *kw['files'], **kw)
+	
+	def app_evince(self, **kw):
+		return run('evince', *kw['files'], **kw)
diff --git a/ranger/conf/keys.py b/ranger/conf/keys.py
index fef36b6e..b9816919 100644
--- a/ranger/conf/keys.py
+++ b/ranger/conf/keys.py
@@ -12,6 +12,7 @@ def initialize_commands(cl):
 
 	def curry(fnc, *args, **keywords):
 		return lambda fm: fnc(fm, *args, **keywords)
+	c=curry
 
 	def move(**keywords):
 		return lambda fm: fm.move_pointer(**keywords)
@@ -20,9 +21,10 @@ def initialize_commands(cl):
 		return lambda fm: fm.move_pointer_by_pages(n)
 
 	cl.bind(FM.move_left,               'h', curses.KEY_BACKSPACE, 127)
-	cl.bind(FM.move_right,              'l', curses.KEY_ENTER, ctrl('j'))
-	cl.bind(curry(FM.history_go, -1),   'H')
-	cl.bind(curry(FM.history_go,  1),   'L')
+	cl.bind(FM.move_right,              'l')
+	cl.bind(c(FM.move_right, mode=1),   curses.KEY_ENTER, ctrl('j'))
+	cl.bind(c(FM.history_go, -1),       'H')
+	cl.bind(c(FM.history_go,  1),       'L')
 	cl.bind(move( relative = 1 ),       'j')
 	cl.bind(move_pages( 0.5 ),          'J')
 	cl.bind(move( relative = -1 ),      'k')
@@ -60,6 +62,9 @@ def initialize_commands(cl):
 	cl.bind(FM.resize,       curses.KEY_RESIZE)
 	cl.bind(FM.handle_mouse, curses.KEY_MOUSE)
 	cl.bind(curry(FM.open_console, ':'), ':')
+	cl.bind(curry(FM.open_console, '/'), '/')
+	cl.bind(curry(FM.open_console, '!'), '!')
+	cl.bind(curry(FM.open_console, '@'), 'r')
 
 	cl.rebuild_paths()
 
@@ -78,22 +83,24 @@ def initialize_console_commands(cl):
 
 	def curry_fm(fnc, *args, **keywords):
 		return lambda con, fm: fnc(fm, *args, **keywords)
+	c_fm = curry_fm
+	c = curry
 
 	# movement
-	cl.bind(curry(WConsole.move, relative = -1), curses.KEY_LEFT, ctrl('b'))
-	cl.bind(curry(WConsole.move, relative =  1), curses.KEY_RIGHT, ctrl('f'))
-	cl.bind(curry(WConsole.move, absolute = 0), curses.KEY_HOME, ctrl('a'))
-	cl.bind(curry(WConsole.move, absolute = -1), curses.KEY_END, ctrl('e'))
-	cl.bind(curry(WConsole.delete, 0), curses.KEY_DC, ctrl('d'))
-	cl.bind(curry(WConsole.delete, -1), curses.KEY_BACKSPACE, 127, ctrl('h'))
-	cl.bind(curry(WConsole.delete_rest, -1), ctrl('U'))
-	cl.bind(curry(WConsole.delete_rest,  1), ctrl('K'))
+	cl.bind(c(WConsole.move, relative = -1), curses.KEY_LEFT, ctrl('b'))
+	cl.bind(c(WConsole.move, relative =  1), curses.KEY_RIGHT, ctrl('f'))
+	cl.bind(c(WConsole.move, absolute = 0), curses.KEY_HOME, ctrl('a'))
+	cl.bind(c(WConsole.move, absolute = -1), curses.KEY_END, ctrl('e'))
+	cl.bind(c(WConsole.delete, 0), curses.KEY_DC, ctrl('d'))
+	cl.bind(c(WConsole.delete, -1), curses.KEY_BACKSPACE, 127, ctrl('h'))
+	cl.bind(c(WConsole.delete_rest, -1), ctrl('U'))
+	cl.bind(c(WConsole.delete_rest,  1), ctrl('K'))
 
 	# system functions
-	cl.bind(curry(WConsole.close),    ESC, ctrl('C'))
-	cl.bind(curry(WConsole.execute),  curses.KEY_ENTER, ctrl('j'))
-	cl.bind(curry_fm(FM.redraw), ctrl('L'))
-	cl.bind(curry_fm(FM.resize), curses.KEY_RESIZE)
+	cl.bind(c(WConsole.close),    ESC, ctrl('C'))
+	cl.bind(WConsole.execute,  curses.KEY_ENTER, ctrl('j'))
+	cl.bind(c_fm(FM.redraw), ctrl('L'))
+	cl.bind(c_fm(FM.resize), curses.KEY_RESIZE)
 
 	for i in range(ord(' '), ord('~')):
 		cl.bind(type_key(i), i)
diff --git a/ranger/environment.py b/ranger/environment.py
index e0ac646a..f5dbe176 100644
--- a/ranger/environment.py
+++ b/ranger/environment.py
@@ -86,6 +86,8 @@ class Environment():
 				self.history.pop(0)
 
 	def enter_dir(self, path, history = True):
+		path = str(path)
+
 		# get the absolute path
 		path = normpath(join(self.path, expanduser(path)))
 
diff --git a/ranger/fm.py b/ranger/fm.py
index 05c1e262..95110455 100644
--- a/ranger/fm.py
+++ b/ranger/fm.py
@@ -1,10 +1,12 @@
 from os import devnull
+from ranger.conf.apps import CustomApplications as Applications
 null = open(devnull, 'a')
 
 class FM():
 	def __init__(self, environment, ui):
 		self.env = environment
 		self.ui = ui
+		self.apps = Applications()
 
 	def run(self):
 		self.env.enter_dir(self.env.path)
@@ -39,13 +41,10 @@ class FM():
 	def move_left(self):
 		self.env.enter_dir('..')
 
-	def move_right(self):
-		try:
-			path = self.env.cf.path
-			if not self.env.enter_dir(path):
-				self.execute_file(path)
-		except AttributeError:
-			pass
+	def move_right(self, mode = 0):
+		cf = self.env.cf
+		if not self.env.enter_dir(cf):
+			self.execute_file(cf, mode = mode)
 
 	def history_go(self, relative):
 		self.env.history_go(relative)
@@ -53,21 +52,23 @@ class FM():
 	def handle_mouse(self):
 		self.ui.handle_mouse(self)
 
-	def execute_file(self, path):
-		from subprocess import Popen
-		Popen(('mplayer', '-fs', path), stdout = null, stderr = null)
-
+	def execute_file(self, files, app = '', flags = '', mode = 0):
+		if type(files) not in (list, tuple):
+			files = [files]
+
+		self.apps.get(app)(
+				mainfile = files[0],
+				files = files,
+				flags = flags,
+				mode = mode,
+				fm = self,
+				stdin = None,
+				apps = self.apps)
+	
 	def edit_file(self):
-		from subprocess import Popen
-		import os
-		if self.env.cf is None: return
-
-		self.ui.exit()
-
-		p = Popen(('vim', self.env.cf.path))
-		os.waitpid(p.pid, 0)
-
-		self.ui.initialize()
+		if self.env.cf is None:
+			return
+		self.execute_file(self.env.cf, app = 'editor')
 
 	def open_console(self, mode = ':'):
 		if self.ui.can('open_console'):
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 79077cbc..5cc519b1 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -19,22 +19,23 @@ class MouseEvent():
 
 class UI():
 	def __init__(self, env, commandlist, colorscheme):
+		import os
+		os.environ['ESCDELAY'] = '25' # don't know a cleaner way
+
 		self.env = env
 		self.commandlist = commandlist
 		self.colorscheme = colorscheme
 		self.is_set_up = False
+		self.win = curses.initscr()
 
 		self.widgets = []
 
 	def initialize(self):
-		# dunno if there's a better way for doing this:
-		import os
-		os.environ['ESCDELAY'] = '25'
 
-		self.win = curses.initscr()
 		self.win.leaveok(0)
 		self.win.keypad(1)
 
+		curses.cbreak()
 		curses.noecho()
 		curses.halfdelay(20)
 		curses.curs_set(0)
@@ -48,7 +49,17 @@ class UI():
 		if not self.is_set_up:
 			self.is_set_up = True
 			self.setup()
-			self.resize()
+		self.resize()
+
+	def exit(self):
+		from ranger.helper import log
+		log("exiting ui!")
+		self.win.keypad(0)
+		curses.nocbreak()
+		curses.echo()
+		curses.curs_set(1)
+		curses.mousemask(0)
+		curses.endwin()
 
 	def handle_mouse(self, fm):
 		try:
@@ -108,11 +119,6 @@ class UI():
 		cmd.execute(fm)
 		self.env.key_clear()
 
-	def exit(self):
-		curses.nocbreak()
-		curses.echo()
-		curses.endwin()
-
 	def draw(self):
 		self.win.erase()
 		for widg in self.widgets:
diff --git a/ranger/gui/wconsole.py b/ranger/gui/wconsole.py
index 4411e83b..fdaa7716 100644
--- a/ranger/gui/wconsole.py
+++ b/ranger/gui/wconsole.py
@@ -1,7 +1,8 @@
 from ranger.gui.widget import Widget as SuperClass
 import curses
 
-CONSOLE_MODES = tuple(':/?>!')
+CONSOLE_MODES = tuple(':@/?>!')
+CONSOLE_MODES_DICTIONARY = { '@': 'open with: ' }
 
 class WConsole(SuperClass):
 	def __init__(self, win, colorscheme):
@@ -14,16 +15,26 @@ class WConsole(SuperClass):
 		initialize_console_commands(self.commandlist)
 		self.last_cursor_mode = 1
 		self.clear()
+		self.prompt = None
+		self.execute_funcs = {
+				':': WConsole.execute_command,
+				'@': WConsole.execute_openwith_quick,
+				'/': WConsole.execute_search,
+				'?': WConsole.execute_search,
+				'>': WConsole.execute_noreturn,
+				'!': WConsole.execute_openwith }
+	
+	def feed_env(self, env):
+		self.cf = env.cf
 
 	def draw(self):
 		if self.mode is None:
 			return
-
-		self.win.addstr(self.y, self.x, ":" + self.line)
+		self.win.addstr(self.y, self.x, self.prompt + self.line)
 
 	def finalize(self):
 		try:
-			self.win.move(self.y, self.x + self.pos + 1)
+			self.win.move(self.y, self.x + self.pos + len(self.prompt))
 		except:
 			pass
 
@@ -33,6 +44,10 @@ class WConsole(SuperClass):
 
 		self.last_cursor_mode = curses.curs_set(1)
 		self.mode = mode
+		try:
+			self.prompt = CONSOLE_MODES_DICTIONARY[self.mode]
+		except KeyError:
+			self.prompt = self.mode
 		self.focused = True
 		self.visible = True
 		return True
@@ -50,8 +65,8 @@ class WConsole(SuperClass):
 	
 	def press(self, key, fm, env):
 		from curses.ascii import ctrl, ESC
-		from ranger.helper import log
-		log(key)
+#		from ranger.helper import log
+#		log(key)
 
 		try:
 			cmd = self.commandlist.paths[env.keybuffer]
@@ -93,13 +108,118 @@ class WConsole(SuperClass):
 			self.pos = 0
 	
 	def delete(self, mod):
+		if mod == -1 and len(self.line) == 0:
+			self.close()
 		pos = self.pos + mod
 
 		self.line = self.line[0:pos] + self.line[pos+1:]
 		self.move(relative = mod)
 
-	def execute(self):
+	def execute(self, fm):
+		try:
+			self.execute_funcs[self.mode] (self, fm)
+		except KeyError:
+			pass
 		self.line = ''
 		self.pos = 0
 		self.close()
 
+	def execute_search(self, fm):
+		pass
+
+	def execute_openwith(self, fm):
+		line = self.line
+		if line[0] == '!':
+			fm.execute_file(tuple(line[1:].split()) + (fm.env.cf.path, ))
+		else:
+			fm.execute_file(tuple(line.split()) + (fm.env.cf.path, ), background = True)
+
+	def execute_openwith_quick(self, fm):
+		split = self.line.split()
+		app, flags, mode = get_app_flags_mode(self.line, fm)
+		fm.execute_file(
+				files = [self.cf],
+				app = app,
+				flags = flags,
+				mode = mode )
+
+	def execute_noreturn(self, fm):
+		pass
+
+	def execute_command(self, fm):
+		pass
+
+def get_app_flags_mode(line, fm):
+	app = ''
+	flags = ''
+	mode = 0
+	split = line.split()
+
+	if len(split) == 0:
+		pass
+
+	elif len(split) == 1:
+		part = split[0]
+		if is_app(part, fm):
+			app = part
+		elif is_flags(part):
+			flags = part
+		elif is_mode(part):
+			mode = part
+
+	elif len(split) == 2:
+		part0 = split[0]
+		part1 = split[1]
+
+		if is_app(part0, fm):
+			app = part0
+			if is_flags(part1):
+				flags = part1
+			elif is_mode(part1):
+				mode = part1
+		elif is_flags(part0):
+			flags = part0
+			if is_mode(part1):
+				mode = part1
+		elif is_mode(part0):
+			mode = part0
+			if is_flags(part1):
+				flags = part1
+
+	elif len(split) >= 3:
+		part0 = split[0]
+		part1 = split[1]
+		part2 = split[2]
+
+		if is_app(part0, fm):
+			app = part0
+			if is_flags(part1):
+				flags = part1
+				if is_mode(part2):
+					mode = part2
+			elif is_mode(part1):
+				mode = part1
+				if is_flags(part2):
+					flags = part2
+		elif is_flags(part0):
+			flags = part0
+			if is_mode(part1):
+				mode = part1
+		elif is_mode(part0):
+			mode = part0
+			if is_flags(part1):
+				flags = part1
+
+	return app, flags, int(mode)
+
+def is_app(arg, fm):
+	return fm.apps.has(arg)
+
+def is_flags(arg):
+	from ranger.applications import ALLOWED_FLAGS
+	return all(x in ALLOWED_FLAGS for x in arg)
+
+def is_mode(arg):
+	return all(x in '0123456789' for x in arg)
+
+
diff --git a/ranger/helper.py b/ranger/helper.py
index 5ca40175..150f4f9d 100644
--- a/ranger/helper.py
+++ b/ranger/helper.py
@@ -29,3 +29,55 @@ def human_readable(byte):
 
 	else:
 		return '%.2f %s' % (flt, UNITS[its])
+
+def waitpid_no_intr(pid):
+	import os, errno
+
+	while True:
+		try:
+			return os.waitpid(pid, 0)
+		except OSError as e:
+			if e.errno == errno.EINTR:
+				continue
+			else:
+				raise
+
+import os
+null = open(os.devnull, 'a')
+
+def popen(*args, **kw):
+	from subprocess import Popen
+	from subprocess import PIPE
+
+	flags, fm = kw['flags'], kw['fm']
+	for flag in flags:
+		if ord(flag) <= 90:
+			bad = flag + flag.lower()
+			flags = ''.join(c for c in flags if c not in bad)
+
+	args = map(str, args)
+	popen_kw = {}
+
+	if kw['stdin'] is not None:
+		popen_kw['stdin'] = kw['stdin']
+
+	if 's' in flags or 'd' in flags:
+		popen_kw['stdout'] = popen_kw['stderr'] = popen_kw['stdin'] = null
+	
+	if 'p' in flags:
+		popen_kw['stdout'] = PIPE
+		p1 = Popen(args, **popen_kw)
+		kw['stdin'] = p1.stdout
+		kw['files'] = ()
+		kw['flags'] = ''.join(f for f in kw['flags'] if f in 'd')
+		p2 = kw['apps'].app_pager(**kw)
+		return p2
+	if 'd' in flags:
+		p = Popen(args, **popen_kw)
+		return p
+	else:
+		fm.ui.exit()
+		p = Popen(args, **popen_kw)
+		waitpid_no_intr(p.pid)
+		fm.ui.initialize()
+		return p