Adding support for automatically saving and restoring state of frame or project perspective
authorlaurent
Fri, 27 Apr 2012 02:00:47 +0200
changeset 673 b686f0081e2b
parent 672 d751b1c609b3
child 674 bbffe4110141
Adding support for automatically saving and restoring state of frame or project perspective
PLCOpenEditor.py
--- a/PLCOpenEditor.py	Fri Apr 27 01:55:45 2012 +0200
+++ b/PLCOpenEditor.py	Fri Apr 27 02:00:47 2012 +0200
@@ -138,6 +138,9 @@
  ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, 
 ] = [wx.NewId() for _init_coll_EditMenu_Items in range(7)]
 
+# Define PLCOpenEditor DisplayMenu extra items id
+[ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, 
+] = [wx.NewId() for _init_coll_DisplayMenu_Items in range(1)]
 
 #-------------------------------------------------------------------------------
 #                            EditorToolBar definitions
@@ -302,6 +305,37 @@
             self.ShowErrorMessage(_("\"%s\" is used by one or more POUs. It can't be removed!")%name)
     return DeleteElementFunction
 
+def ComputeTabsOrganization(tabs, rect):
+    if len(tabs) == 1:
+        return tabs[0]
+    split = None
+    for idx, tab in enumerate(tabs):
+        if tab["size"][0] == rect.width:
+            if tab["pos"][1] == rect.y:
+                split = (wx.TOP, float(tab["size"][1]) / float(rect.height))
+                split_rect = wx.Rect(rect.x, rect.y + tab["size"][1] + 7, 
+                                     rect.width, rect.height - tab["size"][1] - 7)
+            elif tab["pos"][1] == rect.height + 1 - tab["size"][1]:
+                split = (wx.BOTTOM, 1.0 - float(tab["size"][1]) / float(rect.height))
+                split_rect = wx.Rect(rect.x, rect.y, 
+                                     rect.width, rect.height - tab["size"][1] - 7)
+            break
+        elif tab["size"][1] == rect.height:
+            if tab["pos"][0] == rect.x:
+                split = (wx.LEFT, float(tab["size"][0]) / float(rect.width))
+                split_rect = wx.Rect(rect.x + tab["size"][0] + 7, rect.y, 
+                                     rect.width - tab["size"][0] - 7, rect.height)
+            elif tab["pos"][0] == rect.width + 1 - tab["size"][0]:
+                split = (wx.RIGHT, 1.0 - float(tab["size"][0]) / float(rect.width))
+                split_rect = wx.Rect(rect.x, rect.y, 
+                                     rect.width - tab["size"][0] - 7, rect.height)
+            break
+    if split != None:
+        split_tab = tabs.pop(idx)
+        return {"split": split,
+                "tab": split_tab, 
+                "others": ComputeTabsOrganization(tabs, split_rect)}
+    return tabs
 
 #-------------------------------------------------------------------------------
 #                              IDEFrame Base Class
@@ -407,10 +441,17 @@
             AppendMenu(zoommenu, help='', id=new_id,
                   kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%")
             self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), id=new_id)
+        
+        if USE_AUI:
+            parent.AppendSeparator()
+            AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE,
+                  kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective'))
+            self.Bind(wx.EVT_MENU, self.OnResetPerspective, id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE)
+        
         self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH)
         if self.EnableDebug:
             self.Bind(wx.EVT_MENU, self.OnClearErrorsMenu, id=wx.ID_CLEAR)
-
+            
     def _init_coll_HelpMenu_Items(self, parent):
         pass
 
@@ -450,6 +491,7 @@
               style=wx.DEFAULT_FRAME_STYLE)
         self.SetClientSize(wx.Size(1000, 600))
         self.Bind(wx.EVT_ACTIVATE, self.OnActivated)
+        self.Bind(wx.EVT_SIZE, self.OnResize)
         
         self.TabsImageList = wx.ImageList(31, 16)
         self.TabsImageListIndexes = {}
