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):
pre>365ba9c5 ^
365ba9c5 ^
2b82ef62 ^
365ba9c5 ^





7d9ae552 ^
dd7a4f63 ^
419e4aa5 ^
f027adc0 ^
f027adc0 ^

dd7a4f63 ^
9506fb8e ^
f027adc0 ^
9506fb8e ^
f027adc0 ^
9506fb8e ^
dd7a4f63 ^
8b60e8ca ^





dd7a4f63 ^

419e4aa5 ^
dd7a4f63 ^
f027adc0 ^
5805deca ^




dd7a4f63 ^
b810fe28 ^
f027adc0 ^



3201b163 ^


419e4aa5 ^
f027adc0 ^
419e4aa5 ^
f027adc0 ^
419e4aa5 ^
5805deca ^


f027adc0 ^

30b25795 ^
dd7a4f63 ^
f027adc0 ^
dd7a4f63 ^
4c05e43d ^

dd7a4f63 ^
4c05e43d ^

dd7a4f63 ^
9506fb8e ^



dd7a4f63 ^
f027adc0 ^



9506fb8e ^
dd7a4f63 ^
d88232a3 ^
65cb1a32 ^
dd7a4f63 ^









d494f019 ^


dd7a4f63 ^





db9bb0c9 ^



dd7a4f63 ^













d88232a3 ^
dd7a4f63 ^

9506fb8e ^
d494f019 ^


22449efd ^
d494f019 ^







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265