]> git.saurik.com Git - wxWidgets.git/blobdiff - utils/wxPython/lib/mvctree.py
I think it's needed...
[wxWidgets.git] / utils / wxPython / lib / mvctree.py
index 49eb4b7980c0a9d778cbd0c2761a38bc7c2c6b9f..05ff67ab9602b7e5443fcabd74843ef217fec1d1 100644 (file)
@@ -2,10 +2,8 @@
 wxMVCTree is a control which handles hierarchical data. It is constructed
 in model-view-controller architecture, so the display of that data, and
 the content of the data can be changed greatly without affecting the other parts.
 wxMVCTree is a control which handles hierarchical data. It is constructed
 in model-view-controller architecture, so the display of that data, and
 the content of the data can be changed greatly without affecting the other parts.
-This module contains the wxMVCTree class (the 'controller' of the MVC trio)
-and PathfinderNode, which it uses internally to manage its info.
 
 
-Pathfinder actually is even more configurable than MVC normally implies, because
+wxMVCTree actually is even more configurable than MVC normally implies, because
 almost every aspect of it is pluggable:
     wxMVCTree - Overall controller, and the window that actually gets placed
     in the GUI.
 almost every aspect of it is pluggable:
     wxMVCTree - Overall controller, and the window that actually gets placed
     in the GUI.
@@ -24,7 +22,7 @@ Author/Maintainer - Bryn Keller <xoltar@starship.python.net>
 
 #------------------------------------------------------------------------
 from wxPython.wx import *
 
 #------------------------------------------------------------------------
 from wxPython.wx import *
-import os, sys
+import os, sys, traceback
 #------------------------------------------------------------------------
 
 class MVCTreeNode:
 #------------------------------------------------------------------------
 
 class MVCTreeNode:
@@ -92,7 +90,9 @@ class LayoutEngine:
     """
     def __init__(self, tree):
         self.tree = tree
     """
     def __init__(self, tree):
         self.tree = tree
-    def layout(self, node):
+    def Layout(self, node):
+        raise NotImplementedError
+    def GetNodeList(self):
         raise NotImplementedError
 
 class Transform:
         raise NotImplementedError
 
 class Transform:
@@ -101,7 +101,7 @@ class Transform:
     """
     def __init__(self, tree):
         self.tree = tree
     """
     def __init__(self, tree):
         self.tree = tree
-    def transform(self, node, offset, rotation):
+    def Transform(self, node, offset, rotation):
         """
         This method should only change the projx and projy attributes of
         the node. These represent the position of the node as it should
         """
         This method should only change the projx and projy attributes of
         the node. These represent the position of the node as it should
@@ -110,6 +110,13 @@ class Transform:
         """
         raise NotImplementedError
 
         """
         raise NotImplementedError
 
+    def GetSize(self):
+        """
+        Returns the size of the entire tree as laid out and transformed
+        as a tuple
+        """
+        raise NotImplementedError
+
 class Painter:
     """
     This is the interface that wxMVCTree expects from painters. All painters should
 class Painter:
     """
     This is the interface that wxMVCTree expects from painters. All painters should
@@ -122,9 +129,7 @@ class Painter:
         self.fgcolor = wxNamedColour("BLUE")
         self.linecolor = wxNamedColour("GREY")
         self.font = wxFont(9, wxDEFAULT, wxNORMAL, wxNORMAL, false)
         self.fgcolor = wxNamedColour("BLUE")
         self.linecolor = wxNamedColour("GREY")
         self.font = wxFont(9, wxDEFAULT, wxNORMAL, wxNORMAL, false)
-        self.knobs = []
-        self.rectangles = []
-        self.minx = self.maxx = self.miny = self.maxy = 0
+        self.bmp = None
 
     def GetFont(self):
         return self.font
 
     def GetFont(self):
         return self.font
@@ -132,8 +137,11 @@ class Painter:
     def SetFont(self, font):
         self.font = font
         self.tree.Refresh()
     def SetFont(self, font):
         self.font = font
         self.tree.Refresh()
-
-    def paint(self, dc, node):
+    def GetBuffer(self):
+        return self.bmp
+    def ClearBuffer(self):
+        self.bmp = None
+    def Paint(self, dc, node, doubleBuffered=1, paintBackground=1):
         raise NotImplementedError
     def GetTextColour(self):
         return self.textcolor
         raise NotImplementedError
     def GetTextColour(self):
         return self.textcolor
@@ -177,19 +185,20 @@ class Painter:
         return self.linebrush
     def OnMouse(self, evt):
         if evt.LeftDClick():
         return self.linebrush
     def OnMouse(self, evt):
         if evt.LeftDClick():
+            x, y = self.tree.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
             for item in self.rectangles:
             for item in self.rectangles:
-                if item[1].contains((evt.GetX(), evt.GetY())):
+                if item[1].Contains((x,y)):
                     self.tree.Edit(item[0].data)
                     self.tree.OnNodeClick(item[0], evt)
                     return
         elif evt.ButtonDown():
                     self.tree.Edit(item[0].data)
                     self.tree.OnNodeClick(item[0], evt)
                     return
         elif evt.ButtonDown():
-            #self.oldpos = (evt.GetX(), evt.GetY())
+            x, y = self.tree.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
             for item in self.rectangles:
             for item in self.rectangles:
-                if item[1].contains((evt.GetX(), evt.GetY())):
+                if item[1].Contains((x, y)):
                     self.tree.OnNodeClick(item[0], evt)
                     return
             for item in self.knobs:
                     self.tree.OnNodeClick(item[0], evt)
                     return
             for item in self.knobs:
