VariablePanel.py
changeset 418 a06221a0930b
child 419 8f97ed01a6a6
equal deleted inserted replaced
417:218142afdb53 418:a06221a0930b
       
     1 # -*- coding: utf-8 -*-
       
     2 
       
     3 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     4 #based on the plcopen standard. 
       
     5 #
       
     6 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
       
     7 #
       
     8 #See COPYING file for copyrights details.
       
     9 #
       
    10 #This library is free software; you can redistribute it and/or
       
    11 #modify it under the terms of the GNU General Public
       
    12 #License as published by the Free Software Foundation; either
       
    13 #version 2.1 of the License, or (at your option) any later version.
       
    14 #
       
    15 #This library is distributed in the hope that it will be useful,
       
    16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    18 #General Public License for more details.
       
    19 #
       
    20 #You should have received a copy of the GNU General Public
       
    21 #License along with this library; if not, write to the Free Software
       
    22 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    23 
       
    24 import wx, wx.grid
       
    25 
       
    26 from PLCOpenEditor import AppendMenu
       
    27 from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS
       
    28 
       
    29 #-------------------------------------------------------------------------------
       
    30 #                            Variables Editor Panel
       
    31 #-------------------------------------------------------------------------------
       
    32 
       
    33 def GetVariableTableColnames(location):
       
    34     _ = lambda x : x
       
    35     if location:
       
    36     	return ["#", _("Name"), _("Class"), _("Type"), _("Location"), _("Initial Value"), _("Retain"), _("Constant")]
       
    37     return ["#", _("Name"), _("Class"), _("Type"), _("Initial Value"), _("Retain"), _("Constant")]
       
    38 
       
    39 def GetAlternativeOptions():
       
    40     _ = lambda x : x
       
    41     return [_("Yes"), _("No")]
       
    42 ALTERNATIVE_OPTIONS_DICT = dict([(_(option), option) for option in GetAlternativeOptions()])
       
    43 
       
    44 def GetFilterChoiceTransfer():
       
    45     _ = lambda x : x
       
    46     return {_("All"): _("All"), _("Interface"): _("Interface"), 
       
    47             _("   Input"): _("Input"), _("   Output"): _("Output"), _("   InOut"): _("InOut"), 
       
    48             _("   External"): _("External"), _("Variables"): _("Variables"), _("   Local"): _("Local"),
       
    49             _("   Temp"): _("Temp"), _("Global"): _("Global")}#, _("Access") : _("Access")}
       
    50 VARIABLE_CLASSES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().itervalues()])
       
    51 
       
    52 class VariableTable(wx.grid.PyGridTableBase):
       
    53     
       
    54     """
       
    55     A custom wx.grid.Grid Table using user supplied data
       
    56     """
       
    57     def __init__(self, parent, data, colnames):
       
    58         # The base class must be initialized *first*
       
    59         wx.grid.PyGridTableBase.__init__(self)
       
    60         self.data = data
       
    61         self.old_value = None
       
    62         self.colnames = colnames
       
    63         self.Errors = {}
       
    64         self.Parent = parent
       
    65         # XXX
       
    66         # we need to store the row length and collength to
       
    67         # see if the table has changed size
       
    68         self._rows = self.GetNumberRows()
       
    69         self._cols = self.GetNumberCols()
       
    70     
       
    71     def GetNumberCols(self):
       
    72         return len(self.colnames)
       
    73         
       
    74     def GetNumberRows(self):
       
    75         return len(self.data)
       
    76 
       
    77     def GetColLabelValue(self, col, translate=True):
       
    78         if col < len(self.colnames):
       
    79             if translate:
       
    80                 return _(self.colnames[col])
       
    81             return self.colnames[col]
       
    82 
       
    83     def GetRowLabelValues(self, row, translate=True):
       
    84         return row
       
    85 
       
    86     def GetValue(self, row, col):
       
    87         if row < self.GetNumberRows():
       
    88             if col == 0:
       
    89                 return self.data[row]["Number"]
       
    90             colname = self.GetColLabelValue(col, False)
       
    91             value = str(self.data[row].get(colname, ""))
       
    92             if colname in ["Class", "Retain", "Constant"]:
       
    93                 return _(value)
       
    94             return value
       
    95     
       
    96     def SetValue(self, row, col, value):
       
    97         if col < len(self.colnames):
       
    98             colname = self.GetColLabelValue(col, False)
       
    99             if colname == "Name":
       
   100                 self.old_value = self.data[row][colname]
       
   101             elif colname == "Class":
       
   102                 value = VARIABLE_CLASSES_DICT[value]
       
   103             elif colname in ["Retain", "Constant"]:
       
   104                 value = ALTERNATIVE_OPTIONS_DICT[value]
       
   105             self.data[row][colname] = value
       
   106     
       
   107     def GetValueByName(self, row, colname):
       
   108         if row < self.GetNumberRows():
       
   109             return self.data[row].get(colname)
       
   110 
       
   111     def SetValueByName(self, row, colname, value):
       
   112         if row < self.GetNumberRows():
       
   113             self.data[row][colname] = value
       
   114 
       
   115     def GetOldValue(self):
       
   116         return self.old_value
       
   117     
       
   118     def ResetView(self, grid):
       
   119         """
       
   120         (wx.grid.Grid) -> Reset the grid view.   Call this to
       
   121         update the grid if rows and columns have been added or deleted
       
   122         """
       
   123         grid.BeginBatch()
       
   124         for current, new, delmsg, addmsg in [
       
   125             (self._rows, self.GetNumberRows(), wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
       
   126             (self._cols, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
       
   127         ]:
       
   128             if new < current:
       
   129                 msg = wx.grid.GridTableMessage(self,delmsg,new,current-new)
       
   130                 grid.ProcessTableMessage(msg)
       
   131             elif new > current:
       
   132                 msg = wx.grid.GridTableMessage(self,addmsg,new-current)
       
   133                 grid.ProcessTableMessage(msg)
       
   134                 self.UpdateValues(grid)
       
   135         grid.EndBatch()
       
   136 
       
   137         self._rows = self.GetNumberRows()
       
   138         self._cols = self.GetNumberCols()
       
   139         # update the column rendering scheme
       
   140         self._updateColAttrs(grid)
       
   141 
       
   142         # update the scrollbars and the displayed part of the grid
       
   143         grid.AdjustScrollbars()
       
   144         grid.ForceRefresh()
       
   145 
       
   146     def UpdateValues(self, grid):
       
   147         """Update all displayed values"""
       
   148         # This sends an event to the grid table to update all of the values
       
   149         msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
       
   150         grid.ProcessTableMessage(msg)
       
   151 
       
   152     def _updateColAttrs(self, grid):
       
   153         """
       
   154         wx.grid.Grid -> update the column attributes to add the
       
   155         appropriate renderer given the column name.
       
   156 
       
   157         Otherwise default to the default renderer.
       
   158         """
       
   159         
       
   160         for row in range(self.GetNumberRows()):
       
   161             for col in range(self.GetNumberCols()):
       
   162                 editor = None
       
   163                 renderer = None
       
   164                 colname = self.GetColLabelValue(col, False)
       
   165                 if col != 0 and self.GetValueByName(row, "Edit"):
       
   166                     grid.SetReadOnly(row, col, False)
       
   167                     if colname == "Name":
       
   168                         if self.Parent.PouIsUsed and self.GetValueByName(row, "Class") in ["Input", "Output", "InOut"]:
       
   169                             grid.SetReadOnly(row, col, True)
       
   170                         else:
       
   171                             editor = wx.grid.GridCellTextEditor()
       
   172                             renderer = wx.grid.GridCellStringRenderer()
       
   173                     elif colname == "Initial Value":
       
   174                         editor = wx.grid.GridCellTextEditor()
       
   175                         renderer = wx.grid.GridCellStringRenderer()
       
   176                     elif colname == "Location":
       
   177                         if self.GetValueByName(row, "Class") in ["Local", "Global"]:
       
   178                             editor = wx.grid.GridCellTextEditor()
       
   179                             renderer = wx.grid.GridCellStringRenderer()
       
   180                         else:
       
   181                             grid.SetReadOnly(row, col, True)
       
   182                     elif colname == "Class":
       
   183                         if len(self.Parent.ClassList) == 1 or self.Parent.PouIsUsed and self.GetValueByName(row, "Class") in ["Input", "Output", "InOut"]:
       
   184                             grid.SetReadOnly(row, col, True)
       
   185                         else:
       
   186                             editor = wx.grid.GridCellChoiceEditor()
       
   187                             excluded = []
       
   188                             if self.Parent.PouIsUsed:
       
   189                                 excluded.extend(["Input","Output","InOut"])
       
   190                             if self.Parent.IsFunctionBlockType(self.data[row]["Type"]):
       
   191                                 excluded.extend(["Local","Temp"])
       
   192                             editor.SetParameters(",".join([_(choice) for choice in self.Parent.ClassList if choice not in excluded]))
       
   193                     elif colname in ["Retain", "Constant"]:
       
   194                         editor = wx.grid.GridCellChoiceEditor()
       
   195                         editor.SetParameters(",".join(map(_, self.Parent.OptionList)))
       
   196                     elif colname == "Type":
       
   197                         editor = wx.grid.GridCellTextEditor()
       
   198                 else:
       
   199                     grid.SetReadOnly(row, col, True)
       
   200                 
       
   201                 grid.SetCellEditor(row, col, editor)
       
   202                 grid.SetCellRenderer(row, col, renderer)
       
   203                 
       
   204                 if row in self.Errors and self.Errors[row][0] == colname.lower():
       
   205                     grid.SetCellBackgroundColour(row, col, wx.Colour(255, 255, 0))
       
   206                     grid.SetCellTextColour(row, col, wx.RED)
       
   207                     grid.MakeCellVisible(row, col)
       
   208                 else:
       
   209                     grid.SetCellTextColour(row, col, wx.BLACK)
       
   210                     grid.SetCellBackgroundColour(row, col, wx.WHITE)
       
   211     
       
   212     def SetData(self, data):
       
   213         self.data = data
       
   214     
       
   215     def GetData(self):
       
   216         return self.data
       
   217     
       
   218     def GetCurrentIndex(self):
       
   219         return self.CurrentIndex
       
   220     
       
   221     def SetCurrentIndex(self, index):
       
   222         self.CurrentIndex = index
       
   223     
       
   224     def AppendRow(self, row_content):
       
   225         self.data.append(row_content)
       
   226 
       
   227     def RemoveRow(self, row_index):
       
   228         self.data.pop(row_index)
       
   229 
       
   230     def GetRow(self, row_index):
       
   231         return self.data[row_index]
       
   232 
       
   233     def Empty(self):
       
   234         self.data = []
       
   235         self.editors = []
       
   236 
       
   237     def AddError(self, infos):
       
   238         self.Errors[infos[0]] = infos[1:]
       
   239 
       
   240     def ClearErrors(self):
       
   241         self.Errors = {}
       
   242 
       
   243 class VariableDropTarget(wx.TextDropTarget):
       
   244     
       
   245     def __init__(self, parent):
       
   246         wx.TextDropTarget.__init__(self)
       
   247         self.ParentWindow = parent
       
   248     
       
   249     def OnDropText(self, x, y, data):
       
   250         x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
       
   251         col = self.ParentWindow.VariablesGrid.XToCol(x)
       
   252         row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
       
   253         if col != wx.NOT_FOUND and row != wx.NOT_FOUND:
       
   254             if self.ParentWindow.Table.GetColLabelValue(col, False) != "Location":
       
   255                 return
       
   256             message = None
       
   257             if not self.ParentWindow.Table.GetValueByName(row, "Edit"):
       
   258                 message = _("Can't affect a location to a function block instance")
       
   259             elif self.ParentWindow.Table.GetValueByName(row, "Class") not in ["Local", "Global"]:
       
   260                 message = _("Can affect a location only to local or global variables")
       
   261             else:
       
   262                 try:
       
   263                     values = eval(data)    
       
   264                 except:
       
   265                     message = _("Invalid value \"%s\" for location")%data
       
   266                     values = None
       
   267                 if not isinstance(values, TupleType):
       
   268                     message = _("Invalid value \"%s\" for location")%data
       
   269                     values = None
       
   270                 if values is not None and values[1] == "location":
       
   271                     location = values[0]
       
   272                     variable_type = self.ParentWindow.Table.GetValueByName(row, "Type")
       
   273                     base_type = self.ParentWindow.Controler.GetBaseType(variable_type)
       
   274                     message = None
       
   275                     if location.startswith("%"):
       
   276                         if base_type != values[2]:
       
   277                             message = _("Incompatible data types between \"%s\" and \"%s\"")%(values[2], variable_type)
       
   278                         else:
       
   279                             self.ParentWindow.Table.SetValue(row, col, location)
       
   280                             self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid)
       
   281                             self.ParentWindow.SaveValues()
       
   282                     else:
       
   283                         if location[0].isdigit() and base_type != "BOOL":
       
   284                             message = _("Incompatible size of data between \"%s\" and \"BOOL\"")%location
       
   285                         elif location[0] not in LOCATIONDATATYPES:
       
   286                             message = _("Unrecognized data size \"%s\"")%location[0]
       
   287                         elif base_type not in LOCATIONDATATYPES[location[0]]:
       
   288                             message = _("Incompatible size of data between \"%s\" and \"%s\"")%(location, variable_type)
       
   289                         else:
       
   290                             dialog = wx.SingleChoiceDialog(self.ParentWindow, _("Select a variable class:"), _("Variable class"), ["Input", "Output", "Memory"], wx.OK|wx.CANCEL)
       
   291                             if dialog.ShowModal() == wx.ID_OK:
       
   292                                 selected = dialog.GetSelection()
       
   293                                 if selected == 0:
       
   294                                     location = "%I" + location
       
   295                                 elif selected == 1:
       
   296                                     location = "%Q" + location
       
   297                                 else:
       
   298                                     location = "%M" + location
       
   299                                 self.ParentWindow.Table.SetValue(row, col, location)
       
   300                                 self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid)
       
   301                                 self.ParentWindow.SaveValues()
       
   302                             dialog.Destroy()
       
   303             if message is not None:
       
   304                 wx.CallAfter(self.ShowMessage, message)
       
   305             
       
   306     def ShowMessage(self, message):
       
   307         message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
       
   308         message.ShowModal()
       
   309         message.Destroy()
       
   310 
       
   311 [ID_VARIABLEEDITORPANEL, ID_VARIABLEEDITORPANELVARIABLESGRID, 
       
   312  ID_VARIABLEEDITORCONTROLPANEL, ID_VARIABLEEDITORPANELRETURNTYPE, 
       
   313  ID_VARIABLEEDITORPANELCLASSFILTER, ID_VARIABLEEDITORPANELADDBUTTON, 
       
   314  ID_VARIABLEEDITORPANELDELETEBUTTON, ID_VARIABLEEDITORPANELUPBUTTON, 
       
   315  ID_VARIABLEEDITORPANELDOWNBUTTON, ID_VARIABLEEDITORPANELSTATICTEXT1, 
       
   316  ID_VARIABLEEDITORPANELSTATICTEXT2, ID_VARIABLEEDITORPANELSTATICTEXT3,
       
   317 ] = [wx.NewId() for _init_ctrls in range(12)]
       
   318 
       
   319 class VariablePanel(wx.Panel):
       
   320     
       
   321     if wx.VERSION < (2, 6, 0):
       
   322         def Bind(self, event, function, id = None):
       
   323             if id is not None:
       
   324                 event(self, id, function)
       
   325             else:
       
   326                 event(self, function)
       
   327     
       
   328     def _init_coll_MainSizer_Items(self, parent):
       
   329         parent.AddWindow(self.VariablesGrid, 0, border=0, flag=wx.GROW)
       
   330         parent.AddWindow(self.ControlPanel, 0, border=5, flag=wx.GROW|wx.ALL)
       
   331     
       
   332     def _init_coll_MainSizer_Growables(self, parent):
       
   333         parent.AddGrowableCol(0)
       
   334         parent.AddGrowableRow(0)
       
   335     
       
   336     def _init_coll_ControlPanelSizer_Items(self, parent):
       
   337         parent.AddSizer(self.ChoicePanelSizer, 0, border=0, flag=wx.GROW)
       
   338         parent.AddSizer(self.ButtonPanelSizer, 0, border=0, flag=wx.ALIGN_CENTER)
       
   339 
       
   340     def _init_coll_ControlPanelSizer_Growables(self, parent):
       
   341         parent.AddGrowableCol(0)
       
   342         parent.AddGrowableRow(0)
       
   343         parent.AddGrowableRow(1)
       
   344 
       
   345     def _init_coll_ChoicePanelSizer_Items(self, parent):
       
   346         parent.AddWindow(self.staticText1, 0, border=0, flag=wx.ALIGN_BOTTOM)
       
   347         parent.AddWindow(self.ReturnType, 0, border=0, flag=0)
       
   348         parent.AddWindow(self.staticText2, 0, border=0, flag=wx.ALIGN_BOTTOM)
       
   349         parent.AddWindow(self.ClassFilter, 0, border=0, flag=0)
       
   350 
       
   351     def _init_coll_ButtonPanelSizer_Items(self, parent):
       
   352         parent.AddWindow(self.UpButton, 0, border=0, flag=0)
       
   353         parent.AddWindow(self.AddButton, 0, border=0, flag=0)
       
   354         parent.AddWindow(self.DownButton, 0, border=0, flag=0)
       
   355         parent.AddWindow(self.DeleteButton, 0, border=0, flag=0)
       
   356         
       
   357     def _init_coll_ButtonPanelSizer_Growables(self, parent):
       
   358         parent.AddGrowableCol(0)
       
   359         parent.AddGrowableCol(1)
       
   360         parent.AddGrowableCol(2)
       
   361         parent.AddGrowableCol(3)
       
   362         parent.AddGrowableRow(0)
       
   363 
       
   364     def _init_sizers(self):
       
   365         self.MainSizer = wx.FlexGridSizer(cols=2, hgap=10, rows=1, vgap=0)
       
   366         self.ControlPanelSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
       
   367         self.ChoicePanelSizer = wx.GridSizer(cols=1, hgap=5, rows=4, vgap=5)
       
   368         self.ButtonPanelSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
       
   369         
       
   370         self._init_coll_MainSizer_Items(self.MainSizer)
       
   371         self._init_coll_MainSizer_Growables(self.MainSizer)
       
   372         self._init_coll_ControlPanelSizer_Items(self.ControlPanelSizer)
       
   373         self._init_coll_ControlPanelSizer_Growables(self.ControlPanelSizer)
       
   374         self._init_coll_ChoicePanelSizer_Items(self.ChoicePanelSizer)
       
   375         self._init_coll_ButtonPanelSizer_Items(self.ButtonPanelSizer)
       
   376         self._init_coll_ButtonPanelSizer_Growables(self.ButtonPanelSizer)
       
   377         
       
   378         self.SetSizer(self.MainSizer)
       
   379         self.ControlPanel.SetSizer(self.ControlPanelSizer)
       
   380 
       
   381     def _init_ctrls(self, prnt):
       
   382         wx.Panel.__init__(self, id=ID_VARIABLEEDITORPANEL,
       
   383               name='VariableEditorPanel', parent=prnt, pos=wx.Point(0, 0),
       
   384               size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
       
   385 
       
   386         self.VariablesGrid = wx.grid.Grid(id=ID_VARIABLEEDITORPANELVARIABLESGRID,
       
   387               name='VariablesGrid', parent=self, pos=wx.Point(0, 0), 
       
   388               size=wx.Size(0, 0), style=wx.VSCROLL)
       
   389         self.VariablesGrid.SetFont(wx.Font(12, 77, wx.NORMAL, wx.NORMAL, False,
       
   390               'Sans'))
       
   391         self.VariablesGrid.SetLabelFont(wx.Font(10, 77, wx.NORMAL, wx.NORMAL,
       
   392               False, 'Sans'))
       
   393         self.VariablesGrid.SetSelectionBackground(wx.WHITE)
       
   394         self.VariablesGrid.SetSelectionForeground(wx.BLACK)
       
   395         if wx.VERSION >= (2, 6, 0):
       
   396             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange)
       
   397             self.VariablesGrid.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnVariablesGridSelectCell)
       
   398             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
       
   399             self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown)
       
   400             #self.VariablesGrid.Bind(wx.EVT_KEY_DOWN, self.OnChar)
       
   401         else:
       
   402             wx.grid.EVT_GRID_CELL_CHANGE(self.VariablesGrid, self.OnVariablesGridCellChange)
       
   403             wx.grid.EVT_GRID_SELECT_CELL(self.VariablesGrid, self.OnVariablesGridSelectCell)
       
   404             wx.grid.EVT_GRID_CELL_LEFT_CLICK(self.VariablesGrid, self.OnVariablesGridCellLeftClick)
       
   405             wx.grid.EVT_GRID_EDITOR_SHOWN(self.VariablesGrid, self.OnVariablesGridEditorShown)
       
   406             #wx.EVT_KEY_DOWN(self.VariablesGrid, self.OnChar)
       
   407         self.VariablesGrid.SetDropTarget(VariableDropTarget(self))
       
   408         
       
   409         self.ControlPanel = wx.ScrolledWindow(id=ID_VARIABLEEDITORCONTROLPANEL,
       
   410               name='ControlPanel', parent=self, pos=wx.Point(0, 0),
       
   411               size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
       
   412         self.ControlPanel.SetScrollRate(0, 10)
       
   413         
       
   414         self.staticText1 = wx.StaticText(id=ID_VARIABLEEDITORPANELSTATICTEXT1,
       
   415               label=_('Return Type:'), name='staticText1', parent=self.ControlPanel,
       
   416               pos=wx.Point(0, 0), size=wx.Size(145, 17), style=0)
       
   417 
       
   418         self.ReturnType = wx.ComboBox(id=ID_VARIABLEEDITORPANELRETURNTYPE,
       
   419               name='ReturnType', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   420               size=wx.Size(145, 28), style=wx.CB_READONLY)
       
   421         self.Bind(wx.EVT_COMBOBOX, self.OnReturnTypeChanged, id=ID_VARIABLEEDITORPANELRETURNTYPE)
       
   422 
       
   423         self.staticText2 = wx.StaticText(id=ID_VARIABLEEDITORPANELSTATICTEXT2,
       
   424               label=_('Class Filter:'), name='staticText2', parent=self.ControlPanel,
       
   425               pos=wx.Point(0, 0), size=wx.Size(145, 17), style=0)
       
   426 
       
   427         self.ClassFilter = wx.ComboBox(id=ID_VARIABLEEDITORPANELCLASSFILTER,
       
   428               name='ClassFilter', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   429               size=wx.Size(145, 28), style=wx.CB_READONLY)
       
   430         self.Bind(wx.EVT_COMBOBOX, self.OnClassFilter, id=ID_VARIABLEEDITORPANELCLASSFILTER)
       
   431 
       
   432         self.AddButton = wx.Button(id=ID_VARIABLEEDITORPANELADDBUTTON, label=_('Add'),
       
   433               name='AddButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   434               size=wx.DefaultSize, style=0)
       
   435         self.Bind(wx.EVT_BUTTON, self.OnAddButton, id=ID_VARIABLEEDITORPANELADDBUTTON)
       
   436 
       
   437         self.DeleteButton = wx.Button(id=ID_VARIABLEEDITORPANELDELETEBUTTON, label=_('Delete'),
       
   438               name='DeleteButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   439               size=wx.DefaultSize, style=0)
       
   440         self.Bind(wx.EVT_BUTTON, self.OnDeleteButton, id=ID_VARIABLEEDITORPANELDELETEBUTTON)
       
   441 
       
   442         self.UpButton = wx.Button(id=ID_VARIABLEEDITORPANELUPBUTTON, label='^',
       
   443               name='UpButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   444               size=wx.Size(32, 32), style=0)
       
   445         self.Bind(wx.EVT_BUTTON, self.OnUpButton, id=ID_VARIABLEEDITORPANELUPBUTTON)
       
   446 
       
   447         self.DownButton = wx.Button(id=ID_VARIABLEEDITORPANELDOWNBUTTON, label='v',
       
   448               name='DownButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   449               size=wx.Size(32, 32), style=0)
       
   450         self.Bind(wx.EVT_BUTTON, self.OnDownButton, id=ID_VARIABLEEDITORPANELDOWNBUTTON)
       
   451 
       
   452         self._init_sizers()
       
   453 
       
   454     def __init__(self, parent, window, controler, element_type):
       
   455         self._init_ctrls(parent)
       
   456         self.ParentWindow = window
       
   457         self.Controler = controler
       
   458         self.ElementType = element_type
       
   459         
       
   460         self.Filter = "All"
       
   461         self.FilterChoices = []
       
   462         self.FilterChoiceTransfer = GetFilterChoiceTransfer()
       
   463         
       
   464         if element_type in ["config", "resource"]:
       
   465             self.DefaultTypes = {"All" : "Global"}
       
   466             self.DefaultValue = {"Name" : "", "Class" : "", "Type" : "INT", "Location" : "", "Initial Value" : "", "Retain" : "No", "Constant" : "No", "Edit" : True}
       
   467         else:
       
   468             self.DefaultTypes = {"All" : "Local", "Interface" : "Input", "Variables" : "Local"}
       
   469             self.DefaultValue = {"Name" : "", "Class" : "", "Type" : "INT", "Location" : "", "Initial Value" : "", "Retain" : "No", "Constant" : "No", "Edit" : True}
       
   470         if element_type in ["config", "resource"] or element_type in ["program", "transition", "action"]:
       
   471             self.Table = VariableTable(self, [], GetVariableTableColnames(True))
       
   472             if element_type not in ["config", "resource"]:
       
   473                 self.FilterChoices = ["All", "Interface", "   Input", "   Output", "   InOut", "   External", "Variables", "   Local", "   Temp"]#,"Access"]
       
   474             else:
       
   475                 self.FilterChoices = ["All", "Global"]#,"Access"]
       
   476             self.ColSizes = [40, 80, 70, 80, 80, 80, 60, 70]
       
   477             self.ColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_CENTER, wx.ALIGN_CENTER]
       
   478         else:
       
   479             self.Table = VariableTable(self, [], GetVariableTableColnames(False))
       
   480             if element_type == "function":
       
   481                 self.FilterChoices = ["All", "Interface", "   Input", "   Output", "   InOut", "Variables", "   Local", "   Temp"]
       
   482             else:
       
   483                 self.FilterChoices = ["All", "Interface", "   Input", "   Output", "   InOut", "   External", "Variables", "   Local", "   Temp"]
       
   484             self.ColSizes = [40, 120, 70, 80, 120, 60, 70]
       
   485             self.ColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_CENTER, wx.ALIGN_CENTER]
       
   486         for choice in self.FilterChoices:
       
   487             self.ClassFilter.Append(_(choice))
       
   488         reverse_transfer = {}
       
   489         for filter, choice in self.FilterChoiceTransfer.items():
       
   490             reverse_transfer[choice] = filter
       
   491         self.ClassFilter.SetStringSelection(_(reverse_transfer[self.Filter]))
       
   492         self.RefreshTypeList()
       
   493 
       
   494         self.OptionList = GetAlternativeOptions()
       
   495         
       
   496         if element_type == "function":
       
   497             for base_type in self.Controler.GetBaseTypes():
       
   498                 self.ReturnType.Append(base_type)
       
   499             self.ReturnType.Enable(True)
       
   500         else:
       
   501             self.ReturnType.Enable(False)
       
   502             self.staticText1.Hide()
       
   503             self.ReturnType.Hide()
       
   504             
       
   505         self.VariablesGrid.SetTable(self.Table)
       
   506         self.VariablesGrid.SetRowLabelSize(0)
       
   507         for col in range(self.Table.GetNumberCols()):
       
   508             attr = wx.grid.GridCellAttr()
       
   509             attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE)
       
   510             self.VariablesGrid.SetColAttr(col, attr)
       
   511             self.VariablesGrid.SetColMinimalWidth(col, self.ColSizes[col])
       
   512             self.VariablesGrid.AutoSizeColumn(col, False)
       
   513     
       
   514     def SetTagName(self, tagname):
       
   515         self.TagName = tagname
       
   516     
       
   517     def IsFunctionBlockType(self, name):
       
   518         bodytype = self.Controler.GetEditedElementBodyType(self.TagName, self.ParentWindow.Debug)
       
   519         pouname, poutype = self.Controler.GetEditedElementType(self.TagName, self.ParentWindow.Debug)
       
   520         if poutype != "function" and bodytype in ["ST", "IL"]:
       
   521             return False
       
   522         else:
       
   523             return name in self.Controler.GetFunctionBlockTypes(self.TagName, self.ParentWindow.Debug)
       
   524     
       
   525     def RefreshView(self):
       
   526         self.PouNames = self.Controler.GetProjectPouNames(self.ParentWindow.Debug)
       
   527         
       
   528         words = self.TagName.split("::")
       
   529         if self.ElementType == "config":
       
   530             self.PouIsUsed = False
       
   531             returnType = None
       
   532             self.Values = self.Controler.GetConfigurationGlobalVars(words[1], self.ParentWindow.Debug)
       
   533         elif self.ElementType == "resource":
       
   534             self.PouIsUsed = False
       
   535             returnType = None
       
   536             self.Values = self.Controler.GetConfigurationResourceGlobalVars(words[1], words[2], self.ParentWindow.Debug)
       
   537         else:
       
   538             self.PouIsUsed = self.Controler.PouIsUsed(words[1], self.ParentWindow.Debug)
       
   539             returnType = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, self.ParentWindow.Debug)
       
   540             self.Values = self.Controler.GetEditedElementInterfaceVars(self.TagName, self.ParentWindow.Debug)
       
   541         
       
   542         if returnType and self.ReturnType.IsEnabled():
       
   543             self.ReturnType.SetStringSelection(returnType)
       
   544         
       
   545         self.RefreshValues()
       
   546         self.RefreshButtons()
       
   547     
       
   548     def OnReturnTypeChanged(self, event):
       
   549         words = self.TagName.split("::")
       
   550         self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection())
       
   551         self.Controler.BufferProject()
       
   552         self.ParentWindow.RefreshEditor(variablepanel = False)
       
   553         self.ParentWindow.RefreshTitle()
       
   554         self.ParentWindow.RefreshEditMenu()
       
   555         self.ParentWindow.RefreshInstancesTree()
       
   556         self.ParentWindow.RefreshLibraryTree()
       
   557         event.Skip()
       
   558     
       
   559     def OnClassFilter(self, event):
       
   560         self.Filter = self.FilterChoiceTransfer[self.ClassFilter.GetStringSelection()]
       
   561         self.RefreshTypeList()
       
   562         self.RefreshValues()
       
   563         self.RefreshButtons()
       
   564         event.Skip()
       
   565 
       
   566     def RefreshTypeList(self):
       
   567         if self.Filter == "All":
       
   568             self.ClassList = [self.FilterChoiceTransfer[choice] for choice in self.FilterChoices if self.FilterChoiceTransfer[choice] not in ["All","Interface","Variables"]]
       
   569         elif self.Filter == "Interface":
       
   570             self.ClassList = ["Input","Output","InOut","External"]
       
   571         elif self.Filter == "Variables":
       
   572             self.ClassList = ["Local","Temp"]
       
   573         else:
       
   574             self.ClassList = [self.Filter]
       
   575 
       
   576     def RefreshButtons(self):
       
   577         if getattr(self, "Table", None):
       
   578             table_length = len(self.Table.data)
       
   579             row_class = None
       
   580             row_edit = True
       
   581             if table_length > 0:
       
   582                 row = self.VariablesGrid.GetGridCursorRow()
       
   583                 row_edit = self.Table.GetValueByName(row, "Edit")
       
   584                 if self.PouIsUsed:
       
   585                     row_class = self.Table.GetValueByName(row, "Class")
       
   586             self.AddButton.Enable(not self.PouIsUsed or self.Filter not in ["Interface", "Input", "Output", "InOut"])
       
   587             self.DeleteButton.Enable(table_length > 0 and row_edit and row_class not in ["Input", "Output", "InOut"])
       
   588             self.UpButton.Enable(table_length > 0 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"])
       
   589             self.DownButton.Enable(table_length > 0 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"])
       
   590 
       
   591     def OnAddButton(self, event):
       
   592         new_row = self.DefaultValue.copy()
       
   593         if self.Filter in self.DefaultTypes:
       
   594             new_row["Class"] = self.DefaultTypes[self.Filter]
       
   595         else:
       
   596             new_row["Class"] = self.Filter
       
   597         if self.Filter == "All" and len(self.Values) > 0:
       
   598             row_index = self.VariablesGrid.GetGridCursorRow() + 1
       
   599             self.Values.insert(row_index, new_row)
       
   600         else:
       
   601             row_index = -1
       
   602             self.Values.append(new_row)
       
   603         self.SaveValues()
       
   604         self.RefreshValues(row_index)
       
   605         self.RefreshButtons()
       
   606         event.Skip()
       
   607 
       
   608     def OnDeleteButton(self, event):
       
   609         row = self.Table.GetRow(self.VariablesGrid.GetGridCursorRow())
       
   610         self.Values.remove(row)
       
   611         self.SaveValues()
       
   612         self.RefreshValues()
       
   613         self.RefreshButtons()
       
   614         event.Skip()
       
   615 
       
   616     def OnUpButton(self, event):
       
   617         self.MoveValue(self.VariablesGrid.GetGridCursorRow(), -1)
       
   618         self.RefreshButtons()
       
   619         event.Skip()
       
   620 
       
   621     def OnDownButton(self, event):
       
   622         self.MoveValue(self.VariablesGrid.GetGridCursorRow(), 1)
       
   623         self.RefreshButtons()
       
   624         event.Skip()
       
   625 
       
   626     def OnVariablesGridCellChange(self, event):
       
   627         row, col = event.GetRow(), event.GetCol()
       
   628         colname = self.Table.GetColLabelValue(col)
       
   629         value = self.Table.GetValue(row, col)
       
   630         if colname == "Name" and value != "":
       
   631             if not TestIdentifier(value):
       
   632                 message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   633                 message.ShowModal()
       
   634                 message.Destroy()
       
   635                 event.Veto()
       
   636             elif value.upper() in IEC_KEYWORDS:
       
   637                 message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   638                 message.ShowModal()
       
   639                 message.Destroy()
       
   640                 event.Veto()
       
   641             elif value.upper() in self.PouNames:
       
   642                 message = wx.MessageDialog(self, _("A pou with \"%s\" as name exists!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   643                 message.ShowModal()
       
   644                 message.Destroy()
       
   645                 event.Veto()
       
   646             elif value.upper() in [var["Name"].upper() for var in self.Values if var != self.Table.data[row]]:
       
   647                 message = wx.MessageDialog(self, _("A variable with \"%s\" as name already exists in this pou!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   648                 message.ShowModal()
       
   649                 message.Destroy()
       
   650                 event.Veto()
       
   651             else:
       
   652                 self.SaveValues(False)
       
   653                 old_value = self.Table.GetOldValue()
       
   654                 if old_value != "":
       
   655                     self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value)
       
   656                 self.Controler.BufferProject()
       
   657                 self.ParentWindow.RefreshEditor(variablepanel = False)
       
   658                 self.ParentWindow.RefreshTitle()
       
   659                 self.ParentWindow.RefreshEditMenu()
       
   660                 self.ParentWindow.RefreshInstancesTree()
       
   661                 self.ParentWindow.RefreshLibraryTree()
       
   662                 event.Skip()
       
   663         else:
       
   664             self.SaveValues()
       
   665             if colname == "Class":
       
   666                 self.Table.ResetView(self.VariablesGrid)
       
   667             event.Skip()
       
   668     
       
   669     def OnVariablesGridEditorShown(self, event):
       
   670         row, col = event.GetRow(), event.GetCol() 
       
   671         classtype = self.Table.GetValueByName(row, "Class")
       
   672         if self.Table.GetColLabelValue(col) == "Type":
       
   673             type_menu = wx.Menu(title='')
       
   674             base_menu = wx.Menu(title='')
       
   675             for base_type in self.Controler.GetBaseTypes():
       
   676                 new_id = wx.NewId()
       
   677                 AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
       
   678                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), id=new_id)
       
   679             type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu)
       
   680             datatype_menu = wx.Menu(title='')
       
   681             for datatype in self.Controler.GetDataTypes(basetypes = False, debug = self.ParentWindow.Debug):
       
   682                 new_id = wx.NewId()
       
   683                 AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
       
   684                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id)
       
   685             type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu)
       
   686             functionblock_menu = wx.Menu(title='')
       
   687             bodytype = self.Controler.GetEditedElementBodyType(self.TagName, self.ParentWindow.Debug)
       
   688             pouname, poutype = self.Controler.GetEditedElementType(self.TagName, self.ParentWindow.Debug)
       
   689             if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]:
       
   690                 for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName, self.ParentWindow.Debug):
       
   691                     new_id = wx.NewId()
       
   692                     AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type)
       
   693                     self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id)
       
   694                 type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu)
       
   695             rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col))
       
   696             self.VariablesGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.VariablesGrid.GetColLabelSize())
       
   697             event.Veto()
       
   698         else:
       
   699             event.Skip()
       
   700     
       
   701     def GetVariableTypeFunction(self, base_type):
       
   702         def VariableTypeFunction(event):
       
   703             row = self.VariablesGrid.GetGridCursorRow()
       
   704             self.Table.SetValueByName(row, "Type", base_type)
       
   705             self.Table.ResetView(self.VariablesGrid)
       
   706             self.SaveValues(False)
       
   707             self.ParentWindow.RefreshEditor(variablepanel = False)
       
   708             self.Controler.BufferProject()
       
   709             self.ParentWindow.RefreshTitle()
       
   710             self.ParentWindow.RefreshEditMenu()
       
   711             self.ParentWindow.RefreshInstancesTree()
       
   712             self.ParentWindow.RefreshLibraryTree()
       
   713             event.Skip()
       
   714         return VariableTypeFunction
       
   715     
       
   716     def OnVariablesGridCellLeftClick(self, event):
       
   717         row = event.GetRow()
       
   718         if event.GetCol() == 0 and self.Table.GetValueByName(row, "Edit"):
       
   719             row = event.GetRow()
       
   720             var_name = self.Table.GetValueByName(row, "Name")
       
   721             var_class = self.Table.GetValueByName(row, "Class")
       
   722             var_type = self.Table.GetValueByName(row, "Type")
       
   723             data = wx.TextDataObject(str((var_name, var_class, var_type, self.TagName)))
       
   724             dragSource = wx.DropSource(self.VariablesGrid)
       
   725             dragSource.SetData(data)
       
   726             dragSource.DoDragDrop()
       
   727         event.Skip()
       
   728     
       
   729     def OnVariablesGridSelectCell(self, event):
       
   730         wx.CallAfter(self.RefreshButtons)
       
   731         event.Skip()
       
   732 
       
   733     def OnChar(self, event):
       
   734         keycode = event.GetKeyCode()
       
   735         if keycode == wx.WXK_DELETE:
       
   736             row = self.Table.GetRow(self.VariablesGrid.GetGridCursorRow())
       
   737             self.Values.remove(row)
       
   738             self.SaveValues()
       
   739             self.RefreshValues()
       
   740             self.RefreshButtons()
       
   741         event.Skip()
       
   742 
       
   743     def MoveValue(self, value_index, move):
       
   744         new_index = max(0, min(value_index + move, len(self.Values) - 1))
       
   745         if new_index != value_index:
       
   746             self.Values.insert(new_index, self.Values.pop(value_index))
       
   747             self.SaveValues()
       
   748             self.RefreshValues()
       
   749             self.VariablesGrid.SetGridCursor(new_index, self.VariablesGrid.GetGridCursorCol())
       
   750         
       
   751     def RefreshValues(self, select=0):
       
   752         if len(self.Table.data) > 0:
       
   753             self.VariablesGrid.SetGridCursor(0, 1)
       
   754         data = []
       
   755         for num, variable in enumerate(self.Values):
       
   756             if variable["Class"] in self.ClassList:
       
   757                 variable["Number"] = num + 1
       
   758                 data.append(variable)
       
   759         self.Table.SetData(data)
       
   760         if len(self.Table.data) > 0:
       
   761             if select == -1:
       
   762                 select = len(self.Table.data) - 1
       
   763             self.VariablesGrid.SetGridCursor(select, 1)
       
   764             self.VariablesGrid.MakeCellVisible(select, 1)
       
   765         self.Table.ResetView(self.VariablesGrid)
       
   766 
       
   767     def SaveValues(self, buffer = True):
       
   768         words = self.TagName.split("::")
       
   769         if self.ElementType == "config":
       
   770             self.Controler.SetConfigurationGlobalVars(words[1], self.Values)
       
   771         elif self.ElementType == "resource":
       
   772             self.Controler.SetConfigurationResourceGlobalVars(words[1], words[2], self.Values)
       
   773         else:
       
   774             if self.ReturnType.IsEnabled():
       
   775                 self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection())
       
   776             self.Controler.SetPouInterfaceVars(words[1], self.Values)
       
   777         if buffer:
       
   778             self.Controler.BufferProject()
       
   779             self.ParentWindow.RefreshTitle()
       
   780             self.ParentWindow.RefreshEditMenu()
       
   781             self.ParentWindow.RefreshInstancesTree()
       
   782             self.ParentWindow.RefreshLibraryTree()
       
   783 
       
   784     def AddVariableError(self, infos):
       
   785         if isinstance(infos[0], TupleType):
       
   786             for i in xrange(*infos[0]):
       
   787                 self.Table.AddError((i,) + infos[1:])
       
   788         else:
       
   789             self.Table.AddError(infos)
       
   790         self.Table.ResetView(self.VariablesGrid)
       
   791 
       
   792     def ClearErrors(self):
       
   793         self.Table.ClearErrors()
       
   794         self.Table.ResetView(self.VariablesGrid)