beremiz

Merge with 092060fd8afb1d950fe467724ce0209bddc48716
  • +82 -62
    Beremiz.py
  • +250 -207
    TextCtrlAutoComplete.py
  • +0 -91
    beremiz_postinst.py
  • +4 -0
    debian/Beremiz.directory
  • +4 -0
    debian/Beremiz_demos.directory
  • +4 -0
    debian/Beremiz_docs.directory
  • +1 -1
    debian/README.Debian
  • +1 -1
    debian/beremiz.desktop
  • +40 -0
    debian/beremiz.menu
  • +12 -0
    debian/beremiz_doc.desktop
  • +13 -0
    debian/beremiz_svgui.desktop
  • +13 -0
    debian/beremiz_wxglade.desktop
  • +1 -1
    debian/changelog
  • +2 -2
    debian/control
  • +23 -28
    debian/rules
  • +0 -9
    doc/about.html
  • +223 -148
    i18n/Beremiz_fr_FR.po
  • +0 -1
    i18n/app.fil
  • +195 -133
    i18n/messages.pot
  • +0 -0
    images/EditCfile.png
  • +0 -0
    images/HideVars.png
  • +0 -0
    images/Maximize.png
  • +0 -0
    images/Minimize.png
  • +0 -0
    images/ShowIECcode.png
  • +0 -0
    images/ShowVars.png
  • +0 -0
    images/editIECrawcode.png
  • +0 -0
    images/editPYTHONcode.png
  • +0 -0
    images/editWXGLADE.png
  • +9 -25
    images/icons.svg
  • +0 -0
    images/splash.png
  • +0 -0
    locale/fr_FR/LC_MESSAGES/Beremiz.mo
  • +111 -45
    plugger.py
  • +0 -5
    plugins/c_ext/CFileEditor.py
  • +23 -9
    plugins/c_ext/c_ext.py
  • +49 -20
    plugins/canfestival/canfestival.py
  • +1 -4
    plugins/python/PythonEditor.py
  • +1 -1
    plugins/python/modules/svgui/pous.xml
  • +12 -3
    plugins/python/modules/svgui/svgui.py
  • +22 -13
    plugins/python/modules/wxglade_hmi/wxglade_hmi.py
  • +1 -1
    plugins/python/pous.xml
  • +23 -9
    plugins/python/python.py
  • +0 -39
    setup.py
  • +6 -0
    targets/Makefile/XSD
  • +7 -0
    targets/Makefile/__init__.py
  • +93 -0
    targets/Makefile/plc_Makefile_main.c
  • +6 -0
    targets/XSD_toolchain_makefile
  • +2 -1
    targets/__init__.py
  • +3 -2
    targets/plc_common_main.c
  • +9 -8
    targets/plc_debug.c
  • +11 -6
    targets/toolchain_gcc.py
  • +42 -0
    targets/toolchain_makefile.py
  • +15 -10
    wxPopen.py
  • --- a/Beremiz.py Tue Oct 27 16:32:54 2009 +0100
    +++ b/Beremiz.py Mon Nov 02 15:38:49 2009 +0100
    @@ -293,6 +293,8 @@
    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_SAVEAS,
    + kind=wx.ITEM_NORMAL, text=_(u'Save as\tCTRL+SHIFT+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,
    @@ -314,6 +316,7 @@
    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.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
    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)
    @@ -367,6 +370,7 @@
    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='',
    @@ -388,6 +392,8 @@
    self.local_runtime_tmpdir = None
    self.DisableEvents = False
    + # Variable allowing disabling of PLCConfig scroll when Popup shown
    + self.ScrollingEnabled = True
    self.PluginInfos = {}
    @@ -477,39 +483,46 @@
    infos = self.PluginRoot.ShowError(self.Log,
    (int(first_line), int(first_column)),
    (int(last_line), int(last_column)))
    -
    +
    + ## Function displaying an Error dialog in PLCOpenEditor.
    + # @return False if closing cancelled.
    + def CheckSaveBeforeClosing(self, title=_("Close Project")):
    + if self.PluginRoot.ProjectTestModified():
    + dialog = wx.MessageDialog(self,
    + _("There are changes, do you want to save?"),
    + title,
    + 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 False
    + return True
    +
    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()
    + if self.PluginRoot is None or self.CheckSaveBeforeClosing(_("Close Application")):
    + self.KillLocalRuntime()
    + event.Skip()
    + else:
    + event.Veto()
    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._showDropDown(False)
    + focused.DismissListBox()
    event.Skip()
    def RefreshFileMenu(self):
    @@ -533,6 +546,7 @@
    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_SAVEAS, True)
    self.FileMenu.Enable(wx.ID_PROPERTIES, True)
    self.FileMenu.Enable(wx.ID_CLOSE_ALL, True)
    else:
    @@ -541,6 +555,7 @@
    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_SAVEAS, False)
    self.FileMenu.Enable(wx.ID_PROPERTIES, False)
    self.FileMenu.Enable(wx.ID_CLOSE_ALL, False)
    @@ -1201,7 +1216,7 @@
    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)
    + 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("")
    @@ -1234,7 +1249,7 @@
    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)
    + 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"])
    @@ -1253,7 +1268,7 @@
    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)
    + 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"])
    @@ -1263,10 +1278,11 @@
    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(150, 25),
    + size=wx.Size(300, 25),
    style=0)
    boxsizer.AddWindow(textctrl, 0, border=0, flag=0)
    @@ -1284,6 +1300,9 @@
    self.DebugVariablePanel.SetDataProducer(None)
    def OnNewProjectMenu(self, event):
    + if self.PluginRoot is not None and not self.CheckSaveBeforeClosing():
    + return
    +
    if not self.Config.HasEntry("lastopenedfolder"):
    defaultpath = os.path.expanduser("~")
    else:
    @@ -1309,6 +1328,9 @@
    self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    def OnOpenProjectMenu(self, event):
    + if self.PluginRoot is not None and not self.CheckSaveBeforeClosing():
    + return
    +
    if not self.Config.HasEntry("lastopenedfolder"):
    defaultpath = os.path.expanduser("~")
    else:
    @@ -1337,21 +1359,12 @@
    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()
    + if self.PluginRoot is not None and not self.CheckSaveBeforeClosing():
    + return
    +
    + self.ResetView()
    + self._Refresh(TITLE, TOOLBAR, FILEMENU, EDITMENU)
    + self.RefreshAll()
    def OnSaveProjectMenu(self, event):
    if self.PluginRoot is not None:
    @@ -1359,6 +1372,13 @@
    self.RefreshAll()
    self.RefreshTitle()
    + def OnSaveProjectAsMenu(self, event):
    + if self.PluginRoot is not None:
    + self.PluginRoot.SaveProjectAs()
    + self.RefreshAll()
    + self.RefreshTitle()
    + event.Skip()
    +
    def OnPropertiesMenu(self, event):
    self.ShowProperties()
    @@ -1391,24 +1411,26 @@
    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()
    + if self.PluginRoot.CheckProjectPathPerm():
    + 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()
    -
    + if self.PluginRoot.CheckProjectPathPerm():
    + 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
    #-------------------------------------------------------------------------------
    @@ -1436,10 +1458,8 @@
    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
    +Please be kind enough to send this file to:
    +edouard.tisserant@gmail.com
    You should now restart Beremiz.
    --- a/TextCtrlAutoComplete.py Tue Oct 27 16:32:54 2009 +0100
    +++ b/TextCtrlAutoComplete.py Mon Nov 02 15:38:49 2009 +0100
    @@ -1,207 +1,250 @@
    -#!/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
    -
    -import wx
    -import cPickle
    -
    -MAX_ITEM_COUNT = 10
    -MAX_ITEM_SHOWN = 6
    -ITEM_HEIGHT = 25
    -
    -class TextCtrlAutoComplete(wx.TextCtrl):
    -
    - def __init__ (self, parent, choices=None, dropDownClick=True,
    - element_path=None, **therest):
    - """
    - Constructor works just like wx.TextCtrl except you can pass in a
    - list of choices. You can also change the choice list at any time
    - by calling setChoices.
    - """
    -
    - therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0)
    -
    - wx.TextCtrl.__init__(self, parent, **therest)
    -
    - #Some variables
    - self._dropDownClick = dropDownClick
    - self._lastinsertionpoint = None
    -
    - 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.SetChoices(choices)
    -
    - #gp = self
    - #while ( gp != None ) :
    - # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp )
    - # 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)
    -
    - #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.dropdownlistbox.Bind(wx.EVT_LISTBOX, self.onListItemSelected)
    - self.dropdownlistbox.Bind(wx.EVT_LISTBOX_DCLICK, self.onListItemSelected)
    -
    - def ChangeValue(self, value):
    - wx.TextCtrl.ChangeValue(self, value)
    - self._refreshListBoxChoices()
    -
    - def onEnteredText(self, event):
    - wx.CallAfter(self._refreshListBoxChoices)
    - event.Skip()
    -
    - 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)
    - 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()
    - elif event.GetKeyCode() == wx.WXK_ESCAPE:
    - self._showDropDown(False)
    - else:
    - event.Skip()
    -
    - def onListItemSelected(self, event):
    - self._setValueFromSelected()
    - event.Skip()
    -
    - def onClickToggleDown(self, event):
    - self._lastinsertionpoint = self.GetInsertionPoint()
    - event.Skip()
    -
    - def onClickToggleUp(self, event):
    - if self.GetInsertionPoint() == self._lastinsertionpoint:
    - self._showDropDown(not self.dropdown.IsShown())
    - self._lastinsertionpoint = None
    - event.Skip()
    -
    - def onControlChanged(self, event):
    - res = self.GetValue()
    - config = wx.ConfigBase.Get()
    - listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([]))))
    - if res and res not in listentries:
    - listentries = (listentries + [res])[-MAX_ITEM_COUNT:]
    - config.Write(self.element_path, cPickle.dumps(listentries))
    - config.Flush()
    - self.SetChoices(listentries)
    - self._showDropDown(False)
    - event.Skip()
    -
    - def SetChoices(self, choices):
    - self._choices = choices
    - 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):
    - """
    - 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:
    - self.SetValue(selected)
    - self._showDropDown(False)
    -
    -
    - 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))
    - else:
    - self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight()))
    - self.dropdown.Show(show)
    -
    +#!/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
    +
    +import wx
    +import cPickle
    +
    +MAX_ITEM_COUNT = 10
    +MAX_ITEM_SHOWN = 6
    +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, appframe, choices=None, dropDownClick=True,
    + element_path=None, **therest):
    + """
    + Constructor works just like wx.TextCtrl except you can pass in a
    + list of choices. You can also change the choice list at any time
    + by calling setChoices.
    + """
    +
    + 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
    +
    + self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
    + self.element_path = element_path
    +
    + self.listbox = None
    +
    + self.SetChoices(choices)
    +
    + #gp = self
    + #while ( gp != None ) :
    + # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp )
    + # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp )
    + # gp = gp.GetParent()
    +
    + 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.Bind(wx.EVT_LEFT_UP, self.OnClickToggleUp)
    +
    + def __del__(self):
    + self.AppFrame = None
    +
    + def ChangeValue(self, value):
    + wx.TextCtrl.ChangeValue(self, value)
    + self.RefreshListBoxChoices()
    +
    + def OnEnteredText(self, event):
    + wx.CallAfter(self.RefreshListBoxChoices)
    + event.Skip()
    +
    + def OnKeyDown(self, event):
    + """ Do some work when the user press on the keys:
    + up and down: move the cursor
    + """
    + keycode = event.GetKeyCode()
    + if keycode in [wx.WXK_DOWN, wx.WXK_UP]:
    + self.PopupListBox()
    + if keycode == wx.WXK_DOWN:
    + self.listbox.MoveSelection(1)
    + else:
    + 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.DismissListBox()
    + else:
    + event.Skip()
    +
    + def OnClickToggleDown(self, event):
    + self._lastinsertionpoint = self.GetInsertionPoint()
    + event.Skip()
    +
    + def OnClickToggleUp(self, event):
    + if self.GetInsertionPoint() == self._lastinsertionpoint:
    + wx.CallAfter(self.PopupListBox)
    + self._lastinsertionpoint = None
    + event.Skip()
    +
    + def OnControlChanged(self, event):
    + res = self.GetValue()
    + config = wx.ConfigBase.Get()
    + listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([]))))
    + if res and res not in listentries:
    + listentries = (listentries + [res])[-MAX_ITEM_COUNT:]
    + config.Write(self.element_path, cPickle.dumps(listentries))
    + config.Flush()
    + self.SetChoices(listentries)
    + self.DismissListBox()
    + event.Skip()
    +
    + def SetChoices(self, choices):
    + self._choices = choices
    + self.RefreshListBoxChoices()
    +
    + def GetChoices(self):
    + return self._choices
    +
    + 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.
    + """
    + if selected != "":
    + self.SetValue(selected)
    + 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 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__':