about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/actions.py50
-rw-r--r--ranger/container/keymap.py27
-rw-r--r--ranger/defaults/keys.py128
-rw-r--r--ranger/ext/direction.py120
-rw-r--r--ranger/gui/ui.py2
-rw-r--r--ranger/gui/widgets/browserview.py8
6 files changed, 307 insertions, 28 deletions
diff --git a/ranger/actions.py b/ranger/actions.py
index c4b75aca..754fd857 100644
--- a/ranger/actions.py
+++ b/ranger/actions.py
@@ -148,8 +148,10 @@ class Actions(EnvironmentAware, SettingsAware):
 		"""Delete the bookmark with the name <key>"""
 		self.bookmarks.delete(key)
 
-	def move_left(self, narg=1):
+	def move_left(self, narg=None):
 		"""Enter the parent directory"""
+		if narg is None:
+			narg = 1
 		try:
 			directory = os.path.join(*(['..'] * narg))
 		except:
@@ -274,6 +276,44 @@ class Actions(EnvironmentAware, SettingsAware):
 		self.env.pwd.move(relative=relative,
 				absolute=absolute, narg=narg)
 
+	def move(self, dir, narg=None):
+		if narg is not None:
+			dir = dir * narg
+
+		self.notify(str(dir))
+
+		if dir.right is not None:
+			if dir.right >= 0:
+				if dir.has_explicit_direction:
+					self.move_right(narg=dir.right)
+				else:
+					self.move_right(narg=dir.original_right - 1)
+			elif dir.right < 0:
+				self.move_left(narg=dir.left)
+		else:
+			if dir.percent:
+				if dir.absolute:
+					self.move_pointer_by_percentage( \
+							absolute=dir.down, narg=narg)
+				else:
+					self.move_pointer_by_percentage( \
+							relative=dir.down, narg=narg)
+			elif dir.pages:
+				self.move_pointer_by_pages(dir.down)
+			elif dir.absolute:
+				if dir.has_explicit_direction:
+					self.move_pointer(absolute=dir.down)
+				else:
+					self.move_pointer(absolute=dir.original_down)
+			else:
+				self.move_pointer(relative=dir.down)
+
+	def draw_bookmarks(self):
+		self.ui.browser.draw_bookmarks = True
+
+	def hide_bookmarks(self):
+		self.ui.browser.draw_bookmarks = False
+
 	def move_pointer_by_pages(self, relative):
 		"""Move the pointer down by <relative> pages"""
 		self.env.pwd.move(relative=int(relative * self.env.termsize[0]))
@@ -288,9 +328,12 @@ class Actions(EnvironmentAware, SettingsAware):
 		if narg is not None:
 			absolute = narg
 
+		if absolute is not None:
+			absolute = int(absolute * factor)
+
 		self.env.pwd.move(
 				relative=int(relative * factor),
-				absolute=int(absolute * factor))
+				absolute=absolute)
 
 	def scroll(self, relative):
 		"""Scroll down by <relative> lines"""
@@ -369,6 +412,9 @@ class Actions(EnvironmentAware, SettingsAware):
 		if hasattr(self.ui, 'notify'):
 			self.ui.notify(text, duration=duration, bad=bad)
 
+	def hint(self, text):
+		self.notify(text)
+
 	def mark(self, all=False, toggle=False, val=None, movedown=None, narg=1):
 		"""
 		A wrapper for the directory.mark_xyz functions.
diff --git a/ranger/container/keymap.py b/ranger/container/keymap.py
index f9be5e95..50b70b33 100644
--- a/ranger/container/keymap.py
+++ b/ranger/container/keymap.py
@@ -16,6 +16,7 @@ import curses
 from string import ascii_lowercase
 from inspect import isfunction, getargspec
 from ranger.ext.tree import Tree
+from ranger.ext.direction import Direction
 
 MAX_ALIAS_RECURSION = 20
 PASSIVE_ACTION = 9003
@@ -26,25 +27,6 @@ DIRECTION = 'direction'
 DIRARG = 'dir'
 ALIASARG = 'alias'
 
-class Direction(object):
-	"""An object with a down and right method"""
-	def __init__(self, down=0, right=0):
-		self.down = down
-		self.right = right
-
-	def copy(self):
-		new = type(self)()
-		new.__dict__.update(self.__dict__)
-		return new
-
-	def __mul__(self, other):
-		copy = self.copy()
-		if other is not None:
-			copy.down *= other
-			copy.right *= other
-		return copy
-	__rmul__ = __mul__
-
 def to_string(i):
 	"""convert a ord'd integer to a string"""
 	try:
@@ -142,6 +124,7 @@ class KeyBuffer(object):
 			return None
 		assert isinstance(key, int)
 		assert key >= 0
+		self.all_keys.append(key)
 
 		# evaluate quantifiers
 		if self.eval_quantifier and self._do_eval_quantifier(key):
@@ -192,7 +175,11 @@ class KeyBuffer(object):
 					self.failure = True
 					return None
 			else:
-				direction = match.actions['dir'] * self.direction_quant
+				if self.direction_quant is not None:
+					direction = match.actions['dir'] * self.direction_quant
+					direction.has_explicit_direction = True
+				else:
+					direction = match.actions['dir'].copy()
 				self.directions.append(direction)
 				self.direction_quant = None
 				self.eval_command = True
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
index f2d93c58..7a17c831 100644
--- a/ranger/defaults/keys.py
+++ b/ranger/defaults/keys.py
@@ -337,21 +337,34 @@ def _basic_movement(command_list):
 
 
 def base_directions():
+	# Direction Keys
 	map = KeyMap()
 	map('<down>', dir=Direction(down=1))
 	map('<up>', dir=Direction(down=-1))
 	map('<left>', dir=Direction(right=-1))
 	map('<right>', dir=Direction(right=1))
+	map('<home>', dir=Direction(down=0, absolute=True))
+	map('<end>', dir=Direction(down=-1, absolute=True))
+	map('<pagedown>', dir=Direction(down=1, pages=True))
+	map('<pageup>', dir=Direction(down=-1, pages=True))
+	map('%<any>', dir=Direction(down=1, percent=True, absolute=True))
+	map('<space>', dir=Direction(down=1, pages=True))
+	map('<CR>', dir=Direction(down=1))
 
 	return map
 
 def vim():
+	# Direction Keys
 	map = KeyMap()
 	map.merge(base_directions())
 	map('j', alias='<down>')
 	map('k', alias='<up>')
 	map('h', alias='<left>')
 	map('l', alias='<right>')
+	map('gg', alias='<home>')
+	map('G', alias='<end>')
+	map('J', dir=Direction(down=20))
+	map('K', dir=Direction(down=-20))
 
 	return map
 
@@ -370,9 +383,11 @@ def browser_keys():
 
 	@map('<dir>')
 	def move(arg):
-		arg.fm.move_pointer(relative=arg.direction.down)
+		arg.fm.move(dir=arg.direction, narg=arg.n)
 	map(fm.exit(), 'Q')
 
+	map('<cr>', fm.move(dir=Direction(right=1)))
+
 	# --------------------------------------------------------- history
 	map('H', fm.history_go(-1))
 	map('L', fm.history_go(1))
@@ -391,7 +406,7 @@ def browser_keys():
 	map('pp', fm.paste())
 	map('po', fm.paste(overwrite=True))
 	map('pl', fm.paste_symlink())
-	map('p<bg>', fm.notify('press //p// once again to confirm pasting' \
+	map('p<bg>', fm.hint('press //p// once again to confirm pasting' \
 			', or //l// to create symlinks'))
 
 	# ---------------------------------------------------- run programs
@@ -400,7 +415,116 @@ def browser_keys():
 	map('.term', fm.execute_command('x-terminal-emulator', flags='d'))
 	map('du', fm.execute_command('du --max-depth=1 -h | less'))
 
+	# -------------------------------------------------- toggle options
+	map('b<bg>', fm.hint("bind_//h//idden //p//review_files" \
+		"//d//irectories_first //c//ollapse_preview flush//i//nput"))
+	map('bh', fm.toggle_boolean_option('show_hidden'))
+	map('bp', fm.toggle_boolean_option('preview_files'))
+	map('bi', fm.toggle_boolean_option('flushinput'))
+	map('bd', fm.toggle_boolean_option('directories_first'))
+	map('bc', fm.toggle_boolean_option('collapse_preview'))
+
+	# ------------------------------------------------------------ sort
+	map('o<bg>', 'O<bg>', fm.hint("//s//ize //b//ase//n//ame //m//time" \
+		" //t//ype //r//everse"))
+	sort_dict = {
+		's': 'size',
+		'b': 'basename',
+		'n': 'basename',
+		'm': 'mtime',
+		't': 'type',
+	}
+
+	for key, val in sort_dict.items():
+		for key, is_capital in ((key, False), (key.upper(), True)):
+			# reverse if any of the two letters is capital
+			map('o' + key, fm.sort(func=val, reverse=is_capital))
+			map('O' + key, fm.sort(func=val, reverse=True))
+
+	map('or', 'Or', 'oR', 'OR', lambda arg: \
+			arg.fm.sort(reverse=not arg.fm.settings.reverse))
+
+	# ----------------------------------------------- console shortcuts
+	@map("A")
+	def append_to_filename(arg):
+		command = 'rename ' + arg.fm.env.cf.basename
+		arg.fm.open_console(cmode.COMMAND, command)
+
+	map('cw', fm.open_console(cmode.COMMAND, 'rename '))
+	map('cd', fm.open_console(cmode.COMMAND, 'cd '))
+	map('f', fm.open_console(cmode.COMMAND_QUICK, 'find '))
+	map('bf', fm.open_console(cmode.COMMAND, 'filter '))
+	map('d<bg>', fm.hint('d//u// (disk usage) d//d// (cut)'))
+
+
+	# --------------------------------------------- jump to directories
+	map('gh', fm.cd('~'))
+	map('ge', fm.cd('/etc'))
+	map('gu', fm.cd('/usr'))
+	map('gd', fm.cd('/dev'))
+	map('gl', fm.cd('/lib'))
+	map('go', fm.cd('/opt'))
+	map('gv', fm.cd('/var'))
+	map('gr', 'g/', fm.cd('/'))
+	map('gm', fm.cd('/media'))
+	map('gn', fm.cd('/mnt'))
+	map('gt', fm.cd('/tmp'))
+	map('gs', fm.cd('/srv'))
+	map('gR', fm.cd(RANGERDIR))
+
+	# ------------------------------------------------------- searching
+	map('/', fm.open_console(cmode.SEARCH))
+
+	map('n', fm.search())
+	map('N', fm.search(forward=False))
+
+	map(TAB, fm.search(order='tag'))
+	map('cc', fm.search(order='ctime'))
+	map('cm', fm.search(order='mimetype'))
+	map('cs', fm.search(order='size'))
+	map('c<bg>', fm.hint('//c//time //m//imetype //s//ize'))
+
+	# ------------------------------------------------------- bookmarks
+	for key in ALLOWED_BOOKMARK_KEYS:
+		map("`" + key, "'" + key, fm.enter_bookmark(key))
+		map("m" + key, fm.set_bookmark(key))
+		map("um" + key, fm.unset_bookmark(key))
+	map("`<bg>", "'<bg>", "m<bg>", fm.draw_bookmarks())
+
+
+	map(':', ';', fm.open_console(cmode.COMMAND))
+
+	# ---------------------------------------------------- change views
+	map('i', fm.display_file())
+	map(ctrl('p'), fm.display_log())
+	map('?', KEY_F1, fm.display_help())
+	map('w', lambda arg: arg.fm.ui.open_taskview())
+
+	# ---------------------------------------------------------- custom
+	# This is useful to track watched episode of a series.
+	@map(']')
+	def tag_next_and_run(arg):
+		fm = arg.fm
+		fm.tag_remove()
+		fm.tag_remove(movedown=False)
+		fm.tag_toggle()
+		fm.move_pointer(relative=-2)
+		fm.move_right()
+		fm.move_pointer(relative=1)
+
+	# "enter" = shortcut for "1l"
+	map('<cr>', fm.move(Direction(right=2)))
+
+	# ------------------------------------------------ system functions
+	map('ZZ', fm.exit())
+	map(ctrl('R'), fm.reset())
+	map('R', fm.reload_cwd())
+	map(ctrl('C'), fm.exit())
+
 	map(':', ';', fm.open_console(cmode.COMMAND))
+	map('>', fm.open_console(cmode.COMMAND_QUICK))
+	map('!', fm.open_console(cmode.OPEN))
+	map('r', fm.open_console(cmode.OPEN_QUICK))
 
 	return map
 
diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py
new file mode 100644
index 00000000..28003941
--- /dev/null
+++ b/ranger/ext/direction.py
@@ -0,0 +1,120 @@
+# Copyright (c) 2009, 2010 hut <hut@lavabit.com>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+class NoDefault(object):
+	pass
+
+class Direction(object):
+	"""An object with a down and right method"""
+	def __init__(self, right=None, down=None, absolute=False,
+			percent=False, pages=False, **keywords):
+		self.has_explicit_direction = False
+
+		if 'up' in keywords:
+			self.down = -keywords['up']
+		else:
+			self.down = down
+
+		if 'left' in keywords:
+			self.right = -keywords['left']
+		else:
+			self.right = right
+
+		if 'relative' in keywords:
+			self.absolute = not relative
+		else:
+			self.absolute = absolute
+
+		if 'default' in keywords:
+			self.default = keywords['default']
+		else:
+			self.default = NoDefault
+
+		self.original_down = self.down
+		self.original_right = self.right
+
+		self.percent = percent
+		self.pages = pages
+	
+	@property
+	def up(self):
+		if self.down is None:
+			return None
+		return -self.down
+
+	@property
+	def left(self):
+		if self.right is None:
+			return None
+		return -self.right
+
+	@property
+	def relative(self):
+		return not self.absolute
+
+	def down_or_default(self, default):
+		if self.has_been_modified:
+			return self.down
+		return default
+
+	def steps_down(self, page_length=10):
+		if self.pages:
+			return self.down * page_length
+		else:
+			return self.down
+
+	def steps_right(self, page_length=10):
+		if self.pages:
+			return self.right * page_length
+		else:
+			return self.right
+
+	def copy(self):
+		new = type(self)()
+		new.__dict__.update(self.__dict__)
+		return new
+
+	def __mul__(self, other):
+		copy = self.copy()
+		if self.absolute:
+			if self.down is not None:
+				copy.down = other
+			if self.right is not None:
+				copy.right = other
+		else:
+			if self.down is not None:
+				copy.down *= other
+			if self.right is not None:
+				copy.right *= other
+		copy.original_down = self.original_down
+		copy.original_right = self.original_right
+		return copy
+	__rmul__ = __mul__
+
+	def __str__(self):
+		s = ['<Direction']
+		if self.down is not None:
+			s.append(" down=" + str(self.down))
+		if self.right is not None:
+			s.append(" right=" + str(self.right))
+		if self.absolute:
+			s.append(" absolute")
+		else:
+			s.append(" relative")
+		if self.pages:
+			s.append(" pages")
+		if self.percent:
+			s.append(" percent")
+		s.append('>')
+		return ''.join(s)
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 2b406113..05efa639 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -136,6 +136,8 @@ class UI(DisplayableContainer):
 		kbuf = self.env.keybuffer
 		cmd = kbuf.command
 
+		self.fm.hide_bookmarks()
+
 		if kbuf.failure:
 			kbuf.clear()
 			return
diff --git a/ranger/gui/widgets/browserview.py b/ranger/gui/widgets/browserview.py
index 54f21fa9..080f1be0 100644
--- a/ranger/gui/widgets/browserview.py
+++ b/ranger/gui/widgets/browserview.py
@@ -23,6 +23,7 @@ class BrowserView(Widget, DisplayableContainer):
 	ratios = None
 	preview = True
 	preview_available = True
+	draw_bookmarks = False
 	stretch_ratios = None
 	need_clear = False
 
@@ -60,10 +61,9 @@ class BrowserView(Widget, DisplayableContainer):
 		self.add_child(self.pager)
 
 	def draw(self):
-		try:
-			if self.env.cmd.show_obj.draw_bookmarks:
-				self._draw_bookmarks()
-		except AttributeError:
+		if self.draw_bookmarks:
+			self._draw_bookmarks()
+		else:
 			if self.need_clear:
 				self.win.erase()
 				self.need_redraw = True