-                if item[1].contains((evt.GetX(), evt.GetY())):
+                if item[1].Contains((x, y)):
                     self.tree.OnKnobClick(item[0])
                     return
         evt.Skip()
                     self.tree.OnKnobClick(item[0])
                     return
         evt.Skip()
@@ -233,7 +242,7 @@ class NodePainter:
     """
     def __init__(self, painter):
         self.painter = painter
     """
     def __init__(self, painter):
         self.painter = painter
-    def paint(self, node, dc, location = None):
+    def Paint(self, node, dc, location = None):
         """
         location should be provided only to draw in an unusual position
         (not the node's normal position), otherwise the node's projected x and y
         """
         location should be provided only to draw in an unusual position
         (not the node's normal position), otherwise the node's projected x and y
@@ -247,7 +256,7 @@ class LinePainter:
     """
     def __init__(self, painter):
         self.painter = painter
     """
     def __init__(self, painter):
         self.painter = painter
-    def paint(self, parent, child, dc):
+    def Paint(self, parent, child, dc):
         raise NotImplementedError
 
 class TextConverter:
         raise NotImplementedError
 
 class TextConverter:
@@ -256,7 +265,7 @@ class TextConverter:
     """
     def __init__(self, painter):
         self.painter = painter
     """
     def __init__(self, painter):
         self.painter = painter
-    def convert(node):
+    def Convert(node):
         """
         Should return a string. The node argument will be an
         MVCTreeNode.
         """
         Should return a string. The node argument will be an
         MVCTreeNode.
@@ -331,7 +340,8 @@ class FileEditor(Editor):
         self.editcomp.SetSelection(0, len(node.fileName))
         self.editcomp.SetFocus()
         self.treenode = treenode
         self.editcomp.SetSelection(0, len(node.fileName))
         self.editcomp.SetFocus()
         self.treenode = treenode
-        EVT_KEY_DOWN(self.editcomp, self._key)
+#        EVT_KEY_DOWN(self.editcomp, self._key)
+        EVT_KEY_UP(self.editcomp, self._key)
         EVT_LEFT_DOWN(self.editcomp, self._mdown)
         self.editcomp.CaptureMouse()
 
         EVT_LEFT_DOWN(self.editcomp, self._mdown)
         self.editcomp.CaptureMouse()
 
@@ -347,10 +357,11 @@ class FileEditor(Editor):
                 os.rename(node.path + os.sep + node.fileName, node.path + os.sep + self.editcomp.GetValue())
                 node.fileName = self.editcomp.GetValue()
             except:
                 os.rename(node.path + os.sep + node.fileName, node.path + os.sep + self.editcomp.GetValue())
                 node.fileName = self.editcomp.GetValue()
             except:
-                import traceback;traceback.print_exc()
+                traceback.print_exc()
         self.editcomp.ReleaseMouse()
         self.editcomp.Destroy()
         del self.editcomp
         self.editcomp.ReleaseMouse()
         self.editcomp.Destroy()
         del self.editcomp
+        self.tree.Refresh()
 
 
     def _key(self, evt):
 
 
     def _key(self, evt):
@@ -364,7 +375,6 @@ class FileEditor(Editor):
     def _mdown(self, evt):
         if evt.IsButton():
             pos = evt.GetPosition()
     def _mdown(self, evt):
         if evt.IsButton():
             pos = evt.GetPosition()
-            print pos.x, pos.y
             edsize = self.editcomp.GetSize()
             if pos.x < 0 or pos.y < 0 or pos.x > edsize.width or pos.y > edsize.height:
                 self.EndEdit(false)
             edsize = self.editcomp.GetSize()
             if pos.x < 0 or pos.y < 0 or pos.x > edsize.width or pos.y > edsize.height:
                 self.EndEdit(false)
@@ -416,7 +426,6 @@ class LateFSTreeModel(FSTreeModel):
         import string
         name = string.split(path, os.sep)[-1]
         pathpart = path[:-len(name)]
         import string
         name = string.split(path, os.sep)[-1]
         pathpart = path[:-len(name)]
-        print pathpart
         fw = FileWrapper(pathpart, name)
         self._Build(path, fw)
         self.SetRoot(fw)
         fw = FileWrapper(pathpart, name)
         self._Build(path, fw)
         self.SetRoot(fw)
@@ -441,15 +450,23 @@ class LateFSTreeModel(FSTreeModel):
         return not os.path.isdir(node.path + os.sep + node.fileName)
 
 class StrTextConverter(TextConverter):
         return not os.path.isdir(node.path + os.sep + node.fileName)
 
 class StrTextConverter(TextConverter):
-    def convert(self, node):
+    def Convert(self, node):
         return str(node.data)
 
 class NullTransform(Transform):
         return str(node.data)
 
 class NullTransform(Transform):
-    def transform(self, node, offset, rotation):
-        node.projx = node.x + offset[0]
-        node.projy = node.y + offset[1]
-        for kid in node.kids:
-            self.transform(kid, offset, rotation)
+    def GetSize(self):
+        return tuple(self.size)
+
+    def Transform(self, node, offset, rotation):
+        self.size = [0,0]
+        list = self.tree.GetLayoutEngine().GetNodeList()
+        for node in list:
+            node.projx = node.x + offset[0]
+            node.projy = node.y + offset[1]
+            if node.projx > self.size[0]:
+                self.size[0] = node.projx
+            if node.projy > self.size[1]:
+                self.size[1] = node.projy
 
 class Rect:
     def __init__(self, x, y, width, height):
 
 class Rect:
     def __init__(self, x, y, width, height):
@@ -464,7 +481,7 @@ class Rect:
         name = ['x', 'y', 'width', 'height'][index]
         setattr(self, name, value)
 
         name = ['x', 'y', 'width', 'height'][index]
         setattr(self, name, value)
 
-    def contains(self, other):
+    def Contains(self, other):
         if type(other) == type(()):
             other = Rect(other[0], other[1], 0, 0)
         if other.x >= self.x:
         if type(other) == type(()):
             other = Rect(other[0], other[1], 0, 0)
         if other.x >= self.x:
@@ -485,17 +502,28 @@ class TreeLayout(LayoutEngine):
         LayoutEngine.__init__(self, tree)
         self.NODE_STEP = 20
         self.NODE_HEIGHT = 20
         LayoutEngine.__init__(self, tree)
         self.NODE_STEP = 20
         self.NODE_HEIGHT = 20
-    def layout(self, node):
+        self.nodelist = []
+
+    def Layout(self, node):
+        self.nodelist = []
+        self.NODE_HEIGHT = self.tree.GetFont().GetPointSize() * 2
+        self.layoutwalk(node)
+
+    def GetNodeList(self):
+        return self.nodelist
+
+    def layoutwalk(self, node):
         if node == self.tree.currentRoot:
             node.level = 1
             self.lastY = (-self.NODE_HEIGHT)
         node.x = self.NODE_STEP * node.level
         node.y = self.lastY + self.NODE_HEIGHT
         self.lastY = node.y
         if node == self.tree.currentRoot:
             node.level = 1
             self.lastY = (-self.NODE_HEIGHT)
         node.x = self.NODE_STEP * node.level
         node.y = self.lastY + self.NODE_HEIGHT
         self.lastY = node.y
+        self.nodelist.append(node)
         if node.expanded:
             for kid in node.kids:
                 kid.level = node.level + 1
         if node.expanded:
             for kid in node.kids:
                 kid.level = node.level + 1
-                self.layout(kid)
+                self.layoutwalk(kid)
 
 class TreePainter(Painter):
     """
 
 class TreePainter(Painter):
     """
@@ -515,7 +543,7 @@ class TreePainter(Painter):
         self.textConverter = textConverter
         self.charWidths = []
 
         self.textConverter = textConverter
         self.charWidths = []
 
-    def paint(self, dc, node):
+    def Paint(self, dc, node, doubleBuffered=1, paintBackground=1):
         if not self.charWidths:
             self.charWidths = []
             for i in range(25):
         if not self.charWidths:
             self.charWidths = []
             for i in range(25):
@@ -530,15 +558,41 @@ class TreePainter(Painter):
         self.fgbrush = wxBrush(self.GetForegroundColour(), wxSOLID)
         self.bgbrush = wxBrush(self.GetBackgroundColour(), wxSOLID)
         self.linebrush = wxPen(self.GetLineColour(), 1, wxSOLID)
         self.fgbrush = wxBrush(self.GetForegroundColour(), wxSOLID)
         self.bgbrush = wxBrush(self.GetBackgroundColour(), wxSOLID)
         self.linebrush = wxPen(self.GetLineColour(), 1, wxSOLID)
-        self.rectangles = []
-        self.knobs = []
+        treesize = self.tree.GetSize()
+        size = self.tree.transform.GetSize()
+        size = (max(treesize.width, size[0]+50), max(treesize.height, size[1]+50))
         dc.BeginDrawing()
         dc.BeginDrawing()
-        dc.SetPen(self.GetBackgroundPen())
-        dc.SetBrush(self.GetBackgroundBrush())
-        size = self.tree.GetSize()
-        dc.DrawRectangle(0, 0, size.width, size.height)
-        if node:
-            self.paintWalk(node, dc)
+        if doubleBuffered:
+            mem_dc = wxMemoryDC()
+            if not self.GetBuffer():
+                self.knobs = []
+                self.rectangles = []
+                self.bmp = wxEmptyBitmap(size[0], size[1])
+                mem_dc.SelectObject(self.GetBuffer())
+                mem_dc.SetPen(self.GetBackgroundPen())
+                mem_dc.SetBrush(self.GetBackgroundBrush())
+                mem_dc.DrawRectangle(0, 0, size[0], size[1])
+                mem_dc.SetFont(self.tree.GetFont())
+                self.paintWalk(node, mem_dc)
+            else:
+                mem_dc.SelectObject(self.GetBuffer())
+            xstart, ystart = self.tree.CalcUnscrolledPosition(0,0)
+            size = self.tree.GetClientSizeTuple()
+            dc.Blit(xstart, ystart, size[0], size[1], mem_dc, xstart, ystart)
+        else:
+            if node == self.tree.currentRoot:
+                self.knobs = []
+                self.rectangles = []
+            dc.SetPen(self.GetBackgroundPen())
+            dc.SetBrush(self.GetBackgroundBrush())
+            dc.SetFont(self.tree.GetFont())
+            if paintBackground:
+                dc.DrawRectangle(0, 0, size[0], size[1])
+            if node:
+                #Call with not paintBackground because if we are told not to paint the
+                #whole background, we have to paint in parts to undo selection coloring.
+                pb = paintBackground
+                self.paintWalk(node, dc, not pb)
         dc.EndDrawing()
 
     def GetDashPen(self):
         dc.EndDrawing()
 
     def GetDashPen(self):
@@ -548,71 +602,68 @@ class TreePainter(Painter):
         Painter.SetLinePen(self, pen)
         self.dashpen = wxPen(pen.GetColour(), 1, wxDOT)
 
         Painter.SetLinePen(self, pen)
         self.dashpen = wxPen(pen.GetColour(), 1, wxDOT)
 
-    def drawBox(self, px, py, node, dc):
-        if self.tree.model.IsLeaf(node.data) or ((node.expanded or not self.tree._assumeChildren) and not len(node.kids)):
-            return
-        dc.SetPen(self.linepen)
-        dc.SetBrush(self.bgbrush)
-        dc.DrawRectangle(px -4, py-4, 9, 9)
-        self.knobs.append(node, Rect(px -4, py -4, 9, 9))
-        dc.SetPen(self.textpen)
-        if not node.expanded:
-            dc.DrawLine(px, py -2, px, py + 3)
-        dc.DrawLine(px -2, py, px + 3, py)
-
-    def paintWalk(self, node, dc):
-        self.linePainter.paint(node.parent, node, dc)
-        self.nodePainter.paint(node, dc)
+    def paintWalk(self, node, dc, paintRects=0):
+        self.linePainter.Paint(node.parent, node, dc)
+        self.nodePainter.Paint(node, dc, drawRects = paintRects)
         if node.expanded:
             for kid in node.kids:
         if node.expanded:
             for kid in node.kids:
-                if not self.paintWalk(kid, dc):
+                if not self.paintWalk(kid, dc, paintRects):
                     return false
             for kid in node.kids:
                 px = (kid.projx - self.tree.layout.NODE_STEP) + 5
                 py = kid.projy + kid.height/2
                     return false
             for kid in node.kids:
                 px = (kid.projx - self.tree.layout.NODE_STEP) + 5
                 py = kid.projy + kid.height/2
-                self.drawBox(px, py, kid, dc)
+                if (not self.tree.model.IsLeaf(kid.data)) or ((kid.expanded or self.tree._assumeChildren) and len(kid.kids)):
+                    dc.SetPen(self.linepen)
+                    dc.SetBrush(self.bgbrush)
+                    dc.DrawRectangle(px -4, py-4, 9, 9)
+                    self.knobs.append(kid, Rect(px -4, py -4, 9, 9))
+                    dc.SetPen(self.textpen)
+                    if not kid.expanded:
+                        dc.DrawLine(px, py -2, px, py + 3)
+                    dc.DrawLine(px -2, py, px + 3, py)
         if node == self.tree.currentRoot:
             px = (node.projx - self.tree.layout.NODE_STEP) + 5
             py = node.projy + node.height/2
         if node == self.tree.currentRoot:
             px = (node.projx - self.tree.layout.NODE_STEP) + 5
             py = node.projy + node.height/2
-            self.drawBox(px, py, node, dc)
+            dc.SetPen(self.linepen)
+            dc.SetBrush(self.bgbrush)
+            dc.DrawRectangle(px -4, py-4, 9, 9)
+            self.knobs.append(node, Rect(px -4, py -4, 9, 9))
+            dc.SetPen(self.textpen)
+            if not node.expanded:
+                dc.DrawLine(px, py -2, px, py + 3)
+            dc.DrawLine(px -2, py, px + 3, py)
         return true
 
     def OnMouse(self, evt):
         Painter.OnMouse(self, evt)
 
 class TreeNodePainter(NodePainter):
         return true
 
     def OnMouse(self, evt):
         Painter.OnMouse(self, evt)
 
 class TreeNodePainter(NodePainter):
-    def paint(self, node, dc, location = None):
-        text = self.painter.textConverter.convert(node)
+    def Paint(self, node, dc, location = None, drawRects = 0):
+        text = self.painter.textConverter.Convert(node)
         extent = dc.GetTextExtent(text)
         node.width = extent[0]
         node.height = extent[1]
         extent = dc.GetTextExtent(text)
         node.width = extent[0]
         node.height = extent[1]
-        if node == self.painter.tree.currentRoot:
-            self.painter.minx = self.painter.maxx = self.painter.miny = self.painter.maxy = 0
-        if node.projx < self.painter.minx:
-            self.painter.minx = node.projx
-        elif node.projx + node.width > self.painter.maxx:
-            self.painter.maxx = node.projx + node.width
-        if node.projy < self.painter.miny:
-            self.painter.miny = node.projy
-        elif node.projy + node.height > self.painter.maxy:
-            self.painter.maxy = node.projy + node.height
         if node.selected:
             dc.SetPen(self.painter.GetLinePen())
             dc.SetBrush(self.painter.GetForegroundBrush())
             dc.SetTextForeground(wxNamedColour("WHITE"))
             dc.DrawRectangle(node.projx -1, node.projy -1, node.width + 3, node.height + 3)
         else:
         if node.selected:
             dc.SetPen(self.painter.GetLinePen())
             dc.SetBrush(self.painter.GetForegroundBrush())
             dc.SetTextForeground(wxNamedColour("WHITE"))
             dc.DrawRectangle(node.projx -1, node.projy -1, node.width + 3, node.height + 3)
         else:
+            if drawRects:
+                dc.SetBrush(self.painter.GetBackgroundBrush())
+                dc.SetPen(self.painter.GetBackgroundPen())
+                dc.DrawRectangle(node.projx -1, node.projy -1, node.width + 3, node.height + 3)
             dc.SetTextForeground(self.painter.GetTextColour())
         dc.DrawText(text, node.projx, node.projy)
         self.painter.rectangles.append((node, Rect(node.projx, node.projy, node.width, node.height)))
 
 class TreeLinePainter(LinePainter):
             dc.SetTextForeground(self.painter.GetTextColour())
         dc.DrawText(text, node.projx, node.projy)
         self.painter.rectangles.append((node, Rect(node.projx, node.projy, node.width, node.height)))
 
 class TreeLinePainter(LinePainter):
-    def paint(self, parent, child, dc):
+    def Paint(self, parent, child, dc):
         dc.SetPen(self.painter.GetDashPen())
         px = py = cx = cy = 0
         if parent is None or child == self.painter.tree.currentRoot:
             px = (child.projx - self.painter.tree.layout.NODE_STEP) + 5
         dc.SetPen(self.painter.GetDashPen())
         px = py = cx = cy = 0
         if parent is None or child == self.painter.tree.currentRoot:
             px = (child.projx - self.painter.tree.layout.NODE_STEP) + 5
-            py = child.projy + self.painter.tree.layout.NODE_HEIGHT/2
+            py = child.projy + self.painter.tree.layout.NODE_HEIGHT/2 -2
             cx = child.projx
             cy = py
             dc.DrawLine(px, py, cx, cy)
             cx = child.projx
             cy = py
             dc.DrawLine(px, py, cx, cy)
@@ -620,7 +671,7 @@ class TreeLinePainter(LinePainter):
             px = parent.projx + 5
             py = parent.projy + parent.height
             cx = child.projx -5
             px = parent.projx + 5
             py = parent.projy + parent.height
             cx = child.projx -5
-            cy = child.projy + self.painter.tree.layout.NODE_HEIGHT/2
+            cy = child.projy + self.painter.tree.layout.NODE_HEIGHT/2 -3
             dc.DrawLine(px, py, px, cy)
             dc.DrawLine(px, cy, cx, cy)
 
             dc.DrawLine(px, py, px, cy)
             dc.DrawLine(px, cy, cx, cy)
 
@@ -671,27 +722,35 @@ class wxMVCTreeEvent(wxPyCommandEvent):
         self.node = node
         self.nodes = nodes
         self.keyEvent = keyEvent
         self.node = node
         self.nodes = nodes
         self.keyEvent = keyEvent
-
+    def GetNode(self):
+        return self.node
+    def GetNodes(self):
+        return self.nodes
+    def getKeyEvent(self):
+        return self.keyEvent
 
 class wxMVCTreeNotifyEvent(wxMVCTreeEvent):
     def __init__(self, type, id, node = None, nodes = None, **kwargs):
         apply(wxMVCTreeEvent.__init__, (self, type, id), kwargs)
         self.notify = wxNotifyEvent(type, id)
 
 class wxMVCTreeNotifyEvent(wxMVCTreeEvent):
     def __init__(self, type, id, node = None, nodes = None, **kwargs):
         apply(wxMVCTreeEvent.__init__, (self, type, id), kwargs)
         self.notify = wxNotifyEvent(type, id)
+    def getNotifyEvent(self):
+        return self.notify
 
 
-class wxMVCTree(wxWindow):
+class wxMVCTree(wxScrolledWindow):
     """
     """
-    The main mvcTree class.
+    The main mvc tree class.
     """
     def __init__(self, parent, id, model = None, layout = None, transform = None,
                  painter = None, *args, **kwargs):
     """
     def __init__(self, parent, id, model = None, layout = None, transform = None,
                  painter = None, *args, **kwargs):
-        apply(wxWindow.__init__, (self, parent, id), kwargs)
+        apply(wxScrolledWindow.__init__, (self, parent, id), kwargs)
         self.nodemap = {}
         self._multiselect = false
         self._selections = []
         self._assumeChildren = false
         self._scrollx = false
         self._scrolly = false
         self.nodemap = {}
         self._multiselect = false
         self._selections = []
         self._assumeChildren = false
         self._scrollx = false
         self._scrolly = false
-        self.doubleBuffered = true
+        self.doubleBuffered = false
+        self._lastPhysicalSize = self.GetSize()
         self._editors = []
         if not model:
             model = BasicTreeModel()
         self._editors = []
         if not model:
             model = BasicTreeModel()
@@ -708,8 +767,22 @@ class wxMVCTree(wxWindow):
         self.painter = painter
         self.SetFont(wxFont(9, wxDEFAULT, wxNORMAL, wxNORMAL, false))
         EVT_MOUSE_EVENTS(self, self.OnMouse)
         self.painter = painter
         self.SetFont(wxFont(9, wxDEFAULT, wxNORMAL, wxNORMAL, false))
         EVT_MOUSE_EVENTS(self, self.OnMouse)
-        EVT_SCROLLWIN(self, self.OnScroll)
         EVT_KEY_DOWN(self, self.OnKeyDown)
         EVT_KEY_DOWN(self, self.OnKeyDown)
+        self.doubleBuffered = true
+
+    def Refresh(self):
+        if self.doubleBuffered:
+            self.painter.ClearBuffer()
+        wxScrolledWindow.Refresh(self, false)
+
+    def GetPainter(self):
+        return self.painter
+
+    def GetLayoutEngine(self):
+        return self.layout
+
+    def GetTransform(self):
+        return self.transform
 
     def __repr__(self):
         return "<wxMVCTree instance at %s>" % str(hex(id(self)))
 
     def __repr__(self):
         return "<wxMVCTree instance at %s>" % str(hex(id(self)))
@@ -720,15 +793,16 @@ class wxMVCTree(wxWindow):
     def NodeAdded(self, parent, child):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_ADD_ITEM, self.GetId(), node = child, nodes = [parent, child])
         self.GetEventHandler().ProcessEvent(e)
     def NodeAdded(self, parent, child):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_ADD_ITEM, self.GetId(), node = child, nodes = [parent, child])
         self.GetEventHandler().ProcessEvent(e)
+        self.painter.ClearBuffer()
 
     def NodeInserted(self, parent, child, index):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_ADD_ITEM, self.GetId(), node = child, nodes = [parent, child])
         self.GetEventHandler().ProcessEvent(e)
 
     def NodeInserted(self, parent, child, index):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_ADD_ITEM, self.GetId(), node = child, nodes = [parent, child])
         self.GetEventHandler().ProcessEvent(e)
-
+        self.painter.ClearBuffer()
     def NodeRemoved(self, node):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_DELETE_ITEM, self.GetId(), node = child, nodes = [parent, child])
         self.GetEventHandler().ProcessEvent(e)
     def NodeRemoved(self, node):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_DELETE_ITEM, self.GetId(), node = child, nodes = [parent, child])
         self.GetEventHandler().ProcessEvent(e)
-
+        self.painter.ClearBuffer()
     def OnKeyDown(self, evt):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_KEY_DOWN, self.GetId(), keyEvent = evt)
         self.GetEventHandler().ProcessEvent(e)
     def OnKeyDown(self, evt):
         e = wxMVCTreeEvent(wxEVT_MVCTREE_KEY_DOWN, self.GetId(), keyEvent = evt)
         self.GetEventHandler().ProcessEvent(e)
@@ -738,7 +812,7 @@ class wxMVCTree(wxWindow):
         dc = wxClientDC(self)
         dc.SetFont(font)
         self.layout.SetHeight(dc.GetTextExtent("")[1] + 18)
         dc = wxClientDC(self)
         dc.SetFont(font)
         self.layout.SetHeight(dc.GetTextExtent("")[1] + 18)
