about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2012-03-16 00:18:34 +0100
committerhut <hut@lavabit.com>2012-03-16 00:18:34 +0100
commit6f8ca83b677412aee792e55d24c62b7d24d4ea84 (patch)
tree5d57326482702b964281545f82ee289f8aa2cb77
parent4dc87fa8ab83844707659e1f82e1e9726279a8a2 (diff)
downloadranger-6f8ca83b677412aee792e55d24c62b7d24d4ea84.tar.gz
implemented "rifle", a new file launcher
ranger/ext/rifle.py is a standalone program that works without ranger.
The configuration is not in python anymore, thus is easier to
understand, modify and parse.
-rw-r--r--ranger/core/actions.py24
-rw-r--r--ranger/core/fm.py15
-rw-r--r--ranger/defaults/rc.conf2
-rw-r--r--ranger/defaults/rifle.conf122
-rw-r--r--ranger/ext/rifle.py145
-rw-r--r--ranger/gui/widgets/browserview.py16
-rw-r--r--ranger/gui/widgets/console.py1
7 files changed, 317 insertions, 8 deletions
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 91261207..5b824476 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -273,8 +273,10 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		mode is a positive integer.
 		Both flags and mode specify how the program is run."""
 
+		mode = kw['mode'] if 'mode' in kw else 0
+
 		# ranger can act as a file chooser when running with --choosefile=...
-		if ('mode' not in kw or kw['mode'] == 0) and 'app' not in kw:
+		if mode == 0 and 'label' not in kw:
 			if ranger.arg.choosefile:
 				open(ranger.arg.choosefile, 'w').write(self.fm.env.cf.path)
 
@@ -298,9 +300,14 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 				files = [self.fm.env.cf]
 
 		self.signal_emit('execute.before', keywords=kw)
+		filenames = [f.path for f in files]
+		mimetype = files[0].mimetype if files else None
+		label = kw['label'] if 'label' in kw else None
 		try:
-			return self.run(files=list(files), **kw)
+			self.ui.suspend()
+			return self.rifle.execute(filenames, mode, label, mimetype)
 		finally:
+			self.ui.initialize()
 			self.signal_emit('execute.after')
 
 	# --------------------------
@@ -467,14 +474,14 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		return self.run(cmd, **kw)
 
 	def edit_file(self, file=None):
-		"""Calls execute_file with the current file and app='editor'"""
+		"""Calls execute_file with the current file and label='editor'"""
 		if file is None:
 			file = self.env.cf
 		elif isinstance(file, str):
 			file = File(os.path.expanduser(file))
 		if file is None:
 			return
-		self.execute_file(file, app = 'editor')
+		self.execute_file(file, label='editor')
 
 	def toggle_option(self, string):
 		"""Toggle a boolean option named <string>"""
@@ -679,6 +686,15 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 	def hide_bookmarks(self):
 		self.ui.browser.draw_bookmarks = False
 
+	def draw_possible_programs(self):
+		selection = [f.path for f in self.env.get_selection()]
+		programs = self.rifle.list_commands(selection)
+		programs = ['%s | %s' % program[0:2] for program in programs]
+		self.ui.browser.draw_info = programs
+
+	def hide_console_info(self):
+		self.ui.browser.draw_info = False
+
 	# --------------------------
 	# -- Pager
 	# --------------------------
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index 83eec582..77c0d7cc 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -20,6 +20,7 @@ from ranger.gui.ui import UI
 from ranger.container.bookmarks import Bookmarks
 from ranger.core.runner import Runner
 from ranger.ext.get_executables import get_executables
+from ranger.ext.rifle import Rifle
 from ranger.fsobject import Directory
 from ranger.ext.signals import SignalDispatcher
 from ranger import __version__
@@ -52,6 +53,14 @@ class FM(Actions, SignalDispatcher):
 
 	def initialize(self):
 		"""If ui/bookmarks are None, they will be initialized here."""
+
+		if not ranger.arg.clean and os.path.isfile(self.confpath('rifle.conf')):
+			rifleconf = self.confpath('rifle.conf')
+		else:
+			rifleconf = self.relpath('defaults/rifle.conf')
+		self.rifle = Rifle(rifleconf)
+		self.rifle.reload_config()
+
 		if self.bookmarks is None:
 			if ranger.arg.clean:
 				bookmarkfile = None
@@ -118,8 +127,8 @@ class FM(Actions, SignalDispatcher):
 					shutil.copy(self.relpath(_from), self.confpath(to))
 				except Exception as e:
 					sys.stderr.write("  ERROR: %s\n" % str(e))
-		if which == 'apps' or which == 'all':
-			copy('defaults/apps.py', 'apps.py')
+		if which == 'rifle' or which == 'all':
+			copy('defaults/rifle.conf', 'rifle.conf')
 		if which == 'commands' or which == 'all':
 			copy('defaults/commands.py', 'commands.py')
 		if which == 'rc' or which == 'all':
@@ -131,7 +140,7 @@ class FM(Actions, SignalDispatcher):
 			os.chmod(self.confpath('scope.sh'),
 				os.stat(self.confpath('scope.sh')).st_mode | stat.S_IXUSR)
 		if which not in \
-				('all', 'apps', 'scope', 'commands', 'rc', 'options'):
+				('all', 'rifle', 'scope', 'commands', 'rc', 'options'):
 			sys.stderr.write("Unknown config file `%s'\n" % which)
 
 	def confpath(self, *paths):
diff --git a/ranger/defaults/rc.conf b/ranger/defaults/rc.conf
index 77ffa5c3..5ac2763a 100644
--- a/ranger/defaults/rc.conf
+++ b/ranger/defaults/rc.conf
@@ -53,7 +53,7 @@ map !  console shell
 map @  console -p6 shell  %%s
 map #  console shell -p 
 map s  console shell 
-map r  console open_with 
+map r  chain draw_possible_programs; console open_with 
 map f  console find 
 map cd console cd 
 
diff --git a/ranger/defaults/rifle.conf b/ranger/defaults/rifle.conf
new file mode 100644
index 00000000..5af05a99
--- /dev/null
+++ b/ranger/defaults/rifle.conf
@@ -0,0 +1,122 @@
+#-------------------------------------------
+# Misc
+#-------------------------------------------
+name [xX]modmap = xmodmap "$1"
+ext 1           = man "$1"
+ext s[wmf]c, has zsnes, X = zsnes "$1"
+ext nes, has fceux, X     = fceux "$1"
+ext exe = wine "$1"
+name ^[mM]akefile$ = make
+
+mime ^text = "$EDITOR" -- "$@"
+ext xml|csv|tex = "$EDITOR" -- "$@"
+
+#--------------------------------------------
+# Code
+#-------------------------------------------
+
+label execute, ext py  = python -- "$1"
+label execute, ext pl  = perl -- "$1"
+label execute, ext rb  = ruby -- "$1"
+label execute, ext sh  = bash -- "$1"
+label execute, ext c   = gcc -o /tmp/a.out "$1" && /tmp/a.out
+label execute, ext tex = pdflatex -- "$1" && "$rifle" "$(echo -n "$1" | sed 's/\..*$//')".pdf
+
+label compile, ext c   = gcc -o /tmp/a.out "$1"
+
+#--------------------------------------------
+# Audio without X
+#-------------------------------------------
+mime ^audio, terminal, has mplayer  = mplayer -- "$@"
+mime ^audio, terminal, has mplayer2 = mplayer2 -- "$@"
+
+#--------------------------------------------
+# Video/Audio with a GUI
+#-------------------------------------------
+
+mime ^video|audio, has gmplayer, X, flag f = gmplayer -- "$@"
+mime ^video|audio, has smplayer, X, flag f = smplayer -- "$@"
+mime ^video,       has mplayer2, X, flag f = mplayer2 -- "$@"
+mime ^video,       has mplayer,  X, flag f = mplayer -- "$@"
+mime ^video,       has mplayer,  X, flag f = mplayer -fs -- "$@"
+mime ^video,       has mplayer,  X, flag f = mplayer -mixer software -- "$@"
+mime ^video|audio, has vlc,      X, flag f = vlc -- "$@"
+mime ^video|audio, has totem,    X, flag f = totem -- "$@"
+mime ^video|audio, has totem,    X, flag f = totem --fullscreen -- "$@"
+
+#--------------------------------------------
+# Video without X:
+#-------------------------------------------
+mime ^video, terminal, has mplayer2   = mplayer2 -- "$@"
+mime ^video, terminal, has mplayer    = mplayer -- "$@"
+
+ext midi?, terminal, has wildmidi           = wildmidi -- "$@"
+
+#-------------------------------------------
+# Image Viewing:
+#-------------------------------------------
+mime ^image, has eog,    X, flag f = eog -- "$@"
+mime ^image, has sxiv,   X, flag f = sxiv -- "$@"
+mime ^image, has feh,    X, flag f = feh -- "$@"
+mime ^image, has mirage, X, flag f = mirage -- "$@"
+mime ^image, has gimp,   X, flag f = gimp -- "$@"
+
+#-------------------------------------------
+# Documents
+#-------------------------------------------
+ext pdf, has llpp,     X, flag f = llpp "$@"
+ext pdf, has zathura,  X, flag f = zathura -- "$@"
+ext pdf, has mupdf,    X, flag f = mupdf -- "$@"
+ext pdf, has apvlv,    X, flag f = apvlv -- "$@"
+ext pdf, has xpdf,     X, flag f = xpdf -- "$@"
+ext pdf, has evince,   X, flag f = evince -- "$@"
+ext pdf, has okular,   X, flag f = okular -- "$@"
+ext pdf, has epdfview, X, flag f = epdfview -- "$@"
+
+ext od[tspfg]|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has gnumeric,    X, flag f = gnumeric -- "$@"
+ext od[tspfg]|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has kspread,     X, flag f = kspread -- "$@"
+ext od[tspfg]|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has libreoffice, X, flag f = libreoffice -- "$@"
+ext od[tspfg]|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has soffice,     X, flag f = soffice -- "$@"
+ext od[tspfg]|sxc|xlsx?|xlt|xlw|gnm|gnumeric, has ooffice,     X, flag f = ooffice -- "$@"
+
+ext djvu, has evince, X, flag f = evince -- "$@"
+
+#-------------------------------------------
+# Archives
+#-------------------------------------------
+ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz|iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has aunpack, flag p = als -- "$@"
+ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz|iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has aunpack = aunpack -- "$@"
+ext tar|gz, has tar = tar vvtf "$@"
+ext tar|gz, has tar = tar vvxf "$@"
+
+#-------------------------------------------
+# Websites
+#-------------------------------------------
+ext x?html?, has surf,           X, flag f = surf -- "$@"
+ext x?html?, has vimprobable,    X, flag f = vimprobable -- "$@"
+ext x?html?, has vimprobable2,   X, flag f = vimprobable2 -- "$@"
+ext x?html?, has jumanji,        X, flag f = jumanji -- "$@"
+ext x?html?, has luakit,         X, flag f = luakit -- "$@"
+ext x?html?, has uzbl,           X, flag f = uzbl -- "$@"
+ext x?html?, has firefox,        X, flag f = firefox -- "$@"
+ext x?html?, has seamonkey,      X, flag f = seamonkey -- "$@"
+ext x?html?, has iceweasel,      X, flag f = iceweasel -- "$@"
+ext x?html?, has opera,          X, flag f = opera -- "$@"
+ext x?html?, has midori,         X, flag f = midori -- "$@"
+ext x?html?, has epiphany,       X, flag f = epiphany -- "$@"
+ext x?html?, has konqueror,      X, flag f = konqueror -- "$@"
+ext x?html?, has elinks,          terminal = elinks "$@"
+ext x?html?, has links2,          terminal = links2 -- "$@"
+ext x?html?, has links,           terminal = links -- "$@"
+ext x?html?, has lynx,            terminal = lynx -- "$@"
+ext x?html?, has w3m,             terminal = w3m -- "$@"
+
+#-------------------------------------------
+# Misc
+#-------------------------------------------
+label wallpaper, mime ^image, X = feh --bg-scale "$1"
+label wallpaper, mime ^image, X = feh --bg-tile "$1"
+label wallpaper, mime ^image, X = feh --bg-center "$1"
+label wallpaper, mime ^image, X = feh --bg-fill "$1"
+
+label editor = "$EDITOR" -- "$@"
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
new file mode 100644
index 00000000..563968ef
--- /dev/null
+++ b/ranger/ext/rifle.py
@@ -0,0 +1,145 @@
+# Copyright (C) 2012  Roman Zimbelmann <romanz@lavabit.com>
+# This software is distributed under the terms of the GNU GPL version 3.
+
+import os.path
+import re, sys
+from subprocess import Popen, PIPE
+from ranger.ext.shell_escape import shell_quote
+from ranger.ext.spawn import spawn
+from ranger.ext.get_executables import get_executables
+import time
+
+def _is_terminal():
+	try:
+		os.ttyname(0)
+		os.ttyname(1)
+		os.ttyname(2)
+	except:
+		return False
+	return True
+
+class Rifle(object):
+	delimiter1 = '='
+	delimiter2 = ','
+
+	def __init__(self, config_file):
+		self.config_file = config_file
+		self._app_flags = False
+
+	def reload_config(self, config_file=None):
+		if config_file is None:
+			config_file = self.config_file
+		f = open(config_file, 'r')
+		self.rules = []
+		for line in f:
+			if line.startswith('#') or line == '\n':
+				continue
+			line = line.strip()
+			if self.delimiter1 not in line:
+				print("Syntax error foo")
+			tests, command = line.split(self.delimiter1, 1)
+			tests = tests.split(self.delimiter2)
+			tests = tuple(tuple(f.strip().split(None, 1)) for f in tests)
+			tests = tuple(tests)
+			command = command.strip()
+			self.rules.append((command, tests))
+
+	def _eval_rule(self, rule, files, label):
+		function = rule[0]
+		argument = rule[1] if len(rule) > 1 else ''
+
+		self._app_flags = ''
+
+		if function == 'ext':
+			extension = os.path.basename(files[0]).rsplit('.', 1)[-1]
+			return bool(re.search('^' + argument + '$', extension))
+		if function == 'name':
+			return bool(re.search(argument, os.path.basename(files[0])))
+		if function == 'path':
+			return bool(re.search(argument, os.path.abspath(files[0])))
+		if function == 'mime':
+			return bool(re.search(argument, self._get_mimetype(files[0])))
+		if function == 'has':
+			return argument in get_executables()
+		if function == 'terminal':
+			return _is_terminal()
+		if function == 'label':
+			if label:
+				self._found_label = argument == label
+			else:
+				# don't care about label in this case
+				self._found_label = True
+			return self._found_label
+		if function == 'flag':
+			self._app_flags = argument
+			return True
+		if function == 'X':
+			return 'DISPLAY' in os.environ
+		if function == 'else':
+			return True
+
+	def _get_mimetype(self, fname):
+		if self._mimetype:
+			return self._mimetype
+		mimetype = spawn("file", "--mime-type", "-Lb", fname)
+		self._mimetype = mimetype
+		return mimetype
+
+	def _build_command(self, files, action):
+		flags = self._app_flags
+		_filenames = "' '".join(f.replace("'", "'\\\''") for f in files)
+		command = "set -- '%s'" % _filenames + '\n'
+		if 'p' in flags and not 'f' in flags and is_terminal():
+			action += '| less'
+		if 'f' in flags:
+			action = "nohup %s >&/dev/null &" % action
+		command += action
+		return command
+
+	def list_commands(self, files, mimetype=None):
+		self._mimetype = mimetype
+		command = None
+		count = 0
+		result = []
+		t = time.time()
+		for cmd, tests in self.rules:
+			for test in tests:
+				if not self._eval_rule(test, files, None):
+					break
+			else:
+				result.append((count, cmd, self._app_flags))
+				count += 1
+		#sys.stderr.write("%f\n" % (time.time() - t))
+		return result
+
+	def execute(self, files, number=0, label=None, mimetype=None):
+		self._mimetype = mimetype
+		self._found_label = True
+		command = None
+		count = 0
+		for cmd, tests in self.rules:
+			if label:
+				self._found_label = False
+			for test in tests:
+				if not self._eval_rule(test, files, label):
+					#print("fails on test %s" % str(test))
+					break
+			else:
+				if not self._found_label:
+					pass
+				elif count != number:
+					count += 1
+				else:
+					command = self._build_command(files, cmd)
+					break
+		#print(command)
+		if command is not None:
+			p = Popen(command, shell=True)
+			p.wait()
+
+if __name__ == '__main__':
+	import sys
+	rifle = Rifle(os.environ['HOME'] + '/.config/ranger/rifle.conf')
+	rifle.reload_config()
+	#print(rifle.list_commands(sys.argv[1:]))
+	rifle.execute(sys.argv[1:], number=0)
diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py
index b5e7210c..89d099ea 100644
--- a/ranger/gui/widgets/browserview.py
+++ b/ranger/gui/widgets/browserview.py
@@ -19,6 +19,7 @@ class BrowserView(Widget, DisplayableContainer):
 	need_clear = False
 	old_collapse = False
 	draw_hints = False
+	draw_info = False
 
 	def __init__(self, win, ratios, preview = True):
 		DisplayableContainer.__init__(self, win)
@@ -98,6 +99,8 @@ class BrowserView(Widget, DisplayableContainer):
 			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:
@@ -185,6 +188,19 @@ class BrowserView(Widget, DisplayableContainer):
 
 		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 = []
diff --git a/ranger/gui/widgets/console.py b/ranger/gui/widgets/console.py
index c3623c51..06552ef6 100644
--- a/ranger/gui/widgets/console.py
+++ b/ranger/gui/widgets/console.py
@@ -119,6 +119,7 @@ class Console(Widget):
 			except:
 				pass
 			self.last_cursor_mode = None
+		self.fm.hide_console_info()
 		self.add_to_history()
 		self.tab_deque = None
 		self.clear()