Merge with 092060fd8afb1d950fe467724ce0209bddc48716
authoredouard
Mon, 02 Nov 2009 15:38:49 +0100
changeset 435 75fe73597273
parent 431 fcd344deae84 (current diff)
parent 434 092060fd8afb (diff)
child 436 c926a8037adc
Merge with 092060fd8afb1d950fe467724ce0209bddc48716
beremiz_postinst.py
images/icons.svg
setup.py
--- a/Beremiz.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/Beremiz.py	Mon Nov 02 15:38:49 2009 +0100
@@ -293,6 +293,8 @@
               kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O'))
         AppendMenu(parent, help='', id=wx.ID_SAVE,
               kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
+        AppendMenu(parent, help='', id=wx.ID_SAVEAS,
+              kind=wx.ITEM_NORMAL, text=_(u'Save as\tCTRL+SHIFT+S'))
         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,
@@ -314,6 +316,7 @@
         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.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
         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)
@@ -367,6 +370,7 @@
         self.PLCConfig.SetBackgroundColour(wx.WHITE)
         self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
         self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow)
+        self.PLCConfig.Bind(wx.EVT_MOUSEWHEEL, self.OnPLCConfigScroll)
         self.BottomNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True)
         
         self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='',
@@ -388,6 +392,8 @@
         self.local_runtime_tmpdir = None
         
         self.DisableEvents = False
+        # Variable allowing disabling of PLCConfig scroll when Popup shown 
+        self.ScrollingEnabled = True
         
         self.PluginInfos = {}
         
@@ -477,39 +483,46 @@
                 infos = self.PluginRoot.ShowError(self.Log,
                                                   (int(first_line), int(first_column)), 
                                                   (int(last_line), int(last_column)))
-		
+	
+    ## Function displaying an Error dialog in PLCOpenEditor.
+    #  @return False if closing cancelled.
+    def CheckSaveBeforeClosing(self, title=_("Close Project")):
+        if self.PluginRoot.ProjectTestModified():
+            dialog = wx.MessageDialog(self,
+                                      _("There are changes, do you want to save?"),
+                                      title,
+                                      wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+            answer = dialog.ShowModal()
+            dialog.Destroy()
+            if answer == wx.ID_YES:
+                self.PluginRoot.SaveProject()
+            elif answer == wx.ID_CANCEL:
+                return False
+        return True
+    
     def OnCloseFrame(self, event):
-        if self.PluginRoot is not None:
-            if self.PluginRoot.ProjectTestModified():
-                dialog = wx.MessageDialog(self,
-                                          _("Save changes ?"),
-                                          _("Close Application"), 
-                                          wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                answer = dialog.ShowModal()
-                dialog.Destroy()
-                if answer == wx.ID_YES:
-                    self.PluginRoot.SaveProject()
-                    event.Skip()
-                elif answer == wx.ID_NO:
-                    event.Skip()
-                    return
-                else:
-                    event.Veto()
-                    return
-
-        self.KillLocalRuntime()
-        
-        event.Skip()
+        if self.PluginRoot is None or self.CheckSaveBeforeClosing(_("Close Application")):
+            self.KillLocalRuntime()
+            event.Skip()
+        else:
+            event.Veto()
     
     def OnMoveWindow(self, event):
         self.GetBestSize()
         self.RefreshScrollBars()
         event.Skip()
     
+    def EnableScrolling(self, enable):
+        self.ScrollingEnabled = enable
+    
+    def OnPLCConfigScroll(self, event):
+        if self.ScrollingEnabled:
+            event.Skip()
+
     def OnPanelLeftDown(self, event):
         focused = self.FindFocus()
         if isinstance(focused, TextCtrlAutoComplete.TextCtrlAutoComplete):
-            focused._showDropDown(False)
+            focused.DismissListBox()
         event.Skip()
     
     def RefreshFileMenu(self):
@@ -533,6 +546,7 @@
                 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_SAVEAS, True)
             self.FileMenu.Enable(wx.ID_PROPERTIES, True)
             self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
         else:
@@ -541,6 +555,7 @@
             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_SAVEAS, False)
             self.FileMenu.Enable(wx.ID_PROPERTIES, False)
             self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
         
@@ -1201,7 +1216,7 @@
                 id = wx.NewId()
                 if isinstance(element_infos["type"], types.ListType):
                     combobox = wx.ComboBox(id=id, name=element_infos["name"], parent=parent, 
-                        pos=wx.Point(0, 0), size=wx.Size(150, 28), style=wx.CB_READONLY)
+                        pos=wx.Point(0, 0), size=wx.Size(300, 28), style=wx.CB_READONLY)
                     boxsizer.AddWindow(combobox, 0, border=0, flag=0)
                     if element_infos["use"] == "optional":
                         combobox.Append("")
@@ -1234,7 +1249,7 @@
                     if "max" in element_infos["type"]:
                         scmax = element_infos["type"]["max"]
                     spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent, 
-                        pos=wx.Point(0, 0), size=wx.Size(150, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
+                        pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
                     spinctrl.SetRange(scmin,scmax)
                     boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
                     spinctrl.SetValue(element_infos["value"])
@@ -1253,7 +1268,7 @@
                             scmin = -(2**31)
                         scmax = 2**31-1
                         spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent, 
-                            pos=wx.Point(0, 0), size=wx.Size(150, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
+                            pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
                         spinctrl.SetRange(scmin, scmax)
                         boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
                         spinctrl.SetValue(element_infos["value"])
@@ -1263,10 +1278,11 @@
                         textctrl = TextCtrlAutoComplete.TextCtrlAutoComplete(id=id, 
                                                                      name=element_infos["name"], 
                                                                      parent=parent, 
+                                                                     appframe=self, 
                                                                      choices=choices, 
                                                                      element_path=element_path,
                                                                      pos=wx.Point(0, 0), 
-                                                                     size=wx.Size(150, 25), 
+                                                                     size=wx.Size(300, 25), 
                                                                      style=0)
                         
                         boxsizer.AddWindow(textctrl, 0, border=0, flag=0)
@@ -1284,6 +1300,9 @@
         self.DebugVariablePanel.SetDataProducer(None)
     
     def OnNewProjectMenu(self, event):
+        if self.PluginRoot is not None and not self.CheckSaveBeforeClosing():
+            return
+        
         if not self.Config.HasEntry("lastopenedfolder"):
             defaultpath = os.path.expanduser("~")
         else:
@@ -1309,6 +1328,9 @@
             self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
     
     def OnOpenProjectMenu(self, event):
+        if self.PluginRoot is not None and not self.CheckSaveBeforeClosing():
+            return
+        
         if not self.Config.HasEntry("lastopenedfolder"):
             defaultpath = os.path.expanduser("~")
         else:
@@ -1337,21 +1359,12 @@
         dialog.Destroy()
     
     def OnCloseProjectMenu(self, event):
-        if self.PluginRoot is not None:
-            if self.PluginRoot.ProjectTestModified():
-                dialog = wx.MessageDialog(self,
-                                          _("Save changes ?"),
-                                          _("Close Application"), 
-                                          wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                answer = dialog.ShowModal()
-                dialog.Destroy()
-                if answer == wx.ID_YES:
-                    self.PluginRoot.SaveProject()
-                elif answer == wx.ID_CANCEL:
-                    return
-            self.ResetView()
-            self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
-            self.RefreshAll()
+        if self.PluginRoot is not None and not self.CheckSaveBeforeClosing():
+            return
+        
+        self.ResetView()
+        self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
+        self.RefreshAll()
     
     def OnSaveProjectMenu(self, event):
         if self.PluginRoot is not None:
@@ -1359,6 +1372,13 @@
             self.RefreshAll()
             self.RefreshTitle()
     
+    def OnSaveProjectAsMenu(self, event):
+        if self.PluginRoot is not None:
+            self.PluginRoot.SaveProjectAs()
+            self.RefreshAll()
+            self.RefreshTitle()
+        event.Skip()
+    
     def OnPropertiesMenu(self, event):
         self.ShowProperties()
     
@@ -1391,24 +1411,26 @@
         return DeleteButtonFunction
     
     def AddPlugin(self, PluginType, plugin):
-        dialog = wx.TextEntryDialog(self, _("Please enter a name for plugin:"), _("Add Plugin"), "", wx.OK|wx.CANCEL)
-        if dialog.ShowModal() == wx.ID_OK:
-            PluginName = dialog.GetValue()
-            plugin.PlugAddChild(PluginName, PluginType)
-            self.RefreshPluginTree()
-            self.PluginRoot.RefreshPluginsBlockLists()
-        dialog.Destroy()
+        if self.PluginRoot.CheckProjectPathPerm():
+            dialog = wx.TextEntryDialog(self, _("Please enter a name for plugin:"), _("Add Plugin"), "", wx.OK|wx.CANCEL)
+            if dialog.ShowModal() == wx.ID_OK:
+                PluginName = dialog.GetValue()
+                plugin.PlugAddChild(PluginName, PluginType)
+                self.RefreshPluginTree()
+                self.PluginRoot.RefreshPluginsBlockLists()
+            dialog.Destroy()
     
     def DeletePlugin(self, plugin):
-        dialog = wx.MessageDialog(self, _("Really delete plugin ?"), _("Remove plugin"), wx.YES_NO|wx.NO_DEFAULT)
-        if dialog.ShowModal() == wx.ID_YES:
-            self.PluginInfos.pop(plugin)
-            plugin.PlugRemove()
-            del plugin
-            self.PluginRoot.RefreshPluginsBlockLists()
-            self.RefreshPluginTree()
-        dialog.Destroy()
-
+        if self.PluginRoot.CheckProjectPathPerm():
+            dialog = wx.MessageDialog(self, _("Really delete plugin ?"), _("Remove plugin"), wx.YES_NO|wx.NO_DEFAULT)
+            if dialog.ShowModal() == wx.ID_YES:
+                self.PluginInfos.pop(plugin)
+                plugin.PlugRemove()
+                del plugin
+                self.PluginRoot.RefreshPluginsBlockLists()
+                self.RefreshPluginTree()
+            dialog.Destroy()
+    
 #-------------------------------------------------------------------------------
 #                               Exception Handler
 #-------------------------------------------------------------------------------
@@ -1436,10 +1458,8 @@
 An unhandled exception (bug) occured. Bug report saved at :
 (%s)
 
-Please contact LOLITech at:
-+33 (0)3 29 57 60 42
-or please be kind enough to send this file to:
-bugs_beremiz@lolitech.fr
+Please be kind enough to send this file to:
+edouard.tisserant@gmail.com
 
 You should now restart Beremiz.
 
--- a/TextCtrlAutoComplete.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/TextCtrlAutoComplete.py	Mon Nov 02 15:38:49 2009 +0100
@@ -1,207 +1,250 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-#This file is part of Beremiz, a Integrated Development Environment for
-#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
-#
-#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
-#
-#See COPYING file for copyrights details.
-#
-#This library is free software; you can redistribute it and/or
-#modify it under the terms of the GNU General Public
-#License as published by the Free Software Foundation; either
-#version 2.1 of the License, or (at your option) any later version.
-#
-#This library is distributed in the hope that it will be useful,
-#but WITHOUT ANY WARRANTY; without even the implied warranty of
-#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#General Public License for more details.
-#
-#You should have received a copy of the GNU General Public
-#License along with this library; if not, write to the Free Software
-#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-import wx
-import cPickle
-
-MAX_ITEM_COUNT = 10
-MAX_ITEM_SHOWN = 6
-ITEM_HEIGHT = 25
-
-class TextCtrlAutoComplete(wx.TextCtrl):
-
-    def __init__ (self, parent, choices=None, dropDownClick=True,
-                  element_path=None, **therest):
-        """
-        Constructor works just like wx.TextCtrl except you can pass in a
-        list of choices.  You can also change the choice list at any time
-        by calling setChoices.
-        """
-
-        therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0)
-
-        wx.TextCtrl.__init__(self, parent, **therest)
-
-        #Some variables
-        self._dropDownClick = dropDownClick
-        self._lastinsertionpoint = None
-        
-        self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
-        self.element_path = element_path
-        
-        #widgets
-        self.dropdown = wx.PopupWindow(self)
-
-        #Control the style
-        flags = wx.LB_HSCROLL | wx.LB_SINGLE | wx.LB_SORT
-        
-        #Create the list and bind the events
-        self.dropdownlistbox = wx.ListBox(self.dropdown, style=flags,
-                                 pos=wx.Point(0, 0))
-        
-        self.SetChoices(choices)
-
-        #gp = self
-        #while ( gp != None ) :
-        #    gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp )
-        #    gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp )
-        #    gp = gp.GetParent()
-
-        self.Bind(wx.EVT_KILL_FOCUS, self.onControlChanged, self)
-        self.Bind(wx.EVT_TEXT_ENTER, self.onControlChanged, self)
-        self.Bind(wx.EVT_TEXT, self.onEnteredText, self)
-        self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown, self)
-
-        #If need drop down on left click
-        if dropDownClick:
-            self.Bind(wx.EVT_LEFT_DOWN , self.onClickToggleDown, self)
-            self.Bind(wx.EVT_LEFT_UP , self.onClickToggleUp, self)
-
-        self.dropdownlistbox.Bind(wx.EVT_LISTBOX, self.onListItemSelected)
-        self.dropdownlistbox.Bind(wx.EVT_LISTBOX_DCLICK, self.onListItemSelected)
-
-    def ChangeValue(self, value):
-        wx.TextCtrl.ChangeValue(self, value)
-        self._refreshListBoxChoices()
-
-    def onEnteredText(self, event):
-        wx.CallAfter(self._refreshListBoxChoices)
-        event.Skip()
-
-    def onKeyDown(self, event) :
-        """ Do some work when the user press on the keys:
-            up and down: move the cursor
-        """
-        visible = self.dropdown.IsShown()
-        keycode = event.GetKeyCode()
-        if keycode in [wx.WXK_DOWN, wx.WXK_UP]:
-            if not visible:
-                self._showDropDown()
-            elif keycode == wx.WXK_DOWN:
-                self._moveSelection(1)
-            else:
-                self._moveSelection(-1)
-        elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and visible:
-            if self.dropdownlistbox.GetSelection() != wx.NOT_FOUND:
-                self._setValueFromSelected()
-            else:
-                self._showDropDown(False)
-                event.Skip()
-        elif event.GetKeyCode() == wx.WXK_ESCAPE:
-            self._showDropDown(False)
-        else:
-            event.Skip()
-
-    def onListItemSelected(self, event):
-        self._setValueFromSelected()
-        event.Skip()
-
-    def onClickToggleDown(self, event):
-        self._lastinsertionpoint = self.GetInsertionPoint()
-        event.Skip()
-
-    def onClickToggleUp(self, event):
-        if self.GetInsertionPoint() == self._lastinsertionpoint:
-            self._showDropDown(not self.dropdown.IsShown())
-        self._lastinsertionpoint = None
-        event.Skip()
-
-    def onControlChanged(self, event):
-        res = self.GetValue()
-        config = wx.ConfigBase.Get()
-        listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([]))))
-        if res and res not in listentries:
-            listentries = (listentries + [res])[-MAX_ITEM_COUNT:]
-            config.Write(self.element_path, cPickle.dumps(listentries))
-            config.Flush()
-            self.SetChoices(listentries)
-        self._showDropDown(False)
-        event.Skip()
-    
-    def SetChoices(self, choices):
-        self._choices = choices
-        self._refreshListBoxChoices()
-        
-    def GetChoices(self):
-        return self._choices
-    
-#-------------------------------------------------------------------------------
-#                           Internal methods
-#-------------------------------------------------------------------------------
-
-    def _refreshListBoxChoices(self):
-        text = self.GetValue()
-
-        self.dropdownlistbox.Clear()
-        for choice in self._choices:
-            if choice.startswith(text):
-                self.dropdownlistbox.Append(choice)
-        
-        itemcount = min(len(self.dropdownlistbox.GetStrings()), MAX_ITEM_SHOWN)
-        self.popupsize = wx.Size(self.GetSize()[0], ITEM_HEIGHT * itemcount + 4)
-        self.dropdownlistbox.SetSize(self.popupsize)
-        self.dropdown.SetClientSize(self.popupsize)
-    
-    def _moveSelection(self, direction):
-        selected = self.dropdownlistbox.GetSelection()
-        if selected == wx.NOT_FOUND:
-            if direction >= 0:
-                selected = 0
-            else:
-                selected = self.dropdownlistbox.GetCount() - 1
-        else:
-            selected = (selected + direction) % (self.dropdownlistbox.GetCount() + 1)
-        if selected == self.dropdownlistbox.GetCount():
-            selected = wx.NOT_FOUND
-        self.dropdownlistbox.SetSelection(selected)
-    
-    def _setValueFromSelected(self):
-         """
-         Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
-         Will do nothing if no item is selected in the wx.ListCtrl.
-         """
-         selected = self.dropdownlistbox.GetStringSelection()
-         if selected:
-            self.SetValue(selected)
-            self._showDropDown(False)
-
-
-    def _showDropDown(self, show=True) :
-        """
-        Either display the drop down list (show = True) or hide it (show = False).
-        """
-        if show :
-            size = self.dropdown.GetSize()
-            width, height = self.GetSizeTuple()
-            x, y = self.ClientToScreenXY(0, height)
-            if size.GetWidth() != width :
-                size.SetWidth(width)
-                self.dropdown.SetSize(size)
-                self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
-            if (y + size.GetHeight()) < self._screenheight :
-                self.dropdown.SetPosition(wx.Point(x, y))
-            else:
-                self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight()))
-        self.dropdown.Show(show) 
-
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of Beremiz, a Integrated Development Environment for
+#programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import wx
+import cPickle
+
+MAX_ITEM_COUNT = 10
+MAX_ITEM_SHOWN = 6
+if wx.Platform == '__WXMSW__':
+    ITEM_INTERVAL_HEIGHT = 3
+else:
+    ITEM_INTERVAL_HEIGHT = 6
+
+if wx.Platform == '__WXMSW__':
+    popupclass = wx.PopupTransientWindow
+else:
+    popupclass = wx.PopupWindow
+
+class PopupWithListbox(popupclass):
+    
+    def __init__(self, parent, choices=[]):
+        popupclass.__init__(self, parent, wx.SIMPLE_BORDER)
+        
+        self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT)
+        if not wx.Platform == '__WXMSW__':
+            self.ListBox.Bind(wx.EVT_LISTBOX, self.OnListBoxClick)
+            self.ListBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnListBoxClick)
+            
+        self.SetChoices(choices)
+        
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+    
+    def SetChoices(self, choices):
+        max_text_width = 0
+        max_text_height = 0
+        
+        self.ListBox.Clear()
+        for choice in choices:
+            self.ListBox.Append(choice)
+            w, h = self.ListBox.GetTextExtent(choice)
+            max_text_width = max(max_text_width, w)
+            max_text_height = max(max_text_height, h)
+        
+        itemcount = min(len(choices), MAX_ITEM_SHOWN)
+        width = self.Parent.GetSize()[0]
+        height = max_text_height * itemcount + ITEM_INTERVAL_HEIGHT * (itemcount + 1)
+        if max_text_width + 10 > width:
+            height += 15
+        size = wx.Size(width, height)
+        self.ListBox.SetSize(size)
+        self.SetClientSize(size)
+    
+    def MoveSelection(self, direction):
+        selected = self.ListBox.GetSelection()
+        if selected == wx.NOT_FOUND:
+            if direction >= 0:
+                selected = 0
+            else:
+                selected = self.ListBox.GetCount() - 1
+        else:
+            selected = (selected + direction) % (self.ListBox.GetCount() + 1)
+        if selected == self.ListBox.GetCount():
+            selected = wx.NOT_FOUND
+        self.ListBox.SetSelection(selected)
+    
+    def GetSelection(self):
+        return self.ListBox.GetStringSelection()
+    
+    def ProcessLeftDown(self, event):
+        selected = self.ListBox.HitTest(wx.Point(event.m_x, event.m_y))
+        if selected != wx.NOT_FOUND:
+            wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected))
+        return False
+    
+    def OnListBoxClick(self, event):
+        selected = event.GetSelection()
+        if selected != wx.NOT_FOUND:
+            wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected))
+        event.Skip()
+    
+    def OnKeyDown(self, event):
+        self.Parent.ProcessEvent(event)
+
+    def OnDismiss(self):
+        self.Parent.listbox = None
+        wx.CallAfter(self.Parent.DismissListBox)
+    
+class TextCtrlAutoComplete(wx.TextCtrl):
+
+    def __init__ (self, parent, appframe, choices=None, dropDownClick=True,
+                  element_path=None, **therest):
+        """
+        Constructor works just like wx.TextCtrl except you can pass in a
+        list of choices.  You can also change the choice list at any time
+        by calling setChoices.
+        """
+
+        therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0)
+
+        wx.TextCtrl.__init__(self, parent, **therest)
+        self.AppFrame = appframe
+        
+        #Some variables
+        self._dropDownClick = dropDownClick
+        self._lastinsertionpoint = None
+        
+        self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
+        self.element_path = element_path
+        
+        self.listbox = None
+        
+        self.SetChoices(choices)
+
+        #gp = self
+        #while ( gp != None ) :
+        #    gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp )
+        #    gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp )
+        #    gp = gp.GetParent()
+
+        self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged)
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnControlChanged)
+        self.Bind(wx.EVT_TEXT, self.OnEnteredText)
+        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
+
+        #If need drop down on left click
+        if dropDownClick:
+            self.Bind(wx.EVT_LEFT_DOWN, self.OnClickToggleDown)
+            self.Bind(wx.EVT_LEFT_UP, self.OnClickToggleUp)
+
+    def __del__(self):
+        self.AppFrame = None
+
+    def ChangeValue(self, value):
+        wx.TextCtrl.ChangeValue(self, value)
+        self.RefreshListBoxChoices()
+
+    def OnEnteredText(self, event):
+        wx.CallAfter(self.RefreshListBoxChoices)
+        event.Skip()
+
+    def OnKeyDown(self, event):
+        """ Do some work when the user press on the keys:
+            up and down: move the cursor
+        """
+        keycode = event.GetKeyCode()
+        if keycode in [wx.WXK_DOWN, wx.WXK_UP]:
+            self.PopupListBox()
+            if keycode == wx.WXK_DOWN:
+                self.listbox.MoveSelection(1)
+            else:
+                self.listbox.MoveSelection(-1)
+        elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and self.listbox is not None:
+            self.SetValueFromSelected(self.listbox.GetSelection())
+        elif event.GetKeyCode() == wx.WXK_ESCAPE:
+            self.DismissListBox()
+        else:
+            event.Skip()
+
+    def OnClickToggleDown(self, event):
+        self._lastinsertionpoint = self.GetInsertionPoint()
+        event.Skip()
+
+    def OnClickToggleUp(self, event):
+        if self.GetInsertionPoint() == self._lastinsertionpoint:
+            wx.CallAfter(self.PopupListBox)
+        self._lastinsertionpoint = None
+        event.Skip()
+
+    def OnControlChanged(self, event):
+        res = self.GetValue()
+        config = wx.ConfigBase.Get()
+        listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([]))))
+        if res and res not in listentries:
+            listentries = (listentries + [res])[-MAX_ITEM_COUNT:]
+            config.Write(self.element_path, cPickle.dumps(listentries))
+            config.Flush()
+            self.SetChoices(listentries)
+        self.DismissListBox()
+        event.Skip()
+    
+    def SetChoices(self, choices):
+        self._choices = choices
+        self.RefreshListBoxChoices()
+        
+    def GetChoices(self):
+        return self._choices
+    
+    def SetValueFromSelected(self, selected):
+         """
+         Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
+         Will do nothing if no item is selected in the wx.ListCtrl.
+         """
+         if selected != "":
+            self.SetValue(selected)
+         self.DismissListBox()
+    
+    def RefreshListBoxChoices(self):
+        if self.listbox is not None:
+            text = self.GetValue()
+            choices = [choice for choice in self._choices if choice.startswith(text)]
+            self.listbox.SetChoices(choices)
+
+    def PopupListBox(self):
+        if self.listbox is None:
+            self.listbox = PopupWithListbox(self)
+            
+            # Show the popup right below or above the button
+            # depending on available screen space...
+            pos = self.ClientToScreen((0, 0))
+            sz = self.GetSize()
+            self.listbox.Position(pos, (0, sz[1]))
+            
+            self.RefreshListBoxChoices()
+            
+            if wx.Platform == '__WXMSW__':
+                self.listbox.Popup()
+            else:
+                self.listbox.Show()
+            self.AppFrame.EnableScrolling(False)
+
+    def DismissListBox(self):
+        if self.listbox is not None:
+            if wx.Platform == '__WXMSW__':
+                self.listbox.Dismiss()
+            else:
+                self.listbox.Destroy()
+            self.listbox = None
+        self.AppFrame.EnableScrolling(True)
+
--- a/beremiz_postinst.py	Tue Oct 27 16:32:54 2009 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# La première ligne doit commencer par #! et contenir python.
-# Elle sera adaptée au système de destination automatiquement
-
-""" This is a part of Beremiz project.
-
-    Post installation script for win32 system
-    
-    This script creat a shortcut for Beremiz.py in the desktop and the
-    start menu, and remove them at the uninstallation
-    
-"""
-
-import os
-import sys
-
-# Ce script sera aussi lancé lors de la désinstallation.
-# Pour n'exécuter du code que lors de l'installation :
-if sys.argv[1] == '-install':
-    # On récupère le dossier où mes fichiers seront installés (dossier où python est aussi installé sous windows)
-    python_path = sys.prefix
-    # On récupère le chemin de pythonw.exe (l'exécutable python qui n'affiche pas de console).
-    # Si vous voulez une console, remplacez pythonw.exe par python.exe
-    pyw_path = os.path.abspath(os.path.join(python_path, 'pythonw.exe'))
-    # On récupère le dossier coincoin
-    Beremiz_dir = os.path.abspath(os.path.join(python_path, 'LOLITech', 'Beremiz'))
-    
-    # On récupère les chemins de coincoin.py, et de coincoin.ico
-    # (Ben oui, l'icone est au format ico, oubliez le svg, ici on en est encore à la préhistoire.
-    # Heureusement que the GIMP sait faire la conversion !)
-    ico_path = os.path.join(Beremiz_dir, 'Beremiz.ico')
-    script_path = os.path.join(Beremiz_dir, 'Beremiz.py')
-    
-    # Création des raccourcis
-    # Pour chaque raccourci, on essaye de le faire pour tous les utilisateurs (Windows NT/2000/XP),
-    # sinon on le fait pour l'utilisateur courant (Windows 95/98/ME)
-    
-    # Raccourcis du bureau
-    # On essaye de trouver un bureau
-    try:
-        desktop_path = get_special_folder_path("CSIDL_COMMON_DESKTOPDIRECTORY")
-    except OSError:
-        desktop_path = get_special_folder_path("CSIDL_DESKTOPDIRECTORY")
-    
-    # On créé le raccourcis
-    create_shortcut(pyw_path, # programme à lancer
-                    "Can Node Editor", # Description
-                    os.path.join(desktop_path, 'Beremiz.lnk'),  # fichier du raccourcis (gardez le .lnk)
-                    script_path, # Argument (script python)
-                    Beremiz_dir, # Dossier courant
-                    ico_path # Fichier de l'icone
-                    )
-    # On va cafter au programme de désinstallation qu'on a fait un fichier, pour qu'il soit supprimé
-    # lors de la désinstallation
-    file_created(os.path.join(desktop_path, 'Beremiz.lnk'))
-    
-    # Raccourcis dans le menu démarrer (idem qu'avant)
-    try:
-        start_path = get_special_folder_path("CSIDL_COMMON_PROGRAMS")
-    except OSError:
-        start_path = get_special_folder_path("CSIDL_PROGRAMS")
-    
-    
-
-    # Création du dossier dans le menu programme
-    programs_path = os.path.join(start_path, "Beremiz project")
-    try :
-        os.mkdir(programs_path)
-
-    except OSError:
-
-        pass
-    directory_created(programs_path)
-    
-    create_shortcut(pyw_path, # Cible
-                    "Can Node Editor", #Description
-                    os.path.join(programs_path, 'Beremiz.lnk'),  # Fichier
-                    script_path, # Argument
-                    Beremiz_dir, # Dossier de travail
-                    ico_path # Icone
-                    )
-    file_created(os.path.join(programs_path, 'Beremiz.lnk'))
-    
-    # End (youpi-message)
-    # Ce message sera affiché (très) furtivement dans l'installateur.
-    # Vous pouvez vous en servir comme moyen de communication secret, c'est très in.
-    sys.stdout.write("Shortcuts created.")
-    # Fin du bidule
-    sys.exit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/Beremiz.directory	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,4 @@
+[Desktop Entry]
+Type=Directory
+Name=Beremiz
+Icon=/usr/share/beremiz/images/brz.png
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/Beremiz_demos.directory	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,4 @@
+[Desktop Entry]
+Type=Directory
+Name=Demos
+Icon=
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/Beremiz_docs.directory	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,4 @@
+[Desktop Entry]
+Type=Directory
+Name=Doc
+Icon=
--- a/debian/README.Debian	Tue Oct 27 16:32:54 2009 +0100
+++ b/debian/README.Debian	Mon Nov 02 15:38:49 2009 +0100
@@ -3,4 +3,4 @@
 
 <possible notes regarding this package - if none, delete this file>
 
- -- lolitech <gregory.trelat@lolitech.fr>  Thu, 29 Nov 2007 14:55:13 +0100
+ -- edouard <edouard.tisserant@gmail.com>  Thu, 29 Nov 2007 14:55:13 +0100
--- a/debian/beremiz.desktop	Tue Oct 27 16:32:54 2009 +0100
+++ b/debian/beremiz.desktop	Mon Nov 02 15:38:49 2009 +0100
@@ -8,6 +8,6 @@
 Terminal=false
 MultipleArgs=false
 Type=Application
-Categories=Application;Development;
+Categories=Beremiz;
 StartupNotify=true
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/beremiz.menu	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,40 @@
+<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN"
+ "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
+<Menu>
+  <Name>Applications</Name>
+  <Directory>Applications.directory</Directory>
+
+  <!-- Read standard .directory and .desktop file locations -->
+  <DefaultAppDirs/>
+  <DefaultDirectoryDirs/>
+	<Menu>
+	  <Name>Development</Name>
+	  <Directory>Development.directory</Directory>
+
+		  <!-- Beremiz submenu -->
+		  <Menu>
+		    <Name>Beremiz</Name>
+		    	<Directory>Beremiz.directory</Directory>
+				<Include>
+					<Category>Beremiz</Category>
+			    	</Include>
+				<Menu>
+				    <Name>Demos</Name>
+				    <Directory>Beremiz_demos.directory</Directory>
+				    <Include>
+					<Category>Beremiz_demo</Category>
+				    </Include>
+				</Menu> <!-- End Beremiz demo -->
+				<Menu>
+				    <Name>Doc</Name>
+				    <Directory>Beremiz_docs.directory</Directory>
+				    <Include>
+					<Category>Beremiz_doc</Category>
+				    </Include>
+				</Menu> <!-- End Beremiz doc -->
+		  </Menu> <!-- End Beremiz -->
+
+  	</Menu> <!-- End Development -->
+  <!-- possibly more submenus -->
+
+</Menu> <!-- End Applications -->
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/beremiz_doc.desktop	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Manual
+Comment=Open Source framework for automation
+Comment[fr]=Environnement de développement Open Source pour l'automatisme
+Exec=/usr/bin/xpdf /usr/share/beremiz/doc/manual_beremiz.pdf
+Icon=
+Terminal=false
+MultipleArgs=false
+Type=Application
+Categories=Beremiz_doc;
+StartupNotify=true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/beremiz_svgui.desktop	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Beremiz Demo (svgui)
+Comment=Open Source framework for automation
+Comment[fr]=Environnement de développement Open Source pour l'automatisme
+Exec=/usr/bin/beremiz /usr/share/beremiz/tests/svgui
+Icon=/usr/share/beremiz/images/brz.png
+Terminal=false
+MultipleArgs=false
+Type=Application
+Categories=Beremiz_demo;
+StartupNotify=true
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/beremiz_wxglade.desktop	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Beremiz Demo (wxGlade)
+Comment=Open Source framework for automation
+Comment[fr]=Environnement de développement Open Source pour l'automatisme
+Exec=/usr/bin/beremiz /usr/share/beremiz/tests/wxGlade
+Icon=/usr/share/beremiz/images/brz.png
+Terminal=false
+MultipleArgs=false
+Type=Application
+Categories=Beremiz_demo;
+StartupNotify=true
+
--- a/debian/changelog	Tue Oct 27 16:32:54 2009 +0100
+++ b/debian/changelog	Mon Nov 02 15:38:49 2009 +0100
@@ -2,5 +2,5 @@
 
   * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>
 
- -- lolitech <gregory.trelat@lolitech.fr>  Thu, 29 Nov 2007 14:55:13 +0100
+ -- edouard <edouard.tisserant@gmail.com>  Thu, 29 Nov 2007 14:55:13 +0100
 
--- a/debian/control	Tue Oct 27 16:32:54 2009 +0100
+++ b/debian/control	Mon Nov 02 15:38:49 2009 +0100
@@ -1,13 +1,13 @@
 Source: beremiz
 Section: devel
 Priority: standard
-Maintainer: lolitech <gregory.trelat@lolitech.fr>
+Maintainer: edouard <edouard.tisserant@gmail.com>
 Build-Depends: debhelper (>= 5)
 Standards-Version: 3.7.2
 
 Package: beremiz
 Architecture: any
-Depends: build-essential, python-wxgtk2.8, python-ctypes, python-wxglade, xpdf, canfestival-devel, canfestival-objdictedit, matiec, docutils, pyro, xmlclass, plcopeneditor
+Depends: build-essential, python-wxgtk2.8, python-ctypes, python-numpy, python-twisted, python-nevow, python-simplejson, python-wxglade, xpdf, canfestival-devel, canfestival-objdictedit, matiec, docutils, pyro, xmlclass, plcopeneditor
 Description: Beremiz is an Open Source framework for automation
  With Beremiz, you can :
     - Automate everything.
--- a/debian/rules	Tue Oct 27 16:32:54 2009 +0100
+++ b/debian/rules	Mon Nov 02 15:38:49 2009 +0100
@@ -1,17 +1,9 @@
 #!/usr/bin/make -f
 # -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
 
 # Uncomment this to turn on verbose mode.
 #export DH_VERBOSE=1
 
-
-
-
 CFLAGS = -Wall -g
 
 ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
@@ -23,8 +15,6 @@
 configure: configure-stamp
 configure-stamp:
 	dh_testdir
-	# Add here commands to configure the package.
-
 	touch configure-stamp
 
 
@@ -32,21 +22,12 @@
 
 build-stamp: configure-stamp 
 	dh_testdir
-
-	# Add here commands to compile the package.
-	#$(MAKE)
-	#docbook-to-man debian/beremiz.sgml > beremiz.1
-
 	touch $@
 
 clean:
 	dh_testdir
 	dh_testroot
 	rm -f build-stamp configure-stamp
-
-	# Add here commands to clean up after the build process.
-	#-$(MAKE) clean
-
 	dh_clean 
 
 install: build
@@ -55,35 +36,49 @@
 	dh_clean -k 
 	dh_installdirs
 
-	# Add here commands to install the package into debian/beremiz.
+	# Copy source files
 	mkdir -p $(CURDIR)/debian/beremiz/usr/share/beremiz
-	mkdir -p $(CURDIR)/debian/beremiz/usr/share/applications
-	
 	cp -a images $(CURDIR)/debian/beremiz/usr/share/beremiz
 	cp -a plugins $(CURDIR)/debian/beremiz/usr/share/beremiz
 	cp -a runtime $(CURDIR)/debian/beremiz/usr/share/beremiz
 	cp -a tests $(CURDIR)/debian/beremiz/usr/share/beremiz
 	cp -a connectors $(CURDIR)/debian/beremiz/usr/share/beremiz
 	cp -a targets $(CURDIR)/debian/beremiz/usr/share/beremiz
-	cp *.xml $(CURDIR)/debian/beremiz/usr/share/beremiz
+	cp -a doc $(CURDIR)/debian/beremiz/usr/share/beremiz
+	cp -a locale $(CURDIR)/debian/beremiz/usr/share/beremiz
+	cp *.js $(CURDIR)/debian/beremiz/usr/share/beremiz
 	cp *.py $(CURDIR)/debian/beremiz/usr/share/beremiz
 
-	cp debian/beremiz.desktop $(CURDIR)/debian/beremiz/usr/share/applications/beremiz.desktop
-
+	# Copy desktop directory file
+	mkdir -p $(CURDIR)/debian/beremiz/usr/share/desktop-directories
+	cp debian/Beremiz.directory $(CURDIR)/debian/beremiz/usr/share/desktop-directories
+	cp debian/Beremiz_demos.directory $(CURDIR)/debian/beremiz/usr/share/desktop-directories
+	cp debian/Beremiz_docs.directory $(CURDIR)/debian/beremiz/usr/share/desktop-directories
+	
+	# Copy desktop menu file
+	mkdir -p $(CURDIR)/debian/beremiz/etc/xdg/menus/applications-merged
+	cp debian/beremiz.menu $(CURDIR)/debian/beremiz/etc/xdg/menus/applications-merged
+	
+	# Copy desktop entry files
+	mkdir -p $(CURDIR)/debian/beremiz/usr/share/applications
+	cp debian/beremiz.desktop $(CURDIR)/debian/beremiz/usr/share/applications/
+	cp debian/beremiz_svgui.desktop $(CURDIR)/debian/beremiz/usr/share/applications/
+	cp debian/beremiz_wxglade.desktop $(CURDIR)/debian/beremiz/usr/share/applications/
+	cp debian/beremiz_doc.desktop $(CURDIR)/debian/beremiz/usr/share/applications/
+	
 	rm -rf `find .|grep CVS`
 	rm -rf `find .|grep .cvsignore`
 	chmod -R a+r $(CURDIR)/debian/beremiz
 
 # Build architecture-independent files here.
 binary-indep: build install
-# We have nothing to do by default.
 
 # Build architecture-dependent files here.
 binary-arch: build install
 	dh_testdir
 	dh_testroot
-	dh_installchangelogs 
-	dh_installdocs
+#	dh_installchangelogs 
+#	dh_installdocs
 	dh_installexamples
 #	dh_install
 #	dh_installmenu
--- a/doc/about.html	Tue Oct 27 16:32:54 2009 +0100
+++ b/doc/about.html	Mon Nov 02 15:38:49 2009 +0100
@@ -17,15 +17,6 @@
       <a href="http://www.fe.up.pt/si/web_page.inicial">http://www.fe.up.pt/si/web_page.inicial</a>
     </TD>
   </TR>
-  <TR>
-    <TD align="right" valign="top">
-      Supported by :
-    </TD>
-    <TD align="left" valign="top">
-      LOLITech<BR>
-      <a href="http://www.lolitech.fr">http://www.lolitech.fr</a>
-    </TD>
-  </TR>
 </TABLE>
 </CENTER>
 </BODY>
--- a/i18n/Beremiz_fr_FR.po	Tue Oct 27 16:32:54 2009 +0100
+++ b/i18n/Beremiz_fr_FR.po	Mon Nov 02 15:38:49 2009 +0100
@@ -7,25 +7,23 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-10-09 16:27+0200\n"
-"PO-Revision-Date: 2009-10-09 17:48+0100\n"
+"POT-Creation-Date: 2009-11-02 10:40+0100\n"
+"PO-Revision-Date: 2009-11-02 10:52+0100\n"
 "Last-Translator: \n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ../Beremiz.py:1432
+#: ../Beremiz.py:1457
 #, python-format
 msgid ""
 "\n"
 "An unhandled exception (bug) occured. Bug report saved at :\n"
 "(%s)\n"
 "\n"
-"Please contact LOLITech at:\n"
-"+33 (0)3 29 57 60 42\n"
-"or please be kind enough to send this file to:\n"
-"bugs_beremiz@lolitech.fr\n"
+"Please be kind enough to send this file to:\n"
+"edouard.tisserant@gmail.com\n"
 "\n"
 "You should now restart Beremiz.\n"
 "\n"
@@ -35,20 +33,18 @@
 "Une erreur inconnue (bug) est apparu. Le rapport d'erreur a été sauvé dans :\n"
 "(%s)\n"
 "\n"
-"Veuillez contacter LOLITech au :\n"
-"+33 (0)3 29 57 60 42\n"
-"ou envoyez ce fichier à l'adresse :\n"
-"bugs_beremiz@lolitech.fr\n"
+"Envoyez ce fichier à l'adresse :\n"
+"edouard.tisserant@gmail.com\n"
 "\n"
 "Vous devriez redémarrer Beremiz.\n"
 "\n"
 "Origine :\n"
 
-#: ../plugger.py:1370
+#: ../plugger.py:1453
 msgid " generation failed !\n"
 msgstr ": la construction a échouée !\n"
 
-#: ../Beremiz.py:1332
+#: ../Beremiz.py:1357
 #, python-format
 msgid "\"%s\" folder is not a valid Beremiz project\n"
 msgstr "Le dossier \"%s\" ne contient pas de projet Beremiz valide\n"
@@ -57,48 +53,48 @@
 msgid "&Edit"
 msgstr "&Editer"
 
-#: ../Beremiz.py:1420
-#: ../Beremiz.py:1422
-#: ../Beremiz.py:1423
+#: ../Beremiz.py:1445
+#: ../Beremiz.py:1447
+#: ../Beremiz.py:1448
 msgid ",   "
 msgstr ",   "
 
-#: ../Beremiz.py:1418
+#: ../Beremiz.py:1443
 msgid ". "
 msgstr ". "
 
-#: ../plugger.py:432
+#: ../plugger.py:443
 #, python-format
 msgid "A child names \"%s\" already exist -> \"%s\"\n"
 msgstr ""
 
-#: ../plugger.py:464
+#: ../plugger.py:475
 #, python-format
 msgid "A child with IEC channel %d already exist -> %d\n"
 msgstr ""
 
-#: ../Beremiz.py:329
+#: ../Beremiz.py:332
 msgid "About"
 msgstr "A propos"
 
-#: ../Beremiz.py:1369
+#: ../Beremiz.py:1392
 msgid "About Beremiz"
 msgstr "A propos de Beremiz"
 
-#: ../Beremiz.py:1391
+#: ../Beremiz.py:1415
 msgid "Add Plugin"
 msgstr "Ajouter un plugin"
 
-#: ../Beremiz.py:585
-#: ../Beremiz.py:849
+#: ../Beremiz.py:603
+#: ../Beremiz.py:867
 msgid "Add a sub plugin"
 msgstr "Ajouter un sous plugin"
 
-#: ../plugger.py:1683
+#: ../plugger.py:1766
 msgid "Already connected. Please disconnect\n"
 msgstr "Déjà connecté. Veuillez déconnecter\n"
 
-#: ../Beremiz.py:1088
+#: ../Beremiz.py:1106
 msgid "Append "
 msgstr "Ajouter "
 
@@ -108,62 +104,62 @@
 msgid "Bad location size : %s"
 msgstr "Mauvaise taille d'adresse : %s"
 
-#: ../Beremiz.py:417
+#: ../Beremiz.py:426
 msgid "Beremiz"
 msgstr "Beremiz"
 
-#: ../Beremiz.py:327
+#: ../Beremiz.py:330
 msgid "Beremiz\tF1"
 msgstr "Beremiz\tF1"
 
-#: ../plugger.py:1466
+#: ../plugger.py:1549
 msgid "Broken"
 msgstr "Cassé"
 
-#: ../plugger.py:1807
+#: ../plugger.py:1890
 msgid "Build"
 msgstr "Compiler"
 
-#: ../plugger.py:1434
+#: ../plugger.py:1517
 msgid "Build directory already clean\n"
 msgstr "Le répertoire de compilation est déjà nettoyé\n"
 
-#: ../plugger.py:1808
+#: ../plugger.py:1891
 msgid "Build project into build folder"
 msgstr "Compiler le projet dans le répertoire ce compilation"
 
-#: ../plugger.py:1388
+#: ../plugger.py:1471
 msgid "C Build crashed !\n"
 msgstr "La compilation du C a mal fonctionné !\n"
 
-#: ../plugger.py:1385
+#: ../plugger.py:1468
 msgid "C Build failed.\n"
 msgstr "La compilation du C a échouée !\n"
 
-#: ../plugger.py:1374
+#: ../plugger.py:1457
 msgid "C code generated successfully.\n"
 msgstr "Code C généré avec succès.\n"
 
-#: ../targets/toolchain_gcc.py:125
+#: ../targets/toolchain_gcc.py:129
 #, python-format
 msgid "C compilation of %s failed.\n"
 msgstr "La compilation C de %s a échouée.\n"
 
-#: ../plugger.py:1117
+#: ../plugger.py:1200
 #, python-format
 msgid "Can't find module for target %s!\n"
 msgstr "Impossible de trouver le module correspondant à la cible %s !\n"
 
-#: ../plugger.py:1756
+#: ../plugger.py:1839
 msgid "Cannot compare latest build to target. Please build.\n"
 msgstr "Impossible de comparer la cible avec la dernière compilation. Veuillez compiler le projet.\n"
 
-#: ../plugger.py:502
+#: ../plugger.py:513
 #, python-format
 msgid "Cannot create child %s of type %s "
 msgstr "Impossible d'ajouter un élément \"%s\" de type \"%s\""
 
-#: ../plugger.py:457
+#: ../plugger.py:468
 #, python-format
 msgid "Cannot find lower free IEC channel than %d\n"
 msgstr "Impossible de trouver un numéro IEC inférieur à %d libre\n"
@@ -172,7 +168,7 @@
 msgid "Cannot get PLC status - connection failed.\n"
 msgstr "Impossible d'obtenir le statut de l'automate - la connexion a échoué.\n"
 
-#: ../plugger.py:1215
+#: ../plugger.py:1298
 msgid "Cannot open/parse VARIABLES.csv!\n"
 msgstr "Impossible d'ouvrir ou d'analyser le fichier VARIABLES.csv !\n"
 
@@ -201,8 +197,12 @@
 msgid "Choose a SVG file"
 msgstr "Choisissez un fichier SVG"
 
-#: ../Beremiz.py:1289
-#: ../Beremiz.py:1314
+#: ../plugger.py:969
+msgid "Choose a directory to save project"
+msgstr "Choisissez un dossier où enregistrer le projet"
+
+#: ../Beremiz.py:1311
+#: ../Beremiz.py:1339
 msgid "Choose a project"
 msgstr "Choisissez un projet"
 
@@ -210,48 +210,48 @@
 msgid "Choose a working directory "
 msgstr "Choisissez un dossier de travail"
 
-#: ../plugger.py:882
+#: ../plugger.py:927
 msgid "Chosen folder doesn't contain a program. It's not a valid project!"
 msgstr "Le répertoire ne contient pas de programme. Ce n'est pas un projet valide !"
 
-#: ../plugger.py:847
+#: ../plugger.py:892
 msgid "Chosen folder isn't empty. You can't use it for a new project!"
 msgstr "Le répertoire n'est pas vide. Vous ne pouvez pas l'utiliser pour créer un nouveau projet !"
 
-#: ../plugger.py:1811
+#: ../plugger.py:1894
 msgid "Clean"
 msgstr "Nettoyer"
 
-#: ../plugger.py:1813
+#: ../plugger.py:1896
 msgid "Clean project build folder"
 msgstr "Nettoyer le répertoire de compilation"
 
-#: ../plugger.py:1431
+#: ../plugger.py:1514
 msgid "Cleaning the build directory\n"
 msgstr "Répertoire de compilation en cours de nettoyage\n"
 
-#: ../Beremiz.py:483
-#: ../Beremiz.py:1341
+#: ../Beremiz.py:504
 msgid "Close Application"
 msgstr "Fermer l'application"
 
-#: ../Beremiz.py:299
+#: ../Beremiz.py:301
+#: ../Beremiz.py:489
 msgid "Close Project"
 msgstr "Fermer le projet"
 
-#: ../Beremiz.py:297
+#: ../Beremiz.py:299
 msgid "Close Tab\tCTRL+W"
 msgstr "Fermer l'onglet\tCTRL+W"
 
-#: ../plugger.py:1039
+#: ../plugger.py:1122
 msgid "Compiling IEC Program into C code...\n"
 msgstr "Compilation du program en IEC vers du code C en cours...\n"
 
-#: ../plugger.py:1835
+#: ../plugger.py:1918
 msgid "Connect"
 msgstr "Connecter"
 
-#: ../plugger.py:1836
+#: ../plugger.py:1919
 msgid "Connect to the target PLC"
 msgstr "Connecter à l'automate cible"
 
@@ -260,23 +260,23 @@
 msgid "Connecting to URI : %s\n"
 msgstr "Connection à l'URI %s en cours...\n"
 
-#: ../plugger.py:1702
+#: ../plugger.py:1785
 msgid "Connection canceled!\n"
 msgstr "La connection a été abandonnée !\n"
 
-#: ../plugger.py:1719
+#: ../plugger.py:1802
 #, python-format
 msgid "Connection failed to %s!\n"
 msgstr "La connection à \"%s\" a échouée !\n"
 
-#: ../plugger.py:625
+#: ../plugger.py:634
 #, python-format
 msgid ""
 "Could not add child \"%s\", type %s :\n"
 "%s\n"
 msgstr ""
 
-#: ../plugger.py:602
+#: ../plugger.py:611
 #, python-format
 msgid ""
 "Couldn't load plugin base parameters %s :\n"
@@ -285,7 +285,7 @@
 "Impossible de charger les paramètres de base du plugin %s :\n"
 " %s"
 
-#: ../plugger.py:613
+#: ../plugger.py:622
 #, python-format
 msgid ""
 "Couldn't load plugin parameters %s :\n"
@@ -294,11 +294,11 @@
 "Impossible de charger les paramètres du plugin %s :\n"
 " %s"
 
-#: ../plugger.py:1647
+#: ../plugger.py:1730
 msgid "Couldn't start PLC debug !\n"
 msgstr "Impossible d'arrêter le débogage de l'automate !\n"
 
-#: ../plugger.py:1677
+#: ../plugger.py:1760
 msgid "Couldn't stop PLC !\n"
 msgstr "Impossible d'arrêter l'automate !\n"
 
@@ -306,54 +306,54 @@
 msgid "Create HMI"
 msgstr "Créer une IHM"
 
-#: ../plugger.py:1821
+#: ../plugger.py:1904
 msgid "Debug"
 msgstr "Déboguer"
 
-#: ../plugger.py:1520
+#: ../plugger.py:1603
 #, python-format
 msgid "Debug : Unknown variable %s\n"
 msgstr "Débogage : variable \"%s\" inconnue\n"
 
-#: ../plugger.py:1632
+#: ../plugger.py:1715
 msgid "Debug Thread couldn't be killed"
 msgstr "Le thread de débogage n'a pu être détruit"
 
-#: ../plugger.py:1616
+#: ../plugger.py:1699
 #, python-format
 msgid "Debug data not coherent %d != %d\n"
 msgstr "Les données de débogage ne sont pas cohérentes %d != %d\n"
 
-#: ../plugger.py:1624
+#: ../plugger.py:1707
 msgid "Debugger disabled\n"
 msgstr "Débogueur désactivé\n"
 
-#: ../Beremiz.py:840
+#: ../Beremiz.py:858
 msgid "Delete this plugin"
 msgstr "Supprimer ce plugin"
 
-#: ../plugger.py:1463
+#: ../plugger.py:1546
 msgid "Dirty"
 msgstr "Corrompu"
 
-#: ../plugger.py:1844
+#: ../plugger.py:1927
 msgid "Disconnect"
 msgstr "Déconnecter"
 
-#: ../plugger.py:1846
+#: ../plugger.py:1929
 msgid "Disconnect from PLC"
 msgstr "Déconnecter l'automate"
 
-#: ../plugger.py:1469
+#: ../plugger.py:1552
 msgid "Disconnected"
 msgstr "Déconnecté"
 
-#: ../plugins/c_ext/c_ext.py:236
-#: ../plugins/c_ext/c_ext.py:237
+#: ../plugins/c_ext/c_ext.py:250
+#: ../plugins/c_ext/c_ext.py:251
 msgid "Edit C File"
 msgstr "Editer le fichier C"
 
-#: ../plugins/canfestival/canfestival.py:217
+#: ../plugins/canfestival/canfestival.py:246
 msgid "Edit CanOpen Network with NetworkEdit"
 msgstr "Editer le réseau CANOpen à l'aide de NetworkEdit"
 
@@ -361,19 +361,19 @@
 msgid "Edit a WxWidgets GUI with WXGlade"
 msgstr "Editer une IHM WxWidgets à l'aide de WXGlade"
 
-#: ../plugins/canfestival/canfestival.py:216
+#: ../plugins/canfestival/canfestival.py:245
 msgid "Edit network"
 msgstr "Editer le réseau"
 
-#: ../plugger.py:1855
+#: ../plugger.py:1938
 msgid "Edit raw IEC code added to code generated by PLCGenerator"
 msgstr "Editer le code IEC ajouté au code généré par PLCGenerator"
 
-#: ../plugger.py:1460
+#: ../plugger.py:1543
 msgid "Empty"
 msgstr "Vide"
 
-#: ../Beremiz.py:790
+#: ../Beremiz.py:808
 msgid "Enable/Disable this plugin"
 msgstr "Activer/Désactiver le plugin"
 
@@ -389,23 +389,24 @@
 msgid "Enter the IP of the interface to bind"
 msgstr "Saisissez l'adresse IP de l'interface à lier"
 
-#: ../Beremiz.py:1446
-#: ../Beremiz.py:1456
+#: ../Beremiz.py:1469
+#: ../Beremiz.py:1479
+#: ../plugger.py:873
 #: ../Beremiz_service.py:268
 #: ../Beremiz_service.py:392
 msgid "Error"
 msgstr "Erreur"
 
-#: ../plugger.py:1087
+#: ../plugger.py:1170
 msgid "Error : At least one configuration and one resource must be declared in PLC !\n"
 msgstr "Erreur : Au moins une configuration ou une ressource doit être déclarée dans l'automate !\n"
 
-#: ../plugger.py:1079
+#: ../plugger.py:1162
 #, python-format
 msgid "Error : IEC to C compiler returned %d\n"
 msgstr "Erreur : Le compilateur d'IEC en C a retourné %d\n"
 
-#: ../plugger.py:1021
+#: ../plugger.py:1104
 #, python-format
 msgid ""
 "Error in ST/IL/SFC code generator :\n"
@@ -414,33 +415,33 @@
 "Erreur dans le générateur de code ST/IL/SFC :\n"
 "%s\n"
 
-#: ../plugger.py:207
+#: ../plugger.py:218
 #, python-format
 msgid "Error while saving \"%s\"\n"
 msgstr "Erreur lors de l'enregistrement de \"%s\"\n"
 
-#: ../plugins/canfestival/canfestival.py:208
+#: ../plugins/canfestival/canfestival.py:237
 msgid "Error: No Master generated\n"
 msgstr "Erreur : Aucun maître généré\n"
 
-#: ../plugins/canfestival/canfestival.py:203
+#: ../plugins/canfestival/canfestival.py:232
 msgid "Error: No PLC built\n"
 msgstr "Erreur : Aucun automate compilé\n"
 
-#: ../plugger.py:1713
+#: ../plugger.py:1796
 #, python-format
 msgid "Exception while connecting %s!\n"
 msgstr "Une exception est apparu au cours de la connexion %s !\n"
 
-#: ../plugger.py:1091
+#: ../plugger.py:1174
 msgid "Extracting Located Variables...\n"
 msgstr "Extraction des variables adressées en cours...\n"
 
-#: ../plugger.py:1771
+#: ../plugger.py:1854
 msgid "Failed : Must build before transfer.\n"
 msgstr "Echec : Le projet doit être compilé avant d'être transféré.\n"
 
-#: ../plugger.py:1379
+#: ../plugger.py:1462
 msgid "Fatal : cannot get builder.\n"
 msgstr "Erreur fatale : impossible de trouver un compilateur.\n"
 
@@ -448,15 +449,15 @@
 msgid "Force runtime reload\n"
 msgstr "Redémarrage du runtime forcé\n"
 
-#: ../plugger.py:1011
+#: ../plugger.py:1094
 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n"
 msgstr "Création du code ST/IL/SFC de l'automate IEC-61131 en cours...\n"
 
-#: ../plugger.py:1329
+#: ../plugger.py:1412
 msgid "Generating plugins C code\n"
 msgstr "Création du code C des plugins en cours\n"
 
-#: ../plugger.py:1321
+#: ../plugger.py:1404
 msgid "IEC-61131-3 code generation failed !\n"
 msgstr "La création du code IEC-61131-3 a échouée !\n"
 
@@ -480,15 +481,15 @@
 msgid "Invalid type \"%s\"-> %d != %d  for location\"%s\""
 msgstr "Type invalide \"%s\"-> %d != %d pour cette adresse \"%s\""
 
-#: ../plugger.py:1777
+#: ../plugger.py:1860
 msgid "Latest build already matches current target. Transfering anyway...\n"
 msgstr "La dernière compilation correspond à la cible actuelle...\n"
 
-#: ../plugger.py:1747
+#: ../plugger.py:1830
 msgid "Latest build does not match with target, please transfer.\n"
 msgstr "La dernière compilation ne correspond pas a la cible actuelle, veuillez transférer le programme.\n"
 
-#: ../plugger.py:1751
+#: ../plugger.py:1834
 msgid "Latest build matches target, no transfer needed.\n"
 msgstr "La dernière compilation correspond à la cible actuelle. il n'est pas nécessaire de transférer le programme.\n"
 
@@ -500,7 +501,7 @@
 msgid "Launch a live Python shell"
 msgstr "Lancer une console Python"
 
-#: ../targets/toolchain_gcc.py:133
+#: ../targets/toolchain_gcc.py:137
 msgid "Linking :\n"
 msgstr "Linkage :\n"
 
@@ -508,11 +509,11 @@
 msgid "Local"
 msgstr "Local"
 
-#: ../Beremiz.py:376
+#: ../Beremiz.py:380
 msgid "Log Console"
 msgstr "Console de log"
 
-#: ../plugger.py:512
+#: ../plugger.py:523
 #, python-format
 msgid "Max count (%d) reached for this plugin of type %s "
 msgstr "Nombre limite(%d) atteint pour les plugin de type %s"
@@ -525,7 +526,7 @@
 msgid "New\tCTRL+N"
 msgstr "Nouveau\tCTRL+N"
 
-#: ../plugger.py:1801
+#: ../plugger.py:1884
 msgid "No PLC to transfer (did build succeed ?)\n"
 msgstr "Aucun automate à transférer (la compilation a-t-elle réussi ?)\n"
 
@@ -562,29 +563,49 @@
 msgid "Open\tCTRL+O"
 msgstr "Ouvrir\tCTRL+O"
 
-#: ../targets/toolchain_gcc.py:101
+#: ../plugins/c_ext/c_ext.py:230
+msgid "Open CFileEditor"
+msgstr "Ouverture de CFileEditor"
+
+#: ../plugins/python/modules/svgui/svgui.py:105
+msgid "Open Inkscape"
+msgstr "Ouverture de Inkscape"
+
+#: ../plugins/canfestival/canfestival.py:208
+msgid "Open NetworkEdit"
+msgstr "Ouverture de NetworkEdit"
+
+#: ../plugins/canfestival/canfestival.py:108
+msgid "Open ObjDictEdit"
+msgstr "Ouverture de ObjdictEdit"
+
+#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:107
+msgid "Open wxGlade"
+msgstr "Ouverture de wxGlade"
+
+#: ../targets/toolchain_gcc.py:105
 msgid "PLC :\n"
 msgstr "Automate :\n"
 
-#: ../plugger.py:1489
-#: ../plugger.py:1733
+#: ../plugger.py:1572
+#: ../plugger.py:1816
 #, python-format
 msgid "PLC is %s\n"
 msgstr "L'automate est dans l'état %s\n"
 
-#: ../Beremiz.py:302
+#: ../Beremiz.py:304
 msgid "Page Setup"
 msgstr "Mise en page..."
 
-#: ../Beremiz.py:1391
+#: ../Beremiz.py:1415
 msgid "Please enter a name for plugin:"
 msgstr "Saisissez un nom pour le plugin :"
 
-#: ../targets/toolchain_gcc.py:99
+#: ../targets/toolchain_gcc.py:103
 msgid "Plugin : "
 msgstr "Plugin :"
 
-#: ../plugger.py:1335
+#: ../plugger.py:1418
 msgid "Plugins code generation failed !\n"
 msgstr "La création du code des plugins a échoué !\n"
 
@@ -596,29 +617,29 @@
 msgid "Port number must be an integer!"
 msgstr "Le numéro de port doit être un entier !"
 
-#: ../Beremiz.py:304
+#: ../Beremiz.py:306
 msgid "Preview"
 msgstr "Aperçu avant impression"
 
-#: ../Beremiz.py:306
+#: ../Beremiz.py:308
 msgid "Print"
 msgstr "Imprimer"
 
-#: ../plugger.py:856
+#: ../plugger.py:901
 msgid "Project not created"
 msgstr "Le projet n'a pu être créé"
 
-#: ../plugger.py:540
+#: ../plugger.py:549
 #, python-format
 msgid "Project tree layout do not match plugin.xml %s!=%s "
 msgstr "L'organisation du projet ne correspond pas à plugin.xml %s!=%s"
 
-#: ../Beremiz.py:309
+#: ../Beremiz.py:311
 msgid "Properties"
 msgstr "Propriétés"
 
 #: ../plugins/python/PythonEditor.py:513
-#: ../plugins/python/PythonEditor.py:566
+#: ../plugins/python/PythonEditor.py:565
 msgid "PythonEditor"
 msgstr "PythonEditor"
 
@@ -626,15 +647,15 @@
 msgid "Quit"
 msgstr "Quitter"
 
-#: ../Beremiz.py:312
+#: ../Beremiz.py:314
 msgid "Quit\tCTRL+Q"
 msgstr "Quitter\tCTRL+Q"
 
-#: ../plugger.py:1854
+#: ../plugger.py:1937
 msgid "Raw IEC code"
 msgstr "Ajout code IEC"
 
-#: ../Beremiz.py:1400
+#: ../Beremiz.py:1425
 msgid "Really delete plugin ?"
 msgstr "Voulez-vous réellement supprimer le plugin ?"
 
@@ -650,11 +671,11 @@
 msgid "Refresh\tCTRL+R"
 msgstr "Actualiser\tCTRL+R"
 
-#: ../Beremiz.py:1400
+#: ../Beremiz.py:1425
 msgid "Remove plugin"
 msgstr "Enlever le plugin"
 
-#: ../plugger.py:1816
+#: ../plugger.py:1899
 msgid "Run"
 msgstr "Exécuter"
 
@@ -666,58 +687,57 @@
 msgid "Save\tCTRL+S"
 msgstr "Enregistrer\tCTRL+S"
 
-#: ../Beremiz.py:482
-#: ../Beremiz.py:1340
-msgid "Save changes ?"
-msgstr "Enregistrer les changements ?"
+#: ../Beremiz.py:297
+msgid "Save as\tCTRL+SHIFT+S"
+msgstr "Enregistrer sous...\tCTRL+SHIFT+S"
 
 #: ../discovery.py:81
 msgid "Services available:"
 msgstr "Services disponibles:"
 
-#: ../plugger.py:1851
+#: ../plugger.py:1934
 msgid "Show IEC code generated by PLCGenerator"
 msgstr "Afficher le code IEC généré par PLCGenerator"
 
-#: ../plugins/canfestival/canfestival.py:220
+#: ../plugins/canfestival/canfestival.py:249
 msgid "Show Master"
 msgstr "Afficher le maître"
 
-#: ../plugins/canfestival/canfestival.py:221
+#: ../plugins/canfestival/canfestival.py:250
 msgid "Show Master generated by config_utils"
 msgstr "Afficher le maître généré par config_utils"
 
-#: ../plugger.py:1849
+#: ../plugger.py:1932
 msgid "Show code"
 msgstr "Afficher le code"
 
-#: ../plugger.py:1818
+#: ../plugger.py:1901
 #: ../Beremiz_service.py:317
 msgid "Start PLC"
 msgstr "Démarrer l'automate"
 
-#: ../plugger.py:1823
+#: ../plugger.py:1906
 msgid "Start PLC (debug mode)"
 msgstr "Démarrer l'automate (en mode debug)"
 
-#: ../plugger.py:1313
+#: ../plugger.py:1396
 #, python-format
 msgid "Start build in %s\n"
 msgstr "Début de la compilation dans %s\n"
 
-#: ../plugger.py:1454
+#: ../plugger.py:1537
 msgid "Started"
 msgstr "Démarré"
 
-#: ../plugger.py:1451
+#: ../plugger.py:1534
 msgid "Starting"
 msgstr "Démarrage"
 
-#: ../plugger.py:1641
+#: ../plugger.py:1724
 msgid "Starting PLC (debug mode)\n"
 msgstr "Démarrage de l'automate (en mode debug) en cours\n"
 
-#: ../plugger.py:1830
+#: ../plugger.py:1913
 msgid "Stop"
 msgstr "Arrêter"
 
@@ -725,35 +745,39 @@
 msgid "Stop PLC"
 msgstr "Arrêter l'automate"
 
-#: ../plugger.py:1832
+#: ../plugger.py:1915
 msgid "Stop Running PLC"
 msgstr "Arrêter l'automate en cours d'exécution"
 
-#: ../plugger.py:1457
+#: ../plugger.py:1540
 msgid "Stopped"
 msgstr "Arrêté"
 
-#: ../plugger.py:1673
+#: ../plugger.py:1756
 msgid "Stopping debug\n"
 msgstr "Arrêt du débogage en cours\n"
 
-#: ../Beremiz.py:370
+#: ../Beremiz.py:492
+msgid "There are changes, do you want to save?"
+msgstr "Le projet a été modifié, voulez-vous l'enregistrer ?"
+
+#: ../Beremiz.py:374
 msgid "Topology"
 msgstr "Topologie"
 
-#: ../plugger.py:1839
+#: ../plugger.py:1922
 msgid "Transfer"
 msgstr "Transférer"
 
-#: ../plugger.py:1841
+#: ../plugger.py:1924
 msgid "Transfer PLC"
 msgstr "Transférer l'automate"
 
-#: ../plugger.py:1797
+#: ../plugger.py:1880
 msgid "Transfer completed successfully.\n"
 msgstr "Transfert effectué avec succès.\n"
 
-#: ../plugger.py:1799
+#: ../plugger.py:1882
 msgid "Transfer failed\n"
 msgstr "Le transfert a échoué\n"
 
@@ -785,7 +809,7 @@
 msgid "WXGLADE GUI"
 msgstr "IHM WXGlade"
 
-#: ../plugger.py:1016
+#: ../plugger.py:1099
 msgid "Warnings in ST/IL/SFC code generator :\n"
 msgstr "Mises en garde du generateur de code ST/IL/SFC :\n"
 
@@ -793,21 +817,69 @@
 msgid "Wrong URI, please check it !\n"
 msgstr "URI inconnue, veuillez vérifier l'adresse !\n"
 
-#: ../wxPopen.py:134
+#: ../plugins/c_ext/c_ext.py:229
+msgid ""
+"You don't have write permissions.\n"
+"Open CFileEditor anyway ?"
+msgstr ""
+"Vous n'avez pas les permissions d'écriture.\n"
+"Ouvrir CFileEditor tout de même ?"
+
+#: ../plugins/python/modules/svgui/svgui.py:104
+msgid ""
+"You don't have write permissions.\n"
+"Open Inkscape anyway ?"
+msgstr ""
+"Vous n'avez pas les permissions d'écriture.\n"
+"Ouvrir Inkscape tout de même ?"
+
+#: ../plugins/canfestival/canfestival.py:207
+msgid ""
+"You don't have write permissions.\n"
+"Open NetworkEdit anyway ?"
+msgstr ""
+"Vous n'avez pas les permissions d'écriture.\n"
+"Ouvrir NetworkEdit tout de même ?"
+
+#: ../plugins/canfestival/canfestival.py:107
+msgid ""
+"You don't have write permissions.\n"
+"Open ObjDictEdit anyway ?"
+msgstr ""
+"Vous n'avez pas les permissions d'écriture.\n"
+"Ouvrir ObjdictEdit tout de même ?"
+
+#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:106
+msgid ""
+"You don't have write permissions.\n"
+"Open wxGlade anyway ?"
+msgstr ""
+"Vous n'avez pas les permissions d'écriture.\n"
+"Ouvrir wxGlade tout de même ?"
+
+#: ../plugger.py:872
+msgid ""
+"You must have permission to work on the project\n"
+"Work on a project copy ?"
+msgstr ""
+"Vous n'avez pas la permission de travailler sur le projet.\n"
+"Travailler sur une copie du projet ?"
+
+#: ../wxPopen.py:145
 #, python-format
 msgid "exited with status %s (pid %s)\n"
 msgstr "a quitté avec le status %s (pid %s)\n"
 
-#: ../Beremiz.py:1420
-#: ../Beremiz.py:1422
+#: ../Beremiz.py:1445
+#: ../Beremiz.py:1447
 msgid "file : "
 msgstr "fichier :"
 
-#: ../Beremiz.py:1423
+#: ../Beremiz.py:1448
 msgid "function : "
 msgstr "fonction :"
 
-#: ../Beremiz.py:1423
+#: ../Beremiz.py:1448
 msgid "line : "
 msgstr "ligne :"
 
@@ -902,6 +974,9 @@
 msgid "XenoConfig"
 msgstr "Config Xenomai"
 
+#~ msgid "Save changes ?"
+#~ msgstr "Enregistrer les changements ?"
+
 #, fuzzy
 #~ msgid "Conflict type for location \"%s\""
 #~ msgstr "Conflit entre types pour l'adresse \"%s\""
--- a/i18n/app.fil	Tue Oct 27 16:32:54 2009 +0100
+++ b/i18n/app.fil	Mon Nov 02 15:38:49 2009 +0100
@@ -1,6 +1,5 @@
 ../Beremiz.py
 ../plugger.py
-../beremiz_postinst.py
 ../Beremiz_service.py
 ../discovery.py
 ../wxPopen.py
--- a/i18n/messages.pot	Tue Oct 27 16:32:54 2009 +0100
+++ b/i18n/messages.pot	Mon Nov 02 15:38:49 2009 +0100
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-10-09 16:27+0200\n"
+"POT-Creation-Date: 2009-11-02 10:40+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,28 +16,26 @@
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ../Beremiz.py:1432
+#: ../Beremiz.py:1457
 #, python-format
 msgid ""
 "\n"
 "An unhandled exception (bug) occured. Bug report saved at :\n"
 "(%s)\n"
 "\n"
-"Please contact LOLITech at:\n"
-"+33 (0)3 29 57 60 42\n"
-"or please be kind enough to send this file to:\n"
-"bugs_beremiz@lolitech.fr\n"
+"Please be kind enough to send this file to:\n"
+"edouard.tisserant@gmail.com\n"
 "\n"
 "You should now restart Beremiz.\n"
 "\n"
 "Traceback:\n"
 msgstr ""
 
-#: ../plugger.py:1370
+#: ../plugger.py:1453
 msgid " generation failed !\n"
 msgstr ""
 
-#: ../Beremiz.py:1332
+#: ../Beremiz.py:1357
 #, python-format
 msgid "\"%s\" folder is not a valid Beremiz project\n"
 msgstr ""
@@ -46,45 +44,45 @@
 msgid "&Edit"
 msgstr ""
 
-#: ../Beremiz.py:1420 ../Beremiz.py:1422 ../Beremiz.py:1423
+#: ../Beremiz.py:1445 ../Beremiz.py:1447 ../Beremiz.py:1448
 msgid ",   "
 msgstr ""
 
-#: ../Beremiz.py:1418
+#: ../Beremiz.py:1443
 msgid ". "
 msgstr ""
 
-#: ../plugger.py:432
+#: ../plugger.py:443
 #, python-format
 msgid "A child names \"%s\" already exist -> \"%s\"\n"
 msgstr ""
 
-#: ../plugger.py:464
+#: ../plugger.py:475
 #, python-format
 msgid "A child with IEC channel %d already exist -> %d\n"
 msgstr ""
 
-#: ../Beremiz.py:329
+#: ../Beremiz.py:332
 msgid "About"
 msgstr ""
 
-#: ../Beremiz.py:1369
+#: ../Beremiz.py:1392
 msgid "About Beremiz"
 msgstr ""
 
-#: ../Beremiz.py:1391
+#: ../Beremiz.py:1415
 msgid "Add Plugin"
 msgstr ""
 
-#: ../Beremiz.py:585 ../Beremiz.py:849
+#: ../Beremiz.py:603 ../Beremiz.py:867
 msgid "Add a sub plugin"
 msgstr ""
 
-#: ../plugger.py:1683
+#: ../plugger.py:1766
 msgid "Already connected. Please disconnect\n"
 msgstr ""
 
-#: ../Beremiz.py:1088
+#: ../Beremiz.py:1106
 msgid "Append "
 msgstr ""
 
@@ -94,62 +92,62 @@
 msgid "Bad location size : %s"
 msgstr ""
 
-#: ../Beremiz.py:417
+#: ../Beremiz.py:426
 msgid "Beremiz"
 msgstr ""
 
-#: ../Beremiz.py:327
+#: ../Beremiz.py:330
 msgid "Beremiz\tF1"
 msgstr ""
 
-#: ../plugger.py:1466
+#: ../plugger.py:1549
 msgid "Broken"
 msgstr ""
 
-#: ../plugger.py:1807
+#: ../plugger.py:1890
 msgid "Build"
 msgstr ""
 
-#: ../plugger.py:1434
+#: ../plugger.py:1517
 msgid "Build directory already clean\n"
 msgstr ""
 
-#: ../plugger.py:1808
+#: ../plugger.py:1891
 msgid "Build project into build folder"
 msgstr ""
 
-#: ../plugger.py:1388
+#: ../plugger.py:1471
 msgid "C Build crashed !\n"
 msgstr ""
 
-#: ../plugger.py:1385
+#: ../plugger.py:1468
 msgid "C Build failed.\n"
 msgstr ""
 
-#: ../plugger.py:1374
+#: ../plugger.py:1457
 msgid "C code generated successfully.\n"
 msgstr ""
 
-#: ../targets/toolchain_gcc.py:125
+#: ../targets/toolchain_gcc.py:129
 #, python-format
 msgid "C compilation of %s failed.\n"
 msgstr ""
 
-#: ../plugger.py:1117
+#: ../plugger.py:1200
 #, python-format
 msgid "Can't find module for target %s!\n"
 msgstr ""
 
-#: ../plugger.py:1756
+#: ../plugger.py:1839
 msgid "Cannot compare latest build to target. Please build.\n"
 msgstr ""
 
-#: ../plugger.py:502
+#: ../plugger.py:513
 #, python-format
 msgid "Cannot create child %s of type %s "
 msgstr ""
 
-#: ../plugger.py:457
+#: ../plugger.py:468
 #, python-format
 msgid "Cannot find lower free IEC channel than %d\n"
 msgstr ""
@@ -158,7 +156,7 @@
 msgid "Cannot get PLC status - connection failed.\n"
 msgstr ""
 
-#: ../plugger.py:1215
+#: ../plugger.py:1298
 msgid "Cannot open/parse VARIABLES.csv!\n"
 msgstr ""
 
@@ -187,7 +185,11 @@
 msgid "Choose a SVG file"
 msgstr ""
 
-#: ../Beremiz.py:1289 ../Beremiz.py:1314
+#: ../plugger.py:969
+msgid "Choose a directory to save project"
+msgstr ""
+
+#: ../Beremiz.py:1311 ../Beremiz.py:1339
 msgid "Choose a project"
 msgstr ""
 
@@ -195,47 +197,47 @@
 msgid "Choose a working directory "
 msgstr ""
 
-#: ../plugger.py:882
+#: ../plugger.py:927
 msgid "Chosen folder doesn't contain a program. It's not a valid project!"
 msgstr ""
 
-#: ../plugger.py:847
+#: ../plugger.py:892
 msgid "Chosen folder isn't empty. You can't use it for a new project!"
 msgstr ""
 
-#: ../plugger.py:1811
+#: ../plugger.py:1894
 msgid "Clean"
 msgstr ""
 
-#: ../plugger.py:1813
+#: ../plugger.py:1896
 msgid "Clean project build folder"
 msgstr ""
 
-#: ../plugger.py:1431
+#: ../plugger.py:1514
 msgid "Cleaning the build directory\n"
 msgstr ""
 
-#: ../Beremiz.py:483 ../Beremiz.py:1341
+#: ../Beremiz.py:504
 msgid "Close Application"
 msgstr ""
 
+#: ../Beremiz.py:301 ../Beremiz.py:489
+msgid "Close Project"
+msgstr ""
+
 #: ../Beremiz.py:299
-msgid "Close Project"
-msgstr ""
-
-#: ../Beremiz.py:297
 msgid "Close Tab\tCTRL+W"
 msgstr ""
 
-#: ../plugger.py:1039
+#: ../plugger.py:1122
 msgid "Compiling IEC Program into C code...\n"
 msgstr ""
 
-#: ../plugger.py:1835
+#: ../plugger.py:1918
 msgid "Connect"
 msgstr ""
 
-#: ../plugger.py:1836
+#: ../plugger.py:1919
 msgid "Connect to the target PLC"
 msgstr ""
 
@@ -244,41 +246,41 @@
 msgid "Connecting to URI : %s\n"
 msgstr ""
 
-#: ../plugger.py:1702
+#: ../plugger.py:1785
 msgid "Connection canceled!\n"
 msgstr ""
 
-#: ../plugger.py:1719
+#: ../plugger.py:1802
 #, python-format
 msgid "Connection failed to %s!\n"
 msgstr ""
 
-#: ../plugger.py:625
+#: ../plugger.py:634
 #, python-format
 msgid ""
 "Could not add child \"%s\", type %s :\n"
 "%s\n"
 msgstr ""
 
-#: ../plugger.py:602
+#: ../plugger.py:611
 #, python-format
 msgid ""
 "Couldn't load plugin base parameters %s :\n"
 " %s"
 msgstr ""
 
-#: ../plugger.py:613
+#: ../plugger.py:622
 #, python-format
 msgid ""
 "Couldn't load plugin parameters %s :\n"
 " %s"
 msgstr ""
 
-#: ../plugger.py:1647
+#: ../plugger.py:1730
 msgid "Couldn't start PLC debug !\n"
 msgstr ""
 
-#: ../plugger.py:1677
+#: ../plugger.py:1760
 msgid "Couldn't stop PLC !\n"
 msgstr ""
 
@@ -286,53 +288,53 @@
 msgid "Create HMI"
 msgstr ""
 
-#: ../plugger.py:1821
+#: ../plugger.py:1904
 msgid "Debug"
 msgstr ""
 
-#: ../plugger.py:1520
+#: ../plugger.py:1603
 #, python-format
 msgid "Debug : Unknown variable %s\n"
 msgstr ""
 
-#: ../plugger.py:1632
+#: ../plugger.py:1715
 msgid "Debug Thread couldn't be killed"
 msgstr ""
 
-#: ../plugger.py:1616
+#: ../plugger.py:1699
 #, python-format
 msgid "Debug data not coherent %d != %d\n"
 msgstr ""
 
-#: ../plugger.py:1624
+#: ../plugger.py:1707
 msgid "Debugger disabled\n"
 msgstr ""
 
-#: ../Beremiz.py:840
+#: ../Beremiz.py:858
 msgid "Delete this plugin"
 msgstr ""
 
-#: ../plugger.py:1463
+#: ../plugger.py:1546
 msgid "Dirty"
 msgstr ""
 
-#: ../plugger.py:1844
+#: ../plugger.py:1927
 msgid "Disconnect"
 msgstr ""
 
-#: ../plugger.py:1846
+#: ../plugger.py:1929
 msgid "Disconnect from PLC"
 msgstr ""
 
-#: ../plugger.py:1469
+#: ../plugger.py:1552
 msgid "Disconnected"
 msgstr ""
 
-#: ../plugins/c_ext/c_ext.py:236 ../plugins/c_ext/c_ext.py:237
+#: ../plugins/c_ext/c_ext.py:250 ../plugins/c_ext/c_ext.py:251
 msgid "Edit C File"
 msgstr ""
 
-#: ../plugins/canfestival/canfestival.py:217
+#: ../plugins/canfestival/canfestival.py:246
 msgid "Edit CanOpen Network with NetworkEdit"
 msgstr ""
 
@@ -340,19 +342,19 @@
 msgid "Edit a WxWidgets GUI with WXGlade"
 msgstr ""
 
-#: ../plugins/canfestival/canfestival.py:216
+#: ../plugins/canfestival/canfestival.py:245
 msgid "Edit network"
 msgstr ""
 
-#: ../plugger.py:1855
+#: ../plugger.py:1938
 msgid "Edit raw IEC code added to code generated by PLCGenerator"
 msgstr ""
 
-#: ../plugger.py:1460
+#: ../plugger.py:1543
 msgid "Empty"
 msgstr ""
 
-#: ../Beremiz.py:790
+#: ../Beremiz.py:808
 msgid "Enable/Disable this plugin"
 msgstr ""
 
@@ -368,54 +370,54 @@
 msgid "Enter the IP of the interface to bind"
 msgstr ""
 
-#: ../Beremiz.py:1446 ../Beremiz.py:1456 ../Beremiz_service.py:268
-#: ../Beremiz_service.py:392
+#: ../Beremiz.py:1469 ../Beremiz.py:1479 ../plugger.py:873
+#: ../Beremiz_service.py:268 ../Beremiz_service.py:392
 msgid "Error"
 msgstr ""
 
-#: ../plugger.py:1087
+#: ../plugger.py:1170
 msgid "Error : At least one configuration and one resource must be declared in PLC !\n"
 msgstr ""
 
-#: ../plugger.py:1079
+#: ../plugger.py:1162
 #, python-format
 msgid "Error : IEC to C compiler returned %d\n"
 msgstr ""
 
-#: ../plugger.py:1021
+#: ../plugger.py:1104
 #, python-format
 msgid ""
 "Error in ST/IL/SFC code generator :\n"
 "%s\n"
 msgstr ""
 
-#: ../plugger.py:207
+#: ../plugger.py:218
 #, python-format
 msgid "Error while saving \"%s\"\n"
 msgstr ""
 
-#: ../plugins/canfestival/canfestival.py:208
+#: ../plugins/canfestival/canfestival.py:237
 msgid "Error: No Master generated\n"
 msgstr ""
 
-#: ../plugins/canfestival/canfestival.py:203
+#: ../plugins/canfestival/canfestival.py:232
 msgid "Error: No PLC built\n"
 msgstr ""
 
-#: ../plugger.py:1713
+#: ../plugger.py:1796
 #, python-format
 msgid "Exception while connecting %s!\n"
 msgstr ""
 
-#: ../plugger.py:1091
+#: ../plugger.py:1174
 msgid "Extracting Located Variables...\n"
 msgstr ""
 
-#: ../plugger.py:1771
+#: ../plugger.py:1854
 msgid "Failed : Must build before transfer.\n"
 msgstr ""
 
-#: ../plugger.py:1379
+#: ../plugger.py:1462
 msgid "Fatal : cannot get builder.\n"
 msgstr ""
 
@@ -423,15 +425,15 @@
 msgid "Force runtime reload\n"
 msgstr ""
 
-#: ../plugger.py:1011
+#: ../plugger.py:1094
 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n"
 msgstr ""
 
-#: ../plugger.py:1329
+#: ../plugger.py:1412
 msgid "Generating plugins C code\n"
 msgstr ""
 
-#: ../plugger.py:1321
+#: ../plugger.py:1404
 msgid "IEC-61131-3 code generation failed !\n"
 msgstr ""
 
@@ -454,15 +456,15 @@
 msgid "Invalid type \"%s\"-> %d != %d  for location\"%s\""
 msgstr ""
 
-#: ../plugger.py:1777
+#: ../plugger.py:1860
 msgid "Latest build already matches current target. Transfering anyway...\n"
 msgstr ""
 
-#: ../plugger.py:1747
+#: ../plugger.py:1830
 msgid "Latest build does not match with target, please transfer.\n"
 msgstr ""
 
-#: ../plugger.py:1751
+#: ../plugger.py:1834
 msgid "Latest build matches target, no transfer needed.\n"
 msgstr ""
 
@@ -474,7 +476,7 @@
 msgid "Launch a live Python shell"
 msgstr ""
 
-#: ../targets/toolchain_gcc.py:133
+#: ../targets/toolchain_gcc.py:137
 msgid "Linking :\n"
 msgstr ""
 
@@ -482,11 +484,11 @@
 msgid "Local"
 msgstr ""
 
-#: ../Beremiz.py:376
+#: ../Beremiz.py:380
 msgid "Log Console"
 msgstr ""
 
-#: ../plugger.py:512
+#: ../plugger.py:523
 #, python-format
 msgid "Max count (%d) reached for this plugin of type %s "
 msgstr ""
@@ -499,7 +501,7 @@
 msgid "New\tCTRL+N"
 msgstr ""
 
-#: ../plugger.py:1801
+#: ../plugger.py:1884
 msgid "No PLC to transfer (did build succeed ?)\n"
 msgstr ""
 
@@ -536,28 +538,48 @@
 msgid "Open\tCTRL+O"
 msgstr ""
 
-#: ../targets/toolchain_gcc.py:101
+#: ../plugins/c_ext/c_ext.py:230
+msgid "Open CFileEditor"
+msgstr ""
+
+#: ../plugins/python/modules/svgui/svgui.py:105
+msgid "Open Inkscape"
+msgstr ""
+
+#: ../plugins/canfestival/canfestival.py:208
+msgid "Open NetworkEdit"
+msgstr ""
+
+#: ../plugins/canfestival/canfestival.py:108
+msgid "Open ObjDictEdit"
+msgstr ""
+
+#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:107
+msgid "Open wxGlade"
+msgstr ""
+
+#: ../targets/toolchain_gcc.py:105
 msgid "PLC :\n"
 msgstr ""
 
-#: ../plugger.py:1489 ../plugger.py:1733
+#: ../plugger.py:1572 ../plugger.py:1816
 #, python-format
 msgid "PLC is %s\n"
 msgstr ""
 
-#: ../Beremiz.py:302
+#: ../Beremiz.py:304
 msgid "Page Setup"
 msgstr ""
 
-#: ../Beremiz.py:1391
+#: ../Beremiz.py:1415
 msgid "Please enter a name for plugin:"
 msgstr ""
 
-#: ../targets/toolchain_gcc.py:99
+#: ../targets/toolchain_gcc.py:103
 msgid "Plugin : "
 msgstr ""
 
-#: ../plugger.py:1335
+#: ../plugger.py:1418
 msgid "Plugins code generation failed !\n"
 msgstr ""
 
@@ -569,28 +591,28 @@
 msgid "Port number must be an integer!"
 msgstr ""
 
-#: ../Beremiz.py:304
+#: ../Beremiz.py:306
 msgid "Preview"
 msgstr ""
 
-#: ../Beremiz.py:306
+#: ../Beremiz.py:308
 msgid "Print"
 msgstr ""
 
-#: ../plugger.py:856
+#: ../plugger.py:901
 msgid "Project not created"
 msgstr ""
 
-#: ../plugger.py:540
+#: ../plugger.py:549
 #, python-format
 msgid "Project tree layout do not match plugin.xml %s!=%s "
 msgstr ""
 
-#: ../Beremiz.py:309
+#: ../Beremiz.py:311
 msgid "Properties"
 msgstr ""
 
-#: ../plugins/python/PythonEditor.py:513 ../plugins/python/PythonEditor.py:566
+#: ../plugins/python/PythonEditor.py:513 ../plugins/python/PythonEditor.py:565
 msgid "PythonEditor"
 msgstr ""
 
@@ -598,15 +620,15 @@
 msgid "Quit"
 msgstr ""
 
-#: ../Beremiz.py:312
+#: ../Beremiz.py:314
 msgid "Quit\tCTRL+Q"
 msgstr ""
 
-#: ../plugger.py:1854
+#: ../plugger.py:1937
 msgid "Raw IEC code"
 msgstr ""
 
-#: ../Beremiz.py:1400
+#: ../Beremiz.py:1425
 msgid "Really delete plugin ?"
 msgstr ""
 
@@ -622,11 +644,11 @@
 msgid "Refresh\tCTRL+R"
 msgstr ""
 
-#: ../Beremiz.py:1400
+#: ../Beremiz.py:1425
 msgid "Remove plugin"
 msgstr ""
 
-#: ../plugger.py:1816
+#: ../plugger.py:1899
 msgid "Run"
 msgstr ""
 
@@ -638,56 +660,56 @@
 msgid "Save\tCTRL+S"
 msgstr ""
 
-#: ../Beremiz.py:482 ../Beremiz.py:1340
-msgid "Save changes ?"
+#: ../Beremiz.py:297
+msgid "Save as\tCTRL+SHIFT+S"
 msgstr ""
 
 #: ../discovery.py:81
 msgid "Services available:"
 msgstr ""
 
-#: ../plugger.py:1851
+#: ../plugger.py:1934
 msgid "Show IEC code generated by PLCGenerator"
 msgstr ""
 
-#: ../plugins/canfestival/canfestival.py:220
+#: ../plugins/canfestival/canfestival.py:249
 msgid "Show Master"
 msgstr ""
 
-#: ../plugins/canfestival/canfestival.py:221
+#: ../plugins/canfestival/canfestival.py:250
 msgid "Show Master generated by config_utils"
 msgstr ""
 
-#: ../plugger.py:1849
+#: ../plugger.py:1932
 msgid "Show code"
 msgstr ""
 
-#: ../plugger.py:1818 ../Beremiz_service.py:317
+#: ../plugger.py:1901 ../Beremiz_service.py:317
 msgid "Start PLC"
 msgstr ""
 
-#: ../plugger.py:1823
+#: ../plugger.py:1906
 msgid "Start PLC (debug mode)"
 msgstr ""
 
-#: ../plugger.py:1313
+#: ../plugger.py:1396
 #, python-format
 msgid "Start build in %s\n"
 msgstr ""
 
-#: ../plugger.py:1454
+#: ../plugger.py:1537
 msgid "Started"
 msgstr ""
 
-#: ../plugger.py:1451
+#: ../plugger.py:1534
 msgid "Starting"
 msgstr ""
 
-#: ../plugger.py:1641
+#: ../plugger.py:1724
 msgid "Starting PLC (debug mode)\n"
 msgstr ""
 
-#: ../plugger.py:1830
+#: ../plugger.py:1913
 msgid "Stop"
 msgstr ""
 
@@ -695,35 +717,39 @@
 msgid "Stop PLC"
 msgstr ""
 
-#: ../plugger.py:1832
+#: ../plugger.py:1915
 msgid "Stop Running PLC"
 msgstr ""
 
-#: ../plugger.py:1457
+#: ../plugger.py:1540
 msgid "Stopped"
 msgstr ""
 
-#: ../plugger.py:1673
+#: ../plugger.py:1756
 msgid "Stopping debug\n"
 msgstr ""
 
-#: ../Beremiz.py:370
+#: ../Beremiz.py:492
+msgid "There are changes, do you want to save?"
+msgstr ""
+
+#: ../Beremiz.py:374
 msgid "Topology"
 msgstr ""
 
-#: ../plugger.py:1839
+#: ../plugger.py:1922
 msgid "Transfer"
 msgstr ""
 
-#: ../plugger.py:1841
+#: ../plugger.py:1924
 msgid "Transfer PLC"
 msgstr ""
 
-#: ../plugger.py:1797
+#: ../plugger.py:1880
 msgid "Transfer completed successfully.\n"
 msgstr ""
 
-#: ../plugger.py:1799
+#: ../plugger.py:1882
 msgid "Transfer failed\n"
 msgstr ""
 
@@ -755,7 +781,7 @@
 msgid "WXGLADE GUI"
 msgstr ""
 
-#: ../plugger.py:1016
+#: ../plugger.py:1099
 msgid "Warnings in ST/IL/SFC code generator :\n"
 msgstr ""
 
@@ -763,20 +789,56 @@
 msgid "Wrong URI, please check it !\n"
 msgstr ""
 
-#: ../wxPopen.py:134
+#: ../plugins/c_ext/c_ext.py:229
+msgid ""
+"You don't have write permissions.\n"
+"Open CFileEditor anyway ?"
+msgstr ""
+
+#: ../plugins/python/modules/svgui/svgui.py:104
+msgid ""
+"You don't have write permissions.\n"
+"Open Inkscape anyway ?"
+msgstr ""
+
+#: ../plugins/canfestival/canfestival.py:207
+msgid ""
+"You don't have write permissions.\n"
+"Open NetworkEdit anyway ?"
+msgstr ""
+
+#: ../plugins/canfestival/canfestival.py:107
+msgid ""
+"You don't have write permissions.\n"
+"Open ObjDictEdit anyway ?"
+msgstr ""
+
+#: ../plugins/python/modules/wxglade_hmi/wxglade_hmi.py:106
+msgid ""
+"You don't have write permissions.\n"
+"Open wxGlade anyway ?"
+msgstr ""
+
+#: ../plugger.py:872
+msgid ""
+"You must have permission to work on the project\n"
+"Work on a project copy ?"
+msgstr ""
+
+#: ../wxPopen.py:145
 #, python-format
 msgid "exited with status %s (pid %s)\n"
 msgstr ""
 
-#: ../Beremiz.py:1420 ../Beremiz.py:1422
+#: ../Beremiz.py:1445 ../Beremiz.py:1447
 msgid "file : "
 msgstr ""
 
-#: ../Beremiz.py:1423
+#: ../Beremiz.py:1448
 msgid "function : "
 msgstr ""
 
-#: ../Beremiz.py:1423
+#: ../Beremiz.py:1448
 msgid "line : "
 msgstr ""
 
Binary file images/EditCfile.png has changed
Binary file images/HideVars.png has changed
Binary file images/Maximize.png has changed
Binary file images/Minimize.png has changed
Binary file images/ShowIECcode.png has changed
Binary file images/ShowVars.png has changed
Binary file images/editIECrawcode.png has changed
Binary file images/editPYTHONcode.png has changed
Binary file images/editWXGLADE.png has changed
--- a/images/icons.svg	Tue Oct 27 16:32:54 2009 +0100
+++ b/images/icons.svg	Mon Nov 02 15:38:49 2009 +0100
@@ -29,8 +29,8 @@
     </rdf:RDF>
   </metadata>
   <sodipodi:namedview
-     inkscape:window-height="750"
-     inkscape:window-width="1255"
+     inkscape:window-height="974"
+     inkscape:window-width="1280"
      inkscape:pageshadow="2"
      inkscape:pageopacity="0.0"
      guidetolerance="10.0"
@@ -40,13 +40,13 @@
      bordercolor="#666666"
      pagecolor="#ffffff"
      id="base"
-     showgrid="true"
-     inkscape:zoom="7.9999996"
-     inkscape:cx="667.296"
-     inkscape:cy="684.08131"
-     inkscape:window-x="15"
+     showgrid="false"
+     inkscape:zoom="1.4142135"
+     inkscape:cx="335.487"
+     inkscape:cy="81.26617"
+     inkscape:window-x="0"
      inkscape:window-y="25"
-     inkscape:current-layer="g46878"
+     inkscape:current-layer="g18993"
      showguides="true"
      inkscape:guide-bbox="true">
     <inkscape:grid
@@ -85166,24 +85166,8 @@
          y="1063.0302"
          id="tspan16413">2009.0 Beta</tspan></text>
     <g
-       id="g17729"
-       transform="matrix(1.1079964,0,0,1.1079964,-1240.4932,723.37188)">
-      <path
-         d="M 210.40571,340.85362 L 202.96571,340.85362 C 200.83237,340.88029 199.11237,340.14696 197.80571,338.65362 C 196.49904,337.16029 195.8457,335.34696 195.84571,333.21362 L 195.84571,315.41362 L 203.60571,315.41362 L 203.60571,329.97362 C 203.6057,331.30697 203.73903,332.22697 204.00571,332.73362 C 204.27236,333.2403 204.89903,333.49363 205.88571,333.49362 L 210.40571,333.45362 L 210.40571,340.85362 L 210.40571,340.85362 M 212.92957,331.69362 C 212.92957,329.1603 213.80957,326.9603 215.56957,325.09362 C 217.32956,323.22698 219.50289,322.29364 222.08957,322.29362 C 225.07622,322.29364 227.43622,323.24031 229.16957,325.13362 C 230.92955,327.0003 231.82288,329.06697 231.84957,331.33362 C 231.84955,331.3603 231.84955,331.38697 231.84957,331.41362 C 231.84955,331.41363 231.84955,331.42697 231.84957,331.45362 C 231.84955,334.57363 230.84955,337.00029 228.84957,338.73362 C 226.87622,340.46696 224.70289,341.36029 222.32957,341.41362 C 222.27623,341.41362 222.22289,341.41362 222.16957,341.41362 C 222.14289,341.41362 222.11623,341.41362 222.08957,341.41362 C 221.66289,341.41362 221.20956,341.37362 220.72957,341.29362 C 220.24956,341.21362 219.76956,341.09362 219.28957,340.93362 C 217.68956,340.37362 216.2229,339.33363 214.88957,337.81362 C 213.5829,336.29363 212.92957,334.26696 212.92957,331.73362 L 212.92957,331.69362 M 220.04957,331.69362 C 220.04956,332.4403 220.27623,333.02697 220.72957,333.45362 C 221.20956,333.85363 221.72956,334.06696 222.28957,334.09362 C 222.31623,334.09363 222.32956,334.09363 222.32957,334.09362 C 222.35623,334.09363 222.38289,334.09363 222.40957,334.09362 C 222.96956,334.09363 223.47622,333.89363 223.92957,333.49362 C 224.40956,333.06697 224.64956,332.4803 224.64957,331.73362 C 224.64956,331.09363 224.44956,330.6003 224.04957,330.25362 C 223.67622,329.8803 223.24956,329.65364 222.76957,329.57362 C 222.71623,329.54697 222.64956,329.53364 222.56957,329.53362 C 222.51623,329.53364 222.46289,329.53364 222.40957,329.53362 C 222.38289,329.53364 222.35623,329.53364 222.32957,329.53362 C 222.32956,329.53364 222.31623,329.53364 222.28957,329.53362 C 221.72956,329.5603 221.20956,329.77364 220.72957,330.17362 C 220.27623,330.54697 220.04956,331.06697 220.04957,331.73362 L 220.04957,331.69362"
-         style="font-size:40px;font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bauhaus-Heavy-Bold;-inkscape-font-specification:Bauhaus-Heavy-Bold Ultra-Light"
-         id="path17724" />
-      <path
-         d="M 249.67894,340.85362 L 242.23894,340.85362 C 240.1056,340.88029 238.38561,340.14696 237.07894,338.65362 C 235.77228,337.16029 235.11894,335.34696 235.11894,333.21362 L 235.11894,315.41362 L 242.87894,315.41362 L 242.87894,329.97362 C 242.87894,331.30697 243.01227,332.22697 243.27894,332.73362 C 243.5456,333.2403 244.17227,333.49363 245.15894,333.49362 L 249.67894,333.45362 L 249.67894,340.85362 L 249.67894,340.85362 M 251.81535,340.85362 L 251.81535,322.89362 L 259.49535,322.89362 L 259.49535,340.85362 L 251.81535,340.85362 L 251.81535,340.85362 M 251.53535,316.85362 C 251.53535,315.60032 251.96202,314.62698 252.81535,313.93362 C 253.66868,313.21365 254.66868,312.85365 255.81535,312.85362 C 255.94868,312.85365 256.06868,312.86699 256.17535,312.89362 C 256.30868,312.89365 256.42868,312.90699 256.53535,312.93362 C 257.44201,313.06699 258.26868,313.48032 259.01535,314.17362 C 259.76201,314.86698 260.13534,315.77365 260.13535,316.89362 L 260.13535,317.13362 C 260.13534,318.44031 259.70867,319.45365 258.85535,320.17362 C 258.00201,320.86698 257.06868,321.24031 256.05535,321.29362 C 256.00201,321.29364 255.94868,321.29364 255.89535,321.29362 C 255.86868,321.29364 255.84201,321.29364 255.81535,321.29362 C 254.74868,321.29364 253.76201,320.93364 252.85535,320.21362 C 251.97535,319.46698 251.53535,318.36031 251.53535,316.89362 L 251.53535,316.85362"
-         style="font-size:40px;font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#5759ce;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34643704px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bauhaus-Heavy-Bold;-inkscape-font-specification:Bauhaus-Heavy-Bold Ultra-Light"
-         id="path17718" />
-      <path
-         d="M 266.90448,340.85362 L 266.90448,322.53362 L 262.62448,322.53362 L 262.62448,315.41362 L 279.18448,315.41362 L 279.18448,322.53362 L 274.94448,322.53362 L 274.94448,340.85362 L 266.90448,340.85362 L 266.90448,340.85362 M 297.09281,329.13362 L 287.93281,334.33362 L 285.85281,330.57362 L 289.33281,328.57362 C 289.1728,328.4403 288.98613,328.30697 288.77281,328.17362 C 288.58613,328.0403 288.38613,327.94697 288.17281,327.89362 C 288.0928,327.86697 288.0128,327.85364 287.93281,327.85362 C 287.8528,327.82697 287.7728,327.81364 287.69281,327.81362 C 287.63946,327.81364 287.5728,327.82697 287.49281,327.85362 C 287.43947,327.85364 287.3728,327.85364 287.29281,327.85362 C 286.6528,327.98697 286.06613,328.4003 285.53281,329.09362 C 284.99947,329.7603 284.7328,330.54697 284.73281,331.45362 C 284.7328,332.6803 285.06613,333.65363 285.73281,334.37362 C 286.42613,335.06696 287.26613,335.41363 288.25281,335.41362 C 288.62613,335.41363 289.02613,335.3603 289.45281,335.25362 C 289.87946,335.1203 290.30613,334.94696 290.73281,334.73362 C 291.13279,334.5203 291.51946,334.26696 291.89281,333.97362 C 292.29279,333.6803 292.66613,333.33363 293.01281,332.93362 L 297.09281,336.89362 C 296.18612,338.22696 294.97279,339.28029 293.45281,340.05362 C 291.93279,340.82696 290.34613,341.29362 288.69281,341.45362 C 288.50613,341.45362 288.31946,341.45362 288.13281,341.45362 C 287.9728,341.48029 287.79946,341.49362 287.61281,341.49362 C 285.31947,341.49362 283.19947,340.77362 281.25281,339.33362 C 279.33281,337.89363 278.30614,335.57363 278.17281,332.37362 C 278.17281,332.34697 278.15947,332.3203 278.13281,332.29362 C 278.13281,332.2403 278.13281,332.18697 278.13281,332.13362 C 278.13281,332.0803 278.11947,332.0403 278.09281,332.01362 C 278.09281,331.9603 278.09281,331.9203 278.09281,331.89362 C 278.09281,329.14697 278.85281,327.0003 280.37281,325.45362 C 281.8928,323.90697 283.55947,322.93364 285.37281,322.53362 C 285.6928,322.45364 286.0128,322.40031 286.33281,322.37362 C 286.67947,322.32031 287.0128,322.29364 287.33281,322.29362 C 290.18613,322.29364 292.34613,322.98698 293.81281,324.37362 C 295.27946,325.73364 296.37279,327.33364 297.09281,329.17362 L 297.09281,329.13362 M 311.8195,340.85362 L 308.4195,340.85362 C 305.11282,340.88029 302.61949,339.94696 300.9395,338.05362 C 299.28616,336.1603 298.4595,334.0803 298.4595,331.81362 C 298.4595,331.73363 298.4595,331.66697 298.4595,331.61362 C 298.4595,331.53363 298.4595,331.45363 298.4595,331.37362 C 298.56616,329.10697 299.47283,327.02697 301.1795,325.13362 C 302.91282,323.24031 305.41949,322.29364 308.6995,322.29362 L 311.8195,322.25362 L 311.8195,328.85362 L 308.4195,328.85362 C 307.69949,328.8803 307.11282,329.1603 306.6595,329.69362 C 306.20615,330.22697 305.93949,330.8003 305.8595,331.41362 C 305.85949,331.46697 305.85949,331.5203 305.8595,331.57362 C 305.85949,331.62697 305.85949,331.6803 305.8595,331.73362 C 305.85949,331.7603 305.85949,331.8003 305.8595,331.85362 C 305.85949,331.8803 305.85949,331.90697 305.8595,331.93362 C 305.91282,332.54697 306.15282,333.10697 306.5795,333.61362 C 307.03282,334.1203 307.64615,334.37363 308.4195,334.37362 L 311.8195,334.33362 L 311.8195,340.85362 L 311.8195,340.85362 M 314.36637,340.85362 L 314.36637,315.41362 L 321.76637,315.41362 L 321.76637,323.73362 C 322.2997,323.36031 322.87303,323.06698 323.48637,322.85362 C 324.09969,322.61364 324.71303,322.46698 325.32637,322.41362 C 325.43303,322.38698 325.52636,322.37364 325.60637,322.37362 C 325.71303,322.37364 325.80636,322.37364 325.88637,322.37362 C 327.45969,322.37364 328.88636,322.97364 330.16637,324.17362 C 331.47302,325.34697 332.12635,327.10697 332.12637,329.45362 L 332.12637,340.85362 L 324.64637,340.85362 L 324.64637,330.29362 C 324.64636,329.86697 324.52636,329.5203 324.28637,329.25362 C 324.04636,328.98697 323.79303,328.82697 323.52637,328.77362 C 323.47303,328.74697 323.41969,328.73364 323.36637,328.73362 C 323.33969,328.73364 323.29969,328.73364 323.24637,328.73362 C 323.21969,328.73364 323.19303,328.73364 323.16637,328.73362 C 323.16636,328.73364 323.15303,328.73364 323.12637,328.73362 C 322.7797,328.7603 322.4597,328.9203 322.16637,329.21362 C 321.8997,329.4803 321.76636,329.85364 321.76637,330.33362 L 321.76637,340.85362 L 314.36637,340.85362 L 314.36637,340.85362"
-         style="font-size:40px;font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:1;fill:#939393;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.34643704px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Bauhaus-Heavy-Bold;-inkscape-font-specification:Bauhaus-Heavy-Bold Ultra-Light"
-         id="path17712" />
-    </g>
-    <g
        id="g2414"
-       transform="matrix(0.5511608,0,0,0.5511608,-864.43103,996.99467)">
+       transform="matrix(0.5511608,0,0,0.5511608,-977.19352,996.99467)">
       <path
          d="M 120.87354,163.01839 L 128.91354,163.01839 L 128.91354,179.93839 C 128.91353,180.60507 129.0602,181.0984 129.35354,181.41839 C 129.67353,181.7384 130.00687,181.8984 130.35354,181.89839 C 130.3802,181.8984 130.39353,181.8984 130.39354,181.89839 C 130.4202,181.8984 130.44687,181.8984 130.47354,181.89839 C 130.79353,181.87173 131.08687,181.6984 131.35354,181.37839 C 131.6202,181.0584 131.75353,180.59173 131.75354,179.97839 L 131.75354,163.01839 L 139.79354,163.01839 L 139.79354,179.65839 C 139.79352,180.3784 139.95352,180.9384 140.27354,181.33839 C 140.59352,181.71173 140.95352,181.8984 141.35354,181.89839 C 141.40685,181.8984 141.44685,181.8984 141.47354,181.89839 C 141.52685,181.8984 141.58019,181.88506 141.63354,181.85839 C 141.95352,181.7784 142.24685,181.5784 142.51354,181.25839 C 142.78019,180.9384 142.91352,180.51173 142.91354,179.97839 L 142.91354,163.01839 L 150.95354,163.01839 L 150.95354,180.77839 C 150.95351,183.12506 150.14018,185.00506 148.51354,186.41839 C 146.88685,187.80506 145.10018,188.63172 143.15354,188.89839 C 142.91352,188.95172 142.67352,188.97839 142.43354,188.97839 C 142.19352,189.00506 141.95352,189.01839 141.71354,189.01839 C 140.56686,189.01839 139.48686,188.81839 138.47354,188.41839 C 137.48686,187.99172 136.67353,187.35173 136.03354,186.49839 C 135.31353,187.32506 134.4202,187.93839 133.35354,188.33839 C 132.28686,188.71172 131.1802,188.89839 130.03354,188.89839 C 129.87353,188.89839 129.71353,188.88506 129.55354,188.85839 C 129.4202,188.85839 129.27353,188.85839 129.11354,188.85839 C 127.0602,188.67172 125.16687,187.87173 123.43354,186.45839 C 121.72687,185.01839 120.87354,183.04506 120.87354,180.53839 L 120.87354,163.01839 L 120.87354,163.01839 M 172.95104,176.73839 L 163.79104,181.93839 L 161.71104,178.17839 L 165.19104,176.17839 C 165.03103,176.04507 164.84436,175.91174 164.63104,175.77839 C 164.44437,175.64507 164.24437,175.55174 164.03104,175.49839 C 163.95103,175.47174 163.87103,175.4584 163.79104,175.45839 C 163.71103,175.43174 163.63103,175.4184 163.55104,175.41839 C 163.4977,175.4184 163.43103,175.43174 163.35104,175.45839 C 163.2977,175.4584 163.23103,175.4584 163.15104,175.45839 C 162.51103,175.59174 161.92437,176.00507 161.39104,176.69839 C 160.8577,177.36507 160.59104,178.15173 160.59104,179.05839 C 160.59104,180.28507 160.92437,181.2584 161.59104,181.97839 C 162.28437,182.67173 163.12437,183.0184 164.11104,183.01839 C 164.48437,183.0184 164.88436,182.96506 165.31104,182.85839 C 165.7377,182.72506 166.16436,182.55173 166.59104,182.33839 C 166.99103,182.12506 167.3777,181.87173 167.75104,181.57839 C 168.15103,181.28507 168.52436,180.9384 168.87104,180.53839 L 172.95104,184.49839 C 172.04436,185.83173 170.83103,186.88506 169.31104,187.65839 C 167.79103,188.43172 166.20436,188.89839 164.55104,189.05839 C 164.36437,189.05839 164.1777,189.05839 163.99104,189.05839 C 163.83103,189.08506 163.6577,189.09839 163.47104,189.09839 C 161.1777,189.09839 159.0577,188.37839 157.11104,186.93839 C 155.19104,185.49839 154.16438,183.1784 154.03104,179.97839 C 154.03104,179.95173 154.01771,179.92507 153.99104,179.89839 C 153.99104,179.84507 153.99104,179.79173 153.99104,179.73839 C 153.99104,179.68507 153.97771,179.64507 153.95104,179.61839 C 153.95104,179.56507 153.95104,179.52507 153.95104,179.49839 C 153.95104,176.75174 154.71104,174.60507 156.23104,173.05839 C 157.75104,171.51174 159.4177,170.53841 161.23104,170.13839 C 161.55103,170.05841 161.87103,170.00508 162.19104,169.97839 C 162.5377,169.92508 162.87103,169.89841 163.19104,169.89839 C 166.04436,169.89841 168.20436,170.59174 169.67104,171.97839 C 171.13769,173.33841 172.23102,174.9384 172.95104,176.77839 L 172.95104,176.73839"
          style="font-size:40px;font-style:normal;font-variant:normal;font-weight:200;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bauhaus-Heavy-Bold;-inkscape-font-specification:Bauhaus-Heavy-Bold Ultra-Light"
Binary file images/splash.png has changed
Binary file locale/fr_FR/LC_MESSAGES/Beremiz.mo has changed
--- a/plugger.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugger.py	Mon Nov 02 15:38:49 2009 +0100
@@ -74,7 +74,17 @@
 # helper func to get path to images
 def opjimg(imgname):
     return os.path.join("images",imgname)
-
+    
+# helper func to check path write permission
+def CheckPathPerm(path):
+    if path is None or not os.path.isdir(path):
+        return False
+    for root, dirs, files in os.walk(path):
+         for name in files:
+             if os.access(root, os.W_OK) is not True or os.access(os.path.join(root, name), os.W_OK) is not True:
+                 return False
+    return True
+    
 class PlugTemplate:
     """
     This class is the one that define plugins.
@@ -181,38 +191,39 @@
         os.mkdir(self.PlugPath())
 
     def PlugRequestSave(self):
-        # If plugin do not have corresponding directory
-        plugpath = self.PlugPath()
-        if not os.path.isdir(plugpath):
-            # Create it
-            os.mkdir(plugpath)
-
-        # generate XML for base XML parameters controller of the plugin
-        if self.MandatoryParams:
-            BaseXMLFile = open(self.PluginBaseXmlFilePath(),'w')
-            BaseXMLFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-            BaseXMLFile.write(self.MandatoryParams[1].generateXMLText(self.MandatoryParams[0], 0))
-            BaseXMLFile.close()
-        
-        # generate XML for XML parameters controller of the plugin
-        if self.PlugParams:
-            XMLFile = open(self.PluginXmlFilePath(),'w')
-            XMLFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
-            XMLFile.write(self.PlugParams[1].generateXMLText(self.PlugParams[0], 0))
-            XMLFile.close()
-        
-        # Call the plugin specific OnPlugSave method
-        result = self.OnPlugSave()
-        if not result:
-            return _("Error while saving \"%s\"\n")%self.PlugPath()
-
-        # mark plugin as saved
-        self.ChangesToSave = False        
-        # go through all childs and do the same
-        for PlugChild in self.IterChilds():
-            result = PlugChild.PlugRequestSave()
-            if result:
-                return result
+        if self.GetPlugRoot().CheckProjectPathPerm(False):
+            # If plugin do not have corresponding directory
+            plugpath = self.PlugPath()
+            if not os.path.isdir(plugpath):
+                # Create it
+                os.mkdir(plugpath)
+    
+            # generate XML for base XML parameters controller of the plugin
+            if self.MandatoryParams:
+                BaseXMLFile = open(self.PluginBaseXmlFilePath(),'w')
+                BaseXMLFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+                BaseXMLFile.write(self.MandatoryParams[1].generateXMLText(self.MandatoryParams[0], 0).encode("utf-8"))
+                BaseXMLFile.close()
+            
+            # generate XML for XML parameters controller of the plugin
+            if self.PlugParams:
+                XMLFile = open(self.PluginXmlFilePath(),'w')
+                XMLFile.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
+                XMLFile.write(self.PlugParams[1].generateXMLText(self.PlugParams[0], 0).encode("utf-8"))
+                XMLFile.close()
+            
+            # Call the plugin specific OnPlugSave method
+            result = self.OnPlugSave()
+            if not result:
+                return _("Error while saving \"%s\"\n")%self.PlugPath()
+    
+            # mark plugin as saved
+            self.ChangesToSave = False
+            # go through all childs and do the same
+            for PlugChild in self.IterChilds():
+                result = PlugChild.PlugRequestSave()
+                if result:
+                    return result
         return None
     
     def PlugImport(self, src_PlugPath):
@@ -690,7 +701,7 @@
                    "LREAL" :      8,
                   } 
 
-import re
+import re, tempfile
 import targets
 import connectors
 from discovery import DiscoveryDialog
@@ -764,7 +775,7 @@
         self.PlugType = "Beremiz"
         # After __init__ root plugin is not valid
         self.ProjectPath = None
-        self.BuildPath = None
+        self._setBuildPath(None)
         self.DebugThread = None
         self.debug_break = False
         self.previous_plcstate = None
@@ -852,6 +863,23 @@
         if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None:
             self.BeremizRoot.setTargetType(self.GetDefaultTarget())
         return PlugTemplate.SetParamsAttribute(self, path, value)
+        
+    # helper func to check project path write permission
+    def CheckProjectPathPerm(self, dosave=True):
+        if CheckPathPerm(self.ProjectPath):
+            return True
+        dialog = wx.MessageDialog(self.AppFrame, 
+                    _('You must have permission to work on the project\nWork on a project copy ?'),
+                    _('Error'), 
+                    wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
+        answer = dialog.ShowModal()
+        dialog.Destroy()
+        if answer == wx.ID_YES:
+            if self.SaveProjectAs():
+                self.AppFrame.RefreshAll()
+                self.AppFrame.RefreshTitle()
+                return True
+        return False
     
     def NewProject(self, ProjectPath, BuildPath=None):
         """
@@ -879,7 +907,7 @@
         self.PluggedChilds = {}
         # Keep track of the root plugin (i.e. project path)
         self.ProjectPath = ProjectPath
-        self.BuildPath = BuildPath
+        self._setBuildPath(BuildPath)
         # get plugins bloclist (is that usefull at project creation?)
         self.RefreshPluginsBlockLists()
         # this will create files base XML files
@@ -906,7 +934,7 @@
         self.PluggedChilds = {}
         # Keep track of the root plugin (i.e. project path)
         self.ProjectPath = ProjectPath
-        self.BuildPath = BuildPath
+        self._setBuildPath(BuildPath)
         # If dir have already be made, and file exist
         if os.path.isdir(self.PlugPath()) and os.path.isfile(self.PluginXmlFilePath()):
             #Load the plugin.xml file into parameters members
@@ -930,11 +958,26 @@
         self.ResetAppFrame(None)
         
     def SaveProject(self):
-        if not self.SaveXMLFile():
+        if self.CheckProjectPathPerm(False):
             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
-        result = self.PlugRequestSave()
-        if result:
-            self.logger.write_error(result)
+            result = self.PlugRequestSave()
+            if result:
+                self.logger.write_error(result)
+    
+    def SaveProjectAs(self, dosave=True):
+        # Ask user to choose a path with write permissions
+        dirdialog = wx.DirDialog(self.AppFrame , _("Choose a directory to save project"), os.getenv("HOME"), wx.DD_NEW_DIR_BUTTON)
+        answer = dirdialog.ShowModal()
+        dirdialog.Destroy()
+        if answer == wx.ID_OK:
+            newprojectpath = dirdialog.GetPath()
+            if os.path.isdir(newprojectpath):
+                self.ProjectPath = newprojectpath
+                if dosave:
+                    self.SaveProject()
+                self._setBuildPath(self.BuildPath)
+                return True
+        return False
     
     # Update PLCOpenEditor Plugin Block types from loaded plugins
     def RefreshPluginsBlockLists(self):
@@ -971,10 +1014,33 @@
     def ParentsBlockTypesFactory(self):
         return self.BlockTypesFactory()
 
+    def _setBuildPath(self, buildpath):
+        if CheckPathPerm(buildpath):
+            self.BuildPath = buildpath
+        else:
+            self.BuildPath = None
+        self.BuildPath = buildpath
+        self.DefaultBuildPath = None
+        if self._builder is not None:
+            self._builder.SetBuildPath(self._getBuildPath())
+
     def _getBuildPath(self):
-        if self.BuildPath is None:
-            return os.path.join(self.ProjectPath, "build")
-        return self.BuildPath
+        # BuildPath is defined by user
+        if self.BuildPath is not None:
+            return self.BuildPath
+        # BuildPath isn't defined by user but already created by default
+        if self.DefaultBuildPath is not None:
+            return self.DefaultBuildPath
+        # Create a build path in project folder if user has permissions
+        if CheckPathPerm(self.ProjectPath):
+            self.DefaultBuildPath = os.path.join(self.ProjectPath, "build")
+        # Create a build path in temp folder
+        else:
+            self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
+            
+        if not os.path.exists(self.DefaultBuildPath):
+            os.makedirs(self.DefaultBuildPath)
+        return self.DefaultBuildPath
     
     def _getExtraFilesPath(self):
         return os.path.join(self._getBuildPath(), "extra_files")
@@ -1287,7 +1353,7 @@
                       "__publish_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]),
                 "init_calls":"\n    ".join([
                       "init_level=%d; "%(i+1)+
-                      "if(res = __init_%s(argc,argv)){"%locstr +
+                      "if((res = __init_%s(argc,argv))){"%locstr +
                       #"printf(\"%s\"); "%locstr + #for debug
                       "return res;}" for i,locstr in enumerate(locstrs)]),
                 "cleanup_calls":"\n    ".join([
--- a/plugins/c_ext/CFileEditor.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/c_ext/CFileEditor.py	Mon Nov 02 15:38:49 2009 +0100
@@ -908,14 +908,12 @@
         selected = self.PartsOpened.GetSelection()
         if selected >= 0:
             self.PartsOpened.DeletePage(selected)
-        event.Skip()
 
     def OnSaveMenu(self, event):
         if getattr(self, "_onsave", None) != None:
             self._onsave()
         self.RefreshTitle()
         self.RefreshEditMenu()
-        event.Skip()
 
 #-------------------------------------------------------------------------------
 #                            Notebook Unified Functions
@@ -957,7 +955,6 @@
         if selected != -1:
             window = self.PartsOpened.GetPage(selected)
             window.RefreshView()
-        event.Skip()
 
     def OnUndoMenu(self, event):
         self.Controler.LoadPrevious()
@@ -967,7 +964,6 @@
             window.RefreshView()
         self.RefreshTitle()
         self.RefreshEditMenu()
-        event.Skip()
     
     def OnRedoMenu(self, event):
         self.Controler.LoadNext()
@@ -977,7 +973,6 @@
             window.RefreshView()
         self.RefreshTitle()
         self.RefreshEditMenu()
-        event.Skip()
         
 #-------------------------------------------------------------------------------
 #                      CFile Editor Panels Management Functions
--- a/plugins/c_ext/c_ext.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/c_ext/c_ext.py	Mon Nov 02 15:38:49 2009 +0100
@@ -222,14 +222,28 @@
     _View = None
     def _OpenView(self):
         if not self._View:
-            def _onclose():
-                self._View = None
-            def _onsave():
-                self.GetPlugRoot().SaveProject()
-            self._View = CFileEditor(self.GetPlugRoot().AppFrame, self)
-            self._View._onclose = _onclose
-            self._View._onsave = _onsave
-            self._View.Show()
+            open_cfileeditor = True
+            has_permissions = self.GetPlugRoot().CheckProjectPathPerm()
+            if not has_permissions:
+                dialog = wx.MessageDialog(self.GetPlugRoot().AppFrame,
+                                          _("You don't have write permissions.\nOpen CFileEditor anyway ?"),
+                                          _("Open CFileEditor"),
+                                          wx.YES_NO|wx.ICON_QUESTION)
+                open_cfileeditor = dialog.ShowModal() == wx.ID_YES
+                dialog.Destroy()
+            if open_cfileeditor:
+                def _onclose():
+                    self._View = None
+                if has_permissions:
+                    def _onsave():
+                        self.GetPlugRoot().SaveProject()
+                else:
+                    def _onsave():
+                        pass
+                self._View = CFileEditor(self.GetPlugRoot().AppFrame, self)
+                self._View._onclose = _onclose
+                self._View._onsave = _onsave
+                self._View.Show()
 
     PluginMethods = [
         {"bitmap" : os.path.join("images", "EditCfile"),
@@ -248,7 +262,7 @@
         text += self.CFile.generateXMLText("CFile", 0, extras)
 
         xmlfile = open(filepath,"w")
-        xmlfile.write(text)
+        xmlfile.write(text.encode("utf-8"))
         xmlfile.close()
         
         self.CFileBuffer.CurrentSaved()
--- a/plugins/canfestival/canfestival.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/canfestival/canfestival.py	Mon Nov 02 15:38:49 2009 +0100
@@ -100,16 +100,31 @@
     _View = None
     def _OpenView(self):
         if not self._View:
-            def _onclose():
-                self._View = None
-            def _onsave():
-                self.GetPlugRoot().SaveProject()
-            self._View = objdictedit(self.GetPlugRoot().AppFrame, self)
-            # TODO redefine BusId when IEC channel change
-            self._View.SetBusId(self.GetCurrentLocation())
-            self._View._onclose = _onclose
-            self._View._onsave = _onsave
-            self._View.Show()
+            open_objdictedit = True
+            has_permissions = self.GetPlugRoot().CheckProjectPathPerm()
+            if not has_permissions:
+                dialog = wx.MessageDialog(self.GetPlugRoot().AppFrame,
+                                          _("You don't have write permissions.\nOpen ObjDictEdit anyway ?"),
+                                          _("Open ObjDictEdit"),
+                                          wx.YES_NO|wx.ICON_QUESTION)
+                open_objdictedit = dialog.ShowModal() == wx.ID_YES
+                dialog.Destroy()
+            if open_objdictedit:
+                def _onclose():
+                    self._View = None
+                if has_permissions:
+                    def _onsave():
+                        self.GetPlugRoot().SaveProject()
+                else:
+                    def _onsave():
+                        pass
+            
+                self._View = objdictedit(self.GetPlugRoot().AppFrame, self)
+                # TODO redefine BusId when IEC channel change
+                self._View.SetBusId(self.GetCurrentLocation())
+                self._View._onclose = _onclose
+                self._View._onsave = _onsave
+                self._View.Show()
 
     PluginMethods = [
         {"bitmap" : os.path.join("images", "NetworkEdit"),
@@ -185,16 +200,30 @@
     _View = None
     def _OpenView(self):
         if not self._View:
-            def _onclose():
-                self._View = None
-            def _onsave():
-                self.GetPlugRoot().SaveProject()
-            self._View = networkedit(self.GetPlugRoot().AppFrame, self)
-            # TODO redefine BusId when IEC channel change
-            self._View.SetBusId(self.GetCurrentLocation())
-            self._View._onclose = _onclose
-            self._View._onsave = _onsave
-            self._View.Show()
+            open_networkedit = True
+            has_permissions = self.GetPlugRoot().CheckProjectPathPerm()
+            if not has_permissions:
+                dialog = wx.MessageDialog(self.GetPlugRoot().AppFrame,
+                                          _("You don't have write permissions.\nOpen NetworkEdit anyway ?"),
+                                          _("Open NetworkEdit"),
+                                          wx.YES_NO|wx.ICON_QUESTION)
+                open_networkedit = dialog.ShowModal() == wx.ID_YES
+                dialog.Destroy()
+            if open_networkedit:
+                def _onclose():
+                    self._View = None
+                if has_permissions:
+                    def _onsave():
+                        self.GetPlugRoot().SaveProject()
+                else:
+                    def _onsave():
+                        pass
+                self._View = networkedit(self.GetPlugRoot().AppFrame, self)
+                # TODO redefine BusId when IEC channel change
+                self._View.SetBusId(self.GetCurrentLocation())
+                self._View._onclose = _onclose
+                self._View._onsave = _onsave
+                self._View.Show()
 
     def _ShowMasterGenerated(self):
         buildpath = self._getBuildPath()
--- a/plugins/python/PythonEditor.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/python/PythonEditor.py	Mon Nov 02 15:38:49 2009 +0100
@@ -560,7 +560,6 @@
             self._onsave()
         self.RefreshTitle()
         self.RefreshEditMenu()
-        event.Skip()
 
     def RefreshTitle(self):
         title = _("PythonEditor")
@@ -577,19 +576,17 @@
 
     def OnRefreshMenu(self, event):
         self.PythonEdited.RefreshView()
-        event.Skip()
 
     def OnUndoMenu(self, event):
         self.Controler.LoadPrevious()
         self.PythonEdited.RefreshView()
         self.RefreshTitle()
         self.RefreshEditMenu()
-        event.Skip()
     
     def OnRedoMenu(self, event):
         self.Controler.LoadNext()
         self.PythonEdited.RefreshView()
         self.RefreshTitle()
         self.RefreshEditMenu()
-        event.Skip()
-
+        
+
--- a/plugins/python/modules/svgui/pous.xml	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/python/modules/svgui/pous.xml	Mon Nov 02 15:38:49 2009 +0100
@@ -3,7 +3,7 @@
          xmlns="http://www.plcopen.org/xml/tc6.xsd"
          xmlns:xhtml="http://www.w3.org/1999/xhtml"
          xsi:schemaLocation="http://www.plcopen.org/xml/tc6.xsd">
-  <fileHeader companyName="LOLITECH"
+  <fileHeader companyName="Beremiz"
               productName="Beremiz"
               productVersion="0.0"
               creationDateTime="2008-12-14T16:53:26"/>
--- a/plugins/python/modules/svgui/svgui.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/python/modules/svgui/svgui.py	Mon Nov 02 15:38:49 2009 +0100
@@ -98,6 +98,15 @@
 
     def _StartInkscape(self):
         svgfile = self._getSVGpath()
-        if not os.path.isfile(svgfile):
-            svgfile = None
-        open_svg(svgfile)
+        open_inkscape = True
+        if not self.GetPlugRoot().CheckProjectPathPerm():
+            dialog = wx.MessageDialog(self.GetPlugRoot().AppFrame,
+                                      _("You don't have write permissions.\nOpen Inkscape anyway ?"),
+                                      _("Open Inkscape"),
+                                      wx.YES_NO|wx.ICON_QUESTION)
+            open_inkscape = dialog.ShowModal() == wx.ID_YES
+            dialog.Destroy()
+        if open_inkscape:
+            if not os.path.isfile(svgfile):
+                svgfile = None
+            open_svg(svgfile)
--- a/plugins/python/modules/wxglade_hmi/wxglade_hmi.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/python/modules/wxglade_hmi/wxglade_hmi.py	Mon Nov 02 15:38:49 2009 +0100
@@ -100,16 +100,25 @@
 
     def _editWXGLADE(self):
         wxg_filename = self._getWXGLADEpath()
-        if not os.path.exists(wxg_filename):
-            hmi_name = self.BaseParams.getName()
-            open(wxg_filename,"w").write("""<?xml version="1.0"?>
-<application path="" name="" class="" option="0" language="python" top_window="%(name)s" encoding="UTF-8" use_gettext="0" overwrite="0" use_new_namespace="1" for_version="2.8" is_template="0">
-    <object class="%(class)s" name="%(name)s" base="EditFrame">
-        <style>wxDEFAULT_FRAME_STYLE</style>
-        <title>frame_1</title>
-    </object>
-</application>
-""" % {"name": hmi_name, "class": "Class_%s" % hmi_name})
-        if wx.Platform == '__WXMSW__':
-            wxg_filename = "\"%s\""%wxg_filename
-        self.launch_wxglade([wxg_filename])
+        open_wxglade = True
+        if not self.GetPlugRoot().CheckProjectPathPerm():
+            dialog = wx.MessageDialog(self.GetPlugRoot().AppFrame,
+                                      _("You don't have write permissions.\nOpen wxGlade anyway ?"),
+                                      _("Open wxGlade"),
+                                      wx.YES_NO|wx.ICON_QUESTION)
+            open_wxglade = dialog.ShowModal() == wx.ID_YES
+            dialog.Destroy()
+        if open_wxglade:
+            if not os.path.exists(wxg_filename):
+                hmi_name = self.BaseParams.getName()
+                open(wxg_filename,"w").write("""<?xml version="1.0"?>
+    <application path="" name="" class="" option="0" language="python" top_window="%(name)s" encoding="UTF-8" use_gettext="0" overwrite="0" use_new_namespace="1" for_version="2.8" is_template="0">
+        <object class="%(class)s" name="%(name)s" base="EditFrame">
+            <style>wxDEFAULT_FRAME_STYLE</style>
+            <title>frame_1</title>
+        </object>
+    </application>
+    """ % {"name": hmi_name, "class": "Class_%s" % hmi_name})
+            if wx.Platform == '__WXMSW__':
+                wxg_filename = "\"%s\""%wxg_filename
+            self.launch_wxglade([wxg_filename])
--- a/plugins/python/pous.xml	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/python/pous.xml	Mon Nov 02 15:38:49 2009 +0100
@@ -3,7 +3,7 @@
          xmlns="http://www.plcopen.org/xml/tc6.xsd"
          xmlns:xhtml="http://www.w3.org/1999/xhtml"
          xsi:schemaLocation="http://www.plcopen.org/xml/tc6.xsd">
-  <fileHeader companyName="LOLITECH"
+  <fileHeader companyName="Beremiz"
               productName="Beremiz"
               productVersion="0.0"
               creationDateTime="2008-12-14T16:53:26"/>
--- a/plugins/python/python.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/plugins/python/python.py	Mon Nov 02 15:38:49 2009 +0100
@@ -141,14 +141,28 @@
     _View = None
     def _OpenView(self):
         if not self._View:
-            def _onclose():
-                self._View = None
-            def _onsave():
-                self.GetPlugRoot().SaveProject()
-            self._View = PythonEditorFrame(self.GetPlugRoot().AppFrame, self)
-            self._View._onclose = _onclose
-            self._View._onsave = _onsave
-            self._View.Show()
+            open_pyeditor = True
+            has_permissions = self.GetPlugRoot().CheckProjectPathPerm()
+            if not has_permissions:
+                dialog = wx.MessageDialog(self.GetPlugRoot().AppFrame,
+                                          _("You don't have write permissions.\nOpen PythonEditor anyway ?"),
+                                          _("Open PythonEditor"),
+                                          wx.YES_NO|wx.ICON_QUESTION)
+                open_pyeditor = dialog.ShowModal() == wx.ID_YES
+                dialog.Destroy()
+            if open_pyeditor:
+                def _onclose():
+                    self._View = None
+                if has_permissions:
+                    def _onsave():
+                        self.GetPlugRoot().SaveProject()
+                else:
+                    def _onsave():
+                        pass
+                self._View = PythonEditorFrame(self.GetPlugRoot().AppFrame, self)
+                self._View._onclose = _onclose
+                self._View._onsave = _onsave
+                self._View.Show()
     
     def OnPlugSave(self):
         filepath = self.PythonFileName()
@@ -160,7 +174,7 @@
         text += self.PythonCode.generateXMLText("Python", 0, extras)
 
         xmlfile = open(filepath,"w")
-        xmlfile.write(text)
+        xmlfile.write(text.encode("utf-8"))
         xmlfile.close()
         
         self.PythonBuffer.CurrentSaved()
--- a/setup.py	Tue Oct 27 16:32:54 2009 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-import os,sys,glob
-from distutils.core import setup
-
-
-install_dir=os.path.join("LOLITech","beremiz")
-
-data_files=[]
-os.getcwd()
-os.chdir(os.getcwd())
-
-def generate(base_dir):
-    listfile=[]
-    if base_dir == "":
-        directory = "."
-    else:
-        directory = base_dir
-    data_files.append((os.path.join(install_dir, base_dir), listfile))
-
-    for element in os.listdir(directory):
-        element_path=os.path.join(base_dir, element)
-        if os.path.isdir(element_path):
-            generate(element_path)
-        elif os.path.isfile(element_path):
-            listfile.append(element_path)
-
-generate("")
-
-
-setup(name='Beremiz',
-      version='0.1',
-      description='Open Source framework for automation',
-      author='Edouard Tisserant, Laurent Bessard',
-      author_email='edouard.tisserant@lolitech.fr,laurent.bessard@lolitech.fr,gregory.trelat@lolitech.fr',
-      url='http://www.beremiz.org',
-      license='GPL',
-      windows=["Beremiz.py"],
-      scripts=['beremiz_postinst.py'],
-      data_files=data_files, # Add files to install
-)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Makefile/XSD	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,6 @@
+
+                  <xsd:element name="Makefile">
+                    <xsd:complexType>
+                      %(toolchain_makefile)s
+                    </xsd:complexType>
+                  </xsd:element>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Makefile/__init__.py	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,7 @@
+from .. import toolchain_makefile
+
+class Makefile_target(toolchain_makefile):
+    extension = ".ld"
+    DebugEnabled = False
+    def getBuilderLDFLAGS(self):
+        return toolchain_makefile.getBuilderLDFLAGS(self)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/Makefile/plc_Makefile_main.c	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,93 @@
+/**
+ * Yagarto specific code
+ **/
+
+//#include <stdio.h>
+
+/* provided by POUS.C */
+extern int common_ticktime__;
+
+void Target_GetTime(IEC_TIME*);
+
+long AtomicCompareExchange(long* atomicvar,long compared, long exchange)
+{
+	return 0;
+}
+
+void PLC_GetTime(IEC_TIME *CURRENT_TIME)
+{
+	/* Call target GetTime function */
+	Target_GetTime(CURRENT_TIME);
+}
+
+void PLC_SetTimer(long long next, long long period)
+{
+}
+
+int startPLC(int argc,char **argv)
+{
+	if(__init(argc,argv) == 0)
+		return 0;
+	else
+		return 1;
+}
+
+int TryEnterDebugSection(void)
+{
+    return 0;
+}
+
+void LeaveDebugSection(void)
+{
+}
+
+int stopPLC(void)
+{
+    __cleanup();
+    return 0;
+}
+
+extern unsigned long __tick;
+/* from plc_debugger.c */
+int WaitDebugData(void)
+{
+    return 0;
+}
+
+/* Called by PLC thread when debug_publish finished
+ * This is supposed to unlock debugger thread in WaitDebugData*/
+void InitiateDebugTransfer(void)
+{
+}
+
+void suspendDebug(void)
+{
+}
+
+void resumeDebug(void)
+{
+}
+
+/* from plc_python.c */
+int WaitPythonCommands(void)
+{
+    return 0;
+}
+
+/* Called by PLC thread on each new python command*/
+void UnBlockPythonCommands(void)
+{
+}
+
+int TryLockPython(void)
+{
+	return 0;
+}
+
+void UnLockPython(void)
+{
+}
+
+void LockPython(void)
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/XSD_toolchain_makefile	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,6 @@
+
+          <xsd:attribute name="BuildPath" type="xsd:string" use="optional" default=""/>
+          <xsd:attribute name="Command" type="xsd:string" use="optional" default="make -C"/>
+          <xsd:attribute name="Arguments" type="xsd:string" use="optional" default="BEREMIZSRC=%(src)s BEREMIZCFLAGS=%(cflags)s USE_BEREMIZ=1 --quiet"/>
+          <xsd:attribute name="Rule" type="xsd:string" use="optional" default="all"/>
+          
--- a/targets/__init__.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/targets/__init__.py	Mon Nov 02 15:38:49 2009 +0100
@@ -74,4 +74,5 @@
     filename = path.join(path.split(__file__)[0],name + ".c")
     return open(filename).read()
 
-from toolchain_gcc import toolchain_gcc
\ No newline at end of file
+from toolchain_gcc import toolchain_gcc
+from toolchain_makefile import toolchain_makefile
\ No newline at end of file
--- a/targets/plc_common_main.c	Tue Oct 27 16:32:54 2009 +0100
+++ b/targets/plc_common_main.c	Mon Nov 02 15:38:49 2009 +0100
@@ -41,7 +41,7 @@
 /*
  * Retrieve input variables, run PLC and publish output variables
  **/
-void __run()
+void __run(void)
 {
     __tick++;
     if (greatest_tick_count__)
@@ -66,6 +66,7 @@
 int __init(int argc,char **argv)
 {
     int res = 0;
+    init_level = 0;
     setlocale(LC_NUMERIC, "C");
     config_init__();
     __init_debug();
@@ -75,7 +76,7 @@
 /*
  * Calls plugin cleanup proc.
  **/
-void __cleanup()
+void __cleanup(void)
 {
     %(cleanup_calls)s
     __cleanup_debug();
--- a/targets/plc_debug.c	Tue Oct 27 16:32:54 2009 +0100
+++ b/targets/plc_debug.c	Mon Nov 02 15:38:49 2009 +0100
@@ -51,17 +51,17 @@
 
 struct_plcvar variable_table[%(variables_pointer_type_table_count)d];
 
-void __init_debug()
+void __init_debug(void)
 {
 %(variables_pointer_type_table_initializer)s
     buffer_state = BUFFER_FREE;
 }
 
-void __cleanup_debug()
+void __cleanup_debug(void)
 {
 }
 
-void __retrieve_debug()
+void __retrieve_debug(void)
 {
 }
 
@@ -71,7 +71,7 @@
 extern void InitiateDebugTransfer(void);
 
 extern unsigned long __tick;
-void __publish_debug()
+void __publish_debug(void)
 {
     /* Check there is no running debugger re-configuration */
     if(TryEnterDebugSection()){
@@ -143,10 +143,11 @@
     latest_subscription = subscription_table;
 }
 
-void FreeDebugData()
+void FreeDebugData(void)
 {
     /* atomically mark buffer as free */
-    long latest_state = AtomicCompareExchange(
+    long latest_state;
+    latest_state = AtomicCompareExchange(
         &buffer_state,
         BUFFER_BUSY,
         BUFFER_FREE);
@@ -169,8 +170,8 @@
         {
             return old_cursor;
         }else{
-            printf("%%d > %%d\n", old_cursor - debug_buffer, BUFFER_SIZE);
-	    return NULL;
+            //printf("%%d > %%d\n", old_cursor - debug_buffer, BUFFER_SIZE);
+            return NULL;
         } 
     }
     *idx = -1;
--- a/targets/toolchain_gcc.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/targets/toolchain_gcc.py	Mon Nov 02 15:38:49 2009 +0100
@@ -12,11 +12,8 @@
     """
     def __init__(self, PluginsRootInstance):
         self.PluginsRootInstance = PluginsRootInstance
-        self.exe = PluginsRootInstance.GetProjectName() + self.extension
-        self.buildpath = PluginsRootInstance._getBuildPath()
-        self.exe_path = os.path.join(self.buildpath, self.exe)
-        self.md5key = None
-        self.srcmd5 = {}
+        self.buildpath = None
+        self.SetBuildPath(self.PluginsRootInstance._getBuildPath())
 
     def getTarget(self):
         target = self.PluginsRootInstance.BeremizRoot.getTargetType()
@@ -54,7 +51,15 @@
                 return open(self._GetMD5FileName(), "r").read()
             except Exception, e:
                 return None
-
+    
+    def SetBuildPath(self, buildpath):
+        if self.buildpath != buildpath:
+            self.buildpath = buildpath
+            self.exe = self.PluginsRootInstance.GetProjectName() + self.extension
+            self.exe_path = os.path.join(self.buildpath, self.exe)
+            self.md5key = None
+            self.srcmd5 = {}
+    
     def check_and_update_hash_and_deps(self, bn):
         # Get latest computed hash and deps
         oldhash, deps = self.srcmd5.get(bn,(None,[]))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/toolchain_makefile.py	Mon Nov 02 15:38:49 2009 +0100
@@ -0,0 +1,42 @@
+import os, re, operator
+from wxPopen import ProcessLogger
+import hashlib, shutil
+from toolchain_gcc import toolchain_gcc
+
+includes_re =  re.compile('\s*#include\s*["<]([^">]*)[">].*')
+
+class toolchain_makefile(toolchain_gcc):
+    """
+    This abstract class contains GCC specific code.
+    It cannot be used as this and should be inherited in a target specific
+    class such as target_linux or target_win32
+    """
+
+    def build(self):
+        srcfiles= []
+        cflags = []
+        for Location, CFilesAndCFLAGS, DoCalls in self.PluginsRootInstance.LocationCFilesAndCFLAGS:
+            # Get CFiles list to give it to makefile 
+            for CFile, CFLAGS in CFilesAndCFLAGS:
+                CFileName = os.path.basename(CFile)
+                srcfiles.append(CFileName)
+                if CFLAGS not in cflags:
+                    cflags.append(CFLAGS)
+                    
+        beremizcommand = {"src": ' '.join(srcfiles),
+                          "cflags": ' '.join(cflags)
+                         }
+        
+        target = self.getTarget().getcontent()["value"]
+        command = target.getCommand().split(' ') +\
+                  [target.getBuildPath()] +\
+                  [arg % beremizcommand for arg in target.getArguments().split(' ')] +\
+                  target.getRule().split(' ')
+        
+        # Call Makefile to build PLC code and link it with target specific code
+        status, result, err_result = ProcessLogger(self.PluginsRootInstance.logger,
+                                                   command).spin()
+        if status :
+            self.PluginsRootInstance.logger.write_error(_("C compilation of %s failed.\n"))
+            return False
+        return True
--- a/wxPopen.py	Tue Oct 27 16:32:54 2009 +0100
+++ b/wxPopen.py	Mon Nov 02 15:38:49 2009 +0100
@@ -69,16 +69,20 @@
 class ProcessLogger:
     def __init__(self, logger, Command, finish_callback=None, no_stdout=False, no_stderr=False, no_gui=True):
         self.logger = logger
-        self.Command_str = Command
-        self.Command = []
-        for i,word in enumerate(Command.replace("'",'"').split('"')):
-            if i % 2 == 0:
-                word = word.strip()
-                if len(word) > 0:
-                    self.Command.extend(word.split())
-            else:
-                self.Command.append(word)
-
+        if not isinstance(Command, list):
+            self.Command_str = Command
+            self.Command = []
+            for i,word in enumerate(Command.replace("'",'"').split('"')):
+                if i % 2 == 0:
+                    word = word.strip()
+                    if len(word) > 0:
+                        self.Command.extend(word.split())
+                else:
+                    self.Command.append(word)
+        else:
+            self.Command = Command
+            self.Command_str = subprocess.list2cmdline(self.Command)
+            
         self.finish_callback = finish_callback
         self.no_stdout = no_stdout
         self.no_stderr = no_stderr
@@ -95,6 +99,7 @@
                "stdin":subprocess.PIPE, 
                "stdout":subprocess.PIPE, 
                "stderr":subprocess.PIPE}
+        
         if no_gui == True and wx.Platform == '__WXMSW__':
             self.startupinfo = subprocess.STARTUPINFO()
             self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW