Changes merged
author'Laurent Bessard <laurent.bessard@lolitech.fr>'
Mon, 21 Sep 2009 12:12:08 +0200
changeset 398 31d08063b5d6
parent 397 6a7ff66a811d (diff)
parent 391 9b1801ef99b5 (current diff)
child 399 77e23bf04c33
Changes merged
runtime/PLCObject.py
--- a/Beremiz.py	Fri Sep 18 14:58:22 2009 +0200
+++ b/Beremiz.py	Mon Sep 21 12:12:08 2009 +0200
@@ -66,7 +66,6 @@
     
     app = wx.PySimpleApp()
     app.SetAppName('beremiz')
-    config = wx.ConfigBase.Get()
     wx.InitAllImageHandlers()
     
     bmp = wx.Image(Bpath("images","splash.png")).ConvertToBitmap()
@@ -100,15 +99,20 @@
 if __name__ == '__main__':
     __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
 
+#Quick hack to be able to find Beremiz IEC tools. Should be config params.
+base_folder = os.path.split(sys.path[0])[0]
+sys.path.append(base_folder)
+sys.path.append(os.path.join(base_folder, "plcopeneditor"))
+sys.path.append(os.path.join(base_folder, "docutils"))
+
 import wx.lib.buttons, wx.lib.statbmp
 import TextCtrlAutoComplete, cPickle
 import types, time, re, platform, time, traceback, commands
 from plugger import PluginsRoot, MATIEC_ERROR_MODEL
 from wxPopen import ProcessLogger
 
-base_folder = os.path.split(sys.path[0])[0]
-sys.path.append(base_folder)
 from docutils import *
+from PLCOpenEditor import IDEFrame, Viewer, AppendMenu, TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, SCALING
 
 SCROLLBAR_UNIT = 10
 WINDOW_COLOUR = wx.Colour(240,240,240)
@@ -279,61 +283,43 @@
  ID_BEREMIZRUNMENURUN, ID_BEREMIZRUNMENUSAVELOG, 
 ] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)]
 
-class Beremiz(wx.Frame):
+class Beremiz(IDEFrame):
 	
     def _init_coll_FileMenu_Items(self, parent):
-        parent.Append(help='', id=wx.ID_NEW,
+        AppendMenu(parent, help='', id=wx.ID_NEW,
               kind=wx.ITEM_NORMAL, text=_(u'New\tCTRL+N'))
-        parent.Append(help='', id=wx.ID_OPEN,
+        AppendMenu(parent, help='', id=wx.ID_OPEN,
               kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O'))
-        parent.Append(help='', id=wx.ID_SAVE,
+        AppendMenu(parent, help='', id=wx.ID_SAVE,
               kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
-        parent.Append(help='', id=wx.ID_CLOSE_ALL,
+        AppendMenu(parent, help='', id=wx.ID_CLOSE,
+              kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W'))
+        AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL,
               kind=wx.ITEM_NORMAL, text=_(u'Close Project'))
         parent.AppendSeparator()
-        parent.Append(help='', id=wx.ID_PROPERTIES,
+        AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
+              kind=wx.ITEM_NORMAL, text=_(u'Page Setup'))
+        AppendMenu(parent, help='', id=wx.ID_PREVIEW,
+              kind=wx.ITEM_NORMAL, text=_(u'Preview'))
+        AppendMenu(parent, help='', id=wx.ID_PRINT,
+              kind=wx.ITEM_NORMAL, text=_(u'Print'))
+        parent.AppendSeparator()
+        AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
               kind=wx.ITEM_NORMAL, text=_(u'Properties'))
         parent.AppendSeparator()
-        parent.Append(help='', id=wx.ID_EXIT,
+        AppendMenu(parent, help='', id=wx.ID_EXIT,
               kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
+        
         self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
         self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
         self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
+        self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
         self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
+        self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
+        self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
+        self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
         self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
         self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
-        
-    def _init_coll_EditMenu_Items(self, parent):
-        parent.Append(help='', id=wx.ID_EDIT,
-              kind=wx.ITEM_NORMAL, text=_(u'Edit PLC\tCTRL+R'))
-        parent.AppendSeparator()
-        parent.Append(help='', id=wx.ID_ADD,
-              kind=wx.ITEM_NORMAL, text=_(u'Add Plugin'))
-        parent.Append(help='', id=wx.ID_DELETE,
-              kind=wx.ITEM_NORMAL, text=_(u'Delete Plugin'))
-        self.Bind(wx.EVT_MENU, self.OnEditPLCMenu, id=wx.ID_EDIT)
-        self.Bind(wx.EVT_MENU, self.OnAddMenu, id=wx.ID_ADD)
-        self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=wx.ID_DELETE)
-    
-    def _init_coll_RunMenu_Items(self, parent):
-        parent.Append(help='', id=ID_BEREMIZRUNMENUBUILD,
-              kind=wx.ITEM_NORMAL, text=_(u'Build\tCTRL+R'))
-        parent.AppendSeparator()
-        parent.Append(help='', id=ID_BEREMIZRUNMENUSIMULATE,
-              kind=wx.ITEM_NORMAL, text=_(u'Simulate'))
-        parent.Append(help='', id=ID_BEREMIZRUNMENURUN,
-              kind=wx.ITEM_NORMAL, text=_(u'Run'))
-        parent.AppendSeparator()
-        parent.Append(help='', id=ID_BEREMIZRUNMENUSAVELOG,
-              kind=wx.ITEM_NORMAL, text=_(u'Save Log'))
-        self.Bind(wx.EVT_MENU, self.OnBuildMenu,
-              id=ID_BEREMIZRUNMENUBUILD)
-        self.Bind(wx.EVT_MENU, self.OnSimulateMenu,
-              id=ID_BEREMIZRUNMENUSIMULATE)
-        self.Bind(wx.EVT_MENU, self.OnRunMenu,
-              id=ID_BEREMIZRUNMENURUN)
-        self.Bind(wx.EVT_MENU, self.OnSaveLogMenu,
-              id=ID_BEREMIZRUNMENUSAVELOG)
     
     def _init_coll_HelpMenu_Items(self, parent):
         parent.Append(help='', id=wx.ID_HELP,
@@ -343,25 +329,6 @@
         self.Bind(wx.EVT_MENU, self.OnBeremizMenu, id=wx.ID_HELP)
         self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
     
-    def _init_coll_MenuBar_Menus(self, parent):
-        parent.Append(menu=self.FileMenu, title=_(u'File'))
-        #parent.Append(menu=self.EditMenu, title=u'Edit')
-        #parent.Append(menu=self.RunMenu, title=u'Run')
-        parent.Append(menu=self.HelpMenu, title=_(u'Help'))
-    
-    def _init_utils(self):
-        self.MenuBar = wx.MenuBar()
-        self.FileMenu = wx.Menu(title=u'')
-        #self.EditMenu = wx.Menu(title=u'')
-        #self.RunMenu = wx.Menu(title=u'')
-        self.HelpMenu = wx.Menu(title=u'')
-        
-        self._init_coll_MenuBar_Menus(self.MenuBar)
-        self._init_coll_FileMenu_Items(self.FileMenu)
-        #self._init_coll_EditMenu_Items(self.EditMenu)
-        #self._init_coll_RunMenu_Items(self.RunMenu)
-        self._init_coll_HelpMenu_Items(self.HelpMenu)
-    
     def _init_coll_PLCConfigMainSizer_Items(self, parent):
         parent.AddSizer(self.PLCParamsSizer, 0, border=10, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
         parent.AddSizer(self.PluginTreeSizer, 0, border=10, flag=wx.BOTTOM|wx.LEFT|wx.RIGHT)
@@ -374,7 +341,7 @@
         parent.AddGrowableCol(0)
         parent.AddGrowableCol(1)
         
-    def _init_sizers(self):
+    def _init_beremiz_sizers(self):
         self.PLCConfigMainSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2)
         self.PLCParamsSizer = wx.BoxSizer(wx.VERTICAL)
         #self.PluginTreeSizer = wx.FlexGridSizer(cols=3, hgap=0, rows=0, vgap=2)
@@ -387,82 +354,68 @@
         self.PLCConfig.SetSizer(self.PLCConfigMainSizer)
         
     def _init_ctrls(self, prnt):
-        wx.Frame.__init__(self, id=ID_BEREMIZ, name=u'Beremiz',
-              parent=prnt, pos=wx.Point(0, 0), size=wx.Size(1000, 600),
-              style=wx.DEFAULT_FRAME_STYLE|wx.CLIP_CHILDREN, title=_(u'Beremiz'))
-        self._init_utils()
-        self.SetClientSize(wx.Size(1000, 600))
-        self.SetMenuBar(self.MenuBar)
-        self.Bind(wx.EVT_ACTIVATE, self.OnFrameActivated)
-        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
-
+        IDEFrame._init_ctrls(self, prnt)
+        
         self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR)
         accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)])
         self.SetAcceleratorTable(accel)
         
-        if wx.VERSION < (2, 8, 0):
-            self.MainSplitter = wx.SplitterWindow(id=ID_BEREMIZMAINSPLITTER,
-                  name='MainSplitter', parent=self, point=wx.Point(0, 0),
-                  size=wx.Size(0, 0), style=wx.SP_3D)
-            self.MainSplitter.SetNeedUpdating(True)
-            self.MainSplitter.SetMinimumPaneSize(1)
-        
-            parent = self.MainSplitter
-        else:
-            parent = self
-        
         self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG,
-              name='PLCConfig', parent=parent, pos=wx.Point(0, 0),
+              name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0),
               size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL)
         self.PLCConfig.SetBackgroundColour(wx.WHITE)
         self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
         self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow)
+        self.LeftNoteBook.AddPage(self.PLCConfig, _("Topology"))
         
         self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='',
-                  name='LogConsole', parent=parent, pos=wx.Point(0, 0),
+                  name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
                   size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2)
         self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick)
-        
-        if wx.VERSION < (2, 8, 0):
-            self.MainSplitter.SplitHorizontally(self.PLCConfig, self.LogConsole, -250)
-        else:
-            self.AUIManager = wx.aui.AuiManager(self)
-            self.AUIManager.SetDockSizeConstraint(0.5, 0.5)
-            
-            self.AUIManager.AddPane(self.PLCConfig, wx.aui.AuiPaneInfo().CenterPane())
-            
-            self.AUIManager.AddPane(self.LogConsole, wx.aui.AuiPaneInfo().
-                Caption(_("Log Console")).Bottom().Layer(1).
-                BestSize(wx.Size(800, 200)).CloseButton(False))
-        
-            self.AUIManager.Update()
-
-        self._init_sizers()
-
-    def __init__(self, parent, projectOpen, buildpath):
-        self._init_ctrls(parent)
+        self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console"))
+        
+        self._init_beremiz_sizers()
+
+    def __init__(self, parent, projectOpen, buildpath, debug=True):
+        IDEFrame.__init__(self, parent, debug)
+        self.Config = wx.ConfigBase.Get()
         
         self.Log = LogPseudoFile(self.LogConsole)
-
+        
         self.local_runtime = None
         self.runtime_port = None
         self.local_runtime_tmpdir = None
         
-        # Add beremiz's icon in top left corner of the frame
-        self.SetIcon(wx.Icon(Bpath( "images", "brz.ico"), wx.BITMAP_TYPE_ICO))
-        
         self.DisableEvents = False
         
         self.PluginInfos = {}
         
         if projectOpen:
             self.PluginRoot = PluginsRoot(self, self.Log)
+            self.Controler = self.PluginRoot
             self.PluginRoot.LoadProject(projectOpen, buildpath)
+            self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
             self.RefreshAll()
         else:
             self.PluginRoot = None
-        
-        self.RefreshMainMenu()
+            self.Controler = None
+        
+        # Add beremiz's icon in top left corner of the frame
+        self.SetIcon(wx.Icon(Bpath( "images", "brz.ico"), wx.BITMAP_TYPE_ICO))
+        
+        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
+        
+        self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
+
+    def RefreshTitle(self):
+        name = _("Beremiz")
+        if self.PluginRoot is not None:
+            projectname = self.PluginRoot.GetProjectName()
+            if self.PluginRoot.PlugTestModified():
+                projectname = "~%s~" % projectname
+            self.SetTitle("%s - %s" % (name, projectname))
+        else:
+            self.SetTitle(name)
 
     def StartLocalRuntime(self, taskbaricon = True):
         if self.local_runtime is None or self.local_runtime.finished:
@@ -544,29 +497,45 @@
         self.RefreshScrollBars()
         event.Skip()
     
-    def OnFrameActivated(self, event):
-        if not event.GetActive() and self.PluginRoot is not None:
-            self.PluginRoot.RefreshPluginsBlockLists()
-    
     def OnPanelLeftDown(self, event):
         focused = self.FindFocus()
         if isinstance(focused, TextCtrlAutoComplete.TextCtrlAutoComplete):
             focused._showDropDown(False)
         event.Skip()
     
-    def RefreshMainMenu(self):
+    def RefreshFileMenu(self):
         if self.PluginRoot is not None:
-##            self.MenuBar.EnableTop(1, True)
-##            self.MenuBar.EnableTop(2, True)
+            selected = self.TabsOpened.GetSelection()
+            if selected >= 0:
+                graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
+            else:
+                graphic_viewer = False
+            if self.TabsOpened.GetPageCount() > 0:
+                self.FileMenu.Enable(wx.ID_CLOSE, True)
+                if graphic_viewer:
+                    self.FileMenu.Enable(wx.ID_PREVIEW, True)
+                    self.FileMenu.Enable(wx.ID_PRINT, True)
+                else:
+                    self.FileMenu.Enable(wx.ID_PREVIEW, False)
+                    self.FileMenu.Enable(wx.ID_PRINT, False)
+            else:
+                self.FileMenu.Enable(wx.ID_CLOSE, False)
+                self.FileMenu.Enable(wx.ID_PREVIEW, False)
+                self.FileMenu.Enable(wx.ID_PRINT, False)
+            self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
             self.FileMenu.Enable(wx.ID_SAVE, True)
+            self.FileMenu.Enable(wx.ID_PROPERTIES, True)
             self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
-            self.FileMenu.Enable(wx.ID_PROPERTIES, True)
+            self.FileMenu.Enable(wx.ID_SAVEAS, True)
         else:
-##            self.MenuBar.EnableTop(1, False)
-##            self.MenuBar.EnableTop(2, False)
+            self.FileMenu.Enable(wx.ID_CLOSE, False)
+            self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
+            self.FileMenu.Enable(wx.ID_PREVIEW, False)
+            self.FileMenu.Enable(wx.ID_PRINT, False)
             self.FileMenu.Enable(wx.ID_SAVE, False)
+            self.FileMenu.Enable(wx.ID_PROPERTIES, False)
             self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
-            self.FileMenu.Enable(wx.ID_PROPERTIES, False)
+            self.FileMenu.Enable(wx.ID_SAVEAS, False)
 
     def RefreshScrollBars(self):
         xstart, ystart = self.PLCConfig.GetViewStart()
@@ -1225,7 +1194,7 @@
                         spinctrl.SetValue(element_infos["value"])
                         spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
                     else:
-                        choices = cPickle.loads(str(config.Read(element_path, cPickle.dumps([""]))))                           
+                        choices = cPickle.loads(str(self.Config.Read(element_path, cPickle.dumps([""]))))                           
                         textctrl = TextCtrlAutoComplete.TextCtrlAutoComplete(id=id, 
                                                                      name=element_infos["name"], 
                                                                      parent=parent, 
@@ -1240,60 +1209,63 @@
                         textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path))
             first = False
     
+    def ResetView(self):
+        IDEFrame.ResetView(self)
+        self.PluginInfos = {}
+        self.PluginRoot = None
+        self.Log.flush()
+        self.DebugVariablePanel.SetDataProducer(None)
+    
     def OnNewProjectMenu(self, event):
-        if not config.HasEntry("lastopenedfolder"):
+        if not self.Config.HasEntry("lastopenedfolder"):
             defaultpath = os.path.expanduser("~")
         else:
-            defaultpath = config.Read("lastopenedfolder")
+            defaultpath = self.Config.Read("lastopenedfolder")
         
         dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
         if dialog.ShowModal() == wx.ID_OK:
             projectpath = dialog.GetPath()
             dialog.Destroy()
-            config.Write("lastopenedfolder", os.path.dirname(projectpath))
-            config.Flush()
-            self.PluginInfos = {}
-            if self.PluginRoot is not None:
-                self.PluginRoot.CloseProject()
+            self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
+            self.Config.Flush()
+            self.ResetView()
             self.PluginRoot = PluginsRoot(self, self.Log)
-            res = self.PluginRoot.NewProject(projectpath)
-            if not res :
+            self.Controler = self.PluginRoot
+            result = self.PluginRoot.NewProject(projectpath)
+            if not result:
+                self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
+                self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
                 self.RefreshAll()
-                self.RefreshMainMenu()
             else:
-                message = wx.MessageDialog(self, res, _("ERROR"), wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
+                self.ShowErrorMessage(result)
+            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
         event.Skip()
     
     def OnOpenProjectMenu(self, event):
-        if not config.HasEntry("lastopenedfolder"):
+        if not self.Config.HasEntry("lastopenedfolder"):
             defaultpath = os.path.expanduser("~")
         else:
-            defaultpath = config.Read("lastopenedfolder")
+            defaultpath = self.Config.Read("lastopenedfolder")
         
         dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
         if dialog.ShowModal() == wx.ID_OK:
             projectpath = dialog.GetPath()
             if os.path.isdir(projectpath):
-                config.Write("lastopenedfolder", os.path.dirname(projectpath))
-                config.Flush()
-                self.PluginInfos = {}
-                if self.PluginRoot is not None:
-                    self.PluginRoot.CloseProject()
+                self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
+                self.Config.Flush()
+                self.ResetView()
                 self.PluginRoot = PluginsRoot(self, self.Log)
+                self.Controler = self.PluginRoot
                 result = self.PluginRoot.LoadProject(projectpath)
                 if not result:
+                    self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
+                    self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
                     self.RefreshAll()
-                    self.RefreshMainMenu()
                 else:
-                    message = wx.MessageDialog(self, result, _("Error"), wx.OK|wx.ICON_ERROR)
-                    message.ShowModal()
-                    message.Destroy()
+                    self.ShowErrorMessage(result)
             else:
-                message = wx.MessageDialog(self, _("\"%s\" folder is not a valid Beremiz project\n")%projectpath, _("Error"), wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
+                self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath)
+            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
         dialog.Destroy()
         event.Skip()
     
@@ -1310,18 +1282,16 @@
                     self.PluginRoot.SaveProject()
                 elif answer == wx.ID_CANCEL:
                     return
-            self.PluginInfos = {}
-            self.PluginRoot.CloseProject()
-            self.PluginRoot = None
-            self.Log.flush()
+            self.ResetView()
+            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
             self.RefreshAll()
-            self.RefreshMainMenu()
         event.Skip()
     
     def OnSaveProjectMenu(self, event):
         if self.PluginRoot is not None:
             self.PluginRoot.SaveProject()
             self.RefreshAll()
+            self.RefreshTitle()
         event.Skip()
     
     def OnPropertiesMenu(self, event):
@@ -1330,32 +1300,7 @@
     def OnQuitMenu(self, event):
         self.Close()
         event.Skip()
-    
-    def OnEditPLCMenu(self, event):
-        self.EditPLC()
-        event.Skip()
-    
-    def OnAddMenu(self, event):
-        self.AddPlugin()
-        event.Skip()
-    
-    def OnDeleteMenu(self, event):
-        self.DeletePlugin()
-        event.Skip()
-
-    def OnBuildMenu(self, event):
-        #self.BuildAutom()
-        event.Skip()
-
-    def OnSimulateMenu(self, event):
-        event.Skip()
-    
-    def OnRunMenu(self, event):
-        event.Skip()
-    
-    def OnSaveLogMenu(self, event):
-        event.Skip()
-    
+        
     def OnBeremizMenu(self, event):
         open_pdf(Bpath( "doc", "manual_beremiz.pdf"))
         event.Skip()
@@ -1364,16 +1309,6 @@
         OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc","about.html"), wx.Size(550, 500))
         event.Skip()
     
-    def OnAddButton(self, event):
-        PluginType = self.PluginChilds.GetStringSelection()
-        if PluginType != "":
-            self.AddPlugin(PluginType)
-        event.Skip()
-    
-    def OnDeleteButton(self, event):
-        self.DeletePlugin()
-        event.Skip()
-    
     def GetAddButtonFunction(self, plugin, window):
         def AddButtonFunction(event):
             if plugin and len(plugin.PlugChildsTypes) > 0:
@@ -1399,6 +1334,7 @@
             PluginName = dialog.GetValue()
             plugin.PlugAddChild(PluginName, PluginType)
             self.RefreshPluginTree()
+            self.PluginRoot.RefreshPluginsBlockLists()
         dialog.Destroy()
     
     def DeletePlugin(self, plugin):
@@ -1407,6 +1343,7 @@
             self.PluginInfos.pop(plugin)
             plugin.PlugRemove()
             del plugin
+            self.PluginRoot.RefreshPluginsBlockLists()
             self.RefreshPluginTree()
         dialog.Destroy()
 
--- a/Beremiz_service.py	Fri Sep 18 14:58:22 2009 +0200
+++ b/Beremiz_service.py	Mon Sep 21 12:12:08 2009 +0200
@@ -526,9 +526,9 @@
         
     class PLCStoppedHMI(PLCHMI):
         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
-                                             tags.h1["PLC IS STOPPED"]
+                                             tags.h1["PLC IS STOPPED"],
                                              ])
-        
+    
     class MainPage(athena.LiveElement):
         jsClass = u"WebInterface.PLC"
         docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[
@@ -582,7 +582,7 @@
         def detachFragmentChildren(self):
             for child in self.liveFragmentChildren[:]:
                 child.detach()
-        
+    
     class WebInterface(athena.LivePage):
 
         docFactory = loaders.stan([tags.raw(xhtml_header),
@@ -594,7 +594,7 @@
                                                    ]]]])
         MainPage = MainPage()
         PLCHMI = PLCHMI
-
+        
         def __init__(self, plcState=False, *a, **kw):
             super(WebInterface, self).__init__(*a, **kw)
             self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, 'webinterface.js')
@@ -645,7 +645,7 @@
             self.MainPage.resetHMI()
             #print reason
             #print "We will be called back when the client disconnects"
-    
+        
     if havewx:
         reactor.registerWxApp(app)
     res = WebInterface()
--- a/discovery.py	Fri Sep 18 14:58:22 2009 +0200
+++ b/discovery.py	Mon Sep 21 12:12:08 2009 +0200
@@ -27,188 +27,185 @@
 import  wx.lib.mixins.listctrl  as  listmix
 
 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
-    def __init__(self, parent, ID, pos=wx.DefaultPosition,
+    def __init__(self, parent, id, name, pos=wx.DefaultPosition,
                  size=wx.DefaultSize, style=0):
-        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
+        wx.ListCtrl.__init__(self, parent, id, pos, size, style, name=name)
         listmix.ListCtrlAutoWidthMixin.__init__(self)
 
+[ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1, 
+ ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON, 
+ ID_DISCOVERYDIALOGLOCALBUTTON, 
+] = [wx.NewId() for _init_ctrls in range(5)]
+
 class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin):
-    def __init__(self, parent, id=-1, title=_('Service Discovery')):
-        self.my_result=None
-        wx.Dialog.__init__(self, parent, id, title, size=(600,600), style=wx.DEFAULT_DIALOG_STYLE)
-
-        # set up dialog sizer
-
-        sizer = wx.FlexGridSizer(2, 1, 2, 2)  # rows, cols, vgap, hgap
-        sizer.AddGrowableRow(0)
-        sizer.AddGrowableCol(0)
-
-        # set up list control
-
-        self.list = AutoWidthListCtrl(self, -1,
-                                      #pos=(50,20), 
-                                      #size=(500,300),
-                                      style=wx.LC_REPORT 
-                                     | wx.LC_EDIT_LABELS
-                                     | wx.LC_SORT_ASCENDING
-                                     | wx.LC_SINGLE_SEL 
-                                     )
-        sizer.Add(self.list, 1, wx.EXPAND)
-
-        btsizer = wx.FlexGridSizer(1, 6, 2, 2)  # rows, cols, vgap, hgap
-        
-        sizer.Add(btsizer, 1, wx.EXPAND)
-
-        self.PopulateList()
-
-        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.list)
-        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.list)
-
-        # set up buttons
-
-        local_id = wx.NewId()
-        b = wx.Button(self, local_id, _("Refresh"))
-        self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, b)
-        btsizer.Add(b)
-
-        btsizer.AddSpacer(0)
-        btsizer.AddGrowableCol(1)
-
-        local_id = wx.NewId()
-        b = wx.Button(self, local_id, _("Local"))
-        self.Bind(wx.EVT_BUTTON, self.ChooseLocalID, b)
-        btsizer.Add(b)
-
-        btsizer.AddSpacer(0)
-        btsizer.AddGrowableCol(3)
-
-        b = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
-        self.Bind(wx.EVT_BUTTON, self.OnCancel, b)
-        btsizer.Add(b)
-
-        b = wx.Button(self, wx.ID_OK, _("OK"))
-        self.Bind(wx.EVT_BUTTON, self.OnOk, b)
-        b.SetDefault()
-        btsizer.Add(b)
-
-        self.SetSizer(sizer)
-
+    
+    def _init_coll_MainSizer_Items(self, parent):
+        parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
+        parent.AddWindow(self.ServicesList, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.GROW)
+        parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW)
+        
+    def _init_coll_MainSizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableRow(1)
+    
+    def _init_coll_ButtonGridSizer_Items(self, parent):
+        parent.AddWindow(self.RefreshButton, 0, border=0, flag=0)
+        parent.AddWindow(self.LocalButton, 0, border=0, flag=0)
+        parent.AddSizer(self.ButtonSizer, 0, border=0, flag=0)
+        
+    def _init_coll_ButtonGridSizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableCol(1)
+        parent.AddGrowableRow(1)
+    
+    def _init_sizers(self):
+        self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
+        self.ButtonGridSizer = wx.FlexGridSizer(cols=3, hgap=5, rows=1, vgap=0)
+        
+        self._init_coll_MainSizer_Items(self.MainSizer)
+        self._init_coll_MainSizer_Growables(self.MainSizer)
+        self._init_coll_ButtonGridSizer_Items(self.ButtonGridSizer)
+        self._init_coll_ButtonGridSizer_Growables(self.ButtonGridSizer)
+        
+        self.SetSizer(self.MainSizer)
+    
+    def _init_ctrls(self, prnt):
+        wx.Dialog.__init__(self, id=ID_DISCOVERYDIALOG, 
+              name='DiscoveryDialog', parent=prnt,  
+              size=wx.Size(600, 600), style=wx.DEFAULT_DIALOG_STYLE,
+              title='Service Discovery')
+        
+        self.staticText1 = wx.StaticText(id=ID_DISCOVERYDIALOGSTATICTEXT1,
+              label=_('Services available:'), name='staticText1', parent=self,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        
+        # Set up list control
+        self.ServicesList = AutoWidthListCtrl(id=ID_DISCOVERYDIALOGSERVICESLIST,
+              name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), 
+              style=wx.LC_REPORT|wx.LC_EDIT_LABELS|wx.LC_SORT_ASCENDING|wx.LC_SINGLE_SEL)
+        self.ServicesList.InsertColumn(0, 'NAME')
+        self.ServicesList.InsertColumn(1, 'TYPE')
+        self.ServicesList.InsertColumn(2, 'IP')
+        self.ServicesList.InsertColumn(3, 'PORT')
+        self.ServicesList.SetColumnWidth(0, 150)
+        self.ServicesList.SetColumnWidth(1, 150)
+        self.ServicesList.SetColumnWidth(2, 150)
+        self.ServicesList.SetColumnWidth(3, 150)
+        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, id=ID_DISCOVERYDIALOGSERVICESLIST)
+        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_DISCOVERYDIALOGSERVICESLIST)
+        
         listmix.ColumnSorterMixin.__init__(self, 4)
-
-        # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
+        
+        self.RefreshButton = wx.Button(id=ID_DISCOVERYDIALOGREFRESHBUTTON,
+              label=_('Refresh'), name='RefreshButton', parent=self,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, id=ID_DISCOVERYDIALOGREFRESHBUTTON)
+        
+        self.LocalButton = wx.Button(id=ID_DISCOVERYDIALOGLOCALBUTTON,
+              label=_('Local'), name='LocalButton', parent=self,
+              pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
+        self.Bind(wx.EVT_BUTTON, self.OnLocalButton, id=ID_DISCOVERYDIALOGLOCALBUTTON)
+        
+        self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTER)
+        
+        self._init_sizers()
+        
+    def __init__(self, parent):
+        self._init_ctrls(parent)
+        
         self.itemDataMap = {}
-
-        # a counter used to assign a unique id to each listctrl item
         self.nextItemId = 0
-
-        self.browser = None
-        self.zConfInstance = Zeroconf()
+        
+        self.URI = None
+        self.Browser = None
+        
+        self.ZeroConfInstance = Zeroconf()
         self.RefreshList()
-
-        self.Bind(wx.EVT_CLOSE, self.OnClose)
-
+        
+    def __del__(self):
+        self.Browser.cancel()
+        self.ZeroConfInstance.close()
+        
     def RefreshList(self):
-        type = "_PYRO._tcp.local."
-        self.browser = ServiceBrowser(self.zConfInstance, type, self)        
+        self.Browser = ServiceBrowser(self.ZeroConfInstance, "_PYRO._tcp.local.", self)        
 
     def OnRefreshButton(self, event):
-        self.list.DeleteAllItems()
-        self.browser.cancel()
+        self.ServicesList.DeleteAllItems()
+        self.Browser.cancel()
         self.RefreshList()
 
-    def OnClose(self, event):
-        self.zConfInstance.close()
-        event.Skip()
-
-    def OnCancel(self, event):
-        self.zConfInstance.close()
-        event.Skip()
-
-    def OnOk(self, event):
-        self.zConfInstance.close()
+    def OnLocalButton(self, event):
+        self.URI = "LOCAL://"
+        self.EndModal(wx.ID_OK)
         event.Skip()
 
     # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py
     def GetListCtrl(self):
-        return self.list
-
-    def PopulateList(self):
-        self.list.InsertColumn(0, 'NAME')
-        self.list.InsertColumn(1, 'TYPE')
-        self.list.InsertColumn(2, 'IP')
-        self.list.InsertColumn(3, 'PORT')
-        self.list.SetColumnWidth(0, 150)
-        self.list.SetColumnWidth(1, 150)
-        self.list.SetColumnWidth(2, 150)
-        self.list.SetColumnWidth(3, 150)
+        return self.ServicesList
 
     def getColumnText(self, index, col):
-        item = self.list.GetItem(index, col)
+        item = self.ServicesList.GetItem(index, col)
         return item.GetText()
 
     def OnItemSelected(self, event):
-        self.currentItem = event.m_itemIndex
-        self.setresult()
+        self.SetURI(event.m_itemIndex)
         event.Skip()
 
     def OnItemActivated(self, event):
-        self.currentItem = event.m_itemIndex
-        self.setresult()
-        self.Close()
+        self.SetURI(event.m_itemIndex)
+        self.EndModal(wx.ID_OK)
         event.Skip()
 
-    def setresult(self):
-        connect_type = self.getColumnText(self.currentItem, 1)
-        connect_address = self.getColumnText(self.currentItem, 2)
-        connect_port = self.getColumnText(self.currentItem, 3)
-        
-        uri = self.CreateURI(connect_type, connect_address, connect_port)
-        self.my_result=uri
-
-    def GetResult(self):
-        return self.my_result
+    def SetURI(self, idx):
+        connect_type = self.getColumnText(idx, 1)
+        connect_address = self.getColumnText(idx, 2)
+        connect_port = self.getColumnText(idx, 3)
+        
+        self.URI = "%s://%s:%s"%(connect_type, connect_address, connect_port)
+
+    def GetURI(self):
+        return self.URI
         
     def removeService(self, zeroconf, type, name):
         '''
         called when a service with the desired type goes offline.
         '''
-
+        
         # loop through the list items looking for the service that went offline
-        for idx in xrange(self.list.GetItemCount()):
+        for idx in xrange(self.ServicesList.GetItemCount()):
             # this is the unique identifier assigned to the item
-            item_id = self.list.GetItemData(idx)
+            item_id = self.ServicesList.GetItemData(idx)
 
             # this is the full typename that was received by addService
             item_name = self.itemDataMap[item_id][4]
 
             if item_name == name:
