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@1696: # Copyright (C) 2017: Andrey Skvortsov <andrej.skvortzov@gmail.com>
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: 
andrej@1853: 
andrej@1853: from __future__ import absolute_import
andrej@2437: from __future__ import division
laurent@738: 
laurent@738: import wx
laurent@738: 
andrej@1853: from editors.EditorPanel import EditorPanel
Laurent@814: 
Laurent@814: from IDEFrame import TITLE, FILEMENU, PROJECTTREE, PAGETITLES
Laurent@814: 
denis@2001: from controls import TextCtrlAutoComplete, UriLocationEditor
Laurent@814: from dialogs import BrowseValuesLibraryDialog
Laurent@814: from util.BitmapLibrary import GetBitmap
laurent@738: 
laurent@738: if wx.Platform == '__WXMSW__':
andrej@1747:     faces = {
andrej@1747:         'times': 'Times New Roman',
andrej@1747:         'mono':  'Courier New',
andrej@1747:         'helv':  'Arial',
andrej@1747:         'other': 'Comic Sans MS',
andrej@1747:         'size':  16,
andrej@1747:     }
laurent@738: else:
andrej@1747:     faces = {
andrej@1747:         'times': 'Times',
andrej@1747:         'mono':  'Courier',
andrej@1747:         'helv':  'Helvetica',
andrej@1747:         'other': 'new century schoolbook',
andrej@1747:         'size':  18,
andrej@1747:     }
laurent@738: 
laurent@738: SCROLLBAR_UNIT = 10
laurent@738: 
andrej@1736: 
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
andrej@1743:         if bmp is not 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
andrej@1740:             bw, bh = bmp.GetWidth(), bmp.GetHeight()
laurent@738:             if not self.up:
laurent@738:                 dw = dy = self.labelDelta
andrej@1743:             hasMask = bmp.GetMask() is not 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: 
andrej@2437:         pos_x = (width - bw) // 2 + dw      # adjust for bitmap and text to centre
andrej@2437:         pos_y = (height - bh - th) // 2 + dy
andrej@1743:         if bmp is not None:
andrej@1737:             dc.DrawBitmap(bmp, pos_x, pos_y, hasMask)  # draw bitmap if available
andrej@2437:             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,
andrej@1744:                  pos=wx.DefaultPosition, size=wx.DefaultSize,
andrej@1744:                  style=0,
andrej@1744:                  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,
andrej@1768:                                  bitmap,
andrej@1768:                                  pos, size,
andrej@1768:                                  style,
andrej@1768:                                  name)
laurent@738: 
andrej@1736: 
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,
andrej@1768:                                    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,
andrej@1768:                                         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,
andrej@1744:                             wx.BOLD, faceName=faces["helv"]))
Edouard@1451:                 baseparamseditor_sizer.AddWindow(self.FullIECChannel,
andrej@1768:                                                  flag=wx.ALIGN_CENTER_VERTICAL)
Edouard@1451: 
laurent@775:                 updownsizer = wx.BoxSizer(wx.VERTICAL)
Edouard@1451:                 baseparamseditor_sizer.AddSizer(updownsizer, border=5,
andrej@1768:                                                 flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL)
andrej@1768: 
andrej@1768:                 self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton(
andrej@1768:                     self.Editor,
andrej@1768:                     bitmap=GetBitmap('IECCDown'),
andrej@1768:                     size=wx.Size(16, 16),
andrej@1768:                     style=wx.NO_BORDER)
Edouard@1451:                 self.IECCUpButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(1),
andrej@1768:                                        self.IECCUpButton)
laurent@781:                 updownsizer.AddWindow(self.IECCUpButton, flag=wx.ALIGN_LEFT)
Edouard@1451: 
andrej@1768:                 self.IECCDownButton = wx.lib.buttons.GenBitmapButton(
andrej@1768:                     self.Editor, bitmap=GetBitmap('IECCUp'),
andrej@1768:                     size=wx.Size(16, 16), style=wx.NO_BORDER)
Edouard@1451:                 self.IECCDownButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(-1),
andrej@1768:                                          self.IECCDownButton)
laurent@781:                 updownsizer.AddWindow(self.IECCDownButton, flag=wx.ALIGN_LEFT)
Edouard@1451: 
Edouard@1451:                 self.ConfNodeName = wx.TextCtrl(self.Editor,
andrej@1768:                                                 size=wx.Size(150, 25))
laurent@775:                 self.ConfNodeName.SetFont(
Edouard@1451:                     wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL,
andrej@1744:                             wx.BOLD, faceName=faces["helv"]))
andrej@1768:                 self.ConfNodeName.Bind(
andrej@1768:                     wx.EVT_TEXT,
andrej@1768:                     self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name", True),
andrej@1768:                     self.ConfNodeName)
andrej@1768:                 baseparamseditor_sizer.AddWindow(
andrej@1768:                     self.ConfNodeName, border=5,
andrej@1768:                     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: 
andrej@1745:             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,
andrej@1768:                                                   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,
andrej@1768:                                             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):
andrej@2437:         normal_bt_font = wx.Font(faces["size"] // 3,    wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName=faces["helv"])
andrej@2437:         mouseover_bt_font = wx.Font(faces["size"] // 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName=faces["helv"], underline=True)
Edouard@1451: 
laurent@738:         msizer = wx.BoxSizer(wx.HORIZONTAL)
Edouard@1451: 
laurent@738:         for confnode_method in self.Controler.ConfNodeMethods:
andrej@1740:             if "method" in confnode_method and confnode_method.get("shown", True):
Laurent@1055:                 button = GenBitmapTextButton(self.Editor,
andrej@1768:                                              bitmap=GetBitmap(confnode_method.get("bitmap", "Unknown")),
andrej@1768:                                              label=confnode_method["name"],
andrej@1768:                                              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
andrej@1750: 
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))
andrej@1733:                 # hack to force size to mini
andrej@1740:                 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: 
denis@2001:     def UriOptions(self, event):
denis@2001:         CTR = self.ParentWindow.CTR
denis@2001:         CTR_BeremizRoot = CTR.BeremizRoot
denis@2001:         CTR_AppFrame = CTR.AppFrame
denis@2001: 
denis@2001:         # Get connector uri
denis@2001:         uri = CTR_BeremizRoot.getURI_location().strip()
denis@2001:         dialog = UriLocationEditor.UriLocationEditor(CTR_AppFrame, uri)
denis@2001: 
denis@2001:         if dialog.ShowModal() == wx.ID_OK:
denis@2001:             CTR_BeremizRoot.setURI_location(dialog.GetURI())
denis@2001:             if CTR._View is not None:
denis@2001:                 CTR._View.RefreshView()
denis@2001:             if CTR_AppFrame is not None:
denis@2001:                 CTR_AppFrame.RefreshTitle()
denis@2001:                 CTR_AppFrame.RefreshFileMenu()
denis@2001:                 CTR_AppFrame.RefreshEditMenu()
denis@2001:                 CTR_AppFrame.RefreshPageTitles()
denis@2001: 
denis@2001:         dialog.Destroy()
denis@2001: 
andrej@1744:     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:
andrej@1734:                 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,
andrej@1768:                                          label=_(label), size=wx.Size(10, 0))
laurent@738:                 staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
surkovsv93@1562:                 flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
laurent@738:                 if first:
surkovsv93@1562:                     flags |= wx.TOP
surkovsv93@1562:                 sizer.AddSizer(staticboxsizer, border=5, flag=flags)
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)
surkovsv93@1562:                 flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
laurent@738:                 if first:
surkovsv93@1562:                     flags |= wx.TOP
surkovsv93@1562:                 sizer.AddSizer(boxsizer, border=5, flag=flags)
andrej@1768:                 staticbitmap = GenStaticBitmap(
andrej@1768:                     ID=-1, bitmapname=element_infos["name"],
andrej@1734:                     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,
andrej@1768:                                            label="%s:" % _(element_infos["name"]))
Edouard@1451:                 boxsizer.AddWindow(statictext, border=5,
andrej@1768:                                    flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
Edouard@1451: 
andrej@2450:                 if isinstance(element_infos["type"], list):
andrej@2450:                     if isinstance(element_infos["value"], tuple):
laurent@738:                         browse_boxsizer = wx.BoxSizer(wx.HORIZONTAL)
laurent@781:                         boxsizer.AddSizer(browse_boxsizer)
Edouard@1451: 
Edouard@1451:                         textctrl = wx.TextCtrl(self.ParamsEditor,
andrej@1768:                                                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: 
andrej@1696:                         button = wx.Button(self.ParamsEditor, label="...")
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,
andrej@1768:                                                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("")
andrej@2450:                         if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], tuple):
andrej@1847:                             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,
andrej@1768:                                                      label="%s - %s" % (_(name), _(value)),
andrej@1768:                                                      size=wx.Size(10, 0))
laurent@738:                             staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
surkovsv93@1562:                             sizer.AddSizer(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
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:
surkovsv93@1712:                             combobox.SetStringSelection(element_infos["value"])
laurent@781:                         combobox.Bind(wx.EVT_COMBOBOX, callback, combobox)
Edouard@1451: 
andrej@2450:                 elif isinstance(element_infos["type"], dict):
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,
andrej@1768:                                            size=wx.Size(300, -1),
andrej@1768:                                            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: 
andrej@1740:                     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,
andrej@1768:                                                size=wx.Size(300, -1),
andrej@1768:                                                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: 
denis@2001:                         if element_infos["name"] == "URI_location":
denis@2001:                             uriSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
denis@2001:                             uriSizer.AddGrowableCol(0)
denis@2001:                             uriSizer.AddGrowableRow(0)
denis@2001: 
denis@2001:                             self.EditButton = wx.Button(self.ParamsEditor, label='...', size=wx.Size(30, -1))
denis@2001:                             self.Bind(wx.EVT_BUTTON, self.UriOptions, self.EditButton)
denis@2001: 
denis@2001:                             uriSizer.AddWindow(textctrl, flag=wx.GROW)
denis@2001:                             uriSizer.AddWindow(self.EditButton, flag=wx.GROW)
denis@2001: 
denis@2001:                             boxsizer.AddWindow(uriSizer)
denis@2001:                         else:
denis@2001:                             boxsizer.AddWindow(textctrl)
denis@2001: 
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)
denis@1991:                         textctrl.Bind(wx.EVT_TEXT, callback)
Laurent@1180:                         textctrl.Bind(wx.EVT_KILL_FOCUS, callback)
laurent@738:             first = False
surkovsv93@1562:         sizer.Layout()
surkovsv93@1562:         self.RefreshScrollbars()
Edouard@1451: 
laurent@738:     def GetItemChannelChangedFunction(self, dir):
laurent@738:         def OnConfNodeTreeItemChannelChanged(event):
laurent@738:             confnode_IECChannel = self.Controler.BaseParams.getIEC_Channel()
andrej@1846:             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
andrej@1740:             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):
andrej@1846:             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]
andrej@1750: 
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()
andrej@2437:         posx = max(0, min(xstart, (maxx - window_size[0]) // SCROLLBAR_UNIT))
andrej@2437:         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,
andrej@2437:                                         maxx // SCROLLBAR_UNIT,
andrej@2437:                                         maxy // SCROLLBAR_UNIT,
andrej@1768:                                         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()