+    def GetErrorInfo(self, modID = None):
+        if modID is None:
+            modID = self.modActive
+        return self.modules[self.modActive][4]
+
+
+    def Exists(self, modID):
+        return self.modules[modID][1] != ""
+
+
+    def UpdateFile(self, modID = None):
+        """Updates the file from which a module was loaded
+        with (possibly updated) source"""
+        if modID is None:
+            modID = self.modActive
+
+        source = self.modules[modID][1]
+        filename = self.modules[modID][2]
+
+        try:        
+            file = open(filename, "wt")
+            file.write(source)
+        finally:
+            file.close()
+
+
+    def Delete(self, modID):
+        if self.modActive == modID:
+            self.SetActive(0)
+
+        self.modules[modID][0] = None
+        self.modules[modID][1] = ""
+        self.modules[modID][2] = ""
+
+
+#---------------------------------------------------------------------------
+
+class DemoError:
+    """Wraps and stores information about the current exception"""
+    def __init__(self, exc_info):
+        import copy
+        
+        excType, excValue = exc_info[:2]
+        # traceback list entries: (filename, line number, function name, text)
+        self.traceback = traceback.extract_tb(exc_info[2])
+
+        # --Based on traceback.py::format_exception_only()--
+        if type(excType) == types.ClassType:
+            self.exception_type = excType.__name__
+        else:
+            self.exception_type = excType
+
+        # If it's a syntax error, extra information needs
+        # to be added to the traceback
+        if excType is SyntaxError:
+            try:
+                msg, (filename, lineno, self.offset, line) = excValue
+            except:
+                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 occured 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, 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()
+        
+