-                self.list.DeleteItem(idx)
+                self.ServicesList.DeleteItem(idx)
                 break
-
+        
     def addService(self, zeroconf, type, name):
         '''
         called when a service with the desired type is discovered.
         '''
-
-        info = self.zConfInstance.getServiceInfo(type, name)
+        info = self.ZeroConfInstance.getServiceInfo(type, name)
 
         svcname  = name.split(".")[0]
         typename = type.split(".")[0][1:]
         ip       = str(socket.inet_ntoa(info.getAddress()))
         port     = info.getPort()
 
-        num_items = self.list.GetItemCount()
+        num_items = self.ServicesList.GetItemCount()
 
         # display the new data in the list
-        new_item = self.list.InsertStringItem(num_items, svcname)
-        self.list.SetStringItem(new_item, 1, "%s" % typename)
-        self.list.SetStringItem(new_item, 2, "%s" % ip)
-        self.list.SetStringItem(new_item, 3, "%s" % port)
+        new_item = self.ServicesList.InsertStringItem(num_items, svcname)
+        self.ServicesList.SetStringItem(new_item, 1, "%s" % typename)
+        self.ServicesList.SetStringItem(new_item, 2, "%s" % ip)
+        self.ServicesList.SetStringItem(new_item, 3, "%s" % port)
 
         # record the new data for the ColumnSorterMixin
         # we assign every list item a unique id (that won't change when items
         # are added or removed)
-        self.list.SetItemData(new_item, self.nextItemId)
+        self.ServicesList.SetItemData(new_item, self.nextItemId)
  
         # the value of each column has to be stored in the itemDataMap
         # so that ColumnSorterMixin knows how to sort the column.
@@ -218,11 +215,4 @@
         self.itemDataMap[self.nextItemId] = [ svcname, typename, ip, port, name ]
 
         self.nextItemId += 1
-
-    def CreateURI(self, connect_type, connect_address, connect_port):
-        uri = "%s://%s:%s"%(connect_type, connect_address, connect_port)
-        return uri
-
-    def ChooseLocalID(self, event):
-        self.my_result = "LOCAL://"
-        self.Close()
+        
\ No newline at end of file
--- a/plugger.py	Fri Sep 18 14:58:22 2009 +0200
+++ b/plugger.py	Mon Sep 21 12:12:08 2009 +0200
@@ -11,8 +11,6 @@
 
 #Quick hack to be able to find Beremiz IEC tools. Should be config params.
 base_folder = os.path.split(sys.path[0])[0]
-sys.path.append(os.path.join(base_folder, "plcopeneditor"))
-sys.path.append(os.path.join(base_folder, "docutils"))
 
 from docpdf import *
 from xmlclass import GenerateClassesFromXSDstring
@@ -178,6 +176,9 @@
             self.PlugParams[1].setElementValue(parts[1], value)
         return value, False
 
+    def PlugMakeDir(self):
+        os.mkdir(self.PlugPath())
+
     def PlugRequestSave(self):
         # If plugin do not have corresponding directory
         plugpath = self.PlugPath()
@@ -533,7 +534,7 @@
                     _self.ChangesToSave = False
                 else:
                     # If plugin do not have corresponding file/dirs - they will be created on Save
-                    os.mkdir(_self.PlugPath())
+                    _self.PlugMakeDir()
                     # Find an IEC number
                     _self.FindNewIEC_Channel(0)
                     # Call the plugin real __init__
@@ -722,6 +723,7 @@
         self.logger = logger
         self._builder = None
         self._connector = None
+        self.Deleting = False
         
         # Setup debug information
         self.IECdebug_datas = {}
@@ -748,8 +750,6 @@
         # After __init__ root plugin is not valid
         self.ProjectPath = None
         self.BuildPath = None
-        self.PLCEditor = None
-        self.PLCDebug = None
         self.DebugThread = None
         self.debug_break = False
         self.previous_plcstate = None
@@ -759,6 +759,9 @@
         self.PluginMethods = [dic.copy() for dic in self.PluginMethods]
         self.LoadSTLibrary()
 
+    def __del__(self):
+        self.Deleting = True
+
     def PluginLibraryFilePath(self):
         return os.path.join(os.path.split(__file__)[0], "pous.xml")
 
@@ -864,26 +867,18 @@
     def SaveProject(self):
         if not self.SaveXMLFile():
             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
-        if self.PLCEditor:
-            self.PLCEditor.RefreshTitle()
         result = self.PlugRequestSave()
         if result:
             self.logger.write_error(result)
     
-    def CloseProject(self):
-        if self.PLCEditor is not None:
-            self.PLCEditor.Close()
-        if self.PLCDebug is not None:
-            self.PLCDebug.Close()
-        
-    
     # Update PLCOpenEditor Plugin Block types from loaded plugins
     def RefreshPluginsBlockLists(self):
         if getattr(self, "PluggedChilds", None) is not None:
             self.ClearPluginTypes()
             self.AddPluginBlockList(self.PluginsBlockTypesFactory())
-        if self.PLCEditor is not None:
-            self.PLCEditor.RefreshEditor()
+        if self.AppFrame is not None:
+            self.AppFrame.RefreshLibraryTree()
+            self.AppFrame.RefreshEditor()
     
     def PluginPath(self):
         return os.path.join(os.path.split(__file__)[0], "plugins")
@@ -1233,8 +1228,8 @@
         """
         Method called by user to (re)build SoftPLC and plugin tree
         """
-        if self.PLCEditor is not None:
-            self.PLCEditor.ClearErrors()
+        if self.AppFrame is not None:
+            self.AppFrame.ClearErrors()
         
         buildpath = self._getBuildPath()
 
@@ -1330,11 +1325,11 @@
     
     def ShowError(self, logger, from_location, to_location):
         chunk_infos = self.GetChunkInfos(from_location, to_location)
-        self._EditPLC()
         for infos, (start_row, start_col) in chunk_infos:
             start = (from_location[0] - start_row, from_location[1] - start_col)
             end = (to_location[0] - start_row, to_location[1] - start_col)
-            self.PLCEditor.ShowError(infos, start, end)
+            if self.AppFrame is not None:
+                self.AppFrame.ShowError(infos, start, end)
 
     def _showIECcode(self):
         plc_file = self._getIECcodepath()
@@ -1361,18 +1356,6 @@
             
         new_dialog.Show()
 
-    def _EditPLC(self):
-        if self.PLCEditor is None:
-            self.RefreshPluginsBlockLists()
-            def _onclose():
-                self.PLCEditor = None
-            def _onsave():
-                self.SaveProject()
-            self.PLCEditor = PLCOpenEditor(self.AppFrame, self)
-            self.PLCEditor._onclose = _onclose
-            self.PLCEditor._onsave = _onsave
-            self.PLCEditor.Show()
-
     def _Clean(self):
         if os.path.isdir(os.path.join(self._getBuildPath())):
             self.logger.write(_("Cleaning the build directory\n"))
@@ -1475,11 +1458,12 @@
         if self.DebugTimer is not None:
             self.DebugTimer.cancel()
 
-        # Timer to prevent rapid-fire when registering many variables
-        # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead
-        self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector])
-        # Rearm anti-rapid-fire timer
-        self.DebugTimer.start()
+        if not self.Deleting:
+            # Timer to prevent rapid-fire when registering many variables
+            # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead
+            self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector])
+            # Rearm anti-rapid-fire timer
+            self.DebugTimer.start()
 
         
     def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs):
@@ -1579,15 +1563,8 @@
         if self.GetIECProgramsAndVariables():
             self._connector.StartPLC(debug=True)
             self.logger.write(_("Starting PLC (debug mode)\n"))
-            if self.PLCDebug is None:
-                self.RefreshPluginsBlockLists()
-                def _onclose():
-                    self.PLCDebug = None
-                self.PLCDebug = PLCOpenEditor(self.AppFrame, self, debug=True)
-                self.PLCDebug._onclose = _onclose
-                self.PLCDebug.Show()
-            else:
-                self.PLCDebug.ResetGraphicViewers()
+            if self.AppFrame:
+                self.AppFrame.ResetGraphicViewers()
             self.DebugThread = Thread(target=self.DebugThreadProc)
             self.DebugThread.start()
         else:
@@ -1639,11 +1616,14 @@
         # if uri is empty launch discovery dialog
         if uri == "":
             # Launch Service Discovery dialog
-            dia = DiscoveryDialog(self.AppFrame)
-            dia.ShowModal()
-            uri = dia.GetResult()
+            dialog = DiscoveryDialog(self.AppFrame)
+            answer = dialog.ShowModal()
+            uri = dialog.GetURI()
+            dialog.Destroy()
+            
             # Nothing choosed or cancel button
-            if uri is None:
+            if uri is None or answer == wx.ID_CANCEL:
+                self.logger.write_error(_("Connection canceled!\n"))
                 return
             else:
                 self.\
@@ -1733,9 +1713,9 @@
             data = builder.GetBinaryCode()
             if data is not None :
                 if self._connector.NewPLC(MD5, data, extrafiles):
-                    if self.PLCDebug is not None:
-                        self.PLCDebug.Close()
-                        self.PLCDebug = None
+                    if self.AppFrame is not None:
+                        self.AppFrame.CloseDebugTabs()
+                        self.AppFrame.RefreshInstanceTree()
                     self.UnsubscribeAllDebugIECVariable()
                     self.ProgramTransferred()
                     self.logger.write(_("Transfer completed successfully.\n"))
@@ -1746,10 +1726,6 @@
         self.UpdateMethodsFromPLCStatus()
 
     PluginMethods = [
-        {"bitmap" : opjimg("editPLC"),
-         "name" : _("Edit PLC"),
-         "tooltip" : _("Edit PLC program with PLCOpenEditor"),
-         "method" : "_EditPLC"},
         {"bitmap" : opjimg("Build"),
          "name" : _("Build"),
          "tooltip" : _("Build project into build folder"),
--- a/runtime/PLCObject.py	Fri Sep 18 14:58:22 2009 +0200
+++ b/runtime/PLCObject.py	Mon Sep 21 12:12:08 2009 +0200
@@ -111,6 +111,7 @@
             else:
                 def DummyIterator(res):
                     self.DummyIteratorLock.acquire()
+                    self.DummyIteratorLock.release()
                     return None
                 self._PythonIterator = DummyIterator
                 
@@ -171,36 +172,10 @@
         self.PLClibraryHandle = None
         # Unload library explicitely
         if getattr(self,"_PLClibraryHandle",None) is not None:
-            PLCprint("Unload PLC")
             dlclose(self._PLClibraryHandle)
-            res = self._DetectDirtyLibs()
-        else:
-            res = False
-
-        self._PLClibraryHandle = None
+            self._PLClibraryHandle = None
+        
         self.PLClibraryLock.release()
-        return res
-
-    def _DetectDirtyLibs(self):
-        # Detect dirty libs
-        # Get lib dependencies (for dirty lib detection)
-        if os.name == "posix":
-            # parasiting libs listed with ldd
-            badlibs = [ toks.split()[0] for toks in commands.getoutput(
-                            "ldd "+self._GetLibFileName()).splitlines() ]
-            for badlib in badlibs:
-                if badlib[:6] in ["libwx_",
-                                  "libwxs",
-                                  "libgtk",
-                                  "libgdk",
-                                  "libatk",
-                                  "libpan",
-                                  "libX11",
-                                  ]:
-                    #badhandle = dlopen(badlib, dl.RTLD_NOLOAD)
-                    PLCprint("Dirty lib detected :" + badlib)
-                    #dlclose(badhandle)
-                    return True
         return False
 
     def PrepareRuntimePy(self):
@@ -308,7 +283,7 @@
     
     def NewPLC(self, md5sum, data, extrafiles):
         PLCprint("NewPLC (%s)"%md5sum)
-        if self.PLCStatus in ["Stopped", "Empty", "Dirty", "Broken"]:
+        if self.PLCStatus in ["Stopped", "Empty", "Broken"]:
             NewFileName = md5sum + lib_ext
             extra_files_log = os.path.join(self.workingdir,"extra_files.txt")
             try:
--- a/targets/Linux/plc_Linux_main.c	Fri Sep 18 14:58:22 2009 +0200
+++ b/targets/Linux/plc_Linux_main.c	Mon Sep 21 12:12:08 2009 +0200
@@ -64,7 +64,7 @@
 }
 
 
-static int __debug_tick;
+static unsigned long __debug_tick;
 
 static pthread_mutex_t python_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t python_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -75,8 +75,8 @@
 int startPLC(int argc,char **argv)
 {
     struct sigevent sigev;
-    /* Translate PLC's microseconds to Ttick nanoseconds */
-    Ttick = 1000000 * maxval(common_ticktime__,1);
+    /* Define Ttick to 1ms if common_ticktime not defined */
+    Ttick = common_ticktime__?common_ticktime__:1000000;
 
     memset (&sigev, 0, sizeof (struct sigevent));
     sigev.sigev_value.sival_int = 0;
@@ -97,6 +97,8 @@
         /* install signal handler for manual break */
 //        signal(SIGTERM, catch_signal);
         signal(SIGINT, catch_signal);
+
+        pthread_mutex_trylock(&debug_mutex);
     }else{
         return 1;
     }
@@ -120,6 +122,7 @@
     timer_delete (PLC_timer);
     __cleanup();
     __debug_tick = -1;
+    pthread_mutex_unlock(&debug_mutex);
     pthread_mutex_unlock(&debug_wait_mutex);
     pthread_mutex_destroy(&debug_wait_mutex);
     pthread_mutex_unlock(&python_wait_mutex);
@@ -127,9 +130,9 @@
     return 0;
 }
 
