about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xranger.py55
-rw-r--r--ranger/api.py21
-rw-r--r--ranger/command.py24
-rw-r--r--ranger/conf/keys.py9
-rw-r--r--ranger/environment.py4
-rw-r--r--ranger/fm.py9
-rw-r--r--ranger/fsobject.py19
-rw-r--r--ranger/gui/ui.py11
-rw-r--r--ranger/main.py84
9 files changed, 148 insertions, 88 deletions
diff --git a/ranger.py b/ranger.py
index 8a2d5967..9a2e59c1 100755
--- a/ranger.py
+++ b/ranger.py
@@ -1,5 +1,7 @@
 #!/usr/bin/python
 # coding=utf-8
+# ranger: Browse your files inside the console.
+
 
 # An embedded shell script. Assuming this file is /usr/bin/ranger,
 # this hack allows you to use the cd-after-exit feature by typing:
@@ -16,53 +18,14 @@ fi
 return 1
 """
 
-from ranger.fm import FM
-from ranger.environment import Environment
-from ranger.command import CommandList
-from ranger.conf import keys, options
-from ranger.gui.defaultui import DefaultUI as UI
-from ranger.conf.colorschemes.snow import MyColorScheme
-
-import sys, os, locale
-
-try:
-	assert sys.argv[1] == '--cd-after-exit'
-	cd_after_exit = True
-	sys.stderr = sys.stdout
-	del sys.argv[1]
-except:
-	cd_after_exit = False
-
-# TODO: Parse arguments
-
-# TODO: load config
-
-os.stat_float_times(True)
-locale.setlocale(locale.LC_ALL, 'en_US.utf8')
-
 try:
-	path = os.path.abspath('.')
-	opt = options.dummy()
-
-	env = Environment(opt)
-	commandlist = CommandList()
-	colorscheme = MyColorScheme()
-	keys.initialize_commands(commandlist)
-
-	my_ui = UI(env, commandlist, colorscheme)
-	my_fm = FM(env)
-	my_fm.feed(path, my_ui)
-	my_fm.run()
+	from ranger.main import main
 
-finally:
-	try:
-		my_ui.exit()
-	except:
-		pass
+except ImportError as errormessage:
+	print(errormessage)
+	print("To run an uninstalled copy of ranger,")
+	print("launch ranger.py in the top directory.")
 
-	if cd_after_exit:
-		try:
-			sys.__stderr__.write(env.pwd.path)
-		except:
-			pass
+else:
+	main()
 
diff --git a/ranger/api.py b/ranger/api.py
index b820e085..5ca40175 100644
--- a/ranger/api.py
+++ b/ranger/api.py
@@ -8,3 +8,24 @@ def log(txt):
 	f.write("\n")
 	f.close()
 
+ONE_KB = 1024
+UNITS = tuple('BKMGTP')
+NINE_THOUSAND = len(UNITS) - 1
+
+def human_readable(byte):
+	import math
+
+	if not byte:
+		return '0 B'
+
+	its = int(math.log(byte, 2) / 10)
+	flt = float(byte) / (1 << (10 * its))
+	
+	if its > NINE_THOUSAND:
+		return '>9000' # off scale
+
+	if int(flt) == flt:
+		return '%.0f %s' % (flt, UNITS[its])
+
+	else:
+		return '%.2f %s' % (flt, UNITS[its])
diff --git a/ranger/command.py b/ranger/command.py
index 5e6c74d1..203a0329 100644
--- a/ranger/command.py
+++ b/ranger/command.py
@@ -9,22 +9,27 @@ class CommandList():
 	# and when to wait for the rest of the key combination. For "gg" we
 	# will assign "g" to a dummy which tells the program not to do the latter.
 	def rebuild_paths(self):
-		paths = self.paths
-
+		""" fill the path dictionary with dummie objects """
 		if self.dummies_in_paths:
 			self.remove_dummies()
 		
 		for cmd in self.commandlist:
 			for key in cmd.keys:
-				path = []
 				for path in self.keypath(key):
-					try: paths[path]
-					except KeyError:
-						paths[path] = self.dummy_object
+					if path not in self.paths:
+						self.paths[path] = self.dummy_object
 
 		self.dummies_in_paths = True
 
 	def keypath(self, tup):
+		""" split a tuple like (a,b,c,d) into [(a,), (a,b), (a,b,c)] """
+		length = len(tup)
+
+		if length == 0:
+			return ()
+		if length == 1:
+			return (tup, )
+
 		current = []
 		all = []
 		
@@ -35,6 +40,7 @@ class CommandList():
 		return all
 
 	def remove_dummies(self):
+		""" remove dummie objects in case you have to rebuild a path dictionary which already contains dummie objects. """
 		for k in tuple(paths.keys()):
 			if paths[k] == self.dummy_object: del paths[k]
 		self.dummies_in_paths = False
@@ -49,13 +55,17 @@ class CommandList():
 		elif isinstance(obj, int):
 			return (obj, )
 		else:
-			raise TypeError('need a str or a tuple for str_to_tuple')
+			raise TypeError('need a str, int or tuple for str_to_tuple')
 	
 	def bind(self, fnc, *keys):
+		""" create a Command object and assign it to the given key combinations. """
 		if len(keys) == 0: return
+
 		keys = tuple(map(self.str_to_tuple, keys))
+
 		cmd = Command(fnc, keys)
 		cmd.commandlist = self
+
 		self.commandlist.append(cmd)
 		for key in keys:
 			self.paths[key] = cmd
diff --git a/ranger/conf/keys.py b/ranger/conf/keys.py
index 3d5fa856..5ad4e510 100644
--- a/ranger/conf/keys.py
+++ b/ranger/conf/keys.py
@@ -10,9 +10,8 @@ def initialize_commands(cl):
 	# * an integer which represents an ascii code
 	# * a tuple of integers
 
-	def move(relative = 0, absolute = None):
-		return lambda fm: fm.move_pointer(
-				relative = relative, absolute = absolute)
+	def move(**keywords):
+		return lambda fm: fm.move_pointer(**keywords)
 
 	def move_pages(n):
 		return lambda fm: fm.move_pointer_by_pages(n)
@@ -34,8 +33,8 @@ def initialize_commands(cl):
 	cl.bind(FM.edit_file,           'E')
 
 	# toggle options
-	cl.bind(toggle_option('show_hidden'), 'th')
-	cl.bind(toggle_option('preview_files'), 'tp')
+	cl.bind(toggle_option('show_hidden'),       'th')
+	cl.bind(toggle_option('preview_files'),     'tp')
 	cl.bind(toggle_option('directories_first'), 'td')
 
 	# key combinations which change the current directory
diff --git a/ranger/environment.py b/ranger/environment.py
index e3d9bb9f..a15abba2 100644
--- a/ranger/environment.py
+++ b/ranger/environment.py
@@ -4,9 +4,9 @@ from ranger.directory import Directory, NoDirectoryGiven
 class Environment():
 	# A collection of data which is relevant for more than
 	# one class.
-	def __init__(self, opt):
+	def __init__(self, path, opt):
+		self.path = abspath(expanduser(path))
 		self.opt = opt
-		self.path = None
 		self.pathway = ()
 		self.directories = {}
 		self.pwd = None # current directory
diff --git a/ranger/fm.py b/ranger/fm.py
index 02cf1300..e88ff964 100644
--- a/ranger/fm.py
+++ b/ranger/fm.py
@@ -2,16 +2,15 @@ from os import devnull
 null = open(devnull, 'a')
 
 class FM():
-	def __init__(self, environment):
+	def __init__(self, environment, ui):
 		self.env = environment
-
-	def feed(self, path, ui):
 		self.ui = ui
-		self.env.path = path
-		self.env.enter_dir(path)
 
 	def run(self):
 		import time
+
+		self.env.enter_dir(self.env.path)
+
 		while 1:
 			try:
 				self.ui.draw()
diff --git a/ranger/fsobject.py b/ranger/fsobject.py
index 485f5165..48a6eee2 100644
--- a/ranger/fsobject.py
+++ b/ranger/fsobject.py
@@ -42,6 +42,7 @@ class FileSystemObject(object):
 		self.loaded = True
 
 		import os
+		from ranger.api import human_readable
 		if os.access(self.path, os.F_OK):
 			self.stat = os.stat(self.path)
 			self.islink = os.path.islink(self.path)
@@ -92,21 +93,3 @@ class FileSystemObject(object):
 			self.load()
 			return True
 		return False
-
-ONE_KB = 1024
-UNITS = tuple('BKMGTY')
-MAX_I = len(UNITS) - 1
-
-def human_readable(byte):
-	i = 0
-	flt = float(byte)
-
-	while flt > ONE_KB and i < MAX_I:
-		flt /= ONE_KB
-		i += 1
-	
-	if int(flt) == flt:
-		return '%.0f %s' % (flt, UNITS[i])
-
-	else:
-		return '%.2f %s' % (flt, UNITS[i])
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 77f88080..34f7c54c 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -5,14 +5,10 @@ class UI():
 		self.env = env
 		self.commandlist = commandlist
 		self.colorscheme = colorscheme
+		self.is_set_up = False
 
 		self.widgets = []
 
-		self.initialize()
-
-		self.setup()
-		self.resize()
-
 	def initialize(self):
 		self.win = curses.initscr()
 		self.win.leaveok(1)
@@ -28,6 +24,11 @@ class UI():
 		avail, old = curses.mousemask(mask)
 		curses.mousemask(avail)
 
+		if not self.is_set_up:
+			self.is_set_up = True
+			self.setup()
+			self.resize()
+
 	def handle_mouse(self, fm):
 		try:
 			event = MouseEvent(curses.getmouse())
diff --git a/ranger/main.py b/ranger/main.py
new file mode 100644
index 00000000..14953682
--- /dev/null
+++ b/ranger/main.py
@@ -0,0 +1,84 @@
+import sys
+import os
+import locale
+from optparse import OptionParser, SUPPRESS_HELP
+
+from ranger.fm import FM
+from ranger.environment import Environment
+from ranger.command import CommandList
+from ranger.conf import keys, options
+from ranger.gui.defaultui import DefaultUI as UI
+from ranger.conf.colorschemes.snow import MyColorScheme
+
+VERSION = '1.0.0'
+
+USAGE = '''%s [options] [path/filename]'''
+
+def main():
+	try:
+		import curses
+	except ImportError as errormessage:
+		print(errormessage)
+		print('ranger requires the python curses module. Aborting.')
+		sys.exit(1)
+
+	locale.setlocale(locale.LC_ALL, 'en_US.utf8')
+	os.stat_float_times(True)
+
+	# Parse options
+	parser = OptionParser(
+			usage = USAGE,
+			version = 'ranger ' + VERSION )
+
+	# Instead of using this directly, use the embedded
+	# shell script by running ranger with:
+	# source /path/to/ranger /path/to/ranger
+	parser.add_option( '--cd-after-exit',
+			action = 'store_true',
+			dest = 'cd_after_exit',
+			help = SUPPRESS_HELP )
+
+	args, rest = parser.parse_args()
+
+	if args.cd_after_exit:
+		sys.stderr = sys.__stdout__
+		if rest[0] == sys.argv[0]:
+			del rest[0]
+	
+	# Initialize objects
+	target = ' '.join(rest)
+	if target:
+		if not os.access(target, os.F_OK):
+			print("File or directory doesn't exist: %s" % target)
+			sys.exit(1)
+		elif os.path.isfile(target):
+			FM.execute_file(FM(0, 0), target)
+			sys.exit(0)
+		else:
+			path = target
+
+	else:
+		path = '.'
+
+	opt = options.dummy()
+
+	env = Environment(path, opt)
+	commandlist = CommandList()
+	colorscheme = MyColorScheme()
+	keys.initialize_commands(commandlist)
+
+	my_ui = UI(env, commandlist, colorscheme)
+	my_fm = FM(env, my_ui)
+
+	try:
+		# Run the file manager
+		my_ui.initialize()
+		my_fm.run()
+
+	finally:
+		# Finish, clean up
+		my_ui.exit()
+
+		if args.cd_after_exit:
+			try: sys.__stderr__.write(env.pwd.path)
+			except: pass