editors/DataTypeEditor.py
changeset 814 5743cbdff669
child 864 bf4f7f0801b9
equal deleted inserted replaced
813:1460273f40ed 814:5743cbdff669
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import re
       
    26 
       
    27 import wx
       
    28 import wx.grid
       
    29 import wx.lib.buttons
       
    30 
       
    31 from plcopen.structures import IEC_KEYWORDS, TestIdentifier
       
    32 from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD
       
    33 from controls import CustomEditableListBox, CustomGrid, CustomTable
       
    34 from EditorPanel import EditorPanel
       
    35 from util.BitmapLibrary import GetBitmap
       
    36 
       
    37 #-------------------------------------------------------------------------------
       
    38 #                                    Helpers
       
    39 #-------------------------------------------------------------------------------
       
    40 
       
    41 DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$")
       
    42 
       
    43 def AppendMenu(parent, help, id, kind, text):
       
    44     parent.Append(help=help, id=id, kind=kind, text=text)
       
    45 
       
    46 def GetElementsTableColnames():
       
    47     _ = lambda x : x
       
    48     return ["#", _("Name"), _("Type"), _("Initial Value")]
       
    49 
       
    50 def GetDatatypeTypes():
       
    51     _ = lambda x : x
       
    52     return [_("Directly"), _("Subrange"), _("Enumerated"), _("Array"), _("Structure")]
       
    53 DATATYPE_TYPES_DICT = dict([(_(datatype), datatype) for datatype in GetDatatypeTypes()])
       
    54 
       
    55 #-------------------------------------------------------------------------------
       
    56 #                            Structure Elements Table
       
    57 #-------------------------------------------------------------------------------
       
    58 
       
    59 class ElementsTable(CustomTable):
       
    60     
       
    61     """
       
    62     A custom wx.grid.Grid Table using user supplied data
       
    63     """
       
    64     def __init__(self, parent, data, colnames):
       
    65         # The base class must be initialized *first*
       
    66         CustomTable.__init__(self, parent, data, colnames)
       
    67         self.old_value = None
       
    68         
       
    69     def GetValue(self, row, col):
       
    70         if row < self.GetNumberRows():
       
    71             if col == 0:
       
    72                 return row + 1
       
    73             name = str(self.data[row].get(self.GetColLabelValue(col, False), ""))
       
    74             return name
       
    75     
       
    76     def SetValue(self, row, col, value):
       
    77         if col < len(self.colnames):
       
    78             colname = self.GetColLabelValue(col, False)
       
    79             if colname == "Name":
       
    80                 self.old_value = self.data[row][colname]
       
    81             self.data[row][colname] = value
       
    82     
       
    83     def GetOldValue(self):
       
    84         return self.old_value
       
    85     
       
    86     def _updateColAttrs(self, grid):
       
    87         """
       
    88         wx.grid.Grid -> update the column attributes to add the
       
    89         appropriate renderer given the column name.
       
    90 
       
    91         Otherwise default to the default renderer.
       
    92         """
       
    93         
       
    94         for row in range(self.GetNumberRows()):
       
    95             row_highlights = self.Highlights.get(row, {})
       
    96             for col in range(self.GetNumberCols()):
       
    97                 editor = None
       
    98                 renderer = None
       
    99                 colname = self.GetColLabelValue(col, False)
       
   100                 if col != 0:
       
   101                     grid.SetReadOnly(row, col, False)
       
   102                     if colname == "Name":
       
   103                         editor = wx.grid.GridCellTextEditor()
       
   104                         renderer = wx.grid.GridCellStringRenderer()
       
   105                     elif colname == "Initial Value":
       
   106                         editor = wx.grid.GridCellTextEditor()
       
   107                         renderer = wx.grid.GridCellStringRenderer()
       
   108                     elif colname == "Type":
       
   109                         editor = wx.grid.GridCellTextEditor()
       
   110                 else:
       
   111                     grid.SetReadOnly(row, col, True)
       
   112                 
       
   113                 grid.SetCellEditor(row, col, editor)
       
   114                 grid.SetCellRenderer(row, col, renderer)
       
   115                 
       
   116                 highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1]
       
   117                 grid.SetCellBackgroundColour(row, col, highlight_colours[0])
       
   118                 grid.SetCellTextColour(row, col, highlight_colours[1])
       
   119             self.ResizeRow(grid, row)
       
   120     
       
   121     def AddHighlight(self, infos, highlight_type):
       
   122         row_highlights = self.Highlights.setdefault(infos[0], {})
       
   123         if infos[1] == "initial":
       
   124             col_highlights = row_highlights.setdefault("initial value", [])
       
   125         else:
       
   126             col_highlights = row_highlights.setdefault(infos[1], [])
       
   127         col_highlights.append(highlight_type)
       
   128 
       
   129 #-------------------------------------------------------------------------------
       
   130 #                          Datatype Editor class
       
   131 #-------------------------------------------------------------------------------
       
   132 
       
   133 class DataTypeEditor(EditorPanel):
       
   134     
       
   135     def _init_Editor(self, parent):
       
   136         self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER)
       
   137         
       
   138         self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10)
       
   139         self.MainSizer.AddGrowableCol(0)
       
   140         self.MainSizer.AddGrowableRow(1)
       
   141         
       
   142         top_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   143         self.MainSizer.AddSizer(top_sizer, border=5, 
       
   144               flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
       
   145         
       
   146         derivation_type_label = wx.StaticText(self.Editor, label=_('Derivation Type:'))
       
   147         top_sizer.AddWindow(derivation_type_label, border=5, 
       
   148               flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT)
       
   149         
       
   150         self.DerivationType = wx.ComboBox(self.Editor,
       
   151               size=wx.Size(200, -1), style=wx.CB_READONLY)
       
   152         self.Bind(wx.EVT_COMBOBOX, self.OnDerivationTypeChanged, self.DerivationType)
       
   153         top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW|wx.RIGHT)
       
   154         
       
   155         typeinfos_staticbox = wx.StaticBox(self.Editor, label=_('Type infos:'))
       
   156         typeinfos_sizer = wx.StaticBoxSizer(typeinfos_staticbox, wx.HORIZONTAL)
       
   157         self.MainSizer.AddSizer(typeinfos_sizer, border=5, 
       
   158               flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT)
       
   159         
       
   160         # Panel for Directly derived data types
       
   161 
       
   162         self.DirectlyPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
       
   163         typeinfos_sizer.AddWindow(self.DirectlyPanel, 1)
       
   164         
       
   165         directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   166         
       
   167         directly_basetype_label = wx.StaticText(self.DirectlyPanel, 
       
   168               label=_('Base Type:'))
       
   169         directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, 
       
   170               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   171         
       
   172         self.DirectlyBaseType = wx.ComboBox(self.DirectlyPanel, style=wx.CB_READONLY)
       
   173         self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.DirectlyPanel)
       
   174         directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5, 
       
   175               flag=wx.GROW|wx.ALL)
       
   176         
       
   177         directly_initialvalue_label = wx.StaticText(self.DirectlyPanel,
       
   178               label=_('Initial Value:'))
       
   179         directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, 
       
   180               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   181         
       
   182         self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, 
       
   183               style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH)
       
   184         self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.DirectlyInitialValue)
       
   185         directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5, 
       
   186               flag=wx.ALL)
       
   187         
       
   188         self.DirectlyPanel.SetSizer(directly_panel_sizer)
       
   189         
       
   190         # Panel for Subrange data types
       
   191 
       
   192         self.SubrangePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
       
   193         typeinfos_sizer.AddWindow(self.SubrangePanel, 1)
       
   194         
       
   195         subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0)
       
   196         
       
   197         subrange_basetype_label = wx.StaticText(self.SubrangePanel,
       
   198               label=_('Base Type:'))
       
   199         subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, 
       
   200               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   201         
       
   202         self.SubrangeBaseType = wx.ComboBox(self.SubrangePanel, style=wx.CB_READONLY)
       
   203         self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged, 
       
   204               self.SubrangeBaseType)
       
   205         subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5, 
       
   206               flag=wx.GROW|wx.ALL)
       
   207         
       
   208         subrange_initialvalue_label = wx.StaticText(self.SubrangePanel,
       
   209               label=_('Initial Value:'))
       
   210         subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, 
       
   211               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   212         
       
   213         self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel, 
       
   214               style=wx.TAB_TRAVERSAL)
       
   215         self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, self.SubrangeInitialValue)
       
   216         subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5, 
       
   217               flag=wx.GROW|wx.ALL)
       
   218         
       
   219         subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:'))
       
   220         subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, 
       
   221               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   222         
       
   223         self.SubrangeMinimum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL)
       
   224         self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMinimumChanged, self.SubrangeMinimum)
       
   225         subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5, 
       
   226               flag=wx.GROW|wx.ALL)
       
   227         
       
   228         for i in xrange(2):
       
   229             subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1)
       
   230         
       
   231         subrange_maximum_label = wx.StaticText(self.SubrangePanel,
       
   232               label=_('Maximum:'))
       
   233         subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, 
       
   234               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   235         
       
   236         self.SubrangeMaximum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL)
       
   237         self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMaximumChanged, self.SubrangeMaximum)
       
   238         
       
   239         subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5, 
       
   240               flag=wx.GROW|wx.ALL)
       
   241         
       
   242         self.SubrangePanel.SetSizer(subrange_panel_sizer)
       
   243         
       
   244         # Panel for Enumerated data types
       
   245         
       
   246         self.EnumeratedPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
       
   247         typeinfos_sizer.AddWindow(self.EnumeratedPanel, 1)
       
   248         
       
   249         enumerated_panel_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   250         
       
   251         self.EnumeratedValues = CustomEditableListBox(self.EnumeratedPanel, 
       
   252               label=_("Values:"), style=wx.gizmos.EL_ALLOW_NEW| 
       
   253                                         wx.gizmos.EL_ALLOW_EDIT| 
       
   254                                         wx.gizmos.EL_ALLOW_DELETE)
       
   255         setattr(self.EnumeratedValues, "_OnLabelEndEdit", self.OnEnumeratedValueEndEdit)
       
   256         for func in ["_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]:
       
   257             setattr(self.EnumeratedValues, func, self.OnEnumeratedValuesChanged)
       
   258         enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5, 
       
   259               flag=wx.GROW|wx.ALL)
       
   260         
       
   261         enumerated_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL)
       
   262         enumerated_panel_sizer.AddSizer(enumerated_panel_rightsizer, 1)
       
   263         
       
   264         enumerated_initialvalue_label = wx.StaticText(self.EnumeratedPanel,
       
   265               label=_('Initial Value:'))
       
   266         enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, 
       
   267               border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   268         
       
   269         self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, 
       
   270               style=wx.CB_READONLY)
       
   271         self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue)
       
   272         enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1, 
       
   273               border=5, flag=wx.ALL)
       
   274         
       
   275         self.EnumeratedPanel.SetSizer(enumerated_panel_sizer)
       
   276         
       
   277         # Panel for Array data types
       
   278 
       
   279         self.ArrayPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
       
   280         typeinfos_sizer.AddWindow(self.ArrayPanel, 1)
       
   281         
       
   282         array_panel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=0)
       
   283         array_panel_sizer.AddGrowableCol(0)
       
   284         array_panel_sizer.AddGrowableCol(1)
       
   285         array_panel_sizer.AddGrowableRow(1)
       
   286         
       
   287         array_panel_leftSizer = wx.BoxSizer(wx.HORIZONTAL)
       
   288         array_panel_sizer.AddSizer(array_panel_leftSizer, flag=wx.GROW)
       
   289         
       
   290         array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:'))
       
   291         array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, 
       
   292               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   293         
       
   294         self.ArrayBaseType = wx.ComboBox(self.ArrayPanel, style=wx.CB_READONLY)
       
   295         self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.ArrayBaseType)
       
   296         array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5, 
       
   297               flag=wx.GROW|wx.ALL)
       
   298     
       
   299         array_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL)
       
   300         array_panel_sizer.AddSizer(array_panel_rightsizer, flag=wx.GROW)
       
   301         
       
   302         array_initialvalue_label = wx.StaticText(self.ArrayPanel,
       
   303               label=_('Initial Value:'))
       
   304         array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, 
       
   305               flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL)
       
   306         
       
   307         self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel,
       
   308               style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH)
       
   309         self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.ArrayInitialValue)
       
   310         array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5, 
       
   311               flag=wx.ALL)        
       
   312         
       
   313         self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel, 
       
   314               label=_("Dimensions:"), style=wx.gizmos.EL_ALLOW_NEW|
       
   315                                             wx.gizmos.EL_ALLOW_EDIT|
       
   316                                             wx.gizmos.EL_ALLOW_DELETE)
       
   317         for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", 
       
   318                      "_OnUpButton", "_OnDownButton"]:
       
   319             setattr(self.ArrayDimensions, func, self.OnDimensionsChanged)
       
   320         array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5, 
       
   321               flag=wx.GROW|wx.ALL)
       
   322         
       
   323         self.ArrayPanel.SetSizer(array_panel_sizer)
       
   324         
       
   325         # Panel for Structure data types
       
   326         
       
   327         self.StructurePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL)
       
   328         typeinfos_sizer.AddWindow(self.StructurePanel, 1)
       
   329         
       
   330         structure_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
   331         structure_panel_sizer.AddGrowableCol(0)
       
   332         structure_panel_sizer.AddGrowableRow(1)
       
   333         
       
   334         structure_button_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0)
       
   335         structure_button_sizer.AddGrowableCol(0)
       
   336         structure_button_sizer.AddGrowableRow(0)
       
   337         structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5, 
       
   338               flag=wx.ALL|wx.GROW)
       
   339         
       
   340         structure_elements_label = wx.StaticText(self.StructurePanel,
       
   341               label=_('Elements :'))
       
   342         structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM)
       
   343         
       
   344         for name, bitmap, help in [
       
   345                 ("StructureAddButton", "add_element", _("Add element")),
       
   346                 ("StructureDeleteButton", "remove_element", _("Remove element")),
       
   347                 ("StructureUpButton", "up", _("Move element up")),
       
   348                 ("StructureDownButton", "down", _("Move element down"))]:
       
   349             button = wx.lib.buttons.GenBitmapButton(self.StructurePanel,
       
   350                   bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   351             button.SetToolTipString(help)
       
   352             setattr(self, name, button)
       
   353             structure_button_sizer.AddWindow(button)
       
   354         
       
   355         self.StructureElementsGrid = CustomGrid(self.StructurePanel, 
       
   356               size=wx.Size(0, 150), style=wx.VSCROLL)
       
   357         self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, 
       
   358               self.OnStructureElementsGridCellChange)
       
   359         self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, 
       
   360               self.OnStructureElementsGridEditorShown)
       
   361         structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW)
       
   362         
       
   363         self.StructurePanel.SetSizer(structure_panel_sizer)
       
   364         
       
   365         self.Editor.SetSizer(self.MainSizer)
       
   366         
       
   367     def __init__(self, parent, tagname, window, controler):
       
   368         EditorPanel.__init__(self, parent, tagname, window, controler)
       
   369         
       
   370         self.StructureElementDefaultValue = {"Name" : "", "Type" : "INT", "Initial Value" : ""}
       
   371         self.StructureElementsTable = ElementsTable(self, [], GetElementsTableColnames())
       
   372         self.StructureColSizes = [40, 150, 100, 250]
       
   373         self.StructureColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]
       
   374         
       
   375         self.StructureElementsGrid.SetTable(self.StructureElementsTable)
       
   376         self.StructureElementsGrid.SetButtons({"Add": self.StructureAddButton,
       
   377                                                "Delete": self.StructureDeleteButton,
       
   378                                                "Up": self.StructureUpButton,
       
   379                                                "Down": self.StructureDownButton})
       
   380         
       
   381         def _AddStructureElement(new_row):
       
   382             self.StructureElementsTable.InsertRow(new_row, self.StructureElementDefaultValue.copy())
       
   383             self.RefreshTypeInfos()
       
   384             self.StructureElementsTable.ResetView(self.StructureElementsGrid)
       
   385             return new_row
       
   386         setattr(self.StructureElementsGrid, "_AddRow", _AddStructureElement)
       
   387         
       
   388         def _DeleteStructureElement(row):
       
   389             self.StructureElementsTable.RemoveRow(row)
       
   390             self.RefreshTypeInfos()
       
   391             self.StructureElementsTable.ResetView(self.StructureElementsGrid)
       
   392         setattr(self.StructureElementsGrid, "_DeleteRow", _DeleteStructureElement)
       
   393             
       
   394         def _MoveStructureElement(row, move):
       
   395             new_row = self.StructureElementsTable.MoveRow(row, move)
       
   396             if new_row != row:
       
   397                 self.RefreshTypeInfos()
       
   398                 self.StructureElementsTable.ResetView(self.StructureElementsGrid)
       
   399             return new_row
       
   400         setattr(self.StructureElementsGrid, "_MoveRow", _MoveStructureElement)
       
   401         
       
   402         self.StructureElementsGrid.SetRowLabelSize(0)
       
   403         for col in range(self.StructureElementsTable.GetNumberCols()):
       
   404             attr = wx.grid.GridCellAttr()
       
   405             attr.SetAlignment(self.StructureColAlignements[col], wx.ALIGN_CENTRE)
       
   406             self.StructureElementsGrid.SetColAttr(col, attr)
       
   407             self.StructureElementsGrid.SetColMinimalWidth(col, self.StructureColSizes[col])
       
   408             self.StructureElementsGrid.AutoSizeColumn(col, False)
       
   409         self.StructureElementsGrid.RefreshButtons()
       
   410         
       
   411         for datatype in GetDatatypeTypes():
       
   412             self.DerivationType.Append(_(datatype))
       
   413         self.SubrangePanel.Hide()
       
   414         self.EnumeratedPanel.Hide()
       
   415         self.ArrayPanel.Hide()
       
   416         self.StructurePanel.Hide()
       
   417         self.CurrentPanel = "Directly"
       
   418         self.Highlights = []
       
   419         self.Initializing = False
       
   420         
       
   421         self.HighlightControls = {
       
   422             ("Directly", "base"): self.DirectlyBaseType,
       
   423             ("Directly", "initial"): self.DirectlyInitialValue,
       
   424             ("Subrange", "base"): self.SubrangeBaseType,
       
   425             ("Subrange", "lower"): self.SubrangeMinimum,
       
   426             ("Subrange", "upper"): self.SubrangeMaximum,
       
   427             ("Subrange", "initial"): self.SubrangeInitialValue,
       
   428             ("Enumerated", "value"): self.EnumeratedValues,
       
   429             ("Enumerated", "initial"): self.EnumeratedInitialValue,
       
   430             ("Array", "initial"): self.ArrayInitialValue,
       
   431             ("Array", "base"): self.ArrayBaseType,
       
   432             ("Array", "range"): self.ArrayDimensions,
       
   433         }
       
   434         
       
   435         self.RefreshHighlightsTimer = wx.Timer(self, -1)
       
   436         self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
       
   437         
       
   438     def __del__(self):
       
   439         self.RefreshHighlightsTimer.Stop()
       
   440     
       
   441     def GetBufferState(self):
       
   442         return self.Controler.GetBufferState()
       
   443         
       
   444     def Undo(self):
       
   445         self.Controler.LoadPrevious()
       
   446         self.ParentWindow.CloseTabsWithoutModel()
       
   447             
       
   448     def Redo(self):
       
   449         self.Controler.LoadNext()
       
   450         self.ParentWindow.CloseTabsWithoutModel()
       
   451     
       
   452     def HasNoModel(self):
       
   453         return self.Controler.GetEditedElement(self.TagName) is None
       
   454         
       
   455     def RefreshView(self):
       
   456         self.Initializing = True
       
   457         self.DirectlyBaseType.Clear()
       
   458         self.ArrayBaseType.Clear()
       
   459         for datatype in self.Controler.GetDataTypes(self.TagName):
       
   460             self.DirectlyBaseType.Append(datatype)
       
   461             self.ArrayBaseType.Append(datatype)
       
   462         self.DirectlyBaseType.SetSelection(0)
       
   463         self.SubrangeBaseType.Clear()
       
   464         words = self.TagName.split("::")
       
   465         for base_type in self.Controler.GetSubrangeBaseTypes(words[1]):
       
   466             self.SubrangeBaseType.Append(base_type)
       
   467         self.SubrangeBaseType.SetSelection(0)
       
   468         self.RefreshBoundsRange()
       
   469         type_infos = self.Controler.GetDataTypeInfos(self.TagName)
       
   470         if type_infos is not None:
       
   471             datatype = type_infos["type"]
       
   472             self.DerivationType.SetStringSelection(_(datatype))
       
   473             if type_infos["type"] == "Directly":
       
   474                 self.DirectlyBaseType.SetStringSelection(type_infos["base_type"])
       
   475                 self.DirectlyInitialValue.SetValue(type_infos["initial"])
       
   476             elif type_infos["type"] == "Subrange":
       
   477                 self.SubrangeBaseType.SetStringSelection(type_infos["base_type"])
       
   478                 self.RefreshBoundsRange()
       
   479                 self.SubrangeMinimum.SetValue(int(type_infos["min"]))
       
   480                 self.SubrangeMaximum.SetValue(int(type_infos["max"]))
       
   481                 self.RefreshSubrangeInitialValueRange()
       
   482                 if type_infos["initial"] != "":
       
   483                     self.SubrangeInitialValue.SetValue(int(type_infos["initial"]))
       
   484                 else:
       
   485                     self.SubrangeInitialValue.SetValue(type_infos["min"])
       
   486             elif type_infos["type"] == "Enumerated":
       
   487                 self.EnumeratedValues.SetStrings(type_infos["values"])
       
   488                 self.RefreshEnumeratedValues()
       
   489                 self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"])
       
   490             elif type_infos["type"] == "Array":
       
   491                 self.ArrayBaseType.SetStringSelection(type_infos["base_type"])
       
   492                 self.ArrayDimensions.SetStrings(map(lambda x : "..".join(x), type_infos["dimensions"]))
       
   493                 self.ArrayInitialValue.SetValue(type_infos["initial"])
       
   494             elif type_infos["type"] == "Structure":
       
   495                 self.StructureElementsTable.SetData(type_infos["elements"])
       
   496             self.RefreshDisplayedInfos()
       
   497         self.ShowHighlights()
       
   498         self.StructureElementsTable.ResetView(self.StructureElementsGrid)
       
   499         self.StructureElementsGrid.RefreshButtons()
       
   500         self.Initializing = False
       
   501     
       
   502     def OnDerivationTypeChanged(self, event):
       
   503         wx.CallAfter(self.RefreshDisplayedInfos)
       
   504         wx.CallAfter(self.RefreshTypeInfos)
       
   505         event.Skip()
       
   506 
       
   507     def OnReturnKeyPressed(self, event):
       
   508         self.RefreshTypeInfos()
       
   509         
       
   510     def OnInfosChanged(self, event):
       
   511         self.RefreshTypeInfos()
       
   512         event.Skip()
       
   513 
       
   514     def OnSubrangeBaseTypeChanged(self, event):
       
   515         self.RefreshBoundsRange()
       
   516         self.RefreshTypeInfos()
       
   517         event.Skip()
       
   518 
       
   519     def OnSubrangeMinimumChanged(self, event):
       
   520         if not self.Initializing:
       
   521             wx.CallAfter(self.SubrangeMinimum.SetValue, min(self.SubrangeMaximum.GetValue(), self.SubrangeMinimum.GetValue()))
       
   522             wx.CallAfter(self.RefreshSubrangeInitialValueRange)
       
   523             wx.CallAfter(self.RefreshTypeInfos)
       
   524         event.Skip()
       
   525 
       
   526     def OnSubrangeMaximumChanged(self, event):
       
   527         if not self.Initializing:
       
   528             wx.CallAfter(self.SubrangeMaximum.SetValue, max(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue()))
       
   529             wx.CallAfter(self.RefreshSubrangeInitialValueRange)
       
   530             wx.CallAfter(self.RefreshTypeInfos)
       
   531         event.Skip()
       
   532 
       
   533     def OnDimensionsChanged(self, event):
       
   534         wx.CallAfter(self.RefreshTypeInfos)
       
   535         event.Skip()
       
   536 
       
   537     def OnEnumeratedValueEndEdit(self, event):
       
   538         text = event.GetText()
       
   539         values = self.EnumeratedValues.GetStrings()
       
   540         index = event.GetIndex()
       
   541         if index >= len(values) or values[index].upper() != text.upper():
       
   542             if text.upper() in [value.upper() for value in values]:
       
   543                 message = wx.MessageDialog(self, _("\"%s\" value already defined!")%text, _("Error"), wx.OK|wx.ICON_ERROR)
       
   544                 message.ShowModal()
       
   545                 message.Destroy()
       
   546                 event.Veto()
       
   547             elif text.upper() in IEC_KEYWORDS:
       
   548                 message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%text, _("Error"), wx.OK|wx.ICON_ERROR)
       
   549                 message.ShowModal()
       
   550                 message.Destroy()
       
   551             else:
       
   552                 initial_selected = None
       
   553                 if index < len(values) and self.EnumeratedInitialValue.GetStringSelection() == values[index]:
       
   554                     initial_selected = text
       
   555                 wx.CallAfter(self.RefreshEnumeratedValues, initial_selected)
       
   556                 wx.CallAfter(self.RefreshTypeInfos)
       
   557                 event.Skip()
       
   558         else:
       
   559             event.Skip()
       
   560     
       
   561     def OnEnumeratedValuesChanged(self, event):
       
   562         wx.CallAfter(self.RefreshEnumeratedValues)
       
   563         wx.CallAfter(self.RefreshTypeInfos)
       
   564         event.Skip()
       
   565     
       
   566     def OnStructureElementsGridCellChange(self, event):
       
   567         row, col = event.GetRow(), event.GetCol()
       
   568         colname = self.StructureElementsTable.GetColLabelValue(col)
       
   569         value = self.StructureElementsTable.GetValue(row, col)
       
   570         if colname == "Name":
       
   571             if not TestIdentifier(value):
       
   572                 message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   573                 message.ShowModal()
       
   574                 message.Destroy()
       
   575                 event.Veto()
       
   576             elif value.upper() in IEC_KEYWORDS:
       
   577                 message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   578                 message.ShowModal()
       
   579                 message.Destroy()
       
   580                 event.Veto()
       
   581 ##            elif value.upper() in self.PouNames:
       
   582 ##                message = wx.MessageDialog(self, "A pou with \"%s\" as name exists!"%value, "Error", wx.OK|wx.ICON_ERROR)
       
   583 ##                message.ShowModal()
       
   584 ##                message.Destroy()
       
   585 ##                event.Veto()
       
   586             elif value.upper() in [var["Name"].upper() for idx, var in enumerate(self.StructureElementsTable.GetData()) if idx != row]:
       
   587                 message = wx.MessageDialog(self, _("An element named \"%s\" already exists in this structure!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   588                 message.ShowModal()
       
   589                 message.Destroy()
       
   590                 event.Veto()
       
   591             else:
       
   592                 self.RefreshTypeInfos()
       
   593                 wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid)
       
   594 ##                old_value = self.Table.GetOldValue()
       
   595 ##                if old_value != "":
       
   596 ##                    self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value)
       
   597 ##                self.Controler.BufferProject()
       
   598                 event.Skip()
       
   599         else:
       
   600             self.RefreshTypeInfos()
       
   601             wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid)
       
   602             event.Skip()
       
   603     
       
   604     def OnStructureElementsGridSelectCell(self, event):
       
   605         wx.CallAfter(self.RefreshStructureButtons)
       
   606         event.Skip()
       
   607     
       
   608     def OnStructureElementsGridEditorShown(self, event):
       
   609         row, col = event.GetRow(), event.GetCol() 
       
   610         if self.StructureElementsTable.GetColLabelValue(col) == "Type":
       
   611             type_menu = wx.Menu(title='')
       
   612             base_menu = wx.Menu(title='')
       
   613             for base_type in self.Controler.GetBaseTypes():
       
   614                 new_id = wx.NewId()
       
   615                 AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
       
   616                 self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(base_type), id=new_id)
       
   617             type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu)
       
   618             datatype_menu = wx.Menu(title='')
       
   619             for datatype in self.Controler.GetDataTypes(self.TagName, False):
       
   620                 new_id = wx.NewId()
       
   621                 AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
       
   622                 self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(datatype), id=new_id)
       
   623             type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu)
       
   624 ##            functionblock_menu = wx.Menu(title='')
       
   625 ##            bodytype = self.Controler.GetEditedElementBodyType(self.TagName)
       
   626 ##            pouname, poutype = self.Controler.GetEditedElementType(self.TagName)
       
   627 ##            if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]:
       
   628 ##                for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName):
       
   629 ##                    new_id = wx.NewId()
       
   630 ##                    AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type)
       
   631 ##                    self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id)
       
   632 ##                type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu)
       
   633             rect = self.StructureElementsGrid.BlockToDeviceRect((row, col), (row, col))
       
   634             self.StructureElementsGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize())
       
   635             type_menu.Destroy()
       
   636             event.Veto()
       
   637         else:
       
   638             event.Skip()
       
   639 
       
   640     def GetElementTypeFunction(self, base_type):
       
   641         def ElementTypeFunction(event):
       
   642             row = self.StructureElementsGrid.GetGridCursorRow()
       
   643             self.StructureElementsTable.SetValueByName(row, "Type", base_type)
       
   644             self.RefreshTypeInfos()
       
   645             self.StructureElementsTable.ResetView(self.StructureElementsGrid)
       
   646         return ElementTypeFunction
       
   647 
       
   648     def RefreshDisplayedInfos(self):
       
   649         selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()]
       
   650         if selected != self.CurrentPanel:
       
   651             if self.CurrentPanel == "Directly":
       
   652                 self.DirectlyPanel.Hide()
       
   653             elif self.CurrentPanel == "Subrange":
       
   654                 self.SubrangePanel.Hide()
       
   655             elif self.CurrentPanel == "Enumerated":
       
   656                 self.EnumeratedPanel.Hide()
       
   657             elif self.CurrentPanel == "Array":
       
   658                 self.ArrayPanel.Hide()
       
   659             elif self.CurrentPanel == "Structure":
       
   660                 self.StructurePanel.Hide()
       
   661             self.CurrentPanel = selected
       
   662             if selected == "Directly":
       
   663                 self.DirectlyPanel.Show()
       
   664             elif selected == "Subrange":
       
   665                 self.SubrangePanel.Show()
       
   666             elif selected == "Enumerated":
       
   667                 self.EnumeratedPanel.Show()
       
   668             elif selected == "Array":
       
   669                 self.ArrayPanel.Show()
       
   670             elif selected == "Structure":
       
   671                 self.StructurePanel.Show()
       
   672             self.MainSizer.Layout()
       
   673 
       
   674     def RefreshEnumeratedValues(self, initial_selected=None):
       
   675         if initial_selected is None:
       
   676             initial_selected = self.EnumeratedInitialValue.GetStringSelection()
       
   677         self.EnumeratedInitialValue.Clear()
       
   678         self.EnumeratedInitialValue.Append("")
       
   679         for value in self.EnumeratedValues.GetStrings():
       
   680             self.EnumeratedInitialValue.Append(value)
       
   681         self.EnumeratedInitialValue.SetStringSelection(initial_selected)
       
   682 
       
   683     def RefreshBoundsRange(self):
       
   684         range = self.Controler.GetDataTypeRange(self.SubrangeBaseType.GetStringSelection())
       
   685         if range is not None:
       
   686             min_value, max_value = range
       
   687             self.SubrangeMinimum.SetRange(min_value, max_value)
       
   688             self.SubrangeMinimum.SetValue(min(max(min_value, self.SubrangeMinimum.GetValue()), max_value))
       
   689             self.SubrangeMaximum.SetRange(min_value, max_value)
       
   690             self.SubrangeMaximum.SetValue(min(max(min_value, self.SubrangeMaximum.GetValue()), max_value))
       
   691 
       
   692     def RefreshSubrangeInitialValueRange(self):
       
   693         self.SubrangeInitialValue.SetRange(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue())
       
   694 
       
   695     def RefreshTypeInfos(self):
       
   696         selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()]
       
   697         infos = {"type" : selected}
       
   698         if selected == "Directly":
       
   699             infos["base_type"] = self.DirectlyBaseType.GetStringSelection()
       
   700             infos["initial"] = self.DirectlyInitialValue.GetValue()
       
   701         elif selected == "Subrange":
       
   702             infos["base_type"] = self.SubrangeBaseType.GetStringSelection()
       
   703             infos["min"] = str(self.SubrangeMinimum.GetValue())
       
   704             infos["max"] = str(self.SubrangeMaximum.GetValue())
       
   705             initial_value = self.SubrangeInitialValue.GetValue()
       
   706             if initial_value == infos["min"]:
       
   707                 infos["initial"] = ""
       
   708             else:
       
   709                 infos["initial"] = str(initial_value)
       
   710         elif selected == "Enumerated":
       
   711             infos["values"] = self.EnumeratedValues.GetStrings()
       
   712             infos["initial"] = self.EnumeratedInitialValue.GetStringSelection()
       
   713         elif selected == "Array":
       
   714             infos["base_type"] = self.ArrayBaseType.GetStringSelection()
       
   715             infos["dimensions"] = []
       
   716             for dimensions in self.ArrayDimensions.GetStrings():
       
   717                 result = DIMENSION_MODEL.match(dimensions)
       
   718                 if result is None:
       
   719                     message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR)
       
   720                     message.ShowModal()
       
   721                     message.Destroy()
       
   722                     self.RefreshView()
       
   723                     return
       
   724                 bounds = result.groups()
       
   725                 if int(bounds[0]) >= int(bounds[1]):
       
   726                     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)
       
   727                     message.ShowModal()
       
   728                     message.Destroy()
       
   729                     self.RefreshView()
       
   730                     return
       
   731                 infos["dimensions"].append(bounds)
       
   732             infos["initial"] = self.ArrayInitialValue.GetValue()
       
   733         elif selected == "Structure":
       
   734             infos["elements"] = self.StructureElementsTable.GetData()
       
   735             infos["initial"] = ""
       
   736         self.Controler.SetDataTypeInfos(self.TagName, infos)
       
   737         self.ParentWindow.RefreshTitle()
       
   738         self.ParentWindow.RefreshFileMenu()
       
   739         self.ParentWindow.RefreshEditMenu()
       
   740 
       
   741 #-------------------------------------------------------------------------------
       
   742 #                        Highlights showing functions
       
   743 #-------------------------------------------------------------------------------
       
   744 
       
   745     def OnRefreshHighlightsTimer(self, event):
       
   746         self.RefreshView()
       
   747         event.Skip()
       
   748 
       
   749     def ClearHighlights(self, highlight_type=None):
       
   750         if highlight_type is None:
       
   751             self.Highlights = []
       
   752         else:
       
   753             self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type]
       
   754         for control in self.HighlightControls.itervalues():
       
   755             if isinstance(control, (wx.ComboBox, wx.SpinCtrl)):
       
   756                 control.SetBackgroundColour(wx.NullColour)
       
   757                 control.SetForegroundColour(wx.NullColour)
       
   758             elif isinstance(control, wx.TextCtrl):
       
   759                 value = control.GetValue()
       
   760                 control.SetStyle(0, len(value), wx.TextAttr(wx.NullColour))
       
   761             elif isinstance(control, wx.gizmos.EditableListBox):
       
   762                 listctrl = control.GetListCtrl()
       
   763                 for i in xrange(listctrl.GetItemCount()):
       
   764                     listctrl.SetItemBackgroundColour(i, wx.NullColour)
       
   765                     listctrl.SetItemTextColour(i, wx.NullColour)
       
   766         self.StructureElementsTable.ClearHighlights(highlight_type)
       
   767         self.RefreshView()
       
   768 
       
   769     def AddHighlight(self, infos, start, end ,highlight_type):
       
   770         self.Highlights.append((infos, start, end, highlight_type))
       
   771         self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
       
   772 
       
   773     def ShowHighlights(self):
       
   774         type_infos = self.Controler.GetDataTypeInfos(self.TagName)
       
   775         for infos, start, end, highlight_type in self.Highlights:
       
   776             if infos[0] == "struct":
       
   777                 self.StructureElementsTable.AddHighlight(infos[1:], highlight_type)
       
   778             else:
       
   779                 control = self.HighlightControls.get((type_infos["type"], infos[0]), None)
       
   780                 if control is not None:
       
   781                     if isinstance(control, (wx.ComboBox, wx.SpinCtrl)):
       
   782                         control.SetBackgroundColour(highlight_type[0])
       
   783                         control.SetForegroundColour(highlight_type[1])
       
   784                     elif isinstance(control, wx.TextCtrl):
       
   785                         control.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0]))
       
   786                     elif isinstance(control, wx.gizmos.EditableListBox):
       
   787                         listctrl = control.GetListCtrl()
       
   788                         listctrl.SetItemBackgroundColour(infos[1], highlight_type[0])
       
   789                         listctrl.SetItemTextColour(infos[1], highlight_type[1])
       
   790                         listctrl.Select(listctrl.FocusedItem, False)
       
   791