-extern int __tick;
+extern unsigned long __tick;
 /* from plc_debugger.c */
-int WaitDebugData()
+unsigned long WaitDebugData()
 {
     /* Wait signal from PLC thread */
     if(pthread_mutex_lock(&debug_wait_mutex)) return -1;
--- a/targets/Win32/plc_Win32_main.c	Fri Sep 18 14:58:22 2009 +0200
+++ b/targets/Win32/plc_Win32_main.c	Mon Sep 21 12:12:08 2009 +0200
@@ -8,7 +8,7 @@
 #include <windows.h>
 
 /* provided by POUS.C */
-extern int common_ticktime__;
+extern unsigned long common_ticktime__;
 
 long AtomicCompareExchange(long* atomicvar, long compared, long exchange)
 {
@@ -74,8 +74,8 @@
 int startPLC(int argc,char **argv)
 {
 	unsigned long thread_id = 0;
-	/* Translate PLC's microseconds to Ttick nanoseconds */
-	Ttick = 1000000 * maxval(common_ticktime__,1);
+	/* Define Ttick to 1ms if common_ticktime not defined */
+    Ttick = common_ticktime__?common_ticktime__:1000000;
 
 	debug_sem = CreateSemaphore(
 							NULL,           // default security attributes
@@ -142,7 +142,7 @@
     }
     return 0;
 }
-static int __debug_tick;
+static unsigned long __debug_tick;
 
 int TryEnterDebugSection(void)
 {
@@ -170,7 +170,7 @@
 }
 
 /* from plc_debugger.c */
-int WaitDebugData()
+unsigned long WaitDebugData()
 {
 	if(WaitForSingleObject(debug_wait_sem, INFINITE) != WAIT_OBJECT_0) return -1;
 	return __debug_tick;
--- a/targets/Xenomai/plc_Xenomai_main.c	Fri Sep 18 14:58:22 2009 +0200
+++ b/targets/Xenomai/plc_Xenomai_main.c	Mon Sep 21 12:12:08 2009 +0200
@@ -29,7 +29,7 @@
 #define WAITDEBUG_PIPE_SIZE         500
 
 /* provided by POUS.C */
-extern int common_ticktime__;
+extern unsigned long common_ticktime__;
 
 long AtomicCompareExchange(long* atomicvar,long compared, long exchange)
 {
@@ -72,7 +72,7 @@
     }
 }
 
-static int __debug_tick;
+static unsigned long __debug_tick;
 
 RT_SEM python_wait_sem;
 RT_MUTEX python_mutex;
@@ -142,8 +142,8 @@
     /* ne-memory-swapping for this program */
     mlockall(MCL_CURRENT | MCL_FUTURE);
 
-    /* Translate PLC's microseconds to Ttick nanoseconds */
-    Ttick = 1000000 * max_val(common_ticktime__,1);
+    /* Define Ttick to 1ms if common_ticktime not defined */
+    Ttick = common_ticktime__?common_ticktime__:1000000;
 
     /* create python_wait_sem */
     ret = rt_sem_create(&python_wait_sem, "python_wait_sem", 0, S_FIFO);
@@ -203,9 +203,9 @@
     rt_mutex_release(&debug_mutex);
 }
 
-extern int __tick;
+extern unsigned long __tick;
 /* from plc_debugger.c */
-int WaitDebugData()
+unsigned long WaitDebugData()
 {
     char message;
     int res;
--- a/targets/plc_common_main.c	Fri Sep 18 14:58:22 2009 +0200
+++ b/targets/plc_common_main.c	Mon Sep 21 12:12:08 2009 +0200
@@ -5,13 +5,13 @@
 #include <locale.h>
 #include "iec_types.h"
 /*
- * Prototypes of functions provied by generated C softPLC
+ * Prototypes of functions provided by generated C softPLC
  **/
-void config_run__(int tick);
+void config_run__(unsigned long tick);
 void config_init__(void);
 
 /*
- * Prototypes of functions provied by generated target C code
+ * Prototypes of functions provided by generated target C code
  * */
 void __init_debug(void);
 void __cleanup_debug(void);
@@ -22,8 +22,13 @@
  *  Variables used by generated C softPLC and plugins
  **/
 IEC_TIME __CURRENT_TIME;
-IEC_BOOL __DEBUG;
-int __tick = -1;
+IEC_BOOL __DEBUG = 0;
+unsigned long __tick = -1;
+
+/*
+ *  Variable generated by C softPLC and plugins
+ **/
+extern unsigned long greatest_tick_count__;
 
 /* Help to quit cleanly when init fail at a certain level */
 static int init_level = 0;
@@ -39,6 +44,8 @@
 void __run()
 {
     __tick++;
+    if (greatest_tick_count__)
+        __tick %%= greatest_tick_count__;
 
     %(retrieve_calls)s
 
@@ -53,7 +60,7 @@
 }
 
 /*
- * Initialize variables according to PLC's defalut values,
+ * Initialize variables according to PLC's default values,
  * and then init plugins with that values
  **/
 int __init(int argc,char **argv)
@@ -85,7 +92,7 @@
 static long long Tsync = 0;
 static long long FreqCorr = 0;
 static int Nticks = 0;
-static int  last_tick = 0;
+static unsigned long last_tick = 0;
 static long long Ttick = 0;
 #define mod %%
 /*
--- a/targets/plc_debug.c	Fri Sep 18 14:58:22 2009 +0200
+++ b/targets/plc_debug.c	Mon Sep 21 12:12:08 2009 +0200
@@ -70,7 +70,7 @@
 extern long AtomicCompareExchange(long*, long, long);
 extern void InitiateDebugTransfer(void);
 
-extern int __tick;
+extern unsigned long __tick;
 void __publish_debug()
 {
     /* Check there is no running debugger re-configuration */
@@ -102,7 +102,7 @@
                 /* compute next cursor positon*/
                 next_cursor = buffer_cursor + size;
                 /* if buffer not full */
-                if(next_cursor < debug_buffer + BUFFER_SIZE)
+                if(next_cursor <= debug_buffer + BUFFER_SIZE)
                 {
                     /* copy data to the buffer */
                     memcpy(buffer_cursor, my_var->ptrvalue, size);