Adding comments and code improvements
authorlaurent
Fri, 24 Jul 2009 12:49:57 +0200
changeset 390 020420ad8914
parent 389 6a72016d721a
child 391 07447ee3538e
Adding comments and code improvements
PLCOpenEditor.py
--- a/PLCOpenEditor.py	Fri Jul 24 12:29:48 2009 +0200
+++ b/PLCOpenEditor.py	Fri Jul 24 12:49:57 2009 +0200
@@ -64,9 +64,11 @@
  ID_PLCOPENEDITORLDTOOLBAR,
 ] = [wx.NewId() for _init_ctrls in range(17)]
 
+# Define PLCOpenEditor FileMenu extra items id
 [ID_PLCOPENEDITORFILEMENUGENERATE, 
 ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)]
 
+# Define PLCOpenEditor EditMenu extra items id
 [ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE, 
  ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, 
  ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, 
@@ -77,7 +79,7 @@
 #                            ToolBars definitions
 #-------------------------------------------------------------------------------
 
-
+# Define PLCOpenEditor Toolbar items id
 [ID_PLCOPENEDITORTOOLBARSELECTION, ID_PLCOPENEDITORTOOLBARCOMMENT,
  ID_PLCOPENEDITORTOOLBARVARIABLE, ID_PLCOPENEDITORTOOLBARBLOCK,
  ID_PLCOPENEDITORTOOLBARCONNECTION, ID_PLCOPENEDITORTOOLBARWIRE,
@@ -89,6 +91,14 @@
  ID_PLCOPENEDITORTOOLBARJUMP,
 ] = [wx.NewId() for _init_coll_DefaultToolBar_Items in range(17)]
 
+# Define behaviour of each Toolbar item according to current POU body type 
+# Informations meaning are in this order:
+#  - Item is toggled
+#  - PLCOpenEditor mode where item is displayed (could be more then one)
+#  - Item id
+#  - Item callback function name
+#  - Item icon filename
+#  - Item tooltip text
 ToolBarItems = {
     "FBD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE,
               ID_PLCOPENEDITORTOOLBARCOMMENT, "OnCommentTool",
@@ -169,12 +179,56 @@
     "IL"  : []
 }
 
+#-------------------------------------------------------------------------------
+#                               Helper Functions
+#-------------------------------------------------------------------------------
+
+# Compatibility function for wx versions < 2.6
 def AppendMenu(parent, help, id, kind, text):
     if wx.VERSION >= (2, 6, 0):
         parent.Append(help=help, id=id, kind=kind, text=text)
     else:
         parent.Append(helpString=help, id=id, kind=kind, item=text)
 
+[TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, 
+ INSTANCESTREE, LIBRARYTREE, SCALING
+] = [wx.NewId() for _refresh_elements in range(9)]
+
+def GetShortcutKeyCallbackFunction(viewer_function):
+    def ShortcutKeyFunction(self, event):
+        control = self.FindFocus()
+        if isinstance(control, (Viewer, TextViewer)):
+            getattr(control, viewer_function)()
+        elif isinstance(control, wx.TextCtrl):
+            control.ProcessEvent(event)
+        event.Skip()
+    return ShortcutKeyFunction
+
+def GetParentName(tree, item, parent_type):
+    parent_item = tree.GetItemParent(item)
+    parent_item_type = tree.GetPyData(parent_item)
+    while parent_item_type != parent_type:
+        parent_item = tree.GetItemParent(parent_item)
+        parent_item_type = tree.GetPyData(parent_item)
+    return tree.GetItemText(parent_item)
+
+def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None):
+    def DeleteElementFunction(self, selected):
+        name = self.TypesTree.GetItemText(selected)
+        if check_function is None or not check_function(self.Controler, name, self.Debug):
+            if parent_type is not None:
+                parent_name = GetParentName(self.TypesTree, selected, parent_type)
+                remove_function(self.Controler, parent_name, name)
+            else:
+                remove_function(self.Controler, name)
+        else:
+            self.ShowErrorMessage("\"%s\" is used by one or more POUs. It can't be removed!"%name)
+    return DeleteElementFunction
+
+#-------------------------------------------------------------------------------
+#                            PLCOpenEditor Main Class
+#-------------------------------------------------------------------------------
+
 class PLCOpenEditor(wx.Frame):
     
     # Compatibility function for wx versions < 2.6
@@ -430,6 +484,10 @@
                   name='InstancesTree', parent=self.TreeNoteBook, 
                   pos=wx.Point(0, 0), size=wx.Size(0, 0),
                   style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER)
+        
+        self.TabsImageList = wx.ImageList(16, 31)
+        self.TabsImageListIndexes = {}
+        
         if self.Debug:
             if wx.VERSION >= (2, 6, 0):
                 self.InstancesTree.Bind(wx.EVT_RIGHT_UP, self.OnInstancesTreeRightUp)
@@ -540,9 +598,7 @@
                 self.TabsOpened = wx.Notebook(id=ID_PLCOPENEDITORTABSOPENED,
                       name='TabsOpened', parent=self.ThirdSplitter, pos=wx.Point(0,
                       0), size=wx.Size(0, 0), style=0)
-                self.TabsImageList = wx.ImageList(16, 31)
                 self.TabsOpened.SetImageList(self.TabsImageList)
-                self.TabsImageListIndexes = {}
                 if wx.VERSION >= (2, 6, 0):
                     self.TabsOpened.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED,
                         self.OnPouSelectedChanged, id=ID_PLCOPENEDITORTABSOPENED)
@@ -579,11 +635,19 @@
         if USE_AUI:
             self.AUIManager.Update()
     
+    ## Constructor of the PLCOpenEditor class.
+    #  @param parent The parent window.
+    #  @param controler The controler been used by PLCOpenEditor (default: None).
+    #  @param fileOpen The filepath to open if no controler defined (default: None).
+    #  @param debug The filepath to open if no controler defined (default: False).
     def __init__(self, parent, controler = None, fileOpen = None, debug = False):
+        # Variable indicating that PLCOpenEditor was opened with a defined controler
         self.ModeSolo = controler == None
         self.Debug = debug
         if self.ModeSolo:
+            # If no controler defined, create a new one
             self.Controler = PLCControler()
+            # Open the filepath if defined
             if fileOpen is not None:
                 self.Controler.OpenXMLFile(fileOpen)
         else:
@@ -591,16 +655,18 @@
         
         self._init_ctrls(parent)
         
-        self.SetIcon(wx.Icon(os.path.join(CWD,"Images","poe.ico"),wx.BITMAP_TYPE_ICO))
-        
-        self.TypesTreeImageList = wx.ImageList(16, 16)
-        self.InstancesTreeImageList = wx.ImageList(16, 16)
-        self.TypesTreeImageDict = {}
-        self.InstancesTreeImageDict = {}
+        # Define PLCOpenEditor icon
+        self.SetIcon(wx.Icon(os.path.join(CWD,"Images", "poe.ico"),wx.BITMAP_TYPE_ICO))
+        
+        # Define Tree item icon list
+        self.TreeImageList = wx.ImageList(16, 16)
+        self.TreeImageDict = {}
+        
+        # Icons for languages
         for language in LANGUAGES:
-            self.TypesTreeImageDict[language]=self.TypesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%language)))
-            self.InstancesTreeImageDict[language]=self.InstancesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%language)))
-        
+            self.TreeImageDict[language]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%language)))
+            
+        # Icons for other items
         for imgname, itemtype in [
             #editables
             ("PROJECT",        ITEM_PROJECT),
@@ -628,10 +694,11 @@
             ("CONFIGURATIONS", ITEM_CONFIGURATIONS),
             ("RESOURCES",      ITEM_RESOURCES),
             ("PROPERTIES",     ITEM_PROPERTIES)]:
-            self.TypesTreeImageDict[itemtype]=self.TypesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname)))
-            self.InstancesTreeImageDict[itemtype]=self.InstancesTreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname)))
-        self.TypesTree.AssignImageList(self.TypesTreeImageList)
-        self.InstancesTree.AssignImageList(self.InstancesTreeImageList)
+            self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname)))
+        
+        # Assign icon list to TreeCtrls
+        self.TypesTree.SetImageList(self.TreeImageList)
+        self.InstancesTree.SetImageList(self.TreeImageList)
         
         self.CurrentToolBar = []
         self.CurrentLanguage = ""
@@ -642,6 +709,7 @@
         if USE_AUI:
             self.AuiTabCtrl = []
         
+        # Initialize Printing configuring elements
         self.PrintData = wx.PrintData()
         self.PrintData.SetPaperId(wx.PAPER_A4)
         self.PrintData.SetPrintMode(wx.PRINT_MODE_PRINTER)
@@ -649,41 +717,41 @@
         self.PageSetupData.SetMarginTopLeft(wx.Point(10, 15))
         self.PageSetupData.SetMarginBottomRight(wx.Point(10, 20))
         
+        # Refresh elements that need to
         if not self.ModeSolo or fileOpen is not None:
-            self.RefreshTypesTree()
-            self.RefreshInstancesTree()
-            self.RefreshLibraryTree()
-        
-        self.RefreshFileMenu()
-        self.RefreshEditMenu()
-        self.RefreshDisplayMenu()
-        self.RefreshTitle()
-        self.RefreshToolBar()
-
-    def ResetSelectedItem(self):
-        self.SelectedItem = None
-
+            self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
+        self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
+
+
+#-------------------------------------------------------------------------------
+#                               General Functions
+#-------------------------------------------------------------------------------
+
+    ## Call PLCOpenEditor refresh functions.
+    #  @param elements List of elements to refresh.
+    def _Refresh(self, *elements):
+        for element in elements:
+            self.RefreshFunctions[element](self)
+
+    ## Callback function when AUINotebook Page closed with CloseButton
+    #  @param event AUINotebook Event.
     def OnPageClose(self, event):
+        # Get Selected Tab
         selected = event.GetSelection()
         if selected >= 0:
+            # Remove corresponding VariablePanel
             if not self.Debug:
                 tagname = self.TabsOpened.GetPage(selected).GetTagName()
                 self.VariablePanelIndexer.RemoveVariablePanel(tagname)
+            # Refresh Tab selection
             if self.TabsOpened.GetPageCount() > 0:
                 new_index = min(selected, self.TabsOpened.GetPageCount() - 1)
                 self.TabsOpened.SetSelection(new_index)
                 if not self.Debug:
                     tagname = self.TabsOpened.GetPage(new_index).GetTagName()
                     self.VariablePanelIndexer.ChangeVariablePanel(tagname)
-            self.RefreshTitle()
-            self.RefreshFileMenu()
-            self.RefreshEditMenu()
-            self.RefreshDisplayMenu()
-            self.RefreshToolBar()
-        wx.CallAfter(self.RefreshTabCtrlEvent)
-        event.Skip()
-
-    def OnPageDragged(self, event):
+            # Refresh all window elements that have changed
+            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
         wx.CallAfter(self.RefreshTabCtrlEvent)
         event.Skip()
 
