]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/demo/Main.py
changed wxGLContext::SetColour() argument from wxChar* to wxString
[wxWidgets.git] / wxPython / demo / Main.py
index a0f0f7645b393b6f3e8557c027325343e365d3f0..495acedf769ae11fd83b16c0d8a31c2ae4bc9784 100644 (file)
 # * Annoying switching between tabs and resulting flicker
 #     how to replace a page in the notebook without deleting/adding?
 #     Where is SetPage!? tried freeze...tried reparent of dummy panel....
+#     AG: It looks like this issue is fixed by Freeze()ing and Thaw()ing the
+#         main frame and not the notebook
 
 # TODO List:
-# * UI design more prefessional
+# * UI design more professional (is the new version more professional?)
 # * save file positions (new field in demoModules) (@ LoadDemoSource)
 # * Update main overview
 
 import sys, os, time, traceback, types
 
 import wx                  # This module uses the new wx namespace
+import wx.aui
 import wx.html
 
 import images
 
 # For debugging
 ##wx.Trap();
-##print "wx.VERSION_STRING = ", wx.VERSION_STRING
-##print os.getpid();
+##print "wx.VERSION_STRING = %s (%s)" % (wx.VERSION_STRING, wx.USE_UNICODE and 'unicode' or 'ansi')
+##print "pid:", os.getpid()
 ##raw_input("Press Enter...")
 
 
 #---------------------------------------------------------------------------
 
