New stack viewer, uses a tree widget.
(XXX: the debugger doesn't yet use this.)
diff --git a/Tools/idle/StackViewer.py b/Tools/idle/StackViewer.py
index 2fa4127..5b3c87a 100644
--- a/Tools/idle/StackViewer.py
+++ b/Tools/idle/StackViewer.py
@@ -1,177 +1,106 @@
 import string
-import sys
-import os
 from Tkinter import *
 import linecache
-from repr import Repr
-from WindowList import ListedToplevel
 
-from ScrolledList import ScrolledList
+from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
+from ObjectBrowser import ObjectTreeItem, make_objecttreeitem
+from OldStackViewer import StackViewer, NamespaceViewer
 
+def StackBrowser(root, flist=None, stack=None):
+    top = Toplevel(root)
+    sc = ScrolledCanvas(top, bg="white", highlightthickness=0)
+    sc.frame.pack(expand=1, fill="both")
+    item = StackTreeItem(flist)
+    node = TreeNode(sc.canvas, None, item)
+    node.expand()
 
-class StackBrowser:
+class StackTreeItem(TreeItem):
 
-    def __init__(self, root, flist, stack=None):
-        self.top = top = ListedToplevel(root)
-        top.protocol("WM_DELETE_WINDOW", self.close)
-        top.bind("<Key-Escape>", self.close)
-        top.wm_title("Stack viewer")
-        top.wm_iconname("Stack")
-        # Create help label
-        self.helplabel = Label(top,
-            text="Click once to view variables; twice for source",
-            borderwidth=2, relief="groove")
-        self.helplabel.pack(fill="x")
-        #
-        self.sv = StackViewer(top, flist, self)
-        if stack is None:
-            stack = get_stack()
-        self.sv.load_stack(stack)
-
-    def close(self, event=None):
-        self.top.destroy()
-
-    localsframe = None
-    localsviewer = None
-    localsdict = None
-    globalsframe = None
-    globalsviewer = None
-    globalsdict = None
-    curframe = None
-
-    def show_frame(self, (frame, lineno)):
-        if frame is self.curframe:
-            return
-        self.curframe = None
-        if frame.f_globals is not self.globalsdict:
-            self.show_globals(frame)
-        self.show_locals(frame)
-        self.curframe = frame
-
-    def show_globals(self, frame):
-        title = "Global Variables"
-        if frame.f_globals.has_key("__name__"):
-            try:
-                name = str(frame.f_globals["__name__"]) + ""
-            except:
-                name = ""
-            if name:
-                title = title + " in module " + name
-        self.globalsdict = None
-        if self.globalsviewer:
-            self.globalsviewer.close()
-        self.globalsviewer = None
-        if not self.globalsframe:
-            self.globalsframe = Frame(self.top)
-        self.globalsdict = frame.f_globals
-        self.globalsviewer = NamespaceViewer(
-            self.globalsframe,
-            title,
-            self.globalsdict)
-        self.globalsframe.pack(fill="both", side="bottom")
-
-    def show_locals(self, frame):
-        self.localsdict = None
-        if self.localsviewer:
-            self.localsviewer.close()
-        self.localsviewer = None
-        if frame.f_locals is not frame.f_globals:
-            title = "Local Variables"
-            code = frame.f_code
-            funcname = code.co_name
-            if funcname not in ("?", "", None):
-                title = title + " in " + funcname
-            if not self.localsframe:
-                self.localsframe = Frame(self.top)
-            self.localsdict = frame.f_locals
-            self.localsviewer = NamespaceViewer(
-                self.localsframe,
-                title,
-                self.localsdict)
-            self.localsframe.pack(fill="both", side="top")
-        else:
-            if self.localsframe:
-                self.localsframe.forget()
-
-
-class StackViewer(ScrolledList):
-
-    def __init__(self, master, flist, browser):
-        ScrolledList.__init__(self, master, width=80)
+    def __init__(self, flist=None):
         self.flist = flist
-        self.browser = browser
-        self.stack = []
+        self.stack = get_stack()
+        self.text = get_exception()
 
-    def load_stack(self, stack, index=None):
-        self.stack = stack
-        self.clear()
-##        if len(stack) > 10:
-##            l["height"] = 10
-##            self.topframe.pack(expand=1)
-##        else:
-##            l["height"] = len(stack)
-##            self.topframe.pack(expand=0)
-        for i in range(len(stack)):
-            frame, lineno = stack[i]
-            try:
-                modname = frame.f_globals["__name__"]
-            except:
-                modname = "?"
-            code = frame.f_code
-            filename = code.co_filename
-            funcname = code.co_name
-            sourceline = linecache.getline(filename, lineno)
-            sourceline = string.strip(sourceline)
-            if funcname in ("?", "", None):
-                item = "%s, line %d: %s" % (modname, lineno, sourceline)
-            else:
-                item = "%s.%s(), line %d: %s" % (modname, funcname,
-                                                 lineno, sourceline)
-            if i == index:
-                item = "> " + item
-            self.append(item)
-        if index is not None:
-            self.select(index)
+    def GetText(self):
+        return self.text
 
-    def popup_event(self, event):
-        if self.stack:
-            return ScrolledList.popup_event(self, event)
+    def GetSubList(self):
+        sublist = []
+        for info in self.stack:
+            item = FrameTreeItem(info, self.flist)
+            sublist.append(item)
+        return sublist
 
-    def fill_menu(self):
-        menu = self.menu
-        menu.add_command(label="Go to source line",
-                         command=self.goto_source_line)
-        menu.add_command(label="Show stack frame",
-                         command=self.show_stack_frame)
+class FrameTreeItem(TreeItem):
 
-    def on_select(self, index):
-        if 0 <= index < len(self.stack):
-            self.browser.show_frame(self.stack[index])
+    def __init__(self, info, flist):
+        self.info = info
+        self.flist = flist
 
-    def on_double(self, index):
-        self.show_source(index)
-
-    def goto_source_line(self):
-        index = self.listbox.index("active")
-        self.show_source(index)
-
-    def show_stack_frame(self):
-        index = self.listbox.index("active")
-        if 0 <= index < len(self.stack):
-            self.browser.show_frame(self.stack[index])
-
-    def show_source(self, index):
-        if not (0 <= index < len(self.stack)):
-            return
-        frame, lineno = self.stack[index]
+    def GetText(self):
+        frame, lineno = self.info
+        try:
+            modname = frame.f_globals["__name__"]
+        except:
+            modname = "?"
         code = frame.f_code
         filename = code.co_filename
-        if os.path.isfile(filename):
+        funcname = code.co_name
+        sourceline = linecache.getline(filename, lineno)
+        sourceline = string.strip(sourceline)
+        if funcname in ("?", "", None):
+            item = "%s, line %d: %s" % (modname, lineno, sourceline)
+        else:
+            item = "%s.%s(...), line %d: %s" % (modname, funcname,
+                                             lineno, sourceline)
+##        if i == index:
+##            item = "> " + item
+        return item
+
+    def GetSubList(self):
+        frame, lineno = self.info
+        sublist = []
+        if frame.f_globals is not frame.f_locals:
+            item = VariablesTreeItem("<locals>", frame.f_locals, self.flist)
+            sublist.append(item)
+        item = VariablesTreeItem("<globals>", frame.f_globals, self.flist)
+        sublist.append(item)
+        return sublist
+
+    def OnDoubleClick(self):
+        if self.flist:
+            frame, lineno = self.info
+            filename = frame.f_code.co_filename
             edit = self.flist.open(filename)
-            if edit:
-                edit.gotoline(lineno)
+            edit.gotoline(lineno)
 
+class VariablesTreeItem(ObjectTreeItem):
 
+    def GetText(self):
+        return self.labeltext
+
+    def GetLabelText(self):
+        return None
+
+    def IsExpandable(self):
+        return len(self.object) > 0
+
+    def keys(self):
+        return self.object.keys()
+
+    def GetSubList(self):
+        sublist = []
+        for key in self.keys():
+            try:
+                value = self.object[key]
+            except KeyError:
+                continue
+            def setfunction(value, key=key, object=self.object):
+                object[key] = value
+            item = make_objecttreeitem(key + " =", value, setfunction)
+            sublist.append(item)
+        return sublist
+        
 def get_stack(t=None, f=None):
     if t is None:
         t = sys.last_traceback
@@ -189,8 +118,7 @@
         t = t.tb_next
     return stack
 
-
-def getexception(type=None, value=None):
+def get_exception(type=None, value=None):
     if type is None:
         type = sys.last_type
         value = sys.last_value
@@ -201,76 +129,7 @@
         s = s + ": " + str(value)
     return s
 
-
-class NamespaceViewer:
-
-    def __init__(self, master, title, dict=None):
-        width = 0
-        height = 40
-        if dict:
-            height = 20*len(dict) # XXX 20 == observed height of Entry widget
-        self.master = master
-        self.title = title
-        self.repr = Repr()
-        self.repr.maxstring = 60
-        self.repr.maxother = 60
-        self.frame = frame = Frame(master)
-        self.frame.pack(expand=1, fill="both")
-        self.label = Label(frame, text=title, borderwidth=2, relief="groove")
-        self.label.pack(fill="x")
-        self.vbar = vbar = Scrollbar(frame, name="vbar")
-        vbar.pack(side="right", fill="y")
-        self.canvas = canvas = Canvas(frame,
-                                      height=min(300, max(40, height)),
-                                      scrollregion=(0, 0, width, height))
-        canvas.pack(side="left", fill="both", expand=1)
-        vbar["command"] = canvas.yview
-        canvas["yscrollcommand"] = vbar.set
-        self.subframe = subframe = Frame(canvas)
-        self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
-        self.load_dict(dict)
-
-    dict = -1
-
-    def load_dict(self, dict, force=0):
-        if dict is self.dict and not force:
-            return
-        subframe = self.subframe
-        frame = self.frame
-        for c in subframe.children.values():
-            c.destroy()
-        self.dict = None
-        if not dict:
-            l = Label(subframe, text="None")
-            l.grid(row=0, column=0)
-        else:
-            names = dict.keys()
-            names.sort()
-            row = 0
-            for name in names:
-                value = dict[name]
-                svalue = self.repr.repr(value) # repr(value)
-                l = Label(subframe, text=name)
-                l.grid(row=row, column=0, sticky="nw")
-    ##            l = Label(subframe, text=svalue, justify="l", wraplength=300)
-                l = Entry(subframe, width=0, borderwidth=0)
-                l.insert(0, svalue)
-    ##            l["state"] = "disabled"
-                l.grid(row=row, column=1, sticky="nw")
-                row = row+1
-        self.dict = dict
-        # XXX Could we use a <Configure> callback for the following?
-        subframe.update_idletasks() # Alas!
-        width = subframe.winfo_reqwidth()
-        height = subframe.winfo_reqheight()
-        canvas = self.canvas
-        self.canvas["scrollregion"] = (0, 0, width, height)
-        if height > 300:
-            canvas["height"] = 300
-            frame.pack(expand=1)
-        else:
-            canvas["height"] = height
-            frame.pack(expand=0)
-
-    def close(self):
-        self.frame.destroy()
+if __name__ == "__main__":
+    root = Tk()
+    root.withdraw()
+    StackBrowser(root)