@@ -469,16 +511,18 @@
             self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, 
                     self.OnAllowNotebookDnD)
             self.AUIManager.AddPane(self.LeftNoteBook, 
-                  wx.aui.AuiPaneInfo().Caption(_("Project")).Left().Layer(1).
+                  wx.aui.AuiPaneInfo().Name("ProjectPane").
+                  Caption(_("Project")).Left().Layer(1).
                   BestSize(wx.Size(300, 500)).CloseButton(False))
-        
+            
             self.BottomNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORBOTTOMNOTEBOOK,
                   style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE|
                         wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)
             self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, 
                     self.OnAllowNotebookDnD)
             self.AUIManager.AddPane(self.BottomNoteBook, 
-                  wx.aui.AuiPaneInfo().Bottom().Layer(0).
+                  wx.aui.AuiPaneInfo().Name("ResultPane").
+                  Bottom().Layer(0).
                   BestSize(wx.Size(800, 300)).CloseButton(False))
             
             self.RightNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORRIGHTNOTEBOOK,
@@ -487,7 +531,8 @@
             self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, 
                     self.OnAllowNotebookDnD)
             self.AUIManager.AddPane(self.RightNoteBook, 
-                  wx.aui.AuiPaneInfo().Right().Layer(0).
+                  wx.aui.AuiPaneInfo().Name("LibraryPane").
+                  Right().Layer(0).
                   BestSize(wx.Size(250, 400)).CloseButton(False))
             
             self.TabsOpened = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORTABSOPENED, 
@@ -500,7 +545,8 @@
                     self.OnPageClose)
             self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG,
                     self.OnPageDragged)
-            self.AUIManager.AddPane(self.TabsOpened, wx.aui.AuiPaneInfo().CentrePane())
+            self.AUIManager.AddPane(self.TabsOpened, 
+                  wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane"))
         
         else:
             self.MainSplitter = wx.SplitterWindow(id=ID_PLCOPENEDITORMAINSPLITTER,
@@ -553,9 +599,11 @@
             self.ThirdSplitter.SplitVertically(self.TabsOpened, self.RightNoteBook, -250)
         
         #-----------------------------------------------------------------------
-        #                       Creating PLCopen Project tree
+        #                    Creating PLCopen Project Types Tree
         #-----------------------------------------------------------------------
         
+        self.MainTabs = {}
+        
         self.TypesTree = wx.TreeCtrl(id=ID_PLCOPENEDITORTYPESTREE,
                   name='TypesTree', parent=self.LeftNoteBook, 
                   pos=wx.Point(0, 0), size=wx.Size(0, 0),
@@ -582,10 +630,12 @@
               id=ID_PLCOPENEDITORTYPESTREE)
         self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTypesTreeItemActivated,
               id=ID_PLCOPENEDITORTYPESTREE)
-        self.LeftNoteBook.AddPage(self.TypesTree, _("Types"))
-
+        
+        self.MainTabs["TypesTree"] = (self.TypesTree, _("Types"))
+        self.LeftNoteBook.AddPage(*self.MainTabs["TypesTree"])
+        
         #-----------------------------------------------------------------------
-        #                       Creating PLCopen Project tree
+        #                  Creating PLCopen Project Instances Tree
         #-----------------------------------------------------------------------
         
         self.InstancesTree = wx.TreeCtrl(id=ID_PLCOPENEDITORINSTANCESTREE,
@@ -601,12 +651,14 @@
                   id=ID_PLCOPENEDITORINSTANCESTREE)
             self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnInstancesTreeItemActivated,
                   id=ID_PLCOPENEDITORINSTANCESTREE)
-        self.LeftNoteBook.AddPage(self.InstancesTree, _("Instances"))
+        
+        self.MainTabs["InstancesTree"] = (self.InstancesTree, _("Instances"))
+        self.LeftNoteBook.AddPage(*self.MainTabs["InstancesTree"])
         
         #-----------------------------------------------------------------------
         #                            Creating Tool Bar
         #-----------------------------------------------------------------------
-
+        
         if USE_AUI:
             MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, wx.DefaultPosition, wx.DefaultSize,
                     wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
@@ -641,13 +693,23 @@
         self.Bind(wx.EVT_MENU, self.OnSelectionTool, 
               id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION)
         
+        #-----------------------------------------------------------------------
+        #                            Creating Search Panel
+        #-----------------------------------------------------------------------
+        
         self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self)
-        self.BottomNoteBook.AddPage(self.SearchResultPanel, _("Search"))
-
+        self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search"))
+        self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"])
+        
+        #-----------------------------------------------------------------------
+        #                            Creating Library Panel
+        #-----------------------------------------------------------------------
+        
         self.LibraryPanel = wx.Panel(id=ID_PLCOPENEDITORLIBRARYPANEL,
               name='LibraryPanel', parent=self.RightNoteBook, pos=wx.Point(0,
               0), size=wx.Size(0, 0), style=0)
-        self.RightNoteBook.AddPage(self.LibraryPanel, _("Library"))
+        self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library"))
+        self.RightNoteBook.AddPage(*self.MainTabs["LibraryPanel"])
         
         self.LibraryTree = wx.TreeCtrl(id=ID_PLCOPENEDITORLIBRARYTREE,
                   name='LibraryTree', parent=self.LibraryPanel, 
@@ -670,7 +732,8 @@
         
         if self.EnableDebug:
             self.DebugVariablePanel = DebugVariablePanel(self.RightNoteBook, self.Controler)
-            self.RightNoteBook.AddPage(self.DebugVariablePanel, _("Debugger"))
+            self.MainTabs["DebugVariablePanel"] = (self.DebugVariablePanel, _("Debugger"))
+            self.RightNoteBook.AddPage(*self.MainTabs["DebugVariablePanel"])
         
         if USE_AUI:
             self.AUIManager.Update()
@@ -737,6 +800,8 @@
         #self.DrawingMode = DRIVENDRAWING_MODE
         if USE_AUI:
             self.AuiTabCtrl = []
+        self.Starting = False
+        self.DefaultPerspective = None
         
         # Initialize Printing configuring elements
         self.PrintData = wx.PrintData()
@@ -750,30 +815,199 @@
     
     def Show(self):
         wx.Frame.Show(self)
-        wx.CallAfter(self.RestoreFrameSize)
+        wx.CallAfter(self.RestoreLastState)
     
     def OnActivated(self, event):
         if event.GetActive():
             wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
         event.Skip()
-    
-    def RestoreFrameSize(self):
+
+#-------------------------------------------------------------------------------
+#                Saving and restoring frame organization functions
+#-------------------------------------------------------------------------------
+
+    def OnResize(self, event):
+        if self.Starting:
+            self.RestoreLastOrganization()
+            self.Starting = False
+            self.RefreshEditor()
+        event.Skip()
+    
+    def GetTabInfos(self, tab):
+        if isinstance(tab, EditorPanel):
+            if tab.IsDebugging():
+                return ("debug", tab.GetInstancePath())
+            else:
+                return ("editor", tab.GetTagName())
+        else:
+            for page_name, (page_ref, page_title) in self.MainTabs.iteritems():
+                if page_ref == tab:
+                    return ("main", page_name)
+        return None
+        
+    def SaveTabOrganization(self, notebook):
+        tabs = []
+        for child in notebook.GetChildren():
+            if isinstance(child, wx.aui.AuiTabCtrl):
+                pos = child.GetPosition()
+                tab = {"pos": (pos.x, pos.y), "pages": []}
+                tab_size = child.GetSize()
+                for page_idx in xrange(child.GetPageCount()):
+                    page = child.GetWindowFromIdx(page_idx)
+                    if not tab.has_key("size"):
+                        tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1])
+                    tab_infos = self.GetTabInfos(page)
+                    if tab_infos is not None:
+                        tab["pages"].append((tab_infos, page_idx == child.GetActivePage()))
+                tabs.append(tab)
+        tabs.sort(lambda x, y: cmp(x["pos"], y["pos"]))
+        size = notebook.GetSize()
+        return ComputeTabsOrganization(tabs, wx.Rect(1, 1, size[0] - 2, size[1] - 2))
+    
+    def LoadTab(self, notebook, page_infos):
+        if page_infos[0] == "main":
+            infos = self.MainTabs.get(page_infos[1])
+            if infos is not None:
+                page_ref, page_title = infos
+                notebook.AddPage(page_ref, page_title)
+                return notebook.GetPageIndex(page_ref)
+        elif page_infos[0] == "editor":
+            tagname = page_infos[1]
+            page_ref = self.EditProjectElement(self.Controler.GetElementType(tagname), tagname)
+            page_ref.RefreshView()
+            return notebook.GetPageIndex(page_ref)
+        elif page_infos[0] == "debug":
+            instance_path = page_infos[1]
+            item = self.GetInstancesTreeItem(self.InstancesTree.GetRootItem(), instance_path)
+            item_infos = self.InstancesTree.GetPyData(item)
+            if item_infos[1] is not None:
+                instance_type = item_infos[1]
+            else:
+                instance_type = self.InstancesTree.GetItemText(item).split(" (")[1].split(")")[0]
+            return notebook.GetPageIndex(self.OpenDebugViewer(item_infos[0], instance_path, instance_type))
+        return None
+            
+    def LoadTabOrganization(self, notebook, tabs, mode="all", first_index=None):
+        if isinstance(tabs, ListType):
+            if len(tabs) == 0:
+                return
+            raise ValueError, "Not supported"
+        
+        if tabs.has_key("split"):
+            self.LoadTabOrganization(notebook, tabs["others"])
+            
+            split_dir, split_ratio = tabs["split"]
+            first_index = self.LoadTabOrganization(notebook, tabs["tab"], mode="first")
+            notebook.Split(first_index, split_dir)
+            self.LoadTabOrganization(notebook, tabs["tab"], mode="others", first_index=first_index)
+            
+        elif mode == "first":
+            return self.LoadTab(notebook, tabs["pages"][0][0])
+        else:
+            selected = first_index
+            if mode == "others":
+                add_tabs = tabs["pages"][1:]
+            else:
+                add_tabs = tabs["pages"]
+            for page_infos, page_selected in add_tabs:
+                page_idx = self.LoadTab(notebook, page_infos)
+                if page_selected:
+                    selected = page_idx
+            if selected is not None:
+                wx.CallAfter(notebook.SetSelection, selected)
+        
+    def RestoreLastState(self):
         frame_size = None
         if self.Config.HasEntry("framesize"):
             frame_size = cPickle.loads(str(self.Config.Read("framesize")))
         
+        self.Starting = True
         if frame_size is None:
             self.Maximize()
         else:
             self.SetClientSize(frame_size)
-    
-    def SaveFrameSize(self):
+        
+    def RestoreLastOrganization(self):
+        if USE_AUI:
+            notebooks = {}
+            for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
+                                         (self.BottomNoteBook, "bottomnotebook"),
+                                         (self.RightNoteBook, "rightnotebook")]:
+                notebooks[entry_name] = self.SaveTabOrganization(notebook)
+            self.DefaultPerspective = {
+                "perspective": self.AUIManager.SavePerspective(),
+                "notebooks": notebooks,
+            }
+            
+            if self.Config.HasEntry("perspective"):
+                self.AUIManager.LoadPerspective(str(self.Config.Read("perspective")))
+        
+            if self.Config.HasEntry("notebooks"):
+                notebooks = cPickle.loads(str(self.Config.Read("notebooks")))
+                
+                for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
+                    for idx in xrange(notebook.GetPageCount()):
+                        notebook.RemovePage(0)
+                        
+                for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
+                                             (self.BottomNoteBook, "bottomnotebook"),
+                                             (self.RightNoteBook, "rightnotebook")]:
+                    self.LoadTabOrganization(notebook, notebooks.get(entry_name))
+    
+            self.LoadProjectOrganization()
+    
+    def SaveLastState(self):
         if not self.IsMaximized():
             self.Config.Write("framesize", cPickle.dumps(self.GetClientSize()))
         elif self.Config.HasEntry("framesize"):
             self.Config.DeleteEntry("framesize")
+        
+        if USE_AUI:
+            notebooks = {}
+            for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
+                                         (self.BottomNoteBook, "bottomnotebook"),
+                                         (self.RightNoteBook, "rightnotebook")]:
+                notebooks[entry_name] = self.SaveTabOrganization(notebook)
+            self.Config.Write("notebooks", cPickle.dumps(notebooks))
+            
+            self.Config.Write("perspective", self.AUIManager.SavePerspective())
+        
+            self.SaveProjectOrganization()
+        
         self.Config.Flush()
 
+    def SaveProjectOrganization(self):
+        if USE_AUI and self.Controler is not None:
+            tabs = []
+            
+            if self.Config.HasEntry("projects"):
+                projects = cPickle.loads(str(self.Config.Read("projects")))
+            else:
+                projects = {}
+            
+            project_infos = {
+                "tabs": self.SaveTabOrganization(self.TabsOpened)
+            }
+            if self.EnableDebug:
+                project_infos["debug_vars"] = self.DebugVariablePanel.GetDebugVariables()
+                
+            projects[os.path.realpath(self.Controler.GetFilePath())] = project_infos    
+            
+            self.Config.Write("projects", cPickle.dumps(projects))
+            self.Config.Flush()
+    
+    def LoadProjectOrganization(self):
+        if USE_AUI and self.Controler is not None and self.Config.HasEntry("projects"):
+            projects = cPickle.loads(str(self.Config.Read("projects")))
+            
+            project = projects.get(os.path.realpath(self.Controler.GetFilePath()))
+            if project is not None:
+                self.LoadTabOrganization(self.TabsOpened, project["tabs"])
+            
+                if self.EnableDebug:
+                    for variable in project["debug_vars"]:
+                        self.DebugVariablePanel.InsertValue(variable, force=True)
+            
 #-------------------------------------------------------------------------------
 #                               General Functions
 #-------------------------------------------------------------------------------
@@ -1227,6 +1461,19 @@
             event.Skip()
         return ZoomFunction
 
+    def OnResetPerspective(self, event):
+        if USE_AUI and self.DefaultPerspective is not None:
+            self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"])
+        
+            for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]:
+                for idx in xrange(notebook.GetPageCount()):
+                    notebook.RemovePage(0)
+                        
+            notebooks = self.DefaultPerspective["notebooks"]
+            for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"),
+                                         (self.BottomNoteBook, "bottomnotebook"),
+                                         (self.RightNoteBook, "rightnotebook")]:
+                self.LoadTabOrganization(notebook, notebooks.get(entry_name))
 
 #-------------------------------------------------------------------------------
 #                      Project Editor Panels Management Functions
@@ -1255,37 +1502,45 @@
                 self.AUIManager.Update()
     
     def OnPouSelectedChanging(self, event):
+        if not self.Starting:
+            selected = self.TabsOpened.GetSelection()
+            if selected >= 0:
+                window = self.TabsOpened.GetPage(selected)
+                if not window.IsDebugging():
+                    window.ResetBuffer()
+        event.Skip()
+    
+    def OnPouSelectedChanged(self, event):
+        if not self.Starting:
+            selected = self.TabsOpened.GetSelection()
+            if selected >= 0:
+                window = self.TabsOpened.GetPage(selected)
+                if not window.IsDebugging():
+                    wx.CallAfter(self.SelectTypesTreeItem, window.GetTagName())
+                    window.RefreshView()
+                else:
+                    wx.CallAfter(self.SelectInstancesTreeItem, window.GetInstancePath())
+            wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
+        event.Skip()
+
+    def RefreshEditor(self):
         selected = self.TabsOpened.GetSelection()
         if selected >= 0:
             window = self.TabsOpened.GetPage(selected)
             if not window.IsDebugging():
-                window.ResetBuffer()
-        event.Skip()
-    
-    def OnPouSelectedChanged(self, event):
-        selected = self.TabsOpened.GetSelection()
-        if selected >= 0:
-            window = self.TabsOpened.GetPage(selected)
-            if not window.IsDebugging():
-                wx.CallAfter(self.SelectTypesTreeItem, window.GetTagName())
+                self.SelectTypesTreeItem(window.GetTagName())
+            else:
+                self.SelectInstancesTreeItem(window.GetInstancePath())
+            if USE_AUI:
+                for child in self.TabsOpened.GetChildren():
+                    if isinstance(child, wx.aui.AuiTabCtrl):
+                        active_page = child.GetActivePage()
+                        if active_page >= 0:
+                            window = child.GetWindowFromIdx(active_page)
+                            window.RefreshView()
+            else:
                 window.RefreshView()
-            else:
-                wx.CallAfter(self.SelectInstancesTreeItem, window.GetInstancePath())
-        wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
-        event.Skip()
-
-    def RefreshEditor(self):
-        selected = self.TabsOpened.GetSelection()
-        if USE_AUI:
-            for child in self.TabsOpened.GetChildren():
-                if isinstance(child, wx.aui.AuiTabCtrl):
-                    active_page = child.GetActivePage()
-                    if active_page >= 0:
-                        window = child.GetWindowFromIdx(active_page)
-                        window.RefreshView()
-        elif selected >= 0:
-            window = self.TabsOpened.GetPage(idx)
-            window.RefreshView()
+            self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR)
     
     def RefreshEditorNames(self, old_tagname, new_tagname):
         for i in xrange(self.TabsOpened.GetPageCount()):
@@ -1646,7 +1901,6 @@
                     icon = self.GenerateBitmap("ACTION", bodytype)
                 new_window.SetIcon(icon)
                 self.AddPage(new_window, "")
-                words = tagname.split("::")
             elif element == ITEM_DATATYPE:
                 new_window = DataTypeEditor(self.TabsOpened, tagname, self, self.Controler)
                 new_window.SetIcon(self.GenerateBitmap("DATATYPE"))
@@ -1666,6 +1920,7 @@
                         self.TabsOpened.SetSelection(i)
                         window.SetFocus()
                 self.RefreshPageTitles()
+            return new_window
     
     def OnTypesTreeRightUp(self, event):
         if wx.Platform == '__WXMSW__':
@@ -1854,71 +2109,83 @@
         if self.Controler.DebugAvailable():
             selected_item = event.GetItem()
             selected_infos = self.InstancesTree.GetPyData(selected_item)
-            if selected_item is not None and selected_infos[0] in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM, ITEM_TRANSITION, ITEM_ACTION]:
-                instance_path = self.InstancesTree.GetItemText(selected_item).split(" (")[0]
+            if selected_item is not None and (
+                selected_infos[0] in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM, ITEM_TRANSITION, ITEM_ACTION] or
+                selected_infos[0] in ITEMS_VARIABLE):
+                
+                instance_path, var_type = self.InstancesTree.GetItemText(selected_item).split(" (")
+                if selected_infos[1] is not None:
+                    instance_type = selected_infos[1]
+                else:
+                    instance_type = var_type.split(")")[0]
                 parent_item = self.InstancesTree.GetItemParent(selected_item)
                 while self.InstancesTree.GetPyData(parent_item)[0] != ITEM_PROJECT:
                     parent_name = self.InstancesTree.GetItemText(parent_item).split(" (")[0]
                     instance_path = "%s.%s"%(parent_name, instance_path)
                     parent_item = self.InstancesTree.GetItemParent(parent_item)
-                openedidx = self.IsOpened(instance_path)
-                if openedidx is not None:
-                    old_selected = self.TabsOpened.GetSelection()
-                    if old_selected != openedidx:
-                        if old_selected >= 0:
-                            self.TabsOpened.GetPage(old_selected).ResetBuffer()
-                        self.TabsOpened.SetSelection(openedidx)
-                elif selected_infos[1] is not None:
-                    bodytype = self.Controler.GetEditedElementBodyType(selected_infos[1], True)
-                    if bodytype == "FBD":
-                        new_window = Viewer(self.TabsOpened, selected_infos[1], self, self.Controler, True, instance_path)
-                        new_window.RefreshScaling(False)
-                    elif bodytype == "LD":
-                        new_window = LD_Viewer(self.TabsOpened, selected_infos[1], self, self.Controler, True, instance_path)
-                        new_window.RefreshScaling(False)
-                    elif bodytype == "SFC":
-                        new_window = SFC_Viewer(self.TabsOpened, selected_infos[1], self, self.Controler, True, instance_path)
-                        new_window.RefreshScaling(False)
-                    else:
-                        new_window = TextViewer(self.TabsOpened, selected_infos[1], self, self.Controler, True, instance_path)
-                        new_window.SetTextSyntax(bodytype)
-                        if bodytype == "IL":
-                            new_window.SetKeywords(IL_KEYWORDS)
-                        else:
-                            new_window.SetKeywords(ST_KEYWORDS)
-                    if selected_infos[0] in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
-                        pou_type = self.Controler.GetEditedElementType(selected_infos[1], True)[1].upper()
-                        icon = self.GenerateBitmap(pou_type, bodytype)
-                    elif selected_infos[0] == ITEM_TRANSITION:
-                        icon = self.GenerateBitmap("TRANSITION", bodytype)
-                    elif selected_infos[0] == ITEM_ACTION:
-                        icon = self.GenerateBitmap("ACTION", bodytype)
-                    new_window.SetIcon(icon)
-                    self.AddPage(new_window, "")
-                    new_window.RefreshView()
-                    new_window.SetFocus()
-                    self.RefreshPageTitles()
-            if selected_item is not None and selected_infos[0] in ITEMS_VARIABLE:
-                var_path, var_type = self.InstancesTree.GetItemText(selected_item).split(" (")
-                var_type = var_type.split(")")[0]
                 
-                if self.Controler.IsOfType(var_type, "ANY_NUM", True) or\
-                   self.Controler.IsOfType(var_type, "ANY_BIT", True):
-                    parent_item = self.InstancesTree.GetItemParent(selected_item)
-                    while self.InstancesTree.GetPyData(parent_item)[0] != ITEM_PROJECT:
-                        parent_name = self.InstancesTree.GetItemText(parent_item).split(" (")[0]
-                        var_path = "%s.%s"%(parent_name, var_path)
-                        parent_item = self.InstancesTree.GetItemParent(parent_item)
-                    
-                    self.OpenGraphicViewer(var_path)
+                self.OpenDebugViewer(selected_infos[0], instance_path, instance_type)
+        
         event.Skip()
 
+    def OpenDebugViewer(self, instance_category, instance_path, instance_type):
+        openedidx = self.IsOpened(instance_path)
+        if openedidx is not None:
+            old_selected = self.TabsOpened.GetSelection()
+            if old_selected != openedidx:
+                if old_selected >= 0:
+                    self.TabsOpened.GetPage(old_selected).ResetBuffer()
+                self.TabsOpened.SetSelection(openedidx)
+        
+        elif instance_category in ITEMS_VARIABLE:
+            if self.Controler.IsOfType(instance_type, "ANY_NUM", True) or\
+               self.Controler.IsOfType(instance_type, "ANY_BIT", True):
+                
+                return self.OpenGraphicViewer(instance_path)
+        
+        else:
+            bodytype = self.Controler.GetEditedElementBodyType(instance_type, True)
+            new_window = None
+            if bodytype == "FBD":
+                new_window = Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
+                new_window.RefreshScaling(False)
+            elif bodytype == "LD":
+                new_window = LD_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
+                new_window.RefreshScaling(False)
+            elif bodytype == "SFC":
+                new_window = SFC_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
+                new_window.RefreshScaling(False)
+            else:
+                new_window = TextViewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path)
+                new_window.SetTextSyntax(bodytype)
+                if bodytype == "IL":
+                    new_window.SetKeywords(IL_KEYWORDS)
+                else:
+                    new_window.SetKeywords(ST_KEYWORDS)
+            if new_window is not None:
+                if instance_category in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]:
+                    pou_type = self.Controler.GetEditedElementType(instance_type, True)[1].upper()
+                    icon = self.GenerateBitmap(pou_type, bodytype)
+                elif instance_category == ITEM_TRANSITION:
+                    icon = self.GenerateBitmap("TRANSITION", bodytype)
+                elif instance_category == ITEM_ACTION:
+                    icon = self.GenerateBitmap("ACTION", bodytype)
+                new_window.SetIcon(icon)
+                self.AddPage(new_window, "")
+                new_window.RefreshView()
+                new_window.SetFocus()
+                self.RefreshPageTitles()
+            return new_window
+        
+        return None
+
     def OpenGraphicViewer(self, var_path):
         new_window = GraphicViewer(self.TabsOpened, self, self.Controler, var_path)
         self.AddPage(new_window, "")
         new_window.RefreshView()
         new_window.SetFocus()
         self.RefreshPageTitles()
+        return new_window
 
     def OnInstancesTreeRightUp(self, event):
         if self.Controler.DebugAvailable():
@@ -2691,7 +2958,7 @@
             if USE_AUI:
                 self.AUIManager.UnInit()
             
-            self.SaveFrameSize()
+            self.SaveLastState()
             
             event.Skip()
         else:
@@ -2787,6 +3054,7 @@
                 self.Controler = PLCControler()
                 result = self.Controler.OpenXMLFile(filepath)
                 if result is None:
+                    self.LoadProjectOrganization()
                     self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
             self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
         dialog.Destroy()
@@ -2797,6 +3065,7 @@
     def OnCloseProjectMenu(self, event):
         if not self.CheckSaveBeforeClosing():
             return
+        self.SaveProjectOrganization()
         self.ResetView()
         self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
 
@@ -4216,6 +4485,8 @@
             if self.GetDataType(item.GetVariable().upper()) is None:
                 self.RemoveDataConsumer(item)
                 self.Table.RemoveItem(idx)
+            else:
+                self.AddDataConsumer(iec_path.upper(), item)
         self.Freeze()
         self.Table.ResetView(self.VariablesGrid)
         self.VariablesGrid.RefreshButtons()
@@ -4263,7 +4534,7 @@
             menu.Destroy()
         event.Skip()
     
-    def InsertValue(self, iec_path, idx = None):
+    def InsertValue(self, iec_path, idx = None, force=False):
         if idx is None:
             idx = self.Table.GetNumberRows()
         for item in self.Table.GetData():
@@ -4271,10 +4542,12 @@
                 return
         item = VariableTableItem(self, iec_path, "")
         result = self.AddDataConsumer(iec_path.upper(), item)
-        if result is not None:
+        if result is not None or force:
             self.Table.InsertItem(idx, item)
             self.RefreshGrid()
-
+        
+    def GetDebugVariables(self):
+        return [item.GetVariable() for item in self.Table.GetData()]
         
 #-------------------------------------------------------------------------------
 #                               Viewer Printout