Adding support for searching text or regular expression in whole project
authorlaurent
Sun, 09 Oct 2011 19:51:14 +0200
changeset 566 6014ef82a98a
parent 565 94c11207aa6f
child 567 eb523c4fa2da
Adding support for searching text or regular expression in whole project
DataTypeEditor.py
Images/BLOCK.png
Images/COIL.png
Images/COMMENT.png
Images/CONNECTOR.png
Images/CONTACT.png
Images/IO_VARIABLE.png
Images/JUMP.png
Images/STEP.png
Images/icons.svg
PLCControler.py
PLCGenerator.py
PLCOpenEditor.py
RessourceEditor.py
SearchResultPanel.py
TextViewer.py
VariablePanel.py
Viewer.py
dialogs/SearchInProjectDialog.py
dialogs/__init__.py
graphics/FBD_Objects.py
graphics/GraphicCommons.py
graphics/LD_Objects.py
graphics/SFC_Objects.py
plcopen/plcopen.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]))
+
Binary file Images/BLOCK.png has changed
Binary file Images/COIL.png has changed
Binary file Images/COMMENT.png has changed
Binary file Images/CONNECTOR.png has changed
Binary file Images/CONTACT.png has changed
Binary file Images/IO_VARIABLE.png has changed
Binary file Images/JUMP.png has changed
Binary file Images/STEP.png has changed
--- 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 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!-- Created with Inkscape (http://www.inkscape.org/) -->
+
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:cc="http://creativecommons.org/ns#"
@@ -13,7 +14,7 @@
    height="16"
    id="svg2"
    sodipodi:version="0.32"
-   inkscape:version="0.46"
+   inkscape:version="0.48.2 r9819"
    sodipodi:modified="TRUE"
    version="1.0"
    inkscape:export-filename="/taf/Pim/workspace_laurent/plcopeneditor/Images/SFC.png"
@@ -485,6 +486,174 @@
        y1="11"
        x2="19"
        y2="23" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-2"
+       id="linearGradient3808-6"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-2">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-8" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-9" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-9"
+       id="linearGradient3808-1"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-9">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-9" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-5"
+       id="linearGradient3808-3"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-5">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-94" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-8" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-3"
+       id="linearGradient3808-8"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-3">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-2" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-1"
+       id="linearGradient3808-19"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-1">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-5" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-6"
+       id="linearGradient3808-2"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-6">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-56" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-63" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-34"
+       id="linearGradient3808-9"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-34">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-6" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-2" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5175-97"
+       id="linearGradient3808-97"
+       gradientUnits="userSpaceOnUse"
+       x1="0"
+       y1="1"
+       x2="15"
+       y2="16"
+       gradientTransform="translate(-40,0)" />
+    <linearGradient
+       id="linearGradient5175-97">
+      <stop
+         style="stop-color:#bdcccd;stop-opacity:1;"
+         offset="0"
+         id="stop5177-3" />
+      <stop
+         style="stop-color:#7979ff;stop-opacity:1;"
+         offset="1"
+         id="stop5179-5" />
+    </linearGradient>
   </defs>
   <sodipodi:namedview
      id="base"
@@ -493,9 +662,9 @@
      borderopacity="1.0"
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
-     inkscape:zoom="2"
-     inkscape:cx="82.340349"
-     inkscape:cy="31.586684"
+     inkscape:zoom="8"
+     inkscape:cx="264.59165"
+     inkscape:cy="46.838908"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      width="16px"
@@ -508,7 +677,8 @@
      inkscape:window-width="1623"
      inkscape:window-height="998"
      inkscape:window-x="52"
-     inkscape:window-y="47">
+     inkscape:window-y="47"
+     inkscape:window-maximized="0">
     <inkscape:grid
        type="xygrid"
        id="grid2410"
@@ -1210,7 +1380,7 @@
          sodipodi:end="3.8497933"
          sodipodi:start="2.4295312"
          transform="matrix(3.333333,0,0,1.666667,-10.63542,-12)"
-         d="M 5.5467128,19.440281 A 2.25,4.5 0 0 1 5.5410496,13.572893"
+         d="M 5.5467128,19.440281 C 4.8199153,17.756163 4.8175085,15.262611 5.5410496,13.572893"
          sodipodi:ry="4.5"
          sodipodi:rx="2.25"
          sodipodi:cy="16.5"
@@ -1223,7 +1393,7 @@
          sodipodi:end="3.8497933"
          sodipodi:start="2.4295312"
          transform="matrix(-3.333333,0,0,1.666667,34.63541,-12)"
-         d="M 5.5467128,19.440281 A 2.25,4.5 0 0 1 5.5410496,13.572893"
+         d="M 5.5467128,19.440281 C 4.8199153,17.756163 4.8175085,15.262611 5.5410496,13.572893"
          sodipodi:ry="4.5"
          sodipodi:rx="2.25"
          sodipodi:cy="16.5"
@@ -1334,7 +1504,7 @@
        transform="translate(-142,91)">
       <path
          transform="matrix(1.25,0,0,1.265625,0,9.9375)"
-         d="M 4,2 A 2,2 0 1 1 0,2 A 2,2 0 1 1 4,2 z"
+         d="M 4,2 C 4,3.1045695 3.1045695,4 2,4 0.8954305,4 0,3.1045695 0,2 0,0.8954305 0.8954305,0 2,0 3.1045695,0 4,0.8954305 4,2 z"
          sodipodi:ry="2"
          sodipodi:rx="2"
          sodipodi:cy="2"
@@ -1344,7 +1514,7 @@
          sodipodi:type="arc" />
       <path
          transform="matrix(1.25,0,0,1.25,19,18.996094)"
-         d="M 4,2 A 2,2 0 1 1 0,2 A 2,2 0 1 1 4,2 z"
+         d="M 4,2 C 4,3.1045695 3.1045695,4 2,4 0.8954305,4 0,3.1045695 0,2 0,0.8954305 0.8954305,0 2,0 3.1045695,0 4,0.8954305 4,2 z"
          sodipodi:ry="2"
          sodipodi:rx="2"
          sodipodi:cy="2"
@@ -2332,5 +2502,370 @@
          x="8"
          id="tspan4028"
          sodipodi:role="line">%% VAR_LOCAL VAR_INPUT VAR_OUTPUT VAR_INOUT %%</tspan></text>
+    <text
+       id="text4026-5"
+       y="-54"
+       x="210"
+       style="font-size:5px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       xml:space="preserve"><tspan
+         y="-54"
+         x="210"
+         id="tspan4028-7"
+         sodipodi:role="line">%% COMMENT BLOCK IO_VARIABLE CONNECTOR CONTACT COIL STEP JUMP %%</tspan></text>
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="COMMENT"
+       transform="translate(265,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="BLOCK"
+       transform="translate(289,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <g
+       style="display:inline"
+       id="g3333-5"
+       inkscape:label="Calque 1"
+       transform="translate(224,-54)">
+      <path
+         inkscape:connector-curvature="0"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+         d="m 3,8 7.65625,0 4.28309,4.15625 0,7.816022 L 3,20 z"
+         id="rect3335-3"
+         sodipodi:nodetypes="cccccc" />
+      <path
+         inkscape:connector-curvature="0"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible"
+         d="m 381,99 0,12 12,0 0,-8.44286 L 389.40625,99 z m 1,1 6,0 0,4 4,0 0,6 -10,0 z m 7,0 3,3 -3,0 z"
+         transform="translate(-378,-91)"
+         id="path3337-9"
+         sodipodi:nodetypes="ccccccccccccccccc" />
+      <text
+         transform="scale(1.0345959,0.96656095)"
+         sodipodi:linespacing="125%"
+         id="text3339-4"
+         y="17.998188"
+         x="4.4307899"
+         style="font-size:3.46388555px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           id="tspan1988-1"
+           y="17.998188"
+           x="4.4307899"
+           sodipodi:role="line">CMT</tspan></text>
+    </g>
+    <g
+       transform="translate(129,-48)"
+       style="display:inline"
+       id="g4204-0">
+      <path
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cccccccccccccccccccccccccccccccccc"
+         id="rect2479-6"
+         d="m 123,2 0,1 -1,0 0,1 1,0 0,2 -1,0 0,1 1,0 0,2 -1,0 0,1 1,0 0,2 -1,0 0,1 1,0 0,1 10,0 0,-3 1,0 0,-1 -1,0 0,-4 1,0 0,-1 -1,0 0,-3 -10,0 z m 1,1 8,0 0,10 -8,0 0,-10 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.33726069px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <text
+         transform="scale(1.0096698,0.9904228)"
+         sodipodi:linespacing="125%"
+         id="text6441-8"
+         y="8.1013546"
+         x="123.80813"
+         style="font-size:4.17388439px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="8.1013546"
+           x="123.80813"
+           id="tspan6443-7"
+           sodipodi:role="line">FB</tspan></text>
+    </g>
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="IO_VARIABLE"
+       transform="translate(316,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <g
+       style="display:inline"
+       id="g3579-7"
+       inkscape:label="Calque 1"
+       transform="translate(269,-52.95581)">
+      <rect
+         y="7.9558101"
+         x="9"
+         height="10"
+         width="10"
+         id="rect3581-3"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+         ry="0" />
+      <path
+         inkscape:connector-curvature="0"
+         inkscape:export-ydpi="90"
+         inkscape:export-xdpi="90"
+         sodipodi:nodetypes="cccccccccccccc"
+         id="path3583-9"
+         d="m 9,8.95581 0,8 10,0 0,-4 2,0 0,-1 -2,0 0,-3 z m 1,1 8,-4e-6 0,6 -8,4e-6 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+      <text
+         sodipodi:linespacing="125%"
+         id="text3585-0"
+         y="13.95581"
+         x="11"
+         style="font-size:2.67459083px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="13.95581"
+           x="11"
+           id="tspan3587-7"
+           sodipodi:role="line">VAR</tspan></text>
+    </g>
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="CONNECTOR"
+       transform="translate(348,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <g
+       style="display:inline"
+       id="g3579-7-2"
+       inkscape:label="Calque 1"
+       transform="translate(301,-52.95581)">
+      <rect
+         y="7.9558101"
+         x="9"
+         height="10"
+         width="10"
+         id="rect3581-3-2"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible"
+         ry="0" />
+      <path
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccc"
+         id="path1951-4"
+         d="M 15,9.3879711 18.35476,12.95581 15,16.5"
+         style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline" />
+      <path
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="ccc"
+         id="path1949-6"
+         d="M 9.4649098,9.5580851 12.55965,12.95581 9.5309398,16.357614"
+         style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline" />
+      <text
+         sodipodi:linespacing="125%"
+         id="text3365-3"
+         y="13.95581"
+         x="13.46875"
+         style="font-size:2.69470572px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="13.95581"
+           x="13.46875"
+           id="tspan3367-9"
+           sodipodi:role="line">C</tspan></text>
+      <path
+         inkscape:connector-curvature="0"
+         inkscape:export-ydpi="90"
+         inkscape:export-xdpi="90"
+         sodipodi:nodetypes="cccccccccccccc"
+         id="path3583-9-4"
+         d="m 9,8.95581 0,8 10,0 0,-4 2,0 0,-1 -2,0 0,-3 z m 1,1 8,-4e-6 0,6 -8,4e-6 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+    </g>
+    <g
+       style="display:inline"
+       id="g3383-5"
+       inkscape:label="Calque 1"
+       transform="translate(335,-49)">
+      <path
+         inkscape:connector-curvature="0"
+         inkscape:export-ydpi="90"
+         inkscape:export-xdpi="90"
+         sodipodi:nodetypes="ccccccccc"
+         id="path3385-2"
+         d="m 10,6 0,9 2,0 0,-4 2,0 0,-1 -2,0 0,-4 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+      <text
+         sodipodi:linespacing="125%"
+         id="text3387-1"
+         y="5"
+         x="5"
+         style="font-size:2.80351019px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="5"
+           x="5"
+           id="tspan3389-3"
+           sodipodi:role="line">VAR</tspan></text>
+      <path
+         inkscape:connector-curvature="0"
+         inkscape:export-ydpi="90"
+         inkscape:export-xdpi="90"
+         sodipodi:nodetypes="ccccccccc"
+         id="path3391-2"
+         d="m 6,6 0,9 -2,0 0,-4 -2,0 0,-1 2,0 0,-4 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+    </g>
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="CONTACT"
+       transform="translate(375,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="COIL"
+       transform="translate(393,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="STEP"
+       transform="translate(411,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <use
+       style="display:inline"
+       x="0"
+       y="0"
+       xlink:href="#g3824"
+       id="JUMP"
+       transform="translate(429,-48)"
+       width="16"
+       height="16"
+       inkscape:label="#use3839" />
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible"
+       d="m 354.98855,-38.999999 0,1 3,-10e-7 0,-1 z"
+       id="path3309-2"
+       sodipodi:nodetypes="ccccc"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible"
+       d="m 366.98855,-39 0,1 -3,-10e-7 0,-1 z"
+       id="path3314-4"
+       sodipodi:nodetypes="ccccc"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+    <path
+       sodipodi:type="arc"
+       style="fill:none;stroke:#000000;stroke-width:0.84852809;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
+       id="path1984-0"
+       sodipodi:cx="7.25"
+       sodipodi:cy="16.5"
+       sodipodi:rx="2.25"
+       sodipodi:ry="3.6750026"
+       d="m 5.3391583,18.440291 a 2.25,3.6750026 0 0 1 -0.00478,-3.867976"
+       transform="matrix(3.333333,0,0,1.666667,341.35313,-65.999999)"
+       sodipodi:start="2.5853841"
+       sodipodi:end="3.6937671"
+       sodipodi:open="true" />
+    <text
+       sodipodi:linespacing="125%"
+       id="text3387-1-9"
+       y="-44"
+       x="357.98856"
+       style="font-size:2.80351019px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
+       xml:space="preserve"><tspan
+         y="-44"
+         x="357.98856"
+         id="tspan3389-3-6"
+         sodipodi:role="line">VAR</tspan></text>
+    <path
+       sodipodi:open="true"
+       sodipodi:end="3.6937671"
+       sodipodi:start="2.5853841"
+       transform="matrix(-3.333333,0,0,1.666667,380.75449,-65.999999)"
+       d="m 5.3391583,18.440291 a 2.25,3.6750026 0 0 1 -0.00478,-3.867976"
+       sodipodi:ry="3.6750026"
+       sodipodi:rx="2.25"
+       sodipodi:cy="16.5"
+       sodipodi:cx="7.25"
+       id="path3855"
+       style="fill:none;stroke:#000000;stroke-width:0.84852809;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
+       sodipodi:type="arc" />
+    <g
+       style="display:inline"
+       id="g3539-4"
+       inkscape:label="Calque 1"
+       transform="translate(364,-53)">
+      <rect
+         y="9"
+         x="10"
+         height="8"
+         width="9"
+         id="rect3541-3"
+         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" />
+      <path
+         inkscape:connector-curvature="0"
+         inkscape:export-ydpi="90"
+         inkscape:export-xdpi="90"
+         sodipodi:nodetypes="cccccccccccccccccccccc"
+         id="path3543-6"
+         d="m 19,9 -4,0 0,-2 -1,0 0,2 -4,0 0,8 4,0 0,2 1,0 0,-2 4,0 0,-4 2,0 0,-1 -2,0 z m -1,1 0,6 -7,0 0,-6 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible" />
+      <text
+         transform="scale(0.94258374,1.0609137)"
+         sodipodi:linespacing="125%"
+         id="text3545-3"
+         y="13.081616"
+         x="12.051801"
+         style="font-size:2.41527915px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           id="tspan1952-7"
+           y="13.081616"
+           x="12.051801"
+           sodipodi:role="line">STEP</tspan></text>
+    </g>
+    <g
+       style="display:inline"
+       id="g3445-5"
+       inkscape:label="Calque 1"
+       transform="translate(383,-51)">
+      <text
+         transform="scale(1.0096698,0.99042281)"
+         sodipodi:linespacing="125%"
+         id="text3447-6"
+         y="11.106368"
+         x="15.846765"
+         style="font-size:4.09374714px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+         xml:space="preserve"><tspan
+           y="11.106368"
+           x="15.846765"
+           id="tspan3449-5"
+           sodipodi:role="line">JP</tspan></text>
+      <path
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cccccccc"
+         id="path1941-7"
+         d="M 11,7 11,11 8,9 11.516466,17 15,9 12,11 12,7 z"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+    </g>
   </g>
 </svg>
--- 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
 #-------------------------------------------------------------------------------
 
--- 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", ())]
--- 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
--- 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):
     
--- /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()
--- 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)
+
--- 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):
     
--- 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
--- /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)
--- 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
--- 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])
+        
--- 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
--- 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])
             
         
--- 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)
         
--- 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: