about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2009-12-22 15:53:15 +0100
committerhut <hut@lavabit.com>2009-12-22 15:53:15 +0100
commitfa704babe536ebd6409a939cfb684d5ac281de8a (patch)
treec119b7312afcfd858da0f512c2396db3825cf321
parent2b82ef62212c9a8d8702523c2720eb10b6b2bb28 (diff)
downloadranger-fa704babe536ebd6409a939cfb684d5ac281de8a.tar.gz
(half-assed) implementation of parallel directory loader
-rw-r--r--ranger/actions.py14
-rw-r--r--ranger/fm.py4
-rw-r--r--ranger/fsobject/__init__.py7
-rw-r--r--ranger/fsobject/directory.py101
-rw-r--r--ranger/fsobject/loader.py61
-rw-r--r--ranger/gui/ui.py15
-rw-r--r--ranger/gui/widgets/filelist.py36
7 files changed, 165 insertions, 73 deletions
diff --git a/ranger/actions.py b/ranger/actions.py
index e699053c..d51b2119 100644
--- a/ranger/actions.py
+++ b/ranger/actions.py
@@ -139,16 +139,10 @@ class Actions(EnvironmentAware, SettingsAware):
 		if isinstance(self.env.settings[string], bool):
 			self.env.settings[string] ^= True
 	
-	def force_load_preview(self, obj=None):
-		if not obj:
-			obj = self.env.cf
-
-		if isinstance(obj, fsobject.Directory):
-			if not obj.force_load:
-				obj.force_load = True
-			else:
-				obj.load_content()
-
+	def force_load_preview(self):
+		cf = self.env.cf
+		if cf is not None:
+			cf.force_load = True
 
 # ------------------------------------ filesystem operations
 
diff --git a/ranger/fm.py b/ranger/fm.py
index db09129c..59568e68 100644
--- a/ranger/fm.py
+++ b/ranger/fm.py
@@ -4,6 +4,7 @@ from ranger.actions import Actions
 from ranger.container import Bookmarks
 from ranger.ext.relpath import relpath_conf
 from ranger import __version__
+from ranger.fsobject import Loader
 
 CTRL_C = 3
 TICKS_BEFORE_COLLECTING_GARBAGE = 100
@@ -16,6 +17,7 @@ class FM(Actions):
 		Actions.__init__(self)
 		self.ui = ui
 		self.bookmarks = bookmarks
+		self.loader = Loader()
 		self.apps = self.settings.apps.CustomApplications()
 
 		from ranger.shared import FileManagerAware
@@ -61,6 +63,8 @@ class FM(Actions):
 				try:
 					self.bookmarks.update_if_outdated()
 					self.ui.redraw()
+					self.loader.work()
+					self.ui.set_load_mode(self.loader)
 
 					key = self.ui.get_next_key()
 
diff --git a/ranger/fsobject/__init__.py b/ranger/fsobject/__init__.py
index ce765e1e..79645c5e 100644
--- a/ranger/fsobject/__init__.py
+++ b/ranger/fsobject/__init__.py
@@ -11,6 +11,7 @@ BAD_INFO = None
 class NotLoadedYet(Exception):
 	pass
 
-from ranger.fsobject.file import File
-from ranger.fsobject.directory import Directory, NoDirectoryGiven
-from ranger.fsobject.fsobject import FileSystemObject
+from .file import File
+from .directory import Directory, NoDirectoryGiven
+from .fsobject import FileSystemObject
+from .loader import Loader
diff --git a/ranger/fsobject/directory.py b/ranger/fsobject/directory.py
index fea7be34..2478616d 100644
--- a/ranger/fsobject/directory.py
+++ b/ranger/fsobject/directory.py
@@ -18,6 +18,7 @@ class NoDirectoryGiven(Exception):
 class Directory(SuperClass, SettingsAware):
 	scheduled = False
 	enterable = False
+	loading = False
 
 	filenames = None
 	files = None
@@ -41,8 +42,9 @@ class Directory(SuperClass, SettingsAware):
 		# to find out if something has changed:
 		self.old_show_hidden = self.settings.show_hidden
 		self.old_directories_first = self.settings.directories_first
-
-	def load_content(self):
+		self.load_progress = None
+	
+	def load_bit_by_bit(self):
 		"""Loads the contents of the directory. Use this sparingly since
 		it takes rather long.
 		"""
@@ -50,43 +52,58 @@ class Directory(SuperClass, SettingsAware):
 		from os import listdir
 
 		self.load_if_outdated()
+		yield
+
+		if self.exists and self.runnable:
+			filenames = []
+			for fname in listdir(self.path):
+				if not self.settings.show_hidden and fname[0] == '.':
+					continue
+				if isinstance(self.filter, str) and self.filter in fname:
+					continue
+				filenames.append(join(self.path, fname))
+			self.scroll_offset = 0
+			self.filenames = filenames
+			self.infostring = ' %d' % len(self.filenames) # update the infostring
+			yield
+
+			files = []
+			for name in self.filenames:
+				if isdir(name):
+					f = Directory(name)
+				else:
+					f = File(name)
+				f.load()
+				files.append(f)
+				yield
+
+			self.files = files
+			self.old_directories_first = None
+
+			if len(self.files) > 0:
+				if self.pointed_file is not None:
+					self.move_pointer_to_file_path(self.pointed_file)
+		else:
+			self.filenames = None
+			self.files = None
+			self.infostring = BAD_INFO
+
+		self.content_loaded = True
+		self.loading = False
+		yield
+
+	def load_content(self, schedule=False):
+		"""Loads the contents of the directory. Use this sparingly since
+		it takes rather long.
+		"""
 
-		try:
-			self.stopped = False
-			if self.exists and self.runnable:
-				filenames = []
-				for fname in listdir(self.path):
-					if not self.settings.show_hidden and fname[0] == '.':
-						continue
-					if isinstance(self.filter, str) and self.filter in fname:
-						continue
-					filenames.append(join(self.path, fname))
-				self.scroll_offset = 0
-				self.filenames = filenames
-				self.infostring = ' %d' % len(self.filenames) # update the infostring
-				files = []
-				for name in self.filenames:
-					if isdir(name):
-						f = Directory(name)
-					else:
-						f = File(name)
-					f.load()
-					files.append(f)
-
-				self.files = files
-				self.old_directories_first = None
-
-				if len(self.files) > 0:
-					if self.pointed_file is not None:
-						self.move_pointer_to_file_path(self.pointed_file)
+		if not self.loading:
+			if schedule and self.fm:
+				self.loading = True
+				self.fm.loader.add(self)
 			else:
-				self.filenames = None
-				self.files = None
-				self.infostring = BAD_INFO
-			self.content_loaded = True
-		except (KeyboardInterrupt, ValueError):
-			self.stopped = True
-			
+				for _ in self.load_bit_by_bit():
+					pass
 
 	def sort(self):
 		"""Sort the containing files"""
@@ -192,22 +209,22 @@ class Directory(SuperClass, SettingsAware):
 			self.pointed_index = i
 			self.pointed_file = self[i]
 		
-	def load_content_once(self):
+	def load_content_once(self, *a, **k):
 		"""Load the contents of the directory if not done yet"""
 		if not self.content_loaded:
-			self.load_content()
+			self.load_content(*a, **k)
 			return True
 		return False
 
-	def load_content_if_outdated(self):
+	def load_content_if_outdated(self, *a, **k):
 		"""Load the contents of the directory if it's
 		outdated or not done yet
 		"""
-		if self.load_content_once(): return True
+		if self.load_content_once(*a, **k): return True
 
 		if self.old_show_hidden != self.settings.show_hidden:
 			self.old_show_hidden = self.settings.show_hidden
-			self.load_content()
+			self.load_content(*a, **k)
 			return True
 
 		import os
diff --git a/ranger/fsobject/loader.py b/ranger/fsobject/loader.py
new file mode 100644
index 00000000..a1ebcc97
--- /dev/null
+++ b/ranger/fsobject/loader.py
@@ -0,0 +1,61 @@
+from collections import deque
+from time import time
+from ranger import log
+import math
+
+def status_generator():
+	while True:
+		yield '/'
+		yield '-'
+		yield '\\'
+		yield '|'
+
+def delayfunc(n):
+	if n < 4:
+		return 0.05
+	else:
+		return math.log(n-2) * 0.2
+
+class Loader(object):
+	seconds_of_work_time = 0.1
+	def __init__(self):
+		self.queue = deque()
+		self.item = None
+		self.load_generator = None
+		self.status_generator = status_generator()
+		self.tick = 0
+		self.rotate()
+	
+	def rotate(self):
+		self.status = next(self.status_generator)
+	
+	def add(self, obj):
+		self.queue.append(obj)
+
+	def work(self):
+		if self.item is None:
+			try:
+				self.item = self.queue.popleft()
+			except IndexError:
+				return
+
+			self.load_generator = self.item.load_bit_by_bit()
+			self.tick = 0
+
+		self.rotate()
+		self.tick += 1
+		start_time = time()
+		end_time = time() + delayfunc(self.tick)
+
+		log(tuple(map(str, self.queue)))
+		try:
+#			log("loading " + self.item.basename)
+			while time() < end_time:
+				next(self.load_generator)
+
+		except StopIteration:
+			self.item = None
+			self.load_generator = None
+	
+	def __nonzero__(self):
+		return bool(self.queue or self.item is not None)
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 2cb66f2f..e55b9ba9 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -7,6 +7,7 @@ from ranger.container import CommandList
 class UI(DisplayableContainer):
 	is_set_up = False
 	mousemask = curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION
+	load_mode = False
 	def __init__(self, commandlist=None, env=None, fm=None):
 		import os
 		os.environ['ESCDELAY'] = '25' # don't know a cleaner way
@@ -29,6 +30,7 @@ class UI(DisplayableContainer):
 		"""initialize curses, then call setup (at the first time) and resize."""
 		self.win.leaveok(0)
 		self.win.keypad(1)
+		self.load_mode = False
 
 		curses.cbreak()
 		curses.noecho()
@@ -64,6 +66,19 @@ class UI(DisplayableContainer):
 		curses.mousemask(0)
 		curses.endwin()
 
+	def set_load_mode(self, boolean):
+		from ranger import log
+		boolean = bool(boolean)
+		if boolean != self.load_mode:
+			self.load_mode = boolean
+
+			if boolean:
+				log('setting halfdelay to 1')
+				curses.halfdelay(1)
+			else:
+				log('setting halfdelay to 20')
+				curses.halfdelay(20)
+
 	def destroy(self):
 		"""Destroy all widgets and turn off curses"""
 		DisplayableContainer.destroy(self)
diff --git a/ranger/gui/widgets/filelist.py b/ranger/gui/widgets/filelist.py
index 58dcd856..b4c756f1 100644
--- a/ranger/gui/widgets/filelist.py
+++ b/ranger/gui/widgets/filelist.py
@@ -71,12 +71,10 @@ class FileList(Widget):
 			self.draw_file()
 		elif type(self.target) == Directory:
 			self.draw_directory()
-#		else:
-#			self.win.addnstr(self.y, self.x, "unknown type.", self.wid)
 
 	def finalize(self):
 		if self.postpone_drawing:
-			self.target.load_content_if_outdated()
+			self.target.load_content_if_outdated(schedule=True)
 			self.draw_directory()
 			self.postpone_drawing = False
 
@@ -111,21 +109,23 @@ class FileList(Widget):
 		self.target.use()
 
 		if not self.target.content_loaded:
-			if self.target.force_load:
-				self.target.stopped = False
-
-			else:
-				if not self.target.stopped:
-					maxdirsize = self.settings.max_dirsize_for_autopreview
-					if maxdirsize is not None and self.target.accessible \
-							and self.target.size > maxdirsize:
-						self.target.stopped = True
-
-				if self.target.stopped:
-					self.color(base_color, 'error')
-					self.win.addnstr(self.y, self.x, "no preview", self.wid)
-					self.color_reset()
-					return
+#			if self.target.force_load:
+#				self.target.stopped = False
+#
+#			else:
+#				if not self.target.stopped:
+#					
+#					if maxdirsize is not None and self.target.accessible \
+#							and self.target.size > maxdirsize:
+#						self.target.stopped = True
+#
+			maxdirsize = self.settings.max_dirsize_for_autopreview
+			if not self.target.force_load and maxdirsize is not None \
+					and self.target.size > maxdirsize:
+				self.color(base_color, 'error')
+				self.win.addnstr(self.y, self.x, "no preview", self.wid)
+				self.color_reset()
+				return
 
 			if self.settings.auto_load_preview:
 				self.color(base_color)