SearchResultPanel.py
changeset 566 6014ef82a98a
child 571 79af7b821233
equal deleted inserted replaced
565:94c11207aa6f 566:6014ef82a98a
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 from types import TupleType
       
    26 
       
    27 import wx
       
    28 import wx.lib.agw.customtreectrl as CT
       
    29 
       
    30 from PLCControler import *
       
    31 
       
    32 CWD = os.path.split(os.path.realpath(__file__))[0]
       
    33 
       
    34 def GenerateName(infos):
       
    35     if infos[0] in ["input", "output"]:
       
    36         return "%s %d:" % (infos[0], infos[1])
       
    37     return "%s:" % infos[0]
       
    38 
       
    39 #-------------------------------------------------------------------------------
       
    40 #                            Search Result Panel
       
    41 #-------------------------------------------------------------------------------
       
    42 
       
    43 [ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL,
       
    44  ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON, 
       
    45 ] = [wx.NewId() for _init_ctrls in range(4)]
       
    46 
       
    47 class SearchResultPanel(wx.Panel):
       
    48 
       
    49     if wx.VERSION < (2, 6, 0):
       
    50         def Bind(self, event, function, id = None):
       
    51             if id is not None:
       
    52                 event(self, id, function)
       
    53             else:
       
    54                 event(self, function)
       
    55 
       
    56     def _init_coll_MainSizer_Items(self, parent):
       
    57         parent.AddSizer(self.HeaderSizer, 0, border=0, flag=wx.GROW)
       
    58         parent.AddWindow(self.SearchResultsTree, 1, border=0, flag=wx.GROW)
       
    59     
       
    60     def _init_coll_MainSizer_Growables(self, parent):
       
    61         parent.AddGrowableCol(0)
       
    62         parent.AddGrowableRow(1)
       
    63 
       
    64     def _init_coll_HeaderSizer_Items(self, parent):
       
    65         parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
       
    66         parent.AddWindow(self.ResetButton, 0, border=0, flag=0)
       
    67     
       
    68     def _init_coll_HeaderSizer_Growables(self, parent):
       
    69         parent.AddGrowableCol(0)
       
    70     
       
    71     def _init_sizers(self):
       
    72         self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
    73         self.HeaderSizer = wx.BoxSizer(wx.HORIZONTAL)
       
    74         
       
    75         self._init_coll_MainSizer_Items(self.MainSizer)
       
    76         self._init_coll_MainSizer_Growables(self.MainSizer)
       
    77         self._init_coll_HeaderSizer_Items(self.HeaderSizer)
       
    78         
       
    79         self.SetSizer(self.MainSizer)
       
    80 
       
    81     def _init_ctrls(self, prnt):
       
    82         wx.Panel.__init__(self, id=ID_SEARCHRESULTPANEL,
       
    83               name='SearchResultPanel', parent=prnt, pos=wx.Point(0, 0),
       
    84               size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
       
    85 
       
    86         self.HeaderLabel = wx.StaticText(id=ID_SEARCHRESULTPANELHEADERLABEL,
       
    87               name='HeaderLabel', parent=self,
       
    88               pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0)
       
    89 
       
    90         self.SearchResultsTree = CT.CustomTreeCtrl(id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE,
       
    91               name="SearchResultsTree", parent=self,
       
    92               pos=wx.Point(0, 0), style=0)
       
    93         self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated,
       
    94               id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE)
       
    95         
       
    96         self.ResetButton = wx.Button(id=ID_SEARCHRESULTPANELRESETBUTTON, label=_('Reset'),
       
    97               name='ResetButton', parent=self, pos=wx.Point(0, 0),
       
    98               size=wx.DefaultSize, style=0)
       
    99         self.Bind(wx.EVT_BUTTON, self.OnResetButton, 
       
   100               id=ID_SEARCHRESULTPANELRESETBUTTON)
       
   101         
       
   102         self._init_sizers()
       
   103 
       
   104     def __init__(self, parent, window):
       
   105         self._init_ctrls(parent)
       
   106         self.ParentWindow = window
       
   107         
       
   108         # Define Tree item icon list
       
   109         self.TreeImageList = wx.ImageList(16, 16)
       
   110         self.TreeImageDict = {}
       
   111         
       
   112         # Icons for other items
       
   113         for imgname, itemtype in [
       
   114             #editables
       
   115             ("PROJECT",        ITEM_PROJECT),
       
   116             ("TRANSITION",     ITEM_TRANSITION),
       
   117             ("ACTION",         ITEM_ACTION),
       
   118             ("CONFIGURATION",  ITEM_CONFIGURATION),
       
   119             ("RESOURCE",       ITEM_RESOURCE),
       
   120             ("DATATYPE",       ITEM_DATATYPE),
       
   121             ("ACTION",         "action_block"),
       
   122             ("IL",             "IL"),
       
   123             ("ST",             "ST")]:
       
   124             self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname)))
       
   125         
       
   126         for itemtype in ["function", "functionBlock", "program",
       
   127                          "comment", "block", "io_variable",
       
   128                          "connector", "contact", "coil",
       
   129                          "step", "transition", "jump", 
       
   130                          "var_local", "var_input", 
       
   131                          "var_inout", "var_output"]:
       
   132             self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%itemtype.upper())))
       
   133         
       
   134         # Assign icon list to TreeCtrl
       
   135         self.SearchResultsTree.SetImageList(self.TreeImageList)
       
   136         
       
   137         self.ResetSearchResults()
       
   138 
       
   139     def SetSearchResults(self, criteria, search_results):
       
   140         self.Criteria = criteria
       
   141         
       
   142         for infos, start, end, text in search_results:
       
   143             if infos[0] not in self.ElementsOrder:
       
   144                 self.ElementsOrder.append(infos[0])
       
   145             
       
   146             results = self.SearchResults.setdefault(infos[0], [])
       
   147             results.append((infos, start, end, text))
       
   148         
       
   149         self.RefreshView()
       
   150     
       
   151     def ResetSearchResults(self):
       
   152         self.Criteria = None
       
   153         self.ElementsOrder = []
       
   154         self.SearchResults = {}
       
   155         self.RefreshView()
       
   156     
       
   157     def RefreshView(self):
       
   158         if self.Criteria is None:
       
   159             self.HeaderLabel.SetLabel(_("No search results available."))
       
   160             self.SearchResultsTree.DeleteAllItems()
       
   161             self.ResetButton.Enable(False)
       
   162         else:
       
   163             matches_number = 0
       
   164             search_results_tree_infos = {"name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(),
       
   165                                          "type": ITEM_PROJECT,
       
   166                                          "data": None,
       
   167                                          "text": None,
       
   168                                          "matches": None,
       
   169                                         }
       
   170             search_results_tree_children = search_results_tree_infos.setdefault("children", [])
       
   171             for tagname in self.ElementsOrder:
       
   172                 results = self.SearchResults.get(tagname, [])
       
   173                 matches_number += len(results)
       
   174                 
       
   175                 words = tagname.split("::")
       
   176                 
       
   177                 element_type = self.ParentWindow.Controler.GetElementType(tagname)
       
   178                 if element_type == ITEM_POU:
       
   179                     element_type = self.ParentWindow.Controler.GetPouType(words[1])
       
   180                 
       
   181                 element_infos = {"name": words[-1],
       
   182                                  "type": element_type,
       
   183                                  "data": tagname,
       
   184                                  "text": None,
       
   185                                  "matches": len(results)}
       
   186                 
       
   187                 children = element_infos.setdefault("children", [])
       
   188                 for infos, start, end, text in results:
       
   189                     child_type = infos[1]
       
   190                     if child_type == "body":
       
   191                         child_name = "body"
       
   192                         if element_type == ITEM_TRANSITION:
       
   193                             child_type = self.ParentWindow.Controler.GetTransitionBodyType(words[1], words[2])
       
   194                         elif element_type == ITEM_ACTION:
       
   195                             child_type = self.ParentWindow.Controler.GetActionBodyType(words[1], words[2])
       
   196                         else:
       
   197                             child_type = self.ParentWindow.Controler.GetPouBodyType(words[1])
       
   198                     else:
       
   199                         child_name = GenerateName(infos[3:])
       
   200                     child_infos = {"name": child_name,
       
   201                                    "type": child_type,
       
   202                                    "data": (infos, start, end ,None),
       
   203                                    "text": text,
       
   204                                    "matches": 1,
       
   205                                    "children": [],
       
   206                                   }
       
   207                     children.append(child_infos)
       
   208                 
       
   209                 if len(words) > 2:
       
   210                     for _element_infos in search_results_tree_children:
       
   211                         if _element_infos["name"] == words[1]:
       
   212                             _element_infos["matches"] += len(children)
       
   213                             _element_infos["children"].append(element_infos)
       
   214                             break
       
   215                 else:
       
   216                     search_results_tree_children.append(element_infos)
       
   217             
       
   218             if matches_number < 2:
       
   219                 header_format = _("'%s' - %d match in project")
       
   220             else:
       
   221                 header_format = _("'%s' - %d matches in project")
       
   222             
       
   223             self.HeaderLabel.SetLabel(header_format % (self.Criteria["raw_pattern"], matches_number))
       
   224             self.ResetButton.Enable(True)
       
   225             
       
   226             if matches_number > 0:
       
   227                 root = self.SearchResultsTree.GetRootItem()
       
   228                 if root is None:
       
   229                     root = self.SearchResultsTree.AddRoot(search_results_tree_infos["name"])
       
   230                 self.GenerateSearchResultsTreeBranch(root, search_results_tree_infos)
       
   231                 self.SearchResultsTree.Expand(root)
       
   232     
       
   233     def GetTextCtrlClickFunction(self, item):
       
   234         def OnTextCtrlClick(event):
       
   235             self.SearchResultsTree.SelectItem(item)
       
   236             event.Skip()
       
   237         return OnTextCtrlClick
       
   238     
       
   239     def GetTextCtrlDClickFunction(self, item):
       
   240         def OnTextCtrlDClick(event):
       
   241             self.ShowSearchResults(item)
       
   242             event.Skip()
       
   243         return OnTextCtrlDClick
       
   244     
       
   245     def GenerateSearchResultsTreeBranch(self, root, infos):
       
   246         to_delete = []
       
   247         if infos["name"] == "body":
       
   248             item_name = "%d:" % infos["data"][1][0]
       
   249         else:
       
   250             item_name = infos["name"]
       
   251         
       
   252         self.SearchResultsTree.SetItemText(root, item_name)
       
   253         self.SearchResultsTree.SetPyData(root, infos["data"])
       
   254         self.SearchResultsTree.SetItemBackgroundColour(root, wx.WHITE)
       
   255         self.SearchResultsTree.SetItemTextColour(root, wx.BLACK)
       
   256         if infos["type"] is not None:
       
   257             if infos["type"] == ITEM_POU:
       
   258                 self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[self.ParentWindow.Controler.GetPouType(infos["name"])])
       
   259             else:
       
   260                 self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[infos["type"]])
       
   261         
       
   262         if infos["text"] is not None:
       
   263             text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), 
       
   264                     value=infos["text"], style=wx.BORDER_NONE|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2)
       
   265             width, height = wx.PaintDC(text_ctrl).GetTextExtent(infos["text"])
       
   266             text_ctrl.SetClientSize(wx.Size(width + 1, height))
       
   267             text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour())
       
   268             text_ctrl.Bind(wx.EVT_LEFT_DOWN, self.GetTextCtrlClickFunction(root))
       
   269             text_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.GetTextCtrlDClickFunction(root))
       
   270             start, end = infos["data"][1:3]
       
   271             text_lines = infos["text"].splitlines()
       
   272             end_idx = reduce(lambda x, y: x + y, map(lambda x: len(x) + 1, text_lines[:end[0] - start[0]]), end[1] + 1)
       
   273             text_ctrl.SetInsertionPoint(0)
       
   274             text_ctrl.SetStyle(start[1], end_idx, wx.TextAttr(wx.BLACK, wx.Colour(206, 204, 247)))
       
   275             
       
   276             self.SearchResultsTree.SetItemWindow(root, text_ctrl)
       
   277         
       
   278         elif infos["type"] is not None and infos["matches"] > 1:
       
   279             text = _("(%d matches)") % infos["matches"]
       
   280             text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), 
       
   281                     value=text, style=wx.BORDER_NONE|wx.TE_MULTILINE|wx.TE_READONLY|wx.TE_RICH2)
       
   282             width, height = wx.PaintDC(text_ctrl).GetTextExtent(text)
       
   283             text_ctrl.SetClientSize(wx.Size(width + 1, height))
       
   284             text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour())
       
   285             text_ctrl.Bind(wx.EVT_LEFT_DOWN, self.GetTextCtrlClickFunction(root))
       
   286             text_ctrl.Bind(wx.EVT_LEFT_DCLICK, self.GetTextCtrlDClickFunction(root))
       
   287             
       
   288             text_ctrl.SetInsertionPoint(0)
       
   289             text_ctrl.SetStyle(0, len(text), wx.TextAttr(wx.Colour(0, 127, 174)))
       
   290             
       
   291             self.SearchResultsTree.SetItemWindow(root, text_ctrl)
       
   292             
       
   293             
       
   294         if wx.VERSION >= (2, 6, 0):
       
   295             item, root_cookie = self.SearchResultsTree.GetFirstChild(root)
       
   296         else:
       
   297             item, root_cookie = self.SearchResultsTree.GetFirstChild(root, 0)
       
   298         for child in infos["children"]:
       
   299             if item is None:
       
   300                 item = self.SearchResultsTree.AppendItem(root, "")
       
   301                 if wx.Platform != '__WXMSW__':
       
   302                     item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
       
   303             self.GenerateSearchResultsTreeBranch(item, child)
       
   304             item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
       
   305         while item is not None:
       
   306             to_delete.append(item)
       
   307             item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie)
       
   308         for item in to_delete:
       
   309             self.SearchResultsTree.Delete(item)
       
   310     
       
   311     def ShowSearchResults(self, item):
       
   312         data = self.SearchResultsTree.GetPyData(item)
       
   313         if isinstance(data, TupleType):
       
   314             search_results = [data]
       
   315         else:
       
   316             search_results = self.SearchResults.get(data, [])
       
   317         for infos, start, end, text in search_results:
       
   318             self.ParentWindow.ShowSearchResult(infos, start, end)
       
   319     
       
   320     def OnSearchResultsTreeItemActivated(self, event):
       
   321         self.ShowSearchResults(event.GetItem())
       
   322         event.Skip()
       
   323     
       
   324     def OnResetButton(self, event):
       
   325         self.ResetSearchResults()
       
   326         self.ParentWindow.ClearSearchResults()
       
   327         event.Skip()