Index: ColorDelegator.py
===================================================================
--- ColorDelegator.py	(revision 58404)
+++ ColorDelegator.py	(working copy)
@@ -248,16 +248,17 @@
             self.tag_remove(tag, "1.0", "end")
 
 def main():
-    from Percolator import Percolator
+    from Percolator import TkTextPercolator
     root = Tk()
     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
     text = Text(background="white")
     text.pack(expand=1, fill="both")
     text.focus_set()
-    p = Percolator(text)
+    p = TkTextPercolator(text)
     d = ColorDelegator()
     p.insertfilter(d)
     root.mainloop()
+    root.destroy()
 
 if __name__ == "__main__":
     main()
Index: EditorWindow.py
===================================================================
--- EditorWindow.py	(revision 58404)
+++ EditorWindow.py	(working copy)
@@ -40,7 +40,7 @@
     return file, filename, descr
 
 class EditorWindow(object):
-    from Percolator import Percolator
+    from Percolator import TkTextPercolator
     from ColorDelegator import ColorDelegator
     from UndoDelegator import UndoDelegator
     from IOBinding import IOBinding, filesystemencoding, encoding
@@ -223,7 +223,7 @@
         # Making the initial values larger slows things down more often.
         self.num_context_lines = 50, 500, 5000000
 
-        self.per = per = self.Percolator(text)
+        self.per = per = self.TkTextPercolator(text)
         if self.ispythonsource(filename):
             self.color = color = self.ColorDelegator()
             per.insertfilter(color)
@@ -589,17 +589,14 @@
         if not self.color:
             return
         self.color.removecolors()
-        self.per.removefilter(self.undo)
         self.per.removefilter(self.color)
         self.color = None
-        self.per.insertfilter(self.undo)
 
     def ResetColorizer(self):
         "Update the colour theme if it is changed"
         # Called from configDialog.py
-        if self.color:
-            self.color = self.ColorDelegator()
-            self.per.insertfilter(self.color)
+        self.rmcolorizer()
+        self.addcolorizer()
         theme = idleConf.GetOption('main','Theme','name')
         self.text.config(idleConf.GetHighlight(theme, "normal"))
 
@@ -838,7 +835,7 @@
             self.color = None
         self.text = None
         self.tkinter_vars = None
-        self.per.close()
+        self.per.percolator_close()
         self.per = None
         self.top.destroy()
         if self.close_hook:
Index: Percolator.py
===================================================================
--- Percolator.py	(revision 58404)
+++ Percolator.py	(working copy)
@@ -1,57 +1,108 @@
 from WidgetRedirector import WidgetRedirector
 from Delegator import Delegator
 
-class Percolator:
 
-    def __init__(self, text):
-        # XXX would be nice to inherit from Delegator
-        self.text = text
-        self.redir = WidgetRedirector(text)
-        self.top = self.bottom = Delegator(text)
-        self.bottom.insert = self.redir.register("insert", self.insert)
-        self.bottom.delete = self.redir.register("delete", self.delete)
-        self.filters = []
+class Percolator(Delegator):
+    """A linked list of Delegator objects, each delegating to the next.
 
-    def close(self):
+    A Percolator is a Delegator, and therefore inherits from Delegator.
+
+    Use 'insertfilter' to add a Delegator to the beginning of the list,
+    and 'removefilter' to remove a Delegator from (anywhere in) the list.
+
+    'setdelegate' and 'getdelegate' refer to the original delegate, to which
+    the last delegator in the list delegates.
+
+    """
+
+    def __init__(self, delegate):
+        Delegator.__init__(self)
+        self.top = self.bottom = Delegator(delegate)
+
+    # Always actually delegate to self.top
+    top = property(lambda self: Delegator.getdelegate(self),
+                   lambda self, delegate: Delegator.setdelegate(self, delegate))
+
+    # setdelegate and getdelegate refer to the original delegate
+    def setdelegate(self, delegate):
+        self.bottom.setdelegate(delegate)
+        self.resetcache()
+        f = self.top
+        while f is not self.bottom:
+            f.resetcache()
+            f = f.getdelegate()
+
+    def getdelegate(self):
+        return self.bottom.getdelegate()
+
+
+    def percolator_close(self):
         while self.top is not self.bottom:
             self.removefilter(self.top)
-        self.top = None
-        self.bottom.setdelegate(None); self.bottom = None
-        self.redir.close(); self.redir = None
-        self.text = None
+        self.setdelegate(None)
+#        self.top = None
 
-    def insert(self, index, chars, tags=None):
-        # Could go away if inheriting from Delegator
-        self.top.insert(index, chars, tags)
-
-    def delete(self, index1, index2=None):
-        # Could go away if inheriting from Delegator
-        self.top.delete(index1, index2)
-
     def insertfilter(self, filter):
         # Perhaps rename to pushfilter()?
         assert isinstance(filter, Delegator)
-        assert filter.delegate is None
+        assert filter.getdelegate() is None
         filter.setdelegate(self.top)
         self.top = filter
 
     def removefilter(self, filter):
         # XXX Perhaps should only support popfilter()?
         assert isinstance(filter, Delegator)
-        assert filter.delegate is not None
+        assert filter.getdelegate() is not None
         f = self.top
         if f is filter:
-            self.top = filter.delegate
-            filter.setdelegate(None)
+            self.top = filter.getdelegate()
         else:
             while f.delegate is not filter:
                 assert f is not self.bottom
                 f.resetcache()
-                f = f.delegate
-            f.setdelegate(filter.delegate)
-            filter.setdelegate(None)
+                f = f.getdelegate()
+            f.setdelegate(filter.getdelegate())
+        filter.setdelegate(None)
 
 
+class TkTextPercolator(Percolator):
+    """A specialized Percolator used to wrap a Tk Text widget.
+
+    The Text widget's insert and delete methods are redirected to the top of
+    the Percolator. The bottom-most Delegator has the original insert and
+    delete methods.
+
+    This uses the WidgetRedirector class which allows redirecting Tcl/Tk calls
+    to Python functions/methods.
+
+    """
+    def __init__(self, text):
+        Percolator.__init__(self, text)
+        self.redir = WidgetRedirector(text)
+
+        # Register functions which dynamically retrieve the methods (see below)
+        self.bottom.insert = self.redir.register("insert", self.insert)
+        self.bottom.delete = self.redir.register("delete", self.delete)
+
+    def close(self):
+        self.redir.close(); self.redir = None
+        Percolator.close(self)
+
+    # The following methods ensure proper dynamic attribute resolution. Without
+    # these, adding filters after contsruction would have no effect.
+    #
+    # For instance, for the 'insert' method, this ensures that 'self.insert' is
+    # properly resolved. Without this, the widget's 'insert' method would be
+    # redirected to whatever self.insert is delegated to during construction,
+    # since self.insert is evaluated when __init__ is run.  (likewise for
+    # 'delete')
+    def insert(self, index, chars, tags=None):
+        self.top.insert(index, chars, tags)
+
+    def delete(self, index1, index2=None):
+        self.top.delete(index1, index2)
+
+
 def main():
     class Tracer(Delegator):
         def __init__(self, name):
@@ -63,12 +114,13 @@
         def delete(self, *args):
             print self.name, ": delete", args
             self.delegate.delete(*args)
+    from Tkinter import Tk, Text
     root = Tk()
     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
     text = Text()
     text.pack()
     text.focus_set()
-    p = Percolator(text)
+    p = TkTextPercolator(text)
     t1 = Tracer("t1")
     t2 = Tracer("t2")
     p.insertfilter(t1)
@@ -79,7 +131,7 @@
     p.insertfilter(t2)
     p.removefilter(t1)
     root.mainloop()
+    root.destroy()
 
 if __name__ == "__main__":
-    from Tkinter import *
     main()
Index: UndoDelegator.py
===================================================================
--- UndoDelegator.py	(revision 58404)
+++ UndoDelegator.py	(working copy)
@@ -337,16 +337,17 @@
         return self.depth
 
 def main():
-    from Percolator import Percolator
+    from Percolator import TkTextPercolator
     root = Tk()
     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
     text = Text()
     text.pack()
     text.focus_set()
-    p = Percolator(text)
+    p = TkTextPercolator(text)
     d = UndoDelegator()
     p.insertfilter(d)
     root.mainloop()
+    root.destroy()
 
 if __name__ == "__main__":
     main()
