DataTypeEditor.py
changeset 295 c6ef6d92ce16
parent 235 7b58a3b5b6ec
child 304 2df3d31d8059
--- a/DataTypeEditor.py	Mon Dec 15 09:45:16 2008 +0100
+++ b/DataTypeEditor.py	Fri Dec 19 15:07:54 2008 +0100
@@ -25,12 +25,183 @@
 import wx
 import wx.grid
 import wx.gizmos
-from plcopen.structures import IEC_KEYWORDS
+from plcopen.structures import IEC_KEYWORDS, TestIdentifier
 
 import re
 
 DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$")
 
+def AppendMenu(parent, help, id, kind, text):
+    if wx.VERSION >= (2, 6, 0):
+        parent.Append(help=help, id=id, kind=kind, text=text)
+    else:
+        parent.Append(helpString=help, id=id, kind=kind, item=text)
+
+#-------------------------------------------------------------------------------
+#                            Structure Elements Table
+#-------------------------------------------------------------------------------
+
+class ElementsTable(wx.grid.PyGridTableBase):
+    
+    """
+    A custom wx.grid.Grid Table using user supplied data
+    """
+    def __init__(self, parent, data, colnames):
+        # The base class must be initialized *first*
+        wx.grid.PyGridTableBase.__init__(self)
+        self.data = data
+        self.old_value = None
+        self.colnames = colnames
+        self.Errors = {}
+        self.Parent = parent
+        # XXX
+        # we need to store the row length and collength to
+        # see if the table has changed size
+        self._rows = self.GetNumberRows()
+        self._cols = self.GetNumberCols()
+    
+    def GetNumberCols(self):
+        return len(self.colnames)
+        
+    def GetNumberRows(self):
+        return len(self.data)
+
+    def GetColLabelValue(self, col):
+        if col < len(self.colnames):
+            return self.colnames[col]
+
+    def GetRowLabelValues(self, row):
+        return row
+
+    def GetValue(self, row, col):
+        if row < self.GetNumberRows():
+            if col == 0:
+                return row + 1
+            name = str(self.data[row].get(self.GetColLabelValue(col), ""))
+            return name
+    
+    def SetValue(self, row, col, value):
+        if col < len(self.colnames):
+            colname = self.GetColLabelValue(col)
+            if colname == "Name":
+                self.old_value = self.data[row][colname]
+            self.data[row][colname] = value
+    
+    def GetValueByName(self, row, colname):
+        if row < self.GetNumberRows():
+            return self.data[row].get(colname)
+
+    def SetValueByName(self, row, colname, value):
+        if row < self.GetNumberRows():
+            self.data[row][colname] = value
+
+    def GetOldValue(self):
+        return self.old_value
+    
+    def ResetView(self, grid):
+        """
+        (wx.grid.Grid) -> Reset the grid view.   Call this to
+        update the grid if rows and columns have been added or deleted
+        """
+        grid.BeginBatch()
+        for current, new, delmsg, addmsg in [
+            (self._rows, self.GetNumberRows(), wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
+            (self._cols, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
+        ]:
+            if new < current:
+                msg = wx.grid.GridTableMessage(self,delmsg,new,current-new)
+                grid.ProcessTableMessage(msg)
+            elif new > current:
+                msg = wx.grid.GridTableMessage(self,addmsg,new-current)
+                grid.ProcessTableMessage(msg)
+                self.UpdateValues(grid)
+        grid.EndBatch()
+
+        self._rows = self.GetNumberRows()
+        self._cols = self.GetNumberCols()
+        # update the column rendering scheme
+        self._updateColAttrs(grid)
+
+        # update the scrollbars and the displayed part of the grid
+        grid.AdjustScrollbars()
+        grid.ForceRefresh()
+
+    def UpdateValues(self, grid):
+        """Update all displayed values"""
+        # This sends an event to the grid table to update all of the values
+        msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
+        grid.ProcessTableMessage(msg)
+
+    def _updateColAttrs(self, grid):
+        """
+        wx.grid.Grid -> update the column attributes to add the
+        appropriate renderer given the column name.
+
+        Otherwise default to the default renderer.
+        """
+        
+        typelist = None
+        accesslist = None
+        for row in range(self.GetNumberRows()):
+            for col in range(self.GetNumberCols()):
+                editor = None
+                renderer = None
+                colname = self.GetColLabelValue(col)
+                if col != 0:
+                    grid.SetReadOnly(row, col, False)
+                    if colname == "Name":
+                        editor = wx.grid.GridCellTextEditor()
+                        renderer = wx.grid.GridCellStringRenderer()
+                    elif colname == "Initial Value":
+                        editor = wx.grid.GridCellTextEditor()
+                        renderer = wx.grid.GridCellStringRenderer()
+                    elif colname == "Type":
+                        editor = wx.grid.GridCellTextEditor()
+                else:
+                    grid.SetReadOnly(row, col, True)
+                
+                grid.SetCellEditor(row, col, editor)
+                grid.SetCellRenderer(row, col, renderer)
+                
+                if row in self.Errors and self.Errors[row][0] == colname.lower():
+                    grid.SetCellBackgroundColour(row, col, wx.Colour(255, 255, 0))
+                    grid.SetCellTextColour(row, col, wx.RED)
+                    grid.MakeCellVisible(row, col)
+                else:
+                    grid.SetCellTextColour(row, col, wx.BLACK)
+                    grid.SetCellBackgroundColour(row, col, wx.WHITE)
+    
+    def SetData(self, data):
+        self.data = data
+    
+    def GetData(self):
+        return self.data
+    
+    def AppendRow(self, row_content):
+        self.data.append(row_content)
+
+    def RemoveRow(self, row_index):
+        self.data.pop(row_index)
+
+    def MoveRow(self, idx, move):
+        new_idx = max(0, min(idx + move, len(self.data) - 1))
+        if new_idx != idx:
+            self.data.insert(new_idx, self.data.pop(idx))
+            return new_idx
+        return None
+    
+    def GetRow(self, row_index):
+        return self.data[row_index]
+
+    def Empty(self):
+        self.data = []
+        self.editors = []
+
+    def AddError(self, infos):
+        self.Errors[infos[0]] = infos[1:]
+
+    def ClearErrors(self):
+        self.Errors = {}
 
 #-------------------------------------------------------------------------------
 #                          Configuration Editor class
@@ -45,12 +216,16 @@
  ID_DATATYPEEDITORENUMERATEDVALUES, ID_DATATYPEEDITORENUMERATEDINITIALVALUE,
  ID_DATATYPEEDITORARRAYPANEL, ID_DATATYPEEDITORARRAYBASETYPE, 
  ID_DATATYPEEDITORARRAYDIMENSIONS, ID_DATATYPEEDITORARRAYINITIALVALUE, 
+ ID_DATATYPEEDITORSTRUCTUREPANEL, ID_DATATYPEEDITORSTRUCTUREELEMENTSGRID,
+ ID_DATATYPEEDITORSTRUCTUREADDBUTTON, ID_DATATYPEEDITORSTRUCTUREDELETEBUTTON,
+ ID_DATATYPEEDITORSTRUCTUREUPBUTTON, ID_DATATYPEEDITORSTRUCTUREDOWNBUTTON,
  ID_DATATYPEEDITORSTATICTEXT1, ID_DATATYPEEDITORSTATICTEXT2, 
  ID_DATATYPEEDITORSTATICTEXT3, ID_DATATYPEEDITORSTATICTEXT4, 
  ID_DATATYPEEDITORSTATICTEXT5, ID_DATATYPEEDITORSTATICTEXT6, 
  ID_DATATYPEEDITORSTATICTEXT7, ID_DATATYPEEDITORSTATICTEXT8,
  ID_DATATYPEEDITORSTATICTEXT9, ID_DATATYPEEDITORSTATICTEXT10, 
-] = [wx.NewId() for _init_ctrls in range(28)]
+ ID_DATATYPEEDITORSTATICTEXT11, 
+] = [wx.NewId() for _init_ctrls in range(35)]
 
 class DataTypeEditor(wx.Panel):
     
@@ -71,6 +246,7 @@
         parent.AddWindow(self.SubrangePanel, 1, border=0, flag=wx.ALL)
         parent.AddWindow(self.EnumeratedPanel, 1, border=0, flag=wx.GROW|wx.ALL)
         parent.AddWindow(self.ArrayPanel, 1, border=0, flag=wx.ALL)
+        parent.AddWindow(self.StructurePanel, 1, border=0, flag=wx.GROW|wx.ALL)
 
     def _init_coll_DirectlyPanelSizer_Items(self, parent):
         parent.AddWindow(self.staticText2, 1, border=5, flag=wx.GROW|wx.ALL)
@@ -113,6 +289,21 @@
         parent.AddWindow(self.staticText10, 1, border=5, flag=wx.GROW|wx.ALL)
         parent.AddWindow(self.ArrayInitialValue, 1, border=5, flag=wx.GROW|wx.ALL)    
 
+    def _init_coll_StructurePanelSizer_Items(self, parent):
+        parent.AddWindow(self.staticText11, 0, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.StructureElementsGrid, 0, border=0, flag=wx.GROW)
+        parent.AddSizer(self.StructurePanelButtonSizer, 0, border=0, flag=wx.ALIGN_RIGHT)
+        
+    def _init_coll_StructurePanelSizer_Growables(self, parent):
+        parent.AddGrowableCol(0)
+        parent.AddGrowableRow(1)
+
+    def _init_coll_StructurePanelButtonSizer_Items(self, parent):
+        parent.AddWindow(self.StructureAddButton, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.StructureDeleteButton, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.StructureUpButton, 1, border=5, flag=wx.GROW|wx.ALL)
+        parent.AddWindow(self.StructureDownButton, 1, border=5, flag=wx.GROW|wx.ALL)
+    
     def _init_sizers(self):
         self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
         self.TopSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -123,7 +314,8 @@
         self.ArrayPanelSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=2, vgap=0)
         self.ArrayPanelLeftSizer = wx.BoxSizer(wx.HORIZONTAL)
         self.ArrayPanelRightSizer = wx.BoxSizer(wx.HORIZONTAL)
-        
+        self.StructurePanelSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=0)
+        self.StructurePanelButtonSizer = wx.BoxSizer(wx.HORIZONTAL)
         self._init_coll_MainSizer_Items(self.MainSizer)
         self._init_coll_MainSizer_Growables(self.MainSizer)
         self._init_coll_TopSizer_Items(self.TopSizer)
@@ -135,12 +327,16 @@
         self._init_coll_ArrayPanelSizer_Growables(self.ArrayPanelSizer)
         self._init_coll_ArrayPanelLeftSizer_Items(self.ArrayPanelLeftSizer)
         self._init_coll_ArrayPanelRightSizer_Items(self.ArrayPanelRightSizer)
+        self._init_coll_StructurePanelSizer_Items(self.StructurePanelSizer)
+        self._init_coll_StructurePanelSizer_Growables(self.StructurePanelSizer)
+        self._init_coll_StructurePanelButtonSizer_Items(self.StructurePanelButtonSizer)
         
         self.SetSizer(self.MainSizer)
         self.DirectlyPanel.SetSizer(self.DirectlyPanelSizer)
         self.SubrangePanel.SetSizer(self.SubrangePanelSizer)
         self.EnumeratedPanel.SetSizer(self.EnumeratedPanelSizer)
         self.ArrayPanel.SetSizer(self.ArrayPanelSizer)
+        self.StructurePanel.SetSizer(self.StructurePanelSizer)
     
     def _init_ctrls(self, prnt):
         wx.Panel.__init__(self, id=ID_DATATYPEEDITOR, name='', parent=prnt,
@@ -282,18 +478,79 @@
               size=wx.Size(0, 24), style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_MULTILINE|wx.TE_RICH)
         self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, id=ID_DATATYPEEDITORARRAYINITIALVALUE)
         
+        # Panel for Structure data types
+        
+        self.StructurePanel = wx.Panel(id=ID_DATATYPEEDITORSTRUCTUREPANEL,
+              name='StructurePanel', parent=self, pos=wx.Point(0, 0),
+              size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
+        
+        self.staticText11 = wx.StaticText(id=ID_DATATYPEEDITORSTATICTEXT11,
+              label='Elements :', name='staticText11', parent=self.StructurePanel,
+              pos=wx.Point(0, 0), size=wx.Size(150, 17), style=0)
+
+        self.StructureElementsGrid = wx.grid.Grid(id=ID_DATATYPEEDITORSTRUCTUREELEMENTSGRID,
+              name='StructureElementsGrid', parent=self.StructurePanel, pos=wx.Point(0, 0), 
+              size=wx.Size(0, 150), style=wx.VSCROLL)
+        self.StructureElementsGrid.SetFont(wx.Font(12, 77, wx.NORMAL, wx.NORMAL, False,
+              'Sans'))
+        self.StructureElementsGrid.SetLabelFont(wx.Font(10, 77, wx.NORMAL, wx.NORMAL,
+              False, 'Sans'))
+        self.StructureElementsGrid.SetSelectionBackground(wx.WHITE)
+        self.StructureElementsGrid.SetSelectionForeground(wx.BLACK)
+        if wx.VERSION >= (2, 6, 0):
+            self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnStructureElementsGridCellChange)
+            self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnStructureElementsGridEditorShown)
+        else:
+            wx.grid.EVT_GRID_CELL_CHANGE(self.StructureElementsGrid, self.OnStructureElementsGridCellChange)
+            wx.grid.EVT_GRID_EDITOR_SHOWN(self.StructureElementsGrid, self.OnStructureElementsGridEditorShown)
+
+        self.StructureAddButton = wx.Button(id=ID_DATATYPEEDITORSTRUCTUREADDBUTTON, label='Add',
+              name='StructureAddButton', parent=self.StructurePanel, pos=wx.Point(0, 0),
+              size=wx.Size(72, 32), style=0)
+        self.Bind(wx.EVT_BUTTON, self.OnStructureAddButton, id=ID_DATATYPEEDITORSTRUCTUREADDBUTTON)
+
+        self.StructureDeleteButton = wx.Button(id=ID_DATATYPEEDITORSTRUCTUREDELETEBUTTON, label='Delete',
+              name='StructureDeleteButton', parent=self.StructurePanel, pos=wx.Point(0, 0),
+              size=wx.Size(72, 32), style=0)
+        self.Bind(wx.EVT_BUTTON, self.OnStructureDeleteButton, id=ID_DATATYPEEDITORSTRUCTUREDELETEBUTTON)
+
+        self.StructureUpButton = wx.Button(id=ID_DATATYPEEDITORSTRUCTUREUPBUTTON, label='^',
+              name='StructureUpButton', parent=self.StructurePanel, pos=wx.Point(0, 0),
+              size=wx.Size(32, 32), style=0)
+        self.Bind(wx.EVT_BUTTON, self.OnStructureUpButton, id=ID_DATATYPEEDITORSTRUCTUREUPBUTTON)
+
+        self.StructureDownButton = wx.Button(id=ID_DATATYPEEDITORSTRUCTUREDOWNBUTTON, label='v',
+              name='StructureDownButton', parent=self.StructurePanel, pos=wx.Point(0, 0),
+              size=wx.Size(32, 32), style=0)
+        self.Bind(wx.EVT_BUTTON, self.OnStructureDownButton, id=ID_DATATYPEEDITORSTRUCTUREDOWNBUTTON)
+
         self._init_sizers()
 
     def __init__(self, parent, tagname, window, controler):
         self._init_ctrls(parent)
         
+        self.StructureElementDefaultValue = {"Name" : "", "Type" : "INT", "Initial Value" : ""}
+        self.StructureElementsTable = ElementsTable(self, [], ["#", "Name", "Type", "Initial Value"])
+        self.StructureColSizes = [40, 150, 100, 250]
+        self.StructureColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
+        
+        self.StructureElementsGrid.SetTable(self.StructureElementsTable)
+        self.StructureElementsGrid.SetRowLabelSize(0)
+        for col in range(self.StructureElementsTable.GetNumberCols()):
+            attr = wx.grid.GridCellAttr()
+            attr.SetAlignment(self.StructureColAlignements[col], wx.ALIGN_CENTRE)
+            self.StructureElementsGrid.SetColAttr(col, attr)
+            self.StructureElementsGrid.SetColSize(col, self.StructureColSizes[col])
+        
         self.DerivationType.Append("Directly")
         self.DerivationType.Append("Subrange")
         self.DerivationType.Append("Enumerated")
         self.DerivationType.Append("Array")
+        self.DerivationType.Append("Structure")
         self.SubrangePanel.Hide()
         self.EnumeratedPanel.Hide()
         self.ArrayPanel.Hide()
+        self.StructurePanel.Hide()
         self.CurrentPanel = "Directly"
         self.Errors = []
         self.Initializing = False
@@ -302,34 +559,6 @@
         self.Controler = controler
         self.TagName = tagname
     
-    def OnEnumeratedValueEndEdit(self, event):
-        text = event.GetText()
-        values = self.EnumeratedValues.GetStrings()
-        index = event.GetIndex()
-        if index >= len(values) or values[index].upper() != text.upper():
-            if text.upper() in [value.upper() for value in values]:
-                message = wx.MessageDialog(self, "\"%s\" value already defined!"%text, "Error", wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
-                event.Veto()
-            elif text.upper() in IEC_KEYWORDS:
-                message = wx.MessageDialog(self, "\"%s\" is a keyword. It can't be used!"%text, "Error", wx.OK|wx.ICON_ERROR)
-                message.ShowModal()
-                message.Destroy()
-            else:
-                wx.CallAfter(self.RefreshEnumeratedValues)
-                wx.CallAfter(self.RefreshTypeInfos)
-                event.Skip()
-        else:
-            wx.CallAfter(self.RefreshEnumeratedValues)
-            wx.CallAfter(self.RefreshTypeInfos)
-            event.Skip()
-    
-    def OnEnumeratedValuesChanged(self, event):
-        wx.CallAfter(self.RefreshEnumeratedValues)
-        wx.CallAfter(self.RefreshTypeInfos)
-        event.Skip()
-    
     def SetTagName(self, tagname):
         self.TagName = tagname
         
@@ -383,6 +612,9 @@
                 self.ArrayBaseType.SetStringSelection(type_infos["base_type"])
                 self.ArrayDimensions.SetStrings(map(lambda x : "..".join(map(str, x)), type_infos["dimensions"]))
                 self.ArrayInitialValue.SetValue(type_infos["initial"])
+            elif type_infos["type"] == "Structure":
+                self.StructureElementsTable.SetData(type_infos["elements"])
+                self.StructureElementsTable.ResetView(self.StructureElementsGrid)
             self.RefreshDisplayedInfos()
         self.ShowErrors()
         self.Initializing = False
@@ -425,6 +657,144 @@
         wx.CallAfter(self.RefreshTypeInfos)
         event.Skip()
 
+    def OnEnumeratedValueEndEdit(self, event):
+        text = event.GetText()
+        values = self.EnumeratedValues.GetStrings()
+        index = event.GetIndex()
+        if index >= len(values) or values[index].upper() != text.upper():
+            if text.upper() in [value.upper() for value in values]:
+                message = wx.MessageDialog(self, "\"%s\" value already defined!"%text, "Error", wx.OK|wx.ICON_ERROR)
+                message.ShowModal()
+                message.Destroy()
+                event.Veto()
+            elif text.upper() in IEC_KEYWORDS:
+                message = wx.MessageDialog(self, "\"%s\" is a keyword. It can't be used!"%text, "Error", wx.OK|wx.ICON_ERROR)
+                message.ShowModal()
+                message.Destroy()
+            else:
+                wx.CallAfter(self.RefreshEnumeratedValues)
+                wx.CallAfter(self.RefreshTypeInfos)
+                event.Skip()
+        else:
+            wx.CallAfter(self.RefreshEnumeratedValues)
+            wx.CallAfter(self.RefreshTypeInfos)
+            event.Skip()
+    
+    def OnEnumeratedValuesChanged(self, event):
+        wx.CallAfter(self.RefreshEnumeratedValues)
+        wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+    
+    def OnStructureAddButton(self, event):
+        new_row = self.StructureElementDefaultValue.copy()
+        self.StructureElementsTable.AppendRow(new_row)
+        self.RefreshTypeInfos()
+        self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+        event.Skip()
+    
+    def OnStructureDeleteButton(self, event):
+        row = self.StructureElementsGrid.GetGridCursorRow()
+        self.StructureElementsTable.RemoveRow(row)
+        self.RefreshTypeInfos()
+        self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+        event.Skip()
+    
+    def OnStructureUpButton(self, event):
+        row = self.StructureElementsGrid.GetGridCursorRow()
+        new_index = self.StructureElementsTable.MoveRow(row, -1)
+        if new_index is not None:
+            self.RefreshTypeInfos()
+            self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+            self.StructureElementsGrid.SetGridCursor(new_index, self.StructureElementsGrid.GetGridCursorCol())
+        event.Skip()
+    
+    def OnStructureDownButton(self, event):
+        row = self.StructureElementsGrid.GetGridCursorRow()
+        new_index = self.StructureElementsTable.MoveRow(row, 1)
+        if new_index is not None:
+            self.RefreshTypeInfos()
+            self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+            self.StructureElementsGrid.SetGridCursor(new_index, self.StructureElementsGrid.GetGridCursorCol())
+        event.Skip()
+
+    def OnStructureElementsGridCellChange(self, event):
+        row, col = event.GetRow(), event.GetCol()
+        colname = self.StructureElementsTable.GetColLabelValue(col)
+        value = self.StructureElementsTable.GetValue(row, col)
+        if colname == "Name":
+            if not TestIdentifier(value):
+                message = wx.MessageDialog(self, "\"%s\" is not a valid identifier!"%value, "Error", wx.OK|wx.ICON_ERROR)
+                message.ShowModal()
+                message.Destroy()
+                event.Veto()
+            elif value.upper() in IEC_KEYWORDS:
+                message = wx.MessageDialog(self, "\"%s\" is a keyword. It can't be used!"%value, "Error", wx.OK|wx.ICON_ERROR)
+                message.ShowModal()
+                message.Destroy()
+                event.Veto()
+##            elif value.upper() in self.PouNames:
+##                message = wx.MessageDialog(self, "A pou with \"%s\" as name exists!"%value, "Error", wx.OK|wx.ICON_ERROR)
+##                message.ShowModal()
+##                message.Destroy()
+##                event.Veto()
+            elif value.upper() in [var["Name"].upper() for idx, var in enumerate(self.StructureElementsTable.GetData()) if idx != row]:
+                message = wx.MessageDialog(self, "A element with \"%s\" as name exists in this structure!"%value, "Error", wx.OK|wx.ICON_ERROR)
+                message.ShowModal()
+                message.Destroy()
+                event.Veto()
+            else:
+                self.RefreshTypeInfos()
+                self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+##                old_value = self.Table.GetOldValue()
+##                if old_value != "":
+##                    self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value)
+##                self.Controler.BufferProject()
+                event.Skip()
+        else:
+            self.RefreshTypeInfos()
+            self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+            event.Skip()
+    
+    def OnStructureElementsGridEditorShown(self, event):
+        row, col = event.GetRow(), event.GetCol() 
+        if self.StructureElementsTable.GetColLabelValue(col) == "Type":
+            type_menu = wx.Menu(title='')
+            base_menu = wx.Menu(title='')
+            for base_type in self.Controler.GetBaseTypes():
+                new_id = wx.NewId()
+                AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
+                self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(base_type), id=new_id)
+            type_menu.AppendMenu(wx.NewId(), "Base Types", base_menu)
+            datatype_menu = wx.Menu(title='')
+            for datatype in self.Controler.GetDataTypes(self.TagName, False, self.ParentWindow.Debug):
+                new_id = wx.NewId()
+                AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
+                self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(datatype), id=new_id)
+            type_menu.AppendMenu(wx.NewId(), "User Data Types", datatype_menu)
+##            functionblock_menu = wx.Menu(title='')
+##            bodytype = self.Controler.GetEditedElementBodyType(self.TagName, self.ParentWindow.Debug)
+##            pouname, poutype = self.Controler.GetEditedElementType(self.TagName, self.ParentWindow.Debug)
+##            if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]:
+##                for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName, self.ParentWindow.Debug):
+##                    new_id = wx.NewId()
+##                    AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type)
+##                    self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id)
+##                type_menu.AppendMenu(wx.NewId(), "Function Block Types", functionblock_menu)
+            rect = self.StructureElementsGrid.BlockToDeviceRect((row, col), (row, col))
+            self.StructureElementsGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize())
+            event.Veto()
+        else:
+            event.Skip()
+
+    def GetElementTypeFunction(self, base_type):
+        def ElementTypeFunction(event):
+            row = self.StructureElementsGrid.GetGridCursorRow()
+            self.StructureElementsTable.SetValueByName(row, "Type", base_type)
+            self.RefreshTypeInfos()
+            self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+            event.Skip()
+        return ElementTypeFunction
+
     def RefreshDisplayedInfos(self):
         selected = self.DerivationType.GetStringSelection()
         if selected != self.CurrentPanel:
@@ -436,6 +806,8 @@
                 self.EnumeratedPanel.Hide()
             elif self.CurrentPanel == "Array":
                 self.ArrayPanel.Hide()
+            elif self.CurrentPanel == "Structure":
+                self.StructurePanel.Hide()
             self.CurrentPanel = selected
             if selected == "Directly":
                 self.DirectlyPanel.Show()
@@ -445,6 +817,8 @@
                 self.EnumeratedPanel.Show()
             elif selected == "Array":
                 self.ArrayPanel.Show()
+            elif selected == "Structure":
+                self.StructurePanel.Show()
             self.MainSizer.Layout()
 
     def RefreshEnumeratedValues(self):
@@ -505,6 +879,9 @@
                     return
                 infos["dimensions"].append(map(int, bounds))
             infos["initial"] = self.ArrayInitialValue.GetValue()
+        elif selected == "Structure":
+            infos["elements"] = self.StructureElementsTable.GetData()
+            infos["initial"] = ""
         self.Controler.SetDataTypeInfos(self.TagName, infos)
         self.ParentWindow.RefreshTitle()
         self.ParentWindow.RefreshEditMenu()