@@ -731,12 +799,8 @@
             new_values["creationDateTime"] = old_values["creationDateTime"]
             if new_values != old_values:
                 self.Controler.SetProjectProperties(None, new_values)
-                self.RefreshTitle()
-                self.RefreshFileMenu()
-                self.RefreshEditMenu()
-                self.RefreshDisplayMenu()
-                self.RefreshTypesTree()
-                self.RefreshScaling()
+                self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, 
+                              TYPESTREE, INSTANCESTREE, SCALING)
         dialog.Destroy()
 
     def OnCloseFrame(self, event):
@@ -744,83 +808,106 @@
             self.AUIManager.UnInit()
             self._onclose()
             event.Skip()
-        elif not self.Controler.ProjectIsSaved():
-            dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-            answer = dialog.ShowModal()
-            dialog.Destroy()
-            if answer == wx.ID_YES:
-                self.SaveProject()
-                event.Skip()
-            elif answer == wx.ID_NO:
-                self.Controler.Reset()
-                self.AUIManager.UnInit()
-                event.Skip()
+        elif self.CheckSaveBeforeClosing():
+            event.Skip()
+        else:
+            event.Veto()
+
+#-------------------------------------------------------------------------------
+#                            Notebook Unified Functions
+#-------------------------------------------------------------------------------
+
+    ## Function that generate bitmap for
+    # for wx.aui.AUINotebook.
+    #  @param window Panel to display in tab.
+    #  @param text title for the tab ctrl.
+    def GenerateBitmap(self, icon1_name, icon2_name = None):
+        # Find index of bitmap if already created
+        index = self.TabsImageListIndexes.get((icon1_name, icon2_name), None)
+        # Return index or bitmap if found
+        if index is not None:
+            if USE_AUI:
+                return self.TabsImageList.GetBitmap(index)
             else:
-                event.Veto()
-        else:
-            event.Skip()
-
-#-------------------------------------------------------------------------------
-#                            Notebook Unified Functions
-#-------------------------------------------------------------------------------
-
-    def GenerateBitmap(self, icon1_name, icon2_name = None):
-        if not USE_AUI:
-            index = self.TabsImageListIndexes.get((icon1_name, icon2_name), None)
-            if index is not None:
                 return index
         if icon2_name is None:
+            # Bitmap with only one icon
             tmp_bitmap = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon1_name))
         else:
+            # Bitmap with two icon
             icon1 = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon1_name))
             icon2 = wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%icon2_name))
+            # Calculate bitmap size
             width = icon1.GetWidth() + icon2.GetWidth() - 1
             height = max(icon1.GetHeight(), icon2.GetHeight())
+            # Create bitmap with both icons
             tmp_bitmap = wx.EmptyBitmap(width, height)
             dc = wx.MemoryDC()
             dc.SelectObject(tmp_bitmap)
             dc.Clear()
             dc.DrawBitmap(icon1, 0, 0)
             dc.DrawBitmap(icon2, icon1.GetWidth() - 1, 0)
+        # Store bitmap in ImageList
+        index = self.TabsImageList.Add(tmp_bitmap)
+        # Save bitmap index in ImageList in dictionary
+        self.TabsImageListIndexes[(icon1_name, icon2_name)] = index
         if USE_AUI:
             return tmp_bitmap
         else:
-            index = self.TabsImageList.Add(tmp_bitmap)
-            self.TabsImageListIndexes[(icon1_name, icon2_name)] = index
             return index
     
+    ## Function that add a tab in Notebook, calling refresh for tab DClick event
+    # for wx.aui.AUINotebook.
+    #  @param window Panel to display in tab.
+    #  @param text title for the tab ctrl.
     def AddPage(self, window, text):
         self.TabsOpened.AddPage(window, text)
         self.RefreshTabCtrlEvent()
     
-    def RefreshTabCtrlEvent(self):
-        if USE_AUI:
-            auitabctrl = []
-            for child in self.TabsOpened.GetChildren():
-                if isinstance(child, wx.aui.AuiTabCtrl):
-                    auitabctrl.append(child)
-                    if child not in self.AuiTabCtrl:
-                        child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child))
-            self.AuiTabCtrl = auitabctrl
-            if self.TabsOpened.GetPageCount() == 0:
-                pane = self.AUIManager.GetPane(self.TabsOpened)
-                if pane.IsMaximized():
-                    self.AUIManager.RestorePane(pane)
-                self.AUIManager.Update()
-    
+    ## Function that fix difference in deleting all tabs between 
+    # wx.Notebook and wx.aui.AUINotebook.
     def DeleteAllPages(self):
         if USE_AUI:
             for idx in xrange(self.TabsOpened.GetPageCount()):
                 self.TabsOpened.DeletePage(0)
         else:
             self.TabsOpened.DeleteAllPages()
-
+        self.RefreshTabCtrlEvent()
+
+    ## Function that fix difference in setting picture on tab between 
+    # wx.Notebook and wx.aui.AUINotebook.
+    #  @param idx Tab index.
+    #  @param bitmap wx.Bitmap to define on tab.
+    #  @return True if operation succeeded
     def SetPageBitmap(self, idx, bitmap):
         if USE_AUI:
             return self.TabsOpened.SetPageBitmap(idx, bitmap)
         else:
             return self.TabsOpened.SetPageImage(idx, bitmap)
 
+#-------------------------------------------------------------------------------
+#                         Dialog Message Functions
+#-------------------------------------------------------------------------------
+
+    ## Function displaying an Error dialog in PLCOpenEditor.
+    #  @param message The message to display.
+    def ShowErrorMessage(self, message):
+        dialog = wx.MessageDialog(self, message, "Error", wx.OK|wx.ICON_ERROR)
+        dialog.ShowModal()
+        dialog.Destroy()
+
+    ## Function displaying an Error dialog in PLCOpenEditor.
+    #  @return False if closing cancelled.
+    def CheckSaveBeforeClosing(self):
+        if not self.Controler.ProjectIsSaved():
+            dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+            answer = dialog.ShowModal()
+            dialog.Destroy()
+            if answer == wx.ID_YES:
+                self.SaveProject()
+            elif answer == wx.ID_CANCEL:
+                return False
+        return True
 
 #-------------------------------------------------------------------------------
 #                            File Menu Functions
@@ -871,23 +958,13 @@
         if dialog.ShowModal() == wx.ID_OK:
             properties = dialog.GetValues()
             self.Controler.CreateNewProject(properties)
-            self.RefreshTitle()
-            self.RefreshFileMenu()
-            self.RefreshEditMenu()
-            self.RefreshTypesTree()
-            self.RefreshInstancesTree()
-            self.RefreshLibraryTree()
+            self._Refresh(TITLE, FILEMENU, EDITMENU, TYPESTREE, INSTANCESTREE, 
+                          LIBRARYTREE)
         event.Skip()
 
     def OnOpenProjectMenu(self, event):
-        if not self.Controler.ProjectIsSaved():
-            dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-            answer = dialog.ShowModal()
-            dialog.Destroy()
-            if answer == wx.ID_YES:
-                self.SaveProject()
-            elif answer == wx.ID_CANCEL:
-                return
+        if not self.CheckSaveBeforeClosing():
+            return
         filepath = self.Controler.GetFilePath()
         if filepath != "":
             directory = os.path.dirname(filepath)
@@ -900,13 +977,8 @@
                 self.DeleteAllPages()
                 self.VariablePanelIndexer.RemoveAllPanels()
                 self.Controler.OpenXMLFile(filepath)
-                self.RefreshTypesTree()
-                self.RefreshInstancesTree()
-                self.RefreshLibraryTree()
-            self.RefreshTitle()
-            self.RefreshFileMenu()
-            self.RefreshEditMenu()
-            self.RefreshToolBar()
+                self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
+            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
         dialog.Destroy()
         event.Skip()
 
@@ -923,29 +995,18 @@
                 if not self.Debug:
                     tagname = self.TabsOpened.GetPage(new_index).GetTagName()
                     self.VariablePanelIndexer.ChangeVariablePanel(tagname)
-            self.RefreshFileMenu()
-            self.RefreshEditMenu()
-            self.RefreshToolBar()
+            self._Refresh(TOOLBAR, FILEMENU, EDITMENU)
         event.Skip()
     
     def OnCloseProjectMenu(self, event):
-        if not self.Controler.ProjectIsSaved():
-            dialog = wx.MessageDialog(self, "There are changes, do you want to save?", "Close Application", wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-            answer = dialog.ShowModal()
-            dialog.Destroy()
-            if answer == wx.ID_YES:
-                self.SaveProject()
-            elif answer == wx.ID_CANCEL:
-                return
+        if not self.CheckSaveBeforeClosing():
+            return
         self.DeleteAllPages()
         self.VariablePanelIndexer.RemoveAllPanels()
         self.TypesTree.DeleteAllItems()
         self.InstancesTree.DeleteAllItems()
         self.Controler.Reset()
-        self.RefreshTitle()
-        self.RefreshFileMenu()
-        self.RefreshEditMenu()
-        self.RefreshToolBar()
+        self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
         event.Skip()
 
     def OnSaveProjectMenu(self, event):
@@ -1002,13 +1063,9 @@
             if os.path.isdir(os.path.dirname(filepath)):
                 result = self.Controler.SaveXMLFile(filepath)
                 if not result:
-                    message = wx.MessageDialog(self, "Can't save project to file %s!"%filepath, "Error", wx.OK|wx.ICON_ERROR)
-                    message.ShowModal()
-                    message.Destroy()
+                    self.ShowErrorMessage("Can't save project to file %s!"%filepath)
             else:
-                message = wx.MessageDialog(self, "%s is not a valid folder!"%os.path.dirname(filepath), "Error", wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
+                self.ShowErrorMessage("\"%s\" is not a valid folder!"%os.path.dirname(filepath))
             self.RefreshTitle()
         dialog.Destroy()
 
@@ -1051,7 +1108,7 @@
             printout = GraphicPrintout(self.TabsOpened.GetPage(selected), page_size, margins)
             
             if not printer.Print(self, printout, True):
-                wx.MessageBox("There was a problem printing.\nPerhaps your current printer is not set correctly?", "Printing", wx.OK)
+                self.ShowErrorMessage("There was a problem printing.\nPerhaps your current printer is not set correctly?")
             printout.Destroy()
         event.Skip()
 
@@ -1101,8 +1158,7 @@
                 self.EditMenu.Enable(wx.ID_ADD, False)
                 self.EditMenu.Enable(wx.ID_DELETE, False)
     
-    def OnUndoMenu(self, event):
-        self.Controler.LoadPrevious()
+    def CloseTabsWithoutModel(self):
         idxs = range(self.TabsOpened.GetPageCount())
         idxs.reverse()
         for idx in idxs:
@@ -1110,70 +1166,32 @@
             if self.Controler.GetEditedElement(tagname, self.Debug) is None:
                 self.VariablePanelIndexer.RemoveVariablePanel(tagname)
                 self.TabsOpened.DeletePage(idx)
-        selected = self.TabsOpened.GetSelection()
-        if selected != -1:
-            window = self.TabsOpened.GetPage(selected)
-            window.RefreshView()
-            self.VariablePanelIndexer.RefreshVariablePanel(window.GetTagName())
-        self.RefreshTitle()
-        self.RefreshEditMenu()
-        self.RefreshTypesTree()
-        self.RefreshInstancesTree()
-        self.RefreshLibraryTree()
-        self.RefreshScaling()
+        
+    def OnUndoMenu(self, event):
+        self.Controler.LoadPrevious()
+        self.CloseTabsWithoutModel()
+        self.RefreshEditor()
+        self._Refresh(TITLE, EDITMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, 
+                      SCALING)
         event.Skip()
     
     def OnRedoMenu(self, event):
         self.Controler.LoadNext()
-        idxs = range(self.TabsOpened.GetPageCount())
-        idxs.reverse()
-        for idx in idxs:
-            tagname = self.TabsOpened.GetPage(idx).GetTagName()
-            if self.Controler.GetEditedElement(tagname, self.Debug) is None:
-                self.VariablePanelIndexer.RemoveVariablePanel(tagname)
-                self.TabsOpened.DeletePage(idx)
-        selected = self.TabsOpened.GetSelection()
-        if selected != -1:
-            window = self.TabsOpened.GetPage(selected)
-            window.RefreshView()
-            self.VariablePanelIndexer.RefreshVariablePanel(window.GetTagName())
-        self.RefreshTitle()
-        self.RefreshEditMenu()
-        self.RefreshTypesTree()
-        self.RefreshInstancesTree()
-        self.RefreshLibraryTree()
-        self.RefreshScaling()
-        event.Skip()
-
+        self.CloseTabsWithoutModel()
+        self.RefreshEditor()
+        self._Refresh(TITLE, EDITMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, 
+                      SCALING)
+        event.Skip()
+    
     def OnEnableUndoRedoMenu(self, event):
         self.Controler.EnableProjectBuffer(event.IsChecked())
         self.RefreshEditMenu()
         event.Skip()
 
-    def OnCutMenu(self, event):
-        control = self.FindFocus()
-        if isinstance(control, (Viewer, TextViewer)):
-            control.Cut()
-        elif isinstance(control, wx.TextCtrl):
-            control.ProcessEvent(event)
-        event.Skip()
-    
-    def OnCopyMenu(self, event):
-        control = self.FindFocus()
-        if isinstance(control, (Viewer, TextViewer)):
-            control.Copy()
-        elif isinstance(control, wx.TextCtrl):
-            control.ProcessEvent(event)
-        event.Skip()
-    
-    def OnPasteMenu(self, event):
-        control = self.FindFocus()
-        if isinstance(control, (Viewer, TextViewer)):
-            control.Paste()
-        elif isinstance(control, wx.TextCtrl):
-            control.ProcessEvent(event)
-        event.Skip()
-    
+    OnCutMenu = GetShortcutKeyCallbackFunction("Cut")
+    OnCopyMenu = GetShortcutKeyCallbackFunction("Copy")
+    OnPasteMenu = GetShortcutKeyCallbackFunction("Paste")
+
     def OnSelectAllMenu(self, event):
         control = self.FindFocus()
         if isinstance(control, (Viewer, TextViewer)):
@@ -1184,70 +1202,27 @@
             control.SetMark(0, control.GetLastPosition() + 1)
         event.Skip()
     
+    DeleteFunctions = {
+        ITEM_DATATYPE: GetDeleteElementFunction(PLCControler.ProjectRemoveDataType, check_function=PLCControler.DataTypeIsUsed),
+        ITEM_POU: GetDeleteElementFunction(PLCControler.ProjectRemovePou, check_function=PLCControler.PouIsUsed),
+        ITEM_TRANSITION: GetDeleteElementFunction(PLCControler.ProjectRemovePouTransition, ITEM_POU),
+        ITEM_ACTION: GetDeleteElementFunction(PLCControler.ProjectRemovePouAction, ITEM_POU),
+        ITEM_CONFIGURATION: GetDeleteElementFunction(PLCControler.ProjectRemoveConfiguration),
+        ITEM_RESOURCE: GetDeleteElementFunction(PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION)
+    }
+    
     def OnDeleteMenu(self, event):
         window = self.FindFocus()
-        if window == self.TypesTree:
+        if window == self.TypesTree or window is None:
             selected = self.TypesTree.GetSelection()
             if selected.IsOk():
                 type = self.TypesTree.GetPyData(selected)
-                tagname = ""
-                if type == ITEM_DATATYPE:
-                    name = self.TypesTree.GetItemText(selected)
-                    if not self.Controler.DataTypeIsUsed(name, self.Debug):
-                        self.Controler.ProjectRemoveDataType(name)
-                        tagname = self.Controler.ComputeDataTypeName(name)
-                    else:
-                        message = wx.MessageDialog(self, "\"%s\" is used by one or more POUs. It can't be removed!"%name, "Error", wx.OK|wx.ICON_ERROR)
-                        message.ShowModal()
-                        message.Destroy()
-                elif type == ITEM_POU:
-                    name = self.TypesTree.GetItemText(selected)
-                    if not self.Controler.PouIsUsed(name, self.Debug):
-                        self.Controler.ProjectRemovePou(name)
-                        tagname = self.Controler.ComputePouName(name)
-                    else:
-                        message = wx.MessageDialog(self, "\"%s\" is used by one or more POUs. It can't be removed!"%name, "Error", wx.OK|wx.ICON_ERROR)
-                        message.ShowModal()
-                        message.Destroy()
-                elif type in [ITEM_TRANSITION, ITEM_ACTION]:
-                    item = self.TypesTree.GetItemParent(selected)
-                    item_type = self.TypesTree.GetPyData(item)
-                    while item_type != ITEM_POU:
-                        item = self.TypesTree.GetItemParent(item)
-                        item_type = self.TypesTree.GetPyData(item)
-                    pou_name = self.TypesTree.GetItemText(item)
-                    if type == ITEM_TRANSITION:
-                        transition = self.TypesTree.GetItemText(selected)
-                        self.Controler.ProjectRemovePouTransition(pou_name, transition)
-                        tagname = self.Controler.ComputePouTransitionName(pou_name, transition)
-                    elif type == ITEM_ACTION:
-                        action = self.TypesTree.GetItemText(selected)
-                        self.Controler.ProjectRemovePouAction(pou_name, action)
-                        tagname = self.Controler.ComputePouActionName(pou_name, action)
-                elif type == ITEM_CONFIGURATION:
-                    name = self.TypesTree.GetItemText(selected)
-                    self.Controler.ProjectRemoveConfiguration(name)
-                    tagname = self.Controler.ComputeConfigurationName(name)
-                elif type == ITEM_RESOURCE:
-                    resource = self.TypesTree.GetItemText(selected)
-                    item = self.TypesTree.GetItemParent(selected)
-                    item_type = self.TypesTree.GetPyData(item)
-                    while item_type != ITEM_CONFIGURATION:
-                        item = self.TypesTree.GetItemParent(item)
-                        item_type = self.TypesTree.GetPyData(item)
-                    config_name = self.TypesTree.GetItemText(item)
-                    self.Controler.ProjectRemoveConfigurationResource(config_name, resource)
-                    tagname = self.Controler.ComputeConfigurationResourceName(config_name, selected)
-                idx = self.IsOpened(tagname)
-                if idx is not None:
-                    self.VariablePanelIndexer.RemoveVariablePanel(tagname)
-                    self.TabsOpened.DeletePage(idx)
-                self.RefreshTitle()
-                self.RefreshEditMenu()
-                self.RefreshTypesTree()
-                self.RefreshInstancesTree()
-                self.RefreshLibraryTree()
-                self.RefreshToolBar()
+                function = self.DeleteFunctions.get(type, None)
+                if function is not None:
+                    function(self, selected)
+                    self.CloseTabsWithoutModel()
+                    self._Refresh(TITLE, TOOLBAR, EDITMENU, TYPESTREE, 
+                                  INSTANCESTREE, LIBRARYTREE)
         elif isinstance(window, (Viewer, TextViewer)):
             event = wx.KeyEvent(wx.EVT_CHAR._getEvtType())
             event.m_keyCode = wx.WXK_DELETE
@@ -1287,12 +1262,7 @@
             self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False)
         
     def OnRefreshMenu(self, event):
-        selected = self.TabsOpened.GetSelection()
-        if selected != -1:
-            window = self.TabsOpened.GetPage(selected)
-            window.RefreshView()
-            if not self.Debug:
-                self.VariablePanelIndexer.RefreshVariablePanel(window.GetTagName())
+        self.RefreshEditor(not self.Debug)
         event.Skip()
 
     def OnClearErrorsMenu(self, event):
@@ -1315,6 +1285,25 @@
 #                      Project Editor Panels Management Functions
 #-------------------------------------------------------------------------------
     
+    def OnPageDragged(self, event):
+        wx.CallAfter(self.RefreshTabCtrlEvent)
+        event.Skip()
+    
+    def RefreshTabCtrlEvent(self):
+        if USE_AUI:
+            auitabctrl = []
+            for child in self.TabsOpened.GetChildren():
+                if isinstance(child, wx.aui.AuiTabCtrl):
+                    auitabctrl.append(child)
+                    if child not in self.AuiTabCtrl:
+                        child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child))
+            self.AuiTabCtrl = auitabctrl
+            if self.TabsOpened.GetPageCount() == 0:
+                pane = self.AUIManager.GetPane(self.TabsOpened)
+                if pane.IsMaximized():
+                    self.AUIManager.RestorePane(pane)
+                self.AUIManager.Update()
+    
     def OnPouSelectedChanged(self, event):
         old_selected = self.TabsOpened.GetSelection()
         if old_selected >= 0:
@@ -1332,10 +1321,7 @@
             window.RefreshView()
             if not self.Debug:
                 self.VariablePanelIndexer.ChangeVariablePanel(window.GetTagName())
-            self.RefreshFileMenu()
-            self.RefreshEditMenu()
-            self.RefreshDisplayMenu()
-            self.RefreshToolBar()
+            self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, TOOLBAR)
         event.Skip()
 
     def RefreshEditor(self, variablepanel = True):
@@ -1412,6 +1398,9 @@
         self.GenerateTypesTreeBranch(root, infos)
         self.TypesTree.Expand(root)
 
+    def ResetSelectedItem(self):
+        self.SelectedItem = None
+
     def GenerateTypesTreeBranch(self, root, infos, topology=False):
         to_delete = []
         self.TypesTree.SetItemText(root, infos["name"])
@@ -1423,9 +1412,9 @@
             self.TypesTree.SetItemBackgroundColour(root, wx.WHITE)
             self.TypesTree.SetItemTextColour(root, wx.BLACK)
         if infos["type"] == ITEM_POU:
-            self.TypesTree.SetItemImage(root, self.TypesTreeImageDict[self.Controler.GetPouBodyType(infos["name"], self.Debug)])
-        else:
-            self.TypesTree.SetItemImage(root, self.TypesTreeImageDict[infos["type"]])      
+            self.TypesTree.SetItemImage(root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"], self.Debug)])
+        else:
+            self.TypesTree.SetItemImage(root, self.TreeImageDict[infos["type"]])      
             
         if wx.VERSION >= (2, 6, 0):
             item, root_cookie = self.TypesTree.GetFirstChild(root)
@@ -1543,12 +1532,7 @@
                         self.RefreshLibraryTree()
                         self.RefreshPageTitles()
                 elif itemtype == ITEM_TRANSITION:
-                    parent = self.TypesTree.GetItemParent(item)
-                    parent_type = self.TypesTree.GetPyData(parent)
-                    while parent_type != ITEM_POU:
-                        parent = self.TypesTree.GetItemParent(parent)
-                        parent_type = self.TypesTree.GetPyData(parent)
-                    pou_name = self.TypesTree.GetItemText(parent)
+                    pou_name = GetParentName(self.TypesTree, item, ITEM_POU)
                     if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
                         message = "A pou with \"%s\" as name exists!"%new_name
                     elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables(pou_name, self.Debug) if name != old_name]:
@@ -1559,12 +1543,7 @@
                                                 self.Controler.ComputePouTransitionName(pou_name, new_name))
                         self.RefreshPageTitles()
                 elif itemtype == ITEM_ACTION:
-                    parent = self.TypesTree.GetItemParent(item)
-                    parent_type = self.TypesTree.GetPyData(parent)
-                    while parent_type != ITEM_POU:
-                        parent = self.TypesTree.GetItemParent(parent)
-                        parent_type = self.TypesTree.GetPyData(parent)
-                    pou_name = self.TypesTree.GetItemText(parent)
+                    pou_name = GetParentName(self.TypesTree, item, ITEM_POU)
                     if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]:
                         message = "A pou with \"%s\" as name exists!"%new_name
                     elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables(pou_name, self.Debug) if name != old_name]:
@@ -1594,12 +1573,7 @@
                                                 self.Controler.ComputeConfigurationName(new_name))
                         self.RefreshPageTitles()
                 elif itemtype == ITEM_RESOURCE:
-                    parent = self.TypesTree.GetItemParent(item)
-                    parent_type = self.TypesTree.GetPyData(parent)
-                    while parent_type != ITEM_CONFIGURATION:
-                        parent = self.TypesTree.GetItemParent(parent)
-                        parent_type = self.TypesTree.GetPyData(parent)
-                    config_name = self.TypesTree.GetItemText(parent)
+                    config_name = GetParentName(self.TypesTree, item, ITEM_CONFIGURATION)
                     if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames(self.Debug)]:
                         message = "\"%s\" config already exists!"%new_name
                         abort = True
@@ -1620,18 +1594,14 @@
                         self.RefreshPageTitles()
             if message or abort:
                 if message:
-                    messageDialog = wx.MessageDialog(self, message, "Error", wx.OK|wx.ICON_ERROR)
-                    messageDialog.ShowModal()
-                    messageDialog.Destroy()
+                    self.ShowErrorMessage(message)
                 item = event.GetItem()
                 wx.CallAfter(self.TypesTree.EditLabel, item)
                 event.Veto()
             else:
                 wx.CallAfter(self.RefreshTypesTree)
                 self.RefreshEditor()
-                self.RefreshFileMenu()
-                self.RefreshEditMenu()
-                self.RefreshTitle()
+                self._Refresh(TITLE, FILEMENU, EDITMENU)
                 event.Skip()
     
     def OnTypesTreeItemActivated(self, event):
@@ -1647,20 +1617,10 @@
         elif data == ITEM_CONFIGURATION:
             self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name))
         elif data == ITEM_RESOURCE:
-            item = self.TypesTree.GetItemParent(selected)
-            item_type = self.TypesTree.GetPyData(item)
-            while item_type != ITEM_CONFIGURATION:
-                item = self.TypesTree.GetItemParent(item)
-                item_type = self.TypesTree.GetPyData(item)
-            config_name = self.TypesTree.GetItemText(item)
+            config_name = GetParentName(self.TypesTree, selected, ITEM_CONFIGURATION)
             self.EditProjectElement(data, self.Controler.ComputeConfigurationResourceName(config_name, name))
         elif data in [ITEM_TRANSITION, ITEM_ACTION]:
-            item = self.TypesTree.GetItemParent(selected)
-            item_type = self.TypesTree.GetPyData(item)
-            while item_type != ITEM_POU:
-                item = self.TypesTree.GetItemParent(item)
-                item_type = self.TypesTree.GetPyData(item)
-            pou_name = self.TypesTree.GetItemText(item)
+            pou_name = GetParentName(self.TypesTree, selected, ITEM_POU)
             if data == ITEM_TRANSITION:
                 tagname = self.Controler.ComputePouTransitionName(pou_name, name)
             elif data == ITEM_ACTION:
@@ -1678,20 +1638,10 @@
         elif data == ITEM_CONFIGURATION:
             self.EditProjectElement(data, self.Controler.ComputeConfigurationName(name), True)
         elif data == ITEM_RESOURCE:
-            item = self.TypesTree.GetItemParent(select_item)
-            item_type = self.TypesTree.GetPyData(item)
-            while item_type != ITEM_CONFIGURATION:
-                item = self.TypesTree.GetItemParent(item)
-                item_type = self.TypesTree.GetPyData(item)
-            config_name = self.TypesTree.GetItemText(item)
+            config_name = GetParentName(self.TypesTree, select_item, ITEM_CONFIGURATION)
             self.EditProjectElement(data, self.Controler.ComputeConfigurationResourceName(config_name, name), True)
         elif data in [ITEM_TRANSITION, ITEM_ACTION]:
-            item = self.TypesTree.GetItemParent(select_item)
-            item_type = self.TypesTree.GetPyData(item)
-            while item_type != ITEM_POU:
-                item = self.TypesTree.GetItemParent(item)
-                item_type = self.TypesTree.GetPyData(item)
-            pou_name = self.TypesTree.GetItemText(item)
+            pou_name = GetParentName(self.TypesTree, select_item, ITEM_POU)
             if data == ITEM_TRANSITION:
                 tagname = self.Controler.ComputePouTransitionName(pou_name, name)
             elif data == ITEM_ACTION:
@@ -1724,12 +1674,9 @@
                 if old_selected >= 0:
                     self.TabsOpened.GetPage(old_selected).ResetBuffer()
                 self.TabsOpened.SetSelection(openedidx)
-            self.TabsOpened.GetPage(openedidx).RefreshView()
             self.VariablePanelIndexer.ChangeVariablePanel(tagname)
             self.RefreshPageTitles()
-            self.RefreshFileMenu()
-            self.RefreshEditMenu()
-            self.RefreshToolBar()
+            self._Refresh(FILEMENU, EDITMENU, TOOLBAR)
         elif not onlyopened:
             if elementtype == ITEM_CONFIGURATION:
                 new_window = ConfigurationEditor(self.TabsOpened, tagname, self, self.Controler)
@@ -1775,9 +1722,7 @@
                     self.TabsOpened.SetSelection(i)
                     window.SetFocus()
                     self.RefreshPageTitles()
-                    self.RefreshFileMenu()
-                    self.RefreshEditMenu()
-                    self.RefreshToolBar()
+                    self._Refresh(FILEMENU, EDITMENU, TOOLBAR)
     
     def OnTypesTreeRightUp(self, event):
         if wx.Platform == '__WXMSW__':
@@ -1815,7 +1760,7 @@
             self.Bind(wx.EVT_MENU, self.OnRenamePouMenu, id=new_id)
             new_id = wx.NewId()
             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text="Delete")
-            self.Bind(wx.EVT_MENU, self.OnRemovePouMenu, id=new_id)
+            self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id)
             self.PopupMenu(menu)
         elif type == ITEM_CONFIGURATION:
             menu = wx.Menu(title='')
@@ -1824,20 +1769,13 @@
             self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(name), id=new_id)
             new_id = wx.NewId()
             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text="Delete")
-            self.Bind(wx.EVT_MENU, self.OnRemoveConfigurationMenu, id=new_id)
+            self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id)
             self.PopupMenu(menu)
         elif type in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]:
             menu = wx.Menu(title='')
             new_id = wx.NewId()
             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text="Delete")
-            if type == ITEM_DATATYPE:
-                self.Bind(wx.EVT_MENU, self.OnRemoveDataTypeMenu, id=new_id)
-            elif type == ITEM_TRANSITION:
-                self.Bind(wx.EVT_MENU, self.OnRemoveTransitionMenu, id=new_id)
-            elif type == ITEM_ACTION:
-                self.Bind(wx.EVT_MENU, self.OnRemoveActionMenu, id=new_id)
-            elif type == ITEM_RESOURCE:
-                self.Bind(wx.EVT_MENU, self.OnRemoveResourceMenu, id=new_id)
+            self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id)
             self.PopupMenu(menu)
         elif type in ITEMS_UNEDITABLE:
             if name == "Data Types":
@@ -1913,7 +1851,7 @@
         else:
             self.InstancesTree.SetItemText(root, infos["name"])
         self.InstancesTree.SetPyData(root, (infos["type"], infos.get("tagname", None)))
-        self.InstancesTree.SetItemImage(root, self.InstancesTreeImageDict[infos["type"]])      
+        self.InstancesTree.SetItemImage(root, self.TreeImageDict[infos["type"]])
             
         if wx.VERSION >= (2, 6, 0):
             item, root_cookie = self.InstancesTree.GetFirstChild(root)
@@ -2642,7 +2580,18 @@
         for i in xrange(self.TabsOpened.GetPageCount()):
             viewer = self.TabsOpened.GetPage(i)
             viewer.ClearErrors()
-        
+
+    RefreshFunctions = {
+        TITLE : RefreshTitle,
+        TOOLBAR : RefreshToolBar,
+        FILEMENU : RefreshFileMenu,
+        EDITMENU : RefreshEditMenu,
+        DISPLAYMENU : RefreshDisplayMenu,
+        TYPESTREE : RefreshTypesTree,
+        INSTANCESTREE : RefreshInstancesTree, 
+        LIBRARYTREE : RefreshLibraryTree,
+        SCALING : RefreshScaling}
+
 current_num = 0
 def GetNewNum():
     global current_num
@@ -4543,7 +4492,7 @@
         self.Table.ResetView(self.VariablesGrid)
 
 #-------------------------------------------------------------------------------
-#                            Variables Editor Panel
+#                            Debug Variables Panel
 #-------------------------------------------------------------------------------
 
 class VariableTableItem(DebugDataConsumer):