graphics/SFC_Objects.py
changeset 814 5743cbdff669
child 1176 f4b434672204
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import wx
       
    26 
       
    27 from GraphicCommons import *
       
    28 from plcopen.structures import *
       
    29 
       
    30 def GetWireSize(block):
       
    31     if isinstance(block, SFC_Step):
       
    32         return SFC_WIRE_MIN_SIZE + block.GetActionExtraLineNumber() * SFC_ACTION_MIN_SIZE[1]
       
    33     else:
       
    34         return SFC_WIRE_MIN_SIZE
       
    35 
       
    36 #-------------------------------------------------------------------------------
       
    37 #                         Sequencial Function Chart Step
       
    38 #-------------------------------------------------------------------------------
       
    39 
       
    40 """
       
    41 Class that implements the graphic representation of a step
       
    42 """
       
    43 
       
    44 class SFC_Step(Graphic_Element, DebugDataConsumer):
       
    45     
       
    46     # Create a new step
       
    47     def __init__(self, parent, name, initial = False, id = None):
       
    48         Graphic_Element.__init__(self, parent)
       
    49         DebugDataConsumer.__init__(self)
       
    50         self.SetName(name)
       
    51         self.Initial = initial
       
    52         self.Id = id
       
    53         self.Highlights = []
       
    54         self.Size = wx.Size(SFC_STEP_DEFAULT_SIZE[0], SFC_STEP_DEFAULT_SIZE[1])
       
    55         # Create an input and output connector
       
    56         if not self.Initial:
       
    57             self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH)
       
    58         else:
       
    59             self.Input = None
       
    60         self.Output = None
       
    61         self.Action = None
       
    62         self.PreviousValue = None
       
    63         self.PreviousSpreading = False
       
    64     
       
    65     def Flush(self):
       
    66         if self.Input is not None:
       
    67             self.Input.Flush()
       
    68             self.Input = None
       
    69         if self.Output is not None:
       
    70             self.Output.Flush()
       
    71             self.Output = None
       
    72         if self.Output is not None:
       
    73             self.Action.Flush()
       
    74             self.Action = None
       
    75     
       
    76     def SetForced(self, forced):
       
    77         if self.Forced != forced:
       
    78             self.Forced = forced
       
    79             if self.Visible:
       
    80                 self.Parent.ElementNeedRefresh(self)
       
    81     
       
    82     def SetValue(self, value):
       
    83         self.PreviousValue = self.Value
       
    84         self.Value = value
       
    85         if self.Value != self.PreviousValue:
       
    86             if self.Visible:
       
    87                 self.Parent.ElementNeedRefresh(self)
       
    88             self.SpreadCurrent()
       
    89     
       
    90     def SpreadCurrent(self):
       
    91         if self.Parent.Debug:
       
    92             spreading = self.Value
       
    93             if spreading and not self.PreviousSpreading:
       
    94                 if self.Output is not None:
       
    95                     self.Output.SpreadCurrent(True)
       
    96                 if self.Action is not None:
       
    97                     self.Action.SpreadCurrent(True)
       
    98             elif not spreading and self.PreviousSpreading:
       
    99                 if self.Output is not None:
       
   100                     self.Output.SpreadCurrent(False)
       
   101                 if self.Action is not None:
       
   102                     self.Action.SpreadCurrent(False)
       
   103             self.PreviousSpreading = spreading
       
   104     
       
   105     # Make a clone of this SFC_Step
       
   106     def Clone(self, parent, id = None, name = "Step", pos = None):
       
   107         step = SFC_Step(parent, name, self.Initial, id)
       
   108         step.SetSize(self.Size[0], self.Size[1])
       
   109         if pos is not None:
       
   110             step.SetPosition(pos.x, pos.y)
       
   111         else:
       
   112             step.SetPosition(self.Pos.x, self.Pos.y)
       
   113         if self.Input:
       
   114             step.Input = self.Input.Clone(step)
       
   115         if self.Output:
       
   116             step.Output = self.Output.Clone(step)
       
   117         if self.Action:
       
   118             step.Action = self.Action.Clone(step)
       
   119         return step
       
   120     
       
   121     def GetConnectorTranslation(self, element):
       
   122         connectors = {}
       
   123         if self.Input is not None:
       
   124             connectors[self.Input] = element.Input
       
   125         if self.Output is not None:
       
   126             connectors[self.Output] = element.Output
       
   127         if self.Action is not None:
       
   128             connectors[self.Action] = element.Action
       
   129         return connectors
       
   130     
       
   131     # Returns the RedrawRect
       
   132     def GetRedrawRect(self, movex = 0, movey = 0):
       
   133         rect = Graphic_Element.GetRedrawRect(self, movex, movey)
       
   134         if self.Input:
       
   135             rect = rect.Union(self.Input.GetRedrawRect(movex, movey))
       
   136         if self.Output:
       
   137             rect = rect.Union(self.Output.GetRedrawRect(movex, movey))
       
   138         if self.Action:
       
   139             rect = rect.Union(self.Action.GetRedrawRect(movex, movey))
       
   140         if movex != 0 or movey != 0:
       
   141             if self.Input and self.Input.IsConnected():
       
   142                 rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
       
   143             if self.Output and self.Output.IsConnected():
       
   144                 rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey))
       
   145             if self.Action and self.Action.IsConnected():
       
   146                 rect = rect.Union(self.Action.GetConnectedRedrawRect(movex, movey))
       
   147         return rect
       
   148     
       
   149     # Delete this step by calling the appropriate method
       
   150     def Delete(self):
       
   151         self.Parent.DeleteStep(self)
       
   152     
       
   153     # Unconnect input and output
       
   154     def Clean(self):
       
   155         if self.Input:
       
   156             self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   157         if self.Output:
       
   158             self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   159         if self.Action:
       
   160             self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   161     
       
   162     # Refresh the size of text for name
       
   163     def RefreshNameSize(self):
       
   164         self.NameSize = self.Parent.GetTextExtent(self.Name)
       
   165     
       
   166     # Add output connector to step
       
   167     def AddInput(self):
       
   168         if not self.Input:
       
   169             self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH)
       
   170             self.RefreshBoundingBox()
       
   171     
       
   172     # Remove output connector from step
       
   173     def RemoveInput(self):
       
   174         if self.Input:
       
   175             self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   176             self.Input = None
       
   177             self.RefreshBoundingBox()
       
   178     
       
   179     # Add output connector to step
       
   180     def AddOutput(self):
       
   181         if not self.Output:
       
   182             self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)
       
   183             self.RefreshBoundingBox()
       
   184     
       
   185     # Remove output connector from step
       
   186     def RemoveOutput(self):
       
   187         if self.Output:
       
   188             self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   189             self.Output = None
       
   190             self.RefreshBoundingBox()
       
   191     
       
   192     # Add action connector to step
       
   193     def AddAction(self):
       
   194         if not self.Action:
       
   195             self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone = True)
       
   196             self.RefreshBoundingBox()
       
   197     
       
   198     # Remove action connector from step
       
   199     def RemoveAction(self):
       
   200         if self.Action:
       
   201             self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   202             self.Action = None
       
   203             self.RefreshBoundingBox()
       
   204     
       
   205     # Refresh the step bounding box
       
   206     def RefreshBoundingBox(self):
       
   207         # Calculate the bounding box size
       
   208         if self.Action:
       
   209             bbx_width = self.Size[0] + CONNECTOR_SIZE
       
   210         else:
       
   211             bbx_width = self.Size[0]
       
   212         if self.Initial:
       
   213             bbx_y = self.Pos.y
       
   214             bbx_height = self.Size[1]
       
   215             if self.Output:
       
   216                 bbx_height += CONNECTOR_SIZE
       
   217         else:
       
   218             bbx_y = self.Pos.y - CONNECTOR_SIZE
       
   219             bbx_height = self.Size[1] + CONNECTOR_SIZE
       
   220             if self.Output:
       
   221                 bbx_height += CONNECTOR_SIZE
       
   222         #self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1)
       
   223         self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
       
   224         
       
   225     # Refresh the positions of the step connectors
       
   226     def RefreshConnectors(self):
       
   227         scaling = self.Parent.GetScaling()
       
   228         horizontal_pos = self.Size[0] / 2
       
   229         vertical_pos = self.Size[1] / 2
       
   230         if scaling is not None:
       
   231             horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x
       
   232             vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y
       
   233         # Update input position if it exists
       
   234         if self.Input:
       
   235             self.Input.SetPosition(wx.Point(horizontal_pos, 0))
       
   236         # Update output position
       
   237         if self.Output:
       
   238             self.Output.SetPosition(wx.Point(horizontal_pos, self.Size[1]))
       
   239         # Update action position if it exists
       
   240         if self.Action:
       
   241             self.Action.SetPosition(wx.Point(self.Size[0], vertical_pos))
       
   242         self.RefreshConnected()
       
   243     
       
   244     # Refresh the position of wires connected to step
       
   245     def RefreshConnected(self, exclude = []):
       
   246         if self.Input:
       
   247             self.Input.MoveConnected(exclude)
       
   248         if self.Output:
       
   249             self.Output.MoveConnected(exclude)
       
   250         if self.Action:
       
   251             self.Action.MoveConnected(exclude)
       
   252     
       
   253     # Returns the step connector that starts with the point given if it exists 
       
   254     def GetConnector(self, position, name = None):
       
   255         # if a name is given
       
   256         if name is not None:
       
   257             # Test input, output and action connector if they exists
       
   258             #if self.Input and name == self.Input.GetName():
       
   259             #    return self.Input
       
   260             if self.Output and name == self.Output.GetName():
       
   261                 return self.Output
       
   262             if self.Action and name == self.Action.GetName():
       
   263                 return self.Action
       
   264         connectors = []
       
   265         # Test input connector if it exists
       
   266         if self.Input:
       
   267             connectors.append(self.Input)
       
   268         # Test output connector if it exists
       
   269         if self.Output:
       
   270             connectors.append(self.Output)
       
   271         # Test action connector if it exists
       
   272         if self.Action:
       
   273             connectors.append(self.Action)
       
   274         return self.FindNearestConnector(position, connectors)
       
   275     
       
   276     # Returns action step connector 
       
   277     def GetActionConnector(self):
       
   278         return self.Action
       
   279         
       
   280     # Returns input and output step connectors 
       
   281     def GetConnectors(self):
       
   282         connectors = {"inputs": [], "outputs": []}
       
   283         if self.Input:
       
   284             connectors["inputs"].append(self.Input)
       
   285         if self.Output:
       
   286             connectors["outputs"].append(self.Output)
       
   287         return connectors
       
   288     
       
   289     # Test if point given is on step input or output connector
       
   290     def TestConnector(self, pt, direction = None, exclude=True):
       
   291         # Test input connector if it exists
       
   292         if self.Input and self.Input.TestPoint(pt, direction, exclude):
       
   293             return self.Input
       
   294         # Test output connector
       
   295         if self.Output and self.Output.TestPoint(pt, direction, exclude):
       
   296             return self.Output
       
   297         # Test action connector
       
   298         if self.Action and self.Action.TestPoint(pt, direction, exclude):
       
   299             return self.Action
       
   300         return None
       
   301 
       
   302     # Changes the step name
       
   303     def SetName(self, name):
       
   304         self.Name = name
       
   305         self.RefreshNameSize()
       
   306 
       
   307     # Returns the step name
       
   308     def GetName(self):
       
   309         return self.Name
       
   310 
       
   311     # Returns the step initial property
       
   312     def GetInitial(self):
       
   313         return self.Initial
       
   314     
       
   315     # Returns the connector connected to input
       
   316     def GetPreviousConnector(self):
       
   317         if self.Input:
       
   318             wires = self.Input.GetWires()
       
   319             if len(wires) == 1:
       
   320                 return wires[0][0].GetOtherConnected(self.Input)
       
   321         return None
       
   322     
       
   323     # Returns the connector connected to output
       
   324     def GetNextConnector(self):
       
   325         if self.Output:
       
   326             wires = self.Output.GetWires()
       
   327             if len(wires) == 1:
       
   328                 return wires[0][0].GetOtherConnected(self.Output)
       
   329         return None
       
   330     
       
   331     # Returns the connector connected to action
       
   332     def GetActionConnected(self):
       
   333         if self.Action:
       
   334             wires = self.Action.GetWires()
       
   335             if len(wires) == 1:
       
   336                 return wires[0][0].GetOtherConnected(self.Action)
       
   337         return None
       
   338     
       
   339     # Returns the number of action line
       
   340     def GetActionExtraLineNumber(self):
       
   341         if self.Action:
       
   342             wires = self.Action.GetWires()
       
   343             if len(wires) != 1:
       
   344                 return 0
       
   345             action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock()
       
   346             return max(0, action_block.GetLineNumber() - 1)
       
   347         return 0
       
   348     
       
   349     # Returns the step minimum size
       
   350     def GetMinSize(self):
       
   351         text_width, text_height = self.Parent.GetTextExtent(self.Name)
       
   352         if self.Initial:
       
   353             return text_width + 14, text_height + 14
       
   354         else:
       
   355             return text_width + 10, text_height + 10
       
   356     
       
   357     # Updates the step size
       
   358     def UpdateSize(self, width, height):
       
   359         diffx = self.Size.GetWidth() / 2 - width / 2
       
   360         diffy = height - self.Size.GetHeight()
       
   361         self.Move(diffx, 0)
       
   362         Graphic_Element.SetSize(self, width, height)
       
   363         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
   364             self.RefreshConnected()
       
   365         else:
       
   366             self.RefreshOutputPosition((0, diffy))
       
   367     
       
   368     # Align input element with this step
       
   369     def RefreshInputPosition(self):
       
   370         if self.Input:
       
   371             current_pos = self.Input.GetPosition(False)
       
   372             input = self.GetPreviousConnector()
       
   373             if input:
       
   374                 input_pos = input.GetPosition(False)
       
   375                 diffx = current_pos.x - input_pos.x
       
   376                 input_block = input.GetParentBlock()
       
   377                 if isinstance(input_block, SFC_Divergence):
       
   378                     input_block.MoveConnector(input, diffx)
       
   379                 else:
       
   380                     if isinstance(input_block, SFC_Step):
       
   381                         input_block.MoveActionBlock((diffx, 0))
       
   382                     input_block.Move(diffx, 0)
       
   383                     input_block.RefreshInputPosition()
       
   384     
       
   385     # Align output element with this step
       
   386     def RefreshOutputPosition(self, move = None):
       
   387         if self.Output:
       
   388             wires = self.Output.GetWires()
       
   389             if len(wires) != 1:
       
   390                 return
       
   391             current_pos = self.Output.GetPosition(False)
       
   392             output = wires[0][0].GetOtherConnected(self.Output)
       
   393             output_pos = output.GetPosition(False)
       
   394             diffx = current_pos.x - output_pos.x
       
   395             output_block = output.GetParentBlock()
       
   396             wire_size = SFC_WIRE_MIN_SIZE + self.GetActionExtraLineNumber() * SFC_ACTION_MIN_SIZE[1]
       
   397             diffy = wire_size - output_pos.y + current_pos.y
       
   398             if diffy != 0:
       
   399                 if isinstance(output_block, SFC_Step):
       
   400                     output_block.MoveActionBlock((diffx, diffy))
       
   401                 wires[0][0].SetPoints([wx.Point(current_pos.x, current_pos.y + wire_size),
       
   402                     wx.Point(current_pos.x, current_pos.y)])
       
   403                 if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0:
       
   404                     output_block.Move(diffx, diffy, self.Parent.Wires)
       
   405                     output_block.RefreshOutputPosition((diffx, diffy))
       
   406                 else:
       
   407                     output_block.RefreshPosition()
       
   408             elif move:
       
   409                 if isinstance(output_block, SFC_Step):
       
   410                     output_block.MoveActionBlock(move)
       
   411                 wires[0][0].Move(move[0], move[1], True)
       
   412                 if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0:
       
   413                     output_block.Move(move[0], move[1], self.Parent.Wires)
       
   414                     output_block.RefreshOutputPosition(move)
       
   415                 else:
       
   416                     output_block.RefreshPosition()
       
   417             elif isinstance(output_block, SFC_Divergence):
       
   418                 output_block.MoveConnector(output, diffx)
       
   419             else:
       
   420                 if isinstance(output_block, SFC_Step):
       
   421                     output_block.MoveActionBlock((diffx, 0))
       
   422                 output_block.Move(diffx, 0)
       
   423                 output_block.RefreshOutputPosition()
       
   424     
       
   425     # Refresh action element with this step
       
   426     def MoveActionBlock(self, move):
       
   427         if self.Action:
       
   428             wires = self.Action.GetWires()
       
   429             if len(wires) != 1:
       
   430                 return
       
   431             action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock()
       
   432             action_block.Move(move[0], move[1], self.Parent.Wires)
       
   433             wires[0][0].Move(move[0], move[1], True)
       
   434     
       
   435     # Resize the divergence from position and size given
       
   436     def Resize(self, x, y, width, height):
       
   437         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
   438             self.UpdateSize(width, height)
       
   439         else:
       
   440             Graphic_Element.Resize(self, x, y, width, height)
       
   441     
       
   442     # Method called when a LeftDClick event have been generated
       
   443     def OnLeftDClick(self, event, dc, scaling):
       
   444         # Edit the step properties
       
   445         self.Parent.EditStepContent(self)
       
   446     
       
   447     # Method called when a RightUp event have been generated
       
   448     def OnRightUp(self, event, dc, scaling):
       
   449         # Popup the menu with special items for a step
       
   450         self.Parent.PopupDefaultMenu()
       
   451     
       
   452     # Refreshes the step state according to move defined and handle selected
       
   453     def ProcessDragging(self, movex, movey, event, scaling):
       
   454         handle_type, handle = self.Handle
       
   455         if handle_type == HANDLE_MOVE:
       
   456             movex = max(-self.BoundingBox.x, movex)
       
   457             movey = max(-self.BoundingBox.y, movey)
       
   458             if scaling is not None:
       
   459                 movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x
       
   460                 movey = round(float(self.Pos.y + movey) / float(scaling[1])) * scaling[1] - self.Pos.y
       
   461             action_block = None
       
   462             if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
   463                 self.Move(movex, movey)
       
   464                 self.RefreshConnected()
       
   465                 return movex, movey
       
   466             elif self.Initial:
       
   467                 self.MoveActionBlock((movex, movey))
       
   468                 self.Move(movex, movey, self.Parent.Wires)
       
   469                 self.RefreshOutputPosition((movex, movey))
       
   470                 return movex, movey
       
   471             else:
       
   472                 self.MoveActionBlock((movex, 0))
       
   473                 self.Move(movex, 0)
       
   474                 self.RefreshInputPosition()
       
   475                 self.RefreshOutputPosition()
       
   476                 return movex, 0
       
   477         else:
       
   478             return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
       
   479     
       
   480     # Refresh input element model
       
   481     def RefreshInputModel(self):
       
   482         if self.Input:
       
   483             input = self.GetPreviousConnector()
       
   484             if input:                
       
   485                 input_block = input.GetParentBlock()
       
   486                 input_block.RefreshModel(False)
       
   487                 if not isinstance(input_block, SFC_Divergence):
       
   488                     input_block.RefreshInputModel()
       
   489     
       
   490     # Refresh output element model
       
   491     def RefreshOutputModel(self, move=False):
       
   492         if self.Output:
       
   493             output = self.GetNextConnector()
       
   494             if output:
       
   495                 output_block = output.GetParentBlock()
       
   496                 output_block.RefreshModel(False)
       
   497                 if not isinstance(output_block, SFC_Divergence) or move:
       
   498                     output_block.RefreshOutputModel(move)
       
   499     
       
   500     # Refreshes the step model
       
   501     def RefreshModel(self, move=True):
       
   502         self.Parent.RefreshStepModel(self)
       
   503         if self.Action:
       
   504             action = self.GetActionConnected()
       
   505             if action:
       
   506                 action_block = action.GetParentBlock()
       
   507                 action_block.RefreshModel(False)
       
   508         # If step has moved, refresh the model of wires connected to output
       
   509         if move:
       
   510             if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
   511                 self.RefreshInputModel()
       
   512                 self.RefreshOutputModel(self.Initial)
       
   513             elif self.Output:
       
   514                 self.Output.RefreshWires()
       
   515     
       
   516     # Adds an highlight to the connection
       
   517     def AddHighlight(self, infos, start, end, highlight_type):
       
   518         if infos[0] == "name" and start[0] == 0 and end[0] == 0:
       
   519             AddHighlight(self.Highlights, (start, end, highlight_type))
       
   520     
       
   521     # Removes an highlight from the connection
       
   522     def RemoveHighlight(self, infos, start, end, highlight_type):
       
   523         if infos[0] == "name":
       
   524             RemoveHighlight(self.Highlights, (start, end, highlight_type))
       
   525     
       
   526     # Removes all the highlights of one particular type from the connection
       
   527     def ClearHighlight(self, highlight_type=None):
       
   528         ClearHighlights(self.Highlights, highlight_type)
       
   529     
       
   530     # Draws step
       
   531     def Draw(self, dc):
       
   532         Graphic_Element.Draw(self, dc)
       
   533         if self.Value:
       
   534             if self.Forced:
       
   535                 dc.SetPen(MiterPen(wx.CYAN))
       
   536             else:
       
   537                 dc.SetPen(MiterPen(wx.GREEN))
       
   538         elif self.Forced:
       
   539             dc.SetPen(MiterPen(wx.BLUE))
       
   540         else:
       
   541             dc.SetPen(MiterPen(wx.BLACK))
       
   542         dc.SetBrush(wx.WHITE_BRUSH)
       
   543         
       
   544         if getattr(dc, "printing", False):
       
   545             name_size = dc.GetTextExtent(self.Name)
       
   546         else:
       
   547             name_size = self.NameSize
       
   548         
       
   549         # Draw two rectangles for representing the step
       
   550         dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
       
   551         if self.Initial:
       
   552             dc.DrawRectangle(self.Pos.x + 2, self.Pos.y + 2, self.Size[0] - 3, self.Size[1] - 3)
       
   553         # Draw step name
       
   554         name_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2,
       
   555                     self.Pos.y + (self.Size[1] - name_size[1]) / 2)
       
   556         dc.DrawText(self.Name, name_pos[0], name_pos[1])
       
   557         # Draw input and output connectors
       
   558         if self.Input:
       
   559             self.Input.Draw(dc)
       
   560         if self.Output:
       
   561             self.Output.Draw(dc)
       
   562         if self.Action:
       
   563             self.Action.Draw(dc)
       
   564         
       
   565         if not getattr(dc, "printing", False):
       
   566             DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1])
       
   567         
       
   568 
       
   569 #-------------------------------------------------------------------------------
       
   570 #                       Sequencial Function Chart Transition
       
   571 #-------------------------------------------------------------------------------
       
   572 
       
   573 """
       
   574 Class that implements the graphic representation of a transition
       
   575 """
       
   576 
       
   577 class SFC_Transition(Graphic_Element, DebugDataConsumer):
       
   578     
       
   579     # Create a new transition
       
   580     def __init__(self, parent, type = "reference", condition = None, priority = 0, id = None):
       
   581         Graphic_Element.__init__(self, parent)
       
   582         DebugDataConsumer.__init__(self)
       
   583         self.Type = None
       
   584         self.Id = id
       
   585         self.Priority = 0
       
   586         self.Size = wx.Size(SFC_TRANSITION_SIZE[0], SFC_TRANSITION_SIZE[1])
       
   587         # Create an input and output connector
       
   588         self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)
       
   589         self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)
       
   590         self.SetType(type, condition)
       
   591         self.SetPriority(priority)
       
   592         self.Highlights = {}
       
   593         self.PreviousValue = None
       
   594         self.PreviousSpreading = False
       
   595     
       
   596     def Flush(self):
       
   597         if self.Input is not None:
       
   598             self.Input.Flush()
       
   599             self.Input = None
       
   600         if self.Output is not None:
       
   601             self.Output.Flush()
       
   602             self.Output = None
       
   603         if self.Type == "connection" and self.Condition is not None:
       
   604             self.Condition.Flush()
       
   605             self.Condition = None
       
   606     
       
   607     def SetForced(self, forced):
       
   608         if self.Forced != forced:
       
   609             self.Forced = forced
       
   610             if self.Visible:
       
   611                 self.Parent.ElementNeedRefresh(self)
       
   612         
       
   613     def SetValue(self, value):
       
   614         self.PreviousValue = self.Value
       
   615         self.Value = value
       
   616         if self.Value != self.PreviousValue:
       
   617             if self.Visible:
       
   618                 self.Parent.ElementNeedRefresh(self)
       
   619             self.SpreadCurrent()
       
   620     
       
   621     def SpreadCurrent(self):
       
   622         if self.Parent.Debug:
       
   623             if self.Value is None:
       
   624                 self.Value = False
       
   625             spreading = self.Input.ReceivingCurrent() & self.Value
       
   626             if spreading and not self.PreviousSpreading:
       
   627                 self.Output.SpreadCurrent(True)
       
   628             elif not spreading and self.PreviousSpreading:
       
   629                 self.Output.SpreadCurrent(False)
       
   630             self.PreviousSpreading = spreading
       
   631     
       
   632     # Make a clone of this SFC_Transition
       
   633     def Clone(self, parent, id = None, pos = None):
       
   634         transition = SFC_Transition(parent, self.Type, self.Condition, self.Priority, id)
       
   635         transition.SetSize(self.Size[0], self.Size[1])
       
   636         if pos is not None:
       
   637             transition.SetPosition(pos.x, pos.y)
       
   638         else:
       
   639             transition.SetPosition(self.Pos.x, self.Pos.y)
       
   640         transition.Input = self.Input.Clone(transition)
       
   641         transition.Output = self.Output.Clone(transition)
       
   642         if self.Type == "connection":
       
   643             transition.Condition = self.Condition.Clone(transition)
       
   644         return transition
       
   645     
       
   646     def GetConnectorTranslation(self, element):
       
   647         connectors = {self.Input : element.Input, self.Output : element.Output}
       
   648         if self.Type == "connection" and self.Condition is not None:
       
   649             connectors[self.Condition] = element.Condition
       
   650         return connectors
       
   651     
       
   652     # Returns the RedrawRect
       
   653     def GetRedrawRect(self, movex = 0, movey = 0):
       
   654         rect = Graphic_Element.GetRedrawRect(self, movex, movey)
       
   655         rect = rect.Union(self.Input.GetRedrawRect(movex, movey))
       
   656         rect = rect.Union(self.Output.GetRedrawRect(movex, movey))
       
   657         if movex != 0 or movey != 0:
       
   658             if self.Input.IsConnected():
       
   659                 rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
       
   660             if self.Output.IsConnected():
       
   661                 rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey))
       
   662             if self.Type == "connection" and self.Condition.IsConnected():
       
   663                 rect = rect.Union(self.Condition.GetConnectedRedrawRect(movex, movey))
       
   664         return rect
       
   665     
       
   666     # Forbids to change the transition size
       
   667     def SetSize(self, width, height):
       
   668         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
   669             Graphic_Element.SetSize(self, width, height)
       
   670     
       
   671     # Forbids to resize the transition
       
   672     def Resize(self, x, y, width, height):
       
   673         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
   674             Graphic_Element.Resize(self, x, y, width, height)
       
   675     
       
   676     # Refresh the size of text for name
       
   677     def RefreshConditionSize(self):
       
   678         if self.Type != "connection":
       
   679             if self.Condition != "":
       
   680                 self.ConditionSize = self.Parent.GetTextExtent(self.Condition)
       
   681             else:
       
   682                 self.ConditionSize = self.Parent.GetTextExtent("Transition")
       
   683     
       
   684     # Refresh the size of text for name
       
   685     def RefreshPrioritySize(self):
       
   686         if self.Priority != "":
       
   687             self.PrioritySize = self.Parent.GetTextExtent(str(self.Priority))
       
   688         else:
       
   689             self.PrioritySize = None
       
   690 
       
   691     # Delete this transition by calling the appropriate method
       
   692     def Delete(self):
       
   693         self.Parent.DeleteTransition(self)
       
   694     
       
   695     # Unconnect input and output
       
   696     def Clean(self):
       
   697         self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   698         self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   699         if self.Type == "connection":
       
   700             self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
   701     
       
   702     # Returns if the point given is in the bounding box
       
   703     def HitTest(self, pt, connectors=True):
       
   704         if self.Type != "connection":
       
   705             # Calculate the bounding box of the condition outside the transition
       
   706             text_width, text_height = self.ConditionSize
       
   707             text_bbx = wx.Rect(self.Pos.x + self.Size[0] + 5,
       
   708                                self.Pos.y + (self.Size[1] - text_height) / 2,
       
   709                                text_width,
       
   710                                text_height)
       
   711             test_text = text_bbx.InsideXY(pt.x, pt.y)
       
   712         else:
       
   713             test_text = False
       
   714         return test_text or Graphic_Element.HitTest(self, pt, connectors)
       
   715     
       
   716     # Refresh the transition bounding box
       
   717     def RefreshBoundingBox(self):
       
   718         bbx_x, bbx_y, bbx_width, bbx_height = self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]
       
   719         if self.Priority != 0:
       
   720             bbx_y = self.Pos.y - self.PrioritySize[1] - 2
       
   721             bbx_width = max(self.Size[0], self.PrioritySize[0])
       
   722             bbx_height = self.Size[1] + self.PrioritySize[1] + 2
       
   723         if self.Type == "connection":
       
   724             bbx_x = self.Pos.x - CONNECTOR_SIZE
       
   725             bbx_width = bbx_width + CONNECTOR_SIZE
       
   726         else:
       
   727             text_width, text_height = self.ConditionSize
       
   728             # Calculate the bounding box size
       
   729             bbx_width = max(bbx_width, self.Size[0] + 5 + text_width)
       
   730             bbx_y = min(bbx_y, self.Pos.y - max(0, (text_height - self.Size[1]) / 2))
       
   731             bbx_height = max(bbx_height, self.Pos.y - bbx_y + (self.Size[1] + text_height) / 2)
       
   732         self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
       
   733         
       
   734     # Returns the connector connected to input
       
   735     def GetPreviousConnector(self):
       
   736         wires = self.Input.GetWires()
       
   737         if len(wires) == 1:
       
   738             return wires[0][0].GetOtherConnected(self.Input)
       
   739         return None
       
   740     
       
   741     # Returns the connector connected to output
       
   742     def GetNextConnector(self):
       
   743         wires = self.Output.GetWires()
       
   744         if len(wires) == 1:
       
   745             return wires[0][0].GetOtherConnected(self.Output)
       
   746         return None
       
   747     
       
   748     # Refresh the positions of the transition connectors
       
   749     def RefreshConnectors(self):
       
   750         scaling = self.Parent.GetScaling()
       
   751         horizontal_pos = self.Size[0] / 2
       
   752         vertical_pos = self.Size[1] / 2
       
   753         if scaling is not None:
       
   754             horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x
       
   755             vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y
       
   756         # Update input position
       
   757         self.Input.SetPosition(wx.Point(horizontal_pos, 0))
       
   758         # Update output position
       
   759         self.Output.SetPosition(wx.Point(horizontal_pos, self.Size[1]))
       
   760         if self.Type == "connection":
       
   761             self.Condition.SetPosition(wx.Point(0, vertical_pos))
       
   762         self.RefreshConnected()
       
   763     
       
   764     # Refresh the position of the wires connected to transition
       
   765     def RefreshConnected(self, exclude = []):
       
   766         self.Input.MoveConnected(exclude)
       
   767         self.Output.MoveConnected(exclude)
       
   768         if self.Type == "connection":
       
   769             self.Condition.MoveConnected(exclude)
       
   770     
       
   771     # Returns the transition connector that starts with the point given if it exists 
       
   772     def GetConnector(self, position, name = None):
       
   773         # if a name is given
       
   774         if name is not None:
       
   775             # Test input and output connector
       
   776             #if name == self.Input.GetName():
       
   777             #    return self.Input
       
   778             if name == self.Output.GetName():
       
   779                 return self.Output
       
   780             if self.Type == "connection" and name == self.Condition.GetName():
       
   781                 return self.Condition
       
   782         connectors = [self.Input, self.Output]
       
   783         if self.Type == "connection":
       
   784             connectors.append(self.Condition)
       
   785         return self.FindNearestConnector(position, connectors)
       
   786     
       
   787     # Returns the transition condition connector
       
   788     def GetConditionConnector(self):
       
   789         if self.Type == "connection":
       
   790             return self.Condition
       
   791         return None
       
   792         
       
   793     # Returns input and output transition connectors
       
   794     def GetConnectors(self):
       
   795         return {"inputs": [self.Input], "outputs": [self.Output]}
       
   796         
       
   797     # Test if point given is on transition input or output connector
       
   798     def TestConnector(self, pt, direction = None, exclude=True):
       
   799         # Test input connector
       
   800         if self.Input.TestPoint(pt, direction, exclude):
       
   801             return self.Input
       
   802         # Test output connector
       
   803         if self.Output.TestPoint(pt, direction, exclude):
       
   804             return self.Output
       
   805         # Test condition connector
       
   806         if self.Type == "connection" and self.Condition.TestPoint(pt, direction, exclude):
       
   807             return self.Condition
       
   808         return None
       
   809 
       
   810     # Changes the transition type
       
   811     def SetType(self, type, condition = None):
       
   812         if self.Type != type:
       
   813             if self.Type == "connection":
       
   814                self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) 
       
   815             self.Type = type
       
   816             if type == "connection":
       
   817                 self.Condition = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2), WEST)
       
   818             else:
       
   819                 if condition == None:
       
   820                     condition = ""
       
   821                 self.Condition = condition
       
   822                 self.RefreshConditionSize()
       
   823         elif self.Type != "connection":
       
   824             if condition == None:
       
   825                 condition = ""
       
   826             self.Condition = condition
       
   827             self.RefreshConditionSize()
       
   828         self.RefreshBoundingBox()
       
   829         
       
   830     # Returns the transition type
       
   831     def GetType(self):
       
   832         return self.Type
       
   833 
       
   834     # Changes the transition priority
       
   835     def SetPriority(self, priority):
       
   836         self.Priority = priority
       
   837         self.RefreshPrioritySize()
       
   838         self.RefreshBoundingBox()
       
   839         
       
   840     # Returns the transition type
       
   841     def GetPriority(self):
       
   842         return self.Priority
       
   843 
       
   844     # Returns the transition condition
       
   845     def GetCondition(self):
       
   846         if self.Type != "connection":
       
   847             return self.Condition
       
   848         return None
       
   849         
       
   850     # Returns the transition minimum size
       
   851     def GetMinSize(self):
       
   852         return SFC_TRANSITION_SIZE
       
   853     
       
   854     # Align input element with this step
       
   855     def RefreshInputPosition(self):
       
   856         wires = self.Input.GetWires()
       
   857         current_pos = self.Input.GetPosition(False)
       
   858         input = self.GetPreviousConnector()
       
   859         if input:
       
   860             input_pos = input.GetPosition(False)
       
   861             diffx = current_pos.x - input_pos.x
       
   862             input_block = input.GetParentBlock()
       
   863             if isinstance(input_block, SFC_Divergence):
       
   864                 input_block.MoveConnector(input, diffx)
       
   865             else:
       
   866                 if isinstance(input_block, SFC_Step):
       
   867                     input_block.MoveActionBlock((diffx, 0))
       
   868                 input_block.Move(diffx, 0)
       
   869                 input_block.RefreshInputPosition()
       
   870     
       
   871     # Align output element with this step
       
   872     def RefreshOutputPosition(self, move = None):
       
   873         wires = self.Output.GetWires()
       
   874         if len(wires) != 1:
       
   875             return
       
   876         current_pos = self.Output.GetPosition(False)
       
   877         output = wires[0][0].GetOtherConnected(self.Output)
       
   878         output_pos = output.GetPosition(False)
       
   879         diffx = current_pos.x - output_pos.x
       
   880         output_block = output.GetParentBlock()
       
   881         if move:
       
   882             if isinstance(output_block, SFC_Step):
       
   883                 output_block.MoveActionBlock(move)
       
   884             wires[0][0].Move(move[0], move[1], True)
       
   885             if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0:
       
   886                 output_block.Move(move[0], move[1], self.Parent.Wires)
       
   887                 output_block.RefreshOutputPosition(move)
       
   888             else:
       
   889                 output_block.RefreshPosition()
       
   890         elif isinstance(output_block, SFC_Divergence):
       
   891             output_block.MoveConnector(output, diffx)
       
   892         else:
       
   893             if isinstance(output_block, SFC_Step):
       
   894                 output_block.MoveActionBlock((diffx, 0))
       
   895             output_block.Move(diffx, 0)
       
   896             output_block.RefreshOutputPosition()
       
   897 
       
   898     # Method called when a LeftDClick event have been generated
       
   899     def OnLeftDClick(self, event, dc, scaling):
       
   900         # Edit the transition properties
       
   901         self.Parent.EditTransitionContent(self)
       
   902     
       
   903     # Method called when a RightUp event have been generated
       
   904     def OnRightUp(self, event, dc, scaling):
       
   905         # Popup the menu with special items for a step
       
   906         self.Parent.PopupDefaultMenu()
       
   907     
       
   908     # Refreshes the transition state according to move defined and handle selected
       
   909     def ProcessDragging(self, movex, movey, event, scaling):
       
   910         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
   911             movex = max(-self.BoundingBox.x, movex)
       
   912             if scaling is not None:
       
   913                 movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x
       
   914             self.Move(movex, 0)
       
   915             self.RefreshInputPosition()
       
   916             self.RefreshOutputPosition()
       
   917             return movex, 0
       
   918         else:
       
   919             return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2, height_fac = 2)
       
   920     
       
   921     # Refresh input element model
       
   922     def RefreshInputModel(self):
       
   923         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
   924             input = self.GetPreviousConnector()
       
   925             if input:
       
   926                 input_block = input.GetParentBlock()
       
   927                 input_block.RefreshModel(False)
       
   928                 if not isinstance(input_block, SFC_Divergence):
       
   929                     input_block.RefreshInputModel()
       
   930     
       
   931     # Refresh output element model
       
   932     def RefreshOutputModel(self, move=False):
       
   933         output = self.GetNextConnector()
       
   934         if output:
       
   935             output_block = output.GetParentBlock()
       
   936             output_block.RefreshModel(False)
       
   937             if not isinstance(output_block, SFC_Divergence) or move:
       
   938                 output_block.RefreshOutputModel(move)
       
   939     
       
   940     # Refreshes the transition model
       
   941     def RefreshModel(self, move=True):
       
   942         self.Parent.RefreshTransitionModel(self)
       
   943         # If transition has moved, refresh the model of wires connected to output
       
   944         if move:
       
   945             if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
   946                 self.RefreshInputModel()
       
   947                 self.RefreshOutputModel()
       
   948             else:
       
   949                 self.Output.RefreshWires()
       
   950     
       
   951     # Adds an highlight to the block
       
   952     def AddHighlight(self, infos, start, end ,highlight_type):
       
   953         if infos[0] in ["reference", "inline", "priority"] and start[0] == 0 and end[0] == 0:
       
   954             highlights = self.Highlights.setdefault(infos[0], [])
       
   955             AddHighlight(highlights, (start, end, highlight_type))
       
   956     
       
   957     # Removes an highlight from the block
       
   958     def RemoveHighlight(self, infos, start, end, highlight_type):
       
   959         if infos[0] in ["reference", "inline", "priority"]:
       
   960             highlights = self.Highlights.get(infos[0], [])
       
   961             if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0:
       
   962                 self.Highlights.pop(infos[0])
       
   963             
       
   964     # Removes all the highlights of one particular type from the block
       
   965     def ClearHighlight(self, highlight_type=None):
       
   966         if highlight_type is None:
       
   967             self.Highlights = {}
       
   968         else:
       
   969             highlight_items = self.Highlights.items()
       
   970             for name, highlights in highlight_items:
       
   971                 highlights = ClearHighlights(highlight, highlight_type)
       
   972                 if len(highlights) == 0:
       
   973                     self.Highlights.pop(name)
       
   974     
       
   975     # Draws transition
       
   976     def Draw(self, dc):
       
   977         Graphic_Element.Draw(self, dc)
       
   978         if self.Value:
       
   979             if self.Forced:
       
   980                 dc.SetPen(MiterPen(wx.CYAN))
       
   981                 dc.SetBrush(wx.CYAN_BRUSH)
       
   982             else:
       
   983                 dc.SetPen(MiterPen(wx.GREEN))
       
   984                 dc.SetBrush(wx.GREEN_BRUSH)
       
   985         elif self.Forced:
       
   986             dc.SetPen(MiterPen(wx.BLUE))
       
   987             dc.SetBrush(wx.BLUE_BRUSH)
       
   988         else:
       
   989             dc.SetPen(MiterPen(wx.BLACK))
       
   990             dc.SetBrush(wx.BLACK_BRUSH)
       
   991         
       
   992         if getattr(dc, "printing", False):
       
   993             if self.Type != "connection":
       
   994                 condition_size = dc.GetTextExtent(self.Condition)
       
   995             if self.Priority != 0:
       
   996                 priority_size = dc.GetTextExtent(str(self.Priority))
       
   997         else:
       
   998             if self.Type != "connection":
       
   999                 condition_size = self.ConditionSize
       
  1000             if self.Priority != 0:
       
  1001                 priority_size = self.PrioritySize
       
  1002         
       
  1003         # Draw plain rectangle for representing the transition
       
  1004         dc.DrawRectangle(self.Pos.x, 
       
  1005                          self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2, 
       
  1006                          self.Size[0] + 1,
       
  1007                          SFC_TRANSITION_SIZE[1] + 1)
       
  1008         vertical_line_x = self.Input.GetPosition()[0]
       
  1009         dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1) 
       
  1010         # Draw transition condition
       
  1011         if self.Type != "connection":
       
  1012             if self.Condition != "":
       
  1013                 condition = self.Condition
       
  1014             else:
       
  1015                 condition = "Transition"
       
  1016             condition_pos = (self.Pos.x + self.Size[0] + 5,
       
  1017                              self.Pos.y + (self.Size[1] - condition_size[1]) / 2)
       
  1018             dc.DrawText(condition, condition_pos[0], condition_pos[1])
       
  1019         # Draw priority number
       
  1020         if self.Priority != 0:
       
  1021             priority_pos = (self.Pos.x, self.Pos.y - priority_size[1] - 2)
       
  1022             dc.DrawText(str(self.Priority), priority_pos[0], priority_pos[1])
       
  1023         # Draw input and output connectors
       
  1024         self.Input.Draw(dc)
       
  1025         self.Output.Draw(dc)
       
  1026         if self.Type == "connection":
       
  1027             self.Condition.Draw(dc)
       
  1028         
       
  1029         if not getattr(dc, "printing", False):
       
  1030             for name, highlights in self.Highlights.iteritems():
       
  1031                 if name == "priority":
       
  1032                     DrawHighlightedText(dc, str(self.Priority), highlights, priority_pos[0], priority_pos[1])
       
  1033                 else:
       
  1034                     DrawHighlightedText(dc, condition, highlights, condition_pos[0], condition_pos[1])
       
  1035 
       
  1036 #-------------------------------------------------------------------------------
       
  1037 #                Sequencial Function Chart Divergence and Convergence
       
  1038 #-------------------------------------------------------------------------------
       
  1039 
       
  1040 """
       
  1041 Class that implements the graphic representation of a divergence or convergence,
       
  1042 selection or simultaneous
       
  1043 """
       
  1044 
       
  1045 class SFC_Divergence(Graphic_Element):
       
  1046     
       
  1047     # Create a new divergence
       
  1048     def __init__(self, parent, type, number = 2, id = None):
       
  1049         Graphic_Element.__init__(self, parent)
       
  1050         self.Type = type
       
  1051         self.Id = id
       
  1052         self.RealConnectors = None
       
  1053         number = max(2, number)
       
  1054         self.Size = wx.Size((number - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL, self.GetMinSize()[1])
       
  1055         # Create an input and output connector
       
  1056         if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
       
  1057             self.Inputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)]
       
  1058             self.Outputs = []
       
  1059             for i in xrange(number):
       
  1060                 self.Outputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone = True))
       
  1061         elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1062             self.Inputs = []
       
  1063             for i in xrange(number):
       
  1064                 self.Inputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True))
       
  1065             self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)]
       
  1066         self.Value = None
       
  1067         self.PreviousValue = None
       
  1068     
       
  1069     def Flush(self):
       
  1070         for input in self.Inputs:
       
  1071             input.Flush()
       
  1072         self.Inputs = []
       
  1073         for output in self.Outputs:
       
  1074             output.Flush()
       
  1075         self.Outputs = []
       
  1076     
       
  1077     def SpreadCurrent(self):
       
  1078         if self.Parent.Debug:
       
  1079             self.PreviousValue = self.Value
       
  1080             if self.Type == SELECTION_CONVERGENCE:
       
  1081                 self.Value = False
       
  1082                 for input in self.Inputs:
       
  1083                     self.Value |= input.ReceivingCurrent()
       
  1084             elif self.Type == SIMULTANEOUS_CONVERGENCE:
       
  1085                 self.Value = True
       
  1086                 for input in self.Inputs:
       
  1087                     self.Value &= input.ReceivingCurrent()
       
  1088             elif self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
       
  1089                 self.Value = self.Inputs[0].ReceivingCurrent()
       
  1090             else:
       
  1091                 self.Value = False
       
  1092             if self.Value and not self.PreviousValue:
       
  1093                 if self.Visible:
       
  1094                     self.Parent.ElementNeedRefresh(self)
       
  1095                 for output in self.Outputs:
       
  1096                     output.SpreadCurrent(True)
       
  1097             elif not self.Value and self.PreviousValue:
       
  1098                 if self.Visible:
       
  1099                     self.Parent.ElementNeedRefresh(self)
       
  1100                 for output in self.Outputs:
       
  1101                     output.SpreadCurrent(False)
       
  1102     
       
  1103     # Make a clone of this SFC_Divergence
       
  1104     def Clone(self, parent, id = None, pos = None):
       
  1105         divergence = SFC_Divergence(parent, self.Type, max(len(self.Inputs), len(self.Outputs)), id)
       
  1106         divergence.SetSize(self.Size[0], self.Size[1])
       
  1107         if pos is not None:
       
  1108             divergence.SetPosition(pos.x, pos.y)
       
  1109         else:
       
  1110             divergence.SetPosition(self.Pos.x, self.Pos.y)
       
  1111         divergence.Inputs = [input.Clone(divergence) for input in self.Inputs]
       
  1112         divergence.Outputs = [output.Clone(divergence) for output in self.Outputs]
       
  1113         return divergence
       
  1114     
       
  1115     def GetConnectorTranslation(self, element):
       
  1116         return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs))
       
  1117     
       
  1118     # Returns the RedrawRect
       
  1119     def GetRedrawRect(self, movex = 0, movey = 0):
       
  1120         rect = Graphic_Element.GetRedrawRect(self, movex, movey)
       
  1121         if movex != 0 or movey != 0:
       
  1122             for input in self.Inputs:
       
  1123                 if input.IsConnected():
       
  1124                     rect = rect.Union(input.GetConnectedRedrawRect(movex, movey))
       
  1125             for output in self.Outputs:
       
  1126                 if output.IsConnected():
       
  1127                     rect = rect.Union(output.GetConnectedRedrawRect(movex, movey))
       
  1128         return rect
       
  1129     
       
  1130     # Forbids to resize the divergence
       
  1131     def Resize(self, x, y, width, height):
       
  1132         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
  1133             Graphic_Element.Resize(self, x, 0, width, self.GetMinSize()[1])
       
  1134     
       
  1135     # Delete this divergence by calling the appropriate method
       
  1136     def Delete(self):
       
  1137         self.Parent.DeleteDivergence(self)
       
  1138     
       
  1139     # Returns the divergence type
       
  1140     def GetType(self):
       
  1141         return self.Type
       
  1142     
       
  1143     # Unconnect input and output
       
  1144     def Clean(self):
       
  1145         for input in self.Inputs:
       
  1146             input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
  1147         for output in self.Outputs:
       
  1148             output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
  1149     
       
  1150     # Add a branch to the divergence
       
  1151     def AddBranch(self):
       
  1152         if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
       
  1153             maxx = 0
       
  1154             for output in self.Outputs:
       
  1155                 pos = output.GetRelPosition()
       
  1156                 maxx = max(maxx, pos.x)
       
  1157             connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone = True)
       
  1158             self.Outputs.append(connector)
       
  1159             self.MoveConnector(connector, 0)
       
  1160         elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1161             maxx = 0
       
  1162             for input in self.Inputs:
       
  1163                 pos = input.GetRelPosition()
       
  1164                 maxx = max(maxx, pos.x)
       
  1165             connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True)
       
  1166             self.Inputs.append(connector)
       
  1167             self.MoveConnector(connector, SFC_DEFAULT_SEQUENCE_INTERVAL)
       
  1168     
       
  1169     # Remove a branch from the divergence
       
  1170     def RemoveBranch(self, connector):
       
  1171         if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
       
  1172             if connector in self.Outputs and len(self.Outputs) > 2:
       
  1173                 self.Outputs.remove(connector)
       
  1174                 self.MoveConnector(self.Outputs[0], 0)
       
  1175         elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1176             if connector in self.Inputs and len(self.Inputs) > 2:
       
  1177                 self.Inputs.remove(connector)
       
  1178                 self.MoveConnector(self.Inputs[0], 0)
       
  1179     
       
  1180     # Remove the handled branch from the divergence
       
  1181     def RemoveHandledBranch(self):
       
  1182         handle_type, handle = self.Handle
       
  1183         if handle_type == HANDLE_CONNECTOR:
       
  1184             handle.UnConnect(delete=True)
       
  1185             self.RemoveBranch(handle)
       
  1186             
       
  1187     # Return the number of branches for the divergence
       
  1188     def GetBranchNumber(self):
       
  1189         if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
       
  1190             return len(self.Outputs)
       
  1191         elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1192             return len(self.Inputs)
       
  1193     
       
  1194     # Returns if the point given is in the bounding box
       
  1195     def HitTest(self, pt, connectors=True):
       
  1196         return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) != None
       
  1197     
       
  1198     # Refresh the divergence bounding box
       
  1199     def RefreshBoundingBox(self):
       
  1200         if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
       
  1201             self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, 
       
  1202                 self.Size[0] + 1, self.Size[1] + 1)
       
  1203         elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1204             self.BoundingBox = wx.Rect(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, 
       
  1205                 self.Size[0] + 2 * SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Size[1] + 1)
       
  1206     
       
  1207     # Refresh the position of wires connected to divergence
       
  1208     def RefreshConnected(self, exclude = []):
       
  1209         for input in self.Inputs:
       
  1210             input.MoveConnected(exclude)
       
  1211         for output in self.Outputs:
       
  1212             output.MoveConnected(exclude)
       
  1213     
       
  1214     # Moves the divergence connector given
       
  1215     def MoveConnector(self, connector, movex):
       
  1216         position = connector.GetRelPosition()
       
  1217         connector.SetPosition(wx.Point(position.x + movex, position.y))
       
  1218         minx = self.Size[0]
       
  1219         maxx = 0
       
  1220         for input in self.Inputs:
       
  1221             input_pos = input.GetRelPosition()
       
  1222             minx = min(minx, input_pos.x)
       
  1223             maxx = max(maxx, input_pos.x)
       
  1224         for output in self.Outputs:
       
  1225             output_pos = output.GetRelPosition()
       
  1226             minx = min(minx, output_pos.x)
       
  1227             maxx = max(maxx, output_pos.x)
       
  1228         if minx != 0:
       
  1229             for input in self.Inputs:
       
  1230                 input_pos = input.GetRelPosition()
       
  1231                 input.SetPosition(wx.Point(input_pos.x - minx, input_pos.y))
       
  1232             for output in self.Outputs:
       
  1233                 output_pos = output.GetRelPosition()
       
  1234                 output.SetPosition(wx.Point(output_pos.x - minx, output_pos.y))
       
  1235         self.Inputs.sort(lambda x, y: cmp(x.Pos.x, y.Pos.x))
       
  1236         self.Outputs.sort(lambda x, y: cmp(x.Pos.x, y.Pos.x))
       
  1237         self.Pos.x += minx
       
  1238         self.Size[0] = maxx - minx
       
  1239         connector.MoveConnected()
       
  1240         self.RefreshBoundingBox()
       
  1241     
       
  1242     # Returns the divergence connector that starts with the point given if it exists 
       
  1243     def GetConnector(self, position, name = None):
       
  1244         # if a name is given
       
  1245         if name is not None:
       
  1246             # Test each input and output connector
       
  1247             #for input in self.Inputs:
       
  1248             #    if name == input.GetName():
       
  1249             #        return input
       
  1250             for output in self.Outputs:
       
  1251                 if name == output.GetName():
       
  1252                     return output
       
  1253         return self.FindNearestConnector(position, self.Inputs + self.Outputs)
       
  1254     
       
  1255     # Returns input and output divergence connectors 
       
  1256     def GetConnectors(self):
       
  1257         return {"inputs": self.Inputs, "outputs": self.Outputs}
       
  1258     
       
  1259     # Test if point given is on divergence input or output connector
       
  1260     def TestConnector(self, pt, direction = None, exclude=True):
       
  1261         # Test input connector
       
  1262         for input in self.Inputs:
       
  1263             if input.TestPoint(pt, direction, exclude):
       
  1264                 return input
       
  1265         # Test output connector
       
  1266         for output in self.Outputs:
       
  1267             if output.TestPoint(pt, direction, exclude):
       
  1268                 return output
       
  1269         return None
       
  1270     
       
  1271     # Changes the divergence size
       
  1272     def SetSize(self, width, height):
       
  1273         height = self.GetMinSize()[1]
       
  1274         for i, input in enumerate(self.Inputs):
       
  1275             position = input.GetRelPosition()
       
  1276             if self.RealConnectors:
       
  1277                 input.SetPosition(wx.Point(int(round(self.RealConnectors["Inputs"][i] * width)), 0))
       
  1278             else:
       
  1279                 input.SetPosition(wx.Point(int(round(float(position.x)*float(width)/float(self.Size[0]))), 0))
       
  1280             input.MoveConnected()
       
  1281         for i, output in enumerate(self.Outputs):
       
  1282             position = output.GetRelPosition()
       
  1283             if self.RealConnectors:
       
  1284                 output.SetPosition(wx.Point(int(round(self.RealConnectors["Outputs"][i] * width)), height))
       
  1285             else:
       
  1286                 output.SetPosition(wx.Point(int(round(float(position.x)*float(width)/float(self.Size[0]))), height))
       
  1287             output.MoveConnected()
       
  1288         self.Size = wx.Size(width, height)
       
  1289         self.RefreshBoundingBox()
       
  1290     
       
  1291     # Returns the divergence minimum size
       
  1292     def GetMinSize(self, default=False):
       
  1293         width = 0
       
  1294         if default:
       
  1295             if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
       
  1296                 width = (len(self.Outputs) - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL
       
  1297             elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1298                 width = (len(self.Inputs) - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL
       
  1299         if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
       
  1300             return width, 1
       
  1301         elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1302             return width, 3
       
  1303         return 0, 0
       
  1304     
       
  1305     # Refresh the position of the block connected to connector
       
  1306     def RefreshConnectedPosition(self, connector):
       
  1307         wires = connector.GetWires()
       
  1308         if len(wires) != 1:
       
  1309             return
       
  1310         current_pos = connector.GetPosition(False)
       
  1311         next = wires[0][0].GetOtherConnected(connector)
       
  1312         next_pos = next.GetPosition(False)
       
  1313         diffx = current_pos.x - next_pos.x
       
  1314         next_block = next.GetParentBlock()
       
  1315         if isinstance(next_block, SFC_Divergence):
       
  1316             next_block.MoveConnector(next, diffx)
       
  1317         else:
       
  1318             next_block.Move(diffx, 0)
       
  1319             if connector in self.Inputs:
       
  1320                 next_block.RefreshInputPosition()
       
  1321             else:
       
  1322                 next_block.RefreshOutputPosition()
       
  1323 
       
  1324     # Refresh the position of this divergence
       
  1325     def RefreshPosition(self):
       
  1326         y = 0
       
  1327         for input in self.Inputs:
       
  1328             wires = input.GetWires()
       
  1329             if len(wires) != 1:
       
  1330                 return
       
  1331             previous = wires[0][0].GetOtherConnected(input)
       
  1332             previous_pos = previous.GetPosition(False)
       
  1333             y = max(y, previous_pos.y + GetWireSize(previous.GetParentBlock()))
       
  1334         diffy = y - self.Pos.y
       
  1335         if diffy != 0:
       
  1336             self.Move(0, diffy, self.Parent.Wires)
       
  1337             self.RefreshOutputPosition((0, diffy))
       
  1338         for input in self.Inputs:
       
  1339             input.MoveConnected()
       
  1340     
       
  1341     # Align output element with this divergence
       
  1342     def RefreshOutputPosition(self, move = None):
       
  1343         if move:
       
  1344             for output_connector in self.Outputs:
       
  1345                 wires = output_connector.GetWires()
       
  1346                 if len(wires) != 1:
       
  1347                     return
       
  1348                 current_pos = output_connector.GetPosition(False)
       
  1349                 output = wires[0][0].GetOtherConnected(self.Output)
       
  1350                 output_pos = output.GetPosition(False)
       
  1351                 diffx = current_pos.x - output_pos.x
       
  1352                 output_block = output.GetParentBlock()
       
  1353                 if isinstance(output_block, SFC_Step):
       
  1354                     output_block.MoveActionBlock(move)
       
  1355                 wires[0][0].Move(move[0], move[1], True)
       
  1356                 if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0:
       
  1357                     output_block.Move(move[0], move[1], self.Parent.Wires)
       
  1358                     output_block.RefreshOutputPosition(move)
       
  1359     
       
  1360     # Method called when a LeftDown event have been generated
       
  1361     def OnLeftDown(self, event, dc, scaling):
       
  1362         connector = None
       
  1363         if event.ControlDown():
       
  1364             pos = GetScaledEventPosition(event, dc, scaling)
       
  1365             # Test if a connector have been handled
       
  1366             connector = self.TestConnector(pos, exclude=False)
       
  1367         if connector:
       
  1368             self.Handle = (HANDLE_CONNECTOR, connector)
       
  1369             wx.CallAfter(self.Parent.SetCurrentCursor, 1)
       
  1370             self.Selected = False
       
  1371             # Initializes the last position
       
  1372             self.oldPos = GetScaledEventPosition(event, dc, scaling)
       
  1373         else:
       
  1374             self.RealConnectors = {"Inputs":[],"Outputs":[]}
       
  1375             for input in self.Inputs:
       
  1376                 position = input.GetRelPosition()
       
  1377                 self.RealConnectors["Inputs"].append(float(position.x)/float(self.Size[0]))
       
  1378             for output in self.Outputs:
       
  1379                 position = output.GetRelPosition()
       
  1380                 self.RealConnectors["Outputs"].append(float(position.x)/float(self.Size[0]))
       
  1381             Graphic_Element.OnLeftDown(self, event, dc, scaling)
       
  1382     
       
  1383     # Method called when a LeftUp event have been generated
       
  1384     def OnLeftUp(self, event, dc, scaling):
       
  1385         handle_type, handle = self.Handle
       
  1386         if handle_type == HANDLE_CONNECTOR and self.Dragging and self.oldPos:
       
  1387             wires = handle.GetWires()
       
  1388             if len(wires) == 1:
       
  1389                 block = wires[0][0].GetOtherConnected(handle).GetParentBlock()
       
  1390                 block.RefreshModel(False)
       
  1391                 if not isinstance(block, SFC_Divergence):
       
  1392                     if handle in self.Inputs:
       
  1393                         block.RefreshInputModel()
       
  1394                     else:
       
  1395                         block.RefreshOutputModel()
       
  1396         Graphic_Element.OnLeftUp(self, event, dc, scaling)
       
  1397         self.RealConnectors = None
       
  1398     
       
  1399     # Method called when a RightUp event have been generated
       
  1400     def OnRightUp(self, event, dc, scaling):
       
  1401         pos = GetScaledEventPosition(event, dc, scaling)
       
  1402         # Popup the menu with special items for a block and a connector if one is handled
       
  1403         connector = self.TestConnector(pos, exclude=False)
       
  1404         if connector:
       
  1405             self.Handle = (HANDLE_CONNECTOR, connector)
       
  1406             self.Parent.PopupDivergenceMenu(True)
       
  1407         else:
       
  1408             # Popup the divergence menu without delete branch
       
  1409             self.Parent.PopupDivergenceMenu(False)
       
  1410     
       
  1411     # Refreshes the divergence state according to move defined and handle selected
       
  1412     def ProcessDragging(self, movex, movey, event, scaling):
       
  1413         handle_type, handle = self.Handle
       
  1414         # A connector has been handled
       
  1415         if handle_type == HANDLE_CONNECTOR:
       
  1416             movex = max(-self.BoundingBox.x, movex)
       
  1417             if scaling is not None:
       
  1418                 movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x
       
  1419             self.MoveConnector(handle, movex)
       
  1420             if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1421                 self.RefreshConnectedPosition(handle)
       
  1422             return movex, 0
       
  1423         elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
  1424             return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
       
  1425         return 0, 0
       
  1426     
       
  1427     # Refresh output element model
       
  1428     def RefreshOutputModel(self, move=False):
       
  1429         if move and self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1430             for output in self.Outputs:
       
  1431                 wires = output.GetWires()
       
  1432                 if len(wires) != 1:
       
  1433                     return
       
  1434                 output_block = wires[0][0].GetOtherConnected(output).GetParentBlock()
       
  1435                 output_block.RefreshModel(False)
       
  1436                 if not isinstance(output_block, SFC_Divergence) or move:
       
  1437                     output_block.RefreshOutputModel(move)
       
  1438     
       
  1439     # Refreshes the divergence model
       
  1440     def RefreshModel(self, move=True):
       
  1441         self.Parent.RefreshDivergenceModel(self)
       
  1442         # If divergence has moved, refresh the model of wires connected to outputs
       
  1443         if move:
       
  1444             if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1445                 self.RefreshOutputModel()
       
  1446             else:
       
  1447                 for output in self.Outputs:
       
  1448                     output.RefreshWires()
       
  1449     
       
  1450     # Draws the highlightment of this element if it is highlighted
       
  1451     def DrawHighlightment(self, dc):
       
  1452         scalex, scaley = dc.GetUserScale()
       
  1453         dc.SetUserScale(1, 1)
       
  1454         dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
       
  1455         dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
       
  1456         dc.SetLogicalFunction(wx.AND)
       
  1457         # Draw two rectangles for representing the contact
       
  1458         posx = self.Pos.x
       
  1459         width = self.Size[0]
       
  1460         if self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1461             posx -= SFC_SIMULTANEOUS_SEQUENCE_EXTRA
       
  1462             width += SFC_SIMULTANEOUS_SEQUENCE_EXTRA * 2
       
  1463         dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2, 
       
  1464                          int(round((self.Pos.y - 1) * scaley)) - 2, 
       
  1465                          int(round((width + 3) * scalex)) + 5, 
       
  1466                          int(round((self.Size.height + 3) * scaley)) + 5)
       
  1467         dc.SetLogicalFunction(wx.COPY)
       
  1468         dc.SetUserScale(scalex, scaley)
       
  1469         
       
  1470     # Draws divergence
       
  1471     def Draw(self, dc):
       
  1472         Graphic_Element.Draw(self, dc)
       
  1473         if self.Value:
       
  1474             dc.SetPen(MiterPen(wx.GREEN))
       
  1475             dc.SetBrush(wx.GREEN_BRUSH)
       
  1476         else:
       
  1477             dc.SetPen(MiterPen(wx.BLACK))
       
  1478             dc.SetBrush(wx.BLACK_BRUSH)
       
  1479         # Draw plain rectangle for representing the divergence
       
  1480         if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
       
  1481             dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
       
  1482         elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]:
       
  1483             dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, 
       
  1484                         self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y)
       
  1485             dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1], 
       
  1486                         self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y + self.Size[1])
       
  1487         # Draw inputs and outputs connectors
       
  1488         for input in self.Inputs:
       
  1489             input.Draw(dc)
       
  1490         for output in self.Outputs:
       
  1491             output.Draw(dc)
       
  1492         
       
  1493 
       
  1494 #-------------------------------------------------------------------------------
       
  1495 #                   Sequencial Function Chart Jump to Step
       
  1496 #-------------------------------------------------------------------------------
       
  1497 
       
  1498 """
       
  1499 Class that implements the graphic representation of a jump to step
       
  1500 """
       
  1501 
       
  1502 class SFC_Jump(Graphic_Element):
       
  1503     
       
  1504     # Create a new jump
       
  1505     def __init__(self, parent, target, id = None):
       
  1506         Graphic_Element.__init__(self, parent)
       
  1507         self.SetTarget(target)
       
  1508         self.Id = id
       
  1509         self.Size = wx.Size(SFC_JUMP_SIZE[0], SFC_JUMP_SIZE[1])
       
  1510         self.Highlights = []
       
  1511         # Create an input and output connector
       
  1512         self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)
       
  1513         self.Value = None
       
  1514         self.PreviousValue = None
       
  1515         
       
  1516     def Flush(self):
       
  1517         if self.Input is not None:
       
  1518             self.Input.Flush()
       
  1519             self.Input = None
       
  1520     
       
  1521     def SpreadCurrent(self):
       
  1522         if self.Parent.Debug:
       
  1523             self.PreviousValue = self.Value
       
  1524             self.Value = self.Input.ReceivingCurrent()
       
  1525             if self.Value != self.PreviousValue and self.Visible:
       
  1526                 self.Parent.ElementNeedRefresh(self)
       
  1527     
       
  1528     # Make a clone of this SFC_Jump
       
  1529     def Clone(self, parent, id = None, pos = None):
       
  1530         jump = SFC_Jump(parent, self.Target, id)
       
  1531         jump.SetSize(self.Size[0], self.Size[1])
       
  1532         if pos is not None:
       
  1533             jump.SetPosition(pos.x, pos.y)
       
  1534         else:
       
  1535             jump.SetPosition(self.Pos.x, self.Pos.y)
       
  1536         jump.Input = self.Input.Clone(jump)
       
  1537         return jump
       
  1538     
       
  1539     def GetConnectorTranslation(self, element):
       
  1540         return {self.Input : element.Input}
       
  1541     
       
  1542     # Returns the RedrawRect
       
  1543     def GetRedrawRect(self, movex = 0, movey = 0):
       
  1544         rect = Graphic_Element.GetRedrawRect(self, movex, movey)
       
  1545         rect = rect.Union(self.Input.GetRedrawRect(movex, movey))
       
  1546         if movex != 0 or movey != 0:
       
  1547             if self.Input.IsConnected():
       
  1548                 rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
       
  1549         return rect
       
  1550     
       
  1551     # Forbids to change the jump size
       
  1552     def SetSize(self, width, height):
       
  1553         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
  1554             Graphic_Element.SetSize(self, width, height)
       
  1555     
       
  1556     # Forbids to resize jump
       
  1557     def Resize(self, x, y, width, height):
       
  1558         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
  1559             Graphic_Element.Resize(self, x, y, width, height)
       
  1560     
       
  1561     # Delete this jump by calling the appropriate method
       
  1562     def Delete(self):
       
  1563         self.Parent.DeleteJump(self)
       
  1564     
       
  1565     # Unconnect input
       
  1566     def Clean(self):
       
  1567         self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
  1568     
       
  1569     # Refresh the size of text for target
       
  1570     def RefreshTargetSize(self):
       
  1571         self.TargetSize = self.Parent.GetTextExtent(self.Target)
       
  1572     
       
  1573     # Returns if the point given is in the bounding box
       
  1574     def HitTest(self, pt, connectors=True):
       
  1575         # Calculate the bounding box of the condition outside the transition
       
  1576         text_width, text_height = self.TargetSize
       
  1577         text_bbx = wx.Rect(self.Pos.x + self.Size[0] + 2,
       
  1578                            self.Pos.y + (self.Size[1] - text_height) / 2,
       
  1579                            text_width,
       
  1580                            text_height)
       
  1581         return text_bbx.InsideXY(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors)
       
  1582     
       
  1583     # Refresh the jump bounding box
       
  1584     def RefreshBoundingBox(self):
       
  1585         text_width, text_height = self.Parent.GetTextExtent(self.Target)
       
  1586         # Calculate the bounding box size
       
  1587         bbx_width = self.Size[0] + 2 + text_width
       
  1588         self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE, 
       
  1589                 bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1)
       
  1590     
       
  1591     # Returns the connector connected to input
       
  1592     def GetPreviousConnector(self):
       
  1593         wires = self.Input.GetWires()
       
  1594         if len(wires) == 1:
       
  1595             return wires[0][0].GetOtherConnected(self.Input)
       
  1596         return None
       
  1597     
       
  1598     # Refresh the element connectors position
       
  1599     def RefreshConnectors(self):
       
  1600         scaling = self.Parent.GetScaling()
       
  1601         horizontal_pos = self.Size[0] / 2
       
  1602         if scaling is not None:
       
  1603             horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x
       
  1604         self.Input.SetPosition(wx.Point(horizontal_pos, 0))
       
  1605         self.RefreshConnected()
       
  1606     
       
  1607     # Refresh the position of wires connected to jump
       
  1608     def RefreshConnected(self, exclude = []):
       
  1609         if self.Input:
       
  1610             self.Input.MoveConnected(exclude)
       
  1611     
       
  1612     # Returns input jump connector 
       
  1613     def GetConnector(self, position = None, name = None):
       
  1614         return self.Input
       
  1615     
       
  1616     # Returns all the jump connectors 
       
  1617     def GetConnectors(self):
       
  1618         return {"inputs": [self.Input], "outputs": []}
       
  1619     
       
  1620     # Test if point given is on jump input connector
       
  1621     def TestConnector(self, pt, direction = None, exclude = True):
       
  1622         # Test input connector
       
  1623         if self.Input and self.Input.TestPoint(pt, direction, exclude):
       
  1624             return self.Input
       
  1625         return None
       
  1626     
       
  1627     # Changes the jump target
       
  1628     def SetTarget(self, target):
       
  1629         self.Target = target
       
  1630         self.RefreshTargetSize()
       
  1631         self.RefreshBoundingBox()
       
  1632     
       
  1633     # Returns the jump target
       
  1634     def GetTarget(self):
       
  1635         return self.Target
       
  1636     
       
  1637     # Returns the jump minimum size
       
  1638     def GetMinSize(self):
       
  1639         return SFC_JUMP_SIZE
       
  1640     
       
  1641     # Align input element with this jump
       
  1642     def RefreshInputPosition(self):
       
  1643         if self.Input:
       
  1644             current_pos = self.Input.GetPosition(False)
       
  1645             input = self.GetPreviousConnector()
       
  1646             if input:
       
  1647                 input_pos = input.GetPosition(False)
       
  1648                 diffx = current_pos.x - input_pos.x
       
  1649                 input_block = input.GetParentBlock()
       
  1650                 if isinstance(input_block, SFC_Divergence):
       
  1651                     input_block.MoveConnector(input, diffx)
       
  1652                 else:
       
  1653                     if isinstance(input_block, SFC_Step):
       
  1654                         input_block.MoveActionBlock((diffx, 0))
       
  1655                     input_block.Move(diffx, 0)
       
  1656                     input_block.RefreshInputPosition()
       
  1657     
       
  1658     # Can't align output element, because there is no output
       
  1659     def RefreshOutputPosition(self, move = None):
       
  1660         pass
       
  1661     
       
  1662     # Method called when a LeftDClick event have been generated
       
  1663     def OnLeftDClick(self, event, dc, scaling):
       
  1664         # Edit the jump properties
       
  1665         self.Parent.EditJumpContent(self)
       
  1666     
       
  1667     # Method called when a RightUp event have been generated
       
  1668     def OnRightUp(self, event, dc, scaling):
       
  1669         # Popup the default menu
       
  1670         self.Parent.PopupDefaultMenu()
       
  1671     
       
  1672     # Refreshes the jump state according to move defined and handle selected
       
  1673     def ProcessDragging(self, movex, movey, event, scaling):
       
  1674         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1675             movex = max(-self.BoundingBox.x, movex)
       
  1676             if scaling is not None:
       
  1677                 movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x
       
  1678             self.Move(movex, 0)
       
  1679             self.RefreshInputPosition()
       
  1680             return movex, 0
       
  1681         else:
       
  1682             return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2)
       
  1683     
       
  1684     # Refresh input element model
       
  1685     def RefreshInputModel(self):
       
  1686         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1687             input = self.GetPreviousConnector()
       
  1688             if input:
       
  1689                 input_block = input.GetParentBlock()
       
  1690                 input_block.RefreshModel(False)
       
  1691                 if not isinstance(input_block, SFC_Divergence):
       
  1692                     input_block.RefreshInputModel()
       
  1693     
       
  1694     # Refresh output element model
       
  1695     def RefreshOutputModel(self, move=False):
       
  1696         pass
       
  1697     
       
  1698     # Refreshes the jump model
       
  1699     def RefreshModel(self, move=True):
       
  1700         self.Parent.RefreshJumpModel(self)
       
  1701         if move:
       
  1702             if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1703                 self.RefreshInputModel()
       
  1704     
       
  1705     # Adds an highlight to the variable
       
  1706     def AddHighlight(self, infos, start, end, highlight_type):
       
  1707         if infos[0] == "target" and start[0] == 0 and end[0] == 0:
       
  1708             AddHighlight(self.Highlights, (start, end, highlight_type))
       
  1709     
       
  1710     # Removes an highlight from the variable
       
  1711     def RemoveHighlight(self, infos, start, end, highlight_type):
       
  1712         if infos[0] == "target":
       
  1713             RemoveHighlight(self.Highlights, (start, end, highlight_type))
       
  1714     
       
  1715     # Removes all the highlights of one particular type from the variable
       
  1716     def ClearHighlight(self, highlight_type=None):
       
  1717         ClearHighlights(self.Highlights, highlight_type)
       
  1718     
       
  1719     # Draws the highlightment of this element if it is highlighted
       
  1720     def DrawHighlightment(self, dc):
       
  1721         scalex, scaley = dc.GetUserScale()
       
  1722         dc.SetUserScale(1, 1)
       
  1723         dc.SetPen(MiterPen(HIGHLIGHTCOLOR))
       
  1724         dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR))
       
  1725         dc.SetLogicalFunction(wx.AND)
       
  1726         points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3, 
       
  1727                            int(round((self.Pos.y - 2) * scaley)) - 2),
       
  1728                   wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4, 
       
  1729                            int(round((self.Pos.y - 2) * scaley)) - 2),
       
  1730                   wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)), 
       
  1731                            int(round((self.Pos.y + self.Size[1] + 3) * scaley)) + 4)]
       
  1732         dc.DrawPolygon(points)
       
  1733         dc.SetLogicalFunction(wx.COPY)
       
  1734         dc.SetUserScale(scalex, scaley)
       
  1735     
       
  1736     # Draws divergence
       
  1737     def Draw(self, dc):
       
  1738         Graphic_Element.Draw(self, dc)
       
  1739         if self.Value:
       
  1740             dc.SetPen(MiterPen(wx.GREEN))
       
  1741             dc.SetBrush(wx.GREEN_BRUSH)
       
  1742         else:
       
  1743             dc.SetPen(MiterPen(wx.BLACK))
       
  1744             dc.SetBrush(wx.BLACK_BRUSH)
       
  1745         
       
  1746         if getattr(dc, "printing", False):
       
  1747             target_size = dc.GetTextExtent(self.Target)
       
  1748         else:
       
  1749             target_size = self.TargetSize
       
  1750         
       
  1751         # Draw plain rectangle for representing the divergence
       
  1752         dc.DrawLine(self.Pos.x + self.Size[0] / 2, self.Pos.y, self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1])
       
  1753         points = [wx.Point(self.Pos.x, self.Pos.y),
       
  1754                   wx.Point(self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1] / 3),
       
  1755                   wx.Point(self.Pos.x + self.Size[0], self.Pos.y),
       
  1756                   wx.Point(self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1])]
       
  1757         dc.DrawPolygon(points)
       
  1758         target_pos = (self.Pos.x + self.Size[0] + 2,
       
  1759                       self.Pos.y + (self.Size[1] - target_size[1]) / 2)
       
  1760         dc.DrawText(self.Target, target_pos[0], target_pos[1])
       
  1761         # Draw input connector
       
  1762         if self.Input:
       
  1763             self.Input.Draw(dc)
       
  1764             
       
  1765         if not getattr(dc, "printing", False):
       
  1766             DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1])
       
  1767         
       
  1768 
       
  1769 #-------------------------------------------------------------------------------
       
  1770 #                   Sequencial Function Chart Action Block
       
  1771 #-------------------------------------------------------------------------------
       
  1772 
       
  1773 """
       
  1774 Class that implements the graphic representation of an action block
       
  1775 """
       
  1776 
       
  1777 class SFC_ActionBlock(Graphic_Element):
       
  1778     
       
  1779     # Create a new action block
       
  1780     def __init__(self, parent, actions = [], id = None):
       
  1781         Graphic_Element.__init__(self, parent)
       
  1782         self.Id = id
       
  1783         self.Size = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1])
       
  1784         self.MinSize = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1])
       
  1785         self.Highlights = {}
       
  1786         # Create an input and output connector
       
  1787         self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone = True)
       
  1788         self.SetActions(actions)
       
  1789         self.Value = None
       
  1790         self.PreviousValue = None
       
  1791     
       
  1792     def Flush(self):
       
  1793         if self.Input is not None:
       
  1794             self.Input.Flush()
       
  1795             self.Input = None
       
  1796     
       
  1797     def SpreadCurrent(self):
       
  1798         if self.Parent.Debug:
       
  1799             self.PreviousValue = self.Value
       
  1800             self.Value = self.Input.ReceivingCurrent()
       
  1801             if self.Value != self.PreviousValue and self.Visible:
       
  1802                 self.Parent.ElementNeedRefresh(self)
       
  1803     
       
  1804     # Make a clone of this SFC_ActionBlock
       
  1805     def Clone(self, parent, id = None, pos = None):
       
  1806         actions = [action.copy() for action in self.Actions]
       
  1807         action_block = SFC_ActionBlock(parent, actions, id)
       
  1808         action_block.SetSize(self.Size[0], self.Size[1])
       
  1809         if pos is not None:
       
  1810             action_block.SetPosition(pos.x, pos.y)
       
  1811         else:
       
  1812             action_block.SetPosition(self.Pos.x, self.Pos.y)
       
  1813         action_block.Input = self.Input.Clone(action_block)
       
  1814         return action_block
       
  1815     
       
  1816     def GetConnectorTranslation(self, element):
       
  1817         return {self.Input : element.Input}
       
  1818     
       
  1819     # Returns the RedrawRect
       
  1820     def GetRedrawRect(self, movex = 0, movey = 0):
       
  1821         rect = Graphic_Element.GetRedrawRect(self, movex, movey)
       
  1822         rect = rect.Union(self.Input.GetRedrawRect(movex, movey))
       
  1823         if movex != 0 or movey != 0:
       
  1824             if self.Input.IsConnected():
       
  1825                 rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey))
       
  1826         return rect
       
  1827     
       
  1828     # Returns the number of action lines
       
  1829     def GetLineNumber(self):
       
  1830         return len(self.Actions)
       
  1831     
       
  1832     def GetLineSize(self):
       
  1833         if len(self.Actions) > 0:
       
  1834             return self.Size[1] / len(self.Actions)
       
  1835         else:
       
  1836             return SFC_ACTION_MIN_SIZE[1]
       
  1837     
       
  1838     # Forbids to resize the action block
       
  1839     def Resize(self, x, y, width, height):
       
  1840         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1841             if x == 0:
       
  1842                 self.SetSize(width, self.Size[1])
       
  1843         else:
       
  1844             Graphic_Element.Resize(self, x, y, width, height)
       
  1845     
       
  1846     # Delete this action block by calling the appropriate method
       
  1847     def Delete(self):
       
  1848         self.Parent.DeleteActionBlock(self)
       
  1849     
       
  1850     # Unconnect input and output
       
  1851     def Clean(self):
       
  1852         self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
       
  1853         
       
  1854     # Refresh the action block bounding box
       
  1855     def RefreshBoundingBox(self):
       
  1856         self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
       
  1857     
       
  1858     # Refresh the position of wires connected to action block
       
  1859     def RefreshConnected(self, exclude = []):
       
  1860         self.Input.MoveConnected(exclude)
       
  1861     
       
  1862     # Returns input action block connector 
       
  1863     def GetConnector(self, position = None, name = None):
       
  1864         return self.Input
       
  1865     
       
  1866     # Returns all the action block connectors 
       
  1867     def GetConnectors(self):
       
  1868         return {"inputs": [self.Input], "outputs": []}
       
  1869     
       
  1870     # Test if point given is on action block input connector
       
  1871     def TestConnector(self, pt, direction = None, exclude = True):
       
  1872         # Test input connector
       
  1873         if self.Input.TestPoint(pt, direction, exclude):
       
  1874             return self.Input
       
  1875         return None
       
  1876     
       
  1877     # Refresh the element connectors position
       
  1878     def RefreshConnectors(self):
       
  1879         scaling = self.Parent.GetScaling()
       
  1880         vertical_pos = SFC_ACTION_MIN_SIZE[1] / 2
       
  1881         if scaling is not None:
       
  1882             vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y
       
  1883         self.Input.SetPosition(wx.Point(0, vertical_pos))
       
  1884         self.RefreshConnected()
       
  1885     
       
  1886     # Changes the action block actions
       
  1887     def SetActions(self, actions):
       
  1888         self.Actions = actions
       
  1889         self.ColSize = [0, 0, 0]
       
  1890         min_height = 0
       
  1891         for action in self.Actions:
       
  1892             width, height = self.Parent.GetTextExtent(action["qualifier"])
       
  1893             self.ColSize[0] = max(self.ColSize[0], width + 10)
       
  1894             row_height = height
       
  1895             if action.has_key("duration"):
       
  1896                 width, height = self.Parent.GetTextExtent(action["duration"])
       
  1897                 row_height = max(row_height, height)
       
  1898                 self.ColSize[0] = max(self.ColSize[0], width + 10)
       
  1899             width, height = self.Parent.GetTextExtent(action["value"])
       
  1900             row_height = max(row_height, height)
       
  1901             self.ColSize[1] = max(self.ColSize[1], width + 10)
       
  1902             if action.get("indicator", "") != "":
       
  1903                 width, height = self.Parent.GetTextExtent(action["indicator"])
       
  1904                 row_height = max(row_height, height)
       
  1905                 self.ColSize[2] = max(self.ColSize[2], width + 10)
       
  1906             min_height += row_height + 5
       
  1907         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
       
  1908             self.Size = wx.Size(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], max(min_height, SFC_ACTION_MIN_SIZE[1], self.Size[1]))
       
  1909             self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2],
       
  1910                 SFC_ACTION_MIN_SIZE[0]), max(SFC_ACTION_MIN_SIZE[1], min_height)
       
  1911             self.RefreshBoundingBox()
       
  1912         else:
       
  1913             self.Size = wx.Size(max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2],
       
  1914                 SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1])
       
  1915             self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2],
       
  1916                 SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1]
       
  1917             self.RefreshBoundingBox()
       
  1918             if self.Input:
       
  1919                 wires = self.Input.GetWires()
       
  1920                 if len(wires) == 1:
       
  1921                     input_block = wires[0][0].GetOtherConnected(self.Input).GetParentBlock()
       
  1922                     input_block.RefreshOutputPosition()
       
  1923                     input_block.RefreshOutputModel(True)
       
  1924     
       
  1925     # Returns the action block actions
       
  1926     def GetActions(self):
       
  1927         return self.Actions
       
  1928     
       
  1929     # Returns the action block minimum size
       
  1930     def GetMinSize(self):
       
  1931         return self.MinSize
       
  1932     
       
  1933     # Method called when a LeftDClick event have been generated
       
  1934     def OnLeftDClick(self, event, dc, scaling):
       
  1935         # Edit the action block properties
       
  1936         self.Parent.EditActionBlockContent(self)
       
  1937     
       
  1938     # Method called when a RightUp event have been generated
       
  1939     def OnRightUp(self, event, dc, scaling):
       
  1940         # Popup the default menu
       
  1941         self.Parent.PopupDefaultMenu()
       
  1942     
       
  1943     # Refreshes the action block state according to move defined and handle selected
       
  1944     def ProcessDragging(self, movex, movey, event, scaling):
       
  1945         if self.Parent.GetDrawingMode() != FREEDRAWING_MODE:
       
  1946             handle_type, handle = self.Handle
       
  1947             if handle_type == HANDLE_MOVE:
       
  1948                 movex = max(-self.BoundingBox.x, movex)
       
  1949                 if scaling is not None:
       
  1950                     movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x
       
  1951                 wires = self.Input.GetWires()
       
  1952                 if len(wires) == 1:
       
  1953                     input_pos = wires[0][0].GetOtherConnected(self.Input).GetPosition(False)
       
  1954                     if self.Pos.x - input_pos.x + movex >= SFC_WIRE_MIN_SIZE:
       
  1955                         self.Move(movex, 0)
       
  1956                         return movex, 0
       
  1957                 return 0, 0
       
  1958             else:
       
  1959                 return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
       
  1960         else:
       
  1961             return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling)
       
  1962 
       
  1963     
       
  1964     # Refreshes the action block model
       
  1965     def RefreshModel(self, move=True):
       
  1966         self.Parent.RefreshActionBlockModel(self)
       
  1967     
       
  1968     # Adds an highlight to the variable
       
  1969     def AddHighlight(self, infos, start, end, highlight_type):
       
  1970         if infos[0] == "action" and infos[1] < len(self.Actions):
       
  1971             action_highlights = self.Highlights.setdefault(infos[1], {})
       
  1972             attribute_highlights = action_highlights.setdefault(infos[2], [])
       
  1973             AddHighlight(attribute_highlights, (start, end, highlight_type))
       
  1974     
       
  1975     # Removes an highlight from the block
       
  1976     def RemoveHighlight(self, infos, start, end, highlight_type):
       
  1977         if infos[0] == "action" and infos[1] < len(self.Actions):
       
  1978             action_highlights = self.Highlights.get(infos[1], {})
       
  1979             attribute_highlights = action_highlights.setdefault(infos[2], [])
       
  1980             if RemoveHighlight(attribute_highlights, (start, end, highlight_type)) and len(attribute_highlights) == 0:
       
  1981                 action_highlights.pop(infos[2])
       
  1982                 if len(action_highlights) == 0:
       
  1983                     self.Highlights.pop(infos[1])
       
  1984     
       
  1985     # Removes all the highlights of one particular type from the block
       
  1986     def ClearHighlight(self, highlight_type=None):
       
  1987         if highlight_type is None:
       
  1988             self.Highlights = {}
       
  1989         else:
       
  1990             highlight_items = self.Highlights.items()
       
  1991             for number, action_highlights in highlight_items:
       
  1992                 action_highlight_items = action_highlights.items()
       
  1993                 for name, attribute_highlights in action_highlights:
       
  1994                     attribute_highlights = ClearHighlights(attribute_highlights, highlight_type)
       
  1995                     if len(attribute_highlights) == 0:
       
  1996                         action_highlights.pop(name)
       
  1997                 if len(action_highlights) == 0:
       
  1998                     self.Highlights.pop(number)
       
  1999     
       
  2000     # Draws divergence
       
  2001     def Draw(self, dc):
       
  2002         Graphic_Element.Draw(self, dc)
       
  2003         if self.Value:
       
  2004             dc.SetPen(MiterPen(wx.GREEN))
       
  2005         else:
       
  2006             dc.SetPen(MiterPen(wx.BLACK))
       
  2007         dc.SetBrush(wx.WHITE_BRUSH)
       
  2008         colsize = [self.ColSize[0], self.Size[0] - self.ColSize[0] - self.ColSize[2], self.ColSize[2]]
       
  2009         # Draw plain rectangle for representing the action block
       
  2010         dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
       
  2011         dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y, 
       
  2012                 self.Pos.x + colsize[0], self.Pos.y + self.Size[1])
       
  2013         dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y, 
       
  2014                 self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1])
       
  2015         line_size = self.GetLineSize()
       
  2016         for i, action in enumerate(self.Actions):
       
  2017             if i != 0:
       
  2018                 dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size, 
       
  2019                     self.Pos.x + self.Size[0], self.Pos.y + i * line_size)
       
  2020             qualifier_size = dc.GetTextExtent(action["qualifier"])
       
  2021             if action.has_key("duration"):
       
  2022                 qualifier_pos = (self.Pos.x + (colsize[0] - qualifier_size[0]) / 2,
       
  2023                                  self.Pos.y + i * line_size + line_size / 2 - qualifier_size[1])
       
  2024                 duration_size = dc.GetTextExtent(action["duration"])
       
  2025                 duration_pos = (self.Pos.x + (colsize[0] - duration_size[0]) / 2,
       
  2026                                 self.Pos.y + i * line_size + line_size / 2)
       
  2027                 dc.DrawText(action["duration"], duration_pos[0], duration_pos[1])
       
  2028             else:
       
  2029                 qualifier_pos = (self.Pos.x + (colsize[0] - qualifier_size[0]) / 2,
       
  2030                                  self.Pos.y + i * line_size + (line_size - qualifier_size[1]) / 2)
       
  2031             dc.DrawText(action["qualifier"], qualifier_pos[0], qualifier_pos[1])
       
  2032             content_size = dc.GetTextExtent(action["value"])
       
  2033             content_pos = (self.Pos.x + colsize[0] + (colsize[1] - content_size[0]) / 2,
       
  2034                            self.Pos.y + i * line_size + (line_size - content_size[1]) / 2)
       
  2035             dc.DrawText(action["value"], content_pos[0], content_pos[1])
       
  2036             if action.has_key("indicator"):
       
  2037                 indicator_size = dc.GetTextExtent(action["indicator"])
       
  2038                 indicator_pos = (self.Pos.x + colsize[0] + colsize[1] + (colsize[2] - indicator_size[0]) / 2,
       
  2039                                  self.Pos.y + i * line_size + (line_size - indicator_size[1]) / 2)
       
  2040                 dc.DrawText(action["indicator"], indicator_pos[0], indicator_pos[1])
       
  2041             
       
  2042             if not getattr(dc, "printing", False):
       
  2043                 action_highlights = self.Highlights.get(i, {})
       
  2044                 for name, attribute_highlights in action_highlights.iteritems():
       
  2045                     if name == "qualifier":
       
  2046                         DrawHighlightedText(dc, action["qualifier"], attribute_highlights, qualifier_pos[0], qualifier_pos[1])
       
  2047                     elif name == "duration":
       
  2048                         DrawHighlightedText(dc, action["duration"], attribute_highlights, duration_pos[0], duration_pos[1])
       
  2049                     elif name in ["reference", "inline"]:
       
  2050                         DrawHighlightedText(dc, action["value"], attribute_highlights, content_pos[0], content_pos[1])
       
  2051                     elif name == "indicator":
       
  2052                         DrawHighlightedText(dc, action["indicator"], attribute_highlights, indicator_pos[0], indicator_pos[1])
       
  2053         
       
  2054         # Draw input connector
       
  2055         self.Input.Draw(dc)
       
  2056