editors/Viewer.py
changeset 1911 c1298e7ffe3a
parent 1619 163d3a883575
child 1644 976ce8c6195d
--- a/editors/Viewer.py	Sun Mar 05 00:38:25 2017 +0000
+++ b/editors/Viewer.py	Fri Mar 24 12:07:47 2017 +0000
@@ -1,26 +1,26 @@
 #!/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.
+# This file is part of Beremiz, a Integrated Development Environment for
+# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
 #
-#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
 #
-#See COPYING file for copyrights details.
+# 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 program 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
+# 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.
+# This program 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
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 import re
 import math
@@ -43,6 +43,7 @@
 SCROLL_ZONE = 10
 
 CURSORS = None
+SFC_Objects = (SFC_Step, SFC_ActionBlock, SFC_Transition, SFC_Divergence, SFC_Jump)
 
 def ResetCursors():
     global CURSORS
@@ -146,6 +147,9 @@
                                         specific_values.priority, id)
     return transition
 
+divergence_types = [SELECTION_DIVERGENCE,
+                    SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]
+
 def GetDivergenceCreationFunction(divergence_type):
     def divergenceCreationFunction(viewer, id, specific_values):
         return SFC_Divergence(viewer, divergence_type,
@@ -243,7 +247,7 @@
                 elif pou_type == "function" and values[1] != "function":
                     message = _("Function Blocks can't be used in Functions!")
                 elif self.ParentWindow.Controler.PouIsUsedBy(pou_name, values[0], self.ParentWindow.Debug):
-                    message = _("\"%s\" is already used by \"%s\"!")%(pou_name, values[0])
+                    message = _("\"{a1}\" is already used by \"{a2}\"!").format(a1 = pou_name, a2 = values[0])
                 else:
                     blockname = values[2]
                     if len(values) > 3:
@@ -282,7 +286,7 @@
                     if not location.startswith("%"):
                         dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow,
                               _("Select a variable class:"), _("Variable class"),
-                              ["Input", "Output", "Memory"],
+                              [_("Input"), _("Output"), _("Memory")],
                               wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
                         if dialog.ShowModal() == wx.ID_OK:
                             selected = dialog.GetSelection()
@@ -301,7 +305,7 @@
                     dlg = wx.TextEntryDialog(
                         self.ParentWindow.ParentWindow,
                         _("Confirm or change variable name"),
-                        'Variable Drop', var_name)
+                        _('Variable Drop'), var_name)
                     dlg.SetValue(var_name)
                     var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None
                     dlg.Destroy()
@@ -331,7 +335,7 @@
                     dlg = wx.TextEntryDialog(
                         self.ParentWindow.ParentWindow,
                         _("Confirm or change variable name"),
-                        'Variable Drop', var_name)
+                        _('Variable Drop'), var_name)
                     dlg.SetValue(var_name)
                     var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None
                     dlg.Destroy()
@@ -353,7 +357,7 @@
                 dlg = wx.TextEntryDialog(
                     self.ParentWindow.ParentWindow,
                     _("Confirm or change variable name"),
-                    'Variable Drop', var_name)
+                    _('Variable Drop'), var_name)
                 dlg.SetValue(var_name)
                 var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None
                 dlg.Destroy()
@@ -573,11 +577,11 @@
             [ID_CLEAR_EXEC_ORDER, ID_RESET_EXEC_ORDER] = [wx.NewId() for i in xrange(2)]
 
             # Create menu items
-            self.AddMenuItems(menu, [
-                 (ID_CLEAR_EXEC_ORDER, wx.ITEM_NORMAL, _(u'Clear Execution Order'), '', self.OnClearExecutionOrderMenu),
-                 (ID_RESET_EXEC_ORDER, wx.ITEM_NORMAL, _(u'Reset Execution Order'), '', self.OnResetExecutionOrderMenu)])
-
-            menu.AppendSeparator()
+            if self.CurrentLanguage == 'FBD':
+                self.AddMenuItems(menu, [
+                    (ID_CLEAR_EXEC_ORDER, wx.ITEM_NORMAL, _(u'Clear Execution Order'), '', self.OnClearExecutionOrderMenu),
+                    (ID_RESET_EXEC_ORDER, wx.ITEM_NORMAL, _(u'Reset Execution Order'), '', self.OnResetExecutionOrderMenu)])
+                menu.AppendSeparator()
 
             add_menu = wx.Menu(title='')
             self.AddAddMenuItems(add_menu)
@@ -600,7 +604,7 @@
     def _init_Editor(self, prnt):
         self.Editor = wx.ScrolledWindow(prnt, name="Viewer",
             pos=wx.Point(0, 0), size=wx.Size(0, 0),
-            style=wx.HSCROLL | wx.VSCROLL | wx.ALWAYS_SHOW_SB)
+            style=wx.HSCROLL | wx.VSCROLL)
         self.Editor.ParentWindow = self
 
     # Create a new Viewer
@@ -757,8 +761,8 @@
                     client_size = self.Editor.GetClientSize()
                     mouse_pos = wx.Point(client_size[0] / 2, client_size[1] / 2)
                     mouse_event = wx.MouseEvent(wx.EVT_MOUSEWHEEL.typeId)
-                    mouse_event.m_x = mouse_pos.x
-                    mouse_event.m_y = mouse_pos.y
+                    mouse_event.x = mouse_pos.x
+                    mouse_event.y = mouse_pos.y
                 else:
                     mouse_pos = mouse_event.GetPosition()
                 pos = mouse_event.GetLogicalPosition(dc)
@@ -883,6 +887,15 @@
             comments.sort(lambda x, y: cmp(x.GetId(), y.GetId()))
         return blocks + wires + comments
 
+    def GetContinuationByName(self, name):
+        blocks = []
+        for block in self.Blocks.itervalues():
+            if isinstance(block, FBD_Connector) and\
+               block.GetType() == CONTINUATION and\
+               block.GetName() == name:
+                blocks.append(block)
+        return blocks
+    
     def GetConnectorByName(self, name):
         for block in self.Blocks.itervalues():
             if isinstance(block, FBD_Connector) and\
@@ -1462,12 +1475,24 @@
         return None
 
     def FindBlockConnector(self, pos, direction = None, exclude = None):
+        result, error = self.FindBlockConnectorWithError(pos, direction, exclude)
+        return result
+
+    def FindBlockConnectorWithError(self, pos, direction = None, exclude = None):
+        error = False        
+        startblock = None
         for block in self.Blocks.itervalues():
-            result = block.TestConnector(pos, direction, exclude)
-            if result:
-                return result
-        return None
-
+            connector = block.TestConnector(pos, direction, exclude)
+            if connector:
+                if self.IsWire(self.SelectedElement):
+                    startblock = self.SelectedElement.StartConnected.GetParentBlock()
+                avail, error = connector.ConnectionAvailable(direction, exclude)
+                if not avail or not self.BlockCompatibility(startblock, block, direction):
+                    connector = None
+                    error = True
+                return connector, error
+        return None, error
+    
     def FindElementById(self, id):
         block = self.Blocks.get(id, None)
         if block is not None:
@@ -2048,47 +2073,7 @@
                     self.SelectedElement.HighlightPoint(pos)
                     self.RefreshBuffer()
                 elif connector is None or self.SelectedElement.GetDragging():
-                    start_connector = self.SelectedElement.GetStartConnected()
-                    start_direction = start_connector.GetDirection()
-
-                    items = []
-
-                    if self.CurrentLanguage == "SFC" and start_direction == SOUTH:
-                        items.extend([
-                            (_(u'Initial Step'), self.GetAddToWireMenuCallBack(self.AddNewStep, True)),
-                            (_(u'Step'), self.GetAddToWireMenuCallBack(self.AddNewStep, False)),
-                            (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, False)),
-                            (_(u'Divergence'), self.GetAddToWireMenuCallBack(self.AddNewDivergence)),
-                            (_(u'Jump'), self.GetAddToWireMenuCallBack(self.AddNewJump)),
-                        ])
-
-                    elif start_direction == EAST:
-
-                        if isinstance(start_connector.GetParentBlock(), SFC_Step):
-                            items.append(
-                                (_(u'Action Block'), self.GetAddToWireMenuCallBack(self.AddNewActionBlock))
-                            )
-                        else:
-                            items.extend([
-                                (_(u'Block'), self.GetAddToWireMenuCallBack(self.AddNewBlock)),
-                                (_(u'Variable'), self.GetAddToWireMenuCallBack(self.AddNewVariable, True)),
-                                (_(u'Connection'), self.GetAddToWireMenuCallBack(self.AddNewConnection)),
-                            ])
-
-                            if self.CurrentLanguage != "FBD":
-                                items.append(
-                                    (_(u'Contact'), self.GetAddToWireMenuCallBack(self.AddNewContact))
-                                )
-                            if self.CurrentLanguage == "LD":
-                                items.extend([
-                                    (_(u'Coil'), self.GetAddToWireMenuCallBack(self.AddNewCoil)),
-                                    (_(u'Power Rail'), self.GetAddToWireMenuCallBack(self.AddNewPowerRail)),
-                                ])
-                            if self.CurrentLanguage == "SFC":
-                                items.append(
-                                    (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, True))
-                                )
-
+                    items = self.GetPopupMenuItems()
                     if len(items) > 0:
                         if self.Editor.HasCapture():
                             self.Editor.ReleaseMouse()