+USE_CUSTOMTREECTRL = False
+ALLOW_AUI_FLOATING = False
+DEFAULT_PERSPECTIVE = "Default Perspective"
+
+#---------------------------------------------------------------------------
+
+_demoPngs = ["overview", "recent", "frame", "dialog", "moredialog", "core",
+             "book", "customcontrol", "morecontrols", "layout", "process", "clipboard",
+             "images", "miscellaneous"]
 
 _treeList = [
     # new stuff
     ('Recent Additions/Updates', [
-        'StockButtons',
-        'Ticker',
-        'Choicebook',
-        'ListCtrl_edit',
         ]),
 
     # managed windows == things with a (optional) caption you can close
     ('Frames and Dialogs', [
+        'AUI_DockingWindowMgr',
+        'AUI_MDI',
         'Dialog',
         'Frame',
         'MDIWindows',
@@ -64,12 +74,14 @@ _treeList = [
 
     # the common dialogs
     ('Common Dialogs', [
+        'AboutBox',
         'ColourDialog',
         'DirDialog',
         'FileDialog',
         'FindReplaceDialog',
         'FontDialog',
         'MessageDialog',
+        'MultiChoiceDialog',
         'PageSetupDialog',
         'PrintDialog',
         'ProgressDialog',
@@ -80,7 +92,6 @@ _treeList = [
     # dialogs from libraries
     ('More Dialogs', [
         'ImageBrowser',
-        'MultipleChoiceDialog',
         'ScrolledMessageDialog',
         ]),
 
@@ -91,7 +102,6 @@ _treeList = [
         'CheckBox',
         'CheckListBox',
         'Choice',
-        'Choicebook',
         'ComboBox',
         'Gauge',
         'Grid',
@@ -100,20 +110,20 @@ _treeList = [
         'ListCtrl',
         'ListCtrl_virtual',
         'ListCtrl_edit',
-        'Listbook',
         'Menu',
-        'Notebook',
         'PopupMenu',
         'PopupWindow',
         'RadioBox',
         'RadioButton',
         'SashWindow',
         'ScrolledWindow',
+        'SearchCtrl',        
         'Slider',
         'SpinButton',
         'SpinCtrl',
         'SplitterWindow',
         'StaticBitmap',
+        'StaticBox',
         'StaticText',
         'StatusBar',
         'StockButtons',
@@ -123,11 +133,24 @@ _treeList = [
         'TreeCtrl',
         'Validator',
         ]),
+    
+    ('"Book" Controls', [
+        'AUI_Notebook',
+        'Choicebook',
+        'Listbook',
+        'Notebook',
+        'Toolbook',
+        'Treebook',
+        ]),
 
     ('Custom Controls', [
-        'AnalogClockWindow',
+        'AnalogClock',
+        'ButtonPanel',
         'ColourSelect',
+        'ComboTreeBox',
+        'CustomTreeCtrl',
         'Editor',
+        'FlatNotebook',
         'GenericButtons',
         'GenericDirCtrl',
         'LEDNumberCtrl',
@@ -142,25 +165,36 @@ _treeList = [
         'ActiveX_FlashWindow',
         'ActiveX_IEHtmlWindow',
         'ActiveX_PDFWindow',
-        #'RightTextCtrl',     deprecated as we have wxTE_RIGHT now.
+        'BitmapComboBox',
         'Calendar',
         'CalendarCtrl',
+        'CheckListCtrlMixin',
+        'CollapsiblePane',
+        'ComboCtrl',
         'ContextHelp',
+        'DatePickerCtrl',
         'DynamicSashWindow',
         'EditableListBox',
+        'ExpandoTextCtrl',
         'FancyText',
         'FileBrowseButton',
         'FloatBar',  
         'FloatCanvas',
+        'FoldPanelBar',
         'HtmlWindow',
+        'HyperLinkCtrl',
         'IntCtrl',
         'MVCTree',   
         'MaskedEditControls',
         'MaskedNumCtrl',
-        'MimeTypesManager',
+        'MediaCtrl',
+        'MultiSplitterWindow',
+        'OwnerDrawnComboBox',
+        'Pickers',
         'PyCrust',
         'PyPlot',
         'PyShell',
+        'RichTextCtrl',
         'ScrolledPanel',
         'SplitTree',
         'StyledTextCtrl_1',
@@ -169,6 +203,7 @@ _treeList = [
         'Throbber',
         'Ticker',
         'TimeCtrl',
+        'TreeMixin',
         'VListBox',
         ]),
 
@@ -180,6 +215,7 @@ _treeList = [
         'Layoutf',
         'RowColSizer',
         'ScrolledPanel',
+        'SizedControls',
         'Sizers',
         'XmlResource',
         'XmlResourceHandler',
@@ -188,13 +224,14 @@ _treeList = [
 
     # ditto
     ('Process and Events', [
+        'DelayedResult',
         'EventManager',
         'KeyEvents',
         'Process',
         'PythonEvents',
         'Threads',
         'Timer',
-        ##'infoframe',    # needs better explaination and some fixing
+        ##'infoframe',    # needs better explanation and some fixing
         ]),
 
     # Clipboard and DnD
@@ -206,36 +243,45 @@ _treeList = [
 
     # Images
     ('Using Images', [
+        'AlphaDrawing',
+        'AnimateCtrl',
         'ArtProvider',
+        'BitmapFromBuffer',
         'Cursor',
         'DragImage',
         'Image',
         'ImageAlpha',
         'ImageFromStream',
+        'Img2PyArtProvider',
         'Mask',
+        'RawBitmapAccess',
         'Throbber',
         ]),
 
     # Other stuff
     ('Miscellaneous', [
+        'AlphaDrawing',
         'ColourDB',
-        ##'DialogUnits',   # needs more explainations
+        ##'DialogUnits',   # needs more explanations
+        'DragScroller',
         'DrawXXXList',
         'FileHistory',
         'FontEnumerator',
+        'GraphicsContext',
+        'GLCanvas',
+        'I18N',        
         'Joystick',
+        'MimeTypesManager',
+        'MouseGestures',
         'OGL',
         'PrintFramework',
+        'PseudoDC',
         'ShapedWindow',
         'Sound',
+        'StandardPaths',
         'Unicode',
         ]),
 
-    # need libs not coming with the demo
-    ('Samples using an external library', [
-        'GLCanvas',
-        ]),
-
 
     ('Check out the samples dir too', [
         ]),
@@ -254,9 +300,10 @@ class MyLog(wx.PyLog):
         self.logTime = logTime
 
     def DoLogString(self, message, timeStamp):
-        if self.logTime:
-            message = time.strftime("%X", time.localtime(timeStamp)) + \
-                      ": " + message
+        #print message, timeStamp
+        #if self.logTime:
+        #    message = time.strftime("%X", time.localtime(timeStamp)) + \
+        #              ": " + message
         if self.tc:
             self.tc.AppendText(message + '\n')
 
@@ -265,6 +312,60 @@ class MyTP(wx.PyTipProvider):
     def GetTip(self):
         return "This is my tip"
 
+#---------------------------------------------------------------------------
+# A class to be used to simply display a message in the demo pane
+# rather than running the sample itself.
+
+class MessagePanel(wx.Panel):
+    def __init__(self, parent, message, caption='', flags=0):
+        wx.Panel.__init__(self, parent)
+
+        # Make widgets
+        if flags:
+            artid = None
+            if flags & wx.ICON_EXCLAMATION:
+                artid = wx.ART_WARNING            
+            elif flags & wx.ICON_ERROR:
+                artid = wx.ART_ERROR
+            elif flags & wx.ICON_QUESTION:
+                artid = wx.ART_QUESTION
+            elif flags & wx.ICON_INFORMATION:
+                artid = wx.ART_INFORMATION
+
+            if artid is not None:
+                bmp = wx.ArtProvider.GetBitmap(artid, wx.ART_MESSAGE_BOX, (32,32))
+                icon = wx.StaticBitmap(self, -1, bmp)
+            else:
+                icon = (32,32) # make a spacer instead
+
+        if caption:
+            caption = wx.StaticText(self, -1, caption)
+            caption.SetFont(wx.Font(28, wx.SWISS, wx.NORMAL, wx.BOLD))
+
+        message = wx.StaticText(self, -1, message)
+
+        # add to sizers for layout
+        tbox = wx.BoxSizer(wx.VERTICAL)
+        if caption:
+            tbox.Add(caption)
+            tbox.Add((10,10))
+        tbox.Add(message)
+        
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        hbox.Add((10,10), 1)
+        hbox.Add(icon)
+        hbox.Add((10,10))
+        hbox.Add(tbox)
+        hbox.Add((10,10), 1)
+
+        box = wx.BoxSizer(wx.VERTICAL)
+        box.Add((10,10), 1)
+        box.Add(hbox, 0, wx.EXPAND)
+        box.Add((10,10), 2)
+
+        self.SetSizer(box)
+        self.Fit()
+        
 
 #---------------------------------------------------------------------------
 # A class to be used to display source code in the demo.  Try using the
@@ -389,9 +490,14 @@ try:
             if wx.Platform == '__WXMSW__':
                 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 
                                   'fore:#000000,back:#FFFFFF,face:Courier New,size:9')
+            elif wx.Platform == '__WXMAC__':
+                # TODO: if this looks fine on Linux too, remove the Mac-specific case 
+                # and use this whenever OS != MSW.
+                self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 
+                                  'fore:#000000,back:#FFFFFF,face:Courier')
             else:
                 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, 
-                                  'fore:#000000,back:#FFFFFF,face:Courier,size:12')
+                                  'fore:#000000,back:#FFFFFF,face:Courier,size:9')
     
             # Clear styles and revert to default.
             self.StyleClearAll()
@@ -461,13 +567,13 @@ except ImportError:
         def GetText(self):
             return self.GetValue()
 
-        def GetPositionFromLine(line):
+        def GetPositionFromLine(self, line):
             return self.XYToPosition(0,line)
 
         def GotoLine(self, line):
-            pos = self.editor.GetPositionFromLine(line)
-            self.editor.SetInsertionPoint(pos)
-            self.editor.ShowPosition(pos)
+            pos = self.GetPositionFromLine(line)
+            self.SetInsertionPoint(pos)
+            self.ShowPosition(pos)
 
         def SelectLine(self, line):
             start = self.GetPositionFromLine(line)
@@ -537,7 +643,9 @@ class DemoCodePanel(wx.Panel):
     def ActiveModuleChanged(self):
         self.LoadDemoSource(self.demoModules.GetSource())
         self.UpdateControlState()
+        self.mainFrame.Freeze()        
         self.ReloadDemo()
+        self.mainFrame.Thaw()
 
         
     def LoadDemoSource(self, source):
@@ -612,8 +720,8 @@ class DemoCodePanel(wx.Panel):
             try:
                 os.makedirs(GetModifiedDirectory())
                 if not os.path.exists(GetModifiedDirectory()):
-                    wx.LogMessage("BUG: Created demo directory but it still doesn't exit")
-                    raise AssetionError
+                    wx.LogMessage("BUG: Created demo directory but it still doesn't exist")
+                    raise AssertionError
             except:
                 wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory())
                 return
@@ -632,24 +740,37 @@ class DemoCodePanel(wx.Panel):
         self.demoModules.LoadFromFile(modModified, modifiedFilename)
         self.ActiveModuleChanged()
 
+        self.mainFrame.SetTreeModified(True)
+
 
     def OnRestore(self, event): # Handles the "Delete Modified" button
         modifiedFilename = GetModifiedFilename(self.demoModules.name)
         self.demoModules.Delete(modModified)
         os.unlink(modifiedFilename) # Delete the modified copy
         busy = wx.BusyInfo("Reloading demo module...")
+        
         self.ActiveModuleChanged()
 
+        self.mainFrame.SetTreeModified(False)
+
 
 #---------------------------------------------------------------------------
 
 def opj(path):
     """Convert paths to the platform-specific separator"""
-    str = apply(os.path.join, tuple(path.split('/')))
+    st = apply(os.path.join, tuple(path.split('/')))
     # HACK: on Linux, a leading / gets lost...
     if path.startswith('/'):
-        str = '/' + str
-    return str
+        st = '/' + st
+    return st
+
+
+def GetDataDir():
+    """
+    Return the standard location on this platform for application data
+    """
+    sp = wx.StandardPaths.Get()
+    return sp.GetUserDataDir()
 
 
 def GetModifiedDirectory():
@@ -657,7 +778,7 @@ def GetModifiedDirectory():
     Returns the directory where modified versions of the demo files
     are stored
     """
-    return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
+    return os.path.join(GetDataDir(), "modified")
 
 
 def GetModifiedFilename(name):
@@ -666,7 +787,7 @@ def GetModifiedFilename(name):
     """
     if not name.endswith(".py"):
         name = name + ".py"
-    return GetModifiedDirectory() + name
+    return os.path.join(GetModifiedDirectory(), name)
 
 
 def GetOriginalFilename(name):
@@ -686,6 +807,25 @@ def DoesModifiedExist(name):
         return False
 
 
+def GetConfig():
+    if not os.path.exists(GetDataDir()):
+        os.makedirs(GetDataDir())
+
+    config = wx.FileConfig(
+        localFilename=os.path.join(GetDataDir(), "options"))
+    return config
+
+
+def SearchDemo(name, keyword):
+    """ Returns whether a demo contains the search keyword or not. """
+    fid = open(GetOriginalFilename(name), "rt")
+    fullText = fid.read()
+    fid.close()
+    if fullText.find(keyword) >= 0:
+        return True
+
+    return False    
+
 #---------------------------------------------------------------------------
 
 class ModuleDictWrapper:
@@ -737,7 +877,8 @@ class DemoModules:
     def LoadDict(self, modID):
         if self.name != __name__:
             source = self.modules[modID][1]
-            description = self.modules[modID][3]
+            #description = self.modules[modID][3]
+            description = self.modules[modID][2]
 
             try:
                 self.modules[modID][0] = {}
@@ -873,7 +1014,7 @@ class DemoErrorPanel(wx.Panel):
         self.box = wx.BoxSizer(wx.VERTICAL)
 
         # Main Label
-        self.box.Add(wx.StaticText(self, -1, "An error has occured while trying to run the demo")
+        self.box.Add(wx.StaticText(self, -1, "An error has occurred while trying to run the demo")
                      , 0, wx.ALIGN_CENTER | wx.TOP, 10)
 
         # Exception Information
@@ -882,7 +1023,7 @@ class DemoErrorPanel(wx.Panel):
         boxInfoGrid  = wx.FlexGridSizer(0, 2, 0, 0)
         textFlags    = wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.TOP
         boxInfoGrid.Add(wx.StaticText(self, -1, "Type: "), 0, textFlags, 5 )
-        boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_type) , 0, textFlags, 5 )
+        boxInfoGrid.Add(wx.StaticText(self, -1, str(demoError.exception_type)) , 0, textFlags, 5 )
         boxInfoGrid.Add(wx.StaticText(self, -1, "Details: ") , 0, textFlags, 5 )
         boxInfoGrid.Add(wx.StaticText(self, -1, demoError.exception_details) , 0, textFlags, 5 )
         boxInfoSizer.Add(boxInfoGrid, 0, wx.ALIGN_CENTRE | wx.ALL, 5 )
@@ -1016,8 +1157,7 @@ class DemoTaskBarIcon(wx.TaskBarIcon):
 
 
     def OnTaskBarChange(self, evt):
-        names = [ "WXPdemo", "WXP", "Mondrian", "Test2m",
-                  "Blom08m", "Blom10m", "Blom15m" ]
+        names = [ "WXPdemo", "Mondrian", "Pencil", "Carrot" ]                  
         name = names[self.imgidx]
         
         getFunc = getattr(images, "get%sImage" % name)
@@ -1038,9 +1178,14 @@ class wxPythonDemo(wx.Frame):
     overviewText = "wxPython Overview"
 
     def __init__(self, parent, title):
-        wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
+        wx.Frame.__init__(self, parent, -1, title, size = (970, 720),
                           style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
 
+        self.SetMinSize((640,480))
+        
+        self.mgr = wx.aui.AuiManager()
+        self.mgr.SetManagedWindow(self)
+
         self.loaded = False
         self.cwd = os.getcwd()
         self.curOverview = ""
@@ -1048,14 +1193,16 @@ class wxPythonDemo(wx.Frame):
         self.codePage = None
         self.shell = None
         self.firstTime = True
+        self.finddlg = None
 
         icon = images.getWXPdemoIcon()
         self.SetIcon(icon)
 
-        self.tbicon = DemoTaskBarIcon(self)
-
-        wx.CallAfter(self.ShowTip)
-
+        try:
+            self.tbicon = DemoTaskBarIcon(self)
+        except:
+            self.tbicon = None
+            
         self.otherWin = None
         self.Bind(wx.EVT_IDLE, self.OnIdle)
         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
@@ -1065,129 +1212,67 @@ class wxPythonDemo(wx.Frame):
         self.Centre(wx.BOTH)
         self.CreateStatusBar(1, wx.ST_SIZEGRIP)
 
-        splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
-        splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
-
-        def EmptyHandler(evt): pass
-        #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
-        #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
-
-        # Prevent TreeCtrl from displaying all items after destruction when True
         self.dying = False
+        self.skipLoad = False
+        
+        def EmptyHandler(evt): pass
 
+        self.ReadConfigurationFile()
+        
         # Create a Notebook
-        self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
-
-        # Make a File menu
-        self.mainmenu = wx.MenuBar()
-        menu = wx.Menu()
-        item = menu.Append(-1, '&Redirect Output',
-                           'Redirect print statements to a window',
-                           wx.ITEM_CHECK)
-        self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
-        item = menu.Append(-1, 'E&xit\tAlt-X', 'Get the heck outta here!')
-        self.Bind(wx.EVT_MENU, self.OnFileExit, item)
-        wx.App_SetMacExitMenuItemId(item.GetId())
-        self.mainmenu.Append(menu, '&File')
-
-        # Make a Demo menu
-        menu = wx.Menu()
-        for item in _treeList:
-            submenu = wx.Menu()
-            for childItem in item[1]:
-                mi = submenu.Append(-1, childItem)
-                self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
-            menu.AppendMenu(wx.NewId(), item[0], submenu)
-        self.mainmenu.Append(menu, '&Demo')
-
-        # Make a Demo Code menu
-        #TODO: Add new menu items
-        #       Like the option-enabled entries to select the
-        #       active module
-        #TODO: should we bother?
-
-        #menu = wx.Menu()
-        #saveID = wx.NewId()
-        #restoreID = wx.NewId()
-       # 
-        #menu.Append(saveID, '&Save\tCtrl-S', 'Save edited demo')
-        #menu.Append(restoreID, '&Delete Modified\tCtrl-R', 'Delete modified copy')
-        #self.Bind(wx.EVT_MENU, self.codePage.OnSave, id=saveID)
-        #self.Bind(wx.EVT_MENU, self.codePage.OnRestore, id=restoreID)
-        #self.mainmenu.Append(menu, 'Demo &Code')
-       # 
-
-        # Make a Help menu
-        menu = wx.Menu()
-        findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
-        findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
-        menu.AppendSeparator()
-
-        shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
-                                'An interactive interpreter window with the demo app and frame objects in the namesapce')
-        menu.AppendSeparator()
-        helpItem = menu.Append(-1, '&About\tCtrl-H', 'wxPython RULES!!!')
-        wx.App_SetMacAboutMenuItemId(helpItem.GetId())
-
-        self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
-        self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
-        self.Bind(wx.EVT_MENU, self.OnHelpFind,  findItem)
-        self.Bind(wx.EVT_MENU, self.OnFindNext,  findnextItem)
-        self.Bind(wx.EVT_COMMAND_FIND, self.OnFind)
-        self.Bind(wx.EVT_COMMAND_FIND_NEXT, self.OnFind)
-        self.Bind(wx.EVT_COMMAND_FIND_CLOSE, self.OnFindClose)
-        self.mainmenu.Append(menu, '&Help')
-        self.SetMenuBar(self.mainmenu)
-
+        self.nb = wx.Notebook(self, -1, style=wx.CLIP_CHILDREN)
+        imgList = wx.ImageList(16, 16)
+        for png in ["overview", "code", "demo"]:
+            bmp = images.catalog[png].getBitmap()
+            imgList.Add(bmp)
+        self.nb.AssignImageList(imgList)
+
+        self.BuildMenuBar()
+        
         self.finddata = wx.FindReplaceData()
-
-        if 0:
-            # This is another way to set Accelerators, in addition to
-            # using the '\t<key>' syntax in the menu items.
-            aTable = wx.AcceleratorTable([(wx.ACCEL_ALT,  ord('X'), exitID),
-                                          (wx.ACCEL_CTRL, ord('H'), helpID),
-                                          (wx.ACCEL_CTRL, ord('F'), findID),
-                                          (wx.ACCEL_NORMAL, WXK_F3, findnextID)
-                                          ])
-            self.SetAcceleratorTable(aTable)
-
+        self.finddata.SetFlags(wx.FR_DOWN)
 
         # Create a TreeCtrl
-        tID = wx.NewId()
+        leftPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN)
         self.treeMap = {}
-        self.tree = wx.TreeCtrl(splitter, tID, style =
-                                wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
-                               )
-
-        root = self.tree.AddRoot("wxPython Overview")
-        firstChild = None
-        for item in _treeList:
-            child = self.tree.AppendItem(root, item[0])
-            if not firstChild: firstChild = child
-            for childItem in item[1]:
-                theDemo = self.tree.AppendItem(child, childItem)
-                self.treeMap[childItem] = theDemo
-
-        self.tree.Expand(root)
-        self.tree.Expand(firstChild)
-        self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
-        self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
-        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
+        self.searchItems = {}
+        
+        self.tree = wxPythonDemoTree(leftPanel)
+        
+        self.filter = wx.SearchCtrl(leftPanel, style=wx.TE_PROCESS_ENTER)
+        self.filter.ShowCancelButton(True)
+        self.filter.Bind(wx.EVT_TEXT, self.RecreateTree)
+        self.filter.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
+                         lambda e: self.filter.SetValue(''))
+        self.filter.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
+
+        searchMenu = wx.Menu()
+        item = searchMenu.AppendRadioItem(-1, "Sample Name")
+        self.Bind(wx.EVT_MENU, self.OnSearchMenu, item)
+        item = searchMenu.AppendRadioItem(-1, "Sample Content")
+        self.Bind(wx.EVT_MENU, self.OnSearchMenu, item)
+        self.filter.SetMenu(searchMenu)
+
+        self.RecreateTree()
+        self.tree.SetExpansionState(self.expansionState)
+        self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded)
+        self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
+        self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged)
         self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
-
+        
         # Set up a wx.html.HtmlWindow on the Overview Notebook page
         # we put it in a panel first because there seems to be a
         # refresh bug of some sort (wxGTK) when it is directly in
         # the notebook...
+        
         if 0:  # the old way
             self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
-            self.nb.AddPage(self.ovr, self.overviewText)
+            self.nb.AddPage(self.ovr, self.overviewText, imageId=0)
 
         else:  # hopefully I can remove this hacky code soon, see SF bug #216861
             panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
             self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
-            self.nb.AddPage(panel, self.overviewText)
+            self.nb.AddPage(panel, self.overviewText, imageId=0)
 
             def OnOvrSize(evt, ovr=self.ovr):
                 ovr.SetSize(evt.GetSize())
@@ -1200,8 +1285,10 @@ class wxPythonDemo(wx.Frame):
 
 
         # Set up a log window
-        self.log = wx.TextCtrl(splitter2, -1,
+        self.log = wx.TextCtrl(self, -1,
                               style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
+        if wx.Platform == "__WXMAC__":
+            self.log.MacCheckSpelling(False)
 
         # Set the wxWindows log target to be this textctrl
         #wx.Log_SetActiveTarget(wx.LogTextCtrl(self.log))
@@ -1213,29 +1300,19 @@ class wxPythonDemo(wx.Frame):
         #wx.Log_SetActiveTarget(wx.LogStderr())
         #wx.Log_SetTraceMask(wx.TraceMessages)
 
-
         self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
         wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
 
         # add the windows to the splitter and split it.
-        splitter2.SplitHorizontally(self.nb, self.log, -160)
-        splitter.SplitVertically(self.tree, splitter2, 200)
-
-        splitter.SetMinimumPaneSize(20)
-        splitter2.SetMinimumPaneSize(20)
-
-        # Make the splitter on the right expand the top window when resized
-        def SplitterOnSize(evt):
-            splitter = evt.GetEventObject()
-            sz = splitter.GetSize()
-            splitter.SetSashPosition(sz.height - 160, False)
-            evt.Skip()
-
-        splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
+        leftBox = wx.BoxSizer(wx.VERTICAL)
+        leftBox.Add(self.tree, 1, wx.EXPAND)
+        leftBox.Add(wx.StaticText(leftPanel, label = "Filter Demos:"), 0, wx.TOP|wx.LEFT, 5)
+        leftBox.Add(self.filter, 0, wx.EXPAND|wx.ALL, 5)
+        leftPanel.SetSizer(leftBox)
 
         # select initial items
         self.nb.SetSelection(0)
-        self.tree.SelectItem(root)
+        self.tree.SelectItem(self.root)
 
         # Load 'Main' module
         self.LoadDemo(self.overviewText)
@@ -1251,8 +1328,289 @@ class wxPythonDemo(wx.Frame):
                 self.tree.SelectItem(selectedDemo)
                 self.tree.EnsureVisible(selectedDemo)
 
+        # Use the aui manager to set up everything
+        self.mgr.AddPane(self.nb, wx.aui.AuiPaneInfo().CenterPane().Name("Notebook"))
+        self.mgr.AddPane(leftPanel,
+                         wx.aui.AuiPaneInfo().
+                         Left().Layer(2).BestSize((240, -1)).
+                         MinSize((160, -1)).
+                         Floatable(ALLOW_AUI_FLOATING).FloatingSize((240, 700)).
+                         Caption("wxPython Demos").
+                         CloseButton(False).
+                         Name("DemoTree"))
+        self.mgr.AddPane(self.log,
+                         wx.aui.AuiPaneInfo().
+                         Bottom().BestSize((-1, 150)).
+                         MinSize((-1, 60)).
+                         Floatable(ALLOW_AUI_FLOATING).FloatingSize((500, 160)).
+                         Caption("Demo Log Messages").
+                         CloseButton(False).
+                         Name("LogWindow"))
+
+        self.auiConfigurations[DEFAULT_PERSPECTIVE] = self.mgr.SavePerspective()
+        self.mgr.Update()
+
+        self.mgr.SetFlags(self.mgr.GetFlags() ^ wx.aui.AUI_MGR_TRANSPARENT_DRAG)
+        
+
 
-    #---------------------------------------------
+    def ReadConfigurationFile(self):
+
+        self.auiConfigurations = {}
+        self.expansionState = [0, 1]
+
+        config = GetConfig()
+        val = config.Read('ExpansionState')
+        if val:
+            self.expansionState = eval(val)
+
+        val = config.Read('AUIPerspectives')
+        if val:
+            self.auiConfigurations = eval(val)
+        
+
+    def BuildMenuBar(self):
+
+        # Make a File menu
+        self.mainmenu = wx.MenuBar()
+        menu = wx.Menu()
+        item = menu.Append(-1, '&Redirect Output',
+                           'Redirect print statements to a window',
+                           wx.ITEM_CHECK)
+        self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
+        exitItem = wx.MenuItem(menu, -1, 'E&xit\tCtrl-Q', 'Get the heck outta here!')
+        exitItem.SetBitmap(images.catalog['exit'].getBitmap())
+        menu.AppendItem(exitItem)
+        self.Bind(wx.EVT_MENU, self.OnFileExit, exitItem)
+        wx.App.SetMacExitMenuItemId(exitItem.GetId())
+        self.mainmenu.Append(menu, '&File')
+
+        # Make a Demo menu
+        menu = wx.Menu()
+        for indx, item in enumerate(_treeList[:-1]):
+            menuItem = wx.MenuItem(menu, -1, item[0])
+            submenu = wx.Menu()
+            for childItem in item[1]:
+                mi = submenu.Append(-1, childItem)
+                self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
+            menuItem.SetBitmap(images.catalog[_demoPngs[indx+1]].getBitmap())
+            menuItem.SetSubMenu(submenu)
+            menu.AppendItem(menuItem)
+        self.mainmenu.Append(menu, '&Demo')
+
+        # Make an Option menu
+        # If we've turned off floatable panels then this menu is not needed
+        if ALLOW_AUI_FLOATING:
+            menu = wx.Menu()
+            auiPerspectives = self.auiConfigurations.keys()
+            auiPerspectives.sort()
+            perspectivesMenu = wx.Menu()
+            item = wx.MenuItem(perspectivesMenu, -1, DEFAULT_PERSPECTIVE, "Load startup default perspective", wx.ITEM_RADIO)
+            self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
+            perspectivesMenu.AppendItem(item)
+            for indx, key in enumerate(auiPerspectives):
+                if key == DEFAULT_PERSPECTIVE:
+                    continue
+                item = wx.MenuItem(perspectivesMenu, -1, key, "Load user perspective %d"%indx, wx.ITEM_RADIO)
+                perspectivesMenu.AppendItem(item)
+                self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
+
+            menu.AppendMenu(wx.ID_ANY, "&AUI Perspectives", perspectivesMenu)
+            self.perspectives_menu = perspectivesMenu
+
+            item = wx.MenuItem(menu, -1, 'Save Perspective', 'Save AUI perspective')
+            item.SetBitmap(images.catalog['saveperspective'].getBitmap())
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, self.OnSavePerspective, item)
+
+            item = wx.MenuItem(menu, -1, 'Delete Perspective', 'Delete AUI perspective')
+            item.SetBitmap(images.catalog['deleteperspective'].getBitmap())
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, self.OnDeletePerspective, item)
+
+            menu.AppendSeparator()
+
+            item = wx.MenuItem(menu, -1, 'Restore Tree Expansion', 'Restore the initial tree expansion state')
+            item.SetBitmap(images.catalog['expansion'].getBitmap())
+            menu.AppendItem(item)
+            self.Bind(wx.EVT_MENU, self.OnTreeExpansion, item)
+
+            self.mainmenu.Append(menu, '&Options')
+        
+        # Make a Help menu
+        menu = wx.Menu()
+        findItem = wx.MenuItem(menu, -1, '&Find\tCtrl-F', 'Find in the Demo Code')
+        findItem.SetBitmap(images.catalog['find'].getBitmap())
+        findNextItem = wx.MenuItem(menu, -1, 'Find &Next\tF3', 'Find Next')
+        findNextItem.SetBitmap(images.catalog['findnext'].getBitmap())
+        menu.AppendItem(findItem)
+        menu.AppendItem(findNextItem)
+        menu.AppendSeparator()
+
+        shellItem = wx.MenuItem(menu, -1, 'Open Py&Shell Window\tF5',
+                                'An interactive interpreter window with the demo app and frame objects in the namesapce')
+        shellItem.SetBitmap(images.catalog['pyshell'].getBitmap())
+        menu.AppendItem(shellItem)
+        inspToolItem = wx.MenuItem(menu, -1, 'Open &Widget Inspector\tF6',
+                                   'A tool that lets you browse the live widgets and sizers in an application')
+        inspToolItem.SetBitmap(images.catalog['inspect'].getBitmap())
+        menu.AppendItem(inspToolItem)        
+        menu.AppendSeparator()
+        helpItem = menu.Append(-1, '&About wxPython Demo', 'wxPython RULES!!!')
+        wx.App.SetMacAboutMenuItemId(helpItem.GetId())
+
+        self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
+        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, inspToolItem)
+        self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
+        self.Bind(wx.EVT_MENU, self.OnHelpFind,  findItem)
+        self.Bind(wx.EVT_MENU, self.OnFindNext,  findNextItem)
+        self.Bind(wx.EVT_FIND, self.OnFind)
+        self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
+        self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
+        self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findNextItem)
+        self.mainmenu.Append(menu, '&Help')
+        self.SetMenuBar(self.mainmenu)
+
+        if False:
+            # This is another way to set Accelerators, in addition to
+            # using the '\t<key>' syntax in the menu items.
+            aTable = wx.AcceleratorTable([(wx.ACCEL_ALT,  ord('X'), exitItem.GetId()),
+                                          (wx.ACCEL_CTRL, ord('H'), helpItem.GetId()),
+                                          (wx.ACCEL_CTRL, ord('F'), findItem.GetId()),
+                                          (wx.ACCEL_NORMAL, wx.WXK_F3, findnextItem.GetId()),
+                                          (wx.ACCEL_NORMAL, wx.WXK_F9, shellItem.GetId()),
+                                          ])
+            self.SetAcceleratorTable(aTable)
+            
+
+    #---------------------------------------------    
+    def RecreateTree(self, evt=None):
+        # Catch the search type (name or content)
+        searchMenu = self.filter.GetMenu().GetMenuItems()
+        fullSearch = searchMenu[1].IsChecked()
+            
+        if evt:
+            if fullSearch:
+                # Do not`scan all the demo files for every char
+                # the user input, use wx.EVT_TEXT_ENTER instead
+                return
+
+        expansionState = self.tree.GetExpansionState()
+
+        current = None
+        item = self.tree.GetSelection()
+        if item:
+            prnt = self.tree.GetItemParent(item)
+            if prnt:
+                current = (self.tree.GetItemText(item),
+                           self.tree.GetItemText(prnt))
+                    
+        self.tree.Freeze()
+        self.tree.DeleteAllItems()
+        self.root = self.tree.AddRoot("wxPython Overview")
+        self.tree.SetItemImage(self.root, 0)
+        self.tree.SetItemPyData(self.root, 0)
+
+        treeFont = self.tree.GetFont()
+        catFont = self.tree.GetFont()
+
+        # The old native treectrl on MSW has a bug where it doesn't
+        # draw all of the text for an item if the font is larger than
+        # the default.  It seems to be clipping the item's label as if
+        # it was the size of the same label in the default font.
+        if 'wxMSW' not in wx.PlatformInfo or wx.GetApp().GetComCtl32Version() >= 600:
+            treeFont.SetPointSize(treeFont.GetPointSize()+2)
+            treeFont.SetWeight(wx.BOLD)
+            catFont.SetWeight(wx.BOLD)
+            
+        self.tree.SetItemFont(self.root, treeFont)
+        
+        firstChild = None
+        selectItem = None
+        filter = self.filter.GetValue()
+        count = 0
+        
+        for category, items in _treeList:
+            count += 1
+            if filter:
+                if fullSearch:
+                    items = self.searchItems[category]
+                else:
+                    items = [item for item in items if filter.lower() in item.lower()]
+            if items:
+                child = self.tree.AppendItem(self.root, category, image=count)
+                self.tree.SetItemFont(child, catFont)
+                self.tree.SetItemPyData(child, count)
+                if not firstChild: firstChild = child
+                for childItem in items:
+                    image = count
+                    if DoesModifiedExist(childItem):
+                        image = len(_demoPngs)
+                    theDemo = self.tree.AppendItem(child, childItem, image=image)
+                    self.tree.SetItemPyData(theDemo, count)
+                    self.treeMap[childItem] = theDemo
+                    if current and (childItem, category) == current:
+                        selectItem = theDemo
+                        
+                    
+        self.tree.Expand(self.root)
+        if firstChild:
+            self.tree.Expand(firstChild)
+        if filter:
+            self.tree.ExpandAll()
+        elif expansionState:
+            self.tree.SetExpansionState(expansionState)
+        if selectItem:
+            self.skipLoad = True
+            self.tree.SelectItem(selectItem)
+            self.skipLoad = False
+        
+        self.tree.Thaw()
+        self.searchItems = {}
+
+
+    def OnSearchMenu(self, event):
+
+        # Catch the search type (name or content)
+        searchMenu = self.filter.GetMenu().GetMenuItems()
+        fullSearch = searchMenu[1].IsChecked()
+        
+        if fullSearch:
+            self.OnSearch()
+        else:
+            self.RecreateTree()
+            
+
+    def OnSearch(self, event=None):
+
+        value = self.filter.GetValue()
+        if not value:
+            self.RecreateTree()
+            return
+
+        wx.BeginBusyCursor()
+        
+        for category, items in _treeList:
+            self.searchItems[category] = []
+            for childItem in items:
+                if SearchDemo(childItem, value):
+                    self.searchItems[category].append(childItem)
+
+        wx.EndBusyCursor()
+        self.RecreateTree()            
+
+
+    def SetTreeModified(self, modified):
+        item = self.tree.GetSelection()
+        if modified:
+            image = len(_demoPngs)
+        else:
+            image = self.tree.GetItemPyData(item)
+        self.tree.SetItemImage(item, image)
+        
+        
     def WriteText(self, text):
         if text[-1:] == '\n':
             text = text[:-1]
@@ -1284,7 +1642,7 @@ class wxPythonDemo(wx.Frame):
 
     #---------------------------------------------
     def OnSelChanged(self, event):
-        if self.dying or  not self.loaded:
+        if self.dying or not self.loaded or self.skipLoad:
             return
 
         item = event.GetItem()
@@ -1295,6 +1653,7 @@ class wxPythonDemo(wx.Frame):
     def LoadDemo(self, demoName):
         try:
             wx.BeginBusyCursor()
+            self.Freeze()
             
             os.chdir(self.cwd)
             self.ShutdownDemoModule()
@@ -1312,13 +1671,13 @@ class wxPythonDemo(wx.Frame):
                     wx.LogMessage("Loading demo %s.py..." % demoName)
                     self.demoModules = DemoModules(demoName)
                     self.LoadDemoSource()
-                    self.tree.Refresh()
                 else:
                     self.SetOverview("wxPython", mainOverview)
                     self.codePage = None
                     self.UpdateNotebook(0)
         finally:
             wx.EndBusyCursor()
+            self.Thaw()
 
     #---------------------------------------------
     def LoadDemoSource(self):
@@ -1336,7 +1695,7 @@ class wxPythonDemo(wx.Frame):
         
         # o The RunTest() for all samples must now return a window that can
         #   be palced in a tab in the main notebook.
-        # o If an error occurs (or has occured before) an error tab is created.
+        # o If an error occurs (or has occurred before) an error tab is created.
         
         if module is not None:
             wx.LogMessage("Running demo module...")
@@ -1349,6 +1708,10 @@ class wxPythonDemo(wx.Frame):
                 self.demoPage = DemoErrorPanel(self.nb, self.codePage,
                                                DemoError(sys.exc_info()), self)
 
+            bg = self.nb.GetThemeBackgroundColour()
+            if bg:
+                self.demoPage.SetBackgroundColour(bg)
+
             assert self.demoPage is not None, "runTest must return a window!"
             
         else:
@@ -1359,7 +1722,7 @@ class wxPythonDemo(wx.Frame):
         self.SetOverview(self.demoModules.name + " Overview", overviewText)
 
         if self.firstTime:
-            # cahnge to the demo page the first time a module is run
+            # change to the demo page the first time a module is run
             self.UpdateNotebook(2)
             self.firstTime = False
         else:
@@ -1379,6 +1742,7 @@ class wxPythonDemo(wx.Frame):
     def UpdateNotebook(self, select = -1):
         nb = self.nb
         debug = False
+        self.Freeze()
         
         def UpdatePage(page, pageText):
             pageExists = False
@@ -1392,15 +1756,13 @@ class wxPythonDemo(wx.Frame):
             if page:
                 if not pageExists:
                     # Add a new page
-                    nb.AddPage(page, pageText)
+                    nb.AddPage(page, pageText, imageId=nb.GetPageCount())
                     if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
                 else:
                     if nb.GetPage(pagePos) != page:
                         # Reload an existing page
-                        nb.Freeze()
                         nb.DeletePage(pagePos)
-                        nb.InsertPage(pagePos, page, pageText)
-                        nb.Thaw()
+                        nb.InsertPage(pagePos, page, pageText, imageId=pagePos)
                         if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
                     else:
                         # Excellent! No redraw/flicker
@@ -1420,7 +1782,9 @@ class wxPythonDemo(wx.Frame):
 
         if select >= 0 and select < nb.GetPageCount():
             nb.SetSelection(select)
-            
+
+        self.Thaw()
+        
     #---------------------------------------------
     def SetOverview(self, name, text):
         self.curOverview = text
@@ -1445,6 +1809,70 @@ class wxPythonDemo(wx.Frame):
         else:
             app.RestoreStdio()
             print "Print statements and other standard output will now be sent to the usual location."
+
+
+    def OnAUIPerspectives(self, event):
+        perspective = self.perspectives_menu.GetLabel(event.GetId())
+        self.mgr.LoadPerspective(self.auiConfigurations[perspective])
+        self.mgr.Update()
+
+
+    def OnSavePerspective(self, event):
+        dlg = wx.TextEntryDialog(self, "Enter a name for the new perspective:", "AUI Configuration")
+        
+        dlg.SetValue(("Perspective %d")%(len(self.auiConfigurations)+1))
+        if dlg.ShowModal() != wx.ID_OK:
+            return
+
+        perspectiveName = dlg.GetValue()
+        menuItems = self.perspectives_menu.GetMenuItems()
+        for item in menuItems:
+            if item.GetLabel() == perspectiveName:
+                wx.MessageBox("The selected perspective name:\n\n%s\n\nAlready exists."%perspectiveName,
+                              "Error", style=wx.ICON_ERROR)
+                return
+                
+        item = wx.MenuItem(self.perspectives_menu, -1, dlg.GetValue(),
+                           "Load user perspective %d"%(len(self.auiConfigurations)+1),
+                           wx.ITEM_RADIO)
+        self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)                
+        self.perspectives_menu.AppendItem(item)
+        item.Check(True)
+        self.auiConfigurations.update({dlg.GetValue(): self.mgr.SavePerspective()})
+
+
+    def OnDeletePerspective(self, event):
+        menuItems = self.perspectives_menu.GetMenuItems()[1:]
+        lst = []
+        loadDefault = False
+        
+        for item in menuItems:
+            lst.append(item.GetLabel())
+            
+        dlg = wx.MultiChoiceDialog(self, 
+                                   "Please select the perspectives\nyou would like to delete:",
+                                   "Delete AUI Perspectives", lst)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            selections = dlg.GetSelections()
+            strings = [lst[x] for x in selections]
+            for sel in strings:
+                self.auiConfigurations.pop(sel)
+                item = menuItems[lst.index(sel)]
+                if item.IsChecked():
+                    loadDefault = True
+                    self.perspectives_menu.GetMenuItems()[0].Check(True)
+                self.perspectives_menu.DeleteItem(item)
+                lst.remove(sel)
+
+        if loadDefault:
+            self.mgr.LoadPerspective(self.auiConfigurations[DEFAULT_PERSPECTIVE])
+            self.mgr.Update()
+
+
+    def OnTreeExpansion(self, event):
+        self.tree.SetExpansionState(self.expansionState)
+        
  
     def OnHelpAbout(self, event):
         from About import MyAboutBox
@@ -1453,25 +1881,40 @@ class wxPythonDemo(wx.Frame):
         about.Destroy()
 
     def OnHelpFind(self, event):
+        if self.finddlg != None:
+            return
+        
         self.nb.SetSelection(1)
         self.finddlg = wx.FindReplaceDialog(self, self.finddata, "Find",
-                        wx.FR_NOUPDOWN |
-                        wx.FR_NOMATCHCASE |
-                        wx.FR_NOWHOLEWORD)
+                        wx.FR_NOMATCHCASE | wx.FR_NOWHOLEWORD)
         self.finddlg.Show(True)
 
+
+    def OnUpdateFindItems(self, evt):
+        evt.Enable(self.finddlg == None)
+
+
     def OnFind(self, event):
         editor = self.codePage.editor
         self.nb.SetSelection(1)
         end = editor.GetLastPosition()
         textstring = editor.GetRange(0, end).lower()
-        start = editor.GetSelection()[1]
         findstring = self.finddata.GetFindString().lower()
-        loc = textstring.find(findstring, start)
+        backward = not (self.finddata.GetFlags() & wx.FR_DOWN)
+        if backward:
+            start = editor.GetSelection()[0]
+            loc = textstring.rfind(findstring, 0, start)
+        else:
+            start = editor.GetSelection()[1]
+            loc = textstring.find(findstring, start)
         if loc == -1 and start != 0:
             # string not found, start at beginning
-            start = 0
-            loc = textstring.find(findstring, start)
+            if backward:
+                start = end
+                loc = textstring.rfind(findstring, 0, start)
+            else:
+                start = 0
+                loc = textstring.find(findstring, start)
         if loc == -1:
             dlg = wx.MessageDialog(self, 'Find String Not Found',
                           'Find String Not Found in Demo File',
@@ -1484,6 +1927,7 @@ class wxPythonDemo(wx.Frame):
                 return
             else:
                 self.finddlg.Destroy()
+                self.finddlg = None
         editor.ShowPosition(loc)
         editor.SetSelection(loc, loc + len(findstring))
 
@@ -1497,6 +1941,7 @@ class wxPythonDemo(wx.Frame):
 
     def OnFindClose(self, event):
         event.GetDialog().Destroy()
+        self.finddlg = None
 
 
     def OnOpenShellWindow(self, evt):
@@ -1525,6 +1970,20 @@ class wxPythonDemo(wx.Frame):
                 evt.Skip()
             self.Bind(wx.EVT_CLOSE, CloseShell)
 
+
+    def OnOpenWidgetInspector(self, evt):
+        # Activate the widget inspection tool
+        from wx.lib.inspection import InspectionTool
+        if not InspectionTool().initialized:
+            InspectionTool().Init()
+
+        # Find a widget to be selected in the tree.  Use either the
+        # one under the cursor, if any, or this frame.
+        wnd = wx.FindWindowAtPointer()
+        if not wnd:
+            wnd = self
+        InspectionTool().Show(wnd, True)
+
         
     #---------------------------------------------
     def OnCloseWindow(self, event):
@@ -1532,7 +1991,14 @@ class wxPythonDemo(wx.Frame):
         self.demoPage = None
         self.codePage = None
         self.mainmenu = None
-        self.tbicon.Destroy()
+        if self.tbicon is not None:
+            self.tbicon.Destroy()
+
+        config = GetConfig()
+        config.Write('ExpansionState', str(self.tree.GetExpansionState()))
+        config.Write('AUIPerspectives', str(self.auiConfigurations))
+        config.Flush()
+
         self.Destroy()
 
 
@@ -1546,18 +2012,20 @@ class wxPythonDemo(wx.Frame):
 
     #---------------------------------------------
     def ShowTip(self):
-        try:
-            showTipText = open(opj("data/showTips")).read()
+        config = GetConfig()
+        showTipText = config.Read("tips")
+        if showTipText:
             showTip, index = eval(showTipText)
-        except IOError:
+        else:
             showTip, index = (1, 0)
+            
         if showTip:
             tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
             ##tp = MyTP(0)
             showTip = wx.ShowTip(self, tp)
             index = tp.GetCurrentTip()
-            open(opj("data/showTips"), "w").write(str( (showTip, index) ))
-
+            config.Write("tips", str( (showTip, index) ))
+            config.Flush()
 
     #---------------------------------------------
     def OnDemoMenu(self, event):
@@ -1596,19 +2064,80 @@ class wxPythonDemo(wx.Frame):
 
 class MySplashScreen(wx.SplashScreen):
     def __init__(self):
-        bmp = wx.Image(opj("bitmaps/splash.gif")).ConvertToBitmap()
+        bmp = wx.Image(opj("bitmaps/splash.png")).ConvertToBitmap()
         wx.SplashScreen.__init__(self, bmp,
                                  wx.SPLASH_CENTRE_ON_SCREEN | wx.SPLASH_TIMEOUT,
-                                 3000, None, -1)
+                                 5000, None, -1)
         self.Bind(wx.EVT_CLOSE, self.OnClose)
+        self.fc = wx.FutureCall(2000, self.ShowMain)
+
 
     def OnClose(self, evt):
+        # Make sure the default handler runs too so this window gets
+        # destroyed
+        evt.Skip()
         self.Hide()
+        
+        # if the timer is still running then go ahead and show the
+        # main frame now
+        if self.fc.IsRunning():
+            self.fc.Stop()
+            self.ShowMain()
+
+
+    def ShowMain(self):
         frame = wxPythonDemo(None, "wxPython: (A Demonstration)")
         frame.Show()
-        evt.Skip()  # Make sure the default handler runs too...
+        if self.fc.IsRunning():
+            self.Raise()
+        wx.CallAfter(frame.ShowTip)
+
+
 
 
+#---------------------------------------------------------------------------
+
+from wx.lib.mixins.treemixin import ExpansionState
+if USE_CUSTOMTREECTRL:
+    import wx.lib.customtreectrl as CT
+    TreeBaseClass = CT.CustomTreeCtrl
+else:
+    TreeBaseClass = wx.TreeCtrl
+    
+
+class wxPythonDemoTree(ExpansionState, TreeBaseClass):
+    def __init__(self, parent):
+        TreeBaseClass.__init__(self, parent, style=wx.TR_DEFAULT_STYLE|
+                               wx.TR_HAS_VARIABLE_ROW_HEIGHT)
+        self.BuildTreeImageList()
+        if USE_CUSTOMTREECTRL:
+            self.SetSpacing(10)
+            self.SetWindowStyle(self.GetWindowStyle() & ~wx.TR_LINES_AT_ROOT)
+
+    def AppendItem(self, parent, text, image=-1, wnd=None):
+        if USE_CUSTOMTREECTRL:
+            item = TreeBaseClass.AppendItem(self, parent, text, image=image, wnd=wnd)
+        else:
+            item = TreeBaseClass.AppendItem(self, parent, text, image=image)
+        return item
+            
+    def BuildTreeImageList(self):
+        imgList = wx.ImageList(16, 16)
+        for png in _demoPngs:
+            imgList.Add(images.catalog[png].getBitmap())
+            
+        # add the image for modified demos.
+        imgList.Add(images.catalog["custom"].getBitmap())
+
+        self.AssignImageList(imgList)
+        
+
+    def GetItemIdentity(self, item):
+        return self.GetPyData(item)
+
+
+#---------------------------------------------------------------------------
+
 class MyApp(wx.App):
     def OnInit(self):
         """
@@ -1616,6 +2145,9 @@ class MyApp(wx.App):
         the main frame when it is time to do so.
         """
 
+        wx.SystemOptions.SetOptionInt("mac.window-plain-transition", 1)
+        self.SetAppName("wxPyDemo")
+        
         # For debugging
         #self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
 
@@ -1624,7 +2156,7 @@ class MyApp(wx.App):
         # initialization, finally creating and showing the main
         # application window(s).  In this case we have nothing else to
         # do so we'll delay showing the main frame until later (see
-        # OnClose above) so the users can see the SplashScreen effect.        
+        # ShowMain above) so the users can see the SplashScreen effect.        
         splash = MySplashScreen()
         splash.Show()