editors/SFCViewer.py
author Andrey Skvortsov <andrej.skvortzov@gmail.com>
Thu, 17 Nov 2016 13:48:04 +0300
changeset 1565 894f31f8ca64
parent 1347 533741e5075c
child 1567 a4bf874e4949
permissions -rw-r--r--
make about dialog boxes use standard wx about dialogs

Any information in dialog can be easily changed without much effort
and the dialog can be easy translated.

Unfortunately on Windows there is no good standard about dialog,
therefore own implementation is used there.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
#based on the plcopen standard.
#
#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
#
#See COPYING file for copyrights details.
#
#This library is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public
#License as published by the Free Software Foundation; either
#version 2.1 of the License, or (at your option) any later version.
#
#This library is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#General Public License for more details.
#
#You should have received a copy of the GNU General Public
#License along with this library; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from types import *

import wx

from Viewer import *

class SFC_Viewer(Viewer):
    
    def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""):
        Viewer.__init__(self, parent, tagname, window, controler, debug, instancepath)
        self.CurrentLanguage = "SFC"
    
    def ConnectConnectors(self, start, end):
        startpoint = [start.GetPosition(False), start.GetDirection()]
        endpoint = [end.GetPosition(False), end.GetDirection()]
        wire = Wire(self, startpoint, endpoint)
        self.AddWire(wire)
        start.Connect((wire, 0), False)
        end.Connect((wire, -1), False)
        wire.ConnectStartPoint(None, start)
        wire.ConnectEndPoint(None, end)
        return wire
    
    def CreateTransition(self, connector, next = None):
        previous = connector.GetParentBlock()
        id = self.GetNewId()
        transition = SFC_Transition(self, "reference", "", 0, id)
        pos = connector.GetPosition(False)
        transition.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
        transition_connectors = transition.GetConnectors()
        wire = self.ConnectConnectors(transition_connectors["input"], connector)
        if isinstance(previous, SFC_Divergence):
            previous.RefreshConnectedPosition(connector)
        else:
            previous.RefreshOutputPosition()
        wire.SetPoints([wx.Point(pos.x, pos.y + GetWireSize(previous)), wx.Point(pos.x, pos.y)])
        self.AddBlock(transition)
        self.Controler.AddEditedElementTransition(self.TagName, id)
        self.RefreshTransitionModel(transition)
        if next:
            wire = self.ConnectConnectors(next, transition_connectors["output"])
            pos = transition_connectors["output"].GetPosition(False)
            next_block = next.GetParentBlock()
            next_pos = next.GetPosition(False)
            transition.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
            if isinstance(next_block, SFC_Divergence):
                next_block.RefreshPosition()
            transition.RefreshOutputModel(True)
        return transition
    
    def RemoveTransition(self, transition):
        connectors = transition.GetConnectors()
        input_wires = connectors["input"].GetWires()
        if len(input_wires) != 1:
            return
        input_wire = input_wires[0][0]
        previous = input_wire.EndConnected
        input_wire.Clean()
        self.RemoveWire(input_wire)
        output_wires = connectors["output"].GetWires()
        if len(output_wires) != 1:
            return
        output_wire = output_wires[0][0]
        next = output_wire.StartConnected
        output_wire.Clean()
        self.RemoveWire(output_wire)
        transition.Clean()
        self.RemoveBlock(transition)
        self.Controler.RemoveEditedElementInstance(self.TagName, transition.GetId())
        wire = self.ConnectConnectors(next, previous)
        return wire
    
    def CreateStep(self, name, connector, next = None):
        previous = connector.GetParentBlock()
        id = self.GetNewId()
        step = SFC_Step(self, name, False, id)
        if next:
            step.AddOutput()
        min_width, min_height = step.GetMinSize()
        pos = connector.GetPosition(False)
        step.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
        step.SetSize(min_width, min_height)
        step_connectors = step.GetConnectors()
        wire = self.ConnectConnectors(step_connectors["input"], connector)
        if isinstance(previous, SFC_Divergence):
            previous.RefreshConnectedPosition(connector)
        else:
            previous.RefreshOutputPosition()
        wire.SetPoints([wx.Point(pos.x, pos.y + GetWireSize(previous)), wx.Point(pos.x, pos.y)])
        self.AddBlock(step)
        self.Controler.AddEditedElementStep(self.TagName, id)
        self.RefreshStepModel(step)
        if next:
            wire = self.ConnectConnectors(next, step_connectors["output"])
            pos = step_connectors["output"].GetPosition(False)
            next_block = next.GetParentBlock()
            next_pos = next.GetPosition(False)
            step.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
            if isinstance(next_block, SFC_Divergence):
                next_block.RefreshPosition()
            step.RefreshOutputModel(True)
        return step

    def RemoveStep(self, step):
        connectors = step.GetConnectors()
        if connectors["input"]:
            input_wires = connectors["input"].GetWires()
            if len(input_wires) != 1:
                return
            input_wire = input_wires[0][0]
            previous = input_wire.EndConnected
            input_wire.Clean()
            self.RemoveWire(input_wire)
        else:
            previous = None
        if connectors["output"]:
            output_wires = connectors["output"].GetWires()
            if len(output_wires) != 1:
                return
            output_wire = output_wires[0][0]
            next = output_wire.StartConnected
            output_wire.Clean()
            self.RemoveWire(output_wire)
        else:
            next = None
        action = step.GetActionConnected()
        if action:
            self.DeleteActionBlock(action.GetParentBlock())
        step.Clean()
        self.RemoveBlock(step)
        self.Controler.RemoveEditedElementInstance(self.TagName, step.GetId())
        if next and previous:
            wire = self.ConnectConnectors(next, previous)
            return wire
        else:
            return None

#-------------------------------------------------------------------------------
#                          Mouse event functions
#-------------------------------------------------------------------------------

    def OnViewerLeftDown(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerLeftDown(self, event)
        elif self.Mode == MODE_SELECTION:
            if event.ShiftDown() and not event.ControlDown() and self.SelectedElement is not None:
                element = self.FindElement(event, True)
                if element and not self.IsWire(element):
                    if isinstance(self.SelectedElement, Graphic_Group):
                        self.SelectedElement.SelectElement(element)
                    else:
                        group = Graphic_Group(self)
                        self.SelectedElement.SetSelected(False)
                        group.SelectElement(self.SelectedElement)
                        group.SelectElement(element)
                        self.SelectedElement = group
                    elements = self.SelectedElement.GetElements()
                    if len(elements) == 0:
                        self.SelectedElement = element
                    elif len(elements) == 1:
                        self.SelectedElement = elements[0]
                    self.SelectedElement.SetSelected(True)
            else:
                element = self.FindElement(event)
                if self.SelectedElement and self.SelectedElement != element:
                    if self.IsWire(self.SelectedElement):
                        self.SelectedElement.SetSelectedSegment(None)
                    else:
                        self.SelectedElement.SetSelected(False)
                    self.SelectedElement = None
                if element:
                    self.SelectedElement = element
                    self.SelectedElement.OnLeftDown(event, self.GetLogicalDC(), self.Scaling)
                    self.SelectedElement.Refresh()
                else:
                    self.rubberBand.Reset()
                    self.rubberBand.OnLeftDown(event, self.GetLogicalDC(), self.Scaling)
        elif self.Mode == MODE_COMMENT:
            self.rubberBand.Reset()
            self.rubberBand.OnLeftDown(event, self.GetLogicalDC(), self.Scaling)
        event.Skip()

    def OnViewerLeftUp(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerLeftUp(self, event)
        elif self.rubberBand.IsShown():
            if self.Mode == MODE_SELECTION:
                elements = self.SearchElements(self.rubberBand.GetCurrentExtent())
                self.rubberBand.OnLeftUp(event, self.GetLogicalDC(), self.Scaling)
                if len(elements) > 0:
                    self.SelectedElement = Graphic_Group(self)
                    self.SelectedElement.SetElements(elements)
                    self.SelectedElement.SetSelected(True)
            elif self.Mode == MODE_COMMENT:
                bbox = self.rubberBand.GetCurrentExtent()
                self.rubberBand.OnLeftUp(event, self.GetLogicalDC(), self.Scaling)
                wx.CallAfter(self.AddComment, bbox)
        elif self.Mode == MODE_INITIALSTEP:
            wx.CallAfter(self.AddInitialStep, GetScaledEventPosition(event, self.GetLogicalDC(), self.Scaling))
        elif self.Mode == MODE_SELECTION and self.SelectedElement:
            if self.IsWire(self.SelectedElement):
                self.SelectedElement.SetSelectedSegment(0)
            else:
                self.SelectedElement.OnLeftUp(event, self.GetLogicalDC(), self.Scaling)
                self.SelectedElement.Refresh()
            wx.CallAfter(self.SetCurrentCursor, 0)
        elif self.Mode == MODE_WIRE and self.SelectedElement:
            self.SelectedElement.ResetPoints()
            self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
            self.SelectedElement.GeneratePoints()
            self.SelectedElement.RefreshModel()
            self.SelectedElement.SetSelected(True)
        event.Skip()
    
    def OnViewerRightUp(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerRightUp(self, event)
        else:
            element = self.FindElement(event)
            if element:
                if self.SelectedElement and self.SelectedElement != element:
                    self.SelectedElement.SetSelected(False)
                self.SelectedElement = element
                if self.IsWire(self.SelectedElement):
                    self.SelectedElement.SetSelectedSegment(0)
                else:
                    self.SelectedElement.SetSelected(True)
                    self.SelectedElement.OnRightUp(event, self.GetLogicalDC(), self.Scaling)
                    self.SelectedElement.Refresh()
                wx.CallAfter(self.SetCurrentCursor, 0)
        event.Skip()
    
    def OnViewerLeftDClick(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerLeftDClick(self, event)
        elif self.Mode == MODE_SELECTION and self.SelectedElement:
            self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling)
            self.Refresh(False)
        event.Skip()
    
    def OnViewerMotion(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnViewerMotion(self, event)
        else:
            if self.rubberBand.IsShown():
                self.rubberBand.OnMotion(event, self.GetLogicalDC(), self.Scaling)
            elif self.Mode == MODE_SELECTION and self.SelectedElement:
                if not self.IsWire(self.SelectedElement) and not isinstance(self.SelectedElement, Graphic_Group):
                    self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
                    self.SelectedElement.Refresh()
            elif self.Mode == MODE_WIRE and self.SelectedElement:
                self.SelectedElement.ResetPoints()
                self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
                self.SelectedElement.GeneratePoints()
                self.SelectedElement.Refresh()
            self.UpdateScrollPos(event)
        event.Skip()

#-------------------------------------------------------------------------------
#                          Keyboard event functions
#-------------------------------------------------------------------------------

    def OnChar(self, event):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.OnChar(self, event)
        else:
            xpos, ypos = self.GetScrollPos(wx.HORIZONTAL), self.GetScrollPos(wx.VERTICAL)
            xmax = self.GetScrollRange(wx.HORIZONTAL) - self.GetScrollThumb(wx.HORIZONTAL)
            ymax = self.GetScrollRange(wx.VERTICAL) - self.GetScrollThumb(wx.VERTICAL)
            keycode = event.GetKeyCode()
            if self.Scaling:
                scaling = self.Scaling
            else:
                scaling = (8, 8)
            if keycode == wx.WXK_DELETE and self.SelectedElement:
                self.SelectedElement.Delete()
                self.SelectedElement = None
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            elif keycode == wx.WXK_LEFT:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(0, ypos)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(-scaling[0], 0)
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(-scaling[0], 0)), False)
            elif keycode == wx.WXK_RIGHT:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(xmax, ypos)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(scaling[0], 0)
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(scaling[0], 0)), False)
            elif keycode == wx.WXK_UP:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(xpos, 0)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(0, -scaling[1])
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(0, -scaling[1])), False)
            elif keycode == wx.WXK_DOWN:
                if event.ControlDown() and event.ShiftDown():
                    self.Scroll(xpos, ymax)
                elif event.ControlDown():
                    event.Skip()
                elif self.SelectedElement:
                    self.SelectedElement.Move(0, scaling[1])
                    self.SelectedElement.RefreshModel()
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(0, scaling[1])), False)
            else:
                event.Skip()
        
#-------------------------------------------------------------------------------
#                          Adding element functions
#-------------------------------------------------------------------------------

    def AddInitialStep(self, pos):
        dialog = SFCStepNameDialog(self.ParentWindow, _("Please enter step name"), _("Add a new initial step"), "", wx.OK|wx.CANCEL)
        dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug))
        dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
        dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)])
        if dialog.ShowModal() == wx.ID_OK:
            id = self.GetNewId()
            name = dialog.GetValue()
            step = SFC_Step(self, name, True, id)
            min_width, min_height = step.GetMinSize()
            step.SetPosition(pos.x, pos.y)
            width, height = step.GetSize()
            step.SetSize(max(min_width, width), max(min_height, height))
            self.AddBlock(step)
            self.Controler.AddEditedElementStep(self.TagName, id)
            self.RefreshStepModel(step)
            self.RefreshBuffer()
            self.RefreshScrollBars()
            self.Refresh(False)
        dialog.Destroy()

    def AddStep(self):
        if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, SFC_Step):
            dialog = SFCStepNameDialog(self.ParentWindow, _("Add a new step"), _("Please enter step name"), "", wx.OK|wx.CANCEL)
            dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug))
            dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
            dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)])
            if dialog.ShowModal() == wx.ID_OK:
                name = dialog.GetValue()
                if self.IsWire(self.SelectedElement):
                    self.SelectedElement.SetSelectedSegment(None)
                    previous = self.SelectedElement.EndConnected
                    next = self.SelectedElement.StartConnected
                    self.SelectedElement.Clean()
                    self.RemoveWire(self.SelectedElement)
                else:
                    connectors = self.SelectedElement.GetConnectors()
                    if connectors["output"]:
                        previous = connectors["output"]
                        wires = previous.GetWires()
                        if len(wires) != 1:
                            return
                        wire = wires[0][0]
                        next = wire.StartConnected
                        wire.Clean()
                        self.RemoveWire(wire)
                    else:
                        self.SelectedElement.AddOutput()
                        connectors = self.SelectedElement.GetConnectors()
                        self.RefreshStepModel(self.SelectedElement)
                        previous = connectors["output"]
                        next = None
                previous_block = previous.GetParentBlock()
                if isinstance(previous_block, SFC_Step) or isinstance(previous_block, SFC_Divergence) and previous_block.GetType() in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]:
                    transition = self.CreateTransition(previous)
                    transition_connectors = transition.GetConnectors()
                    step = self.CreateStep(name, transition_connectors["output"], next)
                else:
                    step = self.CreateStep(name, previous)
                    step.AddOutput()
                    step.RefreshModel()
                    step_connectors = step.GetConnectors()
                    transition = self.CreateTransition(step_connectors["output"], next)
                if self.IsWire(self.SelectedElement):
                    self.SelectedElement = wire
                    self.SelectedElement.SetSelectedSegment(0)
                else:
                    self.SelectedElement.SetSelected(False)
                    self.SelectedElement = step
                    self.SelectedElement.SetSelected(True)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()
    
    def AddStepAction(self):
        if isinstance(self.SelectedElement, SFC_Step):
            connectors = self.SelectedElement.GetConnectors()
            if not connectors["action"]:
                dialog = ActionBlockDialog(self.ParentWindow)
                dialog.SetQualifierList(self.Controler.GetQualifierTypes())
                dialog.SetActionList(self.Controler.GetEditedElementActions(self.TagName, self.Debug))
                dialog.SetVariableList(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
                if dialog.ShowModal() == wx.ID_OK:
                    actions = dialog.GetValues()
                    self.SelectedElement.AddAction()
                    self.RefreshStepModel(self.SelectedElement)
                    connectors = self.SelectedElement.GetConnectors()
                    pos = connectors["action"].GetPosition(False)
                    id = self.GetNewId()
                    actionblock = SFC_ActionBlock(self, [], id)
                    actionblock.SetPosition(pos.x + SFC_WIRE_MIN_SIZE, pos.y - SFC_STEP_DEFAULT_SIZE[1] / 2)
                    actionblock_connector = actionblock.GetConnector()
                    wire = self.ConnectConnectors(actionblock_connector, connectors["action"])
                    wire.SetPoints([wx.Point(pos.x + SFC_WIRE_MIN_SIZE, pos.y), wx.Point(pos.x, pos.y)])
                    actionblock.SetActions(actions)
                    self.AddBlock(actionblock)
                    self.Controler.AddEditedElementActionBlock(self.TagName, id)
                    self.RefreshActionBlockModel(actionblock)
                    self.RefreshBuffer()
                    self.RefreshScrollBars()
                    self.Refresh(False)
                dialog.Destroy()
    
    def AddDivergence(self):
        if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step):        
            dialog = SFCDivergenceDialog(self.ParentWindow)
            dialog.SetPreviewFont(self.GetFont())
            if dialog.ShowModal() == wx.ID_OK:
                value = dialog.GetValues()
                if value["type"] == SELECTION_DIVERGENCE:
                    if self.SelectedElement in self.Wires and isinstance(self.SelectedElement.EndConnected.GetParentBlock(), SFC_Step):
                        self.SelectedElement.SetSelectedSegment(None)
                        previous = self.SelectedElement.EndConnected
                        next = self.SelectedElement.StartConnected
                        self.SelectedElement.Clean()
                        self.RemoveWire(self.SelectedElement)
                        self.SelectedElement = None
                    elif isinstance(self.SelectedElement, SFC_Step):
                        connectors = self.SelectedElement.GetConnectors()
                        if connectors["output"]:
                            previous = connectors["output"]
                            wires = previous.GetWires()
                            if len(wires) != 1:
                                return
                            wire = wires[0][0]
                            next = wire.StartConnected
                            wire.Clean()
                            self.RemoveWire(wire)
                        else:
                            self.SelectedElement.AddOutput()
                            connectors = self.SelectedElement.GetConnectors()
                            self.RefreshStepModel(self.SelectedElement)
                            previous = connectors["output"]
                            next = None
                    else:
                        return
                    id = self.GetNewId()
                    divergence = SFC_Divergence(self, SELECTION_DIVERGENCE, value["number"], id)
                    pos = previous.GetPosition(False)
                    previous_block = previous.GetParentBlock()
                    wire_size = GetWireSize(previous_block)
                    divergence.SetPosition(pos.x, pos.y + wire_size)
                    divergence_connectors = divergence.GetConnectors()
                    wire = self.ConnectConnectors(divergence_connectors["inputs"][0], previous)
                    previous_block.RefreshOutputPosition()
                    wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                    self.AddBlock(divergence)
                    self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"])
                    self.RefreshDivergenceModel(divergence)
                    for index, connector in enumerate(divergence_connectors["outputs"]):
                        if next:
                            wire = self.ConnectConnectors(next, connector)
                            pos = connector.GetPosition(False)
                            next_pos = next.GetPosition(False)
                            next_block = next.GetParentBlock()
                            divergence.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
                            divergence.RefreshConnectedPosition(connector)
                            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                            next_block.RefreshModel()
                            next = None
                        else:
                            transition = self.CreateTransition(connector)
                            transition_connectors = transition.GetConnectors()
                            step = self.CreateStep("Step", transition_connectors["output"])
                elif value["type"] == SIMULTANEOUS_DIVERGENCE:
                    if self.SelectedElement in self.Wires and isinstance(self.SelectedElement.EndConnected.GetParentBlock(), SFC_Transition):
                        self.SelectedElement.SetSelectedSegment(None)
                        previous = self.SelectedElement.EndConnected
                        next = self.SelectedElement.StartConnected
                        self.SelectedElement.Clean()
                        self.RemoveWire(self.SelectedElement)
                        self.SelectedElement = None
                    elif isinstance(self.SelectedElement, SFC_Step):
                        connectors = self.SelectedElement.GetConnectors()
                        if connectors["output"]:
                            previous = connectors["output"]
                            wires = previous.GetWires()
                            if len(wires) != 1:
                                return
                            wire = wires[0][0]
                            next = wire.StartConnected
                            wire.Clean()
                            self.RemoveWire(wire)
                        else:
                            self.SelectedElement.AddOutput()
                            connectors = self.SelectedElement.GetConnectors()
                            self.RefreshStepModel(self.SelectedElement)
                            previous = connectors["output"]
                            next = None
                        transition = self.CreateTransition(previous)
                        transition_connectors = transition.GetConnectors()
                        previous = transition_connectors["output"]
                    else:
                        return
                    id = self.GetNewId()
                    divergence = SFC_Divergence(self, SIMULTANEOUS_DIVERGENCE, value["number"], id)
                    pos = previous.GetPosition(False)
                    previous_block = previous.GetParentBlock()
                    wire_size = GetWireSize(previous_block)
                    divergence.SetPosition(pos.x, pos.y + wire_size)
                    divergence_connectors = divergence.GetConnectors()
                    wire = self.ConnectConnectors(divergence_connectors["inputs"][0], previous)
                    previous_block.RefreshOutputPosition()
                    wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                    self.AddBlock(divergence)
                    self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"])
                    self.RefreshDivergenceModel(divergence)
                    for index, connector in enumerate(divergence_connectors["outputs"]):
                        if next:
                            wire = self.ConnectConnectors(next, connector)
                            pos = connector.GetPosition(False)
                            next_pos = next.GetPosition(False)
                            next_block = next.GetParentBlock()
                            divergence.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
                            divergence.RefreshConnectedPosition(connector)
                            wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                            next_block.RefreshModel()
                            next = None
                        else:
                            step = self.CreateStep("Step", connector)
                elif isinstance(self.SelectedElement, Graphic_Group) and len(self.SelectedElement.GetElements()) > 1:
                    next = None
                    for element in self.SelectedElement.GetElements():
                        connectors = element.GetConnectors()
                        if not isinstance(element, SFC_Step) or connectors["output"] and next:
                            return
                        elif connectors["output"] and not next:
                            wires = connectors["output"].GetWires()
                            if len(wires) != 1:
                                return
                            if value["type"] == SELECTION_CONVERGENCE:
                                transition = wires[0][0].StartConnected.GetParentBlock()
                                transition_connectors = transition.GetConnectors()
                                wires = transition_connectors["output"].GetWires()
                                if len(wires) != 1:
                                    return
                            wire = wires[0][0]
                            next = wire.StartConnected
                            wire.Clean()
                            self.RemoveWire(wire)
                    inputs = []
                    for input in self.SelectedElement.GetElements():
                        input_connectors = input.GetConnectors()
                        if not input_connectors["output"]:
                            input.AddOutput()
                            input.RefreshModel()
                            input_connectors = input.GetConnectors()
                            if value["type"] == SELECTION_CONVERGENCE:
                                transition = self.CreateTransition(input_connectors["output"])
                                transition_connectors = transition.GetConnectors()
                                inputs.append(transition_connectors["output"])
                            else:
                                inputs.append(input_connectors["output"])
                        elif value["type"] == SELECTION_CONVERGENCE:
                            wires = input_connectors["output"].GetWires()
                            transition = wires[0][0].StartConnected.GetParentBlock()
                            transition_connectors = transition.GetConnectors()
                            inputs.append(transition_connectors["output"])
                        else:
                            inputs.append(input_connectors["output"])
                    id = self.GetNewId()
                    divergence = SFC_Divergence(self, value["type"], len(inputs), id)
                    pos = inputs[0].GetPosition(False)
                    divergence.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
                    divergence_connectors = divergence.GetConnectors()
                    for i, input in enumerate(inputs):
                        pos = input.GetPosition(False)
                        wire = self.ConnectConnectors(divergence_connectors["inputs"][i], input)
                        wire_size = GetWireSize(input)
                        wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                        input_block = input.GetParentBlock()
                        input_block.RefreshOutputPosition()
                    divergence.RefreshPosition()
                    pos = divergence_connectors["outputs"][0].GetRelPosition()
                    divergence.MoveConnector(divergence_connectors["outputs"][0], - pos.x)
                    self.AddBlock(divergence)
                    self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"])
                    self.RefreshDivergenceModel(divergence)
                    if next:
                        wire = self.ConnectConnectors(next, divergence_connectors["outputs"][0])
                        pos = divergence_connectors["outputs"][0].GetPosition(False)
                        next_pos = next.GetPosition(False)
                        next_block = next.GetParentBlock()
                        divergence.RefreshOutputPosition((0, pos.y + SFC_WIRE_MIN_SIZE - next_pos.y))
                        divergence.RefreshConnectedPosition(divergence_connectors["outputs"][0])
                        wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                        next_block.RefreshModel()
                    else:
                        if value["type"] == SELECTION_CONVERGENCE:
                            previous = divergence_connectors["outputs"][0]
                        else:
                            transition = self.CreateTransition(divergence_connectors["outputs"][0])
                            transition_connectors = transition.GetConnectors()
                            previous = transition_connectors["output"]
                        self.CreateStep("Step", previous)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()
    
    def AddDivergenceBranch(self, divergence):
        if isinstance(divergence, SFC_Divergence):
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                divergence.AddBranch()
                self.RefreshDivergenceModel(divergence)
            else:
                type = divergence.GetType()
                if type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
                    divergence.AddBranch()
                    divergence_connectors = divergence.GetConnectors()
                    if type == SELECTION_DIVERGENCE:
                        transition = self.CreateTransition(divergence_connectors["outputs"][-1])
                        transition_connectors = transition.GetConnectors()
                        previous = transition_connectors["output"]
                    else:
                        previous = divergence_connectors["outputs"][-1]
                    step = self.CreateStep("Step", previous)
            self.RefreshBuffer()
            self.RefreshScrollBars()
            self.Refresh(False)
    
    def RemoveDivergenceBranch(self, divergence):
        if isinstance(divergence, SFC_Divergence):
            if self.GetDrawingMode() == FREEDRAWING_MODE:
                divergence.RemoveHandledBranch()
                self.RefreshDivergenceModel(divergence)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
    
    def AddJump(self):
        if isinstance(self.SelectedElement, SFC_Step) and not self.SelectedElement.Output:
            choices = []
            for block in self.Blocks:
                if isinstance(block, SFC_Step):
                    choices.append(block.GetName())
            dialog = wx.SingleChoiceDialog(self.ParentWindow, 
                  _("Add a new jump"), _("Please choose a target"), 
                  choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
            if dialog.ShowModal() == wx.ID_OK:
                value = dialog.GetStringSelection()
                self.SelectedElement.AddOutput()
                self.RefreshStepModel(self.SelectedElement)
                step_connectors = self.SelectedElement.GetConnectors()
                transition = self.CreateTransition(step_connectors["output"])
                transition_connectors = transition.GetConnectors()
                id = self.GetNewId()
                jump = SFC_Jump(self, value, id)
                pos = transition_connectors["output"].GetPosition(False)
                jump.SetPosition(pos.x, pos.y + SFC_WIRE_MIN_SIZE)
                self.AddBlock(jump)
                self.Controler.AddEditedElementJump(self.TagName, id)
                jump_connector = jump.GetConnector()
                wire = self.ConnectConnectors(jump_connector, transition_connectors["output"])
                transition.RefreshOutputPosition()
                wire.SetPoints([wx.Point(pos.x, pos.y + SFC_WIRE_MIN_SIZE), wx.Point(pos.x, pos.y)])
                self.RefreshJumpModel(jump)
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()

    def EditStepContent(self, step):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.EditStepContent(self, step)
        else:
            dialog = SFCStepNameDialog(self.ParentWindow, _("Edit step name"), _("Please enter step name"), step.GetName(), wx.OK|wx.CANCEL)
            dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug))
            dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug))
            dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step) and block.GetName() != step.GetName()])
            if dialog.ShowModal() == wx.ID_OK:
                value = dialog.GetValue()
                step.SetName(value)
                min_size = step.GetMinSize()
                size = step.GetSize()
                step.UpdateSize(max(min_size[0], size[0]), max(min_size[1], size[1]))
                step.RefreshModel()
                self.RefreshBuffer()
                self.RefreshScrollBars()
                self.Refresh(False)
            dialog.Destroy()

#-------------------------------------------------------------------------------
#                          Delete element functions
#-------------------------------------------------------------------------------

    def DeleteStep(self, step):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteStep(self, step)
        else:
            step_connectors = step.GetConnectors()
            if not step.GetInitial() or not step_connectors["output"]:
                previous = step.GetPreviousConnector()
                if previous:
                    previous_block = previous.GetParentBlock()
                else:
                    previous_block = None
                next = step.GetNextConnector()
                if next:
                    next_block = next.GetParentBlock()
                else:
                    next_block = None
                if isinstance(next_block, SFC_Transition):
                    self.RemoveTransition(next_block)
                    next = step.GetNextConnector()
                    if next:
                        next_block = next.GetParentBlock()
                    else:
                        next_block = None
                elif isinstance(previous_block, SFC_Transition):
                    self.RemoveTransition(previous_block)
                    previous = step.GetPreviousConnector()
                    if previous:
                        previous_block = previous.GetParentBlock()
                    else:
                        previous_block = None
                wire = self.RemoveStep(step)
                self.SelectedElement = None
                if next_block:
                    if isinstance(next_block, SFC_Divergence) and next_block.GetType() == SIMULTANEOUS_CONVERGENCE and isinstance(previous_block, SFC_Divergence) and previous_block.GetType() == SIMULTANEOUS_DIVERGENCE:
                        wire.Clean()
                        self.RemoveWire(wire)
                        next_block.RemoveBranch(next)
                        if next_block.GetBranchNumber() < 2:
                            self.DeleteDivergence(next_block)
                        else:
                            next_block.RefreshModel()
                        previous_block.RemoveBranch(previous)
                        if previous_block.GetBranchNumber() < 2:
                            self.DeleteDivergence(previous_block)
                        else:
                            previous_block.RefreshModel()
                    else:
                        pos = previous.GetPosition(False)
                        next_pos = next.GetPosition(False)
                        wire_size = GetWireSize(previous_block)
                        previous_block.RefreshOutputPosition((0, pos.y + wire_size - next_pos.y))
                        wire.SetPoints([wx.Point(pos.x, pos.y + wire_size), wx.Point(pos.x, pos.y)])
                        if isinstance(next_block, SFC_Divergence):
                            next_block.RefreshPosition()
                        previous_block.RefreshOutputModel(True)
                else:
                    if isinstance(previous_block, SFC_Step):
                        previous_block.RemoveOutput()
                        self.RefreshStepModel(previous_block)
                    elif isinstance(previous_block, SFC_Divergence):
                        if previous_block.GetType() in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
                            self.DeleteDivergence(previous_block)
                        else:
                            previous_block.RemoveBranch(previous)
                            if previous_block.GetBranchNumber() < 2:
                                self.DeleteDivergence(previous_block)
                            else:
                                self.RefreshDivergenceModel(previous_block)
        
    def DeleteTransition(self, transition):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteTransition(self, transition)
        else:
            previous = transition.GetPreviousConnector()
            previous_block = previous.GetParentBlock()
            next = transition.GetNextConnector()
            next_block = next.GetParentBlock()
            if isinstance(previous_block, SFC_Divergence) and previous_block.GetType() == SELECTION_DIVERGENCE and isinstance(next_block, SFC_Divergence) and next_block.GetType() == SELECTION_CONVERGENCE:
                wires = previous.GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                wire.Clean()
                self.RemoveWire(wire)
                wires = next.GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                wire.Clean()
                self.RemoveWire(wire)
                transition.Clean()
                self.RemoveBlock(transition)
                self.Controler.RemoveEditedElementInstance(self.TagName, transition.GetId())
                previous_block.RemoveBranch(previous)
                if previous_block.GetBranchNumber() < 2:
                    self.DeleteDivergence(previous_block)
                else:
                    self.RefreshDivergenceModel(previous_block)
                next_block.RemoveBranch(next)
                if next_block.GetBranchNumber() < 2:
                    self.DeleteDivergence(next_block)
                else:
                    self.RefreshDivergenceModel(next_block)
            
    def DeleteDivergence(self, divergence):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteDivergence(self, divergence)
        else:
            connectors = divergence.GetConnectors()
            type = divergence.GetType()
            if type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
                wires = connectors["outputs"][0].GetWires()
                if len(wires) > 1:
                    return
                elif len(wires) == 1:
                    next = wires[0][0].StartConnected
                    next_block = next.GetParentBlock()
                    wire = wires[0][0]
                    wire.Clean()
                    self.RemoveWire(wire)
                else:
                    next = None
                    next_block = None
                for index, connector in enumerate(connectors["inputs"]):
                    if next and index == 0:
                        wires = connector.GetWires()
                        wire = wires[0][0]
                        previous = wires[0][0].EndConnected
                        wire.Clean()
                        self.RemoveWire(wire)
                    else:
                        if type == SELECTION_CONVERGENCE:
                            wires = connector.GetWires()
                            previous_block = wires[0][0].EndConnected.GetParentBlock()
                            self.RemoveTransition(previous_block)
                        wires = connector.GetWires()
                        wire = wires[0][0]
                        previous_connector = wire.EndConnected
                        previous_block = previous_connector.GetParentBlock()
                        wire.Clean()
                        self.RemoveWire(wire)
                        if isinstance(previous_block, SFC_Step):
                            previous_block.RemoveOutput()
                            self.RefreshStepModel(previous_block)
                        elif isinstance(previous_block, SFC_Divergence):
                            if previous_block.GetType() in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]:
                                previous_block.RemoveBranch(previous_connector)
                                if previous_block.GetBranchNumber() < 2:
                                    self.DeleteDivergence(previous_block)
                                else:
                                    self.RefreshDivergenceModel(previous_block)
                            else:
                                self.DeleteDivergence(previous_block)
                divergence.Clean()
                self.RemoveBlock(divergence)
                self.Controler.RemoveEditedElementInstance(self.TagName, divergence.GetId())
                if next:
                    wire = self.ConnectConnectors(next, previous)
                    previous_block = previous.GetParentBlock()
                    previous_pos = previous.GetPosition(False)
                    next_pos = next.GetPosition(False)
                    wire_size = GetWireSize(previous_block)
                    previous_block.RefreshOutputPosition((0, previous_pos.y + wire_size - next_pos.y))
                    wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), 
                        wx.Point(previous_pos.x, previous_pos.y)])
                    if isinstance(next_block, SFC_Divergence):
                        next_block.RefreshPosition()
                    previous_block.RefreshOutputModel(True)
            elif divergence.GetBranchNumber() == 1:
                wires = connectors["inputs"][0].GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                previous = wire.EndConnected
                previous_block = previous.GetParentBlock()
                wire.Clean()
                self.RemoveWire(wire)
                wires = connectors["outputs"][0].GetWires()
                if len(wires) != 1:
                    return
                wire = wires[0][0]
                next = wire.StartConnected
                next_block = next.GetParentBlock()
                wire.Clean()
                self.RemoveWire(wire)
                divergence.Clean()
                self.RemoveBlock(divergence)
                self.Controler.RemoveEditedElementInstance(self.TagName, divergence.GetId())
                wire = self.ConnectConnectors(next, previous)
                previous_pos = previous.GetPosition(False)
                next_pos = next.GetPosition(False)
                wire_size = GetWireSize(previous_block)
                previous_block.RefreshOutputPosition((previous_pos.x - next_pos.x, previous_pos.y + wire_size - next_pos.y))
                wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), 
                    wx.Point(previous_pos.x, previous_pos.y)])
                if isinstance(next_block, SFC_Divergence):
                    next_block.RefreshPosition()
                previous_block.RefreshOutputModel(True)
            
    def DeleteJump(self, jump):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteJump(self, jump)
        else:
            previous = jump.GetPreviousConnector()
            previous_block = previous.GetParentBlock()
            if isinstance(previous_block, SFC_Transition):
                self.RemoveTransition(previous_block)
                previous = jump.GetPreviousConnector()
                if previous:
                    previous_block = previous.GetParentBlock()
                else:
                    previous_block = None
            wires = previous.GetWires()
            if len(wires) != 1:
                return
            wire = wires[0][0]
            wire.Clean()
            self.RemoveWire(wire)
            jump.Clean()
            self.RemoveBlock(jump)
            self.Controler.RemoveEditedElementInstance(self.TagName, jump.GetId())
            if isinstance(previous_block, SFC_Step):
                previous_block.RemoveOutput()
                self.RefreshStepModel(previous_block)
            elif isinstance(previous_block, SFC_Divergence):
                if previous_block.GetType() in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]:
                    self.DeleteDivergence(previous_block)
                else:
                    previous_block.RemoveBranch(previous)
                    if previous_block.GetBranchNumber() < 2:
                        self.DeleteDivergence(previous_block)
                    else:
                        previous_block.RefreshModel()
            
    def DeleteActionBlock(self, actionblock):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteActionBlock(self, actionblock)
        else:
            connector = actionblock.GetConnector()
            wires = connector.GetWires()
            if len(wires) != 1:
                return
            wire = wires[0][0]
            step = wire.EndConnected.GetParentBlock()
            wire.Clean()
            self.RemoveWire(wire)
            actionblock.Clean()
            self.RemoveBlock(actionblock)
            self.Controler.RemoveEditedElementInstance(self.TagName, actionblock.GetId())
            step.RemoveAction()
            self.RefreshStepModel(step)
            step.RefreshOutputPosition()
            step.RefreshOutputModel(True)
            
    def DeleteWire(self, wire):
        if self.GetDrawingMode() == FREEDRAWING_MODE:
            Viewer.DeleteWire(self, wire)

#-------------------------------------------------------------------------------
#                          Model update functions
#-------------------------------------------------------------------------------

    def RefreshBlockModel(self, block):
        blockid = block.GetId()
        infos = {}
        infos["type"] = block.GetType()
        infos["name"] = block.GetName()
        infos["x"], infos["y"] = block.GetPosition()
        infos["width"], infos["height"] = block.GetSize()
        infos["connectors"] = block.GetConnectors()
        self.Controler.SetEditedElementBlockInfos(self.TagName, blockid, infos)