Adding Dialog for Step in free drawing
authorlbessard
Tue, 14 Aug 2007 14:53:06 +0200
changeset 71 0578bc212c20
parent 70 0e48629c1e6d
child 72 73212220ad22
Adding Dialog for Step in free drawing
Dialogs.py
PLCControler.py
PLCGenerator.py
PLCOpenEditor.py
SFCViewer.py
Viewer.py
graphics/LD_Objects.py
graphics/SFC_Objects.py
--- a/Dialogs.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/Dialogs.py	Tue Aug 14 14:53:06 2007 +0200
@@ -816,7 +816,7 @@
 
     def _init_ctrls(self, prnt, title, labels):
         wx.Dialog.__init__(self, id=ID_LDELEMENTDIALOG,
-              name='VariablePropertiesDialog', parent=prnt, pos=wx.Point(376, 223),
+              name='LDElementDialog', parent=prnt, pos=wx.Point(376, 223),
               size=wx.Size(350, 260), style=wx.DEFAULT_DIALOG_STYLE,
               title=title)
         self.SetClientSize(wx.Size(350, 260))
@@ -1132,6 +1132,232 @@
 
 
 #-------------------------------------------------------------------------------
+#                          Edit Step Content Dialog
+#-------------------------------------------------------------------------------
+
+[ID_STEPCONTENTDIALOG, ID_STEPCONTENTDIALOGSPACER, 
+ ID_STEPCONTENTDIALOGNAME, ID_STEPCONTENTDIALOGPREVIEW, 
+ ID_STEPCONTENTDIALOGCHECKBOX1, ID_STEPCONTENTDIALOGCHECKBOX2,
+ ID_STEPCONTENTDIALOGCHECKBOX3, ID_STEPCONTENTDIALOGSTATICTEXT1, 
+ ID_STEPCONTENTDIALOGSTATICTEXT2, ID_STEPCONTENTDIALOGSTATICTEXT3, 
+] = [wx.NewId() for _init_ctrls in range(10)]
+
+class StepContentDialog(wx.Dialog):
+    def _init_coll_flexGridSizer1_Items(self, parent):
+        parent.AddSizer(self.MainSizer, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
+        parent.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT)
+        
+    def _init_coll_flexGridSizer1_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableRow(0)
+    
+    def _init_coll_MainSizer_Items(self, parent):
+        parent.AddSizer(self.LeftGridSizer, 1, border=5, flag=wx.GROW|wx.RIGHT)
+        parent.AddSizer(self.RightGridSizer, 1, border=5, flag=wx.GROW|wx.LEFT)
+    
+    def _init_coll_LeftGridSizer_Items(self, parent):
+        parent.AddWindow(self.staticText1, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.Name, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.staticText2, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.checkBox1, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.checkBox2, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.checkBox3, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.Spacer, 0, border=0, flag=wx.GROW)
+        
+    def _init_coll_LeftGridSizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableRow(6)
+            
+    def _init_coll_RightGridSizer_Items(self, parent):
+        parent.AddWindow(self.staticText3, 0, border=0, flag=wx.GROW)
+        parent.AddWindow(self.Preview, 0, border=0, flag=wx.GROW)
+        
+    def _init_coll_RightGridSizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableRow(1)
+
+    def _init_sizers(self):
+        self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
+        self.MainSizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=7, vgap=5)
+        self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
+
+        self._init_coll_flexGridSizer1_Items(self.flexGridSizer1)
+        self._init_coll_flexGridSizer1_Growables(self.flexGridSizer1)
+        self._init_coll_MainSizer_Items(self.MainSizer)
+        self._init_coll_LeftGridSizer_Items(self.LeftGridSizer)
+        self._init_coll_LeftGridSizer_Growables(self.LeftGridSizer)
+        self._init_coll_RightGridSizer_Items(self.RightGridSizer)
+        self._init_coll_RightGridSizer_Growables(self.RightGridSizer)
+
+        self.SetSizer(self.flexGridSizer1)
+
+    def _init_ctrls(self, prnt):
+        wx.Dialog.__init__(self, id=ID_STEPCONTENTDIALOG,
+              name='StepContentDialog', parent=prnt, pos=wx.Point(376, 223),
+              size=wx.Size(400, 250), style=wx.DEFAULT_DIALOG_STYLE,
+              title='Edit Step')
+        self.SetClientSize(wx.Size(400, 250))
+
+        self.staticText1 = wx.StaticText(id=ID_STEPCONTENTDIALOGSTATICTEXT1,
+              label='Name:', name='staticText1', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0)
+
+        self.staticText2 = wx.StaticText(id=ID_STEPCONTENTDIALOGSTATICTEXT2,
+              label='Connectors:', name='staticText2', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0)
+
+        self.staticText3 = wx.StaticText(id=ID_STEPCONTENTDIALOGSTATICTEXT3,
+              label='Preview:', name='staticText4', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0)
+
+        self.Name = wx.TextCtrl(id=ID_STEPCONTENTDIALOGNAME,
+              name='Name', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 24), style=0)
+        self.Bind(wx.EVT_TEXT, self.OnNameChanged, id=ID_STEPCONTENTDIALOGNAME)
+
+        self.checkBox1 = wx.CheckBox(id=ID_STEPCONTENTDIALOGCHECKBOX1,
+              label="Input", name='checkBox1', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(0, 24), style=0)
+        self.Bind(wx.EVT_CHECKBOX, self.OnConnectorsChanged, id=ID_STEPCONTENTDIALOGCHECKBOX1)
+        
+        self.checkBox2 = wx.CheckBox(id=ID_STEPCONTENTDIALOGCHECKBOX2,
+              label="Output", name='checkBox2', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(0, 24), style=0)
+        self.Bind(wx.EVT_CHECKBOX, self.OnConnectorsChanged, id=ID_STEPCONTENTDIALOGCHECKBOX2)
+        
+        self.checkBox3 = wx.CheckBox(id=ID_STEPCONTENTDIALOGCHECKBOX3,
+              label="Action", name='checkBox3', parent=self,
+              pos=wx.Point(0, 0), size=wx.Size(0, 24), style=0)
+        self.Bind(wx.EVT_CHECKBOX, self.OnConnectorsChanged, id=ID_STEPCONTENTDIALOGCHECKBOX3)
+        
+        self.Spacer = wx.Panel(id=ID_STEPCONTENTDIALOGSPACER,
+              name='Spacer', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
+
+        self.Preview = wx.Panel(id=ID_STEPCONTENTDIALOGPREVIEW,
+              name='Preview', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL|wx.SIMPLE_BORDER)
+        self.Preview.SetBackgroundColour(wx.Colour(255,255,255))
+        setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE)
+        setattr(self.Preview, "RefreshStepModel", lambda x:None)
+
+        self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
+        self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
+        
+        self.Bind(wx.EVT_PAINT, self.OnPaint)
+
+        self._init_sizers()
+
+    def __init__(self, parent, initial = False):
+        self._init_ctrls(parent)
+        self.Step = None
+        self.Initial = initial
+        self.MinStepSize = None
+    
+        self.PouNames = []
+        self.Variables = []
+        self.StepNames = []
+    
+    def OnOK(self, event):
+        step_name = self.Name.GetValue()
+        if step_name == "":
+            message = wx.MessageDialog(self, "You must type a name!", "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif not TestIdentifier(step_name):
+            message = wx.MessageDialog(self, "\"%s\" is not a valid identifier!"%step_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif step_name.upper() in IEC_KEYWORDS:
+            message = wx.MessageDialog(self, "\"%s\" is a keyword. It can't be used!"%step_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif step_name.upper() in self.PouNames:
+            message = wx.MessageDialog(self, "A pou with \"%s\" as name exists!"%step_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif step_name.upper() in self.Variables:
+            message = wx.MessageDialog(self, "A variable with \"%s\" as name exists!"%step_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        elif step_name.upper() in self.StepNames:
+            message = wx.MessageDialog(self, "\"%s\" step already exists!"%step_name, "Error", wx.OK|wx.ICON_ERROR)
+            message.ShowModal()
+            message.Destroy()
+        else:
+            self.EndModal(wx.ID_OK)
+    
+    def SetMinStepSize(self, size):
+        self.MinStepSize = size
+
+    def SetPouNames(self, pou_names):
+        self.PouNames = [pou_name.upper() for pou_name in pou_names]
+
+    def SetVariables(self, variables):
+        self.Variables = [var["Name"].upper() for var in variables]
+
+    def SetStepNames(self, step_names):
+        self.StepNames = [step_name.upper() for step_name in step_names]
+
+    def SetValues(self, values):
+        value_name = values.get("name", None)
+        if value_name:
+            self.Name.SetValue(value_name)
+        else:
+            self.Name.SetValue("")
+        self.checkBox1.SetValue(values.get("input", False))
+        self.checkBox2.SetValue(values.get("output", False))
+        self.checkBox3.SetValue(values.get("action", False))
+        self.RefreshPreview()
+        
+    def GetValues(self):
+        values = {}
+        values["name"] = self.Name.GetValue()
+        values["input"] = self.checkBox1.IsChecked()
+        values["output"] = self.checkBox2.IsChecked()
+        values["action"] = self.checkBox3.IsChecked()
+        values["width"], values["height"] = self.Step.GetSize()
+        return values
+    
+    def OnConnectorsChanged(self, event):
+        self.RefreshPreview()
+        event.Skip()
+
+    def OnNameChanged(self, event):
+        self.RefreshPreview()
+        event.Skip()
+    
+    def RefreshPreview(self):
+        dc = wx.ClientDC(self.Preview)
+        dc.Clear()
+        self.Step = SFC_Step(self.Preview, self.Name.GetValue(), self.Initial)
+        if self.checkBox1.IsChecked():
+            self.Step.AddInput()
+        else:
+            self.Step.RemoveInput()
+        if self.checkBox2.IsChecked():
+            self.Step.AddOutput()
+        else:
+            self.Step.RemoveOutput()
+        if self.checkBox3.IsChecked():
+            self.Step.AddAction()    
+        else:
+            self.Step.RemoveAction()
+        width, height = self.MinStepSize
+        min_width, min_height = self.Step.GetMinSize()
+        width, height = max(min_width, width), max(min_height, height)
+        self.Step.SetSize(width, height)
+        clientsize = self.Preview.GetClientSize()
+        x = (clientsize.width - width) / 2
+        y = (clientsize.height - height) / 2
+        self.Step.SetPosition(x, y)
+        self.Step.Draw(dc)
+
+    def OnPaint(self, event):
+        self.RefreshPreview()
+
+#-------------------------------------------------------------------------------
 #                          Edit Transition Content Dialog
 #-------------------------------------------------------------------------------
 
--- a/PLCControler.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/PLCControler.py	Tue Aug 14 14:53:06 2007 +0200
@@ -311,6 +311,15 @@
             # Analyze each pou 
             for pou in pous:
                 name = pou.getName()
+                if pou.interface:
+                    # Extract variables from every varLists
+                    for type, varlist in pou.getVars():
+                        for var in varlist.getVariable():
+                            var_type = var.getType().getValue()
+                            if not isinstance(var_type, (StringType, UnicodeType)):
+                                typename = var_type.getName()
+                                if typename in pounames and name not in self.PouUsingTree[typename]:
+                                    self.PouUsingTree[typename].append(name)
                 bodytype = pou.getBodyType()
                 # If pou is written in a graphical language
                 if bodytype in ["FBD","LD","SFC"]:
@@ -764,6 +773,7 @@
             pou.interface = plcopen.pou_interface()
         # Set Pou interface
         pou.setVars(self.ExtractVarLists(vars))
+        self.RefreshPouUsingTree()
         self.RefreshBlockTypes()
     
     # Replace the return type of the pou given by its name (only for functions)
--- a/PLCGenerator.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/PLCGenerator.py	Tue Aug 14 14:53:06 2007 +0200
@@ -32,6 +32,10 @@
 
 pouTypeNames = {"function" : "FUNCTION", "functionBlock" : "FUNCTION_BLOCK", "program" : "PROGRAM"}
 
+currentProject = None
+currentProgram = ""
+pouComputed = {}
+
 def ReIndentText(text, nb_spaces):
     compute = ""
     lines = text.splitlines()
@@ -88,6 +92,7 @@
                 type = var.getType().getValue()
                 if not isinstance(type, (StringType, UnicodeType)):
                     type = type.getName()
+                    GeneratePouProgram(type)
                 initial = var.getInitialValue()
                 if initial:
                     initial_value = initial.getValue()
@@ -152,6 +157,7 @@
             type = instance.getTypeName()
             block_infos = GetBlockType(type)
             if block_infos["type"] == "function":
+                GeneratePouProgram(type)
                 vars = []
                 for variable in instance.inputVariables.getVariable():
                     connections = variable.connectionPointIn.getConnections()
@@ -528,101 +534,12 @@
         program += self.Program
         program += "END_%s\n\n"%self.Type
         return program
-    
-    def GenerateConfiguration(self, configuration):
-        config = "\nCONFIGURATION %s\n"%configuration.getName()
-        for varlist in configuration.getGlobalVars():
-            config += "  VAR_GLOBAL"
-            if varlist.getRetain():
-                config += " RETAIN"
-            if varlist.getConstant():
-                config += " CONSTANT"
-            config += "\n"
-            for var in varlist.getVariable():
-                var_type = var.getType().getValue()
-                config += "    %s "%var.getName()
-                address = var.getAddress()
-                if address:
-                    config += "AT %s "%address
-                config += ": %s"%var_type
-                initial = var.getInitialValue()
-                if initial:
-                    value = str(initial.getValue())
-                    value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value)
-                    if var_type == "STRING":
-                        config += " := '%s'"%value
-                    elif var_type == "WSTRING":
-                        config += " := \"%s\""%value
-                    else:
-                        config += " := %s"%value
-                config += ";\n"
-            config += "  END_VAR\n"
-        for resource in configuration.getResource():
-            config += self.GenerateResource(resource)
-        config += "END_CONFIGURATION\n"
-        return config
-        
-    def GenerateResource(self, resource):
-        resrce = "\n  RESOURCE %s ON BEREMIZ\n"%resource.getName()
-        for varlist in resource.getGlobalVars():
-            resrce += "    VAR_GLOBAL"
-            if varlist.getRetain():
-                resrce += " RETAIN"
-            if varlist.getConstant():
-                resrce += " CONSTANT"
-            resrce += "\n"
-            for var in varlist.getVariable():
-                var_type = var.getType().getValue()
-                resrce += "      %s "%var.getName()
-                address = var.getAddress()
-                if address:
-                    resrce += "AT %s "%address
-                resrce += ": %s"%var.getType().getValue()
-                initial = var.getInitialValue()
-                if initial:
-                    value = str(initial.getValue())
-                    value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value)
-                    if var_type == "STRING":
-                        resrce += " := '%s'"%value
-                    elif var_type == "WSTRING":
-                        resrce += " := \"%s\""%value
-                    else:
-                        resrce += " := %s"%value
-                resrce += ";\n"
-            resrce += "    END_VAR\n"
-        tasks = resource.getTask()
-        for task in tasks:
-            resrce += "    TASK %s("%task.getName()
-            args = []
-            single = task.getSingle()
-            if single:
-                args.append("SINGLE := %s"%single)
-            interval = task.getInterval()
-            if interval:
-                text = "t#"
-                if interval.hour != 0:
-                    text += "%dh"%interval.hour
-                if interval.minute != 0:
-                    text += "%dm"%interval.minute
-                if interval.second != 0:
-                    text += "%ds"%interval.second
-                if interval.microsecond != 0:
-                    text += "%dms"%(interval.microsecond / 1000)
-                args.append("INTERVAL := %s"%text)
-            args.append("PRIORITY := %s"%str(task.priority.getValue()))
-            resrce += ",".join(args) + ");\n"
-        for task in tasks:
-            for instance in task.getPouInstance():
-                resrce += "    PROGRAM %s WITH %s : %s;\n"%(instance.getName(), task.getName(), instance.getType())
-        for instance in resource.getPouInstance():
-            resrce += "    PROGRAM %s : %s;\n"%(instance.getName(), instance.getType())
-        resrce += "  END_RESOURCE\n"
-        return resrce
-        
-            
-def GenerateCurrentProgram(project):
-    program = ""
-    for pou in project.getPous():
+
+def GeneratePouProgram(pou_name):
+    if not pouComputed.get(pou_name, True):
+        pouComputed[pou_name] = True
+        global currentProject, currentProgram
+        pou = currentProject.getPou(pou_name)
         pou_type = pou.getPouType().getValue()
         if pou_type in pouTypeNames:
             pou_program = PouProgram(pou.getName(), pouTypeNames[pou_type])
@@ -630,8 +547,107 @@
             raise ValueError, "Undefined pou type"
         pou_program.GenerateInterface(pou.getInterface())
         pou_program.GenerateProgram(pou)
-        program += pou_program.GenerateSTProgram()
+        currentProgram += pou_program.GenerateSTProgram()
+
+def GenerateConfiguration(configuration):
+    config = "\nCONFIGURATION %s\n"%configuration.getName()
+    for varlist in configuration.getGlobalVars():
+        config += "  VAR_GLOBAL"
+        if varlist.getRetain():
+            config += " RETAIN"
+        if varlist.getConstant():
+            config += " CONSTANT"
+        config += "\n"
+        for var in varlist.getVariable():
+            var_type = var.getType().getValue()
+            config += "    %s "%var.getName()
+            address = var.getAddress()
+            if address:
+                config += "AT %s "%address
+            config += ": %s"%var_type
+            initial = var.getInitialValue()
+            if initial:
+                value = str(initial.getValue())
+                value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value)
+                if var_type == "STRING":
+                    config += " := '%s'"%value
+                elif var_type == "WSTRING":
+                    config += " := \"%s\""%value
+                else:
+                    config += " := %s"%value
+            config += ";\n"
+        config += "  END_VAR\n"
+    for resource in configuration.getResource():
+        config += GenerateResource(resource)
+    config += "END_CONFIGURATION\n"
+    return config
+    
+def GenerateResource(resource):
+    resrce = "\n  RESOURCE %s ON BEREMIZ\n"%resource.getName()
+    for varlist in resource.getGlobalVars():
+        resrce += "    VAR_GLOBAL"
+        if varlist.getRetain():
+            resrce += " RETAIN"
+        if varlist.getConstant():
+            resrce += " CONSTANT"
+        resrce += "\n"
+        for var in varlist.getVariable():
+            var_type = var.getType().getValue()
+            resrce += "      %s "%var.getName()
+            address = var.getAddress()
+            if address:
+                resrce += "AT %s "%address
+            resrce += ": %s"%var.getType().getValue()
+            initial = var.getInitialValue()
+            if initial:
+                value = str(initial.getValue())
+                value = {"TRUE":"0","FALSE":"1"}.get(value.upper(), value)
+                if var_type == "STRING":
+                    resrce += " := '%s'"%value
+                elif var_type == "WSTRING":
+                    resrce += " := \"%s\""%value
+                else:
+                    resrce += " := %s"%value
+            resrce += ";\n"
+        resrce += "    END_VAR\n"
+    tasks = resource.getTask()
+    for task in tasks:
+        resrce += "    TASK %s("%task.getName()
+        args = []
+        single = task.getSingle()
+        if single:
+            args.append("SINGLE := %s"%single)
+        interval = task.getInterval()
+        if interval:
+            text = "t#"
+            if interval.hour != 0:
+                text += "%dh"%interval.hour
+            if interval.minute != 0:
+                text += "%dm"%interval.minute
+            if interval.second != 0:
+                text += "%ds"%interval.second
+            if interval.microsecond != 0:
+                text += "%dms"%(interval.microsecond / 1000)
+            args.append("INTERVAL := %s"%text)
+        args.append("PRIORITY := %s"%str(task.priority.getValue()))
+        resrce += ",".join(args) + ");\n"
+    for task in tasks:
+        for instance in task.getPouInstance():
+            resrce += "    PROGRAM %s WITH %s : %s;\n"%(instance.getName(), task.getName(), instance.getType())
+    for instance in resource.getPouInstance():
+        resrce += "    PROGRAM %s : %s;\n"%(instance.getName(), instance.getType())
+    resrce += "  END_RESOURCE\n"
+    return resrce
+
+def GenerateCurrentProgram(project):
+    global currentProject, currentProgram
+    currentProject = project
+    currentProgram = ""
+    for pou in project.getPous():
+        pouComputed[pou.getName()] = False
+    for pou_name in pouComputed.keys():
+        GeneratePouProgram(pou_name)
     for config in project.getConfigurations():
-        program += pou_program.GenerateConfiguration(config)
-    return program
-
+        currentProgram += GenerateConfiguration(config)
+    return currentProgram
+
--- a/PLCOpenEditor.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/PLCOpenEditor.py	Tue Aug 14 14:53:06 2007 +0200
@@ -414,8 +414,8 @@
         
         self.CurrentToolBar = []
         self.CurrentLanguage = ""
-        #self.DrawingMode = FREEDRAWING_MODE
-        self.DrawingMode = DRIVENDRAWING_MODE
+        self.DrawingMode = FREEDRAWING_MODE
+        #self.DrawingMode = DRIVENDRAWING_MODE
         
         self.RefreshFileMenu()
         self.RefreshEditMenu()
@@ -2140,7 +2140,7 @@
         
         if element_type == "config":
             self.Viewer = wx.Panel(id=ID_POUEDITORPANELVIEWER,
-              name='ConfigPanel', parent=self.splitterWindow1, pos=wx.Point(0, 0),
+              name='ConfigPanel', parent=self, pos=wx.Point(0, 0),
               size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL)
             self.Viewer.ResetBuffer = lambda: None
             self.Viewer.RefreshView = lambda: None
@@ -2332,7 +2332,7 @@
         self.Viewer.RefreshView()
     
     def RefreshViewerVarList(self):
-        if self.ElementType not in ["config", "ressource"]:
+        if self.ElementType not in ["config", "resource"]:
             varlist = [var["Name"] for var in self.Values]
             if self.ElementType == "transition":
                 language = self.Controler.GetTransitionBodyType(self.PouName, self.TransitionName)
--- a/SFCViewer.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/SFCViewer.py	Tue Aug 14 14:53:06 2007 +0200
@@ -285,18 +285,19 @@
     def OnViewerMotion(self, event):
         if self.GetDrawingMode() == FREEDRAWING_MODE:
             Viewer.OnViewerMotion(self, event)
-        elif 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):
+        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.Refresh()
+            elif self.Mode == MODE_WIRE and self.SelectedElement:
+                self.SelectedElement.ResetPoints()
                 self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
-            self.Refresh()
-        elif self.Mode == MODE_WIRE and self.SelectedElement:
-            self.SelectedElement.ResetPoints()
-            self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling)
-            self.SelectedElement.GeneratePoints()
-            self.Refresh()
-        Viewer.OnViewerMotion(self, event)
+                self.SelectedElement.GeneratePoints()
+                self.Refresh()
+            self.UpdateScrollPos(event)
         event.Skip()
 
 #-------------------------------------------------------------------------------
@@ -717,21 +718,24 @@
             dialog.Destroy()
 
     def EditStepContent(self, step):
-        dialog = StepNameDialog(self.Parent, "Edit step name", "Please enter step name", step.GetName(), wx.OK|wx.CANCEL)
-        dialog.SetPouNames(self.Controler.GetProjectPouNames())
-        dialog.SetVariables(self.Controler.GetCurrentElementEditingInterfaceVars())
-        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()
-        dialog.Destroy()
+        if self.GetDrawingMode() == FREEDRAWING_MODE:
+            Viewer.EditStepContent(self, step)
+        else:
+            dialog = StepNameDialog(self.Parent, "Edit step name", "Please enter step name", step.GetName(), wx.OK|wx.CANCEL)
+            dialog.SetPouNames(self.Controler.GetProjectPouNames())
+            dialog.SetVariables(self.Controler.GetCurrentElementEditingInterfaceVars())
+            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()
+            dialog.Destroy()
 
 #-------------------------------------------------------------------------------
 #                          Delete element functions
--- a/Viewer.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/Viewer.py	Tue Aug 14 14:53:06 2007 +0200
@@ -836,9 +836,9 @@
                 elif self.Mode == MODE_POWERRAIL:
                     wx.CallAfter(self.AddNewPowerRail, bbox)
                 elif self.Mode == MODE_INITIALSTEP:
-                    wx.CallAfter(self.AddNewInitialStep, bbox)
+                    wx.CallAfter(self.AddNewStep, bbox, True)
                 elif self.Mode == MODE_STEP:
-                    wx.CallAfter(self.AddNewStep, bbox)
+                    wx.CallAfter(self.AddNewStep, bbox, False)
                 elif self.Mode == MODE_TRANSITION:
                     wx.CallAfter(self.AddNewTransition, bbox)
                 elif self.Mode == MODE_DIVERGENCE:
@@ -903,6 +903,9 @@
                 self.SelectedElement.OnMotion(event, dc, self.Scaling)
                 self.SelectedElement.GeneratePoints()
                 self.Refresh()
+        event.Skip()
+
+    def UpdateScrollPos(self, event):
         if (event.Dragging() and self.SelectedElement) or self.rubberBand.IsShown():
             position = event.GetPosition()
             move_window = wx.Point()
@@ -919,7 +922,6 @@
             if move_window.x != 0 or move_window.y != 0:
                 self.Scroll(xstart + move_window.x, ystart + move_window.y)
             self.RefreshScrollBars()
-        event.Skip()
 
 #-------------------------------------------------------------------------------
 #                          Keyboard event functions
@@ -1145,6 +1147,39 @@
             self.Refresh()
         dialog.Destroy()
 
+    def AddNewStep(self, bbox, initial = False):
+        dialog = StepContentDialog(self.Parent, initial)
+        dialog.SetPouNames(self.Controler.GetProjectPouNames())
+        dialog.SetVariables(self.Controler.GetCurrentElementEditingInterfaceVars())
+        dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)])
+        dialog.SetMinStepSize((bbox.width, bbox.height))
+        if dialog.ShowModal() == wx.ID_OK:
+            id = self.GetNewId()
+            values = dialog.GetValues()
+            step = SFC_Step(self, values["name"], initial, id)
+            if values["input"]:
+                step.AddInput()
+            else:
+                step.RemoveInput()
+            if values["output"]:
+                step.AddOutput()
+            else:
+                step.RemoveOutput()
+            if values["action"]:
+                step.AddAction()    
+            else:
+                step.RemoveAction()
+            step.SetPosition(bbox.x, bbox.y)
+            min_width, min_height = step.GetMinSize()
+            step.SetSize(max(bbox.width, min_width), max(bbox.height, min_height))
+            self.AddBlock(step)
+            self.Controler.AddCurrentElementEditingStep(id)
+            self.RefreshStepModel(step)
+            self.RefreshBuffer()
+            self.RefreshScrollBars()
+            self.Refresh()
+        dialog.Destroy()
+
     def AddNewTransition(self, bbox):
         dialog = TransitionContentDialog(self.Parent, self.GetDrawingMode() == FREEDRAWING_MODE)
         dialog.SetTransitions(self.Controler.GetCurrentElementEditingTransitions())
@@ -1326,7 +1361,39 @@
             self.Refresh()
         dialog.Destroy()
 
-
+    def EditStepContent(self, step):
+        dialog = StepContentDialog(self.Parent, step.GetInitial())
+        dialog.SetPouNames(self.Controler.GetProjectPouNames())
+        dialog.SetVariables(self.Controler.GetCurrentElementEditingInterfaceVars())
+        dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step) and block.GetName() != step.GetName()])
+        dialog.SetMinStepSize(step.GetSize())
+        values = {"name" : step.GetName()}
+        connectors = step.GetConnectors()
+        values["input"] = connectors["input"] != None
+        values["output"] = connectors["output"] != None
+        values["action"] = connectors["action"] != None
+        dialog.SetValues(values)
+        if dialog.ShowModal() == wx.ID_OK:
+            values = dialog.GetValues()
+            step.SetName(values["name"])
+            if values["input"]:
+                step.AddInput()
+            else:
+                step.RemoveInput()
+            if values["output"]:
+                step.AddOutput()
+            else:
+                step.RemoveOutput()
+            if values["action"]:
+                step.AddAction()    
+            else:
+                step.RemoveAction()
+            step.UpdateSize(values["width"], values["height"])
+            step.RefreshModel()
+            self.RefreshBuffer()
+            self.RefreshScrollBars()
+            self.Refresh()
+        
     def EditTransitionContent(self, transition):
         dialog = TransitionContentDialog(self.Parent, self.GetDrawingMode() == FREEDRAWING_MODE)
         dialog.SetTransitions(self.Controler.GetCurrentElementEditingTransitions())
--- a/graphics/LD_Objects.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/graphics/LD_Objects.py	Tue Aug 14 14:53:06 2007 +0200
@@ -42,6 +42,7 @@
         Graphic_Element.__init__(self, parent)
         self.Type = None
         self.Connectors = []
+        self.RealConnectors = None
         self.Id = id
         self.Extensions = [LD_LINE_SIZE / 2, LD_LINE_SIZE / 2]
         if len(connectors) < 1:
@@ -151,14 +152,24 @@
     
     # Refresh the positions of the power rail connectors
     def RefreshConnectors(self):
-        position = self.Extensions[0]
-        for connector in self.Connectors:
-            if connector:
-                if self.Type == LEFTRAIL:
-                    connector.SetPosition(wx.Point(self.Size[0], position))
-                elif self.Type == RIGHTRAIL:
-                    connector.SetPosition(wx.Point(0, position))
-            position += LD_LINE_SIZE
+        if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
+            height = self.Size[1] - self.Extensions[0] - self.Extensions[1]
+            for i, connector in enumerate(self.Connectors):
+                position = connector.GetRelPosition()
+                if self.RealConnectors:
+                    if self.Type == LEFTRAIL:
+                        connector.SetPosition(wx.Point(self.Size[0], self.Extensions[0] + int(round(self.RealConnectors[i] * height))))
+                    elif self.Type == RIGHTRAIL:
+                        connector.SetPosition(wx.Point(0, self.Extensions[0] + int(round(self.RealConnectors[i] * height))))
+        else:
+            position = self.Extensions[0]
+            for connector in self.Connectors:
+                if connector:
+                    if self.Type == LEFTRAIL:
+                        connector.SetPosition(wx.Point(self.Size[0], position))
+                    elif self.Type == RIGHTRAIL:
+                        connector.SetPosition(wx.Point(0, position))
+                position += LD_LINE_SIZE
         self.RefreshConnected()
     
     # Refresh the position of wires connected to power rail
@@ -221,13 +232,12 @@
             # Initializes the last position
             self.oldPos = GetScaledEventPosition(event, dc, scaling)
         else:
-##            self.RealConnectors = {"Inputs":[],"Outputs":[]}
-##            for input in self.Inputs:
-##                position = input.GetRelPosition()
-##                self.RealConnectors["Inputs"].append(float(position.x)/float(self.Size[0]))
-##            for output in self.Outputs:
-##                position = output.GetRelPosition()
-##                self.RealConnectors["Outputs"].append(float(position.x)/float(self.Size[0]))
+            self.RealConnectors = []
+            height = self.Size[1] - self.Extensions[0] - self.Extensions[1]
+            for connector in self.Connectors:
+                position = connector.GetRelPosition()
+                self.RealConnectors.append(float(position.y - self.Extensions[0])/float(max(1, height)))
+            print self.RealConnectors
             Graphic_Element.OnLeftDown(self, event, dc, scaling)
     
     # Method called when a LeftUp event have been generated
--- a/graphics/SFC_Objects.py	Mon Aug 13 18:06:50 2007 +0200
+++ b/graphics/SFC_Objects.py	Tue Aug 14 14:53:06 2007 +0200
@@ -78,6 +78,19 @@
             self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE)
     
     # Add output connector to step
+    def AddInput(self):
+        if not self.Input:
+            self.Input = Connector(self, "", "ANY", wx.Point(self.Size[0] / 2, 0), NORTH)
+            self.RefreshBoundingBox()
+    
+    # Remove output connector from step
+    def RemoveInput(self):
+        if self.Input:
+            self.Input.UnConnect()
+            self.Input = None
+            self.RefreshBoundingBox()
+    
+    # Add output connector to step
     def AddOutput(self):
         if not self.Output:
             self.Output = Connector(self, "", "ANY", wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH)