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