diff -r 1460273f40ed -r 5743cbdff669 IDEFrame.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IDEFrame.py Fri Sep 07 16:45:55 2012 +0200 @@ -0,0 +1,2553 @@ + +import os, sys +import cPickle +from types import TupleType + +import wx, wx.grid +import wx.aui + +from editors.EditorPanel import EditorPanel +from editors.SFCViewer import SFC_Viewer +from editors.LDViewer import LD_Viewer +from editors.TextViewer import TextViewer +from editors.Viewer import Viewer, ZOOM_FACTORS +from editors.GraphicViewer import GraphicViewer +from editors.ResourceEditor import ConfigurationEditor, ResourceEditor +from editors.DataTypeEditor import DataTypeEditor +from PLCControler import * +from controls import CustomTree, LibraryPanel, PouInstanceVariablesPanel, DebugVariablePanel, SearchResultPanel +from dialogs import ProjectDialog, PouTransitionDialog, PouActionDialog, FindInPouDialog +from util.BitmapLibrary import GetBitmap + +# Define PLCOpenEditor controls id +[ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK, + ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK, + ID_PLCOPENEDITORPROJECTTREE, ID_PLCOPENEDITORMAINSPLITTER, + ID_PLCOPENEDITORSECONDSPLITTER, ID_PLCOPENEDITORTHIRDSPLITTER, + ID_PLCOPENEDITORLIBRARYPANEL, ID_PLCOPENEDITORLIBRARYSEARCHCTRL, + ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT, + ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED, + ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR, + ID_PLCOPENEDITORPROJECTPANEL, +] = [wx.NewId() for _init_ctrls in range(17)] + +# Define PLCOpenEditor EditMenu extra items id +[ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE, + ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, + ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, + ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, + ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, +] = [wx.NewId() for _init_coll_EditMenu_Items in range(9)] + +# Define PLCOpenEditor DisplayMenu extra items id +[ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, +] = [wx.NewId() for _init_coll_DisplayMenu_Items in range(1)] + +#------------------------------------------------------------------------------- +# EditorToolBar definitions +#------------------------------------------------------------------------------- + +# Define PLCOpenEditor Toolbar items id +[ID_PLCOPENEDITOREDITORTOOLBARSELECTION, ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, ID_PLCOPENEDITOREDITORTOOLBARBLOCK, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, ID_PLCOPENEDITOREDITORTOOLBARWIRE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITOREDITORTOOLBARRUNG, + ID_PLCOPENEDITOREDITORTOOLBARCOIL, ID_PLCOPENEDITOREDITORTOOLBARCONTACT, + ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, + ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, + ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, + ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION, +] = [wx.NewId() for _init_coll_DefaultEditorToolBar_Items in range(18)] + + + +# Define behaviour of each Toolbar item according to current POU body type +# Informations meaning are in this order: +# - Item is toggled +# - PLCOpenEditor mode where item is displayed (could be more then one) +# - Item id +# - Item callback function name +# - Item icon filename +# - Item tooltip text +EditorToolBarItems = { + "FBD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view")), + (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", + "add_connection", _("Create a new connection"))], + "LD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", + "add_powerrail", _("Create a new power rail")), + (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool", + "add_rung", _("Create a new rung")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool", + "add_coil", _("Create a new coil")), + (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", + "add_contact", _("Create a new contact")), + (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool", + "add_branch", _("Create a new branch")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", + "add_connection", _("Create a new connection"))], + "SFC" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view")), + (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool", + "add_initial_step", _("Create a new initial step")), + (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool", + "add_step", _("Create a new step")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool", + "add_transition", _("Create a new transition")), + (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", + "add_action", _("Create a new action block")), + (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool", + "add_divergence", _("Create a new divergence")), + (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool", + "add_jump", _("Create a new jump")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", + "add_connection", _("Create a new connection")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", + "add_powerrail", _("Create a new power rail")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", + "add_contact", _("Create a new contact"))], + "ST" : [], + "IL" : [], + "debug": [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view"))], +} + +#------------------------------------------------------------------------------- +# Helper Functions +#------------------------------------------------------------------------------- + +import base64 + +def EncodeFileSystemPath(path, use_base64=True): + path = path.encode(sys.getfilesystemencoding()) + if use_base64: + return base64.encodestring(path) + return path + +def DecodeFileSystemPath(path, is_base64=True): + if is_base64: + path = base64.decodestring(path) + return unicode(path, sys.getfilesystemencoding()) + +# Compatibility function for wx versions < 2.6 +def AppendMenu(parent, help, id, kind, text): + if wx.VERSION >= (2, 6, 0): + parent.Append(help=help, id=id, kind=kind, text=text) + else: + parent.Append(helpString=help, id=id, kind=kind, item=text) + +[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES +] = range(10) + +def GetShortcutKeyCallbackFunction(viewer_function): + def ShortcutKeyFunction(self, event): + control = self.FindFocus() + if control is not None and control.GetName() in ["Viewer", "TextViewer"]: + getattr(control.ParentWindow, viewer_function)() + elif isinstance(control, wx.stc.StyledTextCtrl): + getattr(control, viewer_function)() + elif isinstance(control, wx.TextCtrl): + control.ProcessEvent(event) + return ShortcutKeyFunction + +def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None): + def DeleteElementFunction(self, selected): + name = self.ProjectTree.GetItemText(selected) + if check_function is None or not check_function(self.Controler, name): + if parent_type is not None: + item_infos = self.ProjectTree.GetPyData(selected) + parent_name = item_infos["tagname"].split("::")[1] + remove_function(self.Controler, parent_name, name) + else: + remove_function(self.Controler, name) + else: + self.ShowErrorMessage(_("\"%s\" is used by one or more POUs. It can't be removed!")%name) + return DeleteElementFunction + +if wx.Platform == '__WXMSW__': + TAB_BORDER = 6 + NOTEBOOK_BORDER = 6 +else: + TAB_BORDER = 7 + NOTEBOOK_BORDER = 2 + +def SimplifyTabLayout(tabs, rect): + for tab in tabs: + if tab["pos"][0] == rect.x: + others = [t for t in tabs if t != tab] + others.sort(lambda x,y: cmp(x["pos"][0], y["pos"][0])) + for other in others: + if (other["pos"][1] == tab["pos"][1] and + other["size"][1] == tab["size"][1] and + other["pos"][0] == tab["pos"][0] + tab["size"][0] + TAB_BORDER): + + tab["size"] = (tab["size"][0] + other["size"][0] + TAB_BORDER, tab["size"][1]) + tab["pages"].extend(other["pages"]) + tabs.remove(other) + + if tab["size"][0] == rect.width: + return True + + elif tab["pos"][1] == rect.y: + others = [t for t in tabs if t != tab] + others.sort(lambda x,y: cmp(x["pos"][1], y["pos"][1])) + for other in others: + if (other["pos"][0] == tab["pos"][0] and + other["size"][0] == tab["size"][0] and + other["pos"][1] == tab["pos"][1] + tab["size"][1] + TAB_BORDER): + + tab["size"] = (tab["size"][0], tab["size"][1] + other["size"][1] + TAB_BORDER) + tab["pages"].extend(other["pages"]) + tabs.remove(other) + + if tab["size"][1] == rect.height: + return True + return False + +def ComputeTabsLayout(tabs, rect): + if len(tabs) == 0: + return tabs + if len(tabs) == 1: + return tabs[0] + split = None + for idx, tab in enumerate(tabs): + if len(tab["pages"]) == 0: + raise ValueError, "Not possible" + if tab["size"][0] == rect.width: + if tab["pos"][1] == rect.y: + split = (wx.TOP, float(tab["size"][1]) / float(rect.height)) + split_rect = wx.Rect(rect.x, rect.y + tab["size"][1] + TAB_BORDER, + rect.width, rect.height - tab["size"][1] - TAB_BORDER) + elif tab["pos"][1] == rect.height + 1 - tab["size"][1]: + split = (wx.BOTTOM, 1.0 - float(tab["size"][1]) / float(rect.height)) + split_rect = wx.Rect(rect.x, rect.y, + rect.width, rect.height - tab["size"][1] - TAB_BORDER) + break + elif tab["size"][1] == rect.height: + if tab["pos"][0] == rect.x: + split = (wx.LEFT, float(tab["size"][0]) / float(rect.width)) + split_rect = wx.Rect(rect.x + tab["size"][0] + TAB_BORDER, rect.y, + rect.width - tab["size"][0] - TAB_BORDER, rect.height) + elif tab["pos"][0] == rect.width + 1 - tab["size"][0]: + split = (wx.RIGHT, 1.0 - float(tab["size"][0]) / float(rect.width)) + split_rect = wx.Rect(rect.x, rect.y, + rect.width - tab["size"][0] - TAB_BORDER, rect.height) + break + if split != None: + split_tab = tabs.pop(idx) + return {"split": split, + "tab": split_tab, + "others": ComputeTabsLayout(tabs, split_rect)} + else: + if SimplifyTabLayout(tabs, rect): + return ComputeTabsLayout(tabs, rect) + return tabs + +#------------------------------------------------------------------------------- +# IDEFrame Base Class +#------------------------------------------------------------------------------- + +UNEDITABLE_NAMES_DICT = dict([(_(name), name) for name in UNEDITABLE_NAMES]) + +class IDEFrame(wx.Frame): + + Starting = False + + # Compatibility function for wx versions < 2.6 + if wx.VERSION < (2, 6, 0): + def Bind(self, event, function, id = None): + if id is not None: + event(self, id, function) + else: + event(self, function) + + def _init_coll_MenuBar_Menus(self, parent): + parent.Append(menu=self.FileMenu, title=_(u'&File')) + parent.Append(menu=self.EditMenu, title=_(u'&Edit')) + parent.Append(menu=self.DisplayMenu, title=_(u'&Display')) + parent.Append(menu=self.HelpMenu, title=_(u'&Help')) + + def _init_coll_FileMenu_Items(self, parent): + pass + + def _init_coll_AddMenu_Items(self, parent, add_config=True): + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE, + kind=wx.ITEM_NORMAL, text=_(u'&Data Type')) + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION, + kind=wx.ITEM_NORMAL, text=_(u'&Function')) + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, + kind=wx.ITEM_NORMAL, text=_(u'Function &Block')) + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM, + kind=wx.ITEM_NORMAL, text=_(u'&Program')) + if add_config: + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, + kind=wx.ITEM_NORMAL, text=_(u'&Configuration')) + + def _init_coll_EditMenu_Items(self, parent): + AppendMenu(parent, help='', id=wx.ID_UNDO, + kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z') + AppendMenu(parent, help='', id=wx.ID_REDO, + kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y') + #AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, + # kind=wx.ITEM_CHECK, text=_(u'Enable Undo/Redo')) + enable_undo_redo = _(u'Enable Undo/Redo') # Keeping text in translations for possible menu reactivation + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_CUT, + kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X') + AppendMenu(parent, help='', id=wx.ID_COPY, + kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C') + AppendMenu(parent, help='', id=wx.ID_PASTE, + kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V') + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_FIND, + kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F') + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDNEXT, + kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K') + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, + kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K') + parent.AppendSeparator() + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, + kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F') + parent.AppendSeparator() + add_menu = wx.Menu(title='') + self._init_coll_AddMenu_Items(add_menu) + parent.AppendMenu(wx.ID_ADD, _(u"&Add Element"), add_menu) + AppendMenu(parent, help='', id=wx.ID_SELECTALL, + kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A') + AppendMenu(parent, help='', id=wx.ID_DELETE, + kind=wx.ITEM_NORMAL, text=_(u'&Delete')) + self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO) + self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO) + #self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO) + self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wx.ID_CUT) + self.Bind(wx.EVT_MENU, self.OnCopyMenu, id=wx.ID_COPY) + self.Bind(wx.EVT_MENU, self.OnPasteMenu, id=wx.ID_PASTE) + self.Bind(wx.EVT_MENU, self.OnFindMenu, id=wx.ID_FIND) + self.Bind(wx.EVT_MENU, self.OnFindNextMenu, + id=ID_PLCOPENEDITOREDITMENUFINDNEXT) + self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu, + id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS) + self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, + id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) + self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, + id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) + self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, + id=ID_PLCOPENEDITOREDITMENUADDDATATYPE) + self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("function"), + id=ID_PLCOPENEDITOREDITMENUADDFUNCTION) + self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("functionBlock"), + id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK) + self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("program"), + id=ID_PLCOPENEDITOREDITMENUADDPROGRAM) + self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, + id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION) + self.Bind(wx.EVT_MENU, self.OnSelectAllMenu, id=wx.ID_SELECTALL) + self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=wx.ID_DELETE) + + self.AddToMenuToolBar([(wx.ID_UNDO, "undo", _(u'Undo'), None), + (wx.ID_REDO, "redo", _(u'Redo'), None), + None, + (wx.ID_CUT, "cut", _(u'Cut'), None), + (wx.ID_COPY, "copy", _(u'Copy'), None), + (wx.ID_PASTE, "paste", _(u'Paste'), None), + None, + (ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, "find", _(u'Search in Project'), None)]) + + def _init_coll_DisplayMenu_Items(self, parent): + AppendMenu(parent, help='', id=wx.ID_REFRESH, + kind=wx.ITEM_NORMAL, text=_(u'Refresh') + '\tCTRL+R') + if self.EnableDebug: + AppendMenu(parent, help='', id=wx.ID_CLEAR, + kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K') + parent.AppendSeparator() + zoommenu = wx.Menu(title='') + parent.AppendMenu(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu) + for idx, value in enumerate(ZOOM_FACTORS): + new_id = wx.NewId() + AppendMenu(zoommenu, help='', id=new_id, + kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%") + self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), id=new_id) + + parent.AppendSeparator() + AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, + kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective')) + self.Bind(wx.EVT_MENU, self.OnResetPerspective, id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE) + + self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH) + if self.EnableDebug: + self.Bind(wx.EVT_MENU, self.OnClearErrorsMenu, id=wx.ID_CLEAR) + + def _init_coll_HelpMenu_Items(self, parent): + pass + + def _init_utils(self): + self.MenuBar = wx.MenuBar() + + self.FileMenu = wx.Menu(title='') + self.EditMenu = wx.Menu(title='') + self.DisplayMenu = wx.Menu(title='') + self.HelpMenu = wx.Menu(title='') + + self._init_coll_MenuBar_Menus(self.MenuBar) + self._init_coll_FileMenu_Items(self.FileMenu) + self._init_coll_EditMenu_Items(self.EditMenu) + self._init_coll_DisplayMenu_Items(self.DisplayMenu) + self._init_coll_HelpMenu_Items(self.HelpMenu) + + def _init_ctrls(self, prnt): + wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name='IDEFrame', + parent=prnt, pos=wx.DefaultPosition, size=wx.Size(1000, 600), + style=wx.DEFAULT_FRAME_STYLE) + self.SetClientSize(wx.Size(1000, 600)) + self.Bind(wx.EVT_ACTIVATE, self.OnActivated) + self.Bind(wx.EVT_SIZE, self.OnResize) + + self.TabsImageList = wx.ImageList(31, 16) + self.TabsImageListIndexes = {} + + #----------------------------------------------------------------------- + # Creating main structure + #----------------------------------------------------------------------- + + self.AUIManager = wx.aui.AuiManager(self) + self.AUIManager.SetDockSizeConstraint(0.5, 0.5) + self.Panes = {} + + self.LeftNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORLEFTNOTEBOOK, + style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE| + wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE) + self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, + self.OnAllowNotebookDnD) + self.AUIManager.AddPane(self.LeftNoteBook, + wx.aui.AuiPaneInfo().Name("ProjectPane"). + Left().Layer(1). + BestSize(wx.Size(300, 500)).CloseButton(False)) + + self.BottomNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORBOTTOMNOTEBOOK, + style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE| + wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE) + self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, + self.OnAllowNotebookDnD) + self.AUIManager.AddPane(self.BottomNoteBook, + wx.aui.AuiPaneInfo().Name("ResultPane"). + Bottom().Layer(0). + BestSize(wx.Size(800, 300)).CloseButton(False)) + + self.RightNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORRIGHTNOTEBOOK, + style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE| + wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE) + self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, + self.OnAllowNotebookDnD) + self.AUIManager.AddPane(self.RightNoteBook, + wx.aui.AuiPaneInfo().Name("LibraryPane"). + Right().Layer(0). + BestSize(wx.Size(250, 400)).CloseButton(False)) + + self.TabsOpened = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORTABSOPENED, + style=wx.aui.AUI_NB_DEFAULT_STYLE|wx.aui.AUI_NB_WINDOWLIST_BUTTON) + self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGING, + self.OnPouSelectedChanging) + self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, + self.OnPouSelectedChanged) + self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, + self.OnPageClose) + self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG, + self.OnPageDragged) + self.AUIManager.AddPane(self.TabsOpened, + wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane")) + + #----------------------------------------------------------------------- + # Creating PLCopen Project Types Tree + #----------------------------------------------------------------------- + + self.MainTabs = {} + + self.ProjectPanel = wx.SplitterWindow(id=ID_PLCOPENEDITORPROJECTPANEL, + name='ProjectPanel', parent=self.LeftNoteBook, point=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.SP_3D) + + self.ProjectTree = CustomTree(id=ID_PLCOPENEDITORPROJECTTREE, + name='ProjectTree', parent=self.ProjectPanel, + pos=wx.Point(0, 0), size=wx.Size(0, 0), + style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_EDIT_LABELS) + self.ProjectTree.SetBackgroundBitmap(GetBitmap("custom_tree_background"), + wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM) + add_menu = wx.Menu() + self._init_coll_AddMenu_Items(add_menu) + self.ProjectTree.SetAddMenu(add_menu) + if wx.Platform == '__WXMSW__': + self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnProjectTreeRightUp, + id=ID_PLCOPENEDITORPROJECTTREE) + self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnProjectTreeItemSelected, + id=ID_PLCOPENEDITORPROJECTTREE) + else: + self.ProjectTree.Bind(wx.EVT_RIGHT_UP, self.OnProjectTreeRightUp) + self.ProjectTree.Bind(wx.EVT_LEFT_UP, self.OnProjectTreeLeftUp) + self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnProjectTreeItemChanging, + id=ID_PLCOPENEDITORPROJECTTREE) + self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnProjectTreeBeginDrag, + id=ID_PLCOPENEDITORPROJECTTREE) + self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnProjectTreeItemBeginEdit, + id=ID_PLCOPENEDITORPROJECTTREE) + self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnProjectTreeItemEndEdit, + id=ID_PLCOPENEDITORPROJECTTREE) + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnProjectTreeItemActivated, + id=ID_PLCOPENEDITORPROJECTTREE) + + #----------------------------------------------------------------------- + # Creating PLCopen Project POU Instance Variables Panel + #----------------------------------------------------------------------- + + self.PouInstanceVariablesPanel = PouInstanceVariablesPanel(self.ProjectPanel, self, self.Controler, self.EnableDebug) + + self.MainTabs["ProjectPanel"] = (self.ProjectPanel, _("Project")) + self.LeftNoteBook.AddPage(*self.MainTabs["ProjectPanel"]) + + self.ProjectPanel.SplitHorizontally(self.ProjectTree, self.PouInstanceVariablesPanel, 300) + + #----------------------------------------------------------------------- + # Creating Tool Bar + #----------------------------------------------------------------------- + + MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, wx.DefaultPosition, wx.DefaultSize, + wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + MenuToolBar.SetToolBitmapSize(wx.Size(25, 25)) + MenuToolBar.Realize() + self.Panes["MenuToolBar"] = MenuToolBar + self.AUIManager.AddPane(MenuToolBar, wx.aui.AuiPaneInfo(). + Name("MenuToolBar").Caption(_("Menu ToolBar")). + ToolbarPane().Top(). + LeftDockable(False).RightDockable(False)) + + EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR, wx.DefaultPosition, wx.DefaultSize, + wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + EditorToolBar.SetToolBitmapSize(wx.Size(25, 25)) + EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, + GetBitmap("select"), wx.NullBitmap, _("Select an object")) + EditorToolBar.Realize() + self.Panes["EditorToolBar"] = EditorToolBar + self.AUIManager.AddPane(EditorToolBar, wx.aui.AuiPaneInfo(). + Name("EditorToolBar").Caption(_("Editor ToolBar")). + ToolbarPane().Top().Position(1). + LeftDockable(False).RightDockable(False)) + + self.Bind(wx.EVT_MENU, self.OnSelectionTool, + id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION) + + #----------------------------------------------------------------------- + # Creating Search Panel + #----------------------------------------------------------------------- + + self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self) + self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search")) + self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"]) + + #----------------------------------------------------------------------- + # Creating Library Panel + #----------------------------------------------------------------------- + + self.LibraryPanel = LibraryPanel(self, True) + self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library")) + self.RightNoteBook.AddPage(*self.MainTabs["LibraryPanel"]) + + self._init_utils() + self.SetMenuBar(self.MenuBar) + + if self.EnableDebug: + self.DebugVariablePanel = DebugVariablePanel(self.RightNoteBook, self.Controler) + self.MainTabs["DebugVariablePanel"] = (self.DebugVariablePanel, _("Debugger")) + self.RightNoteBook.AddPage(*self.MainTabs["DebugVariablePanel"]) + + self.AUIManager.Update() + + self.FindDialog = FindInPouDialog(self) + self.FindDialog.Hide() + + ## Constructor of the PLCOpenEditor class. + # @param parent The parent window. + # @param controler The controler been used by PLCOpenEditor (default: None). + # @param fileOpen The filepath to open if no controler defined (default: None). + # @param debug The filepath to open if no controler defined (default: False). + def __init__(self, parent, enable_debug = False): + self.Controler = None + self.Config = wx.ConfigBase.Get() + self.EnableDebug = enable_debug + + self._init_ctrls(parent) + + # Define Tree item icon list + self.TreeImageList = wx.ImageList(16, 16) + self.TreeImageDict = {} + + # Icons for languages + for language in LANGUAGES: + self.TreeImageDict[language] = self.TreeImageList.Add(GetBitmap(language)) + + # Icons for other items + for imgname, itemtype in [ + #editables + ("PROJECT", ITEM_PROJECT), + #("POU", ITEM_POU), + #("VARIABLE", ITEM_VARIABLE), + ("TRANSITION", ITEM_TRANSITION), + ("ACTION", ITEM_ACTION), + ("CONFIGURATION", ITEM_CONFIGURATION), + ("RESOURCE", ITEM_RESOURCE), + ("DATATYPE", ITEM_DATATYPE), + # uneditables + ("DATATYPES", ITEM_DATATYPES), + ("FUNCTION", ITEM_FUNCTION), + ("FUNCTIONBLOCK", ITEM_FUNCTIONBLOCK), + ("PROGRAM", ITEM_PROGRAM), + ("VAR_LOCAL", ITEM_VAR_LOCAL), + ("VAR_LOCAL", ITEM_VAR_GLOBAL), + ("VAR_LOCAL", ITEM_VAR_EXTERNAL), + ("VAR_LOCAL", ITEM_VAR_TEMP), + ("VAR_INPUT", ITEM_VAR_INPUT), + ("VAR_OUTPUT", ITEM_VAR_OUTPUT), + ("VAR_INOUT", ITEM_VAR_INOUT), + ("TRANSITIONS", ITEM_TRANSITIONS), + ("ACTIONS", ITEM_ACTIONS), + ("CONFIGURATIONS", ITEM_CONFIGURATIONS), + ("RESOURCES", ITEM_RESOURCES), + ("PROPERTIES", ITEM_PROPERTIES)]: + self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) + + # Assign icon list to TreeCtrls + self.ProjectTree.SetImageList(self.TreeImageList) + self.PouInstanceVariablesPanel.SetTreeImageList(self.TreeImageList) + + self.CurrentEditorToolBar = [] + self.CurrentMenu = None + self.SelectedItem = None + self.SearchParams = None + self.Highlights = {} + self.DrawingMode = FREEDRAWING_MODE + #self.DrawingMode = DRIVENDRAWING_MODE + self.AuiTabCtrl = [] + self.DefaultPerspective = None + + # Initialize Printing configuring elements + self.PrintData = wx.PrintData() + self.PrintData.SetPaperId(wx.PAPER_A4) + self.PrintData.SetPrintMode(wx.PRINT_MODE_PRINTER) + self.PageSetupData = wx.PageSetupDialogData(self.PrintData) + self.PageSetupData.SetMarginTopLeft(wx.Point(10, 15)) + self.PageSetupData.SetMarginBottomRight(wx.Point(10, 20)) + + self.SetRefreshFunctions() + + def __del__(self): + self.FindDialog.Destroy() + + def ResetStarting(self): + self.Starting = False + + def Show(self): + wx.Frame.Show(self) + wx.CallAfter(self.RestoreLastState) + + def OnActivated(self, event): + if event.GetActive(): + wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) + event.Skip() + +#------------------------------------------------------------------------------- +# Saving and restoring frame organization functions +#------------------------------------------------------------------------------- + + def OnResize(self, event): + if self.Starting: + self.RestoreLastLayout() + if wx.Platform == '__WXMSW__': + wx.CallAfter(self.ResetStarting) + else: + self.ResetStarting() + wx.CallAfter(self.RefreshEditor) + event.Skip() + + def GetProjectConfiguration(self): + projects = {} + try: + if self.Config.HasEntry("projects"): + projects = cPickle.loads(str(self.Config.Read("projects"))) + except: + pass + + return projects.get( + EncodeFileSystemPath(os.path.realpath(self.Controler.GetFilePath())), {}) + + def SavePageState(self, page): + state = page.GetState() + if state is not None: + if self.Config.HasEntry("projects"): + projects = cPickle.loads(str(self.Config.Read("projects"))) + else: + projects = {} + + project_infos = projects.setdefault( + EncodeFileSystemPath(os.path.realpath(self.Controler.GetFilePath())), {}) + editors_state = project_infos.setdefault("editors_state", {}) + + if page.IsDebugging(): + editors_state[page.GetInstancePath()] = state + else: + editors_state[page.GetTagName()] = state + + self.Config.Write("projects", cPickle.dumps(projects)) + self.Config.Flush() + + def GetTabInfos(self, tab): + if isinstance(tab, EditorPanel): + if tab.IsDebugging(): + return ("debug", tab.GetInstancePath()) + else: + return ("editor", tab.GetTagName()) + else: + for page_name, (page_ref, page_title) in self.MainTabs.iteritems(): + if page_ref == tab: + return ("main", page_name) + return None + + def SaveTabLayout(self, notebook): + tabs = [] + for child in notebook.GetChildren(): + if isinstance(child, wx.aui.AuiTabCtrl): + if child.GetPageCount() > 0: + pos = child.GetPosition() + tab = {"pos": (pos.x, pos.y), "pages": []} + tab_size = child.GetSize() + for page_idx in xrange(child.GetPageCount()): + page = child.GetWindowFromIdx(page_idx) + if not tab.has_key("size"): + tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1]) + tab_infos = self.GetTabInfos(page) + if tab_infos is not None: + tab["pages"].append((tab_infos, page_idx == child.GetActivePage())) + tabs.append(tab) + tabs.sort(lambda x, y: cmp(x["pos"], y["pos"])) + size = notebook.GetSize() + return ComputeTabsLayout(tabs, wx.Rect(1, 1, size[0] - NOTEBOOK_BORDER, size[1] - NOTEBOOK_BORDER)) + + def LoadTab(self, notebook, page_infos): + if page_infos[0] == "main": + infos = self.MainTabs.get(page_infos[1]) + if infos is not None: + page_ref, page_title = infos + notebook.AddPage(page_ref, page_title) + return notebook.GetPageIndex(page_ref) + elif page_infos[0] == "editor": + tagname = page_infos[1] + page_ref = self.EditProjectElement(self.Controler.GetElementType(tagname), tagname) + if page_ref is not None: + page_ref.RefreshView() + return notebook.GetPageIndex(page_ref) + elif page_infos[0] == "debug": + instance_path = page_infos[1] + instance_infos = self.Controler.GetInstanceInfos(instance_path, self.EnableDebug) + if instance_infos is not None: + return notebook.GetPageIndex(self.OpenDebugViewer(instance_infos["class"], instance_path, instance_infos["type"])) + return None + + def LoadTabLayout(self, notebook, tabs, mode="all", first_index=None): + if isinstance(tabs, ListType): + if len(tabs) == 0: + return + raise ValueError, "Not supported" + + if tabs.has_key("split"): + self.LoadTabLayout(notebook, tabs["others"]) + + split_dir, split_ratio = tabs["split"] + first_index = self.LoadTabLayout(notebook, tabs["tab"], mode="first") + notebook.Split(first_index, split_dir) + self.LoadTabLayout(notebook, tabs["tab"], mode="others", first_index=first_index) + + elif mode == "first": + return self.LoadTab(notebook, tabs["pages"][0][0]) + else: + selected = first_index + if mode == "others": + add_tabs = tabs["pages"][1:] + else: + add_tabs = tabs["pages"] + for page_infos, page_selected in add_tabs: + page_idx = self.LoadTab(notebook, page_infos) + if page_selected: + selected = page_idx + if selected is not None: + wx.CallAfter(notebook.SetSelection, selected) + + def ResetPerspective(self): + if self.DefaultPerspective is not None: + self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"]) + + for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]: + for idx in xrange(notebook.GetPageCount()): + notebook.RemovePage(0) + + notebooks = self.DefaultPerspective["notebooks"] + for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"), + (self.BottomNoteBook, "bottomnotebook"), + (self.RightNoteBook, "rightnotebook")]: + self.LoadTabLayout(notebook, notebooks.get(entry_name)) + + self._Refresh(EDITORTOOLBAR) + + def RestoreLastState(self): + frame_size = None + if self.Config.HasEntry("framesize"): + frame_size = cPickle.loads(str(self.Config.Read("framesize"))) + + self.Starting = True + if frame_size is None: + self.Maximize() + else: + self.SetClientSize(frame_size) + + def RestoreLastLayout(self): + notebooks = {} + for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"), + (self.BottomNoteBook, "bottomnotebook"), + (self.RightNoteBook, "rightnotebook")]: + notebooks[entry_name] = self.SaveTabLayout(notebook) + self.DefaultPerspective = { + "perspective": self.AUIManager.SavePerspective(), + "notebooks": notebooks, + } + + try: + if self.Config.HasEntry("perspective"): + self.AUIManager.LoadPerspective(unicode(self.Config.Read("perspective"))) + + if self.Config.HasEntry("notebooks"): + notebooks = cPickle.loads(str(self.Config.Read("notebooks"))) + + for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]: + for idx in xrange(notebook.GetPageCount()): + notebook.RemovePage(0) + + for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"), + (self.BottomNoteBook, "bottomnotebook"), + (self.RightNoteBook, "rightnotebook")]: + self.LoadTabLayout(notebook, notebooks.get(entry_name)) + except: + self.ResetPerspective() + + self.LoadProjectLayout() + + self._Refresh(EDITORTOOLBAR) + + def SaveLastState(self): + if not self.IsMaximized(): + self.Config.Write("framesize", cPickle.dumps(self.GetClientSize())) + elif self.Config.HasEntry("framesize"): + self.Config.DeleteEntry("framesize") + + notebooks = {} + for notebook, entry_name in [(self.LeftNoteBook, "leftnotebook"), + (self.BottomNoteBook, "bottomnotebook"), + (self.RightNoteBook, "rightnotebook")]: + notebooks[entry_name] = self.SaveTabLayout(notebook) + self.Config.Write("notebooks", cPickle.dumps(notebooks)) + + pane = self.AUIManager.GetPane(self.TabsOpened) + if pane.IsMaximized(): + self.AUIManager.RestorePane(pane) + self.Config.Write("perspective", self.AUIManager.SavePerspective()) + + self.SaveProjectLayout() + + for i in xrange(self.TabsOpened.GetPageCount()): + self.SavePageState(self.TabsOpened.GetPage(i)) + + self.Config.Flush() + + def SaveProjectLayout(self): + if self.Controler is not None: + tabs = [] + + projects = {} + try: + if self.Config.HasEntry("projects"): + projects = cPickle.loads(str(self.Config.Read("projects"))) + except: + pass + + project_infos = projects.setdefault( + EncodeFileSystemPath(os.path.realpath(self.Controler.GetFilePath())), {}) + project_infos["tabs"] = self.SaveTabLayout(self.TabsOpened) + if self.EnableDebug: + project_infos["debug_vars"] = self.DebugVariablePanel.GetDebugVariables() + + self.Config.Write("projects", cPickle.dumps(projects)) + self.Config.Flush() + + def LoadProjectLayout(self): + if self.Controler is not None: + project = self.GetProjectConfiguration() + + try: + if project.has_key("tabs"): + self.LoadTabLayout(self.TabsOpened, project["tabs"]) + except: + self.DeleteAllPages() + + if self.EnableDebug: + try: + for variable in project.get("debug_vars", []): + self.DebugVariablePanel.InsertValue(variable, force=True) + except: + self.DebugVariablePanel.ResetGrid() + +#------------------------------------------------------------------------------- +# General Functions +#------------------------------------------------------------------------------- + + def SetRefreshFunctions(self): + self.RefreshFunctions = { + TITLE : self.RefreshTitle, + EDITORTOOLBAR : self.RefreshEditorToolBar, + FILEMENU : self.RefreshFileMenu, + EDITMENU : self.RefreshEditMenu, + DISPLAYMENU : self.RefreshDisplayMenu, + PROJECTTREE : self.RefreshProjectTree, + POUINSTANCEVARIABLESPANEL : self.RefreshPouInstanceVariablesPanel, + LIBRARYTREE : self.RefreshLibraryPanel, + SCALING : self.RefreshScaling, + PAGETITLES: self.RefreshPageTitles} + + ## Call PLCOpenEditor refresh functions. + # @param elements List of elements to refresh. + def _Refresh(self, *elements): + try: + for element in elements: + self.RefreshFunctions[element]() + except wx.PyDeadObjectError: + # ignore exceptions caused by refresh while quitting + pass + + ## Callback function when AUINotebook Page closed with CloseButton + # @param event AUINotebook Event. + def OnPageClose(self, event): + selected = self.TabsOpened.GetSelection() + if selected > -1: + window = self.TabsOpened.GetPage(selected) + + if window.CheckSaveBeforeClosing(): + self.SavePageState(window) + + # Refresh all window elements that have changed + wx.CallAfter(self._Refresh, TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) + wx.CallAfter(self.RefreshTabCtrlEvent) + wx.CallAfter(self.CloseFindInPouDialog) + event.Skip() + else: + event.Veto() + + + def GetCopyBuffer(self): + data = None + if wx.TheClipboard.Open(): + dataobj = wx.TextDataObject() + if wx.TheClipboard.GetData(dataobj): + data = dataobj.GetText() + wx.TheClipboard.Close() + return data + + def SetCopyBuffer(self, text): + if wx.TheClipboard.Open(): + data = wx.TextDataObject() + data.SetText(text) + wx.TheClipboard.SetData(data) + wx.TheClipboard.Flush() + wx.TheClipboard.Close() + self.RefreshEditMenu() + + def GetDrawingMode(self): + return self.DrawingMode + + def RefreshScaling(self): + for i in xrange(self.TabsOpened.GetPageCount()): + editor = self.TabsOpened.GetPage(i) + editor.RefreshScaling() + + def EditProjectSettings(self): + old_values = self.Controler.GetProjectProperties() + dialog = ProjectDialog(self) + dialog.SetValues(old_values) + if dialog.ShowModal() == wx.ID_OK: + new_values = dialog.GetValues() + new_values["creationDateTime"] = old_values["creationDateTime"] + if new_values != old_values: + self.Controler.SetProjectProperties(None, new_values) + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, + PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING) + dialog.Destroy() + +#------------------------------------------------------------------------------- +# Notebook Unified Functions +#------------------------------------------------------------------------------- + + ## Function that add a tab in Notebook, calling refresh for tab DClick event + # for wx.aui.AUINotebook. + # @param window Panel to display in tab. + # @param text title for the tab ctrl. + def AddPage(self, window, text): + self.TabsOpened.AddPage(window, text) + self.RefreshTabCtrlEvent() + + ## Function that add a tab in Notebook, calling refresh for tab DClick event + # for wx.aui.AUINotebook. + # @param window Panel to display in tab. + # @param text title for the tab ctrl. + def DeletePage(self, window): + for idx in xrange(self.TabsOpened.GetPageCount()): + if self.TabsOpened.GetPage(idx) == window: + self.TabsOpened.DeletePage(idx) + self.RefreshTabCtrlEvent() + return + + ## Function that fix difference in deleting all tabs between + # wx.Notebook and wx.aui.AUINotebook. + def DeleteAllPages(self): + for idx in xrange(self.TabsOpened.GetPageCount()): + self.TabsOpened.DeletePage(0) + self.RefreshTabCtrlEvent() + + ## Function that fix difference in setting picture on tab between + # wx.Notebook and wx.aui.AUINotebook. + # @param idx Tab index. + # @param bitmap wx.Bitmap to define on tab. + # @return True if operation succeeded + def SetPageBitmap(self, idx, bitmap): + return self.TabsOpened.SetPageBitmap(idx, bitmap) + +#------------------------------------------------------------------------------- +# Dialog Message Functions +#------------------------------------------------------------------------------- + + ## Function displaying an Error dialog in PLCOpenEditor. + # @param message The message to display. + def ShowErrorMessage(self, message): + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog.ShowModal() + dialog.Destroy() + + ## Function displaying an Error dialog in PLCOpenEditor. + # @return False if closing cancelled. + def CheckSaveBeforeClosing(self, title=_("Close Project")): + if not self.Controler.ProjectIsSaved(): + 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.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 + +#------------------------------------------------------------------------------- +# File Menu Functions +#------------------------------------------------------------------------------- + + def RefreshFileMenu(self): + pass + + def ResetView(self): + self.DeleteAllPages() + self.ProjectTree.DeleteAllItems() + self.ProjectTree.Enable(False) + self.PouInstanceVariablesPanel.ResetView() + self.LibraryPanel.ResetTree() + self.LibraryPanel.SetController(None) + if self.EnableDebug: + self.DebugVariablePanel.ResetGrid() + self.Controler = None + + def OnCloseTabMenu(self, event): + selected = self.TabsOpened.GetSelection() + if selected >= 0: + self.TabsOpened.DeletePage(selected) + if self.TabsOpened.GetPageCount() > 0: + new_index = min(selected, self.TabsOpened.GetPageCount() - 1) + self.TabsOpened.SetSelection(new_index) + # Refresh all window elements that have changed + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) + self.RefreshTabCtrlEvent() + + def OnPageSetupMenu(self, event): + dialog = wx.PageSetupDialog(self, self.PageSetupData) + if dialog.ShowModal() == wx.ID_OK: + self.PageSetupData = wx.PageSetupDialogData(dialog.GetPageSetupData()) + self.PrintData = wx.PrintData(self.PageSetupData.GetPrintData()) + dialog.Destroy() + + def OnPreviewMenu(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + data = wx.PrintDialogData(self.PrintData) + properties = self.Controler.GetProjectProperties(window.IsDebugging()) + page_size = map(int, properties["pageSize"]) + margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight()) + printout = GraphicPrintout(window, page_size, margins, True) + printout2 = GraphicPrintout(window, page_size, margins, True) + preview = wx.PrintPreview(printout, printout2, data) + + if preview.Ok(): + preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT) + + preview_frame.Initialize() + + preview_canvas = preview.GetCanvas() + preview_canvas.SetMinSize(preview_canvas.GetVirtualSize()) + preview_frame.Fit() + + preview_frame.Show(True) + + def OnPrintMenu(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + dialog_data = wx.PrintDialogData(self.PrintData) + dialog_data.SetToPage(1) + properties = self.Controler.GetProjectProperties(window.IsDebugging()) + page_size = map(int, properties["pageSize"]) + margins = (self.PageSetupData.GetMarginTopLeft(), self.PageSetupData.GetMarginBottomRight()) + printer = wx.Printer(dialog_data) + printout = GraphicPrintout(window, page_size, margins) + + if not printer.Print(self, printout, True) and printer.GetLastError() != wx.PRINTER_CANCELLED: + self.ShowErrorMessage(_("There was a problem printing.\nPerhaps your current printer is not set correctly?")) + printout.Destroy() + + def OnPropertiesMenu(self, event): + self.EditProjectSettings() + + def OnQuitMenu(self, event): + self.Close() + +#------------------------------------------------------------------------------- +# Edit Menu Functions +#------------------------------------------------------------------------------- + + def RefreshEditMenu(self): + MenuToolBar = self.Panes["MenuToolBar"] + if self.Controler is not None: + selected = self.TabsOpened.GetSelection() + if selected > -1: + window = self.TabsOpened.GetPage(selected) + undo, redo = window.GetBufferState() + else: + undo, redo = self.Controler.GetBufferState() + self.EditMenu.Enable(wx.ID_UNDO, undo) + MenuToolBar.EnableTool(wx.ID_UNDO, undo) + self.EditMenu.Enable(wx.ID_REDO, redo) + MenuToolBar.EnableTool(wx.ID_REDO, redo) + #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True) + #self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, + # self.Controler.IsProjectBufferEnabled()) + self.EditMenu.Enable(wx.ID_FIND, selected > -1) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT, + selected > -1 and self.SearchParams is not None) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, + selected > -1 and self.SearchParams is not None) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True) + MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True) + self.EditMenu.Enable(wx.ID_ADD, True) + self.EditMenu.Enable(wx.ID_DELETE, True) + if self.TabsOpened.GetPageCount() > 0: + self.EditMenu.Enable(wx.ID_CUT, True) + MenuToolBar.EnableTool(wx.ID_CUT, True) + self.EditMenu.Enable(wx.ID_COPY, True) + MenuToolBar.EnableTool(wx.ID_COPY, True) + if self.GetCopyBuffer() is not None: + self.EditMenu.Enable(wx.ID_PASTE, True) + MenuToolBar.EnableTool(wx.ID_PASTE, True) + else: + self.EditMenu.Enable(wx.ID_PASTE, False) + MenuToolBar.EnableTool(wx.ID_PASTE, False) + self.EditMenu.Enable(wx.ID_SELECTALL, True) + else: + self.EditMenu.Enable(wx.ID_CUT, False) + MenuToolBar.EnableTool(wx.ID_CUT, False) + self.EditMenu.Enable(wx.ID_COPY, False) + MenuToolBar.EnableTool(wx.ID_COPY, False) + self.EditMenu.Enable(wx.ID_PASTE, False) + MenuToolBar.EnableTool(wx.ID_PASTE, False) + self.EditMenu.Enable(wx.ID_SELECTALL, False) + else: + self.EditMenu.Enable(wx.ID_UNDO, False) + MenuToolBar.EnableTool(wx.ID_UNDO, False) + self.EditMenu.Enable(wx.ID_REDO, False) + MenuToolBar.EnableTool(wx.ID_REDO, False) + #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False) + self.EditMenu.Enable(wx.ID_CUT, False) + MenuToolBar.EnableTool(wx.ID_CUT, False) + self.EditMenu.Enable(wx.ID_COPY, False) + MenuToolBar.EnableTool(wx.ID_COPY, False) + self.EditMenu.Enable(wx.ID_PASTE, False) + MenuToolBar.EnableTool(wx.ID_PASTE, False) + self.EditMenu.Enable(wx.ID_SELECTALL, False) + self.EditMenu.Enable(wx.ID_FIND, False) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT, False) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, False) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False) + MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False) + self.EditMenu.Enable(wx.ID_ADD, False) + self.EditMenu.Enable(wx.ID_DELETE, False) + + def CloseTabsWithoutModel(self, refresh=True): + idxs = range(self.TabsOpened.GetPageCount()) + idxs.reverse() + for idx in idxs: + window = self.TabsOpened.GetPage(idx) + if window.HasNoModel(): + self.TabsOpened.DeletePage(idx) + if refresh: + self.RefreshEditor() + + def OnUndoMenu(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + window.Undo() + else: + self.Controler.LoadPrevious() + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, + SCALING, PAGETITLES) + + def OnRedoMenu(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + window.Redo() + else: + self.Controler.LoadNext() + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, + SCALING, PAGETITLES) + + def OnEnableUndoRedoMenu(self, event): + self.Controler.EnableProjectBuffer(event.IsChecked()) + self.RefreshEditMenu() + + OnCutMenu = GetShortcutKeyCallbackFunction("Cut") + OnCopyMenu = GetShortcutKeyCallbackFunction("Copy") + OnPasteMenu = GetShortcutKeyCallbackFunction("Paste") + + def OnSelectAllMenu(self, event): + control = self.FindFocus() + if control is not None and control.GetName() == "Viewer": + control.Parent.SelectAll() + elif isinstance(control, wx.stc.StyledTextCtrl): + control.SelectAll() + elif isinstance(control, wx.TextCtrl): + control.SetSelection(0, control.GetLastPosition()) + elif isinstance(control, wx.ComboBox): + control.SetMark(0, control.GetLastPosition() + 1) + + DeleteFunctions = { + ITEM_DATATYPE: GetDeleteElementFunction(PLCControler.ProjectRemoveDataType, check_function=PLCControler.DataTypeIsUsed), + ITEM_POU: GetDeleteElementFunction(PLCControler.ProjectRemovePou, check_function=PLCControler.PouIsUsed), + ITEM_TRANSITION: GetDeleteElementFunction(PLCControler.ProjectRemovePouTransition, ITEM_POU), + ITEM_ACTION: GetDeleteElementFunction(PLCControler.ProjectRemovePouAction, ITEM_POU), + ITEM_CONFIGURATION: GetDeleteElementFunction(PLCControler.ProjectRemoveConfiguration), + ITEM_RESOURCE: GetDeleteElementFunction(PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION) + } + + def OnDeleteMenu(self, event): + window = self.FindFocus() + if window == self.ProjectTree or window is None: + selected = self.ProjectTree.GetSelection() + if selected.IsOk(): + function = self.DeleteFunctions.get(self.ProjectTree.GetPyData(selected)["type"], None) + if function is not None: + function(self, selected) + self.CloseTabsWithoutModel() + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + elif isinstance(window, (Viewer, TextViewer)): + event = wx.KeyEvent(wx.EVT_CHAR._getEvtType()) + event.m_keyCode = wx.WXK_DELETE + window.ProcessEvent(event) + + def OnFindMenu(self, event): + if not self.FindDialog.IsShown(): + self.FindDialog.Show() + + def CloseFindInPouDialog(self): + selected = self.TabsOpened.GetSelection() + if selected == -1 and self.FindDialog.IsShown(): + self.FindDialog.Hide() + + def OnFindNextMenu(self, event): + self.FindInPou(1) + + def OnFindPreviousMenu(self, event): + self.FindInPou(-1) + + def FindInPou(self, direction, search_params=None): + if search_params is not None: + self.SearchParams = search_params + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + window.Find(direction, self.SearchParams) + + def OnSearchInProjectMenu(self, event): + dialog = SearchInProjectDialog(self) + if dialog.ShowModal() == wx.ID_OK: + criteria = dialog.GetCriteria() + result = self.Controler.SearchInProject(criteria) + self.ClearSearchResults() + self.SearchResultPanel.SetSearchResults(criteria, result) + self.BottomNoteBook.SetSelection(self.BottomNoteBook.GetPageIndex(self.SearchResultPanel)) + +#------------------------------------------------------------------------------- +# Display Menu Functions +#------------------------------------------------------------------------------- + + def RefreshDisplayMenu(self): + if self.Controler is not None: + if self.TabsOpened.GetPageCount() > 0: + self.DisplayMenu.Enable(wx.ID_REFRESH, True) + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + if isinstance(window, Viewer): + self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, True) + zoommenu = self.DisplayMenu.FindItemById(wx.ID_ZOOM_FIT).GetSubMenu() + zoomitem = zoommenu.FindItemByPosition(window.GetScale()) + zoomitem.Check(True) + else: + self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + else: + self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + else: + self.DisplayMenu.Enable(wx.ID_REFRESH, False) + self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + if self.EnableDebug: + self.DisplayMenu.Enable(wx.ID_CLEAR, True) + else: + self.DisplayMenu.Enable(wx.ID_REFRESH, False) + if self.EnableDebug: + self.DisplayMenu.Enable(wx.ID_CLEAR, False) + self.DisplayMenu.Enable(wx.ID_ZOOM_FIT, False) + + def OnRefreshMenu(self, event): + self.RefreshEditor() + + def OnClearErrorsMenu(self, event): + self.ClearErrors() + + def GenerateZoomFunction(self, idx): + def ZoomFunction(event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + window.SetScale(idx) + window.RefreshVisibleElements() + window.RefreshScrollBars() + event.Skip() + return ZoomFunction + + def OnResetPerspective(self, event): + self.ResetPerspective() + +#------------------------------------------------------------------------------- +# Project Editor Panels Management Functions +#------------------------------------------------------------------------------- + + def OnPageDragged(self, event): + wx.CallAfter(self.RefreshTabCtrlEvent) + event.Skip() + + def OnAllowNotebookDnD(self, event): + event.Allow() + + def RefreshTabCtrlEvent(self): + auitabctrl = [] + for child in self.TabsOpened.GetChildren(): + if isinstance(child, wx.aui.AuiTabCtrl): + auitabctrl.append(child) + if child not in self.AuiTabCtrl: + child.Bind(wx.EVT_LEFT_DCLICK, self.GetTabsOpenedDClickFunction(child)) + self.AuiTabCtrl = auitabctrl + if self.TabsOpened.GetPageCount() == 0: + pane = self.AUIManager.GetPane(self.TabsOpened) + if pane.IsMaximized(): + self.AUIManager.RestorePane(pane) + self.AUIManager.Update() + + def EnsureTabVisible(self, tab): + notebook = tab.GetParent() + notebook.SetSelection(notebook.GetPageIndex(tab)) + + def OnPouSelectedChanging(self, event): + if not self.Starting: + selected = self.TabsOpened.GetSelection() + if selected >= 0: + window = self.TabsOpened.GetPage(selected) + if not window.IsDebugging(): + window.ResetBuffer() + event.Skip() + + def OnPouSelectedChanged(self, event): + if not self.Starting: + selected = self.TabsOpened.GetSelection() + if selected >= 0: + window = self.TabsOpened.GetPage(selected) + tagname = window.GetTagName() + if not window.IsDebugging(): + wx.CallAfter(self.SelectProjectTreeItem, tagname) + wx.CallAfter(self.PouInstanceVariablesPanel.SetPouType, tagname) + window.RefreshView() + self.EnsureTabVisible(self.LibraryPanel) + else: + instance_path = window.GetInstancePath() + if tagname == "": + instance_path = instance_path.rsplit(".", 1)[0] + tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug) + self.EnsureTabVisible(self.DebugVariablePanel) + wx.CallAfter(self.PouInstanceVariablesPanel.SetPouType, tagname, instance_path) + wx.CallAfter(self._Refresh, FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR) + event.Skip() + + def RefreshEditor(self): + selected = self.TabsOpened.GetSelection() + if selected >= 0: + window = self.TabsOpened.GetPage(selected) + tagname = window.GetTagName() + if not window.IsDebugging(): + self.SelectProjectTreeItem(tagname) + self.PouInstanceVariablesPanel.SetPouType(tagname) + else: + instance_path = window.GetInstancePath() + if tagname == "": + instance_path = instance_path.rsplit(".", 1)[0] + tagname = self.Controler.GetPouInstanceTagName(instance_path, self.EnableDebug) + self.PouInstanceVariablesPanel.SetPouType(tagname, instance_path) + for child in self.TabsOpened.GetChildren(): + if isinstance(child, wx.aui.AuiTabCtrl): + active_page = child.GetActivePage() + if active_page >= 0: + window = child.GetWindowFromIdx(active_page) + window.RefreshView() + self._Refresh(FILEMENU, EDITMENU, DISPLAYMENU, EDITORTOOLBAR) + + def RefreshEditorNames(self, old_tagname, new_tagname): + for i in xrange(self.TabsOpened.GetPageCount()): + editor = self.TabsOpened.GetPage(i) + if editor.GetTagName() == old_tagname: + editor.SetTagName(new_tagname) + + def IsOpened(self, tagname): + for idx in xrange(self.TabsOpened.GetPageCount()): + if self.TabsOpened.GetPage(idx).IsViewing(tagname): + return idx + return None + + def RefreshPageTitles(self): + for idx in xrange(self.TabsOpened.GetPageCount()): + window = self.TabsOpened.GetPage(idx) + icon = window.GetIcon() + if icon is not None: + self.SetPageBitmap(idx, icon) + self.TabsOpened.SetPageText(idx, window.GetTitle()) + + def GetTabsOpenedDClickFunction(self, tabctrl): + def OnTabsOpenedDClick(event): + pos = event.GetPosition() + if tabctrl.TabHitTest(pos.x, pos.y, None): + pane = self.AUIManager.GetPane(self.TabsOpened) + if pane.IsMaximized(): + self.AUIManager.RestorePane(pane) + else: + self.AUIManager.MaximizePane(pane) + self.AUIManager.Update() + event.Skip() + return OnTabsOpenedDClick + +#------------------------------------------------------------------------------- +# Types Tree Management Functions +#------------------------------------------------------------------------------- + + def RefreshProjectTree(self): + infos = self.Controler.GetProjectInfos() + root = self.ProjectTree.GetRootItem() + if not root.IsOk(): + root = self.ProjectTree.AddRoot(infos["name"]) + self.GenerateProjectTreeBranch(root, infos) + self.ProjectTree.Expand(root) + + def ResetSelectedItem(self): + self.SelectedItem = None + + def GenerateProjectTreeBranch(self, root, infos): + to_delete = [] + item_name = infos["name"] + if infos["type"] in ITEMS_UNEDITABLE: + if len(infos["values"]) == 1: + return self.GenerateProjectTreeBranch(root, infos["values"][0]) + item_name = _(item_name) + self.ProjectTree.SetItemText(root, item_name) + self.ProjectTree.SetPyData(root, infos) + highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.WHITE, wx.BLACK)) + self.ProjectTree.SetItemBackgroundColour(root, highlight_colours[0]) + self.ProjectTree.SetItemTextColour(root, highlight_colours[1]) + if infos["type"] == ITEM_POU: + self.ProjectTree.SetItemImage(root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])]) + elif infos.has_key("icon") and infos["icon"] is not None: + icon_name = infos["icon"] + if not self.TreeImageDict.has_key(icon_name): + self.TreeImageDict[icon_name] = self.TreeImageList.Add(GetBitmap(icon_name)) + self.ProjectTree.SetItemImage(root, self.TreeImageDict[icon_name]) + elif self.TreeImageDict.has_key(infos["type"]): + self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) + + if wx.VERSION >= (2, 6, 0): + item, root_cookie = self.ProjectTree.GetFirstChild(root) + else: + item, root_cookie = self.ProjectTree.GetFirstChild(root, 0) + for values in infos["values"]: + if values["type"] not in ITEMS_UNEDITABLE or len(values["values"]) > 0: + if not item.IsOk(): + item = self.ProjectTree.AppendItem(root, "") + if wx.Platform != '__WXMSW__': + item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie) + self.GenerateProjectTreeBranch(item, values) + item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie) + while item.IsOk(): + to_delete.append(item) + item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie) + for item in to_delete: + self.ProjectTree.Delete(item) + + def SelectProjectTreeItem(self, tagname): + if self.ProjectTree is not None: + root = self.ProjectTree.GetRootItem() + if root.IsOk(): + words = tagname.split("::") + if words[0] == "D": + return self.RecursiveProjectTreeItemSelection(root, [(words[1], ITEM_DATATYPE)]) + elif words[0] == "P": + return self.RecursiveProjectTreeItemSelection(root, [(words[1], ITEM_POU)]) + elif words[0] == "T": + return self.RecursiveProjectTreeItemSelection(root, [(words[1], ITEM_POU), (words[2], ITEM_TRANSITION)]) + elif words[0] == "A": + return self.RecursiveProjectTreeItemSelection(root, [(words[1], ITEM_POU), (words[2], ITEM_ACTION)]) + elif words[0] == "C": + return self.RecursiveProjectTreeItemSelection(root, [(words[1], ITEM_CONFIGURATION)]) + elif words[0] == "R": + return self.RecursiveProjectTreeItemSelection(root, [(words[1], ITEM_CONFIGURATION), (words[2], ITEM_RESOURCE)]) + return False + + def RecursiveProjectTreeItemSelection(self, root, items): + found = False + if wx.VERSION >= (2, 6, 0): + item, root_cookie = self.ProjectTree.GetFirstChild(root) + else: + item, root_cookie = self.ProjectTree.GetFirstChild(root, 0) + while item.IsOk() and not found: + item_infos = self.ProjectTree.GetPyData(item) + if (item_infos["name"].split(":")[-1].strip(), item_infos["type"]) == items[0]: + if len(items) == 1: + self.SelectedItem = item + wx.CallAfter(self.ProjectTree.SelectItem, item) + wx.CallAfter(self.ResetSelectedItem) + return True + else: + found = self.RecursiveProjectTreeItemSelection(item, items[1:]) + else: + found = self.RecursiveProjectTreeItemSelection(item, items) + item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie) + return found + + def OnProjectTreeBeginDrag(self, event): + if wx.Platform == '__WXMSW__': + self.SelectedItem = event.GetItem() + if self.SelectedItem is not None and self.ProjectTree.GetPyData(self.SelectedItem)["type"] == ITEM_POU: + block_name = self.ProjectTree.GetItemText(self.SelectedItem) + block_type = self.Controler.GetPouType(block_name) + if block_type != "program": + data = wx.TextDataObject(str((block_name, block_type, ""))) + dragSource = wx.DropSource(self.ProjectTree) + dragSource.SetData(data) + dragSource.DoDragDrop() + self.ResetSelectedItem() + + def OnProjectTreeItemBeginEdit(self, event): + selected = event.GetItem() + if self.ProjectTree.GetPyData(selected)["type"] in ITEMS_UNEDITABLE: + event.Veto() + else: + event.Skip() + + def OnProjectTreeItemEndEdit(self, event): + message = None + abort = False + new_name = event.GetLabel() + if new_name != "": + if not TestIdentifier(new_name): + message = _("\"%s\" is not a valid identifier!")%new_name + elif new_name.upper() in IEC_KEYWORDS: + message = _("\"%s\" is a keyword. It can't be used!")%new_name + else: + item = event.GetItem() + old_name = self.ProjectTree.GetItemText(item) + item_infos = self.ProjectTree.GetPyData(item) + if item_infos["type"] == ITEM_PROJECT: + self.Controler.SetProjectProperties(name = new_name) + elif item_infos["type"] == ITEM_DATATYPE: + if new_name.upper() in [name.upper() for name in self.Controler.GetProjectDataTypeNames() if name != old_name]: + message = _("\"%s\" data type already exists!")%new_name + abort = True + if not abort: + self.Controler.ChangeDataTypeName(old_name, new_name) + self.RefreshEditorNames(self.Controler.ComputeDataTypeName(old_name), + self.Controler.ComputeDataTypeName(new_name)) + self.RefreshPageTitles() + elif item_infos["type"] == ITEM_POU: + if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames() if name != old_name]: + message = _("\"%s\" pou already exists!")%new_name + abort = True + elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables()]: + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + if messageDialog.ShowModal() == wx.ID_NO: + abort = True + messageDialog.Destroy() + if not abort: + self.Controler.ChangePouName(old_name, new_name) + self.RefreshEditorNames(self.Controler.ComputePouName(old_name), + self.Controler.ComputePouName(new_name)) + self.RefreshLibraryPanel() + self.RefreshPageTitles() + elif item_infos["type"] == ITEM_TRANSITION: + if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: + message = _("A POU named \"%s\" already exists!")%new_name + elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables(pou_name) if name != old_name]: + message = _("A variable with \"%s\" as name already exists in this pou!")%new_name + else: + words = item_infos["tagname"].split("::") + self.Controler.ChangePouTransitionName(words[1], old_name, new_name) + self.RefreshEditorNames(self.Controler.ComputePouTransitionName(words[1], old_name), + self.Controler.ComputePouTransitionName(words[1], new_name)) + self.RefreshPageTitles() + elif item_infos["type"] == ITEM_ACTION: + if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: + message = _("A POU named \"%s\" already exists!")%new_name + elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables(pou_name) if name != old_name]: + message = _("A variable with \"%s\" as name already exists in this pou!")%new_name + else: + words = item_infos["tagname"].split("::") + self.Controler.ChangePouActionName(words[1], old_name, new_name) + self.RefreshEditorNames(self.Controler.ComputePouActionName(words[1], old_name), + self.Controler.ComputePouActionName(words[1], new_name)) + self.RefreshPageTitles() + elif item_infos["type"] == ITEM_CONFIGURATION: + if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames() if name != old_name]: + message = _("\"%s\" config already exists!")%new_name + abort = True + elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: + messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + if messageDialog.ShowModal() == wx.ID_NO: + abort = True + messageDialog.Destroy() + elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables()]: + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + if messageDialog.ShowModal() == wx.ID_NO: + abort = True + messageDialog.Destroy() + if not abort: + self.Controler.ChangeConfigurationName(old_name, new_name) + self.RefreshEditorNames(self.Controler.ComputeConfigurationName(old_name), + self.Controler.ComputeConfigurationName(new_name)) + self.RefreshPageTitles() + elif item_infos["type"] == ITEM_RESOURCE: + if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames()]: + message = _("\"%s\" config already exists!")%new_name + abort = True + elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: + messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + if messageDialog.ShowModal() == wx.ID_NO: + abort = True + messageDialog.Destroy() + elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariables()]: + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + if messageDialog.ShowModal() == wx.ID_NO: + abort = True + messageDialog.Destroy() + if not abort: + words = item_infos["tagname"].split("::") + self.Controler.ChangeConfigurationResourceName(words[1], old_name, new_name) + self.RefreshEditorNames(self.Controler.ComputeConfigurationResourceName(words[1], old_name), + self.Controler.ComputeConfigurationResourceName(words[1], new_name)) + self.RefreshPageTitles() + if message or abort: + if message: + self.ShowErrorMessage(message) + item = event.GetItem() + wx.CallAfter(self.ProjectTree.EditLabel, item) + event.Veto() + else: + wx.CallAfter(self.RefreshProjectTree) + self.RefreshEditor() + self._Refresh(TITLE, FILEMENU, EDITMENU) + event.Skip() + + def OnProjectTreeItemActivated(self, event): + selected = event.GetItem() + name = self.ProjectTree.GetItemText(selected) + item_infos = self.ProjectTree.GetPyData(selected) + if item_infos["type"] == ITEM_PROJECT: + self.EditProjectSettings() + else: + if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU, + ITEM_CONFIGURATION, ITEM_RESOURCE, + ITEM_TRANSITION, ITEM_ACTION]: + self.EditProjectElement(item_infos["type"], item_infos["tagname"]) + event.Skip() + + def ProjectTreeItemSelect(self, select_item): + name = self.ProjectTree.GetItemText(select_item) + item_infos = self.ProjectTree.GetPyData(select_item) + if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU, + ITEM_CONFIGURATION, ITEM_RESOURCE, + ITEM_TRANSITION, ITEM_ACTION]: + self.EditProjectElement(item_infos["type"], item_infos["tagname"], True) + self.PouInstanceVariablesPanel.SetPouType(item_infos["tagname"]) + + def OnProjectTreeLeftUp(self, event): + if self.SelectedItem is not None: + self.ProjectTree.SelectItem(self.SelectedItem) + self.ProjectTreeItemSelect(self.SelectedItem) + wx.CallAfter(self.ResetSelectedItem) + event.Skip() + + def OnProjectTreeItemSelected(self, event): + self.ProjectTreeItemSelect(event.GetItem()) + event.Skip() + + def OnProjectTreeItemChanging(self, event): + if self.ProjectTree.GetPyData(event.GetItem())["type"] not in ITEMS_UNEDITABLE and self.SelectedItem is None: + self.SelectedItem = event.GetItem() + event.Veto() + else: + event.Skip() + + def EditProjectElement(self, element, tagname, onlyopened = False): + openedidx = self.IsOpened(tagname) + if openedidx is not None: + old_selected = self.TabsOpened.GetSelection() + if old_selected != openedidx: + if old_selected >= 0: + self.TabsOpened.GetPage(old_selected).ResetBuffer() + self.TabsOpened.SetSelection(openedidx) + self._Refresh(FILEMENU, EDITMENU, EDITORTOOLBAR, PAGETITLES) + elif not onlyopened: + new_window = None + if element == ITEM_CONFIGURATION: + new_window = ConfigurationEditor(self.TabsOpened, tagname, self, self.Controler) + new_window.SetIcon(GetBitmap("CONFIGURATION")) + self.AddPage(new_window, "") + elif element == ITEM_RESOURCE: + new_window = ResourceEditor(self.TabsOpened, tagname, self, self.Controler) + new_window.SetIcon(GetBitmap("RESOURCE")) + self.AddPage(new_window, "") + elif element in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]: + bodytype = self.Controler.GetEditedElementBodyType(tagname) + if bodytype == "FBD": + new_window = Viewer(self.TabsOpened, tagname, self, self.Controler) + new_window.RefreshScaling(False) + elif bodytype == "LD": + new_window = LD_Viewer(self.TabsOpened, tagname, self, self.Controler) + new_window.RefreshScaling(False) + elif bodytype == "SFC": + new_window = SFC_Viewer(self.TabsOpened, tagname, self, self.Controler) + new_window.RefreshScaling(False) + else: + new_window = TextViewer(self.TabsOpened, tagname, self, self.Controler) + new_window.SetTextSyntax(bodytype) + if bodytype == "IL": + new_window.SetKeywords(IL_KEYWORDS) + else: + new_window.SetKeywords(ST_KEYWORDS) + if element == ITEM_POU: + pou_type = self.Controler.GetEditedElementType(tagname)[1].upper() + icon = GetBitmap(pou_type, bodytype) + elif element == ITEM_TRANSITION: + icon = GetBitmap("TRANSITION", bodytype) + elif element == ITEM_ACTION: + icon = GetBitmap("ACTION", bodytype) + new_window.SetIcon(icon) + self.AddPage(new_window, "") + elif element == ITEM_DATATYPE: + new_window = DataTypeEditor(self.TabsOpened, tagname, self, self.Controler) + new_window.SetIcon(GetBitmap("DATATYPE")) + self.AddPage(new_window, "") + elif isinstance(element, EditorPanel): + new_window = element + self.AddPage(element, "") + if new_window is not None: + project_infos = self.GetProjectConfiguration() + if project_infos.has_key("editors_state"): + state = project_infos["editors_state"].get(tagname) + if state is not None: + wx.CallAfter(new_window.SetState, state) + + openedidx = self.IsOpened(tagname) + old_selected = self.TabsOpened.GetSelection() + if old_selected != openedidx: + if old_selected >= 0: + self.TabsOpened.GetPage(old_selected).ResetBuffer() + for i in xrange(self.TabsOpened.GetPageCount()): + window = self.TabsOpened.GetPage(i) + if window == new_window: + self.TabsOpened.SetSelection(i) + window.SetFocus() + self.RefreshPageTitles() + return new_window + + def OnProjectTreeRightUp(self, event): + if wx.Platform == '__WXMSW__': + item = event.GetItem() + else: + item, flags = self.ProjectTree.HitTest(wx.Point(event.GetX(), event.GetY())) + self.ProjectTree.SelectItem(item) + self.ProjectTreeItemSelect(item) + name = self.ProjectTree.GetItemText(item) + item_infos = self.ProjectTree.GetPyData(item) + + menu = None + if item_infos["type"] in ITEMS_UNEDITABLE + [ITEM_PROJECT]: + if item_infos["type"] == ITEM_PROJECT: + name = "Project" + else: + name = UNEDITABLE_NAMES_DICT[name] + + if name == "Data Types": + menu = wx.Menu(title='') + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add DataType")) + self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, id=new_id) + + elif name in ["Functions", "Function Blocks", "Programs", "Project"]: + menu = wx.Menu(title='') + + if name != "Project": + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add POU")) + self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction({"Functions" : "function", "Function Blocks" : "functionBlock", "Programs" : "program"}[name]), id=new_id) + + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Paste POU")) + self.Bind(wx.EVT_MENU, self.OnPastePou, id=new_id) + if self.GetCopyBuffer() is None: + menu.Enable(new_id, False) + + elif name == "Configurations": + menu = wx.Menu(title='') + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Configuration")) + self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, id=new_id) + + elif name == "Transitions": + menu = wx.Menu(title='') + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition")) + parent = self.ProjectTree.GetItemParent(item)["type"] + parent_type = self.ProjectTree.GetPyData(parent) + while parent_type != ITEM_POU: + parent = self.ProjectTree.GetItemParent(parent) + parent_type = self.ProjectTree.GetPyData(parent)["type"] + self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(self.ProjectTree.GetItemText(parent)), id=new_id) + + elif name == "Actions": + menu = wx.Menu(title='') + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action")) + parent = self.ProjectTree.GetItemParent(item) + parent_type = self.ProjectTree.GetPyData(parent)["type"] + while parent_type != ITEM_POU: + parent = self.ProjectTree.GetItemParent(parent) + parent_type = self.ProjectTree.GetPyData(parent)["type"] + self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(self.ProjectTree.GetItemText(parent)), id=new_id) + + elif name == "Resources": + menu = wx.Menu(title='') + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource")) + parent = self.ProjectTree.GetItemParent(item) + parent_type = self.ProjectTree.GetPyData(parent)["type"] + while parent_type not in [ITEM_CONFIGURATION, ITEM_PROJECT]: + parent = self.ProjectTree.GetItemParent(parent) + parent_type = self.ProjectTree.GetPyData(parent)["type"] + if parent_type == ITEM_PROJECT: + parent_name = None + else: + parent_name = self.ProjectTree.GetItemText(parent) + self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(parent_name), id=new_id) + + else: + if item_infos["type"] == ITEM_POU: + menu = wx.Menu(title='') + if self.Controler.GetPouBodyType(name) == "SFC": + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Transition")) + self.Bind(wx.EVT_MENU, self.GenerateAddTransitionFunction(name), id=new_id) + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Action")) + self.Bind(wx.EVT_MENU, self.GenerateAddActionFunction(name), id=new_id) + menu.AppendSeparator() + + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Copy POU")) + self.Bind(wx.EVT_MENU, self.OnCopyPou, id=new_id) + + pou_type = self.Controler.GetPouType(name) + if pou_type in ["function", "functionBlock"]: + change_menu = wx.Menu(title='') + if pou_type == "function": + new_id = wx.NewId() + AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Function Block")) + self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "functionBlock"), id=new_id) + new_id = wx.NewId() + AppendMenu(change_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Program")) + self.Bind(wx.EVT_MENU, self.GenerateChangePouTypeFunction(name, "program"), id=new_id) + menu.AppendMenu(wx.NewId(), _("Change POU Type To"), change_menu) + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Rename")) + self.Bind(wx.EVT_MENU, self.OnRenamePouMenu, id=new_id) + + elif item_infos["type"] == ITEM_CONFIGURATION: + menu = wx.Menu(title='') + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add Resource")) + self.Bind(wx.EVT_MENU, self.GenerateAddResourceFunction(name), id=new_id) + + elif item_infos["type"] in [ITEM_DATATYPE, ITEM_TRANSITION, ITEM_ACTION, ITEM_RESOURCE]: + menu = wx.Menu(title='') + + if menu is not None: + new_id = wx.NewId() + AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete")) + self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=new_id) + + if menu is not None: + self.PopupMenu(menu) + menu.Destroy() + + event.Skip() + + +#------------------------------------------------------------------------------- +# Instances Tree Management Functions +#------------------------------------------------------------------------------- + + def GetTreeImage(self, var_class): + return self.TreeImageDict[var_class] + + def RefreshPouInstanceVariablesPanel(self): + self.PouInstanceVariablesPanel.RefreshView() + + def OpenDebugViewer(self, instance_category, instance_path, instance_type): + openedidx = self.IsOpened(instance_path) + if openedidx is not None: + old_selected = self.TabsOpened.GetSelection() + if old_selected != openedidx: + if old_selected >= 0: + self.TabsOpened.GetPage(old_selected).ResetBuffer() + self.TabsOpened.SetSelection(openedidx) + + elif instance_category in ITEMS_VARIABLE: + if self.Controler.IsOfType(instance_type, "ANY_NUM", True) or\ + self.Controler.IsOfType(instance_type, "ANY_BIT", True): + + return self.OpenGraphicViewer(instance_path) + + else: + bodytype = self.Controler.GetEditedElementBodyType(instance_type, True) + new_window = None + if bodytype == "FBD": + new_window = Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path) + new_window.RefreshScaling(False) + elif bodytype == "LD": + new_window = LD_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path) + new_window.RefreshScaling(False) + elif bodytype == "SFC": + new_window = SFC_Viewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path) + new_window.RefreshScaling(False) + else: + new_window = TextViewer(self.TabsOpened, instance_type, self, self.Controler, True, instance_path) + new_window.SetTextSyntax(bodytype) + if bodytype == "IL": + new_window.SetKeywords(IL_KEYWORDS) + else: + new_window.SetKeywords(ST_KEYWORDS) + if new_window is not None: + project_infos = self.GetProjectConfiguration() + if project_infos.has_key("editors_state"): + state = project_infos["editors_state"].get(instance_path) + if state is not None: + wx.CallAfter(new_window.SetState, state) + + if instance_category in [ITEM_FUNCTIONBLOCK, ITEM_PROGRAM]: + pou_type = self.Controler.GetEditedElementType(instance_type, True)[1].upper() + icon = GetBitmap(pou_type, bodytype) + elif instance_category == ITEM_TRANSITION: + icon = GetBitmap("TRANSITION", bodytype) + elif instance_category == ITEM_ACTION: + icon = GetBitmap("ACTION", bodytype) + new_window.SetIcon(icon) + self.AddPage(new_window, "") + new_window.RefreshView() + new_window.SetFocus() + self.RefreshPageTitles() + return new_window + + return None + + def OpenGraphicViewer(self, var_path): + new_window = GraphicViewer(self.TabsOpened, self, self.Controler, var_path) + new_window.SetIcon(GetBitmap("GRAPH")) + self.AddPage(new_window, "") + new_window.RefreshView() + new_window.SetFocus() + self.RefreshPageTitles() + return new_window + + def ResetGraphicViewers(self): + for i in xrange(self.TabsOpened.GetPageCount()): + editor = self.TabsOpened.GetPage(i) + if isinstance(editor, GraphicViewer): + editor.ResetView() + + def CloseObsoleteDebugTabs(self): + if self.EnableDebug: + idxs = range(self.TabsOpened.GetPageCount()) + idxs.reverse() + for idx in idxs: + editor = self.TabsOpened.GetPage(idx) + if editor.IsDebugging(): + instance_infos = self.Controler.GetInstanceInfos(editor.GetInstancePath(), self.EnableDebug) + if instance_infos is None: + self.TabsOpened.DeletePage(idx) + elif isinstance(editor, GraphicViewer): + editor.ResetView(True) + else: + editor.RefreshView() + self.DebugVariablePanel.UnregisterObsoleteData() + + def AddDebugVariable(self, iec_path, force=False): + if self.EnableDebug: + self.DebugVariablePanel.InsertValue(iec_path, force=force) + self.EnsureTabVisible(self.DebugVariablePanel) + +#------------------------------------------------------------------------------- +# Library Panel Management Function +#------------------------------------------------------------------------------- + + def RefreshLibraryPanel(self): + self.LibraryPanel.RefreshTree() + +#------------------------------------------------------------------------------- +# ToolBars Management Functions +#------------------------------------------------------------------------------- + + def AddToMenuToolBar(self, items): + MenuToolBar = self.Panes["MenuToolBar"] + if MenuToolBar.GetToolsCount() > 0: + MenuToolBar.AddSeparator() + for toolbar_item in items: + if toolbar_item is None: + MenuToolBar.AddSeparator() + else: + id, bitmap, help, callback = toolbar_item + MenuToolBar.AddSimpleTool(id=id, shortHelpString=help, bitmap=GetBitmap(bitmap)) + if callback is not None: + self.Bind(wx.EVT_TOOL, callback, id=id) + MenuToolBar.Realize() + self.AUIManager.GetPane("MenuToolBar").BestSize(MenuToolBar.GetBestSize()) + + def ResetEditorToolBar(self): + EditorToolBar = self.Panes["EditorToolBar"] + + for item in self.CurrentEditorToolBar: + if wx.VERSION >= (2, 6, 0): + self.Unbind(wx.EVT_MENU, id=item) + else: + self.Disconnect(id=item, eventType=wx.wxEVT_COMMAND_MENU_SELECTED) + + if EditorToolBar: + EditorToolBar.DeleteTool(item) + + if EditorToolBar: + EditorToolBar.Realize() + self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize()) + self.AUIManager.GetPane("EditorToolBar").Hide() + self.AUIManager.Update() + + def RefreshEditorToolBar(self): + selected = self.TabsOpened.GetSelection() + menu = None + if selected != -1: + window = self.TabsOpened.GetPage(selected) + if isinstance(window, (Viewer, TextViewer, GraphicViewer)): + if not window.IsDebugging(): + menu = self.Controler.GetEditedElementBodyType(window.GetTagName()) + else: + menu = "debug" + if menu is not None and menu != self.CurrentMenu: + self.ResetEditorToolBar() + self.CurrentMenu = menu + self.CurrentEditorToolBar = [] + EditorToolBar = self.Panes["EditorToolBar"] + if EditorToolBar: + for radio, modes, id, method, picture, help in EditorToolBarItems[menu]: + if modes & self.DrawingMode: + if radio or self.DrawingMode == FREEDRAWING_MODE: + EditorToolBar.AddRadioTool(id, GetBitmap(picture), wx.NullBitmap, help) + else: + EditorToolBar.AddSimpleTool(id, GetBitmap(picture), help) + self.Bind(wx.EVT_MENU, getattr(self, method), id=id) + self.CurrentEditorToolBar.append(id) + EditorToolBar.Realize() + self.AUIManager.GetPane("EditorToolBar").BestSize(EditorToolBar.GetBestSize()) + self.AUIManager.GetPane("EditorToolBar").Show() + self.AUIManager.Update() + elif menu is None: + self.ResetEditorToolBar() + self.CurrentMenu = menu + self.ResetCurrentMode() + + +#------------------------------------------------------------------------------- +# EditorToolBar Items Functions +#------------------------------------------------------------------------------- + + def ResetCurrentMode(self): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + window.SetMode(MODE_SELECTION) + EditorToolBar = self.Panes["EditorToolBar"] + if EditorToolBar: + EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, False) + EditorToolBar.ToggleTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, True) + + def ResetToolToggle(self, id): + tool = self.Panes["EditorToolBar"].FindById(id) + tool.SetToggle(False) + + def OnSelectionTool(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_SELECTION) + + def OnMotionTool(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_MOTION) + + def OnCommentTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOMMENT) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_COMMENT) + + def OnVariableTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARVARIABLE) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_VARIABLE) + + def OnBlockTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARBLOCK) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_BLOCK) + + def OnConnectionTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONNECTION) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_CONNECTION) + + def OnPowerRailTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_POWERRAIL) + + def OnRungTool(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).AddLadderRung() + event.Skip() + + def OnCoilTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCOIL) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_COIL) + event.Skip() + + def OnContactTool(self, event): + if self.DrawingMode == FREEDRAWING_MODE: + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARCONTACT) + selected = self.TabsOpened.GetSelection() + if selected != -1: + if self.DrawingMode == FREEDRAWING_MODE: + self.TabsOpened.GetPage(selected).SetMode(MODE_CONTACT) + else: + self.TabsOpened.GetPage(selected).AddLadderContact() + + def OnBranchTool(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).AddLadderBranch() + + def OnInitialStepTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_INITIALSTEP) + + def OnStepTool(self, event): + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARSTEP) + selected = self.TabsOpened.GetSelection() + if selected != -1: + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.TabsOpened.GetPage(selected).SetMode(MODE_STEP) + else: + self.TabsOpened.GetPage(selected).AddStep() + + def OnActionBlockTool(self, event): + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK) + selected = self.TabsOpened.GetSelection() + if selected != -1: + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.TabsOpened.GetPage(selected).SetMode(MODE_ACTION) + else: + self.TabsOpened.GetPage(selected).AddStepAction() + + def OnTransitionTool(self, event): + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARTRANSITION) + selected = self.TabsOpened.GetSelection() + if selected != -1: + self.TabsOpened.GetPage(selected).SetMode(MODE_TRANSITION) + + def OnDivergenceTool(self, event): + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE) + selected = self.TabsOpened.GetSelection() + if selected != -1: + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.TabsOpened.GetPage(selected).SetMode(MODE_DIVERGENCE) + else: + self.TabsOpened.GetPage(selected).AddDivergence() + + def OnJumpTool(self, event): + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.ResetToolToggle(ID_PLCOPENEDITOREDITORTOOLBARJUMP) + selected = self.TabsOpened.GetSelection() + if selected != -1: + if self.GetDrawingMode() == FREEDRAWING_MODE: + self.TabsOpened.GetPage(selected).SetMode(MODE_JUMP) + else: + self.TabsOpened.GetPage(selected).AddJump() + + +#------------------------------------------------------------------------------- +# Add Project Elements Functions +#------------------------------------------------------------------------------- + + def OnAddDataTypeMenu(self, event): + tagname = self.Controler.ProjectAddDataType() + if tagname is not None: + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE) + self.EditProjectElement(ITEM_DATATYPE, tagname) + + def GenerateAddPouFunction(self, pou_type): + def OnAddPouMenu(event): + dialog = PouDialog(self, pou_type) + dialog.SetPouNames(self.Controler.GetProjectPouNames()) + dialog.SetPouElementNames(self.Controler.GetProjectPouVariables()) + dialog.SetValues({"pouName": self.Controler.GenerateNewName(None, None, "%s%%d" % pou_type)}) + if dialog.ShowModal() == wx.ID_OK: + values = dialog.GetValues() + tagname = self.Controler.ProjectAddPou(values["pouName"], values["pouType"], values["language"]) + if tagname is not None: + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE) + self.EditProjectElement(ITEM_POU, tagname) + dialog.Destroy() + return OnAddPouMenu + + def GenerateAddTransitionFunction(self, pou_name): + def OnAddTransitionMenu(event): + dialog = PouTransitionDialog(self) + dialog.SetPouNames(self.Controler.GetProjectPouNames()) + dialog.SetPouElementNames(self.Controler.GetProjectPouVariables(pou_name)) + dialog.SetValues({"transitionName": self.Controler.GenerateNewName(None, None, "transition%d")}) + if dialog.ShowModal() == wx.ID_OK: + values = dialog.GetValues() + tagname = self.Controler.ProjectAddPouTransition(pou_name, values["transitionName"], values["language"]) + if tagname is not None: + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE) + self.EditProjectElement(ITEM_TRANSITION, tagname) + dialog.Destroy() + return OnAddTransitionMenu + + def GenerateAddActionFunction(self, pou_name): + def OnAddActionMenu(event): + dialog = PouActionDialog(self) + dialog.SetPouNames(self.Controler.GetProjectPouNames()) + dialog.SetPouElementNames(self.Controler.GetProjectPouVariables(pou_name)) + dialog.SetValues({"actionName": self.Controler.GenerateNewName(None, None, "action%d")}) + if dialog.ShowModal() == wx.ID_OK: + values = dialog.GetValues() + tagname = self.Controler.ProjectAddPouAction(pou_name, values["actionName"], values["language"]) + if tagname is not None: + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE) + self.EditProjectElement(ITEM_ACTION, tagname) + dialog.Destroy() + return OnAddActionMenu + + def OnAddConfigurationMenu(self, event): + tagname = self.Controler.ProjectAddConfiguration() + if tagname is not None: + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL) + self.EditProjectElement(ITEM_CONFIGURATION, tagname) + dialog.Destroy() + + def GenerateAddResourceFunction(self, config_name): + def OnAddResourceMenu(event): + tagname = self.Controler.ProjectAddConfigurationResource(config_name) + if tagname is not None: + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL) + self.EditProjectElement(ITEM_RESOURCE, tagname) + return OnAddResourceMenu + + def GenerateChangePouTypeFunction(self, name, new_type): + def OnChangePouTypeMenu(event): + selected = self.ProjectTree.GetSelection() + if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: + self.Controler.ProjectChangePouType(name, new_type) + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE) + return OnChangePouTypeMenu + + def OnCopyPou(self, event): + selected = self.ProjectTree.GetSelection() + pou_name = self.ProjectTree.GetItemText(selected) + + pou_xml = self.Controler.GetPouXml(pou_name) + if pou_xml is not None: + self.SetCopyBuffer(pou_xml) + self._Refresh(EDITMENU) + + def OnPastePou(self, event): + selected = self.ProjectTree.GetSelection() + + if self.ProjectTree.GetPyData(selected)["type"] != ITEM_PROJECT: + pou_type = self.ProjectTree.GetItemText(selected) + pou_type = UNEDITABLE_NAMES_DICT[pou_type] # one of 'Functions', 'Function Blocks' or 'Programs' + pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type] + else: + pou_type = None + + pou_xml = self.GetCopyBuffer() + + result = self.Controler.PastePou(pou_type, pou_xml) + + if not isinstance(result, TupleType): + message = wx.MessageDialog(self, result, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + else: + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE) + self.EditProjectElement(ITEM_POU, result[0]) + +#------------------------------------------------------------------------------- +# Remove Project Elements Functions +#------------------------------------------------------------------------------- + + def OnRemoveDataTypeMenu(self, event): + selected = self.ProjectTree.GetSelection() + if self.ProjectTree.GetPyData(selected)["type"] == ITEM_DATATYPE: + name = self.ProjectTree.GetItemText(selected) + if not self.Controler.DataTypeIsUsed(name): + self.Controler.ProjectRemoveDataType(name) + tagname = self.Controler.ComputeDataTypeName(name) + idx = self.IsOpened(tagname) + if idx is not None: + self.TabsOpened.DeletePage(idx) + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE) + else: + self.ShowErrorMessage(_("\"%s\" is used by one or more POUs. It can't be removed!")) + + def OnRenamePouMenu(self, event): + selected = self.ProjectTree.GetSelection() + if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: + wx.CallAfter(self.ProjectTree.EditLabel, selected) + + def OnRemovePouMenu(self, event): + selected = self.ProjectTree.GetSelection() + if self.ProjectTree.GetPyData(selected)["type"] == ITEM_POU: + name = self.ProjectTree.GetItemText(selected) + if not self.Controler.PouIsUsed(name): + self.Controler.ProjectRemovePou(name) + tagname = self.Controler.ComputePouName(name) + idx = self.IsOpened(tagname) + if idx is not None: + self.TabsOpened.DeletePage(idx) + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + else: + self.ShowErrorMessage(_("\"%s\" is used by one or more POUs. It can't be removed!")) + + def OnRemoveTransitionMenu(self, event): + selected = self.ProjectTree.GetSelection() + item_infos = self.ProjectTree.GetPyData(selected) + if item_infos["type"] == ITEM_TRANSITION: + transition = self.ProjectTree.GetItemText(selected) + pou_name = item_infos["tagname"].split("::")[1] + self.Controler.ProjectRemovePouTransition(pou_name, transition) + tagname = self.Controler.ComputePouTransitionName(pou_name, transition) + idx = self.IsOpened(tagname) + if idx is not None: + self.TabsOpened.DeletePage(idx) + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE) + + def OnRemoveActionMenu(self, event): + selected = self.ProjectTree.GetSelection() + item_infos = self.ProjectTree.GetPyData(selected) + if item_infos["type"] == ITEM_ACTION: + action = self.ProjectTree.GetItemText(selected) + pou_name = item_infos["tagname"].split("::")[1] + self.Controler.ProjectRemovePouAction(pou_name, action) + tagname = self.Controler.ComputePouActionName(pou_name, action) + idx = self.IsOpened(tagname) + if idx is not None: + self.TabsOpened.DeletePage(idx) + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE) + + def OnRemoveConfigurationMenu(self, event): + selected = self.ProjectTree.GetSelection() + if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFIGURATION: + name = self.ProjectTree.GetItemText(selected) + self.Controler.ProjectRemoveConfiguration(name) + tagname = self.Controler.ComputeConfigurationName(name) + idx = self.IsOpened(tagname) + if idx is not None: + self.TabsOpened.DeletePage(idx) + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL) + + def OnRemoveResourceMenu(self, event): + selected = self.ProjectTree.GetSelection() + item_infos = self.ProjectTree.GetPyData(selected) + if item_infos["type"] == ITEM_RESOURCE: + resource = self.ProjectTree.GetItemText(selected) + config_name = item_infos["tagname"].split("::")[1] + self.Controler.ProjectRemoveConfigurationResource(config_name, resource) + tagname = self.Controler.ComputeConfigurationResourceName(config_name, selected) + idx = self.IsOpened(tagname) + if idx is not None: + self.TabsOpened.DeletePage(idx) + self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL) + +#------------------------------------------------------------------------------- +# Highlights showing functions +#------------------------------------------------------------------------------- + + def ShowHighlight(self, infos, start, end, highlight_type): + self.SelectProjectTreeItem(infos[0]) + if infos[1] == "name": + self.Highlights[infos[0]] = highlight_type + self.RefreshProjectTree() + self.ProjectTree.Unselect() + else: + self.EditProjectElement(self.Controler.GetElementType(infos[0]), infos[0]) + selected = self.TabsOpened.GetSelection() + if selected != -1: + viewer = self.TabsOpened.GetPage(selected) + viewer.AddHighlight(infos[1:], start, end, highlight_type) + + def ShowError(self, infos, start, end): + self.ShowHighlight(infos, start, end, ERROR_HIGHLIGHT) + + def ShowSearchResult(self, infos, start, end): + self.ShowHighlight(infos, start, end, SEARCH_RESULT_HIGHLIGHT) + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + self.Highlights = dict([(name, highlight) for name, highlight in self.Highlights.iteritems() if highlight != highlight_type]) + self.RefreshProjectTree() + for i in xrange(self.TabsOpened.GetPageCount()): + viewer = self.TabsOpened.GetPage(i) + viewer.ClearHighlights(highlight_type) + + def ClearErrors(self): + self.ClearHighlights(ERROR_HIGHLIGHT) + + def ClearSearchResults(self): + self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)