merge Beremiz launcher support
authorAndrey Skvortsov <andrej.skvortzov@gmail.com>
Fri, 10 Mar 2017 19:17:43 +0300
changeset 1664 9a91faac4d95
parent 1663 81f6781f7fec (diff)
parent 1660 ccf2bcf66c70 (current diff)
child 1665 081f4463b37d
merge Beremiz launcher support

Closes #13
--- a/Beremiz.py	Fri Mar 10 18:42:24 2017 +0300
+++ b/Beremiz.py	Fri Mar 10 19:17:43 2017 +0300
@@ -4,7 +4,7 @@
 # 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
+# Copyright (C) 2016 - 2017: Andrey Skvortsov <andrej.skvortzov@gmail.com>
 #
 # See COPYING file for copyrights details.
 #
@@ -22,1273 +22,166 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-updateinfo_url = None
+
 
 import os, sys, getopt
+import time
 import __builtin__
-import tempfile
-import shutil
-import random
-import time
-import version
-from types import ListType
 
-beremiz_dir = os.path.dirname(os.path.realpath(__file__))
+class BeremizIDELauncher:
+    def __init__(self):
+        self.updateinfo_url = None
+        self.extensions = []
+        self.app_dir = os.path.dirname(os.path.realpath(__file__))
+        self.projectOpen = None
+        self.buildpath = None
+        self.splash = None
+        self.splashPath = self.Bpath("images", "splash.png")
+
+    def Bpath(self, *args):
+        return os.path.join(self.app_dir,*args)
+
+    def ShowSplashScreen(self):
+        from wx.lib.agw.advancedsplash import AdvancedSplash
+        bmp = wx.Image(self.splashPath).ConvertToBitmap()
+        self.splash = AdvancedSplash(None, bitmap=bmp)
+
+        # process all events
+        # even the events generated by splash themself during showing
+        if wx.Platform == '__WXMSW__':
+            self.splash.Show()
+            self.splash.ProcessEvent(wx.PaintEvent())
+        else:
+            for i in range(0,30):
+                wx.Yield()
+                time.sleep(0.01);
+
+
+    def Usage(self):
+        print "\nUsage:"
+        print "\n   %s [Projectpath] [Buildpath]\n"%sys.argv[0]
+
+    def SetCmdOptions(self):
+        self.shortCmdOpts = "hu:e:"
+        self.longCmdOpts = ["help", "updatecheck=", "extend="]
+
+    def ProcessOption(self, o, a):
+        if o in ("-h", "--help"):
+            self.Usage()
+            sys.exit()
+        if o in ("-u", "--updatecheck"):
+            self.updateinfo_url = a
+        if o in ("-e", "--extend"):
+            self.extensions.append(a)
+
+    def ProcessCommandLineArgs(self):
+        self.SetCmdOptions()
+        try:
+            opts, args = getopt.getopt(sys.argv[1:], self.shortCmdOpts, self.longCmdOpts)
+        except getopt.GetoptError:
+            # print help information and exit:
+            self.Usage()
+            sys.exit(2)
+
+        for o, a in opts:
+            self.ProcessOption(o, a)
+
+        if len(args) > 2:
+            self.Usage()
+            sys.exit()
+
+        elif len(args) == 1:
+            self.projectOpen = args[0]
+            self.buildpath = None
+        elif len(args) == 2:
+            self.projectOpen = args[0]
+            self.buildpath = args[1]
+
+    def CreateApplication(self):
+        if os.path.exists("BEREMIZ_DEBUG"):
+            __builtin__.__dict__["BMZ_DBG"] = True
+        else :
+            __builtin__.__dict__["BMZ_DBG"] = False
+
+        global wxversion, wx
+        import wxversion
+        wxversion.select(['2.8', '3.0'])
+        import wx
+
+        if wx.VERSION >= (3, 0, 0):
+            self.app = wx.App(redirect=BMZ_DBG)
+        else:
+            self.app = wx.PySimpleApp(redirect=BMZ_DBG)
+
+        self.app.SetAppName('beremiz')
+        if wx.VERSION < (3, 0, 0):
+            wx.InitAllImageHandlers()
+
+        self.ShowSplashScreen()
+        self.BackgroundInitialization()
+        self.app.MainLoop()
+
+    def BackgroundInitialization(self):
+        self.InitI18n()
+        self.CheckUpdates()
+        self.LoadExtensions()
+        self.ImportModules()
+        self.InstallExceptionHandler()
+        self.ShowUI()
+
+    def InitI18n(self):
+        from util.misc import InstallLocalRessources
+        InstallLocalRessources(self.app_dir)
+
+    def LoadExtensions(self):
+        for extfilename in self.extensions:
+            from util.TranslationCatalogs import AddCatalog
+            from util.BitmapLibrary import AddBitmapFolder
+            extension_folder = os.path.split(os.path.realpath(extfilename))[0]
+            sys.path.append(extension_folder)
+            AddCatalog(os.path.join(extension_folder, "locale"))
+            AddBitmapFolder(os.path.join(extension_folder, "images"))
+            execfile(extfilename, locals())
+
+    def CheckUpdates(self):
+        if self.updateinfo_url is not None:
+            updateinfo = _("Fetching %s") % self.updateinfo_url
+
+            def updateinfoproc():
+                global updateinfo
+                try :
+                    import urllib2
+                    updateinfo = urllib2.urlopen(self.updateinfo_url,None).read()
+                except :
+                    updateinfo = _("update info unavailable.")
+
+            from threading import Thread
+            self.splash.SetText(text=updateinfo)
+            updateinfoThread = Thread(target=updateinfoproc)
+            updateinfoThread.start()
+            updateinfoThread.join(2)
+            self.splash.SetText(text=updateinfo)
+
+    def ImportModules(self):
+        global BeremizIDE
+        import BeremizIDE
+
+    def InstallExceptionHandler(self):
+        import version
+        import tempfile
+        logpath = tempfile.gettempdir()+os.sep+'Beremiz'
+        BeremizIDE.AddExceptHook(logpath,version.app_version)
+
+    def ShowUI(self):
+        self.frame = BeremizIDE.Beremiz(None, self.projectOpen, self.buildpath)
+        if self.splash:
+            self.splash.Close()
+        self.frame.Show()
+
+    def Start(self):
+        self.ProcessCommandLineArgs()
+        self.CreateApplication()
 
 if __name__ == '__main__':
-    import wxversion
-    wxversion.select(['2.8', '3.0'])
-    import wx
-    
-from wx.lib.agw.advancedsplash import AdvancedSplash
-
-def Bpath(*args):
-    return os.path.join(beremiz_dir,*args)
-
-def ShowSplashScreen():
-    bmp = wx.Image(Bpath("images", "splash.png")).ConvertToBitmap()
-    #splash=AdvancedSplash(None, bitmap=bmp, style=wx.SPLASH_CENTRE_ON_SCREEN, timeout=4000)
-    splash = AdvancedSplash(None, bitmap=bmp)
-
-    # process all events
-    # even the events generated by splash themself during showing
-    for i in range(0,30):
-        wx.Yield()
-        time.sleep(0.01);
-    return splash
-
-if __name__ == '__main__':
-    def usage():
-        print "\nUsage of Beremiz.py :"
-        print "\n   %s [Projectpath] [Buildpath]\n"%sys.argv[0]
-
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], "hu:e:", ["help", "updatecheck=", "extend="])
-    except getopt.GetoptError:
-        # print help information and exit:
-        usage()
-        sys.exit(2)
-
-    extensions=[]
-
-    for o, a in opts:
-        if o in ("-h", "--help"):
-            usage()
-            sys.exit()
-        if o in ("-u", "--updatecheck"):
-            updateinfo_url = a
-        if o in ("-e", "--extend"):
-            extensions.append(a)
-
-    if len(args) > 2:
-        usage()
-        sys.exit()
-    elif len(args) == 1:
-        projectOpen = args[0]
-        buildpath = None
-    elif len(args) == 2:
-        projectOpen = args[0]
-        buildpath = args[1]
-    else:
-        projectOpen = None
-        buildpath = None
-
-    if os.path.exists("BEREMIZ_DEBUG"):
-        __builtin__.__dict__["BMZ_DBG"] = True
-    else :
-        __builtin__.__dict__["BMZ_DBG"] = False
-
-    if wx.VERSION >= (3, 0, 0):
-        app = wx.App(redirect=BMZ_DBG)
-    else:
-        app = wx.PySimpleApp(redirect=BMZ_DBG)
-
-    app.SetAppName('beremiz')
-    if wx.VERSION < (3, 0, 0):
-        wx.InitAllImageHandlers()
-
-    # popup splash
-    splash = ShowSplashScreen()
-
-    # load internatialization files
-    from util.misc import InstallLocalRessources
-    InstallLocalRessources(beremiz_dir)
-    
-    if updateinfo_url is not None:
-        updateinfo = _("Fetching %s") % updateinfo_url
-        # warn for possible updates
-        def updateinfoproc():
-            global updateinfo
-            try :
-                import urllib2
-                updateinfo = urllib2.urlopen(updateinfo_url,None).read()
-            except :
-                updateinfo = _("update info unavailable.")
-
-        from threading import Thread
-        splash.SetText(text=updateinfo)
-        wx.Yield()
-        updateinfoThread = Thread(target=updateinfoproc)
-        updateinfoThread.start()
-        updateinfoThread.join(2)
-        splash.SetText(text=updateinfo)
-        wx.Yield()
-
-    # Load extensions
-    for extfilename in extensions:
-        from util.TranslationCatalogs import AddCatalog
-        from util.BitmapLibrary import AddBitmapFolder
-        extension_folder = os.path.split(os.path.realpath(extfilename))[0]
-        sys.path.append(extension_folder)
-        AddCatalog(os.path.join(extension_folder, "locale"))
-        AddBitmapFolder(os.path.join(extension_folder, "images"))
-        execfile(extfilename, locals())
-
-
-import wx.lib.buttons, wx.lib.statbmp, wx.stc
-import cPickle
-import types, time, re, platform, time, traceback, commands
-
-from docutil import OpenHtmlFrame
-from editors.EditorPanel import EditorPanel
-from editors.Viewer import Viewer
-from editors.TextViewer import TextViewer
-from editors.ResourceEditor import ConfigurationEditor, ResourceEditor
-from editors.DataTypeEditor import DataTypeEditor
-from util.MiniTextControler import MiniTextControler
-from util.ProcessLogger import ProcessLogger
-from controls.LogViewer import LogViewer
-from controls.CustomStyledTextCtrl import CustomStyledTextCtrl
-from controls import EnhancedStatusBar as esb
-from dialogs.AboutDialog import ShowAboutDialog
-
-from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY, ITEM_PROJECT, ITEM_RESOURCE
-from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE
-
-
-MAX_RECENT_PROJECTS = 9
-
-if wx.Platform == '__WXMSW__':
-    faces = {
-        'mono' : 'Courier New',
-        'size' : 8,
-    }
-else:
-    faces = {
-        'mono' : 'Courier',
-        'size' : 10,
-    }
-
-from threading import Lock,Timer,currentThread
-MainThread = currentThread().ident
-REFRESH_PERIOD = 0.1
-from time import time as gettime
-class LogPseudoFile:
-    """ Base class for file like objects to facilitate StdOut for the Shell."""
-    def __init__(self, output, risecall):
-        self.red_white = 1
-        self.red_yellow = 2
-        self.black_white = wx.stc.STC_STYLE_DEFAULT
-        self.output = output
-        self.risecall = risecall
-        # to prevent rapid fire on rising log panel
-        self.rising_timer = 0
-        self.lock = Lock()
-        self.YieldLock = Lock()
-        self.RefreshLock = Lock()
-        self.TimerAccessLock = Lock()
-        self.stack = []
-        self.LastRefreshTime = gettime()
-        self.LastRefreshTimer = None
-
-    def write(self, s, style = None):
-        if self.lock.acquire():
-            self.stack.append((s,style))
-            self.lock.release()
-            current_time = gettime()
-            self.TimerAccessLock.acquire()
-            if self.LastRefreshTimer:
-                self.LastRefreshTimer.cancel()
-                self.LastRefreshTimer=None
-            self.TimerAccessLock.release()
-            if current_time - self.LastRefreshTime > REFRESH_PERIOD and self.RefreshLock.acquire(False):
-                self._should_write()
-            else:
-                self.TimerAccessLock.acquire()
-                self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired)
-                self.LastRefreshTimer.start()
-                self.TimerAccessLock.release()
-
-    def _timer_expired(self):
-        if self.RefreshLock.acquire(False):
-            self._should_write()
-        else:
-            self.TimerAccessLock.acquire()
-            self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired)
-            self.LastRefreshTimer.start()
-            self.TimerAccessLock.release()
-
-    def _should_write(self):
-        wx.CallAfter(self._write)
-        if MainThread == currentThread().ident:
-            app = wx.GetApp()
-            if app is not None:
-                if self.YieldLock.acquire(0):
-                    app.Yield()
-                    self.YieldLock.release()
-
-    def _write(self):
-        if self.output :
-            self.output.Freeze()
-            self.lock.acquire()
-            for s, style in self.stack:
-                if style is None : style=self.black_white
-                if style != self.black_white:
-                    self.output.StartStyling(self.output.GetLength(), 0xff)
-
-                # Temporary deactivate read only mode on StyledTextCtrl for
-                # adding text. It seems that text modifications, even
-                # programmatically, are disabled in StyledTextCtrl when read
-                # only is active
-                start_pos = self.output.GetLength()
-                self.output.SetReadOnly(False)
-                self.output.AppendText(s)
-                self.output.SetReadOnly(True)
-                text_len = self.output.GetLength() - start_pos
-
-                if style != self.black_white:
-                    self.output.SetStyling(text_len, style)
-            self.stack = []
-            self.lock.release()
-            self.output.Thaw()
-            self.LastRefreshTime = gettime()
-            try:
-                self.RefreshLock.release()
-            except:
-                pass
-            newtime = time.time()
-            if newtime - self.rising_timer > 1:
-                self.risecall(self.output)
-            self.rising_timer = newtime
-
-    def write_warning(self, s):
-        self.write(s,self.red_white)
-
-    def write_error(self, s):
-        self.write(s,self.red_yellow)
-
-    def writeyield(self, s):
-        self.write(s)
-        wx.GetApp().Yield()
-
-    def flush(self):
-        # Temporary deactivate read only mode on StyledTextCtrl for clearing
-        # text. It seems that text modifications, even programmatically, are
-        # disabled in StyledTextCtrl when read only is active
-        self.output.SetReadOnly(False)
-        self.output.SetText("")
-        self.output.SetReadOnly(True)
-
-    def isatty(self):
-        return False
-
-ID_FILEMENURECENTPROJECTS = wx.NewId()
-
-from IDEFrame import TITLE,\
-                     EDITORTOOLBAR,\
-                     FILEMENU,\
-                     EDITMENU,\
-                     DISPLAYMENU,\
-                     PROJECTTREE,\
-                     POUINSTANCEVARIABLESPANEL,\
-                     LIBRARYTREE,\
-                     SCALING,\
-                     PAGETITLES,\
-                     IDEFrame, AppendMenu,\
-                     EncodeFileSystemPath, DecodeFileSystemPath
-from util.BitmapLibrary import GetBitmap
-
-class Beremiz(IDEFrame):
-
-    def _init_utils(self):
-        self.ConfNodeMenu = wx.Menu(title='')
-        self.RecentProjectsMenu = wx.Menu(title='')
-
-        IDEFrame._init_utils(self)
-
-    def _init_coll_FileMenu_Items(self, parent):
-        AppendMenu(parent, help='', id=wx.ID_NEW,
-              kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N')
-        AppendMenu(parent, help='', id=wx.ID_OPEN,
-              kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O')
-        parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu)
-        parent.AppendSeparator()
-        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,
-              kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W')
-        parent.AppendSeparator()
-        AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
-              kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P')
-        AppendMenu(parent, help='', id=wx.ID_PREVIEW,
-              kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P')
-        AppendMenu(parent, help='', id=wx.ID_PRINT,
-              kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P')
-        parent.AppendSeparator()
-        AppendMenu(parent, help='', id=wx.ID_EXIT,
-              kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q')
-
-        self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
-        self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
-        self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
-        self.Bind(wx.EVT_MENU, self.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)
-        self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
-        self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
-        self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
-
-        self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None),
-                               (wx.ID_OPEN, "open", _(u'Open'), None),
-                               (wx.ID_SAVE, "save", _(u'Save'), None),
-                               (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None),
-                               (wx.ID_PRINT, "print", _(u'Print'), None)])
-
-    def _RecursiveAddMenuItems(self, menu, items):
-        for name, text, help, children in items:
-            new_id = wx.NewId()
-            if len(children) > 0:
-                new_menu = wx.Menu(title='')
-                menu.AppendMenu(new_id, text, new_menu)
-                self._RecursiveAddMenuItems(new_menu, children)
-            else:
-                AppendMenu(menu, help=help, id=new_id,
-                       kind=wx.ITEM_NORMAL, text=text)
-                self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name),
-                          id=new_id)
-
-    def _init_coll_AddMenu_Items(self, parent):
-        IDEFrame._init_coll_AddMenu_Items(self, parent, False)
-        self._RecursiveAddMenuItems(parent, GetAddMenuItems())
-
-    def _init_coll_HelpMenu_Items(self, parent):
-        parent.Append(help='', id=wx.ID_ABOUT,
-              kind=wx.ITEM_NORMAL, text=_(u'About'))
-        self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
-
-    def _init_coll_ConnectionStatusBar_Fields(self, parent):
-        parent.SetFieldsCount(3)
-
-        parent.SetStatusText(number=0, text='')
-        parent.SetStatusText(number=1, text='')
-        parent.SetStatusText(number=2, text='')
-
-        parent.SetStatusWidths([-1, 300, 200])
-
-    def _init_ctrls(self, prnt):
-        IDEFrame._init_ctrls(self, prnt)
-
-        self.EditMenuSize = self.EditMenu.GetMenuItemCount()
-
-        inspectorID = wx.NewId()
-        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID)
-        accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)]
-
-        keyID = wx.NewId()
-        self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=keyID)
-        accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F12, keyID)]
-
-        for method,shortcut in [("Stop",     wx.WXK_F4),
-                                ("Run",      wx.WXK_F5),
-                                ("Transfer", wx.WXK_F6),
-                                ("Connect",  wx.WXK_F7),
-                                ("Build",    wx.WXK_F11)]:
-            def OnMethodGen(obj,meth):
-                def OnMethod(evt):
-                    if obj.CTR is not None:
-                       obj.CTR.CallMethod('_'+meth)
-                    wx.CallAfter(self.RefreshStatusToolBar)
-                return OnMethod
-            newid = wx.NewId()
-            self.Bind(wx.EVT_MENU, OnMethodGen(self,method), id=newid)
-            accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut,newid)]
-
-        self.SetAcceleratorTable(wx.AcceleratorTable(accels))
-
-        self.LogConsole = CustomStyledTextCtrl(
-                  name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
-                  size=wx.Size(0, 0))
-        self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged)
-        self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged)
-        self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI)
-        self.LogConsole.SetReadOnly(True)
-        self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR)
-
-        # Define Log Console styles
-        self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
-        self.LogConsole.StyleClearAll()
-        self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces)
-        self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)
-
-        # Define Log Console markers
-        self.LogConsole.SetMarginSensitive(1, True)
-        self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
-        self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED")
-
-        self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT)
-
-        self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick)
-        self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified)
-
-        self.MainTabs["LogConsole"] = (self.LogConsole, _("Console"))
-        self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"])
-        #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT)
-
-        self.LogViewer = LogViewer(self.BottomNoteBook, self)
-        self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log"))
-        self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"])
-        #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT)
-
-        StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize,
-                wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
-        StatusToolBar.SetToolBitmapSize(wx.Size(25, 25))
-        StatusToolBar.Realize()
-        self.Panes["StatusToolBar"] = StatusToolBar
-        self.AUIManager.AddPane(StatusToolBar, wx.aui.AuiPaneInfo().
-                  Name("StatusToolBar").Caption(_("Status ToolBar")).
-                  ToolbarPane().Top().Position(1).
-                  LeftDockable(False).RightDockable(False))
-
-        self.AUIManager.Update()
-
-        self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP)
-        self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar)
-        self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range = 100)
-        self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2)        
-        self.ProgressStatusBar.Hide()
-        self.SetStatusBar(self.ConnectionStatusBar)
-
-    def __init_execute_path(self):
-        if os.name == 'nt':
-            # on windows, desktop shortcut launches Beremiz.py
-            # with working dir set to mingw/bin.
-            # then we prefix CWD to PATH in order to ensure that
-            # commands invoked by build process by default are
-            # found here.
-            os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"]
-        
-        
-    def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True):
-        # Add beremiz's icon in top left corner of the frame
-        self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO)
-        self.__init_execute_path()
-        
-        IDEFrame.__init__(self, parent, debug)
-        self.Log = LogPseudoFile(self.LogConsole,self.SelectTab)
-
-        self.local_runtime = None
-        self.runtime_port = None
-        self.local_runtime_tmpdir = None
-
-        self.LastPanelSelected = None
-
-        # Define Tree item icon list
-        self.LocationImageList = wx.ImageList(16, 16)
-        self.LocationImageDict = {}
-
-        # Icons for location items
-        for imgname, itemtype in [
-            ("CONFIGURATION", LOCATION_CONFNODE),
-            ("RESOURCE",      LOCATION_MODULE),
-            ("PROGRAM",       LOCATION_GROUP),
-            ("VAR_INPUT",     LOCATION_VAR_INPUT),
-            ("VAR_OUTPUT",    LOCATION_VAR_OUTPUT),
-            ("VAR_LOCAL",     LOCATION_VAR_MEMORY)]:
-            self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname))
-
-        # Icons for other items
-        for imgname, itemtype in [
-            ("Extension", ITEM_CONFNODE)]:
-            self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))
-
-        if projectOpen is not None:
-            projectOpen = DecodeFileSystemPath(projectOpen, False)
-
-        if projectOpen is not None and os.path.isdir(projectOpen):
-            self.CTR = ProjectController(self, self.Log)
-            self.Controler = self.CTR
-            result, err = self.CTR.LoadProject(projectOpen, buildpath)
-            if not result:
-                self.LibraryPanel.SetController(self.Controler)
-                self.ProjectTree.Enable(True)
-                self.PouInstanceVariablesPanel.SetController(self.Controler)
-                self.RefreshConfigRecentProjects(os.path.abspath(projectOpen))
-                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
-            else:
-                self.ResetView()
-                self.ShowErrorMessage(result)
-        else:
-            self.CTR = ctr
-            self.Controler = ctr
-            if ctr is not None:
-                self.LibraryPanel.SetController(self.Controler)
-                self.ProjectTree.Enable(True)
-                self.PouInstanceVariablesPanel.SetController(self.Controler)
-                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
-        if self.EnableDebug:
-            self.DebugVariablePanel.SetDataProducer(self.CTR)
-
-        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
-
-        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
-        self.RefreshAll()
-        self.LogConsole.SetFocus()
-
-    def RefreshTitle(self):
-        name = _("Beremiz")
-        if self.CTR is not None:
-            projectname = self.CTR.GetProjectName()
-            if self.CTR.ProjectTestModified():
-                projectname = "~%s~" % projectname
-            self.SetTitle("%s - %s" % (name, projectname))
-        else:
-            self.SetTitle(name)
-
-    def StartLocalRuntime(self, taskbaricon = True):
-        if (self.local_runtime is None) or (self.local_runtime.exitcode is not None):
-            # create temporary directory for runtime working directory
-            self.local_runtime_tmpdir = tempfile.mkdtemp()
-            # choose an arbitrary random port for runtime
-            self.runtime_port = int(random.random() * 1000) + 61131
-            # launch local runtime
-            self.local_runtime = ProcessLogger(self.Log,
-                "\"%s\" \"%s\" -p %s -i localhost %s %s"%(
-                    sys.executable,
-                    Bpath("Beremiz_service.py"),
-                    self.runtime_port,
-                    {False : "-x 0", True :"-x 1"}[taskbaricon],
-                    self.local_runtime_tmpdir),
-                no_gui=False,
-                timeout=500, keyword = self.local_runtime_tmpdir,
-                cwd = self.local_runtime_tmpdir)
-            self.local_runtime.spin()
-        return self.runtime_port
-
-    def KillLocalRuntime(self):
-        if self.local_runtime is not None:
-            # shutdown local runtime
-            self.local_runtime.kill(gently=False)
-            # clear temp dir
-            shutil.rmtree(self.local_runtime_tmpdir)
-
-            self.local_runtime = None
-
-    def OnOpenWidgetInspector(self, evt):
-        # Activate the widget inspection tool
-        from wx.lib.inspection import InspectionTool
-        if not InspectionTool().initialized:
-            InspectionTool().Init()
-
-        # Find a widget to be selected in the tree.  Use either the
-        # one under the cursor, if any, or this frame.
-        wnd = wx.FindWindowAtPointer()
-        if not wnd:
-            wnd = self
-        InspectionTool().Show(wnd, True)
-
-    def OnLogConsoleFocusChanged(self, event):
-        self.RefreshEditMenu()
-        event.Skip()
-
-    def OnLogConsoleUpdateUI(self, event):
-        self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True)
-        event.Skip()
-
-    def OnLogConsoleMarginClick(self, event):
-        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
-        wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx))
-        event.Skip()
-
-    def OnLogConsoleModified(self, event):
-        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
-        line = self.LogConsole.GetLine(line_idx)
-        if line:
-            result = MATIEC_ERROR_MODEL.match(line)
-            if result is not None:
-                self.LogConsole.MarkerAdd(line_idx, 0)
-        event.Skip()
-
-    def SearchLineForError(self, line):
-        if self.CTR is not None:
-            result = MATIEC_ERROR_MODEL.match(line)
-            if result is not None:
-                first_line, first_column, last_line, last_column, error = result.groups()
-                infos = self.CTR.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.CTR.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.CTR.SaveProject()
-            elif answer == wx.ID_CANCEL:
-                return False
-
-        for idx in xrange(self.TabsOpened.GetPageCount()):
-            window = self.TabsOpened.GetPage(idx)
-            if not window.CheckSaveBeforeClosing():
-                return False
-
-        return True
-
-    def GetTabInfos(self, tab):
-        if (isinstance(tab, EditorPanel) and
-            not isinstance(tab, (Viewer,
-                                 TextViewer,
-                                 ResourceEditor,
-                                 ConfigurationEditor,
-                                 DataTypeEditor))):
-            return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName())
-        elif (isinstance(tab, TextViewer) and
-              (tab.Controler is None or isinstance(tab.Controler, MiniTextControler))):
-            return ("confnode", None, tab.GetInstancePath())
-        else:
-            return IDEFrame.GetTabInfos(self, tab)
-
-    def LoadTab(self, notebook, page_infos):
-        if page_infos[0] == "confnode":
-            if page_infos[1] is None:
-                confnode = self.CTR
-            else:
-                confnode = self.CTR.GetChildByName(page_infos[1])
-            return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:]))
-        else:
-            return IDEFrame.LoadTab(self, notebook, page_infos)
-
-    # Strange hack required by WAMP connector, using twisted.
-    # Twisted reactor needs to be stopped only before quit,
-    # since it cannot be restarted
-    ToDoBeforeQuit = []
-    def AddToDoBeforeQuit(self, Thing):
-        self.ToDoBeforeQuit.append(Thing)
-
-    def OnCloseFrame(self, event):
-        for evt_type in [wx.EVT_SET_FOCUS,
-                         wx.EVT_KILL_FOCUS,
-                         wx.stc.EVT_STC_UPDATEUI]:
-            self.LogConsole.Unbind(evt_type)
-        if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")):
-            if self.CTR is not None:
-                self.CTR.KillDebugThread()
-            self.KillLocalRuntime()
-
-            self.SaveLastState()
-
-            for Thing in self.ToDoBeforeQuit :
-                Thing()
-            self.ToDoBeforeQuit = []
-
-            event.Skip()
-        else:
-            event.Veto()
-
-    def RefreshFileMenu(self):
-        self.RefreshRecentProjectsMenu()
-
-        MenuToolBar = self.Panes["MenuToolBar"]
-        if self.CTR is not None:
-            selected = self.TabsOpened.GetSelection()
-            if selected >= 0:
-                window = self.TabsOpened.GetPage(selected)
-                viewer_is_modified = window.IsModified()
-                is_viewer = isinstance(window, Viewer)
-            else:
-                viewer_is_modified = is_viewer = False
-            if self.TabsOpened.GetPageCount() > 0:
-                self.FileMenu.Enable(wx.ID_CLOSE, True)
-                if is_viewer:
-                    self.FileMenu.Enable(wx.ID_PREVIEW, True)
-                    self.FileMenu.Enable(wx.ID_PRINT, True)
-                    MenuToolBar.EnableTool(wx.ID_PRINT, True)
-                else:
-                    self.FileMenu.Enable(wx.ID_PREVIEW, False)
-                    self.FileMenu.Enable(wx.ID_PRINT, False)
-                    MenuToolBar.EnableTool(wx.ID_PRINT, False)
-            else:
-                self.FileMenu.Enable(wx.ID_CLOSE, False)
-                self.FileMenu.Enable(wx.ID_PREVIEW, False)
-                self.FileMenu.Enable(wx.ID_PRINT, False)
-                MenuToolBar.EnableTool(wx.ID_PRINT, False)
-            self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
-            project_modified = self.CTR.ProjectTestModified() or viewer_is_modified
-            self.FileMenu.Enable(wx.ID_SAVE, project_modified)
-            MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
-            self.FileMenu.Enable(wx.ID_SAVEAS, True)
-            MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
-            self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
-        else:
-            self.FileMenu.Enable(wx.ID_CLOSE, False)
-            self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
-            self.FileMenu.Enable(wx.ID_PREVIEW, False)
-            self.FileMenu.Enable(wx.ID_PRINT, False)
-            MenuToolBar.EnableTool(wx.ID_PRINT, False)
-            self.FileMenu.Enable(wx.ID_SAVE, False)
-            MenuToolBar.EnableTool(wx.ID_SAVE, False)
-            self.FileMenu.Enable(wx.ID_SAVEAS, False)
-            MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
-            self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
-
-    def RefreshRecentProjectsMenu(self):
-        try:
-            recent_projects = map(DecodeFileSystemPath,
-                                  self.GetConfigEntry("RecentProjects", []))
-        except:
-            recent_projects = []
-
-        while self.RecentProjectsMenu.GetMenuItemCount() > len(recent_projects):
-            item = self.RecentProjectsMenu.FindItemByPosition(0)
-            self.RecentProjectsMenu.RemoveItem(item)
-
-        self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0)
-        for idx, projectpath in enumerate(recent_projects):
-            text = u'&%d: %s' % (idx + 1, projectpath)
-
-            if idx < self.RecentProjectsMenu.GetMenuItemCount():
-                item = self.RecentProjectsMenu.FindItemByPosition(idx)
-                id = item.GetId()
-                item.SetItemLabel(text)
-                self.Disconnect(id, id, wx.EVT_BUTTON._getEvtType())
-            else:
-                id = wx.NewId()
-                AppendMenu(self.RecentProjectsMenu, help='', id=id,
-                           kind=wx.ITEM_NORMAL, text=text)
-            self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), id=id)
-
-    def GenerateOpenRecentProjectFunction(self, projectpath):
-        def OpenRecentProject(event):
-            if self.CTR is not None and not self.CheckSaveBeforeClosing():
-                return
-
-            self.OpenProject(projectpath)
-        return OpenRecentProject
-
-    def GenerateMenuRecursive(self, items, menu):
-        for kind, infos in items:
-            if isinstance(kind, ListType):
-                text, id = infos
-                submenu = wx.Menu('')
-                self.GenerateMenuRecursive(kind, submenu)
-                menu.AppendMenu(id, text, submenu)
-            elif kind == wx.ITEM_SEPARATOR:
-                menu.AppendSeparator()
-            else:
-                text, id, help, callback = infos
-                AppendMenu(menu, help='', id=id, kind=kind, text=text)
-                if callback is not None:
-                    self.Bind(wx.EVT_MENU, callback, id=id)
-
-    def RefreshEditorToolBar(self):
-        IDEFrame.RefreshEditorToolBar(self)
-        self.AUIManager.GetPane("EditorToolBar").Position(2)
-        self.AUIManager.GetPane("StatusToolBar").Position(1)
-        self.AUIManager.Update()
-
-    def RefreshStatusToolBar(self):
-        StatusToolBar = self.Panes["StatusToolBar"]
-        StatusToolBar.ClearTools()
-
-        if self.CTR is not None:
-
-            for confnode_method in self.CTR.StatusMethods:
-                if "method" in confnode_method and confnode_method.get("shown",True):
-                    id = wx.NewId()
-                    StatusToolBar.AddSimpleTool(id,
-                        GetBitmap(confnode_method.get("bitmap", "Unknown")),
-                        confnode_method["tooltip"])
-                    self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), id=id)
-
-            StatusToolBar.Realize()
-            self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show()
-        else:
-            self.AUIManager.GetPane("StatusToolBar").Hide()
-        self.AUIManager.GetPane("EditorToolBar").Position(2)
-        self.AUIManager.GetPane("StatusToolBar").Position(1)
-        self.AUIManager.Update()
-
-    def RefreshEditMenu(self):
-        IDEFrame.RefreshEditMenu(self)
-        if self.FindFocus() == self.LogConsole:
-            self.EditMenu.Enable(wx.ID_COPY, True)
-            self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True)
-
-        if self.CTR is not None:
-            selected = self.TabsOpened.GetSelection()
-            if selected >= 0:
-                panel = self.TabsOpened.GetPage(selected)
-            else:
-                panel = None
-            if panel != self.LastPanelSelected:
-                for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()):
-                    item = self.EditMenu.FindItemByPosition(self.EditMenuSize)
-                    if item is not None:
-                        if item.IsSeparator():
-                            self.EditMenu.RemoveItem(item)
-                        else:
-                            self.EditMenu.Delete(item.GetId())
-                self.LastPanelSelected = panel
-                if panel is not None:
-                    items = panel.GetConfNodeMenuItems()
-                else:
-                    items = []
-                if len(items) > 0:
-                    self.EditMenu.AppendSeparator()
-                    self.GenerateMenuRecursive(items, self.EditMenu)
-            if panel is not None:
-                panel.RefreshConfNodeMenu(self.EditMenu)
-        else:
-            for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()):
-                item = self.EditMenu.FindItemByPosition(i)
-                if item is not None:
-                    if item.IsSeparator():
-                        self.EditMenu.RemoveItem(item)
-                    else:
-                        self.EditMenu.Delete(item.GetId())
-            self.LastPanelSelected = None
-        self.MenuBar.UpdateMenus()
-
-    def RefreshAll(self):
-        self.RefreshStatusToolBar()
-
-    def GetMenuCallBackFunction(self, method):
-        """ Generate the callbackfunc for a given CTR method"""
-        def OnMenu(event):
-            # Disable button to prevent re-entrant call
-            event.GetEventObject().Disable()
-            # Call
-            getattr(self.CTR, method)()
-            # Re-enable button
-            event.GetEventObject().Enable()
-        return OnMenu
-
-    def GetConfigEntry(self, entry_name, default):
-        return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default))))
-
-    def ResetConnectionStatusBar(self):
-        for field in xrange(self.ConnectionStatusBar.GetFieldsCount()):
-            self.ConnectionStatusBar.SetStatusText('', field)
-
-    def ResetView(self):
-        IDEFrame.ResetView(self)
-        self.ConfNodeInfos = {}
-        if self.CTR is not None:
-            self.CTR.CloseProject()
-        self.CTR = None
-        self.Log.flush()
-        if self.EnableDebug:
-            self.DebugVariablePanel.SetDataProducer(None)
-            self.ResetConnectionStatusBar()
-
-    def RefreshConfigRecentProjects(self, projectpath, err=False):
-        try:
-            recent_projects = map(DecodeFileSystemPath,
-                                  self.GetConfigEntry("RecentProjects", []))
-        except:
-            recent_projects = []
-        if projectpath in recent_projects:
-            recent_projects.remove(projectpath)
-        if not err:
-            recent_projects.insert(0, projectpath)
-        self.Config.Write("RecentProjects", cPickle.dumps(
-            map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS])))
-        self.Config.Flush()
-
-    def ResetPerspective(self):
-        IDEFrame.ResetPerspective(self)
-        self.RefreshStatusToolBar()
-
-    def OnNewProjectMenu(self, event):
-        if self.CTR is not None and not self.CheckSaveBeforeClosing():
-            return
-
-        try:
-            defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
-        except:
-            defaultpath = os.path.expanduser("~")
-
-        dialog = wx.DirDialog(self , _("Choose a project"), defaultpath)
-        if dialog.ShowModal() == wx.ID_OK:
-            projectpath = dialog.GetPath()
-            self.Config.Write("lastopenedfolder",
-                              EncodeFileSystemPath(os.path.dirname(projectpath)))
-            self.Config.Flush()
-            self.ResetView()
-            ctr = ProjectController(self, self.Log)
-            result = ctr.NewProject(projectpath)
-            if not result:
-                self.CTR = ctr
-                self.Controler = self.CTR
-                self.LibraryPanel.SetController(self.Controler)
-                self.ProjectTree.Enable(True)
-                self.PouInstanceVariablesPanel.SetController(self.Controler)
-                self.RefreshConfigRecentProjects(projectpath)
-                if self.EnableDebug:
-                    self.DebugVariablePanel.SetDataProducer(self.CTR)
-                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
-            else:
-                self.ResetView()
-                self.ShowErrorMessage(result)
-            self.RefreshAll()
-            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
-        dialog.Destroy()
-
-    def OnOpenProjectMenu(self, event):
-        if self.CTR is not None and not self.CheckSaveBeforeClosing():
-            return
-
-        try:
-            defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
-        except:
-            defaultpath = os.path.expanduser("~")
-
-        dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, style=wx.DEFAULT_DIALOG_STYLE|
-                                                                               wx.RESIZE_BORDER)
-        if dialog.ShowModal() == wx.ID_OK:
-            self.OpenProject(dialog.GetPath())
-        dialog.Destroy()
-
-    def OpenProject(self, projectpath):
-        if os.path.isdir(projectpath):
-            self.Config.Write("lastopenedfolder",
-                              EncodeFileSystemPath(os.path.dirname(projectpath)))
-            self.Config.Flush()
-            self.ResetView()
-            self.CTR = ProjectController(self, self.Log)
-            self.Controler = self.CTR
-            result, err = self.CTR.LoadProject(projectpath)
-            if not result:
-                self.LibraryPanel.SetController(self.Controler)
-                self.ProjectTree.Enable(True)
-                self.PouInstanceVariablesPanel.SetController(self.Controler)
-                if self.EnableDebug:
-                    self.DebugVariablePanel.SetDataProducer(self.CTR)
-                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
-            else:
-                self.ResetView()
-                self.ShowErrorMessage(result)
-            self.RefreshAll()
-            self.SearchResultPanel.ResetSearchResults()
-        else:
-            self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath)
-            err = True
-        self.RefreshConfigRecentProjects(projectpath, err)
-        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
-
-    def OnCloseProjectMenu(self, event):
-        if self.CTR is not None and not self.CheckSaveBeforeClosing():
-            return
-
-        self.ResetView()
-        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
-        self.RefreshAll()
-
-    def OnSaveProjectMenu(self, event):
-        selected = self.TabsOpened.GetSelection()
-        if selected != -1:
-            window = self.TabsOpened.GetPage(selected)
-            window.Save()
-        if self.CTR is not None:
-            self.CTR.SaveProject()
-            self.RefreshAll()
-            self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
-
-    def OnSaveProjectAsMenu(self, event):
-        selected = self.TabsOpened.GetSelection()
-        if selected != -1:
-            window = self.TabsOpened.GetPage(selected)
-            window.SaveAs()
-        if self.CTR is not None:
-            self.CTR.SaveProjectAs()
-            self.RefreshAll()
-            self.RefreshConfigRecentProjects(self.CTR.ProjectPath)
-            self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
-
-    def OnQuitMenu(self, event):
-        self.Close()
-
-    def OnAboutMenu(self, event):
-        info = version.GetAboutDialogInfo()        
-        ShowAboutDialog(self, info)
-
-    def OnProjectTreeItemBeginEdit(self, event):
-        selected = event.GetItem()
-        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE:
-            event.Veto()
-        else:
-            IDEFrame.OnProjectTreeItemBeginEdit(self, event)
-
-    def OnProjectTreeRightUp(self, event):
-        item = event.GetItem()
-        item_infos = self.ProjectTree.GetPyData(item)
-
-        if item_infos["type"] == ITEM_CONFNODE:
-            confnode_menu = wx.Menu(title='')
-
-            confnode = item_infos["confnode"]
-            if confnode is not None:
-                menu_items = confnode.GetContextualMenuItems()
-                if menu_items is not None:
-                    for text, help, callback in menu_items:
-                        new_id = wx.NewId()
-                        confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text)
-                        self.Bind(wx.EVT_MENU, callback, id=new_id)
-                else:
-                    for name, XSDClass, help in confnode.CTNChildrenTypes:
-                        new_id = wx.NewId()
-                        confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Add") + " " + name)
-                        self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name, confnode), id=new_id)
-
-            new_id = wx.NewId()
-            AppendMenu(confnode_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
-            self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), id=new_id)
-
-            self.PopupMenu(confnode_menu)
-            confnode_menu.Destroy()
-
-            event.Skip()
-        elif item_infos["type"] == ITEM_RESOURCE:
-            # prevent last resource to be delted
-            parent = self.ProjectTree.GetItemParent(item)
-            parent_name = self.ProjectTree.GetItemText(parent)
-            if parent_name == _("Resources"):
-                IDEFrame.OnProjectTreeRightUp(self, event)
-        else:
-            IDEFrame.OnProjectTreeRightUp(self, event)
-
-    def OnProjectTreeItemActivated(self, event):
-        selected = event.GetItem()
-        name = self.ProjectTree.GetItemText(selected)
-        item_infos = self.ProjectTree.GetPyData(selected)
-        if item_infos["type"] == ITEM_CONFNODE:
-            item_infos["confnode"]._OpenView()
-            event.Skip()
-        elif item_infos["type"] == ITEM_PROJECT:
-            self.CTR._OpenView()
-        else:
-            IDEFrame.OnProjectTreeItemActivated(self, event)
-
-    def ProjectTreeItemSelect(self, select_item):
-        if select_item is not None and select_item.IsOk():
-            name = self.ProjectTree.GetItemText(select_item)
-            item_infos = self.ProjectTree.GetPyData(select_item)
-            if item_infos["type"] == ITEM_CONFNODE:
-                item_infos["confnode"]._OpenView(onlyopened=True)
-            elif item_infos["type"] == ITEM_PROJECT:
-                self.CTR._OpenView(onlyopened=True)
-            else:
-                IDEFrame.ProjectTreeItemSelect(self, select_item)
-
-    def SelectProjectTreeItem(self, tagname):
-        if self.ProjectTree is not None:
-            root = self.ProjectTree.GetRootItem()
-            if root.IsOk():
-                words = tagname.split("::")
-                if len(words) == 1:
-                    if tagname == "Project":
-                        self.SelectedItem = root
-                        self.ProjectTree.SelectItem(root)
-                        self.ResetSelectedItem()
-                    else:
-                        return self.RecursiveProjectTreeItemSelection(root,
-                              [(word, ITEM_CONFNODE) for word in tagname.split(".")])
-                elif words[0] == "R":
-                    return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)])
-                elif not os.path.exists(words[0]):
-                    IDEFrame.SelectProjectTreeItem(self, tagname)
-
-    def GetAddConfNodeFunction(self, name, confnode=None):
-        def AddConfNodeMenuFunction(event):
-            wx.CallAfter(self.AddConfNode, name, confnode)
-        return AddConfNodeMenuFunction
-
-    def GetDeleteMenuFunction(self, confnode):
-        def DeleteMenuFunction(event):
-            wx.CallAfter(self.DeleteConfNode, confnode)
-        return DeleteMenuFunction
-
-    def AddConfNode(self, ConfNodeType, confnode=None):
-        if self.CTR.CheckProjectPathPerm():
-            ConfNodeName = "%s_0" % ConfNodeType
-            if confnode is not None:
-                confnode.CTNAddChild(ConfNodeName, ConfNodeType)
-            else:
-                self.CTR.CTNAddChild(ConfNodeName, ConfNodeType)
-            self._Refresh(TITLE, FILEMENU, PROJECTTREE)
-
-    def DeleteConfNode(self, confnode):
-        if self.CTR.CheckProjectPathPerm():
-            dialog = wx.MessageDialog(self,
-                _("Really delete node '%s'?") % confnode.CTNName(),
-                _("Remove %s node") % confnode.CTNType,
-                wx.YES_NO|wx.NO_DEFAULT)
-            if dialog.ShowModal() == wx.ID_YES:
-                confnode.CTNRemove()
-                del confnode
-                self._Refresh(TITLE, FILEMENU, PROJECTTREE)
-            dialog.Destroy()
-
-#-------------------------------------------------------------------------------
-#                        Highlights showing functions
-#-------------------------------------------------------------------------------
-
-    def ShowHighlight(self, infos, start, end, highlight_type):
-        config_name = self.Controler.GetProjectMainConfigurationName()
-        if config_name is not None and infos[0] == self.Controler.ComputeConfigurationName(config_name):
-            self.CTR._OpenView()
-            selected = self.TabsOpened.GetSelection()
-            if selected != -1:
-                viewer = self.TabsOpened.GetPage(selected)
-                viewer.AddHighlight(infos[1:], start, end, highlight_type)
-        else:
-            IDEFrame.ShowHighlight(self, infos, start, end, highlight_type)
-
-#-------------------------------------------------------------------------------
-#                               Exception Handler
-#-------------------------------------------------------------------------------
-import threading, traceback
-
-Max_Traceback_List_Size = 20
-
-def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path):
-    trcbck_lst = []
-    for i,line in enumerate(traceback.extract_tb(e_tb)):
-        trcbck = " " + str(i+1) + ". "
-        if line[0].find(os.getcwd()) == -1:
-            trcbck += "file : " + str(line[0]) + ",   "
-        else:
-            trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ",   "
-        trcbck += "line : " + str(line[1]) + ",   " + "function : " + str(line[2])
-        trcbck_lst.append(trcbck)
-
-    # Allow clicking....
-    cap = wx.Window_GetCapture()
-    if cap:
-        cap.ReleaseMouse()
-
-    dlg = wx.SingleChoiceDialog(None,
-        _("""
-An unhandled exception (bug) occured. Bug report saved at :
-(%s)
-
-Please be kind enough to send this file to:
-beremiz-devel@lists.sourceforge.net
-
-You should now restart program.
-
-Traceback:
-""") % bug_report_path +
-        repr(e_type) + " : " + repr(e_value),
-        _("Error"),
-        trcbck_lst)
-    try:
-        res = (dlg.ShowModal() == wx.ID_OK)
-    finally:
-        dlg.Destroy()
-
-    return res
-
-def get_last_traceback(tb):
-    while tb.tb_next:
-        tb = tb.tb_next
-    return tb
-
-
-def format_namespace(d, indent='    '):
-    return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
-
-
-ignored_exceptions = [] # a problem with a line in a module is only reported once per session
-
-def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
-
-    def save_bug_report(e_type, e_value, e_traceback, bug_report_path,date):
-        info = {
-            'app-title': wx.GetApp().GetAppName(),  # app_title
-            'app-version': app_version,
-            'wx-version': wx.VERSION_STRING,
-            'wx-platform': wx.Platform,
-            'python-version': platform.python_version(),  # sys.version.split()[0],
-            'platform': platform.platform(),
-            'e-type': e_type,
-            'e-value': e_value,
-            'date': date,
-            'cwd': os.getcwd(),
-        }
-        if e_traceback:
-            info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
-            last_tb = get_last_traceback(e_traceback)
-            exception_locals = last_tb.tb_frame.f_locals  # the locals at the level of the stack trace where the exception actually occurred
-            info['locals'] = format_namespace(exception_locals)
-            if 'self' in exception_locals:
-                try:
-                    info['self'] = format_namespace(exception_locals['self'].__dict__)
-                except:
-                    pass
-        if not os.path.exists(path):
-            os.mkdir(path)
-        output = open(bug_report_path, 'w')
-        lst = info.keys()
-        lst.sort()
-        for a in lst:
-            output.write(a + ":\n" + str(info[a]) + "\n\n")
-        output.close()
-
-    def handle_exception(e_type, e_value, e_traceback):
-        traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
-        last_tb = get_last_traceback(e_traceback)
-        ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
-        if ex not in ignored_exceptions:
-            ignored_exceptions.append(ex)
-            date = time.ctime()
-            bug_report_path = path + os.sep + "bug_report_" + date.replace(':', '-').replace(' ', '_') + ".txt"
-            save_bug_report(e_type, e_value, e_traceback, bug_report_path, date)
-            Display_Exception_Dialog(e_type, e_value, e_traceback, bug_report_path)
-    #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
-    sys.excepthook = handle_exception
-
-    init_old = threading.Thread.__init__
-    def init(self, *args, **kwargs):
-        init_old(self, *args, **kwargs)
-        run_old = self.run
-        def run_with_except_hook(*args, **kw):
-            try:
-                run_old(*args, **kw)
-            except (KeyboardInterrupt, SystemExit):
-                raise
-            except:
-                sys.excepthook(*sys.exc_info())
-        self.run = run_with_except_hook
-    threading.Thread.__init__ = init
-
-if __name__ == '__main__':
-    # Install a exception handle for bug reports
-    logpath = tempfile.gettempdir()+os.sep+'Beremiz'
-    AddExceptHook(logpath ,version.app_version)
-
-    frame = Beremiz(None, projectOpen, buildpath)
-    if splash:
-        splash.Close()
-    frame.Show()
-    app.MainLoop()
+    beremiz = BeremizIDELauncher()
+    beremiz.Start()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BeremizIDE.py	Fri Mar 10 19:17:43 2017 +0300
@@ -0,0 +1,1177 @@
+#!/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
+# Copyright (C) 2016: Andrey Skvortsov <andrej.skvortzov@gmail.com>
+#
+# See COPYING file for copyrights details.
+#
+# This program 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
+# of the License, or (at your option) any later version.
+#
+# This program 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 program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+
+import os, sys
+import tempfile
+import shutil
+import random
+import time
+import version
+from types import ListType
+
+beremiz_dir = os.path.dirname(os.path.realpath(__file__))
+
+def Bpath(*args):
+    return os.path.join(beremiz_dir,*args)
+
+
+
+import wx.lib.buttons, wx.lib.statbmp, wx.stc
+import cPickle
+import types, time, re, platform, time, traceback, commands
+
+from docutil import OpenHtmlFrame
+from editors.EditorPanel import EditorPanel
+from editors.Viewer import Viewer
+from editors.TextViewer import TextViewer
+from editors.ResourceEditor import ConfigurationEditor, ResourceEditor
+from editors.DataTypeEditor import DataTypeEditor
+from util.MiniTextControler import MiniTextControler
+from util.ProcessLogger import ProcessLogger
+from controls.LogViewer import LogViewer
+from controls.CustomStyledTextCtrl import CustomStyledTextCtrl
+from controls import EnhancedStatusBar as esb
+from dialogs.AboutDialog import ShowAboutDialog
+
+from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY, ITEM_PROJECT, ITEM_RESOURCE
+from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE
+
+
+MAX_RECENT_PROJECTS = 9
+
+if wx.Platform == '__WXMSW__':
+    faces = {
+        'mono' : 'Courier New',
+        'size' : 8,
+    }
+else:
+    faces = {
+        'mono' : 'Courier',
+        'size' : 10,
+    }
+
+from threading import Lock,Timer,currentThread
+MainThread = currentThread().ident
+REFRESH_PERIOD = 0.1
+from time import time as gettime
+class LogPseudoFile:
+    """ Base class for file like objects to facilitate StdOut for the Shell."""
+    def __init__(self, output, risecall):
+        self.red_white = 1
+        self.red_yellow = 2
+        self.black_white = wx.stc.STC_STYLE_DEFAULT
+        self.output = output
+        self.risecall = risecall
+        # to prevent rapid fire on rising log panel
+        self.rising_timer = 0
+        self.lock = Lock()
+        self.YieldLock = Lock()
+        self.RefreshLock = Lock()
+        self.TimerAccessLock = Lock()
+        self.stack = []
+        self.LastRefreshTime = gettime()
+        self.LastRefreshTimer = None
+
+    def write(self, s, style = None):
+        if self.lock.acquire():
+            self.stack.append((s,style))
+            self.lock.release()
+            current_time = gettime()
+            self.TimerAccessLock.acquire()
+            if self.LastRefreshTimer:
+                self.LastRefreshTimer.cancel()
+                self.LastRefreshTimer=None
+            self.TimerAccessLock.release()
+            if current_time - self.LastRefreshTime > REFRESH_PERIOD and self.RefreshLock.acquire(False):
+                self._should_write()
+            else:
+                self.TimerAccessLock.acquire()
+                self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired)
+                self.LastRefreshTimer.start()
+                self.TimerAccessLock.release()
+
+    def _timer_expired(self):
+        if self.RefreshLock.acquire(False):
+            self._should_write()
+        else:
+            self.TimerAccessLock.acquire()
+            self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired)
+            self.LastRefreshTimer.start()
+            self.TimerAccessLock.release()
+
+    def _should_write(self):
+        wx.CallAfter(self._write)
+        if MainThread == currentThread().ident:
+            app = wx.GetApp()
+            if app is not None:
+                if self.YieldLock.acquire(0):
+                    app.Yield()
+                    self.YieldLock.release()
+
+    def _write(self):
+        if self.output :
+            self.output.Freeze()
+            self.lock.acquire()
+            for s, style in self.stack:
+                if style is None : style=self.black_white
+                if style != self.black_white:
+                    self.output.StartStyling(self.output.GetLength(), 0xff)
+
+                # Temporary deactivate read only mode on StyledTextCtrl for
+                # adding text. It seems that text modifications, even
+                # programmatically, are disabled in StyledTextCtrl when read
+                # only is active
+                start_pos = self.output.GetLength()
+                self.output.SetReadOnly(False)
+                self.output.AppendText(s)
+                self.output.SetReadOnly(True)
+                text_len = self.output.GetLength() - start_pos
+
+                if style != self.black_white:
+                    self.output.SetStyling(text_len, style)
+            self.stack = []
+            self.lock.release()
+            self.output.Thaw()
+            self.LastRefreshTime = gettime()
+            try:
+                self.RefreshLock.release()
+            except:
+                pass
+            newtime = time.time()
+            if newtime - self.rising_timer > 1:
+                self.risecall(self.output)
+            self.rising_timer = newtime
+
+    def write_warning(self, s):
+        self.write(s,self.red_white)
+
+    def write_error(self, s):
+        self.write(s,self.red_yellow)
+
+    def writeyield(self, s):
+        self.write(s)
+        wx.GetApp().Yield()
+
+    def flush(self):
+        # Temporary deactivate read only mode on StyledTextCtrl for clearing
+        # text. It seems that text modifications, even programmatically, are
+        # disabled in StyledTextCtrl when read only is active
+        self.output.SetReadOnly(False)
+        self.output.SetText("")
+        self.output.SetReadOnly(True)
+
+    def isatty(self):
+        return False
+
+ID_FILEMENURECENTPROJECTS = wx.NewId()
+
+from IDEFrame import TITLE,\
+                     EDITORTOOLBAR,\
+                     FILEMENU,\
+                     EDITMENU,\
+                     DISPLAYMENU,\
+                     PROJECTTREE,\
+                     POUINSTANCEVARIABLESPANEL,\
+                     LIBRARYTREE,\
+                     SCALING,\
+                     PAGETITLES,\
+                     IDEFrame, AppendMenu,\
+                     EncodeFileSystemPath, DecodeFileSystemPath
+from util.BitmapLibrary import GetBitmap
+
+class Beremiz(IDEFrame):
+
+    def _init_utils(self):
+        self.ConfNodeMenu = wx.Menu(title='')
+        self.RecentProjectsMenu = wx.Menu(title='')
+
+        IDEFrame._init_utils(self)
+
+    def _init_coll_FileMenu_Items(self, parent):
+        AppendMenu(parent, help='', id=wx.ID_NEW,
+              kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N')
+        AppendMenu(parent, help='', id=wx.ID_OPEN,
+              kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O')
+        parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu)
+        parent.AppendSeparator()
+        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,
+              kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W')
+        parent.AppendSeparator()
+        AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
+              kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P')
+        AppendMenu(parent, help='', id=wx.ID_PREVIEW,
+              kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P')
+        AppendMenu(parent, help='', id=wx.ID_PRINT,
+              kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P')
+        parent.AppendSeparator()
+        AppendMenu(parent, help='', id=wx.ID_EXIT,
+              kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q')
+
+        self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
+        self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
+        self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
+        self.Bind(wx.EVT_MENU, self.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)
+        self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
+        self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
+        self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
+
+        self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None),
+                               (wx.ID_OPEN, "open", _(u'Open'), None),
+                               (wx.ID_SAVE, "save", _(u'Save'), None),
+                               (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None),
+                               (wx.ID_PRINT, "print", _(u'Print'), None)])
+
+    def _RecursiveAddMenuItems(self, menu, items):
+        for name, text, help, children in items:
+            new_id = wx.NewId()
+            if len(children) > 0:
+                new_menu = wx.Menu(title='')
+                menu.AppendMenu(new_id, text, new_menu)
+                self._RecursiveAddMenuItems(new_menu, children)
+            else:
+                AppendMenu(menu, help=help, id=new_id,
+                       kind=wx.ITEM_NORMAL, text=text)
+                self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name),
+                          id=new_id)
+
+    def _init_coll_AddMenu_Items(self, parent):
+        IDEFrame._init_coll_AddMenu_Items(self, parent, False)
+        self._RecursiveAddMenuItems(parent, GetAddMenuItems())
+
+    def _init_coll_HelpMenu_Items(self, parent):
+        parent.Append(help='', id=wx.ID_ABOUT,
+              kind=wx.ITEM_NORMAL, text=_(u'About'))
+        self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
+
+    def _init_coll_ConnectionStatusBar_Fields(self, parent):
+        parent.SetFieldsCount(3)
+
+        parent.SetStatusText(number=0, text='')
+        parent.SetStatusText(number=1, text='')
+        parent.SetStatusText(number=2, text='')
+
+        parent.SetStatusWidths([-1, 300, 200])
+
+    def _init_ctrls(self, prnt):
+        IDEFrame._init_ctrls(self, prnt)
+
+        self.EditMenuSize = self.EditMenu.GetMenuItemCount()
+
+        inspectorID = wx.NewId()
+        self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID)
+        accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)]
+
+        keyID = wx.NewId()
+        self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=keyID)
+        accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F12, keyID)]
+
+        for method,shortcut in [("Stop",     wx.WXK_F4),
+                                ("Run",      wx.WXK_F5),
+                                ("Transfer", wx.WXK_F6),
+                                ("Connect",  wx.WXK_F7),
+                                ("Build",    wx.WXK_F11)]:
+            def OnMethodGen(obj,meth):
+                def OnMethod(evt):
+                    if obj.CTR is not None:
+                       obj.CTR.CallMethod('_'+meth)
+                    wx.CallAfter(self.RefreshStatusToolBar)
+                return OnMethod
+            newid = wx.NewId()
+            self.Bind(wx.EVT_MENU, OnMethodGen(self,method), id=newid)
+            accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut,newid)]
+
+        self.SetAcceleratorTable(wx.AcceleratorTable(accels))
+
+        self.LogConsole = CustomStyledTextCtrl(
+                  name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
+                  size=wx.Size(0, 0))
+        self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged)
+        self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged)
+        self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI)
+        self.LogConsole.SetReadOnly(True)
+        self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR)
+
+        # Define Log Console styles
+        self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces)
+        self.LogConsole.StyleClearAll()
+        self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces)
+        self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces)
+
+        # Define Log Console markers
+        self.LogConsole.SetMarginSensitive(1, True)
+        self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
+        self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED")
+
+        self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT)
+
+        self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick)
+        self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified)
+
+        self.MainTabs["LogConsole"] = (self.LogConsole, _("Console"))
+        self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"])
+        #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT)
+
+        self.LogViewer = LogViewer(self.BottomNoteBook, self)
+        self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log"))
+        self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"])
+        #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT)
+
+        StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize,
+                wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER)
+        StatusToolBar.SetToolBitmapSize(wx.Size(25, 25))
+        StatusToolBar.Realize()
+        self.Panes["StatusToolBar"] = StatusToolBar
+        self.AUIManager.AddPane(StatusToolBar, wx.aui.AuiPaneInfo().
+                  Name("StatusToolBar").Caption(_("Status ToolBar")).
+                  ToolbarPane().Top().Position(1).
+                  LeftDockable(False).RightDockable(False))
+
+        self.AUIManager.Update()
+
+        self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP)
+        self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar)
+        self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range = 100)
+        self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2)        
+        self.ProgressStatusBar.Hide()
+        self.SetStatusBar(self.ConnectionStatusBar)
+
+    def __init_execute_path(self):
+        if os.name == 'nt':
+            # on windows, desktop shortcut launches Beremiz.py
+            # with working dir set to mingw/bin.
+            # then we prefix CWD to PATH in order to ensure that
+            # commands invoked by build process by default are
+            # found here.
+            os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"]
+        
+        
+    def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True):
+        # Add beremiz's icon in top left corner of the frame
+        self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO)
+        self.__init_execute_path()
+        
+        IDEFrame.__init__(self, parent, debug)
+        self.Log = LogPseudoFile(self.LogConsole,self.SelectTab)
+
+        self.local_runtime = None
+        self.runtime_port = None
+        self.local_runtime_tmpdir = None
+
+        self.LastPanelSelected = None
+
+        # Define Tree item icon list
+        self.LocationImageList = wx.ImageList(16, 16)
+        self.LocationImageDict = {}
+
+        # Icons for location items
+        for imgname, itemtype in [
+            ("CONFIGURATION", LOCATION_CONFNODE),
+            ("RESOURCE",      LOCATION_MODULE),
+            ("PROGRAM",       LOCATION_GROUP),
+            ("VAR_INPUT",     LOCATION_VAR_INPUT),
+            ("VAR_OUTPUT",    LOCATION_VAR_OUTPUT),
+            ("VAR_LOCAL",     LOCATION_VAR_MEMORY)]:
+            self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname))
+
+        # Icons for other items
+        for imgname, itemtype in [
+            ("Extension", ITEM_CONFNODE)]:
+            self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname))
+
+        if projectOpen is not None:
+            projectOpen = DecodeFileSystemPath(projectOpen, False)
+
+        if projectOpen is not None and os.path.isdir(projectOpen):
+            self.CTR = ProjectController(self, self.Log)
+            self.Controler = self.CTR
+            result, err = self.CTR.LoadProject(projectOpen, buildpath)
+            if not result:
+                self.LibraryPanel.SetController(self.Controler)
+                self.ProjectTree.Enable(True)
+                self.PouInstanceVariablesPanel.SetController(self.Controler)
+                self.RefreshConfigRecentProjects(os.path.abspath(projectOpen))
+                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
+            else:
+                self.ResetView()
+                self.ShowErrorMessage(result)
+        else:
+            self.CTR = ctr
+            self.Controler = ctr
+            if ctr is not None:
+                self.LibraryPanel.SetController(self.Controler)
+                self.ProjectTree.Enable(True)
+                self.PouInstanceVariablesPanel.SetController(self.Controler)
+                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
+        if self.EnableDebug:
+            self.DebugVariablePanel.SetDataProducer(self.CTR)
+
+        self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
+
+        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
+        self.RefreshAll()
+        self.LogConsole.SetFocus()
+
+    def RefreshTitle(self):
+        name = _("Beremiz")
+        if self.CTR is not None:
+            projectname = self.CTR.GetProjectName()
+            if self.CTR.ProjectTestModified():
+                projectname = "~%s~" % projectname
+            self.SetTitle("%s - %s" % (name, projectname))
+        else:
+            self.SetTitle(name)
+
+    def StartLocalRuntime(self, taskbaricon = True):
+        if (self.local_runtime is None) or (self.local_runtime.exitcode is not None):
+            # create temporary directory for runtime working directory
+            self.local_runtime_tmpdir = tempfile.mkdtemp()
+            # choose an arbitrary random port for runtime
+            self.runtime_port = int(random.random() * 1000) + 61131
+            # launch local runtime
+            self.local_runtime = ProcessLogger(self.Log,
+                "\"%s\" \"%s\" -p %s -i localhost %s %s"%(
+                    sys.executable,
+                    Bpath("Beremiz_service.py"),
+                    self.runtime_port,
+                    {False : "-x 0", True :"-x 1"}[taskbaricon],
+                    self.local_runtime_tmpdir),
+                no_gui=False,
+                timeout=500, keyword = self.local_runtime_tmpdir,
+                cwd = self.local_runtime_tmpdir)
+            self.local_runtime.spin()
+        return self.runtime_port
+
+    def KillLocalRuntime(self):
+        if self.local_runtime is not None:
+            # shutdown local runtime
+            self.local_runtime.kill(gently=False)
+            # clear temp dir
+            shutil.rmtree(self.local_runtime_tmpdir)
+
+            self.local_runtime = None
+
+    def OnOpenWidgetInspector(self, evt):
+        # Activate the widget inspection tool
+        from wx.lib.inspection import InspectionTool
+        if not InspectionTool().initialized:
+            InspectionTool().Init()
+
+        # Find a widget to be selected in the tree.  Use either the
+        # one under the cursor, if any, or this frame.
+        wnd = wx.FindWindowAtPointer()
+        if not wnd:
+            wnd = self
+        InspectionTool().Show(wnd, True)
+
+    def OnLogConsoleFocusChanged(self, event):
+        self.RefreshEditMenu()
+        event.Skip()
+
+    def OnLogConsoleUpdateUI(self, event):
+        self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True)
+        event.Skip()
+
+    def OnLogConsoleMarginClick(self, event):
+        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
+        wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx))
+        event.Skip()
+
+    def OnLogConsoleModified(self, event):
+        line_idx = self.LogConsole.LineFromPosition(event.GetPosition())
+        line = self.LogConsole.GetLine(line_idx)
+        if line:
+            result = MATIEC_ERROR_MODEL.match(line)
+            if result is not None:
+                self.LogConsole.MarkerAdd(line_idx, 0)
+        event.Skip()
+
+    def SearchLineForError(self, line):
+        if self.CTR is not None:
+            result = MATIEC_ERROR_MODEL.match(line)
+            if result is not None:
+                first_line, first_column, last_line, last_column, error = result.groups()
+                infos = self.CTR.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.CTR.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.CTR.SaveProject()
+            elif answer == wx.ID_CANCEL:
+                return False
+
+        for idx in xrange(self.TabsOpened.GetPageCount()):
+            window = self.TabsOpened.GetPage(idx)
+            if not window.CheckSaveBeforeClosing():
+                return False
+
+        return True
+
+    def GetTabInfos(self, tab):
+        if (isinstance(tab, EditorPanel) and
+            not isinstance(tab, (Viewer,
+                                 TextViewer,
+                                 ResourceEditor,
+                                 ConfigurationEditor,
+                                 DataTypeEditor))):
+            return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName())
+        elif (isinstance(tab, TextViewer) and
+              (tab.Controler is None or isinstance(tab.Controler, MiniTextControler))):
+            return ("confnode", None, tab.GetInstancePath())
+        else:
+            return IDEFrame.GetTabInfos(self, tab)
+
+    def LoadTab(self, notebook, page_infos):
+        if page_infos[0] == "confnode":
+            if page_infos[1] is None:
+                confnode = self.CTR
+            else:
+                confnode = self.CTR.GetChildByName(page_infos[1])
+            return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:]))
+        else:
+            return IDEFrame.LoadTab(self, notebook, page_infos)
+
+    # Strange hack required by WAMP connector, using twisted.
+    # Twisted reactor needs to be stopped only before quit,
+    # since it cannot be restarted
+    ToDoBeforeQuit = []
+    def AddToDoBeforeQuit(self, Thing):
+        self.ToDoBeforeQuit.append(Thing)
+
+    def OnCloseFrame(self, event):
+        for evt_type in [wx.EVT_SET_FOCUS,
+                         wx.EVT_KILL_FOCUS,
+                         wx.stc.EVT_STC_UPDATEUI]:
+            self.LogConsole.Unbind(evt_type)
+        if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")):
+            if self.CTR is not None:
+                self.CTR.KillDebugThread()
+            self.KillLocalRuntime()
+
+            self.SaveLastState()
+
+            for Thing in self.ToDoBeforeQuit :
+                Thing()
+            self.ToDoBeforeQuit = []
+
+            event.Skip()
+        else:
+            event.Veto()
+
+    def RefreshFileMenu(self):
+        self.RefreshRecentProjectsMenu()
+
+        MenuToolBar = self.Panes["MenuToolBar"]
+        if self.CTR is not None:
+            selected = self.TabsOpened.GetSelection()
+            if selected >= 0:
+                window = self.TabsOpened.GetPage(selected)
+                viewer_is_modified = window.IsModified()
+                is_viewer = isinstance(window, Viewer)
+            else:
+                viewer_is_modified = is_viewer = False
+            if self.TabsOpened.GetPageCount() > 0:
+                self.FileMenu.Enable(wx.ID_CLOSE, True)
+                if is_viewer:
+                    self.FileMenu.Enable(wx.ID_PREVIEW, True)
+                    self.FileMenu.Enable(wx.ID_PRINT, True)
+                    MenuToolBar.EnableTool(wx.ID_PRINT, True)
+                else:
+                    self.FileMenu.Enable(wx.ID_PREVIEW, False)
+                    self.FileMenu.Enable(wx.ID_PRINT, False)
+                    MenuToolBar.EnableTool(wx.ID_PRINT, False)
+            else:
+                self.FileMenu.Enable(wx.ID_CLOSE, False)
+                self.FileMenu.Enable(wx.ID_PREVIEW, False)
+                self.FileMenu.Enable(wx.ID_PRINT, False)
+                MenuToolBar.EnableTool(wx.ID_PRINT, False)
+            self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
+            project_modified = self.CTR.ProjectTestModified() or viewer_is_modified
+            self.FileMenu.Enable(wx.ID_SAVE, project_modified)
+            MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
+            self.FileMenu.Enable(wx.ID_SAVEAS, True)
+            MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
+            self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
+        else:
+            self.FileMenu.Enable(wx.ID_CLOSE, False)
+            self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
+            self.FileMenu.Enable(wx.ID_PREVIEW, False)
+            self.FileMenu.Enable(wx.ID_PRINT, False)
+            MenuToolBar.EnableTool(wx.ID_PRINT, False)
+            self.FileMenu.Enable(wx.ID_SAVE, False)
+            MenuToolBar.EnableTool(wx.ID_SAVE, False)
+            self.FileMenu.Enable(wx.ID_SAVEAS, False)
+            MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
+            self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
+
+    def RefreshRecentProjectsMenu(self):
+        try:
+            recent_projects = map(DecodeFileSystemPath,
+                                  self.GetConfigEntry("RecentProjects", []))
+        except:
+            recent_projects = []
+
+        while self.RecentProjectsMenu.GetMenuItemCount() > len(recent_projects):
+            item = self.RecentProjectsMenu.FindItemByPosition(0)
+            self.RecentProjectsMenu.RemoveItem(item)
+
+        self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0)
+        for idx, projectpath in enumerate(recent_projects):
+            text = u'&%d: %s' % (idx + 1, projectpath)
+
+            if idx < self.RecentProjectsMenu.GetMenuItemCount():
+                item = self.RecentProjectsMenu.FindItemByPosition(idx)
+                id = item.GetId()
+                item.SetItemLabel(text)
+                self.Disconnect(id, id, wx.EVT_BUTTON._getEvtType())
+            else:
+                id = wx.NewId()
+                AppendMenu(self.RecentProjectsMenu, help='', id=id,
+                           kind=wx.ITEM_NORMAL, text=text)
+            self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), id=id)
+
+    def GenerateOpenRecentProjectFunction(self, projectpath):
+        def OpenRecentProject(event):
+            if self.CTR is not None and not self.CheckSaveBeforeClosing():
+                return
+
+            self.OpenProject(projectpath)
+        return OpenRecentProject
+
+    def GenerateMenuRecursive(self, items, menu):
+        for kind, infos in items:
+            if isinstance(kind, ListType):
+                text, id = infos
+                submenu = wx.Menu('')
+                self.GenerateMenuRecursive(kind, submenu)
+                menu.AppendMenu(id, text, submenu)
+            elif kind == wx.ITEM_SEPARATOR:
+                menu.AppendSeparator()
+            else:
+                text, id, help, callback = infos
+                AppendMenu(menu, help='', id=id, kind=kind, text=text)
+                if callback is not None:
+                    self.Bind(wx.EVT_MENU, callback, id=id)
+
+    def RefreshEditorToolBar(self):
+        IDEFrame.RefreshEditorToolBar(self)
+        self.AUIManager.GetPane("EditorToolBar").Position(2)
+        self.AUIManager.GetPane("StatusToolBar").Position(1)
+        self.AUIManager.Update()
+
+    def RefreshStatusToolBar(self):
+        StatusToolBar = self.Panes["StatusToolBar"]
+        StatusToolBar.ClearTools()
+
+        if self.CTR is not None:
+
+            for confnode_method in self.CTR.StatusMethods:
+                if "method" in confnode_method and confnode_method.get("shown",True):
+                    id = wx.NewId()
+                    StatusToolBar.AddSimpleTool(id,
+                        GetBitmap(confnode_method.get("bitmap", "Unknown")),
+                        confnode_method["tooltip"])
+                    self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), id=id)
+
+            StatusToolBar.Realize()
+            self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show()
+        else:
+            self.AUIManager.GetPane("StatusToolBar").Hide()
+        self.AUIManager.GetPane("EditorToolBar").Position(2)
+        self.AUIManager.GetPane("StatusToolBar").Position(1)
+        self.AUIManager.Update()
+
+    def RefreshEditMenu(self):
+        IDEFrame.RefreshEditMenu(self)
+        if self.FindFocus() == self.LogConsole:
+            self.EditMenu.Enable(wx.ID_COPY, True)
+            self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True)
+
+        if self.CTR is not None:
+            selected = self.TabsOpened.GetSelection()
+            if selected >= 0:
+                panel = self.TabsOpened.GetPage(selected)
+            else:
+                panel = None
+            if panel != self.LastPanelSelected:
+                for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()):
+                    item = self.EditMenu.FindItemByPosition(self.EditMenuSize)
+                    if item is not None:
+                        if item.IsSeparator():
+                            self.EditMenu.RemoveItem(item)
+                        else:
+                            self.EditMenu.Delete(item.GetId())
+                self.LastPanelSelected = panel
+                if panel is not None:
+                    items = panel.GetConfNodeMenuItems()
+                else:
+                    items = []
+                if len(items) > 0:
+                    self.EditMenu.AppendSeparator()
+                    self.GenerateMenuRecursive(items, self.EditMenu)
+            if panel is not None:
+                panel.RefreshConfNodeMenu(self.EditMenu)
+        else:
+            for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()):
+                item = self.EditMenu.FindItemByPosition(i)
+                if item is not None:
+                    if item.IsSeparator():
+                        self.EditMenu.RemoveItem(item)
+                    else:
+                        self.EditMenu.Delete(item.GetId())
+            self.LastPanelSelected = None
+        self.MenuBar.UpdateMenus()
+
+    def RefreshAll(self):
+        self.RefreshStatusToolBar()
+
+    def GetMenuCallBackFunction(self, method):
+        """ Generate the callbackfunc for a given CTR method"""
+        def OnMenu(event):
+            # Disable button to prevent re-entrant call
+            event.GetEventObject().Disable()
+            # Call
+            getattr(self.CTR, method)()
+            # Re-enable button
+            event.GetEventObject().Enable()
+        return OnMenu
+
+    def GetConfigEntry(self, entry_name, default):
+        return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default))))
+
+    def ResetConnectionStatusBar(self):
+        for field in xrange(self.ConnectionStatusBar.GetFieldsCount()):
+            self.ConnectionStatusBar.SetStatusText('', field)
+
+    def ResetView(self):
+        IDEFrame.ResetView(self)
+        self.ConfNodeInfos = {}
+        if self.CTR is not None:
+            self.CTR.CloseProject()
+        self.CTR = None
+        self.Log.flush()
+        if self.EnableDebug:
+            self.DebugVariablePanel.SetDataProducer(None)
+            self.ResetConnectionStatusBar()
+
+    def RefreshConfigRecentProjects(self, projectpath, err=False):
+        try:
+            recent_projects = map(DecodeFileSystemPath,
+                                  self.GetConfigEntry("RecentProjects", []))
+        except:
+            recent_projects = []
+        if projectpath in recent_projects:
+            recent_projects.remove(projectpath)
+        if not err:
+            recent_projects.insert(0, projectpath)
+        self.Config.Write("RecentProjects", cPickle.dumps(
+            map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS])))
+        self.Config.Flush()
+
+    def ResetPerspective(self):
+        IDEFrame.ResetPerspective(self)
+        self.RefreshStatusToolBar()
+
+    def OnNewProjectMenu(self, event):
+        if self.CTR is not None and not self.CheckSaveBeforeClosing():
+            return
+
+        try:
+            defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
+        except:
+            defaultpath = os.path.expanduser("~")
+
+        dialog = wx.DirDialog(self , _("Choose a project"), defaultpath)
+        if dialog.ShowModal() == wx.ID_OK:
+            projectpath = dialog.GetPath()
+            self.Config.Write("lastopenedfolder",
+                              EncodeFileSystemPath(os.path.dirname(projectpath)))
+            self.Config.Flush()
+            self.ResetView()
+            ctr = ProjectController(self, self.Log)
+            result = ctr.NewProject(projectpath)
+            if not result:
+                self.CTR = ctr
+                self.Controler = self.CTR
+                self.LibraryPanel.SetController(self.Controler)
+                self.ProjectTree.Enable(True)
+                self.PouInstanceVariablesPanel.SetController(self.Controler)
+                self.RefreshConfigRecentProjects(projectpath)
+                if self.EnableDebug:
+                    self.DebugVariablePanel.SetDataProducer(self.CTR)
+                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
+            else:
+                self.ResetView()
+                self.ShowErrorMessage(result)
+            self.RefreshAll()
+            self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
+        dialog.Destroy()
+
+    def OnOpenProjectMenu(self, event):
+        if self.CTR is not None and not self.CheckSaveBeforeClosing():
+            return
+
+        try:
+            defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder"))
+        except:
+            defaultpath = os.path.expanduser("~")
+
+        dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, style=wx.DEFAULT_DIALOG_STYLE|
+                                                                               wx.RESIZE_BORDER)
+        if dialog.ShowModal() == wx.ID_OK:
+            self.OpenProject(dialog.GetPath())
+        dialog.Destroy()
+
+    def OpenProject(self, projectpath):
+        if os.path.isdir(projectpath):
+            self.Config.Write("lastopenedfolder",
+                              EncodeFileSystemPath(os.path.dirname(projectpath)))
+            self.Config.Flush()
+            self.ResetView()
+            self.CTR = ProjectController(self, self.Log)
+            self.Controler = self.CTR
+            result, err = self.CTR.LoadProject(projectpath)
+            if not result:
+                self.LibraryPanel.SetController(self.Controler)
+                self.ProjectTree.Enable(True)
+                self.PouInstanceVariablesPanel.SetController(self.Controler)
+                if self.EnableDebug:
+                    self.DebugVariablePanel.SetDataProducer(self.CTR)
+                self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE)
+            else:
+                self.ResetView()
+                self.ShowErrorMessage(result)
+            self.RefreshAll()
+            self.SearchResultPanel.ResetSearchResults()
+        else:
+            self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath)
+            err = True
+        self.RefreshConfigRecentProjects(projectpath, err)
+        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
+
+    def OnCloseProjectMenu(self, event):
+        if self.CTR is not None and not self.CheckSaveBeforeClosing():
+            return
+
+        self.ResetView()
+        self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU)
+        self.RefreshAll()
+
+    def OnSaveProjectMenu(self, event):
+        selected = self.TabsOpened.GetSelection()
+        if selected != -1:
+            window = self.TabsOpened.GetPage(selected)
+            window.Save()
+        if self.CTR is not None:
+            self.CTR.SaveProject()
+            self.RefreshAll()
+            self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
+
+    def OnSaveProjectAsMenu(self, event):
+        selected = self.TabsOpened.GetSelection()
+        if selected != -1:
+            window = self.TabsOpened.GetPage(selected)
+            window.SaveAs()
+        if self.CTR is not None:
+            self.CTR.SaveProjectAs()
+            self.RefreshAll()
+            self.RefreshConfigRecentProjects(self.CTR.ProjectPath)
+            self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES)
+
+    def OnQuitMenu(self, event):
+        self.Close()
+
+    def OnAboutMenu(self, event):
+        info = version.GetAboutDialogInfo()        
+        ShowAboutDialog(self, info)
+
+    def OnProjectTreeItemBeginEdit(self, event):
+        selected = event.GetItem()
+        if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE:
+            event.Veto()
+        else:
+            IDEFrame.OnProjectTreeItemBeginEdit(self, event)
+
+    def OnProjectTreeRightUp(self, event):
+        item = event.GetItem()
+        item_infos = self.ProjectTree.GetPyData(item)
+
+        if item_infos["type"] == ITEM_CONFNODE:
+            confnode_menu = wx.Menu(title='')
+
+            confnode = item_infos["confnode"]
+            if confnode is not None:
+                menu_items = confnode.GetContextualMenuItems()
+                if menu_items is not None:
+                    for text, help, callback in menu_items:
+                        new_id = wx.NewId()
+                        confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text)
+                        self.Bind(wx.EVT_MENU, callback, id=new_id)
+                else:
+                    for name, XSDClass, help in confnode.CTNChildrenTypes:
+                        new_id = wx.NewId()
+                        confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Add") + " " + name)
+                        self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name, confnode), id=new_id)
+
+            new_id = wx.NewId()
+            AppendMenu(confnode_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete"))
+            self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), id=new_id)
+
+            self.PopupMenu(confnode_menu)
+            confnode_menu.Destroy()
+
+            event.Skip()
+        elif item_infos["type"] == ITEM_RESOURCE:
+            # prevent last resource to be delted
+            parent = self.ProjectTree.GetItemParent(item)
+            parent_name = self.ProjectTree.GetItemText(parent)
+            if parent_name == _("Resources"):
+                IDEFrame.OnProjectTreeRightUp(self, event)
+        else:
+            IDEFrame.OnProjectTreeRightUp(self, event)
+
+    def OnProjectTreeItemActivated(self, event):
+        selected = event.GetItem()
+        name = self.ProjectTree.GetItemText(selected)
+        item_infos = self.ProjectTree.GetPyData(selected)
+        if item_infos["type"] == ITEM_CONFNODE:
+            item_infos["confnode"]._OpenView()
+            event.Skip()
+        elif item_infos["type"] == ITEM_PROJECT:
+            self.CTR._OpenView()
+        else:
+            IDEFrame.OnProjectTreeItemActivated(self, event)
+
+    def ProjectTreeItemSelect(self, select_item):
+        if select_item is not None and select_item.IsOk():
+            name = self.ProjectTree.GetItemText(select_item)
+            item_infos = self.ProjectTree.GetPyData(select_item)
+            if item_infos["type"] == ITEM_CONFNODE:
+                item_infos["confnode"]._OpenView(onlyopened=True)
+            elif item_infos["type"] == ITEM_PROJECT:
+                self.CTR._OpenView(onlyopened=True)
+            else:
+                IDEFrame.ProjectTreeItemSelect(self, select_item)
+
+    def SelectProjectTreeItem(self, tagname):
+        if self.ProjectTree is not None:
+            root = self.ProjectTree.GetRootItem()
+            if root.IsOk():
+                words = tagname.split("::")
+                if len(words) == 1:
+                    if tagname == "Project":
+                        self.SelectedItem = root
+                        self.ProjectTree.SelectItem(root)
+                        self.ResetSelectedItem()
+                    else:
+                        return self.RecursiveProjectTreeItemSelection(root,
+                              [(word, ITEM_CONFNODE) for word in tagname.split(".")])
+                elif words[0] == "R":
+                    return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)])
+                elif not os.path.exists(words[0]):
+                    IDEFrame.SelectProjectTreeItem(self, tagname)
+
+    def GetAddConfNodeFunction(self, name, confnode=None):
+        def AddConfNodeMenuFunction(event):
+            wx.CallAfter(self.AddConfNode, name, confnode)
+        return AddConfNodeMenuFunction
+
+    def GetDeleteMenuFunction(self, confnode):
+        def DeleteMenuFunction(event):
+            wx.CallAfter(self.DeleteConfNode, confnode)
+        return DeleteMenuFunction
+
+    def AddConfNode(self, ConfNodeType, confnode=None):
+        if self.CTR.CheckProjectPathPerm():
+            ConfNodeName = "%s_0" % ConfNodeType
+            if confnode is not None:
+                confnode.CTNAddChild(ConfNodeName, ConfNodeType)
+            else:
+                self.CTR.CTNAddChild(ConfNodeName, ConfNodeType)
+            self._Refresh(TITLE, FILEMENU, PROJECTTREE)
+
+    def DeleteConfNode(self, confnode):
+        if self.CTR.CheckProjectPathPerm():
+            dialog = wx.MessageDialog(self,
+                _("Really delete node '%s'?") % confnode.CTNName(),
+                _("Remove %s node") % confnode.CTNType,
+                wx.YES_NO|wx.NO_DEFAULT)
+            if dialog.ShowModal() == wx.ID_YES:
+                confnode.CTNRemove()
+                del confnode
+                self._Refresh(TITLE, FILEMENU, PROJECTTREE)
+            dialog.Destroy()
+
+#-------------------------------------------------------------------------------
+#                        Highlights showing functions
+#-------------------------------------------------------------------------------
+
+    def ShowHighlight(self, infos, start, end, highlight_type):
+        config_name = self.Controler.GetProjectMainConfigurationName()
+        if config_name is not None and infos[0] == self.Controler.ComputeConfigurationName(config_name):
+            self.CTR._OpenView()
+            selected = self.TabsOpened.GetSelection()
+            if selected != -1:
+                viewer = self.TabsOpened.GetPage(selected)
+                viewer.AddHighlight(infos[1:], start, end, highlight_type)
+        else:
+            IDEFrame.ShowHighlight(self, infos, start, end, highlight_type)
+
+#-------------------------------------------------------------------------------
+#                               Exception Handler
+#-------------------------------------------------------------------------------
+import threading, traceback
+
+Max_Traceback_List_Size = 20
+
+def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path):
+    trcbck_lst = []
+    for i,line in enumerate(traceback.extract_tb(e_tb)):
+        trcbck = " " + str(i+1) + ". "
+        if line[0].find(os.getcwd()) == -1:
+            trcbck += "file : " + str(line[0]) + ",   "
+        else:
+            trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ",   "
+        trcbck += "line : " + str(line[1]) + ",   " + "function : " + str(line[2])
+        trcbck_lst.append(trcbck)
+
+    # Allow clicking....
+    cap = wx.Window_GetCapture()
+    if cap:
+        cap.ReleaseMouse()
+
+    dlg = wx.SingleChoiceDialog(None,
+        _("""
+An unhandled exception (bug) occured. Bug report saved at :
+(%s)
+
+Please be kind enough to send this file to:
+beremiz-devel@lists.sourceforge.net
+
+You should now restart program.
+
+Traceback:
+""") % bug_report_path +
+        repr(e_type) + " : " + repr(e_value),
+        _("Error"),
+        trcbck_lst)
+    try:
+        res = (dlg.ShowModal() == wx.ID_OK)
+    finally:
+        dlg.Destroy()
+
+    return res
+
+def get_last_traceback(tb):
+    while tb.tb_next:
+        tb = tb.tb_next
+    return tb
+
+
+def format_namespace(d, indent='    '):
+    return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
+
+
+ignored_exceptions = [] # a problem with a line in a module is only reported once per session
+
+def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
+
+    def save_bug_report(e_type, e_value, e_traceback, bug_report_path,date):
+        info = {
+            'app-title': wx.GetApp().GetAppName(),  # app_title
+            'app-version': app_version,
+            'wx-version': wx.VERSION_STRING,
+            'wx-platform': wx.Platform,
+            'python-version': platform.python_version(),  # sys.version.split()[0],
+            'platform': platform.platform(),
+            'e-type': e_type,
+            'e-value': e_value,
+            'date': date,
+            'cwd': os.getcwd(),
+        }
+        if e_traceback:
+            info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
+            last_tb = get_last_traceback(e_traceback)
+            exception_locals = last_tb.tb_frame.f_locals  # the locals at the level of the stack trace where the exception actually occurred
+            info['locals'] = format_namespace(exception_locals)
+            if 'self' in exception_locals:
+                try:
+                    info['self'] = format_namespace(exception_locals['self'].__dict__)
+                except:
+                    pass
+        if not os.path.exists(path):
+            os.mkdir(path)
+        output = open(bug_report_path, 'w')
+        lst = info.keys()
+        lst.sort()
+        for a in lst:
+            output.write(a + ":\n" + str(info[a]) + "\n\n")
+        output.close()
+
+    def handle_exception(e_type, e_value, e_traceback):
+        traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
+        last_tb = get_last_traceback(e_traceback)
+        ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
+        if ex not in ignored_exceptions:
+            ignored_exceptions.append(ex)
+            date = time.ctime()
+            bug_report_path = path + os.sep + "bug_report_" + date.replace(':', '-').replace(' ', '_') + ".txt"
+            save_bug_report(e_type, e_value, e_traceback, bug_report_path, date)
+            Display_Exception_Dialog(e_type, e_value, e_traceback, bug_report_path)
+    #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
+    sys.excepthook = handle_exception
+
+    init_old = threading.Thread.__init__
+    def init(self, *args, **kwargs):
+        init_old(self, *args, **kwargs)
+        run_old = self.run
+        def run_with_except_hook(*args, **kw):
+            try:
+                run_old(*args, **kw)
+            except (KeyboardInterrupt, SystemExit):
+                raise
+            except:
+                sys.excepthook(*sys.exc_info())
+        self.run = run_with_except_hook
+    threading.Thread.__init__ = init