about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2009-12-23 18:27:51 +0100
committerhut <hut@lavabit.com>2009-12-23 18:27:51 +0100
commit3201b163ffcabd31ab5b82e3192ab43ed5ec006e (patch)
tree8f640694f3c710dd746e10c78ddbf7f498308838
parentc2a890822a9d38b86695c79b4bc7c25bd0a80c7e (diff)
downloadranger-3201b163ffcabd31ab5b82e3192ab43ed5ec006e.tar.gz
implemented marking, for operations on multiple files
-rw-r--r--ranger/actions.py57
-rw-r--r--ranger/colorschemes/default.py11
-rw-r--r--ranger/container/environment.py12
-rw-r--r--ranger/defaults/keys.py3
-rw-r--r--ranger/fsobject/directory.py77
-rw-r--r--ranger/fsobject/fsobject.py9
-rw-r--r--ranger/gui/colorscheme.py1
-rw-r--r--ranger/gui/widgets/filelist.py3
-rw-r--r--ranger/gui/widgets/statusbar.py36
9 files changed, 172 insertions, 37 deletions
diff --git a/ranger/actions.py b/ranger/actions.py
index 19e0028d..9afb0ade 100644
--- a/ranger/actions.py
+++ b/ranger/actions.py
@@ -63,12 +63,15 @@ class Actions(EnvironmentAware, SettingsAware):
 		"""Enter the parent directory"""
 		self.env.enter_dir('..')
 	
-	def move_right(self, mode = 0):
+	def move_right(self, mode=0):
 		"""Enter the current directory or execute the current file"""
 		cf = self.env.cf
+		marked_items = self.env.pwd.marked_items
+		sel = self.env.get_selection()
+
 		if not self.env.enter_dir(cf):
-			if cf is not None:
-				if not self.execute_file(cf, mode = mode):
+			if sel:
+				if not self.execute_file(sel, mode=mode):
 					self.open_console('@')
 
 	def history_go(self, relative):
@@ -79,18 +82,20 @@ class Actions(EnvironmentAware, SettingsAware):
 		"""Handle mouse-buttons if one was pressed"""
 		self.ui.handle_mouse()
 
-	def execute_file(self, files, app = '', flags = '', mode = 0):
+	def execute_file(self, files, app='', flags='', mode=0):
 		"""Execute a file.
 		app is the name of a method in Applications, without the "app_"
 		flags is a string consisting of applications.ALLOWED_FLAGS
 		mode is a positive integer.
 		Both flags and mode specify how the program is run."""
 
-		if type(files) not in (list, tuple):
+		if type(files) not in (list, tuple, set):
 			files = [files]
 
+		arbitrary_file = tuple(files)[0]
+
 		return self.apps.get(app)(
-				mainfile = files[0],
+				mainfile = arbitrary_file,
 				files = files,
 				flags = flags,
 				mode = mode,
@@ -159,7 +164,7 @@ class Actions(EnvironmentAware, SettingsAware):
 	def copy(self):
 		"""Copy the selected items"""
 
-		selected = set([self.env.cf])
+		selected = self.env.get_selection()
 		self.env.copy = set(f for f in selected if f in self.env.pwd.files)
 		self.env.cut = False
 	
@@ -205,7 +210,7 @@ class Actions(EnvironmentAware, SettingsAware):
 
 	def delete(self):
 		msg = self.notify("Deleting ...", duration=0)
-		selected = set([self.env.cf])
+		selected = self.env.get_selection()
 		self.env.copy -= selected
 		if selected:
 			for f in selected:
@@ -234,6 +239,42 @@ class Actions(EnvironmentAware, SettingsAware):
 			pass
 		else:
 			return method(text, duration=duration, bad=bad)
+	
+	def mark(self, all=False, toggle=False, val=None, movedown=None):
+		"""
+		A wrapper for the directory.mark_xyz functions.
+		If all is True, change the marked-status of all files/directories.
+		If toggle is True, toggle the marked-status.
+		If val is True, mark the file(s). If False, unmark them.
+		If movedown is True, move the pointer down finally.
+		"""
+
+		if self.env.pwd is None:
+			return
+
+		pwd = self.env.pwd
+
+		if movedown is None:
+			movedown = not all
+
+		if val is None and toggle is False:
+			return
+
+		if all:
+			if toggle:
+				pwd.toggle_all_marks()
+			else:
+				pwd.mark_all(val)
+		else:
+			item = self.env.cf
+			if item is not None:
+				if toggle:
+					pwd.toggle_mark(item)
+				else:
+					pwd.mark_item(item, val)
+
+		if movedown:
+			self.move_pointer(relative=1)
 
 	# aliases:
 	cd = enter_dir
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index 4f7e5498..13a20db9 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -40,8 +40,12 @@ class Default(ColorScheme):
 			if context.link:
 				fg = context.good and cyan or magenta
 
-			if context.maindisplay and context.selected:
-				attr |= bold
+			if context.maindisplay:
+				if context.selected:
+					attr |= bold
+				if context.marked:
+					attr |= bold
+					fg = yellow
 
 		elif context.in_titlebar:
 			attr |= bold
@@ -61,6 +65,9 @@ class Default(ColorScheme):
 					fg = cyan
 				elif context.bad:
 					fg = magenta
+			if context.marked:
+				attr |= bold | reverse
+				fg = yellow
 
 		elif context.in_notify:
 			attr |= reverse
diff --git a/ranger/container/environment.py b/ranger/container/environment.py
index 1b26d48e..b08b5320 100644
--- a/ranger/container/environment.py
+++ b/ranger/container/environment.py
@@ -12,7 +12,6 @@ class Environment(SettingsAware):
 	cf = None  # current file
 	copy = None
 	cut = None
-	selection = None
 	termsize = None
 	history = None
 	directories = None
@@ -26,7 +25,6 @@ class Environment(SettingsAware):
 		self.pathway = ()
 		self.directories = {}
 		self.keybuffer = KeyBuffer()
-		self.selection = set()
 		self.copy = set()
 		self.history = History(self.settings.max_history_size)
 
@@ -69,14 +67,20 @@ class Environment(SettingsAware):
 				if value.is_older_than(1200):
 					del self.directories[key]
 	
+	def get_selection(self):
+		if self.pwd:
+			return self.pwd.get_selection()
+		return set()
+	
 	def get_directory(self, path):
 		"""Get the directory object at the given path"""
 		path = abspath(path)
 		try:
 			return self.directories[path]
 		except KeyError:
-			self.directories[path] = Directory(path)
-			return self.directories[path]
+			obj = Directory(path)
+			self.directories[path] = obj
+			return obj
 
 	def assign_correct_cursor_positions(self):
 		"""Assign correct cursor positions for subdirectories"""
diff --git a/ranger/defaults/keys.py b/ranger/defaults/keys.py
index e3be795d..2fa02285 100644
--- a/ranger/defaults/keys.py
+++ b/ranger/defaults/keys.py
@@ -34,6 +34,9 @@ def initialize_commands(command_list):
 	bind('E', do('edit_file'))
 	bind('o', do('force_load_preview'))
 
+	bind(' ', do('mark', toggle=True))
+	bind('v', do('mark', all=True, toggle=True))
+	bind('V', do('mark', all=True, val=False))
 
 	bind('yy', 'cp', do('copy'))
 	bind('cut', do('cut'))
diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py
index 7fa130ec..6ca25475 100644
--- a/ranger/fsobject/directory.py
+++ b/ranger/fsobject/directory.py
@@ -24,6 +24,7 @@ class Directory(SuperClass, SettingsAware):
 	filenames = None
 	files = None
 	filter = None
+	marked_items = None
 	pointed_index = None
 	pointed_file = None
 	scroll_begin = 0
@@ -40,10 +41,59 @@ class Directory(SuperClass, SettingsAware):
 
 		SuperClass.__init__(self, path)
 
+		self.marked_items = set()
+
 		# to find out if something has changed:
 		self.old_show_hidden = self.settings.show_hidden
 		self.old_directories_first = self.settings.directories_first
-		self.load_progress = None
+	
+	def mark_item(self, item, val):
+		item._mark(bool(val))
+		if val:
+			if item in self.files:
+				self.marked_items.add(item)
+		else:
+			if item in self.marked_items:
+				self.marked_items.remove(item)
+
+	def toggle_mark(self, item):
+		if item.marked:
+			return self.unmark_item(item)
+		return self.mark_item(item)
+
+	def toggle_all_marks(self):
+		for item in self.files:
+			self.toggle_mark(item)
+	
+	def mark_all(self, val):
+		if val:
+			for item in self.files:
+				self.mark_item(item)
+		else:
+			for item in self.files:
+				self.unmark_item(item)
+			self.marked_items.clear()
+			self._clear_marked_items()
+	
+	def _gc_marked_items(self):
+		for item in self.marked_items.copy():
+			if item.path not in self.filenames:
+				self.marked_items.remove(item)
+	
+	def _clear_marked_items(self):
+		for item in self.marked_items:
+			item._mark(False)
+		self.marked_items.clear()
+
+	def get_selection(self):
+		"""READ ONLY"""
+		self._gc_marked_items()
+		if self.marked_items:
+			return set(self.marked_items)
+		elif self.pointed_file:
+			return set([self.pointed_file])
+		else:
+			return set()
 	
 	def load_bit_by_bit(self):
 		"""Loads the contents of the directory. Use this sparingly since
@@ -71,17 +121,28 @@ class Directory(SuperClass, SettingsAware):
 			self.infostring = ' %d' % len(self.filenames) # update the infostring
 			yield
 
+			marked_paths = set(map( \
+					lambda obj: obj.path, self.marked_items))
+			self._clear_marked_items()
+
 			files = []
 			for name in self.filenames:
 				if isdir(name):
-					f = Directory(name)
+					item = Directory(name)
 				else:
-					f = File(name)
-				f.load()
-				files.append(f)
+					item = File(name)
+				item.load()
+				files.append(item)
 				yield
 
 			self.files = files
+
+			for item in self.files:
+				if item.path in marked_paths:
+					self.mark_item(item)
+				else:
+					self.unmark_item(item)
+
 			self.old_directories_first = None
 
 			if len(self.files) > 0:
@@ -226,6 +287,9 @@ class Directory(SuperClass, SettingsAware):
 
 			self.pointed_index = i
 			self.pointed_file = self[i]
+
+		if self == self.fm.env.pwd:
+			self.fm.env.cf = self.pointed_file
 		
 	def load_content_once(self, *a, **k):
 		"""Load the contents of the directory if not done yet"""
@@ -279,3 +343,6 @@ class Directory(SuperClass, SettingsAware):
 	def __neq__(self, other):
 		"""Check for inequality of the directories paths"""
 		return not self.__eq__(other)
+	
+	def __hash__(self):
+		return hash(self.path)
diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py
index 7899e5f5..d968d65f 100644
--- a/ranger/fsobject/fsobject.py
+++ b/ranger/fsobject/fsobject.py
@@ -16,7 +16,6 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 	accessible = False
 	marked = False
 	tagged = False
-	frozen = False
 	loaded = False
 	runnable = False
 	islink = False
@@ -92,6 +91,14 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 
 		if self.mimetype == '':
 			self.mimetype = None
+	
+	def mark(self, boolean):
+		directory = self.env.get_directory(self.dirname)
+		directory.mark_item(self)
+	
+	def _mark(self, boolean):
+		"""Called by directory.mark_item() and similar functions"""
+		self.marked = boolean
 
 	def load(self):
 		"""reads useful information about the filesystem-object from the
diff --git a/ranger/gui/colorscheme.py b/ranger/gui/colorscheme.py
index d8dbc927..386c0f85 100644
--- a/ranger/gui/colorscheme.py
+++ b/ranger/gui/colorscheme.py
@@ -8,6 +8,7 @@ CONTEXT_KEYS = [ 'reset', 'error',
 		'good', 'bad',
 		'space', 'permissions', 'owner', 'group', 'mtime', 'nlink',
 		'scroll', 'all', 'bot', 'top', 'percentage',
+		'marked',
 		'keybuffer']
 
 # colorscheme specification:
diff --git a/ranger/gui/widgets/filelist.py b/ranger/gui/widgets/filelist.py
index 2ece3077..8b4443d3 100644
--- a/ranger/gui/widgets/filelist.py
+++ b/ranger/gui/widgets/filelist.py
@@ -174,6 +174,9 @@ class FileList(Widget):
 			if i == selected_i:
 				this_color.append('selected')
 
+			if drawed.marked:
+				this_color.append('marked')
+
 			if isinstance(drawed, Directory):
 				this_color.append('directory')
 			else:
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index b8b418c9..17a5348b 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -94,28 +94,30 @@ class StatusBar(Widget):
 
 	def _get_right_part(self):
 		part = []
-		if self.filelist is not None:
-			target = self.filelist.target
-		else:
-			target = self.env.at_level(0)
+		if self.filelist is None:
+			return part
+
+		target = self.filelist.target
+#		target = self.env.at_level(0)
 
 		if not target.content_loaded or not target.accessible:
 			return part
 
-		if self.filelist is not None:
-			pos = target.scroll_begin
-			max_pos = len(target) - self.filelist.hei
-
-			if max_pos > 0:
-				if pos == 0:
-					part.append([['scroll', 'top'], 'Top'])
-				elif pos >= max_pos:
-					part.append([['scroll', 'bot'], 'Bot'])
-				else:
-					part.append([['scroll', 'percentage'], \
-						'{0:0>.0f}%'.format(100.0 * pos / max_pos)])
+		pos = target.scroll_begin
+		max_pos = len(target) - self.filelist.hei
+
+		if target.marked_items:
+			part.append([['scroll', 'marked'], 'Mrk'])
+		elif max_pos > 0:
+			if pos == 0:
+				part.append([['scroll', 'top'], 'Top'])
+			elif pos >= max_pos:
+				part.append([['scroll', 'bot'], 'Bot'])
 			else:
-				part.append([['scroll', 'all'], 'All'])
+				part.append([['scroll', 'percentage'], \
+					'{0:0>.0f}%'.format(100.0 * pos / max_pos)])
+		else:
+			part.append([['scroll', 'all'], 'All'])
 		return part
 
 	def _combine_parts(self, left, right):