+                pass
+            else:
+                if not filename:
+                    filename = "<string>"
+                line = line.strip()
+                self.traceback.append( (filename, lineno, "", line) )
+                excValue = msg
+        try:
+            self.exception_details = str(excValue)
+        except:
+            self.exception_details = "<unprintable %s object>" & type(excValue).__name__
+
+        del exc_info
+        
+    def __str__(self):
+        ret = "Type %s \n \
+        Traceback: %s \n \
+        Details  : %s" % ( str(self.exception_type), str(self.traceback), self.exception_details )
+        return ret
+
+#---------------------------------------------------------------------------
+
+class DemoErrorPanel(wx.Panel):
+    """Panel put into the demo tab when the demo fails to run due  to errors"""
+
+    def __init__(self, parent, codePanel, demoError, log):
+        wx.Panel.__init__(self, parent, -1)#, style=wx.NO_FULL_REPAINT_ON_RESIZE)
+        self.codePanel = codePanel
+        self.nb = parent
+        self.log = log
+
+        self.box = wx.BoxSizer(wx.VERTICAL)
+
+        # Main Label
+        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
+        boxInfo      = wx.StaticBox(self, -1, "Exception Info" )
+        boxInfoSizer = wx.StaticBoxSizer(boxInfo, wx.VERTICAL ) # Used to center the grid within the box
+        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, 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 )
+        self.box.Add(boxInfoSizer, 0, wx.ALIGN_CENTER | wx.ALL, 5)
+       
+        # Set up the traceback list
+        # This one automatically resizes last column to take up remaining space
+        from ListCtrl import TestListCtrl
+        self.list = TestListCtrl(self, -1, style=wx.LC_REPORT  | wx.SUNKEN_BORDER)
+        self.list.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
+        self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)
+        self.list.InsertColumn(0, "Filename")
+        self.list.InsertColumn(1, "Line", wx.LIST_FORMAT_RIGHT)
+        self.list.InsertColumn(2, "Function")
+        self.list.InsertColumn(3, "Code")
+        self.InsertTraceback(self.list, demoError.traceback)
+        self.list.SetColumnWidth(0, wx.LIST_AUTOSIZE)
+        self.list.SetColumnWidth(2, wx.LIST_AUTOSIZE)
+        self.box.Add(wx.StaticText(self, -1, "Traceback:")
+                     , 0, wx.ALIGN_CENTER | wx.TOP, 5)
+        self.box.Add(self.list, 1, wx.GROW | wx.ALIGN_CENTER | wx.ALL, 5)
+        self.box.Add(wx.StaticText(self, -1, "Entries from the demo module are shown in blue\n"
+                                           + "Double-click on them to go to the offending line")
+                     , 0, wx.ALIGN_CENTER | wx.BOTTOM, 5)
+
+        self.box.Fit(self)
+        self.SetSizer(self.box)
+
+
+    def InsertTraceback(self, list, traceback):
+        #Add the traceback data
+        for x in range(len(traceback)):
+            data = traceback[x]
+            list.InsertStringItem(x, os.path.basename(data[0])) # Filename
+            list.SetStringItem(x, 1, str(data[1]))              # Line
+            list.SetStringItem(x, 2, str(data[2]))              # Function
+            list.SetStringItem(x, 3, str(data[3]))              # Code
+            
+            # Check whether this entry is from the demo module
+            if data[0] == "<original>" or data[0] == "<modified>": # FIXME: make more generalised
+                self.list.SetItemData(x, int(data[1]))   # Store line number for easy access
+                # Give it a blue colour
+                item = self.list.GetItem(x)
+                item.SetTextColour(wx.BLUE)
+                self.list.SetItem(item)
+            else:
+                self.list.SetItemData(x, -1)        # Editor can't jump into this one's code
+       
+
+    def OnItemSelected(self, event):
+        # This occurs before OnDoubleClick and can be used to set the
+        # currentItem. OnDoubleClick doesn't get a wxListEvent....
+        self.currentItem = event.m_itemIndex
+        event.Skip()
+
+        
+    def OnDoubleClick(self, event):
+        # If double-clicking on a demo's entry, jump to the line number
+        line = self.list.GetItemData(self.currentItem)
+        if line != -1:
+            self.nb.SetSelection(1) # Switch to the code viewer tab
+            wx.CallAfter(self.codePanel.JumpToLine, line-1, True)
+        event.Skip()
+        
+
+#---------------------------------------------------------------------------
+
+class DemoTaskBarIcon(wx.TaskBarIcon):
+    TBMENU_RESTORE = wx.NewId()
+    TBMENU_CLOSE   = wx.NewId()
+    TBMENU_CHANGE  = wx.NewId()
+    TBMENU_REMOVE  = wx.NewId()
+    
+    def __init__(self, frame):
+        wx.TaskBarIcon.__init__(self)
+        self.frame = frame
+
+        # Set the image
+        icon = self.MakeIcon(images.getWXPdemoImage())
+        self.SetIcon(icon, "wxPython Demo")
+        self.imgidx = 1
+        
+        # bind some events
+        self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
+        self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
+        self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
+        self.Bind(wx.EVT_MENU, self.OnTaskBarChange, id=self.TBMENU_CHANGE)
+        self.Bind(wx.EVT_MENU, self.OnTaskBarRemove, id=self.TBMENU_REMOVE)
+
+
+    def CreatePopupMenu(self):
+        """
+        This method is called by the base class when it needs to popup
+        the menu for the default EVT_RIGHT_DOWN event.  Just create
+        the menu how you want it and return it from this function,
+        the base class takes care of the rest.
+        """
+        menu = wx.Menu()
+        menu.Append(self.TBMENU_RESTORE, "Restore wxPython Demo")
+        menu.Append(self.TBMENU_CLOSE,   "Close wxPython Demo")
+        menu.AppendSeparator()
+        menu.Append(self.TBMENU_CHANGE, "Change the TB Icon")
+        menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
+        return menu
+
+
+    def MakeIcon(self, img):
+        """
+        The various platforms have different requirements for the
+        icon size...
+        """
+        if "wxMSW" in wx.PlatformInfo:
+            img = img.Scale(16, 16)
+        elif "wxGTK" in wx.PlatformInfo:
+            img = img.Scale(22, 22)
+        # wxMac can be any size upto 128x128, so leave the source img alone....
+        icon = wx.IconFromBitmap(img.ConvertToBitmap() )
+        return icon
+    
+
+    def OnTaskBarActivate(self, evt):
+        if self.frame.IsIconized():
+            self.frame.Iconize(False)
+        if not self.frame.IsShown():
+            self.frame.Show(True)
+        self.frame.Raise()
+
+
+    def OnTaskBarClose(self, evt):
+        self.frame.Close()
+
+
+    def OnTaskBarChange(self, evt):
+        names = [ "WXPdemo", "Mondrian", "Pencil", "Carrot" ]                  
+        name = names[self.imgidx]
+        
+        getFunc = getattr(images, "get%sImage" % name)
+        self.imgidx += 1
+        if self.imgidx >= len(names):
+            self.imgidx = 0
+            
+        icon = self.MakeIcon(getFunc())
+        self.SetIcon(icon, "This is a new icon: " + name)
+
+
+    def OnTaskBarRemove(self, evt):
+        self.RemoveIcon()
+
+
+#---------------------------------------------------------------------------
+class wxPythonDemo(wx.Frame):
+    overviewText = "wxPython Overview"
+
+    def __init__(self, parent, title):
+        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 = ""
+        self.demoPage = None
+        self.codePage = None
+        self.shell = None
+        self.firstTime = True
+        self.finddlg = None
+
+        icon = images.getWXPdemoIcon()
+        self.SetIcon(icon)
+
+        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)
+        self.Bind(wx.EVT_ICONIZE, self.OnIconfiy)
+        self.Bind(wx.EVT_MAXIMIZE, self.OnMaximize)
+
+        self.Centre(wx.BOTH)
+        self.CreateStatusBar(1, wx.ST_SIZEGRIP)
+
+        self.dying = False
+        self.skipLoad = False
+        
+        def EmptyHandler(evt): pass
+
+        self.ReadConfigurationFile()
+        
+        # Create a Notebook
+        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()
+        self.finddata.SetFlags(wx.FR_DOWN)
+
+        # Create a TreeCtrl
+        leftPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN)
+        self.treeMap = {}
+        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, 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, imageId=0)
+
+            def OnOvrSize(evt, ovr=self.ovr):
+                ovr.SetSize(evt.GetSize())
+            panel.Bind(wx.EVT_SIZE, OnOvrSize)
+            panel.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
+
+        if "gtk2" in wx.PlatformInfo:
+            self.ovr.SetStandardFonts()
+        self.SetOverview(self.overviewText, mainOverview)
+
+
+        # Set up a log window
+        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))
+
+        # But instead of the above we want to show how to use our own wx.Log class
+        wx.Log_SetActiveTarget(MyLog(self.log))
+        
+        # for serious debugging
+        #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.
+        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(self.root)
+
+        # Load 'Main' module
+        self.LoadDemo(self.overviewText)
+        self.loaded = True
+
+        # select some other initial module?
+        if len(sys.argv) > 1:
+            arg = sys.argv[1]
+            if arg.endswith('.py'):
+                arg = arg[:-3]
+            selectedDemo = self.treeMap.get(arg, None)