Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1571: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1571: # andrej@1571: # See COPYING file for copyrights details. andrej@1571: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. andrej@1571: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. andrej@1571: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Laurent@814: Laurent@814: from types import TupleType Laurent@814: Laurent@814: import wx Laurent@814: import wx.lib.buttons Laurent@814: import wx.lib.agw.customtreectrl as CT Laurent@814: Laurent@814: from PLCControler import * Laurent@814: from util.BitmapLibrary import GetBitmap Laurent@814: Laurent@814: def GenerateName(infos): Laurent@814: if infos[0] in ["input", "output", "value"]: Laurent@814: return "%s %d:" % (infos[0], infos[1]) Laurent@814: elif infos[0] == "range": Laurent@814: return "%s %d %s" % (infos[0], infos[1], infos[2]) Laurent@814: elif infos[0] == "struct": Laurent@814: return "element %d %s" % (infos[1], infos[2]) Laurent@814: return "%s:" % infos[0] Laurent@814: Laurent@814: #------------------------------------------------------------------------------- Laurent@814: # Search Result Panel Laurent@814: #------------------------------------------------------------------------------- Laurent@814: Laurent@814: [ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL, Laurent@814: ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON, Laurent@814: ] = [wx.NewId() for _init_ctrls in range(4)] Laurent@814: Laurent@814: class SearchResultPanel(wx.Panel): Laurent@814: Laurent@814: if wx.VERSION < (2, 6, 0): Laurent@814: def Bind(self, event, function, id = None): Laurent@814: if id is not None: Laurent@814: event(self, id, function) Laurent@814: else: Laurent@814: event(self, function) Laurent@814: Laurent@814: def _init_coll_MainSizer_Items(self, parent): Laurent@814: parent.AddSizer(self.HeaderSizer, 0, border=0, flag=wx.GROW) Laurent@814: parent.AddWindow(self.SearchResultsTree, 1, border=0, flag=wx.GROW) Laurent@814: Laurent@814: def _init_coll_MainSizer_Growables(self, parent): Laurent@814: parent.AddGrowableCol(0) Laurent@814: parent.AddGrowableRow(1) Laurent@814: Laurent@814: def _init_coll_HeaderSizer_Items(self, parent): Laurent@814: parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) Laurent@814: parent.AddWindow(self.ResetButton, 0, border=0, flag=0) Laurent@814: Laurent@814: def _init_coll_HeaderSizer_Growables(self, parent): Laurent@814: parent.AddGrowableCol(0) Laurent@814: Laurent@814: def _init_sizers(self): Laurent@814: self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) Laurent@814: self.HeaderSizer = wx.BoxSizer(wx.HORIZONTAL) Laurent@814: Laurent@814: self._init_coll_MainSizer_Items(self.MainSizer) Laurent@814: self._init_coll_MainSizer_Growables(self.MainSizer) Laurent@814: self._init_coll_HeaderSizer_Items(self.HeaderSizer) Laurent@814: Laurent@814: self.SetSizer(self.MainSizer) Laurent@814: Laurent@814: def _init_ctrls(self, prnt): Laurent@814: wx.Panel.__init__(self, id=ID_SEARCHRESULTPANEL, Laurent@814: name='SearchResultPanel', parent=prnt, pos=wx.Point(0, 0), Laurent@814: size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) Laurent@814: Laurent@814: self.HeaderLabel = wx.StaticText(id=ID_SEARCHRESULTPANELHEADERLABEL, Laurent@814: name='HeaderLabel', parent=self, Laurent@814: pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) Laurent@814: Laurent@814: search_results_tree_style = CT.TR_HAS_BUTTONS|CT.TR_NO_LINES|CT.TR_HAS_VARIABLE_ROW_HEIGHT Laurent@814: self.SearchResultsTree = CT.CustomTreeCtrl(id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE, Laurent@814: name="SearchResultsTree", parent=self, Laurent@814: pos=wx.Point(0, 0), style=search_results_tree_style) Laurent@814: if wx.VERSION >= (2, 8, 11): Laurent@814: self.SearchResultsTree.SetAGWWindowStyleFlag(search_results_tree_style) Laurent@814: self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated, Laurent@814: id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE) Laurent@814: Laurent@814: self.ResetButton = wx.lib.buttons.GenBitmapButton(self, Laurent@814: bitmap=GetBitmap("reset"), size=wx.Size(28, 28), style=wx.NO_BORDER) Laurent@814: self.ResetButton.SetToolTipString(_("Reset search result")) Laurent@814: self.Bind(wx.EVT_BUTTON, self.OnResetButton, self.ResetButton) Laurent@814: Laurent@814: self._init_sizers() Laurent@814: Laurent@814: def __init__(self, parent, window): Laurent@814: self.ParentWindow = window Laurent@814: Laurent@814: self._init_ctrls(parent) Laurent@814: Laurent@814: # Define Tree item icon list Laurent@814: self.TreeImageList = wx.ImageList(16, 16) Laurent@814: self.TreeImageDict = {} Laurent@814: Laurent@814: # Icons for other items Laurent@814: for imgname, itemtype in [ Laurent@814: #editables Laurent@814: ("PROJECT", ITEM_PROJECT), Laurent@814: ("TRANSITION", ITEM_TRANSITION), Laurent@814: ("ACTION", ITEM_ACTION), Laurent@814: ("CONFIGURATION", ITEM_CONFIGURATION), Laurent@814: ("RESOURCE", ITEM_RESOURCE), Laurent@814: ("DATATYPE", ITEM_DATATYPE), Laurent@814: ("ACTION", "action_block"), Laurent@814: ("IL", "IL"), Laurent@814: ("ST", "ST")]: Laurent@814: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) Laurent@814: Laurent@814: for itemtype in ["function", "functionBlock", "program", Laurent@814: "comment", "block", "io_variable", Laurent@814: "connector", "contact", "coil", Laurent@814: "step", "transition", "jump", Laurent@814: "var_local", "var_input", Laurent@814: "var_inout", "var_output"]: Laurent@814: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(itemtype.upper())) Laurent@814: Laurent@814: # Assign icon list to TreeCtrl Laurent@814: self.SearchResultsTree.SetImageList(self.TreeImageList) Laurent@814: Laurent@814: self.ResetSearchResults() Laurent@814: Laurent@814: def SetSearchResults(self, criteria, search_results): Laurent@814: self.Criteria = criteria Laurent@814: self.SearchResults = {} Laurent@814: self.ElementsOrder = [] Laurent@814: Laurent@814: for infos, start, end, text in search_results: Laurent@814: if infos[0] not in self.ElementsOrder: Laurent@814: self.ElementsOrder.append(infos[0]) Laurent@814: Laurent@814: results = self.SearchResults.setdefault(infos[0], []) Laurent@814: results.append((infos, start, end, text)) Laurent@814: Laurent@814: self.RefreshView() Laurent@814: Laurent@814: def ResetSearchResults(self): Laurent@814: self.Criteria = None Laurent@814: self.ElementsOrder = [] Laurent@814: self.SearchResults = {} Laurent@814: self.RefreshView() Laurent@814: Laurent@814: def RefreshView(self): Laurent@814: self.SearchResultsTree.DeleteAllItems() Laurent@814: if self.Criteria is None: Laurent@814: self.HeaderLabel.SetLabel(_("No search results available.")) Laurent@814: self.ResetButton.Enable(False) Laurent@814: else: Laurent@814: matches_number = 0 Laurent@814: search_results_tree_infos = {"name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(), Laurent@814: "type": ITEM_PROJECT, Laurent@814: "data": None, Laurent@814: "text": None, Laurent@814: "matches": None, Laurent@814: } Laurent@814: search_results_tree_children = search_results_tree_infos.setdefault("children", []) Laurent@814: for tagname in self.ElementsOrder: Laurent@814: results = self.SearchResults.get(tagname, []) Laurent@814: matches_number += len(results) Laurent@814: Laurent@814: words = tagname.split("::") Laurent@814: Laurent@814: element_type = self.ParentWindow.Controler.GetElementType(tagname) Laurent@814: if element_type == ITEM_POU: Laurent@814: element_type = self.ParentWindow.Controler.GetPouType(words[1]) Laurent@814: Laurent@814: element_infos = {"name": words[-1], Laurent@814: "type": element_type, Laurent@814: "data": tagname, Laurent@814: "text": None, Laurent@814: "matches": len(results)} Laurent@814: Laurent@814: children = element_infos.setdefault("children", []) Laurent@814: for infos, start, end, text in results: Laurent@814: if infos[1] == "name" or element_type == ITEM_DATATYPE: Laurent@814: child_name = GenerateName(infos[1:]) Laurent@814: child_type = element_type Laurent@814: else: Laurent@814: if element_type == ITEM_RESOURCE: Laurent@814: child_type = element_type Laurent@814: else: Laurent@814: child_type = infos[1] Laurent@814: if child_type == "name": Laurent@814: child_name = "name" Laurent@814: elif child_type == "body": Laurent@814: child_name = "body" Laurent@814: if element_type == ITEM_TRANSITION: Laurent@814: child_type = self.ParentWindow.Controler.GetTransitionBodyType(words[1], words[2]) Laurent@814: elif element_type == ITEM_ACTION: Laurent@814: child_type = self.ParentWindow.Controler.GetActionBodyType(words[1], words[2]) Laurent@814: else: Laurent@814: child_type = self.ParentWindow.Controler.GetPouBodyType(words[1]) Laurent@814: else: Laurent@814: child_name = GenerateName(infos[3:]) Laurent@814: child_infos = {"name": child_name, Laurent@814: "type": child_type, Laurent@814: "data": (infos, start, end ,None), Laurent@814: "text": text, Laurent@814: "matches": 1, Laurent@814: "children": [], Laurent@814: } Laurent@814: children.append(child_infos) Laurent@814: Laurent@814: if len(words) > 2: Laurent@814: for _element_infos in search_results_tree_children: Laurent@814: if _element_infos["name"] == words[1]: Laurent@814: _element_infos["matches"] += len(children) Laurent@814: _element_infos["children"].append(element_infos) Laurent@814: break Laurent@899: if element_type == ITEM_RESOURCE: Laurent@899: search_results_tree_children.append(element_infos) Laurent@899: else: Laurent@899: _tagname = self.ParentWindow.Controler.ComputePouName(words[1]) Laurent@899: _element_type = self.ParentWindow.Controler.GetPouType(words[1]) Laurent@899: Laurent@899: _element_infos = {"name": words[1], Laurent@899: "type": _element_type, Laurent@899: "data": _tagname, Laurent@899: "text": None, Laurent@899: "matches": 1, Laurent@899: "children": [element_infos]} Laurent@899: Laurent@899: search_results_tree_children.append(_element_infos) Laurent@899: Laurent@814: else: Laurent@814: search_results_tree_children.append(element_infos) Laurent@814: Laurent@814: if matches_number < 2: Laurent@814: header_format = _("'%s' - %d match in project") Laurent@814: else: Laurent@814: header_format = _("'%s' - %d matches in project") Laurent@814: surkovsv93@1556: self.HeaderLabel.SetLabel(header_format % (self.Criteria["find_pattern"], matches_number)) Laurent@814: self.ResetButton.Enable(True) Laurent@814: Laurent@814: if matches_number > 0: Laurent@814: root = self.SearchResultsTree.GetRootItem() Laurent@814: if root is None: Laurent@814: root = self.SearchResultsTree.AddRoot(search_results_tree_infos["name"]) Laurent@814: self.GenerateSearchResultsTreeBranch(root, search_results_tree_infos) Laurent@814: self.SearchResultsTree.Expand(root) Laurent@814: Laurent@814: def GetTextCtrlClickFunction(self, item): Laurent@814: def OnTextCtrlClick(event): Laurent@814: self.SearchResultsTree.SelectItem(item) Laurent@814: event.Skip() Laurent@814: return OnTextCtrlClick Laurent@814: Laurent@814: def GetTextCtrlDClickFunction(self, item): Laurent@814: def OnTextCtrlDClick(event): Laurent@814: self.ShowSearchResults(item) Laurent@814: event.Skip() Laurent@814: return OnTextCtrlDClick Laurent@814: Laurent@814: def GenerateSearchResultsTreeBranch(self, root, infos): Laurent@814: to_delete = [] Laurent@814: if infos["name"] == "body": Laurent@814: item_name = "%d:" % infos["data"][1][0] Laurent@814: else: Laurent@814: item_name = infos["name"] Laurent@814: Laurent@814: self.SearchResultsTree.SetItemText(root, item_name) Laurent@814: self.SearchResultsTree.SetPyData(root, infos["data"]) Laurent@814: self.SearchResultsTree.SetItemBackgroundColour(root, wx.WHITE) Laurent@814: self.SearchResultsTree.SetItemTextColour(root, wx.BLACK) Laurent@814: if infos["type"] is not None: Laurent@814: if infos["type"] == ITEM_POU: Laurent@814: self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[self.ParentWindow.Controler.GetPouType(infos["name"])]) Laurent@814: else: Laurent@814: self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) Laurent@814: Laurent@814: text = None Laurent@814: if infos["text"] is not None: Laurent@814: text = infos["text"] Laurent@814: start, end = infos["data"][1:3] Laurent@814: text_lines = infos["text"].splitlines() Laurent@814: start_idx = start[1] Laurent@814: end_idx = reduce(lambda x, y: x + y, map(lambda x: len(x) + 1, text_lines[:end[0] - start[0]]), end[1] + 1) Laurent@814: style = wx.TextAttr(wx.BLACK, wx.Colour(206, 204, 247)) Laurent@814: elif infos["type"] is not None and infos["matches"] > 1: Laurent@814: text = _("(%d matches)") % infos["matches"] Laurent@814: start_idx, end_idx = 0, len(text) Laurent@814: style = wx.TextAttr(wx.Colour(0, 127, 174)) Laurent@814: Laurent@814: if text is not None: Laurent@814: text_ctrl_style = wx.BORDER_NONE|wx.TE_READONLY|wx.TE_RICH2 Laurent@814: if wx.Platform != '__WXMSW__' or len(text.splitlines()) > 1: Laurent@814: text_ctrl_style |= wx.TE_MULTILINE Laurent@814: text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), Laurent@814: value=text, style=text_ctrl_style) Laurent@814: width, height = text_ctrl.GetTextExtent(text) Laurent@814: text_ctrl.SetClientSize(wx.Size(width + 1, height)) Laurent@814: text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour()) Laurent@814: text_ctrl.Bind(wx.EVT_LEFT_DOWN, self.GetTextCtrlClickFunction(root)) Laurent@814: text_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.GetTextCtrlDClickFunction(root)) Laurent@814: text_ctrl.SetInsertionPoint(0) Laurent@814: text_ctrl.SetStyle(start_idx, end_idx, style) Laurent@814: self.SearchResultsTree.SetItemWindow(root, text_ctrl) Laurent@814: Laurent@814: if wx.VERSION >= (2, 6, 0): Laurent@814: item, root_cookie = self.SearchResultsTree.GetFirstChild(root) Laurent@814: else: Laurent@814: item, root_cookie = self.SearchResultsTree.GetFirstChild(root, 0) Laurent@814: for child in infos["children"]: Laurent@814: if item is None: Laurent@814: item = self.SearchResultsTree.AppendItem(root, "") Laurent@814: item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) Laurent@814: self.GenerateSearchResultsTreeBranch(item, child) Laurent@814: item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) Laurent@814: Laurent@814: def ShowSearchResults(self, item): Laurent@814: data = self.SearchResultsTree.GetPyData(item) Laurent@814: if isinstance(data, TupleType): Laurent@814: search_results = [data] Laurent@814: else: Laurent@814: search_results = self.SearchResults.get(data, []) Laurent@814: for infos, start, end, text in search_results: Laurent@814: self.ParentWindow.ShowSearchResult(infos, start, end) Laurent@814: Laurent@814: def OnSearchResultsTreeItemActivated(self, event): Laurent@814: self.ShowSearchResults(event.GetItem()) Laurent@814: event.Skip() Laurent@814: Laurent@814: def OnResetButton(self, event): Laurent@814: self.ResetSearchResults() Laurent@814: self.ParentWindow.ClearSearchResults() Laurent@814: event.Skip()