TextCtrlAutoComplete.py
changeset 295 bc6fc07c3153
parent 268 66843376a982
child 326 386566f263f3
equal deleted inserted replaced
294:39b3d4a2195b 295:bc6fc07c3153
     1 
     1 #!/usr/bin/env python
     2 '''
     2 # -*- coding: utf-8 -*-
     3 
     3 
     4 wxPython Custom Widget Collection 20060207
     4 #This file is part of Beremiz, a Integrated Development Environment for
     5 Written By: Edward Flick (eddy -=at=- cdf-imaging -=dot=- com)
     5 #programming IEC 61131-3 automates supporting plcopen standard and CanFestival. 
     6             Michele Petrazzo (michele -=dot=- petrazzo -=at=- unipex -=dot=- it)
     6 #
     7             Will Sadkin (wsadkin-=at=- nameconnector -=dot=- com)
     7 #Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
     8 Copyright 2006 (c) CDF Inc. ( http://www.cdf-imaging.com )
     8 #
     9 Contributed to the wxPython project under the wxPython project's license.
     9 #See COPYING file for copyrights details.
    10 
    10 #
    11 '''
    11 #This library is free software; you can redistribute it and/or
    12 
    12 #modify it under the terms of the GNU General Public
    13 import locale, wx, sys, cStringIO
    13 #License as published by the Free Software Foundation; either
    14 
    14 #version 2.1 of the License, or (at your option) any later version.
    15 import  wx.lib.mixins.listctrl  as  listmix
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 import wx
    16 import cPickle
    26 import cPickle
    17 from wx import ImageFromStream, BitmapFromImage
    27 
    18 #----------------------------------------------------------------------
    28 MAX_ITEM_COUNT = 10
    19 def getSmallUpArrowData():
    29 MAX_ITEM_SHOWN = 6
    20     return \
    30 ITEM_HEIGHT = 25
    21 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
    31 
    22 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
    32 class TextCtrlAutoComplete(wx.TextCtrl):
    23 \x00\x00<IDAT8\x8dcddbf\xa0\x040Q\xa4{h\x18\xf0\xff\xdf\xdf\xffd\x1b\x00\xd3\
    33 
    24 \x8c\xcf\x10\x9c\x06\xa0k\xc2e\x08m\xc2\x00\x97m\xd8\xc41\x0c \x14h\xe8\xf2\
    34     def __init__ (self, parent, choices=None, dropDownClick=True,
    25 \x8c\xa3)q\x10\x18\x00\x00R\xd8#\xec\xb2\xcd\xc1Y\x00\x00\x00\x00IEND\xaeB`\
    35                   element_path=None, **therest):
    26 \x82'
    36         """
    27 
       
    28 def getSmallUpArrowBitmap():
       
    29     return BitmapFromImage(getSmallUpArrowImage())
       
    30 
       
    31 def getSmallUpArrowImage():
       
    32     stream = cStringIO.StringIO(getSmallUpArrowData())
       
    33     return ImageFromStream(stream)
       
    34 
       
    35 
       
    36 def getSmallDnArrowData():
       
    37     return \
       
    38 "\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
       
    39 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
       
    40 \x00\x00HIDAT8\x8dcddbf\xa0\x040Q\xa4{\xd4\x00\x06\x06\x06\x06\x06\x16t\x81\
       
    41 \xff\xff\xfe\xfe'\xa4\x89\x91\x89\x99\x11\xa7\x0b\x90%\ti\xc6j\x00>C\xb0\x89\
       
    42 \xd3.\x10\xd1m\xc3\xe5*\xbc.\x80i\xc2\x17.\x8c\xa3y\x81\x01\x00\xa1\x0e\x04e\
       
    43 ?\x84B\xef\x00\x00\x00\x00IEND\xaeB`\x82"
       
    44 
       
    45 def getSmallDnArrowBitmap():
       
    46     return BitmapFromImage(getSmallDnArrowImage())
       
    47 
       
    48 def getSmallDnArrowImage():
       
    49     stream = cStringIO.StringIO(getSmallDnArrowData())
       
    50     return ImageFromStream(stream)
       
    51 #----------------------------------------------------------------------
       
    52 
       
    53 class myListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
       
    54     def __init__(self, parent, ID=-1, pos=wx.DefaultPosition,
       
    55                  size=wx.DefaultSize, style=0):
       
    56         wx.ListCtrl.__init__(self, parent, ID, pos, size, style)
       
    57         listmix.ListCtrlAutoWidthMixin.__init__(self)
       
    58 
       
    59 class TextCtrlAutoComplete (wx.TextCtrl, listmix.ColumnSorterMixin ):
       
    60 
       
    61     def __init__ ( self, parent, colNames=None, choices = None,
       
    62                   multiChoices=None, showHead=True, dropDownClick=True,
       
    63                   colFetch=-1, colSearch=0, hideOnNoMatch=True,
       
    64                   selectCallback=None, entryCallback=None, matchFunction=None,
       
    65                   element_path=None, **therest) :
       
    66         '''
       
    67         Constructor works just like wx.TextCtrl except you can pass in a
    37         Constructor works just like wx.TextCtrl except you can pass in a
    68         list of choices.  You can also change the choice list at any time
    38         list of choices.  You can also change the choice list at any time
    69         by calling setChoices.
    39         by calling setChoices.
    70         '''
    40         """
    71 
    41 
    72         if therest.has_key('style'):
    42         therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0)
    73             therest['style']=wx.TE_PROCESS_ENTER | therest['style']
    43 
    74         else:
    44         wx.TextCtrl.__init__(self, parent, **therest)
    75             therest['style']=wx.TE_PROCESS_ENTER
       
    76 
       
    77         wx.TextCtrl.__init__(self, parent, **therest )
       
    78 
    45 
    79         #Some variables
    46         #Some variables
    80         self._dropDownClick = dropDownClick
    47         self._dropDownClick = dropDownClick
    81         self._colNames = colNames
       
    82         self._multiChoices = multiChoices
       
    83         self._showHead = showHead
       
    84         self._choices = choices
       
    85         self._lastinsertionpoint = 0
    48         self._lastinsertionpoint = 0
    86         self._hideOnNoMatch = hideOnNoMatch
    49         
    87         self._selectCallback = selectCallback
    50         self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
    88         self._entryCallback = entryCallback
       
    89         self._matchFunction = matchFunction
       
    90 
       
    91         self._screenheight = wx.SystemSettings.GetMetric( wx.SYS_SCREEN_Y )
       
    92         self.element_path = element_path
    51         self.element_path = element_path
    93         #sort variable needed by listmix
    52         
    94         self.itemDataMap = dict()
       
    95 
       
    96         #widgets
    53         #widgets
    97         self.dropdown = wx.PopupWindow( self )
    54         self.dropdown = wx.PopupWindow(self)
    98 
    55 
    99         #Control the style
    56         #Control the style
   100         flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING
    57         flags = wx.LB_HSCROLL | wx.LB_SINGLE | wx.LB_SORT
   101         if not (showHead and multiChoices) :
    58         
   102             flags = flags | wx.LC_NO_HEADER
       
   103 
       
   104         #Create the list and bind the events
    59         #Create the list and bind the events
   105         self.dropdownlistbox = myListCtrl( self.dropdown, style=flags,
    60         self.dropdownlistbox = wx.ListBox(self.dropdown, style=flags,
   106                                  pos=wx.Point( 0, 0) )
    61                                  pos=wx.Point(0, 0))
   107 
    62         
   108         #initialize the parent
    63         self.SetChoices(choices)
   109         if multiChoices: ln = len(multiChoices)
       
   110         else: ln = 1
       
   111         #else: ln = len(choices)
       
   112         listmix.ColumnSorterMixin.__init__(self, ln)
       
   113 
       
   114         #load the data
       
   115         if multiChoices: self.SetMultipleChoices (multiChoices, colSearch=colSearch, colFetch=colFetch)
       
   116         else: self.SetChoices ( choices )
       
   117 
    64 
   118         #gp = self
    65         #gp = self
   119         #while ( gp != None ) :
    66         #while ( gp != None ) :
   120         #    gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp )
    67         #    gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp )
   121         #    gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp )
    68         #    gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp )
   122         #    gp = gp.GetParent()
    69         #    gp = gp.GetParent()
   123 
    70 
   124         self.Bind( wx.EVT_KILL_FOCUS, self.onControlChanged, self )
    71         self.Bind(wx.EVT_KILL_FOCUS, self.onControlChanged, self)
   125         self.Bind( wx.EVT_TEXT , self.onEnteredText, self )
    72         self.Bind(wx.EVT_TEXT_ENTER, self.onControlChanged, self)
   126         self.Bind( wx.EVT_KEY_DOWN , self.onKeyDown, self )
    73         self.Bind(wx.EVT_TEXT, self.onEnteredText, self)
       
    74         self.Bind(wx.EVT_KEY_DOWN, self.onKeyDown, self)
   127 
    75 
   128         #If need drop down on left click
    76         #If need drop down on left click
   129         if dropDownClick:
    77         if dropDownClick:
   130             self.Bind ( wx.EVT_LEFT_DOWN , self.onClickToggleDown, self )
    78             self.Bind(wx.EVT_LEFT_DOWN , self.onClickToggleDown, self)
   131             self.Bind ( wx.EVT_LEFT_UP , self.onClickToggleUp, self )
    79             self.Bind(wx.EVT_LEFT_UP , self.onClickToggleUp, self)
   132 
    80 
   133         self.dropdown.Bind( wx.EVT_LISTBOX , self.onListItemSelected, self.dropdownlistbox )
    81         self.dropdownlistbox.Bind(wx.EVT_LISTBOX, self.onListItemSelected)
   134         self.dropdownlistbox.Bind(wx.EVT_LEFT_DOWN, self.onListClick)
    82         self.dropdownlistbox.Bind(wx.EVT_LISTBOX_DCLICK, self.onListItemSelected)
   135         self.dropdownlistbox.Bind(wx.EVT_LEFT_DCLICK, self.onListDClick)
    83 
   136         self.dropdownlistbox.Bind(wx.EVT_LIST_COL_CLICK, self.onListColClick)
    84     def ChangeValue(self, value):
   137 
    85         wx.TextCtrl.ChangeValue(self, value)
   138         self.il = wx.ImageList(16, 16)
    86         self._refreshListBoxChoices()
   139 
       
   140         self.sm_dn = self.il.Add(getSmallDnArrowBitmap())
       
   141         self.sm_up = self.il.Add(getSmallUpArrowBitmap())
       
   142 
       
   143         self.dropdownlistbox.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
       
   144         self._ascending = True
       
   145 
       
   146 
       
   147     #-- methods called from mixin class
       
   148     def GetSortImages(self):
       
   149         return (self.sm_dn, self.sm_up)
       
   150 
       
   151     def GetListCtrl(self):
       
   152         return self.dropdownlistbox
       
   153 
       
   154     # -- event methods
       
   155     def onListClick(self, evt):
       
   156         toSel, flag = self.dropdownlistbox.HitTest( evt.GetPosition() )
       
   157         #no values on poition, return
       
   158         if toSel == -1: return
       
   159         self.dropdownlistbox.Select(toSel)
       
   160 
       
   161     def onListDClick(self, evt):
       
   162         self._setValueFromSelected()
       
   163 
       
   164     def onListColClick(self, evt):
       
   165         col = evt.GetColumn()
       
   166 
       
   167         #reverse the sort
       
   168         if col == self._colSearch:
       
   169             self._ascending = not self._ascending
       
   170 
       
   171         self.SortListItems( evt.GetColumn(), ascending=self._ascending )
       
   172         self._colSearch = evt.GetColumn()
       
   173         evt.Skip()
       
   174 
    87 
   175     def onEnteredText(self, event):
    88     def onEnteredText(self, event):
   176         text = event.GetString()
    89         wx.CallAfter(self._refreshListBoxChoices)
   177 
    90         event.Skip()
   178         if self._entryCallback:
    91 
   179             self._entryCallback()
    92     def onKeyDown(self, event) :
   180 
       
   181         if not text:
       
   182             # control is empty; hide dropdown if shown:
       
   183             if self.dropdown.IsShown():
       
   184                 self._showDropDown(False)
       
   185             event.Skip()
       
   186             return
       
   187 
       
   188 
       
   189         found = False
       
   190         if self._multiChoices:
       
   191             #load the sorted data into the listbox
       
   192             dd = self.dropdownlistbox
       
   193             choices = [dd.GetItem(x, self._colSearch).GetText()
       
   194                 for x in xrange(dd.GetItemCount())]
       
   195         else:
       
   196             choices = self._choices
       
   197 
       
   198         for numCh, choice in enumerate(choices):
       
   199             if self._matchFunction and self._matchFunction(text, choice):
       
   200                 found = True
       
   201             elif choice.lower().startswith(text.lower()) :
       
   202                 found = True
       
   203             if found:
       
   204                 self._showDropDown(True)
       
   205                 item = self.dropdownlistbox.GetItem(numCh)
       
   206                 toSel = item.GetId()
       
   207                 self.dropdownlistbox.Select(toSel)
       
   208                 break
       
   209 
       
   210         if not found:
       
   211             self.dropdownlistbox.Select(self.dropdownlistbox.GetFirstSelected(), False)
       
   212             if self._hideOnNoMatch:
       
   213                 self._showDropDown(False)
       
   214 
       
   215         self._listItemVisible()
       
   216 
       
   217         event.Skip ()
       
   218 
       
   219     def onKeyDown ( self, event ) :
       
   220         """ Do some work when the user press on the keys:
    93         """ Do some work when the user press on the keys:
   221             up and down: move the cursor
    94             up and down: move the cursor
   222             left and right: move the search
    95         """
   223         """
       
   224         skip = True
       
   225         sel = self.dropdownlistbox.GetFirstSelected()
       
   226         visible = self.dropdown.IsShown()
    96         visible = self.dropdown.IsShown()
   227 
    97         keycode = event.GetKeyCode()
   228         KC = event.GetKeyCode()
    98         if keycode in [wx.WXK_DOWN, wx.WXK_UP]:
   229         if KC == wx.WXK_DOWN :
    99             if not visible:
   230             if sel < (self.dropdownlistbox.GetItemCount () - 1) :
   100                 self._showDropDown()
   231                 self.dropdownlistbox.Select ( sel+1 )
   101             elif keycode == wx.WXK_DOWN:
   232                 self._listItemVisible()
   102                 self._moveSelection(1)
   233             self._showDropDown ()
   103             else:
   234             skip = False
   104                 self._moveSelection(-1)
   235         elif KC == wx.WXK_UP :
   105         elif keycode in [wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_RETURN] and visible:
   236             if sel > 0 :
   106             if self.dropdownlistbox.GetSelection() != wx.NOT_FOUND:
   237                 self.dropdownlistbox.Select ( sel - 1 )
       
   238                 self._listItemVisible()
       
   239             self._showDropDown ()
       
   240             skip = False
       
   241         elif KC == wx.WXK_LEFT :
       
   242             if not self._multiChoices: return
       
   243             if self._colSearch > 0:
       
   244                 self._colSearch -=1
       
   245             self._showDropDown ()
       
   246         elif KC == wx.WXK_RIGHT:
       
   247             if not self._multiChoices: return
       
   248             if self._colSearch < self.dropdownlistbox.GetColumnCount() -1:
       
   249                 self._colSearch += 1
       
   250             self._showDropDown()
       
   251 
       
   252         if visible :
       
   253             if event.GetKeyCode() == wx.WXK_RETURN :
       
   254                 self._setValueFromSelected()
   107                 self._setValueFromSelected()
   255                 skip = False
   108             else:
   256             if event.GetKeyCode() == wx.WXK_ESCAPE :
   109                 self._showDropDown(False)
   257                 self._showDropDown( False )
   110                 event.Skip()
   258                 skip = False
   111         elif event.GetKeyCode() == wx.WXK_ESCAPE:
   259         if skip :
   112             self._showDropDown(False)
       
   113         else:
   260             event.Skip()
   114             event.Skip()
   261 
   115 
   262     def onListItemSelected (self, event):
   116     def onListItemSelected(self, event):
   263         self._setValueFromSelected()
   117         self._setValueFromSelected()
   264         event.Skip()
   118         event.Skip()
   265 
   119 
   266     def onClickToggleDown(self, event):
   120     def onClickToggleDown(self, event):
   267         self._lastinsertionpoint = self.GetInsertionPoint()
   121         self._lastinsertionpoint = self.GetInsertionPoint()
   268         event.Skip ()
   122         event.Skip()
   269 
   123 
   270     def onClickToggleUp ( self, event ) :
   124     def onClickToggleUp(self, event):
   271         if ( self.GetInsertionPoint() == self._lastinsertionpoint ) :
   125         if self.GetInsertionPoint() == self._lastinsertionpoint:
   272             self._showDropDown ( not self.dropdown.IsShown() )
   126             self._showDropDown(not self.dropdown.IsShown())
   273         event.Skip ()
   127         event.Skip()
   274 
   128 
   275     def onControlChanged(self, event):
   129     def onControlChanged(self, event):
   276         res = self.GetValue()
   130         res = self.GetValue()
   277         config = wx.ConfigBase.Get()
   131         config = wx.ConfigBase.Get()
   278         listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([]))))
   132         listentries = cPickle.loads(str(config.Read(self.element_path, cPickle.dumps([]))))
   279         if len(res) and res not in listentries:
   133         if res and res not in listentries:
   280             config.Write(self.element_path, cPickle.dumps((listentries + [res])[-10:]))
   134             listentries = (listentries + [res])[-MAX_ITEM_COUNT:]
       
   135             config.Write(self.element_path, cPickle.dumps(listentries))
   281             config.Flush()
   136             config.Flush()
   282             self.SetChoices((listentries + [res])[-10:])
   137             self.SetChoices(listentries)
   283         self._showDropDown( False )
   138         self._showDropDown(False)
   284         event.Skip()
   139         event.Skip()
   285 
   140     
   286 
   141     def SetChoices(self, choices):
   287     # -- Interfaces methods
   142         self._choices = choices
   288     def SetMultipleChoices(self, choices, colSearch=0, colFetch=-1):
   143         self._refreshListBoxChoices()
   289         ''' Set multi-column choice
   144         
   290         '''
   145     def GetChoices(self):
   291         self._multiChoices = choices
   146         return self._choices
   292         self._choices = None
   147     
   293         if not isinstance(self._multiChoices, list):
   148 #-------------------------------------------------------------------------------
   294             self._multiChoices = [ x for x in self._multiChoices]
   149 #                           Internal methods
   295 
   150 #-------------------------------------------------------------------------------
   296         flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING
   151 
   297         if not self._showHead:
   152     def _refreshListBoxChoices(self):
   298             flags |= wx.LC_NO_HEADER
   153         text = self.GetValue()
   299         self.dropdownlistbox.SetWindowStyleFlag(flags)
   154 
   300 
   155         self.dropdownlistbox.Clear()
   301         #prevent errors on "old" systems
   156         for choice in self._choices:
   302         if sys.version.startswith("2.3"):
   157             if choice.startswith(text):
   303             self._multiChoices.sort(lambda x, y: cmp(x[0].lower(), y[0].lower()))
   158                 self.dropdownlistbox.Append(choice)
       
   159         
       
   160         itemcount = min(len(self.dropdownlistbox.GetStrings()), MAX_ITEM_SHOWN)
       
   161         self.popupsize = wx.Size(self.GetSize()[0], ITEM_HEIGHT * itemcount + 4)
       
   162         self.dropdownlistbox.SetSize(self.popupsize)
       
   163         self.dropdown.SetClientSize(self.popupsize)
       
   164     
       
   165     def _moveSelection(self, direction):
       
   166         selected = self.dropdownlistbox.GetSelection()
       
   167         if selected == wx.NOT_FOUND:
       
   168             if direction >= 0:
       
   169                 selected = 0
       
   170             else:
       
   171                 selected = self.dropdownlistbox.GetCount() - 1
   304         else:
   172         else:
   305             self._multiChoices.sort(key=lambda x: locale.strxfrm(x[0]).lower() )
   173             selected = (selected + direction) % (self.dropdownlistbox.GetCount() + 1)
   306 
   174         if selected == self.dropdownlistbox.GetCount():
   307         self._updateDataList(self._multiChoices)
   175             selected = wx.NOT_FOUND
   308 
   176         self.dropdownlistbox.SetSelection(selected)
   309         lChoices = len(choices)
   177     
   310         if lChoices < 2:
   178     def _setValueFromSelected(self):
   311             raise ValueError, "You have to pass me a multi-dimension list"
   179          """
   312 
       
   313         for numCol, rowValues in enumerate(choices[0]):
       
   314 
       
   315             if self._colNames: colName = self._colNames[numCol]
       
   316             else: colName = "Select %i" % numCol
       
   317 
       
   318             self.dropdownlistbox.InsertColumn(numCol, colName)
       
   319 
       
   320         for numRow, valRow in enumerate(choices):
       
   321 
       
   322             for numCol, colVal in enumerate(valRow):
       
   323                 if numCol == 0:
       
   324                     index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)
       
   325                 self.dropdownlistbox.SetStringItem(index, numCol, colVal)
       
   326                 self.dropdownlistbox.SetItemData(index, numRow)
       
   327 
       
   328         self._setListSize()
       
   329         self._colSearch = colSearch
       
   330         self._colFetch = colFetch
       
   331 
       
   332     def SetChoices(self, choices):
       
   333         '''
       
   334         Sets the choices available in the popup wx.ListBox.
       
   335         The items will be sorted case insensitively.
       
   336         '''
       
   337         self._choices = choices
       
   338         self._multiChoices = None
       
   339         flags = wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_SORT_ASCENDING | wx.LC_NO_HEADER
       
   340         self.dropdownlistbox.SetWindowStyleFlag(flags)
       
   341                 
       
   342         #if not isinstance(choices, list):
       
   343         #    self._choices = [ x for x in choices if len(x)]
       
   344         self._choices = [ x for x in choices if len(x)]
       
   345         #prevent errors on "old" systems
       
   346         if sys.version.startswith("2.3"):
       
   347             self._choices.sort(lambda x, y: cmp(x.lower(), y.lower()))
       
   348         else:
       
   349             self._choices.sort(key=lambda x: locale.strxfrm(x).lower())
       
   350 
       
   351         self._updateDataList(self._choices)
       
   352 
       
   353         self.dropdownlistbox.InsertColumn(0, "")
       
   354 
       
   355         for num, colVal in enumerate(self._choices):
       
   356             index = self.dropdownlistbox.InsertImageStringItem(sys.maxint, colVal, -1)
       
   357 
       
   358             self.dropdownlistbox.SetStringItem(index, 0, colVal)
       
   359             self.dropdownlistbox.SetItemData(index, num)
       
   360 
       
   361         self._setListSize()
       
   362 
       
   363         # there is only one choice for both search and fetch if setting a single column:
       
   364         self._colSearch = 0
       
   365         self._colFetch = -1
       
   366 
       
   367     def GetChoices(self):
       
   368         if self._choices:
       
   369             return self._choices
       
   370         else:
       
   371             return self._multiChoices
       
   372 
       
   373     def SetSelectCallback(self, cb=None):
       
   374         self._selectCallback = cb
       
   375 
       
   376     def SetEntryCallback(self, cb=None):
       
   377         self._entryCallback = cb
       
   378 
       
   379     def SetMatchFunction(self, mf=None):
       
   380         self._matchFunction = mf
       
   381 
       
   382 
       
   383     #-- Internal methods
       
   384     def _setValueFromSelected( self ) :
       
   385          '''
       
   386          Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
   180          Sets the wx.TextCtrl value from the selected wx.ListCtrl item.
   387          Will do nothing if no item is selected in the wx.ListCtrl.
   181          Will do nothing if no item is selected in the wx.ListCtrl.
   388          '''
   182          """
   389          sel = self.dropdownlistbox.GetFirstSelected()
   183          selected = self.dropdownlistbox.GetStringSelection()
   390          if sel > -1:
   184          if selected:
   391             if self._colFetch != -1: col = self._colFetch
   185             self.SetValue(selected)
   392             else: col = self._colSearch
   186             self._showDropDown(False)
   393 
   187 
   394             itemtext = self.dropdownlistbox.GetItem(sel, col).GetText()
   188 
   395             if self._selectCallback:
   189     def _showDropDown(self, show=True) :
   396                 dd = self.dropdownlistbox
   190         """
   397                 values = [dd.GetItem(sel, x).GetText()
       
   398                     for x in xrange(dd.GetColumnCount())]
       
   399                 self._selectCallback( values )
       
   400 
       
   401             self.SetValue (itemtext)
       
   402             self.SetInsertionPointEnd ()
       
   403             self.SetSelection ( -1, -1 )
       
   404             self._showDropDown ( False )
       
   405 
       
   406 
       
   407     def _showDropDown ( self, show = True ) :
       
   408         '''
       
   409         Either display the drop down list (show = True) or hide it (show = False).
   191         Either display the drop down list (show = True) or hide it (show = False).
   410         '''
   192         """
   411         if show :
   193         if show :
   412             size = self.dropdown.GetSize()
   194             size = self.dropdown.GetSize()
   413             width, height = self . GetSizeTuple()
   195             width, height = self.GetSizeTuple()
   414             x, y = self . ClientToScreenXY ( 0, height )
   196             x, y = self.ClientToScreenXY(0, height)
   415             if size.GetWidth() != width :
   197             if size.GetWidth() != width :
   416                 size.SetWidth(width)
   198                 size.SetWidth(width)
   417                 self.dropdown.SetSize(size)
   199                 self.dropdown.SetSize(size)
   418                 self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
   200                 self.dropdownlistbox.SetSize(self.dropdown.GetClientSize())
   419             if (y + size.GetHeight()) < self._screenheight :
   201             if (y + size.GetHeight()) < self._screenheight :
   420                 self.dropdown . SetPosition ( wx.Point(x, y) )
   202                 self.dropdown.SetPosition(wx.Point(x, y))
   421             else:
   203             else:
   422                 self.dropdown . SetPosition ( wx.Point(x, y - height - size.GetHeight()) )
   204                 self.dropdown.SetPosition(wx.Point(x, y - height - size.GetHeight()))
   423         self.dropdown.Show ( show )
   205         self.dropdown.Show(show) 
   424 
   206 
   425     def _listItemVisible( self ) :
       
   426         '''
       
   427         Moves the selected item to the top of the list ensuring it is always visible.
       
   428         '''
       
   429         toSel =  self.dropdownlistbox.GetFirstSelected ()
       
   430         if toSel == -1: return
       
   431         self.dropdownlistbox.EnsureVisible( toSel )
       
   432 
       
   433     def _updateDataList(self, choices):
       
   434         #delete, if need, all the previous data
       
   435         if self.dropdownlistbox.GetColumnCount() != 0:
       
   436             self.dropdownlistbox.DeleteAllColumns()
       
   437             self.dropdownlistbox.DeleteAllItems()
       
   438 
       
   439         #and update the dict
       
   440         if choices:
       
   441             for numVal, data in enumerate(choices):
       
   442                 self.itemDataMap[numVal] = data
       
   443         else:
       
   444             numVal = 0
       
   445         self.SetColumnCount(numVal)
       
   446 
       
   447     def _setListSize(self):
       
   448         if self._multiChoices:
       
   449             choices = self._multiChoices
       
   450         else:
       
   451             choices = self._choices
       
   452 
       
   453         longest = 0
       
   454         for choice in choices :
       
   455             longest = max(len(choice), longest)
       
   456 
       
   457         longest += 3
       
   458         itemcount = min( len( choices ) , 7 ) + 2
       
   459         charheight = self.dropdownlistbox.GetCharHeight()
       
   460         charwidth = self.dropdownlistbox.GetCharWidth()
       
   461         self.popupsize = wx.Size( charwidth*longest, charheight*itemcount )
       
   462         self.dropdownlistbox.SetSize ( self.popupsize )
       
   463         self.dropdown.SetClientSize( self.popupsize )
       
   464 
       
   465     
       
   466 
       
   467 
       
   468 class test:
       
   469     def __init__(self):
       
   470         args = dict()
       
   471         if 1:
       
   472             args["colNames"] = ("col1", "col2")
       
   473             args["multiChoices"] = [ ("Zoey","WOW"), ("Alpha", "wxPython"),
       
   474                                     ("Ceda","Is"), ("Beta", "fantastic"),
       
   475                                     ("zoebob", "!!")]
       
   476             args["colFetch"] = 1
       
   477         else:
       
   478             args["choices"] = ["123", "cs", "cds", "Bob","Marley","Alpha"]
       
   479 
       
   480         args["selectCallback"] = self.selectCallback
       
   481 
       
   482         self.dynamic_choices = [
       
   483                         'aardvark', 'abandon', 'acorn', 'acute', 'adore',
       
   484                         'aegis', 'ascertain', 'asteroid',
       
   485                         'beautiful', 'bold', 'classic',
       
   486                         'daring', 'dazzling', 'debonair', 'definitive',
       
   487                         'effective', 'elegant',
       
   488                         'http://python.org', 'http://www.google.com',
       
   489                         'fabulous', 'fantastic', 'friendly', 'forgiving', 'feature',
       
   490                         'sage', 'scarlet', 'scenic', 'seaside', 'showpiece', 'spiffy',
       
   491                         'www.wxPython.org', 'www.osafoundation.org'
       
   492                         ]
       
   493 
       
   494 
       
   495         app = wx.PySimpleApp()
       
   496         frm = wx.Frame(None,-1,"Test",style=wx.TAB_TRAVERSAL|wx.DEFAULT_FRAME_STYLE)
       
   497         panel = wx.Panel(frm)
       
   498         sizer = wx.BoxSizer(wx.VERTICAL)
       
   499 
       
   500         self._ctrl = TextCtrlAutoComplete(panel, **args)
       
   501         but = wx.Button(panel,label="Set other multi-choice")
       
   502         but.Bind(wx.EVT_BUTTON, self.onBtMultiChoice)
       
   503         but2 = wx.Button(panel,label="Set other one-colum choice")
       
   504         but2.Bind(wx.EVT_BUTTON, self.onBtChangeChoice)
       
   505         but3 = wx.Button(panel,label="Set the starting choices")
       
   506         but3.Bind(wx.EVT_BUTTON, self.onBtStartChoices)
       
   507         but4 = wx.Button(panel,label="Enable dynamic choices")
       
   508         but4.Bind(wx.EVT_BUTTON, self.onBtDynamicChoices)
       
   509 
       
   510         sizer.Add(but, 0, wx.ADJUST_MINSIZE, 0)
       
   511         sizer.Add(but2, 0, wx.ADJUST_MINSIZE, 0)
       
   512         sizer.Add(but3, 0, wx.ADJUST_MINSIZE, 0)
       
   513         sizer.Add(but4, 0, wx.ADJUST_MINSIZE, 0)
       
   514         sizer.Add(self._ctrl, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
       
   515         panel.SetAutoLayout(True)
       
   516         panel.SetSizer(sizer)
       
   517         sizer.Fit(panel)
       
   518         sizer.SetSizeHints(panel)
       
   519         panel.Layout()
       
   520         app.SetTopWindow(frm)
       
   521         frm.Show()
       
   522         but.SetFocus()
       
   523         app.MainLoop()
       
   524 
       
   525     def onBtChangeChoice(self, event):
       
   526         #change the choices
       
   527         self._ctrl.SetChoices(["123", "cs", "cds", "Bob","Marley","Alpha"])
       
   528         self._ctrl.SetEntryCallback(None)
       
   529         self._ctrl.SetMatchFunction(None)
       
   530 
       
   531     def onBtMultiChoice(self, event):
       
   532         #change the choices
       
   533         self._ctrl.SetMultipleChoices( [ ("Test","Hello"), ("Other word","World"),
       
   534                                         ("Yes!","it work?") ], colFetch = 1 )
       
   535         self._ctrl.SetEntryCallback(None)
       
   536         self._ctrl.SetMatchFunction(None)
       
   537 
       
   538     def onBtStartChoices(self, event):
       
   539         #change the choices
       
   540         self._ctrl.SetMultipleChoices( [ ("Zoey","WOW"), ("Alpha", "wxPython"),
       
   541                                     ("Ceda","Is"), ("Beta", "fantastic"),
       
   542                                     ("zoebob", "!!")], colFetch = 1 )
       
   543         self._ctrl.SetEntryCallback(None)
       
   544         self._ctrl.SetMatchFunction(None)
       
   545 
       
   546     def onBtDynamicChoices(self, event):
       
   547         '''
       
   548         Demonstrate dynamic adjustment of the auto-complete list, based on what's
       
   549         been typed so far:
       
   550         '''
       
   551         self._ctrl.SetChoices(self.dynamic_choices)
       
   552         self._ctrl.SetEntryCallback(self.setDynamicChoices)
       
   553         self._ctrl.SetMatchFunction(self.match)
       
   554 
       
   555 
       
   556     def match(self, text, choice):
       
   557         '''
       
   558         Demonstrate "smart" matching feature, by ignoring http:// and www. when doing
       
   559         matches.
       
   560         '''
       
   561         t = text.lower()
       
   562         c = choice.lower()
       
   563         if c.startswith(t): return True
       
   564         if c.startswith(r'http://'): c = c[7:]
       
   565         if c.startswith(t): return True
       
   566         if c.startswith('www.'): c = c[4:]
       
   567         return c.startswith(t)
       
   568 
       
   569     def setDynamicChoices(self):
       
   570         ctrl = self._ctrl
       
   571         text = ctrl.GetValue().lower()
       
   572         current_choices = ctrl.GetChoices()
       
   573         choices = [choice for choice in self.dynamic_choices if self.match(text, choice)]
       
   574         if choices != current_choices:
       
   575             ctrl.SetChoices(choices)
       
   576 
       
   577     def selectCallback(self, values):
       
   578         """ Simply function that receive the row values when the
       
   579             user select an item
       
   580         """
       
   581         print "Select Callback called...:",  values
       
   582 
       
   583 
       
   584 if __name__ == "__main__":
       
   585     test()
       
   586