-
+        self.painter.ClearBuffer()
     def GetFont(self):
         return self.painter.GetFont()
 
     def GetFont(self):
         return self.painter.GetFont()
 
@@ -752,18 +826,17 @@ class wxMVCTree(wxWindow):
         self.painter.OnMouse(evt)
 
     def OnNodeClick(self, node, mouseEvent):
         self.painter.OnMouse(evt)
 
     def OnNodeClick(self, node, mouseEvent):
-        if node.selected:
+        if node.selected and (self.IsMultiSelect() and mouseEvent.ControlDown()):
             self.RemoveFromSelection(node.data)
         else:
             self.RemoveFromSelection(node.data)
         else:
-            self.AddToSelection(node.data, mouseEvent.ControlDown())
-        self.Refresh()
+            self.AddToSelection(node.data, mouseEvent.ControlDown(), mouseEvent.ShiftDown())
 
     def OnKnobClick(self, node):
         self.SetExpanded(node.data, not node.expanded)
 
     def GetDisplayText(self, node):
         treenode = self.nodemap[node]
 
     def OnKnobClick(self, node):
         self.SetExpanded(node.data, not node.expanded)
 
     def GetDisplayText(self, node):
         treenode = self.nodemap[node]
-        return self.painter.textConverter.convert(treenode)
+        return self.painter.textConverter.Convert(treenode)
 
     def IsDoubleBuffered(self):
         return self.doubleBuffered
 
     def IsDoubleBuffered(self):
         return self.doubleBuffered
@@ -793,6 +866,7 @@ class wxMVCTree(wxWindow):
         self.currentRoot = self.layoutRoot
         self.offset = [0,0]
         self.rotation = 0
         self.currentRoot = self.layoutRoot
         self.offset = [0,0]
         self.rotation = 0
+        self._scrollset = None
         self.Refresh()
 
     def GetCurrentRoot(self):
         self.Refresh()
 
     def GetCurrentRoot(self):
@@ -817,12 +891,11 @@ class wxMVCTree(wxWindow):
         pass
 
     def OnSize(self, evt):
         pass
 
     def OnSize(self, evt):
-        try:
-            size = self.GetSizeTuple()
-            self.center = (size[0]/2, size[1]/2)
-            del self.bmp
-        except:
-            pass
+        size = self.GetSize()
+        self.center = (size.width/2, size.height/2)
+        if self._lastPhysicalSize.width < size.width or self._lastPhysicalSize.height < size.height:
+            self.painter.ClearBuffer()
+        self._lastPhysicalSize = size
 
     def GetSelection(self):
         "Returns a tuple of selected nodes."
 
     def GetSelection(self):
         "Returns a tuple of selected nodes."
@@ -902,14 +975,14 @@ class wxMVCTree(wxWindow):
         else:
             e = wxMVCTreeEvent(wxEVT_MVCTREE_ITEM_COLLAPSED, self.GetId(), node)
         self.GetEventHandler().ProcessEvent(e)
         else:
             e = wxMVCTreeEvent(wxEVT_MVCTREE_ITEM_COLLAPSED, self.GetId(), node)
         self.GetEventHandler().ProcessEvent(e)
-        self.layout.layout(self.currentRoot)
-        self.transform.transform(self.currentRoot, self.offset, self.rotation)
+        self.layout.Layout(self.currentRoot)
+        self.transform.Transform(self.currentRoot, self.offset, self.rotation)
         self.Refresh()
 
     def IsExpanded(self, node):
         return self.nodemap[node].expanded
 
         self.Refresh()
 
     def IsExpanded(self, node):
         return self.nodemap[node].expanded
 
-    def AddToSelection(self, nodeOrTuple, enableMulti = true):
+    def AddToSelection(self, nodeOrTuple, enableMulti = true, shiftMulti = false):
         nodeTuple = nodeOrTuple
         if type(nodeOrTuple)!= type(()):
             nodeTuple = (nodeOrTuple,)
         nodeTuple = nodeOrTuple
         if type(nodeOrTuple)!= type(()):
             nodeTuple = (nodeOrTuple,)
@@ -917,38 +990,69 @@ class wxMVCTree(wxWindow):
         self.GetEventHandler().ProcessEvent(e)
         if not e.notify.IsAllowed():
             return
         self.GetEventHandler().ProcessEvent(e)
         if not e.notify.IsAllowed():
             return
-        if not self.IsMultiSelect() or not enableMulti:
+        changeparents = []
+        if not (self.IsMultiSelect() and (enableMulti or shiftMulti)):
             for node in self._selections:
                 treenode = self.nodemap[node]
                 treenode.selected = false
             for node in self._selections:
                 treenode = self.nodemap[node]
                 treenode.selected = false
+                changeparents.append(treenode)
             node = nodeTuple[0]
             self._selections = [node]
             treenode = self.nodemap[node]
             node = nodeTuple[0]
             self._selections = [node]
             treenode = self.nodemap[node]
+            changeparents.append(treenode)
             treenode.selected = true
         else:
             treenode.selected = true
         else:
-            for node in nodeTuple:
-                try:
-                    self._selections.index(node)
-                except ValueError:
-                    self._selections.append(node)
+            if shiftMulti:
+                for node in nodeTuple:
                     treenode = self.nodemap[node]
                     treenode = self.nodemap[node]
-                    treenode.selected = true
+                    oldtreenode = self.nodemap[self._selections[0]]
+                    if treenode.parent == oldtreenode.parent:
+                        found = 0
+                        for kid in oldtreenode.parent.kids:
+                            if kid == treenode or kid == oldtreenode:
+                                found = not found
+                                kid.selected = true
+                                self._selections.append(kid.data)
+                                changeparents.append(kid)
+                            elif found:
+                                kid.selected = true
+                                self._selections.append(kid.data)
+                                changeparents.append(kid)
+            else:
+                for node in nodeTuple:
+                    try:
+                        self._selections.index(node)
+                    except ValueError:
+                        self._selections.append(node)
+                        treenode = self.nodemap[node]
+                        treenode.selected = true
+                        changeparents.append(treenode)
         e = wxMVCTreeEvent(wxEVT_MVCTREE_SEL_CHANGED, self.GetId(), nodeTuple[0], nodes = nodeTuple)
         self.GetEventHandler().ProcessEvent(e)
         e = wxMVCTreeEvent(wxEVT_MVCTREE_SEL_CHANGED, self.GetId(), nodeTuple[0], nodes = nodeTuple)
         self.GetEventHandler().ProcessEvent(e)
+        dc = wxClientDC(self)
+        self.PrepareDC(dc)
+        for node in changeparents:
+            if node:
+                self.painter.Paint(dc, node, doubleBuffered = 0, paintBackground = 0)
+        self.painter.ClearBuffer()
 
     def RemoveFromSelection(self, nodeTuple):
         if type(nodeTuple) != type(()):
             nodeTuple = (nodeTuple,)
 
     def RemoveFromSelection(self, nodeTuple):
         if type(nodeTuple) != type(()):
             nodeTuple = (nodeTuple,)
+        changeparents = []
         for node in nodeTuple:
         for node in nodeTuple:
-            try:
-                self._selections.index(node)
-            except IndexError:
-                self._selections.remove(node)
-                treenode = self.nodemap[node]
-                node.selected = false
+            self._selections.remove(node)
+            treenode = self.nodemap[node]
+            changeparents.append(treenode)
+            treenode.selected = false
         e = wxMVCTreeEvent(wxEVT_MVCTREE_SEL_CHANGED, self.GetId(), node, nodes = nodeTuple)
         self.GetEventHandler().ProcessEvent(e)
         e = wxMVCTreeEvent(wxEVT_MVCTREE_SEL_CHANGED, self.GetId(), node, nodes = nodeTuple)
         self.GetEventHandler().ProcessEvent(e)
-
+        dc = wxClientDC(self)
+        self.PrepareDC(dc)
+        for node in changeparents:
+            if node:
+                self.painter.Paint(dc, node, doubleBuffered = 0, paintBackground = 0)
+        self.painter.ClearBuffer()
 
 
     def GetBackgroundColour(self):
 
 
     def GetBackgroundColour(self):
@@ -978,126 +1082,39 @@ class wxMVCTree(wxWindow):
     def GetAssumeChildren(self):
         return self._assumeChildren
 
     def GetAssumeChildren(self):
         return self._assumeChildren
 
-    def OnScroll(self, evt):
-        type = evt.GetEventType()
-        field = [self.painter.maxx - self.painter.minx, self.painter.maxy - self.painter.miny]
-        size = self.GetSizeTuple()
-        index = 1
-        if evt.GetOrientation() == wxHORIZONTAL:
-            index = 0
-            self._scrollx = true
-        else:
-            self._scrolly = true
-            index = 1
-        if type ==  wxEVT_SCROLLWIN_TOP:
-            self.offset[index] = 0
-        elif type == wxEVT_SCROLLWIN_LINEUP:
-            self.offset[index] = self.offset[index] + 1
-        elif type == wxEVT_SCROLLWIN_LINEDOWN:
-            self.offset[index] = self.offset[index] - 1
-        elif type == wxEVT_SCROLLWIN_PAGEUP:
-            self.offset[index] = self.offset[index] + int(20 * float(field[index])/float(size[index]))
-        elif type == wxEVT_SCROLLWIN_PAGEDOWN:
-            self.offset[index] = self.offset[index] - int(20 * float(field[index])/float(size[index]))
-        elif type == wxEVT_SCROLLWIN_THUMBTRACK:
-            self.offset[index] = -(evt.GetPosition())
-        elif type == wxEVT_SCROLLWIN_BOTTOM:
-            self.offset[index] = field[index]
-        self.transformed = false
-        self.Refresh()
-
     def OnPaint(self, evt):
         """
         Ensures that the tree has been laid out and transformed, then calls the painter
         to paint the control.
         """
         try:
     def OnPaint(self, evt):
         """
         Ensures that the tree has been laid out and transformed, then calls the painter
         to paint the control.
         """
         try:
+            self.EnableScrolling(false, false)
             if not self.laidOut:
             if not self.laidOut:
-                self.layout.layout(self.currentRoot)
+                self.layout.Layout(self.currentRoot)
                 self.laidOut = true
                 self.laidOut = true
+                self.transformed = false
             if not self.transformed:
             if not self.transformed:
-                self.transform.transform(self.currentRoot, self.offset, self.rotation)
+                self.transform.Transform(self.currentRoot, self.offset, self.rotation)
                 self.transformed = true
                 self.transformed = true
+            tsize = None
+            tsize = list(self.transform.GetSize())
+            tsize[0] = tsize[0] + 50
+            tsize[1] = tsize[1] + 50
+            size = self.GetSizeTuple()
+            if tsize[0] > size[0] or tsize[1] > size[1]:
+                if not hasattr(self, '_oldsize') or (tsize[0] > self._oldsize[0] or tsize[1] > self._oldsize[1]):
+                    self._oldsize = tsize
+                    oldstart = self.ViewStart()
+                    self._lastPhysicalSize = self.GetSize()
+                    self.SetScrollbars(10, 10, tsize[0]/10, tsize[1]/10)
+                    self.Scroll(oldstart[0], oldstart[1])
             dc = wxPaintDC(self)
             dc = wxPaintDC(self)
+            self.PrepareDC(dc)
             dc.SetFont(self.GetFont())
             dc.SetFont(self.GetFont())
-            if self.doubleBuffered:
-                size = self.GetSize()
-                if not hasattr(self, 'bmp'):
-                    self.bmp = bmp =wxEmptyBitmap(size.width, size.height)
-                else:
-                    bmp = self.bmp
-                mem_dc = wxMemoryDC()
-                mem_dc.SetFont(self.GetFont())
-                mem_dc.SelectObject(bmp)
-                self.painter.paint(mem_dc, self.currentRoot)
-                dc.Blit(0, 0, size.width, size.height, mem_dc, 0, 0);
-            else:
-                self.painter.paint(dc, self.currentRoot)
-            size = self.GetSizeTuple()
-            if self._scrollx or self.painter.minx < 0 or self.painter.maxx > size[0]:
-                field = self.painter.maxx - self.painter.minx
-                self.SetScrollbar(wxHORIZONTAL, -self.offset[0], size[0]/field, field, true)
-                self._scrollx = false
-            if self._scrolly or self.painter.miny < 0 or self.painter.maxy > size[1]:
-                field = self.painter.maxy - self.painter.miny
-                self.SetScrollbar(wxVERTICAL, -self.offset[1], size[1]/field, field, true)
-                self._scrolly = false
+            self.painter.Paint(dc, self.currentRoot, self.doubleBuffered)
         except:
         except:
-            import traceback;traceback.print_exc()
-
-if __name__ == '__main__':
-    def exit(evt):
-        import sys;sys.exit()
-
-    block = 0
-
-    def selchanging(evt):
-        print "SelChanging!"
-        print evt.node
-        global block
-        if block:
-            evt.notify.Veto()
-        block = not block
-
-    def selchanged(evt):
-        print "SelChange!"
-        print evt.node
-    def expanded(evt):
-        print "Expanded!"
-    def closed(evt):
-        print "Closed!"
-    def key(evt):
-        print "Key"
-    def add(evt):
-        print "Add"
-    def delitem(evt):
-        print "Delete"
-
-    class MyApp(wxApp):
-        def OnInit(self):
-            f = wxFrame(NULL, -1, "wxMVCTree")
-            p = None
-            p = wxMVCTree(f, -1)
-            p.SetAssumeChildren(true)
-            if len(sys.argv) > 1:
-                p.SetModel(LateFSTreeModel(sys.argv[1]))
-                p.AddEditor(FileEditor(p))
-            p.SetMultiSelect(true)
-            f.Show(true)
-            EVT_CLOSE(f, exit)
-            EVT_MVCTREE_SEL_CHANGED(p, p.GetId(), selchanged)
-            EVT_MVCTREE_SEL_CHANGING(p, p.GetId(), selchanging)
-            EVT_MVCTREE_ITEM_EXPANDED(p, p.GetId(), expanded)
-            EVT_MVCTREE_ITEM_COLLAPSED(p, p.GetId(), closed)
-            EVT_MVCTREE_ADD_ITEM(p, p.GetId(), add)
-            EVT_MVCTREE_DELETE_ITEM(p, p.GetId(), delitem)
-            EVT_MVCTREE_KEY_DOWN(p, p.GetId(), key)
-            p.SetForegroundColour(wxNamedColour("GREEN"))
-            self.SetTopWindow(f)
-            return true
-
-    app = MyApp(false)
-    app.MainLoop()
+            traceback.print_exc()
+