lbessard@295: #!/usr/bin/env python lbessard@295: # -*- coding: utf-8 -*- lbessard@295: lbessard@295: #This file is part of Beremiz, a Integrated Development Environment for lbessard@295: #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. lbessard@295: # lbessard@295: #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD lbessard@295: # lbessard@295: #See COPYING file for copyrights details. lbessard@295: # lbessard@295: #This library is free software; you can redistribute it and/or lbessard@295: #modify it under the terms of the GNU General Public lbessard@295: #License as published by the Free Software Foundation; either lbessard@295: #version 2.1 of the License, or (at your option) any later version. lbessard@295: # lbessard@295: #This library is distributed in the hope that it will be useful, lbessard@295: #but WITHOUT ANY WARRANTY; without even the implied warranty of lbessard@295: #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU lbessard@295: #General Public License for more details. lbessard@295: # lbessard@295: #You should have received a copy of the GNU General Public lbessard@295: #License along with this library; if not, write to the Free Software lbessard@295: #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA lbessard@295: lbessard@295: import wx greg@268: import cPickle lbessard@295: lbessard@295: MAX_ITEM_COUNT = 10 lbessard@295: MAX_ITEM_SHOWN = 6 lbessard@295: ITEM_HEIGHT = 25 lbessard@295: lbessard@295: class TextCtrlAutoComplete(wx.TextCtrl): lbessard@295: lbessard@295: def __init__ (self, parent, choices=None, dropDownClick=True, lbessard@295: element_path=None, **therest): lbessard@295: """ greg@268: Constructor works just like wx.TextCtrl except you can pass in a greg@268: list of choices. You can also change the choice list at any time greg@268: by calling setChoices. lbessard@295: """ lbessard@295: lbessard@295: therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) lbessard@295: lbessard@295: wx.TextCtrl.__init__(self, parent, **therest) greg@268: greg@268: #Some variables greg@268: self._dropDownClick = dropDownClick lbessard@326: self._lastinsertionpoint = None lbessard@295: lbessard@295: self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) greg@268: self.element_path = element_path lbessard@295: greg@268: #widgets lbessard@295: self.dropdown = wx.PopupWindow(self) greg@268: greg@268: #Control the style lbessard@295: flags = wx.LB_HSCROLL | wx.LB_SINGLE | wx.LB_SORT lbessard@295: greg@268: #Create the list and bind the events lbessard@295: self.dropdownlistbox = wx.ListBox(self.dropdown, style=flags, lbessard@295: pos=wx.Point(0, 0)) lbessard@295: lbessard@295: self.SetChoices(choices) greg@268: greg@268: #gp = self greg@268: #while ( gp != None ) : greg@268: # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) greg@268: # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) greg@268: # gp = gp.GetParent() greg@268: lbessard@295: self.Bind(wx.EVT_KILL_FOCUS, self.onControlChanged, self) lbessard@295: self.Bind(wx.EVT_TEXT_ENTER, self.onControlChanged, self) lbessard@295: self.Bind(wx.EVT_TEXT, self.onEnteredText, self) lbessard@295: self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown, self) greg@268: greg@268: #If need drop down on left click greg@268: if dropDownClick: lbessard@295: self.Bind(wx.EVT_LEFT_DOWN , self.onClickToggleDown, self) lbessard@295: self.Bind(wx.EVT_LEFT_UP , self.onClickToggleUp, self) lbessard@295: lbessard@295: self.dropdownlistbox.Bind(wx.EVT_LISTBOX, self.onListItemSelected) lbessard@295: self.dropdownlistbox.Bind(wx.EVT_LISTBOX_DCLICK, self.onListItemSelected) lbessard@295: lbessard@295: def ChangeValue(self, value): lbessard@295: wx.TextCtrl.ChangeValue(self, value) lbessard@295: self._refreshListBoxChoices() greg@268: greg@268: def onEnteredText(self, event): lbessard@295: wx.CallAfter(self._refreshListBoxChoices) lbessard@295: event.Skip() lbessard@295: lbessard@295: def onKeyDown(self, event) : greg@268: """ Do some work when the user press on the keys: greg@268: up and down: move the cursor lbessard@295: """ greg@268: visible = self.dropdown.IsShown() lbessard@295: keycode = event.GetKeyCode() lbessard@295: if keycode in [wx.WXK_DOWN, wx.WXK_UP]: lbessard@295: if not visible: lbessard@295: self._showDropDown() lbessard@295: elif keycode == wx.WXK_DOWN: lbessard@295: self._moveSelection(1) lbessard@295: else: lbessard@295: self._moveSelection(-1) lbessard@295: elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and visible: lbessard@295: if self.dropdownlistbox.GetSelection() != wx.NOT_FOUND: greg@268: self._setValueFromSelected() lbessard@295: else: lbessard@295: self._showDropDown(False) lbessard@295: event.Skip() lbessard@295: elif event.GetKeyCode() == wx.WXK_ESCAPE: lbessard@295: self._showDropDown(False) lbessard@295: else: greg@268: event.Skip() greg@268: lbessard@295: def onListItemSelected(self, event): greg@268: self._setValueFromSelected() greg@268: event.Skip() greg@268: greg@268: def onClickToggleDown(self, event): greg@268: self._lastinsertionpoint = self.GetInsertionPoint() lbessard@295: event.Skip() lbessard@295: lbessard@295: def onClickToggleUp(self, event): lbessard@295: if self.GetInsertionPoint() == self._lastinsertionpoint: lbessard@295: self._showDropDown(not self.dropdown.IsShown()) lbessard@326: self._lastinsertionpoint = None lbessard@295: event.Skip() greg@268: greg@268: def onControlChanged(self, event): greg@268: res = self.GetValue() greg@268: config = wx.ConfigBase.Get() greg@268: listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([])))) lbessard@295: if res and res not in listentries: lbessard@295: listentries = (listentries + [res])[-MAX_ITEM_COUNT:] lbessard@295: config.Write(self.element_path, cPickle.dumps(listentries)) greg@268: config.Flush() lbessard@295: self.SetChoices(listentries) lbessard@295: self._showDropDown(False) lbessard@295: event.Skip() lbessard@295: lbessard@295: def SetChoices(self, choices): lbessard@295: self._choices = choices lbessard@295: self._refreshListBoxChoices() lbessard@295: lbessard@295: def GetChoices(self): lbessard@295: return self._choices lbessard@295: lbessard@295: #------------------------------------------------------------------------------- lbessard@295: # Internal methods lbessard@295: #------------------------------------------------------------------------------- lbessard@295: lbessard@295: def _refreshListBoxChoices(self): lbessard@295: text = self.GetValue() lbessard@295: lbessard@295: self.dropdownlistbox.Clear() lbessard@295: for choice in self._choices: lbessard@295: if choice.startswith(text): lbessard@295: self.dropdownlistbox.Append(choice) lbessard@295: lbessard@295: itemcount = min(len(self.dropdownlistbox.GetStrings()), MAX_ITEM_SHOWN) lbessard@295: self.popupsize = wx.Size(self.GetSize()[0], ITEM_HEIGHT * itemcount + 4) lbessard@295: self.dropdownlistbox.SetSize(self.popupsize) lbessard@295: self.dropdown.SetClientSize(self.popupsize) lbessard@295: lbessard@295: def _moveSelection(self, direction): lbessard@295: selected = self.dropdownlistbox.GetSelection() lbessard@295: if selected == wx.NOT_FOUND: lbessard@295: if direction >= 0: lbessard@295: selected = 0 lbessard@295: else: lbessard@295: selected = self.dropdownlistbox.GetCount() - 1 greg@268: else: lbessard@295: selected = (selected + direction) % (self.dropdownlistbox.GetCount() + 1) lbessard@295: if selected == self.dropdownlistbox.GetCount(): lbessard@295: selected = wx.NOT_FOUND lbessard@295: self.dropdownlistbox.SetSelection(selected) lbessard@295: lbessard@295: def _setValueFromSelected(self): lbessard@295: """ greg@268: Sets the wx.TextCtrl value from the selected wx.ListCtrl item. greg@268: Will do nothing if no item is selected in the wx.ListCtrl. lbessard@295: """ lbessard@295: selected = self.dropdownlistbox.GetStringSelection() lbessard@295: if selected: lbessard@295: self.SetValue(selected) lbessard@295: self._showDropDown(False) lbessard@295: lbessard@295: lbessard@295: def _showDropDown(self, show=True) : lbessard@295: """ greg@268: Either display the drop down list (show = True) or hide it (show = False). lbessard@295: """ greg@268: if show : greg@268: size = self.dropdown.GetSize() lbessard@295: width, height = self.GetSizeTuple() lbessard@295: x, y = self.ClientToScreenXY(0, height) greg@268: if size.GetWidth() != width : greg@268: size.SetWidth(width) greg@268: self.dropdown.SetSize(size) greg@268: self.dropdownlistbox.SetSize(self.dropdown.GetClientSize()) greg@268: if (y + size.GetHeight()) < self._screenheight : lbessard@295: self.dropdown.SetPosition(wx.Point(x, y)) lbessard@295: else: lbessard@295: self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight())) lbessard@295: self.dropdown.Show(show) lbessard@295: