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: andrej@1881: andrej@1881: from __future__ import absolute_import andrej@2456: from functools import reduce 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 Edouard@1948: from plcopen.types_enums import GetElementType Laurent@814: andrej@1736: 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: andrej@1782: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Search Result Panel andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: class SearchResultPanel(wx.Panel): 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) andrej@1730: 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): andrej@1745: 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) andrej@1730: Laurent@814: def _init_coll_HeaderSizer_Growables(self, parent): Laurent@814: parent.AddGrowableCol(0) andrej@1730: 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) andrej@1730: 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) andrej@1730: Laurent@814: self.SetSizer(self.MainSizer) Laurent@814: Laurent@814: def _init_ctrls(self, prnt): Edouard@2737: self.HeaderLabel = wx.StaticText(name='HeaderLabel', parent=self, andrej@1768: pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) andrej@1730: andrej@1745: search_results_tree_style = CT.TR_HAS_BUTTONS | CT.TR_NO_LINES | CT.TR_HAS_VARIABLE_ROW_HEIGHT Edouard@2737: self.SearchResultsTree = CT.CustomTreeCtrl(name="SearchResultsTree", parent=self, andrej@1768: pos=wx.Point(0, 0), style=search_results_tree_style) Edouard@2737: self.SearchResultsTree.SetAGWWindowStyleFlag(search_results_tree_style) Laurent@814: self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated, Edouard@2737: self.SearchResultsTree) andrej@1768: andrej@1768: self.ResetButton = wx.lib.buttons.GenBitmapButton( andrej@1768: self, bitmap=GetBitmap("reset"), andrej@1768: 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) andrej@1730: Laurent@814: self._init_sizers() Laurent@814: Laurent@814: def __init__(self, parent, window): Edouard@2737: wx.Panel.__init__(self, andrej@1836: name='SearchResultPanel', parent=parent, andrej@1836: pos=wx.Point(0, 0), andrej@1836: size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) andrej@1836: Laurent@814: self.ParentWindow = window andrej@1730: Laurent@814: self._init_ctrls(parent) andrej@1730: Laurent@814: # Define Tree item icon list Laurent@814: self.TreeImageList = wx.ImageList(16, 16) Laurent@814: self.TreeImageDict = {} andrej@1730: Laurent@814: # Icons for other items Laurent@814: for imgname, itemtype in [ andrej@1766: # editables andrej@1766: ("PROJECT", ITEM_PROJECT), andrej@1766: ("TRANSITION", ITEM_TRANSITION), andrej@1766: ("ACTION", ITEM_ACTION), andrej@1766: ("CONFIGURATION", ITEM_CONFIGURATION), andrej@1766: ("RESOURCE", ITEM_RESOURCE), andrej@1766: ("DATATYPE", ITEM_DATATYPE), andrej@1766: ("ACTION", "action_block"), andrej@1766: ("IL", "IL"), Edouard@2524: ("ST", "ST"), Edouard@2551: ("FILE", ITEM_CONFNODE)]: Laurent@814: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) andrej@1730: Laurent@814: for itemtype in ["function", "functionBlock", "program", Laurent@814: "comment", "block", "io_variable", Laurent@814: "connector", "contact", "coil", andrej@1730: "step", "transition", "jump", andrej@1730: "var_local", "var_input", Laurent@814: "var_inout", "var_output"]: Laurent@814: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(itemtype.upper())) andrej@1730: Laurent@814: # Assign icon list to TreeCtrl Laurent@814: self.SearchResultsTree.SetImageList(self.TreeImageList) andrej@1730: 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 = [] andrej@1730: 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]) andrej@1730: Laurent@814: results = self.SearchResults.setdefault(infos[0], []) Laurent@814: results.append((infos, start, end, text)) andrej@1730: Laurent@814: self.RefreshView() andrej@1730: Laurent@814: def ResetSearchResults(self): Laurent@814: self.Criteria = None Laurent@814: self.ElementsOrder = [] Laurent@814: self.SearchResults = {} Laurent@814: self.RefreshView() andrej@1730: Laurent@814: def RefreshView(self): Laurent@814: self.SearchResultsTree.DeleteAllItems() Laurent@814: if self.Criteria is None: surkovsv93@1884: self.SearchResultsTree.AddRoot("") surkovsv93@1884: root = self.SearchResultsTree.GetRootItem() surkovsv93@1884: root.SetHilight(False) Laurent@814: self.HeaderLabel.SetLabel(_("No search results available.")) Laurent@814: self.ResetButton.Enable(False) Laurent@814: else: Laurent@814: matches_number = 0 andrej@1773: search_results_tree_infos = { andrej@1773: "name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(), andrej@1773: "type": ITEM_PROJECT, andrej@1773: "data": None, andrej@1773: "text": None, andrej@1773: "matches": None, andrej@1773: } 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) andrej@1730: Laurent@814: words = tagname.split("::") andrej@1730: Edouard@1948: element_type = GetElementType(tagname) Laurent@814: if element_type == ITEM_POU: Laurent@814: element_type = self.ParentWindow.Controler.GetPouType(words[1]) andrej@1730: 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)} andrej@1730: Laurent@814: children = element_infos.setdefault("children", []) Laurent@814: for infos, start, end, text in results: Edouard@2524: if len(words) == 1: # CTN match Edouard@2551: child_name = {"body": str(start[0])+":", Edouard@2551: "var_inout": _("Variable:")}[infos[1]] Edouard@2551: child_type = {"body": ITEM_CONFNODE, Edouard@2551: "var_inout": "var_inout"}[infos[1]] Edouard@2524: elif 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:]) andrej@1773: child_infos = { andrej@1773: "name": child_name, andrej@1773: "type": child_type, andrej@1773: "data": (infos, start, end, None), andrej@1773: "text": text, andrej@1773: "matches": 1, andrej@1773: "children": [], andrej@1773: } Laurent@814: children.append(child_infos) andrej@1730: Edouard@2524: # not Project node 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) Edouard@2524: else: # Project node or CTN Laurent@814: search_results_tree_children.append(element_infos) andrej@1730: Laurent@814: if matches_number < 2: andrej@1581: header_format = _("'{a1}' - {a2} match in project") Laurent@814: else: andrej@1581: header_format = _("'{a1}' - {a2} matches in project") andrej@1730: andrej@1744: self.HeaderLabel.SetLabel(header_format.format(a1=self.Criteria["find_pattern"], a2=matches_number)) Laurent@814: self.ResetButton.Enable(True) andrej@1730: 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) andrej@1730: 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 andrej@1730: Laurent@814: def GetTextCtrlDClickFunction(self, item): Laurent@814: def OnTextCtrlDClick(event): Laurent@814: self.ShowSearchResults(item) Laurent@814: event.Skip() Laurent@814: return OnTextCtrlDClick andrej@1730: Laurent@814: def GenerateSearchResultsTreeBranch(self, root, infos): Laurent@814: if infos["name"] == "body": Laurent@814: item_name = "%d:" % infos["data"][1][0] Laurent@814: else: Laurent@814: item_name = infos["name"] andrej@1730: 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"]]) andrej@1730: 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)) andrej@1730: Laurent@814: if text is not None: andrej@1745: text_ctrl_style = wx.BORDER_NONE | wx.TE_READONLY | wx.TE_RICH2 Laurent@814: if wx.Platform != '__WXMSW__' or len(text.splitlines()) > 1: Edouard@2524: text_ctrl_style |= wx.TE_MULTILINE | wx.TE_NO_VSCROLL andrej@1730: text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), andrej@1768: 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) andrej@1730: andrej@1870: item, root_cookie = self.SearchResultsTree.GetFirstChild(root) 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) andrej@1730: Laurent@814: def ShowSearchResults(self, item): Laurent@814: data = self.SearchResultsTree.GetPyData(item) andrej@2450: if isinstance(data, tuple): Laurent@814: search_results = [data] Laurent@814: else: Laurent@814: search_results = self.SearchResults.get(data, []) surkovsv93@1622: self.ParentWindow.ClearHighlights(SEARCH_RESULT_HIGHLIGHT) andrej@1847: for infos, start, end, _text in search_results: Laurent@814: self.ParentWindow.ShowSearchResult(infos, start, end) andrej@1730: Laurent@814: def OnSearchResultsTreeItemActivated(self, event): Laurent@814: self.ShowSearchResults(event.GetItem()) Laurent@814: event.Skip() andrej@1730: Laurent@814: def OnResetButton(self, event): Laurent@814: self.ResetSearchResults() Laurent@814: self.ParentWindow.ClearSearchResults() Laurent@814: event.Skip()