+class DemoCodePanel(wx.Panel):
+    """Panel for the 'Demo Code' tab"""
+    def __init__(self, parent, mainFrame):
+        wx.Panel.__init__(self, parent, size=(1,1))
+        if 'wxMSW' in wx.PlatformInfo:
+            self.Hide()
+        self.mainFrame = mainFrame
+        self.editor = DemoCodeEditor(self)
+        self.editor.RegisterModifiedEvent(self.OnCodeModified)
+
+        self.btnSave = wx.Button(self, -1, "Save Changes")
+        self.btnRestore = wx.Button(self, -1, "Delete Modified")
+        self.btnSave.Enable(False)
+        self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
+        self.btnRestore.Bind(wx.EVT_BUTTON, self.OnRestore)
+
+        self.radioButtons = { modOriginal: wx.RadioButton(self, -1, "Original", style = wx.RB_GROUP),
+                              modModified: wx.RadioButton(self, -1, "Modified") }
+
+        self.controlBox = wx.BoxSizer(wx.HORIZONTAL)
+        self.controlBox.Add(wx.StaticText(self, -1, "Active Version:"), 0,
+                            wx.RIGHT | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
+        for modID, radioButton in self.radioButtons.items():
+            self.controlBox.Add(radioButton, 0, wx.EXPAND | wx.RIGHT, 5)
+            radioButton.modID = modID # makes it easier for the event handler
+            radioButton.Bind(wx.EVT_RADIOBUTTON, self.OnRadioButton)
+            
+        self.controlBox.Add(self.btnSave, 0, wx.RIGHT, 5)
+        self.controlBox.Add(self.btnRestore, 0)
+
+        self.box = wx.BoxSizer(wx.VERTICAL)
+        self.box.Add(self.controlBox, 0, wx.EXPAND)
+        self.box.Add(wx.StaticLine(self), 0, wx.EXPAND)
+        self.box.Add(self.editor, 1, wx.EXPAND)
+        
+        self.box.Fit(self)
+        self.SetSizer(self.box)
+
+
+    # Loads a demo from a DemoModules object
+    def LoadDemo(self, demoModules):
+        self.demoModules = demoModules
+        if (modDefault == modModified) and demoModules.Exists(modModified):
+            demoModules.SetActive(modModified)
+        else:
+            demoModules.SetActive(modOriginal)
+        self.radioButtons[demoModules.GetActiveID()].Enable(True)
+        self.ActiveModuleChanged()
+
+
+    def ActiveModuleChanged(self):
+        self.LoadDemoSource(self.demoModules.GetSource())
+        self.UpdateControlState()
+        self.ReloadDemo()
+
+        
+    def LoadDemoSource(self, source):
+        self.editor.Clear()
+        self.editor.SetValue(source)
+        self.JumpToLine(0)
+        self.btnSave.Enable(False)
+
+
+    def JumpToLine(self, line, highlight=False):
+        self.editor.GotoLine(line)
+        self.editor.SetFocus()
+        if highlight:
+            self.editor.SelectLine(line)
+        
+       
+    def UpdateControlState(self):
+        active = self.demoModules.GetActiveID()
+        # Update the radio/restore buttons
+        for moduleID in self.radioButtons:
+            btn = self.radioButtons[moduleID]
+            if moduleID == active:
+                btn.SetValue(True)
+            else:
+                btn.SetValue(False)
+
+            if self.demoModules.Exists(moduleID):
+                btn.Enable(True)
+                if moduleID == modModified:
+                    self.btnRestore.Enable(True)
+            else:
+                btn.Enable(False)
+                if moduleID == modModified:
+                    self.btnRestore.Enable(False)
+
+                    
+    def OnRadioButton(self, event):
+        radioSelected = event.GetEventObject()
+        modSelected = radioSelected.modID
+        if modSelected != self.demoModules.GetActiveID():
+            busy = wx.BusyInfo("Reloading demo module...")
+            self.demoModules.SetActive(modSelected)
+            self.ActiveModuleChanged()
+
+
+    def ReloadDemo(self):
+        if self.demoModules.name != __name__:
+            self.mainFrame.RunModule()
+
+                
+    def OnCodeModified(self, event):
+        self.btnSave.Enable(self.editor.IsModified())
+
+        
+    def OnSave(self, event):
+        if self.demoModules.Exists(modModified):
+            if self.demoModules.GetActiveID() == modOriginal:
+                overwriteMsg = "You are about to overwrite an already existing modified copy\n" + \
+                               "Do you want to continue?"
+                dlg = wx.MessageDialog(self, overwriteMsg, "wxPython Demo",
+                                       wx.YES_NO | wx.NO_DEFAULT| wx.ICON_EXCLAMATION)
+                result = dlg.ShowModal()
+                if result == wx.ID_NO:
+                    return
+                dlg.Destroy()
+            
+        self.demoModules.SetActive(modModified)
+        modifiedFilename = GetModifiedFilename(self.demoModules.name)
+
+        # Create the demo directory if one doesn't already exist
+        if not os.path.exists(GetModifiedDirectory()):
+            try:
+                os.makedirs(GetModifiedDirectory())
+                if not os.path.exists(GetModifiedDirectory()):
+                    wx.LogMessage("BUG: Created demo directory but it still doesn't exit")
+                    raise AssetionError
+            except:
+                wx.LogMessage("Error creating demo directory: %s" % GetModifiedDirectory())
+                return
+            else:
+                wx.LogMessage("Created directory for modified demos: %s" % GetModifiedDirectory())
+            
+        # Save
+        f = open(modifiedFilename, "wt")
+        source = self.editor.GetText()
+        try:
+            f.write(source)
+        finally:
+            f.close()
+            
+        busy = wx.BusyInfo("Reloading demo module...")
+        self.demoModules.LoadFromFile(modModified, modifiedFilename)
+        self.ActiveModuleChanged()
+
+
+    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()
+
+
+#---------------------------------------------------------------------------
+
+def opj(path):
+    """Convert paths to the platform-specific separator"""
+    str = apply(os.path.join, tuple(path.split('/')))
+    # HACK: on Linux, a leading / gets lost...
+    if path.startswith('/'):
+        str = '/' + str
+    return str
+
+
+def GetModifiedDirectory():
+    """
+    Returns the directory where modified versions of the demo files
+    are stored
+    """
+    return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
+
+
+def GetModifiedFilename(name):
+    """
+    Returns the filename of the modified version of the specified demo
+    """
+    if not name.endswith(".py"):
+        name = name + ".py"
+    return GetModifiedDirectory() + name
+
+
+def GetOriginalFilename(name):
+    """
+    Returns the filename of the original version of the specified demo
+    """
+    if not name.endswith(".py"):
+        name = name + ".py"
+    return name
+
+
+def DoesModifiedExist(name):
+    """Returns whether the specified demo has a modified copy"""
+    if os.path.exists(GetModifiedFilename(name)):
+        return True
+    else:
+        return False
+
+
+#---------------------------------------------------------------------------
+
+class ModuleDictWrapper:
+    """Emulates a module with a dynamically compiled __dict__"""
+    def __init__(self, dict):
+        self.dict = dict
+        
+    def __getattr__(self, name):
+        if name in self.dict:
+            return self.dict[name]
+        else:
+            raise AttributeError
+
+class DemoModules:
+    """
+    Dynamically manages the original/modified versions of a demo
+    module
+    """
+    def __init__(self, name):
+        self.modActive = -1
+        self.name = name
+        
+        #              (dict , source ,  filename , description   , error information )        
+        #              (  0  ,   1    ,     2     ,      3        ,          4        )        
+        self.modules = [[None,  ""    ,    ""     , "<original>"  ,        None],
+                        [None,  ""    ,    ""     , "<modified>"  ,        None]]
+        
+        # load original module
+        self.LoadFromFile(modOriginal, GetOriginalFilename(name))
+        self.SetActive(modOriginal)
+
+        # load modified module (if one exists)
+        if DoesModifiedExist(name):
+           self.LoadFromFile(modModified, GetModifiedFilename(name))
+
+
+    def LoadFromFile(self, modID, filename):
+        self.modules[modID][2] = filename
+        file = open(filename, "rt")
+        self.LoadFromSource(modID, file.read())
+        file.close()
+
+
+    def LoadFromSource(self, modID, source):
+        self.modules[modID][1] = source
+        self.LoadDict(modID)
+
+
+    def LoadDict(self, modID):
+        if self.name != __name__:
+            source = self.modules[modID][1]
+            description = self.modules[modID][3]
+
+            try:
+                self.modules[modID][0] = {}
+                code = compile(source, description, "exec")        
+                exec code in self.modules[modID][0]
+            except:
+                self.modules[modID][4] = DemoError(sys.exc_info())
+                self.modules[modID][0] = None
+            else:
+                self.modules[modID][4] = None