# HG changeset patch # User laurent # Date 1318182674 -7200 # Node ID 6014ef82a98ac2b6e95480872c7222c81cc703f6 # Parent 94c11207aa6f1ec1765f1bdb19e273237c9bcf8f Adding support for searching text or regular expression in whole project diff -r 94c11207aa6f -r 6014ef82a98a DataTypeEditor.py --- a/DataTypeEditor.py Fri Sep 30 17:16:02 2011 +0200 +++ b/DataTypeEditor.py Sun Oct 09 19:51:14 2011 +0200 @@ -26,6 +26,7 @@ import wx.grid import wx.gizmos from plcopen.structures import IEC_KEYWORDS, TestIdentifier +from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD import re @@ -261,7 +262,7 @@ parent.AddWindow(self.staticText2, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) parent.AddWindow(self.DirectlyBaseType, 1, border=5, flag=wx.GROW|wx.ALL) parent.AddWindow(self.staticText3, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) - parent.AddWindow(self.DirectlyInitialValue, 1, border=5, flag=wx.GROW|wx.ALL) + parent.AddWindow(self.DirectlyInitialValue, 1, border=5, flag=wx.ALL) def _init_coll_SubrangePanelSizer_Items(self, parent): parent.AddWindow(self.staticText4, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) @@ -299,7 +300,7 @@ def _init_coll_ArrayPanelRightSizer_Items(self, parent): parent.AddWindow(self.staticText10, 1, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) - parent.AddWindow(self.ArrayInitialValue, 1, border=5, flag=wx.GROW|wx.ALL) + parent.AddWindow(self.ArrayInitialValue, 1, border=5, flag=wx.ALL) def _init_coll_StructurePanelSizer_Items(self, parent): parent.AddWindow(self.staticText11, 0, border=5, flag=wx.ALL) @@ -582,12 +583,18 @@ self.ArrayPanel.Hide() self.StructurePanel.Hide() self.CurrentPanel = "Directly" - self.Errors = [] + self.Highlights = [] self.Initializing = False self.ParentWindow = window self.Controler = controler self.TagName = tagname + + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) + + def __del__(self): + self.RefreshHighlightsTimer.Stop() def SetTagName(self, tagname): self.TagName = tagname @@ -921,63 +928,62 @@ self.ParentWindow.RefreshEditMenu() #------------------------------------------------------------------------------- -# Errors showing functions +# Highlights showing functions #------------------------------------------------------------------------------- - def ClearErrors(self): - self.Errors = [] + def OnRefreshHighlightsTimer(self, event): self.RefreshView() - - def AddShownError(self, infos, start, end): - self.Errors.append((infos, start, end)) + event.Skip() + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = [] + else: + self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type] + self.RefreshView() + + def AddHighlight(self, infos, start, end ,highlight_type): + self.Highlights.append((infos, start, end, highlight_type)) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) def ShowErrors(self): type_infos = self.Controler.GetDataTypeInfos(self.TagName) - for infos, start, end in self.Errors: + for infos, start, end, highlight_type in self.Highlights: if infos[0] == "base": if type_infos["type"] == "Directly": - self.DirectlyBaseType.SetBackgroundColour(wx.Colour(255, 255, 0)) - self.DirectlyBaseType.SetForegroundColour(wx.RED) + self.DirectlyBaseType.SetBackgroundColour(highlight_type[0]) + self.DirectlyBaseType.SetForegroundColour(highlight_type[1]) elif type_infos["type"] == "Subrange": - self.SubrangeBaseType.SetBackgroundColour(wx.Colour(255, 255, 0)) - self.SubrangeBaseType.SetForegroundColour(wx.RED) + self.SubrangeBaseType.SetBackgroundColour(highlight_type[0]) + self.SubrangeBaseType.SetForegroundColour(highlight_type[1]) elif type_infos["type"] == "Array": - self.ArrayBaseType.SetBackgroundColour(wx.Colour(255, 255, 0)) - self.ArrayBaseType.SetForegroundColour(wx.RED) + self.ArrayBaseType.SetBackgroundColour(highlight_type[0]) + self.ArrayBaseType.SetForegroundColour(highlight_type[1]) elif infos[0] == "lower": - self.SubrangeMinimum.SetBackgroundColour(wx.Colour(255, 255, 0)) - self.SubrangeMaximum.SetForegroundColour(wx.RED) + self.SubrangeMinimum.SetBackgroundColour(highlight_type[0]) + self.SubrangeMaximum.SetForegroundColour(highlight_type[1]) elif infos[0] == "upper": - self.SubrangeMinimum.SetBackgroundColour(wx.Colour(255, 255, 0)) - self.SubrangeMaximum.SetForegroundColour(wx.RED) + self.SubrangeMinimum.SetBackgroundColour(highlight_type[0]) + self.SubrangeMaximum.SetForegroundColour(highlight_type[1]) elif infos[0] == "value": listctrl = self.EnumeratedValues.GetListCtrl() - listctrl.SetItemBackgroundColour(infos[1], wx.Colour(255, 255, 0)) - listctrl.SetItemTextColour(infos[1], wx.RED) + listctrl.SetItemBackgroundColour(infos[1], highlight_type[0]) + listctrl.SetItemTextColour(infos[1], highlight_type[1]) listctrl.Select(listctrl.FocusedItem, False) elif infos[0] == "range": listctrl = self.EnumeratedValues.GetListCtrl() - listctrl.SetItemBackgroundColour(infos[1], wx.Colour(255, 255, 0)) - listctrl.SetItemTextColour(infos[1], wx.RED) + listctrl.SetItemBackgroundColour(infos[1], highlight_type[0]) + listctrl.SetItemTextColour(infos[1], highlight_type[1]) listctrl.SetStringSelection("") elif infos[0] == "initial": if type_infos["type"] == "Directly": - text = self.DirectlyInitialValue.GetValue() - self.DirectlyInitialValue.SetValue(text[:start[1]]) - self.DirectlyInitialValue.SetDefaultStyle(wx.TextAttr(wx.RED, wx.Colour(255, 255, 0))) - self.DirectlyInitialValue.AppendText(text[start[1]:end[1] + 1]) - self.DirectlyInitialValue.SetDefaultStyle(wx.TextAttr(wx.BLACK, wx.WHITE)) - self.DirectlyInitialValue.AppendText(text[end[1] + 1:]) + self.DirectlyInitialValue.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0])) elif type_infos["type"] == "Subrange": - self.SubrangeInitialValue.SetBackgroundColour(wx.Colour(255, 255, 0)) - self.SubrangeInitialValue.SetForegroundColour(wx.RED) + self.SubrangeInitialValue.SetBackgroundColour(highlight_type[0]) + self.SubrangeInitialValue.SetForegroundColour(highlight_type[1]) elif type_infos["type"] == "Enumerated": - self.EnumeratedInitialValue.SetBackgroundColour(wx.Colour(255, 255, 0)) - self.EnumeratedInitialValue.SetForegroundColour(wx.RED) + self.EnumeratedInitialValue.SetBackgroundColour(highlight_type[0]) + self.EnumeratedInitialValue.SetForegroundColour(highlight_type[1]) elif type_infos["type"] == "Array": - text = self.ArrayInitialValue.GetValue() - self.ArrayInitialValue.SetValue(text[:start[1]]) - self.ArrayInitialValue.SetDefaultStyle(wx.TextAttr(wx.RED, wx.Colour(255, 255, 0))) - self.ArrayInitialValue.AppendText(text[start[1]:end[1] + 1]) - self.ArrayInitialValue.SetDefaultStyle(wx.TextAttr(wx.BLACK, wx.WHITE)) - self.ArrayInitialValue.AppendText(text[end[1] + 1:]) + self.ArrayInitialValue.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0])) + diff -r 94c11207aa6f -r 6014ef82a98a Images/BLOCK.png Binary file Images/BLOCK.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/COIL.png Binary file Images/COIL.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/COMMENT.png Binary file Images/COMMENT.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/CONNECTOR.png Binary file Images/CONNECTOR.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/CONTACT.png Binary file Images/CONTACT.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/IO_VARIABLE.png Binary file Images/IO_VARIABLE.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/JUMP.png Binary file Images/JUMP.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/STEP.png Binary file Images/STEP.png has changed diff -r 94c11207aa6f -r 6014ef82a98a Images/icons.svg --- a/Images/icons.svg Fri Sep 30 17:16:02 2011 +0200 +++ b/Images/icons.svg Sun Oct 09 19:51:14 2011 +0200 @@ -1,5 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + inkscape:window-y="47" + inkscape:window-maximized="0"> %% VAR_LOCAL VAR_INPUT VAR_OUTPUT VAR_INOUT %% + %% COMMENT BLOCK IO_VARIABLE CONNECTOR CONTACT COIL STEP JUMP %% + + + + + + CMT + + + + FB + + + + + + VAR + + + + + + + C + + + + + VAR + + + + + + + + + + VAR + + + + + STEP + + + JP + + diff -r 94c11207aa6f -r 6014ef82a98a PLCControler.py --- a/PLCControler.py Fri Sep 30 17:16:02 2011 +0200 +++ b/PLCControler.py Sun Oct 09 19:51:14 2011 +0200 @@ -311,7 +311,14 @@ self.Project.setcontentHeader(properties) if name is not None or properties is not None: self.BufferProject() - + + # Return project name + def GetProjectName(self, debug=False): + project = self.GetProject(debug) + if project is not None: + return project.getname() + return None + # Return project properties def GetProjectProperties(self, debug = False): project = self.GetProject(debug) @@ -2695,6 +2702,13 @@ return True #------------------------------------------------------------------------------- +# Search in Current Project Functions +#------------------------------------------------------------------------------- + + def SearchInProject(self, criteria): + return self.Project.Search(criteria) + +#------------------------------------------------------------------------------- # Current Buffering Management Functions #------------------------------------------------------------------------------- diff -r 94c11207aa6f -r 6014ef82a98a PLCGenerator.py --- a/PLCGenerator.py Fri Sep 30 17:16:02 2011 +0200 +++ b/PLCGenerator.py Sun Oct 09 19:51:14 2011 +0200 @@ -40,6 +40,12 @@ pouTypeNames = {"function" : "FUNCTION", "functionBlock" : "FUNCTION_BLOCK", "program" : "PROGRAM"} +errorVarTypes = { + "VAR_INPUT": "var_input", + "VAR_OUTPUT": "var_output", + "VAR_INOUT": "var_inout", +} + # Helper function for reindenting text def ReIndentText(text, nb_spaces): compute = "" @@ -247,57 +253,39 @@ var_number = 0 # Generate any global variable in configuration for varlist in configuration.getglobalVars(): + variable_type = errorVarTypes.get("VAR_GLOBAL", "var_local") # Generate variable block with modifier config += [(" VAR_GLOBAL", ())] if varlist.getconstant(): - config += [(" CONSTANT", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "constant"))] + config += [(" CONSTANT", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "constant"))] elif varlist.getretain(): - config += [(" RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "retain"))] + config += [(" RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "retain"))] elif varlist.getnonretain(): - config += [(" NON_RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "non_retain"))] + config += [(" NON_RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "non_retain"))] config += [("\n", ())] # Generate any variable of this block for var in varlist.getvariable(): vartype_content = var.gettype().getcontent() - # Variable type is a user data type if vartype_content["name"] == "derived": var_type = vartype_content["value"].getname() - # Variable type is a string type - elif vartype_content["name"] in ["string", "wstring"]: - var_type = vartype_content["name"].upper() - # Variable type is an array - elif vartype_content["name"] == "array": - base_type = vartype_content["value"].baseType.getcontent() - # Array derived directly from a user defined type - if base_type["name"] == "derived": - basetype_name = base_type["value"].getname() - self.GenerateDataType(basetype_name) - # Array derived directly from a string type - elif base_type["name"] in ["string", "wstring"]: - basetype_name = base_type["name"].upper() - # Array derived directly from an elementary type - else: - basetype_name = base_type["name"] - var_type = "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name) - # Variable type is an elementary type - else: - var_type = vartype_content["name"] + self.GenerateDataType(var_type) + config += [(" ", ()), - (var.getname(), (tagname, "variable", var_number, "name")), + (var.getname(), (tagname, variable_type, var_number, "name")), (" ", ())] # Generate variable address if exists address = var.getaddress() if address: config += [("AT ", ()), - (address, (tagname, "variable", var_number, "address")), + (address, (tagname, variable_type, var_number, "address")), (" ", ())] config += [(": ", ()), - (var_type, (tagname, "variable", var_number, "type"))] + (var.gettypeAsText(), (tagname, variable_type, var_number, "type"))] # Generate variable initial value if exists initial = var.getinitialValue() if initial: config += [(" := ", ()), - (self.ComputeValue(initial.getvalue(), var_type), (tagname, "variable", var_number, "initial"))] + (self.ComputeValue(initial.getvalue(), var_type), (tagname, variable_type, var_number, "initial"))] config += [(";\n", ())] var_number += 1 config += [(" END_VAR\n", ())] @@ -316,57 +304,39 @@ var_number = 0 # Generate any global variable in configuration for varlist in resource.getglobalVars(): + variable_type = errorVarTypes.get("VAR_GLOBAL", "var_local") # Generate variable block with modifier resrce += [(" VAR_GLOBAL", ())] if varlist.getconstant(): - resrce += [(" CONSTANT", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "constant"))] + resrce += [(" CONSTANT", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "constant"))] elif varlist.getretain(): - resrce += [(" RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "retain"))] + resrce += [(" RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "retain"))] elif varlist.getnonretain(): - resrce += [(" NON_RETAIN", (tagname, "variable", (var_number, var_number + len(varlist.getvariable())), "non_retain"))] + resrce += [(" NON_RETAIN", (tagname, variable_type, (var_number, var_number + len(varlist.getvariable())), "non_retain"))] resrce += [("\n", ())] # Generate any variable of this block for var in varlist.getvariable(): vartype_content = var.gettype().getcontent() - # Variable type is a user data type if vartype_content["name"] == "derived": var_type = vartype_content["value"].getname() - # Variable type is a string type - elif vartype_content["name"] in ["string", "wstring"]: - var_type = vartype_content["name"].upper() - # Variable type is an array - elif vartype_content["name"] == "array": - base_type = vartype_content["value"].baseType.getcontent() - # Array derived directly from a user defined type - if base_type["name"] == "derived": - basetype_name = base_type["value"].getname() - self.GenerateDataType(basetype_name) - # Array derived directly from a string type - elif base_type["name"] in ["string", "wstring"]: - basetype_name = base_type["name"].upper() - # Array derived directly from an elementary type - else: - basetype_name = base_type["name"] - var_type = "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name) - # Variable type is an elementary type - else: - var_type = vartype_content["name"] + self.GenerateDataType(var_type) + resrce += [(" ", ()), - (var.getname(), (tagname, "variable", var_number, "name")), + (var.getname(), (tagname, variable_type, var_number, "name")), (" ", ())] address = var.getaddress() # Generate variable address if exists if address: resrce += [("AT ", ()), - (address, (tagname, "variable", var_number, "address")), + (address, (tagname, variable_type, var_number, "address")), (" ", ())] resrce += [(": ", ()), - (var_type, (tagname, "variable", var_number, "type"))] + (var.gettypeAsText(), (tagname, variable_type, var_number, "type"))] # Generate variable initial value if exists initial = var.getinitialValue() if initial: resrce += [(" := ", ()), - (self.ComputeValue(initial.getvalue(), var_type), (tagname, "variable", var_number, "initial"))] + (self.ComputeValue(initial.getvalue(), var_type), (tagname, variable_type, var_number, "initial"))] resrce += [(";\n", ())] var_number += 1 resrce += [(" END_VAR\n", ())] @@ -598,6 +568,7 @@ else: variables.append(variable) else: + self.ParentGenerator.GenerateDataType(var_type) initial = var.getinitialValue() if initial: initial_value = initial.getvalue() @@ -609,30 +580,13 @@ else: variables.append((vartype_content["value"].getname(), var.getname(), None, initial_value)) else: + var_type = var.gettypeAsText() initial = var.getinitialValue() if initial: initial_value = initial.getvalue() else: initial_value = None address = var.getaddress() - if vartype_content["name"] in ["string", "wstring"]: - var_type = vartype_content["name"].upper() - # Variable type is an array - elif vartype_content["name"] == "array": - base_type = vartype_content["value"].baseType.getcontent() - # Array derived directly from a user defined type - if base_type["name"] == "derived": - basetype_name = base_type["value"].getname() - self.ParentGenerator.GenerateDataType(basetype_name) - # Array derived directly from a string type - elif base_type["name"] in ["string", "wstring"]: - basetype_name = base_type["name"].upper() - # Array derived directly from an elementary type - else: - basetype_name = base_type["name"] - var_type = "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name) - else: - var_type = vartype_content["name"] if address is not None: located.append((var_type, var.getname(), address, initial_value)) else: @@ -879,6 +833,10 @@ block_type = instance.gettypeName() self.ParentGenerator.GeneratePouProgram(block_type) block_infos = self.GetBlockType(block_type, tuple([self.ConnectionTypes.get(variable.connectionPointIn, "ANY") for variable in instance.inputVariables.getvariable() if variable.getformalParameter() != "EN"])) + if block_infos is None: + block_infos = self.GetBlockType(block_type) + if block_infos is None: + raise PLCGenException, _("Undefined block type \"%s\" in \"%s\" POU")%(block_type, self.Name) block_infos["generate"](self, instance, block_infos, body, None) elif isinstance(instance, plcopen.commonObjects_connector): connector = instance.getname() @@ -934,6 +892,10 @@ block_type = next.gettypeName() self.ParentGenerator.GeneratePouProgram(block_type) block_infos = self.GetBlockType(block_type, tuple([self.ConnectionTypes.get(variable.connectionPointIn, "ANY") for variable in next.inputVariables.getvariable() if variable.getformalParameter() != "EN"])) + if block_infos is None: + block_infos = self.GetBlockType(block_type) + if block_infos is None: + raise PLCGenException, _("Undefined block type \"%s\" in \"%s\" POU")%(block_type, self.Name) paths.append(str(block_infos["generate"](self, next, block_infos, body, connection, order))) elif isinstance(next, plcopen.commonObjects_continuation): name = next.getname() @@ -1331,24 +1293,25 @@ raise PLCGenException, _("No body defined in \"%s\" POU")%self.Name var_number = 0 for list_type, option, located, variables in self.Interface: + variable_type = errorVarTypes.get(list_type, "var_local") program += [(" %s"%list_type, ())] if option is not None: - program += [(" %s"%option, (self.TagName, "variable", (var_number, var_number + len(variables)), option.lower()))] + program += [(" %s"%option, (self.TagName, variable_type, (var_number, var_number + len(variables)), option.lower()))] program += [("\n", ())] for var_type, var_name, var_address, var_initial in variables: program += [(" ", ())] if var_name: - program += [(var_name, (self.TagName, "variable", var_number, "name")), + program += [(var_name, (self.TagName, variable_type, var_number, "name")), (" ", ())] if var_address != None: program += [("AT ", ()), - (var_address, (self.TagName, "variable", var_number, "address")), + (var_address, (self.TagName, variable_type, var_number, "address")), (" ", ())] program += [(": ", ()), - (var_type, (self.TagName, "variable", var_number, "type"))] + (var_type, (self.TagName, variable_type, var_number, "type"))] if var_initial != None: program += [(" := ", ()), - (self.ParentGenerator.ComputeValue(var_initial, var_type), (self.TagName, "variable", var_number, "initial"))] + (self.ParentGenerator.ComputeValue(var_initial, var_type), (self.TagName, variable_type, var_number, "initial"))] program += [(";\n", ())] var_number += 1 program += [(" END_VAR\n", ())] diff -r 94c11207aa6f -r 6014ef82a98a PLCOpenEditor.py --- a/PLCOpenEditor.py Fri Sep 30 17:16:02 2011 +0200 +++ b/PLCOpenEditor.py Sun Oct 09 19:51:14 2011 +0200 @@ -114,6 +114,7 @@ from DataTypeEditor import * from PLCControler import * from VariablePanel import VariablePanel +from SearchResultPanel import SearchResultPanel # Define PLCOpenEditor controls id [ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK, @@ -135,8 +136,9 @@ # Define PLCOpenEditor EditMenu extra items id [ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE, ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, - ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, -] = [wx.NewId() for _init_coll_EditMenu_Items in range(6)] + ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, + ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, +] = [wx.NewId() for _init_coll_EditMenu_Items in range(7)] #------------------------------------------------------------------------------- @@ -330,6 +332,9 @@ AppendMenu(parent, help='', id=wx.ID_PASTE, kind=wx.ITEM_NORMAL, text=_(u'Paste\tCTRL+V')) parent.AppendSeparator() + AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, + kind=wx.ITEM_NORMAL, text=_(u'Search in Project')) + parent.AppendSeparator() addmenu = wx.Menu(title='') parent.AppendMenu(wx.ID_ADD, _("Add Element"), addmenu) AppendMenu(addmenu, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE, @@ -352,6 +357,8 @@ 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.OnSearchInProjectMenu, + id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, id=ID_PLCOPENEDITOREDITMENUADDDATATYPE) self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("function"), @@ -594,6 +601,9 @@ self.VariablePanelIndexer = VariablePanelIndexer(self.BottomNoteBook, self) self.BottomNoteBook.AddPage(self.VariablePanelIndexer, _("Variables")) + self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self) + self.BottomNoteBook.AddPage(self.SearchResultPanel, _("Search")) + self.LibraryPanel = wx.Panel(id=ID_PLCOPENEDITORLIBRARYPANEL, name='LibraryPanel', parent=self.RightNoteBook, pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0) @@ -613,9 +623,9 @@ id=ID_PLCOPENEDITORLIBRARYTREE) self.LibraryComment = wx.TextCtrl(id=ID_PLCOPENEDITORLIBRARYCOMMENT, - name='LibraryComment', parent=self.LibraryPanel, - pos=wx.Point(0, 0), size=wx.Size(0, 160), - style=wx.TE_READONLY|wx.TE_MULTILINE) + name='LibraryComment', parent=self.LibraryPanel, + pos=wx.Point(0, 0), size=wx.Size(0, 160), + style=wx.TE_READONLY|wx.TE_MULTILINE) self._init_sizers() @@ -683,7 +693,7 @@ self.CurrentToolBar = [] self.CurrentLanguage = "" self.SelectedItem = None - self.Errors = [] + self.Highlights = {} self.DrawingMode = FREEDRAWING_MODE #self.DrawingMode = DRIVENDRAWING_MODE if USE_AUI: @@ -992,6 +1002,7 @@ #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True) #self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, # self.Controler.IsProjectBufferEnabled()) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True) self.EditMenu.Enable(wx.ID_ADD, True) self.EditMenu.Enable(wx.ID_DELETE, True) if self.TabsOpened.GetPageCount() > 0: @@ -1015,6 +1026,7 @@ self.EditMenu.Enable(wx.ID_COPY, False) self.EditMenu.Enable(wx.ID_PASTE, False) self.EditMenu.Enable(wx.ID_SELECTALL, False) + self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, False) self.EditMenu.Enable(wx.ID_ADD, False) self.EditMenu.Enable(wx.ID_DELETE, False) @@ -1086,7 +1098,15 @@ event.m_keyCode = wx.WXK_DELETE window.ProcessEvent(event) - + 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 #------------------------------------------------------------------------------- @@ -1273,12 +1293,9 @@ item_name = _(item_name) self.TypesTree.SetItemText(root, item_name) self.TypesTree.SetPyData(root, infos["type"]) - if infos.get("tagname", None) in self.Errors: - self.TypesTree.SetItemBackgroundColour(root, wx.Colour(255, 255, 0)) - self.TypesTree.SetItemTextColour(root, wx.RED) - else: - self.TypesTree.SetItemBackgroundColour(root, wx.WHITE) - self.TypesTree.SetItemTextColour(root, wx.BLACK) + highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.WHITE, wx.BLACK)) + self.TypesTree.SetItemBackgroundColour(root, highlight_colours[0]) + self.TypesTree.SetItemTextColour(root, highlight_colours[1]) if infos["type"] == ITEM_POU: self.TypesTree.SetItemImage(root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])]) else: @@ -2422,32 +2439,48 @@ #------------------------------------------------------------------------------- -# Errors showing functions -#------------------------------------------------------------------------------- - - def ShowError(self, infos, start, end): - self.EditProjectElement(self.Controler.GetElementType(infos[0]), infos[0]) +# Highlights showing functions +#------------------------------------------------------------------------------- + + def ShowHighlight(self, infos, start, end, highlight_type): + print infos, start, end, highlight_type self.SelectTypesTreeItem(infos[0]) if infos[1] == "name": - self.Errors.append(infos[0]) + self.Highlights[infos[0]] = highlight_type self.RefreshTypesTree() self.TypesTree.Unselect() - elif infos[1] == "variable": - self.VariablePanelIndexer.AddVariableError(infos) else: - selected = self.TabsOpened.GetSelection() - if selected != -1: - viewer = self.TabsOpened.GetPage(selected) - viewer.AddShownError(infos[1:], start, end) - viewer.RefreshView() - - def ClearErrors(self): - self.Errors = [] + self.EditProjectElement(self.Controler.GetElementType(infos[0]), infos[0]) + if infos[1] in plcopen.searchResultVarTypes.values() + ["var_local"]: + self.VariablePanelIndexer.AddVariableHighlight(infos, highlight_type) + else: + 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.RefreshTypesTree() - self.VariablePanelIndexer.ClearErrors() + self.VariablePanelIndexer.ClearHighlights(highlight_type) for i in xrange(self.TabsOpened.GetPageCount()): viewer = self.TabsOpened.GetPage(i) - viewer.ClearErrors() + viewer.ClearHighlights(highlight_type) + + def ClearErrors(self): + self.ClearHighlights(ERROR_HIGHLIGHT) + + def ClearSearchResults(self): + self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT) #------------------------------------------------------------------------------- # PLCOpenEditor Main Class @@ -3814,6 +3847,12 @@ # Variables Editor Panel #------------------------------------------------------------------------------- +def PouTagname(tagname): + words = tagname.split("::") + if words[0] in ["T", "A"]: + return "P::%s" % words[1] + return tagname + class VariablePanelIndexer(wx.Panel): def _init_sizers(self): @@ -3837,58 +3876,72 @@ self.CurrentPanel = None def AddVariablePanel(self, tagname, element_type): - new_panel = VariablePanel(self, self.ParentWindow, self.ParentWindow.Controler, element_type) - new_panel.SetTagName(tagname) - new_panel.Hide() - new_panel.RefreshView() - self.MainSizer.AddWindow(new_panel, 1, border=0, flag=wx.GROW) - self.VariablePanelList[tagname] = new_panel + tagname = PouTagname(tagname) + panel, users = self.VariablePanelList.get(tagname, (None, 0)) + if panel is None: + panel = VariablePanel(self, self.ParentWindow, self.ParentWindow.Controler, element_type) + panel.SetTagName(tagname) + panel.Hide() + panel.RefreshView() + self.MainSizer.AddWindow(panel, 1, border=0, flag=wx.GROW) + self.VariablePanelList[tagname] = (panel, users + 1) def RemoveVariablePanel(self, tagname): - if tagname in self.VariablePanelList: - panel = self.VariablePanelList.pop(tagname) + tagname = PouTagname(tagname) + panel, users = self.VariablePanelList.pop(tagname, (None, 0)) + if panel is not None: + if users > 1: + self.VariablePanelList[tagname] = (panel, users - 1) + else: + self.MainSizer.Remove(panel) + panel.Destroy() + if self.CurrentPanel == tagname: + self.CurrentPanel = None + + def RemoveAllPanels(self): + for panel, users in self.VariablePanelList.itervalues(): self.MainSizer.Remove(panel) panel.Destroy() - if self.CurrentPanel == tagname: - self.CurrentPanel = None - - def RemoveAllPanels(self): - for tagname in self.VariablePanelList.keys(): - self.RemoveVariablePanel(tagname) + self.VariablePanelList = {} + self.CurrentPanel = None def UpdateVariablePanelTagName(self, old_tagname, new_tagname): - if old_tagname in self.VariablePanelList: + old_tagname = PouTagname(old_tagname) + new_tagname = PouTagname(new_tagname) + if old_tagname in self.VariablePanelList and old_tagname != new_tagname: self.VariablePanelList[new_tagname] = self.VariablePanelList.pop(old_tagname) if self.CurrentPanel == old_tagname: self.CurrentPanel = new_tagname def ChangeVariablePanel(self, tagname): - if tagname in self.VariablePanelList: - if tagname != self.CurrentPanel: - if self.CurrentPanel is not None: - self.VariablePanelList[self.CurrentPanel].Hide() - self.CurrentPanel = tagname - self.VariablePanelList[self.CurrentPanel].RefreshView() - self.VariablePanelList[self.CurrentPanel].Show() - self.MainSizer.Layout() - else: + tagname = PouTagname(tagname) + panel, users = self.VariablePanelList.get(tagname, (None, 0)) + if panel is None: if self.CurrentPanel is not None: - self.VariablePanelList[self.CurrentPanel].Hide() + self.VariablePanelList[self.CurrentPanel][0].Hide() self.CurrentPanel = None self.MainSizer.Layout() + elif tagname != self.CurrentPanel: + if self.CurrentPanel is not None: + self.VariablePanelList[self.CurrentPanel][0].Hide() + self.CurrentPanel = tagname + panel.RefreshView() + panel.Show() + self.MainSizer.Layout() def RefreshVariablePanel(self, tagname): - if tagname in self.VariablePanelList: - self.VariablePanelList[self.CurrentPanel].RefreshView() - - def AddVariableError(self, infos): + panel, users = self.VariablePanelList.get(PouTagname(tagname), (None, 0)) + if panel is not None: + panel.RefreshView() + + def AddVariableHighlight(self, infos, highlight_type): self.ChangeVariablePanel(infos[0]) if self.CurrentPanel is not None: - self.VariablePanelList[self.CurrentPanel].AddVariableError(infos[2:]) - - def ClearErrors(self): - for panel in self.VariablePanelList.values(): - panel.ClearErrors() + self.VariablePanelList[self.CurrentPanel][0].AddVariableHighlight(infos[2:], highlight_type) + + def ClearHighlights(self, highlight_type=None): + for panel, users in self.VariablePanelList.values(): + panel.ClearHighlights(highlight_type) #------------------------------------------------------------------------------- # Debug Variables Panel diff -r 94c11207aa6f -r 6014ef82a98a RessourceEditor.py --- a/RessourceEditor.py Fri Sep 30 17:16:02 2011 +0200 +++ b/RessourceEditor.py Sun Oct 09 19:51:14 2011 +0200 @@ -101,7 +101,7 @@ wx.grid.PyGridTableBase.__init__(self) self.data = data self.colnames = colnames - self.Errors = {} + self.Highlights = {} self.Parent = parent self.ColAlignements = [] @@ -210,6 +210,7 @@ grid.SetColSize(col, self.ColSizes[col]) for row in range(self.GetNumberRows()): + row_highlights = self.Highlights.get(row, {}) for col in range(self.GetNumberCols()): editor = None renderer = None @@ -244,13 +245,9 @@ grid.SetCellEditor(row, col, editor) grid.SetCellRenderer(row, col, renderer) - if row in self.Errors and self.Errors[row][0] == colname.lower(): - grid.SetCellBackgroundColour(row, col, wx.Colour(255, 255, 0)) - grid.SetCellTextColour(row, col, wx.RED) - grid.MakeCellVisible(row, col) - else: - grid.SetCellTextColour(row, col, wx.BLACK) - grid.SetCellBackgroundColour(row, col, wx.WHITE) + highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] + grid.SetCellBackgroundColour(row, col, highlight_colours[0]) + grid.SetCellTextColour(row, col, highlight_colours[1]) if wx.Platform == '__WXMSW__': grid.SetRowMinimalHeight(row, 20) else: @@ -289,11 +286,31 @@ self.data = [] self.editors = [] - def AddError(self, infos): - self.Errors[infos[0]] = infos[1:] - - def ClearErrors(self): - self.Errors = {} +#------------------------------------------------------------------------------- +# Highlights showing functions +#------------------------------------------------------------------------------- + + def OnRefreshHighlightsTimer(self, event): + self.Table.ResetView(self.VariablesGrid) + event.Skip() + + def AddHighlight(self, infos, highlight_type): + row_highlights = self.Highlights.setdefault(infos[0], {}) + col_highlights = row_highlights.setdefault(infos[1], []) + col_highlights.append(highlight_type) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + for row, row_highlights in self.Highlights.iteritems(): + row_items = row_highlights.items() + for col, col_highlights in row_items: + if highlight_type in col_highlights: + col_highlights.remove(highlight_type) + if len(col_highlights) == 0: + row_highlights.pop(col) [ID_RESOURCEEDITOR, ID_RESOURCEEDITORSTATICTEXT1, ID_RESOURCEEDITORSTATICTEXT2, ID_RESOURCEEDITORINSTANCESGRID, @@ -465,6 +482,9 @@ self.Controler = controler self.TagName = tagname + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) + self.TasksDefaultValue = {"Name" : "", "Triggering" : "", "Single" : "", "Interval" : "", "Priority" : 0} self.TasksTable = ResourceTable(self, [], GetTasksTableColnames()) self.TasksTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, wx.ALIGN_RIGHT]) @@ -481,6 +501,9 @@ self.InstancesGrid.SetRowLabelSize(0) self.InstancesTable.ResetView(self.InstancesGrid) + def __del__(self): + self.RefreshHighlightsTimer.Stop() + def SetTagName(self, tagname): self.TagName = tagname @@ -668,21 +691,37 @@ event.Skip() #------------------------------------------------------------------------------- -# Errors showing functions -#------------------------------------------------------------------------------- - - def ClearErrors(self): - self.TasksTable.ClearErrors() - self.InstancesTable.ClearErrors() +# Search result showing functions +#------------------------------------------------------------------------------- + + def AddShownSearchResult(self, infos, start, end): + if infos[0] == "task": + self.TasksTable.AddSearchResult(infos[1:]) + elif infos[0] == "instance": + self.InstancesTable.AddSearchResult(infos[1:]) + + def ClearSearchResults(self): + self.TasksTable.ClearSearchResults() + self.InstancesTable.ClearSearchResults() self.TasksTable.ResetView(self.TasksGrid) self.InstancesTable.ResetView(self.InstancesGrid) - def AddShownError(self, infos, start, end): + +#------------------------------------------------------------------------------- +# Highlights showing functions +#------------------------------------------------------------------------------- + + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "task": - self.TasksTable.AddError(infos[1:]) + self.TasksTable.AddHighlight(infos[1:], highlight_type) elif infos[0] == "instance": - self.InstancesTable.AddError(infos[1:]) - + self.InstancesTable.AddHighlight(infos[1:], highlight_type) + + def ClearHighlights(self, highlight_type=None): + self.TasksTable.ClearHighlights(highlight_type) + self.InstancesTable.ClearHighlights(highlight_type) + self.TasksTable.ResetView(self.TasksGrid) + self.InstancesTable.ResetView(self.InstancesGrid) class DurationCellControl(wx.PyControl): diff -r 94c11207aa6f -r 6014ef82a98a SearchResultPanel.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SearchResultPanel.py Sun Oct 09 19:51:14 2011 +0200 @@ -0,0 +1,327 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from types import TupleType + +import wx +import wx.lib.agw.customtreectrl as CT + +from PLCControler import * + +CWD = os.path.split(os.path.realpath(__file__))[0] + +def GenerateName(infos): + if infos[0] in ["input", "output"]: + return "%s %d:" % (infos[0], infos[1]) + return "%s:" % infos[0] + +#------------------------------------------------------------------------------- +# Search Result Panel +#------------------------------------------------------------------------------- + +[ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL, + ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON, +] = [wx.NewId() for _init_ctrls in range(4)] + +class SearchResultPanel(wx.Panel): + + 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_MainSizer_Items(self, parent): + parent.AddSizer(self.HeaderSizer, 0, border=0, flag=wx.GROW) + parent.AddWindow(self.SearchResultsTree, 1, border=0, flag=wx.GROW) + + def _init_coll_MainSizer_Growables(self, parent): + parent.AddGrowableCol(0) + parent.AddGrowableRow(1) + + def _init_coll_HeaderSizer_Items(self, parent): + parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + parent.AddWindow(self.ResetButton, 0, border=0, flag=0) + + def _init_coll_HeaderSizer_Growables(self, parent): + parent.AddGrowableCol(0) + + def _init_sizers(self): + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) + self.HeaderSizer = wx.BoxSizer(wx.HORIZONTAL) + + self._init_coll_MainSizer_Items(self.MainSizer) + self._init_coll_MainSizer_Growables(self.MainSizer) + self._init_coll_HeaderSizer_Items(self.HeaderSizer) + + self.SetSizer(self.MainSizer) + + def _init_ctrls(self, prnt): + wx.Panel.__init__(self, id=ID_SEARCHRESULTPANEL, + name='SearchResultPanel', parent=prnt, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + + self.HeaderLabel = wx.StaticText(id=ID_SEARCHRESULTPANELHEADERLABEL, + name='HeaderLabel', parent=self, + pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) + + self.SearchResultsTree = CT.CustomTreeCtrl(id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE, + name="SearchResultsTree", parent=self, + pos=wx.Point(0, 0), style=0) + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated, + id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE) + + self.ResetButton = wx.Button(id=ID_SEARCHRESULTPANELRESETBUTTON, label=_('Reset'), + name='ResetButton', parent=self, pos=wx.Point(0, 0), + size=wx.DefaultSize, style=0) + self.Bind(wx.EVT_BUTTON, self.OnResetButton, + id=ID_SEARCHRESULTPANELRESETBUTTON) + + self._init_sizers() + + def __init__(self, parent, window): + self._init_ctrls(parent) + self.ParentWindow = window + + # Define Tree item icon list + self.TreeImageList = wx.ImageList(16, 16) + self.TreeImageDict = {} + + # Icons for other items + for imgname, itemtype in [ + #editables + ("PROJECT", ITEM_PROJECT), + ("TRANSITION", ITEM_TRANSITION), + ("ACTION", ITEM_ACTION), + ("CONFIGURATION", ITEM_CONFIGURATION), + ("RESOURCE", ITEM_RESOURCE), + ("DATATYPE", ITEM_DATATYPE), + ("ACTION", "action_block"), + ("IL", "IL"), + ("ST", "ST")]: + self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname))) + + for itemtype in ["function", "functionBlock", "program", + "comment", "block", "io_variable", + "connector", "contact", "coil", + "step", "transition", "jump", + "var_local", "var_input", + "var_inout", "var_output"]: + self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%itemtype.upper()))) + + # Assign icon list to TreeCtrl + self.SearchResultsTree.SetImageList(self.TreeImageList) + + self.ResetSearchResults() + + def SetSearchResults(self, criteria, search_results): + self.Criteria = criteria + + for infos, start, end, text in search_results: + if infos[0] not in self.ElementsOrder: + self.ElementsOrder.append(infos[0]) + + results = self.SearchResults.setdefault(infos[0], []) + results.append((infos, start, end, text)) + + self.RefreshView() + + def ResetSearchResults(self): + self.Criteria = None + self.ElementsOrder = [] + self.SearchResults = {} + self.RefreshView() + + def RefreshView(self): + if self.Criteria is None: + self.HeaderLabel.SetLabel(_("No search results available.")) + self.SearchResultsTree.DeleteAllItems() + self.ResetButton.Enable(False) + else: + matches_number = 0 + search_results_tree_infos = {"name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(), + "type": ITEM_PROJECT, + "data": None, + "text": None, + "matches": None, + } + search_results_tree_children = search_results_tree_infos.setdefault("children", []) + for tagname in self.ElementsOrder: + results = self.SearchResults.get(tagname, []) + matches_number += len(results) + + words = tagname.split("::") + + element_type = self.ParentWindow.Controler.GetElementType(tagname) + if element_type == ITEM_POU: + element_type = self.ParentWindow.Controler.GetPouType(words[1]) + + element_infos = {"name": words[-1], + "type": element_type, + "data": tagname, + "text": None, + "matches": len(results)} + + children = element_infos.setdefault("children", []) + for infos, start, end, text in results: + child_type = infos[1] + if child_type == "body": + child_name = "body" + if element_type == ITEM_TRANSITION: + child_type = self.ParentWindow.Controler.GetTransitionBodyType(words[1], words[2]) + elif element_type == ITEM_ACTION: + child_type = self.ParentWindow.Controler.GetActionBodyType(words[1], words[2]) + else: + child_type = self.ParentWindow.Controler.GetPouBodyType(words[1]) + else: + child_name = GenerateName(infos[3:]) + child_infos = {"name": child_name, + "type": child_type, + "data": (infos, start, end ,None), + "text": text, + "matches": 1, + "children": [], + } + children.append(child_infos) + + if len(words) > 2: + for _element_infos in search_results_tree_children: + if _element_infos["name"] == words[1]: + _element_infos["matches"] += len(children) + _element_infos["children"].append(element_infos) + break + else: + search_results_tree_children.append(element_infos) + + if matches_number < 2: + header_format = _("'%s' - %d match in project") + else: + header_format = _("'%s' - %d matches in project") + + self.HeaderLabel.SetLabel(header_format % (self.Criteria["raw_pattern"], matches_number)) + self.ResetButton.Enable(True) + + if matches_number > 0: + root = self.SearchResultsTree.GetRootItem() + if root is None: + root = self.SearchResultsTree.AddRoot(search_results_tree_infos["name"]) + self.GenerateSearchResultsTreeBranch(root, search_results_tree_infos) + self.SearchResultsTree.Expand(root) + + def GetTextCtrlClickFunction(self, item): + def OnTextCtrlClick(event): + self.SearchResultsTree.SelectItem(item) + event.Skip() + return OnTextCtrlClick + + def GetTextCtrlDClickFunction(self, item): + def OnTextCtrlDClick(event): + self.ShowSearchResults(item) + event.Skip() + return OnTextCtrlDClick + + def GenerateSearchResultsTreeBranch(self, root, infos): + to_delete = [] + if infos["name"] == "body": + item_name = "%d:" % infos["data"][1][0] + else: + item_name = infos["name"] + + self.SearchResultsTree.SetItemText(root, item_name) + self.SearchResultsTree.SetPyData(root, infos["data"]) + self.SearchResultsTree.SetItemBackgroundColour(root, wx.WHITE) + self.SearchResultsTree.SetItemTextColour(root, wx.BLACK) + if infos["type"] is not None: + if infos["type"] == ITEM_POU: + self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[self.ParentWindow.Controler.GetPouType(infos["name"])]) + else: + self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) + + if infos["text"] is not None: + text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), + value=infos["text"], style=wx.BORDER_NONE|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2) + width, height = wx.PaintDC(text_ctrl).GetTextExtent(infos["text"]) + text_ctrl.SetClientSize(wx.Size(width + 1, height)) + text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour()) + text_ctrl.Bind(wx.EVT_LEFT_DOWN, self.GetTextCtrlClickFunction(root)) + text_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.GetTextCtrlDClickFunction(root)) + start, end = infos["data"][1:3] + text_lines = infos["text"].splitlines() + end_idx = reduce(lambda x, y: x + y, map(lambda x: len(x) + 1, text_lines[:end[0] - start[0]]), end[1] + 1) + text_ctrl.SetInsertionPoint(0) + text_ctrl.SetStyle(start[1], end_idx, wx.TextAttr(wx.BLACK, wx.Colour(206, 204, 247))) + + self.SearchResultsTree.SetItemWindow(root, text_ctrl) + + elif infos["type"] is not None and infos["matches"] > 1: + text = _("(%d matches)") % infos["matches"] + text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), + value=text, style=wx.BORDER_NONE|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2) + width, height = wx.PaintDC(text_ctrl).GetTextExtent(text) + text_ctrl.SetClientSize(wx.Size(width + 1, height)) + text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour()) + text_ctrl.Bind(wx.EVT_LEFT_DOWN, self.GetTextCtrlClickFunction(root)) + text_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.GetTextCtrlDClickFunction(root)) + + text_ctrl.SetInsertionPoint(0) + text_ctrl.SetStyle(0, len(text), wx.TextAttr(wx.Colour(0, 127, 174))) + + self.SearchResultsTree.SetItemWindow(root, text_ctrl) + + + if wx.VERSION >= (2, 6, 0): + item, root_cookie = self.SearchResultsTree.GetFirstChild(root) + else: + item, root_cookie = self.SearchResultsTree.GetFirstChild(root, 0) + for child in infos["children"]: + if item is None: + item = self.SearchResultsTree.AppendItem(root, "") + if wx.Platform != '__WXMSW__': + item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) + self.GenerateSearchResultsTreeBranch(item, child) + item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) + while item is not None: + to_delete.append(item) + item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) + for item in to_delete: + self.SearchResultsTree.Delete(item) + + def ShowSearchResults(self, item): + data = self.SearchResultsTree.GetPyData(item) + if isinstance(data, TupleType): + search_results = [data] + else: + search_results = self.SearchResults.get(data, []) + for infos, start, end, text in search_results: + self.ParentWindow.ShowSearchResult(infos, start, end) + + def OnSearchResultsTreeItemActivated(self, event): + self.ShowSearchResults(event.GetItem()) + event.Skip() + + def OnResetButton(self, event): + self.ResetSearchResults() + self.ParentWindow.ClearSearchResults() + event.Skip() diff -r 94c11207aa6f -r 6014ef82a98a TextViewer.py --- a/TextViewer.py Fri Sep 30 17:16:02 2011 +0200 +++ b/TextViewer.py Sun Oct 09 19:51:14 2011 +0200 @@ -28,6 +28,8 @@ import re +from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD + #------------------------------------------------------------------------------- # Textual programs Viewer class #------------------------------------------------------------------------------- @@ -42,7 +44,7 @@ [STC_PLC_WORD, STC_PLC_COMMENT, STC_PLC_NUMBER, STC_PLC_STRING, STC_PLC_VARIABLE, STC_PLC_PARAMETER, STC_PLC_FUNCTION, STC_PLC_JUMP, - STC_PLC_ERROR] = range(9) + STC_PLC_ERROR, STC_PLC_SEARCH_RESULT] = range(10) [SPACE, WORD, NUMBER, STRING, WSTRING, COMMENT] = range(6) [ID_TEXTVIEWER, @@ -70,6 +72,11 @@ LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]"%re_texts) EXTENSIBLE_PARAMETER = re.compile("IN[1-9][0-9]*$") +HIGHLIGHT_TYPES = { + ERROR_HIGHLIGHT: STC_PLC_ERROR, + SEARCH_RESULT_HIGHLIGHT: STC_PLC_SEARCH_RESULT, +} + def GetCursorPos(old, new): old_length = len(old) new_length = len(new) @@ -128,6 +135,7 @@ self.StyleSetSpec(STC_PLC_STRING, "fore:#007F00,size:%(size)d" % faces) self.StyleSetSpec(STC_PLC_JUMP, "fore:#FF7FFF,size:%(size)d" % faces) self.StyleSetSpec(STC_PLC_ERROR, "fore:#FF0000,back:#FFFF00,size:%(size)d" % faces) + self.StyleSetSpec(STC_PLC_SEARCH_RESULT, "fore:#FFFFFF,back:#FFA500,size:%(size)d" % faces) # Indicators styles self.IndicatorSetStyle(0, wx.stc.STC_INDIC_SQUIGGLE) @@ -154,7 +162,7 @@ self.TextSyntax = "ST" self.CurrentAction = None self.TagName = tagname - self.Errors = [] + self.Highlights = [] self.Debug = debug self.InstancePath = instancepath self.ContextStack = [] @@ -163,6 +171,9 @@ self.ParentWindow = window self.Controler = controler + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) + self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT| wx.stc.STC_MOD_BEFOREDELETE| wx.stc.STC_PERFORMED_USER) @@ -174,6 +185,9 @@ self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_TEXTVIEWER) + def __del__(self): + self.RefreshHighlightsTimer.Stop() + def SetTagName(self, tagname): self.TagName = tagname @@ -568,7 +582,7 @@ self.SetStyling(current_pos - last_styled_pos, 31) else: self.SetStyling(current_pos - start_pos, 31) - self.ShowErrors(start_pos, end_pos) + self.ShowHighlights(start_pos, end_pos) event.Skip() def Cut(self): @@ -637,27 +651,39 @@ event.Skip() #------------------------------------------------------------------------------- -# Errors showing functions +# Highlights showing functions #------------------------------------------------------------------------------- - def ClearErrors(self): - self.Errors = [] + def OnRefreshHighlightsTimer(self, event): self.RefreshView() - - def AddShownError(self, infos, start, end): - if infos[0] == "body": - self.Errors.append((infos[1], start, end)) - - def ShowErrors(self, start_pos, end_pos): - for indent, start, end in self.Errors: + event.Skip() + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = [] + else: + highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) + if highlight_type is not None: + self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type] + self.RefreshView() + + def AddHighlight(self, infos, start, end, highlight_type): + highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) + if infos[0] == "body" and highlight_type is not None: + self.Highlights.append((infos[1], start, end, highlight_type)) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ShowHighlights(self, start_pos, end_pos): + for indent, start, end, highlight_type in self.Highlights: if start[0] == 0: - error_start_pos = start[1] - indent - else: - error_start_pos = self.GetLineEndPosition(start[0] - 1) + start[1] - indent + 1 + highlight_start_pos = start[1] - indent + else: + highlight_start_pos = self.GetLineEndPosition(start[0] - 1) + start[1] - indent + 1 if end[0] == 0: - error_end_pos = end[1] - indent + 1 - else: - error_end_pos = self.GetLineEndPosition(end[0] - 1) + end[1] - indent + 2 - if start_pos <= error_start_pos <= end_pos or start_pos <= error_end_pos <= end_pos: - self.StartStyling(error_start_pos, 0xff) - self.SetStyling(error_end_pos - error_start_pos, STC_PLC_ERROR) + highlight_end_pos = end[1] - indent + 1 + else: + highlight_end_pos = self.GetLineEndPosition(end[0] - 1) + end[1] - indent + 2 + if highlight_start_pos < end_pos and highlight_end_pos > start_pos: + self.StartStyling(highlight_start_pos, 0xff) + self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type) + diff -r 94c11207aa6f -r 6014ef82a98a VariablePanel.py --- a/VariablePanel.py Fri Sep 30 17:16:02 2011 +0200 +++ b/VariablePanel.py Sun Oct 09 19:51:14 2011 +0200 @@ -1,3 +1,4 @@ +#!/usr/bin/env python # -*- coding: utf-8 -*- #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor @@ -28,6 +29,7 @@ from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY +from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD from dialogs import ArrayTypeDialog CWD = os.path.split(os.path.realpath(__file__))[0] @@ -94,7 +96,7 @@ self.data = data self.old_value = None self.colnames = colnames - self.Errors = {} + self.Highlights = {} self.Parent = parent # XXX # we need to store the row length and collength to @@ -200,6 +202,7 @@ for row in range(self.GetNumberRows()): var_class = self.GetValueByName(row, "Class") var_type = self.GetValueByName(row, "Type") + row_highlights = self.Highlights.get(row, {}) for col in range(self.GetNumberCols()): editor = None renderer = None @@ -254,13 +257,9 @@ grid.SetCellEditor(row, col, editor) grid.SetCellRenderer(row, col, renderer) - if row in self.Errors and self.Errors[row][0] == colname.lower(): - grid.SetCellBackgroundColour(row, col, wx.Colour(255, 255, 0)) - grid.SetCellTextColour(row, col, wx.RED) - grid.MakeCellVisible(row, col) - else: - grid.SetCellTextColour(row, col, wx.BLACK) - grid.SetCellBackgroundColour(row, col, wx.WHITE) + highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] + grid.SetCellBackgroundColour(row, col, highlight_colours[0]) + grid.SetCellTextColour(row, col, highlight_colours[1]) if wx.Platform == '__WXMSW__': grid.SetRowMinimalHeight(row, 20) else: @@ -292,11 +291,23 @@ self.data = [] self.editors = [] - def AddError(self, infos): - self.Errors[infos[0]] = infos[1:] - - def ClearErrors(self): - self.Errors = {} + def AddHighlight(self, infos, highlight_type): + row_highlights = self.Highlights.setdefault(infos[0], {}) + col_highlights = row_highlights.setdefault(infos[1], []) + col_highlights.append(highlight_type) + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + for row, row_highlights in self.Highlights.iteritems(): + row_items = row_highlights.items() + for col, col_highlights in row_items: + if highlight_type in col_highlights: + col_highlights.remove(highlight_type) + if len(col_highlights) == 0: + row_highlights.pop(col) + class VariableDropTarget(wx.TextDropTarget): ''' @@ -524,6 +535,9 @@ self.Controler = controler self.ElementType = element_type + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) + self.Filter = "All" self.FilterChoices = [] self.FilterChoiceTransfer = GetFilterChoiceTransfer() @@ -597,6 +611,9 @@ self.VariablesGrid.SetColMinimalWidth(col, self.ColSizes[col]) self.VariablesGrid.AutoSizeColumn(col, False) + def __del__(self): + self.RefreshHighlightsTimer.Stop() + def SetTagName(self, tagname): self.TagName = tagname @@ -920,17 +937,27 @@ self.Controler.BufferProject() self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, INSTANCESTREE, LIBRARYTREE) - def AddVariableError(self, infos): +#------------------------------------------------------------------------------- +# Highlights showing functions +#------------------------------------------------------------------------------- + + def OnRefreshHighlightsTimer(self, event): + self.Table.ResetView(self.VariablesGrid) + event.Skip() + + def AddVariableHighlight(self, infos, highlight_type): if isinstance(infos[0], TupleType): for i in xrange(*infos[0]): - self.Table.AddError((i,) + infos[1:]) - else: - self.Table.AddError(infos) + self.Table.AddHighlight((i,) + infos[1:], highlight_type) + else: + self.Table.AddHighlight(infos, highlight_type) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ClearHighlights(self, highlight_type=None): + self.Table.ClearHighlights(highlight_type) self.Table.ResetView(self.VariablesGrid) - def ClearErrors(self): - self.Table.ClearErrors() - self.Table.ResetView(self.VariablesGrid) + class LocationCellControl(wx.PyControl): diff -r 94c11207aa6f -r 6014ef82a98a Viewer.py --- a/Viewer.py Fri Sep 30 17:16:02 2011 +0200 +++ b/Viewer.py Sun Oct 09 19:51:14 2011 +0200 @@ -77,7 +77,6 @@ ZOOM_FACTORS = [math.sqrt(2) ** x for x in xrange(-6, 7)] - def GetVariableCreationFunction(variable_type): def variableCreationFunction(viewer, id, specific_values): return FBD_Variable(viewer, variable_type, @@ -466,7 +465,7 @@ self.DrawingWire = False self.current_id = 0 self.TagName = tagname - self.Errors = [] + self.Highlights = [] self.InstancePath = instancepath self.StartMousePos = None self.StartScreenPos = None @@ -507,6 +506,9 @@ self.SetScale(len(ZOOM_FACTORS) / 2) + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) + self.ResetView() # Link Viewer event to corresponding methods @@ -526,6 +528,10 @@ self.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheelWindow) self.Bind(wx.EVT_SIZE, self.OnMoveWindow) + def __del__(self): + DebugViewer.__del__(self) + self.RefreshHighlightsTimer.Stop() + def SetCurrentCursor(self, cursor): global CURSORS if self.CurrentCursor != cursor: @@ -839,7 +845,7 @@ self.Inhibit(False) self.RefreshVisibleElements() - self.ShowErrors() + self.ShowHighlights() self.Refresh(False) def GetPreviousSteps(self, connectors): @@ -1848,6 +1854,7 @@ dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), "", wx.OK|wx.CANCEL|wx.TE_MULTILINE) else: dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), "", wx.OK|wx.CANCEL) + dialog.SetClientSize(wx.Size(400, 200)) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetValue() id = self.GetNewId() @@ -2349,6 +2356,7 @@ dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), comment.GetContent(), wx.OK|wx.CANCEL|wx.TE_MULTILINE) else: dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), comment.GetContent(), wx.OK|wx.CANCEL) + dialog.SetClientSize(wx.Size(400, 200)) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetValue() rect = comment.GetRedrawRect(1, 1) @@ -2747,22 +2755,30 @@ #------------------------------------------------------------------------------- -# Errors showing functions -#------------------------------------------------------------------------------- - - def ClearErrors(self): - self.Errors = [] +# Highlights showing functions +#------------------------------------------------------------------------------- + + def OnRefreshHighlightsTimer(self, event): self.RefreshView() - - def AddShownError(self, infos, start, end): - self.Errors.append((infos, start, end)) - - def ShowErrors(self): - for infos, start, end in self.Errors: - if infos[0] in ["io_variable", "block", "coil", "contact", "transition", "step", "action_block"]: + event.Skip() + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = [] + else: + self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type] + self.RefreshView() + + def AddHighlight(self, infos, start, end, highlight_type): + self.Highlights.append((infos, start, end, highlight_type)) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ShowHighlights(self): + for infos, start, end, highlight_type in self.Highlights: + if infos[0] in ["comment", "io_variable", "block", "connector", "coil", "contact", "step", "transition", "jump", "action_block"]: block = self.FindElementById(infos[1]) if block is not None: - block.AddError(infos[2:], start, end) + block.AddHighlight(infos[2:], start, end, highlight_type) #------------------------------------------------------------------------------- # Drawing functions diff -r 94c11207aa6f -r 6014ef82a98a dialogs/SearchInProjectDialog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dialogs/SearchInProjectDialog.py Sun Oct 09 19:51:14 2011 +0200 @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import re + +import wx + +RE_ESCAPED_CHARACTERS = ".*+()[]?:|{}^$<>=-," + +def EscapeText(text): + text = text.replace('\\', '\\\\') + for c in RE_ESCAPED_CHARACTERS: + text = text.replace(c, '\\' + c) + return text + +#------------------------------------------------------------------------------- +# Search In Project Dialog +#------------------------------------------------------------------------------- + +def GetElementsChoices(): + _ = lambda x: x + return [("datatype", _("Data Type")), + ("function", _("Function")), + ("functionBlock", _("Function Block")), + ("program", _("Program")), + ("configuration", _("Configuration"))] + +[ID_SEARCHINPROJECTDIALOG, ID_SEARCHINPROJECTDIALOGPATTERNLABEL, + ID_SEARCHINPROJECTDIALOGPATTERN, ID_SEARCHINPROJECTDIALOGCASESENSITIVE, + ID_SEARCHINPROJECTDIALOGREGULAREXPRESSION, ID_SEARCHINPROJECTDIALOGSCOPESTATICBOX, + ID_SEARCHINPROJECTDIALOGWHOLEPROJECT, ID_SEARCHINPROJECTDIALOGONLYELEMENTS, + ID_SEARCHINPROJECTDIALOGELEMENTSLIST, +] = [wx.NewId() for _init_ctrls in range(9)] + +class SearchInProjectDialog(wx.Dialog): + + 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_MainSizer_Items(self, parent): + parent.AddSizer(self.PatternSizer, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + parent.AddSizer(self.ScopeSizer, 0, border=20, flag=wx.GROW|wx.LEFT|wx.RIGHT) + parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) + + def _init_coll_MainSizer_Growables(self, parent): + parent.AddGrowableCol(0) + parent.AddGrowableRow(1) + + def _init_coll_PatternSizer_Items(self, parent): + parent.AddWindow(self.PatternLabel, 0, border=0, flag=wx.ALIGN_BOTTOM) + parent.AddWindow(self.CaseSensitive, 0, border=0, flag=wx.GROW) + parent.AddWindow(self.Pattern, 0, border=0, flag=wx.GROW) + parent.AddWindow(self.RegularExpression, 0, border=0, flag=wx.GROW) + + def _init_coll_PatternSizer_Growables(self, parent): + parent.AddGrowableCol(0) + + def _init_coll_ScopeSizer_Items(self, parent): + parent.AddSizer(self.ScopeSelectionSizer, 1, border=5, flag=wx.GROW|wx.TOP|wx.LEFT|wx.BOTTOM) + parent.AddWindow(self.ElementsList, 1, border=5, flag=wx.GROW|wx.TOP|wx.RIGHT|wx.BOTTOM) + + def _init_coll_ScopeSelectionSizer_Items(self, parent): + parent.AddWindow(self.WholeProject, 0, border=5, flag=wx.GROW|wx.BOTTOM) + parent.AddWindow(self.OnlyElements, 0, border=0, flag=wx.GROW) + + def _init_sizers(self): + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) + self.PatternSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) + self.ScopeSizer = wx.StaticBoxSizer(self.ScopeStaticBox, wx.HORIZONTAL) + self.ScopeSelectionSizer = wx.BoxSizer(wx.VERTICAL) + + self._init_coll_MainSizer_Items(self.MainSizer) + self._init_coll_MainSizer_Growables(self.MainSizer) + self._init_coll_PatternSizer_Items(self.PatternSizer) + self._init_coll_PatternSizer_Growables(self.PatternSizer) + self._init_coll_ScopeSizer_Items(self.ScopeSizer) + self._init_coll_ScopeSelectionSizer_Items(self.ScopeSelectionSizer) + + self.SetSizer(self.MainSizer) + + def _init_ctrls(self, prnt): + wx.Dialog.__init__(self, id=ID_SEARCHINPROJECTDIALOG, + name='SearchInProjectDialog', parent=prnt, + size=wx.Size(600, 300), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, + title=_('Search in Project')) + + self.PatternLabel = wx.StaticText(id=ID_SEARCHINPROJECTDIALOGPATTERNLABEL, + label=_('Pattern to search:'), name='PatternLabel', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + self.Pattern = wx.TextCtrl(id=ID_SEARCHINPROJECTDIALOGPATTERN, + name='Pattern', parent=self, pos=wx.Point(0, 0), + size=wx.Size(0, 24), style=0) + + self.CaseSensitive = wx.CheckBox(id=ID_SEARCHINPROJECTDIALOGCASESENSITIVE, + label=_('Case sensitive'), name='CaseSensitive', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + self.RegularExpression = wx.CheckBox(id=ID_SEARCHINPROJECTDIALOGREGULAREXPRESSION, + label=_('Regular expression'), name='RegularExpression', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + self.ScopeStaticBox = wx.StaticBox(id=ID_SEARCHINPROJECTDIALOGSCOPESTATICBOX, + label=_('Scope'), name='ScopeStaticBox', parent=self, + pos=wx.Point(0, 0), size=wx.Size(0, 0), style=0) + + self.WholeProject = wx.RadioButton(id=ID_SEARCHINPROJECTDIALOGWHOLEPROJECT, + label=_('Whole Project'), name='WholeProject', parent=self, + pos=wx.Point(0, 0), size=wx.Size(0, 24), style=wx.RB_GROUP) + self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, id=ID_SEARCHINPROJECTDIALOGWHOLEPROJECT) + self.WholeProject.SetValue(True) + + self.OnlyElements = wx.RadioButton(id=ID_SEARCHINPROJECTDIALOGONLYELEMENTS, + label=_('Only Elements'), name='OnlyElements', parent=self, + pos=wx.Point(0, 0), size=wx.Size(0, 24), style=0) + self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, id=ID_SEARCHINPROJECTDIALOGONLYELEMENTS) + self.OnlyElements.SetValue(False) + + self.ElementsList = wx.CheckListBox(id=ID_SEARCHINPROJECTDIALOGELEMENTSLIST, + name='ElementsList', parent=self, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=0) + self.ElementsList.Enable(False) + + self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + if wx.VERSION >= (2, 5, 0): + ok_button = self.ButtonSizer.GetAffirmativeButton() + else: + ok_button = self.ButtonSizer.GetChildren()[0].GetSizer().GetChildren()[0].GetWindow() + ok_button.SetLabel(_('Search')) + self.Bind(wx.EVT_BUTTON, self.OnOK, id=ok_button.GetId()) + + self._init_sizers() + + def __init__(self, parent): + self._init_ctrls(parent) + + for name, label in GetElementsChoices(): + self.ElementsList.Append(_(label)) + + def GetCriteria(self): + raw_pattern = self.Pattern.GetValue() + if not self.CaseSensitive.GetValue(): + pattern = raw_pattern.upper() + if not self.RegularExpression.GetValue(): + pattern = EscapeText(raw_pattern) + criteria = { + "raw_pattern": raw_pattern, + "pattern": re.compile(pattern), + "case_sensitive": self.CaseSensitive.GetValue(), + "regular_expression": self.RegularExpression.GetValue(), + } + if self.WholeProject.GetValue(): + criteria["filter"] = "all" + elif self.OnlyElements.GetValue(): + criteria["filter"] = [] + for index, (name, label) in enumerate(GetElementsChoices()): + if self.ElementsList.IsChecked(index): + criteria["filter"].append(name) + return criteria + + def OnScopeChanged(self, event): + self.ElementsList.Enable(self.OnlyElements.GetValue()) + event.Skip() + + def OnOK(self, event): + if self.Pattern.GetValue() == "": + message = wx.MessageDialog(self, _("Form isn't complete. Pattern to search must be filled!"), _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + else: + wrong_pattern = False + if self.RegularExpression.GetValue(): + try: + re.compile(self.Pattern.GetValue()) + except: + wrong_pattern = True + if wrong_pattern: + message = wx.MessageDialog(self, _("Syntax error in regular expression of pattern to search!"), _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + else: + self.EndModal(wx.ID_OK) diff -r 94c11207aa6f -r 6014ef82a98a dialogs/__init__.py --- a/dialogs/__init__.py Fri Sep 30 17:16:02 2011 +0200 +++ b/dialogs/__init__.py Sun Oct 09 19:51:14 2011 +0200 @@ -37,3 +37,4 @@ from ForceVariableDialog import ForceVariableDialog from ArrayTypeDialog import ArrayTypeDialog from DurationEditorDialog import DurationEditorDialog +from SearchInProjectDialog import SearchInProjectDialog diff -r 94c11207aa6f -r 6014ef82a98a graphics/FBD_Objects.py --- a/graphics/FBD_Objects.py Fri Sep 30 17:16:02 2011 +0200 +++ b/graphics/FBD_Objects.py Sun Oct 09 19:51:14 2011 +0200 @@ -51,7 +51,7 @@ self.Colour = wx.BLACK self.Pen = MiterPen(wx.BLACK) self.SetType(type, extension, inputs, connectors, executionControl) - self.Errors = {} + self.Highlights = {} # Make a clone of this FBD_Block def Clone(self, parent, id = None, name = "", pos = None): @@ -368,13 +368,41 @@ for output in self.Outputs: output.RefreshWires() - def AddError(self, infos, start, end): + # Adds an highlight to the block + def AddHighlight(self, infos, start, end ,highlight_type): if infos[0] in ["type", "name"] and start[0] == 0 and end[0] == 0: - self.Errors[infos[0]] = (start, end) + highlights = self.Highlights.setdefault(infos[0], []) + AddHighlight(highlights, (start, end, highlight_type)) elif infos[0] == "input" and infos[1] < len(self.Inputs): - self.Inputs[infos[1]].AddError(infos[2:], start, end) + self.Inputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type) elif infos[0] == "output" and infos[1] < len(self.Outputs): - self.Outputs[infos[1]].AddError(infos[2:], start, end) + self.Outputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type) + + # Removes an highlight from the block + def RemoveHighlight(self, infos, start, end, highlight_type): + if infos[0] in ["type", "name"]: + highlights = self.Highlights.get(infos[0], []) + if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: + self.Highlights.pop(infos[0]) + elif infos[0] == "input" and infos[1] < len(self.Inputs): + self.Inputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type) + elif infos[0] == "output" and infos[1] < len(self.Outputs): + self.Outputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type) + + # Removes all the highlights of one particular type from the block + def ClearHighlight(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + highlight_items = self.Highlights.items() + for name, highlights in highlight_items: + highlights = ClearHighlights(highlights, highlight_type) + if len(highlights) == 0: + self.Highlights.pop(name) + for input in self.Inputs: + input.ClearHighlights(highlight_type) + for output in self.Outputs: + output.ClearHighlights(highlight_type) # Draws block def Draw(self, dc): @@ -410,11 +438,10 @@ # Draw block execution order dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0], self.Pos.y + self.Size[1] + 2) - if self.Errors.has_key("name"): - HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1]) - if self.Errors.has_key("type"): - HighlightErrorZone(dc, type_pos[0], type_pos[1], type_size[0], type_size[1]) - dc.SetTextForeground(wx.BLACK) + + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, self.Name, self.Highlights.get("name", []), name_pos[0], name_pos[1]) + DrawHighlightedText(dc, self.Type, self.Highlights.get("type", []), type_pos[0], type_pos[1]) #------------------------------------------------------------------------------- @@ -438,7 +465,7 @@ self.Input = None self.Output = None self.SetType(type, value_type) - self.Errors = [] + self.Highlights = [] # Make a clone of this FBD_Variable def Clone(self, parent, id = None, pos = None): @@ -644,9 +671,19 @@ if self.Output: self.Output.RefreshWires() - def AddError(self, infos, start, end): + # Adds an highlight to the variable + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "expression" and start[0] == 0 and end[0] == 0: - self.Errors.append((start[1], end[1])) + AddHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes an highlight from the variable + def RemoveHighlight(self, infos, start, end, highlight_type): + if infos[0] == "expression": + RemoveHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes all the highlights of one particular type from the variable + def ClearHighlight(self, highlight_type=None): + ClearHighlights(self.Highlights, highlight_type) # Draws variable def Draw(self, dc): @@ -676,11 +713,9 @@ # Draw variable execution order dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0], self.Pos.y + self.Size[1] + 2) - for start, end in self.Errors: - offset = dc.GetTextExtent(self.Name[:start]) - size = dc.GetTextExtent(self.Name[start:end + 1]) - HighlightErrorZone(dc, text_pos[0] + offset[0], text_pos[1], size[0], size[1]) - + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1]) + #------------------------------------------------------------------------------- # Function Block Diagram Connector #------------------------------------------------------------------------------- @@ -699,6 +734,7 @@ self.SetName(name) self.Pos = wx.Point(0, 0) self.Size = wx.Size(0, 0) + self.Highlights = [] # Create an input or output connector according to connection type if self.Type == CONNECTOR: self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone = True) @@ -851,6 +887,20 @@ if self.Connector: self.Connector.RefreshWires() + # Adds an highlight to the connection + def AddHighlight(self, infos, start, end, highlight_type): + if infos[0] == "name" and start[0] == 0 and end[0] == 0: + AddHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes an highlight from the connection + def RemoveHighlight(self, infos, start, end, highlight_type): + if infos[0] == "name": + RemoveHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes all the highlights of one particular type from the connection + def ClearHighlight(self, highlight_type=None): + ClearHighlights(self.Highlights, highlight_type) + # Draws connection def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -874,9 +924,13 @@ dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2, self.Pos.x + self.Size[0] - arrowsize, self.Pos.y + self.Size[1]) # Draw connection name - dc.DrawText(self.Name, self.Pos.x + (self.Size[0] - name_size[0]) / 2, - self.Pos.y + (self.Size[1] - name_size[1]) / 2) + text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, + self.Pos.y + (self.Size[1] - name_size[1]) / 2) + dc.DrawText(self.Name, text_pos[0], text_pos[1]) # Draw connector if self.Connector: self.Connector.Draw(dc) + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1]) + diff -r 94c11207aa6f -r 6014ef82a98a graphics/GraphicCommons.py --- a/graphics/GraphicCommons.py Fri Sep 30 17:16:02 2011 +0200 +++ b/graphics/GraphicCommons.py Sun Oct 09 19:51:14 2011 +0200 @@ -93,7 +93,14 @@ # Color for Highlighting HIGHLIGHTCOLOR = wx.CYAN - + +# Define highlight types +ERROR_HIGHLIGHT = (wx.NamedColour("yellow"), wx.RED) +SEARCH_RESULT_HIGHLIGHT = (wx.NamedColour("orange"), wx.WHITE) + +# Define highlight refresh inhibition period in second +REFRESH_HIGHLIGHT_PERIOD = 0.1 + HANDLE_CURSORS = { (1, 1) : 2, (3, 3) : 2, @@ -531,19 +538,38 @@ event.Skip() #------------------------------------------------------------------------------- -# Helper for highlighting error in drawn text +# Helpers for highlighting text #------------------------------------------------------------------------------- -def HighlightErrorZone(dc, x, y, width, height): +def AddHighlight(highlights, infos): + RemoveHighlight(highlights, infos) + highlights.append(infos) + +def RemoveHighlight(highlights, infos): + if infos in highlights: + highlights.remove(infos) + return True + return False + +def ClearHighlight(highlights, highlight_type=None): + if highlight_type is not None: + return [highlight for highlight in highlights if highlight[2] != highlight_type] + return [] + +def DrawHighlightedText(dc, text, highlights, x, y): + current_pen = dc.GetPen() dc.SetPen(wx.TRANSPARENT_PEN) - dc.SetLogicalFunction(wx.AND) - dc.SetBrush(wx.Brush(wx.Colour(0,255,0))) - dc.DrawRectangle(x, y, width, height) - dc.SetLogicalFunction(wx.XOR) - dc.SetBrush(wx.Brush(wx.Colour(255,0,0))) - dc.DrawRectangle(x, y, width, height) - dc.SetLogicalFunction(wx.COPY) - + for start, end, highlight_type in highlights: + dc.SetBrush(wx.Brush(highlight_type[0])) + offset_width, offset_height = dc.GetTextExtent(text[:start[1]]) + part = text[start[1]:end[1] + 1] + part_width, part_height = dc.GetTextExtent(part) + dc.DrawRectangle(x + offset_width, y, part_width, part_height) + dc.SetTextForeground(highlight_type[1]) + dc.DrawText(part, x + offset_width, y) + dc.SetPen(current_pen) + dc.SetTextForeground(wx.BLACK) + #------------------------------------------------------------------------------- # Graphic element base class #------------------------------------------------------------------------------- @@ -934,7 +960,16 @@ return movex, movey return 0, 0 - def AddError(self, infos, start, end): + # Override this method for defining the method to call for adding an highlight to this element + def AddHighlight(self, infos, start, end, highlight_type): + pass + + # Override this method for defining the method to call for removing an highlight from this element + def RemoveHighlight(self, infos, start, end, highlight_type): + pass + + # Override this method for defining the method to call for removing all the highlights of one particular type from this element + def ClearHighlight(self, highlight_type=None): pass # Override this method for defining the method to call for refreshing the model of this element @@ -1300,7 +1335,7 @@ self.Value = None self.Forced = False self.Selected = False - self.Errors = {} + self.Highlights = [] self.RefreshNameSize() def Flush(self): @@ -1396,7 +1431,7 @@ self.Valid = True for wire, handle in self.Wires: self.Valid &= wire.GetValid() - + def ReceivingCurrent(self): current = False for wire, handle in self.Wires: @@ -1607,21 +1642,50 @@ dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - def AddError(self, infos, start, end): - if len(infos) == 0: + # Adds an highlight to the connector + def AddHighlight(self, infos, start, end, highlight_type): + if highlight_type == ERROR_HIGHLIGHT: for wire, handle in self.Wires: wire.SetValid(False) + AddHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes an highlight from the connector + def RemoveHighlight(self, infos, start, end, highlight_type): + error = False + highlights = [] + for highlight in self.Highlights: + if highlight != (start, end, highlight_type): + highlights.append(highlight) + error |= highlight == ERROR_HIGHLIGHT + self.Highlights = highlights + if not error: + for wire, handle in self.Wires: + wire.SetValid(wire.IsConnectedCompatible()) + + # Removes all the highlights of one particular type from the connector + def ClearHighlight(self, highlight_type=None): + error = False + if highlight_type is None: + self.Highlights = [] else: - self.Errors[infos[0]] = (start, end) + highlights = [] + for highlight in self.Highlights: + if highlight[2] != highlight_type: + highlights.append(highlight) + error |= highlight == ERROR_HIGHLIGHT + self.Highlights = highlights + if not error: + for wire, handle in self.Wires: + wire.SetValid(wire.IsConnectedCompatible()) # Draws the connector def Draw(self, dc): if self.Selected: dc.SetPen(MiterPen(wx.BLUE, 3)) dc.SetBrush(wx.WHITE_BRUSH) - elif len(self.Errors) > 0: - dc.SetPen(MiterPen(wx.RED)) - dc.SetBrush(wx.Brush(wx.Colour(255, 255, 0))) + #elif len(self.Highlights) > 0: + # dc.SetPen(MiterPen(self.Highlights[-1][1])) + # dc.SetBrush(wx.Brush(self.Highlights[-1][0])) else: if not self.Valid: dc.SetPen(MiterPen(wx.RED)) @@ -1672,9 +1736,6 @@ xend = xstart + CONNECTOR_SIZE * self.Direction[0] yend = ystart + CONNECTOR_SIZE * self.Direction[1] dc.DrawLine(xstart + self.Direction[0], ystart + self.Direction[1], xend, yend) - if len(self.Errors) > 0: - dc.SetPen(self.Pen) - dc.SetBrush(wx.WHITE_BRUSH) if self.Direction[0] != 0: ytext = parent_pos[1] + self.Pos.y - name_size[1] / 2 if self.Direction[0] < 0: @@ -1689,7 +1750,8 @@ ytext = parent_pos[1] + self.Pos.y - (name_size[1] + 5) # Draw the text dc.DrawText(self.Name, xtext, ytext) - + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, self.Name, self.Highlights, xtext, ytext) #------------------------------------------------------------------------------- # Common Wire Element @@ -2775,7 +2837,7 @@ if self.EndConnected is not None: self.EndConnected.DrawHighlightment(dc) self.EndConnected.Draw(dc) - + # Draws the wire lines and points def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -2848,6 +2910,26 @@ # Graphic comment element #------------------------------------------------------------------------------- +def FilterHighlightsByRow(highlights, row, length): + _highlights = [] + for start, end, highlight_type in highlights: + if start[0] <= row and end[0] >= row: + if start[0] < row: + start = (row, 0) + if end[0] > row: + end = (row, length) + _highlights.append((start, end, highlight_type)) + return _highlights + +def FilterHighlightsByColumn(highlights, start_col, end_col): + _highlights = [] + for start, end, highlight_type in highlights: + if end[1] > start_col and start[1] < end_col: + start = (start[0], max(start[1], start_col) - start_col) + end = (end[0], min(end[1], end_col) - start_col) + _highlights.append((start, end, highlight_type)) + return _highlights + """ Class that implements a comment """ @@ -2861,6 +2943,7 @@ self.Content = content self.Pos = wx.Point(0, 0) self.Size = wx.Size(0, 0) + self.Highlights = [] # Make a clone of this comment def Clone(self, parent, id = None, pos = None): @@ -2958,6 +3041,19 @@ # Edit the comment content self.Parent.EditCommentContent(self) + # Adds an highlight to the comment + def AddHighlight(self, infos, start, end, highlight_type): + if infos[0] == "content": + AddHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes an highlight from the comment + def RemoveHighlight(self, infos, start, end, highlight_type): + RemoveHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes all the highlights of one particular type from the comment + def ClearHighlight(self, highlight_type=None): + self.Highlights = ClearHighlights(self.Highlights, highlight_type) + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -2999,32 +3095,46 @@ dc.DrawLines(lines) # Draws the comment content y = self.Pos.y + 10 - for line in self.Content.splitlines(): + for idx, line in enumerate(self.Content.splitlines()): first = True linetext = "" words = line.split(" ") + if not getattr(dc, "printing", False): + highlights = FilterHighlightsByRow(self.Highlights, idx, len(line)) + highlights_offset = 0 for i, word in enumerate(words): if first: - test = word + text = word else: - test = linetext + " " + word - wordwidth, wordheight = dc.GetTextExtent(test) + text = linetext + " " + word + wordwidth, wordheight = dc.GetTextExtent(text) if y + wordheight > self.Pos.y + self.Size[1] - 10: break if wordwidth < self.Size[0] - 20: if i < len(words) - 1: - linetext = test + linetext = text first = False else: - dc.DrawText(test, self.Pos.x + 10, y) + dc.DrawText(text, self.Pos.x + 10, y) + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, text, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(text)), self.Pos.x + 10, y) + highlights_offset += len(text) + 1 y += wordheight + 5 else: - dc.DrawText(linetext, self.Pos.x + 10, y) - if i == len(words) - 1: - y += wordheight + 5 - if y + wordheight > self.Pos.y + self.Size[1] - 10: - break + if not first: + dc.DrawText(linetext, self.Pos.x + 10, y) + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, linetext, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(linetext)), self.Pos.x + 10, y) + highlights_offset += len(linetext) + 1 + if first or i == len(words) - 1: + if not first: + y += wordheight + 5 + if y + wordheight > self.Pos.y + self.Size[1] - 10: + break dc.DrawText(word, self.Pos.x + 10, y) + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, word, FilterHighlightsByColumn(highlights, highlights_offset, highlights_offset + len(word)), self.Pos.x + 10, y) + highlights_offset += len(word) + 1 else: linetext = word y += wordheight + 5 diff -r 94c11207aa6f -r 6014ef82a98a graphics/LD_Objects.py --- a/graphics/LD_Objects.py Fri Sep 30 17:16:02 2011 +0200 +++ b/graphics/LD_Objects.py Sun Oct 09 19:51:14 2011 +0200 @@ -354,7 +354,7 @@ self.Name = name self.Id = id self.Size = wx.Size(LD_ELEMENT_SIZE[0], LD_ELEMENT_SIZE[1]) - self.Errors = {} + self.Highlights = {} # Create an input and output connector self.Input = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2 + 1), WEST) self.Output = Connector(self, "", "BOOL", wx.Point(self.Size[0], self.Size[1] / 2 + 1), EAST) @@ -595,8 +595,31 @@ dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - def AddError(self, infos, start, end): - self.Errors[infos[0]] = (start[1], end[1]) + # Adds an highlight to the connection + def AddHighlight(self, infos, start, end, highlight_type): + highlights = self.Highlights.setdefault(infos[0], []) + if infos[0] == "reference": + if start[0] == 0 and end[0] == 0: + AddHighlight(highlights, (start, end, highlight_type)) + else: + AddHighlight(highlights, ((0, 0), (0, 1), highlight_type)) + + # Removes an highlight from the connection + def RemoveHighlight(self, infos, start, end, highlight_type): + highlights = self.Highlights.get(infos[0], []) + if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: + self.Highlights.pop(infos[0]) + + # Removes all the highlights of one particular type from the connection + def ClearHighlight(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + highlight_items = self.Highlights.items() + for name, highlights in highlight_items: + highlights = ClearHighlights(highlight, highlight_type) + if len(highlights) == 0: + self.Highlights.pop(name) # Draws contact def Draw(self, dc): @@ -651,11 +674,13 @@ # Draw input and output connectors self.Input.Draw(dc) self.Output.Draw(dc) - if self.Errors.has_key("reference"): - HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1]) - if typetext != "" and (self.Errors.has_key("negated") or self.Errors.has_key("rising") or self.Errors.has_key("falling")): - HighlightErrorZone(dc, type_pos[0], type_pos[1], type_size[0], type_size[1]) - + + if not getattr(dc, "printing", False): + for name, highlights in self.Highlights.iteritems(): + if name == "reference": + DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1]) + elif typetext != "": + DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1]) #------------------------------------------------------------------------------- # Ladder Diagram Coil @@ -674,7 +699,7 @@ self.Name = name self.Id = id self.Size = wx.Size(LD_ELEMENT_SIZE[0], LD_ELEMENT_SIZE[1]) - self.Errors = {} + self.Highlights = {} # Create an input and output connector self.Input = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2 + 1), WEST) self.Output = Connector(self, "", "BOOL", wx.Point(self.Size[0], self.Size[1] / 2 + 1), EAST) @@ -891,8 +916,31 @@ dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - def AddError(self, infos, start, end): - self.Errors[infos[0]] = (start[1], end[1]) + # Adds an highlight to the connection + def AddHighlight(self, infos, start, end, highlight_type): + highlights = self.Highlights.setdefault(infos[0], []) + if infos[0] == "reference": + if start[0] == 0 and end[0] == 0: + AddHighlight(highlights, (start, end, highlight_type)) + else: + AddHighlight(highlights, ((0, 0), (0, 1), highlight_type)) + + # Removes an highlight from the connection + def RemoveHighlight(self, infos, start, end, highlight_type): + highlights = self.Highlights.get(infos[0], []) + if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: + self.Highlights.pop(infos[0]) + + # Removes all the highlights of one particular type from the connection + def ClearHighlight(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + highlight_items = self.Highlights.items() + for name, highlights in highlight_items: + highlights = ClearHighlights(highlight, highlight_type) + if len(highlights) == 0: + self.Highlights.pop(name) # Draws coil def Draw(self, dc): @@ -954,9 +1002,12 @@ # Draw input and output connectors self.Input.Draw(dc) self.Output.Draw(dc) - if self.Errors.has_key("reference"): - HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1]) - if typetext != "" and (self.Errors.has_key("negated") or self.Errors.has_key("rising") or self.Errors.has_key("falling")): - HighlightErrorZone(dc, type_pos[0], type_pos[1], type_size[0], type_size[1]) + + if not getattr(dc, "printing", False): + for name, highlights in self.Highlights.iteritems(): + if name == "reference": + DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1]) + elif typetext != "": + DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1]) diff -r 94c11207aa6f -r 6014ef82a98a graphics/SFC_Objects.py --- a/graphics/SFC_Objects.py Fri Sep 30 17:16:02 2011 +0200 +++ b/graphics/SFC_Objects.py Sun Oct 09 19:51:14 2011 +0200 @@ -50,7 +50,7 @@ self.SetName(name) self.Initial = initial self.Id = id - self.Error = None + self.Highlights = [] self.Size = wx.Size(SFC_STEP_DEFAULT_SIZE[0], SFC_STEP_DEFAULT_SIZE[1]) # Create an input and output connector if not self.Initial: @@ -513,9 +513,19 @@ elif self.Output: self.Output.RefreshWires() - def AddError(self, infos, start, end): + # Adds an highlight to the connection + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "name" and start[0] == 0 and end[0] == 0: - self.Error = (start[1], end[1]) + AddHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes an highlight from the connection + def RemoveHighlight(self, infos, start, end, highlight_type): + if infos[0] == "name": + RemoveHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes all the highlights of one particular type from the connection + def ClearHighlight(self, highlight_type=None): + ClearHighlights(self.Highlights, highlight_type) # Draws step def Draw(self, dc): @@ -551,8 +561,9 @@ self.Output.Draw(dc) if self.Action: self.Action.Draw(dc) - if self.Error is not None: - HighlightErrorZone(dc, name_pos[0], name_pos[1], name_size[0], name_size[1]) + + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1]) #------------------------------------------------------------------------------- @@ -578,7 +589,7 @@ self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True) self.SetType(type, condition) self.SetPriority(priority) - self.Errors = {} + self.Highlights = {} self.PreviousValue = None self.PreviousSpreading = False @@ -923,15 +934,29 @@ else: self.Output.RefreshWires() - def AddError(self, infos, start, end): - if infos[0] == "priority" and start[0] == 0 and start[1] == 0: - self.Errors[infos[0]] = (start[1], end[1]) - elif infos[0] == "inline": - if not self.Errors.has_key(infos[0]): - self.Errors[infos[0]] = [] - self.Errors[infos[0]].append((start[1], end[1])) - else: - pass + # Adds an highlight to the block + def AddHighlight(self, infos, start, end ,highlight_type): + if infos[0] in ["reference", "inline", "priority"] and start[0] == 0 and end[0] == 0: + highlights = self.Highlights.setdefault(infos[0], []) + AddHighlight(highlights, (start, end, highlight_type)) + + # Removes an highlight from the block + def RemoveHighlight(self, infos, start, end, highlight_type): + if infos[0] in ["reference", "inline", "priority"]: + highlights = self.Highlights.get(infos[0], []) + if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: + self.Highlights.pop(infos[0]) + + # Removes all the highlights of one particular type from the block + def ClearHighlight(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + highlight_items = self.Highlights.items() + for name, highlights in highlight_items: + highlights = ClearHighlights(highlight, highlight_type) + if len(highlights) == 0: + self.Highlights.pop(name) # Draws transition def Draw(self, dc): @@ -986,14 +1011,13 @@ self.Output.Draw(dc) if self.Type == "connection": self.Condition.Draw(dc) - if self.Errors.has_key("priority"): - HighlightErrorZone(dc, priority_pos[0], priority_pos[1], priority_size[0], priority_size[1]) - if self.Errors.has_key("inline"): - for start, end in self.Errors["inline"]: - offset = dc.GetTextExtent(self.Condition[:start]) - size = dc.GetTextExtent(self.Condition[start:end + 1]) - HighlightErrorZone(dc, condition_pos[0] + offset[0], condition_pos[1], size[0], size[1]) - + + if not getattr(dc, "printing", False): + for name, highlights in self.Highlights.iteritems(): + if name == "priority": + DrawHighlightedText(dc, str(self.Priority), highlights, priority_pos[0], priority_pos[1]) + else: + DrawHighlightedText(dc, condition, highlights, condition_pos[0], condition_pos[1]) #------------------------------------------------------------------------------- # Sequencial Function Chart Divergence and Convergence @@ -1474,7 +1498,7 @@ self.SetTarget(target) self.Id = id self.Size = wx.Size(SFC_JUMP_SIZE[0], SFC_JUMP_SIZE[1]) - self.Errors = {} + self.Highlights = [] # Create an input and output connector self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True) self.Value = None @@ -1659,9 +1683,19 @@ if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: self.RefreshInputModel() - def AddError(self, infos, start, end): + # Adds an highlight to the variable + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "target" and start[0] == 0 and end[0] == 0: - self.Errors[infos[0]] = (start[1], end[1]) + AddHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes an highlight from the variable + def RemoveHighlight(self, infos, start, end, highlight_type): + if infos[0] == "target": + RemoveHighlight(self.Highlights, (start, end, highlight_type)) + + # Removes all the highlights of one particular type from the variable + def ClearHighlight(self, highlight_type=None): + ClearHighlights(self.Highlights, highlight_type) # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): @@ -1708,8 +1742,9 @@ # Draw input connector if self.Input: self.Input.Draw(dc) - if self.Errors.has_key("target"): - HighlightErrorZone(dc, target_pos[0], target_pos[1], target_size[0], target_size[1]) + + if not getattr(dc, "printing", False): + DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1]) #------------------------------------------------------------------------------- @@ -1728,7 +1763,7 @@ self.Id = id self.Size = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) self.MinSize = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) - self.Errors = {} + self.Highlights = {} # Create an input and output connector self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone = True) self.SetActions(actions) @@ -1907,20 +1942,41 @@ return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - # Refreshes the action block model + # Refreshes the action block model def RefreshModel(self, move=True): self.Parent.RefreshActionBlockModel(self) - def AddError(self, infos, start, end): + # Adds an highlight to the variable + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "action" and infos[1] < len(self.Actions): - if not self.Errors.has_key(infos[1]): - self.Errors[infos[1]] = {} - if infos[2] == "inline": - if not self.Errors[infos[1]].has_key(infos[2]): - self.Errors[infos[1]][infos[2]] = [] - self.Errors[infos[1]][infos[2]].append((start[1], end[1])) - else: - self.Errors[infos[1]][infos[2]] = (start[1], end[1]) + action_highlights = self.Highlights.setdefault(infos[1], {}) + attribute_highlights = action_highlights.setdefault(infos[2], []) + AddHighlight(attribute_highlights, (start, end, highlight_type)) + + # Removes an highlight from the block + def RemoveHighlight(self, infos, start, end, highlight_type): + if infos[0] == "action" and infos[1] < len(self.Actions): + action_highlights = self.Highlights.get(infos[1], {}) + attribute_highlights = action_highlights.setdefault(infos[2], []) + if RemoveHighlight(attribute_highlights, (start, end, highlight_type)) and len(attribute_highlights) == 0: + action_highlights.pop(infos[2]) + if len(action_highlights) == 0: + self.Highlights.pop(infos[1]) + + # Removes all the highlights of one particular type from the block + def ClearHighlight(self, highlight_type=None): + if highlight_type is None: + self.Highlights = {} + else: + highlight_items = self.Highlights.items() + for number, action_highlights in highlight_items: + action_highlight_items = action_highlights.items() + for name, attribute_highlights in action_highlights: + attribute_highlights = ClearHighlights(attribute_highlights, highlight_type) + if len(attribute_highlights) == 0: + action_highlights.pop(name) + if len(action_highlights) == 0: + self.Highlights.pop(number) # Draws divergence def Draw(self, dc): @@ -1963,20 +2019,19 @@ indicator_pos = (self.Pos.x + colsize[0] + colsize[1] + (colsize[2] - indicator_size[0]) / 2, self.Pos.y + i * line_size + (line_size - indicator_size[1]) / 2) dc.DrawText(action["indicator"], indicator_pos[0], indicator_pos[1]) - if i in self.Errors: - if self.Errors[i].has_key("duration") and action.has_key("duration"): - HighlightErrorZone(dc, duration_pos[0], duration_pos[1], duration_size[0], duration_size[1]) - if self.Errors[i].has_key("qualifier"): - HighlightErrorZone(dc, qualifier_pos[0], qualifier_pos[1], qualifier_size[0], qualifier_size[1]) - if self.Errors[i].has_key("reference"): - HighlightErrorZone(dc, content_pos[0], content_pos[1], content_size[0], content_size[1]) - elif self.Errors[i].has_key("inline"): - for start, end in self.Errors[i]["inline"]: - offset = dc.GetTextExtent(action["value"][:start]) - size = dc.GetTextExtent(action["value"][start:end + 1]) - HighlightErrorZone(dc, content_pos[0] + offset[0], content_pos[1], size[0], size[1]) - if self.Errors[i].has_key("indicator"): - HighlightErrorZone(dc, indicator_pos[0], indicator_pos[1], indicator_size[0], indicator_size[1]) + + if not getattr(dc, "printing", False): + action_highlights = self.Highlights.get(i, {}) + for name, attribute_highlights in action_highlights.iteritems(): + if name == "qualifier": + DrawHighlightedText(dc, action["qualifier"], attribute_highlights, qualifier_pos[0], qualifier_pos[1]) + elif name == "duration": + DrawHighlightedText(dc, action["duration"], attribute_highlights, duration_pos[0], duration_pos[1]) + elif name in ["reference", "inline"]: + DrawHighlightedText(dc, action["value"], attribute_highlights, content_pos[0], content_pos[1]) + elif name == "indicator": + DrawHighlightedText(dc, action["indicator"], attribute_highlights, indicator_pos[0], indicator_pos[1]) + # Draw input connector self.Input.Draw(dc) diff -r 94c11207aa6f -r 6014ef82a98a plcopen/plcopen.py --- a/plcopen/plcopen.py Fri Sep 30 17:16:02 2011 +0200 +++ b/plcopen/plcopen.py Sun Oct 09 19:51:14 2011 +0200 @@ -34,6 +34,12 @@ "Output" : "outputVars", "InOut" : "inOutVars", "External" : "externalVars", "Global" : "globalVars", "Access" : "accessVars"} +searchResultVarTypes = { + "inputVars": "var_input", + "outputVars": "var_output", + "inOutVars": "var_inout" +} + """ Define in which order var types must be displayed """ @@ -96,7 +102,25 @@ if self.y_min is not None and self.y_max is not None: height = self.y_max - self.y_min return self.x_min, self.y_min, width, height - + +def TextLenInRowColumn(text): + if text == "": + return (0, 0) + lines = text.split("\n") + return len(lines) - 1, len(lines[-1]) + +def TestTextElement(text, criteria): + lines = text.splitlines() + if not criteria["case_sensitive"]: + text = text.upper() + test_result = [] + result = criteria["pattern"].search(text) + while result is not None: + start = TextLenInRowColumn(text[:result.start()]) + end = TextLenInRowColumn(text[:result.end() - 1]) + test_result.append((start, end, "\n".join(lines[start[0]:end[0] + 1]))) + result = criteria["pattern"].search(text, result.end()) + return test_result PLCOpenClasses = GenerateClassesFromXSD(os.path.join(os.path.split(__file__)[0], "tc6_xml_v201.xsd")) @@ -127,6 +151,10 @@ result = address_model.search(self.text, startpos) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos): + return [(tuple(parent_infos),) + result for result in TestTextElement(self.gettext(), criteria)] + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("project", None) if cls: cls.singleLineAttributes = False @@ -677,6 +705,13 @@ return True setattr(cls, "IsLocatableType", IsLocatableType) + def Search(self, criteria, parent_infos=[]): + result = self.types.Search(criteria, parent_infos) + for configuration in self.instances.configurations.getconfiguration(): + result.extend(configuration.Search(criteria, parent_infos)) + return result + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("project_fileHeader", None) if cls: cls.singleLineAttributes = False @@ -746,61 +781,99 @@ return 0, 0 setattr(cls, "getscaling", getscaling) +def _Search(attributes, criteria, parent_infos): + search_result = [] + for attr, value in attributes: + if value is not None: + search_result.extend([(tuple(parent_infos + [attr]),) + result for result in TestTextElement(value, criteria)]) + return search_result + +def _updateConfigurationResourceElementName(self, old_name, new_name): + for varlist in self.getglobalVars(): + for var in varlist.getvariable(): + var_address = var.getaddress() + if var_address is not None: + if var_address == old_name: + var.setaddress(new_name) + if var.getname() == old_name: + var.setname(new_name) + +def _updateConfigurationResourceElementAddress(self, address_model, new_leading): + for varlist in self.getglobalVars(): + for var in varlist.getvariable(): + var_address = var.getaddress() + if var_address is not None: + var.setaddress(update_address(var_address, address_model, new_leading)) + +def _removeConfigurationResourceVariableByAddress(self, address): + for varlist in self.getglobalVars(): + variables = varlist.getvariable() + for i in xrange(len(variables)-1, -1, -1): + if variables[i].getaddress() == address: + variables.pop(i) + +def _removeConfigurationResourceVariableByFilter(self, address_model): + for varlist in self.getglobalVars(): + variables = varlist.getvariable() + for i in xrange(len(variables)-1, -1, -1): + var_address = variables[i].getaddress() + if var_address is not None: + result = address_model.match(var_address) + if result is not None: + variables.pop(i) + +def _SearchInConfigurationResource(self, criteria, parent_infos=[]): + search_result = [] + for result in TestTextElement(self.getname(), criteria): + search_result.append((tuple(parent_infos + ["name"]),) + result) + var_number = 0 + for varlist in self.getglobalVars(): + variable_type = searchResultVarTypes.get("globalVars", "var_local") + variables = varlist.getvariable() + for modifier, has_modifier in [("constant", varlist.getconstant()), + ("retain", varlist.getretain()), + ("non_retain", varlist.getnonretain())]: + if has_modifier: + for result in TestTextElement(modifier, criteria): + search_result.append((tuple(parent_infos + [variable_type, (var_number, var_number + len(variables)), modifier]),) + result) + break + for variable in variables: + search_result.extend(variable.Search(criteria, parent_infos + [variable_type, var_number])) + var_number += 1 + return search_result + cls = PLCOpenClasses.get("configurations_configuration", None) if cls: def updateElementName(self, old_name, new_name): - for varlist in self.getglobalVars(): - for var in varlist.getvariable(): - var_address = var.getaddress() - if var_address is not None: - if var_address == old_name: - var.setaddress(new_name) - if var.getname() == old_name: - var.setname(new_name) + _updateConfigurationResourceElementName(self, old_name, new_name) for resource in self.getresource(): resource.updateElementName(old_name, new_name) setattr(cls, "updateElementName", updateElementName) def updateElementAddress(self, address_model, new_leading): - for varlist in self.getglobalVars(): - for var in varlist.getvariable(): - var_address = var.getaddress() - if var_address is not None: - var.setaddress(update_address(var_address, address_model, new_leading)) + _updateConfigurationResourceElementAddress(self, address_model, new_leading) for resource in self.getresource(): resource.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) - def removeVariableByAddress(self, address): - for varlist in self.getglobalVars(): - variables = varlist.getvariable() - for i in xrange(len(variables)-1, -1, -1): - if variables[i].getaddress() == address: - variables.pop(i) - setattr(cls, "removeVariableByAddress", removeVariableByAddress) - - def removeVariableByFilter(self, address_model): - for varlist in self.getglobalVars(): - variables = varlist.getvariable() - for i in xrange(len(variables)-1, -1, -1): - var_address = variables[i].getaddress() - if var_address is not None: - result = address_model.match(var_address) - if result is not None: - variables.pop(i) - setattr(cls, "removeVariableByFilter", removeVariableByFilter) - + setattr(cls, "removeVariableByAddress", _removeConfigurationResourceVariableByAddress) + setattr(cls, "removeVariableByFilter", _removeConfigurationResourceVariableByFilter) + + def Search(self, criteria, parent_infos=[]): + search_result = [] + parent_infos = parent_infos + ["C::%s" % self.getname()] + filter = criteria["filter"] + if filter == "all" or "configuration" in filter: + search_result = _SearchInConfigurationResource(self, criteria, parent_infos) + for resource in self.getresource(): + search_result.extend(resource.Search(criteria, parent_infos)) + return search_result + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("configuration_resource", None) if cls: def updateElementName(self, old_name, new_name): - for varlist in self.getglobalVars(): - for var in varlist.getvariable(): - var_address = var.getaddress() - if var_address is not None: - if var_address == old_name: - var.setaddress(new_name) - if var.getname() == old_name: - var.setname(new_name) + _updateConfigurationResourceElementName(self, old_name, new_name) for instance in self.getpouInstance(): instance.updateElementName(old_name, new_name) for task in self.gettask(): @@ -808,33 +881,35 @@ setattr(cls, "updateElementName", updateElementName) def updateElementAddress(self, address_model, new_leading): - for varlist in self.getglobalVars(): - for var in varlist.getvariable(): - var_address = var.getaddress() - if var_address is not None: - var.setaddress(update_address(var_address, address_model, new_leading)) + _updateConfigurationResourceElementAddress(self, address_model, new_leading) for task in self.gettask(): task.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) - def removeVariableByAddress(self, address): - for varlist in self.getglobalVars(): - variables = varlist.getvariable() - for i in xrange(len(variables)-1, -1, -1): - if variables[i].getaddress() == address: - variables.pop(i) - setattr(cls, "removeVariableByAddress", removeVariableByAddress) - - def removeVariableByFilter(self, address_model): - for varlist in self.getglobalVars(): - variables = varlist.getvariable() - for i in xrange(len(variables)-1, -1, -1): - var_address = variables[i].getaddress() - if var_address is not None: - result = address_model.match(var_address) - if result is not None: - variables.pop(i) - setattr(cls, "removeVariableByFilter", removeVariableByFilter) + setattr(cls, "removeVariableByAddress", _removeConfigurationResourceVariableByAddress) + setattr(cls, "removeVariableByFilter", _removeConfigurationResourceVariableByFilter) + + def Search(self, criteria, parent_infos=[]): + parent_infos = parent_infos[:-1] + ["R::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] + search_result = _SearchInConfigurationResource(self, criteria, parent_infos) + task_number = 0 + instance_number = 0 + for task in self.gettask(): + results = TestTextElement(task.getname(), criteria) + for result in results: + search_result.append((tuple(parent_infos + ["task", task_number, "name"]),) + result) + search_result.extend(task.Search(criteria, parent_infos + ["task", task_number])) + task_number += 1 + for instance in task.getpouInstance(): + search_result.extend(task.Search(criteria, parent_infos + ["instance", instance_number])) + for result in results: + search_result.append((tuple(parent_infos + ["instance", instance_number, "task"]),) + result) + instance_number += 1 + for instance in self.getpouInstance(): + search_result.extend(instance.Search(criteria, parent_infos + ["instance", instance_number])) + instance_number += 1 + return search_result + setattr(cls, "Search", Search) cls = PLCOpenClasses.get("resource_task", None) if cls: @@ -878,6 +953,13 @@ self.interval = update_address(self.interval, address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos=[]): + return _Search([("single", self.getsingle()), + ("interval", self.getinterval()), + ("priority", str(self.getpriority()))], + criteria, parent_infos) + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("pouInstance", None) if cls: def compatibility(self, tree): @@ -890,6 +972,53 @@ self.typeName = new_name setattr(cls, "updateElementName", updateElementName) + def Search(self, criteria, parent_infos=[]): + return _Search([("name", self.getname()), + ("type", self.gettypeName())], + criteria, parent_infos) + setattr(cls, "Search", Search) + +cls = PLCOpenClasses.get("varListPlain_variable", None) +if cls: + def gettypeAsText(self): + vartype_content = self.gettype().getcontent() + # Variable type is a user data type + if vartype_content["name"] == "derived": + return vartype_content["value"].getname() + # Variable type is a string type + elif vartype_content["name"] in ["string", "wstring"]: + return vartype_content["name"].upper() + # Variable type is an array + elif vartype_content["name"] == "array": + base_type = vartype_content["value"].baseType.getcontent() + # Array derived directly from a user defined type + if base_type["name"] == "derived": + basetype_name = base_type["value"].getname() + # Array derived directly from a string type + elif base_type["name"] in ["string", "wstring"]: + basetype_name = base_type["name"].upper() + # Array derived directly from an elementary type + else: + basetype_name = base_type["name"] + return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content["value"].getdimension())), basetype_name) + # Variable type is an elementary type + return vartype_content["name"] + setattr(cls, "gettypeAsText", gettypeAsText) + + def Search(self, criteria, parent_infos=[]): + search_result = _Search([("name", self.getname()), + ("type", self.gettypeAsText()), + ("address", self.getaddress())], + criteria, parent_infos) + initial = self.getinitialValue() + if initial is not None: + search_result.extend(_Search([("initial", initial.getvalue())], criteria, parent_infos)) + doc = self.getdocumentation() + if doc is not None: + search_result.extend(doc.Search(criterias, parent_infos + ["documentation"])) + return search_result + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("project_types", None) if cls: def getdataTypeElements(self): @@ -965,6 +1094,17 @@ raise ValueError, _("\"%s\" POU doesn't exist !!!")%name setattr(cls, "removepouElement", removepouElement) + def Search(self, criteria, parent_infos=[]): + search_result = [] + filter = criteria["filter"] + #if filter == "all" or "datatype" in filter: + # for datatype in self.dataTypes.getdataType(): + # search_result.extend(datatype.Search(criteria, parent_infos)) + for pou in self.pous.getpou(): + search_result.extend(pou.Search(criteria, parent_infos)) + return search_result + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("pous_pou", None) if cls: @@ -1287,6 +1427,37 @@ if result is not None: variables.pop(i) setattr(cls, "removeVariableByFilter", removeVariableByFilter) + + def Search(self, criteria, parent_infos=[]): + search_result = [] + filter = criteria["filter"] + if filter == "all" or self.getpouType() in filter: + parent_infos = parent_infos + ["P::%s" % self.getname()] + for result in TestTextElement(self.getname(), criteria): + search_result.append((tuple(parent_infos + ["name"]),) + result) + if self.interface is not None: + var_number = 0 + for content in self.interface.getcontent(): + variable_type = searchResultVarTypes.get(content["value"], "var_local") + variables = content["value"].getvariable() + for modifier, has_modifier in [("constant", content["value"].getconstant()), + ("retain", content["value"].getretain()), + ("non_retain", content["value"].getnonretain())]: + if has_modifier: + for result in TestTextElement(modifier, criteria): + search_result.append((tuple(parent_infos + [variable_type, (var_number, var_number + len(variables)), modifier]),) + result) + break + for variable in variables: + search_result.extend(variable.Search(criteria, parent_infos + [variable_type, var_number])) + var_number += 1 + if len(self.body) > 0: + search_result.extend(self.body[0].Search(criteria, parent_infos)) + for action in self.getactionList(): + search_result.extend(action.Search(criteria, parent_infos)) + for transition in self.gettransitionList(): + search_result.extend(transition.Search(criteria, parent_infos)) + return search_result + setattr(cls, "Search", Search) def setbodyType(self, type): if type == "IL": @@ -1370,6 +1541,15 @@ return False setattr(cls, "hasblock", hasblock) + def Search(self, criteria, parent_infos): + search_result = [] + parent_infos = parent_infos[:-1] + ["T::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] + for result in TestTextElement(self.getname(), criteria): + search_result.append((tuple(parent_infos + ["name"]),) + result) + search_result.extend(self.body.Search(criteria, parent_infos)) + return search_result + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("actions_action", None) if cls: setattr(cls, "setbodyType", setbodyType) @@ -1402,6 +1582,15 @@ return False setattr(cls, "hasblock", hasblock) + def Search(self, criteria, parent_infos): + search_result = [] + parent_infos = parent_infos[:-1] + ["A::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] + for result in TestTextElement(self.getname(), criteria): + search_result.append((tuple(parent_infos + ["name"]),) + result) + search_result.extend(self.body.Search(criteria, parent_infos)) + return search_result + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("body", None) if cls: cls.currentExecutionOrderId = 0 @@ -1569,6 +1758,16 @@ element["value"].updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos=[]): + if self.content["name"] in ["IL", "ST"]: + search_result = self.content["value"].Search(criteria, parent_infos + ["body", 0]) + else: + search_result = [] + for element in self.content["value"].getcontent(): + search_result.extend(element["value"].Search(criteria, parent_infos)) + return search_result + setattr(cls, "Search", Search) + def getx(self): return self.position.getx() @@ -1681,6 +1880,9 @@ def _updateElementAddress(self, address_model, new_leading): pass +def _SearchInElement(self, criteria, parent_infos=[]): + return [] + _connectionsFunctions = { "bbox": {"none": _getBoundingBox, "single": _getBoundingBoxSingle, @@ -1693,7 +1895,7 @@ "multiple": _filterConnectionsMultiple}, "update": {"none": lambda self, translation: {}, "single": _updateConnectionsIdSingle, - "multiple": _updateConnectionsIdMultiple} + "multiple": _updateConnectionsIdMultiple}, } def _initElementClass(name, classname, connectionPointInType="none"): @@ -1710,6 +1912,7 @@ setattr(cls, "translate", _connectionsFunctions["translate"][connectionPointInType]) setattr(cls, "filterConnections", _connectionsFunctions["filter"][connectionPointInType]) setattr(cls, "updateConnectionsId", _connectionsFunctions["update"][connectionPointInType]) + setattr(cls, "Search", _SearchInElement) return cls def _getexecutionOrder(instance, specific_values): @@ -1867,6 +2070,10 @@ self.content.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos=[]): + return self.content.Search(criteria, parent_infos + ["comment", self.getlocalId(), "content"]) + setattr(cls, "Search", Search) + cls = _initElementClass("block", "fbdObjects_block") if cls: def getBoundingBox(self): @@ -1912,6 +2119,20 @@ _translateConnections(input.connectionPointIn, dx, dy) setattr(cls, "translate", translate) + def Search(self, criteria, parent_infos=[]): + parent_infos = parent_infos + ["block", self.getlocalId()] + search_result = _Search([("name", self.getinstanceName()), + ("type", self.gettypeName())], + criteria, parent_infos) + for i, variable in enumerate(self.inputVariables.getvariable()): + for result in TestTextElement(variable.getformalParameter(), criteria): + search_result.append((tuple(parent_infos + ["input", i]),) + result) + for i, variable in enumerate(self.outputVariables.getvariable()): + for result in TestTextElement(variable.getformalParameter(), criteria): + search_result.append((tuple(parent_infos + ["output", i]),) + result) + return search_result + setattr(cls, "Search", Search) + cls = _initElementClass("leftPowerRail", "ldObjects_leftPowerRail") if cls: setattr(cls, "getinfos", _getpowerrailinfosFunction("leftPowerRail")) @@ -1933,6 +2154,10 @@ self.variable = update_address(self.variable, address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos=[]): + return _Search([("reference", self.getvariable())], criteria, parent_infos + ["contact", self.getlocalId()]) + setattr(cls, "Search", Search) + cls = _initElementClass("coil", "ldObjects_coil", "single") if cls: setattr(cls, "getinfos", _getldelementinfosFunction("coil")) @@ -1946,6 +2171,10 @@ self.variable = update_address(self.variable, address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos=[]): + return _Search([("reference", self.getvariable())], criteria, parent_infos + ["coil", self.getlocalId()]) + setattr(cls, "Search", Search) + cls = _initElementClass("step", "sfcObjects_step", "single") if cls: def getinfos(self): @@ -1963,6 +2192,10 @@ return infos setattr(cls, "getinfos", getinfos) + def Search(self, criteria, parent_infos=[]): + return _Search([("name", self.getname())], criteria, parent_infos + ["step", self.getlocalId()]) + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("transition_condition", None) if cls: def compatibility(self, tree): @@ -2058,6 +2291,17 @@ return content["value"].getconnections() setattr(cls, "getconnections", getconnections) + def Search(self, criteria, parent_infos=[]): + parent_infos = parent_infos + ["transition", self.getlocalId()] + search_result = [] + content = self.condition.getcontent() + if content["name"] == "reference": + search_result.extend(_Search([("reference", content["value"].getname())], criteria, parent_infos)) + elif content["name"] == "inline": + search_result.extend(content["value"].Search(criteria, parent_infos + ["inline"])) + return search_result + setattr(cls, "Search", Search) + cls = _initElementClass("selectionDivergence", "sfcObjects_selectionDivergence", "single") if cls: setattr(cls, "getinfos", _getdivergenceinfosFunction(True, False)) @@ -2084,6 +2328,10 @@ return infos setattr(cls, "getinfos", getinfos) + def Search(self, criteria, parent_infos): + return _Search([("target", self.gettargetName())], criteria, parent_infos + ["jump", self.getlocalId()]) + setattr(cls, "Search", Search) + cls = PLCOpenClasses.get("actionBlock_action", None) if cls: def compatibility(self, tree): @@ -2134,6 +2382,18 @@ self.inline.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos=[]): + qualifier = self.getqualifier() + if qualifier is None: + qualifier = "N" + return _Search([("inline", self.getinlineContent()), + ("reference", self.getreferenceName()), + ("qualifier", qualifier), + ("duration", self.getduration()), + ("indicator", self.getindicator())], + criteria, parent_infos) + setattr(cls, "Search", Search) + cls = _initElementClass("actionBlock", "commonObjects_actionBlock", "single") if cls: def compatibility(self, tree): @@ -2201,6 +2461,17 @@ action.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + def Search(self, criteria, parent_infos=[]): + parent_infos = parent_infos + ["action_block", self.getlocalId()] + search_result = [] + for idx, action in enumerate(self.action): + search_result.extend(action.Search(criteria, parent_infos + ["action", idx])) + return search_result + setattr(cls, "Search", Search) + +def _SearchInIOVariable(self, criteria, parent_infos=[]): + return _Search([("expression", self.getexpression())], criteria, parent_infos + ["io_variable", self.getlocalId()]) + cls = _initElementClass("inVariable", "fbdObjects_inVariable") if cls: setattr(cls, "getinfos", _getvariableinfosFunction("input", False, True)) @@ -2214,6 +2485,8 @@ self.expression = update_address(self.expression, address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + setattr(cls, "Search", _SearchInIOVariable) + cls = _initElementClass("outVariable", "fbdObjects_outVariable", "single") if cls: setattr(cls, "getinfos", _getvariableinfosFunction("output", True, False)) @@ -2227,6 +2500,8 @@ self.expression = update_address(self.expression, address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + setattr(cls, "Search", _SearchInIOVariable) + cls = _initElementClass("inOutVariable", "fbdObjects_inOutVariable", "single") if cls: setattr(cls, "getinfos", _getvariableinfosFunction("inout", True, True)) @@ -2240,13 +2515,21 @@ self.expression = update_address(self.expression, address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) + setattr(cls, "Search", _SearchInIOVariable) + + +def _SearchInConnector(self, criteria, parent_infos=[]): + return _Search([("name", self.getname())], criteria, parent_infos + ["connector", self.getlocalId()]) + cls = _initElementClass("continuation", "commonObjects_continuation") if cls: setattr(cls, "getinfos", _getconnectorinfosFunction("continuation")) + setattr(cls, "Search", _SearchInConnector) cls = _initElementClass("connector", "commonObjects_connector", "single") if cls: setattr(cls, "getinfos", _getconnectorinfosFunction("connector")) + setattr(cls, "Search", _SearchInConnector) cls = PLCOpenClasses.get("connection", None) if cls: