diff -r 1460273f40ed -r 5743cbdff669 editors/DataTypeEditor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/editors/DataTypeEditor.py Fri Sep 07 16:45:55 2012 +0200 @@ -0,0 +1,791 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor +#based on the plcopen standard. +# +#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +#See COPYING file for copyrights details. +# +#This library is free software; you can redistribute it and/or +#modify it under the terms of the GNU General Public +#License as published by the Free Software Foundation; either +#version 2.1 of the License, or (at your option) any later version. +# +#This library is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +#General Public License for more details. +# +#You should have received a copy of the GNU General Public +#License along with this library; if not, write to the Free Software +#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import re + +import wx +import wx.grid +import wx.lib.buttons + +from plcopen.structures import IEC_KEYWORDS, TestIdentifier +from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD +from controls import CustomEditableListBox, CustomGrid, CustomTable +from EditorPanel import EditorPanel +from util.BitmapLibrary import GetBitmap + +#------------------------------------------------------------------------------- +# Helpers +#------------------------------------------------------------------------------- + +DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$") + +def AppendMenu(parent, help, id, kind, text): + parent.Append(help=help, id=id, kind=kind, text=text) + +def GetElementsTableColnames(): + _ = lambda x : x + return ["#", _("Name"), _("Type"), _("Initial Value")] + +def GetDatatypeTypes(): + _ = lambda x : x + return [_("Directly"), _("Subrange"), _("Enumerated"), _("Array"), _("Structure")] +DATATYPE_TYPES_DICT = dict([(_(datatype), datatype) for datatype in GetDatatypeTypes()]) + +#------------------------------------------------------------------------------- +# Structure Elements Table +#------------------------------------------------------------------------------- + +class ElementsTable(CustomTable): + + """ + A custom wx.grid.Grid Table using user supplied data + """ + def __init__(self, parent, data, colnames): + # The base class must be initialized *first* + CustomTable.__init__(self, parent, data, colnames) + self.old_value = None + + def GetValue(self, row, col): + if row < self.GetNumberRows(): + if col == 0: + return row + 1 + name = str(self.data[row].get(self.GetColLabelValue(col, False), "")) + return name + + def SetValue(self, row, col, value): + if col < len(self.colnames): + colname = self.GetColLabelValue(col, False) + if colname == "Name": + self.old_value = self.data[row][colname] + self.data[row][colname] = value + + def GetOldValue(self): + return self.old_value + + def _updateColAttrs(self, grid): + """ + wx.grid.Grid -> update the column attributes to add the + appropriate renderer given the column name. + + Otherwise default to the default renderer. + """ + + for row in range(self.GetNumberRows()): + row_highlights = self.Highlights.get(row, {}) + for col in range(self.GetNumberCols()): + editor = None + renderer = None + colname = self.GetColLabelValue(col, False) + if col != 0: + grid.SetReadOnly(row, col, False) + if colname == "Name": + editor = wx.grid.GridCellTextEditor() + renderer = wx.grid.GridCellStringRenderer() + elif colname == "Initial Value": + editor = wx.grid.GridCellTextEditor() + renderer = wx.grid.GridCellStringRenderer() + elif colname == "Type": + editor = wx.grid.GridCellTextEditor() + else: + grid.SetReadOnly(row, col, True) + + grid.SetCellEditor(row, col, editor) + grid.SetCellRenderer(row, col, renderer) + + highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] + grid.SetCellBackgroundColour(row, col, highlight_colours[0]) + grid.SetCellTextColour(row, col, highlight_colours[1]) + self.ResizeRow(grid, row) + + def AddHighlight(self, infos, highlight_type): + row_highlights = self.Highlights.setdefault(infos[0], {}) + if infos[1] == "initial": + col_highlights = row_highlights.setdefault("initial value", []) + else: + col_highlights = row_highlights.setdefault(infos[1], []) + col_highlights.append(highlight_type) + +#------------------------------------------------------------------------------- +# Datatype Editor class +#------------------------------------------------------------------------------- + +class DataTypeEditor(EditorPanel): + + def _init_Editor(self, parent): + self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER) + + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) + self.MainSizer.AddGrowableCol(0) + self.MainSizer.AddGrowableRow(1) + + top_sizer = wx.BoxSizer(wx.HORIZONTAL) + self.MainSizer.AddSizer(top_sizer, border=5, + flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + + derivation_type_label = wx.StaticText(self.Editor, label=_('Derivation Type:')) + top_sizer.AddWindow(derivation_type_label, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT) + + self.DerivationType = wx.ComboBox(self.Editor, + size=wx.Size(200, -1), style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnDerivationTypeChanged, self.DerivationType) + top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW|wx.RIGHT) + + typeinfos_staticbox = wx.StaticBox(self.Editor, label=_('Type infos:')) + typeinfos_sizer = wx.StaticBoxSizer(typeinfos_staticbox, wx.HORIZONTAL) + self.MainSizer.AddSizer(typeinfos_sizer, border=5, + flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) + + # Panel for Directly derived data types + + self.DirectlyPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) + typeinfos_sizer.AddWindow(self.DirectlyPanel, 1) + + directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) + + directly_basetype_label = wx.StaticText(self.DirectlyPanel, + label=_('Base Type:')) + directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.DirectlyBaseType = wx.ComboBox(self.DirectlyPanel, style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.DirectlyPanel) + directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5, + flag=wx.GROW|wx.ALL) + + directly_initialvalue_label = wx.StaticText(self.DirectlyPanel, + label=_('Initial Value:')) + directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, + style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH) + self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.DirectlyInitialValue) + directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5, + flag=wx.ALL) + + self.DirectlyPanel.SetSizer(directly_panel_sizer) + + # Panel for Subrange data types + + self.SubrangePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) + typeinfos_sizer.AddWindow(self.SubrangePanel, 1) + + subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0) + + subrange_basetype_label = wx.StaticText(self.SubrangePanel, + label=_('Base Type:')) + subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.SubrangeBaseType = wx.ComboBox(self.SubrangePanel, style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged, + self.SubrangeBaseType) + subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5, + flag=wx.GROW|wx.ALL) + + subrange_initialvalue_label = wx.StaticText(self.SubrangePanel, + label=_('Initial Value:')) + subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel, + style=wx.TAB_TRAVERSAL) + self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, self.SubrangeInitialValue) + subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5, + flag=wx.GROW|wx.ALL) + + subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:')) + subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.SubrangeMinimum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) + self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMinimumChanged, self.SubrangeMinimum) + subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5, + flag=wx.GROW|wx.ALL) + + for i in xrange(2): + subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1) + + subrange_maximum_label = wx.StaticText(self.SubrangePanel, + label=_('Maximum:')) + subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.SubrangeMaximum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) + self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMaximumChanged, self.SubrangeMaximum) + + subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5, + flag=wx.GROW|wx.ALL) + + self.SubrangePanel.SetSizer(subrange_panel_sizer) + + # Panel for Enumerated data types + + self.EnumeratedPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) + typeinfos_sizer.AddWindow(self.EnumeratedPanel, 1) + + enumerated_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) + + self.EnumeratedValues = CustomEditableListBox(self.EnumeratedPanel, + label=_("Values:"), style=wx.gizmos.EL_ALLOW_NEW| + wx.gizmos.EL_ALLOW_EDIT| + wx.gizmos.EL_ALLOW_DELETE) + setattr(self.EnumeratedValues, "_OnLabelEndEdit", self.OnEnumeratedValueEndEdit) + for func in ["_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]: + setattr(self.EnumeratedValues, func, self.OnEnumeratedValuesChanged) + enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5, + flag=wx.GROW|wx.ALL) + + enumerated_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) + enumerated_panel_sizer.AddSizer(enumerated_panel_rightsizer, 1) + + enumerated_initialvalue_label = wx.StaticText(self.EnumeratedPanel, + label=_('Initial Value:')) + enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, + border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, + style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue) + enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1, + border=5, flag=wx.ALL) + + self.EnumeratedPanel.SetSizer(enumerated_panel_sizer) + + # Panel for Array data types + + self.ArrayPanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) + typeinfos_sizer.AddWindow(self.ArrayPanel, 1) + + array_panel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=0) + array_panel_sizer.AddGrowableCol(0) + array_panel_sizer.AddGrowableCol(1) + array_panel_sizer.AddGrowableRow(1) + + array_panel_leftSizer = wx.BoxSizer(wx.HORIZONTAL) + array_panel_sizer.AddSizer(array_panel_leftSizer, flag=wx.GROW) + + array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:')) + array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.ArrayBaseType = wx.ComboBox(self.ArrayPanel, style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.ArrayBaseType) + array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5, + flag=wx.GROW|wx.ALL) + + array_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) + array_panel_sizer.AddSizer(array_panel_rightsizer, flag=wx.GROW) + + array_initialvalue_label = wx.StaticText(self.ArrayPanel, + label=_('Initial Value:')) + array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, + flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + + self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel, + style=wx.TAB_TRAVERSAL|wx.TE_PROCESS_ENTER|wx.TE_RICH) + self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.ArrayInitialValue) + array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5, + flag=wx.ALL) + + self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel, + label=_("Dimensions:"), style=wx.gizmos.EL_ALLOW_NEW| + wx.gizmos.EL_ALLOW_EDIT| + wx.gizmos.EL_ALLOW_DELETE) + for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", + "_OnUpButton", "_OnDownButton"]: + setattr(self.ArrayDimensions, func, self.OnDimensionsChanged) + array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5, + flag=wx.GROW|wx.ALL) + + self.ArrayPanel.SetSizer(array_panel_sizer) + + # Panel for Structure data types + + self.StructurePanel = wx.Panel(self.Editor, style=wx.TAB_TRAVERSAL) + typeinfos_sizer.AddWindow(self.StructurePanel, 1) + + structure_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) + structure_panel_sizer.AddGrowableCol(0) + structure_panel_sizer.AddGrowableRow(1) + + structure_button_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) + structure_button_sizer.AddGrowableCol(0) + structure_button_sizer.AddGrowableRow(0) + structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5, + flag=wx.ALL|wx.GROW) + + structure_elements_label = wx.StaticText(self.StructurePanel, + label=_('Elements :')) + structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM) + + for name, bitmap, help in [ + ("StructureAddButton", "add_element", _("Add element")), + ("StructureDeleteButton", "remove_element", _("Remove element")), + ("StructureUpButton", "up", _("Move element up")), + ("StructureDownButton", "down", _("Move element down"))]: + button = wx.lib.buttons.GenBitmapButton(self.StructurePanel, + bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + button.SetToolTipString(help) + setattr(self, name, button) + structure_button_sizer.AddWindow(button) + + self.StructureElementsGrid = CustomGrid(self.StructurePanel, + size=wx.Size(0, 150), style=wx.VSCROLL) + self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.OnStructureElementsGridCellChange) + self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, + self.OnStructureElementsGridEditorShown) + structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW) + + self.StructurePanel.SetSizer(structure_panel_sizer) + + self.Editor.SetSizer(self.MainSizer) + + def __init__(self, parent, tagname, window, controler): + EditorPanel.__init__(self, parent, tagname, window, controler) + + self.StructureElementDefaultValue = {"Name" : "", "Type" : "INT", "Initial Value" : ""} + self.StructureElementsTable = ElementsTable(self, [], GetElementsTableColnames()) + self.StructureColSizes = [40, 150, 100, 250] + self.StructureColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT] + + self.StructureElementsGrid.SetTable(self.StructureElementsTable) + self.StructureElementsGrid.SetButtons({"Add": self.StructureAddButton, + "Delete": self.StructureDeleteButton, + "Up": self.StructureUpButton, + "Down": self.StructureDownButton}) + + def _AddStructureElement(new_row): + self.StructureElementsTable.InsertRow(new_row, self.StructureElementDefaultValue.copy()) + self.RefreshTypeInfos() + self.StructureElementsTable.ResetView(self.StructureElementsGrid) + return new_row + setattr(self.StructureElementsGrid, "_AddRow", _AddStructureElement) + + def _DeleteStructureElement(row): + self.StructureElementsTable.RemoveRow(row) + self.RefreshTypeInfos() + self.StructureElementsTable.ResetView(self.StructureElementsGrid) + setattr(self.StructureElementsGrid, "_DeleteRow", _DeleteStructureElement) + + def _MoveStructureElement(row, move): + new_row = self.StructureElementsTable.MoveRow(row, move) + if new_row != row: + self.RefreshTypeInfos() + self.StructureElementsTable.ResetView(self.StructureElementsGrid) + return new_row + setattr(self.StructureElementsGrid, "_MoveRow", _MoveStructureElement) + + self.StructureElementsGrid.SetRowLabelSize(0) + for col in range(self.StructureElementsTable.GetNumberCols()): + attr = wx.grid.GridCellAttr() + attr.SetAlignment(self.StructureColAlignements[col], wx.ALIGN_CENTRE) + self.StructureElementsGrid.SetColAttr(col, attr) + self.StructureElementsGrid.SetColMinimalWidth(col, self.StructureColSizes[col]) + self.StructureElementsGrid.AutoSizeColumn(col, False) + self.StructureElementsGrid.RefreshButtons() + + for datatype in GetDatatypeTypes(): + self.DerivationType.Append(_(datatype)) + self.SubrangePanel.Hide() + self.EnumeratedPanel.Hide() + self.ArrayPanel.Hide() + self.StructurePanel.Hide() + self.CurrentPanel = "Directly" + self.Highlights = [] + self.Initializing = False + + self.HighlightControls = { + ("Directly", "base"): self.DirectlyBaseType, + ("Directly", "initial"): self.DirectlyInitialValue, + ("Subrange", "base"): self.SubrangeBaseType, + ("Subrange", "lower"): self.SubrangeMinimum, + ("Subrange", "upper"): self.SubrangeMaximum, + ("Subrange", "initial"): self.SubrangeInitialValue, + ("Enumerated", "value"): self.EnumeratedValues, + ("Enumerated", "initial"): self.EnumeratedInitialValue, + ("Array", "initial"): self.ArrayInitialValue, + ("Array", "base"): self.ArrayBaseType, + ("Array", "range"): self.ArrayDimensions, + } + + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) + + def __del__(self): + self.RefreshHighlightsTimer.Stop() + + def GetBufferState(self): + return self.Controler.GetBufferState() + + def Undo(self): + self.Controler.LoadPrevious() + self.ParentWindow.CloseTabsWithoutModel() + + def Redo(self): + self.Controler.LoadNext() + self.ParentWindow.CloseTabsWithoutModel() + + def HasNoModel(self): + return self.Controler.GetEditedElement(self.TagName) is None + + def RefreshView(self): + self.Initializing = True + self.DirectlyBaseType.Clear() + self.ArrayBaseType.Clear() + for datatype in self.Controler.GetDataTypes(self.TagName): + self.DirectlyBaseType.Append(datatype) + self.ArrayBaseType.Append(datatype) + self.DirectlyBaseType.SetSelection(0) + self.SubrangeBaseType.Clear() + words = self.TagName.split("::") + for base_type in self.Controler.GetSubrangeBaseTypes(words[1]): + self.SubrangeBaseType.Append(base_type) + self.SubrangeBaseType.SetSelection(0) + self.RefreshBoundsRange() + type_infos = self.Controler.GetDataTypeInfos(self.TagName) + if type_infos is not None: + datatype = type_infos["type"] + self.DerivationType.SetStringSelection(_(datatype)) + if type_infos["type"] == "Directly": + self.DirectlyBaseType.SetStringSelection(type_infos["base_type"]) + self.DirectlyInitialValue.SetValue(type_infos["initial"]) + elif type_infos["type"] == "Subrange": + self.SubrangeBaseType.SetStringSelection(type_infos["base_type"]) + self.RefreshBoundsRange() + self.SubrangeMinimum.SetValue(int(type_infos["min"])) + self.SubrangeMaximum.SetValue(int(type_infos["max"])) + self.RefreshSubrangeInitialValueRange() + if type_infos["initial"] != "": + self.SubrangeInitialValue.SetValue(int(type_infos["initial"])) + else: + self.SubrangeInitialValue.SetValue(type_infos["min"]) + elif type_infos["type"] == "Enumerated": + self.EnumeratedValues.SetStrings(type_infos["values"]) + self.RefreshEnumeratedValues() + self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"]) + elif type_infos["type"] == "Array": + self.ArrayBaseType.SetStringSelection(type_infos["base_type"]) + self.ArrayDimensions.SetStrings(map(lambda x : "..".join(x), type_infos["dimensions"])) + self.ArrayInitialValue.SetValue(type_infos["initial"]) + elif type_infos["type"] == "Structure": + self.StructureElementsTable.SetData(type_infos["elements"]) + self.RefreshDisplayedInfos() + self.ShowHighlights() + self.StructureElementsTable.ResetView(self.StructureElementsGrid) + self.StructureElementsGrid.RefreshButtons() + self.Initializing = False + + def OnDerivationTypeChanged(self, event): + wx.CallAfter(self.RefreshDisplayedInfos) + wx.CallAfter(self.RefreshTypeInfos) + event.Skip() + + def OnReturnKeyPressed(self, event): + self.RefreshTypeInfos() + + def OnInfosChanged(self, event): + self.RefreshTypeInfos() + event.Skip() + + def OnSubrangeBaseTypeChanged(self, event): + self.RefreshBoundsRange() + self.RefreshTypeInfos() + event.Skip() + + def OnSubrangeMinimumChanged(self, event): + if not self.Initializing: + wx.CallAfter(self.SubrangeMinimum.SetValue, min(self.SubrangeMaximum.GetValue(), self.SubrangeMinimum.GetValue())) + wx.CallAfter(self.RefreshSubrangeInitialValueRange) + wx.CallAfter(self.RefreshTypeInfos) + event.Skip() + + def OnSubrangeMaximumChanged(self, event): + if not self.Initializing: + wx.CallAfter(self.SubrangeMaximum.SetValue, max(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue())) + wx.CallAfter(self.RefreshSubrangeInitialValueRange) + wx.CallAfter(self.RefreshTypeInfos) + event.Skip() + + def OnDimensionsChanged(self, event): + wx.CallAfter(self.RefreshTypeInfos) + event.Skip() + + def OnEnumeratedValueEndEdit(self, event): + text = event.GetText() + values = self.EnumeratedValues.GetStrings() + index = event.GetIndex() + if index >= len(values) or values[index].upper() != text.upper(): + if text.upper() in [value.upper() for value in values]: + message = wx.MessageDialog(self, _("\"%s\" value already defined!")%text, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + event.Veto() + elif text.upper() in IEC_KEYWORDS: + message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%text, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + else: + initial_selected = None + if index < len(values) and self.EnumeratedInitialValue.GetStringSelection() == values[index]: + initial_selected = text + wx.CallAfter(self.RefreshEnumeratedValues, initial_selected) + wx.CallAfter(self.RefreshTypeInfos) + event.Skip() + else: + event.Skip() + + def OnEnumeratedValuesChanged(self, event): + wx.CallAfter(self.RefreshEnumeratedValues) + wx.CallAfter(self.RefreshTypeInfos) + event.Skip() + + def OnStructureElementsGridCellChange(self, event): + row, col = event.GetRow(), event.GetCol() + colname = self.StructureElementsTable.GetColLabelValue(col) + value = self.StructureElementsTable.GetValue(row, col) + if colname == "Name": + if not TestIdentifier(value): + message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%value, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + event.Veto() + elif value.upper() in IEC_KEYWORDS: + message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%value, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + event.Veto() +## elif value.upper() in self.PouNames: +## message = wx.MessageDialog(self, "A pou with \"%s\" as name exists!"%value, "Error", wx.OK|wx.ICON_ERROR) +## message.ShowModal() +## message.Destroy() +## event.Veto() + elif value.upper() in [var["Name"].upper() for idx, var in enumerate(self.StructureElementsTable.GetData()) if idx != row]: + message = wx.MessageDialog(self, _("An element named \"%s\" already exists in this structure!")%value, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + event.Veto() + else: + self.RefreshTypeInfos() + wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid) +## old_value = self.Table.GetOldValue() +## if old_value != "": +## self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) +## self.Controler.BufferProject() + event.Skip() + else: + self.RefreshTypeInfos() + wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid) + event.Skip() + + def OnStructureElementsGridSelectCell(self, event): + wx.CallAfter(self.RefreshStructureButtons) + event.Skip() + + def OnStructureElementsGridEditorShown(self, event): + row, col = event.GetRow(), event.GetCol() + if self.StructureElementsTable.GetColLabelValue(col) == "Type": + type_menu = wx.Menu(title='') + base_menu = wx.Menu(title='') + for base_type in self.Controler.GetBaseTypes(): + new_id = wx.NewId() + AppendMenu(base_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type) + self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(base_type), id=new_id) + type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu) + datatype_menu = wx.Menu(title='') + for datatype in self.Controler.GetDataTypes(self.TagName, False): + new_id = wx.NewId() + AppendMenu(datatype_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) + self.Bind(wx.EVT_MENU, self.GetElementTypeFunction(datatype), id=new_id) + type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu) +## functionblock_menu = wx.Menu(title='') +## bodytype = self.Controler.GetEditedElementBodyType(self.TagName) +## pouname, poutype = self.Controler.GetEditedElementType(self.TagName) +## if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]: +## for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName): +## new_id = wx.NewId() +## AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type) +## self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id) +## type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) + rect = self.StructureElementsGrid.BlockToDeviceRect((row, col), (row, col)) + self.StructureElementsGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize()) + type_menu.Destroy() + event.Veto() + else: + event.Skip() + + def GetElementTypeFunction(self, base_type): + def ElementTypeFunction(event): + row = self.StructureElementsGrid.GetGridCursorRow() + self.StructureElementsTable.SetValueByName(row, "Type", base_type) + self.RefreshTypeInfos() + self.StructureElementsTable.ResetView(self.StructureElementsGrid) + return ElementTypeFunction + + def RefreshDisplayedInfos(self): + selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] + if selected != self.CurrentPanel: + if self.CurrentPanel == "Directly": + self.DirectlyPanel.Hide() + elif self.CurrentPanel == "Subrange": + self.SubrangePanel.Hide() + elif self.CurrentPanel == "Enumerated": + self.EnumeratedPanel.Hide() + elif self.CurrentPanel == "Array": + self.ArrayPanel.Hide() + elif self.CurrentPanel == "Structure": + self.StructurePanel.Hide() + self.CurrentPanel = selected + if selected == "Directly": + self.DirectlyPanel.Show() + elif selected == "Subrange": + self.SubrangePanel.Show() + elif selected == "Enumerated": + self.EnumeratedPanel.Show() + elif selected == "Array": + self.ArrayPanel.Show() + elif selected == "Structure": + self.StructurePanel.Show() + self.MainSizer.Layout() + + def RefreshEnumeratedValues(self, initial_selected=None): + if initial_selected is None: + initial_selected = self.EnumeratedInitialValue.GetStringSelection() + self.EnumeratedInitialValue.Clear() + self.EnumeratedInitialValue.Append("") + for value in self.EnumeratedValues.GetStrings(): + self.EnumeratedInitialValue.Append(value) + self.EnumeratedInitialValue.SetStringSelection(initial_selected) + + def RefreshBoundsRange(self): + range = self.Controler.GetDataTypeRange(self.SubrangeBaseType.GetStringSelection()) + if range is not None: + min_value, max_value = range + self.SubrangeMinimum.SetRange(min_value, max_value) + self.SubrangeMinimum.SetValue(min(max(min_value, self.SubrangeMinimum.GetValue()), max_value)) + self.SubrangeMaximum.SetRange(min_value, max_value) + self.SubrangeMaximum.SetValue(min(max(min_value, self.SubrangeMaximum.GetValue()), max_value)) + + def RefreshSubrangeInitialValueRange(self): + self.SubrangeInitialValue.SetRange(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue()) + + def RefreshTypeInfos(self): + selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] + infos = {"type" : selected} + if selected == "Directly": + infos["base_type"] = self.DirectlyBaseType.GetStringSelection() + infos["initial"] = self.DirectlyInitialValue.GetValue() + elif selected == "Subrange": + infos["base_type"] = self.SubrangeBaseType.GetStringSelection() + infos["min"] = str(self.SubrangeMinimum.GetValue()) + infos["max"] = str(self.SubrangeMaximum.GetValue()) + initial_value = self.SubrangeInitialValue.GetValue() + if initial_value == infos["min"]: + infos["initial"] = "" + else: + infos["initial"] = str(initial_value) + elif selected == "Enumerated": + infos["values"] = self.EnumeratedValues.GetStrings() + infos["initial"] = self.EnumeratedInitialValue.GetStringSelection() + elif selected == "Array": + infos["base_type"] = self.ArrayBaseType.GetStringSelection() + infos["dimensions"] = [] + for dimensions in self.ArrayDimensions.GetStrings(): + result = DIMENSION_MODEL.match(dimensions) + if result is None: + message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + self.RefreshView() + return + bounds = result.groups() + if int(bounds[0]) >= int(bounds[1]): + message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + self.RefreshView() + return + infos["dimensions"].append(bounds) + infos["initial"] = self.ArrayInitialValue.GetValue() + elif selected == "Structure": + infos["elements"] = self.StructureElementsTable.GetData() + infos["initial"] = "" + self.Controler.SetDataTypeInfos(self.TagName, infos) + self.ParentWindow.RefreshTitle() + self.ParentWindow.RefreshFileMenu() + self.ParentWindow.RefreshEditMenu() + +#------------------------------------------------------------------------------- +# Highlights showing functions +#------------------------------------------------------------------------------- + + def OnRefreshHighlightsTimer(self, event): + self.RefreshView() + event.Skip() + + def ClearHighlights(self, highlight_type=None): + if highlight_type is None: + self.Highlights = [] + else: + self.Highlights = [(infos, start, end, highlight) for (infos, start, end, highlight) in self.Highlights if highlight != highlight_type] + for control in self.HighlightControls.itervalues(): + if isinstance(control, (wx.ComboBox, wx.SpinCtrl)): + control.SetBackgroundColour(wx.NullColour) + control.SetForegroundColour(wx.NullColour) + elif isinstance(control, wx.TextCtrl): + value = control.GetValue() + control.SetStyle(0, len(value), wx.TextAttr(wx.NullColour)) + elif isinstance(control, wx.gizmos.EditableListBox): + listctrl = control.GetListCtrl() + for i in xrange(listctrl.GetItemCount()): + listctrl.SetItemBackgroundColour(i, wx.NullColour) + listctrl.SetItemTextColour(i, wx.NullColour) + self.StructureElementsTable.ClearHighlights(highlight_type) + self.RefreshView() + + def AddHighlight(self, infos, start, end ,highlight_type): + self.Highlights.append((infos, start, end, highlight_type)) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ShowHighlights(self): + type_infos = self.Controler.GetDataTypeInfos(self.TagName) + for infos, start, end, highlight_type in self.Highlights: + if infos[0] == "struct": + self.StructureElementsTable.AddHighlight(infos[1:], highlight_type) + else: + control = self.HighlightControls.get((type_infos["type"], infos[0]), None) + if control is not None: + if isinstance(control, (wx.ComboBox, wx.SpinCtrl)): + control.SetBackgroundColour(highlight_type[0]) + control.SetForegroundColour(highlight_type[1]) + elif isinstance(control, wx.TextCtrl): + control.SetStyle(start[1], end[1] + 1, wx.TextAttr(highlight_type[1], highlight_type[0])) + elif isinstance(control, wx.gizmos.EditableListBox): + listctrl = control.GetListCtrl() + listctrl.SetItemBackgroundColour(infos[1], highlight_type[0]) + listctrl.SetItemTextColour(infos[1], highlight_type[1]) + listctrl.Select(listctrl.FocusedItem, False) +