@@ -2273,7 +2258,8 @@
                 self.rubberBand.OnMotion(event, dc, self.Scaling)
             elif not self.Debug and self.Mode == MODE_SELECTION and self.SelectedElement is not None:
                 if self.DrawingWire:
-                    connector = self.FindBlockConnector(pos, self.SelectedElement.GetConnectionDirection(), self.SelectedElement.EndConnected)
+                    connector, errorHighlight = self.FindBlockConnectorWithError(pos, self.SelectedElement.GetConnectionDirection(), self.SelectedElement.EndConnected)
+                    self.SelectedElement.ErrHighlight = errorHighlight;
                     if not connector or self.SelectedElement.EndConnected == None:
                         self.SelectedElement.ResetPoints()
                         movex, movey = self.SelectedElement.OnMotion(event, dc, self.Scaling)
@@ -2340,6 +2326,54 @@
                 self.Scroll(xstart + move_window.x, ystart + move_window.y)
                 self.RefreshScrollBars(move_window.x, move_window.y)
 
+    def BlockCompatibility(self, startblock=None, endblock=None, direction = None):
+        return True
+
+    def GetPopupMenuItems(self):
+        start_connector = self.SelectedElement.GetStartConnected()
+        start_direction = start_connector.GetDirection()
+        startblock = start_connector.GetParentBlock()
+        items = []
+        if isinstance(startblock, SFC_Objects):
+            startblockname = self.GetBlockName(startblock)
+            poss_div_types = []
+
+            SFC_WireMenu_Buttons = {
+                'SFC_Step': (_(u'Step'), self.GetAddToWireMenuCallBack(self.AddNewStep, False)),
+                'SFC_Jump': (_(u'Jump'), self.GetAddToWireMenuCallBack(self.AddNewJump)),
+                'SFC_Transition': (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, False)),
+                'SFC_ActionBlock': (_(u'Action Block'), self.GetAddToWireMenuCallBack(self.AddNewActionBlock))}
+
+            for endblock in self.SFC_StandardRules.get(startblockname):
+                if start_direction in endblock:
+                    if endblock[0] in divergence_types:
+                        poss_div_types.append(endblock[0])
+                    else:
+                        items.append(SFC_WireMenu_Buttons[endblock[0]])
+            if len(poss_div_types) > 0:
+                items.append((_(u'Divergence'), self.GetAddToWireMenuCallBack(self.AddNewDivergence,
+                                                                              poss_div_types)))
+        elif start_direction == EAST:
+                items.extend([
+                    (_(u'Block'), self.GetAddToWireMenuCallBack(self.AddNewBlock)),
+                    (_(u'Connection'), self.GetAddToWireMenuCallBack(self.AddNewConnection))])
+
+                if self.CurrentLanguage != "FBD":
+                    items.append((_(u'Contact'), self.GetAddToWireMenuCallBack(self.AddNewContact)))
+
+                if self.CurrentLanguage == "LD":
+                    items.extend([
+                        (_(u'Coil'), self.GetAddToWireMenuCallBack(self.AddNewCoil)),
+                        (_(u'Power Rail'), self.GetAddToWireMenuCallBack(self.AddNewPowerRail))])
+
+                if self.CurrentLanguage == "SFC":
+                    items.append(
+                        (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, True)))
+                else:
+                    items.append(
+                        (_(u'Variable'), self.GetAddToWireMenuCallBack(self.AddNewVariable, True)))
+        return items
+
 #-------------------------------------------------------------------------------
 #                          Keyboard event functions
 #-------------------------------------------------------------------------------
@@ -2398,7 +2432,7 @@
             if self.IsBlock(self.SelectedElement) or self.IsComment(self.SelectedElement):
                 block = self.CopyBlock(self.SelectedElement, wx.Point(*self.SelectedElement.GetPosition()))
                 event = wx.MouseEvent()
-                event.m_x, event.m_y = self.Editor.ScreenToClient(wx.GetMousePosition())
+                event.x, event.y = self.Editor.ScreenToClient(wx.GetMousePosition())
                 dc = self.GetLogicalDC()
                 self.SelectedElement.OnLeftUp(event, dc, self.Scaling)
                 self.SelectedElement.SetSelected(False)
@@ -2607,7 +2641,7 @@
                 "name": self.Controler.GenerateNewName(
                     self.TagName, None, "Step%d", 0),
                 "input": True,
-                "output": False,
+                "output": True,
                 "action":False}
         else:
             dialog = SFCStepDialog(self.ParentWindow, self.Controler, self.TagName, initial)
@@ -2651,8 +2685,8 @@
                 connector = transition.GetConnectors()["inputs"][0]
             self.AddNewElement(transition, bbox, wire, connector)
 
-    def AddNewDivergence(self, bbox, wire=None):
-        dialog = SFCDivergenceDialog(self.ParentWindow, self.Controler, self.TagName)
+    def AddNewDivergence(self, bbox, poss_div_types = None, wire=None):
+        dialog = SFCDivergenceDialog(self.ParentWindow, self.Controler, self.TagName, poss_div_types)
         dialog.SetPreviewFont(self.GetFont())
         dialog.SetMinElementSize((bbox.width, bbox.height))
         if dialog.ShowModal() == wx.ID_OK:
@@ -2868,7 +2902,20 @@
         if dialog.ShowModal() == wx.ID_OK:
             values = dialog.GetValues()
             rect = step.GetRedrawRect(1, 1)
-            step.SetName(values["name"])
+
+            new_name = values["name"]
+            if self.GetDrawingMode() == DRIVENDRAWING_MODE:
+                old_name = step.GetName().upper()
+                if new_name.upper() != old_name:
+                    for block in self.Blocks.itervalues():
+                        if isinstance(block, SFC_Jump):
+                            if old_name == block.GetTarget().upper():
+                                block.SetTarget(new_name)
+                                block.RefreshModel()
+                                rect = rect.Union(block.GetRedrawRect())
+                                block.Refresh(rect)
+            step.SetName(new_name)
+            
             if values["input"]:
                 step.AddInput()
             else:
@@ -2915,7 +2962,12 @@
         dialog = wx.SingleChoiceDialog(self.ParentWindow,
               _("Edit jump target"), _("Please choose a target"),
               choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
-        dialog.SetSelection(choices.index(jump.GetTarget()))
+        try:
+            indx = choices.index(jump.GetTarget())
+            dialog.SetSelection(indx)
+        except ValueError:
+            pass
+
         if dialog.ShowModal() == wx.ID_OK:
             value = dialog.GetStringSelection()
             rect = jump.GetRedrawRect(1, 1)
@@ -3216,6 +3268,17 @@
                 if element not in elements:
                     elements.append(element)
         step.Clean()
+
+        if self.GetDrawingMode() == DRIVENDRAWING_MODE:
+            name = step.GetName().upper()
+            remove_jumps = []
+            for block in self.Blocks.itervalues():
+                if isinstance(block, SFC_Jump):
+                    if name == block.GetTarget().upper():
+                        remove_jumps.append(block)
+            for jump in remove_jumps:
+                self.DeleteJump(jump)
+
         self.RemoveBlock(step)
         self.Controler.RemoveEditedElementInstance(self.TagName, step.GetId())
         for element in elements:
@@ -3399,22 +3462,16 @@
             self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT)
 
             self.SearchParams = search_params
-            criteria = {
-                "raw_pattern": search_params["find_pattern"],
-                "pattern": re.compile(search_params["find_pattern"]),
-                "case_sensitive": search_params["case_sensitive"],
-                "regular_expression": search_params["regular_expression"],
-                "filter": "all"}
-
             self.SearchResults = []
             blocks = []
-            for infos, start, end, text in self.Controler.SearchInPou(self.TagName, criteria, self.Debug):
-                if infos[1] in ["var_local", "var_input", "var_output", "var_inout"]:
-                    self.SearchResults.append((infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT))
-                else:
-                    block = self.Blocks.get(infos[2])
-                    if block is not None:
-                        blocks.append((block, (infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT)))
+            for infos, start, end, text in self.Controler.SearchInPou(self.TagName, search_params, self.Debug):
+                if (infos[0] == self.TagName or self.TagName.split("::")[0] in ['A', 'T']) and infos[1] is not 'name':
+                    if infos[1] in ["var_local", "var_input", "var_output", "var_inout"]:
+                        self.SearchResults.append((infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT))
+                    else:
+                        block = self.Blocks.get(infos[2])
+                        if block is not None:
+                            blocks.append((block, (infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT)))
             blocks.sort(sort_blocks)
             self.SearchResults.extend([infos for block, infos in blocks])
             self.CurrentFindHighlight = None