editors/DataTypeEditor.py
changeset 814 5743cbdff669
child 864 bf4f7f0801b9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/editors/DataTypeEditor.py	Fri Sep 07 16:45:55 2012 +0200
@@ -0,0 +1,791 @@
+#!/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
+
+import re
+
+import wx
+import wx.grid
+import wx.lib.buttons
+
+from plcopen.structures import IEC_KEYWORDS, TestIdentifier
+from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD
+from controls import CustomEditableListBox, CustomGrid, CustomTable
+from EditorPanel import EditorPanel
+from util.BitmapLibrary import GetBitmap
+
+#-------------------------------------------------------------------------------
+#                                    Helpers
+#-------------------------------------------------------------------------------
+
+DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$")
+
+def AppendMenu(parent, help, id, kind, text):
+    parent.Append(help=help, id=id, kind=kind, text=text)
+
+def GetElementsTableColnames():
+    _ = lambda x : x
+    return ["#", _("Name"), _("Type"), _("Initial Value")]
+
+def GetDatatypeTypes():
+    _ = lambda x : x
+    return [_("Directly"), _("Subrange"), _("Enumerated"), _("Array"), _("Structure")]
+DATATYPE_TYPES_DICT = dict([(_(datatype), datatype) for datatype in GetDatatypeTypes()])
+
+#-------------------------------------------------------------------------------
+#                            Structure Elements Table
+#-------------------------------------------------------------------------------
+
+class ElementsTable(CustomTable):
+    
+    """
+    A custom wx.grid.Grid Table using user supplied data
+    """
+    def __init__(self, parent, data, colnames):
+        # The base class must be initialized *first*
+        CustomTable.__init__(self, parent, data, colnames)
+        self.old_value = None
+        
+    def GetValue(self, row, col):
+        if row < self.GetNumberRows():
+            if col == 0:
+                return row + 1
+            name = str(self.data[row].get(self.GetColLabelValue(col, False), ""))
+            return name
+    
+    def SetValue(self, row, col, value):
+        if col < len(self.colnames):
+            colname = self.GetColLabelValue(col, False)
+            if colname == "Name":
+                self.old_value = self.data[row][colname]
+            self.data[row][colname] = value
+    
+    def GetOldValue(self):
+        return self.old_value
+    
+    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.
+        """
+        
+        for row in range(self.GetNumberRows()):
+            row_highlights = self.Highlights.get(row, {})
+            for col in range(self.GetNumberCols()):
+                editor = None
+                renderer = None
+                colname = self.GetColLabelValue(col, False)
+                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)
+                
+                highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1]
+                grid.SetCellBackgroundColour(row, col, highlight_colours[0])
+                grid.SetCellTextColour(row, col, highlight_colours[1])
+            self.ResizeRow(grid, row)
+    
+    def AddHighlight(self, infos, highlight_type):
+        row_highlights = self.Highlights.setdefault(infos[0], {})
+        if infos[1] == "initial":
+            col_highlights = row_highlights.setdefault("initial value", [])
+        else:
+            col_highlights = row_highlights.setdefault(infos[1], [])
+        col_highlights.append(highlight_type)
+
+#-------------------------------------------------------------------------------
+#                          Datatype Editor class
+#-------------------------------------------------------------------------------
+
+class DataTypeEditor(EditorPanel):
+    
+    def _init_Editor(self, parent):
+        self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER)
+        
+        self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
+        self.MainSizer.AddGrowableCol(0)
+        self.MainSizer.AddGrowableRow(1)
+        
+        top_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.MainSizer.AddSizer(top_sizer, border=5, 
+              flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
+        
+        derivation_type_label = wx.StaticText(self.Editor, label=_('Derivation Type:'))
+        top_sizer.AddWindow(derivation_type_label, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT)
+        
+        self.DerivationType = wx.ComboBox(self.Editor,
+              size=wx.Size(200, -1), style=wx.CB_READONLY)
+        self.Bind(wx.EVT_COMBOBOX, self.OnDerivationTypeChanged, self.DerivationType)
+        top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW|wx.RIGHT)
+        
+        typeinfos_staticbox = wx.StaticBox(self.Editor, label=_('Type infos:'))
+        typeinfos_sizer = wx.StaticBoxSizer(typeinfos_staticbox, wx.HORIZONTAL)
+        self.MainSizer.AddSizer(typeinfos_sizer, border=5, 
+              flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT)
+        
+        # Panel for Directly derived data types
+
+        self.DirectlyPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
+        typeinfos_sizer.AddWindow(self.DirectlyPanel, 1)
+        
+        directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        directly_basetype_label = wx.StaticText(self.DirectlyPanel, 
+              label=_('Base Type:'))
+        directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.DirectlyBaseType = wx.ComboBox(self.DirectlyPanel, style=wx.CB_READONLY)
+        self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.DirectlyPanel)
+        directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5, 
+              flag=wx.GROW|wx.ALL)
+        
+        directly_initialvalue_label = wx.StaticText(self.DirectlyPanel,
+              label=_('Initial Value:'))
+        directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, 
+              style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH)
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.DirectlyInitialValue)
+        directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5, 
+              flag=wx.ALL)
+        
+        self.DirectlyPanel.SetSizer(directly_panel_sizer)
+        
+        # Panel for Subrange data types
+
+        self.SubrangePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
+        typeinfos_sizer.AddWindow(self.SubrangePanel, 1)
+        
+        subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0)
+        
+        subrange_basetype_label = wx.StaticText(self.SubrangePanel,
+              label=_('Base Type:'))
+        subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.SubrangeBaseType = wx.ComboBox(self.SubrangePanel, style=wx.CB_READONLY)
+        self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged, 
+              self.SubrangeBaseType)
+        subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5, 
+              flag=wx.GROW|wx.ALL)
+        
+        subrange_initialvalue_label = wx.StaticText(self.SubrangePanel,
+              label=_('Initial Value:'))
+        subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel, 
+              style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, self.SubrangeInitialValue)
+        subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5, 
+              flag=wx.GROW|wx.ALL)
+        
+        subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:'))
+        subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.SubrangeMinimum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMinimumChanged, self.SubrangeMinimum)
+        subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5, 
+              flag=wx.GROW|wx.ALL)
+        
+        for i in xrange(2):
+            subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1)
+        
+        subrange_maximum_label = wx.StaticText(self.SubrangePanel,
+              label=_('Maximum:'))
+        subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.SubrangeMaximum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL)
+        self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMaximumChanged, self.SubrangeMaximum)
+        
+        subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5, 
+              flag=wx.GROW|wx.ALL)
+        
+        self.SubrangePanel.SetSizer(subrange_panel_sizer)
+        
+        # Panel for Enumerated data types
+        
+        self.EnumeratedPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
+        typeinfos_sizer.AddWindow(self.EnumeratedPanel, 1)
+        
+        enumerated_panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self.EnumeratedValues = CustomEditableListBox(self.EnumeratedPanel, 
+              label=_("Values:"), style=wx.gizmos.EL_ALLOW_NEW| 
+                                        wx.gizmos.EL_ALLOW_EDIT| 
+                                        wx.gizmos.EL_ALLOW_DELETE)
+        setattr(self.EnumeratedValues, "_OnLabelEndEdit", self.OnEnumeratedValueEndEdit)
+        for func in ["_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]:
+            setattr(self.EnumeratedValues, func, self.OnEnumeratedValuesChanged)
+        enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5, 
+              flag=wx.GROW|wx.ALL)
+        
+        enumerated_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL)
+        enumerated_panel_sizer.AddSizer(enumerated_panel_rightsizer, 1)
+        
+        enumerated_initialvalue_label = wx.StaticText(self.EnumeratedPanel,
+              label=_('Initial Value:'))
+        enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, 
+              border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, 
+              style=wx.CB_READONLY)
+        self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue)
+        enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1, 
+              border=5, flag=wx.ALL)
+        
+        self.EnumeratedPanel.SetSizer(enumerated_panel_sizer)
+        
+        # Panel for Array data types
+
+        self.ArrayPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
+        typeinfos_sizer.AddWindow(self.ArrayPanel, 1)
+        
+        array_panel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=0)
+        array_panel_sizer.AddGrowableCol(0)
+        array_panel_sizer.AddGrowableCol(1)
+        array_panel_sizer.AddGrowableRow(1)
+        
+        array_panel_leftSizer = wx.BoxSizer(wx.HORIZONTAL)
+        array_panel_sizer.AddSizer(array_panel_leftSizer, flag=wx.GROW)
+        
+        array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:'))
+        array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.ArrayBaseType = wx.ComboBox(self.ArrayPanel, style=wx.CB_READONLY)
+        self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.ArrayBaseType)
+        array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5, 
+              flag=wx.GROW|wx.ALL)
+    
+        array_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL)
+        array_panel_sizer.AddSizer(array_panel_rightsizer, flag=wx.GROW)
+        
+        array_initialvalue_label = wx.StaticText(self.ArrayPanel,
+              label=_('Initial Value:'))
+        array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, 
+              flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
+        
+        self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel,
+              style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH)
+        self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.ArrayInitialValue)
+        array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5, 
+              flag=wx.ALL)        
+        
+        self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel, 
+              label=_("Dimensions:"), style=wx.gizmos.EL_ALLOW_NEW|
+                                            wx.gizmos.EL_ALLOW_EDIT|
+                                            wx.gizmos.EL_ALLOW_DELETE)
+        for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", 
+                     "_OnUpButton", "_OnDownButton"]:
+            setattr(self.ArrayDimensions, func, self.OnDimensionsChanged)
+        array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5, 
+              flag=wx.GROW|wx.ALL)
+        
+        self.ArrayPanel.SetSizer(array_panel_sizer)
+        
+        # Panel for Structure data types
+        
+        self.StructurePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
+        typeinfos_sizer.AddWindow(self.StructurePanel, 1)
+        
+        structure_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
+        structure_panel_sizer.AddGrowableCol(0)
+        structure_panel_sizer.AddGrowableRow(1)
+        
+        structure_button_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
+        structure_button_sizer.AddGrowableCol(0)
+        structure_button_sizer.AddGrowableRow(0)
+        structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5, 
+              flag=wx.ALL|wx.GROW)
+        
+        structure_elements_label = wx.StaticText(self.StructurePanel,
+              label=_('Elements :'))
+        structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM)
+        
+        for name, bitmap, help in [
+                ("StructureAddButton", "add_element", _("Add element")),
+                ("StructureDeleteButton", "remove_element", _("Remove element")),
+                ("StructureUpButton", "up", _("Move element up")),
+                ("StructureDownButton", "down", _("Move element down"))]:
+            button = wx.lib.buttons.GenBitmapButton(self.StructurePanel,
+                  bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER)
+            button.SetToolTipString(help)
+            setattr(self, name, button)
+            structure_button_sizer.AddWindow(button)
+        
+        self.StructureElementsGrid = CustomGrid(self.StructurePanel, 
+              size=wx.Size(0, 150), style=wx.VSCROLL)
+        self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, 
+              self.OnStructureElementsGridCellChange)
+        self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, 
+              self.OnStructureElementsGridEditorShown)
+        structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW)
+        
+        self.StructurePanel.SetSizer(structure_panel_sizer)
+        
+        self.Editor.SetSizer(self.MainSizer)
+        
+    def __init__(self, parent, tagname, window, controler):
+        EditorPanel.__init__(self, parent, tagname, window, controler)
+        
+        self.StructureElementDefaultValue = {"Name" : "", "Type" : "INT", "Initial Value" : ""}
+        self.StructureElementsTable = ElementsTable(self, [], GetElementsTableColnames())
+        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.SetButtons({"Add": self.StructureAddButton,
+                                               "Delete": self.StructureDeleteButton,
+                                               "Up": self.StructureUpButton,
+                                               "Down": self.StructureDownButton})
+        
+        def _AddStructureElement(new_row):
+            self.StructureElementsTable.InsertRow(new_row, self.StructureElementDefaultValue.copy())
+            self.RefreshTypeInfos()
+            self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+            return new_row
+        setattr(self.StructureElementsGrid, "_AddRow", _AddStructureElement)
+        
+        def _DeleteStructureElement(row):
+            self.StructureElementsTable.RemoveRow(row)
+            self.RefreshTypeInfos()
+            self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+        setattr(self.StructureElementsGrid, "_DeleteRow", _DeleteStructureElement)
+            
+        def _MoveStructureElement(row, move):
+            new_row = self.StructureElementsTable.MoveRow(row, move)
+            if new_row != row:
+                self.RefreshTypeInfos()
+                self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+            return new_row
+        setattr(self.StructureElementsGrid, "_MoveRow", _MoveStructureElement)
+        
+        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.SetColMinimalWidth(col, self.StructureColSizes[col])
+            self.StructureElementsGrid.AutoSizeColumn(col, False)
+        self.StructureElementsGrid.RefreshButtons()
+        
+        for datatype in GetDatatypeTypes():
+            self.DerivationType.Append(_(datatype))
+        self.SubrangePanel.Hide()
+        self.EnumeratedPanel.Hide()
+        self.ArrayPanel.Hide()
+        self.StructurePanel.Hide()
+        self.CurrentPanel = "Directly"
+        self.Highlights = []
+        self.Initializing = False
+        
+        self.HighlightControls = {
+            ("Directly", "base"): self.DirectlyBaseType,
+            ("Directly", "initial"): self.DirectlyInitialValue,
+            ("Subrange", "base"): self.SubrangeBaseType,
+            ("Subrange", "lower"): self.SubrangeMinimum,
+            ("Subrange", "upper"): self.SubrangeMaximum,
+            ("Subrange", "initial"): self.SubrangeInitialValue,
+            ("Enumerated", "value"): self.EnumeratedValues,
+            ("Enumerated", "initial"): self.EnumeratedInitialValue,
+            ("Array", "initial"): self.ArrayInitialValue,
+            ("Array", "base"): self.ArrayBaseType,
+            ("Array", "range"): self.ArrayDimensions,
+        }
+        
+        self.RefreshHighlightsTimer = wx.Timer(self, -1)
+        self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
+        
+    def __del__(self):
+        self.RefreshHighlightsTimer.Stop()
+    
+    def GetBufferState(self):
+        return self.Controler.GetBufferState()
+        
+    def Undo(self):
+        self.Controler.LoadPrevious()
+        self.ParentWindow.CloseTabsWithoutModel()
+            
+    def Redo(self):
+        self.Controler.LoadNext()
+        self.ParentWindow.CloseTabsWithoutModel()
+    
+    def HasNoModel(self):
+        return self.Controler.GetEditedElement(self.TagName) is None
+        
+    def RefreshView(self):
+        self.Initializing = True
+        self.DirectlyBaseType.Clear()
+        self.ArrayBaseType.Clear()
+        for datatype in self.Controler.GetDataTypes(self.TagName):
+            self.DirectlyBaseType.Append(datatype)
+            self.ArrayBaseType.Append(datatype)
+        self.DirectlyBaseType.SetSelection(0)
+        self.SubrangeBaseType.Clear()
+        words = self.TagName.split("::")
+        for base_type in self.Controler.GetSubrangeBaseTypes(words[1]):
+            self.SubrangeBaseType.Append(base_type)
+        self.SubrangeBaseType.SetSelection(0)
+        self.RefreshBoundsRange()
+        type_infos = self.Controler.GetDataTypeInfos(self.TagName)
+        if type_infos is not None:
+            datatype = type_infos["type"]
+            self.DerivationType.SetStringSelection(_(datatype))
+            if type_infos["type"] == "Directly":
+                self.DirectlyBaseType.SetStringSelection(type_infos["base_type"])
+                self.DirectlyInitialValue.SetValue(type_infos["initial"])
+            elif type_infos["type"] == "Subrange":
+                self.SubrangeBaseType.SetStringSelection(type_infos["base_type"])
+                self.RefreshBoundsRange()
+                self.SubrangeMinimum.SetValue(int(type_infos["min"]))
+                self.SubrangeMaximum.SetValue(int(type_infos["max"]))
+                self.RefreshSubrangeInitialValueRange()
+                if type_infos["initial"] != "":
+                    self.SubrangeInitialValue.SetValue(int(type_infos["initial"]))
+                else:
+                    self.SubrangeInitialValue.SetValue(type_infos["min"])
+            elif type_infos["type"] == "Enumerated":
+                self.EnumeratedValues.SetStrings(type_infos["values"])
+                self.RefreshEnumeratedValues()
+                self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"])
+            elif type_infos["type"] == "Array":
+                self.ArrayBaseType.SetStringSelection(type_infos["base_type"])
+                self.ArrayDimensions.SetStrings(map(lambda x : "..".join(x), type_infos["dimensions"]))
+                self.ArrayInitialValue.SetValue(type_infos["initial"])
+            elif type_infos["type"] == "Structure":
+                self.StructureElementsTable.SetData(type_infos["elements"])
+            self.RefreshDisplayedInfos()
+        self.ShowHighlights()
+        self.StructureElementsTable.ResetView(self.StructureElementsGrid)
+        self.StructureElementsGrid.RefreshButtons()
+        self.Initializing = False
+    
+    def OnDerivationTypeChanged(self, event):
+        wx.CallAfter(self.RefreshDisplayedInfos)
+        wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+
+    def OnReturnKeyPressed(self, event):
+        self.RefreshTypeInfos()
+        
+    def OnInfosChanged(self, event):
+        self.RefreshTypeInfos()
+        event.Skip()
+
+    def OnSubrangeBaseTypeChanged(self, event):
+        self.RefreshBoundsRange()
+        self.RefreshTypeInfos()
+        event.Skip()
+
+    def OnSubrangeMinimumChanged(self, event):
+        if not self.Initializing:
+            wx.CallAfter(self.SubrangeMinimum.SetValue, min(self.SubrangeMaximum.GetValue(), self.SubrangeMinimum.GetValue()))
+            wx.CallAfter(self.RefreshSubrangeInitialValueRange)
+            wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+
+    def OnSubrangeMaximumChanged(self, event):
+        if not self.Initializing:
+            wx.CallAfter(self.SubrangeMaximum.SetValue, max(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue()))
+            wx.CallAfter(self.RefreshSubrangeInitialValueRange)
+            wx.CallAfter(self.RefreshTypeInfos)
+        event.Skip()
+
+    def OnDimensionsChanged(self, event):
+        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:
+                initial_selected = None
+                if index < len(values) and self.EnumeratedInitialValue.GetStringSelection() == values[index]:
+                    initial_selected = text
+                wx.CallAfter(self.RefreshEnumeratedValues, initial_selected)
+                wx.CallAfter(self.RefreshTypeInfos)
+                event.Skip()
+        else:
+            event.Skip()
+    
+    def OnEnumeratedValuesChanged(self, event):
+        wx.CallAfter(self.RefreshEnumeratedValues)
+        wx.CallAfter(self.RefreshTypeInfos)
+        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, _("An element named \"%s\" already exists in this structure!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
+                message.ShowModal()
+                message.Destroy()
+                event.Veto()
+            else:
+                self.RefreshTypeInfos()
+                wx.CallAfter(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()
+            wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid)
+            event.Skip()
+    
+    def OnStructureElementsGridSelectCell(self, event):
+        wx.CallAfter(self.RefreshStructureButtons)
+        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):
+                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)
+##            pouname, poutype = self.Controler.GetEditedElementType(self.TagName)
+##            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):
+##                    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())
+            type_menu.Destroy()
+            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)
+        return ElementTypeFunction
+
+    def RefreshDisplayedInfos(self):
+        selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()]
+        if selected != self.CurrentPanel:
+            if self.CurrentPanel == "Directly":
+                self.DirectlyPanel.Hide()
+            elif self.CurrentPanel == "Subrange":
+                self.SubrangePanel.Hide()
+            elif self.CurrentPanel == "Enumerated":
+                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()
+            elif selected == "Subrange":
+                self.SubrangePanel.Show()
+            elif selected == "Enumerated":
+                self.EnumeratedPanel.Show()
+            elif selected == "Array":
+                self.ArrayPanel.Show()
+            elif selected == "Structure":
+                self.StructurePanel.Show()
+            self.MainSizer.Layout()
+
+    def RefreshEnumeratedValues(self, initial_selected=None):
+        if initial_selected is None:
+            initial_selected = self.EnumeratedInitialValue.GetStringSelection()
+        self.EnumeratedInitialValue.Clear()
+        self.EnumeratedInitialValue.Append("")
+        for value in self.EnumeratedValues.GetStrings():
+            self.EnumeratedInitialValue.Append(value)
+        self.EnumeratedInitialValue.SetStringSelection(initial_selected)
+
+    def RefreshBoundsRange(self):
+        range = self.Controler.GetDataTypeRange(self.SubrangeBaseType.GetStringSelection())
+        if range is not None:
+            min_value, max_value = range
+            self.SubrangeMinimum.SetRange(min_value, max_value)
+            self.SubrangeMinimum.SetValue(min(max(min_value, self.SubrangeMinimum.GetValue()), max_value))
+            self.SubrangeMaximum.SetRange(min_value, max_value)
+            self.SubrangeMaximum.SetValue(min(max(min_value, self.SubrangeMaximum.GetValue()), max_value))
+
+    def RefreshSubrangeInitialValueRange(self):
+        self.SubrangeInitialValue.SetRange(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue())
+
+    def RefreshTypeInfos(self):
+        selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()]
+        infos = {"type" : selected}
+        if selected == "Directly":
+            infos["base_type"] = self.DirectlyBaseType.GetStringSelection()
+            infos["initial"] = self.DirectlyInitialValue.GetValue()
+        elif selected == "Subrange":
+            infos["base_type"] = self.SubrangeBaseType.GetStringSelection()
+            infos["min"] = str(self.SubrangeMinimum.GetValue())
+            infos["max"] = str(self.SubrangeMaximum.GetValue())
+            initial_value = self.SubrangeInitialValue.GetValue()
+            if initial_value == infos["min"]:
+                infos["initial"] = ""
+            else:
+                infos["initial"] = str(initial_value)
+        elif selected == "Enumerated":
+            infos["values"] = self.EnumeratedValues.GetStrings()
+            infos["initial"] = self.EnumeratedInitialValue.GetStringSelection()
+        elif selected == "Array":
+            infos["base_type"] = self.ArrayBaseType.GetStringSelection()
+            infos["dimensions"] = []
+            for dimensions in self.ArrayDimensions.GetStrings():
+                result = DIMENSION_MODEL.match(dimensions)
+                if result is None:
+                    message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR)
+                    message.ShowModal()
+                    message.Destroy()
+                    self.RefreshView()
+                    return
+                bounds = result.groups()
+                if int(bounds[0]) >= int(bounds[1]):
+                    message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR)
+                    message.ShowModal()
+                    message.Destroy()
+                    self.RefreshView()
+                    return
+                infos["dimensions"].append(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.RefreshFileMenu()
+        self.ParentWindow.RefreshEditMenu()
+
+#-------------------------------------------------------------------------------
+#                        Highlights showing functions
+#-------------------------------------------------------------------------------
+
+    def OnRefreshHighlightsTimer(self, event):
+        self.RefreshView()
+        event.Skip()
+
+    def ClearHighlights(self, highlight_type=None):
+        if highlight_type is None:
+            self.Highlights = []
+        else:
+            self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type]
+        for control in self.HighlightControls.itervalues():
+            if isinstance(control, (wx.ComboBox, wx.SpinCtrl)):
+                control.SetBackgroundColour(wx.NullColour)
+                control.SetForegroundColour(wx.NullColour)
+            elif isinstance(control, wx.TextCtrl):
+                value = control.GetValue()
+                control.SetStyle(0, len(value), wx.TextAttr(wx.NullColour))
+            elif isinstance(control, wx.gizmos.EditableListBox):
+                listctrl = control.GetListCtrl()
+                for i in xrange(listctrl.GetItemCount()):
+                    listctrl.SetItemBackgroundColour(i, wx.NullColour)
+                    listctrl.SetItemTextColour(i, wx.NullColour)
+        self.StructureElementsTable.ClearHighlights(highlight_type)
+        self.RefreshView()
+
+    def AddHighlight(self, infos, start, end ,highlight_type):
+        self.Highlights.append((infos, start, end, highlight_type))
+        self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
+
+    def ShowHighlights(self):
+        type_infos = self.Controler.GetDataTypeInfos(self.TagName)
+        for infos, start, end, highlight_type in self.Highlights:
+            if infos[0] == "struct":
+                self.StructureElementsTable.AddHighlight(infos[1:], highlight_type)
+            else:
+                control = self.HighlightControls.get((type_infos["type"], infos[0]), None)
+                if control is not None:
+                    if isinstance(control, (wx.ComboBox, wx.SpinCtrl)):
+                        control.SetBackgroundColour(highlight_type[0])
+                        control.SetForegroundColour(highlight_type[1])
+                    elif isinstance(control, wx.TextCtrl):
+                        control.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0]))
+                    elif isinstance(control, wx.gizmos.EditableListBox):
+                        listctrl = control.GetListCtrl()
+                        listctrl.SetItemBackgroundColour(infos[1], highlight_type[0])
+                        listctrl.SetItemTextColour(infos[1], highlight_type[1])
+                        listctrl.Select(listctrl.FocusedItem, False)
+