about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--TODO3
-rw-r--r--ranger/gui/defaultui.py22
-rw-r--r--ranger/gui/displayable.py135
-rw-r--r--ranger/gui/widgets/filelistcontainer.py18
-rw-r--r--test/tc_displayable.py12
-rw-r--r--test/tc_ui.py2
6 files changed, 128 insertions, 64 deletions
diff --git a/TODO b/TODO
index f0024ac5..7b92c24b 100644
--- a/TODO
+++ b/TODO
@@ -9,7 +9,6 @@ Console
 
 General
 
-   ( ) #3   09/12/06  MVC for widgets
    (X) #5   09/12/06  move code from fm into objects
    (X) #6   09/12/06  move main to __init__
    (X) #7   09/12/06  cooler titlebar
@@ -18,5 +17,5 @@ General
    (X) #10  09/12/24  sorting
    (X) #11  09/12/27  filter
    (X) #12  09/12/27  jump through the list in a specific order
-   ( ) #14  09/12/29  make filelists inherit from pagers
+   (X) #14  09/12/29  make filelists inherit from pagers
    ( ) #15  09/12/29  better way of running processes!!~
diff --git a/ranger/gui/defaultui.py b/ranger/gui/defaultui.py
index 717bb925..08ec0b4b 100644
--- a/ranger/gui/defaultui.py
+++ b/ranger/gui/defaultui.py
@@ -13,28 +13,36 @@ class DefaultUI(UI):
 		from ranger.gui.widgets.notify import Notify
 		from ranger.gui.widgets.pager import Pager
 
+		# Create a title bar
 		self.titlebar = TitleBar(self.win)
-		self.add_obj(self.titlebar)
+		self.add_child(self.titlebar)
 
+		# Create the container for the filelists
 		self.filelist_container = FileListContainer(self.win, RATIO)
-		self.add_obj(self.filelist_container)
+		self.add_child(self.filelist_container)
 		self.main_filelist = self.filelist_container.main_filelist
 
+		# Create the process manager
 		self.pman = ProcessManager(self.win)
 		self.pman.visible = False
-		self.add_obj(self.pman)
+		self.add_child(self.pman)
 
+		# Create the (initially hidden) notify bar
 		self.notify = Notify(self.win)
-		self.add_obj(self.notify)
+		self.add_child(self.notify)
 
+		# Create the status bar
 		self.status = StatusBar(self.win, self.main_filelist)
-		self.add_obj(self.status)
+		self.add_child(self.status)
+
+		# Create the console
 		self.console = Console(self.win)
-		self.add_obj(self.console)
+		self.add_child(self.console)
 		self.console.visible = False
 
+		# Create the pager
 		self.pager = Pager(self.win)
-		self.add_obj(self.pager)
+		self.add_child(self.pager)
 
 	def update_size(self):
 		"""resize all widgets"""
diff --git a/ranger/gui/displayable.py b/ranger/gui/displayable.py
index 41ed9ae4..315d2f7b 100644
--- a/ranger/gui/displayable.py
+++ b/ranger/gui/displayable.py
@@ -3,10 +3,42 @@ from ranger import log
 import _curses
 
 class Displayable(EnvironmentAware, FileManagerAware, SettingsAware):
-	focused = False
-	visible = True
-	win = None
-	colorscheme = None
+	"""
+	Displayables are objects which are displayed on the screen.
+
+	This is just the abstract class, defining basic operations
+	such as resizing, printing, changing colors.
+	Subclasses of displayable should implement this interface:
+
+	draw() -- draw the object here. Is only called if visible.
+	poke() -- is called just before draw(), even if not visible.
+	finalize() -- called after all objects finished drawing.
+	click(event) -- called with a MouseEvent. This is called on all
+		visible objects under the mouse, until one returns True.
+	press(key) -- called after a key press on focused objects.
+	destroy() -- called before destroying the displayable object
+
+	This abstract class defines the following (helper) methods:
+
+	color(*keys) -- sets the color associated with the keys from
+		the current colorscheme.
+	color_at(y, x, wid, *keys) -- sets the color at the given position
+	color_reset() -- resets the color to the default
+	addstr(*args) -- failsafe version of self.win.addstr(*args)
+	__contains__(item) -- is the item (y, x) inside the widget?
+	
+	These attributes are set:
+
+	Modifiable:
+		focused -- Focused objects receive press() calls.
+		visible -- Visible objects receive draw() and finalize() calls
+	
+	Read-Only: (i.e. reccomended not to change manually)
+		win -- the own curses window object
+		parent -- the parent (DisplayableContainer) object or None
+		x, y, wid, hei -- absolute coordinates and boundaries
+		settings, fm, env -- inherited shared variables
+	"""
 
 	def __init__(self, win, env=None, fm=None, settings=None):
 		from ranger.gui.ui import UI
@@ -17,11 +49,12 @@ class Displayable(EnvironmentAware, FileManagerAware, SettingsAware):
 		if settings is not None:
 			self.settings = settings
 
+		self.focused = False
+		self.visible = True
 		self.x = 0
 		self.y = 0
 		self.wid = 0
 		self.hei = 0
-		self.colorscheme = self.settings.colorscheme
 		self.parent = None
 
 		if win is not None:
@@ -57,7 +90,7 @@ class Displayable(EnvironmentAware, FileManagerAware, SettingsAware):
 	def color(self, keylist = None, *keys):
 		"""Change the colors from now on."""
 		keys = combine(keylist, keys)
-		attr = self.colorscheme.get_attr(*keys)
+		attr = self.settings.colorscheme.get_attr(*keys)
 		try:
 			self.win.attrset(attr)
 		except _curses.error:
@@ -66,7 +99,7 @@ class Displayable(EnvironmentAware, FileManagerAware, SettingsAware):
 	def color_at(self, y, x, wid, keylist = None, *keys):
 		"""Change the colors at the specified position"""
 		keys = combine(keylist, keys)
-		attr = self.colorscheme.get_attr(*keys)
+		attr = self.settings.colorscheme.get_attr(*keys)
 		try:
 			self.win.chgat(y, x, wid, attr)
 		except _curses.error:
@@ -107,15 +140,6 @@ class Displayable(EnvironmentAware, FileManagerAware, SettingsAware):
 		"""
 		pass
 
-	def activate(self, boolean):
-		boolean = bool(boolean)
-		self.visible = boolean
-		self.focused = boolean
-	
-	def show(self, boolean):
-		boolean = bool(boolean)
-		self.visible = boolean
-
 	def poke(self):
 		"""Called before drawing, even if invisible"""
 	
@@ -162,7 +186,6 @@ class Displayable(EnvironmentAware, FileManagerAware, SettingsAware):
 
 		if hei != self.hei or wid != self.wid:
 			try:
-#				log("resizing " + self.__class__.__name__)
 				self.win.resize(hei, wid)
 			except:
 				# Not enough space for resizing...
@@ -189,7 +212,23 @@ class Displayable(EnvironmentAware, FileManagerAware, SettingsAware):
 				self.x += self.parent.x
 
 class DisplayableContainer(Displayable):
-	container = None
+	"""
+	DisplayableContainers are Displayables which contain other Displayables.
+
+	This is also an abstract class. The methods draw, poke, finalize,
+	click, press and destroy are overridden here and will recursively
+	call the function on all contained objects.
+
+	New methods:
+
+	add_child(object) -- add the object to the container.
+	remove_child(object) -- remove the object from the container.
+
+	New attributes:
+
+	container -- a list with all contained objects (rw)
+	"""
+
 	def __init__(self, win, env=None, fm=None, settings=None):
 		if env is not None:
 			self.env = env
@@ -198,9 +237,12 @@ class DisplayableContainer(Displayable):
 		if settings is not None:
 			self.settings = settings
 
-		Displayable.__init__(self, win)
 		self.container = []
 
+		Displayable.__init__(self, win)
+	
+	# ----------------------------------------------- overrides
+
 	def poke(self):
 		"""Recursively called on objects in container"""
 		for displayable in self.container:
@@ -217,24 +259,10 @@ class DisplayableContainer(Displayable):
 		for displayable in self.container:
 			if displayable.visible:
 				displayable.finalize()
-	
-	def get_focused_obj(self):
-		"""Finds a focused displayable object in the container."""
-		for displayable in self.container:
-			if displayable.focused:
-				return displayable
-			try:
-				obj = displayable.get_focused_obj()
-			except AttributeError:
-				pass
-			else:
-				if obj is not None:
-					return obj
-		return None
 
 	def press(self, key):
 		"""Recursively called on objects in container"""
-		focused_obj = self.get_focused_obj()
+		focused_obj = self._get_focused_obj()
 
 		if focused_obj:
 			focused_obj.press(key)
@@ -243,7 +271,7 @@ class DisplayableContainer(Displayable):
 
 	def click(self, event):
 		"""Recursively called on objects in container"""
-		focused_obj = self.get_focused_obj()
+		focused_obj = self._get_focused_obj()
 		if focused_obj and focused_obj.click(event):
 			return True
 
@@ -254,16 +282,43 @@ class DisplayableContainer(Displayable):
 
 		return False
 
-	def add_obj(self, *objs):
-		self.container.extend(objs)
-		for obj in objs:
-			obj.parent = self
-
 	def destroy(self):
 		"""Recursively called on objects in container"""
 		for displayable in self.container:
 			displayable.destroy()
 
+	# ----------------------------------------------- new methods
+
+	def add_child(self, obj):
+		"""Add the objects to the container."""
+		if obj.parent:
+			obj.parent.remove_child(obj)
+		self.container.append(obj)
+		obj.parent = self
+	
+	def remove_child(self, obj):
+		"""Remove the object from the container."""
+		try:
+			container.remove(obj)
+		except ValueError:
+			pass
+		else:
+			obj.parent = None
+	
+	def _get_focused_obj(self):
+		# Finds a focused displayable object in the container.
+		for displayable in self.container:
+			if displayable.focused:
+				return displayable
+			try:
+				obj = displayable._get_focused_obj()
+			except AttributeError:
+				pass
+			else:
+				if obj is not None:
+					return obj
+		return None
+
 class OutOfBoundsException(Exception):
 	pass
 
diff --git a/ranger/gui/widgets/filelistcontainer.py b/ranger/gui/widgets/filelistcontainer.py
index b5ef4190..ed1922b8 100644
--- a/ranger/gui/widgets/filelistcontainer.py
+++ b/ranger/gui/widgets/filelistcontainer.py
@@ -30,7 +30,7 @@ class FileListContainer(Widget, DisplayableContainer):
 
 		for level in range(len(ratios)):
 			fl = FileList(self.win, level + offset)
-			self.add_obj(fl)
+			self.add_child(fl)
 
 		try:
 			self.main_filelist = self.container[preview and -2 or -1]
@@ -41,7 +41,7 @@ class FileListContainer(Widget, DisplayableContainer):
 			self.main_filelist.main_display = True
 
 		self.pager = Pager(self.win, embedded=True)
-		self.add_obj(self.pager)
+		self.add_child(self.pager)
 	
 	def resize(self, y, x, hei, wid):
 		"""Resize all the filelists according to the given ratio"""
@@ -84,20 +84,22 @@ class FileListContainer(Widget, DisplayableContainer):
 			DisplayableContainer.click(self, event)
 	
 	def open_pager(self):
-		self.pager.activate(True)
+		self.pager.visible = True
+		self.pager.focused = True
 		self.pager.open()
 		try:
-			self.container[-2].show(False)
-			self.container[-3].show(False)
+			self.container[-2].visible = False
+			self.container[-3].visible = False
 		except IndexError:
 			pass
 	
 	def close_pager(self):
-		self.pager.activate(False)
+		self.pager.visible = False
+		self.pager.focused = False
 		self.pager.close()
 		try:
-			self.container[-2].show(True)
-			self.container[-3].show(True)
+			self.container[-2].visible = True
+			self.container[-3].visible = True
 		except IndexError:
 			pass
 	
diff --git a/test/tc_displayable.py b/test/tc_displayable.py
index 3acf338b..d7466809 100644
--- a/test/tc_displayable.py
+++ b/test/tc_displayable.py
@@ -74,7 +74,7 @@ class TestDisplayableContainer(unittest.TestCase):
 
 		self.disp = Displayable(**self.initdict)
 		self.disc = DisplayableContainer(**self.initdict)
-		self.disc.add_obj(self.disp)
+		self.disc.add_child(self.disp)
 
 		hei, wid = (100, 100)
 		self.env.termsize = (hei, wid)
@@ -103,20 +103,20 @@ class TestDisplayableContainer(unittest.TestCase):
 	def test_focused_object(self):
 		d1 = Displayable(**self.initdict)
 		d2 = DisplayableContainer(**self.initdict)
-		d2.add_obj(*[Displayable(**self.initdict) for x in range(5)])
+		d2.add_child(*[Displayable(**self.initdict) for x in range(5)])
 		d3 = DisplayableContainer(**self.initdict)
-		d3.add_obj(*[Displayable(**self.initdict) for x in range(5)])
+		d3.add_child(*[Displayable(**self.initdict) for x in range(5)])
 
-		self.disc.add_obj(d1, d2, d3)
+		self.disc.add_child(d1, d2, d3)
 
 		d3.container[3].focused = True
 
-		self.assertEqual(self.disc.get_focused_obj(), d3.container[3])
+		self.assertEqual(self.disc._get_focused_obj(), d3.container[3])
 
 		d3.container[3].focused = False
 		d2.container[0].focused = True
 
-		self.assertEqual(self.disc.get_focused_obj(), d2.container[0])
+		self.assertEqual(self.disc._get_focused_obj(), d2.container[0])
 
 if __name__ == '__main__':
 	unittest.main()
diff --git a/test/tc_ui.py b/test/tc_ui.py
index f509ff36..eb503c5d 100644
--- a/test/tc_ui.py
+++ b/test/tc_ui.py
@@ -17,7 +17,7 @@ class Test(unittest.TestCase):
 
 		def fakesetup():
 			self.ui.widget = Fake()
-			self.ui.add_obj(self.ui.widget)
+			self.ui.add_child(self.ui.widget)
 		self.ui.setup = fakesetup
 
 		self.ui.initialize()