VariablePanel.py
changeset 586 9aa96a36cf33
parent 585 bd8c7a033b17
child 587 98445f90f45d
equal deleted inserted replaced
585:bd8c7a033b17 586:9aa96a36cf33
     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 os
       
    26 import wx, wx.grid
       
    27 
       
    28 from types import TupleType, StringType, UnicodeType
       
    29 
       
    30 from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS
       
    31 from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
       
    32 from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD
       
    33 from dialogs import ArrayTypeDialog
       
    34 from controls import CustomGrid
       
    35 
       
    36 CWD = os.path.split(os.path.realpath(__file__))[0]
       
    37 
       
    38 # Compatibility function for wx versions < 2.6
       
    39 def AppendMenu(parent, help, id, kind, text):
       
    40     if wx.VERSION >= (2, 6, 0):
       
    41         parent.Append(help=help, id=id, kind=kind, text=text)
       
    42     else:
       
    43         parent.Append(helpString=help, id=id, kind=kind, item=text)
       
    44 
       
    45 [TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, 
       
    46  INSTANCESTREE, LIBRARYTREE, SCALING
       
    47 ] = range(9)
       
    48 
       
    49 #-------------------------------------------------------------------------------
       
    50 #                            Variables Editor Panel
       
    51 #-------------------------------------------------------------------------------
       
    52 
       
    53 def GetVariableTableColnames(location):
       
    54     _ = lambda x : x
       
    55     if location:
       
    56     	return ["#", _("Name"), _("Class"), _("Type"), _("Location"), _("Initial Value"), _("Option"), _("Documentation")]
       
    57     return ["#", _("Name"), _("Class"), _("Type"), _("Initial Value"), _("Option"), _("Documentation")]
       
    58 
       
    59 def GetOptions(constant=True, retain=True, non_retain=True):
       
    60     _ = lambda x : x
       
    61     options = [""]
       
    62     if constant:
       
    63         options.append(_("Constant"))
       
    64     if retain:
       
    65         options.append(_("Retain"))
       
    66     if non_retain:
       
    67         options.append(_("Non-Retain"))
       
    68     return options
       
    69 OPTIONS_DICT = dict([(_(option), option) for option in GetOptions()])
       
    70 
       
    71 def GetFilterChoiceTransfer():
       
    72     _ = lambda x : x
       
    73     return {_("All"): _("All"), _("Interface"): _("Interface"), 
       
    74             _("   Input"): _("Input"), _("   Output"): _("Output"), _("   InOut"): _("InOut"), 
       
    75             _("   External"): _("External"), _("Variables"): _("Variables"), _("   Local"): _("Local"),
       
    76             _("   Temp"): _("Temp"), _("Global"): _("Global")}#, _("Access") : _("Access")}
       
    77 VARIABLE_CHOICES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().iterkeys()])
       
    78 VARIABLE_CLASSES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().itervalues()])
       
    79 
       
    80 CheckOptionForClass = {"Local": lambda x: x,
       
    81                        "Temp": lambda x: "",
       
    82                        "Input": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""),
       
    83                        "InOut": lambda x: "",
       
    84                        "Output": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""),
       
    85                        "Global": lambda x: {"Constant": "Constant", "Retain": "Retain"}.get(x, ""),
       
    86                        "External": lambda x: {"Constant": "Constant"}.get(x, "")
       
    87                       }
       
    88 
       
    89 class VariableTable(wx.grid.PyGridTableBase):
       
    90     
       
    91     """
       
    92     A custom wx.grid.Grid Table using user supplied data
       
    93     """
       
    94     def __init__(self, parent, data, colnames):
       
    95         # The base class must be initialized *first*
       
    96         wx.grid.PyGridTableBase.__init__(self)
       
    97         self.data = data
       
    98         self.old_value = None
       
    99         self.colnames = colnames
       
   100         self.Highlights = {}
       
   101         self.Parent = parent
       
   102         # XXX
       
   103         # we need to store the row length and collength to
       
   104         # see if the table has changed size
       
   105         self._rows = self.GetNumberRows()
       
   106         self._cols = self.GetNumberCols()
       
   107     
       
   108     def GetNumberCols(self):
       
   109         return len(self.colnames)
       
   110         
       
   111     def GetNumberRows(self):
       
   112         return len(self.data)
       
   113 
       
   114     def GetColLabelValue(self, col, translate=True):
       
   115         if col < len(self.colnames):
       
   116             if translate:
       
   117                 return _(self.colnames[col])
       
   118             return self.colnames[col]
       
   119 
       
   120     def GetRowLabelValues(self, row, translate=True):
       
   121         return row
       
   122 
       
   123     def GetValue(self, row, col):
       
   124         if row < self.GetNumberRows():
       
   125             if col == 0:
       
   126                 return self.data[row]["Number"]
       
   127             colname = self.GetColLabelValue(col, False)
       
   128             value = self.data[row].get(colname, "")
       
   129             if colname == "Type" and isinstance(value, TupleType):
       
   130                 if value[0] == "array":
       
   131                     return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "..".join(x), value[2])), value[1])
       
   132             if not isinstance(value, (StringType, UnicodeType)):
       
   133                 value = str(value)
       
   134             if colname in ["Class", "Option"]:
       
   135                 return _(value)
       
   136             return value
       
   137     
       
   138     def SetValue(self, row, col, value):
       
   139         if col < len(self.colnames):
       
   140             colname = self.GetColLabelValue(col, False)
       
   141             if colname == "Name":
       
   142                 self.old_value = self.data[row][colname]
       
   143             elif colname == "Class":
       
   144                 value = VARIABLE_CLASSES_DICT[value]
       
   145                 self.SetValueByName(row, "Option", CheckOptionForClass[value](self.GetValueByName(row, "Option")))
       
   146                 if value == "External":
       
   147                     self.SetValueByName(row, "Initial Value", "")
       
   148             elif colname == "Option":
       
   149                 value = OPTIONS_DICT[value]
       
   150             self.data[row][colname] = value
       
   151     
       
   152     def GetValueByName(self, row, colname):
       
   153         if row < self.GetNumberRows():
       
   154             return self.data[row].get(colname)
       
   155 
       
   156     def SetValueByName(self, row, colname, value):
       
   157         if row < self.GetNumberRows():
       
   158             self.data[row][colname] = value
       
   159 
       
   160     def GetOldValue(self):
       
   161         return self.old_value
       
   162     
       
   163     def ResetView(self, grid):
       
   164         """
       
   165         (wx.grid.Grid) -> Reset the grid view.   Call this to
       
   166         update the grid if rows and columns have been added or deleted
       
   167         """
       
   168         grid.BeginBatch()
       
   169         for current, new, delmsg, addmsg in [
       
   170             (self._rows, self.GetNumberRows(), wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED),
       
   171             (self._cols, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED),
       
   172         ]:
       
   173             if new < current:
       
   174                 msg = wx.grid.GridTableMessage(self,delmsg,new,current-new)
       
   175                 grid.ProcessTableMessage(msg)
       
   176             elif new > current:
       
   177                 msg = wx.grid.GridTableMessage(self,addmsg,new-current)
       
   178                 grid.ProcessTableMessage(msg)
       
   179                 self.UpdateValues(grid)
       
   180         grid.EndBatch()
       
   181 
       
   182         self._rows = self.GetNumberRows()
       
   183         self._cols = self.GetNumberCols()
       
   184         # update the column rendering scheme
       
   185         self._updateColAttrs(grid)
       
   186 
       
   187         # update the scrollbars and the displayed part of the grid
       
   188         grid.AdjustScrollbars()
       
   189         grid.ForceRefresh()
       
   190 
       
   191     def UpdateValues(self, grid):
       
   192         """Update all displayed values"""
       
   193         # This sends an event to the grid table to update all of the values
       
   194         msg = wx.grid.GridTableMessage(self, wx.grid.GRIDTABLE_REQUEST_VIEW_GET_VALUES)
       
   195         grid.ProcessTableMessage(msg)
       
   196 
       
   197     def _updateColAttrs(self, grid):
       
   198         """
       
   199         wx.grid.Grid -> update the column attributes to add the
       
   200         appropriate renderer given the column name.
       
   201 
       
   202         Otherwise default to the default renderer.
       
   203         """
       
   204         for row in range(self.GetNumberRows()):
       
   205             var_class = self.GetValueByName(row, "Class")
       
   206             var_type = self.GetValueByName(row, "Type")
       
   207             row_highlights = self.Highlights.get(row, {})
       
   208             for col in range(self.GetNumberCols()):
       
   209                 editor = None
       
   210                 renderer = None
       
   211                 colname = self.GetColLabelValue(col, False)
       
   212                 if colname == "Option":
       
   213                     options = GetOptions(constant = var_class in ["Local", "External", "Global"],
       
   214                                          retain = self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output", "Global"],
       
   215                                          non_retain = self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output"])
       
   216                     if len(options) > 1:
       
   217                         editor = wx.grid.GridCellChoiceEditor()
       
   218                         editor.SetParameters(",".join(map(_, options)))
       
   219                     else:
       
   220                         grid.SetReadOnly(row, col, True)
       
   221                 elif col != 0 and self.GetValueByName(row, "Edit"):
       
   222                     grid.SetReadOnly(row, col, False)
       
   223                     if colname == "Name":
       
   224                         if self.Parent.PouIsUsed and var_class in ["Input", "Output", "InOut"]:
       
   225                             grid.SetReadOnly(row, col, True)
       
   226                         else:
       
   227                             editor = wx.grid.GridCellTextEditor()
       
   228                             renderer = wx.grid.GridCellStringRenderer()
       
   229                     elif colname == "Initial Value":
       
   230                         if var_class != "External":
       
   231                             if self.Parent.Controler.IsEnumeratedType(var_type):
       
   232                                 editor = wx.grid.GridCellChoiceEditor()
       
   233                                 editor.SetParameters(",".join(self.Parent.Controler.GetEnumeratedDataValues(var_type)))
       
   234                             else:
       
   235                                 editor = wx.grid.GridCellTextEditor()
       
   236                             renderer = wx.grid.GridCellStringRenderer()
       
   237                         else:
       
   238                             grid.SetReadOnly(row, col, True)
       
   239                     elif colname == "Location":
       
   240                         if var_class in ["Local", "Global"] and self.Parent.Controler.IsLocatableType(var_type):
       
   241                             editor = LocationCellEditor(self, self.Parent.Controler)
       
   242                             renderer = wx.grid.GridCellStringRenderer()
       
   243                         else:
       
   244                             grid.SetReadOnly(row, col, True)
       
   245                     elif colname == "Class":
       
   246                         if len(self.Parent.ClassList) == 1 or self.Parent.PouIsUsed and var_class in ["Input", "Output", "InOut"]:
       
   247                             grid.SetReadOnly(row, col, True)
       
   248                         else:
       
   249                             editor = wx.grid.GridCellChoiceEditor()
       
   250                             excluded = []
       
   251                             if self.Parent.PouIsUsed:
       
   252                                 excluded.extend(["Input","Output","InOut"])
       
   253                             if self.Parent.IsFunctionBlockType(var_type):
       
   254                                 excluded.extend(["Local","Temp"])
       
   255                             editor.SetParameters(",".join([_(choice) for choice in self.Parent.ClassList if choice not in excluded]))
       
   256                 elif colname != "Documentation":
       
   257                     grid.SetReadOnly(row, col, True)
       
   258                 
       
   259                 grid.SetCellEditor(row, col, editor)
       
   260                 grid.SetCellRenderer(row, col, renderer)
       
   261                 
       
   262                 highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1]
       
   263                 grid.SetCellBackgroundColour(row, col, highlight_colours[0])
       
   264                 grid.SetCellTextColour(row, col, highlight_colours[1])
       
   265             if wx.Platform == '__WXMSW__':
       
   266                 grid.SetRowMinimalHeight(row, 20)
       
   267             else:
       
   268                 grid.SetRowMinimalHeight(row, 28)
       
   269             grid.AutoSizeRow(row, False)
       
   270     
       
   271     def SetData(self, data):
       
   272         self.data = data
       
   273     
       
   274     def GetData(self):
       
   275         return self.data
       
   276     
       
   277     def GetCurrentIndex(self):
       
   278         return self.CurrentIndex
       
   279     
       
   280     def SetCurrentIndex(self, index):
       
   281         self.CurrentIndex = index
       
   282     
       
   283     def AppendRow(self, row_content):
       
   284         self.data.append(row_content)
       
   285 
       
   286     def RemoveRow(self, row_index):
       
   287         self.data.pop(row_index)
       
   288 
       
   289     def GetRow(self, row_index):
       
   290         return self.data[row_index]
       
   291 
       
   292     def Empty(self):
       
   293         self.data = []
       
   294         self.editors = []
       
   295 
       
   296     def AddHighlight(self, infos, highlight_type):
       
   297         row_highlights = self.Highlights.setdefault(infos[0], {})
       
   298         col_highlights = row_highlights.setdefault(infos[1], [])
       
   299         col_highlights.append(highlight_type)
       
   300 
       
   301     def ClearHighlights(self, highlight_type=None):
       
   302         if highlight_type is None:
       
   303             self.Highlights = {}
       
   304         else:
       
   305             for row, row_highlights in self.Highlights.iteritems():
       
   306                 row_items = row_highlights.items()
       
   307                 for col, col_highlights in row_items:
       
   308                     if highlight_type in col_highlights:
       
   309                         col_highlights.remove(highlight_type)
       
   310                     if len(col_highlights) == 0:
       
   311                         row_highlights.pop(col)
       
   312                     
       
   313 
       
   314 class VariableDropTarget(wx.TextDropTarget):
       
   315     '''
       
   316     This allows dragging a variable location from somewhere to the Location
       
   317     column of a variable row.
       
   318     
       
   319     The drag source should be a TextDataObject containing a Python tuple like:
       
   320         ('%ID0.0.0', 'location', 'REAL')
       
   321     
       
   322     c_ext/CFileEditor.py has an example of this (you can drag a C extension
       
   323     variable to the Location column of the variable panel).
       
   324     '''
       
   325     def __init__(self, parent):
       
   326         wx.TextDropTarget.__init__(self)
       
   327         self.ParentWindow = parent
       
   328     
       
   329     def OnDropText(self, x, y, data):
       
   330         x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
       
   331         col = self.ParentWindow.VariablesGrid.XToCol(x)
       
   332         row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
       
   333         if col != wx.NOT_FOUND and row != wx.NOT_FOUND:
       
   334             if self.ParentWindow.Table.GetColLabelValue(col, False) != "Location":
       
   335                 return
       
   336             message = None
       
   337             if not self.ParentWindow.Table.GetValueByName(row, "Edit"):
       
   338                 message = _("Can't give a location to a function block instance")
       
   339             elif self.ParentWindow.Table.GetValueByName(row, "Class") not in ["Local", "Global"]:
       
   340                 message = _("Can only give a location to local or global variables")
       
   341             else:
       
   342                 try:
       
   343                     values = eval(data)    
       
   344                 except:
       
   345                     message = _("Invalid value \"%s\" for location")%data
       
   346                     values = None
       
   347                 if not isinstance(values, TupleType):
       
   348                     message = _("Invalid value \"%s\" for location")%data
       
   349                     values = None
       
   350                 if values is not None and values[1] == "location":
       
   351                     location = values[0]
       
   352                     variable_type = self.ParentWindow.Table.GetValueByName(row, "Type")
       
   353                     base_type = self.ParentWindow.Controler.GetBaseType(variable_type)
       
   354                     message = None
       
   355                     if location.startswith("%"):
       
   356                         if base_type != values[2]:
       
   357                             message = _("Incompatible data types between \"%s\" and \"%s\"")%(values[2], variable_type)
       
   358                         else:
       
   359                             self.ParentWindow.Table.SetValue(row, col, location)
       
   360                             self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid)
       
   361                             self.ParentWindow.SaveValues()
       
   362                     else:
       
   363                         if location[0].isdigit() and base_type != "BOOL":
       
   364                             message = _("Incompatible size of data between \"%s\" and \"BOOL\"")%location
       
   365                         elif location[0] not in LOCATIONDATATYPES:
       
   366                             message = _("Unrecognized data size \"%s\"")%location[0]
       
   367                         elif base_type not in LOCATIONDATATYPES[location[0]]:
       
   368                             message = _("Incompatible size of data between \"%s\" and \"%s\"")%(location, variable_type)
       
   369                         else:
       
   370                             dialog = wx.SingleChoiceDialog(self.ParentWindow, _("Select a variable class:"), _("Variable class"), ["Input", "Output", "Memory"], wx.OK|wx.CANCEL)
       
   371                             if dialog.ShowModal() == wx.ID_OK:
       
   372                                 selected = dialog.GetSelection()
       
   373                                 if selected == 0:
       
   374                                     location = "%I" + location
       
   375                                 elif selected == 1:
       
   376                                     location = "%Q" + location
       
   377                                 else:
       
   378                                     location = "%M" + location
       
   379                                 self.ParentWindow.Table.SetValue(row, col, location)
       
   380                                 self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid)
       
   381                                 self.ParentWindow.SaveValues()
       
   382                             dialog.Destroy()
       
   383             if message is not None:
       
   384                 wx.CallAfter(self.ShowMessage, message)
       
   385             
       
   386     def ShowMessage(self, message):
       
   387         message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
       
   388         message.ShowModal()
       
   389         message.Destroy()
       
   390 
       
   391 [ID_VARIABLEEDITORPANEL, ID_VARIABLEEDITORPANELVARIABLESGRID, 
       
   392  ID_VARIABLEEDITORCONTROLPANEL, ID_VARIABLEEDITORPANELRETURNTYPE, 
       
   393  ID_VARIABLEEDITORPANELCLASSFILTER, ID_VARIABLEEDITORPANELADDBUTTON, 
       
   394  ID_VARIABLEEDITORPANELDELETEBUTTON, ID_VARIABLEEDITORPANELUPBUTTON, 
       
   395  ID_VARIABLEEDITORPANELDOWNBUTTON, ID_VARIABLEEDITORPANELSTATICTEXT1, 
       
   396  ID_VARIABLEEDITORPANELSTATICTEXT2, ID_VARIABLEEDITORPANELSTATICTEXT3,
       
   397 ] = [wx.NewId() for _init_ctrls in range(12)]
       
   398 
       
   399 class VariablePanel(wx.Panel):
       
   400     
       
   401     if wx.VERSION < (2, 6, 0):
       
   402         def Bind(self, event, function, id = None):
       
   403             if id is not None:
       
   404                 event(self, id, function)
       
   405             else:
       
   406                 event(self, function)
       
   407     
       
   408     def _init_coll_MainSizer_Items(self, parent):
       
   409         parent.AddWindow(self.VariablesGrid, 0, border=0, flag=wx.GROW)
       
   410         parent.AddWindow(self.ControlPanel, 0, border=5, flag=wx.GROW|wx.ALL)
       
   411     
       
   412     def _init_coll_MainSizer_Growables(self, parent):
       
   413         parent.AddGrowableCol(0)
       
   414         parent.AddGrowableRow(0)
       
   415     
       
   416     def _init_coll_ControlPanelSizer_Items(self, parent):
       
   417         parent.AddSizer(self.ChoicePanelSizer, 0, border=0, flag=wx.GROW)
       
   418         parent.AddSizer(self.ButtonPanelSizer, 0, border=0, flag=wx.ALIGN_CENTER)
       
   419 
       
   420     def _init_coll_ControlPanelSizer_Growables(self, parent):
       
   421         parent.AddGrowableCol(0)
       
   422         parent.AddGrowableRow(0)
       
   423         parent.AddGrowableRow(1)
       
   424 
       
   425     def _init_coll_ChoicePanelSizer_Items(self, parent):
       
   426         parent.AddWindow(self.staticText1, 0, border=0, flag=wx.ALIGN_BOTTOM)
       
   427         parent.AddWindow(self.ReturnType, 0, border=0, flag=0)
       
   428         parent.AddWindow(self.staticText2, 0, border=0, flag=wx.ALIGN_BOTTOM)
       
   429         parent.AddWindow(self.ClassFilter, 0, border=0, flag=0)
       
   430 
       
   431     def _init_coll_ButtonPanelSizer_Items(self, parent):
       
   432         parent.AddWindow(self.UpButton, 0, border=0, flag=0)
       
   433         parent.AddWindow(self.AddButton, 0, border=0, flag=0)
       
   434         parent.AddWindow(self.DownButton, 0, border=0, flag=0)
       
   435         parent.AddWindow(self.DeleteButton, 0, border=0, flag=0)
       
   436         
       
   437     def _init_coll_ButtonPanelSizer_Growables(self, parent):
       
   438         parent.AddGrowableCol(0)
       
   439         parent.AddGrowableCol(1)
       
   440         parent.AddGrowableCol(2)
       
   441         parent.AddGrowableCol(3)
       
   442         parent.AddGrowableRow(0)
       
   443 
       
   444     def _init_sizers(self):
       
   445         self.MainSizer = wx.FlexGridSizer(cols=2, hgap=10, rows=1, vgap=0)
       
   446         self.ControlPanelSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5)
       
   447         self.ChoicePanelSizer = wx.GridSizer(cols=1, hgap=5, rows=4, vgap=5)
       
   448         self.ButtonPanelSizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
       
   449         
       
   450         self._init_coll_MainSizer_Items(self.MainSizer)
       
   451         self._init_coll_MainSizer_Growables(self.MainSizer)
       
   452         self._init_coll_ControlPanelSizer_Items(self.ControlPanelSizer)
       
   453         self._init_coll_ControlPanelSizer_Growables(self.ControlPanelSizer)
       
   454         self._init_coll_ChoicePanelSizer_Items(self.ChoicePanelSizer)
       
   455         self._init_coll_ButtonPanelSizer_Items(self.ButtonPanelSizer)
       
   456         self._init_coll_ButtonPanelSizer_Growables(self.ButtonPanelSizer)
       
   457         
       
   458         self.SetSizer(self.MainSizer)
       
   459         self.ControlPanel.SetSizer(self.ControlPanelSizer)
       
   460 
       
   461     def _init_ctrls(self, prnt):
       
   462         wx.Panel.__init__(self, id=ID_VARIABLEEDITORPANEL,
       
   463               name='VariableEditorPanel', parent=prnt, pos=wx.Point(0, 0),
       
   464               size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
       
   465 
       
   466         self.VariablesGrid = CustomGrid(id=ID_VARIABLEEDITORPANELVARIABLESGRID,
       
   467               name='VariablesGrid', parent=self, pos=wx.Point(0, 0), 
       
   468               size=wx.Size(0, 0), style=wx.VSCROLL)
       
   469         self.VariablesGrid.SetFont(wx.Font(12, 77, wx.NORMAL, wx.NORMAL, False,
       
   470               'Sans'))
       
   471         self.VariablesGrid.SetLabelFont(wx.Font(10, 77, wx.NORMAL, wx.NORMAL,
       
   472               False, 'Sans'))
       
   473         self.VariablesGrid.SetSelectionBackground(wx.WHITE)
       
   474         self.VariablesGrid.SetSelectionForeground(wx.BLACK)
       
   475         if wx.VERSION >= (2, 6, 0):
       
   476             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange)
       
   477             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
       
   478             self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown)
       
   479         else:
       
   480             wx.grid.EVT_GRID_CELL_CHANGE(self.VariablesGrid, self.OnVariablesGridCellChange)
       
   481             wx.grid.EVT_GRID_CELL_LEFT_CLICK(self.VariablesGrid, self.OnVariablesGridCellLeftClick)
       
   482             wx.grid.EVT_GRID_EDITOR_SHOWN(self.VariablesGrid, self.OnVariablesGridEditorShown)
       
   483         
       
   484         self.ControlPanel = wx.ScrolledWindow(id=ID_VARIABLEEDITORCONTROLPANEL,
       
   485               name='ControlPanel', parent=self, pos=wx.Point(0, 0),
       
   486               size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
       
   487         self.ControlPanel.SetScrollRate(0, 10)
       
   488         
       
   489         self.staticText1 = wx.StaticText(id=ID_VARIABLEEDITORPANELSTATICTEXT1,
       
   490               label=_('Return Type:'), name='staticText1', parent=self.ControlPanel,
       
   491               pos=wx.Point(0, 0), size=wx.Size(145, 17), style=0)
       
   492 
       
   493         self.ReturnType = wx.ComboBox(id=ID_VARIABLEEDITORPANELRETURNTYPE,
       
   494               name='ReturnType', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   495               size=wx.Size(145, 28), style=wx.CB_READONLY)
       
   496         self.Bind(wx.EVT_COMBOBOX, self.OnReturnTypeChanged, id=ID_VARIABLEEDITORPANELRETURNTYPE)
       
   497 
       
   498         self.staticText2 = wx.StaticText(id=ID_VARIABLEEDITORPANELSTATICTEXT2,
       
   499               label=_('Class Filter:'), name='staticText2', parent=self.ControlPanel,
       
   500               pos=wx.Point(0, 0), size=wx.Size(145, 17), style=0)
       
   501 
       
   502         self.ClassFilter = wx.ComboBox(id=ID_VARIABLEEDITORPANELCLASSFILTER,
       
   503               name='ClassFilter', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   504               size=wx.Size(145, 28), style=wx.CB_READONLY)
       
   505         self.Bind(wx.EVT_COMBOBOX, self.OnClassFilter, id=ID_VARIABLEEDITORPANELCLASSFILTER)
       
   506 
       
   507         self.AddButton = wx.Button(id=ID_VARIABLEEDITORPANELADDBUTTON, label=_('Add'),
       
   508               name='AddButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   509               size=wx.DefaultSize, style=0)
       
   510         
       
   511         self.DeleteButton = wx.Button(id=ID_VARIABLEEDITORPANELDELETEBUTTON, label=_('Delete'),
       
   512               name='DeleteButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   513               size=wx.DefaultSize, style=0)
       
   514         
       
   515         self.UpButton = wx.Button(id=ID_VARIABLEEDITORPANELUPBUTTON, label='^',
       
   516               name='UpButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   517               size=wx.Size(32, 32), style=0)
       
   518         
       
   519         self.DownButton = wx.Button(id=ID_VARIABLEEDITORPANELDOWNBUTTON, label='v',
       
   520               name='DownButton', parent=self.ControlPanel, pos=wx.Point(0, 0),
       
   521               size=wx.Size(32, 32), style=0)
       
   522         
       
   523         self._init_sizers()
       
   524 
       
   525     def __init__(self, parent, window, controler, element_type):
       
   526         self._init_ctrls(parent)
       
   527         self.ParentWindow = window
       
   528         self.Controler = controler
       
   529         self.ElementType = element_type
       
   530         
       
   531         self.RefreshHighlightsTimer = wx.Timer(self, -1)
       
   532         self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer)
       
   533         
       
   534         self.Filter = "All"
       
   535         self.FilterChoices = []
       
   536         self.FilterChoiceTransfer = GetFilterChoiceTransfer()
       
   537         
       
   538         self.DefaultValue = {   "Name" : "", "Class" : "", "Type" : "INT", "Location" : "",
       
   539                                 "Initial Value" : "", "Option" : "",
       
   540                                 "Documentation" : "", "Edit" : True
       
   541                             }
       
   542 
       
   543         if element_type in ["config", "resource"]:
       
   544             self.DefaultTypes = {"All" : "Global"}
       
   545         else:
       
   546             self.DefaultTypes = {"All" : "Local", "Interface" : "Input", "Variables" : "Local"}
       
   547 
       
   548         if element_type in ["config", "resource"] \
       
   549         or element_type in ["program", "transition", "action"]:
       
   550             # this is an element that can have located variables
       
   551             self.Table = VariableTable(self, [], GetVariableTableColnames(True))
       
   552 
       
   553             if element_type in ["config", "resource"]:
       
   554                 self.FilterChoices = ["All", "Global"]#,"Access"]
       
   555             else:
       
   556                 self.FilterChoices = ["All",
       
   557                                         "Interface", "   Input", "   Output", "   InOut", "   External",
       
   558                                         "Variables", "   Local", "   Temp"]#,"Access"]
       
   559 
       
   560             # these condense the ColAlignements list
       
   561             l = wx.ALIGN_LEFT
       
   562             c = wx.ALIGN_CENTER 
       
   563 
       
   564             #                      Num  Name    Class   Type    Loc     Init    Option   Doc
       
   565             self.ColSizes       = [40,  80,     70,     80,     80,     80,     100,     80]
       
   566             self.ColAlignements = [c,   l,      l,      l,      l,      l,      l,       l]
       
   567 
       
   568         else:
       
   569             # this is an element that cannot have located variables
       
   570             self.Table = VariableTable(self, [], GetVariableTableColnames(False))
       
   571 
       
   572             if element_type == "function":
       
   573                 self.FilterChoices = ["All",
       
   574                                         "Interface", "   Input", "   Output", "   InOut",
       
   575                                         "Variables", "   Local"]
       
   576             else:
       
   577                 self.FilterChoices = ["All",
       
   578                                         "Interface", "   Input", "   Output", "   InOut", "   External",
       
   579                                         "Variables", "   Local", "   Temp"]
       
   580 
       
   581             # these condense the ColAlignements list
       
   582             l = wx.ALIGN_LEFT
       
   583             c = wx.ALIGN_CENTER 
       
   584 
       
   585             #                      Num  Name    Class   Type    Init    Option   Doc
       
   586             self.ColSizes       = [40,  80,     70,     80,     80,     100,     160]
       
   587             self.ColAlignements = [c,   l,      l,      l,      l,      l,       l]
       
   588 
       
   589         for choice in self.FilterChoices:
       
   590             self.ClassFilter.Append(_(choice))
       
   591 
       
   592         reverse_transfer = {}
       
   593         for filter, choice in self.FilterChoiceTransfer.items():
       
   594             reverse_transfer[choice] = filter
       
   595         self.ClassFilter.SetStringSelection(_(reverse_transfer[self.Filter]))
       
   596         self.RefreshTypeList()
       
   597 
       
   598         self.VariablesGrid.SetTable(self.Table)
       
   599         self.VariablesGrid.SetButtons({"Add": self.AddButton,
       
   600                                        "Delete": self.DeleteButton,
       
   601                                        "Up": self.UpButton,
       
   602                                        "Down": self.DownButton})
       
   603         
       
   604         def _AddVariable(new_row):
       
   605             if not self.PouIsUsed or self.Filter not in ["Interface", "Input", "Output", "InOut"]:
       
   606                 row_content = self.DefaultValue.copy()
       
   607                 if self.Filter in self.DefaultTypes:
       
   608                     row_content["Class"] = self.DefaultTypes[self.Filter]
       
   609                 else:
       
   610                     row_content["Class"] = self.Filter
       
   611                 if self.Filter == "All" and len(self.Values) > 0:
       
   612                     self.Values.insert(new_row, row_content)
       
   613                 else:
       
   614                     self.Values.append(row_content)
       
   615                     new_row = self.Table.GetNumberRows()
       
   616                 self.SaveValues()
       
   617                 self.RefreshValues()
       
   618                 return new_row
       
   619             return self.VariablesGrid.GetGridCursorRow()
       
   620         setattr(self.VariablesGrid, "_AddRow", _AddVariable)
       
   621         
       
   622         def _DeleteVariable(row):
       
   623             if (self.Table.GetValueByName(row, "Edit") and 
       
   624                 (not self.PouIsUsed or self.Table.GetValueByName(row, "Class") not in ["Input", "Output", "InOut"])):
       
   625                 self.Values.remove(self.Table.GetRow(row))
       
   626                 self.SaveValues()
       
   627                 self.RefreshValues()
       
   628         setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
       
   629             
       
   630         def _MoveVariable(row, move):
       
   631             if (self.Filter == "All" and 
       
   632                 (not self.PouIsUsed or self.Table.GetValueByName(row, "Class") not in ["Input", "Output", "InOut"])):
       
   633                 new_row = max(0, min(row + move, len(self.Values) - 1))
       
   634                 if new_row != row:
       
   635                     self.Values.insert(new_row, self.Values.pop(row))
       
   636                     self.SaveValues()
       
   637                     self.RefreshValues()
       
   638                 return new_row
       
   639             return row
       
   640         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
       
   641         
       
   642         def _RefreshButtons():
       
   643             table_length = len(self.Table.data)
       
   644             row_class = None
       
   645             row_edit = True
       
   646             row = 0
       
   647             if table_length > 0:
       
   648                 row = self.VariablesGrid.GetGridCursorRow()
       
   649                 row_edit = self.Table.GetValueByName(row, "Edit")
       
   650                 if self.PouIsUsed:
       
   651                     row_class = self.Table.GetValueByName(row, "Class")
       
   652             self.AddButton.Enable(not self.PouIsUsed or self.Filter not in ["Interface", "Input", "Output", "InOut"])
       
   653             self.DeleteButton.Enable(table_length > 0 and row_edit and row_class not in ["Input", "Output", "InOut"])
       
   654             self.UpButton.Enable(table_length > 0 and row > 0 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"])
       
   655             self.DownButton.Enable(table_length > 0 and row < table_length - 1 and self.Filter == "All" and row_class not in ["Input", "Output", "InOut"])
       
   656         setattr(self.VariablesGrid, "RefreshButtons", _RefreshButtons)
       
   657         
       
   658         self.VariablesGrid.SetRowLabelSize(0)
       
   659         for col in range(self.Table.GetNumberCols()):
       
   660             attr = wx.grid.GridCellAttr()
       
   661             attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE)
       
   662             self.VariablesGrid.SetColAttr(col, attr)
       
   663             self.VariablesGrid.SetColMinimalWidth(col, self.ColSizes[col])
       
   664             self.VariablesGrid.AutoSizeColumn(col, False)
       
   665     
       
   666     def __del__(self):
       
   667         self.RefreshHighlightsTimer.Stop()
       
   668     
       
   669     def SetTagName(self, tagname):
       
   670         self.TagName = tagname
       
   671     
       
   672     def IsFunctionBlockType(self, name):
       
   673         bodytype = self.Controler.GetEditedElementBodyType(self.TagName)
       
   674         pouname, poutype = self.Controler.GetEditedElementType(self.TagName)
       
   675         if poutype != "function" and bodytype in ["ST", "IL"]:
       
   676             return False
       
   677         else:
       
   678             return name in self.Controler.GetFunctionBlockTypes(self.TagName)
       
   679     
       
   680     def RefreshView(self):
       
   681         self.PouNames = self.Controler.GetProjectPouNames()
       
   682         
       
   683         words = self.TagName.split("::")
       
   684         if self.ElementType == "config":
       
   685             self.PouIsUsed = False
       
   686             returnType = None
       
   687             self.Values = self.Controler.GetConfigurationGlobalVars(words[1])
       
   688         elif self.ElementType == "resource":
       
   689             self.PouIsUsed = False
       
   690             returnType = None
       
   691             self.Values = self.Controler.GetConfigurationResourceGlobalVars(words[1], words[2])
       
   692         else:
       
   693             if self.ElementType == "function":
       
   694                 self.ReturnType.Clear()
       
   695                 for base_type in self.Controler.GetDataTypes(self.TagName, True):
       
   696                     self.ReturnType.Append(base_type)
       
   697                 returnType = self.Controler.GetEditedElementInterfaceReturnType(self.TagName)
       
   698             else:
       
   699                 returnType = None
       
   700             self.PouIsUsed = self.Controler.PouIsUsed(words[1])
       
   701             self.Values = self.Controler.GetEditedElementInterfaceVars(self.TagName)
       
   702         
       
   703         if returnType is not None:
       
   704             self.ReturnType.SetStringSelection(returnType)
       
   705             self.ReturnType.Enable(True)
       
   706             self.staticText1.Show()
       
   707             self.ReturnType.Show()
       
   708         else:
       
   709             self.ReturnType.Enable(False)
       
   710             self.staticText1.Hide()
       
   711             self.ReturnType.Hide()
       
   712         
       
   713         self.RefreshValues()
       
   714         self.VariablesGrid.RefreshButtons()
       
   715     
       
   716     def OnReturnTypeChanged(self, event):
       
   717         words = self.TagName.split("::")
       
   718         self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection())
       
   719         self.Controler.BufferProject()
       
   720         self.ParentWindow.RefreshEditor(variablepanel = False)
       
   721         self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, INSTANCESTREE, LIBRARYTREE)
       
   722         event.Skip()
       
   723     
       
   724     def OnClassFilter(self, event):
       
   725         self.Filter = self.FilterChoiceTransfer[VARIABLE_CHOICES_DICT[self.ClassFilter.GetStringSelection()]]
       
   726         self.RefreshTypeList()
       
   727         self.RefreshValues()
       
   728         self.VariablesGrid.RefreshButtons()
       
   729         event.Skip()
       
   730 
       
   731     def RefreshTypeList(self):
       
   732         if self.Filter == "All":
       
   733             self.ClassList = [self.FilterChoiceTransfer[choice] for choice in self.FilterChoices if self.FilterChoiceTransfer[choice] not in ["All","Interface","Variables"]]
       
   734         elif self.Filter == "Interface":
       
   735             self.ClassList = ["Input","Output","InOut","External"]
       
   736         elif self.Filter == "Variables":
       
   737             self.ClassList = ["Local","Temp"]
       
   738         else:
       
   739             self.ClassList = [self.Filter]
       
   740 
       
   741     def OnVariablesGridCellChange(self, event):
       
   742         row, col = event.GetRow(), event.GetCol()
       
   743         colname = self.Table.GetColLabelValue(col, False)
       
   744         value = self.Table.GetValue(row, col)
       
   745 
       
   746         if colname == "Name" and value != "":
       
   747             if not TestIdentifier(value):
       
   748                 message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   749                 message.ShowModal()
       
   750                 message.Destroy()
       
   751                 event.Veto()
       
   752             elif value.upper() in IEC_KEYWORDS:
       
   753                 message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   754                 message.ShowModal()
       
   755                 message.Destroy()
       
   756                 event.Veto()
       
   757             elif value.upper() in self.PouNames:
       
   758                 message = wx.MessageDialog(self, _("A POU named \"%s\" already exists!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   759                 message.ShowModal()
       
   760                 message.Destroy()
       
   761                 event.Veto()
       
   762             elif value.upper() in [var["Name"].upper() for var in self.Values if var != self.Table.data[row]]:
       
   763                 message = wx.MessageDialog(self, _("A variable with \"%s\" as name already exists in this pou!")%value, _("Error"), wx.OK|wx.ICON_ERROR)
       
   764                 message.ShowModal()
       
   765                 message.Destroy()
       
   766                 event.Veto()
       
   767             else:
       
   768                 self.SaveValues(False)
       
   769                 old_value = self.Table.GetOldValue()
       
   770                 if old_value != "":
       
   771                     self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value)
       
   772                 self.Controler.BufferProject()
       
   773                 self.ParentWindow.RefreshEditor(variablepanel = False)
       
   774                 self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, INSTANCESTREE, LIBRARYTREE)
       
   775                 event.Skip()
       
   776         else:
       
   777             self.SaveValues()
       
   778             if colname == "Class":
       
   779                 self.ParentWindow.RefreshEditor(variablepanel = False)
       
   780             event.Skip()
       
   781     
       
   782     def OnVariablesGridEditorShown(self, event):
       
   783         row, col = event.GetRow(), event.GetCol() 
       
   784 
       
   785         label_value = self.Table.GetColLabelValue(col)
       
   786         if label_value == "Type":
       
   787             type_menu = wx.Menu(title='')   # the root menu
       
   788 
       
   789             # build a submenu containing standard IEC types
       
   790             base_menu = wx.Menu(title='')
       
   791             for base_type in self.Controler.GetBaseTypes():
       
   792                 new_id = wx.NewId()
       
   793                 AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type)
       
   794                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), id=new_id)
       
   795 
       
   796             type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu)
       
   797 
       
   798             # build a submenu containing user-defined types
       
   799             datatype_menu = wx.Menu(title='')
       
   800             
       
   801             # TODO : remove complextypes argument when matiec can manage complex types in pou interface
       
   802             datatypes = self.Controler.GetDataTypes(basetypes = False)
       
   803             for datatype in datatypes:
       
   804                 new_id = wx.NewId()
       
   805                 AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype)
       
   806                 self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id)
       
   807 
       
   808             type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu)
       
   809 
       
   810             # build a submenu containing function block types
       
   811             bodytype = self.Controler.GetEditedElementBodyType(self.TagName)
       
   812             pouname, poutype = self.Controler.GetEditedElementType(self.TagName)
       
   813             classtype = self.Table.GetValueByName(row, "Class")
       
   814             
       
   815             new_id = wx.NewId()
       
   816             AppendMenu(type_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Array"))
       
   817             self.Bind(wx.EVT_MENU, self.VariableArrayTypeFunction, id=new_id)
       
   818             
       
   819             if classtype in ["Input", "Output", "InOut", "External", "Global"] or \
       
   820             poutype != "function" and bodytype in ["ST", "IL"]:
       
   821                 functionblock_menu = wx.Menu(title='')
       
   822                 fbtypes = self.Controler.GetFunctionBlockTypes(self.TagName)
       
   823                 for functionblock_type in fbtypes:
       
   824                     new_id = wx.NewId()
       
   825                     AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type)
       
   826                     self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id)
       
   827 
       
   828                 type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu)
       
   829 
       
   830             rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col))
       
   831             corner_x = rect.x + rect.width
       
   832             corner_y = rect.y + self.VariablesGrid.GetColLabelSize()
       
   833 
       
   834             # pop up this new menu
       
   835             self.VariablesGrid.PopupMenuXY(type_menu, corner_x, corner_y)
       
   836             event.Veto()
       
   837         else:
       
   838             event.Skip()
       
   839     
       
   840     def GetVariableTypeFunction(self, base_type):
       
   841         def VariableTypeFunction(event):
       
   842             row = self.VariablesGrid.GetGridCursorRow()
       
   843             self.Table.SetValueByName(row, "Type", base_type)
       
   844             self.Table.ResetView(self.VariablesGrid)
       
   845             self.SaveValues(False)
       
   846             self.ParentWindow.RefreshEditor(variablepanel = False)
       
   847             self.Controler.BufferProject()
       
   848             self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, INSTANCESTREE, LIBRARYTREE)
       
   849         return VariableTypeFunction
       
   850     
       
   851     def VariableArrayTypeFunction(self, event):
       
   852         row = self.VariablesGrid.GetGridCursorRow()
       
   853         dialog = ArrayTypeDialog(self, 
       
   854                                  self.Controler.GetDataTypes(self.TagName), 
       
   855                                  self.Table.GetValueByName(row, "Type"))
       
   856         if dialog.ShowModal() == wx.ID_OK:
       
   857             self.Table.SetValueByName(row, "Type", dialog.GetValue())
       
   858             self.Table.ResetView(self.VariablesGrid)
       
   859             self.SaveValues(False)
       
   860             self.ParentWindow.RefreshEditor(variablepanel = False)
       
   861             self.Controler.BufferProject()
       
   862             self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, INSTANCESTREE, LIBRARYTREE)
       
   863         dialog.Destroy()
       
   864     
       
   865     def OnVariablesGridCellLeftClick(self, event):
       
   866         row = event.GetRow()
       
   867         if event.GetCol() == 0 and self.Table.GetValueByName(row, "Edit"):
       
   868             row = event.GetRow()
       
   869             var_name = self.Table.GetValueByName(row, "Name")
       
   870             var_class = self.Table.GetValueByName(row, "Class")
       
   871             var_type = self.Table.GetValueByName(row, "Type")
       
   872             data = wx.TextDataObject(str((var_name, var_class, var_type, self.TagName)))
       
   873             dragSource = wx.DropSource(self.VariablesGrid)
       
   874             dragSource.SetData(data)
       
   875             dragSource.DoDragDrop()
       
   876         event.Skip()
       
   877     
       
   878     def RefreshValues(self):
       
   879         data = []
       
   880         for num, variable in enumerate(self.Values):
       
   881             if variable["Class"] in self.ClassList:
       
   882                 variable["Number"] = num + 1
       
   883                 data.append(variable)
       
   884         self.Table.SetData(data)
       
   885         self.Table.ResetView(self.VariablesGrid)
       
   886             
       
   887     def SaveValues(self, buffer = True):
       
   888         words = self.TagName.split("::")
       
   889         if self.ElementType == "config":
       
   890             self.Controler.SetConfigurationGlobalVars(words[1], self.Values)
       
   891         elif self.ElementType == "resource":
       
   892             self.Controler.SetConfigurationResourceGlobalVars(words[1], words[2], self.Values)
       
   893         else:
       
   894             if self.ReturnType.IsEnabled():
       
   895                 self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection())
       
   896             self.Controler.SetPouInterfaceVars(words[1], self.Values)
       
   897         if buffer:
       
   898             self.Controler.BufferProject()
       
   899             self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, INSTANCESTREE, LIBRARYTREE)            
       
   900 
       
   901 #-------------------------------------------------------------------------------
       
   902 #                        Highlights showing functions
       
   903 #-------------------------------------------------------------------------------
       
   904 
       
   905     def OnRefreshHighlightsTimer(self, event):
       
   906         self.Table.ResetView(self.VariablesGrid)
       
   907         event.Skip()
       
   908 
       
   909     def AddVariableHighlight(self, infos, highlight_type):
       
   910         if isinstance(infos[0], TupleType):
       
   911             for i in xrange(*infos[0]):
       
   912                 self.Table.AddHighlight((i,) + infos[1:], highlight_type)
       
   913         else:
       
   914             self.Table.AddHighlight(infos, highlight_type)
       
   915         self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True)
       
   916 
       
   917     def ClearHighlights(self, highlight_type=None):
       
   918         self.Table.ClearHighlights(highlight_type)
       
   919         self.Table.ResetView(self.VariablesGrid)
       
   920 
       
   921 
       
   922 
       
   923 class LocationCellControl(wx.PyControl):
       
   924     
       
   925     def _init_coll_MainSizer_Items(self, parent):
       
   926         parent.AddWindow(self.Location, 0, border=0, flag=wx.GROW)
       
   927         parent.AddWindow(self.BrowseButton, 0, border=0, flag=wx.GROW)
       
   928         
       
   929     def _init_coll_MainSizer_Growables(self, parent):
       
   930         parent.AddGrowableCol(0)
       
   931         parent.AddGrowableRow(0)
       
   932     
       
   933     def _init_sizers(self):
       
   934         self.MainSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
       
   935         
       
   936         self._init_coll_MainSizer_Items(self.MainSizer)
       
   937         self._init_coll_MainSizer_Growables(self.MainSizer)
       
   938         
       
   939         self.SetSizer(self.MainSizer)
       
   940     
       
   941     def _init_ctrls(self, prnt):
       
   942         wx.Control.__init__(self, id=-1, 
       
   943               name='LocationCellControl', parent=prnt,  
       
   944               size=wx.DefaultSize, style=0)
       
   945         
       
   946         # create location text control
       
   947         self.Location = wx.TextCtrl(id=-1, name='Location', parent=self,
       
   948               pos=wx.Point(0, 0), size=wx.Size(0, 0), style=wx.TE_PROCESS_ENTER)
       
   949         self.Location.Bind(wx.EVT_KEY_DOWN, self.OnLocationChar)
       
   950         
       
   951         # create browse button
       
   952         self.BrowseButton = wx.Button(id=-1, label='...', 
       
   953               name='BrowseButton', parent=self, pos=wx.Point(0, 0), 
       
   954               size=wx.Size(30, 0), style=0)
       
   955         self.BrowseButton.Bind(wx.EVT_BUTTON, self.OnBrowseButtonClick)
       
   956 
       
   957         self.Bind(wx.EVT_SIZE, self.OnSize)
       
   958         
       
   959         self._init_sizers()
       
   960 
       
   961     '''
       
   962     Custom cell editor control with a text box and a button that launches
       
   963     the BrowseLocationsDialog.
       
   964     '''
       
   965     def __init__(self, parent, locations):
       
   966         self._init_ctrls(parent)
       
   967         self.Locations = locations
       
   968         self.VarType = None
       
   969 
       
   970     def SetVarType(self, vartype):
       
   971         self.VarType = vartype
       
   972 
       
   973     def SetValue(self, value):
       
   974         self.Location.SetValue(value)
       
   975     
       
   976     def GetValue(self):
       
   977         return self.Location.GetValue()
       
   978 
       
   979     def OnSize(self, event):
       
   980         self.Layout()
       
   981 
       
   982     def OnBrowseButtonClick(self, event):
       
   983         # pop up the location browser dialog
       
   984         dialog = BrowseLocationsDialog(self, self.VarType, self.Locations)
       
   985         if dialog.ShowModal() == wx.ID_OK:
       
   986             infos = dialog.GetValues()
       
   987 
       
   988             # set the location
       
   989             self.Location.SetValue(infos["location"])
       
   990 
       
   991         dialog.Destroy()
       
   992 
       
   993         self.Location.SetFocus()
       
   994 
       
   995     def OnLocationChar(self, event):
       
   996         keycode = event.GetKeyCode()
       
   997         if keycode == wx.WXK_RETURN or keycode == wx.WXK_TAB:
       
   998             self.Parent.Parent.ProcessEvent(event)
       
   999             self.Parent.Parent.SetFocus()
       
  1000         else:
       
  1001             event.Skip()
       
  1002 
       
  1003     def SetInsertionPoint(self, i):
       
  1004         self.Location.SetInsertionPoint(i)
       
  1005     
       
  1006     def SetFocus(self):
       
  1007         self.Location.SetFocus()
       
  1008 
       
  1009 class LocationCellEditor(wx.grid.PyGridCellEditor):
       
  1010     '''
       
  1011     Grid cell editor that uses LocationCellControl to display a browse button.
       
  1012     '''
       
  1013     def __init__(self, table, controler):
       
  1014         wx.grid.PyGridCellEditor.__init__(self)
       
  1015         self.Table = table
       
  1016         self.Controler = controler
       
  1017 
       
  1018     def __del__(self):
       
  1019         self.CellControl = None
       
  1020     
       
  1021     def Create(self, parent, id, evt_handler):
       
  1022         locations = self.Controler.GetVariableLocationTree()
       
  1023         if len(locations) > 0:
       
  1024             self.CellControl = LocationCellControl(parent, locations)
       
  1025         else:
       
  1026             self.CellControl = wx.TextCtrl(parent, -1)
       
  1027         self.SetControl(self.CellControl)
       
  1028         if evt_handler:
       
  1029             self.CellControl.PushEventHandler(evt_handler)
       
  1030 
       
  1031     def BeginEdit(self, row, col, grid):
       
  1032         self.CellControl.SetValue(self.Table.GetValueByName(row, 'Location'))
       
  1033         if isinstance(self.CellControl, LocationCellControl):
       
  1034             self.CellControl.SetVarType(self.Controler.GetBaseType(self.Table.GetValueByName(row, 'Type')))
       
  1035         self.CellControl.SetFocus()
       
  1036 
       
  1037     def EndEdit(self, row, col, grid):
       
  1038         loc = self.CellControl.GetValue()
       
  1039         old_loc = self.Table.GetValueByName(row, 'Location')
       
  1040         if loc != old_loc:
       
  1041             self.Table.SetValueByName(row, 'Location', loc)
       
  1042             return True
       
  1043         return False
       
  1044 
       
  1045     def SetSize(self, rect):
       
  1046         self.CellControl.SetDimensions(rect.x + 1, rect.y,
       
  1047                                         rect.width, rect.height,
       
  1048                                         wx.SIZE_ALLOW_MINUS_ONE)
       
  1049 
       
  1050     def Clone(self):
       
  1051         return LocationCellEditor(self.Table, self.Controler)
       
  1052 
       
  1053 def GetDirChoiceOptions():
       
  1054     _ = lambda x : x
       
  1055     return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]), 
       
  1056             (_("Input"), [LOCATION_VAR_INPUT]), 
       
  1057             (_("Output"), [LOCATION_VAR_OUTPUT]), 
       
  1058             (_("Memory"), [LOCATION_VAR_MEMORY])]
       
  1059 DIRCHOICE_OPTIONS_FILTER = dict([(_(option), filter) for option, filter in GetDirChoiceOptions()])
       
  1060 
       
  1061 # turn LOCATIONDATATYPES inside-out
       
  1062 LOCATION_SIZES = {}
       
  1063 for size, types in LOCATIONDATATYPES.iteritems():
       
  1064     for type in types:
       
  1065         LOCATION_SIZES[type] = size
       
  1066 
       
  1067 [ID_BROWSELOCATIONSDIALOG, ID_BROWSELOCATIONSDIALOGLOCATIONSTREE, 
       
  1068  ID_BROWSELOCATIONSDIALOGDIRCHOICE, ID_BROWSELOCATIONSDIALOGSTATICTEXT1, 
       
  1069  ID_BROWSELOCATIONSDIALOGSTATICTEXT2, 
       
  1070 ] = [wx.NewId() for _init_ctrls in range(5)]
       
  1071 
       
  1072 class BrowseLocationsDialog(wx.Dialog):
       
  1073     
       
  1074     if wx.VERSION < (2, 6, 0):
       
  1075         def Bind(self, event, function, id = None):
       
  1076             if id is not None:
       
  1077                 event(self, id, function)
       
  1078             else:
       
  1079                 event(self, function)
       
  1080     
       
  1081     def _init_coll_MainSizer_Items(self, parent):
       
  1082         parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW)
       
  1083         parent.AddWindow(self.LocationsTree, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.GROW)
       
  1084         parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW)
       
  1085         
       
  1086     def _init_coll_MainSizer_Growables(self, parent):
       
  1087         parent.AddGrowableCol(0)
       
  1088         parent.AddGrowableRow(1)
       
  1089     
       
  1090     def _init_coll_ButtonGridSizer_Items(self, parent):
       
  1091         parent.AddWindow(self.staticText2, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
       
  1092         parent.AddWindow(self.DirChoice, 0, border=0, flag=wx.ALIGN_CENTER_VERTICAL)
       
  1093         parent.AddSizer(self.ButtonSizer, 0, border=0, flag=wx.ALIGN_RIGHT)
       
  1094         
       
  1095     def _init_coll_ButtonGridSizer_Growables(self, parent):
       
  1096         parent.AddGrowableCol(2)
       
  1097         parent.AddGrowableRow(0)
       
  1098     
       
  1099     def _init_sizers(self):
       
  1100         self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10)
       
  1101         self.ButtonGridSizer = wx.FlexGridSizer(cols=3, hgap=5, rows=1, vgap=0)
       
  1102         
       
  1103         self._init_coll_MainSizer_Items(self.MainSizer)
       
  1104         self._init_coll_MainSizer_Growables(self.MainSizer)
       
  1105         self._init_coll_ButtonGridSizer_Items(self.ButtonGridSizer)
       
  1106         self._init_coll_ButtonGridSizer_Growables(self.ButtonGridSizer)
       
  1107         
       
  1108         self.SetSizer(self.MainSizer)
       
  1109     
       
  1110     def _init_ctrls(self, prnt):
       
  1111         wx.Dialog.__init__(self, id=ID_BROWSELOCATIONSDIALOG, 
       
  1112               name='BrowseLocationsDialog', parent=prnt,  
       
  1113               size=wx.Size(600, 400), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
       
  1114               title=_('Browse Locations'))
       
  1115     
       
  1116         self.staticText1 = wx.StaticText(id=ID_BROWSELOCATIONSDIALOGSTATICTEXT1,
       
  1117               label=_('Locations available:'), name='staticText1', parent=self,
       
  1118               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
  1119         
       
  1120         if wx.Platform == '__WXMSW__':
       
  1121             treestyle = wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER
       
  1122         else:
       
  1123             treestyle = wx.TR_HAS_BUTTONS|wx.TR_HIDE_ROOT|wx.TR_SINGLE|wx.SUNKEN_BORDER
       
  1124         self.LocationsTree = wx.TreeCtrl(id=ID_BROWSELOCATIONSDIALOGLOCATIONSTREE,
       
  1125               name='LocationsTree', parent=self, pos=wx.Point(0, 0),
       
  1126               size=wx.Size(0, 0), style=treestyle)
       
  1127         self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, 
       
  1128                   id=ID_BROWSELOCATIONSDIALOGLOCATIONSTREE)
       
  1129         
       
  1130         self.staticText2 = wx.StaticText(id=ID_BROWSELOCATIONSDIALOGSTATICTEXT2,
       
  1131               label=_('Direction:'), name='staticText2', parent=self,
       
  1132               pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
       
  1133         
       
  1134         self.DirChoice = wx.ComboBox(id=ID_BROWSELOCATIONSDIALOGDIRCHOICE,
       
  1135               name='DirChoice', parent=self, pos=wx.Point(0, 0),
       
  1136               size=wx.DefaultSize, style=wx.CB_READONLY)
       
  1137         self.Bind(wx.EVT_COMBOBOX, self.OnDirChoice, id=ID_BROWSELOCATIONSDIALOGDIRCHOICE)
       
  1138         
       
  1139         self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE)
       
  1140         if wx.VERSION >= (2, 5, 0):
       
  1141             self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId())
       
  1142         else:
       
  1143             self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetChildren()[0].GetSizer().GetChildren()[0].GetWindow().GetId())
       
  1144         
       
  1145         self._init_sizers()
       
  1146     
       
  1147     def __init__(self, parent, var_type, locations):
       
  1148         self._init_ctrls(parent)
       
  1149         self.VarType = var_type
       
  1150         self.Locations = locations
       
  1151         
       
  1152         # Define Tree item icon list
       
  1153         self.TreeImageList = wx.ImageList(16, 16)
       
  1154         self.TreeImageDict = {}
       
  1155         
       
  1156         # Icons for items
       
  1157         for imgname, itemtype in [
       
  1158             ("CONFIGURATION", LOCATION_PLUGIN), 
       
  1159             ("RESOURCE",      LOCATION_MODULE), 
       
  1160             ("PROGRAM",       LOCATION_GROUP), 
       
  1161             ("VAR_INPUT",     LOCATION_VAR_INPUT), 
       
  1162             ("VAR_OUTPUT",    LOCATION_VAR_OUTPUT), 
       
  1163             ("VAR_LOCAL",     LOCATION_VAR_MEMORY)]:
       
  1164             self.TreeImageDict[itemtype]=self.TreeImageList.Add(wx.Bitmap(os.path.join(CWD, 'Images', '%s.png'%imgname)))
       
  1165         
       
  1166         # Assign icon list to TreeCtrls
       
  1167         self.LocationsTree.SetImageList(self.TreeImageList)
       
  1168         
       
  1169         # Set a options for the choice
       
  1170         for option, filter in GetDirChoiceOptions():
       
  1171             self.DirChoice.Append(_(option))
       
  1172         self.DirChoice.SetStringSelection(_("All"))
       
  1173         self.RefreshFilter()
       
  1174         
       
  1175         self.RefreshLocationsTree()
       
  1176     
       
  1177     def RefreshFilter(self):
       
  1178         self.Filter = DIRCHOICE_OPTIONS_FILTER[self.DirChoice.GetStringSelection()]
       
  1179     
       
  1180     def RefreshLocationsTree(self):
       
  1181         root = self.LocationsTree.GetRootItem()
       
  1182         if not root.IsOk():
       
  1183             if wx.Platform == '__WXMSW__':
       
  1184                 root = self.LocationsTree.AddRoot(_('Plugins'))
       
  1185             else:
       
  1186                 root = self.LocationsTree.AddRoot("")
       
  1187         self.GenerateLocationsTreeBranch(root, self.Locations)
       
  1188         self.LocationsTree.Expand(root)
       
  1189     
       
  1190     def GenerateLocationsTreeBranch(self, root, locations):
       
  1191         to_delete = []
       
  1192         if wx.VERSION >= (2, 6, 0):
       
  1193             item, root_cookie = self.LocationsTree.GetFirstChild(root)
       
  1194         else:
       
  1195             item, root_cookie = self.LocationsTree.GetFirstChild(root, 0)
       
  1196         for loc_infos in locations:
       
  1197             infos = loc_infos.copy()
       
  1198             if infos["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP] or\
       
  1199                infos["type"] in self.Filter and (infos["IEC_type"] == self.VarType or
       
  1200                infos["IEC_type"] is None and LOCATION_SIZES[self.VarType] == infos["size"]):
       
  1201                 children = [child for child in infos.pop("children")]
       
  1202                 if not item.IsOk():
       
  1203                     item = self.LocationsTree.AppendItem(root, infos["name"])
       
  1204                     if wx.Platform != '__WXMSW__':
       
  1205                         item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie)
       
  1206                 else:
       
  1207                     self.LocationsTree.SetItemText(item, infos["name"])
       
  1208                 self.LocationsTree.SetPyData(item, infos)
       
  1209                 self.LocationsTree.SetItemImage(item, self.TreeImageDict[infos["type"]])
       
  1210                 self.GenerateLocationsTreeBranch(item, children)
       
  1211                 item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie)
       
  1212         while item.IsOk():
       
  1213             to_delete.append(item)
       
  1214             item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie)
       
  1215         for item in to_delete:
       
  1216             self.LocationsTree.Delete(item)
       
  1217     
       
  1218     def OnLocationsTreeItemActivated(self, event):
       
  1219         infos = self.LocationsTree.GetPyData(event.GetItem())
       
  1220         if infos["type"] not in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]:
       
  1221             wx.CallAfter(self.EndModal, wx.ID_OK)
       
  1222         event.Skip()
       
  1223     
       
  1224     def OnDirChoice(self, event):
       
  1225         self.RefreshFilter()
       
  1226         self.RefreshLocationsTree()
       
  1227         
       
  1228     def GetValues(self):
       
  1229         selected = self.LocationsTree.GetSelection()
       
  1230         return self.LocationsTree.GetPyData(selected)
       
  1231         
       
  1232     def OnOK(self, event):
       
  1233         selected = self.LocationsTree.GetSelection()
       
  1234         var_infos = None
       
  1235         if selected.IsOk():
       
  1236             var_infos = self.LocationsTree.GetPyData(selected)
       
  1237         if var_infos is None or var_infos["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]:
       
  1238             message = wx.MessageDialog(self, _("A location must be selected!"), _("Error"), wx.OK|wx.ICON_ERROR)
       
  1239             message.ShowModal()
       
  1240             message.Destroy()
       
  1241         else:
       
  1242             self.EndModal(wx.ID_OK)