beremiz

3f285782ac9b
Parents f390e9fdd2cf
Children 7ac746c07ff2
Bugs with TextCtrlAutoComplete on Windows fixed
  • +1536 -1525
    Beremiz.py
  • +141 -98
    TextCtrlAutoComplete.py
  • --- a/Beremiz.py Thu Oct 22 11:28:11 2009 +0200
    +++ b/Beremiz.py Thu Oct 22 17:20:24 2009 +0200
    @@ -1,1525 +1,1536 @@
    -#!/usr/bin/env python
    -# -*- coding: utf-8 -*-
    -
    -#This file is part of Beremiz, a Integrated Development Environment for
    -#programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
    -#
    -#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
    -
    -__version__ = "$Revision$"
    -
    -import os, sys, getopt, wx
    -import tempfile
    -import shutil
    -import random
    -
    -CWD = os.path.split(os.path.realpath(__file__))[0]
    -
    -def Bpath(*args):
    - return os.path.join(CWD,*args)
    -
    -if __name__ == '__main__':
    - def usage():
    - print "\nUsage of Beremiz.py :"
    - print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0]
    -
    - try:
    - opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
    - except getopt.GetoptError:
    - # print help information and exit:
    - usage()
    - sys.exit(2)
    -
    - for o, a in opts:
    - if o in ("-h", "--help"):
    - usage()
    - sys.exit()
    -
    - if len(args) > 2:
    - usage()
    - sys.exit()
    - elif len(args) == 1:
    - projectOpen = args[0]
    - buildpath = None
    - elif len(args) == 2:
    - projectOpen = args[0]
    - buildpath = args[1]
    - else:
    - projectOpen = None
    - buildpath = None
    -
    - app = wx.PySimpleApp()
    - app.SetAppName('beremiz')
    - wx.InitAllImageHandlers()
    -
    - bmp = wx.Image(Bpath("images","splash.png")).ConvertToBitmap()
    - splash=wx.SplashScreen(bmp,wx.SPLASH_CENTRE_ON_SCREEN, 1000, None)
    - wx.Yield()
    -
    -# Import module for internationalization
    -import gettext
    -import __builtin__
    -
    -# Get folder containing translation files
    -localedir = os.path.join(CWD,"locale")
    -# Get the default language
    -langid = wx.LANGUAGE_DEFAULT
    -# Define translation domain (name of translation files)
    -domain = "Beremiz"
    -
    -# Define locale for wx
    -loc = __builtin__.__dict__.get('loc', None)
    -if loc is None:
    - loc = wx.Locale(langid)
    - __builtin__.__dict__['loc'] = loc
    -# Define location for searching translation files
    -loc.AddCatalogLookupPathPrefix(localedir)
    -# Define locale domain
    -loc.AddCatalog(domain)
    -
    -def unicode_translation(message):
    - return wx.GetTranslation(message).encode("utf-8")
    -
    -if __name__ == '__main__':
    - __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
    -
    -#Quick hack to be able to find Beremiz IEC tools. Should be config params.
    -base_folder = os.path.split(sys.path[0])[0]
    -sys.path.append(base_folder)
    -sys.path.append(os.path.join(base_folder, "plcopeneditor"))
    -sys.path.append(os.path.join(base_folder, "docutils"))
    -
    -import wx.lib.buttons, wx.lib.statbmp
    -import TextCtrlAutoComplete, cPickle
    -import types, time, re, platform, time, traceback, commands
    -from plugger import PluginsRoot, MATIEC_ERROR_MODEL
    -from wxPopen import ProcessLogger
    -
    -from docutils import *
    -from PLCOpenEditor import IDEFrame, Viewer, AppendMenu, TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, SCALING
    -from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
    -
    -SCROLLBAR_UNIT = 10
    -WINDOW_COLOUR = wx.Colour(240,240,240)
    -TITLE_COLOUR = wx.Colour(200,200,220)
    -CHANGED_TITLE_COLOUR = wx.Colour(220,200,220)
    -CHANGED_WINDOW_COLOUR = wx.Colour(255,240,240)
    -
    -if wx.Platform == '__WXMSW__':
    - faces = { 'times': 'Times New Roman',
    - 'mono' : 'Courier New',
    - 'helv' : 'Arial',
    - 'other': 'Comic Sans MS',
    - 'size' : 16,
    - }
    -else:
    - faces = { 'times': 'Times',
    - 'mono' : 'Courier',
    - 'helv' : 'Helvetica',
    - 'other': 'new century schoolbook',
    - 'size' : 18,
    - }
    -
    -# Some helpers to tweak GenBitmapTextButtons
    -# TODO: declare customized classes instead.
    -gen_mini_GetBackgroundBrush = lambda obj:lambda dc: wx.Brush(obj.GetParent().GetBackgroundColour(), wx.SOLID)
    -gen_textbutton_GetLabelSize = lambda obj:lambda:(wx.lib.buttons.GenButton._GetLabelSize(obj)[:-1] + (False,))
    -
    -def make_genbitmaptogglebutton_flat(button):
    - button.GetBackgroundBrush = gen_mini_GetBackgroundBrush(button)
    - button.labelDelta = 0
    - button.SetBezelWidth(0)
    - button.SetUseFocusIndicator(False)
    -
    -# Patch wx.lib.imageutils so that gray is supported on alpha images
    -import wx.lib.imageutils
    -from wx.lib.imageutils import grayOut as old_grayOut
    -def grayOut(anImage):
    - if anImage.HasAlpha():
    - AlphaData = anImage.GetAlphaData()
    - else :
    - AlphaData = None
    -
    - old_grayOut(anImage)
    -
    - if AlphaData is not None:
    - anImage.SetAlphaData(AlphaData)
    -
    -wx.lib.imageutils.grayOut = grayOut
    -
    -class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton):
    - def _GetLabelSize(self):
    - """ used internally """
    - w, h = self.GetTextExtent(self.GetLabel())
    - if not self.bmpLabel:
    - return w, h, False # if there isn't a bitmap use the size of the text
    -
    - w_bmp = self.bmpLabel.GetWidth()+2
    - h_bmp = self.bmpLabel.GetHeight()+2
    - height = h + h_bmp
    - if w_bmp > w:
    - width = w_bmp
    - else:
    - width = w
    - return width, height, False
    -
    - def DrawLabel(self, dc, width, height, dw=0, dy=0):
    - bmp = self.bmpLabel
    - if bmp != None: # if the bitmap is used
    - if self.bmpDisabled and not self.IsEnabled():
    - bmp = self.bmpDisabled
    - if self.bmpFocus and self.hasFocus:
    - bmp = self.bmpFocus
    - if self.bmpSelected and not self.up:
    - bmp = self.bmpSelected
    - bw,bh = bmp.GetWidth(), bmp.GetHeight()
    - if not self.up:
    - dw = dy = self.labelDelta
    - hasMask = bmp.GetMask() != None
    - else:
    - bw = bh = 0 # no bitmap -> size is zero
    -
    - dc.SetFont(self.GetFont())
    - if self.IsEnabled():
    - dc.SetTextForeground(self.GetForegroundColour())
    - else:
    - dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
    -
    - label = self.GetLabel()
    - tw, th = dc.GetTextExtent(label) # size of text
    - if not self.up:
    - dw = dy = self.labelDelta
    -
    - pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre
    - pos_y = (height-bh-th)/2+dy
    - if bmp !=None:
    - dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available
    - pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre
    - pos_y += bh + 2
    -
    - dc.DrawText(label, pos_x, pos_y) # draw the text
    -
    -
    -class GenStaticBitmap(wx.lib.statbmp.GenStaticBitmap):
    - """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32,
    - and accept image name as __init__ parameter, fail silently if file do not exist"""
    - def __init__(self, parent, ID, bitmapname,
    - pos = wx.DefaultPosition, size = wx.DefaultSize,
    - style = 0,
    - name = "genstatbmp"):
    -
    - bitmappath = Bpath( "images", bitmapname)
    - if os.path.isfile(bitmappath):
    - bitmap = wx.Bitmap(bitmappath)
    - else:
    - bitmap = None
    - wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, ID, bitmap,
    - pos, size,
    - style,
    - name)
    -
    - def OnPaint(self, event):
    - dc = wx.PaintDC(self)
    - colour = self.GetParent().GetBackgroundColour()
    - dc.SetPen(wx.Pen(colour))
    - dc.SetBrush(wx.Brush(colour ))
    - dc.DrawRectangle(0, 0, *dc.GetSizeTuple())
    - if self._bitmap:
    - dc.DrawBitmap(self._bitmap, 0, 0, True)
    -
    -
    -class LogPseudoFile:
    - """ Base class for file like objects to facilitate StdOut for the Shell."""
    - def __init__(self, output):
    - self.red_white = wx.TextAttr("RED", "WHITE")
    - self.red_yellow = wx.TextAttr("RED", "YELLOW")
    - self.black_white = wx.TextAttr("BLACK", "WHITE")
    - self.default_style = None
    - self.output = output
    -
    - def write(self, s, style = None):
    - if style is None : style=self.black_white
    - self.output.Freeze();
    - if self.default_style != style:
    - self.output.SetDefaultStyle(style)
    - self.default_style = style
    - self.output.AppendText(s)
    - self.output.ScrollLines(s.count('\n')+1)
    - self.output.ShowPosition(self.output.GetLastPosition())
    - self.output.Thaw()
    -
    - def write_warning(self, s):
    - self.write(s,self.red_white)
    -
    - def write_error(self, s):
    - self.write(s,self.red_yellow)
    -
    - def flush(self):
    - self.output.SetValue("")
    -
    - def isatty(self):
    - return false
    -
    -[ID_BEREMIZ, ID_BEREMIZMAINSPLITTER,
    - ID_BEREMIZPLCCONFIG, ID_BEREMIZLOGCONSOLE,
    - ID_BEREMIZINSPECTOR] = [wx.NewId() for _init_ctrls in range(5)]
    -
    -[ID_BEREMIZRUNMENUBUILD, ID_BEREMIZRUNMENUSIMULATE,
    - ID_BEREMIZRUNMENURUN, ID_BEREMIZRUNMENUSAVELOG,
    -] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)]
    -
    -class Beremiz(IDEFrame):
    -
    - def _init_coll_FileMenu_Items(self, parent):
    - AppendMenu(parent, help='', id=wx.ID_NEW,
    - kind=wx.ITEM_NORMAL, text=_(u'New\tCTRL+N'))
    - AppendMenu(parent, help='', id=wx.ID_OPEN,
    - kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O'))
    - AppendMenu(parent, help='', id=wx.ID_SAVE,
    - kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
    - AppendMenu(parent, help='', id=wx.ID_CLOSE,
    - kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W'))
    - AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL,
    - kind=wx.ITEM_NORMAL, text=_(u'Close Project'))
    - parent.AppendSeparator()
    - AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
    - kind=wx.ITEM_NORMAL, text=_(u'Page Setup'))
    - AppendMenu(parent, help='', id=wx.ID_PREVIEW,
    - kind=wx.ITEM_NORMAL, text=_(u'Preview'))
    - AppendMenu(parent, help='', id=wx.ID_PRINT,
    - kind=wx.ITEM_NORMAL, text=_(u'Print'))
    - parent.AppendSeparator()
    - AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
    - kind=wx.ITEM_NORMAL, text=_(u'Properties'))
    - parent.AppendSeparator()
    - AppendMenu(parent, help='', id=wx.ID_EXIT,
    - kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
    -
    - self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
    - self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
    - self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
    - self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
    - self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
    - self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
    - self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
    - self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
    - self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
    - self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
    -
    - def _init_coll_HelpMenu_Items(self, parent):
    - parent.Append(help='', id=wx.ID_HELP,
    - kind=wx.ITEM_NORMAL, text=_(u'Beremiz\tF1'))
    - parent.Append(help='', id=wx.ID_ABOUT,
    - kind=wx.ITEM_NORMAL, text=_(u'About'))
    - self.Bind(wx.EVT_MENU, self.OnBeremizMenu, id=wx.ID_HELP)
    - self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
    -
    - def _init_coll_PLCConfigMainSizer_Items(self, parent):
    - parent.AddSizer(self.PLCParamsSizer, 0, border=10, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    - parent.AddSizer(self.PluginTreeSizer, 0, border=10, flag=wx.BOTTOM|wx.LEFT|wx.RIGHT)
    -
    - def _init_coll_PLCConfigMainSizer_Growables(self, parent):
    - parent.AddGrowableCol(0)
    - parent.AddGrowableRow(1)
    -
    - def _init_coll_PluginTreeSizer_Growables(self, parent):
    - parent.AddGrowableCol(0)
    - parent.AddGrowableCol(1)
    -
    - def _init_beremiz_sizers(self):
    - self.PLCConfigMainSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2)
    - self.PLCParamsSizer = wx.BoxSizer(wx.VERTICAL)
    - #self.PluginTreeSizer = wx.FlexGridSizer(cols=3, hgap=0, rows=0, vgap=2)
    - self.PluginTreeSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=0, vgap=2)
    -
    - self._init_coll_PLCConfigMainSizer_Items(self.PLCConfigMainSizer)
    - self._init_coll_PLCConfigMainSizer_Growables(self.PLCConfigMainSizer)
    - self._init_coll_PluginTreeSizer_Growables(self.PluginTreeSizer)
    -
    - self.PLCConfig.SetSizer(self.PLCConfigMainSizer)
    -
    - def _init_ctrls(self, prnt):
    - IDEFrame._init_ctrls(self, prnt)
    -
    - self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR)
    - accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)])
    - self.SetAcceleratorTable(accel)
    -
    - self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG,
    - name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0),
    - size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL)
    - self.PLCConfig.SetBackgroundColour(wx.WHITE)
    - self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
    - self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow)
    - self.BottomNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True)
    -
    - self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='',
    - name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
    - size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2)
    - self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick)
    - self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console"))
    -
    - self._init_beremiz_sizers()
    -
    - def __init__(self, parent, projectOpen=None, buildpath=None, plugin_root=None, debug=True):
    - IDEFrame.__init__(self, parent, debug)
    - self.Config = wx.ConfigBase.Get()
    -
    - self.Log = LogPseudoFile(self.LogConsole)
    -
    - self.local_runtime = None
    - self.runtime_port = None
    - self.local_runtime_tmpdir = None
    -
    - self.DisableEvents = False
    -
    - self.PluginInfos = {}
    -
    - if projectOpen is not None and os.path.isdir(projectOpen):
    - self.PluginRoot = PluginsRoot(self, self.Log)
    - self.Controler = self.PluginRoot
    - result = self.PluginRoot.LoadProject(projectOpen, buildpath)
    - if not result:
    - self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
    - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    - self.RefreshAll()
    - else:
    - self.ResetView()
    - self.ShowErrorMessage(result)
    - else:
    - self.PluginRoot = plugin_root
    - self.Controler = plugin_root
    - if plugin_root is not None:
    - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    - self.RefreshAll()
    -
    - # Add beremiz's icon in top left corner of the frame
    - self.SetIcon(wx.Icon(Bpath( "images", "brz.ico"), wx.BITMAP_TYPE_ICO))
    -
    - self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
    -
    - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
    -
    - def RefreshTitle(self):
    - name = _("Beremiz")
    - if self.PluginRoot is not None:
    - projectname = self.PluginRoot.GetProjectName()
    - if self.PluginRoot.PlugTestModified():
    - projectname = "~%s~" % projectname
    - self.SetTitle("%s - %s" % (name, projectname))
    - else:
    - self.SetTitle(name)
    -
    - def StartLocalRuntime(self, taskbaricon = True):
    - if self.local_runtime is None or self.local_runtime.finished:
    - # create temporary directory for runtime working directory
    - self.local_runtime_tmpdir = tempfile.mkdtemp()
    - # choose an arbitrary random port for runtime
    - self.runtime_port = int(random.random() * 1000) + 61131
    - # launch local runtime
    - self.local_runtime = ProcessLogger(self.Log,
    - "\"%s\" \"%s\" -p %s -i localhost %s %s"%(sys.executable,
    - Bpath("Beremiz_service.py"),
    - self.runtime_port,
    - {False : "-x 0", True :"-x 1"}[taskbaricon],
    - self.local_runtime_tmpdir),
    - no_gui=False)
    - self.local_runtime.spin(timeout=500, keyword = "working", kill_it = False)
    - return self.runtime_port
    -
    - def KillLocalRuntime(self):
    - if self.local_runtime is not None:
    - # shutdown local runtime
    - self.local_runtime.kill(gently=False)
    - # clear temp dir
    - shutil.rmtree(self.local_runtime_tmpdir)
    -
    - def OnOpenWidgetInspector(self, evt):
    - # Activate the widget inspection tool
    - from wx.lib.inspection import InspectionTool
    - if not InspectionTool().initialized:
    - InspectionTool().Init()
    -
    - # Find a widget to be selected in the tree. Use either the
    - # one under the cursor, if any, or this frame.
    - wnd = wx.FindWindowAtPointer()
    - if not wnd:
    - wnd = self
    - InspectionTool().Show(wnd, True)
    -
    - def OnLogConsoleDClick(self, event):
    - wx.CallAfter(self.SearchLineForError)
    - event.Skip()
    -
    - def SearchLineForError(self):
    - if self.PluginRoot is not None:
    - text = self.LogConsole.GetRange(0, self.LogConsole.GetInsertionPoint())
    - line = self.LogConsole.GetLineText(len(text.splitlines()) - 1)
    - result = MATIEC_ERROR_MODEL.match(line)
    - if result is not None:
    - first_line, first_column, last_line, last_column, error = result.groups()
    - infos = self.PluginRoot.ShowError(self.Log,
    - (int(first_line), int(first_column)),
    - (int(last_line), int(last_column)))
    -
    - def OnCloseFrame(self, event):
    - if self.PluginRoot is not None:
    - if self.PluginRoot.ProjectTestModified():
    - dialog = wx.MessageDialog(self,
    - _("Save changes ?"),
    - _("Close Application"),
    - wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
    - answer = dialog.ShowModal()
    - dialog.Destroy()
    - if answer == wx.ID_YES:
    - self.PluginRoot.SaveProject()
    - event.Skip()
    - elif answer == wx.ID_NO:
    - event.Skip()
    - return
    - else:
    - event.Veto()
    - return
    -
    - self.KillLocalRuntime()
    -
    - event.Skip()
    -
    - def OnMoveWindow(self, event):
    - self.GetBestSize()
    - self.RefreshScrollBars()
    - event.Skip()
    -
    - def OnPanelLeftDown(self, event):
    - focused = self.FindFocus()
    - if isinstance(focused, TextCtrlAutoComplete.TextCtrlAutoComplete):
    - focused._showDropDown(False)
    - event.Skip()
    -
    - def RefreshFileMenu(self):
    - if self.PluginRoot is not None:
    - selected = self.TabsOpened.GetSelection()
    - if selected >= 0:
    - graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
    - else:
    - graphic_viewer = False
    - if self.TabsOpened.GetPageCount() > 0:
    - self.FileMenu.Enable(wx.ID_CLOSE, True)
    - if graphic_viewer:
    - self.FileMenu.Enable(wx.ID_PREVIEW, True)
    - self.FileMenu.Enable(wx.ID_PRINT, True)
    - else:
    - self.FileMenu.Enable(wx.ID_PREVIEW, False)
    - self.FileMenu.Enable(wx.ID_PRINT, False)
    - else:
    - self.FileMenu.Enable(wx.ID_CLOSE, False)
    - self.FileMenu.Enable(wx.ID_PREVIEW, False)
    - self.FileMenu.Enable(wx.ID_PRINT, False)
    - self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
    - self.FileMenu.Enable(wx.ID_SAVE, True)
    - self.FileMenu.Enable(wx.ID_PROPERTIES, True)
    - self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
    - else:
    - self.FileMenu.Enable(wx.ID_CLOSE, False)
    - self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
    - self.FileMenu.Enable(wx.ID_PREVIEW, False)
    - self.FileMenu.Enable(wx.ID_PRINT, False)
    - self.FileMenu.Enable(wx.ID_SAVE, False)
    - self.FileMenu.Enable(wx.ID_PROPERTIES, False)
    - self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
    -
    - def RefreshScrollBars(self):
    - xstart, ystart = self.PLCConfig.GetViewStart()
    - window_size = self.PLCConfig.GetClientSize()
    - sizer = self.PLCConfig.GetSizer()
    - if sizer:
    - maxx, maxy = sizer.GetMinSize()
    - self.PLCConfig.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
    - maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT,
    - max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)),
    - max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)))
    -
    - def RefreshPLCParams(self):
    - self.Freeze()
    - self.ClearSizer(self.PLCParamsSizer)
    -
    - if self.PluginRoot is not None:
    - plcwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    - if self.PluginRoot.PlugTestModified():
    - bkgdclr = CHANGED_TITLE_COLOUR
    - else:
    - bkgdclr = TITLE_COLOUR
    -
    - if self.PluginRoot not in self.PluginInfos:
    - self.PluginInfos[self.PluginRoot] = {"right_visible" : False}
    -
    - plcwindow.SetBackgroundColour(TITLE_COLOUR)
    - plcwindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
    - self.PLCParamsSizer.AddWindow(plcwindow, 0, border=0, flag=wx.GROW)
    -
    - plcwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
    - plcwindow.SetSizer(plcwindowsizer)
    -
    - st = wx.StaticText(plcwindow, -1)
    - st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    - st.SetLabel(self.PluginRoot.GetProjectName())
    - plcwindowsizer.AddWindow(st, 0, border=5, flag=wx.ALL|wx.ALIGN_CENTER)
    -
    - addbutton_id = wx.NewId()
    - addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')),
    - name='AddPluginButton', parent=plcwindow, pos=wx.Point(0, 0),
    - size=wx.Size(16, 16), style=wx.NO_BORDER)
    - addbutton.SetToolTipString(_("Add a sub plugin"))
    - addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(self.PluginRoot), id=addbutton_id)
    - plcwindowsizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
    -
    - plcwindowmainsizer = wx.BoxSizer(wx.VERTICAL)
    - plcwindowsizer.AddSizer(plcwindowmainsizer, 0, border=5, flag=wx.ALL)
    -
    - plcwindowbuttonsizer = wx.BoxSizer(wx.HORIZONTAL)
    - plcwindowmainsizer.AddSizer(plcwindowbuttonsizer, 0, border=0, flag=wx.ALIGN_CENTER)
    -
    - msizer = self.GenerateMethodButtonSizer(self.PluginRoot, plcwindow, not self.PluginInfos[self.PluginRoot]["right_visible"])
    - plcwindowbuttonsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW)
    -
    - paramswindow = wx.Panel(plcwindow, -1, size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL)
    - paramswindow.SetBackgroundColour(TITLE_COLOUR)
    - paramswindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
    - plcwindowbuttonsizer.AddWindow(paramswindow, 0, border=0, flag=0)
    -
    - psizer = wx.BoxSizer(wx.HORIZONTAL)
    - paramswindow.SetSizer(psizer)
    -
    - plugin_infos = self.PluginRoot.GetParamsAttributes()
    - self.RefreshSizerElement(paramswindow, psizer, self.PluginRoot, plugin_infos, None, False)
    -
    - if not self.PluginInfos[self.PluginRoot]["right_visible"]:
    - paramswindow.Hide()
    -
    - minimizebutton_id = wx.NewId()
    - minimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=minimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')),
    - name='MinimizeButton', parent=plcwindow, pos=wx.Point(0, 0),
    - size=wx.Size(24, 24), style=wx.NO_BORDER)
    - make_genbitmaptogglebutton_flat(minimizebutton)
    - minimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png')))
    - minimizebutton.SetToggle(self.PluginInfos[self.PluginRoot]["right_visible"])
    - plcwindowbuttonsizer.AddWindow(minimizebutton, 0, border=5, flag=wx.ALL)
    -
    - def togglewindow(event):
    - if minimizebutton.GetToggle():
    - paramswindow.Show()
    - msizer.SetCols(1)
    - else:
    - paramswindow.Hide()
    - msizer.SetCols(len(self.PluginRoot.PluginMethods))
    - self.PluginInfos[self.PluginRoot]["right_visible"] = minimizebutton.GetToggle()
    - self.PLCConfigMainSizer.Layout()
    - self.RefreshScrollBars()
    - event.Skip()
    - minimizebutton.Bind(wx.EVT_BUTTON, togglewindow, id=minimizebutton_id)
    -
    - self.PluginInfos[self.PluginRoot]["main"] = plcwindow
    - self.PluginInfos[self.PluginRoot]["params"] = paramswindow
    -
    - self.PLCConfigMainSizer.Layout()
    - self.RefreshScrollBars()
    - self.Thaw()
    -
    - def GenerateMethodButtonSizer(self, plugin, parent, horizontal = True):
    - normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])
    - mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"])
    - if horizontal:
    - msizer = wx.FlexGridSizer(cols=len(plugin.PluginMethods))
    - else:
    - msizer = wx.FlexGridSizer(cols=1)
    - for plugin_method in plugin.PluginMethods:
    - if "method" in plugin_method and plugin_method.get("shown",True):
    - id = wx.NewId()
    - label = plugin_method["name"]
    - button = GenBitmapTextButton(id=id, parent=parent,
    - bitmap=wx.Bitmap(Bpath( "%s.png"%plugin_method.get("bitmap", os.path.join("images", "Unknown")))), label=label,
    - name=label, pos=wx.DefaultPosition, style=wx.NO_BORDER)
    - button.SetFont(normal_bt_font)
    - button.SetToolTipString(plugin_method["tooltip"])
    - button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(plugin, plugin_method["method"]), id=id)
    - # a fancy underline on mouseover
    - def setFontStyle(b, s):
    - def fn(event):
    - b.SetFont(s)
    - b.Refresh()
    - event.Skip()
    - return fn
    - button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font))
    - button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font))
    - #hack to force size to mini
    - if not plugin_method.get("enabled",True):
    - button.Disable()
    - msizer.AddWindow(button, 0, border=0, flag=wx.ALIGN_CENTER)
    - return msizer
    -
    - def RefreshPluginTree(self):
    - self.Freeze()
    - self.ClearSizer(self.PluginTreeSizer)
    - if self.PluginRoot is not None:
    - for child in self.PluginRoot.IECSortedChilds():
    - self.GenerateTreeBranch(child)
    - if not self.PluginInfos[child]["expanded"]:
    - self.CollapsePlugin(child)
    - self.PLCConfigMainSizer.Layout()
    - self.RefreshScrollBars()
    - self.Thaw()
    -
    - def SetPluginParamsAttribute(self, plugin, *args, **kwargs):
    - res, StructChanged = plugin.SetParamsAttribute(*args, **kwargs)
    - if StructChanged:
    - wx.CallAfter(self.RefreshPluginTree)
    - else:
    - if plugin == self.PluginRoot:
    - bkgdclr = CHANGED_TITLE_COLOUR
    - items = ["main", "params"]
    - else:
    - bkgdclr = CHANGED_WINDOW_COLOUR
    - items = ["left", "right", "params"]
    - for i in items:
    - self.PluginInfos[plugin][i].SetBackgroundColour(bkgdclr)
    - self.PluginInfos[plugin][i].Refresh()
    - return res
    -
    - def ExpandPlugin(self, plugin, force = False):
    - for child in self.PluginInfos[plugin]["children"]:
    - self.PluginInfos[child]["left"].Show()
    - self.PluginInfos[child]["right"].Show()
    - if force or not self.PluginInfos[child]["expanded"]:
    - self.ExpandPlugin(child, force)
    - if force:
    - self.PluginInfos[child]["expanded"] = True
    - locations_infos = self.PluginInfos[plugin].get("locations_infos", None)
    - if locations_infos is not None:
    - if force or locations_infos["root"]["expanded"]:
    - self.ExpandLocation(locations_infos, "root", force)
    - if force:
    - locations_infos["root"]["expanded"] = True
    -
    -
    - def CollapsePlugin(self, plugin, force = False):
    - for child in self.PluginInfos[plugin]["children"]:
    - self.PluginInfos[child]["left"].Hide()
    - self.PluginInfos[child]["right"].Hide()
    - self.CollapsePlugin(child, force)
    - if force:
    - self.PluginInfos[child]["expanded"] = False
    - locations_infos = self.PluginInfos[plugin].get("locations_infos", None)
    - if locations_infos is not None:
    - self.CollapseLocation(locations_infos, "root", force)
    - if force:
    - locations_infos["root"]["expanded"] = False
    -
    - def ExpandLocation(self, locations_infos, group, force = False):
    - for child in locations_infos[group]["children"]:
    - locations_infos[child]["left"].Show()
    - locations_infos[child]["right"].Show()
    - if force or locations_infos[child]["expanded"]:
    - self.ExpandLocation(locations_infos, child, force)
    - if force:
    - locations_infos[child]["expanded"] = True
    -
    - def CollapseLocation(self, locations_infos, group, force = False):
    - for child in locations_infos[group]["children"]:
    - locations_infos[child]["left"].Hide()
    - locations_infos[child]["right"].Hide()
    - self.CollapseLocation(locations_infos, child, force)
    - if force:
    - locations_infos[child]["expanded"] = False
    -
    - def GenerateTreeBranch(self, plugin):
    - leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    - if plugin.PlugTestModified():
    - bkgdclr=CHANGED_WINDOW_COLOUR
    - else:
    - bkgdclr=WINDOW_COLOUR
    -
    - leftwindow.SetBackgroundColour(bkgdclr)
    -
    - if not self.PluginInfos.has_key(plugin):
    - self.PluginInfos[plugin] = {"expanded" : False, "right_visible" : False}
    -
    - self.PluginInfos[plugin]["children"] = plugin.IECSortedChilds()
    - plugin_locations = []
    - if len(self.PluginInfos[plugin]["children"]) == 0:
    - plugin_locations = plugin.GetVariableLocationTree()["children"]
    - if not self.PluginInfos[plugin].has_key("locations_infos"):
    - self.PluginInfos[plugin]["locations_infos"] = {"root": {"expanded" : False}}
    -
    - self.PluginInfos[plugin]["locations_infos"]["root"]["children"] = []
    -
    - self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW)
    -
    - leftwindowsizer = wx.FlexGridSizer(cols=1, rows=2)
    - leftwindowsizer.AddGrowableCol(0)
    - leftwindow.SetSizer(leftwindowsizer)
    -
    - leftbuttonmainsizer = wx.FlexGridSizer(cols=3, rows=1)
    - leftbuttonmainsizer.AddGrowableCol(0)
    - leftwindowsizer.AddSizer(leftbuttonmainsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT) #|wx.TOP
    -
    - leftbuttonsizer = wx.BoxSizer(wx.HORIZONTAL)
    - leftbuttonmainsizer.AddSizer(leftbuttonsizer, 0, border=5, flag=wx.GROW|wx.RIGHT)
    -
    - leftsizer = wx.BoxSizer(wx.VERTICAL)
    - leftbuttonsizer.AddSizer(leftsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - rolesizer = wx.BoxSizer(wx.HORIZONTAL)
    - leftsizer.AddSizer(rolesizer, 0, border=0, flag=wx.GROW|wx.RIGHT)
    -
    - enablebutton_id = wx.NewId()
    - enablebutton = wx.lib.buttons.GenBitmapToggleButton(id=enablebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Disabled.png')),
    - name='EnableButton', parent=leftwindow, size=wx.Size(16, 16), pos=wx.Point(0, 0), style=0)#wx.NO_BORDER)
    - enablebutton.SetToolTipString(_("Enable/Disable this plugin"))
    - make_genbitmaptogglebutton_flat(enablebutton)
    - enablebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Enabled.png')))
    - enablebutton.SetToggle(plugin.MandatoryParams[1].getEnabled())
    - def toggleenablebutton(event):
    - res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle())
    - enablebutton.SetToggle(res)
    - event.Skip()
    - enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id)
    - rolesizer.AddWindow(enablebutton, 0, border=0, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - roletext = wx.StaticText(leftwindow, -1)
    - roletext.SetLabel(plugin.PlugHelp)
    - rolesizer.AddWindow(roletext, 0, border=5, flag=wx.RIGHT|wx.ALIGN_LEFT)
    -
    - plugin_IECChannel = plugin.BaseParams.getIEC_Channel()
    -
    - iecsizer = wx.BoxSizer(wx.HORIZONTAL)
    - leftsizer.AddSizer(iecsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - st = wx.StaticText(leftwindow, -1)
    - st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    - st.SetLabel(plugin.GetFullIEC_Channel())
    - iecsizer.AddWindow(st, 0, border=0, flag=0)
    -
    - updownsizer = wx.BoxSizer(wx.VERTICAL)
    - iecsizer.AddSizer(updownsizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
    -
    - if plugin_IECChannel > 0:
    - ieccdownbutton_id = wx.NewId()
    - ieccdownbutton = wx.lib.buttons.GenBitmapButton(id=ieccdownbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCDown.png')),
    - name='IECCDownButton', parent=leftwindow, pos=wx.Point(0, 0),
    - size=wx.Size(16, 16), style=wx.NO_BORDER)
    - ieccdownbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel - 1), id=ieccdownbutton_id)
    - updownsizer.AddWindow(ieccdownbutton, 0, border=0, flag=wx.ALIGN_LEFT)
    -
    - ieccupbutton_id = wx.NewId()
    - ieccupbutton = wx.lib.buttons.GenBitmapTextButton(id=ieccupbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCUp.png')),
    - name='IECCUpButton', parent=leftwindow, pos=wx.Point(0, 0),
    - size=wx.Size(16, 16), style=wx.NO_BORDER)
    - ieccupbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel + 1), id=ieccupbutton_id)
    - updownsizer.AddWindow(ieccupbutton, 0, border=0, flag=wx.ALIGN_LEFT)
    -
    - adddeletesizer = wx.BoxSizer(wx.VERTICAL)
    - iecsizer.AddSizer(adddeletesizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
    -
    - deletebutton_id = wx.NewId()
    - deletebutton = wx.lib.buttons.GenBitmapButton(id=deletebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Delete.png')),
    - name='DeletePluginButton', parent=leftwindow, pos=wx.Point(0, 0),
    - size=wx.Size(16, 16), style=wx.NO_BORDER)
    - deletebutton.SetToolTipString(_("Delete this plugin"))
    - deletebutton.Bind(wx.EVT_BUTTON, self.GetDeleteButtonFunction(plugin), id=deletebutton_id)
    - adddeletesizer.AddWindow(deletebutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
    -
    - if len(plugin.PlugChildsTypes) > 0:
    - addbutton_id = wx.NewId()
    - addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')),
    - name='AddPluginButton', parent=leftwindow, pos=wx.Point(0, 0),
    - size=wx.Size(16, 16), style=wx.NO_BORDER)
    - addbutton.SetToolTipString(_("Add a sub plugin"))
    - addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(plugin), id=addbutton_id)
    - adddeletesizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
    -
    - expandbutton_id = wx.NewId()
    - expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')),
    - name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0),
    - size=wx.Size(13, 13), style=wx.NO_BORDER)
    - expandbutton.labelDelta = 0
    - expandbutton.SetBezelWidth(0)
    - expandbutton.SetUseFocusIndicator(False)
    - expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png')))
    -
    - if len(self.PluginInfos[plugin]["children"]) > 0:
    - expandbutton.SetToggle(self.PluginInfos[plugin]["expanded"])
    - def togglebutton(event):
    - if expandbutton.GetToggle():
    - self.ExpandPlugin(plugin)
    - else:
    - self.CollapsePlugin(plugin)
    - self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
    - self.PLCConfigMainSizer.Layout()
    - self.RefreshScrollBars()
    - event.Skip()
    - expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
    - elif len(plugin_locations) > 0:
    - locations_infos = self.PluginInfos[plugin]["locations_infos"]
    - expandbutton.SetToggle(locations_infos["root"]["expanded"])
    - def togglebutton(event):
    - if expandbutton.GetToggle():
    - self.ExpandLocation(locations_infos, "root")
    - else:
    - self.CollapseLocation(locations_infos, "root")
    - self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
    - locations_infos["root"]["expanded"] = expandbutton.GetToggle()
    - self.PLCConfigMainSizer.Layout()
    - self.RefreshScrollBars()
    - event.Skip()
    - expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
    - else:
    - expandbutton.Enable(False)
    - iecsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - tc_id = wx.NewId()
    - tc = wx.TextCtrl(leftwindow, tc_id, size=wx.Size(150, 25), style=wx.NO_BORDER)
    - tc.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    - tc.ChangeValue(plugin.MandatoryParams[1].getName())
    - tc.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(tc, plugin, "BaseParams.Name"), id=tc_id)
    - iecsizer.AddWindow(tc, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    - rightwindow.SetBackgroundColour(bkgdclr)
    -
    - self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
    -
    - rightwindowmainsizer = wx.BoxSizer(wx.VERTICAL)
    - rightwindow.SetSizer(rightwindowmainsizer)
    -
    - rightwindowsizer = wx.FlexGridSizer(cols=2, rows=1)
    - rightwindowsizer.AddGrowableCol(1)
    - rightwindowsizer.AddGrowableRow(0)
    - rightwindowmainsizer.AddSizer(rightwindowsizer, 0, border=8, flag=wx.TOP|wx.GROW)
    -
    - msizer = self.GenerateMethodButtonSizer(plugin, rightwindow, not self.PluginInfos[plugin]["right_visible"])
    - rightwindowsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW)
    -
    - rightparamssizer = wx.BoxSizer(wx.HORIZONTAL)
    - rightwindowsizer.AddSizer(rightparamssizer, 0, border=0, flag=wx.ALIGN_RIGHT)
    -
    - paramswindow = wx.Panel(rightwindow, -1, size=wx.Size(-1, -1))
    - paramswindow.SetBackgroundColour(bkgdclr)
    -
    - psizer = wx.BoxSizer(wx.HORIZONTAL)
    - paramswindow.SetSizer(psizer)
    - self.PluginInfos[plugin]["params"] = paramswindow
    -
    - rightparamssizer.AddWindow(paramswindow, 0, border=5, flag=wx.ALL)
    -
    - plugin_infos = plugin.GetParamsAttributes()
    - self.RefreshSizerElement(paramswindow, psizer, plugin, plugin_infos, None, False)
    -
    - if not self.PluginInfos[plugin]["right_visible"]:
    - paramswindow.Hide()
    -
    - rightminimizebutton_id = wx.NewId()
    - rightminimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=rightminimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')),
    - name='MinimizeButton', parent=rightwindow, pos=wx.Point(0, 0),
    - size=wx.Size(24, 24), style=wx.NO_BORDER)
    - make_genbitmaptogglebutton_flat(rightminimizebutton)
    - rightminimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png')))
    - rightminimizebutton.SetToggle(self.PluginInfos[plugin]["right_visible"])
    - rightparamssizer.AddWindow(rightminimizebutton, 0, border=5, flag=wx.ALL)
    -
    - def togglerightwindow(event):
    - if rightminimizebutton.GetToggle():
    - rightparamssizer.Show(0)
    - msizer.SetCols(1)
    - else:
    - rightparamssizer.Hide(0)
    - msizer.SetCols(len(plugin.PluginMethods))
    - self.PluginInfos[plugin]["right_visible"] = rightminimizebutton.GetToggle()
    - self.PLCConfigMainSizer.Layout()
    - self.RefreshScrollBars()
    - event.Skip()
    - rightminimizebutton.Bind(wx.EVT_BUTTON, togglerightwindow, id=rightminimizebutton_id)
    -
    - self.PluginInfos[plugin]["left"] = leftwindow
    - self.PluginInfos[plugin]["right"] = rightwindow
    - for child in self.PluginInfos[plugin]["children"]:
    - self.GenerateTreeBranch(child)
    - if not self.PluginInfos[child]["expanded"]:
    - self.CollapsePlugin(child)
    - if len(plugin_locations) > 0:
    - locations_infos = self.PluginInfos[plugin]["locations_infos"]
    - for location in plugin_locations:
    - locations_infos["root"]["children"].append("root.%s" % location["name"])
    - self.GenerateLocationTreeBranch(locations_infos, "root", location)
    - if not locations_infos["root"]["expanded"]:
    - self.CollapseLocation(locations_infos, "root")
    -
    - LOCATION_BITMAP = {LOCATION_PLUGIN: "CONFIGURATION",
    - LOCATION_MODULE: "RESOURCE",
    - LOCATION_GROUP: "PROGRAM",
    - LOCATION_VAR_INPUT: "VAR_INPUT",
    - LOCATION_VAR_OUTPUT: "VAR_OUTPUT",
    - LOCATION_VAR_MEMORY: "VAR_LOCAL"}
    -
    - def GenerateLocationTreeBranch(self, locations_infos, parent, location):
    - leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    - self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW)
    -
    - leftwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
    - leftwindow.SetSizer(leftwindowsizer)
    -
    - rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    - self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
    -
    - location_name = "%s.%s" % (parent, location["name"])
    - if not locations_infos.has_key(location_name):
    - locations_infos[location_name] = {"expanded" : False}
    -
    - if location["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]:
    - leftwindow.SetBackgroundColour(WINDOW_COLOUR)
    - rightwindow.SetBackgroundColour(WINDOW_COLOUR)
    -
    - st = wx.StaticText(leftwindow, -1)
    - st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    - st.SetLabel(location["location"])
    - leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT)
    -
    - expandbutton_id = wx.NewId()
    - expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')),
    - name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0),
    - size=wx.Size(13, 13), style=wx.NO_BORDER)
    - expandbutton.labelDelta = 0
    - expandbutton.SetBezelWidth(0)
    - expandbutton.SetUseFocusIndicator(False)
    - expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png')))
    - expandbutton.SetToggle(locations_infos[location_name]["expanded"])
    -
    - if len(location["children"]) > 0:
    - def togglebutton(event):
    - if expandbutton.GetToggle():
    - self.ExpandLocation(locations_infos, location_name)
    - else:
    - self.CollapseLocation(locations_infos, location_name)
    - locations_infos[location_name]["expanded"] = expandbutton.GetToggle()
    - self.PLCConfigMainSizer.Layout()
    - self.RefreshScrollBars()
    - event.Skip()
    - expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
    - else:
    - expandbutton.Enable(False)
    - leftwindowsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - else:
    - leftwindow.SetBackgroundColour(wx.WHITE)
    - rightwindow.SetBackgroundColour(wx.WHITE)
    -
    - leftwindowsizer.Add(wx.Size(20, 16), 0)
    -
    - sb = wx.StaticBitmap(leftwindow, -1)
    - sb.SetBitmap(wx.Bitmap(os.path.join(base_folder, "plcopeneditor", 'Images', '%s.png' % self.LOCATION_BITMAP[location["type"]])))
    - leftwindowsizer.AddWindow(sb, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - st_id = wx.NewId()
    - st = wx.StaticText(leftwindow, st_id, size=wx.DefaultSize, style=wx.NO_BORDER)
    - label = location["name"]
    - if location["type"] in [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]:
    - label += " (%s)" % location["location"]
    - infos = location.copy()
    - infos.pop("children")
    - st.SetFont(wx.Font(faces["size"] * 0.5, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]))
    - st.Bind(wx.EVT_LEFT_DOWN, self.GenerateLocationLeftDownFunction(infos))
    - else:
    - st.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    - st.SetLabel(label)
    - leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    -
    - locations_infos[location_name]["left"] = leftwindow
    - locations_infos[location_name]["right"] = rightwindow
    - locations_infos[location_name]["children"] = []
    - for child in location["children"]:
    - child_name = "%s.%s" % (location_name, child["name"])
    - locations_infos[location_name]["children"].append(child_name)
    - self.GenerateLocationTreeBranch(locations_infos, location_name, child)
    - if not locations_infos[location_name]["expanded"]:
    - self.CollapseLocation(locations_infos, location_name)
    -
    - def GenerateLocationLeftDownFunction(self, infos):
    - def OnLocationLeftDownFunction(event):
    - data = wx.TextDataObject(str((infos["location"], "location", infos["IEC_type"], infos["name"], infos["description"])))
    - dragSource = wx.DropSource(self)
    - dragSource.SetData(data)
    - dragSource.DoDragDrop()
    - event.Skip()
    - return OnLocationLeftDownFunction
    -
    - def RefreshAll(self):
    - self.RefreshPLCParams()
    - self.RefreshPluginTree()
    -
    - def GetItemChannelChangedFunction(self, plugin, value):
    - def OnPluginTreeItemChannelChanged(event):
    - res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value)
    - event.Skip()
    - return OnPluginTreeItemChannelChanged
    -
    - def _GetAddPluginFunction(self, name, plugin):
    - def OnPluginMenu(event):
    - wx.CallAfter(self.AddPlugin, name, plugin)
    - return OnPluginMenu
    -
    - def Gen_AddPluginMenu(self, plugin):
    - def AddPluginMenu(event):
    - main_menu = wx.Menu(title='')
    - if len(plugin.PlugChildsTypes) > 0:
    - for name, XSDClass, help in plugin.PlugChildsTypes:
    - new_id = wx.NewId()
    - main_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Append ")+help)
    - self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id)
    - self.PopupMenuXY(main_menu)
    - return AddPluginMenu
    -
    - def GetButtonCallBackFunction(self, plugin, method):
    - """ Generate the callbackfunc for a given plugin method"""
    - def OnButtonClick(event):
    - # Disable button to prevent re-entrant call
    - event.GetEventObject().Disable()
    - # Call
    - getattr(plugin,method)()
    - # Re-enable button
    - event.GetEventObject().Enable()
    - # Trigger refresh on Idle
    - wx.CallAfter(self.RefreshAll)
    - event.Skip()
    - return OnButtonClick
    -
    - def GetChoiceCallBackFunction(self, choicectrl, plugin, path):
    - def OnChoiceChanged(event):
    - res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
    - choicectrl.SetStringSelection(res)
    - event.Skip()
    - return OnChoiceChanged
    -
    - def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, plugin, path):
    - def OnChoiceContentChanged(event):
    - res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
    - if wx.VERSION < (2, 8, 0):
    - self.ParamsPanel.Freeze()
    - choicectrl.SetStringSelection(res)
    - infos = self.PluginRoot.GetParamsAttributes(path)
    - staticbox = staticboxsizer.GetStaticBox()
    - staticbox.SetLabel("%(name)s - %(value)s"%infos)
    - self.RefreshSizerElement(self.ParamsPanel, staticboxsizer, infos["children"], "%s.%s"%(path, infos["name"]), selected=selected)
    - self.ParamsPanelMainSizer.Layout()
    - self.ParamsPanel.Thaw()
    - self.ParamsPanel.Refresh()
    - else:
    - wx.CallAfter(self.RefreshAll)
    - event.Skip()
    - return OnChoiceContentChanged
    -
    - def GetTextCtrlCallBackFunction(self, textctrl, plugin, path):
    - def OnTextCtrlChanged(event):
    - res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue())
    - if res != textctrl.GetValue():
    - textctrl.ChangeValue(res)
    - event.Skip()
    - return OnTextCtrlChanged
    -
    - def GetCheckBoxCallBackFunction(self, chkbx, plugin, path):
    - def OnCheckBoxChanged(event):
    - res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked())
    - chkbx.SetValue(res)
    - event.Skip()
    - return OnCheckBoxChanged
    -
    - def ClearSizer(self, sizer):
    - staticboxes = []
    - for item in sizer.GetChildren():
    - if item.IsSizer():
    - item_sizer = item.GetSizer()
    - self.ClearSizer(item_sizer)
    - if isinstance(item_sizer, wx.StaticBoxSizer):
    - staticboxes.append(item_sizer.GetStaticBox())
    - sizer.Clear(True)
    - for staticbox in staticboxes:
    - staticbox.Destroy()
    -
    - def RefreshSizerElement(self, parent, sizer, plugin, elements, path, clean = True):
    - if clean:
    - if wx.VERSION < (2, 8, 0):
    - self.ClearSizer(sizer)
    - else:
    - sizer.Clear(True)
    - first = True
    - for element_infos in elements:
    - if path:
    - element_path = "%s.%s"%(path, element_infos["name"])
    - else:
    - element_path = element_infos["name"]
    - if element_infos["type"] == "element":
    - label = element_infos["name"]
    - staticbox = wx.StaticBox(id=-1, label=_(label),
    - name='%s_staticbox'%element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0)
    - staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
    - if first:
    - sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW|wx.TOP)
    - else:
    - sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW)
    - self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path)
    - else:
    - boxsizer = wx.FlexGridSizer(cols=3, rows=1)
    - boxsizer.AddGrowableCol(1)
    - if first:
    - sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.ALL)
    - else:
    - sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
    - staticbitmap = GenStaticBitmap(ID=-1, bitmapname="%s.png"%element_infos["name"],
    - name="%s_bitmap"%element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0)
    - boxsizer.AddWindow(staticbitmap, 0, border=5, flag=wx.RIGHT)
    - label = element_infos["name"]
    - statictext = wx.StaticText(id=-1, label="%s:"%_(label),
    - name="%s_label"%element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    - boxsizer.AddWindow(statictext, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT)
    - id = wx.NewId()
    - if isinstance(element_infos["type"], types.ListType):
    - combobox = wx.ComboBox(id=id, name=element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.Size(150, 28), style=wx.CB_READONLY)
    - boxsizer.AddWindow(combobox, 0, border=0, flag=0)
    - if element_infos["use"] == "optional":
    - combobox.Append("")
    - if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType):
    - for choice, xsdclass in element_infos["type"]:
    - combobox.Append(choice)
    - name = element_infos["name"]
    - value = element_infos["value"]
    - staticbox = wx.StaticBox(id=-1, label="%s - %s"%(_(name), _(value)),
    - name='%s_staticbox'%element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0)
    - staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
    - sizer.AddSizer(staticboxsizer, 0, border=5, flag=wx.GROW|wx.BOTTOM)
    - self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path)
    - callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, plugin, element_path)
    - else:
    - for choice in element_infos["type"]:
    - combobox.Append(choice)
    - callback = self.GetChoiceCallBackFunction(combobox, plugin, element_path)
    - if element_infos["value"] is None:
    - combobox.SetStringSelection("")
    - else:
    - combobox.SetStringSelection(element_infos["value"])
    - combobox.Bind(wx.EVT_COMBOBOX, callback, id=id)
    - elif isinstance(element_infos["type"], types.DictType):
    - scmin = -(2**31)
    - scmax = 2**31-1
    - if "min" in element_infos["type"]:
    - scmin = element_infos["type"]["min"]
    - if "max" in element_infos["type"]:
    - scmax = element_infos["type"]["max"]
    - spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.Size(150, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
    - spinctrl.SetRange(scmin,scmax)
    - boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
    - spinctrl.SetValue(element_infos["value"])
    - spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
    - else:
    - if element_infos["type"] == "boolean":
    - checkbox = wx.CheckBox(id=id, name=element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.Size(17, 25), style=0)
    - boxsizer.AddWindow(checkbox, 0, border=0, flag=0)
    - checkbox.SetValue(element_infos["value"])
    - checkbox.Bind(wx.EVT_CHECKBOX, self.GetCheckBoxCallBackFunction(checkbox, plugin, element_path), id=id)
    - elif element_infos["type"] in ["unsignedLong", "long","integer"]:
    - if element_infos["type"].startswith("unsigned"):
    - scmin = 0
    - else:
    - scmin = -(2**31)
    - scmax = 2**31-1
    - spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent,
    - pos=wx.Point(0, 0), size=wx.Size(150, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
    - spinctrl.SetRange(scmin, scmax)
    - boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
    - spinctrl.SetValue(element_infos["value"])
    - spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
    - else:
    - choices = cPickle.loads(str(self.Config.Read(element_path, cPickle.dumps([""]))))
    - textctrl = TextCtrlAutoComplete.TextCtrlAutoComplete(id=id,
    - name=element_infos["name"],
    - parent=parent,
    - choices=choices,
    - element_path=element_path,
    - pos=wx.Point(0, 0),
    - size=wx.Size(150, 25),
    - style=0)
    -
    - boxsizer.AddWindow(textctrl, 0, border=0, flag=0)
    - textctrl.ChangeValue(str(element_infos["value"]))
    - textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path))
    - first = False
    -
    - def ResetView(self):
    - IDEFrame.ResetView(self)
    - self.PluginInfos = {}
    - if self.PluginRoot is not None:
    - self.PluginRoot.CloseProject()
    - self.PluginRoot = None
    - self.Log.flush()
    - self.DebugVariablePanel.SetDataProducer(None)
    -
    - def OnNewProjectMenu(self, event):
    - if not self.Config.HasEntry("lastopenedfolder"):
    - defaultpath = os.path.expanduser("~")
    - else:
    - defaultpath = self.Config.Read("lastopenedfolder")
    -
    - dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
    - if dialog.ShowModal() == wx.ID_OK:
    - projectpath = dialog.GetPath()
    - dialog.Destroy()
    - self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
    - self.Config.Flush()
    - self.ResetView()
    - self.PluginRoot = PluginsRoot(self, self.Log)
    - self.Controler = self.PluginRoot
    - result = self.PluginRoot.NewProject(projectpath)
    - if not result:
    - self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
    - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    - self.RefreshAll()
    - else:
    - self.ResetView()
    - self.ShowErrorMessage(result)
    - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    -
    - def OnOpenProjectMenu(self, event):
    - if not self.Config.HasEntry("lastopenedfolder"):
    - defaultpath = os.path.expanduser("~")
    - else:
    - defaultpath = self.Config.Read("lastopenedfolder")
    -
    - dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
    - if dialog.ShowModal() == wx.ID_OK:
    - projectpath = dialog.GetPath()
    - if os.path.isdir(projectpath):
    - self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
    - self.Config.Flush()
    - self.ResetView()
    - self.PluginRoot = PluginsRoot(self, self.Log)
    - self.Controler = self.PluginRoot
    - result = self.PluginRoot.LoadProject(projectpath)
    - if not result:
    - self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
    - self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    - self.RefreshAll()
    - else:
    - self.ResetView()
    - self.ShowErrorMessage(result)
    - else:
    - self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath)
    - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    - dialog.Destroy()
    -
    - def OnCloseProjectMenu(self, event):
    - if self.PluginRoot is not None:
    - if self.PluginRoot.ProjectTestModified():
    - dialog = wx.MessageDialog(self,
    - _("Save changes ?"),
    - _("Close Application"),
    - wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
    - answer = dialog.ShowModal()
    - dialog.Destroy()
    - if answer == wx.ID_YES:
    - self.PluginRoot.SaveProject()
    - elif answer == wx.ID_CANCEL:
    - return
    - self.ResetView()
    - self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    - self.RefreshAll()
    -
    - def OnSaveProjectMenu(self, event):
    - if self.PluginRoot is not None:
    - self.PluginRoot.SaveProject()
    - self.RefreshAll()
    - self.RefreshTitle()
    -
    - def OnPropertiesMenu(self, event):
    - self.ShowProperties()
    -
    - def OnQuitMenu(self, event):
    - self.Close()
    -
    - def OnBeremizMenu(self, event):
    - open_pdf(Bpath( "doc", "manual_beremiz.pdf"))
    -
    - def OnAboutMenu(self, event):
    - OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc","about.html"), wx.Size(550, 500))
    -
    - def GetAddButtonFunction(self, plugin, window):
    - def AddButtonFunction(event):
    - if plugin and len(plugin.PlugChildsTypes) > 0:
    - plugin_menu = wx.Menu(title='')
    - for name, XSDClass, help in plugin.PlugChildsTypes:
    - new_id = wx.NewId()
    - plugin_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=name)
    - self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id)
    - window_pos = window.GetPosition()
    - wx.CallAfter(self.PLCConfig.PopupMenu, plugin_menu)
    - event.Skip()
    - return AddButtonFunction
    -
    - def GetDeleteButtonFunction(self, plugin):
    - def DeleteButtonFunction(event):
    - wx.CallAfter(self.DeletePlugin, plugin)
    - event.Skip()
    - return DeleteButtonFunction
    -
    - def AddPlugin(self, PluginType, plugin):
    - dialog = wx.TextEntryDialog(self, _("Please enter a name for plugin:"), _("Add Plugin"), "", wx.OK|wx.CANCEL)
    - if dialog.ShowModal() == wx.ID_OK:
    - PluginName = dialog.GetValue()
    - plugin.PlugAddChild(PluginName, PluginType)
    - self.RefreshPluginTree()
    - self.PluginRoot.RefreshPluginsBlockLists()
    - dialog.Destroy()
    -
    - def DeletePlugin(self, plugin):
    - dialog = wx.MessageDialog(self, _("Really delete plugin ?"), _("Remove plugin"), wx.YES_NO|wx.NO_DEFAULT)
    - if dialog.ShowModal() == wx.ID_YES:
    - self.PluginInfos.pop(plugin)
    - plugin.PlugRemove()
    - del plugin
    - self.PluginRoot.RefreshPluginsBlockLists()
    - self.RefreshPluginTree()
    - dialog.Destroy()
    -
    -#-------------------------------------------------------------------------------
    -# Exception Handler
    -#-------------------------------------------------------------------------------
    -
    -Max_Traceback_List_Size = 20
    -
    -def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path):
    - trcbck_lst = []
    - for i,line in enumerate(traceback.extract_tb(e_tb)):
    - trcbck = " " + str(i+1) + _(". ")
    - if line[0].find(os.getcwd()) == -1:
    - trcbck += _("file : ") + str(line[0]) + _(", ")
    - else:
    - trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ")
    - trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2])
    - trcbck_lst.append(trcbck)
    -
    - # Allow clicking....
    - cap = wx.Window_GetCapture()
    - if cap:
    - cap.ReleaseMouse()
    -
    - dlg = wx.SingleChoiceDialog(None,
    - _("""
    -An unhandled exception (bug) occured. Bug report saved at :
    -(%s)
    -
    -Please contact LOLITech at:
    -+33 (0)3 29 57 60 42
    -or please be kind enough to send this file to:
    -bugs_beremiz@lolitech.fr
    -
    -You should now restart Beremiz.
    -
    -Traceback:
    -""") % bug_report_path +
    - str(e_type) + " : " + str(e_value),
    - _("Error"),
    - trcbck_lst)
    - try:
    - res = (dlg.ShowModal() == wx.ID_OK)
    - finally:
    - dlg.Destroy()
    -
    - return res
    -
    -def Display_Error_Dialog(e_value):
    - message = wxMessageDialog(None, str(e_value), _("Error"), wxOK|wxICON_ERROR)
    - message.ShowModal()
    - message.Destroy()
    -
    -def get_last_traceback(tb):
    - while tb.tb_next:
    - tb = tb.tb_next
    - return tb
    -
    -
    -def format_namespace(d, indent=' '):
    - return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
    -
    -
    -ignored_exceptions = [] # a problem with a line in a module is only reported once per session
    -
    -def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
    -
    - def handle_exception(e_type, e_value, e_traceback):
    - traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
    - last_tb = get_last_traceback(e_traceback)
    - ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
    - if str(e_value).startswith("!!!"):
    - Display_Error_Dialog(e_value)
    - elif ex not in ignored_exceptions:
    - date = time.ctime()
    - bug_report_path = path+os.sep+"bug_report_"+date.replace(':','-').replace(' ','_')+".txt"
    - result = Display_Exception_Dialog(e_type,e_value,e_traceback,bug_report_path)
    - if result:
    - ignored_exceptions.append(ex)
    - info = {
    - 'app-title' : wx.GetApp().GetAppName(), # app_title
    - 'app-version' : app_version,
    - 'wx-version' : wx.VERSION_STRING,
    - 'wx-platform' : wx.Platform,
    - 'python-version' : platform.python_version(), #sys.version.split()[0],
    - 'platform' : platform.platform(),
    - 'e-type' : e_type,
    - 'e-value' : e_value,
    - 'date' : date,
    - 'cwd' : os.getcwd(),
    - }
    - if e_traceback:
    - info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
    - last_tb = get_last_traceback(e_traceback)
    - exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
    - info['locals'] = format_namespace(exception_locals)
    - if 'self' in exception_locals:
    - info['self'] = format_namespace(exception_locals['self'].__dict__)
    -
    - output = open(bug_report_path,'w')
    - lst = info.keys()
    - lst.sort()
    - for a in lst:
    - output.write(a+":\n"+str(info[a])+"\n\n")
    -
    - #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
    - sys.excepthook = handle_exception
    -
    -if __name__ == '__main__':
    - # Install a exception handle for bug reports
    - AddExceptHook(os.getcwd(),__version__)
    -
    - frame = Beremiz(None, projectOpen, buildpath)
    - frame.Show()
    - splash.Close()
    - app.MainLoop()
    +#!/usr/bin/env python
    +# -*- coding: utf-8 -*-
    +
    +#This file is part of Beremiz, a Integrated Development Environment for
    +#programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
    +#
    +#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
    +
    +__version__ = "$Revision$"
    +
    +import os, sys, getopt, wx
    +import tempfile
    +import shutil
    +import random
    +
    +CWD = os.path.split(os.path.realpath(__file__))[0]
    +
    +def Bpath(*args):
    + return os.path.join(CWD,*args)
    +
    +if __name__ == '__main__':
    + def usage():
    + print "\nUsage of Beremiz.py :"
    + print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0]
    +
    + try:
    + opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
    + except getopt.GetoptError:
    + # print help information and exit:
    + usage()
    + sys.exit(2)
    +
    + for o, a in opts:
    + if o in ("-h", "--help"):
    + usage()
    + sys.exit()
    +
    + if len(args) > 2:
    + usage()
    + sys.exit()
    + elif len(args) == 1:
    + projectOpen = args[0]
    + buildpath = None
    + elif len(args) == 2:
    + projectOpen = args[0]
    + buildpath = args[1]
    + else:
    + projectOpen = None
    + buildpath = None
    +
    + app = wx.PySimpleApp()
    + app.SetAppName('beremiz')
    + wx.InitAllImageHandlers()
    +
    + bmp = wx.Image(Bpath("images","splash.png")).ConvertToBitmap()
    + splash=wx.SplashScreen(bmp,wx.SPLASH_CENTRE_ON_SCREEN, 1000, None)
    + wx.Yield()
    +
    +# Import module for internationalization
    +import gettext
    +import __builtin__
    +
    +# Get folder containing translation files
    +localedir = os.path.join(CWD,"locale")
    +# Get the default language
    +langid = wx.LANGUAGE_DEFAULT
    +# Define translation domain (name of translation files)
    +domain = "Beremiz"
    +
    +# Define locale for wx
    +loc = __builtin__.__dict__.get('loc', None)
    +if loc is None:
    + loc = wx.Locale(langid)
    + __builtin__.__dict__['loc'] = loc
    +# Define location for searching translation files
    +loc.AddCatalogLookupPathPrefix(localedir)
    +# Define locale domain
    +loc.AddCatalog(domain)
    +
    +def unicode_translation(message):
    + return wx.GetTranslation(message).encode("utf-8")
    +
    +if __name__ == '__main__':
    + __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation
    +
    +#Quick hack to be able to find Beremiz IEC tools. Should be config params.
    +base_folder = os.path.split(sys.path[0])[0]
    +sys.path.append(base_folder)
    +sys.path.append(os.path.join(base_folder, "plcopeneditor"))
    +sys.path.append(os.path.join(base_folder, "docutils"))
    +
    +import wx.lib.buttons, wx.lib.statbmp
    +import TextCtrlAutoComplete, cPickle
    +import types, time, re, platform, time, traceback, commands
    +from plugger import PluginsRoot, MATIEC_ERROR_MODEL
    +from wxPopen import ProcessLogger
    +
    +from docutils import *
    +from PLCOpenEditor import IDEFrame, Viewer, AppendMenu, TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, TYPESTREE, INSTANCESTREE, LIBRARYTREE, SCALING
    +from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
    +
    +SCROLLBAR_UNIT = 10
    +WINDOW_COLOUR = wx.Colour(240,240,240)
    +TITLE_COLOUR = wx.Colour(200,200,220)
    +CHANGED_TITLE_COLOUR = wx.Colour(220,200,220)
    +CHANGED_WINDOW_COLOUR = wx.Colour(255,240,240)
    +
    +if wx.Platform == '__WXMSW__':
    + faces = { 'times': 'Times New Roman',
    + 'mono' : 'Courier New',
    + 'helv' : 'Arial',
    + 'other': 'Comic Sans MS',
    + 'size' : 16,
    + }
    +else:
    + faces = { 'times': 'Times',
    + 'mono' : 'Courier',
    + 'helv' : 'Helvetica',
    + 'other': 'new century schoolbook',
    + 'size' : 18,
    + }
    +
    +# Some helpers to tweak GenBitmapTextButtons
    +# TODO: declare customized classes instead.
    +gen_mini_GetBackgroundBrush = lambda obj:lambda dc: wx.Brush(obj.GetParent().GetBackgroundColour(), wx.SOLID)
    +gen_textbutton_GetLabelSize = lambda obj:lambda:(wx.lib.buttons.GenButton._GetLabelSize(obj)[:-1] + (False,))
    +
    +def make_genbitmaptogglebutton_flat(button):
    + button.GetBackgroundBrush = gen_mini_GetBackgroundBrush(button)
    + button.labelDelta = 0
    + button.SetBezelWidth(0)
    + button.SetUseFocusIndicator(False)
    +
    +# Patch wx.lib.imageutils so that gray is supported on alpha images
    +import wx.lib.imageutils
    +from wx.lib.imageutils import grayOut as old_grayOut
    +def grayOut(anImage):
    + if anImage.HasAlpha():
    + AlphaData = anImage.GetAlphaData()
    + else :
    + AlphaData = None
    +
    + old_grayOut(anImage)
    +
    + if AlphaData is not None:
    + anImage.SetAlphaData(AlphaData)
    +
    +wx.lib.imageutils.grayOut = grayOut
    +
    +class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton):
    + def _GetLabelSize(self):
    + """ used internally """
    + w, h = self.GetTextExtent(self.GetLabel())
    + if not self.bmpLabel:
    + return w, h, False # if there isn't a bitmap use the size of the text
    +
    + w_bmp = self.bmpLabel.GetWidth()+2
    + h_bmp = self.bmpLabel.GetHeight()+2
    + height = h + h_bmp
    + if w_bmp > w:
    + width = w_bmp
    + else:
    + width = w
    + return width, height, False
    +
    + def DrawLabel(self, dc, width, height, dw=0, dy=0):
    + bmp = self.bmpLabel
    + if bmp != None: # if the bitmap is used
    + if self.bmpDisabled and not self.IsEnabled():
    + bmp = self.bmpDisabled
    + if self.bmpFocus and self.hasFocus:
    + bmp = self.bmpFocus
    + if self.bmpSelected and not self.up:
    + bmp = self.bmpSelected
    + bw,bh = bmp.GetWidth(), bmp.GetHeight()
    + if not self.up:
    + dw = dy = self.labelDelta
    + hasMask = bmp.GetMask() != None
    + else:
    + bw = bh = 0 # no bitmap -> size is zero
    +
    + dc.SetFont(self.GetFont())
    + if self.IsEnabled():
    + dc.SetTextForeground(self.GetForegroundColour())
    + else:
    + dc.SetTextForeground(wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
    +
    + label = self.GetLabel()
    + tw, th = dc.GetTextExtent(label) # size of text
    + if not self.up:
    + dw = dy = self.labelDelta
    +
    + pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre
    + pos_y = (height-bh-th)/2+dy
    + if bmp !=None:
    + dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available
    + pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre
    + pos_y += bh + 2
    +
    + dc.DrawText(label, pos_x, pos_y) # draw the text
    +
    +
    +class GenStaticBitmap(wx.lib.statbmp.GenStaticBitmap):
    + """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32,
    + and accept image name as __init__ parameter, fail silently if file do not exist"""
    + def __init__(self, parent, ID, bitmapname,
    + pos = wx.DefaultPosition, size = wx.DefaultSize,
    + style = 0,
    + name = "genstatbmp"):
    +
    + bitmappath = Bpath( "images", bitmapname)
    + if os.path.isfile(bitmappath):
    + bitmap = wx.Bitmap(bitmappath)
    + else:
    + bitmap = None
    + wx.lib.statbmp.GenStaticBitmap.__init__(self, parent, ID, bitmap,
    + pos, size,
    + style,
    + name)
    +
    + def OnPaint(self, event):
    + dc = wx.PaintDC(self)
    + colour = self.GetParent().GetBackgroundColour()
    + dc.SetPen(wx.Pen(colour))
    + dc.SetBrush(wx.Brush(colour ))
    + dc.DrawRectangle(0, 0, *dc.GetSizeTuple())
    + if self._bitmap:
    + dc.DrawBitmap(self._bitmap, 0, 0, True)
    +
    +
    +class LogPseudoFile:
    + """ Base class for file like objects to facilitate StdOut for the Shell."""
    + def __init__(self, output):
    + self.red_white = wx.TextAttr("RED", "WHITE")
    + self.red_yellow = wx.TextAttr("RED", "YELLOW")
    + self.black_white = wx.TextAttr("BLACK", "WHITE")
    + self.default_style = None
    + self.output = output
    +
    + def write(self, s, style = None):
    + if style is None : style=self.black_white
    + self.output.Freeze();
    + if self.default_style != style:
    + self.output.SetDefaultStyle(style)
    + self.default_style = style
    + self.output.AppendText(s)
    + self.output.ScrollLines(s.count('\n')+1)
    + self.output.ShowPosition(self.output.GetLastPosition())
    + self.output.Thaw()
    +
    + def write_warning(self, s):
    + self.write(s,self.red_white)
    +
    + def write_error(self, s):
    + self.write(s,self.red_yellow)
    +
    + def flush(self):
    + self.output.SetValue("")
    +
    + def isatty(self):
    + return false
    +
    +[ID_BEREMIZ, ID_BEREMIZMAINSPLITTER,
    + ID_BEREMIZPLCCONFIG, ID_BEREMIZLOGCONSOLE,
    + ID_BEREMIZINSPECTOR] = [wx.NewId() for _init_ctrls in range(5)]
    +
    +[ID_BEREMIZRUNMENUBUILD, ID_BEREMIZRUNMENUSIMULATE,
    + ID_BEREMIZRUNMENURUN, ID_BEREMIZRUNMENUSAVELOG,
    +] = [wx.NewId() for _init_coll_EditMenu_Items in range(4)]
    +
    +class Beremiz(IDEFrame):
    +
    + def _init_coll_FileMenu_Items(self, parent):
    + AppendMenu(parent, help='', id=wx.ID_NEW,
    + kind=wx.ITEM_NORMAL, text=_(u'New\tCTRL+N'))
    + AppendMenu(parent, help='', id=wx.ID_OPEN,
    + kind=wx.ITEM_NORMAL, text=_(u'Open\tCTRL+O'))
    + AppendMenu(parent, help='', id=wx.ID_SAVE,
    + kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
    + AppendMenu(parent, help='', id=wx.ID_CLOSE,
    + kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W'))
    + AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL,
    + kind=wx.ITEM_NORMAL, text=_(u'Close Project'))
    + parent.AppendSeparator()
    + AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
    + kind=wx.ITEM_NORMAL, text=_(u'Page Setup'))
    + AppendMenu(parent, help='', id=wx.ID_PREVIEW,
    + kind=wx.ITEM_NORMAL, text=_(u'Preview'))
    + AppendMenu(parent, help='', id=wx.ID_PRINT,
    + kind=wx.ITEM_NORMAL, text=_(u'Print'))
    + parent.AppendSeparator()
    + AppendMenu(parent, help='', id=wx.ID_PROPERTIES,
    + kind=wx.ITEM_NORMAL, text=_(u'Properties'))
    + parent.AppendSeparator()
    + AppendMenu(parent, help='', id=wx.ID_EXIT,
    + kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
    +
    + self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW)
    + self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN)
    + self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
    + self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
    + self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL)
    + self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
    + self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
    + self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
    + self.Bind(wx.EVT_MENU, self.OnPropertiesMenu, id=wx.ID_PROPERTIES)
    + self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
    +
    + def _init_coll_HelpMenu_Items(self, parent):
    + parent.Append(help='', id=wx.ID_HELP,
    + kind=wx.ITEM_NORMAL, text=_(u'Beremiz\tF1'))
    + parent.Append(help='', id=wx.ID_ABOUT,
    + kind=wx.ITEM_NORMAL, text=_(u'About'))
    + self.Bind(wx.EVT_MENU, self.OnBeremizMenu, id=wx.ID_HELP)
    + self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT)
    +
    + def _init_coll_PLCConfigMainSizer_Items(self, parent):
    + parent.AddSizer(self.PLCParamsSizer, 0, border=10, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT)
    + parent.AddSizer(self.PluginTreeSizer, 0, border=10, flag=wx.BOTTOM|wx.LEFT|wx.RIGHT)
    +
    + def _init_coll_PLCConfigMainSizer_Growables(self, parent):
    + parent.AddGrowableCol(0)
    + parent.AddGrowableRow(1)
    +
    + def _init_coll_PluginTreeSizer_Growables(self, parent):
    + parent.AddGrowableCol(0)
    + parent.AddGrowableCol(1)
    +
    + def _init_beremiz_sizers(self):
    + self.PLCConfigMainSizer = wx.FlexGridSizer(cols=1, hgap=2, rows=2, vgap=2)
    + self.PLCParamsSizer = wx.BoxSizer(wx.VERTICAL)
    + #self.PluginTreeSizer = wx.FlexGridSizer(cols=3, hgap=0, rows=0, vgap=2)
    + self.PluginTreeSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=0, vgap=2)
    +
    + self._init_coll_PLCConfigMainSizer_Items(self.PLCConfigMainSizer)
    + self._init_coll_PLCConfigMainSizer_Growables(self.PLCConfigMainSizer)
    + self._init_coll_PluginTreeSizer_Growables(self.PluginTreeSizer)
    +
    + self.PLCConfig.SetSizer(self.PLCConfigMainSizer)
    +
    + def _init_ctrls(self, prnt):
    + IDEFrame._init_ctrls(self, prnt)
    +
    + self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=ID_BEREMIZINSPECTOR)
    + accel = wx.AcceleratorTable([wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), ID_BEREMIZINSPECTOR)])
    + self.SetAcceleratorTable(accel)
    +
    + self.PLCConfig = wx.ScrolledWindow(id=ID_BEREMIZPLCCONFIG,
    + name='PLCConfig', parent=self.LeftNoteBook, pos=wx.Point(0, 0),
    + size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER|wx.HSCROLL|wx.VSCROLL)
    + self.PLCConfig.SetBackgroundColour(wx.WHITE)
    + self.PLCConfig.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
    + self.PLCConfig.Bind(wx.EVT_SIZE, self.OnMoveWindow)
    + self.PLCConfig.Bind(wx.EVT_MOUSEWHEEL, self.OnPLCConfigScroll)
    + self.BottomNoteBook.InsertPage(0, self.PLCConfig, _("Topology"), True)
    +
    + self.LogConsole = wx.TextCtrl(id=ID_BEREMIZLOGCONSOLE, value='',
    + name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0),
    + size=wx.Size(0, 0), style=wx.TE_MULTILINE|wx.TE_RICH2)
    + self.LogConsole.Bind(wx.EVT_LEFT_DCLICK, self.OnLogConsoleDClick)
    + self.BottomNoteBook.AddPage(self.LogConsole, _("Log Console"))
    +
    + self._init_beremiz_sizers()
    +
    + def __init__(self, parent, projectOpen=None, buildpath=None, plugin_root=None, debug=True):
    + IDEFrame.__init__(self, parent, debug)
    + self.Config = wx.ConfigBase.Get()
    +
    + self.Log = LogPseudoFile(self.LogConsole)
    +
    + self.local_runtime = None
    + self.runtime_port = None
    + self.local_runtime_tmpdir = None
    +
    + self.DisableEvents = False
    + # Variable allowing disabling of PLCConfig scroll when Popup shown
    + self.ScrollingEnabled = True
    +
    + self.PluginInfos = {}
    +
    + if projectOpen is not None and os.path.isdir(projectOpen):
    + self.PluginRoot = PluginsRoot(self, self.Log)
    + self.Controler = self.PluginRoot
    + result = self.PluginRoot.LoadProject(projectOpen, buildpath)
    + if not result:
    + self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
    + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    + self.RefreshAll()
    + else:
    + self.ResetView()
    + self.ShowErrorMessage(result)
    + else:
    + self.PluginRoot = plugin_root
    + self.Controler = plugin_root
    + if plugin_root is not None:
    + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    + self.RefreshAll()
    +
    + # Add beremiz's icon in top left corner of the frame
    + self.SetIcon(wx.Icon(Bpath( "images", "brz.ico"), wx.BITMAP_TYPE_ICO))
    +
    + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame)
    +
    + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU)
    +
    + def RefreshTitle(self):
    + name = _("Beremiz")
    + if self.PluginRoot is not None:
    + projectname = self.PluginRoot.GetProjectName()
    + if self.PluginRoot.PlugTestModified():
    + projectname = "~%s~" % projectname
    + self.SetTitle("%s - %s" % (name, projectname))
    + else:
    + self.SetTitle(name)
    +
    + def StartLocalRuntime(self, taskbaricon = True):
    + if self.local_runtime is None or self.local_runtime.finished:
    + # create temporary directory for runtime working directory
    + self.local_runtime_tmpdir = tempfile.mkdtemp()
    + # choose an arbitrary random port for runtime
    + self.runtime_port = int(random.random() * 1000) + 61131
    + # launch local runtime
    + self.local_runtime = ProcessLogger(self.Log,
    + "\"%s\" \"%s\" -p %s -i localhost %s %s"%(sys.executable,
    + Bpath("Beremiz_service.py"),
    + self.runtime_port,
    + {False : "-x 0", True :"-x 1"}[taskbaricon],
    + self.local_runtime_tmpdir),
    + no_gui=False)
    + self.local_runtime.spin(timeout=500, keyword = "working", kill_it = False)
    + return self.runtime_port
    +
    + def KillLocalRuntime(self):
    + if self.local_runtime is not None:
    + # shutdown local runtime
    + self.local_runtime.kill(gently=False)
    + # clear temp dir
    + shutil.rmtree(self.local_runtime_tmpdir)
    +
    + def OnOpenWidgetInspector(self, evt):
    + # Activate the widget inspection tool
    + from wx.lib.inspection import InspectionTool
    + if not InspectionTool().initialized:
    + InspectionTool().Init()
    +
    + # Find a widget to be selected in the tree. Use either the
    + # one under the cursor, if any, or this frame.
    + wnd = wx.FindWindowAtPointer()
    + if not wnd:
    + wnd = self
    + InspectionTool().Show(wnd, True)
    +
    + def OnLogConsoleDClick(self, event):
    + wx.CallAfter(self.SearchLineForError)
    + event.Skip()
    +
    + def SearchLineForError(self):
    + if self.PluginRoot is not None:
    + text = self.LogConsole.GetRange(0, self.LogConsole.GetInsertionPoint())
    + line = self.LogConsole.GetLineText(len(text.splitlines()) - 1)
    + result = MATIEC_ERROR_MODEL.match(line)
    + if result is not None:
    + first_line, first_column, last_line, last_column, error = result.groups()
    + infos = self.PluginRoot.ShowError(self.Log,
    + (int(first_line), int(first_column)),
    + (int(last_line), int(last_column)))
    +
    + def OnCloseFrame(self, event):
    + if self.PluginRoot is not None:
    + if self.PluginRoot.ProjectTestModified():
    + dialog = wx.MessageDialog(self,
    + _("Save changes ?"),
    + _("Close Application"),
    + wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
    + answer = dialog.ShowModal()
    + dialog.Destroy()
    + if answer == wx.ID_YES:
    + self.PluginRoot.SaveProject()
    + event.Skip()
    + elif answer == wx.ID_NO:
    + event.Skip()
    + return
    + else:
    + event.Veto()
    + return
    +
    + self.KillLocalRuntime()
    +
    + event.Skip()
    +
    + def OnMoveWindow(self, event):
    + self.GetBestSize()
    + self.RefreshScrollBars()
    + event.Skip()
    +
    + def EnableScrolling(self, enable):
    + self.ScrollingEnabled = enable
    +
    + def OnPLCConfigScroll(self, event):
    + if self.ScrollingEnabled:
    + event.Skip()
    +
    + def OnPanelLeftDown(self, event):
    + focused = self.FindFocus()
    + if isinstance(focused, TextCtrlAutoComplete.TextCtrlAutoComplete):
    + focused.DismissListBox()
    + event.Skip()
    +
    + def RefreshFileMenu(self):
    + if self.PluginRoot is not None:
    + selected = self.TabsOpened.GetSelection()
    + if selected >= 0:
    + graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
    + else:
    + graphic_viewer = False
    + if self.TabsOpened.GetPageCount() > 0:
    + self.FileMenu.Enable(wx.ID_CLOSE, True)
    + if graphic_viewer:
    + self.FileMenu.Enable(wx.ID_PREVIEW, True)
    + self.FileMenu.Enable(wx.ID_PRINT, True)
    + else:
    + self.FileMenu.Enable(wx.ID_PREVIEW, False)
    + self.FileMenu.Enable(wx.ID_PRINT, False)
    + else:
    + self.FileMenu.Enable(wx.ID_CLOSE, False)
    + self.FileMenu.Enable(wx.ID_PREVIEW, False)
    + self.FileMenu.Enable(wx.ID_PRINT, False)
    + self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
    + self.FileMenu.Enable(wx.ID_SAVE, True)
    + self.FileMenu.Enable(wx.ID_PROPERTIES, True)
    + self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
    + else:
    + self.FileMenu.Enable(wx.ID_CLOSE, False)
    + self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
    + self.FileMenu.Enable(wx.ID_PREVIEW, False)
    + self.FileMenu.Enable(wx.ID_PRINT, False)
    + self.FileMenu.Enable(wx.ID_SAVE, False)
    + self.FileMenu.Enable(wx.ID_PROPERTIES, False)
    + self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
    +
    + def RefreshScrollBars(self):
    + xstart, ystart = self.PLCConfig.GetViewStart()
    + window_size = self.PLCConfig.GetClientSize()
    + sizer = self.PLCConfig.GetSizer()
    + if sizer:
    + maxx, maxy = sizer.GetMinSize()
    + self.PLCConfig.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
    + maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT,
    + max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT)),
    + max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)))
    +
    + def RefreshPLCParams(self):
    + self.Freeze()
    + self.ClearSizer(self.PLCParamsSizer)
    +
    + if self.PluginRoot is not None:
    + plcwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    + if self.PluginRoot.PlugTestModified():
    + bkgdclr = CHANGED_TITLE_COLOUR
    + else:
    + bkgdclr = TITLE_COLOUR
    +
    + if self.PluginRoot not in self.PluginInfos:
    + self.PluginInfos[self.PluginRoot] = {"right_visible" : False}
    +
    + plcwindow.SetBackgroundColour(TITLE_COLOUR)
    + plcwindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
    + self.PLCParamsSizer.AddWindow(plcwindow, 0, border=0, flag=wx.GROW)
    +
    + plcwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
    + plcwindow.SetSizer(plcwindowsizer)
    +
    + st = wx.StaticText(plcwindow, -1)
    + st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    + st.SetLabel(self.PluginRoot.GetProjectName())
    + plcwindowsizer.AddWindow(st, 0, border=5, flag=wx.ALL|wx.ALIGN_CENTER)
    +
    + addbutton_id = wx.NewId()
    + addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')),
    + name='AddPluginButton', parent=plcwindow, pos=wx.Point(0, 0),
    + size=wx.Size(16, 16), style=wx.NO_BORDER)
    + addbutton.SetToolTipString(_("Add a sub plugin"))
    + addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(self.PluginRoot), id=addbutton_id)
    + plcwindowsizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
    +
    + plcwindowmainsizer = wx.BoxSizer(wx.VERTICAL)
    + plcwindowsizer.AddSizer(plcwindowmainsizer, 0, border=5, flag=wx.ALL)
    +
    + plcwindowbuttonsizer = wx.BoxSizer(wx.HORIZONTAL)
    + plcwindowmainsizer.AddSizer(plcwindowbuttonsizer, 0, border=0, flag=wx.ALIGN_CENTER)
    +
    + msizer = self.GenerateMethodButtonSizer(self.PluginRoot, plcwindow, not self.PluginInfos[self.PluginRoot]["right_visible"])
    + plcwindowbuttonsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW)
    +
    + paramswindow = wx.Panel(plcwindow, -1, size=wx.Size(-1, -1), style=wx.TAB_TRAVERSAL)
    + paramswindow.SetBackgroundColour(TITLE_COLOUR)
    + paramswindow.Bind(wx.EVT_LEFT_DOWN, self.OnPanelLeftDown)
    + plcwindowbuttonsizer.AddWindow(paramswindow, 0, border=0, flag=0)
    +
    + psizer = wx.BoxSizer(wx.HORIZONTAL)
    + paramswindow.SetSizer(psizer)
    +
    + plugin_infos = self.PluginRoot.GetParamsAttributes()
    + self.RefreshSizerElement(paramswindow, psizer, self.PluginRoot, plugin_infos, None, False)
    +
    + if not self.PluginInfos[self.PluginRoot]["right_visible"]:
    + paramswindow.Hide()
    +
    + minimizebutton_id = wx.NewId()
    + minimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=minimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')),
    + name='MinimizeButton', parent=plcwindow, pos=wx.Point(0, 0),
    + size=wx.Size(24, 24), style=wx.NO_BORDER)
    + make_genbitmaptogglebutton_flat(minimizebutton)
    + minimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png')))
    + minimizebutton.SetToggle(self.PluginInfos[self.PluginRoot]["right_visible"])
    + plcwindowbuttonsizer.AddWindow(minimizebutton, 0, border=5, flag=wx.ALL)
    +
    + def togglewindow(event):
    + if minimizebutton.GetToggle():
    + paramswindow.Show()
    + msizer.SetCols(1)
    + else:
    + paramswindow.Hide()
    + msizer.SetCols(len(self.PluginRoot.PluginMethods))
    + self.PluginInfos[self.PluginRoot]["right_visible"] = minimizebutton.GetToggle()
    + self.PLCConfigMainSizer.Layout()
    + self.RefreshScrollBars()
    + event.Skip()
    + minimizebutton.Bind(wx.EVT_BUTTON, togglewindow, id=minimizebutton_id)
    +
    + self.PluginInfos[self.PluginRoot]["main"] = plcwindow
    + self.PluginInfos[self.PluginRoot]["params"] = paramswindow
    +
    + self.PLCConfigMainSizer.Layout()
    + self.RefreshScrollBars()
    + self.Thaw()
    +
    + def GenerateMethodButtonSizer(self, plugin, parent, horizontal = True):
    + normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])
    + mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"])
    + if horizontal:
    + msizer = wx.FlexGridSizer(cols=len(plugin.PluginMethods))
    + else:
    + msizer = wx.FlexGridSizer(cols=1)
    + for plugin_method in plugin.PluginMethods:
    + if "method" in plugin_method and plugin_method.get("shown",True):
    + id = wx.NewId()
    + label = plugin_method["name"]
    + button = GenBitmapTextButton(id=id, parent=parent,
    + bitmap=wx.Bitmap(Bpath( "%s.png"%plugin_method.get("bitmap", os.path.join("images", "Unknown")))), label=label,
    + name=label, pos=wx.DefaultPosition, style=wx.NO_BORDER)
    + button.SetFont(normal_bt_font)
    + button.SetToolTipString(plugin_method["tooltip"])
    + button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(plugin, plugin_method["method"]), id=id)
    + # a fancy underline on mouseover
    + def setFontStyle(b, s):
    + def fn(event):
    + b.SetFont(s)
    + b.Refresh()
    + event.Skip()
    + return fn
    + button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font))
    + button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font))
    + #hack to force size to mini
    + if not plugin_method.get("enabled",True):
    + button.Disable()
    + msizer.AddWindow(button, 0, border=0, flag=wx.ALIGN_CENTER)
    + return msizer
    +
    + def RefreshPluginTree(self):
    + self.Freeze()
    + self.ClearSizer(self.PluginTreeSizer)
    + if self.PluginRoot is not None:
    + for child in self.PluginRoot.IECSortedChilds():
    + self.GenerateTreeBranch(child)
    + if not self.PluginInfos[child]["expanded"]:
    + self.CollapsePlugin(child)
    + self.PLCConfigMainSizer.Layout()
    + self.RefreshScrollBars()
    + self.Thaw()
    +
    + def SetPluginParamsAttribute(self, plugin, *args, **kwargs):
    + res, StructChanged = plugin.SetParamsAttribute(*args, **kwargs)
    + if StructChanged:
    + wx.CallAfter(self.RefreshPluginTree)
    + else:
    + if plugin == self.PluginRoot:
    + bkgdclr = CHANGED_TITLE_COLOUR
    + items = ["main", "params"]
    + else:
    + bkgdclr = CHANGED_WINDOW_COLOUR
    + items = ["left", "right", "params"]
    + for i in items:
    + self.PluginInfos[plugin][i].SetBackgroundColour(bkgdclr)
    + self.PluginInfos[plugin][i].Refresh()
    + return res
    +
    + def ExpandPlugin(self, plugin, force = False):
    + for child in self.PluginInfos[plugin]["children"]:
    + self.PluginInfos[child]["left"].Show()
    + self.PluginInfos[child]["right"].Show()
    + if force or not self.PluginInfos[child]["expanded"]:
    + self.ExpandPlugin(child, force)
    + if force:
    + self.PluginInfos[child]["expanded"] = True
    + locations_infos = self.PluginInfos[plugin].get("locations_infos", None)
    + if locations_infos is not None:
    + if force or locations_infos["root"]["expanded"]:
    + self.ExpandLocation(locations_infos, "root", force)
    + if force:
    + locations_infos["root"]["expanded"] = True
    +
    +
    + def CollapsePlugin(self, plugin, force = False):
    + for child in self.PluginInfos[plugin]["children"]:
    + self.PluginInfos[child]["left"].Hide()
    + self.PluginInfos[child]["right"].Hide()
    + self.CollapsePlugin(child, force)
    + if force:
    + self.PluginInfos[child]["expanded"] = False
    + locations_infos = self.PluginInfos[plugin].get("locations_infos", None)
    + if locations_infos is not None:
    + self.CollapseLocation(locations_infos, "root", force)
    + if force:
    + locations_infos["root"]["expanded"] = False
    +
    + def ExpandLocation(self, locations_infos, group, force = False):
    + for child in locations_infos[group]["children"]:
    + locations_infos[child]["left"].Show()
    + locations_infos[child]["right"].Show()
    + if force or locations_infos[child]["expanded"]:
    + self.ExpandLocation(locations_infos, child, force)
    + if force:
    + locations_infos[child]["expanded"] = True
    +
    + def CollapseLocation(self, locations_infos, group, force = False):
    + for child in locations_infos[group]["children"]:
    + locations_infos[child]["left"].Hide()
    + locations_infos[child]["right"].Hide()
    + self.CollapseLocation(locations_infos, child, force)
    + if force:
    + locations_infos[child]["expanded"] = False
    +
    + def GenerateTreeBranch(self, plugin):
    + leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    + if plugin.PlugTestModified():
    + bkgdclr=CHANGED_WINDOW_COLOUR
    + else:
    + bkgdclr=WINDOW_COLOUR
    +
    + leftwindow.SetBackgroundColour(bkgdclr)
    +
    + if not self.PluginInfos.has_key(plugin):
    + self.PluginInfos[plugin] = {"expanded" : False, "right_visible" : False}
    +
    + self.PluginInfos[plugin]["children"] = plugin.IECSortedChilds()
    + plugin_locations = []
    + if len(self.PluginInfos[plugin]["children"]) == 0:
    + plugin_locations = plugin.GetVariableLocationTree()["children"]
    + if not self.PluginInfos[plugin].has_key("locations_infos"):
    + self.PluginInfos[plugin]["locations_infos"] = {"root": {"expanded" : False}}
    +
    + self.PluginInfos[plugin]["locations_infos"]["root"]["children"] = []
    +
    + self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW)
    +
    + leftwindowsizer = wx.FlexGridSizer(cols=1, rows=2)
    + leftwindowsizer.AddGrowableCol(0)
    + leftwindow.SetSizer(leftwindowsizer)
    +
    + leftbuttonmainsizer = wx.FlexGridSizer(cols=3, rows=1)
    + leftbuttonmainsizer.AddGrowableCol(0)
    + leftwindowsizer.AddSizer(leftbuttonmainsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT) #|wx.TOP
    +
    + leftbuttonsizer = wx.BoxSizer(wx.HORIZONTAL)
    + leftbuttonmainsizer.AddSizer(leftbuttonsizer, 0, border=5, flag=wx.GROW|wx.RIGHT)
    +
    + leftsizer = wx.BoxSizer(wx.VERTICAL)
    + leftbuttonsizer.AddSizer(leftsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + rolesizer = wx.BoxSizer(wx.HORIZONTAL)
    + leftsizer.AddSizer(rolesizer, 0, border=0, flag=wx.GROW|wx.RIGHT)
    +
    + enablebutton_id = wx.NewId()
    + enablebutton = wx.lib.buttons.GenBitmapToggleButton(id=enablebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Disabled.png')),
    + name='EnableButton', parent=leftwindow, size=wx.Size(16, 16), pos=wx.Point(0, 0), style=0)#wx.NO_BORDER)
    + enablebutton.SetToolTipString(_("Enable/Disable this plugin"))
    + make_genbitmaptogglebutton_flat(enablebutton)
    + enablebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Enabled.png')))
    + enablebutton.SetToggle(plugin.MandatoryParams[1].getEnabled())
    + def toggleenablebutton(event):
    + res = self.SetPluginParamsAttribute(plugin, "BaseParams.Enabled", enablebutton.GetToggle())
    + enablebutton.SetToggle(res)
    + event.Skip()
    + enablebutton.Bind(wx.EVT_BUTTON, toggleenablebutton, id=enablebutton_id)
    + rolesizer.AddWindow(enablebutton, 0, border=0, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + roletext = wx.StaticText(leftwindow, -1)
    + roletext.SetLabel(plugin.PlugHelp)
    + rolesizer.AddWindow(roletext, 0, border=5, flag=wx.RIGHT|wx.ALIGN_LEFT)
    +
    + plugin_IECChannel = plugin.BaseParams.getIEC_Channel()
    +
    + iecsizer = wx.BoxSizer(wx.HORIZONTAL)
    + leftsizer.AddSizer(iecsizer, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + st = wx.StaticText(leftwindow, -1)
    + st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    + st.SetLabel(plugin.GetFullIEC_Channel())
    + iecsizer.AddWindow(st, 0, border=0, flag=0)
    +
    + updownsizer = wx.BoxSizer(wx.VERTICAL)
    + iecsizer.AddSizer(updownsizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
    +
    + if plugin_IECChannel > 0:
    + ieccdownbutton_id = wx.NewId()
    + ieccdownbutton = wx.lib.buttons.GenBitmapButton(id=ieccdownbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCDown.png')),
    + name='IECCDownButton', parent=leftwindow, pos=wx.Point(0, 0),
    + size=wx.Size(16, 16), style=wx.NO_BORDER)
    + ieccdownbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel - 1), id=ieccdownbutton_id)
    + updownsizer.AddWindow(ieccdownbutton, 0, border=0, flag=wx.ALIGN_LEFT)
    +
    + ieccupbutton_id = wx.NewId()
    + ieccupbutton = wx.lib.buttons.GenBitmapTextButton(id=ieccupbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'IECCUp.png')),
    + name='IECCUpButton', parent=leftwindow, pos=wx.Point(0, 0),
    + size=wx.Size(16, 16), style=wx.NO_BORDER)
    + ieccupbutton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(plugin, plugin_IECChannel + 1), id=ieccupbutton_id)
    + updownsizer.AddWindow(ieccupbutton, 0, border=0, flag=wx.ALIGN_LEFT)
    +
    + adddeletesizer = wx.BoxSizer(wx.VERTICAL)
    + iecsizer.AddSizer(adddeletesizer, 0, border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
    +
    + deletebutton_id = wx.NewId()
    + deletebutton = wx.lib.buttons.GenBitmapButton(id=deletebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Delete.png')),
    + name='DeletePluginButton', parent=leftwindow, pos=wx.Point(0, 0),
    + size=wx.Size(16, 16), style=wx.NO_BORDER)
    + deletebutton.SetToolTipString(_("Delete this plugin"))
    + deletebutton.Bind(wx.EVT_BUTTON, self.GetDeleteButtonFunction(plugin), id=deletebutton_id)
    + adddeletesizer.AddWindow(deletebutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
    +
    + if len(plugin.PlugChildsTypes) > 0:
    + addbutton_id = wx.NewId()
    + addbutton = wx.lib.buttons.GenBitmapButton(id=addbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Add.png')),
    + name='AddPluginButton', parent=leftwindow, pos=wx.Point(0, 0),
    + size=wx.Size(16, 16), style=wx.NO_BORDER)
    + addbutton.SetToolTipString(_("Add a sub plugin"))
    + addbutton.Bind(wx.EVT_BUTTON, self.Gen_AddPluginMenu(plugin), id=addbutton_id)
    + adddeletesizer.AddWindow(addbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER)
    +
    + expandbutton_id = wx.NewId()
    + expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')),
    + name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0),
    + size=wx.Size(13, 13), style=wx.NO_BORDER)
    + expandbutton.labelDelta = 0
    + expandbutton.SetBezelWidth(0)
    + expandbutton.SetUseFocusIndicator(False)
    + expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png')))
    +
    + if len(self.PluginInfos[plugin]["children"]) > 0:
    + expandbutton.SetToggle(self.PluginInfos[plugin]["expanded"])
    + def togglebutton(event):
    + if expandbutton.GetToggle():
    + self.ExpandPlugin(plugin)
    + else:
    + self.CollapsePlugin(plugin)
    + self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
    + self.PLCConfigMainSizer.Layout()
    + self.RefreshScrollBars()
    + event.Skip()
    + expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
    + elif len(plugin_locations) > 0:
    + locations_infos = self.PluginInfos[plugin]["locations_infos"]
    + expandbutton.SetToggle(locations_infos["root"]["expanded"])
    + def togglebutton(event):
    + if expandbutton.GetToggle():
    + self.ExpandLocation(locations_infos, "root")
    + else:
    + self.CollapseLocation(locations_infos, "root")
    + self.PluginInfos[plugin]["expanded"] = expandbutton.GetToggle()
    + locations_infos["root"]["expanded"] = expandbutton.GetToggle()
    + self.PLCConfigMainSizer.Layout()
    + self.RefreshScrollBars()
    + event.Skip()
    + expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
    + else:
    + expandbutton.Enable(False)
    + iecsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + tc_id = wx.NewId()
    + tc = wx.TextCtrl(leftwindow, tc_id, size=wx.Size(150, 25), style=wx.NO_BORDER)
    + tc.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    + tc.ChangeValue(plugin.MandatoryParams[1].getName())
    + tc.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(tc, plugin, "BaseParams.Name"), id=tc_id)
    + iecsizer.AddWindow(tc, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    + rightwindow.SetBackgroundColour(bkgdclr)
    +
    + self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
    +
    + rightwindowmainsizer = wx.BoxSizer(wx.VERTICAL)
    + rightwindow.SetSizer(rightwindowmainsizer)
    +
    + rightwindowsizer = wx.FlexGridSizer(cols=2, rows=1)
    + rightwindowsizer.AddGrowableCol(1)
    + rightwindowsizer.AddGrowableRow(0)
    + rightwindowmainsizer.AddSizer(rightwindowsizer, 0, border=8, flag=wx.TOP|wx.GROW)
    +
    + msizer = self.GenerateMethodButtonSizer(plugin, rightwindow, not self.PluginInfos[plugin]["right_visible"])
    + rightwindowsizer.AddSizer(msizer, 0, border=0, flag=wx.GROW)
    +
    + rightparamssizer = wx.BoxSizer(wx.HORIZONTAL)
    + rightwindowsizer.AddSizer(rightparamssizer, 0, border=0, flag=wx.ALIGN_RIGHT)
    +
    + paramswindow = wx.Panel(rightwindow, -1, size=wx.Size(-1, -1))
    + paramswindow.SetBackgroundColour(bkgdclr)
    +
    + psizer = wx.BoxSizer(wx.HORIZONTAL)
    + paramswindow.SetSizer(psizer)
    + self.PluginInfos[plugin]["params"] = paramswindow
    +
    + rightparamssizer.AddWindow(paramswindow, 0, border=5, flag=wx.ALL)
    +
    + plugin_infos = plugin.GetParamsAttributes()
    + self.RefreshSizerElement(paramswindow, psizer, plugin, plugin_infos, None, False)
    +
    + if not self.PluginInfos[plugin]["right_visible"]:
    + paramswindow.Hide()
    +
    + rightminimizebutton_id = wx.NewId()
    + rightminimizebutton = wx.lib.buttons.GenBitmapToggleButton(id=rightminimizebutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'Maximize.png')),
    + name='MinimizeButton', parent=rightwindow, pos=wx.Point(0, 0),
    + size=wx.Size(24, 24), style=wx.NO_BORDER)
    + make_genbitmaptogglebutton_flat(rightminimizebutton)
    + rightminimizebutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'Minimize.png')))
    + rightminimizebutton.SetToggle(self.PluginInfos[plugin]["right_visible"])
    + rightparamssizer.AddWindow(rightminimizebutton, 0, border=5, flag=wx.ALL)
    +
    + def togglerightwindow(event):
    + if rightminimizebutton.GetToggle():
    + rightparamssizer.Show(0)
    + msizer.SetCols(1)
    + else:
    + rightparamssizer.Hide(0)
    + msizer.SetCols(len(plugin.PluginMethods))
    + self.PluginInfos[plugin]["right_visible"] = rightminimizebutton.GetToggle()
    + self.PLCConfigMainSizer.Layout()
    + self.RefreshScrollBars()
    + event.Skip()
    + rightminimizebutton.Bind(wx.EVT_BUTTON, togglerightwindow, id=rightminimizebutton_id)
    +
    + self.PluginInfos[plugin]["left"] = leftwindow
    + self.PluginInfos[plugin]["right"] = rightwindow
    + for child in self.PluginInfos[plugin]["children"]:
    + self.GenerateTreeBranch(child)
    + if not self.PluginInfos[child]["expanded"]:
    + self.CollapsePlugin(child)
    + if len(plugin_locations) > 0:
    + locations_infos = self.PluginInfos[plugin]["locations_infos"]
    + for location in plugin_locations:
    + locations_infos["root"]["children"].append("root.%s" % location["name"])
    + self.GenerateLocationTreeBranch(locations_infos, "root", location)
    + if not locations_infos["root"]["expanded"]:
    + self.CollapseLocation(locations_infos, "root")
    +
    + LOCATION_BITMAP = {LOCATION_PLUGIN: "CONFIGURATION",
    + LOCATION_MODULE: "RESOURCE",
    + LOCATION_GROUP: "PROGRAM",
    + LOCATION_VAR_INPUT: "VAR_INPUT",
    + LOCATION_VAR_OUTPUT: "VAR_OUTPUT",
    + LOCATION_VAR_MEMORY: "VAR_LOCAL"}
    +
    + def GenerateLocationTreeBranch(self, locations_infos, parent, location):
    + leftwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    + self.PluginTreeSizer.AddWindow(leftwindow, 0, border=0, flag=wx.GROW)
    +
    + leftwindowsizer = wx.BoxSizer(wx.HORIZONTAL)
    + leftwindow.SetSizer(leftwindowsizer)
    +
    + rightwindow = wx.Panel(self.PLCConfig, -1, size=wx.Size(-1, -1))
    + self.PluginTreeSizer.AddWindow(rightwindow, 0, border=0, flag=wx.GROW)
    +
    + location_name = "%s.%s" % (parent, location["name"])
    + if not locations_infos.has_key(location_name):
    + locations_infos[location_name] = {"expanded" : False}
    +
    + if location["type"] in [LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP]:
    + leftwindow.SetBackgroundColour(WINDOW_COLOUR)
    + rightwindow.SetBackgroundColour(WINDOW_COLOUR)
    +
    + st = wx.StaticText(leftwindow, -1)
    + st.SetFont(wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    + st.SetLabel(location["location"])
    + leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT)
    +
    + expandbutton_id = wx.NewId()
    + expandbutton = wx.lib.buttons.GenBitmapToggleButton(id=expandbutton_id, bitmap=wx.Bitmap(Bpath( 'images', 'plus.png')),
    + name='ExpandButton', parent=leftwindow, pos=wx.Point(0, 0),
    + size=wx.Size(13, 13), style=wx.NO_BORDER)
    + expandbutton.labelDelta = 0
    + expandbutton.SetBezelWidth(0)
    + expandbutton.SetUseFocusIndicator(False)
    + expandbutton.SetBitmapSelected(wx.Bitmap(Bpath( 'images', 'minus.png')))
    + expandbutton.SetToggle(locations_infos[location_name]["expanded"])
    +
    + if len(location["children"]) > 0:
    + def togglebutton(event):
    + if expandbutton.GetToggle():
    + self.ExpandLocation(locations_infos, location_name)
    + else:
    + self.CollapseLocation(locations_infos, location_name)
    + locations_infos[location_name]["expanded"] = expandbutton.GetToggle()
    + self.PLCConfigMainSizer.Layout()
    + self.RefreshScrollBars()
    + event.Skip()
    + expandbutton.Bind(wx.EVT_BUTTON, togglebutton, id=expandbutton_id)
    + else:
    + expandbutton.Enable(False)
    + leftwindowsizer.AddWindow(expandbutton, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + else:
    + leftwindow.SetBackgroundColour(wx.WHITE)
    + rightwindow.SetBackgroundColour(wx.WHITE)
    +
    + leftwindowsizer.Add(wx.Size(20, 16), 0)
    +
    + sb = wx.StaticBitmap(leftwindow, -1)
    + sb.SetBitmap(wx.Bitmap(os.path.join(base_folder, "plcopeneditor", 'Images', '%s.png' % self.LOCATION_BITMAP[location["type"]])))
    + leftwindowsizer.AddWindow(sb, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + st_id = wx.NewId()
    + st = wx.StaticText(leftwindow, st_id, size=wx.DefaultSize, style=wx.NO_BORDER)
    + label = location["name"]
    + if location["type"] in [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]:
    + label += " (%s)" % location["location"]
    + infos = location.copy()
    + infos.pop("children")
    + st.SetFont(wx.Font(faces["size"] * 0.5, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]))
    + st.Bind(wx.EVT_LEFT_DOWN, self.GenerateLocationLeftDownFunction(infos))
    + else:
    + st.SetFont(wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName = faces["helv"]))
    + st.SetLabel(label)
    + leftwindowsizer.AddWindow(st, 0, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
    +
    + locations_infos[location_name]["left"] = leftwindow
    + locations_infos[location_name]["right"] = rightwindow
    + locations_infos[location_name]["children"] = []
    + for child in location["children"]:
    + child_name = "%s.%s" % (location_name, child["name"])
    + locations_infos[location_name]["children"].append(child_name)
    + self.GenerateLocationTreeBranch(locations_infos, location_name, child)
    + if not locations_infos[location_name]["expanded"]:
    + self.CollapseLocation(locations_infos, location_name)
    +
    + def GenerateLocationLeftDownFunction(self, infos):
    + def OnLocationLeftDownFunction(event):
    + data = wx.TextDataObject(str((infos["location"], "location", infos["IEC_type"], infos["name"], infos["description"])))
    + dragSource = wx.DropSource(self)
    + dragSource.SetData(data)
    + dragSource.DoDragDrop()
    + event.Skip()
    + return OnLocationLeftDownFunction
    +
    + def RefreshAll(self):
    + self.RefreshPLCParams()
    + self.RefreshPluginTree()
    +
    + def GetItemChannelChangedFunction(self, plugin, value):
    + def OnPluginTreeItemChannelChanged(event):
    + res = self.SetPluginParamsAttribute(plugin, "BaseParams.IEC_Channel", value)
    + event.Skip()
    + return OnPluginTreeItemChannelChanged
    +
    + def _GetAddPluginFunction(self, name, plugin):
    + def OnPluginMenu(event):
    + wx.CallAfter(self.AddPlugin, name, plugin)
    + return OnPluginMenu
    +
    + def Gen_AddPluginMenu(self, plugin):
    + def AddPluginMenu(event):
    + main_menu = wx.Menu(title='')
    + if len(plugin.PlugChildsTypes) > 0:
    + for name, XSDClass, help in plugin.PlugChildsTypes:
    + new_id = wx.NewId()
    + main_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Append ")+help)
    + self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id)
    + self.PopupMenuXY(main_menu)
    + return AddPluginMenu
    +
    + def GetButtonCallBackFunction(self, plugin, method):
    + """ Generate the callbackfunc for a given plugin method"""
    + def OnButtonClick(event):
    + # Disable button to prevent re-entrant call
    + event.GetEventObject().Disable()
    + # Call
    + getattr(plugin,method)()
    + # Re-enable button
    + event.GetEventObject().Enable()
    + # Trigger refresh on Idle
    + wx.CallAfter(self.RefreshAll)
    + event.Skip()
    + return OnButtonClick
    +
    + def GetChoiceCallBackFunction(self, choicectrl, plugin, path):
    + def OnChoiceChanged(event):
    + res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
    + choicectrl.SetStringSelection(res)
    + event.Skip()
    + return OnChoiceChanged
    +
    + def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, plugin, path):
    + def OnChoiceContentChanged(event):
    + res = self.SetPluginParamsAttribute(plugin, path, choicectrl.GetStringSelection())
    + if wx.VERSION < (2, 8, 0):
    + self.ParamsPanel.Freeze()
    + choicectrl.SetStringSelection(res)
    + infos = self.PluginRoot.GetParamsAttributes(path)
    + staticbox = staticboxsizer.GetStaticBox()
    + staticbox.SetLabel("%(name)s - %(value)s"%infos)
    + self.RefreshSizerElement(self.ParamsPanel, staticboxsizer, infos["children"], "%s.%s"%(path, infos["name"]), selected=selected)
    + self.ParamsPanelMainSizer.Layout()
    + self.ParamsPanel.Thaw()
    + self.ParamsPanel.Refresh()
    + else:
    + wx.CallAfter(self.RefreshAll)
    + event.Skip()
    + return OnChoiceContentChanged
    +
    + def GetTextCtrlCallBackFunction(self, textctrl, plugin, path):
    + def OnTextCtrlChanged(event):
    + res = self.SetPluginParamsAttribute(plugin, path, textctrl.GetValue())
    + if res != textctrl.GetValue():
    + textctrl.ChangeValue(res)
    + event.Skip()
    + return OnTextCtrlChanged
    +
    + def GetCheckBoxCallBackFunction(self, chkbx, plugin, path):
    + def OnCheckBoxChanged(event):
    + res = self.SetPluginParamsAttribute(plugin, path, chkbx.IsChecked())
    + chkbx.SetValue(res)
    + event.Skip()
    + return OnCheckBoxChanged
    +
    + def ClearSizer(self, sizer):
    + staticboxes = []
    + for item in sizer.GetChildren():
    + if item.IsSizer():
    + item_sizer = item.GetSizer()
    + self.ClearSizer(item_sizer)
    + if isinstance(item_sizer, wx.StaticBoxSizer):
    + staticboxes.append(item_sizer.GetStaticBox())
    + sizer.Clear(True)
    + for staticbox in staticboxes:
    + staticbox.Destroy()
    +
    + def RefreshSizerElement(self, parent, sizer, plugin, elements, path, clean = True):
    + if clean:
    + if wx.VERSION < (2, 8, 0):
    + self.ClearSizer(sizer)
    + else:
    + sizer.Clear(True)
    + first = True
    + for element_infos in elements:
    + if path:
    + element_path = "%s.%s"%(path, element_infos["name"])
    + else:
    + element_path = element_infos["name"]
    + if element_infos["type"] == "element":
    + label = element_infos["name"]
    + staticbox = wx.StaticBox(id=-1, label=_(label),
    + name='%s_staticbox'%element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0)
    + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
    + if first:
    + sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW|wx.TOP)
    + else:
    + sizer.AddSizer(staticboxsizer, 0, border=0, flag=wx.GROW)
    + self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path)
    + else:
    + boxsizer = wx.FlexGridSizer(cols=3, rows=1)
    + boxsizer.AddGrowableCol(1)
    + if first:
    + sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.ALL)
    + else:
    + sizer.AddSizer(boxsizer, 0, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
    + staticbitmap = GenStaticBitmap(ID=-1, bitmapname="%s.png"%element_infos["name"],
    + name="%s_bitmap"%element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0)
    + boxsizer.AddWindow(staticbitmap, 0, border=5, flag=wx.RIGHT)
    + label = element_infos["name"]
    + statictext = wx.StaticText(id=-1, label="%s:"%_(label),
    + name="%s_label"%element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0)
    + boxsizer.AddWindow(statictext, 0, border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT)
    + id = wx.NewId()
    + if isinstance(element_infos["type"], types.ListType):
    + combobox = wx.ComboBox(id=id, name=element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.Size(300, 28), style=wx.CB_READONLY)
    + boxsizer.AddWindow(combobox, 0, border=0, flag=0)
    + if element_infos["use"] == "optional":
    + combobox.Append("")
    + if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType):
    + for choice, xsdclass in element_infos["type"]:
    + combobox.Append(choice)
    + name = element_infos["name"]
    + value = element_infos["value"]
    + staticbox = wx.StaticBox(id=-1, label="%s - %s"%(_(name), _(value)),
    + name='%s_staticbox'%element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.Size(10, 0), style=0)
    + staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
    + sizer.AddSizer(staticboxsizer, 0, border=5, flag=wx.GROW|wx.BOTTOM)
    + self.RefreshSizerElement(parent, staticboxsizer, plugin, element_infos["children"], element_path)
    + callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, plugin, element_path)
    + else:
    + for choice in element_infos["type"]:
    + combobox.Append(choice)
    + callback = self.GetChoiceCallBackFunction(combobox, plugin, element_path)
    + if element_infos["value"] is None:
    + combobox.SetStringSelection("")
    + else:
    + combobox.SetStringSelection(element_infos["value"])
    + combobox.Bind(wx.EVT_COMBOBOX, callback, id=id)
    + elif isinstance(element_infos["type"], types.DictType):
    + scmin = -(2**31)
    + scmax = 2**31-1
    + if "min" in element_infos["type"]:
    + scmin = element_infos["type"]["min"]
    + if "max" in element_infos["type"]:
    + scmax = element_infos["type"]["max"]
    + spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
    + spinctrl.SetRange(scmin,scmax)
    + boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
    + spinctrl.SetValue(element_infos["value"])
    + spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
    + else:
    + if element_infos["type"] == "boolean":
    + checkbox = wx.CheckBox(id=id, name=element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.Size(17, 25), style=0)
    + boxsizer.AddWindow(checkbox, 0, border=0, flag=0)
    + checkbox.SetValue(element_infos["value"])
    + checkbox.Bind(wx.EVT_CHECKBOX, self.GetCheckBoxCallBackFunction(checkbox, plugin, element_path), id=id)
    + elif element_infos["type"] in ["unsignedLong", "long","integer"]:
    + if element_infos["type"].startswith("unsigned"):
    + scmin = 0
    + else:
    + scmin = -(2**31)
    + scmax = 2**31-1
    + spinctrl = wx.SpinCtrl(id=id, name=element_infos["name"], parent=parent,
    + pos=wx.Point(0, 0), size=wx.Size(300, 25), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT)
    + spinctrl.SetRange(scmin, scmax)
    + boxsizer.AddWindow(spinctrl, 0, border=0, flag=0)
    + spinctrl.SetValue(element_infos["value"])
    + spinctrl.Bind(wx.EVT_SPINCTRL, self.GetTextCtrlCallBackFunction(spinctrl, plugin, element_path), id=id)
    + else:
    + choices = cPickle.loads(str(self.Config.Read(element_path, cPickle.dumps([""]))))
    + textctrl = TextCtrlAutoComplete.TextCtrlAutoComplete(id=id,
    + name=element_infos["name"],
    + parent=parent,
    + appframe=self,
    + choices=choices,
    + element_path=element_path,
    + pos=wx.Point(0, 0),
    + size=wx.Size(300, 25),
    + style=0)
    +
    + boxsizer.AddWindow(textctrl, 0, border=0, flag=0)
    + textctrl.ChangeValue(str(element_infos["value"]))
    + textctrl.Bind(wx.EVT_TEXT, self.GetTextCtrlCallBackFunction(textctrl, plugin, element_path))
    + first = False
    +
    + def ResetView(self):
    + IDEFrame.ResetView(self)
    + self.PluginInfos = {}
    + if self.PluginRoot is not None:
    + self.PluginRoot.CloseProject()
    + self.PluginRoot = None
    + self.Log.flush()
    + self.DebugVariablePanel.SetDataProducer(None)
    +
    + def OnNewProjectMenu(self, event):
    + if not self.Config.HasEntry("lastopenedfolder"):
    + defaultpath = os.path.expanduser("~")
    + else:
    + defaultpath = self.Config.Read("lastopenedfolder")
    +
    + dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
    + if dialog.ShowModal() == wx.ID_OK:
    + projectpath = dialog.GetPath()
    + dialog.Destroy()
    + self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
    + self.Config.Flush()
    + self.ResetView()
    + self.PluginRoot = PluginsRoot(self, self.Log)
    + self.Controler = self.PluginRoot
    + result = self.PluginRoot.NewProject(projectpath)
    + if not result:
    + self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
    + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    + self.RefreshAll()
    + else:
    + self.ResetView()
    + self.ShowErrorMessage(result)
    + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    +
    + def OnOpenProjectMenu(self, event):
    + if not self.Config.HasEntry("lastopenedfolder"):
    + defaultpath = os.path.expanduser("~")
    + else:
    + defaultpath = self.Config.Read("lastopenedfolder")
    +
    + dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, wx.DD_NEW_DIR_BUTTON)
    + if dialog.ShowModal() == wx.ID_OK:
    + projectpath = dialog.GetPath()
    + if os.path.isdir(projectpath):
    + self.Config.Write("lastopenedfolder", os.path.dirname(projectpath))
    + self.Config.Flush()
    + self.ResetView()
    + self.PluginRoot = PluginsRoot(self, self.Log)
    + self.Controler = self.PluginRoot
    + result = self.PluginRoot.LoadProject(projectpath)
    + if not result:
    + self.DebugVariablePanel.SetDataProducer(self.PluginRoot)
    + self._Refresh(TYPESTREE, INSTANCESTREE, LIBRARYTREE)
    + self.RefreshAll()
    + else:
    + self.ResetView()
    + self.ShowErrorMessage(result)
    + else:
    + self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath)
    + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    + dialog.Destroy()
    +
    + def OnCloseProjectMenu(self, event):
    + if self.PluginRoot is not None:
    + if self.PluginRoot.ProjectTestModified():
    + dialog = wx.MessageDialog(self,
    + _("Save changes ?"),
    + _("Close Application"),
    + wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
    + answer = dialog.ShowModal()
    + dialog.Destroy()
    + if answer == wx.ID_YES:
    + self.PluginRoot.SaveProject()
    + elif answer == wx.ID_CANCEL:
    + return
    + self.ResetView()
    + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    + self.RefreshAll()
    +
    + def OnSaveProjectMenu(self, event):
    + if self.PluginRoot is not None:
    + self.PluginRoot.SaveProject()
    + self.RefreshAll()
    + self.RefreshTitle()
    +
    + def OnPropertiesMenu(self, event):
    + self.ShowProperties()
    +
    + def OnQuitMenu(self, event):
    + self.Close()
    +
    + def OnBeremizMenu(self, event):
    + open_pdf(Bpath( "doc", "manual_beremiz.pdf"))
    +
    + def OnAboutMenu(self, event):
    + OpenHtmlFrame(self,_("About Beremiz"), Bpath("doc","about.html"), wx.Size(550, 500))
    +
    + def GetAddButtonFunction(self, plugin, window):
    + def AddButtonFunction(event):
    + if plugin and len(plugin.PlugChildsTypes) > 0:
    + plugin_menu = wx.Menu(title='')
    + for name, XSDClass, help in plugin.PlugChildsTypes:
    + new_id = wx.NewId()
    + plugin_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=name)
    + self.Bind(wx.EVT_MENU, self._GetAddPluginFunction(name, plugin), id=new_id)
    + window_pos = window.GetPosition()
    + wx.CallAfter(self.PLCConfig.PopupMenu, plugin_menu)
    + event.Skip()
    + return AddButtonFunction
    +
    + def GetDeleteButtonFunction(self, plugin):
    + def DeleteButtonFunction(event):
    + wx.CallAfter(self.DeletePlugin, plugin)
    + event.Skip()
    + return DeleteButtonFunction
    +
    + def AddPlugin(self, PluginType, plugin):
    + dialog = wx.TextEntryDialog(self, _("Please enter a name for plugin:"), _("Add Plugin"), "", wx.OK|wx.CANCEL)
    + if dialog.ShowModal() == wx.ID_OK:
    + PluginName = dialog.GetValue()
    + plugin.PlugAddChild(PluginName, PluginType)
    + self.RefreshPluginTree()
    + self.PluginRoot.RefreshPluginsBlockLists()
    + dialog.Destroy()
    +
    + def DeletePlugin(self, plugin):
    + dialog = wx.MessageDialog(self, _("Really delete plugin ?"), _("Remove plugin"), wx.YES_NO|wx.NO_DEFAULT)
    + if dialog.ShowModal() == wx.ID_YES:
    + self.PluginInfos.pop(plugin)
    + plugin.PlugRemove()
    + del plugin
    + self.PluginRoot.RefreshPluginsBlockLists()
    + self.RefreshPluginTree()
    + dialog.Destroy()
    +
    +#-------------------------------------------------------------------------------
    +# Exception Handler
    +#-------------------------------------------------------------------------------
    +
    +Max_Traceback_List_Size = 20
    +
    +def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path):
    + trcbck_lst = []
    + for i,line in enumerate(traceback.extract_tb(e_tb)):
    + trcbck = " " + str(i+1) + _(". ")
    + if line[0].find(os.getcwd()) == -1:
    + trcbck += _("file : ") + str(line[0]) + _(", ")
    + else:
    + trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ")
    + trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2])
    + trcbck_lst.append(trcbck)
    +
    + # Allow clicking....
    + cap = wx.Window_GetCapture()
    + if cap:
    + cap.ReleaseMouse()
    +
    + dlg = wx.SingleChoiceDialog(None,
    + _("""
    +An unhandled exception (bug) occured. Bug report saved at :
    +(%s)
    +
    +Please contact LOLITech at:
    ++33 (0)3 29 57 60 42
    +or please be kind enough to send this file to:
    +bugs_beremiz@lolitech.fr
    +
    +You should now restart Beremiz.
    +
    +Traceback:
    +""") % bug_report_path +
    + str(e_type) + " : " + str(e_value),
    + _("Error"),
    + trcbck_lst)
    + try:
    + res = (dlg.ShowModal() == wx.ID_OK)
    + finally:
    + dlg.Destroy()
    +
    + return res
    +
    +def Display_Error_Dialog(e_value):
    + message = wxMessageDialog(None, str(e_value), _("Error"), wxOK|wxICON_ERROR)
    + message.ShowModal()
    + message.Destroy()
    +
    +def get_last_traceback(tb):
    + while tb.tb_next:
    + tb = tb.tb_next
    + return tb
    +
    +
    +def format_namespace(d, indent=' '):
    + return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()])
    +
    +
    +ignored_exceptions = [] # a problem with a line in a module is only reported once per session
    +
    +def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]):
    +
    + def handle_exception(e_type, e_value, e_traceback):
    + traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func
    + last_tb = get_last_traceback(e_traceback)
    + ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno)
    + if str(e_value).startswith("!!!"):
    + Display_Error_Dialog(e_value)
    + elif ex not in ignored_exceptions:
    + date = time.ctime()
    + bug_report_path = path+os.sep+"bug_report_"+date.replace(':','-').replace(' ','_')+".txt"
    + result = Display_Exception_Dialog(e_type,e_value,e_traceback,bug_report_path)
    + if result:
    + ignored_exceptions.append(ex)
    + info = {
    + 'app-title' : wx.GetApp().GetAppName(), # app_title
    + 'app-version' : app_version,
    + 'wx-version' : wx.VERSION_STRING,
    + 'wx-platform' : wx.Platform,
    + 'python-version' : platform.python_version(), #sys.version.split()[0],
    + 'platform' : platform.platform(),
    + 'e-type' : e_type,
    + 'e-value' : e_value,
    + 'date' : date,
    + 'cwd' : os.getcwd(),
    + }
    + if e_traceback:
    + info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value)
    + last_tb = get_last_traceback(e_traceback)
    + exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred
    + info['locals'] = format_namespace(exception_locals)
    + if 'self' in exception_locals:
    + info['self'] = format_namespace(exception_locals['self'].__dict__)
    +
    + output = open(bug_report_path,'w')
    + lst = info.keys()
    + lst.sort()
    + for a in lst:
    + output.write(a+":\n"+str(info[a])+"\n\n")
    +
    + #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args)
    + sys.excepthook = handle_exception
    +
    +if __name__ == '__main__':
    + # Install a exception handle for bug reports
    + AddExceptHook(os.getcwd(),__version__)
    +
    + frame = Beremiz(None, projectOpen, buildpath)
    + frame.Show()
    + splash.Close()
    + app.MainLoop()
    --- a/TextCtrlAutoComplete.py Thu Oct 22 11:28:11 2009 +0200
    +++ b/TextCtrlAutoComplete.py Thu Oct 22 17:20:24 2009 +0200
    @@ -27,11 +27,88 @@
    MAX_ITEM_COUNT = 10
    MAX_ITEM_SHOWN = 6
    -ITEM_HEIGHT = 25
    +if wx.Platform == '__WXMSW__':
    + ITEM_INTERVAL_HEIGHT = 3
    +else:
    + ITEM_INTERVAL_HEIGHT = 6
    +
    +if wx.Platform == '__WXMSW__':
    + popupclass = wx.PopupTransientWindow
    +else:
    + popupclass = wx.PopupWindow
    +class PopupWithListbox(popupclass):
    +
    + def __init__(self, parent, choices=[]):
    + popupclass.__init__(self, parent, wx.SIMPLE_BORDER)
    +
    + self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT)
    + if not wx.Platform == '__WXMSW__':
    + self.ListBox.Bind(wx.EVT_LISTBOX, self.OnListBoxClick)
    + self.ListBox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnListBoxClick)
    +
    + self.SetChoices(choices)
    +
    + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
    +
    + def SetChoices(self, choices):
    + max_text_width = 0
    + max_text_height = 0
    +
    + self.ListBox.Clear()
    + for choice in choices:
    + self.ListBox.Append(choice)
    + w, h = self.ListBox.GetTextExtent(choice)
    + max_text_width = max(max_text_width, w)
    + max_text_height = max(max_text_height, h)
    +
    + itemcount = min(len(choices), MAX_ITEM_SHOWN)
    + width = self.Parent.GetSize()[0]
    + height = max_text_height * itemcount + ITEM_INTERVAL_HEIGHT * (itemcount + 1)
    + if max_text_width + 10 > width:
    + height += 15
    + size = wx.Size(width, height)
    + self.ListBox.SetSize(size)
    + self.SetClientSize(size)
    +
    + def MoveSelection(self, direction):
    + selected = self.ListBox.GetSelection()
    + if selected == wx.NOT_FOUND:
    + if direction >= 0:
    + selected = 0
    + else:
    + selected = self.ListBox.GetCount() - 1
    + else:
    + selected = (selected + direction) % (self.ListBox.GetCount() + 1)
    + if selected == self.ListBox.GetCount():
    + selected = wx.NOT_FOUND
    + self.ListBox.SetSelection(selected)
    +
    + def GetSelection(self):
    + return self.ListBox.GetStringSelection()
    +
    + def ProcessLeftDown(self, event):
    + selected = self.ListBox.HitTest(wx.Point(event.m_x, event.m_y))
    + if selected != wx.NOT_FOUND:
    + wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected))
    + return False
    +
    + def OnListBoxClick(self, event):
    + selected = event.GetSelection()
    + if selected != wx.NOT_FOUND:
    + wx.CallAfter(self.Parent.SetValueFromSelected, self.ListBox.GetString(selected))
    + event.Skip()
    +
    + def OnKeyDown(self, event):
    + self.Parent.ProcessEvent(event)
    +
    + def OnDismiss(self):
    + self.Parent.listbox = None
    + wx.CallAfter(self.Parent.DismissListBox)
    +
    class TextCtrlAutoComplete(wx.TextCtrl):
    - def __init__ (self, parent, choices=None, dropDownClick=True,
    + def __init__ (self, parent, appframe, choices=None, dropDownClick=True,
    element_path=None, **therest):
    """
    Constructor works just like wx.TextCtrl except you can pass in a
    @@ -42,7 +119,8 @@
    therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0)
    wx.TextCtrl.__init__(self, parent, **therest)
    -
    + self.AppFrame = appframe
    +
    #Some variables
    self._dropDownClick = dropDownClick
    self._lastinsertionpoint = None
    @@ -50,15 +128,7 @@
    self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
    self.element_path = element_path
    - #widgets
    - self.dropdown = wx.PopupWindow(self)
    -
    - #Control the style
    - flags = wx.LB_HSCROLL | wx.LB_SINGLE | wx.LB_SORT
    -
    - #Create the list and bind the events
    - self.dropdownlistbox = wx.ListBox(self.dropdown, style=flags,
    - pos=wx.Point(0, 0))
    + self.listbox = None
    self.SetChoices(choices)
    @@ -68,66 +138,56 @@
    # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp )
    # gp = gp.GetParent()
    - self.Bind(wx.EVT_KILL_FOCUS, self.onControlChanged, self)
    - self.Bind(wx.EVT_TEXT_ENTER, self.onControlChanged, self)
    - self.Bind(wx.EVT_TEXT, self.onEnteredText, self)
    - self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown, self)
    + self.Bind(wx.EVT_KILL_FOCUS, self.OnControlChanged)
    + self.Bind(wx.EVT_TEXT_ENTER, self.OnControlChanged)
    + self.Bind(wx.EVT_TEXT, self.OnEnteredText)
    + self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
    #If need drop down on left click
    if dropDownClick:
    - self.Bind(wx.EVT_LEFT_DOWN , self.onClickToggleDown, self)
    - self.Bind(wx.EVT_LEFT_UP , self.onClickToggleUp, self)
    + self.Bind(wx.EVT_LEFT_DOWN, self.OnClickToggleDown)
    + self.Bind(wx.EVT_LEFT_UP, self.OnClickToggleUp)
    - self.dropdownlistbox.Bind(wx.EVT_LISTBOX, self.onListItemSelected)
    - self.dropdownlistbox.Bind(wx.EVT_LISTBOX_DCLICK, self.onListItemSelected)
    + def __del__(self):
    + self.AppFrame = None
    def ChangeValue(self, value):
    wx.TextCtrl.ChangeValue(self, value)
    - self._refreshListBoxChoices()
    + self.RefreshListBoxChoices()
    - def onEnteredText(self, event):
    - wx.CallAfter(self._refreshListBoxChoices)
    + def OnEnteredText(self, event):
    + wx.CallAfter(self.RefreshListBoxChoices)
    event.Skip()
    - def onKeyDown(self, event) :
    + def OnKeyDown(self, event):
    """ Do some work when the user press on the keys:
    up and down: move the cursor
    """
    - visible = self.dropdown.IsShown()
    keycode = event.GetKeyCode()
    if keycode in [wx.WXK_DOWN, wx.WXK_UP]:
    - if not visible:
    - self._showDropDown()
    - elif keycode == wx.WXK_DOWN:
    - self._moveSelection(1)
    + self.PopupListBox()
    + if keycode == wx.WXK_DOWN:
    + self.listbox.MoveSelection(1)
    else:
    - self._moveSelection(-1)
    - elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and visible:
    - if self.dropdownlistbox.GetSelection() != wx.NOT_FOUND:
    - self._setValueFromSelected()
    - else:
    - self._showDropDown(False)
    - event.Skip()
    + self.listbox.MoveSelection(-1)
    + elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and self.listbox is not None:
    + self.SetValueFromSelected(self.listbox.GetSelection())
    elif event.GetKeyCode() == wx.WXK_ESCAPE:
    - self._showDropDown(False)
    + self.DismissListBox()
    else:
    event.Skip()
    - def onListItemSelected(self, event):
    - self._setValueFromSelected()
    - event.Skip()
    -
    - def onClickToggleDown(self, event):
    + def OnClickToggleDown(self, event):
    self._lastinsertionpoint = self.GetInsertionPoint()
    event.Skip()
    - def onClickToggleUp(self, event):
    + def OnClickToggleUp(self, event):
    if self.GetInsertionPoint() == self._lastinsertionpoint:
    - self._showDropDown(not self.dropdown.IsShown())
    + wx.CallAfter(self.PopupListBox)
    self._lastinsertionpoint = None
    event.Skip()
    - def onControlChanged(self, event):
    + def OnControlChanged(self, event):
    res = self.GetValue()
    config = wx.ConfigBase.Get()
    listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([]))))
    @@ -136,72 +196,55 @@
    config.Write(self.element_path, cPickle.dumps(listentries))
    config.Flush()
    self.SetChoices(listentries)
    - self._showDropDown(False)
    + self.DismissListBox()
    event.Skip()
    def SetChoices(self, choices):
    self._choices = choices
    - self._refreshListBoxChoices()
    + self.RefreshListBoxChoices()
    def GetChoices(self):
    return self._choices
    -#-------------------------------------------------------------------------------
    -# Internal methods
    -#-------------------------------------------------------------------------------
    -
    - def _refreshListBoxChoices(self):
    - text = self.GetValue()
    -
    - self.dropdownlistbox.Clear()
    - for choice in self._choices:
    - if choice.startswith(text):
    - self.dropdownlistbox.Append(choice)
    -
    - itemcount = min(len(self.dropdownlistbox.GetStrings()), MAX_ITEM_SHOWN)
    - self.popupsize = wx.Size(self.GetSize()[0], ITEM_HEIGHT * itemcount + 4)
    - self.dropdownlistbox.SetSize(self.popupsize)
    - self.dropdown.SetClientSize(self.popupsize)
    -
    - def _moveSelection(self, direction):
    - selected = self.dropdownlistbox.GetSelection()
    - if selected == wx.NOT_FOUND:
    - if direction >= 0:
    - selected = 0
    - else:
    - selected = self.dropdownlistbox.GetCount() - 1
    - else:
    - selected = (selected + direction) % (self.dropdownlistbox.GetCount() + 1)
    - if selected == self.dropdownlistbox.GetCount():
    - selected = wx.NOT_FOUND
    - self.dropdownlistbox.SetSelection(selected)
    -
    - def _setValueFromSelected(self):
    + def SetValueFromSelected(self, selected):
    """
    Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
    Will do nothing if no item is selected in the wx.ListCtrl.
    """
    - selected = self.dropdownlistbox.GetStringSelection()
    - if selected:
    + if selected != "":
    self.SetValue(selected)
    - self._showDropDown(False)
    -
    + self.DismissListBox()
    +
    + def RefreshListBoxChoices(self):
    + if self.listbox is not None:
    + text = self.GetValue()
    + choices = [choice for choice in self._choices if choice.startswith(text)]
    + self.listbox.SetChoices(choices)
    - def _showDropDown(self, show=True) :
    - """
    - Either display the drop down list (show = True) or hide it (show = False).
    - """
    - if show :
    - size = self.dropdown.GetSize()
    - width, height = self.GetSizeTuple()
    - x, y = self.ClientToScreenXY(0, height)
    - if size.GetWidth() != width :
    - size.SetWidth(width)
    - self.dropdown.SetSize(size)
    - self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
    - if (y + size.GetHeight()) < self._screenheight :
    - self.dropdown.SetPosition(wx.Point(x, y))
    + def PopupListBox(self):
    + if self.listbox is None:
    + self.listbox = PopupWithListbox(self)
    +
    + # Show the popup right below or above the button
    + # depending on available screen space...
    + pos = self.ClientToScreen((0, 0))
    + sz = self.GetSize()
    + self.listbox.Position(pos, (0, sz[1]))
    +
    + self.RefreshListBoxChoices()
    +
    + if wx.Platform == '__WXMSW__':
    + self.listbox.Popup()
    else:
    - self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight()))
    - self.dropdown.Show(show)
    + self.listbox.Show()
    + self.AppFrame.EnableScrolling(False)
    + def DismissListBox(self):
    + if self.listbox is not None:
    + if wx.Platform == '__WXMSW__':
    + self.listbox.Dismiss()
    + else:
    + self.listbox.Destroy()
    + self.listbox = None
    + self.AppFrame.EnableScrolling(True)
    +