andrej@1662: #!/usr/bin/env python andrej@1662: # -*- coding: utf-8 -*- andrej@1662: andrej@1662: # This file is part of Beremiz, a Integrated Development Environment for andrej@1662: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1662: # andrej@1662: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1662: # Copyright (C) 2016: Andrey Skvortsov andrej@1662: # andrej@1662: # See COPYING file for copyrights details. andrej@1662: # andrej@1662: # This program is free software; you can redistribute it and/or andrej@1662: # modify it under the terms of the GNU General Public License andrej@1662: # as published by the Free Software Foundation; either version 2 andrej@1662: # of the License, or (at your option) any later version. andrej@1662: # andrej@1662: # This program is distributed in the hope that it will be useful, andrej@1662: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1662: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1662: # GNU General Public License for more details. andrej@1662: # andrej@1662: # You should have received a copy of the GNU General Public License andrej@1662: # along with this program; if not, write to the Free Software andrej@1662: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. andrej@1662: andrej@1662: andrej@1732: import os kinsamanka@3758: import pickle andrej@1732: import sys andrej@1662: import shutil andrej@1834: import time edouard@3712: import signal andrej@1832: from time import time as gettime edouard@3833: from threading import Lock, Timer, current_thread andrej@1832: andrej@1732: import wx.lib.buttons andrej@1732: import wx.lib.statbmp andrej@1732: import wx.stc edouard@3570: import wx.adv andrej@1832: andrej@1832: andrej@1832: import version andrej@1662: from editors.EditorPanel import EditorPanel andrej@1662: from editors.Viewer import Viewer andrej@1662: from editors.TextViewer import TextViewer andrej@1662: from editors.ResourceEditor import ConfigurationEditor, ResourceEditor andrej@1662: from editors.DataTypeEditor import DataTypeEditor edouard@3906: from util.paths import Bpath, ThirdPartyPath andrej@1662: from util.MiniTextControler import MiniTextControler andrej@1834: from util.BitmapLibrary import GetBitmap andrej@1662: from controls.LogViewer import LogViewer andrej@1662: from controls.CustomStyledTextCtrl import CustomStyledTextCtrl andrej@1662: from controls import EnhancedStatusBar as esb andrej@1662: from dialogs.AboutDialog import ShowAboutDialog andrej@1662: Edouard@1948: from plcopen.types_enums import \ Edouard@1948: ComputeConfigurationName, \ andrej@1783: LOCATION_CONFNODE, \ andrej@1783: LOCATION_MODULE, \ andrej@1783: LOCATION_GROUP, \ andrej@1783: LOCATION_VAR_INPUT, \ andrej@1783: LOCATION_VAR_OUTPUT, \ andrej@1783: LOCATION_VAR_MEMORY, \ andrej@1783: ITEM_PROJECT, \ Edouard@1948: ITEM_RESOURCE, \ Edouard@1948: ITEM_CONFNODE Edouard@1948: Edouard@1948: from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL andrej@1662: andrej@1783: from IDEFrame import \ andrej@1783: TITLE,\ andrej@1783: EDITORTOOLBAR,\ andrej@1783: FILEMENU,\ andrej@1783: EDITMENU,\ andrej@1783: DISPLAYMENU,\ andrej@1783: PROJECTTREE,\ andrej@1783: POUINSTANCEVARIABLESPANEL,\ andrej@1783: LIBRARYTREE,\ andrej@1783: PAGETITLES,\ andrej@1783: IDEFrame, \ andrej@1783: EncodeFileSystemPath, \ andrej@1783: DecodeFileSystemPath andrej@1783: edouard@3501: from LocalRuntimeMixin import LocalRuntimeMixin edouard@3501: andrej@1783: Edouard@2737: def AppendMenu(parent, help, id, kind, text): edouard@3303: return parent.Append(wx.MenuItem(helpString=help, id=id, kind=kind, text=text)) andrej@1662: andrej@1662: MAX_RECENT_PROJECTS = 9 andrej@1662: andrej@1783: andrej@1662: if wx.Platform == '__WXMSW__': andrej@1662: faces = { andrej@1739: 'mono': 'Courier New', andrej@1739: 'size': 8, andrej@1662: } andrej@1662: else: andrej@1662: faces = { edouard@2704: 'mono': 'FreeMono', andrej@1739: 'size': 10, andrej@1662: } andrej@1662: andrej@1783: edouard@3833: MainThread = current_thread().ident andrej@1662: REFRESH_PERIOD = 0.1 andrej@1736: andrej@1736: andrej@1831: class LogPseudoFile(object): andrej@1662: """ Base class for file like objects to facilitate StdOut for the Shell.""" edouard@3423: def __init__(self, output, risecall, logf): andrej@1662: self.red_white = 1 andrej@1662: self.red_yellow = 2 andrej@1662: self.black_white = wx.stc.STC_STYLE_DEFAULT andrej@1662: self.output = output andrej@1662: self.risecall = risecall andrej@1662: # to prevent rapid fire on rising log panel andrej@1662: self.rising_timer = 0 edouard@2728: self.StackLock = Lock() andrej@1662: self.YieldLock = Lock() andrej@1662: self.RefreshLock = Lock() andrej@1662: self.TimerAccessLock = Lock() andrej@1662: self.stack = [] andrej@1662: self.LastRefreshTime = gettime() andrej@1662: self.LastRefreshTimer = None edouard@2730: self.refreshPending = False edouard@3423: self.logf = logf andrej@1662: andrej@1744: def write(self, s, style=None): edouard@3423: if self.logf is not None: edouard@3423: self.logf.write(s) edouard@3437: self.logf.flush() edouard@2728: self.StackLock.acquire() edouard@2728: self.stack.append((s, style)) edouard@2728: self.StackLock.release() edouard@2728: current_time = gettime() edouard@2728: with self.TimerAccessLock: edouard@2728: if self.LastRefreshTimer is not None: edouard@2728: self.LastRefreshTimer.cancel() edouard@2728: self.LastRefreshTimer = None edouard@2728: elapsed = current_time - self.LastRefreshTime edouard@2728: if elapsed > REFRESH_PERIOD: edouard@2728: self._should_write() edouard@2728: else: edouard@2726: with self.TimerAccessLock: edouard@2728: if self.LastRefreshTimer is None: edouard@2728: self.LastRefreshTimer = Timer(REFRESH_PERIOD - elapsed, self._timer_expired) edouard@2728: self.LastRefreshTimer.start() andrej@1662: andrej@1662: def _timer_expired(self): edouard@2726: self._should_write() edouard@2726: with self.TimerAccessLock: edouard@2726: self.LastRefreshTimer = None andrej@1662: andrej@1662: def _should_write(self): edouard@3833: if MainThread == current_thread().ident: edouard@2726: app = wx.GetApp() andrej@1662: if app is not None: edouard@2726: self._write() andrej@1662: if self.YieldLock.acquire(0): andrej@1662: app.Yield() andrej@1662: self.YieldLock.release() edouard@2726: else: edouard@2726: with self.RefreshLock: edouard@2726: if not self.refreshPending: edouard@2726: self.refreshPending = True edouard@2726: wx.CallAfter(self._write) andrej@1662: andrej@1662: def _write(self): andrej@1739: if self.output: edouard@2726: with self.RefreshLock: edouard@2726: self.output.Freeze() edouard@2728: self.output.AnnotationClearAll() edouard@2728: self.StackLock.acquire() edouard@2726: for s, style in self.stack: edouard@2726: if style is None: edouard@2726: style = self.black_white edouard@2726: if style != self.black_white: edouard@3303: self.output.StartStyling(self.output.GetLength()) edouard@2726: edouard@2726: # Temporary deactivate read only mode on StyledTextCtrl for edouard@2726: # adding text. It seems that text modifications, even edouard@2726: # programmatically, are disabled in StyledTextCtrl when read edouard@2726: # only is active edouard@2726: start_pos = self.output.GetLength() edouard@2726: self.output.SetReadOnly(False) edouard@2726: self.output.AppendText(s) edouard@2726: self.output.SetReadOnly(True) edouard@2726: text_len = self.output.GetLength() - start_pos edouard@2726: edouard@2726: if style != self.black_white: edouard@2726: self.output.SetStyling(text_len, style) edouard@2726: self.stack = [] edouard@2728: self.StackLock.release() Edouard@2731: self.output.ScrollToEnd() edouard@2726: self.output.Thaw() edouard@2726: self.LastRefreshTime = gettime() edouard@2726: newtime = time.time() edouard@2726: if newtime - self.rising_timer > 1: edouard@2726: self.risecall(self.output) edouard@2726: self.rising_timer = newtime edouard@2726: self.refreshPending = False edouard@2726: andrej@1662: andrej@1662: def write_warning(self, s): andrej@1740: self.write(s, self.red_white) andrej@1662: andrej@1662: def write_error(self, s): andrej@1740: self.write(s, self.red_yellow) andrej@1662: andrej@1662: def flush(self): andrej@1662: # Temporary deactivate read only mode on StyledTextCtrl for clearing andrej@1662: # text. It seems that text modifications, even programmatically, are andrej@1662: # disabled in StyledTextCtrl when read only is active andrej@1662: self.output.SetReadOnly(False) andrej@1662: self.output.SetText("") andrej@1662: self.output.SetReadOnly(True) andrej@1662: andrej@1662: def isatty(self): andrej@1662: return False andrej@1662: edouard@2730: def progress(self, text): Edouard@2740: l = max(self.output.GetLineCount()-2, 0) edouard@2730: self.output.AnnotationSetText(l, text) edouard@2730: self.output.AnnotationSetVisible(wx.stc.STC_ANNOTATION_BOXED) edouard@2730: self.output.AnnotationSetStyle(l, self.black_white) edouard@2730: if self.YieldLock.acquire(0): edouard@2730: app = wx.GetApp() edouard@2730: app.Yield() edouard@2730: self.YieldLock.release() edouard@2730: andrej@1749: kinsamanka@3766: ID_FILEMENURECENTPROJECTS = wx.NewIdRef() andrej@1662: andrej@1736: edouard@3501: class Beremiz(IDEFrame, LocalRuntimeMixin): andrej@1662: andrej@1662: def _init_utils(self): andrej@1662: self.ConfNodeMenu = wx.Menu(title='') andrej@1662: self.RecentProjectsMenu = wx.Menu(title='') edouard@3351: self.TutorialsProjectsMenu = wx.Menu(title='') andrej@1662: andrej@1662: IDEFrame._init_utils(self) andrej@1662: andrej@1662: def _init_coll_FileMenu_Items(self, parent): andrej@1662: AppendMenu(parent, help='', id=wx.ID_NEW, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('New') + '\tCTRL+N') andrej@1662: AppendMenu(parent, help='', id=wx.ID_OPEN, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Open') + '\tCTRL+O') edouard@3833: self.RecentProjectsMenuItem = parent.AppendSubMenu(self.RecentProjectsMenu, _("&Recent Projects")) andrej@1662: parent.AppendSeparator() edouard@3833: parent.AppendSubMenu(self.TutorialsProjectsMenu, _("&Tutorials and Examples")) edouard@3351: edouard@3351: exemples_dir = Bpath("exemples") edouard@3351: project_list = sorted(os.listdir(exemples_dir)) edouard@3351: edouard@3351: for idx, dirname in enumerate(project_list): kinsamanka@3750: text = '&%d: %s' % (idx + 1, dirname) edouard@3351: edouard@3351: item = self.TutorialsProjectsMenu.Append(wx.ID_ANY, text, '') edouard@3351: edouard@3351: projectpath = os.path.join(exemples_dir, dirname) edouard@3351: edouard@3390: def OpenExemple(event, projectpath=projectpath): edouard@3351: if self.CTR is not None and not self.CheckSaveBeforeClosing(): edouard@3351: return edouard@3351: edouard@3351: self.OpenProject(projectpath) edouard@3351: if not self.CTR.CheckProjectPathPerm(): edouard@3351: self.ResetView() edouard@3351: edouard@3351: self.Bind(wx.EVT_MENU, OpenExemple, item) edouard@3351: parent.AppendSeparator() andrej@1662: AppendMenu(parent, help='', id=wx.ID_SAVE, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Save') + '\tCTRL+S') andrej@1662: AppendMenu(parent, help='', id=wx.ID_SAVEAS, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Save as') + '\tCTRL+SHIFT+S') andrej@1662: AppendMenu(parent, help='', id=wx.ID_CLOSE, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Close Tab') + '\tCTRL+W') andrej@1662: AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Close Project') + '\tCTRL+SHIFT+W') andrej@1662: parent.AppendSeparator() andrej@1662: AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Page Setup') + '\tCTRL+ALT+P') andrej@1662: AppendMenu(parent, help='', id=wx.ID_PREVIEW, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Preview') + '\tCTRL+SHIFT+P') andrej@1662: AppendMenu(parent, help='', id=wx.ID_PRINT, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Print') + '\tCTRL+P') andrej@1662: parent.AppendSeparator() andrej@1662: AppendMenu(parent, help='', id=wx.ID_EXIT, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('Quit') + '\tCTRL+Q') andrej@1662: andrej@1662: self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) andrej@1662: self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) andrej@1662: self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) andrej@1662: self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) andrej@1662: self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) andrej@1662: self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) andrej@1662: self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) andrej@1662: self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) andrej@1662: self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) andrej@1662: self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) andrej@1662: kinsamanka@3750: self.AddToMenuToolBar([(wx.ID_NEW, "new", _('New'), None), kinsamanka@3750: (wx.ID_OPEN, "open", _('Open'), None), kinsamanka@3750: (wx.ID_SAVE, "save", _('Save'), None), kinsamanka@3750: (wx.ID_SAVEAS, "saveas", _('Save As...'), None), kinsamanka@3750: (wx.ID_PRINT, "print", _('Print'), None)]) andrej@1662: andrej@1662: def _RecursiveAddMenuItems(self, menu, items): Edouard@2569: for name, text, helpstr, children in items: andrej@1662: if len(children) > 0: andrej@1662: new_menu = wx.Menu(title='') edouard@3303: menu.AppendSubMenu(new_menu, text) andrej@1662: self._RecursiveAddMenuItems(new_menu, children) andrej@1662: else: edouard@3336: item = menu.Append(wx.MenuItem(text=text, helpString=helpstr, kind=wx.ITEM_NORMAL, id=wx.ID_ANY)) Edouard@2569: self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), item) andrej@1662: andrej@1662: def _init_coll_AddMenu_Items(self, parent): andrej@1662: IDEFrame._init_coll_AddMenu_Items(self, parent, False) andrej@1662: self._RecursiveAddMenuItems(parent, GetAddMenuItems()) andrej@1662: andrej@1662: def _init_coll_HelpMenu_Items(self, parent): andrej@1762: def handler(event): andrej@1762: return wx.MessageBox( andrej@1762: version.GetCommunityHelpMsg(), kinsamanka@3750: _('Community support'), andrej@1762: wx.OK | wx.ICON_INFORMATION) andrej@1762: kinsamanka@3750: item = parent.Append(wx.ID_ANY, _('Community support'), '') Edouard@2569: self.Bind(wx.EVT_MENU, handler, item) andrej@1730: edouard@3303: parent.Append(wx.MenuItem(helpString='', id=wx.ID_ABOUT, kinsamanka@3750: kind=wx.ITEM_NORMAL, text=_('About'))) andrej@1662: self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) andrej@1662: andrej@1662: def _init_coll_ConnectionStatusBar_Fields(self, parent): andrej@1662: parent.SetFieldsCount(3) andrej@1662: edouard@3303: parent.SetStatusText(i=0, text='') edouard@3303: parent.SetStatusText(i=1, text='') edouard@3303: parent.SetStatusText(i=2, text='') andrej@1662: andrej@1662: parent.SetStatusWidths([-1, 300, 200]) andrej@1662: andrej@1662: def _init_ctrls(self, prnt): andrej@1662: IDEFrame._init_ctrls(self, prnt) andrej@1662: andrej@1662: self.EditMenuSize = self.EditMenu.GetMenuItemCount() andrej@1662: kinsamanka@3766: inspectorID = wx.NewIdRef() andrej@1662: self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) andrej@1745: accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord('I'), inspectorID)] andrej@1662: edouard@3445: self.methodLock = Lock() edouard@3445: andrej@1740: for method, shortcut in [("Stop", wx.WXK_F4), andrej@1740: ("Run", wx.WXK_F5), andrej@1740: ("Transfer", wx.WXK_F6), andrej@1740: ("Connect", wx.WXK_F7), Edouard@2735: ("Clean", wx.WXK_F9), andrej@1740: ("Build", wx.WXK_F11)]: andrej@1740: def OnMethodGen(obj, meth): andrej@1662: def OnMethod(evt): andrej@1662: if obj.CTR is not None: edouard@3445: if obj.methodLock.acquire(False): edouard@3445: obj.CTR.CallMethod('_'+meth) edouard@3445: obj.methodLock.release() edouard@3445: wx.CallAfter(obj.RefreshStatusToolBar) edouard@3445: else: edouard@3445: # Postpone call if one of method already running edouard@3445: # can happen because of long method using log, edouard@3445: # itself calling wx.Yield edouard@3445: wx.CallLater(50, OnMethod, evt) andrej@1662: return OnMethod kinsamanka@3766: newid = wx.NewIdRef() andrej@1740: self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid) andrej@1740: accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut, newid)] andrej@1662: andrej@1662: self.SetAcceleratorTable(wx.AcceleratorTable(accels)) andrej@1662: andrej@1662: self.LogConsole = CustomStyledTextCtrl( andrej@1878: name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0), andrej@1878: size=wx.Size(0, 0)) andrej@1662: self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged) andrej@1662: self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged) andrej@1662: self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI) andrej@1662: self.LogConsole.SetReadOnly(True) andrej@1662: self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR) andrej@1662: andrej@1662: # Define Log Console styles andrej@1662: self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) andrej@1662: self.LogConsole.StyleClearAll() andrej@1662: self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces) andrej@1662: self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces) andrej@1662: andrej@1662: # Define Log Console markers andrej@1662: self.LogConsole.SetMarginSensitive(1, True) andrej@1662: self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL) andrej@1662: self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED") andrej@1662: andrej@1662: self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT) edouard@3424: self.LogConsole.SetCaretPeriod(0) andrej@1662: andrej@1662: self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick) andrej@1662: self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified) andrej@1662: andrej@1662: self.MainTabs["LogConsole"] = (self.LogConsole, _("Console")) andrej@1662: self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"]) andrej@1782: # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT) andrej@1662: andrej@1662: self.LogViewer = LogViewer(self.BottomNoteBook, self) andrej@1662: self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log")) andrej@1662: self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"]) andrej@1782: # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT) andrej@1662: andrej@1662: StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, edouard@3588: wx.TB_FLAT | wx.TB_HORIZONTAL | wx.NO_BORDER) andrej@1662: StatusToolBar.SetToolBitmapSize(wx.Size(25, 25)) andrej@1662: StatusToolBar.Realize() andrej@1662: self.Panes["StatusToolBar"] = StatusToolBar andrej@1662: self.AUIManager.AddPane(StatusToolBar, wx.aui.AuiPaneInfo(). andrej@1768: Name("StatusToolBar").Caption(_("Status ToolBar")). andrej@1768: ToolbarPane().Top().Position(1). andrej@1768: LeftDockable(False).RightDockable(False)) andrej@1662: andrej@1662: self.AUIManager.Update() andrej@1662: edouard@3303: self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.STB_SIZEGRIP) andrej@1662: self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar) andrej@1744: self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range=100) andrej@1730: self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2) andrej@1662: self.ProgressStatusBar.Hide() andrej@1662: self.SetStatusBar(self.ConnectionStatusBar) andrej@1662: andrej@1662: def __init_execute_path(self): andrej@1662: if os.name == 'nt': andrej@1662: # on windows, desktop shortcut launches Beremiz.py andrej@1662: # with working dir set to mingw/bin. andrej@1662: # then we prefix CWD to PATH in order to ensure that andrej@1662: # commands invoked by build process by default are andrej@1662: # found here. andrej@1662: os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] andrej@1730: edouard@3423: def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True, logf=None): edouard@3501: andrej@1662: # Add beremiz's icon in top left corner of the frame andrej@1662: self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO) andrej@1662: self.__init_execute_path() andrej@1730: andrej@1662: IDEFrame.__init__(self, parent, debug) edouard@3423: self.Log = LogPseudoFile(self.LogConsole, self.SelectTab, logf) andrej@1662: edouard@3502: LocalRuntimeMixin.__init__(self, self.Log) edouard@3502: andrej@1662: self.LastPanelSelected = None andrej@1662: andrej@1662: # Define Tree item icon list andrej@1662: self.LocationImageList = wx.ImageList(16, 16) andrej@1662: self.LocationImageDict = {} andrej@1662: andrej@1662: # Icons for location items andrej@1662: for imgname, itemtype in [ andrej@1766: ("CONFIGURATION", LOCATION_CONFNODE), andrej@1766: ("RESOURCE", LOCATION_MODULE), andrej@1766: ("PROGRAM", LOCATION_GROUP), andrej@1766: ("VAR_INPUT", LOCATION_VAR_INPUT), andrej@1766: ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), andrej@1766: ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: andrej@1662: self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname)) andrej@1662: andrej@1662: # Icons for other items andrej@1662: for imgname, itemtype in [ andrej@1769: ("Extension", ITEM_CONFNODE)]: andrej@1662: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) andrej@1662: andrej@1662: if projectOpen is not None: andrej@1662: projectOpen = DecodeFileSystemPath(projectOpen, False) andrej@1662: andrej@1662: if projectOpen is not None and os.path.isdir(projectOpen): andrej@1662: self.CTR = ProjectController(self, self.Log) andrej@1662: self.Controler = self.CTR andrej@1847: result, _err = self.CTR.LoadProject(projectOpen, buildpath) andrej@1662: if not result: andrej@1662: self.LibraryPanel.SetController(self.Controler) andrej@1662: self.ProjectTree.Enable(True) andrej@1662: self.PouInstanceVariablesPanel.SetController(self.Controler) andrej@1662: self.RefreshConfigRecentProjects(os.path.abspath(projectOpen)) Edouard@1933: self.RefreshAfterLoad() andrej@1662: else: andrej@1662: self.ResetView() andrej@1662: self.ShowErrorMessage(result) andrej@1662: else: andrej@1662: self.CTR = ctr andrej@1662: self.Controler = ctr andrej@1662: if ctr is not None: Edouard@1933: ctr.SetAppFrame(self, self.Log) andrej@1662: self.LibraryPanel.SetController(self.Controler) andrej@1662: self.ProjectTree.Enable(True) andrej@1662: self.PouInstanceVariablesPanel.SetController(self.Controler) Edouard@1933: self.RefreshAfterLoad() andrej@1662: if self.EnableDebug: andrej@1662: self.DebugVariablePanel.SetDataProducer(self.CTR) andrej@1662: andrej@1662: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) andrej@1662: self.RefreshAll() andrej@1662: self.LogConsole.SetFocus() andrej@1662: edouard@3712: signal.signal(signal.SIGTERM,self.signalTERM_handler) edouard@3712: andrej@1662: def RefreshTitle(self): andrej@1662: name = _("Beremiz") andrej@1662: if self.CTR is not None: andrej@1662: projectname = self.CTR.GetProjectName() andrej@1662: if self.CTR.ProjectTestModified(): andrej@1662: projectname = "~%s~" % projectname andrej@1662: self.SetTitle("%s - %s" % (name, projectname)) andrej@1662: else: andrej@1662: self.SetTitle(name) andrej@1662: andrej@1662: def OnOpenWidgetInspector(self, evt): andrej@1662: # Activate the widget inspection tool andrej@1662: from wx.lib.inspection import InspectionTool andrej@1662: if not InspectionTool().initialized: andrej@1662: InspectionTool().Init() andrej@1662: andrej@1662: # Find a widget to be selected in the tree. Use either the andrej@1662: # one under the cursor, if any, or this frame. andrej@1662: wnd = wx.FindWindowAtPointer() andrej@1662: if not wnd: andrej@1662: wnd = self andrej@1662: InspectionTool().Show(wnd, True) andrej@1662: andrej@1662: def OnLogConsoleFocusChanged(self, event): andrej@1662: self.RefreshEditMenu() andrej@1662: event.Skip() andrej@1662: andrej@1662: def OnLogConsoleUpdateUI(self, event): edouard@3564: if event.GetUpdated()==wx.stc.STC_UPDATE_SELECTION: edouard@3564: self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True) andrej@1662: event.Skip() andrej@1662: andrej@1662: def OnLogConsoleMarginClick(self, event): andrej@1662: line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) andrej@1662: wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx)) andrej@1662: event.Skip() andrej@1662: andrej@1662: def OnLogConsoleModified(self, event): andrej@1662: line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) andrej@1662: line = self.LogConsole.GetLine(line_idx) andrej@1662: if line: andrej@1662: result = MATIEC_ERROR_MODEL.match(line) andrej@1662: if result is not None: andrej@1662: self.LogConsole.MarkerAdd(line_idx, 0) andrej@1662: event.Skip() andrej@1662: andrej@1662: def SearchLineForError(self, line): andrej@1662: if self.CTR is not None: andrej@1662: result = MATIEC_ERROR_MODEL.match(line) andrej@1662: if result is not None: andrej@1847: first_line, first_column, last_line, last_column, _error = result.groups() andrej@1846: self.CTR.ShowError(self.Log, andrej@1846: (int(first_line), int(first_column)), andrej@1846: (int(last_line), int(last_column))) andrej@1662: andrej@1662: def CheckSaveBeforeClosing(self, title=_("Close Project")): andrej@1781: """Function displaying an Error dialog in PLCOpenEditor. andrej@1781: andrej@1781: :returns: False if closing cancelled. andrej@1781: """ andrej@1662: if self.CTR.ProjectTestModified(): andrej@1662: dialog = wx.MessageDialog(self, andrej@1662: _("There are changes, do you want to save?"), andrej@1662: title, andrej@1745: wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION) andrej@1662: answer = dialog.ShowModal() andrej@1662: dialog.Destroy() andrej@1662: if answer == wx.ID_YES: andrej@1662: self.CTR.SaveProject() andrej@1662: elif answer == wx.ID_CANCEL: andrej@1662: return False andrej@1662: kinsamanka@3750: for idx in range(self.TabsOpened.GetPageCount()): andrej@1662: window = self.TabsOpened.GetPage(idx) andrej@1662: if not window.CheckSaveBeforeClosing(): andrej@1662: return False andrej@1662: andrej@1662: return True andrej@1662: andrej@1662: def GetTabInfos(self, tab): andrej@1878: if isinstance(tab, EditorPanel) and \ andrej@1878: not isinstance(tab, (Viewer, andrej@1878: TextViewer, andrej@1878: ResourceEditor, andrej@1878: ConfigurationEditor, andrej@1878: DataTypeEditor)): andrej@1662: return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName()) andrej@1662: elif (isinstance(tab, TextViewer) and andrej@1662: (tab.Controler is None or isinstance(tab.Controler, MiniTextControler))): andrej@1662: return ("confnode", None, tab.GetInstancePath()) andrej@1662: else: andrej@1662: return IDEFrame.GetTabInfos(self, tab) andrej@1662: andrej@1662: def LoadTab(self, notebook, page_infos): andrej@1662: if page_infos[0] == "confnode": andrej@1662: if page_infos[1] is None: andrej@1662: confnode = self.CTR andrej@1662: else: andrej@1662: confnode = self.CTR.GetChildByName(page_infos[1]) andrej@1662: return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:])) andrej@1662: else: andrej@1662: return IDEFrame.LoadTab(self, notebook, page_infos) andrej@1662: andrej@1662: # Strange hack required by WAMP connector, using twisted. andrej@1662: # Twisted reactor needs to be stopped only before quit, andrej@1662: # since it cannot be restarted andrej@1662: ToDoBeforeQuit = [] andrej@1751: andrej@1662: def AddToDoBeforeQuit(self, Thing): andrej@1662: self.ToDoBeforeQuit.append(Thing) andrej@1662: Edouard@2483: def TryCloseFrame(self): andrej@1662: if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")): andrej@1662: if self.CTR is not None: andrej@1662: self.CTR.KillDebugThread() edouard@3348: self.CTR._Disconnect() andrej@1662: self.KillLocalRuntime() andrej@1662: andrej@1662: self.SaveLastState() andrej@1662: andrej@1739: for Thing in self.ToDoBeforeQuit: andrej@1662: Thing() andrej@1662: self.ToDoBeforeQuit = [] andrej@1662: Edouard@2483: return True Edouard@2483: return False Edouard@2483: Edouard@2483: def OnCloseFrame(self, event): Edouard@2483: if self.TryCloseFrame(): edouard@2624: self.LogConsole.Disconnect(-1, -1, wx.wxEVT_KILL_FOCUS) edouard@3350: super(Beremiz, self).OnCloseFrame(event) andrej@1662: event.Skip() andrej@1662: else: Edouard@2483: # prevent event to continue, i.e. cancel closing andrej@1662: event.Veto() andrej@1662: edouard@3712: def signalTERM_handler(self, sig, frame): edouard@3712: print ("Signal TERM caught: kill local runtime and quit, no save") edouard@3712: self.KillLocalRuntime() edouard@3712: sys.exit() edouard@3712: andrej@1662: def RefreshFileMenu(self): andrej@1662: self.RefreshRecentProjectsMenu() andrej@1662: andrej@1662: MenuToolBar = self.Panes["MenuToolBar"] andrej@1662: if self.CTR is not None: andrej@1662: selected = self.TabsOpened.GetSelection() andrej@1662: if selected >= 0: andrej@1662: window = self.TabsOpened.GetPage(selected) andrej@1662: viewer_is_modified = window.IsModified() andrej@1662: is_viewer = isinstance(window, Viewer) andrej@1662: else: andrej@1662: viewer_is_modified = is_viewer = False andrej@1662: if self.TabsOpened.GetPageCount() > 0: andrej@1662: self.FileMenu.Enable(wx.ID_CLOSE, True) andrej@1662: if is_viewer: andrej@1662: self.FileMenu.Enable(wx.ID_PREVIEW, True) andrej@1662: self.FileMenu.Enable(wx.ID_PRINT, True) andrej@1662: MenuToolBar.EnableTool(wx.ID_PRINT, True) andrej@1662: else: andrej@1662: self.FileMenu.Enable(wx.ID_PREVIEW, False) andrej@1662: self.FileMenu.Enable(wx.ID_PRINT, False) andrej@1662: MenuToolBar.EnableTool(wx.ID_PRINT, False) andrej@1662: else: andrej@1662: self.FileMenu.Enable(wx.ID_CLOSE, False) andrej@1662: self.FileMenu.Enable(wx.ID_PREVIEW, False) andrej@1662: self.FileMenu.Enable(wx.ID_PRINT, False) andrej@1662: MenuToolBar.EnableTool(wx.ID_PRINT, False) andrej@1662: self.FileMenu.Enable(wx.ID_PAGE_SETUP, True) andrej@1662: project_modified = self.CTR.ProjectTestModified() or viewer_is_modified andrej@1662: self.FileMenu.Enable(wx.ID_SAVE, project_modified) andrej@1662: MenuToolBar.EnableTool(wx.ID_SAVE, project_modified) andrej@1662: self.FileMenu.Enable(wx.ID_SAVEAS, True) andrej@1662: MenuToolBar.EnableTool(wx.ID_SAVEAS, True) andrej@1662: self.FileMenu.Enable(wx.ID_CLOSE_ALL, True) andrej@1662: else: andrej@1662: self.FileMenu.Enable(wx.ID_CLOSE, False) andrej@1662: self.FileMenu.Enable(wx.ID_PAGE_SETUP, False) andrej@1662: self.FileMenu.Enable(wx.ID_PREVIEW, False) andrej@1662: self.FileMenu.Enable(wx.ID_PRINT, False) andrej@1662: MenuToolBar.EnableTool(wx.ID_PRINT, False) andrej@1662: self.FileMenu.Enable(wx.ID_SAVE, False) andrej@1662: MenuToolBar.EnableTool(wx.ID_SAVE, False) andrej@1662: self.FileMenu.Enable(wx.ID_SAVEAS, False) andrej@1662: MenuToolBar.EnableTool(wx.ID_SAVEAS, False) andrej@1662: self.FileMenu.Enable(wx.ID_CLOSE_ALL, False) andrej@1662: andrej@1662: def RefreshRecentProjectsMenu(self): andrej@1662: try: kinsamanka@3750: recent_projects = list(map(DecodeFileSystemPath, kinsamanka@3750: self.GetConfigEntry("RecentProjects", []))) andrej@1780: except Exception: andrej@1662: recent_projects = [] andrej@1662: Edouard@2571: while self.RecentProjectsMenu.GetMenuItemCount() > 0: andrej@1662: item = self.RecentProjectsMenu.FindItemByPosition(0) edouard@3303: self.RecentProjectsMenu.Remove(item) andrej@1662: edouard@3833: self.RecentProjectsMenuItem.Enable(len(recent_projects) > 0) andrej@1662: for idx, projectpath in enumerate(recent_projects): kinsamanka@3750: text = '&%d: %s' % (idx + 1, projectpath) andrej@1662: Edouard@2571: item = self.RecentProjectsMenu.Append(wx.ID_ANY, text, '') Edouard@2569: self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), item) andrej@1662: andrej@1662: def GenerateOpenRecentProjectFunction(self, projectpath): andrej@1662: def OpenRecentProject(event): andrej@1662: if self.CTR is not None and not self.CheckSaveBeforeClosing(): andrej@1662: return andrej@1662: andrej@1662: self.OpenProject(projectpath) andrej@1662: return OpenRecentProject andrej@1662: andrej@1662: def GenerateMenuRecursive(self, items, menu): andrej@1662: for kind, infos in items: andrej@2450: if isinstance(kind, list): andrej@1662: text, id = infos andrej@1662: submenu = wx.Menu('') andrej@1662: self.GenerateMenuRecursive(kind, submenu) andrej@1662: menu.AppendMenu(id, text, submenu) andrej@1662: elif kind == wx.ITEM_SEPARATOR: andrej@1662: menu.AppendSeparator() andrej@1662: else: andrej@1847: text, id, _help, callback = infos andrej@1662: AppendMenu(menu, help='', id=id, kind=kind, text=text) andrej@1662: if callback is not None: andrej@1662: self.Bind(wx.EVT_MENU, callback, id=id) andrej@1662: andrej@1662: def RefreshEditorToolBar(self): andrej@1662: IDEFrame.RefreshEditorToolBar(self) andrej@1662: self.AUIManager.GetPane("EditorToolBar").Position(2) andrej@1662: self.AUIManager.GetPane("StatusToolBar").Position(1) andrej@1662: self.AUIManager.Update() andrej@1662: andrej@1662: def RefreshStatusToolBar(self): andrej@1662: StatusToolBar = self.Panes["StatusToolBar"] andrej@1662: StatusToolBar.ClearTools() andrej@2255: StatusToolBar.SetMinSize(StatusToolBar.GetToolBitmapSize()) andrej@1662: andrej@1662: if self.CTR is not None: andrej@1662: andrej@1662: for confnode_method in self.CTR.StatusMethods: andrej@1740: if "method" in confnode_method and confnode_method.get("shown", True): edouard@3303: tool = StatusToolBar.AddTool( edouard@3588: wx.ID_ANY, confnode_method["name"], edouard@3588: GetBitmap(confnode_method.get("bitmap", "Unknown")), edouard@3588: confnode_method["tooltip"]) Edouard@2566: self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), tool) andrej@1662: andrej@1662: StatusToolBar.Realize() andrej@1662: self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show() andrej@1662: else: andrej@1662: self.AUIManager.GetPane("StatusToolBar").Hide() andrej@1662: self.AUIManager.GetPane("EditorToolBar").Position(2) andrej@1662: self.AUIManager.GetPane("StatusToolBar").Position(1) andrej@1662: self.AUIManager.Update() andrej@1662: andrej@1662: def RefreshEditMenu(self): andrej@1662: IDEFrame.RefreshEditMenu(self) andrej@1662: if self.FindFocus() == self.LogConsole: andrej@1662: self.EditMenu.Enable(wx.ID_COPY, True) andrej@1662: self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True) andrej@1662: andrej@1662: if self.CTR is not None: andrej@1662: selected = self.TabsOpened.GetSelection() andrej@1662: if selected >= 0: andrej@1662: panel = self.TabsOpened.GetPage(selected) andrej@1662: else: andrej@1662: panel = None andrej@1662: if panel != self.LastPanelSelected: kinsamanka@3750: for i in range(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): andrej@1662: item = self.EditMenu.FindItemByPosition(self.EditMenuSize) andrej@1662: if item is not None: andrej@1662: if item.IsSeparator(): andrej@1662: self.EditMenu.RemoveItem(item) andrej@1662: else: andrej@1662: self.EditMenu.Delete(item.GetId()) andrej@1662: self.LastPanelSelected = panel andrej@1662: if panel is not None: andrej@1662: items = panel.GetConfNodeMenuItems() andrej@1662: else: andrej@1662: items = [] andrej@1662: if len(items) > 0: andrej@1662: self.EditMenu.AppendSeparator() andrej@1662: self.GenerateMenuRecursive(items, self.EditMenu) andrej@1662: if panel is not None: andrej@1662: panel.RefreshConfNodeMenu(self.EditMenu) andrej@1662: else: kinsamanka@3750: for i in range(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): andrej@1662: item = self.EditMenu.FindItemByPosition(i) andrej@1662: if item is not None: andrej@1662: if item.IsSeparator(): andrej@1662: self.EditMenu.RemoveItem(item) andrej@1662: else: andrej@1662: self.EditMenu.Delete(item.GetId()) andrej@1662: self.LastPanelSelected = None edouard@3303: self.MenuBar.Refresh() andrej@1662: andrej@1662: def RefreshAll(self): andrej@1662: self.RefreshStatusToolBar() andrej@1662: andrej@1662: def GetMenuCallBackFunction(self, method): andrej@1662: """ Generate the callbackfunc for a given CTR method""" andrej@1662: def OnMenu(event): andrej@1662: # Disable button to prevent re-entrant call andrej@1662: event.GetEventObject().Disable() andrej@1662: # Call andrej@1662: getattr(self.CTR, method)() andrej@1662: # Re-enable button andrej@1662: event.GetEventObject().Enable() andrej@1662: return OnMenu andrej@1662: andrej@1662: def GetConfigEntry(self, entry_name, default): kinsamanka@3758: return pickle.loads(self.Config.Read(entry_name, kinsamanka@3758: pickle.dumps(default, kinsamanka@3758: 0).decode()).encode()) andrej@1662: andrej@1662: def ResetConnectionStatusBar(self): kinsamanka@3750: for field in range(self.ConnectionStatusBar.GetFieldsCount()): andrej@1662: self.ConnectionStatusBar.SetStatusText('', field) andrej@1662: andrej@1662: def ResetView(self): andrej@1662: IDEFrame.ResetView(self) andrej@1662: if self.CTR is not None: andrej@1662: self.CTR.CloseProject() andrej@1662: self.CTR = None andrej@1662: self.Log.flush() andrej@1662: if self.EnableDebug: andrej@1662: self.DebugVariablePanel.SetDataProducer(None) andrej@1662: self.ResetConnectionStatusBar() andrej@1662: andrej@1662: def RefreshConfigRecentProjects(self, projectpath, err=False): andrej@1662: try: kinsamanka@3750: recent_projects = list(map(DecodeFileSystemPath, kinsamanka@3750: self.GetConfigEntry("RecentProjects", []))) andrej@1780: except Exception: andrej@1662: recent_projects = [] andrej@1662: if projectpath in recent_projects: andrej@1662: recent_projects.remove(projectpath) andrej@1662: if not err: andrej@1662: recent_projects.insert(0, projectpath) kinsamanka@3758: self.Config.Write("RecentProjects", kinsamanka@3758: pickle.dumps( kinsamanka@3758: list(map(EncodeFileSystemPath, kinsamanka@3758: recent_projects[:MAX_RECENT_PROJECTS])), kinsamanka@3758: 0)) andrej@1662: self.Config.Flush() andrej@1662: andrej@1662: def ResetPerspective(self): andrej@1662: IDEFrame.ResetPerspective(self) andrej@1662: self.RefreshStatusToolBar() andrej@1662: andrej@1662: def OnNewProjectMenu(self, event): andrej@1662: if self.CTR is not None and not self.CheckSaveBeforeClosing(): andrej@1662: return andrej@1662: andrej@1662: try: kinsamanka@3755: defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder").encode()) andrej@1780: except Exception: andrej@1662: defaultpath = os.path.expanduser("~") andrej@1662: andrej@1739: dialog = wx.DirDialog(self, _("Choose an empty directory for new project"), defaultpath) andrej@1662: if dialog.ShowModal() == wx.ID_OK: andrej@1662: projectpath = dialog.GetPath() andrej@1662: self.Config.Write("lastopenedfolder", andrej@1662: EncodeFileSystemPath(os.path.dirname(projectpath))) andrej@1662: self.Config.Flush() andrej@1662: self.ResetView() andrej@1662: ctr = ProjectController(self, self.Log) andrej@1662: result = ctr.NewProject(projectpath) andrej@1662: if not result: andrej@1662: self.CTR = ctr andrej@1662: self.Controler = self.CTR andrej@1662: self.LibraryPanel.SetController(self.Controler) andrej@1662: self.ProjectTree.Enable(True) andrej@1662: self.PouInstanceVariablesPanel.SetController(self.Controler) andrej@1662: self.RefreshConfigRecentProjects(projectpath) andrej@1662: if self.EnableDebug: andrej@1662: self.DebugVariablePanel.SetDataProducer(self.CTR) Edouard@1933: self.RefreshAfterLoad() surkovsv93@1708: IDEFrame.OnAddNewProject(self, event) andrej@1662: else: andrej@1662: self.ResetView() andrej@1662: self.ShowErrorMessage(result) andrej@1662: self.RefreshAll() andrej@1662: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) andrej@1662: dialog.Destroy() andrej@1662: andrej@1662: def OnOpenProjectMenu(self, event): andrej@1662: if self.CTR is not None and not self.CheckSaveBeforeClosing(): andrej@1662: return andrej@1662: andrej@1662: try: andrej@1662: defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) andrej@1780: except Exception: andrej@1662: defaultpath = os.path.expanduser("~") andrej@1662: andrej@1767: dialog = wx.DirDialog(self, _("Choose a project"), defaultpath, andrej@1767: style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) andrej@1662: if dialog.ShowModal() == wx.ID_OK: andrej@1662: self.OpenProject(dialog.GetPath()) andrej@1662: dialog.Destroy() andrej@1662: andrej@1662: def OpenProject(self, projectpath): andrej@1662: if os.path.isdir(projectpath): andrej@1662: self.Config.Write("lastopenedfolder", andrej@1662: EncodeFileSystemPath(os.path.dirname(projectpath))) andrej@1662: self.Config.Flush() andrej@1662: self.ResetView() andrej@1662: self.CTR = ProjectController(self, self.Log) andrej@1662: self.Controler = self.CTR andrej@1662: result, err = self.CTR.LoadProject(projectpath) andrej@1662: if not result: andrej@1662: self.LibraryPanel.SetController(self.Controler) andrej@1662: self.ProjectTree.Enable(True) andrej@1662: self.PouInstanceVariablesPanel.SetController(self.Controler) andrej@1662: if self.EnableDebug: andrej@1662: self.DebugVariablePanel.SetDataProducer(self.CTR) Edouard@1933: self.RefreshAfterLoad() andrej@1662: else: andrej@1662: self.ResetView() andrej@1662: self.ShowErrorMessage(result) andrej@1662: self.RefreshAll() andrej@1662: self.SearchResultPanel.ResetSearchResults() andrej@1662: else: andrej@1662: self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) andrej@1662: err = True andrej@1662: self.RefreshConfigRecentProjects(projectpath, err) andrej@1662: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) andrej@1662: andrej@1662: def OnCloseProjectMenu(self, event): andrej@1662: if self.CTR is not None and not self.CheckSaveBeforeClosing(): andrej@1662: return andrej@1662: andrej@1662: self.ResetView() andrej@1662: self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) andrej@1662: self.RefreshAll() andrej@1662: Edouard@1933: def RefreshAfterLoad(self): Edouard@1933: self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) Edouard@1933: Edouard@1927: def RefreshAfterSave(self): Edouard@1927: self.RefreshAll() Edouard@1927: self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) Edouard@1927: andrej@1662: def OnSaveProjectMenu(self, event): andrej@1662: selected = self.TabsOpened.GetSelection() andrej@1662: if selected != -1: andrej@1662: window = self.TabsOpened.GetPage(selected) andrej@1662: window.Save() andrej@1662: if self.CTR is not None: andrej@1662: self.CTR.SaveProject() Edouard@1927: self.RefreshAfterSave() andrej@1662: andrej@1662: def OnSaveProjectAsMenu(self, event): andrej@1662: selected = self.TabsOpened.GetSelection() andrej@1662: if selected != -1: andrej@1662: window = self.TabsOpened.GetPage(selected) andrej@1662: window.SaveAs() andrej@1662: if self.CTR is not None: andrej@1662: self.CTR.SaveProjectAs() Edouard@1927: self.RefreshAfterSave() andrej@1662: self.RefreshConfigRecentProjects(self.CTR.ProjectPath) andrej@1662: andrej@1662: def OnQuitMenu(self, event): andrej@1662: self.Close() andrej@1662: andrej@1662: def OnAboutMenu(self, event): edouard@3570: info = wx.adv.AboutDialogInfo() edouard@3570: info = version.GetAboutDialogInfo(info) edouard@3570: info.Name = "Beremiz" edouard@3909: try: edouard@3909: with open(ThirdPartyPath("revisions.txt")) as f: edouard@3909: revisions=f.read() edouard@3909: except Exception as e: edouard@3909: revisions="Can't load revisions.txt: "+str(e) edouard@3909: finally: edouard@3906: info.SetVersion(info.GetVersion(), longVersion=revisions) edouard@3906: edouard@3570: info.Description = _("Open Source framework for automation, " edouard@3586: "implementing IEC 61131 IDE with constantly growing set of extensions " edouard@3570: "and flexible PLC runtime.") edouard@3570: edouard@3570: info.Icon = wx.Icon(Bpath("images", "about_brz_logo.png"), wx.BITMAP_TYPE_PNG) edouard@3570: andrej@1662: ShowAboutDialog(self, info) andrej@1662: andrej@1662: def OnProjectTreeItemBeginEdit(self, event): andrej@1662: selected = event.GetItem() kinsamanka@3789: if self.ProjectTree.GetItemData(selected)["type"] == ITEM_CONFNODE: andrej@1662: event.Veto() andrej@1662: else: andrej@1662: IDEFrame.OnProjectTreeItemBeginEdit(self, event) andrej@1662: andrej@1662: def OnProjectTreeRightUp(self, event): andrej@1662: item = event.GetItem() kinsamanka@3789: item_infos = self.ProjectTree.GetItemData(item) andrej@1662: andrej@1662: if item_infos["type"] == ITEM_CONFNODE: andrej@1662: confnode_menu = wx.Menu(title='') andrej@1662: andrej@1662: confnode = item_infos["confnode"] andrej@1662: if confnode is not None: andrej@1662: menu_items = confnode.GetContextualMenuItems() andrej@1662: if menu_items is not None: Edouard@2569: for text, helpstr, callback in menu_items: Edouard@2569: item = confnode_menu.Append(wx.ID_ANY, text, helpstr) Edouard@2569: self.Bind(wx.EVT_MENU, callback, item) andrej@1662: else: Edouard@2569: for name, XSDClass, helpstr in confnode.CTNChildrenTypes: surkovsv93@1684: if not hasattr(XSDClass, 'CTNMaxCount') or not confnode.Children.get(name) \ surkovsv93@1684: or len(confnode.Children[name]) < XSDClass.CTNMaxCount: Edouard@2569: item = confnode_menu.Append(wx.ID_ANY, _("Add") + " " + name, helpstr) Edouard@2569: self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name, confnode), item) Edouard@2569: item = confnode_menu.Append(wx.ID_ANY, _("Delete")) Edouard@2569: self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), item) andrej@1662: andrej@1662: self.PopupMenu(confnode_menu) andrej@1662: confnode_menu.Destroy() andrej@1662: andrej@1662: event.Skip() andrej@1662: elif item_infos["type"] == ITEM_RESOURCE: andrej@1662: # prevent last resource to be delted andrej@1662: parent = self.ProjectTree.GetItemParent(item) andrej@1662: parent_name = self.ProjectTree.GetItemText(parent) andrej@1662: if parent_name == _("Resources"): andrej@1662: IDEFrame.OnProjectTreeRightUp(self, event) andrej@1662: else: andrej@1662: IDEFrame.OnProjectTreeRightUp(self, event) andrej@1662: andrej@1662: def OnProjectTreeItemActivated(self, event): andrej@1662: selected = event.GetItem() kinsamanka@3789: item_infos = self.ProjectTree.GetItemData(selected) andrej@1662: if item_infos["type"] == ITEM_CONFNODE: andrej@1662: item_infos["confnode"]._OpenView() andrej@1662: event.Skip() andrej@1662: elif item_infos["type"] == ITEM_PROJECT: andrej@1662: self.CTR._OpenView() andrej@1662: else: andrej@1662: IDEFrame.OnProjectTreeItemActivated(self, event) andrej@1662: andrej@1662: def ProjectTreeItemSelect(self, select_item): andrej@1662: if select_item is not None and select_item.IsOk(): kinsamanka@3789: item_infos = self.ProjectTree.GetItemData(select_item) andrej@1662: if item_infos["type"] == ITEM_CONFNODE: andrej@1662: item_infos["confnode"]._OpenView(onlyopened=True) andrej@1662: elif item_infos["type"] == ITEM_PROJECT: andrej@1662: self.CTR._OpenView(onlyopened=True) andrej@1662: else: andrej@1662: IDEFrame.ProjectTreeItemSelect(self, select_item) andrej@1662: Edouard@2524: def GetProjectElementWindow(self, element, tagname): Edouard@2551: is_a_CTN_tagname = len(tagname.split("::")) == 1 Edouard@2524: if is_a_CTN_tagname: Edouard@2524: confnode = self.CTR.GetChildByName(tagname) Edouard@2551: return confnode.GetView() Edouard@2551: else: Edouard@2524: return IDEFrame.GetProjectElementWindow(self, element, tagname) Edouard@2524: andrej@1662: def SelectProjectTreeItem(self, tagname): andrej@1662: if self.ProjectTree is not None: andrej@1662: root = self.ProjectTree.GetRootItem() andrej@1662: if root.IsOk(): andrej@1662: words = tagname.split("::") andrej@1662: if len(words) == 1: andrej@1662: if tagname == "Project": andrej@1662: self.SelectedItem = root andrej@1662: self.ProjectTree.SelectItem(root) andrej@1662: self.ResetSelectedItem() andrej@1662: else: andrej@1768: return self.RecursiveProjectTreeItemSelection( andrej@1768: root, andrej@1768: [(word, ITEM_CONFNODE) for word in tagname.split(".")]) andrej@1662: elif words[0] == "R": andrej@1662: return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)]) andrej@1662: elif not os.path.exists(words[0]): andrej@1662: IDEFrame.SelectProjectTreeItem(self, tagname) andrej@1662: andrej@1662: def GetAddConfNodeFunction(self, name, confnode=None): andrej@1662: def AddConfNodeMenuFunction(event): andrej@1662: wx.CallAfter(self.AddConfNode, name, confnode) andrej@1662: return AddConfNodeMenuFunction andrej@1662: andrej@1662: def GetDeleteMenuFunction(self, confnode): andrej@1662: def DeleteMenuFunction(event): andrej@1662: wx.CallAfter(self.DeleteConfNode, confnode) andrej@1662: return DeleteMenuFunction andrej@1662: andrej@1662: def AddConfNode(self, ConfNodeType, confnode=None): andrej@1662: if self.CTR.CheckProjectPathPerm(): andrej@1662: ConfNodeName = "%s_0" % ConfNodeType andrej@1662: if confnode is not None: andrej@1662: confnode.CTNAddChild(ConfNodeName, ConfNodeType) andrej@1662: else: andrej@1662: self.CTR.CTNAddChild(ConfNodeName, ConfNodeType) andrej@1662: self._Refresh(TITLE, FILEMENU, PROJECTTREE) andrej@1662: andrej@1662: def DeleteConfNode(self, confnode): andrej@1662: if self.CTR.CheckProjectPathPerm(): andrej@1768: dialog = wx.MessageDialog( andrej@1768: self, andrej@1662: _("Really delete node '%s'?") % confnode.CTNName(), andrej@1662: _("Remove %s node") % confnode.CTNType, andrej@1745: wx.YES_NO | wx.NO_DEFAULT) andrej@1662: if dialog.ShowModal() == wx.ID_YES: andrej@1662: confnode.CTNRemove() andrej@1662: del confnode andrej@1662: self._Refresh(TITLE, FILEMENU, PROJECTTREE) andrej@1662: dialog.Destroy() andrej@1662: andrej@1782: # ------------------------------------------------------------------------------- andrej@1662: # Highlights showing functions andrej@1782: # ------------------------------------------------------------------------------- andrej@1662: andrej@1662: def ShowHighlight(self, infos, start, end, highlight_type): andrej@1662: config_name = self.Controler.GetProjectMainConfigurationName() Edouard@1948: if config_name is not None and infos[0] == ComputeConfigurationName(config_name): andrej@1662: self.CTR._OpenView() andrej@1662: selected = self.TabsOpened.GetSelection() andrej@1662: if selected != -1: andrej@1662: viewer = self.TabsOpened.GetPage(selected) andrej@1662: viewer.AddHighlight(infos[1:], start, end, highlight_type) andrej@1662: else: andrej@1662: IDEFrame.ShowHighlight(self, infos, start, end, highlight_type)