andrej@1511: #!/usr/bin/env python andrej@1511: # -*- coding: utf-8 -*- andrej@1511: andrej@1511: # This file is part of Beremiz, a Integrated Development Environment for andrej@1511: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1511: # andrej@1511: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD andrej@1511: # andrej@1511: # See COPYING file for copyrights details. andrej@1511: # andrej@1511: # This program is free software; you can redistribute it and/or andrej@1511: # modify it under the terms of the GNU General Public License andrej@1511: # as published by the Free Software Foundation; either version 2 andrej@1511: # of the License, or (at your option) any later version. andrej@1511: # andrej@1511: # This program is distributed in the hope that it will be useful, andrej@1511: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1511: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1511: # GNU General Public License for more details. andrej@1511: # andrej@1511: # You should have received a copy of the GNU General Public License andrej@1511: # along with this program; if not, write to the Free Software andrej@1511: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. laurent@738: laurent@738: import os laurent@738: import types laurent@738: laurent@738: import wx laurent@738: Laurent@814: from EditorPanel import EditorPanel Laurent@814: Laurent@814: from IDEFrame import TITLE, FILEMENU, PROJECTTREE, PAGETITLES Laurent@814: Laurent@814: from controls import TextCtrlAutoComplete Laurent@814: from dialogs import BrowseValuesLibraryDialog Laurent@814: from util.BitmapLibrary import GetBitmap laurent@738: laurent@738: if wx.Platform == '__WXMSW__': laurent@738: faces = { 'times': 'Times New Roman', laurent@738: 'mono' : 'Courier New', laurent@738: 'helv' : 'Arial', laurent@738: 'other': 'Comic Sans MS', laurent@738: 'size' : 16, laurent@738: } laurent@738: else: laurent@738: faces = { 'times': 'Times', laurent@738: 'mono' : 'Courier', laurent@738: 'helv' : 'Helvetica', laurent@738: 'other': 'new century schoolbook', laurent@738: 'size' : 18, laurent@738: } laurent@738: laurent@738: SCROLLBAR_UNIT = 10 laurent@738: laurent@738: class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton): laurent@738: def _GetLabelSize(self): laurent@738: """ used internally """ laurent@738: w, h = self.GetTextExtent(self.GetLabel()) laurent@738: if not self.bmpLabel: laurent@738: return w, h, False # if there isn't a bitmap use the size of the text laurent@738: laurent@738: w_bmp = self.bmpLabel.GetWidth()+2 laurent@738: h_bmp = self.bmpLabel.GetHeight()+2 laurent@738: height = h + h_bmp laurent@738: if w_bmp > w: laurent@738: width = w_bmp laurent@738: else: laurent@738: width = w laurent@738: return width, height, False laurent@738: laurent@738: def DrawLabel(self, dc, width, height, dw=0, dy=0): laurent@738: bmp = self.bmpLabel laurent@738: if bmp != None: # if the bitmap is used laurent@738: if self.bmpDisabled and not self.IsEnabled(): laurent@738: bmp = self.bmpDisabled laurent@738: if self.bmpFocus and self.hasFocus: laurent@738: bmp = self.bmpFocus laurent@738: if self.bmpSelected and not self.up: laurent@738: bmp = self.bmpSelected laurent@738: bw,bh = bmp.GetWidth(), bmp.GetHeight() laurent@738: if not self.up: laurent@738: dw = dy = self.labelDelta laurent@738: hasMask = bmp.GetMask() != None laurent@738: else: laurent@738: bw = bh = 0 # no bitmap -> size is zero laurent@738: laurent@738: dc.SetFont(self.GetFont()) laurent@738: if self.IsEnabled(): laurent@738: dc.SetTextForeground(self.GetForegroundColour()) laurent@738: else: laurent@738: dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)) laurent@738: laurent@738: label = self.GetLabel() laurent@738: tw, th = dc.GetTextExtent(label) # size of text laurent@738: if not self.up: laurent@738: dw = dy = self.labelDelta laurent@738: laurent@738: pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre laurent@738: pos_y = (height-bh-th)/2+dy laurent@738: if bmp !=None: laurent@738: dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available laurent@738: pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre laurent@738: pos_y += bh + 2 laurent@738: laurent@738: dc.DrawText(label, pos_x, pos_y) # draw the text laurent@738: laurent@738: Laurent@1162: class GenStaticBitmap(wx.StaticBitmap): Edouard@1451: """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32, laurent@738: and accept image name as __init__ parameter, fail silently if file do not exist""" laurent@738: def __init__(self, parent, ID, bitmapname, laurent@738: pos = wx.DefaultPosition, size = wx.DefaultSize, laurent@738: style = 0, laurent@738: name = "genstatbmp"): Edouard@1451: Laurent@1162: bitmap = GetBitmap(bitmapname) Laurent@1162: if bitmap is None: Laurent@1162: bitmap = wx.EmptyBitmap(0, 0) Edouard@1451: Edouard@1451: wx.StaticBitmap.__init__(self, parent, ID, Laurent@1162: bitmap, laurent@738: pos, size, laurent@738: style, laurent@738: name) laurent@738: laurent@738: class ConfTreeNodeEditor(EditorPanel): Edouard@1451: laurent@775: SHOW_BASE_PARAMS = True laurent@762: SHOW_PARAMS = True Laurent@920: CONFNODEEDITOR_TABS = [] Edouard@1451: laurent@781: def _init_Editor(self, parent): Laurent@920: tabs_num = len(self.CONFNODEEDITOR_TABS) Laurent@1055: if self.SHOW_PARAMS and len(self.Controler.GetParamsAttributes()) > 0: Laurent@920: tabs_num += 1 Edouard@1451: Laurent@1055: if tabs_num > 1 or self.SHOW_BASE_PARAMS: Edouard@1451: self.Editor = wx.Panel(parent, Laurent@920: style=wx.SUNKEN_BORDER|wx.SP_3D) Edouard@1451: Laurent@1055: self.MainSizer = wx.BoxSizer(wx.VERTICAL) Edouard@1451: laurent@775: if self.SHOW_BASE_PARAMS: laurent@775: baseparamseditor_sizer = wx.BoxSizer(wx.HORIZONTAL) Edouard@1451: self.MainSizer.AddSizer(baseparamseditor_sizer, border=5, Laurent@1055: flag=wx.GROW|wx.ALL) Edouard@1451: Laurent@1055: self.FullIECChannel = wx.StaticText(self.Editor, -1) laurent@775: self.FullIECChannel.SetFont( Edouard@1451: wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, laurent@775: wx.BOLD, faceName = faces["helv"])) Edouard@1451: baseparamseditor_sizer.AddWindow(self.FullIECChannel, laurent@781: flag=wx.ALIGN_CENTER_VERTICAL) Edouard@1451: laurent@775: updownsizer = wx.BoxSizer(wx.VERTICAL) Edouard@1451: baseparamseditor_sizer.AddSizer(updownsizer, border=5, laurent@781: flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) Edouard@1451: Edouard@1451: self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton(self.Editor, laurent@781: bitmap=GetBitmap('IECCDown'), size=wx.Size(16, 16), style=wx.NO_BORDER) Edouard@1451: self.IECCUpButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(1), laurent@781: self.IECCUpButton) laurent@781: updownsizer.AddWindow(self.IECCUpButton, flag=wx.ALIGN_LEFT) Edouard@1451: Edouard@1451: self.IECCDownButton = wx.lib.buttons.GenBitmapButton(self.Editor, laurent@781: bitmap=GetBitmap('IECCUp'), size=wx.Size(16, 16), style=wx.NO_BORDER) Edouard@1451: self.IECCDownButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(-1), laurent@781: self.IECCDownButton) laurent@781: updownsizer.AddWindow(self.IECCDownButton, flag=wx.ALIGN_LEFT) Edouard@1451: Edouard@1451: self.ConfNodeName = wx.TextCtrl(self.Editor, Laurent@1162: size=wx.Size(150, 25)) laurent@775: self.ConfNodeName.SetFont( Edouard@1451: wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, laurent@775: wx.BOLD, faceName = faces["helv"])) Edouard@1451: self.ConfNodeName.Bind(wx.EVT_TEXT, Edouard@1451: self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name", True), laurent@781: self.ConfNodeName) Edouard@1451: baseparamseditor_sizer.AddWindow(self.ConfNodeName, border=5, laurent@781: flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) Edouard@1451: laurent@775: buttons_sizer = self.GenerateMethodButtonSizer() laurent@781: baseparamseditor_sizer.AddSizer(buttons_sizer, flag=wx.ALIGN_CENTER) Edouard@1451: Laurent@1055: if tabs_num > 1: Laurent@1055: self.ConfNodeNoteBook = wx.Notebook(self.Editor) Laurent@1055: parent = self.ConfNodeNoteBook Laurent@1055: self.MainSizer.AddWindow(self.ConfNodeNoteBook, 1, flag=wx.GROW) laurent@775: else: Laurent@1059: parent = self.Editor Laurent@1055: self.ConfNodeNoteBook = None Edouard@1451: Laurent@1055: self.Editor.SetSizer(self.MainSizer) Laurent@1055: else: Laurent@1055: self.ConfNodeNoteBook = None Laurent@1055: self.Editor = None Edouard@1451: Laurent@1055: for title, create_func_name in self.CONFNODEEDITOR_TABS: Laurent@1055: editor = getattr(self, create_func_name)(parent) Laurent@1055: if self.ConfNodeNoteBook is not None: Laurent@1055: self.ConfNodeNoteBook.AddPage(editor, title) Laurent@1055: elif self.SHOW_BASE_PARAMS: Laurent@1055: self.MainSizer.AddWindow(editor, 1, flag=wx.GROW) Laurent@1055: else: Laurent@1055: self.Editor = editor Edouard@1451: Laurent@1055: if self.SHOW_PARAMS and len(self.Controler.GetParamsAttributes()) > 0: Edouard@1451: Laurent@1055: panel_style = wx.TAB_TRAVERSAL|wx.HSCROLL|wx.VSCROLL Laurent@1059: if self.ConfNodeNoteBook is None and parent != self.Editor: Laurent@1055: panel_style |= wx.SUNKEN_BORDER Edouard@1451: self.ParamsEditor = wx.ScrolledWindow(parent, Laurent@1055: style=panel_style) Laurent@1180: self.ParamsEditor.Bind(wx.EVT_SIZE, self.OnParamsEditorResize) Laurent@1180: self.ParamsEditor.Bind(wx.EVT_SCROLLWIN, self.OnParamsEditorScroll) Edouard@1451: Laurent@1055: self.ParamsEditorSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=1, vgap=5) Laurent@1055: self.ParamsEditorSizer.AddGrowableCol(0) Laurent@1055: self.ParamsEditorSizer.AddGrowableRow(0) Laurent@1055: self.ParamsEditor.SetSizer(self.ParamsEditorSizer) Edouard@1451: laurent@762: self.ConfNodeParamsSizer = wx.BoxSizer(wx.VERTICAL) Edouard@1451: self.ParamsEditorSizer.AddSizer(self.ConfNodeParamsSizer, border=5, laurent@781: flag=wx.LEFT|wx.RIGHT|wx.BOTTOM) Edouard@1451: laurent@775: self.RefreshConfNodeParamsSizer() Edouard@1451: Laurent@920: if self.ConfNodeNoteBook is not None: Laurent@920: self.ConfNodeNoteBook.AddPage(self.ParamsEditor, _("Config")) Laurent@1055: elif self.SHOW_BASE_PARAMS: Laurent@1055: self.MainSizer.AddWindow(self.ParamsEditor, 1, flag=wx.GROW) Laurent@920: else: Laurent@920: self.Editor = self.ParamsEditor laurent@762: else: laurent@762: self.ParamsEditor = None Edouard@1451: laurent@743: def __init__(self, parent, controler, window, tagname=""): laurent@738: EditorPanel.__init__(self, parent, tagname, window, controler) Edouard@1451: laurent@781: icon_name = self.Controler.GetIconName() laurent@781: if icon_name is not None: laurent@781: self.SetIcon(GetBitmap(icon_name)) laurent@781: else: laurent@781: self.SetIcon(GetBitmap("Extension")) Edouard@1451: laurent@738: def __del__(self): laurent@738: self.Controler.OnCloseEditor(self) Edouard@1451: laurent@738: def GetTagName(self): laurent@738: return self.Controler.CTNFullName() Edouard@1451: laurent@738: def GetTitle(self): laurent@738: fullname = self.Controler.CTNFullName() laurent@738: if self.Controler.CTNTestModified(): laurent@738: return "~%s~" % fullname laurent@738: return fullname Edouard@1451: laurent@738: def HasNoModel(self): laurent@738: return False Edouard@1451: laurent@743: def GetBufferState(self): laurent@743: return False, False Edouard@1451: laurent@743: def Undo(self): laurent@743: pass Edouard@1451: laurent@743: def Redo(self): laurent@743: pass Edouard@1451: laurent@746: def RefreshView(self): laurent@746: EditorPanel.RefreshView(self) Laurent@1055: if self.SHOW_BASE_PARAMS: Laurent@1055: self.ConfNodeName.ChangeValue(self.Controler.MandatoryParams[1].getName()) Laurent@1055: self.RefreshIECChannelControlsState() laurent@762: if self.ParamsEditor is not None: laurent@762: self.RefreshConfNodeParamsSizer() Laurent@840: self.RefreshScrollbars() Edouard@1451: laurent@738: def RefreshIECChannelControlsState(self): laurent@738: self.FullIECChannel.SetLabel(self.Controler.GetFullIEC_Channel()) laurent@738: self.IECCDownButton.Enable(self.Controler.BaseParams.getIEC_Channel() > 0) Laurent@1059: self.MainSizer.Layout() Edouard@1451: laurent@738: def RefreshConfNodeParamsSizer(self): laurent@738: self.Freeze() laurent@738: self.ConfNodeParamsSizer.Clear(True) Edouard@1451: laurent@738: confnode_infos = self.Controler.GetParamsAttributes() laurent@738: if len(confnode_infos) > 0: laurent@738: self.GenerateSizerElements(self.ConfNodeParamsSizer, confnode_infos, None, False) Edouard@1451: laurent@738: self.ParamsEditorSizer.Layout() laurent@738: self.Thaw() Edouard@1451: laurent@738: def GenerateMethodButtonSizer(self): laurent@738: normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]) laurent@738: mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"]) Edouard@1451: laurent@738: msizer = wx.BoxSizer(wx.HORIZONTAL) Edouard@1451: laurent@738: for confnode_method in self.Controler.ConfNodeMethods: laurent@738: if "method" in confnode_method and confnode_method.get("shown",True): Laurent@1055: button = GenBitmapTextButton(self.Editor, Edouard@1451: bitmap=GetBitmap(confnode_method.get("bitmap", "Unknown")), laurent@781: label=confnode_method["name"], style=wx.NO_BORDER) laurent@738: button.SetFont(normal_bt_font) laurent@738: button.SetToolTipString(confnode_method["tooltip"]) laurent@767: if confnode_method.get("push", False): laurent@767: button.Bind(wx.EVT_LEFT_DOWN, self.GetButtonCallBackFunction(confnode_method["method"], True)) laurent@767: else: laurent@781: button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(confnode_method["method"]), button) laurent@738: # a fancy underline on mouseover laurent@738: def setFontStyle(b, s): laurent@738: def fn(event): laurent@738: b.SetFont(s) laurent@738: b.Refresh() laurent@738: event.Skip() laurent@738: return fn laurent@738: button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font)) laurent@738: button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font)) laurent@738: #hack to force size to mini laurent@738: if not confnode_method.get("enabled",True): laurent@738: button.Disable() laurent@781: msizer.AddWindow(button, flag=wx.ALIGN_CENTER) laurent@738: return msizer Edouard@1451: laurent@738: def GenerateSizerElements(self, sizer, elements, path, clean = True): laurent@738: if clean: laurent@738: sizer.Clear(True) laurent@738: first = True laurent@738: for element_infos in elements: laurent@738: if path: laurent@738: element_path = "%s.%s"%(path, element_infos["name"]) laurent@738: else: laurent@738: element_path = element_infos["name"] laurent@738: if element_infos["type"] == "element": laurent@1282: name = element_infos["name"] laurent@1282: value = element_infos["value"] laurent@1282: label = _(name) laurent@1282: if value is not None: laurent@1282: label += " - %s" % _(value) Edouard@1451: staticbox = wx.StaticBox(self.ParamsEditor, laurent@781: label=_(label), size=wx.Size(10, 0)) laurent@738: staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) laurent@738: if first: Edouard@1451: sizer.AddSizer(staticboxsizer, border=5, laurent@781: flag=wx.GROW|wx.TOP|wx.BOTTOM) laurent@738: else: Edouard@1451: sizer.AddSizer(staticboxsizer, border=5, laurent@781: flag=wx.GROW|wx.BOTTOM) Edouard@1451: self.GenerateSizerElements(staticboxsizer, Edouard@1451: element_infos["children"], laurent@781: element_path) laurent@738: else: laurent@738: boxsizer = wx.FlexGridSizer(cols=3, rows=1) laurent@738: boxsizer.AddGrowableCol(1) laurent@738: if first: Edouard@1451: sizer.AddSizer(boxsizer, border=5, laurent@781: flag=wx.GROW|wx.ALL) laurent@738: else: Edouard@1451: sizer.AddSizer(boxsizer, border=5, laurent@781: flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) Edouard@1451: laurent@781: staticbitmap = GenStaticBitmap(ID=-1, bitmapname=element_infos["name"], laurent@738: name="%s_bitmap"%element_infos["name"], parent=self.ParamsEditor, laurent@738: pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0) laurent@781: boxsizer.AddWindow(staticbitmap, border=5, flag=wx.RIGHT) Edouard@1451: Edouard@1451: statictext = wx.StaticText(self.ParamsEditor, laurent@781: label="%s:"%_(element_infos["name"])) Edouard@1451: boxsizer.AddWindow(statictext, border=5, laurent@781: flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) Edouard@1451: laurent@738: if isinstance(element_infos["type"], types.ListType): laurent@738: if isinstance(element_infos["value"], types.TupleType): laurent@738: browse_boxsizer = wx.BoxSizer(wx.HORIZONTAL) laurent@781: boxsizer.AddSizer(browse_boxsizer) Edouard@1451: Edouard@1451: textctrl = wx.TextCtrl(self.ParamsEditor, laurent@846: size=wx.Size(275, -1), style=wx.TE_READONLY) laurent@738: if element_infos["value"] is not None: laurent@738: textctrl.SetValue(element_infos["value"][0]) laurent@738: value_infos = element_infos["value"][1] laurent@738: else: laurent@738: value_infos = None laurent@781: browse_boxsizer.AddWindow(textctrl) Edouard@1451: Edouard@1451: button = wx.Button(self.ParamsEditor, laurent@781: label="...", size=wx.Size(25, 25)) laurent@781: browse_boxsizer.AddWindow(button) Edouard@1451: button.Bind(wx.EVT_BUTTON, Edouard@1451: self.GetBrowseCallBackFunction(element_infos["name"], textctrl, element_infos["type"], Edouard@1451: value_infos, element_path), laurent@781: button) laurent@738: else: Edouard@1451: combobox = wx.ComboBox(self.ParamsEditor, laurent@846: size=wx.Size(300, -1), style=wx.CB_READONLY) laurent@781: boxsizer.AddWindow(combobox) Edouard@1451: laurent@738: if element_infos["use"] == "optional": laurent@738: combobox.Append("") laurent@738: if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType): laurent@738: for choice, xsdclass in element_infos["type"]: laurent@738: combobox.Append(choice) laurent@738: name = element_infos["name"] laurent@738: value = element_infos["value"] Edouard@1451: Edouard@1451: staticbox = wx.StaticBox(self.ParamsEditor, laurent@781: label="%s - %s"%(_(name), _(value)), size=wx.Size(10, 0)) laurent@738: staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) laurent@781: sizer.AddSizer(staticboxsizer, border=5, flag=wx.GROW|wx.BOTTOM) laurent@738: self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) laurent@738: callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, element_path) laurent@738: else: laurent@738: for choice in element_infos["type"]: laurent@738: combobox.Append(choice) laurent@738: callback = self.GetChoiceCallBackFunction(combobox, element_path) laurent@738: if element_infos["value"] is None: laurent@738: combobox.SetStringSelection("") laurent@738: else: laurent@738: combobox.SetStringSelection(element_infos["value"]) laurent@781: combobox.Bind(wx.EVT_COMBOBOX, callback, combobox) Edouard@1451: laurent@738: elif isinstance(element_infos["type"], types.DictType): laurent@738: scmin = -(2**31) laurent@738: scmax = 2**31-1 laurent@738: if "min" in element_infos["type"]: laurent@738: scmin = element_infos["type"]["min"] laurent@738: if "max" in element_infos["type"]: laurent@738: scmax = element_infos["type"]["max"] Edouard@1451: spinctrl = wx.SpinCtrl(self.ParamsEditor, laurent@846: size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) laurent@781: spinctrl.SetRange(scmin, scmax) laurent@781: boxsizer.AddWindow(spinctrl) laurent@738: if element_infos["value"] is not None: laurent@738: spinctrl.SetValue(element_infos["value"]) Edouard@1451: spinctrl.Bind(wx.EVT_SPINCTRL, laurent@781: self.GetTextCtrlCallBackFunction(spinctrl, element_path), laurent@781: spinctrl) Edouard@1451: laurent@738: else: laurent@738: if element_infos["type"] == "boolean": laurent@781: checkbox = wx.CheckBox(self.ParamsEditor, size=wx.Size(17, 25)) laurent@781: boxsizer.AddWindow(checkbox) laurent@738: if element_infos["value"] is not None: laurent@738: checkbox.SetValue(element_infos["value"]) Edouard@1451: checkbox.Bind(wx.EVT_CHECKBOX, Edouard@1451: self.GetCheckBoxCallBackFunction(checkbox, element_path), laurent@781: checkbox) Edouard@1451: laurent@738: elif element_infos["type"] in ["unsignedLong", "long","integer"]: laurent@738: if element_infos["type"].startswith("unsigned"): laurent@738: scmin = 0 laurent@738: else: laurent@738: scmin = -(2**31) laurent@738: scmax = 2**31-1 Edouard@1451: spinctrl = wx.SpinCtrl(self.ParamsEditor, laurent@846: size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) laurent@738: spinctrl.SetRange(scmin, scmax) laurent@781: boxsizer.AddWindow(spinctrl) laurent@738: if element_infos["value"] is not None: laurent@738: spinctrl.SetValue(element_infos["value"]) Edouard@1451: spinctrl.Bind(wx.EVT_SPINCTRL, Edouard@1451: self.GetTextCtrlCallBackFunction(spinctrl, element_path), laurent@781: spinctrl) Edouard@1451: laurent@738: else: laurent@738: choices = self.ParentWindow.GetConfigEntry(element_path, [""]) Edouard@1451: textctrl = TextCtrlAutoComplete(name=element_infos["name"], Edouard@1451: parent=self.ParamsEditor, Edouard@1451: choices=choices, laurent@781: element_path=element_path, laurent@846: size=wx.Size(300, -1)) Edouard@1451: laurent@781: boxsizer.AddWindow(textctrl) laurent@738: if element_infos["value"] is not None: laurent@738: textctrl.ChangeValue(str(element_infos["value"])) Laurent@1180: callback = self.GetTextCtrlCallBackFunction(textctrl, element_path) Laurent@1180: textctrl.Bind(wx.EVT_TEXT_ENTER, callback) Laurent@1180: textctrl.Bind(wx.EVT_KILL_FOCUS, callback) laurent@738: first = False Edouard@1451: Edouard@1451: laurent@738: def GetItemChannelChangedFunction(self, dir): laurent@738: def OnConfNodeTreeItemChannelChanged(event): laurent@738: confnode_IECChannel = self.Controler.BaseParams.getIEC_Channel() laurent@738: res = self.SetConfNodeParamsAttribute("BaseParams.IEC_Channel", confnode_IECChannel + dir) laurent@738: wx.CallAfter(self.RefreshIECChannelControlsState) laurent@738: wx.CallAfter(self.ParentWindow._Refresh, TITLE, FILEMENU, PROJECTTREE) laurent@738: event.Skip() laurent@738: return OnConfNodeTreeItemChannelChanged Edouard@1451: laurent@738: def SetConfNodeParamsAttribute(self, *args, **kwargs): laurent@738: res, StructChanged = self.Controler.SetParamsAttribute(*args, **kwargs) Laurent@1111: if StructChanged and self.ParamsEditor is not None: laurent@738: wx.CallAfter(self.RefreshConfNodeParamsSizer) laurent@738: wx.CallAfter(self.ParentWindow._Refresh, TITLE, FILEMENU) laurent@738: return res Edouard@1451: laurent@767: def GetButtonCallBackFunction(self, method, push=False): laurent@738: """ Generate the callbackfunc for a given confnode method""" laurent@738: def OnButtonClick(event): Edouard@1451: # Disable button to prevent re-entrant call laurent@738: event.GetEventObject().Disable() laurent@738: # Call laurent@738: getattr(self.Controler,method)() Edouard@1451: # Re-enable button laurent@738: event.GetEventObject().Enable() Edouard@1451: laurent@767: if not push: laurent@767: event.Skip() laurent@738: return OnButtonClick Edouard@1451: laurent@738: def GetChoiceCallBackFunction(self, choicectrl, path): laurent@738: def OnChoiceChanged(event): laurent@738: res = self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection()) Laurent@1315: if res is None: Laurent@1315: res = "" laurent@738: choicectrl.SetStringSelection(res) laurent@738: event.Skip() laurent@738: return OnChoiceChanged Edouard@1451: laurent@738: def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, path): laurent@738: def OnChoiceContentChanged(event): laurent@738: res = self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection()) laurent@746: wx.CallAfter(self.RefreshConfNodeParamsSizer) laurent@738: event.Skip() laurent@738: return OnChoiceContentChanged Edouard@1451: laurent@746: def GetTextCtrlCallBackFunction(self, textctrl, path, refresh=False): laurent@738: def OnTextCtrlChanged(event): laurent@738: res = self.SetConfNodeParamsAttribute(path, textctrl.GetValue()) laurent@738: if res != textctrl.GetValue(): laurent@844: if isinstance(textctrl, wx.SpinCtrl): laurent@844: textctrl.SetValue(res) Laurent@1179: elif res is not None: Laurent@1179: textctrl.ChangeValue(str(res)) laurent@746: if refresh: laurent@738: wx.CallAfter(self.ParentWindow._Refresh, TITLE, FILEMENU, PROJECTTREE, PAGETITLES) laurent@738: wx.CallAfter(self.ParentWindow.SelectProjectTreeItem, self.GetTagName()) laurent@738: event.Skip() laurent@738: return OnTextCtrlChanged Edouard@1451: laurent@738: def GetCheckBoxCallBackFunction(self, chkbx, path): laurent@738: def OnCheckBoxChanged(event): laurent@738: res = self.SetConfNodeParamsAttribute(path, chkbx.IsChecked()) laurent@738: chkbx.SetValue(res) laurent@738: event.Skip() laurent@738: return OnCheckBoxChanged Edouard@1451: laurent@738: def GetBrowseCallBackFunction(self, name, textctrl, library, value_infos, path): laurent@738: infos = [value_infos] laurent@738: def OnBrowseButton(event): laurent@738: dialog = BrowseValuesLibraryDialog(self, name, library, infos[0]) laurent@738: if dialog.ShowModal() == wx.ID_OK: laurent@738: value, value_infos = self.SetConfNodeParamsAttribute(path, dialog.GetValueInfos()) laurent@738: textctrl.ChangeValue(value) laurent@738: infos[0] = value_infos laurent@738: dialog.Destroy() laurent@738: event.Skip() laurent@738: return OnBrowseButton Edouard@1451: Laurent@840: def RefreshScrollbars(self): laurent@746: self.ParamsEditor.GetBestSize() laurent@738: xstart, ystart = self.ParamsEditor.GetViewStart() laurent@738: window_size = self.ParamsEditor.GetClientSize() laurent@738: maxx, maxy = self.ParamsEditorSizer.GetMinSize() laurent@738: posx = max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)) laurent@738: posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)) laurent@738: self.ParamsEditor.Scroll(posx, posy) Edouard@1451: self.ParamsEditor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, laurent@738: maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy) Edouard@1451: Laurent@1180: def OnParamsEditorResize(self, event): Laurent@840: self.RefreshScrollbars() laurent@738: event.Skip() Edouard@1451: Laurent@1180: def OnParamsEditorScroll(self, event): Laurent@1180: control = self.ParamsEditor.FindFocus() Laurent@1180: if isinstance(control, TextCtrlAutoComplete): Laurent@1180: control.DismissListBox() Laurent@1180: self.Refresh() Laurent@1180: event.Skip() Edouard@1451: